Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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