text: (function(global, factory) {
  
      global["Long"] = factory();
  
  })(this, function() {
      "use strict";
  
      function Long(low, high, unsigned) {
          this.low = low | 0;
          this.high = high | 0;
          this.unsigned = !! unsigned;
      }
      Long.prototype.__isLong__;
  
      Object.defineProperty(Long.prototype, "__isLong__", {
          value: true,
          enumerable: false,
          configurable: false
      });
  
      function isLong(obj) {
          return (obj && obj["__isLong__"]) === true;
      }
      Long.isLong = isLong;
      var INT_CACHE = {};
      var UINT_CACHE = {};
  
      function fromInt(value, unsigned) {
          var obj, cachedObj, cache;
          if (unsigned) {
              value >>>= 0;
              if (cache = (0 <= value && value < 256)) {
                  cachedObj = UINT_CACHE[value];
                  if (cachedObj)
                      return cachedObj;
              }
              obj = fromBits(value, (value | 0) < 0 ? -1 : 0, true);
              if (cache)
                  UINT_CACHE[value] = obj;
              return obj;
          } else {
              value |= 0;
              if (cache = (-128 <= value && value < 128)) {
                  cachedObj = INT_CACHE[value];
                  if (cachedObj)
                      return cachedObj;
              }
              obj = fromBits(value, value < 0 ? -1 : 0, false);
              if (cache)
                  INT_CACHE[value] = obj;
              return obj;
          }
      }
      Long.fromInt = fromInt;
  
      function fromNumber(value, unsigned) {
          if (isNaN(value) || !isFinite(value))
              return unsigned ? UZERO : ZERO;
          if (unsigned) {
              if (value < 0)
                  return UZERO;
              if (value >= TWO_PWR_64_DBL)
                  return MAX_UNSIGNED_VALUE;
          } else {
              if (value <= -TWO_PWR_63_DBL)
                  return MIN_VALUE;
              if (value + 1 >= TWO_PWR_63_DBL)
                  return MAX_VALUE;
          }
          if (value < 0)
              return fromNumber(-value, unsigned).neg();
          return fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned);
      }
      Long.fromNumber = fromNumber;
  
      function fromBits(lowBits, highBits, unsigned) {
          return new Long(lowBits, highBits, unsigned);
      }
      Long.fromBits = fromBits;
      var pow_dbl = Math.pow; // Used 4 times (4*8 to 15+4)
  
      function fromString(str, unsigned, radix) {
          if (str.length === 0)
              throw Error('empty string');
          if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity")
              return ZERO;
          if (typeof unsigned === 'number') {
              // For goog.math.long compatibility
              radix = unsigned,
              unsigned = false;
          } else {
              unsigned = !! unsigned;
          }
          radix = radix || 10;
          if (radix < 2 || 36 < radix)
              throw RangeError('radix');
  
          var p;
          if ((p = str.indexOf('-')) > 0)
              throw Error('interior hyphen');
          else if (p === 0) {
              return fromString(str.substring(1), unsigned, radix).neg();
          }
          var radixToPower = fromNumber(pow_dbl(radix, 8));
  
          var result = ZERO;
          for (var i = 0; i < str.length; i += 8) {
              var size = Math.min(8, str.length - i),
                  value = parseInt(str.substring(i, i + size), radix);
              if (size < 8) {
                  var power = fromNumber(pow_dbl(radix, size));
                  result = result.mul(power).add(fromNumber(value));
              } else {
                  result = result.mul(radixToPower);
                  result = result.add(fromNumber(value));
              }
          }
          result.unsigned = unsigned;
          return result;
      }
      Long.fromString = fromString;
  
      function fromValue(val) {
          if (val /* is compatible */ instanceof Long)
              return val;
          if (typeof val === 'number')
              return fromNumber(val);
          if (typeof val === 'string')
              return fromString(val);
          // Throws for non-objects, converts non-instanceof Long:
          return fromBits(val.low, val.high, val.unsigned);
      }
      Long.fromValue = fromValue;
      var TWO_PWR_16_DBL = 1 << 16;
      var TWO_PWR_24_DBL = 1 << 24;
      var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
      var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL;
      var TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2;
      var TWO_PWR_24 = fromInt(TWO_PWR_24_DBL);
      var ZERO = fromInt(0);
      Long.ZERO = ZERO;
      var UZERO = fromInt(0, true);
      Long.UZERO = UZERO;
      var ONE = fromInt(1);
      Long.ONE = ONE;
      var UONE = fromInt(1, true);
      Long.UONE = UONE;
      var NEG_ONE = fromInt(-1);
      Long.NEG_ONE = NEG_ONE;
      var MAX_VALUE = fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0, false);
      Long.MAX_VALUE = MAX_VALUE;
      var MAX_UNSIGNED_VALUE = fromBits(0xFFFFFFFF | 0, 0xFFFFFFFF | 0, true);
      Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE;
      var MIN_VALUE = fromBits(0, 0x80000000 | 0, false);
      Long.MIN_VALUE = MIN_VALUE;
      var LongPrototype = Long.prototype;
      LongPrototype.toInt = function toInt() {
          return this.unsigned ? this.low >>> 0 : this.low;
      };
      LongPrototype.toNumber = function toNumber() {
          if (this.unsigned)
              return ((this.high >>> 0) * TWO_PWR_32_DBL) + (this.low >>> 0);
          return this.high * TWO_PWR_32_DBL + (this.low >>> 0);
      };
  
      LongPrototype.toString = function toString(radix) {
          radix = radix || 10;
          if (radix < 2 || 36 < radix)
              throw RangeError('radix');
          if (this.isZero())
              return '0';
          if (this.isNegative()) { // Unsigned Longs are never negative
              if (this.eq(MIN_VALUE)) {
                  // We need to change the Long value before it can be negated, so we remove
                  // the bottom-most digit in this base and then recurse to do the rest.
                  var radixLong = fromNumber(radix),
                      div = this.div(radixLong),
                      rem1 = div.mul(radixLong).sub(this);
                  return div.toString(radix) + rem1.toInt().toString(radix);
              } else
                  return '-' + this.neg().toString(radix);
          }
          var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned),
              rem = this;
          var result = '';
          while (true) {
              var remDiv = rem.div(radixToPower),
                  intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0,
                  digits = intval.toString(radix);
              rem = remDiv;
              if (rem.isZero())
                  return digits + result;
              else {
                  while (digits.length < 6)
                      digits = '0' + digits;
                  result = '' + digits + result;
              }
          }
      };
      LongPrototype.getHighBits = function getHighBits() {
          return this.high;
      };
      LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() {
          return this.high >>> 0;
      };
      LongPrototype.getLowBits = function getLowBits() {
          return this.low;
      };
      LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() {
          return this.low >>> 0;
      };
      LongPrototype.getNumBitsAbs = function getNumBitsAbs() {
          if (this.isNegative()) // Unsigned Longs are never negative
              return this.eq(MIN_VALUE) ? 64 : this.neg().getNumBitsAbs();
          var val = this.high != 0 ? this.high : this.low;
          for (var bit = 31; bit > 0; bit--)
              if ((val & (1 << bit)) != 0)
                  break;
          return this.high != 0 ? bit + 33 : bit + 1;
      };
      LongPrototype.isZero = function isZero() {
          return this.high === 0 && this.low === 0;
      };
      LongPrototype.isNegative = function isNegative() {
          return !this.unsigned && this.high < 0;
      };
      LongPrototype.isPositive = function isPositive() {
          return this.unsigned || this.high >= 0;
      };
      LongPrototype.isOdd = function isOdd() {
          return (this.low & 1) === 1;
      };
      LongPrototype.isEven = function isEven() {
          return (this.low & 1) === 0;
      };
      LongPrototype.equals = function equals(other) {
          if (!isLong(other))
              other = fromValue(other);
          if (this.unsigned !== other.unsigned && (this.high >>> 31) === 1 && (other.high >>> 31) === 1)
              return false;
          return this.high === other.high && this.low === other.low;
      };
      LongPrototype.eq = LongPrototype.equals;
      LongPrototype.notEquals = function notEquals(other) {
          return !this.eq( /* validates */ other);
      };
      LongPrototype.neq = LongPrototype.notEquals;
  
      /**
       * Tests if this Long's value is less than the specified's.
       * @param {!Long|number|string} other Other value
       * @returns {boolean}
       */
      LongPrototype.lessThan = function lessThan(other) {
          return this.comp( /* validates */ other) < 0;
      };
  
      /**
       * Tests if this Long's value is less than the specified's. This is an alias of {@link Long#lessThan}.
       * @function
       * @param {!Long|number|string} other Other value
       * @returns {boolean}
       */
      LongPrototype.lt = LongPrototype.lessThan;
  
      /**
       * Tests if this Long's value is less than or equal the specified's.
       * @param {!Long|number|string} other Other value
       * @returns {boolean}
       */
      LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) {
          return this.comp( /* validates */ other) <= 0;
      };
  
      /**
       * Tests if this Long's value is less than or equal the specified's. This is an alias of {@link Long#lessThanOrEqual}.
       * @function
       * @param {!Long|number|string} other Other value
       * @returns {boolean}
       */
      LongPrototype.lte = LongPrototype.lessThanOrEqual;
  
      /**
       * Tests if this Long's value is greater than the specified's.
       * @param {!Long|number|string} other Other value
       * @returns {boolean}
       */
      LongPrototype.greaterThan = function greaterThan(other) {
          return this.comp( /* validates */ other) > 0;
      };
  
      /**
       * Tests if this Long's value is greater than the specified's. This is an alias of {@link Long#greaterThan}.
       * @function
       * @param {!Long|number|string} other Other value
       * @returns {boolean}
       */
      LongPrototype.gt = LongPrototype.greaterThan;
  
      /**
       * Tests if this Long's value is greater than or equal the specified's.
       * @param {!Long|number|string} other Other value
       * @returns {boolean}
       */
      LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) {
          return this.comp( /* validates */ other) >= 0;
      };
  
      /**
       * Tests if this Long's value is greater than or equal the specified's. This is an alias of {@link Long#greaterThanOrEqual}.
       * @function
       * @param {!Long|number|string} other Other value
       * @returns {boolean}
       */
      LongPrototype.gte = LongPrototype.greaterThanOrEqual;
  
      /**
       * Compares this Long's value with the specified's.
       * @param {!Long|number|string} other Other value
       * @returns {number} 0 if they are the same, 1 if the this is greater and -1
       *  if the given one is greater
       */
      LongPrototype.compare = function compare(other) {
          if (!isLong(other))
              other = fromValue(other);
          if (this.eq(other))
              return 0;
          var thisNeg = this.isNegative(),
              otherNeg = other.isNegative();
          if (thisNeg && !otherNeg)
              return -1;
          if (!thisNeg && otherNeg)
              return 1;
          // At this point the sign bits are the same
          if (!this.unsigned)
              return this.sub(other).isNegative() ? -1 : 1;
          // Both are positive if at least one is unsigned
          return (other.high >>> 0) > (this.high >>> 0) || (other.high === this.high && (other.low >>> 0) > (this.low >>> 0)) ? -1 : 1;
      };
  
      /**
       * Compares this Long's value with the specified's. This is an alias of {@link Long#compare}.
       * @function
       * @param {!Long|number|string} other Other value
       * @returns {number} 0 if they are the same, 1 if the this is greater and -1
       *  if the given one is greater
       */
      LongPrototype.comp = LongPrototype.compare;
  
      /**
       * Negates this Long's value.
       * @returns {!Long} Negated Long
       */
      LongPrototype.negate = function negate() {
          if (!this.unsigned && this.eq(MIN_VALUE))
              return MIN_VALUE;
          return this.not().add(ONE);
      };
  
      /**
       * Negates this Long's value. This is an alias of {@link Long#negate}.
       * @function
       * @returns {!Long} Negated Long
       */
      LongPrototype.neg = LongPrototype.negate;
  
      /**
       * Returns the sum of this and the specified Long.
       * @param {!Long|number|string} addend Addend
       * @returns {!Long} Sum
       */
      LongPrototype.add = function add(addend) {
          if (!isLong(addend))
              addend = fromValue(addend);
  
          // Divide each number into 4 chunks of 16 bits, and then sum the chunks.
  
          var a48 = this.high >>> 16;
          var a32 = this.high & 0xFFFF;
          var a16 = this.low >>> 16;
          var a00 = this.low & 0xFFFF;
  
          var b48 = addend.high >>> 16;
          var b32 = addend.high & 0xFFFF;
          var b16 = addend.low >>> 16;
          var b00 = addend.low & 0xFFFF;
  
          var c48 = 0,
              c32 = 0,
              c16 = 0,
              c00 = 0;
          c00 += a00 + b00;
          c16 += c00 >>> 16;
          c00 &= 0xFFFF;
          c16 += a16 + b16;
          c32 += c16 >>> 16;
          c16 &= 0xFFFF;
          c32 += a32 + b32;
          c48 += c32 >>> 16;
          c32 &= 0xFFFF;
          c48 += a48 + b48;
          c48 &= 0xFFFF;
          return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned);
      };
  
      /**
       * Returns the difference of this and the specified Long.
       * @param {!Long|number|string} subtrahend Subtrahend
       * @returns {!Long} Difference
       */
      LongPrototype.subtract = function subtract(subtrahend) {
          if (!isLong(subtrahend))
              subtrahend = fromValue(subtrahend);
          return this.add(subtrahend.neg());
      };
  
      /**
       * Returns the difference of this and the specified Long. This is an alias of {@link Long#subtract}.
       * @function
       * @param {!Long|number|string} subtrahend Subtrahend
       * @returns {!Long} Difference
       */
      LongPrototype.sub = LongPrototype.subtract;
  
      /**
       * Returns the product of this and the specified Long.
       * @param {!Long|number|string} multiplier Multiplier
       * @returns {!Long} Product
       */
      LongPrototype.multiply = function multiply(multiplier) {
          if (this.isZero())
              return ZERO;
          if (!isLong(multiplier))
              multiplier = fromValue(multiplier);
          if (multiplier.isZero())
              return ZERO;
          if (this.eq(MIN_VALUE))
              return multiplier.isOdd() ? MIN_VALUE : ZERO;
          if (multiplier.eq(MIN_VALUE))
              return this.isOdd() ? MIN_VALUE : ZERO;
  
          if (this.isNegative()) {
              if (multiplier.isNegative())
                  return this.neg().mul(multiplier.neg());
              else
                  return this.neg().mul(multiplier).neg();
          } else if (multiplier.isNegative())
              return this.mul(multiplier.neg()).neg();
  
          // If both longs are small, use float multiplication
          if (this.lt(TWO_PWR_24) && multiplier.lt(TWO_PWR_24))
              return fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned);
  
          // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products.
          // We can skip products that would overflow.
  
          var a48 = this.high >>> 16;
          var a32 = this.high & 0xFFFF;
          var a16 = this.low >>> 16;
          var a00 = this.low & 0xFFFF;
  
          var b48 = multiplier.high >>> 16;
          var b32 = multiplier.high & 0xFFFF;
          var b16 = multiplier.low >>> 16;
          var b00 = multiplier.low & 0xFFFF;
  
          var c48 = 0,
              c32 = 0,
              c16 = 0,
              c00 = 0;
          c00 += a00 * b00;
          c16 += c00 >>> 16;
          c00 &= 0xFFFF;
          c16 += a16 * b00;
          c32 += c16 >>> 16;
          c16 &= 0xFFFF;
          c16 += a00 * b16;
          c32 += c16 >>> 16;
          c16 &= 0xFFFF;
          c32 += a32 * b00;
          c48 += c32 >>> 16;
          c32 &= 0xFFFF;
          c32 += a16 * b16;
          c48 += c32 >>> 16;
          c32 &= 0xFFFF;
          c32 += a00 * b32;
          c48 += c32 >>> 16;
          c32 &= 0xFFFF;
          c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
          c48 &= 0xFFFF;
          return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned);
      };
  
      /**
       * Returns the product of this and the specified Long. This is an alias of {@link Long#multiply}.
       * @function
       * @param {!Long|number|string} multiplier Multiplier
       * @returns {!Long} Product
       */
      LongPrototype.mul = LongPrototype.multiply;
  
      /**
       * Returns this Long divided by the specified. The result is signed if this Long is signed or
       *  unsigned if this Long is unsigned.
       * @param {!Long|number|string} divisor Divisor
       * @returns {!Long} Quotient
       */
      LongPrototype.divide = function divide(divisor) {
          if (!isLong(divisor))
              divisor = fromValue(divisor);
          if (divisor.isZero())
              throw Error('division by zero');
          if (this.isZero())
              return this.unsigned ? UZERO : ZERO;
          var approx, rem, res;
          if (!this.unsigned) {
              // This section is only relevant for signed longs and is derived from the
              // closure library as a whole.
              if (this.eq(MIN_VALUE)) {
                  if (divisor.eq(ONE) || divisor.eq(NEG_ONE))
                      return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE
                  else if (divisor.eq(MIN_VALUE))
                      return ONE;
                  else {
                      // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
                      var halfThis = this.shr(1);
                      approx = halfThis.div(divisor).shl(1);
                      if (approx.eq(ZERO)) {
                          return divisor.isNegative() ? ONE : NEG_ONE;
                      } else {
                          rem = this.sub(divisor.mul(approx));
                          res = approx.add(rem.div(divisor));
                          return res;
                      }
                  }
              } else if (divisor.eq(MIN_VALUE))
                  return this.unsigned ? UZERO : ZERO;
              if (this.isNegative()) {
                  if (divisor.isNegative())
                      return this.neg().div(divisor.neg());
                  return this.neg().div(divisor).neg();
              } else if (divisor.isNegative())
                  return this.div(divisor.neg()).neg();
              res = ZERO;
          } else {
              // The algorithm below has not been made for unsigned longs. It's therefore
              // required to take special care of the MSB prior to running it.
              if (!divisor.unsigned)
                  divisor = divisor.toUnsigned();
              if (divisor.gt(this))
                  return UZERO;
              if (divisor.gt(this.shru(1))) // 15 >>> 1 = 7 ; with divisor = 8 ; true
                  return UONE;
              res = UZERO;
          }
  
          // Repeat the following until the remainder is less than other:  find a
          // floating-point that approximates remainder / other *from below*, add this
          // into the result, and subtract it from the remainder.  It is critical that
          // the approximate value is less than or equal to the real value so that the
          // remainder never becomes negative.
          rem = this;
          while (rem.gte(divisor)) {
              // Approximate the result of division. This may be a little greater or
              // smaller than the actual value.
              approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber()));
  
              // We will tweak the approximate result by changing it in the 48-th digit or
              // the smallest non-fractional digit, whichever is larger.
              var log2 = Math.ceil(Math.log(approx) / Math.LN2),
                  delta = (log2 <= 48) ? 1 : pow_dbl(2, log2 - 48),
  
                  // Decrease the approximation until it is smaller than the remainder.  Note
                  // that if it is too large, the product overflows and is negative.
                  approxRes = fromNumber(approx),
                  approxRem = approxRes.mul(divisor);
              while (approxRem.isNegative() || approxRem.gt(rem)) {
                  approx -= delta;
                  approxRes = fromNumber(approx, this.unsigned);
                  approxRem = approxRes.mul(divisor);
              }
  
              // We know the answer can't be zero... and actually, zero would cause
              // infinite recursion since we would make no progress.
              if (approxRes.isZero())
                  approxRes = ONE;
  
              res = res.add(approxRes);
              rem = rem.sub(approxRem);
          }
          return res;
      };
  
      /**
       * Returns this Long divided by the specified. This is an alias of {@link Long#divide}.
       * @function
       * @param {!Long|number|string} divisor Divisor
       * @returns {!Long} Quotient
       */
      LongPrototype.div = LongPrototype.divide;
  
      /**
       * Returns this Long modulo the specified.
       * @param {!Long|number|string} divisor Divisor
       * @returns {!Long} Remainder
       */
      LongPrototype.modulo = function modulo(divisor) {
          if (!isLong(divisor))
              divisor = fromValue(divisor);
          return this.sub(this.div(divisor).mul(divisor));
      };
  
      /**
       * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}.
       * @function
       * @param {!Long|number|string} divisor Divisor
       * @returns {!Long} Remainder
       */
      LongPrototype.mod = LongPrototype.modulo;
  
      /**
       * Returns the bitwise NOT of this Long.
       * @returns {!Long}
       */
      LongPrototype.not = function not() {
          return fromBits(~this.low, ~this.high, this.unsigned);
      };
  
      /**
       * Returns the bitwise AND of this Long and the specified.
       * @param {!Long|number|string} other Other Long
       * @returns {!Long}
       */
      LongPrototype.and = function and(other) {
          if (!isLong(other))
              other = fromValue(other);
          return fromBits(this.low & other.low, this.high & other.high, this.unsigned);
      };
  
      /**
       * Returns the bitwise OR of this Long and the specified.
       * @param {!Long|number|string} other Other Long
       * @returns {!Long}
       */
      LongPrototype.or = function or(other) {
          if (!isLong(other))
              other = fromValue(other);
          return fromBits(this.low | other.low, this.high | other.high, this.unsigned);
      };
  
      /**
       * Returns the bitwise XOR of this Long and the given one.
       * @param {!Long|number|string} other Other Long
       * @returns {!Long}
       */
      LongPrototype.xor = function xor(other) {
          if (!isLong(other))
              other = fromValue(other);
          return fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned);
      };
  
      /**
       * Returns this Long with bits shifted to the left by the given amount.
       * @param {number|!Long} numBits Number of bits
       * @returns {!Long} Shifted Long
       */
      LongPrototype.shiftLeft = function shiftLeft(numBits) {
          if (isLong(numBits))
              numBits = numBits.toInt();
          if ((numBits &= 63) === 0)
              return this;
          else if (numBits < 32)
              return fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned);
          else
              return fromBits(0, this.low << (numBits - 32), this.unsigned);
      };
  
      /**
       * Returns this Long with bits shifted to the left by the given amount. This is an alias of {@link Long#shiftLeft}.
       * @function
       * @param {number|!Long} numBits Number of bits
       * @returns {!Long} Shifted Long
       */
      LongPrototype.shl = LongPrototype.shiftLeft;
  
      /**
       * Returns this Long with bits arithmetically shifted to the right by the given amount.
       * @param {number|!Long} numBits Number of bits
       * @returns {!Long} Shifted Long
       */
      LongPrototype.shiftRight = function shiftRight(numBits) {
          if (isLong(numBits))
              numBits = numBits.toInt();
          if ((numBits &= 63) === 0)
              return this;
          else if (numBits < 32)
              return fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned);
          else
              return fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned);
      };
  
      /**
       * Returns this Long with bits arithmetically shifted to the right by the given amount. This is an alias of {@link Long#shiftRight}.
       * @function
       * @param {number|!Long} numBits Number of bits
       * @returns {!Long} Shifted Long
       */
      LongPrototype.shr = LongPrototype.shiftRight;
  
      /**
       * Returns this Long with bits logically shifted to the right by the given amount.
       * @param {number|!Long} numBits Number of bits
       * @returns {!Long} Shifted Long
       */
      LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) {
          if (isLong(numBits))
              numBits = numBits.toInt();
          numBits &= 63;
          if (numBits === 0)
              return this;
          else {
              var high = this.high;
              if (numBits < 32) {
                  var low = this.low;
                  return fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned);
              } else if (numBits === 32)
                  return fromBits(high, 0, this.unsigned);
              else
                  return fromBits(high >>> (numBits - 32), 0, this.unsigned);
          }
      };
  
      /**
       * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}.
       * @function
       * @param {number|!Long} numBits Number of bits
       * @returns {!Long} Shifted Long
       */
      LongPrototype.shru = LongPrototype.shiftRightUnsigned;
  
      /**
       * Converts this Long to signed.
       * @returns {!Long} Signed long
       */
      LongPrototype.toSigned = function toSigned() {
          if (!this.unsigned)
              return this;
          return fromBits(this.low, this.high, false);
      };
  
      /**
       * Converts this Long to unsigned.
       * @returns {!Long} Unsigned long
       */
      LongPrototype.toUnsigned = function toUnsigned() {
          if (this.unsigned)
              return this;
          return fromBits(this.low, this.high, true);
      };
  
      /**
       * Converts this Long to its byte representation.
       * @param {boolean=} le Whether little or big endian, defaults to big endian
       * @returns {!Array.<number>} Byte representation
       */
      LongPrototype.toBytes = function(le) {
          return le ? this.toBytesLE() : this.toBytesBE();
      }
  
      /**
       * Converts this Long to its little endian byte representation.
       * @returns {!Array.<number>} Little endian byte representation
       */
      LongPrototype.toBytesLE = function() {
          var hi = this.high,
              lo = this.low;
          return [
              lo & 0xff, (lo >>> 8) & 0xff, (lo >>> 16) & 0xff, (lo >>> 24) & 0xff,
              hi & 0xff, (hi >>> 8) & 0xff, (hi >>> 16) & 0xff, (hi >>> 24) & 0xff
          ];
      }
  
      /**
       * Converts this Long to its big endian byte representation.
       * @returns {!Array.<number>} Big endian byte representation
       */
      LongPrototype.toBytesBE = function() {
          var hi = this.high,
              lo = this.low;
          return [
              (hi >>> 24) & 0xff, (hi >>> 16) & 0xff, (hi >>> 8) & 0xff,
              hi & 0xff, (lo >>> 24) & 0xff, (lo >>> 16) & 0xff, (lo >>> 8) & 0xff,
              lo & 0xff
          ];
      }
  
      return Long;
  });
  
  
  if (typeof Array.prototype.forEach != 'function') {
      Array.prototype.forEach = function(callback){
        for (var i = 0; i < this.length; i++){
          callback.apply(this, [this[i], i, this]);
        }
      };
  }
  /* webim javascript SDK
   * VER 1.7.0
   */
  
  /* webim API definitions
   */
      var msgCache = {};
  var webim = { // namespace object webim
  
      /* function init
       *   sdk登录
       * params:
       *   loginInfo      - Object, 登录身份相关参数集合，详见下面
       *   {
       *     sdkAppID     - String, 用户标识接入SDK的应用ID，必填
       *     accountType  - int, 账号类型，必填
       *     identifier   - String, 用户帐号,必须是字符串类型，必填
       *     identifierNick   - String, 用户昵称，选填
       *     userSig      - String, 鉴权Token，必须是字符串类型，必填
       *   }
       *   listeners      - Object, 事件回调函数集合, 详见下面
       *   {
       *     onConnNotify - function(connInfo), 用于收到连接状态相关通知的回调函数,目前未使用
       *     jsonpCallback -function(rspData),//IE9(含)以下浏览器用到的jsonp回调函数
       *     onMsgNotify  - function(newMsgList), 用于收到消息通知的回调函数,
       *      newMsgList为新消息数组，格式为[Msg对象]
       *      使用方有两种处理回调: 1)处理newMsgList中的增量消息,2)直接访问webim.MsgStore获取最新的消息
       *     onGroupInfoChangeNotify  - function(groupInfo), 用于监听群组资料变更的回调函数,
       *          groupInfo为新的群组资料信息
       *     onGroupSystemNotifys - Object, 用于监听（多终端同步）群系统消息的回调函数对象
       *
       *   }
       *   options        - Object, 其它选项, 目前未使用
       * return:
       *   (无)
       */
      login: function(loginInfo, listeners, options) {},
  
      /* function syncMsgs
       *   拉取最新C2C消息
       *   一般不需要使用方直接调用, SDK底层会自动同步最新消息并通知使用方, 一种有用的调用场景是用户手动触发刷新消息
       * params:
       *   cbOk   - function(msgList)类型, 当同步消息成功时的回调函数, msgList为新消息数组，格式为[Msg对象],
       *            如果此参数为null或undefined则同步消息成功后会像自动同步那样回调cbNotify
       *   cbErr  - function(err)类型, 当同步消息失败时的回调函数, err为错误对象
       * return:
       *   (无)
       */
      syncMsgs: function(cbOk, cbErr) {},
  
  
      /* function getC2CHistoryMsgs
       * 拉取C2C漫游消息
       * params:
       *   options    - 请求参数
       *   cbOk   - function(msgList)类型, 成功时的回调函数, msgList为消息数组，格式为[Msg对象],
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getC2CHistoryMsgs: function(options, cbOk, cbErr) {},
  
      /* function syncGroupMsgs
       * 拉取群漫游消息
       * params:
       *   options    - 请求参数
       *   cbOk   - function(msgList)类型, 成功时的回调函数, msgList为消息数组，格式为[Msg对象],
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      syncGroupMsgs: function(options, cbOk, cbErr) {},
  
      /* function sendMsg
       *   发送一条消息
       * params:
       *   msg    - webim.Msg类型, 要发送的消息对象
       *   cbOk   - function()类型, 当发送消息成功时的回调函数
       *   cbErr  - function(err)类型, 当发送消息失败时的回调函数, err为错误对象
       * return:
       *   (无)
       */
      sendMsg: function(msg, cbOk, cbErr) {},
  
      /* function logout
       *   sdk登出
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      logout: function(cbOk, cbErr) {},
  
      /* function setAutoRead
       * 设置会话自动已读上报标志
       * params:
       *   selSess    - webim.Session类型, 当前会话
       *   isOn   - boolean, 将selSess的自动已读消息标志改为isOn，同时是否上报当前会话已读消息
       *   isResetAll - boolean，是否重置所有会话的自动已读标志
       * return:
       *   (无)
       */
      setAutoRead: function(selSess, isOn, isResetAll) {},
  
      /* function getProfilePortrait
       *   拉取资料（搜索用户）
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getProfilePortrait: function(options, cbOk, cbErr) {},
  
      /* function setProfilePortrait
       *   设置个人资料
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      setProfilePortrait: function(options, cbOk, cbErr) {},
  
      /* function applyAddFriend
       *   申请添加好友
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      applyAddFriend: function(options, cbOk, cbErr) {},
  
      /* function getPendency
       *   拉取好友申请
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getPendency: function(options, cbOk, cbErr) {},
  
      /* function deletePendency
       *   删除好友申请
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      deletePendency: function(options, cbOk, cbErr) {},
  
      /* function responseFriend
       *   响应好友申请
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      responseFriend: function(options, cbOk, cbErr) {},
  
      /* function getAllFriend
       *   拉取我的好友
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getAllFriend: function(options, cbOk, cbErr) {},
  
      /* function deleteFriend
       *   删除好友
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      deleteFriend: function(options, cbOk, cbErr) {},
  
      /* function addBlackList
       *   增加黑名单
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      addBlackList: function(options, cbOk, cbErr) {},
  
      /* function getBlackList
       *   删除黑名单
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getBlackList: function(options, cbOk, cbErr) {},
  
      /* function deleteBlackList
       *   我的黑名单
       * params:
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      deleteBlackList: function(options, cbOk, cbErr) {},
  
      /* function uploadPic
       *   上传图片
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      uploadPic: function(options, cbOk, cbErr) {},
  
      /* function createGroup
       *   创建群
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      createGroup: function(options, cbOk, cbErr) {},
  
      /* function applyJoinGroup
       *   申请加群
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      applyJoinGroup: function(options, cbOk, cbErr) {},
  
      /* function handleApplyJoinGroup
       *   处理申请加群(同意或拒绝)
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      handleApplyJoinGroup: function(options, cbOk, cbErr) {},
  
      /* function deleteApplyJoinGroupPendency
       *   删除加群申请
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      deleteApplyJoinGroupPendency: function(options, cbOk, cbErr) {},
  
  
      /* function quitGroup
       *  主动退群
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      quitGroup: function(options, cbOk, cbErr) {},
  
      /* function getGroupPublicInfo
       *   读取群公开资料-高级接口
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getGroupPublicInfo: function(options, cbOk, cbErr) {},
  
      /* function getGroupInfo
       *   读取群详细资料-高级接口
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getGroupInfo: function(options, cbOk, cbErr) {},
  
      /* function modifyGroupBaseInfo
       *   修改群基本资料
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      modifyGroupBaseInfo: function(options, cbOk, cbErr) {},
  
      /* function destroyGroup
       *  解散群
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      destroyGroup: function(options, cbOk, cbErr) {},
  
      /* function getJoinedGroupListHigh
       *   获取我的群组-高级接口
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getJoinedGroupListHigh: function(options, cbOk, cbErr) {},
  
      /* function getGroupMemberInfo
       *   获取群组成员列表
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getGroupMemberInfo: function(options, cbOk, cbErr) {},
  
      /* function addGroupMember
       *   邀请好友加群
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      addGroupMember: function(options, cbOk, cbErr) {},
  
      /* function modifyGroupMember
       *   修改群成员资料（角色或者群消息提类型示）
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      modifyGroupMember: function(options, cbOk, cbErr) {},
  
      /* function forbidSendMsg
       *   设置群成员禁言时间
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      forbidSendMsg: function(options, cbOk, cbErr) {},
  
      /* function deleteGroupMember
       *   删除群成员
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      deleteGroupMember: function(options, cbOk, cbErr) {},
  
      /* function getPendencyGroup
       *   获取群组未决列表
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getPendencyGroup: function(options, cbOk, cbErr) {},
  
      /* function getPendencyReport
       *   好友未决已读上报
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getPendencyReport: function(options, cbOk, cbErr) {},
  
      /* function getPendencyGroupRead
       *   群组未决已读上报
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      getPendencyGroupRead: function(options, cbOk, cbErr) {},
  
      /* function sendCustomGroupNotify
       *   发送自定义群通知
       * params:
       *   options    - 请求参数，详见api文档
       *   cbOk   - function()类型, 成功时回调函数
       *   cbErr  - function(err)类型, 失败时回调函数, err为错误对象
       * return:
       *   (无)
       */
      sendCustomGroupNotify: function(options, cbOk, cbErr) {},
  
      /* class webim.Msg
       *   一条消息的描述类, 消息发送、接收的API中都会涉及此类型的对象
       * properties:
       *   sess   - Session object-ref, 消息所属的会话(e.g:我与好友A的C2C会话，我与群组G的GROUP会话)
       *   isSend - Boolean, true表示是我发出消息, false表示是发给我的消息)
       *   seq    - Integer, 消息序列号, 用于判断消息是否同一条
       *   random - Integer, 消息随机数,用于判断消息是否同一条
       *   time   - Integer, 消息时间戳, 为unix timestamp
       *   fromAccount -String,  消息发送者帐号
       *   subType -Integer,  消息子类型，c2c消息时，0-表示普通消息；群消息时，0-普通消息，1-点赞消息，2-提示消息
       *   fromAccountNick -String,  消息发送者昵称
       *   elems  - Array of webim.Msg.Elem, 描述消息内容的元素列表
       * constructor:
       *   Msg(sess, isSend, seq,random time,fromAccount) - 构造函数, 参数定义同上面properties中定义
       * methods:
       *   addText(text)  - 向elems中添加一个TEXT元素
       *   addFace(face)  - 向elems中添加一个FACE元素
       *   toHtml()       - 转成可展示的html String
       *addFace
       * sub-class webim.Msg.Elem
       *   消息中一个组成元素的描述类, 一条消息的内容被抽象描述为N个元素的有序列表
       * properties:
       *   type   - 元素类型, 目前有TEXT(文本)、FACE(表情)、IMAGE(图片)等
       *   content- 元素内容体, 当TEXT时为String, 当PIC时为UrlString
       * constructor:
       *   Elem(type, content) - 构造函数, 参数定义同上面properties中定义
       *
       * sub-class webim.Msg.Elem.TextElem
       *   文本
       * properties:
       *   text  - String 内容
       * constructor:
       *   TextElem(text) - 构造函数, 参数定义同上面properties中定义
       *
       * sub-class webim.Msg.Elem.FaceElem
       *   表情
       * properties:
       *   index  - Integer 表情索引, 用户自定义
       *   data   - String 额外数据，用户自定义
       * constructor:
       *   FaceElem(index,data) - 构造函数, 参数定义同上面properties中定义
       *
       *
       */
      Msg: function(sess, isSend, seq, random, time, fromAccount, subType, fromAccountNick) { /*Class constructor*/ },
  
      /* singleton object MsgStore
       * webim.MsgStore是消息数据的Model对象(参考MVC概念), 它提供接口访问当前存储的会话和消息数据
       * 下面说明下会话数据类型: Session
       *
       * class Session
       *   一个Session对象描述一个会话，会话可简单理解为最近会话列表的一个条目，它由两个字段唯一标识:
       *     type - String, 会话类型(如"C2C", "GROUP", ...)
       *     id   - String, 会话ID(如C2C类型中为对方帐号,"C2C"时为好友ID,"GROUP"时为群ID)
       * properties:
       *   (Session对象未对外暴露任何属性字段, 所有访问通过下面的getter方法进行)
       * methods:
       *   type()     - String, 返回会话类型,"C2C"表示与好友私聊，"GROUP"表示群聊
       *   id()       - String, 返回会话ID
       *   name()     - String, 返回会话标题(如C2C类型中为对方的昵称,暂不支持)
       *   icon()     - String, 返回会话图标(对C2C类型中为对方的头像URL，暂不支持)
       *   unread()           - Integer, 返回会话未读条数
       *   time()     - Integer, 返回会话最后活跃时间, 为unix timestamp
       *   curMaxMsgSeq() - Integer, 返回会话最大消息序列号
       *   msgCount() - Integer, 返回会话中所有消息条数
       *   msg(index) - webim.Msg, 返回会话中第index条消息
       */
      MsgStore: {
          /* function sessMap
           *   获取所有会话
           * return:
           *   所有会话对象
           */
          sessMap: function() {
              return { /*Object*/ };
          },
          /* function sessCount
           *   获取当前会话的个数
           * return:
           *   Integer, 会话个数
           */
          sessCount: function() {
              return 0;
          },
  
          /* function sessByTypeId
           *   根据会话类型和会话ID取得相应会话
           * params:
           *   type   - String, 会话类型(如"C2C", "GROUP", ...)
           *   id     - String, 会话ID(如对方ID)
           * return:
           *   Session, 会话对象(说明见上面)
           */
          sessByTypeId: function(type, id) {
              return { /*Session Object*/ };
          },
          /* function delSessByTypeId
           *   根据会话类型和会话ID删除相应会话
           * params:
           *   type   - String, 会话类型(如"C2C", "GROUP", ...)
           *   id     - String, 会话ID(如对方ID)
           * return:
           *   Boolean, 布尔类型
           */
          delSessByTypeId: function(type, id) {
              return true;
          },
  
          /* function resetCookieAndSyncFlag
           *   重置上一次读取新c2c消息Cookie和是否继续拉取标记
           * return:
           *
           */
          resetCookieAndSyncFlag: function() {},
  
          downloadMap: {}
      }
  
  };
  
  /* webim API implementation
   */
  (function(webim) {
      //sdk版本
      var SDK = {
          'VERSION': '1.7.0', //sdk版本号
          'APPID': '537048168', //web im sdk 版本 APPID
          'PLAATFORM': "10" //发送请求时判断其是来自web端的请求
      };
  
      //是否启用正式环境，默认启用
      var isAccessFormaEnvironment = true;
      // var isAccessFormaEnvironment = false;
  
      //后台接口主机
      var SRV_HOST = {
          'FORMAL': {
              'COMMON': 'https://webim.tim.qq.com',
              'PIC': 'https://pic.tim.qq.com'
          },
          'TEST': {
              'COMMON': 'https://test.tim.qq.com',
              'PIC': 'https://pic.tim.qq.com'
          }
      };
  
      //浏览器版本信息
      var BROWSER_INFO = {};
      //是否为ie9（含）以下
      var lowerBR = false;
  
      //服务名称
      var SRV_NAME = {
          'OPEN_IM': 'openim', //私聊（拉取未读c2c消息，长轮询，c2c消息已读上报等）服务名
          'GROUP': 'group_open_http_svc', //群组管理（拉取群消息，创建群，群成员管理，群消息已读上报等）服务名
          'FRIEND': 'sns', //关系链管理（好友管理，黑名单管理等）服务名
          'PROFILE': 'profile', //资料管理（查询，设置个人资料等）服务名
          'RECENT_CONTACT': 'recentcontact', //最近联系人服务名
          'PIC': 'openpic', //图片（或文件）服务名
          'BIG_GROUP': 'group_open_http_noauth_svc', //直播大群 群组管理（申请加大群）服务名
          'BIG_GROUP_LONG_POLLING': 'group_open_long_polling_http_noauth_svc', //直播大群 长轮询（拉取消息等）服务名
          'IM_OPEN_STAT': 'imopenstat', //质量上报，统计接口错误率
                  'DEL_CHAT': 'recentcontact' //删除会话
      };
  
      //不同服务对应的版本号
      var SRV_NAME_VER = {
          'openim': 'v4',
          'group_open_http_svc': 'v4',
          'sns': 'v4',
          'profile': 'v4',
          'recentcontact': 'v4',
          'openpic': 'v4',
          'group_open_http_noauth_svc': 'v1',
          'group_open_long_polling_http_noauth_svc': 'v1',
          'imopenstat': 'v4'
      };
  
      //不同的命令名对应的上报类型ID，用于接口质量上报
      var CMD_EVENT_ID_MAP = {
          'login': 1, //登录
          'pic_up': 3, //上传图片
          'apply_join_group': 9, //申请加入群组
          'create_group': 10, //创建群组
          'longpolling': 18, //普通长轮询
          'send_group_msg': 19, //群聊
          'sendmsg': 20 //私聊
      };
  
      //聊天类型
      var SESSION_TYPE = {
          'C2C': 'C2C', //私聊
          'GROUP': 'GROUP' //群聊
      };
  
      //最近联系人类型
      var RECENT_CONTACT_TYPE = {
          'C2C': 1, //好友
          'GROUP': 2 //群
      };
  
      //消息最大长度（字节）
      var MSG_MAX_LENGTH = {
          'C2C': 12000, //私聊消息
          'GROUP': 8898 //群聊
      };
  
      //后台接口返回类型
      var ACTION_STATUS = {
          'OK': 'OK', //成功
          'FAIL': 'FAIL' //失败
      };
  
      var ERROR_CODE_CUSTOM = 99999; //自定义后台接口返回错误码
  
      //消息元素类型
      var MSG_ELEMENT_TYPE = {
          'TEXT': 'TIMTextElem', //文本
          'FACE': 'TIMFaceElem', //表情
          'IMAGE': 'TIMImageElem', //图片
          'CUSTOM': 'TIMCustomElem', //自定义
          'SOUND': 'TIMSoundElem', //语音,只支持显示
          'FILE': 'TIMFileElem', //文件,只支持显示
          'LOCATION': 'TIMLocationElem', //地理位置
          'GROUP_TIP': 'TIMGroupTipElem' //群提示消息,只支持显示
      };
  
      //图片类型
      var IMAGE_TYPE = {
          'ORIGIN': 1, //原图
          'LARGE': 2, //缩略大图
          'SMALL': 3 //缩略小图
      };
              //图片格式
              var IMAGE_FORMAT = {
                  JPG: 0x1,
                  JPEG: 0x1,
                  GIF: 0x2,
                  PNG: 0x3,
                  BMP: 0x4,
                  UNKNOWN: 0xff
              };
  
      //上传资源包类型
      var UPLOAD_RES_PKG_FLAG = {
          'RAW_DATA': 0, //原始数据
          'BASE64_DATA': 1 //base64编码数据
      };
  
      //下载文件配置
      var DOWNLOAD_FILE = {
          'BUSSINESS_ID': '10001', //下载文件业务ID
          'AUTH_KEY': '617574686b6579', //下载文件authkey
          'SERVER_IP': '182.140.186.147' //下载文件服务器IP
      };
  
      //下载文件类型
      var DOWNLOAD_FILE_TYPE = {
          "SOUND": 2106, //语音
          "FILE": 2107 //普通文件
      };
  
      //上传资源类型
      var UPLOAD_RES_TYPE = {
          "IMAGE": 1, //图片
          "FILE": 2, //文件
          "SHORT_VIDEO": 3, //短视频
          "SOUND": 4 //语音，PTT
      };
  
      //版本号，用于上传图片或文件接口
      var VERSION_INFO = {
          'APP_VERSION': '2.1', //应用版本号
          'SERVER_VERSION': 1 //服务端版本号
      };
  
      //长轮询消息类型
      var LONG_POLLINNG_EVENT_TYPE = {
          "C2C": 1 //新的c2c消息通知
          ,
          "GROUP_COMMON": 3 //新的群普通消息
          ,
          "GROUP_TIP": 4 //新的群提示消息
          ,
          "GROUP_SYSTEM": 5 //新的群系统消息
          ,
          "GROUP_TIP2": 6 //新的群提示消息2
          ,
          "FRIEND_NOTICE": 7 //好友系统通知
          ,
          "PROFILE_NOTICE": 8 //资料系统通知
          ,
          "C2C_COMMON": 9 //新的C2C消息
          ,
          "C2C_EVENT": 10
      };
  
      //c2c消息子类型
      var C2C_MSG_SUB_TYPE = {
          "COMMON": 0 //普通消息
      };
      //c2c消息子类型
      var C2C_EVENT_SUB_TYPE = {
                  "READED": 92, //已读消息同步
                  "KICKEDOUT": 96
      };
  
      //群消息子类型
      var GROUP_MSG_SUB_TYPE = {
          "COMMON": 0, //普通消息
          "LOVEMSG": 1, //点赞消息
          "TIP": 2, //提示消息
          "REDPACKET": 3 //红包消息
      };
  
      //群消息优先级类型
      var GROUP_MSG_PRIORITY_TYPE = {
          "REDPACKET": 1, //红包消息
          "COMMON": 2, //普通消息
          "LOVEMSG": 3 //点赞消息
      };
  
      //群提示消息类型
      var GROUP_TIP_TYPE = {
          "JOIN": 1, //加入群组
          "QUIT": 2, //退出群组
          "KICK": 3, //被踢出群组
          "SET_ADMIN": 4, //被设置为管理员
          "CANCEL_ADMIN": 5, //被取消管理员
          "MODIFY_GROUP_INFO": 6, //修改群资料
          "MODIFY_MEMBER_INFO": 7 //修改群成员信息
      };
  
      //群提示消息-群资料变更类型
      var GROUP_TIP_MODIFY_GROUP_INFO_TYPE = {
          "FACE_URL": 1, //修改群头像URL
          "NAME": 2, //修改群名称
          "OWNER": 3, //修改群主
          "NOTIFICATION": 4, //修改群公告
          "INTRODUCTION": 5 //修改群简介
      };
  
      //群系统消息类型
      var GROUP_SYSTEM_TYPE = {
          "JOIN_GROUP_REQUEST": 1, //申请加群请求（只有管理员会收到）
          "JOIN_GROUP_ACCEPT": 2, //申请加群被同意（只有申请人能够收到）
          "JOIN_GROUP_REFUSE": 3, //申请加群被拒绝（只有申请人能够收到）
          "KICK": 4, //被管理员踢出群(只有被踢者接收到)
          "DESTORY": 5, //群被解散(全员接收)
          "CREATE": 6, //创建群(创建者接收, 不展示)
          "INVITED_JOIN_GROUP_REQUEST": 7, //邀请加群(被邀请者接收)
          "QUIT": 8, //主动退群(主动退出者接收, 不展示)
          "SET_ADMIN": 9, //设置管理员(被设置者接收)
          "CANCEL_ADMIN": 10, //取消管理员(被取消者接收)
          "REVOKE": 11, //群已被回收(全员接收, 不展示)
          "READED": 15, //群消息已读同步
          "CUSTOM": 255, //用户自定义通知(默认全员接收)
          "INVITED_JOIN_GROUP_REQUEST_AGREE": 12, //邀请加群(被邀请者需同意)
      };
  
      //好友系统通知子类型
      var FRIEND_NOTICE_TYPE = {
          "FRIEND_ADD": 1, //好友表增加
          "FRIEND_DELETE": 2, //好友表删除
          "PENDENCY_ADD": 3, //未决增加
          "PENDENCY_DELETE": 4, //未决删除
          "BLACK_LIST_ADD": 5, //黑名单增加
          "BLACK_LIST_DELETE": 6, //黑名单删除
          "PENDENCY_REPORT": 7, //未决已读上报
          "FRIEND_UPDATE": 8 //好友数据更新
      };
  
      //资料系统通知子类型
      var PROFILE_NOTICE_TYPE = {
          "PROFILE_MODIFY": 1 //资料修改
      };
  
      //腾讯登录服务错误码（用于托管模式）
      var TLS_ERROR_CODE = {
          'OK': 0, //成功
          'SIGNATURE_EXPIRATION': 11 //用户身份凭证过期
      };
  
      //长轮询连接状态
      var CONNECTION_STATUS = {
          'INIT': -1, //初始化
          'ON': 0, //连接正常
          'RECONNECT': 1, //连接恢复正常
          'OFF': 9999 //连接已断开,可能是用户网络问题，或者长轮询接口报错引起的
      };
  
      var UPLOAD_PIC_BUSSINESS_TYPE = { //图片业务类型
          'GROUP_MSG': 1, //私聊图片
          'C2C_MSG': 2, //群聊图片
          'USER_HEAD': 3, //用户头像
          'GROUP_HEAD': 4 //群头像
      };
  
      var FRIEND_WRITE_MSG_ACTION = { //好友输入消息状态
          'ING': 14, //正在输入
          'STOP': 15 //停止输入
      };
  
      //ajax默认超时时间，单位：毫秒
      var ajaxDefaultTimeOut = 15000;
  
      //大群长轮询接口返回正常时，延时一定时间再发起下一次请求
      var OK_DELAY_TIME = 1000;
  
      //大群长轮询接口发生错误时，延时一定时间再发起下一次请求
      var ERROR_DELAY_TIME = 5000;
  
      //群提示消息最多显示人数
      var GROUP_TIP_MAX_USER_COUNT = 10;
  
      //长轮询连接状态
      var curLongPollingStatus = CONNECTION_STATUS.INIT;
  
      //当长轮询连接断开后，是否已经回调过
      var longPollingOffCallbackFlag = false;
  
      //当前长轮询返回错误次数
      var curLongPollingRetErrorCount = 0;
  
      //长轮询默认超时时间，单位：毫秒
      var longPollingDefaultTimeOut = 60000;
  
      //长轮询返回错误次数达到一定值后，发起新的长轮询请求间隔时间，单位：毫秒
      var longPollingIntervalTime = 5000;
  
      //没有新消息时，长轮询返回60008错误码是正常的
      var longPollingTimeOutErrorCode = 60008;
  
      //多实例登录被kick的错误码
      var longPollingKickedErrorCode = 91101;
  
      var LongPollingId = null;
  
      //当前大群长轮询返回错误次数
      var curBigGroupLongPollingRetErrorCount = 0;
  
      //最大允许长轮询返回错误次数
      var LONG_POLLING_MAX_RET_ERROR_COUNT = 10;
  
      //上传重试累计
      var Upload_Retry_Times = 0;
      //最大上传重试
      var Upload_Retry_Max_Times = 20;
  
      //ie7/8/9采用jsonp方法解决ajax跨域限制
      var jsonpRequestId = 0; //jsonp请求id
      //最新jsonp请求返回的json数据
      var jsonpLastRspData = null;
      //兼容ie7/8/9,jsonp回调函数
      var jsonpCallback = null;
  
      var uploadResultIframeId = 0; //用于上传图片的iframe id
  
      var ipList = []; //文件下载地址
      var authkey = null; //文件下载票据
      var expireTime = null; //文件下载票据超时时间
  
      //错误码
      var ERROR = {};
      //当前登录用户
      var ctx = {
          sdkAppID: null,
          appIDAt3rd: null,
          accountType: null,
          identifier: null,
          tinyid: null,
          identifierNick: null,
          userSig: null,
          a2: null,
          contentType: 'json',
          apn: 1
      };
      var opt = {};
      var xmlHttpObjSeq = 0; //ajax请求id
      var xmlHttpObjMap = {}; //发起的ajax请求
      var curSeq = 0; //消息seq
      var tempC2CMsgList = []; //新c2c消息临时缓存
      var tempC2CHistoryMsgList = []; //漫游c2c消息临时缓存
  
      var maxApiReportItemCount = 20; //一次最多上报条数
      var apiReportItems = []; //暂存api接口质量上报数据
  
      var Resources = {
          downloadMap: {}
      };
  
      //表情标识字符和索引映射关系对象，用户可以自定义
      var emotionDataIndexs = {
          "[惊讶]": 0,
          "[撇嘴]": 1,
          "[色]": 2,
          "[发呆]": 3,
          "[得意]": 4,
          "[流泪]": 5,
          "[害羞]": 6,
          "[闭嘴]": 7,
          "[睡]": 8,
          "[大哭]": 9,
          "[尴尬]": 10,
          "[发怒]": 11,
          "[调皮]": 12,
          "[龇牙]": 13,
          "[微笑]": 14,
          "[难过]": 15,
          "[酷]": 16,
          "[冷汗]": 17,
          "[抓狂]": 18,
          "[吐]": 19,
          "[偷笑]": 20,
          "[可爱]": 21,
          "[白眼]": 22,
          "[傲慢]": 23,
          "[饿]": 24,
          "[困]": 25,
          "[惊恐]": 26,
          "[流汗]": 27,
          "[憨笑]": 28,
          "[大兵]": 29,
          "[奋斗]": 30,
          "[咒骂]": 31,
          "[疑问]": 32,
          "[嘘]": 33,
          "[晕]": 34
      };
  
              //表情对象，用户可以自定义
              var emotions = {};
              //工具类
              var tool = new function() {
  
              //格式化时间戳
              //format格式如下：
              //yyyy-MM-dd hh:mm:ss 年月日时分秒(默认格式)
              //yyyy-MM-dd 年月日
              //hh:mm:ss 时分秒
              this.formatTimeStamp = function(timestamp, format) {
                  if (!timestamp) {
                      return 0;
                  }
                  var formatTime;
                  format = format || 'yyyy-MM-dd hh:mm:ss';
                  var date = new Date(timestamp * 1000);
                  var o = {
                      "M+": date.getMonth() + 1, //月份
                      "d+": date.getDate(), //日
                      "h+": date.getHours(), //小时
                      "m+": date.getMinutes(), //分
                      "s+": date.getSeconds() //秒
                  };
                  if (/(y+)/.test(format)) {
                      formatTime = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
                  } else {
                      formatTime = format;
                  }
                  for (var k in o) {
                      if (new RegExp("(" + k + ")").test(formatTime))
                          formatTime = formatTime.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
                  }
                  return formatTime;
              };
  
              //根据群类型英文名转换成中文名
              this.groupTypeEn2Ch = function(type_en) {
                  var type_ch = null;
                  switch (type_en) {
                      case 'Public':
                          type_ch = '公开群';
                          break;
                      case 'ChatRoom':
                          type_ch = '聊天室';
                          break;
                      case 'Private':
                          type_ch = '私有群'; //即讨论组
                          break;
                      case 'AVChatRoom':
                          type_ch = '直播聊天室';
                          break;
                      default:
                          type_ch = type_en;
                          break;
                  }
                  return type_ch;
              };
              //根据群类型中文名转换成英文名
              this.groupTypeCh2En = function(type_ch) {
                  var type_en = null;
                  switch (type_ch) {
                      case '公开群':
                          type_en = 'Public';
                          break;
                      case '聊天室':
                          type_en = 'ChatRoom';
                          break;
                      case '私有群': //即讨论组
                          type_en = 'Private';
                          break;
                      case '直播聊天室':
                          type_en = 'AVChatRoom';
                          break;
                      default:
                          type_en = type_ch;
                          break;
                  }
                  return type_en;
              };
              //根据群身份英文名转换成群身份中文名
              this.groupRoleEn2Ch = function(role_en) {
                  var role_ch = null;
                  switch (role_en) {
                      case 'Member':
                          role_ch = '成员';
                          break;
                      case 'Admin':
                          role_ch = '管理员';
                          break;
                      case 'Owner':
                          role_ch = '群主';
                          break;
                      default:
                          role_ch = role_en;
                          break;
                  }
                  return role_ch;
              };
              //根据群身份中文名转换成群身份英文名
              this.groupRoleCh2En = function(role_ch) {
                  var role_en = null;
                  switch (role_ch) {
                      case '成员':
                          role_en = 'Member';
                          break;
                      case '管理员':
                          role_en = 'Admin';
                          break;
                      case '群主':
                          role_en = 'Owner';
                          break;
                      default:
                          role_en = role_ch;
                          break;
                  }
                  return role_en;
              };
              //根据群消息提示类型英文转换中文
              this.groupMsgFlagEn2Ch = function(msg_flag_en) {
                  var msg_flag_ch = null;
                  switch (msg_flag_en) {
                      case 'AcceptAndNotify':
                          msg_flag_ch = '接收并提示';
                          break;
                      case 'AcceptNotNotify':
                          msg_flag_ch = '接收不提示';
                          break;
                      case 'Discard':
                          msg_flag_ch = '屏蔽';
                          break;
                      default:
                          msg_flag_ch = msg_flag_en;
                          break;
                  }
                  return msg_flag_ch;
              };
              //根据群消息提示类型中文名转换英文名
              this.groupMsgFlagCh2En = function(msg_flag_ch) {
                  var msg_flag_en = null;
                  switch (msg_flag_ch) {
                      case '接收并提示':
                          msg_flag_en = 'AcceptAndNotify';
                          break;
                      case '接收不提示':
                          msg_flag_en = 'AcceptNotNotify';
                          break;
                      case '屏蔽':
                          msg_flag_en = 'Discard';
                          break;
                      default:
                          msg_flag_en = msg_flag_ch;
                          break;
                  }
                  return msg_flag_en;
              };
              //将空格和换行符转换成HTML标签
              this.formatText2Html = function(text) {
                  var html = text;
                  if (html) {
                      html = this.xssFilter(html); //用户昵称或群名称等字段会出现脚本字符串
                      html = html.replace(/ /g, "&nbsp;");
                      html = html.replace(/\n/g, "<br/>");
                  }
                  return html;
              };
              //将HTML标签转换成空格和换行符
              this.formatHtml2Text = function(html) {
                  var text = html;
                  if (text) {
                      text = text.replace(/&nbsp;/g, " ");
                      text = text.replace(/<br\/>/g, "\n");
                  }
                  return text;
              };
              //获取字符串(UTF-8编码)所占字节数
              //参考：http://zh.wikipedia.org/zh-cn/UTF-8
              this.getStrBytes = function(str) {
                  if (str == null || str === undefined) return 0;
                  if (typeof str != "string") {
                      return 0;
                  }
                  var total = 0,
                      charCode, i, len;
                  for (i = 0, len = str.length; i < len; i++) {
                      charCode = str.charCodeAt(i);
                      if (charCode <= 0x007f) {
                          total += 1; //字符代码在000000 – 00007F之间的，用一个字节编码
                      } else if (charCode <= 0x07ff) {
                          total += 2; //000080 – 0007FF之间的字符用两个字节
                      } else if (charCode <= 0xffff) {
                          total += 3; //000800 – 00D7FF 和 00E000 – 00FFFF之间的用三个字节，注: Unicode在范围 D800-DFFF 中不存在任何字符
                      } else {
                          total += 4; //010000 – 10FFFF之间的用4个字节
                      }
                  }
                  return total;
              };
  
  
              //防止XSS攻击
              this.xssFilter = function(val) {
                  val = val.toString();
                  val = val.replace(/[<]/g, "&lt;");
                  val = val.replace(/[>]/g, "&gt;");
                  val = val.replace(/"/g, "&quot;");
                  //val = val.replace(/'/g, "&#039;");
                  return val;
              };
  
              //去掉头尾空白符
              this.trimStr = function(str) {
                  if (!str) return '';
                  str = str.toString();
                  return str.replace(/(^\s*)|(\s*$)/g, "");
              };
              //判断是否为8位整数
              this.validNumber = function(str) {
                  str = str.toString();
                  return str.match(/(^\d{1,8}$)/g);
              };
              this.getReturnError = function(errorInfo, errorCode) {
                  if (!errorCode) {
                      errorCode = -100;
                  }
                  var error = {
                      'ActionStatus': ACTION_STATUS.FAIL,
                      'ErrorCode': errorCode,
                      'ErrorInfo': errorInfo + "[" + errorCode + "]"
                  };
                  return error;
              };
              //设置cookie
              //name 名字
              //value 值
              //expires 有效期(单位：秒)
              //path
              //domain 作用域
              this.setCookie = function(name, value, expires, path, domain) {
                  var exp = new Date();
                  exp.setTime(exp.getTime() + expires * 1000);
                  document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
              };
              //获取cookie
              this.getCookie = function(name) {
                  var result = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
                  if (result != null) {
                      return unescape(result[2]);
                  }
                  return null;
              };
              //删除cookie
              this.delCookie = function(name) {
                  var exp = new Date();
                  exp.setTime(exp.getTime() - 1);
                  var value = this.getCookie(name);
                  if (value != null)
                      document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
              };
              //根据名字获取url参数值
              this.getQueryString = function(name) {
                  var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
                  var r = window.location.search.substr(1).match(reg);
                  if (r != null) return unescape(r[2]);
                  return null;
              };
              //判断IE版本号，ver表示版本号
              this.isIE = function(ver) {
                  var b = document.createElement('b')
                  b.innerHTML = '<!--[if IE ' + ver + ']><i></i><![endif]-->'
                  return b.getElementsByTagName('i').length === 1;
              };
              //判断浏览器版本
              this.getBrowserInfo = function() {
                  var Sys = {};
                  var ua = navigator.userAgent.toLowerCase();
                  log.info('navigator.userAgent=' + ua);
                  var s;
                  (s = ua.match(/msie ([\d.]+)/)) ? Sys.ie = s[1] :
                      (s = ua.match(/firefox\/([\d.]+)/)) ? Sys.firefox = s[1] :
                      (s = ua.match(/chrome\/([\d.]+)/)) ? Sys.chrome = s[1] :
                      (s = ua.match(/opera.([\d.]+)/)) ? Sys.opera = s[1] :
                      (s = ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = s[1] : 0;
                  if (Sys.ie) { //Js判断为IE浏览器
                      //ie10的判断这里有个问题
                      // Mozilla/5.0 (compatible; MSIE 9.0; qdesk 2.5.1277.202; Windows NT 6.1; WOW64; Trident/6.0)
                      // 是IE10 而不是IE9
                      if (ua.match(/trident\/(\d)\./) && ua.match(/trident\/(\d)\./)[1] == 6) {
                          Sys.ie = 10
                      }
                      return {
                          'type': 'ie',
                          'ver': Sys.ie
                      };
                  }
                  if (Sys.firefox) { //Js判断为火狐(firefox)浏览器
                      return {
                          'type': 'firefox',
                          'ver': Sys.firefox
                      };
                  }
                  if (Sys.chrome) { //Js判断为谷歌chrome浏览器
                      return {
                          'type': 'chrome',
                          'ver': Sys.chrome
                      };
                  }
                  if (Sys.opera) { //Js判断为opera浏览器
                      return {
                          'type': 'opera',
                          'ver': Sys.opera
                      };
                  }
                  if (Sys.safari) { //Js判断为苹果safari浏览器
                      return {
                          'type': 'safari',
                          'ver': Sys.safari
                      };
                  }
                  return {
                      'type': 'unknow',
                      'ver': -1
                  };
              };
  
  
                 
              this.replaceObject = function(keyMap, json ){
                  for(var a in json){
                      if( keyMap[a] ){
                          json[keyMap[a]] = json[a]
                          delete json[a]
                          if( json[keyMap[a]] instanceof Array ){
                              var len = json[keyMap[a]].length
                              for(var i = 0; i < len; i++){
                                  json[keyMap[a]][i] = this.replaceObject(keyMap, json[keyMap[a]][i])
                              }
                          }else if( typeof json[keyMap[a]] === 'object'){
                              json[keyMap[a]] = this.replaceObject(keyMap, json[keyMap[a]])
                          }
                      }
                  }
                  return json;
              }
  
          };
  
      //日志对象
      var log = new function() {
  
              var on = true;
  
              this.setOn = function(onFlag) {
                  on = onFlag;
              };
  
              this.getOn = function() {
                  return on;
              };
  
              this.error = function(logStr) {
                  try {
                      on && console.error(logStr);
                  } catch (e) {}
              };
              this.warn = function(logStr) {
                  try {
                      on && console.warn(logStr);
                  } catch (e) {}
              };
              this.info = function(logStr) {
                  try {
                      on && console.info(logStr);
                  } catch (e) {}
              };
              this.debug = function(logStr) {
                  try {
                      on && console.debug(logStr);
                  } catch (e) {}
              };
          };
      //获取unix时间戳
      var unixtime = function(d) {
          if (!d) d = new Date();
          return Math.round(d.getTime() / 1000);
      };
      //时间戳转日期
      var fromunixtime = function(t) {
          return new Date(t * 1000);
      };
      //获取下一个消息序号
      var nextSeq = function() {
          if (curSeq) {
              curSeq = curSeq + 1;
          } else {
              curSeq = Math.round(Math.random() * 10000000);
          }
          return curSeq;
      };
      //产生随机数
      var createRandom = function() {
          return Math.round(Math.random() * 4294967296);
      };
  
      //获取ajax请求对象
      var getXmlHttp = function() {
          var xmlhttp = null;
          if (window.XMLHttpRequest) {
              xmlhttp = new XMLHttpRequest();
          } else {
              try {
                  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
              } catch (e) {
                  try {
                      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                  } catch (e) {
                      return null;
                  }
              }
          }
          return xmlhttp;
      }
      //发起ajax请求
      var ajaxRequest = function(meth, url, req, timeout, content_type, isLongPolling, cbOk, cbErr) {
  
          var xmlHttpObj = getXmlHttp();
  
          var error, errInfo;
          if (!xmlHttpObj) {
              errInfo = "创建请求失败";
              var error = tool.getReturnError(errInfo, -1);
              log.error(errInfo);
              if (cbErr) cbErr(error);
              return;
          }
          //保存ajax请求对象
          xmlHttpObjSeq++;
          xmlHttpObjMap[xmlHttpObjSeq] = xmlHttpObj;
  
          xmlHttpObj.open(meth, url, true);
          xmlHttpObj.onreadystatechange = function() {
              if (xmlHttpObj.readyState == 4) {
                  xmlHttpObjMap[xmlHttpObjSeq] = null; //清空
                  if (xmlHttpObj.status == 200) {
                      if (cbOk) cbOk(xmlHttpObj.responseText);
                      xmlHttpObj = null;
                      curLongPollingRetErrorCount = curBigGroupLongPollingRetErrorCount = 0;
                  } else {
                      xmlHttpObj = null;
                      //避免刷新的时候，由于abord ajax引起的错误回调
                      setTimeout(function() {
                          var errInfo = "请求服务器失败,请检查你的网络是否正常";
                          var error = tool.getReturnError(errInfo, -2);
                          //if (!isLongPolling && cbErr) cbErr(error);
                          if (cbErr) cbErr(error);
                      }, 16);
                  }
              }
          };
          xmlHttpObj.setRequestHeader('Content-Type', content_type );
          //设置超时时间
          if (!timeout) {
              timeout = ajaxDefaultTimeOut; //设置ajax默认超时时间
          }
          if (timeout) {
              xmlHttpObj.timeout = timeout;
              xmlHttpObj.ontimeout = function(event) {
                  xmlHttpObj = null;
                  //var errInfo = "请求服务器超时";
                  //var error = tool.getReturnError(errInfo, -3);
                  //if (cbErr) cbErr(error);
              };
          }
          //
          if (xmlHttpObj.overrideMimeType) {
              xmlHttpObj.overrideMimeType("application/json");
          }
  
          xmlHttpObj.send(req);
      }
      //发起ajax请求（json格式数据）
      var ajaxRequestJson = function(meth, url, req, timeout, content_type, isLongPolling, cbOk, cbErr) {
          ajaxRequest(meth, url, JSON.stringify(req), timeout, content_type, isLongPolling, function(resp) {
              var json = null;
              if (resp) json = JSON.parse(resp); //将返回的json字符串转换成json对象
              if (cbOk) cbOk(json);
          }, cbErr);
      }
      //判断用户是否已登录
      var isLogin = function() {
          return ctx.sdkAppID && ctx.identifier;
      };
      //检查是否登录
      var checkLogin = function(cbErr, isNeedCallBack) {
          if (!isLogin()) {
              if (isNeedCallBack) {
                  var errInfo = "请登录";
                  var error = tool.getReturnError(errInfo, -4);
  
                  if (cbErr) cbErr(error);
              }
              return false;
          }
          return true;
      };
  
      //检查是否访问正式环境
      var isAccessFormalEnv = function() {
          return isAccessFormaEnvironment;
      };
  
      //根据不同的服务名和命令，获取对应的接口地址
      var getApiUrl = function(srvName, cmd, cbOk, cbErr) {
          var srvHost = SRV_HOST;
          if (isAccessFormalEnv()) {
              srvHost = SRV_HOST.FORMAL.COMMON;
          } else {
              srvHost = SRV_HOST.TEST.COMMON;
          }
  
          //if (srvName == SRV_NAME.RECENT_CONTACT) {
          //    srvHost = SRV_HOST.TEST.COMMON;
          //}
  
          if (srvName == SRV_NAME.PIC) {
              if (isAccessFormalEnv()) {
                  srvHost = SRV_HOST.FORMAL.PIC;
              } else {
                  srvHost = SRV_HOST.TEST.PIC;
              }
          }
  
          var url = srvHost + '/' + SRV_NAME_VER[srvName] + '/' + srvName + '/' + cmd + '?websdkappid=' + SDK.APPID + "&v=" + SDK.VERSION + "&platform=" + SDK.PLAATFORM;;
  
          if (isLogin()) {
              if (cmd == 'login') {
                  url += '&identifier=' + encodeURIComponent(ctx.identifier) + '&usersig=' + ctx.userSig;
              } else {
                  if (ctx.tinyid && ctx.a2) {
                      url += '&tinyid=' + ctx.tinyid + '&a2=' + ctx.a2;
                  } else {
                      if (cbErr) {
                          log.error("tinyid或a2为空[" + srvName + "][" + cmd + "]");
                          cbErr(tool.getReturnError("tinyid或a2为空[" + srvName + "][" + cmd + "]", -5));
                          return false;
                      }
                  }
              }
              url += '&contenttype=' + ctx.contentType;
          }
          url += '&sdkappid=' + ctx.sdkAppID + '&accounttype=' + ctx.accountType + '&apn=' + ctx.apn + '&reqtime=' + unixtime();
          return url;
      };
  
      //获取语音下载url
      var getSoundDownUrl = function(uuid, senderId) {
          var soundUrl = null;
          if (authkey && ipList[0]) {
              soundUrl = "http://" + ipList[0] + "/asn.com/stddownload_common_file?authkey=" + authkey + "&bid=" + DOWNLOAD_FILE.BUSSINESS_ID + "&subbid=" + ctx.sdkAppID + "&fileid=" + uuid + "&filetype=" + DOWNLOAD_FILE_TYPE.SOUND + "&openid=" + senderId + "&ver=0";
          } else {
              log.error("拼接语音下载url不报错：ip或者authkey为空");
          }
          return soundUrl;
      };
  
      //获取文件下载地址
      var getFileDownUrl = function(uuid, senderId, fileName) {
          var fileUrl = null;
          if (authkey && ipList[0]) {
              fileUrl = "http://" + ipList[0] + "/asn.com/stddownload_common_file?authkey=" + authkey + "&bid=" + DOWNLOAD_FILE.BUSSINESS_ID + "&subbid=" + ctx.sdkAppID + "&fileid=" + uuid + "&filetype=" + DOWNLOAD_FILE_TYPE.FILE + "&openid=" + senderId + "&ver=0&filename=" + encodeURIComponent(fileName);
          } else {
              log.error("拼接文件下载url不报错：ip或者authkey为空");
          }
          Resources.downloadMap["uuid_" + uuid] = fileUrl;
          return fileUrl;
      };
  
      //获取文件下载地址
      var getFileDownUrlV2 = function(uuid, senderId, fileName, downFlag, receiverId, busiId, type) {
          var options = {
              "From_Account": senderId, //"identifer_0",       // 类型: String, 发送者tinyid
              "To_Account": receiverId, //"identifer_1",         // 类型: String, 接收者tinyid
              "os_platform": 10, // 类型: Number, 终端的类型 1(android) 2(ios) 3(windows) 10(others...)
              "Timestamp": unixtime().toString(), // 类型: Number, 时间戳
              "Random": createRandom().toString(), // 类型: Number, 随机值
              "request_info": [ // 类型: Array
                  {
                      "busi_id": busiId, // 类型: Number, 群(1) C2C(2) 其他请联系sdk开发者分配
                      "download_flag": downFlag, // 类型: Number, 申请下载地址标识  0(申请架平下载地址)  1(申请COS平台下载地址)  2(不需要申请, 直接拿url下载(这里应该不会为2))
                      "type": type, // 类型: Number, 0(短视频缩略图), 1(文件), 2(短视频), 3(ptt), 其他待分配
                      "uuid": uuid, // 类型: Number, 唯一标识一个文件的uuid
                      "version": VERSION_INFO.SERVER_VERSION, // 类型: Number, 架平server版本
                      "auth_key": authkey, // 类型: String, 认证签名
                      "ip": ipList[0] // 类型: Number, 架平IP
                  }
              ]
          };
          //获取下载地址
          proto_applyDownload(options, function(resp) {
              if (resp.error_code == 0 && resp.response_info) {
                  Resources.downloadMap["uuid_" + options.uuid] = resp.response_info.url;
              }
              if (onAppliedDownloadUrl) {
                  onAppliedDownloadUrl({
                      uuid: options.uuid,
                      url: resp.response_info.url,
                      maps: Resources.downloadMap
                  });
              }
          }, function(resp) {
              log.error("获取下载地址失败", options.uuid)
          });
      };
  
  
      //重置ajax请求
      var clearXmlHttpObjMap = function() {
          //遍历xmlHttpObjMap{}
          for (var seq in xmlHttpObjMap) {
              var xmlHttpObj = xmlHttpObjMap[seq];
              if (xmlHttpObj) {
                  xmlHttpObj.abort(); //中断ajax请求(长轮询)
                  xmlHttpObjMap[xmlHttpObjSeq] = null; //清空
              }
          }
          xmlHttpObjSeq = 0;
          xmlHttpObjMap = {};
      };
  
      //重置sdk全局变量
      var clearSdk = function() {
  
          clearXmlHttpObjMap();
  
          //当前登录用户
          ctx = {
              sdkAppID: null,
              appIDAt3rd: null,
              accountType: null,
              identifier: null,
              identifierNick: null,
              userSig: null,
              contentType: 'json',
              apn: 1
          };
          opt = {};
  
          curSeq = 0;
  
          //ie8,9采用jsonp方法解决ajax跨域限制
          jsonpRequestId = 0; //jsonp请求id
          //最新jsonp请求返回的json数据
          jsonpLastRspData = null;
  
          apiReportItems = [];
  
          MsgManager.clear();
          MsgStore.clear();
  
          //重置longpollingId
          LongPollingId = null;
      };
  
      //登录
      var _login = function(loginInfo, listeners, options, cbOk, cbErr) {
  
          clearSdk();
  
          if (options) opt = options;
          if (opt.isAccessFormalEnv == false) {
              log.error("请切换为正式环境！！！！");
              isAccessFormaEnvironment = opt.isAccessFormalEnv;
          }
          if (opt.isLogOn == false) {
              log.setOn(opt.isLogOn);
          }
          /*
           if(opt.emotions){
           emotions=opt.emotions;
           webim.Emotions= emotions;
           }
           if(opt.emotionDataIndexs){
           emotionDataIndexs=opt.emotionDataIndexs;
           webim.EmotionDataIndexs= emotionDataIndexs;
           }*/
  
          if (!loginInfo) {
              if (cbErr) {
                  cbErr(tool.getReturnError("loginInfo is empty", -6));
                  return;
              }
          }
          if (!loginInfo.sdkAppID) {
              if (cbErr) {
                  cbErr(tool.getReturnError("loginInfo.sdkAppID is empty", -7));
                  return;
              }
          }
          if (!loginInfo.accountType) {
              if (cbErr) {
                  cbErr(tool.getReturnError("loginInfo.accountType is empty", -8));
                  return;
              }
          }
  
          if (loginInfo.identifier) {
              ctx.identifier = loginInfo.identifier.toString();
          }
          if (loginInfo.identifier && !loginInfo.userSig) {
              if (cbErr) {
                  cbErr(tool.getReturnError("loginInfo.userSig is empty", -9));
                  return;
              }
          }
          if (loginInfo.userSig) {
              ctx.userSig = loginInfo.userSig.toString();
          }
          ctx.sdkAppID = loginInfo.sdkAppID;
          ctx.accountType = loginInfo.accountType;
  
          if (ctx.identifier && ctx.userSig) { //带登录态
              //登录
              proto_login(
                  function(identifierNick, headurl) {
                      MsgManager.init(
                          listeners,
                          function(mmInitResp) {
                              if (cbOk) {
                                  mmInitResp.identifierNick = identifierNick;
                                  mmInitResp.headurl = headurl;
                                  cbOk(mmInitResp);
                              }
                          }, cbErr
                      );
                  },
                  cbErr
              );
          } else { //不带登录态，进入直播场景sdk
              MsgManager.init(
                  listeners,
                  cbOk,
                  cbErr
              );
          }
      };
  
      //初始化浏览器信息
      var initBrowserInfo = function() {
          //初始化浏览器类型
          BROWSER_INFO = tool.getBrowserInfo();
          log.info('BROWSER_INFO: type=' + BROWSER_INFO.type + ', ver=' + BROWSER_INFO.ver);
          if (BROWSER_INFO.type == "ie") {
              if (parseInt(BROWSER_INFO.ver) < 10) {
                  lowerBR = true;
              }
          }
      };
  
      //接口质量上报
      var reportApiQuality = function(cmd, errorCode, errorInfo) {
          if (cmd == 'longpolling' && (errorCode == longPollingTimeOutErrorCode || errorCode == longPollingKickedErrorCode)) { //longpolling 返回60008错误可以视为正常,可以不上报
              return;
          }
          var eventId = CMD_EVENT_ID_MAP[cmd];
          if (eventId) {
              var reportTime = unixtime();
              var uniqKey = null;
              var msgCmdErrorCode = {
                  'Code': errorCode,
                  'ErrMsg': errorInfo
              };
              if (ctx.a2) {
                  uniqKey = ctx.a2.substring(0, 10) + "_" + reportTime + "_" + createRandom();
              } else if (ctx.userSig) {
                  uniqKey = ctx.userSig.substring(0, 10) + "_" + reportTime + "_" + createRandom();
              }
  
              if (uniqKey) {
  
                  var rptEvtItem = {
                      "UniqKey": uniqKey,
                      "EventId": eventId,
                      "ReportTime": reportTime,
                      "MsgCmdErrorCode": msgCmdErrorCode
                  };
  
                  if (cmd == 'login') {
                      var loginApiReportItems = [];
                      loginApiReportItems.push(rptEvtItem);
                      var loginReportOpt = {
                          "EvtItems": loginApiReportItems,
                          "MainVersion": SDK.VERSION,
                          "Version": "0"
                      };
                      proto_reportApiQuality(loginReportOpt,
                          function(resp) {
                              loginApiReportItems = null; //
                          },
                          function(err) {
                              loginApiReportItems = null; //
                          }
                      );
                  } else {
                      apiReportItems.push(rptEvtItem);
                      if (apiReportItems.length >= maxApiReportItemCount) { //累计一定条数再上报
                          var reportOpt = {
                              "EvtItems": apiReportItems,
                              "MainVersion": SDK.VERSION,
                              "Version": "0"
                          };
                          proto_reportApiQuality(reportOpt,
                              function(resp) {
                                  apiReportItems = []; //清空
                              },
                              function(err) {
                                  apiReportItems = []; //清空
                              }
                          );
                      }
                  }
  
              }
          }
      };
  
      // REST API calls
      //上线
      var proto_login = function(cbOk, cbErr) {
          ConnManager.apiCall(SRV_NAME.OPEN_IM, "login", {
                  "State": "Online"
              },
              function(loginResp) {
                  if (loginResp.TinyId) {
                      ctx.tinyid = loginResp.TinyId;
                  } else {
                      if (cbErr) {
                          cbErr(tool.getReturnError("TinyId is empty", -10));
                          return;
                      }
                  }
                  if (loginResp.A2Key) {
                      ctx.a2 = loginResp.A2Key;
                  } else {
                      if (cbErr) {
                          cbErr(tool.getReturnError("A2Key is empty", -11));
                          return;
                      }
                  }
                  var tag_list = [
                      "Tag_Profile_IM_Nick",
                      "Tag_Profile_IM_Image"
                  ];
                  var options = {
                      'From_Account': ctx.identifier,
                      'To_Account': [ctx.identifier],
                      'LastStandardSequence': 0,
                      'TagList': tag_list
                  };
                  proto_getProfilePortrait(
                      options,
                      function(resp) {
                                      var nick, image;
                          if (resp.UserProfileItem && resp.UserProfileItem.length > 0) {
                              for (var i in resp.UserProfileItem) {
                                  for (var j in resp.UserProfileItem[i].ProfileItem) {
                                      switch (resp.UserProfileItem[i].ProfileItem[j].Tag) {
                                          case 'Tag_Profile_IM_Nick':
                                              nick = resp.UserProfileItem[i].ProfileItem[j].Value;
                                              if (nick) ctx.identifierNick = nick;
                                              break;
                                          case 'Tag_Profile_IM_Image':
                                              image = resp.UserProfileItem[i].ProfileItem[j].Value;
                                              if (image) ctx.headurl = image;
                                              break;
                                      }
                                  }
                              }
                          }
                          if (cbOk) cbOk(ctx.identifierNick, ctx.headurl); //回传当前用户昵称
                      }, cbErr);
              }, cbErr);
      };
      //下线
      var proto_logout = function(type, cbOk, cbErr) {
          if (!checkLogin(cbErr, false)) { //不带登录态
              clearSdk();
              if (cbOk) cbOk({
                  'ActionStatus': ACTION_STATUS.OK,
                  'ErrorCode': 0,
                  'ErrorInfo': 'logout success'
              });
              return;
          }
          if (type == "all") {
              ConnManager.apiCall(SRV_NAME.OPEN_IM, "logout", {},
                  function(resp) {
                      clearSdk();
                      if (cbOk) cbOk(resp);
                  },
                  cbErr);
          } else {
              ConnManager.apiCall(SRV_NAME.OPEN_IM, "longpollinglogout", {
                      LongPollingId: LongPollingId
                  },
                  function(resp) {
                      clearSdk();
                      if (cbOk) cbOk(resp);
                  },
                  cbErr);
          }
      };
      //发送消息，包括私聊和群聊
      var proto_sendMsg = function(msg, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          var msgInfo = null;
  
          switch (msg.sess.type()) {
              case SESSION_TYPE.C2C:
                  msgInfo = {
                      'From_Account': ctx.identifier,
                      'To_Account': msg.sess.id().toString(),
                      'MsgTimeStamp': msg.time,
                      'MsgSeq': msg.seq,
                      'MsgRandom': msg.random,
                      'MsgBody': []
                  };
                  break;
              case SESSION_TYPE.GROUP:
                  var subType = msg.getSubType();
                  msgInfo = {
                      'GroupId': msg.sess.id().toString(),
                      'From_Account': ctx.identifier,
                      'Random': msg.random,
                      'MsgBody': []
                  };
                  switch (subType) {
                      case GROUP_MSG_SUB_TYPE.COMMON:
                          msgInfo.MsgPriority = "COMMON";
                          break;
                      case GROUP_MSG_SUB_TYPE.REDPACKET:
                          msgInfo.MsgPriority = "REDPACKET";
                          break;
                      case GROUP_MSG_SUB_TYPE.LOVEMSG:
                          msgInfo.MsgPriority = "LOVEMSG";
                          break;
                      case GROUP_MSG_SUB_TYPE.TIP:
                          log.error("不能主动发送群提示消息,subType=" + subType);
                          break;
                      default:
                          log.error("发送群消息时，出现未知子消息类型：subType=" + subType);
                          return;
                          break;
                  }
                  break;
              default:
                  break;
          }
  
          for (var i in msg.elems) {
              var elem = msg.elems[i];
              var msgContent = null;
              var msgType = elem.type;
              switch (msgType) {
                  case MSG_ELEMENT_TYPE.TEXT: //文本
                      msgContent = {
                          'Text': elem.content.text
                      };
                      break;
                  case MSG_ELEMENT_TYPE.FACE: //表情
                      msgContent = {
                          'Index': elem.content.index,
                          'Data': elem.content.data
                      };
                      break;
                  case MSG_ELEMENT_TYPE.IMAGE: //图片
                      var ImageInfoArray = [];
                      for (var j in elem.content.ImageInfoArray) {
                          ImageInfoArray.push({
                              'Type': elem.content.ImageInfoArray[j].type,
                              'Size': elem.content.ImageInfoArray[j].size,
                              'Width': elem.content.ImageInfoArray[j].width,
                              'Height': elem.content.ImageInfoArray[j].height,
                              'URL': elem.content.ImageInfoArray[j].url
                          });
                      }
                      msgContent = {
                                      'ImageFormat': elem.content.ImageFormat,
                          'UUID': elem.content.UUID,
                          'ImageInfoArray': ImageInfoArray
                      };
                      break;
                  case MSG_ELEMENT_TYPE.SOUND: //
                      log.warn('web端暂不支持发送语音消息');
                      continue;
                      break;
                  case MSG_ELEMENT_TYPE.LOCATION: //
                      log.warn('web端暂不支持发送地理位置消息');
                      continue;
                      break;
                  case MSG_ELEMENT_TYPE.FILE: //
                      msgContent = {
                          'UUID': elem.content.uuid,
                          'FileName': elem.content.name,
                          'FileSize': elem.content.size,
                          'DownloadFlag': elem.content.downFlag
                      };
                      break;
                  case MSG_ELEMENT_TYPE.CUSTOM: //
                      msgContent = {
                          'Data': elem.content.data,
                          'Desc': elem.content.desc,
                          'Ext': elem.content.ext
                      };
                      msgType = MSG_ELEMENT_TYPE.CUSTOM;
                      break;
                  default:
                      log.warn('web端暂不支持发送' + elem.type + '消息');
                      continue;
                      break;
              }
  
              if (msg.PushInfoBoolean) {
                  msgInfo.OfflinePushInfo = msg.PushInfo; //当android终端进程被杀掉时才走push，IOS退到后台即可
              }
  
              msgInfo.MsgBody.push({
                  'MsgType': msgType,
                  'MsgContent': msgContent
              });
          }
          if (msg.sess.type() == SESSION_TYPE.C2C) { //私聊
              ConnManager.apiCall(SRV_NAME.OPEN_IM, "sendmsg", msgInfo, cbOk, cbErr);
          } else if (msg.sess.type() == SESSION_TYPE.GROUP) { //群聊
              ConnManager.apiCall(SRV_NAME.GROUP, "send_group_msg", msgInfo, cbOk, cbErr);
          }
      };
      //长轮询接口
      var proto_longPolling = function(options, cbOk, cbErr) {
          if (!isAccessFormaEnvironment && typeof stopPolling != "undefined" && stopPolling == true) {
              return;
          }
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.OPEN_IM, "longpolling", options, cbOk, cbErr, longPollingDefaultTimeOut, true);
      };
  
      //长轮询接口(拉取直播聊天室新消息)
      var proto_bigGroupLongPolling = function(options, cbOk, cbErr, timeout) {
          ConnManager.apiCall(SRV_NAME.BIG_GROUP_LONG_POLLING, "get_msg", options, cbOk, cbErr, timeout);
      };
  
      //拉取未读c2c消息接口
      var proto_getMsgs = function(cookie, syncFlag, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.OPEN_IM, "getmsg", {
                  'Cookie': cookie,
                  'SyncFlag': syncFlag
              },
              function(resp) {
  
                  if (resp.MsgList && resp.MsgList.length) {
                      for (var i in resp.MsgList) {
                          tempC2CMsgList.push(resp.MsgList[i]);
                      }
                  }
                  if (resp.SyncFlag == 1) {
                      proto_getMsgs(resp.Cookie, resp.SyncFlag, cbOk, cbErr);
                  } else {
                      resp.MsgList = tempC2CMsgList;
                      tempC2CMsgList = [];
                      if (cbOk) cbOk(resp);
                  }
              },
              cbErr);
      };
      //C2C消息已读上报接口
      var proto_c2CMsgReaded = function(cookie, c2CMsgReadedItem, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          var tmpC2CMsgReadedItem = [];
          for (var i in c2CMsgReadedItem) {
              var item = {
                  'To_Account': c2CMsgReadedItem[i].toAccount,
                  'LastedMsgTime': c2CMsgReadedItem[i].lastedMsgTime
              };
              tmpC2CMsgReadedItem.push(item);
          }
          ConnManager.apiCall(SRV_NAME.OPEN_IM, "msgreaded", {
              C2CMsgReaded: {
                  'Cookie': cookie,
                  'C2CMsgReadedItem': tmpC2CMsgReadedItem
              }
          }, cbOk, cbErr);
      };
  
      //删除c2c消息
      var proto_deleteC2CMsg = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.OPEN_IM, "deletemsg", options,
              cbOk, cbErr);
      };
  
      //拉取c2c历史消息接口
      var proto_getC2CHistoryMsgs = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.OPEN_IM, "getroammsg", options,
              function(resp) {
                  var reqMsgCount = options.MaxCnt;
                  var complete = resp.Complete;
                  var rspMsgCount = resp.MaxCnt;
                  var msgKey = resp.MsgKey;
                  var lastMsgTime = resp.LastMsgTime;
  
                  if (resp.MsgList && resp.MsgList.length) {
                      for (var i in resp.MsgList) {
                          tempC2CHistoryMsgList.push(resp.MsgList[i]);
                      }
                  }
                  var netxOptions = null;
                  if (complete == 0) { //还有历史消息可拉取
                      if (rspMsgCount < reqMsgCount) {
                          netxOptions = {
                              'Peer_Account': options.Peer_Account,
                              'MaxCnt': reqMsgCount - rspMsgCount,
                              'LastMsgTime': lastMsgTime,
                              'MsgKey': msgKey
                          };
                      }
                  }
  
                  if (netxOptions) { //继续拉取
                      proto_getC2CHistoryMsgs(netxOptions, cbOk, cbErr);
                  } else {
                      resp.MsgList = tempC2CHistoryMsgList;
                      tempC2CHistoryMsgList = [];
                      if (cbOk) cbOk(resp);
                  }
              },
              cbErr);
      };
  
      //群组接口
      //创建群组
      //协议参考：https://www.qcloud.com/doc/product/269/1615
      var proto_createGroup = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          var opt = {
              //必填    群组形态，包括Public（公开群），Private（私有群），ChatRoom（聊天室），AVChatRoom（互动直播聊天室）。
              'Type': options.Type,
              //必填    群名称，最长30字节。
              'Name': options.Name
          };
          var member_list = [];
  
          //Array 选填  初始群成员列表，最多500个。成员信息字段详情参见：群成员资料。
          for (var i = 0; i < options.MemberList.length; i++) {
              member_list.push({
                  'Member_Account': options.MemberList[i]
              })
          }
          opt.MemberList = member_list;
          //选填    为了使得群组ID更加简单，便于记忆传播，腾讯云支持APP在通过REST API创建群组时自定义群组ID。详情参见：自定义群组ID。
          if (options.GroupId) {
              opt.GroupId = options.GroupId;
          }
          //选填    群主id，自动添加到群成员中。如果不填，群没有群主。
          if (options.Owner_Account) {
              opt.Owner_Account = options.Owner_Account;
          }
          //选填    群简介，最长240字节。
          if (options.Introduction) {
              opt.Introduction = options.Introduction;
          }
          //选填    群公告，最长300字节。
          if (options.Notification) {
              opt.Notification = options.Notification;
          }
          //选填    最大群成员数量，最大为10000，不填默认为2000个。
          if (options.MaxMemberCount) {
              opt.MaxMemberCount = options.MaxMemberCount;
          }
          //选填    申请加群处理方式。包含FreeAccess（自由加入），NeedPermission（需要验证），DisableApply（禁止加群），不填默认为NeedPermission（需要验证）。
          if (options.ApplyJoinOption) { //
              opt.ApplyJoinOption = options.ApplyJoinOption;
          }
          //Array 选填  群组维度的自定义字段，默认情况是没有的，需要开通，详情参见：自定义字段。
          if (options.AppDefinedData) {
              opt.AppDefinedData = options.AppDefinedData;
          }
          //选填    群头像URL，最长100字节。
          if (options.FaceUrl) {
              opt.FaceUrl = options.FaceUrl;
          }
          ConnManager.apiCall(SRV_NAME.GROUP, "create_group", opt,
              cbOk, cbErr);
      };
  
      //创建群组-高级接口
      //协议参考：https://www.qcloud.com/doc/product/269/1615
      var proto_createGroupHigh = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.GROUP, "create_group", options,
              cbOk, cbErr);
      };
  
      //修改群组基本资料
      //协议参考：https://www.qcloud.com/doc/product/269/1620
      var proto_modifyGroupBaseInfo = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "modify_group_base_info", options,
              cbOk, cbErr);
      };
  
      //申请加群
      var proto_applyJoinGroup = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          options.GroupId = String(options.GroupId)
          ConnManager.apiCall(SRV_NAME.GROUP, "apply_join_group", {
                  'GroupId': options.GroupId,
                  'ApplyMsg': options.ApplyMsg,
                  'UserDefinedField': options.UserDefinedField
              },
              cbOk, cbErr);
      };
  
      //申请加入大群
      var BigGroupId;
      var proto_applyJoinBigGroup = function(options, cbOk, cbErr) {
          options.GroupId = String(options.GroupId)
          BigGroupId = options.GroupId;
          var srvName;
          if (!checkLogin(cbErr, false)) { //未登录
              srvName = SRV_NAME.BIG_GROUP;
          } else { //已登录
              srvName = SRV_NAME.GROUP;
          }
          ConnManager.apiCall(srvName, "apply_join_group", {
                  'GroupId': options.GroupId,
                  'ApplyMsg': options.ApplyMsg,
                  'UserDefinedField': options.UserDefinedField
              },
              function(resp) {
                  if (resp.JoinedStatus && resp.JoinedStatus == 'JoinedSuccess') {
                      if (resp.LongPollingKey) {
                          MsgManager.setBigGroupLongPollingOn(true); //开启长轮询
                          MsgManager.setBigGroupLongPollingKey(resp.LongPollingKey); //更新大群长轮询key
                          MsgManager.setBigGroupLongPollingMsgMap(options.GroupId, 0); //收到的群消息置0
                          MsgManager.bigGroupLongPolling(); //开启长轮询
                      } else { //没有返回LongPollingKey，说明申请加的群不是直播聊天室(AVChatRoom)
                          cbErr && cbErr(tool.getReturnError("Join Group succeed; But the type of group is not AVChatRoom: groupid=" + options.GroupId, -12));
                          return;
                      }
                  }
                  if (cbOk) cbOk(resp);
              }, function(err) {
  
                  if (cbErr) cbErr(err);
              });
      };
  
      //处理加群申请(同意或拒绝)
      var proto_handleApplyJoinGroupPendency = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "handle_apply_join_group", {
                  'GroupId': options.GroupId,
                  'Applicant_Account': options.Applicant_Account,
                  'HandleMsg': options.HandleMsg,
                  'Authentication': options.Authentication,
                  'MsgKey': options.MsgKey,
                  'ApprovalMsg': options.ApprovalMsg,
                  'UserDefinedField': options.UserDefinedField
              },
              cbOk,
              function(err) {
                  if (err.ErrorCode == 10024) { //apply has be handled
                      if (cbOk) {
                          var resp = {
                              'ActionStatus': ACTION_STATUS.OK,
                              'ErrorCode': 0,
                              'ErrorInfo': '该申请已经被处理过'
                          };
                          cbOk(resp);
                      }
                  } else {
                      if (cbErr) cbErr(err);
                  }
              }
          );
      };
  
      //获取群组未决列表
      var proto_getPendencyGroup = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "get_pendency", {
                  'StartTime': options.StartTime,
                  'Limit': options.Limit,
                  'Handle_Account': ctx.identifier
              },
              cbOk,
              function(err) {
  
              }
          );
      };
  
  
      //群组未决已经上报
      var proto_getPendencyGroupRead = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "report_pendency", {
                  'ReportTime': options.ReportTime,
                  'From_Account': ctx.identifier
              },
              cbOk,
              function(err) {
  
              }
          );
      };
  
      //处理被邀请进群申请(同意或拒绝)
      var proto_handleInviteJoinGroupRequest = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "handle_invite_join_group", {
                  'GroupId': options.GroupId,
                  'Inviter_Account': options.Inviter_Account,
                  'HandleMsg': options.HandleMsg,
                  'Authentication': options.Authentication,
                  'MsgKey': options.MsgKey,
                  'ApprovalMsg': options.ApprovalMsg,
                  'UserDefinedField': options.UserDefinedField
              },
              cbOk,
              function(err) {
  
              }
          );
      };
  
      //主动退群
      var proto_quitGroup = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "quit_group", {
                  'GroupId': options.GroupId
              },
              cbOk, cbErr);
      };
  
      //退出大群
      var proto_quitBigGroup = function(options, cbOk, cbErr) {
          var srvName;
          if (!checkLogin(cbErr, false)) { //未登录
              srvName = SRV_NAME.BIG_GROUP;
          } else { //已登录
              srvName = SRV_NAME.GROUP;
          }
                      MsgManager.resetBigGroupLongPollingInfo();
          ConnManager.apiCall(srvName, "quit_group", {
                  'GroupId': options.GroupId
              },
              function(resp) {
                  
                  MsgStore.delSessByTypeId(SESSION_TYPE.GROUP, options.GroupId);  
                  //重置当前再请求中的ajax
                  //clearXmlHttpObjMap();
                  //退出大群成功之后需要重置长轮询信息
                              // MsgManager.resetBigGroupLongPollingInfo();
                  if (cbOk) cbOk(resp);
              },
              cbErr);
      };
      //查找群(按名称)
      var proto_searchGroupByName = function(options, cbOk, cbErr) {
          ConnManager.apiCall(SRV_NAME.GROUP, "search_group", options, cbOk, cbErr);
      };
  
      //获取群组公开资料
      var proto_getGroupPublicInfo = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "get_group_public_info", {
                  'GroupIdList': options.GroupIdList,
                  'ResponseFilter': {
                      'GroupBasePublicInfoFilter': options.GroupBasePublicInfoFilter
                  }
              },
              function(resp) {
                  resp.ErrorInfo = '';
                  if (resp.GroupInfo) {
                      for (var i in resp.GroupInfo) {
                          var errorCode = resp.GroupInfo[i].ErrorCode;
                          if (errorCode > 0) {
                              resp.ActionStatus = ACTION_STATUS.FAIL;
                              resp.GroupInfo[i].ErrorInfo = "[" + errorCode + "]" + resp.GroupInfo[i].ErrorInfo;
                              resp.ErrorInfo += resp.GroupInfo[i].ErrorInfo + '\n';
                          }
                      }
                  }
                  if (resp.ActionStatus == ACTION_STATUS.FAIL) {
                      if (cbErr) {
                          cbErr(resp);
                      }
                  } else if (cbOk) {
                      cbOk(resp);
                  }
  
              },
              cbErr);
      };
  
      //获取群组详细资料--高级
      //请求协议参考：https://www.qcloud.com/doc/product/269/1616
      var proto_getGroupInfo = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          var opt = {
              'GroupIdList': options.GroupIdList,
              'ResponseFilter': {
                  'GroupBaseInfoFilter': options.GroupBaseInfoFilter,
                  'MemberInfoFilter': options.MemberInfoFilter
              }
          };
          if (options.AppDefinedDataFilter_Group) {
              opt.ResponseFilter.AppDefinedDataFilter_Group = options.AppDefinedDataFilter_Group;
          }
          if (options.AppDefinedDataFilter_GroupMember) {
              opt.ResponseFilter.AppDefinedDataFilter_GroupMember = options.AppDefinedDataFilter_GroupMember;
          }
          ConnManager.apiCall(SRV_NAME.GROUP, "get_group_info", opt,
              cbOk, cbErr);
      };
  
      //获取群组成员-高级接口
      //协议参考：https://www.qcloud.com/doc/product/269/1617
      var proto_getGroupMemberInfo = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "get_group_member_info", {
                  'GroupId': options.GroupId,
                  'Offset': options.Offset,
                  'Limit': options.Limit,
                  'MemberInfoFilter': options.MemberInfoFilter,
                  'MemberRoleFilter': options.MemberRoleFilter,
                  'AppDefinedDataFilter_GroupMember': options.AppDefinedDataFilter_GroupMember
              },
              cbOk, cbErr);
      };
  
  
      //增加群组成员
      //协议参考：https://www.qcloud.com/doc/product/269/1621
      var proto_addGroupMember = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "add_group_member", {
                  'GroupId': options.GroupId,
                  'Silence': options.Silence,
                  'MemberList': options.MemberList
              },
              cbOk, cbErr);
      };
      //修改群组成员资料
      //协议参考：https://www.qcloud.com/doc/product/269/1623
      var proto_modifyGroupMember = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          var opt = {};
          if (options.GroupId) {
              opt.GroupId = options.GroupId;
          }
          if (options.Member_Account) {
              opt.Member_Account = options.Member_Account;
          }
          //Admin或者Member
          if (options.Role) {
              opt.Role = options.Role;
          }
          // AcceptAndNotify代表解收并提示消息，Discard代表不接收也不提示消息，AcceptNotNotify代表接收消息但不提示
          if (options.MsgFlag) {
              opt.MsgFlag = options.MsgFlag;
          }
          if (options.ShutUpTime) { //禁言时间
              opt.ShutUpTime = options.ShutUpTime;
          }
          if (options.NameCard) { //群名片,最大不超过50个字节
              opt.NameCard = options.NameCard;
          }
          if (options.AppMemberDefinedData) { //群成员维度的自定义字段，默认情况是没有的，需要开通
              opt.AppMemberDefinedData = options.AppMemberDefinedData;
          }
          ConnManager.apiCall(SRV_NAME.GROUP, "modify_group_member_info", opt,
              cbOk, cbErr);
      };
      //删除群组成员
      //协议参考：https://www.qcloud.com/doc/product/269/1622
      var proto_deleteGroupMember = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "delete_group_member", {
                  'GroupId': options.GroupId,
                  'Silence': options.Silence,
                  'MemberToDel_Account': options.MemberToDel_Account,
                  'Reason': options.Reason
              },
              cbOk, cbErr);
      };
      //解散群组
      //协议参考：https://www.qcloud.com/doc/product/269/1624
      var proto_destroyGroup = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "destroy_group", {
                  'GroupId': options.GroupId
              },
              cbOk, cbErr);
      };
      //转让群组
      //协议参考：https://www.qcloud.com/doc/product/269/1633
      var proto_changeGroupOwner = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.GROUP, "change_group_owner", options, cbOk, cbErr);
      };
      //获取用户所加入的群组-高级接口
      //协议参考：https://www.qcloud.com/doc/product/269/1625
      var proto_getJoinedGroupListHigh = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "get_joined_group_list", {
                  'Member_Account': options.Member_Account,
                  'Limit': options.Limit,
                  'Offset': options.Offset,
                  'GroupType': options.GroupType,
                  'ResponseFilter': {
                      'GroupBaseInfoFilter': options.GroupBaseInfoFilter,
                      'SelfInfoFilter': options.SelfInfoFilter
                  }
              },
              cbOk, cbErr);
      };
      //查询一组UserId在群中的身份
      //协议参考：https://www.qcloud.com/doc/product/269/1626
      var proto_getRoleInGroup = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "get_role_in_group", {
                  'GroupId': options.GroupId,
                  'User_Account': options.User_Account
              },
              cbOk, cbErr);
      };
      //设置取消成员禁言时间
      //协议参考：https://www.qcloud.com/doc/product/269/1627
      var proto_forbidSendMsg = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
  
          ConnManager.apiCall(SRV_NAME.GROUP, "forbid_send_msg", {
                  'GroupId': options.GroupId,
                  'Members_Account': options.Members_Account,
                  'ShutUpTime': options.ShutUpTime //单位为秒，为0时表示取消禁言
              },
              cbOk, cbErr);
      };
  
      //发送自定义群系统通知
      var proto_sendCustomGroupNotify = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.GROUP, "send_group_system_notification", options,
              cbOk, cbErr);
      };
  
      //拉取群消息接口
      var proto_getGroupMsgs = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.GROUP, "group_msg_get", {
                  "GroupId": options.GroupId,
                  "ReqMsgSeq": options.ReqMsgSeq,
                  "ReqMsgNumber": options.ReqMsgNumber
              },
              cbOk, cbErr);
      };
      //群消息已读上报接口
      var proto_groupMsgReaded = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.GROUP, "msg_read_report", {
                  'GroupId': options.GroupId,
                  'MsgReadedSeq': options.MsgReadedSeq
              },
              cbOk, cbErr);
      };
      //end
  
      //好友接口
      //处理好友接口返回的错误码
      var convertErrorEn2ZhFriend = function(resp) {
          var errorAccount = [];
          if (resp.Fail_Account && resp.Fail_Account.length) {
              errorAccount = resp.Fail_Account;
          }
          if (resp.Invalid_Account && resp.Invalid_Account.length) {
              for (var k in resp.Invalid_Account) {
                  errorAccount.push(resp.Invalid_Account[k]);
              }
          }
          if (errorAccount.length) {
              resp.ActionStatus = ACTION_STATUS.FAIL;
              resp.ErrorCode = ERROR_CODE_CUSTOM;
              resp.ErrorInfo = '';
              for (var i in errorAccount) {
                  var failCount = errorAccount[i];
                  for (var j in resp.ResultItem) {
                      if (resp.ResultItem[j].To_Account == failCount) {
  
                          var resultCode = resp.ResultItem[j].ResultCode;
                          resp.ResultItem[j].ResultInfo = "[" + resultCode + "]" + resp.ResultItem[j].ResultInfo;
                          resp.ErrorInfo += resp.ResultItem[j].ResultInfo + "\n";
                          break;
                      }
                  }
              }
          }
          return resp;
      };
      //添加好友
      var proto_applyAddFriend = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "friend_add", {
                  'From_Account': ctx.identifier,
                  'AddFriendItem': options.AddFriendItem
              },
              function(resp) {
                  var newResp = convertErrorEn2ZhFriend(resp);
                  if (newResp.ActionStatus == ACTION_STATUS.FAIL) {
                      if (cbErr) cbErr(newResp);
                  } else if (cbOk) {
                      cbOk(newResp);
                  }
              }, cbErr);
      };
      //删除好友
      var proto_deleteFriend = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "friend_delete", {
                  'From_Account': ctx.identifier,
                  'To_Account': options.To_Account,
                  'DeleteType': options.DeleteType
              },
              function(resp) {
                  var newResp = convertErrorEn2ZhFriend(resp);
                  if (newResp.ActionStatus == ACTION_STATUS.FAIL) {
                      if (cbErr) cbErr(newResp);
                  } else if (cbOk) {
                      cbOk(newResp);
                  }
              }, cbErr);
      };
                  //删除会话
                  var proto_deleteChat = function(options, cbOk, cbErr) {
                      if (!checkLogin(cbErr, true)) return;
  
                      if (options.chatType == 1) {
                          ConnManager.apiCall(SRV_NAME.DEL_CHAT, "delete", {
                                  'From_Account': ctx.identifier,
                                  'Type': options.chatType,
                                  'To_Account': options.To_Account
                              },
                              cbOk, cbErr);
                      } else {
                          ConnManager.apiCall(SRV_NAME.DEL_CHAT, "delete", {
                                  'From_Account': ctx.identifier,
                                  'Type': options.chatType,
                                  'ToGroupid': options.To_Account
                              },
                              cbOk, cbErr);
  
                      }
  
                  };
      //获取好友申请
      var proto_getPendency = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "pendency_get", {
                  "From_Account": ctx.identifier,
                  "PendencyType": options.PendencyType,
                  "StartTime": options.StartTime,
                  "MaxLimited": options.MaxLimited,
                  "LastSequence": options.LastSequence
              },
              cbOk, cbErr);
      };
      //好友申请已读上报
      var proto_getPendencyReport = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "PendencyReport", {
                  "From_Account": ctx.identifier,
                  "LatestPendencyTimeStamp": options.LatestPendencyTimeStamp
              },
              cbOk, cbErr);
      };
      //删除好友申请
      var proto_deletePendency = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "pendency_delete", {
                  "From_Account": ctx.identifier,
                  "PendencyType": options.PendencyType,
                  "To_Account": options.To_Account
  
              },
              function(resp) {
                  var newResp = convertErrorEn2ZhFriend(resp);
                  if (newResp.ActionStatus == ACTION_STATUS.FAIL) {
                      if (cbErr) cbErr(newResp);
                  } else if (cbOk) {
                      cbOk(newResp);
                  }
              }, cbErr);
      };
      //处理好友申请
      var proto_responseFriend = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "friend_response", {
                  'From_Account': ctx.identifier,
                  'ResponseFriendItem': options.ResponseFriendItem
              },
              function(resp) {
                  var newResp = convertErrorEn2ZhFriend(resp);
                  if (newResp.ActionStatus == ACTION_STATUS.FAIL) {
                      if (cbErr) cbErr(newResp);
                  } else if (cbOk) {
                      cbOk(newResp);
                  }
              }, cbErr);
      };
      //我的好友
      var proto_getAllFriend = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "friend_get_all", {
                  'From_Account': ctx.identifier,
                  'TimeStamp': options.TimeStamp,
                  'StartIndex': options.StartIndex,
                  'GetCount': options.GetCount,
                  'LastStandardSequence': options.LastStandardSequence,
                  'TagList': options.TagList
              },
              cbOk, cbErr);
      };
  
      //资料接口
      //查看个人资料
      var proto_getProfilePortrait = function(options, cbOk, cbErr) {
          if (options.To_Account.length > 100) {
              options.To_Account.length = 100;
              log.error('获取用户资料人数不能超过100人')
          }
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.PROFILE, "portrait_get", {
                  'From_Account': ctx.identifier,
                  'To_Account': options.To_Account,
                  //'LastStandardSequence':options.LastStandardSequence,
                  'TagList': options.TagList
              },
              function(resp) {
                  var errorAccount = [];
                  if (resp.Fail_Account && resp.Fail_Account.length) {
                      errorAccount = resp.Fail_Account;
                  }
                  if (resp.Invalid_Account && resp.Invalid_Account.length) {
                      for (var k in resp.Invalid_Account) {
                          errorAccount.push(resp.Invalid_Account[k]);
                      }
                  }
                  if (errorAccount.length) {
                      resp.ActionStatus = ACTION_STATUS.FAIL;
                      resp.ErrorCode = ERROR_CODE_CUSTOM;
                      resp.ErrorInfo = '';
                      for (var i in errorAccount) {
                          var failCount = errorAccount[i];
                          for (var j in resp.UserProfileItem) {
                              if (resp.UserProfileItem[j].To_Account == failCount) {
                                  var resultCode = resp.UserProfileItem[j].ResultCode;
                                  resp.UserProfileItem[j].ResultInfo = "[" + resultCode + "]" + resp.UserProfileItem[j].ResultInfo;
                                  resp.ErrorInfo += "账号:" + failCount + "," + resp.UserProfileItem[j].ResultInfo + "\n";
                                  break;
                              }
                          }
                      }
                  }
                  if (resp.ActionStatus == ACTION_STATUS.FAIL) {
                      if (cbErr) cbErr(resp);
                  } else if (cbOk) {
                      cbOk(resp);
                  }
              },
              cbErr);
      };
  
      //设置个人资料
      var proto_setProfilePortrait = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.PROFILE, "portrait_set", {
                  'From_Account': ctx.identifier,
                  'ProfileItem': options.ProfileItem
              },
              function(resp) {
                  for (var i in options.ProfileItem) {
                      var profile = options.ProfileItem[i];
                      if (profile.Tag == 'Tag_Profile_IM_Nick') {
                          ctx.identifierNick = profile.Value; //更新昵称
                          break;
                      }
                  }
                  if (cbOk) cbOk(resp);
              }, cbErr);
      };
  
      //增加黑名单
      var proto_addBlackList = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "black_list_add", {
                  'From_Account': ctx.identifier,
                  'To_Account': options.To_Account
              },
              function(resp) {
                  var newResp = convertErrorEn2ZhFriend(resp);
                  if (newResp.ActionStatus == ACTION_STATUS.FAIL) {
                      if (cbErr) cbErr(newResp);
                  } else if (cbOk) {
                      cbOk(newResp);
                  }
              }, cbErr);
      };
  
      //删除黑名单
      var proto_deleteBlackList = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "black_list_delete", {
                  'From_Account': ctx.identifier,
                  'To_Account': options.To_Account
              },
              function(resp) {
                  var newResp = convertErrorEn2ZhFriend(resp);
                  if (newResp.ActionStatus == ACTION_STATUS.FAIL) {
                      if (cbErr) cbErr(newResp);
                  } else if (cbOk) {
                      cbOk(newResp);
                  }
              }, cbErr);
      };
  
      //我的黑名单
      var proto_getBlackList = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.FRIEND, "black_list_get", {
                  'From_Account': ctx.identifier,
                  'StartIndex': options.StartIndex,
                  'MaxLimited': options.MaxLimited,
                  'LastSequence': options.LastSequence
              },
              cbOk, cbErr);
      };
  
      //获取最近联系人
      var proto_getRecentContactList = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.RECENT_CONTACT, "get", {
                  'From_Account': ctx.identifier,
                  'Count': options.Count
              },
              cbOk, cbErr);
      };
  
      //上传图片或文件
      var proto_uploadPic = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          var cmdName;
          if (isAccessFormalEnv()) {
              cmdName = 'pic_up';
          } else {
              cmdName = 'pic_up_test';
          }
          ConnManager.apiCall(SRV_NAME.PIC, cmdName, {
                  'App_Version': VERSION_INFO.APP_VERSION,
                  'From_Account': ctx.identifier,
                  'To_Account': options.To_Account,
                  'Seq': options.Seq,
                  'Timestamp': options.Timestamp,
                  'Random': options.Random,
                  'File_Str_Md5': options.File_Str_Md5,
                  'File_Size': options.File_Size,
                  'File_Type': options.File_Type,
                  'Server_Ver': VERSION_INFO.SERVER_VERSION,
                  'Auth_Key': authkey,
                  'Busi_Id': options.Busi_Id,
                  'PkgFlag': options.PkgFlag,
                  'Slice_Offset': options.Slice_Offset,
                  'Slice_Size': options.Slice_Size,
                  'Slice_Data': options.Slice_Data,
                  'content_type':"application/x-www-form-urlencoded"
              },
              cbOk, cbErr);
      };
  
      //获取语音和文件下载IP和authkey
      var proto_getIpAndAuthkey = function(cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.OPEN_IM, "authkey", {}, cbOk, cbErr);
      };
  
      //接口质量上报
      var proto_reportApiQuality = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.IM_OPEN_STAT, "web_report", options, cbOk, cbErr);
      };
  
  
      var proto_getLongPollingId = function(options, cbOk, cbErr) {
          if (!checkLogin(cbErr, true)) return;
          ConnManager.apiCall(SRV_NAME.OPEN_IM, "getlongpollingid", {},
              function(resp) {
                  cbOk && cbOk(resp);
              }, cbErr);
      }
  
  
      var proto_applyDownload = function(options, cbOk, cbErr) {
          //把下载地址push到map中
          ConnManager.apiCall(SRV_NAME.PIC, "apply_download", options, cbOk, cbErr);
      }
  
      //end
      initBrowserInfo();
      // singleton object ConnManager
      var ConnManager = lowerBR == false ? new function() {
              var onConnCallback = null; //回调函数
              this.init = function(onConnNotify, cbOk, cbErr) {
                  if (onConnNotify) onConnCallback = onConnNotify;
              };
              this.callBack = function(info) {
                  if (onConnCallback) onConnCallback(info);
              };
              this.clear = function() {
                  onConnCallback = null;
              };
              //请求后台服务接口
              this.apiCall = function(type, cmd, data, cbOk, cbErr, timeout, isLongPolling) {
                  //封装后台服务接口地址
                  var url = getApiUrl(type, cmd, cbOk, cbErr);
                  if (url == false) return;
                  //发起ajax请求
                  var content_type = data.content_type ? data.content_type :  "application/json";
                  ajaxRequestJson("POST", url, data, timeout, content_type, isLongPolling, function(resp) {
                      var errorCode = null,
                          tempErrorInfo = '';
                      if (cmd == 'pic_up') {
                          data.Slice_Data = '';
                      }
                      var info = "\n request url: \n" + url + "\n request body: \n" + JSON.stringify(data) + "\n response: \n" + JSON.stringify(resp);
                      //成功
                      if (resp.ActionStatus == ACTION_STATUS.OK) {
                          log.info("[" + type + "][" + cmd + "]success: " + info);
                          if (cbOk) cbOk(resp); //回调
                          errorCode = 0;
                          tempErrorInfo = '';
                      } else {
                          errorCode = resp.ErrorCode;
                          tempErrorInfo = resp.ErrorInfo;
                          //报错
                          if (cbErr) {
                              resp.SrcErrorInfo = resp.ErrorInfo;
                              resp.ErrorInfo = "[" + type + "][" + cmd + "]failed: " + info;
                              if (cmd != 'longpolling' || resp.ErrorCode != longPollingTimeOutErrorCode) {
                                  log.error(resp.ErrorInfo);
                              }
                              cbErr(resp);
                          }
                      }
                      reportApiQuality(cmd, errorCode, tempErrorInfo); //接口质量上报
                  }, function(err) {
                      cbErr && cbErr(err);
                      reportApiQuality(cmd, err.ErrorCode, err.ErrorInfo); //接口质量上报
                  });
              };
          } : new function() {
              var onConnCallback = null; //回调函数
              this.init = function(onConnNotify, cbOk, cbErr) {
                  if (onConnNotify) onConnCallback = onConnNotify;
              };
              this.callBack = function(info) {
                  if (onConnCallback) onConnCallback(info);
              };
              this.clear = function() {
                  onConnCallback = null;
              };
              //请求后台服务接口
              this.apiCall = function(type, cmd, data, cbOk, cbErr, timeout, isLongPolling) {
                  //封装后台服务接口地址
                  var url = getApiUrl(type, cmd, cbOk, cbErr);
                  if (url == false) return;
                  //发起jsonp请求
                  var reqId = jsonpRequestId++,
                      cbkey = 'jsonpcallback', // the 'callback' key
                      cbval = 'jsonpRequest' + reqId, // the 'callback' value
                      script = document.createElement('script'),
                      loaded = 0;
  
                  window[cbval] = jsonpCallback;
                  script.type = 'text/javascript';
                  url = url + "&" + cbkey + "=" + cbval + "&jsonpbody=" + encodeURIComponent(JSON.stringify(data));
                  script.src = url;
                  script.async = true;
  
                  if (typeof script.onreadystatechange !== 'undefined') {
                      // need this for IE due to out-of-order onreadystatechange(), binding script
                      // execution to an event listener gives us control over when the script
                      // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html
                      script.event = 'onclick';
                      script.htmlFor = script.id = '_jsonpRequest_' + reqId;
                  }
  
                  script.onload = script.onreadystatechange = function() {
                      if ((this.readyState && this.readyState !== 'complete' && this.readyState !== 'loaded') || loaded) {
                          return false;
                      }
                      script.onload = script.onreadystatechange = null;
                      script.onclick && script.onclick();
                      // Call the user callback with the last value stored and clean up values and scripts.
                      var resp = jsonpLastRspData;
                      var info = "\n request url: \n" + url + "\n request body: \n" + JSON.stringify(data) + "\n response: \n" + JSON.stringify(resp);
                      if (resp.ActionStatus == ACTION_STATUS.OK) {
                          log.info("[" + type + "][" + cmd + "]success: " + info);
                          cbOk && cbOk(resp);
                      } else {
                          resp.ErrorInfo = "[" + type + "][" + cmd + "]failed " + info;
                          if (cmd != 'longpolling' || resp.ErrorCode != longPollingTimeOutErrorCode) {
                              log.error(resp.ErrorInfo);
                          } else {
                              log.warn("[" + type + "][" + cmd + "]success: " + info);
                          }
                          cbErr && cbErr(resp);
                      }
                      jsonpLastRspData = undefined;
                      document.body.removeChild(script);
                      loaded = 1;
                  };
  
                  // Add the script to the DOM head
                  document.body.appendChild(script);
              };
          };
      // class Session
      var Session = function(type, id, name, icon, time, seq) {
          this._impl = {
              skey: Session.skey(type, id),
              type: type,
              id: id,
              name: name,
              icon: icon,
              unread: 0, //未读消息数
              isAutoRead: false,
              time: time >= 0 ? time : 0,
              curMaxMsgSeq: seq >= 0 ? seq : 0,
              msgs: [],
              isFinished: 1
          };
      };
      Session.skey = function(type, id) {
          return type + id;
      };
      Session.prototype.type = function() {
          return this._impl.type;
      };
      Session.prototype.id = function() {
          return this._impl.id;
      };
      Session.prototype.name = function() {
          return this._impl.name;
      };
      Session.prototype.icon = function() {
          return this._impl.icon;
      };
      Session.prototype.unread = function(val) {
          if (typeof val !== 'undefined') {
              this._impl.unread = val;
          } else {
              return this._impl.unread;
          }
      };
      Session.prototype.isFinished = function(val) {
          if (typeof val !== 'undefined') {
              this._impl.isFinished = val;
          } else {
              return this._impl.isFinished;
          }
      };
      Session.prototype.time = function() {
          return this._impl.time;
      };
      Session.prototype.curMaxMsgSeq = function(seq) {
          if (typeof seq !== 'undefined') {
              this._impl.curMaxMsgSeq = seq;
          } else {
              return this._impl.curMaxMsgSeq;
          }
      };
      Session.prototype.msgCount = function() {
          return this._impl.msgs.length;
      };
      Session.prototype.msg = function(index) {
          return this._impl.msgs[index];
      };
      Session.prototype.msgs = function() {
          return this._impl.msgs;
      };
      Session.prototype._impl_addMsg = function(msg) {
          this._impl.msgs.push(msg);
          //if (!msg.isSend && msg.time > this._impl.time)
          if (msg.time > this._impl.time)
              this._impl.time = msg.time;
          //if (!msg.isSend && msg.seq > this._impl.curMaxMsgSeq)
          if (msg.seq > this._impl.curMaxMsgSeq)
              this._impl.curMaxMsgSeq = msg.seq;
          //自己发送的消息不计入未读数
          if (!msg.isSend && !this._impl.isAutoRead) {
              this._impl.unread++;
          }
      };
      //class C2CMsgReadedItem
      var C2CMsgReadedItem = function(toAccount, lastedMsgTime) {
          this.toAccount = toAccount;
          this.lastedMsgTime = lastedMsgTime;
      }
  
      var calcUniqId = function(num1, num2) {
          var str1 = parseInt(num1).toString(2) + '00000000000000000000000000000000';
          var str2 = parseInt(num2).toString(2);
          var arr1 = str1.split('').reverse();
          var arr2 = str2.split('').reverse();
          var res = [];
          var length = arr1.length > arr2.length ? arr1.length : arr2.length;
          for (var a = 0; a < length; a++) {
              sig = Number(arr1[a] || 0) || Number(arr2[a] || 0);
              res.push(sig);
          }
          var numstr = res.reverse().join("");
          var long = {
              high: "0x" + parseInt(numstr.substr(0, numstr.length - 32), 2).toString(16),
              low: "0x" + parseInt(numstr.substr(numstr.length - 32 - 1), 2).toString(16)
          }
          var longVal = new Long(long.low, long.high, true);
          return longVal.toString();
      };
      // class Msg
      var Msg = function(sess, isSend, seq, random, time, fromAccount, subType, fromAccountNick) {
          this.sess = sess;
          this.subType = subType >= 0 ? subType : 0; //消息类型,c2c消息时，type取值为0；group消息时，type取值0和1，0-普通群消息，1-群提示消息
          this.fromAccount = fromAccount;
          this.fromAccountNick = fromAccountNick ? fromAccountNick : fromAccount;
          this.isSend = Boolean(isSend);
          this.seq = seq >= 0 ? seq : nextSeq();
          this.random = random >= 0 ? random : createRandom();
          this.time = time >= 0 ? time : unixtime();
          this.elems = [];
          var type = sess.type();
          switch (type) {
              case SESSION_TYPE.GROUP:
                  this.uniqueId = calcUniqId(this.seq, this.random);
                  break;
              case SESSION_TYPE.C2C:
              default:
                  this.uniqueId = calcUniqId(this.time, this.random);
                  break;
          }
  
  
      };
      Msg.prototype.getSession = function() {
          return this.sess;
      };
      Msg.prototype.getType = function() {
          return this.subType;
      };
      Msg.prototype.getSubType = function() {
          return this.subType;
      };
      Msg.prototype.getFromAccount = function() {
          return this.fromAccount;
      };
      Msg.prototype.getFromAccountNick = function() {
          return this.fromAccountNick;
      };
      Msg.prototype.getIsSend = function() {
          return this.isSend;
      };
      Msg.prototype.getSeq = function() {
          return this.seq;
      };
      Msg.prototype.getTime = function() {
          return this.time;
      };
      Msg.prototype.getRandom = function() {
          return this.random;
      };
      Msg.prototype.getElems = function() {
          return this.elems;
      };
      Msg.prototype.getMsgUniqueId = function() {
          return this.uniqueId;
      };
      //文本
      Msg.prototype.addText = function(text) {
          this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.TEXT, text));
      };
      //表情
      Msg.prototype.addFace = function(face) {
          this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.FACE, face));
      };
      //图片
      Msg.prototype.addImage = function(image) {
          this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.IMAGE, image));
      };
      //地理位置
      Msg.prototype.addLocation = function(location) {
          this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.LOCATION, location));
      };
      //文件
      Msg.prototype.addFile = function(file) {
          this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.FILE, file));
      };
      //自定义
      Msg.prototype.addCustom = function(custom) {
          this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.CUSTOM, custom));
      };
      Msg.prototype.addElem = function(elem) {
          this.elems.push(elem);
      };
      Msg.prototype.toHtml = function() {
          var html = "";
          for (var i in this.elems) {
              var elem = this.elems[i];
              html += elem.toHtml();
          }
          return html;
      };
  
      // class Msg.Elem
      Msg.Elem = function(type, value) {
          this.type = type;
          this.content = value;
      };
      Msg.Elem.prototype.getType = function() {
          return this.type;
      };
      Msg.Elem.prototype.getContent = function() {
          return this.content;
      };
      Msg.Elem.prototype.toHtml = function() {
          var html;
          html = this.content.toHtml();
          return html;
      };
  
      // class Msg.Elem.Text
      Msg.Elem.Text = function(text) {
          this.text = tool.xssFilter(text);
      };
      Msg.Elem.Text.prototype.getText = function() {
          return this.text;
      };
      Msg.Elem.Text.prototype.toHtml = function() {
          return this.text;
      };
  
      // class Msg.Elem.Face
      Msg.Elem.Face = function(index, data) {
          this.index = index;
          this.data = data;
      };
      Msg.Elem.Face.prototype.getIndex = function() {
          return this.index;
      };
      Msg.Elem.Face.prototype.getData = function() {
          return this.data;
      };
      Msg.Elem.Face.prototype.toHtml = function() {
          var faceUrl = null;
          var index = emotionDataIndexs[this.data];
          var emotion = emotions[index];
          if (emotion && emotion[1]) {
              faceUrl = emotion[1];
          }
          if (faceUrl) {
              return "<img src='" + faceUrl + "'/>";
          } else {
              return this.data;
          }
      };
  
      // 地理位置消息 class Msg.Elem.Location
      Msg.Elem.Location = function(longitude, latitude, desc) {
          this.latitude = latitude; //纬度
          this.longitude = longitude; //经度
          this.desc = desc; //描述
      };
      Msg.Elem.Location.prototype.getLatitude = function() {
          return this.latitude;
      };
      Msg.Elem.Location.prototype.getLongitude = function() {
          return this.longitude;
      };
      Msg.Elem.Location.prototype.getDesc = function() {
          return this.desc;
      };
      Msg.Elem.Location.prototype.toHtml = function() {
          return '经度=' + this.longitude + ',纬度=' + this.latitude + ',描述=' + this.desc;
      };
  
      //图片消息
      // class Msg.Elem.Images
                  Msg.Elem.Images = function(imageId, format) {
                      this.UUID = imageId;
                      if (typeof format !== 'number') {
                          format = parseInt(IMAGE_FORMAT[format] || IMAGE_FORMAT['UNKNOWN'], 10);
                      }
                      this.ImageFormat = format;
          this.ImageInfoArray = [];
      };
      Msg.Elem.Images.prototype.addImage = function(image) {
          this.ImageInfoArray.push(image);
      };
      Msg.Elem.Images.prototype.toHtml = function() {
          var smallImage = this.getImage(IMAGE_TYPE.SMALL);
          var bigImage = this.getImage(IMAGE_TYPE.LARGE);
          var oriImage = this.getImage(IMAGE_TYPE.ORIGIN);
          if (!bigImage) {
              bigImage = smallImage;
          }
          if (!oriImage) {
              oriImage = smallImage;
          }
          return "<img src='" + smallImage.getUrl() + "#" + bigImage.getUrl() + "#" + oriImage.getUrl() + "' style='CURSOR: hand' id='" + this.getImageId() + "' bigImgUrl='" + bigImage.getUrl() + "' onclick='imageClick(this)' />";
  
      };
      Msg.Elem.Images.prototype.getImageId = function() {
          return this.UUID;
                  };
                  Msg.Elem.Images.prototype.getImageFormat = function() {
                      return this.ImageFormat;
                  };
      Msg.Elem.Images.prototype.getImage = function(type) {
          for (var i in this.ImageInfoArray) {
              if (this.ImageInfoArray[i].getType() == type) {
                  return this.ImageInfoArray[i];
              }
          }
          var img = null;
          this.ImageInfoArray.forEach(function( image ){
              if ( image.getType() == type ) {
                  img = image;
              }
          })
          return img;
      };
      // class Msg.Elem.Images.Image
      Msg.Elem.Images.Image = function(type, size, width, height, url) {
          this.type = type;
          this.size = size;
          this.width = width;
          this.height = height;
          this.url = url;
      };
      Msg.Elem.Images.Image.prototype.getType = function() {
          return this.type;
      };
      Msg.Elem.Images.Image.prototype.getSize = function() {
          return this.size;
      };
      Msg.Elem.Images.Image.prototype.getWidth = function() {
          return this.width;
      };
      Msg.Elem.Images.Image.prototype.getHeight = function() {
          return this.height;
      };
      Msg.Elem.Images.Image.prototype.getUrl = function() {
          return this.url;
      };
  
      // class Msg.Elem.Sound
      Msg.Elem.Sound = function(uuid, second, size, senderId, receiverId, downFlag, chatType) {
          this.uuid = uuid; //文件id
          this.second = second; //时长，单位：秒
          this.size = size; //大小，单位：字节
          this.senderId = senderId; //发送者
          this.receiverId = receiverId; //接收方id
          this.downFlag = downFlag; //下载标志位
          this.busiId = chatType == SESSION_TYPE.C2C ? 2 : 1; //busi_id ( 1：群    2:C2C)
  
          //根据不同情况拉取数据
          //是否需要申请下载地址  0:到架平申请  1:到cos申请  2:不需要申请, 直接拿url下载
          if (downFlag !== undefined && busiId !== undefined) {
              getFileDownUrlV2(uuid, senderId, second, downFlag, receiverId, this.busiId, UPLOAD_RES_TYPE.SOUND);
          } else {
              this.downUrl = getSoundDownUrl(uuid, senderId, second); //下载地址
          }
      };
      Msg.Elem.Sound.prototype.getUUID = function() {
          return this.uuid;
      };
      Msg.Elem.Sound.prototype.getSecond = function() {
          return this.second;
      };
      Msg.Elem.Sound.prototype.getSize = function() {
          return this.size;
      };
      Msg.Elem.Sound.prototype.getSenderId = function() {
          return this.senderId;
      };
      Msg.Elem.Sound.prototype.getDownUrl = function() {
          return this.downUrl;
      };
      Msg.Elem.Sound.prototype.toHtml = function() {
          if (BROWSER_INFO.type == 'ie' && parseInt(BROWSER_INFO.ver) <= 8) {
              return '[这是一条语音消息]demo暂不支持ie8(含)以下浏览器播放语音,语音URL:' + this.downUrl;
          }
          return '<audio id="uuid_' + this.uuid + '" src="' + this.downUrl + '" controls="controls" onplay="onChangePlayAudio(this)" preload="none"></audio>';
      };
  
      // class Msg.Elem.File
      Msg.Elem.File = function(uuid, name, size, senderId, receiverId, downFlag, chatType) {
          this.uuid = uuid; //文件id
          this.name = name; //文件名
          this.size = size; //大小，单位：字节
          this.senderId = senderId; //发送者
          this.receiverId = receiverId; //接收方id
          this.downFlag = downFlag; //下载标志位
  
          this.busiId = chatType == SESSION_TYPE.C2C ? 2 : 1; //busi_id ( 1：群    2:C2C)
          //根据不同情况拉取数据
          //是否需要申请下载地址  0:到架平申请  1:到cos申请  2:不需要申请, 直接拿url下载
          if (downFlag !== undefined && busiId !== undefined) {
              getFileDownUrlV2(uuid, senderId, name, downFlag, receiverId, this.busiId, UPLOAD_RES_TYPE.FILE);
          } else {
              this.downUrl = getFileDownUrl(uuid, senderId, name); //下载地址
          }
      };
      Msg.Elem.File.prototype.getUUID = function() {
          return this.uuid;
      };
      Msg.Elem.File.prototype.getName = function() {
          return this.name;
      };
      Msg.Elem.File.prototype.getSize = function() {
          return this.size;
      };
      Msg.Elem.File.prototype.getSenderId = function() {
          return this.senderId;
      };
      Msg.Elem.File.prototype.getDownUrl = function() {
          return this.downUrl;
      };
      Msg.Elem.File.prototype.getDownFlag = function() {
          return this.downFlag;
      };
      Msg.Elem.File.prototype.toHtml = function() {
          var fileSize, unitStr;
          fileSize = this.size;
          unitStr = "Byte";
          if (this.size >= 1024) {
              fileSize = Math.round(this.size / 1024);
              unitStr = "KB";
          }
          return '<a href="javascript" onclick="webim.onDownFile("' + this.uuid + '")" title="点击下载文件" ><i class="glyphicon glyphicon-file">&nbsp;' + this.name + '(' + fileSize + unitStr + ')</i></a>';
      };
  
      // class Msg.Elem.GroupTip 群提示消息对象
      Msg.Elem.GroupTip = function(opType, opUserId, groupId, groupName, userIdList, userinfo) {
          this.opType = opType; //操作类型
          this.opUserId = opUserId; //操作者id
          this.groupId = groupId; //群id
          this.groupName = groupName; //群名称
          this.userIdList = userIdList ? userIdList : []; //被操作的用户id列表
          this.groupInfoList = []; //新的群资料信息，群资料变更时才有值
          this.memberInfoList = []; //新的群成员资料信息，群成员资料变更时才有值
          this.groupMemberNum = null; //群成员数，操作类型为加群或者退群时才有值
          this.userinfo = userinfo ? userinfo : []; //被操作的用户信息列表列表
      };
      Msg.Elem.GroupTip.prototype.addGroupInfo = function(groupInfo) {
          this.groupInfoList.push(groupInfo);
      };
      Msg.Elem.GroupTip.prototype.addMemberInfo = function(memberInfo) {
          this.memberInfoList.push(memberInfo);
      };
      Msg.Elem.GroupTip.prototype.getOpType = function() {
          return this.opType;
      };
      Msg.Elem.GroupTip.prototype.getOpUserId = function() {
          return this.opUserId;
      };
      Msg.Elem.GroupTip.prototype.getGroupId = function() {
          return this.groupId;
      };
      Msg.Elem.GroupTip.prototype.getGroupName = function() {
          return this.groupName;
      };
      Msg.Elem.GroupTip.prototype.getUserIdList = function() {
          return this.userIdList;
      };
      Msg.Elem.GroupTip.prototype.getUserInfo = function() {
          return this.userinfo;
      };
      Msg.Elem.GroupTip.prototype.getGroupInfoList = function() {
          return this.groupInfoList;
      };
      Msg.Elem.GroupTip.prototype.getMemberInfoList = function() {
          return this.memberInfoList;
      };
      Msg.Elem.GroupTip.prototype.getGroupMemberNum = function() {
          return this.groupMemberNum;
      };
      Msg.Elem.GroupTip.prototype.setGroupMemberNum = function(groupMemberNum) {
          return this.groupMemberNum = groupMemberNum;
      };
      Msg.Elem.GroupTip.prototype.toHtml = function() {
          var text = "[群提示消息]";
          var maxIndex = GROUP_TIP_MAX_USER_COUNT - 1;
          switch (this.opType) {
              case GROUP_TIP_TYPE.JOIN: //加入群
                  text += this.opUserId + "邀请了";
                  for (var m in this.userIdList) {
                      text += this.userIdList[m] + ",";
                      if (this.userIdList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) {
                          text += "等" + this.userIdList.length + "人";
                          break;
                      }
                  }
                  text += "加入该群";
                  break;
              case GROUP_TIP_TYPE.QUIT: //退出群
                  text += this.opUserId + "主动退出该群";
                  break;
              case GROUP_TIP_TYPE.KICK: //踢出群
                  text += this.opUserId + "将";
                  for (var m in this.userIdList) {
                      text += this.userIdList[m] + ",";
                      if (this.userIdList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) {
                          text += "等" + this.userIdList.length + "人";
                          break;
                      }
                  }
                  text += "踢出该群";
                  break;
              case GROUP_TIP_TYPE.SET_ADMIN: //设置管理员
                  text += this.opUserId + "将";
                  for (var m in this.userIdList) {
                      text += this.userIdList[m] + ",";
                      if (this.userIdList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) {
                          text += "等" + this.userIdList.length + "人";
                          break;
                      }
                  }
                  text += "设为管理员";
                  break;
              case GROUP_TIP_TYPE.CANCEL_ADMIN: //取消管理员
                  text += this.opUserId + "取消";
                  for (var m in this.userIdList) {
                      text += this.userIdList[m] + ",";
                      if (this.userIdList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) {
                          text += "等" + this.userIdList.length + "人";
                          break;
                      }
                  }
                  text += "的管理员资格";
                  break;
  
  
              case GROUP_TIP_TYPE.MODIFY_GROUP_INFO: //群资料变更
                  text += this.opUserId + "修改了群资料：";
                  for (var m in this.groupInfoList) {
                      var type = this.groupInfoList[m].getType();
                      var value = this.groupInfoList[m].getValue();
                      switch (type) {
                          case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.FACE_URL:
                              text += "群头像为" + value + "; ";
                              break;
                          case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.NAME:
                              text += "群名称为" + value + "; ";
                              break;
                          case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.OWNER:
                              text += "群主为" + value + "; ";
                              break;
                          case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.NOTIFICATION:
                              text += "群公告为" + value + "; ";
                              break;
                          case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.INTRODUCTION:
                              text += "群简介为" + value + "; ";
                              break;
                          default:
                              text += "未知信息为:type=" + type + ",value=" + value + "; ";
                              break;
                      }
                  }
                  break;
  
              case GROUP_TIP_TYPE.MODIFY_MEMBER_INFO: //群成员资料变更(禁言时间)
                  text += this.opUserId + "修改了群成员资料:";
                  for (var m in this.memberInfoList) {
                      var userId = this.memberInfoList[m].getUserId();
                      var shutupTime = this.memberInfoList[m].getShutupTime();
                      text += userId + ": ";
                      if (shutupTime != null && shutupTime !== undefined) {
                          if (shutupTime == 0) {
                              text += "取消禁言; ";
                          } else {
                              text += "禁言" + shutupTime + "秒; ";
                          }
                      } else {
                          text += " shutupTime为空";
                      }
                      if (this.memberInfoList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) {
                          text += "等" + this.memberInfoList.length + "人";
                          break;
                      }
                  }
                  break;
  
              case GROUP_TIP_TYPE.READED: //消息已读
                  /**/
                  Log.info("消息已读同步")
                  break;
              default:
                  text += "未知群提示消息类型：type=" + this.opType;
                  break;
          }
          return text;
      };
  
      // class Msg.Elem.GroupTip.GroupInfo，变更的群资料信息对象
      Msg.Elem.GroupTip.GroupInfo = function(type, value) {
          this.type = type; //群资料信息类型
          this.value = value; //对应的值
      };
      Msg.Elem.GroupTip.GroupInfo.prototype.getType = function() {
          return this.type;
      };
      Msg.Elem.GroupTip.GroupInfo.prototype.getValue = function() {
          return this.value;
      };
  
      // class Msg.Elem.GroupTip.MemberInfo，变更的群成员资料信息对象
      Msg.Elem.GroupTip.MemberInfo = function(userId, shutupTime) {
          this.userId = userId; //群成员id
          this.shutupTime = shutupTime; //群成员被禁言时间，0表示取消禁言，大于0表示被禁言时长，单位：秒
      };
      Msg.Elem.GroupTip.MemberInfo.prototype.getUserId = function() {
          return this.userId;
      };
      Msg.Elem.GroupTip.MemberInfo.prototype.getShutupTime = function() {
          return this.shutupTime;
      };
  
      // 自定义消息类型 class Msg.Elem.Custom
      Msg.Elem.Custom = function(data, desc, ext) {
          this.data = data; //数据
          this.desc = desc; //描述
          this.ext = ext; //扩展字段
      };
      Msg.Elem.Custom.prototype.getData = function() {
          return this.data;
      };
      Msg.Elem.Custom.prototype.getDesc = function() {
          return this.desc;
      };
      Msg.Elem.Custom.prototype.getExt = function() {
          return this.ext;
      };
      Msg.Elem.Custom.prototype.toHtml = function() {
          return this.data;
      };
  
      // singleton object MsgStore
      var MsgStore = new function() {
              var sessMap = {}; //跟所有用户或群的聊天记录MAP
              var sessTimeline = []; //按时间降序排列的会话列表
              msgCache = {}; //消息缓存，用于判重
              //C2C
              this.cookie = ""; //上一次拉取新c2c消息的cookie
              this.syncFlag = 0; //上一次拉取新c2c消息的是否继续拉取标记
  
              var visitSess = function(visitor) {
                  for (var i in sessMap) {
                      visitor(sessMap[i]);
                  }
              };
              //消息查重
              var checkDupMsg = function(msg) {
                  var dup = false;
                  var first_key = msg.sess._impl.skey;
                  var second_key = msg.isSend + msg.seq + msg.random;
                  var tempMsg = msgCache[first_key] && msgCache[first_key][second_key];
                  if (tempMsg) {
                      dup = true;
                  }
                  if (msgCache[first_key]) {
                      msgCache[first_key][second_key] = {
                          time: msg.time
                      };
                  } else {
                      msgCache[first_key] = {};
                      msgCache[first_key][second_key] = {
                          time: msg.time
                      };
                  }
                  return dup;
              };
  
              this.sessMap = function() {
                  return sessMap;
              };
              this.sessCount = function() {
                  return sessTimeline.length;
              };
              this.sessByTypeId = function(type, id) {
                  var skey = Session.skey(type, id);
                  if (skey === undefined || skey == null) return null;
                  return sessMap[skey];
              };
              this.delSessByTypeId = function(type, id) {
                  var skey = Session.skey(type, id);
                  if (skey === undefined || skey == null) return false;
                  if (sessMap[skey]) {
                      delete sessMap[skey];
                      delete msgCache[skey];
                  }
                  return true;
              };
              this.resetCookieAndSyncFlag = function() {
                  this.cookie = "";
                  this.syncFlag = 0;
              };
  
              //切换将当前会话的自动读取消息标志为isOn,重置其他会话的自动读取消息标志为false
              this.setAutoRead = function(selSess, isOn, isResetAll) {
                  if (isResetAll)
                      visitSess(function(s) {
                          s._impl.isAutoRead = false;
                      });
                  if (selSess) {
                      selSess._impl.isAutoRead = isOn; //
                      if (isOn) { //是否调用已读上报接口
                          selSess._impl.unread = 0;
  
                          if (selSess._impl.type == SESSION_TYPE.C2C) { //私聊消息已读上报
                              var tmpC2CMsgReadedItem = [];
                              tmpC2CMsgReadedItem.push(new C2CMsgReadedItem(selSess._impl.id, selSess._impl.time));
                              //调用C2C消息已读上报接口
                              proto_c2CMsgReaded(MsgStore.cookie,
                                  tmpC2CMsgReadedItem,
                                  function(resp) {
                                      log.info("[setAutoRead]: c2CMsgReaded success");
                                  },
                                  function(err) {
                                      log.error("[setAutoRead}: c2CMsgReaded failed:" + err.ErrorInfo);
                                  });
                          } else if (selSess._impl.type == SESSION_TYPE.GROUP) { //群聊消息已读上报
                              var tmpOpt = {
                                  'GroupId': selSess._impl.id,
                                  'MsgReadedSeq': selSess._impl.curMaxMsgSeq
                              };
                              //调用group消息已读上报接口
                              proto_groupMsgReaded(tmpOpt,
                                  function(resp) {
                                      log.info("groupMsgReaded success");
  
                                  },
                                  function(err) {
                                      log.error("groupMsgReaded failed:" + err.ErrorInfo);
  
                                  });
                          }
                      }
                  }
              };
  
              this.c2CMsgReaded = function(opts, cbOk, cbErr) {
                  var tmpC2CMsgReadedItem = [];
                  tmpC2CMsgReadedItem.push(new C2CMsgReadedItem(opts.To_Account, opts.LastedMsgTime));
                  //调用C2C消息已读上报接口
                  proto_c2CMsgReaded(MsgStore.cookie,
                      tmpC2CMsgReadedItem,
                      function(resp) {
                          if (cbOk) {
                              log.info("c2CMsgReaded success");
                              cbOk(resp);
                          }
                      },
                      function(err) {
                          if (cbErr) {
                              log.error("c2CMsgReaded failed:" + err.ErrorInfo);
                              cbErr(err);
                          }
                      });
              };
  
              this.addSession = function(sess) {
                  sessMap[sess._impl.skey] = sess;
              };
              this.delSession = function(sess) {
                  delete sessMap[sess._impl.skey];
              };
              this.clear = function(){
                  sessMap = {}; //跟所有用户或群的聊天记录MAP
                  sessTimeline = []; //按时间降序排列的会话列表
                  msgCache = {}; //消息缓存，用于判重
                  this.cookie = ""; //上一次拉取新c2c消息的cookie
                  this.syncFlag = 0; //上一次拉取新c2c消息的是否继续拉取标记
              };
              this.addMsg = function(msg) {
                  if (checkDupMsg(msg)) return false;
                  var sess = msg.sess;
                  if (!sessMap[sess._impl.skey]) this.addSession(sess);
                  sess._impl_addMsg(msg);
                  return true;
              };
              this.updateTimeline = function() {
                  var arr = new Array;
                  visitSess(function(sess) {
                      arr.push(sess);
                  });
                  arr.sort(function(a, b) {
                      return b.time - a.time;
                  });
                  sessTimeline = arr;
              };
          };
      // singleton object MsgManager
      var MsgManager = new function() {
  
              var onMsgCallback = null; //新消息(c2c和group)回调
  
              var onGroupInfoChangeCallback = null; //群资料变化回调
              //收到新群系统消息回调列表
              var onGroupSystemNotifyCallbacks = {
                  "1": null,
                  "2": null,
                  "3": null,
                  "4": null,
                  "5": null,
                  "6": null,
                  "7": null,
                  "8": null,
                  "9": null,
                  "10": null,
                  "11": null,
                  "15": null,
                  "255": null,
                  "12": null,
              };
              //监听好友系统通知函数
              var onFriendSystemNotifyCallbacks = {
                  "1": null,
                  "2": null,
                  "3": null,
                  "4": null,
                  "5": null,
                  "6": null,
                  "7": null,
                  "8": null
              };
  
              var onProfileSystemNotifyCallbacks = {
                  "1": null
              };
  
              var onKickedEventCall = null;
  
              var onMsgReadCallback = null;
  
              //普通长轮询
              var longPollingOn = false; //是否开启普通长轮询
              var isLongPollingRequesting = false; //是否在长轮询ing
              var notifySeq = 0; //c2c通知seq
              var noticeSeq = 0; //群消息seq
  
              //大群长轮询
              var onBigGroupMsgCallback = null; //大群消息回调
              var bigGroupLongPollingOn = false; //是否开启长轮询
              var bigGroupLongPollingStartSeq = 0; //请求拉消息的起始seq(大群长轮询)
              var bigGroupLongPollingHoldTime = 90; //客户端长轮询的超时时间，单位是秒(大群长轮询)
              var bigGroupLongPollingKey = null; //客户端加入群组后收到的的Key(大群长轮询)
              var bigGroupLongPollingMsgMap = {}; //记录收到的群消息数
                          var onC2cEventCallbacks = {
                              "92": null, //消息已读通知,
                              "96": null
                          };;
                          var onAppliedDownloadUrl = null;
  
  
              var getLostGroupMsgCount = 0; //补拉丢失的群消息次数
              //我的群当前最大的seq
              var myGroupMaxSeqs = {}; //用于补拉丢失的群消息
  
              var groupSystemMsgsCache = {}; //群组系统消息缓存,用于判重
  
              //设置长轮询开关
              //isOn=true 开启
              //isOn=false 停止
              this.setLongPollingOn = function(isOn) {
                  longPollingOn = isOn;
              };
              this.getLongPollingOn = function() {
                  return longPollingOn;
              };
  
              //重置长轮询变量
              this.resetLongPollingInfo = function() {
                  longPollingOn = false;
                  notifySeq = 0;
                  noticeSeq = 0;
              };
  
              //设置大群长轮询开关
              //isOn=true 开启
              //isOn=false 停止
              this.setBigGroupLongPollingOn = function(isOn) {
                  bigGroupLongPollingOn = isOn;
              };
              //设置大群长轮询key
              this.setBigGroupLongPollingKey = function(key) {
                  bigGroupLongPollingKey = key;
              };
              //重置大群长轮询变量
              this.resetBigGroupLongPollingInfo = function() {
                  bigGroupLongPollingOn = false;
                  bigGroupLongPollingStartSeq = 0;
                  bigGroupLongPollingKey = null;
                  bigGroupLongPollingMsgMap = {};
              };
  
              //设置群消息数据条数
              this.setBigGroupLongPollingMsgMap = function(groupId, count) {
                  var bigGroupLongPollingMsgCount = bigGroupLongPollingMsgMap[groupId];
                  if (bigGroupLongPollingMsgCount) {
                      bigGroupLongPollingMsgCount = parseInt(bigGroupLongPollingMsgCount) + count;
                      bigGroupLongPollingMsgMap[groupId] = bigGroupLongPollingMsgCount;
                  } else {
                      bigGroupLongPollingMsgMap[groupId] = count;
                  }
              };
  
              //重置
              this.clear = function() {
  
                  onGroupInfoChangeCallback = null;
                  onGroupSystemNotifyCallbacks = {
                      "1": null, //申请加群请求（只有管理员会收到）
                      "2": null, //申请加群被同意（只有申请人能够收到）
                      "3": null, //申请加群被拒绝（只有申请人能够收到）
                      "4": null, //被管理员踢出群(只有被踢者接收到)
                      "5": null, //群被解散(全员接收)
                      "6": null, //创建群(创建者接收)
                      "7": null, //邀请加群(被邀请者接收)
                      "8": null, //主动退群(主动退出者接收)
                      "9": null, //设置管理员(被设置者接收)
                      "10": null, //取消管理员(被取消者接收)
                      "11": null, //群已被回收(全员接收)
                      "15": null, //群已被回收(全员接收)
                      "255": null, //用户自定义通知(默认全员接收)
                      "12": null, //邀请加群(被邀请者需要同意)
                  };
                  onFriendSystemNotifyCallbacks = {
                      "1": null, //好友表增加
                      "2": null, //好友表删除
                      "3": null, //未决增加
                      "4": null, //未决删除
                      "5": null, //黑名单增加
                      "6": null, //黑名单删除
                      "7": null, //未决已读上报
                      "8": null //好友信息(备注，分组)变更
                  };
                  onProfileSystemNotifyCallbacks = {
                      "1": null //资料修改
                  };
                  //重置普通长轮询参数
                  onMsgCallback = null;
                  longPollingOn = false;
                  notifySeq = 0; //c2c新消息通知seq
                  noticeSeq = 0; //group新消息seq
  
                  //重置大群长轮询参数
                  onBigGroupMsgCallback = null;
                  bigGroupLongPollingOn = false;
                  bigGroupLongPollingStartSeq = 0;
                  bigGroupLongPollingKey = null;
                  bigGroupLongPollingMsgMap = {};
  
                  groupSystemMsgsCache = {};
  
                  ipList = []; //文件下载地址
                  authkey = null; //文件下载票据
                  expireTime = null; //票据超时时间
              };
  
              //初始化文件下载ip和票据
              var initIpAndAuthkey = function(cbOk, cbErr) {
                  proto_getIpAndAuthkey(function(resp) {
                          ipList = resp.IpList;
                          authkey = resp.AuthKey;
                          expireTime = resp.ExpireTime;
                          if (cbOk) cbOk(resp);
                      },
                      function(err) {
                          log.error("initIpAndAuthkey failed:" + err.ErrorInfo);
                          if (cbErr) cbErr(err);
                      }
                  );
              };
  
              //初始化我的群当前最大的seq，用于补拉丢失的群消息
              var initMyGroupMaxSeqs = function(cbOk, cbErr) {
                  var opts = {
                      'Member_Account': ctx.identifier,
                      'Limit': 1000,
                      'Offset': 0,
                      'GroupBaseInfoFilter': [
                          'NextMsgSeq'
                      ]
                  };
                  proto_getJoinedGroupListHigh(opts, function(resp) {
                          if (!resp.GroupIdList || resp.GroupIdList.length == 0) {
                              log.info("initMyGroupMaxSeqs: 目前还没有加入任何群组");
                              if (cbOk) cbOk(resp);
                              return;
                          }
                          for (var i = 0; i < resp.GroupIdList.length; i++) {
                              var group_id = resp.GroupIdList[i].GroupId;
                              var curMaxSeq = resp.GroupIdList[i].NextMsgSeq - 1;
                              myGroupMaxSeqs[group_id] = curMaxSeq;
                          }
  
                          if (cbOk) cbOk(resp);
  
                      },
                      function(err) {
                          log.error("initMyGroupMaxSeqs failed:" + err.ErrorInfo);
                          if (cbErr) cbErr(err);
                      }
                  );
              };
  
              //补拉群消息
              var getLostGroupMsgs = function(groupId, reqMsgSeq, reqMsgNumber) {
                  getLostGroupMsgCount++;
                  //发起一个拉群群消息请求
                  var tempOpts = {
                      'GroupId': groupId,
                      'ReqMsgSeq': reqMsgSeq,
                      'ReqMsgNumber': reqMsgNumber
                  };
                  //发起一个拉群群消息请求
                  log.warn("第" + getLostGroupMsgCount + "次补齐群消息,参数=" + JSON.stringify(tempOpts));
                  MsgManager.syncGroupMsgs(tempOpts);
              };
  
              //更新群当前最大消息seq
              var updateMyGroupCurMaxSeq = function(groupId, msgSeq) {
                  //更新myGroupMaxSeqs中的群最大seq
                  var curMsgSeq = myGroupMaxSeqs[groupId]
                  if (curMsgSeq) { //如果存在，比较大小
                      if (msgSeq > curMsgSeq) {
                          myGroupMaxSeqs[groupId] = msgSeq;
                      }
                  } else { //不存在，新增
                      myGroupMaxSeqs[groupId] = msgSeq;
                  }
              };
  
              //添加群消息列表
              var addGroupMsgList = function(msgs, new_group_msgs) {
                  for (var p in msgs) {
                      var newGroupMsg = msgs[p];
                      //发群消息时，长轮询接口会返回用户自己发的群消息
                      //if(newGroupMsg.From_Account && newGroupMsg.From_Account!=ctx.identifier ){
                      if (newGroupMsg.From_Account) {
                          //false-不是主动拉取的历史消息
                          //true-需要保存到sdk本地session,并且需要判重
                          var msg = handlerGroupMsg(newGroupMsg, false, true);
                          if (msg) { //不为空，加到新消息里
                              new_group_msgs.push(msg);
                          }
                          //更新myGroupMaxSeqs中的群最大seq
                          updateMyGroupCurMaxSeq(newGroupMsg.ToGroupId, newGroupMsg.MsgSeq);
                      }
                  }
                  return new_group_msgs;
              };
  
              //处理收到的群普通和提示消息
              var handlerOrdinaryAndTipC2cMsgs = function(eventType, groupMsgArray) {
                  var groupMsgMap = {}; //保存收到的C2c消息信息（群号，最小，最大消息seq，消息列表）
                  var new_group_msgs = [];
                  var minGroupMsgSeq = 99999999;
                  var maxGroupMsgSeq = -1;
                  for (var j in groupMsgArray) {
  
                      var groupMsgs = groupMsgMap[groupMsgArray[j].ToGroupId];
                      if (!groupMsgs) {
                          groupMsgs = groupMsgMap[groupMsgArray[j].ToGroupId] = {
                              "min": minGroupMsgSeq, //收到新消息最小seq
                              "max": maxGroupMsgSeq, //收到新消息最大seq
                              "msgs": [] //收到的新消息
                          };
                      }
                      //更新长轮询的群NoticeSeq
                      if (groupMsgArray[j].NoticeSeq > noticeSeq) {
                          log.warn("noticeSeq=" + noticeSeq + ",msgNoticeSeq=" + groupMsgArray[j].NoticeSeq);
                          noticeSeq = groupMsgArray[j].NoticeSeq;
                      }
                      groupMsgArray[j].Event = eventType;
                      groupMsgMap[groupMsgArray[j].ToGroupId].msgs.push(groupMsgArray[j]); //新增一条消息
                      if (groupMsgArray[j].MsgSeq < groupMsgs.min) { //记录最小的消息seq
                          groupMsgMap[groupMsgArray[j].ToGroupId].min = groupMsgArray[j].MsgSeq;
                      }
                      if (groupMsgArray[j].MsgSeq > groupMsgs.max) { //记录最大的消息seq
                          groupMsgMap[groupMsgArray[j].ToGroupId].max = groupMsgArray[j].MsgSeq;
                      }
                  }
  
                  for (var groupId in groupMsgMap) {
                      var tempCount = groupMsgMap[groupId].max - groupMsgMap[groupId].min + 1; //收到的新的群消息数
                      var curMaxMsgSeq = myGroupMaxSeqs[groupId]; //获取本地保存的群最大消息seq
                      if (curMaxMsgSeq) { //存在这个群的最大消息seq
                          //高并发情况下，长轮询可能存在丢消息，这时需要客户端通过拉取群消息接口补齐下
                          //1、如果收到的新消息最小seq比当前最大群消息seq大于1，则表示收到的群消息发生跳跃，需要补齐
                          //2、收到的新群消息seq存在不连续情况，也需要补齐
                          if (groupMsgMap[groupId].min - curMaxMsgSeq > 1 || groupMsgMap[groupId].msgs.length < tempCount) {
                              //发起一个拉群群消息请求
                              log.warn("发起一次补齐群消息请求,curMaxMsgSeq=" + curMaxMsgSeq + ", minMsgSeq=" + groupMsgMap[groupId].min + ", maxMsgSeq=" + groupMsgMap[groupId].max + ", msgs.length=" + groupMsgMap[groupId].msgs.length + ", tempCount=" + tempCount);
                              getLostGroupMsgs(groupId, groupMsgMap[groupId].max, groupMsgMap[groupId].max - curMaxMsgSeq);
                              //更新myGroupMaxSeqs中的群最大seq
                              updateMyGroupCurMaxSeq(groupId, groupMsgMap[groupId].max);
                          } else {
                              new_group_msgs = addGroupMsgList(groupMsgMap[groupId].msgs, new_group_msgs);
                          }
                      } else { //不存在该群的最大消息seq
                          log.warn("不存在该群的最大消息seq，群id=" + groupId);
                          //高并发情况下，长轮询可能存在丢消息，这时需要客户端通过拉取群消息接口补齐下
                          //1、收到的新群消息seq存在不连续情况，也需要补齐
                          if (groupMsgMap[groupId].msgs.length < tempCount) {
                              //发起一个拉群群消息请求
                              log.warn("发起一次补齐群消息请求,minMsgSeq=" + groupMsgMap[groupId].min + ", maxMsgSeq=" + groupMsgMap[groupId].max + ", msgs.length=" + groupMsgMap[groupId].msgs.length + ", tempCount=" + tempCount);
                              getLostGroupMsgs(groupId, groupMsgMap[groupId].max, tempCount);
                              //更新myGroupMaxSeqs中的群最大seq
                              updateMyGroupCurMaxSeq(groupId, groupMsgMap[groupId].max);
                          } else {
                              new_group_msgs = addGroupMsgList(groupMsgMap[groupId].msgs, new_group_msgs);
                          }
                      }
                  }
                  if (new_group_msgs.length) {
                      MsgStore.updateTimeline();
                  }
                  if (onMsgCallback && new_group_msgs.length) onMsgCallback(new_group_msgs);
  
              };
  
              //处理收到的群普通和提示消息
              var handlerOrdinaryAndTipGroupMsgs = function(eventType, groupMsgArray) {
                  var groupMsgMap = {}; //保存收到的群消息信息（群号，最小，最大消息seq，消息列表）
                  var new_group_msgs = [];
                  var minGroupMsgSeq = 99999999;
                  var maxGroupMsgSeq = -1;
                  for (var j in groupMsgArray) {
  
                      var groupMsgs = groupMsgMap[groupMsgArray[j].ToGroupId];
                      if (!groupMsgs) {
                          groupMsgs = groupMsgMap[groupMsgArray[j].ToGroupId] = {
                              "min": minGroupMsgSeq, //收到新消息最小seq
                              "max": maxGroupMsgSeq, //收到新消息最大seq
                              "msgs": [] //收到的新消息
                          };
                      }
                      //更新长轮询的群NoticeSeq
                      if (groupMsgArray[j].NoticeSeq > noticeSeq) {
                          log.warn("noticeSeq=" + noticeSeq + ",msgNoticeSeq=" + groupMsgArray[j].NoticeSeq);
                          noticeSeq = groupMsgArray[j].NoticeSeq;
                      }
                      groupMsgArray[j].Event = eventType;
                      groupMsgMap[groupMsgArray[j].ToGroupId].msgs.push(groupMsgArray[j]); //新增一条消息
                      if (groupMsgArray[j].MsgSeq < groupMsgs.min) { //记录最小的消息seq
                          groupMsgMap[groupMsgArray[j].ToGroupId].min = groupMsgArray[j].MsgSeq;
                      }
                      if (groupMsgArray[j].MsgSeq > groupMsgs.max) { //记录最大的消息seq
                          groupMsgMap[groupMsgArray[j].ToGroupId].max = groupMsgArray[j].MsgSeq;
                      }
                  }
  
                  for (var groupId in groupMsgMap) {
                      var tempCount = groupMsgMap[groupId].max - groupMsgMap[groupId].min + 1; //收到的新的群消息数
                      var curMaxMsgSeq = myGroupMaxSeqs[groupId]; //获取本地保存的群最大消息seq
                      if (curMaxMsgSeq) { //存在这个群的最大消息seq
                          //高并发情况下，长轮询可能存在丢消息，这时需要客户端通过拉取群消息接口补齐下
                          //1、如果收到的新消息最小seq比当前最大群消息seq大于1，则表示收到的群消息发生跳跃，需要补齐
                          //2、收到的新群消息seq存在不连续情况，也需要补齐
                          if (groupMsgMap[groupId].min - curMaxMsgSeq > 1 || groupMsgMap[groupId].msgs.length < tempCount) {
                              //发起一个拉群群消息请求
                              log.warn("发起一次补齐群消息请求,curMaxMsgSeq=" + curMaxMsgSeq + ", minMsgSeq=" + groupMsgMap[groupId].min + ", maxMsgSeq=" + groupMsgMap[groupId].max + ", msgs.length=" + groupMsgMap[groupId].msgs.length + ", tempCount=" + tempCount);
                              getLostGroupMsgs(groupId, groupMsgMap[groupId].max, groupMsgMap[groupId].max - curMaxMsgSeq);
                              //更新myGroupMaxSeqs中的群最大seq
                              updateMyGroupCurMaxSeq(groupId, groupMsgMap[groupId].max);
                          } else {
                              new_group_msgs = addGroupMsgList(groupMsgMap[groupId].msgs, new_group_msgs);
                          }
                      } else { //不存在该群的最大消息seq
                          log.warn("不存在该群的最大消息seq，群id=" + groupId);
                          //高并发情况下，长轮询可能存在丢消息，这时需要客户端通过拉取群消息接口补齐下
                          //1、收到的新群消息seq存在不连续情况，也需要补齐
                          if (groupMsgMap[groupId].msgs.length < tempCount) {
                              //发起一个拉群群消息请求
                              log.warn("发起一次补齐群消息请求,minMsgSeq=" + groupMsgMap[groupId].min + ", maxMsgSeq=" + groupMsgMap[groupId].max + ", msgs.length=" + groupMsgMap[groupId].msgs.length + ", tempCount=" + tempCount);
                              getLostGroupMsgs(groupId, groupMsgMap[groupId].max, tempCount);
                              //更新myGroupMaxSeqs中的群最大seq
                              updateMyGroupCurMaxSeq(groupId, groupMsgMap[groupId].max);
                          } else {
                              new_group_msgs = addGroupMsgList(groupMsgMap[groupId].msgs, new_group_msgs);
                          }
                      }
                  }
                  if (new_group_msgs.length) {
                      MsgStore.updateTimeline();
                  }
                  if (onMsgCallback && new_group_msgs.length) onMsgCallback(new_group_msgs);
  
              };
  
              //处理新的群提示消息
              var handlerGroupTips = function(groupTips) {
                  var new_group_msgs = [];
                  for (var o in groupTips) {
                      var groupTip = groupTips[o];
                      //添加event字段
                      groupTip.Event = LONG_POLLINNG_EVENT_TYPE.GROUP_TIP;
                      //更新群消息通知seq
                      if (groupTip.NoticeSeq > noticeSeq) {
                          noticeSeq = groupTip.NoticeSeq;
                      }
                      var msg = handlerGroupMsg(groupTip, false, true);
                      if (msg) {
                          new_group_msgs.push(msg);
                      }
                  }
                  if (new_group_msgs.length) {
                      MsgStore.updateTimeline();
                  }
                  if (onMsgCallback && new_group_msgs.length) onMsgCallback(new_group_msgs);
              };
  
              //处理新的群系统消息
              //isNeedValidRepeatMsg 是否需要判重
              var handlerGroupSystemMsgs = function(groupSystemMsgs, isNeedValidRepeatMsg) {
                  for (var k in groupSystemMsgs) {
                      var groupTip = groupSystemMsgs[k];
                      var groupReportTypeMsg = groupTip.MsgBody;
                      var reportType = groupReportTypeMsg.ReportType;
                      //当长轮询返回的群系统消息，才需要更新群消息通知seq
                      if (isNeedValidRepeatMsg == false && groupTip.NoticeSeq && groupTip.NoticeSeq > noticeSeq) {
                          noticeSeq = groupTip.NoticeSeq;
                      }
                      var toAccount = groupTip.GroupInfo.To_Account;
                      //过滤本不应该给自己的系统消息
                      /*if (!toAccount || toAccount != ctx.identifier) {
                   log.error("收到本不应该给自己的系统消息: To_Account=" + toAccount);
                   continue;
                   }*/
                      if (isNeedValidRepeatMsg) {
                          //var key=groupTip.ToGroupId+"_"+reportType+"_"+groupTip.MsgTimeStamp+"_"+groupReportTypeMsg.Operator_Account;
                          var key = groupTip.ToGroupId + "_" + reportType + "_" + groupReportTypeMsg.Operator_Account;
                          var isExist = groupSystemMsgsCache[key];
                          if (isExist) {
                              log.warn("收到重复的群系统消息：key=" + key);
                              continue;
                          }
                          groupSystemMsgsCache[key] = true;
                      }
  
                      var notify = {
                          "SrcFlag": 0,
                          "ReportType": reportType,
                          "GroupId": groupTip.ToGroupId,
                          "GroupName": groupTip.GroupInfo.GroupName,
                          "Operator_Account": groupReportTypeMsg.Operator_Account,
                          "MsgTime": groupTip.MsgTimeStamp,
                          "groupReportTypeMsg": groupReportTypeMsg
                      };
                      switch (reportType) {
                          case GROUP_SYSTEM_TYPE.JOIN_GROUP_REQUEST: //申请加群(只有管理员会接收到)
                              notify["RemarkInfo"] = groupReportTypeMsg.RemarkInfo;
                              notify["MsgKey"] = groupReportTypeMsg.MsgKey;
                              notify["Authentication"] = groupReportTypeMsg.Authentication;
                              notify["UserDefinedField"] = groupTip.UserDefinedField;
                              notify["From_Account"] = groupTip.From_Account;
                              notify["MsgSeq"] = groupTip.ClientSeq;
                              notify["MsgRandom"] = groupTip.MsgRandom;
                              break;
                          case GROUP_SYSTEM_TYPE.JOIN_GROUP_ACCEPT: //申请加群被同意(只有申请人自己接收到)
                          case GROUP_SYSTEM_TYPE.JOIN_GROUP_REFUSE: //申请加群被拒绝(只有申请人自己接收到)
                              notify["RemarkInfo"] = groupReportTypeMsg.RemarkInfo;
                              break;
                          case GROUP_SYSTEM_TYPE.KICK: //被管理员踢出群(只有被踢者接收到)
                          case GROUP_SYSTEM_TYPE.DESTORY: //群被解散(全员接收)
                          case GROUP_SYSTEM_TYPE.CREATE: //创建群(创建者接收, 不展示)
                          case GROUP_SYSTEM_TYPE.INVITED_JOIN_GROUP_REQUEST: //邀请加群(被邀请者接收)
                          case GROUP_SYSTEM_TYPE.INVITED_JOIN_GROUP_REQUEST_AGREE: //邀请加群(被邀请者需同意)
                          case GROUP_SYSTEM_TYPE.QUIT: //主动退群(主动退出者接收, 不展示)
                          case GROUP_SYSTEM_TYPE.SET_ADMIN: //群设置管理员(被设置者接收)
                          case GROUP_SYSTEM_TYPE.CANCEL_ADMIN: //取消管理员(被取消者接收)
                          case GROUP_SYSTEM_TYPE.REVOKE: //群已被回收(全员接收, 不展示)
                              break;
                          case GROUP_SYSTEM_TYPE.READED: //群消息已读同步
                              break;
                          case GROUP_SYSTEM_TYPE.CUSTOM: //用户自定义通知(默认全员接收)
                              notify["MsgSeq"] = groupTip.MsgSeq;
                              notify["UserDefinedField"] = groupReportTypeMsg.UserDefinedField;
                              break;
                          default:
                              log.error("未知群系统消息类型：reportType=" + reportType);
                              break;
                      }
  
                                  if (isNeedValidRepeatMsg) {
                                      //注释只收取一种通知
                                      // if (reportType == GROUP_SYSTEM_TYPE.JOIN_GROUP_REQUEST) {
                                      //回调
                                      if (onGroupSystemNotifyCallbacks[reportType]) {
                                          onGroupSystemNotifyCallbacks[reportType](notify);
                                      } else {
                                          log.error("未知群系统消息类型：reportType=" + reportType);
                                      }
                                      //}
                                  } else {
                                      //回调
                                      if (onGroupSystemNotifyCallbacks[reportType]) {
                                          if (reportType == GROUP_SYSTEM_TYPE.READED) {
                                              var arr = notify.groupReportTypeMsg.GroupReadInfoArray;
                                              for (var i = 0, l = arr.length; i < l; i++) {
                                                  var item = arr[i];
                                                  onGroupSystemNotifyCallbacks[reportType](item);
                                              }
                                          } else {
                                              onGroupSystemNotifyCallbacks[reportType](notify);
                                          }
                                      }
                                  }
                              } //loop
                          };
  
  
              //处理新的好友系统通知
              //isNeedValidRepeatMsg 是否需要判重
              var handlerFriendSystemNotices = function(friendSystemNotices, isNeedValidRepeatMsg) {
                  var friendNotice, type, notify;
                  for (var k in friendSystemNotices) {
                      friendNotice = friendSystemNotices[k];
                      type = friendNotice.PushType;
                      //当长轮询返回的群系统消息，才需要更新通知seq
                      if (isNeedValidRepeatMsg == false && friendNotice.NoticeSeq && friendNotice.NoticeSeq > noticeSeq) {
                          noticeSeq = friendNotice.NoticeSeq;
                      }
                      notify = {
                          'Type': type
                      };
                      switch (type) {
                          case FRIEND_NOTICE_TYPE.FRIEND_ADD: //好友表增加
                              notify["Accounts"] = friendNotice.FriendAdd_Account;
                              break;
                          case FRIEND_NOTICE_TYPE.FRIEND_DELETE: //好友表删除
                              notify["Accounts"] = friendNotice.FriendDel_Account;
                              break;
                          case FRIEND_NOTICE_TYPE.PENDENCY_ADD: //未决增加
                              notify["PendencyList"] = friendNotice.PendencyAdd;
                              break;
                          case FRIEND_NOTICE_TYPE.PENDENCY_DELETE: //未决删除
                              notify["Accounts"] = friendNotice.FrienPencydDel_Account;
                              break;
                          case FRIEND_NOTICE_TYPE.BLACK_LIST_ADD: //黑名单增加
                              notify["Accounts"] = friendNotice.BlackListAdd_Account;
                              break;
                          case FRIEND_NOTICE_TYPE.BLACK_LIST_DELETE: //黑名单删除
                              notify["Accounts"] = friendNotice.BlackListDel_Account;
                              break;
                              /*case FRIEND_NOTICE_TYPE.PENDENCY_REPORT://未决已读上报
  
                       break;
                       case FRIEND_NOTICE_TYPE.FRIEND_UPDATE://好友数据更新
  
                       break;
                       */
                          default:
                              log.error("未知好友系统通知类型：friendNotice=" + JSON.stringify(friendNotice));
                              break;
                      }
  
                      if (isNeedValidRepeatMsg) {
                          if (type == FRIEND_NOTICE_TYPE.PENDENCY_ADD) {
                              //回调
                              if (onFriendSystemNotifyCallbacks[type]) onFriendSystemNotifyCallbacks[type](notify);
                          }
                      } else {
                          //回调
                          if (onFriendSystemNotifyCallbacks[type]) onFriendSystemNotifyCallbacks[type](notify);
                      }
                  } //loop
              };
  
              //处理新的资料系统通知
              //isNeedValidRepeatMsg 是否需要判重
              var handlerProfileSystemNotices = function(profileSystemNotices, isNeedValidRepeatMsg) {
                  var profileNotice, type, notify;
                  for (var k in profileSystemNotices) {
                      profileNotice = profileSystemNotices[k];
                      type = profileNotice.PushType;
                      //当长轮询返回的群系统消息，才需要更新通知seq
                      if (isNeedValidRepeatMsg == false && profileNotice.NoticeSeq && profileNotice.NoticeSeq > noticeSeq) {
                          noticeSeq = profileNotice.NoticeSeq;
                      }
                      notify = {
                          'Type': type
                      };
                      switch (type) {
                          case PROFILE_NOTICE_TYPE.PROFILE_MODIFY: //资料修改
                              notify["Profile_Account"] = profileNotice.Profile_Account;
                              notify["ProfileList"] = profileNotice.ProfileList;
                              break;
                          default:
                              log.error("未知资料系统通知类型：profileNotice=" + JSON.stringify(profileNotice));
                              break;
                      }
  
                      if (isNeedValidRepeatMsg) {
                          if (type == PROFILE_NOTICE_TYPE.PROFILE_MODIFY) {
                              //回调
                              if (onProfileSystemNotifyCallbacks[type]) onProfileSystemNotifyCallbacks[type](notify);
                          }
                      } else {
                          //回调
                          if (onProfileSystemNotifyCallbacks[type]) onProfileSystemNotifyCallbacks[type](notify);
                      }
                  } //loop
              };
  
              //处理新的群系统消息(用于直播大群长轮询)
              var handlerGroupSystemMsg = function(groupTip) {
                  var groupReportTypeMsg = groupTip.MsgBody;
                  var reportType = groupReportTypeMsg.ReportType;
                  var toAccount = groupTip.GroupInfo.To_Account;
                  //过滤本不应该给自己的系统消息
                  //if(!toAccount || toAccount!=ctx.identifier){
                  //    log.error("收到本不应该给自己的系统消息: To_Account="+toAccount);
                  //    continue;
                  //}
                  var notify = {
                      "SrcFlag": 1,
                      "ReportType": reportType,
                      "GroupId": groupTip.ToGroupId,
                      "GroupName": groupTip.GroupInfo.GroupName,
                      "Operator_Account": groupReportTypeMsg.Operator_Account,
                      "MsgTime": groupTip.MsgTimeStamp
                  };
                  switch (reportType) {
                      case GROUP_SYSTEM_TYPE.JOIN_GROUP_REQUEST: //申请加群(只有管理员会接收到)
                          notify["RemarkInfo"] = groupReportTypeMsg.RemarkInfo;
                          notify["MsgKey"] = groupReportTypeMsg.MsgKey;
                          notify["Authentication"] = groupReportTypeMsg.Authentication;
                          notify["UserDefinedField"] = groupTip.UserDefinedField;
                          notify["From_Account"] = groupTip.From_Account;
                          notify["MsgSeq"] = groupTip.ClientSeq;
                          notify["MsgRandom"] = groupTip.MsgRandom;
                          break;
                      case GROUP_SYSTEM_TYPE.JOIN_GROUP_ACCEPT: //申请加群被同意(只有申请人自己接收到)
                      case GROUP_SYSTEM_TYPE.JOIN_GROUP_REFUSE: //申请加群被拒绝(只有申请人自己接收到)
                          notify["RemarkInfo"] = groupReportTypeMsg.RemarkInfo;
                          break;
                      case GROUP_SYSTEM_TYPE.KICK: //被管理员踢出群(只有被踢者接收到)
                      case GROUP_SYSTEM_TYPE.DESTORY: //群被解散(全员接收)
                      case GROUP_SYSTEM_TYPE.CREATE: //创建群(创建者接收, 不展示)
                      case GROUP_SYSTEM_TYPE.INVITED_JOIN_GROUP_REQUEST: //邀请加群(被邀请者接收)
                      case GROUP_SYSTEM_TYPE.INVITED_JOIN_GROUP_REQUEST_AGREE: //邀请加群(被邀请者需要同意)
                      case GROUP_SYSTEM_TYPE.QUIT: //主动退群(主动退出者接收, 不展示)
                      case GROUP_SYSTEM_TYPE.SET_ADMIN: //群设置管理员(被设置者接收)
                      case GROUP_SYSTEM_TYPE.CANCEL_ADMIN: //取消管理员(被取消者接收)
                      case GROUP_SYSTEM_TYPE.REVOKE: //群已被回收(全员接收, 不展示)
                          break;
                      case GROUP_SYSTEM_TYPE.CUSTOM: //用户自定义通知(默认全员接收)
                          notify["MsgSeq"] = groupTip.MsgSeq;
                          notify["UserDefinedField"] = groupReportTypeMsg.UserDefinedField;
                          break;
                      default:
                          log.error("未知群系统消息类型：reportType=" + reportType);
                          break;
                  }
                  //回调
                  if (onGroupSystemNotifyCallbacks[reportType]) onGroupSystemNotifyCallbacks[reportType](notify);
  
              };
  
              //处理C2C EVENT 消息通道Array
              var handlerC2cNotifyMsgArray = function(arr) {
                  for (var i = 0, l = arr.length; i < l; i++) {
                      handlerC2cEventMsg(arr[i]);
                  }
              }
  
              //处理C2C EVENT 消息通道Item
              var handlerC2cEventMsg = function(notify) {
                  var subType = notify.SubMsgType;
                  switch (subType) {
                      case C2C_EVENT_SUB_TYPE.READED: //已读通知
                          break;
                      case C2C_EVENT_SUB_TYPE.KICKEDOUT: //已读通知
  	                    if (onC2cEventCallbacks[subType]) {
  	                        onC2cEventCallbacks[subType]();
  	                    }
                      	break;
                      default:
                                      log.error("未知C2c系统消息：subType=" + subType);
                          break;
                  }
                  // stopPolling = true;
                  //回调onMsgReadCallback
                  if (notify.ReadC2cMsgNotify.UinPairReadArray && onC2cEventCallbacks[subType]) {
                      for (var i = 0, l = notify.ReadC2cMsgNotify.UinPairReadArray.length; i < l; i++) {
                          var item = notify.ReadC2cMsgNotify.UinPairReadArray[i];
                          onC2cEventCallbacks[subType](item);
                      }
                  }
              };
  
              //长轮询
              this.longPolling = function(cbOk, cbErr) {
  
  
                  var opts = {
                      'Timeout': longPollingDefaultTimeOut / 1000,
                      'Cookie': {
                          'NotifySeq': notifySeq,
                          'NoticeSeq': noticeSeq
                      }
                  };
                  if (LongPollingId) {
                      opts.Cookie.LongPollingId = LongPollingId;
                      doPolling();
                  } else {
                      proto_getLongPollingId({}, function(resp) {
                          LongPollingId = opts.Cookie.LongPollingId = resp.LongPollingId;
                          //根据回包设置超时时间，超时时长不能>60秒，因为webkit手机端的最长超时时间不能大于60s
                          longPollingDefaultTimeOut = resp.Timeout > 60 ? longPollingDefaultTimeOut : resp.Timeout * 1000;
                          doPolling();
                      });
                  }
  
                  function doPolling() {
                      proto_longPolling(opts, function(resp) {
                          for (var i in resp.EventArray) {
                              var e = resp.EventArray[i];
                              switch (e.Event) {
                                  case LONG_POLLINNG_EVENT_TYPE.C2C: //c2c消息通知
                                      //更新C2C消息通知seq
                                      notifySeq = e.NotifySeq;
                                      log.warn("longpolling: received new c2c msg");
                                      //获取新消息
                                      MsgManager.syncMsgs();
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.GROUP_COMMON: //普通群消息通知
                                      log.warn("longpolling: received new group msgs");
                                      handlerOrdinaryAndTipGroupMsgs(e.Event, e.GroupMsgArray);
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.GROUP_TIP: //（全员广播）群提示消息
                                                  log.warn("longpolling: received new group tips");
                                                  handlerOrdinaryAndTipGroupMsgs(e.Event, e.GroupTips);
                                                  break;
                                              case LONG_POLLINNG_EVENT_TYPE.GROUP_TIP2: //群提示消息
                                      log.warn("longpolling: received new group tips");
                                      handlerOrdinaryAndTipGroupMsgs(e.Event, e.GroupTips);
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.GROUP_SYSTEM: //（多终端同步）群系统消息
                                      log.warn("longpolling: received new group system msgs");
                                      //false 表示 通过长轮询收到的群系统消息，可以不判重
                                      handlerGroupSystemMsgs(e.GroupTips, false);
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.FRIEND_NOTICE: //好友系统通知
                                      log.warn("longpolling: received new friend system notice");
                                      //false 表示 通过长轮询收到的好友系统通知，可以不判重
                                      handlerFriendSystemNotices(e.FriendListMod, false);
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.PROFILE_NOTICE: //资料系统通知
                                      log.warn("longpolling: received new profile system notice");
                                      //false 表示 通过长轮询收到的资料系统通知，可以不判重
                                      handlerProfileSystemNotices(e.ProfileDataMod, false);
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.C2C_COMMON: //c2c消息通知
                                      noticeSeq = e.C2cMsgArray[0].NoticeSeq;
                                      //更新C2C消息通知seq
                                      log.warn("longpolling: received new c2c_common msg", noticeSeq);
                                      handlerOrdinaryAndTipC2cMsgs(e.Event, e.C2cMsgArray);
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.C2C_EVENT: //c2c已读消息通知
                                      noticeSeq = e.C2cNotifyMsgArray[0].NoticeSeq;
                                      log.warn("longpolling: received new c2c_event msg");
                                      handlerC2cNotifyMsgArray(e.C2cNotifyMsgArray);
                                      break;
                                  default:
                                      log.error("longpolling收到未知新消息类型: Event=" + e.Event);
                                      break;
                              }
                          }
                          var successInfo = {
                              'ActionStatus': ACTION_STATUS.OK,
                              'ErrorCode': 0
                          };
                          updatecLongPollingStatus(successInfo);
                      }, function(err) {
                          //log.error(err);
                          updatecLongPollingStatus(err);
                          if (cbErr) cbErr(err);
                      });
                  }
              };
  
  
              //大群 长轮询
              this.bigGroupLongPolling = function(cbOk, cbErr) {
                  var GroupId = BigGroupId;
                  var opts = {
                      'USP': 1,
                      'StartSeq': bigGroupLongPollingStartSeq, //请求拉消息的起始seq
                      'HoldTime': bigGroupLongPollingHoldTime, //客户端长轮询的超时时间，单位是秒
                      'Key': bigGroupLongPollingKey //客户端加入群组后收到的的Key
                  };
  
                  proto_bigGroupLongPolling(opts, function(resp) {
                      if (GroupId != BigGroupId) return;
  
                      var msgObjList = [];
                      bigGroupLongPollingStartSeq = resp.NextSeq;
                      bigGroupLongPollingHoldTime = resp.HoldTime;
                      bigGroupLongPollingKey = resp.Key;
  
                      if (resp.RspMsgList && resp.RspMsgList.length > 0) {
                          var msgCount = 0,
                              msgInfo, event, msg;
                          for (var i = resp.RspMsgList.length - 1; i >= 0; i--) {
                              msgInfo = resp.RspMsgList[i];
                              //后台这里做了调整，缩短字段名，以下是兼容代码
                              var keyMap = {
                                  "F_Account" : "From_Account",
                                  "T_Account" : "To_Account",
                                  "FAType" : "EnumFrom_AccountType",
                                  "TAType" : "EnumTo_AccountType",
                                  "GCode" : "GroupCode",
                                  "GName" : "GroupName",
                                  "GId" : "GroupId",
                                  "MFlg" : "MsgFlag",
                                  "FAEInfo" : "MsgFrom_AccountExtraInfo",
                                  "Evt" : "Event",
                                  "GInfo" : "GroupInfo",
                                  "BPlc" : "IsPlaceMsg",
                                  "MBody" : "MsgBody",
                                  "Pri" : "MsgPriority",
                                  "Rdm" : "MsgRandom",
                                  "MSeq" : "MsgSeq",
                                  "TStp" : "MsgTimeStamp",
                                  "TGId" : "ToGroupId",
                                  "UEInfo" : "UinExtInfo",
                                  "UId" : "UserId",
                                  "BSys" : "IsSystemMsg",
                                  "FAHUrl" : "From_AccountHeadurl",
                                  "FANick" : "From_AccountNick"
                              };
                              msgInfo = tool.replaceObject(keyMap, msgInfo);
                              //如果是已经删除的消息或者发送者帐号为空或者消息内容为空
                              //IsPlaceMsg=1
                              if (msgInfo.IsPlaceMsg || !msgInfo.From_Account || !msgInfo.MsgBody || msgInfo.MsgBody.length == 0) {
                                  continue;
                              }
  
                              event = msgInfo.Event; //群消息类型
                              switch (event) {
                                  case LONG_POLLINNG_EVENT_TYPE.GROUP_COMMON: //群普通消息
                                      log.info("bigGroupLongPolling: return new group msg");
                                      msg = handlerGroupMsg(msgInfo, false, false);
                                      msg && msgObjList.push(msg);
                                      msgCount = msgCount + 1;
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.GROUP_TIP: //群提示消息
                                  case LONG_POLLINNG_EVENT_TYPE.GROUP_TIP2: //群提示消息
                                      log.info("bigGroupLongPolling: return new group tip");
                                      msg = handlerGroupMsg(msgInfo, false, false);
                                      msg && msgObjList.push(msg);
                                      //msgCount=msgCount+1;
                                      break;
                                  case LONG_POLLINNG_EVENT_TYPE.GROUP_SYSTEM: //群系统消息
                                      log.info("bigGroupLongPolling: new group system msg");
                                      handlerGroupSystemMsg(msgInfo);
                                      break;
                                  default:
                                      log.error("bigGroupLongPolling收到未知新消息类型: Event=" + event);
                                      break;
                              }
                          } // for loop
                          if (msgCount > 0) {
                              MsgManager.setBigGroupLongPollingMsgMap(msgInfo.ToGroupId, msgCount); //
                              log.warn("current bigGroupLongPollingMsgMap: " + JSON.stringify(bigGroupLongPollingMsgMap));
                          }
                      }
                      curBigGroupLongPollingRetErrorCount = 0;
                      //返回连接状态
                      var successInfo = {
                          'ActionStatus': ACTION_STATUS.OK,
                          'ErrorCode': CONNECTION_STATUS.ON,
                          'ErrorInfo': 'connection is ok...'
                      };
                      ConnManager.callBack(successInfo);
  
                      if (cbOk) cbOk(msgObjList);
                      else if (onBigGroupMsgCallback) onBigGroupMsgCallback(msgObjList); //返回新消息
  
                      //重新启动长轮询
                      bigGroupLongPollingOn && MsgManager.bigGroupLongPolling();
  
                  }, function(err) {
                      //
                      if (err.ErrorCode != longPollingTimeOutErrorCode) {
                          log.error(err.ErrorInfo);
                          //记录长轮询返回错误次数
                          curBigGroupLongPollingRetErrorCount++;
                      }
                                  if (err.ErrorCode == longPollingKickedErrorCode) {
                          //登出
                          log.error("多实例登录，被kick");
                          if (onKickedEventCall) {
                              onKickedEventCall();
                          }
                      }
                      //累计超过一定次数，不再发起长轮询请求
                      if (curBigGroupLongPollingRetErrorCount < LONG_POLLING_MAX_RET_ERROR_COUNT) {
                          bigGroupLongPollingOn && MsgManager.bigGroupLongPolling();
                      } else {
                          var errInfo = {
                              'ActionStatus': ACTION_STATUS.FAIL,
                              'ErrorCode': CONNECTION_STATUS.OFF,
                              'ErrorInfo': 'connection is off'
                          };
                          ConnManager.callBack(errInfo);
                      }
                      if (cbErr) cbErr(err);
  
                  }, bigGroupLongPollingHoldTime * 1000);
              };
  
              //更新连接状态
              var updatecLongPollingStatus = function(errObj) {
                  if (errObj.ErrorCode == 0 || errObj.ErrorCode == longPollingTimeOutErrorCode) {
                      curLongPollingRetErrorCount = 0;
                      longPollingOffCallbackFlag = false;
                      var errorInfo;
                      var isNeedCallback = false;
                      switch (curLongPollingStatus) {
                          case CONNECTION_STATUS.INIT:
                              isNeedCallback = true;
                              curLongPollingStatus = CONNECTION_STATUS.ON;
                              errorInfo = "create connection successfully(INIT->ON)";
                              break;
                          case CONNECTION_STATUS.ON:
                              errorInfo = "connection is on...(ON->ON)";
                              break;
                          case CONNECTION_STATUS.RECONNECT:
                              curLongPollingStatus = CONNECTION_STATUS.ON;
                              errorInfo = "connection is on...(RECONNECT->ON)";
                              break;
                          case CONNECTION_STATUS.OFF:
                              isNeedCallback = true;
                              curLongPollingStatus = CONNECTION_STATUS.RECONNECT;
                              errorInfo = "reconnect successfully(OFF->RECONNECT)";
                              break;
                      }
                      var successInfo = {
                          'ActionStatus': ACTION_STATUS.OK,
                          'ErrorCode': curLongPollingStatus,
                          'ErrorInfo': errorInfo
                      };
                      isNeedCallback && ConnManager.callBack(successInfo);
                      longPollingOn && MsgManager.longPolling();
                  } else if (errObj.ErrorCode == longPollingKickedErrorCode) {
                      //登出
                      log.error("多实例登录，被kick");
                      if (onKickedEventCall) {
                          onKickedEventCall();
                      }
                  } else {
                      //记录长轮询返回解析json错误次数
                      curLongPollingRetErrorCount++;
                      log.warn("longPolling接口第" + curLongPollingRetErrorCount + "次报错: " + errObj.ErrorInfo);
                      //累计超过一定次数
                      if (curLongPollingRetErrorCount <= LONG_POLLING_MAX_RET_ERROR_COUNT) {
                          setTimeout(startNextLongPolling, 100); //
                      } else {
                          curLongPollingStatus = CONNECTION_STATUS.OFF;
                          var errInfo = {
                              'ActionStatus': ACTION_STATUS.FAIL,
                              'ErrorCode': CONNECTION_STATUS.OFF,
                              'ErrorInfo': 'connection is off'
                          };
                          longPollingOffCallbackFlag == false && ConnManager.callBack(errInfo);
                          longPollingOffCallbackFlag = true;
                          log.warn(longPollingIntervalTime + "毫秒之后,SDK会发起新的longPolling请求...");
                          setTimeout(startNextLongPolling, longPollingIntervalTime); //长轮询接口报错次数达到一定值，每间隔5s发起新的长轮询
                      }
                  }
              };
  
              //处理收到的普通C2C消息
              var handlerOrdinaryAndTipC2cMsgs = function(eventType, C2cMsgArray) {
                  //处理c2c消息
                  var notifyInfo = [];
                  var msgInfos = [];
                  msgInfos = C2cMsgArray; //返回的消息列表
                  // MsgStore.cookie = resp.Cookie;//cookies，记录当前读到的最新消息位置
  
                  for (var i in msgInfos) {
                      var msgInfo = msgInfos[i];
                      var isSendMsg, id, headUrl;
                      if (msgInfo.From_Account == ctx.identifier) { //当前用户发送的消息
                          isSendMsg = true;
                          id = msgInfo.To_Account; //读取接收者信息
                          headUrl = '';
                      } else { //当前用户收到的消息
                          isSendMsg = false;
                          id = msgInfo.From_Account; //读取发送者信息
                          headUrl = '';
                      }
                      var sess = MsgStore.sessByTypeId(SESSION_TYPE.C2C, id);
                      if (!sess) {
                          sess = new Session(SESSION_TYPE.C2C, id, id, headUrl, 0, 0);
                      }
                      var msg = new Msg(sess, isSendMsg, msgInfo.MsgSeq, msgInfo.MsgRandom, msgInfo.MsgTimeStamp, msgInfo.From_Account);
                      var msgBody = null;
                      var msgContent = null;
                      var msgType = null;
                      for (var mi in msgInfo.MsgBody) {
                          msgBody = msgInfo.MsgBody[mi];
                          msgType = msgBody.MsgType;
                          switch (msgType) {
                              case MSG_ELEMENT_TYPE.TEXT:
                                  msgContent = new Msg.Elem.Text(msgBody.MsgContent.Text);
                                  break;
                              case MSG_ELEMENT_TYPE.FACE:
                                  msgContent = new Msg.Elem.Face(
                                      msgBody.MsgContent.Index,
                                      msgBody.MsgContent.Data
                                  );
                                  break;
                              case MSG_ELEMENT_TYPE.IMAGE:
                                  msgContent = new Msg.Elem.Images(
                                                  msgBody.MsgContent.UUID,
                                                  msgBody.MsgContent.ImageFormat || ""
                                  );
                                  for (var j in msgBody.MsgContent.ImageInfoArray) {
                                      var tempImg = msgBody.MsgContent.ImageInfoArray[j];
                                      msgContent.addImage(
                                          new Msg.Elem.Images.Image(
                                              tempImg.Type,
                                              tempImg.Size,
                                              tempImg.Width,
                                              tempImg.Height,
                                              tempImg.URL
                                          )
                                      );
                                  }
                                  break;
                              case MSG_ELEMENT_TYPE.SOUND:
                                  if (msgBody.MsgContent) {
                                      msgContent = new Msg.Elem.Sound(
                                          msgBody.MsgContent.UUID,
                                          msgBody.MsgContent.Second,
                                          msgBody.MsgContent.Size,
                                          msgInfo.From_Account,
                                          msgInfo.To_Account,
                                          msgBody.MsgContent.Download_Flag,
                                          SESSION_TYPE.C2C
                                      );
                                  } else {
                                      msgType = MSG_ELEMENT_TYPE.TEXT;
                                      msgContent = new Msg.Elem.Text('[语音消息]下载地址解析出错');
                                  }
                                  break;
                              case MSG_ELEMENT_TYPE.LOCATION:
                                  msgContent = new Msg.Elem.Location(
                                      msgBody.MsgContent.Longitude,
                                      msgBody.MsgContent.Latitude,
                                      msgBody.MsgContent.Desc
                                  );
                                  break;
                              case MSG_ELEMENT_TYPE.FILE:
                              case MSG_ELEMENT_TYPE.FILE + " ":
                                  msgType = MSG_ELEMENT_TYPE.FILE;
                                  if (msgBody.MsgContent) {
                                      msgContent = new Msg.Elem.File(
                                          msgBody.MsgContent.UUID,
                                          msgBody.MsgContent.FileName,
                                          msgBody.MsgContent.FileSize,
                                          msgInfo.From_Account,
                                          msgInfo.To_Account,
                                          msgBody.MsgContent.Download_Flag,
                                          SESSION_TYPE.C2C
                                      );
                                  } else {
                                      msgType = MSG_ELEMENT_TYPE.TEXT;
                                      msgContent = new Msg.Elem.Text('[文件消息下载地址解析出错]');
                                  }
                                  break;
                              case MSG_ELEMENT_TYPE.CUSTOM:
                                  try {
                                      var data = JSON.parse(msgBody.MsgContent.Data);
                                      if (data && data.userAction && data.userAction == FRIEND_WRITE_MSG_ACTION.ING) { //过滤安卓或ios的正在输入自定义消息
                                          continue;
                                      }
                                  } catch (e) {}
  
                                  msgType = MSG_ELEMENT_TYPE.CUSTOM;
                                  msgContent = new Msg.Elem.Custom(
                                      msgBody.MsgContent.Data,
                                      msgBody.MsgContent.Desc,
                                      msgBody.MsgContent.Ext
                                  );
                                  break;
                              default:
                                  msgType = MSG_ELEMENT_TYPE.TEXT;
                                  msgContent = new Msg.Elem.Text('web端暂不支持' + msgBody.MsgType + '消息');
                                  break;
                          }
                          msg.elems.push(new Msg.Elem(msgType, msgContent));
                      }
  
                      if (msg.elems.length > 0 && MsgStore.addMsg(msg)) {
                          notifyInfo.push(msg);
                      }
                  } // for loop
                  if (notifyInfo.length > 0)
                      MsgStore.updateTimeline();
                  if (notifyInfo.length > 0) {
                      if (onMsgCallback) onMsgCallback(notifyInfo);
                  }
              };
  
              //发起新的长轮询请求
              var startNextLongPolling = function() {
                  longPollingOn && MsgManager.longPolling();
              };
  
              //处理未决的加群申请消息列表
              var handlerApplyJoinGroupSystemMsgs = function(eventArray) {
                  for (var i in eventArray) {
                      var e = eventArray[i];
                      handlerGroupSystemMsgs(e.GroupTips, true);
                      switch (e.Event) {
                          case LONG_POLLINNG_EVENT_TYPE.GROUP_SYSTEM: //（多终端同步）群系统消息
                              log.warn("handlerApplyJoinGroupSystemMsgs： handler new group system msg");
                              //true 表示 解决加群申请通知存在重复的问题（已处理的通知，下次登录还会拉到），需要判重
                              handlerGroupSystemMsgs(e.GroupTips, true);
                              break;
                          default:
                              log.error("syncMsgs收到未知的群系统消息类型: Event=" + e.Event);
                              break;
                      }
                  }
              };
  
              //拉取c2c消息(包含加群未决消息，需要处理)
              this.syncMsgs = function(cbOk, cbErr) {
                  var notifyInfo = [];
                  var msgInfos = [];
                  //读取C2C消息
                  proto_getMsgs(MsgStore.cookie, MsgStore.syncFlag, function(resp) {
                      //拉取完毕
                      if (resp.SyncFlag == 2) {
                          MsgStore.syncFlag = 0;
                      }
                      //处理c2c消息
                      msgInfos = resp.MsgList; //返回的消息列表
                      MsgStore.cookie = resp.Cookie; //cookies，记录当前读到的最新消息位置
  
                      for (var i in msgInfos) {
                          var msgInfo = msgInfos[i];
                          var isSendMsg, id, headUrl;
                          if (msgInfo.From_Account == ctx.identifier) { //当前用户发送的消息
                              isSendMsg = true;
                              id = msgInfo.To_Account; //读取接收者信息
                              headUrl = '';
                          } else { //当前用户收到的消息
                              isSendMsg = false;
                              id = msgInfo.From_Account; //读取发送者信息
                              headUrl = '';
                          }
                          var sess = MsgStore.sessByTypeId(SESSION_TYPE.C2C, id);
                          if (!sess) {
                              sess = new Session(SESSION_TYPE.C2C, id, id, headUrl, 0, 0);
                          }
                          var msg = new Msg(sess, isSendMsg, msgInfo.MsgSeq, msgInfo.MsgRandom, msgInfo.MsgTimeStamp, msgInfo.From_Account);
                          var msgBody = null;
                          var msgContent = null;
                          var msgType = null;
                          for (var mi in msgInfo.MsgBody) {
                              msgBody = msgInfo.MsgBody[mi];
                              msgType = msgBody.MsgType;
                              switch (msgType) {
                                  case MSG_ELEMENT_TYPE.TEXT:
                                      msgContent = new Msg.Elem.Text(msgBody.MsgContent.Text);
                                      break;
                                  case MSG_ELEMENT_TYPE.FACE:
                                      msgContent = new Msg.Elem.Face(
                                          msgBody.MsgContent.Index,
                                          msgBody.MsgContent.Data
                                      );
                                      break;
                                  case MSG_ELEMENT_TYPE.IMAGE:
                                      msgContent = new Msg.Elem.Images(
                                                      msgBody.MsgContent.UUID,
                                                      msgBody.MsgContent.ImageFormat
                                      );
                                      for (var j in msgBody.MsgContent.ImageInfoArray) {
                                          var tempImg = msgBody.MsgContent.ImageInfoArray[j];
                                          msgContent.addImage(
                                              new Msg.Elem.Images.Image(
                                                  tempImg.Type,
                                                  tempImg.Size,
                                                  tempImg.Width,
                                                  tempImg.Height,
                                                  tempImg.URL
                                              )
                                          );
                                      }
                                      break;
                                  case MSG_ELEMENT_TYPE.SOUND:
                                      // var soundUrl = getSoundDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account);
                                      if (msgBody.MsgContent) {
                                          msgContent = new Msg.Elem.Sound(
                                              msgBody.MsgContent.UUID,
                                              msgBody.MsgContent.Second,
                                              msgBody.MsgContent.Size,
                                              msgInfo.From_Account,
                                              msgInfo.To_Account,
                                              msgBody.MsgContent.Download_Flag,
                                              SESSION_TYPE.C2C
                                          );
                                      } else {
                                          msgType = MSG_ELEMENT_TYPE.TEXT;
                                          msgContent = new Msg.Elem.Text('[语音消息]下载地址解析出错');
                                      }
                                      break;
                                  case MSG_ELEMENT_TYPE.LOCATION:
                                      msgContent = new Msg.Elem.Location(
                                          msgBody.MsgContent.Longitude,
                                          msgBody.MsgContent.Latitude,
                                          msgBody.MsgContent.Desc
                                      );
                                      break;
                                  case MSG_ELEMENT_TYPE.FILE:
                                  case MSG_ELEMENT_TYPE.FILE + " ":
                                      msgType = MSG_ELEMENT_TYPE.FILE;
                                      // var fileUrl = getFileDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account, msgBody.MsgContent.FileName);
                                      if (msgBody.MsgContent) {
                                          msgContent = new Msg.Elem.File(
                                              msgBody.MsgContent.UUID,
                                              msgBody.MsgContent.FileName,
                                              msgBody.MsgContent.FileSize,
                                              msgInfo.From_Account,
                                              msgInfo.To_Account,
                                              msgBody.MsgContent.Download_Flag,
                                              SESSION_TYPE.C2C
                                          );
                                      } else {
                                          msgType = MSG_ELEMENT_TYPE.TEXT;
                                          msgContent = new Msg.Elem.Text('[文件消息下载地址解析出错]');
                                      }
                                      break;
                                  case MSG_ELEMENT_TYPE.CUSTOM:
                                      try {
                                          var data = JSON.parse(msgBody.MsgContent.Data);
                                          if (data && data.userAction && data.userAction == FRIEND_WRITE_MSG_ACTION.ING) { //过滤安卓或ios的正在输入自定义消息
                                              continue;
                                          }
                                      } catch (e) {}
  
                                      msgType = MSG_ELEMENT_TYPE.CUSTOM;
                                      msgContent = new Msg.Elem.Custom(
                                          msgBody.MsgContent.Data,
                                          msgBody.MsgContent.Desc,
                                          msgBody.MsgContent.Ext
                                      );
                                      break;
                                  default:
                                      msgType = MSG_ELEMENT_TYPE.TEXT;
                                      msgContent = new Msg.Elem.Text('web端暂不支持' + msgBody.MsgType + '消息');
                                      break;
                              }
                              msg.elems.push(new Msg.Elem(msgType, msgContent));
                          }
  
                          if (msg.elems.length > 0 && MsgStore.addMsg(msg)) {
                              notifyInfo.push(msg);
                          }
                      } // for loop
  
                      //处理加群未决申请消息
                      handlerApplyJoinGroupSystemMsgs(resp.EventArray);
  
                      if (notifyInfo.length > 0)
                          MsgStore.updateTimeline();
                      if (cbOk) cbOk(notifyInfo);
                      else if (notifyInfo.length > 0) {
                          if (onMsgCallback) onMsgCallback(notifyInfo);
                      }
  
                  }, function(err) {
                      log.error("getMsgs failed:" + err.ErrorInfo);
                      if (cbErr) cbErr(err);
                  });
              };
  
  
              //拉取C2C漫游消息
              this.getC2CHistoryMsgs = function(options, cbOk, cbErr) {
  
                  if (!options.Peer_Account) {
                      if (cbErr) {
                          cbErr(tool.getReturnError("Peer_Account is empty", -13));
                          return;
                      }
                  }
                  if (!options.MaxCnt) {
                      options.MaxCnt = 15;
                  }
                  if (options.MaxCnt <= 0) {
                      if (cbErr) {
                          cbErr(tool.getReturnError("MaxCnt should be greater than 0", -14));
                          return;
                      }
                  }
                  if (options.MaxCnt > 15) {
                      if (cbErr) {
                          cbErr(tool.getReturnError("MaxCnt can not be greater than 15", -15));
                          return;
                      }
                      return;
                  }
                  if (options.MsgKey == null || options.MsgKey === undefined) {
                      options.MsgKey = '';
                  }
                  var opts = {
                      'Peer_Account': options.Peer_Account,
                      'MaxCnt': options.MaxCnt,
                      'LastMsgTime': options.LastMsgTime,
                      'MsgKey': options.MsgKey
                  };
                  //读取c2c漫游消息
                  proto_getC2CHistoryMsgs(opts, function(resp) {
                      var msgObjList = [];
                      var msgInfos = [];
                      //处理c2c消息
                      msgInfos = resp.MsgList; //返回的消息列表
                      var sess = MsgStore.sessByTypeId(SESSION_TYPE.C2C, options.Peer_Account);
                      if (!sess) {
                          sess = new Session(SESSION_TYPE.C2C, options.Peer_Account, options.Peer_Account, '', 0, 0);
                      }
                      for (var i in msgInfos) {
                          var msgInfo = msgInfos[i];
                          var isSendMsg, id, headUrl;
                          if (msgInfo.From_Account == ctx.identifier) { //当前用户发送的消息
                              isSendMsg = true;
                              id = msgInfo.To_Account; //读取接收者信息
                              headUrl = '';
                          } else { //当前用户收到的消息
                              isSendMsg = false;
                              id = msgInfo.From_Account; //读取发送者信息
                              headUrl = '';
                          }
                          var msg = new Msg(sess, isSendMsg, msgInfo.MsgSeq, msgInfo.MsgRandom, msgInfo.MsgTimeStamp, msgInfo.From_Account);
                          var msgBody = null;
                          var msgContent = null;
                          var msgType = null;
                          for (var mi in msgInfo.MsgBody) {
                              msgBody = msgInfo.MsgBody[mi];
                              msgType = msgBody.MsgType;
                              switch (msgType) {
                                  case MSG_ELEMENT_TYPE.TEXT:
                                      msgContent = new Msg.Elem.Text(msgBody.MsgContent.Text);
                                      break;
                                  case MSG_ELEMENT_TYPE.FACE:
                                      msgContent = new Msg.Elem.Face(
                                          msgBody.MsgContent.Index,
                                          msgBody.MsgContent.Data
                                      );
                                      break;
                                  case MSG_ELEMENT_TYPE.IMAGE:
                                      msgContent = new Msg.Elem.Images(
                                                      msgBody.MsgContent.UUID,
                                                      msgBody.MsgContent.ImageFormat
                                      );
                                      for (var j in msgBody.MsgContent.ImageInfoArray) {
                                          var tempImg = msgBody.MsgContent.ImageInfoArray[j];
                                          msgContent.addImage(
                                              new Msg.Elem.Images.Image(
                                                  tempImg.Type,
                                                  tempImg.Size,
                                                  tempImg.Width,
                                                  tempImg.Height,
                                                  tempImg.URL
                                              )
                                          );
                                      }
                                      break;
                                  case MSG_ELEMENT_TYPE.SOUND:
  
                                      // var soundUrl = getSoundDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account);
  
                                      if (msgBody.MsgContent) {
                                          msgContent = new Msg.Elem.Sound(
                                              msgBody.MsgContent.UUID,
                                              msgBody.MsgContent.Second,
                                              msgBody.MsgContent.Size,
                                              msgInfo.From_Account,
                                              msgInfo.To_Account,
                                              msgBody.MsgContent.Download_Flag,
                                              SESSION_TYPE.C2C
                                          );
                                      } else {
                                          msgType = MSG_ELEMENT_TYPE.TEXT;
                                          msgContent = new Msg.Elem.Text('[语音消息]下载地址解析出错');
                                      }
                                      break;
                                  case MSG_ELEMENT_TYPE.LOCATION:
                                      msgContent = new Msg.Elem.Location(
                                          msgBody.MsgContent.Longitude,
                                          msgBody.MsgContent.Latitude,
                                          msgBody.MsgContent.Desc
                                      );
                                      break;
                                  case MSG_ELEMENT_TYPE.FILE:
                                  case MSG_ELEMENT_TYPE.FILE + " ":
                                      msgType = MSG_ELEMENT_TYPE.FILE;
                                      // var fileUrl = getFileDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account, msgBody.MsgContent.FileName);
  
                                      if (msgBody.MsgContent) {
                                          msgContent = new Msg.Elem.File(
                                              msgBody.MsgContent.UUID,
                                              msgBody.MsgContent.FileName,
                                              msgBody.MsgContent.FileSize,
                                              msgInfo.From_Account,
                                              msgInfo.To_Account,
                                              msgBody.MsgContent.Download_Flag,
                                              SESSION_TYPE.C2C
                                          );
                                      } else {
                                          msgType = MSG_ELEMENT_TYPE.TEXT;
                                          msgContent = new Msg.Elem.Text('[文件消息下载地址解析出错]');
                                      }
                                      break;
                                  case MSG_ELEMENT_TYPE.CUSTOM:
                                      msgType = MSG_ELEMENT_TYPE.CUSTOM;
                                      msgContent = new Msg.Elem.Custom(
                                          msgBody.MsgContent.Data,
                                          msgBody.MsgContent.Desc,
                                          msgBody.MsgContent.Ext
                                      );
  
                                      break;
                                  default:
                                      msgType = MSG_ELEMENT_TYPE.TEXT;
                                      msgContent = new Msg.Elem.Text('web端暂不支持' + msgBody.MsgType + '消息');
                                      break;
                              }
                              msg.elems.push(new Msg.Elem(msgType, msgContent));
                          }
                          MsgStore.addMsg(msg);
                          msgObjList.push(msg);
                      } // for loop
  
                      MsgStore.updateTimeline();
                      if (cbOk) {
  
                          var newResp = {
                              'Complete': resp.Complete,
                              'MsgCount': msgObjList.length,
                              'LastMsgTime': resp.LastMsgTime,
                              'MsgKey': resp.MsgKey,
                              'MsgList': msgObjList
                          };
                          sess.isFinished(resp.Complete);
                          cbOk(newResp);
                      }
  
                  }, function(err) {
                      log.error("getC2CHistoryMsgs failed:" + err.ErrorInfo);
                      if (cbErr) cbErr(err);
                  });
              };
  
              //拉群历史消息
              //不传cbOk 和 cbErr，则会调用新消息回调函数
              this.syncGroupMsgs = function(options, cbOk, cbErr) {
                  if (options.ReqMsgSeq <= 0) {
                      if (cbErr) {
                          var errInfo = "ReqMsgSeq must be greater than 0";
                          var error = tool.getReturnError(errInfo, -16);
                          cbErr(error);
                      }
                      return;
                  }
                  var opts = {
                      'GroupId': options.GroupId,
                      'ReqMsgSeq': options.ReqMsgSeq,
                      'ReqMsgNumber': options.ReqMsgNumber
                  };
                  //读群漫游消息
                  proto_getGroupMsgs(opts, function(resp) {
                      var notifyInfo = [];
                      var group_id = resp.GroupId; //返回的群id
                      var msgInfos = resp.RspMsgList; //返回的消息列表
                      var isFinished = resp.IsFinished;
  
                      if (msgInfos == null || msgInfos === undefined) {
                          if (cbOk) {
                              cbOk([]);
                          }
                          return;
                      }
                      for (var i = msgInfos.length - 1; i >= 0; i--) {
                          var msgInfo = msgInfos[i];
                          //如果是已经删除的消息或者发送者帐号为空或者消息内容为空
                          //IsPlaceMsg=1
                          if (msgInfo.IsPlaceMsg || !msgInfo.From_Account || !msgInfo.MsgBody || msgInfo.MsgBody.length == 0) {
                              continue;
                          }
                          var msg = handlerGroupMsg(msgInfo, true, true, isFinished);
                          if (msg) {
                              notifyInfo.push(msg);
                          }
                      } // for loop
                      if (notifyInfo.length > 0)
                          MsgStore.updateTimeline();
                      if (cbOk) cbOk(notifyInfo);
                      else if (notifyInfo.length > 0) {
                          if (onMsgCallback) onMsgCallback(notifyInfo);
                      }
  
                  }, function(err) {
                      log.error("getGroupMsgs failed:" + err.ErrorInfo);
                      if (cbErr) cbErr(err);
                  });
              };
  
              //处理群消息(普通消息+提示消息)
              //isSyncGroupMsgs 是否主动拉取群消息标志
              //isAddMsgFlag 是否需要保存到MsgStore，如果需要，这里会存在判重逻辑
              var handlerGroupMsg = function(msgInfo, isSyncGroupMsgs, isAddMsgFlag, isFinished) {
                  if (msgInfo.IsPlaceMsg || !msgInfo.From_Account || !msgInfo.MsgBody || msgInfo.MsgBody.length == 0) {
                      return null;
                  }
                  var isSendMsg, id, headUrl, fromAccountNick, fromAccountHeadurl;
                  var group_id = msgInfo.ToGroupId;
                  var group_name = group_id;
                  if (msgInfo.GroupInfo) { //取出群名称
                      if (msgInfo.GroupInfo.GroupName) {
                          group_name = msgInfo.GroupInfo.GroupName;
                      }
                  }
                  //取出成员昵称
                  fromAccountNick = msgInfo.From_Account;
                  //fromAccountHeadurl = msgInfo.GroupInfo.From_AccountHeadurl;
                  if (msgInfo.GroupInfo) {
                      if (msgInfo.GroupInfo.From_AccountNick) {
                          fromAccountNick = msgInfo.GroupInfo.From_AccountNick;
  
                      }
                      if (msgInfo.GroupInfo.From_AccountHeadurl) {
                          fromAccountHeadurl = msgInfo.GroupInfo.From_AccountHeadurl;
                      } else {
                          fromAccountHeadurl = null;
                      }
                  }
                  if (msgInfo.From_Account == ctx.identifier) { //当前用户发送的消息
                      isSendMsg = true;
                      id = msgInfo.From_Account; //读取接收者信息
                      headUrl = '';
                  } else { //当前用户收到的消息
                      isSendMsg = false;
                      id = msgInfo.From_Account; //读取发送者信息
                      headUrl = '';
                  }
                  var sess = MsgStore.sessByTypeId(SESSION_TYPE.GROUP, group_id);
                  if (!sess) {
                      sess = new Session(SESSION_TYPE.GROUP, group_id, group_name, headUrl, 0, 0);
                  }
                  if (typeof isFinished !== "undefined") {
                      sess.isFinished(isFinished || 0);
                  }
                  var subType = GROUP_MSG_SUB_TYPE.COMMON; //消息类型
                  //群提示消息,重新封装下
                  if (LONG_POLLINNG_EVENT_TYPE.GROUP_TIP == msgInfo.Event || LONG_POLLINNG_EVENT_TYPE.GROUP_TIP2 == msgInfo.Event) {
                      subType = GROUP_MSG_SUB_TYPE.TIP;
                      var groupTip = msgInfo.MsgBody;
                      msgInfo.MsgBody = [];
                      msgInfo.MsgBody.push({
                          "MsgType": MSG_ELEMENT_TYPE.GROUP_TIP,
                          "MsgContent": groupTip
                      });
                  } else if (msgInfo.MsgPriority) { //群点赞消息
                      if (msgInfo.MsgPriority == GROUP_MSG_PRIORITY_TYPE.REDPACKET) {
                          subType = GROUP_MSG_SUB_TYPE.REDPACKET;
                      } else if (msgInfo.MsgPriority == GROUP_MSG_PRIORITY_TYPE.LOVEMSG) {
                          subType = GROUP_MSG_SUB_TYPE.LOVEMSG;
                      }
  
                  }
                  var msg = new Msg(sess, isSendMsg, msgInfo.MsgSeq, msgInfo.MsgRandom, msgInfo.MsgTimeStamp, msgInfo.From_Account, subType, fromAccountNick, fromAccountHeadurl);
                  var msgBody = null;
                  var msgContent = null;
                  var msgType = null;
                  for (var mi in msgInfo.MsgBody) {
                      msgBody = msgInfo.MsgBody[mi];
                      msgType = msgBody.MsgType;
                      switch (msgType) {
                          case MSG_ELEMENT_TYPE.TEXT:
                              msgContent = new Msg.Elem.Text(msgBody.MsgContent.Text);
                              break;
                          case MSG_ELEMENT_TYPE.FACE:
                              msgContent = new Msg.Elem.Face(
                                  msgBody.MsgContent.Index,
                                  msgBody.MsgContent.Data
                              );
                              break;
                          case MSG_ELEMENT_TYPE.IMAGE:
                              msgContent = new Msg.Elem.Images(
                                              msgBody.MsgContent.UUID,
                                              msgBody.MsgContent.ImageFormat || ""
                              );
                              for (var j in msgBody.MsgContent.ImageInfoArray) {
                                  msgContent.addImage(
                                      new Msg.Elem.Images.Image(
                                          msgBody.MsgContent.ImageInfoArray[j].Type,
                                          msgBody.MsgContent.ImageInfoArray[j].Size,
                                          msgBody.MsgContent.ImageInfoArray[j].Width,
                                          msgBody.MsgContent.ImageInfoArray[j].Height,
                                          msgBody.MsgContent.ImageInfoArray[j].URL
                                      )
                                  );
                              }
                              break;
                          case MSG_ELEMENT_TYPE.SOUND:
                              if (msgBody.MsgContent) {
                                  msgContent = new Msg.Elem.Sound(
                                      msgBody.MsgContent.UUID,
                                      msgBody.MsgContent.Second,
                                      msgBody.MsgContent.Size,
                                      msgInfo.From_Account,
                                      msgInfo.To_Account,
                                      msgBody.MsgContent.Download_Flag,
                                      SESSION_TYPE.GROUP
                                  );
                              } else {
                                  msgType = MSG_ELEMENT_TYPE.TEXT;
                                  msgContent = new Msg.Elem.Text('[语音消息]下载地址解析出错');
                              }
                              break;
                          case MSG_ELEMENT_TYPE.LOCATION:
                              msgContent = new Msg.Elem.Location(
                                  msgBody.MsgContent.Longitude,
                                  msgBody.MsgContent.Latitude,
                                  msgBody.MsgContent.Desc
                              );
                              break;
                          case MSG_ELEMENT_TYPE.FILE:
                          case MSG_ELEMENT_TYPE.FILE + " ":
                              msgType = MSG_ELEMENT_TYPE.FILE;
                              var fileUrl = getFileDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account, msgBody.MsgContent.FileName);
  
                              if (msgBody.MsgContent) {
                                  msgContent = new Msg.Elem.File(
                                      msgBody.MsgContent.UUID,
                                      msgBody.MsgContent.FileName,
                                      msgBody.MsgContent.FileSize,
                                      msgInfo.From_Account,
                                      msgInfo.To_Account,
                                      msgBody.MsgContent.Download_Flag,
                                      SESSION_TYPE.GROUP
                                  );
                              } else {
                                  msgType = MSG_ELEMENT_TYPE.TEXT;
                                  msgContent = new Msg.Elem.Text('[文件消息]地址解析出错');
                              }
                              break;
                          case MSG_ELEMENT_TYPE.GROUP_TIP:
                              var opType = msgBody.MsgContent.OpType;
                              msgContent = new Msg.Elem.GroupTip(
                                  opType,
                                  msgBody.MsgContent.Operator_Account,
                                  group_id,
                                  msgInfo.GroupInfo.GroupName,
                                  msgBody.MsgContent.List_Account,
                                  msgBody.MsgContent.MsgMemberExtraInfo
                              );
                              if (GROUP_TIP_TYPE.JOIN == opType || GROUP_TIP_TYPE.QUIT == opType) { //加群或退群时，设置最新群成员数
                                  msgContent.setGroupMemberNum(msgBody.MsgContent.MemberNum);
                              } else if (GROUP_TIP_TYPE.MODIFY_GROUP_INFO == opType) { //群资料变更
                                  var tempIsCallbackFlag = false;
                                  var tempNewGroupInfo = {
                                      "GroupId": group_id,
                                      "GroupFaceUrl": null,
                                      "GroupName": null,
                                      "OwnerAccount": null,
                                      "GroupNotification": null,
                                      "GroupIntroduction": null
                                  };
                                  var msgGroupNewInfo = msgBody.MsgContent.MsgGroupNewInfo;
                                  if (msgGroupNewInfo.GroupFaceUrl) {
                                      var tmpNGIFaceUrl = new Msg.Elem.GroupTip.GroupInfo(
                                          GROUP_TIP_MODIFY_GROUP_INFO_TYPE.FACE_URL,
                                          msgGroupNewInfo.GroupFaceUrl
                                      );
                                      msgContent.addGroupInfo(tmpNGIFaceUrl);
                                      tempIsCallbackFlag = true;
                                      tempNewGroupInfo.GroupFaceUrl = msgGroupNewInfo.GroupFaceUrl;
                                  }
                                  if (msgGroupNewInfo.GroupName) {
                                      var tmpNGIName = new Msg.Elem.GroupTip.GroupInfo(
                                          GROUP_TIP_MODIFY_GROUP_INFO_TYPE.NAME,
                                          msgGroupNewInfo.GroupName
                                      );
                                      msgContent.addGroupInfo(tmpNGIName);
                                      tempIsCallbackFlag = true;
                                      tempNewGroupInfo.GroupName = msgGroupNewInfo.GroupName;
                                  }
                                  if (msgGroupNewInfo.Owner_Account) {
                                      var tmpNGIOwner = new Msg.Elem.GroupTip.GroupInfo(
                                          GROUP_TIP_MODIFY_GROUP_INFO_TYPE.OWNER,
                                          msgGroupNewInfo.Owner_Account
                                      );
                                      msgContent.addGroupInfo(tmpNGIOwner);
                                      tempIsCallbackFlag = true;
                                      tempNewGroupInfo.OwnerAccount = msgGroupNewInfo.Owner_Account;
                                  }
                                  if (msgGroupNewInfo.GroupNotification) {
                                      var tmpNGINotification = new Msg.Elem.GroupTip.GroupInfo(
                                          GROUP_TIP_MODIFY_GROUP_INFO_TYPE.NOTIFICATION,
                                          msgGroupNewInfo.GroupNotification
                                      );
                                      msgContent.addGroupInfo(tmpNGINotification);
                                      tempIsCallbackFlag = true;
                                      tempNewGroupInfo.GroupNotification = msgGroupNewInfo.GroupNotification;
                                  }
                                  if (msgGroupNewInfo.GroupIntroduction) {
                                      var tmpNGIIntroduction = new Msg.Elem.GroupTip.GroupInfo(
                                          GROUP_TIP_MODIFY_GROUP_INFO_TYPE.INTRODUCTION,
                                          msgGroupNewInfo.GroupIntroduction
                                      );
                                      msgContent.addGroupInfo(tmpNGIIntroduction);
                                      tempIsCallbackFlag = true;
                                      tempNewGroupInfo.GroupIntroduction = msgGroupNewInfo.GroupIntroduction;
                                  }
  
                                  //回调群资料变化通知方法
                                  if (isSyncGroupMsgs == false && tempIsCallbackFlag && onGroupInfoChangeCallback) {
                                      onGroupInfoChangeCallback(tempNewGroupInfo);
                                  }
  
                              } else if (GROUP_TIP_TYPE.MODIFY_MEMBER_INFO == opType) { //群成员变更
                                  var memberInfos = msgBody.MsgContent.MsgMemberInfo;
                                  for (var n in memberInfos) {
                                      var memberInfo = memberInfos[n];
                                      msgContent.addMemberInfo(
                                          new Msg.Elem.GroupTip.MemberInfo(
                                              memberInfo.User_Account, memberInfo.ShutupTime
                                          )
                                      );
                                  }
                              }
                              break;
                          case MSG_ELEMENT_TYPE.CUSTOM:
                              msgType = MSG_ELEMENT_TYPE.CUSTOM;
                              msgContent = new Msg.Elem.Custom(
                                  msgBody.MsgContent.Data,
                                  msgBody.MsgContent.Desc,
                                  msgBody.MsgContent.Ext
                              );
                              break;
                          default:
                              msgType = MSG_ELEMENT_TYPE.TEXT;
                              msgContent = new Msg.Elem.Text('web端暂不支持' + msgBody.MsgType + '消息');
                              break;
                      }
                      msg.elems.push(new Msg.Elem(msgType, msgContent));
                  }
  
                  if (isAddMsgFlag == false) { //不需要保存消息
                      return msg;
                  }
  
                  if (MsgStore.addMsg(msg)) {
                      return msg;
                  } else {
                      return null;
                  }
              };
  
              //初始化
              this.init = function(listeners, cbOk, cbErr) {
                  if (!listeners.onMsgNotify) {
                      log.warn('listeners.onMsgNotify is empty');
                  }
                  onMsgCallback = listeners.onMsgNotify;
  
                  if (listeners.onBigGroupMsgNotify) {
                      onBigGroupMsgCallback = listeners.onBigGroupMsgNotify;
                  } else {
                      log.warn('listeners.onBigGroupMsgNotify is empty');
                  }
  
                  if (listeners.onC2cEventNotifys) {
                      onC2cEventCallbacks = listeners.onC2cEventNotifys;
                  } else {
                      log.warn('listeners.onC2cEventNotifys is empty');
                  }
                  if (listeners.onGroupSystemNotifys) {
                      onGroupSystemNotifyCallbacks = listeners.onGroupSystemNotifys;
                  } else {
                      log.warn('listeners.onGroupSystemNotifys is empty');
                  }
                  if (listeners.onGroupInfoChangeNotify) {
                      onGroupInfoChangeCallback = listeners.onGroupInfoChangeNotify;
                  } else {
                      log.warn('listeners.onGroupInfoChangeNotify is empty');
                  }
                  if (listeners.onFriendSystemNotifys) {
                      onFriendSystemNotifyCallbacks = listeners.onFriendSystemNotifys;
                  } else {
                      log.warn('listeners.onFriendSystemNotifys is empty');
                  }
                  if (listeners.onProfileSystemNotifys) {
                      onProfileSystemNotifyCallbacks = listeners.onProfileSystemNotifys;
                  } else {
                      log.warn('listeners.onProfileSystemNotifys is empty');
                  }
                  if (listeners.onKickedEventCall) {
                      onKickedEventCall = listeners.onKickedEventCall;
                  } else {
                      log.warn('listeners.onKickedEventCall is empty');
                  }
  
                  if (listeners.onAppliedDownloadUrl) {
                      onAppliedDownloadUrl = listeners.onAppliedDownloadUrl;
                  } else {
                      log.warn('listeners.onAppliedDownloadUrl is empty');
                  }
  
                  if (!ctx.identifier || !ctx.userSig) {
                      if (cbOk) {
                          var success = {
                              'ActionStatus': ACTION_STATUS.OK,
                              'ErrorCode': 0,
                              'ErrorInfo': "login success(no login state)"
                          };
                          cbOk(success);
                      }
                      return;
                  }
  
                  //初始化
                  initMyGroupMaxSeqs(
                      function(resp) {
                          log.info('initMyGroupMaxSeqs success');
                          //初始化文件
                          initIpAndAuthkey(
                              function(initIpAndAuthkeyResp) {
                                  log.info('initIpAndAuthkey success');
                                  if (cbOk) {
                                      log.info('login success(have login state))');
                                      var success = {
                                          'ActionStatus': ACTION_STATUS.OK,
                                          'ErrorCode': 0,
                                          'ErrorInfo': "login success"
                                      };
                                      cbOk(success);
                                  }
                                  MsgManager.setLongPollingOn(true); //开启长轮询
                                  longPollingOn && MsgManager.longPolling(cbOk);
                              }, cbErr);
                      }, cbErr);
              };
  
              //发消息（私聊或群聊）
              this.sendMsg = function(msg, cbOk, cbErr) {
                  proto_sendMsg(msg, function(resp) {
                      //私聊时，加入自己的发的消息，群聊时，由于seq和服务器的seq不一样，所以不作处理
                      if (msg.sess.type() == SESSION_TYPE.C2C) {
                          if (!MsgStore.addMsg(msg)) {
                              var errInfo = "sendMsg: addMsg failed!";
                              var error = tool.getReturnError(errInfo, -17);
                              log.error(errInfo);
                              if (cbErr) cbErr(error);
                              return;
                          }
                          //更新信息流时间
                          MsgStore.updateTimeline();
                      }
                      if (cbOk) cbOk(resp);
                  }, function(err) {
                      if (cbErr) cbErr(err);
                  });
              };
          };
  
      //上传文件
      var FileUploader = new function() {
              this.fileMd5 = null;
              //获取文件MD5
              var getFileMD5 = function(file, cbOk, cbErr) {
  
                  //FileReader pc浏览器兼容性
                  //Feature   Firefox (Gecko) Chrome  Internet Explorer   Opera   Safari
                  //Basic support 3.6 7   10                      12.02   6.0.2
                  var fileReader = null;
                  try {
                      fileReader = new FileReader(); //分块读取文件对象
                  } catch (e) {
                      if (cbErr) {
                          cbErr(tool.getReturnError('当前浏览器不支持FileReader', -18));
                          return;
                      }
                  }
                  //file的slice方法，注意它的兼容性，在不同浏览器的写法不同
                  var blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice;
                  if (!blobSlice) {
                      if (cbErr) {
                          cbErr(tool.getReturnError('当前浏览器不支持FileAPI', -19));
                          return;
                      }
                  }
  
                  var chunkSize = 2 * 1024 * 1024; //分块大小，2M
                  var chunks = Math.ceil(file.size / chunkSize); //总块数
                  var currentChunk = 0; //当前块数
                  var spark = new SparkMD5(); //获取MD5对象
  
                  fileReader.onload = function(e) { //数据加载完毕事件
  
                      var binaryStr = "";
                      var bytes = new Uint8Array(e.target.result);
                      var length = bytes.byteLength;
                      for (var i = 0; i < length; i++) {
                          binaryStr += String.fromCharCode(bytes[i]); //二进制转换字符串
                      }
                      spark.appendBinary(binaryStr);
                      currentChunk++;
                      if (currentChunk < chunks) {
                          loadNext(); //读取下一块数据
                      } else {
                          this.fileMd5 = spark.end(); //得到文件MD5值
                          if (cbOk) {
                              cbOk(this.fileMd5);
                          }
                      }
                  };
                  //分片读取文件
  
                  function loadNext() {
                      var start = currentChunk * chunkSize,
                          end = start + chunkSize >= file.size ? file.size : start + chunkSize;
                      //根据开始和结束位置，切割文件
                      var b = blobSlice.call(file, start, end);
                      //readAsBinaryString ie浏览器不兼容此方法
                      //fileReader.readAsBinaryString(blobSlice.call(file, start, end));
                      fileReader.readAsArrayBuffer(b); //ie，chrome，firefox等主流浏览器兼容此方法
  
                  }
  
                  loadNext(); //开始读取
              };
              //提交上传图片表单(用于低版本IE9以下)
              this.submitUploadFileForm = function(options, cbOk, cbErr) {
                  var errInfo;
                  var error;
                  var formId = options.formId;
                  var fileId = options.fileId;
                  var iframeNum = uploadResultIframeId++;
                  var iframeName = "uploadResultIframe_" + iframeNum;
                  var toAccount = options.To_Account;
                  var businessType = options.businessType;
  
                  var form = document.getElementById(formId);
                  if (!form) {
                      errInfo = "获取表单对象为空: formId=" + formId + "(formId非法)";
                      error = tool.getReturnError(errInfo, -20);
                      if (cbErr) cbErr(error);
                      return;
                  }
  
                  var fileObj = document.getElementById(fileId);
                  if (!fileObj) {
                      errInfo = "获取文件对象为空: fileId=" + fileId + "(没有选择文件或者fileId非法)";
                      error = tool.getReturnError(errInfo, -21);
                      if (cbErr) cbErr(error);
                      return;
                  }
                  //fileObj.type="file";//ie8下不起作用，必须由业务自己设置
                  fileObj.name = "file";
  
                  var iframe = document.createElement("iframe");
                  iframe.name = iframeName;
                  iframe.id = iframeName;
                  iframe.style.display = "none";
                  document.body.appendChild(iframe);
  
                  var cmdName;
                  if (isAccessFormalEnv()) {
                      cmdName = 'pic_up';
                  } else {
                      cmdName = 'pic_up_test';
                  }
                  var uploadApiUrl = "https://pic.tim.qq.com/v4/openpic/" + cmdName + "?tinyid=" + ctx.tinyid + "&a2=" + ctx.a2 + "&sdkappid=" + ctx.sdkAppID + "&accounttype=" + ctx.accountType + "&contenttype=http";
                  form.action = uploadApiUrl;
                  form.method = 'post';
                  //form.enctype='multipart/form-data';//ie8下不起作用，必须由业务自己设置
                  form.target = iframeName;
  
                  function createFormInput(name, value) {
                      var tempInput = document.createElement("input");
                      tempInput.type = "hidden";
                      tempInput.name = name;
                      tempInput.value = value;
                      form.appendChild(tempInput);
                  }
  
                  createFormInput("App_Version", VERSION_INFO.APP_VERSION);
                  createFormInput("From_Account", ctx.identifier);
                  createFormInput("To_Account", toAccount);
                  createFormInput("Seq", nextSeq().toString());
                  createFormInput("Timestamp", unixtime().toString());
                  createFormInput("Random", createRandom().toString());
                  createFormInput("Busi_Id", businessType);
                  createFormInput("PkgFlag", UPLOAD_RES_PKG_FLAG.RAW_DATA.toString());
                  createFormInput("Auth_Key", authkey);
                  createFormInput("Server_Ver", VERSION_INFO.SERVER_VERSION.toString());
                  createFormInput("File_Type", options.fileType);
  
  
                  //检测iframe.contentWindow.name是否有值
  
                  function checkFrameName() {
                      var resp;
                      try {
                          resp = JSON.parse(iframe.contentWindow.name) || {};
                      } catch (e) {
                          resp = {};
                      }
                      if (resp.ActionStatus) { //上传接口返回
                          // We've got what we need. Stop the iframe from loading further content.
                          iframe.src = "about:blank";
                          iframe.parentNode.removeChild(iframe);
                          iframe = null;
  
                          if (resp.ActionStatus == ACTION_STATUS.OK) {
                              cbOk && cbOk(resp);
                          } else {
                              cbErr && cbErr(resp);
                          }
                      } else {
                          setTimeout(checkFrameName, 100);
                      }
                  }
  
                  setTimeout(checkFrameName, 500);
  
                  form.submit(); //提交上传图片表单
              };
              //上传图片或文件(用于高版本浏览器，支持FileAPI)
              this.uploadFile = function(options, cbOk, cbErr) {
  
                  var file_upload = {
                      //初始化
                      init: function(options, cbOk, cbErr) {
                          var me = this;
                          me.file = options.file;
                          //分片上传进度回调事件
                          me.onProgressCallBack = options.onProgressCallBack;
                          //停止上传图片按钮
                          if (options.abortButton) {
                              options.abortButton.onclick = me.abortHandler;
                          }
                          me.total = me.file.size; //文件总大小
                          me.loaded = 0; //已读取字节数
                          me.step = 1080 * 1024; //分块大小，1080K
                          me.sliceSize = 0; //分片大小
                          me.sliceOffset = 0; //当前分片位置
                          me.timestamp = unixtime(); //当前时间戳
                          me.seq = nextSeq(); //请求seq
                          me.random = createRandom(); //请求随机数
                          me.fromAccount = ctx.identifier; //发送者
                          me.toAccount = options.To_Account; //接收者
                          me.fileMd5 = options.fileMd5; //文件MD5
                          me.businessType = options.businessType; //图片或文件的业务类型，群消息:1; c2c消息:2; 个人头像：3; 群头像：4;
                          me.fileType = options.fileType; //文件类型，不填为默认认为上传的是图片；1：图片；2：文件；3：短视频；4：PTT
  
                          me.cbOk = cbOk; //上传成功回调事件
                          me.cbErr = cbErr; //上传失败回调事件
  
                          me.reader = new FileReader(); //读取文件对象
                          me.blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice; //file的slice方法,不同浏览器不一样
  
                          me.reader.onloadstart = me.onLoadStart; //开始读取回调事件
                          me.reader.onprogress = me.onProgress; //读取文件进度回调事件
                          me.reader.onabort = me.onAbort; //停止读取回调事件
                          me.reader.onerror = me.onerror; //读取发生错误回调事件
                          me.reader.onload = me.onLoad; //分片加载完毕回调事件
                          me.reader.onloadend = me.onLoadEnd; //读取文件完毕回调事件
                      },
                      //上传方法
                      upload: function() {
                          var me = file_upload;
                          //读取第一块
                          me.readBlob(0);
                      },
                      onLoadStart: function() {
                          var me = file_upload;
                      },
                      onProgress: function(e) {
                          var me = file_upload;
                          me.loaded += e.loaded;
                          if (me.onProgressCallBack) {
                              me.onProgressCallBack(me.loaded, me.total);
                          }
                      },
                      onAbort: function() {
                          var me = file_upload;
                      },
                      onError: function() {
                          var me = file_upload;
                      },
                      onLoad: function(e) {
                          var me = file_upload;
                          if (e.target.readyState == FileReader.DONE) {
                              var slice_data_base64 = e.target.result;
                              //注意，一定要去除base64编码头部
                              var pos = slice_data_base64.indexOf(",");
                              if (pos != -1) {
                                  slice_data_base64 = slice_data_base64.substr(pos + 1);
                              }
                              //封装上传图片接口的请求参数
                              var opt = {
                                  'From_Account': me.fromAccount,
                                  'To_Account': me.toAccount,
                                  'Busi_Id': me.businessType,
                                  'File_Type': me.fileType,
                                  'File_Str_Md5': me.fileMd5,
                                  'PkgFlag': UPLOAD_RES_PKG_FLAG.BASE64_DATA,
                                  'File_Size': me.total,
                                  'Slice_Offset': me.sliceOffset,
                                  'Slice_Size': me.sliceSize,
                                  'Slice_Data': slice_data_base64,
                                  'Seq': me.seq,
                                  'Timestamp': me.timestamp,
                                  'Random': me.random
                              };
  
                              //上传成功的成功回调
                              var succCallback = function(resp) {
                                  if (resp.IsFinish == 0) {
                                      me.loaded = resp.Next_Offset;
                                      if (me.loaded < me.total) {
                                          me.readBlob(me.loaded);
                                      } else {
                                          me.loaded = me.total;
                                      }
                                  } else {
  
                                      if (me.cbOk) {
                                          var tempResp = {
                                              'ActionStatus': resp.ActionStatus,
                                              'ErrorCode': resp.ErrorCode,
                                              'ErrorInfo': resp.ErrorInfo,
                                              'File_UUID': resp.File_UUID,
                                              'File_Size': resp.Next_Offset,
                                              'URL_INFO': resp.URL_INFO,
                                              'Download_Flag': resp.Download_Flag
                                          };
                                          if (me.fileType == UPLOAD_RES_TYPE.FILE) { //如果上传的是文件，下载地址需要sdk内部拼接
                                              tempResp.URL_INFO = getFileDownUrl(resp.File_UUID, ctx.identifier, me.file.name);
                                          }
                                          me.cbOk(tempResp);
                                      }
                                  }
                                  Upload_Retry_Times = 0;
                              };
                              //上传失败的回调
                              var errorCallback = function(resp) {
                                  if (Upload_Retry_Times < Upload_Retry_Max_Times) {
                                      Upload_Retry_Times++;
                                      setTimeout(function() {
                                          proto_uploadPic(opt, succCallback, errorCallback);
                                      }, 1000);
                                  } else {
                                      me.cbErr(resp);
                                  }
                                  //me.cbErr
                              };
                              //分片上传图片接口
                              proto_uploadPic(opt, succCallback, errorCallback);
                          }
                      },
                      onLoadEnd: function() {
                          var me = file_upload;
                      },
                      //分片读取文件方法
                      readBlob: function(start) {
                          var me = file_upload;
                          var blob, file = me.file;
                          var end = start + me.step;
                          if (end > me.total) {
                              end = me.total;
                              me.sliceSize = end - start;
                          } else {
                              me.sliceSize = me.step;
                          }
                          me.sliceOffset = start;
                          //根据起始和结束位置，分片读取文件
                          blob = me.blobSlice.call(file, start, end);
                          //将分片的二进制数据转换为base64编码
                          me.reader.readAsDataURL(blob);
                      },
                      abortHandler: function() {
                          var me = file_upload;
                          if (me.reader) {
                              me.reader.abort();
                          }
                      }
                  };
  
                  //读取文件MD5
                  getFileMD5(options.file,
                      function(fileMd5) {
                          log.info('fileMd5: ' + fileMd5);
                          options.fileMd5 = fileMd5;
                          //初始化上传参数
                          file_upload.init(options, cbOk, cbErr);
                          //开始上传文件
                          file_upload.upload();
                      },
                      cbErr
                  );
              };
          };
  
  
      //web im 基础对象
  
      //常量对象
  
      //会话类型
      webim.SESSION_TYPE = SESSION_TYPE;
  
      webim.MSG_MAX_LENGTH = MSG_MAX_LENGTH;
  
      //c2c消息子类型
      webim.C2C_MSG_SUB_TYPE = C2C_MSG_SUB_TYPE;
  
      //群消息子类型
      webim.GROUP_MSG_SUB_TYPE = GROUP_MSG_SUB_TYPE;
  
      //消息元素类型
      webim.MSG_ELEMENT_TYPE = MSG_ELEMENT_TYPE;
  
      //群提示消息类型
      webim.GROUP_TIP_TYPE = GROUP_TIP_TYPE;
  
      //图片类型
      webim.IMAGE_TYPE = IMAGE_TYPE;
  
      //群系统消息类型
      webim.GROUP_SYSTEM_TYPE = GROUP_SYSTEM_TYPE;
  
      //好友系统通知子类型
      webim.FRIEND_NOTICE_TYPE = FRIEND_NOTICE_TYPE;
  
      //群提示消息-群资料变更类型
      webim.GROUP_TIP_MODIFY_GROUP_INFO_TYPE = GROUP_TIP_MODIFY_GROUP_INFO_TYPE;
  
      //浏览器信息
      webim.BROWSER_INFO = BROWSER_INFO;
  
      //表情对象
      webim.Emotions = webim.EmotionPicData = emotions;
      //表情标识符和index Map
      webim.EmotionDataIndexs = webim.EmotionPicDataIndex = emotionDataIndexs;
  
      //腾讯登录服务错误码(托管模式)
      webim.TLS_ERROR_CODE = TLS_ERROR_CODE;
  
      //连接状态
      webim.CONNECTION_STATUS = CONNECTION_STATUS;
  
      //上传图片业务类型
      webim.UPLOAD_PIC_BUSSINESS_TYPE = UPLOAD_PIC_BUSSINESS_TYPE;
  
      //最近联系人类型
      webim.RECENT_CONTACT_TYPE = RECENT_CONTACT_TYPE;
  
      //上传资源类型
      webim.UPLOAD_RES_TYPE = UPLOAD_RES_TYPE;
  
  
      /**************************************/
  
      //类对象
      //
      //工具对象
      webim.Tool = tool;
      //控制台打印日志对象
      webim.Log = log;
  
      //消息对象
      webim.Msg = Msg;
      //会话对象
      webim.Session = Session;
      //会话存储对象
      webim.MsgStore = {
          sessMap: function() {
              return MsgStore.sessMap();
          },
          sessCount: function() {
              return MsgStore.sessCount();
          },
          sessByTypeId: function(type, id) {
              return MsgStore.sessByTypeId(type, id);
          },
          delSessByTypeId: function(type, id) {
              return MsgStore.delSessByTypeId(type, id);
          },
          resetCookieAndSyncFlag: function() {
              return MsgStore.resetCookieAndSyncFlag();
          }
      };
  
      webim.Resources = Resources;
  
      /**************************************/
  
      // webim API impl
      //
      //基本接口
      //登录
      webim.login = webim.init = function(loginInfo, listeners, opts, cbOk, cbErr) {
  
          //初始化连接状态回调函数
          ConnManager.init(listeners.onConnNotify, cbOk, cbErr);
  
          //设置ie9以下浏览器jsonp回调
          if (listeners.jsonpCallback) jsonpCallback = listeners.jsonpCallback;
          //登录
          _login(loginInfo, listeners, opts, cbOk, cbErr);
      };
      //登出
      //需要传长轮询id
      //这样登出之后其他的登录实例还可以继续收取消息
      webim.logout = webim.offline = function(cbOk, cbErr) {
          return proto_logout('instance', cbOk, cbErr);
      };
  
      //登出
      //这种登出方式，所有的实例都将不会收到消息推送，直到重新登录
      webim.logoutAll = function(cbOk, cbErr) {
          return proto_logout('all', cbOk, cbErr);
      };
  
  
      //消息管理接口
      //发消息接口（私聊和群聊）
      webim.sendMsg = function(msg, cbOk, cbErr) {
          return MsgManager.sendMsg(msg, cbOk, cbErr);
      };
      //拉取未读c2c消息
      webim.syncMsgs = function(cbOk, cbErr) {
          return MsgManager.syncMsgs(cbOk, cbErr);
      };
      //拉取C2C漫游消息
      webim.getC2CHistoryMsgs = function(options, cbOk, cbErr) {
          return MsgManager.getC2CHistoryMsgs(options, cbOk, cbErr);
      };
      //拉取群漫游消息
      webim.syncGroupMsgs = function(options, cbOk, cbErr) {
          return MsgManager.syncGroupMsgs(options, cbOk, cbErr);
      };
  
      //上报c2c消息已读
      webim.c2CMsgReaded = function(options, cbOk, cbErr) {
          return MsgStore.c2CMsgReaded(options, cbOk, cbErr);
      };
  
      //上报群消息已读
      webim.groupMsgReaded = function(options, cbOk, cbErr) {
          return proto_groupMsgReaded(options, cbOk, cbErr);
      };
  
      //设置聊天会话自动标记已读
      webim.setAutoRead = function(selSess, isOn, isResetAll) {
          return MsgStore.setAutoRead(selSess, isOn, isResetAll);
      };
  
      //群组管理接口
      //
      //创建群
      webim.createGroup = function(options, cbOk, cbErr) {
          return proto_createGroup(options, cbOk, cbErr);
      };
      //创建群-高级接口
      webim.createGroupHigh = function(options, cbOk, cbErr) {
          return proto_createGroupHigh(options, cbOk, cbErr);
      };
      //申请加群
      webim.applyJoinGroup = function(options, cbOk, cbErr) {
          return proto_applyJoinGroup(options, cbOk, cbErr);
      };
      //处理加群申请(同意或拒绝)
      webim.handleApplyJoinGroupPendency = function(options, cbOk, cbErr) {
          return proto_handleApplyJoinGroupPendency(options, cbOk, cbErr);
      };
  
      //获取群组未决列表
      webim.getPendencyGroup = function(options, cbOk, cbErr) {
          return proto_getPendencyGroup(options, cbOk, cbErr);
      };
  
      //群未决已读上报
      webim.getPendencyGroupRead = function(options, cbOk, cbErr) {
          return proto_getPendencyGroupRead(options, cbOk, cbErr);
      };
  
      //处理邀请进群申请(同意或拒绝)
      webim.handleInviteJoinGroupRequest = function(options, cbOk, cbErr) {
          return proto_handleInviteJoinGroupRequest(options, cbOk, cbErr);
      };
  
      //删除加群申请
      webim.deleteApplyJoinGroupPendency = function(options, cbOk, cbErr) {
          return proto_deleteC2CMsg(options, cbOk, cbErr);
      };
  
      //主动退群
      webim.quitGroup = function(options, cbOk, cbErr) {
          return proto_quitGroup(options, cbOk, cbErr);
      };
      //搜索群组(根据名称)
      webim.searchGroupByName = function(options, cbOk, cbErr) {
          return proto_searchGroupByName(options, cbOk, cbErr);
      };
      //获取群组公开资料(根据群id搜索)
      webim.getGroupPublicInfo = function(options, cbOk, cbErr) {
          return proto_getGroupPublicInfo(options, cbOk, cbErr);
      };
      //获取群组详细资料-高级接口
      webim.getGroupInfo = function(options, cbOk, cbErr) {
          return proto_getGroupInfo(options, cbOk, cbErr);
      };
      //修改群基本资料
      webim.modifyGroupBaseInfo = function(options, cbOk, cbErr) {
          return proto_modifyGroupBaseInfo(options, cbOk, cbErr);
      };
      //获取群成员列表
      webim.getGroupMemberInfo = function(options, cbOk, cbErr) {
          return proto_getGroupMemberInfo(options, cbOk, cbErr);
      };
      //邀请好友加群
      webim.addGroupMember = function(options, cbOk, cbErr) {
          return proto_addGroupMember(options, cbOk, cbErr);
      };
      //修改群成员资料
      webim.modifyGroupMember = function(options, cbOk, cbErr) {
          return proto_modifyGroupMember(options, cbOk, cbErr);
      };
      //删除群成员
      webim.deleteGroupMember = function(options, cbOk, cbErr) {
          return proto_deleteGroupMember(options, cbOk, cbErr);
      };
      //解散群
      webim.destroyGroup = function(options, cbOk, cbErr) {
          return proto_destroyGroup(options, cbOk, cbErr);
      };
      //转让群组
      webim.changeGroupOwner = function(options, cbOk, cbErr) {
          return proto_changeGroupOwner(options, cbOk, cbErr);
      };
  
      //获取我的群组列表-高级接口
      webim.getJoinedGroupListHigh = function(options, cbOk, cbErr) {
          return proto_getJoinedGroupListHigh(options, cbOk, cbErr);
      };
      //获取群成员角色
      webim.getRoleInGroup = function(options, cbOk, cbErr) {
          return proto_getRoleInGroup(options, cbOk, cbErr);
      };
      //设置群成员禁言时间
      webim.forbidSendMsg = function(options, cbOk, cbErr) {
          return proto_forbidSendMsg(options, cbOk, cbErr);
      };
      //发送自定义群系统通知
      webim.sendCustomGroupNotify = function(options, cbOk, cbErr) {
          return proto_sendCustomGroupNotify(options, cbOk, cbErr);
      };
  
      //进入大群
      webim.applyJoinBigGroup = function(options, cbOk, cbErr) {
          return proto_applyJoinBigGroup(options, cbOk, cbErr);
      };
      //退出大群
      webim.quitBigGroup = function(options, cbOk, cbErr) {
          return proto_quitBigGroup(options, cbOk, cbErr);
      };
  
      //资料关系链管理接口
      //
      //获取个人资料接口，可用于搜索用户
      webim.getProfilePortrait = function(options, cbOk, cbErr) {
          return proto_getProfilePortrait(options, cbOk, cbErr);
      };
      //设置个人资料
      webim.setProfilePortrait = function(options, cbOk, cbErr) {
          return proto_setProfilePortrait(options, cbOk, cbErr);
      };
      //申请加好友
      webim.applyAddFriend = function(options, cbOk, cbErr) {
          return proto_applyAddFriend(options, cbOk, cbErr);
      };
      //获取好友申请列表
      webim.getPendency = function(options, cbOk, cbErr) {
          return proto_getPendency(options, cbOk, cbErr);
      };
      //好友申请列表已读上报
      webim.getPendencyReport = function(options, cbOk, cbErr) {
          return proto_getPendencyReport(options, cbOk, cbErr);
      };
      //删除好友申请
      webim.deletePendency = function(options, cbOk, cbErr) {
          return proto_deletePendency(options, cbOk, cbErr);
      };
      //处理好友申请
      webim.responseFriend = function(options, cbOk, cbErr) {
          return proto_responseFriend(options, cbOk, cbErr);
      };
      //获取我的好友
      webim.getAllFriend = function(options, cbOk, cbErr) {
          return proto_getAllFriend(options, cbOk, cbErr);
      };
                  //删除会话
                  webim.deleteChat = function(options, cbOk, cbErr) {
                      return proto_deleteChat(options, cbOk, cbErr);
                  };
      //删除好友
      webim.deleteFriend = function(options, cbOk, cbErr) {
          return proto_deleteFriend(options, cbOk, cbErr);
      };
      //拉黑
      webim.addBlackList = function(options, cbOk, cbErr) {
          return proto_addBlackList(options, cbOk, cbErr);
      };
      //删除黑名单
      webim.deleteBlackList = function(options, cbOk, cbErr) {
          return proto_deleteBlackList(options, cbOk, cbErr);
      };
      //获取我的黑名单
      webim.getBlackList = function(options, cbOk, cbErr) {
          return proto_getBlackList(options, cbOk, cbErr);
      };
  
      //获取最近会话
      webim.getRecentContactList = function(options, cbOk, cbErr) {
          return proto_getRecentContactList(options, cbOk, cbErr);
      };
  
      //图片或文件服务接口
      //
      //上传文件接口（高版本浏览器）
      webim.uploadFile = webim.uploadPic = function(options, cbOk, cbErr) {
          return FileUploader.uploadFile(options, cbOk, cbErr);
      };
      //提交上传图片表单接口（用于低版本ie）
      webim.submitUploadFileForm = function(options, cbOk, cbErr) {
          return FileUploader.submitUploadFileForm(options, cbOk, cbErr);
      };
      //上传图片或文件(Base64)接口
      webim.uploadFileByBase64 = webim.uploadPicByBase64 = function(options, cbOk, cbErr) {
          //请求参数
          var opt = {
              'To_Account': options.toAccount,
              'Busi_Id': options.businessType,
              'File_Type': options.File_Type,
              'File_Str_Md5': options.fileMd5,
              'PkgFlag': UPLOAD_RES_PKG_FLAG.BASE64_DATA,
              'File_Size': options.totalSize,
              'Slice_Offset': 0,
              'Slice_Size': options.totalSize,
              'Slice_Data': options.base64Str,
              'Seq': nextSeq(),
              'Timestamp': unixtime(),
              'Random': createRandom()
          };
          return proto_uploadPic(opt, cbOk, cbErr);
      };
  
      //设置jsonp返回的值
      webim.setJsonpLastRspData = function(rspData) {
          jsonpLastRspData = typeof(rspData) == "string" ? JSON.parse(rspData) : rspData;
      };
  
      //获取长轮询ID
      webim.getLongPollingId = function(options, cbOk, cbErr) {
          return proto_getLongPollingId(options, cbOk, cbErr);
      };
  
      //获取下载地址
      webim.applyDownload = function(options, cbOk, cbErr) {
          return proto_applyDownload(options, cbOk, cbErr);
      };
  
      //获取下载地址
      webim.onDownFile = function(uuid) {
          window.open(Resources.downloadMap["uuid_" + uuid]);
      };
  
      //检查是否登录
      webim.checkLogin = function(cbErr, isNeedCallBack) {
          return checkLogin(cbErr, isNeedCallBack);
      };
  })(webim);