1/*! 2 * bootstrap-fileinput v4.3.6 3 * http://plugins.krajee.com/file-input 4 * 5 * Author: Kartik Visweswaran 6 * Copyright: 2014 - 2016, Kartik Visweswaran, Krajee.com 7 * 8 * Licensed under the BSD 3-Clause 9 * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md 10 */ 11(function (factory) { 12 "use strict"; 13 //noinspection JSUnresolvedVariable 14 if (typeof define === 'function' && define.amd) { // jshint ignore:line 15 // AMD. Register as an anonymous module. 16 define(['jquery'], factory); // jshint ignore:line 17 } else { // noinspection JSUnresolvedVariable 18 if (typeof module === 'object' && module.exports) { // jshint ignore:line 19 // Node/CommonJS 20 // noinspection JSUnresolvedVariable 21 module.exports = factory(require('jquery')); // jshint ignore:line 22 } else { 23 // Browser globals 24 factory(window.jQuery); 25 } 26 } 27}(function ($) { 28 "use strict"; 29 30 $.fn.fileinputLocales = {}; 31 $.fn.fileinputThemes = {}; 32 33 var NAMESPACE, MODAL_ID, STYLE_SETTING, OBJECT_PARAMS, DEFAULT_PREVIEW, objUrl, compare, isIE, handler, 34 previewCache, getNum, hasFileAPISupport, hasDragDropSupport, hasFileUploadSupport, addCss, tMain1, tMain2, 35 tPreview, tFileIcon, tClose, tCaption, tBtnDefault, tBtnLink, tBtnBrowse, tModalMain, tModal, tProgress, tSize, 36 tFooter, tActions, tActionDelete, tActionUpload, tActionZoom, tActionDrag, tTagBef, tTagBef1, tTagBef2, tTagAft, 37 tGeneric, tHtml, tImage, tText, tVideo, tAudio, tFlash, tObject, tPdf, tOther, defaultFileActionSettings, 38 defaultLayoutTemplates, defaultPreviewTemplates, defaultPreviewZoomSettings, defaultPreviewTypes, getElement, 39 defaultPreviewSettings, defaultFileTypeSettings, isEmpty, isArray, ifSet, uniqId, htmlEncode, replaceTags, 40 cleanMemory, findFileName, checkFullScreen, toggleFullScreen, moveArray, FileInput; 41 42 NAMESPACE = '.fileinput'; 43 MODAL_ID = 'kvFileinputModal'; 44 STYLE_SETTING = 'style="width:{width};height:{height};"'; 45 OBJECT_PARAMS = '<param name="controller" value="true" />\n' + 46 '<param name="allowFullScreen" value="true" />\n' + 47 '<param name="allowScriptAccess" value="always" />\n' + 48 '<param name="autoPlay" value="false" />\n' + 49 '<param name="autoStart" value="false" />\n' + 50 '<param name="quality" value="high" />\n'; 51 DEFAULT_PREVIEW = '<div class="file-preview-other">\n' + 52 '<span class="{previewFileIconClass}">{previewFileIcon}</span>\n' + 53 '</div>'; 54 //noinspection JSUnresolvedVariable 55 objUrl = window.URL || window.webkitURL; 56 compare = function (input, str, exact) { 57 return input !== undefined && (exact ? input === str : input.match(str)); 58 }; 59 isIE = function (ver) { 60 // check for IE versions < 11 61 if (navigator.appName !== 'Microsoft Internet Explorer') { 62 return false; 63 } 64 if (ver === 10) { 65 return new RegExp('msie\\s' + ver, 'i').test(navigator.userAgent); 66 } 67 var div = document.createElement("div"), status; 68 div.innerHTML = "<!--[if IE " + ver + "]> <i></i> <![endif]-->"; 69 status = div.getElementsByTagName("i").length; 70 document.body.appendChild(div); 71 div.parentNode.removeChild(div); 72 return status; 73 }; 74 handler = function ($el, event, callback, skipNS) { 75 var ev = skipNS ? event : event.split(' ').join(NAMESPACE + ' ') + NAMESPACE; 76 $el.off(ev).on(ev, callback); 77 }; 78 previewCache = { 79 data: {}, 80 init: function (obj) { 81 var content = obj.initialPreview, id = obj.id; 82 if (content.length > 0 && !isArray(content)) { 83 content = content.split(obj.initialPreviewDelimiter); 84 } 85 previewCache.data[id] = { 86 content: content, 87 config: obj.initialPreviewConfig, 88 tags: obj.initialPreviewThumbTags, 89 delimiter: obj.initialPreviewDelimiter, 90 previewFileType: obj.initialPreviewFileType, 91 previewAsData: obj.initialPreviewAsData, 92 template: obj.previewGenericTemplate, 93 showZoom: obj.fileActionSettings.showZoom, 94 showDrag: obj.fileActionSettings.showDrag, 95 getSize: function (size) { 96 return obj._getSize(size); 97 }, 98 parseTemplate: function (cat, data, fname, ftype, pId, ftr, ind) { 99 var fc = ' file-preview-initial'; 100 return obj._generatePreviewTemplate(cat, data, fname, ftype, pId, false, null, fc, ftr, ind); 101 }, 102 msg: function (n) { 103 return obj._getMsgSelected(n); 104 }, 105 initId: obj.previewInitId, 106 footer: obj._getLayoutTemplate('footer').replace(/\{progress}/g, obj._renderThumbProgress()), 107 isDelete: obj.initialPreviewShowDelete, 108 caption: obj.initialCaption, 109 actions: function (showUpload, showDelete, showZoom, showDrag, disabled, url, key) { 110 return obj._renderFileActions(showUpload, showDelete, showZoom, showDrag, disabled, url, key, true); 111 } 112 }; 113 }, 114 fetch: function (id) { 115 return previewCache.data[id].content.filter(function (n) { 116 return n !== null; 117 }); 118 }, 119 count: function (id, all) { 120 return !!previewCache.data[id] && !!previewCache.data[id].content ? 121 (all ? previewCache.data[id].content.length : previewCache.fetch(id).length) : 0; 122 }, 123 get: function (id, i, isDisabled) { 124 var ind = 'init_' + i, data = previewCache.data[id], config = data.config[i], content = data.content[i], 125 previewId = data.initId + '-' + ind, out, $tmp, frameClass = ' file-preview-initial', cat, cap, ftr, 126 ftype, asData = ifSet('previewAsData', config, data.previewAsData); 127 isDisabled = isDisabled === undefined ? true : isDisabled; 128 /** @namespace config.frameAttr */ 129 /** @namespace config.frameClass */ 130 /** @namespace config.filetype */ 131 if (!content) { 132 return ''; 133 } 134 if (config && config.frameClass) { 135 frameClass += ' ' + config.frameClass; 136 } 137 if (asData) { 138 cat = data.previewAsData ? ifSet('type', config, data.previewFileType || 'generic') : 'generic'; 139 cap = ifSet('caption', config); 140 ftr = previewCache.footer(id, i, isDisabled, (config && config.size || null)); 141 ftype = ifSet('filetype', config, cat); 142 out = data.parseTemplate(cat, content, cap, ftype, previewId, ftr, ind, null); 143 } else { 144 out = data.template 145 .replace(/\{previewId}/g, previewId).replace(/\{frameClass}/g, frameClass) 146 .replace(/\{fileindex}
146/g, ind).replace(/\{content}/g, data.content[i]) 147 .replace(/\{template}/g, ifSet('type', config, data.previewFileType)) 148 .replace(/\{footer}/g, previewCache.footer(id, i, isDisabled, (config && config.size || null))); 149 } 150 if (data.tags.length && data.tags[i]) { 151 out = replaceTags(out, data.tags[i]); 152 } 153 if (!isEmpty(config) && !isEmpty(config.frameAttr)) { 154 $tmp = $(document.createElement('div')).html(out); 155 $tmp.find('.file-preview-initial').attr(config.frameAttr); 156 out = $tmp.html(); 157 $tmp.remove(); 158 } 159 return out; 160 }, 161 add: function (id, content, config, tags, append) { 162 var data = $.extend(true, {}, previewCache.data[id]), index; 163 if (!isArray(content)) { 164 content = content.split(data.delimiter); 165 } 166 if (append) { 167 index = data.content.push(content) - 1; 168 data.config[index] = config; 169 data.tags[index] = tags; 170 } else { 171 index = content.length - 1; 172 data.content = content; 173 data.config = config; 174 data.tags = tags; 175 } 176 previewCache.data[id] = data; 177 return index; 178 }, 179 set: function (id, content, config, tags, append) { 180 var data = $.extend(true, {}, previewCache.data[id]), i, chk; 181 if (!content || !content.length) { 182 return; 183 } 184 if (!isArray(content)) { 185 content = content.split(data.delimiter); 186 } 187 chk = content.filter(function (n) { 188 return n !== null; 189 }); 190 if (!chk.length) { 191 return; 192 } 193 if (data.content === undefined) { 194 data.content = []; 195 } 196 if (data.config === undefined) { 197 data.config = []; 198 } 199 if (data.tags === undefined) { 200 data.tags = []; 201 } 202 if (append) { 203 for (i = 0; i < content.length; i++) { 204 if (content[i]) { 205 data.content.push(content[i]); 206 } 207 } 208 for (i = 0; i < config.length; i++) { 209 if (config[i]) { 210 data.config.push(config[i]); 211 } 212 } 213 for (i = 0; i < tags.length; i++) { 214 if (tags[i]) { 215 data.tags.push(tags[i]); 216 } 217 } 218 } else { 219 data.content = content; 220 data.config = config; 221 data.tags = tags; 222 } 223 previewCache.data[id] = data; 224 }, 225 unset: function (obj, index) { 226 var chk = previewCache.count(obj.id); 227 if (!chk) { 228 return; 229 } 230 if (chk === 1) { 231 previewCache.data[obj.id].content = []; 232 previewCache.data[obj.id].config = []; 233 previewCache.data[obj.id].tags = []; 234 obj.initialPreview = []; 235 obj.initialPreviewConfig = []; 236 obj.initialPreviewThumbTags = []; 237 return; 238 } 239 240 previewCache.data[obj.id].content[index] = null; 241 previewCache.data[obj.id].config[index] = null; 242 previewCache.data[obj.id].tags[index] = null; 243 }, 244 out: function (id) { 245 var html = '', data = previewCache.data[id], caption, len = previewCache.count(id, true); 246 if (len === 0) { 247 return {content: '', caption: ''}; 248 } 249 for (var i = 0; i < len; i++) { 250 html += previewCache.get(id, i); 251 } 252 caption = data.msg(previewCache.count(id)); 253 return {content: '<div class="file-initial-thumbs">' + html + '</div>', caption: caption}; 254 }, 255 footer: function (id, i, isDisabled, size) { 256 var data = previewCache.data[id]; 257 isDisabled = isDisabled === undefined ? true : isDisabled; 258 if (data.config.length === 0 || isEmpty(data.config[i])) { 259 return ''; 260 } 261 var config = data.config[i], caption = ifSet('caption', config), width = ifSet('width', config, 'auto'),
262 url = ifSet('url', config, false), key = ifSet('key', config, null), 263 showDel = ifSet('showDelete', config, true), showZoom = ifSet('showZoom', config, data.showZoom), 264 showDrag = ifSet('showDrag', config, data.showDrag), disabled = (url === false) && isDisabled, 265 actions = data.isDelete ? data.actions(false, showDel, showZoom, showDrag, disabled, url, key) : '', 266 footer = data.footer.replace(/\{actions}/g, actions); 267 return footer.replace(/\{caption}/g, caption).replace(/\{size}/g, data.getSize(size)) 268 .replace(/\{width}/g, width).replace(/\{indicator}/g, '').replace(/\{indicatorTitle}/g, ''); 269 } 270 }; 271 getNum = function (num, def) { 272 def = def || 0; 273 if (typeof num === "number") { 274 return num; 275 } 276 if (typeof num === "string") { 277 num = parseFloat(num); 278 } 279 return isNaN(num) ? def : num; 280 }; 281 hasFileAPISupport = function () { 282 return !!(window.File && window.FileReader); 283 }; 284 hasDragDropSupport = function () { 285 var div = document.createElement('div'); 286 /** @namespace div.draggable */ 287 /** @namespace div.ondragstart */ 288 /** @namespace div.ondrop */ 289 return !isIE(9) && (div.draggable !== undefined || (div.ondragstart !== undefined && div.ondrop !== undefined)); 290 }; 291 hasFileUploadSupport = function () { 292 return hasFileAPISupport() && window.FormData; 293 }; 294 addCss = function ($el, css) { 295 $el.removeClass(css).addClass(css); 296 }; 297 defaultFileActionSettings = { 298 showRemove: true, 299 showUpload: true, 300 showZoom: true, 301 showDrag: true, 302 removeIcon: '<i class="glyphicon glyphicon-trash text-danger"></i>', 303 removeClass: 'btn btn-xs btn-default', 304 removeTitle: 'Remove file', 305 uploadIcon: '<i class="glyphicon glyphicon-upload text-info"></i>', 306 uploadClass: 'btn btn-xs btn-default', 307 uploadTitle: 'Upload file', 308 zoomIcon: '<i class="glyphicon glyphicon-zoom-in"></i>', 309 zoomClass: 'btn btn-xs btn-default', 310 zoomTitle: 'View Details', 311 dragIcon: '<i class="glyphicon glyphicon-menu-hamburger"></i>', 312 dragClass: 'text-info', 313 dragTitle: 'Move / Rearrange', 314 dragSettings: {}, 315 indicatorNew: '<i class="glyphicon glyphicon-hand-down text-warning"></i>', 316 indicatorSuccess: '<i class="glyphicon glyphicon-ok-sign text-success"></i>', 317 indicatorError: '<i class="glyphicon glyphicon-exclamation-sign text-danger"></i>', 318 indicatorLoading: '<i class="glyphicon glyphicon-hand-up text-muted"></i>', 319 indicatorNewTitle: 'Not uploaded yet', 320 indicatorSuccessTitle: 'Uploaded', 321 indicatorErrorTitle: 'Upload Error', 322 indicatorLoadingTitle: 'Uploading ...' 323 }; 324 tMain1 = '{preview}\n' + 325 '<div class="kv-upload-progress hide"></div>\n' + 326 '<div class="input-group {class}">\n' + 327 ' {caption}\n' + 328 ' <div class="input-group-btn">\n' + 329 ' {remove}\n' + 330 ' {cancel}\n' + 331 ' {upload}\n' + 332 ' {browse}\n' + 333 ' </div>\n' + 334 '</div>'; 335 tMain2 = '{preview}\n<div class="kv-upload-progress hide"></div>\n{remove}\n{cancel}\n{upload}\n{browse}\n'; 336 tPreview = '<div class="file-preview {class}">\n' + 337 ' {close}' + 338 ' <div class="{dropClass}">\n' + 339 ' <div class="file-preview-thumbnails">\n' + 340 ' </div>\n' + 341 ' <div class="clearfix"></div>' + 342 ' <div class="file-preview-status text-center text-success"></div>\n' + 343 ' <div class="kv-fileinput-error"></div>\n' + 344 ' </div>\n' + 345 '</div>'; 346 tClose = '<div class="close fileinput-remove">×</div>\n'; 347 tFileIcon = '<i class="glyphicon glyphicon-file kv-caption-icon"></i>'; 348 tCaption = '<div tabindex="500" class="form-control file-caption {class}">\n' + 349 ' <div class="file-caption-name"></div>\n' + 350 '</div>\n'; 351 //noinspection HtmlUnknownAttribute 352 tBtnDefault = '<button type="{type}" tabindex="500" title="{title}" class="{css}" {status}>{icon} {label}</button>'; 353 //noinspection HtmlUnknownAttribute 354 tBtnLink = '<a href="{href}" tabindex="500" title="{title}" class="{css}" {status}>{icon} {label}</a>'; 355 //noinspection HtmlUnknownAttribute 356 tBtnBrowse = '<div tabindex="500" class="{css}" {status}>{icon} {label}</div>'; 357 tModalMain = '<div id="' + MODAL_ID + '" class="file-zoom-dialog modal fade" tabindex="-1" aria-labelledby="' + 358 MODAL_ID + 'Label"></div>'; 359 tModal = '<div class="modal-dialog modal-lg" role="document">\n' + 360 ' <div class="modal-content">\n' + 361 ' <div class="modal-header">\n' + 362 ' <div class="kv-zoom-actions pull-right">{toggleheader}{fullscreen}{borderless}{close}</div>\n' + 363 ' <h3 class="modal-title">{heading} <small><span class="kv-zoom-title"></span></small></h3>\n' + 364 ' </div>\n' + 365 ' <div class="modal-body">\n' + 366 ' <div class="floating-buttons"></div>\n' + 367 ' <div class="kv-zoom-body file-zoom-content"></div>\n' + '{prev} {next}\n' + 368 ' </div>\n' + 369 ' </div>\n' + 370 '</div>\n'; 371 tProgress = '<div class="progress">\n' + 372 ' <div class="{class}" role="progressbar"' + 373 ' aria-valuenow="{percent}" aria-valuemin="0" aria-valuemax="100" style="width:{percent}%;">\n' + 374 ' {percent}%\n' + 375 ' </div>\n' + 376 '</div>'; 377 tSize = ' <br><samp>({sizeText})</samp>'; 378 tFooter = '<div class="file-thumbnail-footer">\n' + 379 ' <div class="file-footer-caption" title="{caption}">{caption}{size}</div>\n' + 380 ' {progress} {actions}\n' + 381 '</div>'; 382 tActions = '<div class="file-actions">\n' + 383 ' <div class="file-footer-buttons">\n' + 384 ' {upload} {delete} {zoom} {other}
384' + 385 ' </div>\n' + 386 ' {drag}\n' + 387 ' <div class="file-upload-indicator" title="{indicatorTitle}">{indicator}</div>\n' + 388 ' <div class="clearfix"></div>\n' + 389 '</div>'; 390 //noinspection HtmlUnknownAttribute 391 tActionDelete = '<button type="button" class="kv-file-remove {removeClass}" ' + 'title="{removeTitle}" {dataUrl}{dataKey}>{removeIcon}</button>\n'; 392 tActionUpload = '<button type="button" class="kv-file-upload {uploadClass}" title="{uploadTitle}">' + 393 '{uploadIcon}</button>'; 394 tActionZoom = '<button type="button" class="kv-file-zoom {zoomClass}" title="{zoomTitle}">{zoomIcon}</button>'; 395 tActionDrag = '<span class="file-drag-handle {dragClass}" title="{dragTitle}">{dragIcon}</span>'; 396 tTagBef = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + 397 ' data-template="{template}"'; 398 tTagBef1 = tTagBef + '><div class="kv-file-content">\n'; 399 tTagBef2 = tTagBef + ' title="{caption}" ' + STYLE_SETTING + '><div class="kv-file-content">\n'; 400 tTagAft = '</div>{footer}\n</div>\n'; 401 tGeneric = '{content}\n'; 402 tHtml = '<div class="kv-preview-data file-preview-html" title="{caption}" ' + STYLE_SETTING + '>{data}</div>\n'; 403 tImage = '<img src="{data}" class="kv-preview-data file-preview-image" title="{caption}" alt="{caption}" ' + 404 STYLE_SETTING + '>\n'; 405 tText = '<textarea class="kv-preview-data file-preview-text" title="{caption}" readonly ' + STYLE_SETTING + 406 '>{data}</textarea>\n'; 407 tVideo = '<video class="kv-preview-data" width="{width}" height="{height}" controls>\n' + 408 '<source src="{data}" type="{type}">\n' + DEFAULT_PREVIEW + '\n</video>\n'; 409 tAudio = '<audio class="kv-preview-data" controls>\n<source src="' + '{data}' + '" type="{type}">\n' + 410 DEFAULT_PREVIEW + '\n</audio>\n'; 411 tFlash = '<object class="kv-preview-data file-object" type="application/x-shockwave-flash" ' + 412 'width="{width}" height="{height}" data="{data}">\n' + OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n</object>\n'; 413 tObject = '<object class="kv-preview-data file-object" data="{data}" type="{type}" width="{width}" height="{height}">\n' + 414 '<param name="movie" value="{caption}" />\n' + OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n</object>\n'; 415 tPdf = '<embed class="kv-preview-data" src="{data}" width="{width}" height="{height}" type="application/pdf">\n'; 416 tOther = '<div class="kv-preview-data file-preview-other-frame">\n' + DEFAULT_PREVIEW + '\n</div>\n'; 417 defaultLayoutTemplates = { 418 main1: tMain1, 419 main2: tMain2, 420 preview: tPreview, 421 close: tClose, 422 fileIcon: tFileIcon, 423 caption: tCaption, 424 modalMain: tModalMain, 425 modal: tModal, 426 progress: tProgress, 427 size: tSize, 428 footer: tFooter, 429 actions: tActions, 430 actionDelete: tActionDelete, 431 actionUpload: tActionUpload, 432 actionZoom: tActionZoom, 433 actionDrag: tActionDrag, 434 btnDefault: tBtnDefault, 435 btnLink: tBtnLink, 436 btnBrowse: tBtnBrowse 437 }; 438 defaultPreviewTemplates = { 439 generic: tTagBef1 + tGeneric + tTagAft, 440 html: tTagBef1 + tHtml + tTagAft, 441 image: tTagBef1 + tImage + tTagAft, 442 text: tTagBef1 + tText + tTagAft, 443 video: tTagBef2 + tVideo + tTagAft, 444 audio: tTagBef2 + tAudio + tTagAft, 445 flash: tTagBef2 + tFlash + tTagAft, 446 object: tTagBef2 + tObject + tTagAft, 447 pdf: tTagBef2 + tPdf + tTagAft, 448 other: tTagBef2 + tOther + tTagAft 449 }; 450 defaultPreviewTypes = ['image', 'html', 'text', 'video', 'audio', 'flash', 'pdf', 'object']; 451 defaultPreviewSettings = { 452 image: {width: "auto", height: "160px"}, 453 html: {width: "213px", height: "160px"}, 454 text: {width: "213px", height: "160px"}, 455 video: {width: "213px", height: "160px"}, 456 audio: {width: "213px", height: "80px"}, 457 flash: {width: "213px", height: "160px"}, 458 object: {width: "160px", height: "160px"}, 459 pdf: {width: "160px", height: "160px"}, 460 other: {width: "160px", height: "160px"} 461 };
462 defaultPreviewZoomSettings = { 463 image: {width: "auto", height: "auto", 'max-width': "100%",'max-height': "100%"}, 464 html: {width: "100%", height: "100%", 'min-height': "480px"}, 465 text: {width: "100%", height: "100%", 'min-height': "480px"}, 466 video: {width: "auto", height: "100%", 'max-width': "100%"}, 467 audio: {width: "100%", height: "30px"}, 468 flash: {width: "auto", height: "480px"}, 469 object: {width: "auto", height: "100%", 'min-height': "480px"}, 470 pdf: {width: "100%", height: "100%", 'min-height': "480px"}, 471 other: {width: "auto", height: "100%", 'min-height': "480px"} 472 }; 473 defaultFileTypeSettings = { 474 image: function (vType, vName) { 475 return compare(vType, 'image.*') || compare(vName, /\.(gif|png|jpe?g)$/i); 476 }, 477 html: function (vType, vName) { 478 return compare(vType, 'text/html') || compare(vName, /\.(htm|html)$/i); 479 }, 480 text: function (vType, vName) { 481 return compare(vType, 'text.*') || compare(vName, /\.(xml|javascript)$/i) || 482 compare(vName, /\.(txt|md|csv|nfo|ini|json|php|js|css)$/i); 483 }, 484 video: function (vType, vName) { 485 return compare(vType, 'video.*') && (compare(vType, /(ogg|mp4|mp?g|mov|webm|3gp)$/i) || 486 compare(vName, /\.(og?|mp4|webm|mp?g|mov|3gp)$/i)); 487 }, 488 audio: function (vType, vName) { 489 return compare(vType, 'audio.*') && (compare(vName, /(ogg|mp3|mp?g|wav)$/i) || 490 compare(vName, /\.(og?|mp3|mp?g|wav)$/i)); 491 }, 492 flash: function (vType, vName) { 493 return compare(vType, 'application/x-shockwave-flash', true) || compare(vName, /\.(swf)$/i); 494 }, 495 pdf: function (vType, vName) { 496 return compare(vType, 'application/pdf', true) || compare(vName, /\.(pdf)$/i); 497 }, 498 object: function () { 499 return true; 500 }, 501 other: function () { 502 return true; 503 } 504 }; 505 isEmpty = function (value, trim) { 506 return value === undefined || value === null || value.length === 0 || (trim && $.trim(value) === ''); 507 }; 508 isArray = function (a) { 509 return Array.isArray(a) || Object.prototype.toString.call(a) === '[object Array]'; 510 }; 511 ifSet = function (needle, haystack, def) { 512 def = def || ''; 513 return (haystack && typeof haystack === 'object' && needle in haystack) ? haystack[needle] : def; 514 }; 515 getElement = function (options, param, value) { 516 return (isEmpty(options) || isEmpty(options[param])) ? value : $(options[param]); 517 }; 518 uniqId = function () { 519 return Math.round(new Date().getTime() + (Math.random() * 100)); 520 }; 521 htmlEncode = function (str) { 522 return str.replace(/&/g, '&') 523 .replace(/</g, '<') 524 .replace(/>/g, '>') 525 .replace(/"/g, '"') 526 .replace(/'/g, '''); 527 }; 528 replaceTags = function (str, tags) { 529 var out = str; 530 if (!tags) { 531 return out; 532 } 533 $.each(tags, function (key, value) { 534 if (typeof value === "function") { 535 value = value(); 536 } 537 out = out.split(key).join(value); 538 }); 539 return out; 540 }; 541 cleanMemory = function ($thumb) { 542 var data = $thumb.is('img') ? $thumb.attr('src') : $thumb.find('source').attr('src'); 543 /** @namespace objUrl.revokeObjectURL */ 544 objUrl.revokeObjectURL(data); 545 }; 546 findFileName = function (filePath) { 547 var sepIndex = filePath.lastIndexOf('/'); 548 if (sepIndex === -1) { 549 sepIndex = filePath.lastIndexOf('\\'); 550 } 551 return filePath.split(filePath.substring(sepIndex, sepIndex + 1)).pop(); 552 }; 553 checkFullScreen = function () { 554 //noinspection JSUnresolvedVariable 555 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || 556 document.msFullscreenElement; 557 }; 558 toggleFullScreen = function (maximize) { 559 if (maximize && !checkFullScreen()) { 560 /** @namespace document.documentElement.requestFullscreen */ 561 /** @namespace document.documentElement.msRequestFullscreen */ 562 /** @namespace document.documentElement.mozRequestFullScreen */ 563 /** @namespace document.documentElement.webkitRequestFullscreen */ 564 /** @namespace Element.ALLOW_KEYBOARD_INPUT */ 565 if (document.documentElement.requestFullscreen) { 566 document.documentElement.requestFullscreen(); 567 } else if (document.documentElement.msRequestFullscreen) { 568 document.documentElement.msRequestFullscreen(); 569 } else if (document.documentElement.mozRequestFullScreen) { 570 document.documentElement.mozRequestFullScreen(); 571 } else if (document.documentElement.webkitRequestFullscreen) { 572 document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); 573 } 574 } else { 575 /** @namespace document.exitFullscreen */ 576 /** @namespace document.msExitFullscreen */ 577 /** @namespace document.mozCancelFullScreen */ 578 /** @namespace document.webkitExitFullscreen */ 579 if (document.exitFullscreen) { 580 document.exitFullscreen(); 581 } else if (document.msExitFullscreen) { 582 document.msExitFullscreen(); 583 } else if (document.mozCancelFullScreen) { 584 document.mozCancelFullScreen(); 585 } else if (document.webkitExitFullscreen) { 586 document.webkitExitFullscreen(); 587 } 588 } 589 }; 590 moveArray = function (arr, oldIndex, newIndex) { 591 if (newIndex >= arr.length) { 592 var k = newIndex - arr.length; 593 while ((k--) + 1) { 594 arr.push(undefined); 595 } 596 } 597 arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]); 598 return arr; 599 }; 600 FileInput = function (element, options) { 601 var self = this; 602 self.$element = $(element); 603 if (!self._validate()) { 604 return; 605 } 606 self.isPreviewable = hasFileAPISupport(); 607 self.isIE9 = isIE(9); 608 self.isIE10 = isIE(10); 609 if (self.isPreviewable || self.isIE9) { 610 self._init(options); 611 self._listen(); 612 } else { 613 self.$element.removeClass('file-loading'); 614 } 615 }; 616 FileInput.prototype = { 617 constructor: FileInput, 618 _init: function (options) { 619 var self = this, $el = self.$element, t; 620 $.each(options, function (key, value) { 621 switch (key) { 622 case 'minFileCount':
623 case 'maxFileCount': 624 case 'maxFileSize': 625 self[key] = getNum(value); 626 break; 627 default: 628 self[key] = value; 629 break; 630 } 631 }); 632 self.fileInputCleared = false; 633 self.fileBatchCompleted = true; 634 if (!self.isPreviewable) { 635 self.showPreview = false; 636 } 637 self.uploadFileAttr = !isEmpty($el.attr('name')) ? $el.attr('name') : 'file_data'; 638 self.reader = null; 639 self.formdata = {}; 640 self.clearStack(); 641 self.uploadCount = 0; 642 self.uploadStatus = {}; 643 self.uploadLog = []; 644 self.uploadAsyncCount = 0; 645 self.loadedImages = []; 646 self.totalImagesCount = 0; 647 self.ajaxRequests = []; 648 self.isError = false; 649 self.ajaxAborted = false; 650 self.cancelling = false; 651 t = self._getLayoutTemplate('progress'); 652 self.progressTemplate = t.replace('{class}', self.progressClass); 653 self.progressCompleteTemplate = t.replace('{class}', self.progressCompleteClass); 654 self.progressErrorTemplate = t.replace('{class}', self.progressErrorClass); 655 self.dropZoneEnabled = hasDragDropSupport() && self.dropZoneEnabled; 656 self.isDisabled = self.$element.attr('disabled') || self.$element.attr('readonly'); 657 self.isUploadable = hasFileUploadSupport() && !isEmpty(self.uploadUrl); 658 self.isClickable = self.browseOnZoneClick && self.showPreview && 659 (self.isUploadable && self.dropZoneEnabled || !isEmpty(self.defaultPreviewContent)); 660 self.slug = typeof options.slugCallback === "function" ? options.slugCallback : self._slugDefault; 661 self.mainTemplate = self.showCaption ? self._getLayoutTemplate('main1') : self._getLayoutTemplate('main2'); 662 self.captionTemplate = self._getLayoutTemplate('caption'); 663 self.previewGenericTemplate = self._getPreviewTemplate('generic'); 664 if (self.resizeImage && (self.maxImageWidth || self.maxImageHeight)) { 665 self.imageCanvas = document.createElement('canvas'); 666 self.imageCanvasContext = self.imageCanvas.getContext('2d'); 667 } 668 if (isEmpty(self.$element.attr('id'))) { 669 self.$element.attr('id', uniqId()); 670 } 671 if (self.$container === undefined) { 672 self.$container = self._createContainer(); 673 } else { 674 self._refreshContainer(); 675 } 676 self.$dropZone = self.$container.find('.file-drop-zone'); 677 self.$progress = self.$container.find('.kv-upload-progress'); 678 self.$btnUpload = self.$container.find('.fileinput-upload'); 679 self.$captionContainer = getElement(options, 'elCaptionContainer', self.$container.find('.file-caption')); 680 self.$caption = getElement(options, 'elCaptionText', self.$container.find('.file-caption-name')); 681 self.$previewContainer = getElement(options, 'elPreviewContainer', self.$container.find('.file-preview')); 682 self.$preview = getElement(options, 'elPreviewImage', self.$container.find('.file-preview-thumbnails')); 683 self.$previewStatus = getElement(options, 'elPreviewStatus', self.$container.find('.file-preview-status')); 684 self.$errorContainer = getElement(options, 'elErrorContainer', 685 self.$previewContainer.find('.kv-fileinput-error')); 686 if (!isEmpty(self.msgErrorClass)) { 687 addCss(self.$errorContainer, self.msgErrorClass); 688 } 689 self.$errorContainer.hide(); 690 self.fileActionSettings = $.extend(true, defaultFileActionSettings, options.fileActionSettings); 691 self.previewInitId = "preview-" + uniqId(); 692 self.id = self.$element.attr('id'); 693 previewCache.init(self); 694 self._initPreview(true); 695 self._initPreviewActions(); 696 self.options = options; 697 self._setFileDropZoneTitle(); 698 self.$element.removeClass('file-loading'); 699 if (self.$element.attr('disabled')) { 700 self.disable(); 701 } 702 self._initZoom(); 703 }, 704 _validate: function () { 705 var self = this, $exception; 706 if (self.$element.attr('type') === 'file') { 707 return true; 708 } 709 $exception = '<div class="help-block alert alert-warning">' + 710 '<h4>Invalid Input Type</h4>' + 711 'You must set an input <code>type = file</code> for <b>bootstrap-fileinput</b> plugin to initialize.' + 712 '</div>'; 713 self.$element.after($exception); 714 return false;
715 }, 716 _errorsExist: function () { 717 var self = this, $err; 718 if (self.$errorContainer.find('li').length) { 719 return true; 720 } 721 $err = $(document.createElement('div')).html(self.$errorContainer.html()); 722 $err.find('span.kv-error-close').remove(); 723 $err.find('ul').remove(); 724 return $.trim($err.text()).length ? true : false; 725 }, 726 _errorHandler: function (evt, caption) { 727 var self = this, err = evt.target.error; 728 /** @namespace err.NOT_FOUND_ERR */ 729 /** @namespace err.SECURITY_ERR */ 730 /** @namespace err.NOT_READABLE_ERR */ 731 if (err.code === err.NOT_FOUND_ERR) { 732 self._showError(self.msgFileNotFound.replace('{name}', caption)); 733 } else if (err.code === err.SECURITY_ERR) { 734 self._showError(self.msgFileSecured.replace('{name}', caption)); 735 } else if (err.code === err.NOT_READABLE_ERR) { 736 self._showError(self.msgFileNotReadable.replace('{name}', caption)); 737 } else if (err.code === err.ABORT_ERR) { 738 self._showError(self.msgFilePreviewAborted.replace('{name}', caption)); 739 } else { 740 self._showError(self.msgFilePreviewError.replace('{name}', caption)); 741 } 742 }, 743 _addError: function (msg) { 744 var self = this, $error = self.$errorContainer; 745 if (msg && $error.length) { 746 $error.html(self.errorCloseButton + msg); 747 handler($error.find('.kv-error-close'), 'click', function () { 748 $error.fadeOut('slow'); 749 }); 750 } 751 }, 752 _resetErrors: function (fade) { 753 var self = this, $error = self.$errorContainer; 754 self.isError = false; 755 self.$container.removeClass('has-error'); 756 $error.html(''); 757 if (fade) { 758 $error.fadeOut('slow'); 759 } else { 760 $error.hide(); 761 } 762 }, 763 _showFolderError: function (folders) { 764 var self = this, $error = self.$errorContainer, msg; 765 if (!folders) { 766 return; 767 } 768 msg = self.msgFoldersNotAllowed.replace(/\{n}/g, folders); 769 self._addError(msg); 770 addCss(self.$container, 'has-error'); 771 $error.fadeIn(800); 772 self._raise('filefoldererror', [folders, msg]); 773 }, 774 _showUploadError: function (msg, params, event) { 775 var self = this, $error = self.$errorContainer, ev = event || 'fileuploaderror', e = params && params.id ? 776 '<li data-file-id="' + params.id + '">' + msg + '</li>' : '<li>' + msg + '</li>'; 777 if ($error.find('ul').length === 0) { 778 self._addError('<ul>' + e + '</ul>'); 779 } else { 780 $error.find('ul').append(e); 781 } 782 $error.fadeIn(800); 783 self._raise(ev, [params, msg]); 784 self.$container.removeClass('file-input-new'); 785 addCss(self.$container, 'has-error'); 786 return true; 787 }, 788 _showError: function (msg, params, event) { 789 var self = this, $error = self.$errorContainer, ev = event || 'fileerror'; 790 params = params || {}; 791 params.reader = self.reader; 792 self._addError(msg); 793 $error.fadeIn(800); 794 self._raise(ev, [params, msg]); 795 if (!self.isUploadable) { 796 self._clearFileInput(); 797 } 798 self.$container.removeClass('file-input-new'); 799 addCss(self.$container, 'has-error'); 800 self.$btnUpload.attr('disabled', true); 801 return true; 802 }, 803 _noFilesError: function (params) { 804 var self = this, label = self.minFileCount > 1 ? self.filePlural : self.fileSingle, 805 msg = self.msgFilesTooLess.replace('{n}', self.minFileCount).replace('{files}', label), 806 $error = self.$errorContainer; 807 self._addError(msg); 808 self.isError = true; 809 self._updateFileDetails(0); 810 $error.fadeIn(800); 811 self._raise('fileerror', [params, msg]); 812 self._clearFileInput(); 813 addCss(self.$container, 'has-error'); 814 }, 815 _parseError: function (jqXHR, errorThrown, fileName) { 816 /** @namespace jqXHR.responseJSON */ 817 var self = this, errMsg = $.trim(errorThrown + ''), 818 dot = errMsg.slice(-1) === '.' ? '' : '.', 819 text = jqXHR.responseJSON !== undefined && jqXHR.responseJSON.error !== undefined ? 820 jqXHR.responseJSON.error : jqXHR.responseText; 821 if (self.cancelling && self.msgUploadAborted) { 822 errMsg = self.msgUploadAborted; 823 } 824 if (self.showAjaxErrorDetails && text) { 825 text = $.trim(text.replace(/\n\s*\n/g, '\n')); 826 text = text.length > 0 ? '<pre>
826' + text + '</pre>' : ''; 827 errMsg += dot + text; 828 } else { 829 errMsg += dot; 830 } 831 self.cancelling = false; 832 return fileName ? '<b>' + fileName + ': </b>' + errMsg : errMsg; 833 }, 834 _parseFileType: function (file) { 835 var self = this, isValid, vType, cat, i; 836 for (i = 0; i < defaultPreviewTypes.length; i += 1) { 837 cat = defaultPreviewTypes[i]; 838 isValid = ifSet(cat, self.fileTypeSettings, defaultFileTypeSettings[cat]); 839 vType = isValid(file.type, file.name) ? cat : ''; 840 if (!isEmpty(vType)) { 841 return vType; 842 } 843 } 844 return 'other'; 845 }, 846 _parseFilePreviewIcon: function (content, fname) { 847 var self = this, proceed, ext, icn = self.previewFileIcon; 848 if (fname && fname.indexOf('.') > -1) { 849 ext = fname.split('.').pop(); 850 if (self.previewFileIconSettings && self.previewFileIconSettings[ext]) { 851 icn = self.previewFileIconSettings[ext]; 852 } 853 if (self.previewFileExtSettings) { 854 $.each(self.previewFileExtSettings, function (key, func) { 855 if (self.previewFileIconSettings[key] && func(ext)) { 856 icn = self.previewFileIconSettings[key]; 857 return; 858 } 859 proceed = true; 860 }); 861 } 862 } 863 if (content.indexOf('{previewFileIcon}') > -1) { 864 return content.replace(/\{previewFileIconClass}/g, self.previewFileIconClass).replace( 865 /\{previewFileIcon}/g, icn); 866 } 867 return content; 868 }, 869 _raise: function (event, params) { 870 var self = this, e = $.Event(event); 871 if (params !== undefined) { 872 self.$element.trigger(e, params); 873 } else { 874 self.$element.trigger(e); 875 } 876 if (e.isDefaultPrevented()) { 877 return false; 878 } 879 if (!e.result) { 880 return e.result; 881 } 882 switch (event) { 883 // ignore these events 884 case 'filebatchuploadcomplete': 885 case 'filebatchuploadsuccess': 886 case 'fileuploaded': 887 case 'fileclear': 888 case 'filecleared': 889 case 'filereset': 890 case 'fileerror': 891 case 'filefoldererror': 892 case 'fileuploaderror': 893 case 'filebatchuploaderror': 894 case 'filedeleteerror': 895 case 'filecustomerror': 896 case 'filesuccessremove': 897 break; 898 // receive data response via `filecustomerror` event` 899 default: 900 self.ajaxAborted = e.result; 901 break; 902 } 903 return true; 904 }, 905 _listenFullScreen: function (isFullScreen) { 906 var self = this, $modal = self.$modal, $btnFull, $btnBord; 907 if (!$modal || !$modal.length) { 908 return; 909 } 910 $btnFull = $modal && $modal.find('.btn-fullscreen'); 911 $btnBord = $modal && $modal.find('.btn-borderless'); 912 if (!$btnFull.length || !$btnBord.length) { 913 return; 914 } 915 $btnFull.removeClass('active').attr('aria-pressed', 'false'); 916 $btnBord.removeClass('active').attr('aria-pressed', 'false'); 917 if (isFullScreen) { 918 $btnFull.addClass('active').attr('aria-pressed', 'true'); 919 } else { 920 $btnBord.addClass('active').attr('aria-pressed', 'true'); 921 } 922 if ($modal.hasClass('file-zoom-fullscreen')) { 923 self._maximizeZoomDialog(); 924 } else { 925 if (isFullScreen) { 926 self._maximizeZoomDialog(); 927 } else { 928 $btnBord.removeClass('active').attr('aria-pressed', 'false'); 929 } 930 } 931 }, 932 _listen: function () { 933 var self = this, $el = self.$element, $form = $el.closest('form'), $cont = self.$container; 934 handler($el, 'change', $.proxy(self._change, self)); 935 if (self.showBrowse) { 936 handler(self.$btnFile, 'click', $.proxy(self._browse, self)); 937 } 938 handler($form, 'reset', $.proxy(self.reset, self)); 939 handler($cont.find('.fileinput-remove:not([disabled])'), 'click', $.proxy(self.clear, self)); 940 handler($cont.find('.fileinput-cancel'), 'click', $.proxy(self.cancel, self)); 941 self._initDragDrop(); 942 if (!self.isUploadable) { 943 handler($form, 'submit', $.proxy(self._submitForm, self)); 944 } 945 handler(self.$container.find('.fileinput-upload'), 'click', $.proxy(self._uploadClick, self)); 946 handler($(window), 'resize', function () { 947 self._listenFullScreen(screen.width === window.innerWidth && screen.height === window.innerHeight); 948 }); 949 handler($(document), 'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange', 950 function () { 951 self._listenFullScreen(checkFullScreen()); 952 }); 953 self._initClickable(); 954 }, 955 _initClickable: function () { 956 var self = this, $zone; 957 if (!self.isClickable) { 958 return; 959 } 960 $zone = self.isUploadable ? self.$dropZone : self.$preview.find('.file-default-preview'); 961 addCss($zone, 'clickable'); 962 $zone.attr('tabindex', -1); 963 handler($zone, 'click', function (e) { 964 var $target = $(e.target);
965 if (!$target.parents('.file-preview-thumbnails').length || $target.parents( 966 '.file-default-preview').length) { 967 self.$element.trigger('click'); 968 $zone.blur(); 969 } 970 }); 971 }, 972 _initDragDrop: function () { 973 var self = this, $zone = self.$dropZone; 974 if (self.isUploadable && self.dropZoneEnabled && self.showPreview) { 975 handler($zone, 'dragenter dragover', $.proxy(self._zoneDragEnter, self)); 976 handler($zone, 'dragleave', $.proxy(self._zoneDragLeave, self)); 977 handler($zone, 'drop', $.proxy(self._zoneDrop, self)); 978 handler($(document), 'dragenter dragover drop', self._zoneDragDropInit); 979 } 980 }, 981 _zoneDragDropInit: function (e) { 982 e.stopPropagation(); 983 e.preventDefault(); 984 }, 985 _zoneDragEnter: function (e) { 986 var self = this, hasFiles = $.inArray('Files', e.originalEvent.dataTransfer.types) > -1; 987 self._zoneDragDropInit(e); 988 if (self.isDisabled || !hasFiles) { 989 e.originalEvent.dataTransfer.effectAllowed = 'none'; 990 e.originalEvent.dataTransfer.dropEffect = 'none'; 991 return; 992 } 993 addCss(self.$dropZone, 'file-highlighted'); 994 }, 995 _zoneDragLeave: function (e) { 996 var self = this; 997 self._zoneDragDropInit(e); 998 if (self.isDisabled) { 999 return; 1000 } 1001 self.$dropZone.removeClass('file-highlighted'); 1002 }, 1003 _zoneDrop: function (e) { 1004 var self = this; 1005 e.preventDefault(); 1006 /** @namespace e.originalEvent.dataTransfer */ 1007 if (self.isDisabled || isEmpty(e.originalEvent.dataTransfer.files)) { 1008 return; 1009 } 1010 self._change(e, 'dragdrop'); 1011 self.$dropZone.removeClass('file-highlighted'); 1012 }, 1013 _uploadClick: function (e) { 1014 var self = this, $btn = self.$container.find('.fileinput-upload'), $form, 1015 isEnabled = !$btn.hasClass('disabled') && isEmpty($btn.attr('disabled')); 1016 if (e && e.isDefaultPrevented()) { 1017 return; 1018 } 1019 if (!self.isUploadable) { 1020 if (isEnabled && $btn.attr('type') !== 'submit') { 1021 $form = $btn.closest('form'); 1022 // downgrade to normal form submit if possible 1023 if ($form.length) { 1024 $form.trigger('submit'); 1025 } 1026 e.preventDefault(); 1027 } 1028 return; 1029 } 1030 e.preventDefault(); 1031 if (isEnabled) { 1032 self.upload(); 1033 } 1034 }, 1035 _submitForm: function () { 1036 var self = this, $el = self.$element, files = $el.get(0).files; 1037 if (files && self.minFileCount > 0 && self._getFileCount(files.length) < self.minFileCount) { 1038 self._noFilesError({}); 1039 return false; 1040 } 1041 return !self._abort({}); 1042 }, 1043 _clearPreview: function () { 1044 var self = this, $thumbs = !self.showUploadedThumbs ? self.$preview.find('.file-preview-frame') : 1045 self.$preview.find('.file-preview-frame:not(.file-preview-success)'); 1046 $thumbs.remove(); 1047 if (!self.$preview.find('.file-preview-frame').length || !self.showPreview) { 1048 self._resetUpload(); 1049 } 1050 self._validateDefaultPreview(); 1051 }, 1052 _initSortable: function () { 1053 var self = this, $preview = self.$preview, $el, settings; 1054 if (!window.KvSortable) { 1055 return; 1056 } 1057 $el = $preview.find('.file-initial-thumbs'); 1058 //noinspection JSUnusedGlobalSymbols 1059 settings = { 1060 handle: '.drag-handle-init', 1061 dataIdAttr: 'data-preview-id', 1062 draggable: '.file-preview-initial', 1063 onSort: function (e) { 1064 var oldIndex = e.oldIndex, newIndex = e.newIndex; 1065 self.initialPreview = moveArray(self.initialPreview, oldIndex, newIndex); 1066 self.initialPreviewConfig = moveArray(self.initialPreviewConfig, oldIndex, newIndex); 1067 previewCache.init(self); 1068 for(var i = 0; i < self.initialPreviewConfig.length; i++) { 1069 if(self.initialPreviewConfig[i] !== null) { 1070 var key = self.initialPreviewConfig[i].key; 1071 var $frame = $(".kv-file-remove[data-key='" + key + "']"); 1072 $frame = $frame.closest('.file-preview-frame'); 1073 $frame.attr('data-fileindex', 'init_'+i); 1074 $frame.data('fileindex', 'init_'+i); 1075 } 1076 } 1077 self._raise('filesorted', { 1078 previewId: $(e.item).attr('id'), 1079 'oldIndex': oldIndex, 1080 'newIndex': newIndex, 1081 stack: self.initialPreviewConfig 1082 }); 1083 } 1084 }; 1085 if ($el.data('kvsortable')) { 1086 $el.kvsortable('destroy'); 1087 } 1088 $.extend(true, settings, self.fileActionSettings.dragSettings);
1089 $el.kvsortable(settings); 1090 }, 1091 _initPreview: function (isInit) { 1092 var self = this, cap = self.initialCaption || '', out; 1093 if (!previewCache.count(self.id)) { 1094 self._clearPreview(); 1095 if (isInit) { 1096 self._setCaption(cap); 1097 } else { 1098 self._initCaption(); 1099 } 1100 return; 1101 } 1102 out = previewCache.out(self.id); 1103 cap = isInit && self.initialCaption ? self.initialCaption : out.caption; 1104 self.$preview.html(out.content); 1105 self._setCaption(cap); 1106 self._initSortable(); 1107 if (!isEmpty(out.content)) { 1108 self.$container.removeClass('file-input-new'); 1109 } 1110 }, 1111 _getZoomButton: function (type) { 1112 var self = this, label = self.previewZoomButtonIcons[type], css = self.previewZoomButtonClasses[type], 1113 title = ' title="' + (self.previewZoomButtonTitles[type] || '') + '" ', 1114 params = title + (type === 'close' ? ' data-dismiss="modal" aria-hidden="true"' : ''); 1115 if (type === 'fullscreen' || type === 'borderless' || type === 'toggleheader') { 1116 params += ' data-toggle="button" aria-pressed="false" autocomplete="off"'; 1117 } 1118 return '<button type="button" class="' + css + ' btn-' + type + '"' + params + '>' + label + '</button>'; 1119 }, 1120 _getModalContent: function () { 1121 var self = this; 1122 return self._getLayoutTemplate('modal') 1123 .replace(/\{heading}/g, self.msgZoomModalHeading) 1124 .replace(/\{prev}/g, self._getZoomButton('prev')) 1125 .replace(/\{next}/g, self._getZoomButton('next')) 1126 .replace(/\{toggleheader}/g, self._getZoomButton('toggleheader')) 1127 .replace(/\{fullscreen}/g, self._getZoomButton('fullscreen')) 1128 .replace(/\{borderless}/g, self._getZoomButton('borderless')) 1129 .replace(/\{close}/g, self._getZoomButton('close')); 1130 }, 1131 _listenModalEvent: function (event) { 1132 var self = this, $modal = self.$modal, getParams = function (e) { 1133 return { 1134 sourceEvent: e, 1135 previewId: $modal.data('previewId'), 1136 modal: $modal 1137 }; 1138 }; 1139 $modal.on(event + '.bs.modal', function (e) { 1140 var $btnFull = $modal.find('.btn-fullscreen'), $btnBord = $modal.find('.btn-borderless'); 1141 self._raise('filezoom' + event, getParams(e)); 1142 if (event === 'shown') { 1143 $btnBord.removeClass('active').attr('aria-pressed', 'false'); 1144 $btnFull.removeClass('active').attr('aria-pressed', 'false'); 1145 if ($modal.hasClass('file-zoom-fullscreen')) { 1146 self._maximizeZoomDialog(); 1147 if (checkFullScreen()) { 1148 $btnFull.addClass('active').attr('aria-pressed', 'true'); 1149 } else { 1150 $btnBord.addClass('active').attr('aria-pressed', 'true'); 1151 } 1152 } 1153 } 1154 }); 1155 }, 1156 _initZoom: function () { 1157 var self = this, $dialog, modalMain = self._getLayoutTemplate('modalMain'), modalId = '#' + MODAL_ID; 1158 self.$modal = $(modalId); 1159 if (!self.$modal || !self.$modal.length) { 1160 $dialog = $(document.createElement('div')).html(modalMain).insertAfter(self.$container); 1161 self.$modal = $('#' + MODAL_ID).insertBefore($dialog); 1162 $dialog.remove(); 1163 } 1164 self.$modal.html(self._getModalContent()); 1165 self._listenModalEvent('show'); 1166 self._listenModalEvent('shown'); 1167 self._listenModalEvent('hide'); 1168 self._listenModalEvent('hidden'); 1169 self._listenModalEvent('loaded'); 1170 }, 1171 _initZoomButtons: function () { 1172 var self = this, previewId = self.$modal.data('previewId') || '', $first, $last, 1173 frames = self.$preview.find('.file-preview-frame').toArray(), len = frames.length, 1174 $prev = self.$modal.find('.btn-prev'), $next = self.$modal.find('.btn-next'); 1175 1176 if (!len) { 1177 return; 1178 } 1179 $first = $(frames[0]); 1180 $last = $(frames[len - 1]); 1181 $prev.removeAttr('disabled'); 1182 $next.removeAttr('disabled'); 1183 if ($first.length && $first.attr('id') === previewId) { 1184 $prev.attr('disabled', true); 1185 } 1186 if ($last.length && $last.attr('id') === previewId) { 1187 $next.attr('disabled', true); 1188 } 1189 }, 1190 _maximizeZoomDialog: function () { 1191 var self = this, $modal = self.$modal, $head = $modal.find('.modal
1191-header:visible'), 1192 $foot = $modal.find('.modal-footer:visible'), $body = $modal.find('.modal-body'), 1193 h = $(window).height(), diff = 0; 1194 $modal.addClass('file-zoom-fullscreen'); 1195 if ($head && $head.length) { 1196 h -= $head.outerHeight(true); 1197 } 1198 if ($foot && $foot.length) { 1199 h -= $foot.outerHeight(true); 1200 } 1201 if ($body && $body.length) { 1202 diff = $body.outerHeight(true) - $body.height(); 1203 h -= diff; 1204 } 1205 $modal.find('.kv-zoom-body').height(h); 1206 }, 1207 _resizeZoomDialog: function (fullScreen) { 1208 var self = this, $modal = self.$modal, $btnFull = $modal.find('.btn-fullscreen'), 1209 $btnBord = $modal.find('.btn-borderless'); 1210 if ($modal.hasClass('file-zoom-fullscreen')) { 1211 toggleFullScreen(false); 1212 if (!fullScreen) { 1213 if (!$btnFull.hasClass('active')) { 1214 $modal.removeClass('file-zoom-fullscreen'); 1215 self.$modal.find('.kv-zoom-body').css('height', self.zoomModalHeight); 1216 } else { 1217 $btnFull.removeClass('active').attr('aria-pressed', 'false'); 1218 } 1219 } else { 1220 if (!$btnFull.hasClass('active')) { 1221 $modal.removeClass('file-zoom-fullscreen'); 1222 self._resizeZoomDialog(true); 1223 if ($btnBord.hasClass('active')) { 1224 $btnBord.removeClass('active').attr('aria-pressed', 'false'); 1225 } 1226 } 1227 } 1228 } else { 1229 if (!fullScreen) { 1230 self._maximizeZoomDialog(); 1231 return; 1232 } 1233 toggleFullScreen(true); 1234 } 1235 $modal.focus(); 1236 }, 1237 _setZoomContent: function ($preview, animate) { 1238 var self = this, $content, tmplt, body, title, $body, $dataEl, config, previewId = $preview.attr('id'), 1239 $modal = self.$modal, $prev = $modal.find('.btn-prev'), $next = $modal.find('.btn-next'), $tmp, 1240 $btnFull = $modal.find('.btn-fullscreen'), $btnBord = $modal.find('.btn-borderless'), 1241 $btnTogh = $modal.find('.btn-toggleheader'); 1242 tmplt = $preview.data('template') || 'generic'; 1243 $content = $preview.find('.kv-file-content'); 1244 body = $content.length ? $content.html() : ''; 1245 title = $preview.find('.file-footer-caption').text() || ''; 1246 $modal.find('.kv-zoom-title').html(title); 1247 $body = $modal.find('.kv-zoom-body'); 1248 if (animate) { 1249 $tmp = $body.clone().insertAfter($body); 1250 $body.html(body).hide(); 1251 $tmp.fadeOut('fast', function () { 1252 $body.fadeIn('fast'); 1253 $tmp.remove(); 1254 }); 1255 } else { 1256 $body.html(body); 1257 } 1258 config = self.previewZoomSettings[tmplt]; 1259 if (config) { 1260 $dataEl = $body.find('.kv-preview-data'); 1261 addCss($dataEl, 'file-zoom-detail'); 1262 $.each(config, function (key, value) { 1263 $dataEl.css(key, value); 1264 if (($dataEl.attr('width') && key === 'width') || ($dataEl.attr('height') && key === 'height')) { 1265 $dataEl.removeAttr(key); 1266 } 1267 }); 1268 } 1269 $modal.data('previewId', previewId); 1270 handler($prev, 'click', function () { 1271 self._zoomSlideShow('prev', previewId); 1272 }); 1273 handler($next, 'click', function () { 1274 self._zoomSlideShow('next', previewId); 1275 }); 1276 handler($btnFull, 'click', function () { 1277 self._resizeZoomDialog(true); 1278 }); 1279 handler($btnBord, 'click', function () { 1280 self._resizeZoomDialog(false); 1281 }); 1282 handler($btnTogh, 'click', function () {
1283 var $header = $modal.find('.modal-header'), $floatBar = $modal.find('.modal-body .floating-buttons'), 1284 ht, $actions = $header.find('.kv-zoom-actions'), resize = function (height) { 1285 var $body = self.$modal.find('.kv-zoom-body'), h = self.zoomModalHeight; 1286 if ($modal.hasClass('file-zoom-fullscreen')) { 1287 h = $body.outerHeight(true); 1288 if (!height) { 1289 h = h - $header.outerHeight(true); 1290 } 1291 } 1292 $body.css('height', height ? h + height : h); 1293 }; 1294 if ($header.is(':visible')) { 1295 ht = $header.outerHeight(true); 1296 $header.slideUp('slow', function () { 1297 $actions.find('.btn').appendTo($floatBar); 1298 resize(ht); 1299 }); 1300 } else { 1301 $floatBar.find('.btn').appendTo($actions); 1302 $header.slideDown('slow', function () { 1303 resize(); 1304 }); 1305 } 1306 $modal.focus(); 1307 }); 1308 handler($modal, 'keydown', function (e) { 1309 var key = e.which || e.keyCode; 1310 if (key === 37 && !$prev.attr('disabled')) { 1311 self._zoomSlideShow('prev', previewId); 1312 } 1313 if (key === 39 && !$next.attr('disabled')) { 1314 self._zoomSlideShow('next', previewId); 1315 } 1316 }); 1317 }, 1318 _zoomPreview: function ($btn) { 1319 var self = this, $preview; 1320 if (!$btn.length) { 1321 throw 'Cannot zoom to detailed preview!'; 1322 } 1323 self.$modal.html(self._getModalContent()); 1324 $preview = $btn.closest('.file-preview-frame'); 1325 self._setZoomContent($preview); 1326 self.$modal.modal('show'); 1327 self._initZoomButtons(); 1328 }, 1329 _zoomSlideShow: function (dir, previewId) { 1330 var self = this, $btn = self.$modal.find('.kv-zoom-actions .btn-' + dir), $targFrame, i, 1331 frames = self.$preview.find('.file-preview-frame').toArray(), len = frames.length, out; 1332 if ($btn.attr('disabled')) { 1333 return; 1334 } 1335 for (i = 0; i < len; i++) { 1336 if ($(frames[i]).attr('id') === previewId) { 1337 out = dir === 'prev' ? i - 1 : i + 1; 1338 break; 1339 } 1340 } 1341 if (out < 0 || out >= len || !frames[out]) { 1342 return; 1343 } 1344 $targFrame = $(frames[out]); 1345 if ($targFrame.length) { 1346 self._setZoomContent($targFrame, true); 1347 } 1348 self._initZoomButtons(); 1349 self._raise('filezoom' + dir, {'previewId': previewId, modal: self.$modal}); 1350 }, 1351 _initZoomButton: function () { 1352 var self = this; 1353 self.$preview.find('.kv-file-zoom').each(function () { 1354 var $el = $(this); 1355 handler($el, 'click', function () { 1356 self._zoomPreview($el); 1357 }); 1358 }); 1359 }, 1360 _initPreviewActions: function () { 1361 var self = this, deleteExtraData = self.deleteExtraData || {}, 1362 resetProgress = function () { 1363 var hasFiles = self.isUploadable ? previewCache.count(self.id) : self.$element.get(0).files.length; 1364 if (self.$preview.find('.kv-file-remove').length === 0 && !hasFiles) { 1365 self.reset(); 1366 self.initialCaption = ''; 1367 } 1368 }; 1369 self._initZoomButton(); 1370 self.$preview.find('.kv-file-remove').each(function () { 1371 var $el = $(this), vUrl = $el.data('url') || self.deleteUrl, vKey = $el.data('key'); 1372 if (isEmpty(vUrl) || vKey === undefined) { 1373 return; 1374 } 1375 var $frame = $el.closest('.file-preview-frame'), cache = previewCache.data[self.id], 1376 settings, params, index = $frame.data('fileindex'), config, extraData; 1377 index = parseInt(index.replace('init_', '')); 1378 config = isEmpty(cache.config) && isEmpty(cache.config[index]) ? null : cache.config[index]; 1379 extraData = isEmpty(config) || isEmpty(config.extra) ? deleteExtraData : config.extra; 1380 if (typeof extraData === "function") { 1381 extraData = extraData(); 1382 } 1383 params = {id: $el.attr('id'), key: vKey, extra: extraData}; 1384 settings = $.extend(true, {}, { 1385 url: vUrl, 1386 type: 'POST', 1387 dataType: 'json', 1388 data: $.extend(true, {}, {key: vKey}, extraData), 1389 beforeSend: function (jqXHR) { 1390 self.ajaxAborted = false;
1391 self._raise('filepredelete', [vKey, jqXHR, extraData]); 1392 if (self.ajaxAborted) { 1393 jqXHR.abort(); 1394 } else { 1395 addCss($frame, 'file-uploading'); 1396 addCss($el, 'disabled'); 1397 } 1398 }, 1399 success: function (data, textStatus, jqXHR) { 1400 var n, cap; 1401 if (isEmpty(data) || isEmpty(data.error)) { 1402 previewCache.init(self); 1403 index = parseInt(($frame.data('fileindex')).replace('init_', '')); 1404 previewCache.unset(self, index); 1405 n = previewCache.count(self.id); 1406 cap = n > 0 ? self._getMsgSelected(n) : ''; 1407 self._raise('filedeleted', [vKey, jqXHR, extraData]); 1408 self._setCaption(cap); 1409 } else { 1410 params.jqXHR = jqXHR; 1411 params.response = data; 1412 self._showError(data.error, params, 'filedeleteerror'); 1413 $frame.removeClass('file-uploading'); 1414 $el.removeClass('disabled'); 1415 resetProgress(); 1416 return; 1417 } 1418 $frame.removeClass('file-uploading').addClass('file-deleted'); 1419 $frame.fadeOut('slow', function () { 1420 self._clearObjects($frame); 1421 $frame.remove(); 1422 resetProgress(); 1423 if (!n && self.getFileStack().length === 0) { 1424 self._setCaption(''); 1425 self.reset(); 1426 } 1427 }); 1428 }, 1429 error: function (jqXHR, textStatus, errorThrown) { 1430 var errMsg = self._parseError(jqXHR, errorThrown); 1431 params.jqXHR = jqXHR; 1432 params.response = {}; 1433 self._showError(errMsg, params, 'filedeleteerror'); 1434 $frame.removeClass('file-uploading'); 1435 resetProgress(); 1436 } 1437 }, self.ajaxDeleteSettings); 1438 handler($el, 'click', function () { 1439 if (!self._validateMinCount()) { 1440 return false; 1441 } 1442 $.ajax(settings); 1443 }); 1444 }); 1445 }, 1446 _clearObjects: function ($el) { 1447 $el.find('video audio').each(function () { 1448 this.pause(); 1449 $(this).remove(); 1450 }); 1451 $el.find('img object div').each(function () { 1452 $(this).remove(); 1453 }); 1454 }, 1455 _clearFileInput: function () { 1456 var self = this, $el = self.$element, $srcFrm, $tmpFrm, $tmpEl; 1457 self.fileInputCleared = true; 1458 if (isEmpty($el.val())) { 1459 return; 1460 } 1461 // Fix for IE ver < 11, that does not clear file inputs. Requires a sequence of steps to prevent IE 1462 // crashing but still allow clearing of the file input. 1463 if (self.isIE9 || self.isIE10) { 1464 $srcFrm = $el.closest('form'); 1465 $tmpFrm = $(document.createElement('form')); 1466 $tmpEl = $(document.createElement('div')); 1467 $el.before($tmpEl); 1468 if ($srcFrm.length) { 1469 $srcFrm.after($tmpFrm); 1470 } else { 1471 $tmpEl.after($tmpFrm); 1472 } 1473 $tmpFrm.append($el).trigger('reset'); 1474 $tmpEl.before($el).remove(); 1475 $tmpFrm.remove(); 1476 } else { // normal input clear behavior for other sane browsers 1477 $el.val(''); 1478 } 1479 }, 1480 _resetUpload: function () { 1481 var self = this; 1482 self.uploadCache = {content: [], config: [], tags: [], append: true}; 1483 self.uploadCount = 0; 1484 self.uploadStatus = {}; 1485 self.uploadLog = []; 1486 self.uploadAsyncCount = 0; 1487 self.loadedImages = []; 1488 self.totalImagesCount = 0; 1489 self.$btnUpload.removeAttr('disabled'); 1490 self._setProgress(0); 1491 addCss(self.$progress, 'hide'); 1492 self._resetErrors(false); 1493 self.ajaxAborted = false;
1494 self.ajaxRequests = []; 1495 self._resetCanvas(); 1496 }, 1497 _resetCanvas: function () { 1498 var self = this; 1499 if (self.canvas && self.imageCanvasContext) { 1500 self.imageCanvasContext.clearRect(0, 0, self.canvas.width, self.canvas.height); 1501 } 1502 }, 1503 _hasInitialPreview: function () { 1504 var self = this; 1505 return !self.overwriteInitial && previewCache.count(self.id); 1506 }, 1507 _resetPreview: function () { 1508 var self = this, out, cap; 1509 if (previewCache.count(self.id)) { 1510 out = previewCache.out(self.id); 1511 self.$preview.html(out.content); 1512 cap = self.initialCaption ? self.initialCaption : out.caption; 1513 self._setCaption(cap); 1514 } else { 1515 self._clearPreview(); 1516 self._initCaption(); 1517 } 1518 if (self.showPreview) { 1519 self._initZoom(); 1520 self._initSortable(); 1521 } 1522 }, 1523 _clearDefaultPreview: function () { 1524 var self = this; 1525 self.$preview.find('.file-default-preview').remove(); 1526 }, 1527 _validateDefaultPreview: function () { 1528 var self = this; 1529 if (!self.showPreview || isEmpty(self.defaultPreviewContent)) { 1530 return; 1531 } 1532 self.$preview.html('<div class="file-default-preview">' + self.defaultPreviewContent + '</div>'); 1533 self.$container.removeClass('file-input-new'); 1534 self._initClickable(); 1535 }, 1536 _resetPreviewThumbs: function (isAjax) { 1537 var self = this, out; 1538 if (isAjax) { 1539 self._clearPreview(); 1540 self.clearStack(); 1541 return; 1542 } 1543 if (self._hasInitialPreview()) { 1544 out = previewCache.out(self.id); 1545 self.$preview.html(out.content); 1546 self._setCaption(out.caption); 1547 self._initPreviewActions(); 1548 } else { 1549 self._clearPreview(); 1550 } 1551 }, 1552 _getLayoutTemplate: function (t) { 1553 var self = this, 1554 template = ifSet(t, self.layoutTemplates, defaultLayoutTemplates[t]); 1555 if (isEmpty(self.customLayoutTags)) { 1556 return template; 1557 } 1558 return replaceTags(template, self.customLayoutTags); 1559 }, 1560 _getPreviewTemplate: function (t) { 1561 var self = this, 1562 template = ifSet(t, self.previewTemplates, defaultPreviewTemplates[t]); 1563 if (isEmpty(self.customPreviewTags)) { 1564 return template; 1565 } 1566 return replaceTags(template, self.customPreviewTags); 1567 }, 1568 _getOutData: function (jqXHR, responseData, filesData) { 1569 var self = this; 1570 jqXHR = jqXHR || {}; 1571 responseData = responseData || {}; 1572 filesData = filesData || self.filestack.slice(0) || {}; 1573 return { 1574 form: self.formdata, 1575 files: filesData, 1576 filenames: self.filenames, 1577 filescount: self.getFilesCount(), 1578 extra: self._getExtraData(), 1579 response: responseData, 1580 reader: self.reader, 1581 jqXHR: jqXHR 1582 }; 1583 }, 1584 _getMsgSelected: function (n) { 1585 var self = this, strFiles = n === 1 ? self.fileSingle : self.filePlural; 1586 return n > 0 ? self.msgSelected.replace('{n}', n).replace('{files}', strFiles) : self.msgNoFilesSelected; 1587 }, 1588 _getThumbs: function (css) { 1589 css = css || ''; 1590 return this.$preview.find('.file-preview-frame:not(.file-preview-initial)' + css); 1591 }, 1592 _getExtraData: function (previewId, index) { 1593 var self = this, data = self.uploadExtraData; 1594 if (typeof self.uploadExtraData === "function") { 1595 data = self.uploadExtraData(previewId, index); 1596 } 1597 return data; 1598 }, 1599 _initXhr: function (xhrobj, previewId, fileCount) { 1600 var self = this; 1601 if (xhrobj.upload) { 1602 xhrobj.upload.addEventListener('progress', function (event) { 1603 var pct = 0, total = event.total, position = event.loaded || event.position; 1604 /** @namespace event.lengthComputable */ 1605 if (event.lengthComputable) { 1606 pct = Math.floor(position / total * 100); 1607 } 1608 if (previewId) { 1609 self._setAsyncUploadStatus(previewId, pct, fileCount); 1610 } else { 1611 self._setProgress(pct); 1612 } 1613 }, false); 1614 } 1615 return xhrobj; 1616 }, 1617 _ajaxSubmit: function (fnBefore, fnSuccess, fnComplete, fnError, previewId, index) { 1618 var self = this, settings; 1619 self._raise('filepreajax', [previewId, index]); 1620 self._uploadExtra(previewId, index); 1621 settings = $.extend(true, {}, { 1622 xhr: function () { 1623 var xhrobj = $.ajaxSettings.xhr(); 1624 return self._initXhr(xhrobj, previewId, self.getFileStack().length); 1625 }, 1626 url: self.uploadUrl, 1627 type: 'POST', 1628 dataType: 'json', 1629 data: self.formdata, 1630 cache: false, 1631 processData: false, 1632 contentType: false, 1633 beforeSend: fnBefore, 1634 success: fnSuccess, 1635 complete: fnComplete, 1636 error: fnError 1637 }, self.ajaxSettings); 1638 self.ajaxRequests.push($.ajax(settings)); 1639 }, 1640 _initUploadSuccess: function (out, $thumb, allFiles) { 1641 var self = this, append, data, index, $newThumb, content, config, tags, i, 1642 mergeArray = function (prop, content) { 1643 if (!(self[prop] instanceof Array)) { 1644 self[prop] = []; 1645 } 1646 if (content && content.length) { 1647 self[prop] = self[prop].concat(content); 1648 } 1649 }; 1650 if (!self.showPreview || typeof out !== 'object' || $.isEmptyObject(out)) { 1651 return; 1652 } 1653 if (out.initialPreview !== undefined && out.initialPreview.length > 0) { 1654 self.hasInitData = true; 1655 content = out.initialPreview || []; 1656 config = out.initialPreviewConfig || []; 1657 tags = out.initialPreviewThumbTags || []; 1658 append = out.append === undefined || out.append ? true : false;
1659 if (content.length > 0 && !isArray(content)) { 1660 content = content.split(self.initialPreviewDelimiter); 1661 } 1662 self.overwriteInitial = false; 1663 mergeArray('initialPreview', content); 1664 mergeArray('initialPreviewConfig', config); 1665 mergeArray('initialPreviewThumbTags', tags); 1666 if ($thumb !== undefined) { 1667 if (!allFiles) { 1668 index = previewCache.add(self.id, content, config[0], tags[0], append); 1669 data = previewCache.get(self.id, index, false); 1670 $newThumb = $(data).hide(); 1671 $thumb.after($newThumb).fadeOut('slow', function () { 1672 $newThumb.fadeIn('slow').css('display:inline-block'); 1673 self._initPreviewActions(); 1674 self._clearFileInput(); 1675 $thumb.remove(); 1676 }); 1677 } else { 1678 i = $thumb.attr('data-fileindex'); 1679 self.uploadCache.content[i] = content[0]; 1680 self.uploadCache.config[i] = config[0] || []; 1681 self.uploadCache.tags[i] = tags[0] || []; 1682 self.uploadCache.append = append; 1683 } 1684 } else { 1685 previewCache.set(self.id, content, config, tags, append); 1686 self._initPreview(); 1687 self._initPreviewActions(); 1688 } 1689 } 1690 }, 1691 _initSuccessThumbs: function () { 1692 var self = this; 1693 if (!self.showPreview) { 1694 return; 1695 } 1696 self._getThumbs('.file-preview-success').each(function () { 1697 var $thumb = $(this), $remove = $thumb.find('.kv-file-remove'); 1698 $remove.removeAttr('disabled'); 1699 handler($remove, 'click', function () { 1700 var out = self._raise('filesuccessremove', [$thumb.attr('id'), $thumb.data('fileindex')]); 1701 cleanMemory($thumb); 1702 if (out === false) { 1703 return; 1704 } 1705 $thumb.fadeOut('slow', function () { 1706 $thumb.remove(); 1707 if (!self.$preview.find('.file-preview-frame').length) { 1708 self.reset(); 1709 } 1710 }); 1711 }); 1712 }); 1713 }, 1714 _checkAsyncComplete: function () { 1715 var self = this, previewId, i; 1716 for (i = 0; i < self.filestack.length; i++) { 1717 if (self.filestack[i]) { 1718 previewId = self.previewInitId + "-" + i; 1719 if ($.inArray(previewId, self.uploadLog) === -1) { 1720 return false; 1721 } 1722 } 1723 } 1724 return (self.uploadAsyncCount === self.uploadLog.length); 1725 }, 1726 _uploadExtra: function (previewId, index) { 1727 var self = this, data = self._getExtraData(previewId, index); 1728 if (data.length === 0) { 1729 return; 1730 } 1731 $.each(data, function (key, value) { 1732 self.formdata.append(key, value); 1733 }); 1734 }, 1735 _uploadSingle: function (i, files, allFiles) { 1736 var self = this, total = self.getFileStack().length, formdata = new FormData(), outData, 1737 previewId = self.previewInitId + "-" + i, $thumb, chkComplete, $btnUpload, $btnDelete, 1738 hasPostData = self.filestack.length > 0 || !$.isEmptyObject(self.uploadExtraData), 1739 fnBefore, fnSuccess, fnComplete, fnError, updateUploadLog, params = {id: previewId, index: i}; 1740 self.formdata = formdata; 1741 if (self.showPreview) { 1742 $thumb = $('#' + previewId + ':not(.file-preview-initial)'); 1743 $btnUpload = $thumb.find('.kv-file-upload'); 1744 $btnDelete = $thumb.find('.kv-file-remove'); 1745 $('#' + previewId).find('.file-thumb-progress').removeClass('hide'); 1746 } 1747 if (total === 0 || !hasPostData || ($btnUpload && $btnUpload.hasClass('disabled')) || self._abort(params)) { 1748 return; 1749 } 1750 updateUploadLog = function (i, previewId) { 1751 self.updateStack(i, undefined); 1752 self.uploadLog.push(previewId); 1753 if (self._checkAsyncComplete()) { 1754 self.fileBatchCompleted = true; 1755 } 1756 }; 1757 chkComplete = function () { 1758 var u = self.uploadCache; 1759 if (!self.fileBatchCompleted) { 1760 return; 1761 } 1762 setTimeout(function () { 1763 if (self.showPreview) { 1764 previewCache.set(self.id, u.content, u.config, u.tags, u.append); 1765 if (self.hasInitData) { 1766 self._initPreview(); 1767 self._initPreviewActions(); 1768 } 1769 } 1770 self.unlock(); 1771 self._clearFileInput(); 1772 self._raise('filebatchuploadcomplete', [self.filestack, self._getExtraData()]); 1773 self.uploadCount = 0; 1774 self.uploadStatus = {}; 1775 self.uploadLog = []; 1776 self._setProgress(101); 1777 }, 100); 1778 }; 1779 fnBefore = function (jqXHR) { 1780 outData = self._getOutData(jqXHR); 1781 self.fileBatchCompleted = false;
1782 if (self.showPreview) { 1783 if (!$thumb.hasClass('file-preview-success')) { 1784 self._setThumbStatus($thumb, 'Loading'); 1785 addCss($thumb, 'file-uploading'); 1786 } 1787 $btnUpload.attr('disabled', true); 1788 $btnDelete.attr('disabled', true); 1789 } 1790 if (!allFiles) { 1791 self.lock(); 1792 } 1793 self._raise('filepreupload', [outData, previewId, i]); 1794 $.extend(true, params, outData); 1795 if (self._abort(params)) { 1796 jqXHR.abort(); 1797 self._setProgressCancelled(); 1798 } 1799 }; 1800 fnSuccess = function (data, textStatus, jqXHR) { 1801 var pid = self.showPreview && $thumb.attr('id') ? $thumb.attr('id') : previewId; 1802 outData = self._getOutData(jqXHR, data); 1803 $.extend(true, params, outData); 1804 setTimeout(function () { 1805 if (isEmpty(data) || isEmpty(data.error)) { 1806 if (self.showPreview) { 1807 self._setThumbStatus($thumb, 'Success'); 1808 $btnUpload.hide(); 1809 self._initUploadSuccess(data, $thumb, allFiles); 1810 } 1811 self._raise('fileuploaded', [outData, pid, i]); 1812 if (!allFiles) { 1813 self.updateStack(i, undefined); 1814 } else { 1815 updateUploadLog(i, pid); 1816 } 1817 } else { 1818 self._showUploadError(data.error, params); 1819 self._setPreviewError($thumb, i); 1820 if (allFiles) { 1821 updateUploadLog(i, pid); 1822 } 1823 } 1824 }, 100); 1825 }; 1826 fnComplete = function () { 1827 setTimeout(function () { 1828 if (self.showPreview) { 1829 $btnUpload.removeAttr('disabled'); 1830 $btnDelete.removeAttr('disabled'); 1831 $thumb.removeClass('file-uploading'); 1832 self._setProgress(101, $('#' + previewId).find('.file-thumb-progress')); 1833 } 1834 if (!allFiles) { 1835 self.unlock(false); 1836 self._clearFileInput(); 1837 } else { 1838 chkComplete(); 1839 } 1840 self._initSuccessThumbs(); 1841 }, 100); 1842 }; 1843 fnError = function (jqXHR, textStatus, errorThrown) { 1844 var errMsg = self._parseError(jqXHR, errorThrown, (allFiles ? files[i].name : null)); 1845 setTimeout(function () { 1846 if (allFiles) { 1847 updateUploadLog(i, previewId); 1848 } 1849 self.uploadStatus[previewId] = 100; 1850 self._setPreviewError($thumb, i); 1851 $.extend(true, params, self._getOutData(jqXHR)); 1852 self._showUploadError(errMsg, params); 1853 }, 100); 1854 }; 1855 formdata.append(self.uploadFileAttr, files[i], self.filenames[i]); 1856 formdata.append('file_id', i); 1857 self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, previewId, i); 1858 }, 1859 _uploadBatch: function () { 1860 var self = this, files = self.filestack, total = files.length, params = {}, fnBefore, fnSuccess, fnError, 1861 fnComplete, hasPostData = self.filestack.length > 0 || !$.isEmptyObject(self.uploadExtraData), 1862 setAllUploaded; 1863 self.formdata = new FormData(); 1864 if (total === 0 || !hasPostData || self._abort(params)) { 1865 return; 1866 } 1867 setAllUploaded = function () { 1868 $.each(files, function (key) { 1869 self.updateStack(key, undefined); 1870 }); 1871 self._clearFileInput(); 1872 }; 1873 fnBefore = function (jqXHR) { 1874 self.lock(); 1875 var outData = self._getOutData(jqXHR); 1876 if (self.showPreview) { 1877 self._getThumbs().each(function () { 1878 var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'), 1879 $btnDelete = $thumb.find('.kv-file-remove'); 1880 if (!$thumb.hasClass('file-preview-success')) { 1881 self._setThumbStatus($thumb, 'Loading'); 1882 addCss($thumb, 'file-uploading'); 1883 } 1884 $btnUpload.attr('disabled', true); 1885 $btnDelete.attr('disabled', true); 1886 }); 1887 } 1888 self._raise('filebatchpreupload', [outData]); 1889 if (self._abort(outData)) { 1890 jqXHR.abort(); 1891 self._setProgressCancelled(); 1892 } 1893 }; 1894 fnSuccess = function (data, textStatus, jqXHR) { 1895 /** @namespace data.errorkeys */ 1896 var outData = self._getOutData(jqXHR, data), $thumbs = self._getThumbs(':not(.file-preview-error)'), key = 0, 1897 keys = isEmpty(data) || isEmpty(data.errorkeys) ? [] : data.errorkeys; 1898 if (isEmpty(data) || isEmpty(data.error)) { 1899 self._raise('filebatchuploadsuccess', [outData]); 1900 setAllUploaded(); 1901 if (self.showPreview) { 1902 $thumbs.each(function () { 1903 var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'); 1904 $thumb.find('.kv-file-upload').hide(); 1905 self._setThumbStatus($thumb, 'Success'); 1906 $thumb.removeClass('file-uploading'); 1907 $btnUpload.removeAttr('disabled'); 1908 }); 1909 self._initUploadSuccess(data); 1910 } else { 1911 self.reset(); 1912 } 1913 } else { 1914 if (self.showPreview) { 1915 $thumbs.each(function () { 1916 var $thumb = $(this), $btnDelete = $thumb.find('.kv-file-remove'), 1917 $btnUpload = $thumb.find('.kv-file-upload'); 1918 $thumb.removeClass('file-uploading'); 1919 $btnUpload.removeAttr('disabled'); 1920 $btnDelete.removeAttr('disabled'); 1921 if (keys.length === 0) { 1922 self._setPreviewError($thumb); 1923 return; 1924 } 1925 if ($.inArray(key, keys) !== -1) { 1926 self._setPreviewError($thumb); 1927 } else { 1928 $thumb.find('.kv-file-upload').hide(); 1929 self._setThumbStatus($thumb, 'Success'); 1930 self.updateStack(key, undefined); 1931 } 1932 key++; 1933 }); 1934 self._initUploadSuccess(data); 1935 } 1936 self._showUploadError(data.error, outData, 'filebatchuploaderror'); 1937 } 1938 }; 1939 fnComplete = function () { 1940 self._setProgress(101); 1941 self.unlock(); 1942 self._initSuccessThumbs(); 1943 self._clearFileInput(); 1944 self._raise('filebatchuploadcomplete', [self.filestack, self._getExtraData()]); 1945 }; 1946 fnError = function (jqXHR, textStatus, errorThrown) { 1947 var outData = self._getOutData(jqXHR), errMsg = self._parseError(jqXHR, errorThrown); 1948 self._showUploadError(errMsg, outData, 'filebatchuploaderror'); 1949 self.uploadFileCount = total - 1; 1950 if (!self.showPreview) { 1951 return; 1952 } 1953 self._getThumbs().each(function () { 1954 var $thumb = $(this), key = $thumb.attr('data-fileindex'); 1955 $thumb.removeClass('file-uploading'); 1956 if (self.filestack[key] !== undefined) { 1957 self._setPreviewError($thumb); 1958 } 1959 }); 1960 self._getThumbs().removeClass('file-uploading'); 1961 self._getThumbs(' .kv-file-upload').removeAttr('disabled'); 1962 self._getThumbs(' .kv-file-delete').removeAttr('disabled'); 1963 }; 1964 $.each(files, function (key, data) { 1965 if (!isEmpty(files[key])) { 1966 self.formdata.append(self.uploadFileAttr, data, self.filenames[key]); 1967 } 1968 }); 1969 self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError); 1970 }, 1971 _uploadExtraOnly: function () { 1972 var self = this, params = {}, fnBefore, fnSuccess, fnComplete, fnError; 1973 self.formdata = new FormData(); 1974 if (self._abort(params)) { 1975 return; 1976 } 1977 fnBefore = function (jqXHR) { 1978 self.lock(); 1979 var outData = self._getOutData(jqXHR); 1980 self._raise('filebatchpreupload', [outData]); 1981 self._setProgress(50); 1982 params.data = outData; 1983 params.xhr = jqXHR; 1984 if (self._abort(params)) { 1985 jqXHR.abort(); 1986 self._setProgressCancelled(); 1987 } 1988 }; 1989 fnSuccess = function (data, textStatus, jqXHR) { 1990 var outData = self._getOutData(jqXHR, data); 1991 if (isEmpty(data) || isEmpty(data.error)) { 1992 self._raise('filebatchuploadsuccess', [outData]); 1993 self._clearFileInput(); 1994 self._initUploadSuccess(data); 1995 } else { 1996 self._showUploadError(data.error, outData, 'filebatchuploaderror'); 1997 } 1998 }; 1999 fnComplete = function () { 2000 self._setProgress(101); 2001 self.unlock(); 2002 self._clearFileInput(); 2003 self._raise('filebatchuploadcomplete', [self.filestack, self._getExtraData()]); 2004 }; 2005 fnError = function (jqXHR, textStatus, errorThrown) { 2006 var outData = self._getOutData(jqXHR), errMsg = self._parseError(jqXHR, errorThrown); 2007 params.data = outData; 2008 self._showUploadError(errMsg, outData, 'filebatchuploaderror'); 2009 }; 2010 self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError); 2011 }, 2012 _initFileActions: function () { 2013 var self = this; 2014 if (!self.showPreview) { 2015 return; 2016 } 2017 self._initZoomButton(); 2018 self.$preview.find('.kv-file-remove').each(function () { 2019 var $el = $(this), $frame = $el.closest('.file-preview-frame'), hasError, 2020 id = $frame.attr('id'), ind = $frame.attr('data-fileindex'), n, cap, status; 2021 handler($el, 'click', function () { 2022 status = self._raise('filepreremove', [id, ind]); 2023 if (status === false || !self._validateMinCount()) { 2024 return false;
2025 } 2026 hasError = $frame.hasClass('file-preview-error'); 2027 cleanMemory($frame); 2028 $frame.fadeOut('slow', function () { 2029 self.updateStack(ind, undefined); 2030 self._clearObjects($frame); 2031 $frame.remove(); 2032 if (id && hasError) { 2033 self.$errorContainer.find('li[data-file-id="' + id + '"]').fadeOut('fast', function () { 2034 $(this).remove(); 2035 if (!self._errorsExist()) { 2036 self._resetErrors(); 2037 } 2038 }); 2039 } 2040 self._clearFileInput(); 2041 var filestack = self.getFileStack(true), chk = previewCache.count(self.id), 2042 len = filestack.length, 2043 hasThumb = self.showPreview && self.$preview.find('.file-preview-frame').length; 2044 if (len === 0 && chk === 0 && !hasThumb) { 2045 self.reset(); 2046 } else { 2047 n = chk + len; 2048 cap = n > 1 ? self._getMsgSelected(n) : (filestack[0] ? self._getFileNames()[0] : ''); 2049 self._setCaption(cap); 2050 } 2051 self._raise('fileremoved', [id, ind]); 2052 }); 2053 }); 2054 }); 2055 self.$preview.find('.kv-file-upload').each(function () { 2056 var $el = $(this); 2057 handler($el, 'click', function () { 2058 var $frame = $el.closest('.file-preview-frame'), 2059 ind = $frame.attr('data-fileindex'); 2060 if (!$frame.hasClass('file-preview-error')) { 2061 self._uploadSingle(ind, self.filestack, false); 2062 } 2063 }); 2064 }); 2065 }, 2066 _hideFileIcon: function () { 2067 if (this.overwriteInitial) { 2068 this.$captionContainer.find('.kv-caption-icon').hide(); 2069 } 2070 }, 2071 _showFileIcon: function () { 2072 this.$captionContainer.find('.kv-caption-icon').show(); 2073 }, 2074 _getSize: function (bytes) { 2075 var self = this, size = parseFloat(bytes); 2076 if (!bytes || !size || isNaN(bytes) || isNaN(size)) { 2077 return self._getLayoutTemplate('size').replace('{sizeText}', '0.00 KB'); 2078 } 2079 var i, func = self.fileSizeGetter, sizes, out; 2080 if (typeof func === 'function') { 2081 out = func(bytes); 2082 } else { 2083 i = Math.floor(Math.log(size) / Math.log(1024)); 2084 sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 2085 out = (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i]; 2086 } 2087 return self._getLayoutTemplate('size').replace('{sizeText}', out); 2088 }, 2089 _generatePreviewTemplate: function (cat, data, fname, ftype, previewId, isError, size, frameClass, foot, ind) { 2090 var self = this, tmplt = self._getPreviewTemplate(cat), content, sText, css = frameClass || '', 2091 config = ifSet(cat, self.previewSettings, defaultPreviewSettings[cat]), caption = self.slug(fname), 2092 footer = foot || self._renderFileFooter(caption, size, config.width, isError); 2093 ind = ind || previewId.slice(previewId.lastIndexOf('-') + 1); 2094 tmplt = self._parseFilePreviewIcon(tmplt, fname); 2095 if (cat === 'text' || cat === 'html') { 2096 sText = cat === 'text' ? htmlEncode(data) : data; 2097 content = tmplt.replace(/\{previewId}/g, previewId).replace(/\{caption}/g, caption) 2098 .replace(/\{width}/g, config.width).replace(/\{height}/g, config.height) 2099 .replace(/\{frameClass}/g, css).replace(/\{cat}/g, ftype) 2100 .replace(/\{footer}/g, footer).replace(/\{fileindex}/g, ind) 2101 .replace(/\{data}/g, sText).replace(/\{template}/g, cat); 2102 } else { 2103 content = tmplt.replace(/\{previewId}
2103/g, previewId).replace(/\{caption}/g, caption) 2104 .replace(/\{frameClass}/g, css).replace(/\{type}/g, ftype).replace(/\{fileindex}/g, ind) 2105 .replace(/\{width}/g, config.width).replace(/\{height}/g, config.height) 2106 .replace(/\{footer}/g, footer).replace(/\{data}/g, data).replace(/\{template}/g, cat); 2107 } 2108 return content; 2109 }, 2110 _previewDefault: function (file, previewId, isDisabled) { 2111 var self = this, $preview = self.$preview, $previewLive = $preview.find('.file-live-thumbs'); 2112 if (!self.showPreview) { 2113 return; 2114 } 2115 var fname = file ? file.name : '', ftype = file ? file.type : '', content, 2116 isError = isDisabled === true && !self.isUploadable, data = objUrl.createObjectURL(file); 2117 self._clearDefaultPreview(); 2118 content = self._generatePreviewTemplate('other', data, fname, ftype, previewId, isError, file.size); 2119 if (!$previewLive.length) { 2120 $previewLive = $(document.createElement('div')).addClass('file-live-thumbs').appendTo($preview); 2121 } 2122 $previewLive.append("\n" + content); 2123 if (isDisabled === true && self.isUploadable) { 2124 self._setThumbStatus($('#' + previewId), 'Error'); 2125 } 2126 }, 2127 _previewFile: function (i, file, theFile, previewId, data) { 2128 if (!this.showPreview) { 2129 return; 2130 } 2131 var self = this, cat = self._parseFileType(file), fname = file ? file.name : '', caption = self.slug(fname), 2132 types = self.allowedPreviewTypes, mimes = self.allowedPreviewMimeTypes, $preview = self.$preview, 2133 chkTypes = types && types.indexOf(cat) >= 0, $previewLive = $preview.find('.file-live-thumbs'), 2134 iData = (cat === 'text' || cat === 'html' || cat === 'image') ? theFile.target.result : data, content, 2135 chkMimes = mimes && mimes.indexOf(file.type) !== -1; 2136 if (!$previewLive.length) { 2137 $previewLive = $(document.createElement('div')).addClass('file-live-thumbs').appendTo($preview); 2138 } 2139 /** @namespace window.DOMPurify */ 2140 if (cat === 'html' && self.purifyHtml && window.DOMPurify) { 2141 iData = window.DOMPurify.sanitize(iData); 2142 } 2143 if (chkTypes || chkMimes) { 2144 content = self._generatePreviewTemplate(cat, iData, fname, file.type, previewId, false, file.size); 2145 self._clearDefaultPreview(); 2146 $previewLive.append("\n" + content); 2147 self._validateImage(i, previewId, caption, file.type); 2148 } else { 2149 self._previewDefault(file, previewId); 2150 } 2151 self._initSortable(); 2152 }, 2153 _slugDefault: function (text) { 2154 return isEmpty(text) ? '' : String(text).replace(/[\-\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g, '_'); 2155 }, 2156 _readFiles: function (files) { 2157 this.reader = new FileReader(); 2158 var self = this, $el = self.$element, $preview = self.$preview, reader = self.reader, 2159 $container = self.$previewContainer, $status = self.$previewStatus, msgLoading = self.msgLoading, 2160 msgProgress = self.msgProgress, previewInitId = self.previewInitId, numFiles = files.length, 2161 settings = self.fileTypeSettings, ctr = self.filestack.length, readFile, 2162 maxPreviewSize = self.maxFilePreviewSize && parseFloat(self.maxFilePreviewSize), 2163 canPreview = $preview.length && (!maxPreviewSize || isNaN(maxPreviewSize)), 2164 throwError = function (msg, file, previewId, index) { 2165 var p1 = $.extend(true, {}, self._getOutData({}, {}, files), {id: previewId, index: index}), 2166 p2 = {id: previewId, index: index, file: file, files: files}; 2167 self._previewDefault(file, previewId, true); 2168 if (self.isUploadable) { 2169 self.addToStack(undefined); 2170 setTimeout(function () { 2171 readFile(index + 1); 2172 }, 100); 2173 } 2174 self._initFileActions(); 2175 if (self.removeFromPreviewOnError) { 2176 $('#' + previewId).remove(); 2177 } 2178 return self.isUploadable ? self._showUploadError(msg, p1) : self._showError(msg, p2); 2179 }; 2180 2181 self.loadedImages = []; 2182 self.totalImagesCount = 0; 2183 2184 $.each(files, function (key, file) { 2185 var func = self.fileTypeSettings.image || defaultFileTypeSettings.image; 2186 if (func && func(file.type)) { 2187 self.totalImagesCount++; 2188 } 2189 }); 2190 readFile = function (i) { 2191 if (isEmpty($el.attr('multiple'))) { 2192 numFiles = 1; 2193 } 2194 if (i >= numFiles) { 2195 if (self.isUploadable && self.filestack.length > 0) { 2196 self._raise('filebatchselected', [self.getFileStack()]); 2197 } else { 2198 self._raise('filebatchselected', [files]); 2199 } 2200 $container.removeClass('file-thumb-loading'); 2201 $status.html(''); 2202 return; 2203 } 2204 var node = ctr + i, previewId = previewInitId + "-" + node, isText, isImage, file = files[i], f
2204SizeKB, 2205 caption = file.name ? self.slug(file.name) : '', fileSize = (file.size || 0) / 1000, checkFile, 2206 fileExtExpr = '', previewData = objUrl.createObjectURL(file), fileCount = 0, j, msg, typ, chk, 2207 fileTypes = self.allowedFileTypes, strTypes = isEmpty(fileTypes) ? '' : fileTypes.join(', '), 2208 fileExt = self.allowedFileExtensions, strExt = isEmpty(fileExt) ? '' : fileExt.join(', '); 2209 2210 if (caption === false) { 2211 readFile(i + 1); 2212 return; 2213 } 2214 if (caption.length === 0) { 2215 msg = self.msgInvalidFileName.replace('{name}', htmlEncode(file.name)); 2216 self.isError = throwError(msg, file, previewId, i); 2217 return; 2218 } 2219 if (!isEmpty(fileExt)) { 2220 fileExtExpr = new RegExp('\\.(' + fileExt.join('|') + ')$', 'i'); 2221 } 2222 fSizeKB = fileSize.toFixed(2); 2223 if (self.maxFileSize > 0 && fileSize > self.maxFileSize) { 2224 msg = self.msgSizeTooLarge.replace('{name}', caption).replace('{size}', fSizeKB) 2225 .replace('{maxSize}', self.maxFileSize); 2226 self.isError = throwError(msg, file, previewId, i); 2227 return; 2228 } 2229 if (self.minFileSize !== null && fileSize <= getNum(self.minFileSize)) { 2230 msg = self.msgSizeTooSmall.replace('{name}', caption).replace('{size}', fSizeKB) 2231 .replace('{minSize}', self.minFileSize); 2232 self.isError = throwError(msg, file, previewId, i); 2233 return; 2234 } 2235 if (!isEmpty(fileTypes) && isArray(fileTypes)) { 2236 for (j = 0; j < fileTypes.length; j += 1) { 2237 typ = fileTypes[j]; 2238 checkFile = settings[typ]; 2239 chk = (checkFile !== undefined && checkFile(file.type, caption)); 2240 fileCount += isEmpty(chk) ? 0 : chk.length; 2241 } 2242 if (fileCount === 0) { 2243 msg = self.msgInvalidFileType.replace('{name}', caption).replace('{types}', strTypes); 2244 self.isError = throwError(msg, file, previewId, i); 2245 return; 2246 } 2247 } 2248 if (fileCount === 0 && !isEmpty(fileExt) && isArray(fileExt) && !isEmpty(fileExtExpr)) { 2249 chk = compare(caption, fileExtExpr); 2250 fileCount += isEmpty(chk) ? 0 : chk.length; 2251 if (fileCount === 0) { 2252 msg = self.msgInvalidFileExtension.replace('{name}', caption).replace('{extensions}', strExt); 2253 self.isError = throwError(msg, file, previewId, i); 2254 return; 2255 } 2256 } 2257 if (!self.showPreview) { 2258 self.addToStack(file); 2259 setTimeout(function () { 2260 readFile(i + 1); 2261 }, 100); 2262 self._raise('fileloaded', [file, previewId, i, reader]); 2263 return; 2264 } 2265 if (!canPreview && fileSize > maxPreviewSize) { 2266 self.addToStack(file); 2267 $container.addClass('file-thumb-loading'); 2268 self._previewDefault(file, previewId); 2269 self._initFileActions(); 2270 self._updateFileDetails(numFiles); 2271 readFile(i + 1); 2272 return; 2273 } 2274 if ($preview.length && FileReader !== undefined) { 2275 $status.html(msgLoading.replace('{index}', i + 1).replace('{files}', numFiles)); 2276 $container.addClass('file-thumb-loading'); 2277 reader.onerror = function (evt) { 2278 self._errorHandler(evt, caption); 2279 }; 2280 reader.onload = function (theFile) { 2281 self._previewFile(i, file, theFile, previewId, previewData); 2282 self._initFileActions(); 2283 }; 2284 reader.onloadend = function () { 2285 msg = msgProgress.replace('{index}', i + 1).replace('{files}', numFiles) 2286 .replace('{percent}', 50).replace('{name}', caption); 2287 setTimeout(function () { 2288 $status.html(msg); 2289 self._updateFileDetails(numFiles); 2290 readFile(i + 1); 2291 }, 100); 2292 self._raise('fileloaded', [file, previewId, i, reader]); 2293 }; 2294 reader.onprogress = function (data) { 2295 if (data.lengthComputable) { 2296 var fact = (data.loaded / data.total) * 100, progress = Math.ceil(fact); 2297 msg = msgProgress.replace('{index}', i + 1).replace('{files}', numFiles) 2298 .replace('{percent}', progress).replace('{name}', caption); 2299 setTimeout(function () { 2300 $status.html(msg); 2301 }, 100); 2302 } 2303 }; 2304 isText = ifSet('text', settings, defaultFileTypeSettings.text); 2305 isImage = ifSet('image', settings, defaultFileTypeSettings.image); 2306 2307 if (isText(file.type, caption)) { 2308 reader.readAsText(file, self.textEncoding); 2309 } else { 2310 if (isImage(file.type, caption)) {
2311 reader.readAsDataURL(file); 2312 } else { 2313 reader.readAsArrayBuffer(file); 2314 } 2315 } 2316 } else { 2317 self._previewDefault(file, previewId); 2318 setTimeout(function () { 2319 readFile(i + 1); 2320 self._updateFileDetails(numFiles); 2321 }, 100); 2322 self._raise('fileloaded', [file, previewId, i, reader]); 2323 } 2324 self.addToStack(file); 2325 }; 2326 2327 readFile(0); 2328 self._updateFileDetails(numFiles, false); 2329 }, 2330 _updateFileDetails: function (numFiles) { 2331 var self = this, $el = self.$element, fileStack = self.getFileStack(), 2332 name = (isIE(9) && findFileName($el.val())) || 2333 ($el[0].files[0] && $el[0].files[0].name) || (fileStack.length && fileStack[0].name) || '', 2334 label = self.slug(name), n = self.isUploadable ? fileStack.length : numFiles, 2335 nFiles = previewCache.count(self.id) + n, log = n > 1 ? self._getMsgSelected(nFiles) : label; 2336 if (self.isError) { 2337 self.$previewContainer.removeClass('file-thumb-loading'); 2338 self.$previewStatus.html(''); 2339 self.$captionContainer.find('.kv-caption-icon').hide(); 2340 } else { 2341 self._showFileIcon(); 2342 } 2343 self._setCaption(log, self.isError); 2344 self.$container.removeClass('file-input-new file-input-ajax-new'); 2345 if (arguments.length === 1) { 2346 self._raise('fileselect', [numFiles, label]); 2347 } 2348 if (previewCache.count(self.id)) { 2349 self._initPreviewActions(); 2350 } 2351 }, 2352 _setThumbStatus: function ($thumb, status) { 2353 var self = this; 2354 if (!self.showPreview) { 2355 return; 2356 } 2357 var icon = 'indicator' + status, msg = icon + 'Title', 2358 css = 'file-preview-' + status.toLowerCase(), 2359 $indicator = $thumb.find('.file-upload-indicator'), 2360 config = self.fileActionSettings; 2361 $thumb.removeClass('file-preview-success file-preview-error file-preview-loading'); 2362 if (status === 'Error') { 2363 $thumb.find('.kv-file-upload').attr('disabled', true); 2364 } 2365 if (status === 'Success') { 2366 $thumb.find('.file-drag-handle').remove(); 2367 $indicator.css('margin-left', 0); 2368 } 2369 $indicator.html(config[icon]); 2370 $indicator.attr('title', config[msg]); 2371 $thumb.addClass(css); 2372 }, 2373 _setProgressCancelled: function () { 2374 var self = this; 2375 self._setProgress(101, self.$progress, self.msgCancelled); 2376 }, 2377 _setProgress: function (p, $el, error) { 2378 var self = this, pct = Math.min(p, 100), template = pct < 100 ? self.progressTemplate : 2379 (error ? self.progressErrorTemplate : (p <= 100 ? self.progressTemplate : self.progressCompleteTemplate)), 2380 pctLimit = self.progressUploadThreshold; 2381 $el = $el || self.$progress; 2382 if (!isEmpty(template)) { 2383 if (pctLimit && pct > pctLimit && p <= 100) { 2384 var out = template.replace('{percent}', pctLimit).replace('{percent}', pctLimit).replace('{percent}%', self.msgUploadThreshold); 2385 $el.html(out); 2386 } else { 2387 $el.html(template.replace(/\{percent}/g, pct)); 2388 } 2389 if (error) { 2390 $el.find('[role="progressbar"]').html(error); 2391 } 2392 } 2393 }, 2394 _setFileDropZoneTitle: function () { 2395 var self = this, $zone = self.$container.find('.file-drop-zone'), title = self.dropZoneTitle, strFiles; 2396 if (self.isClickable) { 2397 strFiles = isEmpty(self.$element.attr('multiple')) ? self.fileSingle : self.filePlural; 2398 title += self.dropZoneClickTitle.replace('{files}', strFiles); 2399 } 2400 $zone.find('.' + self.dropZoneTitleClass).remove(); 2401 if (!self.isUploadable || !self.showPreview || $zone.length === 0 || self.getFileStack().length > 0 || !self.dropZoneEnabled) { 2402 return; 2403 } 2404 if ($zone.find('.file-preview-frame').length === 0 && isEmpty(self.defaultPreviewContent)) { 2405 $zone.prepend('<div class="' + self.dropZoneTitleClass + '">' + title + '</div>'); 2406 } 2407 self.$container.removeClass('file-input-new'); 2408 addCss(self.$container, 'file-input-ajax-new'); 2409 }, 2410 _setAsyncUploadStatus: function (previewId, pct, total) { 2411 var self = this, sum = 0; 2412 self._setProgress(pct, $('#' + previewId).find('.file-thumb-progress')); 2413 self.uploadStatus[previewId] = pct; 2414 $.each(self.uploadStatus, function (key, value) { 2415 sum += value; 2416 }); 2417 self._setProgress(Math.floor(sum / total)); 2418 2419 }, 2420 _validateMinCount: function () { 2421 var self = this, len = self.isUploadable ? self.getFileStack().length : self.$element.get(0).files.length; 2422 if (self.validateInitialCount && self.minFileCount > 0 && self._getFileCount(len - 1) < self.minFileCount) { 2423 self._noFilesError({}); 2424 return false;
2425 } 2426 return true; 2427 }, 2428 _getFileCount: function (fileCount) { 2429 var self = this, addCount = 0; 2430 if (self.validateInitialCount && !self.overwriteInitial) { 2431 addCount = previewCache.count(self.id); 2432 fileCount += addCount; 2433 } 2434 return fileCount; 2435 }, 2436 _getFileName: function (file) { 2437 return file && file.name ? this.slug(file.name) : undefined; 2438 }, 2439 _getFileNames: function (skipNull) { 2440 var self = this; 2441 return self.filenames.filter(function (n) { 2442 return (skipNull ? n !== undefined : n !== undefined && n !== null); 2443 }); 2444 }, 2445 _setPreviewError: function ($thumb, i, val) { 2446 var self = this; 2447 if (i !== undefined) { 2448 self.updateStack(i, val); 2449 } 2450 if (self.removeFromPreviewOnError) { 2451 $thumb.remove(); 2452 } else { 2453 self._setThumbStatus($thumb, 'Error'); 2454 } 2455 }, 2456 _checkDimensions: function (i, chk, $img, $thumb, fname, type, params) { 2457 var self = this, msg, dim, tag = chk === 'Small' ? 'min' : 'max', limit = self[tag + 'Image' + type], 2458 $imgEl, isValid; 2459 if (isEmpty(limit) || !$img.length) { 2460 return; 2461 } 2462 $imgEl = $img[0]; 2463 dim = (type === 'Width') ? $imgEl.naturalWidth || $imgEl.width : $imgEl.naturalHeight || $imgEl.height; 2464 isValid = chk === 'Small' ? dim >= limit : dim <= limit; 2465 if (isValid) { 2466 return; 2467 } 2468 msg = self['msgImage' + type + chk].replace('{name}', fname).replace('{size}', limit); 2469 self._showUploadError(msg, params); 2470 self._setPreviewError($thumb, i, null); 2471 }, 2472 _validateImage: function (i, previewId, fname, ftype) { 2473 var self = this, $preview = self.$preview, params, w1, w2, 2474 $thumb = $preview.find("#" + previewId), $img = $thumb.find('img'); 2475 fname = fname || 'Untitled'; 2476 if (!$img.length) { 2477 return; 2478 } 2479 handler($img, 'load', function () { 2480 w1 = $thumb.width(); 2481 w2 = $preview.width(); 2482 if (w1 > w2) { 2483 $img.css('width', '100%'); 2484 $thumb.css('width', '97%'); 2485 } 2486 params = {ind: i, id: previewId}; 2487 self._checkDimensions(i, 'Small', $img, $thumb, fname, 'Width', params); 2488 self._checkDimensions(i, 'Small', $img, $thumb, fname, 'Height', params); 2489 if (!self.resizeImage) { 2490 self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Width', params); 2491 self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Height', params); 2492 } 2493 self._raise('fileimageloaded', [previewId]); 2494 self.loadedImages.push({ind: i, img: $img, thumb: $thumb, pid: previewId, typ: ftype}); 2495 self._validateAllImages(); 2496 }); 2497 }, 2498 _validateAllImages: function () { 2499 var self = this, i, config, $img, $thumb, pid, ind, params = {}, errFunc; 2500 if (self.loadedImages.length !== self.totalImagesCount) { 2501 return; 2502 } 2503 self._raise('fileimagesloaded'); 2504 if (!self.resizeImage) { 2505 return; 2506 } 2507 errFunc = self.isUploadable ? self._showUploadError : self._showError; 2508 var counter = {val: 0}; 2509 for (i = 0; i < self.loadedImages.length; i++) { 2510 config = self.loadedImages[i]; 2511 $img = config.img; 2512 $thumb = config.thumb; 2513 pid = config.pid; 2514 ind = config.ind; 2515 params = {id: pid, 'index': ind}; 2516 if (!self._getResizedImage($img[0], config.typ, pid, ind, counter, self.loadedImages.length)) { 2517 errFunc(self.msgImageResizeError, params, 'fileimageresizeerror'); 2518 self._setPreviewError($thumb, ind); 2519 } 2520 } 2521 }, 2522 _getResizedImage: function (image, type, pid, ind, counter, num_imgs) {
2523 var self = this, width = image.naturalWidth, height = image.naturalHeight, ratio = 1, 2524 maxWidth = self.maxImageWidth || width, maxHeight = self.maxImageHeight || height, 2525 isValidImage = (width && height), chkWidth, chkHeight, 2526 canvas = self.imageCanvas, context = self.imageCanvasContext; 2527 if (!isValidImage) { 2528 counter.val++; 2529 if (counter.val === num_imgs) { 2530 self._raise('fileimagesresized'); 2531 } 2532 return false; 2533 } 2534 if (width === maxWidth && height === maxHeight) { 2535 self._raise('fileimageresized', [pid, ind]); 2536 counter.val++; 2537 if (counter.val === num_imgs) { 2538 self._raise('fileimagesresized'); 2539 } 2540 return true; 2541 } 2542 type = type || self.resizeDefaultImageType; 2543 chkWidth = width > maxWidth; 2544 chkHeight = height > maxHeight; 2545 if (self.resizePreference === 'width') { 2546 ratio = chkWidth ? maxWidth / width : (chkHeight ? maxHeight / height : 1); 2547 } else { 2548 ratio = chkHeight ? maxHeight / height : (chkWidth ? maxWidth / width : 1); 2549 } 2550 self._resetCanvas(); 2551 width *= ratio; 2552 height *= ratio; 2553 canvas.width = width; 2554 canvas.height = height; 2555 try { 2556 context.drawImage(image, 0, 0, width, height); 2557 canvas.toBlob(function (blob) { 2558 self.filestack[ind] = blob; 2559 self._raise('fileimageresized', [pid, ind]); 2560 counter.val++; 2561 if (counter.val === num_imgs) { 2562 self._raise('fileimagesresized', [undefined, undefined]); 2563 } 2564 }, type, self.resizeQuality); 2565 return true; 2566 } 2567 catch (err) { 2568 counter.val++; 2569 if (counter.val === num_imgs) { 2570 self._raise('fileimagesresized', [undefined, undefined]); 2571 } 2572 return false; 2573 } 2574 }, 2575 _initBrowse: function ($container) { 2576 var self = this; 2577 if (self.showBrowse) { 2578 self.$btnFile = $container.find('.btn-file'); 2579 self.$btnFile.append(self.$element); 2580 } else { 2581 self.$element.hide(); 2582 } 2583 }, 2584 _initCaption: function () { 2585 var self = this, cap = self.initialCaption || ''; 2586 if (self.overwriteInitial || isEmpty(cap)) { 2587 self.$caption.html(''); 2588 return false; 2589 } 2590 self._setCaption(cap); 2591 return true; 2592 }, 2593 _setCaption: function (content, isError) { 2594 var self = this, title, out, n, cap, stack = self.getFileStack(); 2595 if (!self.$caption.length) { 2596 return; 2597 } 2598 if (isError) { 2599 title = $('<div>' + self.msgValidationError + '</div>').text(); 2600 n = stack.length; 2601 if (n) { 2602 cap = n === 1 && stack[0] ? self._getFileNames()[0] : self._getMsgSelected(n); 2603 } else { 2604 cap = self._getMsgSelected(self.msgNo); 2605 } 2606 out = '<span class="' + self.msgValidationErrorClass + '">' + self.msgValidationErrorIcon + 2607 (isEmpty(content) ? cap : content) + '</span>'; 2608 } else { 2609 if (isEmpty(content)) { 2610 return; 2611 } 2612 title = $('<div>' + content + '</div>').text(); 2613 out = self._getLayoutTemplate('fileIcon') + title; 2614 } 2615 self.$caption.html(out); 2616 self.$caption.attr('title', title); 2617 self.$captionContainer.find('.file-caption-ellipsis').attr('title', title); 2618 }, 2619 _createContainer: function () { 2620 var self = this, $container = $(document.createElement("div")) 2621 .attr({"class": 'file-input file-input-new'}) 2622 .html(self._renderMain()); 2623 self.$element.before($container); 2624 self._initBrowse($container); 2625 if (self.theme) { 2626 $container.addClass('theme-' + self.theme); 2627 } 2628 return $container; 2629 }, 2630 _refreshContainer: function () { 2631 var self = this, $container = self.$container; 2632 $container.before(self.$element); 2633 $container.html(self._renderMain()); 2634 self._initBrowse($container); 2635 }, 2636 _renderMain: function () { 2637 var self = this, dropCss = (self.isUploadable && self.dropZoneEnabled) ? ' file-drop-zone' : 'file-drop-disabled', 2638 close = !self.showClose ? '' : self._getLayoutTemplate('close'), 2639 preview = !self.showPreview ? '' : self._getLayoutTemplate('preview')
2640 .replace(/\{class}/g, self.previewClass) 2641 .replace(/\{dropClass}/g, dropCss), 2642 css = self.isDisabled ? self.captionClass + ' file-caption-disabled' : self.captionClass, 2643 caption = self.captionTemplate.replace(/\{class}/g, css + ' kv-fileinput-caption'); 2644 return self.mainTemplate.replace(/\{class}/g, self.mainClass + 2645 (!self.showBrowse && self.showCaption ? ' no-browse' : '')) 2646 .replace(/\{preview}/g, preview) 2647 .replace(/\{close}/g, close) 2648 .replace(/\{caption}/g, caption) 2649 .replace(/\{upload}/g, self._renderButton('upload')) 2650 .replace(/\{remove}/g, self._renderButton('remove')) 2651 .replace(/\{cancel}/g, self._renderButton('cancel')) 2652 .replace(/\{browse}/g, self._renderButton('browse')); 2653 }, 2654 _renderButton: function (type) { 2655 var self = this, tmplt = self._getLayoutTemplate('btnDefault'), css = self[type + 'Class'], 2656 title = self[type + 'Title'], icon = self[type + 'Icon'], label = self[type + 'Label'], 2657 status = self.isDisabled ? ' disabled' : '', btnType = 'button'; 2658 switch (type) { 2659 case 'remove': 2660 if (!self.showRemove) { 2661 return ''; 2662 } 2663 break; 2664 case 'cancel': 2665 if (!self.showCancel) { 2666 return ''; 2667 } 2668 css += ' hide'; 2669 break; 2670 case 'upload': 2671 if (!self.showUpload) { 2672 return ''; 2673 } 2674 if (self.isUploadable && !self.isDisabled) { 2675 tmplt = self._getLayoutTemplate('btnLink').replace('{href}', self.uploadUrl); 2676 } else { 2677 btnType = 'submit'; 2678 } 2679 break; 2680 case 'browse': 2681 if (!self.showBrowse) { 2682 return ''; 2683 } 2684 tmplt = self._getLayoutTemplate('btnBrowse'); 2685 break; 2686 default: 2687 return ''; 2688 } 2689 2690 css += type === 'browse' ? ' btn-file' : ' fileinput-' + type + ' fileinput-' + type + '-button'; 2691 if (!isEmpty(label)) { 2692 label = ' <span class="' + self.buttonLabelClass + '">' + label + '</span>'; 2693 } 2694 return tmplt.replace('{type}', btnType).replace('{css}', css).replace('{title}', title) 2695 .replace('{status}', status).replace('{icon}', icon).replace('{label}', label); 2696 }, 2697 _renderThumbProgress: function () { 2698 return '<div class="file-thumb-progress hide">' + this.progressTemplate.replace(/\{percent}/g, 2699 '0') + '</div>'; 2700 }, 2701 _renderFileFooter: function (caption, size, width, isError) { 2702 var self = this, config = self.fileActionSettings, rem = config.showRemove, drg = config.showDrag, 2703 upl = config.showUpload, zoom = config.showZoom, out, template = self._getLayoutTemplate('footer'), 2704 indicator = isError ? config.indicatorError : config.indicatorNew, 2705 title = isError ? config.indicatorErrorTitle : config.indicatorNewTitle; 2706 size = self._getSize(size); 2707 if (self.isUploadable) { 2708 out = template.replace(/\{actions}/g, self._renderFileActions(upl, rem, zoom, drg, false, false, false)) 2709 .replace(/\{caption}/g, caption).replace(/\{size}/g, size).replace(/\{width}/g, width) 2710 .replace(/\{progress}/g, self._renderThumbProgress()).replace(/\{indicator}/g, indicator) 2711 .replace(/\{indicatorTitle}/g, title); 2712 } else { 2713 out = template.replace(/\{actions}/g, 2714 self._renderFileActions(false, false, zoom, drg, false, false, false))
2715 .replace(/\{caption}/g, caption).replace(/\{size}/g, size).replace(/\{width}/g, width) 2716 .replace(/\{progress}/g, '').replace(/\{indicator}/g, indicator) 2717 .replace(/\{indicatorTitle}/g, title); 2718 } 2719 out = replaceTags(out, self.previewThumbTags); 2720 return out; 2721 }, 2722 _renderFileActions: function (showUpload, showDelete, showZoom, showDrag, disabled, url, key, isInit) { 2723 if (!showUpload && !showDelete && !showZoom && !showDrag) { 2724 return ''; 2725 } 2726 var self = this, 2727 vUrl = url === false ? '' : ' data-url="' + url + '"', 2728 vKey = key === false ? '' : ' data-key="' + key + '"', 2729 btnDelete = '', btnUpload = '', btnZoom = '', btnDrag = '', css, 2730 template = self._getLayoutTemplate('actions'), config = self.fileActionSettings, 2731 otherButtons = self.otherActionButtons.replace(/\{dataKey}/g, vKey), 2732 removeClass = disabled ? config.removeClass + ' disabled' : config.removeClass; 2733 if (showDelete) { 2734 btnDelete = self._getLayoutTemplate('actionDelete') 2735 .replace(/\{removeClass}/g, removeClass) 2736 .replace(/\{removeIcon}/g, config.removeIcon) 2737 .replace(/\{removeTitle}/g, config.removeTitle) 2738 .replace(/\{dataUrl}/g, vUrl) 2739 .replace(/\{dataKey}/g, vKey); 2740 } 2741 if (showUpload) { 2742 btnUpload = self._getLayoutTemplate('actionUpload') 2743 .replace(/\{uploadClass}/g, config.uploadClass) 2744 .replace(/\{uploadIcon}/g, config.uploadIcon) 2745 .replace(/\{uploadTitle}/g, config.uploadTitle); 2746 } 2747 if (showZoom) { 2748 btnZoom = self._getLayoutTemplate('actionZoom') 2749 .replace(/\{zoomClass}/g, config.zoomClass) 2750 .replace(/\{zoomIcon}/g, config.zoomIcon) 2751 .replace(/\{zoomTitle}/g, config.zoomTitle); 2752 } 2753 if (showDrag && isInit) { 2754 css = 'drag-handle-init ' + config.dragClass; 2755 btnDrag = self._getLayoutTemplate('actionDrag').replace(/\{dragClass}/g, css) 2756 .replace(/\{dragTitle}/g, config.dragTitle) 2757 .replace(/\{dragIcon}/g, config.dragIcon); 2758 } 2759 return template.replace(/\{delete}/g, btnDelete) 2760 .replace(/\{upload}/g, btnUpload) 2761 .replace(/\{zoom}/g, btnZoom) 2762 .replace(/\{drag}/g, btnDrag) 2763 .replace(/\{other}/g, otherButtons); 2764 }, 2765 _browse: function (e) { 2766 var self = this; 2767 self._raise('filebrowse'); 2768 if (e && e.isDefaultPrevented()) { 2769 return; 2770 } 2771 if (self.isError && !self.isUploadable) { 2772 self.clear(); 2773 } 2774 self.$captionContainer.focus(); 2775 }, 2776 _change: function (e) { 2777 var self = this, $el = self.$element; 2778 if (!self.isUploadable && isEmpty($el.val()) && self.fileInputCleared) { // IE 11 fix 2779 self.fileInputCleared = false; 2780 return; 2781 } 2782 self.fileInputCleared = false; 2783 var tfiles, msg, total, isDragDrop = arguments.length > 1, isAjaxUpload = self.isUploadable, i = 0, f, n, len, 2784 files = isDragDrop ? e.originalEvent.dataTransfer.files : $el.get(0).files, ctr = self.filestack.length, 2785 isSingleUpload = isEmpty($el.attr('multiple')), flagSingle = (isSingleUpload && ctr > 0), folders = 0, 2786 throwError = function (mesg, file, previewId, index) { 2787 var p1 = $.extend(true, {}, self._getOutData({}, {}, files), {id: previewId, index: index}), 2788 p2 = {id: previewId, index: index, file: file, files: files}; 2789 return self.isUploadable ? self._showUploadError(mesg, p1) : self._showError(mesg, p2); 2790 }; 2791 self.reader = null; 2792 self._resetUpload(); 2793 self._hideFileIcon(); 2794 if (self.isUploadable) { 2795 self.$container.find('.file-drop-zone .' + self.dropZoneTitleClass).remove(); 2796 } 2797 if (isDragDrop) { 2798 tfiles = []; 2799 while (files[i]) { 2800 f = files[i]; 2801 if (!f.type && f.size % 4096 === 0) { 2802 folders++; 2803 } else { 2804 tfiles.push(f); 2805 } 2806 i++; 2807 } 2808 } else { 2809 if (e.target.files === undefined) { 2810 tfiles = e.target && e.target.value ? [ 2811 {name: e.target.value.replace(/^.+\\/, '')} 2812 ] : []; 2813 } else { 2814 tfiles = e.target.files; 2815 } 2816 } 2817 if (isEmpty(tfiles) || tfiles.length === 0) { 2818 if (!isAjaxUpload) { 2819 self.clear(); 2820 } 2821 self._showFolderError(folders); 2822 self._raise('fileselectnone'); 2823 return; 2824 } 2825 self._resetErrors(); 2826 len = tfiles.length; 2827 total = self._getFileCount(self.isUploadable ? (self.getFileStack().length + len) : len);
2828 if (self.maxFileCount > 0 && total > self.maxFileCount) { 2829 if (!self.autoReplace || len > self.maxFileCount) { 2830 n = (self.autoReplace && len > self.maxFileCount) ? len : total; 2831 msg = self.msgFilesTooMany.replace('{m}', self.maxFileCount).replace('{n}', n); 2832 self.isError = throwError(msg, null, null, null); 2833 self.$captionContainer.find('.kv-caption-icon').hide(); 2834 self._setCaption('', true); 2835 self.$container.removeClass('file-input-new file-input-ajax-new'); 2836 return; 2837 } 2838 if (total > self.maxFileCount) { 2839 self._resetPreviewThumbs(isAjaxUpload); 2840 } 2841 } else { 2842 if (!isAjaxUpload || flagSingle) { 2843 self._resetPreviewThumbs(false); 2844 if (flagSingle) { 2845 self.clearStack(); 2846 } 2847 } else { 2848 if (isAjaxUpload && ctr === 0 && (!previewCache.count(self.id) || self.overwriteInitial)) { 2849 self._resetPreviewThumbs(true); 2850 } 2851 } 2852 } 2853 if (self.isPreviewable) { 2854 self._readFiles(tfiles); 2855 } else { 2856 self._updateFileDetails(1); 2857 } 2858 self._showFolderError(folders); 2859 }, 2860 _abort: function (params) { 2861 var self = this, data; 2862 if (self.ajaxAborted && typeof self.ajaxAborted === "object" && self.ajaxAborted.message !== undefined) { 2863 data = $.extend(true, {}, self._getOutData(), params); 2864 data.abortData = self.ajaxAborted.data || {}; 2865 data.abortMessage = self.ajaxAborted.message; 2866 self.cancel(); 2867 self._setProgress(101, self.$progress, self.msgCancelled); 2868 self._showUploadError(self.ajaxAborted.message, data, 'filecustomerror'); 2869 return true; 2870 } 2871 return false; 2872 }, 2873 _resetFileStack: function () { 2874 var self = this, i = 0, newstack = [], newnames = []; 2875 self._getThumbs().each(function () { 2876 var $thumb = $(this), ind = $thumb.attr('data-fileindex'), 2877 file = self.filestack[ind]; 2878 if (ind === -1) { 2879 return; 2880 } 2881 if (file !== undefined) { 2882 newstack[i] = file; 2883 newnames[i] = self._getFileName(file); 2884 $thumb.attr({ 2885 'id': self.previewInitId + '-' + i, 2886 'data-fileindex': i 2887 }); 2888 i++; 2889 } else { 2890 $thumb.attr({ 2891 'id': 'uploaded-' + uniqId(), 2892 'data-fileindex': '-1' 2893 }); 2894 } 2895 }); 2896 self.filestack = newstack; 2897 self.filenames = newnames; 2898 }, 2899 clearStack: function () { 2900 var self = this; 2901 self.filestack = []; 2902 self.filenames = []; 2903 return self.$element; 2904 }, 2905 updateStack: function (i, file) { 2906 var self = this; 2907 self.filestack[i] = file; 2908 self.filenames[i] = self._getFileName(file); 2909 return self.$element; 2910 }, 2911 addToStack: function (file) { 2912 var self = this; 2913 self.filestack.push(file); 2914 self.filenames.push(self._getFileName(file)); 2915 return self.$element; 2916 }, 2917 getFileStack: function (skipNull) { 2918 var self = this; 2919 return self.filestack.filter(function (n) { 2920 return (skipNull ? n !== undefined : n !== undefined && n !== null); 2921 }); 2922 }, 2923 getFilesCount: function () { 2924 var self = this, len = self.isUploadable ? self.getFileStack().length : self.$element.get(0).files.length; 2925 return self._getFileCount(len); 2926 }, 2927 lock: function () { 2928 var self = this; 2929 self._resetErrors(); 2930 self.disable(); 2931 if (self.showRemove) { 2932 addCss(self.$container.find('.fileinput-remove'), 'hide'); 2933 } 2934 if (self.showCancel) { 2935 self.$container.find('.fileinput-cancel').removeClass('hide'); 2936 } 2937 self._raise('filelock', [self.filestack, self._getExtraData()]); 2938 return self.$element; 2939 }, 2940 unlock: function (reset) { 2941 var self = this; 2942 if (reset === undefined) { 2943 reset = true; 2944 } 2945 self.enable(); 2946 if (self.showCancel) { 2947 addCss(self.$container.find('.fileinput-cancel'), 'hide'); 2948 } 2949 if (self.showRemove) { 2950 self.$container.find('.fileinput-remove').removeClass('hide'); 2951 } 2952 if (reset) { 2953 self._resetFileStack(); 2954 } 2955 self._raise('fileunlock', [self.filestack, self._getExtraData()]); 2956 return self.$element; 2957 }, 2958 cancel: function () { 2959 var self = this, xhr = self.ajaxRequests, len = xhr.length, i; 2960 if (len > 0) { 2961 for (i = 0; i < len; i += 1) { 2962 self.cancelling = true; 2963 xhr[i].abort(); 2964 } 2965 } 2966 self._setProgressCancelled(); 2967 self._getThumbs().each(function () { 2968 var $thumb = $(this), ind = $thumb.attr('data-fileindex'); 2969 $thumb.removeClass('file-uploading'); 2970 if (self.filestack[ind] !== undefined) { 2971 $thumb.find('.kv-file-upload').removeClass('disabled').removeAttr('disabled'); 2972 $thumb.find('.kv-file-remove').removeClass('disabled').removeAttr('disabled'); 2973 } 2974 self.unlock(); 2975 }); 2976 return self.$element; 2977 }, 2978 clear: function () { 2979 var self = this, cap; 2980 self.$btnUpload.removeAttr('disabled'); 2981 self._getThumbs().find('video,audio,img').each(function () { 2982 cleanMemory($(this)); 2983 }); 2984 self._resetUpload(); 2985 self.clearStack(); 2986 self._clearFileInput(); 2987 self._resetErrors(true);
2988 self._raise('fileclear'); 2989 if (self._hasInitialPreview()) { 2990 self._showFileIcon(); 2991 self._resetPreview(); 2992 self._initPreviewActions(); 2993 self.$container.removeClass('file-input-new'); 2994 } else { 2995 self._getThumbs().each(function () { 2996 self._clearObjects($(this)); 2997 }); 2998 if (self.isUploadable) { 2999 previewCache.data[self.id] = {}; 3000 } 3001 self.$preview.html(''); 3002 cap = (!self.overwriteInitial && self.initialCaption.length > 0) ? self.initialCaption : ''; 3003 self.$caption.html(cap); 3004 self.$caption.attr('title', ''); 3005 addCss(self.$container, 'file-input-new'); 3006 self._validateDefaultPreview(); 3007 } 3008 if (self.$container.find('.file-preview-frame').length === 0) { 3009 if (!self._initCaption()) { 3010 self.$captionContainer.find('.kv-caption-icon').hide(); 3011 } 3012 } 3013 self._hideFileIcon(); 3014 self._raise('filecleared'); 3015 self.$captionContainer.focus(); 3016 self._setFileDropZoneTitle(); 3017 return self.$element; 3018 }, 3019 reset: function () { 3020 var self = this; 3021 self._resetPreview(); 3022 self.$container.find('.fileinput-filename').text(''); 3023 self._raise('filereset'); 3024 addCss(self.$container, 'file-input-new'); 3025 if (self.$preview.find('.file-preview-frame').length || self.isUploadable && self.dropZoneEnabled) { 3026 self.$container.removeClass('file-input-new'); 3027 } 3028 self._setFileDropZoneTitle(); 3029 self.clearStack(); 3030 self.formdata = {}; 3031 return self.$element; 3032 }, 3033 disable: function () { 3034 var self = this; 3035 self.isDisabled = true; 3036 self._raise('filedisabled'); 3037 self.$element.attr('disabled', 'disabled'); 3038 self.$container.find(".kv-fileinput-caption").addClass("file-caption-disabled"); 3039 self.$container.find(".btn-file, .fileinput-remove, .fileinput-upload, .file-preview-frame button").attr( 3040 "disabled", 3041 true); 3042 self._initDragDrop(); 3043 return self.$element; 3044 }, 3045 enable: function () { 3046 var self = this; 3047 self.isDisabled = false; 3048 self._raise('fileenabled'); 3049 self.$element.removeAttr('disabled'); 3050 self.$container.find(".kv-fileinput-caption").removeClass("file-caption-disabled"); 3051 self.$container.find( 3052 ".btn-file, .fileinput-remove, .fileinput-upload, .file-preview-frame button").removeAttr("disabled"); 3053 self._initDragDrop(); 3054 return self.$element; 3055 }, 3056 upload: function () { 3057 var self = this, totLen = self.getFileStack().length, params = {}, 3058 i, outData, len, hasExtraData = !$.isEmptyObject(self._getExtraData()); 3059 if (self.minFileCount > 0 && self._getFileCount(totLen) < self.minFileCount) { 3060 self._noFilesError(params); 3061 return; 3062 } 3063 if (!self.isUploadable || self.isDisabled || (totLen === 0 && !hasExtraData)) { 3064 return; 3065 } 3066 self._resetUpload(); 3067 self.$progress.removeClass('hide'); 3068 self.uploadCount = 0; 3069 self.uploadStatus = {}; 3070 self.uploadLog = []; 3071 self.lock(); 3072 self._setProgress(2); 3073 if (totLen === 0 && hasExtraData) { 3074 self._uploadExtraOnly(); 3075 return; 3076 } 3077 len = self.filestack.length; 3078 self.hasInitData = false; 3079 if (self.uploadAsync) { 3080 outData = self._getOutData(); 3081 self._raise('filebatchpreupload', [outData]); 3082 self.fileBatchCompleted = false; 3083 self.uploadCache = {content: [], config: [], tags: [], append: true}; 3084 self.uploadAsyncCount = self.getFileStack().length; 3085 for (i = 0; i < len; i++) { 3086 self.uploadCache.content[i] = null; 3087 self.uploadCache.config[i] = null; 3088 self.uploadCache.tags[i] = null; 3089 } 3090 for (i = 0; i < len; i++) { 3091 if (self.filestack[i] !== undefined) { 3092 self._uploadSingle(i, self.filestack, true); 3093 } 3094 } 3095 return; 3096 } 3097 self._uploadBatch(); 3098 return self.$element; 3099 }, 3100 destroy: function () { 3101 var self = this, $cont = self.$container; 3102 $cont.find('.file-drop-zone').off(); 3103 self.$element.insertBefore($cont).off(NAMESPACE).removeData(); 3104 $cont.off().remove(); 3105 return self.$element; 3106 }, 3107 refresh: function (options) { 3108 var self = this, $el = self.$element; 3109 options = options ? $.extend(true, {}, self.options, options) : self.options; 3110 self.destroy(); 3111 $el.fileinput(options); 3112 if ($el.val()) { 3113 $el.trigger('change.fileinput'); 3114 } 3115 return $el; 3116 } 3117 }; 3118 3119 $.fn.fileinput = function (option) { 3120 if (!hasFileAPISupport() && !isIE(9)) { 3121 return; 3122 } 3123 var args = Array.apply(null, arguments), retvals = []; 3124 args.shift(); 3125 this.each(function () { 3126 var self = $(this), data = self.data('fileinput'), options = typeof option === 'object' && option, 3127 theme = options.theme || self.data('theme'), l = {}, t = {}, 3128 lang = $('html').attr('lang') || options.language || self.data('language') || 'en', opts; 3129 if (!data) { 3130 if (theme) { 3131 t = $.fn.fileinputThemes[theme] || {}; 3132 } 3133 if (lang !== 'en' && !isEmpty($.fn.fileinputLocales[lang])) { 3134 l = $.fn.fileinputLocales[lang] || {}; 3135 } 3136 opts = $.extend(true, {}, $.fn.fileinput.defaults, t, $.fn.fileinputLocales.en, l, options, 3137 self.data()); 3138 data = new FileInput(this, opts); 3139 self.data('fileinput', data); 3140 } 3141 3142 if (typeof option === 'string') { 3143 retvals.push(data[option].apply(data, args)); 3144 } 3145 }); 3146 switch (retvals.length) { 3147 case 0: 3148 return this; 3149 case 1: 3150 return retvals[0]; 3151 default: 3152 return retvals; 3153 } 3154 }; 3155 3156 $.fn.fileinput.defaults = { 3157 language: 'en', 3158 showCaption: true, 3159 showBrowse: true, 3160 showPreview: true, 3161 showRemove: true, 3162 showUpload: true, 3163 showCancel: true, 3164 showClose: true, 3165 showUploadedThumbs: true, 3166 browseOnZoneClick: false, 3167 autoReplace: false, 3168 previewClass: '', 3169 captionClass: '', 3170 mainClass: 'file-caption-main', 3171 mainTemplate: null, 3172 purifyHtml: true, 3173 fileSizeGetter: null, 3174 initialCaption: '', 3175 initialPreview: [], 3176 initialPreviewDelimiter: '*$$*', 3177 initialPreviewAsData: false, 3178 initialPreviewFileType: 'image', 3179 initialPreviewConfig: [], 3180 initialPreviewThumbTags: [], 3181 previewThumbTags: {}, 3182 initialPreviewShowDelete: true, 3183 removeFromPreviewOnError: false, 3184 deleteUrl: '', 3185 deleteExtraData: {}, 3186 overwriteInitial: true, 3187 layoutTemplates: defaultLayoutTemplates, 3188 previewTemplates: defaultPreviewTemplates,
3189 previewZoomSettings: defaultPreviewZoomSettings, 3190 previewZoomButtonIcons: { 3191 prev: '<i class="glyphicon glyphicon-triangle-left"></i>', 3192 next: '<i class="glyphicon glyphicon-triangle-right"></i>', 3193 toggleheader: '<i class="glyphicon glyphicon-resize-vertical"></i>', 3194 fullscreen: '<i class="glyphicon glyphicon-fullscreen"></i>', 3195 borderless: '<i class="glyphicon glyphicon-resize-full"></i>', 3196 close: '<i class="glyphicon glyphicon-remove"></i>' 3197 }, 3198 previewZoomButtonClasses: { 3199 prev: 'btn btn-navigate', 3200 next: 'btn btn-navigate', 3201 toggleheader: 'btn btn-default btn-header-toggle', 3202 fullscreen: 'btn btn-default', 3203 borderless: 'btn btn-default', 3204 close: 'btn btn-default' 3205 }, 3206 allowedPreviewTypes: defaultPreviewTypes, 3207 allowedPreviewMimeTypes: null, 3208 allowedFileTypes: null, 3209 allowedFileExtensions: null, 3210 defaultPreviewContent: null, 3211 customLayoutTags: {}, 3212 customPreviewTags: {}, 3213 previewSettings: defaultPreviewSettings, 3214 fileTypeSettings: defaultFileTypeSettings, 3215 previewFileIcon: '<i class="glyphicon glyphicon-file"></i>', 3216 previewFileIconClass: 'file-other-icon', 3217 previewFileIconSettings: {}, 3218 previewFileExtSettings: {}, 3219 buttonLabelClass: 'hidden-xs', 3220 browseIcon: '<i class="glyphicon glyphicon-folder-open"></i> ', 3221 browseClass: 'btn btn-primary', 3222 removeIcon: '<i class="glyphicon glyphicon-trash"></i>', 3223 removeClass: 'btn btn-default', 3224 cancelIcon: '<i class="glyphicon glyphicon-ban-circle"></i>', 3225 cancelClass: 'btn btn-default', 3226 uploadIcon: '<i class="glyphicon glyphicon-upload"></i>', 3227 uploadClass: 'btn btn-default', 3228 uploadUrl: null, 3229 uploadAsync: true, 3230 uploadExtraData: {}, 3231 zoomModalHeight: 480, 3232 minImageWidth: null, 3233 minImageHeight: null, 3234 maxImageWidth: null, 3235 maxImageHeight: null, 3236 resizeImage: false, 3237 resizePreference: 'width', 3238 resizeQuality: 0.92, 3239 resizeDefaultImageType: 'image/jpeg', 3240 minFileSize: 0, 3241 maxFileSize: 0, 3242 maxFilePreviewSize: 25600, // 25 MB 3243 minFileCount: 0, 3244 maxFileCount: 0, 3245 validateInitialCount: false, 3246 msgValidationErrorClass: 'text-danger', 3247 msgValidationErrorIcon: '<i class="glyphicon glyphicon-exclamation-sign"></i> ', 3248 msgErrorClass: 'file-error-message', 3249 progressThumbClass: "progress-bar progress-bar-success progress-bar-striped active", 3250 progressClass: "progress-bar progress-bar-success progress-bar-striped active", 3251 progressCompleteClass: "progress-bar progress-bar-success", 3252 progressErrorClass: "progress-bar progress-bar-danger", 3253 progressUploadThreshold: 99, 3254 previewFileType: 'image', 3255 elCaptionContainer: null, 3256 elCaptionText: null, 3257 elPreviewContainer: null, 3258 elPreviewImage: null, 3259 elPreviewStatus: null, 3260 elErrorContainer: null, 3261 errorCloseButton: '<span class="close kv-error-close">×</span>', 3262 slugCallback: null, 3263 dropZoneEnabled: true, 3264 dropZoneTitleClass: 'file-drop-zone-title', 3265 fileActionSettings: {}, 3266 otherActionButtons: '', 3267 textEncoding: 'UTF-8', 3268 ajaxSettings: {}, 3269 ajaxDeleteSettings: {}, 3270 showAjaxErrorDetails: true 3271 }; 3272 3273 $.fn.fileinputLocales.en = { 3274 fileSingle: 'file', 3275 filePlural: 'files', 3276 browseLabel: 'Browse …', 3277 removeLabel: 'Remove', 3278 removeTitle: 'Clear selected files', 3279 cancelLabel: 'Cancel', 3280 cancelTitle: 'Abort ongoing upload', 3281 uploadLabel: 'Upload', 3282 uploadTitle: 'Upload selected files', 3283 msgNo: 'No', 3284 msgNoFilesSelected: 'No files selected', 3285 msgCancelled: 'Cancelled', 3286 msgZoomModalHeading: 'Detailed Preview', 3287 msgSizeTooSmall: 'File "{name}" (<b>{size} KB</b>) is too small and must be larger than <b>{minSize} KB</b>.', 3288 msgSizeTooLarge: 'File "{name}" (<b>{size} KB</b>) exceeds maximum allowed upload size of <b>{maxSize} KB</b>.', 3289 msgFilesTooLess: 'You must select at least <b>{n}</b> {files} to upload.', 3290 msgFilesTooMany: 'Number of files selected for upload <b>({n})</b> exceeds maximum allowed limit of <b>{m}</b>.', 3291 msgFileNotFound: 'File "{name}" not found!', 3292 msgFileSecured: 'Security restrictions prevent reading the file "{name}".', 3293 msgFileNotReadable: 'File "{name}" is not readable.', 3294 msgFilePreviewAborted: 'File preview aborted for "{name}".', 3295 msgFilePreviewError: 'An error occurred while reading the file "{name}".', 3296 msgInvalidFileName: 'Invalid or unsupported characters in file name "{name}".', 3297 msgInvalidFileType: 'Invalid type for file "{name}". Only "{types}" files are supported.', 3298 msgInvalidFileExtension: 'Invalid extension for file "{name}". Only "{extensions}" files are supported.', 3299 msgUploadAborted: 'The file upload was aborted', 3300 msgUploadThreshold: 'Processing...', 3301 msgValidationError: 'Validation Error', 3302 msgLoading: 'Loading file {index} of {files} …', 3303 msgProgress: 'Loading file {index} of {files} - {name} - {percent}% completed.', 3304 msgSelected: '{n} {files} selected', 3305 msgFoldersNotAllowed: 'Drag & drop files only! {n} folder(s) dropped were skipped.', 3306 msgImageWidthSmall: 'Width of image file "{name}" must be at least {size} px.', 3307 msgImageHeightSmall: 'Height of image file "{name}" must be at least {size} px.', 3308 msgImageWidthLarge: 'Width of image file "{name}" cannot exceed {size} px.', 3309 msgImageHeightLarge: 'Height of image file "{name}" cannot exceed {size} px.', 3310 msgImageResizeError: 'Could not get the image dimensions to resize.', 3311 msgImageResizeException: 'Error while resizing the image.<pre>{errors}</pre>', 3312 dropZoneTitle: 'Drag & drop files here …', 3313 dropZoneClickTitle: '<br>(or click to select {files})', 3314 previewZoomButtonTitles: { 3315 prev: 'View previous file', 3316 next: 'View next file', 3317 toggleheader: 'Toggle header', 3318 fullscreen: 'Toggle full screen', 3319 borderless: 'Toggle borderless mode', 3320 close: 'Close detailed preview' 3321 } 3322 }; 3323 3324 $.fn.fileinputLocales.fr = { 3325 fileSingle: 'fichier', 3326 filePlural: 'fichiers',
3327 browseLabel: 'Parcourir …', 3328 removeLabel: 'Supprimer', 3329 removeTitle: 'Effacer', 3330 cancelLabel: 'Annuler', 3331 cancelTitle: 'Annuler le chargement', 3332 uploadLabel: 'Télécharger', 3333 uploadTitle: 'Télécharger', 3334 msgNo: 'Non', 3335 msgNoFilesSelected: 'Aucun fichier sélectionné', 3336 msgCancelled: 'Annulé', 3337 msgZoomModalHeading: 'Appercu', 3338 msgSizeTooSmall: 'Fichier "{name}" (<b>{size} KB</b>) est trop petit et doit avoir minimum <b>{minSize} KB</b>.', 3339 msgSizeTooLarge: 'Fichier "{name}" (<b>{size} KB</b>) excède la taille de <b>{maxSize} KB</b>.', 3340 msgFilesTooLess: 'Vous devez sélectionner au moins <b>{n}</b> {files} à charger.', 3341 msgFilesTooMany: 'Le nombre <b>({n})</b> de fichier a charger excède le nombre maximale alloué de <b>{m}</b>.', 3342 msgFileNotFound: 'Fichier "{name}" non trouvé!', 3343 msgFileSecured: 'Security restrictions prevent reading the file "{name}".', 3344 msgFileNotReadable: 'File "{name}" is not readable.', 3345 msgFilePreviewAborted: 'File preview aborted for "{name}".', 3346 msgFilePreviewError: 'Une erreur est survenue pendant la lecture du fichier "{name}".', 3347 msgInvalidFileName: 'Caractères invalide dans le nom du fichier "{name}".', 3348 msgInvalidFileType: 'Type de fichier invalide "{name}". Seulement "{types}" sont acceptés.', 3349 msgInvalidFileExtension: 'Fichier de type invalide "{name}". Seulement les fichiers de type "{extensions}" sont acceptés.', 3350 msgUploadAborted: 'Le chargement a été interompu', 3351 msgUploadThreshold: 'Chargement...', 3352 msgValidationError: 'Erreur de validation', 3353 msgLoading: 'Chargement {index} de {files} …', 3354 msgProgress: 'Chargement {index} de {files} - {name} - {percent}% complété.', 3355 msgSelected: '{n} {files} sélectionné(s)', 3356 msgFoldersNotAllowed: 'Drag & drop files only! {n} folder(s) dropped were skipped.', 3357 msgImageWidthSmall: 'Width of image file "{name}" must be at least {size} px.', 3358 msgImageHeightSmall: 'Height of image file "{name}" must be at least {size} px.', 3359 msgImageWidthLarge: 'Width of image file "{name}" cannot exceed {size} px.', 3360 msgImageHeightLarge: 'Height of image file "{name}" cannot exceed {size} px.', 3361 msgImageResizeError: 'Could not get the image dimensions to resize.', 3362 msgImageResizeException: 'Error while resizing the image.<pre>{errors}</pre>', 3363 dropZoneTitle: 'Drag & drop files here …', 3364 dropZoneClickTitle: '<br>(ou cliquez pour sélectionner {files})', 3365 previewZoomButtonTitles: { 3366 prev: 'Fichier précédent', 3367 next: 'Fichier suivant', 3368 toggleheader: 'Masquer/afficher entête', 3369 fullscreen: 'Mode plein écran', 3370 borderless: 'Modem sans bordures', 3371 close: 'Fermer l\'appercu' 3372 } 3373 }; 3374 3375 $.fn.fileinput.Constructor = FileInput; 3376 3377 /** 3378 * Convert automatically file inputs with class 'file' into a bootstrap fileinput control. 3379 */ 3380 $(document).ready(function () { 3381 var $input = $('input.file[type=file]'); 3382 if ($input.length) { 3383 $input.fileinput(); 3384 } 3385 }); 3386}));
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.