// ==UserScript== // @name 老男人助手 // @namespace http://tampermonkey.net/ // @version 0.8.12 // @description 适用于老男人游戏论坛:https://bbs.oldmantvg.net/ 的小工具 // @author rock128 // @match https://bbs.oldmanemu.net/* // @match https://bbs.oldmantvg.net/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant GM_addStyle // @grant GM_getResourceText // @require https://cdn.bootcdn.net/ajax/libs/jscolor/2.4.7/jscolor.min.js // @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/jszip/3.7.1/jszip.min.js // @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/FileSaver.js/2.0.5/FileSaver.min.js // @resource css https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.css // @license GPL-3.0 License // @downloadURL none // ==/UserScript== (function () { 'use strict'; GM_addStyle(GM_getResourceText("css")); // 旋转图片 // 自动组合云盘链接 // 与佛论禅编解码 var VERSION = 1; //所有的功能都在这里定义 var settingObject = { autoSignIn: { // 和所属对象属性名保持一致 id: "autoSignIn", // 显示在界面上的标题 title: "自动签到", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false, signTitle: "签到" }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: { // 这行含义是: config.signTitle 和页面上id为sign-key的标签值是对应着的 signTitle: { element: "#sign-key" } }, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("index") || Utils.isIndexPage() }, // 功能生效的逻辑代码 doAction: function () { if ($("#sign_title").text() == this.config.signTitle) { $.xpost(xn.url("my-sign"), "", function (message) { $("#sign_title").text("已签") }); } }, // 功能配置的html代码 contentHtml: function () { let html = ` 保持和签到按钮上的文字完全一致
` return Utils.createDivWithTitle("签到按钮文本", html) } }, deng: { // 和所属对象属性名保持一致 id: "deng", // 显示在界面上的标题 title: "去掉节日灯笼", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return true }, // 功能生效的逻辑代码 doAction: function () { $(".deng") && $(".deng").remove() }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

从所有页面移除节日灯笼

") } }, blacklist: { // 和所属对象属性名保持一致 id: "blacklist", // 显示在界面上的标题 title: "黑名单", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false, blacklist: {} }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: { blacklist: { element: "#black-list", getVal: function () { let ret = {} if ($(this.element) && $(this.element).val().trim() != "") { let info = $(this.element).val().split("\n"); for (let line of info) { let tmpArray = line.split("=") ret[tmpArray[0]] = { banType: tmpArray[1], username: tmpArray[2] } } } return ret } } }, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("") || Utils.isMatchPageCategory("index") }, // 功能生效的逻辑代码 doAction: function () { let configObject = this.config.blacklist let isBan = function (userId, type) { var blackUserObject = configObject[userId]; if (!blackUserObject) { return false; } return blackUserObject.banType == type } let hideBlackListUserContent = function () { $(".media.thread.tap") && $(".media.thread.tap").each(function (i, item) { var id = $(item).children().eq(0).attr("href") id = id.replace("user-", "").replace(".htm", "") if (isBan(id, Utils.BLACK_TYPE_ALL) || isBan(id, Utils.BLACK_TYPE_THREAD)) { $(item).hide() } }) $(".media.post") && $(".media.post").each(function (i, item) { var id = $(item).children().eq(0).attr("href") id = id.replace("user-", "").replace(".htm", "") if (isBan(id, Utils.BLACK_TYPE_ALL) || isBan(id, Utils.BLACK_TYPE_REPLY)) { $(item).hide() } }) } hideBlackListUserContent() $(".avatar-3").mouseenter(function () { var href = $(this).parent().attr("href") var userId = href.split("-")[1].split(".")[0] var name = "" try { // 帖子列表页的路径 var text = $(this).parent().next().find(".icon-user-o").parent().text() name = text.split("于")[0].trim() if (name == "") { // 帖子详情页的路径 text = $(this).parent().next().find(".username").first().text() name = text.trim() } } catch (e) { console.log(e.message); } let clickCallBack = function () { var id = $(this).attr("user-id") var username = $(this).attr("user-name") var banType = $(this).attr("banType") configObject[id] = { banType: banType, username: username } hideBlackListUserContent() $("#user-operate-menu").hide(); Utils.saveConfig(settingObject.blacklist.id) } let operateMenu = $("#user-operate-menu") operateMenu.empty() let buttons = [ Utils.createBlackTypeButton(Utils.BLACK_TYPE_THREAD, userId, name, "屏蔽用户帖子", true), Utils.createBlackTypeButton(Utils.BLACK_TYPE_REPLY, userId, name, "屏蔽用户在帖子中的回复", true), Utils.createBlackTypeButton(Utils.BLACK_TYPE_ALL, userId, name, "全部屏蔽", true) ] for (let button of buttons) { $(button).click(clickCallBack); operateMenu.append(button) } operateMenu.css("position", "absolute"); operateMenu.css("top", $(this).offset().top); operateMenu.css("left", $(this).offset().left - 70); operateMenu.show(); operateMenu.unbind() operateMenu.mouseleave(function () { operateMenu.hide(); }) }) $('.avatar-3').mouseleave(function () { var href = $(this).parent().attr("href") var offset = $("#user-operate-menu").offset() var width = $("#user-operate-menu").outerWidth(true) + 10 var height = $("#user-operate-menu").outerHeight(true) + 10 if (window.__xx && window.__yy) { if (window.__xx > Math.ceil(offset.left + width) || window.__yy > Math.ceil(offset.top + height) || window.__xx < Math.ceil(offset.left) || window.__yy < Math.ceil(offset.height)) { $("#user-operate-menu").hide(); } } //console.log("mouse:"+window.__xx+","+window.__yy+" div:"+offset.left+","+offset.top+","+width+","+height+" "+Math.ceil(offset.left + width)+","+Math.ceil(offset.top + height)) }) }, // 功能配置的html代码 contentHtml: function () { let genBlackList = function () { var blackUserInfo = [] for (let key in this.config.blacklist) { let blackUserObject = this.config.blacklist[key] blackUserInfo.push(key + "=" + blackUserObject.banType + "=" + blackUserObject.username) } return blackUserInfo.join("\n") } let html = ` ` return Utils.createDivWithTitle("名单", html) } }, autoReply: { // 和所属对象属性名保持一致 id: "autoReply", // 显示在界面上的标题 title: "自动回复隐藏帖子", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false, msgTemplates: [] }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: { msgTemplates: { element: "#auto-reply", getVal: function () { let val = $(this.element).val().trim() return val == "" ? [] : val.split("\n") } } }, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread") }, // 功能生效的逻辑代码 doAction: function () { let issueTime = $(".username").eq(1).next().text().trim() let isRecently = /^[0-9]+(秒|分钟|小时|天)前$/.test(issueTime) if (isRecently && issueTime.indexOf("天前") != -1) { let day = issueTime.replace("天前", "") isRecently = parseInt(day) < 7 } if (isRecently && $(".alert-warning") && $(".alert-warning").text().indexOf("此处隐藏内容请回复后再查看") != -1) { var msg = this.config.msgTemplates[Math.floor(Math.random() * this.config.msgTemplates.length)]; $(".message .form-control").val(msg) $("#quick_reply_form").submit() } }, // 功能配置的html代码 contentHtml: function () { let html = `
${Utils.createDivWithTitle("自动回复消息模板", '为防止过于久远的帖子被自动回复顶贴,只有发布时间是7天以内的帖子才会自动回复
')}
` return html } }, keyboardNavigation: { // 和所属对象属性名保持一致 id: "keyboardNavigation", // 显示在界面上的标题 title: "键盘翻页", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return true }, // 功能生效的逻辑代码 doAction: function () { $(document).keydown(function (event) { if (event.keyCode == 37) { let preA = $(".page-link:contains('◀')") if (preA && preA.length > 0) { window.location.href = preA.attr("href") } } else if (event.keyCode == 39) { let nextA = $(".page-link:contains('▶')") if (nextA && nextA.length > 0) { window.location.href = nextA.attr("href") } } }); }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

用键盘左右箭头翻页

") } }, readMode: { // 和所属对象属性名保持一致 id: "readMode", // 显示在界面上的标题 title: "阅读模式", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false, backgroundColor: '#C7EDCC', fontColor: '#000000' }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: { backgroundColor: { element: "#background-color-input" }, fontColor: { element: "#font-color-input" } }, // 功能生效的前提条件检查 matchCondition: function () { return true }, // 功能生效的逻辑代码 doAction: function () { let config = this.config $(document).find("*").each(function (i, item) { if ($(item).attr("id") == "setting-panel") { return } if ($(item).parents('#setting-panel').length > 0) { return } let oldCss = item.style.cssText || "" $(item).css("cssText", oldCss + "background-color:" + config.backgroundColor + "!important;color:" + config.fontColor + "!important;background-image:none!important;") }) }, // 功能配置的html代码 contentHtml: function () { let html = `
${Utils.createDivWithTitle("背景色", ' ')} ${Utils.createDivWithTitle("文字颜色", '')}
` return html } }, doubleClickScrollTop: { // 和所属对象属性名保持一致 id: "doubleClickScrollTop", // 显示在界面上的标题 title: "双击滚动到顶部", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return true }, // 功能生效的逻辑代码 doAction: function () { $(document).dblclick("click", function () { $('html,body').animate({ scrollTop: '0px' }, 300) }); }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

在任意界面双击返回顶部

") } }, highlightAuthor: { // 和所属对象属性名保持一致 id: "highlightAuthor", // 显示在界面上的标题 title: "高亮楼主", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false, backgroundColor: '#96a48b', quoteColor: '#b7b1a5' }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: { backgroundColor: { element: "#author-background-color-input" }, quoteColor: { element: "#author-quote-background-color-input" } }, // 功能生效的前提条件检查 matchCondition: function () { return true }, // 功能生效的逻辑代码 doAction: function () { let config = this.config $(".badge.badge-secondary.small.haya-post-info-first-floor").each(function () { var block = $(this).parent().parent().parent().parent().get(0) let oldCss = block.style.cssText || "" $(block).css("cssText", oldCss + "background-color:" + config.backgroundColor + "!important;") var quote = $(block).find(".blockquote").get(0) if (quote) { oldCss = quote.style.cssText || "" $(quote).css("cssText", oldCss + "background-color:" + config.quoteColor + "!important;") } }) }, // 功能配置的html代码 contentHtml: function () { let html = `
${Utils.createDivWithTitle("背景色", ' ')} ${Utils.createDivWithTitle("引用背景颜色", '')}
` return html } }, emojisSupport: { // 和所属对象属性名保持一致 id: "emojisSupport", // 显示在界面上的标题 title: "表情", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread") && !Utils.hasElement(".emojis-panel") }, // 功能生效的逻辑代码 doAction: function () { var emojisPanel = $('
') $("body").append(emojisPanel) var emojRange = [ [128513, 128591], [127744, 128511], [128640, 128704], [9986, 10160] ]; for (var i = 0; i < emojRange.length; i++) { var range = emojRange[i]; for (var x = range[0]; x < range[1]; x++) { var d = $('
&#' + x + ';
'); d.addClass("emoji-item") d.attr("code", "0x" + x.toString(16)) d.click(function () { var code = $(this).attr("code") var msg = $("#message").val() var start = $("#message")[0].selectionStart var end = $("#message")[0].selectionEnd $("#message").val(msg.substring(0, start) + String.fromCodePoint(code) + msg.substring(end, msg.length)) }) emojisPanel.append(d) } } var button = $('
😄
') $(button).click(function () { if ($(".emojis-panel").css("display") === 'none') { $(".emojis-panel").css("display", "flex"); $(".emojis-panel").css("position", "absolute"); $(".emojis-panel").css("top", $('#message').offset().top - 310); $(".emojis-panel").css("left", $('#message').offset().left); $(".emojis-panel").show(200); } else { $(".emojis-panel").hide(200) } }) $("#message").focus(function () { if ($(".emojis-panel").css("display") != 'none') { $(".emojis-panel").hide(200) } }) $("#submit").parent().css("display", "flex") $("#submit").parent().append(button) }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

表情支持

") } }, downloadThreadImages: { // 和所属对象属性名保持一致 id: "downloadThreadImages", // 显示在界面上的标题 title: "一键下载主题图片", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false, grade: 0 }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: { grade: { element: "#grade-input" } }, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread") && !Utils.hasElement(".package-download-image") }, // 功能生效的逻辑代码 doAction: function () { var grade = this.config.grade var title = "打包下载图片" var button = $('') button.click(function () { $(this).attr("disabled", true); if (grade != 0) { var jform = $("#reward_form"); if (jform && jform.attr('action')) { $.xpost(jform.attr('action'), "credits1=" + grade); } } var array = []; $('.message.break-all').find('img').each(function () { let src = $(this).attr("src") if (src.indexOf("upload/avatar") == -1 && src.indexOf("view/img/avatar.png") == -1) { array.push(src) } }) if (array.length == 0) { alert("没有图片可供下载!") $(this).attr("disabled", false); return } var progress = 0; function getBase64Image(images, callback) { var img = new Image(); img.setAttribute("crossOrigin", 'anonymous') img.onload = function () { var canvas = document.createElement("canvas"); canvas.width = img.width canvas.height = img.height canvas.getContext("2d").drawImage(img, 0, 0); var dataURL = canvas.toDataURL(); callback ? callback(dataURL) : null; } img.src = images; } var zip = new JSZip() var file_name = '图片.zip'; $(".package-download-image").text("打包进度:(" + progress + "/" + array.length + ")") array.forEach(item => { getBase64Image(item, function (dataURL) { var img_arr = dataURL.split(','); let name = progress + "-" + item.substring(item.lastIndexOf("/") + 1) zip.file(name, img_arr[1], { base64: true }); progress++; $(".package-download-image").text("打包进度:(" + progress + "/" + array.length + ")") if (Object.keys(zip.files).length == array.length) { $(".package-download-image").text("打包完成准备下载") zip.generateAsync({ type: "blob" }).then(function (content) { $(".package-download-image").attr("disabled", false); $(".package-download-image").text(title) saveAs(content, file_name); }); } }) }) }) $(".plugin.d-flex.justify-content-center.mt-3").append(button) }, // 功能配置的html代码 contentHtml: function () { return Utils.createDivWithTitle("自动打分", ` 下载的同时自动给楼主帖子打 分(填0或者不填表示不打分)
`) } }, openPageToNewTab: { // 和所属对象属性名保持一致 id: "openPageToNewTab", // 显示在界面上的标题 title: "新标签页打开帖子", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return true }, // 功能生效的逻辑代码 doAction: function () { $('.subject.break-all a').each(function () { $(this).attr("target", "_blank") }) }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

打开帖子时新开一个标签页

") } }, onlyViewAuthor: { // 和所属对象属性名保持一致 id: "onlyViewAuthor", // 显示在界面上的标题 title: "只看楼主", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread") && !Utils.hasElement(".only-author-btn") }, // 功能生效的逻辑代码 doAction: function () { let open = "只看楼主" let close = "全部显示" var button = $('') button.click(function () { if ($(this).text() == open) { $(".avatar-3").each(function () { let authorId = $("div.media").eq(0).find("a").eq(0).attr("href").replace("user-", "").replace(".htm", "") let uid = $(this).parent().attr("href").replace("user-", "").replace(".htm", "") if (authorId != uid) { $(this).parent().parent().addClass("only-author-btn-mark") } }) $(".only-author-btn-mark").hide(100) button.text(close) } else { $(".only-author-btn-mark").show(100) $(".only-author-btn-mark").removeClass("only-author-btn-mark") button.text(open) } }) $(".plugin.d-flex.justify-content-center.mt-3").append(button) }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

打开后帖子里会添加一个“只看楼主”按钮,点击后,当前页面只显示楼主的回复

") } }, replyAdditionNumber: { // 和所属对象属性名保持一致 id: "replyAdditionNumber", // 显示在界面上的标题 title: "回复楼层号", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread") && !Utils.hasElement(".bindReplyAdditionNumberEventMark") }, // 功能生效的逻辑代码 doAction: function () { if (Utils.hasElement(".icon-reply")) { $(".icon-reply").eq(0).addClass("bindReplyAdditionNumberEventMark") $(".icon-reply").click(function () { let number = $(this).parent().next().text().trim() number = "回复" + number + ": " var msg = $("#message").val() if (!msg.startsWith(number)) { $("#message").val(number + msg) } }) } }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

当你回复某一楼层时,回复中会自动附带楼层号码

") } }, baiduYunLink: { // 和所属对象属性名保持一致 id: "baiduYunLink", // 显示在界面上的标题 title: "百度云链接增强", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread") && !Utils.hasElement(".parseBaiduYunLinkMark") }, // 功能生效的逻辑代码 doAction: function () { var linkpattern = /https?:\/\/pan\.baidu\.com\/s\/[a-zA-Z0-9?=_-]*/ $(":contains(https://pan.baidu.com/s/)").filter(function () { return $(this).children().length === 0 }).each(function () { let parent = $(this).parent() let text = parent.text() let url = linkpattern.exec(text)[0] let code = "" let finalUrl = url if (url.indexOf("pwd=") == -1) { var codepattern = new RegExp(url + ".*(提取码.*)?([a-zA-Z0-9]{4})"); let match = codepattern.exec(text) if (!match || match.length == 0) { // 没有找到本链接对应的提取码,略过处理下一个 return } code = match[0] code = code.replace(url, "") code = /[a-zA-Z0-9]{4}/.exec(code)[0] code = code.trim() finalUrl = finalUrl + "?pwd=" + code } else { code = /pwd=[a-zA-Z0-9]{4}/.exec(url)[0].replace("pwd=", "") } let html = parent.html() html = html.replace(new RegExp("链接[::]?"), "") html = html.replace(new RegExp("提取码[::]?"), "") html = html.replace(url, "") html = html.replace(code, "") html = html.replace(new RegExp("--来自百度网盘超级会员V\\d的分享"), "") parent.html(html) $('.message.break-all').eq(0).append(Utils.createDivWithTitle("老男人助手解析百度云链接结果", '' + finalUrl + ' 链接已经复制到剪贴板', true, "width:100%;border: 2px solid orange;border-style: dashed;", "background-color:#f8f9fa !important;")) $(".alert.alert-success").each(function () { if ($(this).text().trim() == "" && !Utils.hasElement("img", this)) { $(this).hide() } }) $(".copy-link").click(function () { let url = $(this).attr("url") navigator.clipboard.writeText(url) let tip = $(this).next() tip.show(); setTimeout(function () { tip.hide(); }, 500) }) }) $("body").addClass("parseBaiduYunLinkMark") }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

把百度云链接和提取码组合成一个可直接访问的链接,免去手动输入提取码

") } }, insertHiddenContent: { // 和所属对象属性名保持一致 id: "insertHiddenContent", // 显示在界面上的标题 title: "插入隐藏内容优化", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread-create") && !Utils.hasElement(".insertHiddenContentMark") }, // 功能生效的逻辑代码 doAction: function () { let loginDisplay = $(".btn.btn-primary").eq(0) let replyDisplay = $(".btn.btn-primary").eq(1) loginDisplay.attr("onclick", "") replyDisplay.attr("onclick", "") loginDisplay.unbind() replyDisplay.unbind() loginDisplay.click(function () { let c = prompt("以下填写内容其他人登录可见:") if (c && c.trim().length > 0) { try { UM.getEditor('message').setContent('[ttlogin]' + c + '[/ttlogin]', true) } catch (err) { document.getElementById('message').innerHTML += '[ttlogin]' + c + '[/ttlogin]'; } } }) replyDisplay.click(function () { let c = prompt("以下填写内容其他人回复可见:") if (c && c.trim().length > 0) { try { UM.getEditor('message').setContent('[ttreply]' + c + '[/ttreply]', true) } catch (err) { document.getElementById('message').innerHTML += '[ttreply]' + c + '[/ttreply]'; } } }) $("body").addClass("insertHiddenContentMark") }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

发帖页面插入隐藏内容按钮优化

") } }, replyFixed: { // 和所属对象属性名保持一致 id: "replyFixed", // 显示在界面上的标题 title: "回复框吸附", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread") && !Utils.hasElement(".replyFixed") }, // 功能生效的逻辑代码 doAction: function () { $("#message").focus(function () { let replyInput = $(".post.newpost.media") if (!replyInput) { return } function effect() { replyInput.addClass("fixed-reply") let width = replyInput.parent().parent().outerWidth() replyInput.css("width", (width + 20) + "px") $("#advanced_reply").parent().prepend(' 取消吸附') $("#cancel-reply-fixed").click(function () { cancel() }) $(".fixed-reply").find(".mr-3").hide() $(".fixed-reply").find(".d-flex.justify-content-between.small.text-muted").css("cssText", "display:none !important") let o = $($($(".fixed-reply").children()[1]).children()[1]) o.css("display", "flex") o.css("justify-content", "center") o.css("align-items", "center") $("#quick_reply_form").css("width", "98%") } function cancel() { $("#cancel-reply-fixed").remove() $(".fixed-reply").find(".mr-3").show() $(".fixed-reply").find(".d-flex.justify-content-between.small.text-muted").show() let o = $($($(".fixed-reply").children()[1]).children()[1]) o.css("display", "") o.css("justify-content", "") o.css("align-items", "") $("#quick_reply_form").css("width", "") replyInput.removeClass("fixed-reply") replyInput.css("width", "") } if (!replyInput.hasClass("fixed-reply")) { effect() } }) $("body").addClass("replyFixed") }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("

功能打开后,当在帖子详情页面里,回复框获得焦点后,回复框会吸附在屏幕底部

") } }, InfiniteScrolling: { // 和所属对象属性名保持一致 id: "InfiniteScrolling", // 显示在界面上的标题 title: "瀑布流评论区", // 配置变化时是否需要重新加载页面 needReload: false, // 所有用到的配置全部写在这里,config对象会持久化 // 除 enable 属性外,其他属性要在 configKeyElementMaps 中定义一个同名属性来映射页面元素 config: { enable: false }, // 该功能用到的 config 属性名和 元素class/id的映射 configKeyElementMaps: {}, // 功能生效的前提条件检查 matchCondition: function () { return Utils.isMatchPageCategory("thread") && !Utils.hasElement(".InfiniteScrolling") }, // 功能生效的逻辑代码 doAction: function () { // 分页区域 $(".pagination.my-4.justify-content-center.flex-wrap").hide() let last = $(".pagination.my-4.justify-content-center.flex-wrap").eq(0).children().last() if(last.text() == "▶"){ last = last.prev() } if(last.text() == ""){ return } // 评论区域 let replyZone = $(".card.card-postlist").find(".list-unstyled.postlist").eq(0).children() // 没有回复时,length是2 if(!replyZone || replyZone.length <= 2){ return; } // 分页数 let pageCount = parseInt(last.text()); let lastReplyElement = null; let currentLoadPage = 1; // 获取评论区最后一条评论 let getLastReplyElement = function(){ return $(".card.card-postlist").find(".list-unstyled.postlist").eq(0).children().eq(-3)[0] } // 监听评论区最后一条评论 let observeLastReplyElement = function(){ if(lastReplyElement){ observer.unobserve(lastReplyElement); } if(currentLoadPage>=pageCount){ return } lastReplyElement = getLastReplyElement() if(!lastReplyElement){ return } observer.observe(lastReplyElement); } let nextPageUrl = function() { currentLoadPage++ let href = window.location.pathname href = href.replace(".htm","") href = href + "-"+currentLoadPage+".htm" return href } const observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { // 这时滚动到本页最后一条回复位置了,加载下一页数据 Utils.fetchHtml(nextPageUrl(),function(object){ let data = object.find(".card.card-postlist").find(".list-unstyled.postlist").eq(0).children() if(data && data.length >2){ let index = 0 let endIndex = data.length - 2 while(index < endIndex){ data.eq(index).find(".floor.mr-0").text((currentLoadPage-1)*20+index+1) let last = getLastReplyElement() last.after(data[index]) index++ } } observeLastReplyElement() },true) } }); }); observeLastReplyElement() $("body").addClass("InfiniteScrolling") }, // 功能配置的html代码 contentHtml: function () { return Utils.createMsgDiv("
在帖子详情页面,去掉了页码显示。如果有很多页,鼠标滚动到最后一条评论位置时,自动加载下一页评论,拼接在后面。
假设点进帖子时,评论有3页,那么最多自动加载页数会固定为3,在阅读评论期间且没刷新过页面时,评论已经到4页,该功能不会自动加载第4页的内容,此时需要手动刷新页面来重置自动加载页数。这么做是防止在最后一页时,滚动页面,导致反复触发请求服务器新数据,减轻服务器压力
") } } } // 所有的工具函数都放在这里 var Utils = { configPrefix: "OldManHelperConfig", BLACK_TYPE_ALL: "全部", BLACK_TYPE_THREAD: "帖子", BLACK_TYPE_REPLY: "回复", //工具用到的所有css样式都定义在这里 styles: ` `, //设置按钮的html定义 settingButtonHtml: `
`, // 插件初始化 init: function () { var headHTML = document.getElementsByTagName('head')[0].innerHTML; headHTML = '' + headHTML; document.getElementsByTagName('head')[0].innerHTML = headHTML; //加载配置 this.loadConfig() //创建设置按钮以及加载css document.documentElement.insertAdjacentHTML('beforeend', this.styles + this.settingButtonHtml); //创建设置面板 $('body').append(this.createSettingPanel(true)) //绑定背景层事件 $("#overlay-panel").click(function () { $("#setting-panel").hide(200) $("#overlay-panel").hide() Utils.saveConfig(window.currentFunctionKey) Utils.reloadPage(window.currentFunctionKey) }) //创建功能列表 this.createSettingItems(settingObject) //绑定设置按钮点击事件,点击后隐藏/显示设置面板 window.openOldManHelper = function () { let settingPanel = $("#setting-panel"); let overlayPanel = $("#overlay-panel"); if (settingPanel.css("display") === 'none') { Utils.setCurrentContentHtml(Utils.getCurrentFunctionObject()) var d = document.documentElement settingPanel.css("display", "flex"); settingPanel.css("position", "absolute"); settingPanel.css("top", d.scrollTop + (d.clientHeight - 400) / 2); settingPanel.css("left", d.scrollLeft + (d.clientWidth - 600) / 2); settingPanel.show(200); overlayPanel.show() } else { settingPanel.hide(200) overlayPanel.hide() Utils.saveConfig(window.currentFunctionKey) Utils.reloadPage(window.currentFunctionKey) } } $("#setting_btn").click(function () { window.openOldManHelper() }) $('body').mousemove(function (e) { e = e || window.event; window.__xx = e.pageX || e.clientX + document.body.scroolLeft; window.__yy = e.pageY || e.clientY + document.body.scrollTop; }); this.applyFunction() }, saveConfig: function (functionKey) { if (!functionKey || !settingObject[functionKey]) { return } let functionObject = settingObject[functionKey] var config = {} // 如果设置页面处于显示状态,需要从设置页面读取值更新内存,最后再持久化存储 // 如果设置页面没有显示,略过从设置页面读取值更新内存这一步,直接将内存配置持久化存储 if (this.isSettingPanelShow()) { for (let configName of Object.keys(functionObject.config)) { let val = null //功能开关统一处理 if (configName == "enable") { val = document.getElementById('switch-' + functionKey).checked } else { //拿到属性key的映射对象 let mapObject = functionObject.configKeyElementMaps[configName] if (mapObject) { // 如果映射对象有getVal函数,那么就调用getVal函数取得值,如果没有,就默认调用 getElementVal 函数取值 val = mapObject.getVal ? mapObject.getVal() : this.getElementVal(mapObject.element) } } functionObject.config[configName] = val config[configName] = val } } else { config = functionObject.config } //将window.Config对象数据保存到localStorage localStorage.setItem(this.configPrefix + "__" + functionKey, JSON.stringify(config)); this.applyFunction(functionKey) }, loadConfig: function () { for (let functionName of Object.keys(settingObject)) { let item = settingObject[functionName] let c = localStorage.getItem(this.configPrefix + "__" + functionName) if (!c) { console.log("加载[" + item.title + "]功能配置出错:未找到配置!将使用默认配置。") continue; } try { let cobj = JSON.parse(c) for (let key of Object.keys(cobj)) { item.config[key] = cobj[key] } } catch (e) { console.log("加载[" + item.title + "]功能配置出错:json解析错误!将使用默认配置。" + e.toString()) } } }, noticeSetRead: function (id) { $.xpost(xn.url('my-notice'), { act: 'readone', nid: id }, function (code, message) { }); }, parsePageIdAndQuoteId: function (href) { var tmp = href.replace("thread-", "") tmp = tmp.substring(0, tmp.lastIndexOf(".")) return { pageId: tmp, quoteId: href.split("#")[1] } }, quickReply: function (pageId, quoteId, content, successCallBack = null, errorCallBack = null) { try { $.ajax({ type: "post", url: "post-create-" + pageId + "-1-" + quoteId + ".htm", data: "doctype=1&return_html=1"epid=" + quoteId + "&message=" + content, async: true, success: function (html) { successCallBack && successCallBack() }, error: function (e) { errorCallBack && errorCallBack() } }); } catch (e) { console.log(e) } }, getCurrentPageThreadId: function () { return window.location.href.split("-")[1].replace(".htm", "") }, hasElement: function (el, root = null) { return root ? $(root).find(el).length > 0 : $(el).length > 0 }, getCurrentFunctionObject: function () { if (!window.currentFunctionKey) { return null; } let object = settingObject[window.currentFunctionKey] return object ? object : null; }, isSettingPanelShow: function () { return $("#setting-panel").css("display") != 'none' }, reloadPage: function (functionKey) { if (!functionKey || !settingObject[functionKey]) { return } let functionObject = settingObject[functionKey] if (functionObject.needReload) { window.location.reload(); } }, loadJSColor: function () { var input = $("[data-jscolor]"); for (var i = 0; i < input.length; i++) { var picker = new jscolor(input[i]); picker.hash = true; } jscolor.init(); }, // 判断页面地址是否有指定前缀 isMatchPageCategory: function (pagePrefix) { return window.location.href.startsWith(window.location.protocol + "//" + window.location.host + "/" + pagePrefix) }, // 判断当前页面是否为主页 isIndexPage: function () { return window.location.href == 'https://bbs.oldmanemu.net/' || window.location.href == 'https://bbs.oldmantvg.net/' }, getElementVal(idOrClass) { return $(idOrClass).val() }, applyFunction: function (functionName = "") { let applyFn = function (fn) { if (fn && fn.config && fn.config.enable && fn.doAction) { if (!fn.matchCondition || (fn.matchCondition && fn.matchCondition())) { fn.doAction(); } } } if (functionName == "") { for (let functionName of Object.keys(settingObject)) { applyFn(settingObject[functionName]) } } else { applyFn(settingObject[functionName]) } }, // 创建开关按钮 createIosThemeSwitch: function (item, jqueryObject = false) { let checked = function (item) { return item.config.enable ? 'checked="checked"' : '' } let html = `
` return jqueryObject ? $(html) : html }, //创建功能div createSettingItem: function (functionKey, item, jqueryObject = false) { let html = `
${item.title}
${this.createIosThemeSwitch(item)}
` return jqueryObject ? $(html) : html }, //创建设置面板 createSettingPanel: function (jqueryObject = false) { let html = `
${this.createDefaultContentHtml()}
` return jqueryObject ? $(html) : html }, createQuickReplyForm: function (pageId, quoteId, content, jqueryObject = false) { let html = `
` return jqueryObject ? $(html) : html }, setCurrentContentHtml: function (functionObject, ifFnIsNullUseDefault = true) { $(".content").html(functionObject && functionObject.contentHtml ? functionObject.contentHtml() : ifFnIsNullUseDefault ? this.createDefaultContentHtml() : "") Utils.loadJSColor() }, // 创建功能列表 createSettingItems: function (settingObject) { var list = $(".setting-item-list") let index = 0; let lastIndex = Object.keys(settingObject).length - 1 for (let functionName of Object.keys(settingObject)) { let item = settingObject[functionName] let setting = this.createSettingItem(functionName, item, true) setting.click(function () { window.lastFunctionKey = window.currentFunctionKey window.currentFunctionKey = $(this).attr("functionKey") Utils.saveConfig(window.lastFunctionKey) $(".setting-item").removeClass("select-item") $(this).addClass("select-item") Utils.setCurrentContentHtml(item) }) list.append(setting) if (index != lastIndex) { list.append($('
')) } } }, createDivWithTitle: function (title, contentHtml, jqueryObject = false, styles = "", titleStyles = "") { let html = `

${title}

${contentHtml}
` return jqueryObject ? $(html) : html }, createDefaultContentHtml: function (jqueryObject = false) { let html = ` 老男人助手by rock

有任何bug反馈或者功能建议,请在论坛回复我的帖子,或者发送邮件给我 1099607871@qq.com

感谢使用

` return jqueryObject ? $(html) : html }, createBlackTypeButton: function (banType, userId, username, text, jqueryObject = false) { let html = ` ` return jqueryObject ? $(html) : html }, createMsgDiv: function (msgHtml, jqueryObject = false) { let html = `
${msgHtml}
` return jqueryObject ? $(html) : html }, createReplyItem: function (infoObject, jqueryObject = false) { let html = `
${this.createDivWithTitle(infoObject.username, '
' + infoObject.time + ' ' + infoObject.threadName + ' : ' + infoObject.replyInfo + '
', false, "width:240px;border: 2px solid gray;border-style:dashed;", "background-color: white !important;")}
` return jqueryObject ? $(html) : html }, getHost: function () { return window.location.protocol + "//" + window.location.host }, fetchHtml: function (webPath, callback, jqueryObject = false) { $.ajax({ type: "get", url: Utils.getHost() + webPath, dataType: "html", async: true, success: function (html) { callback && callback(jqueryObject ? $(html) : html) } }); } } $(document).ready(function () { Utils.init() }); })();