// ==UserScript== // @name Tripcolor // @name:de Tripcolor // @namespace http://www.4chan.org/ // @version 1.0.0 // @description Colorize tripcodes, the way YOU want it // @description:de Färbe Tripcodes, wie es DIR gefällt // @author Wolvan // @match *://boards.4chan.org/* // @grant GM_getValue // @grant GM_setValue // @icon http://i.imgur.com/S2VvpO3.png // @downloadURL none // ==/UserScript== /* jshint -W097 */ (function () { 'use strict'; const US_META = { name: "Tripcolorizer", author: "Wolvan", version: "1.0.0", description: "Colorize Tripcodes" }; const MENU_HTML = '
'; const MENU_CSS = '#TripcolorizerMenu{border-width:1px;border-color:#292929;border-style:solid;background-color:#222;color:#BBB;position:fixed;z-index:1002;top:50%;left:50%;transform:translate(-50%,-50%)}#TripcolorizerGlass{background-color:rgba(21,21,21,.82);width:100%;height:100%;z-index:1001;top:0;left:0;position:fixed}#TripcolorizerMenu label{display:inherit}#TripcolorizerMenu .TripcolorizerScrolldiv{max-height:500px;overflow-y:auto}#TripcolorizerMenu #ButtonsDiv{text-align:center}#TripcolorizerMenu input[type=checkbox]{display:inline-block!important}#TripcolorizerMenu .riceCheck{display:none}#TripcolorizerMenuButton{cursor:pointer}'; Object.filter = function (obj, predicate) { var result = {}, key; for (key in obj) { if (obj.hasOwnProperty(key) && predicate(obj[key])) { result[key] = obj[key]; } } return result; }; const utils = { append: { script: function (scriptCode, name = "TripcolorizerJS") { var newScript = document.createElement("script"); newScript.id = name; newScript.innerHTML = scriptCode; document.head.appendChild(newScript); }, css: function (style, name = "TripcolorizerStyle") { var newStyle = document.createElement("style"); newStyle.id = name; newStyle.innerHTML = style; document.head.appendChild(newStyle); }, div: function (htmlCode, name = "TripcolorizerDiv") { var newChild = document.createElement("div"); newChild.id = name; newChild.innerHTML = htmlCode; document.body.appendChild(newChild); } }, randomFrom: { object: function (obj) { var keys = Object.keys(obj); var randomProp = keys[Math.floor(Math.random() * keys.length)]; return { key: randomProp, value: obj[randomProp] }; }, array: function (arr) { return arr[Math.floor(Math.random * arr.length)]; } } }; const defaultConfig = { tripcodes: {}, settings: { colorNewCodes: { type: "checkbox", defaultValue: true, value: true, text: "Color unknown tripcodes", hint: "Automatically add new Tripcodes to the list" }, colorNames: { type: "checkbox", defaultValue: true, value: true, text: "Color names", hint: "Color the names next to the tripcode as well" }, autoColorOnUpdate: { type: "checkbox", defaultValue: true, value: true, text: "Color new posts automatically", hint: "When auto-polling for new replies, the new tripcode gets automatically colored" }, defaultColorSystem: { type: "dropdown", defaultValue: "rgb", value: "rgb", values: [{ value: "rgb", text: "RGB" }, { value: "hsl", text: "HSL" }, { value: "hsv", text: "HSV" } ], text: "Default Color Representation", hint: "Which color representation should new trip values use" } } }; var config; var tmpConfig; var mutObs = new MutationObserver(function (mutations) { for (let mutation of mutations) { for (let node of mutation.addedNodes) { if (node && node.classList && node.classList.contains("postContainer")) { colorTrips(); } } } }); function loadConfig() { try { var conf = GM_getValue("tripcolorizerOptions", JSON.stringify(defaultConfig)); config = { tripcodes: {}, settings: defaultConfig.settings }; var parsedConfig = JSON.parse(conf); if (parsedConfig.tripcodes) { for (var trip in parsedConfig.tripcodes) { config.tripcodes[trip] = parsedConfig.tripcodes[trip]; } } if (parsedConfig.settings) { for (var setting in parsedConfig.settings) { if (config.settings[setting]) config.settings[setting].value = parsedConfig.settings[setting].value; } } console.log(`[${US_META.name}]Config loaded`); } catch (error) { config = Object.assign({}, defaultConfig); console.log(`[${US_META.name}]Failed to load config. Using default. Error: ${error}`); return; } } function worstHashAlgorithm(str) { var sum = 0; for (var ind = 0; ind < str.length; ind++) { sum += str.charCodeAt(ind); } return sum; } function colorTrips() { function hsv2hsl(hsvstr) { var [hue, sat, val] = hsvstr.match(/\d+/g); return `hsl(${ hue }, ${ sat * val / ((hue = (2 - sat) * val) < 1 ? hue : 2 - hue) }, ${ hue / 2 })`; } var tripNodes = document.getElementsByClassName("postertrip"); for (var tripNode of tripNodes) { let trip = tripNode.innerHTML; if (!config.tripcodes[trip] && config.settings.colorNewCodes.value) { let strippedTrip = trip.replace(/!/g, ""); let baseValues = [ worstHashAlgorithm(strippedTrip.substr(0, 4)) % 255, worstHashAlgorithm(strippedTrip.substr(4, 4)) % 255, worstHashAlgorithm(strippedTrip.substr(8)) % 255 ]; switch (config.settings.defaultColorSystem.value) { case "hsl": case "hsv": let hue = Math.floor((baseValues[0] / 255) * 360); let sat = baseValues[1] / 255; let light = baseValues[2] / 255; config.tripcodes[trip] = `hsl(${hue}, ${Math.floor(sat * 100)}%, ${Math.floor(light * 100)}%)`; if (config.settings.defaultColorSystem.value === "hsl") break; sat *= light < 0.5 ? light : 1 - light; config.tripcodes[trip] = `hsv(${ hue }, ${ Math.floor((2 * sat / (light + sat)) * 100) }%, ${ Math.floor((light + sat) * 100) }%)` break; case "rgb": default: config.tripcodes[trip] = `rgb(${ baseValues[0] }, ${ baseValues[1] }, ${ baseValues[2] })` break; } } if (config.tripcodes[trip]) { let targets; if (config.settings.colorNames.value) { targets = tripNode.parentNode.children; } else { targets = [tripNode]; } for (let target of targets) { if (target.tagName.toLowerCase() === "span") { target.classList.add("TripcolorizerStyled"); target.style.setProperty("color", config.tripcodes[trip].match(/hsv/gi) ? hsv2hsl(config.tripcodes[trip]) : config.tripcodes[trip], "important"); } } } } } function uncolorTrips() { var allStyled = document.getElementsByClassName("TripcolorizerStyled"); while (allStyled.length > 0) { allStyled[0].style.removeProperty("color"); allStyled[0].classList.remove("TripcolorizerStyled"); } } function showMenu() { tmpConfig = {}; tmpConfig.settings = Object.assign({}, config.settings); tmpConfig.tripcodes = Object.assign({}, config.tripcodes); function closeMenu() { document.getElementById("TripcolorizerGlass").remove(); document.getElementById("TripcolorizerMenu").remove(); } function showGlass() { var name = "TripcolorizerGlass"; var oldelem = document.getElementById(name); if (oldelem) oldelem.remove(); utils.append.div("", name); } const STRING_TEMPLATES = { checkbox: '