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