Skip to content

Instantly share code, notes, and snippets.

@Kethsar
Last active December 18, 2023 01:03
Show Gist options
  • Save Kethsar/3212a5a584df9135a32db1da17c96025 to your computer and use it in GitHub Desktop.
Save Kethsar/3212a5a584df9135a32db1da17c96025 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name 4chan Request Window
// @namespace KethRequest
// @version 0.1
// @description Show all requests that start with /r/
// @author Kethsar
// @match https://boards.4channel.org/a/thread/*
// @match http://boards.4channel.org/a/thread/*
// @match https://boards.4chan.org/a/thread/*
// @match http://boards.4chan.org/a/thread/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
var body = document.getElementsByTagName("body")[0],
html = document.getElementsByTagName("html")[0],
reqdiv = document.createElement("div"),
reqhead = document.createElement("div"),
reqbody = document.createElement("div"),
closeBtn = document.createElement("button"),
reqbottom = document.createElement("div"),
reqtable = document.createElement("table"),
rtheadtable = document.createElement("table"),
magich,
magicw,
refreshed = false,
postsRoot = document.getElementsByClassName("thread")[0],
reqRE = new RegExp('/r/(.*)', 'i'),
redditRE = new RegExp('reddit.com', 'i'),
reqs = [];
function init()
{
createReqWindow();
registerEventHandlers();
createStyles();
createOptionTab();
}
function createStyles()
{
let css = document.createElement("style"),
height = window.innerHeight / 2,
left = window.innerWidth - 425;
css.type = "text/css";
css.innerHTML = "#reqdiv { position: fixed; height: " + height + "px; width: 400px; border: 1px solid grey; background-color: inherit; top: 125px; left: " + left + "px; }\n" +
"#reqhead { height: 15px; background-color: grey; padding: 2px; cursor: move; text-align: center; }\n" +
"#reqbody { padding: 2px; overflow: auto; }\n" +
"#closeBtn { float: right; font-weight: bold; }\n" +
".reqBtn { height: 15px; background-color: darkgrey; border: none; cursor: pointer; }\n" +
"#reqbottom { height: 4px; cursor: ns-resize; }\n" +
"#reqtable { width: 100%; table-layout: fixed; }\n" +
".cbhead { width: 10%; }\n" +
".cbcell { width: 10%; text-align: center; }\n" +
"#rtext { width: 80%; }\n" +
"#rthead { width: 100%; }";
document.head.appendChild(css);
}
function createReqWindow()
{
reqdiv.id = "reqdiv";
reqhead.id = "reqhead";
reqbottom.id = "reqbottom";
reqtable.id = "reqtable";
reqbody.id = "reqbody";
rtheadtable.id = "rthead";
closeBtn.innerText = "X";
closeBtn.id = "closeBtn";
closeBtn.classList.add("reqBtn");
let rthead = document.createElement("tr"),
th1 = document.createElement("th"),
th2 = document.createElement("th"),
th3 = document.createElement("th");
th3.id = "rtext";
th1.classList.add("cbhead");
th2.classList.add("cbhead");
th1.innerText = "Q'd";
th2.innerText = "Skip";
th3.innerText = "Request";
rthead.append(th1);
rthead.append(th2);
rthead.append(th3);
rtheadtable.append(rthead);
reqhead.append("Requests");
reqhead.append(closeBtn);
reqbody.append(reqtable);
reqdiv.append(reqhead);
reqdiv.append(rtheadtable);
reqdiv.append(reqbody);
reqdiv.append(reqbottom);
hideRequestsWindow();
body.append(reqdiv);
}
function createOptionTab()
{
// If headerbar, do stuff for it, else navtopright
let reqa = document.createElement("a");
reqa.innerText = "Requests";
reqa.addEventListener("click", showRequestsWindow);
reqa.style.cursor = "pointer";
let navright = document.getElementById("shortcuts");
if (!navright)
navright = document.getElementById("navtopright");
navright.append(" [");
navright.appendChild(reqa);
navright.append("]");
}
function registerEventHandlers()
{
reqhead.addEventListener("mousedown", reqheadMouseDown);
window.addEventListener("mouseup", windowMouseUp);
html.addEventListener("mousemove", mouseMoveHandler);
reqbottom.addEventListener("mousedown", reqbottomMouseDown);
window.addEventListener("resize", resizeHandler);
closeBtn.addEventListener("click", hideRequestsWindow);
}
function reqbottomMouseDown(e)
{
e.preventDefault();
reqbottom.classList.add("drag");
}
function reqheadMouseDown(e)
{
e.preventDefault();
reqhead.classList.add("drag");
magicw = e.screenX - reqdiv.offsetLeft;
magich = e.screenY - reqdiv.offsetTop;
}
function windowMouseUp(e)
{
reqhead.classList.remove("drag");
reqbottom.classList.remove("drag");
}
function resizeReqWindowHeight(e)
{
e.preventDefault();
let reqheadH = reqhead.offsetHeight,
reqbotH = reqbottom.offsetHeight,
reqtbheadH = rtheadtable.offsetHeight,
reqTop = reqdiv.offsetTop,
mouseY = e.clientY,
newH = mouseY - reqTop;
newH = newH < 100 ? 100 : newH;
reqdiv.style.height = newH + "px";
reqbody.style.height = (newH - reqheadH - reqbotH - reqtbheadH) + "px";
}
function moveRequestWindow(e)
{
e.preventDefault();
let w = e.screenX - magicw,
h = e.screenY - magich,
reqw = window.innerWidth - reqdiv.offsetWidth,
reqh = window.innerHeight - reqdiv.offsetHeight;
if (h > reqh)
reqdiv.style.top = reqh + "px";
else if (h > 0)
reqdiv.style.top = h + "px";
else
reqdiv.style.top = "0px";
if (w > reqw)
reqdiv.style.left = reqw + "px";
else if (w > 0)
reqdiv.style.left = w + "px";
else
reqdiv.style.left = "0px";
}
function mouseMoveHandler(e)
{
if (reqhead.classList.contains("drag"))
{
moveRequestWindow(e);
}
else if (reqbottom.classList.contains("drag"))
{
resizeReqWindowHeight(e);
}
}
function resizeHandler(e)
{
if (reqdiv.offsetLeft + reqdiv.offsetWidth > window.innerWidth)
reqdiv.style.left = window.innerWidth - reqdiv.offsetWidth + "px";
if (reqdiv.offsetTop + reqdiv.offsetHeight > window.innerHeight)
reqdiv.style.top = window.innerHeight - reqdiv.offsetHeight + "px";
}
function showRequestsWindow()
{
let topH = 0,
rtheadH = 0,
reqbodyH = 0,
reqdH = 0;
reqdiv.style.display = "block";
topH = reqhead.offsetHeight + 6;
rtheadH = rtheadtable.offsetHeight;
reqdH = reqdiv.offsetHeight;
reqbodyH = reqdH - topH - rtheadH;
reqbody.style.height = reqbodyH + "px";
resetWindowPosition();
if (!refreshed)
{
refreshRequestList();
createObserver();
}
}
function hideRequestsWindow()
{
reqdiv.style.display = "none";
}
function resetWindowPosition()
{
reqdiv.style.top = "";
reqdiv.style.left = "";
}
function refreshRequestList()
{
if (refreshed)
return;
let posts = postsRoot.children,
i = 1;
while (i < posts.length)
{
let post = posts[i];
checkForRequest(post);
i++;
}
refreshed = true;
}
function checkForRequest(post)
{
let bquote = post.getElementsByTagName("blockquote")[0],
postText = null,
lines = null;
if (!bquote)
return;
postText = bquote.innerText;
lines = postText.split('\n');
for (let j = 0; j < lines.length; j++)
{
let match = lines[j].match(reqRE);
if(match && lines[j].search(redditRE) < 0)
addRequest(match[1].trim(), post.id);
}
}
function addRequest(text, postid)
{
if (reqs.includes(text.toLowerCase()))
return
let row = document.createElement("tr"),
qcell = document.createElement("td"),
nocell = document.createElement("td"),
reqcell = document.createElement("td"),
req = document.createElement("a");
if(!text)
text = "[empty, check post]";
else
reqs.push(text.toLowerCase());
qcell.innerHTML = '<input type="checkbox">';
qcell.classList.add("cbcell");
nocell.innerHTML = '<input type="checkbox">';
nocell.classList.add("cbcell");
req.href = "#" + postid;
req.innerText = text;
reqcell.append(req);
row.append(qcell);
row.append(nocell);
row.append(reqcell);
reqtable.append(row);
}
function createObserver()
{
let obsConfig = { childList: true };
let observer = new MutationObserver(observerCallback);
observer.observe(postsRoot, obsConfig);
}
function observerCallback(mutationsList, observer)
{
for (let i = 0; i < mutationsList.length; i++)
{
let mut = mutationsList[i],
nodes = mut.addedNodes;
for (let n of nodes)
{
if (n.classList.contains("postContainer"))
checkForRequest(n);
}
}
}
init();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment