github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/libs/qiniu-js-sdk-master/demo/js/plupload/moxie.js (about) 1 /** 2 * mOxie - multi-runtime File API & XMLHttpRequest L2 Polyfill 3 * v1.2.0 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-01-16 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 erro 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', blobpool[this.uid]); 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 _xhr.destroy(); 5158 _xhr = null; 5159 self.dispatchEvent('loadend'); 5160 self = null; 5161 } 5162 5163 function exec(runtime) { 5164 _xhr.bind('LoadStart', function(e) { 5165 _p('readyState', XMLHttpRequest.LOADING); 5166 self.dispatchEvent('readystatechange'); 5167 5168 self.dispatchEvent(e); 5169 5170 if (_upload_events_flag) { 5171 self.upload.dispatchEvent(e); 5172 } 5173 }); 5174 5175 _xhr.bind('Progress', function(e) { 5176 if (_p('readyState') !== XMLHttpRequest.LOADING) { 5177 _p('readyState', XMLHttpRequest.LOADING); // LoadStart unreliable (in Flash for example) 5178 self.dispatchEvent('readystatechange'); 5179 } 5180 self.dispatchEvent(e); 5181 }); 5182 5183 _xhr.bind('UploadProgress', function(e) { 5184 if (_upload_events_flag) { 5185 self.upload.dispatchEvent({ 5186 type: 'progress', 5187 lengthComputable: false, 5188 total: e.total, 5189 loaded: e.loaded 5190 }); 5191 } 5192 }); 5193 5194 _xhr.bind('Load', function(e) { 5195 _p('readyState', XMLHttpRequest.DONE); 5196 _p('status', Number(runtime.exec.call(_xhr, 'XMLHttpRequest', 'getStatus') || 0)); 5197 _p('statusText', httpCode[_p('status')] || ""); 5198 5199 _p('response', runtime.exec.call(_xhr, 'XMLHttpRequest', 'getResponse', _p('responseType'))); 5200 5201 if (!!~Basic.inArray(_p('responseType'), ['text', ''])) { 5202 _p('responseText', _p('response')); 5203 } else if (_p('responseType') === 'document') { 5204 _p('responseXML', _p('response')); 5205 } 5206 5207 _responseHeaders = runtime.exec.call(_xhr, 'XMLHttpRequest', 'getAllResponseHeaders'); 5208 5209 self.dispatchEvent('readystatechange'); 5210 5211 if (_p('status') > 0) { // status 0 usually means that server is unreachable 5212 if (_upload_events_flag) { 5213 self.upload.dispatchEvent(e); 5214 } 5215 self.dispatchEvent(e); 5216 } else { 5217 _error_flag = true; 5218 self.dispatchEvent('error'); 5219 } 5220 loadEnd(); 5221 }); 5222 5223 _xhr.bind('Abort', function(e) { 5224 self.dispatchEvent(e); 5225 loadEnd(); 5226 }); 5227 5228 _xhr.bind('Error', function(e) { 5229 _error_flag = true; 5230 _p('readyState', XMLHttpRequest.DONE); 5231 self.dispatchEvent('readystatechange'); 5232 _upload_complete_flag = true; 5233 self.dispatchEvent(e); 5234 loadEnd(); 5235 }); 5236 5237 runtime.exec.call(_xhr, 'XMLHttpRequest', 'send', { 5238 url: _url, 5239 method: _method, 5240 async: _async, 5241 user: _user, 5242 password: _password, 5243 headers: _headers, 5244 mimeType: _mimeType, 5245 encoding: _encoding, 5246 responseType: self.responseType, 5247 withCredentials: self.withCredentials, 5248 options: _options 5249 }, data); 5250 } 5251 5252 // clarify our requirements 5253 if (typeof(_options.required_caps) === 'string') { 5254 _options.required_caps = Runtime.parseCaps(_options.required_caps); 5255 } 5256 5257 _options.required_caps = Basic.extend({}, _options.required_caps, { 5258 return_response_type: self.responseType 5259 }); 5260 5261 if (data instanceof FormData) { 5262 _options.required_caps.send_multipart = true; 5263 } 5264 5265 if (!_same_origin_flag) { 5266 _options.required_caps.do_cors = true; 5267 } 5268 5269 5270 if (_options.ruid) { // we do not need to wait if we can connect directly 5271 exec(_xhr.connectRuntime(_options)); 5272 } else { 5273 _xhr.bind('RuntimeInit', function(e, runtime) { 5274 exec(runtime); 5275 }); 5276 _xhr.bind('RuntimeError', function(e, err) { 5277 self.dispatchEvent('RuntimeError', err); 5278 }); 5279 _xhr.connectRuntime(_options); 5280 } 5281 } 5282 5283 5284 function _reset() { 5285 _p('responseText', ""); 5286 _p('responseXML', null); 5287 _p('response', null); 5288 _p('status', 0); 5289 _p('statusText', ""); 5290 _start_time = _timeoutset_time = null; 5291 } 5292 } 5293 5294 XMLHttpRequest.UNSENT = 0; 5295 XMLHttpRequest.OPENED = 1; 5296 XMLHttpRequest.HEADERS_RECEIVED = 2; 5297 XMLHttpRequest.LOADING = 3; 5298 XMLHttpRequest.DONE = 4; 5299 5300 XMLHttpRequest.prototype = EventTarget.instance; 5301 5302 return XMLHttpRequest; 5303 }); 5304 5305 // Included from: src/javascript/runtime/Transporter.js 5306 5307 /** 5308 * Transporter.js 5309 * 5310 * Copyright 2013, Moxiecode Systems AB 5311 * Released under GPL License. 5312 * 5313 * License: http://www.plupload.com/license 5314 * Contributing: http://www.plupload.com/contributing 5315 */ 5316 5317 define("moxie/runtime/Transporter", [ 5318 "moxie/core/utils/Basic", 5319 "moxie/core/utils/Encode", 5320 "moxie/runtime/RuntimeClient", 5321 "moxie/core/EventTarget" 5322 ], function(Basic, Encode, RuntimeClient, EventTarget) { 5323 function Transporter() { 5324 var mod, _runtime, _data, _size, _pos, _chunk_size; 5325 5326 RuntimeClient.call(this); 5327 5328 Basic.extend(this, { 5329 uid: Basic.guid('uid_'), 5330 5331 state: Transporter.IDLE, 5332 5333 result: null, 5334 5335 transport: function(data, type, options) { 5336 var self = this; 5337 5338 options = Basic.extend({ 5339 chunk_size: 204798 5340 }, options); 5341 5342 // should divide by three, base64 requires this 5343 if ((mod = options.chunk_size % 3)) { 5344 options.chunk_size += 3 - mod; 5345 } 5346 5347 _chunk_size = options.chunk_size; 5348 5349 _reset.call(this); 5350 _data = data; 5351 _size = data.length; 5352 5353 if (Basic.typeOf(options) === 'string' || options.ruid) { 5354 _run.call(self, type, this.connectRuntime(options)); 5355 } else { 5356 // we require this to run only once 5357 var cb = function(e, runtime) { 5358 self.unbind("RuntimeInit", cb); 5359 _run.call(self, type, runtime); 5360 }; 5361 this.bind("RuntimeInit", cb); 5362 this.connectRuntime(options); 5363 } 5364 }, 5365 5366 abort: function() { 5367 var self = this; 5368 5369 self.state = Transporter.IDLE; 5370 if (_runtime) { 5371 _runtime.exec.call(self, 'Transporter', 'clear'); 5372 self.trigger("TransportingAborted"); 5373 } 5374 5375 _reset.call(self); 5376 }, 5377 5378 5379 destroy: function() { 5380 this.unbindAll(); 5381 _runtime = null; 5382 this.disconnectRuntime(); 5383 _reset.call(this); 5384 } 5385 }); 5386 5387 function _reset() { 5388 _size = _pos = 0; 5389 _data = this.result = null; 5390 } 5391 5392 function _run(type, runtime) { 5393 var self = this; 5394 5395 _runtime = runtime; 5396 5397 //self.unbind("RuntimeInit"); 5398 5399 self.bind("TransportingProgress", function(e) { 5400 _pos = e.loaded; 5401 5402 if (_pos < _size && Basic.inArray(self.state, [Transporter.IDLE, Transporter.DONE]) === -1) { 5403 _transport.call(self); 5404 } 5405 }, 999); 5406 5407 self.bind("TransportingComplete", function() { 5408 _pos = _size; 5409 self.state = Transporter.DONE; 5410 _data = null; // clean a bit 5411 self.result = _runtime.exec.call(self, 'Transporter', 'getAsBlob', type || ''); 5412 }, 999); 5413 5414 self.state = Transporter.BUSY; 5415 self.trigger("TransportingStarted"); 5416 _transport.call(self); 5417 } 5418 5419 function _transport() { 5420 var self = this, 5421 chunk, 5422 bytesLeft = _size - _pos; 5423 5424 if (_chunk_size > bytesLeft) { 5425 _chunk_size = bytesLeft; 5426 } 5427 5428 chunk = Encode.btoa(_data.substr(_pos, _chunk_size)); 5429 _runtime.exec.call(self, 'Transporter', 'receive', chunk, _size); 5430 } 5431 } 5432 5433 Transporter.IDLE = 0; 5434 Transporter.BUSY = 1; 5435 Transporter.DONE = 2; 5436 5437 Transporter.prototype = EventTarget.instance; 5438 5439 return Transporter; 5440 }); 5441 5442 // Included from: src/javascript/image/Image.js 5443 5444 /** 5445 * Image.js 5446 * 5447 * Copyright 2013, Moxiecode Systems AB 5448 * Released under GPL License. 5449 * 5450 * License: http://www.plupload.com/license 5451 * Contributing: http://www.plupload.com/contributing 5452 */ 5453 5454 define("moxie/image/Image", [ 5455 "moxie/core/utils/Basic", 5456 "moxie/core/utils/Dom", 5457 "moxie/core/Exceptions", 5458 "moxie/file/FileReaderSync", 5459 "moxie/xhr/XMLHttpRequest", 5460 "moxie/runtime/Runtime", 5461 "moxie/runtime/RuntimeClient", 5462 "moxie/runtime/Transporter", 5463 "moxie/core/utils/Env", 5464 "moxie/core/EventTarget", 5465 "moxie/file/Blob", 5466 "moxie/file/File", 5467 "moxie/core/utils/Encode" 5468 ], function(Basic, Dom, x, FileReaderSync, XMLHttpRequest, Runtime, RuntimeClient, Transporter, Env, EventTarget, Blob, File, Encode) { 5469 /** 5470 Image preloading and manipulation utility. Additionally it provides access to image meta info (Exif, GPS) and raw binary data. 5471 5472 @class Image 5473 @constructor 5474 @extends EventTarget 5475 */ 5476 var dispatches = [ 5477 'progress', 5478 5479 /** 5480 Dispatched when loading is complete. 5481 5482 @event load 5483 @param {Object} event 5484 */ 5485 'load', 5486 5487 'error', 5488 5489 /** 5490 Dispatched when resize operation is complete. 5491 5492 @event resize 5493 @param {Object} event 5494 */ 5495 'resize', 5496 5497 /** 5498 Dispatched when visual representation of the image is successfully embedded 5499 into the corresponsing container. 5500 5501 @event embedded 5502 @param {Object} event 5503 */ 5504 'embedded' 5505 ]; 5506 5507 function Image() { 5508 RuntimeClient.call(this); 5509 5510 Basic.extend(this, { 5511 /** 5512 Unique id of the component 5513 5514 @property uid 5515 @type {String} 5516 */ 5517 uid: Basic.guid('uid_'), 5518 5519 /** 5520 Unique id of the connected runtime, if any. 5521 5522 @property ruid 5523 @type {String} 5524 */ 5525 ruid: null, 5526 5527 /** 5528 Name of the file, that was used to create an image, if available. If not equals to empty string. 5529 5530 @property name 5531 @type {String} 5532 @default "" 5533 */ 5534 name: "", 5535 5536 /** 5537 Size of the image in bytes. Actual value is set only after image is preloaded. 5538 5539 @property size 5540 @type {Number} 5541 @default 0 5542 */ 5543 size: 0, 5544 5545 /** 5546 Width of the image. Actual value is set only after image is preloaded. 5547 5548 @property width 5549 @type {Number} 5550 @default 0 5551 */ 5552 width: 0, 5553 5554 /** 5555 Height of the image. Actual value is set only after image is preloaded. 5556 5557 @property height 5558 @type {Number} 5559 @default 0 5560 */ 5561 height: 0, 5562 5563 /** 5564 Mime type of the image. Currently only image/jpeg and image/png are supported. Actual value is set only after image is preloaded. 5565 5566 @property type 5567 @type {String} 5568 @default "" 5569 */ 5570 type: "", 5571 5572 /** 5573 Holds meta info (Exif, GPS). Is populated only for image/jpeg. Actual value is set only after image is preloaded. 5574 5575 @property meta 5576 @type {Object} 5577 @default {} 5578 */ 5579 meta: {}, 5580 5581 /** 5582 Alias for load method, that takes another mOxie.Image object as a source (see load). 5583 5584 @method clone 5585 @param {Image} src Source for the image 5586 @param {Boolean} [exact=false] Whether to activate in-depth clone mode 5587 */ 5588 clone: function() { 5589 this.load.apply(this, arguments); 5590 }, 5591 5592 /** 5593 Loads image from various sources. Currently the source for new image can be: mOxie.Image, mOxie.Blob/mOxie.File, 5594 native Blob/File, dataUrl or URL. Depending on the type of the source, arguments - differ. When source is URL, 5595 Image will be downloaded from remote destination and loaded in memory. 5596 5597 @example 5598 var img = new mOxie.Image(); 5599 img.onload = function() { 5600 var blob = img.getAsBlob(); 5601 5602 var formData = new mOxie.FormData(); 5603 formData.append('file', blob); 5604 5605 var xhr = new mOxie.XMLHttpRequest(); 5606 xhr.onload = function() { 5607 // upload complete 5608 }; 5609 xhr.open('post', 'upload.php'); 5610 xhr.send(formData); 5611 }; 5612 img.load("http://www.moxiecode.com/images/mox-logo.jpg"); // notice file extension (.jpg) 5613 5614 5615 @method load 5616 @param {Image|Blob|File|String} src Source for the image 5617 @param {Boolean|Object} [mixed] 5618 */ 5619 load: function() { 5620 // this is here because to bind properly we need an uid first, which is created above 5621 this.bind('Load Resize', function() { 5622 _updateInfo.call(this); 5623 }, 999); 5624 5625 this.convertEventPropsToHandlers(dispatches); 5626 5627 _load.apply(this, arguments); 5628 }, 5629 5630 /** 5631 Downsizes the image to fit the specified width/height. If crop is supplied, image will be cropped to exact dimensions. 5632 5633 @method downsize 5634 @param {Number} width Resulting width 5635 @param {Number} [height=width] Resulting height (optional, if not supplied will default to width) 5636 @param {Boolean} [crop=false] Whether to crop the image to exact dimensions 5637 @param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize) 5638 */ 5639 downsize: function(width, height, crop, preserveHeaders) { 5640 try { 5641 if (!this.size) { // only preloaded image objects can be used as source 5642 throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); 5643 } 5644 5645 // no way to reliably intercept the crash due to high resolution, so we simply avoid it 5646 if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) { 5647 throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR); 5648 } 5649 5650 if (!width && !height || Basic.typeOf(crop) === 'undefined') { 5651 crop = false; 5652 } 5653 5654 width = width || this.width; 5655 height = height || this.height; 5656 5657 preserveHeaders = (Basic.typeOf(preserveHeaders) === 'undefined' ? true : !!preserveHeaders); 5658 5659 this.getRuntime().exec.call(this, 'Image', 'downsize', width, height, crop, preserveHeaders); 5660 } catch(ex) { 5661 // for now simply trigger error event 5662 this.trigger('error', ex); 5663 } 5664 }, 5665 5666 /** 5667 Alias for downsize(width, height, true). (see downsize) 5668 5669 @method crop 5670 @param {Number} width Resulting width 5671 @param {Number} [height=width] Resulting height (optional, if not supplied will default to width) 5672 @param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize) 5673 */ 5674 crop: function(width, height, preserveHeaders) { 5675 this.downsize(width, height, true, preserveHeaders); 5676 }, 5677 5678 getAsCanvas: function() { 5679 if (!Env.can('create_canvas')) { 5680 throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR); 5681 } 5682 5683 var runtime = this.connectRuntime(this.ruid); 5684 return runtime.exec.call(this, 'Image', 'getAsCanvas'); 5685 }, 5686 5687 /** 5688 Retrieves image in it's current state as mOxie.Blob object. Cannot be run on empty or image in progress (throws 5689 DOMException.INVALID_STATE_ERR). 5690 5691 @method getAsBlob 5692 @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png 5693 @param {Number} [quality=90] Applicable only together with mime type image/jpeg 5694 @return {Blob} Image as Blob 5695 */ 5696 getAsBlob: function(type, quality) { 5697 if (!this.size) { 5698 throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); 5699 } 5700 5701 if (!type) { 5702 type = 'image/jpeg'; 5703 } 5704 5705 if (type === 'image/jpeg' && !quality) { 5706 quality = 90; 5707 } 5708 5709 return this.getRuntime().exec.call(this, 'Image', 'getAsBlob', type, quality); 5710 }, 5711 5712 /** 5713 Retrieves image in it's current state as dataURL string. Cannot be run on empty or image in progress (throws 5714 DOMException.INVALID_STATE_ERR). 5715 5716 @method getAsDataURL 5717 @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png 5718 @param {Number} [quality=90] Applicable only together with mime type image/jpeg 5719 @return {String} Image as dataURL string 5720 */ 5721 getAsDataURL: function(type, quality) { 5722 if (!this.size) { 5723 throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); 5724 } 5725 return this.getRuntime().exec.call(this, 'Image', 'getAsDataURL', type, quality); 5726 }, 5727 5728 /** 5729 Retrieves image in it's current state as binary string. Cannot be run on empty or image in progress (throws 5730 DOMException.INVALID_STATE_ERR). 5731 5732 @method getAsBinaryString 5733 @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png 5734 @param {Number} [quality=90] Applicable only together with mime type image/jpeg 5735 @return {String} Image as binary string 5736 */ 5737 getAsBinaryString: function(type, quality) { 5738 var dataUrl = this.getAsDataURL(type, quality); 5739 return Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7)); 5740 }, 5741 5742 /** 5743 Embeds the image, or better to say, it's visual representation into the specified node. Depending on the runtime 5744 in use, might be a canvas, or image (actual ) element or shim object (Flash or SilverLight - very rare, used for 5745 legacy browsers that do not have canvas or proper dataURI support). 5746 5747 @method embed 5748 @param {DOMElement} el DOM element to insert the image object into 5749 @param {Object} options Set of key/value pairs controlling the mime type, dimensions and cropping factor of resulting 5750 representation 5751 */ 5752 embed: function(el) { 5753 var self = this 5754 , imgCopy 5755 , type, quality, crop 5756 , options = arguments[1] || {} 5757 , width = this.width 5758 , height = this.height 5759 , runtime // this has to be outside of all the closures to contain proper runtime 5760 ; 5761 5762 function onResize() { 5763 // if possible, embed a canvas element directly 5764 if (Env.can('create_canvas')) { 5765 var canvas = imgCopy.getAsCanvas(); 5766 if (canvas) { 5767 el.appendChild(canvas); 5768 canvas = null; 5769 imgCopy.destroy(); 5770 self.trigger('embedded'); 5771 return; 5772 } 5773 } 5774 5775 var dataUrl = imgCopy.getAsDataURL(type, quality); 5776 if (!dataUrl) { 5777 throw new x.ImageError(x.ImageError.WRONG_FORMAT); 5778 } 5779 5780 if (Env.can('use_data_uri_of', dataUrl.length)) { 5781 el.innerHTML = '<img src="' + dataUrl + '" width="' + imgCopy.width + '" height="' + imgCopy.height + '" />'; 5782 imgCopy.destroy(); 5783 self.trigger('embedded'); 5784 } else { 5785 var tr = new Transporter(); 5786 5787 tr.bind("TransportingComplete", function() { 5788 runtime = self.connectRuntime(this.result.ruid); 5789 5790 self.bind("Embedded", function() { 5791 // position and size properly 5792 Basic.extend(runtime.getShimContainer().style, { 5793 //position: 'relative', 5794 top: '0px', 5795 left: '0px', 5796 width: imgCopy.width + 'px', 5797 height: imgCopy.height + 'px' 5798 }); 5799 5800 // some shims (Flash/SilverLight) reinitialize, if parent element is hidden, reordered or it's 5801 // position type changes (in Gecko), but since we basically need this only in IEs 6/7 and 5802 // sometimes 8 and they do not have this problem, we can comment this for now 5803 /*tr.bind("RuntimeInit", function(e, runtime) { 5804 tr.destroy(); 5805 runtime.destroy(); 5806 onResize.call(self); // re-feed our image data 5807 });*/ 5808 5809 runtime = null; 5810 }, 999); 5811 5812 runtime.exec.call(self, "ImageView", "display", this.result.uid, width, height); 5813 imgCopy.destroy(); 5814 }); 5815 5816 tr.transport(Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7)), type, Basic.extend({}, options, { 5817 required_caps: { 5818 display_media: true 5819 }, 5820 runtime_order: 'flash,silverlight', 5821 container: el 5822 })); 5823 } 5824 } 5825 5826 try { 5827 if (!(el = Dom.get(el))) { 5828 throw new x.DOMException(x.DOMException.INVALID_NODE_TYPE_ERR); 5829 } 5830 5831 if (!this.size) { // only preloaded image objects can be used as source 5832 throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); 5833 } 5834 5835 if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) { 5836 throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR); 5837 } 5838 5839 type = options.type || this.type || 'image/jpeg'; 5840 quality = options.quality || 90; 5841 crop = Basic.typeOf(options.crop) !== 'undefined' ? options.crop : false; 5842 5843 // figure out dimensions for the thumb 5844 if (options.width) { 5845 width = options.width; 5846 height = options.height || width; 5847 } else { 5848 // if container element has > 0 dimensions, take them 5849 var dimensions = Dom.getSize(el); 5850 if (dimensions.w && dimensions.h) { // both should be > 0 5851 width = dimensions.w; 5852 height = dimensions.h; 5853 } 5854 } 5855 5856 imgCopy = new Image(); 5857 5858 imgCopy.bind("Resize", function() { 5859 onResize.call(self); 5860 }); 5861 5862 imgCopy.bind("Load", function() { 5863 imgCopy.downsize(width, height, crop, false); 5864 }); 5865 5866 imgCopy.clone(this, false); 5867 5868 return imgCopy; 5869 } catch(ex) { 5870 // for now simply trigger error event 5871 this.trigger('error', ex); 5872 } 5873 }, 5874 5875 /** 5876 Properly destroys the image and frees resources in use. If any. Recommended way to dispose mOxie.Image object. 5877 5878 @method destroy 5879 */ 5880 destroy: function() { 5881 if (this.ruid) { 5882 this.getRuntime().exec.call(this, 'Image', 'destroy'); 5883 this.disconnectRuntime(); 5884 } 5885 this.unbindAll(); 5886 } 5887 }); 5888 5889 5890 function _updateInfo(info) { 5891 if (!info) { 5892 info = this.getRuntime().exec.call(this, 'Image', 'getInfo'); 5893 } 5894 5895 this.size = info.size; 5896 this.width = info.width; 5897 this.height = info.height; 5898 this.type = info.type; 5899 this.meta = info.meta; 5900 5901 // update file name, only if empty 5902 if (this.name === '') { 5903 this.name = info.name; 5904 } 5905 } 5906 5907 5908 function _load(src) { 5909 var srcType = Basic.typeOf(src); 5910 5911 try { 5912 // if source is Image 5913 if (src instanceof Image) { 5914 if (!src.size) { // only preloaded image objects can be used as source 5915 throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); 5916 } 5917 _loadFromImage.apply(this, arguments); 5918 } 5919 // if source is o.Blob/o.File 5920 else if (src instanceof Blob) { 5921 if (!~Basic.inArray(src.type, ['image/jpeg', 'image/png'])) { 5922 throw new x.ImageError(x.ImageError.WRONG_FORMAT); 5923 } 5924 _loadFromBlob.apply(this, arguments); 5925 } 5926 // if native blob/file 5927 else if (Basic.inArray(srcType, ['blob', 'file']) !== -1) { 5928 _load.call(this, new File(null, src), arguments[1]); 5929 } 5930 // if String 5931 else if (srcType === 'string') { 5932 // if dataUrl String 5933 if (/^data:[^;]*;base64,/.test(src)) { 5934 _load.call(this, new Blob(null, { data: src }), arguments[1]); 5935 } 5936 // else assume Url, either relative or absolute 5937 else { 5938 _loadFromUrl.apply(this, arguments); 5939 } 5940 } 5941 // if source seems to be an img node 5942 else if (srcType === 'node' && src.nodeName.toLowerCase() === 'img') { 5943 _load.call(this, src.src, arguments[1]); 5944 } 5945 else { 5946 throw new x.DOMException(x.DOMException.TYPE_MISMATCH_ERR); 5947 } 5948 } catch(ex) { 5949 // for now simply trigger error event 5950 this.trigger('error', ex); 5951 } 5952 } 5953 5954 5955 function _loadFromImage(img, exact) { 5956 var runtime = this.connectRuntime(img.ruid); 5957 this.ruid = runtime.uid; 5958 runtime.exec.call(this, 'Image', 'loadFromImage', img, (Basic.typeOf(exact) === 'undefined' ? true : exact)); 5959 } 5960 5961 5962 function _loadFromBlob(blob, options) { 5963 var self = this; 5964 5965 self.name = blob.name || ''; 5966 5967 function exec(runtime) { 5968 self.ruid = runtime.uid; 5969 runtime.exec.call(self, 'Image', 'loadFromBlob', blob); 5970 } 5971 5972 if (blob.isDetached()) { 5973 this.bind('RuntimeInit', function(e, runtime) { 5974 exec(runtime); 5975 }); 5976 5977 // convert to object representation 5978 if (options && typeof(options.required_caps) === 'string') { 5979 options.required_caps = Runtime.parseCaps(options.required_caps); 5980 } 5981 5982 this.connectRuntime(Basic.extend({ 5983 required_caps: { 5984 access_image_binary: true, 5985 resize_image: true 5986 } 5987 }, options)); 5988 } else { 5989 exec(this.connectRuntime(blob.ruid)); 5990 } 5991 } 5992 5993 5994 function _loadFromUrl(url, options) { 5995 var self = this, xhr; 5996 5997 xhr = new XMLHttpRequest(); 5998 5999 xhr.open('get', url); 6000 xhr.responseType = 'blob'; 6001 6002 xhr.onprogress = function(e) { 6003 self.trigger(e); 6004 }; 6005 6006 xhr.onload = function() { 6007 _loadFromBlob.call(self, xhr.response, true); 6008 }; 6009 6010 xhr.onerror = function(e) { 6011 self.trigger(e); 6012 }; 6013 6014 xhr.onloadend = function() { 6015 xhr.destroy(); 6016 }; 6017 6018 xhr.bind('RuntimeError', function(e, err) { 6019 self.trigger('RuntimeError', err); 6020 }); 6021 6022 xhr.send(null, options); 6023 } 6024 } 6025 6026 // virtual world will crash on you if image has a resolution higher than this: 6027 Image.MAX_RESIZE_WIDTH = 6500; 6028 Image.MAX_RESIZE_HEIGHT = 6500; 6029 6030 Image.prototype = EventTarget.instance; 6031 6032 return Image; 6033 }); 6034 6035 // Included from: src/javascript/runtime/html5/Runtime.js 6036 6037 /** 6038 * Runtime.js 6039 * 6040 * Copyright 2013, Moxiecode Systems AB 6041 * Released under GPL License. 6042 * 6043 * License: http://www.plupload.com/license 6044 * Contributing: http://www.plupload.com/contributing 6045 */ 6046 6047 /*global File:true */ 6048 6049 /** 6050 Defines constructor for HTML5 runtime. 6051 6052 @class moxie/runtime/html5/Runtime 6053 @private 6054 */ 6055 define("moxie/runtime/html5/Runtime", [ 6056 "moxie/core/utils/Basic", 6057 "moxie/core/Exceptions", 6058 "moxie/runtime/Runtime", 6059 "moxie/core/utils/Env" 6060 ], function(Basic, x, Runtime, Env) { 6061 6062 var type = "html5", extensions = {}; 6063 6064 function Html5Runtime(options) { 6065 var I = this 6066 , Test = Runtime.capTest 6067 , True = Runtime.capTrue 6068 ; 6069 6070 var caps = Basic.extend({ 6071 access_binary: Test(window.FileReader || window.File && window.File.getAsDataURL), 6072 access_image_binary: function() { 6073 return I.can('access_binary') && !!extensions.Image; 6074 }, 6075 display_media: Test(Env.can('create_canvas') || Env.can('use_data_uri_over32kb')), 6076 do_cors: Test(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()), 6077 drag_and_drop: Test(function() { 6078 // this comes directly from Modernizr: http://www.modernizr.com/ 6079 var div = document.createElement('div'); 6080 // IE has support for drag and drop since version 5, but doesn't support dropping files from desktop 6081 return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && (Env.browser !== 'IE' || Env.version > 9); 6082 }()), 6083 filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest 6084 return (Env.browser === 'Chrome' && Env.version >= 28) || (Env.browser === 'IE' && Env.version >= 10); 6085 }()), 6086 return_response_headers: True, 6087 return_response_type: function(responseType) { 6088 if (responseType === 'json' && !!window.JSON) { // we can fake this one even if it's not supported 6089 return true; 6090 } 6091 return Env.can('return_response_type', responseType); 6092 }, 6093 return_status_code: True, 6094 report_upload_progress: Test(window.XMLHttpRequest && new XMLHttpRequest().upload), 6095 resize_image: function() { 6096 return I.can('access_binary') && Env.can('create_canvas'); 6097 }, 6098 select_file: function() { 6099 return Env.can('use_fileinput') && window.File; 6100 }, 6101 select_folder: function() { 6102 return I.can('select_file') && Env.browser === 'Chrome' && Env.version >= 21; 6103 }, 6104 select_multiple: function() { 6105 // it is buggy on Safari Windows and iOS 6106 return I.can('select_file') && 6107 !(Env.browser === 'Safari' && Env.os === 'Windows') && 6108 !(Env.os === 'iOS' && Env.verComp(Env.osVersion, "7.0.4", '<')); 6109 }, 6110 send_binary_string: Test(window.XMLHttpRequest && (new XMLHttpRequest().sendAsBinary || (window.Uint8Array && window.ArrayBuffer))), 6111 send_custom_headers: Test(window.XMLHttpRequest), 6112 send_multipart: function() { 6113 return !!(window.XMLHttpRequest && new XMLHttpRequest().upload && window.FormData) || I.can('send_binary_string'); 6114 }, 6115 slice_blob: Test(window.File && (File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice)), 6116 stream_upload: function(){ 6117 return I.can('slice_blob') && I.can('send_multipart'); 6118 }, 6119 summon_file_dialog: Test(function() { // yeah... some dirty sniffing here... 6120 return (Env.browser === 'Firefox' && Env.version >= 4) || 6121 (Env.browser === 'Opera' && Env.version >= 12) || 6122 (Env.browser === 'IE' && Env.version >= 10) || 6123 !!~Basic.inArray(Env.browser, ['Chrome', 'Safari']); 6124 }()), 6125 upload_filesize: True 6126 }, 6127 arguments[2] 6128 ); 6129 6130 Runtime.call(this, options, (arguments[1] || type), caps); 6131 6132 6133 Basic.extend(this, { 6134 6135 init : function() { 6136 this.trigger("Init"); 6137 }, 6138 6139 destroy: (function(destroy) { // extend default destroy method 6140 return function() { 6141 destroy.call(I); 6142 destroy = I = null; 6143 }; 6144 }(this.destroy)) 6145 }); 6146 6147 Basic.extend(this.getShim(), extensions); 6148 } 6149 6150 Runtime.addConstructor(type, Html5Runtime); 6151 6152 return extensions; 6153 }); 6154 6155 // Included from: src/javascript/runtime/html5/file/Blob.js 6156 6157 /** 6158 * Blob.js 6159 * 6160 * Copyright 2013, Moxiecode Systems AB 6161 * Released under GPL License. 6162 * 6163 * License: http://www.plupload.com/license 6164 * Contributing: http://www.plupload.com/contributing 6165 */ 6166 6167 /** 6168 @class moxie/runtime/html5/file/Blob 6169 @private 6170 */ 6171 define("moxie/runtime/html5/file/Blob", [ 6172 "moxie/runtime/html5/Runtime", 6173 "moxie/file/Blob" 6174 ], function(extensions, Blob) { 6175 6176 function HTML5Blob() { 6177 function w3cBlobSlice(blob, start, end) { 6178 var blobSlice; 6179 6180 if (window.File.prototype.slice) { 6181 try { 6182 blob.slice(); // depricated version will throw WRONG_ARGUMENTS_ERR exception 6183 return blob.slice(start, end); 6184 } catch (e) { 6185 // depricated slice method 6186 return blob.slice(start, end - start); 6187 } 6188 // slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672 6189 } else if ((blobSlice = window.File.prototype.webkitSlice || window.File.prototype.mozSlice)) { 6190 return blobSlice.call(blob, start, end); 6191 } else { 6192 return null; // or throw some exception 6193 } 6194 } 6195 6196 this.slice = function() { 6197 return new Blob(this.getRuntime().uid, w3cBlobSlice.apply(this, arguments)); 6198 }; 6199 } 6200 6201 return (extensions.Blob = HTML5Blob); 6202 }); 6203 6204 // Included from: src/javascript/core/utils/Events.js 6205 6206 /** 6207 * Events.js 6208 * 6209 * Copyright 2013, Moxiecode Systems AB 6210 * Released under GPL License. 6211 * 6212 * License: http://www.plupload.com/license 6213 * Contributing: http://www.plupload.com/contributing 6214 */ 6215 6216 define('moxie/core/utils/Events', [ 6217 'moxie/core/utils/Basic' 6218 ], function(Basic) { 6219 var eventhash = {}, uid = 'moxie_' + Basic.guid(); 6220 6221 // IE W3C like event funcs 6222 function preventDefault() { 6223 this.returnValue = false; 6224 } 6225 6226 function stopPropagation() { 6227 this.cancelBubble = true; 6228 } 6229 6230 /** 6231 Adds an event handler to the specified object and store reference to the handler 6232 in objects internal Plupload registry (@see removeEvent). 6233 6234 @method addEvent 6235 @for Utils 6236 @static 6237 @param {Object} obj DOM element like object to add handler to. 6238 @param {String} name Name to add event listener to. 6239 @param {Function} callback Function to call when event occurs. 6240 @param {String} [key] that might be used to add specifity to the event record. 6241 */ 6242 var addEvent = function(obj, name, callback, key) { 6243 var func, events; 6244 6245 name = name.toLowerCase(); 6246 6247 // Add event listener 6248 if (obj.addEventListener) { 6249 func = callback; 6250 6251 obj.addEventListener(name, func, false); 6252 } else if (obj.attachEvent) { 6253 func = function() { 6254 var evt = window.event; 6255 6256 if (!evt.target) { 6257 evt.target = evt.srcElement; 6258 } 6259 6260 evt.preventDefault = preventDefault; 6261 evt.stopPropagation = stopPropagation; 6262 6263 callback(evt); 6264 }; 6265 6266 obj.attachEvent('on' + name, func); 6267 } 6268 6269 // Log event handler to objects internal mOxie registry 6270 if (!obj[uid]) { 6271 obj[uid] = Basic.guid(); 6272 } 6273 6274 if (!eventhash.hasOwnProperty(obj[uid])) { 6275 eventhash[obj[uid]] = {}; 6276 } 6277 6278 events = eventhash[obj[uid]]; 6279 6280 if (!events.hasOwnProperty(name)) { 6281 events[name] = []; 6282 } 6283 6284 events[name].push({ 6285 func: func, 6286 orig: callback, // store original callback for IE 6287 key: key 6288 }); 6289 }; 6290 6291 6292 /** 6293 Remove event handler from the specified object. If third argument (callback) 6294 is not specified remove all events with the specified name. 6295 6296 @method removeEvent 6297 @static 6298 @param {Object} obj DOM element to remove event listener(s) from. 6299 @param {String} name Name of event listener to remove. 6300 @param {Function|String} [callback] might be a callback or unique key to match. 6301 */ 6302 var removeEvent = function(obj, name, callback) { 6303 var type, undef; 6304 6305 name = name.toLowerCase(); 6306 6307 if (obj[uid] && eventhash[obj[uid]] && eventhash[obj[uid]][name]) { 6308 type = eventhash[obj[uid]][name]; 6309 } else { 6310 return; 6311 } 6312 6313 for (var i = type.length - 1; i >= 0; i--) { 6314 // undefined or not, key should match 6315 if (type[i].orig === callback || type[i].key === callback) { 6316 if (obj.removeEventListener) { 6317 obj.removeEventListener(name, type[i].func, false); 6318 } else if (obj.detachEvent) { 6319 obj.detachEvent('on'+name, type[i].func); 6320 } 6321 6322 type[i].orig = null; 6323 type[i].func = null; 6324 type.splice(i, 1); 6325 6326 // If callback was passed we are done here, otherwise proceed 6327 if (callback !== undef) { 6328 break; 6329 } 6330 } 6331 } 6332 6333 // If event array got empty, remove it 6334 if (!type.length) { 6335 delete eventhash[obj[uid]][name]; 6336 } 6337 6338 // If mOxie registry has become empty, remove it 6339 if (Basic.isEmptyObj(eventhash[obj[uid]])) { 6340 delete eventhash[obj[uid]]; 6341 6342 // IE doesn't let you remove DOM object property with - delete 6343 try { 6344 delete obj[uid]; 6345 } catch(e) { 6346 obj[uid] = undef; 6347 } 6348 } 6349 }; 6350 6351 6352 /** 6353 Remove all kind of events from the specified object 6354 6355 @method removeAllEvents 6356 @static 6357 @param {Object} obj DOM element to remove event listeners from. 6358 @param {String} [key] unique key to match, when removing events. 6359 */ 6360 var removeAllEvents = function(obj, key) { 6361 if (!obj || !obj[uid]) { 6362 return; 6363 } 6364 6365 Basic.each(eventhash[obj[uid]], function(events, name) { 6366 removeEvent(obj, name, key); 6367 }); 6368 }; 6369 6370 return { 6371 addEvent: addEvent, 6372 removeEvent: removeEvent, 6373 removeAllEvents: removeAllEvents 6374 }; 6375 }); 6376 6377 // Included from: src/javascript/runtime/html5/file/FileInput.js 6378 6379 /** 6380 * FileInput.js 6381 * 6382 * Copyright 2013, Moxiecode Systems AB 6383 * Released under GPL License. 6384 * 6385 * License: http://www.plupload.com/license 6386 * Contributing: http://www.plupload.com/contributing 6387 */ 6388 6389 /** 6390 @class moxie/runtime/html5/file/FileInput 6391 @private 6392 */ 6393 define("moxie/runtime/html5/file/FileInput", [ 6394 "moxie/runtime/html5/Runtime", 6395 "moxie/core/utils/Basic", 6396 "moxie/core/utils/Dom", 6397 "moxie/core/utils/Events", 6398 "moxie/core/utils/Mime", 6399 "moxie/core/utils/Env" 6400 ], function(extensions, Basic, Dom, Events, Mime, Env) { 6401 6402 function FileInput() { 6403 var _files = [], _options; 6404 6405 Basic.extend(this, { 6406 init: function(options) { 6407 var comp = this, I = comp.getRuntime(), input, shimContainer, mimes, browseButton, zIndex, top; 6408 6409 _options = options; 6410 _files = []; 6411 6412 // figure out accept string 6413 mimes = _options.accept.mimes || Mime.extList2mimes(_options.accept, I.can('filter_by_extension')); 6414 6415 shimContainer = I.getShimContainer(); 6416 6417 shimContainer.innerHTML = '<input id="' + I.uid +'" type="file" style="font-size:999px;opacity:0;"' + 6418 (_options.multiple && I.can('select_multiple') ? 'multiple' : '') + 6419 (_options.directory && I.can('select_folder') ? 'webkitdirectory directory' : '') + // Chrome 11+ 6420 (mimes ? ' accept="' + mimes.join(',') + '"' : '') + ' />'; 6421 6422 input = Dom.get(I.uid); 6423 6424 // prepare file input to be placed underneath the browse_button element 6425 Basic.extend(input.style, { 6426 position: 'absolute', 6427 top: 0, 6428 left: 0, 6429 width: '100%', 6430 height: '100%' 6431 }); 6432 6433 6434 browseButton = Dom.get(_options.browse_button); 6435 6436 // Route click event to the input[type=file] element for browsers that support such behavior 6437 if (I.can('summon_file_dialog')) { 6438 if (Dom.getStyle(browseButton, 'position') === 'static') { 6439 browseButton.style.position = 'relative'; 6440 } 6441 6442 zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1; 6443 6444 browseButton.style.zIndex = zIndex; 6445 shimContainer.style.zIndex = zIndex - 1; 6446 6447 Events.addEvent(browseButton, 'click', function(e) { 6448 var input = Dom.get(I.uid); 6449 if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file] 6450 input.click(); 6451 } 6452 e.preventDefault(); 6453 }, comp.uid); 6454 } 6455 6456 /* Since we have to place input[type=file] on top of the browse_button for some browsers, 6457 browse_button loses interactivity, so we restore it here */ 6458 top = I.can('summon_file_dialog') ? browseButton : shimContainer; 6459 6460 Events.addEvent(top, 'mouseover', function() { 6461 comp.trigger('mouseenter'); 6462 }, comp.uid); 6463 6464 Events.addEvent(top, 'mouseout', function() { 6465 comp.trigger('mouseleave'); 6466 }, comp.uid); 6467 6468 Events.addEvent(top, 'mousedown', function() { 6469 comp.trigger('mousedown'); 6470 }, comp.uid); 6471 6472 Events.addEvent(Dom.get(_options.container), 'mouseup', function() { 6473 comp.trigger('mouseup'); 6474 }, comp.uid); 6475 6476 6477 input.onchange = function onChange() { // there should be only one handler for this 6478 _files = []; 6479 6480 if (_options.directory) { 6481 // folders are represented by dots, filter them out (Chrome 11+) 6482 Basic.each(this.files, function(file) { 6483 if (file.name !== ".") { // if it doesn't looks like a folder 6484 _files.push(file); 6485 } 6486 }); 6487 } else { 6488 _files = [].slice.call(this.files); 6489 } 6490 6491 // clearing the value enables the user to select the same file again if they want to 6492 if (Env.browser !== 'IE') { 6493 this.value = ''; 6494 } else { 6495 // in IE input[type="file"] is read-only so the only way to reset it is to re-insert it 6496 var clone = this.cloneNode(true); 6497 this.parentNode.replaceChild(clone, this); 6498 clone.onchange = onChange; 6499 } 6500 comp.trigger('change'); 6501 }; 6502 6503 // ready event is perfectly asynchronous 6504 comp.trigger({ 6505 type: 'ready', 6506 async: true 6507 }); 6508 6509 shimContainer = null; 6510 }, 6511 6512 getFiles: function() { 6513 return _files; 6514 }, 6515 6516 disable: function(state) { 6517 var I = this.getRuntime(), input; 6518 6519 if ((input = Dom.get(I.uid))) { 6520 input.disabled = !!state; 6521 } 6522 }, 6523 6524 destroy: function() { 6525 var I = this.getRuntime() 6526 , shim = I.getShim() 6527 , shimContainer = I.getShimContainer() 6528 ; 6529 6530 Events.removeAllEvents(shimContainer, this.uid); 6531 Events.removeAllEvents(_options && Dom.get(_options.container), this.uid); 6532 Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid); 6533 6534 if (shimContainer) { 6535 shimContainer.innerHTML = ''; 6536 } 6537 6538 shim.removeInstance(this.uid); 6539 6540 _files = _options = shimContainer = shim = null; 6541 } 6542 }); 6543 } 6544 6545 return (extensions.FileInput = FileInput); 6546 }); 6547 6548 // Included from: src/javascript/runtime/html5/file/FileDrop.js 6549 6550 /** 6551 * FileDrop.js 6552 * 6553 * Copyright 2013, Moxiecode Systems AB 6554 * Released under GPL License. 6555 * 6556 * License: http://www.plupload.com/license 6557 * Contributing: http://www.plupload.com/contributing 6558 */ 6559 6560 /** 6561 @class moxie/runtime/html5/file/FileDrop 6562 @private 6563 */ 6564 define("moxie/runtime/html5/file/FileDrop", [ 6565 "moxie/runtime/html5/Runtime", 6566 "moxie/core/utils/Basic", 6567 "moxie/core/utils/Dom", 6568 "moxie/core/utils/Events", 6569 "moxie/core/utils/Mime" 6570 ], function(extensions, Basic, Dom, Events, Mime) { 6571 6572 function FileDrop() { 6573 var _files = [], _allowedExts = [], _options; 6574 6575 Basic.extend(this, { 6576 init: function(options) { 6577 var comp = this, dropZone; 6578 6579 _options = options; 6580 _allowedExts = _extractExts(_options.accept); 6581 dropZone = _options.container; 6582 6583 Events.addEvent(dropZone, 'dragover', function(e) { 6584 e.preventDefault(); 6585 e.stopPropagation(); 6586 e.dataTransfer.dropEffect = 'copy'; 6587 }, comp.uid); 6588 6589 Events.addEvent(dropZone, 'drop', function(e) { 6590 e.preventDefault(); 6591 e.stopPropagation(); 6592 6593 _files = []; 6594 6595 // Chrome 21+ accepts folders via Drag'n'Drop 6596 if (e.dataTransfer.items && e.dataTransfer.items[0].webkitGetAsEntry) { 6597 _readItems(e.dataTransfer.items, function() { 6598 comp.trigger("drop"); 6599 }); 6600 } else { 6601 Basic.each(e.dataTransfer.files, function(file) { 6602 if (_isAcceptable(file)) { 6603 _files.push(file); 6604 } 6605 }); 6606 comp.trigger("drop"); 6607 } 6608 }, comp.uid); 6609 6610 Events.addEvent(dropZone, 'dragenter', function(e) { 6611 e.preventDefault(); 6612 e.stopPropagation(); 6613 comp.trigger("dragenter"); 6614 }, comp.uid); 6615 6616 Events.addEvent(dropZone, 'dragleave', function(e) { 6617 e.preventDefault(); 6618 e.stopPropagation(); 6619 comp.trigger("dragleave"); 6620 }, comp.uid); 6621 }, 6622 6623 getFiles: function() { 6624 return _files; 6625 }, 6626 6627 destroy: function() { 6628 Events.removeAllEvents(_options && Dom.get(_options.container), this.uid); 6629 _files = _allowedExts = _options = null; 6630 } 6631 }); 6632 6633 6634 function _extractExts(accept) { 6635 var exts = []; 6636 for (var i = 0; i < accept.length; i++) { 6637 [].push.apply(exts, accept[i].extensions.split(/\s*,\s*/)); 6638 } 6639 return Basic.inArray('*', exts) === -1 ? exts : []; 6640 } 6641 6642 6643 function _isAcceptable(file) { 6644 var ext = Mime.getFileExtension(file.name); 6645 return !ext || !_allowedExts.length || Basic.inArray(ext, _allowedExts) !== -1; 6646 } 6647 6648 6649 function _readItems(items, cb) { 6650 var entries = []; 6651 Basic.each(items, function(item) { 6652 var entry = item.webkitGetAsEntry(); 6653 // Address #998 (https://code.google.com/p/chromium/issues/detail?id=332579) 6654 if (entry) { 6655 // file() fails on OSX when the filename contains a special character (e.g. umlaut): see #61 6656 if (entry.isFile) { 6657 var file = item.getAsFile(); 6658 if (_isAcceptable(file)) { 6659 _files.push(file); 6660 } 6661 } else { 6662 entries.push(entry); 6663 } 6664 } 6665 }); 6666 6667 if (entries.length) { 6668 _readEntries(entries, cb); 6669 } else { 6670 cb(); 6671 } 6672 } 6673 6674 6675 function _readEntries(entries, cb) { 6676 var queue = []; 6677 Basic.each(entries, function(entry) { 6678 queue.push(function(cbcb) { 6679 _readEntry(entry, cbcb); 6680 }); 6681 }); 6682 Basic.inSeries(queue, function() { 6683 cb(); 6684 }); 6685 } 6686 6687 function _readEntry(entry, cb) { 6688 if (entry.isFile) { 6689 entry.file(function(file) { 6690 if (_isAcceptable(file)) { 6691 _files.push(file); 6692 } 6693 cb(); 6694 }, function() { 6695 // fire an error event maybe 6696 cb(); 6697 }); 6698 } else if (entry.isDirectory) { 6699 _readDirEntry(entry, cb); 6700 } else { 6701 cb(); // not file, not directory? what then?.. 6702 } 6703 } 6704 6705 function _readDirEntry(dirEntry, cb) { 6706 var entries = [], dirReader = dirEntry.createReader(); 6707 6708 // keep quering recursively till no more entries 6709 function getEntries(cbcb) { 6710 dirReader.readEntries(function(moreEntries) { 6711 if (moreEntries.length) { 6712 [].push.apply(entries, moreEntries); 6713 getEntries(cbcb); 6714 } else { 6715 cbcb(); 6716 } 6717 }, cbcb); 6718 } 6719 6720 // ...and you thought FileReader was crazy... 6721 getEntries(function() { 6722 _readEntries(entries, cb); 6723 }); 6724 } 6725 } 6726 6727 return (extensions.FileDrop = FileDrop); 6728 }); 6729 6730 // Included from: src/javascript/runtime/html5/file/FileReader.js 6731 6732 /** 6733 * FileReader.js 6734 * 6735 * Copyright 2013, Moxiecode Systems AB 6736 * Released under GPL License. 6737 * 6738 * License: http://www.plupload.com/license 6739 * Contributing: http://www.plupload.com/contributing 6740 */ 6741 6742 /** 6743 @class moxie/runtime/html5/file/FileReader 6744 @private 6745 */ 6746 define("moxie/runtime/html5/file/FileReader", [ 6747 "moxie/runtime/html5/Runtime", 6748 "moxie/core/utils/Encode", 6749 "moxie/core/utils/Basic" 6750 ], function(extensions, Encode, Basic) { 6751 6752 function FileReader() { 6753 var _fr, _convertToBinary = false; 6754 6755 Basic.extend(this, { 6756 6757 read: function(op, blob) { 6758 var target = this; 6759 6760 _fr = new window.FileReader(); 6761 6762 _fr.addEventListener('progress', function(e) { 6763 target.trigger(e); 6764 }); 6765 6766 _fr.addEventListener('load', function(e) { 6767 target.trigger(e); 6768 }); 6769 6770 _fr.addEventListener('error', function(e) { 6771 target.trigger(e, _fr.error); 6772 }); 6773 6774 _fr.addEventListener('loadend', function() { 6775 _fr = null; 6776 }); 6777 6778 if (Basic.typeOf(_fr[op]) === 'function') { 6779 _convertToBinary = false; 6780 _fr[op](blob.getSource()); 6781 } else if (op === 'readAsBinaryString') { // readAsBinaryString is depricated in general and never existed in IE10+ 6782 _convertToBinary = true; 6783 _fr.readAsDataURL(blob.getSource()); 6784 } 6785 }, 6786 6787 getResult: function() { 6788 return _fr && _fr.result ? (_convertToBinary ? _toBinary(_fr.result) : _fr.result) : null; 6789 }, 6790 6791 abort: function() { 6792 if (_fr) { 6793 _fr.abort(); 6794 } 6795 }, 6796 6797 destroy: function() { 6798 _fr = null; 6799 } 6800 }); 6801 6802 function _toBinary(str) { 6803 return Encode.atob(str.substring(str.indexOf('base64,') + 7)); 6804 } 6805 } 6806 6807 return (extensions.FileReader = FileReader); 6808 }); 6809 6810 // Included from: src/javascript/runtime/html5/xhr/XMLHttpRequest.js 6811 6812 /** 6813 * XMLHttpRequest.js 6814 * 6815 * Copyright 2013, Moxiecode Systems AB 6816 * Released under GPL License. 6817 * 6818 * License: http://www.plupload.com/license 6819 * Contributing: http://www.plupload.com/contributing 6820 */ 6821 6822 /*global ActiveXObject:true */ 6823 6824 /** 6825 @class moxie/runtime/html5/xhr/XMLHttpRequest 6826 @private 6827 */ 6828 define("moxie/runtime/html5/xhr/XMLHttpRequest", [ 6829 "moxie/runtime/html5/Runtime", 6830 "moxie/core/utils/Basic", 6831 "moxie/core/utils/Mime", 6832 "moxie/core/utils/Url", 6833 "moxie/file/File", 6834 "moxie/file/Blob", 6835 "moxie/xhr/FormData", 6836 "moxie/core/Exceptions", 6837 "moxie/core/utils/Env" 6838 ], function(extensions, Basic, Mime, Url, File, Blob, FormData, x, Env) { 6839 6840 function XMLHttpRequest() { 6841 var self = this 6842 , _xhr 6843 , _filename 6844 ; 6845 6846 Basic.extend(this, { 6847 send: function(meta, data) { 6848 var target = this 6849 , isGecko2_5_6 = (Env.browser === 'Mozilla' && Env.version >= 4 && Env.version < 7) 6850 , isAndroidBrowser = Env.browser === 'Android Browser' 6851 , mustSendAsBinary = false 6852 ; 6853 6854 // extract file name 6855 _filename = meta.url.replace(/^.+?\/([\w\-\.]+)$/, '$1').toLowerCase(); 6856 6857 _xhr = _getNativeXHR(); 6858 _xhr.open(meta.method, meta.url, meta.async, meta.user, meta.password); 6859 6860 6861 // prepare data to be sent 6862 if (data instanceof Blob) { 6863 if (data.isDetached()) { 6864 mustSendAsBinary = true; 6865 } 6866 data = data.getSource(); 6867 } else if (data instanceof FormData) { 6868 6869 if (data.hasBlob()) { 6870 if (data.getBlob().isDetached()) { 6871 data = _prepareMultipart.call(target, data); // _xhr must be instantiated and be in OPENED state 6872 mustSendAsBinary = true; 6873 } else if ((isGecko2_5_6 || isAndroidBrowser) && Basic.typeOf(data.getBlob().getSource()) === 'blob' && window.FileReader) { 6874 // Gecko 2/5/6 can't send blob in FormData: https://bugzilla.mozilla.org/show_bug.cgi?id=649150 6875 // Android browsers (default one and Dolphin) seem to have the same issue, see: #613 6876 _preloadAndSend.call(target, meta, data); 6877 return; // _preloadAndSend will reinvoke send() with transmutated FormData =%D 6878 } 6879 } 6880 6881 // transfer fields to real FormData 6882 if (data instanceof FormData) { // if still a FormData, e.g. not mangled by _prepareMultipart() 6883 var fd = new window.FormData(); 6884 data.each(function(value, name) { 6885 if (value instanceof Blob) { 6886 fd.append(name, value.getSource()); 6887 } else { 6888 fd.append(name, value); 6889 } 6890 }); 6891 data = fd; 6892 } 6893 } 6894 6895 6896 // if XHR L2 6897 if (_xhr.upload) { 6898 if (meta.withCredentials) { 6899 _xhr.withCredentials = true; 6900 } 6901 6902 _xhr.addEventListener('load', function(e) { 6903 target.trigger(e); 6904 }); 6905 6906 _xhr.addEventListener('error', function(e) { 6907 target.trigger(e); 6908 }); 6909 6910 // additionally listen to progress events 6911 _xhr.addEventListener('progress', function(e) { 6912 target.trigger(e); 6913 }); 6914 6915 _xhr.upload.addEventListener('progress', function(e) { 6916 target.trigger({ 6917 type: 'UploadProgress', 6918 loaded: e.loaded, 6919 total: e.total 6920 }); 6921 }); 6922 // ... otherwise simulate XHR L2 6923 } else { 6924 _xhr.onreadystatechange = function onReadyStateChange() { 6925 6926 // fake Level 2 events 6927 switch (_xhr.readyState) { 6928 6929 case 1: // XMLHttpRequest.OPENED 6930 // readystatechanged is fired twice for OPENED state (in IE and Mozilla) - neu 6931 break; 6932 6933 // looks like HEADERS_RECEIVED (state 2) is not reported in Opera (or it's old versions) - neu 6934 case 2: // XMLHttpRequest.HEADERS_RECEIVED 6935 break; 6936 6937 case 3: // XMLHttpRequest.LOADING 6938 // try to fire progress event for not XHR L2 6939 var total, loaded; 6940 6941 try { 6942 if (Url.hasSameOrigin(meta.url)) { // Content-Length not accessible for cross-domain on some browsers 6943 total = _xhr.getResponseHeader('Content-Length') || 0; // old Safari throws an exception here 6944 } 6945 6946 if (_xhr.responseText) { // responseText was introduced in IE7 6947 loaded = _xhr.responseText.length; 6948 } 6949 } catch(ex) { 6950 total = loaded = 0; 6951 } 6952 6953 target.trigger({ 6954 type: 'progress', 6955 lengthComputable: !!total, 6956 total: parseInt(total, 10), 6957 loaded: loaded 6958 }); 6959 break; 6960 6961 case 4: // XMLHttpRequest.DONE 6962 // release readystatechange handler (mostly for IE) 6963 _xhr.onreadystatechange = function() {}; 6964 6965 // usually status 0 is returned when server is unreachable, but FF also fails to status 0 for 408 timeout 6966 if (_xhr.status === 0) { 6967 target.trigger('error'); 6968 } else { 6969 target.trigger('load'); 6970 } 6971 break; 6972 } 6973 }; 6974 } 6975 6976 6977 // set request headers 6978 if (!Basic.isEmptyObj(meta.headers)) { 6979 Basic.each(meta.headers, function(value, header) { 6980 _xhr.setRequestHeader(header, value); 6981 }); 6982 } 6983 6984 // request response type 6985 if ("" !== meta.responseType && 'responseType' in _xhr) { 6986 if ('json' === meta.responseType && !Env.can('return_response_type', 'json')) { // we can fake this one 6987 _xhr.responseType = 'text'; 6988 } else { 6989 _xhr.responseType = meta.responseType; 6990 } 6991 } 6992 6993 // send ... 6994 if (!mustSendAsBinary) { 6995 _xhr.send(data); 6996 } else { 6997 if (_xhr.sendAsBinary) { // Gecko 6998 _xhr.sendAsBinary(data); 6999 } else { // other browsers having support for typed arrays 7000 (function() { 7001 // mimic Gecko's sendAsBinary 7002 var ui8a = new Uint8Array(data.length); 7003 for (var i = 0; i < data.length; i++) { 7004 ui8a[i] = (data.charCodeAt(i) & 0xff); 7005 } 7006 _xhr.send(ui8a.buffer); 7007 }()); 7008 } 7009 } 7010 7011 target.trigger('loadstart'); 7012 }, 7013 7014 getStatus: function() { 7015 // according to W3C spec it should return 0 for readyState < 3, but instead it throws an exception 7016 try { 7017 if (_xhr) { 7018 return _xhr.status; 7019 } 7020 } catch(ex) {} 7021 return 0; 7022 }, 7023 7024 getResponse: function(responseType) { 7025 var I = this.getRuntime(); 7026 7027 try { 7028 switch (responseType) { 7029 case 'blob': 7030 var file = new File(I.uid, _xhr.response); 7031 7032 // try to extract file name from content-disposition if possible (might be - not, if CORS for example) 7033 var disposition = _xhr.getResponseHeader('Content-Disposition'); 7034 if (disposition) { 7035 // extract filename from response header if available 7036 var match = disposition.match(/filename=([\'\"'])([^\1]+)\1/); 7037 if (match) { 7038 _filename = match[2]; 7039 } 7040 } 7041 file.name = _filename; 7042 7043 // pre-webkit Opera doesn't set type property on the blob response 7044 if (!file.type) { 7045 file.type = Mime.getFileMime(_filename); 7046 } 7047 return file; 7048 7049 case 'json': 7050 if (!Env.can('return_response_type', 'json')) { 7051 return _xhr.status === 200 && !!window.JSON ? JSON.parse(_xhr.responseText) : null; 7052 } 7053 return _xhr.response; 7054 7055 case 'document': 7056 return _getDocument(_xhr); 7057 7058 default: 7059 return _xhr.responseText !== '' ? _xhr.responseText : null; // against the specs, but for consistency across the runtimes 7060 } 7061 } catch(ex) { 7062 return null; 7063 } 7064 }, 7065 7066 getAllResponseHeaders: function() { 7067 try { 7068 return _xhr.getAllResponseHeaders(); 7069 } catch(ex) {} 7070 return ''; 7071 }, 7072 7073 abort: function() { 7074 if (_xhr) { 7075 _xhr.abort(); 7076 } 7077 }, 7078 7079 destroy: function() { 7080 self = _filename = null; 7081 } 7082 }); 7083 7084 7085 // here we go... ugly fix for ugly bug 7086 function _preloadAndSend(meta, data) { 7087 var target = this, blob, fr; 7088 7089 // get original blob 7090 blob = data.getBlob().getSource(); 7091 7092 // preload blob in memory to be sent as binary string 7093 fr = new window.FileReader(); 7094 fr.onload = function() { 7095 // overwrite original blob 7096 data.append(data.getBlobName(), new Blob(null, { 7097 type: blob.type, 7098 data: fr.result 7099 })); 7100 // invoke send operation again 7101 self.send.call(target, meta, data); 7102 }; 7103 fr.readAsBinaryString(blob); 7104 } 7105 7106 7107 function _getNativeXHR() { 7108 if (window.XMLHttpRequest && !(Env.browser === 'IE' && Env.version < 8)) { // IE7 has native XHR but it's buggy 7109 return new window.XMLHttpRequest(); 7110 } else { 7111 return (function() { 7112 var progIDs = ['Msxml2.XMLHTTP.6.0', 'Microsoft.XMLHTTP']; // if 6.0 available, use it, otherwise failback to default 3.0 7113 for (var i = 0; i < progIDs.length; i++) { 7114 try { 7115 return new ActiveXObject(progIDs[i]); 7116 } catch (ex) {} 7117 } 7118 })(); 7119 } 7120 } 7121 7122 // @credits Sergey Ilinsky (http://www.ilinsky.com/) 7123 function _getDocument(xhr) { 7124 var rXML = xhr.responseXML; 7125 var rText = xhr.responseText; 7126 7127 // Try parsing responseText (@see: http://www.ilinsky.com/articles/XMLHttpRequest/#bugs-ie-responseXML-content-type) 7128 if (Env.browser === 'IE' && rText && rXML && !rXML.documentElement && /[^\/]+\/[^\+]+\+xml/.test(xhr.getResponseHeader("Content-Type"))) { 7129 rXML = new window.ActiveXObject("Microsoft.XMLDOM"); 7130 rXML.async = false; 7131 rXML.validateOnParse = false; 7132 rXML.loadXML(rText); 7133 } 7134 7135 // Check if there is no error in document 7136 if (rXML) { 7137 if ((Env.browser === 'IE' && rXML.parseError !== 0) || !rXML.documentElement || rXML.documentElement.tagName === "parsererror") { 7138 return null; 7139 } 7140 } 7141 return rXML; 7142 } 7143 7144 7145 function _prepareMultipart(fd) { 7146 var boundary = '----moxieboundary' + new Date().getTime() 7147 , dashdash = '--' 7148 , crlf = '\r\n' 7149 , multipart = '' 7150 , I = this.getRuntime() 7151 ; 7152 7153 if (!I.can('send_binary_string')) { 7154 throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR); 7155 } 7156 7157 _xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary); 7158 7159 // append multipart parameters 7160 fd.each(function(value, name) { 7161 // Firefox 3.6 failed to convert multibyte characters to UTF-8 in sendAsBinary(), 7162 // so we try it here ourselves with: unescape(encodeURIComponent(value)) 7163 if (value instanceof Blob) { 7164 // Build RFC2388 blob 7165 multipart += dashdash + boundary + crlf + 7166 'Content-Disposition: form-data; name="' + name + '"; filename="' + unescape(encodeURIComponent(value.name || 'blob')) + '"' + crlf + 7167 'Content-Type: ' + (value.type || 'application/octet-stream') + crlf + crlf + 7168 value.getSource() + crlf; 7169 } else { 7170 multipart += dashdash + boundary + crlf + 7171 'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf + 7172 unescape(encodeURIComponent(value)) + crlf; 7173 } 7174 }); 7175 7176 multipart += dashdash + boundary + dashdash + crlf; 7177 7178 return multipart; 7179 } 7180 } 7181 7182 return (extensions.XMLHttpRequest = XMLHttpRequest); 7183 }); 7184 7185 // Included from: src/javascript/runtime/html5/utils/BinaryReader.js 7186 7187 /** 7188 * BinaryReader.js 7189 * 7190 * Copyright 2013, Moxiecode Systems AB 7191 * Released under GPL License. 7192 * 7193 * License: http://www.plupload.com/license 7194 * Contributing: http://www.plupload.com/contributing 7195 */ 7196 7197 /** 7198 @class moxie/runtime/html5/utils/BinaryReader 7199 @private 7200 */ 7201 define("moxie/runtime/html5/utils/BinaryReader", [], function() { 7202 return function() { 7203 var II = false, bin; 7204 7205 // Private functions 7206 function read(idx, size) { 7207 var mv = II ? 0 : -8 * (size - 1), sum = 0, i; 7208 7209 for (i = 0; i < size; i++) { 7210 sum |= (bin.charCodeAt(idx + i) << Math.abs(mv + i*8)); 7211 } 7212 7213 return sum; 7214 } 7215 7216 function putstr(segment, idx, length) { 7217 length = arguments.length === 3 ? length : bin.length - idx - 1; 7218 bin = bin.substr(0, idx) + segment + bin.substr(length + idx); 7219 } 7220 7221 function write(idx, num, size) { 7222 var str = '', mv = II ? 0 : -8 * (size - 1), i; 7223 7224 for (i = 0; i < size; i++) { 7225 str += String.fromCharCode((num >> Math.abs(mv + i*8)) & 255); 7226 } 7227 7228 putstr(str, idx, size); 7229 } 7230 7231 // Public functions 7232 return { 7233 II: function(order) { 7234 if (order === undefined) { 7235 return II; 7236 } else { 7237 II = order; 7238 } 7239 }, 7240 7241 init: function(binData) { 7242 II = false; 7243 bin = binData; 7244 }, 7245 7246 SEGMENT: function(idx, length, segment) { 7247 switch (arguments.length) { 7248 case 1: 7249 return bin.substr(idx, bin.length - idx - 1); 7250 case 2: 7251 return bin.substr(idx, length); 7252 case 3: 7253 putstr(segment, idx, length); 7254 break; 7255 default: return bin; 7256 } 7257 }, 7258 7259 BYTE: function(idx) { 7260 return read(idx, 1); 7261 }, 7262 7263 SHORT: function(idx) { 7264 return read(idx, 2); 7265 }, 7266 7267 LONG: function(idx, num) { 7268 if (num === undefined) { 7269 return read(idx, 4); 7270 } else { 7271 write(idx, num, 4); 7272 } 7273 }, 7274 7275 SLONG: function(idx) { // 2's complement notation 7276 var num = read(idx, 4); 7277 7278 return (num > 2147483647 ? num - 4294967296 : num); 7279 }, 7280 7281 STRING: function(idx, size) { 7282 var str = ''; 7283 7284 for (size += idx; idx < size; idx++) { 7285 str += String.fromCharCode(read(idx, 1)); 7286 } 7287 7288 return str; 7289 } 7290 }; 7291 }; 7292 }); 7293 7294 // Included from: src/javascript/runtime/html5/image/JPEGHeaders.js 7295 7296 /** 7297 * JPEGHeaders.js 7298 * 7299 * Copyright 2013, Moxiecode Systems AB 7300 * Released under GPL License. 7301 * 7302 * License: http://www.plupload.com/license 7303 * Contributing: http://www.plupload.com/contributing 7304 */ 7305 7306 /** 7307 @class moxie/runtime/html5/image/JPEGHeaders 7308 @private 7309 */ 7310 define("moxie/runtime/html5/image/JPEGHeaders", [ 7311 "moxie/runtime/html5/utils/BinaryReader" 7312 ], function(BinaryReader) { 7313 7314 return function JPEGHeaders(data) { 7315 var headers = [], read, idx, marker, length = 0; 7316 7317 read = new BinaryReader(); 7318 read.init(data); 7319 7320 // Check if data is jpeg 7321 if (read.SHORT(0) !== 0xFFD8) { 7322 return; 7323 } 7324 7325 idx = 2; 7326 7327 while (idx <= data.length) { 7328 marker = read.SHORT(idx); 7329 7330 // omit RST (restart) markers 7331 if (marker >= 0xFFD0 && marker <= 0xFFD7) { 7332 idx += 2; 7333 continue; 7334 } 7335 7336 // no headers allowed after SOS marker 7337 if (marker === 0xFFDA || marker === 0xFFD9) { 7338 break; 7339 } 7340 7341 length = read.SHORT(idx + 2) + 2; 7342 7343 // APPn marker detected 7344 if (marker >= 0xFFE1 && marker <= 0xFFEF) { 7345 headers.push({ 7346 hex: marker, 7347 name: 'APP' + (marker & 0x000F), 7348 start: idx, 7349 length: length, 7350 segment: read.SEGMENT(idx, length) 7351 }); 7352 } 7353 7354 idx += length; 7355 } 7356 7357 read.init(null); // free memory 7358 7359 return { 7360 headers: headers, 7361 7362 restore: function(data) { 7363 var max, i; 7364 7365 read.init(data); 7366 7367 idx = read.SHORT(2) == 0xFFE0 ? 4 + read.SHORT(4) : 2; 7368 7369 for (i = 0, max = headers.length; i < max; i++) { 7370 read.SEGMENT(idx, 0, headers[i].segment); 7371 idx += headers[i].length; 7372 } 7373 7374 data = read.SEGMENT(); 7375 read.init(null); 7376 return data; 7377 }, 7378 7379 strip: function(data) { 7380 var headers, jpegHeaders, i; 7381 7382 jpegHeaders = new JPEGHeaders(data); 7383 headers = jpegHeaders.headers; 7384 jpegHeaders.purge(); 7385 7386 read.init(data); 7387 7388 i = headers.length; 7389 while (i--) { 7390 read.SEGMENT(headers[i].start, headers[i].length, ''); 7391 } 7392 7393 data = read.SEGMENT(); 7394 read.init(null); 7395 return data; 7396 }, 7397 7398 get: function(name) { 7399 var array = []; 7400 7401 for (var i = 0, max = headers.length; i < max; i++) { 7402 if (headers[i].name === name.toUpperCase()) { 7403 array.push(headers[i].segment); 7404 } 7405 } 7406 return array; 7407 }, 7408 7409 set: function(name, segment) { 7410 var array = [], i, ii, max; 7411 7412 if (typeof(segment) === 'string') { 7413 array.push(segment); 7414 } else { 7415 array = segment; 7416 } 7417 7418 for (i = ii = 0, max = headers.length; i < max; i++) { 7419 if (headers[i].name === name.toUpperCase()) { 7420 headers[i].segment = array[ii]; 7421 headers[i].length = array[ii].length; 7422 ii++; 7423 } 7424 if (ii >= array.length) { 7425 break; 7426 } 7427 } 7428 }, 7429 7430 purge: function() { 7431 headers = []; 7432 read.init(null); 7433 read = null; 7434 } 7435 }; 7436 }; 7437 }); 7438 7439 // Included from: src/javascript/runtime/html5/image/ExifParser.js 7440 7441 /** 7442 * ExifParser.js 7443 * 7444 * Copyright 2013, Moxiecode Systems AB 7445 * Released under GPL License. 7446 * 7447 * License: http://www.plupload.com/license 7448 * Contributing: http://www.plupload.com/contributing 7449 */ 7450 7451 /** 7452 @class moxie/runtime/html5/image/ExifParser 7453 @private 7454 */ 7455 define("moxie/runtime/html5/image/ExifParser", [ 7456 "moxie/core/utils/Basic", 7457 "moxie/runtime/html5/utils/BinaryReader" 7458 ], function(Basic, BinaryReader) { 7459 7460 return function ExifParser() { 7461 // Private ExifParser fields 7462 var data, tags, Tiff, offsets = {}, tagDescs; 7463 7464 data = new BinaryReader(); 7465 7466 tags = { 7467 tiff : { 7468 /* 7469 The image orientation viewed in terms of rows and columns. 7470 7471 1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side. 7472 2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side. 7473 3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side. 7474 4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side. 7475 5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top. 7476 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top. 7477 7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom. 7478 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom. 7479 */ 7480 0x0112: 'Orientation', 7481 0x010E: 'ImageDescription', 7482 0x010F: 'Make', 7483 0x0110: 'Model', 7484 0x0131: 'Software', 7485 0x8769: 'ExifIFDPointer', 7486 0x8825: 'GPSInfoIFDPointer' 7487 }, 7488 exif : { 7489 0x9000: 'ExifVersion', 7490 0xA001: 'ColorSpace', 7491 0xA002: 'PixelXDimension', 7492 0xA003: 'PixelYDimension', 7493 0x9003: 'DateTimeOriginal', 7494 0x829A: 'ExposureTime', 7495 0x829D: 'FNumber', 7496 0x8827: 'ISOSpeedRatings', 7497 0x9201: 'ShutterSpeedValue', 7498 0x9202: 'ApertureValue' , 7499 0x9207: 'MeteringMode', 7500 0x9208: 'LightSource', 7501 0x9209: 'Flash', 7502 0x920A: 'FocalLength', 7503 0xA402: 'ExposureMode', 7504 0xA403: 'WhiteBalance', 7505 0xA406: 'SceneCaptureType', 7506 0xA404: 'DigitalZoomRatio', 7507 0xA408: 'Contrast', 7508 0xA409: 'Saturation', 7509 0xA40A: 'Sharpness' 7510 }, 7511 gps : { 7512 0x0000: 'GPSVersionID', 7513 0x0001: 'GPSLatitudeRef', 7514 0x0002: 'GPSLatitude', 7515 0x0003: 'GPSLongitudeRef', 7516 0x0004: 'GPSLongitude' 7517 } 7518 }; 7519 7520 tagDescs = { 7521 'ColorSpace': { 7522 1: 'sRGB', 7523 0: 'Uncalibrated' 7524 }, 7525 7526 'MeteringMode': { 7527 0: 'Unknown', 7528 1: 'Average', 7529 2: 'CenterWeightedAverage', 7530 3: 'Spot', 7531 4: 'MultiSpot', 7532 5: 'Pattern', 7533 6: 'Partial', 7534 255: 'Other' 7535 }, 7536 7537 'LightSource': { 7538 1: 'Daylight', 7539 2: 'Fliorescent', 7540 3: 'Tungsten', 7541 4: 'Flash', 7542 9: 'Fine weather', 7543 10: 'Cloudy weather', 7544 11: 'Shade', 7545 12: 'Daylight fluorescent (D 5700 - 7100K)', 7546 13: 'Day white fluorescent (N 4600 -5400K)', 7547 14: 'Cool white fluorescent (W 3900 - 4500K)', 7548 15: 'White fluorescent (WW 3200 - 3700K)', 7549 17: 'Standard light A', 7550 18: 'Standard light B', 7551 19: 'Standard light C', 7552 20: 'D55', 7553 21: 'D65', 7554 22: 'D75', 7555 23: 'D50', 7556 24: 'ISO studio tungsten', 7557 255: 'Other' 7558 }, 7559 7560 'Flash': { 7561 0x0000: 'Flash did not fire.', 7562 0x0001: 'Flash fired.', 7563 0x0005: 'Strobe return light not detected.', 7564 0x0007: 'Strobe return light detected.', 7565 0x0009: 'Flash fired, compulsory flash mode', 7566 0x000D: 'Flash fired, compulsory flash mode, return light not detected', 7567 0x000F: 'Flash fired, compulsory flash mode, return light detected', 7568 0x0010: 'Flash did not fire, compulsory flash mode', 7569 0x0018: 'Flash did not fire, auto mode', 7570 0x0019: 'Flash fired, auto mode', 7571 0x001D: 'Flash fired, auto mode, return light not detected', 7572 0x001F: 'Flash fired, auto mode, return light detected', 7573 0x0020: 'No flash function', 7574 0x0041: 'Flash fired, red-eye reduction mode', 7575 0x0045: 'Flash fired, red-eye reduction mode, return light not detected', 7576 0x0047: 'Flash fired, red-eye reduction mode, return light detected', 7577 0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode', 7578 0x004D: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected', 7579 0x004F: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected', 7580 0x0059: 'Flash fired, auto mode, red-eye reduction mode', 7581 0x005D: 'Flash fired, auto mode, return light not detected, red-eye reduction mode', 7582 0x005F: 'Flash fired, auto mode, return light detected, red-eye reduction mode' 7583 }, 7584 7585 'ExposureMode': { 7586 0: 'Auto exposure', 7587 1: 'Manual exposure', 7588 2: 'Auto bracket' 7589 }, 7590 7591 'WhiteBalance': { 7592 0: 'Auto white balance', 7593 1: 'Manual white balance' 7594 }, 7595 7596 'SceneCaptureType': { 7597 0: 'Standard', 7598 1: 'Landscape', 7599 2: 'Portrait', 7600 3: 'Night scene' 7601 }, 7602 7603 'Contrast': { 7604 0: 'Normal', 7605 1: 'Soft', 7606 2: 'Hard' 7607 }, 7608 7609 'Saturation': { 7610 0: 'Normal', 7611 1: 'Low saturation', 7612 2: 'High saturation' 7613 }, 7614 7615 'Sharpness': { 7616 0: 'Normal', 7617 1: 'Soft', 7618 2: 'Hard' 7619 }, 7620 7621 // GPS related 7622 'GPSLatitudeRef': { 7623 N: 'North latitude', 7624 S: 'South latitude' 7625 }, 7626 7627 'GPSLongitudeRef': { 7628 E: 'East longitude', 7629 W: 'West longitude' 7630 } 7631 }; 7632 7633 function extractTags(IFD_offset, tags2extract) { 7634 var length = data.SHORT(IFD_offset), i, ii, 7635 tag, type, count, tagOffset, offset, value, values = [], hash = {}; 7636 7637 for (i = 0; i < length; i++) { 7638 // Set binary reader pointer to beginning of the next tag 7639 offset = tagOffset = IFD_offset + 12 * i + 2; 7640 7641 tag = tags2extract[data.SHORT(offset)]; 7642 7643 if (tag === undefined) { 7644 continue; // Not the tag we requested 7645 } 7646 7647 type = data.SHORT(offset+=2); 7648 count = data.LONG(offset+=2); 7649 7650 offset += 4; 7651 values = []; 7652 7653 switch (type) { 7654 case 1: // BYTE 7655 case 7: // UNDEFINED 7656 if (count > 4) { 7657 offset = data.LONG(offset) + offsets.tiffHeader; 7658 } 7659 7660 for (ii = 0; ii < count; ii++) { 7661 values[ii] = data.BYTE(offset + ii); 7662 } 7663 7664 break; 7665 7666 case 2: // STRING 7667 if (count > 4) { 7668 offset = data.LONG(offset) + offsets.tiffHeader; 7669 } 7670 7671 hash[tag] = data.STRING(offset, count - 1); 7672 7673 continue; 7674 7675 case 3: // SHORT 7676 if (count > 2) { 7677 offset = data.LONG(offset) + offsets.tiffHeader; 7678 } 7679 7680 for (ii = 0; ii < count; ii++) { 7681 values[ii] = data.SHORT(offset + ii*2); 7682 } 7683 7684 break; 7685 7686 case 4: // LONG 7687 if (count > 1) { 7688 offset = data.LONG(offset) + offsets.tiffHeader; 7689 } 7690 7691 for (ii = 0; ii < count; ii++) { 7692 values[ii] = data.LONG(offset + ii*4); 7693 } 7694 7695 break; 7696 7697 case 5: // RATIONAL 7698 offset = data.LONG(offset) + offsets.tiffHeader; 7699 7700 for (ii = 0; ii < count; ii++) { 7701 values[ii] = data.LONG(offset + ii*4) / data.LONG(offset + ii*4 + 4); 7702 } 7703 7704 break; 7705 7706 case 9: // SLONG 7707 offset = data.LONG(offset) + offsets.tiffHeader; 7708 7709 for (ii = 0; ii < count; ii++) { 7710 values[ii] = data.SLONG(offset + ii*4); 7711 } 7712 7713 break; 7714 7715 case 10: // SRATIONAL 7716 offset = data.LONG(offset) + offsets.tiffHeader; 7717 7718 for (ii = 0; ii < count; ii++) { 7719 values[ii] = data.SLONG(offset + ii*4) / data.SLONG(offset + ii*4 + 4); 7720 } 7721 7722 break; 7723 7724 default: 7725 continue; 7726 } 7727 7728 value = (count == 1 ? values[0] : values); 7729 7730 if (tagDescs.hasOwnProperty(tag) && typeof value != 'object') { 7731 hash[tag] = tagDescs[tag][value]; 7732 } else { 7733 hash[tag] = value; 7734 } 7735 } 7736 7737 return hash; 7738 } 7739 7740 function getIFDOffsets() { 7741 var idx = offsets.tiffHeader; 7742 7743 // Set read order of multi-byte data 7744 data.II(data.SHORT(idx) == 0x4949); 7745 7746 // Check if always present bytes are indeed present 7747 if (data.SHORT(idx+=2) !== 0x002A) { 7748 return false; 7749 } 7750 7751 offsets.IFD0 = offsets.tiffHeader + data.LONG(idx += 2); 7752 Tiff = extractTags(offsets.IFD0, tags.tiff); 7753 7754 if ('ExifIFDPointer' in Tiff) { 7755 offsets.exifIFD = offsets.tiffHeader + Tiff.ExifIFDPointer; 7756 delete Tiff.ExifIFDPointer; 7757 } 7758 7759 if ('GPSInfoIFDPointer' in Tiff) { 7760 offsets.gpsIFD = offsets.tiffHeader + Tiff.GPSInfoIFDPointer; 7761 delete Tiff.GPSInfoIFDPointer; 7762 } 7763 return true; 7764 } 7765 7766 // At the moment only setting of simple (LONG) values, that do not require offset recalculation, is supported 7767 function setTag(ifd, tag, value) { 7768 var offset, length, tagOffset, valueOffset = 0; 7769 7770 // If tag name passed translate into hex key 7771 if (typeof(tag) === 'string') { 7772 var tmpTags = tags[ifd.toLowerCase()]; 7773 for (var hex in tmpTags) { 7774 if (tmpTags[hex] === tag) { 7775 tag = hex; 7776 break; 7777 } 7778 } 7779 } 7780 offset = offsets[ifd.toLowerCase() + 'IFD']; 7781 length = data.SHORT(offset); 7782 7783 for (var i = 0; i < length; i++) { 7784 tagOffset = offset + 12 * i + 2; 7785 7786 if (data.SHORT(tagOffset) == tag) { 7787 valueOffset = tagOffset + 8; 7788 break; 7789 } 7790 } 7791 7792 if (!valueOffset) { 7793 return false; 7794 } 7795 7796 data.LONG(valueOffset, value); 7797 return true; 7798 } 7799 7800 7801 // Public functions 7802 return { 7803 init: function(segment) { 7804 // Reset internal data 7805 offsets = { 7806 tiffHeader: 10 7807 }; 7808 7809 if (segment === undefined || !segment.length) { 7810 return false; 7811 } 7812 7813 data.init(segment); 7814 7815 // Check if that's APP1 and that it has EXIF 7816 if (data.SHORT(0) === 0xFFE1 && data.STRING(4, 5).toUpperCase() === "EXIF\0") { 7817 return getIFDOffsets(); 7818 } 7819 return false; 7820 }, 7821 7822 TIFF: function() { 7823 return Tiff; 7824 }, 7825 7826 EXIF: function() { 7827 var Exif; 7828 7829 // Populate EXIF hash 7830 Exif = extractTags(offsets.exifIFD, tags.exif); 7831 7832 // Fix formatting of some tags 7833 if (Exif.ExifVersion && Basic.typeOf(Exif.ExifVersion) === 'array') { 7834 for (var i = 0, exifVersion = ''; i < Exif.ExifVersion.length; i++) { 7835 exifVersion += String.fromCharCode(Exif.ExifVersion[i]); 7836 } 7837 Exif.ExifVersion = exifVersion; 7838 } 7839 7840 return Exif; 7841 }, 7842 7843 GPS: function() { 7844 var GPS; 7845 7846 GPS = extractTags(offsets.gpsIFD, tags.gps); 7847 7848 // iOS devices (and probably some others) do not put in GPSVersionID tag (why?..) 7849 if (GPS.GPSVersionID && Basic.typeOf(GPS.GPSVersionID) === 'array') { 7850 GPS.GPSVersionID = GPS.GPSVersionID.join('.'); 7851 } 7852 7853 return GPS; 7854 }, 7855 7856 setExif: function(tag, value) { 7857 // Right now only setting of width/height is possible 7858 if (tag !== 'PixelXDimension' && tag !== 'PixelYDimension') {return false;} 7859 7860 return setTag('exif', tag, value); 7861 }, 7862 7863 7864 getBinary: function() { 7865 return data.SEGMENT(); 7866 }, 7867 7868 purge: function() { 7869 data.init(null); 7870 data = Tiff = null; 7871 offsets = {}; 7872 } 7873 }; 7874 }; 7875 }); 7876 7877 // Included from: src/javascript/runtime/html5/image/JPEG.js 7878 7879 /** 7880 * JPEG.js 7881 * 7882 * Copyright 2013, Moxiecode Systems AB 7883 * Released under GPL License. 7884 * 7885 * License: http://www.plupload.com/license 7886 * Contributing: http://www.plupload.com/contributing 7887 */ 7888 7889 /** 7890 @class moxie/runtime/html5/image/JPEG 7891 @private 7892 */ 7893 define("moxie/runtime/html5/image/JPEG", [ 7894 "moxie/core/utils/Basic", 7895 "moxie/core/Exceptions", 7896 "moxie/runtime/html5/image/JPEGHeaders", 7897 "moxie/runtime/html5/utils/BinaryReader", 7898 "moxie/runtime/html5/image/ExifParser" 7899 ], function(Basic, x, JPEGHeaders, BinaryReader, ExifParser) { 7900 7901 function JPEG(binstr) { 7902 var _binstr, _br, _hm, _ep, _info, hasExif; 7903 7904 function _getDimensions() { 7905 var idx = 0, marker, length; 7906 7907 // examine all through the end, since some images might have very large APP segments 7908 while (idx <= _binstr.length) { 7909 marker = _br.SHORT(idx += 2); 7910 7911 if (marker >= 0xFFC0 && marker <= 0xFFC3) { // SOFn 7912 idx += 5; // marker (2 bytes) + length (2 bytes) + Sample precision (1 byte) 7913 return { 7914 height: _br.SHORT(idx), 7915 width: _br.SHORT(idx += 2) 7916 }; 7917 } 7918 length = _br.SHORT(idx += 2); 7919 idx += length - 2; 7920 } 7921 return null; 7922 } 7923 7924 _binstr = binstr; 7925 7926 _br = new BinaryReader(); 7927 _br.init(_binstr); 7928 7929 // check if it is jpeg 7930 if (_br.SHORT(0) !== 0xFFD8) { 7931 throw new x.ImageError(x.ImageError.WRONG_FORMAT); 7932 } 7933 7934 // backup headers 7935 _hm = new JPEGHeaders(binstr); 7936 7937 // extract exif info 7938 _ep = new ExifParser(); 7939 hasExif = !!_ep.init(_hm.get('app1')[0]); 7940 7941 // get dimensions 7942 _info = _getDimensions.call(this); 7943 7944 Basic.extend(this, { 7945 type: 'image/jpeg', 7946 7947 size: _binstr.length, 7948 7949 width: _info && _info.width || 0, 7950 7951 height: _info && _info.height || 0, 7952 7953 setExif: function(tag, value) { 7954 if (!hasExif) { 7955 return false; // or throw an exception 7956 } 7957 7958 if (Basic.typeOf(tag) === 'object') { 7959 Basic.each(tag, function(value, tag) { 7960 _ep.setExif(tag, value); 7961 }); 7962 } else { 7963 _ep.setExif(tag, value); 7964 } 7965 7966 // update internal headers 7967 _hm.set('app1', _ep.getBinary()); 7968 }, 7969 7970 writeHeaders: function() { 7971 if (!arguments.length) { 7972 // if no arguments passed, update headers internally 7973 return (_binstr = _hm.restore(_binstr)); 7974 } 7975 return _hm.restore(arguments[0]); 7976 }, 7977 7978 stripHeaders: function(binstr) { 7979 return _hm.strip(binstr); 7980 }, 7981 7982 purge: function() { 7983 _purge.call(this); 7984 } 7985 }); 7986 7987 if (hasExif) { 7988 this.meta = { 7989 tiff: _ep.TIFF(), 7990 exif: _ep.EXIF(), 7991 gps: _ep.GPS() 7992 }; 7993 } 7994 7995 function _purge() { 7996 if (!_ep || !_hm || !_br) { 7997 return; // ignore any repeating purge requests 7998 } 7999 _ep.purge(); 8000 _hm.purge(); 8001 _br.init(null); 8002 _binstr = _info = _hm = _ep = _br = null; 8003 } 8004 } 8005 8006 return JPEG; 8007 }); 8008 8009 // Included from: src/javascript/runtime/html5/image/PNG.js 8010 8011 /** 8012 * PNG.js 8013 * 8014 * Copyright 2013, Moxiecode Systems AB 8015 * Released under GPL License. 8016 * 8017 * License: http://www.plupload.com/license 8018 * Contributing: http://www.plupload.com/contributing 8019 */ 8020 8021 /** 8022 @class moxie/runtime/html5/image/PNG 8023 @private 8024 */ 8025 define("moxie/runtime/html5/image/PNG", [ 8026 "moxie/core/Exceptions", 8027 "moxie/core/utils/Basic", 8028 "moxie/runtime/html5/utils/BinaryReader" 8029 ], function(x, Basic, BinaryReader) { 8030 8031 function PNG(binstr) { 8032 var _binstr, _br, _hm, _ep, _info; 8033 8034 _binstr = binstr; 8035 8036 _br = new BinaryReader(); 8037 _br.init(_binstr); 8038 8039 // check if it's png 8040 (function() { 8041 var idx = 0, i = 0 8042 , signature = [0x8950, 0x4E47, 0x0D0A, 0x1A0A] 8043 ; 8044 8045 for (i = 0; i < signature.length; i++, idx += 2) { 8046 if (signature[i] != _br.SHORT(idx)) { 8047 throw new x.ImageError(x.ImageError.WRONG_FORMAT); 8048 } 8049 } 8050 }()); 8051 8052 function _getDimensions() { 8053 var chunk, idx; 8054 8055 chunk = _getChunkAt.call(this, 8); 8056 8057 if (chunk.type == 'IHDR') { 8058 idx = chunk.start; 8059 return { 8060 width: _br.LONG(idx), 8061 height: _br.LONG(idx += 4) 8062 }; 8063 } 8064 return null; 8065 } 8066 8067 function _purge() { 8068 if (!_br) { 8069 return; // ignore any repeating purge requests 8070 } 8071 _br.init(null); 8072 _binstr = _info = _hm = _ep = _br = null; 8073 } 8074 8075 _info = _getDimensions.call(this); 8076 8077 Basic.extend(this, { 8078 type: 'image/png', 8079 8080 size: _binstr.length, 8081 8082 width: _info.width, 8083 8084 height: _info.height, 8085 8086 purge: function() { 8087 _purge.call(this); 8088 } 8089 }); 8090 8091 // for PNG we can safely trigger purge automatically, as we do not keep any data for later 8092 _purge.call(this); 8093 8094 function _getChunkAt(idx) { 8095 var length, type, start, CRC; 8096 8097 length = _br.LONG(idx); 8098 type = _br.STRING(idx += 4, 4); 8099 start = idx += 4; 8100 CRC = _br.LONG(idx + length); 8101 8102 return { 8103 length: length, 8104 type: type, 8105 start: start, 8106 CRC: CRC 8107 }; 8108 } 8109 } 8110 8111 return PNG; 8112 }); 8113 8114 // Included from: src/javascript/runtime/html5/image/ImageInfo.js 8115 8116 /** 8117 * ImageInfo.js 8118 * 8119 * Copyright 2013, Moxiecode Systems AB 8120 * Released under GPL License. 8121 * 8122 * License: http://www.plupload.com/license 8123 * Contributing: http://www.plupload.com/contributing 8124 */ 8125 8126 /** 8127 @class moxie/runtime/html5/image/ImageInfo 8128 @private 8129 */ 8130 define("moxie/runtime/html5/image/ImageInfo", [ 8131 "moxie/core/utils/Basic", 8132 "moxie/core/Exceptions", 8133 "moxie/runtime/html5/image/JPEG", 8134 "moxie/runtime/html5/image/PNG" 8135 ], function(Basic, x, JPEG, PNG) { 8136 /** 8137 Optional image investigation tool for HTML5 runtime. Provides the following features: 8138 - ability to distinguish image type (JPEG or PNG) by signature 8139 - ability to extract image width/height directly from it's internals, without preloading in memory (fast) 8140 - ability to extract APP headers from JPEGs (Exif, GPS, etc) 8141 - ability to replace width/height tags in extracted JPEG headers 8142 - ability to restore APP headers, that were for example stripped during image manipulation 8143 8144 @class ImageInfo 8145 @constructor 8146 @param {String} binstr Image source as binary string 8147 */ 8148 return function(binstr) { 8149 var _cs = [JPEG, PNG], _img; 8150 8151 // figure out the format, throw: ImageError.WRONG_FORMAT if not supported 8152 _img = (function() { 8153 for (var i = 0; i < _cs.length; i++) { 8154 try { 8155 return new _cs[i](binstr); 8156 } catch (ex) { 8157 // console.info(ex); 8158 } 8159 } 8160 throw new x.ImageError(x.ImageError.WRONG_FORMAT); 8161 }()); 8162 8163 Basic.extend(this, { 8164 /** 8165 Image Mime Type extracted from it's depths 8166 8167 @property type 8168 @type {String} 8169 @default '' 8170 */ 8171 type: '', 8172 8173 /** 8174 Image size in bytes 8175 8176 @property size 8177 @type {Number} 8178 @default 0 8179 */ 8180 size: 0, 8181 8182 /** 8183 Image width extracted from image source 8184 8185 @property width 8186 @type {Number} 8187 @default 0 8188 */ 8189 width: 0, 8190 8191 /** 8192 Image height extracted from image source 8193 8194 @property height 8195 @type {Number} 8196 @default 0 8197 */ 8198 height: 0, 8199 8200 /** 8201 Sets Exif tag. Currently applicable only for width and height tags. Obviously works only with JPEGs. 8202 8203 @method setExif 8204 @param {String} tag Tag to set 8205 @param {Mixed} value Value to assign to the tag 8206 */ 8207 setExif: function() {}, 8208 8209 /** 8210 Restores headers to the source. 8211 8212 @method writeHeaders 8213 @param {String} data Image source as binary string 8214 @return {String} Updated binary string 8215 */ 8216 writeHeaders: function(data) { 8217 return data; 8218 }, 8219 8220 /** 8221 Strip all headers from the source. 8222 8223 @method stripHeaders 8224 @param {String} data Image source as binary string 8225 @return {String} Updated binary string 8226 */ 8227 stripHeaders: function(data) { 8228 return data; 8229 }, 8230 8231 /** 8232 Dispose resources. 8233 8234 @method purge 8235 */ 8236 purge: function() {} 8237 }); 8238 8239 Basic.extend(this, _img); 8240 8241 this.purge = function() { 8242 _img.purge(); 8243 _img = null; 8244 }; 8245 }; 8246 }); 8247 8248 // Included from: src/javascript/runtime/html5/image/MegaPixel.js 8249 8250 /** 8251 (The MIT License) 8252 8253 Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>; 8254 8255 Permission is hereby granted, free of charge, to any person obtaining 8256 a copy of this software and associated documentation files (the 8257 'Software'), to deal in the Software without restriction, including 8258 without limitation the rights to use, copy, modify, merge, publish, 8259 distribute, sublicense, and/or sell copies of the Software, and to 8260 permit persons to whom the Software is furnished to do so, subject to 8261 the following conditions: 8262 8263 The above copyright notice and this permission notice shall be 8264 included in all copies or substantial portions of the Software. 8265 8266 THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 8267 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 8268 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 8269 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 8270 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 8271 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 8272 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8273 */ 8274 8275 /** 8276 * Mega pixel image rendering library for iOS6 Safari 8277 * 8278 * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel), 8279 * which causes unexpected subsampling when drawing it in canvas. 8280 * By using this library, you can safely render the image with proper stretching. 8281 * 8282 * Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com> 8283 * Released under the MIT license 8284 */ 8285 8286 /** 8287 @class moxie/runtime/html5/image/MegaPixel 8288 @private 8289 */ 8290 define("moxie/runtime/html5/image/MegaPixel", [], function() { 8291 8292 /** 8293 * Rendering image element (with resizing) into the canvas element 8294 */ 8295 function renderImageToCanvas(img, canvas, options) { 8296 var iw = img.naturalWidth, ih = img.naturalHeight; 8297 var width = options.width, height = options.height; 8298 var x = options.x || 0, y = options.y || 0; 8299 var ctx = canvas.getContext('2d'); 8300 if (detectSubsampling(img)) { 8301 iw /= 2; 8302 ih /= 2; 8303 } 8304 var d = 1024; // size of tiling canvas 8305 var tmpCanvas = document.createElement('canvas'); 8306 tmpCanvas.width = tmpCanvas.height = d; 8307 var tmpCtx = tmpCanvas.getContext('2d'); 8308 var vertSquashRatio = detectVerticalSquash(img, iw, ih); 8309 var sy = 0; 8310 while (sy < ih) { 8311 var sh = sy + d > ih ? ih - sy : d; 8312 var sx = 0; 8313 while (sx < iw) { 8314 var sw = sx + d > iw ? iw - sx : d; 8315 tmpCtx.clearRect(0, 0, d, d); 8316 tmpCtx.drawImage(img, -sx, -sy); 8317 var dx = (sx * width / iw + x) << 0; 8318 var dw = Math.ceil(sw * width / iw); 8319 var dy = (sy * height / ih / vertSquashRatio + y) << 0; 8320 var dh = Math.ceil(sh * height / ih / vertSquashRatio); 8321 ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh); 8322 sx += d; 8323 } 8324 sy += d; 8325 } 8326 tmpCanvas = tmpCtx = null; 8327 } 8328 8329 /** 8330 * Detect subsampling in loaded image. 8331 * In iOS, larger images than 2M pixels may be subsampled in rendering. 8332 */ 8333 function detectSubsampling(img) { 8334 var iw = img.naturalWidth, ih = img.naturalHeight; 8335 if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image 8336 var canvas = document.createElement('canvas'); 8337 canvas.width = canvas.height = 1; 8338 var ctx = canvas.getContext('2d'); 8339 ctx.drawImage(img, -iw + 1, 0); 8340 // subsampled image becomes half smaller in rendering size. 8341 // check alpha channel value to confirm image is covering edge pixel or not. 8342 // if alpha value is 0 image is not covering, hence subsampled. 8343 return ctx.getImageData(0, 0, 1, 1).data[3] === 0; 8344 } else { 8345 return false; 8346 } 8347 } 8348 8349 8350 /** 8351 * Detecting vertical squash in loaded image. 8352 * Fixes a bug which squash image vertically while drawing into canvas for some images. 8353 */ 8354 function detectVerticalSquash(img, iw, ih) { 8355 var canvas = document.createElement('canvas'); 8356 canvas.width = 1; 8357 canvas.height = ih; 8358 var ctx = canvas.getContext('2d'); 8359 ctx.drawImage(img, 0, 0); 8360 var data = ctx.getImageData(0, 0, 1, ih).data; 8361 // search image edge pixel position in case it is squashed vertically. 8362 var sy = 0; 8363 var ey = ih; 8364 var py = ih; 8365 while (py > sy) { 8366 var alpha = data[(py - 1) * 4 + 3]; 8367 if (alpha === 0) { 8368 ey = py; 8369 } else { 8370 sy = py; 8371 } 8372 py = (ey + sy) >> 1; 8373 } 8374 canvas = null; 8375 var ratio = (py / ih); 8376 return (ratio === 0) ? 1 : ratio; 8377 } 8378 8379 return { 8380 isSubsampled: detectSubsampling, 8381 renderTo: renderImageToCanvas 8382 }; 8383 }); 8384 8385 // Included from: src/javascript/runtime/html5/image/Image.js 8386 8387 /** 8388 * Image.js 8389 * 8390 * Copyright 2013, Moxiecode Systems AB 8391 * Released under GPL License. 8392 * 8393 * License: http://www.plupload.com/license 8394 * Contributing: http://www.plupload.com/contributing 8395 */ 8396 8397 /** 8398 @class moxie/runtime/html5/image/Image 8399 @private 8400 */ 8401 define("moxie/runtime/html5/image/Image", [ 8402 "moxie/runtime/html5/Runtime", 8403 "moxie/core/utils/Basic", 8404 "moxie/core/Exceptions", 8405 "moxie/core/utils/Encode", 8406 "moxie/file/File", 8407 "moxie/runtime/html5/image/ImageInfo", 8408 "moxie/runtime/html5/image/MegaPixel", 8409 "moxie/core/utils/Mime", 8410 "moxie/core/utils/Env" 8411 ], function(extensions, Basic, x, Encode, File, ImageInfo, MegaPixel, Mime, Env) { 8412 8413 function HTML5Image() { 8414 var me = this 8415 , _img, _imgInfo, _canvas, _binStr, _blob 8416 , _modified = false // is set true whenever image is modified 8417 , _preserveHeaders = true 8418 ; 8419 8420 Basic.extend(this, { 8421 loadFromBlob: function(blob) { 8422 var comp = this, I = comp.getRuntime() 8423 , asBinary = arguments.length > 1 ? arguments[1] : true 8424 ; 8425 8426 if (!I.can('access_binary')) { 8427 throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR); 8428 } 8429 8430 _blob = blob; 8431 8432 if (blob.isDetached()) { 8433 _binStr = blob.getSource(); 8434 _preload.call(this, _binStr); 8435 return; 8436 } else { 8437 _readAsDataUrl.call(this, blob.getSource(), function(dataUrl) { 8438 if (asBinary) { 8439 _binStr = _toBinary(dataUrl); 8440 } 8441 _preload.call(comp, dataUrl); 8442 }); 8443 } 8444 }, 8445 8446 loadFromImage: function(img, exact) { 8447 this.meta = img.meta; 8448 8449 _blob = new File(null, { 8450 name: img.name, 8451 size: img.size, 8452 type: img.type 8453 }); 8454 8455 _preload.call(this, exact ? (_binStr = img.getAsBinaryString()) : img.getAsDataURL()); 8456 }, 8457 8458 getInfo: function() { 8459 var I = this.getRuntime(), info; 8460 8461 if (!_imgInfo && _binStr && I.can('access_image_binary')) { 8462 _imgInfo = new ImageInfo(_binStr); 8463 } 8464 8465 info = { 8466 width: _getImg().width || 0, 8467 height: _getImg().height || 0, 8468 type: _blob.type || Mime.getFileMime(_blob.name), 8469 size: _binStr && _binStr.length || _blob.size || 0, 8470 name: _blob.name || '', 8471 meta: _imgInfo && _imgInfo.meta || this.meta || {} 8472 }; 8473 8474 return info; 8475 }, 8476 8477 downsize: function() { 8478 _downsize.apply(this, arguments); 8479 }, 8480 8481 getAsCanvas: function() { 8482 if (_canvas) { 8483 _canvas.id = this.uid + '_canvas'; 8484 } 8485 return _canvas; 8486 }, 8487 8488 getAsBlob: function(type, quality) { 8489 if (type !== this.type) { 8490 // if different mime type requested prepare image for conversion 8491 _downsize.call(this, this.width, this.height, false); 8492 } 8493 return new File(null, { 8494 name: _blob.name || '', 8495 type: type, 8496 data: me.getAsBinaryString.call(this, type, quality) 8497 }); 8498 }, 8499 8500 getAsDataURL: function(type) { 8501 var quality = arguments[1] || 90; 8502 8503 // if image has not been modified, return the source right away 8504 if (!_modified) { 8505 return _img.src; 8506 } 8507 8508 if ('image/jpeg' !== type) { 8509 return _canvas.toDataURL('image/png'); 8510 } else { 8511 try { 8512 // older Geckos used to result in an exception on quality argument 8513 return _canvas.toDataURL('image/jpeg', quality/100); 8514 } catch (ex) { 8515 return _canvas.toDataURL('image/jpeg'); 8516 } 8517 } 8518 }, 8519 8520 getAsBinaryString: function(type, quality) { 8521 // if image has not been modified, return the source right away 8522 if (!_modified) { 8523 // if image was not loaded from binary string 8524 if (!_binStr) { 8525 _binStr = _toBinary(me.getAsDataURL(type, quality)); 8526 } 8527 return _binStr; 8528 } 8529 8530 if ('image/jpeg' !== type) { 8531 _binStr = _toBinary(me.getAsDataURL(type, quality)); 8532 } else { 8533 var dataUrl; 8534 8535 // if jpeg 8536 if (!quality) { 8537 quality = 90; 8538 } 8539 8540 try { 8541 // older Geckos used to result in an exception on quality argument 8542 dataUrl = _canvas.toDataURL('image/jpeg', quality/100); 8543 } catch (ex) { 8544 dataUrl = _canvas.toDataURL('image/jpeg'); 8545 } 8546 8547 _binStr = _toBinary(dataUrl); 8548 8549 if (_imgInfo) { 8550 _binStr = _imgInfo.stripHeaders(_binStr); 8551 8552 if (_preserveHeaders) { 8553 // update dimensions info in exif 8554 if (_imgInfo.meta && _imgInfo.meta.exif) { 8555 _imgInfo.setExif({ 8556 PixelXDimension: this.width, 8557 PixelYDimension: this.height 8558 }); 8559 } 8560 8561 // re-inject the headers 8562 _binStr = _imgInfo.writeHeaders(_binStr); 8563 } 8564 8565 // will be re-created from fresh on next getInfo call 8566 _imgInfo.purge(); 8567 _imgInfo = null; 8568 } 8569 } 8570 8571 _modified = false; 8572 8573 return _binStr; 8574 }, 8575 8576 destroy: function() { 8577 me = null; 8578 _purge.call(this); 8579 this.getRuntime().getShim().removeInstance(this.uid); 8580 } 8581 }); 8582 8583 8584 function _getImg() { 8585 if (!_canvas && !_img) { 8586 throw new x.ImageError(x.DOMException.INVALID_STATE_ERR); 8587 } 8588 return _canvas || _img; 8589 } 8590 8591 8592 function _toBinary(str) { 8593 return Encode.atob(str.substring(str.indexOf('base64,') + 7)); 8594 } 8595 8596 8597 function _toDataUrl(str, type) { 8598 return 'data:' + (type || '') + ';base64,' + Encode.btoa(str); 8599 } 8600 8601 8602 function _preload(str) { 8603 var comp = this; 8604 8605 _img = new Image(); 8606 _img.onerror = function() { 8607 _purge.call(this); 8608 comp.trigger('error', new x.ImageError(x.ImageError.WRONG_FORMAT)); 8609 }; 8610 _img.onload = function() { 8611 comp.trigger('load'); 8612 }; 8613 8614 _img.src = /^data:[^;]*;base64,/.test(str) ? str : _toDataUrl(str, _blob.type); 8615 } 8616 8617 8618 function _readAsDataUrl(file, callback) { 8619 var comp = this, fr; 8620 8621 // use FileReader if it's available 8622 if (window.FileReader) { 8623 fr = new FileReader(); 8624 fr.onload = function() { 8625 callback(this.result); 8626 }; 8627 fr.onerror = function() { 8628 comp.trigger('error', new x.FileException(x.FileException.NOT_READABLE_ERR)); 8629 }; 8630 fr.readAsDataURL(file); 8631 } else { 8632 return callback(file.getAsDataURL()); 8633 } 8634 } 8635 8636 function _downsize(width, height, crop, preserveHeaders) { 8637 var self = this 8638 , scale 8639 , mathFn 8640 , x = 0 8641 , y = 0 8642 , img 8643 , destWidth 8644 , destHeight 8645 , orientation 8646 ; 8647 8648 _preserveHeaders = preserveHeaders; // we will need to check this on export (see getAsBinaryString()) 8649 8650 // take into account orientation tag 8651 orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1; 8652 8653 if (Basic.inArray(orientation, [5,6,7,8]) !== -1) { // values that require 90 degree rotation 8654 // swap dimensions 8655 var tmp = width; 8656 width = height; 8657 height = tmp; 8658 } 8659 8660 img = _getImg(); 8661 8662 // unify dimensions 8663 mathFn = !crop ? Math.min : Math.max; 8664 scale = mathFn(width/img.width, height/img.height); 8665 8666 // we only downsize here 8667 if (scale > 1 && (!crop || preserveHeaders)) { // when cropping one of dimensions may still exceed max, so process it anyway 8668 this.trigger('Resize'); 8669 return; 8670 } 8671 8672 // prepare canvas if necessary 8673 if (!_canvas) { 8674 _canvas = document.createElement("canvas"); 8675 } 8676 8677 // calculate dimensions of proportionally resized image 8678 destWidth = Math.round(img.width * scale); 8679 destHeight = Math.round(img.height * scale); 8680 8681 8682 // scale image and canvas 8683 if (crop) { 8684 _canvas.width = width; 8685 _canvas.height = height; 8686 8687 // if dimensions of the resulting image still larger than canvas, center it 8688 if (destWidth > width) { 8689 x = Math.round((destWidth - width) / 2); 8690 } 8691 8692 if (destHeight > height) { 8693 y = Math.round((destHeight - height) / 2); 8694 } 8695 } else { 8696 _canvas.width = destWidth; 8697 _canvas.height = destHeight; 8698 } 8699 8700 // rotate if required, according to orientation tag 8701 if (!_preserveHeaders) { 8702 _rotateToOrientaion(_canvas.width, _canvas.height, orientation); 8703 } 8704 8705 _drawToCanvas.call(this, img, _canvas, -x, -y, destWidth, destHeight); 8706 8707 this.width = _canvas.width; 8708 this.height = _canvas.height; 8709 8710 _modified = true; 8711 self.trigger('Resize'); 8712 } 8713 8714 8715 function _drawToCanvas(img, canvas, x, y, w, h) { 8716 if (Env.OS === 'iOS') { 8717 // avoid squish bug in iOS6 8718 MegaPixel.renderTo(img, canvas, { width: w, height: h, x: x, y: y }); 8719 } else { 8720 var ctx = canvas.getContext('2d'); 8721 ctx.drawImage(img, x, y, w, h); 8722 } 8723 } 8724 8725 8726 /** 8727 * Transform canvas coordination according to specified frame size and orientation 8728 * Orientation value is from EXIF tag 8729 * @author Shinichi Tomita <shinichi.tomita@gmail.com> 8730 */ 8731 function _rotateToOrientaion(width, height, orientation) { 8732 switch (orientation) { 8733 case 5: 8734 case 6: 8735 case 7: 8736 case 8: 8737 _canvas.width = height; 8738 _canvas.height = width; 8739 break; 8740 default: 8741 _canvas.width = width; 8742 _canvas.height = height; 8743 } 8744 8745 /** 8746 1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side. 8747 2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side. 8748 3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side. 8749 4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side. 8750 5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top. 8751 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top. 8752 7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom. 8753 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom. 8754 */ 8755 8756 var ctx = _canvas.getContext('2d'); 8757 switch (orientation) { 8758 case 2: 8759 // horizontal flip 8760 ctx.translate(width, 0); 8761 ctx.scale(-1, 1); 8762 break; 8763 case 3: 8764 // 180 rotate left 8765 ctx.translate(width, height); 8766 ctx.rotate(Math.PI); 8767 break; 8768 case 4: 8769 // vertical flip 8770 ctx.translate(0, height); 8771 ctx.scale(1, -1); 8772 break; 8773 case 5: 8774 // vertical flip + 90 rotate right 8775 ctx.rotate(0.5 * Math.PI); 8776 ctx.scale(1, -1); 8777 break; 8778 case 6: 8779 // 90 rotate right 8780 ctx.rotate(0.5 * Math.PI); 8781 ctx.translate(0, -height); 8782 break; 8783 case 7: 8784 // horizontal flip + 90 rotate right 8785 ctx.rotate(0.5 * Math.PI); 8786 ctx.translate(width, -height); 8787 ctx.scale(-1, 1); 8788 break; 8789 case 8: 8790 // 90 rotate left 8791 ctx.rotate(-0.5 * Math.PI); 8792 ctx.translate(-width, 0); 8793 break; 8794 } 8795 } 8796 8797 8798 function _purge() { 8799 if (_imgInfo) { 8800 _imgInfo.purge(); 8801 _imgInfo = null; 8802 } 8803 _binStr = _img = _canvas = _blob = null; 8804 _modified = false; 8805 } 8806 } 8807 8808 return (extensions.Image = HTML5Image); 8809 }); 8810 8811 // Included from: src/javascript/runtime/flash/Runtime.js 8812 8813 /** 8814 * Runtime.js 8815 * 8816 * Copyright 2013, Moxiecode Systems AB 8817 * Released under GPL License. 8818 * 8819 * License: http://www.plupload.com/license 8820 * Contributing: http://www.plupload.com/contributing 8821 */ 8822 8823 /*global ActiveXObject:true */ 8824 8825 /** 8826 Defines constructor for Flash runtime. 8827 8828 @class moxie/runtime/flash/Runtime 8829 @private 8830 */ 8831 define("moxie/runtime/flash/Runtime", [ 8832 "moxie/core/utils/Basic", 8833 "moxie/core/utils/Env", 8834 "moxie/core/utils/Dom", 8835 "moxie/core/Exceptions", 8836 "moxie/runtime/Runtime" 8837 ], function(Basic, Env, Dom, x, Runtime) { 8838 8839 var type = 'flash', extensions = {}; 8840 8841 /** 8842 Get the version of the Flash Player 8843 8844 @method getShimVersion 8845 @private 8846 @return {Number} Flash Player version 8847 */ 8848 function getShimVersion() { 8849 var version; 8850 8851 try { 8852 version = navigator.plugins['Shockwave Flash']; 8853 version = version.description; 8854 } catch (e1) { 8855 try { 8856 version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); 8857 } catch (e2) { 8858 version = '0.0'; 8859 } 8860 } 8861 version = version.match(/\d+/g); 8862 return parseFloat(version[0] + '.' + version[1]); 8863 } 8864 8865 /** 8866 Constructor for the Flash Runtime 8867 8868 @class FlashRuntime 8869 @extends Runtime 8870 */ 8871 function FlashRuntime(options) { 8872 var I = this, initTimer; 8873 8874 options = Basic.extend({ swf_url: Env.swf_url }, options); 8875 8876 Runtime.call(this, options, type, { 8877 access_binary: function(value) { 8878 return value && I.mode === 'browser'; 8879 }, 8880 access_image_binary: function(value) { 8881 return value && I.mode === 'browser'; 8882 }, 8883 display_media: Runtime.capTrue, 8884 do_cors: Runtime.capTrue, 8885 drag_and_drop: false, 8886 report_upload_progress: function() { 8887 return I.mode === 'client'; 8888 }, 8889 resize_image: Runtime.capTrue, 8890 return_response_headers: false, 8891 return_response_type: function(responseType) { 8892 if (responseType === 'json' && !!window.JSON) { 8893 return true; 8894 } 8895 return !Basic.arrayDiff(responseType, ['', 'text', 'document']) || I.mode === 'browser'; 8896 }, 8897 return_status_code: function(code) { 8898 return I.mode === 'browser' || !Basic.arrayDiff(code, [200, 404]); 8899 }, 8900 select_file: Runtime.capTrue, 8901 select_multiple: Runtime.capTrue, 8902 send_binary_string: function(value) { 8903 return value && I.mode === 'browser'; 8904 }, 8905 send_browser_cookies: function(value) { 8906 return value && I.mode === 'browser'; 8907 }, 8908 send_custom_headers: function(value) { 8909 return value && I.mode === 'browser'; 8910 }, 8911 send_multipart: Runtime.capTrue, 8912 slice_blob: Runtime.capTrue, 8913 stream_upload: function(value) { 8914 return value && I.mode === 'browser'; 8915 }, 8916 summon_file_dialog: false, 8917 upload_filesize: function(size) { 8918 return Basic.parseSizeStr(size) <= 2097152 || I.mode === 'client'; 8919 }, 8920 use_http_method: function(methods) { 8921 return !Basic.arrayDiff(methods, ['GET', 'POST']); 8922 } 8923 }, { 8924 // capabilities that require specific mode 8925 access_binary: function(value) { 8926 return value ? 'browser' : 'client'; 8927 }, 8928 access_image_binary: function(value) { 8929 return value ? 'browser' : 'client'; 8930 }, 8931 report_upload_progress: function(value) { 8932 return value ? 'browser' : 'client'; 8933 }, 8934 return_response_type: function(responseType) { 8935 return Basic.arrayDiff(responseType, ['', 'text', 'json', 'document']) ? 'browser' : ['client', 'browser']; 8936 }, 8937 return_status_code: function(code) { 8938 return Basic.arrayDiff(code, [200, 404]) ? 'browser' : ['client', 'browser']; 8939 }, 8940 send_binary_string: function(value) { 8941 return value ? 'browser' : 'client'; 8942 }, 8943 send_browser_cookies: function(value) { 8944 return value ? 'browser' : 'client'; 8945 }, 8946 send_custom_headers: function(value) { 8947 return value ? 'browser' : 'client'; 8948 }, 8949 stream_upload: function(value) { 8950 return value ? 'client' : 'browser'; 8951 }, 8952 upload_filesize: function(size) { 8953 return Basic.parseSizeStr(size) >= 2097152 ? 'client' : 'browser'; 8954 } 8955 }, 'client'); 8956 8957 8958 // minimal requirement for Flash Player version 8959 if (getShimVersion() < 10) { 8960 this.mode = false; // with falsy mode, runtime won't operable, no matter what the mode was before 8961 } 8962 8963 8964 Basic.extend(this, { 8965 8966 getShim: function() { 8967 return Dom.get(this.uid); 8968 }, 8969 8970 shimExec: function(component, action) { 8971 var args = [].slice.call(arguments, 2); 8972 return I.getShim().exec(this.uid, component, action, args); 8973 }, 8974 8975 init: function() { 8976 var html, el, container; 8977 8978 container = this.getShimContainer(); 8979 8980 // 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) 8981 Basic.extend(container.style, { 8982 position: 'absolute', 8983 top: '-8px', 8984 left: '-8px', 8985 width: '9px', 8986 height: '9px', 8987 overflow: 'hidden' 8988 }); 8989 8990 // insert flash object 8991 html = '<object id="' + this.uid + '" type="application/x-shockwave-flash" data="' + options.swf_url + '" '; 8992 8993 if (Env.browser === 'IE') { 8994 html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '; 8995 } 8996 8997 html += 'width="100%" height="100%" style="outline:0">' + 8998 '<param name="movie" value="' + options.swf_url + '" />' + 8999 '<param name="flashvars" value="uid=' + escape(this.uid) + '&target=' + Env.global_event_dispatcher + '" />' + 9000 '<param name="wmode" value="transparent" />' + 9001 '<param name="allowscriptaccess" value="always" />' + 9002 '</object>'; 9003 9004 if (Env.browser === 'IE') { 9005 el = document.createElement('div'); 9006 container.appendChild(el); 9007 el.outerHTML = html; 9008 el = container = null; // just in case 9009 } else { 9010 container.innerHTML = html; 9011 } 9012 9013 // Init is dispatched by the shim 9014 initTimer = setTimeout(function() { 9015 if (I && !I.initialized) { // runtime might be already destroyed by this moment 9016 I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR)); 9017 } 9018 }, 5000); 9019 }, 9020 9021 destroy: (function(destroy) { // extend default destroy method 9022 return function() { 9023 destroy.call(I); 9024 clearTimeout(initTimer); // initialization check might be still onwait 9025 options = initTimer = destroy = I = null; 9026 }; 9027 }(this.destroy)) 9028 9029 }, extensions); 9030 } 9031 9032 Runtime.addConstructor(type, FlashRuntime); 9033 9034 return extensions; 9035 }); 9036 9037 // Included from: src/javascript/runtime/flash/file/Blob.js 9038 9039 /** 9040 * Blob.js 9041 * 9042 * Copyright 2013, Moxiecode Systems AB 9043 * Released under GPL License. 9044 * 9045 * License: http://www.plupload.com/license 9046 * Contributing: http://www.plupload.com/contributing 9047 */ 9048 9049 /** 9050 @class moxie/runtime/flash/file/Blob 9051 @private 9052 */ 9053 define("moxie/runtime/flash/file/Blob", [ 9054 "moxie/runtime/flash/Runtime", 9055 "moxie/file/Blob" 9056 ], function(extensions, Blob) { 9057 9058 var FlashBlob = { 9059 slice: function(blob, start, end, type) { 9060 var self = this.getRuntime(); 9061 9062 if (start < 0) { 9063 start = Math.max(blob.size + start, 0); 9064 } else if (start > 0) { 9065 start = Math.min(start, blob.size); 9066 } 9067 9068 if (end < 0) { 9069 end = Math.max(blob.size + end, 0); 9070 } else if (end > 0) { 9071 end = Math.min(end, blob.size); 9072 } 9073 9074 blob = self.shimExec.call(this, 'Blob', 'slice', start, end, type || ''); 9075 9076 if (blob) { 9077 blob = new Blob(self.uid, blob); 9078 } 9079 return blob; 9080 } 9081 }; 9082 9083 return (extensions.Blob = FlashBlob); 9084 }); 9085 9086 // Included from: src/javascript/runtime/flash/file/FileInput.js 9087 9088 /** 9089 * FileInput.js 9090 * 9091 * Copyright 2013, Moxiecode Systems AB 9092 * Released under GPL License. 9093 * 9094 * License: http://www.plupload.com/license 9095 * Contributing: http://www.plupload.com/contributing 9096 */ 9097 9098 /** 9099 @class moxie/runtime/flash/file/FileInput 9100 @private 9101 */ 9102 define("moxie/runtime/flash/file/FileInput", [ 9103 "moxie/runtime/flash/Runtime" 9104 ], function(extensions) { 9105 9106 var FileInput = { 9107 init: function(options) { 9108 this.getRuntime().shimExec.call(this, 'FileInput', 'init', { 9109 name: options.name, 9110 accept: options.accept, 9111 multiple: options.multiple 9112 }); 9113 this.trigger('ready'); 9114 } 9115 }; 9116 9117 return (extensions.FileInput = FileInput); 9118 }); 9119 9120 // Included from: src/javascript/runtime/flash/file/FileReader.js 9121 9122 /** 9123 * FileReader.js 9124 * 9125 * Copyright 2013, Moxiecode Systems AB 9126 * Released under GPL License. 9127 * 9128 * License: http://www.plupload.com/license 9129 * Contributing: http://www.plupload.com/contributing 9130 */ 9131 9132 /** 9133 @class moxie/runtime/flash/file/FileReader 9134 @private 9135 */ 9136 define("moxie/runtime/flash/file/FileReader", [ 9137 "moxie/runtime/flash/Runtime", 9138 "moxie/core/utils/Encode" 9139 ], function(extensions, Encode) { 9140 9141 var _result = ''; 9142 9143 function _formatData(data, op) { 9144 switch (op) { 9145 case 'readAsText': 9146 return Encode.atob(data, 'utf8'); 9147 case 'readAsBinaryString': 9148 return Encode.atob(data); 9149 case 'readAsDataURL': 9150 return data; 9151 } 9152 return null; 9153 } 9154 9155 var FileReader = { 9156 read: function(op, blob) { 9157 var target = this, self = target.getRuntime(); 9158 9159 // special prefix for DataURL read mode 9160 if (op === 'readAsDataURL') { 9161 _result = 'data:' + (blob.type || '') + ';base64,'; 9162 } 9163 9164 target.bind('Progress', function(e, data) { 9165 if (data) { 9166 _result += _formatData(data, op); 9167 } 9168 }); 9169 9170 return self.shimExec.call(this, 'FileReader', 'readAsBase64', blob.uid); 9171 }, 9172 9173 getResult: function() { 9174 return _result; 9175 }, 9176 9177 destroy: function() { 9178 _result = null; 9179 } 9180 }; 9181 9182 return (extensions.FileReader = FileReader); 9183 }); 9184 9185 // Included from: src/javascript/runtime/flash/file/FileReaderSync.js 9186 9187 /** 9188 * FileReaderSync.js 9189 * 9190 * Copyright 2013, Moxiecode Systems AB 9191 * Released under GPL License. 9192 * 9193 * License: http://www.plupload.com/license 9194 * Contributing: http://www.plupload.com/contributing 9195 */ 9196 9197 /** 9198 @class moxie/runtime/flash/file/FileReaderSync 9199 @private 9200 */ 9201 define("moxie/runtime/flash/file/FileReaderSync", [ 9202 "moxie/runtime/flash/Runtime", 9203 "moxie/core/utils/Encode" 9204 ], function(extensions, Encode) { 9205 9206 function _formatData(data, op) { 9207 switch (op) { 9208 case 'readAsText': 9209 return Encode.atob(data, 'utf8'); 9210 case 'readAsBinaryString': 9211 return Encode.atob(data); 9212 case 'readAsDataURL': 9213 return data; 9214 } 9215 return null; 9216 } 9217 9218 var FileReaderSync = { 9219 read: function(op, blob) { 9220 var result, self = this.getRuntime(); 9221 9222 result = self.shimExec.call(this, 'FileReaderSync', 'readAsBase64', blob.uid); 9223 if (!result) { 9224 return null; // or throw ex 9225 } 9226 9227 // special prefix for DataURL read mode 9228 if (op === 'readAsDataURL') { 9229 result = 'data:' + (blob.type || '') + ';base64,' + result; 9230 } 9231 9232 return _formatData(result, op, blob.type); 9233 } 9234 }; 9235 9236 return (extensions.FileReaderSync = FileReaderSync); 9237 }); 9238 9239 // Included from: src/javascript/runtime/flash/xhr/XMLHttpRequest.js 9240 9241 /** 9242 * XMLHttpRequest.js 9243 * 9244 * Copyright 2013, Moxiecode Systems AB 9245 * Released under GPL License. 9246 * 9247 * License: http://www.plupload.com/license 9248 * Contributing: http://www.plupload.com/contributing 9249 */ 9250 9251 /** 9252 @class moxie/runtime/flash/xhr/XMLHttpRequest 9253 @private 9254 */ 9255 define("moxie/runtime/flash/xhr/XMLHttpRequest", [ 9256 "moxie/runtime/flash/Runtime", 9257 "moxie/core/utils/Basic", 9258 "moxie/file/Blob", 9259 "moxie/file/File", 9260 "moxie/file/FileReaderSync", 9261 "moxie/xhr/FormData", 9262 "moxie/runtime/Transporter" 9263 ], function(extensions, Basic, Blob, File, FileReaderSync, FormData, Transporter) { 9264 9265 var XMLHttpRequest = { 9266 9267 send: function(meta, data) { 9268 var target = this, self = target.getRuntime(); 9269 9270 function send() { 9271 meta.transport = self.mode; 9272 self.shimExec.call(target, 'XMLHttpRequest', 'send', meta, data); 9273 } 9274 9275 9276 function appendBlob(name, blob) { 9277 self.shimExec.call(target, 'XMLHttpRequest', 'appendBlob', name, blob.uid); 9278 data = null; 9279 send(); 9280 } 9281 9282 9283 function attachBlob(blob, cb) { 9284 var tr = new Transporter(); 9285 9286 tr.bind("TransportingComplete", function() { 9287 cb(this.result); 9288 }); 9289 9290 tr.transport(blob.getSource(), blob.type, { 9291 ruid: self.uid 9292 }); 9293 } 9294 9295 // copy over the headers if any 9296 if (!Basic.isEmptyObj(meta.headers)) { 9297 Basic.each(meta.headers, function(value, header) { 9298 self.shimExec.call(target, 'XMLHttpRequest', 'setRequestHeader', header, value.toString()); // Silverlight doesn't accept integers into the arguments of type object 9299 }); 9300 } 9301 9302 // transfer over multipart params and blob itself 9303 if (data instanceof FormData) { 9304 var blobField; 9305 data.each(function(value, name) { 9306 if (value instanceof Blob) { 9307 blobField = name; 9308 } else { 9309 self.shimExec.call(target, 'XMLHttpRequest', 'append', name, value); 9310 } 9311 }); 9312 9313 if (!data.hasBlob()) { 9314 data = null; 9315 send(); 9316 } else { 9317 var blob = data.getBlob(); 9318 if (blob.isDetached()) { 9319 attachBlob(blob, function(attachedBlob) { 9320 blob.destroy(); 9321 appendBlob(blobField, attachedBlob); 9322 }); 9323 } else { 9324 appendBlob(blobField, blob); 9325 } 9326 } 9327 } else if (data instanceof Blob) { 9328 if (data.isDetached()) { 9329 attachBlob(data, function(attachedBlob) { 9330 data.destroy(); 9331 data = attachedBlob.uid; 9332 send(); 9333 }); 9334 } else { 9335 data = data.uid; 9336 send(); 9337 } 9338 } else { 9339 send(); 9340 } 9341 }, 9342 9343 getResponse: function(responseType) { 9344 var frs, blob, self = this.getRuntime(); 9345 9346 blob = self.shimExec.call(this, 'XMLHttpRequest', 'getResponseAsBlob'); 9347 9348 if (blob) { 9349 blob = new File(self.uid, blob); 9350 9351 if ('blob' === responseType) { 9352 return blob; 9353 } 9354 9355 try { 9356 frs = new FileReaderSync(); 9357 9358 if (!!~Basic.inArray(responseType, ["", "text"])) { 9359 return frs.readAsText(blob); 9360 } else if ('json' === responseType && !!window.JSON) { 9361 return JSON.parse(frs.readAsText(blob)); 9362 } 9363 } finally { 9364 blob.destroy(); 9365 } 9366 } 9367 return null; 9368 }, 9369 9370 abort: function(upload_complete_flag) { 9371 var self = this.getRuntime(); 9372 9373 self.shimExec.call(this, 'XMLHttpRequest', 'abort'); 9374 9375 this.dispatchEvent('readystatechange'); 9376 // this.dispatchEvent('progress'); 9377 this.dispatchEvent('abort'); 9378 9379 //if (!upload_complete_flag) { 9380 // this.dispatchEvent('uploadprogress'); 9381 //} 9382 } 9383 }; 9384 9385 return (extensions.XMLHttpRequest = XMLHttpRequest); 9386 }); 9387 9388 // Included from: src/javascript/runtime/flash/runtime/Transporter.js 9389 9390 /** 9391 * Transporter.js 9392 * 9393 * Copyright 2013, Moxiecode Systems AB 9394 * Released under GPL License. 9395 * 9396 * License: http://www.plupload.com/license 9397 * Contributing: http://www.plupload.com/contributing 9398 */ 9399 9400 /** 9401 @class moxie/runtime/flash/runtime/Transporter 9402 @private 9403 */ 9404 define("moxie/runtime/flash/runtime/Transporter", [ 9405 "moxie/runtime/flash/Runtime", 9406 "moxie/file/Blob" 9407 ], function(extensions, Blob) { 9408 9409 var Transporter = { 9410 getAsBlob: function(type) { 9411 var self = this.getRuntime() 9412 , blob = self.shimExec.call(this, 'Transporter', 'getAsBlob', type) 9413 ; 9414 if (blob) { 9415 return new Blob(self.uid, blob); 9416 } 9417 return null; 9418 } 9419 }; 9420 9421 return (extensions.Transporter = Transporter); 9422 }); 9423 9424 // Included from: src/javascript/runtime/flash/image/Image.js 9425 9426 /** 9427 * Image.js 9428 * 9429 * Copyright 2013, Moxiecode Systems AB 9430 * Released under GPL License. 9431 * 9432 * License: http://www.plupload.com/license 9433 * Contributing: http://www.plupload.com/contributing 9434 */ 9435 9436 /** 9437 @class moxie/runtime/flash/image/Image 9438 @private 9439 */ 9440 define("moxie/runtime/flash/image/Image", [ 9441 "moxie/runtime/flash/Runtime", 9442 "moxie/core/utils/Basic", 9443 "moxie/runtime/Transporter", 9444 "moxie/file/Blob", 9445 "moxie/file/FileReaderSync" 9446 ], function(extensions, Basic, Transporter, Blob, FileReaderSync) { 9447 9448 var Image = { 9449 loadFromBlob: function(blob) { 9450 var comp = this, self = comp.getRuntime(); 9451 9452 function exec(srcBlob) { 9453 self.shimExec.call(comp, 'Image', 'loadFromBlob', srcBlob.uid); 9454 comp = self = null; 9455 } 9456 9457 if (blob.isDetached()) { // binary string 9458 var tr = new Transporter(); 9459 tr.bind("TransportingComplete", function() { 9460 exec(tr.result.getSource()); 9461 }); 9462 tr.transport(blob.getSource(), blob.type, { ruid: self.uid }); 9463 } else { 9464 exec(blob.getSource()); 9465 } 9466 }, 9467 9468 loadFromImage: function(img) { 9469 var self = this.getRuntime(); 9470 return self.shimExec.call(this, 'Image', 'loadFromImage', img.uid); 9471 }, 9472 9473 getAsBlob: function(type, quality) { 9474 var self = this.getRuntime() 9475 , blob = self.shimExec.call(this, 'Image', 'getAsBlob', type, quality) 9476 ; 9477 if (blob) { 9478 return new Blob(self.uid, blob); 9479 } 9480 return null; 9481 }, 9482 9483 getAsDataURL: function() { 9484 var self = this.getRuntime() 9485 , blob = self.Image.getAsBlob.apply(this, arguments) 9486 , frs 9487 ; 9488 if (!blob) { 9489 return null; 9490 } 9491 frs = new FileReaderSync(); 9492 return frs.readAsDataURL(blob); 9493 } 9494 }; 9495 9496 return (extensions.Image = Image); 9497 }); 9498 9499 // Included from: src/javascript/runtime/silverlight/Runtime.js 9500 9501 /** 9502 * RunTime.js 9503 * 9504 * Copyright 2013, Moxiecode Systems AB 9505 * Released under GPL License. 9506 * 9507 * License: http://www.plupload.com/license 9508 * Contributing: http://www.plupload.com/contributing 9509 */ 9510 9511 /*global ActiveXObject:true */ 9512 9513 /** 9514 Defines constructor for Silverlight runtime. 9515 9516 @class moxie/runtime/silverlight/Runtime 9517 @private 9518 */ 9519 define("moxie/runtime/silverlight/Runtime", [ 9520 "moxie/core/utils/Basic", 9521 "moxie/core/utils/Env", 9522 "moxie/core/utils/Dom", 9523 "moxie/core/Exceptions", 9524 "moxie/runtime/Runtime" 9525 ], function(Basic, Env, Dom, x, Runtime) { 9526 9527 var type = "silverlight", extensions = {}; 9528 9529 function isInstalled(version) { 9530 var isVersionSupported = false, control = null, actualVer, 9531 actualVerArray, reqVerArray, requiredVersionPart, actualVersionPart, index = 0; 9532 9533 try { 9534 try { 9535 control = new ActiveXObject('AgControl.AgControl'); 9536 9537 if (control.IsVersionSupported(version)) { 9538 isVersionSupported = true; 9539 } 9540 9541 control = null; 9542 } catch (e) { 9543 var plugin = navigator.plugins["Silverlight Plug-In"]; 9544 9545 if (plugin) { 9546 actualVer = plugin.description; 9547 9548 if (actualVer === "1.0.30226.2") { 9549 actualVer = "2.0.30226.2"; 9550 } 9551 9552 actualVerArray = actualVer.split("."); 9553 9554 while (actualVerArray.length > 3) { 9555 actualVerArray.pop(); 9556 } 9557 9558 while ( actualVerArray.length < 4) { 9559 actualVerArray.push(0); 9560 } 9561 9562 reqVerArray = version.split("."); 9563 9564 while (reqVerArray.length > 4) { 9565 reqVerArray.pop(); 9566 } 9567 9568 do { 9569 requiredVersionPart = parseInt(reqVerArray[index], 10); 9570 actualVersionPart = parseInt(actualVerArray[index], 10); 9571 index++; 9572 } while (index < reqVerArray.length && requiredVersionPart === actualVersionPart); 9573 9574 if (requiredVersionPart <= actualVersionPart && !isNaN(requiredVersionPart)) { 9575 isVersionSupported = true; 9576 } 9577 } 9578 } 9579 } catch (e2) { 9580 isVersionSupported = false; 9581 } 9582 9583 return isVersionSupported; 9584 } 9585 9586 /** 9587 Constructor for the Silverlight Runtime 9588 9589 @class SilverlightRuntime 9590 @extends Runtime 9591 */ 9592 function SilverlightRuntime(options) { 9593 var I = this, initTimer; 9594 9595 options = Basic.extend({ xap_url: Env.xap_url }, options); 9596 9597 Runtime.call(this, options, type, { 9598 access_binary: Runtime.capTrue, 9599 access_image_binary: Runtime.capTrue, 9600 display_media: Runtime.capTrue, 9601 do_cors: Runtime.capTrue, 9602 drag_and_drop: false, 9603 report_upload_progress: Runtime.capTrue, 9604 resize_image: Runtime.capTrue, 9605 return_response_headers: function(value) { 9606 return value && I.mode === 'client'; 9607 }, 9608 return_response_type: function(responseType) { 9609 if (responseType !== 'json') { 9610 return true; 9611 } else { 9612 return !!window.JSON; 9613 } 9614 }, 9615 return_status_code: function(code) { 9616 return I.mode === 'client' || !Basic.arrayDiff(code, [200, 404]); 9617 }, 9618 select_file: Runtime.capTrue, 9619 select_multiple: Runtime.capTrue, 9620 send_binary_string: Runtime.capTrue, 9621 send_browser_cookies: function(value) { 9622 return value && I.mode === 'browser'; 9623 }, 9624 send_custom_headers: function(value) { 9625 return value && I.mode === 'client'; 9626 }, 9627 send_multipart: Runtime.capTrue, 9628 slice_blob: Runtime.capTrue, 9629 stream_upload: true, 9630 summon_file_dialog: false, 9631 upload_filesize: Runtime.capTrue, 9632 use_http_method: function(methods) { 9633 return I.mode === 'client' || !Basic.arrayDiff(methods, ['GET', 'POST']); 9634 } 9635 }, { 9636 // capabilities that require specific mode 9637 return_response_headers: function(value) { 9638 return value ? 'client' : 'browser'; 9639 }, 9640 return_status_code: function(code) { 9641 return Basic.arrayDiff(code, [200, 404]) ? 'client' : ['client', 'browser']; 9642 }, 9643 send_browser_cookies: function(value) { 9644 return value ? 'browser' : 'client'; 9645 }, 9646 send_custom_headers: function(value) { 9647 return value ? 'client' : 'browser'; 9648 }, 9649 use_http_method: function(methods) { 9650 return Basic.arrayDiff(methods, ['GET', 'POST']) ? 'client' : ['client', 'browser']; 9651 } 9652 }); 9653 9654 9655 // minimal requirement 9656 if (!isInstalled('2.0.31005.0') || Env.browser === 'Opera') { 9657 this.mode = false; 9658 } 9659 9660 9661 Basic.extend(this, { 9662 getShim: function() { 9663 return Dom.get(this.uid).content.Moxie; 9664 }, 9665 9666 shimExec: function(component, action) { 9667 var args = [].slice.call(arguments, 2); 9668 return I.getShim().exec(this.uid, component, action, args); 9669 }, 9670 9671 init : function() { 9672 var container; 9673 9674 container = this.getShimContainer(); 9675 9676 container.innerHTML = '<object id="' + this.uid + '" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">' + 9677 '<param name="source" value="' + options.xap_url + '"/>' + 9678 '<param name="background" value="Transparent"/>' + 9679 '<param name="windowless" value="true"/>' + 9680 '<param name="enablehtmlaccess" value="true"/>' + 9681 '<param name="initParams" value="uid=' + this.uid + ',target=' + Env.global_event_dispatcher + '"/>' + 9682 '</object>'; 9683 9684 // Init is dispatched by the shim 9685 initTimer = setTimeout(function() { 9686 if (I && !I.initialized) { // runtime might be already destroyed by this moment 9687 I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR)); 9688 } 9689 }, Env.OS !== 'Windows'? 10000 : 5000); // give it more time to initialize in non Windows OS (like Mac) 9690 }, 9691 9692 destroy: (function(destroy) { // extend default destroy method 9693 return function() { 9694 destroy.call(I); 9695 clearTimeout(initTimer); // initialization check might be still onwait 9696 options = initTimer = destroy = I = null; 9697 }; 9698 }(this.destroy)) 9699 9700 }, extensions); 9701 } 9702 9703 Runtime.addConstructor(type, SilverlightRuntime); 9704 9705 return extensions; 9706 }); 9707 9708 // Included from: src/javascript/runtime/silverlight/file/Blob.js 9709 9710 /** 9711 * Blob.js 9712 * 9713 * Copyright 2013, Moxiecode Systems AB 9714 * Released under GPL License. 9715 * 9716 * License: http://www.plupload.com/license 9717 * Contributing: http://www.plupload.com/contributing 9718 */ 9719 9720 /** 9721 @class moxie/runtime/silverlight/file/Blob 9722 @private 9723 */ 9724 define("moxie/runtime/silverlight/file/Blob", [ 9725 "moxie/runtime/silverlight/Runtime", 9726 "moxie/core/utils/Basic", 9727 "moxie/runtime/flash/file/Blob" 9728 ], function(extensions, Basic, Blob) { 9729 return (extensions.Blob = Basic.extend({}, Blob)); 9730 }); 9731 9732 // Included from: src/javascript/runtime/silverlight/file/FileInput.js 9733 9734 /** 9735 * FileInput.js 9736 * 9737 * Copyright 2013, Moxiecode Systems AB 9738 * Released under GPL License. 9739 * 9740 * License: http://www.plupload.com/license 9741 * Contributing: http://www.plupload.com/contributing 9742 */ 9743 9744 /** 9745 @class moxie/runtime/silverlight/file/FileInput 9746 @private 9747 */ 9748 define("moxie/runtime/silverlight/file/FileInput", [ 9749 "moxie/runtime/silverlight/Runtime" 9750 ], function(extensions) { 9751 9752 var FileInput = { 9753 init: function(options) { 9754 9755 function toFilters(accept) { 9756 var filter = ''; 9757 for (var i = 0; i < accept.length; i++) { 9758 filter += (filter !== '' ? '|' : '') + accept[i].title + " | *." + accept[i].extensions.replace(/,/g, ';*.'); 9759 } 9760 return filter; 9761 } 9762 9763 this.getRuntime().shimExec.call(this, 'FileInput', 'init', toFilters(options.accept), options.name, options.multiple); 9764 this.trigger('ready'); 9765 } 9766 }; 9767 9768 return (extensions.FileInput = FileInput); 9769 }); 9770 9771 // Included from: src/javascript/runtime/silverlight/file/FileDrop.js 9772 9773 /** 9774 * FileDrop.js 9775 * 9776 * Copyright 2013, Moxiecode Systems AB 9777 * Released under GPL License. 9778 * 9779 * License: http://www.plupload.com/license 9780 * Contributing: http://www.plupload.com/contributing 9781 */ 9782 9783 /** 9784 @class moxie/runtime/silverlight/file/FileDrop 9785 @private 9786 */ 9787 define("moxie/runtime/silverlight/file/FileDrop", [ 9788 "moxie/runtime/silverlight/Runtime", 9789 "moxie/core/utils/Dom", 9790 "moxie/core/utils/Events" 9791 ], function(extensions, Dom, Events) { 9792 9793 // not exactly useful, since works only in safari (...crickets...) 9794 var FileDrop = { 9795 init: function() { 9796 var comp = this, self = comp.getRuntime(), dropZone; 9797 9798 dropZone = self.getShimContainer(); 9799 9800 Events.addEvent(dropZone, 'dragover', function(e) { 9801 e.preventDefault(); 9802 e.stopPropagation(); 9803 e.dataTransfer.dropEffect = 'copy'; 9804 }, comp.uid); 9805 9806 Events.addEvent(dropZone, 'dragenter', function(e) { 9807 e.preventDefault(); 9808 var flag = Dom.get(self.uid).dragEnter(e); 9809 // If handled, then stop propagation of event in DOM 9810 if (flag) { 9811 e.stopPropagation(); 9812 } 9813 }, comp.uid); 9814 9815 Events.addEvent(dropZone, 'drop', function(e) { 9816 e.preventDefault(); 9817 var flag = Dom.get(self.uid).dragDrop(e); 9818 // If handled, then stop propagation of event in DOM 9819 if (flag) { 9820 e.stopPropagation(); 9821 } 9822 }, comp.uid); 9823 9824 return self.shimExec.call(this, 'FileDrop', 'init'); 9825 } 9826 }; 9827 9828 return (extensions.FileDrop = FileDrop); 9829 }); 9830 9831 // Included from: src/javascript/runtime/silverlight/file/FileReader.js 9832 9833 /** 9834 * FileReader.js 9835 * 9836 * Copyright 2013, Moxiecode Systems AB 9837 * Released under GPL License. 9838 * 9839 * License: http://www.plupload.com/license 9840 * Contributing: http://www.plupload.com/contributing 9841 */ 9842 9843 /** 9844 @class moxie/runtime/silverlight/file/FileReader 9845 @private 9846 */ 9847 define("moxie/runtime/silverlight/file/FileReader", [ 9848 "moxie/runtime/silverlight/Runtime", 9849 "moxie/core/utils/Basic", 9850 "moxie/runtime/flash/file/FileReader" 9851 ], function(extensions, Basic, FileReader) { 9852 return (extensions.FileReader = Basic.extend({}, FileReader)); 9853 }); 9854 9855 // Included from: src/javascript/runtime/silverlight/file/FileReaderSync.js 9856 9857 /** 9858 * FileReaderSync.js 9859 * 9860 * Copyright 2013, Moxiecode Systems AB 9861 * Released under GPL License. 9862 * 9863 * License: http://www.plupload.com/license 9864 * Contributing: http://www.plupload.com/contributing 9865 */ 9866 9867 /** 9868 @class moxie/runtime/silverlight/file/FileReaderSync 9869 @private 9870 */ 9871 define("moxie/runtime/silverlight/file/FileReaderSync", [ 9872 "moxie/runtime/silverlight/Runtime", 9873 "moxie/core/utils/Basic", 9874 "moxie/runtime/flash/file/FileReaderSync" 9875 ], function(extensions, Basic, FileReaderSync) { 9876 return (extensions.FileReaderSync = Basic.extend({}, FileReaderSync)); 9877 }); 9878 9879 // Included from: src/javascript/runtime/silverlight/xhr/XMLHttpRequest.js 9880 9881 /** 9882 * XMLHttpRequest.js 9883 * 9884 * Copyright 2013, Moxiecode Systems AB 9885 * Released under GPL License. 9886 * 9887 * License: http://www.plupload.com/license 9888 * Contributing: http://www.plupload.com/contributing 9889 */ 9890 9891 /** 9892 @class moxie/runtime/silverlight/xhr/XMLHttpRequest 9893 @private 9894 */ 9895 define("moxie/runtime/silverlight/xhr/XMLHttpRequest", [ 9896 "moxie/runtime/silverlight/Runtime", 9897 "moxie/core/utils/Basic", 9898 "moxie/runtime/flash/xhr/XMLHttpRequest" 9899 ], function(extensions, Basic, XMLHttpRequest) { 9900 return (extensions.XMLHttpRequest = Basic.extend({}, XMLHttpRequest)); 9901 }); 9902 9903 // Included from: src/javascript/runtime/silverlight/runtime/Transporter.js 9904 9905 /** 9906 * Transporter.js 9907 * 9908 * Copyright 2013, Moxiecode Systems AB 9909 * Released under GPL License. 9910 * 9911 * License: http://www.plupload.com/license 9912 * Contributing: http://www.plupload.com/contributing 9913 */ 9914 9915 /** 9916 @class moxie/runtime/silverlight/runtime/Transporter 9917 @private 9918 */ 9919 define("moxie/runtime/silverlight/runtime/Transporter", [ 9920 "moxie/runtime/silverlight/Runtime", 9921 "moxie/core/utils/Basic", 9922 "moxie/runtime/flash/runtime/Transporter" 9923 ], function(extensions, Basic, Transporter) { 9924 return (extensions.Transporter = Basic.extend({}, Transporter)); 9925 }); 9926 9927 // Included from: src/javascript/runtime/silverlight/image/Image.js 9928 9929 /** 9930 * Image.js 9931 * 9932 * Copyright 2013, Moxiecode Systems AB 9933 * Released under GPL License. 9934 * 9935 * License: http://www.plupload.com/license 9936 * Contributing: http://www.plupload.com/contributing 9937 */ 9938 9939 /** 9940 @class moxie/runtime/silverlight/image/Image 9941 @private 9942 */ 9943 define("moxie/runtime/silverlight/image/Image", [ 9944 "moxie/runtime/silverlight/Runtime", 9945 "moxie/core/utils/Basic", 9946 "moxie/runtime/flash/image/Image" 9947 ], function(extensions, Basic, Image) { 9948 return (extensions.Image = Basic.extend({}, Image, { 9949 9950 getInfo: function() { 9951 var self = this.getRuntime() 9952 , grps = ['tiff', 'exif', 'gps'] 9953 , info = { meta: {} } 9954 , rawInfo = self.shimExec.call(this, 'Image', 'getInfo') 9955 ; 9956 9957 if (rawInfo.meta) { 9958 Basic.each(grps, function(grp) { 9959 var meta = rawInfo.meta[grp] 9960 , tag 9961 , i 9962 , length 9963 , value 9964 ; 9965 if (meta && meta.keys) { 9966 info.meta[grp] = {}; 9967 for (i = 0, length = meta.keys.length; i < length; i++) { 9968 tag = meta.keys[i]; 9969 value = meta[tag]; 9970 if (value) { 9971 // convert numbers 9972 if (/^(\d|[1-9]\d+)$/.test(value)) { // integer (make sure doesn't start with zero) 9973 value = parseInt(value, 10); 9974 } else if (/^\d*\.\d+$/.test(value)) { // double 9975 value = parseFloat(value); 9976 } 9977 info.meta[grp][tag] = value; 9978 } 9979 } 9980 } 9981 }); 9982 } 9983 9984 info.width = parseInt(rawInfo.width, 10); 9985 info.height = parseInt(rawInfo.height, 10); 9986 info.size = parseInt(rawInfo.size, 10); 9987 info.type = rawInfo.type; 9988 info.name = rawInfo.name; 9989 9990 return info; 9991 } 9992 })); 9993 }); 9994 9995 // Included from: src/javascript/runtime/html4/Runtime.js 9996 9997 /** 9998 * Runtime.js 9999 * 10000 * Copyright 2013, Moxiecode Systems AB 10001 * Released under GPL License. 10002 * 10003 * License: http://www.plupload.com/license 10004 * Contributing: http://www.plupload.com/contributing 10005 */ 10006 10007 /*global File:true */ 10008 10009 /** 10010 Defines constructor for HTML4 runtime. 10011 10012 @class moxie/runtime/html4/Runtime 10013 @private 10014 */ 10015 define("moxie/runtime/html4/Runtime", [ 10016 "moxie/core/utils/Basic", 10017 "moxie/core/Exceptions", 10018 "moxie/runtime/Runtime", 10019 "moxie/core/utils/Env" 10020 ], function(Basic, x, Runtime, Env) { 10021 10022 var type = 'html4', extensions = {}; 10023 10024 function Html4Runtime(options) { 10025 var I = this 10026 , Test = Runtime.capTest 10027 , True = Runtime.capTrue 10028 ; 10029 10030 Runtime.call(this, options, type, { 10031 access_binary: Test(window.FileReader || window.File && File.getAsDataURL), 10032 access_image_binary: false, 10033 display_media: Test(extensions.Image && (Env.can('create_canvas') || Env.can('use_data_uri_over32kb'))), 10034 do_cors: false, 10035 drag_and_drop: false, 10036 filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest 10037 return (Env.browser === 'Chrome' && Env.version >= 28) || (Env.browser === 'IE' && Env.version >= 10); 10038 }()), 10039 resize_image: function() { 10040 return extensions.Image && I.can('access_binary') && Env.can('create_canvas'); 10041 }, 10042 report_upload_progress: false, 10043 return_response_headers: false, 10044 return_response_type: function(responseType) { 10045 if (responseType === 'json' && !!window.JSON) { 10046 return true; 10047 } 10048 return !!~Basic.inArray(responseType, ['text', 'document', '']); 10049 }, 10050 return_status_code: function(code) { 10051 return !Basic.arrayDiff(code, [200, 404]); 10052 }, 10053 select_file: function() { 10054 return Env.can('use_fileinput'); 10055 }, 10056 select_multiple: false, 10057 send_binary_string: false, 10058 send_custom_headers: false, 10059 send_multipart: true, 10060 slice_blob: false, 10061 stream_upload: function() { 10062 return I.can('select_file'); 10063 }, 10064 summon_file_dialog: Test(function() { // yeah... some dirty sniffing here... 10065 return (Env.browser === 'Firefox' && Env.version >= 4) || 10066 (Env.browser === 'Opera' && Env.version >= 12) || 10067 !!~Basic.inArray(Env.browser, ['Chrome', 'Safari']); 10068 }()), 10069 upload_filesize: True, 10070 use_http_method: function(methods) { 10071 return !Basic.arrayDiff(methods, ['GET', 'POST']); 10072 } 10073 }); 10074 10075 10076 Basic.extend(this, { 10077 init : function() { 10078 this.trigger("Init"); 10079 }, 10080 10081 destroy: (function(destroy) { // extend default destroy method 10082 return function() { 10083 destroy.call(I); 10084 destroy = I = null; 10085 }; 10086 }(this.destroy)) 10087 }); 10088 10089 Basic.extend(this.getShim(), extensions); 10090 } 10091 10092 Runtime.addConstructor(type, Html4Runtime); 10093 10094 return extensions; 10095 }); 10096 10097 // Included from: src/javascript/runtime/html4/file/FileInput.js 10098 10099 /** 10100 * FileInput.js 10101 * 10102 * Copyright 2013, Moxiecode Systems AB 10103 * Released under GPL License. 10104 * 10105 * License: http://www.plupload.com/license 10106 * Contributing: http://www.plupload.com/contributing 10107 */ 10108 10109 /** 10110 @class moxie/runtime/html4/file/FileInput 10111 @private 10112 */ 10113 define("moxie/runtime/html4/file/FileInput", [ 10114 "moxie/runtime/html4/Runtime", 10115 "moxie/core/utils/Basic", 10116 "moxie/core/utils/Dom", 10117 "moxie/core/utils/Events", 10118 "moxie/core/utils/Mime", 10119 "moxie/core/utils/Env" 10120 ], function(extensions, Basic, Dom, Events, Mime, Env) { 10121 10122 function FileInput() { 10123 var _uid, _files = [], _mimes = [], _options; 10124 10125 function addInput() { 10126 var comp = this, I = comp.getRuntime(), shimContainer, browseButton, currForm, form, input, uid; 10127 10128 uid = Basic.guid('uid_'); 10129 10130 shimContainer = I.getShimContainer(); // we get new ref everytime to avoid memory leaks in IE 10131 10132 if (_uid) { // move previous form out of the view 10133 currForm = Dom.get(_uid + '_form'); 10134 if (currForm) { 10135 Basic.extend(currForm.style, { top: '100%' }); 10136 } 10137 } 10138 10139 // build form in DOM, since innerHTML version not able to submit file for some reason 10140 form = document.createElement('form'); 10141 form.setAttribute('id', uid + '_form'); 10142 form.setAttribute('method', 'post'); 10143 form.setAttribute('enctype', 'multipart/form-data'); 10144 form.setAttribute('encoding', 'multipart/form-data'); 10145 10146 Basic.extend(form.style, { 10147 overflow: 'hidden', 10148 position: 'absolute', 10149 top: 0, 10150 left: 0, 10151 width: '100%', 10152 height: '100%' 10153 }); 10154 10155 input = document.createElement('input'); 10156 input.setAttribute('id', uid); 10157 input.setAttribute('type', 'file'); 10158 input.setAttribute('name', _options.name || 'Filedata'); 10159 input.setAttribute('accept', _mimes.join(',')); 10160 10161 Basic.extend(input.style, { 10162 fontSize: '999px', 10163 opacity: 0 10164 }); 10165 10166 form.appendChild(input); 10167 shimContainer.appendChild(form); 10168 10169 // prepare file input to be placed underneath the browse_button element 10170 Basic.extend(input.style, { 10171 position: 'absolute', 10172 top: 0, 10173 left: 0, 10174 width: '100%', 10175 height: '100%' 10176 }); 10177 10178 if (Env.browser === 'IE' && Env.version < 10) { 10179 Basic.extend(input.style, { 10180 filter : "progid:DXImageTransform.Microsoft.Alpha(opacity=0)" 10181 }); 10182 } 10183 10184 input.onchange = function() { // there should be only one handler for this 10185 var file; 10186 10187 if (!this.value) { 10188 return; 10189 } 10190 10191 if (this.files) { 10192 file = this.files[0]; 10193 } else { 10194 file = { 10195 name: this.value 10196 }; 10197 } 10198 10199 _files = [file]; 10200 10201 this.onchange = function() {}; // clear event handler 10202 addInput.call(comp); 10203 10204 // after file is initialized as o.File, we need to update form and input ids 10205 comp.bind('change', function onChange() { 10206 var input = Dom.get(uid), form = Dom.get(uid + '_form'), file; 10207 10208 comp.unbind('change', onChange); 10209 10210 if (comp.files.length && input && form) { 10211 file = comp.files[0]; 10212 10213 input.setAttribute('id', file.uid); 10214 form.setAttribute('id', file.uid + '_form'); 10215 10216 // set upload target 10217 form.setAttribute('target', file.uid + '_iframe'); 10218 } 10219 input = form = null; 10220 }, 998); 10221 10222 input = form = null; 10223 comp.trigger('change'); 10224 }; 10225 10226 10227 // route click event to the input 10228 if (I.can('summon_file_dialog')) { 10229 browseButton = Dom.get(_options.browse_button); 10230 Events.removeEvent(browseButton, 'click', comp.uid); 10231 Events.addEvent(browseButton, 'click', function(e) { 10232 if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file] 10233 input.click(); 10234 } 10235 e.preventDefault(); 10236 }, comp.uid); 10237 } 10238 10239 _uid = uid; 10240 10241 shimContainer = currForm = browseButton = null; 10242 } 10243 10244 Basic.extend(this, { 10245 init: function(options) { 10246 var comp = this, I = comp.getRuntime(), shimContainer; 10247 10248 // figure out accept string 10249 _options = options; 10250 _mimes = options.accept.mimes || Mime.extList2mimes(options.accept, I.can('filter_by_extension')); 10251 10252 shimContainer = I.getShimContainer(); 10253 10254 (function() { 10255 var browseButton, zIndex, top; 10256 10257 browseButton = Dom.get(options.browse_button); 10258 10259 // Route click event to the input[type=file] element for browsers that support such behavior 10260 if (I.can('summon_file_dialog')) { 10261 if (Dom.getStyle(browseButton, 'position') === 'static') { 10262 browseButton.style.position = 'relative'; 10263 } 10264 10265 zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1; 10266 10267 browseButton.style.zIndex = zIndex; 10268 shimContainer.style.zIndex = zIndex - 1; 10269 } 10270 10271 /* Since we have to place input[type=file] on top of the browse_button for some browsers, 10272 browse_button loses interactivity, so we restore it here */ 10273 top = I.can('summon_file_dialog') ? browseButton : shimContainer; 10274 10275 Events.addEvent(top, 'mouseover', function() { 10276 comp.trigger('mouseenter'); 10277 }, comp.uid); 10278 10279 Events.addEvent(top, 'mouseout', function() { 10280 comp.trigger('mouseleave'); 10281 }, comp.uid); 10282 10283 Events.addEvent(top, 'mousedown', function() { 10284 comp.trigger('mousedown'); 10285 }, comp.uid); 10286 10287 Events.addEvent(Dom.get(options.container), 'mouseup', function() { 10288 comp.trigger('mouseup'); 10289 }, comp.uid); 10290 10291 browseButton = null; 10292 }()); 10293 10294 addInput.call(this); 10295 10296 shimContainer = null; 10297 10298 // trigger ready event asynchronously 10299 comp.trigger({ 10300 type: 'ready', 10301 async: true 10302 }); 10303 }, 10304 10305 getFiles: function() { 10306 return _files; 10307 }, 10308 10309 disable: function(state) { 10310 var input; 10311 10312 if ((input = Dom.get(_uid))) { 10313 input.disabled = !!state; 10314 } 10315 }, 10316 10317 destroy: function() { 10318 var I = this.getRuntime() 10319 , shim = I.getShim() 10320 , shimContainer = I.getShimContainer() 10321 ; 10322 10323 Events.removeAllEvents(shimContainer, this.uid); 10324 Events.removeAllEvents(_options && Dom.get(_options.container), this.uid); 10325 Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid); 10326 10327 if (shimContainer) { 10328 shimContainer.innerHTML = ''; 10329 } 10330 10331 shim.removeInstance(this.uid); 10332 10333 _uid = _files = _mimes = _options = shimContainer = shim = null; 10334 } 10335 }); 10336 } 10337 10338 return (extensions.FileInput = FileInput); 10339 }); 10340 10341 // Included from: src/javascript/runtime/html4/file/FileReader.js 10342 10343 /** 10344 * FileReader.js 10345 * 10346 * Copyright 2013, Moxiecode Systems AB 10347 * Released under GPL License. 10348 * 10349 * License: http://www.plupload.com/license 10350 * Contributing: http://www.plupload.com/contributing 10351 */ 10352 10353 /** 10354 @class moxie/runtime/html4/file/FileReader 10355 @private 10356 */ 10357 define("moxie/runtime/html4/file/FileReader", [ 10358 "moxie/runtime/html4/Runtime", 10359 "moxie/runtime/html5/file/FileReader" 10360 ], function(extensions, FileReader) { 10361 return (extensions.FileReader = FileReader); 10362 }); 10363 10364 // Included from: src/javascript/runtime/html4/xhr/XMLHttpRequest.js 10365 10366 /** 10367 * XMLHttpRequest.js 10368 * 10369 * Copyright 2013, Moxiecode Systems AB 10370 * Released under GPL License. 10371 * 10372 * License: http://www.plupload.com/license 10373 * Contributing: http://www.plupload.com/contributing 10374 */ 10375 10376 /** 10377 @class moxie/runtime/html4/xhr/XMLHttpRequest 10378 @private 10379 */ 10380 define("moxie/runtime/html4/xhr/XMLHttpRequest", [ 10381 "moxie/runtime/html4/Runtime", 10382 "moxie/core/utils/Basic", 10383 "moxie/core/utils/Dom", 10384 "moxie/core/utils/Url", 10385 "moxie/core/Exceptions", 10386 "moxie/core/utils/Events", 10387 "moxie/file/Blob", 10388 "moxie/xhr/FormData" 10389 ], function(extensions, Basic, Dom, Url, x, Events, Blob, FormData) { 10390 10391 function XMLHttpRequest() { 10392 var _status, _response, _iframe; 10393 10394 function cleanup(cb) { 10395 var target = this, uid, form, inputs, i, hasFile = false; 10396 10397 if (!_iframe) { 10398 return; 10399 } 10400 10401 uid = _iframe.id.replace(/_iframe$/, ''); 10402 10403 form = Dom.get(uid + '_form'); 10404 if (form) { 10405 inputs = form.getElementsByTagName('input'); 10406 i = inputs.length; 10407 10408 while (i--) { 10409 switch (inputs[i].getAttribute('type')) { 10410 case 'hidden': 10411 inputs[i].parentNode.removeChild(inputs[i]); 10412 break; 10413 case 'file': 10414 hasFile = true; // flag the case for later 10415 break; 10416 } 10417 } 10418 inputs = []; 10419 10420 if (!hasFile) { // we need to keep the form for sake of possible retries 10421 form.parentNode.removeChild(form); 10422 } 10423 form = null; 10424 } 10425 10426 // without timeout, request is marked as canceled (in console) 10427 setTimeout(function() { 10428 Events.removeEvent(_iframe, 'load', target.uid); 10429 if (_iframe.parentNode) { // #382 10430 _iframe.parentNode.removeChild(_iframe); 10431 } 10432 10433 // check if shim container has any other children, if - not, remove it as well 10434 var shimContainer = target.getRuntime().getShimContainer(); 10435 if (!shimContainer.children.length) { 10436 shimContainer.parentNode.removeChild(shimContainer); 10437 } 10438 10439 shimContainer = _iframe = null; 10440 cb(); 10441 }, 1); 10442 } 10443 10444 Basic.extend(this, { 10445 send: function(meta, data) { 10446 var target = this, I = target.getRuntime(), uid, form, input, blob; 10447 10448 _status = _response = null; 10449 10450 function createIframe() { 10451 var container = I.getShimContainer() || document.body 10452 , temp = document.createElement('div') 10453 ; 10454 10455 // IE 6 won't be able to set the name using setAttribute or iframe.name 10456 temp.innerHTML = '<iframe id="' + uid + '_iframe" name="' + uid + '_iframe" src="javascript:""" style="display:none"></iframe>'; 10457 _iframe = temp.firstChild; 10458 container.appendChild(_iframe); 10459 10460 /* _iframe.onreadystatechange = function() { 10461 console.info(_iframe.readyState); 10462 };*/ 10463 10464 Events.addEvent(_iframe, 'load', function() { // _iframe.onload doesn't work in IE lte 8 10465 var el; 10466 10467 try { 10468 el = _iframe.contentWindow.document || _iframe.contentDocument || window.frames[_iframe.id].document; 10469 10470 // try to detect some standard error pages 10471 if (/^4(0[0-9]|1[0-7]|2[2346])\s/.test(el.title)) { // test if title starts with 4xx HTTP error 10472 _status = el.title.replace(/^(\d+).*$/, '$1'); 10473 } else { 10474 _status = 200; 10475 // get result 10476 _response = Basic.trim(el.body.innerHTML); 10477 10478 // we need to fire these at least once 10479 target.trigger({ 10480 type: 'progress', 10481 loaded: _response.length, 10482 total: _response.length 10483 }); 10484 10485 if (blob) { // if we were uploading a file 10486 target.trigger({ 10487 type: 'uploadprogress', 10488 loaded: blob.size || 1025, 10489 total: blob.size || 1025 10490 }); 10491 } 10492 } 10493 } catch (ex) { 10494 if (Url.hasSameOrigin(meta.url)) { 10495 // if response is sent with error code, iframe in IE gets redirected to res://ieframe.dll/http_x.htm 10496 // which obviously results to cross domain error (wtf?) 10497 _status = 404; 10498 } else { 10499 cleanup.call(target, function() { 10500 target.trigger('error'); 10501 }); 10502 return; 10503 } 10504 } 10505 10506 cleanup.call(target, function() { 10507 target.trigger('load'); 10508 }); 10509 }, target.uid); 10510 } // end createIframe 10511 10512 // prepare data to be sent and convert if required 10513 if (data instanceof FormData && data.hasBlob()) { 10514 blob = data.getBlob(); 10515 uid = blob.uid; 10516 input = Dom.get(uid); 10517 form = Dom.get(uid + '_form'); 10518 if (!form) { 10519 throw new x.DOMException(x.DOMException.NOT_FOUND_ERR); 10520 } 10521 } else { 10522 uid = Basic.guid('uid_'); 10523 10524 form = document.createElement('form'); 10525 form.setAttribute('id', uid + '_form'); 10526 form.setAttribute('method', meta.method); 10527 form.setAttribute('enctype', 'multipart/form-data'); 10528 form.setAttribute('encoding', 'multipart/form-data'); 10529 form.setAttribute('target', uid + '_iframe'); 10530 10531 I.getShimContainer().appendChild(form); 10532 } 10533 10534 if (data instanceof FormData) { 10535 data.each(function(value, name) { 10536 if (value instanceof Blob) { 10537 if (input) { 10538 input.setAttribute('name', name); 10539 } 10540 } else { 10541 var hidden = document.createElement('input'); 10542 10543 Basic.extend(hidden, { 10544 type : 'hidden', 10545 name : name, 10546 value : value 10547 }); 10548 10549 // make sure that input[type="file"], if it's there, comes last 10550 if (input) { 10551 form.insertBefore(hidden, input); 10552 } else { 10553 form.appendChild(hidden); 10554 } 10555 } 10556 }); 10557 } 10558 10559 // set destination url 10560 form.setAttribute("action", meta.url); 10561 10562 createIframe(); 10563 form.submit(); 10564 target.trigger('loadstart'); 10565 }, 10566 10567 getStatus: function() { 10568 return _status; 10569 }, 10570 10571 getResponse: function(responseType) { 10572 if ('json' === responseType) { 10573 // strip off <pre>..</pre> tags that might be enclosing the response 10574 if (Basic.typeOf(_response) === 'string' && !!window.JSON) { 10575 try { 10576 return JSON.parse(_response.replace(/^\s*<pre[^>]*>/, '').replace(/<\/pre>\s*$/, '')); 10577 } catch (ex) { 10578 return null; 10579 } 10580 } 10581 } else if ('document' === responseType) { 10582 10583 } 10584 return _response; 10585 }, 10586 10587 abort: function() { 10588 var target = this; 10589 10590 if (_iframe && _iframe.contentWindow) { 10591 if (_iframe.contentWindow.stop) { // FireFox/Safari/Chrome 10592 _iframe.contentWindow.stop(); 10593 } else if (_iframe.contentWindow.document.execCommand) { // IE 10594 _iframe.contentWindow.document.execCommand('Stop'); 10595 } else { 10596 _iframe.src = "about:blank"; 10597 } 10598 } 10599 10600 cleanup.call(this, function() { 10601 // target.dispatchEvent('readystatechange'); 10602 target.dispatchEvent('abort'); 10603 }); 10604 } 10605 }); 10606 } 10607 10608 return (extensions.XMLHttpRequest = XMLHttpRequest); 10609 }); 10610 10611 // Included from: src/javascript/runtime/html4/image/Image.js 10612 10613 /** 10614 * Image.js 10615 * 10616 * Copyright 2013, Moxiecode Systems AB 10617 * Released under GPL License. 10618 * 10619 * License: http://www.plupload.com/license 10620 * Contributing: http://www.plupload.com/contributing 10621 */ 10622 10623 /** 10624 @class moxie/runtime/html4/image/Image 10625 @private 10626 */ 10627 define("moxie/runtime/html4/image/Image", [ 10628 "moxie/runtime/html4/Runtime", 10629 "moxie/runtime/html5/image/Image" 10630 ], function(extensions, Image) { 10631 return (extensions.Image = Image); 10632 }); 10633 10634 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"]); 10635 })(this);/** 10636 * o.js 10637 * 10638 * Copyright 2013, Moxiecode Systems AB 10639 * Released under GPL License. 10640 * 10641 * License: http://www.plupload.com/license 10642 * Contributing: http://www.plupload.com/contributing 10643 */ 10644 10645 /*global moxie:true */ 10646 10647 /** 10648 Globally exposed namespace with the most frequently used public classes and handy methods. 10649 10650 @class o 10651 @static 10652 @private 10653 */ 10654 (function() { 10655 "use strict"; 10656 10657 var o = {}, inArray = moxie.core.utils.Basic.inArray; 10658 10659 // directly add some public classes 10660 // (we do it dynamically here, since for custom builds we cannot know beforehand what modules were included) 10661 (function addAlias(ns) { 10662 var name, itemType; 10663 for (name in ns) { 10664 itemType = typeof(ns[name]); 10665 if (itemType === 'object' && !~inArray(name, ['Exceptions', 'Env', 'Mime'])) { 10666 addAlias(ns[name]); 10667 } else if (itemType === 'function') { 10668 o[name] = ns[name]; 10669 } 10670 } 10671 })(window.moxie); 10672 10673 // add some manually 10674 o.Env = window.moxie.core.utils.Env; 10675 o.Mime = window.moxie.core.utils.Mime; 10676 o.Exceptions = window.moxie.core.Exceptions; 10677 10678 // expose globally 10679 window.mOxie = o; 10680 if (!window.o) { 10681 window.o = o; 10682 } 10683 return o; 10684 })();