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