// ==UserScript== // @name Flag Randomizer // @namespace http://ponepaste.org/5555 // @match https://boards.4channel.org/mlp/thread/* // @match https://boards.4chan.org/mlp/thread/* // @grant none // @version 0.2.3 // @author Fillyanon, ScriptFilly, RandomSchmuck, Marker // @description Adds a flag randomizer to the Board. // ==/UserScript== "use strict"; // Possible values are: Submit, Keystroke, Timer const changeFlagOn = "Submit"; const flags = Object.freeze({ MISC: ["4CC", "AN"], G4: [ "AJ", "ANF", "APB", "AU", "BB", "BM", "BP", "BS", "CB", "CG", "CHE", "CL", "CO", "DAY", "DD", "DER", "DIS", "DT", "FAU", "FL", "FLE", "GI", "LI", "LT", "LY", "MA", "MAU", "MIN", "NI", "NUR", "OCT", "PAR", "PC", "PCE", "PI", "PLU", "PM", "QC", "RAR", "RD", "RLU", "S1L", "SCO", "SHI", "SIL", "SP", "SPI", "STA", "STL", "SUN", "SUS", "SWB", "TS", "TWI", "TX", "VS", "ZE", ], G5: ["HT", "IZ", "PP", "SPT", "ZS", "SS"], EQG: [ "ADA", "AB", "SON", "EQA", "EQF", "EQP", "EQR", "EQT", "EQI", "EQS", "ERA", ], TFH: ["TFA", "TFO", "TFP", "TFS", "TFT", "TFV", "TP"], }); const allFlags = Object.values(flags).flat(); const getRand = (coll) => { return coll[Math.floor(Math.random() * coll.length)]; }; const getPost = () => { const use4chanX = document.getElementById("qr"); const quickReply = document.getElementById("qrForm"); const qrForm = use4chanX ?? quickReply; return qrForm ?? document.forms.post; }; const changeFlag = () => { if (selector.value == "OFF") return; const post = getPost(); const flagSelector = post.querySelector(".flagSelector"); const origValue = flagSelector.value; flagSelector.value = (selector.value != "ALL") ? getRand(flags[selector.value]) : getRand(allFlags); // Workaround for 4chanX issue where its flag selector is 3 missing flags. // Attempting to assign a Selector to an option that doesn't exist would // reset it's value to "". // When this happens, we restore it to its initial value. if (flagSelector.value == "") flagSelector.value = origValue; }; const makeOpt = (option) => { const opt = document.createElement("option"); opt.value = opt.innerText = option; selector.appendChild(opt); }; const selector = document.createElement('select'); const botLine = document.querySelector('.navLinksBot'); selector.style.marginLeft = '1rem'; Object.keys(flags).forEach(key => makeOpt(key)); makeOpt('ALL'); makeOpt('OFF'); selector.addEventListener('change', () => { window.localStorage.setItem("flagGroup", selector.value); }); selector.value = window.localStorage.getItem("flagGroup") ?? "G4"; botLine.appendChild(selector); // Setup Randomizer Functions const creationObserver = (function () { const observedNodes = []; const callbacks = []; const executeCallback = (fn, node) => { if (observedNodes.includes(node)) return; observedNodes.push(node); fn(node); }; const obs = new MutationObserver(mutationRecords => { mutationRecords.forEach(mutation => { mutation.addedNodes.forEach(node => { if (!(node instanceof HTMLElement)) return; callbacks.forEach(([selector, fn]) => { if (node.matches(selector)) executeCallback(fn, node); node.querySelectorAll(selector).forEach(childNode => executeCallback(fn, childNode)); }); }); }); }); obs.observe(document.body, { childList: true, subtree: true }); return function (selector, fn) { document.querySelectorAll(selector).forEach(node => executeCallback(fn, node)); callbacks.push([selector, fn]); }; })(); const selectors = [ 'form[name="post"]', // 4chan default post 'form[name="qrPost"]', // 4chanNX quick reply form 'div#qr form', // 4chanX quick reply form ].join(', '); // Keystroke function debounce(func, wait, immediate) { let timeout; return function () { const context = this, args = arguments; const later = function () { timeout = null; if (!immediate) func.apply(context, args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; } const changeAfterKeystroke = debounce(changeFlag, 250, false); const config = {attributes: true, childList: true, subtree: true}; // Randomize the flag if (changeFlagOn === "Keystroke") { const keystrokeHandler = function() { const is4chanXQrActive = document.getElementById("qr"); const post = getPost(); // update the flag const reply = (is4chanXQrActive) ? post.querySelector("textarea[data-name=\"com\"]") : post.querySelector("textarea[name=\"com\"]"); reply.addEventListener("input", changeAfterKeystroke); } creationObserver(selectors, form => { if (!(form instanceof HTMLFormElement)) return; keystrokeHandler(); }); } else if (changeFlagOn === "Timer") { setInterval(() => { changeFlag(); }, 500); } else if (changeFlagOn === "Submit") { const submitHandler = (event) => { const target = event.target; if (!target.matches('input[type="submit"]')) return; const form = event.currentTarget; changeFlag(form); } creationObserver(selectors, form => { if (!(form instanceof HTMLFormElement)) return; form.addEventListener('click', submitHandler, { capture: true }); }); }