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

     1  /**
     2   * mOxie - multi-runtime File API & XMLHttpRequest L2 Polyfill
     3   * v1.2.0
     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-01-16
    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 erro
   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', blobpool[this.uid]);
  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  				_xhr.destroy();
  5158  				_xhr = null;
  5159  				self.dispatchEvent('loadend');
  5160  				self = null;
  5161  			}
  5162  
  5163  			function exec(runtime) {
  5164  				_xhr.bind('LoadStart', function(e) {
  5165  					_p('readyState', XMLHttpRequest.LOADING);
  5166  					self.dispatchEvent('readystatechange');
  5167  
  5168  					self.dispatchEvent(e);
  5169  					
  5170  					if (_upload_events_flag) {
  5171  						self.upload.dispatchEvent(e);
  5172  					}
  5173  				});
  5174  				
  5175  				_xhr.bind('Progress', function(e) {
  5176  					if (_p('readyState') !== XMLHttpRequest.LOADING) {
  5177  						_p('readyState', XMLHttpRequest.LOADING); // LoadStart unreliable (in Flash for example)
  5178  						self.dispatchEvent('readystatechange');
  5179  					}
  5180  					self.dispatchEvent(e);
  5181  				});
  5182  				
  5183  				_xhr.bind('UploadProgress', function(e) {
  5184  					if (_upload_events_flag) {
  5185  						self.upload.dispatchEvent({
  5186  							type: 'progress',
  5187  							lengthComputable: false,
  5188  							total: e.total,
  5189  							loaded: e.loaded
  5190  						});
  5191  					}
  5192  				});
  5193  				
  5194  				_xhr.bind('Load', function(e) {
  5195  					_p('readyState', XMLHttpRequest.DONE);
  5196  					_p('status', Number(runtime.exec.call(_xhr, 'XMLHttpRequest', 'getStatus') || 0));
  5197  					_p('statusText', httpCode[_p('status')] || "");
  5198  					
  5199  					_p('response', runtime.exec.call(_xhr, 'XMLHttpRequest', 'getResponse', _p('responseType')));
  5200  
  5201  					if (!!~Basic.inArray(_p('responseType'), ['text', ''])) {
  5202  						_p('responseText', _p('response'));
  5203  					} else if (_p('responseType') === 'document') {
  5204  						_p('responseXML', _p('response'));
  5205  					}
  5206  
  5207  					_responseHeaders = runtime.exec.call(_xhr, 'XMLHttpRequest', 'getAllResponseHeaders');
  5208  
  5209  					self.dispatchEvent('readystatechange');
  5210  					
  5211  					if (_p('status') > 0) { // status 0 usually means that server is unreachable
  5212  						if (_upload_events_flag) {
  5213  							self.upload.dispatchEvent(e);
  5214  						}
  5215  						self.dispatchEvent(e);
  5216  					} else {
  5217  						_error_flag = true;
  5218  						self.dispatchEvent('error');
  5219  					}
  5220  					loadEnd();
  5221  				});
  5222  
  5223  				_xhr.bind('Abort', function(e) {
  5224  					self.dispatchEvent(e);
  5225  					loadEnd();
  5226  				});
  5227  				
  5228  				_xhr.bind('Error', function(e) {
  5229  					_error_flag = true;
  5230  					_p('readyState', XMLHttpRequest.DONE);
  5231  					self.dispatchEvent('readystatechange');
  5232  					_upload_complete_flag = true;
  5233  					self.dispatchEvent(e);
  5234  					loadEnd();
  5235  				});
  5236  
  5237  				runtime.exec.call(_xhr, 'XMLHttpRequest', 'send', {
  5238  					url: _url,
  5239  					method: _method,
  5240  					async: _async,
  5241  					user: _user,
  5242  					password: _password,
  5243  					headers: _headers,
  5244  					mimeType: _mimeType,
  5245  					encoding: _encoding,
  5246  					responseType: self.responseType,
  5247  					withCredentials: self.withCredentials,
  5248  					options: _options
  5249  				}, data);
  5250  			}
  5251  
  5252  			// clarify our requirements
  5253  			if (typeof(_options.required_caps) === 'string') {
  5254  				_options.required_caps = Runtime.parseCaps(_options.required_caps);
  5255  			}
  5256  
  5257  			_options.required_caps = Basic.extend({}, _options.required_caps, {
  5258  				return_response_type: self.responseType
  5259  			});
  5260  
  5261  			if (data instanceof FormData) {
  5262  				_options.required_caps.send_multipart = true;
  5263  			}
  5264  
  5265  			if (!_same_origin_flag) {
  5266  				_options.required_caps.do_cors = true;
  5267  			}
  5268  			
  5269  
  5270  			if (_options.ruid) { // we do not need to wait if we can connect directly
  5271  				exec(_xhr.connectRuntime(_options));
  5272  			} else {
  5273  				_xhr.bind('RuntimeInit', function(e, runtime) {
  5274  					exec(runtime);
  5275  				});
  5276  				_xhr.bind('RuntimeError', function(e, err) {
  5277  					self.dispatchEvent('RuntimeError', err);
  5278  				});
  5279  				_xhr.connectRuntime(_options);
  5280  			}
  5281  		}
  5282  	
  5283  		
  5284  		function _reset() {
  5285  			_p('responseText', "");
  5286  			_p('responseXML', null);
  5287  			_p('response', null);
  5288  			_p('status', 0);
  5289  			_p('statusText', "");
  5290  			_start_time = _timeoutset_time = null;
  5291  		}
  5292  	}
  5293  
  5294  	XMLHttpRequest.UNSENT = 0;
  5295  	XMLHttpRequest.OPENED = 1;
  5296  	XMLHttpRequest.HEADERS_RECEIVED = 2;
  5297  	XMLHttpRequest.LOADING = 3;
  5298  	XMLHttpRequest.DONE = 4;
  5299  	
  5300  	XMLHttpRequest.prototype = EventTarget.instance;
  5301  
  5302  	return XMLHttpRequest;
  5303  });
  5304  
  5305  // Included from: src/javascript/runtime/Transporter.js
  5306  
  5307  /**
  5308   * Transporter.js
  5309   *
  5310   * Copyright 2013, Moxiecode Systems AB
  5311   * Released under GPL License.
  5312   *
  5313   * License: http://www.plupload.com/license
  5314   * Contributing: http://www.plupload.com/contributing
  5315   */
  5316  
  5317  define("moxie/runtime/Transporter", [
  5318  	"moxie/core/utils/Basic",
  5319  	"moxie/core/utils/Encode",
  5320  	"moxie/runtime/RuntimeClient",
  5321  	"moxie/core/EventTarget"
  5322  ], function(Basic, Encode, RuntimeClient, EventTarget) {
  5323  	function Transporter() {
  5324  		var mod, _runtime, _data, _size, _pos, _chunk_size;
  5325  
  5326  		RuntimeClient.call(this);
  5327  
  5328  		Basic.extend(this, {
  5329  			uid: Basic.guid('uid_'),
  5330  
  5331  			state: Transporter.IDLE,
  5332  
  5333  			result: null,
  5334  
  5335  			transport: function(data, type, options) {
  5336  				var self = this;
  5337  
  5338  				options = Basic.extend({
  5339  					chunk_size: 204798
  5340  				}, options);
  5341  
  5342  				// should divide by three, base64 requires this
  5343  				if ((mod = options.chunk_size % 3)) {
  5344  					options.chunk_size += 3 - mod;
  5345  				}
  5346  
  5347  				_chunk_size = options.chunk_size;
  5348  
  5349  				_reset.call(this);
  5350  				_data = data;
  5351  				_size = data.length;
  5352  
  5353  				if (Basic.typeOf(options) === 'string' || options.ruid) {
  5354  					_run.call(self, type, this.connectRuntime(options));
  5355  				} else {
  5356  					// we require this to run only once
  5357  					var cb = function(e, runtime) {
  5358  						self.unbind("RuntimeInit", cb);
  5359  						_run.call(self, type, runtime);
  5360  					};
  5361  					this.bind("RuntimeInit", cb);
  5362  					this.connectRuntime(options);
  5363  				}
  5364  			},
  5365  
  5366  			abort: function() {
  5367  				var self = this;
  5368  
  5369  				self.state = Transporter.IDLE;
  5370  				if (_runtime) {
  5371  					_runtime.exec.call(self, 'Transporter', 'clear');
  5372  					self.trigger("TransportingAborted");
  5373  				}
  5374  
  5375  				_reset.call(self);
  5376  			},
  5377  
  5378  
  5379  			destroy: function() {
  5380  				this.unbindAll();
  5381  				_runtime = null;
  5382  				this.disconnectRuntime();
  5383  				_reset.call(this);
  5384  			}
  5385  		});
  5386  
  5387  		function _reset() {
  5388  			_size = _pos = 0;
  5389  			_data = this.result = null;
  5390  		}
  5391  
  5392  		function _run(type, runtime) {
  5393  			var self = this;
  5394  
  5395  			_runtime = runtime;
  5396  
  5397  			//self.unbind("RuntimeInit");
  5398  
  5399  			self.bind("TransportingProgress", function(e) {
  5400  				_pos = e.loaded;
  5401  
  5402  				if (_pos < _size && Basic.inArray(self.state, [Transporter.IDLE, Transporter.DONE]) === -1) {
  5403  					_transport.call(self);
  5404  				}
  5405  			}, 999);
  5406  
  5407  			self.bind("TransportingComplete", function() {
  5408  				_pos = _size;
  5409  				self.state = Transporter.DONE;
  5410  				_data = null; // clean a bit
  5411  				self.result = _runtime.exec.call(self, 'Transporter', 'getAsBlob', type || '');
  5412  			}, 999);
  5413  
  5414  			self.state = Transporter.BUSY;
  5415  			self.trigger("TransportingStarted");
  5416  			_transport.call(self);
  5417  		}
  5418  
  5419  		function _transport() {
  5420  			var self = this,
  5421  				chunk,
  5422  				bytesLeft = _size - _pos;
  5423  
  5424  			if (_chunk_size > bytesLeft) {
  5425  				_chunk_size = bytesLeft;
  5426  			}
  5427  
  5428  			chunk = Encode.btoa(_data.substr(_pos, _chunk_size));
  5429  			_runtime.exec.call(self, 'Transporter', 'receive', chunk, _size);
  5430  		}
  5431  	}
  5432  
  5433  	Transporter.IDLE = 0;
  5434  	Transporter.BUSY = 1;
  5435  	Transporter.DONE = 2;
  5436  
  5437  	Transporter.prototype = EventTarget.instance;
  5438  
  5439  	return Transporter;
  5440  });
  5441  
  5442  // Included from: src/javascript/image/Image.js
  5443  
  5444  /**
  5445   * Image.js
  5446   *
  5447   * Copyright 2013, Moxiecode Systems AB
  5448   * Released under GPL License.
  5449   *
  5450   * License: http://www.plupload.com/license
  5451   * Contributing: http://www.plupload.com/contributing
  5452   */
  5453  
  5454  define("moxie/image/Image", [
  5455  	"moxie/core/utils/Basic",
  5456  	"moxie/core/utils/Dom",
  5457  	"moxie/core/Exceptions",
  5458  	"moxie/file/FileReaderSync",
  5459  	"moxie/xhr/XMLHttpRequest",
  5460  	"moxie/runtime/Runtime",
  5461  	"moxie/runtime/RuntimeClient",
  5462  	"moxie/runtime/Transporter",
  5463  	"moxie/core/utils/Env",
  5464  	"moxie/core/EventTarget",
  5465  	"moxie/file/Blob",
  5466  	"moxie/file/File",
  5467  	"moxie/core/utils/Encode"
  5468  ], function(Basic, Dom, x, FileReaderSync, XMLHttpRequest, Runtime, RuntimeClient, Transporter, Env, EventTarget, Blob, File, Encode) {
  5469  	/**
  5470  	Image preloading and manipulation utility. Additionally it provides access to image meta info (Exif, GPS) and raw binary data.
  5471  
  5472  	@class Image
  5473  	@constructor
  5474  	@extends EventTarget
  5475  	*/
  5476  	var dispatches = [
  5477  		'progress',
  5478  
  5479  		/**
  5480  		Dispatched when loading is complete.
  5481  
  5482  		@event load
  5483  		@param {Object} event
  5484  		*/
  5485  		'load',
  5486  
  5487  		'error',
  5488  
  5489  		/**
  5490  		Dispatched when resize operation is complete.
  5491  		
  5492  		@event resize
  5493  		@param {Object} event
  5494  		*/
  5495  		'resize',
  5496  
  5497  		/**
  5498  		Dispatched when visual representation of the image is successfully embedded
  5499  		into the corresponsing container.
  5500  
  5501  		@event embedded
  5502  		@param {Object} event
  5503  		*/
  5504  		'embedded'
  5505  	];
  5506  
  5507  	function Image() {
  5508  		RuntimeClient.call(this);
  5509  
  5510  		Basic.extend(this, {
  5511  			/**
  5512  			Unique id of the component
  5513  
  5514  			@property uid
  5515  			@type {String}
  5516  			*/
  5517  			uid: Basic.guid('uid_'),
  5518  
  5519  			/**
  5520  			Unique id of the connected runtime, if any.
  5521  
  5522  			@property ruid
  5523  			@type {String}
  5524  			*/
  5525  			ruid: null,
  5526  
  5527  			/**
  5528  			Name of the file, that was used to create an image, if available. If not equals to empty string.
  5529  
  5530  			@property name
  5531  			@type {String}
  5532  			@default ""
  5533  			*/
  5534  			name: "",
  5535  
  5536  			/**
  5537  			Size of the image in bytes. Actual value is set only after image is preloaded.
  5538  
  5539  			@property size
  5540  			@type {Number}
  5541  			@default 0
  5542  			*/
  5543  			size: 0,
  5544  
  5545  			/**
  5546  			Width of the image. Actual value is set only after image is preloaded.
  5547  
  5548  			@property width
  5549  			@type {Number}
  5550  			@default 0
  5551  			*/
  5552  			width: 0,
  5553  
  5554  			/**
  5555  			Height of the image. Actual value is set only after image is preloaded.
  5556  
  5557  			@property height
  5558  			@type {Number}
  5559  			@default 0
  5560  			*/
  5561  			height: 0,
  5562  
  5563  			/**
  5564  			Mime type of the image. Currently only image/jpeg and image/png are supported. Actual value is set only after image is preloaded.
  5565  
  5566  			@property type
  5567  			@type {String}
  5568  			@default ""
  5569  			*/
  5570  			type: "",
  5571  
  5572  			/**
  5573  			Holds meta info (Exif, GPS). Is populated only for image/jpeg. Actual value is set only after image is preloaded.
  5574  
  5575  			@property meta
  5576  			@type {Object}
  5577  			@default {}
  5578  			*/
  5579  			meta: {},
  5580  
  5581  			/**
  5582  			Alias for load method, that takes another mOxie.Image object as a source (see load).
  5583  
  5584  			@method clone
  5585  			@param {Image} src Source for the image
  5586  			@param {Boolean} [exact=false] Whether to activate in-depth clone mode
  5587  			*/
  5588  			clone: function() {
  5589  				this.load.apply(this, arguments);
  5590  			},
  5591  
  5592  			/**
  5593  			Loads image from various sources. Currently the source for new image can be: mOxie.Image, mOxie.Blob/mOxie.File, 
  5594  			native Blob/File, dataUrl or URL. Depending on the type of the source, arguments - differ. When source is URL, 
  5595  			Image will be downloaded from remote destination and loaded in memory.
  5596  
  5597  			@example
  5598  				var img = new mOxie.Image();
  5599  				img.onload = function() {
  5600  					var blob = img.getAsBlob();
  5601  					
  5602  					var formData = new mOxie.FormData();
  5603  					formData.append('file', blob);
  5604  
  5605  					var xhr = new mOxie.XMLHttpRequest();
  5606  					xhr.onload = function() {
  5607  						// upload complete
  5608  					};
  5609  					xhr.open('post', 'upload.php');
  5610  					xhr.send(formData);
  5611  				};
  5612  				img.load("http://www.moxiecode.com/images/mox-logo.jpg"); // notice file extension (.jpg)
  5613  			
  5614  
  5615  			@method load
  5616  			@param {Image|Blob|File|String} src Source for the image
  5617  			@param {Boolean|Object} [mixed]
  5618  			*/
  5619  			load: function() {
  5620  				// this is here because to bind properly we need an uid first, which is created above
  5621  				this.bind('Load Resize', function() {
  5622  					_updateInfo.call(this);
  5623  				}, 999);
  5624  
  5625  				this.convertEventPropsToHandlers(dispatches);
  5626  
  5627  				_load.apply(this, arguments);
  5628  			},
  5629  
  5630  			/**
  5631  			Downsizes the image to fit the specified width/height. If crop is supplied, image will be cropped to exact dimensions.
  5632  
  5633  			@method downsize
  5634  			@param {Number} width Resulting width
  5635  			@param {Number} [height=width] Resulting height (optional, if not supplied will default to width)
  5636  			@param {Boolean} [crop=false] Whether to crop the image to exact dimensions
  5637  			@param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
  5638  			*/
  5639  			downsize: function(width, height, crop, preserveHeaders) {
  5640  				try {
  5641  					if (!this.size) { // only preloaded image objects can be used as source
  5642  						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5643  					}
  5644  
  5645  					// no way to reliably intercept the crash due to high resolution, so we simply avoid it
  5646  					if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
  5647  						throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
  5648  					}
  5649  
  5650  					if (!width && !height || Basic.typeOf(crop) === 'undefined') {
  5651  						crop = false;
  5652  					}
  5653  
  5654  					width = width || this.width;
  5655  					height = height || this.height;
  5656  
  5657  					preserveHeaders = (Basic.typeOf(preserveHeaders) === 'undefined' ? true : !!preserveHeaders);
  5658  
  5659  					this.getRuntime().exec.call(this, 'Image', 'downsize', width, height, crop, preserveHeaders);
  5660  				} catch(ex) {
  5661  					// for now simply trigger error event
  5662  					this.trigger('error', ex);
  5663  				}
  5664  			},
  5665  
  5666  			/**
  5667  			Alias for downsize(width, height, true). (see downsize)
  5668  			
  5669  			@method crop
  5670  			@param {Number} width Resulting width
  5671  			@param {Number} [height=width] Resulting height (optional, if not supplied will default to width)
  5672  			@param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
  5673  			*/
  5674  			crop: function(width, height, preserveHeaders) {
  5675  				this.downsize(width, height, true, preserveHeaders);
  5676  			},
  5677  
  5678  			getAsCanvas: function() {
  5679  				if (!Env.can('create_canvas')) {
  5680  					throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  5681  				}
  5682  
  5683  				var runtime = this.connectRuntime(this.ruid);
  5684  				return runtime.exec.call(this, 'Image', 'getAsCanvas');
  5685  			},
  5686  
  5687  			/**
  5688  			Retrieves image in it's current state as mOxie.Blob object. Cannot be run on empty or image in progress (throws
  5689  			DOMException.INVALID_STATE_ERR).
  5690  
  5691  			@method getAsBlob
  5692  			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  5693  			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
  5694  			@return {Blob} Image as Blob
  5695  			*/
  5696  			getAsBlob: function(type, quality) {
  5697  				if (!this.size) {
  5698  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5699  				}
  5700  
  5701  				if (!type) {
  5702  					type = 'image/jpeg';
  5703  				}
  5704  
  5705  				if (type === 'image/jpeg' && !quality) {
  5706  					quality = 90;
  5707  				}
  5708  
  5709  				return this.getRuntime().exec.call(this, 'Image', 'getAsBlob', type, quality);
  5710  			},
  5711  
  5712  			/**
  5713  			Retrieves image in it's current state as dataURL string. Cannot be run on empty or image in progress (throws
  5714  			DOMException.INVALID_STATE_ERR).
  5715  
  5716  			@method getAsDataURL
  5717  			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  5718  			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
  5719  			@return {String} Image as dataURL string
  5720  			*/
  5721  			getAsDataURL: function(type, quality) {
  5722  				if (!this.size) {
  5723  					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5724  				}
  5725  				return this.getRuntime().exec.call(this, 'Image', 'getAsDataURL', type, quality);
  5726  			},
  5727  
  5728  			/**
  5729  			Retrieves image in it's current state as binary string. Cannot be run on empty or image in progress (throws
  5730  			DOMException.INVALID_STATE_ERR).
  5731  
  5732  			@method getAsBinaryString
  5733  			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  5734  			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
  5735  			@return {String} Image as binary string
  5736  			*/
  5737  			getAsBinaryString: function(type, quality) {
  5738  				var dataUrl = this.getAsDataURL(type, quality);
  5739  				return Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7));
  5740  			},
  5741  
  5742  			/**
  5743  			Embeds the image, or better to say, it's visual representation into the specified node. Depending on the runtime
  5744  			in use, might be a canvas, or image (actual ) element or shim object (Flash or SilverLight - very rare, used for
  5745  			legacy browsers that do not have canvas or proper dataURI support).
  5746  
  5747  			@method embed
  5748  			@param {DOMElement} el DOM element to insert the image object into
  5749  			@param {Object} options Set of key/value pairs controlling the mime type, dimensions and cropping factor of resulting
  5750  			representation
  5751  			*/
  5752  			embed: function(el) {
  5753  				var self = this
  5754  				, imgCopy
  5755  				, type, quality, crop
  5756  				, options = arguments[1] || {}
  5757  				, width = this.width
  5758  				, height = this.height
  5759  				, runtime // this has to be outside of all the closures to contain proper runtime
  5760  				;
  5761  
  5762  				function onResize() {
  5763  					// if possible, embed a canvas element directly
  5764  					if (Env.can('create_canvas')) {
  5765  						var canvas = imgCopy.getAsCanvas();
  5766  						if (canvas) {
  5767  							el.appendChild(canvas);
  5768  							canvas = null;
  5769  							imgCopy.destroy();
  5770  							self.trigger('embedded');
  5771  							return;
  5772  						}
  5773  					}
  5774  
  5775  					var dataUrl = imgCopy.getAsDataURL(type, quality);
  5776  					if (!dataUrl) {
  5777  						throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  5778  					}
  5779  
  5780  					if (Env.can('use_data_uri_of', dataUrl.length)) {
  5781  						el.innerHTML = '<img src="' + dataUrl + '" width="' + imgCopy.width + '" height="' + imgCopy.height + '" />';
  5782  						imgCopy.destroy();
  5783  						self.trigger('embedded');
  5784  					} else {
  5785  						var tr = new Transporter();
  5786  
  5787  						tr.bind("TransportingComplete", function() {
  5788  							runtime = self.connectRuntime(this.result.ruid);
  5789  
  5790  							self.bind("Embedded", function() {
  5791  								// position and size properly
  5792  								Basic.extend(runtime.getShimContainer().style, {
  5793  									//position: 'relative',
  5794  									top: '0px',
  5795  									left: '0px',
  5796  									width: imgCopy.width + 'px',
  5797  									height: imgCopy.height + 'px'
  5798  								});
  5799  
  5800  								// some shims (Flash/SilverLight) reinitialize, if parent element is hidden, reordered or it's
  5801  								// position type changes (in Gecko), but since we basically need this only in IEs 6/7 and
  5802  								// sometimes 8 and they do not have this problem, we can comment this for now
  5803  								/*tr.bind("RuntimeInit", function(e, runtime) {
  5804  									tr.destroy();
  5805  									runtime.destroy();
  5806  									onResize.call(self); // re-feed our image data
  5807  								});*/
  5808  
  5809  								runtime = null;
  5810  							}, 999);
  5811  
  5812  							runtime.exec.call(self, "ImageView", "display", this.result.uid, width, height);
  5813  							imgCopy.destroy();
  5814  						});
  5815  
  5816  						tr.transport(Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7)), type, Basic.extend({}, options, {
  5817  							required_caps: {
  5818  								display_media: true
  5819  							},
  5820  							runtime_order: 'flash,silverlight',
  5821  							container: el
  5822  						}));
  5823  					}
  5824  				}
  5825  
  5826  				try {
  5827  					if (!(el = Dom.get(el))) {
  5828  						throw new x.DOMException(x.DOMException.INVALID_NODE_TYPE_ERR);
  5829  					}
  5830  
  5831  					if (!this.size) { // only preloaded image objects can be used as source
  5832  						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5833  					}
  5834  
  5835  					if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
  5836  						throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
  5837  					}
  5838  
  5839  					type = options.type || this.type || 'image/jpeg';
  5840  					quality = options.quality || 90;
  5841  					crop = Basic.typeOf(options.crop) !== 'undefined' ? options.crop : false;
  5842  
  5843  					// figure out dimensions for the thumb
  5844  					if (options.width) {
  5845  						width = options.width;
  5846  						height = options.height || width;
  5847  					} else {
  5848  						// if container element has > 0 dimensions, take them
  5849  						var dimensions = Dom.getSize(el);
  5850  						if (dimensions.w && dimensions.h) { // both should be > 0
  5851  							width = dimensions.w;
  5852  							height = dimensions.h;
  5853  						}
  5854  					}
  5855  
  5856  					imgCopy = new Image();
  5857  
  5858  					imgCopy.bind("Resize", function() {
  5859  						onResize.call(self);
  5860  					});
  5861  
  5862  					imgCopy.bind("Load", function() {
  5863  						imgCopy.downsize(width, height, crop, false);
  5864  					});
  5865  
  5866  					imgCopy.clone(this, false);
  5867  
  5868  					return imgCopy;
  5869  				} catch(ex) {
  5870  					// for now simply trigger error event
  5871  					this.trigger('error', ex);
  5872  				}
  5873  			},
  5874  
  5875  			/**
  5876  			Properly destroys the image and frees resources in use. If any. Recommended way to dispose mOxie.Image object.
  5877  
  5878  			@method destroy
  5879  			*/
  5880  			destroy: function() {
  5881  				if (this.ruid) {
  5882  					this.getRuntime().exec.call(this, 'Image', 'destroy');
  5883  					this.disconnectRuntime();
  5884  				}
  5885  				this.unbindAll();
  5886  			}
  5887  		});
  5888  
  5889  
  5890  		function _updateInfo(info) {
  5891  			if (!info) {
  5892  				info = this.getRuntime().exec.call(this, 'Image', 'getInfo');
  5893  			}
  5894  
  5895  			this.size = info.size;
  5896  			this.width = info.width;
  5897  			this.height = info.height;
  5898  			this.type = info.type;
  5899  			this.meta = info.meta;
  5900  
  5901  			// update file name, only if empty
  5902  			if (this.name === '') {
  5903  				this.name = info.name;
  5904  			}
  5905  		}
  5906  		
  5907  
  5908  		function _load(src) {
  5909  			var srcType = Basic.typeOf(src);
  5910  
  5911  			try {
  5912  				// if source is Image
  5913  				if (src instanceof Image) {
  5914  					if (!src.size) { // only preloaded image objects can be used as source
  5915  						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  5916  					}
  5917  					_loadFromImage.apply(this, arguments);
  5918  				}
  5919  				// if source is o.Blob/o.File
  5920  				else if (src instanceof Blob) {
  5921  					if (!~Basic.inArray(src.type, ['image/jpeg', 'image/png'])) {
  5922  						throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  5923  					}
  5924  					_loadFromBlob.apply(this, arguments);
  5925  				}
  5926  				// if native blob/file
  5927  				else if (Basic.inArray(srcType, ['blob', 'file']) !== -1) {
  5928  					_load.call(this, new File(null, src), arguments[1]);
  5929  				}
  5930  				// if String
  5931  				else if (srcType === 'string') {
  5932  					// if dataUrl String
  5933  					if (/^data:[^;]*;base64,/.test(src)) {
  5934  						_load.call(this, new Blob(null, { data: src }), arguments[1]);
  5935  					}
  5936  					// else assume Url, either relative or absolute
  5937  					else {
  5938  						_loadFromUrl.apply(this, arguments);
  5939  					}
  5940  				}
  5941  				// if source seems to be an img node
  5942  				else if (srcType === 'node' && src.nodeName.toLowerCase() === 'img') {
  5943  					_load.call(this, src.src, arguments[1]);
  5944  				}
  5945  				else {
  5946  					throw new x.DOMException(x.DOMException.TYPE_MISMATCH_ERR);
  5947  				}
  5948  			} catch(ex) {
  5949  				// for now simply trigger error event
  5950  				this.trigger('error', ex);
  5951  			}
  5952  		}
  5953  
  5954  
  5955  		function _loadFromImage(img, exact) {
  5956  			var runtime = this.connectRuntime(img.ruid);
  5957  			this.ruid = runtime.uid;
  5958  			runtime.exec.call(this, 'Image', 'loadFromImage', img, (Basic.typeOf(exact) === 'undefined' ? true : exact));
  5959  		}
  5960  
  5961  
  5962  		function _loadFromBlob(blob, options) {
  5963  			var self = this;
  5964  
  5965  			self.name = blob.name || '';
  5966  
  5967  			function exec(runtime) {
  5968  				self.ruid = runtime.uid;
  5969  				runtime.exec.call(self, 'Image', 'loadFromBlob', blob);
  5970  			}
  5971  
  5972  			if (blob.isDetached()) {
  5973  				this.bind('RuntimeInit', function(e, runtime) {
  5974  					exec(runtime);
  5975  				});
  5976  
  5977  				// convert to object representation
  5978  				if (options && typeof(options.required_caps) === 'string') {
  5979  					options.required_caps = Runtime.parseCaps(options.required_caps);
  5980  				}
  5981  
  5982  				this.connectRuntime(Basic.extend({
  5983  					required_caps: {
  5984  						access_image_binary: true,
  5985  						resize_image: true
  5986  					}
  5987  				}, options));
  5988  			} else {
  5989  				exec(this.connectRuntime(blob.ruid));
  5990  			}
  5991  		}
  5992  
  5993  
  5994  		function _loadFromUrl(url, options) {
  5995  			var self = this, xhr;
  5996  
  5997  			xhr = new XMLHttpRequest();
  5998  
  5999  			xhr.open('get', url);
  6000  			xhr.responseType = 'blob';
  6001  
  6002  			xhr.onprogress = function(e) {
  6003  				self.trigger(e);
  6004  			};
  6005  
  6006  			xhr.onload = function() {
  6007  				_loadFromBlob.call(self, xhr.response, true);
  6008  			};
  6009  
  6010  			xhr.onerror = function(e) {
  6011  				self.trigger(e);
  6012  			};
  6013  
  6014  			xhr.onloadend = function() {
  6015  				xhr.destroy();
  6016  			};
  6017  
  6018  			xhr.bind('RuntimeError', function(e, err) {
  6019  				self.trigger('RuntimeError', err);
  6020  			});
  6021  
  6022  			xhr.send(null, options);
  6023  		}
  6024  	}
  6025  
  6026  	// virtual world will crash on you if image has a resolution higher than this:
  6027  	Image.MAX_RESIZE_WIDTH = 6500;
  6028  	Image.MAX_RESIZE_HEIGHT = 6500; 
  6029  
  6030  	Image.prototype = EventTarget.instance;
  6031  
  6032  	return Image;
  6033  });
  6034  
  6035  // Included from: src/javascript/runtime/html5/Runtime.js
  6036  
  6037  /**
  6038   * Runtime.js
  6039   *
  6040   * Copyright 2013, Moxiecode Systems AB
  6041   * Released under GPL License.
  6042   *
  6043   * License: http://www.plupload.com/license
  6044   * Contributing: http://www.plupload.com/contributing
  6045   */
  6046  
  6047  /*global File:true */
  6048  
  6049  /**
  6050  Defines constructor for HTML5 runtime.
  6051  
  6052  @class moxie/runtime/html5/Runtime
  6053  @private
  6054  */
  6055  define("moxie/runtime/html5/Runtime", [
  6056  	"moxie/core/utils/Basic",
  6057  	"moxie/core/Exceptions",
  6058  	"moxie/runtime/Runtime",
  6059  	"moxie/core/utils/Env"
  6060  ], function(Basic, x, Runtime, Env) {
  6061  	
  6062  	var type = "html5", extensions = {};
  6063  	
  6064  	function Html5Runtime(options) {
  6065  		var I = this
  6066  		, Test = Runtime.capTest
  6067  		, True = Runtime.capTrue
  6068  		;
  6069  
  6070  		var caps = Basic.extend({
  6071  				access_binary: Test(window.FileReader || window.File && window.File.getAsDataURL),
  6072  				access_image_binary: function() {
  6073  					return I.can('access_binary') && !!extensions.Image;
  6074  				},
  6075  				display_media: Test(Env.can('create_canvas') || Env.can('use_data_uri_over32kb')),
  6076  				do_cors: Test(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()),
  6077  				drag_and_drop: Test(function() {
  6078  					// this comes directly from Modernizr: http://www.modernizr.com/
  6079  					var div = document.createElement('div');
  6080  					// IE has support for drag and drop since version 5, but doesn't support dropping files from desktop
  6081  					return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && (Env.browser !== 'IE' || Env.version > 9);
  6082  				}()),
  6083  				filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
  6084  					return (Env.browser === 'Chrome' && Env.version >= 28) || (Env.browser === 'IE' && Env.version >= 10);
  6085  				}()),
  6086  				return_response_headers: True,
  6087  				return_response_type: function(responseType) {
  6088  					if (responseType === 'json' && !!window.JSON) { // we can fake this one even if it's not supported
  6089  						return true;
  6090  					} 
  6091  					return Env.can('return_response_type', responseType);
  6092  				},
  6093  				return_status_code: True,
  6094  				report_upload_progress: Test(window.XMLHttpRequest && new XMLHttpRequest().upload),
  6095  				resize_image: function() {
  6096  					return I.can('access_binary') && Env.can('create_canvas');
  6097  				},
  6098  				select_file: function() {
  6099  					return Env.can('use_fileinput') && window.File;
  6100  				},
  6101  				select_folder: function() {
  6102  					return I.can('select_file') && Env.browser === 'Chrome' && Env.version >= 21;
  6103  				},
  6104  				select_multiple: function() {
  6105  					// it is buggy on Safari Windows and iOS
  6106  					return I.can('select_file') && 
  6107  						!(Env.browser === 'Safari' && Env.os === 'Windows') && 
  6108  						!(Env.os === 'iOS' && Env.verComp(Env.osVersion, "7.0.4", '<'));
  6109  				},
  6110  				send_binary_string: Test(window.XMLHttpRequest && (new XMLHttpRequest().sendAsBinary || (window.Uint8Array && window.ArrayBuffer))),
  6111  				send_custom_headers: Test(window.XMLHttpRequest),
  6112  				send_multipart: function() {
  6113  					return !!(window.XMLHttpRequest && new XMLHttpRequest().upload && window.FormData) || I.can('send_binary_string');
  6114  				},
  6115  				slice_blob: Test(window.File && (File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice)),
  6116  				stream_upload: function(){
  6117  					return I.can('slice_blob') && I.can('send_multipart');
  6118  				},
  6119  				summon_file_dialog: Test(function() { // yeah... some dirty sniffing here...
  6120  					return (Env.browser === 'Firefox' && Env.version >= 4) ||
  6121  						(Env.browser === 'Opera' && Env.version >= 12) ||
  6122  						(Env.browser === 'IE' && Env.version >= 10) ||
  6123  						!!~Basic.inArray(Env.browser, ['Chrome', 'Safari']);
  6124  				}()),
  6125  				upload_filesize: True
  6126  			}, 
  6127  			arguments[2]
  6128  		);
  6129  
  6130  		Runtime.call(this, options, (arguments[1] || type), caps);
  6131  
  6132  
  6133  		Basic.extend(this, {
  6134  
  6135  			init : function() {
  6136  				this.trigger("Init");
  6137  			},
  6138  
  6139  			destroy: (function(destroy) { // extend default destroy method
  6140  				return function() {
  6141  					destroy.call(I);
  6142  					destroy = I = null;
  6143  				};
  6144  			}(this.destroy))
  6145  		});
  6146  
  6147  		Basic.extend(this.getShim(), extensions);
  6148  	}
  6149  
  6150  	Runtime.addConstructor(type, Html5Runtime);
  6151  
  6152  	return extensions;
  6153  });
  6154  
  6155  // Included from: src/javascript/runtime/html5/file/Blob.js
  6156  
  6157  /**
  6158   * Blob.js
  6159   *
  6160   * Copyright 2013, Moxiecode Systems AB
  6161   * Released under GPL License.
  6162   *
  6163   * License: http://www.plupload.com/license
  6164   * Contributing: http://www.plupload.com/contributing
  6165   */
  6166  
  6167  /**
  6168  @class moxie/runtime/html5/file/Blob
  6169  @private
  6170  */
  6171  define("moxie/runtime/html5/file/Blob", [
  6172  	"moxie/runtime/html5/Runtime",
  6173  	"moxie/file/Blob"
  6174  ], function(extensions, Blob) {
  6175  
  6176  	function HTML5Blob() {
  6177  		function w3cBlobSlice(blob, start, end) {
  6178  			var blobSlice;
  6179  
  6180  			if (window.File.prototype.slice) {
  6181  				try {
  6182  					blob.slice();	// depricated version will throw WRONG_ARGUMENTS_ERR exception
  6183  					return blob.slice(start, end);
  6184  				} catch (e) {
  6185  					// depricated slice method
  6186  					return blob.slice(start, end - start);
  6187  				}
  6188  			// slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672
  6189  			} else if ((blobSlice = window.File.prototype.webkitSlice || window.File.prototype.mozSlice)) {
  6190  				return blobSlice.call(blob, start, end);
  6191  			} else {
  6192  				return null; // or throw some exception
  6193  			}
  6194  		}
  6195  
  6196  		this.slice = function() {
  6197  			return new Blob(this.getRuntime().uid, w3cBlobSlice.apply(this, arguments));
  6198  		};
  6199  	}
  6200  
  6201  	return (extensions.Blob = HTML5Blob);
  6202  });
  6203  
  6204  // Included from: src/javascript/core/utils/Events.js
  6205  
  6206  /**
  6207   * Events.js
  6208   *
  6209   * Copyright 2013, Moxiecode Systems AB
  6210   * Released under GPL License.
  6211   *
  6212   * License: http://www.plupload.com/license
  6213   * Contributing: http://www.plupload.com/contributing
  6214   */
  6215  
  6216  define('moxie/core/utils/Events', [
  6217  	'moxie/core/utils/Basic'
  6218  ], function(Basic) {
  6219  	var eventhash = {}, uid = 'moxie_' + Basic.guid();
  6220  	
  6221  	// IE W3C like event funcs
  6222  	function preventDefault() {
  6223  		this.returnValue = false;
  6224  	}
  6225  
  6226  	function stopPropagation() {
  6227  		this.cancelBubble = true;
  6228  	}
  6229  
  6230  	/**
  6231  	Adds an event handler to the specified object and store reference to the handler
  6232  	in objects internal Plupload registry (@see removeEvent).
  6233  	
  6234  	@method addEvent
  6235  	@for Utils
  6236  	@static
  6237  	@param {Object} obj DOM element like object to add handler to.
  6238  	@param {String} name Name to add event listener to.
  6239  	@param {Function} callback Function to call when event occurs.
  6240  	@param {String} [key] that might be used to add specifity to the event record.
  6241  	*/
  6242  	var addEvent = function(obj, name, callback, key) {
  6243  		var func, events;
  6244  					
  6245  		name = name.toLowerCase();
  6246  
  6247  		// Add event listener
  6248  		if (obj.addEventListener) {
  6249  			func = callback;
  6250  			
  6251  			obj.addEventListener(name, func, false);
  6252  		} else if (obj.attachEvent) {
  6253  			func = function() {
  6254  				var evt = window.event;
  6255  
  6256  				if (!evt.target) {
  6257  					evt.target = evt.srcElement;
  6258  				}
  6259  
  6260  				evt.preventDefault = preventDefault;
  6261  				evt.stopPropagation = stopPropagation;
  6262  
  6263  				callback(evt);
  6264  			};
  6265  
  6266  			obj.attachEvent('on' + name, func);
  6267  		}
  6268  		
  6269  		// Log event handler to objects internal mOxie registry
  6270  		if (!obj[uid]) {
  6271  			obj[uid] = Basic.guid();
  6272  		}
  6273  		
  6274  		if (!eventhash.hasOwnProperty(obj[uid])) {
  6275  			eventhash[obj[uid]] = {};
  6276  		}
  6277  		
  6278  		events = eventhash[obj[uid]];
  6279  		
  6280  		if (!events.hasOwnProperty(name)) {
  6281  			events[name] = [];
  6282  		}
  6283  				
  6284  		events[name].push({
  6285  			func: func,
  6286  			orig: callback, // store original callback for IE
  6287  			key: key
  6288  		});
  6289  	};
  6290  	
  6291  	
  6292  	/**
  6293  	Remove event handler from the specified object. If third argument (callback)
  6294  	is not specified remove all events with the specified name.
  6295  	
  6296  	@method removeEvent
  6297  	@static
  6298  	@param {Object} obj DOM element to remove event listener(s) from.
  6299  	@param {String} name Name of event listener to remove.
  6300  	@param {Function|String} [callback] might be a callback or unique key to match.
  6301  	*/
  6302  	var removeEvent = function(obj, name, callback) {
  6303  		var type, undef;
  6304  		
  6305  		name = name.toLowerCase();
  6306  		
  6307  		if (obj[uid] && eventhash[obj[uid]] && eventhash[obj[uid]][name]) {
  6308  			type = eventhash[obj[uid]][name];
  6309  		} else {
  6310  			return;
  6311  		}
  6312  			
  6313  		for (var i = type.length - 1; i >= 0; i--) {
  6314  			// undefined or not, key should match
  6315  			if (type[i].orig === callback || type[i].key === callback) {
  6316  				if (obj.removeEventListener) {
  6317  					obj.removeEventListener(name, type[i].func, false);
  6318  				} else if (obj.detachEvent) {
  6319  					obj.detachEvent('on'+name, type[i].func);
  6320  				}
  6321  				
  6322  				type[i].orig = null;
  6323  				type[i].func = null;
  6324  				type.splice(i, 1);
  6325  				
  6326  				// If callback was passed we are done here, otherwise proceed
  6327  				if (callback !== undef) {
  6328  					break;
  6329  				}
  6330  			}
  6331  		}
  6332  		
  6333  		// If event array got empty, remove it
  6334  		if (!type.length) {
  6335  			delete eventhash[obj[uid]][name];
  6336  		}
  6337  		
  6338  		// If mOxie registry has become empty, remove it
  6339  		if (Basic.isEmptyObj(eventhash[obj[uid]])) {
  6340  			delete eventhash[obj[uid]];
  6341  			
  6342  			// IE doesn't let you remove DOM object property with - delete
  6343  			try {
  6344  				delete obj[uid];
  6345  			} catch(e) {
  6346  				obj[uid] = undef;
  6347  			}
  6348  		}
  6349  	};
  6350  	
  6351  	
  6352  	/**
  6353  	Remove all kind of events from the specified object
  6354  	
  6355  	@method removeAllEvents
  6356  	@static
  6357  	@param {Object} obj DOM element to remove event listeners from.
  6358  	@param {String} [key] unique key to match, when removing events.
  6359  	*/
  6360  	var removeAllEvents = function(obj, key) {		
  6361  		if (!obj || !obj[uid]) {
  6362  			return;
  6363  		}
  6364  		
  6365  		Basic.each(eventhash[obj[uid]], function(events, name) {
  6366  			removeEvent(obj, name, key);
  6367  		});
  6368  	};
  6369  
  6370  	return {
  6371  		addEvent: addEvent,
  6372  		removeEvent: removeEvent,
  6373  		removeAllEvents: removeAllEvents
  6374  	};
  6375  });
  6376  
  6377  // Included from: src/javascript/runtime/html5/file/FileInput.js
  6378  
  6379  /**
  6380   * FileInput.js
  6381   *
  6382   * Copyright 2013, Moxiecode Systems AB
  6383   * Released under GPL License.
  6384   *
  6385   * License: http://www.plupload.com/license
  6386   * Contributing: http://www.plupload.com/contributing
  6387   */
  6388  
  6389  /**
  6390  @class moxie/runtime/html5/file/FileInput
  6391  @private
  6392  */
  6393  define("moxie/runtime/html5/file/FileInput", [
  6394  	"moxie/runtime/html5/Runtime",
  6395  	"moxie/core/utils/Basic",
  6396  	"moxie/core/utils/Dom",
  6397  	"moxie/core/utils/Events",
  6398  	"moxie/core/utils/Mime",
  6399  	"moxie/core/utils/Env"
  6400  ], function(extensions, Basic, Dom, Events, Mime, Env) {
  6401  	
  6402  	function FileInput() {
  6403  		var _files = [], _options;
  6404  
  6405  		Basic.extend(this, {
  6406  			init: function(options) {
  6407  				var comp = this, I = comp.getRuntime(), input, shimContainer, mimes, browseButton, zIndex, top;
  6408  
  6409  				_options = options;
  6410  				_files = [];
  6411  
  6412  				// figure out accept string
  6413  				mimes = _options.accept.mimes || Mime.extList2mimes(_options.accept, I.can('filter_by_extension'));
  6414  
  6415  				shimContainer = I.getShimContainer();
  6416  
  6417  				shimContainer.innerHTML = '<input id="' + I.uid +'" type="file" style="font-size:999px;opacity:0;"' +
  6418  					(_options.multiple && I.can('select_multiple') ? 'multiple' : '') + 
  6419  					(_options.directory && I.can('select_folder') ? 'webkitdirectory directory' : '') + // Chrome 11+
  6420  					(mimes ? ' accept="' + mimes.join(',') + '"' : '') + ' />';
  6421  
  6422  				input = Dom.get(I.uid);
  6423  
  6424  				// prepare file input to be placed underneath the browse_button element
  6425  				Basic.extend(input.style, {
  6426  					position: 'absolute',
  6427  					top: 0,
  6428  					left: 0,
  6429  					width: '100%',
  6430  					height: '100%'
  6431  				});
  6432  
  6433  
  6434  				browseButton = Dom.get(_options.browse_button);
  6435  
  6436  				// Route click event to the input[type=file] element for browsers that support such behavior
  6437  				if (I.can('summon_file_dialog')) {
  6438  					if (Dom.getStyle(browseButton, 'position') === 'static') {
  6439  						browseButton.style.position = 'relative';
  6440  					}
  6441  
  6442  					zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
  6443  
  6444  					browseButton.style.zIndex = zIndex;
  6445  					shimContainer.style.zIndex = zIndex - 1;
  6446  
  6447  					Events.addEvent(browseButton, 'click', function(e) {
  6448  						var input = Dom.get(I.uid);
  6449  						if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
  6450  							input.click();
  6451  						}
  6452  						e.preventDefault();
  6453  					}, comp.uid);
  6454  				}
  6455  
  6456  				/* Since we have to place input[type=file] on top of the browse_button for some browsers,
  6457  				browse_button loses interactivity, so we restore it here */
  6458  				top = I.can('summon_file_dialog') ? browseButton : shimContainer;
  6459  
  6460  				Events.addEvent(top, 'mouseover', function() {
  6461  					comp.trigger('mouseenter');
  6462  				}, comp.uid);
  6463  
  6464  				Events.addEvent(top, 'mouseout', function() {
  6465  					comp.trigger('mouseleave');
  6466  				}, comp.uid);
  6467  
  6468  				Events.addEvent(top, 'mousedown', function() {
  6469  					comp.trigger('mousedown');
  6470  				}, comp.uid);
  6471  
  6472  				Events.addEvent(Dom.get(_options.container), 'mouseup', function() {
  6473  					comp.trigger('mouseup');
  6474  				}, comp.uid);
  6475  
  6476  
  6477  				input.onchange = function onChange() { // there should be only one handler for this
  6478  					_files = [];
  6479  
  6480  					if (_options.directory) {
  6481  						// folders are represented by dots, filter them out (Chrome 11+)
  6482  						Basic.each(this.files, function(file) {
  6483  							if (file.name !== ".") { // if it doesn't looks like a folder
  6484  								_files.push(file);
  6485  							}
  6486  						});
  6487  					} else {
  6488  						_files = [].slice.call(this.files);
  6489  					}
  6490  
  6491  					// clearing the value enables the user to select the same file again if they want to
  6492  					if (Env.browser !== 'IE') {
  6493  						this.value = '';
  6494  					} else {
  6495  						// in IE input[type="file"] is read-only so the only way to reset it is to re-insert it
  6496  						var clone = this.cloneNode(true);
  6497  						this.parentNode.replaceChild(clone, this);
  6498  						clone.onchange = onChange;
  6499  					}
  6500  					comp.trigger('change');
  6501  				};
  6502  
  6503  				// ready event is perfectly asynchronous
  6504  				comp.trigger({
  6505  					type: 'ready',
  6506  					async: true
  6507  				});
  6508  
  6509  				shimContainer = null;
  6510  			},
  6511  
  6512  			getFiles: function() {
  6513  				return _files;
  6514  			},
  6515  
  6516  			disable: function(state) {
  6517  				var I = this.getRuntime(), input;
  6518  
  6519  				if ((input = Dom.get(I.uid))) {
  6520  					input.disabled = !!state;
  6521  				}
  6522  			},
  6523  
  6524  			destroy: function() {
  6525  				var I = this.getRuntime()
  6526  				, shim = I.getShim()
  6527  				, shimContainer = I.getShimContainer()
  6528  				;
  6529  				
  6530  				Events.removeAllEvents(shimContainer, this.uid);
  6531  				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
  6532  				Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
  6533  				
  6534  				if (shimContainer) {
  6535  					shimContainer.innerHTML = '';
  6536  				}
  6537  
  6538  				shim.removeInstance(this.uid);
  6539  
  6540  				_files = _options = shimContainer = shim = null;
  6541  			}
  6542  		});
  6543  	}
  6544  
  6545  	return (extensions.FileInput = FileInput);
  6546  });
  6547  
  6548  // Included from: src/javascript/runtime/html5/file/FileDrop.js
  6549  
  6550  /**
  6551   * FileDrop.js
  6552   *
  6553   * Copyright 2013, Moxiecode Systems AB
  6554   * Released under GPL License.
  6555   *
  6556   * License: http://www.plupload.com/license
  6557   * Contributing: http://www.plupload.com/contributing
  6558   */
  6559  
  6560  /**
  6561  @class moxie/runtime/html5/file/FileDrop
  6562  @private
  6563  */
  6564  define("moxie/runtime/html5/file/FileDrop", [
  6565  	"moxie/runtime/html5/Runtime",
  6566  	"moxie/core/utils/Basic",
  6567  	"moxie/core/utils/Dom",
  6568  	"moxie/core/utils/Events",
  6569  	"moxie/core/utils/Mime"
  6570  ], function(extensions, Basic, Dom, Events, Mime) {
  6571  	
  6572  	function FileDrop() {
  6573  		var _files = [], _allowedExts = [], _options;
  6574  
  6575  		Basic.extend(this, {
  6576  			init: function(options) {
  6577  				var comp = this, dropZone;
  6578  
  6579  				_options = options;
  6580  				_allowedExts = _extractExts(_options.accept);
  6581  				dropZone = _options.container;
  6582  
  6583  				Events.addEvent(dropZone, 'dragover', function(e) {
  6584  					e.preventDefault();
  6585  					e.stopPropagation();
  6586  					e.dataTransfer.dropEffect = 'copy';
  6587  				}, comp.uid);
  6588  
  6589  				Events.addEvent(dropZone, 'drop', function(e) {
  6590  					e.preventDefault();
  6591  					e.stopPropagation();
  6592  
  6593  					_files = [];
  6594  
  6595  					// Chrome 21+ accepts folders via Drag'n'Drop
  6596  					if (e.dataTransfer.items && e.dataTransfer.items[0].webkitGetAsEntry) {
  6597  						_readItems(e.dataTransfer.items, function() {
  6598  							comp.trigger("drop");
  6599  						});
  6600  					} else {
  6601  						Basic.each(e.dataTransfer.files, function(file) {
  6602  							if (_isAcceptable(file)) {
  6603  								_files.push(file);
  6604  							}
  6605  						});
  6606  						comp.trigger("drop");
  6607  					}
  6608  				}, comp.uid);
  6609  
  6610  				Events.addEvent(dropZone, 'dragenter', function(e) {
  6611  					e.preventDefault();
  6612  					e.stopPropagation();
  6613  					comp.trigger("dragenter");
  6614  				}, comp.uid);
  6615  
  6616  				Events.addEvent(dropZone, 'dragleave', function(e) {
  6617  					e.preventDefault();
  6618  					e.stopPropagation();
  6619  					comp.trigger("dragleave");
  6620  				}, comp.uid);
  6621  			},
  6622  
  6623  			getFiles: function() {
  6624  				return _files;
  6625  			},
  6626  
  6627  			destroy: function() {
  6628  				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
  6629  				_files = _allowedExts = _options = null;
  6630  			}
  6631  		});
  6632  
  6633  		
  6634  		function _extractExts(accept) {
  6635  			var exts = [];
  6636  			for (var i = 0; i < accept.length; i++) {
  6637  				[].push.apply(exts, accept[i].extensions.split(/\s*,\s*/));
  6638  			}
  6639  			return Basic.inArray('*', exts) === -1 ? exts : [];
  6640  		}
  6641  
  6642  
  6643  		function _isAcceptable(file) {
  6644  			var ext = Mime.getFileExtension(file.name);
  6645  			return !ext || !_allowedExts.length || Basic.inArray(ext, _allowedExts) !== -1;
  6646  		}
  6647  
  6648  
  6649  		function _readItems(items, cb) {
  6650  			var entries = [];
  6651  			Basic.each(items, function(item) {
  6652  				var entry = item.webkitGetAsEntry();
  6653  				// Address #998 (https://code.google.com/p/chromium/issues/detail?id=332579)
  6654  				if (entry) {
  6655  					// file() fails on OSX when the filename contains a special character (e.g. umlaut): see #61
  6656  					if (entry.isFile) {
  6657  						var file = item.getAsFile();
  6658  						if (_isAcceptable(file)) {
  6659  							_files.push(file);
  6660  						}
  6661  					} else {
  6662  						entries.push(entry);
  6663  					}
  6664  				}
  6665  			});
  6666  
  6667  			if (entries.length) {
  6668  				_readEntries(entries, cb);
  6669  			} else {
  6670  				cb();
  6671  			}
  6672  		}
  6673  
  6674  
  6675  		function _readEntries(entries, cb) {
  6676  			var queue = [];
  6677  			Basic.each(entries, function(entry) {
  6678  				queue.push(function(cbcb) {
  6679  					_readEntry(entry, cbcb);
  6680  				});
  6681  			});
  6682  			Basic.inSeries(queue, function() {
  6683  				cb();
  6684  			});
  6685  		}
  6686  
  6687  		function _readEntry(entry, cb) {
  6688  			if (entry.isFile) {
  6689  				entry.file(function(file) {
  6690  					if (_isAcceptable(file)) {
  6691  						_files.push(file);
  6692  					}
  6693  					cb();
  6694  				}, function() {
  6695  					// fire an error event maybe
  6696  					cb();
  6697  				});
  6698  			} else if (entry.isDirectory) {
  6699  				_readDirEntry(entry, cb);
  6700  			} else {
  6701  				cb(); // not file, not directory? what then?..
  6702  			}
  6703  		}
  6704  
  6705  		function _readDirEntry(dirEntry, cb) {
  6706  			var entries = [], dirReader = dirEntry.createReader();
  6707  
  6708  			// keep quering recursively till no more entries
  6709  			function getEntries(cbcb) {
  6710  				dirReader.readEntries(function(moreEntries) {
  6711  					if (moreEntries.length) {
  6712  						[].push.apply(entries, moreEntries);
  6713  						getEntries(cbcb);
  6714  					} else {
  6715  						cbcb();
  6716  					}
  6717  				}, cbcb);
  6718  			}
  6719  
  6720  			// ...and you thought FileReader was crazy...
  6721  			getEntries(function() {
  6722  				_readEntries(entries, cb);
  6723  			}); 
  6724  		}
  6725  	}
  6726  
  6727  	return (extensions.FileDrop = FileDrop);
  6728  });
  6729  
  6730  // Included from: src/javascript/runtime/html5/file/FileReader.js
  6731  
  6732  /**
  6733   * FileReader.js
  6734   *
  6735   * Copyright 2013, Moxiecode Systems AB
  6736   * Released under GPL License.
  6737   *
  6738   * License: http://www.plupload.com/license
  6739   * Contributing: http://www.plupload.com/contributing
  6740   */
  6741  
  6742  /**
  6743  @class moxie/runtime/html5/file/FileReader
  6744  @private
  6745  */
  6746  define("moxie/runtime/html5/file/FileReader", [
  6747  	"moxie/runtime/html5/Runtime",
  6748  	"moxie/core/utils/Encode",
  6749  	"moxie/core/utils/Basic"
  6750  ], function(extensions, Encode, Basic) {
  6751  	
  6752  	function FileReader() {
  6753  		var _fr, _convertToBinary = false;
  6754  
  6755  		Basic.extend(this, {
  6756  
  6757  			read: function(op, blob) {
  6758  				var target = this;
  6759  
  6760  				_fr = new window.FileReader();
  6761  
  6762  				_fr.addEventListener('progress', function(e) {
  6763  					target.trigger(e);
  6764  				});
  6765  
  6766  				_fr.addEventListener('load', function(e) {
  6767  					target.trigger(e);
  6768  				});
  6769  
  6770  				_fr.addEventListener('error', function(e) {
  6771  					target.trigger(e, _fr.error);
  6772  				});
  6773  
  6774  				_fr.addEventListener('loadend', function() {
  6775  					_fr = null;
  6776  				});
  6777  
  6778  				if (Basic.typeOf(_fr[op]) === 'function') {
  6779  					_convertToBinary = false;
  6780  					_fr[op](blob.getSource());
  6781  				} else if (op === 'readAsBinaryString') { // readAsBinaryString is depricated in general and never existed in IE10+
  6782  					_convertToBinary = true;
  6783  					_fr.readAsDataURL(blob.getSource());
  6784  				}
  6785  			},
  6786  
  6787  			getResult: function() {
  6788  				return _fr && _fr.result ? (_convertToBinary ? _toBinary(_fr.result) : _fr.result) : null;
  6789  			},
  6790  
  6791  			abort: function() {
  6792  				if (_fr) {
  6793  					_fr.abort();
  6794  				}
  6795  			},
  6796  
  6797  			destroy: function() {
  6798  				_fr = null;
  6799  			}
  6800  		});
  6801  
  6802  		function _toBinary(str) {
  6803  			return Encode.atob(str.substring(str.indexOf('base64,') + 7));
  6804  		}
  6805  	}
  6806  
  6807  	return (extensions.FileReader = FileReader);
  6808  });
  6809  
  6810  // Included from: src/javascript/runtime/html5/xhr/XMLHttpRequest.js
  6811  
  6812  /**
  6813   * XMLHttpRequest.js
  6814   *
  6815   * Copyright 2013, Moxiecode Systems AB
  6816   * Released under GPL License.
  6817   *
  6818   * License: http://www.plupload.com/license
  6819   * Contributing: http://www.plupload.com/contributing
  6820   */
  6821  
  6822  /*global ActiveXObject:true */
  6823  
  6824  /**
  6825  @class moxie/runtime/html5/xhr/XMLHttpRequest
  6826  @private
  6827  */
  6828  define("moxie/runtime/html5/xhr/XMLHttpRequest", [
  6829  	"moxie/runtime/html5/Runtime",
  6830  	"moxie/core/utils/Basic",
  6831  	"moxie/core/utils/Mime",
  6832  	"moxie/core/utils/Url",
  6833  	"moxie/file/File",
  6834  	"moxie/file/Blob",
  6835  	"moxie/xhr/FormData",
  6836  	"moxie/core/Exceptions",
  6837  	"moxie/core/utils/Env"
  6838  ], function(extensions, Basic, Mime, Url, File, Blob, FormData, x, Env) {
  6839  	
  6840  	function XMLHttpRequest() {
  6841  		var self = this
  6842  		, _xhr
  6843  		, _filename
  6844  		;
  6845  
  6846  		Basic.extend(this, {
  6847  			send: function(meta, data) {
  6848  				var target = this
  6849  				, isGecko2_5_6 = (Env.browser === 'Mozilla' && Env.version >= 4 && Env.version < 7)
  6850  				, isAndroidBrowser = Env.browser === 'Android Browser'
  6851  				, mustSendAsBinary = false
  6852  				;
  6853  
  6854  				// extract file name
  6855  				_filename = meta.url.replace(/^.+?\/([\w\-\.]+)$/, '$1').toLowerCase();
  6856  
  6857  				_xhr = _getNativeXHR();
  6858  				_xhr.open(meta.method, meta.url, meta.async, meta.user, meta.password);
  6859  
  6860  
  6861  				// prepare data to be sent
  6862  				if (data instanceof Blob) {
  6863  					if (data.isDetached()) {
  6864  						mustSendAsBinary = true;
  6865  					}
  6866  					data = data.getSource();
  6867  				} else if (data instanceof FormData) {
  6868  
  6869  					if (data.hasBlob()) {
  6870  						if (data.getBlob().isDetached()) {
  6871  							data = _prepareMultipart.call(target, data); // _xhr must be instantiated and be in OPENED state
  6872  							mustSendAsBinary = true;
  6873  						} else if ((isGecko2_5_6 || isAndroidBrowser) && Basic.typeOf(data.getBlob().getSource()) === 'blob' && window.FileReader) {
  6874  							// Gecko 2/5/6 can't send blob in FormData: https://bugzilla.mozilla.org/show_bug.cgi?id=649150
  6875  							// Android browsers (default one and Dolphin) seem to have the same issue, see: #613
  6876  							_preloadAndSend.call(target, meta, data);
  6877  							return; // _preloadAndSend will reinvoke send() with transmutated FormData =%D
  6878  						}	
  6879  					}
  6880  
  6881  					// transfer fields to real FormData
  6882  					if (data instanceof FormData) { // if still a FormData, e.g. not mangled by _prepareMultipart()
  6883  						var fd = new window.FormData();
  6884  						data.each(function(value, name) {
  6885  							if (value instanceof Blob) {
  6886  								fd.append(name, value.getSource());
  6887  							} else {
  6888  								fd.append(name, value);
  6889  							}
  6890  						});
  6891  						data = fd;
  6892  					}
  6893  				}
  6894  
  6895  
  6896  				// if XHR L2
  6897  				if (_xhr.upload) {
  6898  					if (meta.withCredentials) {
  6899  						_xhr.withCredentials = true;
  6900  					}
  6901  
  6902  					_xhr.addEventListener('load', function(e) {
  6903  						target.trigger(e);
  6904  					});
  6905  
  6906  					_xhr.addEventListener('error', function(e) {
  6907  						target.trigger(e);
  6908  					});
  6909  
  6910  					// additionally listen to progress events
  6911  					_xhr.addEventListener('progress', function(e) {
  6912  						target.trigger(e);
  6913  					});
  6914  
  6915  					_xhr.upload.addEventListener('progress', function(e) {
  6916  						target.trigger({
  6917  							type: 'UploadProgress',
  6918  							loaded: e.loaded,
  6919  							total: e.total
  6920  						});
  6921  					});
  6922  				// ... otherwise simulate XHR L2
  6923  				} else {
  6924  					_xhr.onreadystatechange = function onReadyStateChange() {
  6925  						
  6926  						// fake Level 2 events
  6927  						switch (_xhr.readyState) {
  6928  							
  6929  							case 1: // XMLHttpRequest.OPENED
  6930  								// readystatechanged is fired twice for OPENED state (in IE and Mozilla) - neu
  6931  								break;
  6932  							
  6933  							// looks like HEADERS_RECEIVED (state 2) is not reported in Opera (or it's old versions) - neu
  6934  							case 2: // XMLHttpRequest.HEADERS_RECEIVED
  6935  								break;
  6936  								
  6937  							case 3: // XMLHttpRequest.LOADING 
  6938  								// try to fire progress event for not XHR L2
  6939  								var total, loaded;
  6940  								
  6941  								try {
  6942  									if (Url.hasSameOrigin(meta.url)) { // Content-Length not accessible for cross-domain on some browsers
  6943  										total = _xhr.getResponseHeader('Content-Length') || 0; // old Safari throws an exception here
  6944  									}
  6945  
  6946  									if (_xhr.responseText) { // responseText was introduced in IE7
  6947  										loaded = _xhr.responseText.length;
  6948  									}
  6949  								} catch(ex) {
  6950  									total = loaded = 0;
  6951  								}
  6952  
  6953  								target.trigger({
  6954  									type: 'progress',
  6955  									lengthComputable: !!total,
  6956  									total: parseInt(total, 10),
  6957  									loaded: loaded
  6958  								});
  6959  								break;
  6960  								
  6961  							case 4: // XMLHttpRequest.DONE
  6962  								// release readystatechange handler (mostly for IE)
  6963  								_xhr.onreadystatechange = function() {};
  6964  
  6965  								// usually status 0 is returned when server is unreachable, but FF also fails to status 0 for 408 timeout
  6966  								if (_xhr.status === 0) {
  6967  									target.trigger('error');
  6968  								} else {
  6969  									target.trigger('load');
  6970  								}							
  6971  								break;
  6972  						}
  6973  					};
  6974  				}
  6975  				
  6976  
  6977  				// set request headers
  6978  				if (!Basic.isEmptyObj(meta.headers)) {
  6979  					Basic.each(meta.headers, function(value, header) {
  6980  						_xhr.setRequestHeader(header, value);
  6981  					});
  6982  				}
  6983  
  6984  				// request response type
  6985  				if ("" !== meta.responseType && 'responseType' in _xhr) {
  6986  					if ('json' === meta.responseType && !Env.can('return_response_type', 'json')) { // we can fake this one
  6987  						_xhr.responseType = 'text';
  6988  					} else {
  6989  						_xhr.responseType = meta.responseType;
  6990  					}
  6991  				}
  6992  
  6993  				// send ...
  6994  				if (!mustSendAsBinary) {
  6995  					_xhr.send(data);
  6996  				} else {
  6997  					if (_xhr.sendAsBinary) { // Gecko
  6998  						_xhr.sendAsBinary(data);
  6999  					} else { // other browsers having support for typed arrays
  7000  						(function() {
  7001  							// mimic Gecko's sendAsBinary
  7002  							var ui8a = new Uint8Array(data.length);
  7003  							for (var i = 0; i < data.length; i++) {
  7004  								ui8a[i] = (data.charCodeAt(i) & 0xff);
  7005  							}
  7006  							_xhr.send(ui8a.buffer);
  7007  						}());
  7008  					}
  7009  				}
  7010  
  7011  				target.trigger('loadstart');
  7012  			},
  7013  
  7014  			getStatus: function() {
  7015  				// according to W3C spec it should return 0 for readyState < 3, but instead it throws an exception
  7016  				try {
  7017  					if (_xhr) {
  7018  						return _xhr.status;
  7019  					}
  7020  				} catch(ex) {}
  7021  				return 0;
  7022  			},
  7023  
  7024  			getResponse: function(responseType) {
  7025  				var I = this.getRuntime();
  7026  
  7027  				try {
  7028  					switch (responseType) {
  7029  						case 'blob':
  7030  							var file = new File(I.uid, _xhr.response);
  7031  							
  7032  							// try to extract file name from content-disposition if possible (might be - not, if CORS for example)	
  7033  							var disposition = _xhr.getResponseHeader('Content-Disposition');
  7034  							if (disposition) {
  7035  								// extract filename from response header if available
  7036  								var match = disposition.match(/filename=([\'\"'])([^\1]+)\1/);
  7037  								if (match) {
  7038  									_filename = match[2];
  7039  								}
  7040  							}
  7041  							file.name = _filename;
  7042  
  7043  							// pre-webkit Opera doesn't set type property on the blob response
  7044  							if (!file.type) {
  7045  								file.type = Mime.getFileMime(_filename);
  7046  							}
  7047  							return file;
  7048  
  7049  						case 'json':
  7050  							if (!Env.can('return_response_type', 'json')) {
  7051  								return _xhr.status === 200 && !!window.JSON ? JSON.parse(_xhr.responseText) : null;
  7052  							}
  7053  							return _xhr.response;
  7054  
  7055  						case 'document':
  7056  							return _getDocument(_xhr);
  7057  
  7058  						default:
  7059  							return _xhr.responseText !== '' ? _xhr.responseText : null; // against the specs, but for consistency across the runtimes
  7060  					}
  7061  				} catch(ex) {
  7062  					return null;
  7063  				}				
  7064  			},
  7065  
  7066  			getAllResponseHeaders: function() {
  7067  				try {
  7068  					return _xhr.getAllResponseHeaders();
  7069  				} catch(ex) {}
  7070  				return '';
  7071  			},
  7072  
  7073  			abort: function() {
  7074  				if (_xhr) {
  7075  					_xhr.abort();
  7076  				}
  7077  			},
  7078  
  7079  			destroy: function() {
  7080  				self = _filename = null;
  7081  			}
  7082  		});
  7083  
  7084  
  7085  		// here we go... ugly fix for ugly bug
  7086  		function _preloadAndSend(meta, data) {
  7087  			var target = this, blob, fr;
  7088  				
  7089  			// get original blob
  7090  			blob = data.getBlob().getSource();
  7091  			
  7092  			// preload blob in memory to be sent as binary string
  7093  			fr = new window.FileReader();
  7094  			fr.onload = function() {
  7095  				// overwrite original blob
  7096  				data.append(data.getBlobName(), new Blob(null, {
  7097  					type: blob.type,
  7098  					data: fr.result
  7099  				}));
  7100  				// invoke send operation again
  7101  				self.send.call(target, meta, data);
  7102  			};
  7103  			fr.readAsBinaryString(blob);
  7104  		}
  7105  
  7106  		
  7107  		function _getNativeXHR() {
  7108  			if (window.XMLHttpRequest && !(Env.browser === 'IE' && Env.version < 8)) { // IE7 has native XHR but it's buggy
  7109  				return new window.XMLHttpRequest();
  7110  			} else {
  7111  				return (function() {
  7112  					var progIDs = ['Msxml2.XMLHTTP.6.0', 'Microsoft.XMLHTTP']; // if 6.0 available, use it, otherwise failback to default 3.0
  7113  					for (var i = 0; i < progIDs.length; i++) {
  7114  						try {
  7115  							return new ActiveXObject(progIDs[i]);
  7116  						} catch (ex) {}
  7117  					}
  7118  				})();
  7119  			}
  7120  		}
  7121  		
  7122  		// @credits Sergey Ilinsky	(http://www.ilinsky.com/)
  7123  		function _getDocument(xhr) {
  7124  			var rXML = xhr.responseXML;
  7125  			var rText = xhr.responseText;
  7126  			
  7127  			// Try parsing responseText (@see: http://www.ilinsky.com/articles/XMLHttpRequest/#bugs-ie-responseXML-content-type)
  7128  			if (Env.browser === 'IE' && rText && rXML && !rXML.documentElement && /[^\/]+\/[^\+]+\+xml/.test(xhr.getResponseHeader("Content-Type"))) {
  7129  				rXML = new window.ActiveXObject("Microsoft.XMLDOM");
  7130  				rXML.async = false;
  7131  				rXML.validateOnParse = false;
  7132  				rXML.loadXML(rText);
  7133  			}
  7134  	
  7135  			// Check if there is no error in document
  7136  			if (rXML) {
  7137  				if ((Env.browser === 'IE' && rXML.parseError !== 0) || !rXML.documentElement || rXML.documentElement.tagName === "parsererror") {
  7138  					return null;
  7139  				}
  7140  			}
  7141  			return rXML;
  7142  		}
  7143  
  7144  
  7145  		function _prepareMultipart(fd) {
  7146  			var boundary = '----moxieboundary' + new Date().getTime()
  7147  			, dashdash = '--'
  7148  			, crlf = '\r\n'
  7149  			, multipart = ''
  7150  			, I = this.getRuntime()
  7151  			;
  7152  
  7153  			if (!I.can('send_binary_string')) {
  7154  				throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  7155  			}
  7156  
  7157  			_xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
  7158  
  7159  			// append multipart parameters
  7160  			fd.each(function(value, name) {
  7161  				// Firefox 3.6 failed to convert multibyte characters to UTF-8 in sendAsBinary(), 
  7162  				// so we try it here ourselves with: unescape(encodeURIComponent(value))
  7163  				if (value instanceof Blob) {
  7164  					// Build RFC2388 blob
  7165  					multipart += dashdash + boundary + crlf +
  7166  						'Content-Disposition: form-data; name="' + name + '"; filename="' + unescape(encodeURIComponent(value.name || 'blob')) + '"' + crlf +
  7167  						'Content-Type: ' + (value.type || 'application/octet-stream') + crlf + crlf +
  7168  						value.getSource() + crlf;
  7169  				} else {
  7170  					multipart += dashdash + boundary + crlf +
  7171  						'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf +
  7172  						unescape(encodeURIComponent(value)) + crlf;
  7173  				}
  7174  			});
  7175  
  7176  			multipart += dashdash + boundary + dashdash + crlf;
  7177  
  7178  			return multipart;
  7179  		}
  7180  	}
  7181  
  7182  	return (extensions.XMLHttpRequest = XMLHttpRequest);
  7183  });
  7184  
  7185  // Included from: src/javascript/runtime/html5/utils/BinaryReader.js
  7186  
  7187  /**
  7188   * BinaryReader.js
  7189   *
  7190   * Copyright 2013, Moxiecode Systems AB
  7191   * Released under GPL License.
  7192   *
  7193   * License: http://www.plupload.com/license
  7194   * Contributing: http://www.plupload.com/contributing
  7195   */
  7196  
  7197  /**
  7198  @class moxie/runtime/html5/utils/BinaryReader
  7199  @private
  7200  */
  7201  define("moxie/runtime/html5/utils/BinaryReader", [], function() {
  7202  	return function() {
  7203  		var II = false, bin;
  7204  
  7205  		// Private functions
  7206  		function read(idx, size) {
  7207  			var mv = II ? 0 : -8 * (size - 1), sum = 0, i;
  7208  
  7209  			for (i = 0; i < size; i++) {
  7210  				sum |= (bin.charCodeAt(idx + i) << Math.abs(mv + i*8));
  7211  			}
  7212  
  7213  			return sum;
  7214  		}
  7215  
  7216  		function putstr(segment, idx, length) {
  7217  			length = arguments.length === 3 ? length : bin.length - idx - 1;
  7218  			bin = bin.substr(0, idx) + segment + bin.substr(length + idx);
  7219  		}
  7220  
  7221  		function write(idx, num, size) {
  7222  			var str = '', mv = II ? 0 : -8 * (size - 1), i;
  7223  
  7224  			for (i = 0; i < size; i++) {
  7225  				str += String.fromCharCode((num >> Math.abs(mv + i*8)) & 255);
  7226  			}
  7227  
  7228  			putstr(str, idx, size);
  7229  		}
  7230  
  7231  		// Public functions
  7232  		return {
  7233  			II: function(order) {
  7234  				if (order === undefined) {
  7235  					return II;
  7236  				} else {
  7237  					II = order;
  7238  				}
  7239  			},
  7240  
  7241  			init: function(binData) {
  7242  				II = false;
  7243  				bin = binData;
  7244  			},
  7245  
  7246  			SEGMENT: function(idx, length, segment) {
  7247  				switch (arguments.length) {
  7248  					case 1:
  7249  						return bin.substr(idx, bin.length - idx - 1);
  7250  					case 2:
  7251  						return bin.substr(idx, length);
  7252  					case 3:
  7253  						putstr(segment, idx, length);
  7254  						break;
  7255  					default: return bin;
  7256  				}
  7257  			},
  7258  
  7259  			BYTE: function(idx) {
  7260  				return read(idx, 1);
  7261  			},
  7262  
  7263  			SHORT: function(idx) {
  7264  				return read(idx, 2);
  7265  			},
  7266  
  7267  			LONG: function(idx, num) {
  7268  				if (num === undefined) {
  7269  					return read(idx, 4);
  7270  				} else {
  7271  					write(idx, num, 4);
  7272  				}
  7273  			},
  7274  
  7275  			SLONG: function(idx) { // 2's complement notation
  7276  				var num = read(idx, 4);
  7277  
  7278  				return (num > 2147483647 ? num - 4294967296 : num);
  7279  			},
  7280  
  7281  			STRING: function(idx, size) {
  7282  				var str = '';
  7283  
  7284  				for (size += idx; idx < size; idx++) {
  7285  					str += String.fromCharCode(read(idx, 1));
  7286  				}
  7287  
  7288  				return str;
  7289  			}
  7290  		};
  7291  	};
  7292  });
  7293  
  7294  // Included from: src/javascript/runtime/html5/image/JPEGHeaders.js
  7295  
  7296  /**
  7297   * JPEGHeaders.js
  7298   *
  7299   * Copyright 2013, Moxiecode Systems AB
  7300   * Released under GPL License.
  7301   *
  7302   * License: http://www.plupload.com/license
  7303   * Contributing: http://www.plupload.com/contributing
  7304   */
  7305   
  7306  /**
  7307  @class moxie/runtime/html5/image/JPEGHeaders
  7308  @private
  7309  */
  7310  define("moxie/runtime/html5/image/JPEGHeaders", [
  7311  	"moxie/runtime/html5/utils/BinaryReader"
  7312  ], function(BinaryReader) {
  7313  	
  7314  	return function JPEGHeaders(data) {
  7315  		var headers = [], read, idx, marker, length = 0;
  7316  
  7317  		read = new BinaryReader();
  7318  		read.init(data);
  7319  
  7320  		// Check if data is jpeg
  7321  		if (read.SHORT(0) !== 0xFFD8) {
  7322  			return;
  7323  		}
  7324  
  7325  		idx = 2;
  7326  
  7327  		while (idx <= data.length) {
  7328  			marker = read.SHORT(idx);
  7329  
  7330  			// omit RST (restart) markers
  7331  			if (marker >= 0xFFD0 && marker <= 0xFFD7) {
  7332  				idx += 2;
  7333  				continue;
  7334  			}
  7335  
  7336  			// no headers allowed after SOS marker
  7337  			if (marker === 0xFFDA || marker === 0xFFD9) {
  7338  				break;
  7339  			}
  7340  
  7341  			length = read.SHORT(idx + 2) + 2;
  7342  
  7343  			// APPn marker detected
  7344  			if (marker >= 0xFFE1 && marker <= 0xFFEF) {
  7345  				headers.push({
  7346  					hex: marker,
  7347  					name: 'APP' + (marker & 0x000F),
  7348  					start: idx,
  7349  					length: length,
  7350  					segment: read.SEGMENT(idx, length)
  7351  				});
  7352  			}
  7353  
  7354  			idx += length;
  7355  		}
  7356  
  7357  		read.init(null); // free memory
  7358  
  7359  		return {
  7360  			headers: headers,
  7361  
  7362  			restore: function(data) {
  7363  				var max, i;
  7364  
  7365  				read.init(data);
  7366  
  7367  				idx = read.SHORT(2) == 0xFFE0 ? 4 + read.SHORT(4) : 2;
  7368  
  7369  				for (i = 0, max = headers.length; i < max; i++) {
  7370  					read.SEGMENT(idx, 0, headers[i].segment);
  7371  					idx += headers[i].length;
  7372  				}
  7373  
  7374  				data = read.SEGMENT();
  7375  				read.init(null);
  7376  				return data;
  7377  			},
  7378  
  7379  			strip: function(data) {
  7380  				var headers, jpegHeaders, i;
  7381  
  7382  				jpegHeaders = new JPEGHeaders(data);
  7383  				headers = jpegHeaders.headers;
  7384  				jpegHeaders.purge();
  7385  
  7386  				read.init(data);
  7387  
  7388  				i = headers.length;
  7389  				while (i--) {
  7390  					read.SEGMENT(headers[i].start, headers[i].length, '');
  7391  				}
  7392  				
  7393  				data = read.SEGMENT();
  7394  				read.init(null);
  7395  				return data;
  7396  			},
  7397  
  7398  			get: function(name) {
  7399  				var array = [];
  7400  
  7401  				for (var i = 0, max = headers.length; i < max; i++) {
  7402  					if (headers[i].name === name.toUpperCase()) {
  7403  						array.push(headers[i].segment);
  7404  					}
  7405  				}
  7406  				return array;
  7407  			},
  7408  
  7409  			set: function(name, segment) {
  7410  				var array = [], i, ii, max;
  7411  
  7412  				if (typeof(segment) === 'string') {
  7413  					array.push(segment);
  7414  				} else {
  7415  					array = segment;
  7416  				}
  7417  
  7418  				for (i = ii = 0, max = headers.length; i < max; i++) {
  7419  					if (headers[i].name === name.toUpperCase()) {
  7420  						headers[i].segment = array[ii];
  7421  						headers[i].length = array[ii].length;
  7422  						ii++;
  7423  					}
  7424  					if (ii >= array.length) {
  7425  						break;
  7426  					}
  7427  				}
  7428  			},
  7429  
  7430  			purge: function() {
  7431  				headers = [];
  7432  				read.init(null);
  7433  				read = null;
  7434  			}
  7435  		};
  7436  	};
  7437  });
  7438  
  7439  // Included from: src/javascript/runtime/html5/image/ExifParser.js
  7440  
  7441  /**
  7442   * ExifParser.js
  7443   *
  7444   * Copyright 2013, Moxiecode Systems AB
  7445   * Released under GPL License.
  7446   *
  7447   * License: http://www.plupload.com/license
  7448   * Contributing: http://www.plupload.com/contributing
  7449   */
  7450  
  7451  /**
  7452  @class moxie/runtime/html5/image/ExifParser
  7453  @private
  7454  */
  7455  define("moxie/runtime/html5/image/ExifParser", [
  7456  	"moxie/core/utils/Basic",
  7457  	"moxie/runtime/html5/utils/BinaryReader"
  7458  ], function(Basic, BinaryReader) {
  7459  	
  7460  	return function ExifParser() {
  7461  		// Private ExifParser fields
  7462  		var data, tags, Tiff, offsets = {}, tagDescs;
  7463  
  7464  		data = new BinaryReader();
  7465  
  7466  		tags = {
  7467  			tiff : {
  7468  				/*
  7469  				The image orientation viewed in terms of rows and columns.
  7470  
  7471  				1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
  7472  				2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
  7473  				3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
  7474  				4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
  7475  				5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
  7476  				6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
  7477  				7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
  7478  				8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
  7479  				*/
  7480  				0x0112: 'Orientation',
  7481  				0x010E: 'ImageDescription',
  7482  				0x010F: 'Make',
  7483  				0x0110: 'Model',
  7484  				0x0131: 'Software',
  7485  				0x8769: 'ExifIFDPointer',
  7486  				0x8825:	'GPSInfoIFDPointer'
  7487  			},
  7488  			exif : {
  7489  				0x9000: 'ExifVersion',
  7490  				0xA001: 'ColorSpace',
  7491  				0xA002: 'PixelXDimension',
  7492  				0xA003: 'PixelYDimension',
  7493  				0x9003: 'DateTimeOriginal',
  7494  				0x829A: 'ExposureTime',
  7495  				0x829D: 'FNumber',
  7496  				0x8827: 'ISOSpeedRatings',
  7497  				0x9201: 'ShutterSpeedValue',
  7498  				0x9202: 'ApertureValue'	,
  7499  				0x9207: 'MeteringMode',
  7500  				0x9208: 'LightSource',
  7501  				0x9209: 'Flash',
  7502  				0x920A: 'FocalLength',
  7503  				0xA402: 'ExposureMode',
  7504  				0xA403: 'WhiteBalance',
  7505  				0xA406: 'SceneCaptureType',
  7506  				0xA404: 'DigitalZoomRatio',
  7507  				0xA408: 'Contrast',
  7508  				0xA409: 'Saturation',
  7509  				0xA40A: 'Sharpness'
  7510  			},
  7511  			gps : {
  7512  				0x0000: 'GPSVersionID',
  7513  				0x0001: 'GPSLatitudeRef',
  7514  				0x0002: 'GPSLatitude',
  7515  				0x0003: 'GPSLongitudeRef',
  7516  				0x0004: 'GPSLongitude'
  7517  			}
  7518  		};
  7519  
  7520  		tagDescs = {
  7521  			'ColorSpace': {
  7522  				1: 'sRGB',
  7523  				0: 'Uncalibrated'
  7524  			},
  7525  
  7526  			'MeteringMode': {
  7527  				0: 'Unknown',
  7528  				1: 'Average',
  7529  				2: 'CenterWeightedAverage',
  7530  				3: 'Spot',
  7531  				4: 'MultiSpot',
  7532  				5: 'Pattern',
  7533  				6: 'Partial',
  7534  				255: 'Other'
  7535  			},
  7536  
  7537  			'LightSource': {
  7538  				1: 'Daylight',
  7539  				2: 'Fliorescent',
  7540  				3: 'Tungsten',
  7541  				4: 'Flash',
  7542  				9: 'Fine weather',
  7543  				10: 'Cloudy weather',
  7544  				11: 'Shade',
  7545  				12: 'Daylight fluorescent (D 5700 - 7100K)',
  7546  				13: 'Day white fluorescent (N 4600 -5400K)',
  7547  				14: 'Cool white fluorescent (W 3900 - 4500K)',
  7548  				15: 'White fluorescent (WW 3200 - 3700K)',
  7549  				17: 'Standard light A',
  7550  				18: 'Standard light B',
  7551  				19: 'Standard light C',
  7552  				20: 'D55',
  7553  				21: 'D65',
  7554  				22: 'D75',
  7555  				23: 'D50',
  7556  				24: 'ISO studio tungsten',
  7557  				255: 'Other'
  7558  			},
  7559  
  7560  			'Flash': {
  7561  				0x0000: 'Flash did not fire.',
  7562  				0x0001: 'Flash fired.',
  7563  				0x0005: 'Strobe return light not detected.',
  7564  				0x0007: 'Strobe return light detected.',
  7565  				0x0009: 'Flash fired, compulsory flash mode',
  7566  				0x000D: 'Flash fired, compulsory flash mode, return light not detected',
  7567  				0x000F: 'Flash fired, compulsory flash mode, return light detected',
  7568  				0x0010: 'Flash did not fire, compulsory flash mode',
  7569  				0x0018: 'Flash did not fire, auto mode',
  7570  				0x0019: 'Flash fired, auto mode',
  7571  				0x001D: 'Flash fired, auto mode, return light not detected',
  7572  				0x001F: 'Flash fired, auto mode, return light detected',
  7573  				0x0020: 'No flash function',
  7574  				0x0041: 'Flash fired, red-eye reduction mode',
  7575  				0x0045: 'Flash fired, red-eye reduction mode, return light not detected',
  7576  				0x0047: 'Flash fired, red-eye reduction mode, return light detected',
  7577  				0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode',
  7578  				0x004D: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
  7579  				0x004F: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
  7580  				0x0059: 'Flash fired, auto mode, red-eye reduction mode',
  7581  				0x005D: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
  7582  				0x005F: 'Flash fired, auto mode, return light detected, red-eye reduction mode'
  7583  			},
  7584  
  7585  			'ExposureMode': {
  7586  				0: 'Auto exposure',
  7587  				1: 'Manual exposure',
  7588  				2: 'Auto bracket'
  7589  			},
  7590  
  7591  			'WhiteBalance': {
  7592  				0: 'Auto white balance',
  7593  				1: 'Manual white balance'
  7594  			},
  7595  
  7596  			'SceneCaptureType': {
  7597  				0: 'Standard',
  7598  				1: 'Landscape',
  7599  				2: 'Portrait',
  7600  				3: 'Night scene'
  7601  			},
  7602  
  7603  			'Contrast': {
  7604  				0: 'Normal',
  7605  				1: 'Soft',
  7606  				2: 'Hard'
  7607  			},
  7608  
  7609  			'Saturation': {
  7610  				0: 'Normal',
  7611  				1: 'Low saturation',
  7612  				2: 'High saturation'
  7613  			},
  7614  
  7615  			'Sharpness': {
  7616  				0: 'Normal',
  7617  				1: 'Soft',
  7618  				2: 'Hard'
  7619  			},
  7620  
  7621  			// GPS related
  7622  			'GPSLatitudeRef': {
  7623  				N: 'North latitude',
  7624  				S: 'South latitude'
  7625  			},
  7626  
  7627  			'GPSLongitudeRef': {
  7628  				E: 'East longitude',
  7629  				W: 'West longitude'
  7630  			}
  7631  		};
  7632  
  7633  		function extractTags(IFD_offset, tags2extract) {
  7634  			var length = data.SHORT(IFD_offset), i, ii,
  7635  				tag, type, count, tagOffset, offset, value, values = [], hash = {};
  7636  
  7637  			for (i = 0; i < length; i++) {
  7638  				// Set binary reader pointer to beginning of the next tag
  7639  				offset = tagOffset = IFD_offset + 12 * i + 2;
  7640  
  7641  				tag = tags2extract[data.SHORT(offset)];
  7642  
  7643  				if (tag === undefined) {
  7644  					continue; // Not the tag we requested
  7645  				}
  7646  
  7647  				type = data.SHORT(offset+=2);
  7648  				count = data.LONG(offset+=2);
  7649  
  7650  				offset += 4;
  7651  				values = [];
  7652  
  7653  				switch (type) {
  7654  					case 1: // BYTE
  7655  					case 7: // UNDEFINED
  7656  						if (count > 4) {
  7657  							offset = data.LONG(offset) + offsets.tiffHeader;
  7658  						}
  7659  
  7660  						for (ii = 0; ii < count; ii++) {
  7661  							values[ii] = data.BYTE(offset + ii);
  7662  						}
  7663  
  7664  						break;
  7665  
  7666  					case 2: // STRING
  7667  						if (count > 4) {
  7668  							offset = data.LONG(offset) + offsets.tiffHeader;
  7669  						}
  7670  
  7671  						hash[tag] = data.STRING(offset, count - 1);
  7672  
  7673  						continue;
  7674  
  7675  					case 3: // SHORT
  7676  						if (count > 2) {
  7677  							offset = data.LONG(offset) + offsets.tiffHeader;
  7678  						}
  7679  
  7680  						for (ii = 0; ii < count; ii++) {
  7681  							values[ii] = data.SHORT(offset + ii*2);
  7682  						}
  7683  
  7684  						break;
  7685  
  7686  					case 4: // LONG
  7687  						if (count > 1) {
  7688  							offset = data.LONG(offset) + offsets.tiffHeader;
  7689  						}
  7690  
  7691  						for (ii = 0; ii < count; ii++) {
  7692  							values[ii] = data.LONG(offset + ii*4);
  7693  						}
  7694  
  7695  						break;
  7696  
  7697  					case 5: // RATIONAL
  7698  						offset = data.LONG(offset) + offsets.tiffHeader;
  7699  
  7700  						for (ii = 0; ii < count; ii++) {
  7701  							values[ii] = data.LONG(offset + ii*4) / data.LONG(offset + ii*4 + 4);
  7702  						}
  7703  
  7704  						break;
  7705  
  7706  					case 9: // SLONG
  7707  						offset = data.LONG(offset) + offsets.tiffHeader;
  7708  
  7709  						for (ii = 0; ii < count; ii++) {
  7710  							values[ii] = data.SLONG(offset + ii*4);
  7711  						}
  7712  
  7713  						break;
  7714  
  7715  					case 10: // SRATIONAL
  7716  						offset = data.LONG(offset) + offsets.tiffHeader;
  7717  
  7718  						for (ii = 0; ii < count; ii++) {
  7719  							values[ii] = data.SLONG(offset + ii*4) / data.SLONG(offset + ii*4 + 4);
  7720  						}
  7721  
  7722  						break;
  7723  
  7724  					default:
  7725  						continue;
  7726  				}
  7727  
  7728  				value = (count == 1 ? values[0] : values);
  7729  
  7730  				if (tagDescs.hasOwnProperty(tag) && typeof value != 'object') {
  7731  					hash[tag] = tagDescs[tag][value];
  7732  				} else {
  7733  					hash[tag] = value;
  7734  				}
  7735  			}
  7736  
  7737  			return hash;
  7738  		}
  7739  
  7740  		function getIFDOffsets() {
  7741  			var idx = offsets.tiffHeader;
  7742  
  7743  			// Set read order of multi-byte data
  7744  			data.II(data.SHORT(idx) == 0x4949);
  7745  
  7746  			// Check if always present bytes are indeed present
  7747  			if (data.SHORT(idx+=2) !== 0x002A) {
  7748  				return false;
  7749  			}
  7750  
  7751  			offsets.IFD0 = offsets.tiffHeader + data.LONG(idx += 2);
  7752  			Tiff = extractTags(offsets.IFD0, tags.tiff);
  7753  
  7754  			if ('ExifIFDPointer' in Tiff) {
  7755  				offsets.exifIFD = offsets.tiffHeader + Tiff.ExifIFDPointer;
  7756  				delete Tiff.ExifIFDPointer;
  7757  			}
  7758  
  7759  			if ('GPSInfoIFDPointer' in Tiff) {
  7760  				offsets.gpsIFD = offsets.tiffHeader + Tiff.GPSInfoIFDPointer;
  7761  				delete Tiff.GPSInfoIFDPointer;
  7762  			}
  7763  			return true;
  7764  		}
  7765  
  7766  		// At the moment only setting of simple (LONG) values, that do not require offset recalculation, is supported
  7767  		function setTag(ifd, tag, value) {
  7768  			var offset, length, tagOffset, valueOffset = 0;
  7769  
  7770  			// If tag name passed translate into hex key
  7771  			if (typeof(tag) === 'string') {
  7772  				var tmpTags = tags[ifd.toLowerCase()];
  7773  				for (var hex in tmpTags) {
  7774  					if (tmpTags[hex] === tag) {
  7775  						tag = hex;
  7776  						break;
  7777  					}
  7778  				}
  7779  			}
  7780  			offset = offsets[ifd.toLowerCase() + 'IFD'];
  7781  			length = data.SHORT(offset);
  7782  
  7783  			for (var i = 0; i < length; i++) {
  7784  				tagOffset = offset + 12 * i + 2;
  7785  
  7786  				if (data.SHORT(tagOffset) == tag) {
  7787  					valueOffset = tagOffset + 8;
  7788  					break;
  7789  				}
  7790  			}
  7791  
  7792  			if (!valueOffset) {
  7793  				return false;
  7794  			}
  7795  
  7796  			data.LONG(valueOffset, value);
  7797  			return true;
  7798  		}
  7799  
  7800  
  7801  		// Public functions
  7802  		return {
  7803  			init: function(segment) {
  7804  				// Reset internal data
  7805  				offsets = {
  7806  					tiffHeader: 10
  7807  				};
  7808  
  7809  				if (segment === undefined || !segment.length) {
  7810  					return false;
  7811  				}
  7812  
  7813  				data.init(segment);
  7814  
  7815  				// Check if that's APP1 and that it has EXIF
  7816  				if (data.SHORT(0) === 0xFFE1 && data.STRING(4, 5).toUpperCase() === "EXIF\0") {
  7817  					return getIFDOffsets();
  7818  				}
  7819  				return false;
  7820  			},
  7821  
  7822  			TIFF: function() {
  7823  				return Tiff;
  7824  			},
  7825  
  7826  			EXIF: function() {
  7827  				var Exif;
  7828  
  7829  				// Populate EXIF hash
  7830  				Exif = extractTags(offsets.exifIFD, tags.exif);
  7831  
  7832  				// Fix formatting of some tags
  7833  				if (Exif.ExifVersion && Basic.typeOf(Exif.ExifVersion) === 'array') {
  7834  					for (var i = 0, exifVersion = ''; i < Exif.ExifVersion.length; i++) {
  7835  						exifVersion += String.fromCharCode(Exif.ExifVersion[i]);
  7836  					}
  7837  					Exif.ExifVersion = exifVersion;
  7838  				}
  7839  
  7840  				return Exif;
  7841  			},
  7842  
  7843  			GPS: function() {
  7844  				var GPS;
  7845  
  7846  				GPS = extractTags(offsets.gpsIFD, tags.gps);
  7847  
  7848  				// iOS devices (and probably some others) do not put in GPSVersionID tag (why?..)
  7849  				if (GPS.GPSVersionID && Basic.typeOf(GPS.GPSVersionID) === 'array') {
  7850  					GPS.GPSVersionID = GPS.GPSVersionID.join('.');
  7851  				}
  7852  
  7853  				return GPS;
  7854  			},
  7855  
  7856  			setExif: function(tag, value) {
  7857  				// Right now only setting of width/height is possible
  7858  				if (tag !== 'PixelXDimension' && tag !== 'PixelYDimension') {return false;}
  7859  
  7860  				return setTag('exif', tag, value);
  7861  			},
  7862  
  7863  
  7864  			getBinary: function() {
  7865  				return data.SEGMENT();
  7866  			},
  7867  
  7868  			purge: function() {
  7869  				data.init(null);
  7870  				data = Tiff = null;
  7871  				offsets = {};
  7872  			}
  7873  		};
  7874  	};
  7875  });
  7876  
  7877  // Included from: src/javascript/runtime/html5/image/JPEG.js
  7878  
  7879  /**
  7880   * JPEG.js
  7881   *
  7882   * Copyright 2013, Moxiecode Systems AB
  7883   * Released under GPL License.
  7884   *
  7885   * License: http://www.plupload.com/license
  7886   * Contributing: http://www.plupload.com/contributing
  7887   */
  7888  
  7889  /**
  7890  @class moxie/runtime/html5/image/JPEG
  7891  @private
  7892  */
  7893  define("moxie/runtime/html5/image/JPEG", [
  7894  	"moxie/core/utils/Basic",
  7895  	"moxie/core/Exceptions",
  7896  	"moxie/runtime/html5/image/JPEGHeaders",
  7897  	"moxie/runtime/html5/utils/BinaryReader",
  7898  	"moxie/runtime/html5/image/ExifParser"
  7899  ], function(Basic, x, JPEGHeaders, BinaryReader, ExifParser) {
  7900  	
  7901  	function JPEG(binstr) {
  7902  		var _binstr, _br, _hm, _ep, _info, hasExif;
  7903  
  7904  		function _getDimensions() {
  7905  			var idx = 0, marker, length;
  7906  
  7907  			// examine all through the end, since some images might have very large APP segments
  7908  			while (idx <= _binstr.length) {
  7909  				marker = _br.SHORT(idx += 2);
  7910  
  7911  				if (marker >= 0xFFC0 && marker <= 0xFFC3) { // SOFn
  7912  					idx += 5; // marker (2 bytes) + length (2 bytes) + Sample precision (1 byte)
  7913  					return {
  7914  						height: _br.SHORT(idx),
  7915  						width: _br.SHORT(idx += 2)
  7916  					};
  7917  				}
  7918  				length = _br.SHORT(idx += 2);
  7919  				idx += length - 2;
  7920  			}
  7921  			return null;
  7922  		}
  7923  
  7924  		_binstr = binstr;
  7925  
  7926  		_br = new BinaryReader();
  7927  		_br.init(_binstr);
  7928  
  7929  		// check if it is jpeg
  7930  		if (_br.SHORT(0) !== 0xFFD8) {
  7931  			throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  7932  		}
  7933  
  7934  		// backup headers
  7935  		_hm = new JPEGHeaders(binstr);
  7936  
  7937  		// extract exif info
  7938  		_ep = new ExifParser();
  7939  		hasExif = !!_ep.init(_hm.get('app1')[0]);
  7940  
  7941  		// get dimensions
  7942  		_info = _getDimensions.call(this);
  7943  
  7944  		Basic.extend(this, {
  7945  			type: 'image/jpeg',
  7946  
  7947  			size: _binstr.length,
  7948  
  7949  			width: _info && _info.width || 0,
  7950  
  7951  			height: _info && _info.height || 0,
  7952  
  7953  			setExif: function(tag, value) {
  7954  				if (!hasExif) {
  7955  					return false; // or throw an exception
  7956  				}
  7957  
  7958  				if (Basic.typeOf(tag) === 'object') {
  7959  					Basic.each(tag, function(value, tag) {
  7960  						_ep.setExif(tag, value);
  7961  					});
  7962  				} else {
  7963  					_ep.setExif(tag, value);
  7964  				}
  7965  
  7966  				// update internal headers
  7967  				_hm.set('app1', _ep.getBinary());
  7968  			},
  7969  
  7970  			writeHeaders: function() {
  7971  				if (!arguments.length) {
  7972  					// if no arguments passed, update headers internally
  7973  					return (_binstr = _hm.restore(_binstr));
  7974  				}
  7975  				return _hm.restore(arguments[0]);
  7976  			},
  7977  
  7978  			stripHeaders: function(binstr) {
  7979  				return _hm.strip(binstr);
  7980  			},
  7981  
  7982  			purge: function() {
  7983  				_purge.call(this);
  7984  			}
  7985  		});
  7986  
  7987  		if (hasExif) {
  7988  			this.meta = {
  7989  				tiff: _ep.TIFF(),
  7990  				exif: _ep.EXIF(),
  7991  				gps: _ep.GPS()
  7992  			};
  7993  		}
  7994  
  7995  		function _purge() {
  7996  			if (!_ep || !_hm || !_br) { 
  7997  				return; // ignore any repeating purge requests
  7998  			}
  7999  			_ep.purge();
  8000  			_hm.purge();
  8001  			_br.init(null);
  8002  			_binstr = _info = _hm = _ep = _br = null;
  8003  		}
  8004  	}
  8005  
  8006  	return JPEG;
  8007  });
  8008  
  8009  // Included from: src/javascript/runtime/html5/image/PNG.js
  8010  
  8011  /**
  8012   * PNG.js
  8013   *
  8014   * Copyright 2013, Moxiecode Systems AB
  8015   * Released under GPL License.
  8016   *
  8017   * License: http://www.plupload.com/license
  8018   * Contributing: http://www.plupload.com/contributing
  8019   */
  8020  
  8021  /**
  8022  @class moxie/runtime/html5/image/PNG
  8023  @private
  8024  */
  8025  define("moxie/runtime/html5/image/PNG", [
  8026  	"moxie/core/Exceptions",
  8027  	"moxie/core/utils/Basic",
  8028  	"moxie/runtime/html5/utils/BinaryReader"
  8029  ], function(x, Basic, BinaryReader) {
  8030  	
  8031  	function PNG(binstr) {
  8032  		var _binstr, _br, _hm, _ep, _info;
  8033  
  8034  		_binstr = binstr;
  8035  
  8036  		_br = new BinaryReader();
  8037  		_br.init(_binstr);
  8038  
  8039  		// check if it's png
  8040  		(function() {
  8041  			var idx = 0, i = 0
  8042  			, signature = [0x8950, 0x4E47, 0x0D0A, 0x1A0A]
  8043  			;
  8044  
  8045  			for (i = 0; i < signature.length; i++, idx += 2) {
  8046  				if (signature[i] != _br.SHORT(idx)) {
  8047  					throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  8048  				}
  8049  			}
  8050  		}());
  8051  
  8052  		function _getDimensions() {
  8053  			var chunk, idx;
  8054  
  8055  			chunk = _getChunkAt.call(this, 8);
  8056  
  8057  			if (chunk.type == 'IHDR') {
  8058  				idx = chunk.start;
  8059  				return {
  8060  					width: _br.LONG(idx),
  8061  					height: _br.LONG(idx += 4)
  8062  				};
  8063  			}
  8064  			return null;
  8065  		}
  8066  
  8067  		function _purge() {
  8068  			if (!_br) {
  8069  				return; // ignore any repeating purge requests
  8070  			}
  8071  			_br.init(null);
  8072  			_binstr = _info = _hm = _ep = _br = null;
  8073  		}
  8074  
  8075  		_info = _getDimensions.call(this);
  8076  
  8077  		Basic.extend(this, {
  8078  			type: 'image/png',
  8079  
  8080  			size: _binstr.length,
  8081  
  8082  			width: _info.width,
  8083  
  8084  			height: _info.height,
  8085  
  8086  			purge: function() {
  8087  				_purge.call(this);
  8088  			}
  8089  		});
  8090  
  8091  		// for PNG we can safely trigger purge automatically, as we do not keep any data for later
  8092  		_purge.call(this);
  8093  
  8094  		function _getChunkAt(idx) {
  8095  			var length, type, start, CRC;
  8096  
  8097  			length = _br.LONG(idx);
  8098  			type = _br.STRING(idx += 4, 4);
  8099  			start = idx += 4;
  8100  			CRC = _br.LONG(idx + length);
  8101  
  8102  			return {
  8103  				length: length,
  8104  				type: type,
  8105  				start: start,
  8106  				CRC: CRC
  8107  			};
  8108  		}
  8109  	}
  8110  
  8111  	return PNG;
  8112  });
  8113  
  8114  // Included from: src/javascript/runtime/html5/image/ImageInfo.js
  8115  
  8116  /**
  8117   * ImageInfo.js
  8118   *
  8119   * Copyright 2013, Moxiecode Systems AB
  8120   * Released under GPL License.
  8121   *
  8122   * License: http://www.plupload.com/license
  8123   * Contributing: http://www.plupload.com/contributing
  8124   */
  8125  
  8126  /**
  8127  @class moxie/runtime/html5/image/ImageInfo
  8128  @private
  8129  */
  8130  define("moxie/runtime/html5/image/ImageInfo", [
  8131  	"moxie/core/utils/Basic",
  8132  	"moxie/core/Exceptions",
  8133  	"moxie/runtime/html5/image/JPEG",
  8134  	"moxie/runtime/html5/image/PNG"
  8135  ], function(Basic, x, JPEG, PNG) {
  8136  	/**
  8137  	Optional image investigation tool for HTML5 runtime. Provides the following features:
  8138  	- ability to distinguish image type (JPEG or PNG) by signature
  8139  	- ability to extract image width/height directly from it's internals, without preloading in memory (fast)
  8140  	- ability to extract APP headers from JPEGs (Exif, GPS, etc)
  8141  	- ability to replace width/height tags in extracted JPEG headers
  8142  	- ability to restore APP headers, that were for example stripped during image manipulation
  8143  
  8144  	@class ImageInfo
  8145  	@constructor
  8146  	@param {String} binstr Image source as binary string
  8147  	*/
  8148  	return function(binstr) {
  8149  		var _cs = [JPEG, PNG], _img;
  8150  
  8151  		// figure out the format, throw: ImageError.WRONG_FORMAT if not supported
  8152  		_img = (function() {
  8153  			for (var i = 0; i < _cs.length; i++) {
  8154  				try {
  8155  					return new _cs[i](binstr);
  8156  				} catch (ex) {
  8157  					// console.info(ex);
  8158  				}
  8159  			}
  8160  			throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  8161  		}());
  8162  
  8163  		Basic.extend(this, {
  8164  			/**
  8165  			Image Mime Type extracted from it's depths
  8166  
  8167  			@property type
  8168  			@type {String}
  8169  			@default ''
  8170  			*/
  8171  			type: '',
  8172  
  8173  			/**
  8174  			Image size in bytes
  8175  
  8176  			@property size
  8177  			@type {Number}
  8178  			@default 0
  8179  			*/
  8180  			size: 0,
  8181  
  8182  			/**
  8183  			Image width extracted from image source
  8184  
  8185  			@property width
  8186  			@type {Number}
  8187  			@default 0
  8188  			*/
  8189  			width: 0,
  8190  
  8191  			/**
  8192  			Image height extracted from image source
  8193  
  8194  			@property height
  8195  			@type {Number}
  8196  			@default 0
  8197  			*/
  8198  			height: 0,
  8199  
  8200  			/**
  8201  			Sets Exif tag. Currently applicable only for width and height tags. Obviously works only with JPEGs.
  8202  
  8203  			@method setExif
  8204  			@param {String} tag Tag to set
  8205  			@param {Mixed} value Value to assign to the tag
  8206  			*/
  8207  			setExif: function() {},
  8208  
  8209  			/**
  8210  			Restores headers to the source.
  8211  
  8212  			@method writeHeaders
  8213  			@param {String} data Image source as binary string
  8214  			@return {String} Updated binary string
  8215  			*/
  8216  			writeHeaders: function(data) {
  8217  				return data;
  8218  			},
  8219  
  8220  			/**
  8221  			Strip all headers from the source.
  8222  
  8223  			@method stripHeaders
  8224  			@param {String} data Image source as binary string
  8225  			@return {String} Updated binary string
  8226  			*/
  8227  			stripHeaders: function(data) {
  8228  				return data;
  8229  			},
  8230  
  8231  			/**
  8232  			Dispose resources.
  8233  
  8234  			@method purge
  8235  			*/
  8236  			purge: function() {}
  8237  		});
  8238  
  8239  		Basic.extend(this, _img);
  8240  
  8241  		this.purge = function() {
  8242  			_img.purge();
  8243  			_img = null;
  8244  		};
  8245  	};
  8246  });
  8247  
  8248  // Included from: src/javascript/runtime/html5/image/MegaPixel.js
  8249  
  8250  /**
  8251  (The MIT License)
  8252  
  8253  Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>;
  8254  
  8255  Permission is hereby granted, free of charge, to any person obtaining
  8256  a copy of this software and associated documentation files (the
  8257  'Software'), to deal in the Software without restriction, including
  8258  without limitation the rights to use, copy, modify, merge, publish,
  8259  distribute, sublicense, and/or sell copies of the Software, and to
  8260  permit persons to whom the Software is furnished to do so, subject to
  8261  the following conditions:
  8262  
  8263  The above copyright notice and this permission notice shall be
  8264  included in all copies or substantial portions of the Software.
  8265  
  8266  THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
  8267  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  8268  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  8269  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  8270  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  8271  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  8272  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  8273  */
  8274  
  8275  /**
  8276   * Mega pixel image rendering library for iOS6 Safari
  8277   *
  8278   * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel),
  8279   * which causes unexpected subsampling when drawing it in canvas.
  8280   * By using this library, you can safely render the image with proper stretching.
  8281   *
  8282   * Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>
  8283   * Released under the MIT license
  8284   */
  8285  
  8286  /**
  8287  @class moxie/runtime/html5/image/MegaPixel
  8288  @private
  8289  */
  8290  define("moxie/runtime/html5/image/MegaPixel", [], function() {
  8291  
  8292  	/**
  8293  	 * Rendering image element (with resizing) into the canvas element
  8294  	 */
  8295  	function renderImageToCanvas(img, canvas, options) {
  8296  		var iw = img.naturalWidth, ih = img.naturalHeight;
  8297  		var width = options.width, height = options.height;
  8298  		var x = options.x || 0, y = options.y || 0;
  8299  		var ctx = canvas.getContext('2d');
  8300  		if (detectSubsampling(img)) {
  8301  			iw /= 2;
  8302  			ih /= 2;
  8303  		}
  8304  		var d = 1024; // size of tiling canvas
  8305  		var tmpCanvas = document.createElement('canvas');
  8306  		tmpCanvas.width = tmpCanvas.height = d;
  8307  		var tmpCtx = tmpCanvas.getContext('2d');
  8308  		var vertSquashRatio = detectVerticalSquash(img, iw, ih);
  8309  		var sy = 0;
  8310  		while (sy < ih) {
  8311  			var sh = sy + d > ih ? ih - sy : d;
  8312  			var sx = 0;
  8313  			while (sx < iw) {
  8314  				var sw = sx + d > iw ? iw - sx : d;
  8315  				tmpCtx.clearRect(0, 0, d, d);
  8316  				tmpCtx.drawImage(img, -sx, -sy);
  8317  				var dx = (sx * width / iw + x) << 0;
  8318  				var dw = Math.ceil(sw * width / iw);
  8319  				var dy = (sy * height / ih / vertSquashRatio + y) << 0;
  8320  				var dh = Math.ceil(sh * height / ih / vertSquashRatio);
  8321  				ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
  8322  				sx += d;
  8323  			}
  8324  			sy += d;
  8325  		}
  8326  		tmpCanvas = tmpCtx = null;
  8327  	}
  8328  
  8329  	/**
  8330  	 * Detect subsampling in loaded image.
  8331  	 * In iOS, larger images than 2M pixels may be subsampled in rendering.
  8332  	 */
  8333  	function detectSubsampling(img) {
  8334  		var iw = img.naturalWidth, ih = img.naturalHeight;
  8335  		if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image
  8336  			var canvas = document.createElement('canvas');
  8337  			canvas.width = canvas.height = 1;
  8338  			var ctx = canvas.getContext('2d');
  8339  			ctx.drawImage(img, -iw + 1, 0);
  8340  			// subsampled image becomes half smaller in rendering size.
  8341  			// check alpha channel value to confirm image is covering edge pixel or not.
  8342  			// if alpha value is 0 image is not covering, hence subsampled.
  8343  			return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
  8344  		} else {
  8345  			return false;
  8346  		}
  8347  	}
  8348  
  8349  
  8350  	/**
  8351  	 * Detecting vertical squash in loaded image.
  8352  	 * Fixes a bug which squash image vertically while drawing into canvas for some images.
  8353  	 */
  8354  	function detectVerticalSquash(img, iw, ih) {
  8355  		var canvas = document.createElement('canvas');
  8356  		canvas.width = 1;
  8357  		canvas.height = ih;
  8358  		var ctx = canvas.getContext('2d');
  8359  		ctx.drawImage(img, 0, 0);
  8360  		var data = ctx.getImageData(0, 0, 1, ih).data;
  8361  		// search image edge pixel position in case it is squashed vertically.
  8362  		var sy = 0;
  8363  		var ey = ih;
  8364  		var py = ih;
  8365  		while (py > sy) {
  8366  			var alpha = data[(py - 1) * 4 + 3];
  8367  			if (alpha === 0) {
  8368  				ey = py;
  8369  			} else {
  8370  			sy = py;
  8371  			}
  8372  			py = (ey + sy) >> 1;
  8373  		}
  8374  		canvas = null;
  8375  		var ratio = (py / ih);
  8376  		return (ratio === 0) ? 1 : ratio;
  8377  	}
  8378  
  8379  	return {
  8380  		isSubsampled: detectSubsampling,
  8381  		renderTo: renderImageToCanvas
  8382  	};
  8383  });
  8384  
  8385  // Included from: src/javascript/runtime/html5/image/Image.js
  8386  
  8387  /**
  8388   * Image.js
  8389   *
  8390   * Copyright 2013, Moxiecode Systems AB
  8391   * Released under GPL License.
  8392   *
  8393   * License: http://www.plupload.com/license
  8394   * Contributing: http://www.plupload.com/contributing
  8395   */
  8396  
  8397  /**
  8398  @class moxie/runtime/html5/image/Image
  8399  @private
  8400  */
  8401  define("moxie/runtime/html5/image/Image", [
  8402  	"moxie/runtime/html5/Runtime",
  8403  	"moxie/core/utils/Basic",
  8404  	"moxie/core/Exceptions",
  8405  	"moxie/core/utils/Encode",
  8406  	"moxie/file/File",
  8407  	"moxie/runtime/html5/image/ImageInfo",
  8408  	"moxie/runtime/html5/image/MegaPixel",
  8409  	"moxie/core/utils/Mime",
  8410  	"moxie/core/utils/Env"
  8411  ], function(extensions, Basic, x, Encode, File, ImageInfo, MegaPixel, Mime, Env) {
  8412  	
  8413  	function HTML5Image() {
  8414  		var me = this
  8415  		, _img, _imgInfo, _canvas, _binStr, _blob
  8416  		, _modified = false // is set true whenever image is modified
  8417  		, _preserveHeaders = true
  8418  		;
  8419  
  8420  		Basic.extend(this, {
  8421  			loadFromBlob: function(blob) {
  8422  				var comp = this, I = comp.getRuntime()
  8423  				, asBinary = arguments.length > 1 ? arguments[1] : true
  8424  				;
  8425  
  8426  				if (!I.can('access_binary')) {
  8427  					throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  8428  				}
  8429  
  8430  				_blob = blob;
  8431  
  8432  				if (blob.isDetached()) {
  8433  					_binStr = blob.getSource();
  8434  					_preload.call(this, _binStr);
  8435  					return;
  8436  				} else {
  8437  					_readAsDataUrl.call(this, blob.getSource(), function(dataUrl) {
  8438  						if (asBinary) {
  8439  							_binStr = _toBinary(dataUrl);
  8440  						}
  8441  						_preload.call(comp, dataUrl);
  8442  					});
  8443  				}
  8444  			},
  8445  
  8446  			loadFromImage: function(img, exact) {
  8447  				this.meta = img.meta;
  8448  
  8449  				_blob = new File(null, {
  8450  					name: img.name,
  8451  					size: img.size,
  8452  					type: img.type
  8453  				});
  8454  
  8455  				_preload.call(this, exact ? (_binStr = img.getAsBinaryString()) : img.getAsDataURL());
  8456  			},
  8457  
  8458  			getInfo: function() {
  8459  				var I = this.getRuntime(), info;
  8460  
  8461  				if (!_imgInfo && _binStr && I.can('access_image_binary')) {
  8462  					_imgInfo = new ImageInfo(_binStr);
  8463  				}
  8464  
  8465  				info = {
  8466  					width: _getImg().width || 0,
  8467  					height: _getImg().height || 0,
  8468  					type: _blob.type || Mime.getFileMime(_blob.name),
  8469  					size: _binStr && _binStr.length || _blob.size || 0,
  8470  					name: _blob.name || '',
  8471  					meta: _imgInfo && _imgInfo.meta || this.meta || {}
  8472  				};
  8473  
  8474  				return info;
  8475  			},
  8476  
  8477  			downsize: function() {
  8478  				_downsize.apply(this, arguments);
  8479  			},
  8480  
  8481  			getAsCanvas: function() {
  8482  				if (_canvas) {
  8483  					_canvas.id = this.uid + '_canvas';
  8484  				}
  8485  				return _canvas;
  8486  			},
  8487  
  8488  			getAsBlob: function(type, quality) {
  8489  				if (type !== this.type) {
  8490  					// if different mime type requested prepare image for conversion
  8491  					_downsize.call(this, this.width, this.height, false);
  8492  				}
  8493  				return new File(null, {
  8494  					name: _blob.name || '',
  8495  					type: type,
  8496  					data: me.getAsBinaryString.call(this, type, quality)
  8497  				});
  8498  			},
  8499  
  8500  			getAsDataURL: function(type) {
  8501  				var quality = arguments[1] || 90;
  8502  
  8503  				// if image has not been modified, return the source right away
  8504  				if (!_modified) {
  8505  					return _img.src;
  8506  				}
  8507  
  8508  				if ('image/jpeg' !== type) {
  8509  					return _canvas.toDataURL('image/png');
  8510  				} else {
  8511  					try {
  8512  						// older Geckos used to result in an exception on quality argument
  8513  						return _canvas.toDataURL('image/jpeg', quality/100);
  8514  					} catch (ex) {
  8515  						return _canvas.toDataURL('image/jpeg');
  8516  					}
  8517  				}
  8518  			},
  8519  
  8520  			getAsBinaryString: function(type, quality) {
  8521  				// if image has not been modified, return the source right away
  8522  				if (!_modified) {
  8523  					// if image was not loaded from binary string
  8524  					if (!_binStr) {
  8525  						_binStr = _toBinary(me.getAsDataURL(type, quality));
  8526  					}
  8527  					return _binStr;
  8528  				}
  8529  
  8530  				if ('image/jpeg' !== type) {
  8531  					_binStr = _toBinary(me.getAsDataURL(type, quality));
  8532  				} else {
  8533  					var dataUrl;
  8534  
  8535  					// if jpeg
  8536  					if (!quality) {
  8537  						quality = 90;
  8538  					}
  8539  
  8540  					try {
  8541  						// older Geckos used to result in an exception on quality argument
  8542  						dataUrl = _canvas.toDataURL('image/jpeg', quality/100);
  8543  					} catch (ex) {
  8544  						dataUrl = _canvas.toDataURL('image/jpeg');
  8545  					}
  8546  
  8547  					_binStr = _toBinary(dataUrl);
  8548  
  8549  					if (_imgInfo) {
  8550  						_binStr = _imgInfo.stripHeaders(_binStr);
  8551  
  8552  						if (_preserveHeaders) {
  8553  							// update dimensions info in exif
  8554  							if (_imgInfo.meta && _imgInfo.meta.exif) {
  8555  								_imgInfo.setExif({
  8556  									PixelXDimension: this.width,
  8557  									PixelYDimension: this.height
  8558  								});
  8559  							}
  8560  
  8561  							// re-inject the headers
  8562  							_binStr = _imgInfo.writeHeaders(_binStr);
  8563  						}
  8564  
  8565  						// will be re-created from fresh on next getInfo call
  8566  						_imgInfo.purge();
  8567  						_imgInfo = null;
  8568  					}
  8569  				}
  8570  
  8571  				_modified = false;
  8572  
  8573  				return _binStr;
  8574  			},
  8575  
  8576  			destroy: function() {
  8577  				me = null;
  8578  				_purge.call(this);
  8579  				this.getRuntime().getShim().removeInstance(this.uid);
  8580  			}
  8581  		});
  8582  
  8583  
  8584  		function _getImg() {
  8585  			if (!_canvas && !_img) {
  8586  				throw new x.ImageError(x.DOMException.INVALID_STATE_ERR);
  8587  			}
  8588  			return _canvas || _img;
  8589  		}
  8590  
  8591  
  8592  		function _toBinary(str) {
  8593  			return Encode.atob(str.substring(str.indexOf('base64,') + 7));
  8594  		}
  8595  
  8596  
  8597  		function _toDataUrl(str, type) {
  8598  			return 'data:' + (type || '') + ';base64,' + Encode.btoa(str);
  8599  		}
  8600  
  8601  
  8602  		function _preload(str) {
  8603  			var comp = this;
  8604  
  8605  			_img = new Image();
  8606  			_img.onerror = function() {
  8607  				_purge.call(this);
  8608  				comp.trigger('error', new x.ImageError(x.ImageError.WRONG_FORMAT));
  8609  			};
  8610  			_img.onload = function() {
  8611  				comp.trigger('load');
  8612  			};
  8613  
  8614  			_img.src = /^data:[^;]*;base64,/.test(str) ? str : _toDataUrl(str, _blob.type);
  8615  		}
  8616  
  8617  
  8618  		function _readAsDataUrl(file, callback) {
  8619  			var comp = this, fr;
  8620  
  8621  			// use FileReader if it's available
  8622  			if (window.FileReader) {
  8623  				fr = new FileReader();
  8624  				fr.onload = function() {
  8625  					callback(this.result);
  8626  				};
  8627  				fr.onerror = function() {
  8628  					comp.trigger('error', new x.FileException(x.FileException.NOT_READABLE_ERR));
  8629  				};
  8630  				fr.readAsDataURL(file);
  8631  			} else {
  8632  				return callback(file.getAsDataURL());
  8633  			}
  8634  		}
  8635  
  8636  		function _downsize(width, height, crop, preserveHeaders) {
  8637  			var self = this
  8638  			, scale
  8639  			, mathFn
  8640  			, x = 0
  8641  			, y = 0
  8642  			, img
  8643  			, destWidth
  8644  			, destHeight
  8645  			, orientation
  8646  			;
  8647  
  8648  			_preserveHeaders = preserveHeaders; // we will need to check this on export (see getAsBinaryString())
  8649  
  8650  			// take into account orientation tag
  8651  			orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1;
  8652  
  8653  			if (Basic.inArray(orientation, [5,6,7,8]) !== -1) { // values that require 90 degree rotation
  8654  				// swap dimensions
  8655  				var tmp = width;
  8656  				width = height;
  8657  				height = tmp;
  8658  			}
  8659  
  8660  			img = _getImg();
  8661  
  8662  			// unify dimensions
  8663  			mathFn = !crop ? Math.min : Math.max;
  8664  			scale = mathFn(width/img.width, height/img.height);
  8665  		
  8666  			// we only downsize here
  8667  			if (scale > 1 && (!crop || preserveHeaders)) { // when cropping one of dimensions may still exceed max, so process it anyway
  8668  				this.trigger('Resize');
  8669  				return;
  8670  			}
  8671  
  8672  			// prepare canvas if necessary
  8673  			if (!_canvas) {
  8674  				_canvas = document.createElement("canvas");
  8675  			}
  8676  
  8677  			// calculate dimensions of proportionally resized image
  8678  			destWidth = Math.round(img.width * scale);	
  8679  			destHeight = Math.round(img.height * scale);
  8680  
  8681  
  8682  			// scale image and canvas
  8683  			if (crop) {
  8684  				_canvas.width = width;
  8685  				_canvas.height = height;
  8686  
  8687  				// if dimensions of the resulting image still larger than canvas, center it
  8688  				if (destWidth > width) {
  8689  					x = Math.round((destWidth - width) / 2);
  8690  				}
  8691  
  8692  				if (destHeight > height) {
  8693  					y = Math.round((destHeight - height) / 2);
  8694  				}
  8695  			} else {
  8696  				_canvas.width = destWidth;
  8697  				_canvas.height = destHeight;
  8698  			}
  8699  
  8700  			// rotate if required, according to orientation tag
  8701  			if (!_preserveHeaders) {
  8702  				_rotateToOrientaion(_canvas.width, _canvas.height, orientation);
  8703  			}
  8704  
  8705  			_drawToCanvas.call(this, img, _canvas, -x, -y, destWidth, destHeight);
  8706  
  8707  			this.width = _canvas.width;
  8708  			this.height = _canvas.height;
  8709  
  8710  			_modified = true;
  8711  			self.trigger('Resize');
  8712  		}
  8713  
  8714  
  8715  		function _drawToCanvas(img, canvas, x, y, w, h) {
  8716  			if (Env.OS === 'iOS') { 
  8717  				// avoid squish bug in iOS6
  8718  				MegaPixel.renderTo(img, canvas, { width: w, height: h, x: x, y: y });
  8719  			} else {
  8720  				var ctx = canvas.getContext('2d');
  8721  				ctx.drawImage(img, x, y, w, h);
  8722  			}
  8723  		}
  8724  
  8725  
  8726  		/**
  8727  		* Transform canvas coordination according to specified frame size and orientation
  8728  		* Orientation value is from EXIF tag
  8729  		* @author Shinichi Tomita <shinichi.tomita@gmail.com>
  8730  		*/
  8731  		function _rotateToOrientaion(width, height, orientation) {
  8732  			switch (orientation) {
  8733  				case 5:
  8734  				case 6:
  8735  				case 7:
  8736  				case 8:
  8737  					_canvas.width = height;
  8738  					_canvas.height = width;
  8739  					break;
  8740  				default:
  8741  					_canvas.width = width;
  8742  					_canvas.height = height;
  8743  			}
  8744  
  8745  			/**
  8746  			1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
  8747  			2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
  8748  			3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
  8749  			4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
  8750  			5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
  8751  			6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
  8752  			7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
  8753  			8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
  8754  			*/
  8755  
  8756  			var ctx = _canvas.getContext('2d');
  8757  			switch (orientation) {
  8758  				case 2:
  8759  					// horizontal flip
  8760  					ctx.translate(width, 0);
  8761  					ctx.scale(-1, 1);
  8762  					break;
  8763  				case 3:
  8764  					// 180 rotate left
  8765  					ctx.translate(width, height);
  8766  					ctx.rotate(Math.PI);
  8767  					break;
  8768  				case 4:
  8769  					// vertical flip
  8770  					ctx.translate(0, height);
  8771  					ctx.scale(1, -1);
  8772  					break;
  8773  				case 5:
  8774  					// vertical flip + 90 rotate right
  8775  					ctx.rotate(0.5 * Math.PI);
  8776  					ctx.scale(1, -1);
  8777  					break;
  8778  				case 6:
  8779  					// 90 rotate right
  8780  					ctx.rotate(0.5 * Math.PI);
  8781  					ctx.translate(0, -height);
  8782  					break;
  8783  				case 7:
  8784  					// horizontal flip + 90 rotate right
  8785  					ctx.rotate(0.5 * Math.PI);
  8786  					ctx.translate(width, -height);
  8787  					ctx.scale(-1, 1);
  8788  					break;
  8789  				case 8:
  8790  					// 90 rotate left
  8791  					ctx.rotate(-0.5 * Math.PI);
  8792  					ctx.translate(-width, 0);
  8793  					break;
  8794  			}
  8795  		}
  8796  
  8797  
  8798  		function _purge() {
  8799  			if (_imgInfo) {
  8800  				_imgInfo.purge();
  8801  				_imgInfo = null;
  8802  			}
  8803  			_binStr = _img = _canvas = _blob = null;
  8804  			_modified = false;
  8805  		}
  8806  	}
  8807  
  8808  	return (extensions.Image = HTML5Image);
  8809  });
  8810  
  8811  // Included from: src/javascript/runtime/flash/Runtime.js
  8812  
  8813  /**
  8814   * Runtime.js
  8815   *
  8816   * Copyright 2013, Moxiecode Systems AB
  8817   * Released under GPL License.
  8818   *
  8819   * License: http://www.plupload.com/license
  8820   * Contributing: http://www.plupload.com/contributing
  8821   */
  8822  
  8823  /*global ActiveXObject:true */
  8824  
  8825  /**
  8826  Defines constructor for Flash runtime.
  8827  
  8828  @class moxie/runtime/flash/Runtime
  8829  @private
  8830  */
  8831  define("moxie/runtime/flash/Runtime", [
  8832  	"moxie/core/utils/Basic",
  8833  	"moxie/core/utils/Env",
  8834  	"moxie/core/utils/Dom",
  8835  	"moxie/core/Exceptions",
  8836  	"moxie/runtime/Runtime"
  8837  ], function(Basic, Env, Dom, x, Runtime) {
  8838  	
  8839  	var type = 'flash', extensions = {};
  8840  
  8841  	/**
  8842  	Get the version of the Flash Player
  8843  
  8844  	@method getShimVersion
  8845  	@private
  8846  	@return {Number} Flash Player version
  8847  	*/
  8848  	function getShimVersion() {
  8849  		var version;
  8850  
  8851  		try {
  8852  			version = navigator.plugins['Shockwave Flash'];
  8853  			version = version.description;
  8854  		} catch (e1) {
  8855  			try {
  8856  				version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
  8857  			} catch (e2) {
  8858  				version = '0.0';
  8859  			}
  8860  		}
  8861  		version = version.match(/\d+/g);
  8862  		return parseFloat(version[0] + '.' + version[1]);
  8863  	}
  8864  
  8865  	/**
  8866  	Constructor for the Flash Runtime
  8867  
  8868  	@class FlashRuntime
  8869  	@extends Runtime
  8870  	*/
  8871  	function FlashRuntime(options) {
  8872  		var I = this, initTimer;
  8873  
  8874  		options = Basic.extend({ swf_url: Env.swf_url }, options);
  8875  
  8876  		Runtime.call(this, options, type, {
  8877  			access_binary: function(value) {
  8878  				return value && I.mode === 'browser';
  8879  			},
  8880  			access_image_binary: function(value) {
  8881  				return value && I.mode === 'browser';
  8882  			},
  8883  			display_media: Runtime.capTrue,
  8884  			do_cors: Runtime.capTrue,
  8885  			drag_and_drop: false,
  8886  			report_upload_progress: function() {
  8887  				return I.mode === 'client';
  8888  			},
  8889  			resize_image: Runtime.capTrue,
  8890  			return_response_headers: false,
  8891  			return_response_type: function(responseType) {
  8892  				if (responseType === 'json' && !!window.JSON) {
  8893  					return true;
  8894  				} 
  8895  				return !Basic.arrayDiff(responseType, ['', 'text', 'document']) || I.mode === 'browser';
  8896  			},
  8897  			return_status_code: function(code) {
  8898  				return I.mode === 'browser' || !Basic.arrayDiff(code, [200, 404]);
  8899  			},
  8900  			select_file: Runtime.capTrue,
  8901  			select_multiple: Runtime.capTrue,
  8902  			send_binary_string: function(value) {
  8903  				return value && I.mode === 'browser';
  8904  			},
  8905  			send_browser_cookies: function(value) {
  8906  				return value && I.mode === 'browser';
  8907  			},
  8908  			send_custom_headers: function(value) {
  8909  				return value && I.mode === 'browser';
  8910  			},
  8911  			send_multipart: Runtime.capTrue,
  8912  			slice_blob: Runtime.capTrue,
  8913  			stream_upload: function(value) {
  8914  				return value && I.mode === 'browser';
  8915  			},
  8916  			summon_file_dialog: false,
  8917  			upload_filesize: function(size) {
  8918  				return Basic.parseSizeStr(size) <= 2097152 || I.mode === 'client';
  8919  			},
  8920  			use_http_method: function(methods) {
  8921  				return !Basic.arrayDiff(methods, ['GET', 'POST']);
  8922  			}
  8923  		}, { 
  8924  			// capabilities that require specific mode
  8925  			access_binary: function(value) {
  8926  				return value ? 'browser' : 'client';
  8927  			},
  8928  			access_image_binary: function(value) {
  8929  				return value ? 'browser' : 'client';
  8930  			},
  8931  			report_upload_progress: function(value) {
  8932  				return value ? 'browser' : 'client';
  8933  			},
  8934  			return_response_type: function(responseType) {
  8935  				return Basic.arrayDiff(responseType, ['', 'text', 'json', 'document']) ? 'browser' : ['client', 'browser'];
  8936  			},
  8937  			return_status_code: function(code) {
  8938  				return Basic.arrayDiff(code, [200, 404]) ? 'browser' : ['client', 'browser'];
  8939  			},
  8940  			send_binary_string: function(value) {
  8941  				return value ? 'browser' : 'client';
  8942  			},
  8943  			send_browser_cookies: function(value) {
  8944  				return value ? 'browser' : 'client';
  8945  			},
  8946  			send_custom_headers: function(value) {
  8947  				return value ? 'browser' : 'client';
  8948  			},
  8949  			stream_upload: function(value) {
  8950  				return value ? 'client' : 'browser';
  8951  			},
  8952  			upload_filesize: function(size) {
  8953  				return Basic.parseSizeStr(size) >= 2097152 ? 'client' : 'browser';
  8954  			}
  8955  		}, 'client');
  8956  
  8957  
  8958  		// minimal requirement for Flash Player version
  8959  		if (getShimVersion() < 10) {
  8960  			this.mode = false; // with falsy mode, runtime won't operable, no matter what the mode was before
  8961  		}
  8962  
  8963  
  8964  		Basic.extend(this, {
  8965  
  8966  			getShim: function() {
  8967  				return Dom.get(this.uid);
  8968  			},
  8969  
  8970  			shimExec: function(component, action) {
  8971  				var args = [].slice.call(arguments, 2);
  8972  				return I.getShim().exec(this.uid, component, action, args);
  8973  			},
  8974  
  8975  			init: function() {
  8976  				var html, el, container;
  8977  
  8978  				container = this.getShimContainer();
  8979  
  8980  				// 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)
  8981  				Basic.extend(container.style, {
  8982  					position: 'absolute',
  8983  					top: '-8px',
  8984  					left: '-8px',
  8985  					width: '9px',
  8986  					height: '9px',
  8987  					overflow: 'hidden'
  8988  				});
  8989  
  8990  				// insert flash object
  8991  				html = '<object id="' + this.uid + '" type="application/x-shockwave-flash" data="' +  options.swf_url + '" ';
  8992  
  8993  				if (Env.browser === 'IE') {
  8994  					html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
  8995  				}
  8996  
  8997  				html += 'width="100%" height="100%" style="outline:0">'  +
  8998  					'<param name="movie" value="' + options.swf_url + '" />' +
  8999  					'<param name="flashvars" value="uid=' + escape(this.uid) + '&target=' + Env.global_event_dispatcher + '" />' +
  9000  					'<param name="wmode" value="transparent" />' +
  9001  					'<param name="allowscriptaccess" value="always" />' +
  9002  				'</object>';
  9003  
  9004  				if (Env.browser === 'IE') {
  9005  					el = document.createElement('div');
  9006  					container.appendChild(el);
  9007  					el.outerHTML = html;
  9008  					el = container = null; // just in case
  9009  				} else {
  9010  					container.innerHTML = html;
  9011  				}
  9012  
  9013  				// Init is dispatched by the shim
  9014  				initTimer = setTimeout(function() {
  9015  					if (I && !I.initialized) { // runtime might be already destroyed by this moment
  9016  						I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
  9017  					}
  9018  				}, 5000);
  9019  			},
  9020  
  9021  			destroy: (function(destroy) { // extend default destroy method
  9022  				return function() {
  9023  					destroy.call(I);
  9024  					clearTimeout(initTimer); // initialization check might be still onwait
  9025  					options = initTimer = destroy = I = null;
  9026  				};
  9027  			}(this.destroy))
  9028  
  9029  		}, extensions);
  9030  	}
  9031  
  9032  	Runtime.addConstructor(type, FlashRuntime);
  9033  
  9034  	return extensions;
  9035  });
  9036  
  9037  // Included from: src/javascript/runtime/flash/file/Blob.js
  9038  
  9039  /**
  9040   * Blob.js
  9041   *
  9042   * Copyright 2013, Moxiecode Systems AB
  9043   * Released under GPL License.
  9044   *
  9045   * License: http://www.plupload.com/license
  9046   * Contributing: http://www.plupload.com/contributing
  9047   */
  9048  
  9049  /**
  9050  @class moxie/runtime/flash/file/Blob
  9051  @private
  9052  */
  9053  define("moxie/runtime/flash/file/Blob", [
  9054  	"moxie/runtime/flash/Runtime",
  9055  	"moxie/file/Blob"
  9056  ], function(extensions, Blob) {
  9057  
  9058  	var FlashBlob = {
  9059  		slice: function(blob, start, end, type) {
  9060  			var self = this.getRuntime();
  9061  
  9062  			if (start < 0) {
  9063  				start = Math.max(blob.size + start, 0);
  9064  			} else if (start > 0) {
  9065  				start = Math.min(start, blob.size);
  9066  			}
  9067  
  9068  			if (end < 0) {
  9069  				end = Math.max(blob.size + end, 0);
  9070  			} else if (end > 0) {
  9071  				end = Math.min(end, blob.size);
  9072  			}
  9073  
  9074  			blob = self.shimExec.call(this, 'Blob', 'slice', start, end, type || '');
  9075  
  9076  			if (blob) {
  9077  				blob = new Blob(self.uid, blob);
  9078  			}
  9079  			return blob;
  9080  		}
  9081  	};
  9082  
  9083  	return (extensions.Blob = FlashBlob);
  9084  });
  9085  
  9086  // Included from: src/javascript/runtime/flash/file/FileInput.js
  9087  
  9088  /**
  9089   * FileInput.js
  9090   *
  9091   * Copyright 2013, Moxiecode Systems AB
  9092   * Released under GPL License.
  9093   *
  9094   * License: http://www.plupload.com/license
  9095   * Contributing: http://www.plupload.com/contributing
  9096   */
  9097  
  9098  /**
  9099  @class moxie/runtime/flash/file/FileInput
  9100  @private
  9101  */
  9102  define("moxie/runtime/flash/file/FileInput", [
  9103  	"moxie/runtime/flash/Runtime"
  9104  ], function(extensions) {
  9105  	
  9106  	var FileInput = {		
  9107  		init: function(options) {
  9108  			this.getRuntime().shimExec.call(this, 'FileInput', 'init', {
  9109  				name: options.name,
  9110  				accept: options.accept,
  9111  				multiple: options.multiple
  9112  			});
  9113  			this.trigger('ready');
  9114  		}
  9115  	};
  9116  
  9117  	return (extensions.FileInput = FileInput);
  9118  });
  9119  
  9120  // Included from: src/javascript/runtime/flash/file/FileReader.js
  9121  
  9122  /**
  9123   * FileReader.js
  9124   *
  9125   * Copyright 2013, Moxiecode Systems AB
  9126   * Released under GPL License.
  9127   *
  9128   * License: http://www.plupload.com/license
  9129   * Contributing: http://www.plupload.com/contributing
  9130   */
  9131  
  9132  /**
  9133  @class moxie/runtime/flash/file/FileReader
  9134  @private
  9135  */
  9136  define("moxie/runtime/flash/file/FileReader", [
  9137  	"moxie/runtime/flash/Runtime",
  9138  	"moxie/core/utils/Encode"
  9139  ], function(extensions, Encode) {
  9140  
  9141  	var _result = '';
  9142  
  9143  	function _formatData(data, op) {
  9144  		switch (op) {
  9145  			case 'readAsText':
  9146  				return Encode.atob(data, 'utf8');
  9147  			case 'readAsBinaryString':
  9148  				return Encode.atob(data);
  9149  			case 'readAsDataURL':
  9150  				return data;
  9151  		}
  9152  		return null;
  9153  	}
  9154  
  9155  	var FileReader = {
  9156  		read: function(op, blob) {
  9157  			var target = this, self = target.getRuntime();
  9158  
  9159  			// special prefix for DataURL read mode
  9160  			if (op === 'readAsDataURL') {
  9161  				_result = 'data:' + (blob.type || '') + ';base64,';
  9162  			}
  9163  
  9164  			target.bind('Progress', function(e, data) {
  9165  				if (data) {
  9166  					_result += _formatData(data, op);
  9167  				}
  9168  			});
  9169  
  9170  			return self.shimExec.call(this, 'FileReader', 'readAsBase64', blob.uid);
  9171  		},
  9172  
  9173  		getResult: function() {
  9174  			return _result;
  9175  		},
  9176  
  9177  		destroy: function() {
  9178  			_result = null;
  9179  		}
  9180  	};
  9181  
  9182  	return (extensions.FileReader = FileReader);
  9183  });
  9184  
  9185  // Included from: src/javascript/runtime/flash/file/FileReaderSync.js
  9186  
  9187  /**
  9188   * FileReaderSync.js
  9189   *
  9190   * Copyright 2013, Moxiecode Systems AB
  9191   * Released under GPL License.
  9192   *
  9193   * License: http://www.plupload.com/license
  9194   * Contributing: http://www.plupload.com/contributing
  9195   */
  9196  
  9197  /**
  9198  @class moxie/runtime/flash/file/FileReaderSync
  9199  @private
  9200  */
  9201  define("moxie/runtime/flash/file/FileReaderSync", [
  9202  	"moxie/runtime/flash/Runtime",
  9203  	"moxie/core/utils/Encode"
  9204  ], function(extensions, Encode) {
  9205  	
  9206  	function _formatData(data, op) {
  9207  		switch (op) {
  9208  			case 'readAsText':
  9209  				return Encode.atob(data, 'utf8');
  9210  			case 'readAsBinaryString':
  9211  				return Encode.atob(data);
  9212  			case 'readAsDataURL':
  9213  				return data;
  9214  		}
  9215  		return null;
  9216  	}
  9217  
  9218  	var FileReaderSync = {
  9219  		read: function(op, blob) {
  9220  			var result, self = this.getRuntime();
  9221  
  9222  			result = self.shimExec.call(this, 'FileReaderSync', 'readAsBase64', blob.uid);
  9223  			if (!result) {
  9224  				return null; // or throw ex
  9225  			}
  9226  
  9227  			// special prefix for DataURL read mode
  9228  			if (op === 'readAsDataURL') {
  9229  				result = 'data:' + (blob.type || '') + ';base64,' + result;
  9230  			}
  9231  
  9232  			return _formatData(result, op, blob.type);
  9233  		}
  9234  	};
  9235  
  9236  	return (extensions.FileReaderSync = FileReaderSync);
  9237  });
  9238  
  9239  // Included from: src/javascript/runtime/flash/xhr/XMLHttpRequest.js
  9240  
  9241  /**
  9242   * XMLHttpRequest.js
  9243   *
  9244   * Copyright 2013, Moxiecode Systems AB
  9245   * Released under GPL License.
  9246   *
  9247   * License: http://www.plupload.com/license
  9248   * Contributing: http://www.plupload.com/contributing
  9249   */
  9250  
  9251  /**
  9252  @class moxie/runtime/flash/xhr/XMLHttpRequest
  9253  @private
  9254  */
  9255  define("moxie/runtime/flash/xhr/XMLHttpRequest", [
  9256  	"moxie/runtime/flash/Runtime",
  9257  	"moxie/core/utils/Basic",
  9258  	"moxie/file/Blob",
  9259  	"moxie/file/File",
  9260  	"moxie/file/FileReaderSync",
  9261  	"moxie/xhr/FormData",
  9262  	"moxie/runtime/Transporter"
  9263  ], function(extensions, Basic, Blob, File, FileReaderSync, FormData, Transporter) {
  9264  	
  9265  	var XMLHttpRequest = {
  9266  
  9267  		send: function(meta, data) {
  9268  			var target = this, self = target.getRuntime();
  9269  
  9270  			function send() {
  9271  				meta.transport = self.mode;
  9272  				self.shimExec.call(target, 'XMLHttpRequest', 'send', meta, data);
  9273  			}
  9274  
  9275  
  9276  			function appendBlob(name, blob) {
  9277  				self.shimExec.call(target, 'XMLHttpRequest', 'appendBlob', name, blob.uid);
  9278  				data = null;
  9279  				send();
  9280  			}
  9281  
  9282  
  9283  			function attachBlob(blob, cb) {
  9284  				var tr = new Transporter();
  9285  
  9286  				tr.bind("TransportingComplete", function() {
  9287  					cb(this.result);
  9288  				});
  9289  
  9290  				tr.transport(blob.getSource(), blob.type, {
  9291  					ruid: self.uid
  9292  				});
  9293  			}
  9294  
  9295  			// copy over the headers if any
  9296  			if (!Basic.isEmptyObj(meta.headers)) {
  9297  				Basic.each(meta.headers, function(value, header) {
  9298  					self.shimExec.call(target, 'XMLHttpRequest', 'setRequestHeader', header, value.toString()); // Silverlight doesn't accept integers into the arguments of type object
  9299  				});
  9300  			}
  9301  
  9302  			// transfer over multipart params and blob itself
  9303  			if (data instanceof FormData) {
  9304  				var blobField;
  9305  				data.each(function(value, name) {
  9306  					if (value instanceof Blob) {
  9307  						blobField = name;
  9308  					} else {
  9309  						self.shimExec.call(target, 'XMLHttpRequest', 'append', name, value);
  9310  					}
  9311  				});
  9312  
  9313  				if (!data.hasBlob()) {
  9314  					data = null;
  9315  					send();
  9316  				} else {
  9317  					var blob = data.getBlob();
  9318  					if (blob.isDetached()) {
  9319  						attachBlob(blob, function(attachedBlob) {
  9320  							blob.destroy();
  9321  							appendBlob(blobField, attachedBlob);		
  9322  						});
  9323  					} else {
  9324  						appendBlob(blobField, blob);
  9325  					}
  9326  				}
  9327  			} else if (data instanceof Blob) {
  9328  				if (data.isDetached()) {
  9329  					attachBlob(data, function(attachedBlob) {
  9330  						data.destroy();
  9331  						data = attachedBlob.uid;
  9332  						send();
  9333  					});
  9334  				} else {
  9335  					data = data.uid;
  9336  					send();
  9337  				}
  9338  			} else {
  9339  				send();
  9340  			}
  9341  		},
  9342  
  9343  		getResponse: function(responseType) {
  9344  			var frs, blob, self = this.getRuntime();
  9345  
  9346  			blob = self.shimExec.call(this, 'XMLHttpRequest', 'getResponseAsBlob');
  9347  
  9348  			if (blob) {
  9349  				blob = new File(self.uid, blob);
  9350  
  9351  				if ('blob' === responseType) {
  9352  					return blob;
  9353  				}
  9354  
  9355  				try { 
  9356  					frs = new FileReaderSync();
  9357  
  9358  					if (!!~Basic.inArray(responseType, ["", "text"])) {
  9359  						return frs.readAsText(blob);
  9360  					} else if ('json' === responseType && !!window.JSON) {
  9361  						return JSON.parse(frs.readAsText(blob));
  9362  					}
  9363  				} finally {
  9364  					blob.destroy();
  9365  				}
  9366  			}
  9367  			return null;
  9368  		},
  9369  
  9370  		abort: function(upload_complete_flag) {
  9371  			var self = this.getRuntime();
  9372  
  9373  			self.shimExec.call(this, 'XMLHttpRequest', 'abort');
  9374  
  9375  			this.dispatchEvent('readystatechange');
  9376  			// this.dispatchEvent('progress');
  9377  			this.dispatchEvent('abort');
  9378  
  9379  			//if (!upload_complete_flag) {
  9380  				// this.dispatchEvent('uploadprogress');
  9381  			//}
  9382  		}
  9383  	};
  9384  
  9385  	return (extensions.XMLHttpRequest = XMLHttpRequest);
  9386  });
  9387  
  9388  // Included from: src/javascript/runtime/flash/runtime/Transporter.js
  9389  
  9390  /**
  9391   * Transporter.js
  9392   *
  9393   * Copyright 2013, Moxiecode Systems AB
  9394   * Released under GPL License.
  9395   *
  9396   * License: http://www.plupload.com/license
  9397   * Contributing: http://www.plupload.com/contributing
  9398   */
  9399  
  9400  /**
  9401  @class moxie/runtime/flash/runtime/Transporter
  9402  @private
  9403  */
  9404  define("moxie/runtime/flash/runtime/Transporter", [
  9405  	"moxie/runtime/flash/Runtime",
  9406  	"moxie/file/Blob"
  9407  ], function(extensions, Blob) {
  9408  
  9409  	var Transporter = {
  9410  		getAsBlob: function(type) {
  9411  			var self = this.getRuntime()
  9412  			, blob = self.shimExec.call(this, 'Transporter', 'getAsBlob', type)
  9413  			;
  9414  			if (blob) {
  9415  				return new Blob(self.uid, blob);
  9416  			}
  9417  			return null;
  9418  		}
  9419  	};
  9420  
  9421  	return (extensions.Transporter = Transporter);
  9422  });
  9423  
  9424  // Included from: src/javascript/runtime/flash/image/Image.js
  9425  
  9426  /**
  9427   * Image.js
  9428   *
  9429   * Copyright 2013, Moxiecode Systems AB
  9430   * Released under GPL License.
  9431   *
  9432   * License: http://www.plupload.com/license
  9433   * Contributing: http://www.plupload.com/contributing
  9434   */
  9435  
  9436  /**
  9437  @class moxie/runtime/flash/image/Image
  9438  @private
  9439  */
  9440  define("moxie/runtime/flash/image/Image", [
  9441  	"moxie/runtime/flash/Runtime",
  9442  	"moxie/core/utils/Basic",
  9443  	"moxie/runtime/Transporter",
  9444  	"moxie/file/Blob",
  9445  	"moxie/file/FileReaderSync"
  9446  ], function(extensions, Basic, Transporter, Blob, FileReaderSync) {
  9447  	
  9448  	var Image = {
  9449  		loadFromBlob: function(blob) {
  9450  			var comp = this, self = comp.getRuntime();
  9451  
  9452  			function exec(srcBlob) {
  9453  				self.shimExec.call(comp, 'Image', 'loadFromBlob', srcBlob.uid);
  9454  				comp = self = null;
  9455  			}
  9456  
  9457  			if (blob.isDetached()) { // binary string
  9458  				var tr = new Transporter();
  9459  				tr.bind("TransportingComplete", function() {
  9460  					exec(tr.result.getSource());
  9461  				});
  9462  				tr.transport(blob.getSource(), blob.type, { ruid: self.uid });
  9463  			} else {
  9464  				exec(blob.getSource());
  9465  			}
  9466  		},
  9467  
  9468  		loadFromImage: function(img) {
  9469  			var self = this.getRuntime();
  9470  			return self.shimExec.call(this, 'Image', 'loadFromImage', img.uid);
  9471  		},
  9472  
  9473  		getAsBlob: function(type, quality) {
  9474  			var self = this.getRuntime()
  9475  			, blob = self.shimExec.call(this, 'Image', 'getAsBlob', type, quality)
  9476  			;
  9477  			if (blob) {
  9478  				return new Blob(self.uid, blob);
  9479  			}
  9480  			return null;
  9481  		},
  9482  
  9483  		getAsDataURL: function() {
  9484  			var self = this.getRuntime()
  9485  			, blob = self.Image.getAsBlob.apply(this, arguments)
  9486  			, frs
  9487  			;
  9488  			if (!blob) {
  9489  				return null;
  9490  			}
  9491  			frs = new FileReaderSync();
  9492  			return frs.readAsDataURL(blob);
  9493  		}
  9494  	};
  9495  
  9496  	return (extensions.Image = Image);
  9497  });
  9498  
  9499  // Included from: src/javascript/runtime/silverlight/Runtime.js
  9500  
  9501  /**
  9502   * RunTime.js
  9503   *
  9504   * Copyright 2013, Moxiecode Systems AB
  9505   * Released under GPL License.
  9506   *
  9507   * License: http://www.plupload.com/license
  9508   * Contributing: http://www.plupload.com/contributing
  9509   */
  9510  
  9511  /*global ActiveXObject:true */
  9512  
  9513  /**
  9514  Defines constructor for Silverlight runtime.
  9515  
  9516  @class moxie/runtime/silverlight/Runtime
  9517  @private
  9518  */
  9519  define("moxie/runtime/silverlight/Runtime", [
  9520  	"moxie/core/utils/Basic",
  9521  	"moxie/core/utils/Env",
  9522  	"moxie/core/utils/Dom",
  9523  	"moxie/core/Exceptions",
  9524  	"moxie/runtime/Runtime"
  9525  ], function(Basic, Env, Dom, x, Runtime) {
  9526  	
  9527  	var type = "silverlight", extensions = {};
  9528  
  9529  	function isInstalled(version) {
  9530  		var isVersionSupported = false, control = null, actualVer,
  9531  			actualVerArray, reqVerArray, requiredVersionPart, actualVersionPart, index = 0;
  9532  
  9533  		try {
  9534  			try {
  9535  				control = new ActiveXObject('AgControl.AgControl');
  9536  
  9537  				if (control.IsVersionSupported(version)) {
  9538  					isVersionSupported = true;
  9539  				}
  9540  
  9541  				control = null;
  9542  			} catch (e) {
  9543  				var plugin = navigator.plugins["Silverlight Plug-In"];
  9544  
  9545  				if (plugin) {
  9546  					actualVer = plugin.description;
  9547  
  9548  					if (actualVer === "1.0.30226.2") {
  9549  						actualVer = "2.0.30226.2";
  9550  					}
  9551  
  9552  					actualVerArray = actualVer.split(".");
  9553  
  9554  					while (actualVerArray.length > 3) {
  9555  						actualVerArray.pop();
  9556  					}
  9557  
  9558  					while ( actualVerArray.length < 4) {
  9559  						actualVerArray.push(0);
  9560  					}
  9561  
  9562  					reqVerArray = version.split(".");
  9563  
  9564  					while (reqVerArray.length > 4) {
  9565  						reqVerArray.pop();
  9566  					}
  9567  
  9568  					do {
  9569  						requiredVersionPart = parseInt(reqVerArray[index], 10);
  9570  						actualVersionPart = parseInt(actualVerArray[index], 10);
  9571  						index++;
  9572  					} while (index < reqVerArray.length && requiredVersionPart === actualVersionPart);
  9573  
  9574  					if (requiredVersionPart <= actualVersionPart && !isNaN(requiredVersionPart)) {
  9575  						isVersionSupported = true;
  9576  					}
  9577  				}
  9578  			}
  9579  		} catch (e2) {
  9580  			isVersionSupported = false;
  9581  		}
  9582  
  9583  		return isVersionSupported;
  9584  	}
  9585  
  9586  	/**
  9587  	Constructor for the Silverlight Runtime
  9588  
  9589  	@class SilverlightRuntime
  9590  	@extends Runtime
  9591  	*/
  9592  	function SilverlightRuntime(options) {
  9593  		var I = this, initTimer;
  9594  
  9595  		options = Basic.extend({ xap_url: Env.xap_url }, options);
  9596  
  9597  		Runtime.call(this, options, type, {
  9598  			access_binary: Runtime.capTrue,
  9599  			access_image_binary: Runtime.capTrue,
  9600  			display_media: Runtime.capTrue,
  9601  			do_cors: Runtime.capTrue,
  9602  			drag_and_drop: false,
  9603  			report_upload_progress: Runtime.capTrue,
  9604  			resize_image: Runtime.capTrue,
  9605  			return_response_headers: function(value) {
  9606  				return value && I.mode === 'client';
  9607  			},
  9608  			return_response_type: function(responseType) {
  9609  				if (responseType !== 'json') {
  9610  					return true;
  9611  				} else {
  9612  					return !!window.JSON;
  9613  				}
  9614  			},
  9615  			return_status_code: function(code) {
  9616  				return I.mode === 'client' || !Basic.arrayDiff(code, [200, 404]);
  9617  			},
  9618  			select_file: Runtime.capTrue,
  9619  			select_multiple: Runtime.capTrue,
  9620  			send_binary_string: Runtime.capTrue,
  9621  			send_browser_cookies: function(value) {
  9622  				return value && I.mode === 'browser';
  9623  			},
  9624  			send_custom_headers: function(value) {
  9625  				return value && I.mode === 'client';
  9626  			},
  9627  			send_multipart: Runtime.capTrue,
  9628  			slice_blob: Runtime.capTrue,
  9629  			stream_upload: true,
  9630  			summon_file_dialog: false,
  9631  			upload_filesize: Runtime.capTrue,
  9632  			use_http_method: function(methods) {
  9633  				return I.mode === 'client' || !Basic.arrayDiff(methods, ['GET', 'POST']);
  9634  			}
  9635  		}, { 
  9636  			// capabilities that require specific mode
  9637  			return_response_headers: function(value) {
  9638  				return value ? 'client' : 'browser';
  9639  			},
  9640  			return_status_code: function(code) {
  9641  				return Basic.arrayDiff(code, [200, 404]) ? 'client' : ['client', 'browser'];
  9642  			},
  9643  			send_browser_cookies: function(value) {
  9644  				return value ? 'browser' : 'client';
  9645  			},
  9646  			send_custom_headers: function(value) {
  9647  				return value ? 'client' : 'browser';
  9648  			},
  9649  			use_http_method: function(methods) {
  9650  				return Basic.arrayDiff(methods, ['GET', 'POST']) ? 'client' : ['client', 'browser'];
  9651  			}
  9652  		});
  9653  
  9654  
  9655  		// minimal requirement
  9656  		if (!isInstalled('2.0.31005.0') || Env.browser === 'Opera') {
  9657  			this.mode = false;
  9658  		}
  9659  
  9660  
  9661  		Basic.extend(this, {
  9662  			getShim: function() {
  9663  				return Dom.get(this.uid).content.Moxie;
  9664  			},
  9665  
  9666  			shimExec: function(component, action) {
  9667  				var args = [].slice.call(arguments, 2);
  9668  				return I.getShim().exec(this.uid, component, action, args);
  9669  			},
  9670  
  9671  			init : function() {
  9672  				var container;
  9673  
  9674  				container = this.getShimContainer();
  9675  
  9676  				container.innerHTML = '<object id="' + this.uid + '" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">' +
  9677  					'<param name="source" value="' + options.xap_url + '"/>' +
  9678  					'<param name="background" value="Transparent"/>' +
  9679  					'<param name="windowless" value="true"/>' +
  9680  					'<param name="enablehtmlaccess" value="true"/>' +
  9681  					'<param name="initParams" value="uid=' + this.uid + ',target=' + Env.global_event_dispatcher + '"/>' +
  9682  				'</object>';
  9683  
  9684  				// Init is dispatched by the shim
  9685  				initTimer = setTimeout(function() {
  9686  					if (I && !I.initialized) { // runtime might be already destroyed by this moment
  9687  						I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
  9688  					}
  9689  				}, Env.OS !== 'Windows'? 10000 : 5000); // give it more time to initialize in non Windows OS (like Mac)
  9690  			},
  9691  
  9692  			destroy: (function(destroy) { // extend default destroy method
  9693  				return function() {
  9694  					destroy.call(I);
  9695  					clearTimeout(initTimer); // initialization check might be still onwait
  9696  					options = initTimer = destroy = I = null;
  9697  				};
  9698  			}(this.destroy))
  9699  
  9700  		}, extensions);
  9701  	}
  9702  
  9703  	Runtime.addConstructor(type, SilverlightRuntime); 
  9704  
  9705  	return extensions;
  9706  });
  9707  
  9708  // Included from: src/javascript/runtime/silverlight/file/Blob.js
  9709  
  9710  /**
  9711   * Blob.js
  9712   *
  9713   * Copyright 2013, Moxiecode Systems AB
  9714   * Released under GPL License.
  9715   *
  9716   * License: http://www.plupload.com/license
  9717   * Contributing: http://www.plupload.com/contributing
  9718   */
  9719  
  9720  /**
  9721  @class moxie/runtime/silverlight/file/Blob
  9722  @private
  9723  */
  9724  define("moxie/runtime/silverlight/file/Blob", [
  9725  	"moxie/runtime/silverlight/Runtime",
  9726  	"moxie/core/utils/Basic",
  9727  	"moxie/runtime/flash/file/Blob"
  9728  ], function(extensions, Basic, Blob) {
  9729  	return (extensions.Blob = Basic.extend({}, Blob));
  9730  });
  9731  
  9732  // Included from: src/javascript/runtime/silverlight/file/FileInput.js
  9733  
  9734  /**
  9735   * FileInput.js
  9736   *
  9737   * Copyright 2013, Moxiecode Systems AB
  9738   * Released under GPL License.
  9739   *
  9740   * License: http://www.plupload.com/license
  9741   * Contributing: http://www.plupload.com/contributing
  9742   */
  9743  
  9744  /**
  9745  @class moxie/runtime/silverlight/file/FileInput
  9746  @private
  9747  */
  9748  define("moxie/runtime/silverlight/file/FileInput", [
  9749  	"moxie/runtime/silverlight/Runtime"
  9750  ], function(extensions) {
  9751  	
  9752  	var FileInput = {
  9753  		init: function(options) {
  9754  
  9755  			function toFilters(accept) {
  9756  				var filter = '';
  9757  				for (var i = 0; i < accept.length; i++) {
  9758  					filter += (filter !== '' ? '|' : '') + accept[i].title + " | *." + accept[i].extensions.replace(/,/g, ';*.');
  9759  				}
  9760  				return filter;
  9761  			}
  9762  			
  9763  			this.getRuntime().shimExec.call(this, 'FileInput', 'init', toFilters(options.accept), options.name, options.multiple);
  9764  			this.trigger('ready');
  9765  		}
  9766  	};
  9767  
  9768  	return (extensions.FileInput = FileInput);
  9769  });
  9770  
  9771  // Included from: src/javascript/runtime/silverlight/file/FileDrop.js
  9772  
  9773  /**
  9774   * FileDrop.js
  9775   *
  9776   * Copyright 2013, Moxiecode Systems AB
  9777   * Released under GPL License.
  9778   *
  9779   * License: http://www.plupload.com/license
  9780   * Contributing: http://www.plupload.com/contributing
  9781   */
  9782  
  9783  /**
  9784  @class moxie/runtime/silverlight/file/FileDrop
  9785  @private
  9786  */
  9787  define("moxie/runtime/silverlight/file/FileDrop", [
  9788  	"moxie/runtime/silverlight/Runtime",
  9789  	"moxie/core/utils/Dom", 
  9790  	"moxie/core/utils/Events"
  9791  ], function(extensions, Dom, Events) {
  9792  
  9793  	// not exactly useful, since works only in safari (...crickets...)
  9794  	var FileDrop = {
  9795  		init: function() {
  9796  			var comp = this, self = comp.getRuntime(), dropZone;
  9797  
  9798  			dropZone = self.getShimContainer();
  9799  
  9800  			Events.addEvent(dropZone, 'dragover', function(e) {
  9801  				e.preventDefault();
  9802  				e.stopPropagation();
  9803  				e.dataTransfer.dropEffect = 'copy';
  9804  			}, comp.uid);
  9805  
  9806  			Events.addEvent(dropZone, 'dragenter', function(e) {
  9807  				e.preventDefault();
  9808  				var flag = Dom.get(self.uid).dragEnter(e);
  9809  				// If handled, then stop propagation of event in DOM
  9810  				if (flag) {
  9811  					e.stopPropagation();
  9812  				}
  9813  			}, comp.uid);
  9814  
  9815  			Events.addEvent(dropZone, 'drop', function(e) {
  9816  				e.preventDefault();
  9817  				var flag = Dom.get(self.uid).dragDrop(e);
  9818  				// If handled, then stop propagation of event in DOM
  9819  				if (flag) {
  9820  					e.stopPropagation();
  9821  				}
  9822  			}, comp.uid);
  9823  
  9824  			return self.shimExec.call(this, 'FileDrop', 'init');
  9825  		}
  9826  	};
  9827  
  9828  	return (extensions.FileDrop = FileDrop);
  9829  });
  9830  
  9831  // Included from: src/javascript/runtime/silverlight/file/FileReader.js
  9832  
  9833  /**
  9834   * FileReader.js
  9835   *
  9836   * Copyright 2013, Moxiecode Systems AB
  9837   * Released under GPL License.
  9838   *
  9839   * License: http://www.plupload.com/license
  9840   * Contributing: http://www.plupload.com/contributing
  9841   */
  9842  
  9843  /**
  9844  @class moxie/runtime/silverlight/file/FileReader
  9845  @private
  9846  */
  9847  define("moxie/runtime/silverlight/file/FileReader", [
  9848  	"moxie/runtime/silverlight/Runtime",
  9849  	"moxie/core/utils/Basic",
  9850  	"moxie/runtime/flash/file/FileReader"
  9851  ], function(extensions, Basic, FileReader) {
  9852  	return (extensions.FileReader = Basic.extend({}, FileReader));
  9853  });
  9854  
  9855  // Included from: src/javascript/runtime/silverlight/file/FileReaderSync.js
  9856  
  9857  /**
  9858   * FileReaderSync.js
  9859   *
  9860   * Copyright 2013, Moxiecode Systems AB
  9861   * Released under GPL License.
  9862   *
  9863   * License: http://www.plupload.com/license
  9864   * Contributing: http://www.plupload.com/contributing
  9865   */
  9866  
  9867  /**
  9868  @class moxie/runtime/silverlight/file/FileReaderSync
  9869  @private
  9870  */
  9871  define("moxie/runtime/silverlight/file/FileReaderSync", [
  9872  	"moxie/runtime/silverlight/Runtime",
  9873  	"moxie/core/utils/Basic",
  9874  	"moxie/runtime/flash/file/FileReaderSync"
  9875  ], function(extensions, Basic, FileReaderSync) {
  9876  	return (extensions.FileReaderSync = Basic.extend({}, FileReaderSync));
  9877  });
  9878  
  9879  // Included from: src/javascript/runtime/silverlight/xhr/XMLHttpRequest.js
  9880  
  9881  /**
  9882   * XMLHttpRequest.js
  9883   *
  9884   * Copyright 2013, Moxiecode Systems AB
  9885   * Released under GPL License.
  9886   *
  9887   * License: http://www.plupload.com/license
  9888   * Contributing: http://www.plupload.com/contributing
  9889   */
  9890  
  9891  /**
  9892  @class moxie/runtime/silverlight/xhr/XMLHttpRequest
  9893  @private
  9894  */
  9895  define("moxie/runtime/silverlight/xhr/XMLHttpRequest", [
  9896  	"moxie/runtime/silverlight/Runtime",
  9897  	"moxie/core/utils/Basic",
  9898  	"moxie/runtime/flash/xhr/XMLHttpRequest"
  9899  ], function(extensions, Basic, XMLHttpRequest) {
  9900  	return (extensions.XMLHttpRequest = Basic.extend({}, XMLHttpRequest));
  9901  });
  9902  
  9903  // Included from: src/javascript/runtime/silverlight/runtime/Transporter.js
  9904  
  9905  /**
  9906   * Transporter.js
  9907   *
  9908   * Copyright 2013, Moxiecode Systems AB
  9909   * Released under GPL License.
  9910   *
  9911   * License: http://www.plupload.com/license
  9912   * Contributing: http://www.plupload.com/contributing
  9913   */
  9914  
  9915  /**
  9916  @class moxie/runtime/silverlight/runtime/Transporter
  9917  @private
  9918  */
  9919  define("moxie/runtime/silverlight/runtime/Transporter", [
  9920  	"moxie/runtime/silverlight/Runtime",
  9921  	"moxie/core/utils/Basic",
  9922  	"moxie/runtime/flash/runtime/Transporter"
  9923  ], function(extensions, Basic, Transporter) {
  9924  	return (extensions.Transporter = Basic.extend({}, Transporter));
  9925  });
  9926  
  9927  // Included from: src/javascript/runtime/silverlight/image/Image.js
  9928  
  9929  /**
  9930   * Image.js
  9931   *
  9932   * Copyright 2013, Moxiecode Systems AB
  9933   * Released under GPL License.
  9934   *
  9935   * License: http://www.plupload.com/license
  9936   * Contributing: http://www.plupload.com/contributing
  9937   */
  9938   
  9939  /**
  9940  @class moxie/runtime/silverlight/image/Image
  9941  @private
  9942  */
  9943  define("moxie/runtime/silverlight/image/Image", [
  9944  	"moxie/runtime/silverlight/Runtime",
  9945  	"moxie/core/utils/Basic",
  9946  	"moxie/runtime/flash/image/Image"
  9947  ], function(extensions, Basic, Image) {
  9948  	return (extensions.Image = Basic.extend({}, Image, {
  9949  
  9950  		getInfo: function() {
  9951  			var self = this.getRuntime()
  9952  			, grps = ['tiff', 'exif', 'gps']
  9953  			, info = { meta: {} }
  9954  			, rawInfo = self.shimExec.call(this, 'Image', 'getInfo')
  9955  			;
  9956  
  9957  			if (rawInfo.meta) {
  9958  				Basic.each(grps, function(grp) {
  9959  					var meta = rawInfo.meta[grp]
  9960  					, tag
  9961  					, i
  9962  					, length
  9963  					, value
  9964  					;
  9965  					if (meta && meta.keys) {
  9966  						info.meta[grp] = {};
  9967  						for (i = 0, length = meta.keys.length; i < length; i++) {
  9968  							tag = meta.keys[i];
  9969  							value = meta[tag];
  9970  							if (value) {
  9971  								// convert numbers
  9972  								if (/^(\d|[1-9]\d+)$/.test(value)) { // integer (make sure doesn't start with zero)
  9973  									value = parseInt(value, 10);
  9974  								} else if (/^\d*\.\d+$/.test(value)) { // double
  9975  									value = parseFloat(value);
  9976  								}
  9977  								info.meta[grp][tag] = value;
  9978  							}
  9979  						}
  9980  					}
  9981  				});
  9982  			}
  9983  
  9984  			info.width = parseInt(rawInfo.width, 10);
  9985  			info.height = parseInt(rawInfo.height, 10);
  9986  			info.size = parseInt(rawInfo.size, 10);
  9987  			info.type = rawInfo.type;
  9988  			info.name = rawInfo.name;
  9989  
  9990  			return info;
  9991  		}
  9992  	}));
  9993  });
  9994  
  9995  // Included from: src/javascript/runtime/html4/Runtime.js
  9996  
  9997  /**
  9998   * Runtime.js
  9999   *
 10000   * Copyright 2013, Moxiecode Systems AB
 10001   * Released under GPL License.
 10002   *
 10003   * License: http://www.plupload.com/license
 10004   * Contributing: http://www.plupload.com/contributing
 10005   */
 10006  
 10007  /*global File:true */
 10008  
 10009  /**
 10010  Defines constructor for HTML4 runtime.
 10011  
 10012  @class moxie/runtime/html4/Runtime
 10013  @private
 10014  */
 10015  define("moxie/runtime/html4/Runtime", [
 10016  	"moxie/core/utils/Basic",
 10017  	"moxie/core/Exceptions",
 10018  	"moxie/runtime/Runtime",
 10019  	"moxie/core/utils/Env"
 10020  ], function(Basic, x, Runtime, Env) {
 10021  	
 10022  	var type = 'html4', extensions = {};
 10023  
 10024  	function Html4Runtime(options) {
 10025  		var I = this
 10026  		, Test = Runtime.capTest
 10027  		, True = Runtime.capTrue
 10028  		;
 10029  
 10030  		Runtime.call(this, options, type, {
 10031  			access_binary: Test(window.FileReader || window.File && File.getAsDataURL),
 10032  			access_image_binary: false,
 10033  			display_media: Test(extensions.Image && (Env.can('create_canvas') || Env.can('use_data_uri_over32kb'))),
 10034  			do_cors: false,
 10035  			drag_and_drop: false,
 10036  			filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
 10037  				return (Env.browser === 'Chrome' && Env.version >= 28) || (Env.browser === 'IE' && Env.version >= 10);
 10038  			}()),
 10039  			resize_image: function() {
 10040  				return extensions.Image && I.can('access_binary') && Env.can('create_canvas');
 10041  			},
 10042  			report_upload_progress: false,
 10043  			return_response_headers: false,
 10044  			return_response_type: function(responseType) {
 10045  				if (responseType === 'json' && !!window.JSON) {
 10046  					return true;
 10047  				} 
 10048  				return !!~Basic.inArray(responseType, ['text', 'document', '']);
 10049  			},
 10050  			return_status_code: function(code) {
 10051  				return !Basic.arrayDiff(code, [200, 404]);
 10052  			},
 10053  			select_file: function() {
 10054  				return Env.can('use_fileinput');
 10055  			},
 10056  			select_multiple: false,
 10057  			send_binary_string: false,
 10058  			send_custom_headers: false,
 10059  			send_multipart: true,
 10060  			slice_blob: false,
 10061  			stream_upload: function() {
 10062  				return I.can('select_file');
 10063  			},
 10064  			summon_file_dialog: Test(function() { // yeah... some dirty sniffing here...
 10065  				return (Env.browser === 'Firefox' && Env.version >= 4) ||
 10066  					(Env.browser === 'Opera' && Env.version >= 12) ||
 10067  					!!~Basic.inArray(Env.browser, ['Chrome', 'Safari']);
 10068  			}()),
 10069  			upload_filesize: True,
 10070  			use_http_method: function(methods) {
 10071  				return !Basic.arrayDiff(methods, ['GET', 'POST']);
 10072  			}
 10073  		});
 10074  
 10075  
 10076  		Basic.extend(this, {
 10077  			init : function() {
 10078  				this.trigger("Init");
 10079  			},
 10080  
 10081  			destroy: (function(destroy) { // extend default destroy method
 10082  				return function() {
 10083  					destroy.call(I);
 10084  					destroy = I = null;
 10085  				};
 10086  			}(this.destroy))
 10087  		});
 10088  
 10089  		Basic.extend(this.getShim(), extensions);
 10090  	}
 10091  
 10092  	Runtime.addConstructor(type, Html4Runtime);
 10093  
 10094  	return extensions;
 10095  });
 10096  
 10097  // Included from: src/javascript/runtime/html4/file/FileInput.js
 10098  
 10099  /**
 10100   * FileInput.js
 10101   *
 10102   * Copyright 2013, Moxiecode Systems AB
 10103   * Released under GPL License.
 10104   *
 10105   * License: http://www.plupload.com/license
 10106   * Contributing: http://www.plupload.com/contributing
 10107   */
 10108  
 10109  /**
 10110  @class moxie/runtime/html4/file/FileInput
 10111  @private
 10112  */
 10113  define("moxie/runtime/html4/file/FileInput", [
 10114  	"moxie/runtime/html4/Runtime",
 10115  	"moxie/core/utils/Basic",
 10116  	"moxie/core/utils/Dom",
 10117  	"moxie/core/utils/Events",
 10118  	"moxie/core/utils/Mime",
 10119  	"moxie/core/utils/Env"
 10120  ], function(extensions, Basic, Dom, Events, Mime, Env) {
 10121  	
 10122  	function FileInput() {
 10123  		var _uid, _files = [], _mimes = [], _options;
 10124  
 10125  		function addInput() {
 10126  			var comp = this, I = comp.getRuntime(), shimContainer, browseButton, currForm, form, input, uid;
 10127  
 10128  			uid = Basic.guid('uid_');
 10129  
 10130  			shimContainer = I.getShimContainer(); // we get new ref everytime to avoid memory leaks in IE
 10131  
 10132  			if (_uid) { // move previous form out of the view
 10133  				currForm = Dom.get(_uid + '_form');
 10134  				if (currForm) {
 10135  					Basic.extend(currForm.style, { top: '100%' });
 10136  				}
 10137  			}
 10138  
 10139  			// build form in DOM, since innerHTML version not able to submit file for some reason
 10140  			form = document.createElement('form');
 10141  			form.setAttribute('id', uid + '_form');
 10142  			form.setAttribute('method', 'post');
 10143  			form.setAttribute('enctype', 'multipart/form-data');
 10144  			form.setAttribute('encoding', 'multipart/form-data');
 10145  
 10146  			Basic.extend(form.style, {
 10147  				overflow: 'hidden',
 10148  				position: 'absolute',
 10149  				top: 0,
 10150  				left: 0,
 10151  				width: '100%',
 10152  				height: '100%'
 10153  			});
 10154  
 10155  			input = document.createElement('input');
 10156  			input.setAttribute('id', uid);
 10157  			input.setAttribute('type', 'file');
 10158  			input.setAttribute('name', _options.name || 'Filedata');
 10159  			input.setAttribute('accept', _mimes.join(','));
 10160  
 10161  			Basic.extend(input.style, {
 10162  				fontSize: '999px',
 10163  				opacity: 0
 10164  			});
 10165  
 10166  			form.appendChild(input);
 10167  			shimContainer.appendChild(form);
 10168  
 10169  			// prepare file input to be placed underneath the browse_button element
 10170  			Basic.extend(input.style, {
 10171  				position: 'absolute',
 10172  				top: 0,
 10173  				left: 0,
 10174  				width: '100%',
 10175  				height: '100%'
 10176  			});
 10177  
 10178  			if (Env.browser === 'IE' && Env.version < 10) {
 10179  				Basic.extend(input.style, {
 10180  					filter : "progid:DXImageTransform.Microsoft.Alpha(opacity=0)"
 10181  				});
 10182  			}
 10183  
 10184  			input.onchange = function() { // there should be only one handler for this
 10185  				var file;
 10186  
 10187  				if (!this.value) {
 10188  					return;
 10189  				}
 10190  
 10191  				if (this.files) {
 10192  					file = this.files[0];
 10193  				} else {
 10194  					file = {
 10195  						name: this.value
 10196  					};
 10197  				}
 10198  
 10199  				_files = [file];
 10200  
 10201  				this.onchange = function() {}; // clear event handler
 10202  				addInput.call(comp);
 10203  
 10204  				// after file is initialized as o.File, we need to update form and input ids
 10205  				comp.bind('change', function onChange() {
 10206  					var input = Dom.get(uid), form = Dom.get(uid + '_form'), file;
 10207  
 10208  					comp.unbind('change', onChange);
 10209  
 10210  					if (comp.files.length && input && form) {
 10211  						file = comp.files[0];
 10212  
 10213  						input.setAttribute('id', file.uid);
 10214  						form.setAttribute('id', file.uid + '_form');
 10215  
 10216  						// set upload target
 10217  						form.setAttribute('target', file.uid + '_iframe');
 10218  					}
 10219  					input = form = null;
 10220  				}, 998);
 10221  
 10222  				input = form = null;
 10223  				comp.trigger('change');
 10224  			};
 10225  
 10226  
 10227  			// route click event to the input
 10228  			if (I.can('summon_file_dialog')) {
 10229  				browseButton = Dom.get(_options.browse_button);
 10230  				Events.removeEvent(browseButton, 'click', comp.uid);
 10231  				Events.addEvent(browseButton, 'click', function(e) {
 10232  					if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
 10233  						input.click();
 10234  					}
 10235  					e.preventDefault();
 10236  				}, comp.uid);
 10237  			}
 10238  
 10239  			_uid = uid;
 10240  
 10241  			shimContainer = currForm = browseButton = null;
 10242  		}
 10243  
 10244  		Basic.extend(this, {
 10245  			init: function(options) {
 10246  				var comp = this, I = comp.getRuntime(), shimContainer;
 10247  
 10248  				// figure out accept string
 10249  				_options = options;
 10250  				_mimes = options.accept.mimes || Mime.extList2mimes(options.accept, I.can('filter_by_extension'));
 10251  
 10252  				shimContainer = I.getShimContainer();
 10253  
 10254  				(function() {
 10255  					var browseButton, zIndex, top;
 10256  
 10257  					browseButton = Dom.get(options.browse_button);
 10258  
 10259  					// Route click event to the input[type=file] element for browsers that support such behavior
 10260  					if (I.can('summon_file_dialog')) {
 10261  						if (Dom.getStyle(browseButton, 'position') === 'static') {
 10262  							browseButton.style.position = 'relative';
 10263  						}
 10264  
 10265  						zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
 10266  
 10267  						browseButton.style.zIndex = zIndex;
 10268  						shimContainer.style.zIndex = zIndex - 1;
 10269  					}
 10270  
 10271  					/* Since we have to place input[type=file] on top of the browse_button for some browsers,
 10272  					browse_button loses interactivity, so we restore it here */
 10273  					top = I.can('summon_file_dialog') ? browseButton : shimContainer;
 10274  
 10275  					Events.addEvent(top, 'mouseover', function() {
 10276  						comp.trigger('mouseenter');
 10277  					}, comp.uid);
 10278  
 10279  					Events.addEvent(top, 'mouseout', function() {
 10280  						comp.trigger('mouseleave');
 10281  					}, comp.uid);
 10282  
 10283  					Events.addEvent(top, 'mousedown', function() {
 10284  						comp.trigger('mousedown');
 10285  					}, comp.uid);
 10286  
 10287  					Events.addEvent(Dom.get(options.container), 'mouseup', function() {
 10288  						comp.trigger('mouseup');
 10289  					}, comp.uid);
 10290  
 10291  					browseButton = null;
 10292  				}());
 10293  
 10294  				addInput.call(this);
 10295  
 10296  				shimContainer = null;
 10297  
 10298  				// trigger ready event asynchronously
 10299  				comp.trigger({
 10300  					type: 'ready',
 10301  					async: true
 10302  				});
 10303  			},
 10304  
 10305  			getFiles: function() {
 10306  				return _files;
 10307  			},
 10308  
 10309  			disable: function(state) {
 10310  				var input;
 10311  
 10312  				if ((input = Dom.get(_uid))) {
 10313  					input.disabled = !!state;
 10314  				}
 10315  			},
 10316  
 10317  			destroy: function() {
 10318  				var I = this.getRuntime()
 10319  				, shim = I.getShim()
 10320  				, shimContainer = I.getShimContainer()
 10321  				;
 10322  				
 10323  				Events.removeAllEvents(shimContainer, this.uid);
 10324  				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
 10325  				Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
 10326  				
 10327  				if (shimContainer) {
 10328  					shimContainer.innerHTML = '';
 10329  				}
 10330  
 10331  				shim.removeInstance(this.uid);
 10332  
 10333  				_uid = _files = _mimes = _options = shimContainer = shim = null;
 10334  			}
 10335  		});
 10336  	}
 10337  
 10338  	return (extensions.FileInput = FileInput);
 10339  });
 10340  
 10341  // Included from: src/javascript/runtime/html4/file/FileReader.js
 10342  
 10343  /**
 10344   * FileReader.js
 10345   *
 10346   * Copyright 2013, Moxiecode Systems AB
 10347   * Released under GPL License.
 10348   *
 10349   * License: http://www.plupload.com/license
 10350   * Contributing: http://www.plupload.com/contributing
 10351   */
 10352  
 10353  /**
 10354  @class moxie/runtime/html4/file/FileReader
 10355  @private
 10356  */
 10357  define("moxie/runtime/html4/file/FileReader", [
 10358  	"moxie/runtime/html4/Runtime",
 10359  	"moxie/runtime/html5/file/FileReader"
 10360  ], function(extensions, FileReader) {
 10361  	return (extensions.FileReader = FileReader);
 10362  });
 10363  
 10364  // Included from: src/javascript/runtime/html4/xhr/XMLHttpRequest.js
 10365  
 10366  /**
 10367   * XMLHttpRequest.js
 10368   *
 10369   * Copyright 2013, Moxiecode Systems AB
 10370   * Released under GPL License.
 10371   *
 10372   * License: http://www.plupload.com/license
 10373   * Contributing: http://www.plupload.com/contributing
 10374   */
 10375  
 10376  /**
 10377  @class moxie/runtime/html4/xhr/XMLHttpRequest
 10378  @private
 10379  */
 10380  define("moxie/runtime/html4/xhr/XMLHttpRequest", [
 10381  	"moxie/runtime/html4/Runtime",
 10382  	"moxie/core/utils/Basic",
 10383  	"moxie/core/utils/Dom",
 10384  	"moxie/core/utils/Url",
 10385  	"moxie/core/Exceptions",
 10386  	"moxie/core/utils/Events",
 10387  	"moxie/file/Blob",
 10388  	"moxie/xhr/FormData"
 10389  ], function(extensions, Basic, Dom, Url, x, Events, Blob, FormData) {
 10390  	
 10391  	function XMLHttpRequest() {
 10392  		var _status, _response, _iframe;
 10393  
 10394  		function cleanup(cb) {
 10395  			var target = this, uid, form, inputs, i, hasFile = false;
 10396  
 10397  			if (!_iframe) {
 10398  				return;
 10399  			}
 10400  
 10401  			uid = _iframe.id.replace(/_iframe$/, '');
 10402  
 10403  			form = Dom.get(uid + '_form');
 10404  			if (form) {
 10405  				inputs = form.getElementsByTagName('input');
 10406  				i = inputs.length;
 10407  
 10408  				while (i--) {
 10409  					switch (inputs[i].getAttribute('type')) {
 10410  						case 'hidden':
 10411  							inputs[i].parentNode.removeChild(inputs[i]);
 10412  							break;
 10413  						case 'file':
 10414  							hasFile = true; // flag the case for later
 10415  							break;
 10416  					}
 10417  				}
 10418  				inputs = [];
 10419  
 10420  				if (!hasFile) { // we need to keep the form for sake of possible retries
 10421  					form.parentNode.removeChild(form);
 10422  				}
 10423  				form = null;
 10424  			}
 10425  
 10426  			// without timeout, request is marked as canceled (in console)
 10427  			setTimeout(function() {
 10428  				Events.removeEvent(_iframe, 'load', target.uid);
 10429  				if (_iframe.parentNode) { // #382
 10430  					_iframe.parentNode.removeChild(_iframe);
 10431  				}
 10432  
 10433  				// check if shim container has any other children, if - not, remove it as well
 10434  				var shimContainer = target.getRuntime().getShimContainer();
 10435  				if (!shimContainer.children.length) {
 10436  					shimContainer.parentNode.removeChild(shimContainer);
 10437  				}
 10438  
 10439  				shimContainer = _iframe = null;
 10440  				cb();
 10441  			}, 1);
 10442  		}
 10443  
 10444  		Basic.extend(this, {
 10445  			send: function(meta, data) {
 10446  				var target = this, I = target.getRuntime(), uid, form, input, blob;
 10447  
 10448  				_status = _response = null;
 10449  
 10450  				function createIframe() {
 10451  					var container = I.getShimContainer() || document.body
 10452  					, temp = document.createElement('div')
 10453  					;
 10454  
 10455  					// IE 6 won't be able to set the name using setAttribute or iframe.name
 10456  					temp.innerHTML = '<iframe id="' + uid + '_iframe" name="' + uid + '_iframe" src="javascript:&quot;&quot;" style="display:none"></iframe>';
 10457  					_iframe = temp.firstChild;
 10458  					container.appendChild(_iframe);
 10459  
 10460  					/* _iframe.onreadystatechange = function() {
 10461  						console.info(_iframe.readyState);
 10462  					};*/
 10463  
 10464  					Events.addEvent(_iframe, 'load', function() { // _iframe.onload doesn't work in IE lte 8
 10465  						var el;
 10466  
 10467  						try {
 10468  							el = _iframe.contentWindow.document || _iframe.contentDocument || window.frames[_iframe.id].document;
 10469  
 10470  							// try to detect some standard error pages
 10471  							if (/^4(0[0-9]|1[0-7]|2[2346])\s/.test(el.title)) { // test if title starts with 4xx HTTP error
 10472  								_status = el.title.replace(/^(\d+).*$/, '$1');
 10473  							} else {
 10474  								_status = 200;
 10475  								// get result
 10476  								_response = Basic.trim(el.body.innerHTML);
 10477  
 10478  								// we need to fire these at least once
 10479  								target.trigger({
 10480  									type: 'progress',
 10481  									loaded: _response.length,
 10482  									total: _response.length
 10483  								});
 10484  
 10485  								if (blob) { // if we were uploading a file
 10486  									target.trigger({
 10487  										type: 'uploadprogress',
 10488  										loaded: blob.size || 1025,
 10489  										total: blob.size || 1025
 10490  									});
 10491  								}
 10492  							}
 10493  						} catch (ex) {
 10494  							if (Url.hasSameOrigin(meta.url)) {
 10495  								// if response is sent with error code, iframe in IE gets redirected to res://ieframe.dll/http_x.htm
 10496  								// which obviously results to cross domain error (wtf?)
 10497  								_status = 404;
 10498  							} else {
 10499  								cleanup.call(target, function() {
 10500  									target.trigger('error');
 10501  								});
 10502  								return;
 10503  							}
 10504  						}	
 10505  					
 10506  						cleanup.call(target, function() {
 10507  							target.trigger('load');
 10508  						});
 10509  					}, target.uid);
 10510  				} // end createIframe
 10511  
 10512  				// prepare data to be sent and convert if required
 10513  				if (data instanceof FormData && data.hasBlob()) {
 10514  					blob = data.getBlob();
 10515  					uid = blob.uid;
 10516  					input = Dom.get(uid);
 10517  					form = Dom.get(uid + '_form');
 10518  					if (!form) {
 10519  						throw new x.DOMException(x.DOMException.NOT_FOUND_ERR);
 10520  					}
 10521  				} else {
 10522  					uid = Basic.guid('uid_');
 10523  
 10524  					form = document.createElement('form');
 10525  					form.setAttribute('id', uid + '_form');
 10526  					form.setAttribute('method', meta.method);
 10527  					form.setAttribute('enctype', 'multipart/form-data');
 10528  					form.setAttribute('encoding', 'multipart/form-data');
 10529  					form.setAttribute('target', uid + '_iframe');
 10530  
 10531  					I.getShimContainer().appendChild(form);
 10532  				}
 10533  
 10534  				if (data instanceof FormData) {
 10535  					data.each(function(value, name) {
 10536  						if (value instanceof Blob) {
 10537  							if (input) {
 10538  								input.setAttribute('name', name);
 10539  							}
 10540  						} else {
 10541  							var hidden = document.createElement('input');
 10542  
 10543  							Basic.extend(hidden, {
 10544  								type : 'hidden',
 10545  								name : name,
 10546  								value : value
 10547  							});
 10548  
 10549  							// make sure that input[type="file"], if it's there, comes last
 10550  							if (input) {
 10551  								form.insertBefore(hidden, input);
 10552  							} else {
 10553  								form.appendChild(hidden);
 10554  							}
 10555  						}
 10556  					});
 10557  				}
 10558  
 10559  				// set destination url
 10560  				form.setAttribute("action", meta.url);
 10561  
 10562  				createIframe();
 10563  				form.submit();
 10564  				target.trigger('loadstart');
 10565  			},
 10566  
 10567  			getStatus: function() {
 10568  				return _status;
 10569  			},
 10570  
 10571  			getResponse: function(responseType) {
 10572  				if ('json' === responseType) {
 10573  					// strip off <pre>..</pre> tags that might be enclosing the response
 10574  					if (Basic.typeOf(_response) === 'string' && !!window.JSON) {
 10575  						try {
 10576  							return JSON.parse(_response.replace(/^\s*<pre[^>]*>/, '').replace(/<\/pre>\s*$/, ''));
 10577  						} catch (ex) {
 10578  							return null;
 10579  						}
 10580  					} 
 10581  				} else if ('document' === responseType) {
 10582  
 10583  				}
 10584  				return _response;
 10585  			},
 10586  
 10587  			abort: function() {
 10588  				var target = this;
 10589  
 10590  				if (_iframe && _iframe.contentWindow) {
 10591  					if (_iframe.contentWindow.stop) { // FireFox/Safari/Chrome
 10592  						_iframe.contentWindow.stop();
 10593  					} else if (_iframe.contentWindow.document.execCommand) { // IE
 10594  						_iframe.contentWindow.document.execCommand('Stop');
 10595  					} else {
 10596  						_iframe.src = "about:blank";
 10597  					}
 10598  				}
 10599  
 10600  				cleanup.call(this, function() {
 10601  					// target.dispatchEvent('readystatechange');
 10602  					target.dispatchEvent('abort');
 10603  				});
 10604  			}
 10605  		});
 10606  	}
 10607  
 10608  	return (extensions.XMLHttpRequest = XMLHttpRequest);
 10609  });
 10610  
 10611  // Included from: src/javascript/runtime/html4/image/Image.js
 10612  
 10613  /**
 10614   * Image.js
 10615   *
 10616   * Copyright 2013, Moxiecode Systems AB
 10617   * Released under GPL License.
 10618   *
 10619   * License: http://www.plupload.com/license
 10620   * Contributing: http://www.plupload.com/contributing
 10621   */
 10622  
 10623  /**
 10624  @class moxie/runtime/html4/image/Image
 10625  @private
 10626  */
 10627  define("moxie/runtime/html4/image/Image", [
 10628  	"moxie/runtime/html4/Runtime",
 10629  	"moxie/runtime/html5/image/Image"
 10630  ], function(extensions, Image) {
 10631  	return (extensions.Image = Image);
 10632  });
 10633  
 10634  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"]);
 10635  })(this);/**
 10636   * o.js
 10637   *
 10638   * Copyright 2013, Moxiecode Systems AB
 10639   * Released under GPL License.
 10640   *
 10641   * License: http://www.plupload.com/license
 10642   * Contributing: http://www.plupload.com/contributing
 10643   */
 10644  
 10645  /*global moxie:true */
 10646  
 10647  /**
 10648  Globally exposed namespace with the most frequently used public classes and handy methods.
 10649  
 10650  @class o
 10651  @static
 10652  @private
 10653  */
 10654  (function() {
 10655  	"use strict";
 10656  
 10657  	var o = {}, inArray = moxie.core.utils.Basic.inArray;
 10658  
 10659  	// directly add some public classes
 10660  	// (we do it dynamically here, since for custom builds we cannot know beforehand what modules were included)
 10661  	(function addAlias(ns) {
 10662  		var name, itemType;
 10663  		for (name in ns) {
 10664  			itemType = typeof(ns[name]);
 10665  			if (itemType === 'object' && !~inArray(name, ['Exceptions', 'Env', 'Mime'])) {
 10666  				addAlias(ns[name]);
 10667  			} else if (itemType === 'function') {
 10668  				o[name] = ns[name];
 10669  			}
 10670  		}
 10671  	})(window.moxie);
 10672  
 10673  	// add some manually
 10674  	o.Env = window.moxie.core.utils.Env;
 10675  	o.Mime = window.moxie.core.utils.Mime;
 10676  	o.Exceptions = window.moxie.core.Exceptions;
 10677  
 10678  	// expose globally
 10679  	window.mOxie = o;
 10680  	if (!window.o) {
 10681  		window.o = o;
 10682  	}
 10683  	return o;
 10684  })();