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