Wod 自动完成下拉框

Add auto completion text field for item type and skill bonus selector

//-----------------------------------------------------------------------------
// [WoD] Item type / skill bonus auto completion
// Copyright (c) Jim Zhai
//-----------------------------------------------------------------------------
// ==UserScript==
// @name          Wod 自动完成下拉框
// @icon          http://info.world-of-dungeons.org/wod/css/WOD.gif
// @author        Jim Zhai
// @namespace     org.toj
// @description   Add auto completion text field for item type and skill bonus selector
// @include       http*://*.world-of-dungeons.*/wod/spiel/trade/trade.php*
// @include       http*://*.world-of-dungeons.*/wod/spiel/hero/items.php*
// @include       http*://*.world-of-dungeons.*/wod/spiel/tournament/duell.php*
// @include       http*://*.world-of-dungeons.*/wod/spiel/hero/skillconf_nojs.php*
// @include       http*://*.world-of-dungeons.*/wod/spiel/hero/skillconf.php*
// @include       http*://*.world-of-dungeons.*/wod/spiel/hero/skillconfig.php*
// @include       http*://zhao.world-of-dungeons.*
// @require       https://code.jquery.com/jquery-3.3.1.min.js
// @require       https://unpkg.com/select2@4.0.13/dist/js/select2.min.js
// @require       https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js
// @modifier      Christophero
// @version       2023.08.01.1
// ==/UserScript==

$(function () {
  ("use strict");

  let styleinitialized = false;

  function insertCss(select, styles) {
    if (document.styleSheets.length === 0) {
      //如果没有style标签,则创建一个style标签
      var style = document.createElement("style");
      document.head.appendChild(style);
    }
    var styleSheet = document.styleSheets[document.styleSheets.length - 1]; //如果有style 标签.则插入到最后一个style标签中
    var str = select + " {"; //插入的内容必须是字符串,所以得把obj转化为字符串
    for (var prop in styles) {
      str +=
        prop.replace(/([A-Z])/g, function (item) {
          //使用正则把大写字母替换成 '-小写字母'
          return "-" + item.toLowerCase();
        }) +
        ":" +
        styles[prop] +
        ";";
    }
    str += "}";
    styleSheet.insertRule(str, styleSheet.cssRules.length); //插入样式到最后一个style标签中的最后面
  }

  let $toolbarCss = $("<link>");
  $("head").prepend($toolbarCss);
  $toolbarCss.attr({
    rel: "stylesheet",
    type: "text/css",
    href: "https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css",
  });

  let minOptionCount = 13;
  let ignoreFields = [/^EquipItem\[\d+\]$/i, /^sndgrp\d*\[\d+\]$/i];
  const ignoreIds = [
    "xaerodegreaz_userscript_hero_select",
    "changeHeroGroupSelect",
  ];

  function needSelect2($this) {
    return (
      minOptionCount > 0 &&
      $this.find("option").length > minOptionCount &&
      nameCheck($this.prop("name")) &&
      idCheck($this.attr("id"))
    );
  }

  function addHideClass() {
    let $this = $(this);
    if (!needSelect2($this)) return;
    $this.addClass("select2-hidden-accessible");
  }

  function fullAddHideClass() {
    $("#orders select").each(addHideClass);
  }

  function fullReSelect2() {
    console.log("销毁并重构");
    // 先销毁所有select2
    $("#orders select").each(function (i, e) {
      if ($(this).next(".select2-container").length) {
        $(this).select2("destroy");
      }
    });
    // 移除所有隐藏类
    $("#orders select").removeClass("select2-hidden-accessible");
    // 对所有可见的select构建select2的下拉框
    $("#orders select:visible").each(function (i, e) {
      let $this = $(this);
      if (!needSelect2($this)) return;
      $this.find("option").each(function () {
        if (!this.text) {
          this.text = " ";
        }
      });
      $this
        .select2({
          width: "resolve",
          language: {
            noResults: function (params) {
              return "暂无数据";
            },
          },
        })
        .on("select2:select", function () {
          deFullReSelect2();
        });
    });
  }

  const deFullReSelect2 = _.debounce(fullReSelect2, 50, {
    leading: true,
    trailing: false,
  });
  const deFullAddHideClass = _.debounce(fullAddHideClass, 50, {
    leading: true,
    trailing: false,
  });

  function prepareSelect2() {
    // 初始化select2样式
    initSelect2Style();

    if (
      [
        "/wod/spiel/hero/skillconf_nojs.php",
        "/wod/spiel/hero/skillconf.php",
        "/wod/spiel/hero/skillconfig.php",
      ].includes(location.pathname)
    ) {
      // 选择技能时会导致页面显示刷新,将select2的隐藏class移除,这里需要加回来
      $("#orders select").on("change", function () {
        // fullAddHideClass();
        deFullAddHideClass();
        if (!$(this).next(".select2-container").length) {
          // fullReSelect2();
          deFullReSelect2();
        }
      });
      // 点击行动时会根据行动重新生成右侧select,这时select2还是之前数据,需要销毁重新生成
      // 点击地城/决斗/一般分页也会导致页面刷新,追加
      // 点击侧面按钮也会导致页面刷新
      $(
        '#orders .wod-list-items, .wod-tabs li[id^="wod-orders-tab-"], .wod-list-buttons img, .wod-list-buttons button'
      ).click(function () {
        deFullReSelect2();
      });

      deFullReSelect2();

      $("#main_content select").each(function (i, select) {
        let $this = $(select);
        let previousValue = select.style.display;
        // 监听select的style变化,这里在select显示隐藏发生变化时进行处理
        const observer = new MutationObserver(function (mutations) {
          mutations.forEach(function (mutation) {
            if (mutation.attributeName !== "style") return;
            const currentValue = mutation.target.style.display;
            if (currentValue !== previousValue) {
              // select 显示
              if (previousValue === "none" && currentValue !== "none") {
                console.log("display none has just been removed!");
                if (!needSelect2($this)) return;
                $this.find("option").each(function () {
                  if (!this.text) {
                    this.text = " ";
                  }
                });
                // 选中选项时,需要重新刷新select2状态
                $this
                  .select2({
                    width: "resolve",
                    language: {
                      noResults: function (params) {
                        return "暂无数据";
                      },
                    },
                  })
                  .on("select2:select", function () {
                    deFullAddHideClass();
                    // setTimeout(fullReSelect2, 50);
                  });
              } else if (previousValue !== "none" && currentValue === "none") {
                try {
                  $this.select2("destroy");
                } catch (e) {}
              }

              previousValue = mutation.target.style.display;
            }
          });
        });
        observer.observe(select, { attributes: true });
      });
    } else {
      // 非设置页面直接进行select2设置就行了
      $("select").each(function () {
        let $this = $(this);
        if (!needSelect2($this)) return;
        $this.find("option").each(function () {
          if (!this.text) {
            this.text = " ";
          }
        });

        $this.select2({
          width: "resolve",
          language: {
            noResults: function (params) {
              return "暂无数据";
            },
          },
        });
      });
    }
  }

  /**
   *
   * @returns 初始化select2下拉框样式,从第一个select上获得高度,颜色等
   */
  function initSelect2Style() {
    if (styleinitialized) return;
    let $this = $(".main_content select:visible:first");
    let dummy = false;
    if (!$this.length) {
      const $dummySelect = $("<select><option>DUMMY SELECT</option></select>");
      $(".main_content").append($dummySelect);
      $this = $dummySelect;
      dummy = true;
    }
    const height = $this.css("height");
    const outerHeight = $this.outerHeight() + "px";
    const backgroundColor = $this.css("background-color");
    const fontColor = $this.css("color");
    const border = $this.css("border");
    const fontSize =
      ((parseFloat($this.css("font-size")) * 72.0) / 96.0).toFixed() + "pt";
    const borderRadius = $this.css("border-radius");
    initStyle({
      outerHeight,
      border,
      borderRadius,
      backgroundColor,
      height,
      fontSize,
      fontColor,
    });
    if (dummy) {
      $this.remove();
    }
  }

  function initStyle({
    outerHeight,
    border,
    borderRadius,
    backgroundColor,
    height,
    fontSize,
    fontColor,
  }) {
    if (styleinitialized) return;
    insertCss(".select2-results__option", { padding: "2px 5px" });
    insertCss(
      ".select2-container--default .select2-results>.select2-results__options",
      { maxHeight: "60vh", borderRadius: "0 0 5px 5px" }
    );
    insertCss(".select2-container .select2-selection--single", {
      height: outerHeight,
      border,
      borderRadius,
      backgroundColor,
    });
    insertCss(
      ".select2-container--default .select2-selection--single .select2-selection__rendered, .select2-container--default .select2-selection--multiple .select2-selection__rendered",
      {
        fontSize,
        color: fontColor,
        paddingLeft: "5px",
        lineHeight: "1.5",
        textOverflow: "inherit",
      }
    );
    insertCss(
      ".select2-container--default .select2-selection--single .select2-selection__rendered",
      {
        height,
      }
    );
    insertCss(
      ".select2-container--default .select2-selection--single .select2-selection__arrow",
      { height }
    );
    insertCss(
      ".select2-container--default .select2-selection--single, .select2-container--default .select2-selection--multiple",
      {
        backgroundColor,
        fontSize,
        color: fontColor,
        border,
      }
    );
    insertCss(".select2-dropdown", {
      backgroundColor,
      fontSize,
      color: fontColor,
      border,
    });
    insertCss(".select2-selection", {
      backgroundColor,
      fontSize,
    });
    insertCss(".select2-results__option", {
      backgroundColor,
      color: fontColor,
    });
    const arrowColor =
      backgroundColor == "rgb(255, 255, 255)" ? "#000" : "#fff";
    insertCss(
      ".select2-container--default .select2-selection--single .select2-selection__arrow b, .select2-container--default .select2-selection--multiple .select2-selection__arrow b",
      { borderColor: `${arrowColor} transparent transparent transparent` }
    );
    insertCss(
      ".select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b, .select2-container--default.select2-container--open .select2-selection--multiple .select2-selection__arrow b",
      { borderColor: `transparent transparent ${arrowColor} transparent` }
    );
    if (
      backgroundColor == "rgb(102, 91, 71)" &&
      fontColor == "rgb(255, 255, 255)"
    ) {
      insertCss(
        ".select2-container--default .select2-selection--multiple .select2-selection__choice",
        {
          backgroundColor: "rgb(99 71 19)",
        }
      );
    }
  }

  /**
   * 跳过名称符合正则的下拉框
   * @param {*} field
   * @returns
   */
  function nameCheck(field) {
    if (ignoreFields.length <= 0) {
      return true;
    }

    let flag = true;
    $.each(ignoreFields, function (index, pattern) {
      if (field.search(pattern) != -1) {
        flag = false;
      }
    });
    return flag;
  }

  /**
   * 跳过ID在列表中的下拉框
   * @param {*} id
   * @returns
   */
  function idCheck(id) {
    if (ignoreIds.includes(id)) {
      return false;
    }
    return true;
  }

  prepareSelect2();
});