TEXT   213   0
   577 5.97 KB    233

FlagRandomizer.js

By Script_Filly
Created: 2022-01-19 02:15:55
Updated: 2022-01-25 11:34:59
Expiry: Never

  1. // ==UserScript==
  2. // @name Flag Randomizer
  3. // @grant none
  4. // @version 0.2.3
  5. // @author Fillyanon, ScriptFilly, RandomSchmuck, Marker
  6. // @description Adds a flag randomizer to the Board.
  7. // ==/UserScript==
  8.  
  9. "use strict";
  10.  
  11. // Possible values are: Submit, Keystroke, Timer
  12. const changeFlagOn = "Submit";
  13.  
  14. const flags = Object.freeze({
  15. MISC: ["4CC", "AN"],
  16.  
  17. G4: [
  18. "AJ",
  19. "ANF",
  20. "APB",
  21. "AU",
  22. "BB",
  23. "BM",
  24. "BP",
  25. "BS",
  26. "CB",
  27. "CG",
  28. "CHE",
  29. "CL",
  30. "CO",
  31. "DAY",
  32. "DD",
  33. "DER",
  34. "DIS",
  35. "DT",
  36. "FAU",
  37. "FL",
  38. "FLE",
  39. "GI",
  40. "LI",
  41. "LT",
  42. "LY",
  43. "MA",
  44. "MAU",
  45. "MIN",
  46. "NI",
  47. "NUR",
  48. "OCT",
  49. "PAR",
  50. "PC",
  51. "PCE",
  52. "PI",
  53. "PLU",
  54. "PM",
  55. "QC",
  56. "RAR",
  57. "RD",
  58. "RLU",
  59. "S1L",
  60. "SCO",
  61. "SHI",
  62. "SIL",
  63. "SP",
  64. "SPI",
  65. "STA",
  66. "STL",
  67. "SUN",
  68. "SUS",
  69. "SWB",
  70. "TS",
  71. "TWI",
  72. "TX",
  73. "VS",
  74. "ZE",
  75. ],
  76.  
  77. G5: ["HT", "IZ", "PP", "SPT", "ZS", "SS"],
  78.  
  79. EQG: [
  80. "ADA",
  81. "AB",
  82. "SON",
  83. "EQA",
  84. "EQF",
  85. "EQP",
  86. "EQR",
  87. "EQT",
  88. "EQI",
  89. "EQS",
  90. "ERA",
  91. ],
  92.  
  93. TFH: ["TFA", "TFO", "TFP", "TFS", "TFT", "TFV", "TP"],
  94. });
  95.  
  96. const allFlags = Object.values(flags).flat();
  97.  
  98. const getRand = (coll) => {
  99. return coll[Math.floor(Math.random() * coll.length)];
  100. };
  101.  
  102. const getPost = () => {
  103. const use4chanX = document.getElementById("qr");
  104. const quickReply = document.getElementById("qrForm");
  105. const qrForm = use4chanX ?? quickReply;
  106. return qrForm ?? document.forms.post;
  107. };
  108.  
  109. const changeFlag = () => {
  110. if (selector.value == "OFF") return;
  111. const post = getPost();
  112. const flagSelector = post.querySelector(".flagSelector");
  113. const origValue = flagSelector.value;
  114. flagSelector.value = (selector.value != "ALL") ? getRand(flags[selector.value]) : getRand(allFlags);
  115.  
  116. // Workaround for 4chanX issue where its flag selector is 3 missing flags.
  117. // Attempting to assign a Selector to an option that doesn't exist would
  118. // reset it's value to "".
  119. // When this happens, we restore it to its initial value.
  120. if (flagSelector.value == "") flagSelector.value = origValue;
  121. };
  122.  
  123. const makeOpt = (option) => {
  124. const opt = document.createElement("option");
  125. opt.value = opt.innerText = option;
  126. selector.appendChild(opt);
  127. };
  128.  
  129. const selector = document.createElement('select');
  130. const botLine = document.querySelector('.navLinksBot');
  131. selector.style.marginLeft = '1rem';
  132. Object.keys(flags).forEach(key => makeOpt(key));
  133. makeOpt('ALL');
  134. makeOpt('OFF');
  135. selector.addEventListener('change', () => {
  136. window.localStorage.setItem("flagGroup", selector.value);
  137. });
  138. selector.value = window.localStorage.getItem("flagGroup") ?? "G4";
  139. botLine.appendChild(selector);
  140.  
  141. // Setup Randomizer Functions
  142.  
  143. const creationObserver = (function () {
  144. const observedNodes = [];
  145. const callbacks = [];
  146. const executeCallback = (fn, node) => {
  147. if (observedNodes.includes(node))
  148. return;
  149. observedNodes.push(node);
  150. fn(node);
  151. };
  152. const obs = new MutationObserver(mutationRecords => {
  153. mutationRecords.forEach(mutation => {
  154. mutation.addedNodes.forEach(node => {
  155. if (!(node instanceof HTMLElement))
  156. return;
  157. callbacks.forEach(([selector, fn]) => {
  158. if (node.matches(selector))
  159. executeCallback(fn, node);
  160. node.querySelectorAll(selector).forEach(childNode => executeCallback(fn, childNode));
  161. });
  162. });
  163. });
  164. });
  165. obs.observe(document.body, { childList: true, subtree: true });
  166. return function (selector, fn) {
  167. document.querySelectorAll(selector).forEach(node => executeCallback(fn, node));
  168. callbacks.push([selector, fn]);
  169. };
  170. })();
  171.  
  172. const selectors = [
  173. 'form[name="post"]', // 4chan default post
  174. 'form[name="qrPost"]', // 4chanNX quick reply form
  175. 'div#qr form', // 4chanX quick reply form
  176. ].join(', ');
  177.  
  178. // Keystroke
  179. function debounce(func, wait, immediate) {
  180. let timeout;
  181. return function () {
  182. const context = this, args = arguments;
  183. const later = function () {
  184. timeout = null;
  185. if (!immediate) func.apply(context, args);
  186. };
  187. const callNow = immediate && !timeout;
  188. clearTimeout(timeout);
  189. timeout = setTimeout(later, wait);
  190. if (callNow) func.apply(context, args);
  191. };
  192. }
  193. const changeAfterKeystroke = debounce(changeFlag, 250, false);
  194. const config = {attributes: true, childList: true, subtree: true};
  195.  
  196. // Randomize the flag
  197. if (changeFlagOn === "Keystroke") {
  198. const keystrokeHandler = function() {
  199. const is4chanXQrActive = document.getElementById("qr");
  200. const post = getPost();
  201.  
  202. // update the flag
  203. const reply = (is4chanXQrActive)
  204. ? post.querySelector("textarea[data-name=\"com\"]")
  205. : post.querySelector("textarea[name=\"com\"]");
  206. reply.addEventListener("input", changeAfterKeystroke);
  207. }
  208. creationObserver(selectors, form => {
  209. if (!(form instanceof HTMLFormElement))
  210. return;
  211. keystrokeHandler();
  212. });
  213. } else if (changeFlagOn === "Timer") {
  214. setInterval(() => {
  215. changeFlag();
  216. }, 500);
  217. } else if (changeFlagOn === "Submit") {
  218. const submitHandler = (event) => {
  219. const target = event.target;
  220. if (!target.matches('input[type="submit"]'))
  221. return;
  222. const form = event.currentTarget;
  223. changeFlag(form);
  224. }
  225. creationObserver(selectors, form => {
  226. if (!(form instanceof HTMLFormElement))
  227. return;
  228. form.addEventListener('click', submitHandler, { capture: true });
  229. });
  230. }

FlagRandomizer.js

by Script_Filly