MUTED64 / SearchEngineJumpPlus 搜索引擎快捷跳转+

// ==UserScript==
// @name           SearchEngineJumpPlus 搜索引擎快捷跳转+
// @author         NLF & 锐经(修改) & iqxin(修改) & MUTED64(修改)
// @contributor    MUTED64
// @description    Fork版本搜索引擎跳转脚本,优化一些使用体验
// @version        5.31.1
// @created        2011-07-02
// @lastUpdated    2022-12-18

// @namespace      https://greasyfork.org/en/scripts/454280-searchenginejumpplus
// @homepage       https://github.com/MUTED64/SearchEngineJumpPlus
// @require        https://greasyfork.org/scripts/408009-togbk/code/toGBK.js?version=832799
// @require        https://greasyfork.org/scripts/456710-searchenginejumpplusenginelist/code/SearchEngineJumpPlusEngineList.js?version=1129525
// @require        https://greasyfork.org/scripts/456711-searchenginejumpplusrules/code/SearchEngineJumpPlusRules.js?version=1129524
// @resource       GLOBAL_STYLE https://greasyfork.org/scripts/455977-searchenginejumpplusglobalstyle/code/SearchEngineJumpPlusGlobalStyle.user.css
// @icon           data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFSElEQVR4nMWXX4hdVxXGf2vfe89kJg61ia0DYzMTMWnoQ0FJtKmtJsFixT8DBSmYtGMLgq0PCqMEKwmxYzSGyUPBB7XRNi0FC6JtwYovgcS0klJD8SHakoExYhLQFkwn9/aeOfv7fDi3SStJ5o4muN4O7L32b33rz94H/s8WS10cvR3yVQaY++wnkESkwDK2sMy1EwXDtzRRziBhu+dGDG48smSA5kUP//wmAFIkrNwiGMOsBzYAQwTzEEeBY8BJO1fYtF+4laGPv/i/Afz1C1sAYwngZiKmsDcDI0DrHUtL4DRwMGAmUnVcCtpHPsrQbS/1DZDe+VFHblKziIjYBjwD3Iu5ARBwBjgJnAkwMAa+z+ZZqXEX8VZg0T784aUDzH3uk0DtVQvlVsMjwGpMB3gauAu8ieB2YDPwxR5gF/gQ+MeoNUFzACI4d+imvgDOp0BVRWo2AW62eRi8wvY/wNtrgGhDL+7a/gIcBLYBu4HrsPdSzr8K/JlcLk2BaCQstSxN2VptuYO93an7WES0UyORGg1Wfu0QKivyQhfb56yhn4B3Ynew1kD1oDTfJF20vi8NYBvjMVubbWHrOdtPhwaAYPVvfs8Hf1u32bJbDtXVbgFvAj4AOgTGzhPhGMdV/wCvbtmAJSyttzRiuWv7CdttAlY/f/iimwdvfQGiAfmtczg/jnOJ8/txtRbnvgAu6FSPtg1AC3wGPAvgWGRYqiSowLwC1Ru4GoFyFPc3ZM8DfGPLB1jZXlhe74sS6AAc+O6vL+tg6LaX2LP/SSA6tkpcYeee36/0D/C7Ve9BwZs97iLMEMDAE5N07z1wSQebvl/y3KkAGDIUsrHpRp8ACeDGw38kZdPMPtrILhvZ1yZ5TZJxvnwuW40GzSSaDa1vJq1oJXVbKZ9qpv5qoO6Cqr5ULB+zfNrygOX7LS+PlCgeu+eimz/1w0yWaTTScIqYTEERcDoiXovFauddAAA22CeRDyKD/Bnkbd32PNgUj09S/GwrUMt+x14hiWVFI1LEVyPidggi4hfOnuv3nr8AEGC5sj1j+4TtAcu7i4HlDwLLqRawMmtmnidn6JYLGIa7C/mbwHeAgYATQexPjVCVxcZd7SUACDCEfRyznXoMr8Sawf4lcDdwI7AKWAdss/0r2dOyr6kFpCn7hiyPRlDY5mM7z10W4F1KFT+/p6ZwDkgT2HuN19Tz3yXWG+NnJ8uR9h0FSStSRAFBwAmbpu3xbP/T9rzkp2zvtt2RzcvfG15EAaC8/8m6FkgmpWdsTyD/COtv9esnj1haZXvEtiXP2d5jc6es+3qHv8/2uO1v2d4hedA2H/n2vxZX4LwS+78E1PcDqprAOPZao9Gxs5PNkc6dXUKnIuI1Z8+lRijLo8AR2+OWqeeBS8n7bE8bd2x4Zc97FwcAaP307vqyiXi7QzBi7OyXGel8GkJEBAFUWUREIXlnL/LCvgBheZ9h2lLHyvxp5rrFAZZiG3e16zliBm3vsD0lu6i5ja0awppWrjrKmeOPjAL/UQP/rf1h11BPJHckT/dkL+vDjeXC0pRy3qGcB22x9oHZKwcAcPTh5UimzrWnexGXlrCFlAvlakq5eiiX3eLtSXnFAABe3j1c/0PgTp1z77NUKmesjHMulKuttq9X/eq+sgAAx35wTZ0OqWNrWqr2KVelqoqcF3DOL1r5dStfHQCoW03K9ApuWrnam/PCnHN+StZDRHSK1jLgCnXBpeymr/8dS+SFbmH7eiu/TkQnNRrkqmL20XVXFwBg7QOzRASSsDJFaxndssPso+uu9tH92b8BowSyPc/iZtEAAAAASUVORK5CYII=
// @license        MIT

// @match          *://**/*
// @exclude        *://mega.nz/*
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_addStyle
// @grant          GM_deleteValue
// @grant          GM_setClipboard
// @grant          GM_registerMenuCommand
// @grant          GM_openInTab
// @grant          GM_getResourceText
// @grant          window.onurlchange
// @run-at         document-end

// ==/UserScript==

(function () {
  "use strict";

  startScript();
  listenUrlChange();

  // For some websites with iframe and some websites need delay to load
  function startScript() {
    if (window.self != window.top) return;

    console.info(
      `\n%c ${GM_info.script.name} v${GM_info.script.version} \n%c 问题反馈(GitHub):\t\thttps://github.com/MUTED64/SearchEngineJumpPlus/issues/new\t\t\t\t\t\t\t\n%c 问题反馈(GreasyFork):\thttps://greasyfork.org/scripts/454280-searchenginejumpplus-搜索引擎快捷跳转/feedback\t\n`,
      "color:#eee;background:#444;padding:6px 0;border-radius:6px 6px 0 0;",
      "color:#444;background:#eee;padding:6px 0;border-radius:0 6px 0 0",
      "color:#444;background:#eee;padding:6px 0;border-radius:0 0 6px 6px;"
    );

    const delayList = [
      /^https?:\/\/google\.infinitynewtab\.com\/\?q/,
      /^https?:\/\/www\.zhihu\.com\/search\?/,
      /^https?:\/\/www\.iciba\.com\/word\?/,
      /^https?:\/\/neeva\.com\/search\?/i,
      /^https?:\/\/s\.taobao\.com\/search/,
      /^https?:\/\/y\.qq\.com\/n\/ryqq\/search/i,
      /^https?:\/\/www\.quora\.com\/search\?/i,
      /^https?:\/\/search\.bilibili\.com\/*/,
    ];
    const needDelay = delayList.some(
      (delaySite) => location.href.search(delaySite) !== -1
    );

    if (needDelay) {
      setTimeout(function () {
        const isRunning = document.querySelector("sejspan");
        if (isRunning) {
          return;
        } else {
          mainLogic();
        }
      }, 1000);
    } else {
      mainLogic();
    }
  }

  // For SPA websites with javascript router
  function listenUrlChange() {
    if (window.onurlchange === null) {
      let lastURL = decodeURI(location.href).replaceAll(" ", "+");
      window.addEventListener("urlchange", (e) => {
        const newURL = decodeURI(e.url).replaceAll(" ", "+");
        if (lastURL === newURL) return;
        lastURL = newURL;
        document.querySelectorAll("sejspan")?.forEach((i) => i.remove());
        mainLogic();
      });
    }
  }

  function mainLogic() {
    const rules = searchEngineJumpPlusRules;
    let engineList = searchEngineJumpPlusEngines;

    // 有些图标需要重复使用
    const icon = {
      edit: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAACDklEQVR4nJXVzUtUURjH8Y/mSNKkki2iwiApxHQ1q/6C+gusoCB6oxbRRqFNL4sWtRKqhVSLIDe1CqpNiwjKIilKLKKFEr2Z2qI0xxHN0+LOm+PMOPOc1T2H7/f5ncO991BdNer30zmxKrl0xV2zKJjRoy6aqkkvbbdVLPuUq+8+5uGXnVILki7qsxgtNDtrTNLcijHvrdYsft0/wQ8DZgSzeqMUDW4IJceYHcvwCd1ies0KZvWI1TnhIH6574Olgg0E74zmhZ902j304by4Cxp5LPjtQNmjy3XPVK2rgmCBCcGgdVXhdBgUBCMEwVMNVeIvBMFLifKC8vgrndFBlRJUhJcWFMd3ZfGuzFRxwWrdu3KTxQQVhi8lqApfKVhf0d4bc2/OckG9Pkur7r3TEw+1FRO0GxdM2Vc2/HHBgr1If935UTfigbt5+C27MeSo9+m5GJYitlCwWR2G8oQZ/FgWX1aFgnZMG852v5nFR4rhMn+2dDVJYFpKqy0SDksUhF9FsE0bWgyIa9bIanihoEUcDTrSz4ueOVMOLxQkzVkrZcaoNz755rmpcnihYNghm3w26Ys/5cGcIKgRBJDyqCIquj8C1PqKZvHK+qVrJ5bMRwmGterU64pkkZupWO3RjXkzUZj9+jVZMGK6IsEaHTbgjpOSUYZL/pa5m4qPIbtyznpHvJaqGB53O33h4T/3VzLuzDhE6AAAAABJRU5ErkJggg==",
      del: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAADAFBMVEUAAADsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVH///9VVVVWVlZXV1dYWFhZWVlaWlpbW1tcXFxdXV1eXl5fX19gYGBhYWFiYmJjY2NkZGRlZWVmZmZnZ2doaGhpaWlqampra2tsbGxtbW1ubm5vb29wcHBxcXFycnJzc3N0dHR1dXV2dnZ3d3d4eHh5eXl6enp7e3t8fHx9fX1+fn5/f3+AgICBgYGCgoKDg4OEhISFhYWGhoaHh4eIiIiJiYmKioqLi4uMjIyNjY2Ojo6Pj4+QkJCRkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqbm5ucnJydnZ2enp6fn5+goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6vr6+wsLCxsbGysrKzs7O0tLS1tbW2tra3t7e4uLi5ubm6urq7u7u8vLy9vb2+vr6/v7/AwMDBwcHCwsLDw8PExMTFxcXGxsbHx8fIyMjJycnKysrLy8vMzMzNzc3Ozs7Pz8/Q0NDR0dHS0tLT09PU1NTV1dXW1tbX19fY2NjZ2dna2trb29vc3Nzd3d3e3t7f39/g4ODh4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7///8dej9TAAAAU3RSTlMAAABm7P/sZgAAABPO////zhQAAB/i/////////+IfAAAe4fvk4AAAAAAd/+Q3GxwAFR85FQBjz+LPY+v////r6//////rZM/h4c9jABUdHRUAAP0EcPoAAAEuSURBVHic7ZRnc8IwDIbdEUZHGB0kDsMOMcOMttBBB93Qvcj//y9VjB0Czh13/dz3ixT5OVmSYyMktLK6tm74oYxEMpVGUW1sbm2bM8DMZHP5OWBnd2+/YNnYAWHbKhRL5cocQKjrWFWPuSDmVS3HpUQu1eoNQkiTM9xqd7oHoG6n3cKMNyHcqNfQ4VGPUsr7nh0FbK/PIdw7PkGnZwOZNrqF9AfnF+jyaigLixYp/eH1Dbq9u4eAHyOAHh5HaPz0DCnjANjm5fUNvX98QoGCxyo5Fjmh0K/vH2hzAi0KnqnymMgJrU6gzemQBM+DZpX1/XBYUyAYTTAuZTUg+Aw8Zf+BvwJLR730sPTjXgD0H2YB0BUClXKpGAeE1y+fy2ZMfX12gdOpZMLQAfkE/AL7e5vGZF+dOQAAAABJRU5ErkJggg==",
      setting: `<svg style="width: 16px;" class="icon" viewBox="0 0 512 512"><path d="M262.29 192.31a64 64 0 1057.4 57.4 64.13 64.13 0 00-57.4-57.4zM416.39 256a154.34 154.34 0 01-1.53 20.79l45.21 35.46a10.81 10.81 0 012.45 13.75l-42.77 74a10.81 10.81 0 01-13.14 4.59l-44.9-18.08a16.11 16.11 0 00-15.17 1.75A164.48 164.48 0 01325 400.8a15.94 15.94 0 00-8.82 12.14l-6.73 47.89a11.08 11.08 0 01-10.68 9.17h-85.54a11.11 11.11 0 01-10.69-8.87l-6.72-47.82a16.07 16.07 0 00-9-12.22 155.3 155.3 0 01-21.46-12.57 16 16 0 00-15.11-1.71l-44.89 18.07a10.81 10.81 0 01-13.14-4.58l-42.77-74a10.8 10.8 0 012.45-13.75l38.21-30a16.05 16.05 0 006-14.08c-.36-4.17-.58-8.33-.58-12.5s.21-8.27.58-12.35a16 16 0 00-6.07-13.94l-38.19-30A10.81 10.81 0 0149.48 186l42.77-74a10.81 10.81 0 0113.14-4.59l44.9 18.08a16.11 16.11 0 0015.17-1.75A164.48 164.48 0 01187 111.2a15.94 15.94 0 008.82-12.14l6.73-47.89A11.08 11.08 0 01213.23 42h85.54a11.11 11.11 0 0110.69 8.87l6.72 47.82a16.07 16.07 0 009 12.22 155.3 155.3 0 0121.46 12.57 16 16 0 0015.11 1.71l44.89-18.07a10.81 10.81 0 0113.14 4.58l42.77 74a10.8 10.8 0 01-2.45 13.75l-38.21 30a16.05 16.05 0 00-6.05 14.08c.33 4.14.55 8.3.55 12.47z" fill="none" stroke="#555" stroke-linecap="round" stroke-linejoin="round" stroke-width="42"/></svg>`,
    };

    const scriptSettingData = {
      status: 1,
      message:
        "$相关说明$(status: 这个在将来或许很重要)..." +
        "(version: 若有新功能加入,靠这个版本号识别)..." +
        "(addSearchItems: 允许更新时,添加新的搜索网站到你的搜索列表)..." +
        "(modifySearchItems: 允许更新时,修改你的搜索列表中的项目)..." +
        "(closeBtn: 设置页面右上角的“关闭”按钮是否显示。true显示,false隐藏)..." +
        "(newtab: 新标签页打开。0为默认设置,1为新标签页打开)..." +
        "(foldlist: 折叠当前搜索分类列表。true为折叠,false为展开。)..." +
        "(setBtnOpacity: 设置按钮的透明度,值为0-1之间的数,0为透明,1为完全显示,中间值半透明。注:-1为直接关闭按钮,关闭之前请确定自己知道如何再次打开它)..." +
        "(debug: debug模式,开启后,控制台会输出一些信息,“关闭并保存”按钮将不会在刷新页面)..." +
        "(fixedTop: 将搜索栏固定到顶端。 true开启,false关闭)..." +
        "(fixedTopUpward: 固定顶端后,搜索栏下拉不会出现,只有上拉时才出现。 true开启,false关闭)..." +
        "(baiduOffset: 在百度页面鼠标划过的菜单会出现位移,若有使用其他的style样式,可以修改这个来修复二级菜单的偏移)..." +
        "(getIcon: 自己添加搜索后获取图标的方式。0为自动,能连接谷歌的情况下用谷歌获取,无法连接的情况下,域名加favicon.ico获取;1为域名加favicon获取,2为使用谷歌获取,3为使用dnspot的服务获取(不建议使用)。或者添加网址,关键字使用%s代替,未测试)..." +
        "(allOpen:一键搜索,点击相关分类后,打开该分类下的所有搜索)..." +
        "(HideTheSameLink:隐藏同站链接。默认开启,百度页面会隐藏百度搜索。如果想在同一个搜索网站,但是想通过不同语言来搜索, 可以选择false来实现)..." +
        "(center:是否居中显示,主要是为了兼容脚本 ac 百度  : 0 不居中,强制在左。 1, 强制居中 。 2,自动判断)..." +
        "(icon: 图标的显示方式, 0 关闭文字, 只保留图标, 1 显示网站图标,2 显示抽象图标。当脚本中不存在抽象图标时,显示网站图标)..." +
        "(transtion: 是否有动画效果, true为开启所有动画效果,false关闭所有动画(包括模糊效果)。)" +
        "(selectSearch: 划词搜索功能, true为开启划词搜索,false关闭)" +
        "(engineDetails: 第一个值为分类列表标题名称,第二个值与enginelist相关联,必须匹配,第三个值true为显示列表,false为禁用列表。排列顺序与跳转栏上的显示顺序相同,可以用它将分类列表按自己喜欢排序)..." +
        "(engineList: 各个搜索的相关信息)" +
        "(rules: 已弃用--将搜索样式插入到目标网页,同脚本中的rules设置相同,优先级高于脚本中自带的规则。自带了360搜索,可仿写)...",
      version: GM_info.script.version,
      addSearchItems: true,
      modifySearchItems: true,
      closeBtn: true,
      newtab: 0,
      foldlist: true,
      setBtnOpacity: 0.7,
      debug: false,
      fixedTop: true,
      fixedTopUpward: false,
      baiduOffset: -120,
      getIcon: 0,
      allOpen: false,
      HideTheSameLink: true,
      center: 2,
      icon: 1,
      transtion: true,
      selectSearch: true,
      engineDetails: [
        ["网页", "web", true],
        ["翻译", "translate", true],
        ["知识", "knowledge", true],
        ["图片", "image", true],
        ["视频", "video", true],
        ["音乐", "music", true],
        ["学术", "scholar", false],
        ["社交", "sociality", true],
        ["购物", "shopping", true],
        ["下载", "download", false],
        ["新闻", "news", false],
        ["常用", "mine", false],
      ],
      engineList: engineList,
    };
    // --------------------可设置项结束------------------------
    class Settings {
      #storedSettingData = GM_getValue("searchEngineJumpData");
      #scriptSettingData = scriptSettingData;
      settingData;

      constructor() {
        this.initSettings();
      }

      #isVersionOutdated(storedSettingVersion, currentVersion) {
        storedSettingVersion = storedSettingVersion.toString();
        currentVersion = currentVersion.toString();
        const arr1 = storedSettingVersion.split(".");
        const arr2 = currentVersion.split(".");
        const length1 = arr1.length;
        const length2 = arr2.length;
        const minlength = Math.min(length1, length2);
        let i = 0;
        for (i; i < minlength; i++) {
          let a = parseInt(arr1[i]);
          let b = parseInt(arr2[i]);
          if (a > b) {
            return false; // 版本超前
          } else if (a < b) {
            return true; // 版本过时
          }
        }
        if (length1 > length2) {
          for (let j = i; j < length1; j++) {
            if (parseInt(arr1[j]) != 0) {
              return false; // 版本超前
            }
          }
          return false; // 版本相同
        } else if (length1 < length2) {
          for (let j = i; j < length2; j++) {
            if (parseInt(arr2[j]) != 0) {
              return true; // 版本过时
            }
          }
          return false; // 版本相同
        }
        return false; // 版本相同
      }

      #checkSettingDataIntegrity() {
        for (const value in this.#scriptSettingData) {
          if (!this.settingData.hasOwnProperty(value)) {
            console.warn(`属性不存在:${value}`);
            this.settingData[value] = this.#scriptSettingData[value];
            GM_setValue("searchEngineJumpData", this.settingData);
          }
        }
      }

      #checkUpdate() {
        if (
          this.#isVersionOutdated(
            this.#storedSettingData.version,
            this.#scriptSettingData.version
          )
        ) {
          this.settingData.version = this.#scriptSettingData.version;
          this.settingData.message = this.#scriptSettingData.message;

          // 5.29.9 更新
          if (
            this.settingData.setBtnOpacity === "0.2" &&
            this.#isVersionOutdated(this.#storedSettingData.version, "5.29.9")
          ) {
            this.settingData.setBtnOpacity = "0.7";
          }

          // 5.30.2 更新
          if (
            this.#isVersionOutdated(this.#storedSettingData.version, "5.30.2")
          ) {
            this.deleteOutdatedSearchItems(["https://so.letv.com/s?wd=%s"]);
            this.modifyOutdatedSearchItems(
              "https://s.weibo.com/weibo/%s",
              "https://s.weibo.com/weibo/?q=%s"
            );
          }

          // 5.30.4 更新
          if (
            this.#isVersionOutdated(this.#storedSettingData.version, "5.30.4")
          ) {
            this.modifyOutdatedSearchItems(
              "https://www.startpage.com/do/asearch$post$query",
              "https://www.startpage.com/sp/search$post$query"
            );
          }

          // 5.31.1 更新
          if (
            this.#isVersionOutdated(this.#storedSettingData.version, "5.31.1")
          ) {
            this.modifyOutdatedSearchItemsTarget("https://zh.moegirl.org/%s");
            this.modifyOutdatedSearchItemsTarget("https://tieba.baidu.com/f?kw=%s&ie=utf-8");
            this.modifyOutdatedSearchItemsTarget("https://github.com/search?utf8=✓&q=%s");
          }

          console.info(
            `\n%c ${GM_info.script.name} 设置已更新 \n%c 本地设置版本号:\t\t${
              this.#storedSettingData.version
            }\t\t\t\t\t\t\t\n%c 当前版本号:\t\t\t${
              this.settingData.version
            }\t\t\t\t\t\t\t\n`,
            "color:#eee;background:#444;padding:6px 0;border-radius:6px 6px 0 0;",
            "color:#444;background:#eee;padding:6px 0;border-radius:0 6px 0 0",
            "color:#444;background:#eee;padding:6px 0;border-radius:0 0 6px 6px;"
          );
          GM_setValue("searchEngineJumpData", this.settingData);
        }
      }

      initSettings() {
        if (this.#storedSettingData) {
          this.settingData = Object.assign({}, this.#storedSettingData);
          this.#checkSettingDataIntegrity();
          this.#checkUpdate();
        } else {
          this.settingData = this.#scriptSettingData;
          GM_setValue("searchEngineJumpData", this.settingData);
        }

        this.initEngineCategories();
      }

      initEngineCategories() {
        this.settingData.engineList.engineCategories = [];
        for (
          let engineCategoryIndex = 0;
          engineCategoryIndex < this.settingData.engineDetails.length;
          engineCategoryIndex++
        ) {
          if (this.settingData.engineDetails[engineCategoryIndex][2]) {
            this.settingData.engineList.engineCategories[engineCategoryIndex] =
              this.settingData.engineDetails[engineCategoryIndex];
          } else {
            this.settingData.engineList.engineCategories[-engineCategoryIndex] =
              this.settingData.engineDetails[engineCategoryIndex];
          }
        }
      }

      getMatchedRule() {
        for (const rule of [...rules]) {
          if (rule.url.test(location.href)) {
            return rule;
          }
        }
        return null;
      }

      // 更新已过期的搜索链接
      modifyOutdatedSearchItems(oldURL, newURL) {
        for (const value in this.settingData.engineList) {
          var item = this.settingData.engineList[value];
          for (let i = 0; i < item.length; i++) {
            if (item[i].url === oldURL) {
              item[i].url = newURL;
            }
          }
        }
      }
      // 更新搜索target 不为 _blank
      modifyOutdatedSearchItemsTarget(url) {
        for (const value in this.settingData.engineList) {
          var item = this.settingData.engineList[value];
          for (let i = 0; i < item.length; i++) {
            if (item[i].url === url) {
              delete item[i].blank;
            }
          }
        }
      }
      deleteOutdatedSearchItems(urlList) {
        for (const value in this.settingData.engineList) {
          var item = this.settingData.engineList[value];
          for (let i = 0; i < item.length; i++) {
            if (urlList.includes(item[i].url)) {
              console.warn("删除搜索引擎:" + item[i].name);
              item.splice(i, 1);
            }
          }
        }
      }
      // 更新图标
      modifyOutdatedSearchItemsIcon(url, newIcon) {
        for (let i = 0; i < this.settingData.engineList.length; i++) {
          if (this.settingData.engineList[i].url == url) {
            //用户可能自己更改网站名称,所以此处用url来匹配
            this.settingData.engineList[i].favicon = newIcon;
          }
        }
      }
      // 更新本地 rule
      modifyOutdatedSearchItemsRule(name, value) {
        var oldRule = this.settingData.rules;
        for (let item in oldRule) {
          if (oldRule[item].name == name) {
            console.log("匹配成功, 更新 rule : ", name);
            oldRule[item] = value;
          }
        }
      }
    }

    class DropDownList {
      zIndex = 100000001;
      hidden = true;
      showDelay = 233;
      hideDelay = 233;
      aShownClass = "sej-drop-list-trigger-shown";

      constructor(a, list) {
        this.a = a;
        this.list = list;
        this.init();
      }

      init() {
        var a = this.a;
        var list = this.list;

        var self = this;

        // 关闭动画
        if (!settingData.transtion) {
          this.showDelay = 0;
          this.hideDelay = 0;
        }

        // 进入显示
        a.addEventListener("mouseenter", function () {
          clearTimeout(self.hideTimerId);

          if (self.hidden) {
            self.showTimerId = setTimeout(function () {
              self.show();
            }, self.showDelay);
          } else {
            var style = list.style;
            style.top = parseFloat(list.style.top) - 6 + "px";
            style.zIndex = this.zIndex + 1;
            style.opacity = 1;
          }
        });

        // 离开隐藏
        a.addEventListener("mouseleave", function () {
          clearTimeout(self.showTimerId);

          if (!self.hidden) {
            list.style.top = parseFloat(list.style.top) + 6 + "px";
            list.style.opacity = 0.04;
            self.hideTimerId = setTimeout(function () {
              self.hide();
            }, self.hideDelay);
          }
        });

        list.addEventListener("mouseenter", function () {
          clearTimeout(self.hideTimerId);

          var style = list.style;
          style.zIndex = this.zIndex + 1;
          style.opacity = 1;
          style.top = parseFloat(list.style.top) - 6 + "px";
        });

        list.addEventListener("mouseleave", function () {
          list.style.opacity = 0.04;
          list.style.top = parseFloat(list.style.top) + 6 + "px";
          self.hideTimerId = setTimeout(function () {
            self.hide();
          }, self.hideDelay);
        });
      }
      show() {
        if (!this.hidden) return;
        this.hidden = false;

        var scrolled = this.#getScrolled();
        var aBCRect = this.a.getBoundingClientRect();
        var thisBCRect = this.a.parentNode.getBoundingClientRect();

        var style = this.list.style;

        var top = scrolled.y + aBCRect.bottom;
        var left = scrolled.x + aBCRect.left;

        style.top = top + 6 + "px";
        style.left = left + "px";

        style.zIndex = this.zIndex - 1;
        style.display = "block";
        // 二级搜索居中显示
        style.left =
          left -
          (this.list.getBoundingClientRect().width - aBCRect.width) / 2 +
          "px";

        setTimeout(function () {
          style.opacity = 1;
          style.top = top + "px";
        }, 30);
        this.a.classList.add(this.aShownClass);
      }
      hide() {
        if (this.hidden) return;
        this.hidden = true;

        var style = this.list.style;
        style.display = "none";
        style.opacity = 0.2;

        this.a.classList.remove(this.aShownClass);
      }
      // 获取已滚动的距离
      #getScrolled(container) {
        if (container) {
          return {
            x: container.scrollLeft,
            y: container.scrollTop,
          };
        }
        return {
          x:
            "scrollX" in window
              ? window.scrollX
              : "pageXOffset" in window
              ? window.pageXOffset
              : document.documentElement.scrollLeft || document.body.scrollLeft,
          y:
            "scrollY" in window
              ? window.scrollY
              : "pageYOffset" in window
              ? window.pageYOffset
              : document.documentElement.scrollTop || document.body.scrollTop,
        };
      }
    }

    class SettingButton {
      settingButtonElement;

      constructor(jumpBarContainer, settingData) {
        this.parentJumpBarContainer = jumpBarContainer;
        this.settingData = settingData;
        this.#addButtonToJumpBar();
        this.settingButtonElement?.addEventListener("click", () =>
          this.#activateSettingButton()
        );
        GM_registerMenuCommand("设置菜单", () => this.#activateSettingButton());
      }
      #addButtonToJumpBar() {
        if (this.settingData.setBtnOpacity >= 0) {
          this.settingButtonElement = document.createElement("span");
          this.settingButtonElement.id = "setBtn";
          this.settingButtonElement.title = "设置菜单";
          GM_addStyle(`#setBtn{opacity: ${this.settingData.setBtnOpacity};}`);
          this.settingButtonElement.innerHTML = icon.setting;
          this.parentJumpBarContainer.appendChild(this.settingButtonElement);
        }
      }
      #activateSettingButton() {
        if (!this.settingPanel) {
          document.querySelector("#settingLayerMask")?.remove();
          this.settingPanel = new SettingPanel();
        }
        this.settingPanel.show();
      }
    }

    class JumpBar {
      engineButtonTemplate =
        '<a class="sej-engine" target="$blank$" data-iqxincategory="$category$" encoding="$encoding$" gbk="$gbk$" url="$url$"><img src="$favicon$" class="sej-engine-icon" />$name$</a>';
      dropDownLists = [];
      container;
      inputTarget;
      insertTarget;
      insertPositionLabel;
      matchedRule;
      engineList;
      settingData;

      constructor(engineList, settingData, matchedRule) {
        this.engineList = engineList;
        this.settingData = settingData;
        this.matchedRule = matchedRule;
        const inited = this.#initContainer();
        if (inited === false) return;
        this.#initEngines();
        this.#addEnginesToDOM();
        this.#fixCompatibility();
        if (this.settingData.fixedTop && this.matchedRule) {
          const originalContainerDistanceTop =
            this.container.getBoundingClientRect().top + window.scrollY;
          // 判断是否需要只在向上滚动时显示
          if (this.settingData.fixedTopUpward) {
            window.onwheel = document.onwheel = (e) => {
              e.wheelDelta > 0
                ? this.#fixedToTop(
                    this.matchedRule.fixedTop,
                    this.matchedRule.fixedTopColor,
                    originalContainerDistanceTop
                  )
                : {};
            };
          } else {
            window.onscroll = () => {
              this.#fixedToTop(
                this.matchedRule.fixedTop,
                this.matchedRule.fixedTopColor,
                originalContainerDistanceTop
              );
            };
          }
        }
        if (getComputedStyle(this.container).position !== "sticky") {
          this.containerWrapper.style.height =
            this.container.offsetHeight +
            parseFloat(getComputedStyle(this.container).marginTop) +
            parseFloat(getComputedStyle(this.container).marginBottom) +
            "px";
          this.containerWrapper.style.position = "relative";
          this.containerWrapper.style.display = "flow-root";
        }
      }
      #initContainer() {
        if (this.matchedRule?.enabled) {
          this.inputTarget = this.#getInputTarget();
          this.insertTarget = this.#getInsertTarget();
          this.insertPositionLabel = this.#getInsertPositionLabel();
          if (this.inputTarget && this.insertTarget) {
            this.#createContainerDOM();
          } else {
            console.warn(
              `未找到输入框或插入位置,跳过初始化:\n输入框:${this.inputTarget}\n插入位置:${this.insertTarget}`
            );
          }
        } else if (this.#isOnSelectSearchMode()) {
          this.inputTarget = {};
          this.insertTarget = document.body;
          this.insertPositionLabel = "beforeend";
          this.#createContainerDOM();
          this.container.classList.add("selectSearch");
          document.addEventListener("selectionchange", () =>
            this.#toggleSelectSearchJumpBar()
          );
        } else {
          console.info("未启用搜索跳转,跳过初始化");
          return false;
        }

        this.matchedRule?.class
          ? (this.container.className += ` ${this.matchedRule.class}`)
          : {};
        this.container.addEventListener(
          "click",
          (e) => this.#JumpToSelectedEngine(e),
          true
        );
        // 由于与要插入网页的样式无法很好的兼容,更改源网页的样式
        if (this.matchedRule?.stylish) {
          GM_addStyle(this.matchedRule.stylish);
        }
        return true;
      }
      #createContainerDOM() {
        this.containerWrapper = document.createElement("sejspan");
        this.containerWrapper.id = "sej-container-wrapper";
        this.container = document.createElement("sejspan");
        this.container.id = "sej-container";
        this.container.className = "rwl-exempt";
        this.containerWrapper.appendChild(this.container);
      }
      #toggleSelectSearchJumpBar() {
        const selection = getSelection();
        if (selection.isCollapsed) {
          this.container.style.top = "-50px";
        } else {
          this.inputTarget.textContent = selection.toString();
          this.container.style.top = "2px";
        }
      }
      #isOnSelectSearchMode() {
        if (
          (!this.matchedRule || !this.matchedRule.enabled) &&
          this.settingData.selectSearch
        ) {
          return true;
        }
      }
      #getInputTarget() {
        return typeof this.matchedRule?.insertIntoDoc.keyword == "function"
          ? this.matchedRule.insertIntoDoc.keyword
          : this.#getElementBySelector(this.matchedRule?.insertIntoDoc.keyword);
      }
      #getInsertTarget() {
        return typeof this.matchedRule?.insertIntoDoc.target == "function"
          ? this.matchedRule.insertIntoDoc.target()
          : this.#getElementBySelector(this.matchedRule?.insertIntoDoc.target);
      }
      #getInsertPositionLabel() {
        return this.matchedRule?.insertIntoDoc.where.toLowerCase();
      }
      #initEngines() {
        const self = this;
        this.engineList.engineCategories.forEach(function (item) {
          // console.log(item);  // 搜索菜单   ["网页", "web", true]
          const category = item[1]; // "web"
          const cName = item[0]; // "网页"
          let engines = [];

          self.engineList[category].forEach(function (engine) {
            const engineUrl = engine.url;
            if (engine.disable) return;
            if (
              self.settingData.HideTheSameLink &&
              self.matchedRule?.url.test(engineUrl)
            )
              return; // 去掉跳转到当前引擎的引擎

            let engineListButton = self.engineButtonTemplate
              .replace("$encoding$", (engine.encoding || "utf-8").toLowerCase())
              .replace("$url$", engineUrl)
              .replace("$name$", engine.name)
              .replace("$category$", category);

            // 图标
            if (engine.favicon) {
              engineListButton = engineListButton.replace(
                "$favicon$",
                engine.favicon
              );
            } else {
              engineListButton = engineListButton.replace(
                'src="$favicon$"',
                ""
              );
            }
            // gbk编码
            if (engine.gbk) {
              engineListButton = engineListButton.replace("$gbk$", engine.gbk);
            } else {
              engineListButton = engineListButton.replace('gbk="$gbk$"', "");
            }
            // 新标签页
            if (settingData.newtab || engine.blank) {
              engineListButton = engineListButton.replace("$blank$", "_blank");
            } else {
              engineListButton = engineListButton.replace(
                'target="$blank$"',
                ""
              );
            }

            engines.push(engineListButton);
          });
          // 非空列表
          if (!engines.length) return;

          engines = engines.join("");

          // 展开当前搜索分类列表
          if (
            !self.settingData.foldlist &&
            category == self.matchedRule?.engineList
          ) {
            self.container.innerHTML = engines;
          } else {
            const dropDownList = document.createElement("sejspan");
            dropDownList.className = "sej-drop-list rwl-exempt";
            dropDownList.innerHTML = engines;

            //  a:主搜索菜单
            // dropList: 搜索子菜单
            const jumpBarButton =
              dropDownList.firstElementChild.cloneNode(true);
            jumpBarButton.className =
              jumpBarButton.className + " sej-drop-list-trigger";

            // 隐藏主搜索菜单的图标
            if (!self.settingData.icon) {
              cName = "";
            }

            jumpBarButton.lastChild.nodeValue = cName;
            self.dropDownLists.push([jumpBarButton, dropDownList]);
          }
        });
      }
      #addEnginesToDOM() {
        this.dropDownLists.forEach((item) => {
          this.container.appendChild(item[0]); //将搜索列表放入主搜索
          document.body.appendChild(item[1]); // 插入搜索子菜单
          item[1].addEventListener(
            "click",
            (e) => this.#JumpToSelectedEngine(e),
            true
          );
          new DropDownList(item[0], item[1]);
        });

        switch (this.insertPositionLabel) {
          case "beforebegin": // 'beforeBegin'(插入到给定元素的前面) ;
            this.insertTarget.parentNode.insertBefore(
              this.containerWrapper,
              this.insertTarget
            );
            break;
          case "afterbegin": // 'afterBegin'(作为给定元素的第一个子元素) ;
            if (this.insertTarget.firstChild) {
              this.insertTarget.insertBefore(
                this.containerWrapper,
                this.insertTarget.firstChild
              );
            } else {
              this.insertTarget.appendChild(this.container);
            }
            break;
          case "beforeend": // 'beforeEnd' (作为给定元素的最后一个子元素) ;
            this.insertTarget.appendChild(this.containerWrapper);
            break;
          case "afterend": // 'afterEnd'(插入到给定元素的后面);.
            if (this.insertTarget.nextSibling) {
              this.insertTarget.parentNode.insertBefore(
                this.containerWrapper,
                this.insertTarget.nextSibling
              );
            } else {
              this.insertTarget.parentNode.appendChild(this.container);
            }
            break;
          default:
            this.insertTarget.appendChild(this.containerWrapper);
            break;
        }
      }
      #fixCompatibility() {
        if (this.matchedRule?.style) {
          // 判断是否存在脚本 “AC-baidu:重定向优化百度搜狗谷歌搜索_去广告_favicon_双列”
          if (this.settingData.center == 2) {
            // 自动判断是否添加
            if (
              document.querySelector(".AC-style-logo") &&
              this.matchedRule.style_ACBaidu
            ) {
              this.matchedRule.style = this.matchedRule.style_ACBaidu;
            }
          } else if (this.settingData.center == 1) {
            //  强制添加
            this.matchedRule.style = this.matchedRule.style_ACBaidu
              ? this.matchedRule.style_ACBaidu
              : this.matchedRule.style;
          } //
          // 判断是否存在脚本“知乎排版优化”
          if (document.getElementById("SearchMain")) {
            if (
              document.getElementById("SearchMain").style.marginLeft == "150px"
            ) {
              this.matchedRule.style = this.matchedRule.style_ZhihuChenglinz;
              this.matchedRule.fixedTop = null;
            }
          }
          this.container.style.cssText = this.matchedRule.style;
        }

        //兼容ac百度中lite选项, fixedtop和正常的不一样
        setTimeout(function () {
          if (
            document.querySelector(".AC-baiduLiteStyle") &&
            matchedRule.fixedTop2
          ) {
            matchedRule.fixedTop = matchedRule.fixedTop2;
          }
        }, 2500);
      }
      #fixedToTop(fixedTop, color, originalContainerDistanceTop) {
        if (!this.container) {
          return;
        }

        fixedTop = fixedTop ? fixedTop : 0;

        if (this.container.style.position != "sticky") {
          const rect = this.container.getBoundingClientRect();
          if (originalContainerDistanceTop - window.scrollY <= fixedTop) {
            this.container.style.position = "fixed";
            this.container.style.top = fixedTop + "px";
            this.container.style.left = rect.left + "px";
            this.container.style.padding = "0";
            this.container.style.margin = "0";
            this.container.style.backgroundColor = color;
          } else {
            this.container.style.cssText = matchedRule.style;
          }
        }
      }
      #JumpToSelectedEngine(e) {
        const target = e.target;

        if (!target) return;
        if (!target.classList.contains("sej-engine")) return;

        let searchKeyword;
        if (typeof this.inputTarget == "function") {
          searchKeyword = this.inputTarget();
        } else {
          if (this.inputTarget.nodeName == "INPUT") {
            searchKeyword = this.inputTarget.value;
          } else {
            searchKeyword = this.inputTarget.textContent;
          }
        }

        // 如果搜索内容是通过某一网站搜索, 就去掉。 例: 0 site:zhihu.com  只保留0, 后面的网站会去掉
        if (!this.settingData.HideTheSameLink) {
          searchKeyword = searchKeyword.replace(/site:[^\s]+/, "");
        }

        // 编码 解码
        // 对搜索词编码 (未做解码处理,浏览器自动处理) 网站1688采用gbk编码
        const ogbk = target.getAttribute("gbk");
        if (ogbk) {
          searchKeyword = toGBK(searchKeyword);
        } else {
          searchKeyword = encodeURIComponent(searchKeyword);
        }

        let targetURL = target.getAttribute("url");

        // 一键搜索
        if (
          this.settingData.allOpen &&
          target.classList.contains("sej-drop-list-trigger")
        ) {
          var list = this.engineList[target.dataset.iqxincategory];

          for (var i = 0; i < list.length; i++) {
            if (
              list[i].url.indexOf("site:") < 0 &&
              matchedRule?.url.test(list[i].url)
            )
              continue;
            if (list[i].disable) continue;
            var href = list[i].url.replaceAll("%s", searchKeyword);
            GM_openInTab(href);
          }
          target.setAttribute("onclick", "return false;");
          return;
        }

        // 如果有post请求
        var postSign = targetURL?.indexOf("$post$");
        if (postSign && postSign !== -1) {
          target.addEventListener("click", function (e) {
            e.preventDefault();
          });
          var f = this.#getEngineJumpPostForm(
            targetURL.substring(0, postSign),
            [
              targetURL.substring(postSign + 6),
              decodeURIComponent(searchKeyword),
            ],
            target.getAttribute("target")
          );
          document.body.appendChild(f);
          f.submit();
        } else {
          target.href = target
            .getAttribute("url")
            .replaceAll("%s", searchKeyword);
        }
        if (this.#isOnSelectSearchMode()) {
          target.target = "_blank";
        }
        if (target?.target !== "_blank") {
          target.target = "_top";
        }
      }
      #getElementBySelector(selector) {
        if (selector?.startsWith("css;")) {
          return document.querySelector(selector.slice(4));
        } else {
          return document.evaluate(selector, document, null, 9, null)
            .singleNodeValue;
        }
      }
      #getEngineJumpPostForm(url, value, newTab) {
        const postForm = document.createElement("form");
        postForm.method = "post";
        postForm.action = url;
        postForm.style.cssText = "display:none;";
        postForm.innerHTML = `<input type="hidden" name="${value[0]}" value="${value[1]}"/>`;
        newTab ? (postForm.target = "_blank") : {};
        return postForm;
      }
    }

    class SettingPanel {
      static dragEl = null;
      aPatternParent = "<div></div>";
      ele = document.createElement("div");
      mask = document.createElement("div");
      parentTemp = null;
      editTemp = null;
      online = null;

      constructor() {
        this.init();
      }
      init() {
        // console.log("init...");
        var that = this;

        this.ele.id = "settingLayer";
        this.mask.id = "settingLayerMask";

        this.addGlobalStyle();

        this.addContent();

        this.mask.addEventListener("click", function () {
          that.hide();
        });
        this.ele.addEventListener("click", function (e) {
          e.stopPropagation();
        });

        this.mask.appendChild(this.ele);
        document.body.appendChild(this.mask);

        // 绑定事件
        this.ele.addEventListener("click", that.domClick.bind(this), false);
        this.dragEvent();
        this.setDragNode(this.ele); //设置拖动
        // input[range]
        that.rangeChange(true);
        document
          .querySelector("#setBtnOpacityRange")
          .addEventListener("input", that.rangeChange);

        document
          .querySelector("#xin-save")
          .addEventListener("click", function () {
            that.saveData();
            that.hide();
            that.reloadSet();
          });
        document
          .querySelector("#xin-addDel")
          .addEventListener("click", function (e) {
            that.addDel(e);
          });
        document
          .querySelector("#xin-modification")
          .addEventListener("click", function () {
            that.editCodeBox();
          });
        window.addEventListener("resize", this.windowResize.bind(this));
      }
      dragEvent() {
        var that = this;
        var odivsdrag = document.querySelectorAll(".drag");
        [].forEach.call(odivsdrag, function (odiv) {
          odiv.addEventListener("dragstart", that.domdragstart, false);
          odiv.addEventListener("dragenter", that.domdragenter, false);
          odiv.addEventListener("dragover", that.domdragover, false);
          odiv.addEventListener("dragleave", that.domdragleave, false);
          odiv.addEventListener("drop", that.domdrop, false);
          odiv.addEventListener("dragend", that.domdropend, false);
        });
      }
      addContent() {
        var aPattern =
          '<span draggable="true" class="drag">' +
          '<span class="sej-engine"' +
          ' data-xin="$xin$" ' +
          ' data-iqxinimg="$img$" ' +
          ' data-iqxintitle="$title$" ' +
          ' data-iqxinlink="$link$" ' +
          ' data-iqxintarget="$blank$" ' +
          ' data-iqxindisabled="$disabled$" ' +
          ' data-iqxingbk="$gbk$" ' +
          '><img src="$favicon$" class="sej-engine-icon"/><span>$name$</span></span>' +
          ' <span class="iqxin-set-edit" title="编辑 Edit"><img class="sej-engine-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAACDklEQVR4nJXVzUtUURjH8Y/mSNKkki2iwiApxHQ1q/6C+gusoCB6oxbRRqFNL4sWtRKqhVSLIDe1CqpNiwjKIilKLKKFEr2Z2qI0xxHN0+LOm+PMOPOc1T2H7/f5ncO991BdNer30zmxKrl0xV2zKJjRoy6aqkkvbbdVLPuUq+8+5uGXnVILki7qsxgtNDtrTNLcijHvrdYsft0/wQ8DZgSzeqMUDW4IJceYHcvwCd1ies0KZvWI1TnhIH6574Olgg0E74zmhZ902j304by4Cxp5LPjtQNmjy3XPVK2rgmCBCcGgdVXhdBgUBCMEwVMNVeIvBMFLifKC8vgrndFBlRJUhJcWFMd3ZfGuzFRxwWrdu3KTxQQVhi8lqApfKVhf0d4bc2/OckG9Pkur7r3TEw+1FRO0GxdM2Vc2/HHBgr1If935UTfigbt5+C27MeSo9+m5GJYitlCwWR2G8oQZ/FgWX1aFgnZMG852v5nFR4rhMn+2dDVJYFpKqy0SDksUhF9FsE0bWgyIa9bIanihoEUcDTrSz4ueOVMOLxQkzVkrZcaoNz755rmpcnihYNghm3w26Ys/5cGcIKgRBJDyqCIquj8C1PqKZvHK+qVrJ5bMRwmGterU64pkkZupWO3RjXkzUZj9+jVZMGK6IsEaHTbgjpOSUYZL/pa5m4qPIbtyznpHvJaqGB53O33h4T/3VzLuzDhE6AAAAABJRU5ErkJggg=="/></span>' +
          ' <span class="iqxin-set-del" title="删除 Delete"><img class="sej-engine-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAADAFBMVEUAAADsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVHsbVH///9VVVVWVlZXV1dYWFhZWVlaWlpbW1tcXFxdXV1eXl5fX19gYGBhYWFiYmJjY2NkZGRlZWVmZmZnZ2doaGhpaWlqampra2tsbGxtbW1ubm5vb29wcHBxcXFycnJzc3N0dHR1dXV2dnZ3d3d4eHh5eXl6enp7e3t8fHx9fX1+fn5/f3+AgICBgYGCgoKDg4OEhISFhYWGhoaHh4eIiIiJiYmKioqLi4uMjIyNjY2Ojo6Pj4+QkJCRkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqbm5ucnJydnZ2enp6fn5+goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6vr6+wsLCxsbGysrKzs7O0tLS1tbW2tra3t7e4uLi5ubm6urq7u7u8vLy9vb2+vr6/v7/AwMDBwcHCwsLDw8PExMTFxcXGxsbHx8fIyMjJycnKysrLy8vMzMzNzc3Ozs7Pz8/Q0NDR0dHS0tLT09PU1NTV1dXW1tbX19fY2NjZ2dna2trb29vc3Nzd3d3e3t7f39/g4ODh4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7///8dej9TAAAAU3RSTlMAAABm7P/sZgAAABPO////zhQAAB/i/////////+IfAAAe4fvk4AAAAAAd/+Q3GxwAFR85FQBjz+LPY+v////r6//////rZM/h4c9jABUdHRUAAP0EcPoAAAEuSURBVHic7ZRnc8IwDIbdEUZHGB0kDsMOMcOMttBBB93Qvcj//y9VjB0Czh13/dz3ixT5OVmSYyMktLK6tm74oYxEMpVGUW1sbm2bM8DMZHP5OWBnd2+/YNnYAWHbKhRL5cocQKjrWFWPuSDmVS3HpUQu1eoNQkiTM9xqd7oHoG6n3cKMNyHcqNfQ4VGPUsr7nh0FbK/PIdw7PkGnZwOZNrqF9AfnF+jyaigLixYp/eH1Dbq9u4eAHyOAHh5HaPz0DCnjANjm5fUNvX98QoGCxyo5Fjmh0K/vH2hzAi0KnqnymMgJrU6gzemQBM+DZpX1/XBYUyAYTTAuZTUg+Aw8Zf+BvwJLR730sPTjXgD0H2YB0BUClXKpGAeE1y+fy2ZMfX12gdOpZMLQAfkE/AL7e5vGZF+dOQAAAABJRU5ErkJggg=="></span>' +
          "</span>";
        var details = engineList.engineCategories;
        // 若根据数组长度获取,负数引导的为属性,不再length长度之内,所以来个大体的数字,当都为空时,结束循环
        // var detailsLength = details.length;
        var detailsLength = 99;
        for (let i = 0; i < detailsLength; i++) {
          var j = i;
          j = details[j] ? j : -j;
          if (!details[j]) {
            break;
          }

          var odiv = document.createElement("div");
          odiv.id = details[j][1]; // "web"
          odiv.classList.add("iqxin-items");

          var oDivTitle = document.createElement("div");
          oDivTitle.classList.add("sejtitle", "drag");
          oDivTitle.setAttribute("draggable", "true");
          oDivTitle.dataset.iqxintitle = details[j][1];
          oDivTitle.dataset.xin = j;
          oDivTitle.innerHTML =
            '<span class="iqxin-pointer-events">' +
            details[j][0] +
            "</span>" +
            '<span class="iqxin-title-edit" title="编辑 Edit"><img class="sej-engine-icon" src="' +
            icon.edit +
            '"/></span>' +
            ' <span class="iqxin-set-title-del" title="删除 Delete"><img class="sej-engine-icon" src="' +
            icon.del +
            '"></span>';
          odiv.appendChild(oDivTitle);

          var oDivCon = document.createElement("div");
          oDivCon.classList.add("sejcon");
          var oDivConStr = "";
          var engineListItem = engineList[details[j][1]];
          var itemLength = engineListItem.length;
          for (let ii = 0; ii < itemLength; ii++) {
            var jj = ii;
            if (!engineListItem[jj]) {
              break;
            }
            var a = aPattern
              .replace("$name$", engineListItem[jj].name)
              .replace("$favicon$", engineListItem[jj].favicon)
              .replace("$xin$", jj);
            // 添加属性
            a = a
              .replace("$img$", engineListItem[jj].favicon)
              .replace("$title$", engineListItem[jj].name)
              .replace("$link$", engineListItem[jj].url);
            if (engineListItem[jj].blank) {
              a = a.replace("$blank$", "_blank");
            } else {
              a = a.replace('data-iqxintarget="$blank$"', "");
            }
            if (engineListItem[jj].disable) {
              a = a.replace("$disabled$", "true");
            } else {
              a = a.replace('data-iqxindisabled="$disabled$"', "");
            }
            if (engineListItem[jj].gbk) {
              a = a.replace("$gbk$", "true");
            } else {
              a = a.replace('data-iqxingbk="$gbk$"', "");
            }

            oDivConStr += a;
          }

          oDivConStr += "<span class='iqxin-additem'>+</span>";

          oDivCon.innerHTML = oDivConStr;
          odiv.appendChild(oDivCon);

          this.ele.appendChild(odiv);
        }

        // 更多设置 菜单
        var btnEle2 = document.createElement("div");
        btnEle2.id = "btnEle2";
        var fixedTop_checked = settingData.fixedTop ? "checked" : "";
        var fixedTopUpward_checked = settingData.fixedTopUpward
          ? "checked"
          : "";
        var transition_checked = settingData.transtion ? "checked" : "";
        var selectSearch_checked = settingData.selectSearch ? "checked" : "";
        var foldlist_checked = settingData.foldlist ? "checked" : "";
        var allOpen_checked = settingData.allOpen ? "checked" : "";
        var HideTheSameLink_checked = settingData.HideTheSameLink
          ? "checked"
          : "";

        var btnStr2 =
          "<div>" +
          "<span id='xin-reset' title='慎点,出厂重置'>清空设置</span>" +
          "<span id='xin-modification' title='edit 分享自己的配置或清空配置'>配置文件</span>" +
          // "<span id='xin-importing' title='importing 导入更为专业的搜索引擎'>导入</span>" +
          "<span id='xin-selectSearch' title='划词搜索, 只有非搜索页面才会生效, 开关功能需要刷新页面'>" +
          "<label>划词搜索<input id='iqxin-selectSearch' type='checkbox' name='' " +
          selectSearch_checked +
          " style='vertical-align:middle;'></label>" +
          "</span>" +
          "<span id='xin-transtion' title='动画,该设置需要刷新页面生效'>" +
          "<label>动画<input id='iqxin-transtion' type='checkbox' name='' " +
          transition_checked +
          " style='vertical-align:middle;'></label>" +
          "</span>" +
          "<span id='xin-foldlists' title='将当前所在搜索分类折叠'>" +
          "<label>折叠当前搜索分类<input id='iqxin-foldlist' type='checkbox' name='' " +
          foldlist_checked +
          " style='vertical-align:middle;'></label>" +
          "</span>" +
          "<span id='iqxin-fixedTopS' title='fixedTop 当滚动页面时,固定到页面顶端。某些页面的样式存在问题'>" +
          "<label>固定到顶端<input id='iqxin-fixedTop' type='checkbox' name='' " +
          fixedTop_checked +
          " style='vertical-align:middle;'></label>" +
          "</span>" +
          "<span id='iqxin-fixedTopUpward' title='固定到顶端后,仅向上滚动才显示,需要刷新网页生效'>" +
          "<label>仅上拉显示<input id='iqxin-fixedTopUpward-item' type='checkbox' name='' " +
          fixedTopUpward_checked +
          " style='vertical-align:middle;'></label>" +
          "</span>" +
          "<span id='xin-HideTheSameLink' title='隐藏同站链接,如果想在同一个搜索网站,但是想通过不同语言来搜索, 可以取消该选项'>" +
          "<label>隐藏同站链接<input id='iqxin-HideTheSameLink' type='checkbox' name='' " +
          HideTheSameLink_checked +
          " style='vertical-align:middle;'></label>" +
          "</span>" +
          "<span id='xin-setBtnOpacity' title='设置按钮透明度,需要刷新页面'>设置按钮透明度 <input type='range' step='0.05'  min='0' max='1' value='" +
          (settingData.setBtnOpacity < 0
            ? -settingData.setBtnOpacity
            : settingData.setBtnOpacity) +
          "' id='setBtnOpacityRange'><i style='display:inline-block;width:3em;text-align:center;' class='iqxin-setBtnOpacityRangeValue' title='按钮 显示/隐藏(非透明)),请确定知道自己如何再次打开; 火狐非高级玩家建议别禁用'></i></span>" +
          "</div>";
        // "<div><span>test</span></div>";
        btnEle2.innerHTML = btnStr2;
        this.ele.appendChild(btnEle2);

        // 添加按钮
        var btnEle = document.createElement("div");
        btnEle.id = "btnEle";

        var btnStr =
          "<div class='btnEleLayer'>" +
          "<span class='feedback' title='在 GreasyFork 进行反馈'><a target='_blank' href='https://greasyfork.org/en/scripts/454280-searchenginejumpplus'>Greasy Fork</a></span>" +
          "<span class='feedback' title='在 Github 进行反馈'><a target='_blank' href='https://github.com/MUTED64/SearchEngineJumpPlus'>GitHub</a></span>" +
          "<span id='xin-allOpen' title='后台打开该搜索分类的所有网站'>" +
          "<label>一键搜索<input id='iqxin-allOpen-item' type='checkbox' name='' " +
          allOpen_checked +
          " style='vertical-align:middle;'></label>" +
          "</span>" +
          "<span id='xin-centerDisplay' title='center 居中显示。主要是兼容AC-baidu:重定向优化百度搜狗谷歌搜索_去广告_favicon_双列'>居中:" +
          "<select id='iqxin-center'>" +
          "<option value='original'" +
          (settingData.center == 0 ? "selected" : "") +
          ">默认</option>" +
          "<option value='force'" +
          (settingData.center == 1 ? "selected" : "") +
          ">强制</option>" +
          "<option value='auto'" +
          (settingData.center == 2 ? "selected" : "") +
          ">自动</option>" +
          "</select>" +
          "</span> " +
          "<span id='xin-newtab' title='open newtab 是否采用新标签页打开的方式'>打开方式:" +
          "<select id='iqxin-globalNewtab'>" +
          "<option value='globalDef'>默认页面</option>" +
          "<option value='globalNewtab'" +
          (settingData.newtab ? "selected" : "") +
          ">新标签页</option>" +
          "</select>" +
          "</span> " +
          "<span id='xin-addDel' title='add & del 增加新的或者删除现有的搜索'>增加 / 删除</span> " +
          "<span id='moreSet' title='more set'>更多设置</span>" +
          "<span id='xin-save' title='save & close'>保存并关闭</span>" +
          "</div>";
        btnEle.innerHTML = btnStr;
        this.ele.appendChild(btnEle);

        // 可以拖动的顶栏
        var dragDom = document.createElement("div");
        dragDom.id = "dragDom";
        dragDom.style.cssText =
          "height:16px;width:97%;position:absolute;top:0;cursor:move;";
        this.ele.appendChild(dragDom);

        // 增加搜索列表
        var nSearchList = document.createElement("div");
        nSearchList.id = "nSearchList";
        nSearchList.style.cssText =
          "visibility:hidden;opacity:0;transition:0.3s;position:absolute;bottom:10%;right:5%;padding:5px 10px;border-radius:4px;border:1px solid #EC6D51;color:#ec6d51;cursor:pointer;background:#fff;";
        nSearchList.innerHTML = "增加新的搜索列表";
        this.ele.appendChild(nSearchList);

        // 关闭按钮
        if (settingData.closeBtn) {
          var closebtnELe = document.createElement("span");
          closebtnELe.id = "xin-close";
          closebtnELe.setAttribute("title", "close 关闭");
          this.ele.appendChild(closebtnELe);
        }
      }
      show() {
        var style = this.mask.style;
        var eleStyle = this.ele.style;
        style.display = "flex";
        eleStyle.transform = "translateY(-20%)";
        document.body.style.overflow = "hidden";

        this.windowResize();

        setTimeout(function () {
          style.opacity = 1;
          eleStyle.transform = "none";
        }, 30);
      }
      hide() {
        this.allBoxClose(); // 关闭所有次级窗口、菜单

        var style = this.mask.style;
        this.ele.style.transform = "translateY(20%)";
        style.opacity = 0;
        setTimeout(function () {
          style.display = "none";
          document.body.style.overflow = "auto";
        }, 500);
      }
      reset() {
        if (confirm("将会删除用户设置!")) {
          GM_deleteValue("searchEngineJumpData");
          location.reload();
        }
      }
      // 增加 “添加删除框”
      addDel(e) {
        if (e.target.classList.contains("iqxin-btn-active")) {
          this.addDelremove();
        } else {
          // console.log("不存在,增加增加");
          var obtn = document.querySelector("#xin-addDel");
          obtn.classList.add("iqxin-btn-active");

          var odom = document.querySelectorAll(".iqxin-set-del");
          [].forEach.call(odom, function (div) {
            div.classList.add("iqxin-set-active");
          });

          // 标题添加删除框
          var odom = document.querySelectorAll(".iqxin-set-title-del");
          [].forEach.call(odom, function (div) {
            // console.log(div);
            div.classList.add("iqxin-set-active");
          });

          // 增加单个搜索
          var oitemAdd = document.querySelectorAll(".iqxin-additem");
          [].forEach.call(oitemAdd, function (div) {
            // console.log(div);
            div.classList.add("iqxin-set-active");
          });

          // 添加搜索列表
          var olistAdd = document.querySelector("#nSearchList");
          olistAdd.classList.add("iqxin-set-active");
        }
      }
      // 关闭 “添加删除框”
      addDelremove(bool) {
        var obtn = document.querySelector(".iqxin-btn-active");
        if (obtn) {
          obtn.classList.remove("iqxin-btn-active");

          var odom = document.querySelectorAll(".iqxin-set-active");
          [].forEach.call(odom, function (div) {
            div.classList.remove("iqxin-set-active");
          });

          var oitemAdd = document.querySelectorAll(".iqxin-additem");
          [].forEach.call(oitemAdd, function (div) {
            div.classList.remove("iqxin-set-active");
          });
        }
        this.addItemBoxRemove();
      }

      // 界面,框:添加新的搜索
      addItemBox() {
        this.isOnline();
        this.addItemBoxRemove();

        var newDiv = document.createElement("div");
        newDiv.id = "newSearchBox";
        newDiv.style.cssText = "top:43%;opacity:0.1;";
        newDiv.innerHTML = `<span>标&nbsp;&nbsp;&nbsp&nbsp&nbsp&nbsp&nbsp题 : </span><input id='iqxin-newTitle' placeholder='必填' onfocus='this.select()' /> <br/><br/>
             <span>链&nbsp;&nbsp;&nbsp&nbsp&nbsp&nbsp&nbsp接 : </span><input id='iqxin-newLink' placeholder='必填' onfocus='this.select()' /> <br/><br/>
             <span>图&nbsp;&nbsp;&nbsp&nbsp&nbsp&nbsp&nbsp标 : </span><input id='iqxin-newIcon' placeholder='选填,留空则自动获取' onfocus='this.select()' /> <br/><br/>
             <span>打开方式 :
             <select id="iqxin-newTarget" style="border-radius: 4px;border: none;padding: 2px 0 2px 2px">
             <option value="default">新标签页打开</option>
             <option value="newtab">当前页打开</option>
             <select>
             </span>
             <br/><br/>
             <span><a target='_blank' style='color:#999;' href='https://greasyfork.org/en/scripts/454280-searchenginejumpplus'>相关使用说明</a></span>
             &nbsp;&nbsp;&nbsp&nbsp&nbsp&nbsp&nbsp;
             <button id='addItemBoxEnter' class='addItemBoxEnter addItemBoxBtn iqxin-enterBtn'>确定</button>&nbsp;&nbsp;&nbsp&nbsp&nbsp;&nbsp
             <button id='addItemBoxCancel' class='addItemBoxCancel addItemBoxBtn iqxin-closeBtn'>取消</button>`;

        this.ele.appendChild(newDiv);
        setTimeout(function () {
          newDiv.style.cssText = "";
        }, 10);
        document.querySelector("#iqxin-newTitle").focus();
      }
      // 内部逻辑,:添加新的搜索
      addItemEnger() {
        var otitle, olink, oimg, oblank;
        otitle = document.querySelector("#iqxin-newTitle").value;
        olink = document.querySelector("#iqxin-newLink").value;
        oimg = document.querySelector("#iqxin-newIcon").value;
        oblank = document.querySelector("#iqxin-newTarget").selectedIndex;

        if (!oimg) {
          oimg = this.getICON(olink);
        }

        var a =
          '<span class="sej-engine"' +
          ' data-iqxinimg="$img$" ' +
          ' data-iqxintitle="$title$" ' +
          ' data-iqxinlink="$link$" ' +
          ' data-iqxintarget="$blank$" ' +
          '><img src="$favicon$" class="sej-engine-icon" />$name$</span>' +
          '<span class="iqxin-set-edit" title="编辑 Edit">' +
          '<img class="sej-engine-icon" src="' +
          icon.edit +
          '">' +
          "</span> " +
          '<span class="iqxin-set-del iqxin-set-active" title="删除 Delete">' +
          '<img class="sej-engine-icon" src="' +
          icon.del +
          '">' +
          "</span>";

        a = a
          .replace("$img$", oimg)
          .replace("$title$", otitle)
          .replace(
            "$link$",
            olink.indexOf("://") === -1 ? "https://" + olink : olink
          );

        if (oblank) {
          a = a.replace('data-iqxintarget="$blank$"', "");
        } else {
          a = a.replace("$blank$", "_blank");
        }

        a = a.replace("$name$", otitle).replace("$favicon$", oimg);

        var ospan = document.createElement("span");
        ospan.className = "drag";
        ospan.innerHTML = a;

        this.parentNode.insertBefore(ospan, this.parentNode.lastChild);

        // 添加完成,移除添加框
        this.addItemBoxRemove();
      }
      addItemBoxRemove(ele) {
        ele = ele ? ele : "#newSearchBox";
        var newBox = document.querySelector(ele);
        if (newBox) {
          // newBox.style.transform = "translateY(30%)";
          newBox.style.top = "60%";
          newBox.style.opacity = "0";
          setTimeout(function () {
            newBox.parentNode.removeChild(newBox);
          }, 550);
        }
      }
      // 获取图标
      getICON(olink) {
        let ourl;
        let mark;
        let protocol;
        let host;

        if (olink.indexOf("://") !== -1) {
          protocol = olink.split("://")[0] ? olink.split("://")[0] : "https";
          host = olink.split("://")[1].split("/")[0];
        } else {
          protocol = "https";
          host = olink.split("/")[0];
        }

        const siteURL = protocol + "://" + host;

        if (isNaN(settingData.getIcon)) {
          ourl = settingData.getIcon;
        } else {
          mark = parseInt(settingData.getIcon);
          switch (mark) {
            case 1:
              ourl = siteURL + "/favicon.ico";
              break;
            case 2:
              ourl = "https://www.google.com/s2/favicons?domain=" + siteURL;
              break;
            case 3:
              ourl =
                "https://statics.dnspod.cn/proxy_favicon/_/favicon?domain=" +
                host;
              break;
          }
        }

        if (ourl) {
          ourl = ourl.replace("%s", siteURL);
          return ourl;
        }
        if (this.online) {
          ourl = "https://www.google.com/s2/favicons?domain=" + host;
          return ourl;
        } else {
          ourl = protocol + "://" + host + "/favicon.ico";
          return ourl;
        }
      }

      // 界面, 框: 添加新的搜索列表
      addSearchListBox() {
        var odiv = document.querySelector("#newSearchListBox");
        if (odiv) {
          this.boxClose("#newSearchListBox");
          return;
        }
        var newDiv = document.createElement("div");
        newDiv.id = "newSearchListBox";

        var myDate = new Date();
        // var hash = "user" + myDate.getFullYear() + myDate.getMonth() + myDate.getDate() + myDate.getHours() +myDate.getMinutes()+myDate.getSeconds();
        var hash = "user" + myDate.getTime();

        newDiv.innerHTML =
          "" +
          "<span>列表名称: </span><input id='iqxin-newSearchListName' onfocus='this.select()'>" +
          "<br><br>" +
          "<span>内部名称: </span><input id='iqxin-newSearchListInnerName' onfocus='this.select()' value='" +
          hash +
          "'>" +
          "<br><br>" +
          "<button id='addSearchListBoxEnter' class='addSearchListBoxEnter addItemBoxBtn'>确定</button>&nbsp;&nbsp;&nbsp&nbsp&nbsp;&nbsp" +
          "<button id='addSearchListBoxCancel' class='addSearchListBoxCancel addItemBoxBtn'>取消</button>" +
          "";
        this.ele.appendChild(newDiv);

        document.querySelector("#iqxin-newSearchListName").focus();
      }
      addSearchListEnger() {
        var name = document.querySelector("#iqxin-newSearchListName").value;
        var innerName = document.querySelector(
          "#iqxin-newSearchListInnerName"
        ).value;

        if (innerName.length === 0) {
          alert("内部名称不能为空");
          return;
        }
        if (name.length === 0) {
          name = innerName;
        }

        var odiv = document.createElement("div");
        odiv.id = innerName;
        odiv.className = "iqxin-items";
        odiv.innerHTML =
          "" +
          '<div class="sejtitle" data-iqxintitle="' +
          innerName +
          '" data-xin="99">' +
          '<span class="iqxin-pointer-events">' +
          name +
          "</span>" +
          '<span class="iqxin-title-edit" title="编辑 Edit">' +
          '<img class="sej-engine-icon" src="' +
          icon.edit +
          '">' +
          "</span> " +
          '<span class="iqxin-set-title-del iqxin-set-active" title="删除 Delete">' +
          '<img class="sej-engine-icon" src="' +
          icon.del +
          '">' +
          "</span>" +
          "</div>" +
          '<div class="sejcon">' +
          '<span class="iqxin-additem iqxin-set-active">+</span>' +
          "</div>" +
          "";

        // this.boxClose("#newSearchListBox");
        this.addItemBoxRemove("#newSearchListBox");

        var btnEle = document.querySelector("#btnEle");
        btnEle.parentNode.insertBefore(odiv, btnEle);
      }

      boxClose(ele) {
        var odiv = document.querySelector(ele);
        if (odiv) {
          odiv.parentNode.removeChild(odiv);
        }
      }

      // 界面 框:修改框
      addEditBox(e) {
        this.addItemBoxRemove();

        var target = e.target.parentNode.firstChild;

        var otitle = target.dataset.iqxintitle;
        var olink = target.dataset.iqxinlink;
        var oicon = target.dataset.iqxinimg;
        var otarget = target.dataset.iqxintarget;
        var odisabled = target.dataset.iqxindisabled;
        let oGBK = target.dataset.iqxingbk;

        this.editTemp = target;

        var strblank;
        if (otarget) {
          strblank =
            '<option value="default">新标签页打开</option><option value="newtab">当前页打开</option> ';
        } else {
          strblank =
            '<option value="default">新标签页打开</option><option value="newtab" selected="selected">当前页打开</option>';
        }

        var strGBK = "";
        if (oGBK) {
          strGBK = "checked='checked'";
        }

        var newDiv = document.createElement("div");
        newDiv.id = "newSearchBox";
        // 从鼠标点击所在的项目展开菜单(2021-03-16,从上线至今,动画一直有卡顿现象)
        // newDiv.style.cssText = "top:"+(e.screenY-120) +"px;left:"+(e.screenX-140) +"px;";
        newDiv.style.cssText = "top:43%;opacity:0.1;";
        var innerHTML = `
          <span>标&nbsp;&nbsp;&nbsp&nbsp&nbsp&nbsp&nbsp题 : </span><input id="iqxin-newTitle" placeholder="必填" onfocus="this.select()" value="${otitle}" /> <br/><br/>
          <span>链&nbsp;&nbsp;&nbsp&nbsp&nbsp&nbsp&nbsp接 : </span><input id="iqxin-newLink" placeholder="必填" onfocus="this.select()" value="${olink}" /> <br/><br/>
          <span>图&nbsp;&nbsp;&nbsp&nbsp&nbsp&nbsp&nbsp标 : </span><input id="iqxin-newIcon" placeholder="选填,留空则自动获取" onfocus="this.select()" value="${oicon}" /> <br/><br/>
          <span>打开方式 :
              <select id="iqxin-newTarget" style="border-radius: 4px;border: none;padding: 2px 0 2px 2px">
                  ${strblank}
              <select>
          </span>
          <br/><br/>
          <span style=""><label>GBK编码:<input type="checkbox" name="" id="iqxin-newGBK" ${strGBK} style="vertical-align:middle;"></label></span>
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <button id="editItemBoxEnter" class="editItemBoxEnter addItemBoxBtn iqxin-enterBtn">确定</button>&nbsp;&nbsp;&nbsp&nbsp&nbsp;&nbsp
          <button id="addItemBoxCancel" class="addItemBoxCancel addItemBoxBtn iqxin-closeBtn">取消</button>
          `;

        newDiv.innerHTML = innerHTML;

        this.ele.appendChild(newDiv);
        setTimeout(function () {
          newDiv.style.cssText = "";
        }, 10);
        document.querySelector("#iqxin-newTitle").select();
      }
      addEditBoxEnger() {
        var otitle, olink, oimg, oblank, ogbk;
        otitle = document.querySelector("#iqxin-newTitle").value;
        olink = document.querySelector("#iqxin-newLink").value;
        oimg = document.querySelector("#iqxin-newIcon").value;
        oblank = document.querySelector("#iqxin-newTarget").selectedIndex;
        ogbk = document.querySelector("#iqxin-newGBK").checked;

        this.editTemp.dataset.iqxintitle = otitle;
        this.editTemp.lastChild.innerText = otitle; //文本节点

        this.editTemp.dataset.iqxinlink = olink;
        this.editTemp.dataset.iqxinimg = oimg;
        this.editTemp.firstChild.src = oimg;

        // 是否新标签页打开
        if (oblank) {
          this.editTemp.removeAttribute("data-iqxintarget");
        } else {
          this.editTemp.dataset.iqxintarget = "_blank";
        }
        // 是否禁用
        if (ogbk) {
          this.editTemp.dataset.iqxingbk = "true";
        } else {
          this.editTemp.removeAttribute("data-iqxingbk");
        }

        // 修改完成,移除添加框
        this.addItemBoxRemove();
      }

      // 标题编辑
      addTitleEditBox(e) {
        this.addItemBoxRemove();

        var element = e.target.parentNode.firstChild;
        element.classList.remove("iqxin-pointer-events");

        var flag = document.querySelector("#titleEdit");
        // 存在编辑的标题 && 之前的编辑的节点与点击的节点是同一个节点
        if (flag && flag.parentNode == element) {
          element.innerHTML = element.firstChild.value
            ? element.firstChild.value
            : "空";
          element.classList.add("iqxin-pointer-events");
        } else {
          //  存在编辑的标题,但与点击的不是同一个节点
          if (flag) {
            flag.parentNode.innerHTML = flag.parentNode.firstChild.value;
          }
          var oldhtml = element.innerHTML;
          var newobj = document.createElement("input");
          newobj.id = "titleEdit";
          newobj.type = "text";
          newobj.value = oldhtml;
          // newobj.onblur = function(){
          //     element.innerHTML = this.value?this.value:oldhtml;
          // }
          newobj.onkeydown = function (e) {
            if ((e.keyCode || e.which) == 13) {
              element.innerHTML = this.value ? this.value : oldhtml;
            } else if ((e.keyCode || e.which) == 27) {
              element.innerHTML = oldhtml;
            }

            element.classList.add("iqxin-pointer-events");
          };
          element.innerHTML = "";
          element.appendChild(newobj);
          newobj.select();
        }
      }
      addTitleEditBoxRemove() {
        var odiv = document.querySelector("#titleEdit");
        if (odiv) {
          odiv.parentNode.innerHTML = odiv.value ? odiv.value : "空";
        }
      }

      // 高级菜单,配置文件编辑界面
      editCodeBox() {
        console.log("原始数据: ", settingData);
        var userSetting = GM_getValue("searchEngineJumpData");
        var editbox = document.createElement("div");
        // var sData =
        editbox.id = "iqxin-editCodeBox";
        editbox.style.cssText =
          "position:fixed;" +
          "top:50%;left:50%;" +
          "transform:translate(-50%,-50%);" +
          "background:#ccc;" +
          "border-radius:4px;" +
          "padding:10px 20px;";
        var innerH =
          " " +
          "<p><span style='color:red;font-size:1.2em;'>! ! !</span></br>" +
          "此处有更多的设置选项,自由度更高,</br>" +
          "但设置错误会导致脚本无法运行" +
          "</p>" +
          "<textarea wrap='off' cols='45' rows='20' style='overflow:auto;border-radius:4px;'>" +
          JSON.stringify(userSetting, false, 4) +
          "</textarea>" +
          "<br>" +
          "<button id='xin-reset'>清空设置</button> &nbsp;&nbsp;&nbsp;" +
          "<button id='xin-copyCode'>复制</button> &nbsp;&nbsp;&nbsp;" +
          "<button id='codeboxclose' class='iqxin-closeBtn'>关闭</button> &nbsp;&nbsp;&nbsp;" +
          "<button id='xin-codeboxsave' class='iqxin-enterBtn'>保存</button>" +
          "";
        editbox.innerHTML = innerH;
        this.ele.appendChild(editbox);
      }
      editCodeBoxSave() {
        var codevalue = document.querySelector(
          "#iqxin-editCodeBox textarea"
        ).value;
        if (codevalue) {
          GM_setValue("searchEngineJumpData", JSON.parse(codevalue));
          // 刷新页面
          setTimeout(function () {
            location.reload();
          }, 300);
        } else {
          // alert("输入为空");
          this.reset();
        }
      }
      editCodeBoxClose() {
        var box = document.querySelector("#iqxin-editCodeBox");
        if (box) {
          box.parentNode.removeChild(box);
        }
      }
      // “设置按钮” 透明度
      setBtnOpacityFun() {
        if (~window.navigator.userAgent.indexOf("Chrome")) {
          var odom = document.querySelector("#setBtnOpacityRange");
          var odomV = odom.value;
          // odom.style.backgroundSize = odom.value*100 +"% 100%";
          console.log(odomV, settingData.setBtnOpacity);
          if (settingData.setBtnOpacity < 0) {
            document.querySelector(".iqxin-setBtnOpacityRangeValue").innerHTML =
              odomV.toString().padEnd(4, "0");
            odom.style.background =
              "-webkit-linear-gradient(left,#3ABDC1,#83e7ea) no-repeat, #fff";
          } else {
            document.querySelector(".iqxin-setBtnOpacityRangeValue").innerHTML =
              "禁用";
            odom.style.background =
              "-webkit-linear-gradient(left,#bdbdbd,#c6c7c7) no-repeat, #fff";
          }
          odom.style.backgroundSize = odom.value * 100 + "% 100%";

          settingData.setBtnOpacity = -settingData.setBtnOpacity;
        } else {
          this.showPopUp("抱歉,目前只支持chrome类浏览器", 2500);
        }
      }

      // 标题点击 (开关搜索列表)(可以并入到下面的点击事件)
      titleClick(e) {
        var target = e.target;
        target.dataset.xin = -parseInt(target.dataset.xin);
        target.dataset.xin > 0
          ? this.showPopUp("启用")
          : this.showPopUp("禁用");
      }
      // 点击事件   此处的 if 需要根据实际情况替换成 elseif (switch)
      domClick(e) {
        var targetClass = e.target.className;
        var targetid = e.target.id;

        // 删除搜索
        if (~e.target.className.indexOf("iqxin-set-del")) {
          // console.log(e.target);
          e.target.parentNode.parentNode.removeChild(e.target.parentNode);
        }
        // 删除搜索列表
        if (~e.target.className.indexOf("iqxin-set-title-del")) {
          // console.log(e.target, e.target.parentNode.parentNode);
          e.target.parentNode.parentNode.parentNode.removeChild(
            e.target.parentNode.parentNode
          );
        }

        if (~e.target.className.indexOf("iqxin-additem")) {
          this.parentNode = e.target.parentNode;
          this.addItemBox();
        }
        if (e.target.className === "sej-engine") {
          e.target.dataset.iqxindisabled = e.target.dataset.iqxindisabled
            ? ""
            : "true";
          e.target.dataset.iqxindisabled
            ? this.showPopUp("禁用")
            : this.showPopUp("启用");
        }
        if (~targetClass.indexOf("addItemBoxCancel")) {
          this.addItemBoxRemove();
        }
        // 添加新的搜索 确定
        if (~targetClass.indexOf("addItemBoxEnter")) {
          this.addItemEnger();
        }
        // 添加新的搜索列表 确定
        if (targetid === "nSearchList") {
          this.addSearchListBox();
        }
        if (targetid === "addSearchListBoxEnter") {
          this.addSearchListEnger();
        }
        if (targetid === "addSearchListBoxCancel") {
          this.addItemBoxRemove("#newSearchListBox");
        }

        // 修改搜索 确定
        if (~targetClass.indexOf("editItemBoxEnter")) {
          this.addEditBoxEnger();
        }

        // 编辑框
        if (~e.target.className.indexOf("iqxin-set-edit")) {
          this.addEditBox(e);
        }
        // 标题编辑框
        if (~targetClass.indexOf("iqxin-title-edit")) {
          e.stopPropagation();
          this.addTitleEditBox(e);
        }
        if (~targetClass.indexOf("sejtitle")) {
          this.titleClick(e);
        }
        // codebox  源代码编辑框
        if (targetid === "codeboxclose") {
          this.editCodeBoxClose();
        } else if (targetid === "xin-reset") {
          this.reset();
        } else if (targetid === "xin-codeboxsave") {
          this.editCodeBoxSave();
        } else if (targetid === "xin-copyCode") {
          GM_setClipboard(JSON.stringify(settingData, false, 4));
          this.showPopUp("复制成功");
        }

        //  点击更多菜单
        if (targetid === "moreSet") {
          document.querySelector("#btnEle2").classList.toggle("btnEle2active");
          // iqxin-btn-active
          e.target.classList.toggle("iqxin-btn-active");
        }

        // 关闭"设置菜单按钮"
        if (targetClass === "iqxin-setBtnOpacityRangeValue") {
          this.setBtnOpacityFun();
        }

        // 关闭设置菜单
        if (targetid === "xin-close") {
          this.hide();
        }

        // 空白地方点击
        if (
          ~targetClass.indexOf("iqxin-items") ||
          targetid === "settingLayer" ||
          targetClass === "btnEleLayer"
        ) {
          this.allBoxClose();
        }
      }

      // 关闭所有次级窗口、菜单
      allBoxClose() {
        this.addItemBoxRemove(); // 新的搜索添加框
        this.addDelremove(); //  增加/删除界面
        this.editCodeBoxClose(); // code编辑框
        this.addTitleEditBoxRemove(); //标题编辑框
        this.addItemBoxRemove("#newSearchListBox"); // 添加新的搜索列表
        this.boxClose("#iqxin-sortBox"); // 搜索列表排序
        this.addItemBoxRemove("#importingBox"); //导入框
        document.querySelector("#btnEle2").classList.remove("btnEle2active"); // 更多设置
      }

      // 窗口位置拖动
      setDragNode(ele) {
        var node = document.querySelector("#dragDom");

        node.addEventListener("mousedown", function (event) {
          ele.style.transition = "null";
          // offsetLeft 距离 body 的位置, 得到的 dis 即鼠标到窗口左上角的位置
          var disX = event.clientX - ele.offsetLeft;
          var disY = event.clientY - ele.offsetTop;

          var move = function (event) {
            //鼠标的位置减去到左上角的位置 即窗口的位置
            // console.log(event.clientX - disX,event.clientY - disY)
            ele.style.left = event.clientX - disX + "px";
            ele.style.top = event.clientY - disY + "px";
          };

          document.addEventListener("mousemove", move);
          document.addEventListener("mouseup", function () {
            ele.style.transition = "0.5s";
            document.removeEventListener("mousemove", move);
          });
        });
      }

      // 拖动
      domdragstart(e) {
        if (~this.className.indexOf("sejtitle")) {
          SettingPanel.dragEl = this.parentNode;
        } else {
          SettingPanel.dragEl = this;
        }
        e.dataTransfer.effectAllowed = "move";
        e.dataTransfer.setData("text/html", SettingPanel.dragEl.innerHTML);
      }
      domdragenter(e) {
        var target = e.target;
        var targetClass = target.className;
        if (~targetClass.indexOf("sejtitle")) {
          target = target.parentNode;
        }
        target.classList.add("drop-over");
      }
      domdragover(e) {
        if (e.preventDefault) {
          e.preventDefault();
        }
        e.dataTransfer.dropEffect = "move";
        return false;
      }
      domdragleave(e) {
        var target = e.target;
        var targetClass = target.className;
        if (~targetClass.indexOf("sejtitle")) {
          target = target.parentNode;
        }
        target.classList.remove("drop-over");
      }
      domdrop(e) {
        var _this = e.target;
        var that = _this.parentNode;
        var pparentNode = that.parentNode;

        // 防止跨区域移动
        SettingPanel.prototype.domdropend();
        if (SettingPanel.dragEl.className != that.className) {
          console.log("移动对象 之前,现在: ", SettingPanel.dragEl.className);
          console.log(that.className);
          return;
        }

        // Sortable.js https://github.com/RubaXa/Sortable
        var targetRect = _this.getBoundingClientRect(); //
        var width = targetRect.right - targetRect.left; //目标节点的宽
        var height = targetRect.bottom - targetRect.top; //目标节点的高
        var domPosition = null;
        if (~_this.className.indexOf("sejtitle")) {
          if ((e.clientX - targetRect.left) / width > 0.5) {
            domPosition = true;
          } else {
            domPosition = false;
          }
        } else {
          if ((e.clientY - targetRect.top) / height > 0.5) {
            domPosition = true;
          } else {
            domPosition = false;
          }
        }

        SettingPanel.dragEl.style.transformOrigin = "top center";
        SettingPanel.dragEl.style.animation = "sejopen 0.3s";

        if (domPosition) {
          if (pparentNode.lastChild == that) {
            pparentNode.insertBefore(SettingPanel.dragEl, that);
          } else {
            pparentNode.insertBefore(
              SettingPanel.dragEl,
              that.nextElementSibling
            );
          }
        } else {
          that.parentNode.insertBefore(SettingPanel.dragEl, that);
        }

        // 重新绑定拖拽事件
        SettingPanel.prototype.dragEvent();
        return false;
      }
      domdropend() {
        var dom = document.querySelector(".drop-over");
        if (dom) {
          dom.classList.remove("drop-over");
        }
      }

      // 判断是否能连接至google
      isOnline() {
        console.log("this.online", this.online);
        if (this.online) return;

        var that = this;
        var myImage = new Image();
        myImage.src =
          "https://www.google.com/s2/favicons?domain=www.baidu.com&" +
          Math.random();
        setTimeout(function () {
          // console.log("取消加载");
          console.log(myImage.width);
          if (myImage.width) {
            that.online = true;
          } else {
            myImage.src = undefined;
          }
        }, 2000);
      }

      // 重新加载工具
      reloadSet() {
        var elems = document.querySelectorAll(
          "#sej-container, #settingLayerMask, sejspan.sej-drop-list"
        );
        if (!elems) return;
        console.log("elems: " + elems);
        // return;

        [].forEach.call(elems, function (elem) {
          elem.parentNode.removeChild(elem);
        });

        mainLogic();
        this.showPopUp("保存成功");
      }

      // 设置按钮透明度设置
      rangeChange(bool) {
        var odom = document.querySelector("#setBtnOpacityRange");
        if (settingData.setBtnOpacity < 0) {
          odom.style.background =
            "-webkit-linear-gradient(left,#bdbdbd,#c6c7c7) no-repeat, #fff";
          odom.style.backgroundSize = odom.value * 100 + "% 100%";
          document.querySelector(".iqxin-setBtnOpacityRangeValue").innerHTML =
            "禁用";
          settingData.setBtnOpacity = -odom.value;
        } else {
          odom.style.background =
            "-webkit-linear-gradient(left,#3ABDC1,#83e7ea) no-repeat, #fff";
          odom.style.backgroundSize = odom.value * 100 + "% 100%";
          let value = odom.value;
          let valueStr = "";
          if (value == 0) {
            valueStr = "0.00";
          } else if (value == 1) {
            valueStr = "1.00";
          } else {
            valueStr = odom.value.toString().padEnd(4, "0");
          }
          document.querySelector(".iqxin-setBtnOpacityRangeValue").innerHTML =
            valueStr;
          settingData.setBtnOpacity = odom.value;
        }
      }

      // 窗口大小改变
      windowResize() {
        var eleStyle = window.getComputedStyle(this.ele, null);
        var w = parseInt(eleStyle.width);
        var h = parseInt(eleStyle.height) + 54;
        var ww = document.documentElement.clientWidth;
        var wh = document.documentElement.clientHeight;
        var maskStyle = this.mask.style;

        if (w >= ww) {
          maskStyle.justifyContent = "stretch";
        } else {
          maskStyle.justifyContent = "center";
        }
        if (h >= wh) {
          maskStyle.alignItems = "stretch";
        } else {
          maskStyle.alignItems = "center";
        }
      }
      saveData() {
        this.addTitleEditBoxRemove(); //标题栏处于编辑状态

        var obj = {};
        var parentdiv = document.querySelectorAll("#settingLayer .iqxin-items");
        for (let i = 0; i < parentdiv.length; i++) {
          var data = parentdiv[i].querySelectorAll(".sej-engine");
          var id = parentdiv[i].id;
          obj[id] = [];
          for (let ii = 0; ii < data.length; ii++) {
            if (data[ii].dataset.xin < 0) {
              var ij = -ii;
            } else {
              ij = ii;
            }
            obj[id][ij] = {};
            obj[id][ij].favicon = data[ii].dataset.iqxinimg;
            obj[id][ij].name = data[ii].dataset.iqxintitle;
            obj[id][ij].url = data[ii].dataset.iqxinlink;
            if (data[ii].dataset.iqxintarget) {
              obj[id][ij].blank = data[ii].dataset.iqxintarget;
            }
            if (data[ii].dataset.iqxindisabled) {
              obj[id][ij].disable = data[ii].dataset.iqxindisabled;
            }
            if (data[ii].dataset.iqxingbk) {
              obj[id][ij].gbk = data[ii].dataset.iqxingbk;
            }
          }
        }

        // 分类名称
        var engineDetails = [];

        // 分类排序
        var odetails = document.querySelectorAll(".sejtitle");
        var odetailsLength = odetails.length;
        for (let i = 0; i < odetailsLength; i++) {
          engineDetails[i] = [];
          engineDetails[i][0] = odetails[i].firstChild.innerHTML;
          engineDetails[i][1] = odetails[i].dataset.iqxintitle;
          engineDetails[i][2] = odetails[i].dataset.xin >= 0 ? true : false;
        }

        // 新标签页全局设置
        var onewtab = document.querySelector(
          "#iqxin-globalNewtab"
        ).selectedIndex;
        var foldlist = document.querySelector("#iqxin-foldlist").checked;

        // 以防不测,重新获取本地配置文件
        var getData = GM_getValue("searchEngineJumpData");
        getData.newtab = onewtab;
        getData.foldlist = foldlist;
        getData.setBtnOpacity = settingData.setBtnOpacity;
        getData.center = document.querySelector("#iqxin-center").selectedIndex;
        getData.fixedTop = document.querySelector("#iqxin-fixedTop").checked;
        getData.allOpen = document.querySelector("#iqxin-allOpen-item").checked;
        getData.fixedTopUpward = document.querySelector(
          "#iqxin-fixedTopUpward-item"
        ).checked;
        getData.transtion = document.querySelector("#iqxin-transtion").checked;
        getData.HideTheSameLink = document.querySelector(
          "#iqxin-HideTheSameLink"
        ).checked;
        getData.selectSearch = document.querySelector(
          "#iqxin-selectSearch"
        ).checked;
        getData.engineDetails = engineDetails;
        getData.engineList = obj;

        GM_setValue("searchEngineJumpData", getData);
      }
      // 此处的样式主要是设置界面
      addGlobalStyle() {
        // 关闭设置菜单中的所有动画效果
        if (!settingData.transtion) {
          GM_addStyle(
            "#settingLayer," +
              "#btnEle span," +
              "#btnEle2," +
              ".iqxin-set-del," +
              "span.iqxin-additem," +
              "#newSearchBox," +
              ".addItemBoxBtn," +
              "#xin-close," +
              "#settingLayerMask{" +
              "transition:none;" +
              "}" +
              "#settingLayerMask{" +
              "backdrop-filter:none;" +
              // "background-color: rgba(0,0,0,.7);" +
              "}" +
              ""
          );
        }
      }
      showPopUp(text, duration) {
        new PopUp(text, duration);
      }
    }

    class PopUp {
      constructor(text, duration = 1500) {
        this.popUp = document.createElement("iqxinDiv");
        this.popUp.id = "iqixn-global-tip";
        this.show(text);
        setTimeout(() => {
          this.destroy();
        }, duration);
      }
      show(text) {
        this.popUp.innerText = text;
        document.body.appendChild(this.popUp);
        this.popUp.style.opacity = 1;
      }
      destroy() {
        this.popUp.style.opacity = 0;
        const transitionTime =
          parseFloat(getComputedStyle(this.popUp).transitionDuration) * 1000;
        setTimeout(() => {
          this.popUp.remove();
        }, transitionTime);
      }
    }

    const settings = new Settings();
    const settingData = settings.settingData;
    engineList = settingData.engineList;
    const matchedRule = settings.getMatchedRule();

    const globalStyle = GM_getResourceText("GLOBAL_STYLE");
    GM_addStyle(globalStyle);
    // 取消工具列表动画和毛玻璃效果
    if (!settingData.transtion) {
      const nonTransitionStyle = `.sej-engine,.sej-drop-list-trigger,.sej-drop-list{transition:none!important;}#sej-container{animation:none!important;}.sej-drop-list {backdrop-filter:none!important;}`;
      GM_addStyle(nonTransitionStyle);
    }
    // 夜间模式
    if (
      document.getElementsByTagName("meta")?.["color-scheme"]?.content ===
      "dark"
    ) {
      document.body.setAttribute("qxintheme", "dark");
    }

    const jumpBar = new JumpBar(engineList, settingData, matchedRule);
    if (jumpBar.container) {
      new SettingButton(jumpBar.container, settingData);
    } else {
      return;
    }
  }
})();