PageSourceSearch

https://www.waglebagle.net/mall/renewal/js/jquery-plugin/plupload/…/jquery.ui.plupload_custom.min.js

js waglebagle.net collected 2026-10-04 16:31:33 UTC 44,962 bytes, 1,478 lines download raw bytes

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/**
23jQuery UI based implementation of the Plupload API - multi-runtime file uploading API.
24
25To 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
28In general the widget is designed the way that you do not usually need to do anything to it after you instantiate it.
29But! 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/**
116Dispatched when the widget is initialized and ready.
117
118@event ready
119@param {plupload.Uploader} uploader Uploader instance sending the event.
120*/
121
122/**
123Dispatched 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/**
131Dispatched 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/**
139Dispatched when upload is started.
140
141@event start
142@param {plupload.Uploader} uploader Uploader instance sending the event.
143*/
144
145/**
146Dispatched when upload is stopped.
147
148@event stop
149@param {plupload.Uploader} uploader Uploader instance sending the event.
150*/
151
152/**
153Dispatched 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/**
164Dispatched 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/**
173Dispatched 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/**
181Dispatched 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/**
189Dispatched 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
198var uploaders = {};
199
200function _(str) {
201	return plupload.translate(str) || str;
202}
203
204function renderUI(obj) {
205	obj.id = obj.attr('id');
206
207	obj.html(
208		/***
209		'<div class="plupload_wrapper">' +
210			'<div class="ui-widget-content plupload_container">' +
211				'<div class="ui-state-default ui-widget-header plupload_header">' +
212					'<div class="plupload_header_content">' +
213						'<div class="plupload_logo"> </div>' +
214						'<div class="plupload_header_title">' + _('Select files') + '</div>' +
215						'<div class="plupload_header_text">' + _('Add files to the upload queue and click the start button.') + '</div>' +
216						'<div class="plupload_view_switch">' +
217							'<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>' +
218							'<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>' +
219						'</div>' +
220					'</div>' +
221				'</div>' +
222
223				'<table class="plupload_filelist plupload_filelist_header ui-widget-header">' +
224				'<tr>' +
225					'<td class="plupload_cell plupload_file_name">' + _('Filename') + '</td>' +
226					'<td class="plupload_cell plupload_file_status">' + _('Status') + '</td>' +
227					'<td class="plupload_cell plupload_file_size">' + _('Size') + '</td>' +
228					'<td class="plupload_cell plupload_file_action">&nbsp;</td>' +
229				'</tr>' +
230				'</table>' +
231
232				'<div class="plupload_content">' +
233					'<div class="plupload_droptext">' + _("Drag files here.") + '</div>' +
234					'<ul class="plupload_filelist_content"> </ul>' +
235					'<div class="plupload_clearer">&nbsp;</div>' +
236				'</div>' +
237
238				'<table class="plupload_filelist plupload_filelist_footer ui-widget-header">' +
239				'<tr>' +
240					'<td class="plupload_cell plupload_file_name">' +
241						'<div class="plupload_buttons"><!-- Visible -->' +
242							'<a class="plupload_button plupload_add">' + _('Add Files') + '</a>&nbsp;' +
243							'<a class="plupload_button plupload_start">' + _('Start Upload') + '</a>&nbsp;' +
244							'<a class="plupload_button plupload_stop plupload_hidden">'+_('Stop Upload') + '</a>&nbsp;' +
245						'</div>' +
246
247						'<div class="plupload_started plupload_hidden"><!-- Hidden -->' +
248							'<div class="plupload_progress plupload_right">' +
249								'<div class="plupload_progress_container"></div>' +
250							'</div>' +
251
252							'<div class="plupload_cell plupload_upload_status"></div>' +
253
254							'<div class="plupload_clearer">&nbsp;</div>' +
255						'</div>' +
256					'</td>' +
257					'<td class="plupload_file_status"><span class="plupload_total_status">0%</span></td>' +
258					'<td class="plupload_file_size"><span class="plupload_total_file_size">0 kb</span></td>' +
259					'<td class="plupload_file_action"></td>' +
260				'</tr>' +
261				'</table>' +
262
263			'</div>' +
264			'<input class="plupload_count" value="0" type="hidden">' +
265		'</div>'
266		***/
267	);
268}
269
270
271$.widget("ui.plupload", {
272
273	widgetEventPrefix: '',
274
275	contents_bak: '',
276
277	options: {
278		browse_button_hover: 'ui-state-hover',
279		browse_button_active: 'ui-state-active',
280
281		// widget specific
282		dragdrop : true,
283		multiple_queues: true, // re-use widget by default
284		buttons: {
285			browse: true,
286			start: true,
287			stop: true
288		},
289		views: {
290			list: true,
291			thumbs: false,
292			movie: false,
293			active: 'list',
294			remember: false // requires: https://github.com/carhartl/jquery-cookie, otherwise disabled even if set to true
295		},
296		autostart: false,
297		sortable: false,
298		rename: false,
299		max_file_count: 0 // unlimited
300
301		,old_file_count: 0			// edit mode > past added file count
302	},
303
304	FILE_COUNT_ERROR: -9001,
305
306	_create: function() {
307		var id = this.element.attr('id');
308		if (!id) {
309			id = plupload.guid();
310			this.element.attr('id', id);
311		}
312		this.id = id;
313
314		// backup the elements initial state
315		this.contents_bak = this.element.html();
316		renderUI(this.element);
317
318		// container, just in case
319		this.container = $('.plupload_container', this.element).attr('id', id + '_container');
320
321		this.content = $('.plupload_content', this.element);
322
323		if ($.fn.resizable) {
324			this.container.resizable({
325				handles: 's',
326				minHeight: 300
327			});
328		}
329
330		// list of files, may become sortable
331		this.filelist = $('.plupload_filelist_content', this.container)
332			.attr({
333				id: id + '_filelist',
334				unselectable: 'on'
335			});
336
337
338		// buttons
339		this.browse_button = $('.plupload_add', this.container).attr('id', id + '_browse');
340		this.start_button = $('.plupload_start', this.container).attr('id', id + '_start');
341		this.stop_button = $('.plupload_stop', this.container).attr('id', id + '_stop');
342		this.thumbs_switcher = $('#' + id + '_view_thumbs');
343		this.list_switcher = $('#' + id + '_view_list');
344
345		if ($.ui.button) {
346			this.browse_button.button({
347				icons: { primary: 'ui-icon-circle-plus' },
348				disabled: true
349			});
350
351			this.start_button.button({
352				icons: { primary: 'ui-icon-circle-arrow-e' },
353				disabled: true
354			});
355
356			this.stop_button.button({
357				icons: { primary: 'ui-icon-circle-close' }
358			});
359
360			this.list_switcher.button({
361				text: false,
362				icons: { secondary: "ui-icon-grip-dotted-horizontal" }
363			});
364
365			this.thumbs_switcher.button({
366				text: false,
367				icons: { secondary: "ui-icon-image" }
368			});
369		}
370
371		// progressbar
372		this.progressbar = $('.plupload_progress_container', this.container);
373
374		if ($.ui.progressbar) {
375			this.progressbar.progressbar();
376		}
377
378		// counter
379		this.counter = $('.plupload_count', this.element)
380			.attr({
381				id: id + '_count',
382				name: id + '_count'
383			});
384
385		// initialize uploader instance
386		this._initUploader();
387	},
388
389	_initUploader: function() {
390		var self = this
391		, id = this.id
392		, uploader
393		, options = {
394			container: id + '_buttons',
395			browse_button: id + '_browse'
396		}
397		;
398
399		$('.plupload_buttons', this.element).attr('id', id + '_buttons');
400
401		if (self.options.dragdrop) {
402			this.filelist.parent().attr('id', this.id + '_dropbox');
403			options.drop_element = this.id + '_dropbox';
404		}
405
406		uploader = this.uploader = uploaders[id] = new plupload.Uploader($.extend(this.options, options));
407
408		if (self.options.views.thumbs && !self.options.views.movie ) {
409			uploader.settings.required_features.display_media = true;
410		}
411
412
413		uploader.bind('Error', function(up, err) {
414			var message, details = "";
415
416			message = '<strong style="margin-top:10px;">' + err.message + '</strong>';
417
418			switch (err.code) {
419				case plupload.FILE_EXTENSION_ERROR:
420					details = o.sprintf(_("File: %s"), err.file.name);
421					break;
422
423				case plupload.FILE_SIZE_ERROR:
424					message = '<strong style="margin-top:10px;">첨부 가능한 파일용량을 초과했습니다.</strong>';
425					details = o.sprintf(_("<b>Size:</b> %s<br /><b>File:</b> %s"), plupload.formatSize(err.file.size), err.file.name);
426						//- details = o.sprintf(_("File: %s, size: %d, max file size: %d"), err.file.name, err.file.size, plupload.parseSize(self.options.max_file_size));
427					break;
428
429				case plupload.FILE_DUPLICATE_ERROR:
430					details = o.sprintf(_("%s already present in the queue."), err.file.name);
431					break;
432
433				case self.FILE_COUNT_ERROR:
434					// details = o.sprintf(_("Upload element accepts only %d file(s) at a time. Extra files were stripped."), self.options.max_file_count);
435					details = o.sprintf(_("첨부 가능한 최대 파일 갯수 : %d 개"), self.options.max_file_count);
436					break;
437
438				case plupload.IMAGE_FORMAT_ERROR :
439					details = _("Image format either wrong or not supported.");
440					break;
441
442				case plupload.IMAGE_MEMORY_ERROR :
443					details = _("Runtime ran out of available memory.");
444					break;
445
446				/* // This needs a review
447				case plupload.IMAGE_DIMENSIONS_ERROR :
448					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);
449					break;	*/
450
451				case plupload.HTTP_ERROR:
452					details = _("Upload URL might be wrong or doesn't exist.");
453					break;
454			}
455
456			message += " <br /><div style='padding-top:8px; font-size:12px; line-height:18px;'>" + details + "</div>";
457
458			self._trigger('error', null, { up: up, error: err } );
459
460			// do not show UI if no runtime can be initialized
461			if (err.code === plupload.INIT_ERROR) {
462				setTimeout(function() {
463					self.destroy();
464				}, 1);
465			} else {
466				self.notify('error', message);
467			}
468		});
469
470
471		uploader.bind('PostInit', function(up) {
472			// all buttons are optional, so they can be disabled and hidden
473			if (!self.options.buttons.browse) {
474				self.browse_button.button('disable').hide();
475				up.disableBrowse(true);
476			} else {
477				self.browse_button.button('enable');
478			}
479
480			if (!self.options.buttons.start) {
481				self.start_button.button('disable').hide();
482			}
483
484			if (!self.options.buttons.stop) {
485				self.stop_button.button('disable').hide();
486			}
487
488			if (!self.options.unique_names && self.options.rename) {
489				self._enableRenaming();
490			}
491
492			if (self.options.dragdrop && up.features.dragdrop) {
493				self.filelist.parent().addClass('plupload_dropbox');
494			}
495
496			self._enableViewSwitcher();
497
498			self.start_button.click(function(e) {
499				if (!$(this).button('option', 'disabled')) {
500					self.start();
501				}
502				e.preventDefault();
503			});
504
505			self.stop_button.click(function(e) {
506				self.stop();
507				e.preventDefault();
508			});
509
510			self._trigger('ready', null, { up: up });
511		});
512
513
514		// check if file count doesn't exceed the limit
515		if (self.options.max_file_count) {
516			self.options.multiple_queues = false; // one go only
517
518			uploader.bind('FilesAdded', function(up, selectedFiles) {
519				var selectedCount = selectedFiles.length;
520				var extraCount = (up.files.length + self.options.old_file_count + selectedCount) - self.options.max_file_count;
521					//- var extraCount = ( (/* up.files.length */ self.filelist.find('li.plupload_file').length ) + selectedCount ) - self.options.max_file_count;
522
523				if (extraCount > 0) {
524					selectedFiles.splice(selectedCount - extraCount, extraCount);
525					up.trigger('Error', {
526						code : self.FILE_COUNT_ERROR,
527						message : _('첨부가능한 파일 갯수를 초과했습니다.')
528					});
529				}
530			});
531		}
532
533		// uploader internal events must run first
534		uploader.init();
535
536		uploader.bind('FileFiltered', function(up, file) {
537			self._addFiles(file);
538		});
539
540		uploader.bind('FilesAdded', function(up, files) {
541			self._trigger('selected', null, { up: up, files: files } );
542
543			// re-enable sortable
544			if (self.options.sortable && $.ui.sortable) {
545				self._enableSortingList();
546			}
547
548			self._trigger('updatelist', null, { filelist: self.filelist });
549
550			if (self.options.autostart) {
551				// set a little delay to make sure that QueueChanged triggered by the core has time to complete
552				setTimeout(function() {
553					self.start();
554				}, 10);
555			}
556		});
557
558		uploader.bind('FilesRemoved', function(up, files) {
559			self._trigger('removed', null, { up: up, files: files } );
560		});
561
562		uploader.bind('QueueChanged StateChanged', function() {
563			self._handleState();
564		});
565
566		uploader.bind('UploadFile', function(up, file) {
567			self._handleFileStatus(file);
568		});
569
570		uploader.bind('FileUploaded', function(up, file) {
571			self._handleFileStatus(file);
572			self._trigger('uploaded', null, { up: up, file: file } );
573		});
574
575		uploader.bind('UploadProgress', function(up, file) {
576			self._handleFileStatus(file);
577			self._updateTotalProgress();
578			self._trigger('progress', null, { up: up, file: file } );
579		});
580
581		uploader.bind('UploadComplete', function(up, files) {
582			self._addFormFields();
583			self._trigger('complete', null, { up: up, files: files } );
584		});
585
586		uploader.bind('DrawTotalCount', function(up) {
587			self._drawTotalCount();
588			// self._trigger('complete', null, { up: up, files: files } );
589		});
590	},
591
592
593	_setOption: function(key, value) {
594		var self = this;
595
596		if (key == 'buttons' && typeof(value) == 'object') {
597			value = $.extend(self.options.buttons, value);
598
599			if (!value.browse) {
600				self.browse_button.button('disable').hide();
601				self.uploader.disableBrowse(true);
602			} else {
603				self.browse_button.button('enable').show();
604				self.uploader.disableBrowse(false);
605			}
606
607			if (!value.start) {
608				self.start_button.button('disable').hide();
609			} else {
610				self.start_button.button('enable').show();
611			}
612
613			if (!value.stop) {
614				self.stop_button.button('disable').hide();
615			} else {
616				self.start_button.button('enable').show();
617			}
618		}
619
620		self.uploader.settings[key] = value;
621	},
622
623
624	/**
625	Start upload. Triggers `start` event.
626
627	@method start
628	*/
629	start: function() {
630		this.uploader.start();
631		this._trigger('start', null, { up: this.uploader });
632	},
633
634
635	/**
636	Stop upload. Triggers `stop` event.
637
638	@method stop
639	*/
640	stop: function() {
641		this.uploader.stop();
642		this._trigger('stop', null, { up: this.uploader });
643	},
644
645
646	/**
647	Enable browse button.
648
649	@method enable
650	*/
651	enable: function() {
652		this.browse_button.button('enable');
653		this.uploader.disableBrowse(false);
654	},
655
656
657	/**
658	Disable browse button.
659
660	@method disable
661	*/
662	disable: function() {
663		this.browse_button.button('disable');
664		this.uploader.disableBrowse(true);
665	},
666
667
668	/**
669	Retrieve file by it's unique id.
670
671	@method getFile
672	@param {String} id Unique id of the file
673	@return {plupload.File}
674	*/
675	getFile: function(id) {
676		var file;
677
678		if (typeof id === 'number') {
679			file = this.uploader.files[id];
680		} else {
681			file = this.uploader.getFile(id);
682		}
683		return file;
684	},
685
686	/**
687	Return array of files currently in the queue.
688
689	@method getFiles
690	@return {Array} Array of files in the queue represented by plupload.File objects
691	*/
692	getFiles: function() {
693		return this.uploader.files;
694	},
695
696
697	/**
698	Remove the file from the queue.
699
700	@method removeFile
701	@param {plupload.File|String} file File to remove, might be specified directly or by it's unique id
702	*/
703	removeFile: function(file) {
704		if (plupload.typeOf(file) === 'string') {
705			file = this.getFile(file);
706		}
707		this._removeFiles(file);
708	},
709
710
711	/**
712	Clear the file queue.
713
714	@method clearQueue
715	*/
716	clearQueue: function() {
717		this.uploader.splice();
718	},
719
720
721	/**
722	Retrieve internal plupload.Uploader object (usually not required).
723
724	@method getUploader
725	@return {plupload.Uploader}
726	*/
727	getUploader: function() {
728		return this.uploader;
729	},
730
731
732	/**
733	Trigger refresh procedure, specifically browse_button re-measure and re-position operations.
734	Might get handy, when UI Widget is placed within the popup, that is constantly hidden and shown
735	again - without calling this method after each show operation, dialog trigger might get displaced
736	and disfunctional.
737
738	@method refresh
739	*/
740	refresh: function() {
741		this.uploader.refresh();
742	},
743
744
745	/**
746	Display a message in notification area.
747
748	@method notify
749	@param {Enum} type Type of the message, either `error` or `info`
750	@param {String} message The text message to display.
751	*/
752	notify: function(type, message) {
753		var popup = $(
754			'<div class="plupload_message">' +
755				'<span class="plupload_message_close ui-icon ui-icon-circle-close" title="'+_('Close')+'"></span>' +
756				'<p><span class="ui-icon"></span>' + message + '</p>' +
757			'</div>'
758		);
759
760		popup
761			.addClass('ui-state-' + (type === 'error' ? 'error' : 'highlight'))
762			.find('p .ui-icon')
763				.addClass('ui-icon-' + (type === 'error' ? 'alert' : 'info'))
764				.end()
765			.find('.plupload_message_close')
766				.click(function() {
767					popup.remove();
768				})
769				.end();
770
771		$('.plupload_header', this.container).html(popup).show();
772		setTimeout(function(){
773			$('.plupload_header', this.container).hide().html('');
774		}, 3000);
775			//- $('.plupload_header', this.container).append(popup);
776	},
777
778
779	/**
780	Destroy the widget, the uploader, free associated resources and bring back original html.
781
782	@method destroy
783	*/
784	destroy: function() {
785		this._removeFiles([].slice.call(this.uploader.files));
786
787		// destroy uploader instance
788		this.uploader.destroy();
789
790		// unbind all button events
791		$('.plupload_button', this.element).unbind();
792
793		// destroy buttons
794		if ($.ui.button) {
795			$('.plupload_add, .plupload_start, .plupload_stop', this.container)
796				.button('destroy');
797		}
798
799		// destroy progressbar
800		if ($.ui.progressbar) {
801			this.progressbar.progressbar('destroy');
802		}
803
804		// destroy sortable behavior
805		if ($.ui.sortable && this.options.sortable) {
806			$('tbody', this.filelist).sortable('destroy');
807		}
808
809		// restore the elements initial state
810		this.element
811			.empty()
812			.html(this.contents_bak);
813		this.contents_bak = '';
814
815		$.Widget.prototype.destroy.apply(this);
816	},
817
818
819	_handleState: function() {
820		var up = this.uploader;
821
822		if (up.state === plupload.STARTED) {
823			$(this.start_button).button('disable');
824
825			$([])
826				.add(this.stop_button)
827				.add('.plupload_started')
828					.removeClass('plupload_hidden');
829
830			$('.plupload_upload_status', this.element).html(o.sprintf(_('Uploaded %d/%d files'), up.total.uploaded, up.files.length));
831			$('.plupload_header_content', this.element).addClass('plupload_header_content_bw');
832		} else if (up.state === plupload.STOPPED) {
833			$([])
834				.add(this.stop_button)
835				.add('.plupload_started')
836					.addClass('plupload_hidden');
837
838			if (this.options.multiple_queues) {
839				$('.plupload_header_content', this.element).removeClass('plupload_header_content_bw');
840			} else {
841				$([])
842					.add(this.browse_button)
843					.add(this.start_button)
844						.button('disable');
845
846				up.disableBrowse();
847			}
848
849			if (up.files.length === (up.total.uploaded + up.total.failed)) {
850				this.start_button.button('disable');
851			} else {
852				this.start_button.button('enable');
853			}
854
855			this._updateTotalProgress();
856		}
857
858		this._drawTotalCount();
859
860			//		var pic_unit = $('.pic_cnt', this.element).attr('data-unit');
861			//		var pic_cnt_max = $('.pic_cnt', this.element).attr('data-total');
862			//		var pic_byte_unit = $('.pic_byte', this.element).attr('data-unit');
863			//		var pic_byte_max = $('.pic_byte', this.element).attr('data-total');
864			//		if (up.total.queued === 0) {
865			//				/* $('.ui-button-text', this.browse_button).html(_('Add Files')); */
866			//
867			//			if($('.pic_cnt', this.element).length > 0)
868			//				$('.pic_cnt', this.element).html( ((this.options.old_file_count) +''+ pic_unit +' / '+ pic_cnt_max +pic_unit) );
869			//
870			//			if($('.pic_byte', this.element).length > 0)
871			//				$('.pic_byte', this.element).html( ((this.options.old_file_count) +''+ pic_byte_unit +' / '+ plupload.formatSize(pic_byte_max)) );
872			//		} else {
873			//				/* $('.ui-button-text', this.browse_button).html(o.sprintf(_('%d files queued'), up.total.queued)); */
874			//
875			//			if($('.pic_cnt', this.element).length > 0)
876			//				$('.pic_cnt', this.element).html( o.sprintf(_('%d'), (up.total.queued + this.options.old_file_count)) + (pic_unit+' / '+ pic_cnt_max +pic_unit) );
877			//
878			//			if($('.pic_byte', this.element).length > 0)
879			//				$('.pic_byte', this.element).html( plupload.formatSize(up.total.size) + (' / '+ plupload.formatSize(pic_byte_max)) );
880			//		}
881
882		up.refresh();
883
884		this.enable();
885	},
886
887	// Total File Count , Byte Redraw (2014-05-15 by GlauCos)
888	_drawTotalCount: function() {
889		var up = this.uploader;
890		var self = this;
891
892		var pic_unit = $('.pic_cnt', this.element).attr('data-unit');
893		var pic_cnt_max = $('.pic_cnt', this.element).attr('data-total');
894		var pic_byte_unit = $('.pic_byte', this.element).attr('data-unit');
895		var pic_byte_max = $('.pic_byte', this.element).attr('data-total');
896		self.options.old_file_count = parseInt($(this.element).attr('data-old-file-count'));
897
898
899		if (up.total.queued === 0) {
900				/* $('.ui-button-text', this.browse_button).html(_('Add Files')); */
901
902			if($('.pic_cnt', this.element).length > 0)
903				$('.pic_cnt', this.element).html( ((self.options.old_file_count) +''+ pic_unit +' / '+ pic_cnt_max +pic_unit) );
904
905			if($('.pic_byte', this.element).length > 0)
906				$('.pic_byte', this.element).html( ((self.options.old_file_count) +' '+ pic_byte_unit +' / '+ plupload.formatSize(pic_byte_max)) );
907		} else {
908				/* $('.ui-button-text', this.browse_button).html(o.sprintf(_('%d files queued'), up.total.queued)); */
909
910			if($('.pic_cnt', this.element).length > 0)
911				$('.pic_cnt', this.element).html( o.sprintf(_('%d'), (up.total.queued + self.options.old_file_count)) + (pic_unit+' / '+ pic_cnt_max +pic_unit) );
912
913			if($('.pic_byte', this.element).length > 0)
914				$('.pic_byte', this.element).html( plupload.formatSize(up.total.size) + (' / '+ plupload.formatSize(pic_byte_max)) );
915		}
916
917	},
918
919
920	_handleFileStatus: function(file) {
921		var self = this, actionClass, iconClass;
922
923		// since this method might be called asynchronously, file row might not yet be rendered
924		if (!$('#' + file.id).length) {
925			return;
926		}
927
928		// if(self.options.max_file_count && self.options.max_file_count > 0 && $('#'+file.id)> self.options.max_file_count
929
930		switch (file.status) {
931			case plupload.DONE:
932				actionClass = 'plupload_done';
933				iconClass = 'ui-icon ui-icon-circle-check';
934				break;
935
936			case plupload.FAILED:
937				actionClass = 'ui-state-error plupload_failed';
938				iconClass = 'ui-icon ui-icon-alert';
939				break;
940
941			case plupload.QUEUED:
942				actionClass = 'plupload_delete';
943				iconClass = 'ui-icon ui-icon-circle-minus';
944				break;
945
946			case plupload.UPLOADING:
947				actionClass = 'ui-state-highlight plupload_uploading';
948				iconClass = 'ui-icon ui-icon-circle-arrow-w';
949
950				// scroll uploading file into the view if its bottom boundary is out of it
951				var scroller = $('.plupload_scroll', this.container)
952				, scrollTop = scroller.scrollTop()
953				, scrollerHeight = scroller.height()
954				, rowOffset = $('#' + file.id).position().top + $('#' + file.id).height()
955				;
956
957				if (scrollerHeight < rowOffset) {
958					scroller.scrollTop(scrollTop + rowOffset - scrollerHeight);
959				}
960
961				// Set file specific progress
962					/*
963					$('#' + file.id)
964						.find('.plupload_file_percent')
965							.html(file.percent + '%')
966							.end()
967						.find('.plupload_file_progress')
968							.css('width', file.percent + '%')
969							.end()
970						.find('.plupload_file_size')
971							.html(plupload.formatSize(file.size));
972					*/
973				break;
974		}
975		actionClass += ' ui-state-default plupload_file plupload_file_add';
976
977		$('#' + file.id)
978			.attr('class', actionClass)
979			.find('.ui-icon')
980				.attr('class', iconClass)
981				.end()
982			.filter('.plupload_delete, .plupload_done, .plupload_failed')
983				.find('.ui-icon')
984					.click(function(e) {
985						self._removeFiles(file);
986						e.preventDefault();
987					});
988	},
989
990
991	_updateTotalProgress: function() {
992		var up = this.uploader;
993
994		// Scroll to end of file list
995		this.filelist[0].scrollTop = this.filelist[0].scrollHeight;
996
997		this.progressbar.progressbar('value', up.total.percent);
998
999		/*
1000		this.element
1001			.find('.plupload_total_status')
1002				.html(up.total.percent + '%')
1003				.end()
1004			.find('.plupload_total_file_size')
1005				.html(plupload.formatSize(up.total.size))
1006				.end()
1007			.find('.plupload_upload_status')
1008				.html(o.sprintf(_('Uploaded %d/%d files'), up.total.uploaded, up.files.length));
1009		*/
1010
1011		if(up.total.percent > 0) {
1012			if($('#id_layer_progress:visible').length < 1)
1013				$('#id_layer_progress').show();
1014
1015			$('#id_kh_progress_text').html(up.total.percent +'%');
1016			$('#id_kh_progress_bar').css('width', up.total.percent +'%');
1017			$('#id_kh_progress_status').html(o.sprintf(_('%d / %d'), up.total.uploaded, up.files.length));
1018
1019			if(up.total.percent > 99) {
1020				if($('#id_kh_progress_cancel:visible').length > 0) {
1021					$('#id_kh_
1021progress_cancel').hide();
1022					$('#id_kh_progress_loading').show();
1023					$('#id_kh_progress_title').html('파일을 처리하고 있습니다.');
1024				}
1025			} else {
1026				if($('#id_kh_progress_cancel:visible').length < 1)
1027					$('#id_kh_progress_loading').hide();
1028					$('#id_kh_progress_cancel').show();
1029				$('#id_kh_progress_title').html('파일을 업로드 하는 중입니다.');
1030			}
1031		}
1032
1033	},
1034
1035
1036	_displayThumbs: function() {
1037		var self = this
1038		, tw, th // thumb width/height
1039		, cols
1040		, num = 0 // number of simultaneously visible thumbs
1041		, thumbs = [] // array of thumbs to preload at any given moment
1042		, loading = false
1043		;
1044
1045		if (!this.options.views.thumbs) {
1046			return;
1047		}
1048
1049
1050		function onLast(el, eventName, cb) {
1051			var timer;
1052
1053			el.on(eventName, function() {
1054				clearTimeout(timer);
1055				timer = setTimeout(function() {
1056					clearTimeout(timer);
1057					cb();
1058				}, 300);
1059			});
1060		}
1061
1062
1063		// calculate number of simultaneously visible thumbs
1064		function measure() {
1065			if (!tw || !th) {
1066				var wrapper = $('.plupload_file:eq(0)', self.filelist);
1067				tw = wrapper.outerWidth(true);
1068				th = wrapper.outerHeight(true);
1069			}
1070
1071			var aw = self.content.width(), ah = self.content.height();
1072			cols = Math.floor(aw / tw);
1073			num =  cols * (Math.ceil(ah / th) + 1);
1074		}
1075
1076
1077		function pickThumbsToLoad() {
1078			// calculate index of virst visible thumb
1079			var startIdx = Math.floor(self.content.scrollTop() / th) * cols;
1080			// get potentially visible thumbs that are not yet visible
1081			thumbs = $('.plupload_file', self.filelist)
1082				.slice(startIdx, startIdx + num)
1083				.filter(':not(.plupload_file_thumb_loaded)')
1084				.get();
1085		}
1086
1087
1088		function init() {
1089			function mpl() {
1090				if (self.view_mode !== 'thumbs') {
1091					return;
1092				}
1093				measure();
1094				pickThumbsToLoad();
1095				lazyLoad();
1096			}
1097
1098			if ($.fn.resizable) {
1099				onLast(self.container, 'resize', mpl);
1100			}
1101
1102			onLast(self.window, 'resize', mpl);
1103			onLast(self.content, 'scroll',  mpl);
1104
1105			self.element.on('viewchanged selected', mpl);
1106
1107			mpl();
1108		}
1109
1110
1111		function preloadThumb(file, cb) {
1112			var img = new o.Image();
1113
1114			img.onload = function() {
1115				var thumb = $('#' + file.id + ' .plupload_file_thumb', self.filelist).html('');
1116				this.embed(thumb[0], {
1117					width: 110,
1118					height: 66,
1119					crop: true,
1120					swf_url: o.resolveUrl(self.options.flash_swf_url),
1121					xap_url: o.resolveUrl(self.options.silverlight_xap_url)
1122				});
1123			};
1124
1125			img.bind("embedded error", function() {
1126				$('#' + file.id, self.filelist).addClass('plupload_file_thumb_loaded');
1127				this.destroy();
1128				setTimeout(cb, 1); // detach, otherwise ui might hang (in SilverLight for example)
1129			});
1130
1131			try {
1132				img.load(file.getSource());
1133			} catch(e) {
1134
1135			}
1136		}
1137
1138
1139		function lazyLoad() {
1140			if (self.view_mode !== 'thumbs' || loading) {
1141				return;
1142			}
1143
1144			pickThumbsToLoad();
1145			if (!thumbs.length) {
1146				return;
1147			}
1148
1149			loading = true;
1150
1151			preloadThumb(self.getFile($(thumbs.shift()).attr('id')), function() {
1152				loading = false;
1153				lazyLoad();
1154			});
1155		}
1156
1157		// this has to run only once to measure structures and bind listeners
1158		this.element.on('selected', function onselected() {
1159			self.element.off('selected', onselected);
1160			init();
1161		});
1162	},
1163
1164
1165	_addFiles: function(files) {
1166		var self = this, file_html;
1167
1168		file_html = '<li class="plupload_file ui-state-default" id="%id%">' +
1169			'<div class="plupload_file_thumb">' +
1170				'<div class="plupload_file_dummy ui-widget-content"><span class="ui-state-disabled">%ext%</span></div>' +
1171			'</div>' +
1172			'<!--<div class="plupload_file_name" title="%name%"><span class="plupload_file_namespan">%name%</span></div>-->' +
1173			'<div class="plupload_file_action"><div class="ui-icon"> </div></div>' +
1174			'<!--<div class="plupload_file_size">%size% </div>' +
1175			'<div class="plupload_file_status">' +
1176				'<div class="plupload_file_progress ui-widget-header" style="width: 0%"> </div>' +
1177				'<span class="plupload_file_percent">%percent% </span>' +
1178			'</div>-->' +
1179			'<div class="plupload_file_fields"> </div>' +
1180		'</li>';
1181
1182		if (plupload.typeOf(files) !== 'array') {
1183			files = [files];
1184		}
1185
1186		//- if (plupload.typeOf(files) !== 'array') { self._addFileRow(0, files); } else
1187		{
1188			$.each(files, function(i, file) {
1189				var ext = o.Mime.getFileExtension(file.name) || 'none';
1190				//console.log(self.options.max_file_count+"--"+self.options.max_file_count+"--"+self.filelist.find('li.plupload_file_add').length+"--"+self.options.old_file_count+"--"+self.options.max_file_count);
1191				if(self.options.max_file_count && self.options.max_file_count > 0 && (self.filelist.find('li.plupload_file_add').length + self.options.old_file_count) < self.options.max_file_count) {
1192					self.filelist.append(file_html.replace(/%(\w+)%/g, function($0, $1) {
1193						if ('size' === $1) {
1194							return plupload.formatSize(file.size);
1195						} else if ('ext' === $1) {
1196							if(self.options.views.movie) {
1197								return '<img src="/img/20130529_btn_movie.png" border="0" width="130" height="95" />';
1198							} else if(self.options.views.thumbs) {
1199								return ext;
1200							} else {
1201								if($.inArray(ext, ['pptx','docx','xlsx'])) {
1202									ext = ext.substr(0,3);
1203									return '<img src="/img/icon_'+ ext +'.png" border="0" width="25" />';
1204								} else if($.inArray(ext, ['ppt','doc','xls','hwp','pdf','txt'])) {
1205									ext = ext.substr(0,3);
1206									return '<img src="/img/icon_'+ ext +'.png" border="0" width="25" />';
1207								} else {
1208									return '<img src="/img/icon_etc.png" border="0" width="25" />';
1209								}
1210							}
1211						} else {
1212							return file[$1] || '';
1213						}
1214					}));
1215				}
1216
1217				self._handleFileStatus(file);
1218			});
1219		}
1220		self.enable();
1221	},
1222
1223	// 개별 파일 추가할때를 대비해서 임시로 만들어놓음 (2014-05-19 by GlauCos)
1224	_addFileRow : function(i, file) {
1225		var self = this, file_html;
1226
1227		file_html = '<li class="plupload_file ui-state-default" id="%id%">' +
1228			'<div class="plupload_file_thumb">' +
1229				'<div class="plupload_file_dummy ui-widget-content"><span class="ui-state-disabled">%ext%</span></div>' +
1230			'</div>' +
1231			'<div class="plupload_file_name" title="%name%"><span class="plupload_file_namespan">%name%</span></div>' +
1232			'<div class="plupload_file_action"><div class="ui-icon"> </div></div>' +
1233			'<div class="plupload_file_size">
1233%size% </div>' +
1234			'<div class="plupload_file_status">' +
1235				'<div class="plupload_file_progress ui-widget-header" style="width: 0%"> </div>' +
1236				'<span class="plupload_file_percent">%percent% </span>' +
1237			'</div>' +
1238			'<div class="plupload_file_fields"> </div>' +
1239		'</li>';
1240
1241		var ext = o.Mime.getFileExtension(file.name) || 'none';
1242
1243		if(self.options.max_file_count && self.options.max_file_count > 0 && (self.filelist.find('li.plupload_file').length + self.options.old_file_count) < self.options.max_file_count)
1244		{
1245			self.filelist.append(file_html.replace(/%(\w+)%/g, function($0, $1) {
1246				if ('size' === $1) {
1247					return plupload.formatSize(file.size);
1248				} else if ('ext' === $1) {
1249					if(self.options.views.movie) {
1250						return '<img src="/img/20130529_btn_movie.png" border="0" width="130" height="95" />';
1251					} else if(self.options.views.thumbs) {
1252						return ext;
1253					} else {
1254						if($.inArray(ext, ['pptx','docx','xlsx'])) {
1255							ext = ext.substr(0,3);
1256							return '<img src="/img/icon_'+ ext +'.png" border="0" width="25" />';
1257						} else if($.inArray(ext, ['ppt','doc','xls','hwp','pdf','txt'])) {
1258							ext = ext.substr(0,3);
1259							return '<img src="/img/icon_'+ ext +'.png" border="0" width="25" />';
1260						} else {
1261							return '<img src="/img/icon_etc.png" border="0" width="25" />';
1262						}
1263					}
1264				} else {
1265					return file[$1] || '';
1266				}
1267			}));
1268		}
1269
1270		self._handleFileStatus(file);
1271	},
1272
1273
1274	_removeFiles: function(files) {
1275		var self = this, up = this.uploader;
1276
1277		if (plupload.typeOf(files) !== 'array') {
1278			files = [files];
1279		}
1280
1281		// destroy sortable if enabled
1282		if ($.ui.sortable && this.options.sortable) {
1283			$('tbody', self.filelist).sortable('destroy');
1284		}
1285
1286		$.each(files, function(i, file) {
1287			if($('#' + file.id).length) {
1288				$('#' + file.id).remove();
1289			}
1290			up.removeFile(file);
1291		});
1292
1293
1294		if (up.files.length) {
1295			// re-initialize sortable
1296			if (this.options.sortable && $.ui.sortable) {
1297				this._enableSortingList();
1298			}
1299		}
1300
1301		this._trigger('updatelist', null, { filelist: this.filelist });
1302	},
1303
1304
1305	_addFormFields: function() {
1306		var self = this;
1307
1308		// re-add from fresh
1309		$('.plupload_file_fields', this.filelist).html('');
1310
1311		plupload.each(this.uploader.files, function(file, count) {
1312			var fields = ''
1313			, id = self.id + '_' + count
1314			;
1315
1316			if (file.target_name) {
1317				fields += '<input type="hidden" name="' + id + '_tmpname" value="'+plupload.xmlEncode(file.target_name)+'" />';
1318			}
1319			fields += '<input type="hidden" name="' + id + '_name" value="'+plupload.xmlEncode(file.name)+'" />';
1320			fields += '<input type="hidden" name="' + id + '_status" value="' + (file.status === plupload.DONE ? 'done' : 'failed') + '" />';
1321
1322			$('#' + file.id).find('.plupload_file_fields').html(fields);
1323		});
1324
1325		this.counter.val(this.uploader.files.length);
1326	},
1327
1328
1329	_viewChanged: function(view) {
1330		// update or write a new cookie
1331		if (this.options.views.remember && $.cookie) {
1332			$.cookie('plupload_ui_view', view, { expires: 7, path: '/' });
1333		}
1334
1335		// ugly fix for IE6 - make content area stretchable
1336		if (o.Env.browser === 'IE' && o.Env.version < 7) {
1337			this.content.attr('style', 'height:expression(document.getElementById("' + this.id + '_container' + '").clientHeight - ' + (view === 'list' ? 133 : 103) + ');');
1338		}
1339
1340		this.container.removeClass('plupload_view_list plupload_view_thumbs').addClass('plupload_view_' + view);
1341		this.view_mode = view;
1342		this._trigger('viewchanged', null, { view: view });
1343	},
1344
1345
1346	_enableViewSwitcher: function() {
1347		var self = this
1348		, view
1349		, switcher = $('.plupload_view_switch', this.container)
1350		, buttons
1351		, button
1352		;
1353
1354		plupload.each(['list', 'thumbs'], function(view) {
1355			if (!self.options.views[view]) {
1356				switcher.find('[for="' + self.id + '_view_' + view + '"], #'+ self.id +'_view_' + view).remove();
1357			}
1358		});
1359
1360		// check if any visible left
1361		buttons = switcher.find('.plupload_button');
1362
1363		if (buttons.length === 1) {
1364			switcher.hide();
1365			view = buttons.eq(0).data('view');
1366			this._viewChanged(view);
1367		} else if ($.ui.button && buttons.length > 1) {
1368			if (this.options.views.remember && $.cookie) {
1369				view = $.cookie('plupload_ui_view');
1370			}
1371
1372			// if wierd case, bail out to default
1373			if (!~plupload.inArray(view, ['list', 'thumbs'])) {
1374				view = this.options.views.active;
1375			}
1376
1377			switcher
1378				.show()
1379				.buttonset()
1380				.find('.ui-button')
1381					.click(function(e) {
1382						view = $(this).data('view');
1383						self._viewChanged(view);
1384						e.preventDefault(); // avoid auto scrolling to widget in IE and FF (see #850)
1385					});
1386
1387			// if view not active - happens when switcher wasn't clicked manually
1388			button = switcher.find('[for="' + self.id + '_view_'+view+'"]');
1389			if (button.length) {
1390				button.trigger('click');
1391			}
1392		} else {
1393			switcher.show();
1394			this._viewChanged(this.options.views.active);
1395		}
1396
1397		// initialize thumb viewer if requested
1398		if (this.options.views.thumbs) {
1399			this._displayThumbs();
1400		}
1401	},
1402
1403
1404	_enableRenaming: function() {
1405		var self = this;
1406
1407		this.filelist.dblclick(function(e) {
1408			var nameSpan = $(e.target), nameInput, file, parts, name, ext = "";
1409
1410			if (!nameSpan.hasClass('plupload_file_namespan')) {
1411				return;
1412			}
1413
1414			// Get file name and split out name and extension
1415			file = self.uploader.getFile(nameSpan.closest('.plupload_file')[0].id);
1416			name = file.name;
1417			parts = /^(.+)(\.[^.]+)$/.exec(name);
1418			if (parts) {
1419				name = parts[1];
1420				ext = parts[2];
1421			}
1422
1423			// Display input element
1424			nameInput = $('<input class="plupload_file_rename" type="text" />').width(nameSpan.width()).insertAfter(nameSpan.hide());
1425			nameInput.val(name).blur(function() {
1426				nameSpan.show().parent().scrollLeft(0).end().next().remove();
1427			}).keydown(function(e) {
1428				var nameInput = $(this);
1429
1430				if ($.inArray(e.keyCode, [13, 27]) !== -1) {
1431					e.preventDefault();
1432
1433					// Rename file and glue extension back on
1434					if (e.keyCode === 13) {
1435						file.name = nameInput.val() + ext;
1436						nameSpan.html(file.name);
1437					}
1438					nameInput.blur();
1439				}
1440			})[0].focus();
1441		});
1442	},
1443
1444
1445	_enableSortingList: function() {
1446		var self = this;
1447
1448		if ($('.plupload_file', this.filelist).length < 1) {
1449			return;
1450		}
1451
1452		// destroy sortable if enabled
1453		$('tbody', this.filelist).sortable('destroy');
1454
1455		// enable
1456		this.filelist.sortable({
1457			items: '.plupload_delete',
1458
1459			cancel: 'object, .plupload_clearer',
1460
1461			stop: function() {
1462				var files = [];
1463
1464				$.each($(this).sortable('toArray'), function(i, id) {
1465					files[files.length] = self.uploader.getFile(id);
1466				});
1467
1468				files.unshift(files.length);
1469				files.unshift(0);
1470
1471				// re-populate files array
1472				Array.prototype.splice.apply(self.uploader.files, files);
1473			}
1474		});
1475	}
1476});
1477
1478} (window, document, plupload, mOxie, jQuery));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.