Greasy Fork is available in English.
MCBBS模块化优化框架
- // ==UserScript==
- // @name MCBBS Extender Core
- // @namespace https://i.zapic.cc
- // @version v2.0.3
- // @description MCBBS模块化优化框架
- // @author Zapic
- // @match https://*.mcbbs.net/*
- // @run-at document-body
- // ==/UserScript==
- //Core
- const MExt_version = "2.0.3";
- const MExt_vercode = "121043";
- (() => {
- //夹带私货
- console.log(" %c Zapic's Homepage %c https://i.zapic.cc ", "color: #ffffff; background: #E91E63; padding:5px;", "background: #000; padding:5px; color:#ffffff");
- // jQuery检查
- if (typeof jQuery == "undefined") {
- console.error("This page does NOT contain JQuery,MCBBS Extender will not work.");
- return;
- }
- //在手机页面主动禁用
- if (document.getElementsByTagName('meta').viewport) {
- console.log("MCBBS Extender not fully compatible with Moblie page,exit manually");
- return;
- }
- const selfMd = {
- "meta": {
- "id": "MExt_Core",
- "name": "MCBBS Extender Core Loader",
- "version": "2.0.3",
- "updateInfo":[]
- }
- }
- // 初始化配置
- let valueList = null;
- const configList = [];
- const moduleList = {};
- // 加载ValueStorage
- try {
- valueList = JSON.parse(localStorage.getItem("MExt_config"));
- if (typeof valueList != "object" || valueList == null) {
- valueList = {};
- localStorage.setItem("MExt_config", "{}")
- }
- } catch (ig) {
- valueList = {};
- localStorage.setItem("MExt_config", "{}")
- }
- // 导出模块
- const exportModule = (...modules) => {
- for (let m of modules) {
- try {
- moduleLoader(m);
- dispatchEvent(new CustomEvent("MExtModuleLoaded",{"detail":m.meta}));
- } catch (e) {
- console.error("Error occurred while try to load a module:\n" + e);
- }
- }
- }
- const dlg = (m) => {
- console.debug("[MCBBS Extender]" + m);
- };
- const setValue = (name, val) => {
- valueList[name] = val;
- localStorage.setItem("MExt_config", JSON.stringify(valueList));
- }
- const getValue = (name) => {
- return valueList[name];
- }
- const deleteValue = (name) => {
- delete valueList[name];
- localStorage.setItem("MExt_config", JSON.stringify(valueList));
- }
- const appendStyle = (style) => {
- let s = document.createElement("style");
- s.className = "MExtStyle";
- s.innerHTML = style;
- document.head.appendChild(s);
- };
- const getRequest = (variable, url = "") => {
- let query = url ? /\?(.*)/.exec(url)[1] : window.location.search.substring(1);
- let vars = query.split("&");
- for (let i = 0; i < vars.length; i++) {
- let pair = vars[i].split("=");
- if (pair[0] == variable) {
- return pair[1];
- }
- }
- return (false);
- }
- // 模块加载器
- const moduleLoader = (module) => {
- // 载入配置项
- if (typeof module.meta == "undefined" || typeof module.meta.id !== "string") {
- throw new Error("Invalid module meta");
- }
- moduleList[module.meta.id] = module.meta;
- if (typeof module.config !== "undefined") {
- module.config.forEach((v) => {
- if (typeof getValue(v.id) == "undefined") {
- setValue(v.id, v.default);
- }
- let config = v;
- v.value = getValue(v.id);
- configList.push(config);
- });
- }
- // 判断是否应该运行
- if (typeof module.case == "function") {
- if (!module.case()) {
- return;
- }
- }
- // 加载模块CSS
- if (typeof module.style == 'string') {
- appendStyle(module.style);
- }
- // 运行模块Core
- if (typeof module.core == "function") {
- module.core();
- }
- }
- // 对外暴露API
- const MExt = {
- "exportModule": exportModule,
- "jQuery": unsafeWindow.jQuery,
- "configList": configList,
- "moduleList": moduleList,
- "versionName": MExt_version,
- "versionCode": MExt_vercode,
- "Storage": {
- "get": getValue,
- "set": setValue,
- "delete": deleteValue
- },
- "Units": {
- "appendStyle": appendStyle,
- "getRequest": getRequest,
- "debugLog": dlg
- }
- };
- unsafeWindow.MExt = MExt;
- unsafeWindow.dispatchEvent(new CustomEvent("MExtLoaded",{bubbles: true}));
- exportModule(selfMd);
- })();
- // Discuz UI Operate Event Dispatcher
- (async ()=>{
- await new Promise(_ => { !unsafeWindow.MExt ? unsafeWindow.addEventListener("MExtLoaded", __ => { _(unsafeWindow.MExt) }) : _(unsafeWindow.MExt)});
- const removeHandler = (r) => {
- switch (r.target.nodeName) {
- case "TBODY":
- if (typeof r.target.id != "undefined") {
- if (r.target.id.lastIndexOf("normalthread_") >= 0) {
- r.target.dispatchEvent(new CustomEvent("ThreadPreviewClosed",{bubbles: true}));
- }
- }
- break;
- case "DIV":
- if (typeof r.target.id != 'undefined' && r.target.id.lastIndexOf("threadPreview_") >= 0) {
- if (r.removedNodes[0].nodeName == "SPAN" && r.removedNodes[0].innerText == " 请稍候...") {
- r.target.dispatchEvent(new CustomEvent("ThreadPreviewOpened",{bubbles: true}));
- }
- } else if (r.removedNodes.length >= 3 && r.target.id.lastIndexOf("post_") >= 0) {
- if (r.removedNodes[0].nodeName == "A" && r.removedNodes[0].name == "newpost" && r.removedNodes[0].parentNode != null) {
- r.target.dispatchEvent(new CustomEvent("ThreadFlushStarted",{bubbles: true}));
- }
- } else if (r.target.id == "append_parent") {
- if (r.removedNodes[0].nodeName == "DIV") {
- if (r.removedNodes[0].id == "fwin_rate") {
- r.target.dispatchEvent(new CustomEvent("RateWindowClosed",{bubbles: true}));
- } else if (r.removedNodes[0].id == "fwin_reply") {
- r.target.dispatchEvent(new CustomEvent("ReplyWindowClosed",{bubbles: true}));
- } else if (typeof r.removedNodes[0].id != 'undefined' && r.removedNodes[0].id.lastIndexOf("fwin_miscreport") >= 0) {
- r.target.dispatchEvent(new CustomEvent("ReportWindowClosed",{bubbles: true}));
- }
- }
- }
- break;
- }
- }
- const addHandler = (r) => {
- switch (r.target.nodeName) {
- case "DIV":
- if (typeof r.target.id != "undefined") {
- if (r.target.id.lastIndexOf("threadPreview_") >= 0) {
- if (r.addedNodes[0].nodeName == "SPAN" && r.addedNodes[0].innerText == " 请稍候...") {
- r.target.dispatchEvent(new CustomEvent("ThreadPreviewPreOpen",{bubbles: true}));
- }
- } else if (r.addedNodes.length >= 3 && r.target.id.lastIndexOf("post_") >= 0) {
- if (r.addedNodes[0].nodeName == "A" && r.addedNodes[0].name == "newpost" && r.addedNodes[0].parentNode != null) {
- r.target.dispatchEvent(new CustomEvent("ThreadFlushFinished",{bubbles: true}));
- }
- } else if (r.target.id == "append_parent") {
- if (r.addedNodes[0].nodeName == "DIV") {
- if (r.addedNodes[0].id == "fwin_rate") {
- r.addedNodes[0].dispatchEvent(new CustomEvent("RateWindowPreOpen",{bubbles: true}));
- } else if (r.addedNodes[0].id == "fwin_reply") {
- r.addedNodes[0].dispatchEvent(new CustomEvent("ReplyWindowPreOpen",{bubbles: true}));
- } else if (typeof r.addedNodes[0].id != 'undefined' && r.addedNodes[0].id.lastIndexOf("fwin_miscreport") >= 0) {
- r.addedNodes[0].dispatchEvent(new CustomEvent("ReportWindowPreOpen",{bubbles: true}));
- }
- }
- } else if (r.target.id === "") {
- if (r.target.parentElement != null && r.target.parentElement == "postlistreply") {
- r.target.dispatchEvent(new CustomEvent("NewReplyAppended",{bubbles: true}));
- }
- }
- }
- break;
- case "A":
- if (r.addedNodes[0].nodeName == "#text" && typeof tid == "undefined") {
- if (r.addedNodes[0].nodeValue == "正在加载, 请稍后...") {
- r.target.dispatchEvent(new CustomEvent("ThreadsListLoadStart",{bubbles: true}));
- } else if (r.addedNodes[0].nodeValue == "下一页 »") {
- r.target.dispatchEvent(new CustomEvent("ThreadsListLoadFinished",{bubbles: true}));
- }
- }
- break;
- case "TD":
- if (r.target.id == "fwin_content_rate" && r.addedNodes[0].nodeName == "DIV" && r.addedNodes[0].id == "floatlayout_topicadmin") {
- r.target.dispatchEvent(new CustomEvent("RateWindowOpened",{bubbles: true}));
- }
- if (r.target.id == "fwin_content_reply" && r.addedNodes[0].nodeName == "H3" && r.addedNodes[0].id == "fctrl_reply") {
- r.target.dispatchEvent(new CustomEvent("ReplyWindowOpened",{bubbles: true}));
- }
- if (typeof r.target.id != 'undefined' && r.target.id.lastIndexOf("fwin_content_miscreport") >= 0 && r.addedNodes[0].nodeName == "H3" && r.addedNodes[0].id.lastIndexOf("fctrl_miscreport") >= 0) {
- r.target.dispatchEvent(new CustomEvent("ReportWindowOpened",{bubbles: true}));
- }
- break;
- }
- }
- const mainHandler = (r) => {
- if (r.type == "childList") {
- if (r.addedNodes.length > 0) {
- addHandler(r);
- }
- if (r.removedNodes.length > 0) {
- removeHandler(r);
- }
- }
- }
- let O = new MutationObserver((e) => {
- for (let record of e) {
- mainHandler(record);
- }
- });
- document.addEventListener("DOMContentLoaded",()=>{
- O.observe(document.body, { childList: true, subtree: true });
- });
- // 钩住DiscuzAjax函数,使其触发全局事件
- const __ajaxpost = unsafeWindow.ajaxpost;
- unsafeWindow.ajaxpost = (formid, showid, waitid, showidclass, submitbtn, recall) => {
- let relfunc = () => {
- if (typeof recall == 'function') {
- recall();
- } else {
- eval(recall);
- }
- this.dispatchEvent(new CustomEvent("DiscuzAjaxPostFinished",{bubbles: true}));
- }
- __ajaxpost(formid, showid, waitid, showidclass, submitbtn, relfunc);
- }
- const __ajaxget = unsafeWindow.ajaxget;
- unsafeWindow.ajaxget = (url, showid, waitid, loading, display, recall) => {
- let relfunc = () => {
- if (typeof recall == 'function') {
- recall();
- } else {
- eval(recall);
- }
- this.dispatchEvent(new CustomEvent("DiscuzAjaxGetFinished",{bubbles: true}));
- }
- __ajaxget(url, showid, waitid, loading, display, relfunc);
- }
- })();
- // Config Panel
- (async () => {
- const MExt = await new Promise(_ => { !unsafeWindow.MExt ? unsafeWindow.addEventListener("MExtLoaded", __ => { _(unsafeWindow.MExt) }) : _(unsafeWindow.MExt)});
- const $ = MExt.jQuery;
- const Md = {
- "meta": {
- "id": "MExt_Config",
- "name": "MCBBS Extender 设置",
- "version": "2.0.3",
- "updateInfo": []
- },
- "style": `.conf_contain {
- max-height: 45vh;
- overflow-y: auto;
- padding-right: 5px;
- overflow-x: hidden;
- scrollbar-color: rgba(0, 0, 0, 0.17) #f7f7f7;
- scrollbar-width: thin;
- }
- .alert_info ::-webkit-scrollbar {
- background: #f7f7f7;
- height: 7px;
- width: 7px
- }
- .alert_info ::-webkit-scrollbar-thumb:hover {
- background: rgba(0, 0, 0, 0.35);
- }
- .alert_info ::-webkit-scrollbar-thumb {
- background: rgba(0, 0, 0, 0.17);
- }
- .conf_item {
- line-height: 1.2;
- margin-bottom: 5px;
- }
- .conf_title {
- font-weight: 1000;
- }
- .conf_subtitle {
- font-size: 10px;
- color: rgba(0, 0, 0, 0.5);
- padding-right: 40px;
- display: block;
- }
- .conf_check {
- float: right;
- margin-top: -25px;
- }
- .conf_input {
- float: right;
- width: 30px;
- margin-top: -27px;
- }
- .conf_longinput {
- width: 100%;
- margin-top: 5px;
- }
- .conf_textarea {
- width: calc(100% - 4px);
- margin-top: 5px;
- resize: vertical;
- min-height: 50px;
- }`
- };
- MExt.exportModule(Md);
- const getRequest = MExt.Units.getRequest;
- $(() => {
- // 发送警告
- if (location.pathname == "/forum.php" && getRequest('mod') == "post" && getRequest('action') == "newthread" && getRequest('fid') == "246") {
- const alertWin = document.createElement("div");
- alertWin.style = "max-width:430px;position: fixed; left: 20px; top: 80px; z-index: 9999; transform: matrix3d(1, 0, 0, 0.0001, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1.025) translateX(-120%); background: rgba(228, 0, 0, 0.81); color: white; padding: 15px; transition-duration: 0.3s; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.66) 2px 2px 5px 0px;";
- alertWin.innerHTML = `<h1 style="font-size: 3em;float: left;margin-right: 12px;font-weight: 500;margin-top: 6px;">警告</h1><span style="font-size: 1.7em;">您正在向反馈与投诉版发表新的帖子</span><br>如果您正在向论坛报告论坛内的Bug,请先关闭此脚本再进行一次复现,以确保Bug不是由MCBBS Extender造成的.`;
- document.body.appendChild(alertWin);
- setTimeout(() => { alertWin.style.transform = "matrix3d(1, 0, 0, 0.0001, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1.025)"; }, 10);
- setTimeout(() => { alertWin.style.transform = "none"; }, 300);
- setTimeout(() => { alertWin.style.transform = "translateX(-120%)"; }, 10000);
- }
- // 设置界面初始化
- const btnContainer = document.createElement("li");
- const btnMExt = document.createElement("a");
- btnMExt.href = "javascript: void(0);";
- btnMExt.id = "MExt_config";
- btnMExt.innerHTML = "MCBBS Extender 设置";
- btnContainer.appendChild(btnMExt);
- const target = document.querySelector("#user_info_menu .user_info_menu_btn");
- if(target == null) return;
- target.appendChild(btnContainer);
- btnMExt.addEventListener("click", () => {
- let confwinContent = '<style>body{overflow:hidden}.altw{width:700px;max-width:95vh;}.alert_info {background-image: unset;padding-left: 20px;padding-right: 17px;}</style><div class="conf_contain">';
- const inputType = {
- "check": '',
- "num": '',
- "text": '',
- "textarea": ''
- };
- MExt.configList.forEach((v) => {
- switch (v.type) {
- case "check":
- inputType.check += '<p class="conf_item"><span class="conf_title">' + v.name + '</span><br><span class="conf_subtitle">' + v.desc + '</span><input class="conf_check" type="checkbox" id="in_' + v.id + '"></input></p>';
- break;
- case "num":
- inputType.num += '<p class="conf_item"><span class="conf_title">' + v.name + '</span><br><span class="conf_subtitle">' + v.desc + '</span><input type="number" class="conf_input" id="in_' + v.id + '"></input></p>';
- break;
- case "text":
- inputType.text += '<p class="conf_item"><span class="conf_title">' + v.name + '</span><br><span class="conf_subtitle">' + v.desc + '</span><input type="text" class="conf_longinput" id="in_' + v.id + '"></input></p>';
- break;
- case "textarea":
- inputType.textarea += '<p class="conf_item"><span class="conf_title">' + v.name + '</span><br><span class="conf_subtitle">' + v.desc + '</span><textarea class="conf_textarea" id="in_' + v.id + '"></textarea></p>';
- break;
- default:
- inputType.check += '<p class="conf_item"><span class="conf_title">' + v.name + '</span><br><span class="conf_subtitle">' + v.desc + '</span><input class="conf_check" type="checkbox" id="in_' + v.id + '"></input></p>';
- break;
- }
- });
- confwinContent += inputType.check + inputType.num + inputType.text + inputType.textarea + '</div>';
- unsafeWindow.showDialog(
- confwinContent,
- "confirm",
- "MCBBS Extender 设置",
- () => {
- MExt.configList.forEach((v) => {
- let val = '';
- if (v.type == "num" || v.type == "text" || v.type == "textarea") {
- val = $("#in_" + v.id).val();
- } else {
- val = $("#in_" + v.id).prop("checked");
- }
- MExt.ValueStorage.set(v.id, val);
- });
- setTimeout(() => {
- unsafeWindow.showDialog("设置已保存,刷新生效<style>.alert_info{background:url(https://www.mcbbs.net/template/mcbbs/image/right.gif) no-repeat 8px 8px}</style>", "confirm", "", () => { location.reload() }, true, () => { }, "", "刷新", "确定");
- });
- },
- true,
- () => { },
- "MCBBS Extender " + MExt.versionName + " - <s>世界第二委屈公主殿下</s>"
- );
- MExt.configList.forEach((v) => {
- if (v.type == "num" || v.type == "text" || v.type == "textarea") {
- $("#in_" + v.id).val(MExt.ValueStorage.get(v.id));
- } else {
- $("#in_" + v.id).prop("checked", MExt.ValueStorage.get(v.id));
- }
- });
- });
- });
- })();
- // Update Manager
- (async () => {
- const MExt = await new Promise(_ => { !unsafeWindow.MExt ? unsafeWindow.addEventListener("MExtLoaded", __ => { _(unsafeWindow.MExt) }) : _(unsafeWindow.MExt)});
- MExt.exportModule({
- "meta": {
- "id": "MExt_updateManager",
- "name": "MCBBS Extender Update Manager",
- "version": "2.0.3",
- "updateInfo": []
- }
- });
- if (localStorage.getItem("MExt_UpdateMgr") == null) {
- localStorage.setItem("MExt_UpdateMgr", JSON.stringify(MExt.moduleList));
- unsafeWindow.showDialog("<b>欢迎使用MCBBS Extender</b>.<br>脚本本身不包含任何功能,请到<a style=\"color: #E91E63\" href=\"https://github.com/Proj-MExt/Modules-Repo\">模块仓库</a>寻找模块.<br>设置按钮已经放进入了您的个人信息菜单里,如需调整设置请在个人信息菜单里查看.", "right", "欢迎", () => {
- unsafeWindow.showMenu('user_info');
- unsafeWindow.MExt.jQuery("#MExt_config").css("background-color", "#E91E63").css("color", "#fff");
- setTimeout(() => {
- unsafeWindow.hideMenu('user_info_menu');
- unsafeWindow.MExt.jQuery("#MExt_config").css("background-color", "").css("color", "");
- }, 3000);
- });
- return;
- }
- let updateContent = '';
- let source = null;
- try {
- source = JSON.parse(localStorage.getItem("MExt_UpdateMgr"));
- } catch(e){
- localStorage.setItem("MExt_UpdateMgr", JSON.stringify(MExt.moduleList));
- return;
- }finally {
- localStorage.setItem("MExt_UpdateMgr", JSON.stringify(MExt.moduleList));
- }
- const compareVer = (b,a) => {
- return [b,a][0] != [b,a].sort()[0];
- }
- for (let m in MExt.moduleList ){
- if(typeof source[m] != "undefined" && typeof MExt.moduleList[m].version != "undefined"){
- if(compareVer(MExt.moduleList[m].version,source[m].version)){
- if(typeof MExt.moduleList[m].updateInfo !="undefined" && MExt.moduleList[m].updateInfo.length > 0){
- updateContent += "<b>" + (typeof MExt.moduleList[m].name == "undefinded" ? MExt.moduleList[m].id : MExt.moduleList[m].name) + "</b> " + source[m].version + " > " + MExt.moduleList[m].version + "<br>";
- for(let info of MExt.moduleList[m].updateInfo){
- updateContent += info + "<br>"
- }
- }
- }
- }
- }
- if(updateContent == "") return;
- unsafeWindow.showDialog("<b>模块已更新</b>" + updateContent, "right");
- })();