PageSourceSearch

https://static.k618.cn/bower_components/qiniu/dist/qiniu.js

js k618.cn collected 2026-10-01 10:42:21 UTC 62,626 bytes, 1,630 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.