nHentai Helper

Download nHentai manga as compression file easily, and add some useful features. Also support some mirror sites.

As of 2022-09-30. See the latest version.

// ==UserScript==
// @name               nHentai Helper
// @name:zh-CN         nHentai 助手
// @name:zh-TW         nHentai 助手
// @namespace          https://github.com/Tsuk1ko
// @version            3.3.8
// @author             Jindai Kirin
// @description        Download nHentai manga as compression file easily, and add some useful features. Also support some mirror sites.
// @description:zh-CN  为 nHentai 增加压缩打包下载方式以及一些辅助功能,同时还支持一些镜像站
// @description:zh-TW  爲 nHentai 增加壓縮打包下載方式以及一些輔助功能,同時還支援一些鏡像站
// @license            GPL-3.0
// @icon               https://nhentai.net/favicon.ico
// @homepageURL        https://github.com/Tsuk1ko/nhentai-helper
// @supportURL         https://github.com/Tsuk1ko/nhentai-helper/issues
// @include            /^https:\/\/([^/]*\.)?(nya|dog|cat|bug|qq|fox|ee|yy)hentai[0-9]*\./
// @match              https://nhentai.net/*
// @match              https://nhentai.xxx/*
// @match              https://nhentai.to/*
// @match              https://nhentai.website/*
// @require            https://fastly.jsdelivr.net/npm/comlink@4.3.1/dist/umd/comlink.min.js
// @require            https://fastly.jsdelivr.net/npm/eventemitter3@4.0.7/umd/eventemitter3.min.js
// @require            https://fastly.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js
// @require            https://fastly.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.min.js
// @require            https://fastly.jsdelivr.net/npm/jquery-pjax@2.0.1/jquery.pjax.min.js
// @require            https://fastly.jsdelivr.net/npm/localforage@1.10.0/dist/localforage.min.js
// @require            https://fastly.jsdelivr.net/npm/md5@2.3.0/dist/md5.min.js
// @require            https://fastly.jsdelivr.net/npm/noty@3.1.4/lib/noty.min.js
// @require            https://fastly.jsdelivr.net/npm/streamsaver@2.0.6/StreamSaver.min.js
// @require            https://fastly.jsdelivr.net/npm/vue@3.2.38/dist/vue.global.prod.js
// @connect            nhentai.net
// @connect            i.nhentai.net
// @connect            cdn.nhentai.xxx
// @connect            cdn.nload.xyz
// @resource           notyCss         https://fastly.jsdelivr.net/npm/noty@3.1.4/lib/noty.min.css
// @resource           elementPlusJs   https://fastly.jsdelivr.net/npm/element-plus@2.2.16/dist/index.full.min.js
// @resource           elementPlusCss  https://fastly.jsdelivr.net/npm/element-plus@2.2.16/dist/index.css
// @grant              GM_addStyle
// @grant              GM_getResourceText
// @grant              GM_registerMenuCommand
// @grant              unsafeWindow
// @grant              GM_getValue
// @grant              GM_setValue
// @grant              GM_xmlhttpRequest
// @run-at             document-end
// @noframes          
// ==/UserScript==

(t=>{const e=document.createElement("style");e.dataset.source="vite-plugin-monkey",e.innerText=t,document.head.appendChild(e)})('.nhentai-helper-btn:disabled{cursor:wait}.gallery>.nhentai-helper-btn{position:absolute;top:0;min-width:42px;opacity:.8}.gallery:hover>.nhentai-helper-btn{opacity:1}.gallery .download-zip-btn{left:0}.gallery .ignore-btn{right:0}#page-container{position:relative}@media screen and (max-width: 768px){#page-container{padding-top:40px}}#online-view-mode-btn{position:absolute;right:0;top:0;margin:0}.btn-noty-green{background-color:#66bb6a!important}.btn-noty-blue{background-color:#42a5f5!important}.btn-noty:hover{filter:brightness(1.15)}.noty_buttons{padding-top:0!important}.pages-input{-webkit-appearance:none;display:inline-block;border-radius:3px;padding:0 .1em 0 1em;font-size:1em;width:100%;height:40px;border:0;vertical-align:top;margin-top:5px}.gallery.downloaded .caption{color:#999}.download-item[data-v-5e3261cd]{position:relative;white-space:nowrap;padding:2px;overflow:visible}.download-item--can-cancel[data-v-5e3261cd]:hover{width:calc(100% - 30px)}.download-item__cancel[data-v-5e3261cd]{cursor:pointer;position:absolute;top:0;right:-30px;color:#f44336;font-size:20px;line-height:30px;width:30px}.download-item__title[data-v-5e3261cd]{overflow:hidden;text-overflow:ellipsis;text-align:left}.download-item__progress[data-v-5e3261cd]{background-color:#0000ff80;line-height:10px}.download-item--error .download-item__progress[data-v-5e3261cd]{background-color:#ff000080}.download-item--compressing .download-item__progress[data-v-5e3261cd]{background-color:#00ff0080}.download-item__progress-text[data-v-5e3261cd]{transform:scale(.8)}#download-panel[data-v-658acab9]{overflow-x:hidden;position:fixed;top:20vh;right:0;width:calc(50vw - 620px);max-width:300px;min-width:150px;max-height:60vh;background-color:#000000b3;z-index:100;font-size:12px;overflow-y:scroll}#download-panel[data-v-658acab9]::-webkit-scrollbar{width:6px;background-color:#000000b3}#download-panel[data-v-658acab9]::-webkit-scrollbar-thumb{background-color:#fff9}.nhentai-helper-setting-help-buttons{float:left;position:absolute}#nhentai-helper-setting-dialog .asterisk-left:before{content:"*";color:var(--el-color-danger);margin-right:4px}#nhentai-helper-setting-dialog label{font-weight:unset}#nhentai-helper-setting-dialog input:not([type="file"]):not([type="checkbox"]){background:inherit;color:var(--el-input-text-color, var(--el-text-color-regular))}#nhentai-helper-setting-dialog .el-input.is-disabled .el-input__inner{color:var(--el-disabled-text-color)}#nhentai-helper-setting-dialog .el-slider__stop{border:solid 1px var(--el-slider-runway-bg-color)}#nhentai-helper-setting-dialog .el-form-item:last-of-type{margin-bottom:0}#nhentai-helper-setting-dialog .el-form-item.refresh-required>.el-form-item__label-wrap>.el-form-item__label:after{content:"*";color:var(--el-color-danger);margin-left:4px}#nhentai-helper-setting-dialog .el-divider__text{color:var(--el-text-color-secondary);user-select:none}#nhentai-helper-setting-dialog .m-l-16{margin-left:16px}#nhentai-helper-setting-dialog .m-b-32{margin-bottom:32px}#nhentai-helper-setting-dialog .no-sl,#nhentai-helper-setting-dialog .el-form-item__label{user-select:none}');

var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
(function($, jqueryPjax, Vue, eventemitter3, fileSaver, localforage, md5, comlink, Noty, streamsaver) {
  var _a, _b;
  "use strict";
  const _interopDefaultLegacy = (e) => e && typeof e === "object" && "default" in e ? e : { default: e };
  function _interopNamespace(e) {
    if (e && e.__esModule)
      return e;
    const n2 = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
    if (e) {
      for (const k in e) {
        if (k !== "default") {
          const d = Object.getOwnPropertyDescriptor(e, k);
          Object.defineProperty(n2, k, d.get ? d : {
            enumerable: true,
            get: () => e[k]
          });
        }
      }
    }
    n2.default = e;
    return Object.freeze(n2);
  }
  const $__default = /* @__PURE__ */ _interopDefaultLegacy($);
  const Vue__namespace = /* @__PURE__ */ _interopNamespace(Vue);
  const localforage__default = /* @__PURE__ */ _interopDefaultLegacy(localforage);
  const md5__default = /* @__PURE__ */ _interopDefaultLegacy(md5);
  const Noty__default = /* @__PURE__ */ _interopDefaultLegacy(Noty);
  var r = (_a = Reflect.get(document, "__monkeyWindow")) != null ? _a : window;
  r.GM;
  r.unsafeWindow = (_b = r.unsafeWindow) != null ? _b : window;
  var n = r.unsafeWindow;
  r.GM_info;
  r.GM_cookie;
  var u = (...e) => r.GM_setValue(...e), l = (...e) => r.GM_addStyle(...e), m = (...e) => r.GM_registerMenuCommand(...e), b = (...e) => r.GM_xmlhttpRequest(...e), h = (...e) => r.GM_getValue(...e);
  const WORKER_THREAD_NUM = Math.max(navigator.hardwareConcurrency - 1, 1);
  const { pathname, host } = location;
  const IS_PAGE_MANGA_DETAIL = /^\/g\/[0-9]+\/?(\?.*)?$/.test(pathname);
  const IS_PAGE_ONLINE_VIEW = /^\/g\/[0-9]+(\/list)?\/[0-9]+\/?(\?.*)?$/.test(pathname);
  const IS_PAGE_MANGA_LIST = !IS_PAGE_MANGA_DETAIL && !IS_PAGE_ONLINE_VIEW && document.getElementsByClassName("gallery").length > 0;
  const IS_NHENTAI = host === "nhentai.net";
  const IS_NHENTAI_TO = host === "nhentai.to" || host === "nhentai.website";
  const isNodeOrElement = typeof Node === "function" ? (val) => val instanceof Node : (val) => val && typeof val === "object" && typeof val.nodeType === "number" && typeof val.nodeName === "string";
  if (IS_NHENTAI) {
    if (h("prevent_console_clear", false) || localStorage.getItem("NHENTAI_HELPER_DEBUG")) {
      const c = n.console;
      c._clear = c.clear;
      c.clear = () => {
      };
      c._log = c.log;
      c.log = (...args) => {
        if (args.length === 1 && isNodeOrElement(args[0]))
          return;
        return c._log(...args);
      };
    }
  }
  const logger = {
    log: (...args) => console.log("[nhentai-helper]", ...args),
    warn: (...args) => console.warn("[nhentai-helper]", ...args),
    error: (...args) => console.error("[nhentai-helper]", ...args)
  };
  const index = "";
  const cssLoader = (e) => {
    const t = GM_getResourceText(e);
    return GM_addStyle(t), t;
  }, rawLoader = (e) => GM_getResourceText(e);
  cssLoader("notyCss");
  var functionOnce = once;
  function once(fn) {
    var called, value;
    if (typeof fn !== "function") {
      throw new Error("expected a function but got " + fn);
    }
    return function wrap() {
      if (called) {
        return value;
      }
      called = true;
      value = fn.apply(this, arguments);
      fn = void 0;
      return value;
    };
  }
  let getRandomValues;
  const rnds8 = new Uint8Array(16);
  function rng() {
    if (!getRandomValues) {
      getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
      if (!getRandomValues) {
        throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
      }
    }
    return getRandomValues(rnds8);
  }
  const byteToHex = [];
  for (let i = 0; i < 256; ++i) {
    byteToHex.push((i + 256).toString(16).slice(1));
  }
  function unsafeStringify(arr, offset = 0) {
    return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
  }
  const randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
  const native = {
    randomUUID
  };
  function v4(options, buf, offset) {
    if (native.randomUUID && !buf && !options) {
      return native.randomUUID();
    }
    options = options || {};
    const rnds = options.random || (options.rng || rng)();
    rnds[6] = rnds[6] & 15 | 64;
    rnds[8] = rnds[8] & 63 | 128;
    if (buf) {
      offset = offset || 0;
      for (let i = 0; i < 16; ++i) {
        buf[offset + i] = rnds[i];
      }
      return buf;
    }
    return unsafeStringify(rnds);
  }
  const removeAt = (array, index2) => array.splice(index2, 1)[0];
  class AsyncQueue {
    constructor(thread = 1) {
      __publicField(this, "queue", Vue.reactive([]));
      __publicField(this, "emitter", new eventemitter3.EventEmitter());
      __publicField(this, "canSingleStart", () => true);
      __publicField(this, "singleRunning", false);
      this.thread = thread;
    }
    get runningThreadNum() {
      return this.queue.filter(({ running }) => running).length;
    }
    get length() {
      return this.queue.length;
    }
    push(fn, info) {
      this.queue.push({
        id: v4(),
        running: false,
        fn,
        info
      });
    }
    async start() {
      if (this.thread <= 1) {
        if (this.singleRunning || this.queue.length === 0)
          return;
        this.singleRunning = true;
        do {
          if (!this.canSingleStart()) {
            this.singleRunning = false;
            return;
          }
          await this.queue[0].fn();
          this.queue.shift();
        } while (this.queue.length > 0);
        this.singleRunning = false;
        this.emitter.emit("finish");
      } else {
        const running = this.runningThreadNum;
        if (running >= this.thread || this.queue.length === running)
          return;
        const idleItems = this.queue.filter(({ running: running2 }) => !running2);
        for (let i = 0; i < Math.min(idleItems.length, this.thread - running); i++) {
          const item = idleItems[i];
          item.running = true;
          item.fn().then(async () => {
            removeAt(
              this.queue,
              this.queue.findIndex(({ id }) => id === item.id)
            );
            if (this.queue.length)
              await this.start();
            else
              this.emitter.emit("finish");
          }).catch(logger.error);
        }
      }
    }
    async skipFromError() {
      this.queue.shift();
      await this.restartFromError();
    }
    async restartFromError() {
      this.singleRunning = false;
      await this.start();
    }
  }
  var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
  const freeGlobal$1 = freeGlobal;
  var freeSelf = typeof self == "object" && self && self.Object === Object && self;
  var root = freeGlobal$1 || freeSelf || Function("return this")();
  const root$1 = root;
  var Symbol$1 = root$1.Symbol;
  const Symbol$2 = Symbol$1;
  var objectProto$h = Object.prototype;
  var hasOwnProperty$d = objectProto$h.hasOwnProperty;
  var nativeObjectToString$2 = objectProto$h.toString;
  var symToStringTag$1 = Symbol$2 ? Symbol$2.toStringTag : void 0;
  function getRawTag(value) {
    var isOwn = hasOwnProperty$d.call(value, symToStringTag$1), tag = value[symToStringTag$1];
    try {
      value[symToStringTag$1] = void 0;
      var unmasked = true;
    } catch (e) {
    }
    var result = nativeObjectToString$2.call(value);
    if (unmasked) {
      if (isOwn) {
        value[symToStringTag$1] = tag;
      } else {
        delete value[symToStringTag$1];
      }
    }
    return result;
  }
  var objectProto$g = Object.prototype;
  var nativeObjectToString$1 = objectProto$g.toString;
  function objectToString(value) {
    return nativeObjectToString$1.call(value);
  }
  var nullTag = "[object Null]", undefinedTag = "[object Undefined]";
  var symToStringTag = Symbol$2 ? Symbol$2.toStringTag : void 0;
  function baseGetTag(value) {
    if (value == null) {
      return value === void 0 ? undefinedTag : nullTag;
    }
    return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
  }
  function isObjectLike(value) {
    return value != null && typeof value == "object";
  }
  var symbolTag$1 = "[object Symbol]";
  function isSymbol(value) {
    return typeof value == "symbol" || isObjectLike(value) && baseGetTag(value) == symbolTag$1;
  }
  function arrayMap(array, iteratee) {
    var index2 = -1, length = array == null ? 0 : array.length, result = Array(length);
    while (++index2 < length) {
      result[index2] = iteratee(array[index2], index2, array);
    }
    return result;
  }
  var isArray = Array.isArray;
  const isArray$1 = isArray;
  var INFINITY$1 = 1 / 0;
  var symbolProto$1 = Symbol$2 ? Symbol$2.prototype : void 0, symbolToString = symbolProto$1 ? symbolProto$1.toString : void 0;
  function baseToString(value) {
    if (typeof value == "string") {
      return value;
    }
    if (isArray$1(value)) {
      return arrayMap(value, baseToString) + "";
    }
    if (isSymbol(value)) {
      return symbolToString ? symbolToString.call(value) : "";
    }
    var result = value + "";
    return result == "0" && 1 / value == -INFINITY$1 ? "-0" : result;
  }
  function isObject(value) {
    var type = typeof value;
    return value != null && (type == "object" || type == "function");
  }
  function identity(value) {
    return value;
  }
  var asyncTag = "[object AsyncFunction]", funcTag$1 = "[object Function]", genTag = "[object GeneratorFunction]", proxyTag = "[object Proxy]";
  function isFunction(value) {
    if (!isObject(value)) {
      return false;
    }
    var tag = baseGetTag(value);
    return tag == funcTag$1 || tag == genTag || tag == asyncTag || tag == proxyTag;
  }
  var coreJsData = root$1["__core-js_shared__"];
  const coreJsData$1 = coreJsData;
  var maskSrcKey = function() {
    var uid = /[^.]+$/.exec(coreJsData$1 && coreJsData$1.keys && coreJsData$1.keys.IE_PROTO || "");
    return uid ? "Symbol(src)_1." + uid : "";
  }();
  function isMasked(func) {
    return !!maskSrcKey && maskSrcKey in func;
  }
  var funcProto$2 = Function.prototype;
  var funcToString$2 = funcProto$2.toString;
  function toSource(func) {
    if (func != null) {
      try {
        return funcToString$2.call(func);
      } catch (e) {
      }
      try {
        return func + "";
      } catch (e) {
      }
    }
    return "";
  }
  var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
  var reIsHostCtor = /^\[object .+?Constructor\]$/;
  var funcProto$1 = Function.prototype, objectProto$f = Object.prototype;
  var funcToString$1 = funcProto$1.toString;
  var hasOwnProperty$c = objectProto$f.hasOwnProperty;
  var reIsNative = RegExp(
    "^" + funcToString$1.call(hasOwnProperty$c).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$"
  );
  function baseIsNative(value) {
    if (!isObject(value) || isMasked(value)) {
      return false;
    }
    var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
    return pattern.test(toSource(value));
  }
  function getValue(object, key) {
    return object == null ? void 0 : object[key];
  }
  function getNative(object, key) {
    var value = getValue(object, key);
    return baseIsNative(value) ? value : void 0;
  }
  var WeakMap = getNative(root$1, "WeakMap");
  const WeakMap$1 = WeakMap;
  function apply(func, thisArg, args) {
    switch (args.length) {
      case 0:
        return func.call(thisArg);
      case 1:
        return func.call(thisArg, args[0]);
      case 2:
        return func.call(thisArg, args[0], args[1]);
      case 3:
        return func.call(thisArg, args[0], args[1], args[2]);
    }
    return func.apply(thisArg, args);
  }
  var HOT_COUNT = 800, HOT_SPAN = 16;
  var nativeNow = Date.now;
  function shortOut(func) {
    var count = 0, lastCalled = 0;
    return function() {
      var stamp = nativeNow(), remaining = HOT_SPAN - (stamp - lastCalled);
      lastCalled = stamp;
      if (remaining > 0) {
        if (++count >= HOT_COUNT) {
          return arguments[0];
        }
      } else {
        count = 0;
      }
      return func.apply(void 0, arguments);
    };
  }
  function constant(value) {
    return function() {
      return value;
    };
  }
  var defineProperty = function() {
    try {
      var func = getNative(Object, "defineProperty");
      func({}, "", {});
      return func;
    } catch (e) {
    }
  }();
  const defineProperty$1 = defineProperty;
  var baseSetToString = !defineProperty$1 ? identity : function(func, string) {
    return defineProperty$1(func, "toString", {
      "configurable": true,
      "enumerable": false,
      "value": constant(string),
      "writable": true
    });
  };
  const baseSetToString$1 = baseSetToString;
  var setToString = shortOut(baseSetToString$1);
  const setToString$1 = setToString;
  function arrayEach(array, iteratee) {
    var index2 = -1, length = array == null ? 0 : array.length;
    while (++index2 < length) {
      if (iteratee(array[index2], index2, array) === false) {
        break;
      }
    }
    return array;
  }
  var MAX_SAFE_INTEGER$1 = 9007199254740991;
  var reIsUint = /^(?:0|[1-9]\d*)$/;
  function isIndex(value, length) {
    var type = typeof value;
    length = length == null ? MAX_SAFE_INTEGER$1 : length;
    return !!length && (type == "number" || type != "symbol" && reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length);
  }
  function baseAssignValue(object, key, value) {
    if (key == "__proto__" && defineProperty$1) {
      defineProperty$1(object, key, {
        "configurable": true,
        "enumerable": true,
        "value": value,
        "writable": true
      });
    } else {
      object[key] = value;
    }
  }
  function eq(value, other) {
    return value === other || value !== value && other !== other;
  }
  var objectProto$e = Object.prototype;
  var hasOwnProperty$b = objectProto$e.hasOwnProperty;
  function assignValue(object, key, value) {
    var objValue = object[key];
    if (!(hasOwnProperty$b.call(object, key) && eq(objValue, value)) || value === void 0 && !(key in object)) {
      baseAssignValue(object, key, value);
    }
  }
  function copyObject(source, props, object, customizer) {
    var isNew = !object;
    object || (object = {});
    var index2 = -1, length = props.length;
    while (++index2 < length) {
      var key = props[index2];
      var newValue = customizer ? customizer(object[key], source[key], key, object, source) : void 0;
      if (newValue === void 0) {
        newValue = source[key];
      }
      if (isNew) {
        baseAssignValue(object, key, newValue);
      } else {
        assignValue(object, key, newValue);
      }
    }
    return object;
  }
  var nativeMax = Math.max;
  function overRest(func, start, transform) {
    start = nativeMax(start === void 0 ? func.length - 1 : start, 0);
    return function() {
      var args = arguments, index2 = -1, length = nativeMax(args.length - start, 0), array = Array(length);
      while (++index2 < length) {
        array[index2] = args[start + index2];
      }
      index2 = -1;
      var otherArgs = Array(start + 1);
      while (++index2 < start) {
        otherArgs[index2] = args[index2];
      }
      otherArgs[start] = transform(array);
      return apply(func, this, otherArgs);
    };
  }
  function baseRest(func, start) {
    return setToString$1(overRest(func, start, identity), func + "");
  }
  var MAX_SAFE_INTEGER = 9007199254740991;
  function isLength(value) {
    return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
  }
  function isArrayLike(value) {
    return value != null && isLength(value.length) && !isFunction(value);
  }
  function isIterateeCall(value, index2, object) {
    if (!isObject(object)) {
      return false;
    }
    var type = typeof index2;
    if (type == "number" ? isArrayLike(object) && isIndex(index2, object.length) : type == "string" && index2 in object) {
      return eq(object[index2], value);
    }
    return false;
  }
  function createAssigner(assigner) {
    return baseRest(function(object, sources) {
      var index2 = -1, length = sources.length, customizer = length > 1 ? sources[length - 1] : void 0, guard = length > 2 ? sources[2] : void 0;
      customizer = assigner.length > 3 && typeof customizer == "function" ? (length--, customizer) : void 0;
      if (guard && isIterateeCall(sources[0], sources[1], guard)) {
        customizer = length < 3 ? void 0 : customizer;
        length = 1;
      }
      object = Object(object);
      while (++index2 < length) {
        var source = sources[index2];
        if (source) {
          assigner(object, source, index2, customizer);
        }
      }
      return object;
    });
  }
  var objectProto$d = Object.prototype;
  function isPrototype(value) {
    var Ctor = value && value.constructor, proto = typeof Ctor == "function" && Ctor.prototype || objectProto$d;
    return value === proto;
  }
  function baseTimes(n2, iteratee) {
    var index2 = -1, result = Array(n2);
    while (++index2 < n2) {
      result[index2] = iteratee(index2);
    }
    return result;
  }
  var argsTag$2 = "[object Arguments]";
  function baseIsArguments(value) {
    return isObjectLike(value) && baseGetTag(value) == argsTag$2;
  }
  var objectProto$c = Object.prototype;
  var hasOwnProperty$a = objectProto$c.hasOwnProperty;
  var propertyIsEnumerable$1 = objectProto$c.propertyIsEnumerable;
  var isArguments = baseIsArguments(function() {
    return arguments;
  }()) ? baseIsArguments : function(value) {
    return isObjectLike(value) && hasOwnProperty$a.call(value, "callee") && !propertyIsEnumerable$1.call(value, "callee");
  };
  const isArguments$1 = isArguments;
  function stubFalse() {
    return false;
  }
  var freeExports$1 = typeof exports == "object" && exports && !exports.nodeType && exports;
  var freeModule$1 = freeExports$1 && typeof module == "object" && module && !module.nodeType && module;
  var moduleExports$1 = freeModule$1 && freeModule$1.exports === freeExports$1;
  var Buffer = moduleExports$1 ? root$1.Buffer : void 0;
  var nativeIsBuffer = Buffer ? Buffer.isBuffer : void 0;
  var isBuffer = nativeIsBuffer || stubFalse;
  const isBuffer$1 = isBuffer;
  var argsTag$1 = "[object Arguments]", arrayTag$1 = "[object Array]", boolTag$1 = "[object Boolean]", dateTag$1 = "[object Date]", errorTag$2 = "[object Error]", funcTag = "[object Function]", mapTag$2 = "[object Map]", numberTag$1 = "[object Number]", objectTag$3 = "[object Object]", regexpTag$1 = "[object RegExp]", setTag$2 = "[object Set]", stringTag$1 = "[object String]", weakMapTag$1 = "[object WeakMap]";
  var arrayBufferTag$1 = "[object ArrayBuffer]", dataViewTag$2 = "[object DataView]", float32Tag = "[object Float32Array]", float64Tag = "[object Float64Array]", int8Tag = "[object Int8Array]", int16Tag = "[object Int16Array]", int32Tag = "[object Int32Array]", uint8Tag = "[object Uint8Array]", uint8ClampedTag = "[object Uint8ClampedArray]", uint16Tag = "[object Uint16Array]", uint32Tag = "[object Uint32Array]";
  var typedArrayTags = {};
  typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true;
  typedArrayTags[argsTag$1] = typedArrayTags[arrayTag$1] = typedArrayTags[arrayBufferTag$1] = typedArrayTags[boolTag$1] = typedArrayTags[dataViewTag$2] = typedArrayTags[dateTag$1] = typedArrayTags[errorTag$2] = typedArrayTags[funcTag] = typedArrayTags[mapTag$2] = typedArrayTags[numberTag$1] = typedArrayTags[objectTag$3] = typedArrayTags[regexpTag$1] = typedArrayTags[setTag$2] = typedArrayTags[stringTag$1] = typedArrayTags[weakMapTag$1] = false;
  function baseIsTypedArray(value) {
    return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
  }
  function baseUnary(func) {
    return function(value) {
      return func(value);
    };
  }
  var freeExports = typeof exports == "object" && exports && !exports.nodeType && exports;
  var freeModule = freeExports && typeof module == "object" && module && !module.nodeType && module;
  var moduleExports = freeModule && freeModule.exports === freeExports;
  var freeProcess = moduleExports && freeGlobal$1.process;
  var nodeUtil = function() {
    try {
      var types = freeModule && freeModule.require && freeModule.require("util").types;
      if (types) {
        return types;
      }
      return freeProcess && freeProcess.binding && freeProcess.binding("util");
    } catch (e) {
    }
  }();
  const nodeUtil$1 = nodeUtil;
  var nodeIsTypedArray = nodeUtil$1 && nodeUtil$1.isTypedArray;
  var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
  const isTypedArray$1 = isTypedArray;
  var objectProto$b = Object.prototype;
  var hasOwnProperty$9 = objectProto$b.hasOwnProperty;
  function arrayLikeKeys(value, inherited) {
    var isArr = isArray$1(value), isArg = !isArr && isArguments$1(value), isBuff = !isArr && !isArg && isBuffer$1(value), isType = !isArr && !isArg && !isBuff && isTypedArray$1(value), skipIndexes = isArr || isArg || isBuff || isType, result = skipIndexes ? baseTimes(value.length, String) : [], length = result.length;
    for (var key in value) {
      if ((inherited || hasOwnProperty$9.call(value, key)) && !(skipIndexes && (key == "length" || isBuff && (key == "offset" || key == "parent") || isType && (key == "buffer" || key == "byteLength" || key == "byteOffset") || isIndex(key, length)))) {
        result.push(key);
      }
    }
    return result;
  }
  function overArg(func, transform) {
    return function(arg) {
      return func(transform(arg));
    };
  }
  var nativeKeys = overArg(Object.keys, Object);
  const nativeKeys$1 = nativeKeys;
  var objectProto$a = Object.prototype;
  var hasOwnProperty$8 = objectProto$a.hasOwnProperty;
  function baseKeys(object) {
    if (!isPrototype(object)) {
      return nativeKeys$1(object);
    }
    var result = [];
    for (var key in Object(object)) {
      if (hasOwnProperty$8.call(object, key) && key != "constructor") {
        result.push(key);
      }
    }
    return result;
  }
  function keys(object) {
    return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
  }
  function nativeKeysIn(object) {
    var result = [];
    if (object != null) {
      for (var key in Object(object)) {
        result.push(key);
      }
    }
    return result;
  }
  var objectProto$9 = Object.prototype;
  var hasOwnProperty$7 = objectProto$9.hasOwnProperty;
  function baseKeysIn(object) {
    if (!isObject(object)) {
      return nativeKeysIn(object);
    }
    var isProto = isPrototype(object), result = [];
    for (var key in object) {
      if (!(key == "constructor" && (isProto || !hasOwnProperty$7.call(object, key)))) {
        result.push(key);
      }
    }
    return result;
  }
  function keysIn(object) {
    return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
  }
  var assignInWith = createAssigner(function(object, source, srcIndex, customizer) {
    copyObject(source, keysIn(source), object, customizer);
  });
  const extendWith = assignInWith;
  var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, reIsPlainProp = /^\w*$/;
  function isKey(value, object) {
    if (isArray$1(value)) {
      return false;
    }
    var type = typeof value;
    if (type == "number" || type == "symbol" || type == "boolean" || value == null || isSymbol(value)) {
      return true;
    }
    return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object);
  }
  var nativeCreate = getNative(Object, "create");
  const nativeCreate$1 = nativeCreate;
  function hashClear() {
    this.__data__ = nativeCreate$1 ? nativeCreate$1(null) : {};
    this.size = 0;
  }
  function hashDelete(key) {
    var result = this.has(key) && delete this.__data__[key];
    this.size -= result ? 1 : 0;
    return result;
  }
  var HASH_UNDEFINED$2 = "__lodash_hash_undefined__";
  var objectProto$8 = Object.prototype;
  var hasOwnProperty$6 = objectProto$8.hasOwnProperty;
  function hashGet(key) {
    var data = this.__data__;
    if (nativeCreate$1) {
      var result = data[key];
      return result === HASH_UNDEFINED$2 ? void 0 : result;
    }
    return hasOwnProperty$6.call(data, key) ? data[key] : void 0;
  }
  var objectProto$7 = Object.prototype;
  var hasOwnProperty$5 = objectProto$7.hasOwnProperty;
  function hashHas(key) {
    var data = this.__data__;
    return nativeCreate$1 ? data[key] !== void 0 : hasOwnProperty$5.call(data, key);
  }
  var HASH_UNDEFINED$1 = "__lodash_hash_undefined__";
  function hashSet(key, value) {
    var data = this.__data__;
    this.size += this.has(key) ? 0 : 1;
    data[key] = nativeCreate$1 && value === void 0 ? HASH_UNDEFINED$1 : value;
    return this;
  }
  function Hash(entries) {
    var index2 = -1, length = entries == null ? 0 : entries.length;
    this.clear();
    while (++index2 < length) {
      var entry = entries[index2];
      this.set(entry[0], entry[1]);
    }
  }
  Hash.prototype.clear = hashClear;
  Hash.prototype["delete"] = hashDelete;
  Hash.prototype.get = hashGet;
  Hash.prototype.has = hashHas;
  Hash.prototype.set = hashSet;
  function listCacheClear() {
    this.__data__ = [];
    this.size = 0;
  }
  function assocIndexOf(array, key) {
    var length = array.length;
    while (length--) {
      if (eq(array[length][0], key)) {
        return length;
      }
    }
    return -1;
  }
  var arrayProto = Array.prototype;
  var splice = arrayProto.splice;
  function listCacheDelete(key) {
    var data = this.__data__, index2 = assocIndexOf(data, key);
    if (index2 < 0) {
      return false;
    }
    var lastIndex = data.length - 1;
    if (index2 == lastIndex) {
      data.pop();
    } else {
      splice.call(data, index2, 1);
    }
    --this.size;
    return true;
  }
  function listCacheGet(key) {
    var data = this.__data__, index2 = assocIndexOf(data, key);
    return index2 < 0 ? void 0 : data[index2][1];
  }
  function listCacheHas(key) {
    return assocIndexOf(this.__data__, key) > -1;
  }
  function listCacheSet(key, value) {
    var data = this.__data__, index2 = assocIndexOf(data, key);
    if (index2 < 0) {
      ++this.size;
      data.push([key, value]);
    } else {
      data[index2][1] = value;
    }
    return this;
  }
  function ListCache(entries) {
    var index2 = -1, length = entries == null ? 0 : entries.length;
    this.clear();
    while (++index2 < length) {
      var entry = entries[index2];
      this.set(entry[0], entry[1]);
    }
  }
  ListCache.prototype.clear = listCacheClear;
  ListCache.prototype["delete"] = listCacheDelete;
  ListCache.prototype.get = listCacheGet;
  ListCache.prototype.has = listCacheHas;
  ListCache.prototype.set = listCacheSet;
  var Map = getNative(root$1, "Map");
  const Map$1 = Map;
  function mapCacheClear() {
    this.size = 0;
    this.__data__ = {
      "hash": new Hash(),
      "map": new (Map$1 || ListCache)(),
      "string": new Hash()
    };
  }
  function isKeyable(value) {
    var type = typeof value;
    return type == "string" || type == "number" || type == "symbol" || type == "boolean" ? value !== "__proto__" : value === null;
  }
  function getMapData(map, key) {
    var data = map.__data__;
    return isKeyable(key) ? data[typeof key == "string" ? "string" : "hash"] : data.map;
  }
  function mapCacheDelete(key) {
    var result = getMapData(this, key)["delete"](key);
    this.size -= result ? 1 : 0;
    return result;
  }
  function mapCacheGet(key) {
    return getMapData(this, key).get(key);
  }
  function mapCacheHas(key) {
    return getMapData(this, key).has(key);
  }
  function mapCacheSet(key, value) {
    var data = getMapData(this, key), size = data.size;
    data.set(key, value);
    this.size += data.size == size ? 0 : 1;
    return this;
  }
  function MapCache(entries) {
    var index2 = -1, length = entries == null ? 0 : entries.length;
    this.clear();
    while (++index2 < length) {
      var entry = entries[index2];
      this.set(entry[0], entry[1]);
    }
  }
  MapCache.prototype.clear = mapCacheClear;
  MapCache.prototype["delete"] = mapCacheDelete;
  MapCache.prototype.get = mapCacheGet;
  MapCache.prototype.has = mapCacheHas;
  MapCache.prototype.set = mapCacheSet;
  var FUNC_ERROR_TEXT = "Expected a function";
  function memoize(func, resolver) {
    if (typeof func != "function" || resolver != null && typeof resolver != "function") {
      throw new TypeError(FUNC_ERROR_TEXT);
    }
    var memoized = function() {
      var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache;
      if (cache.has(key)) {
        return cache.get(key);
      }
      var result = func.apply(this, args);
      memoized.cache = cache.set(key, result) || cache;
      return result;
    };
    memoized.cache = new (memoize.Cache || MapCache)();
    return memoized;
  }
  memoize.Cache = MapCache;
  var MAX_MEMOIZE_SIZE = 500;
  function memoizeCapped(func) {
    var result = memoize(func, function(key) {
      if (cache.size === MAX_MEMOIZE_SIZE) {
        cache.clear();
      }
      return key;
    });
    var cache = result.cache;
    return result;
  }
  var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
  var reEscapeChar = /\\(\\)?/g;
  var stringToPath = memoizeCapped(function(string) {
    var result = [];
    if (string.charCodeAt(0) === 46) {
      result.push("");
    }
    string.replace(rePropName, function(match2, number, quote, subString) {
      result.push(quote ? subString.replace(reEscapeChar, "$1") : number || match2);
    });
    return result;
  });
  const stringToPath$1 = stringToPath;
  function toString(value) {
    return value == null ? "" : baseToString(value);
  }
  function castPath(value, object) {
    if (isArray$1(value)) {
      return value;
    }
    return isKey(value, object) ? [value] : stringToPath$1(toString(value));
  }
  var INFINITY = 1 / 0;
  function toKey(value) {
    if (typeof value == "string" || isSymbol(value)) {
      return value;
    }
    var result = value + "";
    return result == "0" && 1 / value == -INFINITY ? "-0" : result;
  }
  function baseGet(object, path) {
    path = castPath(path, object);
    var index2 = 0, length = path.length;
    while (object != null && index2 < length) {
      object = object[toKey(path[index2++])];
    }
    return index2 && index2 == length ? object : void 0;
  }
  function get(object, path, defaultValue) {
    var result = object == null ? void 0 : baseGet(object, path);
    return result === void 0 ? defaultValue : result;
  }
  function arrayPush(array, values) {
    var index2 = -1, length = values.length, offset = array.length;
    while (++index2 < length) {
      array[offset + index2] = values[index2];
    }
    return array;
  }
  var getPrototype = overArg(Object.getPrototypeOf, Object);
  const getPrototype$1 = getPrototype;
  var objectTag$2 = "[object Object]";
  var funcProto = Function.prototype, objectProto$6 = Object.prototype;
  var funcToString = funcProto.toString;
  var hasOwnProperty$4 = objectProto$6.hasOwnProperty;
  var objectCtorString = funcToString.call(Object);
  function isPlainObject(value) {
    if (!isObjectLike(value) || baseGetTag(value) != objectTag$2) {
      return false;
    }
    var proto = getPrototype$1(value);
    if (proto === null) {
      return true;
    }
    var Ctor = hasOwnProperty$4.call(proto, "constructor") && proto.constructor;
    return typeof Ctor == "function" && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString;
  }
  var domExcTag = "[object DOMException]", errorTag$1 = "[object Error]";
  function isError(value) {
    if (!isObjectLike(value)) {
      return false;
    }
    var tag = baseGetTag(value);
    return tag == errorTag$1 || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject(value);
  }
  var attempt = baseRest(function(func, args) {
    try {
      return apply(func, void 0, args);
    } catch (e) {
      return isError(e) ? e : new Error(e);
    }
  });
  const attempt$1 = attempt;
  function baseSlice(array, start, end) {
    var index2 = -1, length = array.length;
    if (start < 0) {
      start = -start > length ? 0 : length + start;
    }
    end = end > length ? length : end;
    if (end < 0) {
      end += length;
    }
    length = start > end ? 0 : end - start >>> 0;
    start >>>= 0;
    var result = Array(length);
    while (++index2 < length) {
      result[index2] = array[index2 + start];
    }
    return result;
  }
  function castSlice(array, start, end) {
    var length = array.length;
    end = end === void 0 ? length : end;
    return !start && end >= length ? array : baseSlice(array, start, end);
  }
  var rsAstralRange$2 = "\\ud800-\\udfff", rsComboMarksRange$3 = "\\u0300-\\u036f", reComboHalfMarksRange$3 = "\\ufe20-\\ufe2f", rsComboSymbolsRange$3 = "\\u20d0-\\u20ff", rsComboRange$3 = rsComboMarksRange$3 + reComboHalfMarksRange$3 + rsComboSymbolsRange$3, rsVarRange$2 = "\\ufe0e\\ufe0f";
  var rsZWJ$2 = "\\u200d";
  var reHasUnicode = RegExp("[" + rsZWJ$2 + rsAstralRange$2 + rsComboRange$3 + rsVarRange$2 + "]");
  function hasUnicode(string) {
    return reHasUnicode.test(string);
  }
  function asciiToArray(string) {
    return string.split("");
  }
  var rsAstralRange$1 = "\\ud800-\\udfff", rsComboMarksRange$2 = "\\u0300-\\u036f", reComboHalfMarksRange$2 = "\\ufe20-\\ufe2f", rsComboSymbolsRange$2 = "\\u20d0-\\u20ff", rsComboRange$2 = rsComboMarksRange$2 + reComboHalfMarksRange$2 + rsComboSymbolsRange$2, rsVarRange$1 = "\\ufe0e\\ufe0f";
  var rsAstral = "[" + rsAstralRange$1 + "]", rsCombo$2 = "[" + rsComboRange$2 + "]", rsFitz$1 = "\\ud83c[\\udffb-\\udfff]", rsModifier$1 = "(?:" + rsCombo$2 + "|" + rsFitz$1 + ")", rsNonAstral$1 = "[^" + rsAstralRange$1 + "]", rsRegional$1 = "(?:\\ud83c[\\udde6-\\uddff]){2}", rsSurrPair$1 = "[\\ud800-\\udbff][\\udc00-\\udfff]", rsZWJ$1 = "\\u200d";
  var reOptMod$1 = rsModifier$1 + "?", rsOptVar$1 = "[" + rsVarRange$1 + "]?", rsOptJoin$1 = "(?:" + rsZWJ$1 + "(?:" + [rsNonAstral$1, rsRegional$1, rsSurrPair$1].join("|") + ")" + rsOptVar$1 + reOptMod$1 + ")*", rsSeq$1 = rsOptVar$1 + reOptMod$1 + rsOptJoin$1, rsSymbol = "(?:" + [rsNonAstral$1 + rsCombo$2 + "?", rsCombo$2, rsRegional$1, rsSurrPair$1, rsAstral].join("|") + ")";
  var reUnicode = RegExp(rsFitz$1 + "(?=" + rsFitz$1 + ")|" + rsSymbol + rsSeq$1, "g");
  function unicodeToArray(string) {
    return string.match(reUnicode) || [];
  }
  function stringToArray(string) {
    return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string);
  }
  function createCaseFirst(methodName) {
    return function(string) {
      string = toString(string);
      var strSymbols = hasUnicode(string) ? stringToArray(string) : void 0;
      var chr = strSymbols ? strSymbols[0] : string.charAt(0);
      var trailing = strSymbols ? castSlice(strSymbols, 1).join("") : string.slice(1);
      return chr[methodName]() + trailing;
    };
  }
  var upperFirst = createCaseFirst("toUpperCase");
  const upperFirst$1 = upperFirst;
  function capitalize(string) {
    return upperFirst$1(toString(string).toLowerCase());
  }
  function arrayReduce(array, iteratee, accumulator, initAccum) {
    var index2 = -1, length = array == null ? 0 : array.length;
    if (initAccum && length) {
      accumulator = array[++index2];
    }
    while (++index2 < length) {
      accumulator = iteratee(accumulator, array[index2], index2, array);
    }
    return accumulator;
  }
  function basePropertyOf(object) {
    return function(key) {
      return object == null ? void 0 : object[key];
    };
  }
  var deburredLetters = {
    "\xC0": "A",
    "\xC1": "A",
    "\xC2": "A",
    "\xC3": "A",
    "\xC4": "A",
    "\xC5": "A",
    "\xE0": "a",
    "\xE1": "a",
    "\xE2": "a",
    "\xE3": "a",
    "\xE4": "a",
    "\xE5": "a",
    "\xC7": "C",
    "\xE7": "c",
    "\xD0": "D",
    "\xF0": "d",
    "\xC8": "E",
    "\xC9": "E",
    "\xCA": "E",
    "\xCB": "E",
    "\xE8": "e",
    "\xE9": "e",
    "\xEA": "e",
    "\xEB": "e",
    "\xCC": "I",
    "\xCD": "I",
    "\xCE": "I",
    "\xCF": "I",
    "\xEC": "i",
    "\xED": "i",
    "\xEE": "i",
    "\xEF": "i",
    "\xD1": "N",
    "\xF1": "n",
    "\xD2": "O",
    "\xD3": "O",
    "\xD4": "O",
    "\xD5": "O",
    "\xD6": "O",
    "\xD8": "O",
    "\xF2": "o",
    "\xF3": "o",
    "\xF4": "o",
    "\xF5": "o",
    "\xF6": "o",
    "\xF8": "o",
    "\xD9": "U",
    "\xDA": "U",
    "\xDB": "U",
    "\xDC": "U",
    "\xF9": "u",
    "\xFA": "u",
    "\xFB": "u",
    "\xFC": "u",
    "\xDD": "Y",
    "\xFD": "y",
    "\xFF": "y",
    "\xC6": "Ae",
    "\xE6": "ae",
    "\xDE": "Th",
    "\xFE": "th",
    "\xDF": "ss",
    "\u0100": "A",
    "\u0102": "A",
    "\u0104": "A",
    "\u0101": "a",
    "\u0103": "a",
    "\u0105": "a",
    "\u0106": "C",
    "\u0108": "C",
    "\u010A": "C",
    "\u010C": "C",
    "\u0107": "c",
    "\u0109": "c",
    "\u010B": "c",
    "\u010D": "c",
    "\u010E": "D",
    "\u0110": "D",
    "\u010F": "d",
    "\u0111": "d",
    "\u0112": "E",
    "\u0114": "E",
    "\u0116": "E",
    "\u0118": "E",
    "\u011A": "E",
    "\u0113": "e",
    "\u0115": "e",
    "\u0117": "e",
    "\u0119": "e",
    "\u011B": "e",
    "\u011C": "G",
    "\u011E": "G",
    "\u0120": "G",
    "\u0122": "G",
    "\u011D": "g",
    "\u011F": "g",
    "\u0121": "g",
    "\u0123": "g",
    "\u0124": "H",
    "\u0126": "H",
    "\u0125": "h",
    "\u0127": "h",
    "\u0128": "I",
    "\u012A": "I",
    "\u012C": "I",
    "\u012E": "I",
    "\u0130": "I",
    "\u0129": "i",
    "\u012B": "i",
    "\u012D": "i",
    "\u012F": "i",
    "\u0131": "i",
    "\u0134": "J",
    "\u0135": "j",
    "\u0136": "K",
    "\u0137": "k",
    "\u0138": "k",
    "\u0139": "L",
    "\u013B": "L",
    "\u013D": "L",
    "\u013F": "L",
    "\u0141": "L",
    "\u013A": "l",
    "\u013C": "l",
    "\u013E": "l",
    "\u0140": "l",
    "\u0142": "l",
    "\u0143": "N",
    "\u0145": "N",
    "\u0147": "N",
    "\u014A": "N",
    "\u0144": "n",
    "\u0146": "n",
    "\u0148": "n",
    "\u014B": "n",
    "\u014C": "O",
    "\u014E": "O",
    "\u0150": "O",
    "\u014D": "o",
    "\u014F": "o",
    "\u0151": "o",
    "\u0154": "R",
    "\u0156": "R",
    "\u0158": "R",
    "\u0155": "r",
    "\u0157": "r",
    "\u0159": "r",
    "\u015A": "S",
    "\u015C": "S",
    "\u015E": "S",
    "\u0160": "S",
    "\u015B": "s",
    "\u015D": "s",
    "\u015F": "s",
    "\u0161": "s",
    "\u0162": "T",
    "\u0164": "T",
    "\u0166": "T",
    "\u0163": "t",
    "\u0165": "t",
    "\u0167": "t",
    "\u0168": "U",
    "\u016A": "U",
    "\u016C": "U",
    "\u016E": "U",
    "\u0170": "U",
    "\u0172": "U",
    "\u0169": "u",
    "\u016B": "u",
    "\u016D": "u",
    "\u016F": "u",
    "\u0171": "u",
    "\u0173": "u",
    "\u0174": "W",
    "\u0175": "w",
    "\u0176": "Y",
    "\u0177": "y",
    "\u0178": "Y",
    "\u0179": "Z",
    "\u017B": "Z",
    "\u017D": "Z",
    "\u017A": "z",
    "\u017C": "z",
    "\u017E": "z",
    "\u0132": "IJ",
    "\u0133": "ij",
    "\u0152": "Oe",
    "\u0153": "oe",
    "\u0149": "'n",
    "\u017F": "s"
  };
  var deburrLetter = basePropertyOf(deburredLetters);
  const deburrLetter$1 = deburrLetter;
  var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
  var rsComboMarksRange$1 = "\\u0300-\\u036f", reComboHalfMarksRange$1 = "\\ufe20-\\ufe2f", rsComboSymbolsRange$1 = "\\u20d0-\\u20ff", rsComboRange$1 = rsComboMarksRange$1 + reComboHalfMarksRange$1 + rsComboSymbolsRange$1;
  var rsCombo$1 = "[" + rsComboRange$1 + "]";
  var reComboMark = RegExp(rsCombo$1, "g");
  function deburr(string) {
    string = toString(string);
    return string && string.replace(reLatin, deburrLetter$1).replace(reComboMark, "");
  }
  var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
  function asciiWords(string) {
    return string.match(reAsciiWord) || [];
  }
  var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
  function hasUnicodeWord(string) {
    return reHasUnicodeWord.test(string);
  }
  var rsAstralRange = "\\ud800-\\udfff", rsComboMarksRange = "\\u0300-\\u036f", reComboHalfMarksRange = "\\ufe20-\\ufe2f", rsComboSymbolsRange = "\\u20d0-\\u20ff", rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, rsDingbatRange = "\\u2700-\\u27bf", rsLowerRange = "a-z\\xdf-\\xf6\\xf8-\\xff", rsMathOpRange = "\\xac\\xb1\\xd7\\xf7", rsNonCharRange = "\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf", rsPunctuationRange = "\\u2000-\\u206f", rsSpaceRange = " \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000", rsUpperRange = "A-Z\\xc0-\\xd6\\xd8-\\xde", rsVarRange = "\\ufe0e\\ufe0f", rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
  var rsApos$1 = "['\u2019]", rsBreak = "[" + rsBreakRange + "]", rsCombo = "[" + rsComboRange + "]", rsDigits = "\\d+", rsDingbat = "[" + rsDingbatRange + "]", rsLower = "[" + rsLowerRange + "]", rsMisc = "[^" + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + "]", rsFitz = "\\ud83c[\\udffb-\\udfff]", rsModifier = "(?:" + rsCombo + "|" + rsFitz + ")", rsNonAstral = "[^" + rsAstralRange + "]", rsRegional = "(?:\\ud83c[\\udde6-\\uddff]){2}", rsSurrPair = "[\\ud800-\\udbff][\\udc00-\\udfff]", rsUpper = "[" + rsUpperRange + "]", rsZWJ = "\\u200d";
  var rsMiscLower = "(?:" + rsLower + "|" + rsMisc + ")", rsMiscUpper = "(?:" + rsUpper + "|" + rsMisc + ")", rsOptContrLower = "(?:" + rsApos$1 + "(?:d|ll|m|re|s|t|ve))?", rsOptContrUpper = "(?:" + rsApos$1 + "(?:D|LL|M|RE|S|T|VE))?", reOptMod = rsModifier + "?", rsOptVar = "[" + rsVarRange + "]?", rsOptJoin = "(?:" + rsZWJ + "(?:" + [rsNonAstral, rsRegional, rsSurrPair].join("|") + ")" + rsOptVar + reOptMod + ")*", rsOrdLower = "\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])", rsOrdUpper = "\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])", rsSeq = rsOptVar + reOptMod + rsOptJoin, rsEmoji = "(?:" + [rsDingbat, rsRegional, rsSurrPair].join("|") + ")" + rsSeq;
  var reUnicodeWord = RegExp([
    rsUpper + "?" + rsLower + "+" + rsOptContrLower + "(?=" + [rsBreak, rsUpper, "$"].join("|") + ")",
    rsMiscUpper + "+" + rsOptContrUpper + "(?=" + [rsBreak, rsUpper + rsMiscLower, "$"].join("|") + ")",
    rsUpper + "?" + rsMiscLower + "+" + rsOptContrLower,
    rsUpper + "+" + rsOptContrUpper,
    rsOrdUpper,
    rsOrdLower,
    rsDigits,
    rsEmoji
  ].join("|"), "g");
  function unicodeWords(string) {
    return string.match(reUnicodeWord) || [];
  }
  function words(string, pattern, guard) {
    string = toString(string);
    pattern = guard ? void 0 : pattern;
    if (pattern === void 0) {
      return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string);
    }
    return string.match(pattern) || [];
  }
  var rsApos = "['\u2019]";
  var reApos = RegExp(rsApos, "g");
  function createCompounder(callback) {
    return function(string) {
      return arrayReduce(words(deburr(string).replace(reApos, "")), callback, "");
    };
  }
  var camelCase = createCompounder(function(result, word, index2) {
    word = word.toLowerCase();
    return result + (index2 ? capitalize(word) : word);
  });
  const camelCase$1 = camelCase;
  function stackClear() {
    this.__data__ = new ListCache();
    this.size = 0;
  }
  function stackDelete(key) {
    var data = this.__data__, result = data["delete"](key);
    this.size = data.size;
    return result;
  }
  function stackGet(key) {
    return this.__data__.get(key);
  }
  function stackHas(key) {
    return this.__data__.has(key);
  }
  var LARGE_ARRAY_SIZE = 200;
  function stackSet(key, value) {
    var data = this.__data__;
    if (data instanceof ListCache) {
      var pairs = data.__data__;
      if (!Map$1 || pairs.length < LARGE_ARRAY_SIZE - 1) {
        pairs.push([key, value]);
        this.size = ++data.size;
        return this;
      }
      data = this.__data__ = new MapCache(pairs);
    }
    data.set(key, value);
    this.size = data.size;
    return this;
  }
  function Stack(entries) {
    var data = this.__data__ = new ListCache(entries);
    this.size = data.size;
  }
  Stack.prototype.clear = stackClear;
  Stack.prototype["delete"] = stackDelete;
  Stack.prototype.get = stackGet;
  Stack.prototype.has = stackHas;
  Stack.prototype.set = stackSet;
  function arrayFilter(array, predicate) {
    var index2 = -1, length = array == null ? 0 : array.length, resIndex = 0, result = [];
    while (++index2 < length) {
      var value = array[index2];
      if (predicate(value, index2, array)) {
        result[resIndex++] = value;
      }
    }
    return result;
  }
  function stubArray() {
    return [];
  }
  var objectProto$5 = Object.prototype;
  var propertyIsEnumerable = objectProto$5.propertyIsEnumerable;
  var nativeGetSymbols = Object.getOwnPropertySymbols;
  var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
    if (object == null) {
      return [];
    }
    object = Object(object);
    return arrayFilter(nativeGetSymbols(object), function(symbol) {
      return propertyIsEnumerable.call(object, symbol);
    });
  };
  const getSymbols$1 = getSymbols;
  function baseGetAllKeys(object, keysFunc, symbolsFunc) {
    var result = keysFunc(object);
    return isArray$1(object) ? result : arrayPush(result, symbolsFunc(object));
  }
  function getAllKeys(object) {
    return baseGetAllKeys(object, keys, getSymbols$1);
  }
  var DataView = getNative(root$1, "DataView");
  const DataView$1 = DataView;
  var Promise$1 = getNative(root$1, "Promise");
  const Promise$2 = Promise$1;
  var Set = getNative(root$1, "Set");
  const Set$1 = Set;
  var mapTag$1 = "[object Map]", objectTag$1 = "[object Object]", promiseTag = "[object Promise]", setTag$1 = "[object Set]", weakMapTag = "[object WeakMap]";
  var dataViewTag$1 = "[object DataView]";
  var dataViewCtorString = toSource(DataView$1), mapCtorString = toSource(Map$1), promiseCtorString = toSource(Promise$2), setCtorString = toSource(Set$1), weakMapCtorString = toSource(WeakMap$1);
  var getTag = baseGetTag;
  if (DataView$1 && getTag(new DataView$1(new ArrayBuffer(1))) != dataViewTag$1 || Map$1 && getTag(new Map$1()) != mapTag$1 || Promise$2 && getTag(Promise$2.resolve()) != promiseTag || Set$1 && getTag(new Set$1()) != setTag$1 || WeakMap$1 && getTag(new WeakMap$1()) != weakMapTag) {
    getTag = function(value) {
      var result = baseGetTag(value), Ctor = result == objectTag$1 ? value.constructor : void 0, ctorString = Ctor ? toSource(Ctor) : "";
      if (ctorString) {
        switch (ctorString) {
          case dataViewCtorString:
            return dataViewTag$1;
          case mapCtorString:
            return mapTag$1;
          case promiseCtorString:
            return promiseTag;
          case setCtorString:
            return setTag$1;
          case weakMapCtorString:
            return weakMapTag;
        }
      }
      return result;
    };
  }
  const getTag$1 = getTag;
  var Uint8Array$1 = root$1.Uint8Array;
  const Uint8Array$2 = Uint8Array$1;
  var HASH_UNDEFINED = "__lodash_hash_undefined__";
  function setCacheAdd(value) {
    this.__data__.set(value, HASH_UNDEFINED);
    return this;
  }
  function setCacheHas(value) {
    return this.__data__.has(value);
  }
  function SetCache(values) {
    var index2 = -1, length = values == null ? 0 : values.length;
    this.__data__ = new MapCache();
    while (++index2 < length) {
      this.add(values[index2]);
    }
  }
  SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
  SetCache.prototype.has = setCacheHas;
  function arraySome(array, predicate) {
    var index2 = -1, length = array == null ? 0 : array.length;
    while (++index2 < length) {
      if (predicate(array[index2], index2, array)) {
        return true;
      }
    }
    return false;
  }
  function cacheHas(cache, key) {
    return cache.has(key);
  }
  var COMPARE_PARTIAL_FLAG$5 = 1, COMPARE_UNORDERED_FLAG$3 = 2;
  function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
    var isPartial = bitmask & COMPARE_PARTIAL_FLAG$5, arrLength = array.length, othLength = other.length;
    if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
      return false;
    }
    var arrStacked = stack.get(array);
    var othStacked = stack.get(other);
    if (arrStacked && othStacked) {
      return arrStacked == other && othStacked == array;
    }
    var index2 = -1, result = true, seen = bitmask & COMPARE_UNORDERED_FLAG$3 ? new SetCache() : void 0;
    stack.set(array, other);
    stack.set(other, array);
    while (++index2 < arrLength) {
      var arrValue = array[index2], othValue = other[index2];
      if (customizer) {
        var compared = isPartial ? customizer(othValue, arrValue, index2, other, array, stack) : customizer(arrValue, othValue, index2, array, other, stack);
      }
      if (compared !== void 0) {
        if (compared) {
          continue;
        }
        result = false;
        break;
      }
      if (seen) {
        if (!arraySome(other, function(othValue2, othIndex) {
          if (!cacheHas(seen, othIndex) && (arrValue === othValue2 || equalFunc(arrValue, othValue2, bitmask, customizer, stack))) {
            return seen.push(othIndex);
          }
        })) {
          result = false;
          break;
        }
      } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
        result = false;
        break;
      }
    }
    stack["delete"](array);
    stack["delete"](other);
    return result;
  }
  function mapToArray(map) {
    var index2 = -1, result = Array(map.size);
    map.forEach(function(value, key) {
      result[++index2] = [key, value];
    });
    return result;
  }
  function setToArray(set) {
    var index2 = -1, result = Array(set.size);
    set.forEach(function(value) {
      result[++index2] = value;
    });
    return result;
  }
  var COMPARE_PARTIAL_FLAG$4 = 1, COMPARE_UNORDERED_FLAG$2 = 2;
  var boolTag = "[object Boolean]", dateTag = "[object Date]", errorTag = "[object Error]", mapTag = "[object Map]", numberTag = "[object Number]", regexpTag = "[object RegExp]", setTag = "[object Set]", stringTag = "[object String]", symbolTag = "[object Symbol]";
  var arrayBufferTag = "[object ArrayBuffer]", dataViewTag = "[object DataView]";
  var symbolProto = Symbol$2 ? Symbol$2.prototype : void 0, symbolValueOf = symbolProto ? symbolProto.valueOf : void 0;
  function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
    switch (tag) {
      case dataViewTag:
        if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) {
          return false;
        }
        object = object.buffer;
        other = other.buffer;
      case arrayBufferTag:
        if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array$2(object), new Uint8Array$2(other))) {
          return false;
        }
        return true;
      case boolTag:
      case dateTag:
      case numberTag:
        return eq(+object, +other);
      case errorTag:
        return object.name == other.name && object.message == other.message;
      case regexpTag:
      case stringTag:
        return object == other + "";
      case mapTag:
        var convert = mapToArray;
      case setTag:
        var isPartial = bitmask & COMPARE_PARTIAL_FLAG$4;
        convert || (convert = setToArray);
        if (object.size != other.size && !isPartial) {
          return false;
        }
        var stacked = stack.get(object);
        if (stacked) {
          return stacked == other;
        }
        bitmask |= COMPARE_UNORDERED_FLAG$2;
        stack.set(object, other);
        var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
        stack["delete"](object);
        return result;
      case symbolTag:
        if (symbolValueOf) {
          return symbolValueOf.call(object) == symbolValueOf.call(other);
        }
    }
    return false;
  }
  var COMPARE_PARTIAL_FLAG$3 = 1;
  var objectProto$4 = Object.prototype;
  var hasOwnProperty$3 = objectProto$4.hasOwnProperty;
  function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
    var isPartial = bitmask & COMPARE_PARTIAL_FLAG$3, objProps = getAllKeys(object), objLength = objProps.length, othProps = getAllKeys(other), othLength = othProps.length;
    if (objLength != othLength && !isPartial) {
      return false;
    }
    var index2 = objLength;
    while (index2--) {
      var key = objProps[index2];
      if (!(isPartial ? key in other : hasOwnProperty$3.call(other, key))) {
        return false;
      }
    }
    var objStacked = stack.get(object);
    var othStacked = stack.get(other);
    if (objStacked && othStacked) {
      return objStacked == other && othStacked == object;
    }
    var result = true;
    stack.set(object, other);
    stack.set(other, object);
    var skipCtor = isPartial;
    while (++index2 < objLength) {
      key = objProps[index2];
      var objValue = object[key], othValue = other[key];
      if (customizer) {
        var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack);
      }
      if (!(compared === void 0 ? objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack) : compared)) {
        result = false;
        break;
      }
      skipCtor || (skipCtor = key == "constructor");
    }
    if (result && !skipCtor) {
      var objCtor = object.constructor, othCtor = other.constructor;
      if (objCtor != othCtor && ("constructor" in object && "constructor" in other) && !(typeof objCtor == "function" && objCtor instanceof objCtor && typeof othCtor == "function" && othCtor instanceof othCtor)) {
        result = false;
      }
    }
    stack["delete"](object);
    stack["delete"](other);
    return result;
  }
  var COMPARE_PARTIAL_FLAG$2 = 1;
  var argsTag = "[object Arguments]", arrayTag = "[object Array]", objectTag = "[object Object]";
  var objectProto$3 = Object.prototype;
  var hasOwnProperty$2 = objectProto$3.hasOwnProperty;
  function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
    var objIsArr = isArray$1(object), othIsArr = isArray$1(other), objTag = objIsArr ? arrayTag : getTag$1(object), othTag = othIsArr ? arrayTag : getTag$1(other);
    objTag = objTag == argsTag ? objectTag : objTag;
    othTag = othTag == argsTag ? objectTag : othTag;
    var objIsObj = objTag == objectTag, othIsObj = othTag == objectTag, isSameTag = objTag == othTag;
    if (isSameTag && isBuffer$1(object)) {
      if (!isBuffer$1(other)) {
        return false;
      }
      objIsArr = true;
      objIsObj = false;
    }
    if (isSameTag && !objIsObj) {
      stack || (stack = new Stack());
      return objIsArr || isTypedArray$1(object) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
    }
    if (!(bitmask & COMPARE_PARTIAL_FLAG$2)) {
      var objIsWrapped = objIsObj && hasOwnProperty$2.call(object, "__wrapped__"), othIsWrapped = othIsObj && hasOwnProperty$2.call(other, "__wrapped__");
      if (objIsWrapped || othIsWrapped) {
        var objUnwrapped = objIsWrapped ? object.value() : object, othUnwrapped = othIsWrapped ? other.value() : other;
        stack || (stack = new Stack());
        return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
      }
    }
    if (!isSameTag) {
      return false;
    }
    stack || (stack = new Stack());
    return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
  }
  function baseIsEqual(value, other, bitmask, customizer, stack) {
    if (value === other) {
      return true;
    }
    if (value == null || other == null || !isObjectLike(value) && !isObjectLike(other)) {
      return value !== value && other !== other;
    }
    return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
  }
  var COMPARE_PARTIAL_FLAG$1 = 1, COMPARE_UNORDERED_FLAG$1 = 2;
  function baseIsMatch(object, source, matchData, customizer) {
    var index2 = matchData.length, length = index2, noCustomizer = !customizer;
    if (object == null) {
      return !length;
    }
    object = Object(object);
    while (index2--) {
      var data = matchData[index2];
      if (noCustomizer && data[2] ? data[1] !== object[data[0]] : !(data[0] in object)) {
        return false;
      }
    }
    while (++index2 < length) {
      data = matchData[index2];
      var key = data[0], objValue = object[key], srcValue = data[1];
      if (noCustomizer && data[2]) {
        if (objValue === void 0 && !(key in object)) {
          return false;
        }
      } else {
        var stack = new Stack();
        if (customizer) {
          var result = customizer(objValue, srcValue, key, object, source, stack);
        }
        if (!(result === void 0 ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG$1 | COMPARE_UNORDERED_FLAG$1, customizer, stack) : result)) {
          return false;
        }
      }
    }
    return true;
  }
  function isStrictComparable(value) {
    return value === value && !isObject(value);
  }
  function getMatchData(object) {
    var result = keys(object), length = result.length;
    while (length--) {
      var key = result[length], value = object[key];
      result[length] = [key, value, isStrictComparable(value)];
    }
    return result;
  }
  function matchesStrictComparable(key, srcValue) {
    return function(object) {
      if (object == null) {
        return false;
      }
      return object[key] === srcValue && (srcValue !== void 0 || key in Object(object));
    };
  }
  function baseMatches(source) {
    var matchData = getMatchData(source);
    if (matchData.length == 1 && matchData[0][2]) {
      return matchesStrictComparable(matchData[0][0], matchData[0][1]);
    }
    return function(object) {
      return object === source || baseIsMatch(object, source, matchData);
    };
  }
  function baseHasIn(object, key) {
    return object != null && key in Object(object);
  }
  function hasPath(object, path, hasFunc) {
    path = castPath(path, object);
    var index2 = -1, length = path.length, result = false;
    while (++index2 < length) {
      var key = toKey(path[index2]);
      if (!(result = object != null && hasFunc(object, key))) {
        break;
      }
      object = object[key];
    }
    if (result || ++index2 != length) {
      return result;
    }
    length = object == null ? 0 : object.length;
    return !!length && isLength(length) && isIndex(key, length) && (isArray$1(object) || isArguments$1(object));
  }
  function hasIn(object, path) {
    return object != null && hasPath(object, path, baseHasIn);
  }
  var COMPARE_PARTIAL_FLAG = 1, COMPARE_UNORDERED_FLAG = 2;
  function baseMatchesProperty(path, srcValue) {
    if (isKey(path) && isStrictComparable(srcValue)) {
      return matchesStrictComparable(toKey(path), srcValue);
    }
    return function(object) {
      var objValue = get(object, path);
      return objValue === void 0 && objValue === srcValue ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
    };
  }
  function baseProperty(key) {
    return function(object) {
      return object == null ? void 0 : object[key];
    };
  }
  function basePropertyDeep(path) {
    return function(object) {
      return baseGet(object, path);
    };
  }
  function property(path) {
    return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
  }
  function baseIteratee(value) {
    if (typeof value == "function") {
      return value;
    }
    if (value == null) {
      return identity;
    }
    if (typeof value == "object") {
      return isArray$1(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value);
    }
    return property(value);
  }
  function createBaseFor(fromRight) {
    return function(object, iteratee, keysFunc) {
      var index2 = -1, iterable = Object(object), props = keysFunc(object), length = props.length;
      while (length--) {
        var key = props[fromRight ? length : ++index2];
        if (iteratee(iterable[key], key, iterable) === false) {
          break;
        }
      }
      return object;
    };
  }
  var baseFor = createBaseFor();
  const baseFor$1 = baseFor;
  function baseForOwn(object, iteratee) {
    return object && baseFor$1(object, iteratee, keys);
  }
  function createBaseEach(eachFunc, fromRight) {
    return function(collection, iteratee) {
      if (collection == null) {
        return collection;
      }
      if (!isArrayLike(collection)) {
        return eachFunc(collection, iteratee);
      }
      var length = collection.length, index2 = fromRight ? length : -1, iterable = Object(collection);
      while (fromRight ? index2-- : ++index2 < length) {
        if (iteratee(iterable[index2], index2, iterable) === false) {
          break;
        }
      }
      return collection;
    };
  }
  var baseEach = createBaseEach(baseForOwn);
  const baseEach$1 = baseEach;
  function last(array) {
    var length = array == null ? 0 : array.length;
    return length ? array[length - 1] : void 0;
  }
  function castFunction(value) {
    return typeof value == "function" ? value : identity;
  }
  function forEach(collection, iteratee) {
    var func = isArray$1(collection) ? arrayEach : baseEach$1;
    return func(collection, castFunction(iteratee));
  }
  var htmlEscapes = {
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#39;"
  };
  var escapeHtmlChar = basePropertyOf(htmlEscapes);
  const escapeHtmlChar$1 = escapeHtmlChar;
  var reUnescapedHtml = /[&<>"']/g, reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
  function escape(string) {
    string = toString(string);
    return string && reHasUnescapedHtml.test(string) ? string.replace(reUnescapedHtml, escapeHtmlChar$1) : string;
  }
  function baseValues(object, props) {
    return arrayMap(props, function(key) {
      return object[key];
    });
  }
  function baseInverter(object, setter, iteratee, accumulator) {
    baseForOwn(object, function(value, key, object2) {
      setter(accumulator, iteratee(value), key, object2);
    });
    return accumulator;
  }
  function createInverter(setter, toIteratee) {
    return function(object, iteratee) {
      return baseInverter(object, setter, toIteratee(iteratee), {});
    };
  }
  var objectProto$2 = Object.prototype;
  var nativeObjectToString = objectProto$2.toString;
  var invert = createInverter(function(result, value, key) {
    if (value != null && typeof value.toString != "function") {
      value = nativeObjectToString.call(value);
    }
    result[value] = key;
  }, constant(identity));
  const invert$1 = invert;
  var kebabCase = createCompounder(function(result, word, index2) {
    return result + (index2 ? "-" : "") + word.toLowerCase();
  });
  const kebabCase$1 = kebabCase;
  function mapValues(object, iteratee) {
    var result = {};
    iteratee = baseIteratee(iteratee);
    baseForOwn(object, function(value, key, object2) {
      baseAssignValue(result, key, iteratee(value, key, object2));
    });
    return result;
  }
  var objectProto$1 = Object.prototype;
  var hasOwnProperty$1 = objectProto$1.hasOwnProperty;
  function customDefaultsAssignIn(objValue, srcValue, key, object) {
    if (objValue === void 0 || eq(objValue, objectProto$1[key]) && !hasOwnProperty$1.call(object, key)) {
      return srcValue;
    }
    return objValue;
  }
  var stringEscapes = {
    "\\": "\\",
    "'": "'",
    "\n": "n",
    "\r": "r",
    "\u2028": "u2028",
    "\u2029": "u2029"
  };
  function escapeStringChar(chr) {
    return "\\" + stringEscapes[chr];
  }
  var reInterpolate = /<%=([\s\S]+?)%>/g;
  const reInterpolate$1 = reInterpolate;
  var reEscape = /<%-([\s\S]+?)%>/g;
  const reEscape$1 = reEscape;
  var reEvaluate = /<%([\s\S]+?)%>/g;
  const reEvaluate$1 = reEvaluate;
  var templateSettings = {
    "escape": reEscape$1,
    "evaluate": reEvaluate$1,
    "interpolate": reInterpolate$1,
    "variable": "",
    "imports": {
      "_": { "escape": escape }
    }
  };
  const templateSettings$1 = templateSettings;
  var INVALID_TEMPL_VAR_ERROR_TEXT = "Invalid `variable` option passed into `_.template`";
  var reEmptyStringLeading = /\b__p \+= '';/g, reEmptyStringMiddle = /\b(__p \+=) '' \+/g, reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
  var reForbiddenIdentifierChars = /[()=,{}\[\]\/\s]/;
  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
  var reNoMatch = /($^)/;
  var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
  var objectProto = Object.prototype;
  var hasOwnProperty = objectProto.hasOwnProperty;
  function template(string, options, guard) {
    var settings2 = templateSettings$1.imports._.templateSettings || templateSettings$1;
    if (guard && isIterateeCall(string, options, guard)) {
      options = void 0;
    }
    string = toString(string);
    options = extendWith({}, options, settings2, customDefaultsAssignIn);
    var imports = extendWith({}, options.imports, settings2.imports, customDefaultsAssignIn), importsKeys = keys(imports), importsValues = baseValues(imports, importsKeys);
    var isEscaping, isEvaluating, index2 = 0, interpolate = options.interpolate || reNoMatch, source = "__p += '";
    var reDelimiters = RegExp(
      (options.escape || reNoMatch).source + "|" + interpolate.source + "|" + (interpolate === reInterpolate$1 ? reEsTemplate : reNoMatch).source + "|" + (options.evaluate || reNoMatch).source + "|$",
      "g"
    );
    var sourceURL = hasOwnProperty.call(options, "sourceURL") ? "//# sourceURL=" + (options.sourceURL + "").replace(/\s/g, " ") + "\n" : "";
    string.replace(reDelimiters, function(match2, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
      interpolateValue || (interpolateValue = esTemplateValue);
      source += string.slice(index2, offset).replace(reUnescapedString, escapeStringChar);
      if (escapeValue) {
        isEscaping = true;
        source += "' +\n__e(" + escapeValue + ") +\n'";
      }
      if (evaluateValue) {
        isEvaluating = true;
        source += "';\n" + evaluateValue + ";\n__p += '";
      }
      if (interpolateValue) {
        source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
      }
      index2 = offset + match2.length;
      return match2;
    });
    source += "';\n";
    var variable = hasOwnProperty.call(options, "variable") && options.variable;
    if (!variable) {
      source = "with (obj) {\n" + source + "\n}\n";
    } else if (reForbiddenIdentifierChars.test(variable)) {
      throw new Error(INVALID_TEMPL_VAR_ERROR_TEXT);
    }
    source = (isEvaluating ? source.replace(reEmptyStringLeading, "") : source).replace(reEmptyStringMiddle, "$1").replace(reEmptyStringTrailing, "$1;");
    source = "function(" + (variable || "obj") + ") {\n" + (variable ? "" : "obj || (obj = {});\n") + "var __t, __p = ''" + (isEscaping ? ", __e = _.escape" : "") + (isEvaluating ? ", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n" : ";\n") + source + "return __p\n}";
    var result = attempt$1(function() {
      return Function(importsKeys, sourceURL + "return " + source).apply(void 0, importsValues);
    });
    result.source = source;
    if (isError(result)) {
      throw result;
    }
    return result;
  }
  var __spreadArray = globalThis && globalThis.__spreadArray || function(to, from, pack) {
    if (pack || arguments.length === 2)
      for (var i = 0, l2 = from.length, ar; i < l2; i++) {
        if (ar || !(i in from)) {
          if (!ar)
            ar = Array.prototype.slice.call(from, 0, i);
          ar[i] = from[i];
        }
      }
    return to.concat(ar || Array.prototype.slice.call(from));
  };
  var BrowserInfo = function() {
    function BrowserInfo2(name, version, os) {
      this.name = name;
      this.version = version;
      this.os = os;
      this.type = "browser";
    }
    return BrowserInfo2;
  }();
  var NodeInfo = function() {
    function NodeInfo2(version) {
      this.version = version;
      this.type = "node";
      this.name = "node";
      this.os = process.platform;
    }
    return NodeInfo2;
  }();
  var SearchBotDeviceInfo = function() {
    function SearchBotDeviceInfo2(name, version, os, bot) {
      this.name = name;
      this.version = version;
      this.os = os;
      this.bot = bot;
      this.type = "bot-device";
    }
    return SearchBotDeviceInfo2;
  }();
  var BotInfo = function() {
    function BotInfo2() {
      this.type = "bot";
      this.bot = true;
      this.name = "bot";
      this.version = null;
      this.os = null;
    }
    return BotInfo2;
  }();
  var ReactNativeInfo = function() {
    function ReactNativeInfo2() {
      this.type = "react-native";
      this.name = "react-native";
      this.version = null;
      this.os = null;
    }
    return ReactNativeInfo2;
  }();
  var SEARCHBOX_UA_REGEX = /alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
  var SEARCHBOT_OS_REGEX = /(nuhk|curl|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask\ Jeeves\/Teoma|ia_archiver)/;
  var REQUIRED_VERSION_PARTS = 3;
  var userAgentRules = [
    ["aol", /AOLShield\/([0-9\._]+)/],
    ["edge", /Edge\/([0-9\._]+)/],
    ["edge-ios", /EdgiOS\/([0-9\._]+)/],
    ["yandexbrowser", /YaBrowser\/([0-9\._]+)/],
    ["kakaotalk", /KAKAOTALK\s([0-9\.]+)/],
    ["samsung", /SamsungBrowser\/([0-9\.]+)/],
    ["silk", /\bSilk\/([0-9._-]+)\b/],
    ["miui", /MiuiBrowser\/([0-9\.]+)$/],
    ["beaker", /BeakerBrowser\/([0-9\.]+)/],
    ["edge-chromium", /EdgA?\/([0-9\.]+)/],
    [
      "chromium-webview",
      /(?!Chrom.*OPR)wv\).*Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/
    ],
    ["chrome", /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
    ["phantomjs", /PhantomJS\/([0-9\.]+)(:?\s|$)/],
    ["crios", /CriOS\/([0-9\.]+)(:?\s|$)/],
    ["firefox", /Firefox\/([0-9\.]+)(?:\s|$)/],
    ["fxios", /FxiOS\/([0-9\.]+)/],
    ["opera-mini", /Opera Mini.*Version\/([0-9\.]+)/],
    ["opera", /Opera\/([0-9\.]+)(?:\s|$)/],
    ["opera", /OPR\/([0-9\.]+)(:?\s|$)/],
    ["pie", /^Microsoft Pocket Internet Explorer\/(\d+\.\d+)$/],
    ["pie", /^Mozilla\/\d\.\d+\s\(compatible;\s(?:MSP?IE|MSInternet Explorer) (\d+\.\d+);.*Windows CE.*\)$/],
    ["netfront", /^Mozilla\/\d\.\d+.*NetFront\/(\d.\d)/],
    ["ie", /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
    ["ie", /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
    ["ie", /MSIE\s(7\.0)/],
    ["bb10", /BB10;\sTouch.*Version\/([0-9\.]+)/],
    ["android", /Android\s([0-9\.]+)/],
    ["ios", /Version\/([0-9\._]+).*Mobile.*Safari.*/],
    ["safari", /Version\/([0-9\._]+).*Safari/],
    ["facebook", /FB[AS]V\/([0-9\.]+)/],
    ["instagram", /Instagram\s([0-9\.]+)/],
    ["ios-webview", /AppleWebKit\/([0-9\.]+).*Mobile/],
    ["ios-webview", /AppleWebKit\/([0-9\.]+).*Gecko\)$/],
    ["curl", /^curl\/([0-9\.]+)$/],
    ["searchbot", SEARCHBOX_UA_REGEX]
  ];
  var operatingSystemRules = [
    ["iOS", /iP(hone|od|ad)/],
    ["Android OS", /Android/],
    ["BlackBerry OS", /BlackBerry|BB10/],
    ["Windows Mobile", /IEMobile/],
    ["Amazon OS", /Kindle/],
    ["Windows 3.11", /Win16/],
    ["Windows 95", /(Windows 95)|(Win95)|(Windows_95)/],
    ["Windows 98", /(Windows 98)|(Win98)/],
    ["Windows 2000", /(Windows NT 5.0)|(Windows 2000)/],
    ["Windows XP", /(Windows NT 5.1)|(Windows XP)/],
    ["Windows Server 2003", /(Windows NT 5.2)/],
    ["Windows Vista", /(Windows NT 6.0)/],
    ["Windows 7", /(Windows NT 6.1)/],
    ["Windows 8", /(Windows NT 6.2)/],
    ["Windows 8.1", /(Windows NT 6.3)/],
    ["Windows 10", /(Windows NT 10.0)/],
    ["Windows ME", /Windows ME/],
    ["Windows CE", /Windows CE|WinCE|Microsoft Pocket Internet Explorer/],
    ["Open BSD", /OpenBSD/],
    ["Sun OS", /SunOS/],
    ["Chrome OS", /CrOS/],
    ["Linux", /(Linux)|(X11)/],
    ["Mac OS", /(Mac_PowerPC)|(Macintosh)/],
    ["QNX", /QNX/],
    ["BeOS", /BeOS/],
    ["OS/2", /OS\/2/]
  ];
  function detect(userAgent) {
    if (!!userAgent) {
      return parseUserAgent(userAgent);
    }
    if (typeof document === "undefined" && typeof navigator !== "undefined" && navigator.product === "ReactNative") {
      return new ReactNativeInfo();
    }
    if (typeof navigator !== "undefined") {
      return parseUserAgent(navigator.userAgent);
    }
    return getNodeVersion();
  }
  function matchUserAgent(ua) {
    return ua !== "" && userAgentRules.reduce(function(matched, _a2) {
      var browser = _a2[0], regex = _a2[1];
      if (matched) {
        return matched;
      }
      var uaMatch = regex.exec(ua);
      return !!uaMatch && [browser, uaMatch];
    }, false);
  }
  function parseUserAgent(ua) {
    var matchedRule = matchUserAgent(ua);
    if (!matchedRule) {
      return null;
    }
    var name = matchedRule[0], match2 = matchedRule[1];
    if (name === "searchbot") {
      return new BotInfo();
    }
    var versionParts = match2[1] && match2[1].split(".").join("_").split("_").slice(0, 3);
    if (versionParts) {
      if (versionParts.length < REQUIRED_VERSION_PARTS) {
        versionParts = __spreadArray(__spreadArray([], versionParts, true), createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length), true);
      }
    } else {
      versionParts = [];
    }
    var version = versionParts.join(".");
    var os = detectOS(ua);
    var searchBotMatch = SEARCHBOT_OS_REGEX.exec(ua);
    if (searchBotMatch && searchBotMatch[1]) {
      return new SearchBotDeviceInfo(name, version, os, searchBotMatch[1]);
    }
    return new BrowserInfo(name, version, os);
  }
  function detectOS(ua) {
    for (var ii = 0, count = operatingSystemRules.length; ii < count; ii++) {
      var _a2 = operatingSystemRules[ii], os = _a2[0], regex = _a2[1];
      var match2 = regex.exec(ua);
      if (match2) {
        return os;
      }
    }
    return null;
  }
  function getNodeVersion() {
    var isNode = typeof process !== "undefined" && process.version;
    return isNode ? new NodeInfo(process.version.slice(1)) : null;
  }
  function createVersionParts(count) {
    var output = [];
    for (var ii = 0; ii < count; ii++) {
      output.push("0");
    }
    return output;
  }
  const booleanValidator = (val) => typeof val === "boolean";
  const createNumberValidator = (start, end) => (val) => typeof val === "number" && start <= val && val <= end;
  const settingDefinitions = {
    threadNum: {
      key: "thread_num",
      default: 8,
      validator: createNumberValidator(1, 32),
      formatter: (val) => Math.floor(val)
    },
    openOnNewTab: {
      key: "open_on_new_tab",
      default: true,
      validator: booleanValidator
    },
    customDownloadUrl: {
      key: "custom_download_url",
      default: "",
      validator: (val) => typeof val === "string",
      formatter: (val) => val.trim()
    },
    compressionFileName: {
      key: "cf_name",
      default: "{{japanese}}.zip",
      validator: (val) => typeof val === "string",
      formatter: (val) => val.trim()
    },
    compressionLevel: {
      key: "c_lv",
      default: 0,
      validator: createNumberValidator(0, 9),
      formatter: (val) => Math.floor(val)
    },
    compressionStreamFiles: {
      key: "c_stream_files",
      default: false,
      validator: booleanValidator
    },
    streamDownload: {
      key: "stream_download",
      default: false,
      validator: booleanValidator
    },
    seriesMode: {
      key: "series_mode",
      default: false,
      validator: booleanValidator
    },
    filenameLength: {
      key: "filename_length",
      default: 0,
      validator: (val) => val === "auto" || typeof val === "number" && val >= 0,
      formatter: (val) => typeof val === "number" ? Math.floor(val) : val
    },
    autoCancelDownloadedManga: {
      key: "auto_cancel_downloaded_doujin",
      default: false,
      validator: booleanValidator
    },
    autoRetryWhenErrorOccurs: {
      key: "auto_retry_when_error_occurs",
      default: false,
      validator: booleanValidator
    },
    autoShowAll: {
      key: "auto_show_all",
      default: false,
      validator: booleanValidator
    },
    showIgnoreButton: {
      key: "show_ignore_button",
      default: false,
      validator: booleanValidator
    },
    preventConsoleClearing: {
      key: "prevent_console_clear",
      default: false,
      validator: booleanValidator
    }
  };
  const browserDetect = detect();
  const DISABLE_STREAM_DOWNLOAD = !!browserDetect && (browserDetect.name === "safari" || browserDetect.name === "firefox");
  const readSettings = () => mapValues(
    settingDefinitions,
    ({ key, default: defaultVal }) => h(key, defaultVal)
  );
  const writeableSettings = Vue.reactive(readSettings());
  const settings = writeableSettings;
  if (DISABLE_STREAM_DOWNLOAD && settings.streamDownload)
    writeableSettings.streamDownload = false;
  const startWatchSettings = functionOnce(() => {
    const settingRefs = Vue.toRefs(writeableSettings);
    forEach(settingRefs, (ref, key) => {
      const cur = settingDefinitions[key];
      Vue.watch(ref, (val) => {
        if (!cur.validator(val)) {
          ref.value = cur.default;
          return;
        }
        if (cur.formatter) {
          const formattedVal = cur.formatter(val);
          if (ref.value !== formattedVal) {
            ref.value = formattedVal;
            return;
          }
        }
        logger.log("update setting", cur.key, val);
        u(cur.key, val);
      });
    });
  });
  const dlQueue = new AsyncQueue();
  const zipQueue = new AsyncQueue(WORKER_THREAD_NUM);
  dlQueue.canSingleStart = () => !(settings.seriesMode && zipQueue.length);
  zipQueue.emitter.on("finish", () => {
    if (settings.seriesMode)
      dlQueue.start().catch(logger.error);
  });
  const _withScopeId = (n2) => (Vue.pushScopeId("data-v-5e3261cd"), n2 = n2(), Vue.popScopeId(), n2);
  const _hoisted_1$2 = ["title"];
  const _hoisted_2$1 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ Vue.createElementVNode("i", { class: "fa fa-times" }, null, -1));
  const _hoisted_3$1 = [
    _hoisted_2$1
  ];
  const _hoisted_4$1 = { class: "download-item__title" };
  const _hoisted_5$1 = { class: "download-item__progress-text" };
  const _sfc_main$3 = /* @__PURE__ */ Vue.defineComponent({
    __name: "DownloadItem",
    props: {
      item: null,
      index: null
    },
    setup(__props) {
      const props = __props;
      const title = Vue.computed(() => props.item.gallery.title);
      const progressWidth = Vue.computed(() => {
        const {
          gallery: { pages: pages2 },
          done,
          compressing,
          compressingPercent
        } = props.item;
        const page = pages2.length;
        return compressing ? compressingPercent : page && done ? (100 * done / page).toFixed(2) : 0;
      });
      const canCancel = Vue.computed(() => !props.item.compressing);
      const cancel = () => {
        var _a2;
        const { info } = props.index === 0 ? dlQueue.queue[0] : removeAt(dlQueue.queue, props.index);
        (_a2 = info == null ? void 0 : info.cancel) == null ? void 0 : _a2.call(info);
      };
      return (_ctx, _cache) => {
        return Vue.openBlock(), Vue.createElementBlock("div", {
          class: Vue.normalizeClass(["download-item", {
            "download-item--error": __props.item.error,
            "download-item--compressing": __props.item.compressing && !__props.item.error,
            "download-item--can-cancel": Vue.unref(canCancel)
          }]),
          title: Vue.unref(title)
        }, [
          Vue.unref(canCancel) ? (Vue.openBlock(), Vue.createElementBlock("div", {
            key: 0,
            class: "download-item__cancel",
            onClick: cancel
          }, _hoisted_3$1)) : Vue.createCommentVNode("", true),
          Vue.createElementVNode("div", _hoisted_4$1, Vue.toDisplayString(Vue.unref(title)), 1),
          Vue.createElementVNode("div", {
            class: "download-item__progress",
            style: Vue.normalizeStyle({ width: `${Vue.unref(progressWidth)}%` })
          }, [
            Vue.createElementVNode("div", _hoisted_5$1, Vue.toDisplayString(Vue.unref(progressWidth)) + "%", 1)
          ], 4)
        ], 10, _hoisted_1$2);
      };
    }
  });
  const DownloadItem_vue_vue_type_style_index_0_scoped_5e3261cd_lang = "";
  const _export_sfc = (sfc, props) => {
    const target = sfc.__vccOpts || sfc;
    for (const [key, val] of props) {
      target[key] = val;
    }
    return target;
  };
  const DownloadItem = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-5e3261cd"]]);
  const _hoisted_1$1 = { id: "download-panel" };
  const _sfc_main$2 = /* @__PURE__ */ Vue.defineComponent({
    __name: "DownloadList",
    props: {
      zipList: null,
      dlList: null
    },
    setup(__props) {
      return (_ctx, _cache) => {
        return Vue.openBlock(), Vue.createElementBlock("div", _hoisted_1$1, [
          (Vue.openBlock(true), Vue.createElementBlock(Vue.Fragment, null, Vue.renderList(__props.zipList, (item, index2) => {
            return Vue.openBlock(), Vue.createBlock(DownloadItem, {
              key: index2,
              item,
              index: index2
            }, null, 8, ["item", "index"]);
          }), 128)),
          (Vue.openBlock(true), Vue.createElementBlock(Vue.Fragment, null, Vue.renderList(__props.dlList, (item, index2) => {
            return Vue.openBlock(), Vue.createBlock(DownloadItem, {
              key: index2,
              item,
              index: index2
            }, null, 8, ["item", "index"]);
          }), 128))
        ]);
      };
    }
  });
  const DownloadList_vue_vue_type_style_index_0_scoped_658acab9_lang = "";
  const DownloadList = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-658acab9"]]);
  const _sfc_main$1 = /* @__PURE__ */ Vue.defineComponent({
    __name: "DownloadPanel",
    setup(__props) {
      const { title } = document;
      const zipList = Vue.computed(() => zipQueue.queue.map(({ info }) => info));
      const dlList = Vue.computed(() => dlQueue.queue.map(({ info }) => info));
      const infoList = Vue.computed(() => [...zipList.value, ...dlList.value]);
      const error = Vue.computed(() => {
        var _a2;
        return !!((_a2 = dlList.value[0]) == null ? void 0 : _a2.error);
      });
      const titleWithStatus = Vue.computed(() => {
        if (error.value)
          return `[\xD7] ${title}`;
        const len = infoList.value.length;
        return `[${len || "\u2713"}] ${title}`;
      });
      Vue.watch(infoList, (val) => {
        sessionStorage.setItem("downloadQueue", JSON.stringify(val.map(({ gallery: gallery2 }) => gallery2)));
      });
      Vue.watch(titleWithStatus, (val) => {
        document.title = val;
      });
      return (_ctx, _cache) => {
        return Vue.unref(infoList).length ? (Vue.openBlock(), Vue.createBlock(DownloadList, {
          key: 0,
          "zip-list": Vue.unref(zipList),
          "dl-list": Vue.unref(dlList)
        }, null, 8, ["zip-list", "dl-list"])) : Vue.createCommentVNode("", true);
      };
    }
  });
  /*! Element Plus Icons Vue v2.0.9 */
  var export_helper_default = (sfc, props) => {
    let target = sfc.__vccOpts || sfc;
    for (let [key, val] of props)
      target[key] = val;
    return target;
  };
  var _sfc_main80 = {
    name: "Delete"
  }, _hoisted_180 = {
    viewBox: "0 0 1024 1024",
    xmlns: "http://www.w3.org/2000/svg"
  }, _hoisted_280 = /* @__PURE__ */ Vue.createElementVNode("path", {
    fill: "currentColor",
    d: "M160 256H96a32 32 0 0 1 0-64h256V95.936a32 32 0 0 1 32-32h256a32 32 0 0 1 32 32V192h256a32 32 0 1 1 0 64h-64v672a32 32 0 0 1-32 32H192a32 32 0 0 1-32-32V256zm448-64v-64H416v64h192zM224 896h576V256H224v640zm192-128a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32zm192 0a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32z"
  }, null, -1), _hoisted_379 = [
    _hoisted_280
  ];
  function _sfc_render80(_ctx, _cache, $props, $setup, $data, $options) {
    return Vue.openBlock(), Vue.createElementBlock("svg", _hoisted_180, _hoisted_379);
  }
  var delete_default = /* @__PURE__ */ export_helper_default(_sfc_main80, [["render", _sfc_render80], ["__file", "delete.vue"]]);
  var _sfc_main91 = {
    name: "Download"
  }, _hoisted_191 = {
    viewBox: "0 0 1024 1024",
    xmlns: "http://www.w3.org/2000/svg"
  }, _hoisted_291 = /* @__PURE__ */ Vue.createElementVNode("path", {
    fill: "currentColor",
    d: "M160 832h704a32 32 0 1 1 0 64H160a32 32 0 1 1 0-64zm384-253.696 236.288-236.352 45.248 45.248L508.8 704 192 387.2l45.248-45.248L480 584.704V128h64v450.304z"
  }, null, -1), _hoisted_390 = [
    _hoisted_291
  ];
  function _sfc_render91(_ctx, _cache, $props, $setup, $data, $options) {
    return Vue.openBlock(), Vue.createElementBlock("svg", _hoisted_191, _hoisted_390);
  }
  var download_default = /* @__PURE__ */ export_helper_default(_sfc_main91, [["render", _sfc_render91], ["__file", "download.vue"]]);
  var _sfc_main275 = {
    name: "Upload"
  }, _hoisted_1275 = {
    viewBox: "0 0 1024 1024",
    xmlns: "http://www.w3.org/2000/svg"
  }, _hoisted_2275 = /* @__PURE__ */ Vue.createElementVNode("path", {
    fill: "currentColor",
    d: "M160 832h704a32 32 0 1 1 0 64H160a32 32 0 1 1 0-64zm384-578.304V704h-64V247.296L237.248 490.048 192 444.8 508.8 128l316.8 316.8-45.312 45.248L544 253.696z"
  }, null, -1), _hoisted_3274 = [
    _hoisted_2275
  ];
  function _sfc_render275(_ctx, _cache, $props, $setup, $data, $options) {
    return Vue.openBlock(), Vue.createElementBlock("svg", _hoisted_1275, _hoisted_3274);
  }
  var upload_default = /* @__PURE__ */ export_helper_default(_sfc_main275, [["render", _sfc_render275], ["__file", "upload.vue"]]);
  function getSerializerPromise(localForageInstance) {
    if (getSerializerPromise.result) {
      return getSerializerPromise.result;
    }
    if (!localForageInstance || typeof localForageInstance.getSerializer !== "function") {
      return Promise.reject(new Error("localforage.getSerializer() was not available! localforage v1.4+ is required!"));
    }
    getSerializerPromise.result = localForageInstance.getSerializer();
    return getSerializerPromise.result;
  }
  function executeCallback(promise, callback) {
    if (callback) {
      promise.then(function(result) {
        callback(null, result);
      }, function(error) {
        callback(error);
      });
    }
  }
  function forEachItem(items, keyFn, valueFn, loopFn) {
    function ensurePropGetterMethod(propFn, defaultPropName) {
      var propName = propFn || defaultPropName;
      if ((!propFn || typeof propFn !== "function") && typeof propName === "string") {
        propFn = function propFn2(item2) {
          return item2[propName];
        };
      }
      return propFn;
    }
    var result = [];
    if (Object.prototype.toString.call(items) === "[object Array]") {
      keyFn = ensurePropGetterMethod(keyFn, "key");
      valueFn = ensurePropGetterMethod(valueFn, "value");
      for (var i = 0, len = items.length; i < len; i++) {
        var item = items[i];
        result.push(loopFn(keyFn(item), valueFn(item)));
      }
    } else {
      for (var prop in items) {
        if (items.hasOwnProperty(prop)) {
          result.push(loopFn(prop, items[prop]));
        }
      }
    }
    return result;
  }
  function setItemsIndexedDB(items, keyFn, valueFn, callback) {
    var localforageInstance = this;
    var promise = localforageInstance.ready().then(function() {
      return new Promise(function(resolve, reject) {
        var dbInfo = localforageInstance._dbInfo;
        var transaction = dbInfo.db.transaction(dbInfo.storeName, "readwrite");
        var store = transaction.objectStore(dbInfo.storeName);
        var lastError;
        transaction.oncomplete = function() {
          resolve(items);
        };
        transaction.onabort = transaction.onerror = function(event) {
          reject(lastError || event.target);
        };
        function requestOnError(evt) {
          var request2 = evt.target || this;
          lastError = request2.error || request2.transaction.error;
          reject(lastError);
        }
        forEachItem(items, keyFn, valueFn, function(key, value) {
          if (value === null) {
            value = void 0;
          }
          var request2 = store.put(value, key);
          request2.onerror = requestOnError;
        });
      });
    });
    executeCallback(promise, callback);
    return promise;
  }
  function setItemsWebsql(items, keyFn, valueFn, callback) {
    var localforageInstance = this;
    var promise = new Promise(function(resolve, reject) {
      localforageInstance.ready().then(function() {
        return getSerializerPromise(localforageInstance);
      }).then(function(serializer) {
        var dbInfo = localforageInstance._dbInfo;
        dbInfo.db.transaction(
          function(t) {
            var query = "INSERT OR REPLACE INTO " + dbInfo.storeName + " (key, value) VALUES (?, ?)";
            var itemPromises = forEachItem(items, keyFn, valueFn, function(key, value) {
              return new Promise(function(resolve2, reject2) {
                serializer.serialize(value, function(value2, error) {
                  if (error) {
                    reject2(error);
                  } else {
                    t.executeSql(query, [key, value2], function() {
                      resolve2();
                    }, function(t2, error2) {
                      reject2(error2);
                    });
                  }
                });
              });
            });
            Promise.all(itemPromises).then(function() {
              resolve(items);
            }, reject);
          },
          function(sqlError) {
            reject(sqlError);
          }
        );
      }).catch(reject);
    });
    executeCallback(promise, callback);
    return promise;
  }
  function setItemsGeneric(items, keyFn, valueFn, callback) {
    var localforageInstance = this;
    var itemPromises = forEachItem(items, keyFn, valueFn, function(key, value) {
      return localforageInstance.setItem(key, value);
    });
    var promise = Promise.all(itemPromises);
    executeCallback(promise, callback);
    return promise;
  }
  function localforageSetItems(items, keyFn, valueFn, callback) {
    var localforageInstance = this;
    var currentDriver = localforageInstance.driver();
    if (currentDriver === localforageInstance.INDEXEDDB) {
      return setItemsIndexedDB.call(localforageInstance, items, keyFn, valueFn, callback);
    } else if (currentDriver === localforageInstance.WEBSQL) {
      return setItemsWebsql.call(localforageInstance, items, keyFn, valueFn, callback);
    } else {
      return setItemsGeneric.call(localforageInstance, items, keyFn, valueFn, callback);
    }
  }
  function extendPrototype(localforage$$1) {
    var localforagePrototype = Object.getPrototypeOf(localforage$$1);
    if (localforagePrototype) {
      localforagePrototype.setItems = localforageSetItems;
      localforagePrototype.setItems.indexedDB = function() {
        return setItemsIndexedDB.apply(this, arguments);
      };
      localforagePrototype.setItems.websql = function() {
        return setItemsWebsql.apply(this, arguments);
      };
      localforagePrototype.setItems.generic = function() {
        return setItemsGeneric.apply(this, arguments);
      };
    }
  }
  extendPrototype(localforage__default.default);
  const dateTimeFormatter = new Intl.DateTimeFormat(void 0, {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
    hour12: false
  });
  const jszipWorkerCode = 'importScripts("https://fastly.jsdelivr.net/npm/comlink@4.3.1/dist/umd/comlink.min.js","https://fastly.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js");class DisposableJSZip{zip=new JSZip;file({name:e,data:t}){this.zip.file(e,t)}files(e){e.forEach(({name:t,data:i})=>{this.zip.file(t,i)})}async unzipFile({data:e,path:t,type:i}){return(await JSZip.loadAsync(e)).file(t)?.async(i)}async generateAsync(e,t){const i=await this.zip.generateAsync({...e,type:"uint8array"},t);return Comlink.transfer(i,[i.buffer])}generateStream(e,t,i){const s=this.zip.generateInternalStream({...e,type:"uint8array"}),r=new ReadableStream({start:n=>{s.on("error",a=>{n.error(a),i?.()}),s.on("end",()=>{setTimeout(()=>{n.close(),i?.()})}),s.on("data",(a,p)=>{n.enqueue(a),t?.(p)}),s.resume()}});return Comlink.transfer({zipStream:r},[r])}}Comlink.expose(DisposableJSZip);';
  const WORKER_URL = URL.createObjectURL(new Blob([jszipWorkerCode], { type: "text/javascript" }));
  const getTransferableData = (files) => files.map(({ data }) => data).filter((data) => typeof data !== "string");
  class JSZipWorkerPool {
    constructor() {
      __publicField(this, "pool", []);
      __publicField(this, "waitingQueue", []);
      __publicField(this, "unzipFile", async (params) => {
        const worker = await this.acquireWorker();
        const zip = await new worker.JSZip();
        const clean = () => {
          zip[comlink.releaseProxy]();
          this.releaseWorker(worker);
        };
        try {
          return await zip.unzipFile(comlink.transfer(params, [params.data]));
        } catch (error) {
          clean();
          throw error;
        }
      });
      for (let id = 0; id < WORKER_THREAD_NUM; id++) {
        this.pool.push({
          id,
          idle: true
        });
      }
    }
    createWorker() {
      const worker = new Worker(WORKER_URL);
      return comlink.wrap(worker);
    }
    waitIdleWorker() {
      return new Promise((resolve) => {
        this.waitingQueue.push(resolve);
      });
    }
    async acquireWorker() {
      let worker = this.pool.find(({ idle }) => idle);
      if (!worker)
        worker = await this.waitIdleWorker();
      if (!worker.JSZip)
        worker.JSZip = this.createWorker();
      worker.idle = false;
      return worker;
    }
    releaseWorker(worker) {
      worker.idle = true;
      if (!this.waitingQueue.length)
        return;
      const emit = removeAt(this.waitingQueue, 0);
      emit(worker);
    }
    async generateAsync(files, options, onUpdate) {
      const worker = await this.acquireWorker();
      const zip = await new worker.JSZip();
      try {
        await zip.files(comlink.transfer(files, getTransferableData(files)));
        return await zip.generateAsync(
          options,
          comlink.proxy((metaData) => {
            if (metaData.currentFile)
              onUpdate == null ? void 0 : onUpdate({ workerId: worker.id, ...metaData });
          })
        );
      } finally {
        zip[comlink.releaseProxy]();
        this.releaseWorker(worker);
      }
    }
    async generateStream(files, options, onUpdate) {
      const worker = await this.acquireWorker();
      const zip = await new worker.JSZip();
      try {
        await zip.files(comlink.transfer(files, getTransferableData(files)));
        const { zipStream } = await zip.generateStream(
          options,
          comlink.proxy((metaData) => {
            if (metaData.currentFile)
              onUpdate == null ? void 0 : onUpdate({ workerId: worker.id, ...metaData });
          })
        );
        return zipStream;
      } finally {
        zip[comlink.releaseProxy]();
        this.releaseWorker(worker);
      }
    }
  }
  const jszipPool = new JSZipWorkerPool();
  class JSZip {
    constructor() {
      __publicField(this, "files", []);
    }
    file(name, data) {
      this.files.push({ name, data });
    }
    generateAsync(options, onUpdate) {
      const { files } = this;
      this.files = [];
      return jszipPool.generateAsync(files, options, onUpdate);
    }
    generateStream(options, onUpdate) {
      const { files } = this;
      this.files = [];
      return jszipPool.generateStream(files, options, onUpdate);
    }
  }
  __publicField(JSZip, "unzipFile", (params) => jszipPool.unzipFile(params));
  extendPrototype(localforage__default.default);
  const dlGidStore = localforage__default.default.createInstance({
    name: "nhentai_helper",
    storeName: "dl_history_gid"
  });
  const dlGidStoreReady = dlGidStore.ready().then(() => true).catch((e) => {
    logger.error(e);
    return false;
  });
  const dlTitleStore = localforage__default.default.createInstance({
    name: "nhentai_helper",
    storeName: "dl_history"
  });
  const dlTitleStoreReady = dlTitleStore.ready().then(() => true).catch((e) => {
    logger.error(e);
    return false;
  });
  const markAsDownloaded = (gid2, title) => {
    void dlGidStoreReady.then((ready) => {
      if (!ready)
        return;
      dlGidStore.setItem(String(gid2), true).then(() => logger.log(`mark "${gid2}" as downloaded`)).catch(logger.error);
    });
    if (title) {
      void dlTitleStoreReady.then((ready) => {
        if (!ready)
          return;
        dlTitleStore.setItem(md5__default.default(title.replace(/\s/g, "")), true).then(() => logger.log(`mark "${title}" as downloaded`)).catch(logger.error);
      });
    }
  };
  const unmarkAsDownloaded = (gid2, title) => {
    void dlGidStoreReady.then((ready) => {
      if (!ready)
        return;
      dlGidStore.removeItem(String(gid2)).then(() => logger.log("unmark", gid2, "as downloaded")).catch(logger.error);
    });
    if (title) {
      void dlTitleStoreReady.then((ready) => {
        if (!ready)
          return;
        dlTitleStore.removeItem(md5__default.default(title.replace(/\s/g, ""))).then(() => logger.log("unmark", title, "as downloaded")).catch(logger.error);
      });
    }
  };
  const isDownloadedByGid = async (gid2) => {
    try {
      if (await dlGidStoreReady) {
        return await dlGidStore.getItem(String(gid2)) === true;
      }
    } catch (e) {
      logger.error(e);
    }
    return false;
  };
  const isDownloadedByTitle = async (title) => {
    try {
      if (!await dlTitleStoreReady)
        return false;
      const md5v2 = md5__default.default(title.replace(/\s/g, ""));
      if (await dlTitleStore.getItem(md5v2) === true)
        return true;
      const md5v1 = md5__default.default(title);
      if (await dlTitleStore.getItem(md5v1) === true) {
        dlTitleStore.setItem(md5v2, true).catch(logger.error);
        dlTitleStore.removeItem(md5v1).catch(logger.error);
        return true;
      }
    } catch (e) {
      logger.error(e);
    }
    return false;
  };
  const getDownloadNumber = async () => {
    try {
      if (!await dlGidStoreReady)
        throw new Error("store cannot ready");
      return await dlGidStore.length();
    } catch (error) {
      logger.error(error);
    }
    return NaN;
  };
  const EXPORT_HEADER_GID = "gid:";
  const EXPORT_HEADER_TITLE = "title:";
  const EXPORT_SEPARATOR = ",";
  const EXPORT_TEXT_FILENAME = "history.txt";
  const exportDownloadHistory = async () => {
    try {
      if (!(await dlGidStoreReady && await dlTitleStoreReady))
        throw new Error("store cannot ready");
      const gids = await dlGidStore.keys();
      const titles = await dlTitleStore.keys();
      const text = `${EXPORT_HEADER_GID}${gids.join(EXPORT_SEPARATOR)}
${EXPORT_HEADER_TITLE}${titles.join(EXPORT_SEPARATOR)}`;
      const zip = new JSZip();
      zip.file(EXPORT_TEXT_FILENAME, text);
      const data = await zip.generateAsync({
        compression: "DEFLATE",
        compressionOptions: { level: 9 }
      });
      const timeStr = dateTimeFormatter.format(Date.now()).replace(/[^\d]/g, "");
      const filename = `nhentai-helper-download-history-${timeStr}.zip`;
      fileSaver.saveAs(new File([data], filename, { type: "application/zip" }));
      logger.log("export download history", filename);
      return true;
    } catch (error) {
      logger.error(error);
    }
    return false;
  };
  const importDownloadHistory = async (data) => {
    try {
      if (!(await dlGidStoreReady && await dlTitleStoreReady))
        throw new Error("store cannot ready");
      const str = await JSZip.unzipFile({ data, path: EXPORT_TEXT_FILENAME, type: "string" });
      if (!str) {
        logger.error("zip doesn't contain file", EXPORT_TEXT_FILENAME);
        return false;
      }
      const lines = str.split("\n");
      for (const line of lines) {
        if (line.startsWith(EXPORT_HEADER_GID)) {
          const gids = line.replace(EXPORT_HEADER_GID, "").split(EXPORT_SEPARATOR);
          await dlGidStore.setItems(gids.map((gid2) => ({ key: gid2, value: true })));
        } else if (line.startsWith(EXPORT_HEADER_TITLE)) {
          const titles = line.replace(EXPORT_HEADER_TITLE, "").split(EXPORT_SEPARATOR);
          await dlTitleStore.setItems(titles.map((gid2) => ({ key: gid2, value: true })));
        }
      }
      return true;
    } catch (error) {
      logger.error(error);
    }
    return false;
  };
  const clearDownloadHistory = async () => {
    try {
      if (!(await dlGidStoreReady && await dlTitleStoreReady))
        throw new Error("store cannot ready");
      await dlGidStore.clear();
      await dlTitleStore.clear();
      return true;
    } catch (error) {
      logger.error(error);
    }
    return false;
  };
  const createElement = (tag, props, ...children) => {
    if (typeof tag === "function")
      return tag(props, ...children);
    const element = document.createElement(tag);
    Object.entries(props != null ? props : {}).forEach(([name, value]) => {
      if (name === "html")
        element.innerHTML = value;
      else if (name === "class")
        element.classList.add(...String(value).split(" "));
      else if (name === "style" && typeof value === "object") {
        const styleString = Object.entries(value).map(([k, v]) => `${camelCase$1(k)}:${String(v)}`).join(";");
        element.setAttribute("style", styleString);
      } else if (name.startsWith("on")) {
        element.addEventListener(kebabCase$1(name.replace("on", "")), value);
      } else
        element.setAttribute(name, String(value));
    });
    children.flat().forEach((child) => {
      appendChild(element, child);
    });
    return element;
  };
  const appendChild = (parent, child) => {
    if (!child)
      return;
    parent.appendChild(typeof child === "string" ? document.createTextNode(child) : child);
  };
  const Fragment = (props, ...children) => {
    return children;
  };
  const readFile = (file) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onabort = reject;
    reader.onerror = reject;
    reader.readAsArrayBuffer(file);
  });
  const pickFile = (accept) => new Promise((resolve) => {
    const input = /* @__PURE__ */ createElement("input", {
      type: "file",
      accept,
      onChange: () => {
        var _a2;
        return resolve((_a2 = input.files) == null ? void 0 : _a2[0]);
      }
    });
    input.click();
  });
  const pickAndReadFile = async (accept) => {
    const file = await pickFile(accept);
    if (file)
      return readFile(file);
  };
  const elementPlusJs = rawLoader("elementPlusJs");
  const elementPlusCss = rawLoader("elementPlusCss");
  const elementPlus = functionOnce(() => {
    const win = window;
    if (!win.Vue)
      win.Vue = Vue__namespace;
    eval(elementPlusJs);
    l(elementPlusCss);
    return win.ElementPlus;
  });
  const showMessage = (params) => elementPlus().ElMessage({ ...params, appendTo: r.document.body });
  const _hoisted_1 = { class: "nhentai-helper-setting-help-buttons no-sl" };
  const _hoisted_2 = /* @__PURE__ */ Vue.createTextVNode("Help");
  const _hoisted_3 = /* @__PURE__ */ Vue.createTextVNode("\u8BF4\u660E");
  const _hoisted_4 = ["id"];
  const _hoisted_5 = { id: "nhentai-helper-setting-dialog" };
  const _hoisted_6 = /* @__PURE__ */ Vue.createElementVNode("div", {
    class: "asterisk-left no-sl",
    style: { "margin-bottom": "18px" }
  }, " means refresh is required to take effect ", -1);
  const _hoisted_7 = /* @__PURE__ */ Vue.createTextVNode("Advance Settings");
  const _hoisted_8 = /* @__PURE__ */ Vue.createTextVNode("Download History");
  const _hoisted_9 = { class: "no-sl" };
  const _hoisted_10 = /* @__PURE__ */ Vue.createTextVNode("Export");
  const _hoisted_11 = /* @__PURE__ */ Vue.createTextVNode("Import");
  const _hoisted_12 = /* @__PURE__ */ Vue.createTextVNode("Clear");
  const _hoisted_13 = /* @__PURE__ */ Vue.createElementVNode("p", { class: "no-sl" }, "Notice: Import will not clear the existing history, but merges with it.", -1);
  const _sfc_main = /* @__PURE__ */ Vue.defineComponent({
    __name: "SettingsDialog",
    setup(__props, { expose }) {
      startWatchSettings();
      const threadNumMarks = {
        1: "1",
        4: "4",
        8: "8",
        16: "16",
        32: {
          label: "32",
          style: { whiteSpace: "nowrap" }
        }
      };
      const compressionLevelMarks = {
        0: "0",
        1: "1",
        9: "9"
      };
      const show = Vue.ref(false);
      const downloadNum = Vue.ref(NaN);
      const filenameLengthNumber = Vue.computed({
        get: () => typeof writeableSettings.filenameLength === "number" ? writeableSettings.filenameLength : 0,
        set: (val) => {
          writeableSettings.filenameLength = val;
        }
      });
      const filenameLengthAuto = Vue.computed({
        get: () => writeableSettings.filenameLength === "auto",
        set: (val) => {
          writeableSettings.filenameLength = val ? "auto" : 0;
        }
      });
      const refreshDownloadNum = async () => {
        downloadNum.value = await getDownloadNumber();
      };
      const open = () => {
        show.value = true;
        refreshDownloadNum();
      };
      const openHelp = () => {
        r.open(
          "https://github.com/Tsuk1ko/nhentai-helper/blob/master/README.md#settings",
          "_blank"
        );
      };
      const openHelpCn = () => {
        r.open(
          "https://github.com/Tsuk1ko/nhentai-helper/blob/master/README-ZH.md#%E8%AE%BE%E7%BD%AE",
          "_blank"
        );
      };
      const exporting = Vue.ref(false);
      const importing = Vue.ref(false);
      const clearing = Vue.ref(false);
      const showMessageBySucceed = (succeed) => {
        showMessage({
          type: succeed ? "success" : "error",
          message: succeed ? "Succeed" : "Failed, please check console for error message"
        });
      };
      const exportHistory = async () => {
        exporting.value = true;
        const succeed = await exportDownloadHistory();
        exporting.value = false;
        showMessageBySucceed(succeed);
      };
      const importHistory = async () => {
        const data = await pickAndReadFile("application/zip");
        if (!data)
          return;
        importing.value = true;
        const succeed = await importDownloadHistory(data);
        importing.value = false;
        refreshDownloadNum();
        showMessageBySucceed(succeed);
      };
      const clearHistory = async () => {
        clearing.value = true;
        const succeed = await clearDownloadHistory();
        clearing.value = false;
        refreshDownloadNum();
        showMessageBySucceed(succeed);
      };
      expose({ open });
      return (_ctx, _cache) => {
        const _component_el_button = Vue.resolveComponent("el-button");
        const _component_el_slider = Vue.resolveComponent("el-slider");
        const _component_el_form_item = Vue.resolveComponent("el-form-item");
        const _component_el_switch = Vue.resolveComponent("el-switch");
        const _component_el_input = Vue.resolveComponent("el-input");
        const _component_el_input_number = Vue.resolveComponent("el-input-number");
        const _component_el_checkbox = Vue.resolveComponent("el-checkbox");
        const _component_el_divider = Vue.resolveComponent("el-divider");
        const _component_el_form = Vue.resolveComponent("el-form");
        const _component_el_popconfirm = Vue.resolveComponent("el-popconfirm");
        const _component_el_dialog = Vue.resolveComponent("el-dialog");
        return Vue.openBlock(), Vue.createBlock(_component_el_dialog, {
          modelValue: show.value,
          "onUpdate:modelValue": _cache[16] || (_cache[16] = ($event) => show.value = $event),
          center: true,
          top: "50px"
        }, {
          header: Vue.withCtx(({ titleId, titleClass }) => [
            Vue.createElementVNode("div", _hoisted_1, [
              Vue.createVNode(_component_el_button, {
                size: "small",
                onClick: openHelp
              }, {
                default: Vue.withCtx(() => [
                  _hoisted_2
                ]),
                _: 1
              }),
              Vue.createVNode(_component_el_button, {
                size: "small",
                onClick: openHelpCn
              }, {
                default: Vue.withCtx(() => [
                  _hoisted_3
                ]),
                _: 1
              })
            ]),
            Vue.createElementVNode("span", {
              id: titleId,
              class: Vue.normalizeClass([titleClass, "no-sl"])
            }, "Settings", 10, _hoisted_4)
          ]),
          default: Vue.withCtx(() => [
            Vue.createElementVNode("div", _hoisted_5, [
              _hoisted_6,
              Vue.createVNode(_component_el_form, {
                "label-width": "auto",
                "label-position": "left"
              }, {
                default: Vue.withCtx(() => [
                  Vue.createVNode(_component_el_form_item, {
                    class: "m-b-32",
                    label: "Download thread"
                  }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_slider, {
                        modelValue: Vue.unref(writeableSettings).threadNum,
                        "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => Vue.unref(writeableSettings).threadNum = $event),
                        min: 1,
                        max: 32,
                        marks: threadNumMarks
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, {
                    class: "refresh-required",
                    label: "Open on new tab"
                  }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).openOnNewTab,
                        "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => Vue.unref(writeableSettings).openOnNewTab = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: "Compression filename" }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_input, {
                        modelValue: Vue.unref(writeableSettings).compressionFileName,
                        "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => Vue.unref(writeableSettings).compressionFileName = $event),
                        placeholder: Vue.unref(settingDefinitions).compressionFileName.default,
                        onBlur: _cache[3] || (_cache[3] = ($event) => {
                          if (!Vue.unref(writeableSettings).compressionFileName) {
                            Vue.unref(writeableSettings).compressionFileName = Vue.unref(settingDefinitions).compressionFileName.default;
                          }
                        })
                      }, null, 8, ["modelValue", "placeholder"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, {
                    class: "m-b-32",
                    label: "Compression level"
                  }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_slider, {
                        modelValue: Vue.unref(writeableSettings).compressionLevel,
                        "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => Vue.unref(writeableSettings).compressionLevel = $event),
                        min: 0,
                        max: 9,
                        marks: compressionLevelMarks
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: "Filename length" }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_input_number, {
                        modelValue: Vue.unref(filenameLengthNumber),
                        "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => Vue.isRef(filenameLengthNumber) ? filenameLengthNumber.value = $event : null),
                        min: 0,
                        "value-on-clear": Vue.unref(settingDefinitions).filenameLength.default,
                        "step-strictly": true,
                        disabled: Vue.unref(writeableSettings).filenameLength === "auto"
                      }, null, 8, ["modelValue", "value-on-clear", "disabled"]),
                      Vue.createVNode(_component_el_checkbox, {
                        modelValue: Vue.unref(filenameLengthAuto),
                        "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => Vue.isRef(filenameLengthAuto) ? filenameLengthAuto.value = $event : null),
                        class: "m-l-16",
                        label: "Auto"
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: "Auto cancel downloaded manga" }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).autoCancelDownloadedManga,
                        "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => Vue.unref(writeableSettings).autoCancelDownloadedManga = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: "Auto retry when error occurs" }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).autoRetryWhenErrorOccurs,
                        "onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => Vue.unref(writeableSettings).autoRetryWhenErrorOccurs = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: "Auto show all" }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).autoShowAll,
                        "onUpdate:modelValue": _cache[9] || (_cache[9] = ($event) => Vue.unref(writeableSettings).autoShowAll = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, {
                    class: "refresh-required",
                    label: "Show ignore button"
                  }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).showIgnoreButton,
                        "onUpdate:modelValue": _cache[10] || (_cache[10] = ($event) => Vue.unref(writeableSettings).showIgnoreButton = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_divider, null, {
                    default: Vue.withCtx(() => [
                      _hoisted_7
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: "Custom download URL" }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_input, {
                        modelValue: Vue.unref(writeableSettings).customDownloadUrl,
                        "onUpdate:modelValue": _cache[11] || (_cache[11] = ($event) => Vue.unref(writeableSettings).customDownloadUrl = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: 'Compression "streamFiles"' }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).compressionStreamFiles,
                        "onUpdate:modelValue": _cache[12] || (_cache[12] = ($event) => Vue.unref(writeableSettings).compressionStreamFiles = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: "Series mode" }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).seriesMode,
                        "onUpdate:modelValue": _cache[13] || (_cache[13] = ($event) => Vue.unref(writeableSettings).seriesMode = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  }),
                  Vue.createVNode(_component_el_form_item, { label: "Stream download" }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).streamDownload,
                        "onUpdate:modelValue": _cache[14] || (_cache[14] = ($event) => Vue.unref(writeableSettings).streamDownload = $event),
                        disabled: Vue.unref(DISABLE_STREAM_DOWNLOAD)
                      }, null, 8, ["modelValue", "disabled"])
                    ]),
                    _: 1
                  }),
                  Vue.unref(IS_NHENTAI) ? (Vue.openBlock(), Vue.createBlock(_component_el_form_item, {
                    key: 0,
                    class: "refresh-required",
                    label: "Prevent console clearing"
                  }, {
                    default: Vue.withCtx(() => [
                      Vue.createVNode(_component_el_switch, {
                        modelValue: Vue.unref(writeableSettings).preventConsoleClearing,
                        "onUpdate:modelValue": _cache[15] || (_cache[15] = ($event) => Vue.unref(writeableSettings).preventConsoleClearing = $event)
                      }, null, 8, ["modelValue"])
                    ]),
                    _: 1
                  })) : Vue.createCommentVNode("", true)
                ]),
                _: 1
              }),
              Vue.createVNode(_component_el_divider, null, {
                default: Vue.withCtx(() => [
                  _hoisted_8
                ]),
                _: 1
              }),
              Vue.createElementVNode("p", _hoisted_9, " You have downloaded " + Vue.toDisplayString(downloadNum.value) + " manga on this site using nHentai Helper. ", 1),
              Vue.createVNode(_component_el_button, {
                type: "primary",
                icon: Vue.unref(download_default),
                disabled: !downloadNum.value,
                loading: exporting.value,
                onClick: exportHistory
              }, {
                default: Vue.withCtx(() => [
                  _hoisted_10
                ]),
                _: 1
              }, 8, ["icon", "disabled", "loading"]),
              Vue.createVNode(_component_el_button, {
                type: "primary",
                icon: Vue.unref(upload_default),
                loading: importing.value,
                onClick: importHistory
              }, {
                default: Vue.withCtx(() => [
                  _hoisted_11
                ]),
                _: 1
              }, 8, ["icon", "loading"]),
              Vue.createVNode(_component_el_popconfirm, {
                title: "Are you sure?",
                placement: "top",
                onConfirm: clearHistory
              }, {
                reference: Vue.withCtx(() => [
                  Vue.createVNode(_component_el_button, {
                    type: "danger",
                    icon: Vue.unref(delete_default),
                    loading: clearing.value
                  }, {
                    default: Vue.withCtx(() => [
                      _hoisted_12
                    ]),
                    _: 1
                  }, 8, ["icon", "loading"])
                ]),
                _: 1
              }),
              _hoisted_13
            ])
          ]),
          _: 1
        }, 8, ["modelValue"]);
      };
    }
  });
  const SettingsDialog_vue_vue_type_style_index_0_lang = "";
  const compileTemplate = (tpl) => template(tpl, { interpolate: /{{([\s\S]+?)}}/g });
  const getDownloadExt = () => {
    const ext = last(settings.compressionFileName.split("."));
    if (ext)
      return ext.toLowerCase();
    return "zip";
  };
  const getCompressionOptions = () => {
    return {
      streamFiles: settings.compressionStreamFiles,
      compression: settings.compressionLevel > 0 ? "DEFLATE" : "STORE",
      compressionOptions: { level: settings.compressionLevel }
    };
  };
  const getShowAllBtn = () => new Promise((resolve, reject) => {
    const $btn = $__default.default("#show-all-images-button");
    if ($btn.length > 0) {
      resolve($btn);
      return;
    }
    const container = document.getElementById("thumbnail-container");
    if (!container) {
      reject(new Error("Show all button not found"));
      return;
    }
    new MutationObserver((mutations, self2) => {
      mutations.forEach(({ addedNodes }) => {
        const btnContainer = addedNodes[0];
        if ((btnContainer == null ? void 0 : btnContainer.id) === "show-all-images-container") {
          self2.disconnect();
          resolve($__default.default("#show-all-images-button"));
        }
      });
    }).observe(container, { childList: true });
  });
  const createMangaDownloadInfo = (gallery2) => ({
    gallery: gallery2,
    done: 0,
    compressing: false,
    compressingPercent: "0",
    error: false
  });
  const notyConfirmOption = {
    type: "error",
    layout: "bottomRight",
    theme: "nest",
    timeout: false,
    closeWith: []
  };
  const downloadAgainConfirm = async (title, hasQueue = false) => {
    if (hasQueue && settings.autoCancelDownloadedManga) {
      downloadedTip(title);
      return false;
    }
    return new Promise((resolve) => {
      const n2 = new Noty__default.default({
        ...notyConfirmOption,
        text: `"${title}" is already downloaded${hasQueue ? " or in queue" : ""}.<br>Do you want to download again?`,
        buttons: [
          Noty__default.default.button("YES", "btn btn-noty-blue btn-noty", () => {
            n2.close();
            resolve(true);
          }),
          Noty__default.default.button("NO", "btn btn-noty-green btn-noty", () => {
            n2.close();
            resolve(false);
          })
        ]
      });
      n2.show();
    });
  };
  const errorRetryConfirm = (action, noCb, yesCb) => {
    if (settings.autoRetryWhenErrorOccurs) {
      errorRetryTip(action);
      yesCb == null ? void 0 : yesCb();
      return;
    }
    const n2 = new Noty__default.default({
      ...notyConfirmOption,
      text: `Error occurred while ${action}, retry?`,
      buttons: [
        Noty__default.default.button("NO", "btn btn-noty-blue btn-noty", () => {
          n2.close();
          noCb == null ? void 0 : noCb();
        }),
        Noty__default.default.button("YES", "btn btn-noty-green btn-noty", () => {
          n2.close();
          yesCb == null ? void 0 : yesCb();
        })
      ]
    });
    n2.show();
  };
  const downloadedTip = (title) => {
    new Noty__default.default({
      type: "info",
      layout: "bottomRight",
      theme: "nest",
      closeWith: [],
      timeout: 4e3,
      text: `"${title}" is already downloaded or in queue.`
    }).show();
  };
  const errorRetryTip = (action) => {
    new Noty__default.default({
      type: "warning",
      layout: "bottomRight",
      theme: "nest",
      closeWith: [],
      timeout: 3e3,
      text: `Error occurred while ${action}, retrying...`
    }).show();
  };
  class MultiThread {
    constructor(tasks, taskFunc, params) {
      __publicField(this, "threads", []);
      __publicField(this, "taskIndex", 0);
      __publicField(this, "started", false);
      __publicField(this, "aborted", false);
      this.tasks = tasks;
      this.taskFunc = taskFunc;
      this.params = params;
    }
    startThread(threadId) {
      let abortFunc;
      const threadPromise = (async () => {
        while (true) {
          if (this.aborted)
            break;
          const i = this.taskIndex++;
          if (i >= this.tasks.length)
            break;
          const { abort, promise } = this.taskFunc(this.tasks[i], threadId, this.params);
          abortFunc = abort;
          await promise;
        }
      })();
      return {
        abort: () => abortFunc == null ? void 0 : abortFunc(),
        promise: threadPromise
      };
    }
    start() {
      if (this.started)
        throw new Error("Multi-thread started.");
      this.started = true;
      for (let threadId = 0; threadId < settings.threadNum; threadId++) {
        this.threads.push(this.startThread(threadId));
      }
      return {
        abort: () => {
          this.aborted = true;
          this.threads.forEach(({ abort }) => abort());
        },
        promise: Promise.all(this.threads.map(({ promise }) => promise)).then()
      };
    }
  }
  class RequestAbortError extends Error {
    constructor(url) {
      super(`Request abort ${url}`);
    }
  }
  const isAbortError = (e) => e instanceof RequestAbortError;
  const request = (url, responseType, retry = 3) => {
    let abortFunc;
    const dataPromise = new Promise((resolve, reject) => {
      try {
        const req = b({
          method: "GET",
          url,
          responseType,
          onerror: (e) => {
            if (retry === 0) {
              logger.error("Network error", url);
              reject(e);
            } else {
              logger.warn("Network error, retry", url);
              setTimeout(() => {
                const { abort, dataPromise: dataPromise2 } = request(url, responseType, retry - 1);
                abortFunc = abort;
                resolve(dataPromise2);
              }, 1e3);
            }
          },
          onload: ({ status, response }) => {
            if (status === 200)
              resolve(response);
            else if (retry === 0)
              reject(new Error(`${status} ${url}`));
            else {
              logger.warn(status, url);
              setTimeout(() => {
                const { abort, dataPromise: dataPromise2 } = request(url, responseType, retry - 1);
                abortFunc = abort;
                resolve(dataPromise2);
              }, 1e3);
            }
          }
        });
        abortFunc = () => {
          req.abort();
          logger.log("Request abort", url);
          reject(new RequestAbortError(url));
        };
      } catch (error) {
        reject(error);
      }
    });
    return {
      abort: () => abortFunc == null ? void 0 : abortFunc(),
      dataPromise
    };
  };
  const getJSON = (url) => request(url, "json").dataPromise;
  const getText = (url) => request(url).dataPromise;
  var NHentaiImgExt = /* @__PURE__ */ ((NHentaiImgExt2) => {
    NHentaiImgExt2["j"] = "jpg";
    NHentaiImgExt2["p"] = "png";
    NHentaiImgExt2["g"] = "gif";
    return NHentaiImgExt2;
  })(NHentaiImgExt || {});
  const nHentaiImgExtReversed = invert$1(NHentaiImgExt);
  const getTypeFromExt = (ext) => nHentaiImgExtReversed[ext.toLowerCase()];
  const getMediaDownloadUrl = IS_NHENTAI ? (mid, filename) => `https://i.nhentai.net/galleries/${mid}/${filename}` : IS_NHENTAI_TO ? (mid, filename) => `https://cdn.nload.xyz/galleries/${mid}/${filename}` : (mid, filename) => `https://cdn.nhentai.xxx/g/${mid}/${filename}`;
  const getGalleryFromApi = (gid2) => {
    const url = `https://nhentai.net/api/gallery/${gid2}`;
    return getJSON(url);
  };
  const getGalleryFromWebpage = async (gid) => {
    var _a2;
    let doc = document;
    if (!IS_PAGE_MANGA_DETAIL) {
      const html = await getText(`/g/${gid}`);
      const match = (_a2 = /gallery(\(\{[\s\S]+\}\));/.exec(html)) == null ? void 0 : _a2[1];
      if (match) {
        try {
          const gallery = eval(match);
          gallery.id = Number(gid);
        } catch {
          logger.warn("get gallery by eval failed");
        }
      }
      const parser = new DOMParser();
      doc = parser.parseFromString(html, "text/html");
    }
    const $doc = $__default.default(doc.body);
    const english = $doc.find("#info h1").text();
    const japanese = $doc.find("#info h2").text();
    const pages = [];
    let mediaId = "";
    $doc.find("#thumbnail-container img").each((i, img) => {
      var _a3;
      const src = (_a3 = img.dataset.src) != null ? _a3 : img.src;
      const match2 = /\/(\d+)\/(\d+)t?\.(\w+)/.exec(src);
      if (!match2)
        return;
      const [, mid, index2, ext] = match2;
      if (!mediaId)
        mediaId = mid;
      const t = getTypeFromExt(ext);
      if (!t)
        return;
      pages[Number(index2) - 1] = { t };
    });
    if (!english && !japanese || !mediaId || !pages.length) {
      throw new Error("Get gallery info error.");
    }
    return {
      id: Number(gid),
      media_id: mediaId,
      title: {
        english: english || japanese,
        japanese: japanese || english,
        pretty: english || japanese
      },
      images: {
        pages
      }
    };
  };
  const getGallery = async (gid2) => {
    const gallery2 = IS_NHENTAI ? await getGalleryFromApi(gid2) : await getGalleryFromWebpage(gid2);
    logger.log("gallery", gallery2);
    return gallery2;
  };
  const getGalleryInfo = async (gid2) => {
    const {
      id,
      media_id,
      title: { english: english2, japanese: japanese2, pretty },
      images: { pages: pages2 },
      num_pages
    } = await (async () => {
      var _a2, _b2;
      if (gid2)
        return getGallery(gid2);
      const gidFromUrl = (_a2 = /^\/g\/(\d+)/.exec(location.pathname)) == null ? void 0 : _a2[1];
      const localGallery = (_b2 = n._gallery) != null ? _b2 : n.gallery;
      if (localGallery) {
        if (gidFromUrl)
          localGallery.id = Number(gidFromUrl);
        return localGallery;
      }
      if (gidFromUrl)
        return getGallery(gidFromUrl);
      throw new Error("Cannot get gallery info.");
    })();
    const infoPages = (Array.isArray(pages2) ? pages2 : Object.values(pages2)).map(
      (img, i) => ({ i: i + 1, t: NHentaiImgExt[img.t] })
    );
    const info = {
      gid: id,
      mid: media_id,
      title: japanese2 || english2,
      pages: infoPages,
      cfName: compileTemplate(settings.compressionFileName)({
        english: english2,
        japanese: japanese2 || english2,
        pretty,
        id,
        pages: num_pages
      })
    };
    logger.log("info", info);
    return info;
  };
  const downloadGalleryByInfo = async (info, { progressDisplayController, rangeCheckers } = {}) => {
    info.done = 0;
    let { mid, pages: pages2, cfName } = info.gallery;
    if (rangeCheckers == null ? void 0 : rangeCheckers.length) {
      pages2 = pages2.filter(({ i }) => rangeCheckers.some((check) => check(i)));
    }
    let aborted = false;
    info.cancel = () => {
      aborted = true;
      progressDisplayController == null ? void 0 : progressDisplayController.reset();
    };
    progressDisplayController == null ? void 0 : progressDisplayController.bindInfo(info);
    progressDisplayController == null ? void 0 : progressDisplayController.updateProgress();
    const zip = await new JSZip();
    const downloadTask = (page, threadID, { filenameLength, customDownloadUrl }) => {
      if (info.error)
        return { abort: () => {
        }, promise: Promise.resolve() };
      const url = customDownloadUrl ? compileTemplate(customDownloadUrl)({ mid, index: page.i, ext: page.t }) : getMediaDownloadUrl(mid, `${page.i}.${page.t}`);
      logger.log(`[${threadID}] ${url}`);
      const { abort: abort2, dataPromise } = request(url, "arraybuffer");
      return {
        abort: () => {
          logger.log(`[${threadID}] abort`);
          abort2();
        },
        promise: dataPromise.then(async (data) => {
          if (data) {
            zip.file(`${String(page.i).padStart(filenameLength || 0, "0")}.${page.t}`, data);
          }
          info.done++;
          progressDisplayController == null ? void 0 : progressDisplayController.updateProgress();
        }).catch((e) => {
          if (isAbortError(e))
            return;
          info.error = true;
          throw e;
        })
      };
    };
    const multiThread = new MultiThread(pages2, downloadTask, {
      filenameLength: settings.filenameLength === "auto" ? Math.ceil(Math.log10(Math.max(...pages2.map(({ i }) => Number(i))))) : settings.filenameLength,
      customDownloadUrl: settings.customDownloadUrl
    });
    const { abort, promise } = multiThread.start();
    info.cancel = () => {
      aborted = true;
      abort();
      progressDisplayController == null ? void 0 : progressDisplayController.reset();
    };
    if (!aborted)
      await promise;
    if (aborted)
      return;
    return async () => {
      info.compressing = true;
      progressDisplayController == null ? void 0 : progressDisplayController.updateProgress();
      logger.log("start compressing", cfName);
      let lastZipFile = "";
      const onCompressionUpdate = ({ workerId, percent, currentFile }) => {
        if (lastZipFile !== currentFile && currentFile) {
          lastZipFile = currentFile;
          logger.log(`[${workerId}] compressing ${percent.toFixed(2)}%`, currentFile);
        }
        info.compressingPercent = percent.toFixed(2);
        progressDisplayController == null ? void 0 : progressDisplayController.updateProgress();
      };
      if (settings.streamDownload) {
        logger.log("stream mode on");
        const fileStream = streamsaver.createWriteStream(cfName);
        const zipStream = await zip.generateStream(getCompressionOptions(), onCompressionUpdate);
        await zipStream.pipeTo(fileStream);
      } else {
        const data = await zip.generateAsync(getCompressionOptions(), onCompressionUpdate);
        fileSaver.saveAs(new File([data], cfName, { type: "application/zip" }));
      }
      logger.log("completed", cfName);
      progressDisplayController == null ? void 0 : progressDisplayController.complete();
      progressDisplayController == null ? void 0 : progressDisplayController.unbindInfo();
    };
  };
  const addDownloadGalleryTask = (gallery2, { progressDisplayController, markGalleryDownloaded } = {}) => {
    const info = Vue.reactive(createMangaDownloadInfo(gallery2));
    info.cancel = () => {
      progressDisplayController == null ? void 0 : progressDisplayController.reset();
    };
    dlQueue.push(async () => {
      const zipFunc = await downloadGalleryByInfo(info, { progressDisplayController }).catch((e) => {
        progressDisplayController == null ? void 0 : progressDisplayController.error();
        errorRetryConfirm(
          "downloading",
          () => {
            dlQueue.skipFromError().catch(logger.error);
          },
          () => {
            info.error = false;
            dlQueue.restartFromError().catch(logger.error);
          }
        );
        throw e;
      });
      if (zipFunc) {
        zipQueue.push(async () => {
          try {
            await zipFunc();
            markAsDownloaded(gallery2.gid, gallery2.title);
            markGalleryDownloaded == null ? void 0 : markGalleryDownloaded();
          } catch (error) {
            if (!error)
              logger.warn("user abort stream download");
            logger.error(error);
            progressDisplayController == null ? void 0 : progressDisplayController.error();
          }
        }, info);
        zipQueue.start().catch(logger.error);
      }
    }, info);
    dlQueue.start().catch(logger.error);
  };
  class ProgressDisplayController {
    constructor(enableHeadTxt = false, docTitle) {
      __publicField(this, "downloadBtn");
      __publicField(this, "btnTxt");
      __publicField(this, "info");
      this.enableHeadTxt = enableHeadTxt;
      this.docTitle = docTitle;
      this.btnTxt = /* @__PURE__ */ createElement("span", {
        class: "download-zip-txt"
      }, this.defaultBtnText());
      this.downloadBtn = /* @__PURE__ */ createElement("button", {
        class: "btn btn-secondary nhentai-helper-btn download-zip-btn"
      }, /* @__PURE__ */ createElement("i", {
        class: "fa fa-download"
      }), " ", this.btnTxt);
    }
    get compressingHeadText() {
      return this.enableHeadTxt ? `Compressing ${getDownloadExt()} ` : "";
    }
    get downloadingHeadText() {
      return this.enableHeadTxt ? `Downloading ${getDownloadExt()} ` : "";
    }
    defaultBtnText(suffix) {
      if (!this.enableHeadTxt)
        return suffix != null ? suffix : "";
      return `Download ${getDownloadExt()}${suffix ? ` ${suffix}` : ""}`;
    }
    bindInfo(info) {
      this.info = info;
    }
    unbindInfo() {
      this.info = void 0;
    }
    lockBtn(text) {
      this.downloadBtn.setAttribute("disabled", "disabled");
      if (text)
        this.btnTxt.innerText = text;
    }
    releaseBtn() {
      this.downloadBtn.removeAttribute("disabled");
    }
    complete() {
      this.setDocTitle("\u2713");
      this.btnTxt.innerText = this.defaultBtnText("\u2713");
      this.releaseBtn();
    }
    reset() {
      this.setDocTitle();
      this.btnTxt.innerText = this.defaultBtnText();
      this.releaseBtn();
    }
    error() {
      this.releaseBtn();
      this.btnTxt.innerText = "Error";
      this.setDocTitle("\xD7");
    }
    updateProgress() {
      if (!this.info)
        return;
      const { done, compressing, compressingPercent } = this.info;
      if (compressing) {
        this.setDocTitle(`${compressingPercent}%`);
        this.btnTxt.innerText = `${this.compressingHeadText}${compressingPercent}%`;
      } else {
        const total = this.info.gallery.pages.length;
        this.setDocTitle(`${done}/${total}`);
        this.btnTxt.innerText = `${this.downloadingHeadText}${done}/${total}`;
      }
    }
    setDocTitle(prefix) {
      if (!this.docTitle)
        return;
      document.title = prefix ? `[${prefix}] ${this.docTitle}` : this.docTitle;
    }
  }
  class IgnoreController {
    constructor(text = true, status = false) {
      __publicField(this, "ignoreBtn");
      __publicField(this, "icon");
      __publicField(this, "text");
      this.status = status;
      this.icon = /* @__PURE__ */ createElement("i", {
        class: this.iconClass
      });
      if (text)
        this.text = /* @__PURE__ */ createElement("span", null, this.btnText);
      this.ignoreBtn = /* @__PURE__ */ createElement("button", {
        class: "btn btn-secondary nhentai-helper-btn ignore-btn"
      }, this.icon, " ", this.text);
    }
    setStatus(status) {
      this.status = status;
      this.updateBtn();
    }
    getStatus() {
      return this.status;
    }
    get iconClass() {
      return this.status ? "fa fa-eye-slash" : "fa fa-eye";
    }
    get btnText() {
      return this.status ? "Unignore this" : "Ignore this";
    }
    updateBtn() {
      this.icon.className = this.iconClass;
      if (this.text)
        this.text.innerText = this.btnText;
    }
  }
  const initDetailPage = async () => {
    const progressDisplayController = new ProgressDisplayController(true, document.title);
    const { downloadBtn } = progressDisplayController;
    const pagesInput = /* @__PURE__ */ createElement("input", {
      class: "pages-input",
      placeholder: "Download pages (e.g. 1-10,12,14,18-)"
    });
    $__default.default("#info > .buttons").append(downloadBtn).after(pagesInput);
    const gallery2 = await getGalleryInfo();
    let ignoreController;
    if (settings.showIgnoreButton) {
      const isDownloaded = await isDownloadedByGid(gallery2.gid);
      ignoreController = new IgnoreController(true, isDownloaded);
      const { ignoreBtn } = ignoreController;
      ignoreBtn.addEventListener("click", () => {
        const ignore = ignoreController.getStatus();
        if (ignore)
          unmarkAsDownloaded(gallery2.gid, gallery2.title);
        else
          markAsDownloaded(gallery2.gid, gallery2.title);
        ignoreController.setStatus(!ignore);
      });
      $__default.default("#info > .buttons").append(ignoreBtn);
    }
    downloadBtn.addEventListener("click", async () => {
      var _a2;
      const rangeCheckers = pagesInput.value.split(",").filter((range) => !Number.isNaN(parseInt(range))).map((range) => {
        const [start, end] = range.split("-").map((num) => parseInt(num));
        if (typeof end === "undefined")
          return (page) => page === start;
        else if (Number.isNaN(end))
          return (page) => page >= start;
        else
          return (page) => start <= page && page <= end;
      });
      progressDisplayController.lockBtn();
      try {
        const downloaded = await isDownloadedByGid(gallery2.gid) || await isDownloadedByTitle(gallery2.title);
        if (downloaded && !await downloadAgainConfirm(gallery2.title)) {
          progressDisplayController.reset();
          markAsDownloaded(gallery2.gid, gallery2.title);
          ignoreController == null ? void 0 : ignoreController.setStatus(true);
          return;
        }
        await ((_a2 = await downloadGalleryByInfo(createMangaDownloadInfo(gallery2), {
          progressDisplayController,
          rangeCheckers
        })) == null ? void 0 : _a2());
        markAsDownloaded(gallery2.gid, gallery2.title);
        ignoreController == null ? void 0 : ignoreController.setStatus(true);
      } catch (error) {
        progressDisplayController.error();
        logger.error(error);
      }
    });
    applyAutoShowAll();
  };
  const applyAutoShowAll = () => {
    if (settings.autoShowAll) {
      getShowAllBtn().then(($btn) => $btn.trigger("click")).catch(logger.error);
    }
  };
  const createLangFilter = () => {
    const langFilter = /* @__PURE__ */ createElement("select", {
      id: "lang-filter",
      onChange: () => {
        filterLang(langFilter.value);
        sessionStorage.setItem("lang-filter", langFilter.value);
      }
    }, IS_NHENTAI_TO ? /* @__PURE__ */ createElement(Fragment, null, /* @__PURE__ */ createElement("option", {
      value: "0"
    }, "None"), /* @__PURE__ */ createElement("option", {
      value: "10197"
    }, "Chinese"), /* @__PURE__ */ createElement("option", {
      value: "2"
    }, "Japanese"), /* @__PURE__ */ createElement("option", {
      value: "19"
    }, "English")) : /* @__PURE__ */ createElement(Fragment, null, /* @__PURE__ */ createElement("option", {
      value: "0"
    }, "None"), /* @__PURE__ */ createElement("option", {
      value: "29963"
    }, "Chinese"), /* @__PURE__ */ createElement("option", {
      value: "6346"
    }, "Japanese"), /* @__PURE__ */ createElement("option", {
      value: "12227"
    }, "English")));
    $__default.default("ul.menu.left").append(
      /* @__PURE__ */ createElement("li", {
        style: { padding: "0 10px", userSelect: "none" }
      }, "Filter: ", langFilter)
    );
    return langFilter;
  };
  const filterLang = (lang, $node) => {
    const getNode = $node ? (selector) => $node.find(selector) : (selector) => $__default.default(selector);
    if (lang === "0")
      getNode(".gallery").removeClass("hidden");
    else {
      getNode(`.gallery[data-tags~=${lang}]`).removeClass("hidden");
      getNode(`.gallery:not([data-tags~=${lang}])`).addClass("hidden");
    }
  };
  const initListPage = () => {
    $__default.default(".gallery").each(initGallery);
    const langFilter = initLangFilter();
    initShortcut();
    restoreDownloadQueue();
    const contentEl = $__default.default("#content")[0];
    if (contentEl) {
      new MutationObserver((mutations) => {
        mutations.forEach(({ addedNodes }) => {
          addedNodes.forEach((node) => {
            const $el = $__default.default(node);
            $el.find(".gallery").each(initGallery);
            const lang = langFilter.value;
            if (lang)
              filterLang(lang, $el);
          });
        });
      }).observe(contentEl, { childList: true });
    }
  };
  const initLangFilter = () => {
    const langFilter = createLangFilter();
    const storedLangFilterVal = sessionStorage.getItem("lang-filter");
    if (storedLangFilterVal) {
      langFilter.value = storedLangFilterVal;
      filterLang(storedLangFilterVal);
    }
    return langFilter;
  };
  const initShortcut = () => {
    $__default.default(document).on("keydown", (event) => {
      switch (event.key) {
        case "ArrowLeft":
          $__default.default(".pagination .previous").trigger("click");
          break;
        case "ArrowRight":
          $__default.default(".pagination .next").trigger("click");
          break;
      }
    });
  };
  const restoreDownloadQueue = () => {
    const galleriesJson = sessionStorage.getItem("downloadQueue");
    if (!galleriesJson)
      return;
    try {
      const galleries = JSON.parse(galleriesJson);
      for (const gallery2 of galleries) {
        addDownloadGalleryTask(gallery2);
      }
    } catch (error) {
      logger.error(error);
    }
  };
  const initGallery = function() {
    var _a2;
    const $gallery = $__default.default(this);
    if ($gallery.attr("init"))
      return;
    $gallery.attr("init", "true");
    const $a = $gallery.find("a.cover");
    if (settings.openOnNewTab)
      $a.attr("target", "_blank");
    const gid2 = (_a2 = /\/g\/([0-9]+)/.exec($a.attr("href"))) == null ? void 0 : _a2[1];
    if (!gid2)
      return;
    const progressDisplayController = new ProgressDisplayController();
    const { downloadBtn } = progressDisplayController;
    $gallery.append(downloadBtn);
    let ignoreController;
    let galleryTitle;
    const markGalleryDownloaded = () => {
      $gallery.addClass("downloaded");
      ignoreController == null ? void 0 : ignoreController.setStatus(true);
    };
    const unmarkGalleryDownloaded = () => {
      $gallery.removeClass("downloaded");
      ignoreController == null ? void 0 : ignoreController.setStatus(false);
    };
    void isDownloadedByGid(gid2).then((downloaded) => {
      if (downloaded)
        markGalleryDownloaded();
      if (settings.showIgnoreButton) {
        ignoreController = new IgnoreController(false, downloaded);
        const { ignoreBtn } = ignoreController;
        ignoreBtn.addEventListener("click", () => {
          const ignore = ignoreController.getStatus();
          if (ignore) {
            unmarkGalleryDownloaded();
            unmarkAsDownloaded(gid2, galleryTitle);
          } else {
            markGalleryDownloaded();
            markAsDownloaded(gid2, galleryTitle);
          }
        });
        $gallery.append(ignoreBtn);
      }
    });
    let gallery2;
    let skipDownloadedCheck = false;
    const startDownload = async () => {
      if (!settings.autoCancelDownloadedManga) {
        progressDisplayController.lockBtn("Wait");
      }
      if (!skipDownloadedCheck && await isDownloadedByGid(gid2)) {
        const title = $gallery.find(".caption").text();
        if (!await downloadAgainConfirm(title, true)) {
          progressDisplayController.reset();
          markGalleryDownloaded();
          return;
        }
        skipDownloadedCheck = true;
      }
      if (settings.autoCancelDownloadedManga) {
        progressDisplayController.lockBtn("Wait");
      }
      if (!gallery2) {
        try {
          gallery2 = await getGalleryInfo(gid2);
          galleryTitle = gallery2.title;
        } catch (error) {
          logger.error(error);
          progressDisplayController.error();
          errorRetryConfirm("getting information", void 0, startDownload);
          return;
        }
      }
      if (!skipDownloadedCheck && (await isDownloadedByTitle(gallery2.title) || dlQueue.queue.some(
        ({
          info: {
            gallery: { title }
          }
        }) => title === gallery2.title
      )) && !await downloadAgainConfirm(gallery2.title, true)) {
        progressDisplayController.reset();
        markAsDownloaded(gid2, gallery2.title);
        markGalleryDownloaded();
        return;
      }
      addDownloadGalleryTask(gallery2, { progressDisplayController, markGalleryDownloaded });
    };
    downloadBtn.addEventListener("click", startDownload);
  };
  class StyleInjector {
    constructor(style) {
      __publicField(this, "styleNode");
      this.styleNode = /* @__PURE__ */ createElement("style", null, style);
    }
    inject() {
      document.head.append(this.styleNode);
    }
    remove() {
      this.styleNode.remove();
    }
  }
  const initOnlineViewPage = () => {
    if (!IS_NHENTAI)
      initViewMode();
  };
  const initViewMode = () => {
    const style = new StyleInjector(
      "#image-container img{width:auto;max-width:calc(100vw - 20px);max-height:100vh}"
    );
    const viewModeText = ["[off]", "[on]"];
    let viewMode = h("online_view_mode", 0);
    applyOnlineViewStyle(!!viewMode, style);
    const btnText = /* @__PURE__ */ createElement("span", null, viewModeText[viewMode]);
    const btn = /* @__PURE__ */ createElement("button", {
      id: "online-view-mode-btn",
      class: "btn btn-secondary"
    }, "100% view height ", btnText);
    btn.addEventListener("click", () => {
      viewMode = 1 - viewMode;
      u("online_view_mode", viewMode);
      btnText.innerText = viewModeText[viewMode];
      applyOnlineViewStyle(!!viewMode, style);
    });
    $__default.default("#page-container").prepend(btn);
  };
  const applyOnlineViewStyle = (enable, style) => {
    if (enable)
      style.inject();
    else
      style.remove();
  };
  const initPage = () => {
    if (IS_PAGE_MANGA_LIST) {
      initListPage();
      applyPjax();
    } else if (IS_PAGE_MANGA_DETAIL)
      initDetailPage().catch(logger.error);
    else if (IS_PAGE_ONLINE_VIEW)
      initOnlineViewPage();
  };
  const applyPjax = () => {
    $__default.default(document).pjax(".pagination a, .sort a", {
      container: "#content",
      fragment: "#content",
      timeout: 1e4
    });
    $__default.default(document).on("pjax:end", () => {
      $__default.default(".pagination a").each(function() {
        const $this = $__default.default(this);
        const href = $this.attr("href");
        const isPathname = href.startsWith("/");
        const url = isPathname ? new URL(href, location.origin) : new URL(href);
        url.searchParams.delete("_pjax");
        $this.attr("href", isPathname ? `${url.pathname}${url.search}` : url.href);
      });
      applyLazyLoad();
    });
  };
  const applyLazyLoad = () => {
    const { _n_app } = n;
    if (_n_app) {
      _n_app.install_lazy_loader();
      _n_app.install_blacklisting();
    }
  };
  const createAppAndMount = (component, appInitFunc) => {
    const el = document.createElement("div");
    document.body.append(el);
    const app = Vue.createApp(component);
    appInitFunc == null ? void 0 : appInitFunc(app);
    return app.mount(el);
  };
  const initSettingsDialogApp = functionOnce(
    () => createAppAndMount(_sfc_main, (app) => {
      app.use(elementPlus());
    })
  );
  const openSettingsDialog = () => {
    const dialog = initSettingsDialogApp();
    dialog.open();
  };
  createAppAndMount(_sfc_main$1);
  initPage();
  m("Settings", openSettingsDialog);
})($, null, Vue, EventEmitter3, saveAs, localforage, MD5, Comlink, Noty, streamSaver);