github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/app/lib/plupload-2.1.2/js/jquery.ui.plupload/jquery.ui.plupload.js (about) 1 /** 2 * jquery.ui.plupload.js 3 * 4 * Copyright 2013, Moxiecode Systems AB 5 * Released under GPL License. 6 * 7 * License: http://www.plupload.com/license 8 * Contributing: http://www.plupload.com/contributing 9 * 10 * Depends: 11 * jquery.ui.core.js 12 * jquery.ui.widget.js 13 * jquery.ui.button.js 14 * jquery.ui.progressbar.js 15 * 16 * Optionally: 17 * jquery.ui.sortable.js 18 */ 19 20 /* global jQuery:true */ 21 22 /** 23 jQuery UI based implementation of the Plupload API - multi-runtime file uploading API. 24 25 To use the widget you must include _jQuery_ and _jQuery UI_ bundle (including `ui.core`, `ui.widget`, `ui.button`, 26 `ui.progressbar` and `ui.sortable`). 27 28 In general the widget is designed the way that you do not usually need to do anything to it after you instantiate it. 29 But! You still can intervenue, to some extent, in case you need to. Although, due to the fact that widget is based on 30 _jQuery UI_ widget factory, there are some specifics. See examples below for more details. 31 32 @example 33 <!-- Instantiating: --> 34 <div id="uploader"> 35 <p>Your browser doesn't have Flash, Silverlight or HTML5 support.</p> 36 </div> 37 38 <script> 39 $('#uploader').plupload({ 40 url : '../upload.php', 41 filters : [ 42 {title : "Image files", extensions : "jpg,gif,png"} 43 ], 44 rename: true, 45 sortable: true, 46 flash_swf_url : '../../js/Moxie.swf', 47 silverlight_xap_url : '../../js/Moxie.xap', 48 }); 49 </script> 50 51 @example 52 // Invoking methods: 53 $('#uploader').plupload(options); 54 55 // Display welcome message in the notification area 56 $('#uploader').plupload('notify', 'info', "This might be obvious, but you need to click 'Add Files' to add some files."); 57 58 @example 59 // Subscribing to the events... 60 // ... on initialization: 61 $('#uploader').plupload({ 62 ... 63 viewchanged: function(event, args) { 64 // stuff ... 65 } 66 }); 67 // ... or after initialization 68 $('#uploader').on("viewchanged", function(event, args) { 69 // stuff ... 70 }); 71 72 @class UI.Plupload 73 @constructor 74 @param {Object} settings For detailed information about each option check documentation. 75 @param {String} settings.url URL of the server-side upload handler. 76 @param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled. 77 @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message. 78 @param {Object} [settings.filters={}] Set of file type filters. 79 @param {Array} [settings.filters.mime_types=[]] List of file types to accept, each one defined by title and list of extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR` 80 @param {String|Number} [settings.filters.max_file_size=0] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`. 81 @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`. 82 @param {Number} [settings.filters.max_file_count=0] Limit the number of files that can reside in the queue at the same time (default is 0 - no limit). 83 @param {String} [settings.flash_swf_url] URL of the Flash swf. 84 @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs. 85 @param {Number|String} [settings.max_file_size] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`. 86 @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event. 87 @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message. 88 @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload. 89 @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog. 90 @param {Boolean} [settings.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`. 91 @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess. 92 @param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}` 93 @param {Number} [settings.resize.width] If image is bigger, it will be resized. 94 @param {Number} [settings.resize.height] If image is bigger, it will be resized. 95 @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100). 96 @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally. 97 @param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails. 98 @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap. 99 @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files. 100 101 @param {Boolean} [settings.autostart=false] Whether to auto start uploading right after file selection. 102 @param {Boolean} [settings.dragdrop=true] Enable ability to add file to the queue by drag'n'dropping them from the desktop. 103 @param {Boolean} [settings.rename=false] Enable ability to rename files in the queue. 104 @param {Boolean} [settings.sortable=false] Enable ability to sort files in the queue, changing their uploading priority. 105 @param {Object} [settings.buttons] Control the visibility of functional buttons. 106 @param {Boolean} [settings.buttons.browse=true] Display browse button. 107 @param {Boolean} [settings.buttons.start=true] Display start button. 108 @param {Boolean} [settings.buttons.stop=true] Display stop button. 109 @param {Object} [settings.views] Control various views of the file queue. 110 @param {Boolean} [settings.views.list=true] Enable list view. 111 @param {Boolean} [settings.views.thumbs=false] Enable thumbs view. 112 @param {String} [settings.views.default='list'] Default view. 113 @param {Boolean} [settings.views.remember=true] Whether to remember the current view (requires jQuery Cookie plugin). 114 @param {Boolean} [settings.multiple_queues=true] Re-activate the widget after each upload procedure. 115 */ 116 ;(function(window, document, plupload, o, $) { 117 118 /** 119 Dispatched when the widget is initialized and ready. 120 121 @event ready 122 @param {plupload.Uploader} uploader Uploader instance sending the event. 123 */ 124 125 /** 126 Dispatched when file dialog is closed. 127 128 @event selected 129 @param {plupload.Uploader} uploader Uploader instance sending the event. 130 @param {Array} files Array of selected files represented by plupload.File objects 131 */ 132 133 /** 134 Dispatched when file dialog is closed. 135 136 @event removed 137 @param {plupload.Uploader} uploader Uploader instance sending the event. 138 @param {Array} files Array of removed files represented by plupload.File objects 139 */ 140 141 /** 142 Dispatched when upload is started. 143 144 @event start 145 @param {plupload.Uploader} uploader Uploader instance sending the event. 146 */ 147 148 /** 149 Dispatched when upload is stopped. 150 151 @event stop 152 @param {plupload.Uploader} uploader Uploader instance sending the event. 153 */ 154 155 /** 156 Dispatched during the upload process. 157 158 @event progress 159 @param {plupload.Uploader} uploader Uploader instance sending the event. 160 @param {plupload.File} file File that is being uploaded (includes loaded and percent properties among others). 161 @param {Number} size Total file size in bytes. 162 @param {Number} loaded Number of bytes uploaded of the files total size. 163 @param {Number} percent Number of percentage uploaded of the file. 164 */ 165 166 /** 167 Dispatched when file is uploaded. 168 169 @event uploaded 170 @param {plupload.Uploader} uploader Uploader instance sending the event. 171 @param {plupload.File} file File that was uploaded. 172 @param {Enum} status Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE. 173 */ 174 175 /** 176 Dispatched when upload of the whole queue is complete. 177 178 @event complete 179 @param {plupload.Uploader} uploader Uploader instance sending the event. 180 @param {Array} files Array of uploaded files represented by plupload.File objects 181 */ 182 183 /** 184 Dispatched when the view is changed, e.g. from `list` to `thumbs` or vice versa. 185 186 @event viewchanged 187 @param {plupload.Uploader} uploader Uploader instance sending the event. 188 @param {String} type Current view type. 189 */ 190 191 /** 192 Dispatched when error of some kind is detected. 193 194 @event error 195 @param {plupload.Uploader} uploader Uploader instance sending the event. 196 @param {String} error Error message. 197 @param {plupload.File} file File that was uploaded. 198 @param {Enum} status Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE. 199 */ 200 201 var uploaders = {}; 202 203 function _(str) { 204 return plupload.translate(str) || str; 205 } 206 207 function renderUI(obj) { 208 obj.id = obj.attr('id'); 209 210 obj.html( 211 '<div class="plupload_wrapper">' + 212 '<div class="ui-widget-content plupload_container">' + 213 '<div class="ui-state-default ui-widget-header plupload_header">' + 214 '<div class="plupload_header_content">' + 215 '<div class="plupload_logo"> </div>' + 216 '<div class="plupload_header_title">' + _("Select files") + '</div>' + 217 '<div class="plupload_header_text">' + _("Add files to the upload queue and click the start button.") + '</div>' + 218 '<div class="plupload_view_switch">' + 219 '<input type="radio" id="'+obj.id+'_view_list" name="view_mode_'+obj.id+'" checked="checked" /><label class="plupload_button" for="'+obj.id+'_view_list" data-view="list">' + _('List') + '</label>' + 220 '<input type="radio" id="'+obj.id+'_view_thumbs" name="view_mode_'+obj.id+'" /><label class="plupload_button" for="'+obj.id+'_view_thumbs" data-view="thumbs">' + _('Thumbnails') + '</label>' + 221 '</div>' + 222 '</div>' + 223 '</div>' + 224 225 '<table class="plupload_filelist plupload_filelist_header ui-widget-header">' + 226 '<tr>' + 227 '<td class="plupload_cell plupload_file_name">' + _('Filename') + '</td>' + 228 '<td class="plupload_cell plupload_file_status">' + _('Status') + '</td>' + 229 '<td class="plupload_cell plupload_file_size">' + _('Size') + '</td>' + 230 '<td class="plupload_cell plupload_file_action"> </td>' + 231 '</tr>' + 232 '</table>' + 233 234 '<div class="plupload_content">' + 235 '<div class="plupload_droptext">' + _("Drag files here.") + '</div>' + 236 '<ul class="plupload_filelist_content"> </ul>' + 237 '<div class="plupload_clearer"> </div>' + 238 '</div>' + 239 240 '<table class="plupload_filelist plupload_filelist_footer ui-widget-header">' + 241 '<tr>' + 242 '<td class="plupload_cell plupload_file_name">' + 243 '<div class="plupload_buttons"><!-- Visible -->' + 244 '<a class="plupload_button plupload_add">' + _("Add Files") + '</a> ' + 245 '<a class="plupload_button plupload_start">' + _("Start Upload") + '</a> ' + 246 '<a class="plupload_button plupload_stop plupload_hidden">'+_("Stop Upload") + '</a> ' + 247 '</div>' + 248 249 '<div class="plupload_started plupload_hidden"><!-- Hidden -->' + 250 '<div class="plupload_progress plupload_right">' + 251 '<div class="plupload_progress_container"></div>' + 252 '</div>' + 253 254 '<div class="plupload_cell plupload_upload_status"></div>' + 255 256 '<div class="plupload_clearer"> </div>' + 257 '</div>' + 258 '</td>' + 259 '<td class="plupload_file_status"><span class="plupload_total_status">0%</span></td>' + 260 '<td class="plupload_file_size"><span class="plupload_total_file_size">0 kb</span></td>' + 261 '<td class="plupload_file_action"></td>' + 262 '</tr>' + 263 '</table>' + 264 265 '</div>' + 266 '<input class="plupload_count" value="0" type="hidden">' + 267 '</div>' 268 ); 269 } 270 271 272 $.widget("ui.plupload", { 273 274 widgetEventPrefix: '', 275 276 contents_bak: '', 277 278 options: { 279 browse_button_hover: 'ui-state-hover', 280 browse_button_active: 'ui-state-active', 281 282 filters: {}, 283 284 // widget specific 285 buttons: { 286 browse: true, 287 start: true, 288 stop: true 289 }, 290 291 views: { 292 list: true, 293 thumbs: false, 294 active: 'list', 295 remember: true // requires: https://github.com/carhartl/jquery-cookie, otherwise disabled even if set to true 296 }, 297 298 thumb_width: 100, 299 thumb_height: 60, 300 301 multiple_queues: true, // re-use widget by default 302 dragdrop : true, 303 autostart: false, 304 sortable: false, 305 rename: false 306 }, 307 308 FILE_COUNT_ERROR: -9001, 309 310 _create: function() { 311 var id = this.element.attr('id'); 312 if (!id) { 313 id = plupload.guid(); 314 this.element.attr('id', id); 315 } 316 this.id = id; 317 318 // backup the elements initial state 319 this.contents_bak = this.element.html(); 320 renderUI(this.element); 321 322 // container, just in case 323 this.container = $('.plupload_container', this.element).attr('id', id + '_container'); 324 325 this.content = $('.plupload_content', this.element); 326 327 if ($.fn.resizable) { 328 this.container.resizable({ 329 handles: 's', 330 minHeight: 300 331 }); 332 } 333 334 // list of files, may become sortable 335 this.filelist = $('.plupload_filelist_content', this.container) 336 .attr({ 337 id: id + '_filelist', 338 unselectable: 'on' 339 }); 340 341 342 // buttons 343 this.browse_button = $('.plupload_add', this.container).attr('id', id + '_browse'); 344 this.start_button = $('.plupload_start', this.container).attr('id', id + '_start'); 345 this.stop_button = $('.plupload_stop', this.container).attr('id', id + '_stop'); 346 this.thumbs_switcher = $('#' + id + '_view_thumbs'); 347 this.list_switcher = $('#' + id + '_view_list'); 348 349 if ($.ui.button) { 350 this.browse_button.button({ 351 icons: { primary: 'ui-icon-circle-plus' }, 352 disabled: true 353 }); 354 355 this.start_button.button({ 356 icons: { primary: 'ui-icon-circle-arrow-e' }, 357 disabled: true 358 }); 359 360 this.stop_button.button({ 361 icons: { primary: 'ui-icon-circle-close' } 362 }); 363 364 this.list_switcher.button({ 365 text: false, 366 icons: { secondary: "ui-icon-grip-dotted-horizontal" } 367 }); 368 369 this.thumbs_switcher.button({ 370 text: false, 371 icons: { secondary: "ui-icon-image" } 372 }); 373 } 374 375 // progressbar 376 this.progressbar = $('.plupload_progress_container', this.container); 377 378 if ($.ui.progressbar) { 379 this.progressbar.progressbar(); 380 } 381 382 // counter 383 this.counter = $('.plupload_count', this.element) 384 .attr({ 385 id: id + '_count', 386 name: id + '_count' 387 }); 388 389 // initialize uploader instance 390 this._initUploader(); 391 }, 392 393 _initUploader: function() { 394 var self = this 395 , id = this.id 396 , uploader 397 , options = { 398 container: id + '_buttons', 399 browse_button: id + '_browse' 400 } 401 ; 402 403 $('.plupload_buttons', this.element).attr('id', id + '_buttons'); 404 405 if (self.options.dragdrop) { 406 this.filelist.parent().attr('id', this.id + '_dropbox'); 407 options.drop_element = this.id + '_dropbox'; 408 } 409 410 this.filelist.on('click', function(e) { 411 if ($(e.target).hasClass('plupload_action_icon')) { 412 self.removeFile($(e.target).closest('.plupload_file').attr('id')); 413 e.preventDefault(); 414 } 415 }); 416 417 uploader = this.uploader = uploaders[id] = new plupload.Uploader($.extend(this.options, options)); 418 419 if (self.options.views.thumbs) { 420 uploader.settings.required_features.display_media = true; 421 } 422 423 // for backward compatibility 424 if (self.options.max_file_count) { 425 plupload.extend(uploader.getOption('filters'), { 426 max_file_count: self.options.max_file_count 427 }); 428 } 429 430 plupload.addFileFilter('max_file_count', function(maxCount, file, cb) { 431 if (maxCount <= this.files.length - (this.total.uploaded + this.total.failed)) { 432 self.browse_button.button('disable'); 433 this.disableBrowse(); 434 435 this.trigger('Error', { 436 code : self.FILE_COUNT_ERROR, 437 message : _("File count error."), 438 file : file 439 }); 440 cb(false); 441 } else { 442 cb(true); 443 } 444 }); 445 446 447 uploader.bind('Error', function(up, err) { 448 var message, details = ""; 449 450 message = '<strong>' + err.message + '</strong>'; 451 452 switch (err.code) { 453 case plupload.FILE_EXTENSION_ERROR: 454 details = o.sprintf(_("File: %s"), err.file.name); 455 break; 456 457 case plupload.FILE_SIZE_ERROR: 458 details = o.sprintf(_("File: %s, size: %d, max file size: %d"), err.file.name, plupload.formatSize(err.file.size), plupload.formatSize(plupload.parseSize(up.getOption('filters').max_file_size))); 459 break; 460 461 case plupload.FILE_DUPLICATE_ERROR: 462 details = o.sprintf(_("%s already present in the queue."), err.file.name); 463 break; 464 465 case self.FILE_COUNT_ERROR: 466 details = o.sprintf(_("Upload element accepts only %d file(s) at a time. Extra files were stripped."), up.getOption('filters').max_file_count || 0); 467 break; 468 469 case plupload.IMAGE_FORMAT_ERROR : 470 details = _("Image format either wrong or not supported."); 471 break; 472 473 case plupload.IMAGE_MEMORY_ERROR : 474 details = _("Runtime ran out of available memory."); 475 break; 476 477 /* // This needs a review 478 case plupload.IMAGE_DIMENSIONS_ERROR : 479 details = o.sprintf(_('Resoultion out of boundaries! <b>%s</b> runtime supports images only up to %wx%hpx.'), up.runtime, up.features.maxWidth, up.features.maxHeight); 480 break; */ 481 482 case plupload.HTTP_ERROR: 483 details = _("Upload URL might be wrong or doesn't exist."); 484 break; 485 } 486 487 message += " <br /><i>" + details + "</i>"; 488 489 self._trigger('error', null, { up: up, error: err } ); 490 491 // do not show UI if no runtime can be initialized 492 if (err.code === plupload.INIT_ERROR) { 493 setTimeout(function() { 494 self.destroy(); 495 }, 1); 496 } else { 497 self.notify('error', message); 498 } 499 }); 500 501 502 uploader.bind('PostInit', function(up) { 503 // all buttons are optional, so they can be disabled and hidden 504 if (!self.options.buttons.browse) { 505 self.browse_button.button('disable').hide(); 506 up.disableBrowse(true); 507 } else { 508 self.browse_button.button('enable'); 509 } 510 511 if (!self.options.buttons.start) { 512 self.start_button.button('disable').hide(); 513 } 514 515 if (!self.options.buttons.stop) { 516 self.stop_button.button('disable').hide(); 517 } 518 519 if (!self.options.unique_names && self.options.rename) { 520 self._enableRenaming(); 521 } 522 523 if (self.options.dragdrop && up.features.dragdrop) { 524 self.filelist.parent().addClass('plupload_dropbox'); 525 } 526 527 self._enableViewSwitcher(); 528 529 self.start_button.click(function(e) { 530 if (!$(this).button('option', 'disabled')) { 531 self.start(); 532 } 533 e.preventDefault(); 534 }); 535 536 self.stop_button.click(function(e) { 537 self.stop(); 538 e.preventDefault(); 539 }); 540 541 self._trigger('ready', null, { up: up }); 542 }); 543 544 // uploader internal events must run first 545 uploader.init(); 546 547 uploader.bind('FileFiltered', function(up, file) { 548 self._addFiles(file); 549 }); 550 551 uploader.bind('FilesAdded', function(up, files) { 552 self._trigger('selected', null, { up: up, files: files } ); 553 554 // re-enable sortable 555 if (self.options.sortable && $.ui.sortable) { 556 self._enableSortingList(); 557 } 558 559 self._trigger('updatelist', null, { filelist: self.filelist }); 560 561 if (self.options.autostart) { 562 // set a little delay to make sure that QueueChanged triggered by the core has time to complete 563 setTimeout(function() { 564 self.start(); 565 }, 10); 566 } 567 }); 568 569 uploader.bind('FilesRemoved', function(up, files) { 570 // destroy sortable if enabled 571 if ($.ui.sortable && self.options.sortable) { 572 $('tbody', self.filelist).sortable('destroy'); 573 } 574 575 $.each(files, function(i, file) { 576 $('#' + file.id).toggle("highlight", function() { 577 $(this).remove(); 578 }); 579 }); 580 581 if (up.files.length) { 582 // re-initialize sortable 583 if (self.options.sortable && $.ui.sortable) { 584 self._enableSortingList(); 585 } 586 } 587 588 self._trigger('updatelist', null, { filelist: self.filelist }); 589 self._trigger('removed', null, { up: up, files: files } ); 590 }); 591 592 uploader.bind('QueueChanged StateChanged', function() { 593 self._handleState(); 594 }); 595 596 uploader.bind('UploadFile', function(up, file) { 597 self._handleFileStatus(file); 598 }); 599 600 uploader.bind('FileUploaded', function(up, file) { 601 self._handleFileStatus(file); 602 self._trigger('uploaded', null, { up: up, file: file } ); 603 }); 604 605 uploader.bind('UploadProgress', function(up, file) { 606 self._handleFileStatus(file); 607 self._updateTotalProgress(); 608 self._trigger('progress', null, { up: up, file: file } ); 609 }); 610 611 uploader.bind('UploadComplete', function(up, files) { 612 self._addFormFields(); 613 self._trigger('complete', null, { up: up, files: files } ); 614 }); 615 }, 616 617 618 _setOption: function(key, value) { 619 var self = this; 620 621 if (key == 'buttons' && typeof(value) == 'object') { 622 value = $.extend(self.options.buttons, value); 623 624 if (!value.browse) { 625 self.browse_button.button('disable').hide(); 626 self.uploader.disableBrowse(true); 627 } else { 628 self.browse_button.button('enable').show(); 629 self.uploader.disableBrowse(false); 630 } 631 632 if (!value.start) { 633 self.start_button.button('disable').hide(); 634 } else { 635 self.start_button.button('enable').show(); 636 } 637 638 if (!value.stop) { 639 self.stop_button.button('disable').hide(); 640 } else { 641 self.start_button.button('enable').show(); 642 } 643 } 644 645 self.uploader.settings[key] = value; 646 }, 647 648 649 /** 650 Start upload. Triggers `start` event. 651 652 @method start 653 */ 654 start: function() { 655 this.uploader.start(); 656 this._trigger('start', null, { up: this.uploader }); 657 }, 658 659 660 /** 661 Stop upload. Triggers `stop` event. 662 663 @method stop 664 */ 665 stop: function() { 666 this.uploader.stop(); 667 this._trigger('stop', null, { up: this.uploader }); 668 }, 669 670 671 /** 672 Enable browse button. 673 674 @method enable 675 */ 676 enable: function() { 677 this.browse_button.button('enable'); 678 this.uploader.disableBrowse(false); 679 }, 680 681 682 /** 683 Disable browse button. 684 685 @method disable 686 */ 687 disable: function() { 688 this.browse_button.button('disable'); 689 this.uploader.disableBrowse(true); 690 }, 691 692 693 /** 694 Retrieve file by it's unique id. 695 696 @method getFile 697 @param {String} id Unique id of the file 698 @return {plupload.File} 699 */ 700 getFile: function(id) { 701 var file; 702 703 if (typeof id === 'number') { 704 file = this.uploader.files[id]; 705 } else { 706 file = this.uploader.getFile(id); 707 } 708 return file; 709 }, 710 711 /** 712 Return array of files currently in the queue. 713 714 @method getFiles 715 @return {Array} Array of files in the queue represented by plupload.File objects 716 */ 717 getFiles: function() { 718 return this.uploader.files; 719 }, 720 721 722 /** 723 Remove the file from the queue. 724 725 @method removeFile 726 @param {plupload.File|String} file File to remove, might be specified directly or by it's unique id 727 */ 728 removeFile: function(file) { 729 if (plupload.typeOf(file) === 'string') { 730 file = this.getFile(file); 731 } 732 this.uploader.removeFile(file); 733 }, 734 735 736 /** 737 Clear the file queue. 738 739 @method clearQueue 740 */ 741 clearQueue: function() { 742 this.uploader.splice(); 743 }, 744 745 746 /** 747 Retrieve internal plupload.Uploader object (usually not required). 748 749 @method getUploader 750 @return {plupload.Uploader} 751 */ 752 getUploader: function() { 753 return this.uploader; 754 }, 755 756 757 /** 758 Trigger refresh procedure, specifically browse_button re-measure and re-position operations. 759 Might get handy, when UI Widget is placed within the popup, that is constantly hidden and shown 760 again - without calling this method after each show operation, dialog trigger might get displaced 761 and disfunctional. 762 763 @method refresh 764 */ 765 refresh: function() { 766 this.uploader.refresh(); 767 }, 768 769 770 /** 771 Display a message in notification area. 772 773 @method notify 774 @param {Enum} type Type of the message, either `error` or `info` 775 @param {String} message The text message to display. 776 */ 777 notify: function(type, message) { 778 var popup = $( 779 '<div class="plupload_message">' + 780 '<span class="plupload_message_close ui-icon ui-icon-circle-close" title="'+_('Close')+'"></span>' + 781 '<p><span class="ui-icon"></span>' + message + '</p>' + 782 '</div>' 783 ); 784 785 popup 786 .addClass('ui-state-' + (type === 'error' ? 'error' : 'highlight')) 787 .find('p .ui-icon') 788 .addClass('ui-icon-' + (type === 'error' ? 'alert' : 'info')) 789 .end() 790 .find('.plupload_message_close') 791 .click(function() { 792 popup.remove(); 793 }) 794 .end(); 795 796 $('.plupload_header', this.container).append(popup); 797 }, 798 799 800 /** 801 Destroy the widget, the uploader, free associated resources and bring back original html. 802 803 @method destroy 804 */ 805 destroy: function() { 806 // destroy uploader instance 807 this.uploader.destroy(); 808 809 // unbind all button events 810 $('.plupload_button', this.element).unbind(); 811 812 // destroy buttons 813 if ($.ui.button) { 814 $('.plupload_add, .plupload_start, .plupload_stop', this.container) 815 .button('destroy'); 816 } 817 818 // destroy progressbar 819 if ($.ui.progressbar) { 820 this.progressbar.progressbar('destroy'); 821 } 822 823 // destroy sortable behavior 824 if ($.ui.sortable && this.options.sortable) { 825 $('tbody', this.filelist).sortable('destroy'); 826 } 827 828 // restore the elements initial state 829 this.element 830 .empty() 831 .html(this.contents_bak); 832 this.contents_bak = ''; 833 834 $.Widget.prototype.destroy.apply(this); 835 }, 836 837 838 _handleState: function() { 839 var up = this.uploader 840 , filesPending = up.files.length - (up.total.uploaded + up.total.failed) 841 , maxCount = up.getOption('filters').max_file_count || 0 842 ; 843 844 if (plupload.STARTED === up.state) { 845 $([]) 846 .add(this.stop_button) 847 .add('.plupload_started') 848 .removeClass('plupload_hidden'); 849 850 this.start_button.button('disable'); 851 852 if (!this.options.multiple_queues) { 853 this.browse_button.button('disable'); 854 up.disableBrowse(); 855 } 856 857 $('.plupload_upload_status', this.element).html(o.sprintf(_('Uploaded %d/%d files'), up.total.uploaded, up.files.length)); 858 $('.plupload_header_content', this.element).addClass('plupload_header_content_bw'); 859 } 860 else if (plupload.STOPPED === up.state) { 861 $([]) 862 .add(this.stop_button) 863 .add('.plupload_started') 864 .addClass('plupload_hidden'); 865 866 if (filesPending) { 867 this.start_button.button('enable'); 868 } else { 869 this.start_button.button('disable'); 870 } 871 872 if (this.options.multiple_queues) { 873 $('.plupload_header_content', this.element).removeClass('plupload_header_content_bw'); 874 } 875 876 // if max_file_count defined, only that many files can be queued at once 877 if (this.options.multiple_queues && maxCount && maxCount > filesPending) { 878 this.browse_button.button('enable'); 879 up.disableBrowse(false); 880 } 881 882 this._updateTotalProgress(); 883 } 884 885 if (up.total.queued === 0) { 886 $('.ui-button-text', this.browse_button).html(_('Add Files')); 887 } else { 888 $('.ui-button-text', this.browse_button).html(o.sprintf(_('%d files queued'), up.total.queued)); 889 } 890 891 up.refresh(); 892 }, 893 894 895 _handleFileStatus: function(file) { 896 var $file = $('#' + file.id), actionClass, iconClass; 897 898 // since this method might be called asynchronously, file row might not yet be rendered 899 if (!$file.length) { 900 return; 901 } 902 903 switch (file.status) { 904 case plupload.DONE: 905 actionClass = 'plupload_done'; 906 iconClass = 'plupload_action_icon ui-icon ui-icon-circle-check'; 907 break; 908 909 case plupload.FAILED: 910 actionClass = 'ui-state-error plupload_failed'; 911 iconClass = 'plupload_action_icon ui-icon ui-icon-alert'; 912 break; 913 914 case plupload.QUEUED: 915 actionClass = 'plupload_delete'; 916 iconClass = 'plupload_action_icon ui-icon ui-icon-circle-minus'; 917 break; 918 919 case plupload.UPLOADING: 920 actionClass = 'ui-state-highlight plupload_uploading'; 921 iconClass = 'plupload_action_icon ui-icon ui-icon-circle-arrow-w'; 922 923 // scroll uploading file into the view if its bottom boundary is out of it 924 var scroller = $('.plupload_scroll', this.container) 925 , scrollTop = scroller.scrollTop() 926 , scrollerHeight = scroller.height() 927 , rowOffset = $file.position().top + $file.height() 928 ; 929 930 if (scrollerHeight < rowOffset) { 931 scroller.scrollTop(scrollTop + rowOffset - scrollerHeight); 932 } 933 934 // Set file specific progress 935 $file 936 .find('.plupload_file_percent') 937 .html(file.percent + '%') 938 .end() 939 .find('.plupload_file_progress') 940 .css('width', file.percent + '%') 941 .end() 942 .find('.plupload_file_size') 943 .html(plupload.formatSize(file.size)); 944 break; 945 } 946 actionClass += ' ui-state-default plupload_file'; 947 948 $file 949 .attr('class', actionClass) 950 .find('.plupload_action_icon') 951 .attr('class', iconClass); 952 }, 953 954 955 _updateTotalProgress: function() { 956 var up = this.uploader; 957 958 // Scroll to end of file list 959 this.filelist[0].scrollTop = this.filelist[0].scrollHeight; 960 961 this.progressbar.progressbar('value', up.total.percent); 962 963 this.element 964 .find('.plupload_total_status') 965 .html(up.total.percent + '%') 966 .end() 967 .find('.plupload_total_file_size') 968 .html(plupload.formatSize(up.total.size)) 969 .end() 970 .find('.plupload_upload_status') 971 .html(o.sprintf(_('Uploaded %d/%d files'), up.total.uploaded, up.files.length)); 972 }, 973 974 975 _displayThumbs: function() { 976 var self = this 977 , tw, th // thumb width/height 978 , cols 979 , num = 0 // number of simultaneously visible thumbs 980 , thumbs = [] // array of thumbs to preload at any given moment 981 , loading = false 982 ; 983 984 if (!this.options.views.thumbs) { 985 return; 986 } 987 988 989 function onLast(el, eventName, cb) { 990 var timer; 991 992 el.on(eventName, function() { 993 clearTimeout(timer); 994 timer = setTimeout(function() { 995 clearTimeout(timer); 996 cb(); 997 }, 300); 998 }); 999 } 1000 1001 1002 // calculate number of simultaneously visible thumbs 1003 function measure() { 1004 if (!tw || !th) { 1005 var wrapper = $('.plupload_file:eq(0)', self.filelist); 1006 tw = wrapper.outerWidth(true); 1007 th = wrapper.outerHeight(true); 1008 } 1009 1010 var aw = self.content.width(), ah = self.content.height(); 1011 cols = Math.floor(aw / tw); 1012 num = cols * (Math.ceil(ah / th) + 1); 1013 } 1014 1015 1016 function pickThumbsToLoad() { 1017 // calculate index of virst visible thumb 1018 var startIdx = Math.floor(self.content.scrollTop() / th) * cols; 1019 // get potentially visible thumbs that are not yet visible 1020 thumbs = $('.plupload_file', self.filelist) 1021 .slice(startIdx, startIdx + num) 1022 .filter('.plupload_file_loading') 1023 .get(); 1024 } 1025 1026 1027 function init() { 1028 function mpl() { // measure, pick, load 1029 if (self.view_mode !== 'thumbs') { 1030 return; 1031 } 1032 measure(); 1033 pickThumbsToLoad(); 1034 lazyLoad(); 1035 } 1036 1037 if ($.fn.resizable) { 1038 onLast(self.container, 'resize', mpl); 1039 } 1040 1041 onLast(self.window, 'resize', mpl); 1042 onLast(self.content, 'scroll', mpl); 1043 1044 self.element.on('viewchanged selected', mpl); 1045 1046 mpl(); 1047 } 1048 1049 1050 function preloadThumb(file, cb) { 1051 var img = new o.Image(); 1052 1053 img.onload = function() { 1054 var thumb = $('#' + file.id + ' .plupload_file_thumb', self.filelist).html(''); 1055 this.embed(thumb[0], { 1056 width: self.options.thumb_width, 1057 height: self.options.thumb_height, 1058 crop: true, 1059 swf_url: o.resolveUrl(self.options.flash_swf_url), 1060 xap_url: o.resolveUrl(self.options.silverlight_xap_url) 1061 }); 1062 }; 1063 1064 img.bind("embedded error", function() { 1065 $('#' + file.id, self.filelist).removeClass('plupload_file_loading'); 1066 this.destroy(); 1067 setTimeout(cb, 1); // detach, otherwise ui might hang (in SilverLight for example) 1068 }); 1069 1070 img.load(file.getSource()); 1071 } 1072 1073 1074 function lazyLoad() { 1075 if (self.view_mode !== 'thumbs' || loading) { 1076 return; 1077 } 1078 1079 pickThumbsToLoad(); 1080 if (!thumbs.length) { 1081 return; 1082 } 1083 1084 loading = true; 1085 1086 preloadThumb(self.getFile($(thumbs.shift()).attr('id')), function() { 1087 loading = false; 1088 lazyLoad(); 1089 }); 1090 } 1091 1092 // this has to run only once to measure structures and bind listeners 1093 this.element.on('selected', function onselected() { 1094 self.element.off('selected', onselected); 1095 init(); 1096 }); 1097 }, 1098 1099 1100 _addFiles: function(files) { 1101 var self = this, file_html, html = ''; 1102 1103 file_html = '<li class="plupload_file ui-state-default plupload_file_loading plupload_delete" id="%id%" style="width:%thumb_width%px;">' + 1104 '<div class="plupload_file_thumb" style="width:%thumb_width%px;height:%thumb_height%px;">' + 1105 '<div class="plupload_file_dummy ui-widget-content" style="line-height:%thumb_height%px;"><span class="ui-state-disabled">%ext% </span></div>' + 1106 '</div>' + 1107 '<div class="plupload_file_status">' + 1108 '<div class="plupload_file_progress ui-widget-header" style="width: 0%"> </div>' + 1109 '<span class="plupload_file_percent">%percent% </span>' + 1110 '</div>' + 1111 '<div class="plupload_file_name" title="%name%">' + 1112 '<span class="plupload_file_name_wrapper">%name% </span>' + 1113 '</div>' + 1114 '<div class="plupload_file_action">' + 1115 '<div class="plupload_action_icon ui-icon ui-icon-circle-minus"> </div>' + 1116 '</div>' + 1117 '<div class="plupload_file_size">%size% </div>' + 1118 '<div class="plupload_file_fields"> </div>' + 1119 '</li>'; 1120 1121 if (plupload.typeOf(files) !== 'array') { 1122 files = [files]; 1123 } 1124 1125 $.each(files, function(i, file) { 1126 var ext = o.Mime.getFileExtension(file.name) || 'none'; 1127 1128 html += file_html.replace(/%(\w+)%/g, function($0, $1) { 1129 switch ($1) { 1130 case 'thumb_width': 1131 case 'thumb_height': 1132 return self.options[$1]; 1133 1134 case 'size': 1135 return plupload.formatSize(file.size); 1136 1137 case 'ext': 1138 return ext; 1139 1140 default: 1141 return file[$1] || ''; 1142 } 1143 }); 1144 }); 1145 1146 self.filelist.append(html); 1147 }, 1148 1149 1150 _addFormFields: function() { 1151 var self = this; 1152 1153 // re-add from fresh 1154 $('.plupload_file_fields', this.filelist).html(''); 1155 1156 plupload.each(this.uploader.files, function(file, count) { 1157 var fields = '' 1158 , id = self.id + '_' + count 1159 ; 1160 1161 if (file.target_name) { 1162 fields += '<input type="hidden" name="' + id + '_tmpname" value="'+plupload.xmlEncode(file.target_name)+'" />'; 1163 } 1164 fields += '<input type="hidden" name="' + id + '_name" value="'+plupload.xmlEncode(file.name)+'" />'; 1165 fields += '<input type="hidden" name="' + id + '_status" value="' + (file.status === plupload.DONE ? 'done' : 'failed') + '" />'; 1166 1167 $('#' + file.id).find('.plupload_file_fields').html(fields); 1168 }); 1169 1170 this.counter.val(this.uploader.files.length); 1171 }, 1172 1173 1174 _viewChanged: function(view) { 1175 // update or write a new cookie 1176 if (this.options.views.remember && $.cookie) { 1177 $.cookie('plupload_ui_view', view, { expires: 7, path: '/' }); 1178 } 1179 1180 // ugly fix for IE6 - make content area stretchable 1181 if (o.Env.browser === 'IE' && o.Env.version < 7) { 1182 this.content.attr('style', 'height:expression(document.getElementById("' + this.id + '_container' + '").clientHeight - ' + (view === 'list' ? 132 : 102) + ')'); 1183 } 1184 1185 this.container.removeClass('plupload_view_list plupload_view_thumbs').addClass('plupload_view_' + view); 1186 this.view_mode = view; 1187 this._trigger('viewchanged', null, { view: view }); 1188 }, 1189 1190 1191 _enableViewSwitcher: function() { 1192 var self = this 1193 , view 1194 , switcher = $('.plupload_view_switch', this.container) 1195 , buttons 1196 , button 1197 ; 1198 1199 plupload.each(['list', 'thumbs'], function(view) { 1200 if (!self.options.views[view]) { 1201 switcher.find('[for="' + self.id + '_view_' + view + '"], #'+ self.id +'_view_' + view).remove(); 1202 } 1203 }); 1204 1205 // check if any visible left 1206 buttons = switcher.find('.plupload_button'); 1207 1208 if (buttons.length === 1) { 1209 switcher.hide(); 1210 view = buttons.eq(0).data('view'); 1211 this._viewChanged(view); 1212 } else if ($.ui.button && buttons.length > 1) { 1213 if (this.options.views.remember && $.cookie) { 1214 view = $.cookie('plupload_ui_view'); 1215 } 1216 1217 // if wierd case, bail out to default 1218 if (!~plupload.inArray(view, ['list', 'thumbs'])) { 1219 view = this.options.views.active; 1220 } 1221 1222 switcher 1223 .show() 1224 .buttonset() 1225 .find('.ui-button') 1226 .click(function(e) { 1227 view = $(this).data('view'); 1228 self._viewChanged(view); 1229 e.preventDefault(); // avoid auto scrolling to widget in IE and FF (see #850) 1230 }); 1231 1232 // if view not active - happens when switcher wasn't clicked manually 1233 button = switcher.find('[for="' + self.id + '_view_'+view+'"]'); 1234 if (button.length) { 1235 button.trigger('click'); 1236 } 1237 } else { 1238 switcher.show(); 1239 this._viewChanged(this.options.views.active); 1240 } 1241 1242 // initialize thumb viewer if requested 1243 if (this.options.views.thumbs) { 1244 this._displayThumbs(); 1245 } 1246 }, 1247 1248 1249 _enableRenaming: function() { 1250 var self = this; 1251 1252 this.filelist.dblclick(function(e) { 1253 var nameSpan = $(e.target), nameInput, file, parts, name, ext = ""; 1254 1255 if (!nameSpan.hasClass('plupload_file_name_wrapper')) { 1256 return; 1257 } 1258 1259 // Get file name and split out name and extension 1260 file = self.uploader.getFile(nameSpan.closest('.plupload_file')[0].id); 1261 name = file.name; 1262 parts = /^(.+)(\.[^.]+)$/.exec(name); 1263 if (parts) { 1264 name = parts[1]; 1265 ext = parts[2]; 1266 } 1267 1268 // Display input element 1269 nameInput = $('<input class="plupload_file_rename" type="text" />').width(nameSpan.width()).insertAfter(nameSpan.hide()); 1270 nameInput.val(name).blur(function() { 1271 nameSpan.show().parent().scrollLeft(0).end().next().remove(); 1272 }).keydown(function(e) { 1273 var nameInput = $(this); 1274 1275 if ($.inArray(e.keyCode, [13, 27]) !== -1) { 1276 e.preventDefault(); 1277 1278 // Rename file and glue extension back on 1279 if (e.keyCode === 13) { 1280 file.name = nameInput.val() + ext; 1281 nameSpan.html(file.name); 1282 } 1283 nameInput.blur(); 1284 } 1285 })[0].focus(); 1286 }); 1287 }, 1288 1289 1290 _enableSortingList: function() { 1291 var self = this; 1292 1293 if ($('.plupload_file', this.filelist).length < 2) { 1294 return; 1295 } 1296 1297 // destroy sortable if enabled 1298 $('tbody', this.filelist).sortable('destroy'); 1299 1300 // enable 1301 this.filelist.sortable({ 1302 items: '.plupload_delete', 1303 1304 cancel: 'object, .plupload_clearer', 1305 1306 stop: function() { 1307 var files = []; 1308 1309 $.each($(this).sortable('toArray'), function(i, id) { 1310 files[files.length] = self.uploader.getFile(id); 1311 }); 1312 1313 files.unshift(files.length); 1314 files.unshift(0); 1315 1316 // re-populate files array 1317 Array.prototype.splice.apply(self.uploader.files, files); 1318 } 1319 }); 1320 } 1321 }); 1322 1323 } (window, document, plupload, mOxie, jQuery));