tianteng

1. 绅士漫画,下拉浏览页面,由原先的一列模式改为两列模式 2. javbus, 添加 "粘贴并搜索" 按钮; 添加 "打开srbt" 链接; 详情页面添加复制车牌号按钮.

Fra 01.07.2024. Se den seneste versjonen.

// ==UserScript==
// @name         tianteng
// @namespace    https://greasyfork.org/xmlspy
// @version      0.5.0
// @description  1. 绅士漫画,下拉浏览页面,由原先的一列模式改为两列模式 2. javbus, 添加 "粘贴并搜索" 按钮; 添加 "打开srbt" 链接; 详情页面添加复制车牌号按钮.

// @author       xmlspy
// @license      MIT

// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js

// @include      *
// @connect      *

// @run-at document-start

// @grant        GM_xmlhttpRequest
// @grant        GM.setClipboard
// @grant        GM.xmlhttpRequest
// @grant        unsafeWindow
// @grant        GM_log
// @grant        GM_info


// ==/UserScript==

(function () {
  "use strict";
  const enableDebug = true;
  let seq = 0;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////// 事件拦截
  /////////////////////////////////////////////////////////////////////////////////////////////
  debug(document.querySelector("title"));
  //松鼠症倉庫 地址发布页 https://ahrilist.top/
  const ep = EventTarget.prototype;
  if (ep.addEventListenerOriginal == null) {
    ep.addEventListenerOriginal = ep.addEventListener;
    function addEventListenerHook(type, callback, options) {
      // if (/ahri8\.top/.test(window.location.href)) {
      if (this.className && this.className.includes("apo")) {
        debug("松鼠症倉庫-禁止点击广告");
        return;
      }
      // }
      this.allListeners = this.allListeners || [];
      this.allListeners.push({ type, callback, options });
      this.addEventListenerOriginal(type, callback, options);
    }
    ep.addEventListener = addEventListenerHook;
  }

  const skrbtDomain = "skrbtqx";
  const skrbtHost = skrbtDomain + ".top";
  const skrbtUrl = "https://" + skrbtHost;

  // execute(()=>{},()=>{});

  $(function () {
    // execute("松鼠症倉庫", /ahri8\.top/, () => {
    //   removeEvents(document.querySelectorAll("a.apo"), "click");

    //   debug("-------------------------------------------------------xxxx");
    // });
    execute("开源中国", /oschina\.net(\/)?$/, () => {
      $("div.headline > div.head-news > div").each(function (index) {
        const $this = $(this);
        const href = $this.attr("data-href");
        const classCss = $this.attr("class");
        const title = $this.attr("title");
        const html = `<a target="_blank" title="${title}" href="${href}" class="${classCss}">${title}</a>`;
        $this.replaceWith(html);
      });
    });
    //////////////////////////////////////////////////////////////////////////////////////////////
    /////////////////// 紳士漫畫永久域名: wnacg.com 紳士漫畫永久地址發佈頁: wnacg.date
    /////////////////////////////////////////////////////////////////////////////////////////////
    execute("紳士漫畫", '$head>title:contains("紳士漫畫")', () => {
      if (location.href.includes("photos-slide-aid")) {
        const nodeToObserve = document.querySelector("#img_list");
        $(nodeToObserve).css({
          width: "100%",
          display: "flex",
          "flex-wrap": "wrap",
          "justify-content": "flex-start",
          "overflow-x": "hidden",
        });
        const imgWidth = document.documentElement.clientWidth / 2 - 10;
        const imgHeight = document.documentElement.clientHeight - 50;
        const observer = new MutationObserver((mutations, observer) => {
          $("#img_list>div").css({
            flex: "1",
            "background-color": "#cacaca",
            margin: "0 5px 5px 0",
            width: "calc((100% - 10px) / 2)",
            "min-width": "calc((100% - 10px) / 2)",
            "max-width": "calc((100% - 10px) / 2)",
          });
          $("#img_list>div>img").on("load", (e) => {
            drawImage(e.target, imgWidth, imgHeight);
          });
        });
        observer.observe(nodeToObserve, { childList: true });
      }
    });

    //////////////////////////////////////////////////////////////////////////////////////////////
    // 永久域名:https://www.javbus.com 防屏蔽地址:https://www.fanbus.help
    // 防屏蔽地址:https://www.javsee.help 防屏蔽地址:https://www.buscdn.help
    /////////////////////////////////////////////////////////////////////////////////////////////
    execute(
      "javbus",
      [
        '$head>title:contains("JavBus")',
        // /genre/hd , /genre/sub
        "$body > nav > div > div.navbar-header.mh50 > a > img[alt='JavBus']",
        // 论坛
        "#toptb.jav-nav",
      ],
      () => {
        const searchButton = $(
          "button[onclick=\"searchs('search-input')\"]:first"
        );
        const searchInput = $("#search-input:first");
        addPasteAndSearchButton(searchButton, searchInput);

        // 调整样式
        $(".nav>li>a").attr("style", "padding-left:8px;padding-right:8px;");

        //添加skrbt链接
        $(".nav-title.nav-inactive:last,ul.nav.navbar-nav:first").append(`
                      <li class="hidden-md hidden-sm">
                          <a href="${skrbtUrl}" target="_blank">打开skrbt</a>
                      </li>
                  `);

        let chePaiNode = document.querySelector(
          "body > div.container > div.row.movie > div.col-md-3.info > p:nth-child(1) > span:nth-child(2)"
        );
        // 明细页面
        if (chePaiNode) {
          const chePai = chePaiNode.innerText.trim();
          const toAppendElement = document.querySelector(
            "body > div.container > div.row.movie > div.col-md-3.info > p:nth-child(1)"
          );

          appendCopyButton(chePai, toAppendElement);
          // 删除磁力链接中的onclick事件
          setInterval(() => $("#magnet-table td").removeAttr("onclick"), 1000);
        }
      }
    );

    //////////////////////////////////////////////////////////////////////////////////////////////
    /////////////////// 添加 粘贴&搜索 按钮
    /////////////////////////////////////////////////////////////////////////////////////////////
    [
      // 知乎
      {
        title: "知乎-添加'粘贴&搜索'按钮",
        condition: /zhihu\.com/,
        searchButton: '$button[class*="SearchBar-searchButton"]',
        searchInput: "#Popover1-toggle",
      },
      // skrbt 永 久 地 址 ( 务 必 收 藏 ): skrfabu.top skrso.link
      {
        title: "skrbt-添加'粘贴&搜索'按钮",
        condition: `$head>link[rel='shortcut icon'][href*='skrbt']`,
        searchButton: `$button.search-btn`,
        searchInput: `$input.search-input[name='keyword']`,
      },
      // 百度
      {
        title: "百度-添加'粘贴&搜索'按钮",
        condition: /baidu\.com/,
        searchButton: `#su`,
        searchInput: `#kw`,
      },
      // 必应
      // {
      //   condition: /bing\.com/,
      //   searchButton: ()=>{
      //     let inputs = document.querySelectorAll('input');
      //     return inputs[1];
      //   },
      //   searchInput: `#sb_form_q`,
      // },
    ].forEach((v, k) => {
      let { title, condition, searchButton, searchInput, callback } = v;
      execute(title, condition, () => {
        searchButton = evalParam(searchButton);
        searchInput = evalParam(searchInput);
        addPasteAndSearchButton(searchButton, searchInput, callback);
      });
    });
  });
  //////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////// 公共方法
  /////////////////////////////////////////////////////////////////////////////////////////////

  /**
   *
   * @param {String|Function} param
   * 当类型为String时,
   *  - 如果param是以$开头,则认为这个param为Jquery表达式,会去掉字符$,并返回;
   *  - 否则,认为param为常规css选择符,会返回document.querySelector(param)
   * 当类型为Function时,
   *  直接执行此方法,并使用$()包装并返回
   *
   * @returns {Jquery} 返回Jquery对象
   *
   * @example
   * - evalParam('$div[style="btn"]'); // param为jquery的css selector,返回$('div[style="btn"]')
   * - evalParam('#container'); // param为常规的css selector,返回 $(document.querySelecotr('#container'))
   * - evalParam(()=>{
   *     return document.querySelector('#container');
   *   });  // param为方法,返回$(param())
   *
   */
  function evalParam(param) {
    if ($.type(param) === "string") {
      if (param.startsWith("$")) {
        return $(param.substring(1));
      }
      return $(document.querySelector(param));
    }
    if ($.isFunction(param)) {
      return $(param());
    }
  }

  function execute(title, condition, callback) {
    if (checkCondition(condition) === true) {
      if (callback) {
        debug(condition.toString(), title);
        callback();
      }
    }
    // if ($.isArray(condition)) {
    //   for (let c of condition) {
    //     if (checkCondition(c)) {
    //       debug(condition.toString(), title);
    //       callback && callback();
    //       return;
    //     }
    //   }
    // }
  }

  /**
   *
   * @param {RegExp|Function|Boolean|String} condition
   * @returns {Boolean}
   */
  function checkCondition(condition) {
    if ($.type(condition) === "regexp") {
      const href = window.location.href;
      return condition.test(href);
    }
    if ($.isFunction(condition)) {
      return condition() === true;
    }
    if ($.type(condition) === "boolean") {
      return condition === true;
    }
    if ($.type(condition) === "string") {
      if (condition.startsWith("$")) {
        return $(condition.substring(1)).length > 0;
      }
      return document.querySelector(condition) != null;
    }

    if ($.isArray(condition)) {
      for (let c of condition) {
        if (checkCondition(c)) {
          return true;
        }
      }
    }
    return false;
  }

  function addPasteAndSearchButton($searchButton, $searchInput, callback) {
    const styleMap = {};
    $searchButton[0].computedStyleMap().forEach((value, key) => {
      styleMap[key] = value;
    });
    const $pasteAndSearchButton = $(
      `<input type="button" value="粘贴&搜索" id="pasteAndSearch"></input>`
    );
    $pasteAndSearchButton.css(styleMap);
    $searchButton.after($pasteAndSearchButton);
    $pasteAndSearchButton.click(() => {
      navigator.clipboard.readText().then((clipText) => {
        if (clipText != null && $.trim(clipText) != "") {
          $searchInput.val($.trim(clipText));
          $searchButton.click();
        }
      });
    });
    callback && callback($searchButton, $searchInput, $pasteAndSearchButton);
  }

  function appendCopyButton(chePai, toAppendElement) {
    var copyButton = document.createElement("button");
    copyButton.innerHTML = "复 制";
    copyButton.setAttribute("id", "copyButton");
    toAppendElement.appendChild(copyButton);
    document.addEventListener("click", (e) => {
      if (e.target.getAttribute("id") === "copyButton") {
        GM.setClipboard(chePai, "text");
      }
    });
  }

  function debug(str, title) {
    if (enableDebug) {
      if (!str) {
        str = "";
      }
      if (!Array.isArray(str)) {
        str = [str];
      }
      seq++;
      console.log(
        `%c【tianteng ${GM_info.script.version}】 ${title ? title : "debug"}:`,
        "color: yellow;font-size: large;font-weight: bold;background-color: darkblue;",
        seq,
        ...str
      );
    }
  }

  /**
   * 图片按宽高比例进行自动缩放
   * @param ImgObj
   *     缩放图片源对象
   * @param maxWidth
   *     允许缩放的最大宽度
   * @param maxHeight
   *     允许缩放的最大高度
   * @usage
   *     调用:<img src="图片" onload="javascript:drawImage(this,300,200)">
   */
  function drawImage(ImgObj, maxWidth, maxHeight) {
    var image = new Image();
    //原图片原始地址(用于获取原图片的真实宽高,当<img>标签指定了宽、高时不受影响)
    image.src = ImgObj.src;
    // 用于设定图片的宽度和高度
    var tempWidth;
    var tempHeight;

    if (image.width > 0 && image.height > 0) {
      //原图片宽高比例 大于 指定的宽高比例,这就说明了原图片的宽度必然 > 高度
      if (image.width / image.height >= maxWidth / maxHeight) {
        if (image.width > maxWidth) {
          tempWidth = maxWidth;
          // 按原图片的比例进行缩放
          tempHeight = (image.height * maxWidth) / image.width;
        } else {
          // 按原图片的大小进行缩放
          tempWidth = image.width;
          tempHeight = image.height;
        }
      } else {
        // 原图片的高度必然 > 宽度
        if (image.height > maxHeight) {
          tempHeight = maxHeight;
          // 按原图片的比例进行缩放
          tempWidth = (image.width * maxHeight) / image.height;
        } else {
          // 按原图片的大小进行缩放
          tempWidth = image.width;
          tempHeight = image.height;
        }
      }
      // 设置页面图片的宽和高
      ImgObj.height = tempHeight;
      ImgObj.width = tempWidth;
      // 提示图片的原来大小
      ImgObj.alt = image.width + "×" + image.height;
    }
  }

  /**
   *
   * @param {EventTarget|NodeList|Array|jQuery} elements
   * @param {undefined|null|String|Array} events
   */
  function removeEvents(elements, events) {
    if (!elements) return;
    if (elements instanceof EventTarget) {
      elements = [elements];
    }
    if (elements instanceof jQuery) {
      elements = elements.toArray();
    }

    if (!events) {
      elements.forEach((element) => {
        for (let t in element) {
          if (t.startsWith("on") && element[t] != null) {
            element[t] = null;
            console.log("cleanup removed listener from " + element.nodeName, t);
          }
        }
        for (let t of element.allListeners || []) {
          element.removeEventListener(t.type, t.callback, t.options);
          console.log(
            "cleanup removed listener from " + element.nodeName,
            t.type
          );
        }
        element.allListeners = [];
      });
    } else {
      if (typeof events === "string") {
        events = [events];
      }
      if (!Array.isArray(events)) {
        return;
      }
      events.forEach((event) => {
        const onEvent = "on" + event;
        elements.forEach((element) => {
          for (let t in element) {
            if (t.startsWith(onEvent) && element[t] != null) {
              element[t] = null;
              console.log(
                "cleanup removed listener from " + element.nodeName,
                t
              );
            }
          }
          // const toRemoved = [];
          element.allListeners = element.allListeners || [];
          let allListenersNew;
          element.allListeners.forEach((t, i) => {
            if (t.type === event) {
              element.removeEventListener(t.type, t.callback, t.options);
              allListenersNew = element.allListeners.slice(i, 1);
              // toRemoved.push(i);
              console.log(
                "cleanup removed listener from " + element.nodeName,
                t.type
              );
            }
          });
          // toRemoved.forEach((item, index) => {
          //   element.allListeners = element.allListeners.slice(item, 1);
          // });
          if (allListenersNew) {
            element.allListeners = allListenersNew;
          }
        });
      });
    }
  }
})();