// ==UserScript==
// @name DeepFlood X
// @namespace http://www.deepflood.com/
// @version 0.4.1.1
// @description 【原DeepFlood增强】自动签到、无缝翻页帖子评论、快捷回复、代码高亮、屏蔽用户、屏蔽帖子、楼主低等级提醒
// @author dabao
// @match *://*.deepflood.com/*
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACz0lEQVR4Ae3B32tVdQAA8M85u7aVHObmzJVD0+ssiphstLEM62CBlCBEIAYhUoGGD/kiRUo+9CIEElFZgZJFSApBVhCUX2WFrVQKf5Qy26SgdK4pN7eZu+cbtyfJ/gLx83HD9SAhlEyXupiPhUSTeonRfNw1ws2aRJeN5jHcolFhJJ9M8Zj99piDTnv12SjzfzIb9dmrC7Pttt8ykjDVLsu8ZZ1GH1oqeDofJLtJh4fMEw3Y72jlCuEO2+W+sNJFr3vOZ1YIi8NIGA29hDWhGgZDJ2Rt2ZvZSBazmMUsZsPZ1qwVQmcYDNWwhtAbRsNIWJx6WLPDfgxNVkm9nR8hm+XduLba7F9RtcXztmUzyY/YJrUqNPvBYc0eSS3CwXxMl4WG7CarsyEuvU2HOkRNujSw3PosxR6DFurKxx3E/akFohPo0aDfEO61os5LdrtLVWG1TzxokifdiSH9GnTjuGhBqsWE39GOo3kVi8wsmeVW00SJ200zA9r0kFcdQzv+MKElVW/S+L5EE86pmUth3BV/SzCOCUjMVXMWzfsSYybVl1SlSlESkagpuOI1nzshFX1gyAF1UKhJEKOkJFVNXVBv+pJoBK1qBkh86z1/SaR+9o5zEgoDaloxsiSart6F1Bkl83ESHWEKvvEbqZJETaokgSH9hCk6cBLtSs6kDqEb/cZ0K+MnO0X/VdhRGUBZjzH9uA+HUl+a0BvmO+J7bVZSKWz1kehqhfe9oWalNoccDmW9JnyV+toxsy3PK3aY9Gx4gMp567ziV4WawpCXra+MEhZ5xqTtecVycxzXlxA22OK4ZYbt9LjvrM5PkNUp6zVPdNpBv1QKwt126Paxp8zwqXu8kG8pYZdHlT2Rvxo2aVG2ObyYn65UnXLKVULZZrP02ZRfCms1OmAXCSHRYqrLzuZFaDFV6s/8omuERs0Kl/LzITVTvTHDeXTD9eAftAsSYhXYOWUAAAAASUVORK5CYII=
// @require https://s4.zstatic.net/ajax/libs/layui/2.9.9/layui.min.js
// @resource highlightStyle https://s4.zstatic.net/ajax/libs/highlight.js/11.9.0/styles/atom-one-light.min.css
// @resource highlightStyle_dark https://s4.zstatic.net/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_notification
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_getResourceURL
// @grant GM_addElement
// @grant GM_addStyle
// @grant GM_openInTab
// @grant unsafeWindow
// @run-at document-end
// @license GPL-3.0 License
// @supportURL https://www.deepflood.com/
// @homepageURL https://www.deepflood.com/
// ==/UserScript==
(function () {
'use strict';
const { version, author, name, icon } = GM_info.script;
// 适配新站点
const BASE_URL = "https://www.deepflood.com";
const util = {
clog:(c) => {
console.group(`%c %c [${name}]-v${version} by ${author}`, `background:url(${icon}) center/12px no-repeat;padding:3px`, "");
console.log(c);
console.groupEnd();
},
getValue: (name, defaultValue) => GM_getValue(name, defaultValue),
setValue: (name, value) => GM_setValue(name, value),
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
addStyle(id, tag, css) {
tag = tag || 'style';
let doc = document, styleDom = doc.head.querySelector(`#${id}`);
if (styleDom) return;
let style = doc.createElement(tag);
style.rel = 'stylesheet';
style.id = id;
tag === 'style' ? style.innerHTML = css : style.href = css;
doc.head.appendChild(style);
},
removeStyle(id,tag){
tag = tag || 'style';
let doc = document, styleDom = doc.head.querySelector(`#${id}`);
if (styleDom) { doc.head.removeChild(styleDom) };
},
getAttrsByPrefix(element, prefix) {
return Array.from(element.attributes).reduce((acc, { name, value }) => {
if (name.startsWith(prefix)) acc[name] = value;
return acc;
}, {});
},
data(element, key, value) {
if (arguments.length < 2) return undefined;
if (value !== undefined) element.dataset[key] = value;
return element.dataset[key];
},
async post(url, data, headers, responseType = 'json') {
return this.fetchData(url, 'POST', data, headers, responseType);
},
async get(url, headers, responseType = 'json') {
return this.fetchData(url, 'GET', null, headers, responseType);
},
async fetchData(url, method='GET', data=null, headers={}, responseType='json') {
const options = {
method,
headers: { 'Content-Type':'application/json',...headers},
body: data ? JSON.stringify(data) : undefined
};
const response = await fetch(url.startsWith("http") ? url : BASE_URL + url, options);
const result = await response[responseType]().catch(() => null);
return response.ok ? result : Promise.reject(result);
},
getCurrentDate() {
const localTimezoneOffset = (new Date()).getTimezoneOffset();
const beijingOffset = 8 * 60;
const beijingTime = new Date(Date.now() + (localTimezoneOffset + beijingOffset) * 60 * 1000);
const timeNow = `${beijingTime.getFullYear()}/${(beijingTime.getMonth() + 1)}/${beijingTime.getDate()}`;
return timeNow;
},
createElement(tagName, options = {}, childrens = [], doc = document, namespace = null) {
if (Array.isArray(options)) {
if (childrens.length !== 0) {
throw new Error("If options is an array, childrens should not be provided.");
}
childrens = options;
options = {};
}
const { staticClass = '', dynamicClass = '', attrs = {}, on = {} } = options;
const ele = namespace ? doc.createElementNS(namespace, tagName) : doc.createElement(tagName);
if (staticClass) {
staticClass.split(' ').forEach(cls => ele.classList.add(cls.trim()));
}
if (dynamicClass) {
dynamicClass.split(' ').forEach(cls => ele.classList.add(cls.trim()));
}
Object.entries(attrs).forEach(([key, value]) => {
if (key === 'style' && typeof value === 'object') {
Object.entries(value).forEach(([styleKey, styleValue]) => {
ele.style[styleKey] = styleValue;
});
} else {
if (value !== undefined) ele.setAttribute(key, value);
}
});
Object.entries(on).forEach(([event, handler]) => {
ele.addEventListener(event, handler);
});
childrens.forEach(child => {
if (typeof child === 'string') {
child = doc.createTextNode(child);
}
ele.appendChild(child);
});
return ele;
},
b64DecodeUnicode(str) {
return decodeURIComponent(atob(str).split('').map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
};
const opts = {
post: {
pathPattern: /^\/(categories\/|page|award|search|$)/,
scrollThreshold: 1500,
nextPagerSelector: '.nsk-pager a.pager-next',
postListSelector: 'ul.post-list:not(.topic-carousel-panel)',
topPagerSelector: 'div.nsk-pager.pager-top',
bottomPagerSelector: 'div.nsk-pager.pager-bottom',
},
comment: {
pathPattern: /^\/post-/,
scrollThreshold: 690,
nextPagerSelector: '.nsk-pager a.pager-next',
postListSelector: 'ul.comments',
topPagerSelector: 'div.nsk-pager.post-top-pager',
bottomPagerSelector: 'div.nsk-pager.post-bottom-pager',
},
setting: {
SETTING_SIGN_IN_STATUS: 'setting_sign_in_status',
SETTING_SIGN_IN_LAST_DATE: 'setting_sign_in_last_date',
SETTING_SIGN_IN_IGNORE_DATE: 'setting_sign_in_ignore_date',
SETTING_AUTO_LOADING_STATUS: 'setting_auto_loading_status'
},
settings:{
"version": version,
"sign_in": { "enabled": true, "method": 0, "last_date": "", "ignore_date": "" },
"signin_tips": { "enabled": true },
"re_signin": { "enabled": true },
"auto_jump_external_links": { "enabled": true },
"loading_post": { "enabled": true },
"loading_comment": { "enabled": true },
"quick_comment": { "enabled": true },
"open_post_in_new_tab": { "enabled": false },
"block_members": { "enabled": true },
"block_posts": { "enabled": true,"keywords":[] },
"level_tag": { "enabled": true, "low_lv_alarm":false, "low_lv_max_days":30 },
"code_highlight": { "enabled": true },
"image_slide":{ "enabled":true },
"visited_links":{ "enabled": true, "link_color":"","visited_color":"","dark_link_color":"","dark_visited_color":"" },
"user_card_ext": { "enabled":true }
}
};
layui.use(function () {
const layer = layui.layer;
const dropdown = layui.dropdown;
const message = {
info: (text) => message.__msg(text, { "background-color": "#4D82D6" }),
success: (text) => message.__msg(text, { "background-color": "#57BF57" }),
warning: (text) => message.__msg(text, { "background-color": "#D6A14D" }),
error: (text) => message.__msg(text, { "background-color": "#E1715B" }),
__msg: (text, style) => { let index = layer.msg(text, { offset: 't', area: ['100%', 'auto'], anim: 'slideDown' }); layer.style(index, Object.assign({ opacity: 0.9 }, style)); }
};
const Config = {
// 初始化配置数据
initValue() {
const value = [
{ name: opts.setting.SETTING_SIGN_IN_STATUS, defaultValue: 0 },
{ name: opts.setting.SETTING_SIGN_IN_LAST_DATE, defaultValue: '1753/1/1' },
{ name: opts.setting.SETTING_SIGN_IN_IGNORE_DATE, defaultValue: '1753/1/1' },
{ name: opts.setting.SETTING_AUTO_LOADING_STATUS, defaultValue: 1 },
{ name: 'open_post_in_new_tab', defaultValue: 0 },
{ name: 'feedback', defaultValue: 0 }
];
this.upgradeConfig();
value.forEach((v) => util.getValue(v.name) === undefined && util.setValue(v.name, v.defaultValue));
},
// 升级配置项
upgradeConfig() {
const upgradeConfItem = (oldConfKey, newConfKey) => {
if (util.getValue(oldConfKey) && util.getValue(newConfKey) === undefined) {
util.clog(`升级配置项 ${oldConfKey} 为 ${newConfKey}`);
util.setValue(newConfKey, util.getValue(oldConfKey));
GM_deleteValue(oldConfKey);
}
};
upgradeConfItem('menu_signInTime', opts.setting.SETTING_SIGN_IN_LAST_DATE);
},
initializeConfig() {
const defaultConfig = opts.settings;
if (!util.getValue('settings')) {
util.setValue('settings', defaultConfig);
return;
}
if(this.getConfig('version')===version) return;
let storedConfig = util.getValue('settings');
const cleanDefaults = (stored, defaults) => {
Object.keys(stored).forEach(key => {
if (defaults[key] === undefined) {
delete stored[key];
} else if (typeof stored[key] === 'object' && stored[key] !== null && !(stored[key] instanceof Array)) {
cleanDefaults(stored[key], defaults[key]);
}
});
};
const mergeDefaults = (stored, defaults) => {
Object.keys(defaults).forEach(key => {
if (typeof defaults[key] === 'object' && defaults[key] !== null && !(defaults[key] instanceof Array)) {
if (!stored[key]) stored[key] = {};
mergeDefaults(stored[key], defaults[key]);
} else {
if (stored[key] === undefined) {
stored[key] = defaults[key];
}
}
});
};
mergeDefaults(storedConfig, defaultConfig);
cleanDefaults(storedConfig, defaultConfig);
storedConfig.version = version;
util.setValue('settings',storedConfig);
},updateConfig(path, value) {
let config = util.getValue('settings');
let keys = path.split('.');
let lastKey = keys.pop();
let lastObj = keys.reduce((obj, key) => obj[key], config);
lastObj[lastKey] = value;
util.setValue('settings', config);
},getConfig(path) {
let config = GM_getValue('settings');
let keys = path.split('.');
return keys.reduce((obj, key) => obj[key], config);
}
};
const FeatureFlags={
isEnabled(featureName) {
if (Config.getConfig(featureName)) {
return Config.getConfig(`${featureName}.enabled`);
} else {
console.error(`Feature '${featureName}' does not exist.`);
return false;
}
}
};
const main = {
loginStatus: false,
//检查是否登陆
checkLogin() {
if (unsafeWindow.__config__ && unsafeWindow.__config__.user) {
this.loginStatus = true;
util.clog(`当前登录用户 ${unsafeWindow.__config__.user.member_name} (ID ${unsafeWindow.__config__.user.member_id})`);
}
},
// 自动签到
autoSignIn(rand) {
if(!FeatureFlags.isEnabled('sign_in')) return;
if (!this.loginStatus) return
if (util.getValue(opts.setting.SETTING_SIGN_IN_STATUS) === 0) return;
rand = rand || (util.getValue(opts.setting.SETTING_SIGN_IN_STATUS) === 1);
let timeNow = util.getCurrentDate(),
timeOld = util.getValue(opts.setting.SETTING_SIGN_IN_LAST_DATE);
if (!timeOld || timeOld != timeNow) {
util.setValue(opts.setting.SETTING_SIGN_IN_LAST_DATE, timeNow);
this.signInRequest(rand);
}
},
// 重新签到
reSignIn() {
if (!this.loginStatus) return;
if (util.getValue(opts.setting.SETTING_SIGN_IN_STATUS) === 0) {
unsafeWindow.mscAlert('提示', this.getMenuStateText(this._menus[0], 0) + ' 状态时不支持重新签到!');
return;
}
util.setValue(opts.setting.SETTING_SIGN_IN_LAST_DATE, '1753/1/1');
location.reload();
},
addSignTips() {
if(!FeatureFlags.isEnabled('signin_tips')) return;
if (!this.loginStatus) return
if (util.getValue(opts.setting.SETTING_SIGN_IN_STATUS) !== 0) return;
const timeNow = util.getCurrentDate();
const { SETTING_SIGN_IN_IGNORE_DATE, SETTING_SIGN_IN_LAST_DATE } = opts.setting;
const timeIgnore = util.getValue(SETTING_SIGN_IN_IGNORE_DATE);
const timeOld = util.getValue(SETTING_SIGN_IN_LAST_DATE);
if (timeNow === timeIgnore || timeNow === timeOld) return;
const _this = this;
let tip = util.createElement("div", { staticClass: 'nsplus-tip' });
let tip_p = util.createElement('p');
tip_p.innerHTML = '今天你还没有签到哦! 【<a class="sign_in_btn" data-rand="true" href="javascript:;">随机抽个鸡腿</a>】 【<a class="sign_in_btn" data-rand="false" href="javascript:;">只要5个鸡腿</a>】 【<a id="sign_in_ignore" href="javascript:;">今天不再提示</a>】';
tip.appendChild(tip_p);
tip.querySelectorAll('.sign_in_btn').forEach(function (item) {
item.addEventListener("click", function (e) {
const rand = util.data(this, 'rand');
_this.signInRequest(rand);
tip.remove();
util.setValue(SETTING_SIGN_IN_LAST_DATE, timeNow);
})
});
tip.querySelector('#sign_in_ignore').addEventListener("click", function (e) {
tip.remove();
util.setValue(SETTING_SIGN_IN_IGNORE_DATE, timeNow);
});
document.querySelector('header').append(tip);
},
async signInRequest(rand) {
await util.post('/api/attendance?random=' + (rand || false), {}, { "Content-Type": "application/json" }).then(json => {
if (json.success) {
message.success(`签到成功!今天午饭+${json.gain}个鸡腿; 积攒了${json.current}个鸡腿了`);
}
else {
message.info(json.message);
}
}).catch(error => {
message.info(error.message || "发生未知错误");
util.clog(error);
});
util.clog(`[${name}] 签到完成`);
},
is_show_quick_comment: false,
quickComment() {
if (!this.loginStatus || !opts.comment.pathPattern.test(location.pathname)) return;
if (util.getValue(opts.setting.SETTING_AUTO_LOADING_STATUS) === 0) return;
const _this = this;
const onClick = (e) => {
if (_this.is_show_quick_comment) {
return;
}
e.preventDefault();
const mdEditor = document.querySelector('.md-editor');
const clientHeight = document.documentElement.clientHeight, clientWidth = document.documentElement.clientWidth;
const mdHeight = mdEditor.clientHeight, mdWidth = mdEditor.clientWidth;
const top = (clientHeight / 2) - (mdHeight / 2), left = (clientWidth / 2) - (mdWidth / 2);
mdEditor.style.cssText = `position: fixed; top: ${top}px; left: ${left}px; margin: 30px 0px; width: 100%; max-width: ${mdWidth}px; z-index: 999;`;
const moveEl = mdEditor.querySelector('.tab-select.window_header');
moveEl.style.cursor = "move";
moveEl.addEventListener('mousedown', startDrag);
addEditorCloseButton();
_this.is_show_quick_comment = true;
};
const commentDiv = document.querySelector('#fast-nav-button-group #back-to-parent').cloneNode(true);
commentDiv.id = 'back-to-comment';
commentDiv.innerHTML = '<svg class="iconpark-icon" style="width: 24px; height: 24px;"><use href="#comments"></use></svg>';
commentDiv.addEventListener("click", onClick);
document.querySelector('#back-to-parent').before(commentDiv);
document.querySelectorAll('.nsk-post .comment-menu,.comment-container .comments').forEach(x=>x.addEventListener("click",(event) =>{ if(!["引用", "回复", "编辑"].includes(event.target.textContent)) return; onClick(event);},true));
function addEditorCloseButton() {
const fullScreenToolbar = document.querySelector('#editor-body .window_header > :last-child');
const cloneToolbar = fullScreenToolbar.cloneNode(true);
cloneToolbar.setAttribute('title', '关闭');
cloneToolbar.querySelector('span').classList.replace('i-icon-full-screen-one', 'i-icon-close');
cloneToolbar.querySelector('span').innerHTML = '<svg width="16" height="16" viewBox="0 0 48 48" fill="none"><path d="M8 8L40 40" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8 40L40 8" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
cloneToolbar.addEventListener("click", function (e) {
const mdEditor = document.querySelector('.md-editor');
mdEditor.style = "";
const moveEl = mdEditor.querySelector('.tab-select.window_header');
moveEl.style.cursor = "";
moveEl.removeEventListener('mousedown', startDrag);
this.remove();
_this.is_show_quick_comment = false;
});
fullScreenToolbar.after(cloneToolbar);
}
function startDrag(event) {
if (event.button !== 0) return;
const draggableElement = document.querySelector('.md-editor');
const parentMarginTop = parseInt(window.getComputedStyle(draggableElement).marginTop);
const initialX = event.clientX - draggableElement.offsetLeft;
const initialY = event.clientY - draggableElement.offsetTop + parentMarginTop;
document.onmousemove = function (event) {
const newX = event.clientX - initialX;
const newY = event.clientY - initialY;
draggableElement.style.left = newX + 'px';
draggableElement.style.top = newY + 'px';
};
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
};
}
},
//自动点击跳转页链接
autoJump() {
document.querySelectorAll('a[href*="/jump?to="]').forEach(link => {
try {
const urlObj = new URL(link.href);
const encodedUrl = urlObj.searchParams.get('to');
if (encodedUrl) {
const decodedUrl = decodeURIComponent(encodedUrl);
link.href = decodedUrl;
}
} catch (e) {
console.error('处理链接时出错:', e);
}
});
if (!/^\/jump/.test(location.pathname)) return;
document.querySelector('.btn').click();
},
blockPost(ele) {
ele = ele || document;
ele.querySelectorAll('.post-title>a[href]').forEach(function (item) {
if (item.textContent.toLowerCase().includes("__keys__")) {
item.closest(".post-list-item").classList.add('blocked-post')
}
});
},
blockPostsByViewLevel(ele) {
ele = ele || document;
let level=0;
if (this.loginStatus) level = unsafeWindow.__config__.user.rank;
[...ele.querySelectorAll('.post-list-item use[href="#lock"]')].forEach(el => {
const n = +el.closest('span')?.textContent.match(/\d+/)?.[0] || 0;
if (n > level) el.closest('.post-list-item')?.classList.add('blocked-post');
});
},
//屏蔽用户
blockMemberDOMInsert() {
if (!this.loginStatus) return;
const _this = this;
Array.from(document.querySelectorAll(".post-list .post-list-item,.content-item")).forEach((function (t, n) {
var r = t.querySelector('.avatar-normal');
r.addEventListener("click", (function (n) {
n.preventDefault();
let intervalId = setInterval(async () => {
const userCard = document.querySelector('div.user-card.hover-user-card');
const pmButton = document.querySelector('div.user-card.hover-user-card a.btn');
if (userCard && pmButton) {
clearInterval(intervalId);
const dataVAttrs = util.getAttrsByPrefix(userCard, 'data-v');
const userName = userCard.querySelector('a.Username').textContent;
dataVAttrs.style = "float:left; background-color:rgba(0,0,0,.3)";
const blockBtn = util.createElement("a", {
staticClass: "btn", attrs: dataVAttrs, on: {
click: function (e) {
e.preventDefault();
unsafeWindow.mscConfirm(`确定要屏蔽“${userName}”吗?`, '你可以在本站的 设置=>屏蔽用户 中解除屏蔽', function () { blockMember(userName); })
}
}
}, ["屏蔽"]);
pmButton.after(blockBtn);
}
}, 50);
}))
}))
function blockMember(userName) {
util.post("/api/block-list/add", { "block_member_name": userName }, { "Content-Type": "application/json" }).then(function (data) {
if (data.success) {
let msg = '屏蔽用户【' + userName + '】成功!';
unsafeWindow.mscAlert(msg);
util.clog(msg);
} else {
let msg = '屏蔽用户【' + userName + '】失败!' + data.message;
unsafeWindow.mscAlert(msg);
util.clog(msg);
}
}).catch(function (err) {
util.clog(err);
});
}
},
addImageSlide() {
if (!opts.comment.pathPattern.test(location.pathname)) return;
const posts = document.querySelectorAll('article.post-content');
posts.forEach(function (post, i) {
const images = post.querySelectorAll('img:not(.sticker)');
if (images.length === 0) return;
images.forEach(function (image, i) {
const newImg = image.cloneNode(true);
image.parentNode.replaceChild(newImg, image);
newImg.addEventListener('click', function (e) {
e.preventDefault();
const imgArr = Array.from(post.querySelectorAll('img:not(.sticker)'));
const clickedIndex = imgArr.indexOf(this);
const photoData = imgArr.map((img, i) => ({ alt: img.alt, pid: i + 1, src: img.src }));
layer.photos({ photos: { "title": "图片预览", "start": clickedIndex, "data": photoData } });
}, true);
});
});
},
addLevelTag() {
if (!this.loginStatus) return;
if (!opts.comment.pathPattern.test(location.pathname)) return;
this.getUserInfo(unsafeWindow.__config__.postData.op.uid).then((user) => {
let warningInfo = '';
const daysDiff = Math.floor((new Date() - new Date(user.created_at)) / (1000 * 60 * 60 * 24));
if (daysDiff < 30) {
warningInfo = `⚠️`;
}
console.log(user);
const span = util.createElement("span", { staticClass: `nsk-badge role-tag user-level user-lv${user.rank}`, on: { mouseenter: function (e) { layer.tips(`注册 <span class="layui-badge">${daysDiff}</span> 天;帖子 ${user.nPost};评论 ${user.nComment}`, this, { tips: 3, time: 0 }); }, mouseleave: function (e) { layer.closeAll(); } } }, [util.createElement("span", [`${warningInfo}Lv ${user.rank}`])]);
const authorLink = document.querySelector('#nsk-body .nsk-post .nsk-content-meta-info .author-info>a');
if (authorLink != null) {
authorLink.after(span);
}
});
},
getUserInfo(uid) {
return new Promise((resolve, reject) => {
util.get(`/api/account/getInfo/${uid}`, {}, 'json').then((data) => {
if (!data.success) {
util.clog(data);
return;
}
resolve(data.detail);
}).catch((err) => reject(err));
})
},
userCardEx() {
if (!this.loginStatus) return;
if (!(opts.post.pathPattern.test(location.pathname)|| opts.comment.pathPattern.test(location.pathname))) return;
const updateNotificationElement = (element, href, iconHref, text, count) => {
element.querySelector("a").setAttribute("href", `${href}`);
element.querySelector("a > svg > use").setAttribute("href", `${iconHref}`);
element.querySelector("a > :nth-child(2)").textContent = `${text} `;
element.querySelector("a > :last-child").textContent = count;
const countEl = element.querySelector("a > :last-child");
countEl.classList.toggle("notify-count", count > 0);
return element;
};
const userCard = document.querySelector(".user-card .user-stat");
const lastElement = userCard.querySelector(".stat-block:first-child > :last-child");
const atMeElement = lastElement.cloneNode(true);
const msgElement = lastElement.cloneNode(true);
lastElement.after(atMeElement);
userCard.querySelector(".stat-block:last-child").append(msgElement);
const updateAllCounts = (counts) => {
updateNotificationElement(atMeElement, "/notification#/atMe", "#at-sign", "我", counts.atMe);
updateNotificationElement(msgElement, "/notification#/message?mode=list", "#envelope-one", "私信", counts.message);
updateNotificationElement(lastElement, "/notification#/reply", "#remind-6nce9p47", "回复", counts.reply);
};
updateAllCounts(unsafeWindow.__config__.user.unViewedCount);
let interval = 30000;
const minInterval = 30000;
const maxInterval = 10 * 60 * 1000;
function fetchUnreadCount() {
fetch(BASE_URL + "/api/notification/unread-count", { credentials: "include" })
.then(res => res.ok ? res.json() : Promise.reject(res.status))
.then(data => {
if (data.success && data.unreadCount) updateAllCounts(data.unreadCount);
interval = minInterval;
})
.catch(err => {
console.error(err);
interval = Math.min(interval * 2, maxInterval);
})
.finally(() => setTimeout(fetchUnreadCount, interval));
}
fetchUnreadCount();
},
// 自动翻页
autoLoading() {
if (util.getValue(opts.setting.SETTING_AUTO_LOADING_STATUS) === 0) return;
let opt = {};
if (opts.post.pathPattern.test(location.pathname)) { opt = opts.post; }
else if (opts.comment.pathPattern.test(location.pathname)) { opt = opts.comment; }
else { return; }
let is_requesting = false;
let _this = this;
this.windowScroll(function (direction, e) {
if (direction === 'down') {
let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
if (document.documentElement.scrollHeight <= document.documentElement.clientHeight + scrollTop + opt.scrollThreshold && !is_requesting) {
if (!document.querySelector(opt.nextPagerSelector)) return;
let nextUrl = document.querySelector(opt.nextPagerSelector).attributes.href.value;
is_requesting = true;
util.get(nextUrl, {}, 'text').then(function (data) {
let doc = new DOMParser().parseFromString(data, "text/html");
_this.blockPost(doc);
_this.blockPostsByViewLevel(doc);
if (opts.comment.pathPattern.test(location.pathname)){
let el = doc.getElementById('temp-script')
let jsonText = el.textContent;
if (jsonText) {
let conf = JSON.parse(util.b64DecodeUnicode(jsonText))
unsafeWindow.__config__.postData.comments.push(...conf.postData.comments);
}
}
document.querySelector(opt.postListSelector).append(...doc.querySelector(opt.postListSelector).childNodes);
document.querySelector(opt.topPagerSelector).innerHTML = doc.querySelector(opt.topPagerSelector).innerHTML;
document.querySelector(opt.bottomPagerSelector).innerHTML = doc.querySelector(opt.bottomPagerSelector).innerHTML;
history.pushState(null, null, nextUrl);
if (opts.comment.pathPattern.test(location.pathname)){
const vue = document.querySelector('.comment-menu').__vue__;
Array.from(document.querySelectorAll(".content-item")).forEach(function (t,e) {
var n = t.querySelector(".comment-menu-mount");
if(!n) return;
let o = new vue.$root.constructor(vue.$options);
o.setIndex(e);
o.$mount(n);
});
}
is_requesting = false;
}).catch(function (err) {
is_requesting = false;
util.clog(err);
});
}
}
});
},
// 滚动条事件
windowScroll(fn1) {
let beforeScrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop,
fn = fn1 || function () { };
setTimeout(function () {
window.addEventListener('scroll', function (e) {
const afterScrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop,
delta = afterScrollTop - beforeScrollTop;
if (delta == 0) return false;
fn(delta > 0 ? 'down' : 'up', e);
beforeScrollTop = afterScrollTop;
}, false);
}, 1000)
},
// 平滑滚动
smoothScroll(){
const scroll = (selector, top = 0) => {
const btn = document.querySelector(selector);
if (btn) {
btn.onclick = null;
btn.removeAttribute('onclick');
btn.addEventListener('click', e => {
e.preventDefault();
e.stopImmediatePropagation();
if(e.target.querySelector('use[href=\"#down\"]')){
top = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
}
window.scrollTo({ top, behavior: 'smooth' });
}, true);
}
};
scroll('#back-to-top', 0);
scroll('#back-to-bottom');
},
// === 新增:只切脚本本地状态 + 自动刷新菜单 + 绑定行为 ===
dfxSetOpenInNewTab(on) {
try {
const v = on ? 1 : 0;
GM_setValue('open_post_in_new_tab', v);
console.log('已写入 open_post_in_new_tab =', v);
this.registerMenus?.();
unsafeWindow?.mscAlert?.(`已${on ? '开启' : '关闭'}新标签页打开链接(仅脚本本地状态)`);
this.bindOpenInNewTabHandler?.();
} catch (e) {
console.error(e);
}
},
// === 新增:拦截帖子链接点击,按脚本状态强制新标签打开 ===
bindOpenInNewTabHandler() {
try {
if (unsafeWindow.__dfxOpenInNewTabHandler) {
document.removeEventListener('click', unsafeWindow.__dfxOpenInNewTabHandler, true);
}
} catch(e){}
unsafeWindow.__dfxOpenInNewTabHandler = (e) => {
const a = e.target.closest('a[href]');
if (!a) return;
const href = a.href;
const isPost = /^https?:\/\/www\.deepflood\.com\/post-\d+-\d+/.test(href);
if (isPost && util.getValue('open_post_in_new_tab') === 1) {
e.preventDefault();
e.stopImmediatePropagation();
window.open(href, '_blank');
}
};
document.addEventListener('click', unsafeWindow.__dfxOpenInNewTabHandler, true);
},
// === 完整初始化历史记录 ===
init() {
// 初始化页面时运行以下操作
this.checkLogin();
this.addSignTips();
this.autoSignIn();
this.autoLoading();
this.addImageSlide();
this.addLevelTag();
this.smoothScroll();
this.dfxSetOpenInNewTab(1); // Example: Enable 'new tab' feature
}
};
main.init();
});
})();