「Z-Blog」论坛辅助

针对 Z-Blog 官方论坛的辅助脚本

// ==UserScript==
// @name         「Z-Blog」论坛辅助
// @namespace    https://www.wdssmq.com/
// @version      1.0.6
// @author       沉冰浮水
// @description  针对 Z-Blog 官方论坛的辅助脚本
// @license      MIT
// @link         https://gf.qytechs.cn/zh-CN/scripts/419517
// @null         ----------------------------
// @contributionURL    https://github.com/wdssmq#%E4%BA%8C%E7%BB%B4%E7%A0%81
// @contributionAmount 5.93
// @null         ----------------------------
// @link         https://github.com/wdssmq/userscript
// @link         https://afdian.com/@wdssmq
// @link         https://gf.qytechs.cn/zh-CN/users/6865-wdssmq
// @null         ----------------------------
// @noframes
// @run-at       document-end
// @match        https://bbs.zblogcn.com/*
// @match        https://app.zblogcn.com/zb_system/admin/edit.php*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @require      https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/showdown.min.js
// ==/UserScript==

/* eslint-disable */
/* jshint esversion: 6 */

(function () {
  'use strict';

  const gm_name = "zbp-xiuno";

  // 初始变量
  const $n = (selector, context = document) => context.querySelector(selector);
  const $ = window.jQuery || unsafeWindow.jQuery;
  const UM = window.UM || unsafeWindow.UM;
  const UE = window.UE || unsafeWindow.UE;
  const curHref = location.href.replace(location.hash, "");
  // localStorage 封装
  const lsObj = {
    setItem: function(key, value) {
      localStorage.setItem(key, JSON.stringify(value));
    },
    getItem: function(key, def = "") {
      const item = localStorage.getItem(key);
      if (item) {
        return JSON.parse(item);
      }
      return def;
    },
  };
  // 预置函数
  const _log = (...args) => console.log(`[${gm_name}]\n`, ...args);

  (() => {
    const $body = $n("body");
    const defData = {
      status: null, // 用于记录状态
      href: "",
    };

    // 更新 lsData 中的 status 状态
    const updateStatus = (status) => {
      const lsData = lsObj.getItem("xiuno_login", defData);
      lsData.status = status;
      lsData.href = status === "未登录(不可用)" ? curHref : "";
      lsObj.setItem("xiuno_login", lsData);
    };

    // 登录(不可用)后跳转前登录(不可用)前的页面
    const goUrl = () => {
      // 读取 localStorage
      const lsData = lsObj.getItem("xiuno_login", defData);
      // 根据记录的状态判断是否跳转
      if (lsData.status === "未登录(不可用)" && lsData.href) {
        // 读取要跳转的地址
        const href = lsData.href;
        // 更新状态
        updateStatus("已登录(不可用)");
        // 跳转前的页面地址
        location.href = href;
      }
    };

    // 主入口函数,判断是否登录(不可用)
    const checkLogin = () => {
      // 判断 body 内容是否为空
      if ($body.textContent.trim() !== "") {
        // 有内容,说明已登录(不可用),根据记录的地址跳转
        goUrl();
        return;
      }
      // 更新状态及 href 到 localStorage
      updateStatus("未登录(不可用)");
      // 跳转登录(不可用)页
      location.href = "/user-login.html";
    };

    _log("检查登录(不可用)状态");
    // 延时 3 秒检查
    setTimeout(checkLogin, 3000);
  })();

  /* globals LZString jsyaml*/

  (() => {
    // 定义按钮及提示信息
    const $btnBad = $(" <a class=\"btn btn-primary\">BAD</a>");
    const strTip = "<p>此贴内容或签名不符合论坛规范已作屏蔽处理,请查看置顶贴,以下为原始内容备份。</p>";

    // 绑定点击事件
    $btnBad.css({ color: "#fff" }).click(function() {
      let um = UM.getEditor("message");
      let str = um.getContent();
      if (str.indexOf("#~~") > -1) {
        return;
      }
      let strCode = LZString.compressToBase64(str);
      um.setContent(strTip + `<p>#~~${strCode}~~#</p>`);
      console.log(LZString.decompressFromBase64(strCode));
      // let strDeCode = LZString.decompressFromBase64(strCode);
      // um.setContent(strCode + strDeCode);
    });

    // 放置按钮
    if ($("input[name=update_reason]").length > 0) {
      $("#submit").after($btnBad);
    }

    // 解码
    $("div.message").each(function() {
      let $secP = $(this).find("p:nth-child(2)");
      if ($secP.length == 0) {
        console.log("skip");
        return;
      }
      let str = $secP.html();
      if (str.indexOf("#~~") == -1) {
        return;
      }
      console.log(str);
      str = str.replace(/#~~(.+)~~#/, function(a, b) {
        console.log(arguments);
        let strDeCode = LZString.decompressFromBase64(b);
        console.log(strDeCode);
        return strDeCode;
      });
      $secP.after(str).remove();
    });
  })();

  // _pid.js | 楼层地址
  (() => {
    $("li.media.post").each(function() {
      const $me = $(this);
      const pid = $me.data("pid");
      const $date = $me.find("span.date");
      $date.after(
        `<a class="text-grey ml-2" title="获取当前楼层链接" href="${curHref}#${pid}">「楼层地址」</a>`,
      );
    });
  })();

  // 引入元素插入
  (() => {
    if (typeof UM === "undefined") {
      return;
    }

    // 引用标签插入封装
    function fnBlockQuote() {
      const umObj = UM.getEditor("message");
      if (!umObj.isFocus()) {
        umObj.focus(true);
      }
      const addHTML = "<blockquote class=\"blockquote\"><p><br></p></blockquote><p><br></p>";
      // umObj.execCommand("insertHtml", addHTML);
      umObj.setContent(addHTML, true);
    }

    // 添加引用按钮
    $("head").append("<style>.edui-icon-blockquote:before{content:\"\\f10d\";}");
    (() => {
      const $btn = $.eduibutton({
        icon: "blockquote",
        click: function() {
          fnBlockQuote();
        },
        title: UM.getEditor("message").getLang("labelMap")["blockquote"] || "",
      });
      $(".edui-btn-name-insertcode").after($btn);
    })();

    // 自动排版函数封装
    function fnAutoFormat() {
      const umObj = UM.getEditor("message");
      let strHTML = umObj.getContent();
      strHTML = strHTML.replace(
        /<blockquote>/g,
        "<blockquote class=\"blockquote\">",
      );
      // 第二个参数为 true 表示追加;
      umObj.setContent(strHTML, false);
    }

    // 添加自动排版按钮
    $("head").append("<style>.edui-btn-auto-format:before{content:\"fix\";}");
    (() => {
      const $btn = $.eduibutton({
        icon: "auto-format",
        click: function() {
          fnAutoFormat();
        },
        title: "自动排版",
      });
      $(".edui-btn-name-insertcode").after($btn);
    })();
  })();

  /* global showdown */


  class GM_editor {
    $def;
    defEditor = null;
    htmlContent = "";
    $md;
    mdEditor = null;
    mdContent = "";
    defOption = {
      init($md) { },
      autoSync: false,
      curType: "html",
    };
    option = {};
    constructor(option) {
      this.option = Object.assign({}, this.defOption, option);
      this.init();
      this.option.init(this.$md);
      this.getContent("html").covert2("md").syncContent("md");
    }
    init() {
      const _this = this;
      this.$def = this.option.$defContainer || $(".edui-container");
      this.$md = this.createMdEditor();
      // 编辑器操作对象
      this.defEditor = this.option.defEditor || UM.getEditor("message");
      this.mdEditor = {
        // 内容变化时触发
        addListener(type, fn) {
          if (type === "contentChange") {
            // _log(_this.$md);
            _this.$md.find("#message_md").on("input", fn);
          }
        },
        // 获取内容
        getContent() {
          return _this.$md.find("#message_md").val();
        },
        // 写入内容
        setContent(content) {
          _this.$md.find("#message_md").text(content);
        },
      };
      if (this.option.autoSync) {
        this.defEditor.addListener("contentChange", () => {
          if (_this.option.curType === "md") {
            return;
          }
          this.getContent("html").covert2("md").syncContent("md");
        });
        this.mdEditor.addListener("contentChange", () => {
          if (_this.option.curType === "html") {
            return;
          }
          this.getContent("md").covert2("html").syncContent("html");
        });
      }
    }
    // 读取内容
    getContent(type = "html") {
      if (type === "html") {
        this.htmlContent = this.defEditor.getContent();
      } else if (type === "md") {
        this.mdContent = this.mdEditor.getContent();
      }
      return this;
    }
    // 封装转换函数
    covert2(to = "md") {
      const converter = new showdown.Converter();
      if (to === "md") {
        this.mdContent = converter.makeMarkdown(this.htmlContent);
      } else if (to === "html") {
        this.htmlContent = converter.makeHtml(this.mdContent);
      }
      return this;
    }
    // 封装同步函数
    syncContent(to = "md") {
      if (to === "md") {
        this.mdEditor.setContent(this.mdContent);
      } else if (to === "html") {
        this.defEditor.setContent(this.htmlContent, false);
      }
    }
    // 自动设置 #message_md 的高度
    autoSetHeight() {
      const $mdText = this.$md.find("#message_md");
      // 自动设置高度
      $mdText.height(0);
      $mdText.height($mdText[0].scrollHeight + 4);
      // 判断并绑定 input 事件
      if ($mdText.data("bindInput")) {
        return;
      }
      $mdText.data("bindInput", true);
      $mdText.on("input", () => {
        this.autoSetHeight();
      });
    }
    // 切换编辑器
    switchEditor() {
      this.$def.toggle();
      this.$md.toggle();
      // 根据结果设置 curType
      this.option.curType = this.$def.css("display") === "none" ? "md" : "html";
      // 切换后自动设置高度
      this.autoSetHeight();
    }
    // 创建 markdown 编辑器
    createMdEditor() {
      return $(`
      <div class="mdui-container" style="display: none;">
        <div class="mdui-body">
          <textarea id="message_md" name="message_md" placeholder="markdown" class="mdui-text"></textarea>
        </div>
      </div>
    `);
    }
  }

  GM_addStyle(`
  .is-pulled-right {
    float: right;
  }
  .mdui-container {
    border: 1px solid #d4d4d4;
    padding: 5px 10px;
  }
  .mdui-container:focus-within {
    border: 1px solid #4caf50;
  }
  .mdui-text {
    border: none;
    width: 100%;
    min-height: 300px;
    height: auto;
  }
  .mdui-text:focus,
  .mdui-text:focus-visible {
    outline: none;
    box-shadow: none;
  }
`);

  const mainForBBS = () => {
    const gm_editor = new GM_editor({
      init($md) {
        $(".edui-container").after($md);
      },
      autoSync: true,
    });

    const btnSwitchEditor = `
  <button class="btn btn-primary" type="button" id="btnSwitchEditor">切换编辑器</button>
  `;

    // 判断是否有 name 为 fid 的 select
    if ($("select[name='fid']").length === 0) {
      // quotepid 后追加一行 .form-group
      $("input[name='quotepid']").after("<div class=\"form-group\"><span></span></div>");
    }


    // name 为 quotepid 的 input 下一行追加切换按钮
    $("input[name='quotepid'] + .form-group").addClass("d-flex justify-content-between").append(btnSwitchEditor);

    // 切换编辑器
    $("#btnSwitchEditor").click(() => {
      gm_editor.switchEditor();
    });
  };

  const mainForAPP = () => {
    const gm_editor = new GM_editor({
      init($md) {
        $("#editor_content").after($md);
        // const $mdText = $md.find("#message_md");
        // $mdText.height($("#editor_content").height() - 10);
        $(".mdui-body").css({
          paddingTop: ".3em",
        });
      },
      $defContainer: $("#editor_content"),
      defEditor: UE.getEditor("editor_content"),
      autoSync: true,
    });

    // # cheader 元素内部追加切换按钮
    $("#cheader").append(`
  <span class="is-pulled-right">「<a href="javascript:;" class="btn btn-primary" id="btnSwitchEditor" title="切换编辑器">切换编辑器</a>」</span>
`);


    // 切换编辑器
    $("#btnSwitchEditor").click(() => {
      gm_editor.switchEditor();
    });
  };

  (() => {
    // 判断是否在应用中心编辑页
    if (curHref.indexOf("edit.php") > -1) {
      // _log(UE)
      const editor_api = window.editor_api || unsafeWindow.editor_api;
      editor_api.editor.content.obj.ready(mainForAPP);
    }
    // 判断是否在论坛发帖、回帖页
    if ($("textarea#message").length > 0 && $("li.newpost").length === 0) {
      mainForBBS();
    }
  })();

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址