1/*! 2 * qiniu-js-sdk v1.0.19 3 * 4 * Copyright 2015 by Qiniu 5 * Released under GPL V2 License. 6 * 7 * GitHub: http://github.com/qiniu/js-sdk 8 * 9 * Date: 2017-3-15 10*/ 11 12/*global plupload ,mOxie*/ 13/*global ActiveXObject */ 14/*exported Qiniu */ 15/*exported QiniuJsSDK */ 16 17;(function( global ){ 18 19/** 20 * Creates new cookie or removes cookie with negative expiration 21 * @param key The key or identifier for the store 22 * @param value Contents of the store 23 * @param exp Expiration - creation defaults to 30 days 24 */ 25function createCookie(key, value, exp) { 26 var date = new Date(); 27 date.setTime(date.getTime() + (exp * 24 * 60 * 60 * 1000)); 28 var expires = "; expires=" + date.toGMTString(); 29 document.cookie = key + "=" + value + expires + "; path=/"; 30} 31 32/** 33 * Returns contents of cookie 34 * @param key The key or identifier for the store 35 */ 36function readCookie(key) { 37 var nameEQ = key + "="; 38 var ca = document.cookie.split(';'); 39 for (var i = 0, max = ca.length; i < max; i++) { 40 var c = ca[i]; 41 while (c.charAt(0) === ' ') { 42 c = c.substring(1, c.length); 43 } 44 if (c.indexOf(nameEQ) === 0) { 45 return c.substring(nameEQ.length, c.length); 46 } 47 } 48 return null; 49} 50 51// if current browser is not support localStorage 52// use cookie to make a polyfill 53if ( !window.localStorage ) { 54 window.localStorage = { 55 setItem: function (key, value) { 56 createCookie(key, value, 30); 57 }, 58 getItem: function (key) { 59 return readCookie(key); 60 }, 61 removeItem: function (key) { 62 createCookie(key, '', -1); 63 } 64 }; 65} 66 67function QiniuJsSDK() { 68 69 var that = this; 70 71 /** 72 * detect IE version 73 * if current browser is not IE 74 * it will return false 75 * else 76 * it will return version of current IE browser 77 * @return {Number|Boolean} IE version or false 78 */ 79 this.detectIEVersion = function() { 80 var v = 4, 81 div = document.createElement('div'), 82 all = div.getElementsByTagName('i'); 83 while ( 84 div.innerHTML = '<!--[if gt IE ' + v + ']><i></i><![endif]-->', 85 all[0] 86 ) { 87 v++; 88 } 89 return v > 4 ? v : false; 90 }; 91 92 var logger = { 93 MUTE: 0, 94 FATA: 1, 95 ERROR: 2, 96 WARN: 3, 97 INFO: 4, 98 DEBUG: 5, 99 TRACE: 6, 100 level: 0 101 }; 102 103 function log(type, args){ 104 var header = "[qiniu-js-sdk]["+type+"]"; 105 var msg = header; 106 for (var i = 0; i < args.length; i++) { 107 if (typeof args[i] === "string") { 108 msg += " " + args[i]; 109 } else { 110 msg += " " + that.stringifyJSON(args[i]); 111 } 112 } 113 if (that.detectIEVersion()) { 114 // http://stackoverflow.com/questions/5538972/console-log-apply-not-working-in-ie9 115 //var log = Function.prototype.bind.call(console.log, console); 116 //log.apply(console, args); 117 console.log(msg); 118 }else{ 119 args.unshift(header); 120 console.log.apply(console, args); 121 } 122 if (document.getElementById('qiniu-js-sdk-log')) { 123 document.getElementById('qiniu-js-sdk-log').innerHTML += '<p>'+msg+'</p>'; 124 } 125 } 126 127 function makeLogFunc(code){ 128 var func = code.toLowerCase(); 129 logger[func] = function(){ 130 // logger[func].history = logger[func].history || []; 131 // logger[func].history.push(arguments); 132 if(window.console && window.console.log && logger.level>=logger[code]){ 133 var args = Array.prototype.slice.call(arguments); 134 log(func,args); 135 } 136 }; 137 } 138 139 for (var property in logger){ 140 if (logger.hasOwnProperty(property) && (typeof logger[property]) === "number" && !logger.hasOwnProperty(property.toLowerCase())) { 141 makeLogFunc(property); 142 } 143 } 144 145 146 var qiniuUploadUrl; 147 if (window.location.protocol === 'https:') { 148 qiniuUploadUrl = 'https://up.qbox.me'; 149 } else { 150 qiniuUploadUrl = 'http://upload.qiniu.com'; 151 } 152 153 /** 154 * qiniu upload urls 155 * 'qiniuUploadUrls' is used to change target when current url is not avaliable 156 * @type {Array} 157 */ 158 var qiniuUploadUrls = [ 159 "http://upload.qiniu.com", 160 "http://up.qiniu.com" 161 ]; 162 163 var qiniuUpHosts = { 164 "http": [ 165 "http://upload.qiniu.com", 166 "http://up.qiniu.com" 167 ], 168 "https": [ 169 "https://up.qbox.me" 170 ] 171 }; 172 173 var changeUrlTimes = 0; 174 175 /** 176 * reset upload url 177 * if current page protocal is https 178 * it will always return 'https://up.qbox.me' 179 * else 180 * it will set 'qiniuUploadUrl' value with 'qiniuUploadUrls' looply 181 */ 182 this.resetUploadUrl = function(){ 183 var hosts = window.location.protocol === 'https:' ? qiniuUpHosts.https : qiniuUpHosts.http; 184 var i = changeUrlTimes % hosts.length; 185 qiniuUploadUrl = hosts[i]; 186 changeUrlTimes++; 187 logger.debug('resetUploadUrl: '+qiniuUploadUrl); 188 }; 189
190 // this.resetUploadUrl(); 191 192 193 /** 194 * is image 195 * @param {String} url of a file 196 * @return {Boolean} file is a image or not 197 */ 198 this.isImage = function(url) { 199 url = url.split(/[?#]/)[0]; 200 return (/\.(png|jpg|jpeg|gif|bmp)$/i).test(url); 201 }; 202 203 /** 204 * get file extension 205 * @param {String} filename 206 * @return {String} file extension 207 * @example 208 * input: test.txt 209 * output: txt 210 */ 211 this.getFileExtension = function(filename) { 212 var tempArr = filename.split("."); 213 var ext; 214 if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) { 215 ext = ""; 216 } else { 217 ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case 218 } 219 return ext; 220 }; 221 222 /** 223 * encode string by utf8 224 * @param {String} string to encode 225 * @return {String} encoded string 226 */ 227 this.utf8_encode = function(argString) { 228 // http://kevin.vanzonneveld.net 229 // + original by: Webtoolkit.info (http://www.webtoolkit.info/) 230 // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 231 // + improved by: sowberry 232 // + tweaked by: Jack 233 // + bugfixed by: Onno Marsman 234 // + improved by: Yves Sucaet 235 // + bugfixed by: Onno Marsman 236 // + bugfixed by: Ulrich 237 // + bugfixed by: Rafal Kukawski 238 // + improved by: kirilloid 239 // + bugfixed by: kirilloid 240 // * example 1: this.utf8_encode('Kevin van Zonneveld'); 241 // * returns 1: 'Kevin van Zonneveld' 242 243 if (argString === null || typeof argString === 'undefined') { 244 return ''; 245 } 246 247 var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n'); 248 var utftext = '', 249 start, end, stringl = 0; 250 251 start = end = 0; 252 stringl = string.length; 253 for (var n = 0; n < stringl; n++) { 254 var c1 = string.charCodeAt(n); 255 var enc = null; 256 257 if (c1 < 128) { 258 end++; 259 } else if (c1 > 127 && c1 < 2048) { 260 enc = String.fromCharCode( 261 (c1 >> 6) | 192, (c1 & 63) | 128 262 ); 263 } else if (c1 & 0xF800 ^ 0xD800 > 0) { 264 enc = String.fromCharCode( 265 (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 266 ); 267 } else { // surrogate pairs 268 if (c1 & 0xFC00 ^ 0xD800 > 0) { 269 throw new RangeError('Unmatched trail surrogate at ' + n); 270 } 271 var c2 = string.charCodeAt(++n); 272 if (c2 & 0xFC00 ^ 0xDC00 > 0) { 273 throw new RangeError('Unmatched lead surrogate at ' + (n - 1)); 274 } 275 c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; 276 enc = String.fromCharCode( 277 (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 278 ); 279 } 280 if (enc !== null) { 281 if (end > start) { 282 utftext += string.slice(start, end); 283 } 284 utftext += enc; 285 start = end = n + 1; 286 } 287 } 288 289 if (end > start) { 290 utftext += string.slice(start, stringl); 291 } 292 293 return utftext; 294 }; 295 296 this.base64_decode = function (data) { 297 // http://kevin.vanzonneveld.net 298 // + original by: Tyler Akins (http://rumkin.com) 299 // + improved by: Thunder.m 300 // + input by: Aman Gupta 301 // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 302 // + bugfixed by: Onno Marsman 303 // + bugfixed by: Pellentesque Malesuada 304 // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 305 // + input by: Brett Zamir (http://brett-zamir.me) 306 // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 307 // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); 308 // * returns 1: 'Kevin van Zonneveld' 309 // mozilla has this native 310 // - but breaks in 2.0.0.12! 311 //if (typeof this.window['atob'] == 'function') { 312 // return atob(data); 313 //} 314 var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
314+/="; 315 var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, 316 ac = 0, 317 dec = "", 318 tmp_arr = []; 319 320 if (!data) { 321 return data; 322 } 323 324 data += ''; 325 326 do { // unpack four hexets into three octets using index points in b64 327 h1 = b64.indexOf(data.charAt(i++)); 328 h2 = b64.indexOf(data.charAt(i++)); 329 h3 = b64.indexOf(data.charAt(i++)); 330 h4 = b64.indexOf(data.charAt(i++)); 331 332 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; 333 334 o1 = bits >> 16 & 0xff; 335 o2 = bits >> 8 & 0xff; 336 o3 = bits & 0xff; 337 338 if (h3 === 64) { 339 tmp_arr[ac++] = String.fromCharCode(o1); 340 } else if (h4 === 64) { 341 tmp_arr[ac++] = String.fromCharCode(o1, o2); 342 } else { 343 tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); 344 } 345 } while (i < data.length); 346 347 dec = tmp_arr.join(''); 348 349 return dec; 350 }; 351 352 /** 353 * encode data by base64 354 * @param {String} data to encode 355 * @return {String} encoded data 356 */ 357 this.base64_encode = function(data) { 358 // http://kevin.vanzonneveld.net 359 // + original by: Tyler Akins (http://rumkin.com) 360 // + improved by: Bayron Guevara 361 // + improved by: Thunder.m 362 // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 363 // + bugfixed by: Pellentesque Malesuada 364 // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 365 // - depends on: this.utf8_encode 366 // * example 1: this.base64_encode('Kevin van Zonneveld'); 367 // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' 368 // mozilla has this native 369 // - but breaks in 2.0.0.12! 370 //if (typeof this.window['atob'] == 'function') { 371 // return atob(data); 372 //} 373 var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 374 var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, 375 ac = 0, 376 enc = '', 377 tmp_arr = []; 378 379 if (!data) { 380 return data; 381 } 382 383 data = this.utf8_encode(data + ''); 384 385 do { // pack three octets into four hexets 386 o1 = data.charCodeAt(i++); 387 o2 = data.charCodeAt(i++); 388 o3 = data.charCodeAt(i++); 389 390 bits = o1 << 16 | o2 << 8 | o3; 391 392 h1 = bits >> 18 & 0x3f; 393 h2 = bits >> 12 & 0x3f; 394 h3 = bits >> 6 & 0x3f; 395 h4 = bits & 0x3f; 396 397 // use hexets to index into b64, and append result to encoded string 398 tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); 399 } while (i < data.length); 400 401 enc = tmp_arr.join(''); 402 403 switch (data.length % 3) { 404 case 1: 405 enc = enc.slice(0, -2) + '=='; 406 break; 407 case 2: 408 enc = enc.slice(0, -1) + '='; 409 break; 410 } 411 412 return enc; 413 }; 414 415 /** 416 * encode string in url by base64 417 * @param {String} string in url 418 * @return {String} encoded string 419 */ 420 this.URLSafeBase64Encode = function(v) { 421 v = this.base64_encode(v); 422 return v.replace(/\//g, '_').replace(/\+/g, '-'); 423 }; 424 425 this.URLSafeBase64Decode = function(v) { 426 v = v.replace(/_/g, '/').replace(/-/g, '+'); 427 return this.base64_decode(v); 428 }; 429 430 // TODO: use mOxie 431 /** 432 * craete object used to AJAX 433 * @return {Object} 434 */ 435 this.createAjax = function(argument) { 436 var xmlhttp = {}; 437 if (window.XMLHttpRequest) { 438 xmlhttp = new XMLHttpRequest(); 439 } else { 440 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 441 } 442 return xmlhttp; 443 }; 444 445 // TODO: enhance IE compatibility 446 /** 447 * parse json string to javascript object 448 * @param {String} json string 449 * @return {Object} object 450 */ 451 this.parseJSON = function(data) { 452 // Attempt to parse using the native JSON parser first 453 if (window.JSON && window.JSON.parse) { 454 return window.JSON.parse(data); 455 } 456 457 //var rx_one = /^[\],:{}\s]*$/, 458 // rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, 459 // rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, 460 // rx_four = /(?:^|:|,)(?:\s*\[)+/g, 461 var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 462 463 //var json; 464 465 var text = String(data); 466 rx_dangerous.lastIndex = 0; 467 if(rx_dangerous.test(text)){ 468 text = text.replace(rx_dangerous, function(a){
469 return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 470 }); 471 } 472 473 // todo 使ç¨ä¸ä¸å¤æ,å¢å å®å ¨æ§ 474 //if ( 475 // rx_one.test( 476 // text 477 // .replace(rx_two, '@') 478 // .replace(rx_three, ']') 479 // .replace(rx_four, '') 480 // ) 481 //) { 482 // return eval('(' + text + ')'); 483 //} 484 485 return eval('('+text+')'); 486 }; 487 488 /** 489 * parse javascript object to json string 490 * @param {Object} object 491 * @return {String} json string 492 */ 493 this.stringifyJSON = function(obj) { 494 // Attempt to parse using the native JSON parser first 495 if (window.JSON && window.JSON.stringify) { 496 return window.JSON.stringify(obj); 497 } 498 switch (typeof (obj)) { 499 case 'string': 500 return '"' + obj.replace(/(["\\])/g, '\\$1') + '"'; 501 case 'array': 502 return '[' + obj.map(that.stringifyJSON).join(',') + ']'; 503 case 'object': 504 if (obj instanceof Array) { 505 var strArr = []; 506 var len = obj.length; 507 for (var i = 0; i < len; i++) { 508 strArr.push(that.stringifyJSON(obj[i])); 509 } 510 return '[' + strArr.join(',') + ']'; 511 } else if (obj === null) { 512 return 'null'; 513 } else { 514 var string = []; 515 for (var property in obj) { 516 if (obj.hasOwnProperty(property)) { 517 string.push(that.stringifyJSON(property) + ':' + that.stringifyJSON(obj[property])); 518 } 519 } 520 return '{' + string.join(',') + '}'; 521 } 522 break; 523 case 'number': 524 return obj; 525 case false: 526 return obj; 527 case 'boolean': 528 return obj; 529 } 530 }; 531 532 /** 533 * trim space beside text 534 * @param {String} untrimed string 535 * @return {String} trimed string 536 */ 537 this.trim = function(text) { 538 return text === null ? "" : text.replace(/^\s+|\s+$/g, ''); 539 }; 540 541 /** 542 * create a uploader by QiniuJsSDK 543 * @param {object} options to create a new uploader 544 * @return {object} uploader 545 */ 546 this.uploader = function(op) { 547 548 /********** inner function define start **********/ 549 550 // according the different condition to reset chunk size 551 // and the upload strategy according with the chunk size 552 // when chunk size is zero will cause to direct upload 553 // see the statement binded on 'BeforeUpload' event 554 var reset_chunk_size = function() { 555 var ie = that.detectIEVersion(); 556 var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size; 557 // case Safari 5ãWindows 7ãiOS 7 set isSpecialSafari to true 558 var isSpecialSafari = (mOxie.Env.browser === "Safari" && mOxie.Env.version <= 5 && mOxie.Env.os === "Windows" && mOxie.Env.osVersion === "7") || (mOxie.Env.browser === "Safari" && mOxie.Env.os === "iOS" && mOxie.Env.osVersion === "7"); 559 // case IE 9-ï¼chunk_size is not empty and flash is included in runtimes 560 // set op.chunk_size to zero 561 //if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { 562 if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { 563 // link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not 564 // when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) . 565 op.chunk_size = 0; 566 } else if (isSpecialSafari) { 567 // win7 safari / iOS7 safari have bug when in chunk upload mode 568 // reset chunk_size to 0 569 // disable chunk in special version safari 570 op.chunk_size = 0; 571 } else { 572 BLOCK_BITS = 20; 573 MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M 574 575 chunk_size = plupload.parseSize(op.chunk_size); 576 if (chunk_size > MAX_CHUNK_SIZE) { 577 op.chunk_size = MAX_CHUNK_SIZE; 578 } 579 // qiniu service max_chunk_size is 4m 580 // reset chunk_size to max_chunk_size(4m) when chunk_size > 4m 581 } 582 // if op.chunk_size set 0 will be cause to direct upload 583 }; 584 585 var getHosts = function(hosts) { 586 var result = []; 587 for (var i = 0; i < hosts.length; i++) { 588 var host = hosts[i]; 589 if (host.indexOf('-H') === 0) { 590 result.push(host.split(' ')[2]); 591 } else { 592 result.push(host); 593 } 594 } 595 return result; 596 }; 597 598 var getPutPolicy = function (uptoken) { 599 var segments = uptoken.split(":"); 600 var ak = segments[0]; 601 var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); 602 putPolicy.ak = ak; 603 if (putPolicy.scope.indexOf(":") >= 0) { 604 putPolicy.bucket = putPolicy.scope.split(":")[0]; 605 putPolicy.key = putPolicy.scope.split(":")[1]; 606 } else { 607 putPolicy.bucket = putPolicy.scope; 608 } 609 return putPolicy; 610 }; 611 612 var getUpHosts = function(uptoken) { 613 var putPolicy = getPutPolicy(uptoken); 614 // var uphosts_url = "//uc.qbox.me/v1/query?ak="+ak+"&bucket="+putPolicy.scope; 615 // IE 9- is not support protocal relative url 616 var uphosts_url = window.location.protocol + "//uc.qbox.me/v1/query?ak=" + putPolicy.ak + "&bucket=" + putPolicy.bucket; 617 logger.debug("putPolicy: ", putPolicy); 618 logger.debug("get uphosts from: ", uphosts_url); 619 var ie = that.detectIEVersion(); 620 var ajax; 621 if (ie && ie <= 9) { 622 ajax = new mOxie.XMLHttpRequest(); 623 mOxie.Env.swf_url = op.flash_swf_url; 624 }else{ 625 ajax = that.createAjax(); 626 } 627 ajax.open('GET', uphosts_url, false); 628 var onreadystatechange = function(){ 629 logger.debug("ajax.readyState: ", ajax.readyState); 630 if (ajax.readyState === 4) { 631 logger.debug("ajax.status: ", ajax.status); 632 if (ajax.status < 400) { 633 var res = that.parseJSON(ajax.responseText); 634 qiniuUpHosts.http = getHosts(res.http.up); 635 qiniuUpHosts.https = getHosts(res.https.up); 636 logger.debug("get new uphosts: ", qiniuUpHosts);
637 that.resetUploadUrl(); 638 } else { 639 logger.error("get uphosts error: ", ajax.responseText); 640 } 641 } 642 }; 643 if (ie && ie <= 9) { 644 ajax.bind('readystatechange', onreadystatechange); 645 }else{ 646 ajax.onreadystatechange = onreadystatechange; 647 } 648 ajax.send(); 649 // ajax.send(); 650 // if (ajax.status < 400) { 651 // var res = that.parseJSON(ajax.responseText); 652 // qiniuUpHosts.http = getHosts(res.http.up); 653 // qiniuUpHosts.https = getHosts(res.https.up); 654 // logger.debug("get new uphosts: ", qiniuUpHosts); 655 // that.resetUploadUrl(); 656 // } else { 657 // logger.error("get uphosts error: ", ajax.responseText); 658 // } 659 return; 660 }; 661 662 var getUptoken = function(file) { 663 if (!that.token || (op.uptoken_url && that.tokenInfo.isExpired())) { 664 return getNewUpToken(file); 665 } else { 666 return that.token; 667 } 668 }; 669 670 // getNewUptoken maybe called at Init Event or BeforeUpload Event 671 // case Init Event, the file param of getUptken will be set a null value 672 // if op.uptoken has value, set uptoken with op.uptoken 673 // else if op.uptoken_url has value, set uptoken from op.uptoken_url 674 // else if op.uptoken_func has value, set uptoken by result of op.uptoken_func 675 var getNewUpToken = function(file) { 676 if (op.uptoken) { 677 that.token = op.uptoken; 678 } else if (op.uptoken_url) { 679 logger.debug("get uptoken from: ", that.uptoken_url); 680 // TODO: use mOxie 681 var ajax = that.createAjax(); 682 ajax.open('GET', that.uptoken_url, false); 683 ajax.setRequestHeader("If-Modified-Since", "0"); 684 // ajax.onreadystatechange = function() { 685 // if (ajax.readyState === 4 && ajax.status === 200) { 686 // var res = that.parseJSON(ajax.responseText); 687 // that.token = res.uptoken; 688 // } 689 // }; 690 ajax.send(); 691 if (ajax.status === 200) { 692 var res = that.parseJSON(ajax.responseText); 693 that.token = res.uptoken; 694 var segments = that.token.split(":"); 695 var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); 696 if (!that.tokenMap) { 697 that.tokenMap = {}; 698 } 699 var getTimestamp = function(time) { 700 return Math.ceil(time.getTime()/1000); 701 }; 702 var serverTime = getTimestamp(new Date(ajax.getResponseHeader("date"))); 703 var clientTime = getTimestamp(new Date()); 704 that.tokenInfo = { 705 serverDelay: clientTime - serverTime, 706 deadline: putPolicy.deadline, 707 isExpired: function() { 708 var leftTime = this.deadline - getTimestamp(new Date()) + this.serverDelay; 709 return leftTime < 600; 710 } 711 }; 712 logger.debug("get new uptoken: ", that.token); 713 logger.debug("get token info: ", that.tokenInfo); 714 } else { 715 logger.error("get uptoken error: ", ajax.responseText); 716 } 717 } else if (op.uptoken_func) { 718 logger.debug("get uptoken from uptoken_func"); 719 that.token = op.uptoken_func(file); 720 logger.debug("get new uptoken: ", that.token); 721 } else { 722 logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!"); 723 } 724 if (that.token) { 725 getUpHosts(that.token); 726 } 727 return that.token; 728 }; 729 730 // get file key according with the user passed options 731 var getFileKey = function(up, file, func) { 732 // WARNING 733 // When you set the key in putPolicy by "scope": "bucket:key" 734 // You should understand the risk of override a file in the bucket 735 // So the code below that automatically get key from uptoken has been commented 736 // var putPolicy = getPutPolicy(that.token) 737 // if (putPolicy.key) { 738 // logger.debug("key is defined in putPolicy.scope: ", putPolicy.key) 739 // return putPolicy.key 740 // } 741 var key = '', 742 unique_names = false;
743 if (!op.save_key) { 744 unique_names = up.getOption && up.getOption('unique_names'); 745 unique_names = unique_names || (up.settings && up.settings.unique_names); 746 if (unique_names) { 747 var ext = that.getFileExtension(file.name); 748 key = ext ? file.id + '.' + ext : file.id; 749 } else if (typeof func === 'function') { 750 key = func(up, file); 751 } else { 752 key = file.name; 753 } 754 } 755 return key; 756 }; 757 758 /********** inner function define end **********/ 759 760 if (op.log_level) { 761 logger.level = op.log_level; 762 } 763 764 if (!op.domain) { 765 throw 'domain setting in options is required!'; 766 } 767 768 if (!op.browse_button) { 769 throw 'browse_button setting in options is required!'; 770 } 771 772 if (!op.uptoken && !op.uptoken_url && !op.uptoken_func) { 773 throw 'one of [uptoken, uptoken_url, uptoken_func] settings in options is required!'; 774 } 775 776 logger.debug("init uploader start"); 777 778 logger.debug("environment: ", mOxie.Env); 779 780 logger.debug("userAgent: ", navigator.userAgent); 781 782 var option = {}; 783 784 // hold the handler from user passed options 785 var _Error_Handler = op.init && op.init.Error; 786 var _FileUploaded_Handler = op.init && op.init.FileUploaded; 787 788 // replace the handler for intercept 789 op.init.Error = function() {}; 790 op.init.FileUploaded = function() {}; 791 792 that.uptoken_url = op.uptoken_url; 793 that.token = ''; 794 that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : ''; 795 this.domain = op.domain; 796 // TODO: ctx is global in scope of a uploader instance 797 // this maybe cause error 798 var ctx = ''; 799 var speedCalInfo = { 800 isResumeUpload: false, 801 resumeFilesize: 0, 802 startTime: '', 803 currentTime: '' 804 }; 805 806 reset_chunk_size(); 807 logger.debug("invoke reset_chunk_size()"); 808 logger.debug("op.chunk_size: ", op.chunk_size); 809 810 var defaultSetting = { 811 url: qiniuUploadUrl, 812 multipart_params: { 813 token: '' 814 } 815 }; 816 var ie = that.detectIEVersion(); 817 // case IE 9- 818 // add accept in multipart params 819 if (ie && ie <= 9) { 820 defaultSetting.multipart_params.accept = 'text/plain; charset=utf-8'; 821 logger.debug("add accept text/plain in multipart params"); 822 } 823 824 // compose options with user passed options and default setting 825 plupload.extend(option, op, defaultSetting); 826 827 logger.debug("option: ", option); 828 829 // create a new uploader with composed options 830 var uploader = new plupload.Uploader(option); 831 832 logger.debug("new plupload.Uploader(option)"); 833 834 // bind getNewUpToken to 'Init' event 835 uploader.bind('Init', function(up, params) { 836 logger.debug("Init event activated"); 837 // if op.get_new_uptoken is not true 838 // invoke getNewUptoken when uploader init 839 // else 840 // getNewUptoken everytime before a new file upload 841 if(!op.get_new_uptoken){ 842 getNewUpToken(null); 843 } 844 //getNewUpToken(null); 845 }); 846 847 logger.debug("bind Init event"); 848 849 // bind 'FilesAdded' event 850 // when file be added and auto_start has set value 851 // uploader will auto start upload the file 852 uploader.bind('FilesAdded', function(up, files) { 853 logger.debug("FilesAdded event activated"); 854 var auto_start = up.getOption && up.getOption('auto_start'); 855 auto_start = auto_start || (up.settings && up.settings.auto_start); 856 logger.debug("auto_start: ", auto_start); 857 logger.debug("files: ", files); 858 859 // detect is iOS 860 var is_ios = function (){ 861 if(mOxie.Env.OS.toLowerCase()==="ios") { 862 return true; 863 } else { 864 return false;
865 } 866 }; 867 868 // if current env os is iOS change file name to [time].[ext] 869 if (is_ios()) { 870 for (var i = 0; i < files.length; i++) { 871 var file = files[i]; 872 var ext = that.getFileExtension(file.name); 873 file.name = file.id + "." + ext; 874 } 875 } 876 877 if (auto_start) { 878 setTimeout(function(){ 879 up.start(); 880 logger.debug("invoke up.start()"); 881 }, 0); 882 // up.start(); 883 // plupload.each(files, function(i, file) { 884 // up.start(); 885 // logger.debug("invoke up.start()") 886 // logger.debug("file: ", file); 887 // }); 888 } 889 up.refresh(); // Reposition Flash/Silverlight 890 }); 891 892 logger.debug("bind FilesAdded event"); 893 894 // bind 'BeforeUpload' event 895 // intercept the process of upload 896 // - prepare uptoken 897 // - according the chunk size to make differnt upload strategy 898 // - resume upload with the last breakpoint of file 899 uploader.bind('BeforeUpload', function(up, file) { 900 logger.debug("BeforeUpload event activated"); 901 // add a key named speed for file object 902 file.speed = file.speed || 0; 903 ctx = ''; 904 905 if(op.get_new_uptoken){ 906 getNewUpToken(file); 907 } 908 909 var directUpload = function(up, file, func) { 910 speedCalInfo.startTime = new Date().getTime(); 911 var multipart_params_obj; 912 if (op.save_key) { 913 multipart_params_obj = { 914 'token': that.token 915 }; 916 } else { 917 multipart_params_obj = { 918 'key': getFileKey(up, file, func), 919 'token': that.token 920 }; 921 } 922 var ie = that.detectIEVersion(); 923 // case IE 9- 924 // add accept in multipart params 925 if (ie && ie <= 9) { 926 multipart_params_obj.accept = 'text/plain; charset=utf-8'; 927 logger.debug("add accept text/plain in multipart params"); 928 } 929 930 logger.debug("directUpload multipart_params_obj: ", multipart_params_obj); 931 932 var x_vars = op.x_vars; 933 if (x_vars !== undefined && typeof x_vars === 'object') { 934 for (var x_key in x_vars) { 935 if (x_vars.hasOwnProperty(x_key)) { 936 if (typeof x_vars[x_key] === 'function') { 937 multipart_params_obj['x:' + x_key] = x_vars[x_key](up, file); 938 } else if (typeof x_vars[x_key] !== 'object') { 939 multipart_params_obj['x:' + x_key] = x_vars[x_key]; 940 } 941 } 942 } 943 } 944 945 up.setOption({ 946 'url': qiniuUploadUrl, 947 'multipart': true, 948 'chunk_size': is_android_weixin_or_qq() ? op.max_file_size : undefined, 949 'multipart_params': multipart_params_obj 950 }); 951 }; 952 953 // detect is weixin or qq inner browser 954 var is_android_weixin_or_qq = function (){ 955 var ua = navigator.userAgent.toLowerCase(); 956 if((ua.match(/MicroMessenger/i) || mOxie.Env.browser === "QQBrowser" || ua.match(/V1_AND_SQ/i)) && mOxie.Env.OS.toLowerCase()==="android") { 957 return true; 958 } else { 959 return false; 960 } 961 }; 962 963 var chunk_size = up.getOption && up.getOption('chunk_size'); 964 chunk_size = chunk_size || (up.settings && up.settings.chunk_size); 965 966 logger.debug("uploader.runtime: ",uploader.runtime); 967 logger.debug("chunk_size: ",chunk_size); 968 969 // TODO: flash support chunk upload 970 if ((uploader.runtime === 'html5' || uploader.runtime === 'flash') && chunk_size) { 971 if (file.size < chunk_size || is_android_weixin_or_qq()) { 972 logger.debug("directUpload because file.size < chunk_size || is_android_weixin_or_qq()"); 973 // direct upload if file size is less then the chunk size 974 directUpload(up, file, that.key_handler); 975 } else { 976 // TODO: need a polifill to make it work in IE 9- 977 // ISSUE: if file.name is existed in localStorage 978 // but not the same file maybe cause error 979 var localFileInfo = localStorage.getItem(file.name); 980 var blockSize = chunk_size; 981 if (localFileInfo) { 982 // TODO: although only the html5 runtime will enter this statement 983 // but need uniform way to make convertion between string and json 984 localFileInfo = that.parseJSON(localFileInfo); 985 var now = (new Date()).getTime(); 986 var before = localFileInfo.time || 0; 987 var aDay = 24 * 60 * 60 * 1000; // milliseconds of one day 988 // if the last upload time is within one day 989 // will upload continuously follow the last breakpoint 990 // else 991 // will reupload entire file 992 if (now - before < aDay) { 993 994 if (localFileInfo.percent !== 100) { 995 if (file.size === localFileInfo.total) { 996 // TODO: if file.name and file.size is the same 997 // but not the same file will cause error 998 file.percent = localFileInfo.percent; 999 file.loaded = localFileInfo.offset; 1000 ctx = localFileInfo.ctx; 1001 1002 // set speed info 1003 speedCalInfo.isResumeUpload = true; 1004 speedCalInfo.resumeFilesize = localFileInfo.offset; 1005 1006 // set block size 1007 if (localFileInfo.offset + blockSize > file.size) { 1008 blockSize = file.size - localFileInfo.offset; 1009 } 1010 } else { 1011 // remove file info when file.size is conflict with file info 1012 localStorage.removeItem(file.name); 1013 } 1014 1015 } else { 1016 // remove file info when upload percent is 100% 1017 // avoid 499 bug 1018 localStorage.removeItem(file.name); 1019 } 1020 } else { 1021 // remove file info when last upload time is over one day 1022 localStorage.removeItem(file.name); 1023 } 1024 } 1025 speedCalInfo.startTime = new Date().getTime(); 1026 var multipart_params_obj = {}; 1027 var ie = that.detectIEVersion(); 1028 // case IE 9- 1029 // add accept in multipart params 1030 if (ie && ie <= 9) { 1031 multipart_params_obj.accept = 'text/plain; charset=utf-8'; 1032 logger.debug("add accept text/plain in multipart params"); 1033 } 1034 // TODO: to support bput 1035 // http://developer.qiniu.com/docs/v6/api/reference/up/bput.html 1036 up.setOption({ 1037 'url': qiniuUploadUrl + '/mkblk/' + blockSize, 1038 'multipart': false, 1039 'chunk_size': chunk_size, 1040 'required_features': "chunks", 1041 'headers': { 1042 'Authorization': 'UpToken ' + getUptoken(file) 1043 }, 1044 'multipart_params': multipart_params_obj 1045 }); 1046 } 1047 } else { 1048 logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size"); 1049 // direct upload if runtime is not html5 1050 directUpload(up, file, that.key_handler); 1051 } 1052 }); 1053 1054 logger.debug("bind BeforeUpload event"); 1055 1056 // bind 'UploadProgress' event 1057 // calculate upload speed 1058 uploader.bind('UploadProgress', function(up, file) { 1059 logger.trace("UploadProgress event activated"); 1060 speedCalInfo.currentTime = new Date().getTime(); 1061 var timeUsed = speedCalInfo.currentTime - speedCalInfo.startTime; // ms 1062 var fileUploaded = file.loaded || 0; 1063 if (speedCalInfo.isResumeUpload) { 1064 fileUploaded = file.loaded - speedCalInfo.resumeFilesize; 1065 } 1066 file.speed = (fileUploaded / timeUsed * 1000).toFixed(0) || 0; // unit: byte/s 1067 }); 1068 1069 logger.debug("bind UploadProgress event"); 1070 1071 // bind 'ChunkUploaded' event 1072 // store the chunk upload info and set next chunk upload url 1073 uploader.bind('ChunkUploaded', function(up, file, info) { 1074 logger.debug("ChunkUploaded event activated"); 1075 logger.debug("file: ", file); 1076 logger.debug("info: ", info); 1077 var res = that.parseJSON(info.response); 1078 logger.debug("res: ", res); 1079 // ctx should look like '[chunk01_ctx],[chunk02_ctx],[chunk03_ctx],...' 1080 ctx = ctx ? ctx + ',' + res.ctx : res.ctx; 1081 var leftSize = info.total - info.offset; 1082 var chunk_size = up.getOption && up.getOption('chunk_size'); 1083 chunk_size = chunk_size || (up.settings && up.settings.chunk_size); 1084 if (leftSize < chunk_size) { 1085 up.setOption({ 1086 'url': qiniuUploadUrl + '/mkblk/' + leftSize 1087 }); 1088 logger.debug("up.setOption url: ", qiniuUploadUrl + '/mkblk/' + leftSize); 1089 } 1090 up.setOption({ 1091 'headers': { 1092 'Authorization': 'UpToken ' + getUptoken(file) 1093 } 1094 }); 1095 localStorage.setItem(file.name, that.stringifyJSON({ 1096 ctx: ctx, 1097 percent: file.percent, 1098 total: info.total, 1099 offset: info.offset, 1100 time: (new Date()).getTime() 1101 })); 1102 }); 1103 1104 logger.debug("bind ChunkUploaded event"); 1105 1106 var retries = qiniuUploadUrls.length; 1107 1108 // if error is unkown switch upload url and retry 1109 var unknow_error_retry = function(file){ 1110 if (retries-- > 0) { 1111 setTimeout(function(){
1112 that.resetUploadUrl(); 1113 file.status = plupload.QUEUED; 1114 uploader.stop(); 1115 uploader.start(); 1116 }, 0); 1117 return true; 1118 }else{ 1119 retries = qiniuUploadUrls.length; 1120 return false; 1121 } 1122 }; 1123 1124 // bind 'Error' event 1125 // check the err.code and return the errTip 1126 uploader.bind('Error', (function(_Error_Handler) { 1127 return function(up, err) { 1128 logger.error("Error event activated"); 1129 logger.error("err: ", err); 1130 var errTip = ''; 1131 var file = err.file; 1132 if (file) { 1133 switch (err.code) { 1134 case plupload.FAILED: 1135 errTip = 'ä¸ä¼ 失败ã请ç¨ååè¯ã'; 1136 break; 1137 case plupload.FILE_SIZE_ERROR: 1138 var max_file_size = up.getOption && up.getOption('max_file_size'); 1139 max_file_size = max_file_size || (up.settings && up.settings.max_file_size); 1140 errTip = 'æµè§å¨æå¤§å¯ä¸ä¼ ' + max_file_size + 'ãæ´å¤§æä»¶è¯·ä½¿ç¨å½ä»¤è¡å·¥å ·ã'; 1141 break; 1142 case plupload.FILE_EXTENSION_ERROR: 1143 errTip = 'æä»¶éªè¯å¤±è´¥ã请ç¨åéè¯ã'; 1144 break; 1145 case plupload.HTTP_ERROR: 1146 if (err.response === '') { 1147 // Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE 1148 errTip = err.message || 'æªç¥ç½ç»é误ã'; 1149 if (!unknow_error_retry(file)) { 1150 return; 1151 } 1152 break; 1153 } 1154 var errorObj = that.parseJSON(err.response); 1155 var errorText = errorObj.error; 1156 switch (err.status) { 1157 case 400: 1158 errTip = "è¯·æ±æ¥ææ ¼å¼é误ã"; 1159 break; 1160 case 401: 1161 errTip = "客æ·ç«¯è®¤è¯ææå¤±è´¥ã请éè¯ææäº¤åé¦ã"; 1162 break; 1163 case 405: 1164 errTip = "客æ·ç«¯è¯·æ±é误ã请éè¯ææäº¤åé¦ã"; 1165 break; 1166 case 579: 1167 errTip = "èµæºä¸ä¼ æåï¼ä½åè°å¤±è´¥ã"; 1168 break; 1169 case 599: 1170 errTip = "ç½ç»è¿æ¥å¼å¸¸ã请éè¯ææäº¤åé¦ã"; 1171 if (!unknow_error_retry(file)) { 1172 return; 1173 } 1174 break; 1175 case 614: 1176 errTip = "æä»¶å·²åå¨ã"; 1177 try { 1178 errorObj = that.parseJSON(errorObj.error); 1179 errorText = errorObj.error || 'file exists'; 1180 } catch (e) { 1181 errorText = errorObj.error || 'file exists'; 1182 } 1183 break; 1184 case 631: 1185 errTip = "æå®ç©ºé´ä¸åå¨ã"; 1186 break; 1187 case 701: 1188 errTip = "ä¸ä¼ æ°æ®åæ ¡éªåºéã请éè¯ææäº¤åé¦ã"; 1189 break; 1190 default: 1191 errTip = "æªç¥é误ã"; 1192 if (!unknow_error_retry(file)) { 1193 return; 1194 } 1195 break; 1196 } 1197 errTip = errTip + '(' + err.status + 'ï¼' + errorText + ')'; 1198 break; 1199 case plupload.SECURITY_ERROR: 1200 errTip = 'å®å ¨é ç½®é误ã请èç³»ç½ç«ç®¡çåã'; 1201 break;
1202 case plupload.GENERIC_ERROR: 1203 errTip = 'ä¸ä¼ 失败ã请ç¨ååè¯ã'; 1204 break; 1205 case plupload.IO_ERROR: 1206 errTip = 'ä¸ä¼ 失败ã请ç¨ååè¯ã'; 1207 break; 1208 case plupload.INIT_ERROR: 1209 errTip = 'ç½ç«é ç½®é误ã请èç³»ç½ç«ç®¡çåã'; 1210 uploader.destroy(); 1211 break; 1212 default: 1213 errTip = err.message + err.details; 1214 if (!unknow_error_retry(file)) { 1215 return; 1216 } 1217 break; 1218 } 1219 if (_Error_Handler) { 1220 _Error_Handler(up, err, errTip); 1221 } 1222 } 1223 up.refresh(); // Reposition Flash/Silverlight 1224 }; 1225 })(_Error_Handler)); 1226 1227 logger.debug("bind Error event"); 1228 1229 // bind 'FileUploaded' event 1230 // intercept the complete of upload 1231 // - get downtoken from downtoken_url if bucket is private 1232 // - invoke mkfile api to compose chunks if upload strategy is chunk upload 1233 uploader.bind('FileUploaded', (function(_FileUploaded_Handler) { 1234 return function(up, file, info) { 1235 logger.debug("FileUploaded event activated"); 1236 logger.debug("file: ", file); 1237 logger.debug("info: ", info); 1238 var last_step = function(up, file, info) { 1239 if (op.downtoken_url) { 1240 // if op.dowontoken_url is not empty 1241 // need get downtoken before invoke the _FileUploaded_Handler 1242 var ajax_downtoken = that.createAjax(); 1243 ajax_downtoken.open('POST', op.downtoken_url, true); 1244 ajax_downtoken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 1245 ajax_downtoken.onreadystatechange = function() { 1246 if (ajax_downtoken.readyState === 4) { 1247 if (ajax_downtoken.status === 200) { 1248 var res_downtoken; 1249 try { 1250 res_downtoken = that.parseJSON(ajax_downtoken.responseText); 1251 } catch (e) { 1252 throw ('invalid json format'); 1253 } 1254 var info_extended = {}; 1255 plupload.extend(info_extended, that.parseJSON(info), res_downtoken); 1256 if (_FileUploaded_Handler) { 1257 _FileUploaded_Handler(up, file, that.stringifyJSON(info_extended)); 1258 } 1259 } else { 1260 uploader.trigger('Error', { 1261 status: ajax_downtoken.status, 1262 response: ajax_downtoken.responseText, 1263 file: file, 1264 code: plupload.HTTP_ERROR 1265 }); 1266 } 1267 } 1268 }; 1269 ajax_downtoken.send('key=' + that.parseJSON(info).key + '&domain=' + op.domain); 1270 } else if (_FileUploaded_Handler) { 1271 _FileUploaded_Handler(up, file, info); 1272 } 1273 }; 1274 1275 var res = that.parseJSON(info.response); 1276 ctx = ctx ? ctx : res.ctx; 1277 // if ctx is not empty 1278 // that means the upload strategy is chunk upload 1279 // befroe the invoke the last_step 1280 // we need request the mkfile to compose all uploaded chunks 1281 // else 1282 // invalke the last_step 1283 logger.debug("ctx: ", ctx); 1284 if (ctx) { 1285 var key = ''; 1286 logger.debug("save_key: ", op.save_key); 1287 if (!op.save_key) { 1288 key = getFileKey(up, file, that.key_handler); 1289 key = key ? '/key/' + that.URLSafeBase64Encode(key) : ''; 1290 } 1291 1292 var fname = '/fname/' + that.URLSafeBase64Encode(file.name); 1293 1294 logger.debug("op.x_vars: ", op.x_vars); 1295 var x_vars = op.x_vars, 1296 x_val = '', 1297 x_vars_url = ''; 1298 if (x_vars !== undefined && typeof x_vars === 'object') { 1299 for (var x_key in x_vars) { 1300 if (x_vars.hasOwnProperty(x_key)) { 1301 if (typeof x_vars[x_key] === 'function') { 1302 x_val = that.URLSafeBase64Encode(x_vars[x_key](up, file)); 1303 } else if (typeof x_vars[x_key] !== 'object') { 1304 x_val = that.URLSafeBase64Encode(x_vars[x_key]); 1305 } 1306 x_vars_url += '/x:' + x_key + '/' + x_val; 1307 } 1308 } 1309 } 1310 1311 var url = qiniuUploadUrl + '/mkfile/' + file.size + key + fname + x_vars_url; 1312 1313 var ie = that.detectIEVersion(); 1314 var ajax; 1315 if (ie && ie <= 9) { 1316 ajax = new mOxie.XMLHttpRequest(); 1317 mOxie.Env.swf_url = op.flash_swf_url; 1318 }else{ 1319 ajax = that.createAjax(); 1320 } 1321 ajax.open('POST', url, true); 1322 ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8'); 1323 ajax.setRequestHeader('Authorization', 'UpToken ' + that.token); 1324 var onreadystatechange = function(){ 1325 logger.debug("ajax.readyState: ", ajax.readyState); 1326 if (ajax.readyState === 4) { 1327 localStorage.removeItem(file.name); 1328 var info; 1329 if (ajax.status === 200) { 1330 info = ajax.responseText; 1331 logger.debug("mkfile is success: ", info); 1332 last_step(up, file, info); 1333 } else { 1334 info = { 1335 status: ajax.status, 1336 response: ajax.responseText, 1337 file: file, 1338 code: -200, 1339 responseHeaders: ajax.getAllResponseHeaders() 1340 }; 1341 logger.debug("mkfile is error: ", info); 1342 uploader.trigger('Error', info); 1343 } 1344 } 1345 }; 1346 if (ie && ie <= 9) { 1347 ajax.bind('readystatechange', onreadystatechange); 1348 }else{ 1349 ajax.onreadystatechange = onreadystatechange; 1350 } 1351 ajax.send(ctx); 1352 logger.debug("mkfile: ", url); 1353 } else { 1354 last_step(up, file, info.response); 1355 } 1356 1357 }; 1358 })(_FileUploaded_Handler)); 1359 1360 logger.debug("bind FileUploaded event"); 1361 1362 // init uploader 1363 uploader.init(); 1364 1365 logger.debug("invoke uploader.init()"); 1366 1367 logger.debug("init uploader end"); 1368 1369 return uploader; 1370 }; 1371 1372 /** 1373 * get url by key 1374 * @param {String} key of file 1375 * @return {String} url of file 1376 */ 1377 this.getUrl = function(key) { 1378 if (!key) { 1379 return false;
1380 } 1381 key = encodeURI(key); 1382 var domain = this.domain; 1383 if (domain.slice(domain.length - 1) !== '/') { 1384 domain = domain + '/'; 1385 } 1386 return domain + key; 1387 }; 1388 1389 /** 1390 * invoke the imageView2 api of Qiniu 1391 * @param {Object} api params 1392 * @param {String} key of file 1393 * @return {String} url of processed image 1394 */ 1395 this.imageView2 = function(op, key) { 1396 1397 if (!/^\d$/.test(op.mode)) { 1398 return false; 1399 } 1400 1401 var mode = op.mode, 1402 w = op.w || '', 1403 h = op.h || '', 1404 q = op.q || '', 1405 format = op.format || ''; 1406 1407 if (!w && !h) { 1408 return false; 1409 } 1410 1411 var imageUrl = 'imageView2/' + mode; 1412 imageUrl += w ? '/w/' + w : ''; 1413 imageUrl += h ? '/h/' + h : ''; 1414 imageUrl += q ? '/q/' + q : ''; 1415 imageUrl += format ? '/format/' + format : ''; 1416 if (key) { 1417 imageUrl = this.getUrl(key) + '?' + imageUrl; 1418 } 1419 return imageUrl; 1420 }; 1421 1422 /** 1423 * invoke the imageMogr2 api of Qiniu 1424 * @param {Object} api params 1425 * @param {String} key of file 1426 * @return {String} url of processed image 1427 */ 1428 this.imageMogr2 = function(op, key) { 1429 var auto_orient = op['auto-orient'] || '', 1430 thumbnail = op.thumbnail || '', 1431 strip = op.strip || '', 1432 gravity = op.gravity || '', 1433 crop = op.crop || '', 1434 quality = op.quality || '', 1435 rotate = op.rotate || '', 1436 format = op.format || '', 1437 blur = op.blur || ''; 1438 //Todo check option 1439 1440 var imageUrl = 'imageMogr2'; 1441 1442 imageUrl += auto_orient ? '/auto-orient' : ''; 1443 imageUrl += thumbnail ? '/thumbnail/' + thumbnail : ''; 1444 imageUrl += strip ? '/strip' : ''; 1445 imageUrl += gravity ? '/gravity/' + gravity : ''; 1446 imageUrl += quality ? '/quality/' + quality : ''; 1447 imageUrl += crop ? '/crop/' + crop : ''; 1448 imageUrl += rotate ? '/rotate/' + rotate : ''; 1449 imageUrl += format ? '/format/' + format : ''; 1450 imageUrl += blur ? '/blur/' + blur : ''; 1451 1452 if (key) { 1453 imageUrl = this.getUrl(key) + '?' + imageUrl; 1454 } 1455 return imageUrl; 1456 }; 1457 1458 /** 1459 * invoke the watermark api of Qiniu 1460 * @param {Object} api params 1461 * @param {String} key of file 1462 * @return {String} url of processed image 1463 */ 1464 this.watermark = function(op, key) { 1465 var mode = op.mode; 1466 if (!mode) { 1467 return false; 1468 } 1469 1470 var imageUrl = 'watermark/' + mode; 1471 1472 if (mode === 1) { 1473 var image = op.image || ''; 1474 if (!image) { 1475 return false; 1476 } 1477 imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : ''; 1478 } else if (mode === 2) { 1479 var text = op.text ? op.text : '', 1480 font = op.font ? op.font : '', 1481 fontsize = op.fontsize ? op.fontsize : '', 1482 fill = op.fill ? op.fill : ''; 1483 if (!text) { 1484 return false; 1485 } 1486 imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : ''; 1487 imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : ''; 1488 imageUrl += fontsize ? '/fontsize/' + fontsize : ''; 1489 imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : ''; 1490 } else { 1491 // Todo mode3 1492 return false; 1493 } 1494 1495 var dissolve = op.dissolve || '', 1496 gravity = op.gravity || '', 1497 dx = op.dx || '', 1498 dy = op.dy || ''; 1499 1500 imageUrl += dissolve ? '/dissolve/' + dissolve : ''; 1501 imageUrl += gravity ? '/gravity/' + gravity : ''; 1502 imageUrl += dx ? '/dx/' + dx : ''; 1503 imageUrl += dy ? '/dy/' + dy : ''; 1504 1505 if (key) { 1506 imageUrl = this.getUrl(key) + '?' + imageUrl; 1507 } 1508 return imageUrl; 1509 }; 1510 1511 /** 1512 * invoke the imageInfo api of Qiniu 1513 * @param {String} key of file 1514 * @return {Object} image info 1515 */ 1516 this.imageInfo = function(key) { 1517 if (!key) { 1518 return false;
1519 } 1520 var url = this.getUrl(key) + '?imageInfo'; 1521 var xhr = this.createAjax(); 1522 var info; 1523 var that = this; 1524 xhr.open('GET', url, false); 1525 xhr.onreadystatechange = function() { 1526 if (xhr.readyState === 4 && xhr.status === 200) { 1527 info = that.parseJSON(xhr.responseText); 1528 } 1529 }; 1530 xhr.send(); 1531 return info; 1532 }; 1533 1534 /** 1535 * invoke the exif api of Qiniu 1536 * @param {String} key of file 1537 * @return {Object} image exif 1538 */ 1539 this.exif = function(key) { 1540 if (!key) { 1541 return false; 1542 } 1543 var url = this.getUrl(key) + '?exif'; 1544 var xhr = this.createAjax(); 1545 var info; 1546 var that = this; 1547 xhr.open('GET', url, false); 1548 xhr.onreadystatechange = function() { 1549 if (xhr.readyState === 4 && xhr.status === 200) { 1550 info = that.parseJSON(xhr.responseText); 1551 } 1552 }; 1553 xhr.send(); 1554 return info; 1555 }; 1556 1557 /** 1558 * invoke the exif or imageInfo api of Qiniu 1559 * according with type param 1560 * @param {String} ['exif'|'imageInfo']type of info 1561 * @param {String} key of file 1562 * @return {Object} image exif or info 1563 */ 1564 this.get = function(type, key) { 1565 if (!key || !type) { 1566 return false; 1567 } 1568 if (type === 'exif') { 1569 return this.exif(key); 1570 } else if (type === 'imageInfo') { 1571 return this.imageInfo(key); 1572 } 1573 return false; 1574 }; 1575 1576 /** 1577 * invoke api of Qiniu like a pipeline 1578 * @param {Array of Object} params of a series api call 1579 * each object in array is options of api which name is set as 'fop' property 1580 * each api's output will be next api's input 1581 * @param {String} key of file 1582 * @return {String|Boolean} url of processed image 1583 */ 1584 this.pipeline = function(arr, key) { 1585 var isArray = Object.prototype.toString.call(arr) === '[object Array]'; 1586 var option, errOp, imageUrl = ''; 1587 if (isArray) { 1588 for (var i = 0, len = arr.length; i < len; i++) { 1589 option = arr[i]; 1590 if (!option.fop) { 1591 return false; 1592 } 1593 switch (option.fop) { 1594 case 'watermark': 1595 imageUrl += this.watermark(option) + '|'; 1596 break; 1597 case 'imageView2': 1598 imageUrl += this.imageView2(option) + '|'; 1599 break; 1600 case 'imageMogr2': 1601 imageUrl += this.imageMogr2(option) + '|'; 1602 break; 1603 default: 1604 errOp = true; 1605 break; 1606 } 1607 if (errOp) { 1608 return false; 1609 } 1610 } 1611 if (key) { 1612 imageUrl = this.getUrl(key) + '?' + imageUrl; 1613 var length = imageUrl.length; 1614 if (imageUrl.slice(length - 1) === '|') { 1615 imageUrl = imageUrl.slice(0, length - 1); 1616 } 1617 } 1618 return imageUrl; 1619 } 1620 return false; 1621 }; 1622} 1623 1624var Qiniu = new QiniuJsSDK(); 1625 1626global.Qiniu = Qiniu; 1627 1628global.QiniuJsSDK = QiniuJsSDK; 1629 1630})( window );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.