Skip to content

Commit 99ffd24

Browse files
feat: 从 VIP工具箱 拆分为 5 个独立脚本
1 parent feeef5c commit 99ffd24

15 files changed

Lines changed: 1013 additions & 0 deletions

File tree

src/auto-expand/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# 页面自动展开
2+
3+
自动展开被网站折叠的页面内容,移除"展开全文"提示框。
4+
5+
## 支持的网站
6+
7+
| 网站 | 说明 |
8+
|------|------|
9+
| CSDN 博客 | 自动展开文章、移除遮挡层、点击代码块展开按钮 |
10+
| CSDN 下载 | 展开下载详情 |
11+
| CSDN 文库 | 展开文库内容 |
12+
| 百度文库 | 自动点击"继续阅读"按钮加载全文 |
13+
| 思创 | 点击"阅读更多"展开内容 |
14+
15+
## 使用方式
16+
17+
安装后访问上述网站自动生效,无需配置。
18+
19+
{{changelog}}

src/auto-expand/index.ts

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
export {};
2+
3+
// ─── 展开规则类型 ─────────────────────────────────────────────
4+
5+
interface ExpandRule {
6+
/** 匹配的 hostname */
7+
host: string;
8+
/** 需要移除的遮挡元素选择器 */
9+
removeSelectors: string[];
10+
/** 需要还原高度的内容选择器 */
11+
contentSelectors: string[];
12+
/** 需要自动点击的按钮选择器 */
13+
clickSelectors: string[];
14+
/** 额外注入的样式 */
15+
extraStyle?: string;
16+
/** 额外操作 */
17+
extraScript?: () => void;
18+
}
19+
20+
// ─── 展开规则 ─────────────────────────────────────────────────
21+
22+
const EXPAND_RULES: ExpandRule[] = [
23+
// CSDN 博客
24+
{
25+
host: 'blog.csdn.net',
26+
removeSelectors: ['.guide-box', '.wap-shadowbox', '.readall_box', '.btn_open_app_prompt_div'],
27+
contentSelectors: ['.article_content'],
28+
clickSelectors: ['.hide-preCode-bt'],
29+
extraScript() {
30+
// CSDN 容器内的 data-url 链接直接跳转
31+
document.querySelector('.container-fluid')?.addEventListener('click', (e) => {
32+
const target = e.target as HTMLElement;
33+
const url = target.getAttribute('data-url');
34+
if (url) {
35+
window.location.href = url;
36+
e.preventDefault();
37+
}
38+
});
39+
},
40+
},
41+
// CSDN 下载页
42+
{
43+
host: 'download.csdn.net',
44+
removeSelectors: [],
45+
contentSelectors: ['.detail.hidden.no-preview'],
46+
clickSelectors: ["#download-detail .fl[role]"],
47+
},
48+
// CSDN 文库
49+
{
50+
host: 'wenku.csdn.net',
51+
removeSelectors: ['.guide-box', '.wap-shadowbox', '.readall_box', '.btn_open_app_prompt_div'],
52+
contentSelectors: ['.article_content'],
53+
clickSelectors: ['.hide-preCode-bt'],
54+
},
55+
// 百度文库
56+
{
57+
host: 'wenku.baidu.com',
58+
removeSelectors: [],
59+
contentSelectors: [],
60+
clickSelectors: ['.goBtn', '.read-all'],
61+
},
62+
// 思创
63+
{
64+
host: 'ispacesoft.com',
65+
removeSelectors: [],
66+
contentSelectors: [],
67+
clickSelectors: ['.entry-readmore-btn'],
68+
},
69+
];
70+
71+
// ─── 执行展开 ─────────────────────────────────────────────────
72+
73+
function applyRule(rule: ExpandRule): void {
74+
// 移除遮挡元素
75+
for (const sel of rule.removeSelectors) {
76+
document.querySelectorAll(sel).forEach((el) => el.remove());
77+
}
78+
79+
// 点击展开按钮
80+
for (const sel of rule.clickSelectors) {
81+
const btn = document.querySelector(sel) as HTMLElement | null;
82+
if (btn) {
83+
btn.click();
84+
}
85+
}
86+
87+
// 还原内容高度
88+
if (rule.contentSelectors.length > 0) {
89+
const style = document.createElement('style');
90+
const css = rule.contentSelectors.map((s) => `${s} { height: auto !important; max-height: none !important; }`).join('\n');
91+
style.textContent = css;
92+
document.head.appendChild(style);
93+
}
94+
95+
// 注入额外样式
96+
if (rule.extraStyle) {
97+
const style = document.createElement('style');
98+
style.textContent = rule.extraStyle;
99+
document.head.appendChild(style);
100+
}
101+
102+
// 执行额外脚本
103+
rule.extraScript?.();
104+
}
105+
106+
// ─── 主流程 ───────────────────────────────────────────────────
107+
108+
function run(): void {
109+
const hostname = window.location.hostname;
110+
const rule = EXPAND_RULES.find((r) => r.host === hostname);
111+
112+
if (!rule) return;
113+
114+
// 立即执行一次
115+
applyRule(rule);
116+
117+
// 延迟再执行(等待动态内容加载)
118+
const tryAgain = () => {
119+
// 检查是否有需要点击的按钮仍然存在
120+
for (const sel of rule.clickSelectors) {
121+
const btn = document.querySelector(sel) as HTMLElement | null;
122+
if (btn) {
123+
btn.click();
124+
}
125+
}
126+
};
127+
128+
setTimeout(tryAgain, 2000);
129+
setTimeout(tryAgain, 5000);
130+
}
131+
132+
// ─── 百度文库特殊处理 ─────────────────────────────────────────
133+
134+
function baiduWenkuSpecial(): void {
135+
if (!window.location.hostname.includes('wenku.baidu.com')) return;
136+
137+
// 持续尝试点击展开按钮
138+
const tryClick = () => {
139+
const btn = document.querySelector('.goBtn') ?? document.querySelector('.read-all');
140+
if (btn) {
141+
(btn as HTMLElement).click();
142+
}
143+
};
144+
145+
setInterval(tryClick, 2000);
146+
}
147+
148+
// ─── 入口 ─────────────────────────────────────────────────────
149+
150+
run();
151+
baiduWenkuSpecial();

src/auto-expand/meta.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { defineConfig } from '../shared/define';
2+
3+
export default defineConfig({
4+
meta: {
5+
name: {
6+
'': '页面自动展开',
7+
'zh-CN': '页面自动展开',
8+
en: 'Auto Expand — Unfold hidden page content',
9+
},
10+
namespace: 'https://github.com/XiaoLinXiaoZhu/JavaScriptTools',
11+
version: '0.1.0',
12+
description: {
13+
'': '自动展开 CSDN 文章、百度文库、思创等网站被折叠的页面内容,移除展开提示框。',
14+
en: 'Auto-expand folded content on CSDN, Baidu Wenku, SiChuang and more websites.',
15+
},
16+
author: 'XLXZ',
17+
match: [
18+
'*://blog.csdn.net/*',
19+
'*://download.csdn.net/download/*',
20+
'*://wenku.csdn.net/answer/*',
21+
'*://wenku.baidu.com/view/*',
22+
'*://ispacesoft.com/*.html',
23+
],
24+
grant: ['GM_addStyle', 'GM_getValue', 'GM_setValue', 'GM_registerMenuCommand'],
25+
license: 'MIT',
26+
icon: 'https://www.google.com/s2/favicons?domain=csdn.net',
27+
},
28+
category: 'tools',
29+
greasyforkId: undefined,
30+
});

src/csdn-clean/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# CSDN 页面净化
2+
3+
清理 CSDN 博客、下载站、文库的广告,移除代码复制后缀,自动展开被折叠的文章内容。
4+
5+
## 功能
6+
7+
- **广告清理**:移除页面和侧边栏的各类广告
8+
- **剪切板净化**:移除 CSDN 代码复制时的版权后缀
9+
- **评论区优化**:展开被折叠的评论区
10+
- **文章展开**:自动展开被折叠的文章内容
11+
12+
## 使用方式
13+
14+
安装脚本后访问 CSDN 页面自动生效。通过油猴菜单「⚙️ CSDN 净化 — 设置」可以单独开启/关闭各项功能。
15+
16+
{{changelog}}

src/csdn-clean/index.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { showToast } from '@xlxz/components';
2+
3+
export {};
4+
5+
// ─── 配置 ─────────────────────────────────────────────────────
6+
7+
const DEFAULTS: Record<string, boolean> = {
8+
adClean: true,
9+
clipboardClean: true,
10+
commentClean: true,
11+
articleClean: true,
12+
};
13+
14+
function getCfg(key: string): boolean {
15+
const v = GM_getValue('csdn_' + key, undefined);
16+
return v === undefined ? DEFAULTS[key] ?? true : v;
17+
}
18+
19+
function setCfg(key: string, value: boolean): void {
20+
GM_setValue('csdn_' + key, value);
21+
}
22+
23+
// ─── 广告清理 ─────────────────────────────────────────────────
24+
25+
const AD_SELECTORS = [
26+
'#footerRightAds',
27+
'.side-question-box',
28+
"div[id^='dmp_ad']",
29+
"div[class^='ad_']",
30+
"div[id^='floor-ad_']",
31+
'.adsbygoogle',
32+
'#recommendAdBox',
33+
'#asideNewNps',
34+
'.box-shadow',
35+
'.toolbar-advert',
36+
];
37+
38+
function removeAds(): void {
39+
for (const selector of AD_SELECTORS) {
40+
document.querySelectorAll(selector).forEach((el) => el.remove());
41+
}
42+
}
43+
44+
// ─── 剪切板净化 ───────────────────────────────────────────────
45+
46+
function cleanClipboard(): void {
47+
// 移除 CSDN 的复制劫持
48+
try {
49+
const w = window as any;
50+
if (w.csdn?.copyright) {
51+
w.csdn.copyright.textData = '';
52+
}
53+
} catch { /* ignore */ }
54+
55+
// 修复代码块复制按钮
56+
waitFor('.hljs-button', (copyBtn) => {
57+
copyBtn.classList.remove('signin');
58+
copyBtn.setAttribute('data-title', '复制');
59+
copyBtn.setAttribute(
60+
'onclick',
61+
"hljs.copyCode(event);setTimeout(function(){$('.hljs-button').attr('data-title', '复制');},3500);"
62+
);
63+
});
64+
65+
// 修复内联代码复制
66+
waitFor('code', (codeEl) => {
67+
codeEl.setAttribute('onclick', 'mdcp.copyCode(event)');
68+
codeEl.addEventListener('copy', (e) => {
69+
const selection = window.getSelection()?.toString() ?? '';
70+
if (selection) {
71+
e.preventDefault();
72+
navigator.clipboard.writeText(selection).then(
73+
() => showToast('复制成功', { type: 'success', duration: 2000 }),
74+
() => showToast('复制失败,请重试', { type: 'error', duration: 2000 })
75+
);
76+
}
77+
});
78+
});
79+
80+
// 解除 jQuery 的 copy 事件绑定
81+
try {
82+
(window as any).jQuery?.('#content_views').unbind('copy');
83+
} catch { /* ignore */ }
84+
}
85+
86+
// ─── 评论区优化 ───────────────────────────────────────────────
87+
88+
function cleanComment(): void {
89+
const commentList = document.querySelector('.comment-list-box') as HTMLElement | null;
90+
if (commentList) {
91+
commentList.style.overflow = '';
92+
commentList.style.maxHeight = '';
93+
}
94+
document.getElementById('commentPage')?.classList.remove('d-none');
95+
document.getElementById('btnMoreComment')?.remove();
96+
}
97+
98+
// ─── 文章展开 ─────────────────────────────────────────────────
99+
100+
function expandArticle(): void {
101+
const articleContent = document.getElementById('article_content');
102+
if (articleContent) {
103+
articleContent.removeAttribute('style');
104+
}
105+
document.querySelector('.hide-article-box')?.remove();
106+
}
107+
108+
// ─── 工具函数 ─────────────────────────────────────────────────
109+
110+
function waitFor(selector: string, callback: (el: HTMLElement) => void, maxRetries = 10): void {
111+
let retries = 0;
112+
const check = () => {
113+
const el = document.querySelector(selector) as HTMLElement | null;
114+
if (el) {
115+
callback(el);
116+
} else if (retries < maxRetries) {
117+
retries++;
118+
setTimeout(check, 500);
119+
}
120+
};
121+
setTimeout(check, 800);
122+
}
123+
124+
// ─── 菜单 ─────────────────────────────────────────────────────
125+
126+
GM_registerMenuCommand('⚙️ CSDN 净化 — 设置', () => {
127+
const keys = Object.keys(DEFAULTS);
128+
for (const key of keys) {
129+
const current = getCfg(key);
130+
const labels: Record<string, string> = {
131+
adClean: '广告清理',
132+
clipboardClean: '剪切板净化',
133+
commentClean: '评论区优化',
134+
articleClean: '文章展开',
135+
};
136+
const label = labels[key] ?? key;
137+
const input = prompt(`「${label}」当前为【${current ? '开启' : '关闭'}】。\n输入 0 关闭,1 开启,留空保持不变:`, current ? '1' : '0');
138+
if (input === '0') setCfg(key, false);
139+
else if (input === '1') setCfg(key, true);
140+
}
141+
alert('设置已保存,刷新后生效。');
142+
});
143+
144+
// ─── 主流程 ───────────────────────────────────────────────────
145+
146+
// 广告清理:定时执行(应对动态加载)
147+
if (getCfg('adClean')) {
148+
setInterval(removeAds, 3000);
149+
}
150+
151+
// 剪切板净化
152+
if (getCfg('clipboardClean')) {
153+
cleanClipboard();
154+
}
155+
156+
// 评论区优化
157+
if (getCfg('commentClean')) {
158+
setTimeout(cleanComment, 3000);
159+
}
160+
161+
// 文章展开
162+
if (getCfg('articleClean')) {
163+
setTimeout(expandArticle, 1000);
164+
}

0 commit comments

Comments
 (0)