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