// ==UserScript==
// @name zhihu optimizer
// @namespace https://github.com/Kyouichirou
// @version 3.0.1.6
// @description make zhihu clean and tidy, for better experience
// @author HLA
// @run-at document-start
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant unsafeWindow
// @grant GM_xmlhttpRequest
// @connect www.zhihu.com
// @grant GM_unregisterMenuCommand
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// @grant GM_registerMenuCommand
// @grant GM_notification
// @grant GM_openInTab
// @grant GM_getTab
// @grant GM_getTabs
// @grant GM_saveTab
// @grant window.onurlchange
// @grant window.close
// @match https://*.zhihu.com/*
// @compatible chrome 80+; test on chrome 64(x86), some features don't work
// @license MIT
// @noframes
// @note more spam users of zhihu, https://zhuanlan.zhihu.com/p/127021293, it is recommended to block all of these users.
/* !*******************zhihu*******************************!
www.zhihu.com/api/v4/creator/read_count_statistics*
www.zhihu.com/api/v4/me?include=ad_type*
www.zhihu.com/api/v4/search/top_search
www.zhihu.com/zbst/events/r
www.zhihu.com/api/v4/me/switches?include=is_creator
www.zhihu.com/api/v4/commercial/ecommerce
www.zhihu.com/api/v4/search/preset_words
!*******************zhihu*******************************!*/
//note add these rules to ublock or adblock => ensure the input box clear
// ==/UserScript==
(() => {
"use strict";
const blackKey = ["留学中介", "肖战"];
let blackName = null;
let blackTopicAndQuestion = null;
const Notification = (content = "", title = "", duration = 2500, func) => {
GM_notification({
text: content,
title: title,
timeout: duration,
onclick: func,
});
};
const installTips = () => {
//first time run, open the usermanual webpage
if (GM_getValue("initial")) return;
const usermanual =
"https://github.com/Kyouichirou/D7E1293/blob/main/Tmapermonkey/zhihu_optimizer_manual.md";
GM_setValue("initial", true);
Notification(
"thanks for installing, please read user manual carefully",
"Tips",
6000
);
GM_openInTab(usermanual, { insert: true });
};
const mergeArray = (origin, target) => {
origin = origin.concat(target);
const newArr = [];
const tmpObj = {};
for (const e of origin) {
if (!tmpObj[e]) {
newArr.push(e);
tmpObj[e] = 1;
}
}
return newArr;
};
const getSelection = () => {
const select = window.getSelection();
return select ? select.toString().trim() : null;
};
const escapeBlank = (target) => {
const type = typeof target;
if (type === "object") {
const entries = Object.entries(target);
for (const [key, value] of entries)
target[key] = value.replace(/\s/g, " ");
} else {
target = target.replace(/\s/g, " ");
}
return target;
};
const escapeHTML = (s) => {
const reg = /“|&|’|<|>|[\x00-\x20]|[\x7F-\xFF]|[\u0100-\u2700]/g;
return typeof s !== "string"
? s
: s.replace(reg, ($0) => {
let c = $0.charCodeAt(0),
r = ["&#"];
c = c == 0x20 ? 0xa0 : c;
r.push(c);
r.push(";");
return r.join("");
});
};
//cut out part of title with specified length, take care of chinese character & english character
const titleSlice = (str) => {
let length = 0;
let newstr = "";
for (const e of str) {
length += e.charCodeAt(0).toString(16).length === 4 ? 2 : 1;
newstr += e;
if (length > 27) return `${newstr}...`;
}
return newstr;
};
const createButton = (name, title, otherButton = "", position = "left") => {
title = escapeBlank(title);
const html = `
<div
id = "assist-button-container"
>
<style>
button.assist-button {
border-radius: 0 1px 1px 0;
border: rgb(247, 232, 176) solid 1.2px;
display: inline-block;
margin-top: 4px;
font-size: 14px;
height: 28px;
width: 75px;
box-shadow: 3px 4px 1px #888888;
justify-content: center;
}
div#assist-button-container {
opacity: 0.15;
${position}: 4%;
width: 60px;
flex-direction: column;
position: fixed;
bottom: 7%;
}
div#assist-button-container:hover {
opacity: 1;
transition: opacity 2s;
}
</style>
${otherButton}
<button class="assist-button block" style="color: black;" title=${title}>${name}</button>
</div>`;
document.documentElement.insertAdjacentHTML("beforeend", html);
};
const xmlHTTPRequest = (url, time = 2500, rType = false) => {
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
url: url,
timeout: time,
onload: (response) => {
if (response.status == 200) {
if (rType) {
//after redirect, get the final URL
resolve(response.finalUrl);
} else {
resolve(response.response);
}
} else {
console.log(`err: code ${response.status}`);
reject("request data error");
}
},
onerror: (e) => {
console.log(e);
reject("something error");
},
ontimeout: (e) => {
console.log(e);
reject("timeout error");
},
});
});
};
const rndRangeNum = (start, end, count) => {
if (end < 0 || start < 0) return null;
if (end < start || end - start + 1 < count) return null;
const tmpArr = [];
const rndArr = [];
end++;
for (let i = start; i < end; i++) tmpArr.push(i);
for (; count > 0; count--) {
const ir = tmpArr.length - 1;
const rnd = Math.floor(Math.random() * ir);
rndArr.push(tmpArr[rnd]);
tmpArr[rnd] = tmpArr[ir];
tmpArr.pop();
}
return rndArr;
};
class Database {
/*
dname: name of database;
tname: name of table;
mode: read or read&write;
*/
constructor(dbname, tbname = "", rwmode = false, version = 1) {
this.dbopen =
version === 1
? indexedDB.open(dbname)
: indexedDB.open(dbname, version);
this.RWmode = rwmode ? "readwrite" : "readonly";
this.tbname = tbname;
const getIndex = (fieldname) => this.table.index(fieldname);
}
Initialize() {
return new Promise((resolve, reject) => {
//if the db does not exist, this event will fired firstly;
//adjust the version of db, which can trigger this event => create/delete table or create/delete index (must lauch from this event);
this.dbopen.onupgradeneeded = (e) => {
this.store = e.target.result;
this.updateEvent = true;
this.storeEvent();
resolve(0);
};
this.dbopen.onsuccess = () => {
if (this.store) return;
this.updateEvent = false;
this.store = this.dbopen.result;
this.storeEvent();
resolve(1);
};
this.dbopen.onerror = (e) => {
console.log(e);
reject("error");
};
/*
The event handler for the blocked event.
This event is triggered when the upgradeneeded event should be triggered _
because of a version change but the database is still in use (i.e. not closed) somewhere,
even after the versionchange event was sent.
*/
this.dbopen.onblocked = () => {
console.log("please close others tab to update database");
reject("conflict");
};
this.dbopen.onversionchange = (e) =>
console.log("The version of this database has changed");
});
}
createTable(keyPath) {
if (this.updateEvent) {
const index = keyPath
? { keyPath: keyPath }
: { autoIncrement: true };
this.store.createObjectStore(this.tbname, index);
}
}
createNewTable(keyPath) {
return new Promise((resolve, reject) => {
this.version = this.store.version + 1;
this.store.close();
if (this.storeErr) {
reject("database generates some unknow error");
return;
}
this.dbopen = indexedDB.open(this.store.name, this.version);
this.Initialize().then(
() => {
this.createTable(keyPath);
resolve(true);
},
() => reject("database initial fail")
);
});
}
storeEvent() {
this.store.onclose = () => {
console.log("closing...");
};
this.store.onerror = () => (this.storeErr = true);
}
get checkTable() {
return this.store.objectStoreNames.contains(this.tbname);
}
get Tablenames() {
return this.store.objectStoreNames;
}
get Indexnames() {
return this.table.indexNames;
}
get DBversion() {
return this.store.version;
}
getTablecount(key) {
return this.table.count(key);
}
//take care the transaction, must make sure the transaction is alive when you need deal with something continually
openTable() {
this.isfinish = false;
this.transaction = this.store.transaction(
[this.tbname],
this.RWmode
);
this.table = this.transaction.objectStore(this.tbname);
this.transaction.oncomplete = () => (this.isfinish = true);
}
get Table() {
this.transaction = this.store.transaction(
[this.tbname],
this.RWmode
);
return this.transaction.objectStore(this.tbname);
}
rollback() {
this.transaction && this.transaction.abort();
}
read(keyPath) {
return new Promise((resolve, reject) => {
if (!this.table || this.isfinish) this.openTable();
const request = this.table.get(keyPath);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject("error");
});
}
batchCheck(tables, keyPath) {
return new Promise((resolve) => {
const arr = [];
for (const t of tables) {
const transaction = this.store.transaction(
this.store.objectStoreNames,
this.RWmode
);
const table = transaction.objectStore(t);
const rq = new Promise((reso, rej) => {
const req = table.get(keyPath);
req.onsuccess = () => reso(req.result);
req.onerror = () => rej("error");
});
arr.push(rq);
}
Promise.allSettled(arr).then((results) => {
resolve(
results.map((r) =>
r.status === "rejected" ? null : r.value
)
);
});
});
}
add(info) {
return new Promise((resolve, reject) => {
if (!this.table || this.isfinish) this.openTable();
const op = this.table.add(info);
op.onsuccess = () => resolve(true);
op.onerror = (e) => {
console.log(e);
reject("error");
};
});
}
updateRecord(keyPath) {
return new Promise((resolve, reject) => {
this.read(keyPath).then(
(result) => {
if (result) {
result.visitTime = Date.now();
let times = result.visitTimes;
result.visitTimes = times ? ++times : 2;
this.update(result).then(
() => resolve(true),
(err) => reject(err)
);
} else resolve(true);
},
(err) => reject(err)
);
});
}
update(info, keyPath, mode = false) {
//if db has contained the item, will update the info; if it does not, a new item is added
return new Promise((resolve, reject) => {
if (!this.table || this.isfinish) this.openTable();
//keep cursor
if (mode) {
this.read(info[keyPath]).then(
(result) => {
if (!result) {
this.add(info).then(
() => resolve(true),
(err) => reject(err)
);
} else {
const op = this.table.put(
Object.assign(result, info)
);
op.onsuccess = () => resolve(true);
op.onerror = (e) => {
console.log(e);
reject("error");
};
}
},
(err) => console.log(err)
);
} else {
const op = this.table.put(info);
op.onsuccess = () => resolve(true);
op.onerror = (e) => {
console.log(e);
reject("error");
};
}
});
}
clear() {
if (!this.table) this.openTable();
this.table.clear();
}
//must have primary key
deleteiTems(keyPath) {
return new Promise((resolve, reject) => {
if (!this.table || this.isfinish) this.openTable();
const op = this.table.delete(keyPath);
op.onsuccess = () => {
console.log("delete item successfully");
resolve(true);
};
op.onerror = (e) => {
console.log(e);
reject("error");
};
});
}
//note: create a index must lauch from onupgradeneeded event;
createIndex(indexName, keyPath, objectParameters) {
if (!this.updateEvent) {
console.log("this function must be through onupgradeneeded");
return;
}
this.table.createIndex(indexName, keyPath, objectParameters);
}
deleTable() {
if (!this.updateEvent) {
console.log("this function must be through onupgradeneeded");
return;
}
this.dbopen.deleteObjectStore(this.tbname);
}
deleIndex(indexName) {
if (!this.updateEvent) {
console.log("this function must be through onupgradeneeded");
return;
}
this.table.deleteIndex(indexName);
}
close() {
//The connection is not actually closed until all transactions created using this connection are complete
this.store.close();
}
static deleDB(dbname) {
return new Promise((resolve, reject) => {
const DBDeleteRequest = window.indexedDB.deleteDatabase(dbname);
DBDeleteRequest.onerror = (e) => {
console.log(e);
reject("error");
};
DBDeleteRequest.onsuccess = () => {
console.log("success");
resolve(true);
};
});
}
}
const dataBaseInstance = {
db: null,
additem(columnID) {
const info = {};
info.pid = this.pid;
info.update = Date.now();
info.excerpt = "";
info.visitTimes = 1;
info.visitTime = info.update;
const contentholder = document.getElementsByClassName(
"RichText ztext Post-RichText"
);
if (contentholder.length > 0) {
const chs = contentholder[0].childNodes;
let excerpt = "";
//take some data as this article's digest
let ic = 0;
for (const node of chs) {
if (node.localName === "p") {
excerpt += node.innerText;
if (excerpt.length > 300) {
excerpt = excerpt.slice(0, 300);
break;
}
}
ic++;
if (ic > 5) break;
}
info.excerpt = excerpt;
}
info.userName = "";
info.userID = "";
const user = document.getElementsByClassName(
"UserLink AuthorInfo-name"
);
if (user.length > 0) {
const link = user[0].getElementsByTagName("a");
if (link.length > 0) {
const p = link[0].pathname;
info.userID = p.slice(p.lastIndexOf("/") + 1);
info.userName = link[0].text;
}
}
info.ColumnID = columnID || "";
const defaultV = "A B";
const p = prompt(
"please input some tags about this article, like: javascript python; multiple tags use blank space to isolate",
defaultV
);
let tags = [];
if (p && p !== defaultV && p.trim()) {
const tmp = p.split(" ");
for (let e of tmp) {
e = e.trim();
e && tags.push(e);
}
}
const note = prompt(
"you can input something to hightlight this article, eg: this article is about advantage python usage"
);
info.tags = tags;
const title = document.title;
info.note = note || "";
info.title = title.slice(0, title.length - 5);
this.db.update(info, "pid", false).then(
() =>
Notification(
"add this article to collection successfully",
"Tips"
),
() =>
Notification(
"add this aritcle to collection fail",
"Warning"
)
);
},
fold(info) {
this.db.update(info, "name", true).then(
() => console.log("this answer has been folded"),
() => console.log("add this anser to folded list fail")
);
},
get Table() {
return this.db.Table;
},
batchCheck(tableNames) {
const pid = this.pid;
return new Promise((resolve) =>
this.db
.batchCheck(tableNames, pid)
.then((results) => resolve(results))
);
},
updateRecord(keyPath) {
const pid = keyPath || this.pid;
this.db.updateRecord(pid).then(
() => console.log("update record finished"),
(err) => console.log(err)
);
},
check(keyPath) {
const pid = keyPath || this.pid;
return new Promise((resolve, reject) => {
this.db.read(pid).then(
(result) => resolve(result),
(err) => reject(err)
);
});
},
get pid() {
return location.pathname.slice(3);
},
dele(mode, keyPath) {
const pid = keyPath || this.pid;
this.db.deleteiTems(pid).then(
() =>
mode &&
Notification(
`the article of ${pid} has been deleted from collection successfully`,
"Tips"
),
() => mode && Notification("delete article fail")
);
},
update(info) {
this.db.update(info);
},
close(mode = true) {
mode && this.db.close();
this.db = null;
},
initial(tableNames, mode = false, keyPath = "pid") {
return new Promise((resolve, reject) => {
if (!Array.isArray(tableNames)) {
reject("this parameter must be array");
return;
}
const dbname = "zhihuDatabase";
const db = new Database(
dbname,
tableNames.length === 1 ? tableNames[0] : "",
mode
);
this.db = db;
db.Initialize().then(
(result) => {
if (result === 0) {
for (const table of tableNames) {
db.tbname = table;
db.createTable(keyPath);
}
}
resolve(result);
},
(err) => reject(err)
);
});
},
};
const zhihu = {
getData() {
blackName = GM_getValue("blackname");
(!blackName || !Array.isArray(blackName)) && (blackName = []);
blackTopicAndQuestion = GM_getValue("blacktopicAndquestion");
(!blackTopicAndQuestion || !Array.isArray(blackTopicAndQuestion)) &&
(blackTopicAndQuestion = []);
},
clipboardClear: {
clear(text) {
const cs = [
/。/g,
/:/g,
/;/g,
/?/g,
/!/g,
/(/g,
/)/g,
/“/g,
/”/g,
/、/g,
/,/g,
/《/g,
/》/g,
];
const es = [
". ",
": ",
"; ",
"? ",
"! ",
"(",
")",
'"',
'"',
", ",
", ",
"<",
">",
];
cs.forEach((s, i) => (text = text.replace(s, es[i])));
this.write(text);
},
write(text) {
window.navigator.clipboard.writeText(text);
},
event() {
document.oncopy = (e) => {
e.preventDefault();
e.stopImmediatePropagation();
let copytext = getSelection();
if (!copytext) return;
this.clear(copytext);
};
},
},
turnPage: {
main(mode) {
const overlap = 100;
const wh = window.innerHeight;
let height = wh - overlap;
height < 0 && (height = 0);
let top =
document.documentElement.scrollTop ||
document.body.scrollTop ||
window.pageYOffset;
if (mode) top += height;
else top < height ? (top = 0) : (top -= height);
window.scrollTo(0, top);
},
start(mode) {
//n => scroll down ; u => scroll top
window.requestAnimationFrame(this.main.bind(this, mode));
},
},
scroll: {
toTop() {
let hTop =
document.documentElement.scrollTop ||
document.body.scrollTop;
if (hTop === 0) return;
const rate = 8;
let sid = 0;
const scrollToTop = () => {
hTop =
document.documentElement.scrollTop ||
document.body.scrollTop;
if (hTop > 0) {
sid = window.requestAnimationFrame(scrollToTop);
window.scrollTo(0, hTop - hTop / rate);
} else {
sid !== 0 && window.cancelAnimationFrame(sid);
}
};
scrollToTop();
},
toBottom() {
//take care this, if the webpage adopts waterfall flow design
const height =
document.documentElement.scrollHeight ||
document.body.scrollHeight;
const sTop =
document.documentElement.scrollTop ||
document.body.scrollTop;
if (sTop >= height) return;
let sid = 0;
let shTop = 0;
let rate = 6;
const initial = 100;
const scrollToBottom = () => {
const hTop =
document.documentElement.scrollTop ||
document.body.scrollTop ||
initial;
if (hTop < height && hTop > shTop) {
shTop = hTop;
sid = window.requestAnimationFrame(scrollToBottom);
window.scrollTo(0, hTop + hTop / rate);
rate += 0.2;
} else {
sid !== 0 && window.cancelAnimationFrame(sid);
sid = 0;
rate = 6;
}
};
scrollToBottom();
},
},
multiSearch(keyCode) {
const Names = {
65: "AboutMe",
68: "Douban",
71: "Google",
72: "Github",
77: "MDN",
66: "BiliBili",
90: "Zhihu",
};
const methods = {
Protocols: "https://",
string_length(str) {
const lg = [...str].reduce(
(length, e) =>
(length +=
e.charCodeAt(0).toString(16).length === 4
? 2
: 1),
0
);
return Math.floor(lg / 2);
},
Search(url, parameter = "") {
const select = getSelection();
//baidu restrict the length of search keyword is 38;
if (!select || this.string_length(select) > 38) return;
url += encodeURIComponent(select);
window.open(this.Protocols + url + parameter, "_blank");
},
Google() {
this.Search("www.dogedoge.com/results?q=");
},
Douban() {
this.Search("www.douban.com/search?q=");
},
Zhihu() {
this.Search("www.zhihu.com/search?q=", "&type=content");
},
MDN() {
this.Search("developer.mozilla.org/zh-CN/search?q=");
},
Github() {
this.Search("github.com/search?q=");
},
BiliBili() {
this.Search("search.bilibili.com/all?keyword=");
},
AboutMe() {
zhihu.shade.Support.main();
},
};
const name = Names[keyCode];
name && methods[name]();
},
noteHightlight: {
editable: false,
disableSiderbar(pevent) {
const column = document.getElementById("column_lists");
if (column) column.style.pointerEvents = pevent;
},
EditDoc() {
const [edit, tips, pevent] = this.editable
? ["inherit", "exit", "inherit"]
: ["true", "enter", "none"];
document.body.contentEditable = edit;
Notification(tips + " page editable mode", "Editable");
this.disableSiderbar(pevent);
this.editable = !this.editable;
},
get Selection() {
return window.getSelection();
},
setMark(text, type) {
return `<mark class="AssistantMark ${type}">${text}</mark>`;
},
get createElement() {
return document.createElement("markspan");
},
appendNewNode(node, type) {
const text = node.nodeValue;
const span = this.createElement;
node.parentNode.replaceChild(span, node);
span.outerHTML = this.setMark(text, type);
},
getTextNode(node, type) {
node.nodeType === 3 && this.appendNewNode(node, type);
},
Marker(keyCode) {
const cname = {
82: "red",
89: "yellow",
80: "purple",
71: "green",
};
const type = cname[keyCode];
if (!type) return;
const select = this.Selection;
if (!select.anchorNode || select.isCollapsed) return;
/*
const colors = {
red: "rgb(255, 128, 128)",
green: "rgb(170, 255, 170)",
yellow: "rgb(255, 255, 170)",
purple: "rgb(255, 170, 255)",
};
const color = colors[type];
*/
let i = select.rangeCount;
const r = select.getRangeAt(--i);
let start = r.startContainer;
const end = r.endContainer;
const offs = r.startOffset;
const offe = r.endOffset;
let nodeValue = r.startContainer.nodeValue;
if (start !== end) {
//start part
let next = start.nextSibling;
let p = start.parentNode;
if (!p.className.startsWith("AssistantMark")) {
const text = nodeValue.slice(offs);
const span = this.createElement;
p.replaceChild(span, start);
span.outerHTML =
nodeValue.slice(0, offs) + this.setMark(text, type);
}
//mid part
while (true) {
if (next) {
start = next;
} else {
next = p.nextSibling;
while (!next) {
p = p.parentNode;
next = p.nextSibling;
}
start = next;
}
//get the deepest level node
while (start.childNodes.length > 0)
start = start.childNodes[0];
if (start === end) break;
p = start.parentNode;
next = p.nextSibling;
!p.className.startsWith("AssistantMark") &&
this.getTextNode(start, type);
}
//end part
nodeValue = start.nodeValue;
start = start.parentNode;
if (start.className.startsWith("AssistantMark")) return;
const text = nodeValue.slice(0, offe);
const epan = this.createElement;
start.replaceChild(epan, end);
epan.outerHTML =
this.setMark(text, type) + nodeValue.slice(offe);
} else {
//all value in one node;
const text = nodeValue.slice(offs, offe);
const span = this.createElement;
start.parentNode.replaceChild(span, start);
span.outerHTML =
nodeValue.slice(0, offs) +
this.setMark(text, type) +
nodeValue.slice(offe);
}
},
Restore(node) {
const p = node.parentNode;
if (p.className.startsWith("AssistantMark")) {
p.parentNode.innerHTML = p.parentNode.innerText;
return true;
}
return false;
},
removeMark() {
const select = this.Selection;
if (!select.anchorNode || select.isCollapsed) return;
let i = select.rangeCount;
const r = select.getRangeAt(--i);
let start = r.startContainer;
const end = r.endContainer;
if (start !== end) {
let t = start.nodeType;
if (t !== 3 && r.collapsed) {
const nodes = start.getElementsByClassName(
"AssistantMark"
);
let i = nodes.length;
if (i > 0) {
for (i; i--; ) {
const p = nodes[i].parentNode;
p.innerhHTML = p.innerText;
}
}
return;
}
while (start.childNodes.length > 0)
start = start.childNodes[0];
let p = start.parentNode.parentNode;
let next = start.nextSibling;
let result = this.Restore(start);
//if this is mark node, will be removed, so we need get the parentnode to backup, if it is not mark node, restore the parentnode
!result && (p = start.parentNode);
while (true) {
if (next) {
start = next;
} else {
next = p.nextSibling;
while (!next) {
p = p.parentNode;
next = p.nextSibling;
}
start = next;
}
while (start.childNodes.length > 0)
start = start.childNodes[0];
if (start === end) break;
p = start.parentNode.parentNode;
next = start.nextSibling;
result = this.Restore(start);
!result && (p = start.parentNode);
}
}
this.Restore(start);
},
},
autoScroll: {
stepTime: 40,
keyCount: 1,
scrollState: false,
scrollTime: null,
scrollPos: null,
bottom: 100,
zhuanlanAuto_mode: false,
pageScroll(TimeStamp) {
const position =
document.documentElement.scrollTop ||
document.body.scrollTop ||
window.pageYOffset;
if (this.scrollTime) {
this.scrollPos =
this.scrollPos !== null
? this.scrollPos +
(TimeStamp - this.scrollTime) / this.stepTime
: position;
window.scrollTo(0, this.scrollPos);
}
this.scrollTime = TimeStamp;
if (this.scrollState) {
let h =
document.documentElement.scrollHeight ||
document.body.scrollHeight;
h = h - window.innerHeight - this.bottom;
position < h
? window.requestAnimationFrame(
this.pageScroll.bind(this)
)
: this.stopScroll(true);
}
},
disableEvent(mode) {
const h = document.getElementsByClassName(
"RichText ztext Post-RichText"
);
if (h.length === 0) return;
h[0].style.pointerEvents = mode ? "none" : "inherit";
},
zhuanlanAuto() {
if (zhihu.Column.targetIndex === 0) return;
const text = `${
this.zhuanlanAuto_mode ? "exit" : "enter"
} autoscroll mode`;
Notification(text, "Tips");
this.zhuanlanAuto_mode = !this.zhuanlanAuto_mode;
},
get article_lists() {
const c = document.getElementById("column_lists");
if (!c) return null;
const a = c.getElementsByClassName("article_lists");
return a.length === 0 ? null : a[0].children;
},
nextPage() {
const ch = this.article_lists;
if (!ch) return;
const i = zhihu.Column.targetIndex;
if (ch.length === 0 || i === ch.length) {
Notification(
"no more content, have reach the last page",
"tips"
);
return;
}
ch[i].children[2].click();
setTimeout(() => {
this.keyCount = 2;
this.start();
}, 1800);
},
//0-9 => click target article;
key_Click(keyCode) {
const ch = this.article_lists;
let index = keyCode - 47;
if (index > ch.length) return;
ch[--index].children[2].click();
},
key_next_Pre(keyCode) {
const c = document.getElementById("column_lists");
if (!c) return;
const n = c.getElementsByClassName("nav button");
let i = keyCode - 188;
n.length > 0 &&
(i === 0
? n[0].children[i].click()
: n[0].children[--i].click());
},
popup() {
const html = `
<div
id="autoscroll-tips"
style="
top: 45%;
left: 45%;
position: fixed;
background: whitesmoke;
width: 210px;
height: 96px;
z-index: 1000;
"
>
<div class="autotips_content" style="margin: 5px 5px 5px 22px">
<span class="tips-header">Auto Scroll Mode</span>
<br />
<span class="tips_show" style="font-size: 12px"
>After 3s, auto load next page</span
>
<br />
<button
style="
width: 60px;
height: 24px;
box-shadow: 3px 4px 1px #888888;
margin-top: 10px;
margin-right: 10px;
border: rgb(247, 232, 176) solid 1.2px;
"
>
OK
</button>
<button
style="
width: 60px;
height: 24px;
box-shadow: 3px 4px 1px #888888;
border: white solid 1px;
"
>
Cancel
</button>
</div>
</div>`;
document.documentElement.insertAdjacentHTML("beforeend", html);
const tips = document.getElementById("autoscroll-tips");
let buttons = tips.getElementsByTagName("button");
const id = setTimeout(() => {
tips.remove();
zhihu.scroll.toTop();
this.nextPage();
}, 3000);
buttons[0].onclick = () => {
clearTimeout(id);
tips.remove();
zhihu.scroll.toTop();
this.nextPage();
};
buttons[1].onclick = () => {
clearTimeout(id);
tips.remove();
this.zhuanlanAuto_mode = false;
Notification("exit autoscroll mode successfully", "tips");
};
buttons = null;
},
stopScroll(mode = false) {
if (this.scrollState) {
this.scrollPos = null;
this.scrollTime = null;
this.scrollState = false;
this.keyCount = 1;
}
if (mode) {
this.disableEvent(false);
this.zhuanlanAuto_mode && this.popup();
}
},
speedUP() {
this.stepTime < 5 ? (this.stepTime = 5) : (this.stepTime -= 5);
},
slowDown() {
this.stepTime > 100
? (this.stepTime = 100)
: (this.stepTime += 5);
},
start() {
this.keyCount += 1;
if (this.keyCount % 2 === 0) return;
this.scrollState
? (this.stopScroll(), this.disableEvent(false))
: ((this.scrollState = true),
this.disableEvent(true),
window.requestAnimationFrame(this.pageScroll.bind(this)));
},
Others(keyCode, shift) {
shift
? keyCode === 67
? this.noteHightlight.removeMark()
: keyCode === 219
? this.Column.pagePrint()
: keyCode === 70
? this.Column.follow()
: keyCode === 76
? this.Column.columnsModule.recentModule.log("p")
: keyCode === 83
? this.Column.subscribe()
: this.noteHightlight.Marker(keyCode)
: keyCode === 113
? this.noteHightlight.EditDoc()
: keyCode === 78
? this.turnPage.start(true)
: keyCode === 84
? this.scroll.toTop()
: keyCode === 82
? this.scroll.toBottom()
: keyCode === 85
? this.turnPage.start(false)
: this.multiSearch(keyCode);
},
keyBoardEvent() {
window.onkeydown = (e) => {
if (e.ctrlKey || e.altKey) return;
const className = e.target.className;
if (
(className && className.includes("DraftEditor")) ||
e.target.localName === "input"
)
return;
const keyCode = e.keyCode;
const shift = e.shiftKey;
if (keyCode === 68 || (shift && keyCode === 71)) {
//68, d, default is login shortcut of zhihu
//71, g, + shift, default is scroll to the bottom of webpage
e.preventDefault();
e.stopPropagation();
}
shift
? keyCode === 65
? this.zhuanlanAuto()
: this.Others.call(zhihu, keyCode, shift)
: keyCode === 192
? this.start()
: keyCode === 187
? this.speedUP()
: keyCode === 189
? this.slowDown()
: keyCode > 47 && keyCode < 58
? this.key_Click(keyCode)
: keyCode === 188 || keyCode === 190
? this.key_next_Pre(keyCode)
: this.Others.call(zhihu, keyCode);
};
},
},
shade: {
Support: {
interval: 0,
support: null,
tips: null,
opacity: null,
opacityChange(opacity) {
const target = document.getElementById(
"screen_shade_cover"
);
target &&
(this.opacity === null
? (this.opacity = target.style.opacity)
: target.style.opacity !== opacity) &&
(target.style.opacity = opacity);
},
creatPopup() {
this.opacityChange(0);
const mt = -5;
const html = `
<div
id="support_me"
style="
background: darkgray;
text-align: justify;
width: 700px;
font-size: 16px;
height: 468px;
position: fixed;
left: 31%;
top: 25%;
z-index: 100000;
"
>
<div style="padding: 2.5%; font-weight: bold; font-size: 18px">
Support Me!
</div>
<div style="font-style: italic; font-size: 16px; padding-left: 2%">
Make Thing Better && Simpler!
<img
src="data:image/png;base64,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GEuv6XUAs4ZuQ4CCK840bndhAoCprELkC6QkFHzJHG5R+v2/y78BmfVhpxDogDsAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjAtMDctMTlUMDM6Mzk6MjArMDA6MDCGZw5cAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTAxLTA4VDE3OjI5OjQxKzAwOjAwZ4bKzwAAACB0RVh0c29mdHdhcmUAaHR0cHM6Ly9pbWFnZW1hZ2ljay5vcme8zx2dAAAAGHRFWHRUaHVtYjo6RG9jdW1lbnQ6OlBhZ2VzADGn/7svAAAAF3RFWHRUaHVtYjo6SW1hZ2U6OkhlaWdodAA2NLzgqYQAAAAWdEVYdFRodW1iOjpJbWFnZTo6V2lkdGgANjRET2kJAAAAGXRFWHRUaHVtYjo6TWltZXR5cGUAaW1hZ2UvcG5nP7JWTgAAABd0RVh0VGh1bWI6Ok1UaW1lADE1NDY5Njg1ODEI9oyUAAAAEXRFWHRUaHVtYjo6U2l6ZQAxMzU2QtzgBkMAAABYdEVYdFRodW1iOjpVUkkAZmlsZTovLy9kYXRhL3d3d3Jvb3Qvd3d3LmVhc3lpY29uLm5ldC9jZG4taW1nLmVhc3lpY29uLmNuL2ZpbGVzLzU2LzU2Mjk0OC5wbmdNCDr0AAAAAElFTkSuQmCC"
style="
float: left;
height: 42px;
width: 42px;
margin: -10px 4px 0 0px;
"
/>
</div>
<div
class="support_img"
style="padding-top: 4%; width: 100%; padding-left: 7.5%"
>
<div class="qrCode">
<img
src="data:image/webp;base64,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"
/>
</div>
</div>
<div class="timeout" style="font-size: 12px; padding: 3%">
15s, this Tips will be automatically closed or can you just click
</div>
<a
href="https://github.com/Kyouichirou/D7E1293/blob/main/Tmapermonkey/zhihu_optimizer_manual.md"
target="blank"
style="margin: ${mt}px 10px 0px 0px; float: right; font-size: 14px"
title="user manual"
>
Github: Kyouichirou
</a>
</div>`;
document.documentElement.insertAdjacentHTML(
"beforeend",
html
);
this.support = document.getElementById("support_me");
this.tips = this.support.getElementsByClassName(
"timeout"
)[0];
let time = 15;
this.interval = setInterval(() => {
time--;
this.tips.innerText = `${time}s, this Tips will be automatically closed or you can just click`;
time === 0 && this.remove();
}, 1000);
this.support.onclick = () =>
setTimeout(() => this.remove(), 120);
},
remove() {
clearInterval(this.interval);
this.opacityChange(this.opacity);
this.opacity = null;
this.interval = null;
this.support.remove();
this.support = null;
this.tips = null;
},
main() {
this.support ? this.remove() : this.creatPopup();
},
},
cover(color, opacity = 0.5) {
const html = `
<div
id="screen_shade_cover"
style="
transition: opacity 0.1s ease 0s;
z-index: 10000000;
margin: 0;
border-radius: 0;
padding: 0;
background: ${color};
pointer-events: none;
position: fixed;
top: -10%;
right: -10%;
width: 120%;
height: 120%;
opacity: ${opacity};
mix-blend-mode: multiply;
display: block;
"
></div>`;
document.documentElement.insertAdjacentHTML("afterbegin", html);
},
menu(e) {
const target = document.getElementById("screen_shade_cover");
target &&
target.style.background !== this[e] &&
(target.style.background = this[e]) &&
arguments.length === 2 &&
GM_setValue("color", e);
},
get opacity() {
const date = new Date();
const m = date.getMonth();
const h = date.getHours();
const [start, a] = m > 9 ? [15, 0.08] : [16, 0.12];
let opacity =
h > 20
? h > 22
? 0.6
: 0.5
: h < 8
? 0.65
: h > start
? h === 18
? 0.35
: h === 19
? 0.45
: h === 20
? 0.5
: 0.3
: 0.15;
return (opacity += opacity < 0.2 ? 0 : a);
},
opacityMonitor() {
const opacity = GM_getValue("opacity");
const target = document.getElementById("screen_shade_cover");
target &&
opacity &&
target.style.opacity !== opacity &&
(target.style.opacity = opacity);
},
supportID: null,
SupportMenu() {
this.supportID = GM_registerMenuCommand(
"Support || Donation",
this.Support.main.bind(this.Support),
"d4"
);
},
disableShade: {
id: null,
cmenu() {
this.id = GM_registerMenuCommand(
"Switch",
this.func.bind(this),
"s5"
);
},
rmenu() {
GM_unregisterMenuCommand(this.id);
},
func() {
const target = document.getElementById(
"screen_shade_cover"
);
target &&
(target.style.display =
target.style.display === "block"
? "none"
: "block");
},
},
menuID: null,
Switchfunc() {
const target = document.getElementById("screen_shade_cover");
let result = false;
if (target) {
if (arguments.length > 0 && !arguments[0]) return;
target.remove();
result = true;
this.disableShade.rmenu();
let i = this.menuID.length;
GM_removeValueChangeListener(this.opacitylistenID);
GM_removeValueChangeListener(this.colorlistenID);
for (i; i--; ) GM_unregisterMenuCommand(this.menuID[i]);
this.menuID = null;
} else {
//rebuild menu
if (arguments.length > 0 && arguments[0]) return;
if (this.menuID) return;
GM_unregisterMenuCommand(this.switchID);
GM_unregisterMenuCommand(this.supportID);
this.createShade();
this.SwitchMenu();
this.SupportMenu();
}
arguments.length === 0 && GM_setValue("turnoff", result);
},
switchID: null,
SwitchMenu() {
this.switchID = GM_registerMenuCommand(
"Turn(On/Off)",
this.Switchfunc.bind(this),
"t6"
);
},
turnoffID: null,
start() {
!GM_getValue("turnoff") && this.createShade();
this.SwitchMenu();
this.SupportMenu();
this.turnoffID = GM_addValueChangeListener(
"turnoff",
(name, oldValue, newValue, remote) => {
if (!remote || oldValue === newValue) return;
this.Switchfunc(newValue, true);
}
);
},
colorlistenID: null,
opacitylistenID: null,
createShade() {
const colors = {
yellow: "rgb(247, 232, 176)",
green: "rgb(202 ,232, 207)",
grey: "rgb(182, 182, 182)",
olive: "rgb(207, 230, 161)",
};
let color = GM_getValue("color");
(color && (color = colors[color])) || (color = colors.yellow);
const opacity = this.opacity;
this.cover(color, opacity);
const UpperCase = (e) =>
e.slice(0, 1).toUpperCase() + e.slice(1);
this.menuID = [];
for (const c of Object.entries(colors)) {
const id = GM_registerMenuCommand(
UpperCase(c[0]),
this.menu.bind(colors, c[0], true),
c[0]
);
this.menuID.push(id);
}
//note, who is the "this" in the GM_registerMenuCommand? take care of "this", must bind (function => this)
this.colorlistenID = GM_addValueChangeListener(
"color",
(name, oldValue, newValue, remote) => {
if (!remote || oldValue === newValue) return;
this.menu.call(colors, newValue);
}
);
GM_setValue("opacity", opacity);
this.opacitylistenID = GM_addValueChangeListener(
"opacity",
this.opacityMonitor
);
this.disableShade.cmenu();
},
},
antiRedirect() {
//only those links can be capture, which has the attribute of classname with ' external'
const links = Object.getOwnPropertyDescriptors(
HTMLAnchorElement.prototype
).href;
Object.defineProperty(HTMLAnchorElement.prototype, "href", {
...links,
get() {
let href = decodeURIComponent(links.get.call(this));
href = href.split("link.zhihu.com/?target=");
if (href.length > 1) {
this.href = href[1];
return href[1];
}
return href[0];
},
});
},
antiLogin() {
/*
note:
the timing of the js injection is uncertain, and for some reason the injection maybe late,
so that the occurrence of the event cannot be accurately captured
don't use dom load event =>
*/
let mo = new MutationObserver((events) =>
events.forEach((e) =>
e.addedNodes.forEach((node) => {
if (
node.getElementsByClassName("signFlowModal")
.length > 0
) {
node.style.display = "none";
setTimeout(() => {
const cancel = node.getElementsByClassName(
"Modal-backdrop"
);
if (cancel.length === 0) {
console.log("get cancel login id fail");
return;
}
cancel[0].click();
mo.disconnect();
mo = null;
}, 0);
}
})
)
);
document.body
? mo.observe(document.body, { childList: true })
: (document.onreadystatechange = () =>
mo && mo.observe(document.body, { childList: true }));
},
Filter: {
checked: null,
//click the ico of button
svgCheck(node, targetElements) {
let pnode = node.parentNode;
if (pnode.className === targetElements.buttonClass) {
return pnode;
} else {
pnode = pnode.parentNode;
let className = pnode.className;
let ic = 0;
while (className !== targetElements.buttonClass) {
pnode = pnode.parentNode;
if (!node || ic > 2) return null;
className = pnode.className;
ic++;
}
return pnode;
}
},
getiTem(target, targetElements) {
let item = target.parentNode;
if (item.className === targetElements.itemClass) {
return item;
} else {
item = item.parentNode;
let ic = 0;
let className = item.className;
while (className !== targetElements.itemClass) {
item = item.parentNode;
if (!item || ic > 3) return null;
className = item.className;
ic++;
}
return item;
}
},
//get the url id of the answer || article
getTargetID(item) {
const a = item.getElementsByTagName("a");
if (a.length === 0) return null;
const pathname = a[0].pathname;
return pathname.slice(pathname.lastIndexOf("/") + 1);
},
//checks if the part of answer is expanded
checkExpand(item) {
return (
item.getElementsByClassName(
"RichContent is-collapsed RichContent--unescapable"
).length > 0
);
},
/*
0, normal
1, searchpage => check username
2, click => check all content
3, article page => check expand
if the item has been checked, return
*/
contentCheck(item, targetElements, mode) {
let id = "";
if (mode === 2) {
id = this.getTargetID(item);
if (id && this.checked.includes(id)) return false;
}
const content = item.getElementsByClassName(
targetElements.contentID
);
if (content.length === 0) {
console.log("get content fail");
return false;
}
const text = content[0].innerText;
if (mode === 1) {
const name = text.startsWith("匿名用户:")
? ""
: text.slice(0, text.indexOf(":"));
if (name && blackName.includes(name)) {
console.log(
`%cuser of ${name} has been blocked`,
"color: red;"
);
item.style.display = "none";
return true;
}
}
const result = blackKey.some((e) => text.includes(e));
if (result) {
console.log("%citem has been blocked", "color: red;");
item.style.display = "none";
} else if (
mode === 2 ||
(targetElements.index < 2 && !this.checkExpand(item))
) {
(id || (id = this.getTargetID(item))) &&
this.checked.push(id);
}
return result;
},
userCheck(item, targetElements) {
const user = item.getElementsByClassName(targetElements.userID);
if (user.length === 0) {
console.log("get user fail, anonymous user");
return false;
}
let i = user.length - 1;
i = i > 1 ? 1 : i;
/*
const pathname = user[i].pathname;
if (!pathname) return false;
const id = pathname.slice(pathname.lastIndexOf("/") + 1);
let result = blackID.includes(id);
if (result) {
console.log(
`%cuser of ${id} has been blocked`,
"color: red;"
);
item.style.display = "none";
return result;
}
*/
const name = user[i].innerText;
const result = blackName.includes(name);
if (result) {
console.log(
`%cuser of ${name} has been blocked`,
"color: red;"
);
item.style.display = "none";
}
return result;
},
//block question
get Topic_question_ID() {
const href = location.href;
const reg = /(?<=(question|topic)\/)\d+/;
const match = href.match(reg);
if (!match) return null;
return match[0];
},
Topic_questionButton(targetElements) {
const header_line = (event, mode) => {
let question = null;
if (event) {
const path = event.path;
for (const e of path) {
if (e.className === targetElements.headerID) {
question = e;
break;
}
}
} else {
question = document.getElementsByClassName(
targetElements.headerID
)[0];
}
if (!question) return;
const h = question.getElementsByClassName(
targetElements.headerTitle
);
if (h.length === 0) return;
h[0].style.textDecoration = mode ? "line-through" : "none";
};
let q = document.getElementsByClassName(
targetElements.inserButtonID
);
if (q.length === 0) return;
const id = this.Topic_question_ID;
if (!id) return;
let mode = false;
const type = targetElements.index === 2 ? "topic" : "question";
let [name, title] = (mode = blackTopicAndQuestion.some(
(e) => e.id === id
))
? ["Remove", `remove the ${type} from block list`]
: ["Block", `add the ${type} to block list`];
const html = `
<button
title=${escapeBlank(title)}
style="
font-size: 14px;
height: 22px;
width: 60px;
margin-left: 10px;
box-shadow: 1px 1px 4px #888888;
"
>
${name}
</button>`;
let button = document.createElement("button");
q[0].insertAdjacentElement("beforeend", button);
button.outerHTML = html;
q[0].lastChild.onclick = function (event) {
if (mode) {
const index = blackTopicAndQuestion.findIndex(
(e) => e.id === id
);
if (index > -1) {
blackTopicAndQuestion.splice(index, 1);
GM_setValue(
targetElements.blockValueName,
blackTopicAndQuestion
);
}
} else {
if (blackTopicAndQuestion.some((e) => e.id === id))
return;
const info = {};
info.id = id;
info.type = type;
info.update = Date.now();
blackTopicAndQuestion.push(info);
GM_setValue(
targetElements.blockValueName,
blackTopicAndQuestion
);
}
mode = !mode;
[name, title] = mode
? ["Remove", `remove the ${type} from block list`]
: ["Block", `add the ${type} to block list`];
this.title = title;
this.innerText = name;
header_line(event, mode);
};
button = null;
q = null;
mode && header_line(null, true);
GM_addValueChangeListener(
targetElements.blockValueName,
(name, oldValue, newValue, remote) =>
remote && (blackTopicAndQuestion = newValue)
);
},
check(item, targetElements, mode) {
let result = false;
if (targetElements.index === 3) {
//zhihu hot news(the whole) => don't treat
const h = item.getElementsByClassName("MinorHotSpot");
if (h.length > 0) return;
result = this.contentCheck(item, targetElements, 1);
} else {
result = this.userCheck(item, targetElements);
result =
!result &&
this.contentCheck(item, targetElements, mode);
}
if (!result && this.dbInitial) {
if (targetElements.index < 2) {
this.foldAnswer.check(
item.className !== "ContentItem AnswerItem"
? item.getElementsByClassName(
"ContentItem AnswerItem"
)[0]
: item
);
} else {
this.foldAnswer.Three.main(item);
}
}
},
URL_hasChange: false,
checkURL(targetElements, mode = false) {
const href = location.href;
if (mode && this.currentHREF !== href) {
this.URL_hasChange = true;
this.currentHREF = href;
return false;
}
return targetElements.index < 2
? true
: targetElements.zone.some((e) => href.includes(e));
},
clickCheck(item, targetElements) {
// user without userid when in the search page, if the answer is not expanded
if (targetElements.index === 3) {
const result = this.userCheck(item, targetElements);
if (result) return;
}
setTimeout(
() => this.contentCheck(item, targetElements, 2),
300
);
},
//check the content when the content expanded
colorIndicator: {
lasttarget: null,
index: 0,
change: false,
color(target) {
this.restore();
const colors = ["green", "red", "blue", "purple"];
target.style.color = colors[this.index];
target.style.fontSize = "16px";
target.style.letterSpacing = "0.3px";
if (target.style.fontWeight !== 600) {
target.style.fontWeight = 600;
this.change = true;
} else this.change = false;
this.index > 2 ? (this.index = 0) : (this.index += 1);
this.lasttarget = target;
},
restore() {
if (this.lasttarget) {
this.lasttarget.style.color = "";
this.lasttarget.style.fontSize = "";
this.lasttarget.style.letterSpacing = "";
if (this.change)
this.lasttarget.style.fontWeight = "normal";
this.lasttarget = null;
}
},
},
clickMonitor(node, targetElements) {
const tags = ["blockquote", "p", "br", "li"];
node.onclick = (e) => {
const target = e.target;
const localName = target.localName;
if (tags.includes(localName)) {
if (target.style.color || this.foldAnswer.editableMode)
return;
this.colorIndicator.color(target);
return;
}
this.colorIndicator.restore();
const className = target.className;
if (
className &&
typeof className === "string" &&
className.startsWith("fold") &&
this.dbInitial
) {
if (targetElements.index < 2) {
const ends = [
"block",
"temp",
"element",
"select",
"edit",
];
if (ends.some((e) => className.endsWith(e)))
this.foldAnswer.buttonclick(target);
} else {
const ends = [
"question",
"topic",
"element",
"temp",
"answer",
"article",
];
if (ends.some((e) => className.endsWith(e)))
this.foldAnswer.Three.btnClick(target);
}
return;
}
let item = null;
//click the expand button
if (className === targetElements.buttonClass) {
item = this.getiTem(target, targetElements);
//click the ico of expand button
} else if (localName === "svg") {
const button = this.svgCheck(target, targetElements);
button && (item = this.getiTem(button, targetElements));
//click the answer, the content will be automatically expanded
} else {
if (className !== targetElements.expand) return;
for (const node of e.path) {
const className = node.className;
if (
className === targetElements.itemClass ||
className === targetElements.answerID
) {
item = node;
break;
}
}
}
if (item) this.clickCheck(item, targetElements);
else {
const path = e.path;
let ic = 0;
for (const c of path) {
if (c.localName === "a") {
const cl = c.className;
if (cl && !cl.endsWith("external")) {
const s = c.search;
if (s.startsWith("?target=")) {
e.preventDefault();
e.stopImmediatePropagation();
const h = s.slice(s.indexof("=") + 1);
c.href = h;
window.open(h, "_blank");
break;
}
}
}
ic++;
if (ic > 4) break;
}
}
};
},
topicAndquestion(targetElements, info) {
const items = document.getElementsByClassName(
"ContentItem-meta"
);
let n = items.length;
for (n; n--; ) {
const item = items[n];
const a = item.getElementsByClassName("UserLink-link");
let i = a.length;
if (i > 0) {
const username = a[--i].innerText;
if (username === info.username) {
const t = this.getiTem(item, targetElements);
t && this.setDisplay(t, info);
}
}
}
},
setDisplay(t, info) {
if (info.mode === "block") {
t.style.display !== "none" && (t.style.display = "none");
} else {
t.style.display === "none" && (t.style.display = "block");
}
},
userChange(index) {
const info = GM_getValue("blacknamechange");
if (!info) return;
const targetElements = this.getTagetElements(
index === 0 ? 1 : index
);
index === 0 &&
(targetElements.itemClass = "ContentItem AnswerItem");
if (!this.checkURL(targetElements)) return;
if (index === 3) {
const items = document.getElementsByClassName(
targetElements.itemClass
);
let n = items.length;
for (n; n--; ) {
const item = items[n];
const a = item.getElementsByClassName(
targetElements.userID
);
let i = a.length;
if (i > 0) {
const name = a[--i].innerText;
name === info.username &&
this.setDisplay(item, info);
} else {
const content = item.getElementsByClassName(
targetElements.contentID
);
if (content.length === 0) continue;
const text = content[0].innerText;
const name = text.startsWith("匿名用户:")
? ""
: text.slice(0, text.indexOf(":"));
name &&
name === info.username &&
this.setDisplay(item, info);
}
}
} else this.topicAndquestion(targetElements, info);
},
//standby, commmunication between others and column
connectColumn() {
const r = GM_getValue("removearticleA");
if (r && Array.isArray(r) && r.length > 0) {
for (const e of r) dataBaseInstance.dele(false, e);
GM_setValue("removearticleA", "");
}
const b = GM_getValue("blockarticleA");
if (b && Array.isArray(b) && b.length > 0) {
for (const e of b) dataBaseInstance.fold(e);
GM_setValue("blockarticleA", "");
}
GM_addValueChangeListener(
"blockarticleA",
(name, oldValue, newValue, remote) => {
if (remote) {
dataBaseInstance.fold(newValue[0]);
GM_setValue("blockarticleA", "");
}
}
);
GM_addValueChangeListener(
"removearticleA",
(name, oldValue, newValue, remote) => {
if (remote) {
debugger;
dataBaseInstance.dele(false, newValue[0]);
GM_setValue("removearticleA", "");
}
}
);
},
monitor(targetElements) {
let node = document.getElementById(targetElements.mainID);
if (!node) {
node = document.getElementsByClassName(
targetElements.backupClass
);
if (node.length === 0) {
console.log("%cget main id fail", "color: red;");
return;
} else {
node = node[0];
}
}
const mo = new MutationObserver((e) => {
if (
this.URL_hasChange ||
!this.checkURL(targetElements, targetElements.index > 1)
) {
return;
}
e.forEach((item) => {
if (item.addedNodes.length > 0) {
const additem = item.addedNodes[0];
if (additem.className === targetElements.itemClass)
this.check(additem, targetElements, 0);
}
});
});
mo.observe(node, { childList: true, subtree: true });
this.clickMonitor(node, targetElements);
this.connectColumn();
},
getTagetElements(index) {
const pos = {
1: "questionPage",
2: "topicPage",
3: "searchPage",
0: "answerPage",
};
this.checked = [];
const targetElements = this[pos[index]](index);
return targetElements;
},
dbInitial: false,
URL_change: false,
currentHREF: null,
main(index) {
this.currentHREF = location.href;
this.foldAnswer.initial().then((r) => {
this.checked = [];
this.dbInitial = r;
const targetElements = this.getTagetElements(index);
index !== 0 && this.firstRun(targetElements);
index !== 3 && this.Topic_questionButton(targetElements);
if (index > 1) {
unsafeWindow.addEventListener("urlchange", () =>
this.URL_change
? (this.URL_change = false)
: setTimeout(() => {
document.getElementsByClassName(
"fold_element"
).length === 0 &&
this.firstRun(targetElements, false);
}, 300)
);
//monitor forward or backward, this operation will not fire dom event
window.onpopstate = () => (
(this.URL_change = true),
this.firstRun(targetElements, false)
);
}
});
},
firstRun(targetElements, mode = true) {
if (!this.checkURL(targetElements)) {
mode && this.monitor(targetElements);
return;
}
let ic = 0;
let id = setInterval(() => {
const items = document.getElementsByClassName(
targetElements.itemClass
);
if (items.length > 4 || ic > 25) {
clearInterval(id);
for (const item of items)
this.check(item, targetElements, 0);
mode && this.monitor(targetElements);
this.URL_hasChange = false;
}
ic++;
}, 20);
},
answerPage() {
const targetElements = this.questionPage(1);
const items = document.getElementsByClassName(
targetElements.header
);
for (const item of items) this.check(item, targetElements, 0);
const node = document.getElementsByClassName(
targetElements.backupClass
);
this.clickMonitor(node[0], targetElements);
const all = document.getElementsByClassName(
"QuestionMainAction ViewAll-QuestionMainAction"
);
for (const button of all)
button.onclick = () =>
setTimeout(() => this.firstRun(targetElements), 300);
return targetElements;
},
questionPage(index) {
const targetElements = {
button:
"Button ContentItem-rightButton ContentItem-expandButton Button--plain",
itemClass: "List-item",
mainID: "QuestionAnswers-answers",
contentID: "RichText ztext CopyrightRichText-richText",
userID: "UserLink-link",
backupClass: "Question-main",
header: "ContentItem AnswerItem",
headerID: "QuestionHeader",
headerTitle: "QuestionHeader-title",
expand: "RichText ztext CopyrightRichText-richText",
answerID: "ContentItem AnswerItem",
inserID: "LabelContainer-wrapper",
blockValueName: "blacktopicAndquestion",
inserButtonID: "QuestionHeaderActions",
index: index,
};
return targetElements;
},
searchPage(index) {
const nocontent = document.getElementsByClassName(
"SearchNoContent-title"
);
if (nocontent.length > 0) return null;
const targetElements = {
buttonClass: "Button ContentItem-more Button--plain",
itemClass: "Card SearchResult-Card",
mainID: "SearchMain",
contentID: "RichText ztext CopyrightRichText-richText",
expand: "RichContent-inner",
userID: "UserLink-link",
zone: ["type=content"],
index: index,
};
return targetElements;
},
topicPage(index) {
const targetElements = {
buttonClass: "Button ContentItem-more Button--plain",
itemClass: "List-item TopicFeedItem",
mainID: "TopicMain",
userID: "UserLink-link",
headerID: "ContentItem-head",
headerTitle: "ContentItem-title",
contentID: "RichText ztext CopyrightRichText-richText",
expand: "RichContent-inner",
inserButtonID: "TopicActions TopicMetaCard-actions",
zone: ["/top-answers", "/hot", "newest"],
blockValueName: "blacktopicAndquestion",
index: index,
};
return targetElements;
},
foldAnswer: {
Three: {
initialR: false,
btnClick(button) {
const text = button.innerText;
if (text.startsWith("show")) {
this.showFold(button);
} else {
let bid = "";
if (text !== "Fold") {
bid = this.getbid(button);
if (!bid) return;
}
this[text](button, bid);
}
},
getbid(button) {
const attrs = button.attributes;
if (!attrs) return null;
for (const e of attrs)
if (e.name === "bid") return e.value;
return null;
},
Answer_Article(button, bid, type, n, t, from = "") {
const p = button.parentNode.parentNode;
const user = p.getElementsByClassName("UserLink-link");
let i = user.length - 1;
const info = {};
info.userID = "";
info.userName = "";
if (i > 0) {
i = i > 1 ? 1 : i;
info.userName = user[i].innerText;
const pn = user[i].pathname;
info.userID = pn.slice(pn.lastIndexOf("/") + 1);
}
info.from = from;
info.name = bid;
info.update = Date.now();
info.type = type;
dataBaseInstance.fold(info);
this.changeBtn(button, n, t);
},
changeBtn(button, n, t) {
button.innerText = n;
button.title = t;
this.Fold(button, "show blocked");
},
Question_Topic(button, bid, type, n, t) {
if (blackTopicAndQuestion.some((e) => e.id === bid))
return;
const info = {};
info.id = bid;
info.type = type;
info.update = Date.now();
blackTopicAndQuestion.push(info);
GM_setValue(
"blacktopicAndquestion",
blackTopicAndQuestion
);
this.changeBtn(button, n, t);
},
Answer(button, bid) {
const from = this.getbid(button.nextElementSibling);
this.Answer_Article(
button,
bid,
"answer",
"Remove",
"unblock the answer",
from ? from : ""
);
},
Question(button, bid) {
this.Question_Topic(
button,
bid,
"question",
"Remove",
"unblock the question"
);
},
Article(button, bid) {
this.Answer_Article(
button,
bid,
"article",
"Remove",
"unblock the article"
);
let b = GM_getValue("blockarticleB");
if (b && Array.isArray(b)) {
for (const e of b) if (e.pid === bid) return;
} else b = [];
const r = GM_getValue("removearticleB");
if (r && Array.isArray(r)) {
const i = r.indexOf(bid);
if (i > -1) {
r.splice(i, 1);
GM_setValue("removearticleB", r);
}
}
const info = {};
info.userID = "";
info.from = "";
info.pid = bid;
info.value = 0;
info.update = Date.now();
b.push(info);
GM_setValue("blockarticleB", b);
},
Topic(button, bid) {
this.Question_Topic(
button,
bid,
"topic",
"Remove",
"unblock the topic"
);
},
Remove(button, bid) {
const className = button.className;
const method = {
changeBtn(n, t) {
button.innerText = n;
button.title = t;
button.parentNode.previousElementSibling.innerText =
"show folded";
},
answer() {
dataBaseInstance.dele(false, bid);
this.changeBtn("Answer", "block the answer");
},
remove() {
let i = -1;
for (const e of blackTopicAndQuestion) {
i++;
if (e.id === bid) break;
}
if (i < 0) return;
blackTopicAndQuestion.splice(i, 1);
GM_setValue("", blackTopicAndQuestion);
},
topic() {
this.remove();
this.changeBtn("Topic", "block the topic");
},
article() {
dataBaseInstance.dele(false, bid);
this.changeBtn("Article", "block the article");
let r = GM_getValue("removearticleB");
if (r && Array.isArray(r) && r.length > 0) {
if (r.includes(bid)) return;
} else r = [];
const b = GM_getValue("blockarticleB");
if (b && Array.isArray(b)) {
const i = b.findIndex((e) => e.pid === bid);
if (i > -1) {
b.splice(i, 1);
GM_setValue("blockarticleB", b);
}
}
r.push(bid);
GM_setValue("removearticleB", r);
},
question() {
this.remove();
this.changeBtn(
"Question",
"block the question"
);
},
};
const n = className.slice(className.indexOf("_") + 1);
method[n]();
},
Fold(button, n = "") {
const p = button.parentNode;
p.previousElementSibling.style.display = "block";
p.nextElementSibling.style.display = "none";
p.style.display = "none";
n && (p.previousElementSibling.innerText = n);
},
showFold(button) {
button.style.display = "none";
const n = button.nextElementSibling;
if (!n) return;
n.style.display = "grid";
n.nextElementSibling.style.display = "block";
},
getInfo(item) {
const title = item.getElementsByTagName("h2");
if (title.length === 0) return null;
const a = title[0].getElementsByTagName("a");
if (a.length === 0) return null;
const p = a[0].pathname;
const info = {};
info.cblock = false;
info.ablcok = false;
info.qblock = false;
info.tblock = false;
const flags = ["/topic", "/p/", "/question"];
const index = flags.findIndex((e) => p.includes(e));
if (index === 0) {
info.type = "topic";
info.tid = p.slice(p.lastIndexOf("/") + 1);
} else if (index === 1) {
info.type = "column";
info.cid = p.slice(p.lastIndexOf("/") + 1);
} else {
info.type = "answer";
const tmp = p.split("/");
if (tmp.length !== 5) return null;
info.qid = tmp[2];
info.aid = tmp[4];
}
return info;
},
btnRaw(c, t, n, b) {
return `<button class="fold_${c}" title=${escapeBlank(
t
)} bid=${b}>${n}</button>`;
},
foldRaw(arr, info, adisplay, bdisplay, name) {
const d = JSON.stringify(info);
return `
<div
class="fold_element"
title="the ${info.type} has been folded"
data-info=${d}
style="display:${adisplay};"
>
show ${name}
</div>
<div class="hidden_fold" data-info=${d} style="display:${bdisplay};">
${arr.join("")}
<button class="fold_temp" title="temporarily fold the item">Fold</button>
</div>`;
},
checkAnswerOrArticle(id) {
return new Promise((resolve, reject) => {
dataBaseInstance.check(id).then(
(result) => resolve(result),
(err) => {
console.log(err);
reject();
}
);
});
},
checkTAndQ(id) {
return blackTopicAndQuestion.some((e) => e.id === id);
},
answer(item, info) {
const exe = (r) => {
const f = this.checkTAndQ(info.qid);
const html = [];
info.qblock = f;
info.ablcok = r;
(r || f) && this.hide_content(item);
let [t, n] = r
? ["unblock", "Remove"]
: ["block", "Answer"];
html.push(
this.btnRaw(
"answer",
t + " the answer",
n,
info.aid
)
);
[t, n] = f
? ["unblock", "Remove"]
: ["block", "Question"];
html.push(
this.btnRaw(
"question",
t + " the question",
n,
info.qid
)
);
const [a, b, name] =
r || f
? ["grid", "none", "blocked"]
: ["none", "grid", "folded"];
item.insertAdjacentHTML(
"afterbegin",
this.foldRaw(html, info, a, b, name)
);
};
this.initialR
? this.checkAnswerOrArticle(info.aid).then(
(r) => exe(r),
() => console.log("check blocked answer fail")
)
: exe(false);
},
hide_content(item) {
item.firstChild.style.display = "none";
},
column(item, info) {
const exe = (r) => {
info.cblock = r;
r && this.hide_content(item);
const [t, n, a, b, name] = r
? [
"unblock",
"Remove",
"grid",
"none",
"blocked",
]
: [
"block",
"Article",
"none",
"grid",
"folded",
];
item.insertAdjacentHTML(
"afterbegin",
this.foldRaw(
[
this.btnRaw(
"article",
t + " the article",
n,
info.cid
),
],
info,
a,
b,
name
)
);
};
this.initialR
? this.checkAnswerOrArticle(info.cid).then(
(r) => exe(r),
() =>
console.log("check blocked article fail")
)
: exe(false);
},
topic(item, info) {
const f = this.checkTAndQ(info.tid);
info.tblock = f;
f && this.hide_content(item);
const [t, n, a, b, name] = f
? ["unblock", "Remove", "block", "none", "blocked"]
: ["block", "Topic", "none", "grid", "folded"];
item.insertAdjacentHTML(
"afterbegin",
this.foldRaw(
[
this.btnRaw(
"topic",
t + " the topic",
n,
info.tid
),
],
info,
a,
b,
name
)
);
},
main(item) {
const info = this.getInfo(item);
if (!info) return;
this[info.type](item, info);
},
},
//---------------------------------------------------------------------------------------
buttonclick(button) {
const text = button.innerText;
text.startsWith("show")
? this.showFold(button, text)
: this[text](button);
},
getcontent(button) {
const p = this.getpNode(button);
if (!p) return null;
const expand = p.getElementsByClassName(
"Button ContentItem-rightButton ContentItem-expandButton Button--plain"
);
if (expand.length > 0) expand[0].click();
const ele = p.getElementsByClassName(
"RichText ztext CopyrightRichText-richText"
);
return ele.length === 0 ? null : ele[0];
},
editableMode: false,
Edit(button) {
const ele = this.getcontent(button);
if (!ele) return;
this.editableMode = true;
ele.contentEditable = true;
button.innerText = "Exit";
button.title = "exit editable mode";
},
Exit(button) {
const ele = this.getcontent(button);
if (!ele) return;
this.editableMode = false;
ele.contentEditable = false;
button.innerText = "Edit";
button.title = "edit the answer";
},
Select(button) {
if (this.editableMode) return;
const ele = this.getcontent(button);
if (!ele) return;
const selection = window.getSelection();
selection.removeAllRanges();
const range = new Range();
range.selectNodeContents(ele);
selection.addRange(range);
},
getid(item) {
const attrs = item.attributes;
if (!attrs) return null;
for (const a of attrs)
if (a.name === "name") return a.value;
return null;
},
getpNode(button) {
let pnode = button.parentNode;
let ic = 0;
while (pnode.className !== "ContentItem AnswerItem") {
pnode = pnode.parentNode;
ic++;
if (ic > 4 || !pnode) return null;
}
return pnode;
},
get from() {
const p = location.pathname;
const reg = /(?<=question\/)\d+/;
const m = p.match(reg);
return m ? m[0] : "";
},
Block(button, type = "answer") {
const p = this.getpNode(button);
if (!p) return;
const id = this.getid(p);
if (!id) return;
const info = {};
const user = p.getElementsByClassName("UserLink-link");
let i = user.length - 1;
if (i > 0) {
i = i > 1 ? 1 : i;
info.userName = user[i].innerText;
const pn = user[i].pathname;
info.userID = pn.slice(pn.lastIndexOf("/") + 1);
}
info.name = id;
info.update = Date.now();
info.type = type;
info.from = this.from;
dataBaseInstance.fold(info);
button.innerText = "Remove";
button.title = "remove the answer from block list";
this.insertShowFolded(p, "blocked");
},
Remove(button) {
//show temp button
const p = this.getpNode(button);
if (!p) return;
const id = this.getid(p);
if (!id) return;
dataBaseInstance.dele(false, id);
button.innerText = "Block";
button.title = "fold the answer forever";
},
Fold(button) {
const p = this.getpNode(button);
if (!p) return;
this.insertShowFolded(p, "folded");
},
showFold(button, text) {
const next = button.nextElementSibling;
next.style.display = "block";
button.style.display = "none";
if (text.endsWith("blocked")) this.insertButton(next, true);
},
check(item) {
if (!item) return;
if (this.initialR === 0) {
this.insertButton(item);
return;
}
const id = this.getid(item);
if (!id) return;
dataBaseInstance.check(id).then(
(r) => {
r
? this.insertShowFolded(item, "blocked")
: this.insertButton(item);
},
(err) => console.log(err)
);
},
initialR: 0,
initial() {
return new Promise((resolve) => {
const tables = ["foldedAnswer"];
dataBaseInstance.initial(tables, true, "name").then(
(result) => {
resolve(true),
(this.Three.initialR = this.initialR = result);
},
(err) => {
console.log(err);
resolve(false);
}
);
});
},
insertShowFolded(item, name) {
//if it has already contained this element , to hide or show
const f = item.parentNode.getElementsByClassName(
"fold_element"
);
if (f.length === 0) {
const html = `
<div
class="fold_element"
title="the answer has been folded"
>
show ${name}
</div>`;
item.insertAdjacentHTML("beforebegin", html);
item.style.display = "none";
} else {
item.style.display = "none";
f[0].style.display = "block";
f[0].innerText = `show ${name}`;
}
},
insertButton(item, mode = false) {
const h = item.getElementsByClassName("hidden_fold");
if (h.length === 0) {
const obutton = `
<button class="fold_temp" title="temporarily fold the answer">Fold</button>
<button class="fold_edit" title="edit the answer">Edit</button>
<button class="fold_select" title="select the answer">Select</button>`;
const html = `
<div class="hidden_fold">
<button class="fold_block" title="fold the answer forever">Block</button>
${obutton}
</div>`;
const r = `
<div class="hidden_fold">
<button class="fold_block" title="remove the answer from block list">Remove</button>
${obutton}
</div>`;
const label = item.getElementsByClassName(
"LabelContainer-wrapper"
);
if (label.length > 0)
label[0].insertAdjacentHTML(
"afterend",
mode ? r : html
);
}
},
},
},
addStyle(index) {
const common = `
span.RichText.ztext.CopyrightRichText-richText{text-align: justify !important;}
body{text-shadow: #a9a9a9 0.025em 0.015em 0.02em;}`;
const showfold = `
.fold_element{
max-height: 24px;
margin-left: 45%;
margin-top: 3px;
width: 100px;
text-align: center;
border: 1px solid #ccc !important;
}`;
const contentstyle = `
${showfold}
.hidden_fold:hover {
opacity: 1;
transition: opacity 2s;
}
.hidden_fold{opacity: 0.15;}
.hidden_fold button {
float: right;
border: 1px solid #ccc!important;
box-shadow: 1px 1px 4px #888888;
height: 21px;
font-size: 14px;
width: 54px;
border-radius: 5px;
}
div.Question-mainColumn{
margin: auto !important;
width: 100% !important;
max-width: 1000px !important;
min-width: 1000px !important;
}
.RichText-MCNLinkCardContainer,
div.Question-sideColumn,.Kanshan-container{display: none !important;}
figure{max-width: 70% !important;}
.RichContent-inner{
line-height: 30px !important;
margin: 40px 60px !important;
padding: 40px 50px !important;
border: 6px dashed rgba(133,144,166,0.2) !important;
border-radius: 6px !important;
}
a[href*="u.jd.com"],
.Pc-word,
span.LinkCard-content.LinkCard-ecommerceLoadingCard,
.RichText-MCNLinkCardContainer{display: none !important;}
.Comments{padding: 12px !important; margin: 60px !important;}`;
const inpustyle = `
input::-webkit-input-placeholder {
font-size: 0px !important;
text-align: right;
}`;
const hotsearch =
".Card.TopSearch{display: none !important;}.List-item{position: inherit !important;}";
const topicAndquestion = `
${showfold}
.hidden_fold {
opacity: 0.15;
float: right;
}
.hidden_fold:hover {
opacity: 1;
transition: opacity 2s;
}
.hidden_fold button {
border: 1px solid #ccc!important;
box-shadow: 1px 1px 0px #888888;
height: 18px;
font-size: 12px;
width: 54px;
border-radius: 5px;
margin-top: 2px;
}`;
GM_addStyle(
common +
(index < 2
? contentstyle + inpustyle
: index === 3
? inpustyle + hotsearch + topicAndquestion
: index === 2
? inpustyle + topicAndquestion
: inpustyle)
);
},
clearStorage() {
const rubbish = {};
rubbish.timeStamp = Date.now();
rubbish.words = [];
//localstorage must storage this info to ensure the history show
for (let i = 0; i < 5; i++)
rubbish.words.push({ displayQuery: "", query: "" });
localStorage.setItem("search::top-search", JSON.stringify(rubbish));
localStorage.setItem("search:preset_words", "");
localStorage.setItem("zap:SharedSession", "");
},
inputBox: {
box: null,
controlEventListener() {
const windowEventListener = window.addEventListener;
const eventTargetEventListener =
EventTarget.prototype.addEventListener;
function addEventListener(type, listener, useCapture) {
//take care
const NewEventListener =
this instanceof Window
? windowEventListener
: eventTargetEventListener;
//block original keyboard event to prevent blank search(ads)
if (
type.startsWith("key") &&
!listener.toString().includes("(fuckzhihu)")
)
return;
Reflect.apply(NewEventListener, this, [
type,
listener,
useCapture,
]);
//this => who lauch this function, eg, window, document, htmlelement...
}
window.addEventListener = EventTarget.prototype.addEventListener = addEventListener;
},
monitor() {
this.box = document.getElementsByTagName("input")[0];
this.box.placeholder = "";
unsafeWindow.addEventListener(
"popstate",
(e) => {
e.preventDefault();
e.stopPropagation();
},
true
);
unsafeWindow.addEventListener(
"visibilitychange",
(e) => {
e.preventDefault();
this.box.placeholder = "";
e.stopPropagation();
},
true
);
let button = document.getElementsByClassName(
"Button SearchBar-searchButton Button--primary"
);
if (button.length > 0) {
button[0].onclick = (e) => {
if (this.box.value.length === 0) {
e.preventDefault();
e.stopPropagation();
}
};
}
button = null;
this.box.addEventListener(
"keydown",
(fuckzhihu) => {
if (fuckzhihu.keyCode !== 13) return;
if (
this.box.value.length === 0 ||
this.box.value.trim().length === 0
) {
fuckzhihu.preventDefault();
fuckzhihu.stopImmediatePropagation();
fuckzhihu.stopPropagation();
} else {
const url = `https://www.zhihu.com/search?q=${this.box.value}&type=content`;
window.open(url, "_blank");
}
},
true
);
this.box.onfocus = () => {
this.box.value.length === 0 && (this.box.placeholder = "");
localStorage.setItem("zap:SharedSession", "");
};
this.box.onblur = () => (this.box.placeholder = "");
this.firstRun();
},
firstRun() {
let mo = new MutationObserver((e) => {
if (e.length !== 1 || e[0].addedNodes.length !== 1) return;
const target = e[0].addedNodes[0];
const p = target.getElementsByClassName("Popover-content");
if (p.length === 0) return;
const tmp = p[0].getElementsByClassName(
"AutoComplete-group"
);
if (tmp.length === 0) return;
this.AutoComplete = tmp[0];
if (p[0].innerText.startsWith("知乎热搜"))
this.AutoComplete.style.display = "none";
mo.disconnect();
mo = null;
this.secondRun(p[0].parentNode);
});
mo.observe(document.body, { childList: true });
},
AutoComplete: null,
secondRun(target) {
const mo = new MutationObserver((e) => {
if (e.length === 1) {
if (e[0].addedNodes.length !== 1) {
this.AutoComplete = null;
return;
}
const t = e[0].addedNodes[0];
this.AutoComplete = t.getElementsByClassName(
"AutoComplete-group"
)[0];
if (t.innerText.startsWith("知乎热搜"))
this.AutoComplete.style.display = "none";
} else {
const style =
this.box.value.length > 0 ? "inline" : "none";
this.AutoComplete.style.display !== style &&
(this.AutoComplete.style.display = style);
}
});
mo.observe(target, { childList: true, subtree: true });
},
},
ErrorAutoClose() {
const w = document.getElementsByClassName("PostIndex-warning");
if (w.length === 0) return;
const h = document.createElement("h2");
w[0].insertAdjacentElement("afterbegin", h);
setTimeout(() => window.close(), 6100);
let time = 5;
const dot = ".";
h.innerText = `5s, current web will be automatically closed.....`;
let id = setInterval(() => {
time--;
h.innerText = `${time}s, current web will be automatically closed${dot.repeat(
time
)}`;
if (time === 0) clearInterval(id);
}, 1000);
},
zhuanlanStyle(mode) {
//font, the pic of header, main content, sidebar, main content letter spacing, comment zone, ..
//@media print, print preview, make the background-color can view when save webpage as pdf file
const article = `
mark.AssistantMark.red{background-color: rgba(255, 128, 128, 0.65) !important;box-shadow: rgb(255, 128, 128) 0px 1.2px;border-radius: 0.2em !important;}
mark.AssistantMark.yellow{background-color: rgba(255, 250, 90, 1) !important;box-shadow: rgb(255, 255, 170) 0px 1.2px;border-radius: 0.2em !important;}
mark.AssistantMark.green{background-color: rgba(170, 235, 140, 0.8) !important;box-shadow: rgb(170, 255, 170) 0px 2.2px;border-radius: 0.2em !important;}
mark.AssistantMark.purple{background-color: rgba(255, 170, 255, 0.8) !important;box-shadow: rgb(255, 170, 255) 0px 1.2px;border-radius: 0.2em !important;}
@media print {
mark.AssistantMark { box-shadow: unset !important; -webkit-print-color-adjust: exact !important; }
.CornerButtons,
.toc-bar.toc-bar--collapsed,
div#assist-button-container {display : none;}
#column_lists {display : none !important;}
}
body{text-shadow: #a9a9a9 0.025em 0.015em 0.02em;}
.TitleImage{width: 500px !important}
.Post-Main .Post-RichText{text-align: justify !important;}
.Post-SideActions{left: calc(50vw - 560px) !important;}
.RichText.ztext.Post-RichText{letter-spacing: 0.1px;}
.Sticky.RichContent-actions.is-fixed.is-bottom{position: inherit !important}
.Comments-container,
.Post-RichTextContainer{width: 900px !important;}
a[href*="u.jd.com"],
.RichText-MCNLinkCardContainer,
span.LinkCard-content.LinkCard-ecommerceLoadingCard,
.RichText-MCNLinkCardContainer{display: none !important}`;
const list = `.Card:nth-of-type(3),.Card:last-child,.css-8txec3{width: 900px !important;}`;
if (mode) {
const r = GM_getValue("reader");
if (r) {
GM_addStyle(article + this.Column.clearPage(0).join(""));
this.Column.readerMode = true;
} else {
GM_addStyle(article);
}
if (document.title.startsWith("该内容暂无法显示")) {
window.onload = () => this.ErrorAutoClose();
return;
}
this.Column.isZhuanlan = true;
window.onload = () => {
this.colorAssistant.main();
this.autoScroll.keyBoardEvent();
this.Column.main(0);
};
} else {
GM_addStyle(list);
this.Column.main(2);
}
},
Column: {
isZhuanlan: false,
authorID: "",
authorName: "",
get ColumnDetail() {
const header = document.getElementsByClassName(
"ColumnLink ColumnPageHeader-TitleColumn"
);
if (header.length === 0) {
this.columnName = "";
this.columnID = "";
return false;
}
const href = header[0].href;
this.columnID = href.slice(href.lastIndexOf("/") + 1);
this.columnName = header[0].innerText;
const post = document.getElementsByClassName("Post-Author");
if (post.length > 0) {
const user = post[0].getElementsByClassName(
"UserLink-link"
);
const i = user.length - 1;
const p = user[i].pathname;
this.authorName = user[i].innerText;
this.authorID = p.slice(p.lastIndexOf("/") + 1);
}
return true;
},
updateAuthor(author) {
const html = `
<div
class="AuthorInfo"
itemprop="author"
itemscope=""
itemtype="http://schema.org/Person"
>
<meta itemprop="name" content=${author.name} /><meta
itemprop="image"
content=${author.avatar_url}
/><meta
itemprop="url"
content=https://www.zhihu.com/people/${author.url_token}
/><meta itemprop="zhihu:followerCount" content="3287" /><span
class="UserLink AuthorInfo-avatarWrapper"
><div class="Popover">
<div
id="Popover8-toggle"
aria-haspopup="true"
aria-expanded="false"
aria-owns="Popover8-content"
>
<a
class="UserLink-link"
data-za-detail-view-element_name="User"
target="_blank"
href="//www.zhihu.com/people/${author.url_token}"
><img
class="Avatar Avatar--round AuthorInfo-avatar"
width="38"
height="38"
src=${author.avatar_url}
srcset=${author.avatar_url}
alt=${author.name}
/></a>
</div></div
></span>
<div class="AuthorInfo-content">
<div class="AuthorInfo-head">
<span class="UserLink AuthorInfo-name"
><div class="Popover">
<div
id="Popover9-toggle"
aria-haspopup="true"
aria-expanded="false"
aria-owns="Popover9-content"
>
<a
class="UserLink-link"
data-za-detail-view-element_name="User"
target="_blank"
href="//www.zhihu.com/people/${author.url_token}"
>${author.name}</a
>
</div>
</div></span
>
</div>
<div class="AuthorInfo-detail">
<div class="AuthorInfo-badge">
<div class="ztext AuthorInfo-badgeText">
${author.headline}
</div>
</div>
</div>
</div>
</div>`;
const authorNode = document.getElementsByClassName(
"AuthorInfo"
);
if (authorNode.length > 0) authorNode[0].outerHTML = html;
this.authorID = author.url_token;
this.authorName = author.name;
},
//column homepage
subscribeOrfollow() {
if (!this.ColumnDetail) return;
let fn = "follow";
let sn = "subscribe";
const f = GM_getValue(fn);
if (f && Array.isArray(f))
f.some((e) => this.columnID === e.columnID) &&
(fn = "remove");
const s = GM_getValue(sn);
if (s && Array.isArray(s))
s.some((e) => this.columnID === e.columnID) &&
(sn = "remove");
let [a, b] =
fn === "remove" ? ["remove", "from"] : ["add", "to"];
const ft = `${a} the column ${b} follow list`;
[a, b] = sn === "remove" ? ["remove", "from"] : ["add", "to"];
const st = `${a} the column ${b} subscribe list`;
const html = `
<div class="assistant-button" style="margin-left: 15px">
<style type="text/css">
.assistant-button button {
box-shadow: 1px 1px 2px #848484;
height: 24px;
border: 1px solid #ccc !important;
border-radius: 8px;
}
</style>
<button class="follow" style="width: 80px; margin-right: 5px;color: #2196F3;" title=${ft}>
${fn}
</button>
<button class="subscribe" style="width: 90px" title=${st}>${sn}</button>
</div>`;
//const bhtml = `<button class="block" style="width: 70px" title="block the column">block</button>`;
const user = document.getElementsByClassName(
"AuthorInfo AuthorInfo--plain"
);
if (user.length === 0) return;
user[0].parentNode.insertAdjacentHTML("beforeend", html);
let buttons = document.getElementsByClassName(
"assistant-button"
)[0].children;
const exe = (button, mode) => {
const name = button.innerText;
if (name === "remove") {
const vname = mode === 1 ? "follow" : "subscribe";
const arr = GM_getValue(vname);
if (arr && Array.isArray(arr)) {
const index = arr.findIndex(
(e) => e.columnID === this.columnID
);
if (index > -1) {
arr.splice(index, 1);
GM_setValue(vname, arr);
if (mode === 1 && this.columnsModule.node)
this.columnsModule.database = arr;
}
Notification(`un${vname} successfully`, "Tips");
}
button.innerText = vname;
button.title = `add the column to ${vname} list`;
} else {
if (mode === 1) {
const i = this.follow(true);
if (i !== 1) {
button.innerText = "remove";
button.title =
"remove the column from follow list";
}
} else {
this.subscribe();
button.innerText = "remove";
button.title =
"remove the column from subscribe list";
}
}
};
buttons[1].onclick = function () {
exe(this, 1);
};
buttons[2].onclick = function () {
exe(this, 2);
};
/*
buttons[3].onclick =function () {
exe(this, 3);
}
*/
buttons = null;
},
//shift + f
follow(mode) {
if (!this.columnID) return;
let f = GM_getValue("follow");
if (f && Array.isArray(f)) {
let index = 0;
for (const e of f) {
if (this.columnID === e.columnID) {
const c = confirm(
`you have already followed this column on ${this.timeStampconvertor(
e.update
)}, is unfollow this column?`
);
if (!c) return 0;
f.splice(index, 1);
GM_setValue("follow", f);
Notification(
"unfollow this column successfully",
"Tips"
);
return 1;
}
index++;
}
} else f = [];
const p = prompt(
"please input some tags about this column, like: javascript python; multiple tags use blank space to isolate",
"javascript python"
);
let tags = [];
if (p && p.trim()) {
const tmp = p.split(" ");
for (let e of tmp) {
e = e.trim();
e && tags.push(e);
}
}
if (tags.length === 0 && !mode) {
const top = document.getElementsByClassName(
"TopicList Post-Topics"
);
if (top.length > 0) {
const topic = top[0].children;
for (const e of topic) tags.push(e.innerText);
}
}
const info = {};
info.columnID = this.columnID;
info.update = Date.now();
info.columnName = this.columnName;
info.tags = tags;
f.push(info);
this.columnsModule.node && (this.columnsModule.database = f);
GM_setValue("follow", f);
Notification(
"you have followed this column successfully",
"Tips",
3500
);
return 2;
},
//shift + s
subscribe() {
if (!this.columnID) return;
let s = GM_getValue("subscribe");
if (s && Array.isArray(s)) {
let i = 0;
for (const e of s) {
if (e.columnID === this.columnID) {
s.splice(i, 1);
break;
}
}
} else s = [];
const i = s.length;
const info = {};
info.columnID = this.columnID;
info.update = Date.now();
info.columnName = this.columnName;
if (i === 0) {
s.push(info);
} else {
i === 10 && s.pop();
s.unshift(info);
}
GM_setValue("subscribe", s);
Notification(
"you have subscribed this column successfully",
"Tips",
3500
);
},
Tabs: {
get GUID() {
// blob:https://xxx.com/+ uuid
const link = URL.createObjectURL(new Blob());
const blob = link.toString();
URL.revokeObjectURL(link);
return blob.substr(blob.lastIndexOf("/") + 1);
},
save(columnID) {
//if currentb window does't close, when reflesh page or open new url in current window(how to detect the change ?)
//if open new url in same tab, how to change the uuid?
GM_getTab((tab) => {
const uuid = this.GUID;
tab.id = uuid;
tab.columnID = columnID;
sessionStorage.setItem("uuid", uuid);
GM_saveTab(tab);
});
},
check(columnID) {
return new Promise((resolve) => {
GM_getTabs((tabs) => {
if (tabs) {
//when open a new tab with "_blank" method, this tab will carry the session data of origin tab
const uuid = sessionStorage.getItem("uuid");
if (!uuid) {
resolve(false);
} else {
const tablist = Object.values(tabs);
const f = tablist.some(
(e) =>
e.columnID === columnID &&
uuid === e.id
);
resolve(f);
}
} else resolve(false);
});
});
},
},
tocMenu: {
change: false,
appendNode(toc) {
if (toc.className.endsWith("collapsed")) return;
const header = document.getElementsByClassName(
"Post-Header"
);
if (header.length === 0) {
console.log("the header has been remove");
return;
}
header[0].appendChild(toc);
toc.style.position = "sticky";
toc.style.width = "900px";
this.change = true;
},
restoreNode(toc) {
if (!this.change) return;
document.body.append(toc);
toc.removeAttribute("style");
this.change = false;
},
main(mode) {
const toc = document.getElementById("toc-bar");
toc &&
(mode ? this.restoreNode(toc) : this.appendNode(toc));
},
},
titleChange: false,
clearPage(mode = 0) {
const ids = [
"Post-Sub Post-NormalSub",
"Post-Author",
"span.Voters button",
"ColumnPageHeader-Wrapper",
"Post-SideActions",
"Sticky RichContent-actions is-bottom",
];
if (mode === 0) {
const reg = /\s/g;
const css = ids.map(
(e) =>
`${
e.startsWith("span")
? e
: `.${e.replace(reg, ".")}`
}{display: none;}`
);
return css;
} else {
const style = mode === 1 ? "block" : "none";
ids.forEach((e) => {
const tmp = e.startsWith("span");
tmp &&
(e = e.slice(e.indexOf(".") + 1, e.indexOf(" ")));
const t = document.getElementsByClassName(e);
t.length > 0 &&
(tmp
? (t[0].firstChild.style.display = style)
: (t[0].style.display = style));
});
}
},
titleAlign() {
if (this.modePrint && !this.titleChange) return;
const title = document.getElementsByClassName("Post-Title");
if (title.length === 0) return;
if (this.modePrint) {
title[0].removeAttribute("style");
} else {
if (title[0].innerText.length > 28) return;
title[0].style.textAlign = "center";
}
this.titleChange = !this.titleChange;
},
modePrint: false,
pagePrint() {
Notification(
`${this.modePrint ? "exit" : "enter"} print mode`,
"Print",
3500
);
!this.readerMode && this.clearPage(this.modePrint ? 1 : 2);
this.tocMenu.main(this.modePrint);
this.titleAlign();
!this.modePrint && window.print();
this.modePrint = !this.modePrint;
},
Framework() {
const html = `
<div
id="column_lists"
style="
top: 54px;
width: 380px;
font-size: 14px;
box-sizing: border-box;
padding: 0 10px 10px 0;
box-shadow: 0 1px 3px #ddd;
border-radius: 4px;
transition: width 0.2s ease;
color: #333;
background: #fefefe;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
display: flex;
z-index: 1000;
position: absolute;
left: 2%;
"
>
<style type="text/css">
button.button {
margin: 15px 0px 5px 0px;
width: 60px;
height: 24px;
border-radius: 3px;
box-shadow: 1px 2px 5px #888888;
}
div#column_lists .list.num {
color: #fff;
width: 18px;
height: 18px;
text-align: center;
line-height: 18px;
background: #fff;
border-radius: 2px;
display: inline-block;
background: #00a1d6;
}
div#column_lists ul a:hover{
color: blue;
}
div#column_lists ul {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
line-height: 1.9;
}
div#column_lists .header{
font-weight: bold;
font-size: 16px;
}
</style>
<span
class="right_column"
style="margin-left: 5%; margin-top: 5px; width: 100%"
>
<span class="header current column">
<a
class="column name"
href= https://www.zhihu.com/column/${
this.columnID
}
target="_blank"
title=${
this.isZhuanlan
? ""
: escapeBlank("random column")
}
>${this.columnName}</a
>
<span class="tips" style="
float: right;
font-size: 14px;
font-weight: normal;
"></span>
<hr style="width: 340px" />
</span>
<ul
class="article_lists"
>
</ul>
<div class="nav button">
<button class="button last" title="previous page">Pre</button>
<button class="button next" title="next page">Next</button>
<button class="button hide" title="hide the menu">Hide</button>
<button class="button more" title="show more content">More</button>
<select class="select-pages" size="1" name="pageslist" style="margin-left: 20px; margin-top: 16px; height: 24px; position: absolute; width: 60px;box-shadow: 1px 2px 5px #888888;">
<option value="0" selected>pages</option>
</select>
</div>
</span>
</div>`;
document.body.insertAdjacentHTML("beforeend", html);
},
timeStampconvertor(timestamp) {
if (!timestamp) return "undefined";
if (typeof timestamp === "number") {
const s = timestamp.toString();
if (s.length === 10) timestamp *= 1000;
else if (s.length !== 13) return "undefined";
} else {
if (timestamp.length === 10) {
timestamp = parseInt(timestamp);
timestamp *= 1000;
} else if (timestamp.length === 13) {
timestamp = parseInt(timestamp);
} else {
return "";
}
}
const date = new Date(timestamp);
const y = date.getFullYear() + "-";
const m =
(date.getMonth() + 1 < 10
? "0" + (date.getMonth() + 1)
: date.getMonth() + 1) + "-";
let d = date.getDate();
d = d < 10 ? "0" + d : d;
return y + m + d;
},
backupInfo: null,
next: null,
previous: null,
index: 1,
rqReady: false,
requestData(url) {
if (this.rqReady) {
Notification("please request data slowly...", "Tips");
return;
}
this.rqReady = true;
xmlHTTPRequest(url).then(
(json) => {
typeof json === "string" && (json = JSON.parse(json));
const data = json.data;
let id = this.isReverse ? data.length : 1;
const html = [];
this.backupInfo = [];
const tips =
"click me, show the content in current webpage";
const HREF = location.href;
for (const e of data) {
const type = e.type;
if (type !== "article" && type !== "answer")
continue;
const info = {};
info.id = id;
let time = e.updated;
let title = e.title;
let className = '"list_date"';
let question = "";
info.url = e.url;
const tmp = {};
let fontWeight = "";
if (!time) {
time = e.updated_time;
className = "list_date_question";
title = e.question.title;
question =
"this is a question page, do not show in current page";
info.url = `https://www.zhihu.com/question/${e.question.id}/answer/${e.id}`;
} else {
HREF === info.url &&
((fontWeight =
' style="font-weight:bold;"'),
(this.targetIndex = id));
tmp.author = e.author;
}
info.excerpt = escapeHTML(
`${title} <摘要>: ` + e.excerpt
);
info.updated = this.timeStampconvertor(time);
title = titleSlice(title);
title = escapeHTML(title);
info.title = title;
html.push(
this.liTagRaw(
info,
question || tips,
className,
fontWeight
)
);
if (!question) {
tmp.content = e.content;
tmp.title = e.title;
}
this.backupInfo.push(tmp);
this.isReverse ? id-- : id++;
}
if (this.isReverse) {
this.backupInfo.reverse();
html.reverse();
}
const pag = json.paging;
const totals = pag.totals;
this.appendNode(html, totals);
this.previous = this.isReverse
? pag.is_end
? ""
: pag.next
: pag.is_start
? ""
: pag.previous;
this.next = this.isReverse
? pag.is_start
? ""
: pag.previous
: pag.is_end
? ""
: pag.next;
this.rqReady = false;
},
(err) => {
console.log(err);
this.rqReady = false;
}
);
},
firstAdd: true,
selectRaw(index, name) {
return `<option value=${index}>${name}</option>`;
},
total_pages: 0,
isReverse: false,
//pages list
appendSelect(node, pages, mode = false) {
const select = node.getElementsByClassName("select-pages");
if (select.length === 0) return;
this.total_pages = Math.ceil(pages / 10);
const end = this.total_pages > 30 ? 31 : this.total_pages + 1;
const html = [];
for (let index = 1; index < end; index++)
html.push(this.selectRaw(index, index));
if (this.total_pages > 1)
html.push(this.selectRaw(html.length + 1, "reverse"));
select[0].insertAdjacentHTML("beforeend", html.join(""));
const optionNum = select[0].length + 1;
const [ds, dh] =
optionNum < 9
? [optionNum, 15 * optionNum + "px"]
: [8, "120px"];
select[0].onmousedown = function () {
this.size = ds;
this.style.height = dh;
};
select[0].onblur = function () {
this.style.height = "24px";
this.size = 1;
};
//execute
const exe = (opt) => {
if (this.total_pages === 1) return;
const i = opt.value * 1;
const tmp = i === this.total_pages + 1;
if (tmp) {
if (this.isReverse) return;
this.isReverse = tmp;
this.index = 1;
opt.value = 1;
} else {
if (i === 0) {
if (this.isReverse) {
this.isReverse = false;
this.index = 1;
opt.value = 1;
} else {
return;
}
} else this.index = i;
}
if (mode) {
Reflect.apply(this.homePage.add, this, [
this.homePage.initial,
"f",
true,
]);
} else {
const m = this.isReverse
? this.total_pages - this.index
: this.index - 1;
const URL = `http://www.zhihu.com/api/v4/columns/${
this.columnID
}/items?limit=10&offset=${10 * m}`;
this.requestData(URL);
}
};
select[0].onchange = function () {
this.size = 1;
this.style.height = "24px";
exe(this);
};
},
changeSelect(mode) {
this.index += mode ? 1 : -1;
const column = document.getElementById("column_lists");
const select = column.getElementsByTagName("select")[0];
select.value = this.index;
},
appendNode(html, totals, mode = false) {
const column = document.getElementById("column_lists");
if (!column) {
console.log("the module of column has been deleted");
return;
}
const lists = column.getElementsByClassName("article_lists")[0];
this.firstAdd
? (lists.insertAdjacentHTML("afterbegin", html.join("")),
this.appendSelect(column, totals, mode),
this.clickEvent(column, mode))
: (lists.innerHTML = html.join(""));
this.firstAdd = false;
},
liTagRaw(info, title, className = "list_date", fontWeight = "") {
const html = `
<li${fontWeight}>
<span class="list num">${info.id}</span>
<a
href=${info.url}
target="_blank"
title=${info.excerpt}>${info.title}</a
>
<span class=${className} style="float: right" title=${title}>${info.updated}</span>
</li>`;
return html;
},
nextPage: false,
tipsTimeout: null,
showTips(tips) {
const column = document.getElementById("column_lists");
if (!column) return;
const tipNode = column.getElementsByClassName("tips")[0];
tipNode.innerText = tips;
this.tipsTimeout && clearTimeout(this.tipsTimeout);
this.tipsTimeout = setTimeout(() => {
tipNode.innerText = "";
this.tipsTimeout = null;
}, 2000);
},
/*
need add new function => simple mode and content mode, if it is simple mode, all page direct show the menu of column?
*/
followCursor: null,
homePage: {
follow: null,
get ColumnID() {
const i = Math.floor(Math.random() * this.follow.length);
return this.follow[i].columnID;
},
get initial() {
this.follow = GM_getValue("follow");
if (
!this.follow ||
!Array.isArray(this.follow) ||
this.follow.length === 0
)
return false;
return this.follow;
},
add(follow, direction, page) {
if (!follow) return;
setTimeout(() => {
const html = [];
const k = follow.length;
if (this.isReverse) {
this.followCursor = (this.index - 1) * 10 - 1;
} else {
if (page) {
this.followCursor = k - (this.index - 1) * 10;
} else {
if (this.followCursor === null) {
this.followCursor = k;
} else {
if (direction === "r")
this.followCursor += 20;
if (this.followCursor > k)
this.followCursor = k;
}
}
}
const className = "list_date_follow";
const title = "follow date";
let id = 1;
const prefix = "https://www.zhihu.com/column/";
const methods = {
r() {
this.followCursor += 1;
return this.followCursor < k;
},
f() {
this.followCursor -= 1;
return this.followCursor > -1;
},
};
const func = this.isReverse ? "r" : "f";
while (methods[func].call(this)) {
const e = follow[this.followCursor];
const info = {};
info.title = e.columnName;
info.updated = this.timeStampconvertor(e.update);
info.excerpt = e.tags.join("; ");
info.url = prefix + e.columnID;
info.id = id;
html.push(this.liTagRaw(info, title, className));
id++;
if (id > 10) break;
}
this.appendNode(html, k, true);
this.previous = this.isReverse
? this.index - 1
: !(this.followCursor + id - 1 === k);
this.next = this.isReverse
? this.index === Math.ceil(k / 10)
? 0
: 1
: this.followCursor > -1;
this.homePage.follow = null;
}, 0);
},
},
targetIndex: 0,
clickEvent(node, mode = false) {
let buttons = node.getElementsByClassName("nav button")[0]
.children;
let article = node.getElementsByTagName("ul")[0];
let aid = 0;
//prevent click too fast
let isReady = false;
//show content in current page;
article.onclick = (e) => {
//if under autoscroll mode, => not allow to click
if (
Reflect.get(zhihu.autoScroll, "scrollState") ||
Reflect.get(zhihu.noteHightlight, "editable")
)
return;
if (isReady) {
Notification("please operate slowly...", "Tips");
return;
}
const href = e.target.previousElementSibling.href;
if (location.href === href) return;
const className = e.target.className;
if (className === "list_date_follow")
window.open(href, "_self");
else if (className !== "list_date") return;
const content = document.getElementsByClassName(
"RichText ztext Post-RichText"
);
if (content.length === 0) return;
const p = e.path;
let ic = 0;
for (const e of p) {
if (e.localName === "li") {
let id = e.children[0].innerText;
id *= 1;
if (id === aid) return;
aid = id;
break;
}
if (ic > 2) return;
ic++;
}
isReady = true;
const i = aid - 1;
const title = document.getElementsByClassName("Post-Title");
title.length > 0 &&
(title[0].innerText = this.backupInfo[i].title);
content[0].innerHTML = this.backupInfo[i].content;
zhihu.colorAssistant.main();
window.history.replaceState(null, null, href);
document.title = `${this.backupInfo[i].title} - 知乎`;
//refresh the menu
const toc = document.getElementById("toc-bar");
if (toc) {
const refresh = toc.getElementsByClassName(
"toc-bar__refresh toc-bar__icon-btn"
)[0];
refresh.click();
}
const author = this.backupInfo[i].author;
if (author && this.authorID !== author.url_token)
this.updateAuthor(author);
let pnode = e.target.parentNode;
let j = 0;
while (pnode.localName !== "li") {
pnode = pnode.parentNode;
j++;
if (j > 2) break;
}
j < 3 && (pnode.style.fontWeight = "bold");
if (this.targetIndex > 0) {
pnode.parentNode.children[
this.targetIndex - 1
].style.fontWeight = "normal";
}
this.targetIndex = aid;
this.reInject();
isReady = false;
const links = content[0].getElementsByTagName("a");
for (const link of links) {
const href = decodeURIComponent(link.href).split(
"link.zhihu.com/?target="
);
if (href.length > 1) link.href = href[1];
}
};
article = null;
//last page
let isCollapsed = false;
buttons[0].onclick = () => {
!isCollapsed &&
(this.previous
? (mode
? Reflect.apply(this.homePage.add, this, [
this.homePage.initial,
"r",
false,
])
: this.requestData(this.previous),
(aid = 0),
this.changeSelect(false))
: this.showTips("no more content"));
};
//next page
buttons[1].onclick = () => {
!isCollapsed &&
(this.next
? (mode
? Reflect.apply(this.homePage.add, this, [
this.homePage.initial,
"f",
false,
])
: this.requestData(this.next),
(aid = 0),
this.changeSelect(true))
: this.showTips("no more content"));
};
//hide the sidebar
buttons[2].onclick = function () {
const [style, text, title] = isCollapsed
? ["block", "Hide", "hide the menu"]
: ["none", "Expand", "show the menu"];
this.parentNode.parentNode.children[1].style.display = style;
const more = this.parentNode.nextElementSibling;
if (more) {
more.style.display = style;
more.nextElementSibling.style.display = style;
}
this.innerText = text;
this.title = title;
isCollapsed = !isCollapsed;
};
let addnew = true;
const createModule = (button) => {
const sub = GM_getValue("subscribe");
if (addnew) {
this.columnsModule.liTagRaw = this.liTagRaw;
this.columnsModule.isZhuanlan = this.isZhuanlan;
this.columnsModule.timeStampconvertor = this.timeStampconvertor;
}
let html = null;
let text = "";
if (sub && Array.isArray(sub)) {
let id = 1;
const prefix = "https://www.zhihu.com/column/";
const title = "subscribe time";
html = sub.map((e) => {
const info = {};
info.id = id;
info.url = prefix + e.columnID;
info.updated = this.timeStampconvertor(e.update);
info.title = e.columnName;
info.excerpt = "";
id++;
return this.liTagRaw(info, title);
});
text = "Subscribe";
} else text = "no more data";
addnew
? this.columnsModule.main(
button,
html,
text,
escapeBlank(
this.isZhuanlan
? "column/collection search"
: "column search"
)
)
: this.columnsModule.appendNewNode(html, text);
addnew = false;
};
buttons[3].onclick = function () {
!isCollapsed && createModule(this);
};
//if webpage is column home, expand this menu
mode && createModule(buttons[3]);
buttons = null;
},
columnsModule: {
recentModule: {
log(type) {
//history, pocket, recent collect: h, c, p
const href = location.href;
let r = GM_getValue("recent");
if (r && Array.isArray(r)) {
const index = r.findIndex((e) => e.url === href);
if (index > -1 && r[index].type === type) {
if (index === 0) return;
r.splice(index, 1);
}
} else r = [];
const info = {};
let title = document.title;
title = title.slice(0, title.length - 5);
info.title = title;
info.update = Date.now();
info.type = type;
info.url = href;
const i = r.length;
if (i === 0) {
r.push(info);
} else {
if (i === 5) r.pop();
r.unshift(info);
}
GM_setValue("recent", r);
type === "p" &&
Notification(
"add current article to read later successfully",
"Tips"
);
},
remove(type) {
const href = location.href;
const r = GM_getValue("recent");
if (r && Array.isArray(r)) {
const index = r.findIndex((e) => e.url === href);
if (r[index].type === type) {
r.splice(index, 1);
GM_setValue("recent", r);
}
}
},
addnew: true,
read(node, liTagRaw, timeStampconvertor) {
const r = GM_getValue("recent");
if (!r || !Array.isArray(r) || r.length === 0) return;
const html = r.map((e) => {
const info = {};
const type = e.type;
info.updated = timeStampconvertor(e.update);
info.id = type;
info.excerpt = "";
info.url = e.url;
info.title = titleSlice(e.title);
const title =
"recent " +
(type === "h"
? "read history"
: type === "c"
? "collection"
: "pocket");
return liTagRaw(info, title);
});
let ul = node.getElementsByTagName("ul")[0];
const pre = ul.previousElementSibling;
pre.style.display =
html.length === 0 ? "block" : "none";
ul.innerHTML = html.join("");
if (this.addnew) {
ul.onclick = (e) => {
if (e.target.className === "list_date") {
const a =
e.target.previousElementSibling.href;
location.href !== a &&
window.open(a, "_self");
}
};
}
ul = null;
},
main(node, liTagRaw, timeStampconvertor) {
const n = node.nextElementSibling;
this.read(n, liTagRaw, timeStampconvertor);
let button = n.getElementsByClassName(
"button refresh"
)[0];
button.onclick = () =>
this.read(n, liTagRaw, timeStampconvertor);
button = null;
},
},
database: null,
node: null,
liTagRaw: null,
addNewModule(text, placeholder) {
const html = `
<div class="more columns">
<hr>
<div class="search module" style="margin-bottom: 10px">
<input
type="text"
placeholder=${placeholder}
style="height: 24px; width: 250px"
/>
<button class="button search" style="margin-left: 11px;">Search</button>
</div>
<hr>
<span class="header columns">${text}</span>
<ul class="columns list">
</ul>
</div>
<div class="recently-activity">
<hr>
<span class="header columns"
>Recent
<button
class="button refresh"
style="margin-top: 5px; margin-left: 210px; font-weight: normal"
title="refresh recent content"
>
refresh
</button>
</span>
<hr>
<span class="header columns">no recent data</span>
<ul></ul>
</div>`;
const column = document.getElementById("column_lists");
if (column)
column.children[1].insertAdjacentHTML(
"beforeend",
html
);
},
appendNewNode(html, text = "", node) {
let pnode = node || this.node;
pnode = pnode.parentNode.nextElementSibling;
const ul = pnode.getElementsByTagName("ul")[0];
ul.innerHTML = html.join("");
text && (pnode.children[3].innerText = text);
},
checkInlcudes(e, key) {
if (e.columnName.includes(key)) return true;
return e.tags.some((t) =>
key.length > t.length
? key.includes(t)
: t.includes(key)
);
},
timeStampconvertor: null,
commandFormat(str) {
const treg = /(?<=\$)[dmhyw][<>=][0-9]+/g;
const areg = /(?<=\$)a=\(.+\)/g;
const preg = /(?<=\$)p=[0-9]{5,}/g;
const t = str.match(treg);
const p = str.match(preg);
if (t && p) return null;
const a = str.match(areg);
if (p && a) return null;
if (!(a || p || t)) return null;
const sigs = ["=", ">", "<"];
const sign = {
0: "equal",
1: "great",
2: "less",
};
const type = {};
if (t) {
let cm = "";
let ecount = 0;
let lcount = 0;
let gcount = 0;
for (const e of t) {
if (cm && cm !== e[0]) return null;
if (!type[e[0]]) type[e[0]] = {};
if (type[e[0]]) {
const sg = e[1];
const index = sigs.findIndex((e) => e === sg);
if (index === 0) {
ecount++;
if (ecount > 1) return null;
} else if (index === 1) {
gcount++;
if (gcount > 1) return null;
} else {
lcount++;
if (lcount > 1) return null;
}
const n = sign[index];
if (type[e[0]][n]) return null;
type[e[0]][n] = e.slice(2);
}
}
if (a) {
if (a.length > 1) return null;
const tmp = a[0];
type[tmp[0]] = tmp
.slice(3, tmp.length - 1)
.split(" ");
}
} else if (p) {
if (p.length > 1) return null;
const tmp = p[0];
type[tmp[0]] = tmp[2];
} else if (a) {
if (a.length > 1) return null;
const tmp = a[0];
type[tmp[0]] = tmp.slice(3, tmp.length - 1).split(" ");
}
return type;
},
ExecuteFunc: {
Resultshow(e, i, liTagRaw, timeStampconvertor) {
const info = {};
info.id = i;
info.title = titleSlice(e.title);
info.excerpt = escapeHTML(e.excerpt);
info.updated = timeStampconvertor(e.update);
const title = escapeBlank("collect time");
const prefix = "/p/";
info.url = prefix + e.pid;
return liTagRaw(info, title);
},
less(a, b) {
return a < b;
},
equal(a, b) {
return a === b;
},
great(a, b) {
return a > b;
},
get now() {
return Date.now();
},
get nowDate() {
return new Date();
},
getUpdate(update) {
return new Date(update);
},
w(value, mode, info) {
const now = this.now;
const time = info.update;
const week = (now - time) / (86400000 * 7);
return this[mode](week, value * 1);
},
d(value, mode, info) {
const now = this.now;
const time = info.update;
const day = (now - time) / 86400000;
return this[mode](day, value * 1);
},
m(value, mode, info) {
const endDate = this.nowDate;
const beginDate = this.getUpdate(info.update);
const month =
endDate.getFullYear() * 12 +
endDate.getMonth() -
(beginDate.getFullYear() * 12 +
beginDate.getMonth());
return this[mode](month, value * 1);
},
y(value, mode, info) {
const endDate = this.nowDate;
const beginDate = this.getUpdate(info.update);
const year =
endDate.getFullYear() * 12 +
endDate.getMonth() -
(beginDate.getFullYear() * 12 +
beginDate.getMonth()) /
12;
return this[mode](year, value * 1);
},
h(value, mode, info) {
const now = this.now;
const time = info.update;
const hour = (now - time) / (1000 * 60 * 60);
return this[mode](hour, value * 1);
},
a(value, info) {
const content =
info.title +
" | " +
info.tags.join("") +
info.excerpt;
return value.some((v) => content.includes(v));
},
search(table, fs, liTagRaw, timeStampconvertor) {
return new Promise((resolve, reject) => {
let ic = 0;
const html = [];
const cur = table.openCursor(null, "next");
cur.onsuccess = (e) => {
const cursor = e.target.result;
if (cursor) {
const info = cursor.value;
const result = fs.every((f) => f(info));
if (result) {
ic++;
html.push(
this.Resultshow(
info,
ic,
liTagRaw,
timeStampconvertor
)
);
}
if (ic === 10) {
resolve(html);
} else {
cursor.continue();
}
} else {
resolve(html);
}
};
cur.onerror = (e) => {
console.log(e);
reject("open db cursor fail");
};
});
},
funcs(
type,
liTagRaw,
timeStampconvertor,
node,
appendNewNode
) {
const fs = [];
const keys = Object.keys(type);
for (const f of keys) {
const tmp = type[f];
if (Array.isArray(tmp) || typeof tmp !== "object") {
if (f === "p") {
dataBaseInstance.check().then(
(r) => {
const html = [];
if (r) {
html.push(
this.Resultshow(
r,
1,
liTagRaw,
timeStampconvertor
)
);
}
appendNewNode(
html,
html.length === 0
? "no search result"
: "article search results",
node
);
},
(err) => console.log(err)
);
return;
}
fs.push(this[f].bind(this, tmp));
continue;
}
const tk = Object.keys(tmp);
for (const e of tk)
fs.push(this[f].bind(this, tmp[e], e));
}
return fs;
},
main(
table,
type,
liTagRaw,
timeStampconvertor,
node,
appendNewNode
) {
const fs = this.funcs(
type,
liTagRaw,
timeStampconvertor,
node,
appendNewNode
);
if (fs.length === 0) {
dataBaseInstance.close();
return;
}
this.search(
table,
fs,
liTagRaw,
timeStampconvertor
).then(
(html) => {
appendNewNode(
html,
html.length === 0
? "no search result"
: "article search results",
node
);
dataBaseInstance.close();
},
(err) => {
console.log(err);
dataBaseInstance.close();
}
);
},
},
initialDatabase(type) {
if (!type) return;
dataBaseInstance.initial(["collection"], false).then(
(result) => {
if (result === 0) {
this.appendNewNode([], "no search result");
return;
}
this.ExecuteFunc.main(
dataBaseInstance.Table,
type,
this.liTagRaw,
this.timeStampconvertor,
this.node,
this.appendNewNode
);
},
(err) => {
this.appendNewNode([], "open DB fail");
dataBaseInstance.close();
}
);
},
isZhuanlan: false,
searchDatabase(key) {
if (key.startsWith("$") && this.isZhuanlan) {
const cm = ["a", "p", "d", "m", "y", "h", "w"];
const sc = key.slice(1, 2);
const f = cm.findIndex((e) => sc === e);
if (f < 0) return false;
this.initialDatabase(this.commandFormat(key));
return true;
}
return false;
},
search(key) {
//search follow columns & collection of article
if (this.searchDatabase(key)) return;
let i = 0;
const html = [];
const prefix = "https://www.zhihu.com/column/";
const title = "follow time";
for (const e of this.database) {
if (this.checkInlcudes(e, key)) {
i++;
const info = {};
info.id = i;
info.title = e.columnName;
info.excerpt = e.tags.join("; ");
info.updated = this.timeStampconvertor(e.update);
info.url = prefix + e.columnID;
html.push(this.liTagRaw(info, title));
if (i === 10) break;
}
}
this.appendNewNode(
html,
html.length === 0
? "no search result"
: "column search results"
);
},
event() {
const p = this.node.parentNode.nextElementSibling;
const input = p.getElementsByTagName("input")[0];
input.onkeydown = (e) => {
if (e.keyCode !== 13) return;
const key = input.value.trim();
key.length > 1 && this.search(key);
};
let button = p.getElementsByClassName("button search")[0];
button.onclick = () => {
const key = input.value.trim();
key.length > 1 && this.search(key);
};
button = null;
let ul = p.getElementsByTagName("ul")[0];
ul.onclick = (e) => {
if (e.target.className === "list_date") {
const a = e.target.previousElementSibling.href;
location.href !== a && window.open(a, "_self");
}
};
ul = null;
this.recentModule.main(
p,
this.liTagRaw,
this.timeStampconvertor
);
},
main(node, html, text, placeholder) {
this.node = node;
this.addNewModule(text, placeholder);
html && this.appendNewNode(html);
this.database = GM_getValue("follow");
if (!this.database || !Array.isArray(this.database))
this.database = [];
this.event();
GM_addValueChangeListener(
"follow",
(name, oldValue, newValue, remote) =>
remote && (this.database = newValue)
);
},
},
assistNewModule: {
preferenceModule(ainfo, binfo) {
const html = `
<div class="article_attitude" style="margin-left: 23px;">
<i
class="like"
style="
display: ${ainfo.ldisplay};
width: 26px;
content: url(data:image/webp;base64,UklGRoQBAABXRUJQVlA4IHgBAACQCACdASogAB8APm0ukUakIqGhKA1QgA2JaQATIBYgOyI1I/+d+2b49NA3z97A/6i/5f7VTVdqWUnQ+1B4fpukJQWsIUl77RwBlh9AYAD+/zKmH7r/7EJor7FchRHELu3xLyzt7tJsA4Kzo9o+jvdvg5F0PPnJ9h0T6ZLB/b/rf/u1ZNAgIjiz+e0teMM3r/7DFRJvl0h7Fp4PhhsogsSnsOevchiVl0v6jl73n1FPJaHvKz9qfJjC7VQCJTZ5/BAH9nF7hT/zjn+4fX8wnMmQGU9umlNySGfj4UGkhjkHpIds5Roy6Pi8eUA5zXCS8zCLf44zw/qXPgWSHfCrt+fmUapijvBoIn63X72ltR/wx3tHDi5gRr/wpBL8ERY3qmeKlMEBs9Km2Kg7BwpkKE4XNXjYQXuHn5bv7F0uxIKi2Ubgl/XO+BoeBlATzvrM24XeHW2/tv3nQ/z/wzbte4iSl5C3+7TrtUmPDs/XPieOK13LuZjgAAAA);
height: 26px;
margin-bottom: 5px;
"
title=${ainfo.ltitle}
></i>
<i
class="dislike"
style="
content: url(data:image/webp;base64,UklGRmYBAABXRUJQVlA4IFoBAACQCQCdASogACAAPm0qkkWkIqGYDAYAQAbEtIAJ5c+RfUB0rJ7j9kpzPfcf1XiiNSX5s/7v5gPbD3Bv1F/0K6nH9glbEGQcDYiAI3S+NfKqNr+EMSyQAP7yUv/+QvpX0Z+oaIucX4be+hsni+QhcLK0f/ugn01LW17/NrkTFet/XNw/o/5+bPpPA3v7Ic8/NjVb4alH/V1LUJBkL7kko/v//XOzDfCaCoDzemP+zPNtC3dB6keXLJVpBMrLJgNzY/VUAKf+1SSCB/5BD/4Nf+lylTxAGWJl0/1twWPbOMbH0d+MQkF0fSsPqqBRpvc6FNf+PClifyl4OpWwfxGKiMm1C7O8/w0vDlPCbbwR4FKhQKuj2Fyul9WITHFC4fpzSNIBC39ZlySnghJRgDgCo/P+f9205ENaW+9XKyYLte/Gddtc43354MKLVHwYv44znVWV8gcjChTAAAAA);
display: ${ainfo.ddisplay};
width: 24px;
height: 24px;
"
title=${ainfo.dtitle}
></i>
</div>
<button class="assist-button collect" style="color: black;" title=${binfo.title}>${binfo.name}</button>`;
return html;
},
create(module) {
const node = document.getElementById(
"assist-button-container"
);
node.children[0].insertAdjacentHTML("afterend", module);
return node;
},
main() {
return new Promise((resolve) => {
const tables = ["collection", "preference"];
//open db with r&w mode, if this db is not exist then create db
dataBaseInstance.initial(tables, true).then(
(result) => {
const ainfo = {};
ainfo.ltitle = "like this article";
ainfo.ldisplay = "block";
ainfo.ddisplay = "block";
ainfo.dtitle = "dislike this article";
const binfo = {};
binfo.title = "add this article to collection";
binfo.name = "Collect";
if (result === 0) {
resolve(
this.create(
this.preferenceModule(
escapeBlank(ainfo),
escapeBlank(binfo)
)
)
);
dataBaseInstance.close(false);
} else {
dataBaseInstance
.batchCheck(tables)
.then((results) => {
const c = results[0];
if (c) {
binfo.title =
"remove this acticle from collection list";
binfo.name = "Remove";
}
if (results[1]) {
const pref = results[1].value;
if (pref === 1) {
ainfo.ltitle =
"cancel like this article";
ainfo.ddisplay = "none";
} else if (pref === 0) {
ainfo.dtitle =
"cancel dislike this article";
ainfo.ldisplay = "none";
}
}
resolve(
this.create(
this.preferenceModule(
escapeBlank(ainfo),
escapeBlank(binfo)
)
)
);
dataBaseInstance.close();
});
}
},
(err) => {
console.log(err);
resolve(null);
}
);
});
},
},
syncData(mode, newValue) {
dataBaseInstance.initial(["preference"], true).then(
() => {
mode
? dataBaseInstance.update(newValue[0])
: dataBaseInstance.dele(false, newValue[0]);
dataBaseInstance.close();
},
(err) => console.log(err)
);
},
communication() {
const monitor = () => {
GM_addValueChangeListener(
"blockarticleB",
(name, oldValue, newValue, remote) => {
if (remote) {
this.syncData(true, newValue);
GM_setValue("blockarticleB", "");
}
}
);
GM_addValueChangeListener(
"removearticleB",
(name, oldValue, newValue, remote) => {
if (remote) {
this.syncData(false, newValue);
GM_setValue("removearticleB", "");
}
}
);
};
const r = GM_getValue("removearticleB");
const b = GM_getValue("blockarticleB");
r || b
? dataBaseInstance.initial(["preference"], true).then(
() => {
if (r && Array.isArray(r) && r.length > 0) {
for (const e of r)
dataBaseInstance.dele(false, e);
GM_setValue("removearticleB", "");
}
if (b && Array.isArray(b) && b.length > 0) {
for (const e of b) dataBaseInstance.update(e);
GM_setValue("blockarticleB", "");
}
monitor();
dataBaseInstance.close();
},
(err) => {
console.log(err);
dataBaseInstance.close();
}
)
: monitor();
},
creatAssistantEvent(node, mode) {
const Button = (button, m) => {
if (m) {
if (!this.readerMode) return;
this.createFrame();
button.style.display = "none";
mode = false;
} else {
let text = "";
let style = "";
const column = document.getElementById("column_lists");
let i = 0;
let title = "";
if (this.readerMode) {
text = "Reader";
style = "none";
title = "enter";
if (column) column.style.display = style;
i = 1;
//show content;
} else {
style = "block";
text = "Exit";
title = "exit";
if (column) {
column.style.display = style;
} else {
//hide content
this.Tabs.check(this.columnID).then(
(result) => !result && this.createFrame()
);
}
i = 2;
}
!this.modePrint && this.clearPage(i);
button.title = `${title} the reader mode`;
button.innerText = text;
this.readerMode = !this.readerMode;
GM_setValue("reader", this.readerMode);
mode &&
(button.previousElementSibling.style.display = style);
}
};
let i = node.children.length;
//reder
node.children[--i].onclick = function () {
Button(this, false);
};
//menu
if (mode) {
node.children[--i].onclick = function () {
Button(this, true);
};
}
//collection
let cReady = false;
const collectionClick = (button) => {
if (cReady) return;
cReady = true;
dataBaseInstance.initial(["collection"], true).then(
(result) => {
const text = button.innerText;
let s = "",
t = "";
if (text === "Remove") {
s = "Collect";
t = "add this article to collection list";
dataBaseInstance.dele(true);
this.columnsModule.recentModule.remove();
} else {
s = "Remove";
t = "remove this article frome collection list";
dataBaseInstance.additem(this.columnID);
this.columnsModule.recentModule.log("c");
}
button.innerText = s;
button.title = t;
dataBaseInstance.close();
cReady = false;
},
(err) => {
cReady = false;
console.log(err);
dataBaseInstance.close();
}
);
};
node.children[--i].onclick = function () {
collectionClick(this);
};
//pref
const cm = (mode) => {
const bid = location.pathname.slice(3);
if (mode) {
let b = GM_getValue("blockarticleA");
if (b && Array.isArray(b)) {
for (const e of b) if (e.name === bid) return;
} else b = [];
const r = GM_getValue("removearticleA");
if (r && Array.isArray(r)) {
const i = r.indexOf(bid);
if (i > -1) {
r.splice(i, 1);
GM_setValue("removearticleA", r);
}
}
const info = {};
info.name = bid;
info.type = "article";
info.from = this.columnID;
info.update = Date.now();
info.userID = this.authorID;
info.userName = this.authorName;
b.push(info);
GM_setValue("blockarticleA", b);
} else {
let r = GM_getValue("removearticleA");
if (r && Array.isArray(r) && r.length > 0) {
if (r.includes(bid)) return;
} else r = [];
const b = GM_getValue("blockarticleA");
if (b && Array.isArray(b)) {
const i = b.findIndex((e) => e.name === bid);
if (i > -1) {
b.splice(i, 1);
GM_setValue("blockarticleA", b);
}
}
r.push(bid);
GM_setValue("removearticleA", r);
}
};
let pReady = false;
const prefclick = (button, other, mode) => {
if (pReady) return;
pReady = true;
dataBaseInstance.initial(["preference"], true).then(
(result) => {
let title = "";
let f = false;
let cl = false;
if (other.style.display === "none") {
dataBaseInstance.dele(false);
title = `${
mode ? "like" : "dislike"
} this article`;
other.style.display = "block";
cl = mode;
} else {
const info = {};
info.pid = dataBaseInstance.pid;
info.update = Date.now();
info.userID = this.authorID;
info.from = this.columnID;
info.value = mode ? 1 : 0;
dataBaseInstance.update(info);
title = `cancel ${
mode ? "like" : "dislike"
} this article`;
other.style.display = "none";
f = !mode;
}
button.title = title;
dataBaseInstance.close();
!cl && cm(f);
pReady = false;
},
(err) => {
pReady = false;
console.log(err);
dataBaseInstance.close();
}
);
};
node.children[--i].onclick = (e) => {
const target = e.target;
const className = target.className;
if (className === "like")
prefclick(target, target.nextElementSibling, true);
else if (className === "dislike")
prefclick(target, target.previousElementSibling, false);
};
},
recordID: null,
dataRecord() {
this.recordID && clearTimeout(this.recordID);
this.recordID = setTimeout(() => {
this.recordID = null;
const pid = dataBaseInstance.pid;
dataBaseInstance.initial(["collection"], true).then(
(result) => {
result === 1 && dataBaseInstance.updateRecord(pid);
dataBaseInstance.close();
},
() => console.log("open database fail")
);
if (this.columnID) {
const f = GM_getValue("follow");
if (f && Array.isArray(f) && f.length > 0) {
for (const e of f) {
if (e.columnID === this.columnID) {
e.visitTime = Date.now();
let times = e.visitTimes;
e.visitTimes = times ? ++times : 2;
GM_setValue("follow", f);
break;
}
}
}
}
}, 15000);
},
reInject() {
let assist = document.getElementById("assist-button-container");
if (assist) {
assist.remove();
assist = null;
}
this.injectButton();
},
injectButton(mode) {
const name = this.readerMode ? "Exit" : "Reader";
const mbutton =
'<button class="assist-button siderbar" style="color: black;" title="show the siderbar">Menu</button>';
createButton(
name,
(this.readerMode ? "exit " : "enter ") + "the reader mode",
mode ? mbutton : "",
"right"
);
this.assistNewModule
.main()
.then(
(node) => node && this.creatAssistantEvent(node, mode)
);
this.dataRecord();
},
readerMode: false,
createFrame() {
if (this.columnID) {
this.Framework();
//const pinURL = `https://www.zhihu.com/api/v4/columns/${this.columnID}/pinned-items`;
const url = `https://www.zhihu.com/api/v4/columns/${this.columnID}/items`;
this.requestData(url);
this.Tabs.save(this.columnID);
}
},
columnID: null,
columnName: null,
main(mode = 0) {
if (mode > 0) {
window.onload = () => {
const f = this.homePage.initial;
if (f) {
this.columnID = this.homePage.ColumnID;
this.columnName = "Follow";
this.Framework();
Reflect.apply(this.homePage.add, this, [
f,
"f",
false,
]);
}
mode === 2 && this.subscribeOrfollow();
};
return false;
}
if (this.ColumnDetail) {
if (this.readerMode)
this.Tabs.check(this.columnID).then((result) =>
result
? this.injectButton(true)
: (this.createFrame(), this.injectButton())
);
else this.injectButton();
} else {
this.injectButton();
}
setTimeout(() => this.communication(), 5000);
},
},
colorAssistant: {
index: 0,
arr: null,
blue: true,
grad: 5,
get rgbRed() {
this.index -= this.grad;
if (this.index < 0 || this.index > 255) {
this.index = 0;
this.blue = true;
this.grad < 0 && (this.grad *= -1);
}
const s =
this.index > 233
? 5
: this.index > 182
? 4
: this.index > 120
? 3
: this.index > 88
? 2
: this.index > 35
? 1
: 0;
return `rgb(${this.index}, ${s}, 0)`;
},
redc: false,
get rgbBlue() {
this.index += this.grad;
if (this.index > 255) {
this.blue = false;
if (this.redc) {
this.index = 0;
this.grad *= -1;
this.redc = false;
} else {
this.grad < 0 && (this.grad *= -1);
this.index = 255;
this.redc = true;
}
}
return `rgb(0, 0, ${this.index})`;
},
get textColor() {
return this.blue ? this.rgbBlue : this.rgbRed;
},
setColor(text, color) {
return `<colorspan class="color-node" style="color: ${color} !important;">${text}</colorspan>`;
},
setcolorGrad(tlength) {
this.grad =
tlength > 500
? 1
: tlength > 300
? 2
: tlength > 180
? 3
: tlength > 120
? 4
: tlength > 80
? 5
: 6;
},
num: 0,
textDetach(text) {
this.setcolorGrad(text.length);
const reg = /((\d+[\.-\/]\d+([\.-\/]\d+)?)|\d{2,}|[a-z]{2,})/gi;
let result = null;
let start = 0;
let end = 0;
let tmp = "";
result = reg.exec(text);
const numaColors = ["green", "#8B008B"];
if (result) {
while (result) {
tmp = result[0];
end = reg.lastIndex - tmp.length;
for (start; start < end; start++)
this.arr.push(
this.setColor(text[start], this.textColor)
);
start = reg.lastIndex;
this.arr.push(this.setColor(tmp, numaColors[this.num]));
this.num = this.num ^ 1;
result = reg.exec(text);
}
end = text.length;
for (start; start < end; start++)
this.arr.push(
this.setColor(text[start], this.textColor)
);
} else {
for (let t of text)
this.arr.push(this.setColor(t, this.textColor));
}
},
nodeCount: 0,
getItem(node) {
//those tags will be ignored
const localName = node.localName;
const tags = ["a", "br", "b", "span", "code", "strong", "u"];
if (localName && tags.includes(localName)) {
this.arr.push(node.outerHTML);
this.nodeCount += 1;
return;
} else {
const className = node.className;
if (className && className === "UserLink") {
this.arr.push(node.outerHTML);
this.nodeCount += 1;
return;
}
}
if (node.childNodes.length === 0) {
const text = node.nodeValue;
text && this.textDetach(text);
} else {
//this is a trick, no traversal of textnode, maybe some nodes will lost content, take care
for (const item of node.childNodes) this.getItem(item);
this.arr.length > 0 &&
node.childNodes.length - this.nodeCount <
this.nodeCount + 2 &&
(node.innerHTML = this.arr.join(""));
this.arr = [];
}
},
resetColor() {
this.blue = !this.blue;
this.index = this.blue ? 0 : 255;
},
codeHightlight(node) {
const keywords = [
"abstract",
"arguments",
"await",
"boolean",
"break",
"byte",
"case",
"catch",
"char",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"double",
"else",
"enum",
"eval",
"export",
"extends",
"false",
"final",
"finally",
"float",
"for",
"function",
"goto",
"if",
"implements",
"import",
"in",
"instanceof",
"int",
"interface",
"let",
"long",
"native",
"new",
"null",
"package",
"private",
"protected",
"public",
"return",
"short",
"static",
"super",
"switch",
"synchronized",
"this",
"throw",
"throws",
"transient",
"true",
"try",
"typeof",
"var",
"void",
"volatile",
"while",
"with",
"yield",
];
const code = node.getElementsByClassName("language-text");
if (code.length === 0 || code[0].childNodes.length > 1) return;
let html = code[0].innerHTML;
const reg = /(["'])(.+?)(["'])/g;
const keyReg = /([a-z]+(?=[\s\(]))/g;
const i = html.length;
const h = (match, color) =>
`<hgclass class="hgColor" style="color:${color} !important;">${match}</hgclass>`;
html = html.replace(
reg,
(e) => e[0] + h(e.slice(1, -1), "#FA842B") + e.slice(-1)
);
html = html.replace(keyReg, (e) => {
if (e && keywords.includes(e))
return h(e, "rgb(0, 0, 252)");
return e;
});
const Reg = /\/\//g;
html = html.replace(Reg, (e) => h(e, "green"));
i !== html.length && (code[0].innerHTML = html);
},
rightClickCopyCode: {
checkCodeZone(target) {
if (target.className === "highlight") {
return target;
}
let p = target.parentNode;
let className = p.className;
let i = 0;
while (className !== "highlight") {
p = p.parentNode;
if (!p || i > 2) return null;
className = p.className;
i++;
}
return p;
},
main(node) {
node.oncontextmenu = (e) => {
if (e.button !== 2 || !e.ctrlKey) return;
const code = this.checkCodeZone(e.target);
if (code) {
e.preventDefault();
zhihu.clipboardClear.clear(code.innerText);
Notification(
"this code has been copied to clipboard",
"clipboard"
);
}
};
},
},
main() {
let holder = document.getElementsByClassName(
"RichText ztext Post-RichText"
);
if (holder.length === 0) {
console.log("get content fail");
return;
}
this.blue = Math.ceil(Math.random() * 100) % 2 === 0;
!this.blue && (this.index = 255);
holder = holder[0];
const tags = ["p", "ul", "li", "ol", "blockquote"];
const textNode = [];
let i = -1;
let code = false;
const tips = "Ctrl + Right mouse button to copy this code";
for (const node of holder.childNodes) {
i++;
const type = node.nodeType;
//text node deal with separately
if (type === 3) {
textNode.push(i);
continue;
} else if (!tags.includes(node.tagName.toLowerCase())) {
//the continuity of content is interrupted, reset the color;
this.resetColor();
if (node.className === "highlight") {
this.codeHightlight(node);
node.title = tips;
code = true;
}
continue;
}
this.arr = [];
this.nodeCount = 0;
this.getItem(node);
}
i = textNode.length;
if (i > 0) {
for (i; i--; ) {
let node = holder.childNodes[textNode[i]];
const text = node.nodeValue;
if (text) {
this.arr = [];
this.textDetach(text);
const iNode = document.createElement("colorspan");
holder.insertBefore(iNode, node);
iNode.outerHTML = this.arr.join("");
node.remove();
}
}
}
code && this.rightClickCopyCode.main(holder);
this.arr = null;
},
},
userPage: {
username: null,
changeTitle(mode) {
const title = document.getElementsByClassName(
"ProfileHeader-contentHead"
);
if (title.length === 0) return;
title[0].style.textDecoration =
mode === "Block" ? "none" : "line-through";
},
userManage(mode) {
let text = "";
const info = {};
if (mode === "Block") {
info.mode = "block";
blackName.push(this.username);
text = "add user to blackname successfully";
} else {
info.mode = "unblock";
const i = blackName.indexOf(this.username);
i > -1 && blackName.splice(i, 1);
text = "remove user from blackname successfully";
}
info.username = this.username;
GM_setValue("blacknamechange", info);
GM_setValue("blackname", blackName);
Notification(text, "blackName");
},
injectButton(name) {
createButton(name, name + " this user");
let assist = document.getElementById("assist-button-container");
assist.children[1].onclick = (e) => {
const button = e.target;
const n = button.innerText;
this.userManage(n);
button.innerText = n === "Block" ? "unBlock" : "Block";
this.changeTitle(button.innerText);
this.title = button.innerText + " this user";
};
assist = null;
name === "unBlock" && this.changeTitle(name);
},
changeButton(mode) {
const button = document.getElementById(
"assist-button-container"
);
if (!button) return;
const name = button.children[1].innerText;
if ((mode && name === "unBlock") || (!mode && name === "Block"))
return;
button.children[1].innerText = blackName.includes(this.name)
? "unBlock"
: "Block";
},
main() {
const profile = document.getElementsByClassName(
"ProfileHeader-name"
);
if (profile.length === 0) {
console.log("get usename id fail");
return;
}
this.username = `${profile[0].innerText}`;
this.username &&
this.injectButton(
blackName.includes(this.username) ? "unBlock" : "Block"
);
},
},
QASkeyBoardEvent() {
document.onkeydown = (e) => {
if (e.ctrlKey || e.altKey || e.shiftKey) return;
if (e.target.localName === "input") return;
const className = e.target.className;
if (className && className.includes("DraftEditor")) return;
const keyCode = e.keyCode;
keyCode === 192
? this.autoScroll.start()
: keyCode === 187
? this.autoScroll.speedUP()
: keyCode === 189
? this.autoScroll.slowDown()
: null;
};
},
pageOfQA(index, href) {
//inject as soon as possible; may be need to concern about some eventlisteners and MO
this.inputBox.controlEventListener();
this.addStyle(index);
index < 2 && this.antiLogin();
this.clearStorage();
window.onload = () => {
if (index !== 8) {
this.getData();
this.blackUserMonitor(index);
(index < 4
? !(index === 1 && href.endsWith("/waiting"))
: false) &&
setTimeout(() => {
this.Filter.main(index);
this.QASkeyBoardEvent();
}, 100);
(index === 6 || index === 7) && this.userPage.main();
}
this.inputBox.monitor();
index < 2 && (document.documentElement.style.overflow = 'auto');
};
},
blackUserMonitor(index) {
GM_addValueChangeListener(
"blackname",
(name, oldValue, newValue, remote) => {
if (!remote) return;
//mode => add user to blockname
blackName = newValue;
if (index === 6) {
const mode =
!oldValue || oldValue.length < newValue.length;
this.userPage.changeButton(mode);
} else {
this.Filter.userChange(index);
}
}
);
},
start() {
const pos = [
"/answer/",
"/question/",
"/topic/",
"/search",
"/column",
"/zhuanlan",
"/people/",
"/org/",
"/www",
];
const href = location.href;
const index = pos.findIndex((e) => href.includes(e));
let w = true;
let z = false;
let f = true;
(
(z = index === 5)
? href.endsWith("zhihu.com/")
? (f = this.Column.main(1))
: (w = !href.includes("/write"))
: index === 4
? true
: false
)
? this.zhuanlanStyle(z && href.includes("/p/"))
: index < 0
? null
: f && this.pageOfQA(index, href);
w && this.antiRedirect();
this.shade.start();
this.clipboardClear.event();
installTips();
},
};
zhihu.start();
})();