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