// ==UserScript== // @name Binoculars // @namespace https://theki.club/ // @version 1.2.2 // @description Paste a list of the online users in a neatly-formatted way. // @author Theki / Hoylecake // @match https://twocansandstring.com/forum/sandbox/3382/reply // @icon https://www.google.com/s2/favicons?sz=64&domain=twocansandstring.com // @license GNU GPLv3 // @grant none // ==/UserScript== const buttonDiv = document.querySelector("#content_host > form > div[style]"); const lowerDiv = document.querySelector("#content_host > div[style*=\"text-align:left;\"]"); // This is the area below the textarea const textBox = document.getElementById("post_textbox"); let str = ""; const config = { disableMarkov: { desc: "Exclude Markov", default: true, value: true }, secretMarkov: { desc: "Include Markov if he isn't online (just for a bit of fun;))", default: false, value: false }, prefix: { desc: "Include this before users:", default: "Users online: ", value: "Users online: " }, suffix: { desc: "Include this after users:", default: " are online.", value: " are online." } }; (async function () { 'use strict'; createTable("Binoculars configuration",config); createButton("#7323c4", "#5023cc", "Paste users", () => { getUsers().then(value => { textBox.value += generateList(value); }); }); })(); const rand = (min, max) => Math.floor(Math.random() * (Math.ceil(min) - Math.floor(max) + 1)) + Math.ceil(min); function createButton(gradientStart, gradientEnd, text, callbackFn) { const btn = document.createElement("button"); btn.innerHTML = text; btn.type = "button"; btn.style.background = `linear-gradient(${gradientStart}, ${gradientEnd})`; btn.addEventListener('click', callbackFn); buttonDiv.prepend(btn); } function createTable(title,tableContent) { const container = document.createElement("div"); container.innerHTML = `

${title}

`; const table = document.createElement("table"); table.style = "font-size:9pt; width:50%"; table.cellPadding = 8; const tbody = document.createElement("tbody"); for (let [key,value] of Object.entries(tableContent)) { const tr = document.createElement("tr"); tr.innerHTML = `${value.desc}` const inp = document.createElement("input"); switch (typeof value.default) { case "boolean": inp.type = "checkbox"; inp.checked = value.default; break; case "string": inp.type = "text"; inp.placeholder = value.default; inp.value = value.default; // Add more type considerations as you add more configuration options } inp.addEventListener("change",() => { value.value = inp.type === "checkbox" ? inp.checked : inp.value; }); tr.appendChild(inp); tbody.appendChild(tr); } table.appendChild(tbody); container.appendChild(table); lowerDiv.prepend(container); } function generateList(html) { str = ""; let userLinks = Array.from(html.querySelectorAll("#forum_main_usersonline a")); if (userLinks.filter(userLink => { // This could definitely be optimized, but I couldn't find a way to check .includes for HTMLElements. if (userLink.textContent == "Markov") return true; }).length == 0 && config.secretMarkov.value) { const markov = document.createElement("a"); markov.href = "/users/Markov"; markov.textContent = "Markov?"; userLinks.splice(rand(1, userLinks.length < 2 ? 1 : userLinks.length - 1),0,markov); }; userLinks = userLinks.filter(userLink => { // filter out Markov if we should hide him if (config.disableMarkov.value && userLink.textContent == "Markov") { return false; } return true; }); if (config.prefix.value) str += config.prefix.value; userLinks.forEach((element, index) => { const lin = userLinks.length; str += `${element.textContent}${index == lin - 2 ? ', and ' : (index == lin - 1 ? '' : ', ')}`; }); if (config.suffix.value) str += config.suffix.value; return str; } function makeRequest(method, url) { return new Promise(function (resolve, reject) { let xhr = new XMLHttpRequest(); xhr.responseType = "document"; xhr.open(method, url); xhr.onload = function () { if (this.status >= 200 && this.status < 300) { resolve(xhr.response); } else { reject({ status: this.status, statusText: xhr.statusText }); } }; xhr.onerror = function () { reject({ status: this.status, statusText: xhr.statusText }); }; xhr.send(); }); } // THX STACK OVERFLOW async function getUsers() { const response = await makeRequest("GET", "https://twocansandstring.com/forum"); return response; }