github.com/apremalal/vamps-core@v1.0.1-0.20161221121535-d430b56ec174/server/webapps/app/base/plugins/jquery-file-upload/js/jquery.fileupload-ui.js (about) 1 /* 2 * jQuery File Upload User Interface Plugin 9.6.0 3 * https://github.com/blueimp/jQuery-File-Upload 4 * 5 * Copyright 2010, Sebastian Tschan 6 * https://blueimp.net 7 * 8 * Licensed under the MIT license: 9 * http://www.opensource.org/licenses/MIT 10 */ 11 12 /* jshint nomen:false */ 13 /* global define, window */ 14 15 (function (factory) { 16 'use strict'; 17 if (typeof define === 'function' && define.amd) { 18 // Register as an anonymous AMD module: 19 define([ 20 'jquery', 21 'tmpl', 22 './jquery.fileupload-image.js', 23 './jquery.fileupload-audio', 24 './jquery.fileupload-video', 25 './jquery.fileupload-validate' 26 ], factory); 27 } else { 28 // Browser globals: 29 factory( 30 window.jQuery, 31 window.tmpl 32 ); 33 } 34 }(function ($, tmpl) { 35 'use strict'; 36 37 $.blueimp.fileupload.prototype._specialOptions.push( 38 'filesContainer', 39 'uploadTemplateId', 40 'downloadTemplateId' 41 ); 42 43 // The UI version extends the file upload widget 44 // and adds complete user interface interaction: 45 $.widget('blueimp.fileupload', $.blueimp.fileupload, { 46 47 options: { 48 // By default, files added to the widget are uploaded as soon 49 // as the user clicks on the start buttons. To enable automatic 50 // uploads, set the following option to true: 51 autoUpload: false, 52 // The ID of the upload template: 53 uploadTemplateId: 'template-upload', 54 // The ID of the download template: 55 downloadTemplateId: 'template-download', 56 // The container for the list of files. If undefined, it is set to 57 // an element with class "files" inside of the widget element: 58 filesContainer: undefined, 59 // By default, files are appended to the files container. 60 // Set the following option to true, to prepend files instead: 61 prependFiles: false, 62 // The expected data type of the upload response, sets the dataType 63 // option of the $.ajax upload requests: 64 dataType: 'json', 65 66 // Error and info messages: 67 messages: { 68 unknownError: 'Unknown error' 69 }, 70 71 // Function returning the current number of files, 72 // used by the maxNumberOfFiles validation: 73 getNumberOfFiles: function () { 74 return this.filesContainer.children() 75 .not('.processing').length; 76 }, 77 78 // Callback to retrieve the list of files from the server response: 79 getFilesFromResponse: function (data) { 80 if (data.result && $.isArray(data.result.files)) { 81 return data.result.files; 82 } 83 return []; 84 }, 85 86 // The add callback is invoked as soon as files are added to the fileupload 87 // widget (via file input selection, drag & drop or add API call). 88 // See the basic file upload widget for more information: 89 add: function (e, data) { 90 if (e.isDefaultPrevented()) { 91 return false; 92 } 93 var $this = $(this), 94 that = $this.data('blueimp-fileupload') || 95 $this.data('fileupload'), 96 options = that.options; 97 data.context = that._renderUpload(data.files) 98 .data('data', data) 99 .addClass('processing'); 100 options.filesContainer[ 101 options.prependFiles ? 'prepend' : 'append' 102 ](data.context); 103 that._forceReflow(data.context); 104 that._transition(data.context); 105 data.process(function () { 106 return $this.fileupload('process', data); 107 }).always(function () { 108 data.context.each(function (index) { 109 $(this).find('.size').text( 110 that._formatFileSize(data.files[index].size) 111 ); 112 }).removeClass('processing'); 113 that._renderPreviews(data); 114 }).done(function () { 115 data.context.find('.start').prop('disabled', false); 116 if ((that._trigger('added', e, data) !== false) && 117 (options.autoUpload || data.autoUpload) && 118 data.autoUpload !== false) { 119 data.submit(); 120 } 121 }).fail(function () { 122 if (data.files.error) { 123 data.context.each(function (index) { 124 var error = data.files[index].error; 125 if (error) { 126 $(this).find('.error').text(error); 127 } 128 }); 129 } 130 }); 131 }, 132 // Callback for the start of each file upload request: 133 send: function (e, data) { 134 if (e.isDefaultPrevented()) { 135 return false; 136 } 137 var that = $(this).data('blueimp-fileupload') || 138 $(this).data('fileupload'); 139 if (data.context && data.dataType && 140 data.dataType.substr(0, 6) === 'iframe') { 141 // Iframe Transport does not support progress events. 142 // In lack of an indeterminate progress bar, we set 143 // the progress to 100%, showing the full animated bar: 144 data.context 145 .find('.progress').addClass( 146 !$.support.transition && 'progress-animated' 147 ) 148 .attr('aria-valuenow', 100) 149 .children().first().css( 150 'width', 151 '100%' 152 ); 153 } 154 return that._trigger('sent', e, data); 155 }, 156 // Callback for successful uploads: 157 done: function (e, data) { 158 if (e.isDefaultPrevented()) { 159 return false; 160 } 161 var that = $(this).data('blueimp-fileupload') || 162 $(this).data('fileupload'), 163 getFilesFromResponse = data.getFilesFromResponse || 164 that.options.getFilesFromResponse, 165 files = getFilesFromResponse(data), 166 template, 167 deferred; 168 if (data.context) { 169 data.context.each(function (index) { 170 var file = files[index] || 171 {error: 'Empty file upload result'}; 172 deferred = that._addFinishedDeferreds(); 173 that._transition($(this)).done( 174 function () { 175 var node = $(this); 176 template = that._renderDownload([file]) 177 .replaceAll(node); 178 that._forceReflow(template); 179 that._transition(template).done( 180 function () { 181 data.context = $(this); 182 that._trigger('completed', e, data); 183 that._trigger('finished', e, data); 184 deferred.resolve(); 185 } 186 ); 187 } 188 ); 189 }); 190 } else { 191 template = that._renderDownload(files)[ 192 that.options.prependFiles ? 'prependTo' : 'appendTo' 193 ](that.options.filesContainer); 194 that._forceReflow(template); 195 deferred = that._addFinishedDeferreds(); 196 that._transition(template).done( 197 function () { 198 data.context = $(this); 199 that._trigger('completed', e, data); 200 that._trigger('finished', e, data); 201 deferred.resolve(); 202 } 203 ); 204 } 205 }, 206 // Callback for failed (abort or error) uploads: 207 fail: function (e, data) { 208 if (e.isDefaultPrevented()) { 209 return false; 210 } 211 var that = $(this).data('blueimp-fileupload') || 212 $(this).data('fileupload'), 213 template, 214 deferred; 215 if (data.context) { 216 data.context.each(function (index) { 217 if (data.errorThrown !== 'abort') { 218 var file = data.files[index]; 219 file.error = file.error || data.errorThrown || 220 data.i18n('unknownError'); 221 deferred = that._addFinishedDeferreds(); 222 that._transition($(this)).done( 223 function () { 224 var node = $(this); 225 template = that._renderDownload([file]) 226 .replaceAll(node); 227 that._forceReflow(template); 228 that._transition(template).done( 229 function () { 230 data.context = $(this); 231 that._trigger('failed', e, data); 232 that._trigger('finished', e, data); 233 deferred.resolve(); 234 } 235 ); 236 } 237 ); 238 } else { 239 deferred = that._addFinishedDeferreds(); 240 that._transition($(this)).done( 241 function () { 242 $(this).remove(); 243 that._trigger('failed', e, data); 244 that._trigger('finished', e, data); 245 deferred.resolve(); 246 } 247 ); 248 } 249 }); 250 } else if (data.errorThrown !== 'abort') { 251 data.context = that._renderUpload(data.files)[ 252 that.options.prependFiles ? 'prependTo' : 'appendTo' 253 ](that.options.filesContainer) 254 .data('data', data); 255 that._forceReflow(data.context); 256 deferred = that._addFinishedDeferreds(); 257 that._transition(data.context).done( 258 function () { 259 data.context = $(this); 260 that._trigger('failed', e, data); 261 that._trigger('finished', e, data); 262 deferred.resolve(); 263 } 264 ); 265 } else { 266 that._trigger('failed', e, data); 267 that._trigger('finished', e, data); 268 that._addFinishedDeferreds().resolve(); 269 } 270 }, 271 // Callback for upload progress events: 272 progress: function (e, data) { 273 if (e.isDefaultPrevented()) { 274 return false; 275 } 276 var progress = Math.floor(data.loaded / data.total * 100); 277 if (data.context) { 278 data.context.each(function () { 279 $(this).find('.progress') 280 .attr('aria-valuenow', progress) 281 .children().first().css( 282 'width', 283 progress + '%' 284 ); 285 }); 286 } 287 }, 288 // Callback for global upload progress events: 289 progressall: function (e, data) { 290 if (e.isDefaultPrevented()) { 291 return false; 292 } 293 var $this = $(this), 294 progress = Math.floor(data.loaded / data.total * 100), 295 globalProgressNode = $this.find('.fileupload-progress'), 296 extendedProgressNode = globalProgressNode 297 .find('.progress-extended'); 298 if (extendedProgressNode.length) { 299 extendedProgressNode.html( 300 ($this.data('blueimp-fileupload') || $this.data('fileupload')) 301 ._renderExtendedProgress(data) 302 ); 303 } 304 globalProgressNode 305 .find('.progress') 306 .attr('aria-valuenow', progress) 307 .children().first().css( 308 'width', 309 progress + '%' 310 ); 311 }, 312 // Callback for uploads start, equivalent to the global ajaxStart event: 313 start: function (e) { 314 if (e.isDefaultPrevented()) { 315 return false; 316 } 317 var that = $(this).data('blueimp-fileupload') || 318 $(this).data('fileupload'); 319 that._resetFinishedDeferreds(); 320 that._transition($(this).find('.fileupload-progress')).done( 321 function () { 322 that._trigger('started', e); 323 } 324 ); 325 }, 326 // Callback for uploads stop, equivalent to the global ajaxStop event: 327 stop: function (e) { 328 if (e.isDefaultPrevented()) { 329 return false; 330 } 331 var that = $(this).data('blueimp-fileupload') || 332 $(this).data('fileupload'), 333 deferred = that._addFinishedDeferreds(); 334 $.when.apply($, that._getFinishedDeferreds()) 335 .done(function () { 336 that._trigger('stopped', e); 337 }); 338 that._transition($(this).find('.fileupload-progress')).done( 339 function () { 340 $(this).find('.progress') 341 .attr('aria-valuenow', '0') 342 .children().first().css('width', '0%'); 343 $(this).find('.progress-extended').html(' '); 344 deferred.resolve(); 345 } 346 ); 347 }, 348 processstart: function (e) { 349 if (e.isDefaultPrevented()) { 350 return false; 351 } 352 $(this).addClass('fileupload-processing'); 353 }, 354 processstop: function (e) { 355 if (e.isDefaultPrevented()) { 356 return false; 357 } 358 $(this).removeClass('fileupload-processing'); 359 }, 360 // Callback for file deletion: 361 destroy: function (e, data) { 362 if (e.isDefaultPrevented()) { 363 return false; 364 } 365 var that = $(this).data('blueimp-fileupload') || 366 $(this).data('fileupload'), 367 removeNode = function () { 368 that._transition(data.context).done( 369 function () { 370 $(this).remove(); 371 that._trigger('destroyed', e, data); 372 } 373 ); 374 }; 375 if (data.url) { 376 data.dataType = data.dataType || that.options.dataType; 377 $.ajax(data).done(removeNode).fail(function () { 378 that._trigger('destroyfailed', e, data); 379 }); 380 } else { 381 removeNode(); 382 } 383 } 384 }, 385 386 _resetFinishedDeferreds: function () { 387 this._finishedUploads = []; 388 }, 389 390 _addFinishedDeferreds: function (deferred) { 391 if (!deferred) { 392 deferred = $.Deferred(); 393 } 394 this._finishedUploads.push(deferred); 395 return deferred; 396 }, 397 398 _getFinishedDeferreds: function () { 399 return this._finishedUploads; 400 }, 401 402 // Link handler, that allows to download files 403 // by drag & drop of the links to the desktop: 404 _enableDragToDesktop: function () { 405 var link = $(this), 406 url = link.prop('href'), 407 name = link.prop('download'), 408 type = 'application/octet-stream'; 409 link.bind('dragstart', function (e) { 410 try { 411 e.originalEvent.dataTransfer.setData( 412 'DownloadURL', 413 [type, name, url].join(':') 414 ); 415 } catch (ignore) {} 416 }); 417 }, 418 419 _formatFileSize: function (bytes) { 420 if (typeof bytes !== 'number') { 421 return ''; 422 } 423 if (bytes >= 1000000000) { 424 return (bytes / 1000000000).toFixed(2) + ' GB'; 425 } 426 if (bytes >= 1000000) { 427 return (bytes / 1000000).toFixed(2) + ' MB'; 428 } 429 return (bytes / 1000).toFixed(2) + ' KB'; 430 }, 431 432 _formatBitrate: function (bits) { 433 if (typeof bits !== 'number') { 434 return ''; 435 } 436 if (bits >= 1000000000) { 437 return (bits / 1000000000).toFixed(2) + ' Gbit/s'; 438 } 439 if (bits >= 1000000) { 440 return (bits / 1000000).toFixed(2) + ' Mbit/s'; 441 } 442 if (bits >= 1000) { 443 return (bits / 1000).toFixed(2) + ' kbit/s'; 444 } 445 return bits.toFixed(2) + ' bit/s'; 446 }, 447 448 _formatTime: function (seconds) { 449 var date = new Date(seconds * 1000), 450 days = Math.floor(seconds / 86400); 451 days = days ? days + 'd ' : ''; 452 return days + 453 ('0' + date.getUTCHours()).slice(-2) + ':' + 454 ('0' + date.getUTCMinutes()).slice(-2) + ':' + 455 ('0' + date.getUTCSeconds()).slice(-2); 456 }, 457 458 _formatPercentage: function (floatValue) { 459 return (floatValue * 100).toFixed(2) + ' %'; 460 }, 461 462 _renderExtendedProgress: function (data) { 463 return this._formatBitrate(data.bitrate) + ' | ' + 464 this._formatTime( 465 (data.total - data.loaded) * 8 / data.bitrate 466 ) + ' | ' + 467 this._formatPercentage( 468 data.loaded / data.total 469 ) + ' | ' + 470 this._formatFileSize(data.loaded) + ' / ' + 471 this._formatFileSize(data.total); 472 }, 473 474 _renderTemplate: function (func, files) { 475 if (!func) { 476 return $(); 477 } 478 var result = func({ 479 files: files, 480 formatFileSize: this._formatFileSize, 481 options: this.options 482 }); 483 if (result instanceof $) { 484 return result; 485 } 486 return $(this.options.templatesContainer).html(result).children(); 487 }, 488 489 _renderPreviews: function (data) { 490 data.context.find('.preview').each(function (index, elm) { 491 $(elm).append(data.files[index].preview); 492 }); 493 }, 494 495 _renderUpload: function (files) { 496 return this._renderTemplate( 497 this.options.uploadTemplate, 498 files 499 ); 500 }, 501 502 _renderDownload: function (files) { 503 return this._renderTemplate( 504 this.options.downloadTemplate, 505 files 506 ).find('a[download]').each(this._enableDragToDesktop).end(); 507 }, 508 509 _startHandler: function (e) { 510 e.preventDefault(); 511 var button = $(e.currentTarget), 512 template = button.closest('.template-upload'), 513 data = template.data('data'); 514 button.prop('disabled', true); 515 if (data && data.submit) { 516 data.submit(); 517 } 518 }, 519 520 _cancelHandler: function (e) { 521 e.preventDefault(); 522 var template = $(e.currentTarget) 523 .closest('.template-upload,.template-download'), 524 data = template.data('data') || {}; 525 data.context = data.context || template; 526 if (data.abort) { 527 data.abort(); 528 } else { 529 data.errorThrown = 'abort'; 530 this._trigger('fail', e, data); 531 } 532 }, 533 534 _deleteHandler: function (e) { 535 e.preventDefault(); 536 var button = $(e.currentTarget); 537 this._trigger('destroy', e, $.extend({ 538 context: button.closest('.template-download'), 539 type: 'DELETE' 540 }, button.data())); 541 }, 542 543 _forceReflow: function (node) { 544 return $.support.transition && node.length && 545 node[0].offsetWidth; 546 }, 547 548 _transition: function (node) { 549 var dfd = $.Deferred(); 550 if ($.support.transition && node.hasClass('fade') && node.is(':visible')) { 551 node.bind( 552 $.support.transition.end, 553 function (e) { 554 // Make sure we don't respond to other transitions events 555 // in the container element, e.g. from button elements: 556 if (e.target === node[0]) { 557 node.unbind($.support.transition.end); 558 dfd.resolveWith(node); 559 } 560 } 561 ).toggleClass('in'); 562 } else { 563 node.toggleClass('in'); 564 dfd.resolveWith(node); 565 } 566 return dfd; 567 }, 568 569 _initButtonBarEventHandlers: function () { 570 var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'), 571 filesList = this.options.filesContainer; 572 this._on(fileUploadButtonBar.find('.start'), { 573 click: function (e) { 574 e.preventDefault(); 575 filesList.find('.start').click(); 576 } 577 }); 578 this._on(fileUploadButtonBar.find('.cancel'), { 579 click: function (e) { 580 e.preventDefault(); 581 filesList.find('.cancel').click(); 582 } 583 }); 584 this._on(fileUploadButtonBar.find('.delete'), { 585 click: function (e) { 586 e.preventDefault(); 587 filesList.find('.toggle:checked') 588 .closest('.template-download') 589 .find('.delete').click(); 590 fileUploadButtonBar.find('.toggle') 591 .prop('checked', false); 592 } 593 }); 594 this._on(fileUploadButtonBar.find('.toggle'), { 595 change: function (e) { 596 filesList.find('.toggle').prop( 597 'checked', 598 $(e.currentTarget).is(':checked') 599 ); 600 } 601 }); 602 }, 603 604 _destroyButtonBarEventHandlers: function () { 605 this._off( 606 this.element.find('.fileupload-buttonbar') 607 .find('.start, .cancel, .delete'), 608 'click' 609 ); 610 this._off( 611 this.element.find('.fileupload-buttonbar .toggle'), 612 'change.' 613 ); 614 }, 615 616 _initEventHandlers: function () { 617 this._super(); 618 this._on(this.options.filesContainer, { 619 'click .start': this._startHandler, 620 'click .cancel': this._cancelHandler, 621 'click .delete': this._deleteHandler 622 }); 623 this._initButtonBarEventHandlers(); 624 }, 625 626 _destroyEventHandlers: function () { 627 this._destroyButtonBarEventHandlers(); 628 this._off(this.options.filesContainer, 'click'); 629 this._super(); 630 }, 631 632 _enableFileInputButton: function () { 633 this.element.find('.fileinput-button input') 634 .prop('disabled', false) 635 .parent().removeClass('disabled'); 636 }, 637 638 _disableFileInputButton: function () { 639 this.element.find('.fileinput-button input') 640 .prop('disabled', true) 641 .parent().addClass('disabled'); 642 }, 643 644 _initTemplates: function () { 645 var options = this.options; 646 options.templatesContainer = this.document[0].createElement( 647 options.filesContainer.prop('nodeName') 648 ); 649 if (tmpl) { 650 if (options.uploadTemplateId) { 651 options.uploadTemplate = tmpl(options.uploadTemplateId); 652 } 653 if (options.downloadTemplateId) { 654 options.downloadTemplate = tmpl(options.downloadTemplateId); 655 } 656 } 657 }, 658 659 _initFilesContainer: function () { 660 var options = this.options; 661 if (options.filesContainer === undefined) { 662 options.filesContainer = this.element.find('.files'); 663 } else if (!(options.filesContainer instanceof $)) { 664 options.filesContainer = $(options.filesContainer); 665 } 666 }, 667 668 _initSpecialOptions: function () { 669 this._super(); 670 this._initFilesContainer(); 671 this._initTemplates(); 672 }, 673 674 _create: function () { 675 this._super(); 676 this._resetFinishedDeferreds(); 677 if (!$.support.fileInput) { 678 this._disableFileInputButton(); 679 } 680 }, 681 682 enable: function () { 683 var wasDisabled = false; 684 if (this.options.disabled) { 685 wasDisabled = true; 686 } 687 this._super(); 688 if (wasDisabled) { 689 this.element.find('input, button').prop('disabled', false); 690 this._enableFileInputButton(); 691 } 692 }, 693 694 disable: function () { 695 if (!this.options.disabled) { 696 this.element.find('input, button').prop('disabled', true); 697 this._disableFileInputButton(); 698 } 699 this._super(); 700 } 701 702 }); 703 704 }));