Skip to content

Commit 8690d13

Browse files
committed
新增 [V2ex 增强] 脚本
1 parent ea957f3 commit 8690d13

2 files changed

Lines changed: 375 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
| :----: | :---- | :---- | :----: |
1919
| [<img src="https://static.zhihu.com/heifetz/favicon.ico" height="16px" />](https://www.zhihu.com/people/xiu2) | **知乎 美化** | 宽屏显示、隐藏文章开头大图、调整图片最大高度、隐藏浏览... | **[安装](https://greasyfork.org/zh-CN/scripts/412212)** \| **[备用](https://cdn.jsdelivr.net/gh/XIU2/UserScript@master/Zhihu-Beautification.user.js)** |
2020
| [<img src="https://static.zhihu.com/heifetz/favicon.ico" height="16px" />](https://www.zhihu.com/people/xiu2) | **知乎 增强** | **移除登录弹窗**、一键收起回答、置顶显示时间、区分问题文章... | **[安装](https://greasyfork.org/zh-CN/scripts/419081)** \| **[备用](https://cdn.jsdelivr.net/gh/XIU2/UserScript@master/Zhihu-Enhanced.user.js)** |
21+
| [<img src="https://www.v2ex.com/static/favicon.ico" height="16px" />](https://www.v2ex.com/) | **V2ex 增强** | **自动签到**、自动无缝翻页、回到顶部 (右键点击两侧空白处... | **[安装](https://greasyfork.org/zh-CN/scripts/424246)** \| **[备用](https://cdn.jsdelivr.net/gh/XIU2/UserScript@master/V2ex-Enhanced.user.js)** |
2122
| [<img src="https://i.loli.net/2021/03/30/ULV9XunaHesqGIR.png" height="16px" />](https://github.com/XIU2) | **Github 增强** | **高速下载** Git Clone、Release、Raw、Code(ZIP) 等文件... | **[安装](https://greasyfork.org/zh-CN/scripts/412245)** \| **[备用](https://cdn.jsdelivr.net/gh/XIU2/UserScript@master/GithubEnhanced-High-Speed-Download.user.js)** |
2223
| [<img src="https://i.loli.net/2021/03/30/T85mFlDGnfNWQvp.png" height="16px" />](https://github.com/XIU2) | **自动无缝翻页 \*** | 目前支持:423Down、Apphot、小众软件、PubMed... | **[安装](https://greasyfork.org/zh-CN/scripts/419215)** \| **[备用](https://cdn.jsdelivr.net/gh/XIU2/UserScript@master/Autopage.user.js)** |
2324
| [<img src="https://www.3dmgame.com/favicon.ico" height="16px" />](https://bbs.3dmgame.com) | **3DM论坛 美化** | 精简多余内容、样式优化 | **[安装](https://greasyfork.org/zh-CN/scripts/413593)** \| **[备用](https://cdn.jsdelivr.net/gh/XIU2/UserScript@master/3dm-Beautification.user.js)** |

V2ex-Enhanced.user.js

Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
// ==UserScript==
2+
// @name V2ex 增强
3+
// @version 1.0.0
4+
// @author X.I.U
5+
// @description 自动签到、自动无缝翻页、回到顶部(右键点击两侧空白处)
6+
// @match *://v2ex.com/*
7+
// @match *://*.v2ex.com/*
8+
// @icon https://www.v2ex.com/static/favicon.ico
9+
// @grant GM_xmlhttpRequest
10+
// @grant GM_registerMenuCommand
11+
// @grant GM_unregisterMenuCommand
12+
// @grant GM_openInTab
13+
// @grant GM_getValue
14+
// @grant GM_setValue
15+
// @grant GM_notification
16+
// @license GPL-3.0 License
17+
// @run-at document-end
18+
// @namespace https://github.com/XIU2/UserScript
19+
// ==/UserScript==
20+
21+
(function() {
22+
var menu_ALL = [
23+
['menu_autoClockIn', '自动签到', '自动签到', true],
24+
['menu_pageLoading', '自动无缝翻页', '自动无缝翻页', true],
25+
['menu_pageLoading_reply', '帖子内自动翻页', '帖子内自动翻页', false],
26+
['menu_backToTop', '回到顶部(右键点击两侧空白处)', '回到顶部', true]
27+
], menu_ID = [];
28+
for (let i=0;i<menu_ALL.length;i++){ // 如果读取到的值为 null 就写入默认值
29+
if (GM_getValue(menu_ALL[i][0]) == null){GM_setValue(menu_ALL[i][0], menu_ALL[i][3])};
30+
}
31+
registerMenuCommand();
32+
33+
// 注册脚本菜单
34+
function registerMenuCommand() {
35+
if (menu_ID.length > menu_ALL.length){ // 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
36+
for (let i=0;i<menu_ID.length;i++){
37+
GM_unregisterMenuCommand(menu_ID[i]);
38+
}
39+
}
40+
for (let i=0;i<menu_ALL.length;i++){ // 循环注册脚本菜单
41+
menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
42+
menu_ID[i] = GM_registerMenuCommand(`[ ${menu_ALL[i][3]?'√':'×'} ] ${menu_ALL[i][1]}`, function(){menu_switch(`${menu_ALL[i][3]}`,`${menu_ALL[i][0]}`,`${menu_ALL[i][2]}`)});
43+
}
44+
menu_ID[menu_ID.length] = GM_registerMenuCommand('反馈 & 建议', function () {window.GM_openInTab('https://github.com/XIU2/UserScript#xiu2userscript', {active: true,insert: true,setParent: true});});
45+
}
46+
47+
// 菜单开关
48+
function menu_switch(menu_status, Name, Tips) {
49+
if (menu_status == 'true'){
50+
GM_setValue(`${Name}`, false);
51+
GM_notification({text: `已关闭 [${Tips}] 功能\n(刷新网页后生效)`, title: '吾爱破解论坛增强', timeout: 3000});
52+
}else{
53+
GM_setValue(`${Name}`, true);
54+
GM_notification({text: `已开启 [${Tips}] 功能\n(刷新网页后生效)`, title: '吾爱破解论坛增强', timeout: 3000});
55+
}
56+
registerMenuCommand(); // 重新注册脚本菜单
57+
};
58+
59+
// 返回菜单值
60+
function menu_value(menuName) {
61+
for (let menu of menu_ALL) {
62+
if (menu[0] == menuName) {
63+
return menu[3]
64+
}
65+
}
66+
}
67+
68+
69+
// 默认 ID 为 0
70+
var curSite = {SiteTypeID: 0};
71+
72+
// 自动翻页规则,HT_insert:1 = 插入该元素的前面;2 = 插入该元素内的最后面
73+
let DBSite = {
74+
recent: { // 最近主题页
75+
SiteTypeID: 1,
76+
pager: {
77+
type: 1,
78+
nextLink: '//a[@class="page_current"]/following-sibling::a[1][@href]',
79+
pageElement: 'css;.cell.item',
80+
HT_insert: ['//*[@id="Main"]//div[@class="box"]//div[@class="cell"][last()]', 1],
81+
replaceE: 'css;#Main .box .cell:not(.item) > table',
82+
scrollDelta: 600
83+
}
84+
},
85+
notifications: { // 提醒消息页
86+
SiteTypeID: 2,
87+
pager: {
88+
type: 1,
89+
nextLink: '//a[@class="page_current"]/following-sibling::a[1][@href]',
90+
pageElement: 'css;#notifications > div',
91+
HT_insert: ['css;#notifications', 2],
92+
replaceE: 'css;#Main .box > .cell:not(.item) > table',
93+
scrollDelta: 600
94+
}
95+
},
96+
replies: { // 用户回复页
97+
SiteTypeID: 3,
98+
pager: {
99+
type: 1,
100+
nextLink: '//a[@class="page_current"]/following-sibling::a[1][@href]',
101+
pageElement: '//*[@id="Main"]//div[@class="box"]//div[@class="dock_area"] | //*[@id="Main"]//div[@class="box"]//div[@class="inner"] | //*[@id="Main"]//div[@class="box"]//div[@class="dock_area"][last()]/following-sibling::div[@class="cell"][1]',
102+
HT_insert: ['//*[@id="Main"]//div[@class="box"]//div[@class="cell"][last()]', 1],
103+
replaceE: 'css;#Main .box .cell:not(.item) > table',
104+
scrollDelta: 1000
105+
}
106+
},
107+
go: { // 分类主题页
108+
SiteTypeID: 4,
109+
pager: {
110+
type: 1,
111+
nextLink: '//a[@class="page_current"]/following-sibling::a[1][@href]',
112+
pageElement: 'css;#TopicsNode > div',
113+
HT_insert: ['css;#TopicsNode', 2],
114+
replaceE: 'css;#Main .box > .cell:not(.item) > table',
115+
scrollDelta: 700
116+
}
117+
},
118+
reply: { // 帖子内容页
119+
SiteTypeID: 5,
120+
pager: {
121+
type: 1,
122+
nextLink: '//a[@class="page_current"]/preceding-sibling::a[1][@href]',
123+
pageElement: 'css;.cell[id^="r_"]',
124+
HT_insert: ['//div[starts-with(@id, "r_")][last()]/following-sibling::div[@class="cell"][1]', 1],
125+
replaceE: 'css;#Main .box > .cell:not(.normalUser) > table',
126+
scrollDelta: 700
127+
}
128+
},
129+
reply_positive: { // 帖子内容页(正序)
130+
SiteTypeID: 6,
131+
pager: {
132+
type: 1,
133+
nextLink: '//a[@class="page_current"]/preceding-sibling::a[1][@href]',
134+
pageElement: 'css;.cell[id^="r_"]',
135+
HT_insert: ['//div[starts-with(@id, "r_")][position()=1]', 1],
136+
replaceE: 'css;#Main .box > .cell:not(.normalUser) > table',
137+
scrollDelta: 700
138+
}
139+
}
140+
};
141+
142+
143+
switch (location.pathname) {
144+
case "/recent": // 最近主题页
145+
curSite = DBSite.recent;
146+
break;
147+
case "/notifications": // 提醒消息页
148+
curSite = DBSite.notifications;
149+
break;
150+
case "/member/XIU2/replies": // 用户回复页
151+
curSite = DBSite.replies;
152+
break;
153+
default:
154+
if (location.pathname.indexOf('/go/') > -1) { // 分类主题页
155+
curSite = DBSite.go;
156+
} else if (location.pathname.indexOf('/t/') > -1) { // 帖子内容页
157+
if(menu_value('menu_pageLoading_reply'))curSite = DBSite.reply_positive;
158+
}
159+
}
160+
161+
curSite.pageUrl = ""; // 下一页URL
162+
if(menu_value('menu_autoClockIn'))setTimeout(qianDao, 1000) // 自动签到(后台),延迟 1 秒执行是为了兼容 [V2ex Plus] 扩展
163+
if(menu_value('menu_pageLoading'))pageLoading // 自动翻页(无缝)
164+
if(menu_value('menu_backToTop'))backToTop(); // 回到顶部(右键点击空白处)
165+
166+
167+
// 自动签到(后台)
168+
function qianDao() {
169+
let qiandao = document.querySelector('.box .inner a[href="/mission/daily"]');
170+
if (qiandao) {
171+
let url = (location.origin + "/mission/daily/redeem?" + RegExp("once\\=(\\d+)").exec(document.querySelector('div#Top .tools').innerHTML)[0]);
172+
GM_xmlhttpRequest({
173+
url: url,
174+
method: "GET",
175+
timeout: 5000,
176+
onload: function (response) {
177+
let html = ShowPager.createDocumentByString(response.responseText);
178+
console.log(html)
179+
if (html.querySelector('li.fa.fa-ok-sign')) {
180+
html = html.getElementById('Main').innerText.match(/ (\d+?) /)[0];
181+
qiandao.innerText = `自动签到成功!${html}`;
182+
qiandao.href = '#';
183+
} else {
184+
GM_notification({text: '自动签到失败!请联系作者解决!', timeout: 4000});
185+
}
186+
}
187+
});
188+
} else if (document.getElementById('gift_v2excellent')) { // 兼容 [V2ex Plus] 扩展
189+
document.getElementById('gift_v2excellent').click();
190+
}
191+
}
192+
193+
194+
// 回到顶部(右键点击空白处)
195+
function backToTop() {
196+
document.getElementById("Wrapper").oncontextmenu = function(event){
197+
if (event.target==this) {
198+
event.preventDefault();
199+
window.scrollTo(0,0)
200+
}
201+
}
202+
/*let backToTop = `<div style="position: fixed;bottom: 0;right: 0;opacity: 0.1;"><div style="transition: height .2s ease;"><button type="button" style="display: flex;align-items: center;justify-content: center;background: #fff;border-radius: 3px;width: 200px;height: 200px;color: #8590a6;box-shadow: 0 1px 3px rgba(18,18,18,.1);border: none;cursor: pointer;outline: none;" title="回到顶部" onclick="window.scrollTo(0,0)"><svg fill="currentColor" viewBox="0 0 24 24" width="24" height="24"><path d="M16.036 19.59a1 1 0 0 1-.997.995H9.032a.996.996 0 0 1-.997-.996v-7.005H5.03c-1.1 0-1.36-.633-.578-1.416L11.33 4.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.005z"></path></svg></button></div></div>`
203+
let backToTop_add = document.createElement('div');
204+
backToTop_add.innerHTML = `${backToTop}`;
205+
document.body.appendChild(backToTop_add);*/
206+
}
207+
208+
209+
// 自动无缝翻页
210+
function pageLoading() {
211+
if (curSite.SiteTypeID > 0){
212+
windowScroll(function (direction, e) {
213+
if (direction === "down") { // 下滑才准备翻页
214+
let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
215+
//console.log(document.documentElement.scrollHeight)
216+
let scrollDelta = curSite.pager.scrollDelta;
217+
if (document.documentElement.scrollHeight <= document.documentElement.clientHeight + scrollTop + scrollDelta) {
218+
if (curSite.pager.type === 1) {
219+
ShowPager.loadMorePage();
220+
}else{
221+
let autopbn = document.querySelector(curSite.pager.nextLink);
222+
if (autopbn){
223+
autopbn.click();
224+
}
225+
}
226+
}
227+
}
228+
});
229+
}
230+
}
231+
232+
233+
// 滚动条事件
234+
function windowScroll(fn1) {
235+
var beforeScrollTop = document.documentElement.scrollTop,
236+
fn = fn1 || function () {};
237+
setTimeout(function () { // 延时执行,避免刚载入到页面就触发翻页事件
238+
window.addEventListener("scroll", function (e) {
239+
var afterScrollTop = document.documentElement.scrollTop,
240+
delta = afterScrollTop - beforeScrollTop;
241+
if (delta == 0) return false;
242+
fn(delta > 0 ? "down" : "up", e);
243+
beforeScrollTop = afterScrollTop;
244+
}, false);
245+
}, 1000)
246+
}
247+
248+
249+
var ShowPager = { // 修改自 https://greasyfork.org/scripts/14178
250+
getFullHref: function (e) {
251+
if(e == null) return '';
252+
"string" != typeof e && (e = e.getAttribute("href"));
253+
var t = this.getFullHref.a;
254+
return t || (this.getFullHref.a = t = document.createElement("a")), t.href = e, t.href;
255+
},
256+
createDocumentByString: function (e) {
257+
if (e) {
258+
if ("HTML" !== document.documentElement.nodeName) return (new DOMParser).parseFromString(e, "application/xhtml+xml");
259+
var t;
260+
try {
261+
t = (new DOMParser).parseFromString(e, "text/html");
262+
} catch (e) {
263+
}
264+
if (t) return t;
265+
if (document.implementation.createHTMLDocument) t = document.implementation.createHTMLDocument("ADocument"); else try {
266+
(t = document.cloneNode(!1)).appendChild(t.importNode(document.documentElement, !1)),
267+
t.documentElement.appendChild(t.createElement("head")), t.documentElement.appendChild(t.createElement("body"));
268+
} catch (e) {
269+
}
270+
if (t) {
271+
var r = document.createRange();
272+
r.selectNodeContents(document.body);
273+
var n = r.createContextualFragment(e);
274+
t.body.appendChild(n);
275+
for (var a, o = {
276+
TITLE: !0,
277+
META: !0,
278+
LINK: !0,
279+
STYLE: !0,
280+
BASE: !0
281+
}, i = t.body, s = i.childNodes, c = s.length - 1; c >= 0; c--) o[(a = s[c]).nodeName] && i.removeChild(a);
282+
return t;
283+
}
284+
} else console.error("没有找到要转成DOM的字符串");
285+
},
286+
loadMorePage: function () {
287+
if (curSite.pager) {
288+
let curPageEle = getElementByXpath(curSite.pager.nextLink);
289+
var url = this.getFullHref(curPageEle);
290+
//console.log(`${url} ${curPageEle} ${curSite.pageUrl}`);
291+
if(url === '') return;
292+
if(curSite.pageUrl === url) return;// 不会重复加载相同的页面
293+
curSite.pageUrl = url;
294+
// 读取下一页的数据
295+
curSite.pager.startFilter && curSite.pager.startFilter();
296+
GM_xmlhttpRequest({
297+
url: url,
298+
method: "GET",
299+
timeout: 5000,
300+
onload: function (response) {
301+
try {
302+
var newBody = ShowPager.createDocumentByString(response.responseText);
303+
let pageElems = getAllElements(curSite.pager.pageElement, newBody, newBody);
304+
let toElement = getAllElements(curSite.pager.HT_insert[0])[0];
305+
if (pageElems.length >= 0) {
306+
let addTo = "beforeend";
307+
if (curSite.pager.HT_insert[1] == 1) addTo = "beforebegin";
308+
// 插入新页面元素
309+
pageElems.forEach(function (one) {
310+
toElement.insertAdjacentElement(addTo, one);
311+
});
312+
// 替换待替换元素
313+
try {
314+
let oriE = getAllElements(curSite.pager.replaceE);
315+
let repE = getAllElements(curSite.pager.replaceE, newBody, newBody);
316+
if (oriE.length === repE.length) {
317+
for (var i = 0; i < oriE.length; i++) {
318+
oriE[i].outerHTML = repE[i].outerHTML;
319+
}
320+
}
321+
} catch (e) {
322+
console.log(e);
323+
}
324+
}
325+
} catch (e) {
326+
console.log(e);
327+
}
328+
}
329+
});
330+
}
331+
},
332+
};
333+
334+
335+
function getElementByXpath(e, t, r) {
336+
r = r || document, t = t || r;
337+
try {
338+
return r.evaluate(e, t, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
339+
} catch (t) {
340+
return void console.error("无效的xpath");
341+
}
342+
}
343+
344+
345+
function getAllElements(e, t, r, n, o) {
346+
let getAllElementsByXpath = function(e, t, r) {
347+
return r = r || document, t = t || r, r.evaluate(e, t, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
348+
}
349+
350+
var i, s = [];
351+
if (!e) return s;
352+
if (r = r || document, n = n || window, o = o || void 0, t = t || r, "string" == typeof e) i = 0 === e.search(/^css;/i) ? function getAllElementsByCSS(e, t) {
353+
return (t || document).querySelectorAll(e);
354+
}(e.slice(4), t) : getAllElementsByXpath(e, t, r); else {
355+
if (!(i = e(r, n, o))) return s;
356+
if (i.nodeType) return s[0] = i, s;
357+
}
358+
return function makeArray(e) {
359+
var t, r, n, o = [];
360+
if (e.pop) {
361+
for (t = 0, r = e.length; t < r; t++) (n = e[t]) && (n.nodeType ? o.push(n) : o = o.concat(makeArray(n)));
362+
return a()(o);
363+
}
364+
if (e.item) {
365+
for (t = e.length; t;) o[--t] = e[t];
366+
return o;
367+
}
368+
if (e.iterateNext) {
369+
for (t = e.snapshotLength; t;) o[--t] = e.snapshotItem(t);
370+
return o;
371+
}
372+
}(i);
373+
}
374+
})();

0 commit comments

Comments
 (0)