1/*! 2 * @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 - 2015 3 * @version 4.2.7 4 * 5 * File input styled for Bootstrap 3.0 that utilizes HTML5 File Input's advanced 6 * features including the FileReader API. 7 * 8 * The plugin drastically enhances the HTML file input to preview multiple files on the client before 9 * upload. In addition it provides the ability to preview content of images, text, videos, audio, html, 10 * flash and other objects. It also offers the ability to upload and delete files using AJAX, and add 11 * files in batches (i.e. preview, append, or remove before upload). 12 * 13 * Author: Kartik Visweswaran 14 * Copyright: 2015, Kartik Visweswaran, Krajee.com 15 * For more JQuery plugins visit http://plugins.krajee.com 16 * For more Yii related demos visit http://demos.krajee.com 17 */ 18(function ($) { 19 "use strict"; 20 21 $.fn.fileinputLocales = {}; 22 23 var isIE, isEdge, handler, previewCache, getNum, hasFileAPISupport, hasDragDropSupport, hasFileUploadSupport, addCss, 24 STYLE_SETTING, OBJECT_PARAMS, DEFAULT_PREVIEW, defaultFileActionSettings, tMain1, tMain2, tPreview, tIcon, tClose, 25 tCaption, tBtnDefault, tBtnLink, tBtnBrowse, tModal, tProgress, tFooter, tActions, tActionDelete, tActionUpload, 26 tZoom, tGeneric, tHtml, tImage, tText, tVideo, tAudio, tFlash, tObject, tOther, defaultLayoutTemplates, 27 defaultPreviewTemplates, defaultPreviewTypes, defaultPreviewSettings, defaultFileTypeSettings, isEmpty, isArray, 28 isSet, getElement, uniqId, htmlEncode, replaceTags, objUrl, FileInput; 29 30 isIE = function (ver) { 31 // check for IE versions < 11 32 if (navigator.appName !== 'Microsoft Internet Explorer') { 33 return false; 34 } 35 if (ver === 10) { 36 return new RegExp('msie\\s' + ver, 'i').test(navigator.userAgent); 37 } 38 var div = document.createElement("div"), status; 39 div.innerHTML = "<!--[if IE " + ver + "]> <i></i> <![endif]-->"; 40 status = div.getElementsByTagName("i").length; 41 document.body.appendChild(div); 42 div.parentNode.removeChild(div); 43 return status; 44 }; 45 isEdge = function() { 46 return new RegExp('Edge\/[0-9]+', 'i').test(navigator.userAgent); 47 }; 48 handler = function ($el, event, callback, skipNS) { 49 var ev = skipNS ? event : event + '.fileinput'; 50 $el.off(ev).on(ev, callback); 51 }; 52 previewCache = { 53 data: {}, 54 init: function (obj) { 55 var content = obj.initialPreview, id = obj.id; 56 if (content.length > 0 && !isArray(content)) { 57 content = content.split(obj.initialPreviewDelimiter); 58 } 59 previewCache.data[id] = { 60 content: content, 61 config: obj.initialPreviewConfig, 62 tags: obj.initialPreviewThumbTags, 63 delimiter: obj.initialPreviewDelimiter, 64 template: obj.previewGenericTemplate, 65 msg: function (n) { 66 return obj.getMsgSelected(n); 67 }, 68 initId: obj.previewInitId, 69 footer: obj.getLayoutTemplate('footer').replace(/\{progress}/g, obj.renderThumbProgress()), 70 isDelete: obj.initialPreviewShowDelete, 71 caption: obj.initialCaption, 72 actions: function (showUpload, showDelete, disabled, url, key) { 73 return obj.renderFileActions(showUpload, showDelete, disabled, url, key); 74 } 75 }; 76 }, 77 fetch: function (id) { 78 return previewCache.data[id].content.filter(function (n) { 79 return n !== null; 80 }); 81 }, 82 count: function (id, all) { 83 return !!previewCache.data[id] && !!previewCache.data[id].content ? 84 (all ? previewCache.data[id].content.length : previewCache.fetch(id).length) : 0; 85 }, 86 get: function (id, i, isDisabled) { 87 var ind = 'init_' + i, data = previewCache.data[id], config = data.config[i], 88 previewId = data.initId + '-' + ind, out, $tmp, frameClass = ' file-preview-initial'; 89 /** @namespace config.frameClass */ 90 /** @namespace config.frameAttr */ 91 isDisabled = isDisabled === undefined ? true : isDisabled; 92 if (data.content[i] === null) { 93 return ''; 94 } 95 if (!isEmpty(config) && !isEmpty(config.frameClass)) { 96 frameClass += ' ' + config.frameClass; 97 } 98 out = data.template 99 .replace(/\{previewId}/g, previewId) 100 .replace(/\{frameClass}/g, frameClass) 101 .replace(/\{fileindex}/g, ind)
102 .replace(/\{content}/g, data.content[i]) 103 .replace(/\{footer}/g, previewCache.footer(id, i, isDisabled)); 104 if (data.tags.length && data.tags[i]) { 105 out = replaceTags(out, data.tags[i]); 106 } 107 if (!isEmpty(config) && !isEmpty(config.frameAttr)) { 108 $tmp = $(document.createElement('div')).html(out); 109 $tmp.find('.file-preview-initial').attr(config.frameAttr); 110 out = $tmp.html(); 111 $tmp.remove(); 112 } 113 return out; 114 }, 115 add: function (id, content, config, tags, append) { 116 var data = $.extend(true, {}, previewCache.data[id]), index; 117 if (!isArray(content)) { 118 content = content.split(data.delimiter); 119 } 120 if (append) { 121 index = data.content.push(content) - 1; 122 data.config[index] = config; 123 data.tags[index] = tags; 124 } else { 125 index = content.length; 126 data.content = content; 127 data.config = config; 128 data.tags = tags; 129 } 130 previewCache.data[id] = data; 131 return index; 132 }, 133 set: function (id, content, config, tags, append) { 134 var data = $.extend(true, {}, previewCache.data[id]), i, chk; 135 if (!content || !content.length) { 136 return; 137 } 138 if (!isArray(content)) { 139 content = content.split(data.delimiter); 140 } 141 chk = content.filter(function (n) { 142 return n !== null; 143 }); 144 if (!chk.length) { 145 return; 146 } 147 if (data.content === undefined) { 148 data.content = []; 149 } 150 if (data.config === undefined) { 151 data.config = []; 152 } 153 if (data.tags === undefined) { 154 data.tags = []; 155 } 156 if (append) { 157 for (i = 0; i < content.length; i++) { 158 if (content[i]) { 159 data.content.push(content[i]); 160 } 161 } 162 for (i = 0; i < config.length; i++) { 163 if (config[i]) { 164 data.config.push(config[i]); 165 } 166 } 167 for (i = 0; i < tags.length; i++) { 168 if (tags[i]) { 169 data.tags.push(tags[i]); 170 } 171 } 172 } else { 173 data.content = content; 174 data.config = config; 175 data.tags = tags; 176 } 177 previewCache.data[id] = data; 178 }, 179 unset: function (id, index) { 180 var chk = previewCache.count(id); 181 if (!chk) { 182 return; 183 } 184 if (chk === 1) { 185 previewCache.data[id].content = []; 186 previewCache.data[id].config = []; 187 return; 188 } 189 previewCache.data[id].content[index] = null; 190 previewCache.data[id].config[index] = null; 191 }, 192 out: function (id) { 193 var html = '', data = previewCache.data[id], caption, len = previewCache.count(id, true); 194 if (len === 0) { 195 return {content: '', caption: ''}; 196 } 197 for (var i = 0; i < len; i++) { 198 html += previewCache.get(id, i); 199 } 200 caption = data.msg(previewCache.count(id)); 201 return {content: html, caption: caption}; 202 }, 203 footer: function (id, i, isDisabled) { 204 var data = previewCache.data[id]; 205 isDisabled = isDisabled === undefined ? true : isDisabled; 206 if (data.config.length === 0 || isEmpty(data.config[i])) { 207 return ''; 208 } 209 var config = data.config[i], 210 caption = isSet('caption', config) ? config.caption : '', 211 width = isSet('width', config) ? config.width : 'auto', 212 url = isSet('url', config) ? config.url : false, 213 key = isSet('key', config) ? config.key : null, 214 disabled = (url === false) && isDisabled, 215 actions = data.isDelete ? data.actions(false, true, disabled, url, key) : '', 216 footer = data.footer.replace(/\{actions}/g, actions);
217 return footer.replace(/\{caption}/g, caption) 218 .replace(/\{width}/g, width) 219 .replace(/\{indicator}/g, '') 220 .replace(/\{indicatorTitle}/g, ''); 221 } 222 }; 223 getNum = function (num, def) { 224 def = def || 0; 225 if (typeof num === "number") { 226 return num; 227 } 228 if (typeof num === "string") { 229 num = parseFloat(num); 230 } 231 return isNaN(num) ? def : num; 232 }; 233 hasFileAPISupport = function () { 234 return window.File && window.FileReader; 235 }; 236 hasDragDropSupport = function () { 237 var div = document.createElement('div'); 238 /** @namespace div.draggable */ 239 /** @namespace div.ondragstart */ 240 /** @namespace div.ondrop */ 241 return !isIE(9) && !isEdge() && // Fix for MS Edge drag & drop support bug 242 (div.draggable !== undefined || (div.ondragstart !== undefined && div.ondrop !== undefined)); 243 }; 244 hasFileUploadSupport = function () { 245 return hasFileAPISupport() && window.FormData; 246 }; 247 addCss = function ($el, css) { 248 $el.removeClass(css).addClass(css); 249 }; 250 STYLE_SETTING = 'style="width:{width};height:{height};"'; 251 OBJECT_PARAMS = ' <param name="controller" value="true" />\n' + 252 ' <param name="allowFullScreen" value="true" />\n' + 253 ' <param name="allowScriptAccess" value="always" />\n' + 254 ' <param name="autoPlay" value="false" />\n' + 255 ' <param name="autoStart" value="false" />\n' + 256 ' <param name="quality" value="high" />\n'; 257 DEFAULT_PREVIEW = '<div class="file-preview-other">\n' + 258 ' <span class="{previewFileIconClass}">{previewFileIcon}</span>\n' + 259 '</div>'; 260 defaultFileActionSettings = { 261 removeIcon: '<i class="icon icon-trash text-danger"></i>', 262 removeClass: 'btn btn-xs btn-default', 263 removeTitle: 'Remove file', 264 uploadIcon: '<i class="icon icon-upload-alt text-info"></i>', 265 uploadClass: 'btn btn-xs btn-default', 266 uploadTitle: 'Upload file', 267 indicatorNew: '<i class="icon icon-hand-down text-warning"></i>', 268 indicatorSuccess: '<i class="icon icon-ok-sign text-success"></i>', 269 indicatorError: '<i class="icon icon-exclamation-sign text-danger"></i>', 270 indicatorLoading: '<i class="icon icon-hand-up text-muted"></i>', 271 indicatorNewTitle: 'Not uploaded yet', 272 indicatorSuccessTitle: 'Uploaded', 273 indicatorErrorTitle: 'Upload Error', 274 indicatorLoadingTitle: 'Uploading ...' 275 }; 276 tMain1 = '{preview}\n' + 277 '<div class="kv-upload-progress hide"></div>\n' + 278 '<div class="input-group {class}">\n' + 279 ' {caption}\n' + 280 ' <div class="input-group-btn">\n' + 281 ' {remove}\n' + 282 ' {cancel}\n' + 283 ' {upload}\n' + 284 ' {browse}\n' + 285 ' </div>\n' + 286 '</div>'; 287 tMain2 = '{preview}\n<div class="kv-upload-progress hide"></div>\n{remove}\n{cancel}\n{upload}\n{browse}\n'; 288 tPreview = '<div class="file-preview {class}">\n' + 289 ' {close}' + 290 ' <div class="{dropClass}">\n' + 291 ' <div class="file-preview-thumbnails">\n' + 292 ' </div>\n' + 293 ' <div class="clearfix"></div>' + 294 ' <div class="file-preview-status text-center text-success"></div>\n' + 295 ' <div class="kv-fileinput-error"></div>\n' + 296 ' </div>\n' + 297 '</div>'; 298 tClose = '<div class="close fileinput-remove">×</div>\n'; 299 tIcon = '<span class="icon icon-file kv-caption-icon"></span>'; 300 tCaption = '<div tabindex="500" class="form-control file-caption {class}">\n' + 301 ' <div class="file-caption-name"></div>\n' + 302 '</div>\n'; 303 tBtnDefault = '<button type="{type}" tabindex="500" title="{title}" class="{css}"{status}>{icon}{label}</button>'; 304 tBtnLink = '<a href="{href}" tabindex="500" title="{title}" class="{css}"{status}>{icon}{label}</a>'; 305 tBtnBrowse = '<div tabindex="500" class="{css}"{status}>{icon}{label}</div>'; 306 tModal = '<div id="{id}" class="file-preview-detail-modal modal fade" tabindex="-1">\n' + 307 ' <div class="modal-dialog modal-lg">\n' + 308 ' <div class="modal-content">\n' + 309 ' <div class="modal-header">\n' + 310 ' <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>\n' + 311 ' <h3 class="modal-title">{heading} <small>{title}</small></h3>\n' + 312 ' </div>\n' + 313 ' <div class="modal-body">\n' + 314 ' <pre>{body}</pre>\n' + 315 ' </div>\n' + 316 ' </div>\n' + 317 ' </div>\n' + 318 '</div>'; 319 tProgress = '<div class="progress">\n' + 320 ' <div class="{class}" role="progressbar"' + 321 ' aria-valuenow="{percent}" aria-valuemin="0" aria-valuemax="100" style="width:{percent}%;">\n' + 322 ' {percent}%\n' + 323 ' </div>\n' + 324 '</div>'; 325 tFooter = '<div class="file-thumbnail-footer">\n' + 326 ' <div class="file-footer-caption" title="{caption}">{caption}</div>\n' + 327 ' {progress} {actions}\n' + 328 '</div>'; 329 tActions = '<div class="file-actions">\n' + 330 ' <div class="file-footer-buttons">\n' + 331 ' {upload}{delete}{other}
331' + 332 ' </div>\n' + 333 ' <div class="file-upload-indicator" title="{indicatorTitle}">{indicator}</div>\n' + 334 ' <div class="clearfix"></div>\n' + 335 '</div>'; 336 tActionDelete = '<button type="button" class="kv-file-remove {removeClass}" ' + 337 'title="{removeTitle}"{dataUrl}{dataKey}>{removeIcon}</button>\n'; 338 tActionUpload = '<button type="button" class="kv-file-upload {uploadClass}" title="{uploadTitle}">' + 339 ' {uploadIcon}\n</button>\n'; 340 tZoom = '<button type="button" class="btn btn-default btn-xs btn-block" title="{zoomTitle}: {caption}" onclick="{dialog}">\n' + 341 ' {zoomInd}\n' + 342 '</button>\n'; 343 tGeneric = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + 344 ' {content}\n' + 345 ' {footer}\n' + 346 '</div>\n'; 347 tHtml = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + 348 ' <object class="file-object" data="{data}" type="{type}" width="{width}" height="{height}">\n' + 349 ' ' + DEFAULT_PREVIEW + '\n' + 350 ' </object>\n' + 351 ' {footer}\n' + 352 '</div>'; 353 tImage = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + 354 ' <img src="{data}" class="file-preview-image" title="{caption}" alt="{caption}" ' + STYLE_SETTING + '>\n' + 355 ' {footer}\n' + 356 '</div>\n'; 357 tText = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + 358 ' <pre class="file-preview-text" title="{caption}" ' + STYLE_SETTING + '>{data}</pre>\n' + 359 ' {zoom}\n' + 360 ' {footer}\n' + 361 '</div>'; 362 tVideo = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + 363 ' title="{caption}" ' + STYLE_SETTING + '>\n' + 364 ' <video width="{width}" height="{height}" controls>\n' + 365 ' <source src="{data}" type="{type}">\n' + 366 ' ' + DEFAULT_PREVIEW + '\n' + 367 ' </video>\n' + 368 ' {footer}\n' + 369 '</div>\n'; 370 tAudio = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + 371 ' title="{caption}" ' + STYLE_SETTING + '>\n' + 372 ' <audio controls>\n' + 373 ' <source src="' + '{data}' + '" type="{type}">\n' + 374 ' ' + DEFAULT_PREVIEW + '\n' + 375 ' </audio>\n' + 376 ' {footer}\n' + 377 '</div>'; 378 tFlash = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + 379 ' title="{caption}" ' + STYLE_SETTING + '>\n' + 380 ' <object class="file-object" type="application/x-shockwave-flash" width="{width}" height="{height}" data="{data}">\n' + 381 OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n' + 382 ' </object>\n' + 383 ' {footer}\n' + 384 '</div>\n'; 385 tObject = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + 386 ' title="{caption}" ' + STYLE_SETTING + '>\n' + 387 ' <object class="file-object" data="{data}" type="{type}" width="{width}" height="{height}">\n' + 388 ' <param name="movie" value="{caption}" />\n' + 389 OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n' + 390 ' </object>\n' + 391 ' {footer}\n' + 392 '</div>'; 393 tOther = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + 394 ' title="{caption}" ' + STYLE_SETTING + '>\n' + 395 ' <div class="file-preview-other-frame">\n' + 396 ' ' + DEFAULT_PREVIEW + '\n' + 397 ' </div>\n' + 398 ' <div class="file-preview-other-footer">{footer}</div>\n' + 399 '</div>'; 400 defaultLayoutTemplates = { 401 main1: tMain1, 402 main2: tMain2, 403 preview: tPreview, 404 close: tClose, 405 zoom: tZoom, 406 icon: tIcon, 407 caption: tCaption, 408 modal: tModal, 409 progress: tProgress, 410 footer: tFooter, 411 actions: tActions, 412 actionDelete: tActionDelete, 413 actionUpload: tActionUpload, 414 btnDefault: tBtnDefault, 415 btnLink: tBtnLink, 416 btnBrowse: tBtnBrowse 417 }; 418 defaultPreviewTemplates = { 419 generic: tGeneric, 420 html: tHtml, 421 image: tImage, 422 text: tText, 423 video: tVideo, 424 audio: tAudio, 425 flash: tFlash, 426 object: tObject, 427 other: tOther 428 };
429 defaultPreviewTypes = ['image', 'html', 'text', 'video', 'audio', 'flash', 'object']; 430 defaultPreviewSettings = { 431 image: {width: "auto", height: "160px"}, 432 html: {width: "213px", height: "160px"}, 433 text: {width: "160px", height: "136px"}, 434 video: {width: "213px", height: "160px"}, 435 audio: {width: "213px", height: "80px"}, 436 flash: {width: "213px", height: "160px"}, 437 object: {width: "160px", height: "160px"}, 438 other: {width: "160px", height: "160px"} 439 }; 440 defaultFileTypeSettings = { 441 image: function (vType, vName) { 442 return (vType !== undefined) ? vType.match('image.*') : vName.match(/\.(gif|png|jpe?g)$/i); 443 }, 444 html: function (vType, vName) { 445 return (vType !== undefined) ? vType === 'text/html' : vName.match(/\.(htm|html)$/i); 446 }, 447 text: function (vType, vName) { 448 return (vType !== undefined && vType.match('text.*')) || vName.match(/\.(txt|md|csv|nfo|ini|json|php|js|css)$/i); 449 }, 450 video: function (vType, vName) { 451 return (vType !== undefined && vType.match(/\.video\/(ogg|mp4|webm|3gp)$/i)) || vName.match(/\.(og?|mp4|webm|3gp)$/i); 452 }, 453 audio: function (vType, vName) { 454 return (vType !== undefined && vType.match(/\.audio\/(ogg|mp3|wav)$/i)) || vName.match(/\.(ogg|mp3|wav)$/i); 455 }, 456 flash: function (vType, vName) { 457 return (vType !== undefined && vType === 'application/x-shockwave-flash') || vName.match(/\.(swf)$/i); 458 }, 459 object: function () { 460 return true; 461 }, 462 other: function () { 463 return true; 464 } 465 }; 466 isEmpty = function (value, trim) { 467 return value === undefined || value === null || value.length === 0 || (trim && $.trim(value) === ''); 468 }; 469 isArray = function (a) { 470 return Array.isArray(a) || Object.prototype.toString.call(a) === '[object Array]'; 471 }; 472 isSet = function (needle, haystack) { 473 return (typeof haystack === 'object' && needle in haystack); 474 }; 475 getElement = function (options, param, value) { 476 return (isEmpty(options) || isEmpty(options[param])) ? value : $(options[param]); 477 }; 478 uniqId = function () { 479 return Math.round(new Date().getTime() + (Math.random() * 100)); 480 }; 481 htmlEncode = function (str) { 482 return str.replace(/&/g, '&') 483 .replace(/</g, '<') 484 .replace(/>/g, '>') 485 .replace(/"/g, '"') 486 .replace(/'/g, '''); 487 }; 488 replaceTags = function (str, tags) { 489 var out = str; 490 if (!tags) { 491 return out; 492 } 493 $.each(tags, function (key, value) { 494 if (typeof value === "function") { 495 value = value(); 496 } 497 out = out.split(key).join(value); 498 }); 499 return out; 500 }; 501 objUrl = window.URL || window.webkitURL; 502 FileInput = function (element, options) { 503 var self = this; 504 self.$element = $(element); 505 if (!self.validate()) { 506 return; 507 } 508 self.isPreviewable = hasFileAPISupport(); 509 self.isIE9 = isIE(9); 510 self.isIE10 = isIE(10); 511 if (self.isPreviewable || self.isIE9) { 512 self.init(options); 513 self.listen(); 514 } else { 515 self.$element.removeClass('file-loading'); 516 } 517 }; 518 519 FileInput.prototype = { 520 constructor: FileInput, 521 validate: function () { 522 var self = this, $exception; 523 if (self.$element.attr('type') === 'file') { 524 return true; 525 } 526 $exception = '<div class="help-block alert alert-warning">' + 527 '<h4>Invalid Input Type</h4>' + 528 'You must set an input <code>type = file</code> for <b>bootstrap-fileinput</b> plugin to initialize.' + 529 '</div>'; 530 self.$element.after($exception); 531 return false; 532 }, 533 init: function (options) { 534 var self = this, $el = self.$element, t; 535 $.each(options, function (key, value) { 536 switch (key) { 537 case 'minFileCount':
538 case 'maxFileCount': 539 case 'maxFileSize': 540 self[key] = getNum(value); 541 break; 542 default: 543 self[key] = value; 544 break; 545 } 546 }); 547 self.fileInputCleared = false; 548 self.fileBatchCompleted = true; 549 if (!self.isPreviewable) { 550 self.showPreview = false; 551 } 552 self.uploadFileAttr = !isEmpty($el.attr('name')) ? $el.attr('name') : 'file_data'; 553 self.reader = null; 554 self.formdata = {}; 555 self.filestack = []; 556 self.filenames = []; 557 self.uploadCount = 0; 558 self.uploadStatus = {}; 559 self.uploadLog = []; 560 self.uploadAsyncCount = 0; 561 self.loadedImages = []; 562 self.totalImagesCount = 0; 563 self.ajaxRequests = []; 564 self.isError = false; 565 self.ajaxAborted = false; 566 self.cancelling = false; 567 t = self.getLayoutTemplate('progress'); 568 self.progressTemplate = t.replace('{class}', self.progressClass); 569 self.progressCompleteTemplate = t.replace('{class}', self.progressCompleteClass); 570 self.dropZoneEnabled = hasDragDropSupport() && self.dropZoneEnabled; 571 self.isDisabled = self.$element.attr('disabled') || self.$element.attr('readonly'); 572 self.isUploadable = hasFileUploadSupport() && !isEmpty(self.uploadUrl); 573 self.slug = typeof options.slugCallback === "function" ? options.slugCallback : self.slugDefault; 574 self.mainTemplate = self.showCaption ? self.getLayoutTemplate('main1') : self.getLayoutTemplate('main2'); 575 self.captionTemplate = self.getLayoutTemplate('caption'); 576 self.previewGenericTemplate = self.getPreviewTemplate('generic'); 577 if (self.resizeImage && (self.maxImageWidth || self.maxImageHeight)) { 578 self.imageCanvas = document.createElement('canvas'); 579 self.imageCanvasContext = self.imageCanvas.getContext('2d'); 580 } 581 if (isEmpty(self.$element.attr('id'))) { 582 self.$element.attr('id', uniqId()); 583 } 584 if (self.$container === undefined) { 585 self.$container = self.createContainer(); 586 } else { 587 self.refreshContainer(); 588 } 589 self.$progress = self.$container.find('.kv-upload-progress'); 590 self.$btnUpload = self.$container.find('.fileinput-upload'); 591 self.$captionContainer = getElement(options, 'elCaptionContainer', self.$container.find('.file-caption')); 592 self.$caption = getElement(options, 'elCaptionText', self.$container.find('.file-caption-name')); 593 self.$previewContainer = getElement(options, 'elPreviewContainer', self.$container.find('.file-preview')); 594 self.$preview = getElement(options, 'elPreviewImage', self.$container.find('.file-preview-thumbnails')); 595 self.$previewStatus = getElement(options, 'elPreviewStatus', self.$container.find('.file-preview-status')); 596 self.$errorContainer = getElement(options, 'elErrorContainer', 597 self.$previewContainer.find('.kv-fileinput-error')); 598 if (!isEmpty(self.msgErrorClass)) { 599 addCss(self.$errorContainer, self.msgErrorClass); 600 } 601 self.$errorContainer.hide(); 602 self.fileActionSettings = $.extend(defaultFileActionSettings, options.fileActionSettings); 603 self.previewInitId = "preview-" + uniqId(); 604 self.id = self.$element.attr('id'); 605 previewCache.init(self); 606 self.initPreview(true); 607 self.initPreviewDeletes(); 608 self.options = options; 609 self.setFileDropZoneTitle(); 610 self.$element.removeClass('file-loading'); 611 if (self.$element.attr('disabled')) { 612 self.disable(); 613 } 614 }, 615 parseError: function (jqXHR, errorThrown, fileName) { 616 var self = this, errMsg = $.trim(errorThrown + ''), 617 dot = errMsg.slice(-1) === '.' ? '' : '.', 618 text = jqXHR.responseJSON !== undefined && jqXHR.responseJSON.error !== undefined ? 619 jqXHR.responseJSON.error : jqXHR.responseText; 620 if (self.cancelling && self.msgUploadAborted) { 621 errMsg = self.msgUploadAborted; 622 } 623 if (self.showAjaxErrorDetails && text) { 624 text = $.trim(text.replace(/\n\s*\n/g, '\n')); 625 text = text.length > 0 ? '<pre>
625' + text + '</pre>' : ''; 626 errMsg += dot + text; 627 } else { 628 errMsg += dot; 629 } 630 self.cancelling = false; 631 return fileName ? '<b>' + fileName + ': </b>' + errMsg : errMsg; 632 }, 633 raise: function (event, params) { 634 var self = this, e = $.Event(event); 635 if (params !== undefined) { 636 self.$element.trigger(e, params); 637 } else { 638 self.$element.trigger(e); 639 } 640 if (e.isDefaultPrevented()) { 641 return false; 642 } 643 if (!e.result) { 644 return e.result; 645 } 646 switch (event) { 647 // ignore these events 648 case 'filebatchuploadcomplete': 649 case 'filebatchuploadsuccess': 650 case 'fileuploaded': 651 case 'fileclear': 652 case 'filecleared': 653 case 'filereset': 654 case 'fileerror': 655 case 'filefoldererror': 656 case 'fileuploaderror': 657 case 'filebatchuploaderror': 658 case 'filedeleteerror': 659 case 'filecustomerror': 660 case 'filesuccessremove': 661 break; 662 // receive data response via `filecustomerror` event` 663 default: 664 self.ajaxAborted = e.result; 665 break; 666 } 667 return true; 668 }, 669 getLayoutTemplate: function (t) { 670 var self = this, 671 template = isSet(t, self.layoutTemplates) ? self.layoutTemplates[t] : defaultLayoutTemplates[t]; 672 if (isEmpty(self.customLayoutTags)) { 673 return template; 674 } 675 return replaceTags(template, self.customLayoutTags); 676 }, 677 getPreviewTemplate: function (t) { 678 var self = this, 679 template = isSet(t, self.previewTemplates) ? self.previewTemplates[t] : defaultPreviewTemplates[t]; 680 if (isEmpty(self.customPreviewTags)) { 681 return template; 682 } 683 return replaceTags(template, self.customPreviewTags); 684 }, 685 parseFilePreviewIcon: function (content, fname) { 686 var self = this, ext, icn = self.previewFileIcon; 687 if (fname && fname.indexOf('.') > -1) { 688 ext = fname.split('.').pop(); 689 if (self.previewFileIconSettings && self.previewFileIconSettings[ext]) { 690 icn = self.previewFileIconSettings[ext]; 691 } 692 if (self.previewFileExtSettings) { 693 $.each(self.previewFileExtSettings, function (key, func) { 694 if (self.previewFileIconSettings[key] && func(ext)) { 695 icn = self.previewFileIconSettings[key]; 696 } 697 }); 698 } 699 } 700 if (content.indexOf('{previewFileIcon}') > -1) { 701 return content.replace(/\{previewFileIconClass}/g, self.previewFileIconClass).replace(/\{previewFileIcon}/g, icn); 702 } 703 return content; 704 }, 705 getOutData: function (jqXHR, responseData, filesData) { 706 var self = this; 707 jqXHR = jqXHR || {}; 708 responseData = responseData || {}; 709 filesData = filesData || self.filestack.slice(0) || {}; 710 return { 711 form: self.formdata, 712 files: filesData, 713 extra: self.getExtraData(), 714 response: responseData, 715 reader: self.reader, 716 jqXHR: jqXHR 717 }; 718 }, 719 listen: function () { 720 var self = this, $el = self.$element, $cap = self.$captionContainer, $btnFile = self.$btnFile, 721 $form = $el.closest('form'), $cont = self.$container; 722 handler($el, 'change', $.proxy(self.change, self)); 723 handler($btnFile, 'click', function () { 724 self.raise('filebrowse'); 725 if (self.isError && !self.isUploadable) { 726 self.clear(); 727 } 728 $cap.focus(); 729 }); 730 handler($form, 'reset', $.proxy(self.reset, self));
731 handler($cont.find('.fileinput-remove:not([disabled])'), 'click', $.proxy(self.clear, self)); 732 handler($cont.find('.fileinput-cancel'), 'click', $.proxy(self.cancel, self)); 733 if (self.isUploadable && self.dropZoneEnabled && self.showPreview) { 734 self.initDragDrop(); 735 } 736 if (!self.isUploadable) { 737 handler($form, 'submit', $.proxy(self.submitForm, self)); 738 } 739 handler(self.$container.find('.fileinput-upload'), 'click', function (e) { 740 var $btn = $(this), $form, isEnabled = !$btn.hasClass('disabled') && isEmpty($btn.attr('disabled')); 741 if (!self.isUploadable) { 742 if (isEnabled && $btn.attr('type') !== 'submit') { 743 $form = $btn.closest('form'); 744 // downgrade to normal form submit if possible 745 if ($form.length) { 746 $form.trigger('submit'); 747 } 748 e.preventDefault(); 749 } 750 return; 751 } 752 e.preventDefault(); 753 if (isEnabled) { 754 self.upload(); 755 } 756 }); 757 }, 758 submitForm: function () { 759 var self = this, $el = self.$element, files = $el.get(0).files; 760 if (files && self.minFileCount > 0 && self.getFileCount(files.length) < self.minFileCount) { 761 self.noFilesError({}); 762 return false; 763 } 764 return !self.abort({}); 765 }, 766 abort: function (params) { 767 var self = this, data; 768 if (self.ajaxAborted && typeof self.ajaxAborted === "object" && self.ajaxAborted.message !== undefined) { 769 data = $.extend(self.getOutData(), params); 770 data.abortData = self.ajaxAborted.data || {}; 771 data.abortMessage = self.ajaxAborted.message; 772 self.cancel(); 773 self.setProgress(100); 774 self.showUploadError(self.ajaxAborted.message, data, 'filecustomerror'); 775 return true; 776 } 777 return false; 778 }, 779 noFilesError: function (params) { 780 var self = this, label = self.minFileCount > 1 ? self.filePlural : self.fileSingle, 781 msg = self.msgFilesTooLess.replace('{n}', self.minFileCount).replace('{files}', label), 782 $error = self.$errorContainer; 783 self.addError(msg); 784 self.isError = true; 785 self.updateFileDetails(0); 786 $error.fadeIn(800); 787 self.raise('fileerror', [params]); 788 self.clearFileInput(); 789 addCss(self.$container, 'has-error'); 790 }, 791 setProgress: function (p, $el) { 792 var self = this, pct = Math.min(p, 100), 793 template = pct < 100 ? self.progressTemplate : self.progressCompleteTemplate; 794 $el = $el || self.$progress; 795 if (!isEmpty(template)) { 796 $el.html(template.replace(/\{percent}/g, pct)); 797 } 798 }, 799 lock: function () { 800 var self = this; 801 self.resetErrors(); 802 self.disable(); 803 if (self.showRemove) { 804 addCss(self.$container.find('.fileinput-remove'), 'hide'); 805 } 806 if (self.showCancel) { 807 self.$container.find('.fileinput-cancel').removeClass('hide'); 808 } 809 self.raise('filelock', [self.filestack, self.getExtraData()]); 810 }, 811 unlock: function (reset) { 812 var self = this; 813 if (reset === undefined) { 814 reset = true; 815 } 816 self.enable(); 817 if (self.showCancel) { 818 addCss(self.$container.find('.fileinput-cancel'), 'hide'); 819 } 820 if (self.showRemove) { 821 self.$container.find('.fileinput-remove').removeClass('hide'); 822 } 823 if (reset) { 824 self.resetFileStack(); 825 } 826 self.raise('fileunlock', [self.filestack, self.getExtraData()]); 827 }, 828 resetFileStack: function () { 829 var self = this, i = 0, newstack = []; 830 self.getThumbs().each(function () { 831 var $thumb = $(this), ind = $thumb.attr('data-fileindex'), 832 file = self.filestack[ind]; 833 if (ind === -1) { 834 return; 835 } 836 if (file !== undefined) { 837 newstack[i] = file; 838 $thumb.attr({ 839 'id': self.previewInitId + '-' + i, 840 'data-fileindex': i 841 }); 842 i += 1; 843 } else { 844 $thumb.attr({ 845 'id': 'uploaded-' + uniqId(), 846 'data-fileindex': '-1' 847 }); 848 } 849 }); 850 self.filestack = newstack; 851 self.filenames = []; 852 $.each(self.filestack, function (i, file) { 853 self.filenames[i] = (file && file.name) ? self.slug(file.name) : ''; 854 }); 855 }, 856 destroy: function () { 857 var self = this, $cont = self.$container; 858 $cont.find('.file-drop-zone').off(); 859 self.$element.insertBefore($cont).off('.fileinput').removeData(); 860 $cont.off().remove(); 861 }, 862 refresh: function (options) { 863 var self = this, $el = self.$element; 864 options = options ? $.extend(self.options, options) : self.options; 865 self.destroy(); 866 $el.fileinput(options); 867 if ($el.val()) { 868 $el.trigger('change.fileinput'); 869 } 870 }, 871 initDragDrop: function () { 872 var self = this, $zone = self.$container.find('.file-drop-zone'), 873 allEvents = 'dragenter.fileinput dragover.fileinput drop.fileinput'; 874 handler($zone, 'dragenter.fileinput dragover.fileinput', function (e) { 875 var hasFiles = $.inArray('Files', e.originalEvent.dataTransfer.types) > -1; 876 e.stopPropagation(); 877 e.preventDefault(); 878 if (self.isDisabled || !hasFiles) { 879 e.originalEvent.dataTransfer.effectAllowed = 'none'; 880 e.originalEvent.dataTransfer.dropEffect = 'none'; 881 return; 882 } 883 addCss($(this), 'highlighted'); 884 }, true); 885 handler($zone, 'dragleave', function (e) { 886 e.stopPropagation(); 887 e.preventDefault(); 888 if (self.isDisabled) { 889 return; 890 } 891 $(this).removeClass('highlighted'); 892 }); 893 handler($zone, 'drop', function (e) { 894 e.preventDefault(); 895 /** @namespace e.originalEvent.dataTransfer */ 896 if (self.isDisabled || isEmpty(e.originalEvent.dataTransfer.files)) { 897 return; 898 } 899 self.change(e, 'dragdrop'); 900 $(this).removeClass('highlighted'); 901 }); 902 handler($(document), allEvents, function (e) { 903 e.stopPropagation(); 904 e.preventDefault(); 905 }, true); 906 }, 907 setFileDropZoneTitle: function () { 908 var self = this, $zone = self.$container.find('.file-drop-zone'); 909 $zone.find('.' + self.dropZoneTitleClass).remove(); 910 if (!self.isUploadable || !self.showPreview || $zone.length === 0 || self.getFileStack().length > 0 || !self.dropZoneEnabled) { 911 return; 912 } 913 if ($zone.find('.file-preview-frame').length === 0 && isEmpty(self.defaultPreviewContent)) { 914 $zone.prepend('<div class="' + self.dropZoneTitleClass + '">' + self.dropZoneTitle + '</div>'); 915 } 916 self.$container.removeClass('file-input-new'); 917 addCss(self.$container, 'file-input-ajax-new'); 918 }, 919 errorsExist: function () { 920 var self = this, $err; 921 if (self.$errorContainer.find('li').length) { 922 return true; 923 } 924 $err = $(document.createElement('div')).html(self.$errorContainer.html()); 925 $err.find('span.kv-error-close').remove(); 926 $err.find('ul').remove(); 927 return $.trim($err.text()).length ? true : false; 928 }, 929 getMsgSelected: function (n) { 930 var self = this, strFiles = n === 1 ? self.fileSingle : self.filePlural; 931 return self.msgSelected.replace('{n}', n).replace('{files}', strFiles); 932 }, 933 renderThumbProgress: function () { 934 return '<div class="file-thumb-progress hide">' + this.progressTemplate.replace(/\{percent}/g, '0') + '</div>'; 935 }, 936 renderFileFooter: function (caption, width) {
937 var self = this, config = self.fileActionSettings, footer, out, template = self.getLayoutTemplate('footer'); 938 if (self.isUploadable) { 939 footer = template.replace(/\{actions}/g, self.renderFileActions(true, true, false, false, false)); 940 out = footer.replace(/\{caption}/g, caption) 941 .replace(/\{width}/g, width) 942 .replace(/\{progress}/g, self.renderThumbProgress()) 943 .replace(/\{indicator}/g, config.indicatorNew) 944 .replace(/\{indicatorTitle}/g, config.indicatorNewTitle); 945 } else { 946 out = template.replace(/\{actions}/g, '') 947 .replace(/\{caption}/g, caption) 948 .replace(/\{progress}/g, '') 949 .replace(/\{width}/g, width) 950 .replace(/\{indicator}/g, '') 951 .replace(/\{indicatorTitle}/g, ''); 952 } 953 out = replaceTags(out, self.previewThumbTags); 954 return out; 955 }, 956 renderFileActions: function (showUpload, showDelete, disabled, url, key) { 957 if (!showUpload && !showDelete) { 958 return ''; 959 } 960 var self = this, 961 vUrl = url === false ? '' : ' data-url="' + url + '"', 962 vKey = key === false ? '' : ' data-key="' + key + '"', 963 btnDelete = self.getLayoutTemplate('actionDelete'), 964 btnUpload = '', 965 template = self.getLayoutTemplate('actions'), 966 otherButtons = self.otherActionButtons.replace(/\{dataKey}/g, vKey), 967 config = self.fileActionSettings, 968 removeClass = disabled ? config.removeClass + ' disabled' : config.removeClass; 969 btnDelete = btnDelete 970 .replace(/\{removeClass}/g, removeClass) 971 .replace(/\{removeIcon}/g, config.removeIcon) 972 .replace(/\{removeTitle}/g, config.removeTitle) 973 .replace(/\{dataUrl}/g, vUrl) 974 .replace(/\{dataKey}/g, vKey); 975 if (showUpload) { 976 btnUpload = self.getLayoutTemplate('actionUpload') 977 .replace(/\{uploadClass}/g, config.uploadClass) 978 .replace(/\{uploadIcon}/g, config.uploadIcon) 979 .replace(/\{uploadTitle}/g, config.uploadTitle); 980 } 981 return template 982 .replace(/\{delete}/g, btnDelete) 983 .replace(/\{upload}/g, btnUpload) 984 .replace(/\{other}/g, otherButtons); 985 }, 986 setThumbStatus: function ($thumb, status) { 987 var self = this; 988 if (!self.showPreview) { 989 return; 990 } 991 var icon = 'indicator' + status, msg = icon + 'Title', 992 css = 'file-preview-' + status.toLowerCase(), 993 $indicator = $thumb.find('.file-upload-indicator'), 994 config = self.fileActionSettings; 995 $thumb.removeClass('file-preview-success file-preview-error file-preview-loading'); 996 if (status === 'Error') { 997 $thumb.find('.kv-file-upload').attr('disabled', true); 998 } 999 $indicator.html(config[icon]); 1000 $indicator.attr('title', config[msg]); 1001 $thumb.addClass(css); 1002 }, 1003 clearPreview: function () { 1004 var self = this, $thumbs = !self.showUploadedThumbs ? self.$preview.find('.file-preview-frame') : 1005 self.$preview.find('.file-preview-frame:not(.file-preview-success)'); 1006 $thumbs.remove(); 1007 if (!self.$preview.find('.file-preview-frame').length || !self.showPreview) { 1008 self.resetUpload(); 1009 } 1010 self.validateDefaultPreview(); 1011 }, 1012 initPreview: function (isInit) { 1013 var self = this, cap = self.initialCaption || '', out; 1014 if (!previewCache.count(self.id)) { 1015 self.clearPreview(); 1016 if (isInit) { 1017 self.setCaption(cap); 1018 } else { 1019 self.initCaption(); 1020 } 1021 return; 1022 } 1023 out = previewCache.out(self.id); 1024 cap = isInit && self.initialCaption ? self.initialCaption : out.caption; 1025 self.$preview.html(out.content); 1026 self.setCaption(cap); 1027 if (!isEmpty(out.content)) { 1028 self.$container.removeClass('file-input-new'); 1029 } 1030 }, 1031 initPreviewDeletes: function () { 1032 var self = this, deleteExtraData = self.deleteExtraData || {}, 1033 resetProgress = function () { 1034 var hasFiles = self.isUploadable ? previewCache.count(self.id) : self.$element.get(0).files.length; 1035 if (self.$preview.find('.kv-file-remove').length === 0 && !hasFiles) { 1036 self.reset(); 1037 self.initialCaption = ''; 1038 } 1039 }; 1040 1041 self.$preview.find('.kv-file-remove').each(function () { 1042 var $el = $(this), vUrl = $el.data('url') || self.deleteUrl, vKey = $el.data('key'); 1043 if (isEmpty(vUrl) || vKey === undefined) { 1044 return; 1045 } 1046 var $frame = $el.closest('.file-preview-frame'), cache = previewCache.data[self.id], 1047 settings, params, index = $frame.data('fileindex'), config, extraData; 1048 index = parseInt(index.replace('init_', '')); 1049 config = isEmpty(cache.config) && isEmpty(cache.config[index]) ? null : cache.config[index]; 1050 extraData = isEmpty(config) || isEmpty(config.extra) ? deleteExtraData : config.extra; 1051 if (typeof extraData === "function") { 1052 extraData = extraData(); 1053 } 1054 params = {id: $el.attr('id'), key: vKey, extra: extraData}; 1055 settings = $.extend({ 1056 url: vUrl, 1057 type: 'POST', 1058 dataType: 'json', 1059 data: $.extend({key: vKey}, extraData), 1060 beforeSend: function (jqXHR) { 1061 self.ajaxAborted = false; 1062 self.raise('filepredelete', [vKey, jqXHR, extraData]); 1063 if (self.ajaxAborted) { 1064 jqXHR.abort(); 1065 } else { 1066 addCss($frame, 'file-uploading'); 1067 addCss($el, 'disabled'); 1068 } 1069 }, 1070 success: function (data, textStatus, jqXHR) { 1071 var n, cap; 1072 if (isEmpty(data) || isEmpty(data.error)) { 1073 previewCache.unset(self.id, index); 1074 n = previewCache.count(self.id); 1075 cap = n > 0 ? self.getMsgSelected(n) : ''; 1076 self.raise('filedeleted', [vKey, jqXHR, extraData]); 1077 self.setCaption(cap); 1078 } else { 1079 params.jqXHR = jqXHR; 1080 params.response = data; 1081 self.showError(data.error, params, 'filedeleteerror'); 1082 $frame.removeClass('file-uploading'); 1083 $el.removeClass('disabled'); 1084 resetProgress(); 1085 return; 1086 } 1087 $frame.removeClass('file-uploading').addClass('file-deleted'); 1088 $frame.fadeOut('slow', function () { 1089 self.clearObjects($frame); 1090 $frame.remove(); 1091 resetProgress(); 1092 if (!n && self.getFileStack().length === 0) { 1093 self.setCaption(''); 1094 self.reset(); 1095 } 1096 }); 1097 }, 1098 error: function (jqXHR, textStatus, errorThrown) { 1099 var errMsg = self.parseError(jqXHR, errorThrown); 1100 params.jqXHR = jqXHR; 1101 params.response = {}; 1102 self.showError(errMsg, params, 'filedeleteerror'); 1103 $frame.removeClass('file-uploading'); 1104 resetProgress(); 1105 } 1106 }, self.ajaxDeleteSettings); 1107 handler($el, 'click', function () { 1108 if (!self.validateMinCount()) { 1109 return false; 1110 } 1111 $.ajax(settings); 1112 }); 1113 }); 1114 }, 1115 clearObjects: function ($el) { 1116 $el.find('video audio').each(function () { 1117 this.pause(); 1118 $(this).remove(); 1119 }); 1120 $el.find('img object div').each(function () { 1121 $(this).remove(); 1122 }); 1123 }, 1124 clearFileInput: function () { 1125 var self = this, $el = self.$element, $srcFrm, $tmpFrm, $tmpEl; 1126 if (isEmpty($el.val())) { 1127 return; 1128 } 1129 // Fix for IE ver < 11, that does not clear file inputs 1130 // Requires a sequence of steps to prevent IE crashing but 1131 // still allow clearing of the file input. 1132 if (self.isIE9 || self.isIE10) { 1133 $srcFrm = $el.closest('form'); 1134 $tmpFrm = $(document.createElement('form'));
1135 $tmpEl = $(document.createElement('div')); 1136 $el.before($tmpEl); 1137 if ($srcFrm.length) { 1138 $srcFrm.after($tmpFrm); 1139 } else { 1140 $tmpEl.after($tmpFrm); 1141 } 1142 $tmpFrm.append($el).trigger('reset'); 1143 $tmpEl.before($el).remove(); 1144 $tmpFrm.remove(); 1145 } else { // normal input clear behavior for other sane browsers 1146 $el.val(''); 1147 } 1148 self.fileInputCleared = true; 1149 }, 1150 resetUpload: function () { 1151 var self = this; 1152 self.uploadCache = {content: [], config: [], tags: [], append: true}; 1153 self.uploadCount = 0; 1154 self.uploadStatus = {}; 1155 self.uploadLog = []; 1156 self.uploadAsyncCount = 0; 1157 self.loadedImages = []; 1158 self.totalImagesCount = 0; 1159 self.$btnUpload.removeAttr('disabled'); 1160 self.setProgress(0); 1161 addCss(self.$progress, 'hide'); 1162 self.resetErrors(false); 1163 self.ajaxAborted = false; 1164 self.ajaxRequests = []; 1165 self.resetCanvas(); 1166 }, 1167 resetCanvas: function () { 1168 var self = this; 1169 if (self.canvas && self.imageCanvasContext) { 1170 self.imageCanvasContext.clearRect(0, 0, self.canvas.width, self.canvas.height); 1171 } 1172 }, 1173 cancel: function () { 1174 var self = this, xhr = self.ajaxRequests, len = xhr.length, i; 1175 if (len > 0) { 1176 for (i = 0; i < len; i += 1) { 1177 self.cancelling = true; 1178 xhr[i].abort(); 1179 } 1180 } 1181 self.getThumbs().each(function () { 1182 var $thumb = $(this), ind = $thumb.attr('data-fileindex'); 1183 $thumb.removeClass('file-uploading'); 1184 if (self.filestack[ind] !== undefined) { 1185 $thumb.find('.kv-file-upload').removeClass('disabled').removeAttr('disabled'); 1186 $thumb.find('.kv-file-remove').removeClass('disabled').removeAttr('disabled'); 1187 } 1188 self.unlock(); 1189 }); 1190 }, 1191 cleanMemory: function ($thumb) { 1192 var data = $thumb.is('img') ? $thumb.attr('src') : $thumb.find('source').attr('src'); 1193 /** @namespace objUrl.revokeObjectURL */ 1194 objUrl.revokeObjectURL(data); 1195 }, 1196 hasInitialPreview: function () { 1197 var self = this; 1198 return !self.overwriteInitial && previewCache.count(self.id); 1199 }, 1200 clear: function () { 1201 var self = this, cap; 1202 self.$btnUpload.removeAttr('disabled'); 1203 self.getThumbs().find('video,audio,img').each(function () { 1204 self.cleanMemory($(this)); 1205 }); 1206 self.resetUpload(); 1207 self.filestack = []; 1208 self.clearFileInput(); 1209 self.resetErrors(true); 1210 self.raise('fileclear'); 1211 if (self.hasInitialPreview()) { 1212 self.showFileIcon(); 1213 self.resetPreview(); 1214 self.initPreviewDeletes(); 1215 self.$container.removeClass('file-input-new'); 1216 } else { 1217 self.getThumbs().each(function () { 1218 self.clearObjects($(this)); 1219 }); 1220 if (self.isUploadable) { 1221 previewCache.data[self.id] = {}; 1222 } 1223 self.$preview.html(''); 1224 cap = (!self.overwriteInitial && self.initialCaption.length > 0) ? self.initialCaption : ''; 1225 self.setCaption(cap); 1226 self.$caption.attr('title', ''); 1227 addCss(self.$container, 'file-input-new'); 1228 self.validateDefaultPreview(); 1229 } 1230 if (self.$container.find('.file-preview-frame').length === 0) { 1231 if (!self.initCaption()) { 1232 self.$captionContainer.find('.kv-caption-icon').hide(); 1233 } 1234 } 1235 self.hideFileIcon();
1236 self.raise('filecleared'); 1237 self.$captionContainer.focus(); 1238 self.setFileDropZoneTitle(); 1239 }, 1240 resetPreview: function () { 1241 var self = this, out, cap; 1242 if (previewCache.count(self.id)) { 1243 out = previewCache.out(self.id); 1244 self.$preview.html(out.content); 1245 cap = self.initialCaption ? self.initialCaption : out.caption; 1246 self.setCaption(cap); 1247 } else { 1248 self.clearPreview(); 1249 self.initCaption(); 1250 } 1251 }, 1252 clearDefaultPreview: function() { 1253 var self = this; 1254 self.$preview.find('.file-default-preview').remove(); 1255 }, 1256 validateDefaultPreview: function() { 1257 var self = this; 1258 if (!self.showPreview || isEmpty(self.defaultPreviewContent)) { 1259 return; 1260 } 1261 self.$preview.html('<div class="file-default-preview">' + self.defaultPreviewContent + '</div>'); 1262 self.$container.removeClass('file-input-new'); 1263 }, 1264 resetPreviewThumbs: function (isAjax) { 1265 var self = this, out; 1266 if (isAjax) { 1267 self.clearPreview(); 1268 self.filestack = []; 1269 return; 1270 } 1271 if (self.hasInitialPreview()) { 1272 out = previewCache.out(self.id); 1273 self.$preview.html(out.content); 1274 self.setCaption(out.caption); 1275 self.initPreviewDeletes(); 1276 } else { 1277 self.clearPreview(); 1278 } 1279 }, 1280 reset: function () { 1281 var self = this; 1282 self.resetPreview(); 1283 self.$container.find('.fileinput-filename').text(''); 1284 self.raise('filereset'); 1285 addCss(self.$container, 'file-input-new'); 1286 if (self.$preview.find('.file-preview-frame').length || self.isUploadable && self.dropZoneEnabled) { 1287 self.$container.removeClass('file-input-new'); 1288 } 1289 self.setFileDropZoneTitle(); 1290 self.filestack = []; 1291 self.filenames = []; 1292 self.formdata = {}; 1293 }, 1294 disable: function () { 1295 var self = this; 1296 self.isDisabled = true; 1297 self.raise('filedisabled'); 1298 self.$element.attr('disabled', 'disabled'); 1299 self.$container.find(".kv-fileinput-caption").addClass("file-caption-disabled"); 1300 self.$container.find(".btn-file, .fileinput-remove, .fileinput-upload, .file-preview-frame button").attr("disabled", 1301 true); 1302 self.initDragDrop(); 1303 }, 1304 enable: function () { 1305 var self = this; 1306 self.isDisabled = false; 1307 self.raise('fileenabled'); 1308 self.$element.removeAttr('disabled'); 1309 self.$container.find(".kv-fileinput-caption").removeClass("file-caption-disabled"); 1310 self.$container.find(".btn-file, .fileinput-remove, .fileinput-upload, .file-preview-frame button").removeAttr("disabled"); 1311 self.initDragDrop(); 1312 }, 1313 getThumbs: function (css) { 1314 css = css || ''; 1315 return this.$preview.find('.file-preview-frame:not(.file-preview-initial)' + css); 1316 }, 1317 getExtraData: function (previewId, index) { 1318 var self = this, data = self.uploadExtraData; 1319 if (typeof self.uploadExtraData === "function") { 1320 data = self.uploadExtraData(previewId, index); 1321 } 1322 return data; 1323 }, 1324 uploadExtra: function (previewId, index) { 1325 var self = this, data = self.getExtraData(previewId, index); 1326 if (data.length === 0) { 1327 return; 1328 } 1329 $.each(data, function (key, value) { 1330 self.formdata.append(key, value); 1331 }); 1332 }, 1333 setAsyncUploadStatus: function (previewId, pct, total) { 1334 var self = this, sum = 0; 1335 self.setProgress(pct, $('#' + previewId).find('.file-thumb-progress')); 1336 self.uploadStatus[previewId] = pct; 1337 $.each(self.uploadStatus, function (key, value) { 1338 sum += value; 1339 }); 1340 self.setProgress(Math.ceil(sum / total)); 1341 1342 }, 1343 initXhr: function (xhrobj, previewId, fileCount) { 1344 var self = this; 1345 if (xhrobj.upload) { 1346 xhrobj.upload.addEventListener('progress', function (event) { 1347 var pct = 0, position = event.loaded || event.position, total = event.total; 1348 /** @namespace event.lengthComputable */ 1349 if (event.lengthComputable) {
1350 pct = Math.ceil(position / total * 100); 1351 } 1352 if (previewId) { 1353 self.setAsyncUploadStatus(previewId, pct, fileCount); 1354 } else { 1355 self.setProgress(Math.ceil(pct)); 1356 } 1357 }, false); 1358 } 1359 return xhrobj; 1360 }, 1361 ajaxSubmit: function (fnBefore, fnSuccess, fnComplete, fnError, previewId, index) { 1362 var self = this, settings; 1363 self.raise('filepreajax', [previewId, index]); 1364 self.uploadExtra(previewId, index); 1365 settings = $.extend({ 1366 xhr: function () { 1367 var xhrobj = $.ajaxSettings.xhr(); 1368 return self.initXhr(xhrobj, previewId, self.getFileStack().length); 1369 }, 1370 url: self.uploadUrl, 1371 type: 'POST', 1372 dataType: 'json', 1373 data: self.formdata, 1374 cache: false, 1375 processData: false, 1376 contentType: false, 1377 beforeSend: fnBefore, 1378 success: fnSuccess, 1379 complete: fnComplete, 1380 error: fnError 1381 }, self.ajaxSettings); 1382 self.ajaxRequests.push($.ajax(settings)); 1383 }, 1384 initUploadSuccess: function (out, $thumb, allFiles) { 1385 var self = this, append, data, index, $newThumb, content, config, tags, i; 1386 if (!self.showPreview || typeof out !== 'object' || $.isEmptyObject(out)) { 1387 return; 1388 } 1389 if (out.initialPreview !== undefined && out.initialPreview.length > 0) { 1390 self.hasInitData = true; 1391 content = out.initialPreview || []; 1392 config = out.initialPreviewConfig || []; 1393 tags = out.initialPreviewThumbTags || []; 1394 append = out.append === undefined || out.append ? true : false; 1395 self.overwriteInitial = false; 1396 if ($thumb !== undefined) { 1397 if (!allFiles) { 1398 index = previewCache.add(self.id, content, config[0], tags[0], append); 1399 data = previewCache.get(self.id, index, false); 1400 $newThumb = $(data).hide(); 1401 $thumb.after($newThumb).fadeOut('slow', function () { 1402 $newThumb.fadeIn('slow').css('display:inline-block'); 1403 self.initPreviewDeletes(); 1404 self.clearFileInput(); 1405 $thumb.remove(); 1406 }); 1407 } else { 1408 i = $thumb.attr('data-fileindex'); 1409 self.uploadCache.content[i] = content[0]; 1410 self.uploadCache.config[i] = config[0]; 1411 self.uploadCache.tags[i] = tags[0]; 1412 self.uploadCache.append = append; 1413 } 1414 } else { 1415 previewCache.set(self.id, content, config, tags, append); 1416 self.initPreview(); 1417 self.initPreviewDeletes(); 1418 } 1419 } 1420 }, 1421 initSuccessThumbs: function () { 1422 var self = this; 1423 if (!self.showPreview) { 1424 return; 1425 } 1426 self.getThumbs('.file-preview-success').each(function () { 1427 var $thumb = $(this), $remove = $thumb.find('.kv-file-remove'); 1428 $remove.removeAttr('disabled'); 1429 handler($remove, 'click', function () { 1430 var out = self.raise('filesuccessremove', [$thumb.attr('id'), $thumb.data('fileindex')]); 1431 self.cleanMemory($thumb); 1432 if (out === false) { 1433 return; 1434 } 1435 $thumb.fadeOut('slow', function () { 1436 $thumb.remove(); 1437 if (!self.$preview.find('.file-preview-frame').length) { 1438 self.reset(); 1439 } 1440 }); 1441 }); 1442 }); 1443 }, 1444 checkAsyncComplete: function () { 1445 var self = this, previewId, i; 1446 for (i = 0; i < self.filestack.length; i++) { 1447 if (self.filestack[i]) { 1448 previewId = self.previewInitId + "-" + i; 1449 if ($.inArray(previewId, self.uploadLog) === -1) { 1450 return false; 1451 } 1452 } 1453 } 1454 return (self.uploadAsyncCount === self.uploadLog.length); 1455 }, 1456 uploadSingle: function (i, files, allFiles) { 1457 var self = this, total = self.getFileStack().length, formdata = new FormData(), outData, 1458 previewId = self.previewInitId + "-" + i, $thumb, chkComplete, $btnUpload, $btnDelete, 1459 hasPostData = self.filestack.length > 0 || !$.isEmptyObject(self.uploadExtraData), 1460 fnBefore, fnSuccess, fnComplete, fnError, updateUploadLog, params = {id: previewId, index: i};
1461 self.formdata = formdata; 1462 if (self.showPreview) { 1463 $thumb = $('#' + previewId + ':not(.file-preview-initial)'); 1464 $btnUpload = $thumb.find('.kv-file-upload'); 1465 $btnDelete = $thumb.find('.kv-file-remove'); 1466 $('#' + previewId).find('.file-thumb-progress').removeClass('hide'); 1467 } 1468 if (total === 0 || !hasPostData || ($btnUpload && $btnUpload.hasClass('disabled')) || self.abort(params)) { 1469 return; 1470 } 1471 updateUploadLog = function (i, previewId) { 1472 self.filestack[i] = undefined; 1473 self.uploadLog.push(previewId); 1474 if (self.checkAsyncComplete()) { 1475 self.fileBatchCompleted = true; 1476 } 1477 }; 1478 chkComplete = function () { 1479 if (!self.fileBatchCompleted) { 1480 return; 1481 } 1482 setTimeout(function () { 1483 if (self.showPreview) { 1484 previewCache.set( 1485 self.id, 1486 self.uploadCache.content, 1487 self.uploadCache.config, 1488 self.uploadCache.tags, 1489 self.uploadCache.append 1490 ); 1491 if (self.hasInitData) { 1492 self.initPreview(); 1493 self.initPreviewDeletes(); 1494 } 1495 } 1496 self.unlock(); 1497 self.clearFileInput(); 1498 self.raise('filebatchuploadcomplete', [self.filestack, self.getExtraData()]); 1499 self.uploadCount = 0; 1500 self.uploadStatus = {}; 1501 self.uploadLog = []; 1502 self.setProgress(100); 1503 }, 100); 1504 }; 1505 fnBefore = function (jqXHR) { 1506 outData = self.getOutData(jqXHR); 1507 self.fileBatchCompleted = false; 1508 if (self.showPreview) { 1509 if (!$thumb.hasClass('file-preview-success')) { 1510 self.setThumbStatus($thumb, 'Loading'); 1511 addCss($thumb, 'file-uploading'); 1512 } 1513 $btnUpload.attr('disabled', true); 1514 $btnDelete.attr('disabled', true); 1515 } 1516 if (!allFiles) { 1517 self.lock(); 1518 } 1519 self.raise('filepreupload', [outData, previewId, i]); 1520 params = $.extend(params, outData); 1521 if (self.abort(params)) { 1522 jqXHR.abort(); 1523 self.setProgress(100); 1524 } 1525 }; 1526 fnSuccess = function (data, textStatus, jqXHR) { 1527 outData = self.getOutData(jqXHR, data); 1528 params = $.extend(params, outData); 1529 setTimeout(function () { 1530 if (isEmpty(data) || isEmpty(data.error)) { 1531 if (self.showPreview) { 1532 self.setThumbStatus($thumb, 'Success'); 1533 $btnUpload.hide(); 1534 self.initUploadSuccess(data, $thumb, allFiles); 1535 } 1536 self.raise('fileuploaded', [outData, previewId, i]); 1537 if (!allFiles) { 1538 self.resetFileStack(); 1539 self.filestack[i] = undefined; 1540 self.filenames[i] = undefined; 1541 } else { 1542 updateUploadLog(i, previewId); 1543 } 1544 } else { 1545 self.setThumbStatus($thumb, 'Error'); 1546 self.showUploadError(data.error, params); 1547 if (allFiles) { 1548 updateUploadLog(i, previewId); 1549 } 1550 } 1551 }, 100); 1552 }; 1553 fnComplete = function () { 1554 setTimeout(function () { 1555 if (self.showPreview) { 1556 $btnUpload.removeAttr('disabled'); 1557 $btnDelete.removeAttr('disabled'); 1558 $thumb.removeClass('file-uploading'); 1559 } 1560 if (!allFiles) { 1561 self.unlock(false); 1562 } else { 1563 chkComplete(); 1564 } 1565 self.initSuccessThumbs(); 1566 }, 100); 1567 }; 1568 fnError = function (jqXHR, textStatus, errorThrown) { 1569 var errMsg = self.parseError(jqXHR, errorThrown, (allFiles ? files[i].name : null)); 1570 setTimeout(function () { 1571 if (allFiles) { 1572 updateUploadLog(i, previewId); 1573 } 1574 self.uploadStatus[previewId] = 100; 1575 self.setThumbStatus($thumb, 'Error'); 1576 params = $.extend(params, self.getOutData(jqXHR)); 1577 self.showUploadError(errMsg, params); 1578 }, 100); 1579 }; 1580 formdata.append(self.uploadFileAttr, files[i], self.filenames[i]); 1581 formdata.append('file_id', i); 1582 self.ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, previewId, i); 1583 }, 1584 uploadBatch: function () {
1585 var self = this, files = self.filestack, total = files.length, params = {}, 1586 hasPostData = self.filestack.length > 0 || !$.isEmptyObject(self.uploadExtraData), 1587 setAllUploaded, fnBefore, fnSuccess, fnComplete, fnError; 1588 self.formdata = new FormData(); 1589 if (total === 0 || !hasPostData || self.abort(params)) { 1590 return; 1591 } 1592 setAllUploaded = function () { 1593 $.each(files, function (key) { 1594 self.filestack[key] = undefined; 1595 }); 1596 self.clearFileInput(); 1597 }; 1598 fnBefore = function (jqXHR) { 1599 self.lock(); 1600 var outData = self.getOutData(jqXHR); 1601 if (self.showPreview) { 1602 self.getThumbs().each(function () { 1603 var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'), 1604 $btnDelete = $thumb.find('.kv-file-remove'); 1605 if (!$thumb.hasClass('file-preview-success')) { 1606 self.setThumbStatus($thumb, 'Loading'); 1607 addCss($thumb, 'file-uploading'); 1608 } 1609 $btnUpload.attr('disabled', true); 1610 $btnDelete.attr('disabled', true); 1611 }); 1612 } 1613 self.raise('filebatchpreupload', [outData]); 1614 if (self.abort(outData)) { 1615 jqXHR.abort(); 1616 self.setProgress(100); 1617 } 1618 }; 1619 fnSuccess = function (data, textStatus, jqXHR) { 1620 /** @namespace data.errorkeys */ 1621 var outData = self.getOutData(jqXHR, data), $thumbs = self.getThumbs(), key = 0, 1622 keys = isEmpty(data) || isEmpty(data.errorkeys) ? [] : data.errorkeys; 1623 if (isEmpty(data) || isEmpty(data.error)) { 1624 self.raise('filebatchuploadsuccess', [outData]); 1625 setAllUploaded(); 1626 if (self.showPreview) { 1627 $thumbs.each(function () { 1628 var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'); 1629 $thumb.find('.kv-file-upload').hide(); 1630 self.setThumbStatus($thumb, 'Success'); 1631 $thumb.removeClass('file-uploading'); 1632 $btnUpload.removeAttr('disabled'); 1633 }); 1634 self.initUploadSuccess(data); 1635 } else { 1636 self.reset(); 1637 } 1638 } else { 1639 if (self.showPreview) { 1640 $thumbs.each(function () { 1641 var $thumb = $(this), $btnDelete = $thumb.find('.kv-file-remove'), 1642 $btnUpload = $thumb.find('.kv-file-upload'); 1643 $thumb.removeClass('file-uploading'); 1644 $btnUpload.removeAttr('disabled'); 1645 $btnDelete.removeAttr('disabled'); 1646 if (keys.length === 0) { 1647 self.setThumbStatus($thumb, 'Error'); 1648 return; 1649 } 1650 if ($.inArray(key, keys) !== -1) { 1651 self.setThumbStatus($thumb, 'Error'); 1652 } else { 1653 $thumb.find('.kv-file-upload').hide(); 1654 self.setThumbStatus($thumb, 'Success'); 1655 self.filestack[key] = undefined; 1656 } 1657 key++; 1658 }); 1659 self.initUploadSuccess(data); 1660 } 1661 self.showUploadError(data.error, outData, 'filebatchuploaderror'); 1662 } 1663 }; 1664 fnComplete = function () { 1665 self.setProgress(100); 1666 self.unlock(); 1667 self.initSuccessThumbs(); 1668 self.clearFileInput(); 1669 self.raise('filebatchuploadcomplete', [self.filestack, self.getExtraData()]); 1670 }; 1671 fnError = function (jqXHR, textStatus, errorThrown) { 1672 var outData = self.getOutData(jqXHR), errMsg = self.parseError(jqXHR, errorThrown); 1673 self.showUploadError(errMsg, outData, 'filebatchuploaderror'); 1674 self.uploadFileCount = total - 1; 1675 if (!self.showPreview) { 1676 return; 1677 } 1678 self.getThumbs().each(function () { 1679 var $thumb = $(this), key = $thumb.attr('data-fileindex'); 1680 $thumb.removeClass('file-uploading'); 1681 if (self.filestack[key] !== undefined) { 1682 self.setThumbStatus($thumb, 'Error'); 1683 } 1684 }); 1685 self.getThumbs().removeClass('file-uploading'); 1686 self.getThumbs(' .kv-file-upload').removeAttr('disabled'); 1687 self.getThumbs(' .kv-file-delete').removeAttr('disabled'); 1688 }; 1689 $.each(files, function (key, data) { 1690 if (!isEmpty(files[key])) { 1691 self.formdata.append(self.uploadFileAttr, data, self.filenames[key]); 1692 } 1693 }); 1694 self.ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError); 1695 }, 1696 uploadExtraOnly: function () { 1697 var self = this, params = {}, fnBefore, fnSuccess, fnComplete, fnError; 1698 self.formdata = new FormData(); 1699 if (self.abort(params)) { 1700 return; 1701 } 1702 fnBefore = function (jqXHR) { 1703 self.lock(); 1704 var outData = self.getOutData(jqXHR); 1705 self.raise('filebatchpreupload', [outData]); 1706 self.setProgress(50); 1707 params.data = outData; 1708 params.xhr = jqXHR; 1709 if (self.abort(params)) { 1710 jqXHR.abort(); 1711 self.setProgress(100); 1712 } 1713 }; 1714 fnSuccess = function (data, textStatus, jqXHR) { 1715 var outData = self.getOutData(jqXHR, data); 1716 if (isEmpty(data) || isEmpty(data.error)) { 1717 self.raise('filebatchuploadsuccess', [outData]); 1718 self.clearFileInput(); 1719 self.initUploadSuccess(data); 1720 } else { 1721 self.showUploadError(data.error, outData, 'filebatchuploaderror'); 1722 } 1723 }; 1724 fnComplete = function () { 1725 self.setProgress(100); 1726 self.unlock(); 1727 self.clearFileInput(); 1728 self.raise('filebatchuploadcomplete', [self.filestack, self.getExtraData()]); 1729 }; 1730 fnError = function (jqXHR, textStatus, errorThrown) { 1731 var outData = self.getOutData(jqXHR), errMsg = self.parseError(jqXHR, errorThrown); 1732 params.data = outData; 1733 self.showUploadError(errMsg, outData, 'filebatchuploaderror'); 1734 }; 1735 self.ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError); 1736 }, 1737 upload: function () { 1738 var self = this, totLen = self.getFileStack().length, params = {}, 1739 i, outData, len, hasExtraData = !$.isEmptyObject(self.getExtraData()); 1740 if (self.minFileCount > 0 && self.getFileCount(totLen) < self.minFileCount) { 1741 self.noFilesError(params); 1742 return; 1743 } 1744 if (!self.isUploadable || self.isDisabled || (totLen === 0 && !hasExtraData)) { 1745 return; 1746 } 1747 self.resetUpload(); 1748 self.$progress.removeClass('hide'); 1749 self.uploadCount = 0; 1750 self.uploadStatus = {}; 1751 self.uploadLog = []; 1752 self.lock(); 1753 self.setProgress(2); 1754 if (totLen === 0 && hasExtraData) { 1755 self.uploadExtraOnly(); 1756 return; 1757 } 1758 len = self.filestack.length; 1759 self.hasInitData = false; 1760 if (self.uploadAsync) { 1761 outData = self.getOutData(); 1762 self.raise('filebatchpreupload', [outData]); 1763 self.fileBatchCompleted = false; 1764 self.uploadCache = {content: [], config: [], tags: [], append: true}; 1765 self.uploadAsyncCount = self.getFileStack().length; 1766 for (i = 0; i < len; i++) { 1767 self.uploadCache.content[i] = null; 1768 self.uploadCache.config[i] = null; 1769 self.uploadCache.tags[i] = null; 1770 } 1771 for (i = 0; i < len; i++) { 1772 if (self.filestack[i] !== undefined) { 1773 self.uploadSingle(i, self.filestack, true); 1774 } 1775 } 1776 return; 1777 } 1778 self.uploadBatch(); 1779 }, 1780 initFileActions: function () { 1781 var self = this; 1782 if (!self.showPreview) { 1783 return; 1784 } 1785 self.$preview.find('.kv-file-remove').each(function () { 1786 var $el = $(this), $frame = $el.closest('.file-preview-frame'), hasError, 1787 id = $frame.attr('id'), ind = $frame.attr('data-fileindex'), n, cap, status; 1788 handler($el, 'click', function () { 1789 status = self.raise('filepreremove', [id, ind]); 1790 if (status === false || !self.validateMinCount()) { 1791 return false; 1792 } 1793 hasError = $frame.hasClass('file-preview-error'); 1794 self.cleanMemory($frame); 1795 $frame.fadeOut('slow', function () { 1796 self.filestack[ind] = undefined; 1797 self.filenames[ind] = ''; 1798 self.clearObjects($frame); 1799 $frame.remove(); 1800 if (id && hasError) { 1801 self.$errorContainer.find('li[data-file-id="' + id + '"]').fadeOut('fast', function () { 1802 $(this).remove();
1803 if (!self.errorsExist()) { 1804 self.resetErrors(); 1805 } 1806 }); 1807 } 1808 var filestack = self.getFileStack(true), len = filestack.length, chk = previewCache.count(self.id), 1809 hasThumb = self.showPreview && self.$preview.find('.file-preview-frame').length; 1810 self.clearFileInput(); 1811 if (len === 0 && chk === 0 && !hasThumb) { 1812 self.reset(); 1813 } else { 1814 n = chk + len; 1815 cap = n > 1 ? self.getMsgSelected(n) : (filestack[0] ? filestack[0].name : ''); 1816 self.setCaption(cap); 1817 } 1818 self.raise('fileremoved', [id, ind]); 1819 }); 1820 }); 1821 }); 1822 self.$preview.find('.kv-file-upload').each(function () { 1823 var $el = $(this); 1824 handler($el, 'click', function () { 1825 var $frame = $el.closest('.file-preview-frame'), 1826 ind = $frame.attr('data-fileindex'); 1827 if (!$frame.hasClass('file-preview-error')) { 1828 self.uploadSingle(ind, self.filestack, false); 1829 } 1830 }); 1831 }); 1832 }, 1833 hideFileIcon: function () { 1834 if (this.overwriteInitial) { 1835 this.$captionContainer.find('.kv-caption-icon').hide(); 1836 } 1837 }, 1838 showFileIcon: function () { 1839 this.$captionContainer.find('.kv-caption-icon').show(); 1840 }, 1841 addError: function (msg) { 1842 var self = this, $error = self.$errorContainer; 1843 if (msg && $error.length) { 1844 $error.html(self.errorCloseButton + msg); 1845 $error.find('.kv-error-close').off('click').on('click', function () { 1846 $error.fadeOut('slow'); 1847 }); 1848 } 1849 }, 1850 resetErrors: function (fade) { 1851 var self = this, $error = self.$errorContainer; 1852 self.isError = false; 1853 self.$container.removeClass('has-error'); 1854 $error.html(''); 1855 if (fade) { 1856 $error.fadeOut('slow'); 1857 } else { 1858 $error.hide(); 1859 } 1860 }, 1861 showFolderError: function (folders) { 1862 var self = this, $error = self.$errorContainer; 1863 if (!folders) { 1864 return; 1865 } 1866 self.addError(self.msgFoldersNotAllowed.replace(/\{n}/g, folders)); 1867 $error.fadeIn(800); 1868 addCss(self.$container, 'has-error'); 1869 self.raise('filefoldererror', [folders]); 1870 }, 1871 showUploadError: function (msg, params, event) { 1872 var self = this, $error = self.$errorContainer, ev = event || 'fileuploaderror', 1873 e = params && params.id ? '<li data-file-id="' + params.id + '">' + msg + '</li>' : '<li>' + msg + '</li>'; 1874 if ($error.find('ul').length === 0) { 1875 self.addError('<ul>' + e + '</ul>'); 1876 } else { 1877 $error.find('ul').append(e); 1878 } 1879 $error.fadeIn(800); 1880 self.raise(ev, [params]); 1881 self.$container.removeClass('file-input-new'); 1882 addCss(self.$container, 'has-error'); 1883 return true; 1884 }, 1885 showError: function (msg, params, event) { 1886 var self = this, $error = self.$errorContainer, ev = event || 'fileerror'; 1887 params = params || {}; 1888 params.reader = self.reader; 1889 self.addError(msg); 1890 $error.fadeIn(800); 1891 self.raise(ev, [params]); 1892 if (!self.isUploadable) { 1893 self.clearFileInput(); 1894 } 1895 self.$container.removeClass('file-input-new'); 1896 addCss(self.$container, 'has-error'); 1897 self.$btnUpload.attr('disabled', true); 1898 return true; 1899 }, 1900 errorHandler: function (evt, caption) { 1901 var self = this, err = evt.target.error; 1902 /** @namespace err.NOT_FOUND_ERR */ 1903 /** @namespace err.SECURITY_ERR */ 1904 /** @namespace err.NOT_READABLE_ERR */ 1905 if (err.code === err.NOT_FOUND_ERR) { 1906 self.showError(self.msgFileNotFound.replace('{name}', caption)); 1907 } else if (err.code === err.SECURITY_ERR) { 1908 self.showError(self.msgFileSecured.replace('{name}', caption)); 1909 } else if (err.code === err.NOT_READABLE_ERR) { 1910 self.showError(self.msgFileNotReadable.replace('{name}', caption)); 1911 } else if (err.code === err.ABORT_ERR) { 1912 self.showError(self.msgFilePreviewAborted.replace('{name}', caption)); 1913 } else { 1914 self.showError(self.msgFilePreviewError.replace('{name}', caption)); 1915 } 1916 }, 1917 parseFileType: function (file) { 1918 var self = this, isValid, vType, cat, i; 1919 for (i = 0;
1919 i < defaultPreviewTypes.length; i += 1) { 1920 cat = defaultPreviewTypes[i]; 1921 isValid = isSet(cat, self.fileTypeSettings) ? self.fileTypeSettings[cat] : defaultFileTypeSettings[cat]; 1922 vType = isValid(file.type, file.name) ? cat : ''; 1923 if (!isEmpty(vType)) { 1924 return vType; 1925 } 1926 } 1927 return 'other'; 1928 }, 1929 previewDefault: function (file, previewId, isDisabled) { 1930 if (!this.showPreview) { 1931 return; 1932 } 1933 var self = this, frameClass = '', fname = file ? file.name : '', 1934 /** @namespace objUrl.createObjectURL */ 1935 data = objUrl.createObjectURL(file), ind = previewId.slice(previewId.lastIndexOf('-') + 1), 1936 config = self.previewSettings.other || defaultPreviewSettings.other, 1937 footer = self.renderFileFooter(file.name, config.width), 1938 previewOtherTemplate = self.parseFilePreviewIcon(self.getPreviewTemplate('other'), fname); 1939 if (isDisabled === true) { 1940 if (!self.isUploadable) { 1941 footer += '<div class="file-other-error" title="' + self.fileActionSettings.indicatorErrorTitle + 1942 '">' + self.fileActionSettings.indicatorError + '</div>'; 1943 } 1944 } 1945 self.clearDefaultPreview(); 1946 self.$preview.append("\n" + previewOtherTemplate 1947 .replace(/\{previewId}/g, previewId) 1948 .replace(/\{frameClass}/g, frameClass) 1949 .replace(/\{fileindex}/g, ind) 1950 .replace(/\{caption}/g, self.slug(file.name)) 1951 .replace(/\{width}/g, config.width) 1952 .replace(/\{height}/g, config.height) 1953 .replace(/\{type}/g, file.type) 1954 .replace(/\{data}/g, data) 1955 .replace(/\{footer}/g, footer)); 1956 if (isDisabled === true && self.isUploadable) { 1957 self.setThumbStatus($('#' + previewId), 'Error'); 1958 } 1959 }, 1960 previewFile: function (i, file, theFile, previewId, data) { 1961 if (!this.showPreview) { 1962 return; 1963 } 1964 var self = this, cat = self.parseFileType(file), fname = file ? file.name : '', caption = self.slug(fname), 1965 content, strText, types = self.allowedPreviewTypes, mimes = self.allowedPreviewMimeTypes, 1966 tmplt = self.getPreviewTemplate(cat), chkTypes = types && types.indexOf(cat) >= 0, id, 1967 config = isSet(cat, self.previewSettings) ? self.previewSettings[cat] : defaultPreviewSettings[cat], 1968 chkMimes = mimes && mimes.indexOf(file.type) !== -1, 1969 footer = self.renderFileFooter(caption, config.width), modal = '', 1970 ind = previewId.slice(previewId.lastIndexOf('-') + 1); 1971 if (chkTypes || chkMimes) { 1972 tmplt = self.parseFilePreviewIcon(tmplt, fname.split('.').pop()); 1973 if (cat === 'text') { 1974 strText = htmlEncode(theFile.target.result); 1975 id = 'text-' + uniqId(); 1976 content = tmplt.replace(/\{zoom}/g, self.getLayoutTemplate('zoom')); 1977 modal = self.getLayoutTemplate('modal').replace('{id}', id) 1978 .replace(/\{title}/g, caption) 1979 .replace(/\{body}/g, strText).replace(/\{heading}/g, self.msgZoomModalHeading); 1980 content = content.replace(/\{previewId}/g, previewId).replace(/\{caption}/g, caption) 1981 .replace(/\{width}/g, config.width).replace(/\{height}/g, config.height) 1982 .replace(/\{frameClass}/g, '').replace(/\{zoomInd}/g, self.zoomIndicator) 1983 .replace(/\{footer}/g, footer).replace(/\{fileindex}/g, ind) 1984 .replace(/\{type}/g, file.type).replace(/\{zoomTitle}/g, self.msgZoomTitle) 1985 .replace(/\{dialog}/g, "$('#" + id + "').modal('show')") 1986 .replace(/\{data}/g, strText) + modal; 1987 } else { 1988 content = tmplt.replace(/\{previewId}
1988/g, previewId).replace(/\{caption}/g, caption) 1989 .replace(/\{frameClass}/g, '').replace(/\{type}/g, file.type).replace(/\{fileindex}/g, ind) 1990 .replace(/\{width}/g, config.width).replace(/\{height}/g, config.height) 1991 .replace(/\{footer}/g, footer).replace(/\{data}/g, data); 1992 } 1993 self.clearDefaultPreview(); 1994 self.$preview.append("\n" + content); 1995 self.validateImage(i, previewId, caption, file.type); 1996 } else { 1997 self.previewDefault(file, previewId); 1998 } 1999 }, 2000 slugDefault: function (text) { 2001 return isEmpty(text) ? '' : text.split(/(\\|\/)/g).pop().replace(/[^\w\u00C0-\u017F\-.\\\/ ]+/g, ''); 2002 }, 2003 getFileStack: function (skipNull) { 2004 var self = this; 2005 return self.filestack.filter(function (n) { 2006 return (skipNull ? n !== undefined : n !== undefined && n !== null); 2007 }); 2008 }, 2009 readFiles: function (files) { 2010 this.reader = new FileReader(); 2011 var self = this, $el = self.$element, $preview = self.$preview, reader = self.reader, 2012 $container = self.$previewContainer, $status = self.$previewStatus, msgLoading = self.msgLoading, 2013 msgProgress = self.msgProgress, previewInitId = self.previewInitId, numFiles = files.length, 2014 settings = self.fileTypeSettings, ctr = self.filestack.length, readFile, 2015 throwError = function (msg, file, previewId, index) { 2016 var p1 = $.extend(self.getOutData({}, {}, files), {id: previewId, index: index}), 2017 p2 = {id: previewId, index: index, file: file, files: files}; 2018 self.previewDefault(file, previewId, true); 2019 if (self.isUploadable) { 2020 self.filestack.push(undefined); 2021 } 2022 setTimeout(readFile(index + 1), 100); 2023 self.initFileActions(); 2024 return self.isUploadable ? self.showUploadError(msg, p1) : self.showError(msg, p2); 2025 }; 2026 2027 self.loadedImages = []; 2028 self.totalImagesCount = 0; 2029 2030 $.each(files, function (key, file) { 2031 var cat = 'image', 2032 func = isSet(cat, self.fileTypeSettings) ? self.fileTypeSettings[cat] : defaultFileTypeSettings[cat]; 2033 if (func && func(file.type)) { 2034 self.totalImagesCount++; 2035 } 2036 if (file && file.name) { 2037 self.filenames[key] = file.name; 2038 } 2039 }); 2040 2041 readFile = function (i) { 2042 if (isEmpty($el.attr('multiple'))) { 2043 numFiles = 1; 2044 } 2045 if (i >= numFiles) { 2046 if (self.isUploadable && self.filestack.length > 0) { 2047 self.raise('filebatchselected', [self.getFileStack()]); 2048 } else { 2049 self.raise('filebatchselected', [files]); 2050 } 2051 $container.removeClass('loading'); 2052 $status.html(''); 2053 return; 2054 } 2055 var node = ctr + i, previewId = previewInitId + "-" + node, isText, file = files[i], 2056 caption = self.slug(file.name), fileSize = (file.size || 0) / 1000, checkFile, fileExtExpr = '', 2057 previewData = objUrl.createObjectURL(file), fileCount = 0, j, msg, typ, chk, 2058 fileTypes = self.allowedFileTypes, strTypes = isEmpty(fileTypes) ? '' : fileTypes.join(', '), 2059 fileExt = self.allowedFileExtensions, strExt = isEmpty(fileExt) ? '' : fileExt.join(', '); 2060 if (!isEmpty(fileExt)) { 2061 fileExtExpr = new RegExp('\\.(' + fileExt.join('|') + ')$', 'i'); 2062 } 2063 fileSize = fileSize.toFixed(2); 2064 if (self.maxFileSize > 0 && fileSize > self.maxFileSize) { 2065 msg = self.msgSizeTooLarge.replace('{name}', caption) 2066 .replace('{size}', fileSize) 2067 .replace('{maxSize}', self.maxFileSize); 2068 self.isError = throwError(msg, file, previewId, i); 2069 return; 2070 } 2071 if (!isEmpty(fileTypes) && isArray(fileTypes)) { 2072 for (j = 0; j < fileTypes.length; j += 1) { 2073 typ = fileTypes[j]; 2074 checkFile = settings[typ]; 2075 chk = (checkFile !== undefined && checkFile(file.type, caption)); 2076 fileCount += isEmpty(chk) ? 0 : chk.length; 2077 } 2078 if (fileCount === 0) { 2079 msg = self.msgInvalidFileType.replace('{name}', caption).replace('{types}', strTypes); 2080 self.isError = throwError(msg, file, previewId, i); 2081 return; 2082 } 2083 } 2084 if (fileCount === 0 && !isEmpty(fileExt) && isArray(fileExt) && !isEmpty(fileExtExpr)) { 2085 chk = caption.match(fileExtExpr); 2086 fileCount += isEmpty(chk) ? 0 : chk.length; 2087 if (fileCount === 0) { 2088 msg = self.msgInvalidFileExtension.replace('{name}', caption).replace('{extensions}', 2089 strExt); 2090 self.isError = throwError(msg, file, previewId, i); 2091 return; 2092 } 2093 } 2094 if (!self.showPreview) { 2095 self.filestack.push(file); 2096 setTimeout(readFile(i + 1), 100); 2097 self.raise('fileloaded', [file, previewId, i, reader]); 2098 return; 2099 } 2100 if ($preview.length > 0 && FileReader !== undefined) { 2101 $status.html(msgLoading.replace('{index}', i + 1).replace('{files}', numFiles)); 2102 $container.addClass('loading'); 2103 reader.onerror = function (evt) { 2104 self.errorHandler(evt, caption); 2105 }; 2106 reader.onload = function (theFile) { 2107 self.previewFile(i, file, theFile, previewId, previewData); 2108 self.initFileActions(); 2109 }; 2110 reader.onloadend = function () { 2111 msg = msgProgress 2112 .replace('{index}', i + 1).replace('{files}', numFiles) 2113 .replace('{percent}', 50).replace('{name}', caption); 2114 setTimeout(function () { 2115 $status.html(msg); 2116 self.updateFileDetails(numFiles); 2117 readFile(i + 1); 2118 }, 100); 2119 self.raise('fileloaded', [file, previewId, i, reader]); 2120 }; 2121 reader.onprogress = function (data) { 2122 if (data.lengthComputable) { 2123 var fact = (data.loaded / data.total) * 100, progress = Math.ceil(fact); 2124 msg = msgProgress.replace('{index}', i + 1).replace('{files}', numFiles) 2125 .replace('{percent}', progress).replace('{name}', caption); 2126 setTimeout(function () { 2127 $status.html(msg); 2128 }, 100); 2129 } 2130 }; 2131 isText = isSet('text', settings) ? settings.text : defaultFileTypeSettings.text; 2132 if (isText(file.type, caption)) { 2133 reader.readAsText(file, self.textEncoding); 2134 } else { 2135 reader.readAsArrayBuffer(file); 2136 } 2137 } else { 2138 self.previewDefault(file, previewId); 2139 setTimeout(function () { 2140 readFile(i + 1); 2141 self.updateFileDetails(numFiles); 2142 }, 100); 2143 self.raise('fileloaded', [file, previewId, i, reader]); 2144 } 2145 self.filestack.push(file); 2146 }; 2147 2148 readFile(0); 2149 self.updateFileDetails(numFiles, false); 2150 }, 2151 updateFileDetails: function (numFiles) { 2152 var self = this, $el = self.$element, fileStack = self.getFileStack(), 2153 name = $el.val() || (fileStack.length && fileStack[0].name) || '', label = self.slug(name), 2154 n = self.isUploadable ? fileStack.length : numFiles, 2155 nFiles = previewCache.count(self.id) + n, 2156 log = n > 1 ? self.getMsgSelected(nFiles) : label; 2157 if (self.isError) { 2158 self.$previewContainer.removeClass('loading'); 2159 self.$previewStatus.html(''); 2160 self.$captionContainer.find('.kv-caption-icon').hide(); 2161 } else { 2162 self.showFileIcon(); 2163 } 2164 self.setCaption(log, self.isError); 2165 self.$container.removeClass('file-input-new file-input-ajax-new'); 2166 if (arguments.length === 1) { 2167 self.raise('fileselect', [numFiles, label]); 2168 } 2169 if (previewCache.count(self.id)) { 2170 self.initPreviewDeletes(); 2171 } 2172 }, 2173 validateMinCount: function () { 2174 var self = this, len = self.isUploadable ? self.getFileStack().length : self.$element.get(0).files.length; 2175 if (self.validateInitialCount && self.minFileCount > 0 && self.getFileCount(len - 1) < self.minFileCount) { 2176 self.noFilesError({}); 2177 return false; 2178 } 2179 return true; 2180 }, 2181 getFileCount: function (fileCount) { 2182 var self = this, addCount = 0; 2183 if (self.validateInitialCount && !self.overwriteInitial) { 2184 addCount = previewCache.count(self.id); 2185 fileCount += addCount; 2186 } 2187 return fileCount; 2188 }, 2189 change: function (e) { 2190 var self = this, $el = self.$element; 2191 if (!self.isUploadable && isEmpty($el.val()) && self.fileInputCleared) { // IE 11 fix 2192 self.fileInputCleared = false; 2193 return; 2194 } 2195 self.fileInputCleared = false; 2196 var tfiles, msg, total, isDragDrop = arguments.length > 1, 2197 files = isDragDrop ? e.originalEvent.dataTransfer.files : $el.get(0).files, 2198 isSingleUpload = isEmpty($el.attr('multiple')), i = 0, f, n, folders = 0, 2199 ctr = self.filestack.length, isAjaxUpload = self.isUploadable, len, 2200 flagSingle = (isSingleUpload && ctr > 0), 2201 throwError = function (mesg, file, previewId, index) { 2202 var p1 = $.extend(self.getOutData({}, {}, files), {id: previewId, index: index}), 2203 p2 = {id: previewId, index: index, file: file, files: files}; 2204 return self.isUploadable ? self.showUploadError(mesg, p1) : self.showError(mesg, p2); 2205 }; 2206 self.reader = null; 2207 self.resetUpload(); 2208 self.hideFileIcon(); 2209 if (self.isUploadable) { 2210 self.$container.find('.file-drop-zone .' + self.dropZoneTitleClass).remove(); 2211 } 2212 if (isDragDrop) { 2213 tfiles = []; 2214 while (files[i]) { 2215 f = files[i]; 2216 if (!f.type && f.size % 4096 === 0) { 2217 folders++; 2218 } else { 2219 tfiles.push(f); 2220 } 2221 i++; 2222 } 2223 } else { 2224 if (e.target.files === undefined) { 2225 tfiles = e.target && e.target.value ? [ 2226 {name: e.target.value.replace(/^.+\\/, '')} 2227 ] : []; 2228 } else { 2229 tfiles = e.target.files; 2230 } 2231 } 2232 if (isEmpty(tfiles) || tfiles.length === 0) { 2233 if (!isAjaxUpload) { 2234 self.clear(); 2235 } 2236 self.showFolderError(folders); 2237 self.raise('fileselectnone'); 2238 return; 2239 } 2240 self.resetErrors(); 2241 len = tfiles.length; 2242 total = self.isUploadable ? self.getFileStack().length + len : len; 2243 total = self.getFileCount(total);
2244 if (self.maxFileCount > 0 && total > self.maxFileCount) { 2245 if (!self.autoReplace || len > self.maxFileCount) { 2246 n = (self.autoReplace && len > self.maxFileCount) ? len : total; 2247 msg = self.msgFilesTooMany.replace('{m}', self.maxFileCount).replace('{n}', n); 2248 self.isError = throwError(msg, null, null, null); 2249 self.$captionContainer.find('.kv-caption-icon').hide(); 2250 self.setCaption('', true); 2251 self.$container.removeClass('file-input-new file-input-ajax-new'); 2252 return; 2253 } 2254 if (total > self.maxFileCount) { 2255 self.resetPreviewThumbs(isAjaxUpload); 2256 } 2257 } else { 2258 if (!isAjaxUpload || flagSingle) { 2259 self.resetPreviewThumbs(false); 2260 if (flagSingle) { 2261 self.filestack = []; 2262 } 2263 } else { 2264 if (isAjaxUpload && ctr === 0 && (!previewCache.count(self.id) || self.overwriteInitial)) { 2265 self.resetPreviewThumbs(true); 2266 } 2267 } 2268 } 2269 if (self.isPreviewable) { 2270 self.readFiles(tfiles); 2271 } else { 2272 self.updateFileDetails(1); 2273 } 2274 self.showFolderError(folders); 2275 }, 2276 checkDimensions: function (i, chk, $img, $thumb, fname, type, params) { 2277 var self = this, msg, dim, tag = chk === 'Small' ? 'min' : 'max', 2278 limit = self[tag + 'Image' + type], $imgEl, isValid; 2279 if (isEmpty(limit) || !$img.length) { 2280 return; 2281 } 2282 $imgEl = $img[0]; 2283 dim = (type === 'Width') ? $imgEl.naturalWidth || $imgEl.width : $imgEl.naturalHeight || $imgEl.height; 2284 isValid = chk === 'Small' ? dim >= limit : dim <= limit; 2285 if (isValid) { 2286 return; 2287 } 2288 msg = self['msgImage' + type + chk].replace('{name}', fname).replace('{size}', limit); 2289 self.showUploadError(msg, params); 2290 self.setThumbStatus($thumb, 'Error'); 2291 self.filestack[i] = null; 2292 }, 2293 validateImage: function (i, previewId, fname, ftype) { 2294 var self = this, $preview = self.$preview, params, w1, w2, 2295 $thumb = $preview.find("#" + previewId), $img = $thumb.find('img'); 2296 fname = fname || 'Untitled'; 2297 if (!$img.length) { 2298 return; 2299 } 2300 handler($img, 'load', function () { 2301 w1 = $thumb.width(); 2302 w2 = $preview.width(); 2303 if (w1 > w2) { 2304 $img.css('width', '100%'); 2305 $thumb.css('width', '97%'); 2306 } 2307 params = {ind: i, id: previewId}; 2308 self.checkDimensions(i, 'Small', $img, $thumb, fname, 'Width', params); 2309 self.checkDimensions(i, 'Small', $img, $thumb, fname, 'Height', params); 2310 if (!self.resizeImage) { 2311 self.checkDimensions(i, 'Large', $img, $thumb, fname, 'Width', params); 2312 self.checkDimensions(i, 'Large', $img, $thumb, fname, 'Height', params); 2313 } 2314 self.raise('fileimageloaded', [previewId]); 2315 self.loadedImages.push({ind: i, img: $img, thumb: $thumb, pid: previewId, typ: ftype}); 2316 self.validateAllImages(); 2317 objUrl.revokeObjectURL($img.attr('src')); 2318 }); 2319 }, 2320 validateAllImages: function () { 2321 var self = this, i, config, $img, $thumb, pid, ind, params = {}, errFunc; 2322 if (self.loadedImages.length !== self.totalImagesCount) { 2323 return; 2324 } 2325 self.raise('fileimagesloaded'); 2326 if (!self.resizeImage) { 2327 return; 2328 } 2329 errFunc = self.isUploadable ? self.showUploadError : self.showError; 2330 for (i = 0; i < self.loadedImages.length; i++) { 2331 config = self.loadedImages[i]; 2332 $img = config.img; 2333 $thumb = config.thumb; 2334 pid = config.pid; 2335 ind = config.ind; 2336 params = {id: pid, 'index': ind}; 2337 if (!self.getResizedImage($img[0], config.typ, pid, ind)) { 2338 errFunc(self.msgImageResizeError, params, 'fileimageresizeerror'); 2339 self.setThumbStatus($thumb, 'Error'); 2340 self.filestack[ind] = undefined; 2341 } 2342 } 2343 self.raise('fileimagesresized'); 2344 }, 2345 getResizedImage: function (image, type, pid, ind) {
2346 var self = this, width = image.naturalWidth, height = image.naturalHeight, ratio = 1, 2347 maxWidth = self.maxImageWidth || width, maxHeight = self.maxImageHeight || height, 2348 isValidImage = (width && height), chkWidth, chkHeight, 2349 canvas = self.imageCanvas, context = self.imageCanvasContext; 2350 if (!isValidImage) { 2351 return false; 2352 } 2353 if (width === maxWidth && height === maxHeight) { 2354 return true; 2355 } 2356 type = type || self.resizeDefaultImageType; 2357 chkWidth = width > maxWidth; 2358 chkHeight = height > maxHeight; 2359 if (self.resizePreference === 'width') { 2360 ratio = chkWidth ? maxWidth / width : (chkHeight ? maxHeight / height : 1); 2361 } else { 2362 ratio = chkHeight ? maxHeight / height : (chkWidth ? maxWidth / width : 1); 2363 } 2364 self.resetCanvas(); 2365 width *= ratio; 2366 height *= ratio; 2367 canvas.width = width; 2368 canvas.height = height; 2369 try { 2370 context.drawImage(image, 0, 0, width, height); 2371 canvas.toBlob(function (blob) { 2372 self.raise('fileimageresized', [pid, ind]); 2373 self.filestack[ind] = blob; 2374 }, type, self.resizeQuality); 2375 return true; 2376 } 2377 catch (err) { 2378 return false; 2379 } 2380 }, 2381 initCaption: function () { 2382 var self = this, cap = self.initialCaption || ''; 2383 if (self.overwriteInitial || isEmpty(cap)) { 2384 self.$caption.html(''); 2385 return false; 2386 } 2387 self.setCaption(cap); 2388 return true; 2389 }, 2390 setCaption: function (content, isError) { 2391 var self = this, title, out; 2392 if (isError) { 2393 title = $('<div>' + self.msgValidationError + '</div>').text(); 2394 out = '<span class="' + self.msgValidationErrorClass + '">' + 2395 self.msgValidationErrorIcon + title + '</span>'; 2396 } else { 2397 if (isEmpty(content) || self.$caption.length === 0) { 2398 return; 2399 } 2400 title = $('<div>' + content + '</div>').text(); 2401 out = self.getLayoutTemplate('icon') + title; 2402 } 2403 self.$caption.html(out); 2404 self.$caption.attr('title', title); 2405 self.$captionContainer.find('.file-caption-ellipsis').attr('title', title); 2406 }, 2407 initBrowse: function ($container) { 2408 var self = this; 2409 self.$btnFile = $container.find('.btn-file'); 2410 self.$btnFile.append(self.$element); 2411 }, 2412 createContainer: function () { 2413 var self = this, 2414 $container = $(document.createElement("div")) 2415 .attr({"class": 'file-input file-input-new'}) 2416 .html(self.renderMain()); 2417 self.$element.before($container); 2418 self.initBrowse($container); 2419 return $container; 2420 }, 2421 refreshContainer: function () { 2422 var self = this, $container = self.$container; 2423 $container.before(self.$element); 2424 $container.html(self.renderMain()); 2425 self.initBrowse($container); 2426 }, 2427 renderMain: function () { 2428 var self = this, dropCss = (self.isUploadable && self.dropZoneEnabled) ? ' file-drop-zone' : '', 2429 close = !self.showClose ? '' : self.getLayoutTemplate('close'), 2430 preview = !self.showPreview ? '' : self.getLayoutTemplate('preview') 2431 .replace(/\{class}/g, self.previewClass) 2432 .replace(/\{dropClass}/g, dropCss), 2433 css = self.isDisabled ? self.captionClass + ' file-caption-disabled' : self.captionClass,
2434 caption = self.captionTemplate.replace(/\{class}/g, css + ' kv-fileinput-caption'); 2435 return self.mainTemplate.replace(/\{class}/g, self.mainClass) 2436 .replace(/\{preview}/g, preview) 2437 .replace(/\{close}/g, close) 2438 .replace(/\{caption}/g, caption) 2439 .replace(/\{upload}/g, self.renderButton('upload')) 2440 .replace(/\{remove}/g, self.renderButton('remove')) 2441 .replace(/\{cancel}/g, self.renderButton('cancel')) 2442 .replace(/\{browse}/g, self.renderButton('browse')); 2443 }, 2444 renderButton: function (type) { 2445 var self = this, tmplt = self.getLayoutTemplate('btnDefault'), css = self[type + 'Class'], 2446 title = self[type + 'Title'], icon = self[type + 'Icon'], label = self[type + 'Label'], 2447 status = self.isDisabled ? ' disabled' : '', btnType = 'button'; 2448 switch (type) { 2449 case 'remove': 2450 if (!self.showRemove) { 2451 return ''; 2452 } 2453 break; 2454 case 'cancel': 2455 if (!self.showCancel) { 2456 return ''; 2457 } 2458 css += ' hide'; 2459 break; 2460 case 'upload': 2461 if (!self.showUpload) { 2462 return ''; 2463 } 2464 if (self.isUploadable && !self.isDisabled) { 2465 tmplt = self.getLayoutTemplate('btnLink').replace('{href}', self.uploadUrl); 2466 } else { 2467 btnType = 'submit'; 2468 } 2469 break; 2470 case 'browse': 2471 tmplt = self.getLayoutTemplate('btnBrowse'); 2472 break; 2473 default: 2474 return ''; 2475 } 2476 css += type === 'browse' ? ' btn-file' : ' fileinput-' + type + ' fileinput-' + type + '-button'; 2477 return tmplt.replace('{type}', btnType) 2478 .replace('{css}', css) 2479 .replace('{title}', title) 2480 .replace('{status}', status) 2481 .replace('{icon}', icon) 2482 .replace('{label}', label); 2483 } 2484 } 2485 ; 2486 2487//FileInput plugin definition 2488 $.fn.fileinput = function (option) { 2489 if (!hasFileAPISupport() && !isIE(9)) { 2490 return; 2491 } 2492 var args = Array.apply(null, arguments), retvals = []; 2493 args.shift(); 2494 this.each(function () { 2495 var $this = $(this), data = $this.data('fileinput'), defaults, 2496 options = typeof option === 'object' && option, 2497 lang = options.language || $this.data('language') || 'en'; 2498 2499 if (!data) { 2500 defaults = $.extend({}, $.fn.fileinput.defaults); 2501 if (lang !== 'en' && !isEmpty($.fn.fileinputLocales[lang])) { 2502 defaults = $.extend(defaults, $.fn.fileinputLocales[lang]); 2503 } 2504 data = new FileInput(this, $.extend(defaults, options, $this.data())); 2505 $this.data('fileinput', data); 2506 } 2507 2508 if (typeof option === 'string') { 2509 retvals.push(data[option].apply(data, args)); 2510 } 2511 }); 2512 switch (retvals.length) { 2513 case 0: 2514 return this; 2515 case 1: 2516 return retvals[0]; 2517 default: 2518 return retvals; 2519 } 2520 }; 2521 2522 $.fn.fileinput.defaults = { 2523 language: 'en', 2524 showCaption: true, 2525 showPreview: true, 2526 showRemove: true, 2527 showUpload: true, 2528 showCancel: true, 2529 showClose: true, 2530 showUploadedThumbs: true, 2531 autoReplace: false, 2532 mainClass: '', 2533 previewClass: '', 2534 captionClass: '', 2535 mainTemplate: null, 2536 initialCaption: '', 2537 initialPreview: [], 2538 initialPreviewDelimiter: '*$$*', 2539 initialPreviewConfig: [], 2540 initialPreviewThumbTags: [], 2541 previewThumbTags: {}, 2542 initialPreviewShowDelete: true, 2543 deleteUrl: '', 2544 deleteExtraData: {}, 2545 overwriteInitial: true, 2546 layoutTemplates: defaultLayoutTemplates, 2547 previewTemplates: defaultPreviewTemplates,
2548 allowedPreviewTypes: defaultPreviewTypes, 2549 allowedPreviewMimeTypes: null, 2550 allowedFileTypes: null, 2551 allowedFileExtensions: null, 2552 defaultPreviewContent: null, 2553 customLayoutTags: {}, 2554 customPreviewTags: {}, 2555 previewSettings: defaultPreviewSettings, 2556 fileTypeSettings: defaultFileTypeSettings, 2557 previewFileIcon: '<i class="icon icon-file"></i>', 2558 previewFileIconClass: 'file-icon-4x', 2559 previewFileIconSettings: {}, 2560 previewFileExtSettings: {}, 2561 browseIcon: '<i class="icon icon-upload-alt"></i> ', 2562 browseClass: 'btn btn-primary', 2563 removeIcon: '<i class="icon icon-trash"></i> ', 2564 removeClass: 'btn btn-default', 2565 cancelIcon: '<i class="icon icon-ban-circle"></i> ', 2566 cancelClass: 'btn btn-default', 2567 uploadIcon: '<i class="icon icon-upload-alt"></i> ', 2568 uploadClass: 'btn btn-default', 2569 uploadUrl: null, 2570 uploadAsync: true, 2571 uploadExtraData: {}, 2572 minImageWidth: null, 2573 minImageHeight: null, 2574 maxImageWidth: null, 2575 maxImageHeight: null, 2576 resizeImage: false, 2577 resizePreference: 'width', 2578 resizeQuality: 0.92, 2579 resizeDefaultImageType: 'image/jpeg', 2580 maxFileSize: 0, 2581 minFileCount: 0, 2582 maxFileCount: 0, 2583 validateInitialCount: false, 2584 msgValidationErrorClass: 'text-danger', 2585 msgValidationErrorIcon: '<i class="icon icon-exclamation-sign"></i> ', 2586 msgErrorClass: 'file-error-message', 2587 progressThumbClass: "progress-bar progress-bar-success progress-bar-striped active", 2588 progressClass: "progress-bar progress-bar-success progress-bar-striped active", 2589 progressCompleteClass: "progress-bar progress-bar-success", 2590 previewFileType: 'image', 2591 zoomIndicator: '<i class="icon icon-zoom-in"></i>', 2592 elCaptionContainer: null, 2593 elCaptionText: null, 2594 elPreviewContainer: null, 2595 elPreviewImage: null, 2596 elPreviewStatus: null, 2597 elErrorContainer: null, 2598 errorCloseButton: '<span class="close kv-error-close">×</span>', 2599 slugCallback: null, 2600 dropZoneEnabled: true, 2601 dropZoneTitleClass: 'file-drop-zone-title', 2602 fileActionSettings: {}, 2603 otherActionButtons: '', 2604 textEncoding: 'UTF-8', 2605 ajaxSettings: {}, 2606 ajaxDeleteSettings: {}, 2607 showAjaxErrorDetails: true 2608 }; 2609 2610 $.fn.fileinputLocales.en = { 2611 fileSingle: 'file', 2612 filePlural: 'files', 2613 browseLabel: 'Browse …', 2614 removeLabel: 'Remove', 2615 removeTitle: 'Clear selected files', 2616 cancelLabel: 'Cancel', 2617 cancelTitle: 'Abort ongoing upload', 2618 uploadLabel: 'Upload', 2619 uploadTitle: 'Upload selected files', 2620 msgZoomTitle: 'View details', 2621 msgZoomModalHeading: 'Detailed Preview', 2622 msgSizeTooLarge: 'File "{name}" (<b>{size} KB</b>) exceeds maximum allowed upload size of <b>{maxSize} KB</b>.', 2623 msgFilesTooLess: 'You must select at least <b>{n}</b> {files} to upload.', 2624 msgFilesTooMany: 'Number of files selected for upload <b>({n})</b> exceeds maximum allowed limit of <b>{m}</b>.', 2625 msgFileNotFound: 'File "{name}" not found!', 2626 msgFileSecured: 'Security restrictions prevent reading the file "{name}".', 2627 msgFileNotReadable: 'File "{name}" is not readable.', 2628 msgFilePreviewAborted: 'File preview aborted for "{name}".', 2629 msgFilePreviewError: 'An error occurred while reading the file "{name}".', 2630 msgInvalidFileType: 'Invalid type for file "{name}". Only "{types}" files are supported.', 2631 msgInvalidFileExtension: 'Invalid extension for file "{name}". Only "{extensions}" files are supported.', 2632 msgUploadAborted: 'The file upload was aborted', 2633 msgValidationError: 'File Upload Error', 2634 msgLoading: 'Loading file {index} of {files} …', 2635 msgProgress: 'Loading file {index} of {files} - {name} - {percent}% completed.', 2636 msgSelected: '{n} {files} selected', 2637 msgFoldersNotAllowed: 'Drag & drop files only! {n} folder(s) dropped were skipped.', 2638 msgImageWidthSmall: 'Width of image file "{name}" must be at least {size} px.', 2639 msgImageHeightSmall: 'Height of image file "{name}" must be at least {size} px.', 2640 msgImageWidthLarge: 'Width of image file "{name}" cannot exceed {size} px.', 2641 msgImageHeightLarge: 'Height of image file "{name}" cannot exceed {size} px.', 2642 msgImageResizeError: 'Could not get the image dimensions to resize.', 2643 msgImageResizeException: 'Error while resizing the image.<pre>{errors}</pre>', 2644 dropZoneTitle: 'Drag & drop files here …' 2645 }; 2646 2647 $.extend($.fn.fileinput.defaults, $.fn.fileinputLocales.en); 2648 2649 $.fn.fileinput.Constructor = FileInput; 2650 2651 /** 2652 * Convert automatically file inputs with class 'file' 2653 * into a bootstrap fileinput control. 2654 */ 2655 $(document).ready(function () { 2656 var $input = $('input.file[type=file]'); 2657 if ($input.length) { 2658 $input.fileinput(); 2659 } 2660 });
2661}) 2662(window.jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.