NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name 4chan Manual (You)
// @description Add arbitary posts to 4chan's reply tracking
// @author Marker
// @license MIT
// @namespace https://gist.github.com/marktaiwan
// @version 1.0
// @match https://boards.4chan.org/*
// @match https://boards.4channel.org/*
// @grant none
// @noframes
// @require https://openuserjs.org/src/libs/soufianesakhi/node-creation-observer.js
// @icon https://s.4cdn.org/image/favicon-ws.ico
// ==/UserScript==
/* global Main, Parser, NodeCreationObserver */
(function () {
'use strict';
const $ = (selector, parent = document) => parent.querySelector(selector);
const $$ = (selector, parent = document) => parent.querySelectorAll(selector);
const ADD_STRING = ' [Start tracking]';
const REM_STRING = ' [<b>Tracked</b>]';
function processPost(postEle) {
Parser.trackedReplies = Parser.getTrackedReplies(Main.board, Main.tid) ?? {};
const postId = $('.postInfo input[type="checkbox"]', postEle).name;
const postNum = $('.postInfo .postNum', postEle);
const isOwnPost = Object.keys(Parser.trackedReplies).some(key => key == '>>' + postId);
const button = document.createElement('a');
button.style.cursor = 'pointer';
button.innerHTML = isOwnPost ? REM_STRING : ADD_STRING;
button.dataset.ownPost = isOwnPost ? '1' : '0';
button.addEventListener('click', handleClick);
postNum.after(button);
}
function handleClick(e) {
const button = e.target;
const postEle = button.closest('.postContainer');
const threadId = Main.tid;
const postId = $('.postInfo input[type="checkbox"]', postEle).name;
if (button.dataset.ownPost == '1') {
delete Parser.trackedReplies['>>' + postId];
Parser.saveTrackedReplies(threadId, Parser.trackedReplies);
button.innerHTML = ADD_STRING;
button.dataset.ownPost = '0';
} else {
Parser.trackedReplies['>>' + postId] = '1';
Parser.saveTrackedReplies(threadId, Parser.trackedReplies);
button.innerHTML = REM_STRING;
button.dataset.ownPost = '1';
// Update replied posts by following the backlinks
const backlinks = $$('.backlink .quotelink', postEle);
for (const backlink of backlinks) {
const replyId = backlink.innerText.slice(2);
const replyEle = $('#pc' + replyId);
parseTrackedReplies(replyEle);
}
}
}
function parseTrackedReplies(postEle) {
for (const link of $$('.quotelink', postEle)) {
if (!Parser.trackedReplies[link.textContent] || link.closest('.backlink')) continue;
link.classList.add('ql-tracked');
link.textContent += ' (You)';
Parser.hasYouMarkers = true;
}
}
if (Main.tid) NodeCreationObserver.onCreation('.postContainer', processPost);
})();