diff --git a/.gitignore b/.gitignore index d1f29c32..35c68c80 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules bower_components demo/config.js +deploy.sh diff --git a/.travis.yml b/.travis.yml index f6be0ee3..9a859c23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,19 @@ language: node_js node_js: - - "0.12" +- '4' cache: directories: - - node_modules - - bower_components + - node_modules + - bower_components install: - - npm install - - bower install +- npm install +- bower install script: - - grunt \ No newline at end of file +- grunt +deploy: + provider: npm + email: sdk@qiniu.com + api_key: + secure: d06l6wsuv/YFeY3E0TRGemGjtQ7LZdN9WxiFnseT2exTFkDgMw9EV2uOUWllXNrbxXUY+2ZpDswtYrgZ4exQGfKKQRGFy6/g8oxPgyylVwBz98vlie8NvXXmANoyt7mwQwjninf5/XUiY3cwn9YCaU1jyxcayFdQTex05T++qvc= + on: + tags: true diff --git a/README.md b/README.md index 0c57dbcd..cdd2edeb 100644 --- a/README.md +++ b/README.md @@ -14,18 +14,26 @@ Qiniu-JavaScript-SDK Qiniu-JavaScript-SDK (下文简称为 JS-SDK)适用于 IE8+、Chrome、Firefox、Safari 等浏览器,基于七牛云存储官方 API 构建,其中上传功能基于 [Plupload](http://www.plupload.com/) 插件封装。开发者基于 JS-SDK 可以方便的从浏览器端上传文件至七牛云存储,并对上传成功后的图片进行丰富的数据处理操作。 +不考虑兼容性的情况下,如手机端,建议直接使用 Formdata 结合七牛[表单上传](https://developer.qiniu.com/kodo/manual/form-upload)的方式上传文件。 + +[ Formdata 上传 demo ](http://jssdk.demo.qiniu.io/formdata) + +![ Formdata ](http://oky1vwhqm.bkt.clouddn.com/1486368013.png) + Qiniu-JavaScript-SDK 为客户端 SDK,没有包含 token 生成实现,为了安全,token 建议通过网络从服务端获取,具体生成代码可以参考以下服务端 SDK 的文档。 -- [Java](http://developer.qiniu.com/code/v7/sdk/android.html) -- [PHP](http://developer.qiniu.com/code/v7/sdk/php.html) -- [Python](http://developer.qiniu.com/code/v7/sdk/python.html) -- [Ruby](http://developer.qiniu.com/code/v6/sdk/ruby.html) -- [Go](http://developer.qiniu.com/code/v7/sdk/go.html) -- [Node.js](http://developer.qiniu.com/code/v6/sdk/nodejs.html) -- [C#](http://developer.qiniu.com/code/v6/sdk/csharp.html) -- [C/C++](http://developer.qiniu.com/code/v6/sdk/cpp.html) +- [Android](https://developer.qiniu.com/kodo/sdk/android) +- [Java](https://developer.qiniu.com/kodo/sdk/java) +- [PHP](https://developer.qiniu.com/kodo/sdk/php) +- [Python](https://developer.qiniu.com/kodo/sdk/python) +- [Ruby](https://developer.qiniu.com/kodo/sdk/ruby) +- [Go](https://developer.qiniu.com/kodo/sdk/go) +- [Node.js](https://developer.qiniu.com/kodo/sdk/nodejs) +- [C#](https://developer.qiniu.com/kodo/sdk/csharp) +- [C/C++](https://developer.qiniu.com/kodo/sdk/cpp) +- [Objective-C](https://developer.qiniu.com/kodo/sdk/objc) -Qiniu-JavaScript-SDK 的示例 Demo 中的服务器端部分是基于[ Node.js 服务器端 SDK ](http://developer.qiniu.com/code/v6/sdk/nodejs.html) 开发的。 +Qiniu-JavaScript-SDK 的示例 Demo 中的服务器端部分是基于[ Node.js 服务器端 SDK ](https://developer.qiniu.com/kodo/sdk/nodejs) 开发的。 +
+ + + + + + + + + + + +
+ + + + + +
+
+
+ + + \ No newline at end of file diff --git a/demo/views/index.html b/demo/views/index.html index 769990d4..9da749c9 100644 --- a/demo/views/index.html +++ b/demo/views/index.html @@ -2,7 +2,7 @@ - 七牛云存储 - JavaScript SDK + 七牛云 - JavaScript SDK @@ -28,6 +28,7 @@ @@ -136,7 +137,7 @@ }, 'FileUploaded': function(up, file, info) { // 每个文件上传成功后,处理相关的事情 - // 其中 info 是文件上传成功后,服务端返回的json,形式如 + // 其中 info.response 是文件上传成功后,服务端返回的json,形式如 // { // "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98", // "key": "gogopher.jpg" @@ -144,7 +145,7 @@ // 参考http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html // var domain = up.getOption('domain'); - // var res = parseJSON(info); + // var res = parseJSON(info.response); // var sourceLink = domain + res.key; 获取上传成功后的文件的Url }, 'Error': function(up, err, errTip) { @@ -236,7 +237,7 @@ }, 'FileUploaded': function(up, file, info) { // 每个文件上传成功后,处理相关的事情 - // 其中 info 是文件上传成功后,服务端返回的json,形式如 + // 其中 info.response 是文件上传成功后,服务端返回的json,形式如 // { // "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98", // "key": "gogopher.jpg" @@ -244,7 +245,7 @@ // 参考http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html // var domain = up.getOption('domain'); - // var res = parseJSON(info); + // var res = parseJSON(info.response); // var sourceLink = domain + res.key; 获取上传成功后的文件的Url }, 'Error': function(up, err, errTip) { diff --git a/demo/views/multiple.html b/demo/views/multiple.html index c4e78ab7..04988c19 100644 --- a/demo/views/multiple.html +++ b/demo/views/multiple.html @@ -2,7 +2,7 @@ - 七牛云存储 - JavaScript SDK + 七牛云 - JavaScript SDK diff --git a/demo/views/performance.html b/demo/views/performance.html new file mode 100644 index 00000000..3f94fa31 --- /dev/null +++ b/demo/views/performance.html @@ -0,0 +1,32 @@ + + + + + + + 七牛云 - 上传速度检测 + + +
+
七牛云上传速度检测
+ +
+ + +
+ + + + 开始检测 + +
{{loadMessage}}
+
+ + +
+
+ + + + + diff --git a/dist/qiniu.js b/dist/qiniu.js index ecbc147e..1269a861 100644 --- a/dist/qiniu.js +++ b/dist/qiniu.js @@ -1,1626 +1,1829 @@ /*! - * qiniu-js-sdk v1.0.17.1 + * qiniu-js-sdk v1.0.21 * * Copyright 2015 by Qiniu * Released under GPL V2 License. * * GitHub: http://github.com/qiniu/js-sdk * - * Date: 2016-10-13 -*/ + * Date: 2017-8-10 + */ -/*global plupload ,mOxie*/ +/*global plupload ,moxie*/ /*global ActiveXObject */ /*exported Qiniu */ /*exported QiniuJsSDK */ -;(function( global ){ +;(function (global) { -/** - * Creates new cookie or removes cookie with negative expiration - * @param key The key or identifier for the store - * @param value Contents of the store - * @param exp Expiration - creation defaults to 30 days - */ -function createCookie(key, value, exp) { - var date = new Date(); - date.setTime(date.getTime() + (exp * 24 * 60 * 60 * 1000)); - var expires = "; expires=" + date.toGMTString(); - document.cookie = key + "=" + value + expires + "; path=/"; -} - -/** - * Returns contents of cookie - * @param key The key or identifier for the store - */ -function readCookie(key) { - var nameEQ = key + "="; - var ca = document.cookie.split(';'); - for (var i = 0, max = ca.length; i < max; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1, c.length); - } - if (c.indexOf(nameEQ) === 0) { - return c.substring(nameEQ.length, c.length); - } + /** + * Creates new cookie or removes cookie with negative expiration + * @param key The key or identifier for the store + * @param value Contents of the store + * @param exp Expiration - creation defaults to 30 days + */ + function createCookie(key, value, exp) { + var date = new Date(); + date.setTime(date.getTime() + (exp * 24 * 60 * 60 * 1000)); + var expires = "; expires=" + date.toGMTString(); + document.cookie = key + "=" + value + expires + "; path=/"; } - return null; -} - -// if current browser is not support localStorage -// use cookie to make a polyfill -if ( !window.localStorage ) { - window.localStorage = { - setItem: function (key, value) { - createCookie(key, value, 30); - }, - getItem: function (key) { - return readCookie(key); - }, - removeItem: function (key) { - createCookie(key, '', -1); - } - }; -} - -function QiniuJsSDK() { - - var that = this; /** - * detect IE version - * if current browser is not IE - * it will return false - * else - * it will return version of current IE browser - * @return {Number|Boolean} IE version or false + * Returns contents of cookie + * @param key The key or identifier for the store */ - this.detectIEVersion = function() { - var v = 4, - div = document.createElement('div'), - all = div.getElementsByTagName('i'); - while ( - div.innerHTML = '', - all[0] - ) { - v++; - } - return v > 4 ? v : false; - }; - - var logger = { - MUTE: 0, - FATA: 1, - ERROR: 2, - WARN: 3, - INFO: 4, - DEBUG: 5, - TRACE: 6, - level: 0 - }; - - function log(type, args){ - var header = "[qiniu-js-sdk]["+type+"]"; - var msg = header; - for (var i = 0; i < args.length; i++) { - if (typeof args[i] === "string") { - msg += " " + args[i]; - } else { - msg += " " + that.stringifyJSON(args[i]); + function readCookie(key) { + var nameEQ = key + "="; + var ca = document.cookie.split(';'); + for (var i = 0, max = ca.length; i < max; i++) { + var c = ca[i]; + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) === 0) { + return c.substring(nameEQ.length, c.length); } } - if (that.detectIEVersion()) { - // http://stackoverflow.com/questions/5538972/console-log-apply-not-working-in-ie9 - //var log = Function.prototype.bind.call(console.log, console); - //log.apply(console, args); - console.log(msg); - }else{ - args.unshift(header); - console.log.apply(console, args); - } - if (document.getElementById('qiniu-js-sdk-log')) { - document.getElementById('qiniu-js-sdk-log').innerHTML += '

'+msg+'

'; - } + return null; } - function makeLogFunc(code){ - var func = code.toLowerCase(); - logger[func] = function(){ - // logger[func].history = logger[func].history || []; - // logger[func].history.push(arguments); - if(window.console && window.console.log && logger.level>=logger[code]){ - var args = Array.prototype.slice.call(arguments); - log(func,args); + // if current browser is not support localStorage + // use cookie to make a polyfill + if (!window.localStorage) { + window.localStorage = { + setItem: function (key, value) { + createCookie(key, value, 30); + }, + getItem: function (key) { + return readCookie(key); + }, + removeItem: function (key) { + createCookie(key, '', -1); } }; } - for (var property in logger){ - if (logger.hasOwnProperty(property) && (typeof logger[property]) === "number" && !logger.hasOwnProperty(property.toLowerCase())) { - makeLogFunc(property); - } - } - - - var qiniuUploadUrl; - if (window.location.protocol === 'https:') { - qiniuUploadUrl = 'https://up.qbox.me'; - } else { - qiniuUploadUrl = 'http://upload.qiniu.com'; - } - - /** - * qiniu upload urls - * 'qiniuUploadUrls' is used to change target when current url is not avaliable - * @type {Array} - */ - var qiniuUploadUrls = [ - "http://upload.qiniu.com", - "http://up.qiniu.com" - ]; - - var qiniuUpHosts = { - "http": [ - "http://upload.qiniu.com", - "http://up.qiniu.com" - ], - "https": [ - "https://up.qbox.me" - ] - }; - - var changeUrlTimes = 0; - - /** - * reset upload url - * if current page protocal is https - * it will always return 'https://up.qbox.me' - * else - * it will set 'qiniuUploadUrl' value with 'qiniuUploadUrls' looply - */ - this.resetUploadUrl = function(){ - var hosts = window.location.protocol === 'https:' ? qiniuUpHosts.https : qiniuUpHosts.http; - var i = changeUrlTimes % hosts.length; - qiniuUploadUrl = hosts[i]; - changeUrlTimes++; - logger.debug('resetUploadUrl: '+qiniuUploadUrl); - }; - - // this.resetUploadUrl(); - + function QiniuJsSDK() { - /** - * is image - * @param {String} url of a file - * @return {Boolean} file is a image or not - */ - this.isImage = function(url) { - url = url.split(/[?#]/)[0]; - return (/\.(png|jpg|jpeg|gif|bmp)$/i).test(url); - }; + var that = this; - /** - * get file extension - * @param {String} filename - * @return {String} file extension - * @example - * input: test.txt - * output: txt - */ - this.getFileExtension = function(filename) { - var tempArr = filename.split("."); - var ext; - if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) { - ext = ""; - } else { - ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case - } - return ext; - }; + /** + * detect IE version + * if current browser is not IE + * it will return false + * else + * it will return version of current IE browser + * @return {Number|Boolean} IE version or false + */ + this.detectIEVersion = function () { + var v = 4, + div = document.createElement('div'), + all = div.getElementsByTagName('i'); + while ( + div.innerHTML = '', + all[0] + ) { + v++; + } + return v > 4 ? v : false; + }; - /** - * encode string by utf8 - * @param {String} string to encode - * @return {String} encoded string - */ - this.utf8_encode = function(argString) { - // http://kevin.vanzonneveld.net - // + original by: Webtoolkit.info (http://www.webtoolkit.info/) - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + improved by: sowberry - // + tweaked by: Jack - // + bugfixed by: Onno Marsman - // + improved by: Yves Sucaet - // + bugfixed by: Onno Marsman - // + bugfixed by: Ulrich - // + bugfixed by: Rafal Kukawski - // + improved by: kirilloid - // + bugfixed by: kirilloid - // * example 1: this.utf8_encode('Kevin van Zonneveld'); - // * returns 1: 'Kevin van Zonneveld' - - if (argString === null || typeof argString === 'undefined') { - return ''; - } + var logger = { + MUTE: 0, + FATA: 1, + ERROR: 2, + WARN: 3, + INFO: 4, + DEBUG: 5, + TRACE: 6, + level: 0 + }; - var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n'); - var utftext = '', - start, end, stringl = 0; - - start = end = 0; - stringl = string.length; - for (var n = 0; n < stringl; n++) { - var c1 = string.charCodeAt(n); - var enc = null; - - if (c1 < 128) { - end++; - } else if (c1 > 127 && c1 < 2048) { - enc = String.fromCharCode( - (c1 >> 6) | 192, (c1 & 63) | 128 - ); - } else if (c1 & 0xF800 ^ 0xD800 > 0) { - enc = String.fromCharCode( - (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 - ); - } else { // surrogate pairs - if (c1 & 0xFC00 ^ 0xD800 > 0) { - throw new RangeError('Unmatched trail surrogate at ' + n); - } - var c2 = string.charCodeAt(++n); - if (c2 & 0xFC00 ^ 0xDC00 > 0) { - throw new RangeError('Unmatched lead surrogate at ' + (n - 1)); + function log(type, args) { + var header = "[qiniu-js-sdk][" + type + "]"; + var msg = header; + for (var i = 0; i < args.length; i++) { + if (typeof args[i] === "string") { + msg += " " + args[i]; + } else { + msg += " " + that.stringifyJSON(args[i]); } - c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; - enc = String.fromCharCode( - (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 - ); } - if (enc !== null) { - if (end > start) { - utftext += string.slice(start, end); - } - utftext += enc; - start = end = n + 1; + if (that.detectIEVersion()) { + // http://stackoverflow.com/questions/5538972/console-log-apply-not-working-in-ie9 + //var log = Function.prototype.bind.call(console.log, console); + //log.apply(console, args); + console.log(msg); + } else { + args.unshift(header); + console.log.apply(console, args); + } + if (document.getElementById('qiniu-js-sdk-log')) { + document.getElementById('qiniu-js-sdk-log').innerHTML += '

' + msg + '

'; } } - if (end > start) { - utftext += string.slice(start, stringl); - } - - return utftext; - }; - - this.base64_decode = function (data) { - // http://kevin.vanzonneveld.net - // + original by: Tyler Akins (http://rumkin.com) - // + improved by: Thunder.m - // + input by: Aman Gupta - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + bugfixed by: Onno Marsman - // + bugfixed by: Pellentesque Malesuada - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + input by: Brett Zamir (http://brett-zamir.me) - // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); - // * returns 1: 'Kevin van Zonneveld' - // mozilla has this native - // - but breaks in 2.0.0.12! - //if (typeof this.window['atob'] == 'function') { - // return atob(data); - //} - var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, - ac = 0, - dec = "", - tmp_arr = []; - - if (!data) { - return data; + function makeLogFunc(code) { + var func = code.toLowerCase(); + logger[func] = function () { + // logger[func].history = logger[func].history || []; + // logger[func].history.push(arguments); + if (window.console && window.console.log && logger.level >= logger[code]) { + var args = Array.prototype.slice.call(arguments); + log(func, args); + } + }; } - data += ''; - - do { // unpack four hexets into three octets using index points in b64 - h1 = b64.indexOf(data.charAt(i++)); - h2 = b64.indexOf(data.charAt(i++)); - h3 = b64.indexOf(data.charAt(i++)); - h4 = b64.indexOf(data.charAt(i++)); - - bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; - - o1 = bits >> 16 & 0xff; - o2 = bits >> 8 & 0xff; - o3 = bits & 0xff; - - if (h3 === 64) { - tmp_arr[ac++] = String.fromCharCode(o1); - } else if (h4 === 64) { - tmp_arr[ac++] = String.fromCharCode(o1, o2); - } else { - tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); + for (var property in logger) { + if (logger.hasOwnProperty(property) && (typeof logger[property]) === "number" && !logger.hasOwnProperty(property.toLowerCase())) { + makeLogFunc(property); } - } while (i < data.length); - - dec = tmp_arr.join(''); - - return dec; - }; - - /** - * encode data by base64 - * @param {String} data to encode - * @return {String} encoded data - */ - this.base64_encode = function(data) { - // http://kevin.vanzonneveld.net - // + original by: Tyler Akins (http://rumkin.com) - // + improved by: Bayron Guevara - // + improved by: Thunder.m - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + bugfixed by: Pellentesque Malesuada - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // - depends on: this.utf8_encode - // * example 1: this.base64_encode('Kevin van Zonneveld'); - // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' - // mozilla has this native - // - but breaks in 2.0.0.12! - //if (typeof this.window['atob'] == 'function') { - // return atob(data); - //} - var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, - ac = 0, - enc = '', - tmp_arr = []; - - if (!data) { - return data; - } - - data = this.utf8_encode(data + ''); - - do { // pack three octets into four hexets - o1 = data.charCodeAt(i++); - o2 = data.charCodeAt(i++); - o3 = data.charCodeAt(i++); - - bits = o1 << 16 | o2 << 8 | o3; - - h1 = bits >> 18 & 0x3f; - h2 = bits >> 12 & 0x3f; - h3 = bits >> 6 & 0x3f; - h4 = bits & 0x3f; - - // use hexets to index into b64, and append result to encoded string - tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); - } while (i < data.length); - - enc = tmp_arr.join(''); - - switch (data.length % 3) { - case 1: - enc = enc.slice(0, -2) + '=='; - break; - case 2: - enc = enc.slice(0, -1) + '='; - break; } - return enc; - }; - - /** - * encode string in url by base64 - * @param {String} string in url - * @return {String} encoded string - */ - this.URLSafeBase64Encode = function(v) { - v = this.base64_encode(v); - return v.replace(/\//g, '_').replace(/\+/g, '-'); - }; - - this.URLSafeBase64Decode = function(v) { - v = v.replace(/_/g, '/').replace(/-/g, '+'); - return this.base64_decode(v); - }; - // TODO: use mOxie - /** - * craete object used to AJAX - * @return {Object} - */ - this.createAjax = function(argument) { - var xmlhttp = {}; - if (window.XMLHttpRequest) { - xmlhttp = new XMLHttpRequest(); + var qiniuUploadUrl; + if (window.location.protocol === 'https:') { + qiniuUploadUrl = 'https://up.qbox.me'; } else { - xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); + qiniuUploadUrl = 'http://upload.qiniu.com'; } - return xmlhttp; - }; - // TODO: enhance IE compatibility - /** - * parse json string to javascript object - * @param {String} json string - * @return {Object} object - */ - this.parseJSON = function(data) { - // Attempt to parse using the native JSON parser first - if (window.JSON && window.JSON.parse) { - return window.JSON.parse(data); - } + /** + * qiniu upload urls + * 'qiniuUploadUrls' is used to change target when current url is not avaliable + * @type {Array} + */ + var qiniuUploadUrls = [ + "http://upload.qiniu.com", + "http://up.qiniu.com" + ]; + + var qiniuUpHosts = { + "http": [ + "http://upload.qiniu.com", + "http://up.qiniu.com" + ], + "https": [ + "https://up.qbox.me" + ] + }; - //var rx_one = /^[\],:{}\s]*$/, - // rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, - // rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, - // rx_four = /(?:^|:|,)(?:\s*\[)+/g, - var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var changeUrlTimes = 0; - //var json; + function StatisticsLogger() { + // api to collect upload logs + var qiniuCollectUploadLogUrl = "https://uplog.qbox.me/log/3"; - var text = String(data); - rx_dangerous.lastIndex = 0; - if(rx_dangerous.test(text)){ - text = text.replace(rx_dangerous, function(a){ - return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } + /** + * { log: string, status: number }[] status: 0 待处理, 1 正在发送, 2 发送完毕 + */ + var queue = []; + var TaskStatus = { + waiting: 0, + processing: 1, + finished: 2 + }; - // todo 使用一下判断,增加安全性 - //if ( - // rx_one.test( - // text - // .replace(rx_two, '@') - // .replace(rx_three, ']') - // .replace(rx_four, '') - // ) - //) { - // return eval('(' + text + ')'); - //} - - return eval('('+text+')'); - }; + /** + * send logs to statistics server + * + * @param {number} code status code + * @param {string} req_id request id + * @param {string} host + * @param {string} remote_ip + * @param {string} port + * @param {string} duration + * @param {string} up_time + * @param {number} bytes_sent uploaded size (bytes) + * @param {string} up_type js sdk runtime: html5, html4, flash + * @param {number} file_size file total size (bytes) + */ + this.log = function (code, req_id, host, remote_ip, port, duration, up_time, bytes_sent, up_type, file_size) { + var log = Array.prototype.join.call(arguments, ','); + queue.push({ + log: log, + status: TaskStatus.waiting + }); + logger.debug("[STATISTICS] send log to statistics server", log); + }; - /** - * parse javascript object to json string - * @param {Object} object - * @return {String} json string - */ - this.stringifyJSON = function(obj) { - // Attempt to parse using the native JSON parser first - if (window.JSON && window.JSON.stringify) { - return window.JSON.stringify(obj); - } - switch (typeof (obj)) { - case 'string': - return '"' + obj.replace(/(["\\])/g, '\\$1') + '"'; - case 'array': - return '[' + obj.map(that.stringifyJSON).join(',') + ']'; - case 'object': - if (obj instanceof Array) { - var strArr = []; - var len = obj.length; - for (var i = 0; i < len; i++) { - strArr.push(that.stringifyJSON(obj[i])); + function tick() { + var unFinishedTasks = []; + for (var i = 0; i < queue.length; i++) { + if (queue[i].status !== TaskStatus.finished) { + unFinishedTasks.push(queue[i]); } - return '[' + strArr.join(',') + ']'; - } else if (obj === null) { - return 'null'; - } else { - var string = []; - for (var property in obj) { - if (obj.hasOwnProperty(property)) { - string.push(that.stringifyJSON(property) + ':' + that.stringifyJSON(obj[property])); - } + if (queue[i].status === TaskStatus.waiting) { + send(queue[i]); } - return '{' + string.join(',') + '}'; } - break; - case 'number': - return obj; - case false: - return obj; - case 'boolean': - return obj; - } - }; + queue = unFinishedTasks; + } - /** - * trim space beside text - * @param {String} untrimed string - * @return {String} trimed string - */ - this.trim = function(text) { - return text === null ? "" : text.replace(/^\s+|\s+$/g, ''); - }; + function send(task) { + task.status = TaskStatus.processing; + var ajax = that.createAjax(); + ajax.open('POST', qiniuCollectUploadLogUrl, true); + ajax.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + ajax.setRequestHeader('Authorization', 'UpToken ' + that.token); + ajax.onreadystatechange = function () { + if (ajax.readyState === 4) { + if (ajax.status === 200) { + logger.debug("[STATISTICS] successfully report log to server"); + task.status = TaskStatus.finished; + } else { + logger.debug("[STATISTICS] report log to server failed"); + task.status = TaskStatus.waiting; + } + } + }; + ajax.send(task.log); + } - /** - * create a uploader by QiniuJsSDK - * @param {object} options to create a new uploader - * @return {object} uploader - */ - this.uploader = function(op) { + // start a timer to report + setInterval(tick, 1000); + } + var statisticsLogger = new StatisticsLogger(); + var ExtraErrors = { + ZeroSizeFile: -6, + InvalidToken: -5, + InvalidArgument: -4, + InvalidFile: -3, + Cancelled: -2, + NetworkError: -1, + UnknownError: 0, + TimedOut: -1001, + UnknownHost: -1003, + CannotConnectToHost: -1004, + NetworkConnectionLost: -1005 + }; - /********** inner function define start **********/ + /** + * reset upload url + * if current page protocal is https + * it will always return 'https://up.qbox.me' + * else + * it will set 'qiniuUploadUrl' value with 'qiniuUploadUrls' looply + */ + this.resetUploadUrl = function () { + var hosts = window.location.protocol === 'https:' ? qiniuUpHosts.https : qiniuUpHosts.http; + var i = changeUrlTimes % hosts.length; + qiniuUploadUrl = hosts[i]; + changeUrlTimes++; + logger.debug('resetUploadUrl: ' + qiniuUploadUrl); + }; - // according the different condition to reset chunk size - // and the upload strategy according with the chunk size - // when chunk size is zero will cause to direct upload - // see the statement binded on 'BeforeUpload' event - var reset_chunk_size = function() { - var ie = that.detectIEVersion(); - var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size; - // case Safari 5、Windows 7、iOS 7 set isSpecialSafari to true - 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"); - // case IE 9-,chunk_size is not empty and flash is included in runtimes - // set op.chunk_size to zero - //if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { - if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { - // link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not - // when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) . - op.chunk_size = 0; - } else if (isSpecialSafari) { - // win7 safari / iOS7 safari have bug when in chunk upload mode - // reset chunk_size to 0 - // disable chunk in special version safari - op.chunk_size = 0; - } else { - BLOCK_BITS = 20; - MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M + // this.resetUploadUrl(); - chunk_size = plupload.parseSize(op.chunk_size); - if (chunk_size > MAX_CHUNK_SIZE) { - op.chunk_size = MAX_CHUNK_SIZE; - } - // qiniu service max_chunk_size is 4m - // reset chunk_size to max_chunk_size(4m) when chunk_size > 4m - } - // if op.chunk_size set 0 will be cause to direct upload - }; - var getHosts = function(hosts) { - var result = []; - for (var i = 0; i < hosts.length; i++) { - var host = hosts[i]; - if (host.indexOf('-H') === 0) { - result.push(host.split(' ')[2]); - } else { - result.push(host); - } - } - return result; + /** + * is image + * @param {String} url of a file + * @return {Boolean} file is a image or not + */ + this.isImage = function (url) { + url = url.split(/[?#]/)[0]; + return (/\.(png|jpg|jpeg|gif|bmp)$/i).test(url); }; - var getPutPolicy = function (uptoken) { - var segments = uptoken.split(":"); - var ak = segments[0]; - var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); - putPolicy.ak = ak; - if (putPolicy.scope.indexOf(":") >= 0) { - putPolicy.bucket = putPolicy.scope.split(":")[0]; - putPolicy.key = putPolicy.scope.split(":")[1]; + /** + * get file extension + * @param {String} filename + * @return {String} file extension + * @example + * input: test.txt + * output: txt + */ + this.getFileExtension = function (filename) { + var tempArr = filename.split("."); + var ext; + if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) { + ext = ""; } else { - putPolicy.bucket = putPolicy.scope; + ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case } - return putPolicy; + return ext; }; - var getUpHosts = function(uptoken) { - var putPolicy = getPutPolicy(uptoken); - // var uphosts_url = "//uc.qbox.me/v1/query?ak="+ak+"&bucket="+putPolicy.scope; - // IE 9- is not support protocal relative url - var uphosts_url = window.location.protocol + "//uc.qbox.me/v1/query?ak=" + putPolicy.ak + "&bucket=" + putPolicy.bucket; - logger.debug("putPolicy: ", putPolicy); - logger.debug("get uphosts from: ", uphosts_url); - var ie = that.detectIEVersion(); - var ajax; - if (ie && ie <= 9) { - ajax = new mOxie.XMLHttpRequest(); - mOxie.Env.swf_url = op.flash_swf_url; - }else{ - ajax = that.createAjax(); + /** + * encode string by utf8 + * @param {String} string to encode + * @return {String} encoded string + */ + this.utf8_encode = function (argString) { + // http://kevin.vanzonneveld.net + // + original by: Webtoolkit.info (http://www.webtoolkit.info/) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: sowberry + // + tweaked by: Jack + // + bugfixed by: Onno Marsman + // + improved by: Yves Sucaet + // + bugfixed by: Onno Marsman + // + bugfixed by: Ulrich + // + bugfixed by: Rafal Kukawski + // + improved by: kirilloid + // + bugfixed by: kirilloid + // * example 1: this.utf8_encode('Kevin van Zonneveld'); + // * returns 1: 'Kevin van Zonneveld' + + if (argString === null || typeof argString === 'undefined') { + return ''; } - ajax.open('GET', uphosts_url, true); - var onreadystatechange = function(){ - logger.debug("ajax.readyState: ", ajax.readyState); - if (ajax.readyState === 4) { - logger.debug("ajax.status: ", ajax.status); - if (ajax.status < 400) { - var res = that.parseJSON(ajax.responseText); - qiniuUpHosts.http = getHosts(res.http.up); - qiniuUpHosts.https = getHosts(res.https.up); - logger.debug("get new uphosts: ", qiniuUpHosts); - that.resetUploadUrl(); - } else { - logger.error("get uphosts error: ", ajax.responseText); + + var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + var utftext = '', + start, end, stringl = 0; + + start = end = 0; + stringl = string.length; + for (var n = 0; n < stringl; n++) { + var c1 = string.charCodeAt(n); + var enc = null; + + if (c1 < 128) { + end++; + } else if (c1 > 127 && c1 < 2048) { + enc = String.fromCharCode( + (c1 >> 6) | 192, (c1 & 63) | 128 + ); + } else if (c1 & 0xF800 ^ 0xD800 > 0) { + enc = String.fromCharCode( + (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 + ); + } else { // surrogate pairs + if (c1 & 0xFC00 ^ 0xD800 > 0) { + throw new RangeError('Unmatched trail surrogate at ' + n); + } + var c2 = string.charCodeAt(++n); + if (c2 & 0xFC00 ^ 0xDC00 > 0) { + throw new RangeError('Unmatched lead surrogate at ' + (n - 1)); } + c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; + enc = String.fromCharCode( + (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 + ); + } + if (enc !== null) { + if (end > start) { + utftext += string.slice(start, end); + } + utftext += enc; + start = end = n + 1; } - }; - if (ie && ie <= 9) { - ajax.bind('readystatechange', onreadystatechange); - }else{ - ajax.onreadystatechange = onreadystatechange; } - ajax.send(); - // ajax.send(); - // if (ajax.status < 400) { - // var res = that.parseJSON(ajax.responseText); - // qiniuUpHosts.http = getHosts(res.http.up); - // qiniuUpHosts.https = getHosts(res.https.up); - // logger.debug("get new uphosts: ", qiniuUpHosts); - // that.resetUploadUrl(); - // } else { - // logger.error("get uphosts error: ", ajax.responseText); - // } - return; - }; - var getUptoken = function(file) { - if (!that.token || (op.uptoken_url && that.tokenInfo.isExpired())) { - return getNewUpToken(file); - } else { - return that.token; + if (end > start) { + utftext += string.slice(start, stringl); } + + return utftext; }; - // getNewUptoken maybe called at Init Event or BeforeUpload Event - // case Init Event, the file param of getUptken will be set a null value - // if op.uptoken has value, set uptoken with op.uptoken - // else if op.uptoken_url has value, set uptoken from op.uptoken_url - // else if op.uptoken_func has value, set uptoken by result of op.uptoken_func - var getNewUpToken = function(file) { - if (op.uptoken) { - that.token = op.uptoken; - } else if (op.uptoken_url) { - logger.debug("get uptoken from: ", that.uptoken_url); - // TODO: use mOxie - var ajax = that.createAjax(); - ajax.open('GET', that.uptoken_url, false); - ajax.setRequestHeader("If-Modified-Since", "0"); - // ajax.onreadystatechange = function() { - // if (ajax.readyState === 4 && ajax.status === 200) { - // var res = that.parseJSON(ajax.responseText); - // that.token = res.uptoken; - // } - // }; - ajax.send(); - if (ajax.status === 200) { - var res = that.parseJSON(ajax.responseText); - that.token = res.uptoken; - var segments = that.token.split(":"); - var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); - if (!that.tokenMap) { - that.tokenMap = {}; - } - var getTimestamp = function(time) { - return Math.ceil(time.getTime()/1000); - }; - var serverTime = getTimestamp(new Date(ajax.getResponseHeader("date"))); - var clientTime = getTimestamp(new Date()); - that.tokenInfo = { - serverDelay: clientTime - serverTime, - deadline: putPolicy.deadline, - isExpired: function() { - var leftTime = this.deadline - getTimestamp(new Date()) + this.serverDelay; - return leftTime < 600; - } - }; - logger.debug("get new uptoken: ", that.token); - logger.debug("get token info: ", that.tokenInfo); - } else { - logger.error("get uptoken error: ", ajax.responseText); - } - } else if (op.uptoken_func) { - logger.debug("get uptoken from uptoken_func"); - that.token = op.uptoken_func(file); - logger.debug("get new uptoken: ", that.token); - } else { - logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!"); + this.base64_decode = function (data) { + // http://kevin.vanzonneveld.net + // + original by: Tyler Akins (http://rumkin.com) + // + improved by: Thunder.m + // + input by: Aman Gupta + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + bugfixed by: Pellentesque Malesuada + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); + // * returns 1: 'Kevin van Zonneveld' + // mozilla has this native + // - but breaks in 2.0.0.12! + //if (typeof this.window['atob'] == 'function') { + // return atob(data); + //} + var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, + ac = 0, + dec = "", + tmp_arr = []; + + if (!data) { + return data; } - if (that.token) { - getUpHosts(that.token); - } - return that.token; - }; - // get file key according with the user passed options - var getFileKey = function(up, file, func) { - // WARNING - // When you set the key in putPolicy by "scope": "bucket:key" - // You should understand the risk of override a file in the bucket - // So the code below that automatically get key from uptoken has been commented - // var putPolicy = getPutPolicy(that.token) - // if (putPolicy.key) { - // logger.debug("key is defined in putPolicy.scope: ", putPolicy.key) - // return putPolicy.key - // } - var key = '', - unique_names = false; - if (!op.save_key) { - unique_names = up.getOption && up.getOption('unique_names'); - unique_names = unique_names || (up.settings && up.settings.unique_names); - if (unique_names) { - var ext = that.getFileExtension(file.name); - key = ext ? file.id + '.' + ext : file.id; - } else if (typeof func === 'function') { - key = func(up, file); + data += ''; + + do { // unpack four hexets into three octets using index points in b64 + h1 = b64.indexOf(data.charAt(i++)); + h2 = b64.indexOf(data.charAt(i++)); + h3 = b64.indexOf(data.charAt(i++)); + h4 = b64.indexOf(data.charAt(i++)); + + bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; + + o1 = bits >> 16 & 0xff; + o2 = bits >> 8 & 0xff; + o3 = bits & 0xff; + + if (h3 === 64) { + tmp_arr[ac++] = String.fromCharCode(o1); + } else if (h4 === 64) { + tmp_arr[ac++] = String.fromCharCode(o1, o2); } else { - key = file.name; + tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); } - } - return key; - }; + } while (i < data.length); - /********** inner function define end **********/ + dec = tmp_arr.join(''); - if (op.log_level) { - logger.level = op.log_level; - } + return dec; + }; - if (!op.domain) { - throw 'domain setting in options is required!'; - } + /** + * encode data by base64 + * @param {String} data to encode + * @return {String} encoded data + */ + this.base64_encode = function (data) { + // http://kevin.vanzonneveld.net + // + original by: Tyler Akins (http://rumkin.com) + // + improved by: Bayron Guevara + // + improved by: Thunder.m + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Pellentesque Malesuada + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // - depends on: this.utf8_encode + // * example 1: this.base64_encode('Kevin van Zonneveld'); + // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' + // mozilla has this native + // - but breaks in 2.0.0.12! + //if (typeof this.window['atob'] == 'function') { + // return atob(data); + //} + var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, + ac = 0, + enc = '', + tmp_arr = []; + + if (!data) { + return data; + } - if (!op.browse_button) { - throw 'browse_button setting in options is required!'; - } + data = this.utf8_encode(data + ''); - if (!op.uptoken && !op.uptoken_url && !op.uptoken_func) { - throw 'one of [uptoken, uptoken_url, uptoken_func] settings in options is required!'; - } + do { // pack three octets into four hexets + o1 = data.charCodeAt(i++); + o2 = data.charCodeAt(i++); + o3 = data.charCodeAt(i++); - logger.debug("init uploader start"); + bits = o1 << 16 | o2 << 8 | o3; - logger.debug("environment: ", mOxie.Env); + h1 = bits >> 18 & 0x3f; + h2 = bits >> 12 & 0x3f; + h3 = bits >> 6 & 0x3f; + h4 = bits & 0x3f; - logger.debug("userAgent: ", navigator.userAgent); + // use hexets to index into b64, and append result to encoded string + tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); + } while (i < data.length); - var option = {}; + enc = tmp_arr.join(''); - // hold the handler from user passed options - var _Error_Handler = op.init && op.init.Error; - var _FileUploaded_Handler = op.init && op.init.FileUploaded; + switch (data.length % 3) { + case 1: + enc = enc.slice(0, -2) + '=='; + break; + case 2: + enc = enc.slice(0, -1) + '='; + break; + } - // replace the handler for intercept - op.init.Error = function() {}; - op.init.FileUploaded = function() {}; + return enc; + }; - that.uptoken_url = op.uptoken_url; - that.token = ''; - that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : ''; - this.domain = op.domain; - // TODO: ctx is global in scope of a uploader instance - // this maybe cause error - var ctx = ''; - var speedCalInfo = { - isResumeUpload: false, - resumeFilesize: 0, - startTime: '', - currentTime: '' + /** + * encode string in url by base64 + * @param {String} string in url + * @return {String} encoded string + */ + this.URLSafeBase64Encode = function (v) { + v = this.base64_encode(v); + return v.replace(/\//g, '_').replace(/\+/g, '-'); }; - reset_chunk_size(); - logger.debug("invoke reset_chunk_size()"); - logger.debug("op.chunk_size: ", op.chunk_size); + this.URLSafeBase64Decode = function (v) { + v = v.replace(/_/g, '/').replace(/-/g, '+'); + return this.base64_decode(v); + }; - var defaultSetting = { - url: qiniuUploadUrl, - multipart_params: { - token: '' + // TODO: use mOxie + /** + * craete object used to AJAX + * @return {Object} + */ + this.createAjax = function (argument) { + var xmlhttp = {}; + if (window.XMLHttpRequest) { + xmlhttp = new XMLHttpRequest(); + } else { + xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } + return xmlhttp; }; - var ie = that.detectIEVersion(); - // case IE 9- - // add accept in multipart params - if (ie && ie <= 9) { - defaultSetting.multipart_params.accept = 'text/plain; charset=utf-8'; - logger.debug("add accept text/plain in multipart params"); - } - // compose options with user passed options and default setting - plupload.extend(option, op, defaultSetting); + // TODO: enhance IE compatibility + /** + * parse json string to javascript object + * @param {String} json string + * @return {Object} object + */ + this.parseJSON = function (data) { + // Attempt to parse using the native JSON parser first + if (window.JSON && window.JSON.parse) { + return window.JSON.parse(data); + } + + //var rx_one = /^[\],:{}\s]*$/, + // rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + // rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + // rx_four = /(?:^|:|,)(?:\s*\[)+/g, + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - logger.debug("option: ", option); + //var json; - // create a new uploader with composed options - var uploader = new plupload.Uploader(option); + var text = String(data); + rx_dangerous.lastIndex = 0; + if (rx_dangerous.test(text)) { + text = text.replace(rx_dangerous, function (a) { + return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } - logger.debug("new plupload.Uploader(option)"); + // todo 使用一下判断,增加安全性 + //if ( + // rx_one.test( + // text + // .replace(rx_two, '@') + // .replace(rx_three, ']') + // .replace(rx_four, '') + // ) + //) { + // return eval('(' + text + ')'); + //} + + return eval('(' + text + ')'); + }; - // bind getNewUpToken to 'Init' event - uploader.bind('Init', function(up, params) { - logger.debug("Init event activated"); - // if op.get_new_uptoken is not true - // invoke getNewUptoken when uploader init - // else - // getNewUptoken everytime before a new file upload - if(!op.get_new_uptoken){ - getNewUpToken(null); + /** + * parse javascript object to json string + * @param {Object} object + * @return {String} json string + */ + this.stringifyJSON = function (obj) { + // Attempt to parse using the native JSON parser first + if (window.JSON && window.JSON.stringify) { + return window.JSON.stringify(obj); } - //getNewUpToken(null); - }); - - logger.debug("bind Init event"); - - // bind 'FilesAdded' event - // when file be added and auto_start has set value - // uploader will auto start upload the file - uploader.bind('FilesAdded', function(up, files) { - logger.debug("FilesAdded event activated"); - var auto_start = up.getOption && up.getOption('auto_start'); - auto_start = auto_start || (up.settings && up.settings.auto_start); - logger.debug("auto_start: ", auto_start); - logger.debug("files: ", files); - - // detect is iOS - var is_ios = function (){ - if(mOxie.Env.OS.toLowerCase()==="ios") { - return true; + switch (typeof (obj)) { + case 'string': + return '"' + obj.replace(/(["\\])/g, '\\$1') + '"'; + case 'array': + return '[' + obj.map(that.stringifyJSON).join(',') + ']'; + case 'object': + if (obj instanceof Array) { + var strArr = []; + var len = obj.length; + for (var i = 0; i < len; i++) { + strArr.push(that.stringifyJSON(obj[i])); + } + return '[' + strArr.join(',') + ']'; + } else if (obj === null) { + return 'null'; + } else { + var string = []; + for (var property in obj) { + if (obj.hasOwnProperty(property)) { + string.push(that.stringifyJSON(property) + ':' + that.stringifyJSON(obj[property])); + } + } + return '{' + string.join(',') + '}'; + } + break; + case 'number': + return obj; + case false: + return obj; + case 'boolean': + return obj; + } + }; + + /** + * trim space beside text + * @param {String} untrimed string + * @return {String} trimed string + */ + this.trim = function (text) { + return text === null ? "" : text.replace(/^\s+|\s+$/g, ''); + }; + + /** + * create a uploader by QiniuJsSDK + * @param {object} options to create a new uploader + * @return {object} uploader + */ + this.uploader = function (op) { + + /********** inner function define start **********/ + + // according the different condition to reset chunk size + // and the upload strategy according with the chunk size + // when chunk size is zero will cause to direct upload + // see the statement binded on 'BeforeUpload' event + var reset_chunk_size = function () { + var ie = that.detectIEVersion(); + var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size; + // case Safari 5、Windows 7、iOS 7 set isSpecialSafari to true + var isSpecialSafari = (moxie.core.utils.Env.browser === "Safari" && moxie.core.utils.Env.version <= 5 && moxie.core.utils.Env.os === "Windows" && moxie.core.utils.Env.osVersion === "7") || (moxie.core.utils.Env.browser === "Safari" && moxie.core.utils.Env.os === "iOS" && moxie.core.utils.Env.osVersion === "7"); + // case IE 9-,chunk_size is not empty and flash is included in runtimes + // set op.chunk_size to zero + //if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { + if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { + // link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not + // when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) . + op.chunk_size = 0; + } else if (isSpecialSafari) { + // win7 safari / iOS7 safari have bug when in chunk upload mode + // reset chunk_size to 0 + // disable chunk in special version safari + op.chunk_size = 0; } else { - return false; + BLOCK_BITS = 20; + MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M + + chunk_size = plupload.parseSize(op.chunk_size); + if (chunk_size > MAX_CHUNK_SIZE) { + op.chunk_size = MAX_CHUNK_SIZE; + } + // qiniu service max_chunk_size is 4m + // reset chunk_size to max_chunk_size(4m) when chunk_size > 4m } + // if op.chunk_size set 0 will be cause to direct upload }; - // if current env os is iOS change file name to [time].[ext] - if (is_ios()) { - for (var i = 0; i < files.length; i++) { - var file = files[i]; - var ext = that.getFileExtension(file.name); - file.name = file.id + "." + ext; + var getHosts = function (hosts) { + var result = []; + var uploadIndex = -1; + for (var i = 0; i < hosts.length; i++) { + var host = hosts[i]; + if (host.indexOf("upload") !== -1) { + uploadIndex = i; + } + if (host.indexOf('-H') === 0) { + result.push(host.split(' ')[2]); + } else { + result.push(host); + } } - } - if (auto_start) { - setTimeout(function(){ - up.start(); - logger.debug("invoke up.start()"); - }, 0); - // up.start(); - // plupload.each(files, function(i, file) { - // up.start(); - // logger.debug("invoke up.start()") - // logger.debug("file: ", file); - // }); - } - up.refresh(); // Reposition Flash/Silverlight - }); - - logger.debug("bind FilesAdded event"); - - // bind 'BeforeUpload' event - // intercept the process of upload - // - prepare uptoken - // - according the chunk size to make differnt upload strategy - // - resume upload with the last breakpoint of file - uploader.bind('BeforeUpload', function(up, file) { - logger.debug("BeforeUpload event activated"); - // add a key named speed for file object - file.speed = file.speed || 0; - ctx = ''; - - if(op.get_new_uptoken){ - getNewUpToken(file); - } + if (uploadIndex !== -1) { + //make upload domains first + var uploadDomain = result[uploadIndex]; + result[uploadIndex] = result[0]; + result[0] = uploadDomain; + } + return result; + }; - var directUpload = function(up, file, func) { - speedCalInfo.startTime = new Date().getTime(); - var multipart_params_obj; - if (op.save_key) { - multipart_params_obj = { - 'token': that.token - }; + var getPutPolicy = function (uptoken) { + var segments = uptoken.split(":"); + var ak = segments[0]; + var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); + putPolicy.ak = ak; + if (putPolicy.scope.indexOf(":") >= 0) { + putPolicy.bucket = putPolicy.scope.split(":")[0]; + putPolicy.key = putPolicy.scope.split(":")[1]; } else { - multipart_params_obj = { - 'key': getFileKey(up, file, func), - 'token': that.token - }; + putPolicy.bucket = putPolicy.scope; } + return putPolicy; + }; + + var getUpHosts = function (uptoken) { + var putPolicy = getPutPolicy(uptoken); + // var uphosts_url = "//uc.qbox.me/v1/query?ak="+ak+"&bucket="+putPolicy.scope; + // IE9 does not support protocol relative url + var uphosts_url = window.location.protocol + "//uc.qbox.me/v1/query?ak=" + putPolicy.ak + "&bucket=" + putPolicy.bucket; + logger.debug("putPolicy: ", putPolicy); + logger.debug("get uphosts from: ", uphosts_url); var ie = that.detectIEVersion(); - // case IE 9- - // add accept in multipart params + var ajax; if (ie && ie <= 9) { - multipart_params_obj.accept = 'text/plain; charset=utf-8'; - logger.debug("add accept text/plain in multipart params"); + ajax = new moxie.xhr.XMLHttpRequest(); + moxie.core.utils.Env.swf_url = op.flash_swf_url; + } else { + ajax = that.createAjax(); } - - logger.debug("directUpload multipart_params_obj: ", multipart_params_obj); - - var x_vars = op.x_vars; - if (x_vars !== undefined && typeof x_vars === 'object') { - for (var x_key in x_vars) { - if (x_vars.hasOwnProperty(x_key)) { - if (typeof x_vars[x_key] === 'function') { - multipart_params_obj['x:' + x_key] = x_vars[x_key](up, file); - } else if (typeof x_vars[x_key] !== 'object') { - multipart_params_obj['x:' + x_key] = x_vars[x_key]; - } + ajax.open('GET', uphosts_url, false); + var onreadystatechange = function () { + logger.debug("ajax.readyState: ", ajax.readyState); + if (ajax.readyState === 4) { + logger.debug("ajax.status: ", ajax.status); + if (ajax.status < 400) { + var res = that.parseJSON(ajax.responseText); + qiniuUpHosts.http = getHosts(res.http.up); + qiniuUpHosts.https = getHosts(res.https.up); + logger.debug("get new uphosts: ", qiniuUpHosts); + that.resetUploadUrl(); + } else { + logger.error("get uphosts error: ", ajax.responseText); } } + }; + if (ie && ie <= 9) { + ajax.bind('readystatechange', onreadystatechange); + } else { + ajax.onreadystatechange = onreadystatechange; } - - up.setOption({ - 'url': qiniuUploadUrl, - 'multipart': true, - 'chunk_size': is_android_weixin_or_qq() ? op.max_file_size : undefined, - 'multipart_params': multipart_params_obj - }); + ajax.send(); + // ajax.send(); + // if (ajax.status < 400) { + // var res = that.parseJSON(ajax.responseText); + // qiniuUpHosts.http = getHosts(res.http.up); + // qiniuUpHosts.https = getHosts(res.https.up); + // logger.debug("get new uphosts: ", qiniuUpHosts); + // that.resetUploadUrl(); + // } else { + // logger.error("get uphosts error: ", ajax.responseText); + // } + return; }; - // detect is weixin or qq inner browser - var is_android_weixin_or_qq = function (){ - var ua = navigator.userAgent.toLowerCase(); - if((ua.match(/MicroMessenger/i) || mOxie.Env.browser === "QQBrowser" || ua.match(/V1_AND_SQ/i)) && mOxie.Env.OS.toLowerCase()==="android") { - return true; + var getUptoken = function (file) { + if (!that.token || (op.uptoken_url && that.tokenInfo.isExpired())) { + return getNewUpToken(file); } else { - return false; + return that.token; } }; - var chunk_size = up.getOption && up.getOption('chunk_size'); - chunk_size = chunk_size || (up.settings && up.settings.chunk_size); + // getNewUptoken maybe called at Init Event or BeforeUpload Event + // case Init Event, the file param of getUptken will be set a null value + // if op.uptoken has value, set uptoken with op.uptoken + // else if op.uptoken_url has value, set uptoken from op.uptoken_url + // else if op.uptoken_func has value, set uptoken by result of op.uptoken_func + var getNewUpToken = function (file) { + if (op.uptoken) { + that.token = op.uptoken; + } else if (op.uptoken_url) { + logger.debug("get uptoken from: ", that.uptoken_url); + // TODO: use mOxie + var ajax = that.createAjax(); + ajax.open('GET', that.uptoken_url + '?' + (+new Date()), false); + // ajax.setRequestHeader("If-Modified-Since", "0"); + // ajax.onreadystatechange = function() { + // if (ajax.readyState === 4 && ajax.status === 200) { + // var res = that.parseJSON(ajax.responseText); + // that.token = res.uptoken; + // } + // }; + ajax.send(); + if (ajax.status === 200) { + var res = that.parseJSON(ajax.responseText); + that.token = res.uptoken; + var segments = that.token.split(":"); + var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); + if (!that.tokenMap) { + that.tokenMap = {}; + } + var getTimestamp = function (time) { + return Math.ceil(time.getTime() / 1000); + }; + var serverTime = getTimestamp(new Date(ajax.getResponseHeader("date"))); + var clientTime = getTimestamp(new Date()); + that.tokenInfo = { + serverDelay: clientTime - serverTime, + deadline: putPolicy.deadline, + isExpired: function () { + var leftTime = this.deadline - getTimestamp(new Date()) + this.serverDelay; + return leftTime < 600; + } + }; + logger.debug("get new uptoken: ", that.token); + logger.debug("get token info: ", that.tokenInfo); + } else { + logger.error("get uptoken error: ", ajax.responseText); + } + } else if (op.uptoken_func) { + logger.debug("get uptoken from uptoken_func"); + that.token = op.uptoken_func(file); + logger.debug("get new uptoken: ", that.token); + } else { + logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!"); + } + if (that.token) { + getUpHosts(that.token); + } + return that.token; + }; - logger.debug("uploader.runtime: ",uploader.runtime); - logger.debug("chunk_size: ",chunk_size); + // get file key according with the user passed options + var getFileKey = function (up, file, func) { + // WARNING + // When you set the key in putPolicy by "scope": "bucket:key" + // You should understand the risk of override a file in the bucket + // So the code below that automatically get key from uptoken has been commented + // var putPolicy = getPutPolicy(that.token) + // if (putPolicy.key) { + // logger.debug("key is defined in putPolicy.scope: ", putPolicy.key) + // return putPolicy.key + // } + var key = '', + unique_names = false; + if (!op.save_key) { + unique_names = up.getOption && up.getOption('unique_names'); + unique_names = unique_names || (up.settings && up.settings.unique_names); + if (unique_names) { + var ext = that.getFileExtension(file.name); + key = ext ? file.id + '.' + ext : file.id; + } else if (typeof func === 'function') { + key = func(up, file); + } else { + key = file.name; + } + } + return key; + }; - // TODO: flash support chunk upload - if ((uploader.runtime === 'html5' || uploader.runtime === 'flash') && chunk_size) { - if (file.size < chunk_size || is_android_weixin_or_qq()) { - logger.debug("directUpload because file.size < chunk_size || is_android_weixin_or_qq()"); - // direct upload if file size is less then the chunk size - directUpload(up, file, that.key_handler); - } else { - // TODO: need a polifill to make it work in IE 9- - // ISSUE: if file.name is existed in localStorage - // but not the same file maybe cause error - var localFileInfo = localStorage.getItem(file.name); - var blockSize = chunk_size; - if (localFileInfo) { - // TODO: although only the html5 runtime will enter this statement - // but need uniform way to make convertion between string and json - localFileInfo = that.parseJSON(localFileInfo); - var now = (new Date()).getTime(); - var before = localFileInfo.time || 0; - var aDay = 24 * 60 * 60 * 1000; // milliseconds of one day - // if the last upload time is within one day - // will upload continuously follow the last breakpoint - // else - // will reupload entire file - if (now - before < aDay) { - - if (localFileInfo.percent !== 100) { - if (file.size === localFileInfo.total) { - // TODO: if file.name and file.size is the same - // but not the same file will cause error - file.percent = localFileInfo.percent; - file.loaded = localFileInfo.offset; - ctx = localFileInfo.ctx; - - // set speed info - speedCalInfo.isResumeUpload = true; - speedCalInfo.resumeFilesize = localFileInfo.offset; - - // set block size - if (localFileInfo.offset + blockSize > file.size) { - blockSize = file.size - localFileInfo.offset; - } - } else { - // remove file info when file.size is conflict with file info - localStorage.removeItem(file.name); - } + var getDomainFromUrl = function (url) { + if (url && url.match) { + var groups = url.match(/^https?:\/\/([^:^/]*)/); + return groups ? groups[1] : ""; + } + return ""; + }; - } else { - // remove file info when upload percent is 100% - // avoid 499 bug - localStorage.removeItem(file.name); - } - } else { - // remove file info when last upload time is over one day - localStorage.removeItem(file.name); - } + var getPortFromUrl = function (url) { + if (url && url.match) { + var groups = url.match(/(^https?)/); + if (!groups) { + return ""; } - speedCalInfo.startTime = new Date().getTime(); - var multipart_params_obj = {}; - var ie = that.detectIEVersion(); - // case IE 9- - // add accept in multipart params - if (ie && ie <= 9) { - multipart_params_obj.accept = 'text/plain; charset=utf-8'; - logger.debug("add accept text/plain in multipart params"); + var type = groups[1]; + groups = url.match(/^https?:\/\/([^:^/]*):(\d*)/); + if (groups) { + return groups[2]; + } else if (type === "http") { + return "80"; + } else { + return "443"; } - // TODO: to support bput - // http://developer.qiniu.com/docs/v6/api/reference/up/bput.html - up.setOption({ - 'url': qiniuUploadUrl + '/mkblk/' + blockSize, - 'multipart': false, - 'chunk_size': chunk_size, - 'required_features': "chunks", - 'headers': { - 'Authorization': 'UpToken ' + getUptoken(file) - }, - 'multipart_params': multipart_params_obj - }); } - } else { - logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size"); - // direct upload if runtime is not html5 - directUpload(up, file, that.key_handler); + return ""; + }; + + /********** inner function define end **********/ + + if (op.log_level) { + logger.level = op.log_level; } - }); - - logger.debug("bind BeforeUpload event"); - - // bind 'UploadProgress' event - // calculate upload speed - uploader.bind('UploadProgress', function(up, file) { - logger.trace("UploadProgress event activated"); - speedCalInfo.currentTime = new Date().getTime(); - var timeUsed = speedCalInfo.currentTime - speedCalInfo.startTime; // ms - var fileUploaded = file.loaded || 0; - if (speedCalInfo.isResumeUpload) { - fileUploaded = file.loaded - speedCalInfo.resumeFilesize; + + if (!op.domain) { + throw 'domain setting in options is required!'; } - file.speed = (fileUploaded / timeUsed * 1000).toFixed(0) || 0; // unit: byte/s - }); - - logger.debug("bind UploadProgress event"); - - // bind 'ChunkUploaded' event - // store the chunk upload info and set next chunk upload url - uploader.bind('ChunkUploaded', function(up, file, info) { - logger.debug("ChunkUploaded event activated"); - logger.debug("file: ", file); - logger.debug("info: ", info); - var res = that.parseJSON(info.response); - logger.debug("res: ", res); - // ctx should look like '[chunk01_ctx],[chunk02_ctx],[chunk03_ctx],...' - ctx = ctx ? ctx + ',' + res.ctx : res.ctx; - var leftSize = info.total - info.offset; - var chunk_size = up.getOption && up.getOption('chunk_size'); - chunk_size = chunk_size || (up.settings && up.settings.chunk_size); - if (leftSize < chunk_size) { - up.setOption({ - 'url': qiniuUploadUrl + '/mkblk/' + leftSize - }); - logger.debug("up.setOption url: ", qiniuUploadUrl + '/mkblk/' + leftSize); + + if (!op.browse_button) { + throw 'browse_button setting in options is required!'; } - up.setOption({ - 'headers': { - 'Authorization': 'UpToken ' + getUptoken(file) - } - }); - localStorage.setItem(file.name, that.stringifyJSON({ - ctx: ctx, - percent: file.percent, - total: info.total, - offset: info.offset, - time: (new Date()).getTime() - })); - }); - - logger.debug("bind ChunkUploaded event"); - - var retries = qiniuUploadUrls.length; - - // if error is unkown switch upload url and retry - var unknow_error_retry = function(file){ - if (retries-- > 0) { - setTimeout(function(){ - that.resetUploadUrl(); - file.status = plupload.QUEUED; - uploader.stop(); - uploader.start(); - }, 0); - return true; - }else{ - retries = qiniuUploadUrls.length; - return false; + + if (!op.uptoken && !op.uptoken_url && !op.uptoken_func) { + throw 'one of [uptoken, uptoken_url, uptoken_func] settings in options is required!'; } - }; - // bind 'Error' event - // check the err.code and return the errTip - uploader.bind('Error', (function(_Error_Handler) { - return function(up, err) { - logger.error("Error event activated"); - logger.error("err: ", err); - var errTip = ''; - var file = err.file; - if (file) { - switch (err.code) { - case plupload.FAILED: - errTip = '上传失败。请稍后再试。'; - break; - case plupload.FILE_SIZE_ERROR: - var max_file_size = up.getOption && up.getOption('max_file_size'); - max_file_size = max_file_size || (up.settings && up.settings.max_file_size); - errTip = '浏览器最大可上传' + max_file_size + '。更大文件请使用命令行工具。'; - break; - case plupload.FILE_EXTENSION_ERROR: - errTip = '文件验证失败。请稍后重试。'; - break; - case plupload.HTTP_ERROR: - if (err.response === '') { - // Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE - errTip = err.message || '未知网络错误。'; - if (!unknow_error_retry(file)) { - return; - } - break; - } - var errorObj = that.parseJSON(err.response); - var errorText = errorObj.error; - switch (err.status) { - case 400: - errTip = "请求报文格式错误。"; - break; - case 401: - errTip = "客户端认证授权失败。请重试或提交反馈。"; - break; - case 405: - errTip = "客户端请求错误。请重试或提交反馈。"; - break; - case 579: - errTip = "资源上传成功,但回调失败。"; - break; - case 599: - errTip = "网络连接异常。请重试或提交反馈。"; - if (!unknow_error_retry(file)) { - return; - } - break; - case 614: - errTip = "文件已存在。"; - try { - errorObj = that.parseJSON(errorObj.error); - errorText = errorObj.error || 'file exists'; - } catch (e) { - errorText = errorObj.error || 'file exists'; - } - break; - case 631: - errTip = "指定空间不存在。"; - break; - case 701: - errTip = "上传数据块校验出错。请重试或提交反馈。"; - break; - default: - errTip = "未知错误。"; - if (!unknow_error_retry(file)) { - return; - } - break; - } - errTip = errTip + '(' + err.status + ':' + errorText + ')'; - break; - case plupload.SECURITY_ERROR: - errTip = '安全配置错误。请联系网站管理员。'; - break; - case plupload.GENERIC_ERROR: - errTip = '上传失败。请稍后再试。'; - break; - case plupload.IO_ERROR: - errTip = '上传失败。请稍后再试。'; - break; - case plupload.INIT_ERROR: - errTip = '网站配置错误。请联系网站管理员。'; - uploader.destroy(); - break; - default: - errTip = err.message + err.details; - if (!unknow_error_retry(file)) { - return; - } - break; + logger.debug("init uploader start"); + + logger.debug("environment: ", moxie.core.utils.Env); + + logger.debug("userAgent: ", navigator.userAgent); + + var option = {}; + + // hold the handler from user passed options + var _Error_Handler = op.init && op.init.Error; + var _FileUploaded_Handler = op.init && op.init.FileUploaded; + + // replace the handler for intercept + op.init.Error = function () {}; + op.init.FileUploaded = function () {}; + + that.uptoken_url = op.uptoken_url; + that.token = ''; + that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : ''; + this.domain = op.domain; + // TODO: ctx is global in scope of a uploader instance + // this maybe cause error + var ctx = ''; + var speedCalInfo = { + isResumeUpload: false, + resumeFilesize: 0, + startTime: '', + currentTime: '' + }; + + reset_chunk_size(); + logger.debug("invoke reset_chunk_size()"); + logger.debug("op.chunk_size: ", op.chunk_size); + + var defaultSetting = { + url: qiniuUploadUrl, + multipart_params: { + token: '' + } + }; + var ie = that.detectIEVersion(); + // case IE 9- + // add accept in multipart params + if (ie && ie <= 9) { + defaultSetting.multipart_params.accept = 'text/plain; charset=utf-8'; + logger.debug("add accept text/plain in multipart params"); + } + + // compose options with user passed options and default setting + plupload.extend(option, op, defaultSetting); + + logger.debug("option: ", option); + + // create a new uploader with composed options + var uploader = new plupload.Uploader(option); + + logger.debug("new plupload.Uploader(option)"); + + // bind getNewUpToken to 'Init' event + uploader.bind('Init', function (up, params) { + logger.debug("Init event activated"); + // if op.get_new_uptoken is not true + // invoke getNewUptoken when uploader init + // else + // getNewUptoken everytime before a new file upload + if (!op.get_new_uptoken) { + getNewUpToken(null); + } + //getNewUpToken(null); + }); + + logger.debug("bind Init event"); + + // bind 'FilesAdded' event + // when file be added and auto_start has set value + // uploader will auto start upload the file + uploader.bind('FilesAdded', function (up, files) { + logger.debug("FilesAdded event activated"); + var auto_start = up.getOption && up.getOption('auto_start'); + auto_start = auto_start || (up.settings && up.settings.auto_start); + logger.debug("auto_start: ", auto_start); + logger.debug("files: ", files); + + // detect is iOS + var is_ios = function () { + if (moxie.core.utils.Env.OS.toLowerCase() === "ios") { + return true; + } else { + return false; } - if (_Error_Handler) { - _Error_Handler(up, err, errTip); + }; + + // if current env os is iOS change file name to [time].[ext] + if (is_ios()) { + for (var i = 0; i < files.length; i++) { + var file = files[i]; + var ext = that.getFileExtension(file.name); + file.name = file.id + "." + ext; } } + + if (auto_start) { + setTimeout(function () { + up.start(); + logger.debug("invoke up.start()"); + }, 0); + // up.start(); + // plupload.each(files, function(i, file) { + // up.start(); + // logger.debug("invoke up.start()") + // logger.debug("file: ", file); + // }); + } up.refresh(); // Reposition Flash/Silverlight - }; - })(_Error_Handler)); - - logger.debug("bind Error event"); - - // bind 'FileUploaded' event - // intercept the complete of upload - // - get downtoken from downtoken_url if bucket is private - // - invoke mkfile api to compose chunks if upload strategy is chunk upload - uploader.bind('FileUploaded', (function(_FileUploaded_Handler) { - return function(up, file, info) { - logger.debug("FileUploaded event activated"); - logger.debug("file: ", file); - logger.debug("info: ", info); - var last_step = function(up, file, info) { - if (op.downtoken_url) { - // if op.dowontoken_url is not empty - // need get downtoken before invoke the _FileUploaded_Handler - var ajax_downtoken = that.createAjax(); - ajax_downtoken.open('POST', op.downtoken_url, true); - ajax_downtoken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - ajax_downtoken.onreadystatechange = function() { - if (ajax_downtoken.readyState === 4) { - if (ajax_downtoken.status === 200) { - var res_downtoken; - try { - res_downtoken = that.parseJSON(ajax_downtoken.responseText); - } catch (e) { - throw ('invalid json format'); - } - var info_extended = {}; - plupload.extend(info_extended, that.parseJSON(info), res_downtoken); - if (_FileUploaded_Handler) { - _FileUploaded_Handler(up, file, that.stringifyJSON(info_extended)); - } - } else { - uploader.trigger('Error', { - status: ajax_downtoken.status, - response: ajax_downtoken.responseText, - file: file, - code: plupload.HTTP_ERROR - }); - } - } + }); + + logger.debug("bind FilesAdded event"); + + // bind 'BeforeUpload' event + // intercept the process of upload + // - prepare uptoken + // - according the chunk size to make differnt upload strategy + // - resume upload with the last breakpoint of file + uploader.bind('BeforeUpload', function (up, file) { + logger.debug("BeforeUpload event activated"); + file._start_at = new Date(); + // add a key named speed for file object + file.speed = file.speed || 0; + ctx = ''; + + if (op.get_new_uptoken) { + getNewUpToken(file); + } + + var directUpload = function (up, file, func) { + speedCalInfo.startTime = new Date().getTime(); + var multipart_params_obj; + if (op.save_key) { + multipart_params_obj = { + 'token': that.token + }; + } else { + multipart_params_obj = { + 'key': getFileKey(up, file, func), + 'token': that.token }; - ajax_downtoken.send('key=' + that.parseJSON(info).key + '&domain=' + op.domain); - } else if (_FileUploaded_Handler) { - _FileUploaded_Handler(up, file, info); } - }; - - var res = that.parseJSON(info.response); - ctx = ctx ? ctx : res.ctx; - // if ctx is not empty - // that means the upload strategy is chunk upload - // befroe the invoke the last_step - // we need request the mkfile to compose all uploaded chunks - // else - // invalke the last_step - logger.debug("ctx: ", ctx); - if (ctx) { - var key = ''; - logger.debug("save_key: ", op.save_key); - if (!op.save_key) { - key = getFileKey(up, file, that.key_handler); - key = key ? '/key/' + that.URLSafeBase64Encode(key) : ''; + var ie = that.detectIEVersion(); + // case IE 9- + // add accept in multipart params + if (ie && ie <= 9) { + multipart_params_obj.accept = 'text/plain; charset=utf-8'; + logger.debug("add accept text/plain in multipart params"); } - var fname = '/fname/' + that.URLSafeBase64Encode(file.name); + logger.debug("directUpload multipart_params_obj: ", multipart_params_obj); - logger.debug("op.x_vars: ", op.x_vars); - var x_vars = op.x_vars, - x_val = '', - x_vars_url = ''; + var x_vars = op.x_vars; if (x_vars !== undefined && typeof x_vars === 'object') { for (var x_key in x_vars) { if (x_vars.hasOwnProperty(x_key)) { if (typeof x_vars[x_key] === 'function') { - x_val = that.URLSafeBase64Encode(x_vars[x_key](up, file)); + multipart_params_obj['x:' + x_key] = x_vars[x_key](up, file); } else if (typeof x_vars[x_key] !== 'object') { - x_val = that.URLSafeBase64Encode(x_vars[x_key]); + multipart_params_obj['x:' + x_key] = x_vars[x_key]; } - x_vars_url += '/x:' + x_key + '/' + x_val; } } } - var url = qiniuUploadUrl + '/mkfile/' + file.size + key + fname + x_vars_url; + up.setOption({ + 'url': qiniuUploadUrl, + 'multipart': true, + 'chunk_size': is_android_weixin_or_qq() ? op.max_file_size : undefined, + 'multipart_params': multipart_params_obj + }); + }; - var ie = that.detectIEVersion(); - var ajax; - if (ie && ie <= 9) { - ajax = new mOxie.XMLHttpRequest(); - mOxie.Env.swf_url = op.flash_swf_url; - }else{ - ajax = that.createAjax(); + // detect is weixin or qq inner browser + var is_android_weixin_or_qq = function () { + var ua = navigator.userAgent.toLowerCase(); + if ((ua.match(/MicroMessenger/i) || moxie.core.utils.Env.browser === "QQBrowser" || ua.match(/V1_AND_SQ/i)) && moxie.core.utils.Env.OS.toLowerCase() === "android") { + return true; + } else { + return false; } - ajax.open('POST', url, true); - ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8'); - ajax.setRequestHeader('Authorization', 'UpToken ' + that.token); - var onreadystatechange = function(){ - logger.debug("ajax.readyState: ", ajax.readyState); - if (ajax.readyState === 4) { - localStorage.removeItem(file.name); - var info; - if (ajax.status === 200) { - info = ajax.responseText; - logger.debug("mkfile is success: ", info); - last_step(up, file, info); + }; + + var chunk_size = up.getOption && up.getOption('chunk_size'); + chunk_size = chunk_size || (up.settings && up.settings.chunk_size); + + logger.debug("uploader.runtime: ", uploader.runtime); + logger.debug("chunk_size: ", chunk_size); + + // TODO: flash support chunk upload + if ((uploader.runtime === 'html5' || uploader.runtime === 'flash') && chunk_size) { + if (file.size < chunk_size || is_android_weixin_or_qq()) { + logger.debug("directUpload because file.size < chunk_size || is_android_weixin_or_qq()"); + // direct upload if file size is less then the chunk size + directUpload(up, file, that.key_handler); + } else { + // TODO: need a polifill to make it work in IE 9- + // ISSUE: if file.name is existed in localStorage + // but not the same file maybe cause error + var localFileInfo = localStorage.getItem(file.name); + var blockSize = chunk_size; + if (localFileInfo) { + // TODO: although only the html5 runtime will enter this statement + // but need uniform way to make convertion between string and json + localFileInfo = that.parseJSON(localFileInfo); + var now = (new Date()).getTime(); + var before = localFileInfo.time || 0; + var aDay = 24 * 60 * 60 * 1000; // milliseconds of one day + // if the last upload time is within one day + // will upload continuously follow the last breakpoint + // else + // will reupload entire file + if (now - before < aDay) { + + if (localFileInfo.percent !== 100) { + if (file.size === localFileInfo.total) { + // TODO: if file.name and file.size is the same + // but not the same file will cause error + file.percent = localFileInfo.percent; + file.loaded = localFileInfo.offset; + ctx = localFileInfo.ctx; + + // set speed info + speedCalInfo.isResumeUpload = true; + speedCalInfo.resumeFilesize = localFileInfo.offset; + + // set block size + if (localFileInfo.offset + blockSize > file.size) { + blockSize = file.size - localFileInfo.offset; + } + } else { + // remove file info when file.size is conflict with file info + localStorage.removeItem(file.name); + } + + } else { + // remove file info when upload percent is 100% + // avoid 499 bug + localStorage.removeItem(file.name); + } } else { - info = { - status: ajax.status, - response: ajax.responseText, - file: file, - code: -200 - }; - logger.debug("mkfile is error: ", info); - uploader.trigger('Error', info); + // remove file info when last upload time is over one day + localStorage.removeItem(file.name); } } - }; - if (ie && ie <= 9) { - ajax.bind('readystatechange', onreadystatechange); - }else{ - ajax.onreadystatechange = onreadystatechange; + speedCalInfo.startTime = new Date().getTime(); + var multipart_params_obj = {}; + var ie = that.detectIEVersion(); + // case IE 9- + // add accept in multipart params + if (ie && ie <= 9) { + multipart_params_obj.accept = 'text/plain; charset=utf-8'; + logger.debug("add accept text/plain in multipart params"); + } + // TODO: to support bput + // http://developer.qiniu.com/docs/v6/api/reference/up/bput.html + up.setOption({ + 'url': qiniuUploadUrl + '/mkblk/' + blockSize, + 'multipart': false, + 'chunk_size': chunk_size, + 'required_features': "chunks", + 'headers': { + 'Authorization': 'UpToken ' + getUptoken(file) + }, + 'multipart_params': multipart_params_obj + }); } - ajax.send(ctx); - logger.debug("mkfile: ", url); } else { - last_step(up, file, info.response); + logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size"); + // direct upload if runtime is not html5 + directUpload(up, file, that.key_handler); } + }); - }; - })(_FileUploaded_Handler)); + logger.debug("bind BeforeUpload event"); + + // bind 'UploadProgress' event + // calculate upload speed + uploader.bind('UploadProgress', function (up, file) { + logger.trace("UploadProgress event activated"); + speedCalInfo.currentTime = new Date().getTime(); + var timeUsed = speedCalInfo.currentTime - speedCalInfo.startTime; // ms + var fileUploaded = file.loaded || 0; + if (speedCalInfo.isResumeUpload) { + fileUploaded = file.loaded - speedCalInfo.resumeFilesize; + } + file.speed = (fileUploaded / timeUsed * 1000).toFixed(0) || 0; // unit: byte/s + }); - logger.debug("bind FileUploaded event"); + logger.debug("bind UploadProgress event"); - // init uploader - uploader.init(); + // bind 'ChunkUploaded' event + // store the chunk upload info and set next chunk upload url + uploader.bind('ChunkUploaded', function (up, file, info) { + logger.debug("ChunkUploaded event activated"); + logger.debug("ChunkUploaded file: ", file); + logger.debug("ChunkUploaded info: ", info); + var res = that.parseJSON(info.response); + logger.debug("ChunkUploaded res: ", res); + // ctx should look like '[chunk01_ctx],[chunk02_ctx],[chunk03_ctx],...' + ctx = ctx ? ctx + ',' + res.ctx : res.ctx; + var leftSize = info.total - info.offset; + var chunk_size = up.getOption && up.getOption('chunk_size'); + chunk_size = chunk_size || (up.settings && up.settings.chunk_size); + if (leftSize < chunk_size) { + up.setOption({ + 'url': qiniuUploadUrl + '/mkblk/' + leftSize + }); + logger.debug("up.setOption url: ", qiniuUploadUrl + '/mkblk/' + leftSize); + } + up.setOption({ + 'headers': { + 'Authorization': 'UpToken ' + getUptoken(file) + } + }); + localStorage.setItem(file.name, that.stringifyJSON({ + ctx: ctx, + percent: file.percent, + total: info.total, + offset: info.offset, + time: (new Date()).getTime() + })); + }); - logger.debug("invoke uploader.init()"); + logger.debug("bind ChunkUploaded event"); - logger.debug("init uploader end"); + var retries = qiniuUploadUrls.length; - return uploader; - }; + // if error is unkown switch upload url and retry + var unknow_error_retry = function (file) { + if (retries-- > 0) { + setTimeout(function () { + that.resetUploadUrl(); + file.status = plupload.QUEUED; + uploader.stop(); + uploader.start(); + }, 0); + return true; + } else { + retries = qiniuUploadUrls.length; + return false; + } + }; - /** - * get url by key - * @param {String} key of file - * @return {String} url of file - */ - this.getUrl = function(key) { - if (!key) { - return false; - } - key = encodeURI(key); - var domain = this.domain; - if (domain.slice(domain.length - 1) !== '/') { - domain = domain + '/'; - } - return domain + key; - }; + // bind 'Error' event + // check the err.code and return the errTip + uploader.bind('Error', (function (_Error_Handler) { + return function (up, err) { + logger.error("Error event activated"); + logger.error("err: ", err); + var nowTime = new Date(); + var errTip = ''; + var file = err.file; + if (file) { + switch (err.code) { + case plupload.FAILED: + errTip = '上传失败。请稍后再试。'; + break; + case plupload.FILE_SIZE_ERROR: + var max_file_size = up.getOption && up.getOption('max_file_size'); + max_file_size = max_file_size || (up.settings && up.settings.max_file_size); + errTip = '浏览器最大可上传' + max_file_size + '。更大文件请使用命令行工具。'; + break; + case plupload.FILE_EXTENSION_ERROR: + errTip = '文件验证失败。请稍后重试。'; + break; + case plupload.HTTP_ERROR: + if (err.response === '') { + // Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE + errTip = err.message || '未知网络错误。'; + if (!unknow_error_retry(file)) { + return; + } + break; + } + var errorObj = that.parseJSON(err.response); + var errorText = errorObj.error; + switch (err.status) { + case 400: + errTip = "请求报文格式错误。"; + break; + case 401: + errTip = "客户端认证授权失败。请重试或提交反馈。"; + break; + case 405: + errTip = "客户端请求错误。请重试或提交反馈。"; + break; + case 579: + errTip = "资源上传成功,但回调失败。"; + break; + case 599: + errTip = "网络连接异常。请重试或提交反馈。"; + if (!unknow_error_retry(file)) { + return; + } + break; + case 614: + errTip = "文件已存在。"; + try { + errorObj = that.parseJSON(errorObj.error); + errorText = errorObj.error || 'file exists'; + } catch (e) { + errorText = errorObj.error || 'file exists'; + } + break; + case 631: + errTip = "指定空间不存在。"; + break; + case 701: + errTip = "上传数据块校验出错。请重试或提交反馈。"; + break; + default: + errTip = "未知错误。"; + if (!unknow_error_retry(file)) { + return; + } + break; + } + errTip = errTip + '(' + err.status + ':' + errorText + ')'; + break; + case plupload.SECURITY_ERROR: + errTip = '安全配置错误。请联系网站管理员。'; + break; + case plupload.GENERIC_ERROR: + errTip = '上传失败。请稍后再试。'; + break; + case plupload.IO_ERROR: + errTip = '上传失败。请稍后再试。'; + break; + case plupload.INIT_ERROR: + errTip = '网站配置错误。请联系网站管理员。'; + uploader.destroy(); + break; + default: + errTip = err.message + err.details; + if (!unknow_error_retry(file)) { + return; + } + break; + } + if (_Error_Handler) { + _Error_Handler(up, err, errTip); + } + } + up.refresh(); // Reposition Flash/Silverlight + + // add send log for upload error + if (!op.disable_statistics_report) { + var matchedGroups = (err && err.responseHeaders && err.responseHeaders.match) ? err.responseHeaders.match(/(X-Reqid\:\ )([\w\.\%-]*)/) : []; + var req_id = matchedGroups[2]; + var errcode = plupload.HTTP_ERROR ? err.status : err.code; + var startAt = file._start_at ? file._start_at.getTime() : nowTime.getTime(); + statisticsLogger.log( + errcode === 0 ? ExtraErrors.NetworkError : errcode, + req_id, + getDomainFromUrl(up.settings.url), + undefined, + getPortFromUrl(up.settings.url), + (nowTime.getTime() - startAt)/1000, + startAt/1000, + err.file.size * (err.file.percent / 100), + "jssdk-" + up.runtime, + file.size + ); + } + }; + })(_Error_Handler)); + + logger.debug("bind Error event"); + + // bind 'FileUploaded' event + // intercept the complete of upload + // - get downtoken from downtoken_url if bucket is private + // - invoke mkfile api to compose chunks if upload strategy is chunk upload + uploader.bind('FileUploaded', (function (_FileUploaded_Handler) { + return function (up, file, info) { + logger.debug("FileUploaded event activated"); + logger.debug("FileUploaded file: ", file); + logger.debug("FileUploaded info: ", info); + var nowTime = new Date(); + var last_step = function (up, file, info) { + logger.debug("FileUploaded last step:", info); + if (op.downtoken_url) { + // if op.dowontoken_url is not empty + // need get downtoken before invoke the _FileUploaded_Handler + var ajax_downtoken = that.createAjax(); + ajax_downtoken.open('POST', op.downtoken_url, true); + ajax_downtoken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + ajax_downtoken.onreadystatechange = function () { + if (ajax_downtoken.readyState === 4) { + if (ajax_downtoken.status === 200) { + var res_downtoken; + try { + res_downtoken = that.parseJSON(ajax_downtoken.responseText); + } catch (e) { + throw ('invalid json format'); + } + var info_extended = {}; + plupload.extend(info_extended, that.parseJSON(info.response), res_downtoken); + info.response = that.stringifyJSON(info_extended); + if (_FileUploaded_Handler) { + _FileUploaded_Handler(up, file, info); + } + } else { + uploader.trigger('Error', { + status: ajax_downtoken.status, + response: ajax_downtoken.responseText, + file: file, + code: plupload.HTTP_ERROR + }); + } + } + }; + ajax_downtoken.send('key=' + that.parseJSON(info.response).key + '&domain=' + op.domain); + } else if (_FileUploaded_Handler) { + _FileUploaded_Handler(up, file, info); + } + }; - /** - * invoke the imageView2 api of Qiniu - * @param {Object} api params - * @param {String} key of file - * @return {String} url of processed image - */ - this.imageView2 = function(op, key) { - var mode = op.mode || '', - w = op.w || '', - h = op.h || '', - q = op.q || '', - format = op.format || ''; - if (!mode) { - return false; - } - if (!w && !h) { - return false; - } + var res = that.parseJSON(info.response); + ctx = ctx ? ctx : res.ctx; + // if ctx is not empty + // that means the upload strategy is chunk upload + // before the invoke the last_step + // we need request the mkfile to compose all uploaded chunks + // else + // invoke the last_step + logger.debug("ctx: ", ctx); + if (ctx) { + var key = ''; + logger.debug("save_key: ", op.save_key); + if (!op.save_key) { + key = getFileKey(up, file, that.key_handler); + key = key ? '/key/' + that.URLSafeBase64Encode(key) : ''; + } - var imageUrl = 'imageView2/' + mode; - imageUrl += w ? '/w/' + w : ''; - imageUrl += h ? '/h/' + h : ''; - imageUrl += q ? '/q/' + q : ''; - imageUrl += format ? '/format/' + format : ''; - if (key) { - imageUrl = this.getUrl(key) + '?' + imageUrl; - } - return imageUrl; - }; + var fname = '/fname/' + that.URLSafeBase64Encode(file.name); + + logger.debug("op.x_vars: ", op.x_vars); + var x_vars = op.x_vars, + x_val = '', + x_vars_url = ''; + if (x_vars !== undefined && typeof x_vars === 'object') { + for (var x_key in x_vars) { + if (x_vars.hasOwnProperty(x_key)) { + if (typeof x_vars[x_key] === 'function') { + x_val = that.URLSafeBase64Encode(x_vars[x_key](up, file)); + } else if (typeof x_vars[x_key] !== 'object') { + x_val = that.URLSafeBase64Encode(x_vars[x_key]); + } + x_vars_url += '/x:' + x_key + '/' + x_val; + } + } + } - /** - * invoke the imageMogr2 api of Qiniu - * @param {Object} api params - * @param {String} key of file - * @return {String} url of processed image - */ - this.imageMogr2 = function(op, key) { - var auto_orient = op['auto-orient'] || '', - thumbnail = op.thumbnail || '', - strip = op.strip || '', - gravity = op.gravity || '', - crop = op.crop || '', - quality = op.quality || '', - rotate = op.rotate || '', - format = op.format || '', - blur = op.blur || ''; - //Todo check option - - var imageUrl = 'imageMogr2'; - - imageUrl += auto_orient ? '/auto-orient' : ''; - imageUrl += thumbnail ? '/thumbnail/' + thumbnail : ''; - imageUrl += strip ? '/strip' : ''; - imageUrl += gravity ? '/gravity/' + gravity : ''; - imageUrl += quality ? '/quality/' + quality : ''; - imageUrl += crop ? '/crop/' + crop : ''; - imageUrl += rotate ? '/rotate/' + rotate : ''; - imageUrl += format ? '/format/' + format : ''; - imageUrl += blur ? '/blur/' + blur : ''; - - if (key) { - imageUrl = this.getUrl(key) + '?' + imageUrl; - } - return imageUrl; - }; + var url = qiniuUploadUrl + '/mkfile/' + file.size + key + fname + x_vars_url; - /** - * invoke the watermark api of Qiniu - * @param {Object} api params - * @param {String} key of file - * @return {String} url of processed image - */ - this.watermark = function(op, key) { - var mode = op.mode; - if (!mode) { - return false; - } + var ie = that.detectIEVersion(); + var ajax; + if (ie && ie <= 9) { + ajax = new moxie.xhr.XMLHttpRequest(); + moxie.core.utils.Env.swf_url = op.flash_swf_url; + } else { + ajax = that.createAjax(); + } + ajax.open('POST', url, true); + ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8'); + console.log('uptoken:'+that.token); + ajax.setRequestHeader('Authorization', 'UpToken ' + that.token); + var onreadystatechange = function () { + logger.debug("ajax.readyState: ", ajax.readyState); + if (ajax.readyState === 4) { + localStorage.removeItem(file.name); + var ajaxInfo; + if (ajax.status === 200) { + ajaxInfo = { + status: ajax.status, + response: ajax.responseText, + responseHeaders: ajax.getAllResponseHeaders(), + }; + logger.debug("mkfile is success: ", ajaxInfo); + last_step(up, file, ajaxInfo); + } else { + ajaxInfo = { + status: ajax.status, + response: ajax.responseText, + file: file, + code: -200, + responseHeaders: ajax.getAllResponseHeaders() + }; + logger.debug("mkfile is error: ", ajaxInfo); + uploader.trigger('Error', ajaxInfo); + } + } + }; + if (ie && ie <= 9) { + ajax.bind('readystatechange', onreadystatechange); + } else { + ajax.onreadystatechange = onreadystatechange; + } + ajax.send(ctx); + logger.debug("mkfile: ", url); + } else { + last_step(up, file, info); + } - var imageUrl = 'watermark/' + mode; + // send statistics log + if (!op.disable_statistics_report) { + console.log(123); + console.log(info.responseHeaders); + var req_id = info.responseHeaders.match(/(X-Reqid\:\ )([\w\.\%-]*)/)[2]; + var startAt = file._start_at ? file._start_at.getTime() : nowTime.getTime(); + statisticsLogger.log( + info.status, + req_id, + getDomainFromUrl(up.settings.url), + undefined, + getPortFromUrl(up.settings.url), + (nowTime.getTime() - startAt)/1000, + startAt/1000, + file.size, + "jssdk-" + up.runtime, + file.size + ); + } + }; + })(_FileUploaded_Handler)); + + logger.debug("bind FileUploaded event"); + + // bind 'FilesRemoved' event + // intercept the cancel of upload + // used to send statistics log to server + uploader.bind('FilesRemoved', function (up, files) { + var nowTime = new Date(); + // add cancel log + if (!op.disable_statistics_report) { + for (var i = 0; i < files.length; i++) { + statisticsLogger.log( + ExtraErrors.Cancelled, + undefined, + getDomainFromUrl(up.settings.url), + undefined, + getPortFromUrl(up.settings.url), + (nowTime.getTime() - files[i]._start_at.getTime())/1000, + files[i]._start_at.getTime()/1000, + files[i].size * files[i].percent / 100, + "jssdk-" + up.runtime, + files[i].size + ); + } + } + }); + + logger.debug("bind FilesRemoved event"); + + // init uploader + uploader.init(); + logger.debug("invoke uploader.init()"); - if (mode === 1) { - var image = op.image || ''; - if (!image) { + logger.debug("init uploader end"); + + return uploader; + }; + + /** + * get url by key + * @param {String} key of file + * @return {String} url of file + */ + this.getUrl = function (key) { + if (!key) { return false; } - imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : ''; - } else if (mode === 2) { - var text = op.text ? op.text : '', - font = op.font ? op.font : '', - fontsize = op.fontsize ? op.fontsize : '', - fill = op.fill ? op.fill : ''; - if (!text) { - return false; + key = encodeURI(key); + var domain = this.domain; + if (domain.slice(domain.length - 1) !== '/') { + domain = domain + '/'; } - imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : ''; - imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : ''; - imageUrl += fontsize ? '/fontsize/' + fontsize : ''; - imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : ''; - } else { - // Todo mode3 - return false; - } + return domain + key; + }; - var dissolve = op.dissolve || '', - gravity = op.gravity || '', - dx = op.dx || '', - dy = op.dy || ''; + /** + * invoke the imageView2 api of Qiniu + * @param {Object} api params + * @param {String} key of file + * @return {String} url of processed image + */ + this.imageView2 = function (op, key) { - imageUrl += dissolve ? '/dissolve/' + dissolve : ''; - imageUrl += gravity ? '/gravity/' + gravity : ''; - imageUrl += dx ? '/dx/' + dx : ''; - imageUrl += dy ? '/dy/' + dy : ''; + if (!/^\d$/.test(op.mode)) { + return false; + } - if (key) { - imageUrl = this.getUrl(key) + '?' + imageUrl; - } - return imageUrl; - }; + var mode = op.mode, + w = op.w || '', + h = op.h || '', + q = op.q || '', + format = op.format || ''; - /** - * invoke the imageInfo api of Qiniu - * @param {String} key of file - * @return {Object} image info - */ - this.imageInfo = function(key) { - if (!key) { - return false; - } - var url = this.getUrl(key) + '?imageInfo'; - var xhr = this.createAjax(); - var info; - var that = this; - xhr.open('GET', url, false); - xhr.onreadystatechange = function() { - if (xhr.readyState === 4 && xhr.status === 200) { - info = that.parseJSON(xhr.responseText); + if (!w && !h) { + return false; } + + var imageUrl = 'imageView2/' + mode; + imageUrl += w ? '/w/' + w : ''; + imageUrl += h ? '/h/' + h : ''; + imageUrl += q ? '/q/' + q : ''; + imageUrl += format ? '/format/' + format : ''; + if (key) { + imageUrl = this.getUrl(key) + '?' + imageUrl; + } + return imageUrl; }; - xhr.send(); - return info; - }; - /** - * invoke the exif api of Qiniu - * @param {String} key of file - * @return {Object} image exif - */ - this.exif = function(key) { - if (!key) { - return false; - } - var url = this.getUrl(key) + '?exif'; - var xhr = this.createAjax(); - var info; - var that = this; - xhr.open('GET', url, false); - xhr.onreadystatechange = function() { - if (xhr.readyState === 4 && xhr.status === 200) { - info = that.parseJSON(xhr.responseText); + /** + * invoke the imageMogr2 api of Qiniu + * @param {Object} api params + * @param {String} key of file + * @return {String} url of processed image + */ + this.imageMogr2 = function (op, key) { + var auto_orient = op['auto-orient'] || '', + thumbnail = op.thumbnail || '', + strip = op.strip || '', + gravity = op.gravity || '', + crop = op.crop || '', + quality = op.quality || '', + rotate = op.rotate || '', + format = op.format || '', + blur = op.blur || ''; + //Todo check option + + var imageUrl = 'imageMogr2'; + + imageUrl += auto_orient ? '/auto-orient' : ''; + imageUrl += thumbnail ? '/thumbnail/' + thumbnail : ''; + imageUrl += strip ? '/strip' : ''; + imageUrl += gravity ? '/gravity/' + gravity : ''; + imageUrl += quality ? '/quality/' + quality : ''; + imageUrl += crop ? '/crop/' + crop : ''; + imageUrl += rotate ? '/rotate/' + rotate : ''; + imageUrl += format ? '/format/' + format : ''; + imageUrl += blur ? '/blur/' + blur : ''; + + if (key) { + imageUrl = this.getUrl(key) + '?' + imageUrl; } + return imageUrl; }; - xhr.send(); - return info; - }; - /** - * invoke the exif or imageInfo api of Qiniu - * according with type param - * @param {String} ['exif'|'imageInfo']type of info - * @param {String} key of file - * @return {Object} image exif or info - */ - this.get = function(type, key) { - if (!key || !type) { - return false; - } - if (type === 'exif') { - return this.exif(key); - } else if (type === 'imageInfo') { - return this.imageInfo(key); - } - return false; - }; + /** + * invoke the watermark api of Qiniu + * @param {Object} api params + * @param {String} key of file + * @return {String} url of processed image + */ + this.watermark = function (op, key) { + var mode = op.mode; + if (!mode) { + return false; + } - /** - * invoke api of Qiniu like a pipeline - * @param {Array of Object} params of a series api call - * each object in array is options of api which name is set as 'fop' property - * each api's output will be next api's input - * @param {String} key of file - * @return {String|Boolean} url of processed image - */ - this.pipeline = function(arr, key) { - var isArray = Object.prototype.toString.call(arr) === '[object Array]'; - var option, errOp, imageUrl = ''; - if (isArray) { - for (var i = 0, len = arr.length; i < len; i++) { - option = arr[i]; - if (!option.fop) { + var imageUrl = 'watermark/' + mode; + + if (mode === 1) { + var image = op.image || ''; + if (!image) { return false; } - switch (option.fop) { - case 'watermark': - imageUrl += this.watermark(option) + '|'; - break; - case 'imageView2': - imageUrl += this.imageView2(option) + '|'; - break; - case 'imageMogr2': - imageUrl += this.imageMogr2(option) + '|'; - break; - default: - errOp = true; - break; - } - if (errOp) { + imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : ''; + } else if (mode === 2) { + var text = op.text ? op.text : '', + font = op.font ? op.font : '', + fontsize = op.fontsize ? op.fontsize : '', + fill = op.fill ? op.fill : ''; + if (!text) { return false; } + imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : ''; + imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : ''; + imageUrl += fontsize ? '/fontsize/' + fontsize : ''; + imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : ''; + } else { + // Todo mode3 + return false; } + + var dissolve = op.dissolve || '', + gravity = op.gravity || '', + dx = op.dx || '', + dy = op.dy || ''; + + imageUrl += dissolve ? '/dissolve/' + dissolve : ''; + imageUrl += gravity ? '/gravity/' + gravity : ''; + imageUrl += dx ? '/dx/' + dx : ''; + imageUrl += dy ? '/dy/' + dy : ''; + if (key) { imageUrl = this.getUrl(key) + '?' + imageUrl; - var length = imageUrl.length; - if (imageUrl.slice(length - 1) === '|') { - imageUrl = imageUrl.slice(0, length - 1); - } } return imageUrl; - } - return false; - }; -} + }; + + /** + * invoke the imageInfo api of Qiniu + * @param {String} key of file + * @return {Object} image info + */ + this.imageInfo = function (key) { + if (!key) { + return false; + } + var url = this.getUrl(key) + '?imageInfo'; + var xhr = this.createAjax(); + var info; + var that = this; + xhr.open('GET', url, false); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + info = that.parseJSON(xhr.responseText); + } + }; + xhr.send(); + return info; + }; + + /** + * invoke the exif api of Qiniu + * @param {String} key of file + * @return {Object} image exif + */ + this.exif = function (key) { + if (!key) { + return false; + } + var url = this.getUrl(key) + '?exif'; + var xhr = this.createAjax(); + var info; + var that = this; + xhr.open('GET', url, false); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + info = that.parseJSON(xhr.responseText); + } + }; + xhr.send(); + return info; + }; + + /** + * invoke the exif or imageInfo api of Qiniu + * according with type param + * @param {String} ['exif'|'imageInfo']type of info + * @param {String} key of file + * @return {Object} image exif or info + */ + this.get = function (type, key) { + if (!key || !type) { + return false; + } + if (type === 'exif') { + return this.exif(key); + } else if (type === 'imageInfo') { + return this.imageInfo(key); + } + return false; + }; -var Qiniu = new QiniuJsSDK(); + /** + * invoke api of Qiniu like a pipeline + * @param {Array of Object} params of a series api call + * each object in array is options of api which name is set as 'fop' property + * each api's output will be next api's input + * @param {String} key of file + * @return {String|Boolean} url of processed image + */ + this.pipeline = function (arr, key) { + var isArray = Object.prototype.toString.call(arr) === '[object Array]'; + var option, errOp, imageUrl = ''; + if (isArray) { + for (var i = 0, len = arr.length; i < len; i++) { + option = arr[i]; + if (!option.fop) { + return false; + } + switch (option.fop) { + case 'watermark': + imageUrl += this.watermark(option) + '|'; + break; + case 'imageView2': + imageUrl += this.imageView2(option) + '|'; + break; + case 'imageMogr2': + imageUrl += this.imageMogr2(option) + '|'; + break; + default: + errOp = true; + break; + } + if (errOp) { + return false; + } + } + if (key) { + imageUrl = this.getUrl(key) + '?' + imageUrl; + var length = imageUrl.length; + if (imageUrl.slice(length - 1) === '|') { + imageUrl = imageUrl.slice(0, length - 1); + } + } + return imageUrl; + } + return false; + }; + } -global.Qiniu = Qiniu; + var Qiniu = new QiniuJsSDK(); -global.QiniuJsSDK = QiniuJsSDK; + global.Qiniu = Qiniu; + global.QiniuJsSDK = QiniuJsSDK; -})( window ); +})(window); \ No newline at end of file diff --git a/dist/qiniu.min.js b/dist/qiniu.min.js index 96a9c0b7..d7098593 100644 --- a/dist/qiniu.min.js +++ b/dist/qiniu.min.js @@ -1,3 +1,3 @@ -/*! qiniu-js v1.0.17.1 | Copyright 2015 by Qiniu */ -!function(global){function createCookie(a,b,c){var d=new Date;d.setTime(d.getTime()+24*c*60*60*1e3);var e="; expires="+d.toGMTString();document.cookie=a+"="+b+e+"; path=/"}function readCookie(a){var b=a+"=";var c=document.cookie.split(";");for(var d=0,e=c.length;e>d;d++){var f=c[d];while(" "===f.charAt(0))f=f.substring(1,f.length);if(0===f.indexOf(b))return f.substring(b.length,f.length)}return null}window.localStorage||(window.localStorage={setItem:function(a,b){createCookie(a,b,30)},getItem:function(a){return readCookie(a)},removeItem:function(a){createCookie(a,"",-1)}});function QiniuJsSDK(){var that=this;this.detectIEVersion=function(){var a=4,b=document.createElement("div"),c=b.getElementsByTagName("i");while(b.innerHTML="",c[0])a++;return a>4?a:!1};var logger={MUTE:0,FATA:1,ERROR:2,WARN:3,INFO:4,DEBUG:5,TRACE:6,level:0};function log(a,b){var c="[qiniu-js-sdk]["+a+"]";var d=c;for(var e=0;e")}function makeLogFunc(a){var b=a.toLowerCase();logger[b]=function(){if(window.console&&window.console.log&&logger.level>=logger[a]){var c=Array.prototype.slice.call(arguments);log(b,c)}}}for(var property in logger)logger.hasOwnProperty(property)&&"number"==typeof logger[property]&&!logger.hasOwnProperty(property.toLowerCase())&&makeLogFunc(property);var qiniuUploadUrl;qiniuUploadUrl="https:"===window.location.protocol?"https://up.qbox.me":"http://upload.qiniu.com";var qiniuUploadUrls=["http://upload.qiniu.com","http://up.qiniu.com"];var qiniuUpHosts={http:["http://upload.qiniu.com","http://up.qiniu.com"],https:["https://up.qbox.me"]};var changeUrlTimes=0;this.resetUploadUrl=function(){var a="https:"===window.location.protocol?qiniuUpHosts.https:qiniuUpHosts.http;var b=changeUrlTimes%a.length;qiniuUploadUrl=a[b],changeUrlTimes++,logger.debug("resetUploadUrl: "+qiniuUploadUrl)},this.isImage=function(a){return a=a.split(/[?#]/)[0],/\.(png|jpg|jpeg|gif|bmp)$/i.test(a)},this.getFileExtension=function(a){var b=a.split(".");var c;return c=1===b.length||""===b[0]&&2===b.length?"":b.pop().toLowerCase()},this.utf8_encode=function(a){if(null===a||"undefined"==typeof a)return"";var b=a+"";var c="",d,e,f=0;d=e=0,f=b.length;for(var g=0;f>g;g++){var h=b.charCodeAt(g);var i=null;if(128>h)e++;else if(h>127&&2048>h)i=String.fromCharCode(h>>6|192,63&h|128);else if(63488&h^!0)i=String.fromCharCode(h>>12|224,h>>6&63|128,63&h|128);else{if(64512&h^!0)throw new RangeError("Unmatched trail surrogate at "+g);var j=b.charCodeAt(++g);if(64512&j^!0)throw new RangeError("Unmatched lead surrogate at "+(g-1));h=((1023&h)<<10)+(1023&j)+65536,i=String.fromCharCode(h>>18|240,h>>12&63|128,h>>6&63|128,63&h|128)}null!==i&&(e>d&&(c+=b.slice(d,e)),c+=i,d=e=g+1)}return e>d&&(c+=b.slice(d,f)),c},this.base64_decode=function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var c,d,e,f,g,h,i,j,k=0,l=0,m="",n=[];if(!a)return a;a+="";do f=b.indexOf(a.charAt(k++)),g=b.indexOf(a.charAt(k++)),h=b.indexOf(a.charAt(k++)),i=b.indexOf(a.charAt(k++)),j=f<<18|g<<12|h<<6|i,c=j>>16&255,d=j>>8&255,e=255&j,64===h?n[l++]=String.fromCharCode(c):64===i?n[l++]=String.fromCharCode(c,d):n[l++]=String.fromCharCode(c,d,e);while(k>18&63,g=j>>12&63,h=j>>6&63,i=63&j,n[l++]=b.charAt(f)+b.charAt(g)+b.charAt(h)+b.charAt(i);while(kd;d++)b.push(that.stringifyJSON(a[d]));return"["+b.join(",")+"]"}if(null===a)return"null";var e=[];for(var f in a)a.hasOwnProperty(f)&&e.push(that.stringifyJSON(f)+":"+that.stringifyJSON(a[f]));return"{"+e.join(",")+"}";case"number":return a;case!1:return a;case"boolean":return a}},this.trim=function(a){return null===a?"":a.replace(/^\s+|\s+$/g,"")},this.uploader=function(a){var b=function(){var b=that.detectIEVersion();var c,d,e;var f="Safari"===mOxie.Env.browser&&mOxie.Env.version<=5&&"Windows"===mOxie.Env.os&&"7"===mOxie.Env.osVersion||"Safari"===mOxie.Env.browser&&"iOS"===mOxie.Env.os&&"7"===mOxie.Env.osVersion;b&&9>b&&a.chunk_size&&a.runtimes.indexOf("flash")>=0?a.chunk_size=0:f?a.chunk_size=0:(c=20,d=4<d&&(a.chunk_size=d))};var c=function(a){var b=[];for(var c=0;c=0?(d.bucket=d.scope.split(":")[0],d.key=d.scope.split(":")[1]):d.bucket=d.scope,d};var e=function(b){var e=d(b);var f=window.location.protocol+"//uc.qbox.me/v1/query?ak="+e.ak+"&bucket="+e.bucket;logger.debug("putPolicy: ",e),logger.debug("get uphosts from: ",f);var g=that.detectIEVersion();var h;g&&9>=g?(h=new mOxie.XMLHttpRequest,mOxie.Env.swf_url=a.flash_swf_url):h=that.createAjax(),h.open("GET",f,!0);var i=function(){if(logger.debug("ajax.readyState: ",h.readyState),4===h.readyState)if(logger.debug("ajax.status: ",h.status),h.status<400){var a=that.parseJSON(h.responseText);qiniuUpHosts.http=c(a.http.up),qiniuUpHosts.https=c(a.https.up),logger.debug("get new uphosts: ",qiniuUpHosts),that.resetUploadUrl()}else logger.error("get uphosts error: ",h.responseText)};g&&9>=g?h.bind("readystatechange",i):h.onreadystatechange=i,h.send()};var f=function(b){return!that.token||a.uptoken_url&&that.tokenInfo.isExpired()?g(b):that.token};var g=function(b){if(a.uptoken)that.token=a.uptoken;else if(a.uptoken_url){logger.debug("get uptoken from: ",that.uptoken_url);var c=that.createAjax();if(c.open("GET",that.uptoken_url,!1),c.setRequestHeader("If-Modified-Since","0"),c.send(),200===c.status){var d=that.parseJSON(c.responseText);that.token=d.uptoken;var f=that.token.split(":");var g=that.parseJSON(that.URLSafeBase64Decode(f[2]));that.tokenMap||(that.tokenMap={});var h=function(a){return Math.ceil(a.getTime()/1e3)};var i=h(new Date(c.getResponseHeader("date")));var j=h(new Date);that.tokenInfo={serverDelay:j-i,deadline:g.deadline,isExpired:function(){var a=this.deadline-h(new Date)+this.serverDelay;return 600>a}},logger.debug("get new uptoken: ",that.token),logger.debug("get token info: ",that.tokenInfo)}else logger.error("get uptoken error: ",c.responseText)}else a.uptoken_func?(logger.debug("get uptoken from uptoken_func"),that.token=a.uptoken_func(b),logger.debug("get new uptoken: ",that.token)):logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!");return that.token&&e(that.token),that.token};var h=function(b,c,d){var e="",f=!1;if(!a.save_key)if(f=b.getOption&&b.getOption("unique_names"),f=f||b.settings&&b.settings.unique_names){var g=that.getFileExtension(c.name);e=g?c.id+"."+g:c.id}else e="function"==typeof d?d(b,c):c.name;return e};if(a.log_level&&(logger.level=a.log_level),!a.domain)throw"domain setting in options is required!";if(!a.browse_button)throw"browse_button setting in options is required!";if(!a.uptoken&&!a.uptoken_url&&!a.uptoken_func)throw"one of [uptoken, uptoken_url, uptoken_func] settings in options is required!";logger.debug("init uploader start"),logger.debug("environment: ",mOxie.Env),logger.debug("userAgent: ",navigator.userAgent);var i={};var j=a.init&&a.init.Error;var k=a.init&&a.init.FileUploaded;a.init.Error=function(){},a.init.FileUploaded=function(){},that.uptoken_url=a.uptoken_url,that.token="",that.key_handler="function"==typeof a.init.Key?a.init.Key:"",this.domain=a.domain;var l="";var m={isResumeUpload:!1,resumeFilesize:0,startTime:"",currentTime:""};b(),logger.debug("invoke reset_chunk_size()"),logger.debug("op.chunk_size: ",a.chunk_size);var n={url:qiniuUploadUrl,multipart_params:{token:""}};var o=that.detectIEVersion();o&&9>=o&&(n.multipart_params.accept="text/plain; charset=utf-8",logger.debug("add accept text/plain in multipart params")),plupload.extend(i,a,n),logger.debug("option: ",i);var p=new plupload.Uploader(i);logger.debug("new plupload.Uploader(option)"),p.bind("Init",function(b,c){logger.debug("Init event activated"),a.get_new_uptoken||g(null)}),logger.debug("bind Init event"),p.bind("FilesAdded",function(a,b){logger.debug("FilesAdded event activated");var c=a.getOption&&a.getOption("auto_start");c=c||a.settings&&a.settings.auto_start,logger.debug("auto_start: ",c),logger.debug("files: ",b);var d=function(){return"ios"===mOxie.Env.OS.toLowerCase()?!0:!1};if(d())for(var e=0;e=g&&(f.accept="text/plain; charset=utf-8",logger.debug("add accept text/plain in multipart params")),logger.debug("directUpload multipart_params_obj: ",f);var i=a.x_vars;if(void 0!==i&&"object"==typeof i)for(var j in i)i.hasOwnProperty(j)&&("function"==typeof i[j]?f["x:"+j]=i[j](b,c):"object"!=typeof i[j]&&(f["x:"+j]=i[j]));b.setOption({url:qiniuUploadUrl,multipart:!0,chunk_size:e()?a.max_file_size:void 0,multipart_params:f})};var e=function(){var a=navigator.userAgent.toLowerCase();return(a.match(/MicroMessenger/i)||"QQBrowser"===mOxie.Env.browser||a.match(/V1_AND_SQ/i))&&"android"===mOxie.Env.OS.toLowerCase()?!0:!1};var i=b.getOption&&b.getOption("chunk_size");if(i=i||b.settings&&b.settings.chunk_size,logger.debug("uploader.runtime: ",p.runtime),logger.debug("chunk_size: ",i),"html5"!==p.runtime&&"flash"!==p.runtime||!i)logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size"),d(b,c,that.key_handler);else if(c.sizen-o&&100!==j.percent&&c.size===j.total?(c.percent=j.percent,c.loaded=j.offset,l=j.ctx,m.isResumeUpload=!0,m.resumeFilesize=j.offset,j.offset+k>c.size&&(k=c.size-j.offset)):localStorage.removeItem(c.name)}m.startTime=(new Date).getTime();var r={};var s=that.detectIEVersion();s&&9>=s&&(r.accept="text/plain; charset=utf-8",logger.debug("add accept text/plain in multipart params")),b.setOption({url:qiniuUploadUrl+"/mkblk/"+k,multipart:!1,chunk_size:i,required_features:"chunks",headers:{Authorization:"UpToken "+f(c)},multipart_params:r})}}),logger.debug("bind BeforeUpload event"),p.bind("UploadProgress",function(a,b){logger.trace("UploadProgress event activated"),m.currentTime=(new Date).getTime();var c=m.currentTime-m.startTime;var d=b.loaded||0;m.isResumeUpload&&(d=b.loaded-m.resumeFilesize),b.speed=(d/c*1e3).toFixed(0)||0}),logger.debug("bind UploadProgress event"),p.bind("ChunkUploaded",function(a,b,c){logger.debug("ChunkUploaded event activated"),logger.debug("file: ",b),logger.debug("info: ",c);var d=that.parseJSON(c.response);logger.debug("res: ",d),l=l?l+","+d.ctx:d.ctx;var e=c.total-c.offset;var g=a.getOption&&a.getOption("chunk_size");g=g||a.settings&&a.settings.chunk_size,g>e&&(a.setOption({url:qiniuUploadUrl+"/mkblk/"+e}),logger.debug("up.setOption url: ",qiniuUploadUrl+"/mkblk/"+e)),a.setOption({headers:{Authorization:"UpToken "+f(b)}}),localStorage.setItem(b.name,that.stringifyJSON({ctx:l,percent:b.percent,total:c.total,offset:c.offset,time:(new Date).getTime()}))}),logger.debug("bind ChunkUploaded event");var q=qiniuUploadUrls.length;var r=function(a){return q-->0?(setTimeout(function(){that.resetUploadUrl(),a.status=plupload.QUEUED,p.stop(),p.start()},0),!0):(q=qiniuUploadUrls.length,!1)};return p.bind("Error",function(a){return function(b,c){logger.error("Error event activated"),logger.error("err: ",c);var d="";var e=c.file;if(e){switch(c.code){case plupload.FAILED:d="\u4e0a\u4f20\u5931\u8d25\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002";break;case plupload.FILE_SIZE_ERROR:var f=b.getOption&&b.getOption("max_file_size");f=f||b.settings&&b.settings.max_file_size,d="\u6d4f\u89c8\u5668\u6700\u5927\u53ef\u4e0a\u4f20"+f+"\u3002\u66f4\u5927\u6587\u4ef6\u8bf7\u4f7f\u7528\u547d\u4ee4\u884c\u5de5\u5177\u3002";break;case plupload.FILE_EXTENSION_ERROR:d="\u6587\u4ef6\u9a8c\u8bc1\u5931\u8d25\u3002\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002";break;case plupload.HTTP_ERROR:if(""===c.response){if(d=c.message||"\u672a\u77e5\u7f51\u7edc\u9519\u8bef\u3002",!r(e))return;break}var g=that.parseJSON(c.response);var h=g.error;switch(c.status){case 400:d="\u8bf7\u6c42\u62a5\u6587\u683c\u5f0f\u9519\u8bef\u3002";break;case 401:d="\u5ba2\u6237\u7aef\u8ba4\u8bc1\u6388\u6743\u5931\u8d25\u3002\u8bf7\u91cd\u8bd5\u6216\u63d0\u4ea4\u53cd\u9988\u3002";break;case 405:d="\u5ba2\u6237\u7aef\u8bf7\u6c42\u9519\u8bef\u3002\u8bf7\u91cd\u8bd5\u6216\u63d0\u4ea4\u53cd\u9988\u3002";break;case 579:d="\u8d44\u6e90\u4e0a\u4f20\u6210\u529f\uff0c\u4f46\u56de\u8c03\u5931\u8d25\u3002";break;case 599:if(d="\u7f51\u7edc\u8fde\u63a5\u5f02\u5e38\u3002\u8bf7\u91cd\u8bd5\u6216\u63d0\u4ea4\u53cd\u9988\u3002",!r(e))return;break;case 614:d="\u6587\u4ef6\u5df2\u5b58\u5728\u3002";try{g=that.parseJSON(g.error),h=g.error||"file exists"}catch(i){h=g.error||"file exists"}break;case 631:d="\u6307\u5b9a\u7a7a\u95f4\u4e0d\u5b58\u5728\u3002";break;case 701:d="\u4e0a\u4f20\u6570\u636e\u5757\u6821\u9a8c\u51fa\u9519\u3002\u8bf7\u91cd\u8bd5\u6216\u63d0\u4ea4\u53cd\u9988\u3002";break;default:if(d="\u672a\u77e5\u9519\u8bef\u3002",!r(e))return}d=d+"("+c.status+"\uff1a"+h+")";break;case plupload.SECURITY_ERROR:d="\u5b89\u5168\u914d\u7f6e\u9519\u8bef\u3002\u8bf7\u8054\u7cfb\u7f51\u7ad9\u7ba1\u7406\u5458\u3002";break;case plupload.GENERIC_ERROR:d="\u4e0a\u4f20\u5931\u8d25\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002";break;case plupload.IO_ERROR:d="\u4e0a\u4f20\u5931\u8d25\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002";break;case plupload.INIT_ERROR:d="\u7f51\u7ad9\u914d\u7f6e\u9519\u8bef\u3002\u8bf7\u8054\u7cfb\u7f51\u7ad9\u7ba1\u7406\u5458\u3002",p.destroy();break;default:if(d=c.message+c.details,!r(e))return}a&&a(b,c,d)}b.refresh()}}(j)),logger.debug("bind Error event"),p.bind("FileUploaded",function(b){return function(c,d,e){logger.debug("FileUploaded event activated"),logger.debug("file: ",d),logger.debug("info: ",e);var f=function(c,d,e){if(a.downtoken_url){var f=that.createAjax();f.open("POST",a.downtoken_url,!0),f.setRequestHeader("Content-type","application/x-www-form-urlencoded"),f.onreadystatechange=function(){if(4===f.readyState)if(200===f.status){var a;try{a=that.parseJSON(f.responseText)}catch(g){throw"invalid json format"}var h={};plupload.extend(h,that.parseJSON(e),a),b&&b(c,d,that.stringifyJSON(h))}else p.trigger("Error",{status:f.status,response:f.responseText,file:d,code:plupload.HTTP_ERROR})},f.send("key="+that.parseJSON(e).key+"&domain="+a.domain)}else b&&b(c,d,e)};var g=that.parseJSON(e.response);if(l=l?l:g.ctx,logger.debug("ctx: ",l),l){var i="";logger.debug("save_key: ",a.save_key),a.save_key||(i=h(c,d,that.key_handler),i=i?"/key/"+that.URLSafeBase64Encode(i):"");var j="/fname/"+that.URLSafeBase64Encode(d.name);logger.debug("op.x_vars: ",a.x_vars);var k=a.x_vars,m="",n="";if(void 0!==k&&"object"==typeof k)for(var o in k)k.hasOwnProperty(o)&&("function"==typeof k[o]?m=that.URLSafeBase64Encode(k[o](c,d)):"object"!=typeof k[o]&&(m=that.URLSafeBase64Encode(k[o])),n+="/x:"+o+"/"+m);var q=qiniuUploadUrl+"/mkfile/"+d.size+i+j+n;var r=that.detectIEVersion();var s;r&&9>=r?(s=new mOxie.XMLHttpRequest,mOxie.Env.swf_url=a.flash_swf_url):s=that.createAjax(),s.open("POST",q,!0),s.setRequestHeader("Content-Type","text/plain;charset=UTF-8"),s.setRequestHeader("Authorization","UpToken "+that.token);var t=function(){if(logger.debug("ajax.readyState: ",s.readyState),4===s.readyState){localStorage.removeItem(d.name);var a;200===s.status?(a=s.responseText,logger.debug("mkfile is success: ",a),f(c,d,a)):(a={status:s.status,response:s.responseText,file:d,code:-200},logger.debug("mkfile is error: ",a),p.trigger("Error",a))}};r&&9>=r?s.bind("readystatechange",t):s.onreadystatechange=t,s.send(l),logger.debug("mkfile: ",q)}else f(c,d,e.response)}}(k)),logger.debug("bind FileUploaded event"),p.init(),logger.debug("invoke uploader.init()"),logger.debug("init uploader end"),p},this.getUrl=function(a){if(!a)return!1;a=encodeURI(a);var b=this.domain;return"/"!==b.slice(b.length-1)&&(b+="/"),b+a},this.imageView2=function(a,b){var c=a.mode||"",d=a.w||"",e=a.h||"",f=a.q||"",g=a.format||"";if(!c)return!1;if(!d&&!e)return!1;var h="imageView2/"+c;return h+=d?"/w/"+d:"",h+=e?"/h/"+e:"",h+=f?"/q/"+f:"",h+=g?"/format/"+g:"",b&&(h=this.getUrl(b)+"?"+h),h},this.imageMogr2=function(a,b){var c=a["auto-orient"]||"",d=a.thumbnail||"",e=a.strip||"",f=a.gravity||"",g=a.crop||"",h=a.quality||"",i=a.rotate||"",j=a.format||"",k=a.blur||"";var l="imageMogr2";return l+=c?"/auto-orient":"",l+=d?"/thumbnail/"+d:"",l+=e?"/strip":"",l+=f?"/gravity/"+f:"",l+=h?"/quality/"+h:"",l+=g?"/crop/"+g:"",l+=i?"/rotate/"+i:"",l+=j?"/format/"+j:"",l+=k?"/blur/"+k:"",b&&(l=this.getUrl(b)+"?"+l),l},this.watermark=function(a,b){var c=a.mode;if(!c)return!1;var d="watermark/"+c;if(1===c){var e=a.image||"";if(!e)return!1;d+=e?"/image/"+this.URLSafeBase64Encode(e):""}else{if(2!==c)return!1;var f=a.text?a.text:"",g=a.font?a.font:"",h=a.fontsize?a.fontsize:"",i=a.fill?a.fill:"";if(!f)return!1;d+=f?"/text/"+this.URLSafeBase64Encode(f):"",d+=g?"/font/"+this.URLSafeBase64Encode(g):"",d+=h?"/fontsize/"+h:"",d+=i?"/fill/"+this.URLSafeBase64Encode(i):""}var j=a.dissolve||"",k=a.gravity||"",l=a.dx||"",m=a.dy||"";return d+=j?"/dissolve/"+j:"",d+=k?"/gravity/"+k:"",d+=l?"/dx/"+l:"",d+=m?"/dy/"+m:"",b&&(d=this.getUrl(b)+"?"+d),d},this.imageInfo=function(a){if(!a)return!1;var b=this.getUrl(a)+"?imageInfo";var c=this.createAjax();var d;var e=this;return c.open("GET",b,!1),c.onreadystatechange=function(){4===c.readyState&&200===c.status&&(d=e.parseJSON(c.responseText))},c.send(),d},this.exif=function(a){if(!a)return!1;var b=this.getUrl(a)+"?exif";var c=this.createAjax();var d;var e=this;return c.open("GET",b,!1),c.onreadystatechange=function(){4===c.readyState&&200===c.status&&(d=e.parseJSON(c.responseText))},c.send(),d},this.get=function(a,b){return b&&a?"exif"===a?this.exif(b):"imageInfo"===a?this.imageInfo(b):!1:!1},this.pipeline=function(a,b){var c="[object Array]"===Object.prototype.toString.call(a);var d,e,f="";if(c){for(var g=0,h=a.length;h>g;g++){if(d=a[g],!d.fop)return!1;switch(d.fop){case"watermark":f+=this.watermark(d)+"|";break;case"imageView2":f+=this.imageView2(d)+"|";break;case"imageMogr2":f+=this.imageMogr2(d)+"|";break;default:e=!0}if(e)return!1}if(b){f=this.getUrl(b)+"?"+f;var i=f.length;"|"===f.slice(i-1)&&(f=f.slice(0,i-1))}return f}return!1}}var Qiniu=new QiniuJsSDK;global.Qiniu=Qiniu,global.QiniuJsSDK=QiniuJsSDK}(window); +/*! qiniu-js v1.0.21 | Copyright 2015 by Qiniu */ +!function(global){function createCookie(a,b,c){var d=new Date;d.setTime(d.getTime()+24*c*60*60*1e3);var e="; expires="+d.toGMTString();document.cookie=a+"="+b+e+"; path=/"}function readCookie(a){var b=a+"=";var c=document.cookie.split(";");for(var d=0,e=c.length;e>d;d++){var f=c[d];while(" "===f.charAt(0))f=f.substring(1,f.length);if(0===f.indexOf(b))return f.substring(b.length,f.length)}return null}window.localStorage||(window.localStorage={setItem:function(a,b){createCookie(a,b,30)},getItem:function(a){return readCookie(a)},removeItem:function(a){createCookie(a,"",-1)}});function QiniuJsSDK(){var that=this;this.detectIEVersion=function(){var a=4,b=document.createElement("div"),c=b.getElementsByTagName("i");while(b.innerHTML="",c[0])a++;return a>4?a:!1};var logger={MUTE:0,FATA:1,ERROR:2,WARN:3,INFO:4,DEBUG:5,TRACE:6,level:0};function log(a,b){var c="[qiniu-js-sdk]["+a+"]";var d=c;for(var e=0;e")}function makeLogFunc(a){var b=a.toLowerCase();logger[b]=function(){if(window.console&&window.console.log&&logger.level>=logger[a]){var c=Array.prototype.slice.call(arguments);log(b,c)}}}for(var property in logger)logger.hasOwnProperty(property)&&"number"==typeof logger[property]&&!logger.hasOwnProperty(property.toLowerCase())&&makeLogFunc(property);var qiniuUploadUrl;qiniuUploadUrl="https:"===window.location.protocol?"https://up.qbox.me":"http://upload.qiniu.com";var qiniuUploadUrls=["http://upload.qiniu.com","http://up.qiniu.com"];var qiniuUpHosts={http:["http://upload.qiniu.com","http://up.qiniu.com"],https:["https://up.qbox.me"]};var changeUrlTimes=0;function StatisticsLogger(){var a="https://uplog.qbox.me/log/3";var b=[];var c={waiting:0,processing:1,finished:2};this.log=function(a,d,e,f,g,h,i,j,k,l){var m=Array.prototype.join.call(arguments,",");b.push({log:m,status:c.waiting}),logger.debug("[STATISTICS] send log to statistics server",m)};function d(){var a=[];for(var d=0;dg;g++){var h=b.charCodeAt(g);var i=null;if(128>h)e++;else if(h>127&&2048>h)i=String.fromCharCode(h>>6|192,63&h|128);else if(63488&h^!0)i=String.fromCharCode(h>>12|224,h>>6&63|128,63&h|128);else{if(64512&h^!0)throw new RangeError("Unmatched trail surrogate at "+g);var j=b.charCodeAt(++g);if(64512&j^!0)throw new RangeError("Unmatched lead surrogate at "+(g-1));h=((1023&h)<<10)+(1023&j)+65536,i=String.fromCharCode(h>>18|240,h>>12&63|128,h>>6&63|128,63&h|128)}null!==i&&(e>d&&(c+=b.slice(d,e)),c+=i,d=e=g+1)}return e>d&&(c+=b.slice(d,f)),c},this.base64_decode=function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var c,d,e,f,g,h,i,j,k=0,l=0,m="",n=[];if(!a)return a;a+="";do f=b.indexOf(a.charAt(k++)),g=b.indexOf(a.charAt(k++)),h=b.indexOf(a.charAt(k++)),i=b.indexOf(a.charAt(k++)),j=f<<18|g<<12|h<<6|i,c=j>>16&255,d=j>>8&255,e=255&j,64===h?n[l++]=String.fromCharCode(c):64===i?n[l++]=String.fromCharCode(c,d):n[l++]=String.fromCharCode(c,d,e);while(k>18&63,g=j>>12&63,h=j>>6&63,i=63&j,n[l++]=b.charAt(f)+b.charAt(g)+b.charAt(h)+b.charAt(i);while(kd;d++)b.push(that.stringifyJSON(a[d]));return"["+b.join(",")+"]"}if(null===a)return"null";var e=[];for(var f in a)a.hasOwnProperty(f)&&e.push(that.stringifyJSON(f)+":"+that.stringifyJSON(a[f]));return"{"+e.join(",")+"}";case"number":return a;case!1:return a;case"boolean":return a}},this.trim=function(a){return null===a?"":a.replace(/^\s+|\s+$/g,"")},this.uploader=function(a){var b=function(){var b=that.detectIEVersion();var c,d,e;var f="Safari"===moxie.core.utils.Env.browser&&moxie.core.utils.Env.version<=5&&"Windows"===moxie.core.utils.Env.os&&"7"===moxie.core.utils.Env.osVersion||"Safari"===moxie.core.utils.Env.browser&&"iOS"===moxie.core.utils.Env.os&&"7"===moxie.core.utils.Env.osVersion;b&&9>b&&a.chunk_size&&a.runtimes.indexOf("flash")>=0?a.chunk_size=0:f?a.chunk_size=0:(c=20,d=4<d&&(a.chunk_size=d))};var c=function(a){var b=[];var c=-1;for(var d=0;d=0?(d.bucket=d.scope.split(":")[0],d.key=d.scope.split(":")[1]):d.bucket=d.scope,d};var e=function(b){var e=d(b);var f=window.location.protocol+"//uc.qbox.me/v1/query?ak="+e.ak+"&bucket="+e.bucket;logger.debug("putPolicy: ",e),logger.debug("get uphosts from: ",f);var g=that.detectIEVersion();var h;g&&9>=g?(h=new moxie.xhr.XMLHttpRequest,moxie.core.utils.Env.swf_url=a.flash_swf_url):h=that.createAjax(),h.open("GET",f,!1);var i=function(){if(logger.debug("ajax.readyState: ",h.readyState),4===h.readyState)if(logger.debug("ajax.status: ",h.status),h.status<400){var a=that.parseJSON(h.responseText);qiniuUpHosts.http=c(a.http.up),qiniuUpHosts.https=c(a.https.up),logger.debug("get new uphosts: ",qiniuUpHosts),that.resetUploadUrl()}else logger.error("get uphosts error: ",h.responseText)};g&&9>=g?h.bind("readystatechange",i):h.onreadystatechange=i,h.send()};var f=function(b){return!that.token||a.uptoken_url&&that.tokenInfo.isExpired()?g(b):that.token};var g=function(b){if(a.uptoken)that.token=a.uptoken;else if(a.uptoken_url){logger.debug("get uptoken from: ",that.uptoken_url);var c=that.createAjax();if(c.open("GET",that.uptoken_url+"?"+ +new Date,!1),c.send(),200===c.status){var d=that.parseJSON(c.responseText);that.token=d.uptoken;var f=that.token.split(":");var g=that.parseJSON(that.URLSafeBase64Decode(f[2]));that.tokenMap||(that.tokenMap={});var h=function(a){return Math.ceil(a.getTime()/1e3)};var i=h(new Date(c.getResponseHeader("date")));var j=h(new Date);that.tokenInfo={serverDelay:j-i,deadline:g.deadline,isExpired:function(){var a=this.deadline-h(new Date)+this.serverDelay;return 600>a}},logger.debug("get new uptoken: ",that.token),logger.debug("get token info: ",that.tokenInfo)}else logger.error("get uptoken error: ",c.responseText)}else a.uptoken_func?(logger.debug("get uptoken from uptoken_func"),that.token=a.uptoken_func(b),logger.debug("get new uptoken: ",that.token)):logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!");return that.token&&e(that.token),that.token};var h=function(b,c,d){var e="",f=!1;if(!a.save_key)if(f=b.getOption&&b.getOption("unique_names"),f=f||b.settings&&b.settings.unique_names){var g=that.getFileExtension(c.name);e=g?c.id+"."+g:c.id}else e="function"==typeof d?d(b,c):c.name;return e};var i=function(a){if(a&&a.match){var b=a.match(/^https?:\/\/([^:^\/]*)/);return b?b[1]:""}return""};var j=function(a){if(a&&a.match){var b=a.match(/(^https?)/);if(!b)return"";var c=b[1];return b=a.match(/^https?:\/\/([^:^\/]*):(\d*)/),b?b[2]:"http"===c?"80":"443"}return""};if(a.log_level&&(logger.level=a.log_level),!a.domain)throw"domain setting in options is required!";if(!a.browse_button)throw"browse_button setting in options is required!";if(!a.uptoken&&!a.uptoken_url&&!a.uptoken_func)throw"one of [uptoken, uptoken_url, uptoken_func] settings in options is required!";logger.debug("init uploader start"),logger.debug("environment: ",moxie.core.utils.Env),logger.debug("userAgent: ",navigator.userAgent);var k={};var l=a.init&&a.init.Error;var m=a.init&&a.init.FileUploaded;a.init.Error=function(){},a.init.FileUploaded=function(){},that.uptoken_url=a.uptoken_url,that.token="",that.key_handler="function"==typeof a.init.Key?a.init.Key:"",this.domain=a.domain;var n="";var o={isResumeUpload:!1,resumeFilesize:0,startTime:"",currentTime:""};b(),logger.debug("invoke reset_chunk_size()"),logger.debug("op.chunk_size: ",a.chunk_size);var p={url:qiniuUploadUrl,multipart_params:{token:""}};var q=that.detectIEVersion();q&&9>=q&&(p.multipart_params.accept="text/plain; charset=utf-8",logger.debug("add accept text/plain in multipart params")),plupload.extend(k,a,p),logger.debug("option: ",k);var r=new plupload.Uploader(k);logger.debug("new plupload.Uploader(option)"),r.bind("Init",function(b,c){logger.debug("Init event activated"),a.get_new_uptoken||g(null)}),logger.debug("bind Init event"),r.bind("FilesAdded",function(a,b){logger.debug("FilesAdded event activated");var c=a.getOption&&a.getOption("auto_start");c=c||a.settings&&a.settings.auto_start,logger.debug("auto_start: ",c),logger.debug("files: ",b);var d=function(){return"ios"===moxie.core.utils.Env.OS.toLowerCase()?!0:!1};if(d())for(var e=0;e=g&&(f.accept="text/plain; charset=utf-8",logger.debug("add accept text/plain in multipart params")),logger.debug("directUpload multipart_params_obj: ",f);var i=a.x_vars;if(void 0!==i&&"object"==typeof i)for(var j in i)i.hasOwnProperty(j)&&("function"==typeof i[j]?f["x:"+j]=i[j](b,c):"object"!=typeof i[j]&&(f["x:"+j]=i[j]));b.setOption({url:qiniuUploadUrl,multipart:!0,chunk_size:e()?a.max_file_size:void 0,multipart_params:f})};var e=function(){var a=navigator.userAgent.toLowerCase();return(a.match(/MicroMessenger/i)||"QQBrowser"===moxie.core.utils.Env.browser||a.match(/V1_AND_SQ/i))&&"android"===moxie.core.utils.Env.OS.toLowerCase()?!0:!1};var i=b.getOption&&b.getOption("chunk_size");if(i=i||b.settings&&b.settings.chunk_size,logger.debug("uploader.runtime: ",r.runtime),logger.debug("chunk_size: ",i),"html5"!==r.runtime&&"flash"!==r.runtime||!i)logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size"),d(b,c,that.key_handler);else if(c.sizel-m&&100!==j.percent&&c.size===j.total?(c.percent=j.percent,c.loaded=j.offset,n=j.ctx,o.isResumeUpload=!0,o.resumeFilesize=j.offset,j.offset+k>c.size&&(k=c.size-j.offset)):localStorage.removeItem(c.name)}o.startTime=(new Date).getTime();var q={};var s=that.detectIEVersion();s&&9>=s&&(q.accept="text/plain; charset=utf-8",logger.debug("add accept text/plain in multipart params")),b.setOption({url:qiniuUploadUrl+"/mkblk/"+k,multipart:!1,chunk_size:i,required_features:"chunks",headers:{Authorization:"UpToken "+f(c)},multipart_params:q})}}),logger.debug("bind BeforeUpload event"),r.bind("UploadProgress",function(a,b){logger.trace("UploadProgress event activated"),o.currentTime=(new Date).getTime();var c=o.currentTime-o.startTime;var d=b.loaded||0;o.isResumeUpload&&(d=b.loaded-o.resumeFilesize),b.speed=(d/c*1e3).toFixed(0)||0}),logger.debug("bind UploadProgress event"),r.bind("ChunkUploaded",function(a,b,c){logger.debug("ChunkUploaded event activated"),logger.debug("ChunkUploaded file: ",b),logger.debug("ChunkUploaded info: ",c);var d=that.parseJSON(c.response);logger.debug("ChunkUploaded res: ",d),n=n?n+","+d.ctx:d.ctx;var e=c.total-c.offset;var g=a.getOption&&a.getOption("chunk_size");g=g||a.settings&&a.settings.chunk_size,g>e&&(a.setOption({url:qiniuUploadUrl+"/mkblk/"+e}),logger.debug("up.setOption url: ",qiniuUploadUrl+"/mkblk/"+e)),a.setOption({headers:{Authorization:"UpToken "+f(b)}}),localStorage.setItem(b.name,that.stringifyJSON({ctx:n,percent:b.percent,total:c.total,offset:c.offset,time:(new Date).getTime()}))}),logger.debug("bind ChunkUploaded event");var s=qiniuUploadUrls.length;var t=function(a){return s-->0?(setTimeout(function(){that.resetUploadUrl(),a.status=plupload.QUEUED,r.stop(),r.start()},0),!0):(s=qiniuUploadUrls.length,!1)};return r.bind("Error",function(b){return function(c,d){logger.error("Error event activated"),logger.error("err: ",d);var e=new Date;var f="";var g=d.file;if(g){switch(d.code){case plupload.FAILED:f="\u4e0a\u4f20\u5931\u8d25\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002";break;case plupload.FILE_SIZE_ERROR:var h=c.getOption&&c.getOption("max_file_size");h=h||c.settings&&c.settings.max_file_size,f="\u6d4f\u89c8\u5668\u6700\u5927\u53ef\u4e0a\u4f20"+h+"\u3002\u66f4\u5927\u6587\u4ef6\u8bf7\u4f7f\u7528\u547d\u4ee4\u884c\u5de5\u5177\u3002";break;case plupload.FILE_EXTENSION_ERROR:f="\u6587\u4ef6\u9a8c\u8bc1\u5931\u8d25\u3002\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002";break;case plupload.HTTP_ERROR:if(""===d.response){if(f=d.message||"\u672a\u77e5\u7f51\u7edc\u9519\u8bef\u3002",!t(g))return;break}var k=that.parseJSON(d.response);var l=k.error;switch(d.status){case 400:f="\u8bf7\u6c42\u62a5\u6587\u683c\u5f0f\u9519\u8bef\u3002";break;case 401:f="\u5ba2\u6237\u7aef\u8ba4\u8bc1\u6388\u6743\u5931\u8d25\u3002\u8bf7\u91cd\u8bd5\u6216\u63d0\u4ea4\u53cd\u9988\u3002";break;case 405:f="\u5ba2\u6237\u7aef\u8bf7\u6c42\u9519\u8bef\u3002\u8bf7\u91cd\u8bd5\u6216\u63d0\u4ea4\u53cd\u9988\u3002";break;case 579:f="\u8d44\u6e90\u4e0a\u4f20\u6210\u529f\uff0c\u4f46\u56de\u8c03\u5931\u8d25\u3002";break;case 599:if(f="\u7f51\u7edc\u8fde\u63a5\u5f02\u5e38\u3002\u8bf7\u91cd\u8bd5\u6216\u63d0\u4ea4\u53cd\u9988\u3002",!t(g))return;break;case 614:f="\u6587\u4ef6\u5df2\u5b58\u5728\u3002";try{k=that.parseJSON(k.error),l=k.error||"file exists"}catch(m){l=k.error||"file exists"}break;case 631:f="\u6307\u5b9a\u7a7a\u95f4\u4e0d\u5b58\u5728\u3002";break;case 701:f="\u4e0a\u4f20\u6570\u636e\u5757\u6821\u9a8c\u51fa\u9519\u3002\u8bf7\u91cd\u8bd5\u6216\u63d0\u4ea4\u53cd\u9988\u3002";break;default:if(f="\u672a\u77e5\u9519\u8bef\u3002",!t(g))return}f=f+"("+d.status+"\uff1a"+l+")";break;case plupload.SECURITY_ERROR:f="\u5b89\u5168\u914d\u7f6e\u9519\u8bef\u3002\u8bf7\u8054\u7cfb\u7f51\u7ad9\u7ba1\u7406\u5458\u3002";break;case plupload.GENERIC_ERROR:f="\u4e0a\u4f20\u5931\u8d25\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002";break;case plupload.IO_ERROR:f="\u4e0a\u4f20\u5931\u8d25\u3002\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002";break;case plupload.INIT_ERROR:f="\u7f51\u7ad9\u914d\u7f6e\u9519\u8bef\u3002\u8bf7\u8054\u7cfb\u7f51\u7ad9\u7ba1\u7406\u5458\u3002",r.destroy();break;default:if(f=d.message+d.details,!t(g))return}b&&b(c,d,f)}if(c.refresh(),!a.disable_statistics_report){var n=d&&d.responseHeaders&&d.responseHeaders.match?d.responseHeaders.match(/(X-Reqid\:\ )([\w\.\%-]*)/):[];var o=n[2];var p=plupload.HTTP_ERROR?d.status:d.code;var q=g._start_at?g._start_at.getTime():e.getTime();statisticsLogger.log(0===p?ExtraErrors.NetworkError:p,o,i(c.settings.url),void 0,j(c.settings.url),(e.getTime()-q)/1e3,q/1e3,d.file.size*(d.file.percent/100),"jssdk-"+c.runtime,g.size)}}}(l)),logger.debug("bind Error event"),r.bind("FileUploaded",function(b){return function(c,d,e){logger.debug("FileUploaded event activated"),logger.debug("FileUploaded file: ",d),logger.debug("FileUploaded info: ",e);var f=new Date;var g=function(c,d,e){if(logger.debug("FileUploaded last step:",e),a.downtoken_url){var f=that.createAjax();f.open("POST",a.downtoken_url,!0),f.setRequestHeader("Content-type","application/x-www-form-urlencoded"),f.onreadystatechange=function(){if(4===f.readyState)if(200===f.status){var a;try{a=that.parseJSON(f.responseText)}catch(g){throw"invalid json format"}var h={};plupload.extend(h,that.parseJSON(e.response),a),e.response=that.stringifyJSON(h),b&&b(c,d,e)}else r.trigger("Error",{status:f.status,response:f.responseText,file:d,code:plupload.HTTP_ERROR})},f.send("key="+that.parseJSON(e.response).key+"&domain="+a.domain)}else b&&b(c,d,e)};var k=that.parseJSON(e.response);if(n=n?n:k.ctx,logger.debug("ctx: ",n),n){var l="";logger.debug("save_key: ",a.save_key),a.save_key||(l=h(c,d,that.key_handler),l=l?"/key/"+that.URLSafeBase64Encode(l):"");var m="/fname/"+that.URLSafeBase64Encode(d.name);logger.debug("op.x_vars: ",a.x_vars);var o=a.x_vars,p="",q="";if(void 0!==o&&"object"==typeof o)for(var s in o)o.hasOwnProperty(s)&&("function"==typeof o[s]?p=that.URLSafeBase64Encode(o[s](c,d)):"object"!=typeof o[s]&&(p=that.URLSafeBase64Encode(o[s])),q+="/x:"+s+"/"+p);var t=qiniuUploadUrl+"/mkfile/"+d.size+l+m+q;var u=that.detectIEVersion();var v;u&&9>=u?(v=new moxie.xhr.XMLHttpRequest,moxie.core.utils.Env.swf_url=a.flash_swf_url):v=that.createAjax(),v.open("POST",t,!0),v.setRequestHeader("Content-Type","text/plain;charset=UTF-8"),console.log("uptoken:"+that.token),v.setRequestHeader("Authorization","UpToken "+that.token);var w=function(){if(logger.debug("ajax.readyState: ",v.readyState),4===v.readyState){localStorage.removeItem(d.name);var a;200===v.status?(a={status:v.status,response:v.responseText,responseHeaders:v.getAllResponseHeaders()},logger.debug("mkfile is success: ",a),g(c,d,a)):(a={status:v.status,response:v.responseText,file:d,code:-200,responseHeaders:v.getAllResponseHeaders()},logger.debug("mkfile is error: ",a),r.trigger("Error",a))}};u&&9>=u?v.bind("readystatechange",w):v.onreadystatechange=w,v.send(n),logger.debug("mkfile: ",t)}else g(c,d,e);if(!a.disable_statistics_report){console.log(123),console.log(e.responseHeaders);var x=e.responseHeaders.match(/(X-Reqid\:\ )([\w\.\%-]*)/)[2];var y=d._start_at?d._start_at.getTime():f.getTime();statisticsLogger.log(e.status,x,i(c.settings.url),void 0,j(c.settings.url),(f.getTime()-y)/1e3,y/1e3,d.size,"jssdk-"+c.runtime,d.size)}}}(m)),logger.debug("bind FileUploaded event"),r.bind("FilesRemoved",function(b,c){var d=new Date;if(!a.disable_statistics_report)for(var e=0;eg;g++){if(d=a[g],!d.fop)return!1;switch(d.fop){case"watermark":f+=this.watermark(d)+"|";break;case"imageView2":f+=this.imageView2(d)+"|";break;case"imageMogr2":f+=this.imageMogr2(d)+"|";break;default:e=!0}if(e)return!1}if(b){f=this.getUrl(b)+"?"+f;var i=f.length;"|"===f.slice(i-1)&&(f=f.slice(0,i-1))}return f}return!1}}var Qiniu=new QiniuJsSDK;global.Qiniu=Qiniu,global.QiniuJsSDK=QiniuJsSDK}(window); //# sourceMappingURL=dist/qiniu.min.map \ No newline at end of file diff --git a/dist/qiniu.min.map b/dist/qiniu.min.map index de24ea90..efb93bb5 100644 --- a/dist/qiniu.min.map +++ b/dist/qiniu.min.map @@ -1 +1 @@ -{"version":3,"file":"dist/qiniu.min.js","sources":["dist/qiniu.js"],"names":["global","createCookie","key","value","exp","date","Date","setTime","getTime","expires","toGMTString","document","cookie","readCookie","nameEQ","ca","split","i","max","length","c","charAt","substring","indexOf","window","localStorage","setItem","getItem","removeItem","QiniuJsSDK","that","this","detectIEVersion","v","div","createElement","all","getElementsByTagName","innerHTML","logger","MUTE","FATA","ERROR","WARN","INFO","DEBUG","TRACE","level","log","type","args","header","msg","stringifyJSON","console","unshift","apply","getElementById","makeLogFunc","code","func","toLowerCase","Array","prototype","slice","call","arguments","property","hasOwnProperty","qiniuUploadUrl","location","protocol","qiniuUploadUrls","qiniuUpHosts","http","https","changeUrlTimes","resetUploadUrl","hosts","debug","isImage","url","test","getFileExtension","filename","tempArr","ext","pop","utf8_encode","argString","string","utftext","start","end","stringl","n","c1","charCodeAt","enc","String","fromCharCode","RangeError","c2","base64_decode","data","b64","o1","o2","o3","h1","h2","h3","h4","bits","ac","dec","tmp_arr","join","base64_encode","URLSafeBase64Encode","replace","URLSafeBase64Decode","createAjax","argument","xmlhttp","XMLHttpRequest","ActiveXObject","parseJSON","JSON","parse","rx_dangerous","text","lastIndex","a","toString","eval","obj","stringify","map","strArr","len","push","trim","uploader","op","reset_chunk_size","ie","BLOCK_BITS","MAX_CHUNK_SIZE","chunk_size","isSpecialSafari","mOxie","Env","browser","version","os","osVersion","runtimes","plupload","parseSize","getHosts","result","host","getPutPolicy","uptoken","segments","ak","putPolicy","scope","bucket","getUpHosts","uphosts_url","ajax","swf_url","flash_swf_url","open","onreadystatechange","readyState","status","res","responseText","up","error","bind","send","getUptoken","file","token","uptoken_url","tokenInfo","isExpired","getNewUpToken","setRequestHeader","tokenMap","getTimestamp","time","Math","ceil","serverTime","getResponseHeader","clientTime","serverDelay","deadline","leftTime","uptoken_func","getFileKey","unique_names","save_key","getOption","settings","name","id","log_level","domain","browse_button","navigator","userAgent","option","_Error_Handler","init","Error","_FileUploaded_Handler","FileUploaded","key_handler","Key","ctx","speedCalInfo","isResumeUpload","resumeFilesize","startTime","currentTime","defaultSetting","multipart_params","accept","extend","Uploader","params","get_new_uptoken","files","auto_start","is_ios","OS","setTimeout","refresh","speed","directUpload","multipart_params_obj","x_vars","undefined","x_key","setOption","multipart","is_android_weixin_or_qq","max_file_size","ua","match","runtime","size","localFileInfo","blockSize","now","before","aDay","percent","total","loaded","offset","required_features","headers","Authorization","trace","timeUsed","fileUploaded","toFixed","info","response","leftSize","retries","unknow_error_retry","QUEUED","stop","err","errTip","FAILED","FILE_SIZE_ERROR","FILE_EXTENSION_ERROR","HTTP_ERROR","message","errorObj","errorText","e","SECURITY_ERROR","GENERIC_ERROR","IO_ERROR","INIT_ERROR","destroy","details","last_step","downtoken_url","ajax_downtoken","res_downtoken","info_extended","trigger","fname","x_val","x_vars_url","getUrl","encodeURI","imageView2","mode","w","h","q","format","imageUrl","imageMogr2","auto_orient","thumbnail","strip","gravity","crop","quality","rotate","blur","watermark","image","font","fontsize","fill","dissolve","dx","dy","imageInfo","xhr","exif","get","pipeline","arr","isArray","Object","errOp","fop","Qiniu"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dist/qiniu.min.js","sources":["dist/qiniu.js"],"names":["global","createCookie","key","value","exp","date","Date","setTime","getTime","expires","toGMTString","document","cookie","readCookie","nameEQ","ca","split","i","max","length","c","charAt","substring","indexOf","window","localStorage","setItem","getItem","removeItem","QiniuJsSDK","that","this","detectIEVersion","v","div","createElement","all","getElementsByTagName","innerHTML","logger","MUTE","FATA","ERROR","WARN","INFO","DEBUG","TRACE","level","log","type","args","header","msg","stringifyJSON","console","unshift","apply","getElementById","makeLogFunc","code","func","toLowerCase","Array","prototype","slice","call","arguments","property","hasOwnProperty","qiniuUploadUrl","location","protocol","qiniuUploadUrls","qiniuUpHosts","http","https","changeUrlTimes","StatisticsLogger","qiniuCollectUploadLogUrl","queue","TaskStatus","waiting","processing","finished","req_id","host","remote_ip","port","duration","up_time","bytes_sent","up_type","file_size","join","push","status","debug","tick","unFinishedTasks","send","task","ajax","createAjax","open","setRequestHeader","token","onreadystatechange","readyState","setInterval","statisticsLogger","ExtraErrors","ZeroSizeFile","InvalidToken","InvalidArgument","InvalidFile","Cancelled","NetworkError","UnknownError","TimedOut","UnknownHost","CannotConnectToHost","NetworkConnectionLost","resetUploadUrl","hosts","isImage","url","test","getFileExtension","filename","tempArr","ext","pop","utf8_encode","argString","string","utftext","start","end","stringl","n","c1","charCodeAt","enc","String","fromCharCode","RangeError","c2","base64_decode","data","b64","o1","o2","o3","h1","h2","h3","h4","bits","ac","dec","tmp_arr","base64_encode","URLSafeBase64Encode","replace","URLSafeBase64Decode","argument","xmlhttp","XMLHttpRequest","ActiveXObject","parseJSON","JSON","parse","rx_dangerous","text","lastIndex","a","toString","eval","obj","stringify","map","strArr","len","trim","uploader","op","reset_chunk_size","ie","BLOCK_BITS","MAX_CHUNK_SIZE","chunk_size","isSpecialSafari","moxie","core","utils","Env","browser","version","os","osVersion","runtimes","plupload","parseSize","getHosts","result","uploadIndex","uploadDomain","getPutPolicy","uptoken","segments","ak","putPolicy","scope","bucket","getUpHosts","uphosts_url","xhr","swf_url","flash_swf_url","res","responseText","up","error","bind","getUptoken","file","uptoken_url","tokenInfo","isExpired","getNewUpToken","tokenMap","getTimestamp","time","Math","ceil","serverTime","getResponseHeader","clientTime","serverDelay","deadline","leftTime","uptoken_func","getFileKey","unique_names","save_key","getOption","settings","name","id","getDomainFromUrl","match","groups","getPortFromUrl","log_level","domain","browse_button","navigator","userAgent","option","_Error_Handler","init","Error","_FileUploaded_Handler","FileUploaded","key_handler","Key","ctx","speedCalInfo","isResumeUpload","resumeFilesize","startTime","currentTime","defaultSetting","multipart_params","accept","extend","Uploader","params","get_new_uptoken","files","auto_start","is_ios","OS","setTimeout","refresh","_start_at","speed","directUpload","multipart_params_obj","x_vars","undefined","x_key","setOption","multipart","is_android_weixin_or_qq","max_file_size","ua","runtime","size","localFileInfo","blockSize","now","before","aDay","percent","total","loaded","offset","required_features","headers","Authorization","trace","timeUsed","fileUploaded","toFixed","info","response","leftSize","retries","unknow_error_retry","QUEUED","stop","err","nowTime","errTip","FAILED","FILE_SIZE_ERROR","FILE_EXTENSION_ERROR","HTTP_ERROR","message","errorObj","errorText","e","SECURITY_ERROR","GENERIC_ERROR","IO_ERROR","INIT_ERROR","destroy","details","disable_statistics_report","matchedGroups","responseHeaders","errcode","startAt","last_step","downtoken_url","ajax_downtoken","res_downtoken","info_extended","trigger","fname","x_val","x_vars_url","ajaxInfo","getAllResponseHeaders","getUrl","encodeURI","imageView2","mode","w","h","q","format","imageUrl","imageMogr2","auto_orient","thumbnail","strip","gravity","crop","quality","rotate","blur","watermark","image","font","fontsize","fill","dissolve","dx","dy","imageInfo","exif","get","pipeline","arr","isArray","Object","errOp","fop","Qiniu"],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json index f3cf63ce..dd398d5e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "qiniu-js", "jsName": "qiniu", - "version": "1.0.17.1", + "version": "1.0.21", "private": false, "scripts": { "start": "node demo/server.js" @@ -24,13 +24,17 @@ ], "author": "sdk@qiniu.com", "contributors": [ + { + "name": "luoyeshu0507", + "email": "lizhiwei@qiniu.com" + }, { "name": "codedogfish", "email": "jackyu@qiniu.com" }, { - "name": "dengchenhua", - "email": "dengchenhua@qiniu.com" + "name": "jinxinxin", + "email": "jinxinxin@qiniu.com" } ], "devDependencies": { @@ -44,7 +48,7 @@ "grunt-contrib-jshint": "~0.7.2", "grunt-contrib-uglify": "~0.2.2", "grunt-contrib-watch": "~0.5.3", - "qiniu": "~6.1.1" + "qiniu": "~7.0.5" }, "license": "MIT" } diff --git a/src/qiniu.js b/src/qiniu.js index 43bdb70b..a426d58e 100644 --- a/src/qiniu.js +++ b/src/qiniu.js @@ -7,1621 +7,1823 @@ * GitHub: http://github.com/qiniu/js-sdk * * Date: @DATE -*/ + */ -/*global plupload ,mOxie*/ +/*global plupload ,moxie*/ /*global ActiveXObject */ /*exported Qiniu */ /*exported QiniuJsSDK */ -;(function( global ){ +;(function (global) { -/** - * Creates new cookie or removes cookie with negative expiration - * @param key The key or identifier for the store - * @param value Contents of the store - * @param exp Expiration - creation defaults to 30 days - */ -function createCookie(key, value, exp) { - var date = new Date(); - date.setTime(date.getTime() + (exp * 24 * 60 * 60 * 1000)); - var expires = "; expires=" + date.toGMTString(); - document.cookie = key + "=" + value + expires + "; path=/"; -} - -/** - * Returns contents of cookie - * @param key The key or identifier for the store - */ -function readCookie(key) { - var nameEQ = key + "="; - var ca = document.cookie.split(';'); - for (var i = 0, max = ca.length; i < max; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1, c.length); - } - if (c.indexOf(nameEQ) === 0) { - return c.substring(nameEQ.length, c.length); - } + /** + * Creates new cookie or removes cookie with negative expiration + * @param key The key or identifier for the store + * @param value Contents of the store + * @param exp Expiration - creation defaults to 30 days + */ + function createCookie(key, value, exp) { + var date = new Date(); + date.setTime(date.getTime() + (exp * 24 * 60 * 60 * 1000)); + var expires = "; expires=" + date.toGMTString(); + document.cookie = key + "=" + value + expires + "; path=/"; } - return null; -} - -// if current browser is not support localStorage -// use cookie to make a polyfill -if ( !window.localStorage ) { - window.localStorage = { - setItem: function (key, value) { - createCookie(key, value, 30); - }, - getItem: function (key) { - return readCookie(key); - }, - removeItem: function (key) { - createCookie(key, '', -1); - } - }; -} - -function QiniuJsSDK() { - - var that = this; /** - * detect IE version - * if current browser is not IE - * it will return false - * else - * it will return version of current IE browser - * @return {Number|Boolean} IE version or false + * Returns contents of cookie + * @param key The key or identifier for the store */ - this.detectIEVersion = function() { - var v = 4, - div = document.createElement('div'), - all = div.getElementsByTagName('i'); - while ( - div.innerHTML = '', - all[0] - ) { - v++; - } - return v > 4 ? v : false; - }; - - var logger = { - MUTE: 0, - FATA: 1, - ERROR: 2, - WARN: 3, - INFO: 4, - DEBUG: 5, - TRACE: 6, - level: 0 - }; - - function log(type, args){ - var header = "[qiniu-js-sdk]["+type+"]"; - var msg = header; - for (var i = 0; i < args.length; i++) { - if (typeof args[i] === "string") { - msg += " " + args[i]; - } else { - msg += " " + that.stringifyJSON(args[i]); + function readCookie(key) { + var nameEQ = key + "="; + var ca = document.cookie.split(';'); + for (var i = 0, max = ca.length; i < max; i++) { + var c = ca[i]; + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) === 0) { + return c.substring(nameEQ.length, c.length); } } - if (that.detectIEVersion()) { - // http://stackoverflow.com/questions/5538972/console-log-apply-not-working-in-ie9 - //var log = Function.prototype.bind.call(console.log, console); - //log.apply(console, args); - console.log(msg); - }else{ - args.unshift(header); - console.log.apply(console, args); - } - if (document.getElementById('qiniu-js-sdk-log')) { - document.getElementById('qiniu-js-sdk-log').innerHTML += '

'+msg+'

'; - } + return null; } - function makeLogFunc(code){ - var func = code.toLowerCase(); - logger[func] = function(){ - // logger[func].history = logger[func].history || []; - // logger[func].history.push(arguments); - if(window.console && window.console.log && logger.level>=logger[code]){ - var args = Array.prototype.slice.call(arguments); - log(func,args); + // if current browser is not support localStorage + // use cookie to make a polyfill + if (!window.localStorage) { + window.localStorage = { + setItem: function (key, value) { + createCookie(key, value, 30); + }, + getItem: function (key) { + return readCookie(key); + }, + removeItem: function (key) { + createCookie(key, '', -1); } }; } - for (var property in logger){ - if (logger.hasOwnProperty(property) && (typeof logger[property]) === "number" && !logger.hasOwnProperty(property.toLowerCase())) { - makeLogFunc(property); - } - } - - - var qiniuUploadUrl; - if (window.location.protocol === 'https:') { - qiniuUploadUrl = 'https://up.qbox.me'; - } else { - qiniuUploadUrl = 'http://upload.qiniu.com'; - } - - /** - * qiniu upload urls - * 'qiniuUploadUrls' is used to change target when current url is not avaliable - * @type {Array} - */ - var qiniuUploadUrls = [ - "http://upload.qiniu.com", - "http://up.qiniu.com" - ]; - - var qiniuUpHosts = { - "http": [ - "http://upload.qiniu.com", - "http://up.qiniu.com" - ], - "https": [ - "https://up.qbox.me" - ] - }; - - var changeUrlTimes = 0; - - /** - * reset upload url - * if current page protocal is https - * it will always return 'https://up.qbox.me' - * else - * it will set 'qiniuUploadUrl' value with 'qiniuUploadUrls' looply - */ - this.resetUploadUrl = function(){ - var hosts = window.location.protocol === 'https:' ? qiniuUpHosts.https : qiniuUpHosts.http; - var i = changeUrlTimes % hosts.length; - qiniuUploadUrl = hosts[i]; - changeUrlTimes++; - logger.debug('resetUploadUrl: '+qiniuUploadUrl); - }; - - // this.resetUploadUrl(); - + function QiniuJsSDK() { - /** - * is image - * @param {String} url of a file - * @return {Boolean} file is a image or not - */ - this.isImage = function(url) { - url = url.split(/[?#]/)[0]; - return (/\.(png|jpg|jpeg|gif|bmp)$/i).test(url); - }; + var that = this; - /** - * get file extension - * @param {String} filename - * @return {String} file extension - * @example - * input: test.txt - * output: txt - */ - this.getFileExtension = function(filename) { - var tempArr = filename.split("."); - var ext; - if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) { - ext = ""; - } else { - ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case - } - return ext; - }; + /** + * detect IE version + * if current browser is not IE + * it will return false + * else + * it will return version of current IE browser + * @return {Number|Boolean} IE version or false + */ + this.detectIEVersion = function () { + var v = 4, + div = document.createElement('div'), + all = div.getElementsByTagName('i'); + while ( + div.innerHTML = '', + all[0] + ) { + v++; + } + return v > 4 ? v : false; + }; - /** - * encode string by utf8 - * @param {String} string to encode - * @return {String} encoded string - */ - this.utf8_encode = function(argString) { - // http://kevin.vanzonneveld.net - // + original by: Webtoolkit.info (http://www.webtoolkit.info/) - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + improved by: sowberry - // + tweaked by: Jack - // + bugfixed by: Onno Marsman - // + improved by: Yves Sucaet - // + bugfixed by: Onno Marsman - // + bugfixed by: Ulrich - // + bugfixed by: Rafal Kukawski - // + improved by: kirilloid - // + bugfixed by: kirilloid - // * example 1: this.utf8_encode('Kevin van Zonneveld'); - // * returns 1: 'Kevin van Zonneveld' - - if (argString === null || typeof argString === 'undefined') { - return ''; - } + var logger = { + MUTE: 0, + FATA: 1, + ERROR: 2, + WARN: 3, + INFO: 4, + DEBUG: 5, + TRACE: 6, + level: 0 + }; - var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n'); - var utftext = '', - start, end, stringl = 0; - - start = end = 0; - stringl = string.length; - for (var n = 0; n < stringl; n++) { - var c1 = string.charCodeAt(n); - var enc = null; - - if (c1 < 128) { - end++; - } else if (c1 > 127 && c1 < 2048) { - enc = String.fromCharCode( - (c1 >> 6) | 192, (c1 & 63) | 128 - ); - } else if (c1 & 0xF800 ^ 0xD800 > 0) { - enc = String.fromCharCode( - (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 - ); - } else { // surrogate pairs - if (c1 & 0xFC00 ^ 0xD800 > 0) { - throw new RangeError('Unmatched trail surrogate at ' + n); - } - var c2 = string.charCodeAt(++n); - if (c2 & 0xFC00 ^ 0xDC00 > 0) { - throw new RangeError('Unmatched lead surrogate at ' + (n - 1)); + function log(type, args) { + var header = "[qiniu-js-sdk][" + type + "]"; + var msg = header; + for (var i = 0; i < args.length; i++) { + if (typeof args[i] === "string") { + msg += " " + args[i]; + } else { + msg += " " + that.stringifyJSON(args[i]); } - c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; - enc = String.fromCharCode( - (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 - ); } - if (enc !== null) { - if (end > start) { - utftext += string.slice(start, end); - } - utftext += enc; - start = end = n + 1; + if (that.detectIEVersion()) { + // http://stackoverflow.com/questions/5538972/console-log-apply-not-working-in-ie9 + //var log = Function.prototype.bind.call(console.log, console); + //log.apply(console, args); + console.log(msg); + } else { + args.unshift(header); + console.log.apply(console, args); + } + if (document.getElementById('qiniu-js-sdk-log')) { + document.getElementById('qiniu-js-sdk-log').innerHTML += '

' + msg + '

'; } } - if (end > start) { - utftext += string.slice(start, stringl); - } - - return utftext; - }; - - this.base64_decode = function (data) { - // http://kevin.vanzonneveld.net - // + original by: Tyler Akins (http://rumkin.com) - // + improved by: Thunder.m - // + input by: Aman Gupta - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + bugfixed by: Onno Marsman - // + bugfixed by: Pellentesque Malesuada - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + input by: Brett Zamir (http://brett-zamir.me) - // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); - // * returns 1: 'Kevin van Zonneveld' - // mozilla has this native - // - but breaks in 2.0.0.12! - //if (typeof this.window['atob'] == 'function') { - // return atob(data); - //} - var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, - ac = 0, - dec = "", - tmp_arr = []; - - if (!data) { - return data; + function makeLogFunc(code) { + var func = code.toLowerCase(); + logger[func] = function () { + // logger[func].history = logger[func].history || []; + // logger[func].history.push(arguments); + if (window.console && window.console.log && logger.level >= logger[code]) { + var args = Array.prototype.slice.call(arguments); + log(func, args); + } + }; } - data += ''; - - do { // unpack four hexets into three octets using index points in b64 - h1 = b64.indexOf(data.charAt(i++)); - h2 = b64.indexOf(data.charAt(i++)); - h3 = b64.indexOf(data.charAt(i++)); - h4 = b64.indexOf(data.charAt(i++)); - - bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; - - o1 = bits >> 16 & 0xff; - o2 = bits >> 8 & 0xff; - o3 = bits & 0xff; - - if (h3 === 64) { - tmp_arr[ac++] = String.fromCharCode(o1); - } else if (h4 === 64) { - tmp_arr[ac++] = String.fromCharCode(o1, o2); - } else { - tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); + for (var property in logger) { + if (logger.hasOwnProperty(property) && (typeof logger[property]) === "number" && !logger.hasOwnProperty(property.toLowerCase())) { + makeLogFunc(property); } - } while (i < data.length); - - dec = tmp_arr.join(''); - - return dec; - }; - - /** - * encode data by base64 - * @param {String} data to encode - * @return {String} encoded data - */ - this.base64_encode = function(data) { - // http://kevin.vanzonneveld.net - // + original by: Tyler Akins (http://rumkin.com) - // + improved by: Bayron Guevara - // + improved by: Thunder.m - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + bugfixed by: Pellentesque Malesuada - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // - depends on: this.utf8_encode - // * example 1: this.base64_encode('Kevin van Zonneveld'); - // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' - // mozilla has this native - // - but breaks in 2.0.0.12! - //if (typeof this.window['atob'] == 'function') { - // return atob(data); - //} - var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, - ac = 0, - enc = '', - tmp_arr = []; - - if (!data) { - return data; - } - - data = this.utf8_encode(data + ''); - - do { // pack three octets into four hexets - o1 = data.charCodeAt(i++); - o2 = data.charCodeAt(i++); - o3 = data.charCodeAt(i++); - - bits = o1 << 16 | o2 << 8 | o3; - - h1 = bits >> 18 & 0x3f; - h2 = bits >> 12 & 0x3f; - h3 = bits >> 6 & 0x3f; - h4 = bits & 0x3f; - - // use hexets to index into b64, and append result to encoded string - tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); - } while (i < data.length); - - enc = tmp_arr.join(''); - - switch (data.length % 3) { - case 1: - enc = enc.slice(0, -2) + '=='; - break; - case 2: - enc = enc.slice(0, -1) + '='; - break; } - return enc; - }; - - /** - * encode string in url by base64 - * @param {String} string in url - * @return {String} encoded string - */ - this.URLSafeBase64Encode = function(v) { - v = this.base64_encode(v); - return v.replace(/\//g, '_').replace(/\+/g, '-'); - }; - - this.URLSafeBase64Decode = function(v) { - v = v.replace(/_/g, '/').replace(/-/g, '+'); - return this.base64_decode(v); - }; - // TODO: use mOxie - /** - * craete object used to AJAX - * @return {Object} - */ - this.createAjax = function(argument) { - var xmlhttp = {}; - if (window.XMLHttpRequest) { - xmlhttp = new XMLHttpRequest(); + var qiniuUploadUrl; + if (window.location.protocol === 'https:') { + qiniuUploadUrl = 'https://up.qbox.me'; } else { - xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); + qiniuUploadUrl = 'http://upload.qiniu.com'; } - return xmlhttp; - }; - // TODO: enhance IE compatibility - /** - * parse json string to javascript object - * @param {String} json string - * @return {Object} object - */ - this.parseJSON = function(data) { - // Attempt to parse using the native JSON parser first - if (window.JSON && window.JSON.parse) { - return window.JSON.parse(data); - } + /** + * qiniu upload urls + * 'qiniuUploadUrls' is used to change target when current url is not avaliable + * @type {Array} + */ + var qiniuUploadUrls = [ + "http://upload.qiniu.com", + "http://up.qiniu.com" + ]; + + var qiniuUpHosts = { + "http": [ + "http://upload.qiniu.com", + "http://up.qiniu.com" + ], + "https": [ + "https://up.qbox.me" + ] + }; - //var rx_one = /^[\],:{}\s]*$/, - // rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, - // rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, - // rx_four = /(?:^|:|,)(?:\s*\[)+/g, - var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var changeUrlTimes = 0; - //var json; + function StatisticsLogger() { + // api to collect upload logs + var qiniuCollectUploadLogUrl = "https://uplog.qbox.me/log/3"; - var text = String(data); - rx_dangerous.lastIndex = 0; - if(rx_dangerous.test(text)){ - text = text.replace(rx_dangerous, function(a){ - return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } + /** + * { log: string, status: number }[] status: 0 待处理, 1 正在发送, 2 发送完毕 + */ + var queue = []; + var TaskStatus = { + waiting: 0, + processing: 1, + finished: 2 + }; - // todo 使用一下判断,增加安全性 - //if ( - // rx_one.test( - // text - // .replace(rx_two, '@') - // .replace(rx_three, ']') - // .replace(rx_four, '') - // ) - //) { - // return eval('(' + text + ')'); - //} - - return eval('('+text+')'); - }; + /** + * send logs to statistics server + * + * @param {number} code status code + * @param {string} req_id request id + * @param {string} host + * @param {string} remote_ip + * @param {string} port + * @param {string} duration + * @param {string} up_time + * @param {number} bytes_sent uploaded size (bytes) + * @param {string} up_type js sdk runtime: html5, html4, flash + * @param {number} file_size file total size (bytes) + */ + this.log = function (code, req_id, host, remote_ip, port, duration, up_time, bytes_sent, up_type, file_size) { + var log = Array.prototype.join.call(arguments, ','); + queue.push({ + log: log, + status: TaskStatus.waiting + }); + logger.debug("[STATISTICS] send log to statistics server", log); + }; - /** - * parse javascript object to json string - * @param {Object} object - * @return {String} json string - */ - this.stringifyJSON = function(obj) { - // Attempt to parse using the native JSON parser first - if (window.JSON && window.JSON.stringify) { - return window.JSON.stringify(obj); - } - switch (typeof (obj)) { - case 'string': - return '"' + obj.replace(/(["\\])/g, '\\$1') + '"'; - case 'array': - return '[' + obj.map(that.stringifyJSON).join(',') + ']'; - case 'object': - if (obj instanceof Array) { - var strArr = []; - var len = obj.length; - for (var i = 0; i < len; i++) { - strArr.push(that.stringifyJSON(obj[i])); + function tick() { + var unFinishedTasks = []; + for (var i = 0; i < queue.length; i++) { + if (queue[i].status !== TaskStatus.finished) { + unFinishedTasks.push(queue[i]); } - return '[' + strArr.join(',') + ']'; - } else if (obj === null) { - return 'null'; - } else { - var string = []; - for (var property in obj) { - if (obj.hasOwnProperty(property)) { - string.push(that.stringifyJSON(property) + ':' + that.stringifyJSON(obj[property])); - } + if (queue[i].status === TaskStatus.waiting) { + send(queue[i]); } - return '{' + string.join(',') + '}'; } - break; - case 'number': - return obj; - case false: - return obj; - case 'boolean': - return obj; - } - }; + queue = unFinishedTasks; + } - /** - * trim space beside text - * @param {String} untrimed string - * @return {String} trimed string - */ - this.trim = function(text) { - return text === null ? "" : text.replace(/^\s+|\s+$/g, ''); - }; + function send(task) { + task.status = TaskStatus.processing; + var ajax = that.createAjax(); + ajax.open('POST', qiniuCollectUploadLogUrl, true); + ajax.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + ajax.setRequestHeader('Authorization', 'UpToken ' + that.token); + ajax.onreadystatechange = function () { + if (ajax.readyState === 4) { + if (ajax.status === 200) { + logger.debug("[STATISTICS] successfully report log to server"); + task.status = TaskStatus.finished; + } else { + logger.debug("[STATISTICS] report log to server failed"); + task.status = TaskStatus.waiting; + } + } + }; + ajax.send(task.log); + } - /** - * create a uploader by QiniuJsSDK - * @param {object} options to create a new uploader - * @return {object} uploader - */ - this.uploader = function(op) { + // start a timer to report + setInterval(tick, 1000); + } + var statisticsLogger = new StatisticsLogger(); + var ExtraErrors = { + ZeroSizeFile: -6, + InvalidToken: -5, + InvalidArgument: -4, + InvalidFile: -3, + Cancelled: -2, + NetworkError: -1, + UnknownError: 0, + TimedOut: -1001, + UnknownHost: -1003, + CannotConnectToHost: -1004, + NetworkConnectionLost: -1005 + }; - /********** inner function define start **********/ + /** + * reset upload url + * if current page protocal is https + * it will always return 'https://up.qbox.me' + * else + * it will set 'qiniuUploadUrl' value with 'qiniuUploadUrls' looply + */ + this.resetUploadUrl = function () { + var hosts = window.location.protocol === 'https:' ? qiniuUpHosts.https : qiniuUpHosts.http; + var i = changeUrlTimes % hosts.length; + qiniuUploadUrl = hosts[i]; + changeUrlTimes++; + logger.debug('resetUploadUrl: ' + qiniuUploadUrl); + }; - // according the different condition to reset chunk size - // and the upload strategy according with the chunk size - // when chunk size is zero will cause to direct upload - // see the statement binded on 'BeforeUpload' event - var reset_chunk_size = function() { - var ie = that.detectIEVersion(); - var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size; - // case Safari 5、Windows 7、iOS 7 set isSpecialSafari to true - 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"); - // case IE 9-,chunk_size is not empty and flash is included in runtimes - // set op.chunk_size to zero - //if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { - if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { - // link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not - // when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) . - op.chunk_size = 0; - } else if (isSpecialSafari) { - // win7 safari / iOS7 safari have bug when in chunk upload mode - // reset chunk_size to 0 - // disable chunk in special version safari - op.chunk_size = 0; - } else { - BLOCK_BITS = 20; - MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M + // this.resetUploadUrl(); - chunk_size = plupload.parseSize(op.chunk_size); - if (chunk_size > MAX_CHUNK_SIZE) { - op.chunk_size = MAX_CHUNK_SIZE; - } - // qiniu service max_chunk_size is 4m - // reset chunk_size to max_chunk_size(4m) when chunk_size > 4m - } - // if op.chunk_size set 0 will be cause to direct upload - }; - var getHosts = function(hosts) { - var result = []; - for (var i = 0; i < hosts.length; i++) { - var host = hosts[i]; - if (host.indexOf('-H') === 0) { - result.push(host.split(' ')[2]); - } else { - result.push(host); - } - } - return result; + /** + * is image + * @param {String} url of a file + * @return {Boolean} file is a image or not + */ + this.isImage = function (url) { + url = url.split(/[?#]/)[0]; + return (/\.(png|jpg|jpeg|gif|bmp)$/i).test(url); }; - var getPutPolicy = function (uptoken) { - var segments = uptoken.split(":"); - var ak = segments[0]; - var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); - putPolicy.ak = ak; - if (putPolicy.scope.indexOf(":") >= 0) { - putPolicy.bucket = putPolicy.scope.split(":")[0]; - putPolicy.key = putPolicy.scope.split(":")[1]; + /** + * get file extension + * @param {String} filename + * @return {String} file extension + * @example + * input: test.txt + * output: txt + */ + this.getFileExtension = function (filename) { + var tempArr = filename.split("."); + var ext; + if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) { + ext = ""; } else { - putPolicy.bucket = putPolicy.scope; + ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case } - return putPolicy; + return ext; }; - var getUpHosts = function(uptoken) { - var putPolicy = getPutPolicy(uptoken); - // var uphosts_url = "//uc.qbox.me/v1/query?ak="+ak+"&bucket="+putPolicy.scope; - // IE 9- is not support protocal relative url - var uphosts_url = window.location.protocol + "//uc.qbox.me/v1/query?ak=" + putPolicy.ak + "&bucket=" + putPolicy.bucket; - logger.debug("putPolicy: ", putPolicy); - logger.debug("get uphosts from: ", uphosts_url); - var ie = that.detectIEVersion(); - var ajax; - if (ie && ie <= 9) { - ajax = new mOxie.XMLHttpRequest(); - mOxie.Env.swf_url = op.flash_swf_url; - }else{ - ajax = that.createAjax(); + /** + * encode string by utf8 + * @param {String} string to encode + * @return {String} encoded string + */ + this.utf8_encode = function (argString) { + // http://kevin.vanzonneveld.net + // + original by: Webtoolkit.info (http://www.webtoolkit.info/) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: sowberry + // + tweaked by: Jack + // + bugfixed by: Onno Marsman + // + improved by: Yves Sucaet + // + bugfixed by: Onno Marsman + // + bugfixed by: Ulrich + // + bugfixed by: Rafal Kukawski + // + improved by: kirilloid + // + bugfixed by: kirilloid + // * example 1: this.utf8_encode('Kevin van Zonneveld'); + // * returns 1: 'Kevin van Zonneveld' + + if (argString === null || typeof argString === 'undefined') { + return ''; } - ajax.open('GET', uphosts_url, true); - var onreadystatechange = function(){ - logger.debug("ajax.readyState: ", ajax.readyState); - if (ajax.readyState === 4) { - logger.debug("ajax.status: ", ajax.status); - if (ajax.status < 400) { - var res = that.parseJSON(ajax.responseText); - qiniuUpHosts.http = getHosts(res.http.up); - qiniuUpHosts.https = getHosts(res.https.up); - logger.debug("get new uphosts: ", qiniuUpHosts); - that.resetUploadUrl(); - } else { - logger.error("get uphosts error: ", ajax.responseText); + + var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + var utftext = '', + start, end, stringl = 0; + + start = end = 0; + stringl = string.length; + for (var n = 0; n < stringl; n++) { + var c1 = string.charCodeAt(n); + var enc = null; + + if (c1 < 128) { + end++; + } else if (c1 > 127 && c1 < 2048) { + enc = String.fromCharCode( + (c1 >> 6) | 192, (c1 & 63) | 128 + ); + } else if (c1 & 0xF800 ^ 0xD800 > 0) { + enc = String.fromCharCode( + (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 + ); + } else { // surrogate pairs + if (c1 & 0xFC00 ^ 0xD800 > 0) { + throw new RangeError('Unmatched trail surrogate at ' + n); + } + var c2 = string.charCodeAt(++n); + if (c2 & 0xFC00 ^ 0xDC00 > 0) { + throw new RangeError('Unmatched lead surrogate at ' + (n - 1)); } + c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; + enc = String.fromCharCode( + (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 + ); + } + if (enc !== null) { + if (end > start) { + utftext += string.slice(start, end); + } + utftext += enc; + start = end = n + 1; } - }; - if (ie && ie <= 9) { - ajax.bind('readystatechange', onreadystatechange); - }else{ - ajax.onreadystatechange = onreadystatechange; } - ajax.send(); - // ajax.send(); - // if (ajax.status < 400) { - // var res = that.parseJSON(ajax.responseText); - // qiniuUpHosts.http = getHosts(res.http.up); - // qiniuUpHosts.https = getHosts(res.https.up); - // logger.debug("get new uphosts: ", qiniuUpHosts); - // that.resetUploadUrl(); - // } else { - // logger.error("get uphosts error: ", ajax.responseText); - // } - return; - }; - var getUptoken = function(file) { - if (!that.token || (op.uptoken_url && that.tokenInfo.isExpired())) { - return getNewUpToken(file); - } else { - return that.token; + if (end > start) { + utftext += string.slice(start, stringl); } + + return utftext; }; - // getNewUptoken maybe called at Init Event or BeforeUpload Event - // case Init Event, the file param of getUptken will be set a null value - // if op.uptoken has value, set uptoken with op.uptoken - // else if op.uptoken_url has value, set uptoken from op.uptoken_url - // else if op.uptoken_func has value, set uptoken by result of op.uptoken_func - var getNewUpToken = function(file) { - if (op.uptoken) { - that.token = op.uptoken; - } else if (op.uptoken_url) { - logger.debug("get uptoken from: ", that.uptoken_url); - // TODO: use mOxie - var ajax = that.createAjax(); - ajax.open('GET', that.uptoken_url, false); - ajax.setRequestHeader("If-Modified-Since", "0"); - // ajax.onreadystatechange = function() { - // if (ajax.readyState === 4 && ajax.status === 200) { - // var res = that.parseJSON(ajax.responseText); - // that.token = res.uptoken; - // } - // }; - ajax.send(); - if (ajax.status === 200) { - var res = that.parseJSON(ajax.responseText); - that.token = res.uptoken; - var segments = that.token.split(":"); - var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); - if (!that.tokenMap) { - that.tokenMap = {}; - } - var getTimestamp = function(time) { - return Math.ceil(time.getTime()/1000); - }; - var serverTime = getTimestamp(new Date(ajax.getResponseHeader("date"))); - var clientTime = getTimestamp(new Date()); - that.tokenInfo = { - serverDelay: clientTime - serverTime, - deadline: putPolicy.deadline, - isExpired: function() { - var leftTime = this.deadline - getTimestamp(new Date()) + this.serverDelay; - return leftTime < 600; - } - }; - logger.debug("get new uptoken: ", that.token); - logger.debug("get token info: ", that.tokenInfo); - } else { - logger.error("get uptoken error: ", ajax.responseText); - } - } else if (op.uptoken_func) { - logger.debug("get uptoken from uptoken_func"); - that.token = op.uptoken_func(file); - logger.debug("get new uptoken: ", that.token); - } else { - logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!"); + this.base64_decode = function (data) { + // http://kevin.vanzonneveld.net + // + original by: Tyler Akins (http://rumkin.com) + // + improved by: Thunder.m + // + input by: Aman Gupta + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + bugfixed by: Pellentesque Malesuada + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); + // * returns 1: 'Kevin van Zonneveld' + // mozilla has this native + // - but breaks in 2.0.0.12! + //if (typeof this.window['atob'] == 'function') { + // return atob(data); + //} + var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, + ac = 0, + dec = "", + tmp_arr = []; + + if (!data) { + return data; } - if (that.token) { - getUpHosts(that.token); - } - return that.token; - }; - // get file key according with the user passed options - var getFileKey = function(up, file, func) { - // WARNING - // When you set the key in putPolicy by "scope": "bucket:key" - // You should understand the risk of override a file in the bucket - // So the code below that automatically get key from uptoken has been commented - // var putPolicy = getPutPolicy(that.token) - // if (putPolicy.key) { - // logger.debug("key is defined in putPolicy.scope: ", putPolicy.key) - // return putPolicy.key - // } - var key = '', - unique_names = false; - if (!op.save_key) { - unique_names = up.getOption && up.getOption('unique_names'); - unique_names = unique_names || (up.settings && up.settings.unique_names); - if (unique_names) { - var ext = that.getFileExtension(file.name); - key = ext ? file.id + '.' + ext : file.id; - } else if (typeof func === 'function') { - key = func(up, file); + data += ''; + + do { // unpack four hexets into three octets using index points in b64 + h1 = b64.indexOf(data.charAt(i++)); + h2 = b64.indexOf(data.charAt(i++)); + h3 = b64.indexOf(data.charAt(i++)); + h4 = b64.indexOf(data.charAt(i++)); + + bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; + + o1 = bits >> 16 & 0xff; + o2 = bits >> 8 & 0xff; + o3 = bits & 0xff; + + if (h3 === 64) { + tmp_arr[ac++] = String.fromCharCode(o1); + } else if (h4 === 64) { + tmp_arr[ac++] = String.fromCharCode(o1, o2); } else { - key = file.name; + tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); } - } - return key; - }; + } while (i < data.length); - /********** inner function define end **********/ + dec = tmp_arr.join(''); - if (op.log_level) { - logger.level = op.log_level; - } + return dec; + }; - if (!op.domain) { - throw 'domain setting in options is required!'; - } + /** + * encode data by base64 + * @param {String} data to encode + * @return {String} encoded data + */ + this.base64_encode = function (data) { + // http://kevin.vanzonneveld.net + // + original by: Tyler Akins (http://rumkin.com) + // + improved by: Bayron Guevara + // + improved by: Thunder.m + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Pellentesque Malesuada + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // - depends on: this.utf8_encode + // * example 1: this.base64_encode('Kevin van Zonneveld'); + // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' + // mozilla has this native + // - but breaks in 2.0.0.12! + //if (typeof this.window['atob'] == 'function') { + // return atob(data); + //} + var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, + ac = 0, + enc = '', + tmp_arr = []; + + if (!data) { + return data; + } - if (!op.browse_button) { - throw 'browse_button setting in options is required!'; - } + data = this.utf8_encode(data + ''); - if (!op.uptoken && !op.uptoken_url && !op.uptoken_func) { - throw 'one of [uptoken, uptoken_url, uptoken_func] settings in options is required!'; - } + do { // pack three octets into four hexets + o1 = data.charCodeAt(i++); + o2 = data.charCodeAt(i++); + o3 = data.charCodeAt(i++); - logger.debug("init uploader start"); + bits = o1 << 16 | o2 << 8 | o3; - logger.debug("environment: ", mOxie.Env); + h1 = bits >> 18 & 0x3f; + h2 = bits >> 12 & 0x3f; + h3 = bits >> 6 & 0x3f; + h4 = bits & 0x3f; - logger.debug("userAgent: ", navigator.userAgent); + // use hexets to index into b64, and append result to encoded string + tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); + } while (i < data.length); - var option = {}; + enc = tmp_arr.join(''); - // hold the handler from user passed options - var _Error_Handler = op.init && op.init.Error; - var _FileUploaded_Handler = op.init && op.init.FileUploaded; + switch (data.length % 3) { + case 1: + enc = enc.slice(0, -2) + '=='; + break; + case 2: + enc = enc.slice(0, -1) + '='; + break; + } - // replace the handler for intercept - op.init.Error = function() {}; - op.init.FileUploaded = function() {}; + return enc; + }; - that.uptoken_url = op.uptoken_url; - that.token = ''; - that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : ''; - this.domain = op.domain; - // TODO: ctx is global in scope of a uploader instance - // this maybe cause error - var ctx = ''; - var speedCalInfo = { - isResumeUpload: false, - resumeFilesize: 0, - startTime: '', - currentTime: '' + /** + * encode string in url by base64 + * @param {String} string in url + * @return {String} encoded string + */ + this.URLSafeBase64Encode = function (v) { + v = this.base64_encode(v); + return v.replace(/\//g, '_').replace(/\+/g, '-'); }; - reset_chunk_size(); - logger.debug("invoke reset_chunk_size()"); - logger.debug("op.chunk_size: ", op.chunk_size); + this.URLSafeBase64Decode = function (v) { + v = v.replace(/_/g, '/').replace(/-/g, '+'); + return this.base64_decode(v); + }; - var defaultSetting = { - url: qiniuUploadUrl, - multipart_params: { - token: '' + // TODO: use mOxie + /** + * craete object used to AJAX + * @return {Object} + */ + this.createAjax = function (argument) { + var xmlhttp = {}; + if (window.XMLHttpRequest) { + xmlhttp = new XMLHttpRequest(); + } else { + xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } + return xmlhttp; }; - var ie = that.detectIEVersion(); - // case IE 9- - // add accept in multipart params - if (ie && ie <= 9) { - defaultSetting.multipart_params.accept = 'text/plain; charset=utf-8'; - logger.debug("add accept text/plain in multipart params"); - } - // compose options with user passed options and default setting - plupload.extend(option, op, defaultSetting); + // TODO: enhance IE compatibility + /** + * parse json string to javascript object + * @param {String} json string + * @return {Object} object + */ + this.parseJSON = function (data) { + // Attempt to parse using the native JSON parser first + if (window.JSON && window.JSON.parse) { + return window.JSON.parse(data); + } + + //var rx_one = /^[\],:{}\s]*$/, + // rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + // rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + // rx_four = /(?:^|:|,)(?:\s*\[)+/g, + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - logger.debug("option: ", option); + //var json; - // create a new uploader with composed options - var uploader = new plupload.Uploader(option); + var text = String(data); + rx_dangerous.lastIndex = 0; + if (rx_dangerous.test(text)) { + text = text.replace(rx_dangerous, function (a) { + return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } - logger.debug("new plupload.Uploader(option)"); + // todo 使用一下判断,增加安全性 + //if ( + // rx_one.test( + // text + // .replace(rx_two, '@') + // .replace(rx_three, ']') + // .replace(rx_four, '') + // ) + //) { + // return eval('(' + text + ')'); + //} + + return eval('(' + text + ')'); + }; - // bind getNewUpToken to 'Init' event - uploader.bind('Init', function(up, params) { - logger.debug("Init event activated"); - // if op.get_new_uptoken is not true - // invoke getNewUptoken when uploader init - // else - // getNewUptoken everytime before a new file upload - if(!op.get_new_uptoken){ - getNewUpToken(null); + /** + * parse javascript object to json string + * @param {Object} object + * @return {String} json string + */ + this.stringifyJSON = function (obj) { + // Attempt to parse using the native JSON parser first + if (window.JSON && window.JSON.stringify) { + return window.JSON.stringify(obj); } - //getNewUpToken(null); - }); - - logger.debug("bind Init event"); - - // bind 'FilesAdded' event - // when file be added and auto_start has set value - // uploader will auto start upload the file - uploader.bind('FilesAdded', function(up, files) { - logger.debug("FilesAdded event activated"); - var auto_start = up.getOption && up.getOption('auto_start'); - auto_start = auto_start || (up.settings && up.settings.auto_start); - logger.debug("auto_start: ", auto_start); - logger.debug("files: ", files); - - // detect is iOS - var is_ios = function (){ - if(mOxie.Env.OS.toLowerCase()==="ios") { - return true; + switch (typeof (obj)) { + case 'string': + return '"' + obj.replace(/(["\\])/g, '\\$1') + '"'; + case 'array': + return '[' + obj.map(that.stringifyJSON).join(',') + ']'; + case 'object': + if (obj instanceof Array) { + var strArr = []; + var len = obj.length; + for (var i = 0; i < len; i++) { + strArr.push(that.stringifyJSON(obj[i])); + } + return '[' + strArr.join(',') + ']'; + } else if (obj === null) { + return 'null'; + } else { + var string = []; + for (var property in obj) { + if (obj.hasOwnProperty(property)) { + string.push(that.stringifyJSON(property) + ':' + that.stringifyJSON(obj[property])); + } + } + return '{' + string.join(',') + '}'; + } + break; + case 'number': + return obj; + case false: + return obj; + case 'boolean': + return obj; + } + }; + + /** + * trim space beside text + * @param {String} untrimed string + * @return {String} trimed string + */ + this.trim = function (text) { + return text === null ? "" : text.replace(/^\s+|\s+$/g, ''); + }; + + /** + * create a uploader by QiniuJsSDK + * @param {object} options to create a new uploader + * @return {object} uploader + */ + this.uploader = function (op) { + + /********** inner function define start **********/ + + // according the different condition to reset chunk size + // and the upload strategy according with the chunk size + // when chunk size is zero will cause to direct upload + // see the statement binded on 'BeforeUpload' event + var reset_chunk_size = function () { + var ie = that.detectIEVersion(); + var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size; + // case Safari 5、Windows 7、iOS 7 set isSpecialSafari to true + var isSpecialSafari = (moxie.core.utils.Env.browser === "Safari" && moxie.core.utils.Env.version <= 5 && moxie.core.utils.Env.os === "Windows" && moxie.core.utils.Env.osVersion === "7") || (moxie.core.utils.Env.browser === "Safari" && moxie.core.utils.Env.os === "iOS" && moxie.core.utils.Env.osVersion === "7"); + // case IE 9-,chunk_size is not empty and flash is included in runtimes + // set op.chunk_size to zero + //if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { + if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) { + // link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not + // when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) . + op.chunk_size = 0; + } else if (isSpecialSafari) { + // win7 safari / iOS7 safari have bug when in chunk upload mode + // reset chunk_size to 0 + // disable chunk in special version safari + op.chunk_size = 0; } else { - return false; + BLOCK_BITS = 20; + MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M + + chunk_size = plupload.parseSize(op.chunk_size); + if (chunk_size > MAX_CHUNK_SIZE) { + op.chunk_size = MAX_CHUNK_SIZE; + } + // qiniu service max_chunk_size is 4m + // reset chunk_size to max_chunk_size(4m) when chunk_size > 4m } + // if op.chunk_size set 0 will be cause to direct upload }; - // if current env os is iOS change file name to [time].[ext] - if (is_ios()) { - for (var i = 0; i < files.length; i++) { - var file = files[i]; - var ext = that.getFileExtension(file.name); - file.name = file.id + "." + ext; + var getHosts = function (hosts) { + var result = []; + var uploadIndex = -1; + for (var i = 0; i < hosts.length; i++) { + var host = hosts[i]; + if (host.indexOf("upload") !== -1) { + uploadIndex = i; + } + if (host.indexOf('-H') === 0) { + result.push(host.split(' ')[2]); + } else { + result.push(host); + } } - } - if (auto_start) { - setTimeout(function(){ - up.start(); - logger.debug("invoke up.start()"); - }, 0); - // up.start(); - // plupload.each(files, function(i, file) { - // up.start(); - // logger.debug("invoke up.start()") - // logger.debug("file: ", file); - // }); - } - up.refresh(); // Reposition Flash/Silverlight - }); - - logger.debug("bind FilesAdded event"); - - // bind 'BeforeUpload' event - // intercept the process of upload - // - prepare uptoken - // - according the chunk size to make differnt upload strategy - // - resume upload with the last breakpoint of file - uploader.bind('BeforeUpload', function(up, file) { - logger.debug("BeforeUpload event activated"); - // add a key named speed for file object - file.speed = file.speed || 0; - ctx = ''; - - if(op.get_new_uptoken){ - getNewUpToken(file); - } + if (uploadIndex !== -1) { + //make upload domains first + var uploadDomain = result[uploadIndex]; + result[uploadIndex] = result[0]; + result[0] = uploadDomain; + } + return result; + }; - var directUpload = function(up, file, func) { - speedCalInfo.startTime = new Date().getTime(); - var multipart_params_obj; - if (op.save_key) { - multipart_params_obj = { - 'token': that.token - }; + var getPutPolicy = function (uptoken) { + var segments = uptoken.split(":"); + var ak = segments[0]; + var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); + putPolicy.ak = ak; + if (putPolicy.scope.indexOf(":") >= 0) { + putPolicy.bucket = putPolicy.scope.split(":")[0]; + putPolicy.key = putPolicy.scope.split(":")[1]; } else { - multipart_params_obj = { - 'key': getFileKey(up, file, func), - 'token': that.token - }; + putPolicy.bucket = putPolicy.scope; } + return putPolicy; + }; + + var getUpHosts = function (uptoken) { + var putPolicy = getPutPolicy(uptoken); + // var uphosts_url = "//uc.qbox.me/v1/query?ak="+ak+"&bucket="+putPolicy.scope; + // IE9 does not support protocol relative url + var uphosts_url = window.location.protocol + "//uc.qbox.me/v1/query?ak=" + putPolicy.ak + "&bucket=" + putPolicy.bucket; + logger.debug("putPolicy: ", putPolicy); + logger.debug("get uphosts from: ", uphosts_url); var ie = that.detectIEVersion(); - // case IE 9- - // add accept in multipart params + var ajax; if (ie && ie <= 9) { - multipart_params_obj.accept = 'text/plain; charset=utf-8'; - logger.debug("add accept text/plain in multipart params"); + ajax = new moxie.xhr.XMLHttpRequest(); + moxie.core.utils.Env.swf_url = op.flash_swf_url; + } else { + ajax = that.createAjax(); } - - logger.debug("directUpload multipart_params_obj: ", multipart_params_obj); - - var x_vars = op.x_vars; - if (x_vars !== undefined && typeof x_vars === 'object') { - for (var x_key in x_vars) { - if (x_vars.hasOwnProperty(x_key)) { - if (typeof x_vars[x_key] === 'function') { - multipart_params_obj['x:' + x_key] = x_vars[x_key](up, file); - } else if (typeof x_vars[x_key] !== 'object') { - multipart_params_obj['x:' + x_key] = x_vars[x_key]; - } + ajax.open('GET', uphosts_url, false); + var onreadystatechange = function () { + logger.debug("ajax.readyState: ", ajax.readyState); + if (ajax.readyState === 4) { + logger.debug("ajax.status: ", ajax.status); + if (ajax.status < 400) { + var res = that.parseJSON(ajax.responseText); + qiniuUpHosts.http = getHosts(res.http.up); + qiniuUpHosts.https = getHosts(res.https.up); + logger.debug("get new uphosts: ", qiniuUpHosts); + that.resetUploadUrl(); + } else { + logger.error("get uphosts error: ", ajax.responseText); } } + }; + if (ie && ie <= 9) { + ajax.bind('readystatechange', onreadystatechange); + } else { + ajax.onreadystatechange = onreadystatechange; } - - up.setOption({ - 'url': qiniuUploadUrl, - 'multipart': true, - 'chunk_size': is_android_weixin_or_qq() ? op.max_file_size : undefined, - 'multipart_params': multipart_params_obj - }); + ajax.send(); + // ajax.send(); + // if (ajax.status < 400) { + // var res = that.parseJSON(ajax.responseText); + // qiniuUpHosts.http = getHosts(res.http.up); + // qiniuUpHosts.https = getHosts(res.https.up); + // logger.debug("get new uphosts: ", qiniuUpHosts); + // that.resetUploadUrl(); + // } else { + // logger.error("get uphosts error: ", ajax.responseText); + // } + return; }; - // detect is weixin or qq inner browser - var is_android_weixin_or_qq = function (){ - var ua = navigator.userAgent.toLowerCase(); - if((ua.match(/MicroMessenger/i) || mOxie.Env.browser === "QQBrowser" || ua.match(/V1_AND_SQ/i)) && mOxie.Env.OS.toLowerCase()==="android") { - return true; + var getUptoken = function (file) { + if (!that.token || (op.uptoken_url && that.tokenInfo.isExpired())) { + return getNewUpToken(file); } else { - return false; + return that.token; } }; - var chunk_size = up.getOption && up.getOption('chunk_size'); - chunk_size = chunk_size || (up.settings && up.settings.chunk_size); + // getNewUptoken maybe called at Init Event or BeforeUpload Event + // case Init Event, the file param of getUptken will be set a null value + // if op.uptoken has value, set uptoken with op.uptoken + // else if op.uptoken_url has value, set uptoken from op.uptoken_url + // else if op.uptoken_func has value, set uptoken by result of op.uptoken_func + var getNewUpToken = function (file) { + if (op.uptoken) { + that.token = op.uptoken; + } else if (op.uptoken_url) { + logger.debug("get uptoken from: ", that.uptoken_url); + // TODO: use mOxie + var ajax = that.createAjax(); + ajax.open('GET', that.uptoken_url + '?' + (+new Date()), false); + // ajax.setRequestHeader("If-Modified-Since", "0"); + // ajax.onreadystatechange = function() { + // if (ajax.readyState === 4 && ajax.status === 200) { + // var res = that.parseJSON(ajax.responseText); + // that.token = res.uptoken; + // } + // }; + ajax.send(); + if (ajax.status === 200) { + var res = that.parseJSON(ajax.responseText); + that.token = res.uptoken; + var segments = that.token.split(":"); + var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2])); + if (!that.tokenMap) { + that.tokenMap = {}; + } + var getTimestamp = function (time) { + return Math.ceil(time.getTime() / 1000); + }; + var serverTime = getTimestamp(new Date(ajax.getResponseHeader("date"))); + var clientTime = getTimestamp(new Date()); + that.tokenInfo = { + serverDelay: clientTime - serverTime, + deadline: putPolicy.deadline/1000, + isExpired: function () { + var leftTime = this.deadline - getTimestamp(new Date()) + this.serverDelay; + return leftTime < 600; + } + }; + logger.debug("get new uptoken: ", that.token); + logger.debug("get token info: ", that.tokenInfo); + } else { + logger.error("get uptoken error: ", ajax.responseText); + } + } else if (op.uptoken_func) { + logger.debug("get uptoken from uptoken_func"); + that.token = op.uptoken_func(file); + logger.debug("get new uptoken: ", that.token); + } else { + logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!"); + } + if (that.token) { + getUpHosts(that.token); + } + return that.token; + }; - logger.debug("uploader.runtime: ",uploader.runtime); - logger.debug("chunk_size: ",chunk_size); + // get file key according with the user passed options + var getFileKey = function (up, file, func) { + // WARNING + // When you set the key in putPolicy by "scope": "bucket:key" + // You should understand the risk of override a file in the bucket + // So the code below that automatically get key from uptoken has been commented + // var putPolicy = getPutPolicy(that.token) + // if (putPolicy.key) { + // logger.debug("key is defined in putPolicy.scope: ", putPolicy.key) + // return putPolicy.key + // } + var key = '', + unique_names = false; + if (!op.save_key) { + unique_names = up.getOption && up.getOption('unique_names'); + unique_names = unique_names || (up.settings && up.settings.unique_names); + if (unique_names) { + var ext = that.getFileExtension(file.name); + key = ext ? file.id + '.' + ext : file.id; + } else if (typeof func === 'function') { + key = func(up, file); + } else { + key = file.name; + } + } + return key; + }; - // TODO: flash support chunk upload - if ((uploader.runtime === 'html5' || uploader.runtime === 'flash') && chunk_size) { - if (file.size < chunk_size || is_android_weixin_or_qq()) { - logger.debug("directUpload because file.size < chunk_size || is_android_weixin_or_qq()"); - // direct upload if file size is less then the chunk size - directUpload(up, file, that.key_handler); - } else { - // TODO: need a polifill to make it work in IE 9- - // ISSUE: if file.name is existed in localStorage - // but not the same file maybe cause error - var localFileInfo = localStorage.getItem(file.name); - var blockSize = chunk_size; - if (localFileInfo) { - // TODO: although only the html5 runtime will enter this statement - // but need uniform way to make convertion between string and json - localFileInfo = that.parseJSON(localFileInfo); - var now = (new Date()).getTime(); - var before = localFileInfo.time || 0; - var aDay = 24 * 60 * 60 * 1000; // milliseconds of one day - // if the last upload time is within one day - // will upload continuously follow the last breakpoint - // else - // will reupload entire file - if (now - before < aDay) { - - if (localFileInfo.percent !== 100) { - if (file.size === localFileInfo.total) { - // TODO: if file.name and file.size is the same - // but not the same file will cause error - file.percent = localFileInfo.percent; - file.loaded = localFileInfo.offset; - ctx = localFileInfo.ctx; - - // set speed info - speedCalInfo.isResumeUpload = true; - speedCalInfo.resumeFilesize = localFileInfo.offset; - - // set block size - if (localFileInfo.offset + blockSize > file.size) { - blockSize = file.size - localFileInfo.offset; - } - } else { - // remove file info when file.size is conflict with file info - localStorage.removeItem(file.name); - } + var getDomainFromUrl = function (url) { + if (url && url.match) { + var groups = url.match(/^https?:\/\/([^:^/]*)/); + return groups ? groups[1] : ""; + } + return ""; + }; - } else { - // remove file info when upload percent is 100% - // avoid 499 bug - localStorage.removeItem(file.name); - } - } else { - // remove file info when last upload time is over one day - localStorage.removeItem(file.name); - } + var getPortFromUrl = function (url) { + if (url && url.match) { + var groups = url.match(/(^https?)/); + if (!groups) { + return ""; } - speedCalInfo.startTime = new Date().getTime(); - var multipart_params_obj = {}; - var ie = that.detectIEVersion(); - // case IE 9- - // add accept in multipart params - if (ie && ie <= 9) { - multipart_params_obj.accept = 'text/plain; charset=utf-8'; - logger.debug("add accept text/plain in multipart params"); + var type = groups[1]; + groups = url.match(/^https?:\/\/([^:^/]*):(\d*)/); + if (groups) { + return groups[2]; + } else if (type === "http") { + return "80"; + } else { + return "443"; } - // TODO: to support bput - // http://developer.qiniu.com/docs/v6/api/reference/up/bput.html - up.setOption({ - 'url': qiniuUploadUrl + '/mkblk/' + blockSize, - 'multipart': false, - 'chunk_size': chunk_size, - 'required_features': "chunks", - 'headers': { - 'Authorization': 'UpToken ' + getUptoken(file) - }, - 'multipart_params': multipart_params_obj - }); } - } else { - logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size"); - // direct upload if runtime is not html5 - directUpload(up, file, that.key_handler); + return ""; + }; + + /********** inner function define end **********/ + + if (op.log_level) { + logger.level = op.log_level; } - }); - - logger.debug("bind BeforeUpload event"); - - // bind 'UploadProgress' event - // calculate upload speed - uploader.bind('UploadProgress', function(up, file) { - logger.trace("UploadProgress event activated"); - speedCalInfo.currentTime = new Date().getTime(); - var timeUsed = speedCalInfo.currentTime - speedCalInfo.startTime; // ms - var fileUploaded = file.loaded || 0; - if (speedCalInfo.isResumeUpload) { - fileUploaded = file.loaded - speedCalInfo.resumeFilesize; + + if (!op.domain) { + throw 'domain setting in options is required!'; } - file.speed = (fileUploaded / timeUsed * 1000).toFixed(0) || 0; // unit: byte/s - }); - - logger.debug("bind UploadProgress event"); - - // bind 'ChunkUploaded' event - // store the chunk upload info and set next chunk upload url - uploader.bind('ChunkUploaded', function(up, file, info) { - logger.debug("ChunkUploaded event activated"); - logger.debug("file: ", file); - logger.debug("info: ", info); - var res = that.parseJSON(info.response); - logger.debug("res: ", res); - // ctx should look like '[chunk01_ctx],[chunk02_ctx],[chunk03_ctx],...' - ctx = ctx ? ctx + ',' + res.ctx : res.ctx; - var leftSize = info.total - info.offset; - var chunk_size = up.getOption && up.getOption('chunk_size'); - chunk_size = chunk_size || (up.settings && up.settings.chunk_size); - if (leftSize < chunk_size) { - up.setOption({ - 'url': qiniuUploadUrl + '/mkblk/' + leftSize - }); - logger.debug("up.setOption url: ", qiniuUploadUrl + '/mkblk/' + leftSize); + + if (!op.browse_button) { + throw 'browse_button setting in options is required!'; } - up.setOption({ - 'headers': { - 'Authorization': 'UpToken ' + getUptoken(file) - } - }); - localStorage.setItem(file.name, that.stringifyJSON({ - ctx: ctx, - percent: file.percent, - total: info.total, - offset: info.offset, - time: (new Date()).getTime() - })); - }); - - logger.debug("bind ChunkUploaded event"); - - var retries = qiniuUploadUrls.length; - - // if error is unkown switch upload url and retry - var unknow_error_retry = function(file){ - if (retries-- > 0) { - setTimeout(function(){ - that.resetUploadUrl(); - file.status = plupload.QUEUED; - uploader.stop(); - uploader.start(); - }, 0); - return true; - }else{ - retries = qiniuUploadUrls.length; - return false; + + if (!op.uptoken && !op.uptoken_url && !op.uptoken_func) { + throw 'one of [uptoken, uptoken_url, uptoken_func] settings in options is required!'; } - }; - // bind 'Error' event - // check the err.code and return the errTip - uploader.bind('Error', (function(_Error_Handler) { - return function(up, err) { - logger.error("Error event activated"); - logger.error("err: ", err); - var errTip = ''; - var file = err.file; - if (file) { - switch (err.code) { - case plupload.FAILED: - errTip = '上传失败。请稍后再试。'; - break; - case plupload.FILE_SIZE_ERROR: - var max_file_size = up.getOption && up.getOption('max_file_size'); - max_file_size = max_file_size || (up.settings && up.settings.max_file_size); - errTip = '浏览器最大可上传' + max_file_size + '。更大文件请使用命令行工具。'; - break; - case plupload.FILE_EXTENSION_ERROR: - errTip = '文件验证失败。请稍后重试。'; - break; - case plupload.HTTP_ERROR: - if (err.response === '') { - // Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE - errTip = err.message || '未知网络错误。'; - if (!unknow_error_retry(file)) { - return; - } - break; - } - var errorObj = that.parseJSON(err.response); - var errorText = errorObj.error; - switch (err.status) { - case 400: - errTip = "请求报文格式错误。"; - break; - case 401: - errTip = "客户端认证授权失败。请重试或提交反馈。"; - break; - case 405: - errTip = "客户端请求错误。请重试或提交反馈。"; - break; - case 579: - errTip = "资源上传成功,但回调失败。"; - break; - case 599: - errTip = "网络连接异常。请重试或提交反馈。"; - if (!unknow_error_retry(file)) { - return; - } - break; - case 614: - errTip = "文件已存在。"; - try { - errorObj = that.parseJSON(errorObj.error); - errorText = errorObj.error || 'file exists'; - } catch (e) { - errorText = errorObj.error || 'file exists'; - } - break; - case 631: - errTip = "指定空间不存在。"; - break; - case 701: - errTip = "上传数据块校验出错。请重试或提交反馈。"; - break; - default: - errTip = "未知错误。"; - if (!unknow_error_retry(file)) { - return; - } - break; - } - errTip = errTip + '(' + err.status + ':' + errorText + ')'; - break; - case plupload.SECURITY_ERROR: - errTip = '安全配置错误。请联系网站管理员。'; - break; - case plupload.GENERIC_ERROR: - errTip = '上传失败。请稍后再试。'; - break; - case plupload.IO_ERROR: - errTip = '上传失败。请稍后再试。'; - break; - case plupload.INIT_ERROR: - errTip = '网站配置错误。请联系网站管理员。'; - uploader.destroy(); - break; - default: - errTip = err.message + err.details; - if (!unknow_error_retry(file)) { - return; - } - break; + logger.debug("init uploader start"); + + logger.debug("environment: ", moxie.core.utils.Env); + + logger.debug("userAgent: ", navigator.userAgent); + + var option = {}; + + // hold the handler from user passed options + var _Error_Handler = op.init && op.init.Error; + var _FileUploaded_Handler = op.init && op.init.FileUploaded; + + // replace the handler for intercept + op.init.Error = function () {}; + op.init.FileUploaded = function () {}; + + that.uptoken_url = op.uptoken_url; + that.token = ''; + that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : ''; + this.domain = op.domain; + // TODO: ctx is global in scope of a uploader instance + // this maybe cause error + var ctx = ''; + var speedCalInfo = { + isResumeUpload: false, + resumeFilesize: 0, + startTime: '', + currentTime: '' + }; + + reset_chunk_size(); + logger.debug("invoke reset_chunk_size()"); + logger.debug("op.chunk_size: ", op.chunk_size); + + var defaultSetting = { + url: qiniuUploadUrl, + multipart_params: { + token: '' + } + }; + var ie = that.detectIEVersion(); + // case IE 9- + // add accept in multipart params + if (ie && ie <= 9) { + defaultSetting.multipart_params.accept = 'text/plain; charset=utf-8'; + logger.debug("add accept text/plain in multipart params"); + } + + // compose options with user passed options and default setting + plupload.extend(option, op, defaultSetting); + + logger.debug("option: ", option); + + // create a new uploader with composed options + var uploader = new plupload.Uploader(option); + + logger.debug("new plupload.Uploader(option)"); + + // bind getNewUpToken to 'Init' event + uploader.bind('Init', function (up, params) { + logger.debug("Init event activated"); + // if op.get_new_uptoken is not true + // invoke getNewUptoken when uploader init + // else + // getNewUptoken everytime before a new file upload + if (!op.get_new_uptoken) { + getNewUpToken(null); + } + //getNewUpToken(null); + }); + + logger.debug("bind Init event"); + + // bind 'FilesAdded' event + // when file be added and auto_start has set value + // uploader will auto start upload the file + uploader.bind('FilesAdded', function (up, files) { + logger.debug("FilesAdded event activated"); + var auto_start = up.getOption && up.getOption('auto_start'); + auto_start = auto_start || (up.settings && up.settings.auto_start); + logger.debug("auto_start: ", auto_start); + logger.debug("files: ", files); + + // detect is iOS + var is_ios = function () { + if (moxie.core.utils.Env.OS.toLowerCase() === "ios") { + return true; + } else { + return false; } - if (_Error_Handler) { - _Error_Handler(up, err, errTip); + }; + + // if current env os is iOS change file name to [time].[ext] + if (is_ios()) { + for (var i = 0; i < files.length; i++) { + var file = files[i]; + var ext = that.getFileExtension(file.name); + file.name = file.id + "." + ext; } } + + if (auto_start) { + setTimeout(function () { + up.start(); + logger.debug("invoke up.start()"); + }, 0); + // up.start(); + // plupload.each(files, function(i, file) { + // up.start(); + // logger.debug("invoke up.start()") + // logger.debug("file: ", file); + // }); + } up.refresh(); // Reposition Flash/Silverlight - }; - })(_Error_Handler)); - - logger.debug("bind Error event"); - - // bind 'FileUploaded' event - // intercept the complete of upload - // - get downtoken from downtoken_url if bucket is private - // - invoke mkfile api to compose chunks if upload strategy is chunk upload - uploader.bind('FileUploaded', (function(_FileUploaded_Handler) { - return function(up, file, info) { - logger.debug("FileUploaded event activated"); - logger.debug("file: ", file); - logger.debug("info: ", info); - var last_step = function(up, file, info) { - if (op.downtoken_url) { - // if op.dowontoken_url is not empty - // need get downtoken before invoke the _FileUploaded_Handler - var ajax_downtoken = that.createAjax(); - ajax_downtoken.open('POST', op.downtoken_url, true); - ajax_downtoken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - ajax_downtoken.onreadystatechange = function() { - if (ajax_downtoken.readyState === 4) { - if (ajax_downtoken.status === 200) { - var res_downtoken; - try { - res_downtoken = that.parseJSON(ajax_downtoken.responseText); - } catch (e) { - throw ('invalid json format'); - } - var info_extended = {}; - plupload.extend(info_extended, that.parseJSON(info), res_downtoken); - if (_FileUploaded_Handler) { - _FileUploaded_Handler(up, file, that.stringifyJSON(info_extended)); - } - } else { - uploader.trigger('Error', { - status: ajax_downtoken.status, - response: ajax_downtoken.responseText, - file: file, - code: plupload.HTTP_ERROR - }); - } - } + }); + + logger.debug("bind FilesAdded event"); + + // bind 'BeforeUpload' event + // intercept the process of upload + // - prepare uptoken + // - according the chunk size to make differnt upload strategy + // - resume upload with the last breakpoint of file + uploader.bind('BeforeUpload', function (up, file) { + logger.debug("BeforeUpload event activated"); + file._start_at = new Date(); + // add a key named speed for file object + file.speed = file.speed || 0; + ctx = ''; + + if (op.get_new_uptoken) { + getNewUpToken(file); + } + + var directUpload = function (up, file, func) { + speedCalInfo.startTime = new Date().getTime(); + var multipart_params_obj; + if (op.save_key) { + multipart_params_obj = { + 'token': that.token + }; + } else { + multipart_params_obj = { + 'key': getFileKey(up, file, func), + 'token': that.token }; - ajax_downtoken.send('key=' + that.parseJSON(info).key + '&domain=' + op.domain); - } else if (_FileUploaded_Handler) { - _FileUploaded_Handler(up, file, info); } - }; - - var res = that.parseJSON(info.response); - ctx = ctx ? ctx : res.ctx; - // if ctx is not empty - // that means the upload strategy is chunk upload - // befroe the invoke the last_step - // we need request the mkfile to compose all uploaded chunks - // else - // invalke the last_step - logger.debug("ctx: ", ctx); - if (ctx) { - var key = ''; - logger.debug("save_key: ", op.save_key); - if (!op.save_key) { - key = getFileKey(up, file, that.key_handler); - key = key ? '/key/' + that.URLSafeBase64Encode(key) : ''; + var ie = that.detectIEVersion(); + // case IE 9- + // add accept in multipart params + if (ie && ie <= 9) { + multipart_params_obj.accept = 'text/plain; charset=utf-8'; + logger.debug("add accept text/plain in multipart params"); } - var fname = '/fname/' + that.URLSafeBase64Encode(file.name); + logger.debug("directUpload multipart_params_obj: ", multipart_params_obj); - logger.debug("op.x_vars: ", op.x_vars); - var x_vars = op.x_vars, - x_val = '', - x_vars_url = ''; + var x_vars = op.x_vars; if (x_vars !== undefined && typeof x_vars === 'object') { for (var x_key in x_vars) { if (x_vars.hasOwnProperty(x_key)) { if (typeof x_vars[x_key] === 'function') { - x_val = that.URLSafeBase64Encode(x_vars[x_key](up, file)); + multipart_params_obj['x:' + x_key] = x_vars[x_key](up, file); } else if (typeof x_vars[x_key] !== 'object') { - x_val = that.URLSafeBase64Encode(x_vars[x_key]); + multipart_params_obj['x:' + x_key] = x_vars[x_key]; } - x_vars_url += '/x:' + x_key + '/' + x_val; } } } - var url = qiniuUploadUrl + '/mkfile/' + file.size + key + fname + x_vars_url; + up.setOption({ + 'url': qiniuUploadUrl, + 'multipart': true, + 'chunk_size': is_android_weixin_or_qq() ? op.max_file_size : undefined, + 'multipart_params': multipart_params_obj + }); + }; - var ie = that.detectIEVersion(); - var ajax; - if (ie && ie <= 9) { - ajax = new mOxie.XMLHttpRequest(); - mOxie.Env.swf_url = op.flash_swf_url; - }else{ - ajax = that.createAjax(); + // detect is weixin or qq inner browser + var is_android_weixin_or_qq = function () { + var ua = navigator.userAgent.toLowerCase(); + if ((ua.match(/MicroMessenger/i) || moxie.core.utils.Env.browser === "QQBrowser" || ua.match(/V1_AND_SQ/i)) && moxie.core.utils.Env.OS.toLowerCase() === "android") { + return true; + } else { + return false; } - ajax.open('POST', url, true); - ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8'); - ajax.setRequestHeader('Authorization', 'UpToken ' + that.token); - var onreadystatechange = function(){ - logger.debug("ajax.readyState: ", ajax.readyState); - if (ajax.readyState === 4) { - localStorage.removeItem(file.name); - var info; - if (ajax.status === 200) { - info = ajax.responseText; - logger.debug("mkfile is success: ", info); - last_step(up, file, info); + }; + + var chunk_size = up.getOption && up.getOption('chunk_size'); + chunk_size = chunk_size || (up.settings && up.settings.chunk_size); + + logger.debug("uploader.runtime: ", uploader.runtime); + logger.debug("chunk_size: ", chunk_size); + + // TODO: flash support chunk upload + if ((uploader.runtime === 'html5' || uploader.runtime === 'flash') && chunk_size) { + if (file.size < chunk_size || is_android_weixin_or_qq()) { + logger.debug("directUpload because file.size < chunk_size || is_android_weixin_or_qq()"); + // direct upload if file size is less then the chunk size + directUpload(up, file, that.key_handler); + } else { + // TODO: need a polifill to make it work in IE 9- + // ISSUE: if file.name is existed in localStorage + // but not the same file maybe cause error + var localFileInfo = localStorage.getItem(file.name); + var blockSize = chunk_size; + if (localFileInfo) { + // TODO: although only the html5 runtime will enter this statement + // but need uniform way to make convertion between string and json + localFileInfo = that.parseJSON(localFileInfo); + var now = (new Date()).getTime(); + var before = localFileInfo.time || 0; + var aDay = 24 * 60 * 60 * 1000; // milliseconds of one day + // if the last upload time is within one day + // will upload continuously follow the last breakpoint + // else + // will reupload entire file + if (now - before < aDay) { + + if (localFileInfo.percent !== 100) { + if (file.size === localFileInfo.total) { + // TODO: if file.name and file.size is the same + // but not the same file will cause error + file.percent = localFileInfo.percent; + file.loaded = localFileInfo.offset; + ctx = localFileInfo.ctx; + + // set speed info + speedCalInfo.isResumeUpload = true; + speedCalInfo.resumeFilesize = localFileInfo.offset; + + // set block size + if (localFileInfo.offset + blockSize > file.size) { + blockSize = file.size - localFileInfo.offset; + } + } else { + // remove file info when file.size is conflict with file info + localStorage.removeItem(file.name); + } + + } else { + // remove file info when upload percent is 100% + // avoid 499 bug + localStorage.removeItem(file.name); + } } else { - info = { - status: ajax.status, - response: ajax.responseText, - file: file, - code: -200, - responseHeaders: ajax.getAllResponseHeaders() - }; - logger.debug("mkfile is error: ", info); - uploader.trigger('Error', info); + // remove file info when last upload time is over one day + localStorage.removeItem(file.name); } } - }; - if (ie && ie <= 9) { - ajax.bind('readystatechange', onreadystatechange); - }else{ - ajax.onreadystatechange = onreadystatechange; + speedCalInfo.startTime = new Date().getTime(); + var multipart_params_obj = {}; + var ie = that.detectIEVersion(); + // case IE 9- + // add accept in multipart params + if (ie && ie <= 9) { + multipart_params_obj.accept = 'text/plain; charset=utf-8'; + logger.debug("add accept text/plain in multipart params"); + } + // TODO: to support bput + // http://developer.qiniu.com/docs/v6/api/reference/up/bput.html + up.setOption({ + 'url': qiniuUploadUrl + '/mkblk/' + blockSize, + 'multipart': false, + 'chunk_size': chunk_size, + 'required_features': "chunks", + 'headers': { + 'Authorization': 'UpToken ' + getUptoken(file) + }, + 'multipart_params': multipart_params_obj + }); } - ajax.send(ctx); - logger.debug("mkfile: ", url); } else { - last_step(up, file, info.response); + logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size"); + // direct upload if runtime is not html5 + directUpload(up, file, that.key_handler); } + }); - }; - })(_FileUploaded_Handler)); + logger.debug("bind BeforeUpload event"); + + // bind 'UploadProgress' event + // calculate upload speed + uploader.bind('UploadProgress', function (up, file) { + logger.trace("UploadProgress event activated"); + speedCalInfo.currentTime = new Date().getTime(); + var timeUsed = speedCalInfo.currentTime - speedCalInfo.startTime; // ms + var fileUploaded = file.loaded || 0; + if (speedCalInfo.isResumeUpload) { + fileUploaded = file.loaded - speedCalInfo.resumeFilesize; + } + file.speed = (fileUploaded / timeUsed * 1000).toFixed(0) || 0; // unit: byte/s + }); - logger.debug("bind FileUploaded event"); + logger.debug("bind UploadProgress event"); - // init uploader - uploader.init(); + // bind 'ChunkUploaded' event + // store the chunk upload info and set next chunk upload url + uploader.bind('ChunkUploaded', function (up, file, info) { + logger.debug("ChunkUploaded event activated"); + logger.debug("ChunkUploaded file: ", file); + logger.debug("ChunkUploaded info: ", info); + var res = that.parseJSON(info.response); + logger.debug("ChunkUploaded res: ", res); + // ctx should look like '[chunk01_ctx],[chunk02_ctx],[chunk03_ctx],...' + ctx = ctx ? ctx + ',' + res.ctx : res.ctx; + var leftSize = info.total - info.offset; + var chunk_size = up.getOption && up.getOption('chunk_size'); + chunk_size = chunk_size || (up.settings && up.settings.chunk_size); + if (leftSize < chunk_size) { + up.setOption({ + 'url': qiniuUploadUrl + '/mkblk/' + leftSize + }); + logger.debug("up.setOption url: ", qiniuUploadUrl + '/mkblk/' + leftSize); + } + up.setOption({ + 'headers': { + 'Authorization': 'UpToken ' + getUptoken(file) + } + }); + localStorage.setItem(file.name, that.stringifyJSON({ + ctx: ctx, + percent: file.percent, + total: info.total, + offset: info.offset, + time: (new Date()).getTime() + })); + }); - logger.debug("invoke uploader.init()"); + logger.debug("bind ChunkUploaded event"); - logger.debug("init uploader end"); + var retries = qiniuUploadUrls.length; - return uploader; - }; + // if error is unkown switch upload url and retry + var unknow_error_retry = function (file) { + if (retries-- > 0) { + setTimeout(function () { + that.resetUploadUrl(); + file.status = plupload.QUEUED; + uploader.stop(); + uploader.start(); + }, 0); + return true; + } else { + retries = qiniuUploadUrls.length; + return false; + } + }; - /** - * get url by key - * @param {String} key of file - * @return {String} url of file - */ - this.getUrl = function(key) { - if (!key) { - return false; - } - key = encodeURI(key); - var domain = this.domain; - if (domain.slice(domain.length - 1) !== '/') { - domain = domain + '/'; - } - return domain + key; - }; + // bind 'Error' event + // check the err.code and return the errTip + uploader.bind('Error', (function (_Error_Handler) { + return function (up, err) { + logger.error("Error event activated"); + logger.error("err: ", err); + var nowTime = new Date(); + var errTip = ''; + var file = err.file; + if (file) { + switch (err.code) { + case plupload.FAILED: + errTip = '上传失败。请稍后再试。'; + break; + case plupload.FILE_SIZE_ERROR: + var max_file_size = up.getOption && up.getOption('max_file_size'); + max_file_size = max_file_size || (up.settings && up.settings.max_file_size); + errTip = '浏览器最大可上传' + max_file_size + '。更大文件请使用命令行工具。'; + break; + case plupload.FILE_EXTENSION_ERROR: + errTip = '文件验证失败。请稍后重试。'; + break; + case plupload.HTTP_ERROR: + if (err.response === '') { + // Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE + errTip = err.message || '未知网络错误。'; + if (!unknow_error_retry(file)) { + return; + } + break; + } + var errorObj = that.parseJSON(err.response); + var errorText = errorObj.error; + switch (err.status) { + case 400: + errTip = "请求报文格式错误。"; + break; + case 401: + errTip = "客户端认证授权失败。请重试或提交反馈。"; + break; + case 405: + errTip = "客户端请求错误。请重试或提交反馈。"; + break; + case 579: + errTip = "资源上传成功,但回调失败。"; + break; + case 599: + errTip = "网络连接异常。请重试或提交反馈。"; + if (!unknow_error_retry(file)) { + return; + } + break; + case 614: + errTip = "文件已存在。"; + try { + errorObj = that.parseJSON(errorObj.error); + errorText = errorObj.error || 'file exists'; + } catch (e) { + errorText = errorObj.error || 'file exists'; + } + break; + case 631: + errTip = "指定空间不存在。"; + break; + case 701: + errTip = "上传数据块校验出错。请重试或提交反馈。"; + break; + default: + errTip = "未知错误。"; + if (!unknow_error_retry(file)) { + return; + } + break; + } + errTip = errTip + '(' + err.status + ':' + errorText + ')'; + break; + case plupload.SECURITY_ERROR: + errTip = '安全配置错误。请联系网站管理员。'; + break; + case plupload.GENERIC_ERROR: + errTip = '上传失败。请稍后再试。'; + break; + case plupload.IO_ERROR: + errTip = '上传失败。请稍后再试。'; + break; + case plupload.INIT_ERROR: + errTip = '网站配置错误。请联系网站管理员。'; + uploader.destroy(); + break; + default: + errTip = err.message + err.details; + if (!unknow_error_retry(file)) { + return; + } + break; + } + if (_Error_Handler) { + _Error_Handler(up, err, errTip); + } + } + up.refresh(); // Reposition Flash/Silverlight + + // add send log for upload error + if (!op.disable_statistics_report) { + var matchedGroups = (err && err.responseHeaders && err.responseHeaders.match) ? err.responseHeaders.match(/(X-Reqid\:\ )([\w\.\%-]*)/) : []; + console.log(err); + var req_id = matchedGroups[2].replace(/[\r\n]/g,""); + var errcode = plupload.HTTP_ERROR ? err.status : err.code; + var startAt = file._start_at ? file._start_at.getTime() : nowTime.getTime(); + statisticsLogger.log( + errcode === 0 ? ExtraErrors.NetworkError : errcode, + req_id, + getDomainFromUrl(up.settings.url), + undefined, + getPortFromUrl(up.settings.url), + (nowTime.getTime() - startAt)/1000, + startAt/1000, + err.file.size * (err.file.percent / 100), + "jssdk-" + up.runtime, + file.size + ); + } + }; + })(_Error_Handler)); + + logger.debug("bind Error event"); + + // bind 'FileUploaded' event + // intercept the complete of upload + // - get downtoken from downtoken_url if bucket is private + // - invoke mkfile api to compose chunks if upload strategy is chunk upload + uploader.bind('FileUploaded', (function (_FileUploaded_Handler) { + return function (up, file, info) { + logger.debug("FileUploaded event activated"); + logger.debug("FileUploaded file: ", file); + logger.debug("FileUploaded info: ", info); + var nowTime = new Date(); + var last_step = function (up, file, info) { + logger.debug("FileUploaded last step:", info); + if (op.downtoken_url) { + // if op.dowontoken_url is not empty + // need get downtoken before invoke the _FileUploaded_Handler + var ajax_downtoken = that.createAjax(); + ajax_downtoken.open('POST', op.downtoken_url, true); + ajax_downtoken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + ajax_downtoken.onreadystatechange = function () { + if (ajax_downtoken.readyState === 4) { + if (ajax_downtoken.status === 200) { + var res_downtoken; + try { + res_downtoken = that.parseJSON(ajax_downtoken.responseText); + } catch (e) { + throw ('invalid json format'); + } + var info_extended = {}; + plupload.extend(info_extended, that.parseJSON(info.response), res_downtoken); + info.response = that.stringifyJSON(info_extended); + if (_FileUploaded_Handler) { + _FileUploaded_Handler(up, file, info); + } + } else { + uploader.trigger('Error', { + status: ajax_downtoken.status, + response: ajax_downtoken.responseText, + file: file, + code: plupload.HTTP_ERROR + }); + } + } + }; + ajax_downtoken.send('key=' + that.parseJSON(info.response).key + '&domain=' + op.domain); + } else if (_FileUploaded_Handler) { + _FileUploaded_Handler(up, file, info); + } + }; - /** - * invoke the imageView2 api of Qiniu - * @param {Object} api params - * @param {String} key of file - * @return {String} url of processed image - */ - this.imageView2 = function(op, key) { - var mode = op.mode || '', - w = op.w || '', - h = op.h || '', - q = op.q || '', - format = op.format || ''; - if (!mode) { - return false; - } - if (!w && !h) { - return false; - } + var res = that.parseJSON(info.response); + ctx = ctx ? ctx : res.ctx; + // if ctx is not empty + // that means the upload strategy is chunk upload + // before the invoke the last_step + // we need request the mkfile to compose all uploaded chunks + // else + // invoke the last_step + logger.debug("ctx: ", ctx); + if (ctx) { + var key = ''; + logger.debug("save_key: ", op.save_key); + if (!op.save_key) { + key = getFileKey(up, file, that.key_handler); + key = key ? '/key/' + that.URLSafeBase64Encode(key) : ''; + } - var imageUrl = 'imageView2/' + mode; - imageUrl += w ? '/w/' + w : ''; - imageUrl += h ? '/h/' + h : ''; - imageUrl += q ? '/q/' + q : ''; - imageUrl += format ? '/format/' + format : ''; - if (key) { - imageUrl = this.getUrl(key) + '?' + imageUrl; - } - return imageUrl; - }; + var fname = '/fname/' + that.URLSafeBase64Encode(file.name); + + logger.debug("op.x_vars: ", op.x_vars); + var x_vars = op.x_vars, + x_val = '', + x_vars_url = ''; + if (x_vars !== undefined && typeof x_vars === 'object') { + for (var x_key in x_vars) { + if (x_vars.hasOwnProperty(x_key)) { + if (typeof x_vars[x_key] === 'function') { + x_val = that.URLSafeBase64Encode(x_vars[x_key](up, file)); + } else if (typeof x_vars[x_key] !== 'object') { + x_val = that.URLSafeBase64Encode(x_vars[x_key]); + } + x_vars_url += '/x:' + x_key + '/' + x_val; + } + } + } - /** - * invoke the imageMogr2 api of Qiniu - * @param {Object} api params - * @param {String} key of file - * @return {String} url of processed image - */ - this.imageMogr2 = function(op, key) { - var auto_orient = op['auto-orient'] || '', - thumbnail = op.thumbnail || '', - strip = op.strip || '', - gravity = op.gravity || '', - crop = op.crop || '', - quality = op.quality || '', - rotate = op.rotate || '', - format = op.format || '', - blur = op.blur || ''; - //Todo check option - - var imageUrl = 'imageMogr2'; - - imageUrl += auto_orient ? '/auto-orient' : ''; - imageUrl += thumbnail ? '/thumbnail/' + thumbnail : ''; - imageUrl += strip ? '/strip' : ''; - imageUrl += gravity ? '/gravity/' + gravity : ''; - imageUrl += quality ? '/quality/' + quality : ''; - imageUrl += crop ? '/crop/' + crop : ''; - imageUrl += rotate ? '/rotate/' + rotate : ''; - imageUrl += format ? '/format/' + format : ''; - imageUrl += blur ? '/blur/' + blur : ''; - - if (key) { - imageUrl = this.getUrl(key) + '?' + imageUrl; - } - return imageUrl; - }; + var url = qiniuUploadUrl + '/mkfile/' + file.size + key + fname + x_vars_url; - /** - * invoke the watermark api of Qiniu - * @param {Object} api params - * @param {String} key of file - * @return {String} url of processed image - */ - this.watermark = function(op, key) { - var mode = op.mode; - if (!mode) { - return false; - } + var ie = that.detectIEVersion(); + var ajax; + if (ie && ie <= 9) { + ajax = new moxie.xhr.XMLHttpRequest(); + moxie.core.utils.Env.swf_url = op.flash_swf_url; + } else { + ajax = that.createAjax(); + } + ajax.open('POST', url, true); + ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8'); + console.log('uptoken:'+that.token); + ajax.setRequestHeader('Authorization', 'UpToken ' + that.token); + var onreadystatechange = function () { + logger.debug("ajax.readyState: ", ajax.readyState); + if (ajax.readyState === 4) { + localStorage.removeItem(file.name); + var ajaxInfo; + if (ajax.status === 200) { + ajaxInfo = { + status: ajax.status, + response: ajax.responseText, + responseHeaders: ajax.getAllResponseHeaders(), + }; + logger.debug("mkfile is success: ", ajaxInfo); + last_step(up, file, ajaxInfo); + } else { + ajaxInfo = { + status: ajax.status, + response: ajax.responseText, + file: file, + code: -200, + responseHeaders: ajax.getAllResponseHeaders() + }; + logger.debug("mkfile is error: ", ajaxInfo); + uploader.trigger('Error', ajaxInfo); + } + } + }; + if (ie && ie <= 9) { + ajax.bind('readystatechange', onreadystatechange); + } else { + ajax.onreadystatechange = onreadystatechange; + } + ajax.send(ctx); + logger.debug("mkfile: ", url); + } else { + last_step(up, file, info); + } - var imageUrl = 'watermark/' + mode; + // send statistics log + if (!op.disable_statistics_report) { + console.log(info.responseHeaders); + var req_id = info.responseHeaders.match(/(X-Reqid\:\ )([\w\.\%-]*)/)[2].replace(/[\r\n]/g,""); + var startAt = file._start_at ? file._start_at.getTime() : nowTime.getTime(); + statisticsLogger.log( + info.status, + req_id, + getDomainFromUrl(up.settings.url), + undefined, + getPortFromUrl(up.settings.url), + (nowTime.getTime() - startAt)/1000, + startAt/1000, + file.size, + "jssdk-" + up.runtime, + file.size + ); + } + }; + })(_FileUploaded_Handler)); + + logger.debug("bind FileUploaded event"); + + // bind 'FilesRemoved' event + // intercept the cancel of upload + // used to send statistics log to server + uploader.bind('FilesRemoved', function (up, files) { + var nowTime = new Date(); + // add cancel log + if (!op.disable_statistics_report) { + for (var i = 0; i < files.length; i++) { + statisticsLogger.log( + ExtraErrors.Cancelled, + undefined, + getDomainFromUrl(up.settings.url), + undefined, + getPortFromUrl(up.settings.url), + (nowTime.getTime() - files[i]._start_at.getTime())/1000, + files[i]._start_at.getTime()/1000, + files[i].size * files[i].percent / 100, + "jssdk-" + up.runtime, + files[i].size + ); + } + } + }); + + logger.debug("bind FilesRemoved event"); + + // init uploader + uploader.init(); + logger.debug("invoke uploader.init()"); - if (mode === 1) { - var image = op.image || ''; - if (!image) { + logger.debug("init uploader end"); + + return uploader; + }; + + /** + * get url by key + * @param {String} key of file + * @return {String} url of file + */ + this.getUrl = function (key) { + if (!key) { return false; } - imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : ''; - } else if (mode === 2) { - var text = op.text ? op.text : '', - font = op.font ? op.font : '', - fontsize = op.fontsize ? op.fontsize : '', - fill = op.fill ? op.fill : ''; - if (!text) { - return false; + key = encodeURI(key); + var domain = this.domain; + if (domain.slice(domain.length - 1) !== '/') { + domain = domain + '/'; } - imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : ''; - imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : ''; - imageUrl += fontsize ? '/fontsize/' + fontsize : ''; - imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : ''; - } else { - // Todo mode3 - return false; - } + return domain + key; + }; - var dissolve = op.dissolve || '', - gravity = op.gravity || '', - dx = op.dx || '', - dy = op.dy || ''; + /** + * invoke the imageView2 api of Qiniu + * @param {Object} api params + * @param {String} key of file + * @return {String} url of processed image + */ + this.imageView2 = function (op, key) { - imageUrl += dissolve ? '/dissolve/' + dissolve : ''; - imageUrl += gravity ? '/gravity/' + gravity : ''; - imageUrl += dx ? '/dx/' + dx : ''; - imageUrl += dy ? '/dy/' + dy : ''; + if (!/^\d$/.test(op.mode)) { + return false; + } - if (key) { - imageUrl = this.getUrl(key) + '?' + imageUrl; - } - return imageUrl; - }; + var mode = op.mode, + w = op.w || '', + h = op.h || '', + q = op.q || '', + format = op.format || ''; - /** - * invoke the imageInfo api of Qiniu - * @param {String} key of file - * @return {Object} image info - */ - this.imageInfo = function(key) { - if (!key) { - return false; - } - var url = this.getUrl(key) + '?imageInfo'; - var xhr = this.createAjax(); - var info; - var that = this; - xhr.open('GET', url, false); - xhr.onreadystatechange = function() { - if (xhr.readyState === 4 && xhr.status === 200) { - info = that.parseJSON(xhr.responseText); + if (!w && !h) { + return false; } + + var imageUrl = 'imageView2/' + mode; + imageUrl += w ? '/w/' + w : ''; + imageUrl += h ? '/h/' + h : ''; + imageUrl += q ? '/q/' + q : ''; + imageUrl += format ? '/format/' + format : ''; + if (key) { + imageUrl = this.getUrl(key) + '?' + imageUrl; + } + return imageUrl; }; - xhr.send(); - return info; - }; - /** - * invoke the exif api of Qiniu - * @param {String} key of file - * @return {Object} image exif - */ - this.exif = function(key) { - if (!key) { - return false; - } - var url = this.getUrl(key) + '?exif'; - var xhr = this.createAjax(); - var info; - var that = this; - xhr.open('GET', url, false); - xhr.onreadystatechange = function() { - if (xhr.readyState === 4 && xhr.status === 200) { - info = that.parseJSON(xhr.responseText); + /** + * invoke the imageMogr2 api of Qiniu + * @param {Object} api params + * @param {String} key of file + * @return {String} url of processed image + */ + this.imageMogr2 = function (op, key) { + var auto_orient = op['auto-orient'] || '', + thumbnail = op.thumbnail || '', + strip = op.strip || '', + gravity = op.gravity || '', + crop = op.crop || '', + quality = op.quality || '', + rotate = op.rotate || '', + format = op.format || '', + blur = op.blur || ''; + //Todo check option + + var imageUrl = 'imageMogr2'; + + imageUrl += auto_orient ? '/auto-orient' : ''; + imageUrl += thumbnail ? '/thumbnail/' + thumbnail : ''; + imageUrl += strip ? '/strip' : ''; + imageUrl += gravity ? '/gravity/' + gravity : ''; + imageUrl += quality ? '/quality/' + quality : ''; + imageUrl += crop ? '/crop/' + crop : ''; + imageUrl += rotate ? '/rotate/' + rotate : ''; + imageUrl += format ? '/format/' + format : ''; + imageUrl += blur ? '/blur/' + blur : ''; + + if (key) { + imageUrl = this.getUrl(key) + '?' + imageUrl; } + return imageUrl; }; - xhr.send(); - return info; - }; - /** - * invoke the exif or imageInfo api of Qiniu - * according with type param - * @param {String} ['exif'|'imageInfo']type of info - * @param {String} key of file - * @return {Object} image exif or info - */ - this.get = function(type, key) { - if (!key || !type) { - return false; - } - if (type === 'exif') { - return this.exif(key); - } else if (type === 'imageInfo') { - return this.imageInfo(key); - } - return false; - }; + /** + * invoke the watermark api of Qiniu + * @param {Object} api params + * @param {String} key of file + * @return {String} url of processed image + */ + this.watermark = function (op, key) { + var mode = op.mode; + if (!mode) { + return false; + } - /** - * invoke api of Qiniu like a pipeline - * @param {Array of Object} params of a series api call - * each object in array is options of api which name is set as 'fop' property - * each api's output will be next api's input - * @param {String} key of file - * @return {String|Boolean} url of processed image - */ - this.pipeline = function(arr, key) { - var isArray = Object.prototype.toString.call(arr) === '[object Array]'; - var option, errOp, imageUrl = ''; - if (isArray) { - for (var i = 0, len = arr.length; i < len; i++) { - option = arr[i]; - if (!option.fop) { + var imageUrl = 'watermark/' + mode; + + if (mode === 1) { + var image = op.image || ''; + if (!image) { return false; } - switch (option.fop) { - case 'watermark': - imageUrl += this.watermark(option) + '|'; - break; - case 'imageView2': - imageUrl += this.imageView2(option) + '|'; - break; - case 'imageMogr2': - imageUrl += this.imageMogr2(option) + '|'; - break; - default: - errOp = true; - break; - } - if (errOp) { + imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : ''; + } else if (mode === 2) { + var text = op.text ? op.text : '', + font = op.font ? op.font : '', + fontsize = op.fontsize ? op.fontsize : '', + fill = op.fill ? op.fill : ''; + if (!text) { return false; } + imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : ''; + imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : ''; + imageUrl += fontsize ? '/fontsize/' + fontsize : ''; + imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : ''; + } else { + // Todo mode3 + return false; } + + var dissolve = op.dissolve || '', + gravity = op.gravity || '', + dx = op.dx || '', + dy = op.dy || ''; + + imageUrl += dissolve ? '/dissolve/' + dissolve : ''; + imageUrl += gravity ? '/gravity/' + gravity : ''; + imageUrl += dx ? '/dx/' + dx : ''; + imageUrl += dy ? '/dy/' + dy : ''; + if (key) { imageUrl = this.getUrl(key) + '?' + imageUrl; - var length = imageUrl.length; - if (imageUrl.slice(length - 1) === '|') { - imageUrl = imageUrl.slice(0, length - 1); - } } return imageUrl; - } - return false; - }; -} + }; + + /** + * invoke the imageInfo api of Qiniu + * @param {String} key of file + * @return {Object} image info + */ + this.imageInfo = function (key) { + if (!key) { + return false; + } + var url = this.getUrl(key) + '?imageInfo'; + var xhr = this.createAjax(); + var info; + var that = this; + xhr.open('GET', url, false); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + info = that.parseJSON(xhr.responseText); + } + }; + xhr.send(); + return info; + }; + + /** + * invoke the exif api of Qiniu + * @param {String} key of file + * @return {Object} image exif + */ + this.exif = function (key) { + if (!key) { + return false; + } + var url = this.getUrl(key) + '?exif'; + var xhr = this.createAjax(); + var info; + var that = this; + xhr.open('GET', url, false); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + info = that.parseJSON(xhr.responseText); + } + }; + xhr.send(); + return info; + }; + + /** + * invoke the exif or imageInfo api of Qiniu + * according with type param + * @param {String} ['exif'|'imageInfo']type of info + * @param {String} key of file + * @return {Object} image exif or info + */ + this.get = function (type, key) { + if (!key || !type) { + return false; + } + if (type === 'exif') { + return this.exif(key); + } else if (type === 'imageInfo') { + return this.imageInfo(key); + } + return false; + }; -var Qiniu = new QiniuJsSDK(); + /** + * invoke api of Qiniu like a pipeline + * @param {Array of Object} params of a series api call + * each object in array is options of api which name is set as 'fop' property + * each api's output will be next api's input + * @param {String} key of file + * @return {String|Boolean} url of processed image + */ + this.pipeline = function (arr, key) { + var isArray = Object.prototype.toString.call(arr) === '[object Array]'; + var option, errOp, imageUrl = ''; + if (isArray) { + for (var i = 0, len = arr.length; i < len; i++) { + option = arr[i]; + if (!option.fop) { + return false; + } + switch (option.fop) { + case 'watermark': + imageUrl += this.watermark(option) + '|'; + break; + case 'imageView2': + imageUrl += this.imageView2(option) + '|'; + break; + case 'imageMogr2': + imageUrl += this.imageMogr2(option) + '|'; + break; + default: + errOp = true; + break; + } + if (errOp) { + return false; + } + } + if (key) { + imageUrl = this.getUrl(key) + '?' + imageUrl; + var length = imageUrl.length; + if (imageUrl.slice(length - 1) === '|') { + imageUrl = imageUrl.slice(0, length - 1); + } + } + return imageUrl; + } + return false; + }; + } -global.Qiniu = Qiniu; + var Qiniu = new QiniuJsSDK(); -global.QiniuJsSDK = QiniuJsSDK; + global.Qiniu = Qiniu; + global.QiniuJsSDK = QiniuJsSDK; -})( window ); +})(window); \ No newline at end of file