// ==UserScript==
// @name NGA Fast View Post Fix
// @name:zh-CN NGA “快速浏览这个帖子” 修复
// @description 修复论坛“快速浏览这个帖子”功能,并可在“快速浏览这个帖子”时点赞,以及点赞后更新实时点赞数
// @namespace https://greasyfork.org/users/263018
// @version 1.1.1
// @author snyssss
// @license MIT
// @match *://bbs.nga.cn/*
// @match *://ngabbs.com/*
// @match *://nga.178.com/*
// @require https://update.greasyfork.org/scripts/486070/1378387/NGA%20Library.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @noframes
// ==/UserScript==
((ui, ubbcode) => {
if (!ui) return;
if (!ubbcode) return;
// KEY
const USER_AGENT_KEY = "USER_AGENT";
const IS_SHOW_SCORE_KEY = "IS_SHOW_SCORE";
const IS_SHOW_SCORE_RESULT_KEY = "IS_SHOW_SCORE_RESULT_KEY";
// User Agent
const USER_AGENT = (() => {
const data = GM_getValue(USER_AGENT_KEY) || "Nga_Official";
GM_registerMenuCommand(`修改UA:${data}`, () => {
const value = prompt("修改UA", data);
if (value) {
GM_setValue(USER_AGENT_KEY, value);
location.reload();
}
});
return data;
})();
// 是否显示评分
const showScore = (() => {
const data = GM_getValue(IS_SHOW_SCORE_KEY) || false;
GM_registerMenuCommand(`显示评分:${data ? "是" : "否"}`, () => {
GM_setValue(IS_SHOW_SCORE_KEY, !data);
location.reload();
});
return data;
})();
// 是否显示评分结果
const showScoreResult = (() => {
const data = GM_getValue(IS_SHOW_SCORE_RESULT_KEY) || false;
GM_registerMenuCommand(`显示评分结果:${data ? "是" : "否"}`, () => {
GM_setValue(IS_SHOW_SCORE_RESULT_KEY, !data);
location.reload();
});
return data;
})();
// 简单的统一请求
const request = (url, config = {}) =>
fetch(url, {
headers: {
"X-User-Agent": USER_AGENT,
},
...config,
});
// 获取帖子信息
const getPostInfo = async (tid, pid) => {
const url = `/read.php?tid=${tid}&pid=${pid}`;
const data = await new Promise((resolve) =>
request(url)
.then((res) => res.blob())
.then((res) => {
// 读取内容
const reader = new FileReader();
reader.onload = () => {
const parser = new DOMParser();
const doc = parser.parseFromString(reader.result, "text/html");
// 验证帖子正常
const verify = doc.querySelector("#m_posts");
if (verify === null) {
throw new Error();
}
// 取得顶楼 UID
const uid = (() => {
const ele = doc.querySelector("#postauthor0");
if (ele) {
const res = ele.getAttribute("href").match(/uid=((-?)(\S+))/);
if (res) {
return res[1];
}
}
return 0;
})();
// 取得顶楼标题
const subject = doc.querySelector("#postsubject0").innerHTML;
// 取得顶楼内容
const content = doc.querySelector("#postcontent0").innerHTML;
// 取得用户信息
const user = (() => {
const text = Tools.searchPair(reader.result, `"${uid}":`);
if (text) {
try {
return JSON.parse(text);
} catch {
return null;
}
}
return null;
})();
// 取得额外信息
const extra = (() => {
const content = reader.result.substring(
reader.result.indexOf(`commonui.postArg.proc( 0,`)
);
const text = Tools.searchPair(
content,
`commonui.postArg.proc`,
`(`,
`)`
);
if (text) {
return text;
}
return null;
})();
// 返回结果
resolve({
subject,
content,
user,
extra,
});
};
reader.readAsText(res, "GBK");
})
.catch(() => {
resolve(null);
})
);
return data;
};
// 快速浏览
const fastViewPost = (() => {
const window = ui.createCommmonWindow();
const container = document.createElement("DIV");
container.className = `fastViewPost`;
container.innerHTML = `
<div class="forumbox">
<div class="postrow"></div>
</div>
`;
const list = container.querySelector(".postrow");
window._.addContent(null);
window._.addContent(container);
return async (_, tid, pid = 0, opt) => {
// 如果 opt 是 16,则说明是快速浏览窗口中的快速浏览,追加楼层
// 反之清空楼层
if (opt !== 16) {
list.innerHTML = "";
}
// 如果已加载过,直接返回
if (list && list.querySelector(`[data-pid="${pid}"]`)) {
return;
}
// 请求内容,移除了 opt 参数
// 泥潭会在快速浏览窗口里再次点击快速浏览时,把整个相关对话列出来,反而找不到当前帖子
const data = await getPostInfo(tid, pid);
// 没有内容
if (data === null) {
return;
}
// 解析数据
const { content, user, extra } = data;
// 发帖人姓名
const username = ui.htmlName(user.username);
// 加载楼层
const row = document.createElement("DIV");
row.className = `row${
2 - (list.querySelectorAll(":scope > div").length % 2)
}`;
row.innerHTML = `
<div class="c2" data-pid="${pid}">
<div class="posterInfoLine b">${username}</div>
<span class="postcontent ubbcode">${content}</span>
</div>
`;
list.insertBefore(row, list.firstChild);
// 格式转换完毕后显示窗口
// 但泥潭这样做实际毫无意义,如果窗口开着的情况下进行格式转换,照样会短暂显示乱码
ubbcode.bbsCode({
c: row.querySelector(".ubbcode"),
tId: tid,
pId: pid,
opt: 8,
authorId: user.uid,
noImg: 1,
isNukePost: 0,
callBack: () => {
window._.show();
},
});
// 显示评分
if (showScore) {
// 取得评分数据
const recommend = extra.match(/'(\d+),(\d+),(\d+)'/);
if (recommend) {
const score_1 = recommend[2];
const score_2 = recommend[3];
const ele = document.createElement("DIV");
ele.className = `right_`;
ele.innerHTML = `
<div style="display: inline-block; font-weight: normal;">
<span class="small_colored_text_btn stxt block_txt_c2 vertmod">
<span class="white">
<a class="white" href="javascript: void(0);" title="支持" style="text-decoration: none;">
<span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em; padding: 0 0.1em;">⯅</span>
</a>
<span class="recommendvalue"></span>
<a class="white" href="javascript: void(0);" title="反对" style="text-decoration: none;">
<span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em; padding: 0 0.1em;">⯆</span>
</a>
</span>
</span>
</div>
`;
ele.querySelector(".recommendvalue").innerHTML =
score_1 - score_2 || " ";
row.querySelector(".posterInfoLine").appendChild(ele);
// 绑定事件
(() => {
const bindEvent = () => {
const like = ele.querySelector("a:first-child");
like.onclick = () => {
ui.postScoreAdd(like, {
tid,
pid,
});
};
const unlike = ele.querySelector("a:last-child");
unlike.onclick = () => {
ui.postScoreAdd(
unlike,
{
tid,
pid,
},
1
);
};
};
// 绑定事件
if (ui.postScoreAdd) {
bindEvent();
} else {
__SCRIPTS.asyncLoad("read", bindEvent);
}
})();
}
}
};
})();
// 刷新评分
const refreshScore = (anchor, { tid, pid }) => {
const target = anchor.parentNode.querySelector(".recommendvalue");
if (target === null) {
return;
}
const observer = new MutationObserver(() => {
observer.disconnect();
getPostInfo(tid, pid).then(({ extra }) => {
if (extra) {
const recommend = extra.match(/'(\d+),(\d+),(\d+)'/);
if (recommend) {
const score_1 = recommend[2];
const score_2 = recommend[3];
target.innerHTML = score_1 - score_2 || " ";
}
}
});
});
observer.observe(target, {
childList: true,
});
};
// 加载脚本
(() => {
// 修复快速浏览
ubbcode.fastViewPost = fastViewPost;
// 绑定评分事件
if (ui && showScoreResult) {
Tools.interceptProperty(ui, "postScoreAdd", {
afterGet: (_, args) => {
refreshScore(...args);
},
});
}
})();
})(commonui, ubbcode);