Twitter/X Accent Picker

5/31/2024, 7:37:56 PM

  1. // ==UserScript==
  2. // @name Twitter/X Accent Picker
  3. // @namespace Violentmonkey Scripts
  4. // @match https://x.com/*
  5. // @grant none
  6. // @version 1.0
  7. // @license GPLv3
  8. // @author -
  9. // @description 5/31/2024, 7:37:56 PM
  10. // ==/UserScript==
  11.  
  12. var RGBvalues = (function() {
  13.  
  14. var _hex2dec = function(v) {
  15. return parseInt(v, 16)
  16. };
  17.  
  18. var _splitHEX = function(hex) {
  19. var c;
  20. if (hex.length === 4) {
  21. c = (hex.replace('#','')).split('');
  22. return {
  23. r: _hex2dec((c[0] + c[0])),
  24. g: _hex2dec((c[1] + c[1])),
  25. b: _hex2dec((c[2] + c[2]))
  26. };
  27. } else {
  28. return {
  29. r: _hex2dec(hex.slice(1,3)),
  30. g: _hex2dec(hex.slice(3,5)),
  31. b: _hex2dec(hex.slice(5))
  32. };
  33. }
  34. };
  35.  
  36. var _splitRGB = function(rgb) {
  37. var c = (rgb.slice(rgb.indexOf('(')+1, rgb.indexOf(')'))).split(',');
  38. var flag = false, obj;
  39. c = c.map(function(n,i) {
  40. return (i !== 3) ? parseInt(n, 10) : flag = true, parseFloat(n);
  41. });
  42. obj = {
  43. r: c[0],
  44. g: c[1],
  45. b: c[2]
  46. };
  47. if (flag) obj.a = c[3];
  48. return obj;
  49. };
  50.  
  51. var color = function(col) {
  52. var slc = col.slice(0,1);
  53. if (slc === '#') {
  54. return _splitHEX(col);
  55. } else if (slc.toLowerCase() === 'r') {
  56. return _splitRGB(col);
  57. } else {
  58. console.log('!Ooops! RGBvalues.color('+col+') : HEX, RGB, or RGBa strings only');
  59. }
  60. };
  61.  
  62. return {
  63. color: color
  64. };
  65. }());
  66.  
  67. let newCol = localStorage.getItem("userscript-color") || "rgb(29, 155, 240)";
  68.  
  69. let isOnColorSettings = false;
  70.  
  71. const updatePage = (runInputUpdate = true) => {
  72. let prevFound = isOnColorSettings;
  73. let found = false;
  74. var allElems = document.querySelectorAll("*");
  75.  
  76. for (const el of allElems) {
  77. const style = window.getComputedStyle(el);
  78. let parsedBg = RGBvalues.color(style.backgroundColor);
  79. if (!parsedBg.a) {
  80. if ((parsedBg.r === 29 && parsedBg.g === 155 && parsedBg.b === 240) || el.ariaFlowto) {
  81. el.ariaFlowto = true;
  82. const parsedNewCol = RGBvalues.color(newCol);
  83. const final = `rgba(${parsedNewCol.r}, ${parsedNewCol.g}, ${parsedNewCol.b}, ${parsedBg.a || 1})`;
  84. el.animate([ { backgroundColor: final } ], { duration: 0, fill: "forwards" });
  85. }
  86. }
  87. parsedBg = RGBvalues.color(style.color);
  88. if ((parsedBg.r === 29 && parsedBg.g === 155 && parsedBg.b === 240) || el.ariaRelevant) {
  89. el.ariaRelevant = true;
  90. const parsedNewCol = RGBvalues.color(newCol);
  91. let final = `rgba(${parsedNewCol.r}, ${parsedNewCol.g}, ${parsedNewCol.b}, ${parsedBg.a || 1})`;
  92. el.animate([ { color: final } ], { duration: 0, fill: "forwards" });
  93. }
  94. if (el.ariaLabel === "Color options") {
  95. found = true;
  96. }
  97. }
  98. if (!runInputUpdate) return;
  99. isOnColorSettings = found;
  100. if (isOnColorSettings === false) return;
  101. if (document.getElementById("already-applied-custom")) return;
  102. const colorContainer = document.querySelector("*[aria-label=\"Color options\"]");
  103. colorContainer.style.justifyContent = "left";
  104. colorContainer.style.padding = "8px 16px";
  105. colorContainer.innerHTML = "";
  106. colorContainer.id = "already-applied-custom";
  107. const input = document.createElement("input");
  108. input.value = newCol;
  109. input.type = "color";
  110. input.oninput = (e) => {
  111. newCol = input.value;
  112. localStorage.setItem("userscript-color", input.value);
  113. updatePage(false);
  114. }
  115. colorContainer.append(input);
  116. }
  117.  
  118. const observer = new MutationObserver(() => {
  119. updatePage();
  120. });
  121.  
  122. observer.observe(document.body, {
  123. subtree: true,
  124. childList: true
  125. });
  126.  
  127. updatePage();