text: (function webpackUniversalModuleDefinition(root, factory) {
  	if(typeof exports === 'object' && typeof module === 'object')
  		module.exports = factory();
  	else if(typeof define === 'function' && define.amd)
  		define([], factory);
  	else if(typeof exports === 'object')
  		exports["COS"] = factory();
  	else
  		root["COS"] = factory();
  })(typeof self !== 'undefined' ? self : this, function() {
  return /******/ (function(modules) { // webpackBootstrap
  /******/ 	// The module cache
  /******/ 	var installedModules = {};
  /******/
  /******/ 	// The require function
  /******/ 	function __webpack_require__(moduleId) {
  /******/
  /******/ 		// Check if module is in cache
  /******/ 		if(installedModules[moduleId]) {
  /******/ 			return installedModules[moduleId].exports;
  /******/ 		}
  /******/ 		// Create a new module (and put it into the cache)
  /******/ 		var module = installedModules[moduleId] = {
  /******/ 			i: moduleId,
  /******/ 			l: false,
  /******/ 			exports: {}
  /******/ 		};
  /******/
  /******/ 		// Execute the module function
  /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  /******/
  /******/ 		// Flag the module as loaded
  /******/ 		module.l = true;
  /******/
  /******/ 		// Return the exports of the module
  /******/ 		return module.exports;
  /******/ 	}
  /******/
  /******/
  /******/ 	// expose the modules object (__webpack_modules__)
  /******/ 	__webpack_require__.m = modules;
  /******/
  /******/ 	// expose the module cache
  /******/ 	__webpack_require__.c = installedModules;
  /******/
  /******/ 	// define getter function for harmony exports
  /******/ 	__webpack_require__.d = function(exports, name, getter) {
  /******/ 		if(!__webpack_require__.o(exports, name)) {
  /******/ 			Object.defineProperty(exports, name, {
  /******/ 				configurable: false,
  /******/ 				enumerable: true,
  /******/ 				get: getter
  /******/ 			});
  /******/ 		}
  /******/ 	};
  /******/
  /******/ 	// getDefaultExport function for compatibility with non-harmony modules
  /******/ 	__webpack_require__.n = function(module) {
  /******/ 		var getter = module && module.__esModule ?
  /******/ 			function getDefault() { return module['default']; } :
  /******/ 			function getModuleExports() { return module; };
  /******/ 		__webpack_require__.d(getter, 'a', getter);
  /******/ 		return getter;
  /******/ 	};
  /******/
  /******/ 	// Object.prototype.hasOwnProperty.call
  /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  /******/
  /******/ 	// __webpack_public_path__
  /******/ 	__webpack_require__.p = "/dist/";
  /******/
  /******/ 	// Load entry module and return exports
  /******/ 	return __webpack_require__(__webpack_require__.s = 3);
  /******/ })
  /************************************************************************/
  /******/ ([
  /* 0 */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  
  var md5 = __webpack_require__(5);
  var CryptoJS = __webpack_require__(6);
  var xml2json = __webpack_require__(7);
  var json2xml = __webpack_require__(10);
  
  function camSafeUrlEncode(str) {
      return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A');
  }
  
  //测试用的key后面可以去掉
  var getAuth = function (opt) {
      opt = opt || {};
  
      var SecretId = opt.SecretId;
      var SecretKey = opt.SecretKey;
      var method = (opt.method || opt.Method || 'get').toLowerCase();
      var queryParams = clone(opt.Query || opt.params || {});
      var headers = clone(opt.Headers || opt.headers || {});
  
      var Key = opt.Key || '';
      var pathname;
      if (opt.UseRawKey) {
          pathname = opt.Pathname || opt.pathname || '/' + Key;
      } else {
          pathname = opt.Pathname || opt.pathname || Key;
          pathname.indexOf('/') !== 0 && (pathname = '/' + pathname);
      }
  
      if (!SecretId) return console.error('missing param SecretId');
      if (!SecretKey) return console.error('missing param SecretKey');
  
      var getObjectKeys = function (obj) {
          var list = [];
          for (var key in obj) {
              if (obj.hasOwnProperty(key)) {
                  list.push(key);
              }
          }
          return list.sort(function (a, b) {
              a = a.toLowerCase();
              b = b.toLowerCase();
              return a === b ? 0 : a > b ? 1 : -1;
          });
      };
  
      var obj2str = function (obj) {
          var i, key, val;
          var list = [];
          var keyList = getObjectKeys(obj);
          for (i = 0; i < keyList.length; i++) {
              key = keyList[i];
              val = obj[key] === undefined || obj[key] === null ? '' : '' + obj[key];
              key = key.toLowerCase();
              key = camSafeUrlEncode(key);
              val = camSafeUrlEncode(val) || '';
              list.push(key + '=' + val);
          }
          return list.join('&');
      };
  
      // 签名有效起止时间
      var now = Math.round(getSkewTime(opt.SystemClockOffset) / 1000) - 1;
      var exp = now;
  
      var Expires = opt.Expires || opt.expires;
      if (Expires === undefined) {
          exp += 900; // 签名过期时间为当前 + 900s
      } else {
          exp += Expires * 1 || 0;
      }
  
      // 要用到的 Authorization 参数列表
      var qSignAlgorithm = 'sha1';
      var qAk = SecretId;
      var qSignTime = now + ';' + exp;
      var qKeyTime = now + ';' + exp;
      var qHeaderList = getObjectKeys(headers).join(';').toLowerCase();
      var qUrlParamList = getObjectKeys(queryParams).join(';').toLowerCase();
  
      // 签名算法说明文档：https://www.qcloud.com/document/product/436/7778
      // 步骤一：计算 SignKey
      var signKey = CryptoJS.HmacSHA1(qKeyTime, SecretKey).toString();
  
      // 步骤二：构成 FormatString
      var formatString = [method, pathname, obj2str(queryParams), obj2str(headers), ''].join('\n');
  
      // 步骤三：计算 StringToSign
      var stringToSign = ['sha1', qSignTime, CryptoJS.SHA1(formatString).toString(), ''].join('\n');
  
      // 步骤四：计算 Signature
      var qSignature = CryptoJS.HmacSHA1(stringToSign, signKey).toString();
  
      // 步骤五：构造 Authorization
      var authorization = ['q-sign-algorithm=' + qSignAlgorithm, 'q-ak=' + qAk, 'q-sign-time=' + qSignTime, 'q-key-time=' + qKeyTime, 'q-header-list=' + qHeaderList, 'q-url-param-list=' + qUrlParamList, 'q-signature=' + qSignature].join('&');
  
      return authorization;
  };
  
  var noop = function () {};
  
  // 清除对象里值为的 undefined 或 null 的属性
  var clearKey = function (obj) {
      var retObj = {};
      for (var key in obj) {
          if (obj.hasOwnProperty(key) && obj[key] !== undefined && obj[key] !== null) {
              retObj[key] = obj[key];
          }
      }
      return retObj;
  };
  
  var readAsBinaryString = function (blob, callback) {
      var readFun;
      var fr = new FileReader();
      if (FileReader.prototype.readAsBinaryString) {
          readFun = FileReader.prototype.readAsBinaryString;
          fr.onload = function () {
              callback(this.result);
          };
      } else if (FileReader.prototype.readAsArrayBuffer) {
          // 在 ie11 添加 readAsBinaryString 兼容
          readFun = function (fileData) {
              var binary = "";
              var pt = this;
              var reader = new FileReader();
              reader.onload = function (e) {
                  var bytes = new Uint8Array(reader.result);
                  var length = bytes.byteLength;
                  for (var i = 0; i < length; i++) {
                      binary += String.fromCharCode(bytes[i]);
                  }
                  callback(binary);
              };
              reader.readAsArrayBuffer(fileData);
          };
      } else {
          console.error('FileReader not support readAsBinaryString');
      }
      readFun.call(fr, blob);
  };
  
  // 获取文件 md5 值
  var getFileMd5 = function (blob, callback) {
      readAsBinaryString(blob, function (content) {
          var hash = md5(content, true);
          callback(null, hash);
      });
  };
  
  function clone(obj) {
      return map(obj, function (v) {
          return typeof v === 'object' ? clone(v) : v;
      });
  }
  
  function extend(target, source) {
      each(source, function (val, key) {
          target[key] = source[key];
      });
      return target;
  }
  
  function isArray(arr) {
      return arr instanceof Array;
  }
  
  function isInArray(arr, item) {
      var flag = false;
      for (var i = 0; i < arr.length; i++) {
          if (item === arr[i]) {
              flag = true;
              break;
          }
      }
      return flag;
  }
  
  function each(obj, fn) {
      for (var i in obj) {
          if (obj.hasOwnProperty(i)) {
              fn(obj[i], i);
          }
      }
  }
  
  function map(obj, fn) {
      var o = isArray(obj) ? [] : {};
      for (var i in obj) {
          if (obj.hasOwnProperty(i)) {
              o[i] = fn(obj[i], i);
          }
      }
      return o;
  }
  
  function filter(obj, fn) {
      var iaArr = isArray(obj);
      var o = iaArr ? [] : {};
      for (var i in obj) {
          if (obj.hasOwnProperty(i)) {
              if (fn(obj[i], i)) {
                  if (iaArr) {
                      o.push(obj[i]);
                  } else {
                      o[i] = obj[i];
                  }
              }
          }
      }
      return o;
  }
  
  var binaryBase64 = function (str) {
      var i,
          len,
          char,
          res = '';
      for (i = 0, len = str.length / 2; i < len; i++) {
          char = parseInt(str[i * 2] + str[i * 2 + 1], 16);
          res += String.fromCharCode(char);
      }
      return btoa(res);
  };
  var uuid = function () {
      var S4 = function () {
          return ((1 + Math.random()) * 0x10000 | 0).toString(16).substring(1);
      };
      return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
  };
  
  var hasMissingParams = function (apiName, params) {
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      if (apiName.indexOf('Bucket') > -1 || apiName === 'deleteMultipleObject' || apiName === 'multipartList' || apiName === 'listObjectVersions') {
          if (!Bucket) return 'Bucket';
          if (!Region) return 'Region';
      } else if (apiName.indexOf('Object') > -1 || apiName.indexOf('multipart') > -1 || apiName === 'sliceUploadFile' || apiName === 'abortUploadTask') {
          if (!Bucket) return 'Bucket';
          if (!Region) return 'Region';
          if (!Key) return 'Key';
      }
      return false;
  };
  
  var formatParams = function (apiName, params) {
  
      // 复制参数对象
      params = extend({}, params);
  
      // 统一处理 Headers
      if (apiName !== 'getAuth' && apiName !== 'getV4Auth' && apiName !== 'getObjectUrl') {
          var Headers = params.Headers || {};
          if (params && typeof params === 'object') {
              (function () {
                  for (var key in params) {
                      if (params.hasOwnProperty(key) && key.indexOf('x-cos-') > -1) {
                          Headers[key] = params[key];
                      }
                  }
              })();
  
              var headerMap = {
                  // params headers
                  'x-cos-mfa': 'MFA',
                  'Content-MD5': 'ContentMD5',
                  'Content-Length': 'ContentLength',
                  'Content-Type': 'ContentType',
                  'Expect': 'Expect',
                  'Expires': 'Expires',
                  'Cache-Control': 'CacheControl',
                  'Content-Disposition': 'ContentDisposition',
                  'Content-Encoding': 'ContentEncoding',
                  'Range': 'Range',
                  'If-Modified-Since': 'IfModifiedSince',
                  'If-Unmodified-Since': 'IfUnmodifiedSince',
                  'If-Match': 'IfMatch',
                  'If-None-Match': 'IfNoneMatch',
                  'x-cos-copy-source': 'CopySource',
                  'x-cos-copy-source-Range': 'CopySourceRange',
                  'x-cos-metadata-directive': 'MetadataDirective',
                  'x-cos-copy-source-If-Modified-Since': 'CopySourceIfModifiedSince',
                  'x-cos-copy-source-If-Unmodified-Since': 'CopySourceIfUnmodifiedSince',
                  'x-cos-copy-source-If-Match': 'CopySourceIfMatch',
                  'x-cos-copy-source-If-None-Match': 'CopySourceIfNoneMatch',
                  'x-cos-acl': 'ACL',
                  'x-cos-grant-read': 'GrantRead',
                  'x-cos-grant-write': 'GrantWrite',
                  'x-cos-grant-full-control': 'GrantFullControl',
                  'x-cos-grant-read-acp': 'GrantReadAcp',
                  'x-cos-grant-write-acp': 'GrantWriteAcp',
                  'x-cos-storage-class': 'StorageClass',
                  // SSE-C
                  'x-cos-server-side-encryption-customer-algorithm': 'SSECustomerAlgorithm',
                  'x-cos-server-side-encryption-customer-key': 'SSECustomerKey',
                  'x-cos-server-side-encryption-customer-key-MD5': 'SSECustomerKeyMD5',
                  // SSE-COS、SSE-KMS
                  'x-cos-server-side-encryption': 'ServerSideEncryption',
                  'x-cos-server-side-encryption-cos-kms-key-id': 'SSEKMSKeyId',
                  'x-cos-server-side-encryption-context': 'SSEContext'
              };
              util.each(headerMap, function (paramKey, headerKey) {
                  if (params[paramKey] !== undefined) {
                      Headers[headerKey] = params[paramKey];
                  }
              });
  
              params.Headers = clearKey(Headers);
          }
      }
  
      return params;
  };
  
  var apiWrapper = function (apiName, apiFn) {
      return function (params, callback) {
  
          // 处理参数
          if (typeof params === 'function') {
              callback = params;
              params = {};
          }
  
          // 整理参数格式
          params = formatParams(apiName, params);
  
          // 代理回调函数
          var formatResult = function (result) {
              if (result && result.headers) {
                  result.headers['x-cos-version-id'] && (result.VersionId = result.headers['x-cos-version-id']);
                  result.headers['x-cos-delete-marker'] && (result.DeleteMarker = result.headers['x-cos-delete-marker']);
              }
              return result;
          };
          var _callback = function (err, data) {
              callback && callback(formatResult(err), formatResult(data));
          };
  
          if (apiName !== 'getService' && apiName !== 'abortUploadTask') {
              // 判断参数是否完整
              var missingResult;
              if (missingResult = hasMissingParams(apiName, params)) {
                  _callback({ error: 'missing param ' + missingResult });
                  return;
              }
              // 判断 region 格式
              if (params.Region) {
                  if (params.Region.indexOf('cos.') > -1) {
                      _callback({ error: 'param Region should not be start with "cos."' });
                      return;
                  } else if (!/^([a-z\d-]+)$/.test(params.Region)) {
                      _callback({ error: 'Region format error.' });
                      return;
                  }
                  // 判断 region 格式
                  if (!this.options.CompatibilityMode && params.Region.indexOf('-') === -1 && params.Region !== 'yfb' && params.Region !== 'default') {
                      console.warn('warning: param Region format error, find help here: https://cloud.tencent.com/document/product/436/6224');
                  }
              }
              // 兼容不带 AppId 的 Bucket
              if (params.Bucket) {
                  if (!/^([a-z\d-]+)-(\d+)$/.test(params.Bucket)) {
                      if (params.AppId) {
                          params.Bucket = params.Bucket + '-' + params.AppId;
                      } else if (this.options.AppId) {
                          params.Bucket = params.Bucket + '-' + this.options.AppId;
                      } else {
                          _callback({ error: 'Bucket should format as "test-1250000000".' });
                          return;
                      }
                  }
                  if (params.AppId) {
                      console.warn('warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g Bucket:"test-1250000000" ).');
                      delete params.AppId;
                  }
              }
              // 如果 Key 是 / 开头，强制去掉第一个 /
              if (!this.options.UseRawKey && params.Key && params.Key.substr(0, 1) === '/') {
                  params.Key = params.Key.substr(1);
              }
          }
          var res = apiFn.call(this, params, _callback);
          if (apiName === 'getAuth' || apiName === 'getObjectUrl') {
              return res;
          }
      };
  };
  
  var throttleOnProgress = function (total, onProgress) {
      var self = this;
      var size0 = 0;
      var size1 = 0;
      var time0 = Date.now();
      var time1;
      var timer;
  
      function update() {
          timer = 0;
          if (onProgress && typeof onProgress === 'function') {
              time1 = Date.now();
              var speed = Math.max(0, Math.round((size1 - size0) / ((time1 - time0) / 1000) * 100) / 100);
              var percent;
              if (size1 === 0 && total === 0) {
                  percent = 1;
              } else {
                  percent = Math.round(size1 / total * 100) / 100 || 0;
              }
              time0 = time1;
              size0 = size1;
              try {
                  onProgress({ loaded: size1, total: total, speed: speed, percent: percent });
              } catch (e) {}
          }
      }
  
      return function (info, immediately) {
          if (info) {
              size1 = info.loaded;
              total = info.total;
          }
          if (immediately) {
              clearTimeout(timer);
              update();
          } else {
              if (timer) return;
              timer = setTimeout(update, self.options.ProgressInterval);
          }
      };
  };
  
  var getFileSize = function (api, params, callback) {
      var size;
      if (typeof params.Body === 'string') {
          params.Body = new Blob([params.Body], { type: 'text/plain' });
      }
      if (params.Body && (params.Body instanceof Blob || params.Body.toString() === '[object File]' || params.Body.toString() === '[object Blob]')) {
          size = params.Body.size;
      } else {
          callback({ error: 'params body format error, Only allow File|Blob|String.' });
          return;
      }
      params.ContentLength = size;
      callback(null, size);
  };
  
  var getSkewTime = function (offset) {
      return Date.now() + (offset || 0);
  };
  
  var util = {
      noop: noop,
      formatParams: formatParams,
      apiWrapper: apiWrapper,
      xml2json: xml2json,
      json2xml: json2xml,
      md5: md5,
      clearKey: clearKey,
      getFileMd5: getFileMd5,
      binaryBase64: binaryBase64,
      extend: extend,
      isArray: isArray,
      isInArray: isInArray,
      each: each,
      map: map,
      filter: filter,
      clone: clone,
      uuid: uuid,
      camSafeUrlEncode: camSafeUrlEncode,
      throttleOnProgress: throttleOnProgress,
      getFileSize: getFileSize,
      getSkewTime: getSkewTime,
      getAuth: getAuth,
      isBrowser: true
  };
  
  var fileSliceNeedCopy = function () {
      var compareVersion = function (a, b) {
          a = a.split('.');
          b = b.split('.');
          for (var i = 0; i < b.length; i++) {
              if (a[i] !== b[i]) {
                  return parseInt(a[i]) > parseInt(b[i]) ? 1 : -1;
              }
          }
          return 0;
      };
      var check = function (ua) {
          var ChromeVersion = (ua.match(/Chrome\/([.\d]+)/) || [])[1];
          var QBCoreVersion = (ua.match(/QBCore\/([.\d]+)/) || [])[1];
          var QQBrowserVersion = (ua.match(/QQBrowser\/([.\d]+)/) || [])[1];
          var need = ChromeVersion && compareVersion(ChromeVersion, '53.0.2785.116') < 0 && QBCoreVersion && compareVersion(QBCoreVersion, '3.53.991.400') < 0 && QQBrowserVersion && compareVersion(QQBrowserVersion, '9.0.2524.400') <= 0 || false;
          return need;
      };
      return check(navigator && navigator.userAgent);
  }();
  util.fileSlice = function (file, start, end, isUseToUpload, callback) {
      var blob;
      if (file.slice) {
          blob = file.slice(start, end);
      } else if (file.mozSlice) {
          blob = file.mozSlice(start, end);
      } else if (file.webkitSlice) {
          blob = file.webkitSlice(start, end);
      }
      if (isUseToUpload && fileSliceNeedCopy) {
          var reader = new FileReader();
          reader.onload = function (e) {
              blob = null;
              callback(new Blob([reader.result]));
          };
          reader.readAsArrayBuffer(blob);
      } else {
          callback(blob);
      }
  };
  util.getFileUUID = function (file, ChunkSize) {
      // 如果信息不完整，不获取
      if (file.name && file.size && file.lastModifiedDate && ChunkSize) {
          return util.md5([file.name, file.size, file.lastModifiedDate, ChunkSize].join('::'));
      } else {
          return null;
      }
  };
  util.getBodyMd5 = function (UploadCheckContentMd5, Body, callback) {
      callback = callback || noop;
      if (UploadCheckContentMd5) {
          if (typeof Body === 'string') {
              callback(util.md5(Body, true));
          } else if (Blob && Body instanceof Blob) {
              util.getFileMd5(Body, function (err, md5) {
                  callback(md5);
              });
          } else {
              callback();
          }
      } else {
          callback();
      }
  };
  
  module.exports = util;
  
  /***/ }),
  /* 1 */
  /***/ (function(module, exports) {
  
  /*
   * DOM Level 2
   * Object DOMException
   * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
   * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
   */
  
  function copy(src,dest){
  	for(var p in src){
  		dest[p] = src[p];
  	}
  }
  /**
  ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
  ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
   */
  function _extends(Class,Super){
  	var pt = Class.prototype;
  	if(Object.create){
  		var ppt = Object.create(Super.prototype)
  		pt.__proto__ = ppt;
  	}
  	if(!(pt instanceof Super)){
  		function t(){};
  		t.prototype = Super.prototype;
  		t = new t();
  		copy(pt,t);
  		Class.prototype = pt = t;
  	}
  	if(pt.constructor != Class){
  		if(typeof Class != 'function'){
  			console.error("unknow Class:"+Class)
  		}
  		pt.constructor = Class
  	}
  }
  var htmlns = 'http://www.w3.org/1999/xhtml' ;
  // Node Types
  var NodeType = {}
  var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
  var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
  var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
  var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
  var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
  var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
  var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
  var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
  var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
  var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
  var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
  var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
  
  // ExceptionCode
  var ExceptionCode = {}
  var ExceptionMessage = {};
  var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
  var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
  var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
  var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
  var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
  var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
  var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
  var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
  var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
  var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
  //level2
  var INVALID_STATE_ERR        	= ExceptionCode.INVALID_STATE_ERR        	= ((ExceptionMessage[11]="Invalid state"),11);
  var SYNTAX_ERR               	= ExceptionCode.SYNTAX_ERR               	= ((ExceptionMessage[12]="Syntax error"),12);
  var INVALID_MODIFICATION_ERR 	= ExceptionCode.INVALID_MODIFICATION_ERR 	= ((ExceptionMessage[13]="Invalid modification"),13);
  var NAMESPACE_ERR            	= ExceptionCode.NAMESPACE_ERR           	= ((ExceptionMessage[14]="Invalid namespace"),14);
  var INVALID_ACCESS_ERR       	= ExceptionCode.INVALID_ACCESS_ERR      	= ((ExceptionMessage[15]="Invalid access"),15);
  
  
  function DOMException(code, message) {
  	if(message instanceof Error){
  		var error = message;
  	}else{
  		error = this;
  		Error.call(this, ExceptionMessage[code]);
  		this.message = ExceptionMessage[code];
  		if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException);
  	}
  	error.code = code;
  	if(message) this.message = this.message + ": " + message;
  	return error;
  };
  DOMException.prototype = Error.prototype;
  copy(ExceptionCode,DOMException)
  /**
   * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
   * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
   * The items in the NodeList are accessible via an integral index, starting from 0.
   */
  function NodeList() {
  };
  NodeList.prototype = {
  	/**
  	 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
  	 * @standard level1
  	 */
  	length:0, 
  	/**
  	 * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
  	 * @standard level1
  	 * @param index  unsigned long 
  	 *   Index into the collection.
  	 * @return Node
  	 * 	The node at the indexth position in the NodeList, or null if that is not a valid index. 
  	 */
  	item: function(index) {
  		return this[index] || null;
  	},
  	toString:function(isHTML,nodeFilter){
  		for(var buf = [], i = 0;i<this.length;i++){
  			serializeToString(this[i],buf,isHTML,nodeFilter);
  		}
  		return buf.join('');
  	}
  };
  function LiveNodeList(node,refresh){
  	this._node = node;
  	this._refresh = refresh
  	_updateLiveList(this);
  }
  function _updateLiveList(list){
  	var inc = list._node._inc || list._node.ownerDocument._inc;
  	if(list._inc != inc){
  		var ls = list._refresh(list._node);
  		//console.log(ls.length)
  		__set__(list,'length',ls.length);
  		copy(ls,list);
  		list._inc = inc;
  	}
  }
  LiveNodeList.prototype.item = function(i){
  	_updateLiveList(this);
  	return this[i];
  }
  
  _extends(LiveNodeList,NodeList);
  /**
   * 
   * Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes.
   * NamedNodeMap objects in the DOM are live.
   * used for attributes or DocumentType entities 
   */
  function NamedNodeMap() {
  };
  
  function _findNodeIndex(list,node){
  	var i = list.length;
  	while(i--){
  		if(list[i] === node){return i}
  	}
  }
  
  function _addNamedNode(el,list,newAttr,oldAttr){
  	if(oldAttr){
  		list[_findNodeIndex(list,oldAttr)] = newAttr;
  	}else{
  		list[list.length++] = newAttr;
  	}
  	if(el){
  		newAttr.ownerElement = el;
  		var doc = el.ownerDocument;
  		if(doc){
  			oldAttr && _onRemoveAttribute(doc,el,oldAttr);
  			_onAddAttribute(doc,el,newAttr);
  		}
  	}
  }
  function _removeNamedNode(el,list,attr){
  	//console.log('remove attr:'+attr)
  	var i = _findNodeIndex(list,attr);
  	if(i>=0){
  		var lastIndex = list.length-1
  		while(i<lastIndex){
  			list[i] = list[++i]
  		}
  		list.length = lastIndex;
  		if(el){
  			var doc = el.ownerDocument;
  			if(doc){
  				_onRemoveAttribute(doc,el,attr);
  				attr.ownerElement = null;
  			}
  		}
  	}else{
  		throw DOMException(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
  	}
  }
  NamedNodeMap.prototype = {
  	length:0,
  	item:NodeList.prototype.item,
  	getNamedItem: function(key) {
  //		if(key.indexOf(':')>0 || key == 'xmlns'){
  //			return null;
  //		}
  		//console.log()
  		var i = this.length;
  		while(i--){
  			var attr = this[i];
  			//console.log(attr.nodeName,key)
  			if(attr.nodeName == key){
  				return attr;
  			}
  		}
  	},
  	setNamedItem: function(attr) {
  		var el = attr.ownerElement;
  		if(el && el!=this._ownerElement){
  			throw new DOMException(INUSE_ATTRIBUTE_ERR);
  		}
  		var oldAttr = this.getNamedItem(attr.nodeName);
  		_addNamedNode(this._ownerElement,this,attr,oldAttr);
  		return oldAttr;
  	},
  	/* returns Node */
  	setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
  		var el = attr.ownerElement, oldAttr;
  		if(el && el!=this._ownerElement){
  			throw new DOMException(INUSE_ATTRIBUTE_ERR);
  		}
  		oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
  		_addNamedNode(this._ownerElement,this,attr,oldAttr);
  		return oldAttr;
  	},
  
  	/* returns Node */
  	removeNamedItem: function(key) {
  		var attr = this.getNamedItem(key);
  		_removeNamedNode(this._ownerElement,this,attr);
  		return attr;
  		
  		
  	},// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
  	
  	//for level2
  	removeNamedItemNS:function(namespaceURI,localName){
  		var attr = this.getNamedItemNS(namespaceURI,localName);
  		_removeNamedNode(this._ownerElement,this,attr);
  		return attr;
  	},
  	getNamedItemNS: function(namespaceURI, localName) {
  		var i = this.length;
  		while(i--){
  			var node = this[i];
  			if(node.localName == localName && node.namespaceURI == namespaceURI){
  				return node;
  			}
  		}
  		return null;
  	}
  };
  /**
   * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
   */
  function DOMImplementation(/* Object */ features) {
  	this._features = {};
  	if (features) {
  		for (var feature in features) {
  			 this._features = features[feature];
  		}
  	}
  };
  
  DOMImplementation.prototype = {
  	hasFeature: function(/* string */ feature, /* string */ version) {
  		var versions = this._features[feature.toLowerCase()];
  		if (versions && (!version || version in versions)) {
  			return true;
  		} else {
  			return false;
  		}
  	},
  	// Introduced in DOM Level 2:
  	createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
  		var doc = new Document();
  		doc.implementation = this;
  		doc.childNodes = new NodeList();
  		doc.doctype = doctype;
  		if(doctype){
  			doc.appendChild(doctype);
  		}
  		if(qualifiedName){
  			var root = doc.createElementNS(namespaceURI,qualifiedName);
  			doc.appendChild(root);
  		}
  		return doc;
  	},
  	// Introduced in DOM Level 2:
  	createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
  		var node = new DocumentType();
  		node.name = qualifiedName;
  		node.nodeName = qualifiedName;
  		node.publicId = publicId;
  		node.systemId = systemId;
  		// Introduced in DOM Level 2:
  		//readonly attribute DOMString        internalSubset;
  		
  		//TODO:..
  		//  readonly attribute NamedNodeMap     entities;
  		//  readonly attribute NamedNodeMap     notations;
  		return node;
  	}
  };
  
  
  /**
   * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
   */
  
  function Node() {
  };
  
  Node.prototype = {
  	firstChild : null,
  	lastChild : null,
  	previousSibling : null,
  	nextSibling : null,
  	attributes : null,
  	parentNode : null,
  	childNodes : null,
  	ownerDocument : null,
  	nodeValue : null,
  	namespaceURI : null,
  	prefix : null,
  	localName : null,
  	// Modified in DOM Level 2:
  	insertBefore:function(newChild, refChild){//raises 
  		return _insertBefore(this,newChild,refChild);
  	},
  	replaceChild:function(newChild, oldChild){//raises 
  		this.insertBefore(newChild,oldChild);
  		if(oldChild){
  			this.removeChild(oldChild);
  		}
  	},
  	removeChild:function(oldChild){
  		return _removeChild(this,oldChild);
  	},
  	appendChild:function(newChild){
  		return this.insertBefore(newChild,null);
  	},
  	hasChildNodes:function(){
  		return this.firstChild != null;
  	},
  	cloneNode:function(deep){
  		return cloneNode(this.ownerDocument||this,this,deep);
  	},
  	// Modified in DOM Level 2:
  	normalize:function(){
  		var child = this.firstChild;
  		while(child){
  			var next = child.nextSibling;
  			if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
  				this.removeChild(next);
  				child.appendData(next.data);
  			}else{
  				child.normalize();
  				child = next;
  			}
  		}
  	},
    	// Introduced in DOM Level 2:
  	isSupported:function(feature, version){
  		return this.ownerDocument.implementation.hasFeature(feature,version);
  	},
      // Introduced in DOM Level 2:
      hasAttributes:function(){
      	return this.attributes.length>0;
      },
      lookupPrefix:function(namespaceURI){
      	var el = this;
      	while(el){
      		var map = el._nsMap;
      		//console.dir(map)
      		if(map){
      			for(var n in map){
      				if(map[n] == namespaceURI){
      					return n;
      				}
      			}
      		}
      		el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
      	}
      	return null;
      },
      // Introduced in DOM Level 3:
      lookupNamespaceURI:function(prefix){
      	var el = this;
      	while(el){
      		var map = el._nsMap;
      		//console.dir(map)
      		if(map){
      			if(prefix in map){
      				return map[prefix] ;
      			}
      		}
      		el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
      	}
      	return null;
      },
      // Introduced in DOM Level 3:
      isDefaultNamespace:function(namespaceURI){
      	var prefix = this.lookupPrefix(namespaceURI);
      	return prefix == null;
      }
  };
  
  
  function _xmlEncoder(c){
  	return c == '<' && '&lt;' ||
           c == '>' && '&gt;' ||
           c == '&' && '&amp;' ||
           c == '"' && '&quot;' ||
           '&#'+c.charCodeAt()+';'
  }
  
  
  copy(NodeType,Node);
  copy(NodeType,Node.prototype);
  
  /**
   * @param callback return true for continue,false for break
   * @return boolean true: break visit;
   */
  function _visitNode(node,callback){
  	if(callback(node)){
  		return true;
  	}
  	if(node = node.firstChild){
  		do{
  			if(_visitNode(node,callback)){return true}
          }while(node=node.nextSibling)
      }
  }
  
  
  
  function Document(){
  }
  function _onAddAttribute(doc,el,newAttr){
  	doc && doc._inc++;
  	var ns = newAttr.namespaceURI ;
  	if(ns == 'http://www.w3.org/2000/xmlns/'){
  		//update namespace
  		el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value
  	}
  }
  function _onRemoveAttribute(doc,el,newAttr,remove){
  	doc && doc._inc++;
  	var ns = newAttr.namespaceURI ;
  	if(ns == 'http://www.w3.org/2000/xmlns/'){
  		//update namespace
  		delete el._nsMap[newAttr.prefix?newAttr.localName:'']
  	}
  }
  function _onUpdateChild(doc,el,newChild){
  	if(doc && doc._inc){
  		doc._inc++;
  		//update childNodes
  		var cs = el.childNodes;
  		if(newChild){
  			cs[cs.length++] = newChild;
  		}else{
  			//console.log(1)
  			var child = el.firstChild;
  			var i = 0;
  			while(child){
  				cs[i++] = child;
  				child =child.nextSibling;
  			}
  			cs.length = i;
  		}
  	}
  }
  
  /**
   * attributes;
   * children;
   * 
   * writeable properties:
   * nodeValue,Attr:value,CharacterData:data
   * prefix
   */
  function _removeChild(parentNode,child){
  	var previous = child.previousSibling;
  	var next = child.nextSibling;
  	if(previous){
  		previous.nextSibling = next;
  	}else{
  		parentNode.firstChild = next
  	}
  	if(next){
  		next.previousSibling = previous;
  	}else{
  		parentNode.lastChild = previous;
  	}
  	_onUpdateChild(parentNode.ownerDocument,parentNode);
  	return child;
  }
  /**
   * preformance key(refChild == null)
   */
  function _insertBefore(parentNode,newChild,nextChild){
  	var cp = newChild.parentNode;
  	if(cp){
  		cp.removeChild(newChild);//remove and update
  	}
  	if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
  		var newFirst = newChild.firstChild;
  		if (newFirst == null) {
  			return newChild;
  		}
  		var newLast = newChild.lastChild;
  	}else{
  		newFirst = newLast = newChild;
  	}
  	var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
  
  	newFirst.previousSibling = pre;
  	newLast.nextSibling = nextChild;
  	
  	
  	if(pre){
  		pre.nextSibling = newFirst;
  	}else{
  		parentNode.firstChild = newFirst;
  	}
  	if(nextChild == null){
  		parentNode.lastChild = newLast;
  	}else{
  		nextChild.previousSibling = newLast;
  	}
  	do{
  		newFirst.parentNode = parentNode;
  	}while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
  	_onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
  	//console.log(parentNode.lastChild.nextSibling == null)
  	if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
  		newChild.firstChild = newChild.lastChild = null;
  	}
  	return newChild;
  }
  function _appendSingleChild(parentNode,newChild){
  	var cp = newChild.parentNode;
  	if(cp){
  		var pre = parentNode.lastChild;
  		cp.removeChild(newChild);//remove and update
  		var pre = parentNode.lastChild;
  	}
  	var pre = parentNode.lastChild;
  	newChild.parentNode = parentNode;
  	newChild.previousSibling = pre;
  	newChild.nextSibling = null;
  	if(pre){
  		pre.nextSibling = newChild;
  	}else{
  		parentNode.firstChild = newChild;
  	}
  	parentNode.lastChild = newChild;
  	_onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
  	return newChild;
  	//console.log("__aa",parentNode.lastChild.nextSibling == null)
  }
  Document.prototype = {
  	//implementation : null,
  	nodeName :  '#document',
  	nodeType :  DOCUMENT_NODE,
  	doctype :  null,
  	documentElement :  null,
  	_inc : 1,
  	
  	insertBefore :  function(newChild, refChild){//raises 
  		if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
  			var child = newChild.firstChild;
  			while(child){
  				var next = child.nextSibling;
  				this.insertBefore(child,refChild);
  				child = next;
  			}
  			return newChild;
  		}
  		if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
  			this.documentElement = newChild;
  		}
  		
  		return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
  	},
  	removeChild :  function(oldChild){
  		if(this.documentElement == oldChild){
  			this.documentElement = null;
  		}
  		return _removeChild(this,oldChild);
  	},
  	// Introduced in DOM Level 2:
  	importNode : function(importedNode,deep){
  		return importNode(this,importedNode,deep);
  	},
  	// Introduced in DOM Level 2:
  	getElementById :	function(id){
  		var rtv = null;
  		_visitNode(this.documentElement,function(node){
  			if(node.nodeType == ELEMENT_NODE){
  				if(node.getAttribute('id') == id){
  					rtv = node;
  					return true;
  				}
  			}
  		})
  		return rtv;
  	},
  	
  	//document factory method:
  	createElement :	function(tagName){
  		var node = new Element();
  		node.ownerDocument = this;
  		node.nodeName = tagName;
  		node.tagName = tagName;
  		node.childNodes = new NodeList();
  		var attrs	= node.attributes = new NamedNodeMap();
  		attrs._ownerElement = node;
  		return node;
  	},
  	createDocumentFragment :	function(){
  		var node = new DocumentFragment();
  		node.ownerDocument = this;
  		node.childNodes = new NodeList();
  		return node;
  	},
  	createTextNode :	function(data){
  		var node = new Text();
  		node.ownerDocument = this;
  		node.appendData(data)
  		return node;
  	},
  	createComment :	function(data){
  		var node = new Comment();
  		node.ownerDocument = this;
  		node.appendData(data)
  		return node;
  	},
  	createCDATASection :	function(data){
  		var node = new CDATASection();
  		node.ownerDocument = this;
  		node.appendData(data)
  		return node;
  	},
  	createProcessingInstruction :	function(target,data){
  		var node = new ProcessingInstruction();
  		node.ownerDocument = this;
  		node.tagName = node.target = target;
  		node.nodeValue= node.data = data;
  		return node;
  	},
  	createAttribute :	function(name){
  		var node = new Attr();
  		node.ownerDocument	= this;
  		node.name = name;
  		node.nodeName	= name;
  		node.localName = name;
  		node.specified = true;
  		return node;
  	},
  	createEntityReference :	function(name){
  		var node = new EntityReference();
  		node.ownerDocument	= this;
  		node.nodeName	= name;
  		return node;
  	},
  	// Introduced in DOM Level 2:
  	createElementNS :	function(namespaceURI,qualifiedName){
  		var node = new Element();
  		var pl = qualifiedName.split(':');
  		var attrs	= node.attributes = new NamedNodeMap();
  		node.childNodes = new NodeList();
  		node.ownerDocument = this;
  		node.nodeName = qualifiedName;
  		node.tagName = qualifiedName;
  		node.namespaceURI = namespaceURI;
  		if(pl.length == 2){
  			node.prefix = pl[0];
  			node.localName = pl[1];
  		}else{
  			//el.prefix = null;
  			node.localName = qualifiedName;
  		}
  		attrs._ownerElement = node;
  		return node;
  	},
  	// Introduced in DOM Level 2:
  	createAttributeNS :	function(namespaceURI,qualifiedName){
  		var node = new Attr();
  		var pl = qualifiedName.split(':');
  		node.ownerDocument = this;
  		node.nodeName = qualifiedName;
  		node.name = qualifiedName;
  		node.namespaceURI = namespaceURI;
  		node.specified = true;
  		if(pl.length == 2){
  			node.prefix = pl[0];
  			node.localName = pl[1];
  		}else{
  			//el.prefix = null;
  			node.localName = qualifiedName;
  		}
  		return node;
  	}
  };
  _extends(Document,Node);
  
  
  function Element() {
  	this._nsMap = {};
  };
  Element.prototype = {
  	nodeType : ELEMENT_NODE,
  	hasAttribute : function(name){
  		return this.getAttributeNode(name)!=null;
  	},
  	getAttribute : function(name){
  		var attr = this.getAttributeNode(name);
  		return attr && attr.value || '';
  	},
  	getAttributeNode : function(name){
  		return this.attributes.getNamedItem(name);
  	},
  	setAttribute : function(name, value){
  		var attr = this.ownerDocument.createAttribute(name);
  		attr.value = attr.nodeValue = "" + value;
  		this.setAttributeNode(attr)
  	},
  	removeAttribute : function(name){
  		var attr = this.getAttributeNode(name)
  		attr && this.removeAttributeNode(attr);
  	},
  	
  	//four real opeartion method
  	appendChild:function(newChild){
  		if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
  			return this.insertBefore(newChild,null);
  		}else{
  			return _appendSingleChild(this,newChild);
  		}
  	},
  	setAttributeNode : function(newAttr){
  		return this.attributes.setNamedItem(newAttr);
  	},
  	setAttributeNodeNS : function(newAttr){
  		return this.attributes.setNamedItemNS(newAttr);
  	},
  	removeAttributeNode : function(oldAttr){
  		//console.log(this == oldAttr.ownerElement)
  		return this.attributes.removeNamedItem(oldAttr.nodeName);
  	},
  	//get real attribute name,and remove it by removeAttributeNode
  	removeAttributeNS : function(namespaceURI, localName){
  		var old = this.getAttributeNodeNS(namespaceURI, localName);
  		old && this.removeAttributeNode(old);
  	},
  	
  	hasAttributeNS : function(namespaceURI, localName){
  		return this.getAttributeNodeNS(namespaceURI, localName)!=null;
  	},
  	getAttributeNS : function(namespaceURI, localName){
  		var attr = this.getAttributeNodeNS(namespaceURI, localName);
  		return attr && attr.value || '';
  	},
  	setAttributeNS : function(namespaceURI, qualifiedName, value){
  		var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
  		attr.value = attr.nodeValue = "" + value;
  		this.setAttributeNode(attr)
  	},
  	getAttributeNodeNS : function(namespaceURI, localName){
  		return this.attributes.getNamedItemNS(namespaceURI, localName);
  	},
  	
  	getElementsByTagName : function(tagName){
  		return new LiveNodeList(this,function(base){
  			var ls = [];
  			_visitNode(base,function(node){
  				if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
  					ls.push(node);
  				}
  			});
  			return ls;
  		});
  	},
  	getElementsByTagNameNS : function(namespaceURI, localName){
  		return new LiveNodeList(this,function(base){
  			var ls = [];
  			_visitNode(base,function(node){
  				if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
  					ls.push(node);
  				}
  			});
  			return ls;
  			
  		});
  	}
  };
  Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
  Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
  
  
  _extends(Element,Node);
  function Attr() {
  };
  Attr.prototype.nodeType = ATTRIBUTE_NODE;
  _extends(Attr,Node);
  
  
  function CharacterData() {
  };
  CharacterData.prototype = {
  	data : '',
  	substringData : function(offset, count) {
  		return this.data.substring(offset, offset+count);
  	},
  	appendData: function(text) {
  		text = this.data+text;
  		this.nodeValue = this.data = text;
  		this.length = text.length;
  	},
  	insertData: function(offset,text) {
  		this.replaceData(offset,0,text);
  	
  	},
  	appendChild:function(newChild){
  		throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
  	},
  	deleteData: function(offset, count) {
  		this.replaceData(offset,count,"");
  	},
  	replaceData: function(offset, count, text) {
  		var start = this.data.substring(0,offset);
  		var end = this.data.substring(offset+count);
  		text = start + text + end;
  		this.nodeValue = this.data = text;
  		this.length = text.length;
  	}
  }
  _extends(CharacterData,Node);
  function Text() {
  };
  Text.prototype = {
  	nodeName : "#text",
  	nodeType : TEXT_NODE,
  	splitText : function(offset) {
  		var text = this.data;
  		var newText = text.substring(offset);
  		text = text.substring(0, offset);
  		this.data = this.nodeValue = text;
  		this.length = text.length;
  		var newNode = this.ownerDocument.createTextNode(newText);
  		if(this.parentNode){
  			this.parentNode.insertBefore(newNode, this.nextSibling);
  		}
  		return newNode;
  	}
  }
  _extends(Text,CharacterData);
  function Comment() {
  };
  Comment.prototype = {
  	nodeName : "#comment",
  	nodeType : COMMENT_NODE
  }
  _extends(Comment,CharacterData);
  
  function CDATASection() {
  };
  CDATASection.prototype = {
  	nodeName : "#cdata-section",
  	nodeType : CDATA_SECTION_NODE
  }
  _extends(CDATASection,CharacterData);
  
  
  function DocumentType() {
  };
  DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
  _extends(DocumentType,Node);
  
  function Notation() {
  };
  Notation.prototype.nodeType = NOTATION_NODE;
  _extends(Notation,Node);
  
  function Entity() {
  };
  Entity.prototype.nodeType = ENTITY_NODE;
  _extends(Entity,Node);
  
  function EntityReference() {
  };
  EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
  _extends(EntityReference,Node);
  
  function DocumentFragment() {
  };
  DocumentFragment.prototype.nodeName =	"#document-fragment";
  DocumentFragment.prototype.nodeType =	DOCUMENT_FRAGMENT_NODE;
  _extends(DocumentFragment,Node);
  
  
  function ProcessingInstruction() {
  }
  ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
  _extends(ProcessingInstruction,Node);
  function XMLSerializer(){}
  XMLSerializer.prototype.serializeToString = function(node,isHtml,nodeFilter){
  	return nodeSerializeToString.call(node,isHtml,nodeFilter);
  }
  Node.prototype.toString = nodeSerializeToString;
  function nodeSerializeToString(isHtml,nodeFilter){
  	var buf = [];
  	var refNode = this.nodeType == 9?this.documentElement:this;
  	var prefix = refNode.prefix;
  	var uri = refNode.namespaceURI;
  	
  	if(uri && prefix == null){
  		//console.log(prefix)
  		var prefix = refNode.lookupPrefix(uri);
  		if(prefix == null){
  			//isHTML = true;
  			var visibleNamespaces=[
  			{namespace:uri,prefix:null}
  			//{namespace:uri,prefix:''}
  			]
  		}
  	}
  	serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
  	//console.log('###',this.nodeType,uri,prefix,buf.join(''))
  	return buf.join('');
  }
  function needNamespaceDefine(node,isHTML, visibleNamespaces) {
  	var prefix = node.prefix||'';
  	var uri = node.namespaceURI;
  	if (!prefix && !uri){
  		return false;
  	}
  	if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
  		|| uri == 'http://www.w3.org/2000/xmlns/'){
  		return false;
  	}
  	
  	var i = visibleNamespaces.length 
  	//console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
  	while (i--) {
  		var ns = visibleNamespaces[i];
  		// get namespace prefix
  		//console.log(node.nodeType,node.tagName,ns.prefix,prefix)
  		if (ns.prefix == prefix){
  			return ns.namespace != uri;
  		}
  	}
  	//console.log(isHTML,uri,prefix=='')
  	//if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
  	//	return false;
  	//}
  	//node.flag = '11111'
  	//console.error(3,true,node.flag,node.prefix,node.namespaceURI)
  	return true;
  }
  function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
  	if(nodeFilter){
  		node = nodeFilter(node);
  		if(node){
  			if(typeof node == 'string'){
  				buf.push(node);
  				return;
  			}
  		}else{
  			return;
  		}
  		//buf.sort.apply(attrs, attributeSorter);
  	}
  	switch(node.nodeType){
  	case ELEMENT_NODE:
  		if (!visibleNamespaces) visibleNamespaces = [];
  		var startVisibleNamespaces = visibleNamespaces.length;
  		var attrs = node.attributes;
  		var len = attrs.length;
  		var child = node.firstChild;
  		var nodeName = node.tagName;
  		
  		isHTML =  (htmlns === node.namespaceURI) ||isHTML 
  		buf.push('<',nodeName);
  		
  		
  		
  		for(var i=0;i<len;i++){
  			// add namespaces for attributes
  			var attr = attrs.item(i);
  			if (attr.prefix == 'xmlns') {
  				visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
  			}else if(attr.nodeName == 'xmlns'){
  				visibleNamespaces.push({ prefix: '', namespace: attr.value });
  			}
  		}
  		for(var i=0;i<len;i++){
  			var attr = attrs.item(i);
  			if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
  				var prefix = attr.prefix||'';
  				var uri = attr.namespaceURI;
  				var ns = prefix ? ' xmlns:' + prefix : " xmlns";
  				buf.push(ns, '="' , uri , '"');
  				visibleNamespaces.push({ prefix: prefix, namespace:uri });
  			}
  			serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
  		}
  		// add namespace for current node		
  		if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
  			var prefix = node.prefix||'';
  			var uri = node.namespaceURI;
  			var ns = prefix ? ' xmlns:' + prefix : " xmlns";
  			buf.push(ns, '="' , uri , '"');
  			visibleNamespaces.push({ prefix: prefix, namespace:uri });
  		}
  		
  		if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
  			buf.push('>');
  			//if is cdata child node
  			if(isHTML && /^script$/i.test(nodeName)){
  				while(child){
  					if(child.data){
  						buf.push(child.data);
  					}else{
  						serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
  					}
  					child = child.nextSibling;
  				}
  			}else
  			{
  				while(child){
  					serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
  					child = child.nextSibling;
  				}
  			}
  			buf.push('</',nodeName,'>');
  		}else{
  			buf.push('/>');
  		}
  		// remove added visible namespaces
  		//visibleNamespaces.length = startVisibleNamespaces;
  		return;
  	case DOCUMENT_NODE:
  	case DOCUMENT_FRAGMENT_NODE:
  		var child = node.firstChild;
  		while(child){
  			serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
  			child = child.nextSibling;
  		}
  		return;
  	case ATTRIBUTE_NODE:
  		return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
  	case TEXT_NODE:
  		return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
  	case CDATA_SECTION_NODE:
  		return buf.push( '<![CDATA[',node.data,']]>');
  	case COMMENT_NODE:
  		return buf.push( "<!--",node.data,"-->");
  	case DOCUMENT_TYPE_NODE:
  		var pubid = node.publicId;
  		var sysid = node.systemId;
  		buf.push('<!DOCTYPE ',node.name);
  		if(pubid){
  			buf.push(' PUBLIC "',pubid);
  			if (sysid && sysid!='.') {
  				buf.push( '" "',sysid);
  			}
  			buf.push('">');
  		}else if(sysid && sysid!='.'){
  			buf.push(' SYSTEM "',sysid,'">');
  		}else{
  			var sub = node.internalSubset;
  			if(sub){
  				buf.push(" [",sub,"]");
  			}
  			buf.push(">");
  		}
  		return;
  	case PROCESSING_INSTRUCTION_NODE:
  		return buf.push( "<?",node.target," ",node.data,"?>");
  	case ENTITY_REFERENCE_NODE:
  		return buf.push( '&',node.nodeName,';');
  	//case ENTITY_NODE:
  	//case NOTATION_NODE:
  	default:
  		buf.push('??',node.nodeName);
  	}
  }
  function importNode(doc,node,deep){
  	var node2;
  	switch (node.nodeType) {
  	case ELEMENT_NODE:
  		node2 = node.cloneNode(false);
  		node2.ownerDocument = doc;
  		//var attrs = node2.attributes;
  		//var len = attrs.length;
  		//for(var i=0;i<len;i++){
  			//node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
  		//}
  	case DOCUMENT_FRAGMENT_NODE:
  		break;
  	case ATTRIBUTE_NODE:
  		deep = true;
  		break;
  	//case ENTITY_REFERENCE_NODE:
  	//case PROCESSING_INSTRUCTION_NODE:
  	////case TEXT_NODE:
  	//case CDATA_SECTION_NODE:
  	//case COMMENT_NODE:
  	//	deep = false;
  	//	break;
  	//case DOCUMENT_NODE:
  	//case DOCUMENT_TYPE_NODE:
  	//cannot be imported.
  	//case ENTITY_NODE:
  	//case NOTATION_NODE：
  	//can not hit in level3
  	//default:throw e;
  	}
  	if(!node2){
  		node2 = node.cloneNode(false);//false
  	}
  	node2.ownerDocument = doc;
  	node2.parentNode = null;
  	if(deep){
  		var child = node.firstChild;
  		while(child){
  			node2.appendChild(importNode(doc,child,deep));
  			child = child.nextSibling;
  		}
  	}
  	return node2;
  }
  //
  //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
  //					attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
  function cloneNode(doc,node,deep){
  	var node2 = new node.constructor();
  	for(var n in node){
  		var v = node[n];
  		if(typeof v != 'object' ){
  			if(v != node2[n]){
  				node2[n] = v;
  			}
  		}
  	}
  	if(node.childNodes){
  		node2.childNodes = new NodeList();
  	}
  	node2.ownerDocument = doc;
  	switch (node2.nodeType) {
  	case ELEMENT_NODE:
  		var attrs	= node.attributes;
  		var attrs2	= node2.attributes = new NamedNodeMap();
  		var len = attrs.length
  		attrs2._ownerElement = node2;
  		for(var i=0;i<len;i++){
  			node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
  		}
  		break;;
  	case ATTRIBUTE_NODE:
  		deep = true;
  	}
  	if(deep){
  		var child = node.firstChild;
  		while(child){
  			node2.appendChild(cloneNode(doc,child,deep));
  			child = child.nextSibling;
  		}
  	}
  	return node2;
  }
  
  function __set__(object,key,value){
  	object[key] = value
  }
  //do dynamic
  try{
  	if(Object.defineProperty){
  		Object.defineProperty(LiveNodeList.prototype,'length',{
  			get:function(){
  				_updateLiveList(this);
  				return this.$$length;
  			}
  		});
  		Object.defineProperty(Node.prototype,'textContent',{
  			get:function(){
  				return getTextContent(this);
  			},
  			set:function(data){
  				switch(this.nodeType){
  				case ELEMENT_NODE:
  				case DOCUMENT_FRAGMENT_NODE:
  					while(this.firstChild){
  						this.removeChild(this.firstChild);
  					}
  					if(data || String(data)){
  						this.appendChild(this.ownerDocument.createTextNode(data));
  					}
  					break;
  				default:
  					//TODO:
  					this.data = data;
  					this.value = data;
  					this.nodeValue = data;
  				}
  			}
  		})
  		
  		function getTextContent(node){
  			switch(node.nodeType){
  			case ELEMENT_NODE:
  			case DOCUMENT_FRAGMENT_NODE:
  				var buf = [];
  				node = node.firstChild;
  				while(node){
  					if(node.nodeType!==7 && node.nodeType !==8){
  						buf.push(getTextContent(node));
  					}
  					node = node.nextSibling;
  				}
  				return buf.join('');
  			default:
  				return node.nodeValue;
  			}
  		}
  		__set__ = function(object,key,value){
  			//console.log(value)
  			object['$$'+key] = value
  		}
  	}
  }catch(e){//ie8
  }
  
  //if(typeof require == 'function'){
  	exports.DOMImplementation = DOMImplementation;
  	exports.XMLSerializer = XMLSerializer;
  //}
  
  
  /***/ }),
  /* 2 */
  /***/ (function(module, exports) {
  
  var initEvent = function (cos) {
      var listeners = {};
      var getList = function (action) {
          !listeners[action] && (listeners[action] = []);
          return listeners[action];
      };
      cos.on = function (action, callback) {
          if (action === 'task-list-update') {
              console.warn('warning: Event "' + action + '" has been deprecated. Please use "list-update" instead.');
          }
          getList(action).push(callback);
      };
      cos.off = function (action, callback) {
          var list = getList(action);
          for (var i = list.length - 1; i >= 0; i--) {
              callback === list[i] && list.splice(i, 1);
          }
      };
      cos.emit = function (action, data) {
          var list = getList(action).map(function (cb) {
              return cb;
          });
          for (var i = 0; i < list.length; i++) {
              list[i](data);
          }
      };
  };
  
  var EventProxy = function () {
      initEvent(this);
  };
  
  module.exports.init = initEvent;
  module.exports.EventProxy = EventProxy;
  
  /***/ }),
  /* 3 */
  /***/ (function(module, exports, __webpack_require__) {
  
  var COS = __webpack_require__(4);
  module.exports = COS;
  
  /***/ }),
  /* 4 */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  
  var util = __webpack_require__(0);
  var event = __webpack_require__(2);
  var task = __webpack_require__(11);
  var base = __webpack_require__(12);
  var advance = __webpack_require__(17);
  
  var defaultOptions = {
      AppId: '', // AppId 已废弃，请拼接到 Bucket 后传入，例如：test-1250000000
      SecretId: '',
      SecretKey: '',
      XCosSecurityToken: '', // 使用临时密钥需要注意自行刷新 Token
      FileParallelLimit: 3,
      ChunkParallelLimit: 3,
      ChunkRetryTimes: 3,
      ChunkSize: 1024 * 1024,
      SliceSize: 1024 * 1024,
      CopyChunkParallelLimit: 20,
      CopyChunkSize: 1024 * 1024 * 10,
      CopySliceSize: 1024 * 1024 * 10,
      MaxPartNumber: 10000,
      ProgressInterval: 1000,
      UploadQueueSize: 10000,
      Domain: '',
      ServiceDomain: '',
      Protocol: '',
      CompatibilityMode: false,
      ForcePathStyle: false,
      UseRawKey: false,
      Timeout: 0, // 单位毫秒，0 代表不设置超时时间
      CorrectClockSkew: true,
      SystemClockOffset: 0, // 单位毫秒，ms
      UploadCheckContentMd5: false,
      UploadIdCacheLimit: 50
  };
  
  // 对外暴露的类
  var COS = function (options) {
      this.options = util.extend(util.clone(defaultOptions), options || {});
      this.options.FileParallelLimit = Math.max(1, this.options.FileParallelLimit);
      this.options.ChunkParallelLimit = Math.max(1, this.options.ChunkParallelLimit);
      this.options.ChunkRetryTimes = Math.max(0, this.options.ChunkRetryTimes);
      this.options.ChunkSize = Math.max(1024 * 1024, this.options.ChunkSize);
      this.options.CopyChunkParallelLimit = Math.max(1, this.options.CopyChunkParallelLimit);
      this.options.CopyChunkSize = Math.max(1024 * 1024, this.options.CopyChunkSize);
      this.options.CopySliceSize = Math.max(0, this.options.CopySliceSize);
      this.options.MaxPartNumber = Math.max(1024, Math.min(10000, this.options.MaxPartNumber));
      this.options.Timeout = Math.max(0, this.options.Timeout);
      if (this.options.AppId) {
          console.warn('warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g: "test-1250000000").');
      }
      event.init(this);
      task.init(this);
  };
  
  base.init(COS, task);
  advance.init(COS, task);
  
  COS.getAuthorization = util.getAuth;
  COS.version = '0.5.15';
  
  module.exports = COS;
  
  /***/ }),
  /* 5 */
  /***/ (function(module, exports) {
  
  function md5cycle(x, k) {
      var a = x[0],
          b = x[1],
          c = x[2],
          d = x[3];
  
      a = ff(a, b, c, d, k[0], 7, -680876936);
      d = ff(d, a, b, c, k[1], 12, -389564586);
      c = ff(c, d, a, b, k[2], 17, 606105819);
      b = ff(b, c, d, a, k[3], 22, -1044525330);
      a = ff(a, b, c, d, k[4], 7, -176418897);
      d = ff(d, a, b, c, k[5], 12, 1200080426);
      c = ff(c, d, a, b, k[6], 17, -1473231341);
      b = ff(b, c, d, a, k[7], 22, -45705983);
      a = ff(a, b, c, d, k[8], 7, 1770035416);
      d = ff(d, a, b, c, k[9], 12, -1958414417);
      c = ff(c, d, a, b, k[10], 17, -42063);
      b = ff(b, c, d, a, k[11], 22, -1990404162);
      a = ff(a, b, c, d, k[12], 7, 1804603682);
      d = ff(d, a, b, c, k[13], 12, -40341101);
      c = ff(c, d, a, b, k[14], 17, -1502002290);
      b = ff(b, c, d, a, k[15], 22, 1236535329);
  
      a = gg(a, b, c, d, k[1], 5, -165796510);
      d = gg(d, a, b, c, k[6], 9, -1069501632);
      c = gg(c, d, a, b, k[11], 14, 643717713);
      b = gg(b, c, d, a, k[0], 20, -373897302);
      a = gg(a, b, c, d, k[5], 5, -701558691);
      d = gg(d, a, b, c, k[10], 9, 38016083);
      c = gg(c, d, a, b, k[15], 14, -660478335);
      b = gg(b, c, d, a, k[4], 20, -405537848);
      a = gg(a, b, c, d, k[9], 5, 568446438);
      d = gg(d, a, b, c, k[14], 9, -1019803690);
      c = gg(c, d, a, b, k[3], 14, -187363961);
      b = gg(b, c, d, a, k[8], 20, 1163531501);
      a = gg(a, b, c, d, k[13], 5, -1444681467);
      d = gg(d, a, b, c, k[2], 9, -51403784);
      c = gg(c, d, a, b, k[7], 14, 1735328473);
      b = gg(b, c, d, a, k[12], 20, -1926607734);
  
      a = hh(a, b, c, d, k[5], 4, -378558);
      d = hh(d, a, b, c, k[8], 11, -2022574463);
      c = hh(c, d, a, b, k[11], 16, 1839030562);
      b = hh(b, c, d, a, k[14], 23, -35309556);
      a = hh(a, b, c, d, k[1], 4, -1530992060);
      d = hh(d, a, b, c, k[4], 11, 1272893353);
      c = hh(c, d, a, b, k[7], 16, -155497632);
      b = hh(b, c, d, a, k[10], 23, -1094730640);
      a = hh(a, b, c, d, k[13], 4, 681279174);
      d = hh(d, a, b, c, k[0], 11, -358537222);
      c = hh(c, d, a, b, k[3], 16, -722521979);
      b = hh(b, c, d, a, k[6], 23, 76029189);
      a = hh(a, b, c, d, k[9], 4, -640364487);
      d = hh(d, a, b, c, k[12], 11, -421815835);
      c = hh(c, d, a, b, k[15], 16, 530742520);
      b = hh(b, c, d, a, k[2], 23, -995338651);
  
      a = ii(a, b, c, d, k[0], 6, -198630844);
      d = ii(d, a, b, c, k[7], 10, 1126891415);
      c = ii(c, d, a, b, k[14], 15, -1416354905);
      b = ii(b, c, d, a, k[5], 21, -57434055);
      a = ii(a, b, c, d, k[12], 6, 1700485571);
      d = ii(d, a, b, c, k[3], 10, -1894986606);
      c = ii(c, d, a, b, k[10], 15, -1051523);
      b = ii(b, c, d, a, k[1], 21, -2054922799);
      a = ii(a, b, c, d, k[8], 6, 1873313359);
      d = ii(d, a, b, c, k[15], 10, -30611744);
      c = ii(c, d, a, b, k[6], 15, -1560198380);
      b = ii(b, c, d, a, k[13], 21, 1309151649);
      a = ii(a, b, c, d, k[4], 6, -145523070);
      d = ii(d, a, b, c, k[11], 10, -1120210379);
      c = ii(c, d, a, b, k[2], 15, 718787259);
      b = ii(b, c, d, a, k[9], 21, -343485551);
  
      x[0] = add32(a, x[0]);
      x[1] = add32(b, x[1]);
      x[2] = add32(c, x[2]);
      x[3] = add32(d, x[3]);
  }
  
  function cmn(q, a, b, x, s, t) {
      a = add32(add32(a, q), add32(x, t));
      return add32(a << s | a >>> 32 - s, b);
  }
  
  function ff(a, b, c, d, x, s, t) {
      return cmn(b & c | ~b & d, a, b, x, s, t);
  }
  
  function gg(a, b, c, d, x, s, t) {
      return cmn(b & d | c & ~d, a, b, x, s, t);
  }
  
  function hh(a, b, c, d, x, s, t) {
      return cmn(b ^ c ^ d, a, b, x, s, t);
  }
  
  function ii(a, b, c, d, x, s, t) {
      return cmn(c ^ (b | ~d), a, b, x, s, t);
  }
  
  function md51(s) {
      var n = s.length,
          state = [1732584193, -271733879, -1732584194, 271733878],
          i;
      for (i = 64; i <= s.length; i += 64) {
          md5cycle(state, md5blk(s.substring(i - 64, i)));
      }
      s = s.substring(i - 64);
      var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
      for (i = 0; i < s.length; i++) tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3);
      tail[i >> 2] |= 0x80 << (i % 4 << 3);
      if (i > 55) {
          md5cycle(state, tail);
          for (i = 0; i < 16; i++) tail[i] = 0;
      }
      tail[14] = n * 8;
      md5cycle(state, tail);
      return state;
  }
  
  /* there needs to be support for Unicode here,
   * unless we pretend that we can redefine the MD-5
   * algorithm for multi-byte characters (perhaps
   * by adding every four 16-bit characters and
   * shortening the sum to 32 bits). Otherwise
   * I suggest performing MD-5 as if every character
   * was two bytes--e.g., 0040 0025 = @%--but then
   * how will an ordinary MD-5 sum be matched?
   * There is no way to standardize text to something
   * like UTF-8 before transformation; speed cost is
   * utterly prohibitive. The JavaScript standard
   * itself needs to look at this: it should start
   * providing access to strings as preformed UTF-8
   * 8-bit unsigned value arrays.
   */
  function md5blk(s) {
      /* I figured global was faster.   */
      var md5blks = [],
          i; /* Andy King said do it this way. */
      for (i = 0; i < 64; i += 4) {
          md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
      }
      return md5blks;
  }
  
  var hex_chr = '0123456789abcdef'.split('');
  
  function rhex(n) {
      var s = '',
          j = 0;
      for (; j < 4; j++) s += hex_chr[n >> j * 8 + 4 & 0x0F] + hex_chr[n >> j * 8 & 0x0F];
      return s;
  }
  
  function hex(x) {
      for (var i = 0; i < x.length; i++) x[i] = rhex(x[i]);
      return x.join('');
  }
  
  function Utf8Encode(string) {
      string = string.replace(/\r\n/g, "\n");
      var utftext = "";
      for (var n = 0; n < string.length; n++) {
          var c = string.charCodeAt(n);
          if (c < 128) {
              utftext += String.fromCharCode(c);
          } else if (c > 127 && c < 2048) {
              utftext += String.fromCharCode(c >> 6 | 192);
              utftext += String.fromCharCode(c & 63 | 128);
          } else {
              utftext += String.fromCharCode(c >> 12 | 224);
              utftext += String.fromCharCode(c >> 6 & 63 | 128);
              utftext += String.fromCharCode(c & 63 | 128);
          }
      }
      return utftext;
  }
  
  function md5(s, isBinaryString) {
      if (!isBinaryString) {
          s = Utf8Encode(s);
      }
      return hex(md51(s));
  }
  
  /* this function is much faster,
  so if possible we use it. Some IEs
  are the only ones I know of that
  need the idiotic second function,
  generated by an if clause.  */
  
  var add32 = function (a, b) {
      return a + b & 0xFFFFFFFF;
  };
  if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {
      add32 = function (x, y) {
          var lsw = (x & 0xFFFF) + (y & 0xFFFF),
              msw = (x >> 16) + (y >> 16) + (lsw >> 16);
          return msw << 16 | lsw & 0xFFFF;
      };
  }
  
  module.exports = md5;
  
  /***/ }),
  /* 6 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /*
   CryptoJS v3.1.2
   code.google.com/p/crypto-js
   (c) 2009-2013 by Jeff Mott. All rights reserved.
   code.google.com/p/crypto-js/wiki/License
   */
  var CryptoJS = CryptoJS || function (g, l) {
      var e = {},
          d = e.lib = {},
          m = function () {},
          k = d.Base = { extend: function (a) {
              m.prototype = this;var c = new m();a && c.mixIn(a);c.hasOwnProperty("init") || (c.init = function () {
                  c.$super.init.apply(this, arguments);
              });c.init.prototype = c;c.$super = this;return c;
          }, create: function () {
              var a = this.extend();a.init.apply(a, arguments);return a;
          }, init: function () {}, mixIn: function (a) {
              for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);a.hasOwnProperty("toString") && (this.toString = a.toString);
          }, clone: function () {
              return this.init.prototype.extend(this);
          } },
          p = d.WordArray = k.extend({ init: function (a, c) {
              a = this.words = a || [];this.sigBytes = c != l ? c : 4 * a.length;
          }, toString: function (a) {
              return (a || n).stringify(this);
          }, concat: function (a) {
              var c = this.words,
                  q = a.words,
                  f = this.sigBytes;a = a.sigBytes;this.clamp();if (f % 4) for (var b = 0; b < a; b++) c[f + b >>> 2] |= (q[b >>> 2] >>> 24 - 8 * (b % 4) & 255) << 24 - 8 * ((f + b) % 4);else if (65535 < q.length) for (b = 0; b < a; b += 4) c[f + b >>> 2] = q[b >>> 2];else c.push.apply(c, q);this.sigBytes += a;return this;
          }, clamp: function () {
              var a = this.words,
                  c = this.sigBytes;a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4);a.length = g.ceil(c / 4);
          }, clone: function () {
              var a = k.clone.call(this);a.words = this.words.slice(0);return a;
          }, random: function (a) {
              for (var c = [], b = 0; b < a; b += 4) c.push(4294967296 * g.random() | 0);return new p.init(c, a);
          } }),
          b = e.enc = {},
          n = b.Hex = { stringify: function (a) {
              var c = a.words;a = a.sigBytes;for (var b = [], f = 0; f < a; f++) {
                  var d = c[f >>> 2] >>> 24 - 8 * (f % 4) & 255;b.push((d >>> 4).toString(16));b.push((d & 15).toString(16));
              }return b.join("");
          }, parse: function (a) {
              for (var c = a.length, b = [], f = 0; f < c; f += 2) b[f >>> 3] |= parseInt(a.substr(f, 2), 16) << 24 - 4 * (f % 8);return new p.init(b, c / 2);
          } },
          j = b.Latin1 = { stringify: function (a) {
              var c = a.words;a = a.sigBytes;for (var b = [], f = 0; f < a; f++) b.push(String.fromCharCode(c[f >>> 2] >>> 24 - 8 * (f % 4) & 255));return b.join("");
          }, parse: function (a) {
              for (var c = a.length, b = [], f = 0; f < c; f++) b[f >>> 2] |= (a.charCodeAt(f) & 255) << 24 - 8 * (f % 4);return new p.init(b, c);
          } },
          h = b.Utf8 = { stringify: function (a) {
              try {
                  return decodeURIComponent(escape(j.stringify(a)));
              } catch (c) {
                  throw Error("Malformed UTF-8 data");
              }
          }, parse: function (a) {
              return j.parse(unescape(encodeURIComponent(a)));
          } },
          r = d.BufferedBlockAlgorithm = k.extend({ reset: function () {
              this._data = new p.init();this._nDataBytes = 0;
          }, _append: function (a) {
              "string" == typeof a && (a = h.parse(a));this._data.concat(a);this._nDataBytes += a.sigBytes;
          }, _process: function (a) {
              var c = this._data,
                  b = c.words,
                  f = c.sigBytes,
                  d = this.blockSize,
                  e = f / (4 * d),
                  e = a ? g.ceil(e) : g.max((e | 0) - this._minBufferSize, 0);a = e * d;f = g.min(4 * a, f);if (a) {
                  for (var k = 0; k < a; k += d) this._doProcessBlock(b, k);k = b.splice(0, a);c.sigBytes -= f;
              }return new p.init(k, f);
          }, clone: function () {
              var a = k.clone.call(this);
              a._data = this._data.clone();return a;
          }, _minBufferSize: 0 });d.Hasher = r.extend({ cfg: k.extend(), init: function (a) {
              this.cfg = this.cfg.extend(a);this.reset();
          }, reset: function () {
              r.reset.call(this);this._doReset();
          }, update: function (a) {
              this._append(a);this._process();return this;
          }, finalize: function (a) {
              a && this._append(a);return this._doFinalize();
          }, blockSize: 16, _createHelper: function (a) {
              return function (b, d) {
                  return new a.init(d).finalize(b);
              };
          }, _createHmacHelper: function (a) {
              return function (b, d) {
                  return new s.HMAC.init(a, d).finalize(b);
              };
          } });var s = e.algo = {};return e;
  }(Math);
  (function () {
      var g = CryptoJS,
          l = g.lib,
          e = l.WordArray,
          d = l.Hasher,
          m = [],
          l = g.algo.SHA1 = d.extend({ _doReset: function () {
              this._hash = new e.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]);
          }, _doProcessBlock: function (d, e) {
              for (var b = this._hash.words, n = b[0], j = b[1], h = b[2], g = b[3], l = b[4], a = 0; 80 > a; a++) {
                  if (16 > a) m[a] = d[e + a] | 0;else {
                      var c = m[a - 3] ^ m[a - 8] ^ m[a - 14] ^ m[a - 16];m[a] = c << 1 | c >>> 31;
                  }c = (n << 5 | n >>> 27) + l + m[a];c = 20 > a ? c + ((j & h | ~j & g) + 1518500249) : 40 > a ? c + ((j ^ h ^ g) + 1859775393) : 60 > a ? c + ((j & h | j & g | h & g) - 1894007588) : c + ((j ^ h ^ g) - 899497514);l = g;g = h;h = j << 30 | j >>> 2;j = n;n = c;
              }b[0] = b[0] + n | 0;b[1] = b[1] + j | 0;b[2] = b[2] + h | 0;b[3] = b[3] + g | 0;b[4] = b[4] + l | 0;
          }, _doFinalize: function () {
              var d = this._data,
                  e = d.words,
                  b = 8 * this._nDataBytes,
                  g = 8 * d.sigBytes;e[g >>> 5] |= 128 << 24 - g % 32;e[(g + 64 >>> 9 << 4) + 14] = Math.floor(b / 4294967296);e[(g + 64 >>> 9 << 4) + 15] = b;d.sigBytes = 4 * e.length;this._process();return this._hash;
          }, clone: function () {
              var e = d.clone.call(this);e._hash = this._hash.clone();return e;
          } });g.SHA1 = d._createHelper(l);g.HmacSHA1 = d._createHmacHelper(l);
  })();
  (function () {
      var g = CryptoJS,
          l = g.enc.Utf8;g.algo.HMAC = g.lib.Base.extend({ init: function (e, d) {
              e = this._hasher = new e.init();"string" == typeof d && (d = l.parse(d));var g = e.blockSize,
                  k = 4 * g;d.sigBytes > k && (d = e.finalize(d));d.clamp();for (var p = this._oKey = d.clone(), b = this._iKey = d.clone(), n = p.words, j = b.words, h = 0; h < g; h++) n[h] ^= 1549556828, j[h] ^= 909522486;p.sigBytes = b.sigBytes = k;this.reset();
          }, reset: function () {
              var e = this._hasher;e.reset();e.update(this._iKey);
          }, update: function (e) {
              this._hasher.update(e);return this;
          }, finalize: function (e) {
              var d = this._hasher;e = d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e));
          } });
  })();
  
  (function () {
      // Shortcuts
      var C = CryptoJS;
      var C_lib = C.lib;
      var WordArray = C_lib.WordArray;
      var C_enc = C.enc;
  
      /**
       * Base64 encoding strategy.
       */
      var Base64 = C_enc.Base64 = {
          /**
           * Converts a word array to a Base64 string.
           *
           * @param {WordArray} wordArray The word array.
           *
           * @return {string} The Base64 string.
           *
           * @static
           *
           * @example
           *
           *     var base64String = CryptoJS.enc.Base64.stringify(wordArray);
           */
          stringify: function (wordArray) {
              // Shortcuts
              var words = wordArray.words;
              var sigBytes = wordArray.sigBytes;
              var map = this._map;
  
              // Clamp excess bits
              wordArray.clamp();
  
              // Convert
              var base64Chars = [];
              for (var i = 0; i < sigBytes; i += 3) {
                  var byte1 = words[i >>> 2] >>> 24 - i % 4 * 8 & 0xff;
                  var byte2 = words[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 0xff;
                  var byte3 = words[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 0xff;
  
                  var triplet = byte1 << 16 | byte2 << 8 | byte3;
  
                  for (var j = 0; j < 4 && i + j * 0.75 < sigBytes; j++) {
                      base64Chars.push(map.charAt(triplet >>> 6 * (3 - j) & 0x3f));
                  }
              }
  
              // Add padding
              var paddingChar = map.charAt(64);
              if (paddingChar) {
                  while (base64Chars.length % 4) {
                      base64Chars.push(paddingChar);
                  }
              }
  
              return base64Chars.join('');
          },
  
          /**
           * Converts a Base64 string to a word array.
           *
           * @param {string} base64Str The Base64 string.
           *
           * @return {WordArray} The word array.
           *
           * @static
           *
           * @example
           *
           *     var wordArray = CryptoJS.enc.Base64.parse(base64String);
           */
          parse: function (base64Str) {
              // Shortcuts
              var base64StrLength = base64Str.length;
              var map = this._map;
  
              // Ignore padding
              var paddingChar = map.charAt(64);
              if (paddingChar) {
                  var paddingIndex = base64Str.indexOf(paddingChar);
                  if (paddingIndex != -1) {
                      base64StrLength = paddingIndex;
                  }
              }
  
              // Convert
              var words = [];
              var nBytes = 0;
              for (var i = 0; i < base64StrLength; i++) {
                  if (i % 4) {
                      var bits1 = map.indexOf(base64Str.charAt(i - 1)) << i % 4 * 2;
                      var bits2 = map.indexOf(base64Str.charAt(i)) >>> 6 - i % 4 * 2;
                      words[nBytes >>> 2] |= (bits1 | bits2) << 24 - nBytes % 4 * 8;
                      nBytes++;
                  }
              }
  
              return WordArray.create(words, nBytes);
          },
  
          _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
      };
  })();
  
  if (true) {
      module.exports = CryptoJS;
  } else {
      window.CryptoJS = CryptoJS;
  }
  
  /***/ }),
  /* 7 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Copyright 2015 William Summers, MetaTribal LLC
   * adapted from https://developer.mozilla.org/en-US/docs/JXON
   *
   * Licensed under the MIT License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     https://opensource.org/licenses/MIT
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /**
   * @author William Summers
   * https://github.com/metatribal/xmlToJSON
   */
  var DOMParser = __webpack_require__(8).DOMParser;
  
  var xmlToJSON = function () {
  
      this.version = "1.3.5";
  
      var options = { // set up the default options
          mergeCDATA: true, // extract cdata and merge with text
          normalize: true, // collapse multiple spaces to single space
          stripElemPrefix: true // for elements of same name in diff namespaces, you can enable namespaces and access the nskey property
      };
  
      var prefixMatch = new RegExp(/(?!xmlns)^.*:/);
      var trimMatch = new RegExp(/^\s+|\s+$/g);
  
      this.grokType = function (sValue) {
          if (/^\s*$/.test(sValue)) {
              return null;
          }
          if (/^(?:true|false)$/i.test(sValue)) {
              return sValue.toLowerCase() === "true";
          }
          if (isFinite(sValue)) {
              return parseFloat(sValue);
          }
          return sValue;
      };
  
      this.parseString = function (xmlString, opt) {
          if (xmlString) {
              var xml = this.stringToXML(xmlString);
              if (xml.getElementsByTagName('parsererror').length) {
                  return null;
              } else {
                  return this.parseXML(xml, opt);
              }
          } else {
              return null;
          }
      };
  
      this.parseXML = function (oXMLParent, opt) {
  
          // initialize options
          for (var key in opt) {
              options[key] = opt[key];
          }
  
          var vResult = {},
              nLength = 0,
              sCollectedTxt = "";
  
          // iterate over the children
          var childNum = oXMLParent.childNodes.length;
          if (childNum) {
              for (var oNode, sProp, vContent, nItem = 0; nItem < oXMLParent.childNodes.length; nItem++) {
                  oNode = oXMLParent.childNodes.item(nItem);
  
                  if (oNode.nodeType === 4) {
                      if (options.mergeCDATA) {
                          sCollectedTxt += oNode.nodeValue;
                      }
                  } /* nodeType is "CDATASection" (4) */
                  else if (oNode.nodeType === 3) {
                          sCollectedTxt += oNode.nodeValue;
                      } /* nodeType is "Text" (3) */
                      else if (oNode.nodeType === 1) {
                              /* nodeType is "Element" (1) */
  
                              if (nLength === 0) {
                                  vResult = {};
                              }
  
                              // using nodeName to support browser (IE) implementation with no 'localName' property
                              if (options.stripElemPrefix) {
                                  sProp = oNode.nodeName.replace(prefixMatch, '');
                              } else {
                                  sProp = oNode.nodeName;
                              }
  
                              vContent = xmlToJSON.parseXML(oNode);
  
                              if (vResult.hasOwnProperty(sProp)) {
                                  if (vResult[sProp].constructor !== Array) {
                                      vResult[sProp] = [vResult[sProp]];
                                  }
                                  vResult[sProp].push(vContent);
                              } else {
                                  vResult[sProp] = vContent;
                                  nLength++;
                              }
                          }
              }
          }
  
          if (!Object.keys(vResult).length) {
              // vResult = sCollectedTxt.replace(trimMatch, '') || ''; // by carsonxu 修复 getBucket返回的 Key 是 " /" 这种场景
              vResult = sCollectedTxt || '';
          }
  
          return vResult;
      };
  
      // Convert xmlDocument to a string
      // Returns null on failure
      this.xmlToString = function (xmlDoc) {
          try {
              var xmlString = xmlDoc.xml ? xmlDoc.xml : new XMLSerializer().serializeToString(xmlDoc);
              return xmlString;
          } catch (err) {
              return null;
          }
      };
  
      // Convert a string to XML Node Structure
      // Returns null on failure
      this.stringToXML = function (xmlString) {
          try {
              var xmlDoc = null;
  
              if (window.DOMParser) {
  
                  var parser = new DOMParser();
                  xmlDoc = parser.parseFromString(xmlString, "text/xml");
  
                  return xmlDoc;
              } else {
                  xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
                  xmlDoc.async = false;
                  xmlDoc.loadXML(xmlString);
  
                  return xmlDoc;
              }
          } catch (e) {
              return null;
          }
      };
  
      return this;
  }.call({});
  
  var xml2json = function (xmlString) {
      return xmlToJSON.parseString(xmlString);
  };
  
  module.exports = xml2json;
  
  /***/ }),
  /* 8 */
  /***/ (function(module, exports, __webpack_require__) {
  
  function DOMParser(options){
  	this.options = options ||{locator:{}};
  	
  }
  DOMParser.prototype.parseFromString = function(source,mimeType){
  	var options = this.options;
  	var sax =  new XMLReader();
  	var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
  	var errorHandler = options.errorHandler;
  	var locator = options.locator;
  	var defaultNSMap = options.xmlns||{};
  	var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"}
  	if(locator){
  		domBuilder.setDocumentLocator(locator)
  	}
  	
  	sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
  	sax.domBuilder = options.domBuilder || domBuilder;
  	if(/\/x?html?$/.test(mimeType)){
  		entityMap.nbsp = '\xa0';
  		entityMap.copy = '\xa9';
  		defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
  	}
  	defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
  	if(source){
  		sax.parse(source,defaultNSMap,entityMap);
  	}else{
  		sax.errorHandler.error("invalid doc source");
  	}
  	return domBuilder.doc;
  }
  function buildErrorHandler(errorImpl,domBuilder,locator){
  	if(!errorImpl){
  		if(domBuilder instanceof DOMHandler){
  			return domBuilder;
  		}
  		errorImpl = domBuilder ;
  	}
  	var errorHandler = {}
  	var isCallback = errorImpl instanceof Function;
  	locator = locator||{}
  	function build(key){
  		var fn = errorImpl[key];
  		if(!fn && isCallback){
  			fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl;
  		}
  		errorHandler[key] = fn && function(msg){
  			fn('[xmldom '+key+']\t'+msg+_locator(locator));
  		}||function(){};
  	}
  	build('warning');
  	build('error');
  	build('fatalError');
  	return errorHandler;
  }
  
  //console.log('#\n\n\n\n\n\n\n####')
  /**
   * +ContentHandler+ErrorHandler
   * +LexicalHandler+EntityResolver2
   * -DeclHandler-DTDHandler 
   * 
   * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
   * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
   * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
   */
  function DOMHandler() {
      this.cdata = false;
  }
  function position(locator,node){
  	node.lineNumber = locator.lineNumber;
  	node.columnNumber = locator.columnNumber;
  }
  /**
   * @see org.xml.sax.ContentHandler#startDocument
   * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
   */ 
  DOMHandler.prototype = {
  	startDocument : function() {
      	this.doc = new DOMImplementation().createDocument(null, null, null);
      	if (this.locator) {
          	this.doc.documentURI = this.locator.systemId;
      	}
  	},
  	startElement:function(namespaceURI, localName, qName, attrs) {
  		var doc = this.doc;
  	    var el = doc.createElementNS(namespaceURI, qName||localName);
  	    var len = attrs.length;
  	    appendElement(this, el);
  	    this.currentElement = el;
  	    
  		this.locator && position(this.locator,el)
  	    for (var i = 0 ; i < len; i++) {
  	        var namespaceURI = attrs.getURI(i);
  	        var value = attrs.getValue(i);
  	        var qName = attrs.getQName(i);
  			var attr = doc.createAttributeNS(namespaceURI, qName);
  			this.locator &&position(attrs.getLocator(i),attr);
  			attr.value = attr.nodeValue = value;
  			el.setAttributeNode(attr)
  	    }
  	},
  	endElement:function(namespaceURI, localName, qName) {
  		var current = this.currentElement
  		var tagName = current.tagName;
  		this.currentElement = current.parentNode;
  	},
  	startPrefixMapping:function(prefix, uri) {
  	},
  	endPrefixMapping:function(prefix) {
  	},
  	processingInstruction:function(target, data) {
  	    var ins = this.doc.createProcessingInstruction(target, data);
  	    this.locator && position(this.locator,ins)
  	    appendElement(this, ins);
  	},
  	ignorableWhitespace:function(ch, start, length) {
  	},
  	characters:function(chars, start, length) {
  		chars = _toString.apply(this,arguments)
  		//console.log(chars)
  		if(chars){
  			if (this.cdata) {
  				var charNode = this.doc.createCDATASection(chars);
  			} else {
  				var charNode = this.doc.createTextNode(chars);
  			}
  			if(this.currentElement){
  				this.currentElement.appendChild(charNode);
  			}else if(/^\s*$/.test(chars)){
  				this.doc.appendChild(charNode);
  				//process xml
  			}
  			this.locator && position(this.locator,charNode)
  		}
  	},
  	skippedEntity:function(name) {
  	},
  	endDocument:function() {
  		this.doc.normalize();
  	},
  	setDocumentLocator:function (locator) {
  	    if(this.locator = locator){// && !('lineNumber' in locator)){
  	    	locator.lineNumber = 0;
  	    }
  	},
  	//LexicalHandler
  	comment:function(chars, start, length) {
  		chars = _toString.apply(this,arguments)
  	    var comm = this.doc.createComment(chars);
  	    this.locator && position(this.locator,comm)
  	    appendElement(this, comm);
  	},
  	
  	startCDATA:function() {
  	    //used in characters() methods
  	    this.cdata = true;
  	},
  	endCDATA:function() {
  	    this.cdata = false;
  	},
  	
  	startDTD:function(name, publicId, systemId) {
  		var impl = this.doc.implementation;
  	    if (impl && impl.createDocumentType) {
  	        var dt = impl.createDocumentType(name, publicId, systemId);
  	        this.locator && position(this.locator,dt)
  	        appendElement(this, dt);
  	    }
  	},
  	/**
  	 * @see org.xml.sax.ErrorHandler
  	 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
  	 */
  	warning:function(error) {
  		console.warn('[xmldom warning]\t'+error,_locator(this.locator));
  	},
  	error:function(error) {
  		console.error('[xmldom error]\t'+error,_locator(this.locator));
  	},
  	fatalError:function(error) {
  		console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
  	    throw error;
  	}
  }
  function _locator(l){
  	if(l){
  		return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
  	}
  }
  function _toString(chars,start,length){
  	if(typeof chars == 'string'){
  		return chars.substr(start,length)
  	}else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
  		if(chars.length >= start+length || start){
  			return new java.lang.String(chars,start,length)+'';
  		}
  		return chars;
  	}
  }
  
  /*
   * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
   * used method of org.xml.sax.ext.LexicalHandler:
   *  #comment(chars, start, length)
   *  #startCDATA()
   *  #endCDATA()
   *  #startDTD(name, publicId, systemId)
   *
   *
   * IGNORED method of org.xml.sax.ext.LexicalHandler:
   *  #endDTD()
   *  #startEntity(name)
   *  #endEntity(name)
   *
   *
   * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
   * IGNORED method of org.xml.sax.ext.DeclHandler
   * 	#attributeDecl(eName, aName, type, mode, value)
   *  #elementDecl(name, model)
   *  #externalEntityDecl(name, publicId, systemId)
   *  #internalEntityDecl(name, value)
   * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
   * IGNORED method of org.xml.sax.EntityResolver2
   *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
   *  #resolveEntity(publicId, systemId)
   *  #getExternalSubset(name, baseURI)
   * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
   * IGNORED method of org.xml.sax.DTDHandler
   *  #notationDecl(name, publicId, systemId) {};
   *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
   */
  "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
  	DOMHandler.prototype[key] = function(){return null}
  })
  
  /* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
  function appendElement (hander,node) {
      if (!hander.currentElement) {
          hander.doc.appendChild(node);
      } else {
          hander.currentElement.appendChild(node);
      }
  }//appendChild and setAttributeNS are preformance key
  
  //if(typeof require == 'function'){
  	var XMLReader = __webpack_require__(9).XMLReader;
  	var DOMImplementation = exports.DOMImplementation = __webpack_require__(1).DOMImplementation;
  	exports.XMLSerializer = __webpack_require__(1).XMLSerializer ;
  	exports.DOMParser = DOMParser;
  //}
  
  
  /***/ }),
  /* 9 */
  /***/ (function(module, exports) {
  
  //[4]   	NameStartChar	   ::=   	":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
  //[4a]   	NameChar	   ::=   	NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
  //[5]   	Name	   ::=   	NameStartChar (NameChar)*
  var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF
  var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
  var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
  //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
  //var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
  
  //S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
  //S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
  var S_TAG = 0;//tag name offerring
  var S_ATTR = 1;//attr name offerring 
  var S_ATTR_SPACE=2;//attr name end and space offer
  var S_EQ = 3;//=space?
  var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
  var S_ATTR_END = 5;//attr value end and no space(quot end)
  var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
  var S_TAG_CLOSE = 7;//closed el<el />
  
  function XMLReader(){
  	
  }
  
  XMLReader.prototype = {
  	parse:function(source,defaultNSMap,entityMap){
  		var domBuilder = this.domBuilder;
  		domBuilder.startDocument();
  		_copy(defaultNSMap ,defaultNSMap = {})
  		parse(source,defaultNSMap,entityMap,
  				domBuilder,this.errorHandler);
  		domBuilder.endDocument();
  	}
  }
  function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
  	function fixedFromCharCode(code) {
  		// String.prototype.fromCharCode does not supports
  		// > 2 bytes unicode chars directly
  		if (code > 0xffff) {
  			code -= 0x10000;
  			var surrogate1 = 0xd800 + (code >> 10)
  				, surrogate2 = 0xdc00 + (code & 0x3ff);
  
  			return String.fromCharCode(surrogate1, surrogate2);
  		} else {
  			return String.fromCharCode(code);
  		}
  	}
  	function entityReplacer(a){
  		var k = a.slice(1,-1);
  		if(k in entityMap){
  			return entityMap[k]; 
  		}else if(k.charAt(0) === '#'){
  			return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
  		}else{
  			errorHandler.error('entity not found:'+a);
  			return a;
  		}
  	}
  	function appendText(end){//has some bugs
  		if(end>start){
  			var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
  			locator&&position(start);
  			domBuilder.characters(xt,0,end-start);
  			start = end
  		}
  	}
  	function position(p,m){
  		while(p>=lineEnd && (m = linePattern.exec(source))){
  			lineStart = m.index;
  			lineEnd = lineStart + m[0].length;
  			locator.lineNumber++;
  			//console.log('line++:',locator,startPos,endPos)
  		}
  		locator.columnNumber = p-lineStart+1;
  	}
  	var lineStart = 0;
  	var lineEnd = 0;
  	var linePattern = /.*(?:\r\n?|\n)|.*$/g
  	var locator = domBuilder.locator;
  	
  	var parseStack = [{currentNSMap:defaultNSMapCopy}]
  	var closeMap = {};
  	var start = 0;
  	while(true){
  		try{
  			var tagStart = source.indexOf('<',start);
  			if(tagStart<0){
  				if(!source.substr(start).match(/^\s*$/)){
  					var doc = domBuilder.doc;
  	    			var text = doc.createTextNode(source.substr(start));
  	    			doc.appendChild(text);
  	    			domBuilder.currentElement = text;
  				}
  				return;
  			}
  			if(tagStart>start){
  				appendText(tagStart);
  			}
  			switch(source.charAt(tagStart+1)){
  			case '/':
  				var end = source.indexOf('>',tagStart+3);
  				var tagName = source.substring(tagStart+2,end);
  				var config = parseStack.pop();
  				if(end<0){
  					
  	        		tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
  	        		//console.error('#@@@@@@'+tagName)
  	        		errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
  	        		end = tagStart+1+tagName.length;
  	        	}else if(tagName.match(/\s</)){
  	        		tagName = tagName.replace(/[\s<].*/,'');
  	        		errorHandler.error("end tag name: "+tagName+' maybe not complete');
  	        		end = tagStart+1+tagName.length;
  				}
  				//console.error(parseStack.length,parseStack)
  				//console.error(config);
  				var localNSMap = config.localNSMap;
  				var endMatch = config.tagName == tagName;
  				var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase()
  		        if(endIgnoreCaseMach){
  		        	domBuilder.endElement(config.uri,config.localName,tagName);
  					if(localNSMap){
  						for(var prefix in localNSMap){
  							domBuilder.endPrefixMapping(prefix) ;
  						}
  					}
  					if(!endMatch){
  		            	errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
  					}
  		        }else{
  		        	parseStack.push(config)
  		        }
  				
  				end++;
  				break;
  				// end elment
  			case '?':// <?...?>
  				locator&&position(tagStart);
  				end = parseInstruction(source,tagStart,domBuilder);
  				break;
  			case '!':// <!doctype,<![CDATA,<!--
  				locator&&position(tagStart);
  				end = parseDCC(source,tagStart,domBuilder,errorHandler);
  				break;
  			default:
  				locator&&position(tagStart);
  				var el = new ElementAttributes();
  				var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
  				//elStartEnd
  				var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
  				var len = el.length;
  				
  				
  				if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
  					el.closed = true;
  					if(!entityMap.nbsp){
  						errorHandler.warning('unclosed xml attribute');
  					}
  				}
  				if(locator && len){
  					var locator2 = copyLocator(locator,{});
  					//try{//attribute position fixed
  					for(var i = 0;i<len;i++){
  						var a = el[i];
  						position(a.offset);
  						a.locator = copyLocator(locator,{});
  					}
  					//}catch(e){console.error('@@@@@'+e)}
  					domBuilder.locator = locator2
  					if(appendElement(el,domBuilder,currentNSMap)){
  						parseStack.push(el)
  					}
  					domBuilder.locator = locator;
  				}else{
  					if(appendElement(el,domBuilder,currentNSMap)){
  						parseStack.push(el)
  					}
  				}
  				
  				
  				
  				if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
  					end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder)
  				}else{
  					end++;
  				}
  			}
  		}catch(e){
  			errorHandler.error('element parse error: '+e)
  			//errorHandler.error('element parse error: '+e);
  			end = -1;
  			//throw e;
  		}
  		if(end>start){
  			start = end;
  		}else{
  			//TODO: 这里有可能sax回退，有位置错误风险
  			appendText(Math.max(tagStart,start)+1);
  		}
  	}
  }
  function copyLocator(f,t){
  	t.lineNumber = f.lineNumber;
  	t.columnNumber = f.columnNumber;
  	return t;
  }
  
  /**
   * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
   * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
   */
  function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
  	var attrName;
  	var value;
  	var p = ++start;
  	var s = S_TAG;//status
  	while(true){
  		var c = source.charAt(p);
  		switch(c){
  		case '=':
  			if(s === S_ATTR){//attrName
  				attrName = source.slice(start,p);
  				s = S_EQ;
  			}else if(s === S_ATTR_SPACE){
  				s = S_EQ;
  			}else{
  				//fatalError: equal must after attrName or space after attrName
  				throw new Error('attribute equal must after attrName');
  			}
  			break;
  		case '\'':
  		case '"':
  			if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
  				){//equal
  				if(s === S_ATTR){
  					errorHandler.warning('attribute value must after "="')
  					attrName = source.slice(start,p)
  				}
  				start = p+1;
  				p = source.indexOf(c,start)
  				if(p>0){
  					value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
  					el.add(attrName,value,start-1);
  					s = S_ATTR_END;
  				}else{
  					//fatalError: no end quot match
  					throw new Error('attribute value no end \''+c+'\' match');
  				}
  			}else if(s == S_ATTR_NOQUOT_VALUE){
  				value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
  				//console.log(attrName,value,start,p)
  				el.add(attrName,value,start);
  				//console.dir(el)
  				errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
  				start = p+1;
  				s = S_ATTR_END
  			}else{
  				//fatalError: no equal before
  				throw new Error('attribute value must after "="');
  			}
  			break;
  		case '/':
  			switch(s){
  			case S_TAG:
  				el.setTagName(source.slice(start,p));
  			case S_ATTR_END:
  			case S_TAG_SPACE:
  			case S_TAG_CLOSE:
  				s =S_TAG_CLOSE;
  				el.closed = true;
  			case S_ATTR_NOQUOT_VALUE:
  			case S_ATTR:
  			case S_ATTR_SPACE:
  				break;
  			//case S_EQ:
  			default:
  				throw new Error("attribute invalid close char('/')")
  			}
  			break;
  		case ''://end document
  			//throw new Error('unexpected end of input')
  			errorHandler.error('unexpected end of input');
  			if(s == S_TAG){
  				el.setTagName(source.slice(start,p));
  			}
  			return p;
  		case '>':
  			switch(s){
  			case S_TAG:
  				el.setTagName(source.slice(start,p));
  			case S_ATTR_END:
  			case S_TAG_SPACE:
  			case S_TAG_CLOSE:
  				break;//normal
  			case S_ATTR_NOQUOT_VALUE://Compatible state
  			case S_ATTR:
  				value = source.slice(start,p);
  				if(value.slice(-1) === '/'){
  					el.closed  = true;
  					value = value.slice(0,-1)
  				}
  			case S_ATTR_SPACE:
  				if(s === S_ATTR_SPACE){
  					value = attrName;
  				}
  				if(s == S_ATTR_NOQUOT_VALUE){
  					errorHandler.warning('attribute "'+value+'" missed quot(")!!');
  					el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start)
  				}else{
  					if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
  						errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!')
  					}
  					el.add(value,value,start)
  				}
  				break;
  			case S_EQ:
  				throw new Error('attribute value missed!!');
  			}
  //			console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
  			return p;
  		/*xml space '\x20' | #x9 | #xD | #xA; */
  		case '\u0080':
  			c = ' ';
  		default:
  			if(c<= ' '){//space
  				switch(s){
  				case S_TAG:
  					el.setTagName(source.slice(start,p));//tagName
  					s = S_TAG_SPACE;
  					break;
  				case S_ATTR:
  					attrName = source.slice(start,p)
  					s = S_ATTR_SPACE;
  					break;
  				case S_ATTR_NOQUOT_VALUE:
  					var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
  					errorHandler.warning('attribute "'+value+'" missed quot(")!!');
  					el.add(attrName,value,start)
  				case S_ATTR_END:
  					s = S_TAG_SPACE;
  					break;
  				//case S_TAG_SPACE:
  				//case S_EQ:
  				//case S_ATTR_SPACE:
  				//	void();break;
  				//case S_TAG_CLOSE:
  					//ignore warning
  				}
  			}else{//not space
  //S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
  //S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
  				switch(s){
  				//case S_TAG:void();break;
  				//case S_ATTR:void();break;
  				//case S_ATTR_NOQUOT_VALUE:void();break;
  				case S_ATTR_SPACE:
  					var tagName =  el.tagName;
  					if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
  						errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!')
  					}
  					el.add(attrName,attrName,start);
  					start = p;
  					s = S_ATTR;
  					break;
  				case S_ATTR_END:
  					errorHandler.warning('attribute space is required"'+attrName+'"!!')
  				case S_TAG_SPACE:
  					s = S_ATTR;
  					start = p;
  					break;
  				case S_EQ:
  					s = S_ATTR_NOQUOT_VALUE;
  					start = p;
  					break;
  				case S_TAG_CLOSE:
  					throw new Error("elements closed character '/' and '>' must be connected to");
  				}
  			}
  		}//end outer switch
  		//console.log('p++',p)
  		p++;
  	}
  }
  /**
   * @return true if has new namespace define
   */
  function appendElement(el,domBuilder,currentNSMap){
  	var tagName = el.tagName;
  	var localNSMap = null;
  	//var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
  	var i = el.length;
  	while(i--){
  		var a = el[i];
  		var qName = a.qName;
  		var value = a.value;
  		var nsp = qName.indexOf(':');
  		if(nsp>0){
  			var prefix = a.prefix = qName.slice(0,nsp);
  			var localName = qName.slice(nsp+1);
  			var nsPrefix = prefix === 'xmlns' && localName
  		}else{
  			localName = qName;
  			prefix = null
  			nsPrefix = qName === 'xmlns' && ''
  		}
  		//can not set prefix,because prefix !== ''
  		a.localName = localName ;
  		//prefix == null for no ns prefix attribute 
  		if(nsPrefix !== false){//hack!!
  			if(localNSMap == null){
  				localNSMap = {}
  				//console.log(currentNSMap,0)
  				_copy(currentNSMap,currentNSMap={})
  				//console.log(currentNSMap,1)
  			}
  			currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
  			a.uri = 'http://www.w3.org/2000/xmlns/'
  			domBuilder.startPrefixMapping(nsPrefix, value) 
  		}
  	}
  	var i = el.length;
  	while(i--){
  		a = el[i];
  		var prefix = a.prefix;
  		if(prefix){//no prefix attribute has no namespace
  			if(prefix === 'xml'){
  				a.uri = 'http://www.w3.org/XML/1998/namespace';
  			}if(prefix !== 'xmlns'){
  				a.uri = currentNSMap[prefix || '']
  				
  				//{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
  			}
  		}
  	}
  	var nsp = tagName.indexOf(':');
  	if(nsp>0){
  		prefix = el.prefix = tagName.slice(0,nsp);
  		localName = el.localName = tagName.slice(nsp+1);
  	}else{
  		prefix = null;//important!!
  		localName = el.localName = tagName;
  	}
  	//no prefix element has default namespace
  	var ns = el.uri = currentNSMap[prefix || ''];
  	domBuilder.startElement(ns,localName,tagName,el);
  	//endPrefixMapping and startPrefixMapping have not any help for dom builder
  	//localNSMap = null
  	if(el.closed){
  		domBuilder.endElement(ns,localName,tagName);
  		if(localNSMap){
  			for(prefix in localNSMap){
  				domBuilder.endPrefixMapping(prefix) 
  			}
  		}
  	}else{
  		el.currentNSMap = currentNSMap;
  		el.localNSMap = localNSMap;
  		//parseStack.push(el);
  		return true;
  	}
  }
  function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
  	if(/^(?:script|textarea)$/i.test(tagName)){
  		var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
  		var text = source.substring(elStartEnd+1,elEndStart);
  		if(/[&<]/.test(text)){
  			if(/^script$/i.test(tagName)){
  				//if(!/\]\]>/.test(text)){
  					//lexHandler.startCDATA();
  					domBuilder.characters(text,0,text.length);
  					//lexHandler.endCDATA();
  					return elEndStart;
  				//}
  			}//}else{//text area
  				text = text.replace(/&#?\w+;/g,entityReplacer);
  				domBuilder.characters(text,0,text.length);
  				return elEndStart;
  			//}
  			
  		}
  	}
  	return elStartEnd+1;
  }
  function fixSelfClosed(source,elStartEnd,tagName,closeMap){
  	//if(tagName in closeMap){
  	var pos = closeMap[tagName];
  	if(pos == null){
  		//console.log(tagName)
  		pos =  source.lastIndexOf('</'+tagName+'>')
  		if(pos<elStartEnd){//忘记闭合
  			pos = source.lastIndexOf('</'+tagName)
  		}
  		closeMap[tagName] =pos
  	}
  	return pos<elStartEnd;
  	//} 
  }
  function _copy(source,target){
  	for(var n in source){target[n] = source[n]}
  }
  function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
  	var next= source.charAt(start+2)
  	switch(next){
  	case '-':
  		if(source.charAt(start + 3) === '-'){
  			var end = source.indexOf('-->',start+4);
  			//append comment source.substring(4,end)//<!--
  			if(end>start){
  				domBuilder.comment(source,start+4,end-start-4);
  				return end+3;
  			}else{
  				errorHandler.error("Unclosed comment");
  				return -1;
  			}
  		}else{
  			//error
  			return -1;
  		}
  	default:
  		if(source.substr(start+3,6) == 'CDATA['){
  			var end = source.indexOf(']]>',start+9);
  			domBuilder.startCDATA();
  			domBuilder.characters(source,start+9,end-start-9);
  			domBuilder.endCDATA() 
  			return end+3;
  		}
  		//<!DOCTYPE
  		//startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
  		var matchs = split(source,start);
  		var len = matchs.length;
  		if(len>1 && /!doctype/i.test(matchs[0][0])){
  			var name = matchs[1][0];
  			var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0]
  			var sysid = len>4 && matchs[4][0];
  			var lastMatch = matchs[len-1]
  			domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
  					sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
  			domBuilder.endDTD();
  			
  			return lastMatch.index+lastMatch[0].length
  		}
  	}
  	return -1;
  }
  
  
  
  function parseInstruction(source,start,domBuilder){
  	var end = source.indexOf('?>',start);
  	if(end){
  		var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
  		if(match){
  			var len = match[0].length;
  			domBuilder.processingInstruction(match[1], match[2]) ;
  			return end+2;
  		}else{//error
  			return -1;
  		}
  	}
  	return -1;
  }
  
  /**
   * @param source
   */
  function ElementAttributes(source){
  	
  }
  ElementAttributes.prototype = {
  	setTagName:function(tagName){
  		if(!tagNamePattern.test(tagName)){
  			throw new Error('invalid tagName:'+tagName)
  		}
  		this.tagName = tagName
  	},
  	add:function(qName,value,offset){
  		if(!tagNamePattern.test(qName)){
  			throw new Error('invalid attribute:'+qName)
  		}
  		this[this.length++] = {qName:qName,value:value,offset:offset}
  	},
  	length:0,
  	getLocalName:function(i){return this[i].localName},
  	getLocator:function(i){return this[i].locator},
  	getQName:function(i){return this[i].qName},
  	getURI:function(i){return this[i].uri},
  	getValue:function(i){return this[i].value}
  //	,getIndex:function(uri, localName)){
  //		if(localName){
  //			
  //		}else{
  //			var qName = uri
  //		}
  //	},
  //	getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
  //	getType:function(uri,localName){}
  //	getType:function(i){},
  }
  
  
  
  
  function _set_proto_(thiz,parent){
  	thiz.__proto__ = parent;
  	return thiz;
  }
  if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
  	_set_proto_ = function(thiz,parent){
  		function p(){};
  		p.prototype = parent;
  		p = new p();
  		for(parent in thiz){
  			p[parent] = thiz[parent];
  		}
  		return p;
  	}
  }
  
  function split(source,start){
  	var match;
  	var buf = [];
  	var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
  	reg.lastIndex = start;
  	reg.exec(source);//skip <
  	while(match = reg.exec(source)){
  		buf.push(match);
  		if(match[1])return buf;
  	}
  }
  
  exports.XMLReader = XMLReader;
  
  
  
  /***/ }),
  /* 10 */
  /***/ (function(module, exports) {
  
  //copyright Ryan Day 2010 <http://ryanday.org>, Joscha Feth 2013 <http://www.feth.com> [MIT Licensed]
  
  var element_start_char = "a-zA-Z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FFF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
  var element_non_start_char = "\-.0-9\u00B7\u0300-\u036F\u203F\u2040";
  var element_replace = new RegExp("^([^" + element_start_char + "])|^((x|X)(m|M)(l|L))|([^" + element_start_char + element_non_start_char + "])", "g");
  var not_safe_in_xml = /[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm;
  
  var objKeys = function (obj) {
      var l = [];
      if (obj instanceof Object) {
          for (var k in obj) {
              if (obj.hasOwnProperty(k)) {
                  l.push(k);
              }
          }
      }
      return l;
  };
  var process_to_xml = function (node_data, options) {
  
      var makeNode = function (name, content, attributes, level, hasSubNodes) {
          var indent_value = options.indent !== undefined ? options.indent : "\t";
          var indent = options.prettyPrint ? '\n' + new Array(level).join(indent_value) : '';
          if (options.removeIllegalNameCharacters) {
              name = name.replace(element_replace, '_');
          }
  
          var node = [indent, '<', name, attributes || ''];
          if (content && content.length > 0) {
              node.push('>');
              node.push(content);
              hasSubNodes && node.push(indent);
              node.push('</');
              node.push(name);
              node.push('>');
          } else {
              node.push('/>');
          }
          return node.join('');
      };
  
      return function fn(node_data, node_descriptor, level) {
          var type = typeof node_data;
          if (Array.isArray ? Array.isArray(node_data) : node_data instanceof Array) {
              type = 'array';
          } else if (node_data instanceof Date) {
              type = 'date';
          }
  
          switch (type) {
              //if value is an array create child nodes from values
              case 'array':
                  var ret = [];
                  node_data.map(function (v) {
                      ret.push(fn(v, 1, level + 1));
                      //entries that are values of an array are the only ones that can be special node descriptors
                  });
                  options.prettyPrint && ret.push('\n');
                  return ret.join('');
                  break;
  
              case 'date':
                  // cast dates to ISO 8601 date (soap likes it)
                  return node_data.toJSON ? node_data.toJSON() : node_data + '';
                  break;
  
              case 'object':
                  var nodes = [];
                  for (var name in node_data) {
                      if (node_data.hasOwnProperty(name)) {
                          if (node_data[name] instanceof Array) {
                              for (var j = 0; j < node_data[name].length; j++) {
                                  if (node_data[name].hasOwnProperty(j)) {
                                      nodes.push(makeNode(name, fn(node_data[name][j], 0, level + 1), null, level + 1, objKeys(node_data[name][j]).length));
                                  }
                              }
                          } else {
                              nodes.push(makeNode(name, fn(node_data[name], 0, level + 1), null, level + 1));
                          }
                      }
                  }
                  options.prettyPrint && nodes.length > 0 && nodes.push('\n');
                  return nodes.join('');
                  break;
  
              case 'function':
                  return node_data();
                  break;
  
              default:
                  return options.escape ? esc(node_data) : '' + node_data;
          }
      }(node_data, 0, 0);
  };
  
  var xml_header = function (standalone) {
      var ret = ['<?xml version="1.0" encoding="UTF-8"'];
  
      if (standalone) {
          ret.push(' standalone="yes"');
      }
      ret.push('?>');
  
      return ret.join('');
  };
  
  function esc(str) {
      return ('' + str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/'/g, '&apos;').replace(/"/g, '&quot;').replace(not_safe_in_xml, '');
  }
  
  module.exports = function (obj, options) {
      if (!options) {
          options = {
              xmlHeader: {
                  standalone: true
              },
              prettyPrint: true,
              indent: "  ",
              escape: true
          };
      }
  
      if (typeof obj == 'string') {
          try {
              obj = JSON.parse(obj.toString());
          } catch (e) {
              return false;
          }
      }
  
      var xmlheader = '';
      var docType = '';
      if (options) {
          if (typeof options == 'object') {
              // our config is an object
  
              if (options.xmlHeader) {
                  // the user wants an xml header
                  xmlheader = xml_header(!!options.xmlHeader.standalone);
              }
  
              if (typeof options.docType != 'undefined') {
                  docType = '<!DOCTYPE ' + options.docType + '>';
              }
          } else {
              // our config is a boolean value, so just add xml header
              xmlheader = xml_header();
          }
      }
      options = options || {};
  
      var ret = [xmlheader, options.prettyPrint && docType ? '\n' : '', docType, process_to_xml(obj, options)];
      return ret.join('').replace(/\n{2,}/g, '\n').replace(/\s+$/g, '');
  };
  
  /***/ }),
  /* 11 */
  /***/ (function(module, exports, __webpack_require__) {
  
  var util = __webpack_require__(0);
  
  var originApiMap = {};
  var transferToTaskMethod = function (apiMap, apiName) {
      originApiMap[apiName] = apiMap[apiName];
      apiMap[apiName] = function (params, callback) {
          if (params.SkipTask) {
              originApiMap[apiName].call(this, params, callback);
          } else {
              this._addTask(apiName, params, callback);
          }
      };
  };
  
  var initTask = function (cos) {
  
      var queue = [];
      var tasks = {};
      var uploadingFileCount = 0;
      var nextUploadIndex = 0;
  
      // 接口返回简略的任务信息
      var formatTask = function (task) {
          var t = {
              id: task.id,
              Bucket: task.Bucket,
              Region: task.Region,
              Key: task.Key,
              FilePath: task.FilePath,
              state: task.state,
              loaded: task.loaded,
              size: task.size,
              speed: task.speed,
              percent: task.percent,
              hashPercent: task.hashPercent,
              error: task.error
          };
          if (task.FilePath) t.FilePath = task.FilePath;
          return t;
      };
  
      var emitListUpdate = function () {
          cos.emit('task-list-update', { list: util.map(queue, formatTask) });
          cos.emit('list-update', { list: util.map(queue, formatTask) });
      };
  
      var clearQueue = function () {
          if (queue.length <= cos.options.UploadQueueSize) return;
          for (var i = 0; i < nextUploadIndex && // 小于当前操作的 index 才清理
          i < queue.length && // 大于队列才清理
          queue.length > cos.options.UploadQueueSize // 如果还太多，才继续清理
          ;) {
              var isActive = queue[i].state === 'waiting' || queue[i].state === 'checking' || queue[i].state === 'uploading';
              if (!queue[i] || !isActive) {
                  tasks[queue[i].id] && delete tasks[queue[i].id];
                  queue.splice(i, 1);
                  nextUploadIndex--;
              } else {
                  i++;
              }
          }
          emitListUpdate();
      };
  
      var startNextTask = function () {
          if (nextUploadIndex < queue.length && uploadingFileCount < cos.options.FileParallelLimit) {
              var task = queue[nextUploadIndex];
              nextUploadIndex++;
              if (task.state === 'waiting') {
                  uploadingFileCount++;
                  task.state = 'checking';
                  task.params.onTaskStart && task.params.onTaskStart(formatTask(task));
                  !task.params.UploadData && (task.params.UploadData = {});
                  var apiParams = util.formatParams(task.api, task.params);
                  originApiMap[task.api].call(cos, apiParams, function (err, data) {
                      if (!cos._isRunningTask(task.id)) return;
                      if (task.state === 'checking' || task.state === 'uploading') {
                          task.state = err ? 'error' : 'success';
                          err && (task.error = err);
                          uploadingFileCount--;
                          emitListUpdate();
                          startNextTask(cos);
                          task.callback && task.callback(err, data);
                          if (task.state === 'success') {
                              if (task.params) {
                                  delete task.params.UploadData;
                                  delete task.params.Body;
                                  delete task.params;
                              }
                              delete task.callback;
                          }
                      }
                      clearQueue();
                  });
                  emitListUpdate();
              }
              startNextTask(cos);
          }
      };
  
      var killTask = function (id, switchToState) {
          var task = tasks[id];
          if (!task) return;
          var waiting = task && task.state === 'waiting';
          var running = task && (task.state === 'checking' || task.state === 'uploading');
          if (switchToState === 'canceled' && task.state !== 'canceled' || switchToState === 'paused' && waiting || switchToState === 'paused' && running) {
              if (switchToState === 'paused' && task.params.Body && typeof task.params.Body.pipe === 'function') {
                  console.error('stream not support pause');
                  return;
              }
              task.state = switchToState;
              cos.emit('inner-kill-task', { TaskId: id, toState: switchToState });
              emitListUpdate(true);
              if (running) {
                  uploadingFileCount--;
                  startNextTask(cos);
              }
              if (switchToState === 'canceled') {
                  if (task.params) {
                      delete task.params.UploadData;
                      delete task.params.Body;
                      delete task.params;
                  }
                  delete task.callback;
              }
          }
          clearQueue();
      };
  
      cos._addTasks = function (taskList) {
          util.each(taskList, function (task) {
              cos._addTask(task.api, task.params, task.callback, true);
          });
          emitListUpdate(true);
      };
  
      var isTaskReadyWarning = true;
      cos._addTask = function (api, params, callback, ignoreAddEvent) {
  
          // 复制参数对象
          params = util.formatParams(api, params);
  
          // 生成 id
          var id = util.uuid();
          params.TaskId = id;
          params.onTaskReady && params.onTaskReady(id);
          if (params.TaskReady) {
              params.TaskReady(id);
              isTaskReadyWarning && console.warn('warning: Param "TaskReady" has been deprecated. Please use "onTaskReady" instead.');
              isTaskReadyWarning = false;
          }
  
          var task = {
              // env
              params: params,
              callback: callback,
              api: api,
              index: queue.length,
              // task
              id: id,
              Bucket: params.Bucket,
              Region: params.Region,
              Key: params.Key,
              FilePath: params.FilePath || '',
              state: 'waiting',
              loaded: 0,
              size: 0,
              speed: 0,
              percent: 0,
              hashPercent: 0,
              error: null
          };
          var onHashProgress = params.onHashProgress;
          params.onHashProgress = function (info) {
              if (!cos._isRunningTask(task.id)) return;
              task.hashPercent = info.percent;
              onHashProgress && onHashProgress(info);
              emitListUpdate();
          };
          var onProgress = params.onProgress;
          params.onProgress = function (info) {
              if (!cos._isRunningTask(task.id)) return;
              task.state === 'checking' && (task.state = 'uploading');
              task.loaded = info.loaded;
              task.speed = info.speed;
              task.percent = info.percent;
              onProgress && onProgress(info);
              emitListUpdate();
          };
  
          // 异步获取 filesize
          util.getFileSize(api, params, function (err, size) {
              // 开始处理上传
              if (err) {
                  // 如果获取大小出错，不加入队列
                  callback(err);
                  return;
              }
              // 获取完文件大小再把任务加入队列
              tasks[id] = task;
              queue.push(task);
              task.size = size;
              !ignoreAddEvent && emitListUpdate();
              startNextTask(cos);
              clearQueue();
          });
          return id;
      };
      cos._isRunningTask = function (id) {
          var task = tasks[id];
          return !!(task && (task.state === 'checking' || task.state === 'uploading'));
      };
      cos.getTaskList = function () {
          return util.map(queue, formatTask);
      };
      cos.cancelTask = function (id) {
          killTask(id, 'canceled');
      };
      cos.pauseTask = function (id) {
          killTask(id, 'paused');
      };
      cos.restartTask = function (id) {
          var task = tasks[id];
          if (task && (task.state === 'paused' || task.state === 'error')) {
              task.state = 'waiting';
              emitListUpdate();
              nextUploadIndex = Math.min(nextUploadIndex, task.index);
              startNextTask();
          }
      };
      cos.isUploadRunning = function () {
          return uploadingFileCount || nextUploadIndex < queue.length;
      };
  };
  
  module.exports.transferToTaskMethod = transferToTaskMethod;
  module.exports.init = initTask;
  
  /***/ }),
  /* 12 */
  /***/ (function(module, exports, __webpack_require__) {
  
  var REQUEST = __webpack_require__(13);
  var util = __webpack_require__(0);
  
  // Bucket 相关
  
  /**
   * 查看是否存在该Bucket，是否有权限访问
   * @param  {Object}  params                     参数对象，必须
   *     @param  {String}  params.Bucket          Bucket名称，必须
   *     @param  {String}  params.Region          地域名称，必须
   * @param  {Function}  callback                 回调函数，必须
   * @return  {Object}  err                       请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                      返回的数据
   *     @return  {Boolean}  data.BucketExist     Bucket是否存在
   *     @return  {Boolean}  data.BucketAuth      是否有 Bucket 的访问权限
   */
  function headBucket(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:HeadBucket',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          method: 'HEAD'
      }, function (err, data) {
          callback(err, data);
      });
  }
  
  /**
   * 获取 Bucket 下的 object 列表
   * @param  {Object}  params                         参数对象，必须
   *     @param  {String}  params.Bucket              Bucket名称，必须
   *     @param  {String}  params.Region              地域名称，必须
   *     @param  {String}  params.Prefix              前缀匹配，用来规定返回的文件前缀地址，非必须
   *     @param  {String}  params.Delimiter           定界符为一个符号，如果有Prefix，则将Prefix到delimiter之间的相同路径归为一类，非必须
   *     @param  {String}  params.Marker              默认以UTF-8二进制顺序列出条目，所有列出条目从marker开始，非必须
   *     @param  {String}  params.MaxKeys             单次返回最大的条目数量，默认1000，非必须
   *     @param  {String}  params.EncodingType        规定返回值的编码方式，非必须
   * @param  {Function}  callback                     回调函数，必须
   * @return  {Object}  err                           请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                          返回的数据
   *     @return  {Object}  data.ListBucketResult     返回的 object 列表信息
   */
  function getBucket(params, callback) {
      var reqParams = {};
      reqParams['prefix'] = params['Prefix'] || '';
      reqParams['delimiter'] = params['Delimiter'];
      reqParams['marker'] = params['Marker'];
      reqParams['max-keys'] = params['MaxKeys'];
      reqParams['encoding-type'] = params['EncodingType'];
  
      submitRequest.call(this, {
          Action: 'name/cos:GetBucket',
          ResourceKey: reqParams['prefix'],
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          qs: reqParams
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var ListBucketResult = data.ListBucketResult || {};
          var Contents = ListBucketResult.Contents || [];
          var CommonPrefixes = ListBucketResult.CommonPrefixes || [];
  
          Contents = util.isArray(Contents) ? Contents : [Contents];
          CommonPrefixes = util.isArray(CommonPrefixes) ? CommonPrefixes : [CommonPrefixes];
  
          var result = util.clone(ListBucketResult);
          util.extend(result, {
              Contents: Contents,
              CommonPrefixes: CommonPrefixes,
              statusCode: data.statusCode,
              headers: data.headers
          });
  
          callback(null, result);
      });
  }
  
  /**
   * 删除 Bucket
   * @param  {Object}  params                 参数对象，必须
   *     @param  {String}  params.Bucket      Bucket名称，必须
   *     @param  {String}  params.Region      地域名称，必须
   * @param  {Function}  callback             回调函数，必须
   * @return  {Object}  err                   请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                  返回的数据
   *     @return  {String}  data.Location     操作地址
   */
  function deleteBucket(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:DeleteBucket',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          method: 'DELETE'
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 获取 Bucket 的 权限列表
   * @param  {Object}  params                         参数对象，必须
   *     @param  {String}  params.Bucket              Bucket名称，必须
   *     @param  {String}  params.Region              地域名称，必须
   * @param  {Function}  callback                     回调函数，必须
   * @return  {Object}  err                           请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                          返回的数据
   *     @return  {Object}  data.AccessControlPolicy  访问权限信息
   */
  function getBucketAcl(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketACL',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'acl'
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var AccessControlPolicy = data.AccessControlPolicy || {};
          var Owner = AccessControlPolicy.Owner || {};
          var Grant = AccessControlPolicy.AccessControlList.Grant || [];
          Grant = util.isArray(Grant) ? Grant : [Grant];
          var result = decodeAcl(AccessControlPolicy);
          if (data.headers && data.headers['x-cos-acl']) {
              result.ACL = data.headers['x-cos-acl'];
          }
          result = util.extend(result, {
              Owner: Owner,
              Grants: Grant,
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  /**
   * 设置 Bucket 的 权限列表
   * @param  {Object}  params                         参数对象，必须
   *     @param  {String}  params.Bucket              Bucket名称，必须
   *     @param  {String}  params.Region              地域名称，必须
   *     @param  {String}  params.ACL                 用户自定义文件权限，可以设置：private，public-read；默认值：private，非必须
   *     @param  {String}  params.GrantRead           赋予被授权者读的权限，格式x-cos-grant-read: uin=" ",uin=" "，非必须
   *     @param  {String}  params.GrantWrite          赋予被授权者写的权限，格式x-cos-grant-write: uin=" ",uin=" "，非必须
   *     @param  {String}  params.GrantFullControl    赋予被授权者读写权限，格式x-cos-grant-full-control: uin=" ",uin=" "，非必须
   * @param  {Function}  callback                     回调函数，必须
   * @return  {Object}  err                           请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                          返回的数据
   */
  function putBucketAcl(params, callback) {
      var headers = params.Headers;
  
      var xml = '';
      if (params['AccessControlPolicy']) {
          var AccessControlPolicy = util.clone(params['AccessControlPolicy'] || {});
          var Grants = AccessControlPolicy.Grants || AccessControlPolicy.Grant;
          Grants = util.isArray(Grants) ? Grants : [Grants];
          delete AccessControlPolicy.Grant;
          delete AccessControlPolicy.Grants;
          AccessControlPolicy.AccessControlList = { Grant: Grants };
          xml = util.json2xml({ AccessControlPolicy: AccessControlPolicy });
  
          headers['Content-Type'] = 'application/xml';
          headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
      }
  
      // Grant Header 去重
      util.each(headers, function (val, key) {
          if (key.indexOf('x-cos-grant-') === 0) {
              headers[key] = uniqGrant(headers[key]);
          }
      });
  
      submitRequest.call(this, {
          Action: 'name/cos:PutBucketACL',
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: headers,
          action: 'acl',
          body: xml
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 获取 Bucket 的 跨域设置
   * @param  {Object}  params                         参数对象，必须
   *     @param  {String}  params.Bucket              Bucket名称，必须
   *     @param  {String}  params.Region              地域名称，必须
   * @param  {Function}  callback                     回调函数，必须
   * @return  {Object}  err                           请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                          返回的数据
   *     @return  {Object}  data.CORSRules            Bucket的跨域设置
   */
  function getBucketCors(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketCORS',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'cors'
      }, function (err, data) {
          if (err) {
              if (err.statusCode === 404 && err.error && err.error.Code === 'NoSuchCORSConfiguration') {
                  var result = {
                      CORSRules: [],
                      statusCode: err.statusCode
                  };
                  err.headers && (result.headers = err.headers);
                  callback(null, result);
              } else {
                  callback(err);
              }
              return;
          }
          var CORSConfiguration = data.CORSConfiguration || {};
          var CORSRules = CORSConfiguration.CORSRules || CORSConfiguration.CORSRule || [];
          CORSRules = util.clone(util.isArray(CORSRules) ? CORSRules : [CORSRules]);
  
          util.each(CORSRules, function (rule) {
              util.each(['AllowedOrigin', 'AllowedHeader', 'AllowedMethod', 'ExposeHeader'], function (key, j) {
                  var sKey = key + 's';
                  var val = rule[sKey] || rule[key] || [];
                  delete rule[key];
                  rule[sKey] = util.isArray(val) ? val : [val];
              });
          });
  
          callback(null, {
              CORSRules: CORSRules,
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 设置 Bucket 的 跨域设置
   * @param  {Object}  params                             参数对象，必须
   *     @param  {String}  params.Bucket                  Bucket名称，必须
   *     @param  {String}  params.Region                  地域名称，必须
   *     @param  {Object}  params.CORSConfiguration       相关的跨域设置，必须
   * @param  {Array}  params.CORSConfiguration.CORSRules  对应的跨域规则
   * @param  {Function}  callback                         回调函数，必须
   * @return  {Object}  err                               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                              返回的数据
   */
  function putBucketCors(params, callback) {
  
      var CORSConfiguration = params['CORSConfiguration'] || {};
      var CORSRules = CORSConfiguration['CORSRules'] || params['CORSRules'] || [];
      CORSRules = util.clone(util.isArray(CORSRules) ? CORSRules : [CORSRules]);
      util.each(CORSRules, function (rule) {
          util.each(['AllowedOrigin', 'AllowedHeader', 'AllowedMethod', 'ExposeHeader'], function (key, k) {
              var sKey = key + 's';
              var val = rule[sKey] || rule[key] || [];
              delete rule[sKey];
              rule[key] = util.isArray(val) ? val : [val];
          });
      });
  
      var xml = util.json2xml({ CORSConfiguration: { CORSRule: CORSRules } });
  
      var headers = params.Headers;
      headers['Content-Type'] = 'application/xml';
      headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
  
      submitRequest.call(this, {
          Action: 'name/cos:PutBucketCORS',
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          body: xml,
          action: 'cors',
          headers: headers
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 删除 Bucket 的 跨域设置
   * @param  {Object}  params                 参数对象，必须
   *     @param  {String}  params.Bucket      Bucket名称，必须
   *     @param  {String}  params.Region      地域名称，必须
   * @param  {Function}  callback             回调函数，必须
   * @return  {Object}  err                   请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                  返回的数据
   */
  function deleteBucketCors(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:DeleteBucketCORS',
          method: 'DELETE',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'cors'
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode || err.statusCode,
              headers: data.headers
          });
      });
  }
  
  function putBucketPolicy(params, callback) {
      var Policy = params['Policy'];
      var PolicyStr = Policy;
      try {
          if (typeof Policy === 'string') {
              Policy = JSON.parse(PolicyStr);
          } else {
              PolicyStr = JSON.stringify(Policy);
          }
      } catch (e) {
          callback({ error: 'Policy format error' });
      }
  
      var headers = params.Headers;
      headers['Content-Type'] = 'application/json';
      headers['Content-MD5'] = util.binaryBase64(util.md5(PolicyStr));
  
      submitRequest.call(this, {
          Action: 'name/cos:PutBucketPolicy',
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          action: 'policy',
          body: util.isBrowser ? PolicyStr : Policy,
          headers: headers,
          json: true
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 删除 Bucket 的 跨域设置
   * @param  {Object}  params                 参数对象，必须
   *     @param  {String}  params.Bucket      Bucket名称，必须
   *     @param  {String}  params.Region      地域名称，必须
   * @param  {Function}  callback             回调函数，必须
   * @return  {Object}  err                   请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                  返回的数据
   */
  function deleteBucketPolicy(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:DeleteBucketPolicy',
          method: 'DELETE',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'policy'
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode || err.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 获取 Bucket 的 地域信息
   * @param  {Object}  params             参数对象，必须
   *     @param  {String}  params.Bucket  Bucket名称，必须
   *     @param  {String}  params.Region  地域名称，必须
   * @param  {Function}  callback         回调函数，必须
   * @return  {Object}  err               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data              返回数据，包含地域信息 LocationConstraint
   */
  function getBucketLocation(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketLocation',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'location'
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          callback(null, data);
      });
  }
  
  /**
   * 获取 Bucket 的读取权限策略
   * @param  {Object}  params             参数对象，必须
   *     @param  {String}  params.Bucket  Bucket名称，必须
   *     @param  {String}  params.Region  地域名称，必须
   * @param  {Function}  callback         回调函数，必须
   * @return  {Object}  err               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data              返回数据
   */
  function getBucketPolicy(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketPolicy',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'policy',
          rawBody: true
      }, function (err, data) {
          if (err) {
              if (err.statusCode && err.statusCode === 403) {
                  return callback({ ErrorStatus: 'Access Denied' });
              }
              if (err.statusCode && err.statusCode === 405) {
                  return callback({ ErrorStatus: 'Method Not Allowed' });
              }
              if (err.statusCode && err.statusCode === 404) {
                  return callback({ ErrorStatus: 'Policy Not Found' });
              }
              return callback(err);
          }
          var Policy = {};
          try {
              Policy = JSON.parse(data.body);
          } catch (e) {}
          callback(null, {
              Policy: Policy,
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 获取 Bucket 的标签设置
   * @param  {Object}  params             参数对象，必须
   *     @param  {String}  params.Bucket  Bucket名称，必须
   *     @param  {String}  params.Region  地域名称，必须
   * @param  {Function}  callback         回调函数，必须
   * @return  {Object}  err               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data              返回数据
   */
  function getBucketTagging(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketTagging',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'tagging'
      }, function (err, data) {
          if (err) {
              if (err.statusCode === 404 && err.error && (err.error === "Not Found" || err.error.Code === 'NoSuchTagSet')) {
                  var result = {
                      Tags: [],
                      statusCode: err.statusCode
                  };
                  err.headers && (result.headers = err.headers);
                  callback(null, result);
              } else {
                  callback(err);
              }
              return;
          }
          var Tags = [];
          try {
              Tags = data.Tagging.TagSet.Tag || [];
          } catch (e) {}
          Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
          callback(null, {
              Tags: Tags,
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 设置 Bucket 的标签
   * @param  {Object}  params             参数对象，必须
   *     @param  {String}  params.Bucket  Bucket名称，必须
   *     @param  {String}  params.Region  地域名称，必须
   *     @param  {Array}   params.TagSet  标签设置，必须
   * @param  {Function}  callback         回调函数，必须
   * @return  {Object}  err               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data              返回数据
   */
  function putBucketTagging(params, callback) {
  
      var Tagging = params['Tagging'] || {};
      var Tags = Tagging.TagSet || Tagging.Tags || params['Tags'] || [];
      Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
      var xml = util.json2xml({ Tagging: { TagSet: { Tag: Tags } } });
  
      var headers = params.Headers;
      headers['Content-Type'] = 'application/xml';
      headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
  
      submitRequest.call(this, {
          Action: 'name/cos:PutBucketTagging',
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          body: xml,
          action: 'tagging',
          headers: headers
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 删除 Bucket 的 标签设置
   * @param  {Object}  params             参数对象，必须
   *     @param  {String}  params.Bucket  Bucket名称，必须
   *     @param  {String}  params.Region  地域名称，必须
   * @param  {Function}  callback         回调函数，必须
   * @return  {Object}  err               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data              返回的数据
   */
  function deleteBucketTagging(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:DeleteBucketTagging',
          method: 'DELETE',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'tagging'
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  function putBucketLifecycle(params, callback) {
  
      var LifecycleConfiguration = params['LifecycleConfiguration'] || {};
      var Rules = LifecycleConfiguration.Rules || [];
      Rules = util.clone(Rules);
      var xml = util.json2xml({ LifecycleConfiguration: { Rule: Rules } });
  
      var headers = params.Headers;
      headers['Content-Type'] = 'application/xml';
      headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
  
      submitRequest.call(this, {
          Action: 'name/cos:PutBucketLifecycle',
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          body: xml,
          action: 'lifecycle',
          headers: headers
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  function getBucketLifecycle(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketLifecycle',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'lifecycle'
      }, function (err, data) {
          if (err) {
              if (err.statusCode === 404 && err.error && err.error.Code === 'NoSuchLifecycleConfiguration') {
                  var result = {
                      Rules: [],
                      statusCode: err.statusCode
                  };
                  err.headers && (result.headers = err.headers);
                  callback(null, result);
              } else {
                  callback(err);
              }
              return;
          }
          var Rules = [];
          try {
              Rules = data.LifecycleConfiguration.Rule || [];
          } catch (e) {}
          Rules = util.clone(util.isArray(Rules) ? Rules : [Rules]);
          callback(null, {
              Rules: Rules,
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  function deleteBucketLifecycle(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:DeleteBucketLifecycle',
          method: 'DELETE',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'lifecycle'
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  function putBucketVersioning(params, callback) {
  
      if (!params['VersioningConfiguration']) {
          callback({ error: 'missing param VersioningConfiguration' });
          return;
      }
      var VersioningConfiguration = params['VersioningConfiguration'] || {};
      var xml = util.json2xml({ VersioningConfiguration: VersioningConfiguration });
  
      var headers = params.Headers;
      headers['Content-Type'] = 'application/xml';
      headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
  
      submitRequest.call(this, {
          Action: 'name/cos:PutBucketVersioning',
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          body: xml,
          action: 'versioning',
          headers: headers
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  function getBucketVersioning(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketVersioning',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'versioning'
      }, function (err, data) {
          if (!err) {
              !data.VersioningConfiguration && (data.VersioningConfiguration = {});
          }
          callback(err, data);
      });
  }
  
  function putBucketReplication(params, callback) {
      var ReplicationConfiguration = util.clone(params.ReplicationConfiguration);
      var xml = util.json2xml({ ReplicationConfiguration: ReplicationConfiguration });
      xml = xml.replace(/<(\/?)Rules>/ig, '<$1Rule>');
      xml = xml.replace(/<(\/?)Tags>/ig, '<$1Tag>');
  
      var headers = params.Headers;
      headers['Content-Type'] = 'application/xml';
      headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
  
      submitRequest.call(this, {
          Action: 'name/cos:PutBucketReplication',
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          body: xml,
          action: 'replication',
          headers: headers
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  function getBucketReplication(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketReplication',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'replication'
      }, function (err, data) {
          if (err) {
              if (err.statusCode === 404 && err.error && (err.error === 'Not Found' || err.error.Code === 'ReplicationConfigurationnotFoundError')) {
                  var result = {
                      ReplicationConfiguration: { Rules: [] },
                      statusCode: err.statusCode
                  };
                  err.headers && (result.headers = err.headers);
                  callback(null, result);
              } else {
                  callback(err);
              }
              return;
          }
          if (!err) {
              !data.ReplicationConfiguration && (data.ReplicationConfiguration = {});
          }
          if (data.ReplicationConfiguration.Rule) {
              data.ReplicationConfiguration.Rules = data.ReplicationConfiguration.Rule;
              delete data.ReplicationConfiguration.Rule;
          }
          callback(err, data);
      });
  }
  
  function deleteBucketReplication(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:DeleteBucketReplication',
          method: 'DELETE',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          action: 'replication'
      }, function (err, data) {
          if (err && err.statusCode === 204) {
              return callback(null, { statusCode: err.statusCode });
          } else if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  // Object 相关
  
  /**
   * 取回对应Object的元数据，Head的权限与Get的权限一致
   * @param  {Object}  params                         参数对象，必须
   *     @param  {String}  params.Bucket              Bucket名称，必须
   *     @param  {String}  params.Region              地域名称，必须
   *     @param  {String}  params.Key                 文件名称，必须
   *     @param  {String}  params.IfModifiedSince     当Object在指定时间后被修改，则返回对应Object元信息，否则返回304，非必须
   * @param  {Function}  callback                     回调函数，必须
   * @return  {Object}  err                           请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                          为指定 object 的元数据，如果设置了 IfModifiedSince ，且文件未修改，则返回一个对象，NotModified 属性为 true
   *     @return  {Boolean}  data.NotModified         是否在 IfModifiedSince 时间点之后未修改该 object，则为 true
   */
  function headObject(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:HeadObject',
          method: 'HEAD',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          VersionId: params.VersionId,
          headers: params.Headers
      }, function (err, data) {
          if (err) {
              var statusCode = err.statusCode;
              if (params.Headers['If-Modified-Since'] && statusCode && statusCode === 304) {
                  return callback(null, {
                      NotModified: true,
                      statusCode: statusCode
                  });
              }
              return callback(err);
          }
          if (data.headers && data.headers.etag) {
              data.ETag = data.headers && data.headers.etag;
          }
          callback(null, data);
      });
  }
  
  function listObjectVersions(params, callback) {
      var reqParams = {};
      reqParams['prefix'] = params['Prefix'] || '';
      reqParams['delimiter'] = params['Delimiter'];
      reqParams['key-marker'] = params['KeyMarker'];
      reqParams['version-id-marker'] = params['VersionIdMarker'];
      reqParams['max-keys'] = params['MaxKeys'];
      reqParams['encoding-type'] = params['EncodingType'];
  
      submitRequest.call(this, {
          Action: 'name/cos:GetBucketObjectVersions',
          ResourceKey: reqParams['prefix'],
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          qs: reqParams,
          action: 'versions'
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var ListVersionsResult = data.ListVersionsResult || {};
          var DeleteMarkers = ListVersionsResult.DeleteMarker || [];
          DeleteMarkers = util.isArray(DeleteMarkers) ? DeleteMarkers : [DeleteMarkers];
          var Versions = ListVersionsResult.Version || [];
          Versions = util.isArray(Versions) ? Versions : [Versions];
  
          var result = util.clone(ListVersionsResult);
          delete result.DeleteMarker;
          delete result.Version;
          util.extend(result, {
              DeleteMarkers: DeleteMarkers,
              Versions: Versions,
              statusCode: data.statusCode,
              headers: data.headers
          });
  
          callback(null, result);
      });
  }
  
  /**
   * 下载 object
   * @param  {Object}  params                                 参数对象，必须
   *     @param  {String}  params.Bucket                      Bucket名称，必须
   *     @param  {String}  params.Region                      地域名称，必须
   *     @param  {String}  params.Key                         文件名称，必须
   *     @param  {WriteStream}  params.Output                 文件写入流，非必须
   *     @param  {String}  params.IfModifiedSince             当Object在指定时间后被修改，则返回对应Object元信息，否则返回304，非必须
   *     @param  {String}  params.IfUnmodifiedSince           如果文件修改时间早于或等于指定时间，才返回文件内容。否则返回 412 (precondition failed)，非必须
   *     @param  {String}  params.IfMatch                     当 ETag 与指定的内容一致，才返回文件。否则返回 412 (precondition failed)，非必须
   *     @param  {String}  params.IfNoneMatch                 当 ETag 与指定的内容不一致，才返回文件。否则返回304 (not modified)，非必须
   *     @param  {String}  params.ResponseContentType         设置返回头部中的 Content-Type 参数，非必须
   *     @param  {String}  params.ResponseContentLanguage     设置返回头部中的 Content-Language 参数，非必须
   *     @param  {String}  params.ResponseExpires             设置返回头部中的 Content-Expires 参数，非必须
   *     @param  {String}  params.ResponseCacheControl        设置返回头部中的 Cache-Control 参数，非必须
   *     @param  {String}  params.ResponseContentDisposition  设置返回头部中的 Content-Disposition 参数，非必须
   *     @param  {String}  params.ResponseContentEncoding     设置返回头部中的 Content-Encoding 参数，非必须
   * @param  {Function}  callback                             回调函数，必须
   * @param  {Object}  err                                    请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @param  {Object}  data                                   为对应的 object 数据，包括 body 和 headers
   */
  function getObject(params, callback) {
      var reqParams = {};
  
      reqParams['response-content-type'] = params['ResponseContentType'];
      reqParams['response-content-language'] = params['ResponseContentLanguage'];
      reqParams['response-expires'] = params['ResponseExpires'];
      reqParams['response-cache-control'] = params['ResponseCacheControl'];
      reqParams['response-content-disposition'] = params['ResponseContentDisposition'];
      reqParams['response-content-encoding'] = params['ResponseContentEncoding'];
  
      // 如果用户自己传入了 output
      submitRequest.call(this, {
          Action: 'name/cos:GetObject',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          VersionId: params.VersionId,
          headers: params.Headers,
          qs: reqParams,
          rawBody: true
      }, function (err, data) {
          if (err) {
              var statusCode = err.statusCode;
              if (params.Headers['If-Modified-Since'] && statusCode && statusCode === 304) {
                  return callback(null, {
                      NotModified: true
                  });
              }
              return callback(err);
          }
          var result = {};
          result.Body = data.body;
          if (data.headers && data.headers.etag) {
              result.ETag = data.headers && data.headers.etag;
          }
          util.extend(result, {
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  /**
   * 上传 object
   * @param  {Object} params                                          参数对象，必须
   *     @param  {String}  params.Bucket                              Bucket名称，必须
   *     @param  {String}  params.Region                              地域名称，必须
   *     @param  {String}  params.Key                                 文件名称，必须
   *     @param  {File || Blob || String}  params.Body                上传文件对象或字符串
   *     @param  {String}  params.CacheControl                        RFC 2616 中定义的缓存策略，将作为 Object 元数据保存，非必须
   *     @param  {String}  params.ContentDisposition                  RFC 2616 中定义的文件名称，将作为 Object 元数据保存，非必须
   *     @param  {String}  params.ContentEncoding                     RFC 2616 中定义的编码格式，将作为 Object 元数据保存，非必须
   *     @param  {String}  params.ContentLength                       RFC 2616 中定义的 HTTP 请求内容长度（字节），必须
   *     @param  {String}  params.ContentType                         RFC 2616 中定义的内容类型（MIME），将作为 Object 元数据保存，非必须
   *     @param  {String}  params.Expect                              当使用 Expect: 100-continue 时，在收到服务端确认后，才会发送请求内容，非必须
   *     @param  {String}  params.Expires                             RFC 2616 中定义的过期时间，将作为 Object 元数据保存，非必须
   *     @param  {String}  params.ContentSha1                         RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验，非必须
   *     @param  {String}  params.ACL                                 允许用户自定义文件权限，有效值：private | public-read，非必须
   *     @param  {String}  params.GrantRead                           赋予被授权者读的权限，格式 x-cos-grant-read: uin=" ",uin=" "，非必须
   *     @param  {String}  params.GrantWrite                          赋予被授权者写的权限，格式 x-cos-grant-write: uin=" ",uin=" "，非必须
   *     @param  {String}  params.GrantFullControl                    赋予被授权者读写权限，格式 x-cos-grant-full-control: uin=" ",uin=" "，非必须
   *     @param  {String}  params.ServerSideEncryption               支持按照指定的加密算法进行服务端数据加密，格式 x-cos-server-side-encryption: "AES256"，非必须
   *     @param  {Function}  params.onProgress                        上传进度回调函数
   * @param  {Function}  callback                                     回调函数，必须
   * @return  {Object}  err                                           请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                                          为对应的 object 数据
   *     @return  {String}  data.ETag                                 为对应上传文件的 ETag 值
   */
  function putObject(params, callback) {
      var self = this;
      var FileSize = params.ContentLength;
      var onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
  
      // 特殊处理 Cache-Control
      var headers = params.Headers;
      !headers['Cache-Control'] && (headers['Cache-Control'] = '');
  
      // 获取 File 或 Blob 的 type 属性，如果有，作为文件 Content-Type
      var ContentType = headers['Content-Type'] || params.Body && params.Body.type;
      !headers['Content-Type'] && ContentType && (headers['Content-Type'] = ContentType);
  
      util.getBodyMd5(self.options.UploadCheckContentMd5, params.Body, function (md5) {
          md5 && (params.Headers['Content-MD5'] = util.binaryBase64(md5));
          if (params.ContentLength !== undefined) {
              params.Headers['Content-Length'] = params.ContentLength;
          }
          submitRequest.call(self, {
              Action: 'name/cos:PutObject',
              TaskId: params.TaskId,
              method: 'PUT',
              Bucket: params.Bucket,
              Region: params.Region,
              Key: params.Key,
              headers: params.Headers,
              body: params.Body,
              onProgress: onProgress
          }, function (err, data) {
              if (err) {
                  onProgress(null, true);
                  return callback(err);
              }
              onProgress({ loaded: FileSize, total: FileSize }, true);
              if (data && data.headers && data.headers['etag']) {
                  var url = getUrl({
                      ForcePathStyle: self.options.ForcePathStyle,
                      protocol: self.options.Protocol,
                      domain: self.options.Domain,
                      bucket: params.Bucket,
                      region: params.Region,
                      object: params.Key
                  });
                  url = url.substr(url.indexOf('://') + 3);
                  return callback(null, {
                      Location: url,
                      ETag: data.headers['etag'],
                      statusCode: data.statusCode,
                      headers: data.headers
                  });
              }
              callback(null, data);
          });
      });
  }
  
  /**
   * 删除 object
   * @param  {Object}  params                     参数对象，必须
   *     @param  {String}  params.Bucket          Bucket名称，必须
   *     @param  {String}  params.Region          地域名称，必须
   *     @param  {String}  params.Key             object名称，必须
   * @param  {Function}  callback                 回调函数，必须
   * @param  {Object}  err                        请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @param  {Object}  data                       删除操作成功之后返回的数据
   */
  function deleteObject(params, callback) {
      submitRequest.call(this, {
          Action: 'name/cos:DeleteObject',
          method: 'DELETE',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          headers: params.Headers,
          VersionId: params.VersionId
      }, function (err, data) {
          if (err) {
              var statusCode = err.statusCode;
              if (statusCode && statusCode === 204) {
                  return callback(null, { statusCode: statusCode });
              } else if (statusCode && statusCode === 404) {
                  return callback(null, { BucketNotFound: true, statusCode: statusCode });
              } else {
                  return callback(err);
              }
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 获取 object 的 权限列表
   * @param  {Object}  params                         参数对象，必须
   *     @param  {String}  params.Bucket              Bucket名称，必须
   *     @param  {String}  params.Region              地域名称，必须
   *     @param  {String}  params.Key                 object名称，必须
   * @param  {Function}  callback                     回调函数，必须
   * @return  {Object}  err                           请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                          返回的数据
   *     @return  {Object}  data.AccessControlPolicy  权限列表
   */
  function getObjectAcl(params, callback) {
  
      submitRequest.call(this, {
          Action: 'name/cos:GetObjectACL',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          headers: params.Headers,
          action: 'acl'
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var AccessControlPolicy = data.AccessControlPolicy || {};
          var Owner = AccessControlPolicy.Owner || {};
          var Grant = AccessControlPolicy.AccessControlList && AccessControlPolicy.AccessControlList.Grant || [];
          Grant = util.isArray(Grant) ? Grant : [Grant];
          var result = decodeAcl(AccessControlPolicy);
          if (data.headers && data.headers['x-cos-acl']) {
              result.ACL = data.headers['x-cos-acl'];
          }
          result = util.extend(result, {
              Owner: Owner,
              Grants: Grant,
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  /**
   * 设置 object 的 权限列表
   * @param  {Object}  params             参数对象，必须
   *     @param  {String}  params.Bucket  Bucket名称，必须
   *     @param  {String}  params.Region  地域名称，必须
   *     @param  {String}  params.Key     object名称，必须
   * @param  {Function}  callback         回调函数，必须
   * @return  {Object}  err               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data              返回的数据
   */
  function putObjectAcl(params, callback) {
      var headers = params.Headers;
  
      var xml = '';
      if (params['AccessControlPolicy']) {
          var AccessControlPolicy = util.clone(params['AccessControlPolicy'] || {});
          var Grants = AccessControlPolicy.Grants || AccessControlPolicy.Grant;
          Grants = util.isArray(Grants) ? Grants : [Grants];
          delete AccessControlPolicy.Grant;
          delete AccessControlPolicy.Grants;
          AccessControlPolicy.AccessControlList = { Grant: Grants };
          xml = util.json2xml({ AccessControlPolicy: AccessControlPolicy });
  
          headers['Content-Type'] = 'application/xml';
          headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
      }
  
      // Grant Header 去重
      util.each(headers, function (val, key) {
          if (key.indexOf('x-cos-grant-') === 0) {
              headers[key] = uniqGrant(headers[key]);
          }
      });
  
      submitRequest.call(this, {
          Action: 'name/cos:PutObjectACL',
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          action: 'acl',
          headers: headers,
          body: xml
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * Options Object请求实现跨域访问的预请求。即发出一个 OPTIONS 请求给服务器以确认是否可以进行跨域操作。
   * @param  {Object}  params             参数对象，必须
   *     @param  {String}  params.Bucket  Bucket名称，必须
   *     @param  {String}  params.Region  地域名称，必须
   *     @param  {String}  params.Key     object名称，必须
   * @param  {Function}  callback         回调函数，必须
   * @return  {Object}  err               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data              返回的数据
   */
  function optionsObject(params, callback) {
  
      var headers = params.Headers;
      headers['Origin'] = params['Origin'];
      headers['Access-Control-Request-Method'] = params['AccessControlRequestMethod'];
      headers['Access-Control-Request-Headers'] = params['AccessControlRequestHeaders'];
  
      submitRequest.call(this, {
          Action: 'name/cos:OptionsObject',
          method: 'OPTIONS',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          headers: headers
      }, function (err, data) {
          if (err) {
              if (err.statusCode && err.statusCode === 403) {
                  return callback(null, {
                      OptionsForbidden: true,
                      statusCode: err.statusCode
                  });
              }
              return callback(err);
          }
  
          var headers = data.headers || {};
          callback(null, {
              AccessControlAllowOrigin: headers['access-control-allow-origin'],
              AccessControlAllowMethods: headers['access-control-allow-methods'],
              AccessControlAllowHeaders: headers['access-control-allow-headers'],
              AccessControlExposeHeaders: headers['access-control-expose-headers'],
              AccessControlMaxAge: headers['access-control-max-age'],
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * @param  {Object}                                     参数列表
   *     @param  {String}  Bucket                         Bucket 名称
   *     @param  {String}  Region                         地域名称
   *     @param  {String}  Key                            文件名称
   *     @param  {String}  CopySource                     源文件URL绝对路径，可以通过versionid子资源指定历史版本
   *     @param  {String}  ACL                            允许用户自定义文件权限。有效值：private，public-read默认值：private。
   *     @param  {String}  GrantRead                      赋予被授权者读的权限，格式 x-cos-grant-read: uin=" ",uin=" "，当需要给子账户授权时，uin="RootAcountID/SubAccountID"，当需要给根账户授权时，uin="RootAcountID"。
   *     @param  {String}  GrantWrite                     赋予被授权者写的权限，格式 x-cos-grant-write: uin=" ",uin=" "，当需要给子账户授权时，uin="RootAcountID/SubAccountID"，当需要给根账户授权时，uin="RootAcountID"。
   *     @param  {String}  GrantFullControl               赋予被授权者读写权限，格式 x-cos-grant-full-control: uin=" ",uin=" "，当需要给子账户授权时，uin="RootAcountID/SubAccountID"，当需要给根账户授权时，uin="RootAcountID"。
   *     @param  {String}  MetadataDirective              是否拷贝元数据，枚举值：Copy, Replaced，默认值Copy。假如标记为Copy，忽略Header中的用户元数据信息直接复制；假如标记为Replaced，按Header信息修改元数据。当目标路径和原路径一致，即用户试图修改元数据时，必须为Replaced
   *     @param  {String}  CopySourceIfModifiedSince      当Object在指定时间后被修改，则执行操作，否则返回412。可与x-cos-copy-source-If-None-Match一起使用，与其他条件联合使用返回冲突。
   *     @param  {String}  CopySourceIfUnmodifiedSince    当Object在指定时间后未被修改，则执行操作，否则返回412。可与x-cos-copy-source-If-Match一起使用，与其他条件联合使用返回冲突。
   *     @param  {String}  CopySourceIfMatch              当Object的Etag和给定一致时，则执行操作，否则返回412。可与x-cos-copy-source-If-Unmodified-Since一起使用，与其他条件联合使用返回冲突。
   *     @param  {String}  CopySourceIfNoneMatch          当Object的Etag和给定不一致时，则执行操作，否则返回412。可与x-cos-copy-source-If-Modified-Since一起使用，与其他条件联合使用返回冲突。
   *     @param  {String}  StorageClass                   存储级别，枚举值：存储级别，枚举值：Standard, Standard_IA，Archive；默认值：Standard
   *     @param  {String}  CacheControl                   指定所有缓存机制在整个请求/响应链中必须服从的指令。
   *     @param  {String}  ContentDisposition             MIME 协议的扩展，MIME 协议指示 MIME 用户代理如何显示附加的文件
   *     @param  {String}  ContentEncoding                HTTP 中用来对「采用何种编码格式传输正文」进行协定的一对头部字段
   *     @param  {String}  ContentLength                  设置响应消息的实体内容的大小，单位为字节
   *     @param  {String}  ContentType                    RFC 2616 中定义的 HTTP 请求内容类型（MIME），例如text/plain
   *     @param  {String}  Expect                         请求的特定的服务器行为
   *     @param  {String}  Expires                        响应过期的日期和时间
   *     @param  {String}  params.ServerSideEncryption   支持按照指定的加密算法进行服务端数据加密，格式 x-cos-server-side-encryption: "AES256"，非必须
   *     @param  {String}  ContentLanguage                指定内容语言
   *     @param  {String}  x-cos-meta-*                   允许用户自定义的头部信息，将作为 Object 元数据返回。大小限制2K。
   */
  function putObjectCopy(params, callback) {
  
      // 特殊处理 Cache-Control
      var headers = params.Headers;
      !headers['Cache-Control'] && (headers['Cache-Control'] = '');
  
      var CopySource = params.CopySource || '';
      var m = CopySource.match(/^([^.]+-\d+)\.cos(v6)?\.([^.]+)\.[^/]+\/(.+)$/);
      if (!m) {
          callback({ error: 'CopySource format error' });
          return;
      }
  
      var SourceBucket = m[1];
      var SourceRegion = m[3];
      var SourceKey = decodeURIComponent(m[4]);
  
      submitRequest.call(this, {
          Scope: [{
              action: 'name/cos:GetObject',
              bucket: SourceBucket,
              region: SourceRegion,
              prefix: SourceKey
          }, {
              action: 'name/cos:PutObject',
              bucket: params.Bucket,
              region: params.Region,
              prefix: params.Key
          }],
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          VersionId: params.VersionId,
          headers: params.Headers
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var result = util.clone(data.CopyObjectResult || {});
          util.extend(result, {
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  function uploadPartCopy(params, callback) {
  
      var CopySource = params.CopySource || '';
      var m = CopySource.match(/^([^.]+-\d+)\.cos(v6)?\.([^.]+)\.[^/]+\/(.+)$/);
      if (!m) {
          callback({ error: 'CopySource format error' });
          return;
      }
  
      var SourceBucket = m[1];
      var SourceRegion = m[3];
      var SourceKey = decodeURIComponent(m[4]);
  
      submitRequest.call(this, {
          Scope: [{
              action: 'name/cos:GetObject',
              bucket: SourceBucket,
              region: SourceRegion,
              prefix: SourceKey
          }, {
              action: 'name/cos:PutObject',
              bucket: params.Bucket,
              region: params.Region,
              prefix: params.Key
          }],
          method: 'PUT',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          VersionId: params.VersionId,
          qs: {
              partNumber: params['PartNumber'],
              uploadId: params['UploadId']
          },
          headers: params.Headers
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var result = util.clone(data.CopyPartResult || {});
          util.extend(result, {
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  function deleteMultipleObject(params, callback) {
      var Objects = params.Objects || [];
      var Quiet = params.Quiet;
      Objects = util.isArray(Objects) ? Objects : [Objects];
  
      var xml = util.json2xml({ Delete: { Object: Objects, Quiet: Quiet || false } });
  
      var headers = params.Headers;
      headers['Content-Type'] = 'application/xml';
      headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
  
      var Scope = util.map(Objects, function (v) {
          return {
              action: 'name/cos:DeleteObject',
              bucket: params.Bucket,
              region: params.Region,
              prefix: v.Key
          };
      });
  
      submitRequest.call(this, {
          Scope: Scope,
          method: 'POST',
          Bucket: params.Bucket,
          Region: params.Region,
          body: xml,
          action: 'delete',
          headers: headers
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var DeleteResult = data.DeleteResult || {};
          var Deleted = DeleteResult.Deleted || [];
          var Errors = DeleteResult.Error || [];
  
          Deleted = util.isArray(Deleted) ? Deleted : [Deleted];
          Errors = util.isArray(Errors) ? Errors : [Errors];
  
          var result = util.clone(DeleteResult);
          util.extend(result, {
              Error: Errors,
              Deleted: Deleted,
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  function restoreObject(params, callback) {
      var headers = params.Headers;
      if (!params['RestoreRequest']) {
          callback({ error: 'missing param RestoreRequest' });
          return;
      }
  
      var RestoreRequest = params.RestoreRequest || {};
      var xml = util.json2xml({ RestoreRequest: RestoreRequest });
  
      headers['Content-Type'] = 'application/xml';
      headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
  
      submitRequest.call(this, {
          Action: 'name/cos:RestoreObject',
          method: 'POST',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          VersionId: params.VersionId,
          body: xml,
          action: 'restore',
          headers: headers
      }, function (err, data) {
          callback(err, data);
      });
  }
  
  // 分块上传
  
  
  /**
   * 初始化分块上传
   * @param  {Object}  params                                     参数对象，必须
   *     @param  {String}  params.Bucket                          Bucket名称，必须
   *     @param  {String}  params.Region                          地域名称，必须
   *     @param  {String}  params.Key                             object名称，必须
   *     @param  {String}  params.UploadId                        object名称，必须
   *     @param  {String}  params.CacheControl                    RFC 2616 中定义的缓存策略，将作为 Object 元数据保存，非必须
   *     @param  {String}  params.ContentDisposition              RFC 2616 中定义的文件名称，将作为 Object 元数据保存    ，非必须
   *     @param  {String}  params.ContentEncoding                 RFC 2616 中定义的编码格式，将作为 Object 元数据保存，非必须
   *     @param  {String}  params.ContentType                     RFC 2616 中定义的内容类型（MIME），将作为 Object 元数据保存，非必须
   *     @param  {String}  params.Expires                         RFC 2616 中定义的过期时间，将作为 Object 元数据保存，非必须
   *     @param  {String}  params.ACL                             允许用户自定义文件权限，非必须
   *     @param  {String}  params.GrantRead                       赋予被授权者读的权限 ，非必须
   *     @param  {String}  params.GrantWrite                      赋予被授权者写的权限 ，非必须
   *     @param  {String}  params.GrantFullControl                赋予被授权者读写权限 ，非必须
   *     @param  {String}  params.StorageClass                    设置Object的存储级别，枚举值：Standard，Standard_IA，Archive，非必须
   *     @param  {String}  params.ServerSideEncryption           支持按照指定的加密算法进行服务端数据加密，格式 x-cos-server-side-encryption: "AES256"，非必须
   * @param  {Function}  callback                                 回调函数，必须
   * @return  {Object}  err                                       请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                                      返回的数据
   */
  function multipartInit(params, callback) {
  
      var xml;
      var headers = params.Headers;
      var userAgent = navigator && navigator.userAgent || '';
      var m = userAgent.match(/ TBS\/(\d{6}) /);
      if (location.protocol === 'http:' && m && m[1].length <= 6 && m[1] < '044429') {
          xml = util.json2xml({});
          headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
          // 如果没有 Content-Type 指定一个
          if (!headers['Content-Type']) {
              headers['Content-Type'] = params.Body && params.Body.type || 'application/octet-stream';
          }
      }
  
      // 特殊处理 Cache-Control
      !headers['Cache-Control'] && (headers['Cache-Control'] = '');
  
      submitRequest.call(this, {
          Action: 'name/cos:InitiateMultipartUpload',
          method: 'POST',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          action: 'uploads',
          headers: params.Headers,
          body: xml
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          data = util.clone(data || {});
          if (data && data.InitiateMultipartUploadResult) {
              return callback(null, util.extend(data.InitiateMultipartUploadResult, {
                  statusCode: data.statusCode,
                  headers: data.headers
              }));
          }
          callback(null, data);
      });
  }
  
  /**
   * 分块上传
   * @param  {Object}  params                                 参数对象，必须
   *     @param  {String}  params.Bucket                      Bucket名称，必须
   *     @param  {String}  params.Region                      地域名称，必须
   *     @param  {String}  params.Key                         object名称，必须
   *     @param  {File || Blob || String}  params.Body        上传文件对象或字符串
   *     @param  {String} params.ContentLength                RFC 2616 中定义的 HTTP 请求内容长度（字节），非必须
   *     @param  {String} params.Expect                       当使用 Expect: 100-continue 时，在收到服务端确认后，才会发送请求内容，非必须
   *     @param  {String} params.ServerSideEncryption         支持按照指定的加密算法进行服务端数据加密，格式 x-cos-server-side-encryption: "AES256"，非必须
   *     @param  {String} params.ContentSha1                  RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验值，非必须
   * @param  {Function}  callback                             回调函数，必须
   *     @return  {Object}  err                               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   *     @return  {Object}  data                              返回的数据
   *     @return  {Object}  data.ETag                         返回的文件分块 sha1 值
   */
  function multipartUpload(params, callback) {
  
      var self = this;
      util.getFileSize('multipartUpload', params, function () {
          util.getBodyMd5(self.options.UploadCheckContentMd5, params.Body, function (md5) {
              md5 && (params.Headers['Content-MD5'] = util.binaryBase64(md5));
              submitRequest.call(self, {
                  Action: 'name/cos:UploadPart',
                  TaskId: params.TaskId,
                  method: 'PUT',
                  Bucket: params.Bucket,
                  Region: params.Region,
                  Key: params.Key,
                  qs: {
                      partNumber: params['PartNumber'],
                      uploadId: params['UploadId']
                  },
                  headers: params.Headers,
                  onProgress: params.onProgress,
                  body: params.Body || null
              }, function (err, data) {
                  if (err) {
                      return callback(err);
                  }
                  data['headers'] = data['headers'] || {};
                  callback(null, {
                      ETag: data['headers']['etag'] || '',
                      statusCode: data.statusCode,
                      headers: data.headers
                  });
              });
          });
      });
  }
  
  /**
   * 完成分块上传
   * @param  {Object}  params                             参数对象，必须
   *     @param  {String}  params.Bucket                  Bucket名称，必须
   *     @param  {String}  params.Region                  地域名称，必须
   *     @param  {String}  params.Key                     object名称，必须
   *     @param  {Array}   params.Parts                   分块信息列表，必须
   *     @param  {String}  params.Parts[i].PartNumber     块编号，必须
   *     @param  {String}  params.Parts[i].ETag           分块的 sha1 校验值
   * @param  {Function}  callback                         回调函数，必须
   * @return  {Object}  err                               请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                              返回的数据
   *     @return  {Object}  data.CompleteMultipartUpload  完成分块上传后的文件信息，包括Location, Bucket, Key 和 ETag
   */
  function multipartComplete(params, callback) {
      var self = this;
  
      var UploadId = params.UploadId;
  
      var Parts = params['Parts'];
  
      for (var i = 0, len = Parts.length; i < len; i++) {
          if (Parts[i]['ETag'].indexOf('"') === 0) {
              continue;
          }
          Parts[i]['ETag'] = '"' + Parts[i]['ETag'] + '"';
      }
  
      var xml = util.json2xml({ CompleteMultipartUpload: { Part: Parts } });
  
      var headers = params.Headers;
      headers['Content-Type'] = 'application/xml';
      headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
  
      submitRequest.call(this, {
          Action: 'name/cos:CompleteMultipartUpload',
          method: 'POST',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          qs: {
              uploadId: UploadId
          },
          body: xml,
          headers: headers
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var url = getUrl({
              ForcePathStyle: self.options.ForcePathStyle,
              protocol: self.options.Protocol,
              domain: self.options.Domain,
              bucket: params.Bucket,
              region: params.Region,
              object: params.Key,
              isLocation: true
          });
          var CompleteMultipartUploadResult = data.CompleteMultipartUploadResult || {};
          var result = util.extend(CompleteMultipartUploadResult, {
              Location: url,
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  /**
   * 分块上传任务列表查询
   * @param  {Object}  params                                 参数对象，必须
   *     @param  {String}  params.Bucket                      Bucket名称，必须
   *     @param  {String}  params.Region                      地域名称，必须
   *     @param  {String}  params.Delimiter                   定界符为一个符号，如果有Prefix，则将Prefix到delimiter之间的相同路径归为一类，定义为Common Prefix，然后列出所有Common Prefix。如果没有Prefix，则从路径起点开始，非必须
   *     @param  {String}  params.EncodingType                规定返回值的编码方式，非必须
   *     @param  {String}  params.Prefix                      前缀匹配，用来规定返回的文件前缀地址，非必须
   *     @param  {String}  params.MaxUploads                  单次返回最大的条目数量，默认1000，非必须
   *     @param  {String}  params.KeyMarker                   与upload-id-marker一起使用 </Br>当upload-id-marker未被指定时，ObjectName字母顺序大于key-marker的条目将被列出 </Br>当upload-id-marker被指定时，ObjectName字母顺序大于key-marker的条目被列出，ObjectName字母顺序等于key-marker同时UploadId大于upload-id-marker的条目将被列出，非必须
   *     @param  {String}  params.UploadIdMarker              与key-marker一起使用 </Br>当key-marker未被指定时，upload-id-marker将被忽略 </Br>当key-marker被指定时，ObjectName字母顺序大于key-marker的条目被列出，ObjectName字母顺序等于key-marker同时UploadId大于upload-id-marker的条目将被列出，非必须
   * @param  {Function}  callback                             回调函数，必须
   * @return  {Object}  err                                   请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                                  返回的数据
   *     @return  {Object}  data.ListMultipartUploadsResult   分块上传任务信息
   */
  function multipartList(params, callback) {
      var reqParams = {};
  
      reqParams['delimiter'] = params['Delimiter'];
      reqParams['encoding-type'] = params['EncodingType'];
      reqParams['prefix'] = params['Prefix'] || '';
  
      reqParams['max-uploads'] = params['MaxUploads'];
  
      reqParams['key-marker'] = params['KeyMarker'];
      reqParams['upload-id-marker'] = params['UploadIdMarker'];
  
      reqParams = util.clearKey(reqParams);
  
      submitRequest.call(this, {
          Action: 'name/cos:ListMultipartUploads',
          ResourceKey: reqParams['prefix'],
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          headers: params.Headers,
          qs: reqParams,
          action: 'uploads'
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
  
          if (data && data.ListMultipartUploadsResult) {
              var Upload = data.ListMultipartUploadsResult.Upload || [];
  
              var CommonPrefixes = data.ListMultipartUploadsResult.CommonPrefixes || [];
  
              CommonPrefixes = util.isArray(CommonPrefixes) ? CommonPrefixes : [CommonPrefixes];
              Upload = util.isArray(Upload) ? Upload : [Upload];
  
              data.ListMultipartUploadsResult.Upload = Upload;
              data.ListMultipartUploadsResult.CommonPrefixes = CommonPrefixes;
          }
          var result = util.clone(data.ListMultipartUploadsResult || {});
          util.extend(result, {
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  /**
   * 上传的分块列表查询
   * @param  {Object}  params                                 参数对象，必须
   *     @param  {String}  params.Bucket                      Bucket名称，必须
   *     @param  {String}  params.Region                      地域名称，必须
   *     @param  {String}  params.Key                         object名称，必须
   *     @param  {String}  params.UploadId                    标示本次分块上传的ID，必须
   *     @param  {String}  params.EncodingType                规定返回值的编码方式，非必须
   *     @param  {String}  params.MaxParts                    单次返回最大的条目数量，默认1000，非必须
   *     @param  {String}  params.PartNumberMarker            默认以UTF-8二进制顺序列出条目，所有列出条目从marker开始，非必须
   * @param  {Function}  callback                             回调函数，必须
   * @return  {Object}  err                                   请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   * @return  {Object}  data                                  返回的数据
   *     @return  {Object}  data.ListMultipartUploadsResult   分块信息
   */
  function multipartListPart(params, callback) {
      var reqParams = {};
  
      reqParams['uploadId'] = params['UploadId'];
      reqParams['encoding-type'] = params['EncodingType'];
      reqParams['max-parts'] = params['MaxParts'];
      reqParams['part-number-marker'] = params['PartNumberMarker'];
  
      submitRequest.call(this, {
          Action: 'name/cos:ListParts',
          method: 'GET',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          headers: params.Headers,
          qs: reqParams
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          var ListPartsResult = data.ListPartsResult || {};
          var Part = ListPartsResult.Part || [];
          Part = util.isArray(Part) ? Part : [Part];
  
          ListPartsResult.Part = Part;
          var result = util.clone(ListPartsResult);
          util.extend(result, {
              statusCode: data.statusCode,
              headers: data.headers
          });
          callback(null, result);
      });
  }
  
  /**
   * 抛弃分块上传
   * @param  {Object}  params                 参数对象，必须
   *     @param  {String}  params.Bucket      Bucket名称，必须
   *     @param  {String}  params.Region      地域名称，必须
   *     @param  {String}  params.Key         object名称，必须
   *     @param  {String}  params.UploadId    标示本次分块上传的ID，必须
   * @param  {Function}  callback             回调函数，必须
   *     @return  {Object}    err             请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   *     @return  {Object}    data            返回的数据
   */
  function multipartAbort(params, callback) {
      var reqParams = {};
  
      reqParams['uploadId'] = params['UploadId'];
      submitRequest.call(this, {
          Action: 'name/cos:AbortMultipartUpload',
          method: 'DELETE',
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          headers: params.Headers,
          qs: reqParams
      }, function (err, data) {
          if (err) {
              return callback(err);
          }
          callback(null, {
              statusCode: data.statusCode,
              headers: data.headers
          });
      });
  }
  
  /**
   * 获取签名
   * @param  {Object}  params             参数对象，必须
   *     @param  {String}  params.Method  请求方法，必须
   *     @param  {String}  params.Key     object名称，必须
   *     @param  {String}  params.Expires 名超时时间，单位秒，可选
   * @return  {String}  data              返回签名字符串
   */
  function getAuth(params) {
      var self = this;
      return util.getAuth({
          SecretId: params.SecretId || this.options.SecretId || '',
          SecretKey: params.SecretKey || this.options.SecretKey || '',
          Method: params.Method,
          Key: params.Key,
          Query: params.Query,
          Headers: params.Headers,
          Expires: params.Expires,
          UseRawKey: self.options.UseRawKey,
          SystemClockOffset: self.options.SystemClockOffset
      });
  }
  
  /**
   * 获取文件下载链接
   * @param  {Object}  params                 参数对象，必须
   *     @param  {String}  params.Bucket      Bucket名称，必须
   *     @param  {String}  params.Region      地域名称，必须
   *     @param  {String}  params.Key         object名称，必须
   *     @param  {String}  params.Method      请求的方法，可选
   *     @param  {String}  params.Expires     签名超时时间，单位秒，可选
   * @param  {Function}  callback             回调函数，必须
   *     @return  {Object}    err             请求失败的错误，如果请求成功，则为空。https://cloud.tencent.com/document/product/436/7730
   *     @return  {Object}    data            返回的数据
   */
  function getObjectUrl(params, callback) {
      var self = this;
      var url = getUrl({
          ForcePathStyle: self.options.ForcePathStyle,
          protocol: params.Protocol || self.options.Protocol,
          domain: self.options.Domain,
          bucket: params.Bucket,
          region: params.Region,
          object: params.Key
      });
      if (params.Sign !== undefined && !params.Sign) {
          callback(null, { Url: url });
          return url;
      }
      var AuthData = getAuthorizationAsync.call(this, {
          Action: (params.Method || '').toUpperCase() === 'PUT' ? 'name/cos:PutObject' : 'name/cos:GetObject',
          Bucket: params.Bucket || '',
          Region: params.Region || '',
          Method: params.Method || 'get',
          Key: params.Key,
          Expires: params.Expires
      }, function (err, AuthData) {
          if (!callback) return;
          if (err) {
              callback(err);
              return;
          }
          var signUrl = url;
          signUrl += '?' + (AuthData.Authorization.indexOf('q-signature') > -1 ? AuthData.Authorization : 'sign=' + encodeURIComponent(AuthData.Authorization));
          AuthData.XCosSecurityToken && (signUrl += '&x-cos-security-token=' + AuthData.XCosSecurityToken);
          AuthData.ClientIP && (signUrl += '&clientIP=' + AuthData.ClientIP);
          AuthData.ClientUA && (signUrl += '&clientUA=' + AuthData.ClientUA);
          AuthData.Token && (signUrl += '&token=' + AuthData.Token);
          setTimeout(function () {
              callback(null, { Url: signUrl });
          });
      });
      if (AuthData) {
          return url + '?' + AuthData.Authorization + (AuthData.XCosSecurityToken ? '&x-cos-security-token=' + AuthData.XCosSecurityToken : '');
      } else {
          return url;
      }
  }
  
  /**
   * 私有方法
   */
  function decodeAcl(AccessControlPolicy) {
      var result = {
          GrantFullControl: [],
          GrantWrite: [],
          GrantRead: [],
          GrantReadAcp: [],
          GrantWriteAcp: [],
          ACL: ''
      };
      var GrantMap = {
          'FULL_CONTROL': 'GrantFullControl',
          'WRITE': 'GrantWrite',
          'READ': 'GrantRead',
          'READ_ACP': 'GrantReadAcp',
          'WRITE_ACP': 'GrantWriteAcp'
      };
      var Grant = AccessControlPolicy.AccessControlList.Grant;
      if (Grant) {
          Grant = util.isArray(Grant) ? Grant : [Grant];
      }
      var PublicAcl = { READ: 0, WRITE: 0, FULL_CONTROL: 0 };
      Grant.length && util.each(Grant, function (item) {
          if (item.Grantee.ID === 'qcs::cam::anyone:anyone' || item.Grantee.URI === 'http://cam.qcloud.com/groups/global/AllUsers') {
              PublicAcl[item.Permission] = 1;
          } else if (item.Grantee.ID !== AccessControlPolicy.Owner.ID) {
              result[GrantMap[item.Permission]].push('id="' + item.Grantee.ID + '"');
          }
      });
      if (PublicAcl.FULL_CONTROL || PublicAcl.WRITE && PublicAcl.READ) {
          result.ACL = 'public-read-write';
      } else if (PublicAcl.READ) {
          result.ACL = 'public-read';
      } else {
          result.ACL = 'private';
      }
      util.each(GrantMap, function (item) {
          result[item] = uniqGrant(result[item].join(','));
      });
      return result;
  }
  
  // Grant 去重
  function uniqGrant(str) {
      var arr = str.split(',');
      var exist = {};
      var i, item;
      for (i = 0; i < arr.length;) {
          item = arr[i].trim();
          if (exist[item]) {
              arr.splice(i, 1);
          } else {
              exist[item] = true;
              arr[i] = item;
              i++;
          }
      }
      return arr.join(',');
  }
  
  // 生成操作 url
  function getUrl(params) {
      var longBucket = params.bucket;
      var shortBucket = longBucket.substr(0, longBucket.lastIndexOf('-'));
      var appId = longBucket.substr(longBucket.lastIndexOf('-') + 1);
      var domain = params.domain;
      var region = params.region;
      var object = params.object;
      var protocol = params.protocol || (util.isBrowser && location.protocol === 'http:' ? 'http:' : 'https:');
      if (!domain) {
          if (['cn-south', 'cn-south-2', 'cn-north', 'cn-east', 'cn-southwest', 'sg'].indexOf(region) > -1) {
              domain = '{Region}.myqcloud.com';
          } else {
              domain = 'cos.{Region}.myqcloud.com';
          }
          if (!params.ForcePathStyle) {
              domain = '{Bucket}.' + domain;
          }
      }
      domain = domain.replace(/\{\{AppId\}\}/ig, appId).replace(/\{\{Bucket\}\}/ig, shortBucket).replace(/\{\{Region\}\}/ig, region).replace(/\{\{.*?\}\}/ig, '');
      domain = domain.replace(/\{AppId\}/ig, appId).replace(/\{BucketName\}/ig, shortBucket).replace(/\{Bucket\}/ig, longBucket).replace(/\{Region\}/ig, region).replace(/\{.*?\}/ig, '');
      if (!/^[a-zA-Z]+:\/\//.test(domain)) {
          domain = protocol + '//' + domain;
      }
  
      // 去掉域名最后的斜杆
      if (domain.slice(-1) === '/') {
          domain = domain.slice(0, -1);
      }
      var url = domain;
  
      if (params.ForcePathStyle) {
          url += '/' + longBucket;
      }
      url += '/';
      if (object) {
          url += util.camSafeUrlEncode(object).replace(/%2F/g, '/');
      }
  
      if (params.isLocation) {
          url = url.replace(/^https?:\/\//, '');
      }
      return url;
  }
  
  // 异步获取签名
  function getAuthorizationAsync(params, callback) {
  
      var headers = util.clone(params.Headers);
      delete headers['Content-Type'];
      delete headers['Cache-Control'];
      util.each(headers, function (v, k) {
          v === '' && delete headers[k];
      });
  
      var cb = function (AuthData) {
  
          // 检查签名格式
          var formatAllow = false;
          var auth = AuthData.Authorization;
          if (auth) {
              if (auth.indexOf(' ') > -1) {
                  formatAllow = false;
              } else if (auth.indexOf('q-sign-algorithm=') > -1 && auth.indexOf('q-ak=') > -1 && auth.indexOf('q-sign-time=') > -1 && auth.indexOf('q-key-time=') > -1 && auth.indexOf('q-url-param-list=') > -1) {
                  formatAllow = true;
              } else {
                  try {
                      auth = atob(auth);
                      if (auth.indexOf('a=') > -1 && auth.indexOf('k=') > -1 && auth.indexOf('t=') > -1 && auth.indexOf('r=') > -1 && auth.indexOf('b=') > -1) {
                          formatAllow = true;
                      }
                  } catch (e) {}
              }
          }
          if (formatAllow) {
              callback && callback(null, AuthData);
          } else {
              callback && callback('authorization error');
          }
      };
  
      var self = this;
      var Bucket = params.Bucket || '';
      var Region = params.Region || '';
  
      // PathName
      var KeyName = params.Key || '';
      if (self.options.ForcePathStyle && Bucket) {
          KeyName = Bucket + '/' + KeyName;
      }
      var Pathname = '/' + KeyName;
  
      // Action、ResourceKey
      var StsData = {};
      var Scope = params.Scope;
      if (!Scope) {
          var Action = params.Action || '';
          var ResourceKey = params.ResourceKey || params.Key || '';
          Scope = params.Scope || [{
              action: Action,
              bucket: Bucket,
              region: Region,
              prefix: ResourceKey
          }];
      }
      var ScopeKey = util.md5(JSON.stringify(Scope));
  
      // STS
      self._StsCache = self._StsCache || [];
      (function () {
          var i, AuthData;
          for (i = self._StsCache.length - 1; i >= 0; i--) {
              AuthData = self._StsCache[i];
              var compareTime = Math.round(util.getSkewTime(self.options.SystemClockOffset) / 1000) + 30;
              if (AuthData.StartTime && compareTime < AuthData.StartTime || compareTime >= AuthData.ExpiredTime) {
                  self._StsCache.splice(i, 1);
                  continue;
              }
              if (!AuthData.ScopeLimit || AuthData.ScopeLimit && AuthData.ScopeKey === ScopeKey) {
                  StsData = AuthData;
                  break;
              }
          }
      })();
  
      var calcAuthByTmpKey = function () {
          var Authorization = util.getAuth({
              SecretId: StsData.TmpSecretId,
              SecretKey: StsData.TmpSecretKey,
              Method: params.Method,
              Pathname: Pathname,
              Query: params.Query,
              Headers: headers,
              Expires: params.Expires,
              UseRawKey: self.options.UseRawKey,
              SystemClockOffset: self.options.SystemClockOffset
          });
          var AuthData = {
              Authorization: Authorization,
              XCosSecurityToken: StsData.XCosSecurityToken || '',
              Token: StsData.Token || '',
              ClientIP: StsData.ClientIP || '',
              ClientUA: StsData.ClientUA || ''
          };
          cb(AuthData);
      };
  
      // 先判断是否有临时密钥
      if (StsData.ExpiredTime && StsData.ExpiredTime - util.getSkewTime(self.options.SystemClockOffset) / 1000 > 60) {
          // 如果缓存的临时密钥有效，并还有超过60秒有效期就直接使用
          calcAuthByTmpKey();
      } else if (self.options.getAuthorization) {
          // 外部计算签名或获取临时密钥
          self.options.getAuthorization.call(self, {
              Bucket: Bucket,
              Region: Region,
              Method: params.Method,
              Key: KeyName,
              Pathname: Pathname,
              Query: params.Query,
              Headers: headers,
              Scope: Scope
          }, function (AuthData) {
              if (typeof AuthData === 'string') {
                  AuthData = { Authorization: AuthData };
              }
              if (AuthData.TmpSecretId && AuthData.TmpSecretKey && AuthData.XCosSecurityToken && AuthData.ExpiredTime) {
                  StsData = AuthData || {};
                  StsData.Scope = Scope;
                  StsData.ScopeKey = ScopeKey;
                  self._StsCache.push(StsData);
                  calcAuthByTmpKey();
              } else {
                  cb(AuthData);
              }
          });
      } else if (self.options.getSTS) {
          // 外部获取临时密钥
          self.options.getSTS.call(self, {
              Bucket: Bucket,
              Region: Region
          }, function (data) {
              StsData = data || {};
              StsData.Scope = Scope;
              StsData.ScopeKey = ScopeKey;
              StsData.TmpSecretId = StsData.SecretId;
              StsData.TmpSecretKey = StsData.SecretKey;
              self._StsCache.push(StsData);
              calcAuthByTmpKey();
          });
      } else {
          // 内部计算获取签名
          return function () {
              var Authorization = util.getAuth({
                  SecretId: params.SecretId || self.options.SecretId,
                  SecretKey: params.SecretKey || self.options.SecretKey,
                  Method: params.Method,
                  Pathname: Pathname,
                  Query: params.Query,
                  Headers: headers,
                  Expires: params.Expires,
                  UseRawKey: self.options.UseRawKey,
                  SystemClockOffset: self.options.SystemClockOffset
              });
              var AuthData = {
                  Authorization: Authorization,
                  XCosSecurityToken: self.options.XCosSecurityToken
              };
              cb(AuthData);
              return AuthData;
          }();
      }
      return '';
  }
  
  // 调整时间偏差
  function allowRetry(err) {
      var allowRetry = false;
      var isTimeError = false;
      var serverDate = err.headers && (err.headers.date || err.headers.Date) || err.error && err.error.ServerTime;
      try {
          var errorCode = err.error.Code;
          var errorMessage = err.error.Message;
          if (errorCode === 'RequestTimeTooSkewed' || errorCode === 'AccessDenied' && errorMessage === 'Request has expired') {
              isTimeError = true;
          }
      } catch (e) {}
      if (err) {
          if (isTimeError && serverDate) {
              var serverTime = Date.parse(serverDate);
              if (this.options.CorrectClockSkew && Math.abs(util.getSkewTime(this.options.SystemClockOffset) - serverTime) >= 30000) {
                  console.error('error: Local time is too skewed.');
                  this.options.SystemClockOffset = serverTime - Date.now();
                  allowRetry = true;
              }
          } else if (Math.round(err.statusCode / 100) === 5) {
              allowRetry = true;
          }
      }
      return allowRetry;
  }
  
  // 获取签名并发起请求
  function submitRequest(params, callback) {
      var self = this;
  
      // 处理 headers
      !params.headers && (params.headers = {});
  
      // 处理 query
      !params.qs && (params.qs = {});
      params.VersionId && (params.qs.versionId = params.VersionId);
      params.qs = util.clearKey(params.qs);
  
      // 清理 undefined 和 null 字段
      params.headers && (params.headers = util.clearKey(params.headers));
      params.qs && (params.qs = util.clearKey(params.qs));
  
      var Query = util.clone(params.qs);
      params.action && (Query[params.action] = '');
  
      var next = function (tryIndex) {
          var oldClockOffset = self.options.SystemClockOffset;
          getAuthorizationAsync.call(self, {
              Bucket: params.Bucket || '',
              Region: params.Region || '',
              Method: params.method,
              Key: params.Key,
              Query: Query,
              Headers: params.headers,
              Action: params.Action,
              ResourceKey: params.ResourceKey,
              Scope: params.Scope
          }, function (err, AuthData) {
              params.AuthData = AuthData;
              _submitRequest.call(self, params, function (err, data) {
                  if (err && tryIndex < 2 && (oldClockOffset !== self.options.SystemClockOffset || allowRetry.call(self, err))) {
                      if (params.headers) {
                          delete params.headers.Authorization;
                          delete params.headers['token'];
                          delete params.headers['clientIP'];
                          delete params.headers['clientUA'];
                          delete params.headers['x-cos-security-token'];
                      }
                      next(tryIndex + 1);
                  } else {
                      callback(err, data);
                  }
              });
          });
      };
      next(0);
  }
  
  // 发起请求
  function _submitRequest(params, callback) {
      var self = this;
      var TaskId = params.TaskId;
      if (TaskId && !self._isRunningTask(TaskId)) return;
  
      var bucket = params.Bucket;
      var region = params.Region;
      var object = params.Key;
      var method = params.method || 'GET';
      var url = params.url;
      var body = params.body;
      var json = params.json;
      var rawBody = params.rawBody;
  
      // url
      url = url || getUrl({
          ForcePathStyle: self.options.ForcePathStyle,
          protocol: self.options.Protocol,
          domain: self.options.Domain,
          bucket: bucket,
          region: region,
          object: object
      });
      if (params.action) {
          url = url + '?' + params.action;
      }
  
      var opt = {
          method: method,
          url: url,
          headers: params.headers,
          qs: params.qs,
          body: body,
          json: json
      };
  
      // 获取签名
      opt.headers.Authorization = params.AuthData.Authorization;
      params.AuthData.Token && (opt.headers['token'] = params.AuthData.Token);
      params.AuthData.ClientIP && (opt.headers['clientIP'] = params.AuthData.ClientIP);
      params.AuthData.ClientUA && (opt.headers['clientUA'] = params.AuthData.ClientUA);
      params.AuthData.XCosSecurityToken && (opt.headers['x-cos-security-token'] = params.AuthData.XCosSecurityToken);
  
      // 清理 undefined 和 null 字段
      opt.headers && (opt.headers = util.clearKey(opt.headers));
      opt = util.clearKey(opt);
  
      // progress
      if (params.onProgress && typeof params.onProgress === 'function') {
          var contentLength = body && (body.size || body.length) || 0;
          opt.onProgress = function (e) {
              if (TaskId && !self._isRunningTask(TaskId)) return;
              var loaded = e ? e.loaded : 0;
              params.onProgress({ loaded: loaded, total: contentLength });
          };
      }
      if (this.options.Timeout) {
          opt.timeout = this.options.Timeout;
      }
  
      self.emit('before-send', opt);
      var sender = REQUEST(opt, function (err, response, body) {
          if (err === 'abort') return;
  
          // 返回内容添加 状态码 和 headers
          var hasReturned;
          var cb = function (err, data) {
              TaskId && self.off('inner-kill-task', killTask);
              if (hasReturned) return;
              hasReturned = true;
              var attrs = {};
              response && response.statusCode && (attrs.statusCode = response.statusCode);
              response && response.headers && (attrs.headers = response.headers);
  
              if (err) {
                  err = util.extend(err || {}, attrs);
                  callback(err, null);
              } else {
                  data = util.extend(data || {}, attrs);
                  callback(null, data);
              }
              sender = null;
          };
  
          // 请求错误，发生网络错误
          if (err) {
              cb({ error: err });
              return;
          }
  
          var jsonRes;
          try {
              jsonRes = body && body.indexOf('<') > -1 && body.indexOf('>') > -1 && util.xml2json(body) || {};
          } catch (e) {
              jsonRes = body || {};
          }
  
          // 请求返回码不为 200
          var statusCode = response.statusCode;
          var statusSuccess = Math.floor(statusCode / 100) === 2; // 200 202 204 206
          if (!statusSuccess) {
              cb({ error: jsonRes.Error || jsonRes });
              return;
          }
  
          // 不对 body 进行转换，body 直接挂载返回
          if (rawBody) {
              jsonRes = {};
              jsonRes.body = body;
          }
  
          if (jsonRes.Error) {
              cb({ error: jsonRes.Error });
              return;
          }
          cb(null, jsonRes);
      });
  
      // kill task
      var killTask = function (data) {
          if (data.TaskId === TaskId) {
              sender && sender.abort && sender.abort();
              self.off('inner-kill-task', killTask);
          }
      };
      TaskId && self.on('inner-kill-task', killTask);
  }
  
  var API_MAP = {
      // Bucket 相关方法
      getBucket: getBucket,
      headBucket: headBucket,
      deleteBucket: deleteBucket,
      getBucketAcl: getBucketAcl,
      putBucketAcl: putBucketAcl,
      getBucketCors: getBucketCors,
      putBucketCors: putBucketCors,
      deleteBucketCors: deleteBucketCors,
      getBucketLocation: getBucketLocation,
      putBucketTagging: putBucketTagging,
      getBucketTagging: getBucketTagging,
      deleteBucketTagging: deleteBucketTagging,
      getBucketPolicy: getBucketPolicy,
      putBucketPolicy: putBucketPolicy,
      deleteBucketPolicy: deleteBucketPolicy,
      getBucketLifecycle: getBucketLifecycle,
      putBucketLifecycle: putBucketLifecycle,
      deleteBucketLifecycle: deleteBucketLifecycle,
      putBucketVersioning: putBucketVersioning,
      getBucketVersioning: getBucketVersioning,
      putBucketReplication: putBucketReplication,
      getBucketReplication: getBucketReplication,
      deleteBucketReplication: deleteBucketReplication,
  
      // Object 相关方法
      getObject: getObject,
      headObject: headObject,
      listObjectVersions: listObjectVersions,
      putObject: putObject,
      deleteObject: deleteObject,
      getObjectAcl: getObjectAcl,
      putObjectAcl: putObjectAcl,
      optionsObject: optionsObject,
      putObjectCopy: putObjectCopy,
      deleteMultipleObject: deleteMultipleObject,
      restoreObject: restoreObject,
  
      // 分块上传相关方法
      uploadPartCopy: uploadPartCopy,
      multipartInit: multipartInit,
      multipartUpload: multipartUpload,
      multipartComplete: multipartComplete,
      multipartList: multipartList,
      multipartListPart: multipartListPart,
      multipartAbort: multipartAbort,
  
      // 工具方法
      getObjectUrl: getObjectUrl,
      getAuth: getAuth
  };
  
  module.exports.init = function (COS, task) {
      task.transferToTaskMethod(API_MAP, 'putObject');
      util.each(API_MAP, function (fn, apiName) {
          COS.prototype[apiName] = util.apiWrapper(apiName, fn);
      });
  };
  
  /***/ }),
  /* 13 */
  /***/ (function(module, exports, __webpack_require__) {
  
  var queryString = __webpack_require__(14);
  
  var $ = function () {
      var deletedIds = [];
  
      var slice = deletedIds.slice;
  
      var concat = deletedIds.concat;
  
      var push = deletedIds.push;
  
      var indexOf = deletedIds.indexOf;
  
      var class2type = {};
  
      var toString = class2type.toString;
  
      var hasOwn = class2type.hasOwnProperty;
  
      var support = {};
  
      var version = "1.11.1 -css,-css/addGetHookIf,-css/curCSS,-css/defaultDisplay,-css/hiddenVisibleSelectors,-css/support,-css/swap,-css/var/cssExpand,-css/var/isHidden,-css/var/rmargin,-css/var/rnumnonpx,-effects,-effects/Tween,-effects/animatedSelector,-effects/support,-dimensions,-offset,-deprecated,-event-alias,-wrap",
  
  
      // Define a local copy of jQuery
      jQuery = function (selector, context) {
          // The jQuery object is actually just the init constructor 'enhanced'
          // Need init if jQuery is called (just allow error to be thrown if not included)
          return new jQuery.fn.init(selector, context);
      },
  
  
      // Support: Android<4.1, IE<9
      // Make sure we trim BOM and NBSP
      rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
  
  
      // Matches dashed string for camelizing
      rmsPrefix = /^-ms-/,
          rdashAlpha = /-([\da-z])/gi,
  
  
      // Used by jQuery.camelCase as callback to replace()
      fcamelCase = function (all, letter) {
          return letter.toUpperCase();
      };
  
      jQuery.fn = jQuery.prototype = {
          // The current version of jQuery being used
          jquery: version,
  
          constructor: jQuery,
  
          // Start with an empty selector
          selector: "",
  
          // The default length of a jQuery object is 0
          length: 0,
  
          toArray: function () {
              return slice.call(this);
          },
  
          // Get the Nth element in the matched element set OR
          // Get the whole matched element set as a clean array
          get: function (num) {
              return num != null ?
  
              // Return just the one element from the set
              num < 0 ? this[num + this.length] : this[num] :
  
              // Return all the elements in a clean array
              slice.call(this);
          },
  
          // Take an array of elements and push it onto the stack
          // (returning the new matched element set)
          pushStack: function (elems) {
  
              // Build a new jQuery matched element set
              var ret = jQuery.merge(this.constructor(), elems);
  
              // Add the old object onto the stack (as a reference)
              ret.prevObject = this;
              ret.context = this.context;
  
              // Return the newly-formed element set
              return ret;
          },
  
          // Execute a callback for every element in the matched set.
          // (You can seed the arguments with an array of args, but this is
          // only used internally.)
          each: function (callback, args) {
              return jQuery.each(this, callback, args);
          },
  
          map: function (callback) {
              return this.pushStack(jQuery.map(this, function (elem, i) {
                  return callback.call(elem, i, elem);
              }));
          },
  
          slice: function () {
              return this.pushStack(slice.apply(this, arguments));
          },
  
          first: function () {
              return this.eq(0);
          },
  
          last: function () {
              return this.eq(-1);
          },
  
          eq: function (i) {
              var len = this.length,
                  j = +i + (i < 0 ? len : 0);
              return this.pushStack(j >= 0 && j < len ? [this[j]] : []);
          },
  
          end: function () {
              return this.prevObject || this.constructor(null);
          },
  
          // For internal use only.
          // Behaves like an Array's method, not like a jQuery method.
          push: push,
          sort: deletedIds.sort,
          splice: deletedIds.splice
      };
  
      jQuery.extend = jQuery.fn.extend = function () {
          var src,
              copyIsArray,
              copy,
              name,
              options,
              clone,
              target = arguments[0] || {},
              i = 1,
              length = arguments.length,
              deep = false;
  
          // Handle a deep copy situation
          if (typeof target === "boolean") {
              deep = target;
  
              // skip the boolean and the target
              target = arguments[i] || {};
              i++;
          }
  
          // Handle case when target is a string or something (possible in deep copy)
          if (typeof target !== "object" && !jQuery.isFunction(target)) {
              target = {};
          }
  
          // extend jQuery itself if only one argument is passed
          if (i === length) {
              target = this;
              i--;
          }
  
          for (; i < length; i++) {
              // Only deal with non-null/undefined values
              if ((options = arguments[i]) != null) {
                  // Extend the base object
                  for (name in options) {
                      src = target[name];
                      copy = options[name];
  
                      // Prevent never-ending loop
                      if (target === copy) {
                          continue;
                      }
  
                      // Recurse if we're merging plain objects or arrays
                      if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
                          if (copyIsArray) {
                              copyIsArray = false;
                              clone = src && jQuery.isArray(src) ? src : [];
                          } else {
                              clone = src && jQuery.isPlainObject(src) ? src : {};
                          }
  
                          // Never move original objects, clone them
                          target[name] = jQuery.extend(deep, clone, copy);
  
                          // Don't bring in undefined values
                      } else if (copy !== undefined) {
                          target[name] = copy;
                      }
                  }
              }
          }
  
          // Return the modified object
          return target;
      };
  
      jQuery.extend({
          // Unique for each copy of jQuery on the page
          expando: "jQuery" + (version + Math.random()).replace(/\D/g, ""),
  
          // Assume jQuery is ready without the ready module
          isReady: true,
  
          error: function (msg) {
              throw new Error(msg);
          },
  
          noop: function () {},
  
          // See test/unit/core.js for details concerning isFunction.
          // Since version 1.3, DOM methods and functions like alert
          // aren't supported. They return false on IE (#2968).
          isFunction: function (obj) {
              return jQuery.type(obj) === "function";
          },
  
          isArray: Array.isArray || function (obj) {
              return jQuery.type(obj) === "array";
          },
  
          isWindow: function (obj) {
              /* jshint eqeqeq: false */
              return obj != null && obj == obj.window;
          },
  
          isNumeric: function (obj) {
              // parseFloat NaNs numeric-cast false positives (null|true|false|"")
              // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
              // subtraction forces infinities to NaN
              return !jQuery.isArray(obj) && obj - parseFloat(obj) >= 0;
          },
  
          isEmptyObject: function (obj) {
              var name;
              for (name in obj) {
                  return false;
              }
              return true;
          },
  
          isPlainObject: function (obj) {
              var key;
  
              // Must be an Object.
              // Because of IE, we also have to check the presence of the constructor property.
              // Make sure that DOM nodes and window objects don't pass through, as well
              if (!obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow(obj)) {
                  return false;
              }
  
              try {
                  // Not own constructor property must be Object
                  if (obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
                      return false;
                  }
              } catch (e) {
                  // IE8,9 Will throw exceptions on certain host objects #9897
                  return false;
              }
  
              // Support: IE<9
              // Handle iteration over inherited properties before own properties.
              if (support.ownLast) {
                  for (key in obj) {
                      return hasOwn.call(obj, key);
                  }
              }
  
              // Own properties are enumerated firstly, so to speed up,
              // if last one is own, then all properties are own.
              for (key in obj) {}
  
              return key === undefined || hasOwn.call(obj, key);
          },
  
          type: function (obj) {
              if (obj == null) {
                  return obj + "";
              }
              return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" : typeof obj;
          },
  
          // Evaluates a script in a global context
          // Workarounds based on findings by Jim Driscoll
          // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
          globalEval: function (data) {
              if (data && jQuery.trim(data)) {
                  // We use execScript on Internet Explorer
                  // We use an anonymous function so that context is window
                  // rather than jQuery in Firefox
                  (window.execScript || function (data) {
                      window["eval"].call(window, data);
                  })(data);
              }
          },
  
          // Convert dashed to camelCase; used by the css and data modules
          // Microsoft forgot to hump their vendor prefix (#9572)
          camelCase: function (string) {
              return string.replace(rmsPrefix, "ms-").replace(rdashAlpha, fcamelCase);
          },
  
          nodeName: function (elem, name) {
              return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
          },
  
          // args is for internal usage only
          each: function (obj, callback, args) {
              var value,
                  i = 0,
                  length = obj.length,
                  isArray = isArraylike(obj);
  
              if (args) {
                  if (isArray) {
                      for (; i < length; i++) {
                          value = callback.apply(obj[i], args);
  
                          if (value === false) {
                              break;
                          }
                      }
                  } else {
                      for (i in obj) {
                          value = callback.apply(obj[i], args);
  
                          if (value === false) {
                              break;
                          }
                      }
                  }
  
                  // A special, fast, case for the most common use of each
              } else {
                  if (isArray) {
                      for (; i < length; i++) {
                          value = callback.call(obj[i], i, obj[i]);
  
                          if (value === false) {
                              break;
                          }
                      }
                  } else {
                      for (i in obj) {
                          value = callback.call(obj[i], i, obj[i]);
  
                          if (value === false) {
                              break;
                          }
                      }
                  }
              }
  
              return obj;
          },
  
          // Support: Android<4.1, IE<9
          trim: function (text) {
              return text == null ? "" : (text + "").replace(rtrim, "");
          },
  
          // results is for internal usage only
          makeArray: function (arr, results) {
              var ret = results || [];
  
              if (arr != null) {
                  if (isArraylike(Object(arr))) {
                      jQuery.merge(ret, typeof arr === "string" ? [arr] : arr);
                  } else {
                      push.call(ret, arr);
                  }
              }
  
              return ret;
          },
  
          inArray: function (elem, arr, i) {
              var len;
  
              if (arr) {
                  if (indexOf) {
                      return indexOf.call(arr, elem, i);
                  }
  
                  len = arr.length;
                  i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
  
                  for (; i < len; i++) {
                      // Skip accessing in sparse arrays
                      if (i in arr && arr[i] === elem) {
                          return i;
                      }
                  }
              }
  
              return -1;
          },
  
          merge: function (first, second) {
              var len = +second.length,
                  j = 0,
                  i = first.length;
  
              while (j < len) {
                  first[i++] = second[j++];
              }
  
              // Support: IE<9
              // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
              if (len !== len) {
                  while (second[j] !== undefined) {
                      first[i++] = second[j++];
                  }
              }
  
              first.length = i;
  
              return first;
          },
  
          grep: function (elems, callback, invert) {
              var callbackInverse,
                  matches = [],
                  i = 0,
                  length = elems.length,
                  callbackExpect = !invert;
  
              // Go through the array, only saving the items
              // that pass the validator function
              for (; i < length; i++) {
                  callbackInverse = !callback(elems[i], i);
                  if (callbackInverse !== callbackExpect) {
                      matches.push(elems[i]);
                  }
              }
  
              return matches;
          },
  
          // arg is for internal usage only
          map: function (elems, callback, arg) {
              var value,
                  i = 0,
                  length = elems.length,
                  isArray = isArraylike(elems),
                  ret = [];
  
              // Go through the array, translating each of the items to their new values
              if (isArray) {
                  for (; i < length; i++) {
                      value = callback(elems[i], i, arg);
  
                      if (value != null) {
                          ret.push(value);
                      }
                  }
  
                  // Go through every key on the object,
              } else {
                  for (i in elems) {
                      value = callback(elems[i], i, arg);
  
                      if (value != null) {
                          ret.push(value);
                      }
                  }
              }
  
              // Flatten any nested arrays
              return concat.apply([], ret);
          },
  
          // A global GUID counter for objects
          guid: 1,
  
          // Bind a function to a context, optionally partially applying any
          // arguments.
          proxy: function (fn, context) {
              var args, proxy, tmp;
  
              if (typeof context === "string") {
                  tmp = fn[context];
                  context = fn;
                  fn = tmp;
              }
  
              // Quick check to determine if target is callable, in the spec
              // this throws a TypeError, but we will just return undefined.
              if (!jQuery.isFunction(fn)) {
                  return undefined;
              }
  
              // Simulated bind
              args = slice.call(arguments, 2);
              proxy = function () {
                  return fn.apply(context || this, args.concat(slice.call(arguments)));
              };
  
              // Set the guid of unique handler to the same of original handler, so it can be removed
              proxy.guid = fn.guid = fn.guid || jQuery.guid++;
  
              return proxy;
          },
  
          now: function () {
              return +new Date();
          },
  
          // jQuery.support is not used in Core but other projects attach their
          // properties to it so it needs to exist.
          support: support
      });
  
      // Populate the class2type map
      jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function (i, name) {
          class2type["[object " + name + "]"] = name.toLowerCase();
      });
  
      function isArraylike(obj) {
          var length = obj.length,
              type = jQuery.type(obj);
  
          if (type === "function" || jQuery.isWindow(obj)) {
              return false;
          }
  
          if (obj.nodeType === 1 && length) {
              return true;
          }
  
          return type === "array" || length === 0 || typeof length === "number" && length > 0 && length - 1 in obj;
      }
  
      // Initialize a jQuery object
  
  
      // A central reference to the root jQuery(document)
      var rootjQuery,
  
  
      // Use the correct document accordingly with window argument (sandbox)
      document = window.document,
  
  
      // A simple way to check for HTML strings
      // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
      // Strict HTML recognition (#11290: must start with <)
      rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
          init = jQuery.fn.init = function (selector, context) {
          var match, elem;
  
          // HANDLE: $(""), $(null), $(undefined), $(false)
          if (!selector) {
              return this;
          }
  
          // Handle HTML strings
          if (typeof selector === "string") {
              if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
                  // Assume that strings that start and end with <> are HTML and skip the regex check
                  match = [null, selector, null];
              } else {
                  match = rquickExpr.exec(selector);
              }
  
              // Match html or make sure no context is specified for #id
              if (match && (match[1] || !context)) {
  
                  // HANDLE: $(html) -> $(array)
                  if (match[1]) {
                      context = context instanceof jQuery ? context[0] : context;
  
                      // scripts is true for back-compat
                      // Intentionally let the error be thrown if parseHTML is not present
                      jQuery.merge(this, jQuery.parseHTML(match[1], context && context.nodeType ? context.ownerDocument || context : document, true));
  
                      // HANDLE: $(html, props)
                      if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) {
                          for (match in context) {
                              // Properties of context are called as methods if possible
                              if (jQuery.isFunction(this[match])) {
                                  this[match](context[match]);
  
                                  // ...and otherwise set as attributes
                              } else {
                                  this.attr(match, context[match]);
                              }
                          }
                      }
  
                      return this;
  
                      // HANDLE: $(#id)
                  } else {
                      elem = document.getElementById(match[2]);
  
                      // Check parentNode to catch when Blackberry 4.6 returns
                      // nodes that are no longer in the document #6963
                      if (elem && elem.parentNode) {
                          // Handle the case where IE and Opera return items
                          // by name instead of ID
                          if (elem.id !== match[2]) {
                              return rootjQuery.find(selector);
                          }
  
                          // Otherwise, we inject the element directly into the jQuery object
                          this.length = 1;
                          this[0] = elem;
                      }
  
                      this.context = document;
                      this.selector = selector;
                      return this;
                  }
  
                  // HANDLE: $(expr, $(...))
              } else if (!context || context.jquery) {
                  return (context || rootjQuery).find(selector);
  
                  // HANDLE: $(expr, context)
                  // (which is just equivalent to: $(context).find(expr)
              } else {
                  return this.constructor(context).find(selector);
              }
  
              // HANDLE: $(DOMElement)
          } else if (selector.nodeType) {
              this.context = this[0] = selector;
              this.length = 1;
              return this;
  
              // HANDLE: $(function)
              // Shortcut for document ready
          } else if (jQuery.isFunction(selector)) {
              return typeof rootjQuery.ready !== "undefined" ? rootjQuery.ready(selector) :
              // Execute immediately if ready is not present
              selector(jQuery);
          }
  
          if (selector.selector !== undefined) {
              this.selector = selector.selector;
              this.context = selector.context;
          }
  
          return jQuery.makeArray(selector, this);
      };
  
      // Give the init function the jQuery prototype for later instantiation
      init.prototype = jQuery.fn;
  
      // Initialize central reference
      rootjQuery = jQuery(document);
  
      var rnotwhite = /\S+/g;
  
      // String to Object options format cache
      var optionsCache = {};
  
      // Convert String-formatted options into Object-formatted ones and store in cache
      function createOptions(options) {
          var object = optionsCache[options] = {};
          jQuery.each(options.match(rnotwhite) || [], function (_, flag) {
              object[flag] = true;
          });
          return object;
      }
  
      /*
       * Create a callback list using the following parameters:
       *
       *	options: an optional list of space-separated options that will change how
       *			the callback list behaves or a more traditional option object
       *
       * By default a callback list will act like an event callback list and can be
       * "fired" multiple times.
       *
       * Possible options:
       *
       *	once:			will ensure the callback list can only be fired once (like a Deferred)
       *
       *	memory:			will keep track of previous values and will call any callback added
       *					after the list has been fired right away with the latest "memorized"
       *					values (like a Deferred)
       *
       *	unique:			will ensure a callback can only be added once (no duplicate in the list)
       *
       *	stopOnFalse:	interrupt callings when a callback returns false
       *
       */
      jQuery.Callbacks = function (options) {
  
          // Convert options from String-formatted to Object-formatted if needed
          // (we check in cache first)
          options = typeof options === "string" ? optionsCache[options] || createOptions(options) : jQuery.extend({}, options);
  
          var // Flag to know if list is currently firing
          firing,
  
          // Last fire value (for non-forgettable lists)
          memory,
  
          // Flag to know if list was already fired
          fired,
  
          // End of the loop when firing
          firingLength,
  
          // Index of currently firing callback (modified by remove if needed)
          firingIndex,
  
          // First callback to fire (used internally by add and fireWith)
          firingStart,
  
          // Actual callback list
          list = [],
  
          // Stack of fire calls for repeatable lists
          stack = !options.once && [],
  
          // Fire callbacks
          fire = function (data) {
              memory = options.memory && data;
              fired = true;
              firingIndex = firingStart || 0;
              firingStart = 0;
              firingLength = list.length;
              firing = true;
              for (; list && firingIndex < firingLength; firingIndex++) {
                  if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) {
                      memory = false; // To prevent further calls using add
                      break;
                  }
              }
              firing = false;
              if (list) {
                  if (stack) {
                      if (stack.length) {
                          fire(stack.shift());
                      }
                  } else if (memory) {
                      list = [];
                  } else {
                      self.disable();
                  }
              }
          },
  
          // Actual Callbacks object
          self = {
              // Add a callback or a collection of callbacks to the list
              add: function () {
                  if (list) {
                      // First, we save the current length
                      var start = list.length;
                      (function add(args) {
                          jQuery.each(args, function (_, arg) {
                              var type = jQuery.type(arg);
                              if (type === "function") {
                                  if (!options.unique || !self.has(arg)) {
                                      list.push(arg);
                                  }
                              } else if (arg && arg.length && type !== "string") {
                                  // Inspect recursively
                                  add(arg);
                              }
                          });
                      })(arguments);
                      // Do we need to add the callbacks to the
                      // current firing batch?
                      if (firing) {
                          firingLength = list.length;
                          // With memory, if we're not firing then
                          // we should call right away
                      } else if (memory) {
                          firingStart = start;
                          fire(memory);
                      }
                  }
                  return this;
              },
              // Remove a callback from the list
              remove: function () {
                  if (list) {
                      jQuery.each(arguments, function (_, arg) {
                          var index;
                          while ((index = jQuery.inArray(arg, list, index)) > -1) {
                              list.splice(index, 1);
                              // Handle firing indexes
                              if (firing) {
                                  if (index <= firingLength) {
                                      firingLength--;
                                  }
                                  if (index <= firingIndex) {
                                      firingIndex--;
                                  }
                              }
                          }
                      });
                  }
                  return this;
              },
              // Check if a given callback is in the list.
              // If no argument is given, return whether or not list has callbacks attached.
              has: function (fn) {
                  return fn ? jQuery.inArray(fn, list) > -1 : !!(list && list.length);
              },
              // Remove all callbacks from the list
              empty: function () {
                  list = [];
                  firingLength = 0;
                  return this;
              },
              // Have the list do nothing anymore
              disable: function () {
                  list = stack = memory = undefined;
                  return this;
              },
              // Is it disabled?
              disabled: function () {
                  return !list;
              },
              // Lock the list in its current state
              lock: function () {
                  stack = undefined;
                  if (!memory) {
                      self.disable();
                  }
                  return this;
              },
              // Is it locked?
              locked: function () {
                  return !stack;
              },
              // Call all callbacks with the given context and arguments
              fireWith: function (context, args) {
                  if (list && (!fired || stack)) {
                      args = args || [];
                      args = [context, args.slice ? args.slice() : args];
                      if (firing) {
                          stack.push(args);
                      } else {
                          fire(args);
                      }
                  }
                  return this;
              },
              // Call all the callbacks with the given arguments
              fire: function () {
                  self.fireWith(this, arguments);
                  return this;
              },
              // To know if the callbacks have already been called at least once
              fired: function () {
                  return !!fired;
              }
          };
  
          return self;
      };
  
      jQuery.extend({
  
          Deferred: function (func) {
              var tuples = [
              // action, add listener, listener list, final state
              ["resolve", "done", jQuery.Callbacks("once memory"), "resolved"], ["reject", "fail", jQuery.Callbacks("once memory"), "rejected"], ["notify", "progress", jQuery.Callbacks("memory")]],
                  state = "pending",
                  promise = {
                  state: function () {
                      return state;
                  },
                  always: function () {
                      deferred.done(arguments).fail(arguments);
                      return this;
                  },
                  then: function () /* fnDone, fnFail, fnProgress */{
                      var fns = arguments;
                      return jQuery.Deferred(function (newDefer) {
                          jQuery.each(tuples, function (i, tuple) {
                              var fn = jQuery.isFunction(fns[i]) && fns[i];
                              // deferred[ done | fail | progress ] for forwarding actions to newDefer
                              deferred[tuple[1]](function () {
                                  var returned = fn && fn.apply(this, arguments);
                                  if (returned && jQuery.isFunction(returned.promise)) {
                                      returned.promise().done(newDefer.resolve).fail(newDefer.reject).progress(newDefer.notify);
                                  } else {
                                      newDefer[tuple[0] + "With"](this === promise ? newDefer.promise() : this, fn ? [returned] : arguments);
                                  }
                              });
                          });
                          fns = null;
                      }).promise();
                  },
                  // Get a promise for this deferred
                  // If obj is provided, the promise aspect is added to the object
                  promise: function (obj) {
                      return obj != null ? jQuery.extend(obj, promise) : promise;
                  }
              },
                  deferred = {};
  
              // Keep pipe for back-compat
              promise.pipe = promise.then;
  
              // Add list-specific methods
              jQuery.each(tuples, function (i, tuple) {
                  var list = tuple[2],
                      stateString = tuple[3];
  
                  // promise[ done | fail | progress ] = list.add
                  promise[tuple[1]] = list.add;
  
                  // Handle state
                  if (stateString) {
                      list.add(function () {
                          // state = [ resolved | rejected ]
                          state = stateString;
  
                          // [ reject_list | resolve_list ].disable; progress_list.lock
                      }, tuples[i ^ 1][2].disable, tuples[2][2].lock);
                  }
  
                  // deferred[ resolve | reject | notify ]
                  deferred[tuple[0]] = function () {
                      deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments);
                      return this;
                  };
                  deferred[tuple[0] + "With"] = list.fireWith;
              });
  
              // Make the deferred a promise
              promise.promise(deferred);
  
              // Call given func if any
              if (func) {
                  func.call(deferred, deferred);
              }
  
              // All done!
              return deferred;
          },
  
          // Deferred helper
          when: function (subordinate /* , ..., subordinateN */) {
              var i = 0,
                  resolveValues = slice.call(arguments),
                  length = resolveValues.length,
  
  
              // the count of uncompleted subordinates
              remaining = length !== 1 || subordinate && jQuery.isFunction(subordinate.promise) ? length : 0,
  
  
              // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
              deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
  
  
              // Update function for both resolve and progress values
              updateFunc = function (i, contexts, values) {
                  return function (value) {
                      contexts[i] = this;
                      values[i] = arguments.length > 1 ? slice.call(arguments) : value;
                      if (values === progressValues) {
                          deferred.notifyWith(contexts, values);
                      } else if (! --remaining) {
                          deferred.resolveWith(contexts, values);
                      }
                  };
              },
                  progressValues,
                  progressContexts,
                  resolveContexts;
  
              // add listeners to Deferred subordinates; treat others as resolved
              if (length > 1) {
                  progressValues = new Array(length);
                  progressContexts = new Array(length);
                  resolveContexts = new Array(length);
                  for (; i < length; i++) {
                      if (resolveValues[i] && jQuery.isFunction(resolveValues[i].promise)) {
                          resolveValues[i].promise().done(updateFunc(i, resolveContexts, resolveValues)).fail(deferred.reject).progress(updateFunc(i, progressContexts, progressValues));
                      } else {
                          --remaining;
                      }
                  }
              }
  
              // if we're not waiting on anything, resolve the master
              if (!remaining) {
                  deferred.resolveWith(resolveContexts, resolveValues);
              }
  
              return deferred.promise();
          }
      });
  
      // The deferred used on DOM ready
      var readyList;
  
      jQuery.fn.ready = function (fn) {
          // Add the callback
          jQuery.ready.promise().done(fn);
  
          return this;
      };
  
      jQuery.extend({
          // Is the DOM ready to be used? Set to true once it occurs.
          isReady: false,
  
          // A counter to track how many items to wait for before
          // the ready event fires. See #6781
          readyWait: 1,
  
          // Hold (or release) the ready event
          holdReady: function (hold) {
              if (hold) {
                  jQuery.readyWait++;
              } else {
                  jQuery.ready(true);
              }
          },
  
          // Handle when the DOM is ready
          ready: function (wait) {
  
              // Abort if there are pending holds or we're already ready
              if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
                  return;
              }
  
              // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
              if (!document.body) {
                  return setTimeout(jQuery.ready);
              }
  
              // Remember that the DOM is ready
              jQuery.isReady = true;
  
              // If a normal DOM Ready event fired, decrement, and wait if need be
              if (wait !== true && --jQuery.readyWait > 0) {
                  return;
              }
  
              // If there are functions bound, to execute
              readyList.resolveWith(document, [jQuery]);
  
              // Trigger any bound ready events
              if (jQuery.fn.triggerHandler) {
                  jQuery(document).triggerHandler("ready");
                  jQuery(document).off("ready");
              }
          }
      });
  
      /**
       * Clean-up method for dom ready events
       */
      function detach() {
          if (document.addEventListener) {
              document.removeEventListener("DOMContentLoaded", completed, false);
              window.removeEventListener("load", completed, false);
          } else {
              document.detachEvent("onreadystatechange", completed);
              window.detachEvent("onload", completed);
          }
      }
  
      /**
       * The ready event handler and self cleanup method
       */
      function completed() {
          // readyState === "complete" is good enough for us to call the dom ready in oldIE
          if (document.addEventListener || event.type === "load" || document.readyState === "complete") {
              detach();
              jQuery.ready();
          }
      }
  
      jQuery.ready.promise = function (obj) {
          if (!readyList) {
  
              readyList = jQuery.Deferred();
  
              // Catch cases where $(document).ready() is called after the browser event has already occurred.
              // we once tried to use readyState "interactive" here, but it caused issues like the one
              // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
              if (document.readyState === "complete") {
                  // Handle it asynchronously to allow scripts the opportunity to delay ready
                  setTimeout(jQuery.ready);
  
                  // Standards-based browsers support DOMContentLoaded
              } else if (document.addEventListener) {
                  // Use the handy event callback
                  document.addEventListener("DOMContentLoaded", completed, false);
  
                  // A fallback to window.onload, that will always work
                  window.addEventListener("load", completed, false);
  
                  // If IE event model is used
              } else {
                  // Ensure firing before onload, maybe late but safe also for iframes
                  document.attachEvent("onreadystatechange", completed);
  
                  // A fallback to window.onload, that will always work
                  window.attachEvent("onload", completed);
  
                  // If IE and not a frame
                  // continually check to see if the document is ready
                  var top = false;
  
                  try {
                      top = window.frameElement == null && document.documentElement;
                  } catch (e) {}
  
                  if (top && top.doScroll) {
                      (function doScrollCheck() {
                          if (!jQuery.isReady) {
  
                              try {
                                  // Use the trick by Diego Perini
                                  // http://javascript.nwbox.com/IEContentLoaded/
                                  top.doScroll("left");
                              } catch (e) {
                                  return setTimeout(doScrollCheck, 50);
                              }
  
                              // detach all dom ready events
                              detach();
  
                              // and execute any waiting functions
                              jQuery.ready();
                          }
                      })();
                  }
              }
          }
          return readyList.promise(obj);
      };
  
      var strundefined = typeof undefined;
  
      // Support: IE<9
      // Iteration over object's inherited properties before its own
      var i;
      for (i in jQuery(support)) {
          break;
      }
      support.ownLast = i !== "0";
  
      // Note: most support tests are defined in their respective modules.
      // false until the test is run
      support.inlineBlockNeedsLayout = false;
  
      // Execute ASAP in case we need to set body.style.zoom
      jQuery(function () {
          // Minified: var a,b,c,d
          var val, div, body, container;
  
          body = document.getElementsByTagName("body")[0];
          if (!body || !body.style) {
              // Return for frameset docs that don't have a body
              return;
          }
  
          // Setup
          div = document.createElement("div");
          container = document.createElement("div");
          container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
          body.appendChild(container).appendChild(div);
  
          if (typeof div.style.zoom !== strundefined) {
              // Support: IE<8
              // Check if natively block-level elements act like inline-block
              // elements when setting their display to 'inline' and giving
              // them layout
              div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";
  
              support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
              if (val) {
                  // Prevent IE 6 from affecting layout for positioned elements #11048
                  // Prevent IE from shrinking the body in IE 7 mode #12869
                  // Support: IE<8
                  body.style.zoom = 1;
              }
          }
  
          body.removeChild(container);
      });
  
      (function () {
          var div = document.createElement("div");
  
          // Execute the test only if not already executed in another module.
          if (support.deleteExpando == null) {
              // Support: IE<9
              support.deleteExpando = true;
              try {
                  delete div.test;
              } catch (e) {
                  support.deleteExpando = false;
              }
          }
  
          // Null elements to avoid leaks in IE.
          div = null;
      })();
  
      /**
       * Determines whether an object can have data
       */
      jQuery.acceptData = function (elem) {
          var noData = jQuery.noData[(elem.nodeName + " ").toLowerCase()],
              nodeType = +elem.nodeType || 1;
  
          // Do not set data on non-element DOM nodes because it will not be cleared (#8335).
          return nodeType !== 1 && nodeType !== 9 ? false :
  
          // Nodes accept data unless otherwise specified; rejection can be conditional
          !noData || noData !== true && elem.getAttribute("classid") === noData;
      };
  
      var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
          rmultiDash = /([A-Z])/g;
  
      function dataAttr(elem, key, data) {
          // If nothing was found internally, try to fetch any
          // data from the HTML5 data-* attribute
          if (data === undefined && elem.nodeType === 1) {
  
              var name = "data-" + key.replace(rmultiDash, "-$1").toLowerCase();
  
              data = elem.getAttribute(name);
  
              if (typeof data === "string") {
                  try {
                      data = data === "true" ? true : data === "false" ? false : data === "null" ? null :
                      // Only convert to a number if it doesn't change the string
                      +data + "" === data ? +data : rbrace.test(data) ? jQuery.parseJSON(data) : data;
                  } catch (e) {}
  
                  // Make sure we set the data so it isn't changed later
                  jQuery.data(elem, key, data);
              } else {
                  data = undefined;
              }
          }
  
          return data;
      }
  
      // checks a cache object for emptiness
      function isEmptyDataObject(obj) {
          var name;
          for (name in obj) {
  
              // if the public data object is empty, the private is still empty
              if (name === "data" && jQuery.isEmptyObject(obj[name])) {
                  continue;
              }
              if (name !== "toJSON") {
                  return false;
              }
          }
  
          return true;
      }
  
      function internalData(elem, name, data, pvt /* Internal Use Only */) {
          if (!jQuery.acceptData(elem)) {
              return;
          }
  
          var ret,
              thisCache,
              internalKey = jQuery.expando,
  
  
          // We have to handle DOM nodes and JS objects differently because IE6-7
          // can't GC object references properly across the DOM-JS boundary
          isNode = elem.nodeType,
  
  
          // Only DOM nodes need the global jQuery cache; JS object data is
          // attached directly to the object so GC can occur automatically
          cache = isNode ? jQuery.cache : elem,
  
  
          // Only defining an ID for JS objects if its cache already exists allows
          // the code to shortcut on the same path as a DOM node with no cache
          id = isNode ? elem[internalKey] : elem[internalKey] && internalKey;
  
          // Avoid doing any more work than we need to when trying to get data on an
          // object that has no data at all
          if ((!id || !cache[id] || !pvt && !cache[id].data) && data === undefined && typeof name === "string") {
              return;
          }
  
          if (!id) {
              // Only DOM nodes need a new unique ID for each element since their data
              // ends up in the global cache
              if (isNode) {
                  id = elem[internalKey] = deletedIds.pop() || jQuery.guid++;
              } else {
                  id = internalKey;
              }
          }
  
          if (!cache[id]) {
              // Avoid exposing jQuery metadata on plain JS objects when the object
              // is serialized using JSON.stringify
              cache[id] = isNode ? {} : { toJSON: jQuery.noop };
          }
  
          // An object can be passed to jQuery.data instead of a key/value pair; this gets
          // shallow copied over onto the existing cache
          if (typeof name === "object" || typeof name === "function") {
              if (pvt) {
                  cache[id] = jQuery.extend(cache[id], name);
              } else {
                  cache[id].data = jQuery.extend(cache[id].data, name);
              }
          }
  
          thisCache = cache[id];
  
          // jQuery data() is stored in a separate object inside the object's internal data
          // cache in order to avoid key collisions between internal data and user-defined
          // data.
          if (!pvt) {
              if (!thisCache.data) {
                  thisCache.data = {};
              }
  
              thisCache = thisCache.data;
          }
  
          if (data !== undefined) {
              thisCache[jQuery.camelCase(name)] = data;
          }
  
          // Check for both converted-to-camel and non-converted data property names
          // If a data property was specified
          if (typeof name === "string") {
  
              // First Try to find as-is property data
              ret = thisCache[name];
  
              // Test for null|undefined property data
              if (ret == null) {
  
                  // Try to find the camelCased property
                  ret = thisCache[jQuery.camelCase(name)];
              }
          } else {
              ret = thisCache;
          }
  
          return ret;
      }
  
      function internalRemoveData(elem, name, pvt) {
          if (!jQuery.acceptData(elem)) {
              return;
          }
  
          var thisCache,
              i,
              isNode = elem.nodeType,
  
  
          // See jQuery.data for more information
          cache = isNode ? jQuery.cache : elem,
              id = isNode ? elem[jQuery.expando] : jQuery.expando;
  
          // If there is already no cache entry for this object, there is no
          // purpose in continuing
          if (!cache[id]) {
              return;
          }
  
          if (name) {
  
              thisCache = pvt ? cache[id] : cache[id].data;
  
              if (thisCache) {
  
                  // Support array or space separated string names for data keys
                  if (!jQuery.isArray(name)) {
  
                      // try the string as a key before any manipulation
                      if (name in thisCache) {
                          name = [name];
                      } else {
  
                          // split the camel cased version by spaces unless a key with the spaces exists
                          name = jQuery.camelCase(name);
                          if (name in thisCache) {
                              name = [name];
                          } else {
                              name = name.split(" ");
                          }
                      }
                  } else {
                      // If "name" is an array of keys...
                      // When data is initially created, via ("key", "val") signature,
                      // keys will be converted to camelCase.
                      // Since there is no way to tell _how_ a key was added, remove
                      // both plain key and camelCase key. #12786
                      // This will only penalize the array argument path.
                      name = name.concat(jQuery.map(name, jQuery.camelCase));
                  }
  
                  i = name.length;
                  while (i--) {
                      delete thisCache[name[i]];
                  }
  
                  // If there is no data left in the cache, we want to continue
                  // and let the cache object itself get destroyed
                  if (pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache)) {
                      return;
                  }
              }
          }
  
          // See jQuery.data for more information
          if (!pvt) {
              delete cache[id].data;
  
              // Don't destroy the parent cache unless the internal data object
              // had been the only thing left in it
              if (!isEmptyDataObject(cache[id])) {
                  return;
              }
          }
  
          // Destroy the cache
          if (isNode) {
              jQuery.cleanData([elem], true);
  
              // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
              /* jshint eqeqeq: false */
          } else if (support.deleteExpando || cache != cache.window) {
              /* jshint eqeqeq: true */
              delete cache[id];
  
              // When all else fails, null
          } else {
              cache[id] = null;
          }
      }
  
      jQuery.extend({
          cache: {},
  
          // The following elements (space-suffixed to avoid Object.prototype collisions)
          // throw uncatchable exceptions if you attempt to set expando properties
          noData: {
              "applet ": true,
              "embed ": true,
              // ...but Flash objects (which have this classid) *can* handle expandos
              "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
          },
  
          hasData: function (elem) {
              elem = elem.nodeType ? jQuery.cache[elem[jQuery.expando]] : elem[jQuery.expando];
              return !!elem && !isEmptyDataObject(elem);
          },
  
          data: function (elem, name, data) {
              return internalData(elem, name, data);
          },
  
          removeData: function (elem, name) {
              return internalRemoveData(elem, name);
          },
  
          // For internal use only.
          _data: function (elem, name, data) {
              return internalData(elem, name, data, true);
          },
  
          _removeData: function (elem, name) {
              return internalRemoveData(elem, name, true);
          }
      });
  
      jQuery.fn.extend({
          data: function (key, value) {
              var i,
                  name,
                  data,
                  elem = this[0],
                  attrs = elem && elem.attributes;
  
              // Special expections of .data basically thwart jQuery.access,
              // so implement the relevant behavior ourselves
  
              // Gets all values
              if (key === undefined) {
                  if (this.length) {
                      data = jQuery.data(elem);
  
                      if (elem.nodeType === 1 && !jQuery._data(elem, "parsedAttrs")) {
                          i = attrs.length;
                          while (i--) {
  
                              // Support: IE11+
                              // The attrs elements can be null (#14894)
                              if (attrs[i]) {
                                  name = attrs[i].name;
                                  if (name.indexOf("data-") === 0) {
                                      name = jQuery.camelCase(name.slice(5));
                                      dataAttr(elem, name, data[name]);
                                  }
                              }
                          }
                          jQuery._data(elem, "parsedAttrs", true);
                      }
                  }
  
                  return data;
              }
  
              // Sets multiple values
              if (typeof key === "object") {
                  return this.each(function () {
                      jQuery.data(this, key);
                  });
              }
  
              return arguments.length > 1 ?
  
              // Sets one value
              this.each(function () {
                  jQuery.data(this, key, value);
              }) :
  
              // Gets one value
              // Try to fetch any internally stored data first
              elem ? dataAttr(elem, key, jQuery.data(elem, key)) : undefined;
          },
  
          removeData: function (key) {
              return this.each(function () {
                  jQuery.removeData(this, key);
              });
          }
      });
  
      jQuery.extend({
          queue: function (elem, type, data) {
              var queue;
  
              if (elem) {
                  type = (type || "fx") + "queue";
                  queue = jQuery._data(elem, type);
  
                  // Speed up dequeue by getting out quickly if this is just a lookup
                  if (data) {
                      if (!queue || jQuery.isArray(data)) {
                          queue = jQuery._data(elem, type, jQuery.makeArray(data));
                      } else {
                          queue.push(data);
                      }
                  }
                  return queue || [];
              }
          },
  
          dequeue: function (elem, type) {
              type = type || "fx";
  
              var queue = jQuery.queue(elem, type),
                  startLength = queue.length,
                  fn = queue.shift(),
                  hooks = jQuery._queueHooks(elem, type),
                  next = function () {
                  jQuery.dequeue(elem, type);
              };
  
              // If the fx queue is dequeued, always remove the progress sentinel
              if (fn === "inprogress") {
                  fn = queue.shift();
                  startLength--;
              }
  
              if (fn) {
  
                  // Add a progress sentinel to prevent the fx queue from being
                  // automatically dequeued
                  if (type === "fx") {
                      queue.unshift("inprogress");
                  }
  
                  // clear up the last queue stop function
                  delete hooks.stop;
                  fn.call(elem, next, hooks);
              }
  
              if (!startLength && hooks) {
                  hooks.empty.fire();
              }
          },
  
          // not intended for public consumption - generates a queueHooks object, or returns the current one
          _queueHooks: function (elem, type) {
              var key = type + "queueHooks";
              return jQuery._data(elem, key) || jQuery._data(elem, key, {
                  empty: jQuery.Callbacks("once memory").add(function () {
                      jQuery._removeData(elem, type + "queue");
                      jQuery._removeData(elem, key);
                  })
              });
          }
      });
  
      jQuery.fn.extend({
          queue: function (type, data) {
              var setter = 2;
  
              if (typeof type !== "string") {
                  data = type;
                  type = "fx";
                  setter--;
              }
  
              if (arguments.length < setter) {
                  return jQuery.queue(this[0], type);
              }
  
              return data === undefined ? this : this.each(function () {
                  var queue = jQuery.queue(this, type, data);
  
                  // ensure a hooks for this queue
                  jQuery._queueHooks(this, type);
  
                  if (type === "fx" && queue[0] !== "inprogress") {
                      jQuery.dequeue(this, type);
                  }
              });
          },
          dequeue: function (type) {
              return this.each(function () {
                  jQuery.dequeue(this, type);
              });
          },
          clearQueue: function (type) {
              return this.queue(type || "fx", []);
          },
          // Get a promise resolved when queues of a certain type
          // are emptied (fx is the type by default)
          promise: function (type, obj) {
              var tmp,
                  count = 1,
                  defer = jQuery.Deferred(),
                  elements = this,
                  i = this.length,
                  resolve = function () {
                  if (! --count) {
                      defer.resolveWith(elements, [elements]);
                  }
              };
  
              if (typeof type !== "string") {
                  obj = type;
                  type = undefined;
              }
              type = type || "fx";
  
              while (i--) {
                  tmp = jQuery._data(elements[i], type + "queueHooks");
                  if (tmp && tmp.empty) {
                      count++;
                      tmp.empty.add(resolve);
                  }
              }
              resolve();
              return defer.promise(obj);
          }
      });
  
      /*
       * Helper functions for managing events -- not part of the public interface.
       * Props to Dean Edwards' addEvent library for many of the ideas.
       */
      jQuery.event = {
  
          global: {},
  
          add: function (elem, types, handler, data, selector) {
              var tmp,
                  events,
                  t,
                  handleObjIn,
                  special,
                  eventHandle,
                  handleObj,
                  handlers,
                  type,
                  namespaces,
                  origType,
                  elemData = jQuery._data(elem);
  
              // Don't attach events to noData or text/comment nodes (but allow plain objects)
              if (!elemData) {
                  return;
              }
  
              // Caller can pass in an object of custom data in lieu of the handler
              if (handler.handler) {
                  handleObjIn = handler;
                  handler = handleObjIn.handler;
                  selector = handleObjIn.selector;
              }
  
              // Make sure that the handler has a unique ID, used to find/remove it later
              if (!handler.guid) {
                  handler.guid = jQuery.guid++;
              }
  
              // Init the element's event structure and main handler, if this is the first
              if (!(events = elemData.events)) {
                  events = elemData.events = {};
              }
              if (!(eventHandle = elemData.handle)) {
                  eventHandle = elemData.handle = function (e) {
                      // Discard the second event of a jQuery.event.trigger() and
                      // when an event is called after a page has unloaded
                      return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply(eventHandle.elem, arguments) : undefined;
                  };
                  // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
                  eventHandle.elem = elem;
              }
  
              // Handle multiple events separated by a space
              types = (types || "").match(rnotwhite) || [""];
              t = types.length;
              while (t--) {
                  tmp = rtypenamespace.exec(types[t]) || [];
                  type = origType = tmp[1];
                  namespaces = (tmp[2] || "").split(".").sort();
  
                  // There *must* be a type, no attaching namespace-only handlers
                  if (!type) {
                      continue;
                  }
  
                  // If event changes its type, use the special event handlers for the changed type
                  special = jQuery.event.special[type] || {};
  
                  // If selector defined, determine special event api type, otherwise given type
                  type = (selector ? special.delegateType : special.bindType) || type;
  
                  // Update special based on newly reset type
                  special = jQuery.event.special[type] || {};
  
                  // handleObj is passed to all event handlers
                  handleObj = jQuery.extend({
                      type: type,
                      origType: origType,
                      data: data,
                      handler: handler,
                      guid: handler.guid,
                      selector: selector,
                      needsContext: selector && jQuery.expr.match.needsContext.test(selector),
                      namespace: namespaces.join(".")
                  }, handleObjIn);
  
                  // Init the event handler queue if we're the first
                  if (!(handlers = events[type])) {
                      handlers = events[type] = [];
                      handlers.delegateCount = 0;
  
                      // Only use addEventListener/attachEvent if the special events handler returns false
                      if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) {
                          // Bind the global event handler to the element
                          if (elem.addEventListener) {
                              elem.addEventListener(type, eventHandle, false);
                          } else if (elem.attachEvent) {
                              elem.attachEvent("on" + type, eventHandle);
                          }
                      }
                  }
  
                  if (special.add) {
                      special.add.call(elem, handleObj);
  
                      if (!handleObj.handler.guid) {
                          handleObj.handler.guid = handler.guid;
                      }
                  }
  
                  // Add to the element's handler list, delegates in front
                  if (selector) {
                      handlers.splice(handlers.delegateCount++, 0, handleObj);
                  } else {
                      handlers.push(handleObj);
                  }
  
                  // Keep track of which events have ever been used, for event optimization
                  jQuery.event.global[type] = true;
              }
  
              // Nullify elem to prevent memory leaks in IE
              elem = null;
          },
  
          // Detach an event or set of events from an element
          remove: function (elem, types, handler, selector, mappedTypes) {
              var j,
                  handleObj,
                  tmp,
                  origCount,
                  t,
                  events,
                  special,
                  handlers,
                  type,
                  namespaces,
                  origType,
                  elemData = jQuery.hasData(elem) && jQuery._data(elem);
  
              if (!elemData || !(events = elemData.events)) {
                  return;
              }
  
              // Once for each type.namespace in types; type may be omitted
              types = (types || "").match(rnotwhite) || [""];
              t = types.length;
              while (t--) {
                  tmp = rtypenamespace.exec(types[t]) || [];
                  type = origType = tmp[1];
                  namespaces = (tmp[2] || "").split(".").sort();
  
                  // Unbind all events (on this namespace, if provided) for the element
                  if (!type) {
                      for (type in events) {
                          jQuery.event.remove(elem, type + types[t], handler, selector, true);
                      }
                      continue;
                  }
  
                  special = jQuery.event.special[type] || {};
                  type = (selector ? special.delegateType : special.bindType) || type;
                  handlers = events[type] || [];
                  tmp = tmp[2] && new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)");
  
                  // Remove matching events
                  origCount = j = handlers.length;
                  while (j--) {
                      handleObj = handlers[j];
  
                      if ((mappedTypes || origType === handleObj.origType) && (!handler || handler.guid === handleObj.guid) && (!tmp || tmp.test(handleObj.namespace)) && (!selector || selector === handleObj.selector || selector === "**" && handleObj.selector)) {
                          handlers.splice(j, 1);
  
                          if (handleObj.selector) {
                              handlers.delegateCount--;
                          }
                          if (special.remove) {
                              special.remove.call(elem, handleObj);
                          }
                      }
                  }
  
                  // Remove generic event handler if we removed something and no more handlers exist
                  // (avoids potential for endless recursion during removal of special event handlers)
                  if (origCount && !handlers.length) {
                      if (!special.teardown || special.teardown.call(elem, namespaces, elemData.handle) === false) {
                          jQuery.removeEvent(elem, type, elemData.handle);
                      }
  
                      delete events[type];
                  }
              }
  
              // Remove the expando if it's no longer used
              if (jQuery.isEmptyObject(events)) {
                  delete elemData.handle;
  
                  // removeData also checks for emptiness and clears the expando if empty
                  // so use it instead of delete
                  jQuery._removeData(elem, "events");
              }
          },
  
          trigger: function (event, data, elem, onlyHandlers) {
              var handle,
                  ontype,
                  cur,
                  bubbleType,
                  special,
                  tmp,
                  i,
                  eventPath = [elem || document],
                  type = hasOwn.call(event, "type") ? event.type : event,
                  namespaces = hasOwn.call(event, "namespace") ? event.namespace.split(".") : [];
  
              cur = tmp = elem = elem || document;
  
              // Don't do events on text and comment nodes
              if (elem.nodeType === 3 || elem.nodeType === 8) {
                  return;
              }
  
              // focus/blur morphs to focusin/out; ensure we're not firing them right now
              if (rfocusMorph.test(type + jQuery.event.triggered)) {
                  return;
              }
  
              if (type.indexOf(".") >= 0) {
                  // Namespaced trigger; create a regexp to match event type in handle()
                  namespaces = type.split(".");
                  type = namespaces.shift();
                  namespaces.sort();
              }
              ontype = type.indexOf(":") < 0 && "on" + type;
  
              // Caller can pass in a jQuery.Event object, Object, or just an event type string
              event = event[jQuery.expando] ? event : new jQuery.Event(type, typeof event === "object" && event);
  
              // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
              event.isTrigger = onlyHandlers ? 2 : 3;
              event.namespace = namespaces.join(".");
              event.namespace_re = event.namespace ? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
  
              // Clean up the event in case it is being reused
              event.result = undefined;
              if (!event.target) {
                  event.target = elem;
              }
  
              // Clone any incoming data and prepend the event, creating the handler arg list
              data = data == null ? [event] : jQuery.makeArray(data, [event]);
  
              // Allow special events to draw outside the lines
              special = jQuery.event.special[type] || {};
              if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) {
                  return;
              }
  
              // Determine event propagation path in advance, per W3C events spec (#9951)
              // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
              if (!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)) {
  
                  bubbleType = special.delegateType || type;
                  if (!rfocusMorph.test(bubbleType + type)) {
                      cur = cur.parentNode;
                  }
                  for (; cur; cur = cur.parentNode) {
                      eventPath.push(cur);
                      tmp = cur;
                  }
  
                  // Only add window if we got to document (e.g., not plain obj or detached DOM)
                  if (tmp === (elem.ownerDocument || document)) {
                      eventPath.push(tmp.defaultView || tmp.parentWindow || window);
                  }
              }
  
              // Fire handlers on the event path
              i = 0;
              while ((cur = eventPath[i++]) && !event.isPropagationStopped()) {
  
                  event.type = i > 1 ? bubbleType : special.bindType || type;
  
                  // jQuery handler
                  handle = (jQuery._data(cur, "events") || {})[event.type] && jQuery._data(cur, "handle");
                  if (handle) {
                      handle.apply(cur, data);
                  }
  
                  // Native handler
                  handle = ontype && cur[ontype];
                  if (handle && handle.apply && jQuery.acceptData(cur)) {
                      event.result = handle.apply(cur, data);
                      if (event.result === false) {
                          event.preventDefault();
                      }
                  }
              }
              event.type = type;
  
              // If nobody prevented the default action, do it now
              if (!onlyHandlers && !event.isDefaultPrevented()) {
  
                  if ((!special._default || special._default.apply(eventPath.pop(), data) === false) && jQuery.acceptData(elem)) {
  
                      // Call a native DOM method on the target with the same name name as the event.
                      // Can't use an .isFunction() check here because IE6/7 fails that test.
                      // Don't do default actions on window, that's where global variables be (#6170)
                      if (ontype && elem[type] && !jQuery.isWindow(elem)) {
  
                          // Don't re-trigger an onFOO event when we call its FOO() method
                          tmp = elem[ontype];
  
                          if (tmp) {
                              elem[ontype] = null;
                          }
  
                          // Prevent re-triggering of the same event, since we already bubbled it above
                          jQuery.event.triggered = type;
                          try {
                              elem[type]();
                          } catch (e) {
                              // IE<9 dies on focus/blur to hidden element (#1486,#12518)
                              // only reproducible on winXP IE8 native, not IE9 in IE8 mode
                          }
                          jQuery.event.triggered = undefined;
  
                          if (tmp) {
                              elem[ontype] = tmp;
                          }
                      }
                  }
              }
  
              return event.result;
          },
  
          dispatch: function (event) {
  
              // Make a writable jQuery.Event from the native event object
              event = jQuery.event.fix(event);
  
              var i,
                  ret,
                  handleObj,
                  matched,
                  j,
                  handlerQueue = [],
                  args = slice.call(arguments),
                  handlers = (jQuery._data(this, "events") || {})[event.type] || [],
                  special = jQuery.event.special[event.type] || {};
  
              // Use the fix-ed jQuery.Event rather than the (read-only) native event
              args[0] = event;
              event.delegateTarget = this;
  
              // Call the preDispatch hook for the mapped type, and let it bail if desired
              if (special.preDispatch && special.preDispatch.call(this, event) === false) {
                  return;
              }
  
              // Determine handlers
              handlerQueue = jQuery.event.handlers.call(this, event, handlers);
  
              // Run delegates first; they may want to stop propagation beneath us
              i = 0;
              while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) {
                  event.currentTarget = matched.elem;
  
                  j = 0;
                  while ((handleObj = matched.handlers[j++]) && !event.isImmediatePropagationStopped()) {
  
                      // Triggered event must either 1) have no namespace, or
                      // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
                      if (!event.namespace_re || event.namespace_re.test(handleObj.namespace)) {
  
                          event.handleObj = handleObj;
                          event.data = handleObj.data;
  
                          ret = ((jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler).apply(matched.elem, args);
  
                          if (ret !== undefined) {
                              if ((event.result = ret) === false) {
                                  event.preventDefault();
                                  event.stopPropagation();
                              }
                          }
                      }
                  }
              }
  
              // Call the postDispatch hook for the mapped type
              if (special.postDispatch) {
                  special.postDispatch.call(this, event);
              }
  
              return event.result;
          },
  
          handlers: function (event, handlers) {
              var sel,
                  handleObj,
                  matches,
                  i,
                  handlerQueue = [],
                  delegateCount = handlers.delegateCount,
                  cur = event.target;
  
              // Find delegate handlers
              // Black-hole SVG <use> instance trees (#13180)
              // Avoid non-left-click bubbling in Firefox (#3861)
              if (delegateCount && cur.nodeType && (!event.button || event.type !== "click")) {
  
                  /* jshint eqeqeq: false */
                  for (; cur != this; cur = cur.parentNode || this) {
                      /* jshint eqeqeq: true */
  
                      // Don't check non-elements (#13208)
                      // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
                      if (cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click")) {
                          matches = [];
                          for (i = 0; i < delegateCount; i++) {
                              handleObj = handlers[i];
  
                              // Don't conflict with Object.prototype properties (#13203)
                              sel = handleObj.selector + " ";
  
                              if (matches[sel] === undefined) {
                                  matches[sel] = handleObj.needsContext ? jQuery(sel, this).index(cur) >= 0 : jQuery.find(sel, this, null, [cur]).length;
                              }
                              if (matches[sel]) {
                                  matches.push(handleObj);
                              }
                          }
                          if (matches.length) {
                              handlerQueue.push({ elem: cur, handlers: matches });
                          }
                      }
                  }
              }
  
              // Add the remaining (directly-bound) handlers
              if (delegateCount < handlers.length) {
                  handlerQueue.push({ elem: this, handlers: handlers.slice(delegateCount) });
              }
  
              return handlerQueue;
          },
  
          fix: function (event) {
              if (event[jQuery.expando]) {
                  return event;
              }
  
              // Create a writable copy of the event object and normalize some properties
              var i,
                  prop,
                  copy,
                  type = event.type,
                  originalEvent = event,
                  fixHook = this.fixHooks[type];
  
              if (!fixHook) {
                  this.fixHooks[type] = fixHook = rmouseEvent.test(type) ? this.mouseHooks : rkeyEvent.test(type) ? this.keyHooks : {};
              }
              copy = fixHook.props ? this.props.concat(fixHook.props) : this.props;
  
              event = new jQuery.Event(originalEvent);
  
              i = copy.length;
              while (i--) {
                  prop = copy[i];
                  event[prop] = originalEvent[prop];
              }
  
              // Support: IE<9
              // Fix target property (#1925)
              if (!event.target) {
                  event.target = originalEvent.srcElement || document;
              }
  
              // Support: Chrome 23+, Safari?
              // Target should not be a text node (#504, #13143)
              if (event.target.nodeType === 3) {
                  event.target = event.target.parentNode;
              }
  
              // Support: IE<9
              // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
              event.metaKey = !!event.metaKey;
  
              return fixHook.filter ? fixHook.filter(event, originalEvent) : event;
          },
  
          // Includes some event props shared by KeyEvent and MouseEvent
          props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
  
          fixHooks: {},
  
          keyHooks: {
              props: "char charCode key keyCode".split(" "),
              filter: function (event, original) {
  
                  // Add which for key events
                  if (event.which == null) {
                      event.which = original.charCode != null ? original.charCode : original.keyCode;
                  }
  
                  return event;
              }
          },
  
          mouseHooks: {
              props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
              filter: function (event, original) {
                  var body,
                      eventDoc,
                      doc,
                      button = original.button,
                      fromElement = original.fromElement;
  
                  // Calculate pageX/Y if missing and clientX/Y available
                  if (event.pageX == null && original.clientX != null) {
                      eventDoc = event.target.ownerDocument || document;
                      doc = eventDoc.documentElement;
                      body = eventDoc.body;
  
                      event.pageX = original.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
                      event.pageY = original.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
                  }
  
                  // Add relatedTarget, if necessary
                  if (!event.relatedTarget && fromElement) {
                      event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
                  }
  
                  // Add which for click: 1 === left; 2 === middle; 3 === right
                  // Note: button is not normalized, so don't use it
                  if (!event.which && button !== undefined) {
                      event.which = button & 1 ? 1 : button & 2 ? 3 : button & 4 ? 2 : 0;
                  }
  
                  return event;
              }
          },
  
          special: {
              load: {
                  // Prevent triggered image.load events from bubbling to window.load
                  noBubble: true
              },
              focus: {
                  // Fire native event if possible so blur/focus sequence is correct
                  trigger: function () {
                      if (this !== safeActiveElement() && this.focus) {
                          try {
                              this.focus();
                              return false;
                          } catch (e) {
                              // Support: IE<9
                              // If we error on focus to hidden element (#1486, #12518),
                              // let .trigger() run the handlers
                          }
                      }
                  },
                  delegateType: "focusin"
              },
              blur: {
                  trigger: function () {
                      if (this === safeActiveElement() && this.blur) {
                          this.blur();
                          return false;
                      }
                  },
                  delegateType: "focusout"
              },
              click: {
                  // For checkbox, fire native event so checked state will be right
                  trigger: function () {
                      if (jQuery.nodeName(this, "input") && this.type === "checkbox" && this.click) {
                          this.click();
                          return false;
                      }
                  },
  
                  // For cross-browser consistency, don't fire native .click() on links
                  _default: function (event) {
                      return jQuery.nodeName(event.target, "a");
                  }
              },
  
              beforeunload: {
                  postDispatch: function (event) {
  
                      // Support: Firefox 20+
                      // Firefox doesn't alert if the returnValue field is not set.
                      if (event.result !== undefined && event.originalEvent) {
                          event.originalEvent.returnValue = event.result;
                      }
                  }
              }
          },
  
          simulate: function (type, elem, event, bubble) {
              // Piggyback on a donor event to simulate a different one.
              // Fake originalEvent to avoid donor's stopPropagation, but if the
              // simulated event prevents default then we do the same on the donor.
              var e = jQuery.extend(new jQuery.Event(), event, {
                  type: type,
                  isSimulated: true,
                  originalEvent: {}
              });
              if (bubble) {
                  jQuery.event.trigger(e, null, elem);
              } else {
                  jQuery.event.dispatch.call(elem, e);
              }
              if (e.isDefaultPrevented()) {
                  event.preventDefault();
              }
          }
      };
  
      jQuery.removeEvent = document.removeEventListener ? function (elem, type, handle) {
          if (elem.removeEventListener) {
              elem.removeEventListener(type, handle, false);
          }
      } : function (elem, type, handle) {
          var name = "on" + type;
  
          if (elem.detachEvent) {
  
              // #8545, #7054, preventing memory leaks for custom events in IE6-8
              // detachEvent needed property on element, by name of that event, to properly expose it to GC
              if (typeof elem[name] === strundefined) {
                  elem[name] = null;
              }
  
              elem.detachEvent(name, handle);
          }
      };
  
      jQuery.Event = function (src, props) {
          // Allow instantiation without the 'new' keyword
          if (!(this instanceof jQuery.Event)) {
              return new jQuery.Event(src, props);
          }
  
          // Event object
          if (src && src.type) {
              this.originalEvent = src;
              this.type = src.type;
  
              // Events bubbling up the document may have been marked as prevented
              // by a handler lower down the tree; reflect the correct value.
              this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined &&
              // Support: IE < 9, Android < 4.0
              src.returnValue === false ? returnTrue : returnFalse;
  
              // Event type
          } else {
              this.type = src;
          }
  
          // Put explicitly provided properties onto the event object
          if (props) {
              jQuery.extend(this, props);
          }
  
          // Create a timestamp if incoming event doesn't have one
          this.timeStamp = src && src.timeStamp || jQuery.now();
  
          // Mark it as fixed
          this[jQuery.expando] = true;
      };
  
      var rformElems = /^(?:input|select|textarea)$/i,
          rkeyEvent = /^key/,
          rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
          rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
          rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
  
      function returnTrue() {
          return true;
      }
  
      function returnFalse() {
          return false;
      }
  
      // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
      // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
      jQuery.Event.prototype = {
          isDefaultPrevented: returnFalse,
          isPropagationStopped: returnFalse,
          isImmediatePropagationStopped: returnFalse,
  
          preventDefault: function () {
              var e = this.originalEvent;
  
              this.isDefaultPrevented = returnTrue;
              if (!e) {
                  return;
              }
  
              // If preventDefault exists, run it on the original event
              if (e.preventDefault) {
                  e.preventDefault();
  
                  // Support: IE
                  // Otherwise set the returnValue property of the original event to false
              } else {
                  e.returnValue = false;
              }
          },
          stopPropagation: function () {
              var e = this.originalEvent;
  
              this.isPropagationStopped = returnTrue;
              if (!e) {
                  return;
              }
              // If stopPropagation exists, run it on the original event
              if (e.stopPropagation) {
                  e.stopPropagation();
              }
  
              // Support: IE
              // Set the cancelBubble property of the original event to true
              e.cancelBubble = true;
          },
          stopImmediatePropagation: function () {
              var e = this.originalEvent;
  
              this.isImmediatePropagationStopped = returnTrue;
  
              if (e && e.stopImmediatePropagation) {
                  e.stopImmediatePropagation();
              }
  
              this.stopPropagation();
          }
      };
  
      // IE submit delegation
      if (!support.submitBubbles) {
  
          jQuery.event.special.submit = {
              setup: function () {
                  // Only need this for delegated form submit events
                  if (jQuery.nodeName(this, "form")) {
                      return false;
                  }
  
                  // Lazy-add a submit handler when a descendant form may potentially be submitted
                  jQuery.event.add(this, "click._submit keypress._submit", function (e) {
                      // Node name check avoids a VML-related crash in IE (#9807)
                      var elem = e.target,
                          form = jQuery.nodeName(elem, "input") || jQuery.nodeName(elem, "button") ? elem.form : undefined;
                      if (form && !jQuery._data(form, "submitBubbles")) {
                          jQuery.event.add(form, "submit._submit", function (event) {
                              event._submit_bubble = true;
                          });
                          jQuery._data(form, "submitBubbles", true);
                      }
                  });
                  // return undefined since we don't need an event listener
              },
  
              postDispatch: function (event) {
                  // If form was submitted by the user, bubble the event up the tree
                  if (event._submit_bubble) {
                      delete event._submit_bubble;
                      if (this.parentNode && !event.isTrigger) {
                          jQuery.event.simulate("submit", this.parentNode, event, true);
                      }
                  }
              },
  
              teardown: function () {
                  // Only need this for delegated form submit events
                  if (jQuery.nodeName(this, "form")) {
                      return false;
                  }
  
                  // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
                  jQuery.event.remove(this, "._submit");
              }
          };
      }
  
      // IE change delegation and checkbox/radio fix
      if (!support.changeBubbles) {
  
          jQuery.event.special.change = {
  
              setup: function () {
  
                  if (rformElems.test(this.nodeName)) {
                      // IE doesn't fire change on a check/radio until blur; trigger it on click
                      // after a propertychange. Eat the blur-change in special.change.handle.
                      // This still fires onchange a second time for check/radio after blur.
                      if (this.type === "checkbox" || this.type === "radio") {
                          jQuery.event.add(this, "propertychange._change", function (event) {
                              if (event.originalEvent.propertyName === "checked") {
                                  this._just_changed = true;
                              }
                          });
                          jQuery.event.add(this, "click._change", function (event) {
                              if (this._just_changed && !event.isTrigger) {
                                  this._just_changed = false;
                              }
                              // Allow triggered, simulated change events (#11500)
                              jQuery.event.simulate("change", this, event, true);
                          });
                      }
                      return false;
                  }
                  // Delegated event; lazy-add a change handler on descendant inputs
                  jQuery.event.add(this, "beforeactivate._change", function (e) {
                      var elem = e.target;
  
                      if (rformElems.test(elem.nodeName) && !jQuery._data(elem, "changeBubbles")) {
                          jQuery.event.add(elem, "change._change", function (event) {
                              if (this.parentNode && !event.isSimulated && !event.isTrigger) {
                                  jQuery.event.simulate("change", this.parentNode, event, true);
                              }
                          });
                          jQuery._data(elem, "changeBubbles", true);
                      }
                  });
              },
  
              handle: function (event) {
                  var elem = event.target;
  
                  // Swallow native change events from checkbox/radio, we already triggered them above
                  if (this !== elem || event.isSimulated || event.isTrigger || elem.type !== "radio" && elem.type !== "checkbox") {
                      return event.handleObj.handler.apply(this, arguments);
                  }
              },
  
              teardown: function () {
                  jQuery.event.remove(this, "._change");
  
                  return !rformElems.test(this.nodeName);
              }
          };
      }
  
      // Create "bubbling" focus and blur events
      if (!support.focusinBubbles) {
          jQuery.each({ focus: "focusin", blur: "focusout" }, function (orig, fix) {
  
              // Attach a single capturing handler on the document while someone wants focusin/focusout
              var handler = function (event) {
                  jQuery.event.simulate(fix, event.target, jQuery.event.fix(event), true);
              };
  
              jQuery.event.special[fix] = {
                  setup: function () {
                      var doc = this.ownerDocument || this,
                          attaches = jQuery._data(doc, fix);
  
                      if (!attaches) {
                          doc.addEventListener(orig, handler, true);
                      }
                      jQuery._data(doc, fix, (attaches || 0) + 1);
                  },
                  teardown: function () {
                      var doc = this.ownerDocument || this,
                          attaches = jQuery._data(doc, fix) - 1;
  
                      if (!attaches) {
                          doc.removeEventListener(orig, handler, true);
                          jQuery._removeData(doc, fix);
                      } else {
                          jQuery._data(doc, fix, attaches);
                      }
                  }
              };
          });
      }
  
      jQuery.fn.extend({
  
          on: function (types, selector, data, fn, /*INTERNAL*/one) {
              var type, origFn;
  
              // Types can be a map of types/handlers
              if (typeof types === "object") {
                  // ( types-Object, selector, data )
                  if (typeof selector !== "string") {
                      // ( types-Object, data )
                      data = data || selector;
                      selector = undefined;
                  }
                  for (type in types) {
                      this.on(type, selector, data, types[type], one);
                  }
                  return this;
              }
  
              if (data == null && fn == null) {
                  // ( types, fn )
                  fn = selector;
                  data = selector = undefined;
              } else if (fn == null) {
                  if (typeof selector === "string") {
                      // ( types, selector, fn )
                      fn = data;
                      data = undefined;
                  } else {
                      // ( types, data, fn )
                      fn = data;
                      data = selector;
                      selector = undefined;
                  }
              }
              if (fn === false) {
                  fn = returnFalse;
              } else if (!fn) {
                  return this;
              }
  
              if (one === 1) {
                  origFn = fn;
                  fn = function (event) {
                      // Can use an empty set, since event contains the info
                      jQuery().off(event);
                      return origFn.apply(this, arguments);
                  };
                  // Use same guid so caller can remove using origFn
                  fn.guid = origFn.guid || (origFn.guid = jQuery.guid++);
              }
              return this.each(function () {
                  jQuery.event.add(this, types, fn, data, selector);
              });
          },
          one: function (types, selector, data, fn) {
              return this.on(types, selector, data, fn, 1);
          },
          off: function (types, selector, fn) {
              var handleObj, type;
              if (types && types.preventDefault && types.handleObj) {
                  // ( event )  dispatched jQuery.Event
                  handleObj = types.handleObj;
                  jQuery(types.delegateTarget).off(handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler);
                  return this;
              }
              if (typeof types === "object") {
                  // ( types-object [, selector] )
                  for (type in types) {
                      this.off(type, selector, types[type]);
                  }
                  return this;
              }
              if (selector === false || typeof selector === "function") {
                  // ( types [, fn] )
                  fn = selector;
                  selector = undefined;
              }
              if (fn === false) {
                  fn = returnFalse;
              }
              return this.each(function () {
                  jQuery.event.remove(this, types, fn, selector);
              });
          },
  
          trigger: function (type, data) {
              return this.each(function () {
                  jQuery.event.trigger(type, data, this);
              });
          },
          triggerHandler: function (type, data) {
              var elem = this[0];
              if (elem) {
                  return jQuery.event.trigger(type, data, elem, true);
              }
          }
      });
  
      // Based off of the plugin by Clint Helfers, with permission.
      // http://blindsignals.com/index.php/2009/07/jquery-delay/
      jQuery.fn.delay = function (time, type) {
          time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
          type = type || "fx";
  
          return this.queue(type, function (next, hooks) {
              var timeout = setTimeout(next, time);
              hooks.stop = function () {
                  clearTimeout(timeout);
              };
          });
      };
  
      var nonce = jQuery.now();
  
      var rquery = /\?/;
  
      var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
  
      jQuery.parseJSON = function (data) {
          // Attempt to parse using the native JSON parser first
          if (window.JSON && window.JSON.parse) {
              // Support: Android 2.3
              // Workaround failure to string-cast null input
              return window.JSON.parse(data + "");
          }
  
          var requireNonComma,
              depth = null,
              str = jQuery.trim(data + "");
  
          // Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
          // after removing valid tokens
          return str && !jQuery.trim(str.replace(rvalidtokens, function (token, comma, open, close) {
  
              // Force termination if we see a misplaced comma
              if (requireNonComma && comma) {
                  depth = 0;
              }
  
              // Perform no more replacements after returning to outermost depth
              if (depth === 0) {
                  return token;
              }
  
              // Commas must not follow "[", "{", or ","
              requireNonComma = open || comma;
  
              // Determine new depth
              // array/object open ("[" or "{"): depth += true - false (increment)
              // array/object close ("]" or "}"): depth += false - true (decrement)
              // other cases ("," or primitive): depth += true - true (numeric cast)
              depth += !close - !open;
  
              // Remove this token
              return "";
          })) ? Function("return " + str)() : jQuery.error("Invalid JSON: " + data);
      };
  
      // Cross-browser xml parsing
      jQuery.parseXML = function (data) {
          var xml, tmp;
          if (!data || typeof data !== "string") {
              return null;
          }
          try {
              if (window.DOMParser) {
                  // Standard
                  tmp = new DOMParser();
                  xml = tmp.parseFromString(data, "text/xml");
              } else {
                  // IE
                  xml = new ActiveXObject("Microsoft.XMLDOM");
                  xml.async = "false";
                  xml.loadXML(data);
              }
          } catch (e) {
              xml = undefined;
          }
          if (!xml || !xml.documentElement || xml.getElementsByTagName("parsererror").length) {
              jQuery.error("Invalid XML: " + data);
          }
          return xml;
      };
  
      var
      // Document location
      ajaxLocParts,
          ajaxLocation,
          rhash = /#.*$/,
          rts = /([?&])_=[^&]*/,
          rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg,
          // IE leaves an \r character at EOL
      // #7653, #8125, #8152: local protocol detection
      rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
          rnoContent = /^(?:GET|HEAD)$/,
          rprotocol = /^\/\//,
          rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
  
  
      /* Prefilters
       * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
       * 2) These are called:
       *    - BEFORE asking for a transport
       *    - AFTER param serialization (s.data is a string if s.processData is true)
       * 3) key is the dataType
       * 4) the catchall symbol "*" can be used
       * 5) execution will start with transport dataType and THEN continue down to "*" if needed
       */
      prefilters = {},
  
  
      /* Transports bindings
       * 1) key is the dataType
       * 2) the catchall symbol "*" can be used
       * 3) selection will start with transport dataType and THEN go to "*" if needed
       */
      transports = {},
  
  
      // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
      allTypes = "*/".concat("*");
  
      // #8138, IE may throw an exception when accessing
      // a field from window.location if document.domain has been set
      try {
          ajaxLocation = location.href;
      } catch (e) {
          // Use the href attribute of an A element
          // since IE will modify it given document.location
          ajaxLocation = document.createElement("a");
          ajaxLocation.href = "";
          ajaxLocation = ajaxLocation.href;
      }
  
      // Segment location into parts
      ajaxLocParts = rurl.exec(ajaxLocation.toLowerCase()) || [];
  
      // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
      function addToPrefiltersOrTransports(structure) {
  
          // dataTypeExpression is optional and defaults to "*"
          return function (dataTypeExpression, func) {
  
              if (typeof dataTypeExpression !== "string") {
                  func = dataTypeExpression;
                  dataTypeExpression = "*";
              }
  
              var dataType,
                  i = 0,
                  dataTypes = dataTypeExpression.toLowerCase().match(rnotwhite) || [];
  
              if (jQuery.isFunction(func)) {
                  // For each dataType in the dataTypeExpression
                  while (dataType = dataTypes[i++]) {
                      // Prepend if requested
                      if (dataType.charAt(0) === "+") {
                          dataType = dataType.slice(1) || "*";
                          (structure[dataType] = structure[dataType] || []).unshift(func);
  
                          // Otherwise append
                      } else {
                          (structure[dataType] = structure[dataType] || []).push(func);
                      }
                  }
              }
          };
      }
  
      // Base inspection function for prefilters and transports
      function inspectPrefiltersOrTransports(structure, options, originalOptions, jqXHR) {
  
          var inspected = {},
              seekingTransport = structure === transports;
  
          function inspect(dataType) {
              var selected;
              inspected[dataType] = true;
              jQuery.each(structure[dataType] || [], function (_, prefilterOrFactory) {
                  var dataTypeOrTransport = prefilterOrFactory(options, originalOptions, jqXHR);
                  if (typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[dataTypeOrTransport]) {
                      options.dataTypes.unshift(dataTypeOrTransport);
                      inspect(dataTypeOrTransport);
                      return false;
                  } else if (seekingTransport) {
                      return !(selected = dataTypeOrTransport);
                  }
              });
              return selected;
          }
  
          return inspect(options.dataTypes[0]) || !inspected["*"] && inspect("*");
      }
  
      // A special extend for ajax options
      // that takes "flat" options (not to be deep extended)
      // Fixes #9887
      function ajaxExtend(target, src) {
          var deep,
              key,
              flatOptions = jQuery.ajaxSettings.flatOptions || {};
  
          for (key in src) {
              if (src[key] !== undefined) {
                  (flatOptions[key] ? target : deep || (deep = {}))[key] = src[key];
              }
          }
          if (deep) {
              jQuery.extend(true, target, deep);
          }
  
          return target;
      }
  
      /* Handles responses to an ajax request:
       * - finds the right dataType (mediates between content-type and expected dataType)
       * - returns the corresponding response
       */
      function ajaxHandleResponses(s, jqXHR, responses) {
          var firstDataType,
              ct,
              finalDataType,
              type,
              contents = s.contents,
              dataTypes = s.dataTypes;
  
          // Remove auto dataType and get content-type in the process
          while (dataTypes[0] === "*") {
              dataTypes.shift();
              if (ct === undefined) {
                  ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
              }
          }
  
          // Check if we're dealing with a known content-type
          if (ct) {
              for (type in contents) {
                  if (contents[type] && contents[type].test(ct)) {
                      dataTypes.unshift(type);
                      break;
                  }
              }
          }
  
          // Check to see if we have a response for the expected dataType
          if (dataTypes[0] in responses) {
              finalDataType = dataTypes[0];
          } else {
              // Try convertible dataTypes
              for (type in responses) {
                  if (!dataTypes[0] || s.converters[type + " " + dataTypes[0]]) {
                      finalDataType = type;
                      break;
                  }
                  if (!firstDataType) {
                      firstDataType = type;
                  }
              }
              // Or just use first one
              finalDataType = finalDataType || firstDataType;
          }
  
          // If we found a dataType
          // We add the dataType to the list if needed
          // and return the corresponding response
          if (finalDataType) {
              if (finalDataType !== dataTypes[0]) {
                  dataTypes.unshift(finalDataType);
              }
              return responses[finalDataType];
          }
      }
  
      /* Chain conversions given the request and the original response
       * Also sets the responseXXX fields on the jqXHR instance
       */
      function ajaxConvert(s, response, jqXHR, isSuccess) {
          var conv2,
              current,
              conv,
              tmp,
              prev,
              converters = {},
  
          // Work with a copy of dataTypes in case we need to modify it for conversion
          dataTypes = s.dataTypes.slice();
  
          // Create converters map with lowercased keys
          if (dataTypes[1]) {
              for (conv in s.converters) {
                  converters[conv.toLowerCase()] = s.converters[conv];
              }
          }
  
          current = dataTypes.shift();
  
          // Convert to each sequential dataType
          while (current) {
  
              if (s.responseFields[current]) {
                  jqXHR[s.responseFields[current]] = response;
              }
  
              // Apply the dataFilter if provided
              if (!prev && isSuccess && s.dataFilter) {
                  response = s.dataFilter(response, s.dataType);
              }
  
              prev = current;
              current = dataTypes.shift();
  
              if (current) {
  
                  // There's only work to do if current dataType is non-auto
                  if (current === "*") {
  
                      current = prev;
  
                      // Convert response if prev dataType is non-auto and differs from current
                  } else if (prev !== "*" && prev !== current) {
  
                      // Seek a direct converter
                      conv = converters[prev + " " + current] || converters["* " + current];
  
                      // If none found, seek a pair
                      if (!conv) {
                          for (conv2 in converters) {
  
                              // If conv2 outputs current
                              tmp = conv2.split(" ");
                              if (tmp[1] === current) {
  
                                  // If prev can be converted to accepted input
                                  conv = converters[prev + " " + tmp[0]] || converters["* " + tmp[0]];
                                  if (conv) {
                                      // Condense equivalence converters
                                      if (conv === true) {
                                          conv = converters[conv2];
  
                                          // Otherwise, insert the intermediate dataType
                                      } else if (converters[conv2] !== true) {
                                          current = tmp[0];
                                          dataTypes.unshift(tmp[1]);
                                      }
                                      break;
                                  }
                              }
                          }
                      }
  
                      // Apply converter (if not an equivalence)
                      if (conv !== true) {
  
                          // Unless errors are allowed to bubble, catch and return them
                          if (conv && s["throws"]) {
                              response = conv(response);
                          } else {
                              try {
                                  response = conv(response);
                              } catch (e) {
                                  return {
                                      state: "parsererror",
                                      error: conv ? e : "No conversion from " + prev + " to " + current
                                  };
                              }
                          }
                      }
                  }
              }
          }
  
          return { state: "success", data: response };
      }
  
      jQuery.extend({
  
          // Counter for holding the number of active queries
          active: 0,
  
          // Last-Modified header cache for next request
          lastModified: {},
          etag: {},
  
          ajaxSettings: {
              url: ajaxLocation,
              type: "GET",
              isLocal: rlocalProtocol.test(ajaxLocParts[1]),
              global: true,
              processData: true,
              async: true,
              contentType: "application/x-www-form-urlencoded; charset=UTF-8",
              /*
               timeout: 0,
               data: null,
               dataType: null,
               username: null,
               password: null,
               cache: null,
               throws: false,
               traditional: false,
               headers: {},
               */
  
              accepts: {
                  "*": allTypes,
                  text: "text/plain",
                  html: "text/html",
                  xml: "application/xml, text/xml",
                  json: "application/json, text/javascript"
              },
  
              contents: {
                  xml: /xml/,
                  html: /html/,
                  json: /json/
              },
  
              responseFields: {
                  xml: "responseXML",
                  text: "responseText",
                  json: "responseJSON"
              },
  
              // Data converters
              // Keys separate source (or catchall "*") and destination types with a single space
              converters: {
  
                  // Convert anything to text
                  "* text": String,
  
                  // Text to html (true = no transformation)
                  "text html": true,
  
                  // Evaluate text as a json expression
                  "text json": jQuery.parseJSON,
  
                  // Parse text as xml
                  "text xml": jQuery.parseXML
              },
  
              // For options that shouldn't be deep extended:
              // you can add your own custom options here if
              // and when you create one that shouldn't be
              // deep extended (see ajaxExtend)
              flatOptions: {
                  url: true,
                  context: true
              }
          },
  
          // Creates a full fledged settings object into target
          // with both ajaxSettings and settings fields.
          // If target is omitted, writes into ajaxSettings.
          ajaxSetup: function (target, settings) {
              return settings ?
  
              // Building a settings object
              ajaxExtend(ajaxExtend(target, jQuery.ajaxSettings), settings) :
  
              // Extending ajaxSettings
              ajaxExtend(jQuery.ajaxSettings, target);
          },
  
          ajaxPrefilter: addToPrefiltersOrTransports(prefilters),
          ajaxTransport: addToPrefiltersOrTransports(transports),
  
          // Main method
          ajax: function (url, options) {
  
              // If url is an object, simulate pre-1.5 signature
              if (typeof url === "object") {
                  options = url;
                  url = undefined;
              }
  
              // Force options to be an object
              options = options || {};
  
              var // Cross-domain detection vars
              parts,
  
              // Loop variable
              i,
  
              // URL without anti-cache param
              cacheURL,
  
              // Response headers as string
              responseHeadersString,
  
              // timeout handle
              timeoutTimer,
  
  
              // To know if global events are to be dispatched
              fireGlobals,
                  transport,
  
              // Response headers
              responseHeaders,
  
              // Create the final options object
              s = jQuery.ajaxSetup({}, options),
  
              // Callbacks context
              callbackContext = s.context || s,
  
              // Context for global events is callbackContext if it is a DOM node or jQuery collection
              globalEventContext = s.context && (callbackContext.nodeType || callbackContext.jquery) ? jQuery(callbackContext) : jQuery.event,
  
              // Deferreds
              deferred = jQuery.Deferred(),
                  completeDeferred = jQuery.Callbacks("once memory"),
  
              // Status-dependent callbacks
              statusCode = s.statusCode || {},
  
              // Headers (they are sent all at once)
              requestHeaders = {},
                  requestHeadersNames = {},
  
              // The jqXHR state
              state = 0,
  
              // Default abort message
              strAbort = "canceled",
  
              // Fake xhr
              jqXHR = {
                  readyState: 0,
  
                  // Builds headers hashtable if needed
                  getResponseHeader: function (key) {
                      var match;
                      if (state === 2) {
                          if (!responseHeaders) {
                              responseHeaders = {};
                              while (match = rheaders.exec(responseHeadersString)) {
                                  responseHeaders[match[1].toLowerCase()] = match[2];
                              }
                          }
                          match = responseHeaders[key.toLowerCase()];
                      }
                      return match == null ? null : match;
                  },
  
                  // Raw string
                  getAllResponseHeaders: function () {
                      return state === 2 ? responseHeadersString : null;
                  },
  
                  // Caches the header
                  setRequestHeader: function (name, value) {
                      var lname = name.toLowerCase();
                      if (!state) {
                          name = requestHeadersNames[lname] = requestHeadersNames[lname] || name;
                          requestHeaders[name] = value;
                      }
                      return this;
                  },
  
                  // Overrides response content-type header
                  overrideMimeType: function (type) {
                      if (!state) {
                          s.mimeType = type;
                      }
                      return this;
                  },
  
                  // Status-dependent callbacks
                  statusCode: function (map) {
                      var code;
                      if (map) {
                          if (state < 2) {
                              for (code in map) {
                                  // Lazy-add the new callback in a way that preserves old ones
                                  statusCode[code] = [statusCode[code], map[code]];
                              }
                          } else {
                              // Execute the appropriate callbacks
                              jqXHR.always(map[jqXHR.status]);
                          }
                      }
                      return this;
                  },
  
                  // Cancel the request
                  abort: function (statusText) {
                      var finalText = statusText || strAbort;
                      if (transport) {
                          transport.abort(finalText);
                      }
                      done(0, finalText);
                      return this;
                  }
              };
  
              // Attach deferreds
              deferred.promise(jqXHR).complete = completeDeferred.add;
              jqXHR.success = jqXHR.done;
              jqXHR.error = jqXHR.fail;
  
              // Remove hash character (#7531: and string promotion)
              // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
              // Handle falsy url in the settings object (#10093: consistency with old signature)
              // We also use the url parameter if available
              s.url = ((url || s.url || ajaxLocation) + "").replace(rhash, "").replace(rprotocol, ajaxLocParts[1] + "//");
  
              // Alias method option to type as per ticket #12004
              s.type = options.method || options.type || s.method || s.type;
  
              // Extract dataTypes list
              s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().match(rnotwhite) || [""];
  
              // A cross-domain request is in order when we have a protocol:host:port mismatch
              if (s.crossDomain == null) {
                  parts = rurl.exec(s.url.toLowerCase());
                  s.crossDomain = !!(parts && (parts[1] !== ajaxLocParts[1] || parts[2] !== ajaxLocParts[2] || (parts[3] || (parts[1] === "http:" ? "80" : "443")) !== (ajaxLocParts[3] || (ajaxLocParts[1] === "http:" ? "80" : "443"))));
              }
  
              // Convert data if not already a string
              if (s.data && s.processData && typeof s.data !== "string") {
                  s.data = jQuery.param(s.data, s.traditional);
              }
  
              // Apply prefilters
              inspectPrefiltersOrTransports(prefilters, s, options, jqXHR);
  
              // If request was aborted inside a prefilter, stop there
              if (state === 2) {
                  return jqXHR;
              }
  
              // We can fire global events as of now if asked to
              fireGlobals = s.global;
  
              // Watch for a new set of requests
              if (fireGlobals && jQuery.active++ === 0) {
                  jQuery.event.trigger("ajaxStart");
              }
  
              // Uppercase the type
              s.type = s.type.toUpperCase();
  
              // Determine if request has content
              s.hasContent = !rnoContent.test(s.type);
  
              // Save the URL in case we're toying with the If-Modified-Since
              // and/or If-None-Match header later on
              cacheURL = s.url;
  
              // More options handling for requests with no content
              if (!s.hasContent) {
  
                  // If data is available, append data to url
                  if (s.data) {
                      cacheURL = s.url += (rquery.test(cacheURL) ? "&" : "?") + s.data;
                      // #9682: remove data so that it's not used in an eventual retry
                      delete s.data;
                  }
  
                  // Add anti-cache in url if needed
                  if (s.cache === false) {
                      s.url = rts.test(cacheURL) ?
  
                      // If there is already a '_' parameter, set its value
                      cacheURL.replace(rts, "$1_=" + nonce++) :
  
                      // Otherwise add one to the end
                      cacheURL + (rquery.test(cacheURL) ? "&" : "?") + "_=" + nonce++;
                  }
              }
  
              // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
              if (s.ifModified) {
                  if (jQuery.lastModified[cacheURL]) {
                      jqXHR.setRequestHeader("If-Modified-Since", jQuery.lastModified[cacheURL]);
                  }
                  if (jQuery.etag[cacheURL]) {
                      jqXHR.setRequestHeader("If-None-Match", jQuery.etag[cacheURL]);
                  }
              }
  
              // Set the correct header, if data is being sent
              if (s.data && s.hasContent && s.contentType !== false || options.contentType) {
                  jqXHR.setRequestHeader("Content-Type", s.contentType);
              }
  
              // Set the Accepts header for the server, depending on the dataType
              // jqXHR.setRequestHeader(
              //     "Accept",
              //     s.dataTypes[0] && s.accepts[s.dataTypes[0]] ?
              //         s.accepts[s.dataTypes[0]] + ( s.dataTypes[0] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
              //         s.accepts["*"]
              // );
  
              // Check for headers option
              for (i in s.headers) {
                  jqXHR.setRequestHeader(i, s.headers[i]);
              }
  
              // Allow custom headers/mimetypes and early abort
              if (s.beforeSend && (s.beforeSend.call(callbackContext, jqXHR, s) === false || state === 2)) {
                  // Abort if not done already and return
                  return jqXHR.abort();
              }
  
              // aborting is no longer a cancellation
              strAbort = "abort";
  
              // Install callbacks on deferreds
              for (i in { success: 1, error: 1, complete: 1 }) {
                  jqXHR[i](s[i]);
              }
  
              // Get transport
              transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);
  
              // If no transport, we auto-abort
              if (!transport) {
                  done(-1, "No Transport");
              } else {
                  jqXHR.readyState = 1;
  
                  // Send global event
                  if (fireGlobals) {
                      globalEventContext.trigger("ajaxSend", [jqXHR, s]);
                  }
                  // Timeout
                  if (s.async && s.timeout > 0) {
                      timeoutTimer = setTimeout(function () {
                          jqXHR.abort("timeout");
                      }, s.timeout);
                  }
  
                  try {
                      state = 1;
                      transport.send(requestHeaders, done);
                  } catch (e) {
                      // Propagate exception as error if not done
                      if (state < 2) {
                          done(-1, e);
                          // Simply rethrow otherwise
                      } else {
                          throw e;
                      }
                  }
              }
  
              // Callback for when everything is done
              function done(status, nativeStatusText, responses, headers) {
                  var isSuccess,
                      success,
                      error,
                      response,
                      modified,
                      statusText = nativeStatusText;
  
                  // Called once
                  if (state === 2) {
                      return;
                  }
  
                  // State is "done" now
                  state = 2;
  
                  // Clear timeout if it exists
                  if (timeoutTimer) {
                      clearTimeout(timeoutTimer);
                  }
  
                  // Dereference transport for early garbage collection
                  // (no matter how long the jqXHR object will be used)
                  transport = undefined;
  
                  // Cache response headers
                  responseHeadersString = headers || "";
  
                  // Set readyState
                  jqXHR.readyState = status > 0 ? 4 : 0;
  
                  // Determine if successful
                  isSuccess = status >= 200 && status < 300 || status === 304;
  
                  // Get response data
                  if (responses) {
                      response = ajaxHandleResponses(s, jqXHR, responses);
                  }
  
                  // Convert no matter what (that way responseXXX fields are always set)
                  response = ajaxConvert(s, response, jqXHR, isSuccess);
  
                  // If successful, handle type chaining
                  if (isSuccess) {
  
                      // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
                      if (s.ifModified) {
                          modified = jqXHR.getResponseHeader("Last-Modified");
                          if (modified) {
                              jQuery.lastModified[cacheURL] = modified;
                          }
                          modified = jqXHR.getResponseHeader("etag");
                          if (modified) {
                              jQuery.etag[cacheURL] = modified;
                          }
                      }
  
                      // if no content
                      if (status === 204 || s.type === "HEAD") {
                          statusText = "nocontent";
  
                          // if not modified
                      } else if (status === 304) {
                          statusText = "notmodified";
  
                          // If we have data, let's convert it
                      } else {
                          statusText = response.state;
                          success = response.data;
                          error = response.error;
                          isSuccess = !error;
                      }
                  } else {
                      // We extract error from statusText
                      // then normalize statusText and status for non-aborts
                      error = statusText;
                      if (status || !statusText) {
                          statusText = "error";
                          if (status < 0) {
                              status = 0;
                          }
                      }
                  }
  
                  // Set data for the fake xhr object
                  jqXHR.status = status;
                  jqXHR.statusText = (nativeStatusText || statusText) + "";
  
                  // Success/Error
                  if (isSuccess) {
                      deferred.resolveWith(callbackContext, [success, statusText, jqXHR]);
                  } else {
                      deferred.rejectWith(callbackContext, [jqXHR, statusText, error]);
                  }
  
                  // Status-dependent callbacks
                  jqXHR.statusCode(statusCode);
                  statusCode = undefined;
  
                  if (fireGlobals) {
                      globalEventContext.trigger(isSuccess ? "ajaxSuccess" : "ajaxError", [jqXHR, s, isSuccess ? success : error]);
                  }
  
                  // Complete
                  completeDeferred.fireWith(callbackContext, [jqXHR, statusText]);
  
                  if (fireGlobals) {
                      globalEventContext.trigger("ajaxComplete", [jqXHR, s]);
                      // Handle the global AJAX counter
                      if (! --jQuery.active) {
                          jQuery.event.trigger("ajaxStop");
                      }
                  }
              }
  
              return jqXHR;
          },
  
          getJSON: function (url, data, callback) {
              return jQuery.get(url, data, callback, "json");
          },
  
          getScript: function (url, callback) {
              return jQuery.get(url, undefined, callback, "script");
          }
      });
  
      jQuery.each(["get", "post"], function (i, method) {
          jQuery[method] = function (url, data, callback, type) {
              // shift arguments if data argument was omitted
              if (jQuery.isFunction(data)) {
                  type = type || callback;
                  callback = data;
                  data = undefined;
              }
  
              return jQuery.ajax({
                  url: url,
                  type: method,
                  dataType: type,
                  data: data,
                  success: callback
              });
          };
      });
  
      // Attach a bunch of functions for handling common AJAX events
      jQuery.each(["ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend"], function (i, type) {
          jQuery.fn[type] = function (fn) {
              return this.on(type, fn);
          };
      });
  
      jQuery._evalUrl = function (url) {
          return jQuery.ajax({
              url: url,
              type: "GET",
              dataType: "script",
              async: false,
              global: false,
              "throws": true
          });
      };
  
      var r20 = /%20/g,
          rbracket = /\[\]$/,
          rCRLF = /\r?\n/g,
          rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
          rsubmittable = /^(?:input|select|textarea|keygen)/i;
  
      function buildParams(prefix, obj, traditional, add) {
          var name;
  
          if (jQuery.isArray(obj)) {
              // Serialize array item.
              jQuery.each(obj, function (i, v) {
                  if (traditional || rbracket.test(prefix)) {
                      // Treat each array item as a scalar.
                      add(prefix, v);
                  } else {
                      // Item is non-scalar (array or object), encode its numeric index.
                      buildParams(prefix + "[" + (typeof v === "object" ? i : "") + "]", v, traditional, add);
                  }
              });
          } else if (!traditional && jQuery.type(obj) === "object") {
              // Serialize object item.
              for (name in obj) {
                  buildParams(prefix + "[" + name + "]", obj[name], traditional, add);
              }
          } else {
              // Serialize scalar item.
              add(prefix, obj);
          }
      }
  
      // Serialize an array of form elements or a set of
      // key/values into a query string
      jQuery.param = function (a, traditional) {
          var prefix,
              s = [],
              add = function (key, value) {
              // If value is a function, invoke it and return its value
              value = jQuery.isFunction(value) ? value() : value == null ? "" : value;
              s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
          };
  
          // Set traditional to true for jQuery <= 1.3.2 behavior.
          if (traditional === undefined) {
              traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
          }
  
          // If an array was passed in, assume that it is an array of form elements.
          if (jQuery.isArray(a) || a.jquery && !jQuery.isPlainObject(a)) {
              // Serialize the form elements
              jQuery.each(a, function () {
                  add(this.name, this.value);
              });
          } else {
              // If traditional, encode the "old" way (the way 1.3.2 or older
              // did it), otherwise encode params recursively.
              for (prefix in a) {
                  buildParams(prefix, a[prefix], traditional, add);
              }
          }
  
          // Return the resulting serialization
          return s.join("&").replace(r20, "+");
      };
  
      jQuery.fn.extend({
          serialize: function () {
              return jQuery.param(this.serializeArray());
          },
          serializeArray: function () {
              return this.map(function () {
                  // Can add propHook for "elements" to filter or add form elements
                  var elements = jQuery.prop(this, "elements");
                  return elements ? jQuery.makeArray(elements) : this;
              }).filter(function () {
                  var type = this.type;
                  // Use .is(":disabled") so that fieldset[disabled] works
                  return this.name && !jQuery(this).is(":disabled") && rsubmittable.test(this.nodeName) && !rsubmitterTypes.test(type) && (this.checked || !rcheckableType.test(type));
              }).map(function (i, elem) {
                  var val = jQuery(this).val();
  
                  return val == null ? null : jQuery.isArray(val) ? jQuery.map(val, function (val) {
                      return { name: elem.name, value: val.replace(rCRLF, "\r\n") };
                  }) : { name: elem.name, value: val.replace(rCRLF, "\r\n") };
              }).get();
          }
      });
  
      // Create the request object
      // (This is still attached to ajaxSettings for backward compatibility)
      jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
      // Support: IE6+
      function () {
  
          // XHR cannot access local files, always use ActiveX for that case
          return !this.isLocal &&
  
          // Support: IE7-8
          // oldIE XHR does not support non-RFC2616 methods (#13240)
          // See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
          // and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
          // Although this check for six methods instead of eight
          // since IE also does not support "trace" and "connect"
          /^(get|post|head|put|delete|options)$/i.test(this.type) && createStandardXHR() || createActiveXHR();
      } :
      // For all other browsers, use the standard XMLHttpRequest object
      createStandardXHR;
  
      var xhrId = 0,
          xhrCallbacks = {},
          xhrSupported = jQuery.ajaxSettings.xhr();
  
      // Support: IE<10
      // Open requests must be manually aborted on unload (#5280)
      if (window.ActiveXObject) {
          jQuery(window).on("unload", function () {
              for (var key in xhrCallbacks) {
                  xhrCallbacks[key](undefined, true);
              }
          });
      }
  
      // Determine support properties
      support.cors = !!xhrSupported && "withCredentials" in xhrSupported;
      xhrSupported = support.ajax = !!xhrSupported;
  
      // Create transport if the browser can provide an xhr
      if (xhrSupported) {
  
          jQuery.ajaxTransport(function (options) {
              // Cross domain only allowed if supported through XMLHttpRequest
              if (!options.crossDomain || support.cors) {
  
                  var callback;
  
                  return {
                      send: function (headers, complete) {
                          var i,
                              xhr = options.xhr(),
                              id = ++xhrId;
  
                          // Open the socket
                          xhr.open(options.type, options.url, options.async, options.username, options.password);
  
                          // Apply custom fields if provided
                          if (options.xhrFields) {
                              for (i in options.xhrFields) {
                                  xhr[i] = options.xhrFields[i];
                              }
                          }
  
                          // Override mime type if needed
                          if (options.mimeType && xhr.overrideMimeType) {
                              xhr.overrideMimeType(options.mimeType);
                          }
  
                          // X-Requested-With header
                          // For cross-domain requests, seeing as conditions for a preflight are
                          // akin to a jigsaw puzzle, we simply never set it to be sure.
                          // (it can always be set on a per-request basis or even using ajaxSetup)
                          // For same-domain requests, won't change header if already provided.
                          if (!options.crossDomain && !headers["X-Requested-With"]) {
                              headers["X-Requested-With"] = "XMLHttpRequest";
                          }
  
                          // Set headers
                          for (i in headers) {
                              // Support: IE<9
                              // IE's ActiveXObject throws a 'Type Mismatch' exception when setting
                              // request header to a null-value.
                              //
                              // To keep consistent with other XHR implementations, cast the value
                              // to string and ignore `undefined`.
                              if (headers[i] !== undefined) {
                                  xhr.setRequestHeader(i, headers[i] + "");
                              }
                          }
  
                          if (xhr.upload && options.progress) {
                              xhr.upload.onprogress = options.progress;
                          }
  
                          // Do send the request
                          // This may raise an exception which is actually
                          // handled in jQuery.ajax (so no try/catch here)
                          xhr.send(options.hasContent && (options.body || options.data) || null);
  
                          // Listener
                          callback = function (_, isAbort) {
                              var status, statusText, responses;
  
                              // Was never called and is aborted or complete
                              if (callback && (isAbort || xhr.readyState === 4)) {
                                  // Clean up
                                  delete xhrCallbacks[id];
                                  callback = undefined;
                                  xhr.onreadystatechange = jQuery.noop;
  
                                  // Abort manually if needed
                                  if (isAbort) {
                                      if (xhr.readyState !== 4) {
                                          xhr.abort();
                                      }
                                  } else {
                                      responses = {};
                                      status = xhr.status;
  
                                      // Support: IE<10
                                      // Accessing binary-data responseText throws an exception
                                      // (#11426)
                                      if (typeof xhr.responseText === "string") {
                                          responses.text = xhr.responseText;
                                      }
  
                                      // Firefox throws an exception when accessing
                                      // statusText for faulty cross-domain requests
                                      try {
                                          statusText = xhr.statusText;
                                      } catch (e) {
                                          // We normalize with Webkit giving an empty statusText
                                          statusText = "";
                                      }
  
                                      // Filter status for non standard behaviors
  
                                      // If the request is local and we have data: assume a success
                                      // (success with no data won't get notified, that's the best we
                                      // can do given current implementations)
                                      if (!status && options.isLocal && !options.crossDomain) {
                                          status = responses.text ? 200 : 404;
                                          // IE - #1450: sometimes returns 1223 when it should be 204
                                      } else if (status === 1223) {
                                          status = 204;
                                      }
                                  }
                              }
  
                              // Call complete if needed
                              if (responses) {
                                  complete(status, statusText, responses, xhr.getAllResponseHeaders());
                              }
                          };
  
                          if (!options.async) {
                              // if we're in sync mode we fire the callback
                              callback();
                          } else if (xhr.readyState === 4) {
                              // (IE6 & IE7) if it's in cache and has been
                              // retrieved directly we need to fire the callback
                              setTimeout(callback);
                          } else {
                              // Add to the list of active xhr callbacks
                              xhr.onreadystatechange = xhrCallbacks[id] = callback;
                          }
                      },
  
                      abort: function () {
                          if (callback) {
                              callback(undefined, true);
                          }
                      }
                  };
              }
          });
      }
  
      // Functions to create xhrs
      function createStandardXHR() {
          try {
              return new window.XMLHttpRequest();
          } catch (e) {}
      }
  
      function createActiveXHR() {
          try {
              return new window.ActiveXObject("Microsoft.XMLHTTP");
          } catch (e) {}
      }
  
      // Install script dataType
      jQuery.ajaxSetup({
          accepts: {
              script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
          },
          contents: {
              script: /(?:java|ecma)script/
          },
          converters: {
              "text script": function (text) {
                  jQuery.globalEval(text);
                  return text;
              }
          }
      });
  
      // Handle cache's special case and global
      jQuery.ajaxPrefilter("script", function (s) {
          if (s.cache === undefined) {
              s.cache = false;
          }
          if (s.crossDomain) {
              s.type = "GET";
              s.global = false;
          }
      });
  
      // Bind script tag hack transport
      jQuery.ajaxTransport("script", function (s) {
  
          // This transport only deals with cross domain requests
          if (s.crossDomain) {
  
              var script,
                  head = document.head || jQuery("head")[0] || document.documentElement;
  
              return {
  
                  send: function (_, callback) {
  
                      script = document.createElement("script");
  
                      script.async = true;
  
                      if (s.scriptCharset) {
                          script.charset = s.scriptCharset;
                      }
  
                      script.src = s.url;
  
                      // Attach handlers for all browsers
                      script.onload = script.onreadystatechange = function (_, isAbort) {
  
                          if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
  
                              // Handle memory leak in IE
                              script.onload = script.onreadystatechange = null;
  
                              // Remove the script
                              if (script.parentNode) {
                                  script.parentNode.removeChild(script);
                              }
  
                              // Dereference the script
                              script = null;
  
                              // Callback if not abort
                              if (!isAbort) {
                                  callback(200, "success");
                              }
                          }
                      };
  
                      // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
                      // Use native DOM manipulation to avoid our domManip AJAX trickery
                      head.insertBefore(script, head.firstChild);
                  },
  
                  abort: function () {
                      if (script) {
                          script.onload(undefined, true);
                      }
                  }
              };
          }
      });
  
      var oldCallbacks = [],
          rjsonp = /(=)\?(?=&|$)|\?\?/;
  
      // Default jsonp settings
      jQuery.ajaxSetup({
          jsonp: "callback",
          jsonpCallback: function () {
              var callback = oldCallbacks.pop() || jQuery.expando + "_" + nonce++;
              this[callback] = true;
              return callback;
          }
      });
  
      // Detect, normalize options and install callbacks for jsonp requests
      jQuery.ajaxPrefilter("json jsonp", function (s, originalSettings, jqXHR) {
  
          var callbackName,
              overwritten,
              responseContainer,
              jsonProp = s.jsonp !== false && (rjsonp.test(s.url) ? "url" : typeof s.data === "string" && !(s.contentType || "").indexOf("application/x-www-form-urlencoded") && rjsonp.test(s.data) && "data");
  
          // Handle iff the expected data type is "jsonp" or we have a parameter to set
          if (jsonProp || s.dataTypes[0] === "jsonp") {
  
              // Get callback name, remembering preexisting value associated with it
              callbackName = s.jsonpCallback = jQuery.isFunction(s.jsonpCallback) ? s.jsonpCallback() : s.jsonpCallback;
  
              // Insert callback into url or form data
              if (jsonProp) {
                  s[jsonProp] = s[jsonProp].replace(rjsonp, "$1" + callbackName);
              } else if (s.jsonp !== false) {
                  s.url += (rquery.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName;
              }
  
              // Use data converter to retrieve json after script execution
              s.converters["script json"] = function () {
                  if (!responseContainer) {
                      jQuery.error(callbackName + " was not called");
                  }
                  return responseContainer[0];
              };
  
              // force json dataType
              s.dataTypes[0] = "json";
  
              // Install callback
              overwritten = window[callbackName];
              window[callbackName] = function () {
                  responseContainer = arguments;
              };
  
              // Clean-up function (fires after converters)
              jqXHR.always(function () {
                  // Restore preexisting value
                  window[callbackName] = overwritten;
  
                  // Save back as free
                  if (s[callbackName]) {
                      // make sure that re-using the options doesn't screw things around
                      s.jsonpCallback = originalSettings.jsonpCallback;
  
                      // save the callback name for future use
                      oldCallbacks.push(callbackName);
                  }
  
                  // Call if it was a function and we have a response
                  if (responseContainer && jQuery.isFunction(overwritten)) {
                      overwritten(responseContainer[0]);
                  }
  
                  responseContainer = overwritten = undefined;
              });
  
              // Delegate to script
              return "script";
          }
      });
  
      // data: string of html
      // context (optional): If specified, the fragment will be created in this context, defaults to document
      // keepScripts (optional): If true, will include scripts passed in the html string
      jQuery.parseHTML = function (data, context, keepScripts) {
          if (!data || typeof data !== "string") {
              return null;
          }
          if (typeof context === "boolean") {
              keepScripts = context;
              context = false;
          }
          context = context || document;
  
          var parsed = rsingleTag.exec(data),
              scripts = !keepScripts && [];
  
          // Single tag
          if (parsed) {
              return [context.createElement(parsed[1])];
          }
  
          parsed = jQuery.buildFragment([data], context, scripts);
  
          if (scripts && scripts.length) {
              jQuery(scripts).remove();
          }
  
          return jQuery.merge([], parsed.childNodes);
      };
  
      return jQuery;
  }();
  
  var request = function (options, callback) {
  
      options = $.extend(true, { headers: {}, qs: {} }, options);
  
      // method
      options.type = options.method;
      delete options.method;
  
      // progress
      if (options.onProgress) {
          options.progress = options.onProgress;
          delete options.onProgress;
      }
  
      // qs
      if (options.qs) {
          var qsStr = queryString.stringify(options.qs);
          if (qsStr) {
              options.url += (options.url.indexOf('?') === -1 ? '?' : '&') + qsStr;
          }
          delete options.qs;
      }
  
      // json
      if (options.json) {
          options.data = options.body;
          delete options.json;
          delete options.body;
          !options.headers && (options.headers = {});
          options.headers['Content-Type'] = 'application/json';
      }
  
      // body
      if (options.body) {
          if (!(options.body instanceof Blob || options.body.toString() === '[object File]' || options.body.toString() === '[object Blob]')) {
              options.data = options.body;
              delete options.body;
          }
      }
  
      // headers
      if (options.headers) {
          var headers = options.headers;
          delete options.headers;
          options.beforeSend = function (xhr) {
              for (var key in headers) {
                  if (headers.hasOwnProperty(key) && key.toLowerCase() !== 'content-length' && key.toLowerCase() !== 'user-agent' && key.toLowerCase() !== 'origin' && key.toLowerCase() !== 'host') {
                      xhr.setRequestHeader(key, headers[key]);
                  }
              }
          };
      }
  
      var getResponse = function (xhr) {
          var headers = {};
          xhr.getAllResponseHeaders().trim().split('\n').forEach(function (item) {
              if (item) {
                  var index = item.indexOf(':');
                  var key = item.substr(0, index).trim().toLowerCase();
                  var val = item.substr(index + 1).trim();
                  headers[key] = val;
              }
          });
          return {
              statusCode: xhr.status,
              statusMessage: xhr.statusText,
              headers: headers
          };
      };
  
      // callback
      options.success = function (data, state, xhr) {
          callback(null, getResponse(xhr), data);
      };
      options.error = function (xhr) {
          if (xhr.responseText) {
              callback(null, getResponse(xhr), xhr.responseText);
          } else {
              callback(xhr.statusText, getResponse(xhr), xhr.responseText);
          }
      };
  
      options.dataType = 'text';
  
      // send
      return $.ajax(options);
  };
  
  module.exports = request;
  
  /***/ }),
  /* 14 */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  
  exports.decode = exports.parse = __webpack_require__(15);
  exports.encode = exports.stringify = __webpack_require__(16);
  
  
  /***/ }),
  /* 15 */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  // Copyright Joyent, Inc. and other Node contributors.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a
  // copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to permit
  // persons to whom the Software is furnished to do so, subject to the
  // following conditions:
  //
  // The above copyright notice and this permission notice shall be included
  // in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  // USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  
  
  // If obj.hasOwnProperty has been overridden, then calling
  // obj.hasOwnProperty(prop) will break.
  // See: https://github.com/joyent/node/issues/1707
  function hasOwnProperty(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
  }
  
  module.exports = function(qs, sep, eq, options) {
    sep = sep || '&';
    eq = eq || '=';
    var obj = {};
  
    if (typeof qs !== 'string' || qs.length === 0) {
      return obj;
    }
  
    var regexp = /\+/g;
    qs = qs.split(sep);
  
    var maxKeys = 1000;
    if (options && typeof options.maxKeys === 'number') {
      maxKeys = options.maxKeys;
    }
  
    var len = qs.length;
    // maxKeys <= 0 means that we should not limit keys count
    if (maxKeys > 0 && len > maxKeys) {
      len = maxKeys;
    }
  
    for (var i = 0; i < len; ++i) {
      var x = qs[i].replace(regexp, '%20'),
          idx = x.indexOf(eq),
          kstr, vstr, k, v;
  
      if (idx >= 0) {
        kstr = x.substr(0, idx);
        vstr = x.substr(idx + 1);
      } else {
        kstr = x;
        vstr = '';
      }
  
      k = decodeURIComponent(kstr);
      v = decodeURIComponent(vstr);
  
      if (!hasOwnProperty(obj, k)) {
        obj[k] = v;
      } else if (isArray(obj[k])) {
        obj[k].push(v);
      } else {
        obj[k] = [obj[k], v];
      }
    }
  
    return obj;
  };
  
  var isArray = Array.isArray || function (xs) {
    return Object.prototype.toString.call(xs) === '[object Array]';
  };
  
  
  /***/ }),
  /* 16 */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  // Copyright Joyent, Inc. and other Node contributors.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a
  // copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to permit
  // persons to whom the Software is furnished to do so, subject to the
  // following conditions:
  //
  // The above copyright notice and this permission notice shall be included
  // in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  // USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  
  
  var stringifyPrimitive = function(v) {
    switch (typeof v) {
      case 'string':
        return v;
  
      case 'boolean':
        return v ? 'true' : 'false';
  
      case 'number':
        return isFinite(v) ? v : '';
  
      default:
        return '';
    }
  };
  
  module.exports = function(obj, sep, eq, name) {
    sep = sep || '&';
    eq = eq || '=';
    if (obj === null) {
      obj = undefined;
    }
  
    if (typeof obj === 'object') {
      return map(objectKeys(obj), function(k) {
        var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
        if (isArray(obj[k])) {
          return map(obj[k], function(v) {
            return ks + encodeURIComponent(stringifyPrimitive(v));
          }).join(sep);
        } else {
          return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
        }
      }).join(sep);
  
    }
  
    if (!name) return '';
    return encodeURIComponent(stringifyPrimitive(name)) + eq +
           encodeURIComponent(stringifyPrimitive(obj));
  };
  
  var isArray = Array.isArray || function (xs) {
    return Object.prototype.toString.call(xs) === '[object Array]';
  };
  
  function map (xs, f) {
    if (xs.map) return xs.map(f);
    var res = [];
    for (var i = 0; i < xs.length; i++) {
      res.push(f(xs[i], i));
    }
    return res;
  }
  
  var objectKeys = Object.keys || function (obj) {
    var res = [];
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
    }
    return res;
  };
  
  
  /***/ }),
  /* 17 */
  /***/ (function(module, exports, __webpack_require__) {
  
  var Async = __webpack_require__(18);
  var EventProxy = __webpack_require__(2).EventProxy;
  var util = __webpack_require__(0);
  
  // 文件分块上传全过程，暴露的分块上传接口
  function sliceUploadFile(params, callback) {
      var self = this;
      var ep = new EventProxy();
      var TaskId = params.TaskId;
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var Body = params.Body;
      var ChunkSize = params.ChunkSize || params.SliceSize || self.options.ChunkSize;
      var AsyncLimit = params.AsyncLimit;
      var StorageClass = params.StorageClass || 'Standard';
      var ServerSideEncryption = params.ServerSideEncryption;
      var FileSize;
  
      var onProgress;
      var onHashProgress = params.onHashProgress;
  
      // 上传过程中出现错误，返回错误
      ep.on('error', function (err) {
          if (!self._isRunningTask(TaskId)) return;
          return callback(err);
      });
  
      // 上传分块完成，开始 uploadSliceComplete 操作
      ep.on('upload_complete', function (UploadCompleteData) {
          callback(null, UploadCompleteData);
      });
  
      // 上传分块完成，开始 uploadSliceComplete 操作
      ep.on('upload_slice_complete', function (UploadData) {
          uploadSliceComplete.call(self, {
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              UploadId: UploadData.UploadId,
              SliceList: UploadData.SliceList
          }, function (err, data) {
              if (!self._isRunningTask(TaskId)) return;
              delete uploadIdUsing[UploadData.UploadId];
              if (err) {
                  onProgress(null, true);
                  return ep.emit('error', err);
              }
              onProgress({ loaded: FileSize, total: FileSize }, true);
              removeUploadId.call(self, UploadData.UploadId);
              ep.emit('upload_complete', data);
          });
      });
  
      // 获取 UploadId 完成，开始上传每个分片
      ep.on('get_upload_data_finish', function (UploadData) {
  
          // 处理 UploadId 缓存
          var uuid = util.getFileUUID(Body, params.ChunkSize);
          uuid && setUploadId.call(self, uuid, UploadData.UploadId); // 缓存 UploadId
          uploadIdUsing[UploadData.UploadId] = true; // 标记 UploadId 为正在使用
          TaskId && self.on('inner-kill-task', function (data) {
              if (data.TaskId === TaskId && data.toState === 'canceled') {
                  delete uploadIdUsing[UploadData.UploadId]; // 去除 UploadId 正在使用的标记
              }
          });
  
          // 获取 UploadId
          uploadSliceList.call(self, {
              TaskId: TaskId,
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              Body: Body,
              FileSize: FileSize,
              SliceSize: ChunkSize,
              AsyncLimit: AsyncLimit,
              ServerSideEncryption: ServerSideEncryption,
              UploadData: UploadData,
              onProgress: onProgress
          }, function (err, data) {
              if (!self._isRunningTask(TaskId)) return;
              if (err) {
                  onProgress(null, true);
                  return ep.emit('error', err);
              }
              ep.emit('upload_slice_complete', data);
          });
      });
  
      // 开始获取文件 UploadId，里面会视情况计算 ETag，并比对，保证文件一致性，也优化上传
      ep.on('get_file_size_finish', function () {
  
          onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
  
          if (params.UploadData.UploadId) {
              ep.emit('get_upload_data_finish', params.UploadData);
          } else {
              var _params = util.extend({
                  TaskId: TaskId,
                  Bucket: Bucket,
                  Region: Region,
                  Key: Key,
                  Headers: params.Headers,
                  StorageClass: StorageClass,
                  Body: Body,
                  FileSize: FileSize,
                  SliceSize: ChunkSize,
                  onHashProgress: onHashProgress
              }, params);
              getUploadIdAndPartList.call(self, _params, function (err, UploadData) {
                  if (!self._isRunningTask(TaskId)) return;
                  if (err) return ep.emit('error', err);
                  params.UploadData.UploadId = UploadData.UploadId;
                  params.UploadData.PartList = UploadData.PartList;
                  ep.emit('get_upload_data_finish', params.UploadData);
              });
          }
      });
  
      // 获取上传文件大小
      FileSize = params.ContentLength;
      delete params.ContentLength;
      !params.Headers && (params.Headers = {});
      util.each(params.Headers, function (item, key) {
          if (key.toLowerCase() === 'content-length') {
              delete params.Headers[key];
          }
      });
  
      // 控制分片大小
      (function () {
          var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
          var AutoChunkSize = 1024 * 1024;
          for (var i = 0; i < SIZE.length; i++) {
              AutoChunkSize = SIZE[i] * 1024 * 1024;
              if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
          }
          params.ChunkSize = params.SliceSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
      })();
  
      // 开始上传
      if (FileSize === 0) {
          params.Body = '';
          params.ContentLength = 0;
          params.SkipTask = true;
          self.putObject(params, function (err, data) {
              if (err) {
                  return callback(err);
              }
              callback(null, data);
          });
      } else {
          ep.emit('get_file_size_finish');
      }
  }
  
  // 按照文件特征值，缓存 UploadId
  var uploadIdCache;
  var uploadIdUsing = {};
  var uploadIdCacheKey = 'cos_sdk_upload_cache';
  function initUploadId() {
      var cacheLimit = this.options.UploadIdCacheLimit;
      if (!uploadIdCache) {
          if (cacheLimit) {
              try {
                  uploadIdCache = JSON.parse(localStorage.getItem(uploadIdCacheKey)) || [];
              } catch (e) {}
          }
          if (!uploadIdCache) {
              uploadIdCache = [];
          }
      }
  }
  function setUploadId(uuid, UploadId, isDisabled) {
      initUploadId.call(this);
      for (var i = uploadIdCache.length - 1; i >= 0; i--) {
          if (uploadIdCache[i][0] === uuid && uploadIdCache[i][1] === UploadId) {
              uploadIdCache.splice(i, 1);
          }
      }
      uploadIdCache.unshift([uuid, UploadId]);
      var cacheLimit = this.options.UploadIdCacheLimit;
      if (uploadIdCache.length > cacheLimit) {
          uploadIdCache.splice(cacheLimit);
      }
      cacheLimit && setTimeout(function () {
          try {
              localStorage.setItem(uploadIdCacheKey, JSON.stringify(uploadIdCache));
          } catch (e) {}
      });
  }
  function removeUploadId(UploadId) {
      initUploadId.call(this);
      delete uploadIdUsing[UploadId];
      for (var i = uploadIdCache.length - 1; i >= 0; i--) {
          if (uploadIdCache[i][1] === UploadId) {
              uploadIdCache.splice(i, 1);
          }
      }
      var cacheLimit = this.options.UploadIdCacheLimit;
      if (uploadIdCache.length > cacheLimit) {
          uploadIdCache.splice(cacheLimit);
      }
      cacheLimit && setTimeout(function () {
          try {
              if (uploadIdCache.length) {
                  localStorage.setItem(uploadIdCacheKey, JSON.stringify(uploadIdCache));
              } else {
                  localStorage.removeItem(uploadIdCacheKey);
              }
          } catch (e) {}
      });
  }
  function getUploadId(uuid) {
      initUploadId.call(this);
      var CacheUploadIdList = [];
      for (var i = 0; i < uploadIdCache.length; i++) {
          if (uploadIdCache[i][0] === uuid) {
              CacheUploadIdList.push(uploadIdCache[i][1]);
          }
      }
      return CacheUploadIdList.length ? CacheUploadIdList : null;
  }
  
  // 获取上传任务的 UploadId
  function getUploadIdAndPartList(params, callback) {
      var TaskId = params.TaskId;
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var StorageClass = params.StorageClass;
      var self = this;
  
      // 计算 ETag
      var ETagMap = {};
      var FileSize = params.FileSize;
      var SliceSize = params.SliceSize;
      var SliceCount = Math.ceil(FileSize / SliceSize);
      var FinishSliceCount = 0;
      var FinishSize = 0;
      var onHashProgress = util.throttleOnProgress.call(self, FileSize, params.onHashProgress);
      var getChunkETag = function (PartNumber, callback) {
          var start = SliceSize * (PartNumber - 1);
          var end = Math.min(start + SliceSize, FileSize);
          var ChunkSize = end - start;
  
          if (ETagMap[PartNumber]) {
              callback(null, {
                  PartNumber: PartNumber,
                  ETag: ETagMap[PartNumber],
                  Size: ChunkSize
              });
          } else {
              util.fileSlice(params.Body, start, end, false, function (chunkItem) {
                  util.getFileMd5(chunkItem, function (err, md5) {
                      if (err) return callback(err);
                      var ETag = '"' + md5 + '"';
                      ETagMap[PartNumber] = ETag;
                      FinishSliceCount += 1;
                      FinishSize += ChunkSize;
                      callback(err, {
                          PartNumber: PartNumber,
                          ETag: ETag,
                          Size: ChunkSize
                      });
                      onHashProgress({ loaded: FinishSize, total: FileSize });
                  });
              });
          }
      };
  
      // 通过和文件的 md5 对比，判断 UploadId 是否可用
      var isAvailableUploadList = function (PartList, callback) {
          var PartCount = PartList.length;
          // 如果没有分片，通过
          if (PartCount === 0) {
              return callback(null, true);
          }
          // 检查分片数量
          if (PartCount > SliceCount) {
              return callback(null, false);
          }
          // 检查分片大小
          if (PartCount > 1) {
              var PartSliceSize = Math.max(PartList[0].Size, PartList[1].Size);
              if (PartSliceSize !== SliceSize) {
                  return callback(null, false);
              }
          }
          // 逐个分片计算并检查 ETag 是否一致
          var next = function (index) {
              if (index < PartCount) {
                  var Part = PartList[index];
                  getChunkETag(Part.PartNumber, function (err, chunk) {
                      if (chunk && chunk.ETag === Part.ETag && chunk.Size === Part.Size) {
                          next(index + 1);
                      } else {
                          callback(null, false);
                      }
                  });
              } else {
                  callback(null, true);
              }
          };
          next(0);
      };
  
      var ep = new EventProxy();
      ep.on('error', function (errData) {
          if (!self._isRunningTask(TaskId)) return;
          return callback(errData);
      });
  
      // 存在 UploadId
      ep.on('upload_id_ready', function (UploadData) {
          // 转换成 map
          var map = {};
          var list = [];
          util.each(UploadData.PartList, function (item) {
              map[item.PartNumber] = item;
          });
          for (var PartNumber = 1; PartNumber <= SliceCount; PartNumber++) {
              var item = map[PartNumber];
              if (item) {
                  item.PartNumber = PartNumber;
                  item.Uploaded = true;
              } else {
                  item = {
                      PartNumber: PartNumber,
                      ETag: null,
                      Uploaded: false
                  };
              }
              list.push(item);
          }
          UploadData.PartList = list;
          callback(null, UploadData);
      });
  
      // 不存在 UploadId, 初始化生成 UploadId
      ep.on('no_available_upload_id', function () {
          if (!self._isRunningTask(TaskId)) return;
          var _params = util.extend({
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              Headers: util.clone(params.Headers),
              StorageClass: StorageClass,
              Body: params.Body
          }, params);
          // 获取 File 或 Blob 的 type 属性，如果有，作为文件 Content-Type
          var ContentType = params.Headers['Content-Type'] || params.Body && params.Body.type;
          if (ContentType) {
              _params.Headers['Content-Type'] = ContentType;
          }
          self.multipartInit(_params, function (err, data) {
              if (!self._isRunningTask(TaskId)) return;
              if (err) return ep.emit('error', err);
              var UploadId = data.UploadId;
              if (!UploadId) {
                  return callback({ Message: 'no upload id' });
              }
              ep.emit('upload_id_ready', { UploadId: UploadId, PartList: [] });
          });
      });
  
      // 如果已存在 UploadId，找一个可以用的 UploadId
      ep.on('has_upload_id', function (UploadIdList) {
          // 串行地，找一个内容一致的 UploadId
          UploadIdList = UploadIdList.reverse();
          Async.eachLimit(UploadIdList, 1, function (UploadId, asyncCallback) {
              if (!self._isRunningTask(TaskId)) return;
              // 如果正在上传，跳过
              if (uploadIdUsing[UploadId]) {
                  asyncCallback(); // 检查下一个 UploadId
                  return;
              }
              // 判断 UploadId 是否可用
              wholeMultipartListPart.call(self, {
                  Bucket: Bucket,
                  Region: Region,
                  Key: Key,
                  UploadId: UploadId
              }, function (err, PartListData) {
                  if (!self._isRunningTask(TaskId)) return;
                  if (err) {
                      removeUploadId.call(self, UploadId);
                      return ep.emit('error', err);
                  }
                  var PartList = PartListData.PartList;
                  PartList.forEach(function (item) {
                      item.PartNumber *= 1;
                      item.Size *= 1;
                      item.ETag = item.ETag || '';
                  });
                  isAvailableUploadList(PartList, function (err, isAvailable) {
                      if (!self._isRunningTask(TaskId)) return;
                      if (err) return ep.emit('error', err);
                      if (isAvailable) {
                          asyncCallback({
                              UploadId: UploadId,
                              PartList: PartList
                          }); // 马上结束
                      } else {
                          asyncCallback(); // 检查下一个 UploadId
                      }
                  });
              });
          }, function (AvailableUploadData) {
              if (!self._isRunningTask(TaskId)) return;
              onHashProgress(null, true);
              if (AvailableUploadData && AvailableUploadData.UploadId) {
                  ep.emit('upload_id_ready', AvailableUploadData);
              } else {
                  ep.emit('no_available_upload_id');
              }
          });
      });
  
      // 在本地缓存找可用的 UploadId
      ep.on('seek_local_avail_upload_id', function (RemoteUploadIdList) {
          // 在本地找可用的 UploadId
          var uuid = util.getFileUUID(params.Body, params.ChunkSize),
              LocalUploadIdList;
          if (uuid && (LocalUploadIdList = getUploadId.call(self, uuid))) {
              var next = function (index) {
                  // 如果本地找不到可用 UploadId，再一个个遍历校验远端
                  if (index >= LocalUploadIdList.length) {
                      ep.emit('has_upload_id', RemoteUploadIdList);
                      return;
                  }
                  var UploadId = LocalUploadIdList[index];
                  // 如果不在远端 UploadId 列表里，跳过并删除
                  if (!util.isInArray(RemoteUploadIdList, UploadId)) {
                      removeUploadId.call(self, UploadId);
                      next(index + 1);
                      return;
                  }
                  // 如果正在上传，跳过
                  if (uploadIdUsing[UploadId]) {
                      next(index + 1);
                      return;
                  }
                  // 判断 UploadId 是否存在线上
                  wholeMultipartListPart.call(self, {
                      Bucket: Bucket,
                      Region: Region,
                      Key: Key,
                      UploadId: UploadId
                  }, function (err, PartListData) {
                      if (!self._isRunningTask(TaskId)) return;
                      if (err) {
                          removeUploadId.call(self, UploadId);
                          next(index + 1);
                      } else {
                          // 找到可用 UploadId
                          ep.emit('upload_id_ready', {
                              UploadId: UploadId,
                              PartList: PartListData.PartList
                          });
                      }
                  });
              };
              next(0);
          } else {
              ep.emit('has_upload_id', RemoteUploadIdList);
          }
      });
  
      // 获取线上 UploadId 列表
      ep.on('get_remote_upload_id_list', function (RemoteUploadIdList) {
          // 获取符合条件的 UploadId 列表，因为同一个文件可以有多个上传任务。
          wholeMultipartList.call(self, {
              Bucket: Bucket,
              Region: Region,
              Key: Key
          }, function (err, data) {
              if (!self._isRunningTask(TaskId)) return;
              if (err) {
                  return ep.emit('error', err);
              }
              // 整理远端 UploadId 列表
              var RemoteUploadIdList = util.filter(data.UploadList, function (item) {
                  return item.Key === Key && (!StorageClass || item.StorageClass.toUpperCase() === StorageClass.toUpperCase());
              }).reverse().map(function (item) {
                  return item.UploadId || item.UploadID;
              });
              if (RemoteUploadIdList.length) {
                  ep.emit('seek_local_avail_upload_id', RemoteUploadIdList);
              } else {
                  var uuid = util.getFileUUID(params.Body, params.ChunkSize),
                      LocalUploadIdList;
                  if (uuid && (LocalUploadIdList = getUploadId.call(self, uuid))) {
                      util.each(LocalUploadIdList, function (UploadId) {
                          removeUploadId.call(self, UploadId);
                      });
                  }
                  ep.emit('no_available_upload_id');
              }
          });
      });
  
      // 开始找可用 UploadId
      ep.emit('get_remote_upload_id_list');
  }
  
  // 获取符合条件的全部上传任务 (条件包括 Bucket, Region, Prefix)
  function wholeMultipartList(params, callback) {
      var self = this;
      var UploadList = [];
      var sendParams = {
          Bucket: params.Bucket,
          Region: params.Region,
          Prefix: params.Key
      };
      var next = function () {
          self.multipartList(sendParams, function (err, data) {
              if (err) return callback(err);
              UploadList.push.apply(UploadList, data.Upload || []);
              if (data.IsTruncated === 'true') {
                  // 列表不完整
                  sendParams.KeyMarker = data.NextKeyMarker;
                  sendParams.UploadIdMarker = data.NextUploadIdMarker;
                  next();
              } else {
                  callback(null, { UploadList: UploadList });
              }
          });
      };
      next();
  }
  
  // 获取指定上传任务的分块列表
  function wholeMultipartListPart(params, callback) {
      var self = this;
      var PartList = [];
      var sendParams = {
          Bucket: params.Bucket,
          Region: params.Region,
          Key: params.Key,
          UploadId: params.UploadId
      };
      var next = function () {
          self.multipartListPart(sendParams, function (err, data) {
              if (err) return callback(err);
              PartList.push.apply(PartList, data.Part || []);
              if (data.IsTruncated === 'true') {
                  // 列表不完整
                  sendParams.PartNumberMarker = data.NextPartNumberMarker;
                  next();
              } else {
                  callback(null, { PartList: PartList });
              }
          });
      };
      next();
  }
  
  // 上传文件分块，包括
  /*
   UploadId (上传任务编号)
   AsyncLimit (并发量)，
   SliceList (上传的分块数组)，
   FilePath (本地文件的位置)，
   SliceSize (文件分块大小)
   FileSize (文件大小)
   onProgress (上传成功之后的回调函数)
   */
  function uploadSliceList(params, cb) {
      var self = this;
      var TaskId = params.TaskId;
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var UploadData = params.UploadData;
      var FileSize = params.FileSize;
      var SliceSize = params.SliceSize;
      var ChunkParallel = Math.min(params.AsyncLimit || self.options.ChunkParallelLimit || 1, 256);
      var Body = params.Body;
      var SliceCount = Math.ceil(FileSize / SliceSize);
      var FinishSize = 0;
      var ServerSideEncryption = params.ServerSideEncryption;
      var needUploadSlices = util.filter(UploadData.PartList, function (SliceItem) {
          if (SliceItem['Uploaded']) {
              FinishSize += SliceItem['PartNumber'] >= SliceCount ? FileSize % SliceSize || SliceSize : SliceSize;
          }
          return !SliceItem['Uploaded'];
      });
      var onProgress = params.onProgress;
  
      Async.eachLimit(needUploadSlices, ChunkParallel, function (SliceItem, asyncCallback) {
          if (!self._isRunningTask(TaskId)) return;
          var PartNumber = SliceItem['PartNumber'];
          var currentSize = Math.min(FileSize, SliceItem['PartNumber'] * SliceSize) - (SliceItem['PartNumber'] - 1) * SliceSize;
          var preAddSize = 0;
          uploadSliceItem.call(self, {
              TaskId: TaskId,
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              SliceSize: SliceSize,
              FileSize: FileSize,
              PartNumber: PartNumber,
              ServerSideEncryption: ServerSideEncryption,
              Body: Body,
              UploadData: UploadData,
              onProgress: function (data) {
                  FinishSize += data.loaded - preAddSize;
                  preAddSize = data.loaded;
                  onProgress({ loaded: FinishSize, total: FileSize });
              }
          }, function (err, data) {
              if (!self._isRunningTask(TaskId)) return;
              if (util.isBrowser && !err && !data.ETag) {
                  err = 'get ETag error, please add "ETag" to CORS ExposeHeader setting.';
              }
              if (err) {
                  FinishSize -= preAddSize;
              } else {
                  FinishSize += currentSize - preAddSize;
                  SliceItem.ETag = data.ETag;
              }
              asyncCallback(err || null, data);
          });
      }, function (err) {
          if (!self._isRunningTask(TaskId)) return;
          if (err) return cb(err);
          cb(null, {
              UploadId: UploadData.UploadId,
              SliceList: UploadData.PartList
          });
      });
  }
  
  // 上传指定分片
  function uploadSliceItem(params, callback) {
      var self = this;
      var TaskId = params.TaskId;
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var FileSize = params.FileSize;
      var FileBody = params.Body;
      var PartNumber = params.PartNumber * 1;
      var SliceSize = params.SliceSize;
      var ServerSideEncryption = params.ServerSideEncryption;
      var UploadData = params.UploadData;
      var ChunkRetryTimes = self.options.ChunkRetryTimes + 1;
  
      var start = SliceSize * (PartNumber - 1);
  
      var ContentLength = SliceSize;
  
      var end = start + SliceSize;
  
      if (end > FileSize) {
          end = FileSize;
          ContentLength = end - start;
      }
  
      var PartItem = UploadData.PartList[PartNumber - 1];
      Async.retry(ChunkRetryTimes, function (tryCallback) {
          if (!self._isRunningTask(TaskId)) return;
          util.fileSlice(FileBody, start, end, true, function (Body) {
              self.multipartUpload({
                  TaskId: TaskId,
                  Bucket: Bucket,
                  Region: Region,
                  Key: Key,
                  ContentLength: ContentLength,
                  PartNumber: PartNumber,
                  UploadId: UploadData.UploadId,
                  ServerSideEncryption: ServerSideEncryption,
                  Body: Body,
                  onProgress: params.onProgress
              }, function (err, data) {
                  if (!self._isRunningTask(TaskId)) return;
                  if (err) {
                      return tryCallback(err);
                  } else {
                      PartItem.Uploaded = true;
                      return tryCallback(null, data);
                  }
              });
          });
      }, function (err, data) {
          if (!self._isRunningTask(TaskId)) return;
          return callback(err, data);
      });
  }
  
  // 完成分块上传
  function uploadSliceComplete(params, callback) {
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var UploadId = params.UploadId;
      var SliceList = params.SliceList;
      var self = this;
      var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
      var Parts = SliceList.map(function (item) {
          return {
              PartNumber: item.PartNumber,
              ETag: item.ETag
          };
      });
      // 完成上传的请求也做重试
      Async.retry(ChunkRetryTimes, function (tryCallback) {
          self.multipartComplete({
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              UploadId: UploadId,
              Parts: Parts
          }, tryCallback);
      }, function (err, data) {
          callback(err, data);
      });
  }
  
  // 抛弃分块上传任务
  /*
   AsyncLimit (抛弃上传任务的并发量)，
   UploadId (上传任务的编号，当 Level 为 task 时候需要)
   Level (抛弃分块上传任务的级别，task : 抛弃指定的上传任务，file ： 抛弃指定的文件对应的上传任务，其他值 ：抛弃指定Bucket 的全部上传任务)
   */
  function abortUploadTask(params, callback) {
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var UploadId = params.UploadId;
      var Level = params.Level || 'task';
      var AsyncLimit = params.AsyncLimit;
      var self = this;
  
      var ep = new EventProxy();
  
      ep.on('error', function (errData) {
          return callback(errData);
      });
  
      // 已经获取到需要抛弃的任务列表
      ep.on('get_abort_array', function (AbortArray) {
          abortUploadTaskArray.call(self, {
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              Headers: params.Headers,
              AsyncLimit: AsyncLimit,
              AbortArray: AbortArray
          }, function (err, data) {
              if (err) {
                  return callback(err);
              }
              callback(null, data);
          });
      });
  
      if (Level === 'bucket') {
          // Bucket 级别的任务抛弃，抛弃该 Bucket 下的全部上传任务
          wholeMultipartList.call(self, {
              Bucket: Bucket,
              Region: Region
          }, function (err, data) {
              if (err) {
                  return callback(err);
              }
              ep.emit('get_abort_array', data.UploadList || []);
          });
      } else if (Level === 'file') {
          // 文件级别的任务抛弃，抛弃该文件的全部上传任务
          if (!Key) return callback({ error: 'abort_upload_task_no_key' });
          wholeMultipartList.call(self, {
              Bucket: Bucket,
              Region: Region,
              Key: Key
          }, function (err, data) {
              if (err) {
                  return callback(err);
              }
              ep.emit('get_abort_array', data.UploadList || []);
          });
      } else if (Level === 'task') {
          // 单个任务级别的任务抛弃，抛弃指定 UploadId 的上传任务
          if (!UploadId) return callback({ error: 'abort_upload_task_no_id' });
          if (!Key) return callback({ error: 'abort_upload_task_no_key' });
          ep.emit('get_abort_array', [{
              Key: Key,
              UploadId: UploadId
          }]);
      } else {
          return callback({ error: 'abort_unknown_level' });
      }
  }
  
  // 批量抛弃分块上传任务
  function abortUploadTaskArray(params, callback) {
  
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var AbortArray = params.AbortArray;
      var AsyncLimit = params.AsyncLimit || 1;
      var self = this;
  
      var index = 0;
      var resultList = new Array(AbortArray.length);
      Async.eachLimit(AbortArray, AsyncLimit, function (AbortItem, callback) {
          var eachIndex = index;
          if (Key && Key !== AbortItem.Key) {
              resultList[eachIndex] = { error: { KeyNotMatch: true } };
              callback(null);
              return;
          }
          var UploadId = AbortItem.UploadId || AbortItem.UploadID;
  
          self.multipartAbort({
              Bucket: Bucket,
              Region: Region,
              Key: AbortItem.Key,
              Headers: params.Headers,
              UploadId: UploadId
          }, function (err, data) {
              var task = {
                  Bucket: Bucket,
                  Region: Region,
                  Key: AbortItem.Key,
                  UploadId: UploadId
              };
              resultList[eachIndex] = { error: err, task: task };
              callback(null);
          });
          index++;
      }, function (err) {
          if (err) {
              return callback(err);
          }
  
          var successList = [];
          var errorList = [];
  
          for (var i = 0, len = resultList.length; i < len; i++) {
              var item = resultList[i];
              if (item['task']) {
                  if (item['error']) {
                      errorList.push(item['task']);
                  } else {
                      successList.push(item['task']);
                  }
              }
          }
  
          return callback(null, {
              successList: successList,
              errorList: errorList
          });
      });
  }
  
  // 批量上传文件
  function uploadFiles(params, callback) {
      var self = this;
  
      // 判断多大的文件使用分片上传
      var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
  
      // 汇总返回进度
      var TotalSize = 0;
      var TotalFinish = 0;
      var onTotalProgress = util.throttleOnProgress.call(self, TotalFinish, params.onProgress);
  
      // 汇总返回回调
      var unFinishCount = params.files.length;
      var _onTotalFileFinish = params.onFileFinish;
      var resultList = Array(unFinishCount);
      var onTotalFileFinish = function (err, data, options) {
          onTotalProgress(null, true);
          _onTotalFileFinish && _onTotalFileFinish(err, data, options);
          resultList[options.Index] = {
              options: options,
              error: err,
              data: data
          };
          if (--unFinishCount <= 0 && callback) {
              callback(null, {
                  files: resultList
              });
          }
      };
  
      // 开始处理每个文件
      var taskList = [];
      util.each(params.files, function (fileParams, index) {
          (function () {
              // 对齐 nodejs 缩进
  
              var Body = fileParams.Body;
              var FileSize = Body.size || Body.length || 0;
              var fileInfo = { Index: index, TaskId: '' };
  
              // 更新文件总大小
              TotalSize += FileSize;
  
              // 整理 option，用于返回给回调
              util.each(fileParams, function (v, k) {
                  if (typeof v !== 'object' && typeof v !== 'function') {
                      fileInfo[k] = v;
                  }
              });
  
              // 处理单个文件 TaskReady
              var _onTaskReady = fileParams.onTaskReady;
              var onTaskReady = function (tid) {
                  fileInfo.TaskId = tid;
                  _onTaskReady && _onTaskReady(tid);
              };
              fileParams.onTaskReady = onTaskReady;
  
              // 处理单个文件进度
              var PreAddSize = 0;
              var _onProgress = fileParams.onProgress;
              var onProgress = function (info) {
                  TotalFinish = TotalFinish - PreAddSize + info.loaded;
                  PreAddSize = info.loaded;
                  _onProgress && _onProgress(info);
                  onTotalProgress({ loaded: TotalFinish, total: TotalSize });
              };
              fileParams.onProgress = onProgress;
  
              // 处理单个文件完成
              var _onFileFinish = fileParams.onFileFinish;
              var onFileFinish = function (err, data) {
                  _onFileFinish && _onFileFinish(err, data);
                  onTotalFileFinish && onTotalFileFinish(err, data, fileInfo);
              };
  
              // 添加上传任务
              var api = FileSize >= SliceSize ? 'sliceUploadFile' : 'putObject';
              taskList.push({
                  api: api,
                  params: fileParams,
                  callback: onFileFinish
              });
          })();
      });
      self._addTasks(taskList);
  }
  
  // 分片复制文件
  function sliceCopyFile(params, callback) {
      var ep = new EventProxy();
  
      var self = this;
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var CopySource = params.CopySource;
      var m = CopySource.match(/^([^.]+-\d+)\.cos(v6)?\.([^.]+)\.[^/]+\/(.+)$/);
      if (!m) {
          callback({ error: 'CopySource format error' });
          return;
      }
  
      var SourceBucket = m[1];
      var SourceRegion = m[3];
      var SourceKey = decodeURIComponent(m[4]);
      var CopySliceSize = params.SliceSize === undefined ? self.options.CopySliceSize : params.SliceSize;
      CopySliceSize = Math.max(0, Math.min(CopySliceSize, 5 * 1024 * 1024 * 1024));
  
      var ChunkSize = params.ChunkSize || this.options.CopyChunkSize;
      var ChunkParallel = this.options.CopyChunkParallelLimit;
  
      var FinishSize = 0;
      var FileSize;
      var onProgress;
  
      // 分片复制完成，开始 multipartComplete 操作
      ep.on('copy_slice_complete', function (UploadData) {
          self.multipartComplete({
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              UploadId: UploadData.UploadId,
              Parts: UploadData.PartList
          }, function (err, data) {
              if (err) {
                  onProgress(null, true);
                  return callback(err);
              }
              onProgress({ loaded: FileSize, total: FileSize }, true);
              callback(null, data);
          });
      });
  
      ep.on('get_copy_data_finish', function (UploadData) {
          Async.eachLimit(UploadData.PartList, ChunkParallel, function (SliceItem, asyncCallback) {
              var PartNumber = SliceItem.PartNumber;
              var CopySourceRange = SliceItem.CopySourceRange;
              var currentSize = SliceItem.end - SliceItem.start;
              var preAddSize = 0;
  
              copySliceItem.call(self, {
                  Bucket: Bucket,
                  Region: Region,
                  Key: Key,
                  CopySource: CopySource,
                  UploadId: UploadData.UploadId,
                  PartNumber: PartNumber,
                  CopySourceRange: CopySourceRange,
                  onProgress: function (data) {
                      FinishSize += data.loaded - preAddSize;
                      preAddSize = data.loaded;
                      onProgress({ loaded: FinishSize, total: FileSize });
                  }
              }, function (err, data) {
                  if (err) {
                      return asyncCallback(err);
                  }
                  onProgress({ loaded: FinishSize, total: FileSize });
  
                  FinishSize += currentSize - preAddSize;
                  SliceItem.ETag = data.ETag;
                  asyncCallback(err || null, data);
              });
          }, function (err) {
              if (err) {
                  onProgress(null, true);
                  return callback(err);
              }
  
              ep.emit('copy_slice_complete', UploadData);
          });
      });
  
      ep.on('get_file_size_finish', function (SourceHeaders) {
          // 控制分片大小
          (function () {
              var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
              var AutoChunkSize = 1024 * 1024;
              for (var i = 0; i < SIZE.length; i++) {
                  AutoChunkSize = SIZE[i] * 1024 * 1024;
                  if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
              }
              params.ChunkSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
  
              var ChunkCount = Math.ceil(FileSize / ChunkSize);
  
              var list = [];
              for (var partNumber = 1; partNumber <= ChunkCount; partNumber++) {
                  var start = (partNumber - 1) * ChunkSize;
                  var end = partNumber * ChunkSize < FileSize ? partNumber * ChunkSize - 1 : FileSize - 1;
                  var item = {
                      PartNumber: partNumber,
                      start: start,
                      end: end,
                      CopySourceRange: "bytes=" + start + "-" + end
                  };
                  list.push(item);
              }
              params.PartList = list;
          })();
  
          var TargetHeader;
          if (params.Headers['x-cos-metadata-directive'] === 'Replaced') {
              TargetHeader = params.Headers;
          } else {
              TargetHeader = SourceHeaders;
          }
          TargetHeader['x-cos-storage-class'] = params.Headers['x-cos-storage-class'] || SourceHeaders['x-cos-storage-class'];
          TargetHeader = util.clearKey(TargetHeader);
          self.multipartInit({
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              Headers: TargetHeader
          }, function (err, data) {
              if (err) {
                  return callback(err);
              }
              params.UploadId = data.UploadId;
              ep.emit('get_copy_data_finish', params);
          });
      });
  
      // 获取远端复制源文件的大小
      self.headObject({
          Bucket: SourceBucket,
          Region: SourceRegion,
          Key: SourceKey
      }, function (err, data) {
          if (err) {
              if (err.statusCode && err.statusCode === 404) {
                  callback({ ErrorStatus: SourceKey + ' Not Exist' });
              } else {
                  callback(err);
              }
              return;
          }
  
          FileSize = params.FileSize = data.headers['content-length'];
          if (FileSize === undefined || !FileSize) {
              callback({ error: 'get Content-Length error, please add "Content-Length" to CORS ExposeHeader setting.' });
              return;
          }
  
          onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
  
          // 开始上传
          if (FileSize <= CopySliceSize) {
              if (!params.Headers['x-cos-metadata-directive']) {
                  params.Headers['x-cos-metadata-directive'] = 'Copy';
              }
              self.putObjectCopy(params, function (err, data) {
                  if (err) {
                      onProgress(null, true);
                      return callback(err);
                  }
                  onProgress({ loaded: FileSize, total: FileSize }, true);
                  callback(err, data);
              });
          } else {
              var resHeaders = data.headers;
              var SourceHeaders = {
                  'Cache-Control': resHeaders['cache-control'],
                  'Content-Disposition': resHeaders['content-disposition'],
                  'Content-Encoding': resHeaders['content-encoding'],
                  'Content-Type': resHeaders['content-type'],
                  'Expires': resHeaders['expires'],
                  'x-cos-storage-class': resHeaders['x-cos-storage-class']
              };
              util.each(resHeaders, function (v, k) {
                  var metaPrefix = 'x-cos-meta-';
                  if (k.indexOf(metaPrefix) === 0 && k.length > metaPrefix.length) {
                      SourceHeaders[k] = v;
                  }
              });
              ep.emit('get_file_size_finish', SourceHeaders);
          }
      });
  }
  
  // 复制指定分片
  function copySliceItem(params, callback) {
      var TaskId = params.TaskId;
      var Bucket = params.Bucket;
      var Region = params.Region;
      var Key = params.Key;
      var CopySource = params.CopySource;
      var UploadId = params.UploadId;
      var PartNumber = params.PartNumber * 1;
      var CopySourceRange = params.CopySourceRange;
  
      var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
      var self = this;
  
      Async.retry(ChunkRetryTimes, function (tryCallback) {
          self.uploadPartCopy({
              TaskId: TaskId,
              Bucket: Bucket,
              Region: Region,
              Key: Key,
              CopySource: CopySource,
              UploadId: UploadId,
              PartNumber: PartNumber,
              CopySourceRange: CopySourceRange,
              onProgress: params.onProgress
          }, function (err, data) {
              tryCallback(err || null, data);
          });
      }, function (err, data) {
          return callback(err, data);
      });
  }
  
  var API_MAP = {
      sliceUploadFile: sliceUploadFile,
      abortUploadTask: abortUploadTask,
      uploadFiles: uploadFiles,
      sliceCopyFile: sliceCopyFile
  };
  
  module.exports.init = function (COS, task) {
      task.transferToTaskMethod(API_MAP, 'sliceUploadFile');
      util.each(API_MAP, function (fn, apiName) {
          COS.prototype[apiName] = util.apiWrapper(apiName, fn);
      });
  };
  
  /***/ }),
  /* 18 */
  /***/ (function(module, exports) {
  
  var eachLimit = function (arr, limit, iterator, callback) {
      callback = callback || function () {};
      if (!arr.length || limit <= 0) {
          return callback();
      }
  
      var completed = 0;
      var started = 0;
      var running = 0;
  
      (function replenish() {
          if (completed >= arr.length) {
              return callback();
          }
  
          while (running < limit && started < arr.length) {
              started += 1;
              running += 1;
              iterator(arr[started - 1], function (err) {
  
                  if (err) {
                      callback(err);
                      callback = function () {};
                  } else {
                      completed += 1;
                      running -= 1;
                      if (completed >= arr.length) {
                          callback();
                      } else {
                          replenish();
                      }
                  }
              });
          }
      })();
  };
  
  var retry = function (times, iterator, callback) {
      var next = function (index) {
          iterator(function (err, data) {
              if (err && index < times) {
                  next(index + 1);
              } else {
                  callback(err, data);
              }
          });
      };
      if (times < 1) {
          callback();
      } else {
          next(1);
      }
  };
  
  var async = {
      eachLimit: eachLimit,
      retry: retry
  };
  
  module.exports = async;
  
  /***/ })
  /******/ ]);
  });