// ==UserScript== // @name Freedcamp project colors // @namespace http://freedcamp.com/ // @version 1.05 // @description enable project cards background color // @author devops@freedcamp.com // @match *://freedcamp.com/* // @match *://*.freedcamp.com/* // @require https://raw.github.com/odyniec/MonkeyConfig/master/monkeyconfig.js // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // ==/UserScript== (function() { "use strict"; window.addEventListener("FC_ROUTE_CHANGED", function() { switch (window.location.pathname) { case "/dashboard/home": setTimeout(() => switchHomeProjectsColors(), 0); break; case "/dashboard": setTimeout(() => switchDashboardProjectsColors(true), 0); break; } }); window.addEventListener("DASHBOARD_PROJECTS_RENDERED", function() { setTimeout(() => switchDashboardProjectsColors(true), 0); }); let mtConfig, fpscConfig; const modeSelectConfig = new MonkeyConfig({ title: "Select mode", menuCommand: true, params: { highlight: { type: "custom", html: "" + "
" + "" + "
" + "" + "
" + "" + "" + "name " + "description", set: function(value, parent) { const modeSelected = `#${value[0]}`.toLowerCase(); parent.querySelector(modeSelected).checked = true; parent.querySelector("#mtpn").checked = value[1]; parent.querySelector("#mtpd").checked = value[2]; }, get: function(parent) { const modeSelected = parent .querySelector('input[name="mode"]:checked') .id.toUpperCase(); const mtpdChecked = parent.querySelector("#mtpd").checked; let mtpnChecked = parent.querySelector("#mtpn").checked; // reset to "name" if nothing selected if (!mtpnChecked && !mtpdChecked) { mtpnChecked = true; } return [modeSelected, mtpnChecked, mtpdChecked]; }, default: ["fppc", true, true] } }, onSave: function(values) { location.reload(); } }); const colorOpacityConfig = new MonkeyConfig({ title: "Color opacity", menuCommand: true, params: { opacity: { type: "custom", html: "", set: function(value, parent) { parent.querySelector("input").value = value; }, get: function(parent) { return parent.querySelector("input").value; }, default: "50" } }, onSave: function(values) { location.reload(); } }); const MODE = modeSelectConfig.get("highlight")[0]; const MTPN_CHECKED = modeSelectConfig.get("highlight")[1]; const MTPD_CHECKED = modeSelectConfig.get("highlight")[2]; const OPACITY = colorOpacityConfig.get("opacity") / 100; const HEX_REGEX = /^(\s+)?((#(0x){0,1}|#{0,1})([0-9A-Fa-f]{8}|[0-9A-Fa-f]{6}))(\s+)?$/; const IS_NEW_UI = typeof react_cache_version !== "undefined"; if (MODE === "FPSC") { fpscConfig = new MonkeyConfig({ title: "Set color", menuCommand: true, params: { custom_color: { type: "custom", html: "", set: function(value, parent) { parent.querySelector("input").value = value; }, get: function(parent) { const value = parent.querySelector("input").value; if (!value.match(HEX_REGEX)) { alert("Wrong HEX color! Restored default."); return "#00FF00"; } else { return value.replace(HEX_REGEX, "$2"); } }, default: "#00ff00" } }, onSave: function(values) { location.reload(); } }); } else if (MODE === "MTP") { mtConfig = new MonkeyConfig({ title: "Set keywords", menuCommand: true, params: { keywords: { type: "custom", html: "", set: function(value, parent) { parent.innerHTML = mtpGenerateKeyFields(value); const iframe = document.getElementById("__MonkeyConfig_frame") .contentWindow.document; const configContainer = iframe.querySelector( ".__MonkeyConfig_container" ); document.getElementById( "__MonkeyConfig_frame" ).style.height = `${configContainer.offsetHeight}px`; let isStyleSet = false; parent.querySelector("#addButton").onclick = function() { const button = parent.querySelector("#addButton"); parent.removeChild(button); const newField = " " + "
"; parent.insertAdjacentHTML("beforeend", newField); const oldHeight = document.getElementById("__MonkeyConfig_frame") .offsetHeight; const newHeight = oldHeight + 23; document.getElementById( "__MonkeyConfig_frame" ).style.height = `${newHeight}px`; if (!isStyleSet) { mtpSetCenter(); isStyleSet = true; } parent.append(button); }; let i = 0; for (let key in value) { parent.querySelectorAll("input")[i].value = key; parent.querySelectorAll("input")[i + 1].value = value[key]; i += 2; } for (; i < parent.querySelectorAll("input").length; i++) { parent.querySelectorAll("input")[i].value = ""; } }, get: function(parent) { let result = {}; const inputs = parent.querySelectorAll("input"); for (let i = 0; i < inputs.length; i += 2) { const key = inputs[i].value; const val = inputs[i + 1].value.toLowerCase(); if (key.length > 2 && val.match(HEX_REGEX)) { result[key] = val.replace(HEX_REGEX, "$2"); } } return result; }, default: { keyword1: "#00ff00" } } }, onSave: function(values) { location.reload(); } }); } function waitForProjectsToLoad(func) { const interval = setInterval(function() { if (dashboardProjectsLoaded) { func(); clearInterval(interval); } }, 50); } function mtpGenerateKeyFields(keywords) { let html = " " + "
"; let kCount = 0; for (let key in keywords) { kCount++; } for (let i = 1; i < kCount; i++) { html += " " + "
"; } const buttonStyle = "display:inline-block;" + "border-radius: 2px;" + "background-color: #4CAF50;" + "color: white;" + "padding: 1em;" + "margin-top: 0.5em;" + "width: 15.5em;"; html += ``; return html; } function mtpSetCenter() { document.getElementsByClassName("__MonkeyConfig_layer")[0].style.top = "50%"; document.getElementsByClassName("__MonkeyConfig_layer")[0].style.transform = "translate(0, -50%)"; } // on dashboard page if (window.location.pathname === "/dashboard") { if (!IS_NEW_UI) { switchDashboardProjectsColors(); } else { waitForProjectsToLoad(() => switchDashboardProjectsColors()); } } else if (window.location.pathname === "/dashboard/home") { switchHomeProjectsColors(); } function switchDashboardProjectsColors(enableOnly = false) { const pBlocks = document.querySelectorAll(".project"); if (MODE === "FPPC" || MODE === "FPSC") { for (let i = 0; i < pBlocks.length; i++) { const pBlock = pBlocks[i]; if (isDashboardProjectFavorite(pBlock)) { switchDashboardProjectColor(pBlock, enableOnly); } pBlock.querySelector(".favorite_project_action").onclick = function() { if (IS_NEW_UI) { const url = pBlock.querySelector(".Link--link.project_name").href; const clones = [ ...document.querySelectorAll(".Link--link.project_name") ].filter(a => a.href === url); clones.forEach(clone => switchDashboardProjectColor(clone.parentElement.parentElement) ); } else { switchDashboardProjectColor(pBlock); } }; } } else { for (let i = 0; i < pBlocks.length; i++) { switchDashboardProjectColor(pBlocks[i], enableOnly); } } } function switchDashboardProjectColor(pBlock, enableOnly) { let color, isColorLight, opColor; switch (MODE) { case "FPPC": case "APPC": color = pBlock.querySelector(".card_color").style.backgroundColor; break; case "FPSC": color = hexToRgb(fpscConfig.get("custom_color")); break; case "MTP": { const keywords = mtConfig.get("keywords"); const name = pBlock .querySelector(".project_name") .textContent.toLowerCase(); const description = pBlock .querySelector(".project_desc") .textContent.toLowerCase(); for (let key in keywords) { if (isKeyMatch(key, name, description)) { const value = keywords[key]; color = hexToRgb(value); break; } } break; } } if (color) { isColorLight = isLight(color); opColor = `${color.substring(0, color.length - 1)}, ${OPACITY})`; if (pBlock.style.background) { if (enableOnly) { return; } pBlock.removeAttribute("style"); } else { pBlock.style.background = opColor; } const desc = pBlock.querySelector(".project_desc"); const noDesc = pBlock.querySelector(".no_description"); const cogImage = pBlock.querySelector(".cog_image"); const name = pBlock.querySelector(".project_name"); invertColor(name, isColorLight); if (noDesc) { invertColor(noDesc, isColorLight); } else { invertColor(desc, isColorLight); } if (cogImage.style.color) { cogImage.removeAttribute("style"); } else { cogImage.style.color = "black"; } } } function isDashboardProjectFavorite(pBlock) { return !!pBlock.querySelector(".favorited"); } function switchHomeProjectsColors() { const pBlocks = document.querySelectorAll( ".HomeProjectItem--fk-HomeProjectItem" ); for (let i = 0; i < pBlocks.length; i++) { const pBlock = pBlocks[i]; if (MODE === "FPPC" || MODE === "FPSC") { if (isHomeProjectFavorite(pBlock)) { switchHomeProjectColor(pBlock, true); } const favButton = pBlock.querySelector(".tooltip-trigger"); favButton.onclick = () => switchHomeProjectColor(pBlock); } else { switchHomeProjectColor(pBlock, true); } } } function switchHomeProjectColor(pBlock, isNotHover) { let color, isColorLight, opColor; switch (MODE) { case "FPPC": case "APPC": color = pBlock.querySelector(".ProjectIcon--project-Icon").style .backgroundColor; break; case "FPSC": color = hexToRgb(fpscConfig.get("custom_color")); break; case "MTP": { const keywords = mtConfig.get("keywords"); const name = pBlock .querySelector(".HomeProjectItem--fk-Home-Project-Name") .innerText.toLowerCase(); for (let key in keywords) { if (isKeyMatch(key, name, "")) { const value = keywords[key]; color = hexToRgb(value); break; } } break; } } if (color) { isColorLight = isLight(color); opColor = `${color.substring(0, color.length - 1)}, ${OPACITY})`; const defaultColor = "rgb(235,237,243)"; const isFavorite = isHomeProjectFavorite(pBlock); const light = "white"; const dark = "rgb(0,0,0,0.7)"; if (pBlock.style.background) { pBlock.removeAttribute("style"); } else { pBlock.style.background = opColor; } const apps = pBlock.querySelectorAll( ".HomeProjectItem--fk-HomeProjectItem-App" ); for (let i = 0; i < apps.length; i++) { const appIcon = apps[i].querySelector("svg"); invertColor(appIcon, isColorLight, light, dark, "rgb(160, 166, 199)"); } const dropdownIcon = pBlock .querySelector(".fk-Dropdown-Trigger") .querySelector("svg"); invertColor(dropdownIcon, isColorLight, light, dark, "inherit"); const name = pBlock.querySelector( ".HomeProjectItem--fk-Home-Project-Name" ); invertColor(name, isColorLight, light, dark); const group = pBlock.querySelector( ".HomeProjectItem--fk-Home-Project-Group-Name" ); invertColor(group, isColorLight, light, dark); } } function isHomeProjectFavorite(pBlock) { try { return ( pBlock.querySelector(".tooltip-trigger").querySelector("i").style .color === "rgb(230, 180, 31)" ); } catch (e) { return false; } } // sidebar if (IS_NEW_UI) { window.addEventListener("FC_PROJECT_PICKER_OPENED", () => switchSideProjects(IS_NEW_UI) ); } else { document.querySelector(".fc_project_switcher").onclick = () => switchSideProjects(IS_NEW_UI); } function switchSideProjects(IS_NEW_UI) { const sideProjects = document.querySelectorAll( MODE === "FPPC" || MODE === "FPSC" ? IS_NEW_UI ? ".f_favorite" : ".f_favorite > .fc_project_item" : IS_NEW_UI ? ".ProjectPicker--fk-ProjectPicker-Project" : ".fc_project_item" ); for (let z = 0; z < sideProjects.length; z++) { const sideProject = sideProjects[z]; switchSidebarColor(sideProject, IS_NEW_UI); } } function isKeyMatch(key, name, description) { let matchBool; const matchNameBool = name.toLowerCase().indexOf(key.toLowerCase()) !== -1; const matchDescBool = description.toLowerCase().indexOf(key.toLowerCase()) !== -1; const matchNameDescBool = matchNameBool || matchDescBool; if (MTPN_CHECKED && MTPD_CHECKED) { matchBool = matchNameDescBool; } else if (MTPD_CHECKED) { matchBool = matchDescBool; } else { matchBool = matchNameBool; } return matchBool; } function switchSidebarColor(sideProject, IS_NEW_UI) { let color; switch (MODE) { case "APPC": case "FPPC": color = sideProject.querySelector( IS_NEW_UI ? ".ProjectPicker--fk-ProjectPicker-ProjectColor" : ".color" ).style.backgroundColor; break; case "FPSC": color = hexToRgb(fpscConfig.get("custom_color")); break; case "MTP": { const keywords = mtConfig.get("keywords"); const name = sideProject .querySelector( IS_NEW_UI ? ".ProjectPicker--fk-ProjectPicker-ProjectName" : ".name" ) .textContent.toLowerCase(); const description = sideProject .querySelector( IS_NEW_UI ? ".ProjectPicker--fk-ProjectPicker-ProjectDescription" : ".fc_description" ) .textContent.toLowerCase(); for (let key in keywords) { if (isKeyMatch(key, name, description)) { const value = keywords[key]; color = hexToRgb(value); break; } } break; } } if (color) { const isColorLight = isLight(color); const name = sideProject.querySelector( IS_NEW_UI ? ".ProjectPicker--fk-ProjectPicker-ProjectName" : ".name" ); const desc = sideProject.querySelector( IS_NEW_UI ? ".ProjectPicker--fk-ProjectPicker-ProjectDescription" : ".fc_description" ); const fcApps = sideProject.querySelectorAll( IS_NEW_UI ? ".ProjectPicker--fk-ProjectPicker-ProjectApplications" : ".fc_app" ); invertColor(name, isColorLight); if (desc) { invertColor(desc, isColorLight); } // make buttons dark for (let x = 0; x < fcApps.length; x++) { const btns = fcApps[x].querySelectorAll(".btn"); for (let y = 0; y < btns.length; y++) { btns[y].style.color = "black"; } } sideProject.style.backgroundColor = `${color.substring( 0, color.length - 1 )}, ${OPACITY})`; sideProject.style.borderColor = color; } } function isLight(color) { let r, g, b, hsp; color = color.match( /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/ ); r = 255 - OPACITY * (255 - color[1]); g = 255 - OPACITY * (255 - color[2]); b = 255 - OPACITY * (255 - color[3]); hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)); return hsp > 127.5; } function invertColor( element, isLight, light = "white", dark = "black", defaultColor ) { if (defaultColor) { if (element.style.color !== defaultColor) { element.style.color = defaultColor; } else { element.style.color = isLight ? "black" : "white"; } } else { if (element.style.color) { element.removeAttribute("style"); } else { element.style.color = isLight ? "black" : "white"; } } } function hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? `rgb(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt( result[3], 16 )})` : null; } })();