2380 lines
		
	
	
	
		
			59 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			2380 lines
		
	
	
	
		
			59 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | /** | ||
|  |  * Plupload - multi-runtime File Uploader | ||
|  |  * v2.1.9 | ||
|  |  * | ||
|  |  * Copyright 2013, Moxiecode Systems AB | ||
|  |  * Released under GPL License. | ||
|  |  * | ||
|  |  * License: http://www.plupload.com/license
 | ||
|  |  * Contributing: http://www.plupload.com/contributing
 | ||
|  |  * | ||
|  |  * Date: 2016-05-15 | ||
|  |  */ | ||
|  | /** | ||
|  |  * Plupload.js | ||
|  |  * | ||
|  |  * Copyright 2013, Moxiecode Systems AB | ||
|  |  * Released under GPL License. | ||
|  |  * | ||
|  |  * License: http://www.plupload.com/license
 | ||
|  |  * Contributing: http://www.plupload.com/contributing
 | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * Modified for WordPress, Silverlight and Flash runtimes support was removed. | ||
|  |  * See https://core.trac.wordpress.org/ticket/41755.
 | ||
|  |  */ | ||
|  | 
 | ||
|  | /*global mOxie:true */ | ||
|  | 
 | ||
|  | ;(function(window, o, undef) { | ||
|  | 
 | ||
|  | var delay = window.setTimeout | ||
|  | , fileFilters = {} | ||
|  | ; | ||
|  | 
 | ||
|  | // convert plupload features to caps acceptable by mOxie
 | ||
|  | function normalizeCaps(settings) {		 | ||
|  | 	var features = settings.required_features, caps = {}; | ||
|  | 
 | ||
|  | 	function resolve(feature, value, strict) { | ||
|  | 		// Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
 | ||
|  | 		var map = {  | ||
|  | 			chunks: 'slice_blob', | ||
|  | 			jpgresize: 'send_binary_string', | ||
|  | 			pngresize: 'send_binary_string', | ||
|  | 			progress: 'report_upload_progress', | ||
|  | 			multi_selection: 'select_multiple', | ||
|  | 			dragdrop: 'drag_and_drop', | ||
|  | 			drop_element: 'drag_and_drop', | ||
|  | 			headers: 'send_custom_headers', | ||
|  | 			urlstream_upload: 'send_binary_string', | ||
|  | 			canSendBinary: 'send_binary', | ||
|  | 			triggerDialog: 'summon_file_dialog' | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		if (map[feature]) { | ||
|  | 			caps[map[feature]] = value; | ||
|  | 		} else if (!strict) { | ||
|  | 			caps[feature] = value; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (typeof(features) === 'string') { | ||
|  | 		plupload.each(features.split(/\s*,\s*/), function(feature) { | ||
|  | 			resolve(feature, true); | ||
|  | 		}); | ||
|  | 	} else if (typeof(features) === 'object') { | ||
|  | 		plupload.each(features, function(value, feature) { | ||
|  | 			resolve(feature, value); | ||
|  | 		}); | ||
|  | 	} else if (features === true) { | ||
|  | 		// check settings for required features
 | ||
|  | 		if (settings.chunk_size > 0) { | ||
|  | 			caps.slice_blob = true; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (settings.resize.enabled || !settings.multipart) { | ||
|  | 			caps.send_binary_string = true; | ||
|  | 		} | ||
|  | 		 | ||
|  | 		plupload.each(settings, function(value, feature) { | ||
|  | 			resolve(feature, !!value, true); // strict check
 | ||
|  | 		}); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// WP: only html runtimes.
 | ||
|  | 	settings.runtimes = 'html5,html4'; | ||
|  | 
 | ||
|  | 	return caps; | ||
|  | } | ||
|  | 
 | ||
|  | /**  | ||
|  |  * @module plupload	 | ||
|  |  * @static | ||
|  |  */ | ||
|  | var plupload = { | ||
|  | 	/** | ||
|  | 	 * Plupload version will be replaced on build. | ||
|  | 	 * | ||
|  | 	 * @property VERSION | ||
|  | 	 * @for Plupload | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	VERSION : '2.1.9', | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * The state of the queue before it has started and after it has finished | ||
|  | 	 * | ||
|  | 	 * @property STOPPED | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	STOPPED : 1, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Upload process is running | ||
|  | 	 * | ||
|  | 	 * @property STARTED | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	STARTED : 2, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * File is queued for upload | ||
|  | 	 * | ||
|  | 	 * @property QUEUED | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	QUEUED : 1, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * File is being uploaded | ||
|  | 	 * | ||
|  | 	 * @property UPLOADING | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	UPLOADING : 2, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * File has failed to be uploaded | ||
|  | 	 * | ||
|  | 	 * @property FAILED | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	FAILED : 4, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * File has been uploaded successfully | ||
|  | 	 * | ||
|  | 	 * @property DONE | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	DONE : 5, | ||
|  | 
 | ||
|  | 	// Error constants used by the Error event
 | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Generic error for example if an exception is thrown inside Silverlight. | ||
|  | 	 * | ||
|  | 	 * @property GENERIC_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	GENERIC_ERROR : -100, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * HTTP transport error. For example if the server produces a HTTP status other than 200. | ||
|  | 	 * | ||
|  | 	 * @property HTTP_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	HTTP_ERROR : -200, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Generic I/O error. For example if it wasn't possible to open the file stream on local machine. | ||
|  | 	 * | ||
|  | 	 * @property IO_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	IO_ERROR : -300, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @property SECURITY_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	SECURITY_ERROR : -400, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Initialization error. Will be triggered if no runtime was initialized. | ||
|  | 	 * | ||
|  | 	 * @property INIT_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	INIT_ERROR : -500, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered. | ||
|  | 	 * | ||
|  | 	 * @property FILE_SIZE_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	FILE_SIZE_ERROR : -600, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * File extension error. If the user selects a file that isn't valid according to the filters setting. | ||
|  | 	 * | ||
|  | 	 * @property FILE_EXTENSION_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	FILE_EXTENSION_ERROR : -601, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again. | ||
|  | 	 * | ||
|  | 	 * @property FILE_DUPLICATE_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	FILE_DUPLICATE_ERROR : -602, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Runtime will try to detect if image is proper one. Otherwise will throw this error. | ||
|  | 	 * | ||
|  | 	 * @property IMAGE_FORMAT_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	IMAGE_FORMAT_ERROR : -700, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * While working on files runtime may run out of memory and will throw this error. | ||
|  | 	 * | ||
|  | 	 * @since 2.1.2 | ||
|  | 	 * @property MEMORY_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	MEMORY_ERROR : -701, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error. | ||
|  | 	 * | ||
|  | 	 * @property IMAGE_DIMENSIONS_ERROR | ||
|  | 	 * @static | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	IMAGE_DIMENSIONS_ERROR : -702, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Mime type lookup table. | ||
|  | 	 * | ||
|  | 	 * @property mimeTypes | ||
|  | 	 * @type Object | ||
|  | 	 * @final | ||
|  | 	 */ | ||
|  | 	mimeTypes : o.mimes, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * In some cases sniffing is the only way around :( | ||
|  | 	 */ | ||
|  | 	ua: o.ua, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Gets the true type of the built-in object (better version of typeof). | ||
|  | 	 * @credits Angus Croll (http://javascriptweblog.wordpress.com/)
 | ||
|  | 	 * | ||
|  | 	 * @method typeOf | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} o Object to check. | ||
|  | 	 * @return {String} Object [[Class]] | ||
|  | 	 */ | ||
|  | 	typeOf: o.typeOf, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Extends the specified object with another object. | ||
|  | 	 * | ||
|  | 	 * @method extend | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} target Object to extend. | ||
|  | 	 * @param {Object..} obj Multiple objects to extend with. | ||
|  | 	 * @return {Object} Same as target, the extended object. | ||
|  | 	 */ | ||
|  | 	extend : o.extend, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers. | ||
|  | 	 * The only way a user would be able to get the same ID is if the two persons at the same exact millisecond manages | ||
|  | 	 * to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique. | ||
|  | 	 * It's more probable for the earth to be hit with an asteriod. You can also if you want to be 100% sure set the plupload.guidPrefix property | ||
|  | 	 * to an user unique key. | ||
|  | 	 * | ||
|  | 	 * @method guid | ||
|  | 	 * @static | ||
|  | 	 * @return {String} Virtually unique id. | ||
|  | 	 */ | ||
|  | 	guid : o.guid, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Get array of DOM Elements by their ids. | ||
|  | 	 *  | ||
|  | 	 * @method get | ||
|  | 	 * @param {String} id Identifier of the DOM Element | ||
|  | 	 * @return {Array} | ||
|  | 	*/ | ||
|  | 	getAll : function get(ids) { | ||
|  | 		var els = [], el; | ||
|  | 
 | ||
|  | 		if (plupload.typeOf(ids) !== 'array') { | ||
|  | 			ids = [ids]; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var i = ids.length; | ||
|  | 		while (i--) { | ||
|  | 			el = plupload.get(ids[i]); | ||
|  | 			if (el) { | ||
|  | 				els.push(el); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return els.length ? els : null; | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Get DOM element by id | ||
|  | 
 | ||
|  | 	@method get | ||
|  | 	@param {String} id Identifier of the DOM Element | ||
|  | 	@return {Node} | ||
|  | 	*/ | ||
|  | 	get: o.get, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Executes the callback function for each item in array/object. If you return false in the | ||
|  | 	 * callback it will break the loop. | ||
|  | 	 * | ||
|  | 	 * @method each | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj Object to iterate. | ||
|  | 	 * @param {function} callback Callback function to execute for each item. | ||
|  | 	 */ | ||
|  | 	each : o.each, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields. | ||
|  | 	 * | ||
|  | 	 * @method getPos | ||
|  | 	 * @static | ||
|  | 	 * @param {Element} node HTML element or element id to get x, y position from. | ||
|  | 	 * @param {Element} root Optional root element to stop calculations at. | ||
|  | 	 * @return {object} Absolute position of the specified element object with x, y fields. | ||
|  | 	 */ | ||
|  | 	getPos : o.getPos, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Returns the size of the specified node in pixels. | ||
|  | 	 * | ||
|  | 	 * @method getSize | ||
|  | 	 * @static | ||
|  | 	 * @param {Node} node Node to get the size of. | ||
|  | 	 * @return {Object} Object with a w and h property. | ||
|  | 	 */ | ||
|  | 	getSize : o.getSize, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Encodes the specified string. | ||
|  | 	 * | ||
|  | 	 * @method xmlEncode | ||
|  | 	 * @static | ||
|  | 	 * @param {String} s String to encode. | ||
|  | 	 * @return {String} Encoded string. | ||
|  | 	 */ | ||
|  | 	xmlEncode : function(str) { | ||
|  | 		var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g; | ||
|  | 
 | ||
|  | 		return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) { | ||
|  | 			return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr; | ||
|  | 		}) : str; | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Forces anything into an array. | ||
|  | 	 * | ||
|  | 	 * @method toArray | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj Object with length field. | ||
|  | 	 * @return {Array} Array object containing all items. | ||
|  | 	 */ | ||
|  | 	toArray : o.toArray, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Find an element in array and return its index if present, otherwise return -1. | ||
|  | 	 * | ||
|  | 	 * @method inArray | ||
|  | 	 * @static | ||
|  | 	 * @param {mixed} needle Element to find | ||
|  | 	 * @param {Array} array | ||
|  | 	 * @return {Int} Index of the element, or -1 if not found | ||
|  | 	 */ | ||
|  | 	inArray : o.inArray, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Extends the language pack object with new items. | ||
|  | 	 * | ||
|  | 	 * @method addI18n | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} pack Language pack items to add. | ||
|  | 	 * @return {Object} Extended language pack object. | ||
|  | 	 */ | ||
|  | 	addI18n : o.addI18n, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Translates the specified string by checking for the english string in the language pack lookup. | ||
|  | 	 * | ||
|  | 	 * @method translate | ||
|  | 	 * @static | ||
|  | 	 * @param {String} str String to look for. | ||
|  | 	 * @return {String} Translated string or the input string if it wasn't found. | ||
|  | 	 */ | ||
|  | 	translate : o.translate, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Checks if object is empty. | ||
|  | 	 * | ||
|  | 	 * @method isEmptyObj | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj Object to check. | ||
|  | 	 * @return {Boolean} | ||
|  | 	 */ | ||
|  | 	isEmptyObj : o.isEmptyObj, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Checks if specified DOM element has specified class. | ||
|  | 	 * | ||
|  | 	 * @method hasClass | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj DOM element like object to add handler to. | ||
|  | 	 * @param {String} name Class name | ||
|  | 	 */ | ||
|  | 	hasClass : o.hasClass, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Adds specified className to specified DOM element. | ||
|  | 	 * | ||
|  | 	 * @method addClass | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj DOM element like object to add handler to. | ||
|  | 	 * @param {String} name Class name | ||
|  | 	 */ | ||
|  | 	addClass : o.addClass, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Removes specified className from specified DOM element. | ||
|  | 	 * | ||
|  | 	 * @method removeClass | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj DOM element like object to add handler to. | ||
|  | 	 * @param {String} name Class name | ||
|  | 	 */ | ||
|  | 	removeClass : o.removeClass, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Returns a given computed style of a DOM element. | ||
|  | 	 * | ||
|  | 	 * @method getStyle | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj DOM element like object. | ||
|  | 	 * @param {String} name Style you want to get from the DOM element | ||
|  | 	 */ | ||
|  | 	getStyle : o.getStyle, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Adds an event handler to the specified object and store reference to the handler | ||
|  | 	 * in objects internal Plupload registry (@see removeEvent). | ||
|  | 	 * | ||
|  | 	 * @method addEvent | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj DOM element like object to add handler to. | ||
|  | 	 * @param {String} name Name to add event listener to. | ||
|  | 	 * @param {Function} callback Function to call when event occurs. | ||
|  | 	 * @param {String} (optional) key that might be used to add specifity to the event record. | ||
|  | 	 */ | ||
|  | 	addEvent : o.addEvent, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Remove event handler from the specified object. If third argument (callback) | ||
|  | 	 * is not specified remove all events with the specified name. | ||
|  | 	 * | ||
|  | 	 * @method removeEvent | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj DOM element to remove event listener(s) from. | ||
|  | 	 * @param {String} name Name of event listener to remove. | ||
|  | 	 * @param {Function|String} (optional) might be a callback or unique key to match. | ||
|  | 	 */ | ||
|  | 	removeEvent: o.removeEvent, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Remove all kind of events from the specified object | ||
|  | 	 * | ||
|  | 	 * @method removeAllEvents | ||
|  | 	 * @static | ||
|  | 	 * @param {Object} obj DOM element to remove event listeners from. | ||
|  | 	 * @param {String} (optional) unique key to match, when removing events. | ||
|  | 	 */ | ||
|  | 	removeAllEvents: o.removeAllEvents, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _. | ||
|  | 	 * | ||
|  | 	 * @method cleanName | ||
|  | 	 * @static | ||
|  | 	 * @param {String} s String to clean up. | ||
|  | 	 * @return {String} Cleaned string. | ||
|  | 	 */ | ||
|  | 	cleanName : function(name) { | ||
|  | 		var i, lookup; | ||
|  | 
 | ||
|  | 		// Replace diacritics
 | ||
|  | 		lookup = [ | ||
|  | 			/[\300-\306]/g, 'A', /[\340-\346]/g, 'a', | ||
|  | 			/\307/g, 'C', /\347/g, 'c', | ||
|  | 			/[\310-\313]/g, 'E', /[\350-\353]/g, 'e', | ||
|  | 			/[\314-\317]/g, 'I', /[\354-\357]/g, 'i', | ||
|  | 			/\321/g, 'N', /\361/g, 'n', | ||
|  | 			/[\322-\330]/g, 'O', /[\362-\370]/g, 'o', | ||
|  | 			/[\331-\334]/g, 'U', /[\371-\374]/g, 'u' | ||
|  | 		]; | ||
|  | 
 | ||
|  | 		for (i = 0; i < lookup.length; i += 2) { | ||
|  | 			name = name.replace(lookup[i], lookup[i + 1]); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Replace whitespace
 | ||
|  | 		name = name.replace(/\s+/g, '_'); | ||
|  | 
 | ||
|  | 		// Remove anything else
 | ||
|  | 		name = name.replace(/[^a-z0-9_\-\.]+/gi, ''); | ||
|  | 
 | ||
|  | 		return name; | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Builds a full url out of a base URL and an object with items to append as query string items. | ||
|  | 	 * | ||
|  | 	 * @method buildUrl | ||
|  | 	 * @static | ||
|  | 	 * @param {String} url Base URL to append query string items to. | ||
|  | 	 * @param {Object} items Name/value object to serialize as a querystring. | ||
|  | 	 * @return {String} String with url + serialized query string items. | ||
|  | 	 */ | ||
|  | 	buildUrl : function(url, items) { | ||
|  | 		var query = ''; | ||
|  | 
 | ||
|  | 		plupload.each(items, function(value, name) { | ||
|  | 			query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value); | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		if (query) { | ||
|  | 			url += (url.indexOf('?') > 0 ? '&' : '?') + query; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return url; | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Formats the specified number as a size string for example 1024 becomes 1 KB. | ||
|  | 	 * | ||
|  | 	 * @method formatSize | ||
|  | 	 * @static | ||
|  | 	 * @param {Number} size Size to format as string. | ||
|  | 	 * @return {String} Formatted size string. | ||
|  | 	 */ | ||
|  | 	formatSize : function(size) { | ||
|  | 
 | ||
|  | 		if (size === undef || /\D/.test(size)) { | ||
|  | 			return plupload.translate('N/A'); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		function round(num, precision) { | ||
|  | 			return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var boundary = Math.pow(1024, 4); | ||
|  | 
 | ||
|  | 		// TB
 | ||
|  | 		if (size > boundary) { | ||
|  | 			return round(size / boundary, 1) + " " + plupload.translate('tb'); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// GB
 | ||
|  | 		if (size > (boundary/=1024)) { | ||
|  | 			return round(size / boundary, 1) + " " + plupload.translate('gb'); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// MB
 | ||
|  | 		if (size > (boundary/=1024)) { | ||
|  | 			return round(size / boundary, 1) + " " + plupload.translate('mb'); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// KB
 | ||
|  | 		if (size > 1024) { | ||
|  | 			return Math.round(size / 1024) + " " + plupload.translate('kb'); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return size + " " + plupload.translate('b'); | ||
|  | 	}, | ||
|  | 
 | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Parses the specified size string into a byte value. For example 10kb becomes 10240. | ||
|  | 	 * | ||
|  | 	 * @method parseSize | ||
|  | 	 * @static | ||
|  | 	 * @param {String|Number} size String to parse or number to just pass through. | ||
|  | 	 * @return {Number} Size in bytes. | ||
|  | 	 */ | ||
|  | 	parseSize : o.parseSizeStr, | ||
|  | 
 | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * A way to predict what runtime will be choosen in the current environment with the | ||
|  | 	 * specified settings. | ||
|  | 	 * | ||
|  | 	 * @method predictRuntime | ||
|  | 	 * @static | ||
|  | 	 * @param {Object|String} config Plupload settings to check | ||
|  | 	 * @param {String} [runtimes] Comma-separated list of runtimes to check against | ||
|  | 	 * @return {String} Type of compatible runtime | ||
|  | 	 */ | ||
|  | 	predictRuntime : function(config, runtimes) { | ||
|  | 		var up, runtime; | ||
|  | 
 | ||
|  | 		up = new plupload.Uploader(config); | ||
|  | 		runtime = o.Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes); | ||
|  | 		up.destroy(); | ||
|  | 		return runtime; | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Registers a filter that will be executed for each file added to the queue. | ||
|  | 	 * If callback returns false, file will not be added. | ||
|  | 	 * | ||
|  | 	 * Callback receives two arguments: a value for the filter as it was specified in settings.filters | ||
|  | 	 * and a file to be filtered. Callback is executed in the context of uploader instance. | ||
|  | 	 * | ||
|  | 	 * @method addFileFilter | ||
|  | 	 * @static | ||
|  | 	 * @param {String} name Name of the filter by which it can be referenced in settings.filters | ||
|  | 	 * @param {String} cb Callback - the actual routine that every added file must pass | ||
|  | 	 */ | ||
|  | 	addFileFilter: function(name, cb) { | ||
|  | 		fileFilters[name] = cb; | ||
|  | 	} | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | plupload.addFileFilter('mime_types', function(filters, file, cb) { | ||
|  | 	if (filters.length && !filters.regexp.test(file.name)) { | ||
|  | 		this.trigger('Error', { | ||
|  | 			code : plupload.FILE_EXTENSION_ERROR, | ||
|  | 			message : plupload.translate('File extension error.'), | ||
|  | 			file : file | ||
|  | 		}); | ||
|  | 		cb(false); | ||
|  | 	} else { | ||
|  | 		cb(true); | ||
|  | 	} | ||
|  | }); | ||
|  | 
 | ||
|  | 
 | ||
|  | plupload.addFileFilter('max_file_size', function(maxSize, file, cb) { | ||
|  | 	var undef; | ||
|  | 
 | ||
|  | 	maxSize = plupload.parseSize(maxSize); | ||
|  | 
 | ||
|  | 	// Invalid file size
 | ||
|  | 	if (file.size !== undef && maxSize && file.size > maxSize) { | ||
|  | 		this.trigger('Error', { | ||
|  | 			code : plupload.FILE_SIZE_ERROR, | ||
|  | 			message : plupload.translate('File size error.'), | ||
|  | 			file : file | ||
|  | 		}); | ||
|  | 		cb(false); | ||
|  | 	} else { | ||
|  | 		cb(true); | ||
|  | 	} | ||
|  | }); | ||
|  | 
 | ||
|  | 
 | ||
|  | plupload.addFileFilter('prevent_duplicates', function(value, file, cb) { | ||
|  | 	if (value) { | ||
|  | 		var ii = this.files.length; | ||
|  | 		while (ii--) { | ||
|  | 			// Compare by name and size (size might be 0 or undefined, but still equivalent for both)
 | ||
|  | 			if (file.name === this.files[ii].name && file.size === this.files[ii].size) { | ||
|  | 				this.trigger('Error', { | ||
|  | 					code : plupload.FILE_DUPLICATE_ERROR, | ||
|  | 					message : plupload.translate('Duplicate file error.'), | ||
|  | 					file : file | ||
|  | 				}); | ||
|  | 				cb(false); | ||
|  | 				return; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	cb(true); | ||
|  | }); | ||
|  | 
 | ||
|  | 
 | ||
|  | /** | ||
|  | @class Uploader | ||
|  | @constructor | ||
|  | 
 | ||
|  | @param {Object} settings For detailed information about each option check documentation. | ||
|  | 	@param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger. | ||
|  | 	@param {String} settings.url URL of the server-side upload handler. | ||
|  | 	@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. | ||
|  | 	@param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes. | ||
|  | 	@param {String|DOMElement} [settings.container] id of the DOM element or DOM element itself that will be used to wrap uploader structures. Defaults to immediate parent of the `browse_button` element. | ||
|  | 	@param {String|DOMElement} [settings.drop_element] id of the DOM element or DOM element itself to use as a drop zone for Drag-n-Drop. | ||
|  | 	@param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message. | ||
|  | 	@param {Object} [settings.filters={}] Set of file type filters. | ||
|  | 		@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` | ||
|  | 		@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`. | ||
|  | 		@param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`. | ||
|  | 	@param {String} [settings.flash_swf_url] URL of the Flash swf. (Not used in WordPress) | ||
|  | 	@param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs. | ||
|  | 	@param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event. | ||
|  | 	@param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message. | ||
|  | 	@param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload. | ||
|  | 	@param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog. | ||
|  | 	@param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess. | ||
|  | 	@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}` | ||
|  | 		@param {Number} [settings.resize.width] If image is bigger, it will be resized. | ||
|  | 		@param {Number} [settings.resize.height] If image is bigger, it will be resized. | ||
|  | 		@param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100). | ||
|  | 		@param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally. | ||
|  | 	@param {String} [settings.runtimes="html5,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails. | ||
|  | 	@param {String} [settings.silverlight_xap_url] URL of the Silverlight xap. (Not used in WordPress) | ||
|  | 	@param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files. | ||
|  | 	@param {Boolean} [settings.send_file_name=true] Whether to send file name as additional argument - 'name' (required for chunked uploads and some other cases where file name cannot be sent via normal ways). | ||
|  | */ | ||
|  | plupload.Uploader = function(options) { | ||
|  | 	/** | ||
|  | 	Fires when the current RunTime has been initialized. | ||
|  | 	 | ||
|  | 	@event Init | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires after the init event incase you need to perform actions there. | ||
|  | 	 | ||
|  | 	@event PostInit | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when the option is changed in via uploader.setOption(). | ||
|  | 	 | ||
|  | 	@event OptionChanged | ||
|  | 	@since 2.1 | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {String} name Name of the option that was changed | ||
|  | 	@param {Mixed} value New value for the specified option | ||
|  | 	@param {Mixed} oldValue Previous value of the option | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when the silverlight/flash or other shim needs to move. | ||
|  | 	 | ||
|  | 	@event Refresh | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when the overall state is being changed for the upload queue. | ||
|  | 	 | ||
|  | 	@event StateChanged | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when browse_button is clicked and browse dialog shows. | ||
|  | 	 | ||
|  | 	@event Browse | ||
|  | 	@since 2.1.2 | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	 */	 | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires for every filtered file before it is added to the queue. | ||
|  | 	 | ||
|  | 	@event FileFiltered | ||
|  | 	@since 2.1 | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {plupload.File} file Another file that has to be added to the queue. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance. | ||
|  | 	 | ||
|  | 	@event QueueChanged | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	 */  | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires after files were filtered and added to the queue. | ||
|  | 	 | ||
|  | 	@event FilesAdded | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {Array} files Array of file objects that were added to queue by the user. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when file is removed from the queue. | ||
|  | 	 | ||
|  | 	@event FilesRemoved | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {Array} files Array of files that got removed. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires just before a file is uploaded. Can be used to cancel the upload for the specified file | ||
|  | 	by returning false from the handler. | ||
|  | 	 | ||
|  | 	@event BeforeUpload | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {plupload.File} file File to be uploaded. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when a file is to be uploaded by the runtime. | ||
|  | 	 | ||
|  | 	@event UploadFile | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {plupload.File} file File to be uploaded. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires while a file is being uploaded. Use this event to update the current file upload progress. | ||
|  | 	 | ||
|  | 	@event UploadProgress | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {plupload.File} file File that is currently being uploaded. | ||
|  | 	 */	 | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when file chunk is uploaded. | ||
|  | 	 | ||
|  | 	@event ChunkUploaded | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {plupload.File} file File that the chunk was uploaded for. | ||
|  | 	@param {Object} result Object with response properties. | ||
|  | 		@param {Number} result.offset The amount of bytes the server has received so far, including this chunk. | ||
|  | 		@param {Number} result.total The size of the file. | ||
|  | 		@param {String} result.response The response body sent by the server. | ||
|  | 		@param {Number} result.status The HTTP status code sent by the server. | ||
|  | 		@param {String} result.responseHeaders All the response headers as a single string. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when a file is successfully uploaded. | ||
|  | 	 | ||
|  | 	@event FileUploaded | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {plupload.File} file File that was uploaded. | ||
|  | 	@param {Object} result Object with response properties. | ||
|  | 		@param {String} result.response The response body sent by the server. | ||
|  | 		@param {Number} result.status The HTTP status code sent by the server. | ||
|  | 		@param {String} result.responseHeaders All the response headers as a single string. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when all files in a queue are uploaded. | ||
|  | 	 | ||
|  | 	@event UploadComplete | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {Array} files Array of file objects that was added to queue/selected by the user. | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when a error occurs. | ||
|  | 	 | ||
|  | 	@event Error | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	@param {Object} error Contains code, message and sometimes file and other details. | ||
|  | 		@param {Number} error.code The plupload error code. | ||
|  | 		@param {String} error.message Description of the error (uses i18n). | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	Fires when destroy method is called. | ||
|  | 	 | ||
|  | 	@event Destroy | ||
|  | 	@param {plupload.Uploader} uploader Uploader instance sending the event. | ||
|  | 	 */ | ||
|  | 	var uid = plupload.guid() | ||
|  | 	, settings | ||
|  | 	, files = [] | ||
|  | 	, preferred_caps = {} | ||
|  | 	, fileInputs = [] | ||
|  | 	, fileDrops = [] | ||
|  | 	, startTime | ||
|  | 	, total | ||
|  | 	, disabled = false | ||
|  | 	, xhr | ||
|  | 	; | ||
|  | 
 | ||
|  | 
 | ||
|  | 	// Private methods
 | ||
|  | 	function uploadNext() { | ||
|  | 		var file, count = 0, i; | ||
|  | 
 | ||
|  | 		if (this.state == plupload.STARTED) { | ||
|  | 			// Find first QUEUED file
 | ||
|  | 			for (i = 0; i < files.length; i++) { | ||
|  | 				if (!file && files[i].status == plupload.QUEUED) { | ||
|  | 					file = files[i]; | ||
|  | 					if (this.trigger("BeforeUpload", file)) { | ||
|  | 						file.status = plupload.UPLOADING; | ||
|  | 						this.trigger("UploadFile", file); | ||
|  | 					} | ||
|  | 				} else { | ||
|  | 					count++; | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// All files are DONE or FAILED
 | ||
|  | 			if (count == files.length) { | ||
|  | 				if (this.state !== plupload.STOPPED) { | ||
|  | 					this.state = plupload.STOPPED; | ||
|  | 					this.trigger("StateChanged"); | ||
|  | 				} | ||
|  | 				this.trigger("UploadComplete", files); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function calcFile(file) { | ||
|  | 		file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100; | ||
|  | 		calc(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function calc() { | ||
|  | 		var i, file; | ||
|  | 
 | ||
|  | 		// Reset stats
 | ||
|  | 		total.reset(); | ||
|  | 
 | ||
|  | 		// Check status, size, loaded etc on all files
 | ||
|  | 		for (i = 0; i < files.length; i++) { | ||
|  | 			file = files[i]; | ||
|  | 
 | ||
|  | 			if (file.size !== undef) { | ||
|  | 				// We calculate totals based on original file size
 | ||
|  | 				total.size += file.origSize; | ||
|  | 
 | ||
|  | 				// Since we cannot predict file size after resize, we do opposite and
 | ||
|  | 				// interpolate loaded amount to match magnitude of total
 | ||
|  | 				total.loaded += file.loaded * file.origSize / file.size; | ||
|  | 			} else { | ||
|  | 				total.size = undef; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (file.status == plupload.DONE) { | ||
|  | 				total.uploaded++; | ||
|  | 			} else if (file.status == plupload.FAILED) { | ||
|  | 				total.failed++; | ||
|  | 			} else { | ||
|  | 				total.queued++; | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// If we couldn't calculate a total file size then use the number of files to calc percent
 | ||
|  | 		if (total.size === undef) { | ||
|  | 			total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0; | ||
|  | 		} else { | ||
|  | 			total.bytesPerSec = Math.ceil(total.loaded / ((+new Date() - startTime || 1) / 1000.0)); | ||
|  | 			total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function getRUID() { | ||
|  | 		var ctrl = fileInputs[0] || fileDrops[0]; | ||
|  | 		if (ctrl) { | ||
|  | 			return ctrl.getRuntime().uid; | ||
|  | 		} | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function runtimeCan(file, cap) { | ||
|  | 		if (file.ruid) { | ||
|  | 			var info = o.Runtime.getInfo(file.ruid); | ||
|  | 			if (info) { | ||
|  | 				return info.can(cap); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function bindEventListeners() { | ||
|  | 		this.bind('FilesAdded FilesRemoved', function(up) { | ||
|  | 			up.trigger('QueueChanged'); | ||
|  | 			up.refresh(); | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		this.bind('CancelUpload', onCancelUpload); | ||
|  | 		 | ||
|  | 		this.bind('BeforeUpload', onBeforeUpload); | ||
|  | 
 | ||
|  | 		this.bind('UploadFile', onUploadFile); | ||
|  | 
 | ||
|  | 		this.bind('UploadProgress', onUploadProgress); | ||
|  | 
 | ||
|  | 		this.bind('StateChanged', onStateChanged); | ||
|  | 
 | ||
|  | 		this.bind('QueueChanged', calc); | ||
|  | 
 | ||
|  | 		this.bind('Error', onError); | ||
|  | 
 | ||
|  | 		this.bind('FileUploaded', onFileUploaded); | ||
|  | 
 | ||
|  | 		this.bind('Destroy', onDestroy); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function initControls(settings, cb) { | ||
|  | 		var self = this, inited = 0, queue = []; | ||
|  | 
 | ||
|  | 		// common settings
 | ||
|  | 		var options = { | ||
|  | 			runtime_order: settings.runtimes, | ||
|  | 			required_caps: settings.required_features, | ||
|  | 			preferred_caps: preferred_caps | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		// add runtime specific options if any
 | ||
|  | 		plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) { | ||
|  | 			if (settings[runtime]) { | ||
|  | 				options[runtime] = settings[runtime]; | ||
|  | 			} | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		// initialize file pickers - there can be many
 | ||
|  | 		if (settings.browse_button) { | ||
|  | 			plupload.each(settings.browse_button, function(el) { | ||
|  | 				queue.push(function(cb) { | ||
|  | 					var fileInput = new o.FileInput(plupload.extend({}, options, { | ||
|  | 						accept: settings.filters.mime_types, | ||
|  | 						name: settings.file_data_name, | ||
|  | 						multiple: settings.multi_selection, | ||
|  | 						container: settings.container, | ||
|  | 						browse_button: el | ||
|  | 					})); | ||
|  | 
 | ||
|  | 					fileInput.onready = function() { | ||
|  | 						var info = o.Runtime.getInfo(this.ruid); | ||
|  | 
 | ||
|  | 						// for backward compatibility
 | ||
|  | 						o.extend(self.features, { | ||
|  | 							chunks: info.can('slice_blob'), | ||
|  | 							multipart: info.can('send_multipart'), | ||
|  | 							multi_selection: info.can('select_multiple') | ||
|  | 						}); | ||
|  | 
 | ||
|  | 						inited++; | ||
|  | 						fileInputs.push(this); | ||
|  | 						cb(); | ||
|  | 					}; | ||
|  | 
 | ||
|  | 					fileInput.onchange = function() { | ||
|  | 						self.addFile(this.files); | ||
|  | 					}; | ||
|  | 
 | ||
|  | 					fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) { | ||
|  | 						if (!disabled) { | ||
|  | 							if (settings.browse_button_hover) { | ||
|  | 								if ('mouseenter' === e.type) { | ||
|  | 									o.addClass(el, settings.browse_button_hover); | ||
|  | 								} else if ('mouseleave' === e.type) { | ||
|  | 									o.removeClass(el, settings.browse_button_hover); | ||
|  | 								} | ||
|  | 							} | ||
|  | 
 | ||
|  | 							if (settings.browse_button_active) { | ||
|  | 								if ('mousedown' === e.type) { | ||
|  | 									o.addClass(el, settings.browse_button_active); | ||
|  | 								} else if ('mouseup' === e.type) { | ||
|  | 									o.removeClass(el, settings.browse_button_active); | ||
|  | 								} | ||
|  | 							} | ||
|  | 						} | ||
|  | 					}); | ||
|  | 
 | ||
|  | 					fileInput.bind('mousedown', function() { | ||
|  | 						self.trigger('Browse'); | ||
|  | 					}); | ||
|  | 
 | ||
|  | 					fileInput.bind('error runtimeerror', function() { | ||
|  | 						fileInput = null; | ||
|  | 						cb(); | ||
|  | 					}); | ||
|  | 
 | ||
|  | 					fileInput.init(); | ||
|  | 				}); | ||
|  | 			}); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// initialize drop zones
 | ||
|  | 		if (settings.drop_element) { | ||
|  | 			plupload.each(settings.drop_element, function(el) { | ||
|  | 				queue.push(function(cb) { | ||
|  | 					var fileDrop = new o.FileDrop(plupload.extend({}, options, { | ||
|  | 						drop_zone: el | ||
|  | 					})); | ||
|  | 
 | ||
|  | 					fileDrop.onready = function() { | ||
|  | 						var info = o.Runtime.getInfo(this.ruid); | ||
|  | 
 | ||
|  | 						// for backward compatibility
 | ||
|  | 						o.extend(self.features, { | ||
|  | 							chunks: info.can('slice_blob'), | ||
|  | 							multipart: info.can('send_multipart'), | ||
|  | 							dragdrop: info.can('drag_and_drop') | ||
|  | 						}); | ||
|  | 
 | ||
|  | 						inited++; | ||
|  | 						fileDrops.push(this); | ||
|  | 						cb(); | ||
|  | 					}; | ||
|  | 
 | ||
|  | 					fileDrop.ondrop = function() { | ||
|  | 						self.addFile(this.files); | ||
|  | 					}; | ||
|  | 
 | ||
|  | 					fileDrop.bind('error runtimeerror', function() { | ||
|  | 						fileDrop = null; | ||
|  | 						cb(); | ||
|  | 					}); | ||
|  | 
 | ||
|  | 					fileDrop.init(); | ||
|  | 				}); | ||
|  | 			}); | ||
|  | 		} | ||
|  | 
 | ||
|  | 
 | ||
|  | 		o.inSeries(queue, function() { | ||
|  | 			if (typeof(cb) === 'function') { | ||
|  | 				cb(inited); | ||
|  | 			} | ||
|  | 		}); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function resizeImage(blob, params, cb) { | ||
|  | 		var img = new o.Image(); | ||
|  | 
 | ||
|  | 		try { | ||
|  | 			img.onload = function() { | ||
|  | 				// no manipulation required if...
 | ||
|  | 				if (params.width > this.width && | ||
|  | 					params.height > this.height && | ||
|  | 					params.quality === undef && | ||
|  | 					params.preserve_headers && | ||
|  | 					!params.crop | ||
|  | 				) { | ||
|  | 					this.destroy(); | ||
|  | 					return cb(blob); | ||
|  | 				} | ||
|  | 				// otherwise downsize
 | ||
|  | 				img.downsize(params.width, params.height, params.crop, params.preserve_headers); | ||
|  | 			}; | ||
|  | 
 | ||
|  | 			img.onresize = function() { | ||
|  | 				cb(this.getAsBlob(blob.type, params.quality)); | ||
|  | 				this.destroy(); | ||
|  | 			}; | ||
|  | 
 | ||
|  | 			img.onerror = function() { | ||
|  | 				cb(blob); | ||
|  | 			}; | ||
|  | 
 | ||
|  | 			img.load(blob); | ||
|  | 		} catch(ex) { | ||
|  | 			cb(blob); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function setOption(option, value, init) { | ||
|  | 		var self = this, reinitRequired = false; | ||
|  | 
 | ||
|  | 		function _setOption(option, value, init) { | ||
|  | 			var oldValue = settings[option]; | ||
|  | 
 | ||
|  | 			switch (option) { | ||
|  | 				case 'max_file_size': | ||
|  | 					if (option === 'max_file_size') { | ||
|  | 						settings.max_file_size = settings.filters.max_file_size = value; | ||
|  | 					} | ||
|  | 					break; | ||
|  | 
 | ||
|  | 				case 'chunk_size': | ||
|  | 					if (value = plupload.parseSize(value)) { | ||
|  | 						settings[option] = value; | ||
|  | 						settings.send_file_name = true; | ||
|  | 					} | ||
|  | 					break; | ||
|  | 
 | ||
|  | 				case 'multipart': | ||
|  | 					settings[option] = value; | ||
|  | 					if (!value) { | ||
|  | 						settings.send_file_name = true; | ||
|  | 					} | ||
|  | 					break; | ||
|  | 
 | ||
|  | 				case 'unique_names': | ||
|  | 					settings[option] = value; | ||
|  | 					if (value) { | ||
|  | 						settings.send_file_name = true; | ||
|  | 					} | ||
|  | 					break; | ||
|  | 
 | ||
|  | 				case 'filters': | ||
|  | 					// for sake of backward compatibility
 | ||
|  | 					if (plupload.typeOf(value) === 'array') { | ||
|  | 						value = { | ||
|  | 							mime_types: value | ||
|  | 						}; | ||
|  | 					} | ||
|  | 
 | ||
|  | 					if (init) { | ||
|  | 						plupload.extend(settings.filters, value); | ||
|  | 					} else { | ||
|  | 						settings.filters = value; | ||
|  | 					} | ||
|  | 
 | ||
|  | 					// if file format filters are being updated, regenerate the matching expressions
 | ||
|  | 					if (value.mime_types) { | ||
|  | 						settings.filters.mime_types.regexp = (function(filters) { | ||
|  | 							var extensionsRegExp = []; | ||
|  | 
 | ||
|  | 							plupload.each(filters, function(filter) { | ||
|  | 								plupload.each(filter.extensions.split(/,/), function(ext) { | ||
|  | 									if (/^\s*\*\s*$/.test(ext)) { | ||
|  | 										extensionsRegExp.push('\\.*'); | ||
|  | 									} else { | ||
|  | 										extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&')); | ||
|  | 									} | ||
|  | 								}); | ||
|  | 							}); | ||
|  | 
 | ||
|  | 							return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i'); | ||
|  | 						}(settings.filters.mime_types)); | ||
|  | 					} | ||
|  | 					break; | ||
|  | 	 | ||
|  | 				case 'resize': | ||
|  | 					if (init) { | ||
|  | 						plupload.extend(settings.resize, value, { | ||
|  | 							enabled: true | ||
|  | 						}); | ||
|  | 					} else { | ||
|  | 						settings.resize = value; | ||
|  | 					} | ||
|  | 					break; | ||
|  | 
 | ||
|  | 				case 'prevent_duplicates': | ||
|  | 					settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value; | ||
|  | 					break; | ||
|  | 
 | ||
|  | 				// options that require reinitialisation
 | ||
|  | 				case 'container': | ||
|  | 				case 'browse_button': | ||
|  | 				case 'drop_element': | ||
|  | 						value = 'container' === option | ||
|  | 							? plupload.get(value) | ||
|  | 							: plupload.getAll(value) | ||
|  | 							;  | ||
|  | 				 | ||
|  | 				case 'runtimes': | ||
|  | 				case 'multi_selection': | ||
|  | 					settings[option] = value; | ||
|  | 					if (!init) { | ||
|  | 						reinitRequired = true; | ||
|  | 					} | ||
|  | 					break; | ||
|  | 
 | ||
|  | 				default: | ||
|  | 					settings[option] = value; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (!init) { | ||
|  | 				self.trigger('OptionChanged', option, value, oldValue); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (typeof(option) === 'object') { | ||
|  | 			plupload.each(option, function(value, option) { | ||
|  | 				_setOption(option, value, init); | ||
|  | 			}); | ||
|  | 		} else { | ||
|  | 			_setOption(option, value, init); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (init) { | ||
|  | 			// Normalize the list of required capabilities
 | ||
|  | 			settings.required_features = normalizeCaps(plupload.extend({}, settings)); | ||
|  | 
 | ||
|  | 			// Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
 | ||
|  | 			preferred_caps = normalizeCaps(plupload.extend({}, settings, { | ||
|  | 				required_features: true | ||
|  | 			})); | ||
|  | 		} else if (reinitRequired) { | ||
|  | 			self.trigger('Destroy'); | ||
|  | 			 | ||
|  | 			initControls.call(self, settings, function(inited) { | ||
|  | 				if (inited) { | ||
|  | 					self.runtime = o.Runtime.getInfo(getRUID()).type; | ||
|  | 					self.trigger('Init', { runtime: self.runtime }); | ||
|  | 					self.trigger('PostInit'); | ||
|  | 				} else { | ||
|  | 					self.trigger('Error', { | ||
|  | 						code : plupload.INIT_ERROR, | ||
|  | 						message : plupload.translate('Init error.') | ||
|  | 					}); | ||
|  | 				} | ||
|  | 			}); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	// Internal event handlers
 | ||
|  | 	function onBeforeUpload(up, file) { | ||
|  | 		// Generate unique target filenames
 | ||
|  | 		if (up.settings.unique_names) { | ||
|  | 			var matches = file.name.match(/\.([^.]+)$/), ext = "part"; | ||
|  | 			if (matches) { | ||
|  | 				ext = matches[1]; | ||
|  | 			} | ||
|  | 			file.target_name = file.id + '.' + ext; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function onUploadFile(up, file) { | ||
|  | 		var url = up.settings.url | ||
|  | 		, chunkSize = up.settings.chunk_size | ||
|  | 		, retries = up.settings.max_retries | ||
|  | 		, features = up.features | ||
|  | 		, offset = 0 | ||
|  | 		, blob | ||
|  | 		; | ||
|  | 
 | ||
|  | 		// make sure we start at a predictable offset
 | ||
|  | 		if (file.loaded) { | ||
|  | 			offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		function handleError() { | ||
|  | 			if (retries-- > 0) { | ||
|  | 				delay(uploadNextChunk, 1000); | ||
|  | 			} else { | ||
|  | 				file.loaded = offset; // reset all progress
 | ||
|  | 
 | ||
|  | 				up.trigger('Error', { | ||
|  | 					code : plupload.HTTP_ERROR, | ||
|  | 					message : plupload.translate('HTTP Error.'), | ||
|  | 					file : file, | ||
|  | 					response : xhr.responseText, | ||
|  | 					status : xhr.status, | ||
|  | 					responseHeaders: xhr.getAllResponseHeaders() | ||
|  | 				}); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		function uploadNextChunk() { | ||
|  | 			var chunkBlob, formData, args = {}, curChunkSize; | ||
|  | 
 | ||
|  | 			// make sure that file wasn't cancelled and upload is not stopped in general
 | ||
|  | 			if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) { | ||
|  | 				return; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// send additional 'name' parameter only if required
 | ||
|  | 			if (up.settings.send_file_name) { | ||
|  | 				args.name = file.target_name || file.name; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory 
 | ||
|  | 				curChunkSize = Math.min(chunkSize, blob.size - offset); | ||
|  | 				chunkBlob = blob.slice(offset, offset + curChunkSize); | ||
|  | 			} else { | ||
|  | 				curChunkSize = blob.size; | ||
|  | 				chunkBlob = blob; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
 | ||
|  | 			if (chunkSize && features.chunks) { | ||
|  | 				// Setup query string arguments
 | ||
|  | 				if (up.settings.send_chunk_number) { | ||
|  | 					args.chunk = Math.ceil(offset / chunkSize); | ||
|  | 					args.chunks = Math.ceil(blob.size / chunkSize); | ||
|  | 				} else { // keep support for experimental chunk format, just in case
 | ||
|  | 					args.offset = offset; | ||
|  | 					args.total = blob.size; | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			xhr = new o.XMLHttpRequest(); | ||
|  | 
 | ||
|  | 			// Do we have upload progress support
 | ||
|  | 			if (xhr.upload) { | ||
|  | 				xhr.upload.onprogress = function(e) { | ||
|  | 					file.loaded = Math.min(file.size, offset + e.loaded); | ||
|  | 					up.trigger('UploadProgress', file); | ||
|  | 				}; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			xhr.onload = function() { | ||
|  | 				// check if upload made itself through
 | ||
|  | 				if (xhr.status >= 400) { | ||
|  | 					handleError(); | ||
|  | 					return; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				retries = up.settings.max_retries; // reset the counter
 | ||
|  | 
 | ||
|  | 				// Handle chunk response
 | ||
|  | 				if (curChunkSize < blob.size) { | ||
|  | 					chunkBlob.destroy(); | ||
|  | 
 | ||
|  | 					offset += curChunkSize; | ||
|  | 					file.loaded = Math.min(offset, blob.size); | ||
|  | 
 | ||
|  | 					up.trigger('ChunkUploaded', file, { | ||
|  | 						offset : file.loaded, | ||
|  | 						total : blob.size, | ||
|  | 						response : xhr.responseText, | ||
|  | 						status : xhr.status, | ||
|  | 						responseHeaders: xhr.getAllResponseHeaders() | ||
|  | 					}); | ||
|  | 
 | ||
|  | 					// stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
 | ||
|  | 					if (o.Env.browser === 'Android Browser') { | ||
|  | 						// doesn't harm in general, but is not required anywhere else
 | ||
|  | 						up.trigger('UploadProgress', file); | ||
|  | 					}  | ||
|  | 				} else { | ||
|  | 					file.loaded = file.size; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				chunkBlob = formData = null; // Free memory
 | ||
|  | 
 | ||
|  | 				// Check if file is uploaded
 | ||
|  | 				if (!offset || offset >= blob.size) { | ||
|  | 					// If file was modified, destory the copy
 | ||
|  | 					if (file.size != file.origSize) { | ||
|  | 						blob.destroy(); | ||
|  | 						blob = null; | ||
|  | 					} | ||
|  | 
 | ||
|  | 					up.trigger('UploadProgress', file); | ||
|  | 
 | ||
|  | 					file.status = plupload.DONE; | ||
|  | 
 | ||
|  | 					up.trigger('FileUploaded', file, { | ||
|  | 						response : xhr.responseText, | ||
|  | 						status : xhr.status, | ||
|  | 						responseHeaders: xhr.getAllResponseHeaders() | ||
|  | 					}); | ||
|  | 				} else { | ||
|  | 					// Still chunks left
 | ||
|  | 					delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
 | ||
|  | 				} | ||
|  | 			}; | ||
|  | 
 | ||
|  | 			xhr.onerror = function() { | ||
|  | 				handleError(); | ||
|  | 			}; | ||
|  | 
 | ||
|  | 			xhr.onloadend = function() { | ||
|  | 				this.destroy(); | ||
|  | 				xhr = null; | ||
|  | 			}; | ||
|  | 
 | ||
|  | 			// Build multipart request
 | ||
|  | 			if (up.settings.multipart && features.multipart) { | ||
|  | 				xhr.open("post", url, true); | ||
|  | 
 | ||
|  | 				// Set custom headers
 | ||
|  | 				plupload.each(up.settings.headers, function(value, name) { | ||
|  | 					xhr.setRequestHeader(name, value); | ||
|  | 				}); | ||
|  | 
 | ||
|  | 				formData = new o.FormData(); | ||
|  | 
 | ||
|  | 				// Add multipart params
 | ||
|  | 				plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) { | ||
|  | 					formData.append(name, value); | ||
|  | 				}); | ||
|  | 
 | ||
|  | 				// Add file and send it
 | ||
|  | 				formData.append(up.settings.file_data_name, chunkBlob); | ||
|  | 				xhr.send(formData, { | ||
|  | 					runtime_order: up.settings.runtimes, | ||
|  | 					required_caps: up.settings.required_features, | ||
|  | 					preferred_caps: preferred_caps | ||
|  | 				}); | ||
|  | 			} else { | ||
|  | 				// if no multipart, send as binary stream
 | ||
|  | 				url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params)); | ||
|  | 
 | ||
|  | 				xhr.open("post", url, true); | ||
|  | 
 | ||
|  | 				xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
 | ||
|  | 
 | ||
|  | 				// Set custom headers
 | ||
|  | 				plupload.each(up.settings.headers, function(value, name) { | ||
|  | 					xhr.setRequestHeader(name, value); | ||
|  | 				}); | ||
|  | 
 | ||
|  | 				xhr.send(chunkBlob, { | ||
|  | 					runtime_order: up.settings.runtimes, | ||
|  | 					required_caps: up.settings.required_features, | ||
|  | 					preferred_caps: preferred_caps | ||
|  | 				}); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		blob = file.getSource(); | ||
|  | 
 | ||
|  | 		// Start uploading chunks
 | ||
|  | 		if (up.settings.resize.enabled && runtimeCan(blob, 'send_binary_string') && !!~o.inArray(blob.type, ['image/jpeg', 'image/png'])) { | ||
|  | 			// Resize if required
 | ||
|  | 			resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) { | ||
|  | 				blob = resizedBlob; | ||
|  | 				file.size = resizedBlob.size; | ||
|  | 				uploadNextChunk(); | ||
|  | 			}); | ||
|  | 		} else { | ||
|  | 			uploadNextChunk(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function onUploadProgress(up, file) { | ||
|  | 		calcFile(file); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function onStateChanged(up) { | ||
|  | 		if (up.state == plupload.STARTED) { | ||
|  | 			// Get start time to calculate bps
 | ||
|  | 			startTime = (+new Date()); | ||
|  | 		} else if (up.state == plupload.STOPPED) { | ||
|  | 			// Reset currently uploading files
 | ||
|  | 			for (var i = up.files.length - 1; i >= 0; i--) { | ||
|  | 				if (up.files[i].status == plupload.UPLOADING) { | ||
|  | 					up.files[i].status = plupload.QUEUED; | ||
|  | 					calc(); | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function onCancelUpload() { | ||
|  | 		if (xhr) { | ||
|  | 			xhr.abort(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function onFileUploaded(up) { | ||
|  | 		calc(); | ||
|  | 
 | ||
|  | 		// Upload next file but detach it from the error event
 | ||
|  | 		// since other custom listeners might want to stop the queue
 | ||
|  | 		delay(function() { | ||
|  | 			uploadNext.call(up); | ||
|  | 		}, 1); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function onError(up, err) { | ||
|  | 		if (err.code === plupload.INIT_ERROR) { | ||
|  | 			up.destroy(); | ||
|  | 		} | ||
|  | 		// Set failed status if an error occured on a file
 | ||
|  | 		else if (err.code === plupload.HTTP_ERROR) { | ||
|  | 			err.file.status = plupload.FAILED; | ||
|  | 			calcFile(err.file); | ||
|  | 
 | ||
|  | 			// Upload next file but detach it from the error event
 | ||
|  | 			// since other custom listeners might want to stop the queue
 | ||
|  | 			if (up.state == plupload.STARTED) { // upload in progress
 | ||
|  | 				up.trigger('CancelUpload'); | ||
|  | 				delay(function() { | ||
|  | 					uploadNext.call(up); | ||
|  | 				}, 1); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	function onDestroy(up) { | ||
|  | 		up.stop(); | ||
|  | 
 | ||
|  | 		// Purge the queue
 | ||
|  | 		plupload.each(files, function(file) { | ||
|  | 			file.destroy(); | ||
|  | 		}); | ||
|  | 		files = []; | ||
|  | 
 | ||
|  | 		if (fileInputs.length) { | ||
|  | 			plupload.each(fileInputs, function(fileInput) { | ||
|  | 				fileInput.destroy(); | ||
|  | 			}); | ||
|  | 			fileInputs = []; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (fileDrops.length) { | ||
|  | 			plupload.each(fileDrops, function(fileDrop) { | ||
|  | 				fileDrop.destroy(); | ||
|  | 			}); | ||
|  | 			fileDrops = []; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		preferred_caps = {}; | ||
|  | 		disabled = false; | ||
|  | 		startTime = xhr = null; | ||
|  | 		total.reset(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	// Default settings
 | ||
|  | 	settings = { | ||
|  | 		runtimes: o.Runtime.order, | ||
|  | 		max_retries: 0, | ||
|  | 		chunk_size: 0, | ||
|  | 		multipart: true, | ||
|  | 		multi_selection: true, | ||
|  | 		file_data_name: 'file', | ||
|  | 		filters: { | ||
|  | 			mime_types: [], | ||
|  | 			prevent_duplicates: false, | ||
|  | 			max_file_size: 0 | ||
|  | 		}, | ||
|  | 		resize: { | ||
|  | 			enabled: false, | ||
|  | 			preserve_headers: true, | ||
|  | 			crop: false | ||
|  | 		}, | ||
|  | 		send_file_name: true, | ||
|  | 		send_chunk_number: true | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	 | ||
|  | 	setOption.call(this, options, null, true); | ||
|  | 
 | ||
|  | 	// Inital total state
 | ||
|  | 	total = new plupload.QueueProgress();  | ||
|  | 
 | ||
|  | 	// Add public methods
 | ||
|  | 	plupload.extend(this, { | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Unique id for the Uploader instance. | ||
|  | 		 * | ||
|  | 		 * @property id | ||
|  | 		 * @type String | ||
|  | 		 */ | ||
|  | 		id : uid, | ||
|  | 		uid : uid, // mOxie uses this to differentiate between event targets
 | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED. | ||
|  | 		 * These states are controlled by the stop/start methods. The default value is STOPPED. | ||
|  | 		 * | ||
|  | 		 * @property state | ||
|  | 		 * @type Number | ||
|  | 		 */ | ||
|  | 		state : plupload.STOPPED, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Map of features that are available for the uploader runtime. Features will be filled | ||
|  | 		 * before the init event is called, these features can then be used to alter the UI for the end user. | ||
|  | 		 * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize. | ||
|  | 		 * | ||
|  | 		 * @property features | ||
|  | 		 * @type Object | ||
|  | 		 */ | ||
|  | 		features : {}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Current runtime name. | ||
|  | 		 * | ||
|  | 		 * @property runtime | ||
|  | 		 * @type String | ||
|  | 		 */ | ||
|  | 		runtime : null, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Current upload queue, an array of File instances. | ||
|  | 		 * | ||
|  | 		 * @property files | ||
|  | 		 * @type Array | ||
|  | 		 * @see plupload.File | ||
|  | 		 */ | ||
|  | 		files : files, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Object with name/value settings. | ||
|  | 		 * | ||
|  | 		 * @property settings | ||
|  | 		 * @type Object | ||
|  | 		 */ | ||
|  | 		settings : settings, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Total progess information. How many files has been uploaded, total percent etc. | ||
|  | 		 * | ||
|  | 		 * @property total | ||
|  | 		 * @type plupload.QueueProgress | ||
|  | 		 */ | ||
|  | 		total : total, | ||
|  | 
 | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Initializes the Uploader instance and adds internal event listeners. | ||
|  | 		 * | ||
|  | 		 * @method init | ||
|  | 		 */ | ||
|  | 		init : function() { | ||
|  | 			var self = this, opt, preinitOpt, err; | ||
|  | 			 | ||
|  | 			preinitOpt = self.getOption('preinit'); | ||
|  | 			if (typeof(preinitOpt) == "function") { | ||
|  | 				preinitOpt(self); | ||
|  | 			} else { | ||
|  | 				plupload.each(preinitOpt, function(func, name) { | ||
|  | 					self.bind(name, func); | ||
|  | 				}); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			bindEventListeners.call(self); | ||
|  | 
 | ||
|  | 			// Check for required options
 | ||
|  | 			plupload.each(['container', 'browse_button', 'drop_element'], function(el) { | ||
|  | 				if (self.getOption(el) === null) { | ||
|  | 					err = { | ||
|  | 						code : plupload.INIT_ERROR, | ||
|  | 						message : plupload.translate("'%' specified, but cannot be found.") | ||
|  | 					} | ||
|  | 					return false; | ||
|  | 				} | ||
|  | 			}); | ||
|  | 
 | ||
|  | 			if (err) { | ||
|  | 				return self.trigger('Error', err); | ||
|  | 			} | ||
|  | 
 | ||
|  | 
 | ||
|  | 			if (!settings.browse_button && !settings.drop_element) { | ||
|  | 				return self.trigger('Error', { | ||
|  | 					code : plupload.INIT_ERROR, | ||
|  | 					message : plupload.translate("You must specify either 'browse_button' or 'drop_element'.") | ||
|  | 				}); | ||
|  | 			} | ||
|  | 
 | ||
|  | 
 | ||
|  | 			initControls.call(self, settings, function(inited) { | ||
|  | 				var initOpt = self.getOption('init'); | ||
|  | 				if (typeof(initOpt) == "function") { | ||
|  | 					initOpt(self); | ||
|  | 				} else { | ||
|  | 					plupload.each(initOpt, function(func, name) { | ||
|  | 						self.bind(name, func); | ||
|  | 					}); | ||
|  | 				} | ||
|  | 
 | ||
|  | 				if (inited) { | ||
|  | 					self.runtime = o.Runtime.getInfo(getRUID()).type; | ||
|  | 					self.trigger('Init', { runtime: self.runtime }); | ||
|  | 					self.trigger('PostInit'); | ||
|  | 				} else { | ||
|  | 					self.trigger('Error', { | ||
|  | 						code : plupload.INIT_ERROR, | ||
|  | 						message : plupload.translate('Init error.') | ||
|  | 					}); | ||
|  | 				} | ||
|  | 			}); | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Set the value for the specified option(s). | ||
|  | 		 * | ||
|  | 		 * @method setOption | ||
|  | 		 * @since 2.1 | ||
|  | 		 * @param {String|Object} option Name of the option to change or the set of key/value pairs | ||
|  | 		 * @param {Mixed} [value] Value for the option (is ignored, if first argument is object) | ||
|  | 		 */ | ||
|  | 		setOption: function(option, value) { | ||
|  | 			setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
 | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Get the value for the specified option or the whole configuration, if not specified. | ||
|  | 		 *  | ||
|  | 		 * @method getOption | ||
|  | 		 * @since 2.1 | ||
|  | 		 * @param {String} [option] Name of the option to get | ||
|  | 		 * @return {Mixed} Value for the option or the whole set | ||
|  | 		 */ | ||
|  | 		getOption: function(option) { | ||
|  | 			if (!option) { | ||
|  | 				return settings; | ||
|  | 			} | ||
|  | 			return settings[option]; | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Refreshes the upload instance by dispatching out a refresh event to all runtimes. | ||
|  | 		 * This would for example reposition flash/silverlight shims on the page. | ||
|  | 		 * | ||
|  | 		 * @method refresh | ||
|  | 		 */ | ||
|  | 		refresh : function() { | ||
|  | 			if (fileInputs.length) { | ||
|  | 				plupload.each(fileInputs, function(fileInput) { | ||
|  | 					fileInput.trigger('Refresh'); | ||
|  | 				}); | ||
|  | 			} | ||
|  | 			this.trigger('Refresh'); | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Starts uploading the queued files. | ||
|  | 		 * | ||
|  | 		 * @method start | ||
|  | 		 */ | ||
|  | 		start : function() { | ||
|  | 			if (this.state != plupload.STARTED) { | ||
|  | 				this.state = plupload.STARTED; | ||
|  | 				this.trigger('StateChanged'); | ||
|  | 
 | ||
|  | 				uploadNext.call(this); | ||
|  | 			} | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Stops the upload of the queued files. | ||
|  | 		 * | ||
|  | 		 * @method stop | ||
|  | 		 */ | ||
|  | 		stop : function() { | ||
|  | 			if (this.state != plupload.STOPPED) { | ||
|  | 				this.state = plupload.STOPPED; | ||
|  | 				this.trigger('StateChanged'); | ||
|  | 				this.trigger('CancelUpload'); | ||
|  | 			} | ||
|  | 		}, | ||
|  | 
 | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Disables/enables browse button on request. | ||
|  | 		 * | ||
|  | 		 * @method disableBrowse | ||
|  | 		 * @param {Boolean} disable Whether to disable or enable (default: true) | ||
|  | 		 */ | ||
|  | 		disableBrowse : function() { | ||
|  | 			disabled = arguments[0] !== undef ? arguments[0] : true; | ||
|  | 
 | ||
|  | 			if (fileInputs.length) { | ||
|  | 				plupload.each(fileInputs, function(fileInput) { | ||
|  | 					fileInput.disable(disabled); | ||
|  | 				}); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			this.trigger('DisableBrowse', disabled); | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Returns the specified file object by id. | ||
|  | 		 * | ||
|  | 		 * @method getFile | ||
|  | 		 * @param {String} id File id to look for. | ||
|  | 		 * @return {plupload.File} File object or undefined if it wasn't found; | ||
|  | 		 */ | ||
|  | 		getFile : function(id) { | ||
|  | 			var i; | ||
|  | 			for (i = files.length - 1; i >= 0; i--) { | ||
|  | 				if (files[i].id === id) { | ||
|  | 					return files[i]; | ||
|  | 				} | ||
|  | 			} | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Adds file to the queue programmatically. Can be native file, instance of Plupload.File, | ||
|  | 		 * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded,  | ||
|  | 		 * if any files were added to the queue. Otherwise nothing happens. | ||
|  | 		 * | ||
|  | 		 * @method addFile | ||
|  | 		 * @since 2.0 | ||
|  | 		 * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue. | ||
|  | 		 * @param {String} [fileName] If specified, will be used as a name for the file | ||
|  | 		 */ | ||
|  | 		addFile : function(file, fileName) { | ||
|  | 			var self = this | ||
|  | 			, queue = []  | ||
|  | 			, filesAdded = [] | ||
|  | 			, ruid | ||
|  | 			; | ||
|  | 
 | ||
|  | 			function filterFile(file, cb) { | ||
|  | 				var queue = []; | ||
|  | 				o.each(self.settings.filters, function(rule, name) { | ||
|  | 					if (fileFilters[name]) { | ||
|  | 						queue.push(function(cb) { | ||
|  | 							fileFilters[name].call(self, rule, file, function(res) { | ||
|  | 								cb(!res); | ||
|  | 							}); | ||
|  | 						}); | ||
|  | 					} | ||
|  | 				}); | ||
|  | 				o.inSeries(queue, cb); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * @method resolveFile | ||
|  | 			 * @private | ||
|  | 			 * @param {o.File|o.Blob|plupload.File|File|Blob|input[type="file"]} file | ||
|  | 			 */ | ||
|  | 			function resolveFile(file) { | ||
|  | 				var type = o.typeOf(file); | ||
|  | 
 | ||
|  | 				// o.File
 | ||
|  | 				if (file instanceof o.File) {  | ||
|  | 					if (!file.ruid && !file.isDetached()) { | ||
|  | 						if (!ruid) { // weird case
 | ||
|  | 							return false; | ||
|  | 						} | ||
|  | 						file.ruid = ruid; | ||
|  | 						file.connectRuntime(ruid); | ||
|  | 					} | ||
|  | 					resolveFile(new plupload.File(file)); | ||
|  | 				} | ||
|  | 				// o.Blob 
 | ||
|  | 				else if (file instanceof o.Blob) { | ||
|  | 					resolveFile(file.getSource()); | ||
|  | 					file.destroy(); | ||
|  | 				}  | ||
|  | 				// plupload.File - final step for other branches
 | ||
|  | 				else if (file instanceof plupload.File) { | ||
|  | 					if (fileName) { | ||
|  | 						file.name = fileName; | ||
|  | 					} | ||
|  | 					 | ||
|  | 					queue.push(function(cb) { | ||
|  | 						// run through the internal and user-defined filters, if any
 | ||
|  | 						filterFile(file, function(err) { | ||
|  | 							if (!err) { | ||
|  | 								// make files available for the filters by updating the main queue directly
 | ||
|  | 								files.push(file); | ||
|  | 								// collect the files that will be passed to FilesAdded event
 | ||
|  | 								filesAdded.push(file);  | ||
|  | 
 | ||
|  | 								self.trigger("FileFiltered", file); | ||
|  | 							} | ||
|  | 							delay(cb, 1); // do not build up recursions or eventually we might hit the limits
 | ||
|  | 						}); | ||
|  | 					}); | ||
|  | 				}  | ||
|  | 				// native File or blob
 | ||
|  | 				else if (o.inArray(type, ['file', 'blob']) !== -1) { | ||
|  | 					resolveFile(new o.File(null, file)); | ||
|  | 				}  | ||
|  | 				// input[type="file"]
 | ||
|  | 				else if (type === 'node' && o.typeOf(file.files) === 'filelist') { | ||
|  | 					// if we are dealing with input[type="file"]
 | ||
|  | 					o.each(file.files, resolveFile); | ||
|  | 				}  | ||
|  | 				// mixed array of any supported types (see above)
 | ||
|  | 				else if (type === 'array') { | ||
|  | 					fileName = null; // should never happen, but unset anyway to avoid funny situations
 | ||
|  | 					o.each(file, resolveFile); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			ruid = getRUID(); | ||
|  | 			 | ||
|  | 			resolveFile(file); | ||
|  | 
 | ||
|  | 			if (queue.length) { | ||
|  | 				o.inSeries(queue, function() { | ||
|  | 					// if any files left after filtration, trigger FilesAdded
 | ||
|  | 					if (filesAdded.length) { | ||
|  | 						self.trigger("FilesAdded", filesAdded); | ||
|  | 					} | ||
|  | 				}); | ||
|  | 			} | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Removes a specific file. | ||
|  | 		 * | ||
|  | 		 * @method removeFile | ||
|  | 		 * @param {plupload.File|String} file File to remove from queue. | ||
|  | 		 */ | ||
|  | 		removeFile : function(file) { | ||
|  | 			var id = typeof(file) === 'string' ? file : file.id; | ||
|  | 
 | ||
|  | 			for (var i = files.length - 1; i >= 0; i--) { | ||
|  | 				if (files[i].id === id) { | ||
|  | 					return this.splice(i, 1)[0]; | ||
|  | 				} | ||
|  | 			} | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Removes part of the queue and returns the files removed. This will also trigger the FilesRemoved and QueueChanged events. | ||
|  | 		 * | ||
|  | 		 * @method splice | ||
|  | 		 * @param {Number} start (Optional) Start index to remove from. | ||
|  | 		 * @param {Number} length (Optional) Lengh of items to remove. | ||
|  | 		 * @return {Array} Array of files that was removed. | ||
|  | 		 */ | ||
|  | 		splice : function(start, length) { | ||
|  | 			// Splice and trigger events
 | ||
|  | 			var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length); | ||
|  | 
 | ||
|  | 			// if upload is in progress we need to stop it and restart after files are removed
 | ||
|  | 			var restartRequired = false; | ||
|  | 			if (this.state == plupload.STARTED) { // upload in progress
 | ||
|  | 				plupload.each(removed, function(file) { | ||
|  | 					if (file.status === plupload.UPLOADING) { | ||
|  | 						restartRequired = true; // do not restart, unless file that is being removed is uploading
 | ||
|  | 						return false; | ||
|  | 					} | ||
|  | 				}); | ||
|  | 				 | ||
|  | 				if (restartRequired) { | ||
|  | 					this.stop(); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			this.trigger("FilesRemoved", removed); | ||
|  | 
 | ||
|  | 			// Dispose any resources allocated by those files
 | ||
|  | 			plupload.each(removed, function(file) { | ||
|  | 				file.destroy(); | ||
|  | 			}); | ||
|  | 			 | ||
|  | 			if (restartRequired) { | ||
|  | 				this.start(); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			return removed; | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		Dispatches the specified event name and its arguments to all listeners. | ||
|  | 
 | ||
|  | 		@method trigger | ||
|  | 		@param {String} name Event name to fire. | ||
|  | 		@param {Object..} Multiple arguments to pass along to the listener functions. | ||
|  | 		*/ | ||
|  | 
 | ||
|  | 		// override the parent method to match Plupload-like event logic
 | ||
|  | 		dispatchEvent: function(type) { | ||
|  | 			var list, args, result; | ||
|  | 						 | ||
|  | 			type = type.toLowerCase(); | ||
|  | 							 | ||
|  | 			list = this.hasEventListener(type); | ||
|  | 
 | ||
|  | 			if (list) { | ||
|  | 				// sort event list by priority
 | ||
|  | 				list.sort(function(a, b) { return b.priority - a.priority; }); | ||
|  | 				 | ||
|  | 				// first argument should be current plupload.Uploader instance
 | ||
|  | 				args = [].slice.call(arguments); | ||
|  | 				args.shift(); | ||
|  | 				args.unshift(this); | ||
|  | 
 | ||
|  | 				for (var i = 0; i < list.length; i++) { | ||
|  | 					// Fire event, break chain if false is returned
 | ||
|  | 					if (list[i].fn.apply(list[i].scope, args) === false) { | ||
|  | 						return false; | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 			return true; | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		Check whether uploader has any listeners to the specified event. | ||
|  | 
 | ||
|  | 		@method hasEventListener | ||
|  | 		@param {String} name Event name to check for. | ||
|  | 		*/ | ||
|  | 
 | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		Adds an event listener by name. | ||
|  | 
 | ||
|  | 		@method bind | ||
|  | 		@param {String} name Event name to listen for. | ||
|  | 		@param {function} fn Function to call ones the event gets fired. | ||
|  | 		@param {Object} [scope] Optional scope to execute the specified function in. | ||
|  | 		@param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first | ||
|  | 		*/ | ||
|  | 		bind: function(name, fn, scope, priority) { | ||
|  | 			// adapt moxie EventTarget style to Plupload-like
 | ||
|  | 			plupload.Uploader.prototype.bind.call(this, name, fn, priority, scope); | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		Removes the specified event listener. | ||
|  | 
 | ||
|  | 		@method unbind | ||
|  | 		@param {String} name Name of event to remove. | ||
|  | 		@param {function} fn Function to remove from listener. | ||
|  | 		*/ | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		Removes all event listeners. | ||
|  | 
 | ||
|  | 		@method unbindAll | ||
|  | 		*/ | ||
|  | 
 | ||
|  | 
 | ||
|  | 		/** | ||
|  | 		 * Destroys Plupload instance and cleans after itself. | ||
|  | 		 * | ||
|  | 		 * @method destroy | ||
|  | 		 */ | ||
|  | 		destroy : function() { | ||
|  | 			this.trigger('Destroy'); | ||
|  | 			settings = total = null; // purge these exclusively
 | ||
|  | 			this.unbindAll(); | ||
|  | 		} | ||
|  | 	}); | ||
|  | }; | ||
|  | 
 | ||
|  | plupload.Uploader.prototype = o.EventTarget.instance; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Constructs a new file instance. | ||
|  |  * | ||
|  |  * @class File | ||
|  |  * @constructor | ||
|  |  *  | ||
|  |  * @param {Object} file Object containing file properties | ||
|  |  * @param {String} file.name Name of the file. | ||
|  |  * @param {Number} file.size File size. | ||
|  |  */ | ||
|  | plupload.File = (function() { | ||
|  | 	var filepool = {}; | ||
|  | 
 | ||
|  | 	function PluploadFile(file) { | ||
|  | 
 | ||
|  | 		plupload.extend(this, { | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * File id this is a globally unique id for the specific file. | ||
|  | 			 * | ||
|  | 			 * @property id | ||
|  | 			 * @type String | ||
|  | 			 */ | ||
|  | 			id: plupload.guid(), | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * File name for example "myfile.gif". | ||
|  | 			 * | ||
|  | 			 * @property name | ||
|  | 			 * @type String | ||
|  | 			 */ | ||
|  | 			name: file.name || file.fileName, | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * File type, `e.g image/jpeg` | ||
|  | 			 * | ||
|  | 			 * @property type | ||
|  | 			 * @type String | ||
|  | 			 */ | ||
|  | 			type: file.type || '', | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * File size in bytes (may change after client-side manupilation). | ||
|  | 			 * | ||
|  | 			 * @property size | ||
|  | 			 * @type Number | ||
|  | 			 */ | ||
|  | 			size: file.size || file.fileSize, | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * Original file size in bytes. | ||
|  | 			 * | ||
|  | 			 * @property origSize | ||
|  | 			 * @type Number | ||
|  | 			 */ | ||
|  | 			origSize: file.size || file.fileSize, | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * Number of bytes uploaded of the files total size. | ||
|  | 			 * | ||
|  | 			 * @property loaded | ||
|  | 			 * @type Number | ||
|  | 			 */ | ||
|  | 			loaded: 0, | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * Number of percentage uploaded of the file. | ||
|  | 			 * | ||
|  | 			 * @property percent | ||
|  | 			 * @type Number | ||
|  | 			 */ | ||
|  | 			percent: 0, | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE. | ||
|  | 			 * | ||
|  | 			 * @property status | ||
|  | 			 * @type Number | ||
|  | 			 * @see plupload | ||
|  | 			 */ | ||
|  | 			status: plupload.QUEUED, | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * Date of last modification. | ||
|  | 			 * | ||
|  | 			 * @property lastModifiedDate | ||
|  | 			 * @type {String} | ||
|  | 			 */ | ||
|  | 			lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
 | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * Returns native window.File object, when it's available. | ||
|  | 			 * | ||
|  | 			 * @method getNative | ||
|  | 			 * @return {window.File} or null, if plupload.File is of different origin | ||
|  | 			 */ | ||
|  | 			getNative: function() { | ||
|  | 				var file = this.getSource().getSource(); | ||
|  | 				return o.inArray(o.typeOf(file), ['blob', 'file']) !== -1 ? file : null; | ||
|  | 			}, | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * Returns mOxie.File - unified wrapper object that can be used across runtimes. | ||
|  | 			 * | ||
|  | 			 * @method getSource | ||
|  | 			 * @return {mOxie.File} or null | ||
|  | 			 */ | ||
|  | 			getSource: function() { | ||
|  | 				if (!filepool[this.id]) { | ||
|  | 					return null; | ||
|  | 				} | ||
|  | 				return filepool[this.id]; | ||
|  | 			}, | ||
|  | 
 | ||
|  | 			/** | ||
|  | 			 * Destroys plupload.File object. | ||
|  | 			 * | ||
|  | 			 * @method destroy | ||
|  | 			 */ | ||
|  | 			destroy: function() { | ||
|  | 				var src = this.getSource(); | ||
|  | 				if (src) { | ||
|  | 					src.destroy(); | ||
|  | 					delete filepool[this.id]; | ||
|  | 				} | ||
|  | 			} | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		filepool[this.id] = file; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return PluploadFile; | ||
|  | }()); | ||
|  | 
 | ||
|  | 
 | ||
|  | /** | ||
|  |  * Constructs a queue progress. | ||
|  |  * | ||
|  |  * @class QueueProgress | ||
|  |  * @constructor | ||
|  |  */ | ||
|  |  plupload.QueueProgress = function() { | ||
|  | 	var self = this; // Setup alias for self to reduce code size when it's compressed
 | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Total queue file size. | ||
|  | 	 * | ||
|  | 	 * @property size | ||
|  | 	 * @type Number | ||
|  | 	 */ | ||
|  | 	self.size = 0; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Total bytes uploaded. | ||
|  | 	 * | ||
|  | 	 * @property loaded | ||
|  | 	 * @type Number | ||
|  | 	 */ | ||
|  | 	self.loaded = 0; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Number of files uploaded. | ||
|  | 	 * | ||
|  | 	 * @property uploaded | ||
|  | 	 * @type Number | ||
|  | 	 */ | ||
|  | 	self.uploaded = 0; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Number of files failed to upload. | ||
|  | 	 * | ||
|  | 	 * @property failed | ||
|  | 	 * @type Number | ||
|  | 	 */ | ||
|  | 	self.failed = 0; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Number of files yet to be uploaded. | ||
|  | 	 * | ||
|  | 	 * @property queued | ||
|  | 	 * @type Number | ||
|  | 	 */ | ||
|  | 	self.queued = 0; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Total percent of the uploaded bytes. | ||
|  | 	 * | ||
|  | 	 * @property percent | ||
|  | 	 * @type Number | ||
|  | 	 */ | ||
|  | 	self.percent = 0; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Bytes uploaded per second. | ||
|  | 	 * | ||
|  | 	 * @property bytesPerSec | ||
|  | 	 * @type Number | ||
|  | 	 */ | ||
|  | 	self.bytesPerSec = 0; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Resets the progress to its initial values. | ||
|  | 	 * | ||
|  | 	 * @method reset | ||
|  | 	 */ | ||
|  | 	self.reset = function() { | ||
|  | 		self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0; | ||
|  | 	}; | ||
|  | }; | ||
|  | 
 | ||
|  | window.plupload = plupload; | ||
|  | 
 | ||
|  | }(window, mOxie)); |