github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/libs/plupload-2.1.2/js/moxie.js (about)

     1  /**
     2   * mOxie - multi-runtime File API & XMLHttpRequest L2 Polyfill
     3   * v1.2.1
     4   *
     5   * Copyright 2013, Moxiecode Systems AB
     6   * Released under GPL License.
     7   *
     8   * License: http://www.plupload.com/license
     9   * Contributing: http://www.plupload.com/contributing
    10   *
    11   * Date: 2014-05-14
    12   */
    13  /**
    14   * Compiled inline version. (Library mode)
    15   */
    16  
    17  /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
    18  /*globals $code */
    19  
    20  (function(exports, undefined) {
    21  	"use strict";
    22  
    23  	var modules = {};
    24  
    25  	function require(ids, callback) {
    26  		var module, defs = [];
    27  
    28  		for (var i = 0; i < ids.length; ++i) {
    29  			module = modules[ids[i]] || resolve(ids[i]);
    30  			if (!module) {
    31  				throw 'module definition dependecy not found: ' + ids[i];
    32  			}
    33  
    34  			defs.push(module);
    35  		}
    36  
    37  		callback.apply(null, defs);
    38  	}
    39  
    40  	function define(id, dependencies, definition) {
    41  		if (typeof id !== 'string') {
    42  			throw 'invalid module definition, module id must be defined and be a string';
    43  		}
    44  
    45  		if (dependencies === undefined) {
    46  			throw 'invalid module definition, dependencies must be specified';
    47  		}
    48  
    49  		if (definition === undefined) {
    50  			throw 'invalid module definition, definition function must be specified';
    51  		}
    52  
    53  		require(dependencies, function() {
    54  			modules[id] = definition.apply(null, arguments);
    55  		});
    56  	}
    57  
    58  	function defined(id) {
    59  		return !!modules[id];
    60  	}
    61  
    62  	function resolve(id) {
    63  		var target = exports;
    64  		var fragments = id.split(/[.\/]/);
    65  
    66  		for (var fi = 0; fi < fragments.length; ++fi) {
    67  			if (!target[fragments[fi]]) {
    68  				return;
    69  			}
    70  
    71  			target = target[fragments[fi]];
    72  		}
    73  
    74  		return target;
    75  	}
    76  
    77  	function expose(ids) {
    78  		for (var i = 0; i < ids.length; i++) {
    79  			var target = exports;
    80  			var id = ids[i];
    81  			var fragments = id.split(/[.\/]/);
    82  
    83  			for (var fi = 0; fi < fragments.length - 1; ++fi) {
    84  				if (target[fragments[fi]] === undefined) {
    85  					target[fragments[fi]] = {};
    86  				}
    87  
    88  				target = target[fragments[fi]];
    89  			}
    90  
    91  			target[fragments[fragments.length - 1]] = modules[id];
    92  		}
    93  	}
    94  
    95  // Included from: src/javascript/core/utils/Basic.js
    96  
    97  /**
    98   * Basic.js
    99   *
   100   * Copyright 2013, Moxiecode Systems AB
   101   * Released under GPL License.
   102   *
   103   * License: http://www.plupload.com/license
   104   * Contributing: http://www.plupload.com/contributing
   105   */
   106  
   107  define('moxie/core/utils/Basic', [], function() {
   108  	/**
   109  	Gets the true type of the built-in object (better version of typeof).
   110  	@author Angus Croll (http://javascriptweblog.wordpress.com/)
   111  
   112  	@method typeOf
   113  	@for Utils
   114  	@static
   115  	@param {Object} o Object to check.
   116  	@return {String} Object [[Class]]
   117  	*/
   118  	var typeOf = function(o) {
   119  		var undef;
   120  
   121  		if (o === undef) {
   122  			return 'undefined';
   123  		} else if (o === null) {
   124  			return 'null';
   125  		} else if (o.nodeType) {
   126  			return 'node';
   127  		}
   128  
   129  		// the snippet below is awesome, however it fails to detect null, undefined and arguments types in IE lte 8
   130  		return ({}).toString.call(o).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
   131  	};
   132  		
   133  	/**
   134  	Extends the specified object with another object.
   135  
   136  	@method extend
   137  	@static
   138  	@param {Object} target Object to extend.
   139  	@param {Object} [obj]* Multiple objects to extend with.
   140  	@return {Object} Same as target, the extended object.
   141  	*/
   142  	var extend = function(target) {
   143  		var undef;
   144  
   145  		each(arguments, function(arg, i) {
   146  			if (i > 0) {
   147  				each(arg, function(value, key) {
   148  					if (value !== undef) {
   149  						if (typeOf(target[key]) === typeOf(value) && !!~inArray(typeOf(value), ['array', 'object'])) {
   150  							extend(target[key], value);
   151  						} else {
   152  							target[key] = value;
   153  						}
   154  					}
   155  				});
   156  			}
   157  		});
   158  		return target;
   159  	};
   160  		
   161  	/**
   162  	Executes the callback function for each item in array/object. If you return false in the
   163  	callback it will break the loop.
   164  
   165  	@method each
   166  	@static
   167  	@param {Object} obj Object to iterate.
   168  	@param {function} callback Callback function to execute for each item.
   169  	*/
   170  	var each = function(obj, callback) {
   171  		var length, key, i, undef;
   172  
   173  		if (obj) {
   174  			try {
   175  				length = obj.length;
   176  			} catch(ex) {
   177  				length = undef;
   178  			}
   179  
   180  			if (length === undef) {
   181  				// Loop object items
   182  				for (key in obj) {
   183  					if (obj.hasOwnProperty(key)) {
   184  						if (callback(obj[key], key) === false) {
   185  							return;
   186  						}
   187  					}
   188  				}
   189  			} else {
   190  				// Loop array items
   191  				for (i = 0; i < length; i++) {
   192  					if (callback(obj[i], i) === false) {
   193  						return;
   194  					}
   195  				}
   196  			}
   197  		}
   198  	};
   199  
   200  	/**
   201  	Checks if object is empty.
   202  	
   203  	@method isEmptyObj
   204  	@static
   205  	@param {Object} o Object to check.
   206  	@return {Boolean}
   207  	*/
   208  	var isEmptyObj = function(obj) {
   209  		var prop;
   210  
   211  		if (!obj || typeOf(obj) !== 'object') {
   212  			return true;
   213  		}
   214  
   215  		for (prop in obj) {
   216  			return false;
   217  		}
   218  
   219  		return true;
   220  	};
   221  
   222  	/**
   223  	Recieve an array of functions (usually async) to call in sequence, each  function
   224  	receives a callback as first argument that it should call, when it completes. Finally,
   225  	after everything is complete, main callback is called. Passing truthy value to the
   226  	callback as a first argument will interrupt the sequence and invoke main callback
   227  	immediately.
   228  
   229  	@method inSeries
   230  	@static
   231  	@param {Array} queue Array of functions to call in sequence
   232  	@param {Function} cb Main callback that is called in the end, or in case of error
   233  	*/
   234  	var inSeries = function(queue, cb) {
   235  		var i = 0, length = queue.length;
   236  
   237  		if (typeOf(cb) !== 'function') {
   238  			cb = function() {};
   239  		}
   240  
   241  		if (!queue || !queue.length) {
   242  			cb();
   243  		}
   244  
   245  		function callNext(i) {
   246  			if (typeOf(queue[i]) === 'function') {
   247  				queue[i](function(error) {
   248  					/*jshint expr:true */
   249  					++i < length && !error ? callNext(i) : cb(error);
   250  				});
   251  			}
   252  		}
   253  		callNext(i);
   254  	};
   255  
   256  
   257  	/**
   258  	Recieve an array of functions (usually async) to call in parallel, each  function
   259  	receives a callback as first argument that it should call, when it completes. After 
   260  	everything is complete, main callback is called. Passing truthy value to the
   261  	callback as a first argument will interrupt the process and invoke main callback
   262  	immediately.
   263  
   264  	@method inParallel
   265  	@static
   266  	@param {Array} queue Array of functions to call in sequence
   267  	@param {Function} cb Main callback that is called in the end, or in case of erro
   268  	*/
   269  	var inParallel = function(queue, cb) {
   270  		var count = 0, num = queue.length, cbArgs = new Array(num);
   271  
   272  		each(queue, function(fn, i) {
   273  			fn(function(error) {
   274  				if (error) {
   275  					return cb(error);
   276  				}
   277  				
   278  				var args = [].slice.call(arguments);
   279  				args.shift(); // strip error - undefined or not
   280  
   281  				cbArgs[i] = args;
   282  				count++;
   283  
   284  				if (count === num) {
   285  					cbArgs.unshift(null);
   286  					cb.apply(this, cbArgs);
   287  				} 
   288  			});
   289  		});
   290  	};
   291  	
   292  	
   293  	/**
   294  	Find an element in array and return it's index if present, otherwise return -1.
   295  	
   296  	@method inArray
   297  	@static
   298  	@param {Mixed} needle Element to find
   299  	@param {Array} array
   300  	@return {Int} Index of the element, or -1 if not found
   301  	*/
   302  	var inArray = function(needle, array) {
   303  		if (array) {
   304  			if (Array.prototype.indexOf) {
   305  				return Array.prototype.indexOf.call(array, needle);
   306  			}
   307  		
   308  			for (var i = 0, length = array.length; i < length; i++) {
   309  				if (array[i] === needle) {
   310  					return i;
   311  				}
   312  			}
   313  		}
   314  		return -1;
   315  	};
   316  
   317  
   318  	/**
   319  	Returns elements of first array if they are not present in second. And false - otherwise.
   320  
   321  	@private
   322  	@method arrayDiff
   323  	@param {Array} needles
   324  	@param {Array} array
   325  	@return {Array|Boolean}
   326  	*/
   327  	var arrayDiff = function(needles, array) {
   328  		var diff = [];
   329  
   330  		if (typeOf(needles) !== 'array') {
   331  			needles = [needles];
   332  		}
   333  
   334  		if (typeOf(array) !== 'array') {
   335  			array = [array];
   336  		}
   337  
   338  		for (var i in needles) {
   339  			if (inArray(needles[i], array) === -1) {
   340  				diff.push(needles[i]);
   341  			}	
   342  		}
   343  		return diff.length ? diff : false;
   344  	};
   345  
   346  
   347  	/**
   348  	Find intersection of two arrays.
   349  
   350  	@private
   351  	@method arrayIntersect
   352  	@param {Array} array1
   353  	@param {Array} array2
   354  	@return {Array} Intersection of two arrays or null if there is none
   355  	*/
   356  	var arrayIntersect = function(array1, array2) {
   357  		var result = [];
   358  		each(array1, function(item) {
   359  			if (inArray(item, array2) !== -1) {
   360  				result.push(item);
   361  			}
   362  		});
   363  		return result.length ? result : null;
   364  	};
   365  	
   366  	
   367  	/**
   368  	Forces anything into an array.
   369  	
   370  	@method toArray
   371  	@static
   372  	@param {Object} obj Object with length field.
   373  	@return {Array} Array object containing all items.
   374  	*/
   375  	var toArray = function(obj) {
   376  		var i, arr = [];
   377  
   378  		for (i = 0; i < obj.length; i++) {
   379  			arr[i] = obj[i];
   380  		}
   381  
   382  		return arr;
   383  	};
   384  	
   385  			
   386  	/**
   387  	Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
   388  	The only way a user would be able to get the same ID is if the two persons at the same exact milisecond manages
   389  	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.
   390  	It's more probable for the earth to be hit with an ansteriod. Y
   391  	
   392  	@method guid
   393  	@static
   394  	@param {String} prefix to prepend (by default 'o' will be prepended).
   395  	@method guid
   396  	@return {String} Virtually unique id.
   397  	*/
   398  	var guid = (function() {
   399  		var counter = 0;
   400  		
   401  		return function(prefix) {
   402  			var guid = new Date().getTime().toString(32), i;
   403  
   404  			for (i = 0; i < 5; i++) {
   405  				guid += Math.floor(Math.random() * 65535).toString(32);
   406  			}
   407  			
   408  			return (prefix || 'o_') + guid + (counter++).toString(32);
   409  		};
   410  	}());
   411  	
   412  
   413  	/**
   414  	Trims white spaces around the string
   415  	
   416  	@method trim
   417  	@static
   418  	@param {String} str
   419  	@return {String}
   420  	*/
   421  	var trim = function(str) {
   422  		if (!str) {
   423  			return str;
   424  		}
   425  		return String.prototype.trim ? String.prototype.trim.call(str) : str.toString().replace(/^\s*/, '').replace(/\s*$/, '');
   426  	};
   427  
   428  
   429  	/**
   430  	Parses the specified size string into a byte value. For example 10kb becomes 10240.
   431  	
   432  	@method parseSizeStr
   433  	@static
   434  	@param {String/Number} size String to parse or number to just pass through.
   435  	@return {Number} Size in bytes.
   436  	*/
   437  	var parseSizeStr = function(size) {
   438  		if (typeof(size) !== 'string') {
   439  			return size;
   440  		}
   441  		
   442  		var muls = {
   443  				t: 1099511627776,
   444  				g: 1073741824,
   445  				m: 1048576,
   446  				k: 1024
   447  			},
   448  			mul;
   449  
   450  		size = /^([0-9]+)([mgk]?)$/.exec(size.toLowerCase().replace(/[^0-9mkg]/g, ''));
   451  		mul = size[2];
   452  		size = +size[1];
   453  		
   454  		if (muls.hasOwnProperty(mul)) {
   455  			size *= muls[mul];
   456  		}
   457  		return size;
   458  	};
   459  	
   460  
   461  	return {
   462  		guid: guid,
   463  		typeOf: typeOf,
   464  		extend: extend,
   465  		each: each,
   466  		isEmptyObj: isEmptyObj,
   467  		inSeries: inSeries,
   468  		inParallel: inParallel,
   469  		inArray: inArray,
   470  		arrayDiff: arrayDiff,
   471  		arrayIntersect: arrayIntersect,
   472  		toArray: toArray,
   473  		trim: trim,
   474  		parseSizeStr: parseSizeStr
   475  	};
   476  });
   477  
   478  // Included from: src/javascript/core/I18n.js
   479  
   480  /**
   481   * I18n.js
   482   *
   483   * Copyright 2013, Moxiecode Systems AB
   484   * Released under GPL License.
   485   *
   486   * License: http://www.plupload.com/license
   487   * Contributing: http://www.plupload.com/contributing
   488   */
   489  
   490  define("moxie/core/I18n", [
   491  	"moxie/core/utils/Basic"
   492  ], function(Basic) {
   493  	var i18n = {};
   494  
   495  	return {
   496  		/**
   497  		 * Extends the language pack object with new items.
   498  		 *
   499  		 * @param {Object} pack Language pack items to add.
   500  		 * @return {Object} Extended language pack object.
   501  		 */
   502  		addI18n: function(pack) {
   503  			return Basic.extend(i18n, pack);
   504  		},
   505  
   506  		/**
   507  		 * Translates the specified string by checking for the english string in the language pack lookup.
   508  		 *
   509  		 * @param {String} str String to look for.
   510  		 * @return {String} Translated string or the input string if it wasn't found.
   511  		 */
   512  		translate: function(str) {
   513  			return i18n[str] || str;
   514  		},
   515  
   516  		/**
   517  		 * Shortcut for translate function
   518  		 *
   519  		 * @param {String} str String to look for.
   520  		 * @return {String} Translated string or the input string if it wasn't found.
   521  		 */
   522  		_: function(str) {
   523  			return this.translate(str);
   524  		},
   525  
   526  		/**
   527  		 * Pseudo sprintf implementation - simple way to replace tokens with specified values.
   528  		 *
   529  		 * @param {String} str String with tokens
   530  		 * @return {String} String with replaced tokens
   531  		 */
   532  		sprintf: function(str) {
   533  			var args = [].slice.call(arguments, 1);
   534  
   535  			return str.replace(/%[a-z]/g, function() {
   536  				var value = args.shift();
   537  				return Basic.typeOf(value) !== 'undefined' ? value : '';
   538  			});
   539  		}
   540  	};
   541  });
   542  
   543  // Included from: src/javascript/core/utils/Mime.js
   544  
   545  /**
   546   * Mime.js
   547   *
   548   * Copyright 2013, Moxiecode Systems AB
   549   * Released under GPL License.
   550   *
   551   * License: http://www.plupload.com/license
   552   * Contributing: http://www.plupload.com/contributing
   553   */
   554  
   555  define("moxie/core/utils/Mime", [
   556  	"moxie/core/utils/Basic",
   557  	"moxie/core/I18n"
   558  ], function(Basic, I18n) {
   559  	
   560  	var mimeData = "" +
   561  		"application/msword,doc dot," +
   562  		"application/pdf,pdf," +
   563  		"application/pgp-signature,pgp," +
   564  		"application/postscript,ps ai eps," +
   565  		"application/rtf,rtf," +
   566  		"application/vnd.ms-excel,xls xlb," +
   567  		"application/vnd.ms-powerpoint,ppt pps pot," +
   568  		"application/zip,zip," +
   569  		"application/x-shockwave-flash,swf swfl," +
   570  		"application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx," +
   571  		"application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx," +
   572  		"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx," +
   573  		"application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx," +
   574  		"application/vnd.openxmlformats-officedocument.presentationml.template,potx," +
   575  		"application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx," +
   576  		"application/x-javascript,js," +
   577  		"application/json,json," +
   578  		"audio/mpeg,mp3 mpga mpega mp2," +
   579  		"audio/x-wav,wav," +
   580  		"audio/x-m4a,m4a," +
   581  		"audio/ogg,oga ogg," +
   582  		"audio/aiff,aiff aif," +
   583  		"audio/flac,flac," +
   584  		"audio/aac,aac," +
   585  		"audio/ac3,ac3," +
   586  		"audio/x-ms-wma,wma," +
   587  		"image/bmp,bmp," +
   588  		"image/gif,gif," +
   589  		"image/jpeg,jpg jpeg jpe," +
   590  		"image/photoshop,psd," +
   591  		"image/png,png," +
   592  		"image/svg+xml,svg svgz," +
   593  		"image/tiff,tiff tif," +
   594  		"text/plain,asc txt text diff log," +
   595  		"text/html,htm html xhtml," +
   596  		"text/css,css," +
   597  		"text/csv,csv," +
   598  		"text/rtf,rtf," +
   599  		"video/mpeg,mpeg mpg mpe m2v," +
   600  		"video/quicktime,qt mov," +
   601  		"video/mp4,mp4," +
   602  		"video/x-m4v,m4v," +
   603  		"video/x-flv,flv," +
   604  		"video/x-ms-wmv,wmv," +
   605  		"video/avi,avi," +
   606  		"video/webm,webm," +
   607  		"video/3gpp,3gpp 3gp," +
   608  		"video/3gpp2,3g2," +
   609  		"video/vnd.rn-realvideo,rv," +
   610  		"video/ogg,ogv," + 
   611  		"video/x-matroska,mkv," +
   612  		"application/vnd.oasis.opendocument.formula-template,otf," +
   613  		"application/octet-stream,exe";
   614  	
   615  	
   616  	var Mime = {
   617  
   618  		mimes: {},
   619  
   620  		extensions: {},
   621  
   622  		// Parses the default mime types string into a mimes and extensions lookup maps
   623  		addMimeType: function (mimeData) {
   624  			var items = mimeData.split(/,/), i, ii, ext;
   625  			
   626  			for (i = 0; i < items.length; i += 2) {
   627  				ext = items[i + 1].split(/ /);
   628  
   629  				// extension to mime lookup
   630  				for (ii = 0; ii < ext.length; ii++) {
   631  					this.mimes[ext[ii]] = items[i];
   632  				}
   633  				// mime to extension lookup
   634  				this.extensions[items[i]] = ext;
   635  			}
   636  		},
   637  
   638  
   639  		extList2mimes: function (filters, addMissingExtensions) {
   640  			var self = this, ext, i, ii, type, mimes = [];
   641  			
   642  			// convert extensions to mime types list
   643  			for (i = 0; i < filters.length; i++) {
   644  				ext = filters[i].extensions.split(/\s*,\s*/);
   645  
   646  				for (ii = 0; ii < ext.length; ii++) {
   647  					
   648  					// if there's an asterisk in the list, then accept attribute is not required
   649  					if (ext[ii] === '*') {
   650  						return [];
   651  					}
   652  					
   653  					type = self.mimes[ext[ii]];
   654  					if (!type) {
   655  						if (addMissingExtensions && /^\w+$/.test(ext[ii])) {
   656  							mimes.push('.' + ext[ii]);
   657  						} else {
   658  							return []; // accept all
   659  						}
   660  					} else if (Basic.inArray(type, mimes) === -1) {
   661  						mimes.push(type);
   662  					}
   663  				}
   664  			}
   665  			return mimes;
   666  		},
   667  
   668  
   669  		mimes2exts: function(mimes) {
   670  			var self = this, exts = [];
   671  			
   672  			Basic.each(mimes, function(mime) {
   673  				if (mime === '*') {
   674  					exts = [];
   675  					return false;
   676  				}
   677  
   678  				// check if this thing looks like mime type
   679  				var m = mime.match(/^(\w+)\/(\*|\w+)$/);
   680  				if (m) {
   681  					if (m[2] === '*') { 
   682  						// wildcard mime type detected
   683  						Basic.each(self.extensions, function(arr, mime) {
   684  							if ((new RegExp('^' + m[1] + '/')).test(mime)) {
   685  								[].push.apply(exts, self.extensions[mime]);
   686  							}
   687  						});
   688  					} else if (self.extensions[mime]) {
   689  						[].push.apply(exts, self.extensions[mime]);
   690  					}
   691  				}
   692  			});
   693  			return exts;
   694  		},
   695  
   696  
   697  		mimes2extList: function(mimes) {
   698  			var accept = [], exts = [];
   699  
   700  			if (Basic.typeOf(mimes) === 'string') {
   701  				mimes = Basic.trim(mimes).split(/\s*,\s*/);
   702  			}
   703  
   704  			exts = this.mimes2exts(mimes);
   705  			
   706  			accept.push({
   707  				title: I18n.translate('Files'),
   708  				extensions: exts.length ? exts.join(',') : '*'
   709  			});
   710  			
   711  			// save original mimes string
   712  			accept.mimes = mimes;
   713  
   714  			return accept;
   715  		},
   716  
   717  
   718  		getFileExtension: function(fileName) {
   719  			var matches = fileName && fileName.match(/\.([^.]+)$/);
   720  			if (matches) {
   721  				return matches[1].toLowerCase();
   722  			}
   723  			return '';
   724  		},
   725  
   726  		getFileMime: function(fileName) {
   727  			return this.mimes[this.getFileExtension(fileName)] || '';
   728  		}
   729  	};
   730  
   731  	Mime.addMimeType(mimeData);
   732  
   733  	return Mime;
   734  });
   735  
   736  // Included from: src/javascript/core/utils/Env.js
   737  
   738  /**
   739   * Env.js
   740   *
   741   * Copyright 2013, Moxiecode Systems AB
   742   * Released under GPL License.
   743   *
   744   * License: http://www.plupload.com/license
   745   * Contributing: http://www.plupload.com/contributing
   746   */
   747  
   748  define("moxie/core/utils/Env", [
   749  	"moxie/core/utils/Basic"
   750  ], function(Basic) {
   751  	
   752  	// UAParser.js v0.6.2
   753  	// Lightweight JavaScript-based User-Agent string parser
   754  	// https://github.com/faisalman/ua-parser-js
   755  	//
   756  	// Copyright © 2012-2013 Faisalman <fyzlman@gmail.com>
   757  	// Dual licensed under GPLv2 & MIT
   758  
   759  	var UAParser = (function (undefined) {
   760  
   761  	    //////////////
   762  	    // Constants
   763  	    /////////////
   764  
   765  
   766  	    var EMPTY       = '',
   767  	        UNKNOWN     = '?',
   768  	        FUNC_TYPE   = 'function',
   769  	        UNDEF_TYPE  = 'undefined',
   770  	        OBJ_TYPE    = 'object',
   771  	        MAJOR       = 'major',
   772  	        MODEL       = 'model',
   773  	        NAME        = 'name',
   774  	        TYPE        = 'type',
   775  	        VENDOR      = 'vendor',
   776  	        VERSION     = 'version',
   777  	        ARCHITECTURE= 'architecture',
   778  	        CONSOLE     = 'console',
   779  	        MOBILE      = 'mobile',
   780  	        TABLET      = 'tablet';
   781  
   782  
   783  	    ///////////
   784  	    // Helper
   785  	    //////////
   786  
   787  
   788  	    var util = {
   789  	        has : function (str1, str2) {
   790  	            return str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1;
   791  	        },
   792  	        lowerize : function (str) {
   793  	            return str.toLowerCase();
   794  	        }
   795  	    };
   796  
   797  
   798  	    ///////////////
   799  	    // Map helper
   800  	    //////////////
   801  
   802  
   803  	    var mapper = {
   804  
   805  	        rgx : function () {
   806  
   807  	            // loop through all regexes maps
   808  	            for (var result, i = 0, j, k, p, q, matches, match, args = arguments; i < args.length; i += 2) {
   809  
   810  	                var regex = args[i],       // even sequence (0,2,4,..)
   811  	                    props = args[i + 1];   // odd sequence (1,3,5,..)
   812  
   813  	                // construct object barebones
   814  	                if (typeof(result) === UNDEF_TYPE) {
   815  	                    result = {};
   816  	                    for (p in props) {
   817  	                        q = props[p];
   818  	                        if (typeof(q) === OBJ_TYPE) {
   819  	                            result[q[0]] = undefined;
   820  	                        } else {
   821  	                            result[q] = undefined;
   822  	                        }
   823  	                    }
   824  	                }
   825  
   826  	                // try matching uastring with regexes
   827  	                for (j = k = 0; j < regex.length; j++) {
   828  	                    matches = regex[j].exec(this.getUA());
   829  	                    if (!!matches) {
   830  	                        for (p = 0; p < props.length; p++) {
   831  	                            match = matches[++k];
   832  	                            q = props[p];
   833  	                            // check if given property is actually array
   834  	                            if (typeof(q) === OBJ_TYPE && q.length > 0) {
   835  	                                if (q.length == 2) {
   836  	                                    if (typeof(q[1]) == FUNC_TYPE) {
   837  	                                        // assign modified match
   838  	                                        result[q[0]] = q[1].call(this, match);
   839  	                                    } else {
   840  	                                        // assign given value, ignore regex match
   841  	                                        result[q[0]] = q[1];
   842  	                                    }
   843  	                                } else if (q.length == 3) {
   844  	                                    // check whether function or regex
   845  	                                    if (typeof(q[1]) === FUNC_TYPE && !(q[1].exec && q[1].test)) {
   846  	                                        // call function (usually string mapper)
   847  	                                        result[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
   848  	                                    } else {
   849  	                                        // sanitize match using given regex
   850  	                                        result[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
   851  	                                    }
   852  	                                } else if (q.length == 4) {
   853  	                                        result[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
   854  	                                }
   855  	                            } else {
   856  	                                result[q] = match ? match : undefined;
   857  	                            }
   858  	                        }
   859  	                        break;
   860  	                    }
   861  	                }
   862  
   863  	                if(!!matches) break; // break the loop immediately if match found
   864  	            }
   865  	            return result;
   866  	        },
   867  
   868  	        str : function (str, map) {
   869  
   870  	            for (var i in map) {
   871  	                // check if array
   872  	                if (typeof(map[i]) === OBJ_TYPE && map[i].length > 0) {
   873  	                    for (var j = 0; j < map[i].length; j++) {
   874  	                        if (util.has(map[i][j], str)) {
   875  	                            return (i === UNKNOWN) ? undefined : i;
   876  	                        }
   877  	                    }
   878  	                } else if (util.has(map[i], str)) {
   879  	                    return (i === UNKNOWN) ? undefined : i;
   880  	                }
   881  	            }
   882  	            return str;
   883  	        }
   884  	    };
   885  
   886  
   887  	    ///////////////
   888  	    // String map
   889  	    //////////////
   890  
   891  
   892  	    var maps = {
   893  
   894  	        browser : {
   895  	            oldsafari : {
   896  	                major : {
   897  	                    '1' : ['/8', '/1', '/3'],
   898  	                    '2' : '/4',
   899  	                    '?' : '/'
   900  	                },
   901  	                version : {
   902  	                    '1.0'   : '/8',
   903  	                    '1.2'   : '/1',
   904  	                    '1.3'   : '/3',
   905  	                    '2.0'   : '/412',
   906  	                    '2.0.2' : '/416',
   907  	                    '2.0.3' : '/417',
   908  	                    '2.0.4' : '/419',
   909  	                    '?'     : '/'
   910  	                }
   911  	            }
   912  	        },
   913  
   914  	        device : {
   915  	            sprint : {
   916  	                model : {
   917  	                    'Evo Shift 4G' : '7373KT'
   918  	                },
   919  	                vendor : {
   920  	                    'HTC'       : 'APA',
   921  	                    'Sprint'    : 'Sprint'
   922  	                }
   923  	            }
   924  	        },
   925  
   926  	        os : {
   927  	            windows : {
   928  	                version : {
   929  	                    'ME'        : '4.90',
   930  	                    'NT 3.11'   : 'NT3.51',
   931  	                    'NT 4.0'    : 'NT4.0',
   932  	                    '2000'      : 'NT 5.0',
   933  	                    'XP'        : ['NT 5.1', 'NT 5.2'],
   934  	                    'Vista'     : 'NT 6.0',
   935  	                    '7'         : 'NT 6.1',
   936  	                    '8'         : 'NT 6.2',
   937  	                    '8.1'       : 'NT 6.3',
   938  	                    'RT'        : 'ARM'
   939  	                }
   940  	            }
   941  	        }
   942  	    };
   943  
   944  
   945  	    //////////////
   946  	    // Regex map
   947  	    /////////////
   948  
   949  
   950  	    var regexes = {
   951  
   952  	        browser : [[
   953  
   954  	            // Presto based
   955  	            /(opera\smini)\/((\d+)?[\w\.-]+)/i,                                 // Opera Mini
   956  	            /(opera\s[mobiletab]+).+version\/((\d+)?[\w\.-]+)/i,                // Opera Mobi/Tablet
   957  	            /(opera).+version\/((\d+)?[\w\.]+)/i,                               // Opera > 9.80
   958  	            /(opera)[\/\s]+((\d+)?[\w\.]+)/i                                    // Opera < 9.80
   959  	            
   960  	            ], [NAME, VERSION, MAJOR], [
   961  
   962  	            /\s(opr)\/((\d+)?[\w\.]+)/i                                         // Opera Webkit
   963  	            ], [[NAME, 'Opera'], VERSION, MAJOR], [
   964  
   965  	            // Mixed
   966  	            /(kindle)\/((\d+)?[\w\.]+)/i,                                       // Kindle
   967  	            /(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?((\d+)?[\w\.]+)*/i,
   968  	                                                                                // Lunascape/Maxthon/Netfront/Jasmine/Blazer
   969  
   970  	            // Trident based
   971  	            /(avant\s|iemobile|slim|baidu)(?:browser)?[\/\s]?((\d+)?[\w\.]*)/i,
   972  	                                                                                // Avant/IEMobile/SlimBrowser/Baidu
   973  	            /(?:ms|\()(ie)\s((\d+)?[\w\.]+)/i,                                  // Internet Explorer
   974  
   975  	            // Webkit/KHTML based
   976  	            /(rekonq)((?:\/)[\w\.]+)*/i,                                        // Rekonq
   977  	            /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron)\/((\d+)?[\w\.-]+)/i
   978  	                                                                                // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron
   979  	            ], [NAME, VERSION, MAJOR], [
   980  
   981  	            /(trident).+rv[:\s]((\d+)?[\w\.]+).+like\sgecko/i                   // IE11
   982  	            ], [[NAME, 'IE'], VERSION, MAJOR], [
   983  
   984  	            /(yabrowser)\/((\d+)?[\w\.]+)/i                                     // Yandex
   985  	            ], [[NAME, 'Yandex'], VERSION, MAJOR], [
   986  
   987  	            /(comodo_dragon)\/((\d+)?[\w\.]+)/i                                 // Comodo Dragon
   988  	            ], [[NAME, /_/g, ' '], VERSION, MAJOR], [
   989  
   990  	            /(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?((\d+)?[\w\.]+)/i
   991  	                                                                                // Chrome/OmniWeb/Arora/Tizen/Nokia
   992  	            ], [NAME, VERSION, MAJOR], [
   993  
   994  	            /(dolfin)\/((\d+)?[\w\.]+)/i                                        // Dolphin
   995  	            ], [[NAME, 'Dolphin'], VERSION, MAJOR], [
   996  
   997  	            /((?:android.+)crmo|crios)\/((\d+)?[\w\.]+)/i                       // Chrome for Android/iOS
   998  	            ], [[NAME, 'Chrome'], VERSION, MAJOR], [
   999  
  1000  	            /((?:android.+))version\/((\d+)?[\w\.]+)\smobile\ssafari/i          // Android Browser
  1001  	            ], [[NAME, 'Android Browser'], VERSION, MAJOR], [
  1002  
  1003  	            /version\/((\d+)?[\w\.]+).+?mobile\/\w+\s(safari)/i                 // Mobile Safari
  1004  	            ], [VERSION, MAJOR, [NAME, 'Mobile Safari']], [
  1005  
  1006  	            /version\/((\d+)?[\w\.]+).+?(mobile\s?safari|safari)/i              // Safari & Safari Mobile
  1007  	            ], [VERSION, MAJOR, NAME], [
  1008  
  1009  	            /webkit.+?(mobile\s?safari|safari)((\/[\w\.]+))/i                   // Safari < 3.0
  1010  	            ], [NAME, [MAJOR, mapper.str, maps.browser.oldsafari.major], [VERSION, mapper.str, maps.browser.oldsafari.version]], [
  1011  
  1012  	            /(konqueror)\/((\d+)?[\w\.]+)/i,                                    // Konqueror
  1013  	            /(webkit|khtml)\/((\d+)?[\w\.]+)/i
  1014  	            ], [NAME, VERSION, MAJOR], [
  1015  
  1016  	            // Gecko based
  1017  	            /(navigator|netscape)\/((\d+)?[\w\.-]+)/i                           // Netscape
  1018  	            ], [[NAME, 'Netscape'], VERSION, MAJOR], [
  1019  	            /(swiftfox)/i,                                                      // Swiftfox
  1020  	            /(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?((\d+)?[\w\.\+]+)/i,
  1021  	                                                                                // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
  1022  	            /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\/((\d+)?[\w\.-]+)/i,
  1023  	                                                                                // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
  1024  	            /(mozilla)\/((\d+)?[\w\.]+).+rv\:.+gecko\/\d+/i,                    // Mozilla
  1025  
  1026  	            // Other
  1027  	            /(uc\s?browser|polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|qqbrowser)[\/\s]?((\d+)?[\w\.]+)/i,
  1028  	                                                                                // UCBrowser/Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/QQBrowser
  1029  	            /(links)\s\(((\d+)?[\w\.]+)/i,                                      // Links
  1030  	            /(gobrowser)\/?((\d+)?[\w\.]+)*/i,                                  // GoBrowser
  1031  	            /(ice\s?browser)\/v?((\d+)?[\w\._]+)/i,                             // ICE Browser
  1032  	            /(mosaic)[\/\s]((\d+)?[\w\.]+)/i                                    // Mosaic
  1033  	            ], [NAME, VERSION, MAJOR]
  1034  	        ],
  1035  
  1036  	        engine : [[
  1037  
  1038  	            /(presto)\/([\w\.]+)/i,                                             // Presto
  1039  	            /(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\/([\w\.]+)/i,     // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m
  1040  	            /(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i,                          // KHTML/Tasman/Links
  1041  	            /(icab)[\/\s]([23]\.[\d\.]+)/i                                      // iCab
  1042  	            ], [NAME, VERSION], [
  1043  
  1044  	            /rv\:([\w\.]+).*(gecko)/i                                           // Gecko
  1045  	            ], [VERSION, NAME]
  1046  	        ],
  1047  
  1048  	        os : [[
  1049  
  1050  	            // Windows based
  1051  	            /(windows)\snt\s6\.2;\s(arm)/i,                                     // Windows RT
  1052  	            /(windows\sphone(?:\sos)*|windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i
  1053  	            ], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [
  1054  	            /(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i
  1055  	            ], [[NAME, 'Windows'], [VERSION, mapper.str, maps.os.windows.version]], [
  1056  
  1057  	            // Mobile/Embedded OS
  1058  	            /\((bb)(10);/i                                                      // BlackBerry 10
  1059  	            ], [[NAME, 'BlackBerry'], VERSION], [
  1060  	            /(blackberry)\w*\/?([\w\.]+)*/i,                                    // Blackberry
  1061  	            /(tizen)\/([\w\.]+)/i,                                              // Tizen
  1062  	            /(android|webos|palm\os|qnx|bada|rim\stablet\sos|meego)[\/\s-]?([\w\.]+)*/i
  1063  	                                                                                // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo
  1064  	            ], [NAME, VERSION], [
  1065  	            /(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]+)*/i                 // Symbian
  1066  	            ], [[NAME, 'Symbian'], VERSION],[
  1067  	            /mozilla.+\(mobile;.+gecko.+firefox/i                               // Firefox OS
  1068  	            ], [[NAME, 'Firefox OS'], VERSION], [
  1069  
  1070  	            // Console
  1071  	            /(nintendo|playstation)\s([wids3portablevu]+)/i,                    // Nintendo/Playstation
  1072  
  1073  	            // GNU/Linux based
  1074  	            /(mint)[\/\s\(]?(\w+)*/i,                                           // Mint
  1075  	            /(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk)[\/\s-]?([\w\.-]+)*/i,
  1076  	                                                                                // Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware
  1077  	                                                                                // Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk
  1078  	            /(hurd|linux)\s?([\w\.]+)*/i,                                       // Hurd/Linux
  1079  	            /(gnu)\s?([\w\.]+)*/i                                               // GNU
  1080  	            ], [NAME, VERSION], [
  1081  
  1082  	            /(cros)\s[\w]+\s([\w\.]+\w)/i                                       // Chromium OS
  1083  	            ], [[NAME, 'Chromium OS'], VERSION],[
  1084  
  1085  	            // Solaris
  1086  	            /(sunos)\s?([\w\.]+\d)*/i                                           // Solaris
  1087  	            ], [[NAME, 'Solaris'], VERSION], [
  1088  
  1089  	            // BSD based
  1090  	            /\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]+)*/i                   // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly
  1091  	            ], [NAME, VERSION],[
  1092  
  1093  	            /(ip[honead]+)(?:.*os\s*([\w]+)*\slike\smac|;\sopera)/i             // iOS
  1094  	            ], [[NAME, 'iOS'], [VERSION, /_/g, '.']], [
  1095  
  1096  	            /(mac\sos\sx)\s?([\w\s\.]+\w)*/i                                    // Mac OS
  1097  	            ], [NAME, [VERSION, /_/g, '.']], [
  1098  
  1099  	            // Other
  1100  	            /(haiku)\s(\w+)/i,                                                  // Haiku
  1101  	            /(aix)\s((\d)(?=\.|\)|\s)[\w\.]*)*/i,                               // AIX
  1102  	            /(macintosh|mac(?=_powerpc)|plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos)/i,
  1103  	                                                                                // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS
  1104  	            /(unix)\s?([\w\.]+)*/i                                              // UNIX
  1105  	            ], [NAME, VERSION]
  1106  	        ]
  1107  	    };
  1108  
  1109  
  1110  	    /////////////////
  1111  	    // Constructor
  1112  	    ////////////////
  1113  
  1114  
  1115  	    var UAParser = function (uastring) {
  1116  
  1117  	        var ua = uastring || ((window && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
  1118  
  1119  	        this.getBrowser = function () {
  1120  	            return mapper.rgx.apply(this, regexes.browser);
  1121  	        };
  1122  	        this.getEngine = function () {
  1123  	            return mapper.rgx.apply(this, regexes.engine);
  1124  	        };
  1125  	        this.getOS = function () {
  1126  	            return mapper.rgx.apply(this, regexes.os);
  1127  	        };
  1128  	        this.getResult = function() {
  1129  	            return {
  1130  	                ua      : this.getUA(),
  1131  	                browser : this.getBrowser(),
  1132  	                engine  : this.getEngine(),
  1133  	                os      : this.getOS()
  1134  	            };
  1135  	        };
  1136  	        this.getUA = function () {
  1137  	            return ua;
  1138  	        };
  1139  	        this.setUA = function (uastring) {
  1140  	            ua = uastring;
  1141  	            return this;
  1142  	        };
  1143  	        this.setUA(ua);
  1144  	    };
  1145  
  1146  	    return new UAParser().getResult();
  1147  	})();
  1148  
  1149  
  1150  	function version_compare(v1, v2, operator) {
  1151  	  // From: http://phpjs.org/functions
  1152  	  // +      original by: Philippe Jausions (http://pear.php.net/user/jausions)
  1153  	  // +      original by: Aidan Lister (http://aidanlister.com/)
  1154  	  // + reimplemented by: Kankrelune (http://www.webfaktory.info/)
  1155  	  // +      improved by: Brett Zamir (http://brett-zamir.me)
  1156  	  // +      improved by: Scott Baker
  1157  	  // +      improved by: Theriault
  1158  	  // *        example 1: version_compare('8.2.5rc', '8.2.5a');
  1159  	  // *        returns 1: 1
  1160  	  // *        example 2: version_compare('8.2.50', '8.2.52', '<');
  1161  	  // *        returns 2: true
  1162  	  // *        example 3: version_compare('5.3.0-dev', '5.3.0');
  1163  	  // *        returns 3: -1
  1164  	  // *        example 4: version_compare('4.1.0.52','4.01.0.51');
  1165  	  // *        returns 4: 1
  1166  
  1167  	  // Important: compare must be initialized at 0.
  1168  	  var i = 0,
  1169  	    x = 0,
  1170  	    compare = 0,
  1171  	    // vm maps textual PHP versions to negatives so they're less than 0.
  1172  	    // PHP currently defines these as CASE-SENSITIVE. It is important to
  1173  	    // leave these as negatives so that they can come before numerical versions
  1174  	    // and as if no letters were there to begin with.
  1175  	    // (1alpha is < 1 and < 1.1 but > 1dev1)
  1176  	    // If a non-numerical value can't be mapped to this table, it receives
  1177  	    // -7 as its value.
  1178  	    vm = {
  1179  	      'dev': -6,
  1180  	      'alpha': -5,
  1181  	      'a': -5,
  1182  	      'beta': -4,
  1183  	      'b': -4,
  1184  	      'RC': -3,
  1185  	      'rc': -3,
  1186  	      '#': -2,
  1187  	      'p': 1,
  1188  	      'pl': 1
  1189  	    },
  1190  	    // This function will be called to prepare each version argument.
  1191  	    // It replaces every _, -, and + with a dot.
  1192  	    // It surrounds any nonsequence of numbers/dots with dots.
  1193  	    // It replaces sequences of dots with a single dot.
  1194  	    //    version_compare('4..0', '4.0') == 0
  1195  	    // Important: A string of 0 length needs to be converted into a value
  1196  	    // even less than an unexisting value in vm (-7), hence [-8].
  1197  	    // It's also important to not strip spaces because of this.
  1198  	    //   version_compare('', ' ') == 1
  1199  	    prepVersion = function (v) {
  1200  	      v = ('' + v).replace(/[_\-+]/g, '.');
  1201  	      v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
  1202  	      return (!v.length ? [-8] : v.split('.'));
  1203  	    },
  1204  	    // This converts a version component to a number.
  1205  	    // Empty component becomes 0.
  1206  	    // Non-numerical component becomes a negative number.
  1207  	    // Numerical component becomes itself as an integer.
  1208  	    numVersion = function (v) {
  1209  	      return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
  1210  	    };
  1211  
  1212  	  v1 = prepVersion(v1);
  1213  	  v2 = prepVersion(v2);
  1214  	  x = Math.max(v1.length, v2.length);
  1215  	  for (i = 0; i < x; i++) {
  1216  	    if (v1[i] == v2[i]) {
  1217  	      continue;
  1218  	    }
  1219  	    v1[i] = numVersion(v1[i]);
  1220  	    v2[i] = numVersion(v2[i]);
  1221  	    if (v1[i] < v2[i]) {
  1222  	      compare = -1;
  1223  	      break;
  1224  	    } else if (v1[i] > v2[i]) {
  1225  	      compare = 1;
  1226  	      break;
  1227  	    }
  1228  	  }
  1229  	  if (!operator) {
  1230  	    return compare;
  1231  	  }
  1232  
  1233  	  // Important: operator is CASE-SENSITIVE.
  1234  	  // "No operator" seems to be treated as "<."
  1235  	  // Any other values seem to make the function return null.
  1236  	  switch (operator) {
  1237  	  case '>':
  1238  	  case 'gt':
  1239  	    return (compare > 0);
  1240  	  case '>=':
  1241  	  case 'ge':
  1242  	    return (compare >= 0);
  1243  	  case '<=':
  1244  	  case 'le':
  1245  	    return (compare <= 0);
  1246  	  case '==':
  1247  	  case '=':
  1248  	  case 'eq':
  1249  	    return (compare === 0);
  1250  	  case '<>':
  1251  	  case '!=':
  1252  	  case 'ne':
  1253  	    return (compare !== 0);
  1254  	  case '':
  1255  	  case '<':
  1256  	  case 'lt':
  1257  	    return (compare < 0);
  1258  	  default:
  1259  	    return null;
  1260  	  }
  1261  	}
  1262  
  1263  
  1264  	var can = (function() {
  1265  		var caps = {
  1266  				define_property: (function() {
  1267  					/* // currently too much extra code required, not exactly worth it
  1268  					try { // as of IE8, getters/setters are supported only on DOM elements
  1269  						var obj = {};
  1270  						if (Object.defineProperty) {
  1271  							Object.defineProperty(obj, 'prop', {
  1272  								enumerable: true,
  1273  								configurable: true
  1274  							});
  1275  							return true;
  1276  						}
  1277  					} catch(ex) {}
  1278  
  1279  					if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) {
  1280  						return true;
  1281  					}*/
  1282  					return false;
  1283  				}()),
  1284  
  1285  				create_canvas: (function() {
  1286  					// On the S60 and BB Storm, getContext exists, but always returns undefined
  1287  					// so we actually have to call getContext() to verify
  1288  					// github.com/Modernizr/Modernizr/issues/issue/97/
  1289  					var el = document.createElement('canvas');
  1290  					return !!(el.getContext && el.getContext('2d'));
  1291  				}()),
  1292  
  1293  				return_response_type: function(responseType) {
  1294  					try {
  1295  						if (Basic.inArray(responseType, ['', 'text', 'document']) !== -1) {
  1296  							return true;
  1297  						} else if (window.XMLHttpRequest) {
  1298  							var xhr = new XMLHttpRequest();
  1299  							xhr.open('get', '/'); // otherwise Gecko throws an exception
  1300  							if ('responseType' in xhr) {
  1301  								xhr.responseType = responseType;
  1302  								// as of 23.0.1271.64, Chrome switched from throwing exception to merely logging it to the console (why? o why?)
  1303  								if (xhr.responseType !== responseType) {
  1304  									return false;
  1305  								}
  1306  								return true;
  1307  							}
  1308  						}
  1309  					} catch (ex) {}
  1310  					return false;
  1311  				},
  1312  
  1313  				// ideas for this heavily come from Modernizr (http://modernizr.com/)
  1314  				use_data_uri: (function() {
  1315  					var du = new Image();
  1316  
  1317  					du.onload = function() {
  1318  						caps.use_data_uri = (du.width === 1 && du.height === 1);
  1319  					};
  1320  					
  1321  					setTimeout(function() {
  1322  						du.src = "data:image/gif;base64,R0lGODlhAQABAIAAAP8AAAAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
  1323  					}, 1);
  1324  					return false;
  1325  				}()),
  1326  
  1327  				use_data_uri_over32kb: function() { // IE8
  1328  					return caps.use_data_uri && (Env.browser !== 'IE' || Env.version >= 9);
  1329  				},
  1330  
  1331  				use_data_uri_of: function(bytes) {
  1332  					return (caps.use_data_uri && bytes < 33000 || caps.use_data_uri_over32kb());
  1333  				},
  1334  
  1335  				use_fileinput: function() {
  1336  					var el = document.createElement('input');
  1337  					el.setAttribute('type', 'file');
  1338  					return !el.disabled;
  1339  				}
  1340  			};
  1341  
  1342  		return function(cap) {
  1343  			var args = [].slice.call(arguments);
  1344  			args.shift(); // shift of cap
  1345  			return Basic.typeOf(caps[cap]) === 'function' ? caps[cap].apply(this, args) : !!caps[cap];
  1346  		};
  1347  	}());
  1348  
  1349  
  1350  	var Env = {
  1351  		can: can,
  1352  		
  1353  		browser: UAParser.browser.name,
  1354  		version: parseFloat(UAParser.browser.major),
  1355  		os: UAParser.os.name, // everybody intuitively types it in a lowercase for some reason
  1356  		osVersion: UAParser.os.version,
  1357  
  1358  		verComp: version_compare,
  1359  		
  1360  		swf_url: "../flash/Moxie.swf",
  1361  		xap_url: "../silverlight/Moxie.xap",
  1362  		global_event_dispatcher: "moxie.core.EventTarget.instance.dispatchEvent"
  1363  	};
  1364  
  1365  	// for backward compatibility
  1366  	// @deprecated Use `Env.os` instead
  1367  	Env.OS = Env.os;
  1368  
  1369  	return Env;
  1370  });
  1371  
  1372  // Included from: src/javascript/core/utils/Dom.js
  1373  
  1374  /**
  1375   * Dom.js
  1376   *
  1377   * Copyright 2013, Moxiecode Systems AB
  1378   * Released under GPL License.
  1379   *
  1380   * License: http://www.plupload.com/license
  1381   * Contributing: http://www.plupload.com/contributing
  1382   */
  1383  
  1384  define('moxie/core/utils/Dom', ['moxie/core/utils/Env'], function(Env) {
  1385  
  1386  	/**
  1387  	Get DOM Element by it's id.
  1388  
  1389  	@method get
  1390  	@for Utils
  1391  	@param {String} id Identifier of the DOM Element
  1392  	@return {DOMElement}
  1393  	*/
  1394  	var get = function(id) {
  1395  		if (typeof id !== 'string') {
  1396  			return id;
  1397  		}
  1398  		return document.getElementById(id);
  1399  	};
  1400  
  1401  	/**
  1402  	Checks if specified DOM element has specified class.
  1403  
  1404  	@method hasClass
  1405  	@static
  1406  	@param {Object} obj DOM element like object to add handler to.
  1407  	@param {String} name Class name
  1408  	*/
  1409  	var hasClass = function(obj, name) {
  1410  		if (!obj.className) {
  1411  			return false;
  1412  		}
  1413  
  1414  		var regExp = new RegExp("(^|\\s+)"+name+"(\\s+|$)");
  1415  		return regExp.test(obj.className);
  1416  	};
  1417  
  1418  	/**
  1419  	Adds specified className to specified DOM element.
  1420  
  1421  	@method addClass
  1422  	@static
  1423  	@param {Object} obj DOM element like object to add handler to.
  1424  	@param {String} name Class name
  1425  	*/
  1426  	var addClass = function(obj, name) {
  1427  		if (!hasClass(obj, name)) {
  1428  			obj.className = !obj.className ? name : obj.className.replace(/\s+$/, '') + ' ' + name;
  1429  		}
  1430  	};
  1431  
  1432  	/**
  1433  	Removes specified className from specified DOM element.
  1434  
  1435  	@method removeClass
  1436  	@static
  1437  	@param {Object} obj DOM element like object to add handler to.
  1438  	@param {String} name Class name
  1439  	*/
  1440  	var removeClass = function(obj, name) {
  1441  		if (obj.className) {
  1442  			var regExp = new RegExp("(^|\\s+)"+name+"(\\s+|$)");
  1443  			obj.className = obj.className.replace(regExp, function($0, $1, $2) {
  1444  				return $1 === ' ' && $2 === ' ' ? ' ' : '';
  1445  			});
  1446  		}
  1447  	};
  1448  
  1449  	/**
  1450  	Returns a given computed style of a DOM element.
  1451  
  1452  	@method getStyle
  1453  	@static
  1454  	@param {Object} obj DOM element like object.
  1455  	@param {String} name Style you want to get from the DOM element
  1456  	*/
  1457  	var getStyle = function(obj, name) {
  1458  		if (obj.currentStyle) {
  1459  			return obj.currentStyle[name];
  1460  		} else if (window.getComputedStyle) {
  1461  			return window.getComputedStyle(obj, null)[name];
  1462  		}
  1463  	};
  1464  
  1465  
  1466  	/**
  1467  	Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
  1468  
  1469  	@method getPos
  1470  	@static
  1471  	@param {Element} node HTML element or element id to get x, y position from.
  1472  	@param {Element} root Optional root element to stop calculations at.
  1473  	@return {object} Absolute position of the specified element object with x, y fields.
  1474  	*/
  1475  	var getPos = function(node, root) {
  1476  		var x = 0, y = 0, parent, doc = document, nodeRect, rootRect;
  1477  
  1478  		node = node;
  1479  		root = root || doc.body;
  1480  
  1481  		// Returns the x, y cordinate for an element on IE 6 and IE 7
  1482  		function getIEPos(node) {
  1483  			var bodyElm, rect, x = 0, y = 0;
  1484  
  1485  			if (node) {
  1486  				rect = node.getBoundingClientRect();
  1487  				bodyElm = doc.compatMode === "CSS1Compat" ? doc.documentElement : doc.body;
  1488  				x = rect.left + bodyElm.scrollLeft;
  1489  				y = rect.top + bodyElm.scrollTop;
  1490  			}
  1491  
  1492  			return {
  1493  				x : x,
  1494  				y : y
  1495  			};
  1496  		}
  1497  
  1498  		// Use getBoundingClientRect on IE 6 and IE 7 but not on IE 8 in standards mode
  1499  		if (node && node.getBoundingClientRect && Env.browser === 'IE' && (!doc.documentMode || doc.documentMode < 8)) {
  1500  			nodeRect = getIEPos(node);
  1501  			rootRect = getIEPos(root);
  1502  
  1503  			return {
  1504  				x : nodeRect.x - rootRect.x,
  1505  				y : nodeRect.y - rootRect.y
  1506  			};
  1507  		}
  1508  
  1509  		parent = node;
  1510  		while (parent && parent != root && parent.nodeType) {
  1511  			x += parent.offsetLeft || 0;
  1512  			y += parent.offsetTop || 0;
  1513  			parent = parent.offsetParent;
  1514  		}
  1515  
  1516  		parent = node.parentNode;
  1517  		while (parent && parent != root && parent.nodeType) {
  1518  			x -= parent.scrollLeft || 0;
  1519  			y -= parent.scrollTop || 0;
  1520  			parent = parent.parentNode;
  1521  		}
  1522  
  1523  		return {
  1524  			x : x,
  1525  			y : y
  1526  		};
  1527  	};
  1528  
  1529  	/**
  1530  	Returns the size of the specified node in pixels.
  1531  
  1532  	@method getSize
  1533  	@static
  1534  	@param {Node} node Node to get the size of.
  1535  	@return {Object} Object with a w and h property.
  1536  	*/
  1537  	var getSize = function(node) {
  1538  		return {
  1539  			w : node.offsetWidth || node.clientWidth,
  1540  			h : node.offsetHeight || node.clientHeight
  1541  		};
  1542  	};
  1543  
  1544  	return {
  1545  		get: get,
  1546  		hasClass: hasClass,
  1547  		addClass: addClass,
  1548  		removeClass: removeClass,
  1549  		getStyle: getStyle,
  1550  		getPos: getPos,
  1551  		getSize: getSize
  1552  	};
  1553  });
  1554  
  1555  // Included from: src/javascript/core/Exceptions.js
  1556  
  1557  /**
  1558   * Exceptions.js
  1559   *
  1560   * Copyright 2013, Moxiecode Systems AB
  1561   * Released under GPL License.
  1562   *
  1563   * License: http://www.plupload.com/license
  1564   * Contributing: http://www.plupload.com/contributing
  1565   */
  1566  
  1567  define('moxie/core/Exceptions', [
  1568  	'moxie/core/utils/Basic'
  1569  ], function(Basic) {
  1570  	function _findKey(obj, value) {
  1571  		var key;
  1572  		for (key in obj) {
  1573  			if (obj[key] === value) {
  1574  				return key;
  1575  			}
  1576  		}
  1577  		return null;
  1578  	}
  1579  
  1580  	return {
  1581  		RuntimeError: (function() {
  1582  			var namecodes = {
  1583  				NOT_INIT_ERR: 1,
  1584  				NOT_SUPPORTED_ERR: 9,
  1585  				JS_ERR: 4
  1586  			};
  1587  
  1588  			function RuntimeError(code) {
  1589  				this.code = code;
  1590  				this.name = _findKey(namecodes, code);
  1591  				this.message = this.name + ": RuntimeError " + this.code;
  1592  			}
  1593  			
  1594  			Basic.extend(RuntimeError, namecodes);
  1595  			RuntimeError.prototype = Error.prototype;
  1596  			return RuntimeError;
  1597  		}()),
  1598  		
  1599  		OperationNotAllowedException: (function() {
  1600  			
  1601  			function OperationNotAllowedException(code) {
  1602  				this.code = code;
  1603  				this.name = 'OperationNotAllowedException';
  1604  			}
  1605  			
  1606  			Basic.extend(OperationNotAllowedException, {
  1607  				NOT_ALLOWED_ERR: 1
  1608  			});
  1609  			
  1610  			OperationNotAllowedException.prototype = Error.prototype;
  1611  			
  1612  			return OperationNotAllowedException;
  1613  		}()),
  1614  
  1615  		ImageError: (function() {
  1616  			var namecodes = {
  1617  				WRONG_FORMAT: 1,
  1618  				MAX_RESOLUTION_ERR: 2
  1619  			};
  1620  
  1621  			function ImageError(code) {
  1622  				this.code = code;
  1623  				this.name = _findKey(namecodes, code);
  1624  				this.message = this.name + ": ImageError " + this.code;
  1625  			}
  1626  			
  1627  			Basic.extend(ImageError, namecodes);
  1628  			ImageError.prototype = Error.prototype;
  1629  
  1630  			return ImageError;
  1631  		}()),
  1632  
  1633  		FileException: (function() {
  1634  			var namecodes = {
  1635  				NOT_FOUND_ERR: 1,
  1636  				SECURITY_ERR: 2,
  1637  				ABORT_ERR: 3,
  1638  				NOT_READABLE_ERR: 4,
  1639  				ENCODING_ERR: 5,
  1640  				NO_MODIFICATION_ALLOWED_ERR: 6,
  1641  				INVALID_STATE_ERR: 7,
  1642  				SYNTAX_ERR: 8
  1643  			};
  1644  
  1645  			function FileException(code) {
  1646  				this.code = code;
  1647  				this.name = _findKey(namecodes, code);
  1648  				this.message = this.name + ": FileException " + this.code;
  1649  			}
  1650  			
  1651  			Basic.extend(FileException, namecodes);
  1652  			FileException.prototype = Error.prototype;
  1653  			return FileException;
  1654  		}()),
  1655  		
  1656  		DOMException: (function() {
  1657  			var namecodes = {
  1658  				INDEX_SIZE_ERR: 1,
  1659  				DOMSTRING_SIZE_ERR: 2,
  1660  				HIERARCHY_REQUEST_ERR: 3,
  1661  				WRONG_DOCUMENT_ERR: 4,
  1662  				INVALID_CHARACTER_ERR: 5,
  1663  				NO_DATA_ALLOWED_ERR: 6,
  1664  				NO_MODIFICATION_ALLOWED_ERR: 7,
  1665  				NOT_FOUND_ERR: 8,
  1666  				NOT_SUPPORTED_ERR: 9,
  1667  				INUSE_ATTRIBUTE_ERR: 10,
  1668  				INVALID_STATE_ERR: 11,
  1669  				SYNTAX_ERR: 12,
  1670  				INVALID_MODIFICATION_ERR: 13,
  1671  				NAMESPACE_ERR: 14,
  1672  				INVALID_ACCESS_ERR: 15,
  1673  				VALIDATION_ERR: 16,
  1674  				TYPE_MISMATCH_ERR: 17,
  1675  				SECURITY_ERR: 18,
  1676  				NETWORK_ERR: 19,
  1677  				ABORT_ERR: 20,
  1678  				URL_MISMATCH_ERR: 21,
  1679  				QUOTA_EXCEEDED_ERR: 22,
  1680  				TIMEOUT_ERR: 23,
  1681  				INVALID_NODE_TYPE_ERR: 24,
  1682  				DATA_CLONE_ERR: 25
  1683  			};
  1684  
  1685  			function DOMException(code) {
  1686  				this.code = code;
  1687  				this.name = _findKey(namecodes, code);
  1688  				this.message = this.name + ": DOMException " + this.code;
  1689  			}
  1690  			
  1691  			Basic.extend(DOMException, namecodes);
  1692  			DOMException.prototype = Error.prototype;
  1693  			return DOMException;
  1694  		}()),
  1695  		
  1696  		EventException: (function() {
  1697  			function EventException(code) {
  1698  				this.code = code;
  1699  				this.name = 'EventException';
  1700  			}
  1701  			
  1702  			Basic.extend(EventException, {
  1703  				UNSPECIFIED_EVENT_TYPE_ERR: 0
  1704  			});
  1705  			
  1706  			EventException.prototype = Error.prototype;
  1707  			
  1708  			return EventException;
  1709  		}())
  1710  	};
  1711  });
  1712  
  1713  // Included from: src/javascript/core/EventTarget.js
  1714  
  1715  /**
  1716   * EventTarget.js
  1717   *
  1718   * Copyright 2013, Moxiecode Systems AB
  1719   * Released under GPL License.
  1720   *
  1721   * License: http://www.plupload.com/license
  1722   * Contributing: http://www.plupload.com/contributing
  1723   */
  1724  
  1725  define('moxie/core/EventTarget', [
  1726  	'moxie/core/Exceptions',
  1727  	'moxie/core/utils/Basic'
  1728  ], function(x, Basic) {
  1729  	/**
  1730  	Parent object for all event dispatching components and objects
  1731  
  1732  	@class EventTarget
  1733  	@constructor EventTarget
  1734  	*/
  1735  	function EventTarget() {
  1736  		// hash of event listeners by object uid
  1737  		var eventpool = {};
  1738  				
  1739  		Basic.extend(this, {
  1740  			
  1741  			/**
  1742  			Unique id of the event dispatcher, usually overriden by children
  1743  
  1744  			@property uid
  1745  			@type String
  1746  			*/
  1747  			uid: null,
  1748  			
  1749  			/**
  1750  			Can be called from within a child  in order to acquire uniqie id in automated manner
  1751  
  1752  			@method init
  1753  			*/
  1754  			init: function() {
  1755  				if (!this.uid) {
  1756  					this.uid = Basic.guid('uid_');
  1757  				}
  1758  			},
  1759  
  1760  			/**
  1761  			Register a handler to a specific event dispatched by the object
  1762  
  1763  			@method addEventListener
  1764  			@param {String} type Type or basically a name of the event to subscribe to
  1765  			@param {Function} fn Callback function that will be called when event happens
  1766  			@param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
  1767  			@param {Object} [scope=this] A scope to invoke event handler in
  1768  			*/
  1769  			addEventListener: function(type, fn, priority, scope) {
  1770  				var self = this, list;
  1771  				
  1772  				type = Basic.trim(type);
  1773  				
  1774  				if (/\s/.test(type)) {
  1775  					// multiple event types were passed for one handler
  1776  					Basic.each(type.split(/\s+/), function(type) {
  1777  						self.addEventListener(type, fn, priority, scope);
  1778  					});
  1779  					return;
  1780  				}
  1781  				
  1782  				type = type.toLowerCase();
  1783  				priority = parseInt(priority, 10) || 0;
  1784  				
  1785  				list = eventpool[this.uid] && eventpool[this.uid][type] || [];
  1786  				list.push({fn : fn, priority : priority, scope : scope || this});
  1787  				
  1788  				if (!eventpool[this.uid]) {
  1789  					eventpool[this.uid] = {};
  1790  				}
  1791  				eventpool[this.uid][type] = list;
  1792  			},
  1793  			
  1794  			/**
  1795  			Check if any handlers were registered to the specified event
  1796  
  1797  			@method hasEventListener
  1798  			@param {String} type Type or basically a name of the event to check
  1799  			@return {Mixed} Returns a handler if it was found and false, if - not
  1800  			*/
  1801  			hasEventListener: function(type) {
  1802  				return type ? !!(eventpool[this.uid] && eventpool[this.uid][type]) : !!eventpool[this.uid];
  1803  			},
  1804  			
  1805  			/**
  1806  			Unregister the handler from the event, or if former was not specified - unregister all handlers
  1807  
  1808  			@method removeEventListener
  1809  			@param {String} type Type or basically a name of the event
  1810  			@param {Function} [fn] Handler to unregister
  1811  			*/
  1812  			removeEventListener: function(type, fn) {
  1813  				type = type.toLowerCase();
  1814  	
  1815  				var list = eventpool[this.uid] && eventpool[this.uid][type], i;
  1816  	
  1817  				if (list) {
  1818  					if (fn) {
  1819  						for (i = list.length - 1; i >= 0; i--) {
  1820  							if (list[i].fn === fn) {
  1821  								list.splice(i, 1);
  1822  								break;
  1823  							}
  1824  						}
  1825  					} else {
  1826  						list = [];
  1827  					}
  1828  	
  1829  					// delete event list if it has become empty
  1830  					if (!list.length) {
  1831  						delete eventpool[this.uid][type];
  1832  						
  1833  						// and object specific entry in a hash if it has no more listeners attached
  1834  						if (Basic.isEmptyObj(eventpool[this.uid])) {
  1835  							delete eventpool[this.uid];
  1836  						}
  1837  					}
  1838  				}
  1839  			},
  1840  			
  1841  			/**
  1842  			Remove all event handlers from the object
  1843  
  1844  			@method removeAllEventListeners
  1845  			*/
  1846  			removeAllEventListeners: function() {
  1847  				if (eventpool[this.uid]) {
  1848  					delete eventpool[this.uid];
  1849  				}
  1850  			},
  1851  			
  1852  			/**
  1853  			Dispatch the event
  1854  
  1855  			@method dispatchEvent
  1856  			@param {String/Object} Type of event or event object to dispatch
  1857  			@param {Mixed} [...] Variable number of arguments to be passed to a handlers
  1858  			@return {Boolean} true by default and false if any handler returned false
  1859  			*/
  1860  			dispatchEvent: function(type) {
  1861  				var uid, list, args, tmpEvt, evt = {}, result = true, undef;
  1862  				
  1863  				if (Basic.typeOf(type) !== 'string') {
  1864  					// we can't use original object directly (because of Silverlight)
  1865  					tmpEvt = type;
  1866  
  1867  					if (Basic.typeOf(tmpEvt.type) === 'string') {
  1868  						type = tmpEvt.type;
  1869  
  1870  						if (tmpEvt.total !== undef && tmpEvt.loaded !== undef) { // progress event
  1871  							evt.total = tmpEvt.total;
  1872  							evt.loaded = tmpEvt.loaded;
  1873  						}
  1874  						evt.async = tmpEvt.async || false;
  1875  					} else {
  1876  						throw new x.EventException(x.EventException.UNSPECIFIED_EVENT_TYPE_ERR);
  1877  					}
  1878  				}
  1879  				
  1880  				// check if event is meant to be dispatched on an object having specific uid
  1881  				if (type.indexOf('::') !== -1) {
  1882  					(function(arr) {
  1883  						uid = arr[0];
  1884  						type = arr[1];
  1885  					}(type.split('::')));
  1886  				} else {
  1887  					uid = this.uid;
  1888  				}
  1889  				
  1890  				type = type.toLowerCase();
  1891  								
  1892  				list = eventpool[uid] && eventpool[uid][type];
  1893  
  1894  				if (list) {
  1895  					// sort event list by prority
  1896  					list.sort(function(a, b) { return b.priority - a.priority; });
  1897  					
  1898  					args = [].slice.call(arguments);
  1899  					
  1900  					// first argument will be pseudo-event object
  1901  					args.shift();
  1902  					evt.type = type;
  1903  					args.unshift(evt);
  1904  
  1905  					// Dispatch event to all listeners
  1906  					var queue = [];
  1907  					Basic.each(list, function(handler) {
  1908  						// explicitly set the target, otherwise events fired from shims do not get it
  1909  						args[0].target = handler.scope;
  1910  						// if event is marked as async, detach the handler
  1911  						if (evt.async) {
  1912  							queue.push(function(cb) {
  1913  								setTimeout(function() {
  1914  									cb(handler.fn.apply(handler.scope, args) === false);
  1915  								}, 1);
  1916  							});
  1917  						} else {
  1918  							queue.push(function(cb) {
  1919  								cb(handler.fn.apply(handler.scope, args) === false); // if handler returns false stop propagation
  1920  							});
  1921  						}
  1922  					});
  1923  					if (queue.length) {
  1924  						Basic.inSeries(queue, function(err) {
  1925  							result = !err;
  1926  						});
  1927  					}
  1928  				}
  1929  				return result;
  1930  			},
  1931  			
  1932  			/**
  1933  			Alias for addEventListener
  1934  
  1935  			@method bind
  1936  			@protected
  1937  			*/
  1938  			bind: function() {
  1939  				this.addEventListener.apply(this, arguments);
  1940  			},
  1941  			
  1942  			/**
  1943  			Alias for removeEventListener
  1944  
  1945  			@method unbind
  1946  			@protected
  1947  			*/
  1948  			unbind: function() {
  1949  				this.removeEventListener.apply(this, arguments);
  1950  			},
  1951  			
  1952  			/**
  1953  			Alias for removeAllEventListeners
  1954  
  1955  			@method unbindAll
  1956  			@protected
  1957  			*/
  1958  			unbindAll: function() {
  1959  				this.removeAllEventListeners.apply(this, arguments);
  1960  			},
  1961  			
  1962  			/**
  1963  			Alias for dispatchEvent
  1964  
  1965  			@method trigger
  1966  			@protected
  1967  			*/
  1968  			trigger: function() {
  1969  				return this.dispatchEvent.apply(this, arguments);
  1970  			},
  1971  			
  1972  			
  1973  			/**
  1974  			Converts properties of on[event] type to corresponding event handlers,
  1975  			is used to avoid extra hassle around the process of calling them back
  1976  
  1977  			@method convertEventPropsToHandlers
  1978  			@private
  1979  			*/
  1980  			convertEventPropsToHandlers: function(handlers) {
  1981  				var h;
  1982  						
  1983  				if (Basic.typeOf(handlers) !== 'array') {
  1984  					handlers = [handlers];
  1985  				}
  1986  
  1987  				for (var i = 0; i < handlers.length; i++) {
  1988  					h = 'on' + handlers[i];
  1989  					
  1990  					if (Basic.typeOf(this[h]) === 'function') {
  1991  						this.addEventListener(handlers[i], this[h]);
  1992  					} else if (Basic.typeOf(this[h]) === 'undefined') {
  1993  						this[h] = null; // object must have defined event properties, even if it doesn't make use of them
  1994  					}
  1995  				}
  1996  			}
  1997  			
  1998  		});
  1999  	}
  2000  
  2001  	EventTarget.instance = new EventTarget(); 
  2002  
  2003  	return EventTarget;
  2004  });
  2005  
  2006  // Included from: src/javascript/core/utils/Encode.js
  2007  
  2008  /**
  2009   * Encode.js
  2010   *
  2011   * Copyright 2013, Moxiecode Systems AB
  2012   * Released under GPL License.
  2013   *
  2014   * License: http://www.plupload.com/license
  2015   * Contributing: http://www.plupload.com/contributing
  2016   */
  2017  
  2018  define('moxie/core/utils/Encode', [], function() {
  2019  
  2020  	/**
  2021  	Encode string with UTF-8
  2022  
  2023  	@method utf8_encode
  2024  	@for Utils
  2025  	@static
  2026  	@param {String} str String to encode
  2027  	@return {String} UTF-8 encoded string
  2028  	*/
  2029  	var utf8_encode = function(str) {
  2030  		return unescape(encodeURIComponent(str));
  2031  	};
  2032  	
  2033  	/**
  2034  	Decode UTF-8 encoded string
  2035  
  2036  	@method utf8_decode
  2037  	@static
  2038  	@param {String} str String to decode
  2039  	@return {String} Decoded string
  2040  	*/
  2041  	var utf8_decode = function(str_data) {
  2042  		return decodeURIComponent(escape(str_data));
  2043  	};
  2044  	
  2045  	/**
  2046  	Decode Base64 encoded string (uses browser's default method if available),
  2047  	from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_decode.js
  2048  
  2049  	@method atob
  2050  	@static
  2051  	@param {String} data String to decode
  2052  	@return {String} Decoded string
  2053  	*/
  2054  	var atob = function(data, utf8) {
  2055  		if (typeof(window.atob) === 'function') {
  2056  			return utf8 ? utf8_decode(window.atob(data)) : window.atob(data);
  2057  		}
  2058  
  2059  		// http://kevin.vanzonneveld.net
  2060  		// +   original by: Tyler Akins (http://rumkin.com)
  2061  		// +   improved by: Thunder.m
  2062  		// +      input by: Aman Gupta
  2063  		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2064  		// +   bugfixed by: Onno Marsman
  2065  		// +   bugfixed by: Pellentesque Malesuada
  2066  		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2067  		// +      input by: Brett Zamir (http://brett-zamir.me)
  2068  		// +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2069  		// *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
  2070  		// *     returns 1: 'Kevin van Zonneveld'
  2071  		// mozilla has this native
  2072  		// - but breaks in 2.0.0.12!
  2073  		//if (typeof this.window.atob == 'function') {
  2074  		//    return atob(data);
  2075  		//}
  2076  		var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  2077  		var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
  2078  			ac = 0,
  2079  			dec = "",
  2080  			tmp_arr = [];
  2081  
  2082  		if (!data) {
  2083  			return data;
  2084  		}
  2085  
  2086  		data += '';
  2087  
  2088  		do { // unpack four hexets into three octets using index points in b64
  2089  			h1 = b64.indexOf(data.charAt(i++));
  2090  			h2 = b64.indexOf(data.charAt(i++));
  2091  			h3 = b64.indexOf(data.charAt(i++));
  2092  			h4 = b64.indexOf(data.charAt(i++));
  2093  
  2094  			bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
  2095  
  2096  			o1 = bits >> 16 & 0xff;
  2097  			o2 = bits >> 8 & 0xff;
  2098  			o3 = bits & 0xff;
  2099  
  2100  			if (h3 == 64) {
  2101  				tmp_arr[ac++] = String.fromCharCode(o1);
  2102  			} else if (h4 == 64) {
  2103  				tmp_arr[ac++] = String.fromCharCode(o1, o2);
  2104  			} else {
  2105  				tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
  2106  			}
  2107  		} while (i < data.length);
  2108  
  2109  		dec = tmp_arr.join('');
  2110  
  2111  		return utf8 ? utf8_decode(dec) : dec;
  2112  	};
  2113  	
  2114  	/**
  2115  	Base64 encode string (uses browser's default method if available),
  2116  	from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_encode.js
  2117  
  2118  	@method btoa
  2119  	@static
  2120  	@param {String} data String to encode
  2121  	@return {String} Base64 encoded string
  2122  	*/
  2123  	var btoa = function(data, utf8) {
  2124  		if (utf8) {
  2125  			utf8_encode(data);
  2126  		}
  2127  
  2128  		if (typeof(window.btoa) === 'function') {
  2129  			return window.btoa(data);
  2130  		}
  2131  
  2132  		// http://kevin.vanzonneveld.net
  2133  		// +   original by: Tyler Akins (http://rumkin.com)
  2134  		// +   improved by: Bayron Guevara
  2135  		// +   improved by: Thunder.m
  2136  		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2137  		// +   bugfixed by: Pellentesque Malesuada
  2138  		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2139  		// +   improved by: Rafał Kukawski (http://kukawski.pl)
  2140  		// *     example 1: base64_encode('Kevin van Zonneveld');
  2141  		// *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
  2142  		// mozilla has this native
  2143  		// - but breaks in 2.0.0.12!
  2144  		var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  2145  		var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
  2146  			ac = 0,
  2147  			enc = "",
  2148  			tmp_arr = [];
  2149  
  2150  		if (!data) {
  2151  			return data;
  2152  		}
  2153  
  2154  		do { // pack three octets into four hexets
  2155  			o1 = data.charCodeAt(i++);
  2156  			o2 = data.charCodeAt(i++);
  2157  			o3 = data.charCodeAt(i++);
  2158  
  2159  			bits = o1 << 16 | o2 << 8 | o3;
  2160  
  2161  			h1 = bits >> 18 & 0x3f;
  2162  			h2 = bits >> 12 & 0x3f;
  2163  			h3 = bits >> 6 & 0x3f;
  2164  			h4 = bits & 0x3f;
  2165  
  2166  			// use hexets to index into b64, and append result to encoded string
  2167  			tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
  2168  		} while (i < data.length);
  2169  
  2170  		enc = tmp_arr.join('');
  2171  
  2172  		var r = data.length % 3;
  2173  
  2174  		return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
  2175  	};
  2176  
  2177  
  2178  	return {
  2179  		utf8_encode: utf8_encode,
  2180  		utf8_decode: utf8_decode,
  2181  		atob: atob,
  2182  		btoa: btoa
  2183  	};
  2184  });
  2185  
  2186  // Included from: src/javascript/runtime/Runtime.js
  2187  
  2188  /**
  2189   * Runtime.js
  2190   *
  2191   * Copyright 2013, Moxiecode Systems AB
  2192   * Released under GPL License.
  2193   *
  2194   * License: http://www.plupload.com/license
  2195   * Contributing: http://www.plupload.com/contributing
  2196   */
  2197  
  2198  define('moxie/runtime/Runtime', [
  2199  	"moxie/core/utils/Basic",
  2200  	"moxie/core/utils/Dom",
  2201  	"moxie/core/EventTarget"
  2202  ], function(Basic, Dom, EventTarget) {
  2203  	var runtimeConstructors = {}, runtimes = {};
  2204  
  2205  	/**
  2206  	Common set of methods and properties for every runtime instance
  2207  
  2208  	@class Runtime
  2209  
  2210  	@param {Object} options
  2211  	@param {String} type Sanitized name of the runtime
  2212  	@param {Object} [caps] Set of capabilities that differentiate specified runtime
  2213  	@param {Object} [modeCaps] Set of capabilities that do require specific operational mode
  2214  	@param {String} [preferredMode='browser'] Preferred operational mode to choose if no required capabilities were requested
  2215  	*/
  2216  	function Runtime(options, type, caps, modeCaps, preferredMode) {
  2217  		/**
  2218  		Dispatched when runtime is initialized and ready.
  2219  		Results in RuntimeInit on a connected component.
  2220  
  2221  		@event Init
  2222  		*/
  2223  
  2224  		/**
  2225  		Dispatched when runtime fails to initialize.
  2226  		Results in RuntimeError on a connected component.
  2227  
  2228  		@event Error
  2229  		*/
  2230  
  2231  		var self = this
  2232  		, _shim
  2233  		, _uid = Basic.guid(type + '_')
  2234  		, defaultMode = preferredMode || 'browser'
  2235  		;
  2236  
  2237  		options = options || {};
  2238  
  2239  		// register runtime in private hash
  2240  		runtimes[_uid] = this;
  2241  
  2242  		/**
  2243  		Default set of capabilities, which can be redifined later by specific runtime
  2244  
  2245  		@private
  2246  		@property caps
  2247  		@type Object
  2248  		*/
  2249  		caps = Basic.extend({
  2250  			// Runtime can: 
  2251  			// provide access to raw binary data of the file
  2252  			access_binary: false,
  2253  			// provide access to raw binary data of the image (image extension is optional) 
  2254  			access_image_binary: false,
  2255  			// display binary data as thumbs for example
  2256  			display_media: false,
  2257  			// make cross-domain requests
  2258  			do_cors: false,
  2259  			// accept files dragged and dropped from the desktop
  2260  			drag_and_drop: false,
  2261  			// filter files in selection dialog by their extensions
  2262  			filter_by_extension: true,
  2263  			// resize image (and manipulate it raw data of any file in general)
  2264  			resize_image: false,
  2265  			// periodically report how many bytes of total in the file were uploaded (loaded)
  2266  			report_upload_progress: false,
  2267  			// provide access to the headers of http response 
  2268  			return_response_headers: false,
  2269  			// support response of specific type, which should be passed as an argument
  2270  			// e.g. runtime.can('return_response_type', 'blob')
  2271  			return_response_type: false,
  2272  			// return http status code of the response
  2273  			return_status_code: true,
  2274  			// send custom http header with the request
  2275  			send_custom_headers: false,
  2276  			// pick up the files from a dialog
  2277  			select_file: false,
  2278  			// select whole folder in file browse dialog
  2279  			select_folder: false,
  2280  			// select multiple files at once in file browse dialog
  2281  			select_multiple: true,
  2282  			// send raw binary data, that is generated after image resizing or manipulation of other kind
  2283  			send_binary_string: false,
  2284  			// send cookies with http request and therefore retain session
  2285  			send_browser_cookies: true,
  2286  			// send data formatted as multipart/form-data
  2287  			send_multipart: true,
  2288  			// slice the file or blob to smaller parts
  2289  			slice_blob: false,
  2290  			// upload file without preloading it to memory, stream it out directly from disk
  2291  			stream_upload: false,
  2292  			// programmatically trigger file browse dialog
  2293  			summon_file_dialog: false,
  2294  			// upload file of specific size, size should be passed as argument
  2295  			// e.g. runtime.can('upload_filesize', '500mb')
  2296  			upload_filesize: true,
  2297  			// initiate http request with specific http method, method should be passed as argument
  2298  			// e.g. runtime.can('use_http_method', 'put')
  2299  			use_http_method: true
  2300  		}, caps);
  2301  			
  2302  	
  2303  		// default to the mode that is compatible with preferred caps
  2304  		if (options.preferred_caps) {
  2305  			defaultMode = Runtime.getMode(modeCaps, options.preferred_caps, defaultMode);
  2306  		}
  2307  		
  2308  		// small extension factory here (is meant to be extended with actual extensions constructors)
  2309  		_shim = (function() {
  2310  			var objpool = {};
  2311  			return {
  2312  				exec: function(uid, comp, fn, args) {
  2313  					if (_shim[comp]) {
  2314  						if (!objpool[uid]) {
  2315  							objpool[uid] = {
  2316  								context: this,
  2317  								instance: new _shim[comp]()
  2318  							};
  2319  						}
  2320  						if (objpool[uid].instance[fn]) {
  2321  							return objpool[uid].instance[fn].apply(this, args);
  2322  						}
  2323  					}
  2324  				},
  2325  
  2326  				removeInstance: function(uid) {
  2327  					delete objpool[uid];
  2328  				},
  2329  
  2330  				removeAllInstances: function() {
  2331  					var self = this;
  2332  					Basic.each(objpool, function(obj, uid) {
  2333  						if (Basic.typeOf(obj.instance.destroy) === 'function') {
  2334  							obj.instance.destroy.call(obj.context);
  2335  						}
  2336  						self.removeInstance(uid);
  2337  					});
  2338  				}
  2339  			};
  2340  		}());
  2341  
  2342  
  2343  		// public methods
  2344  		Basic.extend(this, {
  2345  			/**
  2346  			Specifies whether runtime instance was initialized or not
  2347  
  2348  			@property initialized
  2349  			@type {Boolean}
  2350  			@default false
  2351  			*/
  2352  			initialized: false, // shims require this flag to stop initialization retries
  2353  
  2354  			/**
  2355  			Unique ID of the runtime
  2356  
  2357  			@property uid
  2358  			@type {String}
  2359  			*/
  2360  			uid: _uid,
  2361  
  2362  			/**
  2363  			Runtime type (e.g. flash, html5, etc)
  2364  
  2365  			@property type
  2366  			@type {String}
  2367  			*/
  2368  			type: type,
  2369  
  2370  			/**
  2371  			Runtime (not native one) may operate in browser or client mode.
  2372  
  2373  			@property mode
  2374  			@private
  2375  			@type {String|Boolean} current mode or false, if none possible
  2376  			*/
  2377  			mode: Runtime.getMode(modeCaps, (options.required_caps), defaultMode),
  2378  
  2379  			/**
  2380  			id of the DOM container for the runtime (if available)
  2381  
  2382  			@property shimid
  2383  			@type {String}
  2384  			*/
  2385  			shimid: _uid + '_container',
  2386  
  2387  			/**
  2388  			Number of connected clients. If equal to zero, runtime can be destroyed
  2389  
  2390  			@property clients
  2391  			@type {Number}
  2392  			*/
  2393  			clients: 0,
  2394  
  2395  			/**
  2396  			Runtime initialization options
  2397  
  2398  			@property options
  2399  			@type {Object}
  2400  			*/
  2401  			options: options,
  2402  
  2403  			/**
  2404  			Checks if the runtime has specific capability
  2405  
  2406  			@method can
  2407  			@param {String} cap Name of capability to check
  2408  			@param {Mixed} [value] If passed, capability should somehow correlate to the value
  2409  			@param {Object} [refCaps] Set of capabilities to check the specified cap against (defaults to internal set)
  2410  			@return {Boolean} true if runtime has such capability and false, if - not
  2411  			*/
  2412  			can: function(cap, value) {
  2413  				var refCaps = arguments[2] || caps;
  2414  
  2415  				// if cap var is a comma-separated list of caps, convert it to object (key/value)
  2416  				if (Basic.typeOf(cap) === 'string' && Basic.typeOf(value) === 'undefined') {
  2417  					cap = Runtime.parseCaps(cap);
  2418  				}
  2419  
  2420  				if (Basic.typeOf(cap) === 'object') {
  2421  					for (var key in cap) {
  2422  						if (!this.can(key, cap[key], refCaps)) {
  2423  							return false;
  2424  						}
  2425  					}
  2426  					return true;
  2427  				}
  2428  
  2429  				// check the individual cap
  2430  				if (Basic.typeOf(refCaps[cap]) === 'function') {
  2431  					return refCaps[cap].call(this, value);
  2432  				} else {
  2433  					return (value === refCaps[cap]);
  2434  				}
  2435  			},
  2436  
  2437  			/**
  2438  			Returns container for the runtime as DOM element
  2439  
  2440  			@method getShimContainer
  2441  			@return {DOMElement}
  2442  			*/
  2443  			getShimContainer: function() {
  2444  				var container, shimContainer = Dom.get(this.shimid);
  2445  
  2446  				// if no container for shim, create one
  2447  				if (!shimContainer) {
  2448  					container = this.options.container ? Dom.get(this.options.container) : document.body;
  2449  
  2450  					// create shim container and insert it at an absolute position into the outer container
  2451  					shimContainer = document.createElement('div');
  2452  					shimContainer.id = this.shimid;
  2453  					shimContainer.className = 'moxie-shim moxie-shim-' + this.type;
  2454  
  2455  					Basic.extend(shimContainer.style, {
  2456  						position: 'absolute',
  2457  						top: '0px',
  2458  						left: '0px',
  2459  						width: '1px',
  2460  						height: '1px',
  2461  						overflow: 'hidden'
  2462  					});
  2463  
  2464  					container.appendChild(shimContainer);
  2465  					container = null;
  2466  				}
  2467  
  2468  				return shimContainer;
  2469  			},
  2470  
  2471  			/**
  2472  			Returns runtime as DOM element (if appropriate)
  2473  
  2474  			@method getShim
  2475  			@return {DOMElement}
  2476  			*/
  2477  			getShim: function() {
  2478  				return _shim;
  2479  			},
  2480  
  2481  			/**
  2482  			Invokes a method within the runtime itself (might differ across the runtimes)
  2483  
  2484  			@method shimExec
  2485  			@param {Mixed} []
  2486  			@protected
  2487  			@return {Mixed} Depends on the action and component
  2488  			*/
  2489  			shimExec: function(component, action) {
  2490  				var args = [].slice.call(arguments, 2);
  2491  				return self.getShim().exec.call(this, this.uid, component, action, args);
  2492  			},
  2493  
  2494  			/**
  2495  			Operaional interface that is used by components to invoke specific actions on the runtime
  2496  			(is invoked in the scope of component)
  2497  
  2498  			@method exec
  2499  			@param {Mixed} []*
  2500  			@protected
  2501  			@return {Mixed} Depends on the action and component
  2502  			*/
  2503  			exec: function(component, action) { // this is called in the context of component, not runtime
  2504  				var args = [].slice.call(arguments, 2);
  2505  
  2506  				if (self[component] && self[component][action]) {
  2507  					return self[component][action].apply(this, args);
  2508  				}
  2509  				return self.shimExec.apply(this, arguments);
  2510  			},
  2511  
  2512  			/**
  2513  			Destroys the runtime (removes all events and deletes DOM structures)
  2514  
  2515  			@method destroy
  2516  			*/
  2517  			destroy: function() {
  2518  				if (!self) {
  2519  					return; // obviously already destroyed
  2520  				}
  2521  
  2522  				var shimContainer = Dom.get(this.shimid);
  2523  				if (shimContainer) {
  2524  					shimContainer.parentNode.removeChild(shimContainer);
  2525  				}
  2526  
  2527  				if (_shim) {
  2528  					_shim.removeAllInstances();
  2529  				}
  2530  
  2531  				this.unbindAll();
  2532  				delete runtimes[this.uid];
  2533  				this.uid = null; // mark this runtime as destroyed
  2534  				_uid = self = _shim = shimContainer = null;
  2535  			}
  2536  		});
  2537  
  2538  		// once we got the mode, test against all caps
  2539  		if (this.mode && options.required_caps && !this.can(options.required_caps)) {
  2540  			this.mode = false;
  2541  		}	
  2542  	}
  2543  
  2544  
  2545  	/**
  2546  	Default order to try different runtime types
  2547  
  2548  	@property order
  2549  	@type String
  2550  	@static
  2551  	*/
  2552  	Runtime.order = 'html5,flash,silverlight,html4';
  2553  
  2554  
  2555  	/**
  2556  	Retrieves runtime from private hash by it's uid
  2557  
  2558  	@method getRuntime
  2559  	@private
  2560  	@static
  2561  	@param {String} uid Unique identifier of the runtime
  2562  	@return {Runtime|Boolean} Returns runtime, if it exists and false, if - not
  2563  	*/
  2564  	Runtime.getRuntime = function(uid) {
  2565  		return runtimes[uid] ? runtimes[uid] : false;
  2566  	};
  2567  
  2568  
  2569  	/**
  2570  	Register constructor for the Runtime of new (or perhaps modified) type
  2571  
  2572  	@method addConstructor
  2573  	@static
  2574  	@param {String} type Runtime type (e.g. flash, html5, etc)
  2575  	@param {Function} construct Constructor for the Runtime type
  2576  	*/
  2577  	Runtime.addConstructor = function(type, constructor) {
  2578  		constructor.prototype = EventTarget.instance;
  2579  		runtimeConstructors[type] = constructor;
  2580  	};
  2581  
  2582  
  2583  	/**
  2584  	Get the constructor for the specified type.
  2585  
  2586  	method getConstructor
  2587  	@static
  2588  	@param {String} type Runtime type (e.g. flash, html5, etc)
  2589  	@return {Function} Constructor for the Runtime type
  2590  	*/
  2591  	Runtime.getConstructor = function(type) {
  2592  		return runtimeConstructors[type] || null;
  2593  	};
  2594  
  2595  
  2596  	/**
  2597  	Get info about the runtime (uid, type, capabilities)
  2598  
  2599  	@method getInfo
  2600  	@static
  2601  	@param {String} uid Unique identifier of the runtime
  2602  	@return {Mixed} Info object or null if runtime doesn't exist
  2603  	*/
  2604  	Runtime.getInfo = function(uid) {
  2605  		var runtime = Runtime.getRuntime(uid);
  2606  
  2607  		if (runtime) {
  2608  			return {
  2609  				uid: runtime.uid,
  2610  				type: runtime.type,
  2611  				mode: runtime.mode,
  2612  				can: function() {
  2613  					return runtime.can.apply(runtime, arguments);
  2614  				}
  2615  			};
  2616  		}
  2617  		return null;
  2618  	};
  2619  
  2620  
  2621  	/**
  2622  	Convert caps represented by a comma-separated string to the object representation.
  2623  
  2624  	@method parseCaps
  2625  	@static
  2626  	@param {String} capStr Comma-separated list of capabilities
  2627  	@return {Object}
  2628  	*/
  2629  	Runtime.parseCaps = function(capStr) {
  2630  		var capObj = {};
  2631  
  2632  		if (Basic.typeOf(capStr) !== 'string') {
  2633  			return capStr || {};
  2634  		}
  2635  
  2636  		Basic.each(capStr.split(','), function(key) {
  2637  			capObj[key] = true; // we assume it to be - true
  2638  		});
  2639  
  2640  		return capObj;
  2641  	};
  2642  
  2643  	/**
  2644  	Test the specified runtime for specific capabilities.
  2645  
  2646  	@method can
  2647  	@static
  2648  	@param {String} type Runtime type (e.g. flash, html5, etc)
  2649  	@param {String|Object} caps Set of capabilities to check
  2650  	@return {Boolean} Result of the test
  2651  	*/
  2652  	Runtime.can = function(type, caps) {
  2653  		var runtime
  2654  		, constructor = Runtime.getConstructor(type)
  2655  		, mode
  2656  		;
  2657  		if (constructor) {
  2658  			runtime = new constructor({
  2659  				required_caps: caps
  2660  			});
  2661  			mode = runtime.mode;
  2662  			runtime.destroy();
  2663  			return !!mode;
  2664  		}
  2665  		return false;
  2666  	};
  2667  
  2668  
  2669  	/**
  2670  	Figure out a runtime that supports specified capabilities.
  2671  
  2672  	@method thatCan
  2673  	@static
  2674  	@param {String|Object} caps Set of capabilities to check
  2675  	@param {String} [runtimeOrder] Comma-separated list of runtimes to check against
  2676  	@return {String} Usable runtime identifier or null
  2677  	*/
  2678  	Runtime.thatCan = function(caps, runtimeOrder) {
  2679  		var types = (runtimeOrder || Runtime.order).split(/\s*,\s*/);
  2680  		for (var i in types) {
  2681  			if (Runtime.can(types[i], caps)) {
  2682  				return types[i];
  2683  			}
  2684  		}
  2685  		return null;
  2686  	};
  2687  
  2688  
  2689  	/**
  2690  	Figure out an operational mode for the specified set of capabilities.
  2691  
  2692  	@method getMode
  2693  	@static
  2694  	@param {Object} modeCaps Set of capabilities that depend on particular runtime mode
  2695  	@param {Object} [requiredCaps] Supplied set of capabilities to find operational mode for
  2696  	@param {String|Boolean} [defaultMode='browser'] Default mode to use 
  2697  	@return {String|Boolean} Compatible operational mode
  2698  	*/
  2699  	Runtime.getMode = function(modeCaps, requiredCaps, defaultMode) {
  2700  		var mode = null;
  2701  
  2702  		if (Basic.typeOf(defaultMode) === 'undefined') { // only if not specified
  2703  			defaultMode = 'browser';
  2704  		}
  2705  
  2706  		if (requiredCaps && !Basic.isEmptyObj(modeCaps)) {
  2707  			// loop over required caps and check if they do require the same mode
  2708  			Basic.each(requiredCaps, function(value, cap) {
  2709  				if (modeCaps.hasOwnProperty(cap)) {
  2710  					var capMode = modeCaps[cap](value);
  2711  
  2712  					// make sure we always have an array
  2713  					if (typeof(capMode) === 'string') {
  2714  						capMode = [capMode];
  2715  					}
  2716  					
  2717  					if (!mode) {
  2718  						mode = capMode;
  2719  					} else if (!(mode = Basic.arrayIntersect(mode, capMode))) {
  2720  						// if cap requires conflicting mode - runtime cannot fulfill required caps
  2721  						return (mode = false);
  2722  					}
  2723  				}
  2724  			});
  2725  
  2726  			if (mode) {
  2727  				return Basic.inArray(defaultMode, mode) !== -1 ? defaultMode : mode[0];
  2728  			} else if (mode === false) {
  2729  				return false;
  2730  			}
  2731  		}
  2732  		return defaultMode; 
  2733  	};
  2734  
  2735  
  2736  	/**
  2737  	Capability check that always returns true
  2738  
  2739  	@private
  2740  	@static
  2741  	@return {True}
  2742  	*/
  2743  	Runtime.capTrue = function() {
  2744  		return true;
  2745  	};
  2746  
  2747  	/**
  2748  	Capability check that always returns false
  2749  
  2750  	@private
  2751  	@static
  2752  	@return {False}
  2753  	*/
  2754  	Runtime.capFalse = function() {
  2755  		return false;
  2756  	};
  2757  
  2758  	/**
  2759  	Evaluate the expression to boolean value and create a function that always returns it.
  2760  
  2761  	@private
  2762  	@static
  2763  	@param {Mixed} expr Expression to evaluate
  2764  	@return {Function} Function returning the result of evaluation
  2765  	*/
  2766  	Runtime.capTest = function(expr) {
  2767  		return function() {
  2768  			return !!expr;
  2769  		};
  2770  	};
  2771  
  2772  	return Runtime;
  2773  });
  2774  
  2775  // Included from: src/javascript/runtime/RuntimeClient.js
  2776  
  2777  /**
  2778   * RuntimeClient.js
  2779   *
  2780   * Copyright 2013, Moxiecode Systems AB
  2781   * Released under GPL License.
  2782   *
  2783   * License: http://www.plupload.com/license
  2784   * Contributing: http://www.plupload.com/contributing
  2785   */
  2786  
  2787  define('moxie/runtime/RuntimeClient', [
  2788  	'moxie/core/Exceptions',
  2789  	'moxie/core/utils/Basic',
  2790  	'moxie/runtime/Runtime'
  2791  ], function(x, Basic, Runtime) {
  2792  	/**
  2793  	Set of methods and properties, required by a component to acquire ability to connect to a runtime
  2794  
  2795  	@class RuntimeClient
  2796  	*/
  2797  	return function RuntimeClient() {
  2798  		var runtime;
  2799  
  2800  		Basic.extend(this, {
  2801  			/**
  2802  			Connects to the runtime specified by the options. Will either connect to existing runtime or create a new one.
  2803  			Increments number of clients connected to the specified runtime.
  2804  
  2805  			@method connectRuntime
  2806  			@param {Mixed} options Can be a runtme uid or a set of key-value pairs defining requirements and pre-requisites
  2807  			*/
  2808  			connectRuntime: function(options) {
  2809  				var comp = this, ruid;
  2810  
  2811  				function initialize(items) {
  2812  					var type, constructor;
  2813  
  2814  					// if we ran out of runtimes
  2815  					if (!items.length) {
  2816  						comp.trigger('RuntimeError', new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
  2817  						runtime = null;
  2818  						return;
  2819  					}
  2820  
  2821  					type = items.shift();
  2822  					constructor = Runtime.getConstructor(type);
  2823  					if (!constructor) {
  2824  						initialize(items);
  2825  						return;
  2826  					}
  2827  
  2828  					// try initializing the runtime
  2829  					runtime = new constructor(options);
  2830  
  2831  					runtime.bind('Init', function() {
  2832  						// mark runtime as initialized
  2833  						runtime.initialized = true;
  2834  
  2835  						// jailbreak ...
  2836  						setTimeout(function() {
  2837  							runtime.clients++;
  2838  							// this will be triggered on component
  2839  							comp.trigger('RuntimeInit', runtime);
  2840  						}, 1);
  2841  					});
  2842  
  2843  					runtime.bind('Error', function() {
  2844  						runtime.destroy(); // runtime cannot destroy itself from inside at a right moment, thus we do it here
  2845  						initialize(items);
  2846  					});
  2847  
  2848  					/*runtime.bind('Exception', function() { });*/
  2849  
  2850  					// check if runtime managed to pick-up operational mode
  2851  					if (!runtime.mode) {
  2852  						runtime.trigger('Error');
  2853  						return;
  2854  					}
  2855  
  2856  					runtime.init();
  2857  				}
  2858  
  2859  				// check if a particular runtime was requested
  2860  				if (Basic.typeOf(options) === 'string') {
  2861  					ruid = options;
  2862  				} else if (Basic.typeOf(options.ruid) === 'string') {
  2863  					ruid = options.ruid;
  2864  				}
  2865  
  2866  				if (ruid) {
  2867  					runtime = Runtime.getRuntime(ruid);
  2868  					if (runtime) {
  2869  						runtime.clients++;
  2870  						return runtime;
  2871  					} else {
  2872  						// there should be a runtime and there's none - weird case
  2873  						throw new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR);
  2874  					}
  2875  				}
  2876  
  2877  				// initialize a fresh one, that fits runtime list and required features best
  2878  				initialize((options.runtime_order || Runtime.order).split(/\s*,\s*/));
  2879  			},
  2880  
  2881  			/**
  2882  			Returns the runtime to which the client is currently connected.
  2883  
  2884  			@method getRuntime
  2885  			@return {Runtime} Runtime or null if client is not connected
  2886  			*/
  2887  			getRuntime: function() {
  2888  				if (runtime && runtime.uid) {
  2889  					return runtime;
  2890  				}
  2891  				runtime = null; // make sure we do not leave zombies rambling around
  2892  				return null;
  2893  			},
  2894  
  2895  			/**
  2896  			Disconnects from the runtime. Decrements number of clients connected to the specified runtime.
  2897  
  2898  			@method disconnectRuntime
  2899  			*/
  2900  			disconnectRuntime: function() {
  2901  				if (runtime && --runtime.clients <= 0) {
  2902  					runtime.destroy();
  2903  					runtime = null;
  2904  				}
  2905  			}
  2906  
  2907  		});
  2908  	};
  2909  
  2910  
  2911  });
  2912  
  2913  // Included from: src/javascript/file/Blob.js
  2914  
  2915  /**
  2916   * Blob.js
  2917   *
  2918   * Copyright 2013, Moxiecode Systems AB
  2919   * Released under GPL License.
  2920   *
  2921   * License: http://www.plupload.com/license
  2922   * Contributing: http://www.plupload.com/contributing
  2923   */
  2924  
  2925  define('moxie/file/Blob', [
  2926  	'moxie/core/utils/Basic',
  2927  	'moxie/core/utils/Encode',
  2928  	'moxie/runtime/RuntimeClient'
  2929  ], function(Basic, Encode, RuntimeClient) {
  2930  	
  2931  	var blobpool = {};
  2932  
  2933  	/**
  2934  	@class Blob
  2935  	@constructor
  2936  	@param {String} ruid Unique id of the runtime, to which this blob belongs to
  2937  	@param {Object} blob Object "Native" blob object, as it is represented in the runtime
  2938  	*/
  2939  	function Blob(ruid, blob) {
  2940  
  2941  		function _sliceDetached(start, end, type) {
  2942  			var blob, data = blobpool[this.uid];
  2943  
  2944  			if (Basic.typeOf(data) !== 'string' || !data.length) {
  2945  				return null; // or throw exception
  2946  			}
  2947  
  2948  			blob = new Blob(null, {
  2949  				type: type,
  2950  				size: end - start
  2951  			});
  2952  			blob.detach(data.substr(start, blob.size));
  2953  
  2954  			return blob;
  2955  		}
  2956  
  2957  		RuntimeClient.call(this);
  2958  
  2959  		if (ruid) {	
  2960  			this.connectRuntime(ruid);
  2961  		}
  2962  
  2963  		if (!blob) {
  2964  			blob = {};
  2965  		} else if (Basic.typeOf(blob) === 'string') { // dataUrl or binary string
  2966  			blob = { data: blob };
  2967  		}
  2968  
  2969  		Basic.extend(this, {
  2970  			
  2971  			/**
  2972  			Unique id of the component
  2973  
  2974  			@property uid
  2975  			@type {String}
  2976  			*/
  2977  			uid: blob.uid || Basic.guid('uid_'),
  2978  			
  2979  			/**
  2980  			Unique id of the connected runtime, if falsy, then runtime will have to be initialized 
  2981  			before this Blob can be used, modified or sent
  2982  
  2983  			@property ruid
  2984  			@type {String}
  2985  			*/
  2986  			ruid: ruid,
  2987  	
  2988  			/**
  2989  			Size of blob
  2990  
  2991  			@property size
  2992  			@type {Number}
  2993  			@default 0
  2994  			*/
  2995  			size: blob.size || 0,
  2996  			
  2997  			/**
  2998  			Mime type of blob
  2999  
  3000  			@property type
  3001  			@type {String}
  3002  			@default ''
  3003  			*/
  3004  			type: blob.type || '',
  3005  			
  3006  			/**
  3007  			@method slice
  3008  			@param {Number} [start=0]
  3009  			*/
  3010  			slice: function(start, end, type) {		
  3011  				if (this.isDetached()) {
  3012  					return _sliceDetached.apply(this, arguments);
  3013  				}
  3014  				return this.getRuntime().exec.call(this, 'Blob', 'slice', this.getSource(), start, end, type);
  3015  			},
  3016  
  3017  			/**
  3018  			Returns "native" blob object (as it is represented in connected runtime) or null if not found
  3019  
  3020  			@method getSource
  3021  			@return {Blob} Returns "native" blob object or null if not found
  3022  			*/
  3023  			getSource: function() {
  3024  				if (!blobpool[this.uid]) {
  3025  					return null;	
  3026  				}
  3027  				return blobpool[this.uid];
  3028  			},
  3029  
  3030  			/** 
  3031  			Detaches blob from any runtime that it depends on and initialize with standalone value
  3032  
  3033  			@method detach
  3034  			@protected
  3035  			@param {DOMString} [data=''] Standalone value
  3036  			*/
  3037  			detach: function(data) {
  3038  				if (this.ruid) {
  3039  					this.getRuntime().exec.call(this, 'Blob', 'destroy');
  3040  					this.disconnectRuntime();
  3041  					this.ruid = null;
  3042  				}
  3043  
  3044  				data = data || '';
  3045  
  3046  				// if dataUrl, convert to binary string
  3047  				var matches = data.match(/^data:([^;]*);base64,/);
  3048  				if (matches) {
  3049  					this.type = matches[1];
  3050  					data = Encode.atob(data.substring(data.indexOf('base64,') + 7));
  3051  				}
  3052  
  3053  				this.size = data.length;
  3054  
  3055  				blobpool[this.uid] = data;
  3056  			},
  3057  
  3058  			/**
  3059  			Checks if blob is standalone (detached of any runtime)
  3060  			
  3061  			@method isDetached
  3062  			@protected
  3063  			@return {Boolean}
  3064  			*/
  3065  			isDetached: function() {
  3066  				return !this.ruid && Basic.typeOf(blobpool[this.uid]) === 'string';
  3067  			},
  3068  			
  3069  			/** 
  3070  			Destroy Blob and free any resources it was using
  3071  
  3072  			@method destroy
  3073  			*/
  3074  			destroy: function() {
  3075  				this.detach();
  3076  				delete blobpool[this.uid];
  3077  			}
  3078  		});
  3079  
  3080  		
  3081  		if (blob.data) {
  3082  			this.detach(blob.data); // auto-detach if payload has been passed
  3083  		} else {
  3084  			blobpool[this.uid] = blob;	
  3085  		}
  3086  	}
  3087  	
  3088  	return Blob;
  3089  });
  3090  
  3091  // Included from: src/javascript/file/File.js
  3092  
  3093  /**
  3094   * File.js
  3095   *
  3096   * Copyright 2013, Moxiecode Systems AB
  3097   * Released under GPL License.
  3098   *
  3099   * License: http://www.plupload.com/license
  3100   * Contributing: http://www.plupload.com/contributing
  3101   */
  3102  
  3103  define('moxie/file/File', [
  3104  	'moxie/core/utils/Basic',
  3105  	'moxie/core/utils/Mime',
  3106  	'moxie/file/Blob'
  3107  ], function(Basic, Mime, Blob) {
  3108  	/**
  3109  	@class File
  3110  	@extends Blob
  3111  	@constructor
  3112  	@param {String} ruid Unique id of the runtime, to which this blob belongs to
  3113  	@param {Object} file Object "Native" file object, as it is represented in the runtime
  3114  	*/
  3115  	function File(ruid, file) {
  3116  		var name, type;
  3117  
  3118  		if (!file) { // avoid extra errors in case we overlooked something
  3119  			file = {};
  3120  		}
  3121  
  3122  		// figure out the type
  3123  		if (file.type && file.type !== '') {
  3124  			type = file.type;
  3125  		} else {
  3126  			type = Mime.getFileMime(file.name);
  3127  		}
  3128  
  3129  		// sanitize file name or generate new one
  3130  		if (file.name) {
  3131  			name = file.name.replace(/\\/g, '/');
  3132  			name = name.substr(name.lastIndexOf('/') + 1);
  3133  		} else {
  3134  			var prefix = type.split('/')[0];
  3135  			name = Basic.guid((prefix !== '' ? prefix : 'file') + '_');
  3136  			
  3137  			if (Mime.extensions[type]) {
  3138  				name += '.' + Mime.extensions[type][0]; // append proper extension if possible
  3139  			}
  3140  		}
  3141  
  3142  		Blob.apply(this, arguments);
  3143  		
  3144  		Basic.extend(this, {
  3145  			/**
  3146  			File mime type
  3147  
  3148  			@property type
  3149  			@type {String}
  3150  			@default ''
  3151  			*/
  3152  			type: type || '',
  3153  
  3154  			/**
  3155  			File name
  3156  
  3157  			@property name
  3158  			@type {String}
  3159  			@default UID
  3160  			*/
  3161  			name: name || Basic.guid('file_'),
  3162  			
  3163  			/**
  3164  			Date of last modification
  3165  
  3166  			@property lastModifiedDate
  3167  			@type {String}
  3168  			@default now
  3169  			*/
  3170  			lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString() // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
  3171  		});
  3172  	}
  3173  
  3174  	File.prototype = Blob.prototype;
  3175  
  3176  	return File;
  3177  });
  3178  
  3179  // Included from: src/javascript/file/FileInput.js
  3180  
  3181  /**
  3182   * FileInput.js
  3183   *
  3184   * Copyright 2013, Moxiecode Systems AB
  3185   * Released under GPL License.
  3186   *
  3187   * License: http://www.plupload.com/license
  3188   * Contributing: http://www.plupload.com/contributing
  3189   */
  3190  
  3191  define('moxie/file/FileInput', [
  3192  	'moxie/core/utils/Basic',
  3193  	'moxie/core/utils/Mime',
  3194  	'moxie/core/utils/Dom',
  3195  	'moxie/core/Exceptions',
  3196  	'moxie/core/EventTarget',
  3197  	'moxie/core/I18n',
  3198  	'moxie/file/File',
  3199  	'moxie/runtime/Runtime',
  3200  	'moxie/runtime/RuntimeClient'
  3201  ], function(Basic, Mime, Dom, x, EventTarget, I18n, File, Runtime, RuntimeClient) {
  3202  	/**
  3203  	Provides a convenient way to create cross-browser file-picker. Generates file selection dialog on click,
  3204  	converts selected files to _File_ objects, to be used in conjunction with _Image_, preloaded in memory
  3205  	with _FileReader_ or uploaded to a server through _XMLHttpRequest_.
  3206  
  3207  	@class FileInput
  3208  	@constructor
  3209  	@extends EventTarget
  3210  	@uses RuntimeClient
  3211  	@param {Object|String|DOMElement} options If options is string or node, argument is considered as _browse\_button_.
  3212  		@param {String|DOMElement} options.browse_button DOM Element to turn into file picker.
  3213  		@param {Array} [options.accept] Array of mime types to accept. By default accepts all.
  3214  		@param {String} [options.file='file'] Name of the file field (not the filename).
  3215  		@param {Boolean} [options.multiple=false] Enable selection of multiple files.
  3216  		@param {Boolean} [options.directory=false] Turn file input into the folder input (cannot be both at the same time).
  3217  		@param {String|DOMElement} [options.container] DOM Element to use as a container for file-picker. Defaults to parentNode 
  3218  		for _browse\_button_.
  3219  		@param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support.
  3220  
  3221  	@example
  3222  		<div id="container">
  3223  			<a id="file-picker" href="javascript:;">Browse...</a>
  3224  		</div>
  3225  
  3226  		<script>
  3227  			var fileInput = new mOxie.FileInput({
  3228  				browse_button: 'file-picker', // or document.getElementById('file-picker')
  3229  				container: 'container',
  3230  				accept: [
  3231  					{title: "Image files", extensions: "jpg,gif,png"} // accept only images
  3232  				],
  3233  				multiple: true // allow multiple file selection
  3234  			});
  3235  
  3236  			fileInput.onchange = function(e) {
  3237  				// do something to files array
  3238  				console.info(e.target.files); // or this.files or fileInput.files
  3239  			};
  3240  
  3241  			fileInput.init(); // initialize
  3242  		</script>
  3243  	*/
  3244  	var dispatches = [
  3245  		/**
  3246  		Dispatched when runtime is connected and file-picker is ready to be used.
  3247  
  3248  		@event ready
  3249  		@param {Object} event
  3250  		*/
  3251  		'ready',
  3252  
  3253  		/**
  3254  		Dispatched right after [ready](#event_ready) event, and whenever [refresh()](#method_refresh) is invoked. 
  3255  		Check [corresponding documentation entry](#method_refresh) for more info.
  3256  
  3257  		@event refresh
  3258  		@param {Object} event
  3259  		*/
  3260  
  3261  		/**
  3262  		Dispatched when selection of files in the dialog is complete.
  3263  
  3264  		@event change
  3265  		@param {Object} event
  3266  		*/
  3267  		'change',
  3268  
  3269  		'cancel', // TODO: might be useful
  3270  
  3271  		/**
  3272  		Dispatched when mouse cursor enters file-picker area. Can be used to style element
  3273  		accordingly.
  3274  
  3275  		@event mouseenter
  3276  		@param {Object} event
  3277  		*/
  3278  		'mouseenter',
  3279  
  3280  		/**
  3281  		Dispatched when mouse cursor leaves file-picker area. Can be used to style element
  3282  		accordingly.
  3283  
  3284  		@event mouseleave
  3285  		@param {Object} event
  3286  		*/
  3287  		'mouseleave',
  3288  
  3289  		/**
  3290  		Dispatched when functional mouse button is pressed on top of file-picker area.
  3291  
  3292  		@event mousedown
  3293  		@param {Object} event
  3294  		*/
  3295  		'mousedown',
  3296  
  3297  		/**
  3298  		Dispatched when functional mouse button is released on top of file-picker area.
  3299  
  3300  		@event mouseup
  3301  		@param {Object} event
  3302  		*/
  3303  		'mouseup'
  3304  	];
  3305  
  3306  	function FileInput(options) {
  3307  		var self = this,
  3308  			container, browseButton, defaults;
  3309  
  3310  		// if flat argument passed it should be browse_button id
  3311  		if (Basic.inArray(Basic.typeOf(options), ['string', 'node']) !== -1) {
  3312  			options = { browse_button : options };
  3313  		}
  3314  
  3315  		// this will help us to find proper default container
  3316  		browseButton = Dom.get(options.browse_button);
  3317  		if (!browseButton) {
  3318  			// browse button is required
  3319  			throw new x.DOMException(x.DOMException.NOT_FOUND_ERR);
  3320  		}
  3321  
  3322  		// figure out the options
  3323  		defaults = {
  3324  			accept: [{
  3325  				title: I18n.translate('All Files'),
  3326  				extensions: '*'
  3327  			}],
  3328  			name: 'file',
  3329  			multiple: false,
  3330  			required_caps: false,
  3331  			container: browseButton.parentNode || document.body
  3332  		};
  3333  		
  3334  		options = Basic.extend({}, defaults, options);
  3335  
  3336  		// convert to object representation
  3337  		if (typeof(options.required_caps) === 'string') {
  3338  			options.required_caps = Runtime.parseCaps(options.required_caps);
  3339  		}
  3340  					
  3341  		// normalize accept option (could be list of mime types or array of title/extensions pairs)
  3342  		if (typeof(options.accept) === 'string') {
  3343  			options.accept = Mime.mimes2extList(options.accept);
  3344  		}
  3345  
  3346  		container = Dom.get(options.container);
  3347  		// make sure we have container
  3348  		if (!container) {
  3349  			container = document.body;
  3350  		}
  3351  
  3352  		// make container relative, if it's not
  3353  		if (Dom.getStyle(container, 'position') === 'static') {
  3354  			container.style.position = 'relative';
  3355  		}
  3356  
  3357  		container = browseButton = null; // IE
  3358  						
  3359  		RuntimeClient.call(self);
  3360  		
  3361  		Basic.extend(self, {
  3362  			/**
  3363  			Unique id of the component
  3364  
  3365  			@property uid
  3366  			@protected
  3367  			@readOnly
  3368  			@type {String}
  3369  			@default UID
  3370  			*/
  3371  			uid: Basic.guid('uid_'),
  3372  			
  3373  			/**
  3374  			Unique id of the connected runtime, if any.
  3375  
  3376  			@property ruid
  3377  			@protected
  3378  			@type {String}
  3379  			*/
  3380  			ruid: null,
  3381  
  3382  			/**
  3383  			Unique id of the runtime container. Useful to get hold of it for various manipulations.
  3384  
  3385  			@property shimid
  3386  			@protected
  3387  			@type {String}
  3388  			*/
  3389  			shimid: null,
  3390  			
  3391  			/**
  3392  			Array of selected mOxie.File objects
  3393  
  3394  			@property files
  3395  			@type {Array}
  3396  			@default null
  3397  			*/
  3398  			files: null,
  3399  
  3400  			/**
  3401  			Initializes the file-picker, connects it to runtime and dispatches event ready when done.
  3402  
  3403  			@method init
  3404  			*/
  3405  			init: function() {
  3406  				self.convertEventPropsToHandlers(dispatches);
  3407  
  3408  				self.bind('RuntimeInit', function(e, runtime) {
  3409  					self.ruid = runtime.uid;
  3410  					self.shimid = runtime.shimid;
  3411  
  3412  					self.bind("Ready", function() {
  3413  						self.trigger("Refresh");
  3414  					}, 999);
  3415  
  3416  					self.bind("Change", function() {
  3417  						var files = runtime.exec.call(self, 'FileInput', 'getFiles');
  3418  
  3419  						self.files = [];
  3420  
  3421  						Basic.each(files, function(file) {
  3422  							// ignore empty files (IE10 for example hangs if you try to send them via XHR)
  3423  							if (file.size === 0) {
  3424  								return true; 
  3425  							}
  3426  							self.files.push(new File(self.ruid, file));
  3427  						});
  3428  					}, 999);
  3429  
  3430  					// re-position and resize shim container
  3431  					self.bind('Refresh', function() {
  3432  						var pos, size, browseButton, shimContainer;
  3433  						
  3434  						browseButton = Dom.get(options.browse_button);
  3435  						shimContainer = Dom.get(runtime.shimid); // do not use runtime.getShimContainer(), since it will create container if it doesn't exist
  3436  
  3437  						if (browseButton) {
  3438  							pos = Dom.getPos(browseButton, Dom.get(options.container));
  3439  							size = Dom.getSize(browseButton);
  3440  
  3441  							if (shimContainer) {
  3442  								Basic.extend(shimContainer.style, {
  3443  									top     : pos.y + 'px',
  3444  									left    : pos.x + 'px',
  3445  									width   : size.w + 'px',
  3446  									height  : size.h + 'px'
  3447  								});
  3448  							}
  3449  						}
  3450  						shimContainer = browseButton = null;
  3451  					});
  3452  					
  3453  					runtime.exec.call(self, 'FileInput', 'init', options);
  3454  				});
  3455  
  3456  				// runtime needs: options.required_features, options.runtime_order and options.container
  3457  				self.connectRuntime(Basic.extend({}, options, {
  3458  					required_caps: {
  3459  						select_file: true
  3460  					}
  3461  				}));
  3462  			},
  3463  
  3464  			/**
  3465  			Disables file-picker element, so that it doesn't react to mouse clicks.
  3466  
  3467  			@method disable
  3468  			@param {Boolean} [state=true] Disable component if - true, enable if - false
  3469  			*/
  3470  			disable: function(state) {
  3471  				var runtime = this.getRuntime();
  3472  				if (runtime) {
  3473  					runtime.exec.call(this, 'FileInput', 'disable', Basic.typeOf(state) === 'undefined' ? true : state);
  3474  				}
  3475  			},
  3476  
  3477  
  3478  			/**
  3479  			Reposition and resize dialog trigger to match the position and size of browse_button element.
  3480  
  3481  			@method refresh
  3482  			*/
  3483  			refresh: function() {
  3484  				self.trigger("Refresh");
  3485  			},
  3486  
  3487  
  3488  			/**
  3489  			Destroy component.
  3490  
  3491  			@method destroy
  3492  			*/
  3493  			destroy: function() {
  3494  				var runtime = this.getRuntime();
  3495  				if (runtime) {
  3496  					runtime.exec.call(this, 'FileInput', 'destroy');
  3497  					this.disconnectRuntime();
  3498  				}
  3499  
  3500  				if (Basic.typeOf(this.files) === 'array') {
  3501  					// no sense in leaving associated files behind
  3502  					Basic.each(this.files, function(file) {
  3503  						file.destroy();
  3504  					});
  3505  				} 
  3506  				this.files = null;
  3507  			}
  3508  		});
  3509  	}
  3510  
  3511  	FileInput.prototype = EventTarget.instance;
  3512  
  3513  	return FileInput;
  3514  });
  3515  
  3516  // Included from: src/javascript/file/FileDrop.js
  3517  
  3518  /**
  3519   * FileDrop.js
  3520   *
  3521   * Copyright 2013, Moxiecode Systems AB
  3522   * Released under GPL License.
  3523   *
  3524   * License: http://www.plupload.com/license
  3525   * Contributing: http://www.plupload.com/contributing
  3526   */
  3527  
  3528  define('moxie/file/FileDrop', [
  3529  	'moxie/core/I18n',
  3530  	'moxie/core/utils/Dom',
  3531  	'moxie/core/Exceptions',
  3532  	'moxie/core/utils/Basic',
  3533  	'moxie/file/File',
  3534  	'moxie/runtime/RuntimeClient',
  3535  	'moxie/core/EventTarget',
  3536  	'moxie/core/utils/Mime'
  3537  ], function(I18n, Dom, x, Basic, File, RuntimeClient, EventTarget, Mime) {
  3538  	/**
  3539  	Turn arbitrary DOM element to a drop zone accepting files. Converts selected files to _File_ objects, to be used 
  3540  	in conjunction with _Image_, preloaded in memory with _FileReader_ or uploaded to a server through 
  3541  	_XMLHttpRequest_.
  3542  
  3543  	@example
  3544  		<div id="drop_zone">
  3545  			Drop files here
  3546  		</div>
  3547  		<br />
  3548  		<div id="filelist"></div>
  3549  
  3550  		<script type="text/javascript">
  3551  			var fileDrop = new mOxie.FileDrop('drop_zone'), fileList = mOxie.get('filelist');
  3552  
  3553  			fileDrop.ondrop = function() {
  3554  				mOxie.each(this.files, function(file) {
  3555  					fileList.innerHTML += '<div>' + file.name + '</div>';
  3556  				});
  3557  			};
  3558  
  3559  			fileDrop.init();
  3560  		</script>
  3561  
  3562  	@class FileDrop
  3563  	@constructor
  3564  	@extends EventTarget
  3565  	@uses RuntimeClient
  3566  	@param {Object|String} options If options has typeof string, argument is considered as options.drop_zone
  3567  		@param {String|DOMElement} options.drop_zone DOM Element to turn into a drop zone
  3568  		@param {Array} [options.accept] Array of mime types to accept. By default accepts all
  3569  		@param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support
  3570  	*/
  3571  	var dispatches = [
  3572  		/**
  3573  		Dispatched when runtime is connected and drop zone is ready to accept files.
  3574  
  3575  		@event ready
  3576  		@param {Object} event
  3577  		*/
  3578  		'ready', 
  3579  
  3580  		/**
  3581  		Dispatched when dragging cursor enters the drop zone.
  3582  
  3583  		@event dragenter
  3584  		@param {Object} event
  3585  		*/
  3586  		'dragenter',
  3587  
  3588  		/**
  3589  		Dispatched when dragging cursor leaves the drop zone.
  3590  
  3591  		@event dragleave
  3592  		@param {Object} event
  3593  		*/
  3594  		'dragleave', 
  3595  
  3596  		/**
  3597  		Dispatched when file is dropped onto the drop zone.
  3598  
  3599  		@event drop
  3600  		@param {Object} event
  3601  		*/
  3602  		'drop', 
  3603  
  3604  		/**
  3605  		Dispatched if error occurs.
  3606  
  3607  		@event error
  3608  		@param {Object} event
  3609  		*/
  3610  		'error'
  3611  	];
  3612  
  3613  	function FileDrop(options) {
  3614  		var self = this, defaults;
  3615  
  3616  		// if flat argument passed it should be drop_zone id
  3617  		if (typeof(options) === 'string') {
  3618  			options = { drop_zone : options };
  3619  		}
  3620  
  3621  		// figure out the options
  3622  		defaults = {
  3623  			accept: [{
  3624  				title: I18n.translate('All Files'),
  3625  				extensions: '*'
  3626  			}],
  3627  			required_caps: {
  3628  				drag_and_drop: true
  3629  			}
  3630  		};
  3631  		
  3632  		options = typeof(options) === 'object' ? Basic.extend({}, defaults, options) : defaults;
  3633  
  3634  		// this will help us to find proper default container
  3635  		options.container = Dom.get(options.drop_zone) || document.body;
  3636  
  3637  		// make container relative, if it is not
  3638  		if (Dom.getStyle(options.container, 'position') === 'static') {
  3639  			options.container.style.position = 'relative';
  3640  		}
  3641  					
  3642  		// normalize accept option (could be list of mime types or array of title/extensions pairs)
  3643  		if (typeof(options.accept) === 'string') {
  3644  			options.accept = Mime.mimes2extList(options.accept);
  3645  		}
  3646  
  3647  		RuntimeClient.call(self);
  3648  
  3649  		Basic.extend(self, {
  3650  			uid: Basic.guid('uid_'),
  3651  
  3652  			ruid: null,
  3653  
  3654  			files: null,
  3655  
  3656  			init: function() {
  3657  	
  3658  				self.convertEventPropsToHandlers(dispatches);
  3659  		
  3660  				self.bind('RuntimeInit', function(e, runtime) {
  3661  					self.ruid = runtime.uid;
  3662  
  3663  					self.bind("Drop", function() {
  3664  						var files = runtime.exec.call(self, 'FileDrop', 'getFiles');
  3665  
  3666  						self.files = [];
  3667  
  3668  						Basic.each(files, function(file) {
  3669  							self.files.push(new File(self.ruid, file));
  3670  						});
  3671  					}, 999);
  3672  
  3673  					runtime.exec.call(self, 'FileDrop', 'init', options);
  3674  
  3675  					self.dispatchEvent('ready');
  3676  				});
  3677  							
  3678  				// runtime needs: options.required_features, options.runtime_order and options.container
  3679  				self.connectRuntime(options); // throws RuntimeError
  3680  			},
  3681  
  3682  			destroy: function() {
  3683  				var runtime = this.getRuntime();
  3684  				if (runtime) {
  3685  					runtime.exec.call(this, 'FileDrop', 'destroy');
  3686  					this.disconnectRuntime();
  3687  				}
  3688  				this.files = null;
  3689  			}
  3690  		});
  3691  	}
  3692  
  3693  	FileDrop.prototype = EventTarget.instance;
  3694  
  3695  	return FileDrop;
  3696  });
  3697  
  3698  // Included from: src/javascript/runtime/RuntimeTarget.js
  3699  
  3700  /**
  3701   * RuntimeTarget.js
  3702   *
  3703   * Copyright 2013, Moxiecode Systems AB
  3704   * Released under GPL License.
  3705   *
  3706   * License: http://www.plupload.com/license
  3707   * Contributing: http://www.plupload.com/contributing
  3708   */
  3709  
  3710  define('moxie/runtime/RuntimeTarget', [
  3711  	'moxie/core/utils/Basic',
  3712  	'moxie/runtime/RuntimeClient',
  3713  	"moxie/core/EventTarget"
  3714  ], function(Basic, RuntimeClient, EventTarget) {
  3715  	/**
  3716  	Instance of this class can be used as a target for the events dispatched by shims,
  3717  	when allowing them onto components is for either reason inappropriate
  3718  
  3719  	@class RuntimeTarget
  3720  	@constructor
  3721  	@protected
  3722  	@extends EventTarget
  3723  	*/
  3724  	function RuntimeTarget() {
  3725  		this.uid = Basic.guid('uid_');
  3726  		
  3727  		RuntimeClient.call(this);
  3728  
  3729  		this.destroy = function() {
  3730  			this.disconnectRuntime();
  3731  			this.unbindAll();
  3732  		};
  3733  	}
  3734  
  3735  	RuntimeTarget.prototype = EventTarget.instance;
  3736  
  3737  	return RuntimeTarget;
  3738  });
  3739  
  3740  // Included from: src/javascript/file/FileReader.js
  3741  
  3742  /**
  3743   * FileReader.js
  3744   *
  3745   * Copyright 2013, Moxiecode Systems AB
  3746   * Released under GPL License.
  3747   *
  3748   * License: http://www.plupload.com/license
  3749   * Contributing: http://www.plupload.com/contributing
  3750   */
  3751  
  3752  define('moxie/file/FileReader', [
  3753  	'moxie/core/utils/Basic',
  3754  	'moxie/core/utils/Encode',
  3755  	'moxie/core/Exceptions',
  3756  	'moxie/core/EventTarget',
  3757  	'moxie/file/Blob',
  3758  	'moxie/file/File',
  3759  	'moxie/runtime/RuntimeTarget'
  3760  ], function(Basic, Encode, x, EventTarget, Blob, File, RuntimeTarget) {
  3761  	/**
  3762  	Utility for preloading o.Blob/o.File objects in memory. By design closely follows [W3C FileReader](http://www.w3.org/TR/FileAPI/#dfn-filereader)
  3763  	interface. Where possible uses native FileReader, where - not falls back to shims.
  3764  
  3765  	@class FileReader
  3766  	@constructor FileReader
  3767  	@extends EventTarget
  3768  	@uses RuntimeClient
  3769  	*/
  3770  	var dispatches = [
  3771  
  3772  		/** 
  3773  		Dispatched when the read starts.
  3774  
  3775  		@event loadstart
  3776  		@param {Object} event
  3777  		*/
  3778  		'loadstart', 
  3779  
  3780  		/** 
  3781  		Dispatched while reading (and decoding) blob, and reporting partial Blob data (progess.loaded/progress.total).
  3782  
  3783  		@event progress
  3784  		@param {Object} event
  3785  		*/
  3786  		'progress', 
  3787  
  3788  		/** 
  3789  		Dispatched when the read has successfully completed.
  3790  
  3791  		@event load
  3792  		@param {Object} event
  3793  		*/
  3794  		'load', 
  3795  
  3796  		/** 
  3797  		Dispatched when the read has been aborted. For instance, by invoking the abort() method.
  3798  
  3799  		@event abort
  3800  		@param {Object} event
  3801  		*/
  3802  		'abort', 
  3803  
  3804  		/** 
  3805  		Dispatched when the read has failed.
  3806  
  3807  		@event error
  3808  		@param {Object} event
  3809  		*/
  3810  		'error', 
  3811  
  3812  		/** 
  3813  		Dispatched when the request has completed (either in success or failure).
  3814  
  3815  		@event loadend
  3816  		@param {Object} event
  3817  		*/
  3818  		'loadend'
  3819  	];
  3820  	
  3821  	function FileReader() {
  3822  		var self = this, _fr;
  3823  				
  3824  		Basic.extend(this, {
  3825  			/**
  3826  			UID of the component instance.
  3827  
  3828  			@property uid
  3829  			@type {String}
  3830  			*/
  3831  			uid: Basic.guid('uid_'),
  3832  
  3833  			/**
  3834  			Contains current state of FileReader object. Can take values of FileReader.EMPTY, FileReader.LOADING
  3835  			and FileReader.DONE.
  3836  
  3837  			@property readyState
  3838  			@type {Number}
  3839  			@default FileReader.EMPTY
  3840  			*/
  3841  			readyState: FileReader.EMPTY,
  3842  			
  3843  			/**
  3844  			Result of the successful read operation.
  3845  
  3846  			@property result
  3847  			@type {String}
  3848  			*/
  3849  			result: null,
  3850  			
  3851  			/**
  3852  			Stores the error of failed asynchronous read operation.
  3853  
  3854  			@property error
  3855  			@type {DOMError}
  3856  			*/
  3857  			error: null,
  3858  			
  3859  			/**
  3860  			Initiates reading of File/Blob object contents to binary string.
  3861  
  3862  			@method readAsBinaryString
  3863  			@param {Blob|File} blob Object to preload
  3864  			*/
  3865  			readAsBinaryString: function(blob) {
  3866  				_read.call(this, 'readAsBinaryString', blob);
  3867  			},
  3868  			
  3869  			/**
  3870  			Initiates reading of File/Blob object contents to dataURL string.
  3871  
  3872  			@method readAsDataURL
  3873  			@param {Blob|File} blob Object to preload
  3874  			*/
  3875  			readAsDataURL: function(blob) {
  3876  				_read.call(this, 'readAsDataURL', blob);
  3877  			},
  3878  			
  3879  			/**
  3880  			Initiates reading of File/Blob object contents to string.
  3881  
  3882  			@method readAsText
  3883  			@param {Blob|File} blob Object to preload
  3884  			*/
  3885  			readAsText: function(blob) {
  3886  				_read.call(this, 'readAsText', blob);
  3887  			},
  3888  			
  3889  			/**
  3890  			Aborts preloading process.
  3891  
  3892  			@method abort
  3893  			*/
  3894  			abort: function() {
  3895  				this.result = null;
  3896  				
  3897  				if (Basic.inArray(this.readyState, [FileReader.EMPTY, FileReader.DONE]) !== -1) {
  3898  					return;
  3899  				} else if (this.readyState === FileReader.LOADING) {
  3900  					this.readyState = FileReader.DONE;
  3901  				}
  3902  
  3903  				if (_fr) {
  3904  					_fr.getRuntime().exec.call(this, 'FileReader', 'abort');
  3905  				}
  3906  				
  3907  				this.trigger('abort');
  3908  				this.trigger('loadend');
  3909  			},
  3910  
  3911  			/**
  3912  			Destroy component and release resources.
  3913  
  3914  			@method destroy
  3915  			*/
  3916  			destroy: function() {
  3917  				this.abort();
  3918  
  3919  				if (_fr) {
  3920  					_fr.getRuntime().exec.call(this, 'FileReader', 'destroy');
  3921  					_fr.disconnectRuntime();
  3922  				}
  3923  
  3924  				self = _fr = null;
  3925  			}
  3926  		});
  3927  		
  3928  		
  3929  		function _read(op, blob) {
  3930  			_fr = new RuntimeTarget();
  3931  
  3932  			function error(err) {
  3933  				self.readyState = FileReader.DONE;
  3934  				self.error = err;
  3935  				self.trigger('error');
  3936  				loadEnd();
  3937  			}
  3938  
  3939  			function loadEnd() {
  3940  				_fr.destroy();
  3941  				_fr = null;
  3942  				self.trigger('loadend');
  3943  			}
  3944  
  3945  			function exec(runtime) {
  3946  				_fr.bind('Error', function(e, err) {
  3947  					error(err);
  3948  				});
  3949  
  3950  				_fr.bind('Progress', function(e) {
  3951  					self.result = runtime.exec.call(_fr, 'FileReader', 'getResult');
  3952  					self.trigger(e);
  3953  				});
  3954  				
  3955  				_fr.bind('Load', function(e) {
  3956  					self.readyState = FileReader.DONE;
  3957  					self.result = runtime.exec.call(_fr, 'FileReader', 'getResult');
  3958  					self.trigger(e);
  3959  					loadEnd();
  3960  				});
  3961  
  3962  				runtime.exec.call(_fr, 'FileReader', 'read', op, blob);
  3963  			}
  3964  
  3965  			this.convertEventPropsToHandlers(dispatches);
  3966  
  3967  			if (this.readyState === FileReader.LOADING) {
  3968  				return error(new x.DOMException(x.DOMException.INVALID_STATE_ERR));
  3969  			}
  3970  
  3971  			this.readyState = FileReader.LOADING;
  3972  			this.trigger('loadstart');
  3973  
  3974  			// if source is o.Blob/o.File
  3975  			if (blob instanceof Blob) {
  3976  				if (blob.isDetached()) {
  3977  					var src = blob.getSource();
  3978  					switch (op) {
  3979  						case 'readAsText':
  3980  						case 'readAsBinaryString':
  3981  							this.result = src;
  3982  							break;
  3983  						case 'readAsDataURL':
  3984  							this.result = 'data:' + blob.type + ';base64,' + Encode.btoa(src);
  3985  							break;
  3986  					}
  3987  					this.readyState = FileReader.DONE;
  3988  					this.trigger('load');
  3989  					loadEnd();
  3990  				} else {
  3991  					exec(_fr.connectRuntime(blob.ruid));
  3992  				}
  3993  			} else {
  3994  				error(new x.DOMException(x.DOMException.NOT_FOUND_ERR));
  3995  			}
  3996  		}
  3997  	}
  3998  	
  3999  	/**
  4000  	Initial FileReader state
  4001  
  4002  	@property EMPTY
  4003  	@type {Number}
  4004  	@final
  4005  	@static
  4006  	@default 0
  4007  	*/
  4008  	FileReader.EMPTY = 0;
  4009  
  4010  	/**
  4011  	FileReader switches to this state when it is preloading the source
  4012  
  4013  	@property LOADING
  4014  	@type {Number}
  4015  	@final
  4016  	@static
  4017  	@default 1
  4018  	*/
  4019  	FileReader.LOADING = 1;
  4020  
  4021  	/**
  4022  	Preloading is complete, this is a final state
  4023  
  4024  	@property DONE
  4025  	@type {Number}
  4026  	@final
  4027  	@static
  4028  	@default 2
  4029  	*/
  4030  	FileReader.DONE = 2;
  4031  
  4032  	FileReader.prototype = EventTarget.instance;
  4033  
  4034  	return FileReader;
  4035  });
  4036  
  4037  // Included from: src/javascript/core/utils/Url.js
  4038  
  4039  /**
  4040   * Url.js
  4041   *
  4042   * Copyright 2013, Moxiecode Systems AB
  4043   * Released under GPL License.
  4044   *
  4045   * License: http://www.plupload.com/license
  4046   * Contributing: http://www.plupload.com/contributing
  4047   */
  4048  
  4049  define('moxie/core/utils/Url', [], function() {
  4050  	/**
  4051  	Parse url into separate components and fill in absent parts with parts from current url,
  4052  	based on https://raw.github.com/kvz/phpjs/master/functions/url/parse_url.js
  4053  
  4054  	@method parseUrl
  4055  	@for Utils
  4056  	@static
  4057  	@param {String} url Url to parse (defaults to empty string if undefined)
  4058  	@return {Object} Hash containing extracted uri components
  4059  	*/
  4060  	var parseUrl = function(url, currentUrl) {
  4061  		var key = ['source', 'scheme', 'authority', 'userInfo', 'user', 'pass', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment']
  4062  		, i = key.length
  4063  		, ports = {
  4064  			http: 80,
  4065  			https: 443
  4066  		}
  4067  		, uri = {}
  4068  		, regex = /^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/
  4069  		, m = regex.exec(url || '')
  4070  		;
  4071  					
  4072  		while (i--) {
  4073  			if (m[i]) {
  4074  				uri[key[i]] = m[i];
  4075  			}
  4076  		}
  4077  
  4078  		// when url is relative, we set the origin and the path ourselves
  4079  		if (!uri.scheme) {
  4080  			// come up with defaults
  4081  			if (!currentUrl || typeof(currentUrl) === 'string') {
  4082  				currentUrl = parseUrl(currentUrl || document.location.href);
  4083  			}
  4084  
  4085  			uri.scheme = currentUrl.scheme;
  4086  			uri.host = currentUrl.host;
  4087  			uri.port = currentUrl.port;
  4088  
  4089  			var path = '';
  4090  			// for urls without trailing slash we need to figure out the path
  4091  			if (/^[^\/]/.test(uri.path)) {
  4092  				path = currentUrl.path;
  4093  				// if path ends with a filename, strip it
  4094  				if (!/(\/|\/[^\.]+)$/.test(path)) {
  4095  					path = path.replace(/\/[^\/]+$/, '/');
  4096  				} else {
  4097  					path += '/';
  4098  				}
  4099  			}
  4100  			uri.path = path + (uri.path || ''); // site may reside at domain.com or domain.com/subdir
  4101  		}
  4102  
  4103  		if (!uri.port) {
  4104  			uri.port = ports[uri.scheme] || 80;
  4105  		} 
  4106  		
  4107  		uri.port = parseInt(uri.port, 10);
  4108  
  4109  		if (!uri.path) {
  4110  			uri.path = "/";
  4111  		}
  4112  
  4113  		delete uri.source;
  4114  
  4115  		return uri;
  4116  	};
  4117  
  4118  	/**
  4119  	Resolve url - among other things will turn relative url to absolute
  4120  
  4121  	@method resolveUrl
  4122  	@static
  4123  	@param {String} url Either absolute or relative
  4124  	@return {String} Resolved, absolute url
  4125  	*/
  4126  	var resolveUrl = function(url) {
  4127  		var ports = { // we ignore default ports
  4128  			http: 80,
  4129  			https: 443
  4130  		}
  4131  		, urlp = parseUrl(url)
  4132  		;
  4133  
  4134  		return urlp.scheme + '://' + urlp.host + (urlp.port !== ports[urlp.scheme] ? ':' + urlp.port : '') + urlp.path + (urlp.query ? urlp.query : '');
  4135  	};
  4136  
  4137  	/**
  4138  	Check if specified url has the same origin as the current document
  4139  
  4140  	@method hasSameOrigin
  4141  	@param {String|Object} url
  4142  	@return {Boolean}
  4143  	*/
  4144  	var hasSameOrigin = function(url) {
  4145  		function origin(url) {
  4146  			return [url.scheme, url.host, url.port].join('/');
  4147  		}
  4148  			
  4149  		if (typeof url === 'string') {
  4150  			url = parseUrl(url);
  4151  		}	
  4152  		
  4153  		return origin(parseUrl()) === origin(url);
  4154  	};
  4155  
  4156  	return {
  4157  		parseUrl: parseUrl,
  4158  		resolveUrl: resolveUrl,
  4159  		hasSameOrigin: hasSameOrigin
  4160  	};
  4161  });
  4162  
  4163  // Included from: src/javascript/file/FileReaderSync.js
  4164  
  4165  /**
  4166   * FileReaderSync.js
  4167   *
  4168   * Copyright 2013, Moxiecode Systems AB
  4169   * Released under GPL License.
  4170   *
  4171   * License: http://www.plupload.com/license
  4172   * Contributing: http://www.plupload.com/contributing
  4173   */
  4174  
  4175  define('moxie/file/FileReaderSync', [
  4176  	'moxie/core/utils/Basic',
  4177  	'moxie/runtime/RuntimeClient',
  4178  	'moxie/core/utils/Encode'
  4179  ], function(Basic, RuntimeClient, Encode) {
  4180  	/**
  4181  	Synchronous FileReader implementation. Something like this is available in WebWorkers environment, here
  4182  	it can be used to read only preloaded blobs/files and only below certain size (not yet sure what that'd be,
  4183  	but probably < 1mb). Not meant to be used directly by user.
  4184  
  4185  	@class FileReaderSync
  4186  	@private
  4187  	@constructor
  4188  	*/
  4189  	return function() {
  4190  		RuntimeClient.call(this);
  4191  
  4192  		Basic.extend(this, {
  4193  			uid: Basic.guid('uid_'),
  4194  
  4195  			readAsBinaryString: function(blob) {
  4196  				return _read.call(this, 'readAsBinaryString', blob);
  4197  			},
  4198  			
  4199  			readAsDataURL: function(blob) {
  4200  				return _read.call(this, 'readAsDataURL', blob);
  4201  			},
  4202  			
  4203  			/*readAsArrayBuffer: function(blob) {
  4204  				return _read.call(this, 'readAsArrayBuffer', blob);
  4205  			},*/
  4206  			
  4207  			readAsText: function(blob) {
  4208  				return _read.call(this, 'readAsText', blob);
  4209  			}
  4210  		});
  4211  
  4212  		function _read(op, blob) {
  4213  			if (blob.isDetached()) {
  4214  				var src = blob.getSource();
  4215  				switch (op) {
  4216  					case 'readAsBinaryString':
  4217  						return src;
  4218  					case 'readAsDataURL':
  4219  						return 'data:' + blob.type + ';base64,' + Encode.btoa(src);
  4220  					case 'readAsText':
  4221  						var txt = '';
  4222  						for (var i = 0, length = src.length; i < length; i++) {
  4223  							txt += String.fromCharCode(src[i]);
  4224  						}
  4225  						return txt;
  4226  				}
  4227  			} else {
  4228  				var result = this.connectRuntime(blob.ruid).exec.call(this, 'FileReaderSync', 'read', op, blob);
  4229  				this.disconnectRuntime();
  4230  				return result;
  4231  			}
  4232  		}
  4233  	};
  4234  });
  4235  
  4236  // Included from: src/javascript/xhr/FormData.js
  4237  
  4238  /**
  4239   * FormData.js
  4240   *
  4241   * Copyright 2013, Moxiecode Systems AB
  4242   * Released under GPL License.
  4243   *
  4244   * License: http://www.plupload.com/license
  4245   * Contributing: http://www.plupload.com/contributing
  4246   */
  4247  
  4248  define("moxie/xhr/FormData", [
  4249  	"moxie/core/Exceptions",
  4250  	"moxie/core/utils/Basic",
  4251  	"moxie/file/Blob"
  4252  ], function(x, Basic, Blob) {
  4253  	/**
  4254  	FormData
  4255  
  4256  	@class FormData
  4257  	@constructor
  4258  	*/
  4259  	function FormData() {
  4260  		var _blob, _fields = [];
  4261  
  4262  		Basic.extend(this, {
  4263  			/**
  4264  			Append another key-value pair to the FormData object
  4265  
  4266  			@method append
  4267  			@param {String} name Name for the new field
  4268  			@param {String|Blob|Array|Object} value Value for the field
  4269  			*/
  4270  			append: function(name, value) {
  4271  				var self = this, valueType = Basic.typeOf(value);
  4272  
  4273  				// according to specs value might be either Blob or String
  4274  				if (value instanceof Blob) {
  4275  					_blob = {
  4276  						name: name,
  4277  						value: value // unfortunately we can only send single Blob in one FormData
  4278  					};
  4279  				} else if ('array' === valueType) {
  4280  					name += '[]';
  4281  
  4282  					Basic.each(value, function(value) {
  4283  						self.append(name, value);
  4284  					});
  4285  				} else if ('object' === valueType) {
  4286  					Basic.each(value, function(value, key) {
  4287  						self.append(name + '[' + key + ']', value);
  4288  					});
  4289  				} else if ('null' === valueType || 'undefined' === valueType || 'number' === valueType && isNaN(value)) {
  4290  					self.append(name, "false");
  4291  				} else {
  4292  					_fields.push({
  4293  						name: name,
  4294  						value: value.toString()
  4295  					});
  4296  				}
  4297  			},
  4298  
  4299  			/**
  4300  			Checks if FormData contains Blob.
  4301  
  4302  			@method hasBlob
  4303  			@return {Boolean}
  4304  			*/
  4305  			hasBlob: function() {
  4306  				return !!this.getBlob();
  4307  			},
  4308  
  4309  			/**
  4310  			Retrieves blob.
  4311  
  4312  			@method getBlob
  4313  			@return {Object} Either Blob if found or null
  4314  			*/
  4315  			getBlob: function() {
  4316  				return _blob && _blob.value || null;
  4317  			},
  4318  
  4319  			/**
  4320  			Retrieves blob field name.
  4321  
  4322  			@method getBlobName
  4323  			@return {String} Either Blob field name or null
  4324  			*/
  4325  			getBlobName: function() {
  4326  				return _blob && _blob.name || null;
  4327  			},
  4328  
  4329  			/**
  4330  			Loop over the fields in FormData and invoke the callback for each of them.
  4331  
  4332  			@method each
  4333  			@param {Function} cb Callback to call for each field
  4334  			*/
  4335  			each: function(cb) {
  4336  				Basic.each(_fields, function(field) {
  4337  					cb(field.value, field.name);
  4338  				});
  4339  
  4340  				if (_blob) {
  4341  					cb(_blob.value, _blob.name);
  4342  				}
  4343  			},
  4344  
  4345  			destroy: function() {
  4346  				_blob = null;
  4347  				_fields = [];
  4348  			}
  4349  		});
  4350  	}
  4351  
  4352  	return FormData;
  4353  });
  4354  
  4355  // Included from: src/javascript/xhr/XMLHttpRequest.js
  4356  
  4357  /**
  4358   * XMLHttpRequest.js
  4359   *
  4360   * Copyright 2013, Moxiecode Systems AB
  4361   * Released under GPL License.
  4362   *
  4363   * License: http://www.plupload.com/license
  4364   * Contributing: http://www.plupload.com/contributing
  4365   */
  4366  
  4367  define("moxie/xhr/XMLHttpRequest", [
  4368  	"moxie/core/utils/Basic",
  4369  	"moxie/core/Exceptions",
  4370  	"moxie/core/EventTarget",
  4371  	"moxie/core/utils/Encode",
  4372  	"moxie/core/utils/Url",
  4373  	"moxie/runtime/Runtime",
  4374  	"moxie/runtime/RuntimeTarget",
  4375  	"moxie/file/Blob",
  4376  	"moxie/file/FileReaderSync",
  4377  	"moxie/xhr/FormData",
  4378  	"moxie/core/utils/Env",
  4379  	"moxie/core/utils/Mime"
  4380  ], function(Basic, x, EventTarget, Encode, Url, Runtime, RuntimeTarget, Blob, FileReaderSync, FormData, Env, Mime) {
  4381  
  4382  	var httpCode = {
  4383  		100: 'Continue',
  4384  		101: 'Switching Protocols',
  4385  		102: 'Processing',
  4386  
  4387  		200: 'OK',
  4388  		201: 'Created',
  4389  		202: 'Accepted',
  4390  		203: 'Non-Authoritative Information',
  4391  		204: 'No Content',
  4392  		205: 'Reset Content',
  4393  		206: 'Partial Content',
  4394  		207: 'Multi-Status',
  4395  		226: 'IM Used',
  4396  
  4397  		300: 'Multiple Choices',
  4398  		301: 'Moved Permanently',
  4399  		302: 'Found',
  4400  		303: 'See Other',
  4401  		304: 'Not Modified',
  4402  		305: 'Use Proxy',
  4403  		306: 'Reserved',
  4404  		307: 'Temporary Redirect',
  4405  
  4406  		400: 'Bad Request',
  4407  		401: 'Unauthorized',
  4408  		402: 'Payment Required',
  4409  		403: 'Forbidden',
  4410  		404: 'Not Found',
  4411  		405: 'Method Not Allowed',
  4412  		406: 'Not Acceptable',
  4413  		407: 'Proxy Authentication Required',
  4414  		408: 'Request Timeout',
  4415  		409: 'Conflict',
  4416  		410: 'Gone',
  4417  		411: 'Length Required',
  4418  		412: 'Precondition Failed',
  4419  		413: 'Request Entity Too Large',
  4420  		414: 'Request-URI Too Long',
  4421  		415: 'Unsupported Media Type',
  4422  		416: 'Requested Range Not Satisfiable',
  4423  		417: 'Expectation Failed',
  4424  		422: 'Unprocessable Entity',
  4425  		423: 'Locked',
  4426  		424: 'Failed Dependency',
  4427  		426: 'Upgrade Required',
  4428  
  4429  		500: 'Internal Server Error',
  4430  		501: 'Not Implemented',
  4431  		502: 'Bad Gateway',
  4432  		503: 'Service Unavailable',
  4433  		504: 'Gateway Timeout',
  4434  		505: 'HTTP Version Not Supported',
  4435  		506: 'Variant Also Negotiates',
  4436  		507: 'Insufficient Storage',
  4437  		510: 'Not Extended'
  4438  	};
  4439  
  4440  	function XMLHttpRequestUpload() {
  4441  		this.uid = Basic.guid('uid_');
  4442  	}
  4443  	
  4444  	XMLHttpRequestUpload.prototype = EventTarget.instance;
  4445  
  4446  	/**
  4447  	Implementation of XMLHttpRequest
  4448  
  4449  	@class XMLHttpRequest
  4450  	@constructor
  4451  	@uses RuntimeClient
  4452  	@extends EventTarget
  4453  	*/
  4454  	var dispatches = ['loadstart', 'progress', 'abort', 'error', 'load', 'timeout', 'loadend']; // & readystatechange (for historical reasons)
  4455  	
  4456  	var NATIVE = 1, RUNTIME = 2;
  4457  					
  4458  	function XMLHttpRequest() {
  4459  		var self = this,
  4460  			// this (together with _p() @see below) is here to gracefully upgrade to setter/getter syntax where possible
  4461  			props = {
  4462  				/**
  4463  				The amount of milliseconds a request can take before being terminated. Initially zero. Zero means there is no timeout.
  4464  
  4465  				@property timeout
  4466  				@type Number
  4467  				@default 0
  4468  				*/
  4469  				timeout: 0,
  4470  
  4471  				/**
  4472  				Current state, can take following values:
  4473  				UNSENT (numeric value 0)
  4474  				The object has been constructed.
  4475  
  4476  				OPENED (numeric value 1)
  4477  				The open() method has been successfully invoked. During this state request headers can be set using setRequestHeader() and the request can be made using the send() method.
  4478  
  4479  				HEADERS_RECEIVED (numeric value 2)
  4480  				All redirects (if any) have been followed and all HTTP headers of the final response have been received. Several response members of the object are now available.
  4481  
  4482  				LOADING (numeric value 3)
  4483  				The response entity body is being received.
  4484  
  4485  				DONE (numeric value 4)
  4486  
  4487  				@property readyState
  4488  				@type Number
  4489  				@default 0 (UNSENT)
  4490  				*/
  4491  				readyState: XMLHttpRequest.UNSENT,
  4492  
  4493  				/**
  4494  				True when user credentials are to be included in a cross-origin request. False when they are to be excluded
  4495  				in a cross-origin request and when cookies are to be ignored in its response. Initially false.
  4496  
  4497  				@property withCredentials
  4498  				@type Boolean
  4499  				@default false
  4500  				*/
  4501  				withCredentials: false,
  4502  
  4503  				/**
  4504  				Returns the HTTP status code.
  4505  
  4506  				@property status
  4507  				@type Number
  4508  				@default 0
  4509  				*/
  4510  				status: 0,
  4511  
  4512  				/**
  4513  				Returns the HTTP status text.
  4514  
  4515  				@property statusText
  4516  				@type String
  4517  				*/
  4518  				statusText: "",
  4519  
  4520  				/**
  4521  				Returns the response type. Can be set to change the response type. Values are:
  4522  				the empty string (default), "arraybuffer", "blob", "document", "json", and "text".
  4523  				
  4524  				@property responseType
  4525  				@type String
  4526  				*/
  4527  				responseType: "",
  4528  
  4529  				/**
  4530  				Returns the document response entity body.
  4531  				
  4532  				Throws an "InvalidStateError" exception if responseType is not the empty string or "document".
  4533  
  4534  				@property responseXML
  4535  				@type Document
  4536  				*/
  4537  				responseXML: null,
  4538  
  4539  				/**
  4540  				Returns the text response entity body.
  4541  				
  4542  				Throws an "InvalidStateError" exception if responseType is not the empty string or "text".
  4543  
  4544  				@property responseText
  4545  				@type String
  4546  				*/
  4547  				responseText: null,
  4548  
  4549  				/**
  4550  				Returns the response entity body (http://www.w3.org/TR/XMLHttpRequest/#response-entity-body).
  4551  				Can become: ArrayBuffer, Blob, Document, JSON, Text
  4552  				
  4553  				@property response
  4554  				@type Mixed
  4555  				*/
  4556  				response: null
  4557  			},
  4558  
  4559  			_async = true,
  4560  			_url,
  4561  			_method,
  4562  			_headers = {},
  4563  			_user,
  4564  			_password,
  4565  			_encoding = null,
  4566  			_mimeType = null,
  4567  
  4568  			// flags
  4569  			_sync_flag = false,
  4570  			_send_flag = false,
  4571  			_upload_events_flag = false,
  4572  			_upload_complete_flag = false,
  4573  			_error_flag = false,
  4574  			_same_origin_flag = false,
  4575  
  4576  			// times
  4577  			_start_time,
  4578  			_timeoutset_time,
  4579  
  4580  			_finalMime = null,
  4581  			_finalCharset = null,
  4582  
  4583  			_options = {},
  4584  			_xhr,
  4585  			_responseHeaders = '',
  4586  			_responseHeadersBag
  4587  			;
  4588  
  4589  		
  4590  		Basic.extend(this, props, {
  4591  			/**
  4592  			Unique id of the component
  4593  
  4594  			@property uid
  4595  			@type String
  4596  			*/
  4597  			uid: Basic.guid('uid_'),
  4598  			
  4599  			/**
  4600  			Target for Upload events
  4601  
  4602  			@property upload
  4603  			@type XMLHttpRequestUpload
  4604  			*/
  4605  			upload: new XMLHttpRequestUpload(),
  4606  			
  4607  
  4608  			/**
  4609  			Sets the request method, request URL, synchronous flag, request username, and request password.
  4610  
  4611  			Throws a "SyntaxError" exception if one of the following is true:
  4612  
  4613  			method is not a valid HTTP method.
  4614  			url cannot be resolved.
  4615  			url contains the "user:password" format in the userinfo production.
  4616  			Throws a "SecurityError" exception if method is a case-insensitive match for CONNECT, TRACE or TRACK.
  4617  
  4618  			Throws an "InvalidAccessError" exception if one of the following is true:
  4619  
  4620  			Either user or password is passed as argument and the origin of url does not match the XMLHttpRequest origin.
  4621  			There is an associated XMLHttpRequest document and either the timeout attribute is not zero,
  4622  			the withCredentials attribute is true, or the responseType attribute is not the empty string.
  4623  
  4624  
  4625  			@method open
  4626  			@param {String} method HTTP method to use on request
  4627  			@param {String} url URL to request
  4628  			@param {Boolean} [async=true] If false request will be done in synchronous manner. Asynchronous by default.
  4629  			@param {String} [user] Username to use in HTTP authentication process on server-side
  4630  			@param {String} [password] Password to use in HTTP authentication process on server-side
  4631  			*/
  4632  			open: function(method, url, async, user, password) {
  4633  				var urlp;
  4634  				
  4635  				// first two arguments are required
  4636  				if (!method || !url) {
  4637  					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  4638  				}
  4639  				
  4640  				// 2 - check if any code point in method is higher than U+00FF or after deflating method it does not match the method
  4641  				if (/[\u0100-\uffff]/.test(method) || Encode.utf8_encode(method) !== method) {
  4642  					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  4643  				}
  4644  
  4645  				// 3
  4646  				if (!!~Basic.inArray(method.toUpperCase(), ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'TRACE', 'TRACK'])) {
  4647  					_method = method.toUpperCase();
  4648  				}
  4649  				
  4650  				
  4651  				// 4 - allowing these methods poses a security risk
  4652  				if (!!~Basic.inArray(_method, ['CONNECT', 'TRACE', 'TRACK'])) {
  4653  					throw new x.DOMException(x.DOMException.SECURITY_ERR);
  4654  				}
  4655  
  4656  				// 5
  4657  				url = Encode.utf8_encode(url);
  4658  				
  4659  				// 6 - Resolve url relative to the XMLHttpRequest base URL. If the algorithm returns an error, throw a "SyntaxError".
  4660  				urlp = Url.parseUrl(url);
  4661  
  4662  				_same_origin_flag = Url.hasSameOrigin(urlp);
  4663  																
  4664  				// 7 - manually build up absolute url
  4665  				_url = Url.resolveUrl(url);
  4666  		
  4667  				// 9-10, 12-13
  4668  				if ((user || password) && !_same_origin_flag) {
  4669  					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  4670  				}
  4671  
  4672  				_user = user || urlp.user;
  4673  				_password = password || urlp.pass;
  4674  				
  4675  				// 11
  4676  				_async = async || true;
  4677  				
  4678  				if (_async === false && (_p('timeout') || _p('withCredentials') || _p('responseType') !== "")) {
  4679  					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  4680  				}
  4681  				
  4682  				// 14 - terminate abort()
  4683  				
  4684  				// 15 - terminate send()
  4685  
  4686  				// 18
  4687  				_sync_flag = !_async;
  4688  				_send_flag = false;
  4689  				_headers = {};
  4690  				_reset.call(this);
  4691  
  4692  				// 19
  4693  				_p('readyState', XMLHttpRequest.OPENED);
  4694  				
  4695  				// 20
  4696  				this.convertEventPropsToHandlers(['readystatechange']); // unify event handlers
  4697  				this.dispatchEvent('readystatechange');
  4698  			},
  4699  			
  4700  			/**
  4701  			Appends an header to the list of author request headers, or if header is already
  4702  			in the list of author request headers, combines its value with value.
  4703  
  4704  			Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set.
  4705  			Throws a "SyntaxError" exception if header is not a valid HTTP header field name or if value
  4706  			is not a valid HTTP header field value.
  4707  			
  4708  			@method setRequestHeader
  4709  			@param {String} header
  4710  			@param {String|Number} value
  4711  			*/
  4712  			setRequestHeader: function(header, value) {
  4713  				var uaHeaders = [ // these headers are controlled by the user agent
  4714  						"accept-charset",
  4715  						"accept-encoding",
  4716  						"access-control-request-headers",
  4717  						"access-control-request-method",
  4718  						"connection",
  4719  						"content-length",
  4720  						"cookie",
  4721  						"cookie2",
  4722  						"content-transfer-encoding",
  4723  						"date",
  4724  						"expect",
  4725  						"host",
  4726  						"keep-alive",
  4727  						"origin",
  4728  						"referer",
  4729  						"te",
  4730  						"trailer",
  4731  						"transfer-encoding",
  4732  						"upgrade",
  4733  						"user-agent",
  4734  						"via"
  4735  					];
  4736  				
  4737  				// 1-2
  4738  				if (_p('readyState') !== XMLHttpRequest.OPENED || _send_flag) {
  4739  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4740  				}
  4741  
  4742  				// 3
  4743  				if (/[\u0100-\uffff]/.test(header) || Encode.utf8_encode(header) !== header) {
  4744  					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  4745  				}
  4746  
  4747  				// 4
  4748  				/* this step is seemingly bypassed in browsers, probably to allow various unicode characters in header values
  4749  				if (/[\u0100-\uffff]/.test(value) || Encode.utf8_encode(value) !== value) {
  4750  					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  4751  				}*/
  4752  
  4753  				header = Basic.trim(header).toLowerCase();
  4754  				
  4755  				// setting of proxy-* and sec-* headers is prohibited by spec
  4756  				if (!!~Basic.inArray(header, uaHeaders) || /^(proxy\-|sec\-)/.test(header)) {
  4757  					return false;
  4758  				}
  4759  
  4760  				// camelize
  4761  				// browsers lowercase header names (at least for custom ones)
  4762  				// header = header.replace(/\b\w/g, function($1) { return $1.toUpperCase(); });
  4763  				
  4764  				if (!_headers[header]) {
  4765  					_headers[header] = value;
  4766  				} else {
  4767  					// http://tools.ietf.org/html/rfc2616#section-4.2 (last paragraph)
  4768  					_headers[header] += ', ' + value;
  4769  				}
  4770  				return true;
  4771  			},
  4772  
  4773  			/**
  4774  			Returns all headers from the response, with the exception of those whose field name is Set-Cookie or Set-Cookie2.
  4775  
  4776  			@method getAllResponseHeaders
  4777  			@return {String} reponse headers or empty string
  4778  			*/
  4779  			getAllResponseHeaders: function() {
  4780  				return _responseHeaders || '';
  4781  			},
  4782  
  4783  			/**
  4784  			Returns the header field value from the response of which the field name matches header, 
  4785  			unless the field name is Set-Cookie or Set-Cookie2.
  4786  
  4787  			@method getResponseHeader
  4788  			@param {String} header
  4789  			@return {String} value(s) for the specified header or null
  4790  			*/
  4791  			getResponseHeader: function(header) {
  4792  				header = header.toLowerCase();
  4793  
  4794  				if (_error_flag || !!~Basic.inArray(header, ['set-cookie', 'set-cookie2'])) {
  4795  					return null;
  4796  				}
  4797  
  4798  				if (_responseHeaders && _responseHeaders !== '') {
  4799  					// if we didn't parse response headers until now, do it and keep for later
  4800  					if (!_responseHeadersBag) {
  4801  						_responseHeadersBag = {};
  4802  						Basic.each(_responseHeaders.split(/\r\n/), function(line) {
  4803  							var pair = line.split(/:\s+/);
  4804  							if (pair.length === 2) { // last line might be empty, omit
  4805  								pair[0] = Basic.trim(pair[0]); // just in case
  4806  								_responseHeadersBag[pair[0].toLowerCase()] = { // simply to retain header name in original form
  4807  									header: pair[0],
  4808  									value: Basic.trim(pair[1])
  4809  								};
  4810  							}
  4811  						});
  4812  					}
  4813  					if (_responseHeadersBag.hasOwnProperty(header)) {
  4814  						return _responseHeadersBag[header].header + ': ' + _responseHeadersBag[header].value;
  4815  					}
  4816  				}
  4817  				return null;
  4818  			},
  4819  			
  4820  			/**
  4821  			Sets the Content-Type header for the response to mime.
  4822  			Throws an "InvalidStateError" exception if the state is LOADING or DONE.
  4823  			Throws a "SyntaxError" exception if mime is not a valid media type.
  4824  
  4825  			@method overrideMimeType
  4826  			@param String mime Mime type to set
  4827  			*/
  4828  			overrideMimeType: function(mime) {
  4829  				var matches, charset;
  4830  			
  4831  				// 1
  4832  				if (!!~Basic.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) {
  4833  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4834  				}
  4835  
  4836  				// 2
  4837  				mime = Basic.trim(mime.toLowerCase());
  4838  
  4839  				if (/;/.test(mime) && (matches = mime.match(/^([^;]+)(?:;\scharset\=)?(.*)$/))) {
  4840  					mime = matches[1];
  4841  					if (matches[2]) {
  4842  						charset = matches[2];
  4843  					}
  4844  				}
  4845  
  4846  				if (!Mime.mimes[mime]) {
  4847  					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  4848  				}
  4849  
  4850  				// 3-4
  4851  				_finalMime = mime;
  4852  				_finalCharset = charset;
  4853  			},
  4854  			
  4855  			/**
  4856  			Initiates the request. The optional argument provides the request entity body.
  4857  			The argument is ignored if request method is GET or HEAD.
  4858  
  4859  			Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set.
  4860  
  4861  			@method send
  4862  			@param {Blob|Document|String|FormData} [data] Request entity body
  4863  			@param {Object} [options] Set of requirements and pre-requisities for runtime initialization
  4864  			*/
  4865  			send: function(data, options) {					
  4866  				if (Basic.typeOf(options) === 'string') {
  4867  					_options = { ruid: options };
  4868  				} else if (!options) {
  4869  					_options = {};
  4870  				} else {
  4871  					_options = options;
  4872  				}
  4873  													
  4874  				this.convertEventPropsToHandlers(dispatches);
  4875  				this.upload.convertEventPropsToHandlers(dispatches);
  4876  															
  4877  				// 1-2
  4878  				if (this.readyState !== XMLHttpRequest.OPENED || _send_flag) {
  4879  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4880  				}
  4881  				
  4882  				// 3					
  4883  				// sending Blob
  4884  				if (data instanceof Blob) {
  4885  					_options.ruid = data.ruid;
  4886  					_mimeType = data.type || 'application/octet-stream';
  4887  				}
  4888  				
  4889  				// FormData
  4890  				else if (data instanceof FormData) {
  4891  					if (data.hasBlob()) {
  4892  						var blob = data.getBlob();
  4893  						_options.ruid = blob.ruid;
  4894  						_mimeType = blob.type || 'application/octet-stream';
  4895  					}
  4896  				}
  4897  				
  4898  				// DOMString
  4899  				else if (typeof data === 'string') {
  4900  					_encoding = 'UTF-8';
  4901  					_mimeType = 'text/plain;charset=UTF-8';
  4902  					
  4903  					// data should be converted to Unicode and encoded as UTF-8
  4904  					data = Encode.utf8_encode(data);
  4905  				}
  4906  
  4907  				// if withCredentials not set, but requested, set it automatically
  4908  				if (!this.withCredentials) {
  4909  					this.withCredentials = (_options.required_caps && _options.required_caps.send_browser_cookies) && !_same_origin_flag;
  4910  				}
  4911  
  4912  				// 4 - storage mutex
  4913  				// 5
  4914  				_upload_events_flag = (!_sync_flag && this.upload.hasEventListener()); // DSAP
  4915  				// 6
  4916  				_error_flag = false;
  4917  				// 7
  4918  				_upload_complete_flag = !data;
  4919  				// 8 - Asynchronous steps
  4920  				if (!_sync_flag) {
  4921  					// 8.1
  4922  					_send_flag = true;
  4923  					// 8.2
  4924  					// this.dispatchEvent('loadstart'); // will be dispatched either by native or runtime xhr
  4925  					// 8.3
  4926  					//if (!_upload_complete_flag) {
  4927  						// this.upload.dispatchEvent('loadstart');	// will be dispatched either by native or runtime xhr
  4928  					//}
  4929  				}
  4930  				// 8.5 - Return the send() method call, but continue running the steps in this algorithm.
  4931  				_doXHR.call(this, data);
  4932  			},
  4933  			
  4934  			/**
  4935  			Cancels any network activity.
  4936  			
  4937  			@method abort
  4938  			*/
  4939  			abort: function() {
  4940  				_error_flag = true;
  4941  				_sync_flag = false;
  4942  
  4943  				if (!~Basic.inArray(_p('readyState'), [XMLHttpRequest.UNSENT, XMLHttpRequest.OPENED, XMLHttpRequest.DONE])) {
  4944  					_p('readyState', XMLHttpRequest.DONE);
  4945  					_send_flag = false;
  4946  
  4947  					if (_xhr) {
  4948  						_xhr.getRuntime().exec.call(_xhr, 'XMLHttpRequest', 'abort', _upload_complete_flag);
  4949  					} else {
  4950  						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4951  					}
  4952  
  4953  					_upload_complete_flag = true;
  4954  				} else {
  4955  					_p('readyState', XMLHttpRequest.UNSENT);
  4956  				}
  4957  			},
  4958  
  4959  			destroy: function() {
  4960  				if (_xhr) {
  4961  					if (Basic.typeOf(_xhr.destroy) === 'function') {
  4962  						_xhr.destroy();
  4963  					}
  4964  					_xhr = null;
  4965  				}
  4966  
  4967  				this.unbindAll();
  4968  
  4969  				if (this.upload) {
  4970  					this.upload.unbindAll();
  4971  					this.upload = null;
  4972  				}
  4973  			}
  4974  		});
  4975  
  4976  		/* this is nice, but maybe too lengthy
  4977  
  4978  		// if supported by JS version, set getters/setters for specific properties
  4979  		o.defineProperty(this, 'readyState', {
  4980  			configurable: false,
  4981  
  4982  			get: function() {
  4983  				return _p('readyState');
  4984  			}
  4985  		});
  4986  
  4987  		o.defineProperty(this, 'timeout', {
  4988  			configurable: false,
  4989  
  4990  			get: function() {
  4991  				return _p('timeout');
  4992  			},
  4993  
  4994  			set: function(value) {
  4995  
  4996  				if (_sync_flag) {
  4997  					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  4998  				}
  4999  
  5000  				// timeout still should be measured relative to the start time of request
  5001  				_timeoutset_time = (new Date).getTime();
  5002  
  5003  				_p('timeout', value);
  5004  			}
  5005  		});
  5006  
  5007  		// the withCredentials attribute has no effect when fetching same-origin resources
  5008  		o.defineProperty(this, 'withCredentials', {
  5009  			configurable: false,
  5010  
  5011  			get: function() {
  5012  				return _p('withCredentials');
  5013  			},
  5014  
  5015  			set: function(value) {
  5016  				// 1-2
  5017  				if (!~o.inArray(_p('readyState'), [XMLHttpRequest.UNSENT, XMLHttpRequest.OPENED]) || _send_flag) {
  5018  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5019  				}
  5020  
  5021  				// 3-4
  5022  				if (_anonymous_flag || _sync_flag) {
  5023  					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  5024  				}
  5025  
  5026  				// 5
  5027  				_p('withCredentials', value);
  5028  			}
  5029  		});
  5030  
  5031  		o.defineProperty(this, 'status', {
  5032  			configurable: false,
  5033  
  5034  			get: function() {
  5035  				return _p('status');
  5036  			}
  5037  		});
  5038  
  5039  		o.defineProperty(this, 'statusText', {
  5040  			configurable: false,
  5041  
  5042  			get: function() {
  5043  				return _p('statusText');
  5044  			}
  5045  		});
  5046  
  5047  		o.defineProperty(this, 'responseType', {
  5048  			configurable: false,
  5049  
  5050  			get: function() {
  5051  				return _p('responseType');
  5052  			},
  5053  
  5054  			set: function(value) {
  5055  				// 1
  5056  				if (!!~o.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) {
  5057  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5058  				}
  5059  
  5060  				// 2
  5061  				if (_sync_flag) {
  5062  					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  5063  				}
  5064  
  5065  				// 3
  5066  				_p('responseType', value.toLowerCase());
  5067  			}
  5068  		});
  5069  
  5070  		o.defineProperty(this, 'responseText', {
  5071  			configurable: false,
  5072  
  5073  			get: function() {
  5074  				// 1
  5075  				if (!~o.inArray(_p('responseType'), ['', 'text'])) {
  5076  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5077  				}
  5078  
  5079  				// 2-3
  5080  				if (_p('readyState') !== XMLHttpRequest.DONE && _p('readyState') !== XMLHttpRequest.LOADING || _error_flag) {
  5081  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5082  				}
  5083  
  5084  				return _p('responseText');
  5085  			}
  5086  		});
  5087  
  5088  		o.defineProperty(this, 'responseXML', {
  5089  			configurable: false,
  5090  
  5091  			get: function() {
  5092  				// 1
  5093  				if (!~o.inArray(_p('responseType'), ['', 'document'])) {
  5094  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5095  				}
  5096  
  5097  				// 2-3
  5098  				if (_p('readyState') !== XMLHttpRequest.DONE || _error_flag) {
  5099  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5100  				}
  5101  
  5102  				return _p('responseXML');
  5103  			}
  5104  		});
  5105  
  5106  		o.defineProperty(this, 'response', {
  5107  			configurable: false,
  5108  
  5109  			get: function() {
  5110  				if (!!~o.inArray(_p('responseType'), ['', 'text'])) {
  5111  					if (_p('readyState') !== XMLHttpRequest.DONE && _p('readyState') !== XMLHttpRequest.LOADING || _error_flag) {
  5112  						return '';
  5113  					}
  5114  				}
  5115  
  5116  				if (_p('readyState') !== XMLHttpRequest.DONE || _error_flag) {
  5117  					return null;
  5118  				}
  5119  
  5120  				return _p('response');
  5121  			}
  5122  		});
  5123  
  5124  		*/
  5125  
  5126  		function _p(prop, value) {
  5127  			if (!props.hasOwnProperty(prop)) {
  5128  				return;
  5129  			}
  5130  			if (arguments.length === 1) { // get
  5131  				return Env.can('define_property') ? props[prop] : self[prop];
  5132  			} else { // set
  5133  				if (Env.can('define_property')) {
  5134  					props[prop] = value;
  5135  				} else {
  5136  					self[prop] = value;
  5137  				}
  5138  			}
  5139  		}
  5140  		
  5141  		/*
  5142  		function _toASCII(str, AllowUnassigned, UseSTD3ASCIIRules) {
  5143  			// TODO: http://tools.ietf.org/html/rfc3490#section-4.1
  5144  			return str.toLowerCase();
  5145  		}
  5146  		*/
  5147  		
  5148  		
  5149  		function _doXHR(data) {
  5150  			var self = this;
  5151  			
  5152  			_start_time = new Date().getTime();
  5153  
  5154  			_xhr = new RuntimeTarget();
  5155  
  5156  			function loadEnd() {
  5157  				if (_xhr) { // it could have been destroyed by now
  5158  					_xhr.destroy();
  5159  					_xhr = null;
  5160  				}
  5161  				self.dispatchEvent('loadend');
  5162  				self = null;
  5163  			}
  5164  
  5165  			function exec(runtime) {
  5166  				_xhr.bind('LoadStart', function(e) {
  5167  					_p('readyState', XMLHttpRequest.LOADING);
  5168  					self.dispatchEvent('readystatechange');
  5169  
  5170  					self.dispatchEvent(e);
  5171  					
  5172  					if (_upload_events_flag) {
  5173  						self.upload.dispatchEvent(e);
  5174  					}
  5175  				});
  5176  				
  5177  				_xhr.bind('Progress', function(e) {
  5178  					if (_p('readyState') !== XMLHttpRequest.LOADING) {
  5179  						_p('readyState', XMLHttpRequest.LOADING); // LoadStart unreliable (in Flash for example)
  5180  						self.dispatchEvent('readystatechange');
  5181  					}
  5182  					self.dispatchEvent(e);
  5183  				});
  5184  				
  5185  				_xhr.bind('UploadProgress', function(e) {
  5186  					if (_upload_events_flag) {
  5187  						self.upload.dispatchEvent({
  5188  							type: 'progress',
  5189  							lengthComputable: false,
  5190  							total: e.total,
  5191  							loaded: e.loaded
  5192  						});
  5193  					}
  5194  				});
  5195  				
  5196  				_xhr.bind('Load', function(e) {
  5197  					_p('readyState', XMLHttpRequest.DONE);
  5198  					_p('status', Number(runtime.exec.call(_xhr, 'XMLHttpRequest', 'getStatus') || 0));
  5199  					_p('statusText', httpCode[_p('status')] || "");
  5200  					
  5201  					_p('response', runtime.exec.call(_xhr, 'XMLHttpRequest', 'getResponse', _p('responseType')));
  5202  
  5203  					if (!!~Basic.inArray(_p('responseType'), ['text', ''])) {
  5204  						_p('responseText', _p('response'));
  5205  					} else if (_p('responseType') === 'document') {
  5206  						_p('responseXML', _p('response'));
  5207  					}
  5208  
  5209  					_responseHeaders = runtime.exec.call(_xhr, 'XMLHttpRequest', 'getAllResponseHeaders');
  5210  
  5211  					self.dispatchEvent('readystatechange');
  5212  					
  5213  					if (_p('status') > 0) { // status 0 usually means that server is unreachable
  5214  						if (_upload_events_flag) {
  5215  							self.upload.dispatchEvent(e);
  5216  						}
  5217  						self.dispatchEvent(e);
  5218  					} else {
  5219  						_error_flag = true;
  5220  						self.dispatchEvent('error');
  5221  					}
  5222  					loadEnd();
  5223  				});
  5224  
  5225  				_xhr.bind('Abort', function(e) {
  5226  					self.dispatchEvent(e);
  5227  					loadEnd();
  5228  				});
  5229  				
  5230  				_xhr.bind('Error', function(e) {
  5231  					_error_flag = true;
  5232  					_p('readyState', XMLHttpRequest.DONE);
  5233  					self.dispatchEvent('readystatechange');
  5234  					_upload_complete_flag = true;
  5235  					self.dispatchEvent(e);
  5236  					loadEnd();
  5237  				});
  5238  
  5239  				runtime.exec.call(_xhr, 'XMLHttpRequest', 'send', {
  5240  					url: _url,
  5241  					method: _method,
  5242  					async: _async,
  5243  					user: _user,
  5244  					password: _password,
  5245  					headers: _headers,
  5246  					mimeType: _mimeType,
  5247  					encoding: _encoding,
  5248  					responseType: self.responseType,
  5249  					withCredentials: self.withCredentials,
  5250  					options: _options
  5251  				}, data);
  5252  			}
  5253  
  5254  			// clarify our requirements
  5255  			if (typeof(_options.required_caps) === 'string') {
  5256  				_options.required_caps = Runtime.parseCaps(_options.required_caps);
  5257  			}
  5258  
  5259  			_options.required_caps = Basic.extend({}, _options.required_caps, {
  5260  				return_response_type: self.responseType
  5261  			});
  5262  
  5263  			if (data instanceof FormData) {
  5264  				_options.required_caps.send_multipart = true;
  5265  			}
  5266  
  5267  			if (!_same_origin_flag) {
  5268  				_options.required_caps.do_cors = true;
  5269  			}
  5270  			
  5271  
  5272  			if (_options.ruid) { // we do not need to wait if we can connect directly
  5273  				exec(_xhr.connectRuntime(_options));
  5274  			} else {
  5275  				_xhr.bind('RuntimeInit', function(e, runtime) {
  5276  					exec(runtime);
  5277  				});
  5278  				_xhr.bind('RuntimeError', function(e, err) {
  5279  					self.dispatchEvent('RuntimeError', err);
  5280  				});
  5281  				_xhr.connectRuntime(_options);
  5282  			}
  5283  		}
  5284  	
  5285  		
  5286  		function _reset() {
  5287  			_p('responseText', "");
  5288  			_p('responseXML', null);
  5289  			_p('response', null);
  5290  			_p('status', 0);
  5291  			_p('statusText', "");
  5292  			_start_time = _timeoutset_time = null;
  5293  		}
  5294  	}
  5295  
  5296  	XMLHttpRequest.UNSENT = 0;
  5297  	XMLHttpRequest.OPENED = 1;
  5298  	XMLHttpRequest.HEADERS_RECEIVED = 2;
  5299  	XMLHttpRequest.LOADING = 3;
  5300  	XMLHttpRequest.DONE = 4;
  5301  	
  5302  	XMLHttpRequest.prototype = EventTarget.instance;
  5303  
  5304  	return XMLHttpRequest;
  5305  });
  5306  
  5307  // Included from: src/javascript/runtime/Transporter.js
  5308  
  5309  /**
  5310   * Transporter.js
  5311   *
  5312   * Copyright 2013, Moxiecode Systems AB
  5313   * Released under GPL License.
  5314   *
  5315   * License: http://www.plupload.com/license
  5316   * Contributing: http://www.plupload.com/contributing
  5317   */
  5318  
  5319  define("moxie/runtime/Transporter", [
  5320  	"moxie/core/utils/Basic",
  5321  	"moxie/core/utils/Encode",
  5322  	"moxie/runtime/RuntimeClient",
  5323  	"moxie/core/EventTarget"
  5324  ], function(Basic, Encode, RuntimeClient, EventTarget) {
  5325  	function Transporter() {
  5326  		var mod, _runtime, _data, _size, _pos, _chunk_size;
  5327  
  5328  		RuntimeClient.call(this);
  5329  
  5330  		Basic.extend(this, {
  5331  			uid: Basic.guid('uid_'),
  5332  
  5333  			state: Transporter.IDLE,
  5334  
  5335  			result: null,
  5336  
  5337  			transport: function(data, type, options) {
  5338  				var self = this;
  5339  
  5340  				options = Basic.extend({
  5341  					chunk_size: 204798
  5342  				}, options);
  5343  
  5344  				// should divide by three, base64 requires this
  5345  				if ((mod = options.chunk_size % 3)) {
  5346  					options.chunk_size += 3 - mod;
  5347  				}
  5348  
  5349  				_chunk_size = options.chunk_size;
  5350  
  5351  				_reset.call(this);
  5352  				_data = data;
  5353  				_size = data.length;
  5354  
  5355  				if (Basic.typeOf(options) === 'string' || options.ruid) {
  5356  					_run.call(self, type, this.connectRuntime(options));
  5357  				} else {
  5358  					// we require this to run only once
  5359  					var cb = function(e, runtime) {
  5360  						self.unbind("RuntimeInit", cb);
  5361  						_run.call(self, type, runtime);
  5362  					};
  5363  					this.bind("RuntimeInit", cb);
  5364  					this.connectRuntime(options);
  5365  				}
  5366  			},
  5367  
  5368  			abort: function() {
  5369  				var self = this;
  5370  
  5371  				self.state = Transporter.IDLE;
  5372  				if (_runtime) {
  5373  					_runtime.exec.call(self, 'Transporter', 'clear');
  5374  					self.trigger("TransportingAborted");
  5375  				}
  5376  
  5377  				_reset.call(self);
  5378  			},
  5379  
  5380  
  5381  			destroy: function() {
  5382  				this.unbindAll();
  5383  				_runtime = null;
  5384  				this.disconnectRuntime();
  5385  				_reset.call(this);
  5386  			}
  5387  		});
  5388  
  5389  		function _reset() {
  5390  			_size = _pos = 0;
  5391  			_data = this.result = null;
  5392  		}
  5393  
  5394  		function _run(type, runtime) {
  5395  			var self = this;
  5396  
  5397  			_runtime = runtime;
  5398  
  5399  			//self.unbind("RuntimeInit");
  5400  
  5401  			self.bind("TransportingProgress", function(e) {
  5402  				_pos = e.loaded;
  5403  
  5404  				if (_pos < _size && Basic.inArray(self.state, [Transporter.IDLE, Transporter.DONE]) === -1) {
  5405  					_transport.call(self);
  5406  				}
  5407  			}, 999);
  5408  
  5409  			self.bind("TransportingComplete", function() {
  5410  				_pos = _size;
  5411  				self.state = Transporter.DONE;
  5412  				_data = null; // clean a bit
  5413  				self.result = _runtime.exec.call(self, 'Transporter', 'getAsBlob', type || '');
  5414  			}, 999);
  5415  
  5416  			self.state = Transporter.BUSY;
  5417  			self.trigger("TransportingStarted");
  5418  			_transport.call(self);
  5419  		}
  5420  
  5421  		function _transport() {
  5422  			var self = this,
  5423  				chunk,
  5424  				bytesLeft = _size - _pos;
  5425  
  5426  			if (_chunk_size > bytesLeft) {
  5427  				_chunk_size = bytesLeft;
  5428  			}
  5429  
  5430  			chunk = Encode.btoa(_data.substr(_pos, _chunk_size));
  5431  			_runtime.exec.call(self, 'Transporter', 'receive', chunk, _size);
  5432  		}
  5433  	}
  5434  
  5435  	Transporter.IDLE = 0;
  5436  	Transporter.BUSY = 1;
  5437  	Transporter.DONE = 2;
  5438  
  5439  	Transporter.prototype = EventTarget.instance;
  5440  
  5441  	return Transporter;
  5442  });
  5443  
  5444  // Included from: src/javascript/image/Image.js
  5445  
  5446  /**
  5447   * Image.js
  5448   *
  5449   * Copyright 2013, Moxiecode Systems AB
  5450   * Released under GPL License.
  5451   *
  5452   * License: http://www.plupload.com/license
  5453   * Contributing: http://www.plupload.com/contributing
  5454   */
  5455  
  5456  define("moxie/image/Image", [
  5457  	"moxie/core/utils/Basic",
  5458  	"moxie/core/utils/Dom",
  5459  	"moxie/core/Exceptions",
  5460  	"moxie/file/FileReaderSync",
  5461  	"moxie/xhr/XMLHttpRequest",
  5462  	"moxie/runtime/Runtime",
  5463  	"moxie/runtime/RuntimeClient",
  5464  	"moxie/runtime/Transporter",
  5465  	"moxie/core/utils/Env",
  5466  	"moxie/core/EventTarget",
  5467  	"moxie/file/Blob",
  5468  	"moxie/file/File",
  5469  	"moxie/core/utils/Encode"
  5470  ], function(Basic, Dom, x, FileReaderSync, XMLHttpRequest, Runtime, RuntimeClient, Transporter, Env, EventTarget, Blob, File, Encode) {
  5471  	/**
  5472  	Image preloading and manipulation utility. Additionally it provides access to image meta info (Exif, GPS) and raw binary data.
  5473  
  5474  	@class Image
  5475  	@constructor
  5476  	@extends EventTarget
  5477  	*/
  5478  	var dispatches = [
  5479  		'progress',
  5480  
  5481  		/**
  5482  		Dispatched when loading is complete.
  5483  
  5484  		@event load
  5485  		@param {Object} event
  5486  		*/
  5487  		'load',
  5488  
  5489  		'error',
  5490  
  5491  		/**
  5492  		Dispatched when resize operation is complete.
  5493  		
  5494  		@event resize
  5495  		@param {Object} event
  5496  		*/
  5497  		'resize',
  5498  
  5499  		/**
  5500  		Dispatched when visual representation of the image is successfully embedded
  5501  		into the corresponsing container.
  5502  
  5503  		@event embedded
  5504  		@param {Object} event
  5505  		*/
  5506  		'embedded'
  5507  	];
  5508  
  5509  	function Image() {
  5510  		RuntimeClient.call(this);
  5511  
  5512  		Basic.extend(this, {
  5513  			/**
  5514  			Unique id of the component
  5515  
  5516  			@property uid
  5517  			@type {String}
  5518  			*/
  5519  			uid: Basic.guid('uid_'),
  5520  
  5521  			/**
  5522  			Unique id of the connected runtime, if any.
  5523  
  5524  			@property ruid
  5525  			@type {String}
  5526  			*/
  5527  			ruid: null,
  5528  
  5529  			/**
  5530  			Name of the file, that was used to create an image, if available. If not equals to empty string.
  5531  
  5532  			@property name
  5533  			@type {String}
  5534  			@default ""
  5535  			*/
  5536  			name: "",
  5537  
  5538  			/**
  5539  			Size of the image in bytes. Actual value is set only after image is preloaded.
  5540  
  5541  			@property size
  5542  			@type {Number}
  5543  			@default 0
  5544  			*/
  5545  			size: 0,
  5546  
  5547  			/**
  5548  			Width of the image. Actual value is set only after image is preloaded.
  5549  
  5550  			@property width
  5551  			@type {Number}
  5552  			@default 0
  5553  			*/
  5554  			width: 0,
  5555  
  5556  			/**
  5557  			Height of the image. Actual value is set only after image is preloaded.
  5558  
  5559  			@property height
  5560  			@type {Number}
  5561  			@default 0
  5562  			*/
  5563  			height: 0,
  5564  
  5565  			/**
  5566  			Mime type of the image. Currently only image/jpeg and image/png are supported. Actual value is set only after image is preloaded.
  5567  
  5568  			@property type
  5569  			@type {String}
  5570  			@default ""
  5571  			*/
  5572  			type: "",
  5573  
  5574  			/**
  5575  			Holds meta info (Exif, GPS). Is populated only for image/jpeg. Actual value is set only after image is preloaded.
  5576  
  5577  			@property meta
  5578  			@type {Object}
  5579  			@default {}
  5580  			*/
  5581  			meta: {},
  5582  
  5583  			/**
  5584  			Alias for load method, that takes another mOxie.Image object as a source (see load).
  5585  
  5586  			@method clone
  5587  			@param {Image} src Source for the image
  5588  			@param {Boolean} [exact=false] Whether to activate in-depth clone mode
  5589  			*/
  5590  			clone: function() {
  5591  				this.load.apply(this, arguments);
  5592  			},
  5593  
  5594  			/**
  5595  			Loads image from various sources. Currently the source for new image can be: mOxie.Image, mOxie.Blob/mOxie.File, 
  5596  			native Blob/File, dataUrl or URL. Depending on the type of the source, arguments - differ. When source is URL, 
  5597  			Image will be downloaded from remote destination and loaded in memory.
  5598  
  5599  			@example
  5600  				var img = new mOxie.Image();
  5601  				img.onload = function() {
  5602  					var blob = img.getAsBlob();
  5603  					
  5604  					var formData = new mOxie.FormData();
  5605  					formData.append('file', blob);
  5606  
  5607  					var xhr = new mOxie.XMLHttpRequest();
  5608  					xhr.onload = function() {
  5609  						// upload complete
  5610  					};
  5611  					xhr.open('post', 'upload.php');
  5612  					xhr.send(formData);
  5613  				};
  5614  				img.load("http://www.moxiecode.com/images/mox-logo.jpg"); // notice file extension (.jpg)
  5615  			
  5616  
  5617  			@method load
  5618  			@param {Image|Blob|File|String} src Source for the image
  5619  			@param {Boolean|Object} [mixed]
  5620  			*/
  5621  			load: function() {
  5622  				// this is here because to bind properly we need an uid first, which is created above
  5623  				this.bind('Load Resize', function() {
  5624  					_updateInfo.call(this);
  5625  				}, 999);
  5626  
  5627  				this.convertEventPropsToHandlers(dispatches);
  5628  
  5629  				_load.apply(this, arguments);
  5630  			},
  5631  
  5632  			/**
  5633  			Downsizes the image to fit the specified width/height. If crop is supplied, image will be cropped to exact dimensions.
  5634  
  5635  			@method downsize
  5636  			@param {Number} width Resulting width
  5637  			@param {Number} [height=width] Resulting height (optional, if not supplied will default to width)
  5638  			@param {Boolean} [crop=false] Whether to crop the image to exact dimensions
  5639  			@param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
  5640  			*/
  5641  			downsize: function(opts) {
  5642  				var defaults = {
  5643  					width: this.width,
  5644  					height: this.height,
  5645  					crop: false,
  5646  					preserveHeaders: true
  5647  				};
  5648  
  5649  				if (typeof(opts) === 'object') {
  5650  					opts = Basic.extend(defaults, opts);
  5651  				} else {
  5652  					opts = Basic.extend(defaults, {
  5653  						width: arguments[0],
  5654  						height: arguments[1],
  5655  						crop: arguments[2],
  5656  						preserveHeaders: arguments[3]
  5657  					});
  5658  				}
  5659  
  5660  				try {
  5661  					if (!this.size) { // only preloaded image objects can be used as source
  5662  						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5663  					}
  5664  
  5665  					// no way to reliably intercept the crash due to high resolution, so we simply avoid it
  5666  					if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
  5667  						throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
  5668  					}
  5669  
  5670  					this.getRuntime().exec.call(this, 'Image', 'downsize', opts.width, opts.height, opts.crop, opts.preserveHeaders);
  5671  				} catch(ex) {
  5672  					// for now simply trigger error event
  5673  					this.trigger('error', ex.code);
  5674  				}
  5675  			},
  5676  
  5677  			/**
  5678  			Alias for downsize(width, height, true). (see downsize)
  5679  			
  5680  			@method crop
  5681  			@param {Number} width Resulting width
  5682  			@param {Number} [height=width] Resulting height (optional, if not supplied will default to width)
  5683  			@param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
  5684  			*/
  5685  			crop: function(width, height, preserveHeaders) {
  5686  				this.downsize(width, height, true, preserveHeaders);
  5687  			},
  5688  
  5689  			getAsCanvas: function() {
  5690  				if (!Env.can('create_canvas')) {
  5691  					throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  5692  				}
  5693  
  5694  				var runtime = this.connectRuntime(this.ruid);
  5695  				return runtime.exec.call(this, 'Image', 'getAsCanvas');
  5696  			},
  5697  
  5698  			/**
  5699  			Retrieves image in it's current state as mOxie.Blob object. Cannot be run on empty or image in progress (throws
  5700  			DOMException.INVALID_STATE_ERR).
  5701  
  5702  			@method getAsBlob
  5703  			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  5704  			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
  5705  			@return {Blob} Image as Blob
  5706  			*/
  5707  			getAsBlob: function(type, quality) {
  5708  				if (!this.size) {
  5709  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5710  				}
  5711  
  5712  				if (!type) {
  5713  					type = 'image/jpeg';
  5714  				}
  5715  
  5716  				if (type === 'image/jpeg' && !quality) {
  5717  					quality = 90;
  5718  				}
  5719  
  5720  				return this.getRuntime().exec.call(this, 'Image', 'getAsBlob', type, quality);
  5721  			},
  5722  
  5723  			/**
  5724  			Retrieves image in it's current state as dataURL string. Cannot be run on empty or image in progress (throws
  5725  			DOMException.INVALID_STATE_ERR).
  5726  
  5727  			@method getAsDataURL
  5728  			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  5729  			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
  5730  			@return {String} Image as dataURL string
  5731  			*/
  5732  			getAsDataURL: function(type, quality) {
  5733  				if (!this.size) {
  5734  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5735  				}
  5736  				return this.getRuntime().exec.call(this, 'Image', 'getAsDataURL', type, quality);
  5737  			},
  5738  
  5739  			/**
  5740  			Retrieves image in it's current state as binary string. Cannot be run on empty or image in progress (throws
  5741  			DOMException.INVALID_STATE_ERR).
  5742  
  5743  			@method getAsBinaryString
  5744  			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  5745  			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
  5746  			@return {String} Image as binary string
  5747  			*/
  5748  			getAsBinaryString: function(type, quality) {
  5749  				var dataUrl = this.getAsDataURL(type, quality);
  5750  				return Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7));
  5751  			},
  5752  
  5753  			/**
  5754  			Embeds a visual representation of the image into the specified node. Depending on the runtime, 
  5755  			it might be a canvas, an img node or a thrid party shim object (Flash or SilverLight - very rare, 
  5756  			can be used in legacy browsers that do not have canvas or proper dataURI support).
  5757  
  5758  			@method embed
  5759  			@param {DOMElement} el DOM element to insert the image object into
  5760  			@param {Object} [options]
  5761  				@param {Number} [options.width] The width of an embed (defaults to the image width)
  5762  				@param {Number} [options.height] The height of an embed (defaults to the image height)
  5763  				@param {String} [type="image/jpeg"] Mime type
  5764  				@param {Number} [quality=90] Quality of an embed, if mime type is image/jpeg
  5765  				@param {Boolean} [crop=false] Whether to crop an embed to the specified dimensions
  5766  			*/
  5767  			embed: function(el) {
  5768  				var self = this
  5769  				, imgCopy
  5770  				, type, quality, crop
  5771  				, options = arguments[1] || {}
  5772  				, width = this.width
  5773  				, height = this.height
  5774  				, runtime // this has to be outside of all the closures to contain proper runtime
  5775  				;
  5776  
  5777  				function onResize() {
  5778  					// if possible, embed a canvas element directly
  5779  					if (Env.can('create_canvas')) {
  5780  						var canvas = imgCopy.getAsCanvas();
  5781  						if (canvas) {
  5782  							el.appendChild(canvas);
  5783  							canvas = null;
  5784  							imgCopy.destroy();
  5785  							self.trigger('embedded');
  5786  							return;
  5787  						}
  5788  					}
  5789  
  5790  					var dataUrl = imgCopy.getAsDataURL(type, quality);
  5791  					if (!dataUrl) {
  5792  						throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  5793  					}
  5794  
  5795  					if (Env.can('use_data_uri_of', dataUrl.length)) {
  5796  						el.innerHTML = '<img src="' + dataUrl + '" width="' + imgCopy.width + '" height="' + imgCopy.height + '" />';
  5797  						imgCopy.destroy();
  5798  						self.trigger('embedded');
  5799  					} else {
  5800  						var tr = new Transporter();
  5801  
  5802  						tr.bind("TransportingComplete", function() {
  5803  							runtime = self.connectRuntime(this.result.ruid);
  5804  
  5805  							self.bind("Embedded", function() {
  5806  								// position and size properly
  5807  								Basic.extend(runtime.getShimContainer().style, {
  5808  									//position: 'relative',
  5809  									top: '0px',
  5810  									left: '0px',
  5811  									width: imgCopy.width + 'px',
  5812  									height: imgCopy.height + 'px'
  5813  								});
  5814  
  5815  								// some shims (Flash/SilverLight) reinitialize, if parent element is hidden, reordered or it's
  5816  								// position type changes (in Gecko), but since we basically need this only in IEs 6/7 and
  5817  								// sometimes 8 and they do not have this problem, we can comment this for now
  5818  								/*tr.bind("RuntimeInit", function(e, runtime) {
  5819  									tr.destroy();
  5820  									runtime.destroy();
  5821  									onResize.call(self); // re-feed our image data
  5822  								});*/
  5823  
  5824  								runtime = null;
  5825  							}, 999);
  5826  
  5827  							runtime.exec.call(self, "ImageView", "display", this.result.uid, width, height);
  5828  							imgCopy.destroy();
  5829  						});
  5830  
  5831  						tr.transport(Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7)), type, Basic.extend({}, options, {
  5832  							required_caps: {
  5833  								display_media: true
  5834  							},
  5835  							runtime_order: 'flash,silverlight',
  5836  							container: el
  5837  						}));
  5838  					}
  5839  				}
  5840  
  5841  				try {
  5842  					if (!(el = Dom.get(el))) {
  5843  						throw new x.DOMException(x.DOMException.INVALID_NODE_TYPE_ERR);
  5844  					}
  5845  
  5846  					if (!this.size) { // only preloaded image objects can be used as source
  5847  						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5848  					}
  5849  
  5850  					if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
  5851  						throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
  5852  					}
  5853  
  5854  					type = options.type || this.type || 'image/jpeg';
  5855  					quality = options.quality || 90;
  5856  					crop = Basic.typeOf(options.crop) !== 'undefined' ? options.crop : false;
  5857  
  5858  					// figure out dimensions for the thumb
  5859  					if (options.width) {
  5860  						width = options.width;
  5861  						height = options.height || width;
  5862  					} else {
  5863  						// if container element has measurable dimensions, use them
  5864  						var dimensions = Dom.getSize(el);
  5865  						if (dimensions.w && dimensions.h) { // both should be > 0
  5866  							width = dimensions.w;
  5867  							height = dimensions.h;
  5868  						}
  5869  					}
  5870  
  5871  					imgCopy = new Image();
  5872  
  5873  					imgCopy.bind("Resize", function() {
  5874  						onResize.call(self);
  5875  					});
  5876  
  5877  					imgCopy.bind("Load", function() {
  5878  						imgCopy.downsize(width, height, crop, false);
  5879  					});
  5880  
  5881  					imgCopy.clone(this, false);
  5882  
  5883  					return imgCopy;
  5884  				} catch(ex) {
  5885  					// for now simply trigger error event
  5886  					this.trigger('error', ex.code);
  5887  				}
  5888  			},
  5889  
  5890  			/**
  5891  			Properly destroys the image and frees resources in use. If any. Recommended way to dispose mOxie.Image object.
  5892  
  5893  			@method destroy
  5894  			*/
  5895  			destroy: function() {
  5896  				if (this.ruid) {
  5897  					this.getRuntime().exec.call(this, 'Image', 'destroy');
  5898  					this.disconnectRuntime();
  5899  				}
  5900  				this.unbindAll();
  5901  			}
  5902  		});
  5903  
  5904  
  5905  		function _updateInfo(info) {
  5906  			if (!info) {
  5907  				info = this.getRuntime().exec.call(this, 'Image', 'getInfo');
  5908  			}
  5909  
  5910  			this.size = info.size;
  5911  			this.width = info.width;
  5912  			this.height = info.height;
  5913  			this.type = info.type;
  5914  			this.meta = info.meta;
  5915  
  5916  			// update file name, only if empty
  5917  			if (this.name === '') {
  5918  				this.name = info.name;
  5919  			}
  5920  		}
  5921  		
  5922  
  5923  		function _load(src) {
  5924  			var srcType = Basic.typeOf(src);
  5925  
  5926  			try {
  5927  				// if source is Image
  5928  				if (src instanceof Image) {
  5929  					if (!src.size) { // only preloaded image objects can be used as source
  5930  						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5931  					}
  5932  					_loadFromImage.apply(this, arguments);
  5933  				}
  5934  				// if source is o.Blob/o.File
  5935  				else if (src instanceof Blob) {
  5936  					if (!~Basic.inArray(src.type, ['image/jpeg', 'image/png'])) {
  5937  						throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  5938  					}
  5939  					_loadFromBlob.apply(this, arguments);
  5940  				}
  5941  				// if native blob/file
  5942  				else if (Basic.inArray(srcType, ['blob', 'file']) !== -1) {
  5943  					_load.call(this, new File(null, src), arguments[1]);
  5944  				}
  5945  				// if String
  5946  				else if (srcType === 'string') {
  5947  					// if dataUrl String
  5948  					if (/^data:[^;]*;base64,/.test(src)) {
  5949  						_load.call(this, new Blob(null, { data: src }), arguments[1]);
  5950  					}
  5951  					// else assume Url, either relative or absolute
  5952  					else {
  5953  						_loadFromUrl.apply(this, arguments);
  5954  					}
  5955  				}
  5956  				// if source seems to be an img node
  5957  				else if (srcType === 'node' && src.nodeName.toLowerCase() === 'img') {
  5958  					_load.call(this, src.src, arguments[1]);
  5959  				}
  5960  				else {
  5961  					throw new x.DOMException(x.DOMException.TYPE_MISMATCH_ERR);
  5962  				}
  5963  			} catch(ex) {
  5964  				// for now simply trigger error event
  5965  				this.trigger('error', ex.code);
  5966  			}
  5967  		}
  5968  
  5969  
  5970  		function _loadFromImage(img, exact) {
  5971  			var runtime = this.connectRuntime(img.ruid);
  5972  			this.ruid = runtime.uid;
  5973  			runtime.exec.call(this, 'Image', 'loadFromImage', img, (Basic.typeOf(exact) === 'undefined' ? true : exact));
  5974  		}
  5975  
  5976  
  5977  		function _loadFromBlob(blob, options) {
  5978  			var self = this;
  5979  
  5980  			self.name = blob.name || '';
  5981  
  5982  			function exec(runtime) {
  5983  				self.ruid = runtime.uid;
  5984  				runtime.exec.call(self, 'Image', 'loadFromBlob', blob);
  5985  			}
  5986  
  5987  			if (blob.isDetached()) {
  5988  				this.bind('RuntimeInit', function(e, runtime) {
  5989  					exec(runtime);
  5990  				});
  5991  
  5992  				// convert to object representation
  5993  				if (options && typeof(options.required_caps) === 'string') {
  5994  					options.required_caps = Runtime.parseCaps(options.required_caps);
  5995  				}
  5996  
  5997  				this.connectRuntime(Basic.extend({
  5998  					required_caps: {
  5999  						access_image_binary: true,
  6000  						resize_image: true
  6001  					}
  6002  				}, options));
  6003  			} else {
  6004  				exec(this.connectRuntime(blob.ruid));
  6005  			}
  6006  		}
  6007  
  6008  
  6009  		function _loadFromUrl(url, options) {
  6010  			var self = this, xhr;
  6011  
  6012  			xhr = new XMLHttpRequest();
  6013  
  6014  			xhr.open('get', url);
  6015  			xhr.responseType = 'blob';
  6016  
  6017  			xhr.onprogress = function(e) {
  6018  				self.trigger(e);
  6019  			};
  6020  
  6021  			xhr.onload = function() {
  6022  				_loadFromBlob.call(self, xhr.response, true);
  6023  			};
  6024  
  6025  			xhr.onerror = function(e) {
  6026  				self.trigger(e);
  6027  			};
  6028  
  6029  			xhr.onloadend = function() {
  6030  				xhr.destroy();
  6031  			};
  6032  
  6033  			xhr.bind('RuntimeError', function(e, err) {
  6034  				self.trigger('RuntimeError', err);
  6035  			});
  6036  
  6037  			xhr.send(null, options);
  6038  		}
  6039  	}
  6040  
  6041  	// virtual world will crash on you if image has a resolution higher than this:
  6042  	Image.MAX_RESIZE_WIDTH = 6500;
  6043  	Image.MAX_RESIZE_HEIGHT = 6500; 
  6044  
  6045  	Image.prototype = EventTarget.instance;
  6046  
  6047  	return Image;
  6048  });
  6049  
  6050  // Included from: src/javascript/runtime/html5/Runtime.js
  6051  
  6052  /**
  6053   * Runtime.js
  6054   *
  6055   * Copyright 2013, Moxiecode Systems AB
  6056   * Released under GPL License.
  6057   *
  6058   * License: http://www.plupload.com/license
  6059   * Contributing: http://www.plupload.com/contributing
  6060   */
  6061  
  6062  /*global File:true */
  6063  
  6064  /**
  6065  Defines constructor for HTML5 runtime.
  6066  
  6067  @class moxie/runtime/html5/Runtime
  6068  @private
  6069  */
  6070  define("moxie/runtime/html5/Runtime", [
  6071  	"moxie/core/utils/Basic",
  6072  	"moxie/core/Exceptions",
  6073  	"moxie/runtime/Runtime",
  6074  	"moxie/core/utils/Env"
  6075  ], function(Basic, x, Runtime, Env) {
  6076  	
  6077  	var type = "html5", extensions = {};
  6078  	
  6079  	function Html5Runtime(options) {
  6080  		var I = this
  6081  		, Test = Runtime.capTest
  6082  		, True = Runtime.capTrue
  6083  		;
  6084  
  6085  		var caps = Basic.extend({
  6086  				access_binary: Test(window.FileReader || window.File && window.File.getAsDataURL),
  6087  				access_image_binary: function() {
  6088  					return I.can('access_binary') && !!extensions.Image;
  6089  				},
  6090  				display_media: Test(Env.can('create_canvas') || Env.can('use_data_uri_over32kb')),
  6091  				do_cors: Test(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()),
  6092  				drag_and_drop: Test(function() {
  6093  					// this comes directly from Modernizr: http://www.modernizr.com/
  6094  					var div = document.createElement('div');
  6095  					// IE has support for drag and drop since version 5, but doesn't support dropping files from desktop
  6096  					return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && (Env.browser !== 'IE' || Env.version > 9);
  6097  				}()),
  6098  				filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
  6099  					return (Env.browser === 'Chrome' && Env.version >= 28) || (Env.browser === 'IE' && Env.version >= 10);
  6100  				}()),
  6101  				return_response_headers: True,
  6102  				return_response_type: function(responseType) {
  6103  					if (responseType === 'json' && !!window.JSON) { // we can fake this one even if it's not supported
  6104  						return true;
  6105  					} 
  6106  					return Env.can('return_response_type', responseType);
  6107  				},
  6108  				return_status_code: True,
  6109  				report_upload_progress: Test(window.XMLHttpRequest && new XMLHttpRequest().upload),
  6110  				resize_image: function() {
  6111  					return I.can('access_binary') && Env.can('create_canvas');
  6112  				},
  6113  				select_file: function() {
  6114  					return Env.can('use_fileinput') && window.File;
  6115  				},
  6116  				select_folder: function() {
  6117  					return I.can('select_file') && Env.browser === 'Chrome' && Env.version >= 21;
  6118  				},
  6119  				select_multiple: function() {
  6120  					// it is buggy on Safari Windows and iOS
  6121  					return I.can('select_file') && 
  6122  						!(Env.browser === 'Safari' && Env.os === 'Windows') && 
  6123  						!(Env.os === 'iOS' && Env.verComp(Env.osVersion, "7.0.4", '<'));
  6124  				},
  6125  				send_binary_string: Test(window.XMLHttpRequest && (new XMLHttpRequest().sendAsBinary || (window.Uint8Array && window.ArrayBuffer))),
  6126  				send_custom_headers: Test(window.XMLHttpRequest),
  6127  				send_multipart: function() {
  6128  					return !!(window.XMLHttpRequest && new XMLHttpRequest().upload && window.FormData) || I.can('send_binary_string');
  6129  				},
  6130  				slice_blob: Test(window.File && (File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice)),
  6131  				stream_upload: function(){
  6132  					return I.can('slice_blob') && I.can('send_multipart');
  6133  				},
  6134  				summon_file_dialog: Test(function() { // yeah... some dirty sniffing here...
  6135  					return (Env.browser === 'Firefox' && Env.version >= 4) ||
  6136  						(Env.browser === 'Opera' && Env.version >= 12) ||
  6137  						(Env.browser === 'IE' && Env.version >= 10) ||
  6138  						!!~Basic.inArray(Env.browser, ['Chrome', 'Safari']);
  6139  				}()),
  6140  				upload_filesize: True
  6141  			}, 
  6142  			arguments[2]
  6143  		);
  6144  
  6145  		Runtime.call(this, options, (arguments[1] || type), caps);
  6146  
  6147  
  6148  		Basic.extend(this, {
  6149  
  6150  			init : function() {
  6151  				this.trigger("Init");
  6152  			},
  6153  
  6154  			destroy: (function(destroy) { // extend default destroy method
  6155  				return function() {
  6156  					destroy.call(I);
  6157  					destroy = I = null;
  6158  				};
  6159  			}(this.destroy))
  6160  		});
  6161  
  6162  		Basic.extend(this.getShim(), extensions);
  6163  	}
  6164  
  6165  	Runtime.addConstructor(type, Html5Runtime);
  6166  
  6167  	return extensions;
  6168  });
  6169  
  6170  // Included from: src/javascript/runtime/html5/file/Blob.js
  6171  
  6172  /**
  6173   * Blob.js
  6174   *
  6175   * Copyright 2013, Moxiecode Systems AB
  6176   * Released under GPL License.
  6177   *
  6178   * License: http://www.plupload.com/license
  6179   * Contributing: http://www.plupload.com/contributing
  6180   */
  6181  
  6182  /**
  6183  @class moxie/runtime/html5/file/Blob
  6184  @private
  6185  */
  6186  define("moxie/runtime/html5/file/Blob", [
  6187  	"moxie/runtime/html5/Runtime",
  6188  	"moxie/file/Blob"
  6189  ], function(extensions, Blob) {
  6190  
  6191  	function HTML5Blob() {
  6192  		function w3cBlobSlice(blob, start, end) {
  6193  			var blobSlice;
  6194  
  6195  			if (window.File.prototype.slice) {
  6196  				try {
  6197  					blob.slice();	// depricated version will throw WRONG_ARGUMENTS_ERR exception
  6198  					return blob.slice(start, end);
  6199  				} catch (e) {
  6200  					// depricated slice method
  6201  					return blob.slice(start, end - start);
  6202  				}
  6203  			// slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672
  6204  			} else if ((blobSlice = window.File.prototype.webkitSlice || window.File.prototype.mozSlice)) {
  6205  				return blobSlice.call(blob, start, end);
  6206  			} else {
  6207  				return null; // or throw some exception
  6208  			}
  6209  		}
  6210  
  6211  		this.slice = function() {
  6212  			return new Blob(this.getRuntime().uid, w3cBlobSlice.apply(this, arguments));
  6213  		};
  6214  	}
  6215  
  6216  	return (extensions.Blob = HTML5Blob);
  6217  });
  6218  
  6219  // Included from: src/javascript/core/utils/Events.js
  6220  
  6221  /**
  6222   * Events.js
  6223   *
  6224   * Copyright 2013, Moxiecode Systems AB
  6225   * Released under GPL License.
  6226   *
  6227   * License: http://www.plupload.com/license
  6228   * Contributing: http://www.plupload.com/contributing
  6229   */
  6230  
  6231  define('moxie/core/utils/Events', [
  6232  	'moxie/core/utils/Basic'
  6233  ], function(Basic) {
  6234  	var eventhash = {}, uid = 'moxie_' + Basic.guid();
  6235  	
  6236  	// IE W3C like event funcs
  6237  	function preventDefault() {
  6238  		this.returnValue = false;
  6239  	}
  6240  
  6241  	function stopPropagation() {
  6242  		this.cancelBubble = true;
  6243  	}
  6244  
  6245  	/**
  6246  	Adds an event handler to the specified object and store reference to the handler
  6247  	in objects internal Plupload registry (@see removeEvent).
  6248  	
  6249  	@method addEvent
  6250  	@for Utils
  6251  	@static
  6252  	@param {Object} obj DOM element like object to add handler to.
  6253  	@param {String} name Name to add event listener to.
  6254  	@param {Function} callback Function to call when event occurs.
  6255  	@param {String} [key] that might be used to add specifity to the event record.
  6256  	*/
  6257  	var addEvent = function(obj, name, callback, key) {
  6258  		var func, events;
  6259  					
  6260  		name = name.toLowerCase();
  6261  
  6262  		// Add event listener
  6263  		if (obj.addEventListener) {
  6264  			func = callback;
  6265  			
  6266  			obj.addEventListener(name, func, false);
  6267  		} else if (obj.attachEvent) {
  6268  			func = function() {
  6269  				var evt = window.event;
  6270  
  6271  				if (!evt.target) {
  6272  					evt.target = evt.srcElement;
  6273  				}
  6274  
  6275  				evt.preventDefault = preventDefault;
  6276  				evt.stopPropagation = stopPropagation;
  6277  
  6278  				callback(evt);
  6279  			};
  6280  
  6281  			obj.attachEvent('on' + name, func);
  6282  		}
  6283  		
  6284  		// Log event handler to objects internal mOxie registry
  6285  		if (!obj[uid]) {
  6286  			obj[uid] = Basic.guid();
  6287  		}
  6288  		
  6289  		if (!eventhash.hasOwnProperty(obj[uid])) {
  6290  			eventhash[obj[uid]] = {};
  6291  		}
  6292  		
  6293  		events = eventhash[obj[uid]];
  6294  		
  6295  		if (!events.hasOwnProperty(name)) {
  6296  			events[name] = [];
  6297  		}
  6298  				
  6299  		events[name].push({
  6300  			func: func,
  6301  			orig: callback, // store original callback for IE
  6302  			key: key
  6303  		});
  6304  	};
  6305  	
  6306  	
  6307  	/**
  6308  	Remove event handler from the specified object. If third argument (callback)
  6309  	is not specified remove all events with the specified name.
  6310  	
  6311  	@method removeEvent
  6312  	@static
  6313  	@param {Object} obj DOM element to remove event listener(s) from.
  6314  	@param {String} name Name of event listener to remove.
  6315  	@param {Function|String} [callback] might be a callback or unique key to match.
  6316  	*/
  6317  	var removeEvent = function(obj, name, callback) {
  6318  		var type, undef;
  6319  		
  6320  		name = name.toLowerCase();
  6321  		
  6322  		if (obj[uid] && eventhash[obj[uid]] && eventhash[obj[uid]][name]) {
  6323  			type = eventhash[obj[uid]][name];
  6324  		} else {
  6325  			return;
  6326  		}
  6327  			
  6328  		for (var i = type.length - 1; i >= 0; i--) {
  6329  			// undefined or not, key should match
  6330  			if (type[i].orig === callback || type[i].key === callback) {
  6331  				if (obj.removeEventListener) {
  6332  					obj.removeEventListener(name, type[i].func, false);
  6333  				} else if (obj.detachEvent) {
  6334  					obj.detachEvent('on'+name, type[i].func);
  6335  				}
  6336  				
  6337  				type[i].orig = null;
  6338  				type[i].func = null;
  6339  				type.splice(i, 1);
  6340  				
  6341  				// If callback was passed we are done here, otherwise proceed
  6342  				if (callback !== undef) {
  6343  					break;
  6344  				}
  6345  			}
  6346  		}
  6347  		
  6348  		// If event array got empty, remove it
  6349  		if (!type.length) {
  6350  			delete eventhash[obj[uid]][name];
  6351  		}
  6352  		
  6353  		// If mOxie registry has become empty, remove it
  6354  		if (Basic.isEmptyObj(eventhash[obj[uid]])) {
  6355  			delete eventhash[obj[uid]];
  6356  			
  6357  			// IE doesn't let you remove DOM object property with - delete
  6358  			try {
  6359  				delete obj[uid];
  6360  			} catch(e) {
  6361  				obj[uid] = undef;
  6362  			}
  6363  		}
  6364  	};
  6365  	
  6366  	
  6367  	/**
  6368  	Remove all kind of events from the specified object
  6369  	
  6370  	@method removeAllEvents
  6371  	@static
  6372  	@param {Object} obj DOM element to remove event listeners from.
  6373  	@param {String} [key] unique key to match, when removing events.
  6374  	*/
  6375  	var removeAllEvents = function(obj, key) {		
  6376  		if (!obj || !obj[uid]) {
  6377  			return;
  6378  		}
  6379  		
  6380  		Basic.each(eventhash[obj[uid]], function(events, name) {
  6381  			removeEvent(obj, name, key);
  6382  		});
  6383  	};
  6384  
  6385  	return {
  6386  		addEvent: addEvent,
  6387  		removeEvent: removeEvent,
  6388  		removeAllEvents: removeAllEvents
  6389  	};
  6390  });
  6391  
  6392  // Included from: src/javascript/runtime/html5/file/FileInput.js
  6393  
  6394  /**
  6395   * FileInput.js
  6396   *
  6397   * Copyright 2013, Moxiecode Systems AB
  6398   * Released under GPL License.
  6399   *
  6400   * License: http://www.plupload.com/license
  6401   * Contributing: http://www.plupload.com/contributing
  6402   */
  6403  
  6404  /**
  6405  @class moxie/runtime/html5/file/FileInput
  6406  @private
  6407  */
  6408  define("moxie/runtime/html5/file/FileInput", [
  6409  	"moxie/runtime/html5/Runtime",
  6410  	"moxie/core/utils/Basic",
  6411  	"moxie/core/utils/Dom",
  6412  	"moxie/core/utils/Events",
  6413  	"moxie/core/utils/Mime",
  6414  	"moxie/core/utils/Env"
  6415  ], function(extensions, Basic, Dom, Events, Mime, Env) {
  6416  	
  6417  	function FileInput() {
  6418  		var _files = [], _options;
  6419  
  6420  		Basic.extend(this, {
  6421  			init: function(options) {
  6422  				var comp = this, I = comp.getRuntime(), input, shimContainer, mimes, browseButton, zIndex, top;
  6423  
  6424  				_options = options;
  6425  				_files = [];
  6426  
  6427  				// figure out accept string
  6428  				mimes = _options.accept.mimes || Mime.extList2mimes(_options.accept, I.can('filter_by_extension'));
  6429  
  6430  				shimContainer = I.getShimContainer();
  6431  
  6432  				shimContainer.innerHTML = '<input id="' + I.uid +'" type="file" style="font-size:999px;opacity:0;"' +
  6433  					(_options.multiple && I.can('select_multiple') ? 'multiple' : '') + 
  6434  					(_options.directory && I.can('select_folder') ? 'webkitdirectory directory' : '') + // Chrome 11+
  6435  					(mimes ? ' accept="' + mimes.join(',') + '"' : '') + ' />';
  6436  
  6437  				input = Dom.get(I.uid);
  6438  
  6439  				// prepare file input to be placed underneath the browse_button element
  6440  				Basic.extend(input.style, {
  6441  					position: 'absolute',
  6442  					top: 0,
  6443  					left: 0,
  6444  					width: '100%',
  6445  					height: '100%'
  6446  				});
  6447  
  6448  
  6449  				browseButton = Dom.get(_options.browse_button);
  6450  
  6451  				// Route click event to the input[type=file] element for browsers that support such behavior
  6452  				if (I.can('summon_file_dialog')) {
  6453  					if (Dom.getStyle(browseButton, 'position') === 'static') {
  6454  						browseButton.style.position = 'relative';
  6455  					}
  6456  
  6457  					zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
  6458  
  6459  					browseButton.style.zIndex = zIndex;
  6460  					shimContainer.style.zIndex = zIndex - 1;
  6461  
  6462  					Events.addEvent(browseButton, 'click', function(e) {
  6463  						var input = Dom.get(I.uid);
  6464  						if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
  6465  							input.click();
  6466  						}
  6467  						e.preventDefault();
  6468  					}, comp.uid);
  6469  				}
  6470  
  6471  				/* Since we have to place input[type=file] on top of the browse_button for some browsers,
  6472  				browse_button loses interactivity, so we restore it here */
  6473  				top = I.can('summon_file_dialog') ? browseButton : shimContainer;
  6474  
  6475  				Events.addEvent(top, 'mouseover', function() {
  6476  					comp.trigger('mouseenter');
  6477  				}, comp.uid);
  6478  
  6479  				Events.addEvent(top, 'mouseout', function() {
  6480  					comp.trigger('mouseleave');
  6481  				}, comp.uid);
  6482  
  6483  				Events.addEvent(top, 'mousedown', function() {
  6484  					comp.trigger('mousedown');
  6485  				}, comp.uid);
  6486  
  6487  				Events.addEvent(Dom.get(_options.container), 'mouseup', function() {
  6488  					comp.trigger('mouseup');
  6489  				}, comp.uid);
  6490  
  6491  
  6492  				input.onchange = function onChange() { // there should be only one handler for this
  6493  					_files = [];
  6494  
  6495  					if (_options.directory) {
  6496  						// folders are represented by dots, filter them out (Chrome 11+)
  6497  						Basic.each(this.files, function(file) {
  6498  							if (file.name !== ".") { // if it doesn't looks like a folder
  6499  								_files.push(file);
  6500  							}
  6501  						});
  6502  					} else {
  6503  						_files = [].slice.call(this.files);
  6504  					}
  6505  
  6506  					// clearing the value enables the user to select the same file again if they want to
  6507  					if (Env.browser !== 'IE' && Env.browser !== 'IEMobile') {
  6508  						this.value = '';
  6509  					} else {
  6510  						// in IE input[type="file"] is read-only so the only way to reset it is to re-insert it
  6511  						var clone = this.cloneNode(true);
  6512  						this.parentNode.replaceChild(clone, this);
  6513  						clone.onchange = onChange;
  6514  					}
  6515  					comp.trigger('change');
  6516  				};
  6517  
  6518  				// ready event is perfectly asynchronous
  6519  				comp.trigger({
  6520  					type: 'ready',
  6521  					async: true
  6522  				});
  6523  
  6524  				shimContainer = null;
  6525  			},
  6526  
  6527  			getFiles: function() {
  6528  				return _files;
  6529  			},
  6530  
  6531  			disable: function(state) {
  6532  				var I = this.getRuntime(), input;
  6533  
  6534  				if ((input = Dom.get(I.uid))) {
  6535  					input.disabled = !!state;
  6536  				}
  6537  			},
  6538  
  6539  			destroy: function() {
  6540  				var I = this.getRuntime()
  6541  				, shim = I.getShim()
  6542  				, shimContainer = I.getShimContainer()
  6543  				;
  6544  				
  6545  				Events.removeAllEvents(shimContainer, this.uid);
  6546  				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
  6547  				Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
  6548  				
  6549  				if (shimContainer) {
  6550  					shimContainer.innerHTML = '';
  6551  				}
  6552  
  6553  				shim.removeInstance(this.uid);
  6554  
  6555  				_files = _options = shimContainer = shim = null;
  6556  			}
  6557  		});
  6558  	}
  6559  
  6560  	return (extensions.FileInput = FileInput);
  6561  });
  6562  
  6563  // Included from: src/javascript/runtime/html5/file/FileDrop.js
  6564  
  6565  /**
  6566   * FileDrop.js
  6567   *
  6568   * Copyright 2013, Moxiecode Systems AB
  6569   * Released under GPL License.
  6570   *
  6571   * License: http://www.plupload.com/license
  6572   * Contributing: http://www.plupload.com/contributing
  6573   */
  6574  
  6575  /**
  6576  @class moxie/runtime/html5/file/FileDrop
  6577  @private
  6578  */
  6579  define("moxie/runtime/html5/file/FileDrop", [
  6580  	"moxie/runtime/html5/Runtime",
  6581  	"moxie/core/utils/Basic",
  6582  	"moxie/core/utils/Dom",
  6583  	"moxie/core/utils/Events",
  6584  	"moxie/core/utils/Mime"
  6585  ], function(extensions, Basic, Dom, Events, Mime) {
  6586  	
  6587  	function FileDrop() {
  6588  		var _files = [], _allowedExts = [], _options;
  6589  
  6590  		Basic.extend(this, {
  6591  			init: function(options) {
  6592  				var comp = this, dropZone;
  6593  
  6594  				_options = options;
  6595  				_allowedExts = _extractExts(_options.accept);
  6596  				dropZone = _options.container;
  6597  
  6598  				Events.addEvent(dropZone, 'dragover', function(e) {
  6599  					if (!_hasFiles(e)) {
  6600  						return;
  6601  					}
  6602  					e.preventDefault();
  6603  					e.dataTransfer.dropEffect = 'copy';
  6604  				}, comp.uid);
  6605  
  6606  				Events.addEvent(dropZone, 'drop', function(e) {
  6607  					if (!_hasFiles(e)) {
  6608  						return;
  6609  					}
  6610  					e.preventDefault();
  6611  
  6612  					_files = [];
  6613  
  6614  					// Chrome 21+ accepts folders via Drag'n'Drop
  6615  					if (e.dataTransfer.items && e.dataTransfer.items[0].webkitGetAsEntry) {
  6616  						_readItems(e.dataTransfer.items, function() {
  6617  							comp.trigger("drop");
  6618  						});
  6619  					} else {
  6620  						Basic.each(e.dataTransfer.files, function(file) {
  6621  							if (_isAcceptable(file)) {
  6622  								_files.push(file);
  6623  							}
  6624  						});
  6625  						comp.trigger("drop");
  6626  					}
  6627  				}, comp.uid);
  6628  
  6629  				Events.addEvent(dropZone, 'dragenter', function(e) {
  6630  					comp.trigger("dragenter");
  6631  				}, comp.uid);
  6632  
  6633  				Events.addEvent(dropZone, 'dragleave', function(e) {
  6634  					comp.trigger("dragleave");
  6635  				}, comp.uid);
  6636  			},
  6637  
  6638  			getFiles: function() {
  6639  				return _files;
  6640  			},
  6641  
  6642  			destroy: function() {
  6643  				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
  6644  				_files = _allowedExts = _options = null;
  6645  			}
  6646  		});
  6647  
  6648  
  6649  		function _hasFiles(e) {
  6650  			if (!e.dataTransfer || !e.dataTransfer.types) { // e.dataTransfer.files is not available in Gecko during dragover
  6651  				return false;
  6652  			}
  6653  
  6654  			var types = Basic.toArray(e.dataTransfer.types || []);
  6655  
  6656  			return Basic.inArray("Files", types) !== -1 ||
  6657  				Basic.inArray("public.file-url", types) !== -1 || // Safari < 5
  6658  				Basic.inArray("application/x-moz-file", types) !== -1 // Gecko < 1.9.2 (< Firefox 3.6)
  6659  				;
  6660  		}
  6661  
  6662  		
  6663  		function _extractExts(accept) {
  6664  			var exts = [];
  6665  			for (var i = 0; i < accept.length; i++) {
  6666  				[].push.apply(exts, accept[i].extensions.split(/\s*,\s*/));
  6667  			}
  6668  			return Basic.inArray('*', exts) === -1 ? exts : [];
  6669  		}
  6670  
  6671  
  6672  		function _isAcceptable(file) {
  6673  			if (!_allowedExts.length) {
  6674  				return true;
  6675  			}
  6676  			var ext = Mime.getFileExtension(file.name);
  6677  			return !ext || Basic.inArray(ext, _allowedExts) !== -1;
  6678  		}
  6679  
  6680  
  6681  		function _readItems(items, cb) {
  6682  			var entries = [];
  6683  			Basic.each(items, function(item) {
  6684  				var entry = item.webkitGetAsEntry();
  6685  				// Address #998 (https://code.google.com/p/chromium/issues/detail?id=332579)
  6686  				if (entry) {
  6687  					// file() fails on OSX when the filename contains a special character (e.g. umlaut): see #61
  6688  					if (entry.isFile) {
  6689  						var file = item.getAsFile();
  6690  						if (_isAcceptable(file)) {
  6691  							_files.push(file);
  6692  						}
  6693  					} else {
  6694  						entries.push(entry);
  6695  					}
  6696  				}
  6697  			});
  6698  
  6699  			if (entries.length) {
  6700  				_readEntries(entries, cb);
  6701  			} else {
  6702  				cb();
  6703  			}
  6704  		}
  6705  
  6706  
  6707  		function _readEntries(entries, cb) {
  6708  			var queue = [];
  6709  			Basic.each(entries, function(entry) {
  6710  				queue.push(function(cbcb) {
  6711  					_readEntry(entry, cbcb);
  6712  				});
  6713  			});
  6714  			Basic.inSeries(queue, function() {
  6715  				cb();
  6716  			});
  6717  		}
  6718  
  6719  
  6720  		function _readEntry(entry, cb) {
  6721  			if (entry.isFile) {
  6722  				entry.file(function(file) {
  6723  					if (_isAcceptable(file)) {
  6724  						_files.push(file);
  6725  					}
  6726  					cb();
  6727  				}, function() {
  6728  					// fire an error event maybe
  6729  					cb();
  6730  				});
  6731  			} else if (entry.isDirectory) {
  6732  				_readDirEntry(entry, cb);
  6733  			} else {
  6734  				cb(); // not file, not directory? what then?..
  6735  			}
  6736  		}
  6737  
  6738  
  6739  		function _readDirEntry(dirEntry, cb) {
  6740  			var entries = [], dirReader = dirEntry.createReader();
  6741  
  6742  			// keep quering recursively till no more entries
  6743  			function getEntries(cbcb) {
  6744  				dirReader.readEntries(function(moreEntries) {
  6745  					if (moreEntries.length) {
  6746  						[].push.apply(entries, moreEntries);
  6747  						getEntries(cbcb);
  6748  					} else {
  6749  						cbcb();
  6750  					}
  6751  				}, cbcb);
  6752  			}
  6753  
  6754  			// ...and you thought FileReader was crazy...
  6755  			getEntries(function() {
  6756  				_readEntries(entries, cb);
  6757  			}); 
  6758  		}
  6759  	}
  6760  
  6761  	return (extensions.FileDrop = FileDrop);
  6762  });
  6763  
  6764  // Included from: src/javascript/runtime/html5/file/FileReader.js
  6765  
  6766  /**
  6767   * FileReader.js
  6768   *
  6769   * Copyright 2013, Moxiecode Systems AB
  6770   * Released under GPL License.
  6771   *
  6772   * License: http://www.plupload.com/license
  6773   * Contributing: http://www.plupload.com/contributing
  6774   */
  6775  
  6776  /**
  6777  @class moxie/runtime/html5/file/FileReader
  6778  @private
  6779  */
  6780  define("moxie/runtime/html5/file/FileReader", [
  6781  	"moxie/runtime/html5/Runtime",
  6782  	"moxie/core/utils/Encode",
  6783  	"moxie/core/utils/Basic"
  6784  ], function(extensions, Encode, Basic) {
  6785  	
  6786  	function FileReader() {
  6787  		var _fr, _convertToBinary = false;
  6788  
  6789  		Basic.extend(this, {
  6790  
  6791  			read: function(op, blob) {
  6792  				var target = this;
  6793  
  6794  				_fr = new window.FileReader();
  6795  
  6796  				_fr.addEventListener('progress', function(e) {
  6797  					target.trigger(e);
  6798  				});
  6799  
  6800  				_fr.addEventListener('load', function(e) {
  6801  					target.trigger(e);
  6802  				});
  6803  
  6804  				_fr.addEventListener('error', function(e) {
  6805  					target.trigger(e, _fr.error);
  6806  				});
  6807  
  6808  				_fr.addEventListener('loadend', function() {
  6809  					_fr = null;
  6810  				});
  6811  
  6812  				if (Basic.typeOf(_fr[op]) === 'function') {
  6813  					_convertToBinary = false;
  6814  					_fr[op](blob.getSource());
  6815  				} else if (op === 'readAsBinaryString') { // readAsBinaryString is depricated in general and never existed in IE10+
  6816  					_convertToBinary = true;
  6817  					_fr.readAsDataURL(blob.getSource());
  6818  				}
  6819  			},
  6820  
  6821  			getResult: function() {
  6822  				return _fr && _fr.result ? (_convertToBinary ? _toBinary(_fr.result) : _fr.result) : null;
  6823  			},
  6824  
  6825  			abort: function() {
  6826  				if (_fr) {
  6827  					_fr.abort();
  6828  				}
  6829  			},
  6830  
  6831  			destroy: function() {
  6832  				_fr = null;
  6833  			}
  6834  		});
  6835  
  6836  		function _toBinary(str) {
  6837  			return Encode.atob(str.substring(str.indexOf('base64,') + 7));
  6838  		}
  6839  	}
  6840  
  6841  	return (extensions.FileReader = FileReader);
  6842  });
  6843  
  6844  // Included from: src/javascript/runtime/html5/xhr/XMLHttpRequest.js
  6845  
  6846  /**
  6847   * XMLHttpRequest.js
  6848   *
  6849   * Copyright 2013, Moxiecode Systems AB
  6850   * Released under GPL License.
  6851   *
  6852   * License: http://www.plupload.com/license
  6853   * Contributing: http://www.plupload.com/contributing
  6854   */
  6855  
  6856  /*global ActiveXObject:true */
  6857  
  6858  /**
  6859  @class moxie/runtime/html5/xhr/XMLHttpRequest
  6860  @private
  6861  */
  6862  define("moxie/runtime/html5/xhr/XMLHttpRequest", [
  6863  	"moxie/runtime/html5/Runtime",
  6864  	"moxie/core/utils/Basic",
  6865  	"moxie/core/utils/Mime",
  6866  	"moxie/core/utils/Url",
  6867  	"moxie/file/File",
  6868  	"moxie/file/Blob",
  6869  	"moxie/xhr/FormData",
  6870  	"moxie/core/Exceptions",
  6871  	"moxie/core/utils/Env"
  6872  ], function(extensions, Basic, Mime, Url, File, Blob, FormData, x, Env) {
  6873  	
  6874  	function XMLHttpRequest() {
  6875  		var self = this
  6876  		, _xhr
  6877  		, _filename
  6878  		;
  6879  
  6880  		Basic.extend(this, {
  6881  			send: function(meta, data) {
  6882  				var target = this
  6883  				, isGecko2_5_6 = (Env.browser === 'Mozilla' && Env.version >= 4 && Env.version < 7)
  6884  				, isAndroidBrowser = Env.browser === 'Android Browser'
  6885  				, mustSendAsBinary = false
  6886  				;
  6887  
  6888  				// extract file name
  6889  				_filename = meta.url.replace(/^.+?\/([\w\-\.]+)$/, '$1').toLowerCase();
  6890  
  6891  				_xhr = _getNativeXHR();
  6892  				_xhr.open(meta.method, meta.url, meta.async, meta.user, meta.password);
  6893  
  6894  
  6895  				// prepare data to be sent
  6896  				if (data instanceof Blob) {
  6897  					if (data.isDetached()) {
  6898  						mustSendAsBinary = true;
  6899  					}
  6900  					data = data.getSource();
  6901  				} else if (data instanceof FormData) {
  6902  
  6903  					if (data.hasBlob()) {
  6904  						if (data.getBlob().isDetached()) {
  6905  							data = _prepareMultipart.call(target, data); // _xhr must be instantiated and be in OPENED state
  6906  							mustSendAsBinary = true;
  6907  						} else if ((isGecko2_5_6 || isAndroidBrowser) && Basic.typeOf(data.getBlob().getSource()) === 'blob' && window.FileReader) {
  6908  							// Gecko 2/5/6 can't send blob in FormData: https://bugzilla.mozilla.org/show_bug.cgi?id=649150
  6909  							// Android browsers (default one and Dolphin) seem to have the same issue, see: #613
  6910  							_preloadAndSend.call(target, meta, data);
  6911  							return; // _preloadAndSend will reinvoke send() with transmutated FormData =%D
  6912  						}	
  6913  					}
  6914  
  6915  					// transfer fields to real FormData
  6916  					if (data instanceof FormData) { // if still a FormData, e.g. not mangled by _prepareMultipart()
  6917  						var fd = new window.FormData();
  6918  						data.each(function(value, name) {
  6919  							if (value instanceof Blob) {
  6920  								fd.append(name, value.getSource());
  6921  							} else {
  6922  								fd.append(name, value);
  6923  							}
  6924  						});
  6925  						data = fd;
  6926  					}
  6927  				}
  6928  
  6929  
  6930  				// if XHR L2
  6931  				if (_xhr.upload) {
  6932  					if (meta.withCredentials) {
  6933  						_xhr.withCredentials = true;
  6934  					}
  6935  
  6936  					_xhr.addEventListener('load', function(e) {
  6937  						target.trigger(e);
  6938  					});
  6939  
  6940  					_xhr.addEventListener('error', function(e) {
  6941  						target.trigger(e);
  6942  					});
  6943  
  6944  					// additionally listen to progress events
  6945  					_xhr.addEventListener('progress', function(e) {
  6946  						target.trigger(e);
  6947  					});
  6948  
  6949  					_xhr.upload.addEventListener('progress', function(e) {
  6950  						target.trigger({
  6951  							type: 'UploadProgress',
  6952  							loaded: e.loaded,
  6953  							total: e.total
  6954  						});
  6955  					});
  6956  				// ... otherwise simulate XHR L2
  6957  				} else {
  6958  					_xhr.onreadystatechange = function onReadyStateChange() {
  6959  						
  6960  						// fake Level 2 events
  6961  						switch (_xhr.readyState) {
  6962  							
  6963  							case 1: // XMLHttpRequest.OPENED
  6964  								// readystatechanged is fired twice for OPENED state (in IE and Mozilla) - neu
  6965  								break;
  6966  							
  6967  							// looks like HEADERS_RECEIVED (state 2) is not reported in Opera (or it's old versions) - neu
  6968  							case 2: // XMLHttpRequest.HEADERS_RECEIVED
  6969  								break;
  6970  								
  6971  							case 3: // XMLHttpRequest.LOADING 
  6972  								// try to fire progress event for not XHR L2
  6973  								var total, loaded;
  6974  								
  6975  								try {
  6976  									if (Url.hasSameOrigin(meta.url)) { // Content-Length not accessible for cross-domain on some browsers
  6977  										total = _xhr.getResponseHeader('Content-Length') || 0; // old Safari throws an exception here
  6978  									}
  6979  
  6980  									if (_xhr.responseText) { // responseText was introduced in IE7
  6981  										loaded = _xhr.responseText.length;
  6982  									}
  6983  								} catch(ex) {
  6984  									total = loaded = 0;
  6985  								}
  6986  
  6987  								target.trigger({
  6988  									type: 'progress',
  6989  									lengthComputable: !!total,
  6990  									total: parseInt(total, 10),
  6991  									loaded: loaded
  6992  								});
  6993  								break;
  6994  								
  6995  							case 4: // XMLHttpRequest.DONE
  6996  								// release readystatechange handler (mostly for IE)
  6997  								_xhr.onreadystatechange = function() {};
  6998  
  6999  								// usually status 0 is returned when server is unreachable, but FF also fails to status 0 for 408 timeout
  7000  								if (_xhr.status === 0) {
  7001  									target.trigger('error');
  7002  								} else {
  7003  									target.trigger('load');
  7004  								}							
  7005  								break;
  7006  						}
  7007  					};
  7008  				}
  7009  				
  7010  
  7011  				// set request headers
  7012  				if (!Basic.isEmptyObj(meta.headers)) {
  7013  					Basic.each(meta.headers, function(value, header) {
  7014  						_xhr.setRequestHeader(header, value);
  7015  					});
  7016  				}
  7017  
  7018  				// request response type
  7019  				if ("" !== meta.responseType && 'responseType' in _xhr) {
  7020  					if ('json' === meta.responseType && !Env.can('return_response_type', 'json')) { // we can fake this one
  7021  						_xhr.responseType = 'text';
  7022  					} else {
  7023  						_xhr.responseType = meta.responseType;
  7024  					}
  7025  				}
  7026  
  7027  				// send ...
  7028  				if (!mustSendAsBinary) {
  7029  					_xhr.send(data);
  7030  				} else {
  7031  					if (_xhr.sendAsBinary) { // Gecko
  7032  						_xhr.sendAsBinary(data);
  7033  					} else { // other browsers having support for typed arrays
  7034  						(function() {
  7035  							// mimic Gecko's sendAsBinary
  7036  							var ui8a = new Uint8Array(data.length);
  7037  							for (var i = 0; i < data.length; i++) {
  7038  								ui8a[i] = (data.charCodeAt(i) & 0xff);
  7039  							}
  7040  							_xhr.send(ui8a.buffer);
  7041  						}());
  7042  					}
  7043  				}
  7044  
  7045  				target.trigger('loadstart');
  7046  			},
  7047  
  7048  			getStatus: function() {
  7049  				// according to W3C spec it should return 0 for readyState < 3, but instead it throws an exception
  7050  				try {
  7051  					if (_xhr) {
  7052  						return _xhr.status;
  7053  					}
  7054  				} catch(ex) {}
  7055  				return 0;
  7056  			},
  7057  
  7058  			getResponse: function(responseType) {
  7059  				var I = this.getRuntime();
  7060  
  7061  				try {
  7062  					switch (responseType) {
  7063  						case 'blob':
  7064  							var file = new File(I.uid, _xhr.response);
  7065  							
  7066  							// try to extract file name from content-disposition if possible (might be - not, if CORS for example)	
  7067  							var disposition = _xhr.getResponseHeader('Content-Disposition');
  7068  							if (disposition) {
  7069  								// extract filename from response header if available
  7070  								var match = disposition.match(/filename=([\'\"'])([^\1]+)\1/);
  7071  								if (match) {
  7072  									_filename = match[2];
  7073  								}
  7074  							}
  7075  							file.name = _filename;
  7076  
  7077  							// pre-webkit Opera doesn't set type property on the blob response
  7078  							if (!file.type) {
  7079  								file.type = Mime.getFileMime(_filename);
  7080  							}
  7081  							return file;
  7082  
  7083  						case 'json':
  7084  							if (!Env.can('return_response_type', 'json')) {
  7085  								return _xhr.status === 200 && !!window.JSON ? JSON.parse(_xhr.responseText) : null;
  7086  							}
  7087  							return _xhr.response;
  7088  
  7089  						case 'document':
  7090  							return _getDocument(_xhr);
  7091  
  7092  						default:
  7093  							return _xhr.responseText !== '' ? _xhr.responseText : null; // against the specs, but for consistency across the runtimes
  7094  					}
  7095  				} catch(ex) {
  7096  					return null;
  7097  				}				
  7098  			},
  7099  
  7100  			getAllResponseHeaders: function() {
  7101  				try {
  7102  					return _xhr.getAllResponseHeaders();
  7103  				} catch(ex) {}
  7104  				return '';
  7105  			},
  7106  
  7107  			abort: function() {
  7108  				if (_xhr) {
  7109  					_xhr.abort();
  7110  				}
  7111  			},
  7112  
  7113  			destroy: function() {
  7114  				self = _filename = null;
  7115  			}
  7116  		});
  7117  
  7118  
  7119  		// here we go... ugly fix for ugly bug
  7120  		function _preloadAndSend(meta, data) {
  7121  			var target = this, blob, fr;
  7122  				
  7123  			// get original blob
  7124  			blob = data.getBlob().getSource();
  7125  			
  7126  			// preload blob in memory to be sent as binary string
  7127  			fr = new window.FileReader();
  7128  			fr.onload = function() {
  7129  				// overwrite original blob
  7130  				data.append(data.getBlobName(), new Blob(null, {
  7131  					type: blob.type,
  7132  					data: fr.result
  7133  				}));
  7134  				// invoke send operation again
  7135  				self.send.call(target, meta, data);
  7136  			};
  7137  			fr.readAsBinaryString(blob);
  7138  		}
  7139  
  7140  		
  7141  		function _getNativeXHR() {
  7142  			if (window.XMLHttpRequest && !(Env.browser === 'IE' && Env.version < 8)) { // IE7 has native XHR but it's buggy
  7143  				return new window.XMLHttpRequest();
  7144  			} else {
  7145  				return (function() {
  7146  					var progIDs = ['Msxml2.XMLHTTP.6.0', 'Microsoft.XMLHTTP']; // if 6.0 available, use it, otherwise failback to default 3.0
  7147  					for (var i = 0; i < progIDs.length; i++) {
  7148  						try {
  7149  							return new ActiveXObject(progIDs[i]);
  7150  						} catch (ex) {}
  7151  					}
  7152  				})();
  7153  			}
  7154  		}
  7155  		
  7156  		// @credits Sergey Ilinsky	(http://www.ilinsky.com/)
  7157  		function _getDocument(xhr) {
  7158  			var rXML = xhr.responseXML;
  7159  			var rText = xhr.responseText;
  7160  			
  7161  			// Try parsing responseText (@see: http://www.ilinsky.com/articles/XMLHttpRequest/#bugs-ie-responseXML-content-type)
  7162  			if (Env.browser === 'IE' && rText && rXML && !rXML.documentElement && /[^\/]+\/[^\+]+\+xml/.test(xhr.getResponseHeader("Content-Type"))) {
  7163  				rXML = new window.ActiveXObject("Microsoft.XMLDOM");
  7164  				rXML.async = false;
  7165  				rXML.validateOnParse = false;
  7166  				rXML.loadXML(rText);
  7167  			}
  7168  	
  7169  			// Check if there is no error in document
  7170  			if (rXML) {
  7171  				if ((Env.browser === 'IE' && rXML.parseError !== 0) || !rXML.documentElement || rXML.documentElement.tagName === "parsererror") {
  7172  					return null;
  7173  				}
  7174  			}
  7175  			return rXML;
  7176  		}
  7177  
  7178  
  7179  		function _prepareMultipart(fd) {
  7180  			var boundary = '----moxieboundary' + new Date().getTime()
  7181  			, dashdash = '--'
  7182  			, crlf = '\r\n'
  7183  			, multipart = ''
  7184  			, I = this.getRuntime()
  7185  			;
  7186  
  7187  			if (!I.can('send_binary_string')) {
  7188  				throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  7189  			}
  7190  
  7191  			_xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
  7192  
  7193  			// append multipart parameters
  7194  			fd.each(function(value, name) {
  7195  				// Firefox 3.6 failed to convert multibyte characters to UTF-8 in sendAsBinary(), 
  7196  				// so we try it here ourselves with: unescape(encodeURIComponent(value))
  7197  				if (value instanceof Blob) {
  7198  					// Build RFC2388 blob
  7199  					multipart += dashdash + boundary + crlf +
  7200  						'Content-Disposition: form-data; name="' + name + '"; filename="' + unescape(encodeURIComponent(value.name || 'blob')) + '"' + crlf +
  7201  						'Content-Type: ' + (value.type || 'application/octet-stream') + crlf + crlf +
  7202  						value.getSource() + crlf;
  7203  				} else {
  7204  					multipart += dashdash + boundary + crlf +
  7205  						'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf +
  7206  						unescape(encodeURIComponent(value)) + crlf;
  7207  				}
  7208  			});
  7209  
  7210  			multipart += dashdash + boundary + dashdash + crlf;
  7211  
  7212  			return multipart;
  7213  		}
  7214  	}
  7215  
  7216  	return (extensions.XMLHttpRequest = XMLHttpRequest);
  7217  });
  7218  
  7219  // Included from: src/javascript/runtime/html5/utils/BinaryReader.js
  7220  
  7221  /**
  7222   * BinaryReader.js
  7223   *
  7224   * Copyright 2013, Moxiecode Systems AB
  7225   * Released under GPL License.
  7226   *
  7227   * License: http://www.plupload.com/license
  7228   * Contributing: http://www.plupload.com/contributing
  7229   */
  7230  
  7231  /**
  7232  @class moxie/runtime/html5/utils/BinaryReader
  7233  @private
  7234  */
  7235  define("moxie/runtime/html5/utils/BinaryReader", [], function() {
  7236  	return function() {
  7237  		var II = false, bin;
  7238  
  7239  		// Private functions
  7240  		function read(idx, size) {
  7241  			var mv = II ? 0 : -8 * (size - 1), sum = 0, i;
  7242  
  7243  			for (i = 0; i < size; i++) {
  7244  				sum |= (bin.charCodeAt(idx + i) << Math.abs(mv + i*8));
  7245  			}
  7246  
  7247  			return sum;
  7248  		}
  7249  
  7250  		function putstr(segment, idx, length) {
  7251  			length = arguments.length === 3 ? length : bin.length - idx - 1;
  7252  			bin = bin.substr(0, idx) + segment + bin.substr(length + idx);
  7253  		}
  7254  
  7255  		function write(idx, num, size) {
  7256  			var str = '', mv = II ? 0 : -8 * (size - 1), i;
  7257  
  7258  			for (i = 0; i < size; i++) {
  7259  				str += String.fromCharCode((num >> Math.abs(mv + i*8)) & 255);
  7260  			}
  7261  
  7262  			putstr(str, idx, size);
  7263  		}
  7264  
  7265  		// Public functions
  7266  		return {
  7267  			II: function(order) {
  7268  				if (order === undefined) {
  7269  					return II;
  7270  				} else {
  7271  					II = order;
  7272  				}
  7273  			},
  7274  
  7275  			init: function(binData) {
  7276  				II = false;
  7277  				bin = binData;
  7278  			},
  7279  
  7280  			SEGMENT: function(idx, length, segment) {
  7281  				switch (arguments.length) {
  7282  					case 1:
  7283  						return bin.substr(idx, bin.length - idx - 1);
  7284  					case 2:
  7285  						return bin.substr(idx, length);
  7286  					case 3:
  7287  						putstr(segment, idx, length);
  7288  						break;
  7289  					default: return bin;
  7290  				}
  7291  			},
  7292  
  7293  			BYTE: function(idx) {
  7294  				return read(idx, 1);
  7295  			},
  7296  
  7297  			SHORT: function(idx) {
  7298  				return read(idx, 2);
  7299  			},
  7300  
  7301  			LONG: function(idx, num) {
  7302  				if (num === undefined) {
  7303  					return read(idx, 4);
  7304  				} else {
  7305  					write(idx, num, 4);
  7306  				}
  7307  			},
  7308  
  7309  			SLONG: function(idx) { // 2's complement notation
  7310  				var num = read(idx, 4);
  7311  
  7312  				return (num > 2147483647 ? num - 4294967296 : num);
  7313  			},
  7314  
  7315  			STRING: function(idx, size) {
  7316  				var str = '';
  7317  
  7318  				for (size += idx; idx < size; idx++) {
  7319  					str += String.fromCharCode(read(idx, 1));
  7320  				}
  7321  
  7322  				return str;
  7323  			}
  7324  		};
  7325  	};
  7326  });
  7327  
  7328  // Included from: src/javascript/runtime/html5/image/JPEGHeaders.js
  7329  
  7330  /**
  7331   * JPEGHeaders.js
  7332   *
  7333   * Copyright 2013, Moxiecode Systems AB
  7334   * Released under GPL License.
  7335   *
  7336   * License: http://www.plupload.com/license
  7337   * Contributing: http://www.plupload.com/contributing
  7338   */
  7339   
  7340  /**
  7341  @class moxie/runtime/html5/image/JPEGHeaders
  7342  @private
  7343  */
  7344  define("moxie/runtime/html5/image/JPEGHeaders", [
  7345  	"moxie/runtime/html5/utils/BinaryReader"
  7346  ], function(BinaryReader) {
  7347  	
  7348  	return function JPEGHeaders(data) {
  7349  		var headers = [], read, idx, marker, length = 0;
  7350  
  7351  		read = new BinaryReader();
  7352  		read.init(data);
  7353  
  7354  		// Check if data is jpeg
  7355  		if (read.SHORT(0) !== 0xFFD8) {
  7356  			return;
  7357  		}
  7358  
  7359  		idx = 2;
  7360  
  7361  		while (idx <= data.length) {
  7362  			marker = read.SHORT(idx);
  7363  
  7364  			// omit RST (restart) markers
  7365  			if (marker >= 0xFFD0 && marker <= 0xFFD7) {
  7366  				idx += 2;
  7367  				continue;
  7368  			}
  7369  
  7370  			// no headers allowed after SOS marker
  7371  			if (marker === 0xFFDA || marker === 0xFFD9) {
  7372  				break;
  7373  			}
  7374  
  7375  			length = read.SHORT(idx + 2) + 2;
  7376  
  7377  			// APPn marker detected
  7378  			if (marker >= 0xFFE1 && marker <= 0xFFEF) {
  7379  				headers.push({
  7380  					hex: marker,
  7381  					name: 'APP' + (marker & 0x000F),
  7382  					start: idx,
  7383  					length: length,
  7384  					segment: read.SEGMENT(idx, length)
  7385  				});
  7386  			}
  7387  
  7388  			idx += length;
  7389  		}
  7390  
  7391  		read.init(null); // free memory
  7392  
  7393  		return {
  7394  			headers: headers,
  7395  
  7396  			restore: function(data) {
  7397  				var max, i;
  7398  
  7399  				read.init(data);
  7400  
  7401  				idx = read.SHORT(2) == 0xFFE0 ? 4 + read.SHORT(4) : 2;
  7402  
  7403  				for (i = 0, max = headers.length; i < max; i++) {
  7404  					read.SEGMENT(idx, 0, headers[i].segment);
  7405  					idx += headers[i].length;
  7406  				}
  7407  
  7408  				data = read.SEGMENT();
  7409  				read.init(null);
  7410  				return data;
  7411  			},
  7412  
  7413  			strip: function(data) {
  7414  				var headers, jpegHeaders, i;
  7415  
  7416  				jpegHeaders = new JPEGHeaders(data);
  7417  				headers = jpegHeaders.headers;
  7418  				jpegHeaders.purge();
  7419  
  7420  				read.init(data);
  7421  
  7422  				i = headers.length;
  7423  				while (i--) {
  7424  					read.SEGMENT(headers[i].start, headers[i].length, '');
  7425  				}
  7426  				
  7427  				data = read.SEGMENT();
  7428  				read.init(null);
  7429  				return data;
  7430  			},
  7431  
  7432  			get: function(name) {
  7433  				var array = [];
  7434  
  7435  				for (var i = 0, max = headers.length; i < max; i++) {
  7436  					if (headers[i].name === name.toUpperCase()) {
  7437  						array.push(headers[i].segment);
  7438  					}
  7439  				}
  7440  				return array;
  7441  			},
  7442  
  7443  			set: function(name, segment) {
  7444  				var array = [], i, ii, max;
  7445  
  7446  				if (typeof(segment) === 'string') {
  7447  					array.push(segment);
  7448  				} else {
  7449  					array = segment;
  7450  				}
  7451  
  7452  				for (i = ii = 0, max = headers.length; i < max; i++) {
  7453  					if (headers[i].name === name.toUpperCase()) {
  7454  						headers[i].segment = array[ii];
  7455  						headers[i].length = array[ii].length;
  7456  						ii++;
  7457  					}
  7458  					if (ii >= array.length) {
  7459  						break;
  7460  					}
  7461  				}
  7462  			},
  7463  
  7464  			purge: function() {
  7465  				headers = [];
  7466  				read.init(null);
  7467  				read = null;
  7468  			}
  7469  		};
  7470  	};
  7471  });
  7472  
  7473  // Included from: src/javascript/runtime/html5/image/ExifParser.js
  7474  
  7475  /**
  7476   * ExifParser.js
  7477   *
  7478   * Copyright 2013, Moxiecode Systems AB
  7479   * Released under GPL License.
  7480   *
  7481   * License: http://www.plupload.com/license
  7482   * Contributing: http://www.plupload.com/contributing
  7483   */
  7484  
  7485  /**
  7486  @class moxie/runtime/html5/image/ExifParser
  7487  @private
  7488  */
  7489  define("moxie/runtime/html5/image/ExifParser", [
  7490  	"moxie/core/utils/Basic",
  7491  	"moxie/runtime/html5/utils/BinaryReader"
  7492  ], function(Basic, BinaryReader) {
  7493  	
  7494  	return function ExifParser() {
  7495  		// Private ExifParser fields
  7496  		var data, tags, Tiff, offsets = {}, tagDescs;
  7497  
  7498  		data = new BinaryReader();
  7499  
  7500  		tags = {
  7501  			tiff : {
  7502  				/*
  7503  				The image orientation viewed in terms of rows and columns.
  7504  
  7505  				1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
  7506  				2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
  7507  				3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
  7508  				4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
  7509  				5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
  7510  				6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
  7511  				7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
  7512  				8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
  7513  				*/
  7514  				0x0112: 'Orientation',
  7515  				0x010E: 'ImageDescription',
  7516  				0x010F: 'Make',
  7517  				0x0110: 'Model',
  7518  				0x0131: 'Software',
  7519  				0x8769: 'ExifIFDPointer',
  7520  				0x8825:	'GPSInfoIFDPointer'
  7521  			},
  7522  			exif : {
  7523  				0x9000: 'ExifVersion',
  7524  				0xA001: 'ColorSpace',
  7525  				0xA002: 'PixelXDimension',
  7526  				0xA003: 'PixelYDimension',
  7527  				0x9003: 'DateTimeOriginal',
  7528  				0x829A: 'ExposureTime',
  7529  				0x829D: 'FNumber',
  7530  				0x8827: 'ISOSpeedRatings',
  7531  				0x9201: 'ShutterSpeedValue',
  7532  				0x9202: 'ApertureValue'	,
  7533  				0x9207: 'MeteringMode',
  7534  				0x9208: 'LightSource',
  7535  				0x9209: 'Flash',
  7536  				0x920A: 'FocalLength',
  7537  				0xA402: 'ExposureMode',
  7538  				0xA403: 'WhiteBalance',
  7539  				0xA406: 'SceneCaptureType',
  7540  				0xA404: 'DigitalZoomRatio',
  7541  				0xA408: 'Contrast',
  7542  				0xA409: 'Saturation',
  7543  				0xA40A: 'Sharpness'
  7544  			},
  7545  			gps : {
  7546  				0x0000: 'GPSVersionID',
  7547  				0x0001: 'GPSLatitudeRef',
  7548  				0x0002: 'GPSLatitude',
  7549  				0x0003: 'GPSLongitudeRef',
  7550  				0x0004: 'GPSLongitude'
  7551  			}
  7552  		};
  7553  
  7554  		tagDescs = {
  7555  			'ColorSpace': {
  7556  				1: 'sRGB',
  7557  				0: 'Uncalibrated'
  7558  			},
  7559  
  7560  			'MeteringMode': {
  7561  				0: 'Unknown',
  7562  				1: 'Average',
  7563  				2: 'CenterWeightedAverage',
  7564  				3: 'Spot',
  7565  				4: 'MultiSpot',
  7566  				5: 'Pattern',
  7567  				6: 'Partial',
  7568  				255: 'Other'
  7569  			},
  7570  
  7571  			'LightSource': {
  7572  				1: 'Daylight',
  7573  				2: 'Fliorescent',
  7574  				3: 'Tungsten',
  7575  				4: 'Flash',
  7576  				9: 'Fine weather',
  7577  				10: 'Cloudy weather',
  7578  				11: 'Shade',
  7579  				12: 'Daylight fluorescent (D 5700 - 7100K)',
  7580  				13: 'Day white fluorescent (N 4600 -5400K)',
  7581  				14: 'Cool white fluorescent (W 3900 - 4500K)',
  7582  				15: 'White fluorescent (WW 3200 - 3700K)',
  7583  				17: 'Standard light A',
  7584  				18: 'Standard light B',
  7585  				19: 'Standard light C',
  7586  				20: 'D55',
  7587  				21: 'D65',
  7588  				22: 'D75',
  7589  				23: 'D50',
  7590  				24: 'ISO studio tungsten',
  7591  				255: 'Other'
  7592  			},
  7593  
  7594  			'Flash': {
  7595  				0x0000: 'Flash did not fire.',
  7596  				0x0001: 'Flash fired.',
  7597  				0x0005: 'Strobe return light not detected.',
  7598  				0x0007: 'Strobe return light detected.',
  7599  				0x0009: 'Flash fired, compulsory flash mode',
  7600  				0x000D: 'Flash fired, compulsory flash mode, return light not detected',
  7601  				0x000F: 'Flash fired, compulsory flash mode, return light detected',
  7602  				0x0010: 'Flash did not fire, compulsory flash mode',
  7603  				0x0018: 'Flash did not fire, auto mode',
  7604  				0x0019: 'Flash fired, auto mode',
  7605  				0x001D: 'Flash fired, auto mode, return light not detected',
  7606  				0x001F: 'Flash fired, auto mode, return light detected',
  7607  				0x0020: 'No flash function',
  7608  				0x0041: 'Flash fired, red-eye reduction mode',
  7609  				0x0045: 'Flash fired, red-eye reduction mode, return light not detected',
  7610  				0x0047: 'Flash fired, red-eye reduction mode, return light detected',
  7611  				0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode',
  7612  				0x004D: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
  7613  				0x004F: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
  7614  				0x0059: 'Flash fired, auto mode, red-eye reduction mode',
  7615  				0x005D: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
  7616  				0x005F: 'Flash fired, auto mode, return light detected, red-eye reduction mode'
  7617  			},
  7618  
  7619  			'ExposureMode': {
  7620  				0: 'Auto exposure',
  7621  				1: 'Manual exposure',
  7622  				2: 'Auto bracket'
  7623  			},
  7624  
  7625  			'WhiteBalance': {
  7626  				0: 'Auto white balance',
  7627  				1: 'Manual white balance'
  7628  			},
  7629  
  7630  			'SceneCaptureType': {
  7631  				0: 'Standard',
  7632  				1: 'Landscape',
  7633  				2: 'Portrait',
  7634  				3: 'Night scene'
  7635  			},
  7636  
  7637  			'Contrast': {
  7638  				0: 'Normal',
  7639  				1: 'Soft',
  7640  				2: 'Hard'
  7641  			},
  7642  
  7643  			'Saturation': {
  7644  				0: 'Normal',
  7645  				1: 'Low saturation',
  7646  				2: 'High saturation'
  7647  			},
  7648  
  7649  			'Sharpness': {
  7650  				0: 'Normal',
  7651  				1: 'Soft',
  7652  				2: 'Hard'
  7653  			},
  7654  
  7655  			// GPS related
  7656  			'GPSLatitudeRef': {
  7657  				N: 'North latitude',
  7658  				S: 'South latitude'
  7659  			},
  7660  
  7661  			'GPSLongitudeRef': {
  7662  				E: 'East longitude',
  7663  				W: 'West longitude'
  7664  			}
  7665  		};
  7666  
  7667  		function extractTags(IFD_offset, tags2extract) {
  7668  			var length = data.SHORT(IFD_offset), i, ii,
  7669  				tag, type, count, tagOffset, offset, value, values = [], hash = {};
  7670  
  7671  			for (i = 0; i < length; i++) {
  7672  				// Set binary reader pointer to beginning of the next tag
  7673  				offset = tagOffset = IFD_offset + 12 * i + 2;
  7674  
  7675  				tag = tags2extract[data.SHORT(offset)];
  7676  
  7677  				if (tag === undefined) {
  7678  					continue; // Not the tag we requested
  7679  				}
  7680  
  7681  				type = data.SHORT(offset+=2);
  7682  				count = data.LONG(offset+=2);
  7683  
  7684  				offset += 4;
  7685  				values = [];
  7686  
  7687  				switch (type) {
  7688  					case 1: // BYTE
  7689  					case 7: // UNDEFINED
  7690  						if (count > 4) {
  7691  							offset = data.LONG(offset) + offsets.tiffHeader;
  7692  						}
  7693  
  7694  						for (ii = 0; ii < count; ii++) {
  7695  							values[ii] = data.BYTE(offset + ii);
  7696  						}
  7697  
  7698  						break;
  7699  
  7700  					case 2: // STRING
  7701  						if (count > 4) {
  7702  							offset = data.LONG(offset) + offsets.tiffHeader;
  7703  						}
  7704  
  7705  						hash[tag] = data.STRING(offset, count - 1);
  7706  
  7707  						continue;
  7708  
  7709  					case 3: // SHORT
  7710  						if (count > 2) {
  7711  							offset = data.LONG(offset) + offsets.tiffHeader;
  7712  						}
  7713  
  7714  						for (ii = 0; ii < count; ii++) {
  7715  							values[ii] = data.SHORT(offset + ii*2);
  7716  						}
  7717  
  7718  						break;
  7719  
  7720  					case 4: // LONG
  7721  						if (count > 1) {
  7722  							offset = data.LONG(offset) + offsets.tiffHeader;
  7723  						}
  7724  
  7725  						for (ii = 0; ii < count; ii++) {
  7726  							values[ii] = data.LONG(offset + ii*4);
  7727  						}
  7728  
  7729  						break;
  7730  
  7731  					case 5: // RATIONAL
  7732  						offset = data.LONG(offset) + offsets.tiffHeader;
  7733  
  7734  						for (ii = 0; ii < count; ii++) {
  7735  							values[ii] = data.LONG(offset + ii*4) / data.LONG(offset + ii*4 + 4);
  7736  						}
  7737  
  7738  						break;
  7739  
  7740  					case 9: // SLONG
  7741  						offset = data.LONG(offset) + offsets.tiffHeader;
  7742  
  7743  						for (ii = 0; ii < count; ii++) {
  7744  							values[ii] = data.SLONG(offset + ii*4);
  7745  						}
  7746  
  7747  						break;
  7748  
  7749  					case 10: // SRATIONAL
  7750  						offset = data.LONG(offset) + offsets.tiffHeader;
  7751  
  7752  						for (ii = 0; ii < count; ii++) {
  7753  							values[ii] = data.SLONG(offset + ii*4) / data.SLONG(offset + ii*4 + 4);
  7754  						}
  7755  
  7756  						break;
  7757  
  7758  					default:
  7759  						continue;
  7760  				}
  7761  
  7762  				value = (count == 1 ? values[0] : values);
  7763  
  7764  				if (tagDescs.hasOwnProperty(tag) && typeof value != 'object') {
  7765  					hash[tag] = tagDescs[tag][value];
  7766  				} else {
  7767  					hash[tag] = value;
  7768  				}
  7769  			}
  7770  
  7771  			return hash;
  7772  		}
  7773  
  7774  		function getIFDOffsets() {
  7775  			var idx = offsets.tiffHeader;
  7776  
  7777  			// Set read order of multi-byte data
  7778  			data.II(data.SHORT(idx) == 0x4949);
  7779  
  7780  			// Check if always present bytes are indeed present
  7781  			if (data.SHORT(idx+=2) !== 0x002A) {
  7782  				return false;
  7783  			}
  7784  
  7785  			offsets.IFD0 = offsets.tiffHeader + data.LONG(idx += 2);
  7786  			Tiff = extractTags(offsets.IFD0, tags.tiff);
  7787  
  7788  			if ('ExifIFDPointer' in Tiff) {
  7789  				offsets.exifIFD = offsets.tiffHeader + Tiff.ExifIFDPointer;
  7790  				delete Tiff.ExifIFDPointer;
  7791  			}
  7792  
  7793  			if ('GPSInfoIFDPointer' in Tiff) {
  7794  				offsets.gpsIFD = offsets.tiffHeader + Tiff.GPSInfoIFDPointer;
  7795  				delete Tiff.GPSInfoIFDPointer;
  7796  			}
  7797  			return true;
  7798  		}
  7799  
  7800  		// At the moment only setting of simple (LONG) values, that do not require offset recalculation, is supported
  7801  		function setTag(ifd, tag, value) {
  7802  			var offset, length, tagOffset, valueOffset = 0;
  7803  
  7804  			// If tag name passed translate into hex key
  7805  			if (typeof(tag) === 'string') {
  7806  				var tmpTags = tags[ifd.toLowerCase()];
  7807  				for (var hex in tmpTags) {
  7808  					if (tmpTags[hex] === tag) {
  7809  						tag = hex;
  7810  						break;
  7811  					}
  7812  				}
  7813  			}
  7814  			offset = offsets[ifd.toLowerCase() + 'IFD'];
  7815  			length = data.SHORT(offset);
  7816  
  7817  			for (var i = 0; i < length; i++) {
  7818  				tagOffset = offset + 12 * i + 2;
  7819  
  7820  				if (data.SHORT(tagOffset) == tag) {
  7821  					valueOffset = tagOffset + 8;
  7822  					break;
  7823  				}
  7824  			}
  7825  
  7826  			if (!valueOffset) {
  7827  				return false;
  7828  			}
  7829  
  7830  			data.LONG(valueOffset, value);
  7831  			return true;
  7832  		}
  7833  
  7834  
  7835  		// Public functions
  7836  		return {
  7837  			init: function(segment) {
  7838  				// Reset internal data
  7839  				offsets = {
  7840  					tiffHeader: 10
  7841  				};
  7842  
  7843  				if (segment === undefined || !segment.length) {
  7844  					return false;
  7845  				}
  7846  
  7847  				data.init(segment);
  7848  
  7849  				// Check if that's APP1 and that it has EXIF
  7850  				if (data.SHORT(0) === 0xFFE1 && data.STRING(4, 5).toUpperCase() === "EXIF\0") {
  7851  					return getIFDOffsets();
  7852  				}
  7853  				return false;
  7854  			},
  7855  
  7856  			TIFF: function() {
  7857  				return Tiff;
  7858  			},
  7859  
  7860  			EXIF: function() {
  7861  				var Exif;
  7862  
  7863  				// Populate EXIF hash
  7864  				Exif = extractTags(offsets.exifIFD, tags.exif);
  7865  
  7866  				// Fix formatting of some tags
  7867  				if (Exif.ExifVersion && Basic.typeOf(Exif.ExifVersion) === 'array') {
  7868  					for (var i = 0, exifVersion = ''; i < Exif.ExifVersion.length; i++) {
  7869  						exifVersion += String.fromCharCode(Exif.ExifVersion[i]);
  7870  					}
  7871  					Exif.ExifVersion = exifVersion;
  7872  				}
  7873  
  7874  				return Exif;
  7875  			},
  7876  
  7877  			GPS: function() {
  7878  				var GPS;
  7879  
  7880  				GPS = extractTags(offsets.gpsIFD, tags.gps);
  7881  
  7882  				// iOS devices (and probably some others) do not put in GPSVersionID tag (why?..)
  7883  				if (GPS.GPSVersionID && Basic.typeOf(GPS.GPSVersionID) === 'array') {
  7884  					GPS.GPSVersionID = GPS.GPSVersionID.join('.');
  7885  				}
  7886  
  7887  				return GPS;
  7888  			},
  7889  
  7890  			setExif: function(tag, value) {
  7891  				// Right now only setting of width/height is possible
  7892  				if (tag !== 'PixelXDimension' && tag !== 'PixelYDimension') {return false;}
  7893  
  7894  				return setTag('exif', tag, value);
  7895  			},
  7896  
  7897  
  7898  			getBinary: function() {
  7899  				return data.SEGMENT();
  7900  			},
  7901  
  7902  			purge: function() {
  7903  				data.init(null);
  7904  				data = Tiff = null;
  7905  				offsets = {};
  7906  			}
  7907  		};
  7908  	};
  7909  });
  7910  
  7911  // Included from: src/javascript/runtime/html5/image/JPEG.js
  7912  
  7913  /**
  7914   * JPEG.js
  7915   *
  7916   * Copyright 2013, Moxiecode Systems AB
  7917   * Released under GPL License.
  7918   *
  7919   * License: http://www.plupload.com/license
  7920   * Contributing: http://www.plupload.com/contributing
  7921   */
  7922  
  7923  /**
  7924  @class moxie/runtime/html5/image/JPEG
  7925  @private
  7926  */
  7927  define("moxie/runtime/html5/image/JPEG", [
  7928  	"moxie/core/utils/Basic",
  7929  	"moxie/core/Exceptions",
  7930  	"moxie/runtime/html5/image/JPEGHeaders",
  7931  	"moxie/runtime/html5/utils/BinaryReader",
  7932  	"moxie/runtime/html5/image/ExifParser"
  7933  ], function(Basic, x, JPEGHeaders, BinaryReader, ExifParser) {
  7934  	
  7935  	function JPEG(binstr) {
  7936  		var _binstr, _br, _hm, _ep, _info, hasExif;
  7937  
  7938  		function _getDimensions() {
  7939  			var idx = 0, marker, length;
  7940  
  7941  			// examine all through the end, since some images might have very large APP segments
  7942  			while (idx <= _binstr.length) {
  7943  				marker = _br.SHORT(idx += 2);
  7944  
  7945  				if (marker >= 0xFFC0 && marker <= 0xFFC3) { // SOFn
  7946  					idx += 5; // marker (2 bytes) + length (2 bytes) + Sample precision (1 byte)
  7947  					return {
  7948  						height: _br.SHORT(idx),
  7949  						width: _br.SHORT(idx += 2)
  7950  					};
  7951  				}
  7952  				length = _br.SHORT(idx += 2);
  7953  				idx += length - 2;
  7954  			}
  7955  			return null;
  7956  		}
  7957  
  7958  		_binstr = binstr;
  7959  
  7960  		_br = new BinaryReader();
  7961  		_br.init(_binstr);
  7962  
  7963  		// check if it is jpeg
  7964  		if (_br.SHORT(0) !== 0xFFD8) {
  7965  			throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  7966  		}
  7967  
  7968  		// backup headers
  7969  		_hm = new JPEGHeaders(binstr);
  7970  
  7971  		// extract exif info
  7972  		_ep = new ExifParser();
  7973  		hasExif = !!_ep.init(_hm.get('app1')[0]);
  7974  
  7975  		// get dimensions
  7976  		_info = _getDimensions.call(this);
  7977  
  7978  		Basic.extend(this, {
  7979  			type: 'image/jpeg',
  7980  
  7981  			size: _binstr.length,
  7982  
  7983  			width: _info && _info.width || 0,
  7984  
  7985  			height: _info && _info.height || 0,
  7986  
  7987  			setExif: function(tag, value) {
  7988  				if (!hasExif) {
  7989  					return false; // or throw an exception
  7990  				}
  7991  
  7992  				if (Basic.typeOf(tag) === 'object') {
  7993  					Basic.each(tag, function(value, tag) {
  7994  						_ep.setExif(tag, value);
  7995  					});
  7996  				} else {
  7997  					_ep.setExif(tag, value);
  7998  				}
  7999  
  8000  				// update internal headers
  8001  				_hm.set('app1', _ep.getBinary());
  8002  			},
  8003  
  8004  			writeHeaders: function() {
  8005  				if (!arguments.length) {
  8006  					// if no arguments passed, update headers internally
  8007  					return (_binstr = _hm.restore(_binstr));
  8008  				}
  8009  				return _hm.restore(arguments[0]);
  8010  			},
  8011  
  8012  			stripHeaders: function(binstr) {
  8013  				return _hm.strip(binstr);
  8014  			},
  8015  
  8016  			purge: function() {
  8017  				_purge.call(this);
  8018  			}
  8019  		});
  8020  
  8021  		if (hasExif) {
  8022  			this.meta = {
  8023  				tiff: _ep.TIFF(),
  8024  				exif: _ep.EXIF(),
  8025  				gps: _ep.GPS()
  8026  			};
  8027  		}
  8028  
  8029  		function _purge() {
  8030  			if (!_ep || !_hm || !_br) { 
  8031  				return; // ignore any repeating purge requests
  8032  			}
  8033  			_ep.purge();
  8034  			_hm.purge();
  8035  			_br.init(null);
  8036  			_binstr = _info = _hm = _ep = _br = null;
  8037  		}
  8038  	}
  8039  
  8040  	return JPEG;
  8041  });
  8042  
  8043  // Included from: src/javascript/runtime/html5/image/PNG.js
  8044  
  8045  /**
  8046   * PNG.js
  8047   *
  8048   * Copyright 2013, Moxiecode Systems AB
  8049   * Released under GPL License.
  8050   *
  8051   * License: http://www.plupload.com/license
  8052   * Contributing: http://www.plupload.com/contributing
  8053   */
  8054  
  8055  /**
  8056  @class moxie/runtime/html5/image/PNG
  8057  @private
  8058  */
  8059  define("moxie/runtime/html5/image/PNG", [
  8060  	"moxie/core/Exceptions",
  8061  	"moxie/core/utils/Basic",
  8062  	"moxie/runtime/html5/utils/BinaryReader"
  8063  ], function(x, Basic, BinaryReader) {
  8064  	
  8065  	function PNG(binstr) {
  8066  		var _binstr, _br, _hm, _ep, _info;
  8067  
  8068  		_binstr = binstr;
  8069  
  8070  		_br = new BinaryReader();
  8071  		_br.init(_binstr);
  8072  
  8073  		// check if it's png
  8074  		(function() {
  8075  			var idx = 0, i = 0
  8076  			, signature = [0x8950, 0x4E47, 0x0D0A, 0x1A0A]
  8077  			;
  8078  
  8079  			for (i = 0; i < signature.length; i++, idx += 2) {
  8080  				if (signature[i] != _br.SHORT(idx)) {
  8081  					throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  8082  				}
  8083  			}
  8084  		}());
  8085  
  8086  		function _getDimensions() {
  8087  			var chunk, idx;
  8088  
  8089  			chunk = _getChunkAt.call(this, 8);
  8090  
  8091  			if (chunk.type == 'IHDR') {
  8092  				idx = chunk.start;
  8093  				return {
  8094  					width: _br.LONG(idx),
  8095  					height: _br.LONG(idx += 4)
  8096  				};
  8097  			}
  8098  			return null;
  8099  		}
  8100  
  8101  		function _purge() {
  8102  			if (!_br) {
  8103  				return; // ignore any repeating purge requests
  8104  			}
  8105  			_br.init(null);
  8106  			_binstr = _info = _hm = _ep = _br = null;
  8107  		}
  8108  
  8109  		_info = _getDimensions.call(this);
  8110  
  8111  		Basic.extend(this, {
  8112  			type: 'image/png',
  8113  
  8114  			size: _binstr.length,
  8115  
  8116  			width: _info.width,
  8117  
  8118  			height: _info.height,
  8119  
  8120  			purge: function() {
  8121  				_purge.call(this);
  8122  			}
  8123  		});
  8124  
  8125  		// for PNG we can safely trigger purge automatically, as we do not keep any data for later
  8126  		_purge.call(this);
  8127  
  8128  		function _getChunkAt(idx) {
  8129  			var length, type, start, CRC;
  8130  
  8131  			length = _br.LONG(idx);
  8132  			type = _br.STRING(idx += 4, 4);
  8133  			start = idx += 4;
  8134  			CRC = _br.LONG(idx + length);
  8135  
  8136  			return {
  8137  				length: length,
  8138  				type: type,
  8139  				start: start,
  8140  				CRC: CRC
  8141  			};
  8142  		}
  8143  	}
  8144  
  8145  	return PNG;
  8146  });
  8147  
  8148  // Included from: src/javascript/runtime/html5/image/ImageInfo.js
  8149  
  8150  /**
  8151   * ImageInfo.js
  8152   *
  8153   * Copyright 2013, Moxiecode Systems AB
  8154   * Released under GPL License.
  8155   *
  8156   * License: http://www.plupload.com/license
  8157   * Contributing: http://www.plupload.com/contributing
  8158   */
  8159  
  8160  /**
  8161  @class moxie/runtime/html5/image/ImageInfo
  8162  @private
  8163  */
  8164  define("moxie/runtime/html5/image/ImageInfo", [
  8165  	"moxie/core/utils/Basic",
  8166  	"moxie/core/Exceptions",
  8167  	"moxie/runtime/html5/image/JPEG",
  8168  	"moxie/runtime/html5/image/PNG"
  8169  ], function(Basic, x, JPEG, PNG) {
  8170  	/**
  8171  	Optional image investigation tool for HTML5 runtime. Provides the following features:
  8172  	- ability to distinguish image type (JPEG or PNG) by signature
  8173  	- ability to extract image width/height directly from it's internals, without preloading in memory (fast)
  8174  	- ability to extract APP headers from JPEGs (Exif, GPS, etc)
  8175  	- ability to replace width/height tags in extracted JPEG headers
  8176  	- ability to restore APP headers, that were for example stripped during image manipulation
  8177  
  8178  	@class ImageInfo
  8179  	@constructor
  8180  	@param {String} binstr Image source as binary string
  8181  	*/
  8182  	return function(binstr) {
  8183  		var _cs = [JPEG, PNG], _img;
  8184  
  8185  		// figure out the format, throw: ImageError.WRONG_FORMAT if not supported
  8186  		_img = (function() {
  8187  			for (var i = 0; i < _cs.length; i++) {
  8188  				try {
  8189  					return new _cs[i](binstr);
  8190  				} catch (ex) {
  8191  					// console.info(ex);
  8192  				}
  8193  			}
  8194  			throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  8195  		}());
  8196  
  8197  		Basic.extend(this, {
  8198  			/**
  8199  			Image Mime Type extracted from it's depths
  8200  
  8201  			@property type
  8202  			@type {String}
  8203  			@default ''
  8204  			*/
  8205  			type: '',
  8206  
  8207  			/**
  8208  			Image size in bytes
  8209  
  8210  			@property size
  8211  			@type {Number}
  8212  			@default 0
  8213  			*/
  8214  			size: 0,
  8215  
  8216  			/**
  8217  			Image width extracted from image source
  8218  
  8219  			@property width
  8220  			@type {Number}
  8221  			@default 0
  8222  			*/
  8223  			width: 0,
  8224  
  8225  			/**
  8226  			Image height extracted from image source
  8227  
  8228  			@property height
  8229  			@type {Number}
  8230  			@default 0
  8231  			*/
  8232  			height: 0,
  8233  
  8234  			/**
  8235  			Sets Exif tag. Currently applicable only for width and height tags. Obviously works only with JPEGs.
  8236  
  8237  			@method setExif
  8238  			@param {String} tag Tag to set
  8239  			@param {Mixed} value Value to assign to the tag
  8240  			*/
  8241  			setExif: function() {},
  8242  
  8243  			/**
  8244  			Restores headers to the source.
  8245  
  8246  			@method writeHeaders
  8247  			@param {String} data Image source as binary string
  8248  			@return {String} Updated binary string
  8249  			*/
  8250  			writeHeaders: function(data) {
  8251  				return data;
  8252  			},
  8253  
  8254  			/**
  8255  			Strip all headers from the source.
  8256  
  8257  			@method stripHeaders
  8258  			@param {String} data Image source as binary string
  8259  			@return {String} Updated binary string
  8260  			*/
  8261  			stripHeaders: function(data) {
  8262  				return data;
  8263  			},
  8264  
  8265  			/**
  8266  			Dispose resources.
  8267  
  8268  			@method purge
  8269  			*/
  8270  			purge: function() {}
  8271  		});
  8272  
  8273  		Basic.extend(this, _img);
  8274  
  8275  		this.purge = function() {
  8276  			_img.purge();
  8277  			_img = null;
  8278  		};
  8279  	};
  8280  });
  8281  
  8282  // Included from: src/javascript/runtime/html5/image/MegaPixel.js
  8283  
  8284  /**
  8285  (The MIT License)
  8286  
  8287  Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>;
  8288  
  8289  Permission is hereby granted, free of charge, to any person obtaining
  8290  a copy of this software and associated documentation files (the
  8291  'Software'), to deal in the Software without restriction, including
  8292  without limitation the rights to use, copy, modify, merge, publish,
  8293  distribute, sublicense, and/or sell copies of the Software, and to
  8294  permit persons to whom the Software is furnished to do so, subject to
  8295  the following conditions:
  8296  
  8297  The above copyright notice and this permission notice shall be
  8298  included in all copies or substantial portions of the Software.
  8299  
  8300  THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
  8301  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  8302  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  8303  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  8304  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  8305  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  8306  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  8307  */
  8308  
  8309  /**
  8310   * Mega pixel image rendering library for iOS6 Safari
  8311   *
  8312   * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel),
  8313   * which causes unexpected subsampling when drawing it in canvas.
  8314   * By using this library, you can safely render the image with proper stretching.
  8315   *
  8316   * Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>
  8317   * Released under the MIT license
  8318   */
  8319  
  8320  /**
  8321  @class moxie/runtime/html5/image/MegaPixel
  8322  @private
  8323  */
  8324  define("moxie/runtime/html5/image/MegaPixel", [], function() {
  8325  
  8326  	/**
  8327  	 * Rendering image element (with resizing) into the canvas element
  8328  	 */
  8329  	function renderImageToCanvas(img, canvas, options) {
  8330  		var iw = img.naturalWidth, ih = img.naturalHeight;
  8331  		var width = options.width, height = options.height;
  8332  		var x = options.x || 0, y = options.y || 0;
  8333  		var ctx = canvas.getContext('2d');
  8334  		if (detectSubsampling(img)) {
  8335  			iw /= 2;
  8336  			ih /= 2;
  8337  		}
  8338  		var d = 1024; // size of tiling canvas
  8339  		var tmpCanvas = document.createElement('canvas');
  8340  		tmpCanvas.width = tmpCanvas.height = d;
  8341  		var tmpCtx = tmpCanvas.getContext('2d');
  8342  		var vertSquashRatio = detectVerticalSquash(img, iw, ih);
  8343  		var sy = 0;
  8344  		while (sy < ih) {
  8345  			var sh = sy + d > ih ? ih - sy : d;
  8346  			var sx = 0;
  8347  			while (sx < iw) {
  8348  				var sw = sx + d > iw ? iw - sx : d;
  8349  				tmpCtx.clearRect(0, 0, d, d);
  8350  				tmpCtx.drawImage(img, -sx, -sy);
  8351  				var dx = (sx * width / iw + x) << 0;
  8352  				var dw = Math.ceil(sw * width / iw);
  8353  				var dy = (sy * height / ih / vertSquashRatio + y) << 0;
  8354  				var dh = Math.ceil(sh * height / ih / vertSquashRatio);
  8355  				ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
  8356  				sx += d;
  8357  			}
  8358  			sy += d;
  8359  		}
  8360  		tmpCanvas = tmpCtx = null;
  8361  	}
  8362  
  8363  	/**
  8364  	 * Detect subsampling in loaded image.
  8365  	 * In iOS, larger images than 2M pixels may be subsampled in rendering.
  8366  	 */
  8367  	function detectSubsampling(img) {
  8368  		var iw = img.naturalWidth, ih = img.naturalHeight;
  8369  		if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image
  8370  			var canvas = document.createElement('canvas');
  8371  			canvas.width = canvas.height = 1;
  8372  			var ctx = canvas.getContext('2d');
  8373  			ctx.drawImage(img, -iw + 1, 0);
  8374  			// subsampled image becomes half smaller in rendering size.
  8375  			// check alpha channel value to confirm image is covering edge pixel or not.
  8376  			// if alpha value is 0 image is not covering, hence subsampled.
  8377  			return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
  8378  		} else {
  8379  			return false;
  8380  		}
  8381  	}
  8382  
  8383  
  8384  	/**
  8385  	 * Detecting vertical squash in loaded image.
  8386  	 * Fixes a bug which squash image vertically while drawing into canvas for some images.
  8387  	 */
  8388  	function detectVerticalSquash(img, iw, ih) {
  8389  		var canvas = document.createElement('canvas');
  8390  		canvas.width = 1;
  8391  		canvas.height = ih;
  8392  		var ctx = canvas.getContext('2d');
  8393  		ctx.drawImage(img, 0, 0);
  8394  		var data = ctx.getImageData(0, 0, 1, ih).data;
  8395  		// search image edge pixel position in case it is squashed vertically.
  8396  		var sy = 0;
  8397  		var ey = ih;
  8398  		var py = ih;
  8399  		while (py > sy) {
  8400  			var alpha = data[(py - 1) * 4 + 3];
  8401  			if (alpha === 0) {
  8402  				ey = py;
  8403  			} else {
  8404  			sy = py;
  8405  			}
  8406  			py = (ey + sy) >> 1;
  8407  		}
  8408  		canvas = null;
  8409  		var ratio = (py / ih);
  8410  		return (ratio === 0) ? 1 : ratio;
  8411  	}
  8412  
  8413  	return {
  8414  		isSubsampled: detectSubsampling,
  8415  		renderTo: renderImageToCanvas
  8416  	};
  8417  });
  8418  
  8419  // Included from: src/javascript/runtime/html5/image/Image.js
  8420  
  8421  /**
  8422   * Image.js
  8423   *
  8424   * Copyright 2013, Moxiecode Systems AB
  8425   * Released under GPL License.
  8426   *
  8427   * License: http://www.plupload.com/license
  8428   * Contributing: http://www.plupload.com/contributing
  8429   */
  8430  
  8431  /**
  8432  @class moxie/runtime/html5/image/Image
  8433  @private
  8434  */
  8435  define("moxie/runtime/html5/image/Image", [
  8436  	"moxie/runtime/html5/Runtime",
  8437  	"moxie/core/utils/Basic",
  8438  	"moxie/core/Exceptions",
  8439  	"moxie/core/utils/Encode",
  8440  	"moxie/file/File",
  8441  	"moxie/runtime/html5/image/ImageInfo",
  8442  	"moxie/runtime/html5/image/MegaPixel",
  8443  	"moxie/core/utils/Mime",
  8444  	"moxie/core/utils/Env"
  8445  ], function(extensions, Basic, x, Encode, File, ImageInfo, MegaPixel, Mime, Env) {
  8446  	
  8447  	function HTML5Image() {
  8448  		var me = this
  8449  		, _img, _imgInfo, _canvas, _binStr, _blob
  8450  		, _modified = false // is set true whenever image is modified
  8451  		, _preserveHeaders = true
  8452  		;
  8453  
  8454  		Basic.extend(this, {
  8455  			loadFromBlob: function(blob) {
  8456  				var comp = this, I = comp.getRuntime()
  8457  				, asBinary = arguments.length > 1 ? arguments[1] : true
  8458  				;
  8459  
  8460  				if (!I.can('access_binary')) {
  8461  					throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  8462  				}
  8463  
  8464  				_blob = blob;
  8465  
  8466  				if (blob.isDetached()) {
  8467  					_binStr = blob.getSource();
  8468  					_preload.call(this, _binStr);
  8469  					return;
  8470  				} else {
  8471  					_readAsDataUrl.call(this, blob.getSource(), function(dataUrl) {
  8472  						if (asBinary) {
  8473  							_binStr = _toBinary(dataUrl);
  8474  						}
  8475  						_preload.call(comp, dataUrl);
  8476  					});
  8477  				}
  8478  			},
  8479  
  8480  			loadFromImage: function(img, exact) {
  8481  				this.meta = img.meta;
  8482  
  8483  				_blob = new File(null, {
  8484  					name: img.name,
  8485  					size: img.size,
  8486  					type: img.type
  8487  				});
  8488  
  8489  				_preload.call(this, exact ? (_binStr = img.getAsBinaryString()) : img.getAsDataURL());
  8490  			},
  8491  
  8492  			getInfo: function() {
  8493  				var I = this.getRuntime(), info;
  8494  
  8495  				if (!_imgInfo && _binStr && I.can('access_image_binary')) {
  8496  					_imgInfo = new ImageInfo(_binStr);
  8497  				}
  8498  
  8499  				info = {
  8500  					width: _getImg().width || 0,
  8501  					height: _getImg().height || 0,
  8502  					type: _blob.type || Mime.getFileMime(_blob.name),
  8503  					size: _binStr && _binStr.length || _blob.size || 0,
  8504  					name: _blob.name || '',
  8505  					meta: _imgInfo && _imgInfo.meta || this.meta || {}
  8506  				};
  8507  
  8508  				return info;
  8509  			},
  8510  
  8511  			downsize: function() {
  8512  				_downsize.apply(this, arguments);
  8513  			},
  8514  
  8515  			getAsCanvas: function() {
  8516  				if (_canvas) {
  8517  					_canvas.id = this.uid + '_canvas';
  8518  				}
  8519  				return _canvas;
  8520  			},
  8521  
  8522  			getAsBlob: function(type, quality) {
  8523  				if (type !== this.type) {
  8524  					// if different mime type requested prepare image for conversion
  8525  					_downsize.call(this, this.width, this.height, false);
  8526  				}
  8527  				return new File(null, {
  8528  					name: _blob.name || '',
  8529  					type: type,
  8530  					data: me.getAsBinaryString.call(this, type, quality)
  8531  				});
  8532  			},
  8533  
  8534  			getAsDataURL: function(type) {
  8535  				var quality = arguments[1] || 90;
  8536  
  8537  				// if image has not been modified, return the source right away
  8538  				if (!_modified) {
  8539  					return _img.src;
  8540  				}
  8541  
  8542  				if ('image/jpeg' !== type) {
  8543  					return _canvas.toDataURL('image/png');
  8544  				} else {
  8545  					try {
  8546  						// older Geckos used to result in an exception on quality argument
  8547  						return _canvas.toDataURL('image/jpeg', quality/100);
  8548  					} catch (ex) {
  8549  						return _canvas.toDataURL('image/jpeg');
  8550  					}
  8551  				}
  8552  			},
  8553  
  8554  			getAsBinaryString: function(type, quality) {
  8555  				// if image has not been modified, return the source right away
  8556  				if (!_modified) {
  8557  					// if image was not loaded from binary string
  8558  					if (!_binStr) {
  8559  						_binStr = _toBinary(me.getAsDataURL(type, quality));
  8560  					}
  8561  					return _binStr;
  8562  				}
  8563  
  8564  				if ('image/jpeg' !== type) {
  8565  					_binStr = _toBinary(me.getAsDataURL(type, quality));
  8566  				} else {
  8567  					var dataUrl;
  8568  
  8569  					// if jpeg
  8570  					if (!quality) {
  8571  						quality = 90;
  8572  					}
  8573  
  8574  					try {
  8575  						// older Geckos used to result in an exception on quality argument
  8576  						dataUrl = _canvas.toDataURL('image/jpeg', quality/100);
  8577  					} catch (ex) {
  8578  						dataUrl = _canvas.toDataURL('image/jpeg');
  8579  					}
  8580  
  8581  					_binStr = _toBinary(dataUrl);
  8582  
  8583  					if (_imgInfo) {
  8584  						_binStr = _imgInfo.stripHeaders(_binStr);
  8585  
  8586  						if (_preserveHeaders) {
  8587  							// update dimensions info in exif
  8588  							if (_imgInfo.meta && _imgInfo.meta.exif) {
  8589  								_imgInfo.setExif({
  8590  									PixelXDimension: this.width,
  8591  									PixelYDimension: this.height
  8592  								});
  8593  							}
  8594  
  8595  							// re-inject the headers
  8596  							_binStr = _imgInfo.writeHeaders(_binStr);
  8597  						}
  8598  
  8599  						// will be re-created from fresh on next getInfo call
  8600  						_imgInfo.purge();
  8601  						_imgInfo = null;
  8602  					}
  8603  				}
  8604  
  8605  				_modified = false;
  8606  
  8607  				return _binStr;
  8608  			},
  8609  
  8610  			destroy: function() {
  8611  				me = null;
  8612  				_purge.call(this);
  8613  				this.getRuntime().getShim().removeInstance(this.uid);
  8614  			}
  8615  		});
  8616  
  8617  
  8618  		function _getImg() {
  8619  			if (!_canvas && !_img) {
  8620  				throw new x.ImageError(x.DOMException.INVALID_STATE_ERR);
  8621  			}
  8622  			return _canvas || _img;
  8623  		}
  8624  
  8625  
  8626  		function _toBinary(str) {
  8627  			return Encode.atob(str.substring(str.indexOf('base64,') + 7));
  8628  		}
  8629  
  8630  
  8631  		function _toDataUrl(str, type) {
  8632  			return 'data:' + (type || '') + ';base64,' + Encode.btoa(str);
  8633  		}
  8634  
  8635  
  8636  		function _preload(str) {
  8637  			var comp = this;
  8638  
  8639  			_img = new Image();
  8640  			_img.onerror = function() {
  8641  				_purge.call(this);
  8642  				comp.trigger('error', x.ImageError.WRONG_FORMAT);
  8643  			};
  8644  			_img.onload = function() {
  8645  				comp.trigger('load');
  8646  			};
  8647  
  8648  			_img.src = /^data:[^;]*;base64,/.test(str) ? str : _toDataUrl(str, _blob.type);
  8649  		}
  8650  
  8651  
  8652  		function _readAsDataUrl(file, callback) {
  8653  			var comp = this, fr;
  8654  
  8655  			// use FileReader if it's available
  8656  			if (window.FileReader) {
  8657  				fr = new FileReader();
  8658  				fr.onload = function() {
  8659  					callback(this.result);
  8660  				};
  8661  				fr.onerror = function() {
  8662  					comp.trigger('error', x.ImageError.WRONG_FORMAT);
  8663  				};
  8664  				fr.readAsDataURL(file);
  8665  			} else {
  8666  				return callback(file.getAsDataURL());
  8667  			}
  8668  		}
  8669  
  8670  		function _downsize(width, height, crop, preserveHeaders) {
  8671  			var self = this
  8672  			, scale
  8673  			, mathFn
  8674  			, x = 0
  8675  			, y = 0
  8676  			, img
  8677  			, destWidth
  8678  			, destHeight
  8679  			, orientation
  8680  			;
  8681  
  8682  			_preserveHeaders = preserveHeaders; // we will need to check this on export (see getAsBinaryString())
  8683  
  8684  			// take into account orientation tag
  8685  			orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1;
  8686  
  8687  			if (Basic.inArray(orientation, [5,6,7,8]) !== -1) { // values that require 90 degree rotation
  8688  				// swap dimensions
  8689  				var tmp = width;
  8690  				width = height;
  8691  				height = tmp;
  8692  			}
  8693  
  8694  			img = _getImg();
  8695  
  8696  			// unify dimensions
  8697  			if (!crop) {
  8698  				scale = Math.min(width/img.width, height/img.height);
  8699  			} else {
  8700  				// one of the dimensions may exceed the actual image dimensions - we need to take the smallest value
  8701  				width = Math.min(width, img.width);
  8702  				height = Math.min(height, img.height);
  8703  
  8704  				scale = Math.max(width/img.width, height/img.height);
  8705  			}
  8706  		
  8707  			// we only downsize here
  8708  			if (scale > 1 && !crop && preserveHeaders) {
  8709  				this.trigger('Resize');
  8710  				return;
  8711  			}
  8712  
  8713  			// prepare canvas if necessary
  8714  			if (!_canvas) {
  8715  				_canvas = document.createElement("canvas");
  8716  			}
  8717  
  8718  			// calculate dimensions of proportionally resized image
  8719  			destWidth = Math.round(img.width * scale);	
  8720  			destHeight = Math.round(img.height * scale);
  8721  
  8722  			// scale image and canvas
  8723  			if (crop) {
  8724  				_canvas.width = width;
  8725  				_canvas.height = height;
  8726  
  8727  				// if dimensions of the resulting image still larger than canvas, center it
  8728  				if (destWidth > width) {
  8729  					x = Math.round((destWidth - width) / 2);
  8730  				}
  8731  
  8732  				if (destHeight > height) {
  8733  					y = Math.round((destHeight - height) / 2);
  8734  				}
  8735  			} else {
  8736  				_canvas.width = destWidth;
  8737  				_canvas.height = destHeight;
  8738  			}
  8739  
  8740  			// rotate if required, according to orientation tag
  8741  			if (!_preserveHeaders) {
  8742  				_rotateToOrientaion(_canvas.width, _canvas.height, orientation);
  8743  			}
  8744  
  8745  			_drawToCanvas.call(this, img, _canvas, -x, -y, destWidth, destHeight);
  8746  
  8747  			this.width = _canvas.width;
  8748  			this.height = _canvas.height;
  8749  
  8750  			_modified = true;
  8751  			self.trigger('Resize');
  8752  		}
  8753  
  8754  
  8755  		function _drawToCanvas(img, canvas, x, y, w, h) {
  8756  			if (Env.OS === 'iOS') { 
  8757  				// avoid squish bug in iOS6
  8758  				MegaPixel.renderTo(img, canvas, { width: w, height: h, x: x, y: y });
  8759  			} else {
  8760  				var ctx = canvas.getContext('2d');
  8761  				ctx.drawImage(img, x, y, w, h);
  8762  			}
  8763  		}
  8764  
  8765  
  8766  		/**
  8767  		* Transform canvas coordination according to specified frame size and orientation
  8768  		* Orientation value is from EXIF tag
  8769  		* @author Shinichi Tomita <shinichi.tomita@gmail.com>
  8770  		*/
  8771  		function _rotateToOrientaion(width, height, orientation) {
  8772  			switch (orientation) {
  8773  				case 5:
  8774  				case 6:
  8775  				case 7:
  8776  				case 8:
  8777  					_canvas.width = height;
  8778  					_canvas.height = width;
  8779  					break;
  8780  				default:
  8781  					_canvas.width = width;
  8782  					_canvas.height = height;
  8783  			}
  8784  
  8785  			/**
  8786  			1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
  8787  			2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
  8788  			3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
  8789  			4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
  8790  			5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
  8791  			6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
  8792  			7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
  8793  			8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
  8794  			*/
  8795  
  8796  			var ctx = _canvas.getContext('2d');
  8797  			switch (orientation) {
  8798  				case 2:
  8799  					// horizontal flip
  8800  					ctx.translate(width, 0);
  8801  					ctx.scale(-1, 1);
  8802  					break;
  8803  				case 3:
  8804  					// 180 rotate left
  8805  					ctx.translate(width, height);
  8806  					ctx.rotate(Math.PI);
  8807  					break;
  8808  				case 4:
  8809  					// vertical flip
  8810  					ctx.translate(0, height);
  8811  					ctx.scale(1, -1);
  8812  					break;
  8813  				case 5:
  8814  					// vertical flip + 90 rotate right
  8815  					ctx.rotate(0.5 * Math.PI);
  8816  					ctx.scale(1, -1);
  8817  					break;
  8818  				case 6:
  8819  					// 90 rotate right
  8820  					ctx.rotate(0.5 * Math.PI);
  8821  					ctx.translate(0, -height);
  8822  					break;
  8823  				case 7:
  8824  					// horizontal flip + 90 rotate right
  8825  					ctx.rotate(0.5 * Math.PI);
  8826  					ctx.translate(width, -height);
  8827  					ctx.scale(-1, 1);
  8828  					break;
  8829  				case 8:
  8830  					// 90 rotate left
  8831  					ctx.rotate(-0.5 * Math.PI);
  8832  					ctx.translate(-width, 0);
  8833  					break;
  8834  			}
  8835  		}
  8836  
  8837  
  8838  		function _purge() {
  8839  			if (_imgInfo) {
  8840  				_imgInfo.purge();
  8841  				_imgInfo = null;
  8842  			}
  8843  			_binStr = _img = _canvas = _blob = null;
  8844  			_modified = false;
  8845  		}
  8846  	}
  8847  
  8848  	return (extensions.Image = HTML5Image);
  8849  });
  8850  
  8851  // Included from: src/javascript/runtime/flash/Runtime.js
  8852  
  8853  /**
  8854   * Runtime.js
  8855   *
  8856   * Copyright 2013, Moxiecode Systems AB
  8857   * Released under GPL License.
  8858   *
  8859   * License: http://www.plupload.com/license
  8860   * Contributing: http://www.plupload.com/contributing
  8861   */
  8862  
  8863  /*global ActiveXObject:true */
  8864  
  8865  /**
  8866  Defines constructor for Flash runtime.
  8867  
  8868  @class moxie/runtime/flash/Runtime
  8869  @private
  8870  */
  8871  define("moxie/runtime/flash/Runtime", [
  8872  	"moxie/core/utils/Basic",
  8873  	"moxie/core/utils/Env",
  8874  	"moxie/core/utils/Dom",
  8875  	"moxie/core/Exceptions",
  8876  	"moxie/runtime/Runtime"
  8877  ], function(Basic, Env, Dom, x, Runtime) {
  8878  	
  8879  	var type = 'flash', extensions = {};
  8880  
  8881  	/**
  8882  	Get the version of the Flash Player
  8883  
  8884  	@method getShimVersion
  8885  	@private
  8886  	@return {Number} Flash Player version
  8887  	*/
  8888  	function getShimVersion() {
  8889  		var version;
  8890  
  8891  		try {
  8892  			version = navigator.plugins['Shockwave Flash'];
  8893  			version = version.description;
  8894  		} catch (e1) {
  8895  			try {
  8896  				version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
  8897  			} catch (e2) {
  8898  				version = '0.0';
  8899  			}
  8900  		}
  8901  		version = version.match(/\d+/g);
  8902  		return parseFloat(version[0] + '.' + version[1]);
  8903  	}
  8904  
  8905  	/**
  8906  	Constructor for the Flash Runtime
  8907  
  8908  	@class FlashRuntime
  8909  	@extends Runtime
  8910  	*/
  8911  	function FlashRuntime(options) {
  8912  		var I = this, initTimer;
  8913  
  8914  		options = Basic.extend({ swf_url: Env.swf_url }, options);
  8915  
  8916  		Runtime.call(this, options, type, {
  8917  			access_binary: function(value) {
  8918  				return value && I.mode === 'browser';
  8919  			},
  8920  			access_image_binary: function(value) {
  8921  				return value && I.mode === 'browser';
  8922  			},
  8923  			display_media: Runtime.capTrue,
  8924  			do_cors: Runtime.capTrue,
  8925  			drag_and_drop: false,
  8926  			report_upload_progress: function() {
  8927  				return I.mode === 'client';
  8928  			},
  8929  			resize_image: Runtime.capTrue,
  8930  			return_response_headers: false,
  8931  			return_response_type: function(responseType) {
  8932  				if (responseType === 'json' && !!window.JSON) {
  8933  					return true;
  8934  				} 
  8935  				return !Basic.arrayDiff(responseType, ['', 'text', 'document']) || I.mode === 'browser';
  8936  			},
  8937  			return_status_code: function(code) {
  8938  				return I.mode === 'browser' || !Basic.arrayDiff(code, [200, 404]);
  8939  			},
  8940  			select_file: Runtime.capTrue,
  8941  			select_multiple: Runtime.capTrue,
  8942  			send_binary_string: function(value) {
  8943  				return value && I.mode === 'browser';
  8944  			},
  8945  			send_browser_cookies: function(value) {
  8946  				return value && I.mode === 'browser';
  8947  			},
  8948  			send_custom_headers: function(value) {
  8949  				return value && I.mode === 'browser';
  8950  			},
  8951  			send_multipart: Runtime.capTrue,
  8952  			slice_blob: function(value) {
  8953  				return value && I.mode === 'browser';
  8954  			},
  8955  			stream_upload: function(value) {
  8956  				return value && I.mode === 'browser';
  8957  			},
  8958  			summon_file_dialog: false,
  8959  			upload_filesize: function(size) {
  8960  				return Basic.parseSizeStr(size) <= 2097152 || I.mode === 'client';
  8961  			},
  8962  			use_http_method: function(methods) {
  8963  				return !Basic.arrayDiff(methods, ['GET', 'POST']);
  8964  			}
  8965  		}, { 
  8966  			// capabilities that require specific mode
  8967  			access_binary: function(value) {
  8968  				return value ? 'browser' : 'client';
  8969  			},
  8970  			access_image_binary: function(value) {
  8971  				return value ? 'browser' : 'client';
  8972  			},
  8973  			report_upload_progress: function(value) {
  8974  				return value ? 'browser' : 'client';
  8975  			},
  8976  			return_response_type: function(responseType) {
  8977  				return Basic.arrayDiff(responseType, ['', 'text', 'json', 'document']) ? 'browser' : ['client', 'browser'];
  8978  			},
  8979  			return_status_code: function(code) {
  8980  				return Basic.arrayDiff(code, [200, 404]) ? 'browser' : ['client', 'browser'];
  8981  			},
  8982  			send_binary_string: function(value) {
  8983  				return value ? 'browser' : 'client';
  8984  			},
  8985  			send_browser_cookies: function(value) {
  8986  				return value ? 'browser' : 'client';
  8987  			},
  8988  			send_custom_headers: function(value) {
  8989  				return value ? 'browser' : 'client';
  8990  			},
  8991  			stream_upload: function(value) {
  8992  				return value ? 'client' : 'browser';
  8993  			},
  8994  			upload_filesize: function(size) {
  8995  				return Basic.parseSizeStr(size) >= 2097152 ? 'client' : 'browser';
  8996  			}
  8997  		}, 'client');
  8998  
  8999  
  9000  		// minimal requirement for Flash Player version
  9001  		if (getShimVersion() < 10) {
  9002  			this.mode = false; // with falsy mode, runtime won't operable, no matter what the mode was before
  9003  		}
  9004  
  9005  
  9006  		Basic.extend(this, {
  9007  
  9008  			getShim: function() {
  9009  				return Dom.get(this.uid);
  9010  			},
  9011  
  9012  			shimExec: function(component, action) {
  9013  				var args = [].slice.call(arguments, 2);
  9014  				return I.getShim().exec(this.uid, component, action, args);
  9015  			},
  9016  
  9017  			init: function() {
  9018  				var html, el, container;
  9019  
  9020  				container = this.getShimContainer();
  9021  
  9022  				// if not the minimal height, shims are not initialized in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)
  9023  				Basic.extend(container.style, {
  9024  					position: 'absolute',
  9025  					top: '-8px',
  9026  					left: '-8px',
  9027  					width: '9px',
  9028  					height: '9px',
  9029  					overflow: 'hidden'
  9030  				});
  9031  
  9032  				// insert flash object
  9033  				html = '<object id="' + this.uid + '" type="application/x-shockwave-flash" data="' +  options.swf_url + '" ';
  9034  
  9035  				if (Env.browser === 'IE') {
  9036  					html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
  9037  				}
  9038  
  9039  				html += 'width="100%" height="100%" style="outline:0">'  +
  9040  					'<param name="movie" value="' + options.swf_url + '" />' +
  9041  					'<param name="flashvars" value="uid=' + escape(this.uid) + '&target=' + Env.global_event_dispatcher + '" />' +
  9042  					'<param name="wmode" value="transparent" />' +
  9043  					'<param name="allowscriptaccess" value="always" />' +
  9044  				'</object>';
  9045  
  9046  				if (Env.browser === 'IE') {
  9047  					el = document.createElement('div');
  9048  					container.appendChild(el);
  9049  					el.outerHTML = html;
  9050  					el = container = null; // just in case
  9051  				} else {
  9052  					container.innerHTML = html;
  9053  				}
  9054  
  9055  				// Init is dispatched by the shim
  9056  				initTimer = setTimeout(function() {
  9057  					if (I && !I.initialized) { // runtime might be already destroyed by this moment
  9058  						I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
  9059  					}
  9060  				}, 5000);
  9061  			},
  9062  
  9063  			destroy: (function(destroy) { // extend default destroy method
  9064  				return function() {
  9065  					destroy.call(I);
  9066  					clearTimeout(initTimer); // initialization check might be still onwait
  9067  					options = initTimer = destroy = I = null;
  9068  				};
  9069  			}(this.destroy))
  9070  
  9071  		}, extensions);
  9072  	}
  9073  
  9074  	Runtime.addConstructor(type, FlashRuntime);
  9075  
  9076  	return extensions;
  9077  });
  9078  
  9079  // Included from: src/javascript/runtime/flash/file/Blob.js
  9080  
  9081  /**
  9082   * Blob.js
  9083   *
  9084   * Copyright 2013, Moxiecode Systems AB
  9085   * Released under GPL License.
  9086   *
  9087   * License: http://www.plupload.com/license
  9088   * Contributing: http://www.plupload.com/contributing
  9089   */
  9090  
  9091  /**
  9092  @class moxie/runtime/flash/file/Blob
  9093  @private
  9094  */
  9095  define("moxie/runtime/flash/file/Blob", [
  9096  	"moxie/runtime/flash/Runtime",
  9097  	"moxie/file/Blob"
  9098  ], function(extensions, Blob) {
  9099  
  9100  	var FlashBlob = {
  9101  		slice: function(blob, start, end, type) {
  9102  			var self = this.getRuntime();
  9103  
  9104  			if (start < 0) {
  9105  				start = Math.max(blob.size + start, 0);
  9106  			} else if (start > 0) {
  9107  				start = Math.min(start, blob.size);
  9108  			}
  9109  
  9110  			if (end < 0) {
  9111  				end = Math.max(blob.size + end, 0);
  9112  			} else if (end > 0) {
  9113  				end = Math.min(end, blob.size);
  9114  			}
  9115  
  9116  			blob = self.shimExec.call(this, 'Blob', 'slice', start, end, type || '');
  9117  
  9118  			if (blob) {
  9119  				blob = new Blob(self.uid, blob);
  9120  			}
  9121  			return blob;
  9122  		}
  9123  	};
  9124  
  9125  	return (extensions.Blob = FlashBlob);
  9126  });
  9127  
  9128  // Included from: src/javascript/runtime/flash/file/FileInput.js
  9129  
  9130  /**
  9131   * FileInput.js
  9132   *
  9133   * Copyright 2013, Moxiecode Systems AB
  9134   * Released under GPL License.
  9135   *
  9136   * License: http://www.plupload.com/license
  9137   * Contributing: http://www.plupload.com/contributing
  9138   */
  9139  
  9140  /**
  9141  @class moxie/runtime/flash/file/FileInput
  9142  @private
  9143  */
  9144  define("moxie/runtime/flash/file/FileInput", [
  9145  	"moxie/runtime/flash/Runtime"
  9146  ], function(extensions) {
  9147  	
  9148  	var FileInput = {		
  9149  		init: function(options) {
  9150  			this.getRuntime().shimExec.call(this, 'FileInput', 'init', {
  9151  				name: options.name,
  9152  				accept: options.accept,
  9153  				multiple: options.multiple
  9154  			});
  9155  			this.trigger('ready');
  9156  		}
  9157  	};
  9158  
  9159  	return (extensions.FileInput = FileInput);
  9160  });
  9161  
  9162  // Included from: src/javascript/runtime/flash/file/FileReader.js
  9163  
  9164  /**
  9165   * FileReader.js
  9166   *
  9167   * Copyright 2013, Moxiecode Systems AB
  9168   * Released under GPL License.
  9169   *
  9170   * License: http://www.plupload.com/license
  9171   * Contributing: http://www.plupload.com/contributing
  9172   */
  9173  
  9174  /**
  9175  @class moxie/runtime/flash/file/FileReader
  9176  @private
  9177  */
  9178  define("moxie/runtime/flash/file/FileReader", [
  9179  	"moxie/runtime/flash/Runtime",
  9180  	"moxie/core/utils/Encode"
  9181  ], function(extensions, Encode) {
  9182  
  9183  	var _result = '';
  9184  
  9185  	function _formatData(data, op) {
  9186  		switch (op) {
  9187  			case 'readAsText':
  9188  				return Encode.atob(data, 'utf8');
  9189  			case 'readAsBinaryString':
  9190  				return Encode.atob(data);
  9191  			case 'readAsDataURL':
  9192  				return data;
  9193  		}
  9194  		return null;
  9195  	}
  9196  
  9197  	var FileReader = {
  9198  		read: function(op, blob) {
  9199  			var target = this, self = target.getRuntime();
  9200  
  9201  			// special prefix for DataURL read mode
  9202  			if (op === 'readAsDataURL') {
  9203  				_result = 'data:' + (blob.type || '') + ';base64,';
  9204  			}
  9205  
  9206  			target.bind('Progress', function(e, data) {
  9207  				if (data) {
  9208  					_result += _formatData(data, op);
  9209  				}
  9210  			});
  9211  
  9212  			return self.shimExec.call(this, 'FileReader', 'readAsBase64', blob.uid);
  9213  		},
  9214  
  9215  		getResult: function() {
  9216  			return _result;
  9217  		},
  9218  
  9219  		destroy: function() {
  9220  			_result = null;
  9221  		}
  9222  	};
  9223  
  9224  	return (extensions.FileReader = FileReader);
  9225  });
  9226  
  9227  // Included from: src/javascript/runtime/flash/file/FileReaderSync.js
  9228  
  9229  /**
  9230   * FileReaderSync.js
  9231   *
  9232   * Copyright 2013, Moxiecode Systems AB
  9233   * Released under GPL License.
  9234   *
  9235   * License: http://www.plupload.com/license
  9236   * Contributing: http://www.plupload.com/contributing
  9237   */
  9238  
  9239  /**
  9240  @class moxie/runtime/flash/file/FileReaderSync
  9241  @private
  9242  */
  9243  define("moxie/runtime/flash/file/FileReaderSync", [
  9244  	"moxie/runtime/flash/Runtime",
  9245  	"moxie/core/utils/Encode"
  9246  ], function(extensions, Encode) {
  9247  	
  9248  	function _formatData(data, op) {
  9249  		switch (op) {
  9250  			case 'readAsText':
  9251  				return Encode.atob(data, 'utf8');
  9252  			case 'readAsBinaryString':
  9253  				return Encode.atob(data);
  9254  			case 'readAsDataURL':
  9255  				return data;
  9256  		}
  9257  		return null;
  9258  	}
  9259  
  9260  	var FileReaderSync = {
  9261  		read: function(op, blob) {
  9262  			var result, self = this.getRuntime();
  9263  
  9264  			result = self.shimExec.call(this, 'FileReaderSync', 'readAsBase64', blob.uid);
  9265  			if (!result) {
  9266  				return null; // or throw ex
  9267  			}
  9268  
  9269  			// special prefix for DataURL read mode
  9270  			if (op === 'readAsDataURL') {
  9271  				result = 'data:' + (blob.type || '') + ';base64,' + result;
  9272  			}
  9273  
  9274  			return _formatData(result, op, blob.type);
  9275  		}
  9276  	};
  9277  
  9278  	return (extensions.FileReaderSync = FileReaderSync);
  9279  });
  9280  
  9281  // Included from: src/javascript/runtime/flash/xhr/XMLHttpRequest.js
  9282  
  9283  /**
  9284   * XMLHttpRequest.js
  9285   *
  9286   * Copyright 2013, Moxiecode Systems AB
  9287   * Released under GPL License.
  9288   *
  9289   * License: http://www.plupload.com/license
  9290   * Contributing: http://www.plupload.com/contributing
  9291   */
  9292  
  9293  /**
  9294  @class moxie/runtime/flash/xhr/XMLHttpRequest
  9295  @private
  9296  */
  9297  define("moxie/runtime/flash/xhr/XMLHttpRequest", [
  9298  	"moxie/runtime/flash/Runtime",
  9299  	"moxie/core/utils/Basic",
  9300  	"moxie/file/Blob",
  9301  	"moxie/file/File",
  9302  	"moxie/file/FileReaderSync",
  9303  	"moxie/xhr/FormData",
  9304  	"moxie/runtime/Transporter"
  9305  ], function(extensions, Basic, Blob, File, FileReaderSync, FormData, Transporter) {
  9306  	
  9307  	var XMLHttpRequest = {
  9308  
  9309  		send: function(meta, data) {
  9310  			var target = this, self = target.getRuntime();
  9311  
  9312  			function send() {
  9313  				meta.transport = self.mode;
  9314  				self.shimExec.call(target, 'XMLHttpRequest', 'send', meta, data);
  9315  			}
  9316  
  9317  
  9318  			function appendBlob(name, blob) {
  9319  				self.shimExec.call(target, 'XMLHttpRequest', 'appendBlob', name, blob.uid);
  9320  				data = null;
  9321  				send();
  9322  			}
  9323  
  9324  
  9325  			function attachBlob(blob, cb) {
  9326  				var tr = new Transporter();
  9327  
  9328  				tr.bind("TransportingComplete", function() {
  9329  					cb(this.result);
  9330  				});
  9331  
  9332  				tr.transport(blob.getSource(), blob.type, {
  9333  					ruid: self.uid
  9334  				});
  9335  			}
  9336  
  9337  			// copy over the headers if any
  9338  			if (!Basic.isEmptyObj(meta.headers)) {
  9339  				Basic.each(meta.headers, function(value, header) {
  9340  					self.shimExec.call(target, 'XMLHttpRequest', 'setRequestHeader', header, value.toString()); // Silverlight doesn't accept integers into the arguments of type object
  9341  				});
  9342  			}
  9343  
  9344  			// transfer over multipart params and blob itself
  9345  			if (data instanceof FormData) {
  9346  				var blobField;
  9347  				data.each(function(value, name) {
  9348  					if (value instanceof Blob) {
  9349  						blobField = name;
  9350  					} else {
  9351  						self.shimExec.call(target, 'XMLHttpRequest', 'append', name, value);
  9352  					}
  9353  				});
  9354  
  9355  				if (!data.hasBlob()) {
  9356  					data = null;
  9357  					send();
  9358  				} else {
  9359  					var blob = data.getBlob();
  9360  					if (blob.isDetached()) {
  9361  						attachBlob(blob, function(attachedBlob) {
  9362  							blob.destroy();
  9363  							appendBlob(blobField, attachedBlob);		
  9364  						});
  9365  					} else {
  9366  						appendBlob(blobField, blob);
  9367  					}
  9368  				}
  9369  			} else if (data instanceof Blob) {
  9370  				if (data.isDetached()) {
  9371  					attachBlob(data, function(attachedBlob) {
  9372  						data.destroy();
  9373  						data = attachedBlob.uid;
  9374  						send();
  9375  					});
  9376  				} else {
  9377  					data = data.uid;
  9378  					send();
  9379  				}
  9380  			} else {
  9381  				send();
  9382  			}
  9383  		},
  9384  
  9385  		getResponse: function(responseType) {
  9386  			var frs, blob, self = this.getRuntime();
  9387  
  9388  			blob = self.shimExec.call(this, 'XMLHttpRequest', 'getResponseAsBlob');
  9389  
  9390  			if (blob) {
  9391  				blob = new File(self.uid, blob);
  9392  
  9393  				if ('blob' === responseType) {
  9394  					return blob;
  9395  				}
  9396  
  9397  				try { 
  9398  					frs = new FileReaderSync();
  9399  
  9400  					if (!!~Basic.inArray(responseType, ["", "text"])) {
  9401  						return frs.readAsText(blob);
  9402  					} else if ('json' === responseType && !!window.JSON) {
  9403  						return JSON.parse(frs.readAsText(blob));
  9404  					}
  9405  				} finally {
  9406  					blob.destroy();
  9407  				}
  9408  			}
  9409  			return null;
  9410  		},
  9411  
  9412  		abort: function(upload_complete_flag) {
  9413  			var self = this.getRuntime();
  9414  
  9415  			self.shimExec.call(this, 'XMLHttpRequest', 'abort');
  9416  
  9417  			this.dispatchEvent('readystatechange');
  9418  			// this.dispatchEvent('progress');
  9419  			this.dispatchEvent('abort');
  9420  
  9421  			//if (!upload_complete_flag) {
  9422  				// this.dispatchEvent('uploadprogress');
  9423  			//}
  9424  		}
  9425  	};
  9426  
  9427  	return (extensions.XMLHttpRequest = XMLHttpRequest);
  9428  });
  9429  
  9430  // Included from: src/javascript/runtime/flash/runtime/Transporter.js
  9431  
  9432  /**
  9433   * Transporter.js
  9434   *
  9435   * Copyright 2013, Moxiecode Systems AB
  9436   * Released under GPL License.
  9437   *
  9438   * License: http://www.plupload.com/license
  9439   * Contributing: http://www.plupload.com/contributing
  9440   */
  9441  
  9442  /**
  9443  @class moxie/runtime/flash/runtime/Transporter
  9444  @private
  9445  */
  9446  define("moxie/runtime/flash/runtime/Transporter", [
  9447  	"moxie/runtime/flash/Runtime",
  9448  	"moxie/file/Blob"
  9449  ], function(extensions, Blob) {
  9450  
  9451  	var Transporter = {
  9452  		getAsBlob: function(type) {
  9453  			var self = this.getRuntime()
  9454  			, blob = self.shimExec.call(this, 'Transporter', 'getAsBlob', type)
  9455  			;
  9456  			if (blob) {
  9457  				return new Blob(self.uid, blob);
  9458  			}
  9459  			return null;
  9460  		}
  9461  	};
  9462  
  9463  	return (extensions.Transporter = Transporter);
  9464  });
  9465  
  9466  // Included from: src/javascript/runtime/flash/image/Image.js
  9467  
  9468  /**
  9469   * Image.js
  9470   *
  9471   * Copyright 2013, Moxiecode Systems AB
  9472   * Released under GPL License.
  9473   *
  9474   * License: http://www.plupload.com/license
  9475   * Contributing: http://www.plupload.com/contributing
  9476   */
  9477  
  9478  /**
  9479  @class moxie/runtime/flash/image/Image
  9480  @private
  9481  */
  9482  define("moxie/runtime/flash/image/Image", [
  9483  	"moxie/runtime/flash/Runtime",
  9484  	"moxie/core/utils/Basic",
  9485  	"moxie/runtime/Transporter",
  9486  	"moxie/file/Blob",
  9487  	"moxie/file/FileReaderSync"
  9488  ], function(extensions, Basic, Transporter, Blob, FileReaderSync) {
  9489  	
  9490  	var Image = {
  9491  		loadFromBlob: function(blob) {
  9492  			var comp = this, self = comp.getRuntime();
  9493  
  9494  			function exec(srcBlob) {
  9495  				self.shimExec.call(comp, 'Image', 'loadFromBlob', srcBlob.uid);
  9496  				comp = self = null;
  9497  			}
  9498  
  9499  			if (blob.isDetached()) { // binary string
  9500  				var tr = new Transporter();
  9501  				tr.bind("TransportingComplete", function() {
  9502  					exec(tr.result.getSource());
  9503  				});
  9504  				tr.transport(blob.getSource(), blob.type, { ruid: self.uid });
  9505  			} else {
  9506  				exec(blob.getSource());
  9507  			}
  9508  		},
  9509  
  9510  		loadFromImage: function(img) {
  9511  			var self = this.getRuntime();
  9512  			return self.shimExec.call(this, 'Image', 'loadFromImage', img.uid);
  9513  		},
  9514  
  9515  		getAsBlob: function(type, quality) {
  9516  			var self = this.getRuntime()
  9517  			, blob = self.shimExec.call(this, 'Image', 'getAsBlob', type, quality)
  9518  			;
  9519  			if (blob) {
  9520  				return new Blob(self.uid, blob);
  9521  			}
  9522  			return null;
  9523  		},
  9524  
  9525  		getAsDataURL: function() {
  9526  			var self = this.getRuntime()
  9527  			, blob = self.Image.getAsBlob.apply(this, arguments)
  9528  			, frs
  9529  			;
  9530  			if (!blob) {
  9531  				return null;
  9532  			}
  9533  			frs = new FileReaderSync();
  9534  			return frs.readAsDataURL(blob);
  9535  		}
  9536  	};
  9537  
  9538  	return (extensions.Image = Image);
  9539  });
  9540  
  9541  // Included from: src/javascript/runtime/silverlight/Runtime.js
  9542  
  9543  /**
  9544   * RunTime.js
  9545   *
  9546   * Copyright 2013, Moxiecode Systems AB
  9547   * Released under GPL License.
  9548   *
  9549   * License: http://www.plupload.com/license
  9550   * Contributing: http://www.plupload.com/contributing
  9551   */
  9552  
  9553  /*global ActiveXObject:true */
  9554  
  9555  /**
  9556  Defines constructor for Silverlight runtime.
  9557  
  9558  @class moxie/runtime/silverlight/Runtime
  9559  @private
  9560  */
  9561  define("moxie/runtime/silverlight/Runtime", [
  9562  	"moxie/core/utils/Basic",
  9563  	"moxie/core/utils/Env",
  9564  	"moxie/core/utils/Dom",
  9565  	"moxie/core/Exceptions",
  9566  	"moxie/runtime/Runtime"
  9567  ], function(Basic, Env, Dom, x, Runtime) {
  9568  	
  9569  	var type = "silverlight", extensions = {};
  9570  
  9571  	function isInstalled(version) {
  9572  		var isVersionSupported = false, control = null, actualVer,
  9573  			actualVerArray, reqVerArray, requiredVersionPart, actualVersionPart, index = 0;
  9574  
  9575  		try {
  9576  			try {
  9577  				control = new ActiveXObject('AgControl.AgControl');
  9578  
  9579  				if (control.IsVersionSupported(version)) {
  9580  					isVersionSupported = true;
  9581  				}
  9582  
  9583  				control = null;
  9584  			} catch (e) {
  9585  				var plugin = navigator.plugins["Silverlight Plug-In"];
  9586  
  9587  				if (plugin) {
  9588  					actualVer = plugin.description;
  9589  
  9590  					if (actualVer === "1.0.30226.2") {
  9591  						actualVer = "2.0.30226.2";
  9592  					}
  9593  
  9594  					actualVerArray = actualVer.split(".");
  9595  
  9596  					while (actualVerArray.length > 3) {
  9597  						actualVerArray.pop();
  9598  					}
  9599  
  9600  					while ( actualVerArray.length < 4) {
  9601  						actualVerArray.push(0);
  9602  					}
  9603  
  9604  					reqVerArray = version.split(".");
  9605  
  9606  					while (reqVerArray.length > 4) {
  9607  						reqVerArray.pop();
  9608  					}
  9609  
  9610  					do {
  9611  						requiredVersionPart = parseInt(reqVerArray[index], 10);
  9612  						actualVersionPart = parseInt(actualVerArray[index], 10);
  9613  						index++;
  9614  					} while (index < reqVerArray.length && requiredVersionPart === actualVersionPart);
  9615  
  9616  					if (requiredVersionPart <= actualVersionPart && !isNaN(requiredVersionPart)) {
  9617  						isVersionSupported = true;
  9618  					}
  9619  				}
  9620  			}
  9621  		} catch (e2) {
  9622  			isVersionSupported = false;
  9623  		}
  9624  
  9625  		return isVersionSupported;
  9626  	}
  9627  
  9628  	/**
  9629  	Constructor for the Silverlight Runtime
  9630  
  9631  	@class SilverlightRuntime
  9632  	@extends Runtime
  9633  	*/
  9634  	function SilverlightRuntime(options) {
  9635  		var I = this, initTimer;
  9636  
  9637  		options = Basic.extend({ xap_url: Env.xap_url }, options);
  9638  
  9639  		Runtime.call(this, options, type, {
  9640  			access_binary: Runtime.capTrue,
  9641  			access_image_binary: Runtime.capTrue,
  9642  			display_media: Runtime.capTrue,
  9643  			do_cors: Runtime.capTrue,
  9644  			drag_and_drop: false,
  9645  			report_upload_progress: Runtime.capTrue,
  9646  			resize_image: Runtime.capTrue,
  9647  			return_response_headers: function(value) {
  9648  				return value && I.mode === 'client';
  9649  			},
  9650  			return_response_type: function(responseType) {
  9651  				if (responseType !== 'json') {
  9652  					return true;
  9653  				} else {
  9654  					return !!window.JSON;
  9655  				}
  9656  			},
  9657  			return_status_code: function(code) {
  9658  				return I.mode === 'client' || !Basic.arrayDiff(code, [200, 404]);
  9659  			},
  9660  			select_file: Runtime.capTrue,
  9661  			select_multiple: Runtime.capTrue,
  9662  			send_binary_string: Runtime.capTrue,
  9663  			send_browser_cookies: function(value) {
  9664  				return value && I.mode === 'browser';
  9665  			},
  9666  			send_custom_headers: function(value) {
  9667  				return value && I.mode === 'client';
  9668  			},
  9669  			send_multipart: Runtime.capTrue,
  9670  			slice_blob: Runtime.capTrue,
  9671  			stream_upload: true,
  9672  			summon_file_dialog: false,
  9673  			upload_filesize: Runtime.capTrue,
  9674  			use_http_method: function(methods) {
  9675  				return I.mode === 'client' || !Basic.arrayDiff(methods, ['GET', 'POST']);
  9676  			}
  9677  		}, { 
  9678  			// capabilities that require specific mode
  9679  			return_response_headers: function(value) {
  9680  				return value ? 'client' : 'browser';
  9681  			},
  9682  			return_status_code: function(code) {
  9683  				return Basic.arrayDiff(code, [200, 404]) ? 'client' : ['client', 'browser'];
  9684  			},
  9685  			send_browser_cookies: function(value) {
  9686  				return value ? 'browser' : 'client';
  9687  			},
  9688  			send_custom_headers: function(value) {
  9689  				return value ? 'client' : 'browser';
  9690  			},
  9691  			use_http_method: function(methods) {
  9692  				return Basic.arrayDiff(methods, ['GET', 'POST']) ? 'client' : ['client', 'browser'];
  9693  			}
  9694  		});
  9695  
  9696  
  9697  		// minimal requirement
  9698  		if (!isInstalled('2.0.31005.0') || Env.browser === 'Opera') {
  9699  			this.mode = false;
  9700  		}
  9701  
  9702  
  9703  		Basic.extend(this, {
  9704  			getShim: function() {
  9705  				return Dom.get(this.uid).content.Moxie;
  9706  			},
  9707  
  9708  			shimExec: function(component, action) {
  9709  				var args = [].slice.call(arguments, 2);
  9710  				return I.getShim().exec(this.uid, component, action, args);
  9711  			},
  9712  
  9713  			init : function() {
  9714  				var container;
  9715  
  9716  				container = this.getShimContainer();
  9717  
  9718  				container.innerHTML = '<object id="' + this.uid + '" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">' +
  9719  					'<param name="source" value="' + options.xap_url + '"/>' +
  9720  					'<param name="background" value="Transparent"/>' +
  9721  					'<param name="windowless" value="true"/>' +
  9722  					'<param name="enablehtmlaccess" value="true"/>' +
  9723  					'<param name="initParams" value="uid=' + this.uid + ',target=' + Env.global_event_dispatcher + '"/>' +
  9724  				'</object>';
  9725  
  9726  				// Init is dispatched by the shim
  9727  				initTimer = setTimeout(function() {
  9728  					if (I && !I.initialized) { // runtime might be already destroyed by this moment
  9729  						I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
  9730  					}
  9731  				}, Env.OS !== 'Windows'? 10000 : 5000); // give it more time to initialize in non Windows OS (like Mac)
  9732  			},
  9733  
  9734  			destroy: (function(destroy) { // extend default destroy method
  9735  				return function() {
  9736  					destroy.call(I);
  9737  					clearTimeout(initTimer); // initialization check might be still onwait
  9738  					options = initTimer = destroy = I = null;
  9739  				};
  9740  			}(this.destroy))
  9741  
  9742  		}, extensions);
  9743  	}
  9744  
  9745  	Runtime.addConstructor(type, SilverlightRuntime); 
  9746  
  9747  	return extensions;
  9748  });
  9749  
  9750  // Included from: src/javascript/runtime/silverlight/file/Blob.js
  9751  
  9752  /**
  9753   * Blob.js
  9754   *
  9755   * Copyright 2013, Moxiecode Systems AB
  9756   * Released under GPL License.
  9757   *
  9758   * License: http://www.plupload.com/license
  9759   * Contributing: http://www.plupload.com/contributing
  9760   */
  9761  
  9762  /**
  9763  @class moxie/runtime/silverlight/file/Blob
  9764  @private
  9765  */
  9766  define("moxie/runtime/silverlight/file/Blob", [
  9767  	"moxie/runtime/silverlight/Runtime",
  9768  	"moxie/core/utils/Basic",
  9769  	"moxie/runtime/flash/file/Blob"
  9770  ], function(extensions, Basic, Blob) {
  9771  	return (extensions.Blob = Basic.extend({}, Blob));
  9772  });
  9773  
  9774  // Included from: src/javascript/runtime/silverlight/file/FileInput.js
  9775  
  9776  /**
  9777   * FileInput.js
  9778   *
  9779   * Copyright 2013, Moxiecode Systems AB
  9780   * Released under GPL License.
  9781   *
  9782   * License: http://www.plupload.com/license
  9783   * Contributing: http://www.plupload.com/contributing
  9784   */
  9785  
  9786  /**
  9787  @class moxie/runtime/silverlight/file/FileInput
  9788  @private
  9789  */
  9790  define("moxie/runtime/silverlight/file/FileInput", [
  9791  	"moxie/runtime/silverlight/Runtime"
  9792  ], function(extensions) {
  9793  	
  9794  	var FileInput = {
  9795  		init: function(options) {
  9796  
  9797  			function toFilters(accept) {
  9798  				var filter = '';
  9799  				for (var i = 0; i < accept.length; i++) {
  9800  					filter += (filter !== '' ? '|' : '') + accept[i].title + " | *." + accept[i].extensions.replace(/,/g, ';*.');
  9801  				}
  9802  				return filter;
  9803  			}
  9804  			
  9805  			this.getRuntime().shimExec.call(this, 'FileInput', 'init', toFilters(options.accept), options.name, options.multiple);
  9806  			this.trigger('ready');
  9807  		}
  9808  	};
  9809  
  9810  	return (extensions.FileInput = FileInput);
  9811  });
  9812  
  9813  // Included from: src/javascript/runtime/silverlight/file/FileDrop.js
  9814  
  9815  /**
  9816   * FileDrop.js
  9817   *
  9818   * Copyright 2013, Moxiecode Systems AB
  9819   * Released under GPL License.
  9820   *
  9821   * License: http://www.plupload.com/license
  9822   * Contributing: http://www.plupload.com/contributing
  9823   */
  9824  
  9825  /**
  9826  @class moxie/runtime/silverlight/file/FileDrop
  9827  @private
  9828  */
  9829  define("moxie/runtime/silverlight/file/FileDrop", [
  9830  	"moxie/runtime/silverlight/Runtime",
  9831  	"moxie/core/utils/Dom", 
  9832  	"moxie/core/utils/Events"
  9833  ], function(extensions, Dom, Events) {
  9834  
  9835  	// not exactly useful, since works only in safari (...crickets...)
  9836  	var FileDrop = {
  9837  		init: function() {
  9838  			var comp = this, self = comp.getRuntime(), dropZone;
  9839  
  9840  			dropZone = self.getShimContainer();
  9841  
  9842  			Events.addEvent(dropZone, 'dragover', function(e) {
  9843  				e.preventDefault();
  9844  				e.stopPropagation();
  9845  				e.dataTransfer.dropEffect = 'copy';
  9846  			}, comp.uid);
  9847  
  9848  			Events.addEvent(dropZone, 'dragenter', function(e) {
  9849  				e.preventDefault();
  9850  				var flag = Dom.get(self.uid).dragEnter(e);
  9851  				// If handled, then stop propagation of event in DOM
  9852  				if (flag) {
  9853  					e.stopPropagation();
  9854  				}
  9855  			}, comp.uid);
  9856  
  9857  			Events.addEvent(dropZone, 'drop', function(e) {
  9858  				e.preventDefault();
  9859  				var flag = Dom.get(self.uid).dragDrop(e);
  9860  				// If handled, then stop propagation of event in DOM
  9861  				if (flag) {
  9862  					e.stopPropagation();
  9863  				}
  9864  			}, comp.uid);
  9865  
  9866  			return self.shimExec.call(this, 'FileDrop', 'init');
  9867  		}
  9868  	};
  9869  
  9870  	return (extensions.FileDrop = FileDrop);
  9871  });
  9872  
  9873  // Included from: src/javascript/runtime/silverlight/file/FileReader.js
  9874  
  9875  /**
  9876   * FileReader.js
  9877   *
  9878   * Copyright 2013, Moxiecode Systems AB
  9879   * Released under GPL License.
  9880   *
  9881   * License: http://www.plupload.com/license
  9882   * Contributing: http://www.plupload.com/contributing
  9883   */
  9884  
  9885  /**
  9886  @class moxie/runtime/silverlight/file/FileReader
  9887  @private
  9888  */
  9889  define("moxie/runtime/silverlight/file/FileReader", [
  9890  	"moxie/runtime/silverlight/Runtime",
  9891  	"moxie/core/utils/Basic",
  9892  	"moxie/runtime/flash/file/FileReader"
  9893  ], function(extensions, Basic, FileReader) {
  9894  	return (extensions.FileReader = Basic.extend({}, FileReader));
  9895  });
  9896  
  9897  // Included from: src/javascript/runtime/silverlight/file/FileReaderSync.js
  9898  
  9899  /**
  9900   * FileReaderSync.js
  9901   *
  9902   * Copyright 2013, Moxiecode Systems AB
  9903   * Released under GPL License.
  9904   *
  9905   * License: http://www.plupload.com/license
  9906   * Contributing: http://www.plupload.com/contributing
  9907   */
  9908  
  9909  /**
  9910  @class moxie/runtime/silverlight/file/FileReaderSync
  9911  @private
  9912  */
  9913  define("moxie/runtime/silverlight/file/FileReaderSync", [
  9914  	"moxie/runtime/silverlight/Runtime",
  9915  	"moxie/core/utils/Basic",
  9916  	"moxie/runtime/flash/file/FileReaderSync"
  9917  ], function(extensions, Basic, FileReaderSync) {
  9918  	return (extensions.FileReaderSync = Basic.extend({}, FileReaderSync));
  9919  });
  9920  
  9921  // Included from: src/javascript/runtime/silverlight/xhr/XMLHttpRequest.js
  9922  
  9923  /**
  9924   * XMLHttpRequest.js
  9925   *
  9926   * Copyright 2013, Moxiecode Systems AB
  9927   * Released under GPL License.
  9928   *
  9929   * License: http://www.plupload.com/license
  9930   * Contributing: http://www.plupload.com/contributing
  9931   */
  9932  
  9933  /**
  9934  @class moxie/runtime/silverlight/xhr/XMLHttpRequest
  9935  @private
  9936  */
  9937  define("moxie/runtime/silverlight/xhr/XMLHttpRequest", [
  9938  	"moxie/runtime/silverlight/Runtime",
  9939  	"moxie/core/utils/Basic",
  9940  	"moxie/runtime/flash/xhr/XMLHttpRequest"
  9941  ], function(extensions, Basic, XMLHttpRequest) {
  9942  	return (extensions.XMLHttpRequest = Basic.extend({}, XMLHttpRequest));
  9943  });
  9944  
  9945  // Included from: src/javascript/runtime/silverlight/runtime/Transporter.js
  9946  
  9947  /**
  9948   * Transporter.js
  9949   *
  9950   * Copyright 2013, Moxiecode Systems AB
  9951   * Released under GPL License.
  9952   *
  9953   * License: http://www.plupload.com/license
  9954   * Contributing: http://www.plupload.com/contributing
  9955   */
  9956  
  9957  /**
  9958  @class moxie/runtime/silverlight/runtime/Transporter
  9959  @private
  9960  */
  9961  define("moxie/runtime/silverlight/runtime/Transporter", [
  9962  	"moxie/runtime/silverlight/Runtime",
  9963  	"moxie/core/utils/Basic",
  9964  	"moxie/runtime/flash/runtime/Transporter"
  9965  ], function(extensions, Basic, Transporter) {
  9966  	return (extensions.Transporter = Basic.extend({}, Transporter));
  9967  });
  9968  
  9969  // Included from: src/javascript/runtime/silverlight/image/Image.js
  9970  
  9971  /**
  9972   * Image.js
  9973   *
  9974   * Copyright 2013, Moxiecode Systems AB
  9975   * Released under GPL License.
  9976   *
  9977   * License: http://www.plupload.com/license
  9978   * Contributing: http://www.plupload.com/contributing
  9979   */
  9980   
  9981  /**
  9982  @class moxie/runtime/silverlight/image/Image
  9983  @private
  9984  */
  9985  define("moxie/runtime/silverlight/image/Image", [
  9986  	"moxie/runtime/silverlight/Runtime",
  9987  	"moxie/core/utils/Basic",
  9988  	"moxie/runtime/flash/image/Image"
  9989  ], function(extensions, Basic, Image) {
  9990  	return (extensions.Image = Basic.extend({}, Image, {
  9991  
  9992  		getInfo: function() {
  9993  			var self = this.getRuntime()
  9994  			, grps = ['tiff', 'exif', 'gps']
  9995  			, info = { meta: {} }
  9996  			, rawInfo = self.shimExec.call(this, 'Image', 'getInfo')
  9997  			;
  9998  
  9999  			if (rawInfo.meta) {
 10000  				Basic.each(grps, function(grp) {
 10001  					var meta = rawInfo.meta[grp]
 10002  					, tag
 10003  					, i
 10004  					, length
 10005  					, value
 10006  					;
 10007  					if (meta && meta.keys) {
 10008  						info.meta[grp] = {};
 10009  						for (i = 0, length = meta.keys.length; i < length; i++) {
 10010  							tag = meta.keys[i];
 10011  							value = meta[tag];
 10012  							if (value) {
 10013  								// convert numbers
 10014  								if (/^(\d|[1-9]\d+)$/.test(value)) { // integer (make sure doesn't start with zero)
 10015  									value = parseInt(value, 10);
 10016  								} else if (/^\d*\.\d+$/.test(value)) { // double
 10017  									value = parseFloat(value);
 10018  								}
 10019  								info.meta[grp][tag] = value;
 10020  							}
 10021  						}
 10022  					}
 10023  				});
 10024  			}
 10025  
 10026  			info.width = parseInt(rawInfo.width, 10);
 10027  			info.height = parseInt(rawInfo.height, 10);
 10028  			info.size = parseInt(rawInfo.size, 10);
 10029  			info.type = rawInfo.type;
 10030  			info.name = rawInfo.name;
 10031  
 10032  			return info;
 10033  		}
 10034  	}));
 10035  });
 10036  
 10037  // Included from: src/javascript/runtime/html4/Runtime.js
 10038  
 10039  /**
 10040   * Runtime.js
 10041   *
 10042   * Copyright 2013, Moxiecode Systems AB
 10043   * Released under GPL License.
 10044   *
 10045   * License: http://www.plupload.com/license
 10046   * Contributing: http://www.plupload.com/contributing
 10047   */
 10048  
 10049  /*global File:true */
 10050  
 10051  /**
 10052  Defines constructor for HTML4 runtime.
 10053  
 10054  @class moxie/runtime/html4/Runtime
 10055  @private
 10056  */
 10057  define("moxie/runtime/html4/Runtime", [
 10058  	"moxie/core/utils/Basic",
 10059  	"moxie/core/Exceptions",
 10060  	"moxie/runtime/Runtime",
 10061  	"moxie/core/utils/Env"
 10062  ], function(Basic, x, Runtime, Env) {
 10063  	
 10064  	var type = 'html4', extensions = {};
 10065  
 10066  	function Html4Runtime(options) {
 10067  		var I = this
 10068  		, Test = Runtime.capTest
 10069  		, True = Runtime.capTrue
 10070  		;
 10071  
 10072  		Runtime.call(this, options, type, {
 10073  			access_binary: Test(window.FileReader || window.File && File.getAsDataURL),
 10074  			access_image_binary: false,
 10075  			display_media: Test(extensions.Image && (Env.can('create_canvas') || Env.can('use_data_uri_over32kb'))),
 10076  			do_cors: false,
 10077  			drag_and_drop: false,
 10078  			filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
 10079  				return (Env.browser === 'Chrome' && Env.version >= 28) || (Env.browser === 'IE' && Env.version >= 10);
 10080  			}()),
 10081  			resize_image: function() {
 10082  				return extensions.Image && I.can('access_binary') && Env.can('create_canvas');
 10083  			},
 10084  			report_upload_progress: false,
 10085  			return_response_headers: false,
 10086  			return_response_type: function(responseType) {
 10087  				if (responseType === 'json' && !!window.JSON) {
 10088  					return true;
 10089  				} 
 10090  				return !!~Basic.inArray(responseType, ['text', 'document', '']);
 10091  			},
 10092  			return_status_code: function(code) {
 10093  				return !Basic.arrayDiff(code, [200, 404]);
 10094  			},
 10095  			select_file: function() {
 10096  				return Env.can('use_fileinput');
 10097  			},
 10098  			select_multiple: false,
 10099  			send_binary_string: false,
 10100  			send_custom_headers: false,
 10101  			send_multipart: true,
 10102  			slice_blob: false,
 10103  			stream_upload: function() {
 10104  				return I.can('select_file');
 10105  			},
 10106  			summon_file_dialog: Test(function() { // yeah... some dirty sniffing here...
 10107  				return (Env.browser === 'Firefox' && Env.version >= 4) ||
 10108  					(Env.browser === 'Opera' && Env.version >= 12) ||
 10109  					!!~Basic.inArray(Env.browser, ['Chrome', 'Safari']);
 10110  			}()),
 10111  			upload_filesize: True,
 10112  			use_http_method: function(methods) {
 10113  				return !Basic.arrayDiff(methods, ['GET', 'POST']);
 10114  			}
 10115  		});
 10116  
 10117  
 10118  		Basic.extend(this, {
 10119  			init : function() {
 10120  				this.trigger("Init");
 10121  			},
 10122  
 10123  			destroy: (function(destroy) { // extend default destroy method
 10124  				return function() {
 10125  					destroy.call(I);
 10126  					destroy = I = null;
 10127  				};
 10128  			}(this.destroy))
 10129  		});
 10130  
 10131  		Basic.extend(this.getShim(), extensions);
 10132  	}
 10133  
 10134  	Runtime.addConstructor(type, Html4Runtime);
 10135  
 10136  	return extensions;
 10137  });
 10138  
 10139  // Included from: src/javascript/runtime/html4/file/FileInput.js
 10140  
 10141  /**
 10142   * FileInput.js
 10143   *
 10144   * Copyright 2013, Moxiecode Systems AB
 10145   * Released under GPL License.
 10146   *
 10147   * License: http://www.plupload.com/license
 10148   * Contributing: http://www.plupload.com/contributing
 10149   */
 10150  
 10151  /**
 10152  @class moxie/runtime/html4/file/FileInput
 10153  @private
 10154  */
 10155  define("moxie/runtime/html4/file/FileInput", [
 10156  	"moxie/runtime/html4/Runtime",
 10157  	"moxie/core/utils/Basic",
 10158  	"moxie/core/utils/Dom",
 10159  	"moxie/core/utils/Events",
 10160  	"moxie/core/utils/Mime",
 10161  	"moxie/core/utils/Env"
 10162  ], function(extensions, Basic, Dom, Events, Mime, Env) {
 10163  	
 10164  	function FileInput() {
 10165  		var _uid, _files = [], _mimes = [], _options;
 10166  
 10167  		function addInput() {
 10168  			var comp = this, I = comp.getRuntime(), shimContainer, browseButton, currForm, form, input, uid;
 10169  
 10170  			uid = Basic.guid('uid_');
 10171  
 10172  			shimContainer = I.getShimContainer(); // we get new ref everytime to avoid memory leaks in IE
 10173  
 10174  			if (_uid) { // move previous form out of the view
 10175  				currForm = Dom.get(_uid + '_form');
 10176  				if (currForm) {
 10177  					Basic.extend(currForm.style, { top: '100%' });
 10178  				}
 10179  			}
 10180  
 10181  			// build form in DOM, since innerHTML version not able to submit file for some reason
 10182  			form = document.createElement('form');
 10183  			form.setAttribute('id', uid + '_form');
 10184  			form.setAttribute('method', 'post');
 10185  			form.setAttribute('enctype', 'multipart/form-data');
 10186  			form.setAttribute('encoding', 'multipart/form-data');
 10187  
 10188  			Basic.extend(form.style, {
 10189  				overflow: 'hidden',
 10190  				position: 'absolute',
 10191  				top: 0,
 10192  				left: 0,
 10193  				width: '100%',
 10194  				height: '100%'
 10195  			});
 10196  
 10197  			input = document.createElement('input');
 10198  			input.setAttribute('id', uid);
 10199  			input.setAttribute('type', 'file');
 10200  			input.setAttribute('name', _options.name || 'Filedata');
 10201  			input.setAttribute('accept', _mimes.join(','));
 10202  
 10203  			Basic.extend(input.style, {
 10204  				fontSize: '999px',
 10205  				opacity: 0
 10206  			});
 10207  
 10208  			form.appendChild(input);
 10209  			shimContainer.appendChild(form);
 10210  
 10211  			// prepare file input to be placed underneath the browse_button element
 10212  			Basic.extend(input.style, {
 10213  				position: 'absolute',
 10214  				top: 0,
 10215  				left: 0,
 10216  				width: '100%',
 10217  				height: '100%'
 10218  			});
 10219  
 10220  			if (Env.browser === 'IE' && Env.version < 10) {
 10221  				Basic.extend(input.style, {
 10222  					filter : "progid:DXImageTransform.Microsoft.Alpha(opacity=0)"
 10223  				});
 10224  			}
 10225  
 10226  			input.onchange = function() { // there should be only one handler for this
 10227  				var file;
 10228  
 10229  				if (!this.value) {
 10230  					return;
 10231  				}
 10232  
 10233  				if (this.files) {
 10234  					file = this.files[0];
 10235  				} else {
 10236  					file = {
 10237  						name: this.value
 10238  					};
 10239  				}
 10240  
 10241  				_files = [file];
 10242  
 10243  				this.onchange = function() {}; // clear event handler
 10244  				addInput.call(comp);
 10245  
 10246  				// after file is initialized as o.File, we need to update form and input ids
 10247  				comp.bind('change', function onChange() {
 10248  					var input = Dom.get(uid), form = Dom.get(uid + '_form'), file;
 10249  
 10250  					comp.unbind('change', onChange);
 10251  
 10252  					if (comp.files.length && input && form) {
 10253  						file = comp.files[0];
 10254  
 10255  						input.setAttribute('id', file.uid);
 10256  						form.setAttribute('id', file.uid + '_form');
 10257  
 10258  						// set upload target
 10259  						form.setAttribute('target', file.uid + '_iframe');
 10260  					}
 10261  					input = form = null;
 10262  				}, 998);
 10263  
 10264  				input = form = null;
 10265  				comp.trigger('change');
 10266  			};
 10267  
 10268  
 10269  			// route click event to the input
 10270  			if (I.can('summon_file_dialog')) {
 10271  				browseButton = Dom.get(_options.browse_button);
 10272  				Events.removeEvent(browseButton, 'click', comp.uid);
 10273  				Events.addEvent(browseButton, 'click', function(e) {
 10274  					if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
 10275  						input.click();
 10276  					}
 10277  					e.preventDefault();
 10278  				}, comp.uid);
 10279  			}
 10280  
 10281  			_uid = uid;
 10282  
 10283  			shimContainer = currForm = browseButton = null;
 10284  		}
 10285  
 10286  		Basic.extend(this, {
 10287  			init: function(options) {
 10288  				var comp = this, I = comp.getRuntime(), shimContainer;
 10289  
 10290  				// figure out accept string
 10291  				_options = options;
 10292  				_mimes = options.accept.mimes || Mime.extList2mimes(options.accept, I.can('filter_by_extension'));
 10293  
 10294  				shimContainer = I.getShimContainer();
 10295  
 10296  				(function() {
 10297  					var browseButton, zIndex, top;
 10298  
 10299  					browseButton = Dom.get(options.browse_button);
 10300  
 10301  					// Route click event to the input[type=file] element for browsers that support such behavior
 10302  					if (I.can('summon_file_dialog')) {
 10303  						if (Dom.getStyle(browseButton, 'position') === 'static') {
 10304  							browseButton.style.position = 'relative';
 10305  						}
 10306  
 10307  						zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
 10308  
 10309  						browseButton.style.zIndex = zIndex;
 10310  						shimContainer.style.zIndex = zIndex - 1;
 10311  					}
 10312  
 10313  					/* Since we have to place input[type=file] on top of the browse_button for some browsers,
 10314  					browse_button loses interactivity, so we restore it here */
 10315  					top = I.can('summon_file_dialog') ? browseButton : shimContainer;
 10316  
 10317  					Events.addEvent(top, 'mouseover', function() {
 10318  						comp.trigger('mouseenter');
 10319  					}, comp.uid);
 10320  
 10321  					Events.addEvent(top, 'mouseout', function() {
 10322  						comp.trigger('mouseleave');
 10323  					}, comp.uid);
 10324  
 10325  					Events.addEvent(top, 'mousedown', function() {
 10326  						comp.trigger('mousedown');
 10327  					}, comp.uid);
 10328  
 10329  					Events.addEvent(Dom.get(options.container), 'mouseup', function() {
 10330  						comp.trigger('mouseup');
 10331  					}, comp.uid);
 10332  
 10333  					browseButton = null;
 10334  				}());
 10335  
 10336  				addInput.call(this);
 10337  
 10338  				shimContainer = null;
 10339  
 10340  				// trigger ready event asynchronously
 10341  				comp.trigger({
 10342  					type: 'ready',
 10343  					async: true
 10344  				});
 10345  			},
 10346  
 10347  			getFiles: function() {
 10348  				return _files;
 10349  			},
 10350  
 10351  			disable: function(state) {
 10352  				var input;
 10353  
 10354  				if ((input = Dom.get(_uid))) {
 10355  					input.disabled = !!state;
 10356  				}
 10357  			},
 10358  
 10359  			destroy: function() {
 10360  				var I = this.getRuntime()
 10361  				, shim = I.getShim()
 10362  				, shimContainer = I.getShimContainer()
 10363  				;
 10364  				
 10365  				Events.removeAllEvents(shimContainer, this.uid);
 10366  				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
 10367  				Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
 10368  				
 10369  				if (shimContainer) {
 10370  					shimContainer.innerHTML = '';
 10371  				}
 10372  
 10373  				shim.removeInstance(this.uid);
 10374  
 10375  				_uid = _files = _mimes = _options = shimContainer = shim = null;
 10376  			}
 10377  		});
 10378  	}
 10379  
 10380  	return (extensions.FileInput = FileInput);
 10381  });
 10382  
 10383  // Included from: src/javascript/runtime/html4/file/FileReader.js
 10384  
 10385  /**
 10386   * FileReader.js
 10387   *
 10388   * Copyright 2013, Moxiecode Systems AB
 10389   * Released under GPL License.
 10390   *
 10391   * License: http://www.plupload.com/license
 10392   * Contributing: http://www.plupload.com/contributing
 10393   */
 10394  
 10395  /**
 10396  @class moxie/runtime/html4/file/FileReader
 10397  @private
 10398  */
 10399  define("moxie/runtime/html4/file/FileReader", [
 10400  	"moxie/runtime/html4/Runtime",
 10401  	"moxie/runtime/html5/file/FileReader"
 10402  ], function(extensions, FileReader) {
 10403  	return (extensions.FileReader = FileReader);
 10404  });
 10405  
 10406  // Included from: src/javascript/runtime/html4/xhr/XMLHttpRequest.js
 10407  
 10408  /**
 10409   * XMLHttpRequest.js
 10410   *
 10411   * Copyright 2013, Moxiecode Systems AB
 10412   * Released under GPL License.
 10413   *
 10414   * License: http://www.plupload.com/license
 10415   * Contributing: http://www.plupload.com/contributing
 10416   */
 10417  
 10418  /**
 10419  @class moxie/runtime/html4/xhr/XMLHttpRequest
 10420  @private
 10421  */
 10422  define("moxie/runtime/html4/xhr/XMLHttpRequest", [
 10423  	"moxie/runtime/html4/Runtime",
 10424  	"moxie/core/utils/Basic",
 10425  	"moxie/core/utils/Dom",
 10426  	"moxie/core/utils/Url",
 10427  	"moxie/core/Exceptions",
 10428  	"moxie/core/utils/Events",
 10429  	"moxie/file/Blob",
 10430  	"moxie/xhr/FormData"
 10431  ], function(extensions, Basic, Dom, Url, x, Events, Blob, FormData) {
 10432  	
 10433  	function XMLHttpRequest() {
 10434  		var _status, _response, _iframe;
 10435  
 10436  		function cleanup(cb) {
 10437  			var target = this, uid, form, inputs, i, hasFile = false;
 10438  
 10439  			if (!_iframe) {
 10440  				return;
 10441  			}
 10442  
 10443  			uid = _iframe.id.replace(/_iframe$/, '');
 10444  
 10445  			form = Dom.get(uid + '_form');
 10446  			if (form) {
 10447  				inputs = form.getElementsByTagName('input');
 10448  				i = inputs.length;
 10449  
 10450  				while (i--) {
 10451  					switch (inputs[i].getAttribute('type')) {
 10452  						case 'hidden':
 10453  							inputs[i].parentNode.removeChild(inputs[i]);
 10454  							break;
 10455  						case 'file':
 10456  							hasFile = true; // flag the case for later
 10457  							break;
 10458  					}
 10459  				}
 10460  				inputs = [];
 10461  
 10462  				if (!hasFile) { // we need to keep the form for sake of possible retries
 10463  					form.parentNode.removeChild(form);
 10464  				}
 10465  				form = null;
 10466  			}
 10467  
 10468  			// without timeout, request is marked as canceled (in console)
 10469  			setTimeout(function() {
 10470  				Events.removeEvent(_iframe, 'load', target.uid);
 10471  				if (_iframe.parentNode) { // #382
 10472  					_iframe.parentNode.removeChild(_iframe);
 10473  				}
 10474  
 10475  				// check if shim container has any other children, if - not, remove it as well
 10476  				var shimContainer = target.getRuntime().getShimContainer();
 10477  				if (!shimContainer.children.length) {
 10478  					shimContainer.parentNode.removeChild(shimContainer);
 10479  				}
 10480  
 10481  				shimContainer = _iframe = null;
 10482  				cb();
 10483  			}, 1);
 10484  		}
 10485  
 10486  		Basic.extend(this, {
 10487  			send: function(meta, data) {
 10488  				var target = this, I = target.getRuntime(), uid, form, input, blob;
 10489  
 10490  				_status = _response = null;
 10491  
 10492  				function createIframe() {
 10493  					var container = I.getShimContainer() || document.body
 10494  					, temp = document.createElement('div')
 10495  					;
 10496  
 10497  					// IE 6 won't be able to set the name using setAttribute or iframe.name
 10498  					temp.innerHTML = '<iframe id="' + uid + '_iframe" name="' + uid + '_iframe" src="javascript:&quot;&quot;" style="display:none"></iframe>';
 10499  					_iframe = temp.firstChild;
 10500  					container.appendChild(_iframe);
 10501  
 10502  					/* _iframe.onreadystatechange = function() {
 10503  						console.info(_iframe.readyState);
 10504  					};*/
 10505  
 10506  					Events.addEvent(_iframe, 'load', function() { // _iframe.onload doesn't work in IE lte 8
 10507  						var el;
 10508  
 10509  						try {
 10510  							el = _iframe.contentWindow.document || _iframe.contentDocument || window.frames[_iframe.id].document;
 10511  
 10512  							// try to detect some standard error pages
 10513  							if (/^4(0[0-9]|1[0-7]|2[2346])\s/.test(el.title)) { // test if title starts with 4xx HTTP error
 10514  								_status = el.title.replace(/^(\d+).*$/, '$1');
 10515  							} else {
 10516  								_status = 200;
 10517  								// get result
 10518  								_response = Basic.trim(el.body.innerHTML);
 10519  
 10520  								// we need to fire these at least once
 10521  								target.trigger({
 10522  									type: 'progress',
 10523  									loaded: _response.length,
 10524  									total: _response.length
 10525  								});
 10526  
 10527  								if (blob) { // if we were uploading a file
 10528  									target.trigger({
 10529  										type: 'uploadprogress',
 10530  										loaded: blob.size || 1025,
 10531  										total: blob.size || 1025
 10532  									});
 10533  								}
 10534  							}
 10535  						} catch (ex) {
 10536  							if (Url.hasSameOrigin(meta.url)) {
 10537  								// if response is sent with error code, iframe in IE gets redirected to res://ieframe.dll/http_x.htm
 10538  								// which obviously results to cross domain error (wtf?)
 10539  								_status = 404;
 10540  							} else {
 10541  								cleanup.call(target, function() {
 10542  									target.trigger('error');
 10543  								});
 10544  								return;
 10545  							}
 10546  						}	
 10547  					
 10548  						cleanup.call(target, function() {
 10549  							target.trigger('load');
 10550  						});
 10551  					}, target.uid);
 10552  				} // end createIframe
 10553  
 10554  				// prepare data to be sent and convert if required
 10555  				if (data instanceof FormData && data.hasBlob()) {
 10556  					blob = data.getBlob();
 10557  					uid = blob.uid;
 10558  					input = Dom.get(uid);
 10559  					form = Dom.get(uid + '_form');
 10560  					if (!form) {
 10561  						throw new x.DOMException(x.DOMException.NOT_FOUND_ERR);
 10562  					}
 10563  				} else {
 10564  					uid = Basic.guid('uid_');
 10565  
 10566  					form = document.createElement('form');
 10567  					form.setAttribute('id', uid + '_form');
 10568  					form.setAttribute('method', meta.method);
 10569  					form.setAttribute('enctype', 'multipart/form-data');
 10570  					form.setAttribute('encoding', 'multipart/form-data');
 10571  					form.setAttribute('target', uid + '_iframe');
 10572  
 10573  					I.getShimContainer().appendChild(form);
 10574  				}
 10575  
 10576  				if (data instanceof FormData) {
 10577  					data.each(function(value, name) {
 10578  						if (value instanceof Blob) {
 10579  							if (input) {
 10580  								input.setAttribute('name', name);
 10581  							}
 10582  						} else {
 10583  							var hidden = document.createElement('input');
 10584  
 10585  							Basic.extend(hidden, {
 10586  								type : 'hidden',
 10587  								name : name,
 10588  								value : value
 10589  							});
 10590  
 10591  							// make sure that input[type="file"], if it's there, comes last
 10592  							if (input) {
 10593  								form.insertBefore(hidden, input);
 10594  							} else {
 10595  								form.appendChild(hidden);
 10596  							}
 10597  						}
 10598  					});
 10599  				}
 10600  
 10601  				// set destination url
 10602  				form.setAttribute("action", meta.url);
 10603  
 10604  				createIframe();
 10605  				form.submit();
 10606  				target.trigger('loadstart');
 10607  			},
 10608  
 10609  			getStatus: function() {
 10610  				return _status;
 10611  			},
 10612  
 10613  			getResponse: function(responseType) {
 10614  				if ('json' === responseType) {
 10615  					// strip off <pre>..</pre> tags that might be enclosing the response
 10616  					if (Basic.typeOf(_response) === 'string' && !!window.JSON) {
 10617  						try {
 10618  							return JSON.parse(_response.replace(/^\s*<pre[^>]*>/, '').replace(/<\/pre>\s*$/, ''));
 10619  						} catch (ex) {
 10620  							return null;
 10621  						}
 10622  					} 
 10623  				} else if ('document' === responseType) {
 10624  
 10625  				}
 10626  				return _response;
 10627  			},
 10628  
 10629  			abort: function() {
 10630  				var target = this;
 10631  
 10632  				if (_iframe && _iframe.contentWindow) {
 10633  					if (_iframe.contentWindow.stop) { // FireFox/Safari/Chrome
 10634  						_iframe.contentWindow.stop();
 10635  					} else if (_iframe.contentWindow.document.execCommand) { // IE
 10636  						_iframe.contentWindow.document.execCommand('Stop');
 10637  					} else {
 10638  						_iframe.src = "about:blank";
 10639  					}
 10640  				}
 10641  
 10642  				cleanup.call(this, function() {
 10643  					// target.dispatchEvent('readystatechange');
 10644  					target.dispatchEvent('abort');
 10645  				});
 10646  			}
 10647  		});
 10648  	}
 10649  
 10650  	return (extensions.XMLHttpRequest = XMLHttpRequest);
 10651  });
 10652  
 10653  // Included from: src/javascript/runtime/html4/image/Image.js
 10654  
 10655  /**
 10656   * Image.js
 10657   *
 10658   * Copyright 2013, Moxiecode Systems AB
 10659   * Released under GPL License.
 10660   *
 10661   * License: http://www.plupload.com/license
 10662   * Contributing: http://www.plupload.com/contributing
 10663   */
 10664  
 10665  /**
 10666  @class moxie/runtime/html4/image/Image
 10667  @private
 10668  */
 10669  define("moxie/runtime/html4/image/Image", [
 10670  	"moxie/runtime/html4/Runtime",
 10671  	"moxie/runtime/html5/image/Image"
 10672  ], function(extensions, Image) {
 10673  	return (extensions.Image = Image);
 10674  });
 10675  
 10676  expose(["moxie/core/utils/Basic","moxie/core/I18n","moxie/core/utils/Mime","moxie/core/utils/Env","moxie/core/utils/Dom","moxie/core/Exceptions","moxie/core/EventTarget","moxie/core/utils/Encode","moxie/runtime/Runtime","moxie/runtime/RuntimeClient","moxie/file/Blob","moxie/file/File","moxie/file/FileInput","moxie/file/FileDrop","moxie/runtime/RuntimeTarget","moxie/file/FileReader","moxie/core/utils/Url","moxie/file/FileReaderSync","moxie/xhr/FormData","moxie/xhr/XMLHttpRequest","moxie/runtime/Transporter","moxie/image/Image","moxie/core/utils/Events"]);
 10677  })(this);/**
 10678   * o.js
 10679   *
 10680   * Copyright 2013, Moxiecode Systems AB
 10681   * Released under GPL License.
 10682   *
 10683   * License: http://www.plupload.com/license
 10684   * Contributing: http://www.plupload.com/contributing
 10685   */
 10686  
 10687  /*global moxie:true */
 10688  
 10689  /**
 10690  Globally exposed namespace with the most frequently used public classes and handy methods.
 10691  
 10692  @class o
 10693  @static
 10694  @private
 10695  */
 10696  (function(exports) {
 10697  	"use strict";
 10698  
 10699  	var o = {}, inArray = exports.moxie.core.utils.Basic.inArray;
 10700  
 10701  	// directly add some public classes
 10702  	// (we do it dynamically here, since for custom builds we cannot know beforehand what modules were included)
 10703  	(function addAlias(ns) {
 10704  		var name, itemType;
 10705  		for (name in ns) {
 10706  			itemType = typeof(ns[name]);
 10707  			if (itemType === 'object' && !~inArray(name, ['Exceptions', 'Env', 'Mime'])) {
 10708  				addAlias(ns[name]);
 10709  			} else if (itemType === 'function') {
 10710  				o[name] = ns[name];
 10711  			}
 10712  		}
 10713  	})(exports.moxie);
 10714  
 10715  	// add some manually
 10716  	o.Env = exports.moxie.core.utils.Env;
 10717  	o.Mime = exports.moxie.core.utils.Mime;
 10718  	o.Exceptions = exports.moxie.core.Exceptions;
 10719  
 10720  	// expose globally
 10721  	exports.mOxie = o;
 10722  	if (!exports.o) {
 10723  		exports.o = o;
 10724  	}
 10725  	return o;
 10726  })(this);