// ==UserScript== // @name 8chan.moe Penis Grabber // @namespace gock // @match https://8chan.moe/* // @match https://8chan.se/* // @grant none // @version 0 // @author gock // @license MIT // @description love from /vyt/ <3 // @run-at document-start // @noframes // @downloadURL none // ==/UserScript== //copied and modified from thread.js to close qr after posting (closes on error too?) function closeQrAfterPosting() { window.addEventListener("load", () => { //clobbers thread.replyCallback.stop = function() { thread.replyButton.value = thread.originalButtonText; qr.setQRReplyText(thread.originalButtonText); thread.replyButton.disabled = false; qr.setQRReplyEnabled(true); qr.removeQr(); }; }); } //move qr button to above everything else so you just need to press tab once function moveReplyButton() { document.addEventListener("DOMContentLoaded", () => { const qrButton = document.getElementById("quick-reply").childNodes[2].childNodes[13]; const target = document.getElementById("qrFilesBody"); const parent = document.getElementById("quick-reply").childNodes[2]; parent.insertBefore(qrButton, target); }); } //move qr button to above everything else so you just need to press tab once function moveThreadInfo() { document.addEventListener("DOMContentLoaded", () => { const threadInfo = document.getElementById("postCount").parentNode; const target = document.getElementById("navOptionsSpan"); const parent = document.getElementById("dynamicHeaderThread"); parent.insertBefore(threadInfo, target); document.getElementById("fileCount").setAttribute("title", "files"); document.getElementById("idCount").setAttribute("title", "IDs"); document.getElementById("postCount").setAttribute("title", "Posts"); var style = document.createElement("style"); style.textContent = ` .threadInfo { color: var(--text-color); user-select: none; } .threadInfo::before { content: "["; color: var(--navbar-text-color); padding-right: 5px; } .threadInfo::after { content: "]"; color: var(--navbar-text-color); padding-left: 5px; padding-right: 3px; } .threadInfo #postCount::before { content: "" ; } .threadInfo #idCount::before { content: "/" ; color: var(--navbar-text-color); padding-left: 5px; padding-right: 5px; } .threadInfo #fileCount::before { content: "/" ; color: var(--navbar-text-color); padding-left: 5px; padding-right: 5px; } `; document.head.appendChild(style); }); } //counterpart to posting.markPostAsYou() function unmarkPostAsYou(id, obj) { var post = obj || document.getElementById(+id); if (!post) return; var author = post.querySelector(".linkName"); if (!author) return; author.classList.remove("youName"); } //counterpart to api.addYou() function removeYou(boardUri, postId) //(string, int) { var yous = JSON.parse(localStorage[boardUri + "-yous"] || "[]"); var index = yous.indexOf(postId); var indexPosting = posting.yous.indexOf(postId); if (index === -1 || indexPosting === -1) { console.log("tried to remove nonexistant (you)"); return; } yous.splice(index, 1); posting.yous.splice(indexPosting, 1); //in place localStorage.setItem(boardUri + "-yous", JSON.stringify(yous)); } //fixes clicking on IDs to highlight them //also adds nicknames function fixClickOnId() { new MutationObserver((_, observer) => { const scriptTag = document.querySelector("script[src*='multiboardMenu.js']"); if (scriptTag) { observer.disconnect(); //copypaste and modified posting.processIdLabel = function(label) { if (label === undefined) return; var id = label.innerText; var array = posting.idsRelation[id] || []; var cell = label.parentNode.parentNode.parentNode; //add nickname probably best to untie this from this method //end nickname if (cell.parentNode.className === 'inlineQuote' || cell.parentNode.className === 'quoteTooltip') { } else { posting.idsRelation[id] = array; //bad performance ? if (array.indexOf(cell) === -1) { array.push(cell); } } label.onmouseover = function() { label.innerText = id + ' (' + array.length + ')'; } label.onmouseout = function() { label.innerText = id; } label.onclick = function() { var index = posting.highLightedIds.indexOf(id); //window.location.hash = '_'; // whats the point? if (index > -1) { posting.highLightedIds.splice(index, 1); } else { posting.highLightedIds.push(id); } for (var i = 0; i < array.length; i++) { var cellToChange = array[i]; if (cellToChange.className === 'innerOP') { continue; } if (index > -1) { /*? 'innerPost' : 'markedPost';*/ cellToChange.classList.remove("markedPost"); } if (index === -1) { /*? 'innerPost' : 'markedPost';*/ cellToChange.classList.add("markedPost"); } } }; }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll(".linkSelf").forEach(e => posting.parseExistingPost(e, true, false, true, true)); }); } else { document.querySelectorAll(".linkSelf").forEach(e => posting.parseExistingPost(e, true, false, true, true)); } } }).observe(document.documentElement, {childList: true, subtree:true }); } function addWatcherShortcut() { document.addEventListener("DOMContentLoaded", () => { document.addEventListener("keydown", (e) => { if (e.key === "w") { //copypasted from watcher.processOP var op = document.querySelector(".opHead.title"); var checkBox = op.getElementsByClassName('deletionCheckBox')[0]; var nameParts = checkBox.name.split('-'); var board = nameParts[0]; var thread = nameParts[1]; var storedWatchedData = watcher.getStoredWatchedData(); var boardThreads = storedWatchedData[board] || {}; if (boardThreads[thread]) { return; } var subject = op.getElementsByClassName('labelSubject'); var message = op.getElementsByClassName('divMessage')[0]; var label = (subject.length ? subject[0].innerText : null) || message.innerHTML.replace(/(<([^>]+)>)/ig, "").substr(0, 16).trim(); if (!label.length) { label = null; } else { label = label.replace(/[<>"']/g, function(match) { return api.htmlReplaceTable[match] }); } boardThreads[thread] = { lastSeen : new Date().getTime(), lastReplied : new Date().getTime(), label : label }; storedWatchedData[board] = boardThreads; localStorage.watchedData = JSON.stringify(storedWatchedData); watcher.addWatchedCell(board, thread, boardThreads[thread]); } }); }); } function keepWatcherOpen() { document.addEventListener("DOMContentLoaded", () => { document.getElementById("watchedMenu").style.display = "flex"; }); } function toggleYou(post) { if (posting.yous.indexOf(parseInt(post.postInfo.post)) === -1) { //not you api.addYou(post.postInfo.board, parseInt(post.postInfo.post)); posting.markPostAsYou(post.postInfo.post, document.getElementById(post.postInfo.post)); } else { //you removeYou(post.postInfo.board, parseInt(post.postInfo.post)); unmarkPostAsYou(post.postInfo.post, document.getElementById(post.postInfo.post)); } reloadPosts(true, true, false, false, false, true); } function showNickname(cell) { var id = cell.querySelector(".title").querySelector(".spanId").querySelector(".labelId").innerText; //cell is innerPost let board = cell.parentNode.getAttribute("data-boarduri"); //this is requiered as inlinequotes dont have a parent with data-boarduri //recursively searching until you reach an element with data-boarduri doesn't //work either because it isn't placed in the document when this is called //also hover quote things are placed at the very bottom as a child of body if (board === null) { board = window.location.pathname.split("/")[1]; } var title = cell.querySelector(".title"); let nicknames= JSON.parse(localStorage.PG_nicknames || null); if (nicknames !== null) { if (nicknames[board] !== undefined) { if (nicknames[board][id] !== undefined) { let nickname = nicknames[board][id]; let currentNickElement = title.querySelector(".nickname"); if (currentNickElement == undefined) { var nickElement = document.createElement("span"); nickElement.className = "nickname"; nickElement.innerHTML = nickname + " "; title.insertBefore(nickElement, title.querySelector(".labelCreated")); } else if (currentNickElement.innerHTML !== nickname + " ") { currentNickElement.innerHTML = nickname + " "; } } } } } function addNickname(post) { var board = post.postInfo.board.toString(); var id = post.postInfo.id.toString(); var name = prompt("Enter Nickname").toString(); //if cancelled if (name === null) { return; } var nicknames = JSON.parse(localStorage.PG_nicknames || "{}"); if (nicknames[board] === undefined) { nicknames[board] = {}; } nicknames[board][id] = name; localStorage.setItem("PG_nicknames", JSON.stringify(nicknames)); //only Nicknames reloadPosts(true, true, true, false, true); } //modified to spaghetti mode + number signs after quotelinks function overwriteParseExistingPost() { new MutationObserver((_, observer) => { const scriptTag = document.querySelector("script[src*='multiboardMenu.js']"); if (scriptTag) { observer.disconnect(); posting.parseExistingPost = function(linkSelf, noExtras, noModify, noTooltips, onlyIds, onlyNicknames, onlyTooltips) { if (posting.postCellTemplate.template.contains(linkSelf)) { return; } var innerPost = linkSelf.parentNode.parentNode; var postInfo = api.parsePostLink(linkSelf.href); posting.getExtraInfo(innerPost, postInfo); var ret = {}; ret.postInfo = postInfo; ret.linkSelf = linkSelf; ret.innerPost = innerPost; ret.files = innerPost.getElementsByClassName('panelUploads')[0]; ret.message = innerPost.getElementsByClassName("divMessage")[0]; //used to apply the ID fix to posts already loaded at the start //new posts have it already applied if (onlyIds) { posting.processIdLabel(ret.innerPost.getElementsByClassName("labelId")[0]); return; } if (onlyNicknames) { showNickname(ret.innerPost); return; } if (onlyTooltips) { posting.addExternalExtras(ret, noTooltips, onlyTooltips); return; } //this "onlyxyz" shit is stupid. replace with enums or something showNickname(ret.innerPost); //quotelink # link thing //ret.innerPost.querySelectorAll(".quoteLink, .panelBacklinks>a").forEach((e) => { ret.innerPost.querySelectorAll(".quoteLink").forEach((e) => { let linkTarget = e.getAttribute("href"); let noSign = document.createElement("a"); noSign.setAttribute("href", linkTarget); noSign.setAttribute("class", "numberSign"); noSign.innerText = "#"; let spacer = document.createTextNode(" "); e.after(spacer); spacer.after(noSign); }); if (noModify) return; //cache a clone of the node with alt before it gets additional backlinks if (typeof tooltips !== "undefined" && !noTooltips) { tooltips.addToKnownPostsForBackLinks(innerPost); tooltips.postCache[linkSelf.href] = innerPost.cloneNode(true); } //update with local times var labelCreated = innerPost.getElementsByClassName('labelCreated')[0]; if (posting.localTime) { posting.setLocalTime(labelCreated); } if (posting.relativeTime) { posting.addRelativeTime(labelCreated); } //thumbnail hovering/hiding if (typeof thumbs !== "undefined") { Array.from(innerPost.getElementsByClassName('uploadCell')) .forEach((cell) => thumbs.processUploadCell(cell)); } if (typeof embed !== "undefined") { Array.from(ret.message.getElementsByTagName("a")) .forEach((embedLink) => embed.processLinkForEmbed(embedLink)); } if (typeof hiding !== "undefined") { hiding.hideIfHidden(ret, hiding.checkFilterHiding(linkSelf)); } if (!noExtras) posting.addExternalExtras(ret, noTooltips, onlyTooltips); return ret; }; } }).observe(document.documentElement, {childList: true, subtree:true }); } //modified to spaghetti mode + remove you class after you unmark as you function overwriteAddExternalExtras() { new MutationObserver((_, observer) => { const scriptTag = document.querySelector("script[src*='multiboardMenu.js']"); if (scriptTag) { observer.disconnect(); posting.addExternalExtras = function(ret, noTooltips, onlyTooltips) { var innerPost = ret.innerPost; var linkSelf = ret.linkSelf; var postInfo = ret.postInfo; if (!onlyTooltips) { posting.processIdLabel(innerPost.getElementsByClassName("labelId")[0]); //(You)s if (posting.yous && posting.yous.indexOf(+postInfo.post) !== -1) { posting.markPostAsYou(postInfo.post, innerPost); } //load posting menu, hiding menu, and watcher //TODO: coalesce files? if (typeof postingMenu !== "undefined") { interfaceUtils.addMenuDropdown(ret, "Post Menu", "extraMenuButton", postingMenu.buildMenu); } if (typeof hiding !== "undefined") { interfaceUtils.addMenuDropdown(ret, "Hide", "hideButton", hiding.buildMenu); } if (typeof watcher !== "undefined") { if (postInfo.op) watcher.processOP(innerPost); } if (typeof qr !== "undefined") { var linkQuote = innerPost.getElementsByClassName('linkQuote')[0]; linkQuote.onclick = function() { qr.showQr(linkQuote.href.match(/#q(\d+)/)[1]); }; } }//endif !onlytooltips if (typeof tooltips !== "undefined") { Array.from(innerPost.getElementsByClassName('quoteLink')) .forEach((quote) => { var target = api.parsePostLink(quote.href); tooltips.processQuote(quote, false, noTooltips); if (!posting.yous) { return; } if (api.boardUri === target.board && posting.yous.indexOf(+target.post) !== -1) { quote.classList.add("you"); } else { quote.classList.remove("you"); } }); } }; } }).observe(document.documentElement, {childList: true, subtree:true }); } //modified for number signs on backlinks function overwriteAddBackLink() { new MutationObserver((_, observer) => { const scriptTag = document.querySelector("script[src*='hiding.js']"); if (scriptTag) { observer.disconnect(); tooltips.addBackLink = function(quote, quoteTarget, containerPost) { var knownBoard = tooltips.knownPosts[quoteTarget.board]; if (!knownBoard) return; var knownBackLink = knownBoard[quoteTarget.post]; if (!knownBackLink) return; var sourceBoard = containerPost.dataset.boarduri; var sourcePost = containerPost.id; var sourceId = sourceBoard + '_' + sourcePost; if (knownBackLink.added.indexOf(sourceId) > -1) { return; } else { knownBackLink.added.push(sourceId); } var text = '>>'; if (sourceBoard != quoteTarget.board) { text += '/' + containerPost.dataset.boarduri + '/'; } text += sourcePost; var backLink = document.createElement('a'); backLink.innerText = text; backLink.href = '/' + sourceBoard + '/res/' + quoteTarget.thread + '.html#' + sourcePost; var backLinkNoSign = document.createElement("a"); backLinkNoSign.setAttribute("href", backLink.href); backLinkNoSign.setAttribute("class", "numberSign"); backLinkNoSign.innerText = "#"; var spacer = document.createTextNode(" "); knownBackLink.container.appendChild(backLink); knownBackLink.container.appendChild(spacer.cloneNode(true)); knownBackLink.container.appendChild(backLinkNoSign); knownBackLink.container.appendChild(spacer.cloneNode(true)); tooltips.processQuote(backLink, true); var dupe = backLink.cloneNode(true); var dupe2 = backLinkNoSign.cloneNode(true); knownBackLink.altContainer.appendChild(dupe); knownBackLink.altContainer.appendChild(spacer.cloneNode(true)); knownBackLink.altContainer.appendChild(dupe2); knownBackLink.altContainer.appendChild(spacer.cloneNode(true)); tooltips.processQuote(dupe, true); }; } }).observe(document.documentElement, {childList: true, subtree:true }); } //fix recursive backlinks + remove X from inlines function overwriteAddInlineClick() { new MutationObserver((_, observer) => { const scriptTag = document.querySelector("script[src*='hiding.js']"); if (scriptTag) { observer.disconnect(); tooltips.addInlineClick = function(quote, innerPost, isBacklink, quoteTarget, sourceId) { quote.addEventListener("click", function(e) { if (!tooltips.inlineReplies) return; e.preventDefault(); var replyPreview = Array.from(innerPost.children) .find((a) => a.className === "replyPreview"); var divMessage = innerPost.getElementsByClassName("divMessage")[0]; if (tooltips.loadingPreviews[quoteTarget.quoteUrl] || tooltips.quoteAlreadyAdded(quoteTarget.quoteUrl, innerPost)) return; var placeHolder = document.createElement("div"); placeHolder.style.whiteSpace = "normal"; placeHolder.className = "inlineQuote"; tooltips.loadTooltip(placeHolder, quoteTarget.quoteUrl, true, true); placeHolder.append if (!placeHolder.getElementsByClassName("linkSelf")) return; if (!PG_settings["removeXFromInlines"]) { var close = document.createElement("A"); close.innerText = "X"; close.onclick = function() { placeHolder.remove(); } close.style.className = "closeInline"; placeHolder.getElementsByClassName("postInfo")[0].prepend(close); } //new quote.addEventListener("click", function() { placeHolder.remove(); },{"once": true}); //end new Array.from(placeHolder.getElementsByClassName("quoteLink")) .forEach((a) => tooltips.processQuote(a, false, true)); //for some bizzaro reason they only processQuote()'d the bottom backlinks //new if (!tooltips.bottomBacklinks) { var stla = placeHolder.getElementsByClassName("panelBacklinks")[0] Array.from(stla.children) //Array.from(stla.querySelectorAll("a:not(.numberSign)")) .forEach((a) => tooltips.processQuote(a, true)); Array.from(stla.getElementsByClassName("numberSign")) //Array.from(stla.querySelectorAll("a:not(.numberSign)")) .forEach((a) => stla.insertBefore(document.createTextNode(" "), a)); } //end new if (tooltips.bottomBacklinks) { var alts = placeHolder.getElementsByClassName("altBacklinks")[0].firstChild Array.from(alts.children) .forEach((a) => tooltips.processQuote(a, true)); } if (isBacklink) { //innerPost.append(placeHolder); replyPreview.append(placeHolder); } else { quote.insertAdjacentElement("afterEnd", placeHolder); } tooltips.removeIfExists(); }) } } }).observe(document.documentElement, {childList: true, subtree:true }); } //changed to fix recusive inlining function overwriteAddLoadedTooltip() { new MutationObserver((_, observer) => { const scriptTag = document.querySelector("script[src*='hiding.js']"); if (scriptTag) { observer.disconnect(); //add tooltip dynamic content: backlinks, link underlining, (you)s tooltips.addLoadedTooltip = function(htmlContents, tooltip, quoteUrl, replyId, isInline) { var quoteTarget = api.parsePostLink(quoteUrl); var board = quoteTarget.board; var thread = +quoteTarget.thread; var post = +quoteTarget.post; if (!htmlContents) { tooltip.innerText = 'Not found'; //TODO delete and disable hover? return; } Array.from(htmlContents.getElementsByClassName("inlineQuote")).forEach((q) => q.remove()) var deletionCheckBox = htmlContents.getElementsByClassName('deletionCheckBox')[0]; if (deletionCheckBox) { deletionCheckBox.remove(); } // obtain the latest backlinks from the actual post // this has to be done now since backlinks change if (board === api.boardUri && thread === api.threadId) { var backContainer = tooltips.knownPosts[board][post ? post : thread]; if (backContainer) { var contentBack = htmlContents.getElementsByClassName("panelBacklinks"); Array.from(backContainer.container.children).forEach((backlink) => { Array.from(contentBack).forEach((panel) => { panel.append(backlink.cloneNode(true)); }) }); } } //new var altBacklinks = document.createElement("div"); altBacklinks.setAttribute("class", "altBacklinks"); altBacklinks.appendChild(htmlContents.getElementsByClassName("panelBacklinks")[0].cloneNode(true)); htmlContents.appendChild(altBacklinks); var replyPreview = document.createElement("div"); replyPreview.setAttribute("class", "replyPreview"); htmlContents.appendChild(replyPreview); //end new tooltips.addReplyUnderline(htmlContents, board, replyId); //TODO move to HTML/node caching var yous = localStorage.getItem(board + "-yous"); if (yous !== null && JSON.parse(yous).find((a) => a == post) !== undefined) { posting.markPostAsYou(undefined, htmlContents); } htmlContents.className = 'innerPost'; //for innerOPs if (tooltip.firstChild) { tooltip.firstChild.remove(); } //new //end new tooltip.append(htmlContents); //htmlcontents is originally not complete hence recursion etc not working if (isInline) { posting.parseExistingPost(tooltip.getElementsByClassName('linkSelf')[0], false, false, true); } else { tooltips.checkHeight(tooltip); } } } }).observe(document.documentElement, {childList: true, subtree:true }); } function buildMenu() { new MutationObserver((_, observer) => { const scriptTag = document.querySelector("script[src*='posting.js']"); if (scriptTag) { observer.disconnect(); //clobbers. this is used to create the hide and triangle button and their menus postingMenu.buildMenu = function(post, extraMenu) { var hasFiles = post.files && post.files.children.length > 0; var menuCallbacks = []; menuCallbacks.push( { name: "Change Nickname", callback: () => { addNickname(post); } } ); menuCallbacks.push( { name: "Toggle You", callback: () => { toggleYou(post); } } ); menuCallbacks.push( {name: 'Report' ,callback: function() { postingMenu.showReport2(post.postInfo.board, post.postInfo.thread, post.postInfo.post); } } ); menuCallbacks.push( {name: 'Delete Post' ,callback: function() { postingMenu.deleteSinglePost(post.postInfo.board, post.postInfo.thread, post.postInfo.post, null, null, null, post.innerPost); } } ); if (postingMenu.loggedIn && (postingMenu.globalRole < 4 || postingMenu.moddedBoards.indexOf(post.postInfo.board) >= 0)) { postingMenu.setExtraMenuMod(post, menuCallbacks, hasFiles); } return menuCallbacks; }; } }).observe(document.documentElement, {childList: true, subtree:true }); } function reloadPosts(noExtras = true, noModify = false, noTooltips = true, onlyIds = false, onlyNicknames = false, onlyTooltips = false) { const antiduplicateQuery = ".hideButton, .extraMenuButton, .watchButton, .unhideButton"; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { // console.log("penis"); // document.querySelectorAll(antiduplicateQuery).forEach(e => e.remove()); //posting.parseExistingPost(linkSelf, noExtra, noModify, noTooltips) //id event is in extra //noModify does nothing? noTooltips turn off backlinks??? document.querySelectorAll(".linkSelf").forEach(e => posting.parseExistingPost(e, noExtras, noModify, noTooltips, onlyIds, onlyNicknames, onlyTooltips)); }); } else { // var localTime = posting.localTime; // var relativeTime = posting.relativeTime; //document.querySelectorAll(antiduplicateQuery).forEach(e => e.remove()); //posting.parseExistingPost(linkSelf, noExtra, noModify, noTooltips) //id event is in extra //noModify does nothing? noTooltips turn off backlinks??? // document.querySelectorAll(".linkSelf").forEach(e => posting.parseExistingPost(e, true, false, true)); document.querySelectorAll(".linkSelf").forEach(e => posting.parseExistingPost(e, noExtras, noModify, noTooltips, onlyIds, onlyNicknames, onlyTooltips)); } } function addSettings() { document.addEventListener("DOMContentLoaded", () => { var checkboxMoveReplyButton = document.createElement("input"); var checkboxKeepWatcherOpen = document.createElement("input"); var checkboxCloseQrAfterPosting = document.createElement("input"); var checkboxMoveThreadInfo = document.createElement("input"); var checkboxAddWatcherShortcut = document.createElement("input"); var checkboxRemoveXFromInlines = document.createElement("input"); checkboxMoveReplyButton .setAttribute("type", "checkbox"); checkboxKeepWatcherOpen .setAttribute("type", "checkbox"); checkboxCloseQrAfterPosting .setAttribute("type", "checkbox"); checkboxMoveThreadInfo .setAttribute("type", "checkbox"); checkboxAddWatcherShortcut .setAttribute("type", "checkbox"); checkboxRemoveXFromInlines .setAttribute("type", "checkbox"); checkboxMoveReplyButton .setAttribute("id", "PG_checkboxMoveReplyButton"); checkboxKeepWatcherOpen .setAttribute("id", "PG_checkboxKeepWatcherOpen"); checkboxCloseQrAfterPosting .setAttribute("id", "PG_checkboxCloseQrAfterPosting"); checkboxMoveThreadInfo .setAttribute("id", "PG_checkboxMoveThreadInfo"); checkboxAddWatcherShortcut .setAttribute("id", "PG_checkboxAddWatcherShortcut"); checkboxRemoveXFromInlines .setAttribute("id", "PG_checkboxRemoveXFromInlines"); if (PG_settings.moveReplyButton) checkboxMoveReplyButton .setAttribute("checked", ""); if (PG_settings.keepWatcherOpen) checkboxKeepWatcherOpen .setAttribute("checked", ""); if (PG_settings.closeQrAfterPosting) checkboxCloseQrAfterPosting .setAttribute("checked", ""); if (PG_settings.moveThreadInfo) checkboxMoveThreadInfo .setAttribute("checked", ""); if (PG_settings.addWatcherShortcut) checkboxAddWatcherShortcut .setAttribute("checked", ""); if (PG_settings.removeXFromInlines) checkboxRemoveXFromInlines .setAttribute("checked", ""); var labelMoveReplyButton = document.createElement("label"); var labelKeepWatcherOpen = document.createElement("label"); var labelCloseQrAfterPosting = document.createElement("label"); var labelMoveThreadInfo = document.createElement("label"); var labelAddWatcherShortcut = document.createElement("label"); var labelRemoveXFromInlines = document.createElement("label"); labelMoveReplyButton .setAttribute("class", "small"); labelKeepWatcherOpen .setAttribute("class", "small"); labelCloseQrAfterPosting .setAttribute("class", "small"); labelMoveThreadInfo .setAttribute("class", "small"); labelAddWatcherShortcut .setAttribute("class", "small"); labelRemoveXFromInlines .setAttribute("class", "small"); labelMoveReplyButton .htmlFor = "PG_checkboxMoveReplyButton"; labelKeepWatcherOpen .htmlFor = "PG_checkboxKeepWatcherOpen"; labelCloseQrAfterPosting .htmlFor = "PG_checkboxCloseQrAfterPosting"; labelMoveThreadInfo .htmlFor = "PG_checkboxMoveThreadInfo"; labelAddWatcherShortcut .htmlFor = "PG_checkboxAddWatcherShortcut"; labelRemoveXFromInlines .htmlFor = "PG_checkboxRemoveXFromInlines"; labelMoveReplyButton .innerText = "Move Reply Button to Above Everything Else (Now you only need to tab once!)"; labelKeepWatcherOpen .innerText = "Open Threadwatcher on Load"; labelCloseQrAfterPosting .innerText = "Close Quick Reply after Posting"; labelMoveThreadInfo .innerText = "Move Thread Info to Header (posts/IDs/files)"; labelAddWatcherShortcut .innerText = "Add \"Add to Threadwatcher\" Shortcut Key (w)"; labelRemoveXFromInlines .innerText = "Remove \"Remove\" Button (X) from Inlines (You can click on the quotelink)"; var divMoveReplyButton = document.createElement("div"); var divKeepWatcherOpen = document.createElement("div"); var divCloseQrAfterPosting = document.createElement("div"); var divMoveThreadInfo = document.createElement("div"); var divAddWatcherShortcut = document.createElement("div"); var divRemoveXFromInlines = document.createElement("div"); divMoveReplyButton .append(checkboxMoveReplyButton, labelMoveReplyButton); divKeepWatcherOpen .append(checkboxKeepWatcherOpen, labelKeepWatcherOpen); divCloseQrAfterPosting.append(checkboxCloseQrAfterPosting, labelCloseQrAfterPosting); divMoveThreadInfo .append(checkboxMoveThreadInfo, labelMoveThreadInfo); divAddWatcherShortcut .append(checkboxAddWatcherShortcut, labelAddWatcherShortcut); divRemoveXFromInlines .append(checkboxRemoveXFromInlines, labelRemoveXFromInlines); var settings = document.createElement("div"); settings.setAttribute("id", "PG_checkboxes"); var button = document.createElement("button"); button.innerText = "Save"; button.addEventListener("click", function () { PG_settings.moveReplyButton = document.getElementById("PG_checkboxMoveReplyButton").checked ? true : false; PG_settings.keepWatcherOpen = document.getElementById("PG_checkboxKeepWatcherOpen").checked ? true : false; PG_settings.closeQrAfterPosting = document.getElementById("PG_checkboxCloseQrAfterPosting").checked ? true : false; PG_settings.moveThreadInfo = document.getElementById("PG_checkboxMoveThreadInfo").checked ? true : false; PG_settings.addWatcherShortcut = document.getElementById("PG_checkboxAddWatcherShortcut").checked ? true : false; PG_settings.removeXFromInlines = document.getElementById("PG_checkboxRemoveXFromInlines").checked ? true : false; localStorage.setItem("PG_settings", JSON.stringify(PG_settings)); }); var name = document.createElement("span"); name.innerText = "Weanus Settings ⸜(* ॑꒳ ॑* )⸝⋆*"; // I learn about Element.prepend() and .append() at the end. figures settings.append( divMoveReplyButton, divKeepWatcherOpen, divCloseQrAfterPosting, divMoveThreadInfo, divAddWatcherShortcut, divRemoveXFromInlines ); document.getElementById("settings-TG9jYWwgVGltZX").parentNode.parentNode.prepend( name, settings, button, document.createElement("hr") ); }); } function fixAltBacklinkCSS() { var styleFix = document.createElement("style"); styleFix.textContent = ` .altBacklinks > .panelBacklinks { //without these backlinks break (vanilla bug) display: block !important; } .title > .panelBacklinks { display: none !important; } `; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { if (tooltips.bottomBacklinks) { document.head.appendChild(styleFix); } }); } else { if (tooltips.bottomBacklinks) { document.head.appendChild(styleFix); } } } function addCSS() { var style = document.createElement("style"); style.textContent = ` .innerPost:has(.youName) { border-left: solid var(--link-hover-color); } .innerPost:has(.quoteLink.you) { border-left: dashed var(--link-hover-color); } .unhideButton { font-size: 90%; } .hideButton, .extraMenuButton { user-select: none; } .nickname { color: var(--link-hover-color); font-weight: bold; } .numberSign { text-decoration: none !important; } .altBacklinks { background-color: rgba(0, 0, 0, 0) !important; } a.quoteLink{ color: var(--link-color); } `; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { document.head.appendChild(style); }); } else { document.head.appendChild(style); } } /*main ===========================*/ const settingsName = "PG_settings"; const PG_settings = JSON.parse(localStorage.getItem(settingsName)); if (PG_settings === null) { var settings = {}; settings["closeQrAfterPosting"] = true; settings["moveReplyButton"] = true; settings["moveThreadInfo"] = true; settings["keepWatcherOpen"] = true; settings["addWatcherShortcut"] = true; settings["removeXFromInlines"] = true; localStorage.setItem(settingsName, JSON.stringify(settings)); console.log("Initialised settings at localStorage.PG_settings"); PG_settings = JSON.parse(localStorage.getItem(settingsName)); } overwriteParseExistingPost(); overwriteAddExternalExtras(); overwriteAddBackLink(); overwriteAddInlineClick(); overwriteAddLoadedTooltip(); fixClickOnId(); fixAltBacklinkCSS(); buildMenu(); //reload ids to apply id fix reloadPosts(true, true, true, true); //reload nicknames to make them show (combine with above later, perhaps, I cant be arsed) //reloadPosts(true, true, true, false, true); //initialise posts. honestly I'm spagghing myself noModify is enough. I'll fix everything later... reloadPosts(false, true, false); addCSS(); addSettings(); if (PG_settings.closeQrAfterPosting) closeQrAfterPosting(); if (PG_settings.moveReplyButton) moveReplyButton(); if (PG_settings.moveThreadInfo) moveThreadInfo(); if (PG_settings.keepWatcherOpen) keepWatcherOpen(); if (PG_settings.addWatcherShortcut) addWatcherShortcut();