Greasy Fork is available in English.

115备份小工具

改自115转存助手ui优化版3.5,先感谢一下作者,其次我太懒了,不想写新的

// ==UserScript==
// @name         115备份小工具
// @name:zh      115备份小工具
// @description  改自115转存助手ui优化版3.5,先感谢一下作者,其次我太懒了,不想写新的
// @author       115211
// @namespace    null
// @version      3.0.2
// @match        https://115.com/*
// @exclude      https://115.com/s/*
// @license MIT

// @grant        GM_xmlhttpRequest
// @grant        GM_log
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_setClipboard
// @grant        unsafeWindow
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @grant        GM_info

// @connect      proapi.115.com
// @connect      webapi.115.com
// @connect      115.com
// @connect      *

// @require      https://cdn.jsdelivr.net/npm/underscore@1.13.6/underscore-min.js
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11.3.0
// @require      https://cdn.jsdelivr.net/npm/node-forge@0.10.0/dist/forge.min.js
// @require      https://cdn.jsdelivr.net/npm/emoutils@2.0.0/dist/umd/emoutils.min.js
// @require      https://cdn.jsdelivr.net/npm/blueimp-md5@2.19.0/js/md5.min.js
// @require      https://cdn.jsdelivr.net/npm/big-integer@1.6.51/BigInteger.min.js
// ==/UserScript==


/*针对网络问题,只能将不稳定的依赖库放置于此*/

/*jssha@2.3.1/src/sha.js*/
/*
 A JavaScript implementation of the SHA family of hashes, as
 defined in FIPS PUB 180-4 and FIPS PUB 202, as well as the corresponding
 HMAC implementation as defined in FIPS PUB 198a

 Copyright Brian Turek 2008-2017
 Distributed under the BSD License
 See http://caligatio.github.com/jsSHA/ for more information

 Several functions taken from Paul Johnston
 */
'use strict';
(function(Y) {
  function C(c, a, b) {
    var e = 0,
        h = [],
        n = 0,
        g, l, d, f, m, q, u, r, I = !1,
        v = [],
        w = [],
        t, y = !1,
        z = !1,
        x = -1;
    b = b || {};
    g = b.encoding || 'UTF8';
    t = b.numRounds || 1;
    if (t !== parseInt(t, 10) || 1 > t) throw Error(
        'numRounds must a integer >= 1');
    if ('SHA-1' === c) m = 512, q = K, u = Z, f = 160, r = function(a) {
      return a.slice();
    };
    else if (0 === c.lastIndexOf('SHA-', 0))
      if (q = function(a, b) {
        return L(a, b, c);
      }, u = function(a, b, h, e) {
        var k, f;
        if ('SHA-224' === c || 'SHA-256' === c) k = (b + 65 >>> 9 << 4) +
            15, f = 16;
        else if ('SHA-384' === c || 'SHA-512' === c) k = (b + 129 >>> 10 <<
            5) + 31, f = 32;
        else throw Error('Unexpected error in SHA-2 implementation');
        for (; a.length <= k;) a.push(0);
        a[b >>> 5] |= 128 << 24 - b % 32;
        b = b + h;
        a[k] = b & 4294967295;
        a[k - 1] = b / 4294967296 | 0;
        h = a.length;
        for (b = 0; b < h; b += f) e = L(a.slice(b, b + f), e, c);
        if ('SHA-224' === c) a = [e[0], e[1], e[2], e[3], e[4], e[5], e[6]];
        else if ('SHA-256' === c) a = e;
        else if ('SHA-384' === c) a = [
          e[0].a,
          e[0].b,
          e[1].a,
          e[1].b,
          e[2].a,
          e[2].b,
          e[3].a,
          e[3].b,
          e[4].a,
          e[4].b,
          e[5].a,
          e[5].b];
        else if ('SHA-512' === c) a = [
          e[0].a,
          e[0].b,
          e[1].a,
          e[1].b,
          e[2].a,
          e[2].b,
          e[3].a,
          e[3].b,
          e[4].a,
          e[4].b,
          e[5].a,
          e[5].b,
          e[6].a,
          e[6].b,
          e[7].a,
          e[7].b,
        ];
        else throw Error('Unexpected error in SHA-2 implementation');
        return a;
      }, r = function(a) {
        return a.slice();
      }, 'SHA-224' === c) m = 512, f = 224;
      else if ('SHA-256' === c) m = 512, f = 256;
      else if ('SHA-384' === c) m = 1024, f = 384;
      else if ('SHA-512' === c) m = 1024, f = 512;
      else throw Error('Chosen SHA variant is not supported');
    else if (0 === c.lastIndexOf('SHA3-', 0) || 0 ===
        c.lastIndexOf('SHAKE', 0)) {
      var F = 6;
      q = D;
      r = function(a) {
        var c = [],
            e;
        for (e = 0; 5 > e; e += 1) c[e] = a[e].slice();
        return c;
      };
      x = 1;
      if ('SHA3-224' ===
          c) m = 1152, f = 224;
      else if ('SHA3-256' === c) m = 1088, f = 256;
      else if ('SHA3-384' === c) m = 832, f = 384;
      else if ('SHA3-512' === c) m = 576, f = 512;
      else if ('SHAKE128' === c) m = 1344, f = -1, F = 31, z = !0;
      else if ('SHAKE256' === c) m = 1088, f = -1, F = 31, z = !0;
      else throw Error('Chosen SHA variant is not supported');
      u = function(a, c, e, b, h) {
        e = m;
        var k = F,
            f, g = [],
            n = e >>> 5,
            l = 0,
            d = c >>> 5;
        for (f = 0; f < d && c >= e; f += n) b = D(a.slice(f, f + n),
            b), c -= e;
        a = a.slice(f);
        for (c %= e; a.length < n;) a.push(0);
        f = c >>> 3;
        a[f >> 2] ^= k << f % 4 * 8;
        a[n - 1] ^= 2147483648;
        for (b = D(a, b); 32 * g.length < h;) {
          a = b[l %
          5][l / 5 | 0];
          g.push(a.b);
          if (32 * g.length >= h) break;
          g.push(a.a);
          l += 1;
          0 === 64 * l % e && D(null, b);
        }
        return g;
      };
    } else throw Error('Chosen SHA variant is not supported');
    d = M(a, g, x);
    l = A(c);
    this.setHMACKey = function(a, b, h) {
      var k;
      if (!0 === I) throw Error('HMAC key already set');
      if (!0 === y) throw Error('Cannot set HMAC key after calling update');
      if (!0 === z) throw Error('SHAKE is not supported for HMAC');
      g = (h || {}).encoding || 'UTF8';
      b = M(b, g, x)(a);
      a = b.binLen;
      b = b.value;
      k = m >>> 3;
      h = k / 4 - 1;
      if (k < a / 8) {
        for (b = u(b, a, 0, A(c), f); b.length <= h;) b.push(0);
        b[h] &= 4294967040;
      } else if (k > a / 8) {
        for (; b.length <= h;) b.push(0);
        b[h] &= 4294967040;
      }
      for (a = 0; a <= h; a += 1) v[a] = b[a] ^ 909522486, w[a] = b[a] ^
          1549556828;
      l = q(v, l);
      e = m;
      I = !0;
    };
    this.update = function(a) {
      var c, b, k, f = 0,
          g = m >>> 5;
      c = d(a, h, n);
      a = c.binLen;
      b = c.value;
      c = a >>> 5;
      for (k = 0; k < c; k += g) f + m <= a &&
      (l = q(b.slice(k, k + g), l), f += m);
      e += f;
      h = b.slice(f >>> 5);
      n = a % m;
      y = !0;
    };
    this.getHash = function(a, b) {
      var k, g, d, m;
      if (!0 === I) throw Error('Cannot call getHash after setting HMAC key');
      d = N(b);
      if (!0 === z) {
        if (-1 === d.shakeLen) throw Error(
            'shakeLen must be specified in options');
        f = d.shakeLen;
      }
      switch (a) {
        case 'HEX':
          k = function(a) {
            return O(a, f, x, d);
          };
          break;
        case 'B64':
          k = function(a) {
            return P(a, f, x, d);
          };
          break;
        case 'BYTES':
          k = function(a) {
            return Q(a, f, x);
          };
          break;
        case 'ARRAYBUFFER':
          try {
            g = new ArrayBuffer(0);
          } catch (p) {
            throw Error('ARRAYBUFFER not supported by this environment');
          }
          k = function(a) {
            return R(a, f, x);
          };
          break;
        default:
          throw Error('format must be HEX, B64, BYTES, or ARRAYBUFFER');
      }
      m = u(h.slice(), n, e, r(l), f);
      for (g = 1; g < t; g += 1) !0 === z && 0 !== f % 32 &&
      (m[m.length - 1] &= 16777215 >>> 24 - f % 32), m = u(m, f,
          0, A(c), f);
      return k(m);
    };
    this.getHMAC = function(a, b) {
      var k, g, d, p;
      if (!1 === I) throw Error(
          'Cannot call getHMAC without first setting HMAC key');
      d = N(b);
      switch (a) {
        case 'HEX':
          k = function(a) {
            return O(a, f, x, d);
          };
          break;
        case 'B64':
          k = function(a) {
            return P(a, f, x, d);
          };
          break;
        case 'BYTES':
          k = function(a) {
            return Q(a, f, x);
          };
          break;
        case 'ARRAYBUFFER':
          try {
            k = new ArrayBuffer(0);
          } catch (v) {
            throw Error('ARRAYBUFFER not supported by this environment');
          }
          k = function(a) {
            return R(a, f, x);
          };
          break;
        default:
          throw Error('outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER');
      }
      g = u(h.slice(), n, e, r(l), f);
      p = q(w, A(c));
      p = u(g, f, m, p, f);
      return k(p);
    };
  }

  function b(c, a) {
    this.a = c;
    this.b = a;
  }

  function O(c, a, b, e) {
    var h = '';
    a /= 8;
    var n, g, d;
    d = -1 === b ? 3 : 0;
    for (n = 0; n < a; n += 1) g = c[n >>> 2] >>> 8 *
        (d + n % 4 * b), h += '0123456789abcdef'.charAt(g >>> 4 & 15) +
        '0123456789abcdef'.charAt(g & 15);
    return e.outputUpper ? h.toUpperCase() : h;
  }

  function P(c, a, b, e) {
    var h = '',
        n = a / 8,
        g, d, p, f;
    f = -1 === b ? 3 : 0;
    for (g = 0; g < n; g += 3)
      for (d = g + 1 < n ? c[g + 1 >>> 2] : 0, p = g + 2 < n ?
          c[g + 2 >>> 2] :
          0, p = (c[g >>> 2] >>> 8 * (f + g % 4 * b) & 255) << 16 |
          (d >>> 8 * (f + (g + 1) % 4 * b) & 255) << 8 | p >>> 8 * (f +
              (g + 2) % 4 * b) & 255, d = 0; 4 > d; d += 1) 8 * g + 6 * d <= a ?
          h += 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.charAt(
              p >>> 6 * (3 - d) & 63) :
          h += e.b64Pad;
    return h;
  }

  function Q(c, a, b) {
    var e = '';
    a /= 8;
    var h, d, g;
    g = -1 === b ? 3 : 0;
    for (h = 0; h < a; h += 1) d = c[h >>> 2] >>> 8 * (g + h % 4 * b) &
        255, e += String.fromCharCode(d);
    return e;
  }

  function R(c, a, b) {
    a /= 8;
    var e, h = new ArrayBuffer(a),
        d, g;
    g = new Uint8Array(h);
    d = -1 === b ? 3 : 0;
    for (e = 0; e < a; e += 1) g[e] = c[e >>> 2] >>> 8 * (d + e % 4 * b) & 255;
    return h;
  }

  function N(c) {
    var a = {
      outputUpper: !1,
      b64Pad: '=',
      shakeLen: -1,
    };
    c = c || {};
    a.outputUpper = c.outputUpper || !1;
    !0 === c.hasOwnProperty('b64Pad') && (a.b64Pad = c.b64Pad);
    if (!0 === c.hasOwnProperty('shakeLen')) {
      if (0 !== c.shakeLen % 8) throw Error('shakeLen must be a multiple of 8');
      a.shakeLen = c.shakeLen;
    }
    if ('boolean' !== typeof a.outputUpper) throw Error(
        'Invalid outputUpper formatting option');
    if ('string' !== typeof a.b64Pad) throw Error(
        'Invalid b64Pad formatting option');
    return a;
  }

  function M(c, a, b) {
    switch (a) {
      case 'UTF8':
      case 'UTF16BE':
      case 'UTF16LE':
        break;
      default:
        throw Error('encoding must be UTF8, UTF16BE, or UTF16LE');
    }
    switch (c) {
      case 'HEX':
        c = function(a, c, d) {
          var g = a.length,
              l, p, f, m, q, u;
          if (0 !== g % 2) throw Error(
              'String of HEX type must be in byte increments');
          c = c || [0];
          d = d || 0;
          q = d >>> 3;
          u = -1 === b ? 3 : 0;
          for (l = 0; l < g; l += 2) {
            p = parseInt(a.substr(l, 2), 16);
            if (isNaN(p)) throw Error(
                'String of HEX type contains invalid characters');
            m = (l >>> 1) + q;
            for (f = m >>> 2; c.length <= f;) c.push(0);
            c[f] |= p << 8 * (u + m % 4 * b);
          }
          return {
            value: c,
            binLen: 4 * g + d,
          };
        };
        break;
      case 'TEXT':
        c = function(c, h, d) {
          var g, l, p = 0,
              f, m, q, u, r, t;
          h = h || [0];
          d = d || 0;
          q = d >>> 3;
          if ('UTF8' === a)
            for (t = -1 ===
            b ? 3 : 0, f = 0; f < c.length; f += 1)
              for (g = c.charCodeAt(f), l = [], 128 > g ?
                  l.push(g) :
                  2048 > g ?
                      (l.push(192 | g >>> 6), l.push(128 | g & 63)) :
                      55296 > g || 57344 <= g ?
                          l.push(224 | g >>> 12, 128 | g >>> 6 & 63,
                              128 | g & 63) :
                          (f += 1, g = 65536 +
                              ((g & 1023) << 10 | c.charCodeAt(f) &
                                  1023), l.push(240 | g >>> 18,
                              128 | g >>> 12 & 63, 128 | g >>> 6 & 63,
                              128 | g & 63)), m = 0; m < l.length; m += 1) {
                r = p + q;
                for (u = r >>> 2; h.length <= u;) h.push(0);
                h[u] |= l[m] << 8 * (t + r % 4 * b);
                p += 1;
              } else if ('UTF16BE' === a || 'UTF16LE' === a)
            for (t = -1 === b ? 2 : 0, l = 'UTF16LE' === a && 1 !== b ||
                'UTF16LE' !== a && 1 === b, f = 0; f < c.length; f += 1) {
              g = c.charCodeAt(f);
              !0 === l && (m = g & 255, g = m << 8 | g >>> 8);
              r = p + q;
              for (u = r >>> 2; h.length <= u;) h.push(0);
              h[u] |= g << 8 * (t + r % 4 * b);
              p += 2;
            }
          return {
            value: h,
            binLen: 8 * p + d,
          };
        };
        break;
      case 'B64':
        c = function(a, c, d) {
          var g = 0,
              l, p, f, m, q, u, r, t;
          if (-1 === a.search(/^[a-zA-Z0-9=+\/]+$/)) throw Error(
              'Invalid character in base-64 string');
          p = a.indexOf('=');
          a = a.replace(/\=/g, '');
          if (-1 !== p && p < a.length) throw Error(
              'Invalid \'=\' found in base-64 string');
          c = c || [0];
          d = d || 0;
          u = d >>> 3;
          t = -1 === b ? 3 : 0;
          for (p = 0; p < a.length; p += 4) {
            q = a.substr(p, 4);
            for (f = m = 0; f <
            q.length; f += 1) l = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.indexOf(
                q[f]),
                m |= l << 18 - 6 * f;
            for (f = 0; f < q.length - 1; f += 1) {
              r = g + u;
              for (l = r >>> 2; c.length <= l;) c.push(0);
              c[l] |= (m >>> 16 - 8 * f & 255) << 8 * (t + r % 4 * b);
              g += 1;
            }
          }
          return {
            value: c,
            binLen: 8 * g + d,
          };
        };
        break;
      case 'BYTES':
        c = function(a, c, d) {
          var g, l, p, f, m, q;
          c = c || [0];
          d = d || 0;
          p = d >>> 3;
          q = -1 === b ? 3 : 0;
          for (l = 0; l < a.length; l += 1) g = a.charCodeAt(l), m = l +
              p, f = m >>> 2, c.length <= f && c.push(0), c[f] |= g << 8 *
              (q + m % 4 * b);
          return {
            value: c,
            binLen: 8 * a.length + d,
          };
        };
        break;
      case 'ARRAYBUFFER':
        try {
          c = new ArrayBuffer(0);
        } catch (e) {
          throw Error('ARRAYBUFFER not supported by this environment');
        }
        c =
            function(a, c, d) {
              var g, l, p, f, m, q;
              c = c || [0];
              d = d || 0;
              l = d >>> 3;
              m = -1 === b ? 3 : 0;
              q = new Uint8Array(a);
              for (g = 0; g < a.byteLength; g += 1) f = g + l, p = f >>>
                  2, c.length <= p && c.push(0), c[p] |= q[g] << 8 *
                  (m + f % 4 * b);
              return {
                value: c,
                binLen: 8 * a.byteLength + d,
              };
            };
        break;
      default:
        throw Error('format must be HEX, TEXT, B64, BYTES, or ARRAYBUFFER');
    }
    return c;
  }

  function y(c, a) {
    return c << a | c >>> 32 - a;
  }

  function S(c, a) {
    return 32 < a ?
        (a -= 32, new b(c.b << a | c.a >>> 32 - a, c.a << a | c.b >>> 32 - a)) :
        0 !== a ?
            new b(c.a << a | c.b >>> 32 - a, c.b << a | c.a >>> 32 - a) :
            c;
  }

  function w(c, a) {
    return c >>>
        a | c << 32 - a;
  }

  function t(c, a) {
    var k = null,
        k = new b(c.a, c.b);
    return k = 32 >= a ?
        new b(k.a >>> a | k.b << 32 - a & 4294967295,
            k.b >>> a | k.a << 32 - a & 4294967295) :
        new b(k.b >>> a - 32 | k.a << 64 - a & 4294967295,
            k.a >>> a - 32 | k.b << 64 - a & 4294967295);
  }

  function T(c, a) {
    var k = null;
    return k = 32 >= a ?
        new b(c.a >>> a, c.b >>> a | c.a << 32 - a & 4294967295) :
        new b(0, c.a >>> a - 32);
  }

  function aa(c, a, b) {
    return c & a ^ ~c & b;
  }

  function ba(c, a, k) {
    return new b(c.a & a.a ^ ~c.a & k.a, c.b & a.b ^ ~c.b & k.b);
  }

  function U(c, a, b) {
    return c & a ^ c & b ^ a & b;
  }

  function ca(c, a, k) {
    return new b(c.a & a.a ^ c.a & k.a ^ a.a &
        k.a, c.b & a.b ^ c.b & k.b ^ a.b & k.b);
  }

  function da(c) {
    return w(c, 2) ^ w(c, 13) ^ w(c, 22);
  }

  function ea(c) {
    var a = t(c, 28),
        k = t(c, 34);
    c = t(c, 39);
    return new b(a.a ^ k.a ^ c.a, a.b ^ k.b ^ c.b);
  }

  function fa(c) {
    return w(c, 6) ^ w(c, 11) ^ w(c, 25);
  }

  function ga(c) {
    var a = t(c, 14),
        k = t(c, 18);
    c = t(c, 41);
    return new b(a.a ^ k.a ^ c.a, a.b ^ k.b ^ c.b);
  }

  function ha(c) {
    return w(c, 7) ^ w(c, 18) ^ c >>> 3;
  }

  function ia(c) {
    var a = t(c, 1),
        k = t(c, 8);
    c = T(c, 7);
    return new b(a.a ^ k.a ^ c.a, a.b ^ k.b ^ c.b);
  }

  function ja(c) {
    return w(c, 17) ^ w(c, 19) ^ c >>> 10;
  }

  function ka(c) {
    var a = t(c, 19),
        k = t(c, 61);
    c = T(c, 6);
    return new b(a.a ^ k.a ^ c.a, a.b ^ k.b ^ c.b);
  }

  function G(c, a) {
    var b = (c & 65535) + (a & 65535);
    return ((c >>> 16) + (a >>> 16) + (b >>> 16) & 65535) << 16 | b & 65535;
  }

  function la(c, a, b, e) {
    var h = (c & 65535) + (a & 65535) + (b & 65535) + (e & 65535);
    return ((c >>> 16) + (a >>> 16) + (b >>> 16) + (e >>> 16) + (h >>> 16) &
        65535) << 16 | h & 65535;
  }

  function H(c, a, b, e, h) {
    var d = (c & 65535) + (a & 65535) + (b & 65535) + (e & 65535) + (h & 65535);
    return ((c >>> 16) + (a >>> 16) + (b >>> 16) + (e >>> 16) + (h >>> 16) +
        (d >>> 16) & 65535) << 16 | d & 65535;
  }

  function ma(c, a) {
    var d, e, h;
    d = (c.b & 65535) + (a.b & 65535);
    e = (c.b >>> 16) +
        (a.b >>> 16) + (d >>> 16);
    h = (e & 65535) << 16 | d & 65535;
    d = (c.a & 65535) + (a.a & 65535) + (e >>> 16);
    e = (c.a >>> 16) + (a.a >>> 16) + (d >>> 16);
    return new b((e & 65535) << 16 | d & 65535, h);
  }

  function na(c, a, d, e) {
    var h, n, g;
    h = (c.b & 65535) + (a.b & 65535) + (d.b & 65535) + (e.b & 65535);
    n = (c.b >>> 16) + (a.b >>> 16) + (d.b >>> 16) + (e.b >>> 16) + (h >>> 16);
    g = (n & 65535) << 16 | h & 65535;
    h = (c.a & 65535) + (a.a & 65535) + (d.a & 65535) + (e.a & 65535) +
        (n >>> 16);
    n = (c.a >>> 16) + (a.a >>> 16) + (d.a >>> 16) + (e.a >>> 16) + (h >>> 16);
    return new b((n & 65535) << 16 | h & 65535, g);
  }

  function oa(c, a, d, e, h) {
    var n, g, l;
    n = (c.b &
        65535) + (a.b & 65535) + (d.b & 65535) + (e.b & 65535) + (h.b & 65535);
    g = (c.b >>> 16) + (a.b >>> 16) + (d.b >>> 16) + (e.b >>> 16) +
        (h.b >>> 16) + (n >>> 16);
    l = (g & 65535) << 16 | n & 65535;
    n = (c.a & 65535) + (a.a & 65535) + (d.a & 65535) + (e.a & 65535) +
        (h.a & 65535) + (g >>> 16);
    g = (c.a >>> 16) + (a.a >>> 16) + (d.a >>> 16) + (e.a >>> 16) +
        (h.a >>> 16) + (n >>> 16);
    return new b((g & 65535) << 16 | n & 65535, l);
  }

  function B(c, a) {
    return new b(c.a ^ a.a, c.b ^ a.b);
  }

  function A(c) {
    var a = [],
        d;
    if ('SHA-1' === c) a = [
      1732584193,
      4023233417,
      2562383102,
      271733878,
      3285377520];
    else if (0 === c.lastIndexOf('SHA-', 0)) switch (a = [
      3238371032,
      914150663,
      812702999,
      4144912697,
      4290775857,
      1750603025,
      1694076839,
      3204075428], d = [
      1779033703,
      3144134277,
      1013904242,
      2773480762,
      1359893119,
      2600822924,
      528734635,
      1541459225], c) {
      case 'SHA-224':
        break;
      case 'SHA-256':
        a = d;
        break;
      case 'SHA-384':
        a = [
          new b(3418070365, a[0]),
          new b(1654270250, a[1]),
          new b(2438529370, a[2]),
          new b(355462360, a[3]),
          new b(1731405415, a[4]),
          new b(41048885895, a[5]),
          new b(3675008525, a[6]),
          new b(1203062813, a[7])];
        break;
      case 'SHA-512':
        a = [
          new b(d[0], 4089235720),
          new b(d[1], 2227873595),
          new b(d[2], 4271175723),
          new b(d[3], 1595750129),
          new b(d[4], 2917565137),
          new b(d[5], 725511199),
          new b(d[6], 4215389547),
          new b(d[7], 327033209),
        ];
        break;
      default:
        throw Error('Unknown SHA variant');
    } else if (0 === c.lastIndexOf('SHA3-', 0) || 0 ===
        c.lastIndexOf('SHAKE', 0))
      for (c = 0; 5 > c; c += 1) a[c] = [
        new b(0, 0),
        new b(0, 0),
        new b(0, 0),
        new b(0, 0),
        new b(0, 0)];
    else throw Error('No SHA variants supported');
    return a;
  }

  function K(c, a) {
    var b = [],
        e, d, n, g, l, p, f;
    e = a[0];
    d = a[1];
    n = a[2];
    g = a[3];
    l = a[4];
    for (f = 0; 80 > f; f += 1) b[f] = 16 > f ? c[f] : y(b[f -
    3] ^ b[f - 8] ^ b[f - 14] ^ b[f - 16], 1), p = 20 > f ?
        H(y(e, 5), d & n ^ ~d & g, l, 1518500249, b[f]) :
        40 > f ?
            H(y(e, 5), d ^ n ^ g, l, 1859775393, b[f]) :
            60 > f ?
                H(y(e, 5), U(d, n, g), l, 2400959708, b[f]) :
                H(y(e, 5), d ^ n ^ g, l, 3395469782, b[f]), l = g, g = n, n = y(
        d, 30), d = e, e = p;
    a[0] = G(e, a[0]);
    a[1] = G(d, a[1]);
    a[2] = G(n, a[2]);
    a[3] = G(g, a[3]);
    a[4] = G(l, a[4]);
    return a;
  }

  function Z(c, a, b, e) {
    var d;
    for (d = (a + 65 >>> 9 << 4) + 15; c.length <= d;) c.push(0);
    c[a >>> 5] |= 128 << 24 - a % 32;
    a += b;
    c[d] = a & 4294967295;
    c[d - 1] = a / 4294967296 | 0;
    a = c.length;
    for (d = 0; d < a; d += 16) e = K(c.slice(d, d + 16), e);
    return e;
  }

  function L(c,
             a, k) {
    var e, h, n, g, l, p, f, m, q, u, r, t, v, w, y, A, z, x, F, B, C, D,
        E = [],
        J;
    if ('SHA-224' === k || 'SHA-256' ===
        k) u = 64, t = 1, D = Number, v = G, w = la, y = H, A = ha, z = ja, x = da, F = fa, C = U, B = aa, J = d;
    else if ('SHA-384' === k || 'SHA-512' ===
        k) u = 80, t = 2, D = b, v = ma, w = na, y = oa, A = ia, z = ka, x = ea, F = ga, C = ca, B = ba, J = V;
    else throw Error('Unexpected error in SHA-2 implementation');
    k = a[0];
    e = a[1];
    h = a[2];
    n = a[3];
    g = a[4];
    l = a[5];
    p = a[6];
    f = a[7];
    for (r = 0; r < u; r += 1) 16 > r ?
        (q = r * t, m = c.length <= q ? 0 : c[q], q = c.length <= q + 1 ?
            0 :
            c[q + 1], E[r] = new D(m, q)) :
        E[r] = w(z(E[r - 2]), E[r - 7], A(E[r - 15]), E[r -
        16]), m = y(f, F(g), B(g, l, p), J[r], E[r]), q = v(x(k),
        C(k, e, h)), f = p, p = l, l = g, g = v(n,
        m), n = h, h = e, e = k, k = v(m, q);
    a[0] = v(k, a[0]);
    a[1] = v(e, a[1]);
    a[2] = v(h, a[2]);
    a[3] = v(n, a[3]);
    a[4] = v(g, a[4]);
    a[5] = v(l, a[5]);
    a[6] = v(p, a[6]);
    a[7] = v(f, a[7]);
    return a;
  }

  function D(c, a) {
    var d, e, h, n, g = [],
        l = [];
    if (null !== c)
      for (e = 0; e < c.length; e += 2) a[(e >>> 1) % 5][(e >>> 1) / 5 | 0] = B(
          a[(e >>> 1) % 5][(e >>> 1) / 5 | 0], new b(c[e + 1], c[e]));
    for (d = 0; 24 > d; d += 1) {
      n = A('SHA3-');
      for (e = 0; 5 > e; e += 1) {
        h = a[e][0];
        var p = a[e][1],
            f = a[e][2],
            m = a[e][3],
            q = a[e][4];
        g[e] = new b(h.a ^ p.a ^ f.a ^
            m.a ^ q.a, h.b ^ p.b ^ f.b ^ m.b ^ q.b);
      }
      for (e = 0; 5 > e; e += 1) l[e] = B(g[(e + 4) % 5], S(g[(e + 1) % 5], 1));
      for (e = 0; 5 > e; e += 1)
        for (h = 0; 5 > h; h += 1) a[e][h] = B(a[e][h], l[e]);
      for (e = 0; 5 > e; e += 1)
        for (h = 0; 5 > h; h += 1) n[h][(2 * e + 3 * h) % 5] = S(a[e][h],
            W[e][h]);
      for (e = 0; 5 > e; e += 1)
        for (h = 0; 5 > h; h += 1) a[e][h] = B(n[e][h],
            new b(~n[(e + 1) % 5][h].a & n[(e + 2) % 5][h].a,
                ~n[(e + 1) % 5][h].b & n[(e + 2) % 5][h].b));
      a[0][0] = B(a[0][0], X[d]);
    }
    return a;
  }

  var d, V, W, X;
  d = [
    1116352408,
    1899447441,
    3049323471,
    3921009573,
    961987163,
    1508970993,
    2453635748,
    2870763221,
    3624381080,
    310598401,
    607225278,
    1426881987,
    1925078388,
    2162078206,
    2614888103,
    3248222580,
    3835390401,
    4022224774,
    264347078,
    604807628,
    770255983,
    1249150122,
    1555081692,
    1996064986,
    2554220882,
    2821834349,
    2952996808,
    3210313671,
    3336571891,
    3584528711,
    113926993,
    338241895,
    666307205,
    773529912,
    1294757372,
    1396182291,
    1695183700,
    1986661051,
    2177026350,
    2456956037,
    2730485921,
    2820302411,
    3259730800,
    3345764771,
    3516065817,
    3600352804,
    4094571909,
    275423344,
    430227734,
    506948616,
    659060556,
    883997877,
    958139571,
    1322822218,
    1537002063,
    1747873779,
    1955562222,
    2024104815,
    2227730452,
    2361852424,
    2428436474,
    2756734187,
    3204031479,
    3329325298,
  ];
  V = [
    new b(d[0], 3609767458),
    new b(d[1], 602891725),
    new b(d[2], 3964484399),
    new b(d[3], 2173295548),
    new b(d[4], 4081628472),
    new b(d[5], 3053834265),
    new b(d[6], 2937671579),
    new b(d[7], 3664609560),
    new b(d[8], 2734883394),
    new b(d[9], 1164996542),
    new b(d[10], 1323610764),
    new b(d[11], 3590304994),
    new b(d[12], 4068182383),
    new b(d[13], 991336113),
    new b(d[14], 633803317),
    new b(d[15], 3479774868),
    new b(d[16], 2666613458),
    new b(d[17], 944711139),
    new b(d[18], 2341262773),
    new b(d[19], 2007800933),
    new b(d[20], 1495990901),
    new b(d[21], 1856431235),
    new b(d[22], 3175218132),
    new b(d[23], 2198950837),
    new b(d[24], 3999719339),
    new b(d[25], 766784016),
    new b(d[26], 2566594879),
    new b(d[27], 3203337956),
    new b(d[28], 1034457026),
    new b(d[29], 2466948901),
    new b(d[30], 3758326383),
    new b(d[31], 168717936),
    new b(d[32], 1188179964),
    new b(d[33], 1546045734),
    new b(d[34], 1522805485),
    new b(d[35], 2643833823),
    new b(d[36], 2343527390),
    new b(d[37], 1014477480),
    new b(d[38], 1206759142),
    new b(d[39], 344077627),
    new b(d[40], 1290863460),
    new b(d[41], 3158454273),
    new b(d[42], 3505952657),
    new b(d[43], 106217008),
    new b(d[44], 3606008344),
    new b(d[45], 1432725776),
    new b(d[46], 1467031594),
    new b(d[47], 851169720),
    new b(d[48], 3100823752),
    new b(d[49], 1363258195),
    new b(d[50], 3750685593),
    new b(d[51], 3785050280),
    new b(d[52], 3318307427),
    new b(d[53], 3812723403),
    new b(d[54], 2003034995),
    new b(d[55], 3602036899),
    new b(d[56], 1575990012),
    new b(d[57], 1125592928),
    new b(d[58], 2716904306),
    new b(d[59], 442776044),
    new b(d[60], 593698344),
    new b(d[61],
        3733110249),
    new b(d[62], 2999351573),
    new b(d[63], 3815920427),
    new b(3391569614, 3928383900),
    new b(3515267271, 566280711),
    new b(3940187606, 3454069534),
    new b(4118630271, 4000239992),
    new b(116418474, 1914138554),
    new b(174292421, 2731055270),
    new b(289380356, 3203993006),
    new b(460393269, 320620315),
    new b(685471733, 587496836),
    new b(852142971, 1086792851),
    new b(1017036298, 365543100),
    new b(1126000580, 2618297676),
    new b(1288033470, 3409855158),
    new b(1501505948, 4234509866),
    new b(1607167915, 987167468),
    new b(1816402316,
        1246189591),
  ];
  X = [
    new b(0, 1),
    new b(0, 32898),
    new b(2147483648, 32906),
    new b(2147483648, 2147516416),
    new b(0, 32907),
    new b(0, 2147483649),
    new b(2147483648, 2147516545),
    new b(2147483648, 32777),
    new b(0, 138),
    new b(0, 136),
    new b(0, 2147516425),
    new b(0, 2147483658),
    new b(0, 2147516555),
    new b(2147483648, 139),
    new b(2147483648, 32905),
    new b(2147483648, 32771),
    new b(2147483648, 32770),
    new b(2147483648, 128),
    new b(0, 32778),
    new b(2147483648, 2147483658),
    new b(2147483648, 2147516545),
    new b(2147483648, 32896),
    new b(0, 2147483649),
    new b(2147483648, 2147516424),
  ];
  W = [
    [0, 36, 3, 41, 18],
    [1, 44, 10, 45, 2],
    [62, 6, 43, 15, 61],
    [28, 55, 25, 21, 56],
    [27, 20, 39, 8, 14],
  ];
  'function' === typeof define && define.amd ?
      define(function() {
        return C;
      }) :
      'undefined' !== typeof exports ?
          ('undefined' !== typeof module && module.exports &&
          (module.exports = C), exports = C) :
          Y.jsSHA = C;
})(this);

var CreateDownloadTask, CreateDownloadTask_, browserInterface, bytesToString,
    cloneInto, g_key_l, g_key_s, g_kts, m115_asym_decode, m115_asym_encode,
    m115_decode, m115_encode, m115_getkey, m115_sym_decode, m115_sym_encode,
    ref, stringToBytes, xor115_enc;

class MyRsa {
  constructor() {
    // this.n = BigInt('0x8686980c0f5a24c4b9d43020cd2c22703ff3f450756529058b1cf88f09b8602136477198a6e2683149659bd122c33592fdb5ad47944ad1ea4d36c6b172aad6338c3bb6ac6227502d010993ac967d1aef00f0c8e038de2e4d3bc2ec368af2e9f10a6f1eda4f7262f136420c07c331b871bf139f74f3010e3c4fe57df3afb71683')
    // this.e = BigInt('0x10001')
    this.n = bigInt(
        '8686980c0f5a24c4b9d43020cd2c22703ff3f450756529058b1cf88f09b8602136477198a6e2683149659bd122c33592fdb5ad47944ad1ea4d36c6b172aad6338c3bb6ac6227502d010993ac967d1aef00f0c8e038de2e4d3bc2ec368af2e9f10a6f1eda4f7262f136420c07c331b871bf139f74f3010e3c4fe57df3afb71683',
        16);
    this.e = bigInt('10001', 16);
  };

  a2hex(byteArray) {
    var hexString = '';
    var nextHexByte;
    for (var i = 0; i < byteArray.length; i++) {
      nextHexByte = byteArray[i].toString(16);
      if (nextHexByte.length < 2) {
        nextHexByte = '0' + nextHexByte;
      }
      hexString += nextHexByte;
    }
    return hexString;
  }

  hex2a(hex) {
    var str = '';
    for (var i = 0; i < hex.length; i += 2) {
      str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    }
    return str;
  }

  pkcs1pad2(s, n) {
    if (n < s.length + 11) {
      return null;
    }
    var ba = [];
    var i = s.length - 1;
    while (i >= 0 && n > 0) {
      ba[--n] = s.charCodeAt(i--);
    }
    ba[--n] = 0;
    while (n > 2) { // random non-zero pad
      ba[--n] = 0xff;
    }
    ba[--n] = 2;
    ba[--n] = 0;
    var c = this.a2hex(ba);
    return bigInt(c, 16);
  }

  pkcs1unpad2(a) {
    var b = a.toString(16);
    if (b.length % 2 !== 0) {
      b = '0' + b;
    }
    var c = this.hex2a(b);
    var i = 1;
    while (c.charCodeAt(i) !== 0) {
      i++;
    }
    return c.slice(i + 1);
  }

  encrypt(text) {
    var m = this.pkcs1pad2(text, 0x80);
    var c = m.modPow(this.e, this.n);
    var h = c.toString(16);
    while (h.length < 0x80 * 2) {
      h = '0' + h;
    }
    return h;
  };

  decrypt(text) {
    var ba = [];
    var i = 0;
    while (i < text.length) {
      ba[i] = text.charCodeAt(i);
      i += 1;
    }
    var a = bigInt(this.a2hex(ba), 16);
    var c = a.modPow(this.e, this.n);
    var d = this.pkcs1unpad2(c);
    return d;
  };
}

var new_rsa = new MyRsa();

g_kts = [
  240,
  229,
  105,
  174,
  191,
  220,
  191,
  138,
  26,
  69,
  232,
  190,
  125,
  166,
  115,
  184,
  222,
  143,
  231,
  196,
  69,
  218,
  134,
  196,
  155,
  100,
  139,
  20,
  106,
  180,
  241,
  170,
  56,
  1,
  53,
  158,
  38,
  105,
  44,
  134,
  0,
  107,
  79,
  165,
  54,
  52,
  98,
  166,
  42,
  150,
  104,
  24,
  242,
  74,
  253,
  189,
  107,
  151,
  143,
  77,
  143,
  137,
  19,
  183,
  108,
  142,
  147,
  237,
  14,
  13,
  72,
  62,
  215,
  47,
  136,
  216,
  254,
  254,
  126,
  134,
  80,
  149,
  79,
  209,
  235,
  131,
  38,
  52,
  219,
  102,
  123,
  156,
  126,
  157,
  122,
  129,
  50,
  234,
  182,
  51,
  222,
  58,
  169,
  89,
  52,
  102,
  59,
  170,
  186,
  129,
  96,
  72,
  185,
  213,
  129,
  156,
  248,
  108,
  132,
  119,
  255,
  84,
  120,
  38,
  95,
  190,
  232,
  30,
  54,
  159,
  52,
  128,
  92,
  69,
  44,
  155,
  118,
  213,
  27,
  143,
  204,
  195,
  184,
  245];

g_key_s = [0x29, 0x23, 0x21, 0x5E];

g_key_l = [120, 6, 173, 76, 51, 134, 93, 24, 76, 1, 63, 70];

m115_getkey = function(length, key) {
  var i;
  if (key != null) {
    return (function() {
      var j, ref, results;
      results = [];
      for (i = j = 0, ref = length; (0 <= ref ? j < ref : j > ref); i = 0 <=
      ref ? ++j : --j) {
        results.push(((key[i] + g_kts[length * i]) & 0xff) ^
            g_kts[length * (length - 1 - i)]);
      }
      return results;
    })();
  }
  if (length === 12) {
    return g_key_l.slice(0);
  }
  return g_key_s.slice(0);
};

xor115_enc = function(src, srclen, key, keylen) {
  var i, j, k, mod4, ref, ref1, ref2, ret;
  mod4 = srclen % 4;
  ret = [];
  if (mod4 !== 0) {
    for (i = j = 0, ref = mod4; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ?
        ++j :
        --j) {
      ret.push(src[i] ^ key[i % keylen]);
    }
  }
  for (i = k = ref1 = mod4, ref2 = srclen; (ref1 <= ref2 ?
      k < ref2 :
      k > ref2); i = ref1 <= ref2 ? ++k : --k) {
    ret.push(src[i] ^ key[(i - mod4) % keylen]);
  }
  return ret;
};

m115_sym_encode = function(src, srclen, key1, key2) {
  var k1, k2, ret;
  k1 = m115_getkey(4, key1);
  k2 = m115_getkey(12, key2);
  ret = xor115_enc(src, srclen, k1, 4);
  ret.reverse();
  ret = xor115_enc(ret, srclen, k2, 12);
  return ret;
};

m115_sym_decode = function(src, srclen, key1, key2) {
  var k1, k2, ret;
  k1 = m115_getkey(4, key1);
  k2 = m115_getkey(12, key2);
  ret = xor115_enc(src, srclen, k2, 12);
  ret.reverse();
  ret = xor115_enc(ret, srclen, k1, 4);
  return ret;
};

stringToBytes = function(s) {
  var i, j, ref, ret;
  ret = [];
  for (i = j = 0, ref = s.length; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ?
      ++j :
      --j) {
    ret.push(s.charCodeAt(i));
  }
  return ret;
};

bytesToString = function(b) {
  var i, j, len, ret;
  ret = '';
  for (j = 0, len = b.length; j < len; j++) {
    i = b[j];
    ret += String.fromCharCode(i);
  }
  return ret;
};

m115_asym_encode = function(src, srclen) {
  var i, j, m, ref, ret;
  m = 128 - 11;
  ret = '';
  for (i = j = 0, ref = Math.floor((srclen + m - 1) / m); (0 <= ref ?
      j < ref :
      j > ref); i = 0 <= ref ? ++j : --j) {
    ret += new_rsa.encrypt(
        bytesToString(src.slice(i * m, Math.min((i + 1) * m, srclen))));
  }
  return window.btoa(new_rsa.hex2a(ret));
};

m115_asym_decode = function(src, srclen) {
  var i, j, m, ref, ret;
  m = 128;
  ret = '';
  for (i = j = 0, ref = Math.floor((srclen + m - 1) / m); (0 <= ref ?
      j < ref :
      j > ref); i = 0 <= ref ? ++j : --j) {
    ret += new_rsa.decrypt(
        bytesToString(src.slice(i * m, Math.min((i + 1) * m, srclen))));
  }
  return stringToBytes(ret);
};

m115_encode = function(src, tm) {
  var key, tmp, zz;
  key = stringToBytes(md5(`!@###@#${tm}DFDR@#@#`));
  tmp = stringToBytes(src);
  console.log(tmp);
  tmp = m115_sym_encode(tmp, tmp.length, key, null);
  console.log(tmp);
  tmp = key.slice(0, 16).concat(tmp);
  return {
    data: m115_asym_encode(tmp, tmp.length),
    key,
  };
};

m115_decode = function(src, key) {
  console.log(src, key);
  var tmp;
  tmp = stringToBytes(window.atob(src));
  tmp = m115_asym_decode(tmp, tmp.length);
  return bytesToString(
      m115_sym_decode(tmp.slice(16), tmp.length - 16, key, tmp.slice(0, 16)));
};

/*GM_config_zh-CN.js*/

// ==UserScript==
// @name          GM_config_zh-CN
// @author        Mike Medley & zxf10608
// @version       1.3.7
// @description   GM_config_中文版
// @grant         GM_getValue
// @grant         GM_setValue
// @grant         GM_deleteValue
// @exclude       *
// @license       LGPL 3
// ==/UserScript==

/*
 优化说明
 1、改成中文 "确定"、"取消" 按钮。
 2、select 新增了 textContents 数组。
 3、新增了skin: 'tab'换页切换菜单样式
 4、更新部分翻译
 5、优化字体显示效果
 6、优化同一行内CSS样式
 */

// The GM_config constructor
function GM_configStruct() {
  // call init() if settings were passed to constructor
  if (arguments.length) {
    GM_configInit(this, arguments);
    this.onInit();
  }
}

// This is the initializer function
function GM_configInit(config, args) {
  // Initialize instance variables
  if (typeof config.fields == 'undefined') {
    config.fields = {};
    config.onInit = config.onInit || function() {
    };
    config.onOpen = config.onOpen || function() {
    };
    config.onSave = config.onSave || function() {
    };
    config.onClose = config.onClose || function() {
    };
    config.onReset = config.onReset || function() {
    };
    config.isOpen = false;
    config.title = '用户脚本设置';
    config.css = {
      basic: [
        '#GM_config * { font-family: arial,tahoma,myriad pro,sans-serif; }',
        '#GM_config { background: #FFF; }',
        '#GM_config input[type=\'radio\'] { margin-right: 8px; }',
        '#GM_config .indent40 { margin-left: 40%; }',
        '#GM_config .field_label { font-size: 14px; font-weight: bold; margin-right: 6px; }',
        '#GM_config .radio_label { font-size: 14px; }',
        '#GM_config .block { display: block; }',
        '#GM_config .saveclose_buttons { margin: 16px 10px 10px; padding: 2px 12px; }',
        '#GM_config .reset, #GM_config .reset a,' +
        ' #GM_config_buttons_holder { color: #000; text-align: right; }',
        '#GM_config .config_header { font-size: 20pt; margin: 0; }',
        '#GM_config .config_desc, #GM_config .section_desc, #GM_config .reset { font-size: 9pt; }',
        '#GM_config .center { text-align: center; }',
        '#GM_config .section_header_holder { margin-top: 8px; }',
        '#GM_config .config_var { margin: 0 0 4px; }',
        '#GM_config .section_header { background: #414141; border: 1px solid #000; color: #FFF;' +
        ' font-size: 12pt; margin: 0; }',
        '#GM_config .section_desc { background: #EFEFEF; border: 1px solid #CCC; color: #575757;' +
        ' font-size: 10pt; margin: 0 0 6px; }',
        // newer
        '#GM_config input[type=\'number\'] { width: 60px; }',
        '#GM_config .nav-tabs { margin: 10 0}',
        '#GM_config .nav-tabs > div { display: inline; padding: 3px 10px; }',
        '#pv-prefs .section_header_holder { padding-left: 10px; }',
      ].join('\n') + '\n',
      skin_tab: [
        '#GM_config { background: #EEE; }',
        '#GM_config textarea { width: 98%; height: 45px; margin-top: 5px; }',
        '#GM_config .field_label { display: inline-block; font-weight: normal; }',
        // 在同一行内的设置
        '#GM_config .inline input[type=\'checkbox\'] {margin: 3px 3px 3px 0px;}',
        '#GM_config .inline .config_var { margin-left: 15px; }',
        // 内容样式
        '#GM_config .config_var { font-size: 14px; padding: 5px; margin: 0; }',
        '#GM_config .config_header a { text-decoration: none; color: #000; }',
        '#GM_config .nav-tabs { margin: 20 0}',
        '#GM_config .nav-tabs > div { font-size: 15px; color: #999; cursor: pointer; padding: 10px 20px; }',
        '#GM_config .nav-tabs > .active { cursor: default; color: #FFF; }',
        '#GM_config .nav-tabs > div:hover { color: #FFF; }',
      ].join('\n') + '\n',
      skin_1: [ // 仿 Mouseover Popup Image Viewer 样式
        '#GM_config { background: #EEE; }',
        '#GM_config textarea { width: 98%; height: 45px; margin-top: 5px; }',
        '#GM_config .config_var { font-size: 12px; }',
        '#GM_config .inline .config_var { margin-left: 15px; }',
        '#GM_config .field_label { display: inline-block; font-weight: normal; }',
        '#GM_config { padding: 20px 30px; margin: 0; }',
        '#GM_config .config_header { margin-bottom: 10px; }',
        '#GM_config div.config_var { padding: 7px 0; }',
      ].join('\n') + '\n',
      basicPrefix: 'GM_config',
      stylish: '',
    };
  }

  if (args.length == 1 &&
      typeof args[0].id == 'string' &&
      typeof args[0].appendChild != 'function') var settings = args[0];
  else {
    // Provide backwards-compatibility with argument style intialization
    var settings = {};

    // loop through GM_config.init() arguments
    for (var i = 0, l = args.length, arg; i < l; ++i) {
      arg = args[i];

      // An element to use as the config window
      if (typeof arg.appendChild == 'function') {
        settings.frame = arg;
        continue;
      }

      switch (typeof arg) {
        case 'object':
          for (var j in arg) { // could be a callback functions or settings object
            if (typeof arg[j] != 'function') { // we are in the settings object
              if (typeof arg[j] == 'string') {
                settings.frameStyle = arg;
              } else {
                settings.fields = arg; // store settings object
              }
              break; // leave the loop
            } // otherwise it must be a callback function
            if (!settings.events) settings.events = {};
            settings.events[j] = arg[j];
          }
          break;
        case 'function': // passing a bare function is set to open callback
          settings.events = {
            open: arg,
          };
          break;
        case 'string': // could be custom CSS or the title string
          // if (/[\w\.]+\s*\{\s*[\w-]+\s*:\s*\w+[\s|\S]*\}/.test(arg))
          if (/[\w\.]+\s*\{\s*[\w-]+\s*:[\s|\S]*\}/.test(arg))
            settings.css = arg;
          else if (arg)
            settings.title = arg;
          break;
      }
    }
  }

  /* Initialize everything using the new settings object */
  // Set the id
  if (settings.id) config.id = settings.id;
  else if (typeof config.id == 'undefined') config.id = 'GM_config';

  // Set the title
  if (settings.title) config.title = settings.title;

  // Set the custom css
  if (settings.css) config.css.stylish = settings.css;

  if (settings.skin) {
    var skin = config.css['skin_' + settings.skin];
    if (skin) {
      config.css.basic += skin;
    }
  }

  // Set the frame
  if (settings.frame) config.frame = settings.frame;
  if (settings.frameStyle) config.frameStyle = settings.frameStyle;

  config.isTabs = settings.isTabs;

  // Set the event callbacks
  if (settings.events) {
    var events = settings.events;
    for (var e in events)
      config['on' + e.charAt(0).toUpperCase() + e.slice(1)] = events[e];
  }

  // Create the fields
  if (settings.fields) {
    var stored = config.read(), // read the stored settings
        fields = settings.fields,
        customTypes = settings.types || {};

    for (var id in fields) {
      var field = fields[id];

      // for each field definition create a field object
      if (field)
        config.fields[id] = new GM_configField(field, stored[id], id,
            customTypes[field.type]);
      else if (config.fields[id]) delete config.fields[id];
    }
  }

  // If the id has changed we must modify the default style
  if (config.id != config.css.basicPrefix) {
    config.css.basic = config.css.basic.replace(
        new RegExp('#' + config.css.basicPrefix, 'gm'), '#' + config.id);
    config.css.basicPrefix = config.id;
  }
}

GM_configStruct.prototype = {
  // Support old method of initalizing
  init: function() {
    GM_configInit(this, arguments);
    this.onInit();
  },

  // call GM_config.open() from your script to open the menu
  open: function() {
    // Die if the menu is already open on this page
    // You can have multiple instances but you can't open the same instance twice
    var match = document.getElementById(this.id);
    if (match &&
        (match.tagName == 'IFRAME' || match.childNodes.length > 0)) return;

    // Sometimes "this" gets overwritten so create an alias
    var config = this;

    // Function to build the mighty config window :)
    function buildConfigWin(body, head) {
      var create = config.create,
          fields = config.fields,
          configId = config.id,
          bodyWrapper = create('div', {
            id: configId + '_wrapper',
          });

      // Append the style which is our default style plus the user style
      head.appendChild(
          create('style', {
            type: 'text/css',
            textContent: config.css.basic + config.css.stylish,
          }));

      // Add header and title
      bodyWrapper.appendChild(create('div', {
        id: configId + '_header',
        className: 'config_header block center',
      }, config.title));

      // Append elements
      var section = bodyWrapper,
          secNum = 0; // Section count
      var lastParentNode = null;

      // loop through fields
      for (var id in fields) {
        var field = fields[id],
            settings = field.settings;

        if (settings.section) { // the start of a new section
          section = bodyWrapper.appendChild(create('div', {
            className: 'section_header_holder',
            id: configId + '_section_' + secNum,
          }));

          if (Object.prototype.toString.call(settings.section) !==
              '[object Array]')
            settings.section = [settings.section];

          if (settings.section[0])
            section.appendChild(create('div', {
              className: 'section_header center',
              id: configId + '_section_header_' + secNum,
            }, settings.section[0]));

          if (settings.section[1])
            section.appendChild(create('p', {
              className: 'section_desc center',
              id: configId + '_section_desc_' + secNum,
            }, settings.section[1]));
          ++secNum;
        }

        if (settings.line == 'start' && lastParentNode) { // 切换到下一行
          lastParentNode = null;
        }

        // Create field elements and append to current section
        (lastParentNode || section).appendChild(
            (field.wrapper = field.toNode(configId, lastParentNode)));

        if (settings.line == 'start') {
          lastParentNode = field.wrapper;
          lastParentNode.classList.add('inline');
        } else if (settings.line == 'end') {
          lastParentNode = null;
        }
      }

      // Add save and close buttons
      bodyWrapper.appendChild(create('div', {
            id: configId + '_buttons_holder',
          },

          create('button', {
            id: configId + '_saveBtn',
            textContent: '确定',
            title: '部分选项需要刷新页面才能生效',
            className: 'saveclose_buttons',
            onclick: function() {
              config.save();
              config.close();
            },
          }),

          create('button', {
            id: configId + '_closeBtn',
            textContent: '取消',
            title: '取消本次设置,所有选项还原',
            className: 'saveclose_buttons',
            onclick: function() {
              config.close();
            },
          }),

          create('div', {
                className: 'reset_holder block',
              },

              // Reset link
              create('a', {
                id: configId + '_resetLink',
                textContent: '恢复默认设置',
                href: '#',
                title: '恢复所有设置的内容为默认值',
                className: 'reset',
                onclick: function(e) {
                  e.preventDefault();
                  config.reset();
                },
              }),
          )));

      body.appendChild(bodyWrapper); // Paint everything to window at once
      config.center(); // Show and center iframe
      window.addEventListener('resize', config.center, false); // Center frame on resize

      // Call the open() callback function
      config.onOpen(config.frame.contentDocument || config.frame.ownerDocument,
          config.frame.contentWindow || window,
          config.frame);

      if (config.isTabs) {
        config.toTabs();
      }

      // Close frame on window close
      window.addEventListener('beforeunload', function() {
        config.close();
      }, false);

      // Now that everything is loaded, make it visible
      config.frame.style.display = 'block';
      config.isOpen = true;
    }

    // Change this in the onOpen callback using this.frame.setAttribute('style', '')
    var defaultStyle = 'bottom: auto; border: 1px solid #000; display: none; height: 75%;' +
        ' left: 0; margin: 0; max-height: 95%; max-width: 95%; opacity: 0;' +
        ' overflow: auto; padding: 0; position: fixed; right: auto; top: 0;' +
        ' width: 75%; z-index: 999999999;';

    // Either use the element passed to init() or create an iframe
    if (this.frame) {
      this.frame.id = this.id; // Allows for prefixing styles with the config id
      this.frame.setAttribute('style', defaultStyle);
      buildConfigWin(this.frame,
          this.frame.ownerDocument.getElementsByTagName('head')[0]);
    } else {
      // Create frame
      document.body.appendChild((this.frame = this.create('iframe', {
        id: this.id,
        style: defaultStyle,
      })));

      if (this.frameStyle) {
        Object.keys(this.frameStyle).forEach(function(key) {
          config.frame.style[key] = config.frameStyle[key];
        });
      }

      // In WebKit src can't be set until it is added to the page
      this.frame.src = 'about:blank';
      // we wait for the iframe to load before we can modify it
      this.frame.addEventListener('load', function(e) {
        var frame = config.frame;
        var body = frame.contentDocument.getElementsByTagName('body')[0];
        body.id = config.id; // Allows for prefixing styles with the config id
        buildConfigWin(body,
            frame.contentDocument.getElementsByTagName('head')[0]);
      }, false);
    }
  },

  save: function() {
    var forgotten = this.write();
    this.onSave(forgotten); // Call the save() callback function
  },

  close: function() {
    if (!this.frame) return;
    // If frame is an iframe then remove it
    if (this.frame.contentDocument) {
      this.remove(this.frame);
      this.frame = null;
    } else { // else wipe its content
      this.frame.innerHTML = '';
      this.frame.style.display = 'none';
    }

    // Null out all the fields so we don't leak memory
    var fields = this.fields;
    for (var id in fields) {
      var field = fields[id];
      field.wrapper = null;
      field.node = null;
    }

    this.onClose(); //  Call the close() callback function
    this.isOpen = false;
  },

  set: function(name, val) {
    this.fields[name].value = val;

    if (this.fields[name].node) {
      this.fields[name].reload();
    }
  },

  get: function(name, getLive) {
    var field = this.fields[name],
        fieldVal = null;

    if (getLive && field.node) {
      fieldVal = field.toValue();
    }

    return fieldVal != null ? fieldVal : field.value;
  },

  write: function(store, obj) {
    if (!obj) {
      var values = {},
          forgotten = {},
          fields = this.fields;

      for (var id in fields) {
        var field = fields[id];
        var value = field.toValue();

        if (field.save) {
          if (value != null) {
            values[id] = value;
            field.value = value;
          } else
            values[id] = field.value;
        } else
          forgotten[id] = value;
      }
    }
    try {
      this.setValue(store || this.id, this.stringify(obj || values));
    } catch (e) {
      this.log('GM_config failed to save settings!');
    }

    return forgotten;
  },

  read: function(store) {
    try {
      var rval = this.parser(this.getValue(store || this.id, '{}'));
    } catch (e) {
      this.log('GM_config failed to read saved settings!');
      var rval = {};
    }
    return rval;
  },

  reset: function() {
    var fields = this.fields;

    // Reset all the fields
    for (var id in fields) fields[id].reset();

    this.onReset(); // Call the reset() callback function
  },

  create: function() {
    switch (arguments.length) {
      case 1:
        var A = document.createTextNode(arguments[0]);
        break;
      default:
        var A = document.createElement(arguments[0]),
            B = arguments[1];
        for (var b in B) {
          if (b.indexOf('on') == 0)
            A.addEventListener(b.substring(2), B[b], false);
          else if (',style,accesskey,id,name,src,href,which,for'.indexOf(',' +
              b.toLowerCase()) != -1)
            A.setAttribute(b, B[b]);
          else if (typeof B[b] != 'undefined')
            A[b] = B[b];
        }
        if (typeof arguments[2] == 'string')
          A.innerHTML = arguments[2];
        else
          for (var i = 2, len = arguments.length; i < len; ++i)
            A.appendChild(arguments[i]);
    }
    return A;
  },

  center: function() {
    var node = this.frame;
    if (!node) return;
    var style = node.style,
        beforeOpacity = style.opacity;
    if (style.display == 'none') style.opacity = '0';
    style.display = '';
    style.top = Math.floor((window.innerHeight / 2) - (node.offsetHeight / 2)) +
        'px';
    style.left = Math.floor((window.innerWidth / 2) - (node.offsetWidth / 2)) +
        'px';
    style.opacity = '1';
  },

  remove: function(el) {
    if (el && el.parentNode) el.parentNode.removeChild(el);
  },

  toTabs: function() { // 转为 tab 的形式
    var body = this.frame.tagName == 'IFRAME' ?
            this.frame.contentWindow.document :
            this.frame,
        configId = this.id;
    var $ = function(id) {
      return body.getElementById(configId + '_' + id);
    };

    var headers = body.querySelectorAll('.section_header');
    if (!headers.length) return;

    var anch = this.create('div', {
      // id: configId + '_tab_holder',
      className: 'nav-tabs',
    });

    for (var i = 0, header; i < headers.length; i++) {
      header = headers[i];
      if (i == 0) {
        header.classList.add('active');
      }
      anch.appendChild(header);
    }

    anch.addEventListener('click', this.toggleTab.bind(this), false);

    $('section_0').parentNode.insertBefore(anch, $('section_0'));

    var curTab = localStorage.getItem('picviewerCE.config.curTab') || 0;
    this.toggleTab(parseInt(curTab, 10));
  },
  toggleTab: function(e) {
    var body = this.frame.tagName == 'IFRAME' ?
            this.frame.contentWindow.document :
            this.frame,
        configId = this.id;

    var curTab = typeof e == 'number' ? e : /\_(\d+)/.exec(e.target.id)[1];

    [].forEach.call(body.querySelectorAll('.section_header'),
        function(header, i) {
          if (i == curTab) {
            header.classList.add('active');
          } else {
            header.classList.remove('active');
          }
        });

    [].forEach.call(body.querySelectorAll('.section_header_holder'),
        function(holder, i) {
          holder.style.display = (i == curTab) ? 'block' : 'none';
        });

    localStorage.setItem('picviewerCE.config.curTab', curTab);
  },
};

// Define a bunch of API stuff
(function() {
  var isGM = typeof GM_getValue != 'undefined' &&
          typeof GM_getValue('a', 'b') != 'undefined',
      setValue, getValue, stringify, parser;

  // Define value storing and reading API
  if (!isGM) {
    setValue = function(name, value) {
      return localStorage.setItem(name, value);
    };
    getValue = function(name, def) {
      var s = localStorage.getItem(name);
      return s == null ? def : s;
    };

    // We only support JSON parser outside GM
    stringify = JSON.stringify;
    parser = JSON.parse;
  } else {
    setValue = GM_setValue;
    getValue = GM_getValue;
    stringify = typeof JSON == 'undefined' ?
        function(obj) {
          return obj.toSource();
        } : JSON.stringify;
    parser = typeof JSON == 'undefined' ?
        function(jsonData) {
          return (new Function('return ' + jsonData + ';'))();
        } : JSON.parse;
  }

  GM_configStruct.prototype.isGM = isGM;
  GM_configStruct.prototype.setValue = setValue;
  GM_configStruct.prototype.getValue = getValue;
  GM_configStruct.prototype.stringify = stringify;
  GM_configStruct.prototype.parser = parser;
  GM_configStruct.prototype.log = window.console ?
      console.log : (isGM && typeof GM_log != 'undefined' ?
          GM_log : (window.opera ?
                  opera.postError : function() {
                    /* no logging */
                  }
          ));
})();

function GM_configDefaultValue(type, options) {
  var value;

  if (type && type.indexOf('unsigned ') == 0)
    type = type.substring(9);

  switch (type) {
    case 'radio':
    case 'select':
      value = options[0];
      break;
    case 'checkbox':
      value = false;
      break;
    case 'int':
    case 'integer':
    case 'float':
    case 'number':
      value = 0;
      break;
    default:
      value = '';
  }

  return value;
}

function GM_configField(settings, stored, id, customType) {
  // Store the field's settings
  this.settings = settings;
  this.id = id;
  this.node = null;
  this.wrapper = null;
  this.save = typeof settings.save == 'undefined' ? true : settings.save;

  // Buttons are static and don't have a stored value
  if (settings.type == 'button') this.save = false;
  if (settings.type == 'span') this.save = false;

  // if a default value wasn't passed through init() then
  //   if the type is custom use its default value
  //   else use default value for type
  // else use the default value passed through init()
  this['default'] = typeof settings['default'] == 'undefined' ?
      customType ?
          customType['default'] :
          GM_configDefaultValue(settings.type, settings.options) :
      settings['default'];

  // Store the field's value
  this.value = typeof stored == 'undefined' ? this['default'] : stored;

  // Setup methods for a custom type
  if (customType) {
    this.toNode = customType.toNode;
    this.toValue = customType.toValue;
    this.reset = customType.reset;
  }
}

GM_configField.prototype = {
  create: GM_configStruct.prototype.create,

  toNode: function(configId, lastParentNode) {
    var field = this.settings,
        value = this.value,
        options = field.options,
        type = field.type,
        id = this.id,
        labelPos = field.labelPos,
        create = this.create;

    function addLabel(pos, labelEl, parentNode, beforeEl) {
      if (!beforeEl) {
        beforeEl = lastParentNode ?
            parentNode.lastChild :
            parentNode.firstChild; // oneLine 的修正
      }

      switch (pos) {
        case 'right':
        case 'below':
          if (pos == 'below')
            parentNode.appendChild(create('br', {}));
          parentNode.appendChild(labelEl);
          break;
        default:
          if (pos == 'above')
            parentNode.insertBefore(create('br', {}), beforeEl);
          parentNode.insertBefore(labelEl, beforeEl);
      }
    }

    var retNode = create('div', {
          className: 'config_var',
          id: configId + '_' + id + '_var',
          title: field.title || '',
        }),
        firstProp;

    // Retrieve the first prop
    for (var i in field) {
      firstProp = i;
      break;
    }

    var label = field.label && type != 'button' ?
        create('label', {
          id: configId + '_' + id + '_field_label',
          for: configId + '_field_' + id,
          className: 'field_label',
        }, field.label) : null;

    switch (type) {
      case 'span':
        label = null;

        this.node = create('span', {
          innerHTML: field.label,
          className: 'field_label',
          title: field.title,
          style: field.style,
        });
        retNode = this.node;
        break;
      case 'textarea':
        retNode.appendChild((this.node = create('textarea', {
          innerHTML: value,
          id: configId + '_field_' + id,
          className: 'block' + (field.className ? (' ' + field.className) : ''),
          cols: (field.cols ? field.cols : 20),
          rows: (field.rows ? field.rows : 2),
          placeholder: field.placeholder,
        })));
        break;
      case 'radio':
        var wrap = create('div', {
          id: configId + '_field_' + id,
          className: field.className,
        });
        this.node = wrap;

        for (var i = 0, len = options.length; i < len; ++i) {
          var radLabel = create('label', {
            className: 'radio_label',
          }, options[i]);

          var rad = wrap.appendChild(create('input', {
            value: options[i],
            type: 'radio',
            name: id,
            checked: options[i] == value,
          }));

          var radLabelPos = labelPos &&
          (labelPos == 'left' || labelPos == 'right') ?
              labelPos : firstProp == 'options' ? 'left' : 'right';

          addLabel(radLabelPos, radLabel, wrap, rad);
        }

        retNode.appendChild(wrap);
        break;
      case 'select':
        var wrap = create('select', {
          id: configId + '_field_' + id,
        });
        this.node = wrap;

        for (var i = 0, len = options.length; i < len; ++i) {
          var option = options[i];
          wrap.appendChild(create('option', {
            value: option,
            selected: option == value,
          }, option));
        }

        retNode.appendChild(wrap);
        break;
      default: // fields using input elements
        var props = {
          id: configId + '_field_' + id,
          type: type,
          value: type == 'button' ? field.label : value,
        };

        switch (type) {
          case 'checkbox':
            props.checked = value;
            break;
          case 'button':
            props.size = field.size ? field.size : 25;
            if (field.script) field.click = field.script;
            if (field.click) props.onclick = field.click;
            break;
          case 'hidden':
            break;
          default:
            // type = text, int, or float
            props.type = 'text';
            props.size = field.size ? field.size : 25;
        }

        retNode.appendChild((this.node = create('input', props)));
    }

    if (label) {
      // If the label is passed first, insert it before the field
      // else insert it after
      if (!labelPos)
        labelPos = firstProp == 'label' || type == 'radio' ?
            'left' : 'right';

      addLabel(labelPos, label, retNode);
    }

    return retNode;
  },

  toValue: function() {
    var node = this.node,
        field = this.settings,
        type = field.type,
        unsigned = false,
        rval = null;

    if (!node) return rval;

    if (type.indexOf('unsigned ') == 0) {
      type = type.substring(9);
      unsigned = true;
    }

    switch (type) {
      case 'checkbox':
        rval = node.checked;
        break;
      case 'select':
        rval = node[node.selectedIndex].value;
        break;
      case 'radio':
        var radios = node.getElementsByTagName('input');
        for (var i = 0, len = radios.length; i < len; ++i)
          if (radios[i].checked)
            rval = radios[i].value;
        break;
      case 'button':
        break;
      case 'int':
      case 'integer':
      case 'float':
      case 'number':
        var num = Number(node.value);
        var warn = '输入字符 "' + field.label + '" 要求必须为' +
            (unsigned ? ' 正 ' : 'n ') + '整数值';

        if (isNaN(num) || (type.substr(0, 3) == 'int' &&
                Math.ceil(num) != Math.floor(num)) ||
            (unsigned && num < 0)) {
          alert(warn + '.');
          return null;
        }

        if (!this._checkNumberRange(num, warn))
          return null;
        rval = num;
        break;
      default:
        rval = node.value;
        break;
    }

    return rval; // value read successfully
  },

  reset: function() {
    var node = this.node,
        field = this.settings,
        type = field.type;

    if (!node) return;

    switch (type) {
      case 'checkbox':
        node.checked = this['default'];
        break;
      case 'select':
        for (var i = 0, len = node.options.length; i < len; ++i)
          if (node.options[i].value == this['default'])
            node.selectedIndex = i;
        break;
      case 'radio':
        var radios = node.getElementsByTagName('input');
        for (var i = 0, len = radios.length; i < len; ++i)
          if (radios[i].value == this['default'])
            radios[i].checked = true;
        break;
      case 'button':
        break;
      default:
        node.value = this['default'];
        break;
    }
  },

  remove: function(el) {
    GM_configStruct.prototype.remove(el || this.wrapper);
    this.wrapper = null;
    this.node = null;
  },

  reload: function() {
    var wrapper = this.wrapper;
    if (wrapper) {
      var fieldParent = wrapper.parentNode;
      fieldParent.insertBefore((this.wrapper = this.toNode()), wrapper);
      this.remove(wrapper);
    }
  },

  _checkNumberRange: function(num, warn) {
    var field = this.settings;
    if (typeof field.min == 'number' && num < field.min) {
      alert(warn + ' greater than or equal to ' + field.min + '.');
      return null;
    }

    if (typeof field.max == 'number' && num > field.max) {
      alert(warn + ' less than or equal to ' + field.max + '.');
      return null;
    }
    return true;
  },
};

// Create default instance of GM_config
var GM_config = new GM_configStruct();

/*waitForKeyElements*/

/*--- waitForKeyElements(): A utility function, for Greasemonkey scripts,
 that detects and handles AJAXed content.

 Usage example:
 waitForKeyElements ("div.comments", commentCallbackFunction);

 //--- Page-specific function to do what we want when the node is found.
 function commentCallbackFunction (jNode) {
 jNode.text ("This comment changed by waitForKeyElements().");
 }

 IMPORTANT: This function requires your script to have loaded jQuery.
 */

function waitForKeyElements(
    selectorTxt,
    /* Required: The jQuery selector string that
     specifies the desired element(s).
     */
    actionFunction,
    /* Required: The code to run when elements are
     found. It is passed a jNode to the matched
     element.
     */
    bWaitOnce,
    /* Optional: If false, will continue to scan for
     new elements even after the first match is
     found.
     */
    iframeSelector,
    /* Optional: If set, identifies the iframe to
     search.
     */
) {
  var targetNodes, btargetsFound;

  if (typeof iframeSelector == 'undefined')
    targetNodes = $(selectorTxt);
  else
    targetNodes = $(iframeSelector).contents().find(selectorTxt);

  if (targetNodes && targetNodes.length > 0) {
    btargetsFound = true;
    /*--- Found target node(s). Go through each and act if they
     are new.
     */
    targetNodes.each(function() {
      var jThis = $(this);
      var alreadyFound = jThis.data('alreadyFound') || false;

      if (!alreadyFound) {
        //--- Call the payload function.
        var cancelFound = actionFunction(jThis);
        if (cancelFound)
          btargetsFound = false;
        else
          jThis.data('alreadyFound', true);
      }
    });
  } else {
    btargetsFound = false;
  }

  //--- Get the timer-control variable for this selector.
  var controlObj = waitForKeyElements.controlObj || {};
  var controlKey = selectorTxt.replace(/[^\w]/g, '_');
  var timeControl = controlObj[controlKey];

  //--- Now set or clear the timer as appropriate.
  if (btargetsFound && bWaitOnce && timeControl) {
    //--- The only condition where we need to clear the timer.
    clearInterval(timeControl);
    delete controlObj[controlKey];
  } else {
    //--- Set a timer, if needed.
    if (!timeControl) {
      timeControl = setInterval(function() {
            waitForKeyElements(selectorTxt,
                actionFunction,
                bWaitOnce,
                iframeSelector,
            );
          },
          300,
      );
      controlObj[controlKey] = timeControl;
    }
  }
  waitForKeyElements.controlObj = controlObj;
}

(function() {
      'use strict';

      //版本信息
      const TIPS = {
        CurrentVersion: "1",
        LastUpdateDate: "2023.12.12",
        VersionTips: "秒传工具获取",
        UpdateUrl: "https://115211.xyz/forum.php?mod=viewthread&tid=1",
        Sha1FileInputDetails: "",
      };

      const WORKSETTINGS = {
        WorkingItemsNumber: 4, //同时执行任务数
        SleepLittleTime: 500, //短暂休眠,毫秒,暂时在转存中使用
        SleepMoreTime: 1000, //长时休眠,毫秒,暂时在提取中使用
        SleepMuchMoreTime: 8000, //超长休眠,暂时未使用
        ANumber: 27, //随机数,暂时未使用
      };

      GM_addStyle(`
    @keyframes hue {
        from {
            filter: hue-rotate(0);
        }

        to {
            filter: hue-rotate(360deg);
        }
    }

    .rainbow-text {
        display: inline-block;
        color: red;
        animation: hue 6s linear infinite;
        background-image: linear-gradient(to right bottom, rgb(255,0,0), rgb(255,255,0),rgb(255,0,255));
        -webkit-background-clip: text;
    }

        .my115Info{
            color:red
        }
        .btnInGrid{
            height:20px;
            width:20px;
            margin-left:-22px;
            margin-top:36px;
            border:0px;
            border-color:transparent;
            background-color:transparent;
        }

        .btnInGrid i{
            margin:3px -3px
        }

        li:hover .btnInGrid{
            background-color:#2777F8 !important
        }



        /* Style The Dropdown Button */
        .my115Dropbtn {
          background-color: #2777F8;
          color: white;
          font-size: 16px;
          border: none;
          cursor: pointer;
        }

        /* The container <div> - needed to position the dropdown content */
        .my115Dropdown {
          position: relative;
          display: inline-block;
        }

        /* Dropdown Content (Hidden by Default) */
        .my115Dropdown-content {
          display: none;
          position: absolute;
          background-color: #f9f9f9;
          min-width: 230px;
          box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
          z-index: 1;
          margin-top: 32px;
        }

        /* Links inside the dropdown */
        .my115Dropdown-content a {
          color: black;
          padding: 12px 16px;
          text-decoration: none;
          display: block;
          cursor: pointer;
          margin:4px;
        }

        /* Change color of dropdown links on hover */
        .my115Dropdown-content a:hover{
            background-color: #2777F8;
            color:white;
        }

        /* Show the dropdown menu on hover */
        .my115Dropdown:hover .my115Dropdown-content {
          display: block;
        }

        /* Change the background color of the dropdown button when the dropdown content is shown */
        .my115Dropdown:hover .my115Dropbtn {
          background-color: #3e8e41;
        }

    `);

      function getEnviromentInfo() {
        return `
       [gm]:${GM_info.scriptHandler}
       [gmVersion]:${GM_info.version}
       [ua]:${navigator.userAgent}
       [version]:${TIPS.CurrentVersion}
       `;
      }

      function config() {
        console.log('脚本与环境信息:(如果报bug,请附带上如下信息)');
        let env = getEnviromentInfo();
        console.log(env);

        var windowCss = '#Cfg4ne .nav-tabs {margin: 20 2} #Cfg4ne .config_var textarea{width: 310px; height: 50px;} #Cfg4ne .inline {padding-bottom:0px;} #Cfg4ne .config_header a:hover {color:#1e90ff;} #Cfg4ne .config_var {margin-left: 6%;margin-right: 6%;} #Cfg4ne input[type="checkbox"] {margin: 3px 3px 3px 0px;} #Cfg4ne input[type="text"] {width: 60px;} #Cfg4ne {background-color: lightgray;} #Cfg4ne .reset_holder {float: left; position: relative; bottom: -1em;} #Cfg4ne .saveclose_buttons {margin: .7em;} #Cfg4ne .section_desc {font-size: 10pt;}';

        GM_registerMenuCommand('设置', opencfg);

        function opencfg() {
          GM_config.open();
        };

        GM_config.init({
          id: 'Cfg4ne',
          title: GM_config.create('a', {
            href: TIPS.UpdateUrl,
            target: '_blank',
            className: 'setTitle',
            textContent: `${TIPS.VersionTips}设置`,
            title: `作者:Never4Ever 版本:${TIPS.CurrentVersion}点击访问主页`,
          }),
          isTabs: true,
          skin: 'tab',
          css: windowCss,
          frameStyle: {
            height: '490px',
            width: '750px',
            zIndex: '2147483648',
          },
          fields: {
            createRootFolderDefaultValue: {
              section: ['', '转存助手一些功能设置,发包参数暂未开放,敬请期待!'],
              label: '“sha1转存时,强制在保存处新建根目录”这项默认选中',
              labelPos: 'right',
              type: 'checkbox',
              default: true,
            },

            createChildFolderVisible: {
              label: '显示“sha1转存时,不创建任何子目录”选项;不显示则强制创建子目录',
              labelPos: 'right',
              type: 'checkbox',
              default: false,
            },
            createItemSha1: {
              label: '列表模式下:悬浮条显示”获取sha1链接“',
              labelPos: 'right',
              type: 'checkbox',
              default: true,
            },
            createItemSha1InThumb: {
              label: '缩略图模式下:显示”获取sha1链接“',
              labelPos: 'right',
              type: 'checkbox',
              default: true,
            },
            advancedRename: {
              label: '在目录的悬浮工具条处显示“去除分隔符”选项',
              labelPos: 'right',
              type: 'checkbox',
              default: false,
            },
            autoUseSeparator: {
              label: '自动给文件名添加分隔符进行上传,以防文件名违规',
              labelPos: 'right',
              type: 'checkbox',
              default: true,
            },
            autoUseSeparatorToRename: {
              label: '上传结束,自动给文件名去除分隔符,还原原文件名',
              labelPos: 'right',
              type: 'checkbox',
              default: true,
            },
            separator: {
              label: '分隔符方案(推荐生僻字;如果分隔符失效,请自行修改):',
              type: 'text',
              default: '變',
            },
            uploadNumber: {
              //section: ['时间参数设置', '注意:参数设置过快,会引起115服务器无响应,为稳定运行参数未启用!'],
              //label: '转存同时工作任务数:',
              labelPos: 'left',
              type: 'hidden',
              default: '4',
            },
            uploadSleepTime: {
              //label: '转存间隔时间(毫秒):',
              labelPos: 'left',
              type: 'hidden',
              default: '500',
            },
            downloadNumber: {
              //label: '提取同时工作任务数:',
              labelPos: 'left',
              type: 'hidden',
              default: '4',
            },
            downloadSleepTime: {
              //label: '提取间隔时间(毫秒):',
              labelPos: 'left',
              type: 'hidden',
              default: '1300',
            },
            createFolderSleepTime: {
              //label: '目录创建间隔时间(毫秒):',
              labelPos: 'left',
              type: 'hidden',
              default: '300',
            },
            checkUpdate: {
              //section: ['帮助&更新&反馈', '常见错误以及对本脚本进行更新检查与bug反馈'],
              label: '前往github主页',
              labelPos: 'right',
              type: 'button',
              click: function() {
                window.open(TIPS.UpdateUrl, '_blank');
              },
            },

          },

          events: {
            save: function() {
              GM_config.close();
              location.reload();
            },
          },
        });

        GM_registerMenuCommand('脚本与环境信息', jsInfo);

        function jsInfo() {
          postSha1Messgae(createMessage(MessageType.JSINFO, env));
        }
      };
      config();

      var currentConfig = {
        createRootFolderDefaultValue: 'createRootFolderDefaultValue',
        createChildFolderVisible: 'createChildFolderVisible',
        advancedRename: 'advancedRename',
        autoUseSeparator: 'autoUseSeparator',
        autoUseSeparatorToRename: 'autoUseSeparatorToRename',
        separator: 'separator',
        uploadNumber: 'uploadNumber',
        uploadSleepTime: 'uploadSleepTime',
        downloadNumber: 'downloadNumber',
        downloadSleepTime: 'downloadSleepTime',
        createFolderSleepTime: 'createFolderSleepTime',
        createItemSha1: 'createItemSha1',
        createItemSha1InThumb: 'createItemSha1InThumb',

      };

      var offlineTaskButton = `
    <div class="my115Dropdown" id="my115Dropdown">
    <div class="my115Dropbtn">
    <a href="https://115211.xyz/forum.php?mod=viewthread&tid=1" target="_blank" class="button btn-line btn-upload"><i class="icon-operate ifo-linktask"></i><span>获取秒传工具</span><em style="display:none;" class="num-dot"></em></a>
    </div>
  </div>
    `;

      if (!$('#my115Dropdown').length > 0) {

        $('.left-tvf').eq(0).append(offlineTaskButton);
      }

      window.cookie = document.cookie;

      //todo:添加的功能入口
      //列表模式下,项目悬工具条
      waitForKeyElements('div.file-opr', AddShareSHA1Btn);
      //添加任务的弹窗
      waitForKeyElements('div.dialog-bottom', AddDownloadSha1Btn);
      //搜索下的状态
      waitForKeyElements('div.lstc-search', AddShareButtonForSearchItem);
      //缩略图模式下
      waitForKeyElements(`#js_cantain_box .list-thumb li[rel="item"]`,
          AddCeateSha1ButtonInGrid);
      //文件路径旁边的”选中获取项sha1“
      waitForKeyElements('div#js_top_header_file_path_box',
          CreateSha1ButtonForSelectedItems);
      //隐藏截图中的uid
      waitForKeyElements('div[class^="fp-"]', HandleUidDiv);

      function HandleUidDiv(node) {
        node.hide();
        console.log('set uiddiv');
      }

      //#region 20201230新的提取api相关

      function PostData(dict) {
        var k, tmp, v;
        tmp = [];
        for (k in dict) {
          v = dict[k];
          tmp.push(k + '=' + v);
        }
        return tmp.join('&');
      };

      function UrlData(dict) {
        var k, tmp, v;
        tmp = [];
        for (k in dict) {
          v = dict[k];
          tmp.push((encodeURIComponent(k)) + '=' + (encodeURIComponent(v)));
        }
        return tmp.join('&');
      };

      function GetSig(userid, fileid, target, userkey) {
        var sha1, tmp;
        sha1 = new jsSHA('SHA-1', 'TEXT');
        sha1.update('' + userid + fileid + fileid + target + '0');
        tmp = sha1.getHash('HEX');
        sha1 = new jsSHA('SHA-1', 'TEXT');
        sha1.update('' + userkey + tmp + '000000');
        return sha1.getHash('HEX', {
          outputUpper: true,
        });
      }

      function download(filename, content, contentType) {
        if (!contentType) contentType = 'application/octet-stream';
        var a = document.createElement('a');
        var blob = new Blob([content], {
          'type': contentType,
        });
        a.href = window.URL.createObjectURL(blob);
        a.download = filename;
        a.click();
      }

      function RenewCookie() {
        var arryCookie = window.cookie.split(';');
        arryCookie.forEach(function(kv) {
          document.cookie = kv +
              ';expires=Thu, 01 Jan 2100 00:00:00 UTC;;domain=.115.com';
        });
      }

      function DeleteCookie(resp) {
        try {
          var reg = /set-cookie: .+;/g;
          var setcookie = reg.exec(resp)[0].split(';');
          var filecookie = setcookie[0].slice(11) +
              '; expires=Thu, 01 Jan 1970 00:00:00 UTC;' + setcookie[3] +
              ';domain=.115.com';
          document.cookie = filecookie;
          RenewCookie();
          return filecookie;
        } catch (err) {
          return null;
        }
      }

      //#endregion

      function hereDoc(f) {
        return f.toString().
            replace(/^[^\/]+\/\*!?\s?/, '').
            replace(/\*\/[^\/]+$/, '');
      }

      const TaskType = {
        DOWNLOAD: 'Download', //提取
        UPLOAD: 'Upload', //转存
      };

      const MessageType = {
        BEGIN: 0,
        PROCESSING: 1,
        END: 2,
        ERROR: 3,
        CLOSE: 4,
        CANCEL: 5,
        BEGIN4UPLOAD: 6,
        END4UPLOAD: 7,
        NOTIFYINFO: 8,
        BEGIN4CONTINUETASK: 9,
        SHOWCANCEl: 10,
        HIDECANCEL: 11,
        FILEDOWNLOAD: 12,
        MSGERROR: 13,
        JSINFO: 14,
      };

      function createMessage(messageType, msg, id) {
        return {
          messageType: messageType,
          msg: msg,
          targetID: id,
        };
      }

      String.prototype.format = function() {
        if (arguments.length == 0) {
          return this;
        }
        for (var s = this, i = 0; i < arguments.length; i++) {
          s = s.replace(new RegExp('\\{' + i + '\\}', 'g'), arguments[i]);
        }
        return s;
      };

      var getTamplateLines = function() {
        /*
         <div >
         <div class="itemContent" style="color: red;text-align: left;margin: 10px 0;">
         </div>
         <hr />
         <div style="height:140px;overflow-x: hidden;overflow-y: auto;">
         <ul class="errorList"  style="font-size: small;text-align: left;font-style: italic; "></ul>
         </div>
         </div>
         */
      };

      //post from iframe
      function postSha1Messgae(message) {
        var postData = {
          eventID: '115sha1',
          data: message,
        };

        var text = JSON.stringify(postData);
        window.parent.postMessage(text, 'https://115.com/');

      }

      function setTaskCancel() {
        GM_setValue('setTaskCancel', true);
      }

      function resetTaskCancelFlag() {
        GM_setValue('setTaskCancel', false);
      }

      function getTaskCancelFlag() {
        return GM_getValue('setTaskCancel');
      }

      const footerString = `<p class="rainbow-text"><span style="color:#2777F8">[${TIPS.CurrentVersion}]</span>: 操作时,<span class="my115Info">确保本页面置顶</span>,防止脚本休眠!!
    <br><span class="my115Info">无</span>115会员,<span class="my115Info">提取速度</span>受限,<span class="my115Info">转存文件大小</span>不超过5GB!!</p>`;
      //解决提取时的alert不能全屏的问题
      if (window.top === window.self) {
        $(function() {
          var $itemContent = null;
          var $errorList = null;
          var getTamplate = hereDoc(getTamplateLines);

          $(window).on('message', function(e) {
            var dataInfo = typeof e.originalEvent.data == 'string' ?
                JSON.parse(e.originalEvent.data) :
                e.originalEvent.data;
            if (dataInfo.eventID != '115sha1' || e.originalEvent.origin !=
                'https://115.com') return;
            var message = typeof dataInfo.data == 'string' ?
                JSON.parse(dataInfo.data) :
                dataInfo.data;

            //ui:
            if (message.messageType == MessageType.BEGIN) {
              Swal.fire({
                title: '正在操作中...',
                html: getTamplate,
                allowOutsideClick: false,
                allowEscapeKey: false,
                confirmButtonText: `完成`,
                showCancelButton: true,
                cancelButtonText: `取消操作`,
                footer: footerString,
                willOpen: function() {
                  Swal.getCancelButton().style.display = 'none';
                  Swal.showLoading(Swal.getConfirmButton());
                  var $swalContent1 = $(Swal.getHtmlContainer());
                  $errorList = $swalContent1.find('.errorList');
                  $itemContent = $swalContent1.find('.itemContent');
                },
              }).then((result) => {
                if (result.dismiss === Swal.DismissReason.cancel) {
                  setTaskCancel();
                  console.log('Download Cancel Task');
                  Swal.fire({
                    title: '已取消,等待进行中的任务结束...',
                    html: getTamplate,
                    allowOutsideClick: false,
                    allowEscapeKey: false,
                    confirmButtonText: `完成`,
                    footer: footerString,
                    willOpen: function() {
                      Swal.showLoading(Swal.getConfirmButton());
                      var $swalContent1 = $(Swal.getHtmlContainer());
                      let html = $errorList.eq[0];
                      $errorList = $swalContent1.find('.errorList');
                      $errorList.append(html);
                      $itemContent = $swalContent1.find('.itemContent');
                    },
                  });
                }
              });

            } else if (message.messageType == MessageType.PROCESSING) {
              $itemContent.html(message.msg);
            } else if (message.messageType == MessageType.ERROR) {
              $errorList.append('<li><div display: flex;"><p>' + message.msg +
                  '</p><p style="font-style: italic;"><\p><\div><\li><li><hr/></li>');
            } else if (message.messageType == MessageType.END) {
              $itemContent.html(message.msg);
              Swal.getTitle().textContent = '操作完成!';
              Swal.getCancelButton().style.display = 'none';
              Swal.getFooter().style.display = 'none';
              Swal.hideLoading();

            } else if (message.messageType == MessageType.CLOSE) {
              Swal.close();
            } else if (message.messageType == MessageType.BEGIN4UPLOAD) {
              Swal.fire({
                title: '正在操作中...',
                html: getTamplate,
                allowOutsideClick: false,
                allowEscapeKey: false,
                confirmButtonText: `完成`,
                denyButtonText: `打开目录`,
                showCancelButton: true,
                cancelButtonText: '取消操作',
                footer: footerString,
                willOpen: function() {
                  Swal.getCancelButton().style.display = 'none';
                  Swal.getDenyButton().style.display = 'none';
                  Swal.showLoading(Swal.getConfirmButton());
                  var $swalContent1 = $(Swal.getHtmlContainer());
                  $errorList = $swalContent1.find('.errorList');
                  $itemContent = $swalContent1.find('.itemContent');
                },
              }).then(result => {
                if (result.dismiss === Swal.DismissReason.cancel) {
                  setTaskCancel();
                  console.log('Upload Cancel Task');
                  console.log(window.parent.document.myData);
                  Swal.fire({
                    title: '已取消,等待进行中的任务完成...',
                    html: getTamplate,
                    allowOutsideClick: false,
                    allowEscapeKey: false,
                    confirmButtonText: `完成`,
                    denyButtonText: `打开目录`,
                    showCancelButton: false,
                    cancelButtonText: '取消操作',
                    willOpen: function() {

                      Swal.getDenyButton().style.display = 'none';
                      Swal.showLoading(Swal.getConfirmButton());
                      var $swalContent1 = $(Swal.getHtmlContainer());
                      $errorList = $swalContent1.find('.errorList');
                      $itemContent = $swalContent1.find('.itemContent');
                    },
                  });

                }
              });
            } else if (message.messageType == MessageType.END4UPLOAD) {
              $itemContent.html(message.msg);
              Swal.getTitle().textContent = '操作完成!';
              Swal.getCancelButton().style.display = 'none';
              Swal.getDenyButton().style.display = 'block';
              Swal.getDenyButton().addEventListener('click', e => {
                console.log('DenyButton click');
                console.log(message);
                window.location.href = 'https://115.com/?cid=' +
                    message.targetID +
                    '&offset=0&tab=&mode=wangpan';
              });
              Swal.getFooter().style.display = 'none';
              Swal.hideLoading();
            } else if (message.messageType == MessageType.BEGIN4CONTINUETASK) {
              let taskFile = '';
              Swal.fire({
                title: '导入任务文件,继续任务',
                html: `<div style="text-align: left;">
                        选择任务文件(.7task):<input id="continuedTaskFile" type="file" accept=".7task" ></input>
                        <div style="font-size:14px;color:red;margin:10px;text-align: left;">*在没有移动相关的文件以及文件夹,包括目标的所有目录层级,导入任务可继续</div>
                      </div>`,
                focusConfirm: false,
                confirmButtonText: `开始继续任务`,
              }).then(t => {
                if (t.isConfirmed && taskFile) {
                  ContinuedTask(taskFile);
                }
              });

              document.getElementById('continuedTaskFile').
                  addEventListener('change', e => {
                    taskFile = e.target.files[0];
                  });

            } else if (message.messageType == MessageType.SHOWCANCEl) {
              if (Swal.getCancelButton()) {
                //Swal.getCancelButton().style.display = "block";
              }
            } else if (message.messageType == MessageType.HIDECANCEL) {
              if (Swal.getCancelButton()) {
                Swal.getCancelButton().style.display = 'none';
              }

            } else if (message.messageType == MessageType.FILEDOWNLOAD) {
              console.log(message.msg);
              let size = parseInt(message.msg.onlineFile.size);
              if (size > 2 * 1024 * 1024) {
                postSha1Messgae(createMessage(MessageType.MSGERROR,
                    '暂不支持大于2MB的 text|json 文件!'));
              } else {
                Swal.fire({
                  title: '正在下载文件...',
                  html: `<p>${message.msg.onlineFile.name}</p><p id="processInSwal"></p>`,
                  allowOutsideClick: false,
                  allowEscapeKey: false,
                  confirmButtonText: `完成`,
                  showCancelButton: false,
                  willOpen: function() {
                    Swal.showLoading(Swal.getConfirmButton());
                    downloadAFile(message.msg.onlineFile, text => {
                      $(Swal.getHtmlContainer()).
                          find('#processInSwal').
                          html(text);
                    }).then(r => {
                      if (r.state) {
                        console.log(r.text);
                        message.msg.config.text = r.text;

                        UploadFilesBySha1Links(message.msg.config);
                      }
                    });

                  },
                });
              }

            } else if (message.messageType == MessageType.MSGERROR) {
              Swal.fire({
                icon: 'error',
                text: message.msg,
              });
            } else if (message.messageType == MessageType.JSINFO) {
              Swal.fire({
                icon: 'info',
                text: '脚本与环境信息:(如果报bug,请附带上如下信息)\r\n' + message.msg,
              });
            }

          });
        });
      }

      function delay(ms) {

        if (ms == 0) {
          ms = 1000 * (Math.floor(Math.random() * (11 - 4)) + 4);
        }
        return new Promise(resolve => setTimeout(resolve, ms));
      }

      //#region 115 api
      //get   UploadInfo
      //return {state:false,user_id:0,userkey:'0',error:''}
      async function getUploadInfo() {
        const r = await $.ajax({
          url: 'https://proapi.115.com/app/uploadinfo',
          dataType: 'json',
          xhrFields: {
            withCredentials: true,
          },
        });
        return r;
      }

      //add a folder
      //return {state: false, error: "该目录名称已存在。", errno: 20004, errtype: "war"}
      //return {state: true, error: "", errno: "", aid: 1, cid: "2020455078010511975", …}
      async function addFolder(pid, folderName) {
        const postData = PostData({
          pid: pid,
          cname: encodeURIComponent(folderName),
        });

        const r = await $.ajax({
          type: 'POST',
          url: 'https://webapi.115.com/files/add',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            //'Origin': 'https://115.com'
          },
          xhrFields: {
            withCredentials: true,
          },
          dataType: 'json',
          data: postData,
        });

        return r;
      }

      //return {data: Array(30), count: 53, data_source: "DB", sys_count: 0, offset: 0, page_size:115, …}
      //return Array type:
      //      [folder]:{cid: "", aid: "1", pid: "", n: "", m: 0, …}
      //      [file]:  {fid: "", uid: 1447812, aid: 1, cid: "", n: "",pc:"",sha:"",s:0,t:"" …}
      async function getDirectChildItemsByOffset(cid, offset) {
        var tUrl = 'https://webapi.115.com/files?aid=1&cid=' + cid +
            '&o=file_name&asc=1&offset=' + offset +
            '&show_dir=1&limit=1150&code=&scid=&snap=0&natsort=1&record_open_time=1&source=&format=json&fc_mix=&type=&star=&is_share=&suffix=&custom_order=';
        // var tUrl = "https://aps.115.com/natsort/files.php?aid=1&cid=" + cid + "&o=file_name&asc=1&offset=" + offset + "&show_dir=1&limit=1150&code=&scid=&snap=0&natsort=1&record_open_time=1&source=&format=json&fc_mix=0&type=&star=&is_share=&suffix=&custom_order=";
        const result = await $.ajax({
          type: 'GET',
          url: tUrl,
          dataType: 'json',
          xhrFields: {
            withCredentials: true,
          },
        });
        return result;
      }

      //直接子项目少于1200
      async function getDirectChildItemsByOffsetlt1200(cid, offset) {
        //var tUrl = 'https://webapi.115.com/files?aid=1&cid='+cid+'&o=file_name&asc=1&offset='+offset+'&show_dir=1&limit=1150&code=&scid=&snap=0&natsort=1&record_open_time=1&source=&format=json&fc_mix=&type=&star=&is_share=&suffix=&custom_order=';
        var tUrl = 'https://aps.115.com/natsort/files.php?aid=1&cid=' + cid +
            '&o=file_name&asc=1&offset=' + offset +
            '&show_dir=1&limit=1150&code=&scid=&snap=0&natsort=1&record_open_time=1&source=&format=json&fc_mix=0&type=&star=&is_share=&suffix=&custom_order=';
        const result = await $.ajax({
          type: 'GET',
          url: tUrl,
          dataType: 'json',
          xhrFields: {
            withCredentials: true,
          },
        });
        return result;
      }

      //return AllDirect items :{id:"",parentID:cid,isFolder:false,name:"",size:0,pc:"",sha:"",paths[] };
      async function getAllDirectItems(cid, folderProcessCallback) {
        var items = new Array();
        var index = 0;
        var flag = true;
        var pageIndex = 1;
        var first = true;
        var isLT1200 = false;

        while (flag) {
          if (getTaskCancelFlag()) break;

          folderProcessCallback(pageIndex);
          var result = null;
          //1200数量,不同的api;这么写减少发包
          if (first) {
            result = await getDirectChildItemsByOffset(cid, index);
            console.log(`first >1200 :${result.state},${result.count}`);
            if (!result.state) {
              result = await getDirectChildItemsByOffsetlt1200(cid, index);
              console.log(`first <1200 :${result.state},${result.count}`);
              isLT1200 = true;
            }
            first = false;
          } else {
            if (isLT1200) result = await getDirectChildItemsByOffsetlt1200(cid,
                index);
            else result = await getDirectChildItemsByOffset(cid, index);
          }

          var totalCount = parseInt(result.count);
          if (totalCount >= 1) {
            result.data.forEach(function(item) {
              var pItem = {
                id: '',
                parentID: cid,
                isFolder: false,
                name: '',
                size: '',
                pickCode: '',
                sha1: '',
                paths: new Array(),
                preid: '',
                needToRemoved: false,
              };

              if (item.fid) //文件 fid,cid
              {
                pItem.isFolder = false;
                pItem.id = item.fid;
                pItem.name = item.n;
                pItem.pickCode = item.pc;
                pItem.sha1 = item.sha;
                pItem.size = item.s;
              } else //目录 cid,pid
              {
                pItem.isFolder = true;
                pItem.id = item.cid;
                pItem.name = item.n;
                pItem.pickCode = item.pc;
              }

              var itemIndex = items.findIndex(
                  q => q.name == pItem.name && q.pickCode == pItem.pickCode &&
                      q.sha1 == pItem.sha1 &&
                      (_.isEqual(q.paths, pItem.paths)));
              if (itemIndex == -1) items.push(pItem);
              else {
                //可能存在同一个目录下,两个文件一模一样,
                //相同文件处理:不然循环条件退不出
                //fix:pickcode不一样,先保存着吧
                pItem.needToRemoved = true;
                items.push(pItem);
              }
            });
          }

          console.log('_______________totalCount ' + totalCount);
          console.log(items.length);
          //当获取到比pagesize小时,获取结束,1200时有个坑。。。
          if (totalCount <= items.length) {
            break;
          } else {
            await delay(500);
            index = items.length;
            pageIndex = pageIndex + 1;
          }
        }

        console.log('cid: {0}, count: {1}'.format(cid, items.length));

        var noNullItems = items.filter(q => !q.needToRemoved);
        console.log(
            'cid: {0}, 除去完全重复count: {1}'.format(cid, noNullItems.length));

        return noNullItems;
      }

      //return {file_name:"",pick_code:"",sha1:"",count:"",size:"",folder_count:"",paths:[]}
      //return paths:[]层级目录
      async function getFolderInfo(cid) {
        var pUrl = 'https://webapi.115.com/category/get?aid=1&cid=' + cid;
        const result = await $.ajax({
          type: 'GET',
          url: pUrl,
          dataType: 'json',
          xhrFields: {
            withCredentials: true,
          },
        });
        console.log(result);
        var pItem = {
          fileCount: parseInt(result.count),
          folderCount: parseInt(result.folder_count),
          id: cid,
          parentID: '',
          isFolder: true,
          name: result.file_name,
          size: result.size,
          pickCode: result.pick_code,
          sha1: '',
          paths: result.paths,
          preid: '',
        };

        return pItem;
      }

      // get fileArray:{id:"",parentID:cid,isFolder:false,name:"",size:0,pc:"",sha:"",paths[] };
      async function getAllFiles(
          cid, fileArray, topCid, folderProcessCallback) {
        var thisFolder = await getFolderInfo(cid);
        folderProcessCallback(thisFolder.name, 0);
        //空目录,跳过遍历

        if (getTaskCancelFlag()) return;
        if (thisFolder.fileCount == 0) return;
        folderProcessCallback(thisFolder.name);
        var directItems = await getAllDirectItems(thisFolder.id, pageIndex => {
          folderProcessCallback(thisFolder.name, pageIndex);
        });
        //空目录,跳过遍历
        if (directItems.length == 0) return;
        var files = directItems.filter(t => !t.isFolder);
        files.forEach(f => {
          var index = thisFolder.paths.findIndex(
              q => q.file_id.toString() == topCid);
          var paths = new Array();
          if (index != -1) {
            paths = thisFolder.paths.slice(index).map(q => q.file_name);
          }
          paths.push(thisFolder.name);
          f.paths = paths.slice(1);

          fileArray.push(f);
        });

        var folders = directItems.filter(t => t.isFolder);
        for (var folder of folders) {
          if (getTaskCancelFlag()) break;
          await getAllFiles(folder.id, fileArray, topCid,
              folderProcessCallback);
          await delay(200);
        }

      }

      //批量重命名 fileArray  [{id:id,name:ddd}]
      //{"state":true,"error":"","errno":0,"data":{"2187365717527997108":"14214.mp4"}}
      async function renameFiles(fileArray) {
        console.log('renameFiles fileArray');
        console.log(fileArray);
        let datas = fileArray.map((value, index, array) => {
          let dataKey = `files_new_name[${value.id}]`;
          let dataValue = value.name;
          return `${encodeURIComponent(dataKey)}=${encodeURIComponent(
              dataValue)}`;
        }).join('&');

        let renameUrl = 'https://webapi.115.com/files/batch_rename';
        const result = await $.ajax({
          type: 'POST',
          url: renameUrl,
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            //'Origin': 'https://115.com'
          },
          dataType: 'json',
          xhrFields: {
            withCredentials: true,
          },
          data: datas,
        });

        return result;
      }

      async function getUploadHistory(offset, limit) {
        let historyFiles = [];
        let pUrl = `https://webapi.115.com/history/list?type=4&offset=${offset}&limit=${limit}`;
        const result = await $.ajax({
          type: 'GET',
          url: pUrl,
          dataType: 'json',
          xhrFields: {
            withCredentials: true,
          },
        });

        let files = result.data.list;
        for (const aFile of files) {
          historyFiles.push({
            sha1: aFile.sha1,
            id: aFile.file_id,
            createTime: aFile.create_time,
            pickCode: aFile.pick_code, //可能不一样
            name: aFile.file_name,
            parentID: aFile.parent_id,
          });
        }

        return historyFiles;
      }

      async function getLastUploadFiles(
          count, delayTime = 200, processCallback) {
        let files = [];
        let offset = 0;
        let limit = 115;
        let leftCount = count;
        while (leftCount >= limit) {
          processCallback(`正在获取第${offset + 1}-${offset + limit + 1}个文件数据...`);
          let result = await getUploadHistory(offset, limit);
          result.forEach(f => files.push(f));
          offset = offset + limit;
          leftCount = leftCount - limit;
          await delay(delayTime);
        }
        if (leftCount > 0) {
          processCallback(
              `正在获取第${offset + 1}-${offset + leftCount + 1}个文件数据...`);
          let result = await getUploadHistory(offset, leftCount);
          result.forEach(f => files.push(f));
        }

        return files;
      }

      //获取生成sha1需要preid
      //return: {state:,error:,fileItem:}
      function getFileItemPreid(fileItem) {
        console.log('getFileItemPreid');
        console.log(fileItem);
        const f = fileItem;
        let fileSize = parseInt(fileItem.size);
        if (fileSize == 0) {
          return new Promise((resolve, reject) => {
            const errorMsg = '{0} 文件大小为0,已经跳过!'.format(f.filename);
            console.error('errorMsg');
            resolve({
              state: false,
              error: '文件大小为0,已经跳过!',
              fileItem: fileItem,
            });
          });
        }

        const r = new Promise((resolve, reject) => {
              var data, key, tm, tmus;
              tmus = (new Date()).getTime();
              tm = Math.floor(tmus / 1000);
              var test = JSON.stringify({
                pickcode: fileItem.pickCode,
              });
              console.log(
                  '111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111');
              console.log(fileItem.pickCode, test, tm);
              ({data, key} = m115_encode(test, tm));
              console.log("开始")
              //开始做种
              GM_xmlhttpRequest({
                method: "POST",
                url: 'https://proapi.115.com/app/chrome/downurl?t=' + tm,
                headers: {
                  'Content-Type': 'application/x-www-form-urlencoded',
                  'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'
                },
                responseType: 'json',
                data: PostData({
                  data: encodeURIComponent(data)
                }),
                onload: function(r) {
                  if (r.status == 200) {
                    var download_info = r.response;

                    if (download_info.state && download_info.data) {
                      console.log(download_info.data, key)
                      var json = m115_decode(download_info.data, key);
                      console.log(
                          '666666666666666666666666666666666666666666666666666666666666666666666')
                      var url = JSON.parse(json)[fileItem.id]['url']['url'];
                      //todo:不能下载的文件处理
                      if (!url.startsWith("http://cdnfhnfdfs.115.com") &&
                          url.startsWith('http:///')) {
                        console.error(`error url:${url}`);
                        url = url.replace("http:///",
                            "http://cdnfhnfdfs.115.com/")
                      }

                      //获取文件下载链接
                      console.log(url);
                      var base64 = new Base64()

                      console.log(base64.encode(url))

                      GM_xmlhttpRequest({
                        method: 'POST',
                        url: 'https://api.115211.xyz/api/seed',
                        headers: {
                          'Content-Type': 'application/x-www-form-urlencoded',
                          'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
                        },
                        responseType: 'json',
                        data: PostData({
                          name: fileItem.name,
                          size: fileItem.size,
                          sha1: fileItem.sha1,
                          url: base64.encode(url)
                        }),
                        onload: function(res) {
                          console.log(res)
                          let resInfo = res.response;
                          if (resInfo.status == 2) {
                            //秒传成功
                            //   var pre_buff = response.response;
                            //   var data = new Uint8Array(pre_buff);
                            // var sha1 = new jsSHA('SHA-1', 'ARRAYBUFFER');
                            // sha1.update(data.slice(0, 128 * 1024));
                            // var preid = sha1.getHash('HEX', {
                            //   outputUpper: true
                            // });
                            fileItem.preid = "0EEE99A4ED0A1844DA8B6FEF7306067F6156E4B5";
                            resolve({
                              state: true,
                              error: "",
                              fileItem: fileItem
                            });
                          }else{

                          }
                        }
                      })
                    }
                  }
                }
              })







              // GM_xmlhttpRequest({
              //   method: "POST",
              //   url: 'https://proapi.115.com/app/chrome/downurl?t='+tm,
              //   headers: {
              //     'Content-Type': 'application/x-www-form-urlencoded',
              //     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6'
              //      'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
              //   },
              //   responseType: 'json',
              //   data: PostData({
              //     data: encodeURIComponent(data)
              //   }),
              //   onload: function (r) {
              //     if (r.status == 200) {
              //       var download_info = r.response;
              //
              //
              //       if (download_info.state && download_info.data) {
              //         try {
              //           console.log(download_info.data,key)
              //           var json = m115_decode(download_info.data,key);
              //           console.log(json)
              //           console.log(JSON.parse(json))
              //           console.log('666666666666666666666666666666666666666666666666666666666666666666666')
              //           var url = JSON.parse(json)[fileItem.id]['url']['url'];
              //           //todo:不能下载的文件处理
              //           if (!url.startsWith("http://cdnfhnfdfs.115.com") && url.startsWith('http:///')) {
              //             console.error(`error url:${url}`);
              //             url = url.replace("http:///", "http://cdnfhnfdfs.115.com/")
              //           }
              //
              //           //获取文件下载链接
              //           console.log(url);
              //           var resp = r.responseHeaders
              //           var setCookie = DeleteCookie(resp)
              //           var fileCookie = null;
              //           if (setCookie) {
              //             fileCookie = setCookie;
              //           }
              //
              //           GM_xmlhttpRequest({
              //             method: "GET",
              //             url: url,
              //             timeout: 12000,
              //             headers: {
              //               "Range": "bytes=0-131072",
              //               "Cookie": fileCookie,
              //               'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6'
              //              'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
              //             },
              //             responseType: 'arraybuffer',
              //             onload: function (response) {
              //               if (response.status === 206) {
              //                 var pre_buff = response.response;
              //                 var data = new Uint8Array(pre_buff);
              //                 var sha1 = new jsSHA('SHA-1', 'ARRAYBUFFER');
              //                 sha1.update(data.slice(0, 128 * 1024));
              //                 var preid = sha1.getHash('HEX', {
              //                   outputUpper: true
              //                 });
              //                 fileItem.preid = preid;
              //                 resolve({
              //                   state: true,
              //                   error: "",
              //                   fileItem: fileItem
              //                 });
              //               } else if (response.status === 403) {
              //                 console.error("Forbidden, 已经用40个0代替");
              //                 fileItem.preid = "0000000000000000000000000000000000000000";
              //                 resolve({
              //                   state: true,
              //                   error: "",
              //                   fileItem: fileItem
              //                 });
              //               } else {
              //                 //fix v3.3:  修复404文件无法下载导致卡ui问题  @指环王
              //                 console.error("可能文件无法下载或者网络问题");
              //                 console.log(response);
              //                 resolve({
              //                   state: false,
              //                   error: "下载出错,可能文件无法下载或者网络问题",
              //                   fileItem: fileItem
              //                 });
              //               }
              //             },
              //             ontimeout: function (res) {
              //               console.error("下载超时,可能文件无法下载或者网络问题");
              //               console.log(res);
              //               resolve({
              //                 state: false,
              //                 error: "下载超时,可能文件无法下载或者网络问题",
              //                 fileItem: fileItem
              //               });
              //             }
              //           });
              //         } catch (error) {
              //           console.error(error);
              //           resolve({
              //             state: false,
              //             error: "在提取中发生错误...",
              //             fileItem: fileItem
              //           });
              //         }
              //       } else {
              //         console.log(download_info);
              //         resolve({
              //           state: false,
              //           error: download_info.msg,
              //           fileItem: fileItem
              //         });
              //       }
              //
              //     } else {
              //       console.error(response.response);
              //       resolve({
              //         state: false,
              //         error: "在提取中发生错误...",
              //         fileItem: fileItem
              //       });
              //     }
              //   }
              // });
            })
        ;
        return r;
      }

      var utf8ArrayToStr = (function() {
        var charCache = new Array(128); // Preallocate the cache for the common single byte chars
        var charFromCodePt = String.fromCodePoint || String.fromCharCode;
        var result = [];

        return function(array) {
          var codePt, byte1;
          var buffLen = array.length;

          result.length = 0;

          for (var i = 0; i < buffLen;) {
            byte1 = array[i++];

            if (byte1 <= 0x7F) {
              codePt = byte1;
            } else if (byte1 <= 0xDF) {
              codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F);
            } else if (byte1 <= 0xEF) {
              codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) |
                  (array[i++] & 0x3F);
            } else if (String.fromCodePoint) {
              codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) |
                  ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
            } else {
              codePt = 63; // Cannot convert four byte code points, so use "?" instead
              i += 3;
            }

            result.push(charCache[codePt] ||
                (charCache[codePt] = charFromCodePt(codePt)));
          }

          return result.join('');
        };
      })();

      function downloadAFile(fileItem, progressCallback = function(text) {
      }) {
        console.log('downloadAFile');
        console.log(fileItem);
        console.log(
            '999999999999999999999999999999999999999999999999999999999999999999');
        const r = new Promise((resolve, reject) => {
          var data, key, tm, tmus;
          tmus = (new Date()).getTime();
          tm = Math.floor(tmus / 1000);
          var test = JSON.stringify({
            pickcode: fileItem.pickCode,
          });
          console.log(fileItem.pickCode, test, tm);
          ({data, key} = m115_encode(test, tm));
          GM_xmlhttpRequest({
            method: 'POST',
            url: 'https://proapi.115.com/app/chrome/downurl?t=' + tm,
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded',
              // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6',
              'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
            },
            responseType: 'json',
            data: PostData({
              data: encodeURIComponent(data),
            }),
            onload: function(r) {
              if (r.status == 200) {
                var download_info = r.response;
                if (download_info.state && download_info.data) {
                  try {
                    var json = m115_decode(download_info.data, key);
                    //console.log(json)
                    var url = JSON.parse(json)[fileItem.id]['url']['url'];
                    //todo:不能下载的文件处理
                    if (!url.startsWith('http://cdnfhnfdfs.115.com') &&
                        url.startsWith('http:///')) {
                      console.error(`error url:${url}`);
                      url = url.replace('http:///',
                          'http://cdnfhnfdfs.115.com/');
                    }

                    console.log(url);
                    var resp = r.responseHeaders;
                    var setCookie = DeleteCookie(resp);
                    var fileCookie = null;
                    if (setCookie) {
                      fileCookie = setCookie;
                    }

                    GM_xmlhttpRequest({
                      method: 'GET',
                      url: url,
                      headers: {
                        'Content-Type': 'application/octet-stream',
                        'Cookie': fileCookie,
                        // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6',
                        'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
                      },
                      responseType: 'arraybuffer',
                      onload: function(response) {
                        if (response.status === 200) {
                          let str = utf8ArrayToStr(
                              new Uint8Array(response.response));
                          resolve({
                            state: true,
                            error: '',
                            text: str,
                          });
                        } else {

                          console.error(response);
                          resolve({
                            state: false,
                            error: `response.status:${response.status}`,
                            text: '',
                          });

                        }

                      },
                      onprogress: function(event) {
                        let textMsg = `${event.loaded} of ${event.total} bytes, ${(event.loaded /
                            event.total).toFixed(4) * 100}%`;
                        console.log(textMsg);
                        progressCallback(textMsg);
                      },

                    });
                  } catch (error) {
                    console.error(error);
                    resolve({
                      state: false,
                      error: `${error}`,
                      text: '',
                    });

                  }
                } else {
                  console.log(download_info);
                  resolve({
                    state: false,
                    error: download_info.msg,
                    text: '',
                  });
                }

              } else {
                console.log('下载第一阶段');
                console.error(response.response);
                resolve({
                  state: false,
                  error: `在下载中发生错误...${response.status}`,
                  text: '',
                });
              }
            },
          });
        });
        return r;
      }

      //windows平台上限制的字符:/\|":*?<> 其他平台比windows宽泛一些
      function repalceValidatedName(name) {
        return name.replace(/</g, '[').
            replace(/>/g, ']').
            replace(/\|/g, '_').
            replace(/:/g, '_').
            replace(/\//g, '_').
            replace(/\\/g, '_').
            replace(/\*/g, '_').
            replace(/"/g, '\'').
            replace(/\?/g, '_');
      }

      //格式化sha1 链接
      //return type: {state:succeed,msg:""}
      // false:msg->出错信息
      //true: msg->sha1链接
      function convertToSha1Link(fileItem, isSimpleFormat) {
        var succeed = false;
        var msg = '格式生成失败!';
        if (fileItem.name && fileItem.size && fileItem.sha1 && fileItem.preid) {
          var serverId = 50660199;
          var paths = '';
          //生成最终的115SHA1链接
          // var sha1Link = "115://" + repalceValidatedName(fileItem.name) + "|" + fileItem.size + "|" + fileItem.sha1 + "|" + fileItem.preid;
          var sha1Link = '211://' + repalceValidatedName(fileItem.name) + '|' +
              fileItem.size + '|' + fileItem.sha1 + '|' + serverId;
          if (!isSimpleFormat) {
            if (fileItem.paths.length > 0) {
              //console.log(fileItem.paths);
              //fix: v3.3 目录中的‘|’或者‘#’替换为‘/’,防止脚本导出再导入时破坏目录结构
              //fix: v3.4 windows平台上不能限制的字符:/\|":*?<>  替换;去掉了老版本的#分隔符
              paths = fileItem.paths.map(t => repalceValidatedName(t)).
                  join('/');
              // msg = sha1Link + '|' + paths;
            }
          }
          msg = sha1Link + '|' + paths + '|' + fileItem.pickCode;
          ;
          succeed = true;
        }

        return {
          state: succeed,
          msg: msg,
        };
      }

      // 从sha1link 转换为 FileItem
      //return type:{state:succeed,fileItem:{}}
      //true: fileItem, false:null
      function convertFromSha1Link(sha1Link) {
        var succeed = false;
        var item = {};
        if (sha1Link) {
          if (sha1Link.startsWith('115://')) {
            sha1Link = sha1Link.substring(6);
          }
          //v3.4 add 转存时,文件名、文件夹名替换非法字符
          var infos = sha1Link.split('|');
          if (infos.length >= 4) {
            item.id = '';
            item.pickCode = '';
            item.name = repalceValidatedName(infos[0]);
            item.size = infos[1];
            item.sha1 = infos[2];
            item.preid = infos[3];
            item.parentID = '';
            item.paths = new Array();
            if (infos.length > 4) {
              //fix: v3.4 移除了此兼容,因为开放了#作为目录名
              // if (infos.length == 5 && infos[4].includes('#')) {
              //兼容 #字符分割

              //    item.paths = infos[4].split('#');
              //} else {
              item.paths = infos.slice(4).map(t => repalceValidatedName(t));
              //}
            }
            item.extension = '';
            item.formatedName = '';
            succeed = true;
          }
        }

        return {
          state: succeed,
          fileItem: item,
        };
      }

      function createUploadFile(urlData, postData) {
        return new Promise((resolve, reject) => {
          GM_xmlhttpRequest({
            method: 'POST',
            url: 'http://uplb.115.com/3.0/initupload.php?' + urlData,
            data: postData,
            responseType: 'json',
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
              // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/25.0.0.3',
              'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
              //'Origin': 'https://115.com'
            },
            onload: function(response) {
              let data = {
                state: false,
                error: '',
                pickCode: '',
              };
              if (response.status === 200 && response.response.status === 2) {
                data.state = true;
                data.pickCode = response.response.pickcode;
              } else {
                console.error(response);
                let error = '或许sha1链接不匹配(?)';
                if (response.status === 405) {
                  error = '频繁请求,被115限制 ([!]立即停止,尝试停止操作半小时或者重新登录):' +
                      response.statusText;
                } else if (response.response &&
                    response.response.message) error = response.response.message;
                else if (response.response &&
                    response.response.statusmsg) error = '可能参数不正确(?):' +
                    response.response.statusmsg;
                data.error = error;
              }
              resolve(data);
            },
          });

        });
      }

      //return:{state:false,error:"",fileItem:};
      function uploadFile(targetFolder, fileItem, uploadInfo) {

        let fCid = `U_1_${targetFolder}`;
        let appVersion = '25.0.0.3';
        const secret = 'Qclm8MGWUv59TnrR0XPg';
        console.log(fCid);
        let time = (new Date()).getTime();
        let ts = Math.floor(time / 1000);
        console.log(fileItem);
        console.log(uploadInfo);

        console.log();
        console.log(
            `${secret}${fileItem.sha1}${fileItem.size}${fileItem.preid}${uploadInfo.user_id}${ts}${md5(
                uploadInfo.user_id)}${appVersion}`);
        let uploadToken = md5(
            `${secret}${fileItem.sha1}${fileItem.size}${fileItem.preid}${uploadInfo.user_id}${ts}${md5(
                uploadInfo.user_id)}${appVersion}`);

        let urlData = UrlData({
          isp: 0,
          appid: 0,
          topupload: 0,
          rt: 0,
          appversion: appVersion,
          format: 'json',
          sig: GetSig(uploadInfo.user_id, fileItem.sha1, fCid,
              uploadInfo.userkey),
          t: ts,
          token: uploadToken,
        });

        let postData = PostData({
          preid: fileItem.preid,
          fileid: fileItem.sha1,
          quickid: fileItem.sha1,
          app_ver: appVersion,
          filename: encodeURIComponent(fileItem.formatedName),
          filesize: fileItem.size,
          exif: '',
          target: fCid,
          userid: uploadInfo.user_id,

        });
        console.log(urlData);
        console.log(postData);

//         const r = createUploadFile(urlData, postData);

//         const x = r.then(t => {
//             return new Promise((resole, reject) => {
//                 fileItem.state = t.state;
//                 fileItem.pickCode = t.pickCode;
//                 resole({
//                     state: t.state,
//                     error: t.error,
//                     fileItem: fileItem
//                 });
//             })
//         });

//         return x;
      }

      //设置列表视图
      function setListView() {
        GM_xmlhttpRequest({
          method: 'POST',
          url: 'https://115.com/?ct=user_setting&ac=set',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          data: PostData({
            setting: '{"view_file":"list"}',
          }),
          responseType: 'json',
          onload: function(response) {
            if (response.status === 200) {
            }
          },
        });
      }

      //#endregion

      //更新父ID
      async function updateParentID(
          cid, cname, thisLevel, maxLevel, items, sleepTime,
          createFolderCallback) {
        if (thisLevel == maxLevel) return;
        let files = new Array();
        if (thisLevel == 0) {
          files = items;
        } else {
          files = items.filter(f => f.paths[thisLevel - 1] == cname);
        }

        let childFiles = files.filter(q => q.paths.length == thisLevel);
        let childFolderNames = files.map(q => q.paths[thisLevel]).
            filter(q => q).
            filter((x, i, a) => a.indexOf(x) == i);

        console.log(`childFiles :${childFiles.length}`);
        //upload file:
        for (let file of childFiles) {
          file.parentID = cid;
          //console.log(file.parentID);
        }

        //create folder:
        for (let folderName of childFolderNames) {

          let r = await createRootFolder(cid, folderName, 10, sleepTime,
              callbackMsg => {
                createFolderCallback && createFolderCallback({
                  state: true,
                  folderName: folderName,
                  error: callbackMsg,
                });
              });

          //fix: v3.4 有同名文件夹,处理跟根目录相同处理。如果超过重试次数的逻辑未处理
          //let r = await addFolder(cid, folderName);
          console.log(r);

          if (r.state) {
            await updateParentID(r.cid, folderName, thisLevel + 1, maxLevel,
                files, createFolderCallback);
          } else { //ui 目录创建失败
            //todo: !!!尚未解决这个
            console.error(`updateParentID  如果出现这个,说明重复名字的文件夹也太太太多了`);
          }

          await delay(sleepTime);
        }

      }

      function internelFormat(folder, files, folderParents) {
        var paths = folderParents.slice(0);
        paths.push(folder.dir_name);

        for (var file of folder.files) {

          var link = file + '|' + paths.slice(1).join('|');
          files.push(link);
        }

        for (var childFolder of folder.dirs) {

          internelFormat(childFolder, files, paths);
        }
      }

      //格式化为普通json
      //{state:true,error:"",text:""}
      function formatJsonToCommon(text) {

        try {
          var root = JSON.parse(text);
          console.log(root);
          var files = new Array();
          var paths = new Array();
          internelFormat(root, files, paths);

          return {
            state: true,
            error: '',
            text: files.join('\r\n'),
            rootFolder: root.dir_name,
          };
        } catch (error) {
          return {
            state: false,
            error: error,
            text: '',
          };
        }

      }

      //解析Sha1链接到文件数组
      //解析inline text sha1 links,并根据配置设置分隔符;返回FileArray
      function parseSha1LinksToFileArray(text, nameSeparator, errorCallback) {
        let textLines = text.split(/\r?\n/);

        let files = new Array();
        for (let line of textLines) {
          let fLine = line.trim();
          if (!fLine) continue;
          let r = convertFromSha1Link(fLine);
          if (r.state) {
            let nameStrings = r.fileItem.name.split('.');
            let extension = nameStrings.pop();
            r.fileItem.extension = extension;
            //根据配置重新设置文件名
            if (nameSeparator) {
              //使用emoutils.js库来分割,带有emoji的文件名
              let fileName = emojiUtils.toArray(nameStrings.join('.')).
                  map(c => c + nameSeparator).
                  join('').
                  slice(0, -1);
              r.fileItem.formatedName = fileName + '.' + extension;
            } else {
              r.fileItem.formatedName = r.fileItem.name;
            }
            files.push(r.fileItem);
          } else {
            errorCallback && errorCallback(`${fLine} 格式错误?`);
          }

        }

        return files;
      }

      //fix: v3.4 时间日期中含有"/",":"导致目录或者文件下载失败
      function getCurrentTimeString() {
        let time = new Date();
        let timeString = `${time.toLocaleString()} (${time.getMilliseconds()})`;
        return timeString.replace(/\//g, '.').replace(/:/g, '.');
      }

      //在targetCid下创建目录,成功则返回新目录cid,否则返回原cid;返回’-1‘,target已经被移除或者删除
      async function createRootFolder(
          targetCid, folderName, retryTimes, sleepTime, processCallback) {
        let cid = targetCid;
        let newFolderName = folderName;

        if (folderName == '') {

          newFolderName = `auto_create@${getCurrentTimeString()}`;
        }

        for (let i = 0; i < retryTimes; i++) {

          if (i != 0) {

            newFolderName = `${folderName == '' ?
                'auto_create' :
                folderName}@${getCurrentTimeString()}`;
          }

          processCallback && processCallback(`正在自动创建目录${newFolderName}...`);
          let tr = await addFolder(targetCid, newFolderName);
          if (tr.state) {
            cid = tr.cid;
            processCallback && processCallback(`自动创建目录${newFolderName}成功!`);
            break;
          } else {

            processCallback && processCallback(
                `自动创建目录${newFolderName}失败!原因:${tr.error},将自动尝试新的名字...`);
            if (tr.error.includes('云端目录不存在') ||
                tr.error.includes('文件不存在或已删除')) {
              cid = '-1'; //父目录不存在时的提示
              break;
            }
            await delay(sleepTime);
          }
        }

        let state = cid != '-1';
        let error = state ? '' : '云端目录之前已经删除,请重新选择保存位置!';
        //todo:父目录不存在时的提示
        return {
          cid: cid,
          folderName: newFolderName,
          state: state,
          error: error,
        };
      }

      //上传超时文件
      function uploadFileWithTimeOut(timeOut, fileParentID, file, uploadInfo) {
        console.log('uploadFileWithTimeOut');
        let to = delay(timeOut).then(t => {
          return {
            state: false,
            error: `等待上传结果超时,此乃警告!成功与否,看最后统计结果!`,
            fileItem: file,
          };
        });
        let up = uploadFile(fileParentID, file, uploadInfo);

        return Promise.race([to, up]);
      }

      //上传过程
      async function processUpload(
          allFiles, workingNumber, sleepTime, resultCallback) {
        let fileArray = allFiles.filter(q => !q.state);
        let index = 1;
        let fileLength = allFiles.length;
        let completed = fileLength - fileArray.length;
        let promisArray = new Array();
        let uploadInfo = await getUploadInfo();
        let msg;
        for (let file of fileArray) {
          if (getTaskCancelFlag()) {
            console.log('转存取消');
            //postSha1Messgae(createMessage(MessageType.PROCESSING, "已取消,正在等待进行中的任务结束..."));
            break;
          }
          console.log(file);

          //let r = uploadFile(file.parentID, file, uploadInfo).then(t => {
          let r = uploadFileWithTimeOut(8000, file.parentID, file, uploadInfo).
              then(t => {
                completed = completed + 1;
                if (t.state) {
                  msg = `<div align="right"><b>${completed}</b> | <b>${fileLength}</b></div><hr>【 <b>${t.fileItem.name}</b> 】上传成功.`;
                } else {
                  let uploadError = `【 <b>${t.fileItem.name}</b> 】: ${t.error}`;
                  resultCallback && resultCallback({
                    state: false,
                    msg: uploadError,
                  });
                  msg = `<div align="right"><b>${completed}</b> | <b>${fileLength}</b></div><hr>${uploadError}`;
                }
                resultCallback && resultCallback({
                  state: true,
                  msg: msg,
                });
              });

          promisArray.push(r);

          if (index % workingNumber == 0) {
            await delay(sleepTime);
          }

          if (index % 128 == 0) {
            await Promise.all(promisArray);
            let seconds = 3;
            for (let i = 0; i < seconds; i++) {
              resultCallback && resultCallback({
                state: true,
                msg: `防止115服务器限制,暂停发包。<br><br>${seconds - i}秒后继续....`,
              });
              await delay(1000);
            }
            promisArray = new Array();
          }
          index = index + 1;
        }

        await delay(500);
        await Promise.all(promisArray);

        return fileArray;
      }

      // v3.3 转存时获取文件从历史上传中获取,如果文件夹众多,此方案速度优势明显,但不允许多页面操作
      async function processRenameByUsingHistory(
          files, separator, sleepTime, resultCallback) {

        let history = await getLastUploadFiles(files.length, sleepTime, t => {
          resultCallback({
            state: true,
            msg: t,
          });
        });

        resultCallback({
          state: true,
          msg: '正在等待重命名...如果文件较多,请等待;或者停止,等以后手动操作去除分隔符。',
        });
        //fix: v3.3.1 修复含有重复文件的时候,未所有完成重命名的bug
        history.forEach(q => q.isMarked = false);
        for (const file of files) {
          //console.log("file")
          //console.log(file)
          let thisFile = history.find(
              q => q.sha1 == file.sha1 && q.parentID == file.parentID &&
                  q.name ==
                  file.formatedName && !q.isMarked);
          //console.log("thisFile")
          //console.log(thisFile)
          if (thisFile) {
            file.id = thisFile.id;
            thisFile.isMarked = true;
          } else {
            console.error(`历史记录里未找到 ${file.name}`);
          }
        }

        let selectedFiles = files.filter(
            f => f.formatedName.search(separator) != -1 && f.id).map(f => {
          let fo = {
            id: f.id,
            name: f.formatedName.split(separator).join(''),
          };
          return fo;
        });
        console.log(selectedFiles);
        let i, j, temporary, chunk = 115;
        for (i = 0, j = selectedFiles.length; i < j; i += chunk) {
          temporary = selectedFiles.slice(i, i + chunk);
          resultCallback && resultCallback({
            state: true,
            msg: `正在重命名第${i + 1}到${i + temporary.length}个文件...`,
          });
          let renameResult = await renameFiles(temporary);
          if (renameResult.state === true) {
            resultCallback && resultCallback({
              state: true,
              msg: `重命名第${i + 1}到${i + temporary.length}个文件成功!`,
            });
          } else {
            resultCallback && resultCallback({
              state: false,
              msg: renameResult.error,
            });
            resultCallback && resultCallback({
              state: true,
              msg: `重命名第${i + 1}到${i + 1 + temporary.length}个文件中有失败!!!`,
            });
          }
          await delay(sleepTime);
        }

      }

      //过程重命名
      async function processRename(
          targetFolderCid, separator, sleepTime, resultCallback) {
        let onlineFiles = new Array();
        await getAllFiles(targetFolderCid, onlineFiles, targetFolderCid,
            (fname, pIndex) => {
              if (pIndex > 1) {
                resultCallback && resultCallback({
                  state: true,
                  msg: `正在获取 【${fname}】 下第 ${pIndex} 页的内容...`,
                });
              } else {
                resultCallback && resultCallback({
                  state: true,
                  msg: `正在获取 【${fname}】 下的内容...`,
                });
              }
            });

        let selectedFiles = onlineFiles.filter(
            f => f.name.search(separator) != -1).map(f => {
          let fo = {
            id: f.id,
            name: f.name.split(separator).join(''),
          };
          return fo;
        });

        let i, j, temporary, chunk = 115;
        for (i = 0, j = selectedFiles.length; i < j; i += chunk) {
          temporary = selectedFiles.slice(i, i + chunk);
          resultCallback && resultCallback({
            state: true,
            msg: `正在重命名第${i + 1}到${i + temporary.length}个文件...`,
          });
          let renameResult = await renameFiles(temporary);
          if (renameResult.state === true) {
            resultCallback && resultCallback({
              state: true,
              msg: `重命名第${i + 1}到${i + temporary.length}个文件成功!`,
            });
          } else {
            resultCallback && resultCallback({
              state: false,
              msg: renameResult.error,
            });
            resultCallback && resultCallback({
              state: true,
              msg: `重命名第${i + 1}到${i + 1 + temporary.length}个文件中有失败!!!`,
            });
          }
          await delay(sleepTime);
        }

      }

      //通过sha1链接转存文件
      //uploadSetting:{targetCid,text,rootFolder:{needToCreate:true,folderName:""},itemNameSeparator:{needToSeparate:true,separator:""}}
      async function UploadFilesBySha1Links(
          config, continuedTaskSetting = null) {

        let uploadConfig = continuedTaskSetting == null ?
            config :
            continuedTaskSetting.uploadConfig;
        let folderSleepTime = uploadConfig.folderSetting.sleepTime;
        let nameSeparator = '';
        let newTargetCid = '-1';
        let files;
        let fileName = '';

        if (continuedTaskSetting == null) {
          //fix: v3.4 在线获取内容可能有空格,修复解析出错
          let formatedText = uploadConfig.text.trim();
          if (!formatedText) return;
          postSha1Messgae(
              createMessage(MessageType.BEGIN4UPLOAD, '正在解析sha1链接...'));

          //解析json,转为inline text;并且从json中获取root folder name
          if (formatedText.startsWith('{') && formatedText.endsWith('}')) {
            let r = formatJsonToCommon(formatedText);
            if (r.state) {
              uploadConfig.folderSetting.rootFolder.folderName = r.rootFolder;
              formatedText = r.text;
            } else {
              console.error('json 解析失败');
              postSha1Messgae(
                  createMessage(MessageType.END4UPLOAD, 'json解析失败!是不是格式不匹配!'));
              return;
              //json 解析失败,提示,,
            }
          }

          //解析inline text sha1 links,并根据配置设置分隔符

          if (uploadConfig.itemNameSeparator.needToSeparate &&
              uploadConfig.itemNameSeparator.separator) {
            nameSeparator = uploadConfig.itemNameSeparator.separator;
          }

          files = parseSha1LinksToFileArray(formatedText, nameSeparator,
              errorMsg => {
                postSha1Messgae(createMessage(MessageType.ERROR, errorMsg));
              });

          if (files.length == 0) {
            postSha1Messgae(createMessage(MessageType.END, `未获取到有效的链接!`));
            return;
          }

          postSha1Messgae(
              createMessage(MessageType.PROCESSING, `获取到链接个数:${files.length}`));
          await delay(500);

          //根目录设置
          //根据配置重新设置targetCid
          newTargetCid = uploadConfig.targetCid;

          if (uploadConfig.folderSetting.rootFolder.needToCreate === true) {
            let rootFolderName = uploadConfig.folderSetting.rootFolder.folderName;
            let root = await createRootFolder(newTargetCid, rootFolderName, 11,
                folderSleepTime * 2, msg => {
                  postSha1Messgae(createMessage(MessageType.PROCESSING, msg));
                });

            fileName = root.folderName;
            newTargetCid = root.cid;
            await delay(500);
          }
          console.log(`newTargetCid: ${newTargetCid}`);

          if (newTargetCid == '-1') {
            console.log('选择的保存处文件夹已经被删除或者移动');
            postSha1Messgae(createMessage(MessageType.END,
                '自动创建根目录出错:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   <br/>选择的保存处文件夹,已经被删除或者移动,请重新选择保存位置!'));
            return;
          }

          //子目录设置
          files.forEach(f => {
            f.parentID = newTargetCid;
          });

          if (uploadConfig.folderSetting.notCreateAnyChildFolder === false) //可以创建目录
          {
            console.log('需要创建子目录');
            //根据配置设置每个文件的parent id
            //最大的层次
            let maxLevel = Math.max.apply(Math, files.map(e => e.length));
            let level = 0;
            //cid更新
            postSha1Messgae(
                createMessage(MessageType.PROCESSING, `正在配置子目录的生成...`));
            await updateParentID(newTargetCid, '',
                level, maxLevel, files, folderSleepTime * 1.5, t => {
                  let st = t.state ? '成功.' : '失败!!! ' + t.error;
                  let msg = `创建子目录 <b>${t.folderName}</b> ${st}`;
                  postSha1Messgae(createMessage(MessageType.PROCESSING, msg));
                  if (!t.state) postSha1Messgae(
                      createMessage(MessageType.ERROR, msg));
                });

          }
        } else {
          newTargetCid = continuedTaskSetting.targetCid;
          files = continuedTaskSetting.data;
          fileName = continuedTaskSetting.fileName;
        }

        window.parent.document.myData = files;
        postSha1Messgae(createMessage(MessageType.SHOWCANCEl));
        console.log(files.length);
        //文件上传
        await processUpload(files, uploadConfig.upload.workingNumber,
            uploadConfig.upload.sleepTime, result => {
              if (result.state === true) {
                postSha1Messgae(
                    createMessage(MessageType.PROCESSING, result.msg));
              } else {
                postSha1Messgae(createMessage(MessageType.ERROR, result.msg));
              }
            });
        let isTaskCanceled = getTaskCancelFlag();

        postSha1Messgae(createMessage(MessageType.HIDECANCEL));

        //根据配置,重命名文件
        if (uploadConfig.itemNameSeparator.needToSeparate &&
            uploadConfig.itemNameSeparator.needToRemoveSeparator &&
            uploadConfig.itemNameSeparator.separator &&
            !isTaskCanceled) {
          postSha1Messgae(
              createMessage(MessageType.PROCESSING, '开始获取文件,并自动重命名...'));
          await delay(folderSleepTime);
          console.log(files);
          await processRenameByUsingHistory(files,
              uploadConfig.itemNameSeparator.separator, folderSleepTime,
              result => {
                if (result.state === true) {
                  postSha1Messgae(
                      createMessage(MessageType.PROCESSING, result.msg));
                } else {
                  postSha1Messgae(createMessage(MessageType.ERROR, result.msg));
                }
              });
          // await processRename(newTargetCid, uploadConfig.itemNameSeparator.separator, folderSleepTime, result => {
          //     if (result.state === true) {
          //         postSha1Messgae(createMessage(MessageType.PROCESSING, result.msg));
          //     } else {
          //         postSha1Messgae(createMessage(MessageType.ERROR, result.msg));
          //     }
          // });

          postSha1Messgae(
              createMessage(MessageType.PROCESSING, '文件批量去除分隔符(重命名)完成!'));
          await delay(folderSleepTime * 2);
        }

        var fails = files.filter(q => !q.state);
        var failText = fails.map(function(p) {
          var r = convertToSha1Link(p, false);
          return r.msg;
        }).join('\r\n');

        if (failText) GM_setClipboard(failText);
        if (isTaskCanceled) {
          //todo:取消转存
          let file_name = fileName + '_转存_未完成.7task';
          let data = files.map(f => {
            `let tempFile={
                            parentID:f.parentID,
                            name:f.name,
                            size:f.size,
                            sha1:f.sha1,
                            preid:f.preid,
                        };`;
            let tempFile = [
              f.parentID,
              f.name,
              f.formatedName,
              f.size,
              f.sha1,
              f.preid,
              f.state,
            ];
            return tempFile;
          });
          uploadConfig.text = '';
          let taskJson = {
            taskType: TaskType.UPLOAD,
            targetCid: newTargetCid,
            fileName: fileName,
            data: data,
            uploadConfig: uploadConfig,
          };
          let text = JSON.stringify(taskJson);
          download(file_name, text);

        }

        let msg = `完成上传!成功 <b>${(files.length -
            fails.length)}</b> ,失败或者取消 <b>${fails.length}</b>\
                <br><br>如果有失败,已将失败sha1链接复制到剪贴板!如果转存失败,请检查sha1链接格式或者在 chrome 上尝试转存。\
                获取最新版,或者遇到问题去此反馈,感谢 !点击-> <a href="${TIPS.UpdateUrl}" target="_blank">${TIPS.VersionTips}</a>`;
        postSha1Messgae(
            createMessage(MessageType.END4UPLOAD, msg, newTargetCid));

      }

      //获取文件项Preid与超时
      function getFileItemPreidWithTimeOut(timeOut, file) {
        console.log('getFileItemPreidWithTimeOut');
        let to = delay(timeOut).then(t => {
          return {
            state: false,
            error: `等待提取结果超时,此乃警告,成功与否看最后结果!`,
            fileItem: file,
          };
        });
        let up = getFileItemPreid(file);
        // console.log('文件的Preid',up)
        return Promise.race([to, up]);
      }

      function GetFileItemByliNode(liNode) {

        var pItem = {
          id: '',
          parentID: '',
          isFolder: false,
          name: '',
          size: 0,
          pickCode: '',
          sha1: '',
          paths: [],
          preid: '',
          selected: false,

        };

        var type = liNode.getAttribute('file_type');
        pItem.name = liNode.getAttribute('title');
        pItem.parentID = liNode.getAttribute('p_id');

        var isSelected = liNode.getAttribute('class');
        if (isSelected == 'selected') pItem.selected = true;

        if (type == '0') {
          pItem.id = liNode.getAttribute('cate_id');
          pItem.isFolder = true;
        } else {
          pItem.size = liNode.getAttribute('file_size');
          pItem.sha1 = liNode.getAttribute('sha1');
          pItem.pickCode = liNode.getAttribute('pick_code');
          pItem.id = liNode.getAttribute('file_id');
        }

        return pItem;
      }

      const FILESIZE = 128 * 1024;

      //内部创建Sha1链接
      async function InnerCreateSha1Links(allFiles, txtName) {
        var msg = '';
        var index = 1;
        var completedIndex = 1;
        var promisArray = new Array();

        for (let file of allFiles) {
          let fileSize = parseInt(file.size);
          file.size = fileSize;
          if (!file.preid && file.size <= FILESIZE) {
            file.preid = file.sha1;
          }
        }

        let files = allFiles.filter(f => !f.preid);
        completedIndex = allFiles.length - files.length;
        var gt1200files = files.length >= 1200;
        console.log(`>=1200: ${gt1200files}`);
        //postSha1Messgae(createMessage(MessageType.PROCESSING, `总计${allFiles.length},已完成${completedIndex}`));
        postSha1Messgae(createMessage(MessageType.SHOWCANCEl));
        for (var file of files) {

          let taskCancelFlag = getTaskCancelFlag();
          console.log(taskCancelFlag);
          if (taskCancelFlag === true) {
            console.log('创建SHA1链接任务已取消');
            break;
          }

          const f = file;
          var time = Date.parse(new Date()) / 1000;
          //请求做种

          // Seed(file,time)

          // 获取文件Preid
          const r = getFileItemPreidWithTimeOut(20000, f).then((t) => {
            console.log(t)
            if (t.state) {
              msg = '<div align="right"><b>{0}</b> | <b>{1}</b></div><hr>获取【 <b>{2}</b> 】的sha1链接成功'.format(
                  completedIndex, allFiles.length, t.fileItem.name);
              postSha1Messgae(createMessage(MessageType.PROCESSING, msg));
            } else {
              msg = '<div align="right"><b>{0}</b> | <b>{1}</b></div><hr>获取【 <b>{2}</b> 】的sha1链接:{3}'.format(
                  completedIndex, allFiles.length, t.fileItem.name, t.error);
              postSha1Messgae(createMessage(MessageType.PROCESSING, msg));
              var filePath = t.fileItem.paths.join(' > ');
              console.log(filePath);
              if (filePath) msg = '{0},原因:{1},路径:{2}'.format(t.fileItem.name,
                  t.error, filePath);
              else msg = '{0},原因:{1}'.format(t.fileItem.name, t.error);

              postSha1Messgae(createMessage(MessageType.ERROR, msg));
            }
            completedIndex = completedIndex + 1;
          });

          promisArray.push(r);

          //自己改代码吧,怎么弄提取逻辑。。太慢,耗时长;太快,115容易没反应
          if (index % WORKSETTINGS.WorkingItemsNumber == 0) {
            await delay(WORKSETTINGS.SleepMoreTime * 1.5);
            if (index % (WORKSETTINGS.WorkingItemsNumber * 9) == 0) {
              await Promise.all(promisArray);
              let seconds = 2;
              for (let i = 0; i < seconds; i++) {
                postSha1Messgae(createMessage(MessageType.PROCESSING,
                    `防止115服务器限制,暂停发包中。<br><br>${seconds - i}秒后继续...`));
                await delay(1000);
              }
              promisArray = new Array();
            }
          }

          //
          index = index + 1;
        }

        await Promise.all(promisArray);

        var succeedArray = allFiles.filter(q => q.preid);
        if (succeedArray.length == 1) {
          var result = convertToSha1Link(succeedArray[0], false);
          postSha1Messgae(createMessage(MessageType.CLOSE, ''));

          setTimeout(s => {
            prompt('复制分享链接到剪贴板', s);
          }, 100, result.msg);

        } else {

          if (succeedArray.length > 1) {

            let file_name = txtName + '_sha1.txt';
            let text = '';

            if (getTaskCancelFlag()) {
              file_name = txtName + '_提取_未完成.7task';
              let data = allFiles.map(f => {
                `let tempFile={
                            id:f.id,
                            parentID:f.parentID,
                            name:f.name,
                            size:f.size,
                            paths:f.paths,
                            pickCode:f.pickCode,
                            sha1:f.sha1,
                            preid:f.preid,
                        };`;
                let tempFile = [
                  f.id,
                  f.parentID,
                  f.name,
                  f.size,
                  f.paths,
                  f.pickCode,
                  f.sha1,
                  f.preid,
                ];
                return tempFile;
              });
              let taskJson = {
                taskType: TaskType.DOWNLOAD,
                fileName: txtName,
                data: data,
              };
              text = JSON.stringify(taskJson);
              //todo:取消任务

            } else {
              text = allFiles.filter(q => q.preid).map(function(p) {
                var r = convertToSha1Link(p, false);
                return r.msg;
              }).join('\r\n');
            }
            download(file_name, text);
          }

          msg = `
            完成【 <b>${txtName}</b> 】提取!<hr><br>
            总共<b>${allFiles.length}</b> ,取消或者失败 <b>${allFiles.length -
          succeedArray.length}</b>。<br>
            取消后,若未移动文件夹,可导入继续提取。<br>
            点击-> <a href="${TIPS.UpdateUrl}" target="_blank">${TIPS.VersionTips}</a>,获取最新版与反馈!
            `;
          console.log(msg);
          postSha1Messgae(createMessage(MessageType.END, msg));
        }
      }
      async function CreateSha1LinksAll(items, taskName) {
        //ui: 获取文件中...
        var msg = '正在获取文件...';
        postSha1Messgae(createMessage(MessageType.BEGIN, msg));
        var files = new Array();
        for (let item of items) {
          if (getTaskCancelFlag()) break;

          if (!item.isFolder) {
            files.push(item);
          } else {
            msg = `正在获取 ${item.name} 下的内容...`;
            postSha1Messgae(createMessage(MessageType.PROCESSING, msg));
            let children = new Array();
            await getAllFiles(item.id, children, item.id, (fname, pIndex) => {
              if (pIndex > 1) {
                msg = `正在获取 【${fname}】 下第 ${pIndex} 页的内容...`;
              } else {
                msg = `正在获取 【${fname}】 下的内容...`;
              }
              postSha1Messgae(createMessage(MessageType.PROCESSING, msg));
            });

            for (let f of children) {
              f.paths.unshift(item.name);
              files.push(f);
            }

          }
        }

        if (!files || files.length == 0) {
          postSha1Messgae(createMessage(MessageType.END, `未选中任何内容???`));
          return;
        }

        postSha1Messgae(createMessage(MessageType.PROCESSING,
            `获取到 【<b>${taskName}</b>】 的内容 ${files.length} 项`));
        await delay(100);
        if (getTaskCancelFlag()) {
          postSha1Messgae(createMessage(MessageType.END, '已经取消任务!'));
        } else InnerCreateSha1Links(files, taskName);
      }

      //创建SHA1链接
      async function CreateSha1Links(item) {
        //ui: 获取文件中...
        var msg = '正在获取文件...';
        postSha1Messgae(createMessage(MessageType.BEGIN, msg));
        var files = new Array();

        if (!item.isFolder) {
          files.push(item);
        } else {
          msg = `正在获取 ${item.name} 下的内容...`;
          postSha1Messgae(createMessage(MessageType.PROCESSING, msg));

          await getAllFiles(item.id, files, item.id, (fname, pIndex) => {
            if (pIndex > 1) {
              msg = '正在获取 【{0}】 下第 {1} 页的内容...'.format(fname, pIndex);
            } else {
              msg = '正在获取 【{0}】 下的内容...'.format(fname);
            }
            postSha1Messgae(createMessage(MessageType.PROCESSING, msg));
          });

          if (!files || files.length == 0) {
            postSha1Messgae(createMessage(MessageType.END,
                '【<b>{0}</b> 】空目录???'.format(item.name)));
            return;
          }
        }

        postSha1Messgae(createMessage(MessageType.PROCESSING,
            '获取到 【<b>{0}</b>】 的内容 {1} 项'.format(item.name, files.length)));
        await delay(100);
        if (getTaskCancelFlag()) {
          postSha1Messgae(createMessage(MessageType.END, '已经取消任务!'));
        } else InnerCreateSha1Links(files, item.name);
      }

      const autoCreateRootFolderTips = {
        msg: `sha1转存时,强制在保存处新建根目录`,
        details: `选择时:&#013;&#010;1.新建根目录名来自sha1转存文件名或者json中的根元素。\
        &#013;&#010;2.如果没有,则当前时间为文件名生成。`,
      };

      const autoCreateRootFolderString =
          `<div class="linktask-quota" style="height: 40px;display: block">\
        <a>${autoCreateRootFolderTips.msg}</a>\
        <div class="help" title=" ${autoCreateRootFolderTips.details}"><a></a></div>\
        <span>&nbsp;&nbsp;</span><div class="option-switch" style="top:10px;left:10px">\
        <input type="checkbox" checked="true" id="neAutoCreateRootfolder" onclick="function f() {return false}">\
        <label for><i>开启</i><s>关闭</s><b>切换</b></label></div>`;

      const notCreateAnyChildFolderTips = {
        msg: `sha1转存时,不创建任何子目录`,
        details: `选中时,不会自动创建任何子目录。此项与根目录不会影响!`,
      };

      const notCreateAnyChildFolderString =
          `<div id="neNotCreateAnyChildFolderParent" class="linktask-quota" style="height: 40px;display: block">\
        <a>${notCreateAnyChildFolderTips.msg}</a>\
        <div class="help" title=" ${notCreateAnyChildFolderTips.details}"><a></a></div>\
        <span>&nbsp;&nbsp;</span><div class="option-switch" style="top:10px;left:10px">\
        <input type="checkbox" checked="true" id="neNotCreateAnyChildFolder" onclick="function f() {return false}">\
        <label for><i>开启</i><s>关闭</s><b>切换</b></label></div>`;

      const selectFileTips = {
        msg: `或者导入sha1链接文件(txt/json)`,
        details: `如果不能正确显示选择文件按钮,可能是与其他脚本或者插件冲突!!`,
      };
      const selectFileString = `<div id="neFile">
    <div id="neFileOnline">
    <span style="display:flex;margin-top: 10px;">已经选择在线文件:<p style="color:red" id="neOnlineFileName"></p></span>
    </div>
    <div id="neFileUpload" >
    <div class="linktask-quota" style="margin-top: 10px;">\
        <a>${selectFileTips.msg}</a>\
        <div class="help" title="${selectFileTips.details}"><a></a></div>\
        <span>&nbsp;&nbsp;</span><input type="file" id="neSelectFile" accept=".txt,.json" style="display:block;color:#2777F8;visibility: visible;"></input></div>
    </div>
    </div>`;

      const otherSettingString = `<div class="linktask-quota" style="margin-top: 10px;display:none;">\
        分隔符或者其他选项:<a id="neSetting1" href="javascript:;" style="color:#2777F8">点此设置</a>。\
        </div>`;

      const headerString = `<div id="ne115tipsforheader">${TIPS.VersionTips}(${TIPS.LastUpdateDate}),\
    <a style="color:red;" target="_blank" href=${TIPS.UpdateUrl}>更新&反馈点此!</a>\
    <a href="javascript:;" style="color:#2777F8" id="neSetting2">分隔符等设置点此!</a></div>`;

      const beginUploadBySha1String = `<div class="con" id="downsha1"><a class="button" target="_blank" href="https://115211.xyz/forum.php?mod=viewthread&tid=1">开始sha1转存</a></div>`;

      //当前页面所在的目录信息
      function getCurrentFolderDisplayed() {
        let defaultFolder = {
          id: '0',
          name: '根目录',
        };
        let iframes = document.querySelectorAll('iframe');
        for (let item of iframes) {
          let filePath = item.contentWindow.document.body.querySelector(
              '[rel=header_page_local]');
          if (filePath) {
            let folders = filePath.querySelectorAll('.folder');
            let lastFolder = folders[folders.length - 1];
            defaultFolder.name = lastFolder.getAttribute('titletext');
            let search = new URLSearchParams(window.location.search);
            defaultFolder.id = search.get('cid');
            break;
          }
        }

        return defaultFolder;
      }

      //fix: v3.3 修复“添加任务弹窗可能无法关闭”
      function AddDownloadSha1Btn(jNode) {

        document.querySelector(`a[btn="close"]`).
            addEventListener('click', e => {
              window.parent.document.tryUploadItem = null;
            });

        let onlineFile = null;

        var file = '';

        var dialog = document.getElementsByClassName(
            'dialog-box dialog-mini offline-box window-current')[0];
        dialog.style.width = '720px';
        dialog.style.top = '10px';
        if (document.getElementById('ne115tipsforheader') == null) {
          $(headerString).appendTo('.dialog-header[rel$=\'title_box\']');

          $('#neSetting2')[0].addEventListener('click', e => {
            document.querySelector(`a[btn="close"]`).click();
            GM_config.open();
          });
        }

        var textArea = document.querySelector('#js_offline_new_add');
        if (textArea) {
          textArea.style.height = '100px';
        }

        if (document.getElementById('neSelectFile') == null) {
          var div = document.getElementsByClassName(
              'dialog-input input-offline');

          console.log(div);
          var $selectFile = $(selectFileString);
          var $autoCreateRootFolder = $(autoCreateRootFolderString);
          var $notCreateAnyChildFolder = $(notCreateAnyChildFolderString);
          var $otherSetting = $(otherSettingString);
          div[0].style.display = 'grid';
          div[0].appendChild($selectFile[0]);
          div[0].appendChild($autoCreateRootFolder[0]);

          div[0].appendChild($notCreateAnyChildFolder[0]);
          div[0].appendChild($otherSetting[0]);

          //界面选项设置
          //根目录自动创建默认值:
          document.getElementById(
              'neAutoCreateRootfolder').checked = GM_config.get(
              currentConfig.createRootFolderDefaultValue);
          //是否显示不创建任何目录:
          document.getElementById(
              'neNotCreateAnyChildFolderParent').style.display = GM_config.get(
              currentConfig.createChildFolderVisible) === true ?
              'block' :
              'none';
          document.getElementById('neNotCreateAnyChildFolder').checked = false;

          $selectFile[0].addEventListener('change', e => {
            console.log(e.target.files);
            if (e.target.files) {
              file = e.target.files[0];
            } else {
              file = '';
            }
          });

          $('#neSetting1')[0].addEventListener('click', e => {
            document.querySelector(`a[btn="close"]`).click();
            GM_config.open();
          });

        } else {

          //界面选项设置
          document.getElementById('neSelectFile').value = '';
          file = '';
          //根目录自动创建默认值:
          document.getElementById(
              'neAutoCreateRootfolder').checked = GM_config.get(
              currentConfig.createRootFolderDefaultValue);
          //是否显示不创建任何目录:
          document.getElementById(
              'neNotCreateAnyChildFolderParent').style.display = GM_config.get(
              currentConfig.createChildFolderVisible) === true ?
              'block' :
              'none';
          document.getElementById('neNotCreateAnyChildFolder').checked = false;
        }

        if (document.getElementById('downsha1') == null) {

          resetTaskCancelFlag();

          var $btn = $(beginUploadBySha1String);
          jNode[0].appendChild($btn[0]);
          $btn[0].addEventListener('click', e => {

            let cid = $(`li[rel="bts_select_item"][class="selected"]`).
                attr('file_id');
            if (cid == '') {
              //目录不存在,比如把 “云下载” 目录删除
              cid = '0';
            }

            let notCreateAnyChildFolder = document.getElementById(
                'neNotCreateAnyChildFolder').checked;
            let autoCreateRootfolder = document.getElementById(
                'neAutoCreateRootfolder').checked;

            let links = document.getElementById('js_offline_new_add').value;
            let config = {
              targetCid: cid,
              text: '',
              folderSetting: {
                notCreateAnyChildFolder: notCreateAnyChildFolder,
                sleepTime: GM_config.get(currentConfig.createFolderSleepTime),
                rootFolder: {
                  needToCreate: autoCreateRootfolder,
                  folderName: '',
                },
              },
              itemNameSeparator: {
                needToSeparate: GM_config.get(currentConfig.autoUseSeparator),
                needToRemoveSeparator: GM_config.get(
                    currentConfig.autoUseSeparatorToRename),
                separator: GM_config.get(currentConfig.separator),
              },
              upload: {
                workingNumber: GM_config.get(currentConfig.uploadNumber),
                sleepTime: GM_config.get(currentConfig.uploadSleepTime),
              },
            };

            onlineFile = window.parent.document.tryUploadItem;
            if (onlineFile) {
              document.querySelector(`a[btn="close"]`).click();
              console.log('选择了在线文件:');
              config.folderSetting.rootFolder.folderName = onlineFile.name.split(
                  '.').slice(0, -1).join('.');

              postSha1Messgae(createMessage(MessageType.FILEDOWNLOAD, {
                onlineFile: onlineFile,
                config: config,
              }));
              window.parent.document.tryUploadItem = null;

            } else if (file) {
              console.log('选择了文件:');
              console.log(file);
              let reader = new FileReader();
              reader.addEventListener('load', function(t) {
                //fix: v3.3 导入的文件名带"."
                config.folderSetting.rootFolder.folderName = file.name.split(
                    '.').
                    slice(0, -1).
                    join('.');
                config.text = t.target.result;
                file = '';
                UploadFilesBySha1Links(config);
              });
              reader.readAsText(file);
              document.querySelector(`a[btn="close"]`).click();
              //(document.getElementsByClassName('close')[2].click());

            } else if (links) {

              console.log('选择了文本框中输入:');
              // var text = { FileName: "", Content: links };
              config.folderSetting.rootFolder.folderName = '';
              config.text = links;

              document.querySelector(`a[btn="close"]`).click();
              //closeButton.click();
              UploadFilesBySha1Links(config);

            }

          });
        }

        let save = document.querySelector('.bt-task-safe');
        if (save != null && document.querySelector('#saveTip') == null) {
          save.insertAdjacentHTML('afterend',
              `<div id="saveTip"><p style="margin-left:20px;margin-top:-20px;">转存也在此处选择位置。<span style="color:red;">因115页面结构以及接口调整,暂时下线【默认保存至当前位置】</span></p></div>`);
        }

        if (window.parent.document.tryUploadItem) {
          document.getElementById('neFileOnline').style.display = 'block';
          document.getElementById('neFileUpload').style.display = 'none';
          document.getElementById(
              'neOnlineFileName').innerText = window.parent.document.tryUploadItem.name;
        } else {
          document.getElementById('neFileOnline').style.display = 'none';
          document.getElementById('neFileUpload').style.display = 'block';
        }

        let currentFolder = getCurrentFolderDisplayed();
        console.log(currentFolder);

        /*
         let ul = document.querySelector('ul[rel="select_item_ul"]');
         if (ul) {
         //弹窗时可能数据还在获取,延迟修改
         //fix: v3.3.1 优化”脚本修改比自带的快“
         //fix: v3.4 再次延长,优化”脚本修改比自带的快“
         delay(1000).then(t => {
         const lis = ul.querySelectorAll("li");
         let folderIncluded = null;

         for (const li of lis) {

         if(li.className&&li.className.includes("selected"))
         {
         li.classList.remove("selected")
         }

         console.log(li.attributes['file_id'])
         if (li.attributes['file_id'].value == currentFolder.id) {
         folderIncluded = li;
         }
         };

         console.log(folderIncluded)
         if(folderIncluded){
         folderIncluded.classList.add("selected");
         }
         else{
         const li = `<li rel="bts_select_item" class="selected" file_id="${currentFolder.id}"><a href="javascript:;"><span>${currentFolder.name}</span></a></li>`;
         ul.insertAdjacentHTML("afterbegin", li);
         }



         const em= document.querySelector('em[rel="downFileResult"]');
         if(em){
         em.textContent=currentFolder.name;
         }
         });

         }
         */

      }

      // function formatCommonToJson(children, root) {
      //     let childFiles = children.filter(f => f.Paths.length == 0);
      //     root.files = Array();
      //     root.dirs = Array();
      //     childFiles.forEach(c => root.files.push({ Name: c.Name }));

      //     let selectedChildren = children.filter(f => f.Paths.length > 0);

      //     let childFolders = selectedChildren.map(q => q.Paths[0]).filter((v, i, a) => a.indexOf(v) === i);
      //     childFolders.forEach(f => root.dirs.push({ dir_name: f }));

      //     root.dirs.forEach(d => {
      //         let newChildren = selectedChildren.filter(f => f.Paths[0] == d.dir_name)
      //             .map(c => {
      //                 let a = { Name: c.Name, Paths: c.Paths.slice(1) };
      //                 return a;
      //             })
      //         ConverterAdvanced(newChildren, d);
      //     });
      // }

      //在网格中添加创建Sha1按钮
      function AddCeateSha1ButtonInGrid(jNode) {
        //add: v3.4 增加设置是否显示 缩略图模式下获取sha1
        if (!GM_config.get(currentConfig.createItemSha1InThumb)) return;
        let $li = jNode.find('[class~="file-thumb"]');
        //fix: v3.3.1 修正文件夹如果设置封面,获取sha1链接按钮会覆盖的bug
        let $button = $(
            '<button class="btnInGrid" title="获取sha1链接"><i class="icon-operate-light ifol-download" style="height:14px;width:14px;position:inherit"></i></button>');
        $button.appendTo($li);
        $button.click(function(e) {
          e.stopPropagation();
          let pItem = GetFileItemByliNode(jNode[0]);
          console.log('生成sha1');
          console.log(pItem);
          //生成sha1
          resetTaskCancelFlag();
          CreateSha1Links(pItem);
        });

      }

      function renameInToolTip(element, pItem) {
        var $btn1 = $('<a><i></i><span>去除分隔符</span></a>');
        $btn1.prependTo(element);
        $btn1[0].addEventListener('click', async e => {
          let separator = GM_config.get(currentConfig.separator);
          let sleepTime = GM_config.get(currentConfig.createFolderSleepTime);
          postSha1Messgae(createMessage(MessageType.BEGIN4UPLOAD, ''));
          postSha1Messgae(createMessage(MessageType.PROCESSING,
              `即将开始重命名 【${pItem.name}】 下所有文件:<br><br>去除分隔符:${separator}`));
          await delay(1000);
          await processRename(pItem.id, separator, sleepTime, result => {
            if (result.state === true) {
              postSha1Messgae(
                  createMessage(MessageType.PROCESSING, result.msg));
            } else {
              postSha1Messgae(createMessage(MessageType.ERROR, result.msg));
            }
          });

          postSha1Messgae(
              createMessage(MessageType.END4UPLOAD, `对目录 【${pItem.name}】下的文件重命名完成!\
                <br><br>获取最新版,或者遇到问题去此反馈,感谢 !点击->\
                <a href="${TIPS.UpdateUrl}" target="_blank">${TIPS.VersionTips}</a>`,
                  pItem.id));
        });
      }

      //使用在线文件上传工具提示
      function createSha1InToolTip(toopTip, pItem) {
        var $btn = $(
            '<a ><i></i><div style="background:white"><span>获取SHA1链接</span></div></a>');

        $btn.prependTo(toopTip);
        $btn[0].addEventListener('click', e => {
          console.log('生成sha1');
          console.log(pItem);
          //生成sha1
          resetTaskCancelFlag();
          CreateSha1Links(pItem);
        });
      }

      function usingOnlineFileToUploadInToolTip(toopTip, pItem) {

        let temps = pItem.name.split('.');
        let extension = temps[temps.length - 1].toLowerCase();
        if (extension == 'json' || extension == 'txt') {
          let $btn1 = $(
              '<a menu="offline_task" title="暂不支持大于2MB的文本文件操作"><i></i><div style="background:white"><span>尝试转存</span></div></a>');
          $btn1.prependTo(toopTip);
          //尝试转存
          $btn1[0].addEventListener('click', e => {
            window.parent.document.tryUploadItem = pItem;
          });
        }
      }

      //fix:v3.3 修复在回收站显示提取的bug
      function AddShareSHA1Btn(jNode) {

        var parentNode = jNode[0].parentNode;
        var pItem = GetFileItemByliNode(parentNode);
        if (!pItem.name) return;

        jNode[0].style.left = '180px';
        //目录,去除分隔符
        if (pItem.isFolder && GM_config.get(currentConfig.advancedRename)) {
          renameInToolTip(jNode[0], pItem);
        }

        //add: v3.4 增加设置是否显示 列表模式下获取sha1
        if (GM_config.get(currentConfig.createItemSha1)) {
          createSha1InToolTip(jNode[0], pItem);
        }

        if (!pItem.isFolder) {
          usingOnlineFileToUploadInToolTip(jNode[0], pItem);
        }

        //生成json格式
        // if(pItem.isFolder)
        // {
        //     var $btn1 = $('<a><i></i><span>获取SHA1(json)</span></a>');
        //     $btn1.prependTo(jNode[0]);
        //     $btn1[0].addEventListener('click', e => {
        //         console.log(pItem);
        //     //生成sha1
        //         resetTaskCancelFlag();
        //         CreateSha1Links(pItem);
        //     })
        // }

      }

      //获取搜索列表
      async function GetSearchList(isOnlySelected) {
        resetTaskCancelFlag();

        var msg = '正在获取文件...';
        postSha1Messgae(createMessage(MessageType.BEGIN, msg));

        var doc = document.getElementsByClassName('search-iframe')[0];
        if (!doc) doc = document;
        var lis = doc.querySelectorAll(
            '.list-cell.lstc-search > .list-contents > ul > li');
        if (!lis) return;
        console.log(lis);
        var files = new Array();
        for (var li of lis) {
          var fileItem = GetFileItemByliNode(li);
          files.push(fileItem);
        }
        console.log('0: search items{0}'.format(files.length));
        if (isOnlySelected) {
          console.log('search items onlySelected');
          files = files.filter(q => q.selected);
        }

        console.log('1: search items{0}'.format(files.length));

        console.log(document.URL);
        var url = new URL(document.URL);
        var key = url.searchParams.get('search_value');
        key = key ? key : '搜索结果';
        files = files.filter(q => !q.isFolder);
        msg = '获取到符合搜索的文件数:{0}'.format(files.length);
        postSha1Messgae(createMessage(MessageType.PROCESSING, msg));
        await delay(200);
        await InnerCreateSha1Links(files, key);

      }

      //为选中的项目创建Sha1按钮
      function CreateSha1ButtonForSelectedItems(element) {
        if (document.getElementById('my115CreateSha1ForSelected')) return;

        let div = `<div id="my115CreateSha1ForSelected" style="margin-left:20px;cursor:pointer">
        <a hef="javascript=:;" class="button btn-line">
        <i class="icon-operate ifo-share"></i>
        <span>获取选中项的SHA1链接</span>
        </a>
      </div>`;
        element[0].insertAdjacentHTML('beforeend', div);
        document.getElementById('my115CreateSha1ForSelected').
            addEventListener('mousedown', async e => {
              //生成转传sha1

              //停止传播
              e.stopPropagation();
              let seletedElements = new Array();
              //列表模式下:
              let selectedItemsInList = document.querySelectorAll(
                  '.list-contents > ul > li');
              console.log(`列表模式下,选中:${selectedItemsInList.length}`);
              selectedItemsInList.forEach(ele => seletedElements.push(ele));
              //缩略图模式下:
              selectedItemsInList = document.querySelectorAll(
                  '.list-thumb > ul > li');
              console.log(`缩略图模式下,选中:${selectedItemsInList.length}`);
              selectedItemsInList.forEach(ele => seletedElements.push(ele));

              console.log(`选中:${seletedElements.length}`);
              let items = new Array();
              for (let item of seletedElements) {
                let sItem = GetFileItemByliNode(item);
                if (sItem.selected) items.push(sItem);
              }

              if (items.length == 0) return;
              if (items.length == 1) {
                //创建Sha1单个链接
                await CreateSha1Links(items[0]);
              } else {
                //创建Sha1所有链接
                await CreateSha1LinksAll(items,
                    `${items[0].name}等${items.length}个`);
              }

            });
      }

      //为搜索项添加共享按钮
      function AddShareButtonForSearchItem(node) {

        document.getElementById('my115Dropdown').style.display = 'none';

        //每一项
        var lis = node[0].getElementsByTagName('li');
        for (var li of lis) {
          var pItem = GetFileItemByliNode(li);
          var $btn = $('<div class="file-opr" style="left:200px"></div>');
          $btn.appendTo(li);
        }

        //针对当前页面
        $('.left-tvf > a.btn-upload').css('top', '10px');
        if (document.getElementById('btn_selected_sha1') == null) {
          var $btn_selected = $(`<a href="javascript:;" id="btn_selected_sha1" class="button btn-line" style="top:10px">
            <i class="icon-operate ifo-share"></i>
            <span>提取本页选中文件(不包括文件夹)</span>
            <em style="display:none;" class="num-dot"></em>
            </a>`);
          $('.left-tvf').eq(0).append($btn_selected);

          $btn_selected[0].addEventListener('click', e => {
            GetSearchList(true);
          });
        }

        if (document.getElementById('btn_all_sha1') == null) {
          var $btn_all = $(`<a href="javascript:;" id="btn_all_sha1" class="button btn-line" style="top:10px">
            <i class="icon-operate ifo-share"></i>
            <span>提取本页所有文件(不包括文件夹)</span>
            <em style="display:none;" class="num-dot"></em>
            </a>`);
          $('.left-tvf').eq(0).append($btn_all);

          $btn_all[0].addEventListener('click', e => {
            GetSearchList(false);
          });
        }

      }

      //继续任务
      function ContinuedTask(taskJsonFileName) {
        console.log('ContinuedTask');
        postSha1Messgae(createMessage(MessageType.BEGIN, '正在继续任务...'));
        resetTaskCancelFlag();
        let reader = new FileReader();
        reader.addEventListener('load', function(t) {
          try {

            postSha1Messgae(
                createMessage(MessageType.PROCESSING, '正在解析继续任务配置...'));
            let taskJson = JSON.parse(t.target.result);

            console.log(
                `${taskJson.taskType}, ${taskJson.fileName}, ${taskJson.data.length}`);
            let canContinued = true;
            if (taskJson.data.length > 0) {

            } else {
              canContinued = false;
            }
            if (canContinued) {
              if (taskJson.taskType == TaskType.DOWNLOAD) {
                postSha1Messgae(createMessage(MessageType.PROCESSING,
                    `正在开始对【${taskJson.fileName}】继续提取...请稍等!`));
                `
                        提取:
                            let tempFile=[
                            f.id,f.parentID,f.name,f.size,f.paths,f.pickCode,f.sha1,f.preid,
                            ];
                        `;
                let allFiles = taskJson.data.map(f => {
                  return {
                    id: f[0],
                    parentID: f[1],
                    name: f[2],
                    size: f[3],
                    paths: f[4],
                    pickCode: f[5],
                    sha1: f[6],
                    preid: f[7],
                  };
                });
                InnerCreateSha1Links(allFiles, taskJson.fileName);
              } else if (taskJson.taskType == TaskType.UPLOAD) {
                postSha1Messgae(
                    createMessage(MessageType.BEGIN4UPLOAD, '正在解析sha1链接...'));
                `转化格式
                        转存:
                        let tempFile = [
                            f.parentID,f.name, f.formatedName, f.size, f.sha1, f.preid,f.state
                        ]
                        `;
                let allFiles = taskJson.data.map(f => {
                  return {
                    id: '',
                    parentID: f[0],
                    name: f[1],
                    formatedName: f[2],
                    size: f[3],
                    pickCode: '',
                    sha1: f[4],
                    preid: f[5],
                    state: f[6],
                  };
                });

                taskJson.data = allFiles;

                UploadFilesBySha1Links(null, taskJson);

              }
            } else {
              let msg = `
                    获取的继续任务:【 <b>${taskJson.fileName}</b> 】,配置有误!<br>
                    可能不是正确的配置文件, 或者不适用于此版本的配置!
                    `;
              postSha1Messgae(createMessage(MessageType.END, msg));
            }

          } catch (error) {
            console.error(error);
          }

        });
        reader.readAsText(taskJsonFileName);

      }



      /**
       *
       * Base64 encode / decode
       *
       * @author haitao.tu
       * @date 2010-04-26
       * @email tuhaitao@foxmail.com
       *
       */
      function Base64() {
        // private property
        var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
        // public method for encoding
        this.encode = function (input) {
          var output = "";
          var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
          var i = 0;
          input = _utf8_encode(input);
          while (i < input.length) {
            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
            if (isNaN(chr2)) {
              enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
              enc4 = 64;
            }
            output = output +
                _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
                _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
          }
          return output;
        }
        // public method for decoding
        this.decode = function (input) {
          var output = "";
          var chr1, chr2, chr3;
          var enc1, enc2, enc3, enc4;
          var i = 0;
          input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
          while (i < input.length) {
            enc1 = _keyStr.indexOf(input.charAt(i++));
            enc2 = _keyStr.indexOf(input.charAt(i++));
            enc3 = _keyStr.indexOf(input.charAt(i++));
            enc4 = _keyStr.indexOf(input.charAt(i++));
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            output = output + String.fromCharCode(chr1);
            if (enc3 != 64) {
              output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
              output = output + String.fromCharCode(chr3);
            }
          }
          output = _utf8_decode(output);
          return output;
        }
        // private method for UTF-8 encoding
        var _utf8_encode = function (string) {
          string = string.replace(/\r\n/g,"\n");
          var utftext = "";
          for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) {
              utftext += String.fromCharCode(c);
            } else if((c > 127) && (c < 2048)) {
              utftext += String.fromCharCode((c >> 6) | 192);
              utftext += String.fromCharCode((c & 63) | 128);
            } else {
              utftext += String.fromCharCode((c >> 12) | 224);
              utftext += String.fromCharCode(((c >> 6) & 63) | 128);
              utftext += String.fromCharCode((c & 63) | 128);
            }
          }
          return utftext;
        }
        // private method for UTF-8 decoding
        var _utf8_decode = function (utftext) {
          var string = "";
          var i = 0;
          var c = c1 = c2 = 0;
          while ( i < utftext.length ) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
              string += String.fromCharCode(c);
              i++;
            } else if((c > 191) && (c < 224)) {
              c2 = utftext.charCodeAt(i+1);
              string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
              i += 2;
            } else {
              c2 = utftext.charCodeAt(i+1);
              c3 = utftext.charCodeAt(i+2);
              string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
              i += 3;
            }
          }
          return string;
        }
      }




    }

)
();