Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

| Ultima modificación | Ver Log |

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