[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/js/plupload/ -> moxie.js (source)

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