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">&nbsp;</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">&nbsp;</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>&nbsp;' +
   242  							'<a class="plupload_button plupload_start">' + _('Start Upload') + '</a>&nbsp;' +
   243  							'<a class="plupload_button plupload_stop plupload_hidden">'+_('Stop Upload') + '</a>&nbsp;' +
   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">&nbsp;</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));