// ==UserScript==
// @name Reports - Hotpocket Helper
// @version 6.5
// @description My report queue is augmented
// @author (cyg && fsn) == bffs 5ever
// @match https://reports.4chan.org/*
// @exclude https://reports.4chan.org/?action*
// @grant none
// ==/UserScript==
(function() {
'use strict';
var CFRQ = {
//don't touch anything below this line
firstRun: true,
buttons: {
"Threads":"cfrq-threads",
"OP":"cfrq-opsfilt",
"Reply":"cfrq-replyfilt"
}
};
CFRQ.refresh = function() {
CFRQ.stats = {};
CFRQ.threads = {};
CFRQ.xhr = {};
CFRQ.parsePage();
window.RQ.settingsList.autoSortThreads = ["Sort by thread on page load", false];
window.RQ.settingsList.autoScrollNav = ["Pin toolbar to top of page", true];
if (window.RQ.board) CFRQ.fetchThreadlist(window.RQ.board);
};
//Loop through each report, add buttons, link checkboxes, gather stats for thread sorting
CFRQ.parsePage = function() {
var report, reports = $.qsa("article");
for (var b in CFRQ.buttons) { //Reset all button states
if ($.hasClass($.id(CFRQ.buttons[b]), "disabled")) {
$.removeClass($.id(CFRQ.buttons[b]), "disabled");
}
}
for (var i = 0; (report = reports[i]); ++i) CFRQ.parseReport(report);
CFRQ.parseSlugs();
CFRQ.sortThreadsByReply();
CFRQ.firstRun = false;
if (window.RQ.settings.autoSortThreads) CFRQ.filterThreads();
};
CFRQ.parseSlugs = function() {
CFRQ.boardList = [];
var slug, slugs = $.cls("board-slug");
for (var i = 0; (slug = slugs[i]); ++i) {
CFRQ.boardList.push(slug.textContent);
CFRQ.stats[slug.textContent] = slug.getAttribute("data-tip");
slug.id = "board-slug-" + slug.innerHTML;
CFRQ.updateSlug(slug);
}
};
CFRQ.updateSlug = function(slug) {
var level, board = slug.id.split("board-slug-")[1];
var count = parseInt(CFRQ.stats[board]);
if (!count) {
slug.style.color = "#777";
return;
}
slug.style = { fontWeight: "", fontStyle: "", color: "" };
slug.style.color = ["hsl(", ((1-(((count += (count - (count * .4))) > 160 ? count = 160 : count)/160))*160).toString(10),",100%,50%)"].join("");
};
CFRQ.parseReport = function(report) {
if (report.getAttribute("cfrq-parent")) return;
var board = report.id.split("-")[1];
var resto_no = report.getAttribute("data-tid") || report.id.split("-")[2];
var resto = board + "-" + resto_no;
var controls = $.qs(".report-controls", report);
controls.appendChild(CFRQ.createInfoButton(board, resto_no));//Add info button to each report control pane
report.setAttribute("cfrq-parent", resto);
/* If it's an illegal report, add 1000 reports for this thread so it always appears at the top of thread sorting */
if (CFRQ.threads[resto]) CFRQ.threads[resto] += ($.hasClass(report, "report-cat-prio") ? 1000 : 1);
else CFRQ.threads[resto] = ($.hasClass(report, "report-cat-prio") ? 1000 : 1);
CFRQ.parseQuotelinks(report, resto_no, board);
};
CFRQ.createInfoButton = function(board, resto_no) {
var button = $.el("span");
button.textContent = "I";
button.className = "button";
var attr = {'data-board':board, 'data-cmd':"show-preview", 'data-resto':resto_no};
for (var k in attr) button.setAttribute(k, attr[k]);
$.on(button, "mouseover", CFRQ.threadStatsMouseover);
return button;
};
CFRQ.threadStatsMouseover = function(t) {
t = t.target;
var board = t.getAttribute("data-board");
var pid = t.getAttribute("data-resto");
//if (t.getAttribute("data-last-retrieved") > ($.now() - 30)) Tip.timeout = setTimeout(Tip.show, Tip.delay, t, CFRQ.xhr[board+pid]);
if (t.getAttribute("data-last-retrieved") > ($.now() - 30)) Tip.show(t, CFRQ.xhr[board+pid]);
$.xhr("GET", `https://a.4cdn.org/${board}/thread/${pid}.json`, {
onreadystatechange: function() {
if (this.status == 200 && this.readyState == 4) {
t.setAttribute("data-last-retrieved", $.now());
CFRQ.xhr[board+pid] = CFRQ.parseThreadStats(this.responseText, board, pid);
Tip.timeout = setTimeout(Tip.show, Tip.delay, t, CFRQ.xhr[board+pid]);
}
},
onerror: function() {
window.RQ.notify("That post doesn't exist anymore.");
}
});
};
CFRQ.parseThreadStats = function(responseText, board, pid) {
var ret = JSON.parse(responseText);
var posts = ret.posts;
var op = ret.posts[0];
var replies = (op.replies >= 1 ? `Replies: ${op.replies}` : `No replies`);
var images = (op.images >= 1 ? `Images: ${op.images}` : `No images`);
var opAge = `Thread Age: ${$.ago(op.time)}`;
var lastReply = `Last reply: ${$.ago(posts[posts.length -1].time)} ago`;
var info = `${replies}\n${images}\n${opAge}\n${lastReply}\n\nClick to preview OP`;
return info;
};
CFRQ.parseQuotelinks = function(report, resto, board) {
//Post links
var i, qlink, tmp, qlinks = $.cls("quotelink", report), tmptxt = "";
for (i = 0; (qlink = qlinks[i]); ++i) {
qlink.style = "pointer-events:all;cursor:pointer;";
var href = qlink.getAttribute("href");
qlink.target = "_blank";
if (!href) return;
if (qlink.textContent.match(/^>>>/)) {
board = qlink.textContent.split("/")[1];
tmp = href.split("/");
if (tmp.length < 6) {
return;
} else {
resto = tmp[5].split("#")[0];
tmptxt = " (Cross-board)";;
}
} else if (href.includes("thread") && !qlink.getAttribute("data-resto")) {
resto = href.split("/")[3].split("#")[0];
tmptxt = " (Cross-thread)";
}
var linkParts = href.split("/");
var targetPost = linkParts[linkParts.length - 1].split("#p")[1];
var attr = {'data-cmd': 'cfrq-quotelink', 'data-board':board, 'data-resto':resto, 'data-target': targetPost, 'href': "https://boards.4chan.org/" + board + "/thread/" + resto + "#p" + targetPost};
for (var k in attr) qlink.setAttribute(k, attr[k]);
qlink.textContent += tmptxt;
}
qlinks = $.cls("deadlink", report);
if (qlinks.length != 0) {
for (i = 0; (qlink = qlinks[i]); ++i) {
qlink.style = "color:red;";
qlink.setAttribute("href", "#p404");
}
}
};
CFRQ.fetchThreadlist = function(board) {
if (CFRQ.xhr[board]) return;
$.xhr("GET", `https://a.4cdn.org/${board}/threads.json`, {
onreadystatechange: function() {
if (this.status == 200 && this.readyState == 4) {
CFRQ.xhr[board] = JSON.parse(this.responseText);
}
},
onerror: function() {
window.RQ.notify("Connection error");
}
});
};
CFRQ.fetchBoardList = function() {
if ($.getItem("cfrq-boardlist")) { //Have it already breh
CFRQ.boardlist = JSON.parse($.getItem("cfrq-boardlist"));
return;
}
$.xhr("GET", `https://a.4cdn.org/boards.json`, {
onreadystatechange: function() {
if (this.status == 200 && this.readyState == 4) {
CFRQ.boardlist = JSON.parse(this.responseText);
$.setItem("cfrq-boardlist", this.responseText);
}
},
onerror: function() {
window.RQ.notify("Connection error");
}
});
};
CFRQ.incrementStats = function(board, amount) {
return (CFRQ.stats[board] ? CFRQ.stats[board] += amount : CFRQ.stats[board] = amount);
};
CFRQ.decrementStats = function(board, amount) {
return (CFRQ.stats[board] && CFRQ.stats[board] >= 1 ? CFRQ.stats[board] += amount : null);
};
//Add filter buttons to the toolbar at the top /* delete if added natively */
CFRQ.addFilters = function() {
var btntxt,
buttoncbks = {"OP": CFRQ.filterReport, "Reply": CFRQ.filterReport, "Threads": CFRQ.filterThreads},
buttontips = {"OP": "Show OPs only", "Reply": "Show Replies only", "Threads": "Group all by thread"};
for (btntxt in CFRQ.buttons) {
var el = $.el("span");
el.className = "button button-light left";
el.innerHTML = btntxt;
el.id = CFRQ.buttons[btntxt];
el.setAttribute("data-tip", buttontips[btntxt]);
$.id('refresh-btn').parentNode.insertBefore(el, $.id('refresh-btn').nextSibling);
$.on($.id(CFRQ.buttons[btntxt]), "click", buttoncbks[btntxt]);
}
};
CFRQ.filterReport = function(el) {
var btn = el.target;
CFRQ.resetFilter();
if ($.hasClass(btn, "disabled")) {
$.removeClass(btn, "disabled");
return;
}
var report, reports = $.cls("report"), showRepliesOnly = (btn.id == "cfrq-replyfilt");
for (var i = 0; (report = reports[i]); ++i) {
if (showRepliesOnly && !report.hasAttribute("data-tid")) {
$.addClass(report, "hidden-i");
} else if (!showRepliesOnly && report.hasAttribute("data-tid")) {
$.addClass(report, "hidden-i");
}
}
$.addClass(btn, "disabled");
};
CFRQ.sortThreadsByReply = function() {
//Thread sorting by report count
var tempCounts = [];
var tempThreads = [];
for (var thread in CFRQ.threads) {
tempCounts.push(CFRQ.threads[thread]);
}
tempCounts.sort(function(a, b) {
return b - a;
});
for (var i = 0; (thread = tempCounts[i]); ++i) {
for (var x in CFRQ.threads) {
if (CFRQ.threads[x] == thread ) {
tempThreads.push(x);
delete CFRQ.threads[x];
}
}
}
CFRQ.sortedThreads = tempThreads;
return;
};
CFRQ.filterThreads = function() {
if (!CFRQ.createThreadHeaders()) return;
var thread, report;
for (let i = 0; (thread = CFRQ.sortedThreads[i]); i++) { //fix me
var reports = $.qsa('article[cfrq-parent="' + thread + '"]');
for (var j = reports.length - 1; (report = reports[j]); j--) { //Add thread reports under header
$.id('items').removeChild(report);
var th_id = $.id("cfrq-th-" + thread);
if (th_id) {
$.id("items").insertBefore(report, th_id.nextSibling);
} else {
$.id("items").insertBefore(report, $.id("cfrq-th-remainder").nextSibling);
}
}
}
};
CFRQ.createThreadHeaders = function() {
var header, headers = $.cls("cfrq-thread-header");
if (headers.length) {
$.toggleXls($.id("cfrq-threads"), "disabled");
for (let i = 0; (header = headers[i]); ++i) {
//header.parentNode.removeChild(header);
$.toggleXls(header, "hidden-i");
}
return false; //stop
}
$.toggleXls($.id("cfrq-threads"), "disabled");
var thread, reports, report;
for (let i = 0; (thread = CFRQ.sortedThreads[i]); i++) {
reports = $.qsa('article[cfrq-parent="' + thread + '"]');
if (reports.length > 1 || $.hasClass(reports[0], "report-cat-prio")) {
header = $.el("header");
let board = thread.split("-")[0];
let tid = thread.split("-")[1];
let boardLink = `<a href="${window.RQ.linkToPost(board, tid, 0)}" target="_blank">>>/${board}/${tid}</a>`;
header.innerHTML = `${reports.length} ${$.pluralise(reports.length, " report", " reports")} for ${boardLink}`;
header.className = "cfrq-thread-header";
header.id = "cfrq-th-" + thread;
$.id("items").insertBefore(header, $.id('items').childNodes[i]);
}
}
reports = $.qsa('article[cfrq-parent="' + thread + '"]');
header = $.el("header");
header.innerHTML = `Other Reports`;
header.className = "cfrq-thread-header";
header.id = "cfrq-th-remainder";
$.id("items").insertBefore(header, $.id('items').childNodes[CFRQ.sortedThreads.length]);
return true;
};
CFRQ.resetFilter = function() {
var report, reports = $.cls("report");
for (var i = 0; (report = reports[i]); ++i) {
if ($.hasClass(report, "hidden-i")) $.removeClass(report, "hidden-i");
}
};
CFRQ.clearTooltips = function() {
$.qsa(".tip-top").forEach(function(top) {
try {
top.parentNode.removeChild(top);
} catch (e) {
//shut up
}
});
};
CFRQ.onScroll = function() {
var st = window.pageYOffset || document.documentElement.scrollTop;
if (st > 20 && window.RQ.settings.autoScrollNav){
$.id('menu').style = 'position:fixed;top:0;right:0;left:0;z-index:99;';
} else {
if (window.pageYOffset < 20 || !window.RQ.settings.autoScrollNav) $.id('menu').style = '';
}
};
CFRQ.parseContext = function() {
var i;
var content = $.id("context-preview");
if (!CFRQ.viewThread) {
for (i = 0; i < content.childNodes.length; i++) {
if (!content.childNodes[i].classList.contains("focused")) {
content.childNodes[i].classList.add("hidden");
} else {
CFRQ.parseQuotelinks(content.childNodes[i], content.childNodes[0].id.split("-")[2], CFRQ.contextStart[0]);
}
}
var post = $.id("context-" + CFRQ.contextStart[0] + "-" + CFRQ.contextStart[2]);
//if post is null, it's because we've opened the context for a different thread than the original post
if (post) {
CFRQ.contextShow(post, CFRQ.contextStart[1], CFRQ.contextStart[0]);
}
} else {
for (i = 0; i < content.childNodes.length; i++) {
CFRQ.parseQuotelinks(content.childNodes[i], content.childNodes[0].id.split("-")[2], content.childNodes[i].id.split("-")[1]);
}
}
};
CFRQ.contextHide = function(post, resto, board) {
$.addClass(post, "hidden");
$.removeClass(post, "focused");
var quotelinks = $.cls("quotelink", post);
for (var i = 0; i < quotelinks.length; i++) {
if (quotelinks[i].getAttribute("data-target")) {
var npost = $.id("context-" + board + "-" + quotelinks[i].getAttribute("data-target"));
if (npost.classList && !npost.classList.contains("hidden")) {
CFRQ.contextHide(npost, resto, board);
}
}
}
};
CFRQ.contextShow = function(post, resto, board) {
$.removeClass(post, "hidden");
CFRQ.parseQuotelinks(post, resto, board);
};
CFRQ.toggleContextPost = function(board, resto, no) {
var post = $.id("context-" + board + "-" + no);
if (!post) {
window.RQ.showContext(board, resto, no);
return;
}
if ($.hasClass(post, "hidden")) {
CFRQ.contextShow(post, resto, board);
$.addClass(post, "focused");
return true;
} else {
CFRQ.contextHide(post, resto, board);
return false;
}
};
CFRQ.toggleContextFocus = function(board, resto, no) {
$.id("context-" + board + "-" + no).classList.add("focused");
};
CFRQ.contextStartup = function(el, board, resto, no) {
CFRQ.viewThread = false;
CFRQ.contextStart = [];
CFRQ.contextStart.push(board);//start post board
CFRQ.contextStart.push(resto);//start post resto
CFRQ.contextStart.push((el.parentNode.parentNode.id.split("-")[2] ? el.parentNode.parentNode.id.split("-")[2] : el.parentNode.parentNode.parentNode.id.split("-")[2]));//start post no
window.RQ.showContext(board, resto, no);
};
CFRQ.manageExistingContext = function(el, board, resto, no) {
if (CFRQ.viewThread) {
var focus = $.cls("focused", el.parentNode.parentNode.parentNode.parentNode);
for (var i = 0; i < focus.length; i++) focus[i].classList.remove("focused");
var nf = $.id("context-" + el.getAttribute('data-board') + "-" + el.getAttribute('data-target'));
if (nf) {
nf.classList.add("focused");
nf.scrollIntoView();
} else {
window.RQ.showContext(board, resto, no);
}
} else {
if (CFRQ.toggleContextPost(board, resto, no)) {
el.parentNode.parentNode.classList.remove("focused");
} else {
el.parentNode.parentNode.classList.add("focused");
}
}
};
CFRQ.openPostInContext = function(el, target) {
if (el.ctrlKey) return;
el.preventDefault();
el = el.target;
var board = el.getAttribute('data-board');
var resto = el.getAttribute('data-resto');
var no = (target) ? target : el.getAttribute('data-target');
if (RQ.mode != RQ.MODE_CONTEXT) CFRQ.contextStartup(el, board, resto, no);
else CFRQ.manageExistingContext(el, board, resto, no);
};
CFRQ.rqContextStartup = function(panel) {
for (let i = 0; i < panel.childNodes.length; i++) {
panel.childNodes[i].classList.remove("hidden");
}
};
CFRQ.onClick = function(el) {
var t = el.target;
switch(t.getAttribute("data-cmd")) {
case "show-preview":
CFRQ.openPostInContext(el, t.getAttribute('data-resto'));
break;
case "show-context":
CFRQ.viewThread = true;
CFRQ.rqContextStartup($.id("context-preview"));
break;
case "cfrq-quotelink":
CFRQ.openPostInContext(el, null);
break;
default: break;
}
};
//modified helpers.js
if($)$.remByID=function(e){var n=$.id(e);n&&n.parentNode.removeChild(n)};else{var $={id:function(e){return document.getElementById(e)},remByID:function(e){var n=$.id(e);n&&n.parentNode.removeChild(n)},cls:function(e,n){return(n||document).getElementsByClassName(e)},byName:function(e){return document.getElementsByName(e)},tag:function(e,n){return(n||document).getElementsByTagName(e)},el:function(e){return document.createElement(e)},el2:function(e,n,t,i){var o,r;for(r in o=$.el(e),t)o.setAttribute(r,t[r]);return i&&(o.innerHTML=i),n.appendChild(o)},frag:function(){return document.createDocumentFragment()},qs:function(e,n){return(n||document).querySelector(e)},qsa:function(e,n){return(n||document).querySelectorAll(e)},extend:function(e,n){for(var t in n)e[t]=n[t]}};document.documentElement.classList?($.hasClass=function(e,n){return e.classList.contains(n)},$.addClass=function(e,n){e.classList.add(n)},$.removeClass=function(e,n){e.classList.remove(n)}):($.hasClass=function(e,n){return-1!=(" "+e.className+" ").indexOf(" "+n+" ")},$.addClass=function(e,n){e.className=""===e.className?n:e.className+" "+n},$.removeClass=function(e,n){e.className=(" "+e.className+" ").replace(" "+n+" ","")}),$.on=function(e,n,t){e.addEventListener(n,t,!1)},$.dispatch=function(e,n){var t=document.createEvent("Event");t.initEvent(e,!1,!1),document.dispatchEvent(t)},$.off=function(e,n,t){e.removeEventListener(n,t,!1)},$.xhr=function(e,n,t,i,o,r){var s,c,a;if(o=!!o||0,(c=new XMLHttpRequest).open(e,n,!0),t)for(s in t)c[s]=t[s];if(i)if("string"==typeof i)c.setRequestHeader("Content-type","application/x-www-form-urlencoded");else{for(s in a=new FormData,i)a.append(s,i[s]);i=a}else i=null;return o&&(c.withCredentials=!0),c.send(i),c},$.getItem=function(e){return localStorage.getItem(e)},$.setItem=function(e,n){return localStorage.setItem(e,n)},$.removeItem=function(e){return localStorage.removeItem(e)},$.getCookie=function(e){var n,t,i,o;for(o=e+"=",i=document.cookie.split(";"),n=0;t=i[n];++n){for(;" "==t.charAt(0);)t=t.substring(1,t.length);if(0===t.indexOf(o))return decodeURIComponent(t.substring(o.length,t.length))}return null},$.toggleXls=function(e,n){$.hasClass(e,n)?$.removeClass(e,n):$.addClass(e,n)},$.getToken=function(){return document.body.getAttribute("data-tkn")},$.capitalise=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},$.pluralise=function(e,n,t){return 1===e?n||"":t||"s"},$.prettyBytes=function(e){return e>=1048576?(0|e/1048576*100+.5)/100+" MB":e>1024?(0|e/1024+.5)+" KB":e+" B"},$.ago=function(e){var n,t,i,o;return(n=Date.now()/1e3-e)<1?"1 sec":60>n?(0|n)+" secs":3600>n?(t=0|n/60)>1?t+" mins":"1 min":86400>n?(i=(t=0|n/3600)>1?t+"":"1",i+="."+(o=0|n/60-60*t)+" hrs"):(i=(t=0|n/86400)>1?t+"":"1",(o=0|n/3600-24*t)>=1&&(i+="."+o+" days"),i+"")},$.now=function(){return Math.round((new Date).getTime()/1e3)},$.length=function(e){return Object.keys(e).length},$.hidden="hidden",$.visibilitychange="visibilitychange",void 0===document.hidden&&("mozHidden"in document?($.hidden="mozHidden",$.visibilitychange="mozvisibilitychange"):"webkitHidden"in document?($.hidden="webkitHidden",$.visibilitychange="webkitvisibilitychange"):"msHidden"in document&&($.hidden="msHidden",$.visibilitychange="msvisibilitychange")),$.docEl=document.documentElement}
var states = ["complete", "loaded", "interactive"];
if (states.indexOf(document.readyState) != -1) CFRQ.addFilters();
$.on(document, "4chanReportsReady", CFRQ.refresh);
$.on(document, "4chanReportContextReady", CFRQ.parseContext);
$.on(document, "scroll", CFRQ.onScroll);
$.on(document, "click", CFRQ.onClick);
CFRQ.fetchBoardList();
})();