JanitorAI Context Maker

Adds a Location and Character System to JanitorAI with nested grouping functionality

  1. // ==UserScript==
  2. // @name JanitorAI Context Maker
  3. // @namespace http://tampermonkey.net/
  4. // @version 5.7.3
  5. // @license MIT
  6. // @description Adds a Location and Character System to JanitorAI with nested grouping functionality
  7. // @match https://janitorai.com/chats/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=https://janitorai.com/
  9. // @grant GM.setValue
  10. // @grant GM.getValue
  11. // @grant GM.xmlHttpRequest
  12. // @connect *
  13. // ==/UserScript==
  14.  
  15. (async function() {
  16. 'use strict';
  17.  
  18. let c_radius = 300;
  19. let c_labelFontSize = 12;
  20. let c_nodeSize = 24;
  21. let l_radius = 300;
  22. let l_labelFontSize = 12;
  23. let l_nodeSize = 24;
  24.  
  25. let customContextMenu;
  26. let initialMouseX;
  27. let initialMouseY;
  28. const hideDistance = 100; // Distance in pixels to hide the menu
  29. let ItemTransferTarget = null;
  30.  
  31. //.addEventListener('contextmenu', showMenu);
  32. function CreateContextMenu(mainColor, textColor, borderColor, optionNames, optionFunctions) {
  33. // Destroy existing menu if present
  34. DestroyContextMenu();
  35.  
  36. // Create a new custom menu
  37. customContextMenu = document.createElement('div');
  38. customContextMenu.style.position = 'absolute';
  39. customContextMenu.style.background = mainColor;
  40. customContextMenu.style.color = textColor;
  41. customContextMenu.style.border = `1px solid ${borderColor}`; // Use the borderColor parameter
  42.  
  43. // Adjusted padding for 2/3 size
  44. customContextMenu.style.padding = '3.33px 6.67px';
  45. customContextMenu.style.display = 'none';
  46. customContextMenu.style.zIndex = '99999999'; // High z-index
  47.  
  48. // Add menu items with dividers
  49. optionNames.forEach((name, index) => {
  50. const menuItem = document.createElement('div');
  51.  
  52. // Adjusted padding and font size for 2/3 size
  53. menuItem.style.padding = '3.33px 0';
  54. menuItem.style.cursor = 'pointer';
  55. menuItem.style.fontSize = '0.67em';
  56. menuItem.textContent = name;
  57. menuItem.addEventListener('click', () => {
  58. optionFunctions[index]();
  59. hideMenu();
  60. });
  61.  
  62. // Append menu item
  63. customContextMenu.appendChild(menuItem);
  64.  
  65. // Add a divider except after the last item
  66. if (index < optionNames.length - 1) {
  67. const divider = document.createElement('div');
  68. divider.style.borderTop = `1px solid ${borderColor}`; // Use the borderColor parameter
  69. customContextMenu.appendChild(divider);
  70. }
  71. });
  72.  
  73. // Append the menu to the body
  74. document.body.appendChild(customContextMenu);
  75.  
  76. // Check mouse distance
  77. document.addEventListener('mousemove', trackMouseDistance);
  78. }
  79.  
  80. function DestroyContextMenu() {
  81. if (customContextMenu) {
  82. customContextMenu.remove();
  83. document.removeEventListener('contextmenu', showMenu);
  84. document.removeEventListener('click', hideMenu);
  85. document.removeEventListener('mousemove', trackMouseDistance);
  86. customContextMenu = null;
  87. }
  88. }
  89.  
  90. function showMenu(event) {
  91. event.preventDefault();
  92. initialMouseX = event.clientX;
  93. initialMouseY = event.clientY;
  94. if (customContextMenu) {
  95. customContextMenu.style.top = `${event.clientY}px`;
  96. customContextMenu.style.left = `${event.clientX}px`;
  97. customContextMenu.style.display = 'block';
  98. }
  99. }
  100.  
  101. function hideMenu() {
  102. if (customContextMenu) {
  103. customContextMenu.style.display = 'none';
  104. }
  105. }
  106.  
  107. function trackMouseDistance(event) {
  108. if (customContextMenu && customContextMenu.style.display === 'block') {
  109. const distance = Math.sqrt(
  110. Math.pow(event.clientX - initialMouseX, 2) +
  111. Math.pow(event.clientY - initialMouseY, 2)
  112. );
  113. if (distance > hideDistance && !customContextMenu.contains(event.target)) {
  114. hideMenu();
  115. }
  116. }
  117. }
  118.  
  119. // Define Themes
  120. const themes = {
  121. // Default Dark Theme
  122. dark: {
  123. '--bg-color': 'rgba(34, 34, 34, var(--ui-transparency))',
  124. '--bg-color-full': 'rgba(34, 34, 34, 1)',
  125. '--bg-tool': 'rgba(34, 34, 34, 0.8)',
  126. '--text-color': '#ffffff',
  127. '--text-color-darker': '#cccccc',
  128. '--border-color': 'rgba(68, 68, 68, var(--ui-transparency))',
  129. '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))',
  130. '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))',
  131. '--success-bg-color': 'rgba(40, 167, 69, var(--ui-transparency))',
  132. '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))',
  133. '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))',
  134. '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))',
  135. '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))',
  136. '--shadow-color': 'rgba(0,0,0,0.5)',
  137. '--link-color': '#8cb3ff',
  138. '--code-bg-color': 'rgba(0, 0, 0, 0.3)',
  139. '--code-text-color': '#ff9d00'
  140. },
  141.  
  142. // Default Light Theme
  143. light: {
  144. '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))',
  145. '--bg-color-full': 'rgba(255, 255, 255, 1)',
  146. '--bg-tool': 'rgba(255, 255, 255, 0.8)',
  147. '--text-color': '#000000',
  148. '--text-color-darker': '#333333',
  149. '--border-color': 'rgba(204, 204, 204, var(--ui-transparency))',
  150. '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))',
  151. '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))',
  152. '--success-bg-color': 'rgba(40, 167, 69, var(--ui-transparency))',
  153. '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))',
  154. '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))',
  155. '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))',
  156. '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))',
  157. '--shadow-color': 'rgba(0,0,0,0.1)',
  158. '--link-color': '#007bff',
  159. '--code-bg-color': 'rgba(0, 0, 0, 0.05)',
  160. '--code-text-color': '#d63384'
  161. },
  162.  
  163. // Sepia Themes
  164. sepia_light: {
  165. '--bg-color': 'rgba(244, 232, 208, var(--ui-transparency))',
  166. '--bg-color-full': 'rgba(244, 232, 208, 1)',
  167. '--bg-tool': 'rgba(244, 232, 208, 0.8)',
  168. '--text-color': '#2e241c',
  169. '--text-color-darker': '#1a140f',
  170. '--border-color': 'rgba(193, 154, 107, var(--ui-transparency))',
  171. '--button-bg-color': 'rgba(160, 82, 45, var(--ui-transparency))',
  172. '--active-char-color': 'rgba(210, 180, 140, var(--ui-transparency))',
  173. '--success-bg-color': 'rgba(107, 68, 35, var(--ui-transparency))',
  174. '--info-bg-color': 'rgba(194, 148, 110, var(--ui-transparency))',
  175. '--warning-bg-color': 'rgba(215, 172, 116, var(--ui-transparency))',
  176. '--muted-bg-color': 'rgba(160, 130, 94, var(--ui-transparency))',
  177. '--danger-bg-color': 'rgba(168, 96, 50, var(--ui-transparency))',
  178. '--shadow-color': 'rgba(0,0,0,0.3)',
  179. '--link-color': '#6c757d',
  180. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  181. '--code-text-color': '#a0522d'
  182. },
  183. sepia_dark: {
  184. '--bg-color': 'rgba(60, 45, 31, var(--ui-transparency))',
  185. '--bg-color-full': 'rgba(60, 45, 31, 1)',
  186. '--bg-tool': 'rgba(60, 45, 31, 0.8)',
  187. '--text-color': '#d8c6b2',
  188. '--text-color-darker': '#b8a493',
  189. '--border-color': 'rgba(102, 75, 50, var(--ui-transparency))',
  190. '--button-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))',
  191. '--active-char-color': 'rgba(210, 180, 140, var(--ui-transparency))',
  192. '--success-bg-color': 'rgba(107, 68, 35, var(--ui-transparency))',
  193. '--info-bg-color': 'rgba(139, 101, 68, var(--ui-transparency))',
  194. '--warning-bg-color': 'rgba(205, 133, 63, var(--ui-transparency))',
  195. '--muted-bg-color': 'rgba(122, 91, 62, var(--ui-transparency))',
  196. '--danger-bg-color': 'rgba(165, 42, 42, var(--ui-transparency))',
  197. '--shadow-color': 'rgba(0,0,0,0.6)',
  198. '--link-color': '#d2b48c',
  199. '--code-bg-color': 'rgba(0, 0, 0, 0.3)',
  200. '--code-text-color': '#deb887'
  201. },
  202.  
  203. // Solarized Themes
  204. solarized_light: {
  205. '--bg-color': 'rgba(253, 246, 227, var(--ui-transparency))',
  206. '--bg-color-full': 'rgba(253, 246, 227, 1)',
  207. '--bg-tool': 'rgba(253, 246, 227, 0.8)',
  208. '--text-color': '#47565c',
  209. '--text-color-darker': '#2c3438',
  210. '--border-color': 'rgba(238, 232, 213, var(--ui-transparency))',
  211. '--button-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))',
  212. '--active-char-color': 'rgba(133, 153, 0, var(--ui-transparency))',
  213. '--success-bg-color': 'rgba(133, 153, 0, var(--ui-transparency))',
  214. '--info-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))',
  215. '--warning-bg-color': 'rgba(181, 137, 0, var(--ui-transparency))',
  216. '--muted-bg-color': 'rgba(147, 161, 161, var(--ui-transparency))',
  217. '--danger-bg-color': 'rgba(220, 50, 47, var(--ui-transparency))',
  218. '--shadow-color': 'rgba(0,0,0,0.2)',
  219. '--link-color': '#2aa198',
  220. '--code-bg-color': 'rgba(0, 43, 54, 0.7)',
  221. '--code-text-color': '#cb4b16'
  222. },
  223. solarized_dark: {
  224. '--bg-color': 'rgba(0, 43, 54, var(--ui-transparency))',
  225. '--bg-color-full': 'rgba(0, 43, 54, 1)',
  226. '--bg-tool': 'rgba(0, 43, 54, 0.8)',
  227. '--text-color': '#eee8d5',
  228. '--text-color-darker': '#cdc5b0',
  229. '--border-color': 'rgba(7, 54, 66, var(--ui-transparency))',
  230. '--button-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))',
  231. '--active-char-color': 'rgba(133, 153, 0, var(--ui-transparency))',
  232. '--success-bg-color': 'rgba(133, 153, 0, var(--ui-transparency))',
  233. '--info-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))',
  234. '--warning-bg-color': 'rgba(181, 137, 0, var(--ui-transparency))',
  235. '--muted-bg-color': 'rgba(88, 110, 117, var(--ui-transparency))',
  236. '--danger-bg-color': 'rgba(220, 50, 47, var(--ui-transparency))',
  237. '--shadow-color': 'rgba(0, 0, 0, 0.4)',
  238. '--link-color': '#2aa198',
  239. '--code-bg-color': 'rgba(253, 246, 227, 0.1)',
  240. '--code-text-color': '#cb4b16'
  241. },
  242.  
  243. // Forest Themes (Green)
  244. forest_light: {
  245. '--bg-color': 'rgba(233, 245, 233, var(--ui-transparency))',
  246. '--bg-color-full': 'rgba(233, 245, 233, 1)',
  247. '--bg-tool': 'rgba(233, 245, 233, 0.8)',
  248. '--text-color': '#2f4f2f',
  249. '--text-color-darker': '#1c2f1c',
  250. '--border-color': 'rgba(209, 230, 209, var(--ui-transparency))',
  251. '--button-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))',
  252. '--active-char-color': 'rgba(34, 139, 34, var(--ui-transparency))',
  253. '--success-bg-color': 'rgba(144, 238, 144, var(--ui-transparency))',
  254. '--info-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))',
  255. '--warning-bg-color': 'rgba(240, 230, 140, var(--ui-transparency))',
  256. '--muted-bg-color': 'rgba(152, 251, 152, var(--ui-transparency))',
  257. '--danger-bg-color': 'rgba(205, 92, 92, var(--ui-transparency))',
  258. '--shadow-color': 'rgba(0,0,0,0.2)',
  259. '--link-color': '#3cb371',
  260. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  261. '--code-text-color': '#8b4513'
  262. },
  263. forest_dark: {
  264. '--bg-color': 'rgba(34, 49, 34, var(--ui-transparency))',
  265. '--bg-color-full': 'rgba(34, 49, 34, 1)',
  266. '--bg-tool': 'rgba(34, 49, 34, 0.8)',
  267. '--text-color': '#e0f7e9',
  268. '--text-color-darker': '#b0c7b9',
  269. '--border-color': 'rgba(46, 61, 46, var(--ui-transparency))',
  270. '--button-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))',
  271. '--active-char-color': 'rgba(144, 238, 144, var(--ui-transparency))',
  272. '--success-bg-color': 'rgba(34, 139, 34, var(--ui-transparency))',
  273. '--info-bg-color': 'rgba(144, 238, 144, var(--ui-transparency))',
  274. '--warning-bg-color': 'rgba(189, 183, 107, var(--ui-transparency))',
  275. '--muted-bg-color': 'rgba(85, 107, 47, var(--ui-transparency))',
  276. '--danger-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))',
  277. '--shadow-color': 'rgba(0,0,0,0.4)',
  278. '--link-color': '#8fbc8f',
  279. '--code-bg-color': 'rgba(0, 0, 0, 0.3)',
  280. '--code-text-color': '#ffd700'
  281. },
  282.  
  283. // Ocean Themes (Blue)
  284. ocean_light: {
  285. '--bg-color': 'rgba(224, 244, 252, var(--ui-transparency))',
  286. '--bg-color-full': 'rgba(224, 244, 252, 1)',
  287. '--bg-tool': 'rgba(224, 244, 252, 0.8)',
  288. '--text-color': '#004766',
  289. '--text-color-darker': '#00334c',
  290. '--border-color': 'rgba(204, 232, 245, var(--ui-transparency))',
  291. '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))',
  292. '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))',
  293. '--success-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))',
  294. '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))',
  295. '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))',
  296. '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))',
  297. '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))',
  298. '--shadow-color': 'rgba(0,0,0,0.2)',
  299. '--link-color': '#0077b6',
  300. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  301. '--code-text-color': '#ff7f50'
  302. },
  303. ocean_dark: {
  304. '--bg-color': 'rgba(0, 30, 60, var(--ui-transparency))',
  305. '--bg-color-full': 'rgba(0, 30, 60, 1)',
  306. '--bg-tool': 'rgba(0, 30, 60, 0.8)',
  307. '--text-color': '#ffffff',
  308. '--text-color-darker': '#cccccc',
  309. '--border-color': 'rgba(0, 53, 102, var(--ui-transparency))',
  310. '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))',
  311. '--active-char-color': 'rgba(135, 206, 235, var(--ui-transparency))',
  312. '--success-bg-color': 'rgba(46, 139, 87, var(--ui-transparency))',
  313. '--info-bg-color': 'rgba(0, 96, 100, var(--ui-transparency))',
  314. '--warning-bg-color': 'rgba(255, 140, 0, var(--ui-transparency))',
  315. '--muted-bg-color': 'rgba(47, 79, 79, var(--ui-transparency))',
  316. '--danger-bg-color': 'rgba(139, 0, 0, var(--ui-transparency))',
  317. '--shadow-color': 'rgba(0,0,0,0.5)',
  318. '--link-color': '#00ffff',
  319. '--code-bg-color': 'rgba(0, 0, 0, 0.4)',
  320. '--code-text-color': '#ff7f50'
  321. },
  322.  
  323. // Sunset Themes (Red/Orange)
  324. sunset_light: {
  325. '--bg-color': 'rgba(255, 237, 219, var(--ui-transparency))',
  326. '--bg-color-full': 'rgba(255, 237, 219, 1)',
  327. '--bg-tool': 'rgba(255, 237, 219, 0.8)',
  328. '--text-color': '#5d1a1a',
  329. '--text-color-darker': '#3b0f0f',
  330. '--border-color': 'rgba(255, 214, 170, var(--ui-transparency))',
  331. '--button-bg-color': 'rgba(255, 87, 34, var(--ui-transparency))',
  332. '--active-char-color': 'rgba(255, 152, 0, var(--ui-transparency))',
  333. '--success-bg-color': 'rgba(255, 179, 71, var(--ui-transparency))',
  334. '--info-bg-color': 'rgba(255, 138, 101, var(--ui-transparency))',
  335. '--warning-bg-color': 'rgba(255, 112, 67, var(--ui-transparency))',
  336. '--muted-bg-color': 'rgba(255, 224, 178, var(--ui-transparency))',
  337. '--danger-bg-color': 'rgba(183, 28, 28, var(--ui-transparency))',
  338. '--shadow-color': 'rgba(0,0,0,0.2)',
  339. '--link-color': '#e65100',
  340. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  341. '--code-text-color': '#d50000'
  342. },
  343. sunset_dark: {
  344. '--bg-color': 'rgba(66, 28, 82, var(--ui-transparency))',
  345. '--bg-color-full': 'rgba(66, 28, 82, 1)',
  346. '--bg-tool': 'rgba(66, 28, 82, 0.8)',
  347. '--text-color': '#fce4ec',
  348. '--text-color-darker': '#f8bbd0',
  349. '--border-color': 'rgba(127, 63, 152, var(--ui-transparency))',
  350. '--button-bg-color': 'rgba(233, 30, 99, var(--ui-transparency))',
  351. '--active-char-color': 'rgba(255, 64, 129, var(--ui-transparency))',
  352. '--success-bg-color': 'rgba(186, 104, 200, var(--ui-transparency))',
  353. '--info-bg-color': 'rgba(142, 36, 170, var(--ui-transparency))',
  354. '--warning-bg-color': 'rgba(216, 27, 96, var(--ui-transparency))',
  355. '--muted-bg-color': 'rgba(123, 31, 162, var(--ui-transparency))',
  356. '--danger-bg-color': 'rgba(74, 20, 140, var(--ui-transparency))',
  357. '--shadow-color': 'rgba(0,0,0,0.4)',
  358. '--link-color': '#d81b60',
  359. '--code-bg-color': 'rgba(0, 0, 0, 0.3)',
  360. '--code-text-color': '#f50057'
  361. },
  362.  
  363. // Sunshine Themes (Yellow)
  364. sunshine_light: {
  365. '--bg-color': 'rgba(255, 249, 196, var(--ui-transparency))',
  366. '--bg-color-full': 'rgba(255, 249, 196, 1)',
  367. '--bg-tool': 'rgba(255, 249, 196, 0.8)',
  368. '--text-color': '#795548',
  369. '--text-color-darker': '#5d4037',
  370. '--border-color': 'rgba(255, 241, 118, var(--ui-transparency))',
  371. '--button-bg-color': 'rgba(255, 235, 59, var(--ui-transparency))',
  372. '--active-char-color': 'rgba(255, 179, 0, var(--ui-transparency))',
  373. '--success-bg-color': 'rgba(253, 216, 53, var(--ui-transparency))',
  374. '--info-bg-color': 'rgba(255, 202, 40, var(--ui-transparency))',
  375. '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))',
  376. '--muted-bg-color': 'rgba(255, 224, 130, var(--ui-transparency))',
  377. '--danger-bg-color': 'rgba(255, 152, 0, var(--ui-transparency))',
  378. '--shadow-color': 'rgba(0,0,0,0.2)',
  379. '--link-color': '#ffab00',
  380. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  381. '--code-text-color': '#ff6f00'
  382. },
  383. sunshine_dark: {
  384. '--bg-color': 'rgba(50, 50, 0, var(--ui-transparency))',
  385. '--bg-color-full': 'rgba(50, 50, 0, 1)',
  386. '--bg-tool': 'rgba(50, 50, 0, 0.8)',
  387. '--text-color': '#fff9c4',
  388. '--text-color-darker': '#fff59d',
  389. '--border-color': 'rgba(85, 85, 0, var(--ui-transparency))',
  390. '--button-bg-color': 'rgba(255, 214, 0, var(--ui-transparency))',
  391. '--active-char-color': 'rgba(255, 171, 0, var(--ui-transparency))',
  392. '--success-bg-color': 'rgba(255, 238, 88, var(--ui-transparency))',
  393. '--info-bg-color': 'rgba(255, 235, 59, var(--ui-transparency))',
  394. '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))',
  395. '--muted-bg-color': 'rgba(212, 175, 55, var(--ui-transparency))',
  396. '--danger-bg-color': 'rgba(255, 111, 0, var(--ui-transparency))',
  397. '--shadow-color': 'rgba(0,0,0,0.4)',
  398. '--link-color': '#ffab00',
  399. '--code-bg-color': 'rgba(0, 0, 0, 0.3)',
  400. '--code-text-color': '#ff6f00'
  401. },
  402.  
  403. // Twilight Themes (Indigo/Violet)
  404. twilight_light: {
  405. '--bg-color': 'rgba(230, 230, 250, var(--ui-transparency))',
  406. '--bg-color-full': 'rgba(230, 230, 250, 1)',
  407. '--bg-tool': 'rgba(230, 230, 250, 0.8)',
  408. '--text-color': '#4b0082',
  409. '--text-color-darker': '#2e0047',
  410. '--border-color': 'rgba(216, 191, 216, var(--ui-transparency))',
  411. '--button-bg-color': 'rgba(75, 0, 130, var(--ui-transparency))',
  412. '--active-char-color': 'rgba(138, 43, 226, var(--ui-transparency))',
  413. '--success-bg-color': 'rgba(111, 0, 255, var(--ui-transparency))',
  414. '--info-bg-color': 'rgba(153, 50, 204, var(--ui-transparency))',
  415. '--warning-bg-color': 'rgba(186, 85, 211, var(--ui-transparency))',
  416. '--muted-bg-color': 'rgba(148, 0, 211, var(--ui-transparency))',
  417. '--danger-bg-color': 'rgba(199, 21, 133, var(--ui-transparency))',
  418. '--shadow-color': 'rgba(0,0,0,0.2)',
  419. '--link-color': '#8a2be2',
  420. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  421. '--code-text-color': '#9400d3'
  422. },
  423. twilight_dark: {
  424. '--bg-color': 'rgba(18, 10, 30, var(--ui-transparency))',
  425. '--bg-color-full': 'rgba(18, 10, 30, 1)',
  426. '--bg-tool': 'rgba(18, 10, 30, 0.8)',
  427. '--text-color': '#d8bfd8',
  428. '--text-color-darker': '#dda0dd',
  429. '--border-color': 'rgba(49, 24, 73, var(--ui-transparency))',
  430. '--button-bg-color': 'rgba(138, 43, 226, var(--ui-transparency))',
  431. '--active-char-color': 'rgba(153, 50, 204, var(--ui-transparency))',
  432. '--success-bg-color': 'rgba(186, 85, 211, var(--ui-transparency))',
  433. '--info-bg-color': 'rgba(148, 0, 211, var(--ui-transparency))',
  434. '--warning-bg-color': 'rgba(221, 160, 221, var(--ui-transparency))',
  435. '--muted-bg-color': 'rgba(123, 104, 238, var(--ui-transparency))',
  436. '--danger-bg-color': 'rgba(199, 21, 133, var(--ui-transparency))',
  437. '--shadow-color': 'rgba(0,0,0,0.4)',
  438. '--link-color': '#ba55d3',
  439. '--code-bg-color': 'rgba(0, 0, 0, 0.3)',
  440. '--code-text-color': '#ee82ee'
  441. },
  442.  
  443. // Terminal Themes
  444. terminal_light: {
  445. '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))',
  446. '--bg-color-full': 'rgba(255, 255, 255, 1)',
  447. '--bg-tool': 'rgba(255, 255, 255, 0.8)',
  448. '--text-color': '#00ff00',
  449. '--text-color-darker': '#00cc00',
  450. '--border-color': 'rgba(0, 200, 0, var(--ui-transparency))',
  451. '--button-bg-color': 'rgba(0, 125, 0, var(--ui-transparency))',
  452. '--active-char-color': 'rgba(0, 75, 0, var(--ui-transparency))',
  453. '--success-bg-color': 'rgba(0, 128, 0, var(--ui-transparency))',
  454. '--info-bg-color': 'rgba(0, 125, 0, var(--ui-transparency))',
  455. '--warning-bg-color': 'rgba(100, 125, 0, var(--ui-transparency))',
  456. '--muted-bg-color': 'rgba(0, 128, 0, var(--ui-transparency))',
  457. '--danger-bg-color': 'rgba(100, 0, 0, var(--ui-transparency))',
  458. '--shadow-color': 'rgba(0, 50, 0, 0.5)',
  459. '--link-color': '#00ffff',
  460. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  461. '--code-text-color': '#7fff00'
  462. },
  463. terminal_dark: {
  464. '--bg-color': 'rgba(0, 0, 0, var(--ui-transparency))',
  465. '--bg-color-full': 'rgba(0, 0, 0, 1)',
  466. '--bg-tool': 'rgba(0, 0, 0, 0.8)',
  467. '--text-color': '#00ff00',
  468. '--text-color-darker': '#00cc00',
  469. '--border-color': 'rgba(0, 200, 0, var(--ui-transparency))',
  470. '--button-bg-color': 'rgba(0, 125, 0, var(--ui-transparency))',
  471. '--active-char-color': 'rgba(0, 75, 0, var(--ui-transparency))',
  472. '--success-bg-color': 'rgba(0, 128, 0, var(--ui-transparency))',
  473. '--info-bg-color': 'rgba(0, 125, 0, var(--ui-transparency))',
  474. '--warning-bg-color': 'rgba(100, 125, 0, var(--ui-transparency))',
  475. '--muted-bg-color': 'rgba(0, 128, 0, var(--ui-transparency))',
  476. '--danger-bg-color': 'rgba(100, 0, 0, var(--ui-transparency))',
  477. '--shadow-color': 'rgba(0, 50, 0, 0.5)',
  478. '--link-color': '#00ffff',
  479. '--code-bg-color': 'rgba(0, 0, 0, 0.8)',
  480. '--code-text-color': '#7fff00'
  481. },
  482.  
  483. // Retro Themes
  484. retro_light: {
  485. '--bg-color': 'rgba(196, 182, 187, var(--ui-transparency))',
  486. '--bg-color-full': 'rgba(196, 182, 187, 1)',
  487. '--bg-tool': 'rgba(196, 182, 187, 0.8)',
  488. '--text-color': '#191919',
  489. '--text-color-darker': '#000000',
  490. '--border-color': 'rgba(128, 128, 128, var(--ui-transparency))',
  491. '--button-bg-color': 'rgba(255, 105, 180, var(--ui-transparency))',
  492. '--active-char-color': 'rgba(255, 255, 0, var(--ui-transparency))',
  493. '--success-bg-color': 'rgba(50, 205, 50, var(--ui-transparency))',
  494. '--info-bg-color': 'rgba(135, 206, 235, var(--ui-transparency))',
  495. '--warning-bg-color': 'rgba(255, 165, 0, var(--ui-transparency))',
  496. '--muted-bg-color': 'rgba(128, 128, 128, var(--ui-transparency))',
  497. '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))',
  498. '--shadow-color': 'rgba(0,0,0,0.3)',
  499. '--link-color': '#1e90ff',
  500. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  501. '--code-text-color': '#ff1493'
  502. },
  503. retro_dark: {
  504. '--bg-color': 'rgba(34, 32, 52, var(--ui-transparency))',
  505. '--bg-color-full': 'rgba(34, 32, 52, 1)',
  506. '--bg-tool': 'rgba(34, 32, 52, 0.8)',
  507. '--text-color': '#c2c3c7',
  508. '--text-color-darker': '#8b8d90',
  509. '--border-color': 'rgba(69, 40, 60, var(--ui-transparency))',
  510. '--button-bg-color': 'rgba(255, 0, 77, var(--ui-transparency))',
  511. '--active-char-color': 'rgba(255, 163, 0, var(--ui-transparency))',
  512. '--success-bg-color': 'rgba(0, 232, 216, var(--ui-transparency))',
  513. '--info-bg-color': 'rgba(44, 232, 245, var(--ui-transparency))',
  514. '--warning-bg-color': 'rgba(255, 236, 39, var(--ui-transparency))',
  515. '--muted-bg-color': 'rgba(96, 86, 107, var(--ui-transparency))',
  516. '--danger-bg-color': 'rgba(172, 50, 50, var(--ui-transparency))',
  517. '--shadow-color': 'rgba(0,0,0,0.5)',
  518. '--link-color': '#ff004d',
  519. '--code-bg-color': 'rgba(69, 40, 60, 0.8)',
  520. '--code-text-color': '#ff77a8'
  521. },
  522.  
  523. // Neon Themes
  524. neon_light: {
  525. '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))',
  526. '--bg-color-full': 'rgba(255, 255, 255, 1)',
  527. '--bg-tool': 'rgba(255, 255, 255, 0.8)',
  528. '--text-color': '#000000',
  529. '--text-color-darker': '#333333',
  530. '--border-color': 'rgba(0, 0, 0, var(--ui-transparency))',
  531. '--button-bg-color': 'rgba(255, 0, 255, var(--ui-transparency))',
  532. '--active-char-color': 'rgba(0, 255, 0, var(--ui-transparency))',
  533. '--success-bg-color': 'rgba(0, 255, 0, var(--ui-transparency))',
  534. '--info-bg-color': 'rgba(0, 255, 255, var(--ui-transparency))',
  535. '--warning-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))',
  536. '--muted-bg-color': 'rgba(128, 128, 128, var(--ui-transparency))',
  537. '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))',
  538. '--shadow-color': 'rgba(0, 0, 0, 0.5)',
  539. '--link-color': '#ff00ff',
  540. '--code-bg-color': 'rgba(255, 255, 255, 0.8)',
  541. '--code-text-color': '#00ffff'
  542. },
  543. neon_dark: {
  544. '--bg-color': 'rgba(0, 0, 0, var(--ui-transparency))',
  545. '--bg-color-full': 'rgba(0, 0, 0, 1)',
  546. '--bg-tool': 'rgba(0, 0, 0, 0.8)',
  547. '--text-color': '#ffffff',
  548. '--text-color-darker': '#cccccc',
  549. '--border-color': 'rgba(255, 255, 255, var(--ui-transparency))',
  550. '--button-bg-color': 'rgba(255, 0, 255, var(--ui-transparency))',
  551. '--active-char-color': 'rgba(0, 255, 0, var(--ui-transparency))',
  552. '--success-bg-color': 'rgba(0, 255, 0, var(--ui-transparency))',
  553. '--info-bg-color': 'rgba(0, 255, 255, var(--ui-transparency))',
  554. '--warning-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))',
  555. '--muted-bg-color': 'rgba(255, 0, 255, var(--ui-transparency))',
  556. '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))',
  557. '--shadow-color': 'rgba(0, 255, 255, 0.5)',
  558. '--link-color': '#ff00ff',
  559. '--code-bg-color': 'rgba(0, 0, 0, 0.8)',
  560. '--code-text-color': '#00ffff'
  561. },
  562.  
  563. // Vintage Themes
  564. vintage_light: {
  565. '--bg-color': 'rgba(240, 230, 140, var(--ui-transparency))',
  566. '--bg-color-full': 'rgba(240, 230, 140, 1)',
  567. '--bg-tool': 'rgba(240, 230, 140, 0.8)',
  568. '--text-color': '#360505',
  569. '--text-color-darker': '#200303',
  570. '--border-color': 'rgba(139, 69, 19, var(--ui-transparency))',
  571. '--button-bg-color': 'rgba(128, 0, 0, var(--ui-transparency))',
  572. '--active-char-color': 'rgba(255, 215, 0, var(--ui-transparency))',
  573. '--success-bg-color': 'rgba(50, 205, 50, var(--ui-transparency))',
  574. '--info-bg-color': 'rgba(70, 130, 180, var(--ui-transparency))',
  575. '--warning-bg-color': 'rgba(218, 165, 32, var(--ui-transparency))',
  576. '--muted-bg-color': 'rgba(128, 128, 0, var(--ui-transparency))',
  577. '--danger-bg-color': 'rgba(178, 34, 34, var(--ui-transparency))',
  578. '--shadow-color': 'rgba(139, 69, 19, 0.5)',
  579. '--link-color': '#8b0000',
  580. '--code-bg-color': 'rgba(245, 222, 179, 0.5)',
  581. '--code-text-color': '#8b0000'
  582. },
  583. vintage_dark: {
  584. '--bg-color': 'rgba(60, 47, 34, var(--ui-transparency))',
  585. '--bg-color-full': 'rgba(60, 47, 34, 1)',
  586. '--bg-tool': 'rgba(60, 47, 34, 0.8)',
  587. '--text-color': '#e0d4b3',
  588. '--text-color-darker': '#c0b297',
  589. '--border-color': 'rgba(139, 69, 19, var(--ui-transparency))',
  590. '--button-bg-color': 'rgba(128, 0, 0, var(--ui-transparency))',
  591. '--active-char-color': 'rgba(218, 165, 32, var(--ui-transparency))',
  592. '--success-bg-color': 'rgba(107, 142, 35, var(--ui-transparency))',
  593. '--info-bg-color': 'rgba(70, 130, 180, var(--ui-transparency))',
  594. '--warning-bg-color': 'rgba(184, 134, 11, var(--ui-transparency))',
  595. '--muted-bg-color': 'rgba(128, 128, 0, var(--ui-transparency))',
  596. '--danger-bg-color': 'rgba(178, 34, 34, var(--ui-transparency))',
  597. '--shadow-color': 'rgba(0,0,0,0.5)',
  598. '--link-color': '#8b0000',
  599. '--code-bg-color': 'rgba(245, 222, 179, 0.3)',
  600. '--code-text-color': '#ffa07a'
  601. },
  602.  
  603. // Pastel Themes
  604. pastel_light: {
  605. '--bg-color': 'rgba(255, 228, 225, var(--ui-transparency))',
  606. '--bg-color-full': 'rgba(255, 228, 225, 1)',
  607. '--bg-tool': 'rgba(255, 228, 225, 0.8)',
  608. '--text-color': '#4d4d4d',
  609. '--text-color-darker': '#333333',
  610. '--border-color': 'rgba(255, 192, 203, var(--ui-transparency))',
  611. '--button-bg-color': 'rgba(135, 206, 235, var(--ui-transparency))',
  612. '--active-char-color': 'rgba(175, 238, 238, var(--ui-transparency))',
  613. '--success-bg-color': 'rgba(152, 251, 152, var(--ui-transparency))',
  614. '--info-bg-color': 'rgba(135, 206, 235, var(--ui-transparency))',
  615. '--warning-bg-color': 'rgba(255, 228, 181, var(--ui-transparency))',
  616. '--muted-bg-color': 'rgba(238, 130, 238, var(--ui-transparency))',
  617. '--danger-bg-color': 'rgba(240, 128, 128, var(--ui-transparency))',
  618. '--shadow-color': 'rgba(0,0,0,0.1)',
  619. '--link-color': '#ff69b4',
  620. '--code-bg-color': 'rgba(255, 250, 205, 0.8)',
  621. '--code-text-color': '#dc143c'
  622. },
  623. pastel_dark: {
  624. '--bg-color': 'rgba(75, 75, 75, var(--ui-transparency))',
  625. '--bg-color-full': 'rgba(75, 75, 75, 1)',
  626. '--bg-tool': 'rgba(75, 75, 75, 0.8)',
  627. '--text-color': '#e6e6e6',
  628. '--text-color-darker': '#cccccc',
  629. '--border-color': 'rgba(105, 105, 105, var(--ui-transparency))',
  630. '--button-bg-color': 'rgba(176, 196, 222, var(--ui-transparency))',
  631. '--active-char-color': 'rgba(119, 136, 153, var(--ui-transparency))',
  632. '--success-bg-color': 'rgba(144, 238, 144, var(--ui-transparency))',
  633. '--info-bg-color': 'rgba(135, 206, 250, var(--ui-transparency))',
  634. '--warning-bg-color': 'rgba(255, 160, 122, var(--ui-transparency))',
  635. '--muted-bg-color': 'rgba(205, 133, 63, var(--ui-transparency))',
  636. '--danger-bg-color': 'rgba(205, 92, 92, var(--ui-transparency))',
  637. '--shadow-color': 'rgba(0,0,0,0.5)',
  638. '--link-color': '#ff69b4',
  639. '--code-bg-color': 'rgba(255, 182, 193, 0.3)',
  640. '--code-text-color': '#dc143c'
  641. },
  642.  
  643. // Monokai Themes
  644. monokai_light: {
  645. '--bg-color': 'rgba(248, 248, 242, var(--ui-transparency))',
  646. '--bg-color-full': 'rgba(248, 248, 242, 1)',
  647. '--bg-tool': 'rgba(248, 248, 242, 0.8)',
  648. '--text-color': '#272822',
  649. '--text-color-darker': '#49483e',
  650. '--border-color': 'rgba(220, 220, 217, var(--ui-transparency))',
  651. '--button-bg-color': 'rgba(249, 38, 114, var(--ui-transparency))',
  652. '--active-char-color': 'rgba(166, 226, 46, var(--ui-transparency))',
  653. '--success-bg-color': 'rgba(166, 226, 46, var(--ui-transparency))',
  654. '--info-bg-color': 'rgba(102, 217, 239, var(--ui-transparency))',
  655. '--warning-bg-color': 'rgba(253, 151, 31, var(--ui-transparency))',
  656. '--muted-bg-color': 'rgba(117, 113, 94, var(--ui-transparency))',
  657. '--danger-bg-color': 'rgba(249, 38, 114, var(--ui-transparency))',
  658. '--shadow-color': 'rgba(0, 0, 0, 0.2)',
  659. '--link-color': '#ae81ff',
  660. '--code-bg-color': 'rgba(230, 230, 230, 0.8)',
  661. '--code-text-color': '#f92672'
  662. },
  663. monokai_dark: {
  664. '--bg-color': 'rgba(39, 40, 34, var(--ui-transparency))',
  665. '--bg-color-full': 'rgba(39, 40, 34, 1)',
  666. '--bg-tool': 'rgba(39, 40, 34, 0.8)',
  667. '--text-color': '#f8f8f2',
  668. '--text-color-darker': '#e6e6dc',
  669. '--border-color': 'rgba(73, 72, 62, var(--ui-transparency))',
  670. '--button-bg-color': 'rgba(249, 38, 114, var(--ui-transparency))',
  671. '--active-char-color': 'rgba(166, 226, 46, var(--ui-transparency))',
  672. '--success-bg-color': 'rgba(102, 217, 239, var(--ui-transparency))',
  673. '--info-bg-color': 'rgba(102, 217, 239, var(--ui-transparency))',
  674. '--warning-bg-color': 'rgba(253, 151, 31, var(--ui-transparency))',
  675. '--muted-bg-color': 'rgba(117, 113, 94, var(--ui-transparency))',
  676. '--danger-bg-color': 'rgba(249, 38, 114, var(--ui-transparency))',
  677. '--shadow-color': 'rgba(0,0,0,0.5)',
  678. '--link-color': '#ae81ff',
  679. '--code-bg-color': 'rgba(39, 40, 34, 0.8)',
  680. '--code-text-color': '#f92672'
  681. },
  682.  
  683. // Dracula Themes
  684. dracula_light: {
  685. '--bg-color': 'rgba(248, 248, 242, var(--ui-transparency))',
  686. '--bg-color-full': 'rgba(248, 248, 242, 1)',
  687. '--bg-tool': 'rgba(248, 248, 242, 0.8)',
  688. '--text-color': '#282a36',
  689. '--text-color-darker': '#44475a',
  690. '--border-color': 'rgba(211, 212, 219, var(--ui-transparency))',
  691. '--button-bg-color': 'rgba(98, 114, 164, var(--ui-transparency))',
  692. '--active-char-color': 'rgba(189, 147, 249, var(--ui-transparency))',
  693. '--success-bg-color': 'rgba(80, 250, 123, var(--ui-transparency))',
  694. '--info-bg-color': 'rgba(139, 233, 253, var(--ui-transparency))',
  695. '--warning-bg-color': 'rgba(241, 250, 140, var(--ui-transparency))',
  696. '--muted-bg-color': 'rgba(98, 114, 164, var(--ui-transparency))',
  697. '--danger-bg-color': 'rgba(255, 85, 85, var(--ui-transparency))',
  698. '--shadow-color': 'rgba(0, 0, 0, 0.2)',
  699. '--link-color': '#6272a4',
  700. '--code-bg-color': 'rgba(225, 225, 232, 0.8)',
  701. '--code-text-color': '#ff79c6'
  702. },
  703. dracula_dark: {
  704. '--bg-color': 'rgba(40, 42, 54, var(--ui-transparency))',
  705. '--bg-color-full': 'rgba(40, 42, 54, 1)',
  706. '--bg-tool': 'rgba(40, 42, 54, 0.8)',
  707. '--text-color': '#f8f8f2',
  708. '--text-color-darker': '#e6e6dc',
  709. '--border-color': 'rgba(68, 71, 90, var(--ui-transparency))',
  710. '--button-bg-color': 'rgba(98, 114, 164, var(--ui-transparency))',
  711. '--active-char-color': 'rgba(189, 147, 249, var(--ui-transparency))',
  712. '--success-bg-color': 'rgba(80, 250, 123, var(--ui-transparency))',
  713. '--info-bg-color': 'rgba(139, 233, 253, var(--ui-transparency))',
  714. '--warning-bg-color': 'rgba(241, 250, 140, var(--ui-transparency))',
  715. '--muted-bg-color': 'rgba(98, 114, 164, var(--ui-transparency))',
  716. '--danger-bg-color': 'rgba(255, 85, 85, var(--ui-transparency))',
  717. '--shadow-color': 'rgba(0,0,0,0.5)',
  718. '--link-color': '#6272a4',
  719. '--code-bg-color': 'rgba(68, 71, 90, 0.8)',
  720. '--code-text-color': '#ff79c6'
  721. },
  722.  
  723. // Gruvbox Themes
  724. gruvbox_light: {
  725. '--bg-color': 'rgba(251, 241, 199, var(--ui-transparency))',
  726. '--bg-color-full': 'rgba(251, 241, 199, 1)',
  727. '--bg-tool': 'rgba(251, 241, 199, 0.8)',
  728. '--text-color': '#282828',
  729. '--text-color-darker': '#1d2021',
  730. '--border-color': 'rgba(213, 196, 161, var(--ui-transparency))',
  731. '--button-bg-color': 'rgba(184, 187, 38, var(--ui-transparency))',
  732. '--active-char-color': 'rgba(184, 187, 38, var(--ui-transparency))',
  733. '--success-bg-color': 'rgba(121, 116, 14, var(--ui-transparency))',
  734. '--info-bg-color': 'rgba(7, 102, 120, var(--ui-transparency))',
  735. '--warning-bg-color': 'rgba(215, 153, 33, var(--ui-transparency))',
  736. '--muted-bg-color': 'rgba(146, 131, 116, var(--ui-transparency))',
  737. '--danger-bg-color': 'rgba(204, 36, 29, var(--ui-transparency))',
  738. '--shadow-color': 'rgba(0,0,0,0.2)',
  739. '--link-color': '#458588',
  740. '--code-bg-color': 'rgba(235, 219, 178, 0.8)',
  741. '--code-text-color': '#9d0006'
  742. },
  743. gruvbox_dark: {
  744. '--bg-color': 'rgba(40, 40, 40, var(--ui-transparency))',
  745. '--bg-color-full': 'rgba(40, 40, 40, 1)',
  746. '--bg-tool': 'rgba(40, 40, 40, 0.8)',
  747. '--text-color': '#ebdbb2',
  748. '--text-color-darker': '#d5c4a1',
  749. '--border-color': 'rgba(60, 56, 54, var(--ui-transparency))',
  750. '--button-bg-color': 'rgba(213, 196, 161, var(--ui-transparency))',
  751. '--active-char-color': 'rgba(184, 187, 38, var(--ui-transparency))',
  752. '--success-bg-color': 'rgba(121, 116, 14, var(--ui-transparency))',
  753. '--info-bg-color': 'rgba(7, 102, 120, var(--ui-transparency))',
  754. '--warning-bg-color': 'rgba(215, 153, 33, var(--ui-transparency))',
  755. '--muted-bg-color': 'rgba(146, 131, 116, var(--ui-transparency))',
  756. '--danger-bg-color': 'rgba(204, 36, 29, var(--ui-transparency))',
  757. '--shadow-color': 'rgba(0, 0, 0, 0.4)',
  758. '--link-color': '#83a598',
  759. '--code-bg-color': 'rgba(60, 56, 54, 0.8)',
  760. '--code-text-color': '#fb4934'
  761. },
  762.  
  763. // Nord Themes
  764. nord_light: {
  765. '--bg-color': 'rgba(236, 239, 244, var(--ui-transparency))',
  766. '--bg-color-full': 'rgba(236, 239, 244, 1)',
  767. '--bg-tool': 'rgba(236, 239, 244, 0.8)',
  768. '--text-color': '#2e3440',
  769. '--text-color-darker': '#3b4252',
  770. '--border-color': 'rgba(208, 211, 216, var(--ui-transparency))',
  771. '--button-bg-color': 'rgba(94, 129, 172, var(--ui-transparency))',
  772. '--active-char-color': 'rgba(136, 192, 208, var(--ui-transparency))',
  773. '--success-bg-color': 'rgba(163, 190, 140, var(--ui-transparency))',
  774. '--info-bg-color': 'rgba(129, 161, 193, var(--ui-transparency))',
  775. '--warning-bg-color': 'rgba(235, 203, 139, var(--ui-transparency))',
  776. '--muted-bg-color': 'rgba(186, 190, 194, var(--ui-transparency))',
  777. '--danger-bg-color': 'rgba(191, 97, 106, var(--ui-transparency))',
  778. '--shadow-color': 'rgba(0, 0, 0, 0.2)',
  779. '--link-color': '#5e81ac',
  780. '--code-bg-color': 'rgba(208, 211, 216, 0.8)',
  781. '--code-text-color': '#bf616a'
  782. },
  783. nord_dark: {
  784. '--bg-color': 'rgba(46, 52, 64, var(--ui-transparency))',
  785. '--bg-color-full': 'rgba(46, 52, 64, 1)',
  786. '--bg-tool': 'rgba(46, 52, 64, 0.8)',
  787. '--text-color': '#d8dee9',
  788. '--text-color-darker': '#eceff4',
  789. '--border-color': 'rgba(59, 66, 82, var(--ui-transparency))',
  790. '--button-bg-color': 'rgba(94, 129, 172, var(--ui-transparency))',
  791. '--active-char-color': 'rgba(136, 192, 208, var(--ui-transparency))',
  792. '--success-bg-color': 'rgba(163, 190, 140, var(--ui-transparency))',
  793. '--info-bg-color': 'rgba(129, 161, 193, var(--ui-transparency))',
  794. '--warning-bg-color': 'rgba(235, 203, 139, var(--ui-transparency))',
  795. '--muted-bg-color': 'rgba(76, 86, 106, var(--ui-transparency))',
  796. '--danger-bg-color': 'rgba(191, 97, 106, var(--ui-transparency))',
  797. '--shadow-color': 'rgba(0,0,0,0.5)',
  798. '--link-color': '#88c0d0',
  799. '--code-bg-color': 'rgba(59, 66, 82, 0.8)',
  800. '--code-text-color': '#bf616a'
  801. },
  802.  
  803. // High Contrast Themes
  804. high_contrast_light: {
  805. '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))',
  806. '--bg-color-full': 'rgba(255, 255, 255, 1)',
  807. '--bg-tool': 'rgba(255, 255, 255, 0.8)',
  808. '--text-color': '#000000',
  809. '--text-color-darker': '#333333',
  810. '--border-color': 'rgba(0, 0, 0, var(--ui-transparency))',
  811. '--button-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))',
  812. '--active-char-color': 'rgba(255, 0, 0, var(--ui-transparency))',
  813. '--success-bg-color': 'rgba(0, 255, 0, var(--ui-transparency))',
  814. '--info-bg-color': 'rgba(0, 255, 255, var(--ui-transparency))',
  815. '--warning-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))',
  816. '--muted-bg-color': 'rgba(128, 128, 128, var(--ui-transparency))',
  817. '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))',
  818. '--shadow-color': 'rgba(0, 0, 0, 0.5)',
  819. '--link-color': '#0000ff',
  820. '--code-bg-color': 'rgba(0, 0, 0, 0.1)',
  821. '--code-text-color': '#0000ff'
  822. },
  823. high_contrast_dark: {
  824. '--bg-color': 'rgba(0, 0, 0, var(--ui-transparency))',
  825. '--bg-color-full': 'rgba(0, 0, 0, 1)',
  826. '--bg-tool': 'rgba(0, 0, 0, 0.8)',
  827. '--text-color': '#ffffff',
  828. '--text-color-darker': '#cccccc',
  829. '--border-color': 'rgba(255, 255, 255, var(--ui-transparency))',
  830. '--button-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))',
  831. '--active-char-color': 'rgba(255, 0, 0, var(--ui-transparency))',
  832. '--success-bg-color': 'rgba(0, 255, 0, var(--ui-transparency))',
  833. '--info-bg-color': 'rgba(0, 255, 255, var(--ui-transparency))',
  834. '--warning-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))',
  835. '--muted-bg-color': 'rgba(128, 128, 128, var(--ui-transparency))',
  836. '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))',
  837. '--shadow-color': 'rgba(255, 255, 255, 0.5)',
  838. '--link-color': '#00ffff',
  839. '--code-bg-color': 'rgba(255, 255, 255, 0.1)',
  840. '--code-text-color': '#ffff00'
  841. },
  842.  
  843. // Material Design Themes
  844. material_light: {
  845. '--bg-color': 'rgba(250, 250, 250, var(--ui-transparency))',
  846. '--bg-color-full': 'rgba(250, 250, 250, 1)',
  847. '--bg-tool': 'rgba(250, 250, 250, 0.8)',
  848. '--text-color': '#212121',
  849. '--text-color-darker': '#000000',
  850. '--border-color': 'rgba(224, 224, 224, var(--ui-transparency))',
  851. '--button-bg-color': 'rgba(33, 150, 243, var(--ui-transparency))',
  852. '--active-char-color': 'rgba(0, 188, 212, var(--ui-transparency))',
  853. '--success-bg-color': 'rgba(76, 175, 80, var(--ui-transparency))',
  854. '--info-bg-color': 'rgba(3, 169, 244, var(--ui-transparency))',
  855. '--warning-bg-color': 'rgba(255, 152, 0, var(--ui-transparency))',
  856. '--muted-bg-color': 'rgba(158, 158, 158, var(--ui-transparency))',
  857. '--danger-bg-color': 'rgba(244, 67, 54, var(--ui-transparency))',
  858. '--shadow-color': 'rgba(0, 0, 0, 0.2)',
  859. '--link-color': '#009688',
  860. '--code-bg-color': 'rgba(238, 238, 238, .8)',
  861. '--code-text-color': '#ff5722'
  862. },
  863. material_dark: {
  864. '--bg-color': 'rgba(33, 33, 33, var(--ui-transparency))',
  865. '--bg-color-full': 'rgba(33, 33, 33, 1)',
  866. '--bg-tool': 'rgba(33, 33, 33, 0.8)',
  867. '--text-color': '#ffffff',
  868. '--text-color-darker': '#bdbdbd',
  869. '--border-color': 'rgba(66, 66, 66, var(--ui-transparency))',
  870. '--button-bg-color': 'rgba(33, 150, 243, var(--ui-transparency))',
  871. '--active-char-color': 'rgba(0, 188, 212, var(--ui-transparency))',
  872. '--success-bg-color': 'rgba(76, 175, 80, var(--ui-transparency))',
  873. '--info-bg-color': 'rgba(3, 169, 244, var(--ui-transparency))',
  874. '--warning-bg-color': 'rgba(255, 152, 0, var(--ui-transparency))',
  875. '--muted-bg-color': 'rgba(117, 117, 117, var(--ui-transparency))',
  876. '--danger-bg-color': 'rgba(244, 67, 54, var(--ui-transparency))',
  877. '--shadow-color': 'rgba(0, 0, 0, 0.5)',
  878. '--link-color': '#009688',
  879. '--code-bg-color': 'rgba(55, 71, 79, 0.8)',
  880. '--code-text-color': '#ff5722'
  881. },
  882.  
  883. // Desert Themes
  884. desert_light: {
  885. '--bg-color': 'rgba(250, 243, 221, var(--ui-transparency))',
  886. '--bg-color-full': 'rgba(250, 243, 221, 1)',
  887. '--bg-tool': 'rgba(250, 243, 221, 0.8)',
  888. '--text-color': '#5e4a1e',
  889. '--text-color-darker': '#3e300f',
  890. '--border-color': 'rgba(230, 216, 173, var(--ui-transparency))',
  891. '--button-bg-color': 'rgba(205, 133, 63, var(--ui-transparency))',
  892. '--active-char-color': 'rgba(244, 164, 96, var(--ui-transparency))',
  893. '--success-bg-color': 'rgba(218, 165, 32, var(--ui-transparency))',
  894. '--info-bg-color': 'rgba(210, 180, 140, var(--ui-transparency))',
  895. '--warning-bg-color': 'rgba(184, 134, 11, var(--ui-transparency))',
  896. '--muted-bg-color': 'rgba(222, 184, 135, var(--ui-transparency))',
  897. '--danger-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))',
  898. '--shadow-color': 'rgba(0, 0, 0, 0.2)',
  899. '--link-color': '#cd853f',
  900. '--code-bg-color': 'rgba(245, 222, 179, 0.8)',
  901. '--code-text-color': '#8b4513'
  902. },
  903. desert_dark: {
  904. '--bg-color': 'rgba(74, 60, 42, var(--ui-transparency))',
  905. '--bg-color-full': 'rgba(74, 60, 42, 1)',
  906. '--bg-tool': 'rgba(74, 60, 42, 0.8)',
  907. '--text-color': '#f0e68c',
  908. '--text-color-darker': '#d2b48c',
  909. '--border-color': 'rgba(92, 64, 51, var(--ui-transparency))',
  910. '--button-bg-color': 'rgba(205, 133, 63, var(--ui-transparency))',
  911. '--active-char-color': 'rgba(244, 164, 96, var(--ui-transparency))',
  912. '--success-bg-color': 'rgba(218, 165, 32, var(--ui-transparency))',
  913. '--info-bg-color': 'rgba(210, 180, 140, var(--ui-transparency))',
  914. '--warning-bg-color': 'rgba(184, 134, 11, var(--ui-transparency))',
  915. '--muted-bg-color': 'rgba(210, 105, 30, var(--ui-transparency))',
  916. '--danger-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))',
  917. '--shadow-color': 'rgba(0, 0, 0, 0.4)',
  918. '--link-color': '#cd853f',
  919. '--code-bg-color': 'rgba(92, 64, 51, 0.8)',
  920. '--code-text-color': '#ffa07a'
  921. }
  922. };
  923.  
  924. // Define a global object to store the status data
  925. const globalStatus = {
  926. time: '',
  927. weather: '',
  928. plot: ''
  929. };
  930.  
  931. // Initialize CSS variables
  932. function setTheme(themeName) {
  933. const theme = themes[themeName];
  934. Object.keys(theme).forEach(key => {
  935. document.documentElement.style.setProperty(key, theme[key]);
  936. });
  937. }
  938.  
  939. // Retrieve saved settings or set defaults
  940. const defaultTransparency = await GM.getValue('transparency', '0.9');
  941. const defaultTheme = await GM.getValue('theme', 'dark');
  942.  
  943. // Initialize transparency and theme
  944. document.documentElement.style.setProperty('--ui-transparency', defaultTransparency);
  945. setTheme(defaultTheme);
  946.  
  947. // Add placeholder styles
  948. const style = document.createElement('style');
  949. style.innerHTML = `
  950. input::placeholder, textarea::placeholder {
  951. color: var(--text-color);
  952. }
  953. input:-ms-input-placeholder, textarea:-ms-input-placeholder {
  954. color: var(--text-color);
  955. }
  956. input::-ms-input-placeholder, textarea::-ms-input-placeholder {
  957. color: var(--text-color);
  958. }
  959. input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
  960. color: var(--text-color);
  961. }
  962. `;
  963. document.head.appendChild(style);
  964.  
  965. // --- Context Menu and Tool Buttons ---
  966. const menuButtonsContainer = document.createElement('div');
  967. menuButtonsContainer.style.cssText = `
  968. position: fixed;
  969. top: 10px;
  970. left: 50%;
  971. transform: translateX(-50%);
  972. display: flex;
  973. justify-content: center;
  974. z-index: 10000;
  975. `;
  976. document.body.appendChild(menuButtonsContainer);
  977.  
  978. const contextMenuButton = document.createElement('button');
  979. contextMenuButton.textContent = '⚙️';
  980. contextMenuButton.title = 'Menu';
  981. contextMenuButton.style.cssText = `
  982. width: 40px;
  983. height: 40px;
  984. margin-right: 5px;
  985. padding: 0;
  986. border: none;
  987. background-color: var(--button-bg-color);
  988. color: var(--text-color);
  989. border-radius: 50%;
  990. font-size: 24px;
  991. cursor: pointer;
  992. z-index: 10000;
  993. `;
  994. menuButtonsContainer.appendChild(contextMenuButton);
  995.  
  996. const toolButtonsData = [
  997. { emoji: '🧮', title: 'Calculator', iframeSrc: 'https://www.desmos.com/fourfunction', id: 'calculator' },
  998. { emoji: '🎲', title: 'Dice Roller', iframeSrc: '', id: 'dice' },
  999. { emoji: '🎮', title: 'Game', iframeSrc: 'https://freepacman.org/', id: 'game' },
  1000. ];
  1001.  
  1002. toolButtonsData.forEach(tool => {
  1003. const button = document.createElement('button');
  1004. button.textContent = tool.emoji;
  1005. button.title = tool.title;
  1006. button.style.cssText = `
  1007. width: 40px;
  1008. height: 40px;
  1009. margin-right: 5px;
  1010. padding: 0;
  1011. border: none;
  1012. background-color: var(--button-bg-color);
  1013. color: var(--text-color);
  1014. border-radius: 50%;
  1015. font-size: 24px;
  1016. cursor: pointer;
  1017. `;
  1018. button.addEventListener('click', () => {
  1019. openToolWindow(tool);
  1020. });
  1021. menuButtonsContainer.appendChild(button);
  1022. });
  1023.  
  1024. const openWindows = new Set();
  1025.  
  1026. function openToolWindow(tool) {
  1027. if (openWindows.has(tool.id)) {
  1028. return;
  1029. }
  1030.  
  1031. // Create the window container
  1032. const windowContainer = document.createElement('div');
  1033. windowContainer.style.cssText = `
  1034. position: fixed;
  1035. top: 100px;
  1036. left: 100px;
  1037. width: 500px;
  1038. height: 400px;
  1039. background-color: var(--bg-color);
  1040. border: 1px solid var(--border-color);
  1041. box-shadow: 0 0 10px var(--shadow-color);
  1042. border-radius: 5px;
  1043. z-index: 10002;
  1044. display: flex;
  1045. flex-direction: column;
  1046. `;
  1047.  
  1048. // Add a header with the title and close button
  1049. const header = document.createElement('div');
  1050. header.style.cssText = `
  1051. display: flex;
  1052. align-items: center;
  1053. justify-content: space-between;
  1054. padding: 5px;
  1055. background-color: var(--button-bg-color);
  1056. color: var(--text-color);
  1057. cursor: move;
  1058. `;
  1059. const title = document.createElement('span');
  1060. title.textContent = tool.title;
  1061. header.appendChild(title);
  1062. const closeButton = document.createElement('button');
  1063. closeButton.textContent = '✖️';
  1064. closeButton.style.cssText = `
  1065. background: none;
  1066. border: none;
  1067. color: var(--text-color);
  1068. cursor: pointer;
  1069. `;
  1070. closeButton.addEventListener('click', () => {
  1071. document.body.removeChild(windowContainer);
  1072. openWindows.delete(tool.id);
  1073. });
  1074. header.appendChild(closeButton);
  1075.  
  1076. windowContainer.appendChild(header);
  1077.  
  1078. // Add iframe
  1079. const iframe = document.createElement('iframe');
  1080. iframe.style.cssText = `
  1081. flex-grow: 1;
  1082. width: 100%;
  1083. border: none;
  1084. background-color: var(--bg-color);
  1085. color: var(--text-color);
  1086. `;
  1087. let src = tool.iframeSrc;
  1088.  
  1089. // Adjust dice URL based on theme
  1090. if (tool.id === 'dice') {
  1091. const dicehex = getComputedHexValue('--button-bg-color').slice(1);
  1092. const chromahex = getComputedHexValue('--bg-color').slice(1);
  1093. const labelhex = getComputedHexValue('--text-color').slice(1);
  1094. src = `https://dice.bee.ac/?dicehex=${dicehex}&chromahex=${chromahex}&labelhex=${labelhex}`;
  1095. }
  1096.  
  1097. // Adjust calculator URL based on theme
  1098. if (tool.id === 'calculator') {
  1099. const bgColor = getComputedHexValue('--bg-color').slice(1);
  1100. const textColor = getComputedHexValue('--text-color').slice(1);
  1101. const buttonBgColor = getComputedHexValue('--button-bg-color').slice(1);
  1102. src = `https://www.desmos.com/fourfunction?backgroundColor=${bgColor}&textColor=${textColor}&buttonBackgroundColor=${buttonBgColor}`;
  1103. }
  1104.  
  1105. iframe.src = src;
  1106. windowContainer.appendChild(iframe);
  1107.  
  1108. // Make window draggable
  1109. makeElementDraggable(windowContainer, header);
  1110.  
  1111. document.body.appendChild(windowContainer);
  1112. openWindows.add(tool.id);
  1113.  
  1114. document.body.appendChild(windowContainer);
  1115. openWindows.add(tool.id);
  1116. }
  1117.  
  1118. function getComputedHexValue(variableName) {
  1119. const computedStyle = getComputedStyle(document.documentElement);
  1120. const colorValue = computedStyle.getPropertyValue(variableName).trim();
  1121.  
  1122. // Create a temporary div to get the computed color
  1123. const tempDiv = document.createElement('div');
  1124. tempDiv.style.color = colorValue;
  1125. document.body.appendChild(tempDiv);
  1126. const computedColor = getComputedStyle(tempDiv).color;
  1127. document.body.removeChild(tempDiv);
  1128.  
  1129. // Now computedColor is in 'rgb(r, g, b)' or 'rgba(r, g, b, a)' format
  1130. const rgba = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
  1131.  
  1132. if (rgba) {
  1133. const r = parseInt(rgba[1]).toString(16).padStart(2, '0');
  1134. const g = parseInt(rgba[2]).toString(16).padStart(2, '0');
  1135. const b = parseInt(rgba[3]).toString(16).padStart(2, '0');
  1136. return `#${r}${g}${b}`;
  1137. } else {
  1138. // Fallback to default
  1139. return '#FFFFFF';
  1140. }
  1141. }
  1142.  
  1143. function makeElementDraggable(elmnt, handle) {
  1144. let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  1145. if (handle) {
  1146. handle.onmousedown = dragMouseDown;
  1147. } else {
  1148. elmnt.onmousedown = dragMouseDown;
  1149. }
  1150.  
  1151. function dragMouseDown(e) {
  1152. e = e || window.event;
  1153. e.preventDefault();
  1154. pos3 = e.clientX;
  1155. pos4 = e.clientY;
  1156. document.onmouseup = closeDragElement;
  1157. document.onmousemove = elementDrag;
  1158. }
  1159.  
  1160. function elementDrag(e) {
  1161. e = e || window.event;
  1162. e.preventDefault();
  1163. pos1 = pos3 - e.clientX;
  1164. pos2 = pos4 - e.clientY;
  1165. pos3 = e.clientX;
  1166. pos4 = e.clientY;
  1167. elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
  1168. elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  1169. }
  1170.  
  1171. function closeDragElement() {
  1172. document.onmouseup = null;
  1173. document.onmousemove = null;
  1174. }
  1175. }
  1176.  
  1177. // Helper functions for tooltip
  1178. let tooltipElement = null;
  1179.  
  1180. function showTooltip(event, content, set, url, imageMaxWidth = 580, imageMaxHeight = 380) {
  1181. hideTooltip(); // Remove existing tooltip if any
  1182.  
  1183. // Create new tooltip element
  1184. tooltipElement = document.createElement('div');
  1185. tooltipElement.style.cssText = `
  1186. position: absolute;
  1187. z-index: 10000;
  1188. background-color: var(--bg-tool);
  1189. color: var(--text-color);
  1190. border: 1px solid var(--border-color);
  1191. padding: 5px;
  1192. border-radius: 5px;
  1193. font-size: 12px;
  1194. max-width: 600px;
  1195. white-space: pre-wrap;
  1196. box-shadow: 0 0 10px var(--shadow-color);
  1197. `;
  1198. tooltipElement.innerHTML = content;
  1199.  
  1200. // Append tooltip to body first to calculate its size later
  1201. document.body.appendChild(tooltipElement);
  1202.  
  1203. // Get the parent node's bounding rectangle
  1204. const parentRect = event.currentTarget.parentNode.parentNode.getBoundingClientRect();
  1205.  
  1206. if (!set) {
  1207. // Tooltip to the right of the parent element
  1208. tooltipElement.style.left = `${parentRect.right + 4}px`;
  1209. tooltipElement.style.top = `${parentRect.top}px`;
  1210. } else {
  1211. // Tooltip to the left of the parent element
  1212. const tooltipWidth = tooltipElement.offsetWidth; // Get the tooltip width after it's appended
  1213. tooltipElement.style.left = `${parentRect.left - tooltipWidth + 15}px`; // Adjust to the left
  1214. tooltipElement.style.top = `${parentRect.top}px`;
  1215. }
  1216.  
  1217. // Adjust position if the tooltip goes off screen
  1218. const tooltipRect = tooltipElement.getBoundingClientRect();
  1219. if (tooltipRect.right > window.innerWidth) {
  1220. tooltipElement.style.left = `${window.innerWidth - tooltipRect.width - 10}px`;
  1221. }
  1222. if (tooltipRect.left < 0) {
  1223. tooltipElement.style.left = `10px`; // Move it a bit to the right if it goes off screen on the left
  1224. }
  1225. if (tooltipRect.bottom > window.innerHeight) {
  1226. tooltipElement.style.top = `${window.innerHeight - tooltipRect.height - 10}px`;
  1227. }
  1228.  
  1229. // If a valid image URL is provided, attempt to load it
  1230. if (url && isValidImageUrl(url)) {
  1231. const img = document.createElement('img');
  1232. img.src = url;
  1233.  
  1234. // When the image loads, call a function to add it to the tooltip
  1235. img.onload = () => {
  1236. addImageToTooltip(tooltipElement, url, imageMaxWidth, imageMaxHeight);
  1237. };
  1238.  
  1239. // Optionally handle image loading errors
  1240. img.onerror = () => {
  1241. console.warn(`Failed to load image from URL: ${url}`);
  1242. };
  1243. }
  1244. }
  1245.  
  1246. function addImageToTooltip(tooltipElement, url, imageMaxWidth, imageMaxHeight) {
  1247. // Create a container for the image with a background color
  1248. const imgContainer = document.createElement('div');
  1249. imgContainer.style.cssText = `
  1250. display: block;
  1251. margin-top: 10px;
  1252. width: 100%; /* Match the tooltip's width */
  1253. background-color: var(--shadow-color);
  1254. border-radius: 5px;
  1255. overflow: hidden; /* Ensures the image stays within the container's bounds */
  1256. padding: 5px; /* Optional: adds padding inside the background */
  1257. `;
  1258.  
  1259. // Create the image element
  1260. const img = document.createElement('img');
  1261. img.src = url;
  1262. img.style.cssText = `
  1263. display: block; /* Behave like a block element */
  1264. margin: 0 auto; /* Center the image horizontally */
  1265. max-width: ${imageMaxWidth}px; /* Scale down if larger than max width */
  1266. max-height: ${imageMaxHeight}px; /* Scale down if larger than max height */
  1267. width: auto; /* Maintain aspect ratio based on max-width and max-height */
  1268. height: auto; /* Maintain aspect ratio */
  1269. border-radius: 5px; /* Optional: rounded corners for the image */
  1270. `;
  1271.  
  1272. // Append the image to the container
  1273. imgContainer.appendChild(img);
  1274.  
  1275. // Append the container to the tooltip
  1276. tooltipElement.appendChild(imgContainer);
  1277.  
  1278. // Recalculate the tooltip position if necessary (e.g., if the image changes its size)
  1279. const tooltipRect = tooltipElement.getBoundingClientRect();
  1280.  
  1281. if (tooltipRect.right > window.innerWidth) {
  1282. tooltipElement.style.left = `${window.innerWidth - tooltipRect.width - 10}px`;
  1283. }
  1284. if (tooltipRect.left < 0) {
  1285. tooltipElement.style.left = `10px`; // Move it a bit to the right if it goes off screen on the left
  1286. }
  1287. if (tooltipRect.bottom > window.innerHeight) {
  1288. tooltipElement.style.top = `${window.innerHeight - tooltipRect.height - 10}px`;
  1289. }
  1290. }
  1291.  
  1292. function hideTooltip() {
  1293. if (tooltipElement && tooltipElement.parentNode) {
  1294. tooltipElement.parentNode.removeChild(tooltipElement);
  1295. tooltipElement = null; // Set to null after removing
  1296. }
  1297. }
  1298.  
  1299. // --- Context Panel ---
  1300. const contextPanel = document.createElement('div');
  1301. contextPanel.id = 'context-panel';
  1302. contextPanel.style.cssText = `
  1303. position: fixed; /* Keep position fixed to center the panel */
  1304. top: 50%;
  1305. left: 50%;
  1306. transform: translate(-50%, -50%);
  1307. width: 400px;
  1308. padding: 20px;
  1309. background-color: var(--bg-color);
  1310. border: 1px solid var(--border-color);
  1311. box-shadow: 0 0 10px var(--shadow-color);
  1312. border-radius: 5px;
  1313. display: none;
  1314. z-index: 10001;
  1315. /* Remove position: relative; */
  1316. `;
  1317. document.body.appendChild(contextPanel);
  1318.  
  1319. // Add close button to contextPanel
  1320. const contextCloseButton = document.createElement('button');
  1321. contextCloseButton.textContent = '✖️';
  1322. contextCloseButton.style.cssText = `
  1323. position: absolute;
  1324. top: 10px;
  1325. right: 10px;
  1326. background: none;
  1327. border: none;
  1328. font-size: 20px;
  1329. cursor: pointer;
  1330. color: var(--text-color);
  1331. `;
  1332. contextCloseButton.addEventListener('click', () => {
  1333. contextPanel.style.display = 'none';
  1334. });
  1335. contextPanel.appendChild(contextCloseButton);
  1336.  
  1337. const transparencyLabel = document.createElement('label');
  1338. transparencyLabel.textContent = 'UI Transparency';
  1339. transparencyLabel.style.cssText = `
  1340. color: var(--text-color);
  1341. display: block;
  1342. margin-bottom: 5px;
  1343. `;
  1344. contextPanel.appendChild(transparencyLabel);
  1345.  
  1346. const transparencySlider = document.createElement('input');
  1347. transparencySlider.type = 'range';
  1348. transparencySlider.min = '0.1';
  1349. transparencySlider.max = '1';
  1350. transparencySlider.step = '0.1';
  1351. transparencySlider.value = defaultTransparency;
  1352. transparencySlider.style.cssText = `
  1353. width: 100%;
  1354. margin-bottom: 10px;
  1355. `;
  1356. transparencySlider.addEventListener('input', () => {
  1357. document.documentElement.style.setProperty('--ui-transparency', transparencySlider.value);
  1358. // Re-apply current theme to update transparency
  1359. setTheme(themeDropdown.value);
  1360. GM.setValue('transparency', transparencySlider.value);
  1361. });
  1362. contextPanel.appendChild(transparencySlider);
  1363.  
  1364. const dropperLabel = document.createElement('label');
  1365. dropperLabel.textContent = 'UI Theme';
  1366. dropperLabel.style.cssText = `
  1367. color: var(--text-color);
  1368. display: block;
  1369. margin-bottom: 5px;
  1370. `;
  1371. contextPanel.appendChild(dropperLabel);
  1372.  
  1373. const themeDropdown = document.createElement('select');
  1374. themeDropdown.style.cssText = `
  1375. width: 100%;
  1376. padding: 5px;
  1377. margin-bottom: 10px;
  1378. border: 1px solid var(--border-color);
  1379. border-radius: 5px;
  1380. background-color: var(--bg-color);
  1381. color: var(--text-color);
  1382. `;
  1383. Object.keys(themes).forEach(theme => {
  1384. const option = document.createElement('option');
  1385. option.value = theme;
  1386. option.textContent = theme.charAt(0).toUpperCase() + theme.slice(1);
  1387. themeDropdown.appendChild(option);
  1388. });
  1389.  
  1390. themeDropdown.value = defaultTheme;
  1391.  
  1392. themeDropdown.addEventListener('change', () => {
  1393. setTheme(themeDropdown.value);
  1394. GM.setValue('theme', themeDropdown.value);
  1395. });
  1396. contextPanel.appendChild(themeDropdown);
  1397.  
  1398. // Add divider
  1399. const divider = document.createElement('hr');
  1400. divider.style.cssText = `
  1401. border: none;
  1402. border-top: 1px solid var(--border-color);
  1403. margin: 10px 0;
  1404. `;
  1405. contextPanel.appendChild(divider);
  1406.  
  1407. const contextLabel = document.createElement('label');
  1408. contextLabel.textContent = 'Primary Context';
  1409. contextLabel.style.cssText = `
  1410. color: var(--text-color);
  1411. display: block;
  1412. margin-bottom: 5px;
  1413. `;
  1414. contextPanel.appendChild(contextLabel);
  1415.  
  1416. const primaryContextInput = document.createElement('textarea');
  1417. primaryContextInput.placeholder = 'Primary context goes here...';
  1418. primaryContextInput.style.cssText = `
  1419. width: 100%;
  1420. height: 100px;
  1421. margin-bottom: 10px;
  1422. padding: 10px;
  1423. border: 1px solid var(--border-color);
  1424. border-radius: 5px;
  1425. box-sizing: border-box;
  1426. background-color: var(--bg-color);
  1427. color: var(--text-color);
  1428. `;
  1429. contextPanel.appendChild(primaryContextInput);
  1430.  
  1431. // Define the sleep function
  1432. function sleep(ms) {
  1433. return new Promise(resolve => setTimeout(resolve, ms));
  1434. }
  1435.  
  1436. const updateContextButton = document.createElement('button');
  1437. updateContextButton.textContent = 'Update Context';
  1438. updateContextButton.style.cssText = `
  1439. width: 100%;
  1440. padding: 10px;
  1441. border: none;
  1442. background-color: var(--success-bg-color);
  1443. color: var(--text-color);
  1444. border-radius: 5px;
  1445. cursor: pointer;
  1446. `;
  1447.  
  1448. updateContextButton.addEventListener('click', async () => {
  1449.  
  1450. const textarea = document.createElement('textarea');
  1451. var fullContext = `Context;\n"` + (primaryContextInput.value == undefined ? 'Nothing in particular' : primaryContextInput.value) + `"\n`;
  1452. fullContext += `\n` + window.getContext();
  1453. textarea.value = fullContext;
  1454. console.log("setting context; " + textarea.value);
  1455. document.body.appendChild(textarea);
  1456. textarea.select();
  1457. document.execCommand('copy');
  1458. document.body.removeChild(textarea);
  1459.  
  1460. // Display a dialog box
  1461. alert("Context copied\nPlease paste it into the chat memory as automated insertion is broken.");
  1462.  
  1463. /*const primaryContext = primaryContextInput.value;
  1464. var fullContext = `Context;\n"` + primaryContext + `"\n`;
  1465. fullContext += `\n` + window.getContext();
  1466. console.log("setting context; " + fullContext);
  1467.  
  1468. window.manuClick('//*[@id="menu-button-:rb:"]');
  1469. await sleep(250);
  1470.  
  1471. window.manuClick('//*[@id="menu-list-:rb:-menuitem-:ru:"]');
  1472. await sleep(250);
  1473.  
  1474. window.manuWrite("/html/body/div[10]/div[3]/div/section/div/textarea", fullContext);
  1475. await sleep(250);
  1476.  
  1477. window.manuClick("/html/body/div[9]/div[3]/div/section/footer/button[2]");*/
  1478. });
  1479.  
  1480. window.manuWrite = function(xpath, text) {
  1481. var result = document.evaluate(
  1482. xpath,
  1483. document,
  1484. null,
  1485. XPathResult.FIRST_ORDERED_NODE_TYPE,
  1486. null
  1487. );
  1488.  
  1489. var node = result.singleNodeValue;
  1490.  
  1491. if (node) {
  1492. node.focus();
  1493.  
  1494. // Since the node is an HTMLTextAreaElement, get the value setter from its prototype
  1495. const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(
  1496. window.HTMLTextAreaElement.prototype,
  1497. 'value'
  1498. ).set;
  1499.  
  1500. nativeTextAreaValueSetter.call(node, text);
  1501.  
  1502. // Dispatch the 'input' event to simulate user input
  1503. var event = new Event('input', { bubbles: true });
  1504. node.dispatchEvent(event);
  1505.  
  1506. } else {
  1507. console.error("Node not found for XPath:", xpath);
  1508. }
  1509. }
  1510.  
  1511. window.manuClick = function(xpath) {
  1512. var result = document.evaluate(
  1513. xpath,
  1514. document,
  1515. null,
  1516. XPathResult.FIRST_ORDERED_NODE_TYPE,
  1517. null
  1518. );
  1519.  
  1520. var node = result.singleNodeValue;
  1521.  
  1522. if (node) {
  1523. node.click();
  1524. }
  1525. }
  1526.  
  1527. contextPanel.appendChild(updateContextButton);
  1528.  
  1529. contextMenuButton.addEventListener('click', () => {
  1530. contextPanel.style.display = contextPanel.style.display === 'none' ? 'block' : 'none';
  1531. });
  1532.  
  1533. // Helper function to find the active location recursively
  1534. function findActiveLocation(items) {
  1535. for (const item of items) {
  1536. if (item.active) {
  1537. return item;
  1538. }
  1539. if (item.children && item.children.length > 0) {
  1540. const activeChild = findActiveLocation(item.children);
  1541. if (activeChild) {
  1542. return activeChild;
  1543. }
  1544. }
  1545. }
  1546. return null;
  1547. }
  1548.  
  1549. // Helper function to collect all active characters recursively
  1550. function collectActiveCharacters(items, result = []) {
  1551. for (const item of items) {
  1552. if (item.active) {
  1553. result.push(item);
  1554. }
  1555. if (item.children && item.children.length > 0) {
  1556. collectActiveCharacters(item.children, result);
  1557. }
  1558. }
  1559. return result;
  1560. }
  1561.  
  1562. // Modify the getContext function to utilize the helper functions
  1563. window.getContext = function() {
  1564. const activeLocation = findActiveLocation(locationGroups.flatMap(g => g.items));
  1565. if (!activeLocation) return '';
  1566.  
  1567. let context = ``;
  1568.  
  1569. if (globalStatus.plot && globalStatus.plot.trim() !== '') {
  1570. context += `Plot:\n"${globalStatus.plot}"\n`;
  1571. } else {
  1572. context += `Plot:\n"No specific plot."\n`;
  1573. }
  1574.  
  1575. context += `\nSetting;\n"${activeLocation.name}" (${globalStatus.time}, ${globalStatus.weather}):\n`
  1576. context += `\u0009"${activeLocation.description}"\n`;
  1577.  
  1578. context += `\n{{user}}'s characters;\n`;
  1579.  
  1580. const activeUserCharacters = collectActiveCharacters(characterGroups.flatMap(g => g.items)).filter(char => char.characterType == 1);
  1581.  
  1582. activeUserCharacters.forEach(char => {
  1583. context += `"${char.name}" (${char.sex}, ${char.species}, ${char.age}, ${char.bodyType}, ${char.personality}):\n`;
  1584. context += `\u0009"${char.bio}"\n`;
  1585. });
  1586.  
  1587. context += `\n{{char}}'s characters;\n`;
  1588.  
  1589. const activeCharCharacters = collectActiveCharacters(characterGroups.flatMap(g => g.items)).filter(char => char.characterType == 0);
  1590.  
  1591. activeCharCharacters.forEach(char => {
  1592. context += `"${char.name}" (${char.sex}, ${char.species}, ${char.age}, ${char.bodyType}, ${char.personality}):\n`;
  1593. context += `\u0009"${char.bio}"\n`;
  1594. });
  1595.  
  1596. return context;
  1597. };
  1598.  
  1599. // --- Character Sidebar with Groups and Nested Characters ---
  1600. const characterSidebar = document.createElement('div');
  1601. characterSidebar.id = 'character-sheet-sidebar';
  1602. characterSidebar.style.cssText = `
  1603. position: fixed;
  1604. top: 0;
  1605. right: -350px;
  1606. width: 350px;
  1607. height: 100%;
  1608. display: flex;
  1609. flex-direction: column;
  1610. background-color: var(--bg-color);
  1611. border-left: 2px solid var(--border-color);
  1612. box-shadow: -2px 0 5px var(--shadow-color);
  1613. box-sizing: border-box;
  1614. transition: right 0.3s;
  1615. z-index: 9999;
  1616. `;
  1617. document.body.appendChild(characterSidebar);
  1618.  
  1619. const characterToggleButton = document.createElement('button');
  1620. characterToggleButton.textContent = '☰';
  1621. characterToggleButton.style.cssText = `
  1622. position: fixed;
  1623. top: 10px;
  1624. right: 10px;
  1625. padding: 5px 10px;
  1626. border: none;
  1627. background-color: var(--button-bg-color);
  1628. color: var(--text-color);
  1629. border-radius: 5px;
  1630. cursor: pointer;
  1631. transition: right 0.3s;
  1632. z-index: 10000;
  1633. `;
  1634. characterToggleButton.addEventListener('click', () => {
  1635. const isOpen = characterSidebar.style.right === '0px';
  1636. characterSidebar.style.right = isOpen ? '-350px' : '0';
  1637. characterToggleButton.style.right = isOpen ? '10px' : '360px';
  1638. });
  1639. document.body.appendChild(characterToggleButton);
  1640.  
  1641. const characterHeader = document.createElement('div');
  1642. characterHeader.style.cssText = `
  1643. padding: 10px;
  1644. background-color: var(--border-color);
  1645. text-align: center;
  1646. font-weight: bold;
  1647. color: var(--text-color);
  1648. `;
  1649. characterHeader.textContent = 'Characters';
  1650. characterSidebar.appendChild(characterHeader);
  1651.  
  1652. // Add fifth button: "Load" for Character
  1653. function loadCharacter(index = -1) {
  1654. const fileInput = document.createElement('input');
  1655. fileInput.type = 'file';
  1656. fileInput.accept = '.json';
  1657. fileInput.addEventListener('change', async (event) => {
  1658. const file = event.target.files[0];
  1659. const reader = new FileReader();
  1660. reader.onload = (e) => {
  1661. try {
  1662. const character = JSON.parse(e.target.result);
  1663. if (character && character.id) {
  1664. // Add to Ungrouped
  1665. const ungrouped = characterGroups.find(g => g.name === 'Ungrouped');
  1666. if (index < 0 && ungrouped) {
  1667. ungrouped.items.push(character);
  1668. } else if (index >= characterGroups.length && ungrouped) {
  1669. ungrouped.items.push(character);
  1670. } else {
  1671. characterGroups[index].items.push(character);
  1672. }
  1673. renderCharacterGroups();
  1674. } else {
  1675. alert('Invalid character format.');
  1676. }
  1677. } catch (error) {
  1678. alert('Failed to load character: ' + error.message);
  1679. }
  1680. };
  1681. reader.readAsText(file);
  1682. });
  1683. fileInput.click();
  1684. }
  1685.  
  1686. const characterGroupList = document.createElement('div');
  1687. characterGroupList.id = 'character-group-list';
  1688. characterGroupList.style.cssText = `
  1689. flex-grow: 1;
  1690. overflow-y: auto;
  1691. padding: 10px;
  1692. `;
  1693. characterSidebar.appendChild(characterGroupList);
  1694.  
  1695. // Initialize Character Groups
  1696. let characterGroups = [
  1697. { name: 'Ungrouped', items: [], collapsed: false }
  1698. ];
  1699.  
  1700. // Function to create a new Character
  1701. function createNewCharacter(index = -1) {
  1702. const newCharacter = {
  1703. id: generateId(),
  1704. active: true,
  1705. emoji: '😀',
  1706. avatar: 'Url goes here...',
  1707. name: 'New Character',
  1708. sex: '',
  1709. species: '',
  1710. age: '',
  1711. bodyType: '',
  1712. personality: '',
  1713. bio: '',
  1714. characterType: 0,
  1715. children: [],
  1716. collapsed: false
  1717. };
  1718. // Add to Ungrouped
  1719. const ungrouped = characterGroups.find(g => g.name === 'Ungrouped');
  1720. if (index < 0 && ungrouped) {
  1721. ungrouped.items.push(newCharacter);
  1722. } else if (index >= characterGroups.length && ungrouped) {
  1723. ungrouped.items.push(newCharacter);
  1724. } else {
  1725. characterGroups[index].items.push(newCharacter);
  1726. }
  1727. renderCharacterGroups();
  1728. }
  1729.  
  1730. // Function to create a new Character Group
  1731. function createNewCharacterGroup() {
  1732. const groupName = prompt('Enter group name:', `Group ${characterGroups.length}`);
  1733. if (groupName && groupName.trim() !== '') {
  1734. characterGroups.push({ name: groupName.trim(), items: [], collapsed: false });
  1735. renderCharacterGroups();
  1736. }
  1737. }
  1738.  
  1739. // Function to render Character Groups and Items
  1740. function renderCharacterGroups() {
  1741. characterGroupList.innerHTML = '';
  1742.  
  1743. characterGroups.forEach((group, groupIndex) => {
  1744. // Sort items alphabetically by name
  1745. group.items.sort((a, b) => a.name.localeCompare(b.name));
  1746.  
  1747. const groupContainer = document.createElement('div');
  1748. groupContainer.className = 'character-group';
  1749. groupContainer.style.cssText = `
  1750. margin-bottom: 10px;
  1751. border: 1px solid var(--border-color);
  1752. border-radius: 5px;
  1753. background-color: var(--active-char-color);
  1754. `;
  1755.  
  1756. const groupHeader = document.createElement('div');
  1757. groupHeader.style.cssText = `
  1758. display: flex;
  1759. justify-content: space-between;
  1760. align-items: center;
  1761. padding: 5px 10px;
  1762. background-color: var(--button-bg-color);
  1763. color: var(--text-color);
  1764. cursor: pointer;
  1765. user-select: none;
  1766. position: relative;
  1767. `;
  1768. groupHeader.textContent = group.name;
  1769. groupHeader.addEventListener('click', () => {
  1770. group.collapsed = !group.collapsed;
  1771. renderCharacterGroups();
  1772. });
  1773.  
  1774. // Add context menu functionality
  1775. groupHeader.addEventListener('contextmenu', (e) => {
  1776. e.preventDefault();
  1777. CreateContextMenu(
  1778. 'var(--bg-tool)',
  1779. 'var(--text-color)',
  1780. 'var(--border-color)',
  1781. ['✏️ Rename','📝 Create', '🗃️ Import', '🗑️ Delete'],
  1782. [
  1783. () => renameGroup(groupIndex),
  1784. () => createNewCharacter(groupIndex),
  1785. () => loadCharacter(groupIndex),
  1786. () => deleteGroup(groupIndex),
  1787. ]
  1788. );
  1789. showMenu(e);
  1790. });
  1791.  
  1792. groupContainer.appendChild(groupHeader);
  1793.  
  1794. if (!group.collapsed) {
  1795. const itemsContainer = document.createElement('div');
  1796. itemsContainer.style.cssText = `
  1797. padding: 5px 10px;
  1798. display: block;
  1799. `;
  1800. if (group.items.length === 0) {
  1801. const emptyInfo = document.createElement('div');
  1802. emptyInfo.className = 'character-slot';
  1803. emptyInfo.style.cssText = `
  1804. display: flex;
  1805. align-items: center;
  1806. padding: 10px;
  1807. border: 1px solid var(--border-color);
  1808. border-radius: 5px;
  1809. height: 20px;
  1810. margin-bottom: 5px;
  1811. background-color: var(--bg-color);
  1812. cursor: default;
  1813. `;
  1814.  
  1815. const infoText = document.createElement('span');
  1816. infoText.textContent = `Group is empty`;
  1817. infoText.style.cssText = `
  1818. color: var(--text-color);
  1819. `;
  1820.  
  1821. emptyInfo.appendChild(infoText);
  1822. itemsContainer.appendChild(emptyInfo);
  1823. } else {
  1824. group.items.forEach((character) => {
  1825. const characterSlot = createCharacterSlot(character, groupIndex);
  1826. itemsContainer.appendChild(characterSlot);
  1827. });
  1828. }
  1829. groupContainer.appendChild(itemsContainer);
  1830. } else {
  1831. const collapsedInfo = document.createElement('div');
  1832. collapsedInfo.className = 'character-slot';
  1833. collapsedInfo.style.cssText = `
  1834. display: flex;
  1835. align-items: center;
  1836. padding: 10px;
  1837. margin-bottom: 10px;
  1838. border: 1px solid var(--border-color);
  1839. border-radius: 5px;
  1840. height: 20px;
  1841. margin-top: 5px;
  1842. background-color: var(--bg-color);
  1843. cursor: default;
  1844. margin-left: 10px;
  1845. margin-right: 10px;
  1846. `;
  1847. const infoText = document.createElement('span');
  1848. infoText.textContent = `${group.items.length} items hidden`;
  1849.  
  1850. const activeCount = countActiveCharacters(group.items);
  1851. const totalCount = countTotalCharacters(group.items);
  1852. if (activeCount === totalCount && totalCount > 0) {
  1853. infoText.style.cssText = `color: rgba(36, 242, 0, 0.75);`;
  1854. } else if (activeCount === 0) {
  1855. infoText.style.cssText = `color: var(--text-color-darker);`;
  1856. } else {
  1857. infoText.style.cssText = `color: rgba(242, 198, 0, 0.75);`;
  1858. }
  1859.  
  1860. collapsedInfo.appendChild(infoText);
  1861. groupContainer.appendChild(collapsedInfo);
  1862. }
  1863.  
  1864. const groupActions = document.createElement('div');
  1865. groupActions.style.cssText = `
  1866. display: flex;
  1867. align-items: center;
  1868. `;
  1869.  
  1870. // Activate/Deactivate All Button
  1871. const toggleAllButton = document.createElement('button');
  1872. toggleAllButton.title = 'Activate/Deactivate All';
  1873. toggleAllButton.style.cssText = `
  1874. background: none;
  1875. border: none;
  1876. color: var(--text-color);
  1877. cursor: pointer;
  1878. margin-right: 3px;
  1879. font-size: 16px;
  1880. `;
  1881. updateToggleAllButton(toggleAllButton, group);
  1882. toggleAllButton.addEventListener('click', (e) => {
  1883. e.stopPropagation();
  1884. toggleAllGroupCharacters(group);
  1885. updateToggleAllButton(toggleAllButton, group);
  1886. renderCharacterGroups();
  1887. });
  1888. groupActions.appendChild(toggleAllButton);
  1889. groupHeader.appendChild(groupActions);
  1890.  
  1891. // Add dragover and drop events for grouping
  1892. groupContainer.addEventListener('dragover', (e) => {
  1893. e.preventDefault();
  1894. groupContainer.style.border = `2px dashed var(--info-bg-color)`;
  1895. });
  1896.  
  1897. groupContainer.addEventListener('dragleave', (e) => {
  1898. groupContainer.style.border = `1px solid var(--border-color)`;
  1899. });
  1900.  
  1901. groupContainer.addEventListener('drop', (e) => {
  1902. e.preventDefault();
  1903. groupContainer.style.border = `1px solid var(--border-color)`;
  1904. const data = e.dataTransfer.getData('text/plain');
  1905. const { type, id } = JSON.parse(data);
  1906. if (type === 'character') {
  1907. moveCharacterToGroup(id, groupIndex);
  1908. }
  1909. });
  1910.  
  1911. characterGroupList.appendChild(groupContainer);
  1912. });
  1913.  
  1914. // Ensure at least one group exists
  1915. if (characterGroups.length === 0) {
  1916. characterGroups.push({ name: 'Ungrouped', items: [], collapsed: false });
  1917. renderCharacterGroups();
  1918. }
  1919. }
  1920.  
  1921. // Function to update the Toggle All Button appearance
  1922. function updateToggleAllButton(button, group) {
  1923. const activeCount = countActiveCharacters(group.items);
  1924. const totalCount = countTotalCharacters(group.items);
  1925. if (activeCount === totalCount && totalCount > 0) {
  1926. // All active
  1927. button.textContent = '🟢';
  1928. } else if (activeCount === 0) {
  1929. // All inactive
  1930. button.textContent = '🔴';
  1931. } else {
  1932. // Mixed
  1933. button.textContent = '🟡';
  1934. }
  1935. }
  1936.  
  1937. function countActiveCharacters(items) {
  1938. let count = 0;
  1939. items.forEach(item => {
  1940. if (item.active) count++;
  1941. if (item.children && item.children.length > 0) {
  1942. count += countActiveCharacters(item.children);
  1943. }
  1944. });
  1945. return count;
  1946. }
  1947.  
  1948. function countTotalCharacters(items) {
  1949. let count = items.length;
  1950. items.forEach(item => {
  1951. if (item.children && item.children.length > 0) {
  1952. count += countTotalCharacters(item.children);
  1953. }
  1954. });
  1955. return count;
  1956. }
  1957.  
  1958. // Function to toggle all characters in a group
  1959. function toggleAllGroupCharacters(group) {
  1960. const allActive = countActiveCharacters(group.items) === countTotalCharacters(group.items);
  1961. toggleCharacters(group.items, !allActive);
  1962. }
  1963.  
  1964. function toggleCharacters(items, state) {
  1965. items.forEach(item => {
  1966. item.active = state;
  1967. if (item.children && item.children.length > 0) {
  1968. toggleCharacters(item.children, state);
  1969. }
  1970. });
  1971. }
  1972.  
  1973. // Function to generate tooltip content for a character
  1974. function getCharacterTooltipContent(character, isRadial = false) {
  1975. if (isRadial && character.emoji === '📁') {
  1976. // If it's a group node in the radial tree, only show the name
  1977. return `<strong>"${character.name}"</strong>`;
  1978. }
  1979. var content = `<strong>"${character.name}"</strong>`;
  1980.  
  1981. if(character.characterType != 2){
  1982. content += ` <small>(${character.sex || 'Unknown'}, ${character.species || 'Unknown'}, ${character.age || 'Unknown'}, ${character.bodyType || 'Unknown'}, ${character.personality || 'Unknown'})</small>`;
  1983. }
  1984.  
  1985. content += `:<br><em>"${character.bio || 'No bio available.'}"</em>`;
  1986. return content;
  1987. }
  1988.  
  1989. // Function to create a Character Slot DOM element (recursive)
  1990. function createCharacterSlot(character, groupIndex, parent = null, level = 0) {
  1991. const characterSlot = document.createElement('div');
  1992. characterSlot.className = 'character-slot';
  1993. characterSlot.draggable = true;
  1994. characterSlot.dataset.id = character.id;
  1995. characterSlot.style.cssText = `
  1996. display: flex;
  1997. align-items: center;
  1998. justify-content: space-between;
  1999. padding: 5px;
  2000. margin-bottom: 5px;
  2001. border: 1px solid var(--border-color);
  2002. border-radius: 5px;
  2003. background-color: ${character.characterType == 1 ? 'var(--active-char-color)' : 'var(--bg-color)'};
  2004. cursor: grab;
  2005. margin-left: ${level * 20}px;
  2006. `;
  2007.  
  2008. // Drag events
  2009. characterSlot.addEventListener('dragstart', (e) => {
  2010. e.stopPropagation();
  2011. e.dataTransfer.setData('text/plain', JSON.stringify({ type: 'character', id: character.id }));
  2012. e.currentTarget.style.opacity = '0.5';
  2013. });
  2014.  
  2015. characterSlot.addEventListener('dragend', (e) => {
  2016. e.currentTarget.style.opacity = '1';
  2017. });
  2018.  
  2019. characterSlot.addEventListener('dragover', (e) => {
  2020. e.preventDefault();
  2021. e.stopPropagation();
  2022. characterSlot.style.border = `2px dashed var(--info-bg-color)`;
  2023. });
  2024.  
  2025. characterSlot.addEventListener('dragleave', (e) => {
  2026. characterSlot.style.border = `1px solid var(--border-color)`;
  2027. });
  2028.  
  2029. characterSlot.addEventListener('drop', (e) => {
  2030. e.preventDefault();
  2031. e.stopPropagation();
  2032. characterSlot.style.border = `1px solid var(--border-color)`;
  2033. const data = e.dataTransfer.getData('text/plain');
  2034. const { type, id } = JSON.parse(data);
  2035. if (type === 'character' && id !== character.id) {
  2036. moveCharacterToParent(id, character);
  2037. }
  2038. });
  2039.  
  2040. // Context menu (right-click)
  2041. characterSlot.addEventListener('contextmenu', (e) => {
  2042. e.preventDefault();
  2043. CreateContextMenu(
  2044. 'var(--bg-tool)',
  2045. 'var(--text-color)',
  2046. 'var(--border-color)',
  2047. ['ℹ️ Status', '✏️ Edit', '🗑️ Delete', '⬇️ Export'],
  2048. [
  2049. () => console.log('Status clicked'), // Replace with the correct status function
  2050. () => editCharacter(character),
  2051. () => deleteCharacter(character.id),
  2052. () => exportCharacter(character),
  2053. ]
  2054. );
  2055. showMenu(e);
  2056. });
  2057.  
  2058. // Left Div (Emoji, Name)
  2059. const leftDiv = document.createElement('div');
  2060. leftDiv.style.cssText = `
  2061. display: flex;
  2062. align-items: center;
  2063. flex-grow: 1;
  2064. overflow: hidden;
  2065. `;
  2066.  
  2067. const emojiSpan = document.createElement('span');
  2068. emojiSpan.textContent = character.emoji;
  2069. emojiSpan.style.marginRight = '5px';
  2070. leftDiv.appendChild(emojiSpan);
  2071.  
  2072. const nameSpan = document.createElement('span');
  2073. nameSpan.textContent = character.name;
  2074. nameSpan.style.cssText = `
  2075. flex-grow: 1;
  2076. min-width: 0;
  2077. overflow: hidden;
  2078. white-space: nowrap;
  2079. text-overflow: ellipsis;
  2080. color: var(--text-color);
  2081. `;
  2082. leftDiv.appendChild(nameSpan);
  2083.  
  2084. // Remove Collapse/Expand Button
  2085. // (Not needed as per new requirement)
  2086. const activeButton = document.createElement('button');
  2087. activeButton.textContent = character.active ? '🟢' : '🔴';
  2088. activeButton.title = 'Toggle Active';
  2089. activeButton.style.cssText = `
  2090. padding: 3px;
  2091. border: none;
  2092. background: none;
  2093. cursor: pointer;
  2094. color: var(--text-color);
  2095. margin-right: 5px;
  2096. `;
  2097. activeButton.addEventListener('click', (e) => {
  2098. e.stopPropagation();
  2099. character.active = !character.active;
  2100. updateAllToggleButtons();
  2101. renderCharacterGroups();
  2102. });
  2103. leftDiv.appendChild(activeButton);
  2104.  
  2105. if(character.characterType == 2) {
  2106. activeButton.style.display = 'none';
  2107. }
  2108.  
  2109. characterSlot.appendChild(leftDiv);
  2110.  
  2111. // Recursive rendering of children
  2112. const container = document.createElement('div');
  2113. container.appendChild(characterSlot);
  2114.  
  2115. if (character.children && character.children.length > 0) {
  2116. if (!character.collapsed) {
  2117. character.children.forEach((child) => {
  2118. const childSlot = createCharacterSlot(child, groupIndex, character, level + 1);
  2119. container.appendChild(childSlot);
  2120. });
  2121. } else {
  2122. const collapsedInfo = document.createElement('div');
  2123. collapsedInfo.className = 'character-slot';
  2124. collapsedInfo.style.cssText = `
  2125. display: flex;
  2126. align-items: center;
  2127. padding: 10px;
  2128. margin-bottom: 5px;
  2129. border: 1px solid var(--border-color);
  2130. border-radius: 5px;
  2131. background-color: var(--bg-color);
  2132. height: 20px;
  2133. cursor: default;
  2134. margin-left: ${(level + 1) * 20}px;
  2135. `;
  2136. const infoText = document.createElement('span');
  2137. infoText.textContent = `${character.children.length} items hidden`;
  2138.  
  2139. const activeCount = countActiveCharacters(character.children);
  2140. const totalCount = countTotalCharacters(character.children);
  2141. if (activeCount === totalCount && totalCount > 0) {
  2142. infoText.style.cssText = `color: rgba(36, 242, 0, 0.75);`;
  2143. } else if (activeCount === 0) {
  2144. infoText.style.cssText = `color: var(--text-color-darker);`;
  2145. } else {
  2146. infoText.style.cssText = `color: rgba(242, 198, 0, 0.75);`;
  2147. }
  2148.  
  2149. collapsedInfo.appendChild(infoText);
  2150. container.appendChild(collapsedInfo);
  2151. }
  2152. }
  2153.  
  2154. // Add tooltip event listeners
  2155. nameSpan.addEventListener('mouseenter', (e) => {
  2156. showTooltip(e, getCharacterTooltipContent(character), true, character.avatar);
  2157. });
  2158. nameSpan.addEventListener('mouseleave', hideTooltip);
  2159. nameSpan.addEventListener('dragover', hideTooltip);
  2160.  
  2161. return container;
  2162. }
  2163.  
  2164. function updateAllToggleButtons() {
  2165. characterGroups.forEach((group, groupIndex) => {
  2166. const toggleAllButton = toggleAllButtonForGroup(groupIndex);
  2167. if (toggleAllButton) {
  2168. updateToggleAllButton(toggleAllButton, group);
  2169. }
  2170. });
  2171. }
  2172.  
  2173. // Function to get the toggle all button for a group
  2174. function toggleAllButtonForGroup(groupIndex) {
  2175. const groupContainer = characterGroupList.children[groupIndex];
  2176. if (groupContainer) {
  2177. const toggleAllButton = groupContainer.querySelector('button[title="Activate/Deactivate All"]');
  2178. return toggleAllButton;
  2179. }
  2180. return null;
  2181. }
  2182.  
  2183. // Function to rename a group
  2184. function renameGroup(groupIndex) {
  2185. const newName = prompt('Enter new group name:', characterGroups[groupIndex].name);
  2186. if (newName && newName.trim() !== '') {
  2187. characterGroups[groupIndex].name = newName.trim();
  2188. renderCharacterGroups();
  2189. }
  2190. }
  2191.  
  2192. // Function to delete a group
  2193. function deleteGroup(groupIndex) {
  2194. if (characterGroups.length === 1) {
  2195. alert('At least one group must exist.');
  2196. return;
  2197. }
  2198. if (confirm(`Are you sure you want to delete the group "${characterGroups[groupIndex].name}"? All characters in this group will be moved to "Ungrouped".`)) {
  2199. const group = characterGroups.splice(groupIndex, 1)[0];
  2200. const ungrouped = characterGroups.find(g => g.name === 'Ungrouped');
  2201. if (ungrouped) {
  2202. ungrouped.items = ungrouped.items.concat(group.items);
  2203. } else {
  2204. characterGroups.unshift({ name: 'Ungrouped', items: group.items, collapsed: false });
  2205. }
  2206. renderCharacterGroups();
  2207. }
  2208. }
  2209.  
  2210. // Default randomizer options
  2211. const defaultRandomizerOptions = {
  2212. emoji: {
  2213. 'Smileys': "😀%😃%😄%😁%😆%😅%😂%🤣",
  2214. 'People': "😊%😇%🙂%🙃%😉%😌%😍",
  2215. // Add more categories if needed
  2216. },
  2217. name: {
  2218. 'Common': "Alex%Charlie%Sam%Jessie%Max%Taylor%Jordan%Casey%Jamie%Robin",
  2219. 'Fantasy': "Aragorn%Legolas%Gandalf%Frodo%Bilbo%Galadriel%Sauron",
  2220. // Add more categories if needed
  2221. },
  2222. sex: {
  2223. 'Standard': "male*48%female*48%other*4",
  2224. // It's possible to have other options
  2225. },
  2226. species: {
  2227. 'Realistic': "Human",
  2228. 'Fantasy': "Human%Elf%Dwarf%Orc%Demon",
  2229. '25% Human | 75% Fantasy':"Human*2%Elf%Dwarf%Orc%Demon",
  2230. '50% Human | 50% Fantasy':"Human*4%Elf%Dwarf%Orc%Demon",
  2231. '75% Human | 25% Fantasy':"Human*12%Elf%Dwarf%Orc%Demon",
  2232. 'Sci-fi': "Human%Synth%Alien%Hybrid%Cyborg",
  2233. '25% Human | 75% Sci-fi':"Human*2%Synth%Alien%Hybrid%Cyborg",
  2234. '50% Human | 50% Sci-fi':"Human*4%Synth%Alien%Hybrid%Cyborg",
  2235. '75% Human | 25% Sci-fi':"Human*12%Synth%Alien%Hybrid%Cyborg",
  2236. 'Animals':"",
  2237. '25% Human | 75% Animals':"",
  2238. '50% Human | 50% Animals':"",
  2239. '75% Human | 25% Animals':"",
  2240. 'Mythological':"",
  2241. '25% Human | 75% Mythos':"",
  2242. '50% Human | 50% Mythos':"",
  2243. '75% Human | 25% Mythos':"",
  2244. 'Mythos + Animals':"",
  2245. '25% Human | 75% Mythos + Animals':"",
  2246. '50% Human | 50% Mythos + Animals':"",
  2247. '75% Human | 25% Mythos + Animals':"",
  2248. 'Pokemon':"",
  2249. '25% Human | 75% Pokemon':"",
  2250. '50% Human | 50% Pokemon':"",
  2251. '75% Human | 25% Pokemon':"",
  2252. 'Digimon':"",
  2253. '25% Human | 75% Digimon':"",
  2254. '50% Human | 50% Digimon':"",
  2255. '75% Human | 25% Digimon':"",
  2256. 'Pokemon + Digimon':"",
  2257. '25% Human | 75% Pokemon + Digimon':"",
  2258. '50% Human | 50% Pokemon + Digimon':"",
  2259. '75% Human | 25% Pokemon + Digimon':"",
  2260. 'Things':"pencil%chair%table%lamp%book%eraser%stapler%binder clip%paperclip%sticky note%whiteboard%highlighter%filing cabinet%desk%trash can%recycling bin%mouse%laptop%smartphone%charger%power bank%USB drive%light bulb%extension cord%surge protector%adapter%router%modem%switch%hub%camera%lens%memory card%case%suitcase%wallet%purse%handbag%umbrella%sunglasses%watch%bracelet%necklace%ring%earrings%belt%shoelace%mirror%comb%hairbrush%toothbrush%toothpaste%soap%shampoo%conditioner%towel%washcloth%razor%shaving cream%deodorant%nail clippers%nail file%tweezers%cotton swabs%tissue box%toilet paper%plunger%shower curtain%bath mat%sink%bathtub%toilet%tiles%floor mat%vacuum cleaner%broom%dustpan%mop%scrub brush%cleaning cloth%cleaning spray%detergent%laundry basket%clothes hanger%ironing board%iron%sewing machine%needle%scissors%safety pin%buttons%zipper%velcro%glue%adhesive tape%duct tape%masking tape%packing tape%measuring cup%mixing bowl%cutting board%knife%spoon%fork%spatula%whisk%ladle%tongs%peeler%grater%colander%strainer%can opener%bottle opener%corkscrew%baking sheet%cake pan%muffin tin%rolling pin%cookie cutter%oven mitt%potholder%stove%oven%microwave%refrigerator%freezer%blender%toaster%coffee maker%kettle%teapot%cup%mug%glass%plate%bowl%serving tray%pitcher%thermos%lunchbox%picnic basket%water bottle%ice cube tray%salt shaker%pepper grinder%napkin%tablecloth%placemat%coaster%candle%candlestick%matchbox%lighter%fire extinguisher%smoke detector%carbon monoxide detector%thermostat%air conditioner%heater%radiator%humidifier%dehumidifier%filter%blinds%curtains%drapes%curtain rod%window%door%door handle%keychain%padlock%hinge%doormat%intercom%doorbell%security camera%safe%toolbox%tape measure%level%nails%screws%bolts%nuts%washers%brackets%clamps%vice%workbench%ladder%paint tray%drop cloth%paint can%primer%varnish%stain%wallpaper%cable ties%zip ties%carabiner%bungee cord%crowbar%sledgehammer%hatchet%shovel%spade%rake%hoe%pitchfork%wheelbarrow%garden hose%sprinkler%watering can%flower pot%planter%pruning shears%hedge trimmer%lawn mower%weed eater%leaf blower%chainsaw%wood chipper%compost bin%birdhouse%bird feeder%birdbath%mailbox%traffic cone%road sign%bulletin board%chalkboard%corkboard%push pin%staple remover%correction fluid%hole punch%paper cutter%laminator%binding machine%shredder%podium%stage%spotlight%microphone stand%speaker stand%flute%metronome%record player%reel%album%photograph%canvas%easel%paint%paint palette%paintbrush%crayon%marker%colored pencil%charcoal%pastel%ink%pen%ruler%protractor%globe%atlas%microscope%magnifying glass%binoculars%lab coat%goggles%test tube%beaker%flask%graduated cylinder%pipette%dropper%Bunsen burner%tripod%retort stand%test tube rack%petri dish%microscope slide%cover slip%centrifuge%spectrometer%oscilloscope%voltmeter%ammeter%thermometer%barometer%anemometer%GPS device%walkie-talkie%remote control%joystick%game controller%console%VR headset%arcade machine%pinball machine%board game%chess set%checkers%backgammon%dice%playing cards%poker chips%dominoes%puzzle%Rubik's cube%yo-yo%kite%slinky%marbles%jacks%spinning top%toy car%toy train%doll%action figure%stuffed animal%building blocks%LEGO%model kit%RC car%skateboard%roller skates%bicycle%helmet%knee pads%elbow pads%backpack%sleeping bag%lantern%camping stove%flashlight%first aid kit%signal mirror%flare%emergency blanket%water filter%canteen%multi-tool%pocket knife%axe%machete%fishing rod%tackle box%bait%net%cooler%boat%paddle%oar%anchor%buoy%life jacket%wetsuit%snorkel%mask%fins%scuba tank%spear gun%kayak%canoe%raft%surfboard%paddleboard%jet ski%sailboat%yacht%motorboat%fishing boat%cargo ship%cruise ship%submarine%jet%airplane%helicopter%glider%hot air balloon%zeppelin%rocket%drone%parachute%wingsuit%spacesuit%space shuttle%rover%telescope%observatory%planetarium%sundial%clock%wristwatch%stopwatch%timer%alarm clock%grandfather clock%cuckoo clock%calendar%planner%diary%journal%notebook%sketchbook%scrapbook%photo album%painting%figurine%model%diorama%trophy%medal%certificate%diploma%award%plaque%banner%flag%sign%poster%billboard%advertisement%marquee%neon sign%streetlight%lamp post%traffic light%crosswalk signal%barricade%cone%detour sign%map%compass%GPS%sextant%radar%sonar%satellite%antenna%radio%television%monitor%projector%speaker%microphone%headphones%headset%amplifier%mixer%equalizer%synthesizer%keyboard%piano%guitar%drum set%violin%cello%clarinet%saxophone%trumpet%trombone%tuba%harmonica%accordion%tambourine%maracas%xylophone%cymbals%triangle%music stand%sheet music%vinyl record%cassette tape%CD%DVD%Blu-ray%VHS tape%film reel%film strip%projector screen%transparencies%slide%binder%folder%clipboard%envelope%stamp%ink pad%seal%wax%string%rubber band%pin%tack%staple%clip%latch%lock%key%chain%rope%cable%wire%hook%pulley%winch%jack%wrench%screwdriver%hammer%pliers%saw%drill%nail%screw%bolt%nut%washer%bracket%clamp%vise%chisel%file%rasp%sandpaper%sander%grinder%welder%torch%soldering iron%glue gun%adhesive%cement%resin%epoxy%sealant%tape%ribbon%thread%yarn%fabric%leather%lace%felt%foam%sponge%brush%roller%tray%bucket%can%jar%bottle%tube%nozzle%spray%pump%valve%faucet%spigot%hose%pipe%duct%vent%fan%motor%engine%turbine%generator%alternator%battery%cell%capacitor%resistor%transistor%diode%circuit board%chip%processor%memory%hard drive%SSD%USB stick%CD drive%DVD drive%Blu-ray drive%floppy disk%diskette%tape drive%printer%scanner%copier%fax machine%typewriter%calculator%abacus%cash register%ATM%vending machine%kiosk%ticket machine%turnstile%gate%barrier%fence%wall%partition%screen%curtain%drape%blind%shade%awning%canopy%tent%pavilion%gazebo%pergola%arbor%trellis%lattice%post%joist%rafter%truss%column%pillar%arch%dome%roof%ceiling%floor%tile%brick%block%stone%slab%board%sheet%panel%plank%beam%girder%frame%structure%bridge%tunnel%dam%tower%spire%skyscraper%monument%statue%sculpture%fountain%obelisk%pyramid%temple%church%mosque%synagogue%shrine%altar%cross%crucifix%star%crescent%wheel%buzzer%bell%chime%gong%drum%rattle%clapper%whistle%horn%siren%alarm%stapler remover%door wedge%thermometer stand%shoehorn%coat rack%mousepad%stencil%rubber stamp%tripod stool%firewood rack%bike pump%water timer%tool apron%screen protector%note holder%calendar stand%cable organizer%photo mat%step stool%window screen%metal detector",
  2261. 'Food':"Apple%Banana%Orange%Grapes%Watermelon%Strawberries%Blueberries%Raspberries%Pineapple%Mango%Papaya%Kiwi%Peach%Pear%Plum%Apricot%Cherries%Pomegranate%Guava%Lychee%Avocado%Tomato%Cucumber%Bell Pepper%Carrot%Potato%Sweet Potato%Broccoli%Cauliflower%Spinach%Kale%Lettuce%Cabbage%Celery%Asparagus%Artichoke%Eggplant%Zucchini%Squash%Pumpkin%Radish%Beet%Onion%Garlic%Leek%Shallot%Ginger%Chili Pepper%Jalapeño%Corn%Green Beans%Peas%Chickpeas%Lentils%Kidney Beans%Black Beans%Soybeans%Tofu%Tempeh%Seitan%Quinoa%Rice%Brown Rice%Jasmine Rice%Basmati Rice%Wild Rice%Oats%Barley%Rye%Wheat%Bread%Pasta%Noodles%Spaghetti%Fettuccine%Linguine%Ravioli%Tortellini%Lasagna%Macaroni%Couscous%Polenta%Pizza%Bagel%English Muffin%Croissant%Danish Pastry%Cinnamon Roll%Waffle%Pancake%French Toast%Muffin%Cupcake%Cake%Cheesecake%Pie%Tart%Brownie%Cookie%Biscuit%Cracker%Chips%Popcorn%Nuts%Beef%Pork%Lamb%Veal%Chicken%Turkey%Duck%Goose%Quail%Eggs%Milk%Yogurt%Cheese%Cheddar%Mozzarella%Parmesan%Gouda%Brie%Camembert%Feta%Cottage Cheese%Cream Cheese%Sour Cream%Butter%Olive Oil%Canola Oil%Coconut Oil%Sesame Oil%Peanut Oil%Sunflower Oil%Vegetable Oil%Vinegar%Balsamic Vinegar%Apple Cider Vinegar%White Vinegar%Worcestershire Sauce%Ketchup%Mustard%Mayonnaise%Hot Sauce%Sriracha%Tabasco%Salsa%Marinara Sauce%Alfredo Sauce%Gravy%Honey%Maple Syrup%Jam%Jelly%Marmalade%Peanut Butter%Almond Butter%Nutella%Chocolate%Dark Chocolate%Milk Chocolate%White Chocolate%Candy%Gummy Bears%Jelly Beans%Licorice%Marshmallows%Ice Cream%Gelato%Sorbet%Popsicle%Milkshake%Smoothie%Juice%Apple Juice%Orange Juice%Grapefruit Juice%Cranberry Juice%Tomato Juice%Carrot Juice%Lemonade%Iced Tea%Coffee%Espresso%Cappuccino%Latte%Tea%Green Tea%Black Tea%Chamomile Tea%Peppermint Tea%Ginger Tea%Herbal Tea%Soda%Cola%Root Beer%Ginger Ale%Sparkling Water%Energy Drink%Sports Drink%Beer%Wine%Red Wine%White Wine%Rosé Wine%Champagne%Whiskey%Bourbon%Scotch%Vodka%Gin%Rum%Tequila%Brandy%Cognac%Liqueur%Sake%Miso%Natto%Kimchi%Sauerkraut%Pickles%Olives%Capers%Anchovies%Sardines%Tuna%Salmon%Cod%Halibut%Trout%Catfish%Shrimp%Crab%Lobster%Clams%Mussels%Oysters%Scallops%Squid%Octopus%Caviar%Snails%Frog Legs%Alligator%Bison%Venison%Elk%Rabbit%Goat%Wild Boar%Ostrich%Pheasant%Cornish Hen%Foie Gras%Truffles%Morels%Chanterelles%Shiitake%Portobello%Button Mushrooms%Cremini Mushrooms%Enoki Mushrooms%Oyster Mushrooms%Maitake Mushrooms%Lion's Mane Mushrooms%Chia Seeds%Flax Seeds%Hemp Seeds%Pumpkin Seeds%Sesame Seeds%Sunflower Seeds%Poppy Seeds%Peanuts%Cashews%Almonds%Walnuts%Pecans%Pistachios%Macadamia Nuts%Hazelnuts%Brazil Nuts%Pine Nuts%Chestnuts%Coconut%Cacao Nibs%Cacao Powder%Cocoa Powder%Almond Flour%Coconut Flour%Chickpea Flour%Rice Flour%Tapioca Flour%Potato Flour%Cornstarch%Baking Powder%Baking Soda%Yeast%Vanilla Extract%Almond Extract%Peppermint Extract%Lemon Extract%Orange Extract%Rose Water%Saffron%Turmeric%Cumin%Coriander%Cardamom%Cinnamon%Nutmeg%Cloves%Allspice%Paprika%Cayenne Pepper%Red Pepper Flakes%Thyme%Rosemary%Oregano%Basil%Parsley%Cilantro%Mint%Dill%Bay Leaves%Curry Powder%Garam Masala%Chinese Five Spice%Italian Seasoning%Herbes de Provence%Cajun Seasoning%Taco Seasoning%Chili Powder%Garlic Powder%Onion Powder%Celery Salt%Smoked Paprika%Liquid Smoke%Vanilla Beans%Cacao Beans%Coffee Beans%Loose Leaf Tea%Matcha Powder%Protein Powder%Spirulina%Chlorella%Wheat Grass%Barley Grass%Alfalfa%Brewer's Yeast%Nutritional Yeast%Carob%Stevia%Agave Nectar%Molasses%Brown Sugar%Coconut Sugar%Date Sugar%Cane Sugar%Confectioner's Sugar%Xylitol%Erythritol%Monk Fruit Sweetener%Artificial Sweeteners%Margarine%Shortening%Lard%Beef Tallow%Duck Fat%Goose Fat%Chicken Fat%Bacon Grease%Schmaltz%Ghee%Avocado Oil%Grapeseed Oil%Flaxseed Oil%Walnut Oil%Hazelnut Oil%Almond Oil%Macadamia Nut Oil%Balsamic Glaze%Soy Sauce%Liquid Aminos%Coconut Aminos%Miso Paste%Hoisin Sauce%Oyster Sauce%Fish Sauce%Shrimp Paste%Curry Paste%Chili Paste%Harissa%Tahini%Pesto%Chimichurri%Gochujang%Mole%Enchilada Sauce%Tomatillo Salsa%Pico de Gallo%Salsa Verde%Mango Salsa%Peach Salsa%Pineapple Salsa%Corn Salsa%Black Bean Salsa%Guacamole%Hummus%Baba Ghanoush%Tzatziki%Raita%Queso%Nacho Cheese Sauce%Salad Dressing%Ranch Dressing%Italian Dressing%Vinaigrette%Caesar Dressing%Blue Cheese Dressing%Thousand Island Dressing%French Dressing%Russian Dressing%Green Goddess Dressing%Honey Mustard Dressing%Poppy Seed Dressing%Raspberry Vinaigrette%Balsamic Vinaigrette%Olive Oil & Vinegar%Soup%Tomato Soup%Vegetable Soup%Chicken Noodle Soup%Minestrone%Clam Chowder%Lobster Bisque%French Onion Soup%Miso Soup%Hot and Sour Soup%Egg Drop Soup%Wonton Soup%Ramen%Pho%Tom Yum%Gazpacho%Borscht%Vichyssoise%Goulash%Bouillabaisse%Cioppino%Jambalaya%Gumbo%Étouffée%Paella%Biryani%Risotto%Pilaf%Fried Rice%Congee%Oatmeal%Grits%Porridge%Granola%Muesli%Trail Mix%Beef Jerky%Fruit Leather%Dried Fruit%Raisins%Craisins%Apricots%Figs%Dates%Prunes%Freeze-Dried Fruit%Fruit Snacks%Fruit Roll-Ups%Canned Fruit%Applesauce%Fruit Cup%Fruit Salad%Ambrosia Salad%Jello Salad%Waldorf Salad%Coleslaw%Macaroni Salad%Potato Salad%Egg Salad%Chicken Salad%Tuna Salad%Crab Salad%Shrimp Salad%Lobster Salad%Seafood Salad%Caesar Salad%Cobb Salad%Chef Salad%Greek Salad%Caprese Salad%Niçoise Salad%Taco Salad%Burrito Bowl%Poke Bowl%Buddha Bowl%Acai Bowl%Smoothie Bowl%Yogurt Parfait%Chia Pudding%Rice Pudding%Tapioca Pudding%Bread Pudding%Crème Brûlée%Flan%Panna Cotta%Tiramisu%Trifle%Pavlova%Meringue%Macarons%Éclairs%Cream Puffs%Profiteroles%Cannoli%Baklava%Strudel%Rugelach%Hamantaschen%Biscotti%Madeleines%Financiers%Palmiers%Elephant Ears%Churros%Sopapillas%Beignets%Doughnuts%Cronut%Cake Pop%Cake Ball%Truffle%Chocolate Truffle%Caramel%Toffee%Brittle%Nougat%Marzipan%Fudge%Divinity%Turkish Delight%Gummies%Hard Candy%Lollipops%Taffy%Caramels%Salt Water Taffy%Peanut Brittle%Almond Roca%English Toffee%Butterscotch%Pralines%Peppermint Patties%Peanut Butter Cups%Chocolate Covered Raisins%Chocolate Covered Nuts%Chocolate Covered Pretzels%Chocolate Covered Espresso Beans%Chocolate Covered Bacon%Chocolate Fondue%Caramel Apples%Candy Apples%Cotton Candy%Popcorn Balls%Caramel Corn%Kettle Corn%Cheese Popcorn%Flavored Popcorn%Pork Rinds%Cracklins%Chicharrones%Plantain Chips%Tortilla Chips%Potato Chips%Corn Chips%Pretzels%Bagel Chips%Pita Chips%Rice Cakes%Croutons%Bread Crumbs%Panko%Stuffing%Cornbread%Biscuits%Rolls%Brioche%Challah%Naan%Pita%Tortillas%Taco Shells%Wonton Wrappers%Spring Roll Wrappers%Rice Paper%Seaweed%Nori",
  2262. 'Chaos':""
  2263. },
  2264. age: {
  2265. 'All': "1%2%3%4%5%6%7%8%9%10%11%12%13%14%15%16%17%18%19%20%21%22%23%24%25%26%27%28%29%30%31%32%33%34%35%36%37%38%39%40%41%42%43%44%45%46%47%48%49%50%51%52%53%54%55%56%57%58%59%60%61%62%63%64%65%66%67%68%69%70%71%72%73%74%75%76%77%78%79%80%81%82%83%84%85%86%87%88%89%90%91%92%93%94%95%96%97%98%99%100+", // ages 1 to 99
  2266. 'Child': "1%2%3%4%5%6%7%8%9%10%11%12%13%14%15%16%17", // ages 1 to 17
  2267. 'Adult': "18%19%20%21%22%23%24%25%26%27%28%29%30%31%32%33%34%35%36%37%38%39%40%41%42%43%44%45%46%47%48%49%50%51%52%53%54%55%56%57%58%59%60%61%62%63%64%65%66%67%68%69%70", // ages 18 to 7
  2268. 'Elderly':"%71%72%73%74%75%76%77%78%79%80%81%82%83%84%85%86%87%88%89%90%91%92%93%94%95%96%97%98%99%100+",
  2269. 'Ancient': "100%200%300%400%500%600%700%800%900%1000%1100%1200%1300%1400%1500%1600%1700%1800%1900%2000%2100%2200%2300%2400%2500%2600%2700%2800%2900%3000%3100%3200%3300%3400%3500%3600%3700%3800%3900%4000%4100%4200%4300%4400%4500%4600%4700%4800%4900%5000%5100%5200%5300%5400%5500%5600%5700%5800%5900%6000%6100%6200%6300%6400%6500%6600%6700%6800%6900%7000%7100%7200%7300%7400%7500%7600%7700%7800%7900%8000%8100%8200%8300%8400%8500%8600%8700%8800%8900%9000%9100%9200%9300%9400%9500%9600%9700%9800%9900%10000+",
  2270. },
  2271. bodyType: {
  2272. 'All': "Slim%Athletic%Average%Curvy%Muscular%Stocky%Petite%Lean%Toned%Fit%Slender%Shapely%Voluptuous%Chubby%Overweight%Obese%Hefty%Skinny%Scrawny%Gangly%Lanky%Wiry%Sinewy%Flabby%Pudgy%Plump%Portly%Stout%Rotund%Paunchy%Potbellied%Willowy%Statuesque%Towering%Diminutive%Stubby%Squat%Deformed%Grotesque%Hideous%Unsightly%Repulsive%Revolting%Monstrous%Misshapen%Disfigured%Malformed%Gorgeous%Stunning%Handsome%Beautiful%Attractive%Pleasing%Alluring%Captivating%Charming%Elegant",
  2273. 'Slender': "Slim%Lean%Slender%Skinny%Scrawny%Gangly%Lanky%Wiry%Sinewy%Willowy%Lithe%Graceful%Svelte%Sylphlike",
  2274. 'Fit': "Athletic%Toned%Fit%Muscular%Sinewy%Strapping%Brawny%Burly%Powerful%Robust%Vigorous%Mighty%Herculean",
  2275. 'Average': "Average%Shapely%Stocky%Ordinary%Common%Typical%Regular%Unremarkable%Nondescript%Plain%Homely",
  2276. 'Curvy': "Curvy%Voluptuous%Shapely%Buxom%Full-figured%Hourglass%Zaftig%Rubenesque%Lush%Ample%Curvaceous%Bodacious",
  2277. 'Overweight': "Chubby%Overweight%Obese%Hefty%Flabby%Pudgy%Plump%Portly%Stout%Rotund%Paunchy%Potbellied%Corpulent%Fleshy%Bloated",
  2278. 'Short': "Petite%Diminutive%Stubby%Squat%Pint-sized%Compact%Teeny%Miniature%Elfin%Dainty%Tiny%Wee",
  2279. 'Tall': "Towering%Statuesque%Lofty%Gigantic%Colossal%Enormous%Huge%Immense%Mammoth%Gargantuan%Titanic%Mountainous",
  2280. 'Ugly': "Deformed%Grotesque%Hideous%Unsightly%Repulsive%Revolting%Monstrous%Misshapen%Disfigured%Malformed%Unpleasant%Homely%Unappealing%Unattractive",
  2281. 'Attractive': "Gorgeous%Stunning%Handsome%Beautiful%Attractive%Pleasing%Alluring%Captivating%Charming%Elegant%Exquisite%Magnificent%Dazzling%Radiant",
  2282. },
  2283. personality: {
  2284. 'All': "Cheerful%Serious%Energetic%Calm%Average%Shy%Outgoing%Snarky%Kind%Brave%Cautious%Adventurous%Ambitious%Analytical%Charismatic%Confident%Creative%Curious%Dependable%Diplomatic%Disciplined%Empathetic%Enthusiastic%Extroverted%Friendly%Generous%Gentle%Honest%Humble%Humorous%Idealistic%Independent%Introverted%Intuitive%Logical%Loyal%Mysterious%Observant%Optimistic%Passionate%Patient%Perfectionist%Persistent%Pessimistic%Philosophical%Practical%Resourceful%Romantic%Sarcastic%Selfless%Sensible%Sensitive%Skeptical%Sociable%Spontaneous%Stoic%Stubborn%Sympathetic%Thoughtful%Trustworthy%Unpredictable%Wise%Witty%Zealous",
  2285. 'Positive': "Cheerful%Energetic%Kind%Brave%Adventurous%Ambitious%Charismatic%Confident%Creative%Curious%Dependable%Empathetic%Enthusiastic%Friendly%Generous%Gentle%Honest%Humble%Humorous%Idealistic%Independent%Intuitive%Loyal%Optimistic%Passionate%Patient%Persistent%Resourceful%Romantic%Selfless%Sensible%Sensitive%Sociable%Spontaneous%Sympathetic%Thoughtful%Trustworthy%Wise%Witty%Zealous",
  2286. 'Neutral': "Average%Analytical%Calm%Cautious%Diplomatic%Disciplined%Extroverted%Introverted%Logical%Mysterious%Observant%Philosophical%Practical%Stoic",
  2287. 'Negative': "Serious%Shy%Snarky%Cautious%Pessimistic%Perfectionist%Sarcastic%Skeptical%Stubborn%Unpredictable",
  2288. 'Extroverted': "Cheerful%Energetic%Outgoing%Adventurous%Charismatic%Confident%Enthusiastic%Extroverted%Friendly%Humorous%Sociable%Spontaneous%Witty",
  2289. 'Introverted': "Serious%Calm%Shy%Analytical%Cautious%Independent%Introverted%Mysterious%Observant%Thoughtful",
  2290. 'Thinking': "Analytical%Logical%Practical%Skeptical%Resourceful%Sensible%Wise%Philosophical%Perfectionist%Stoic%Disciplined%Persistent",
  2291. 'Feeling': "Kind%Empathetic%Gentle%Romantic%Selfless%Sensitive%Sympathetic%Idealistic%Passionate%Sentimental%Compassionate%Nurturing",
  2292. 'Judging': "Serious%Ambitious%Disciplined%Dependable%Honest%Loyal%Persistent%Practical%Responsible%Sensible%Trustworthy%Decisive%Organized%Punctual",
  2293. 'Perceiving': "Energetic%Adventurous%Creative%Curious%Adaptable%Easygoing%Flexible%Open-minded%Spontaneous%Tolerant%Improvising%Relaxed",
  2294. 'Assertive': "Confident%Independent%Ambitious%Decisive%Determined%Dominant%Firm%Strong-willed%Competitive%Bold%Daring%Fearless",
  2295. 'Turbulent': "Snarky%Sarcastic%Unpredictable%Chaotic%Impulsive%Moody%Paranoid%Pessimistic%Quarrelsome%Resentful%Temperamental%Volatile",
  2296. },
  2297. bio: {
  2298. 'Standard': "Loves exploring new places.%Enjoys reading books and learning new things.%Has a passion for music and the arts.%A dedicated athlete and fitness enthusiast.%An adventurous spirit with a love for travel.%Loyal friend who values honesty and integrity.%Tech-savvy individual with a knack for gadgets.%Creative thinker who enjoys solving problems.%Animal lover who spends time volunteering at shelters.%An aspiring chef who loves experimenting in the kitchen.",
  2299. // Add more categories if needed
  2300. },
  2301. };
  2302.  
  2303. // the "Animals" category was seperated into several different sections as to make it not so absurd in length
  2304. // A section
  2305. defaultRandomizerOptions.species["Animals"] += "Aardvark%Aardwolf%Abyssinian%Abyssinian Guinea Pig%Acadian Flycatcher%Achrioptera Manga%Ackie Monitor%Addax%Adélie Penguin%Admiral Butterfly%Aesculapian Snake%Affenpinscher%Afghan Hound%African Bullfrog%African Bush Elephant%African Civet%African Clawed Frog%African Elephant%African Fish Eagle%African Forest Elephant%African Golden Cat%African Grey Parrot%African Jacana%African Palm Civet%African Penguin%African Sugarcane Borer%African Tree Toad%African Wild Dog%Africanized bee (killer bee)%Agama Lizard%Agkistrodon Contortrix%Agouti%Aidi%Ainu%Airedale Terrier%Airedoodle%Akbash%Akita%Akita Shepherd%Alabai (Central Asian Shepherd)%Alaskan Husky%Alaskan Klee Kai%Alaskan Malamute%Alaskan Pollock%Alaskan Shepherd%Albacore Tuna%Albatross%Albertonectes%Albino (Amelanistic) Corn Snake%Aldabra Giant Tortoise%Alligator Gar%Allosaurus%Allosaurus%Alpaca%Alpine Dachsbracke%Alpine Goat%Alusky%Amano Shrimp%Amargasaurus%Amazon Parrot%Amazon River Dolphin (Pink Dolphin)%Amazon Tree Boa%Amazonian Royal Flycatcher%Amberjack%Ambrosia Beetle%American Alligator%American Alsatian%American Bulldog%American Bully%American Cocker Spaniel%American Cockroach%American Coonhound%American Dog Tick%American Eel%American Eskimo Dog%American Foxhound%American Hairless Terrier%American Leopard Hound%American Paddlefish%American Pit Bull Terrier%American Pugabull%American Pygmy Goat%American Robin%American Staffordshire Terrier%American Toad%American Water Spaniel%American Wirehair%Amethystine Python (Scrub Python)%Amphicoelias Fragillimus%Amur Leopard%Anaconda%Anatolian Shepherd Dog%Anchovies%Andrewsarchus%Angelfish%Angelshark%Angled Sunbeam Caterpillar%Anglerfish%Angora Ferret%Angora Goat%Anhinga%Anna’s Hummingbird%Anole Lizard%Anomalocaris%Ant%Antarctic Scale Worm%Anteater%Antelope%Anteosaurus%Antiguan Racer Snake%Aoudad Sheep%Ape%Apennine Wolf%Appenzeller Dog%Apple Head Chihuahua%Apple Moth%Arabian Cobra%Arabian Wolf%Arafura File Snake%Arambourgiania%Arapaima%Archaeoindris%Archaeopteryx%Archaeotherium%Archelon Turtle%Archerfish%Arctic Char%Arctic Fox%Arctic Hare%Arctic Wolf%Arctodus%Arctotherium%Argentavis Magnificens%Argentine Black and White Tegu%Argentine Horned Frog%Argentinosaurus%Arizona Bark Scorpion%Arizona Black Rattlesnake%Arizona Blonde Tarantula%Arizona Coral Snake%Armadillo%Armadillo Lizard%Armenian Gampr%Armored Catfish%Armyworm%Arsinoitherium%Arthropleura%Aruba Rattlesnake%Ashy Mining Bee%Asian Arowana%Asian Carp%Asian Cockroach%Asian Elephant%Asian Giant Hornet%Asian Lady Beetle%Asian Longhorn Beetle%Asian Palm Civet%Asian Vine Snake%Asian Water Monitor%Asiatic Black Bear%Asp%Assassin Bug%Assassin Snail%Atlantic Cod%Atlantic Salmon%Atlantic Sturgeon%Atlas Beetle%Atlas Moth%Aurochs%Aussiedoodle%Aussiedor%Aussiepom%Australian Bulldog%Australian Cattle Dog%Australian Cockroach%Australian Firehawk%Australian Flathead Perch%Australian Gecko%Australian Kelpie Dog%Australian Labradoodle%Australian Mist%Australian Retriever%Australian Shepherd%Australian Shepherd Mix%Australian Terrier%Australopithecus%Australorp Chicken%Avocet%Axanthic Ball Python%Axolotl%Ayam Cemani%Aye-aye%Azawakh%";
  2306. // B section
  2307. defaultRandomizerOptions.species["Animals"] += "Babirusa%Baboon%Bactrian Camel%Badger%Bagle – Basset Hound Mix%Bagworm Moth%Bagworm Moth Caterpillar%Baird’s Rat Snake%Bald Eagle%Baleen Whale%Balinese%Balkan Lynx%Ball Python%Bamboo Rat%Bamboo Shark%Bamboo Worms%Banana Ball Python%Banana Cinnamon Ball Python%Banana Spider%Banded Krait%Banded Palm Civet%Banded Water Snake%Bandicoot%Banjo Catfish%Barb%Barbet%Barbut’s Cuckoo Bumblebee%Barinasuchus%Bark Beetle%Bark Scorpion%Barn Owl%Barn Spider%Barn Swallow%Barnacle%Barnevelder%Barosaurus%Barracuda%Barramundi Fish%Barred Owl%Barreleye Fish (Barrel Eye)%Barylambda%Basenji Dog%Basenji Mix%Basilisk Lizard%Basilosaurus%Basking Shark%Bass%Bassador%Basset Fauve de Bretagne%Basset Hound%Bassetoodle%Bat-Eared Fox%Batfish%Bavarian Mountain Hound%Baya%Bea-Tzu%Beabull%Beagador%Beagle%Beagle Mix%Beagle Shepherd%Beaglier%Beago%Bear%Bearded Collie%Bearded Dragon%Bearded Fireworm%Bearded Vulture%Beaski%Beauceron%Beauty rat snake%Beaver%Bed Bugs%Bedlington Terrier%Bee%Bee-Eater%Beefalo%Beetle%Beewolf wasp%Belgian Canary%Belgian Laekenois%Belgian Malinois%Belgian Malinois Mix%Belgian Sheepdog%Belgian Shepherd%Belgian Tervuren%Belted Kingfisher%Beluga Sturgeon%Beluga Sturgeon%Bengal Tiger%Bergamasco%Berger Blanc Suisse%Berger Picard%Bernedoodle%Bernese Mountain Dog%Bernese Mountain Dog Mix%Bernese Shepherd%Betta Fish (Siamese Fighting Fish)%Bhutan Takin%Bichir%Bichon Frise%Bichpoo%Biewer Terrier%Bigfin Reef Squid %Bighorn Sheep%Bilby%Binturong%Bird%Bird Of Paradise%Bird Snake%Birman%Biscuit Beetle%Bismarck Ringed Python%Bison%Black And Tan Coonhound%Black and White Warbler%Black Aphids%Black Bass%Black Crappie%Black Dragon Lizard%Black German Shepherd%Black Mamba%Black Marlin%Black Mouth Cur%Black Pastel Ball Python%Black Rat Snake%Black Rhinoceros%Black Russian Terrier%Black Sea Bass%Black Swallowtail Caterpillar%Black Tarantula%Black Throat Monitor%Black Wasp%Black Widow Spider%Black Witch Moth%Black-Bellied Whistling Duck%Black-Capped Chickadee%Black-Footed Ferret%Black-headed python%Black-Tailed Rattlesnake%Blackburnian Warbler%Blackfin Tuna%Blacknose Shark%Blackpoll Warbler%Blacktip Reef Shark%Blacktip Shark %Bladefin Basslet%Blanket Octopus%Blind Snake%Blister Beetle%Blister Beetle%Blobfish%Blood Python%Bloodhound%Blowfly%Blue Andalusian%Blue Belly Lizard%Blue Catfish%Blue Death Feigning Beetle%Blue Dragon Sea Slug%Blue Eyed Pleco%Blue German Shepherd%Blue Gray Gnatcatcher%Blue grosbeak%Blue Iguana%Blue Jay%Blue Lacy Dog%Blue Nose Pit Bull%Blue Picardy Spaniel%Blue Racer%Blue Shark%Blue Tanager (Blue-Grey Tanager)%Blue Tang%Blue Tit%Blue Whale%Blue-Ringed Octopus%Bluefin Tuna%Bluefish%Bluegill%Bluetick Coonhound%Boas%Bobcat%Bobolink%Boelen’s python%Boer Goat%Boerboel%Boggle%Boglen Terrier%Boiga%Bolivian Anaconda%Bolognese Dog%Bombardier Beetle%Bombay%Bonefish%Bongo%Bonito Fish%Bonnethead Shark%Bonobo%Booby%Boomslang%Booted Bantam%Borador%Border Collie%Border Collie Mix%Border Terrier%Bordoodle%Borkie%Bornean Orangutan%Borneo Elephant%Boskimo%Boston Terrier%Bottlenose Dolphin%Bouvier Des Flandres%Bowfin%Bowhead Whale%Box Jellyfish%Box Tree Moth%Box Turtle%Box-Headed Blood Bee%Boxachi%Boxador%Boxer Dog%Boxer Mix%Boxerdoodle%Boxfish%Boxsky%Boxweiler%Bracco Italiano%Brachiosaurus%Brahma Chicken%Brahminy Blindsnake%Braque du Bourbonnais%Braque Francais%Brazilian Black Tarantula%Brazilian Terrier%Brazilian Treehopper%Bredl’s Python%Briard%British Timber%Brittany%Brontosaurus%Bronze Whaler Shark%Bronze-winged Jacana%Brook Trout%Brookesia Micra%Brown Bear%Brown Dog Tick%Brown Headed Cowbird%Brown Hyena%Brown Snake%Brown Tree Snake%Brown Water Snake%Brown-banded Cockroach%Brug%Brussels Griffon%Budgerigar%Buff Orpington Chicken%Buffalo%Buffalo Fish%Bull and Terrier%Bull Shark%Bull Terrier%Bull Trout%Bullboxer%Bulldog%Bulldog Mix%Bullfrog%Bullmastiff%Bullsnake%Bumblebee%Burmese%Burmese Python%Burrowing Frog%Burrowing Owl%Bush Baby%Bush Dog%Bush Viper%Bushmaster Snake%Butterfly%Butterfly Fish%";
  2308. // C section
  2309. defaultRandomizerOptions.species["Animals"] += "Cabbage Moth%Cactus Moth%Cactus Mouse%Cactus Wren%Caecilian%Caiman%Caiman Lizard%Cairn Terrier%California Condor%California Kingsnake%California Tarantula%Camel%Camel Cricket%Camel Spider%Campine Chicken%Canaan Dog%Canada Lynx%Canada Warbler%Canadian Eskimo Dog%Canadian Horse%Cane Corso%Cane Rat%Cane Spider%Cantil%Canvasback%Cape Lion%Capuchin%Capybara%Caracal%Cardinal%Caribbean Reef Shark%Caribou%Carolina Dog%Carolina Parakeet%Carp%Carpenter Ant%Carpet Beetle%Carpet Python%Carpet Viper%Carrion Beetle%Cascabel%Cashmere Goat%Cassowary%Cat%Cat Snake%Cat-Eyed Snake%Cat-Faced Spider%Catahoula Bulldog%Catahoula Leopard%Catalan Sheepdog%Caterpillar%Catfish%Caucasian Mountain Dog (Shepherd)%Caucasian Shepherd%Cava Tzu%Cavador%Cavalier King Charles Spaniel%Cavapoo%Cave Bear%Cave Lion%Cecropia Moth%Cedar Waxwing%Centipede%Central Ranges Taipan%Cephalaspis%Ceratopsian%Ceratosaurus%Cervalces latifrons (Broad-Fronted Moose)%Cesky Fousek%Cesky Terrier%Chain Pickerel%Chameleon%Chamois%Chartreux%Cheagle%Checkered Garter Snake%Cheetah%Chesapeake Bay Retriever%Chestnut-Sided Warbler%Chi Chi%Chickadee%Chicken%Chicken Snake%Chigger%Chihuahua%Chihuahua Mix%Children’s python%Chilean Recluse Spider%Chilean Rose Tarantula%Chilesaurus%Chimaera%Chimpanzee%Chinchilla%Chinese Alligator%Chinese Cobra%Chinese Crested Dog%Chinese Geese%Chinese Paddlefish%Chinese Shar-Pei%Chinese Water Deer%Chinook%Chinook Salmon%Chinstrap Penguin%Chipit%Chipmunk%Chipoo%Chipping Sparrow%Chiton%Chiweenie%Chorkie%Chow Chow%Chow Pom%Chow Shepherd%Christmas Beetle%Christmas Island Red Crab%Chromodoris Willani%Chusky%Cicada%Cichlid%Cinereous Vulture%Cinnamon Ball Python%Cinnamon Bear%Cinnamon Ferret%Clark’s Grebe%Clearnose Skate%Click Beetle%Clock Spider%Clothes Moth%Clouded Leopard%Clownfish%Clumber Spaniel%Coachwhip Snake%Coastal Carpet Python%Coastal Taipan%Coati%Cobalt Blue Tarantula%Cobia Fish%Cobras%Cochin Chicken%Cockalier%Cockapoo%Cockatiel%Cockatoo%Cocker Spaniel%Cockle%Cockroach%Coconut Crab %Codfish%Codling Moth%Coelacanth%Collared Peccary%Collett’s Snake%Collie%Collie Mix%Colossal Squid%Comb Jellyfish%Comb-crested Jacana%Comet Moth%Comfort Retriever%Common Buzzard%Common Carp%Common European Adder%Common Frog%Common Furniture Beetle%Common Goldeneye%Common Grackle%Common Green Magpie%Common House Spider%Common Loon%Common Raven%Common Spotted Cuscus%Common Toad%Common Yellowthroat%Compsognathus%Cone Snail%Conger Eel%Congo Snake%Conure%Cookiecutter Shark%Cooper’s Hawk%Copperhead%Coral%Coral Snake%Corella%Corgidor%Corgipoo%Corkie%Corman Shepherd%Cormorant%Corn Earworm%Corn Rex Cat (Cornish Rex)%Corn Snake%Cory Catfish%Coryphodon%Cosmic Caterpillar%Costa’s Hummingbird%Coton de Tulear%Cotton-top Tamarin%Cottonmouth%Coues Deer%Cougar%Cow%Cow Reticulated Python%Coyote%Crab%Crab Spider%Crab-Eating Fox%Crab-Eating Macaque%Crabeater Seal%Crane%Crappie Fish%Crayfish%Crested Gecko%Crested Penguin%Cricket%Croatian Sheepdog%Crocodile%Crocodile Monitor%Crocodile Shark%Crocodylomorph%Cross Fox%Cross River Gorilla%Crow%Crucian Carp%Cryolophosaurus%Cuban Boa%Cuban Cockroach%Cubera Snapper%Cuckoo%Cucumber Beetle%Curly Coated Retriever%Curly Hair Tarantula%Cuttlefish%Czechoslovakian Wolfdog%";
  2310. // D section
  2311. defaultRandomizerOptions.species["Animals"] += "Dachsador%Dachshund%Dachshund Mix%Daeodon%Dalmadoodle%Dalmador%Dalmatian%Damselfish%Dandie Dinmont Terrier%Daniff%Danios%Danish Swedish Farmdog%Dapple Dachshund%Dark-Eyed Junco%Dark-Eyed Junco%Darkling Beetle%Darwin’s fox%Darwin’s Frog%Daug%De Brazza’s Monkey%De Kay’s Brown Snake%Death Adder%Death’s Head Cockroach%Deathwatch Beetle%Decorator Crab%Deer%Deer Head Chihuahua%Deer Mouse%Deer Tick%Deinocheirus%Deinosuchus%Desert Ghost Ball Python%Desert Kingsnake%Desert Locust%Desert Rain Frog%Desert Tortoise%Desert Wolf%Desmostylus%Deutsche Bracke%Devil’s Coach Horse Beetle%Devon Rex%Dhole%Diamond Python%Diamondback Moth%Dickcissel%Dickinsonia%Dik-Dik%Dilophosaurus%Dimetrodon%Diminutive Woodrat%Dingo%Dinocrocuta%Dinofelis%Dinopithecus%Dinosaur Shrimp%Dinosaurs%Diplodocus%Diprotodon%Dire Wolf%Disco Clam%Discus%Diving Bell Spider (Water Spider)%Diving Duck%Doberman Pinscher%Dobsonfly%Dodo%Doedicurus%Dog%Dog Tick%Dogo Argentino%Dogue De Bordeaux%Dolphin%Donkey%Dorgi%Dorkie%Dorking Chicken%Dormouse%Double Doodle%Douc%Downy Woodpecker%Doxiepoo%Doxle%Draco Volans Lizard%Dragon Eel%Dragon Snake (Javan Tubercle Snake% Javan Mudsnake)%Dragonfish%Dragonfly%Dreadnoughtus%Drever%Dried Fruit Moth%Drum Fish%Dubia Cockroach%Duck%Dugong%Dumeril’s Boa%Dung Beetle%Dungeness Crab%Dunker%Dunkleosteus%Dunnock%Dusky Dolphin%Dusky Shark%Dutch Rabbit%Dutch Shepherd%Dwarf Boa%Dwarf Crocodile%Dwarf Gourami%Dwarf Hamster%";
  2312. // E section
  2313. defaultRandomizerOptions.species["Animals"] += "Eagle%Eagle Ray%Eared Grebe%Earless Monitor Lizard%Earthworm%Earwig%East Siberian Laika%Eastern Barred Bandicoot%Eastern Bluebird%Eastern Box Turtle%Eastern Brown Snake%Eastern Chipmunk%Eastern Coral Snake%Eastern Cottontail%Eastern Diamondback Rattlesnake%Eastern Dobsonfly%Eastern Fence Lizard%Eastern Glass Lizard%Eastern Gorilla%Eastern Gray Squirrel%Eastern Green Mamba%Eastern Hognose Snake%Eastern Indigo Snake%Eastern Kingbird%Eastern Lowland Gorilla%Eastern Meadowlark%Eastern Phoebe%Eastern Racer%Eastern Rat Snake%Eastern Tiger Snake%Eastern Turkey (Wild Turkey)%Eastern Woodrat%Echidna%Eclectus Parrot%Edible Frog%Eel%Eel catfish%Eelpout%Egret%Egyptian Cobra (Egyptian Asp)%Egyptian Goose%Egyptian Mau%Egyptian Tortoise%Egyptian Vulture%Eider%Eland%Elasmosaurus%Elasmotherium%Electric Catfish%Electric Eel%Elegant Tern%Elephant%Elephant Beetle%Elephant Bird%Elephant Fish%Elephant Seal%Elephant Shrew%Elf Owl%Elk%Ember Tetra%Embolotherium%Emerald Toucanet%Emerald Tree Boa%Emerald Tree Monitor%Emperor Angelfish%Emperor Goose%Emperor Penguin%Emperor Tamarin%Emu%Enchi Ball Python%English Angora Rabbit%English Bulldog%English Cocker Spaniel%English Cream Golden Retriever%English Crested Guinea Pig%English Foxhound%English Longhorn Cattle%English Pointer%English Setter%English Shepherd%English Springer Spaniel%English Toy Terrier%Entlebucher Mountain Dog%Epagneul Pont Audemer%Epicyon haydeni%Epidexipteryx%Equatorial Spitting Cobra%Equus giganteus%Ermine%Eryops%Escolar%Eskimo Dog%Eskipoo%Estrela Mountain Dog%Euoplocephalus%Eurasian Beaver%Eurasian Bullfinch%Eurasian Collared Dove%Eurasian Eagle-owl%Eurasian Jay%Eurasian Lynx%Eurasian Nuthatch%Eurasian Wolf%Eurasier%European Bee-Eater%European Corn Borer%European Goldfinch%European Polecat%European Robin%European Starling%European Wildcat%Eurypterus%Evening Bat%Evening Grosbeak%Executioner Wasp %Eyelash Viper%";
  2314. // F section
  2315. defaultRandomizerOptions.species["Animals"] += "Fairy-Wren%Falcon%Fallow deer%False Cobra%False coral snake%False Killer Whale%False Water Cobra%False Widow Spider%Fancy Mouse%Fangtooth%Feather Star%Feist%Fennec Fox%Fer-de-lance Snake%Ferret%Ferruginous Hawk%Fiddler Crab%Field Cuckoo Bumblebee%Field Spaniel%Fierce Snake%Figeater Beetle%Fila Brasileiro%Fin Whale%Finch%Finnish Lapphund%Finnish Spitz%Fire Ball Python%Fire Eel%Fire Salamander%Fire-Bellied Toad%Firefly%Firefly Ball Python%Fish%Fisher%Fishing Cat%Flamingo%Flat-Coated Retriever%Flathead Catfish%Flea%Flea Beetle%Fleckvieh Cattle%Florida Gar%Florida Mouse%Florida Panther%Florida Woods Cockroach%Flounder%Flounder Fish%Flour Beetle%Flowerhorn Fish%Fluke Fish (summer flounder)%Fly%Flycatcher%Flying Fish%Flying Lemur%Flying Snake%Flying Squirrel%Football Fish%Forest Cobra%Forest Cuckoo Bumblebee%Formosan Mountain Dog%Fossa%Fox%Fox Snakes%Fox Squirrel%Fox Terrier%Freeway Ball Python%French Bulldog%French Bulldog Mix%French Lop%Frenchton%Frengle%Freshwater Crocodile%Freshwater Drum%Freshwater Eel%Freshwater Jellyfish%Freshwater Sunfish%Frigatebird%Frilled Lizard%Frilled Shark%Fritillary Butterfly%Frizzle Chicken%Frog%Frogfish%Frug%Fruit Bat%Fruit Fly%Fulvous Whistling Duck%Fur Seal%Furrow Bee%";
  2316. // G section
  2317. defaultRandomizerOptions.species["Animals"] += "Gaboon Viper%Gadwall%Galapagos Penguin%Galapagos Shark%Galapagos Tortoise%Gar%Garden Eel%Garden Spider%Gargoyle Gecko%Garter Snake%Gastornis%Gazelle%Gecko%Genet%Gentoo Penguin%Geoffroys Tamarin%Gerberian Shepsky%Gerbil%German Cockroach%German Longhaired Pointer%German Pinscher%German Shepherd Guide%German Shepherd Mix%German Sheppit%German Sheprador%German Shorthaired Pointer%German Spitz%German Wirehaired Pointer%Gharial%Ghost Catfish%Ghost Crab%Giant African Land Snail%Giant Armadillo%Giant Beaver%Giant Clam%Giant Desert Centipede%Giant Golden Mole%Giant House Spider%Giant Isopod%Giant Leopard Moth%Giant Panda Bear%Giant Salamander%Giant Schnauzer%Giant Schnoodle%Giant Siphonophore%Giant Trevally%Giant Weta%Giant Wood Moth%Gibbon%Gigantopithecus%Gila Monster%Giraffe%Glass Frog%Glass Lizard%Glechon%Glen Of Imaal Terrier%Glowworm%Gnat%Goat%Goberian%Goblin Shark%Goby Fish%Goldador%Goldcrest%Golden Dox%Golden Eagle%Golden Irish%Golden Jackal%Golden Lancehead%Golden Lion Tamarin%Golden Masked Owl%Golden Mole%Golden Newfie%Golden Oriole%Golden Pyrenees%Golden Retriever%Golden Retriever Mix%Golden Saint%Golden Shepherd%Golden Shiner%Golden Tortoise Beetle%Golden Trout%Golden-Crowned Flying Fox%Golden-Crowned Kinglet%Goldendoodle%Goldfish%Goliath Beetle%Goliath Frog%Goliath Grouper%Goliath Tigerfish%Gollie%Gomphotherium%Goonch Catfish%Goose%Gooty Sapphire Tarantula%Gopher%Gopher Tortoise%Goral%Gordon Setter%Gorgosaurus%Gorilla%Goshawk%Gouldian Finch%Gourami%Grapevine Beetle%Grass Carp%Grass Snake%Grass Spider%Grasshopper%Grasshopper Mouse%Gray Catbird%Gray Fox%Gray Tree Frog%Great Blue Heron%Great Crested Flycatcher%Great Dane%Great Dane Mix%Great Danoodle%Great Egret%Great Hammerhead Shark%Great Kiskadee%Great Plains Rat Snake%Great Potoo Bird%Great Pyrenees%Great Pyrenees Mix%Great White Shark%Greater Swiss Mountain Dog%Grebe%Green Anaconda%Green Anole%Green Aphids%Green Bee-Eater%Green Bottle Blue Tarantula%Green Frog%Green Heron%Green June Beetle%Green Mamba%Green Rat Snake%Green Snake%Green Sunfish%Green Tree Frog%Green Tree Python%Greenland Dog%Greenland Shark%Grey Heron%Grey Mouse Lemur%Grey Reef Shark%Grey Seal%Greyhound%Griffon Vulture%Griffonshire%Grizzly Bear%Groenendael%Ground Snake%Ground Squirrel%Groundhog (Woodchuck)%Groundhog Tick%Grouper%Grouse%Grunion%Guadalupe Bass%Guinea Fowl%Guinea Pig%Gulper Catfish %Gulper Eel %Guppy%Gypsy Cuckoo Bumblebee%Gypsy Moth%";
  2318. // H section
  2319. defaultRandomizerOptions.species["Animals"] += "Haast’s Eagle%Habu Snake%Haddock%Hagfish%Haikouichthys%Hainosaurus%Hairy Frogfish%Hairy Woodpecker%Hairy-footed Flower Bee%Halibut%Hallucigenia%Hamburg Chicken%Hammerhead Shark%Hammerhead Worm%Hammond’s flycatcher%Hamster%Harbor Porpoise%Harbor Seal%Hardhead Catfish%Hare%Harlequin Coral Snake%Harlequin Rabbit%Harp Seal%Harpy Eagle%Harrier%Harris’s Hawk%Hartebeest%Hatzegopteryx%Havamalt%Havanese%Havapoo%Havashire%Havashu%Hawaiian Crow%Hawaiian Goose (Nene)%Hawaiian Monk Seal%Hawk%Hawk Moth Caterpillar%Hedgehog%Helicoprion%Hellbender%Hepatic Tanager (Red Tanager)%Hercules Beetle%Hercules Moth%Hermit Crab%Heron%Herrerasaurus%Herring%Herring Gull%Highland Cattle%Himalayan%Hippopotamus%Hippopotamus gorgops%Hoary Bat%Hobo Spider%Hognose snake%Hokkaido%Holy Cross Frog%Honduran White Bat%Honey Badger%Honey Bee%Honey Buzzard%Hooded Oriole%Hooded Seal%Hook-Nosed Sea Snake%Hoopoe%Horgi%Horn Shark%Hornbill%Horned Adder%Horned Beetle%Horned Grebe%Horned Lizard%Horned Viper%Hornet%Horse%Horse Mackerel%Horsefly%Horseshoe Crab%Houdan Chicken%House Finch%House Sparrow (English Sparrow)%House wren%Housefly%Hovasaurus%Hovawart%Howler Monkey%Human%Humboldt Penguin%Humboldt Squid%Hummingbird%Hummingbird Hawk-Moth%Humpback Whale%Huntaway%Huntsman Spider%Huskador%Huskita%Husky%Husky Jack%Huskydoodle%Hyacinth Macaw%Hyaenodon%Hyena%";
  2320. // I section
  2321. defaultRandomizerOptions.species["Animals"] += "Ibex%Ibis%Ibizan Hound%Icadyptes%Icelandic Sheepdog%Ichthyosaurus%Ichthyostega%Iguana%Iguanodon%IMG Boa Constrictor%Immortal Jellyfish%Impala%Imperial Moth%Inchworm%Indian Cobra%Indian Elephant%Indian Giant Squirrel%Indian Palm Squirrel%Indian python%Indian Rhinoceros%Indian Star Tortoise%Indianmeal Moth%Indigo Snake%Indochinese Tiger%Indri%Inland Taipan%Insect%Insects%Io Moth%Irish Doodle%Irish Elk%Irish Setter%Irish Terrier%Irish Water Spaniel%Irish WolfHound%Italian Greyhound%Ivory-billed woodpecker%Ivy Bee%";
  2322. // J section
  2323. defaultRandomizerOptions.species["Animals"] += "Jabiru%Jacana%Jack Crevalle%Jack Russells%Jack-Chi%Jackabee%Jackal%Jackdaw%Jackrabbit%Jackson’s Chameleon%Jagdterrier%Jaguar%Jaguarundi Cat%Jamaican Boa%Jamaican Iguana%Japanese Bantam Chicken%Japanese Beetle%Japanese Chin%Japanese Macaque%Japanese rat snake%Japanese Spitz%Japanese Squirrel%Japanese Terrier%Javan Leopard%Javan Rhinoceros%Javanese%Jellyfish%Jerboa%Jewel Beetle %John Dory%Jonah Crab%Joro Spider%Josephoartigasia monesi%Jumping Spider%Jungle Carpet Python%Junglefowl%";
  2324. // K section
  2325. defaultRandomizerOptions.species["Animals"] += "Kagu%Kai Ken%Kakapo%Kaluga Sturgeon%Kamehameha Butterfly%Kangal Shepherd Dog%Kangaroo%Kangaroo Mouse%Kangaroo Rat%Katydid%Kaua’i ‘Ō‘ō%Keagle%Keel-Billed Toucan%Keelback%Keeshond%Kelp Greenling%Kentucky Warbler%Kenyan Sand Boa%Kermode Bear (Spirit Bear)%Kerry Blue Terrier%Kestrel%Keta Salmon%Key Deer%Keyhole Cichlid%Khao Manee%Khapra Beetle%Kiang%Kiko Goat%Killdeer%Killer Clown Ball Python%Killer Whale%Killifish%Kinabalu Giant Red Leech%Kinder Goat%King Cobra%King Crab%King Eider%King Mackerel%King Penguin%King Rat Snake%King Salmon%King Shepherd%King Snake%King Vulture%Kingfisher%Kingklip%Kinkajou%Kirtland’s Snake%Kishu%Kissing Bugs%Kissing Gourami%Kit Fox%Kitefin Shark%Kiwi%Klipspringer%Knifefish%Knight Anole%Koala%Kodiak Bear%Kodkod%Koi Fish%Kokanee Salmon%Komodo Dragon%Komondor%Kooikerhondje%Koolie%Korean Jindo%Kori Bustard%Kouprey%Kowari%Krait%Krill%Kudu%Kudzu Bug%Kuvasz%";
  2326. // L section
  2327. defaultRandomizerOptions.species["Animals"] += "Labahoula%Labmaraner%Labout’s Fairy Wrasse%Labrabull%Labradane%Labradoodle%Labrador Retriever%Labraheeler%Labrottie%Lace Bug%Lace Monitor%Ladybug%Ladyfish%Lagotto Romagnolo%Lake Sturgeon%Lake Trout%Lakeland Terrier%LaMancha Goat%Lamprey%Lancashire Heeler%Lancetfish%Landseer Newfoundland%Lappet-faced Vulture%Lapponian Herder%Larder Beetle%Large Munsterlander%Largemouth Bass%Laughing Kookaburra%Lavender Albino Ball Python%Lawnmower Blenny%Lazarus Lizard%Leaf-Tailed Gecko%Leafcutter Ant%Leafcutter Bee%Least Flycatcher%Leatherback Sea Turtle%Leech%Leedsichthys%Leghorn Chicken%Leichhardt’s Grasshopper%Lemming%Lemon Blast Ball Python%Lemon Cuckoo Bumblebee%Lemon Shark%Lemur%Leonberger%Leopard%Leopard Cat%Leopard Frog%Leopard Gecko%Leopard Lizard%Leopard Seal%Leopard Shark%Leopard Tortoise%Leptocephalus%Lesser Jacana%Lesser Scaup%Lhasa Apso%Lhasapoo%Liger%Limpet%Lineback Cattle%Linnet%Lion%Lion’s Mane Jellyfish%Lionfish%Liopleurodon%Liopleurodon%Lipstick Albino Boa%Little Brown Bat%Little Penguin%Livyatan%Lizard%Lizardfish%Llama%Loach%Lobster%Locust%Loggerhead Shrike%Lone Star Tick%Long-Eared Owl%Long-Haired Rottweiler%Long-Tailed Tit%Long-Winged Kite Spider%Longfin Mako Shark%Longnose Gar%Lorikeet%Loris%Lowchen%Lumpfish%Luna Moth%Luna Moth Caterpillar%Lungfish%Lurcher%Lykoi Cat%Lynx%Lyrebird%Lystrosaurus%";
  2328. // M section
  2329. defaultRandomizerOptions.species["Animals"] += "Macaque%Macaroni Penguin%Macaw%MacGillivray’s Warbler%Machaeroides%Mackenzie Valley Wolf%Macrauchenia%Madagascar Hissing Cockroach%Madagascar Jacana%Madagascar Tree Boa%Madora Moth%Magellanic Penguin%Maggot%Magnolia Warbler%Magpie%Magyarosaurus%Mahi Mahi (Dolphin Fish)%Maiasaura%Maine Coon%Mal Shi%Malayan Civet%Malayan Krait%Malayan Tiger%Malchi%Mallard%Malteagle%Maltese%Maltese Mix%Maltese Shih Tzu%Maltipom%Maltipoo%Mamba%Mamushi Snake%Man of War Jellyfish%Manatee%Manchester Terrier%Mandarin Rat Snake%Mandrill%Maned Wolf%Mangrove Snake%Mangrove Snapper%Manta Ray%Mantella Frog%Marabou Stork%Marans Chicken%Marble Fox%Maremma Sheepdog%Margay%Marine Iguana%Marine Toad%Markhor%Marmoset%Marmot%Marsh Frog%Marsican Brown Bear%Masiakasaurus%Masked Angelfish%Masked Palm Civet%Mason Bee%Massasauga%Mastador%Mastiff%Mastiff Mix%Mauzer%May Beetle%Mayan Cichlid%Meagle%Mealworm Beetle%Mealybug%Meerkat%Megalania%Megalochelys%Megalodon%Megamouth Shark%Meganeura%Megatherium%Meiolania%Mekong Giant Catfish%Merganser%Mexican Alligator Lizard%Mexican Black Kingsnake%Mexican Eagle (Northern crested caracara)%Mexican Fireleg Tarantula%Mexican Free-Tailed Bat%Mexican Mole Lizard%Microraptor%Midget Faded Rattlesnake%Miki%Milk Snake%Milkfish%Milkweed aphids%Millipede%Mini Labradoodle%Mini Lop%Miniature Bull Terrier%Miniature Husky%Miniature Pinscher%Mink%Minke Whale%Mississippi Kite%Moccasin Snake%Mockingbird%Modern Game Chicken%Mojarra%Mojave Ball Python%Mojave Rattlesnake%Mola mola (Ocean Sunfish)%Mole%Mole Crab (Sand Flea)%Mole Cricket%Mole Snake%Mollusk%Molly%Monarch Butterfly%Mongoose%Mongrel%Monitor Lizard%Monkey%Monkfish%Monocled Cobra%Monte Iberia Eleuth%Moon Jellyfish%Moonglow Boa%Moorhen%Moose%Moray Eel%Morkie%Morpho Butterfly%Mosasaurus%Moscow Watchdog%Mosquito%Moth%Mountain Beaver%Mountain Bluebird%Mountain Cur%Mountain Feist%Mountain Gorilla%Mountain Lion%Mourning Dove%Mourning Gecko%Mourning Warbler%Mouse%Mouse Spider%Mouse-Deer (Chevrotain)%Mozambique Spitting Cobra%Mud Snake%Mudi%Mudpuppy%Mudskipper%Mule%Mule Deer%Mulga Snake%Mullet Fish%Muntjac%Muscovy Duck%Musk Deer%Muskellunge (Muskie)%Muskox%Muskrat%Mussurana Snake%Muttaburrasaurus%Muttaburrasaurus%Myna Bird%";
  2330. // N section
  2331. defaultRandomizerOptions.species["Animals"] += "Nabarlek%Naegleria%Naked Mole Rat%Narwhal%Natterjack%Nautilus%Neanderthal%Neapolitan Mastiff%Nebelung%Needlefish%Nelore Cattle%Nematode%Neon Tetra%Neptune Grouper%Netherland Dwarf Rabbit%New Hampshire Red Chicken%Newfoundland%Newfypoo%Newt%Nguni Cattle%Nicobar pigeon%Nigerian Goat%Night Adder%Night Heron%Night Snake%Nightingale%Nightjar%Nile Crocodile%Nile Monitor%Nile Perch%Nilgai%No See Ums%Norfolk Terrier%Norrbottenspets%North American Black Bear%Northern Alligator Lizard%Northern Bobwhite%Northern Cardinal%Northern Flicker%Northern Fur Seal%Northern Harrier%Northern Inuit Dog%Northern Jacana%Northern Parula%Northern Pintail%Northern Potoo%Northern Screamer%Northern Water Snake%Norway Rat%Norwegian Buhund%Norwegian Elkhound%Norwegian Forest%Norwegian Lundehund%Norwich Terrier%Nose-Horned Viper%Nova Scotia Duck Tolling Retriever%Nubian Goat%Nudibranch%Numbat%Nuralagus%Nurse Shark%Nut Weevil%Nuthatch%Nutria%Nyala%";
  2332. // O section
  2333. defaultRandomizerOptions.species["Animals"] += "Oak Toad%Oarfish%Ocean Perch%Ocean Pout%Ocean Whitefish%Oceanic Whitetip Shark%Ocellated Turkey%Ocelot%Octopus%Oenpelli python%Oilfish%Okapi%Old English Sheepdog%Old House Borer%Oleander Hawk Moth%Olingo%Olive Baboon%Olive python%Olive Sea Snake%Olm%Olympic Marmot%Onagadori Chicken%Onager%Opabinia%Opah%Opaleye (Rudderfish)%Opossum%Oranda Goldfish%Orange Baboon Tarantula%Orange Dream Ball Python%Orange Roughy %Orange Spider%Orange Tanager (Orange-Headed Tanager)%Orange-Crowned Warbler%Orangutan%Orb Weaver%Orchard Oriole%Orchid Dottyback%Oregon Spotted Frog%Ori-Pei%Oribi%Oriental Cockroach%Oriental Dwarf Kingfisher%Orinoco Crocodile%Ornate Bichir%Ornate Black-Tailed Rattlesnake%Ornate Box Turtle%Ornithocheirus%Ornithomimus%Ortolan Bunting%Oscar Fish%Osprey%Ostracod%Ostrich%Otter%Otterhound%Ovenbird%Oviraptor%Owl%Owl Butterfly%Owlfly (Ascalaphidae)%Ox%Oxpecker%Oyster%Oyster Toadfish%Ozark Bass%";
  2334. // P section
  2335. defaultRandomizerOptions.species["Animals"] += "Pachycephalosaurus%Pacific Coast Tick%Pacific Sleeper Shark%Pacific Spaghetti Eel%Paddlefish%Pademelon%Painted Bunting%Painted Turtle%Palaeophis%Paleoparadoxia%Palm Rat%Palo Verde Beetle%Panda Pied Ball Python%Pangolin%Panther%Panthera atrox (American Lion)%Papillon%Papillon Mix%Paradise Flying Snake%Parakeet%Parasaurolophus%Parrot%Parrot Snake%Parrotfish%Parrotlet%Parson Russell Terrier%Parti Schnauzer%Partridge%Patagonian Cavy%Patagonian Mara%Patagotitan%Patas Monkey%Patterdale Terrier%Pea Puffer%Peacock%Peacock Bass%Peacock Butterfly%Peacock Spider%Peagle%Peekapoo%Pekingese%Pelagornis%Pelagornithidae%Pelican%Pelycosaurs%Pembroke Welsh Corgi%Penguin%Pennsylvania Wood Cockroach%Peppered Moth%Peppermint Angelfish%Perch Fish%Père David’s Deer%Peregrine Falcon%Peringuey’s Adder%Perro De Presa Canario%Persian%Peruvian Guinea Pig%Peruvian Inca Orchid%Pesquet’s Parrot (Dracula Parrot)%Petit Basset Griffon Vendéen%Petite Goldendoodle%Pharaoh Hound%Pheasant%Pheasant-tailed Jacana%Philippine Cobra%Phoenix Chicken%Phorusrhacos%Phytosaurs%Picardy Spaniel%Pictus Catfish%Piebald Dachshund%Pied Ball Python%Pied Tamarin%Pied-Billed Grebe%Pig%Pig-Nosed Turtle%Pigeon%Pika%Pike Fish%Pileated Woodpecker%Pinacate Beetle%Pine Beetle%Pine Marten%Pine Siskin%Pine Snake%Pine Snake%Pinfish%Pink Bollworm%Pink Fairy Armadillo%Pink Salmon%Pink Toed Tarantula%Pink-Necked Green Pigeon%Pipe Snake%Pipefish%Piranha%Pit Bull%Pit Viper%Pitador%Pitsky%Plains Hognose Snake%Platinum Arowana%Platybelodon%Platypus%Plesiosaur%Pliosaur%Plott Hound Mix%Plott Hounds%Plymouth Rock Chicken%Pocket Beagle%Pocket Pitbull%Podenco Canario%Pointer%Pointer Mix%Poison Dart Frog%Polacanthus%Polar Bear%Polecat%Polish Chicken%Polish Lowland Sheepdog%Polish Tatra Sheepdog%Polka Dot Stingray%Pollock Fish%Polyphemus Moth%Pomapoo%Pomchi%Pomeagle%Pomeranian%Pomeranian Mix%Pompano Fish%Pomsky%Pond Skater%Poochon%Poodle%Poogle%Pool Frog%Porbeagle Shark%Porcupine%Porcupinefish%Portuguese Podengo%Possum%Potato Beetle%Potoo%Potoroo%Powderpost Beetle%Prairie Chicken%Prairie Dog%Prairie Rattlesnake%Prawn%Praying Mantis%Proboscis Monkey%Procoptodon%Pronghorn%Psittacosaurus%Psittacosaurus%Pteranodon%Pterodactyl%Pudelpointer%Puertasaurus%Puff Adder%Pufferfish%Puffin%Pug%Pug Mix%Pugapoo%Puggle%Pugshire%Puli%Puma%Pumi%Pumpkin Patch Tarantula%Purple Emperor Butterfly%Purple Finch%Purple Gallinule%Purple Tarantula%Purussaurus%Puss Caterpillar%Puss Moth%Pygmy Hippopotamus%Pygmy Marmoset (Finger Monkey)%Pygmy python%Pygmy Rattlesnake%Pygmy Shark%Pygora Goat%Pyjama Shark%Pyrador%Pyredoodle%Pyrenean Mastiff%Pyrenean Shepherd%Pyrosome%Python%";
  2336. // Q section
  2337. defaultRandomizerOptions.species["Animals"] += "Quagga%Quahog Clam%Quail%Queen Snake%Quetzal%Quetzalcoatlus northropi%Quokka%Quoll%";
  2338. // R section
  2339. defaultRandomizerOptions.species["Animals"] += "Rabbit%Raccoon%Raccoon Dog%Racer Snake%Radiated Tortoise%Ragamuffin%Ragdoll%Raggle%Rainbow Boa%Rainbow Grasshopper (Dactylotum bicolor)%Rainbow Kribs (Kribensis)%Rainbow Shark%Rat%Rat Snakes%Rat Terrier%Rattlesnake%Red Ackie Monitor%Red Aphids%Red Deer%Red Diamondback Rattlesnake%Red Drum Fish%Red Finch%Red Fox%Red Kite%Red Knee Tarantula%Red Nose Pit Bull%Red Panda%Red Paper Wasp%Red Racer Snake%Red Spitting Cobra%Red Squirrel%Red Star Chicken%Red Tail Boa (common boa)%Red Wolf%Red-Bellied Black Snake%Red-Bellied Woodpecker%Red-Billed Quelea Bird%Red-Eared Slider%Red-Eyed Tree Frog%Red-Footed Tortoise%Red-handed Tamarin%Red-Headed Vulture%Red-Lipped Batfish%Red-Shouldered Hawk%Red-Tailed Cuckoo Bumblebee%Red-winged blackbird%Redback Spider%Redbone Coonhound%Redcap Chicken%Redear Sunfish%Redhump Eartheater%Redstart%Redtail Catfish%Reef Shark%Regal Jumping Spider%Reindeer%Repenomamus%Reticulated python%Rex Rabbit%Rhamphosuchus%Rhea%Rhesus Macaque%Rhino Beetle%Rhino Viper%Rhinoceros%Rhode Island Red Chicken%Rhodesian Ridgeback%Rhombic Egg-Eater Snake%Ribbon Eel%Ribbon Snake%Rim Rock Crowned Snake%Ring-billed Gull%Ringed Kingfisher%Rinkhals Snake%River Otter%River Turtle%Roadrunner%Robber Flies%Robin%Rock Bass%Rock Crab%Rock Hyrax%Rock Python%Rockfish%Rockhopper Penguin%Rodents%Roe Deer%Roosevelt Elk%Rooster%Root Aphids%Rose-Breasted Grosbeak%Roseate Spoonbill%Rosy Boa%Rotterman%Rottle%Rottsky%Rottweiler%Rottweiler Mix%Rough Earth Snake%Rough Green Snake%Rough-Legged Hawk (Rough-Legged Buzzard)%Rove Beetle%Royal Penguin%Rubber Boa%Ruby-Crowned Kinglet%Ruby-Throated Hummingbird%Ruddy Duck%Ruddy Turnstone%Rufous Hummingbird%Russel’s Viper%Russell Terrier%Russian Bear Dog%Russian Blue%Russian Tortoise%";
  2340. // S section
  2341. defaultRandomizerOptions.species["Animals"] += "Saanen Goat%Saarloos Wolfdog%Saber-Toothed Tiger%Sable%Sable Black German Shepherd%Sable Ferret%Sable German Shepherd%Saddleback Caterpillar%Saiga%Sailfish%Saint Berdoodle%Saint Bernard%Saint Shepherd%Salamander%Salmon%Salmon Shark%Saluki%Sambar%Samoyed%San Francisco Garter Snake%Sand Cat%Sand Crab%Sand Dollar%Sand Lizard%Sand Tiger Shark%Sand Viper%Sandhill Crane%Sandpiper%Sandworm%Saola%Sapsali%Sarcosuchus%Sardines%Sarkastodon%Sarplaninac%Sarus Crane%Satanic Leaf-Tailed Gecko%Saturniidae Moth%Sauropoda%Sauropoda%Savanna Goat%Savannah Monitor%Savannah Sparrow%Savu Python%Saw-scaled Viper%Sawfish%Scale-Crested Pygmy Tyrant%Scaleless Ball Python%Scallops%Scarab Beetle%Scarlet Kingsnake%Scarlet Macaw%Scarlet Tanager%Schapendoes%Schipperke%Schneagle%Schnoodle%Scimitar-horned Oryx%Scissor-tailed Flycatcher%Scorpion%Scorpion Fish%Scotch Collie%Scottish Deerhound%Scottish Fold Cat%Scottish Terrier%Scrotum Frog%Sculpin%Scutosaurus%Sea Anemone%Sea Bass%Sea Dragon%Sea Eagle%Sea Lion%Sea Otter%Sea Roach%Sea Slug%Sea Snake%Sea Spider%Sea Squirt%Sea Trout%Sea Turtle%Sea Urchin%Seagull%Seahorse%Seal%Sealyham Terrier%Sedge Warbler%Sehuencas Water Frog%Sei Whale%Senegal Parrot%Senepol Cattle%Sequined Spider%Serval%Seymouria%Shantungosaurus%Shark%Sharp-Shinned Hawk%Sharp-Tailed Snake%Shastasaurus%Sheep%Sheepadoodle%Sheepshead Fish%Shepadoodle%Shepkita%Shepweiler%Shetland Sheepdog%Shiba Inu%Shiba Inu Mix%Shichi%Shih Poo%Shih Tzu%Shih Tzu Mix%Shikoku%Shiloh Shepherd%Shiranian%Shoebill Stork%Shollie%Short-Eared Owl%Short-Faced Bear%Shortfin Mako Shark%Shrew%Shrimp%Siamese%Siberian%Siberian Husky%Siberian Ibex%Siberian Retriever%Siberian Tiger%Siberpoo%Sichuan Takin (Tibetan Takin)%Sidewinder%Sika Deer%Silken Windhound%Silkie Chicken%Silky Shark%Silky Terrier%Silver Dollar%Silver Labrador%Simbakubwa%Sinosauropteryx%Sivatherium%Sixgill shark%Skate Fish%Skeleton Tarantula%Skink Lizard%Skipjack Tuna%Skua%Skunk%Skye Terrier%Sleeper Shark%Sloth%Slovak Cuvac%Slow Worm%Slug%Smallmouth Bass%Smilosuchus%Smokybrown Cockroach%Smooth Earth Snake%Smooth Fox Terrier%Smooth Green Snake%Smooth Hammerhead Shark%Smooth Snake%Snail%Snailfish%Snake%Snapping Turtle%Snook Fish%Snorkie%Snouted Cobra%Snow Bunting%Snow Crab%Snow Goose%Snow Leopard%Snowberry Clearwing Moth%Snowflake Eel%Snowshoe%Snowshoe Hare%Snowy Owl%Sockeye Salmon%Soldier Beetle%Somali%Song Sparrow%Song Thrush%South China Tiger%Southeastern Blueberry Bee%Southern Black Racer%Southern Flannel Moth%Southern Hognose Snake%Southern House Spider%Southern Pacific Rattlesnake%Spadefoot Toad%Spalax%Spanador%Spanish Goat%Spanish Mackerel%Spanish Mastiff%Spanish Water Dog%Sparrow%Sparrowhawk%Speckled Kingsnake%Speckled Trout%Spectacled Bear%Sperm Whale%Sphynx%Spider%Spider Ball Python%Spider Beetle%Spider Monkey%Spider Wasp%Spider-Tailed Horned Viper%Spinner Shark%Spinone Italiano%Spinosaurus%Spiny bush viper%Spiny Dogfish%Spiny Hill Turtle%Spitting Cobra%Spixs Macaw%Sponge%Spongy Moth%Spongy Moth%Spotted Bass%Spotted Gar%Spotted Garden Eel%Spotted Lanternfly%Spotted python%Spotted Skunk%Springador%Springbok%Springerdoodle%Squash Bee%Squash Beetle%Squid%Squirrel%Squirrel Monkey%Squirrelfish%Sri Lankan Elephant%Stabyhoun%Staffordshire Bull Terrier%Stag Beetle%Standard Schnauzer%Star-nosed mole%Starfish%Stargazer Fish%Steelhead Salmon%Steller’s Sea Cow%Stick Insect%Stiletto Snake%Stingray%Stoat%Stone Crab%Stonechat%Stonefish%Stoplight Loosejaw%Stork%Strawberry Hermit Crab%Striped Bass%Striped Hyena%Striped Rocket Frog%Stromatolite%Stupendemys%Sturgeon%Styracosaurus%Suchomimus%Suckerfish%Sugar Glider%Sulcata Tortoise%Sultan Chicken%Sumatran Elephant%Sumatran Orangutan%Sumatran Rhinoceros%Sumatran Tiger%Summer Tanager%Sun Bear%Sunbeam Snake%Sunset Ball Python%Super Pastel Ball Python%Supersaurus%Superworm%Surgeonfish%Sussex Chicken%Swai Fish%Swainson’s Hawk%Swallow%Swallowtail Butterfly%Swallowtail Caterpillar%Swan%Swedish Elkhound%Swedish Lapphund%Swedish Vallhund%Swordfish%Syrian Hamster%";
  2342. // T section
  2343. defaultRandomizerOptions.species["Animals"] += "Taco Terrier%Tailless Whip Scorpion%Taimen Fish%Taipan%Takin%Tamarin%Tamaskan%Tang%Tangerine Leopard Gecko%Tapanuli Orangutan%Tapir%Tarantula%Tarantula Hawk%Tarbosaurus%Tarpon%Tarsier%Tasmanian Devil%Tasmanian Tiger%Tasmanian Tiger Snake%Tawny Frogmouth%Tawny Mining Bee%Tawny Owl%Teacup Chihuahua%Teacup Maltese%Teacup Miniature Horse%Teacup Poodle%Teddy Bear Hamster%Teddy Guinea Pig%Teddy Roosevelt Terrier%Telescope Fish%Ten-Lined June Beetle%Tennessee Walking Horse%Tenrec%Tent Caterpillar%Tentacled Snake%Tenterfield Terrier%Termite%Terrier%Terror Bird%Tetra%Texas Blind Snake%Texas Brown Tarantula%Texas Coral Snake%Texas Garter Snake%Texas Heeler%Texas Indigo Snake%Texas Night Snake%Texas Rat Snake%Texas Spiny Lizard%Thai Ridgeback%Thalassomedon%Thanatosdrakon%Therizinosaurus%Theropod%Thornback Ray%Thorny Devil%Thresher Shark%Thrush%Thylacoleo%Thylacoleo carnifex%Thylacosmilus%Tibetan Fox%Tibetan Mastiff%Tibetan Spaniel%Tibetan Terrier%Tick%Tiffany%Tiger%Tiger Beetle%Tiger Moth%Tiger Muskellunge (Muskie)%Tiger Rattlesnake%Tiger Salamander%Tiger Shark%Tiger snake%Tiger Swallowtail%Tiger Swallowtail Caterpillar%Tiger Trout%Tiktaalik%Timber Rattlesnake (Canebrake Rattlesnake)%Timor python%Tire Track Eel%Titan Beetle%Titanoboa%Titanosaur%Toadfish%Tokay Gecko%Tomato Hornworm%Torkie%Tornjak%Tortoise%Tosa%Toucan%Towhee%Toxodon%Toy Fox Terrier%Toy Poodle%Transylvanian Hound%Trapdoor spider%Tree Cricket%Tree Frog%Tree Kangaroo%Tree Snake%Tree swallow%Tree Viper (Bamboo Viper)%Treecreeper%Treehopper%Treeing Tennessee Brindle%Treeing Walker Coonhound%Triggerfish%Troodon%Tropicbird%Trout%Tsetse Fly%Tuatara%Tufted Coquette%Tufted Titmouse%Tully Monster%Tuna%Tundra Swan%Turaco%Turkey%Turkey Vulture%Turkish Angora%Turnspit%Turtle Frog%Turtles%Tusoteuthis%Tussock Moth Caterpillar%Twig Snake%Tylosaurus%Tyrannosaurus Rex%";
  2344. // U section
  2345. defaultRandomizerOptions.species["Animals"] +=
  2346. "Uakari%Uaru Cichlid%Uguisu%Uinta Ground Squirrel%Uintatherium%Ulysses Butterfly%Umbrellabird%Unau (Linnaeus’s Two-Toed Sloth)%Underwing Moth%Upland Sandpiper%Ural owl%Urechis unicinctus (Penis Fish)%Urial%Uromastyx (Spiny-Tailed Lizard)%Urutu Snake%Utonagan%";
  2347. // V section
  2348. defaultRandomizerOptions.species["Animals"] +=
  2349. "Valley Bulldog%Vampire Bat%Vampire Crab %Vampire Squid%Vaquita%Veery%Vegavis%Velociraptor%Venus Flytrap%Vermilion Flycatcher%Vervet Monkey%Vestal Cuckoo Bumblebee%Vicuña%Vine Snake%Vinegaroon%Viper%Viper Boa%Viper Shark (dogfish)%Viperfish%Virgin Islands Dwarf Gecko%Vizsla%Volcano Snail%Vole%Volpino Italiano%Vulture%";
  2350. // W section
  2351. defaultRandomizerOptions.species["Animals"] +=
  2352. "Wahoo Fish%Waimanu%Walking Catfish%Wallaby%Walleye Fish%Walrus%Wandering Albatross%Warbler%Warthog%Wasp%Water Beetle%Water Buffalo%Water Bug%Water Dragon%Water Vole%Waterbuck%Wattled Jacana%Wax Moth%Weasel%Weaver Bird%Weimaraner%Weimardoodle%Wels Catfish%Welsh Black Cattle%Welsh Corgi%Welsh Springer Spaniel%Welsh Terrier%West Highland Terrier%West Siberian Laika%Western Blacklegged Tick%Western Blind Snake%Western Diamondback Rattlesnake%Western Gorilla%Western Green Mamba%Western Hognose Snake%Western Kingbird%Western Lowland Gorilla%Western Rat Snake%Western Rattlesnake (Northern Pacific Rattlesnake)%Western Tanager%Westiepoo%Whale Shark%Wheaten Terrier%Whimbrel%Whinchat%Whippet%Whiptail Lizard%White Butterfly%White Catfish%White Crappie%White Ferret / Albino Ferrets%White German Shepherd%White Marlin%White Rhinoceros%White Shark%White Sturgeon %White Tiger%White-Crowned Sparrow%White-Eyed Vireo%White-Faced Capuchin%White-tail deer%White-Tailed Eagle%Whitetail Deer%Whiting%Whoodle%Whooping Crane%Wild Boar%Wildebeest%Willow Flycatcher%Willow Warbler%Winter Moth%Wire Fox Terrier%Wirehaired Pointing Griffon%Wirehaired Vizsla%Wiwaxia%Wolf%Wolf Eel%Wolf Snake%Wolf Spider%Wolffish%Wolverine%Woma Python%Wombat%Wood Bison%Wood Duck%Wood Frog%Wood Tick%Wood Turtle%Woodlouse%Woodlouse Spider%Woodpecker%Woodrat%Wool Carder Bee%Woolly Aphids%Woolly Bear Caterpillar%Woolly Mammoth%Woolly Monkey%Woolly Rhinoceros%Worm%Worm Snake%Wrasse%Writing Spider%Wrought Iron Butterflyfish%Wryneck%Wyandotte Chicken%Wyoming Toad%";
  2353. // X section
  2354. defaultRandomizerOptions.species["Animals"] +=
  2355. "X-Ray Tetra%Xeme (Sabine’s Gull)%Xenacanthus%Xenoceratops%Xenoposeidon%Xenotarsosaurus%Xerus%Xiaosaurus%Xiaotingia%Xingu River Ray%Xiphactinus%Xoloitzcuintli%"
  2356. // Y section
  2357. defaultRandomizerOptions.species["Animals"] +=
  2358. "Yabby%Yak%Yakutian Laika%Yarara%Yellow Aphids%Yellow Bass%Yellow Bellied Sapsucker%Yellow Belly Ball Python%Yellow Cobra%Yellow Crazy Ant%Yellow Perch%Yellow Sac Spider%Yellow Spotted Lizard%Yellow Tanager (Black-and-Yellow Tanager)%Yellow Tang%Yellow-Bellied Sea Snake%Yellow-Eyed Penguin%Yellow-faced Bee%Yellowhammer%Yellowish Cuckoo Bumblebee (formerly Fernald’s Cuckoo Bumblebee)%Yellowjacket (Yellow Jacket)%Yellowtail Snapper%Yellowthroat%Yeti Crab%Yokohama Chicken%Yoranian%Yorkie Bichon%Yorkiepoo%Yorkshire Terrier%";
  2359. // Z section
  2360. defaultRandomizerOptions.species["Animals"] +=
  2361. "Zebra%Zebra Finch%Zebra Mussels%Zebra Pleco%Zebra Shark%Zebra Snake%Zebra Spitting Cobra%Zebra Tarantula%Zebrafish (Zebra Fish)%Zebu%Zokor%Zonkey%Zorse%Zuchon";
  2362.  
  2363. // Also the Mythological ones
  2364. // Section 1
  2365. defaultRandomizerOptions.species["Mythological"] += "Bahamut% Bake-kujira% Cetus% Devil Whale% Encantado% Glashtyn% Gveleshapi% Makara% Mug-wamp% Sea goat% Selkie% Water bull% Water Horse% Ceffyl Dŵr% Each-uisge% Enbarr% Hippocampus% Ichthyocentaurs% Kelpie% Morvarc'h% Nixie% Nuckelavee% Nuggle% Tangie% Anansi% Arachne% Carbuncle% Gold-digging ant% Iktomi% Jorōgumo% Karkinos% Khepri% Mothman% Myrmecoleon% Myrmidons% Pabilsag% Scorpion man% Selket% Tsuchigumo% Balayang% Camazotz% Leutogi% Minyades% Tjinimin% Vetala% Ababil% Adarna% Avalerion% Alicanto% Anqa% Anzû% Bare-fronted Hoodwink% Alkonost% Gumyōchō% Harpy% Aello% Ocypete% Celaeno% Podarge% Horus% Inmyeonjo% Kalavinka% Karura% Kinnara% Siren% Achelois% Aglaonoe% Agalaope% Leucosia% Ligeia% Parthenope% Pisinoe% Thelxinoë% Swan maiden% Caladrius% Chalkydri% Chamrosh% Cinnamon bird% Devil Bird% Gagana% Gandabherunda% Gamayun% Garuda% Hakawai% Hudhud% Huginn% Muninn% Itsumade% Jingwei% Lamassu% Luan% Minokawa% Nachtkrapp% Nine-headed Bird% Oozlum bird% Pamola% Paskunji% Peng% Phoenix% Bennu% Chol% Firebird% Fenghuang% Huma bird% Konrul% Toghrul% Vermilion Bird% Piasa% Qingniao% Ra% Rain Bird% Raróg% Roc% Shangyang% Shedu% Simurgh% Stymphalian birds% Tengu% Three-legged bird% Thunderbird% Thoth% Turul% Veðrfölnir% Vucub Caquix% Yatagarasu% Zhenniao% Alectryon% Basan% Cockatrice% Gallic rooster% Gullinkambi% Rooster of Barcelos% Sarimanok% Víðópnir% Aethon% Griffin% Hippogriff% Hræsvelgr% Poukai% Shahbaz% Triple-headed eagle% Wuchowsen% Ziz% Zu% Nyctimene% Owlman% Sirin% Strix% Bjarndyrakongur% Bugbear% Callisto% Stiff-Legged Bear% Adlet% Amarok% Anubis% Aralez% Asena% Axehandle hound% Black dog% Barghest% Black Shuck% Grim% Beast of Gévaudan% Cerberus% Chupacabra% Cu Sith% Crocotta% Cynocephaly% Dogs of Actaeon% Fenrir% Gelert% Hellhound% Huli jing% Kitsune% Kumiho% Huodou% Kludde% Orthrus% Penghou% Psoglav% Salawa% Sigbin% Sky Fox% Shug Monkey% Tanuki% Tulikettu% Vǎrkolak% Werewolf% Bael% Ball-tailed cat% Cactus cat% Cat-sìth% Cath Palug% Demon Cat% Kaibyō% Bakeneko% Kasha% Nekomata% Nue% Pard% Phantom cat% Blue Mountains panther% Glawackus% Tyger% Underwater panther% Vapula% Wampus cat% White Tiger% Winged cat% Arimanius% Ammit% Barong% Beast of the First Kingdom% Brunswick Lion% Chimera% Chinese guardian lions% Komainu% Shisa% Sin-you% Xiezhi% Dawon% Aker% Ȧmi-Pe% Apedemak% Bast% Hert-ketit-s% Ḥuntheth% Ipy% Maahes% Matit% Mehit% Menhit% Pakhet% Repyt% Sekhmet% Seret% Shesmetet% Taweret% Tefnut% Tutu% Urit-en-kru% Lampago% Leo% Lion of Cithaeron% Nemean lion% Lion of Al-lāt% Manticore% Manussiha% Merlion% Narasimha% Nian% Nongshāba% Pixiu% Questing Beast% Sea-lion% Serpopard% Sharabha% Simhamukha% Snow Lion% Sphinx% Criosphinx% Gopaitioshah% Hieracosphinx% Stratford Lyon% Tigris% Vaikuntha Chaturmurti% Winged lion% Yali% Yaghūth% Yaldabaoth% Werehyena% Kishi% Azeban% Gef% Ichneumon% Kamaitachi%";
  2366.  
  2367. // Section 2
  2368. defaultRandomizerOptions.species["Mythological"] += "Kushtaka% Mujina% Ramidreju% Raijū% Mermaid% Merman% Abaia% Gurangatch% Hippocamp% Ika-Roa% Il Belliegha% Isonade% Namazu% Ningyo% Kun% Salmon of Wisdom% Shachihoko% Lavellan% Drop bear% Bunyip% Akkorokamui% Lou Carcolh% Kraken% Shen% Agropelter% Bigfoot% Sasquatch% Hibagon% Jué yuán% Satori% Shōjō% Shug Monkey% Sun Wukong% Vanara% Yeren% Yeti% Yowie% Al-Mi'raj% Jackalope% Moon rabbit% Skvader% Wisakedjak% Wolpertinger% Agoa% Ammut% Bakunawa% Basilisk% Black Tortoise% Chalkydri% Chinese Dragon% Cipactli% Dragon% Dungavenhooter% Knucker% Kurma% Loch Ness Monster% Loveland frog% Mokele Mbembe% Moʻo% Morgawr% Mungoon-Gali% Peluda% Reptilian humanoids% Sewer alligator% Sobek% Taniwha% Whowie% Wyvern% Zaratan% Alicante% Amphisbaena% Amphithere% Apep% Apophis% Azhi Dahaka% Bakonawa% Biscione% Cockatrice% Drake% Echidna% Fáfnir% Feathered serpent% Garafena% Gorgon% Hoop snake% Hydra% Jaculus% Jasconius% Jörmungandr% Lamia% Lindworm% Madame White Snake% Meretseger% Mongolian Death Worm% Naga% Níðhöggr% Orm% Ouroboros% Python% Rainbow serpent% Sea serpent% Tarasque% Typhon% Ur% Yamata no Orochi% Zilant% Afanc% Ratatoskr% Rat king% Actaeon% Ceryneian Hind% Deer Woman% Eikþyrnir% Goldhorn% Jackalope% Keresh% Peryton% Qilin% Tarand% White stag% Xeglun% Auðumbla% Bai Ze% Kujata% Bicorn% Chichevache% Bonnacon% Hodag% Minotaur% Nandi% Sarangay% Shedu% Ushi-oni% Allocamelus% Heavenly Llama% Amalthea% Aries% Barometz% Capricornus% Chimera% Dahu% Faun% Goldhorn% Heiðrún% Khnum% Satyr% Sidehill gouger% Tanngrisnir% Tanngnjóstr% Chrysomallos% Anggitay% Arion% Balius% Xanthus% Buraq% Centaur% Cheval Gauvin% Cheval Mallet% Chiron% Chollima% Drapé% Gytrash% Haizum% Hippogriff% Ipotane% Karkadann% Kotobuki% Longma% Onocentaur% Pegasus% Pooka% Sleipnir% Simurgh% Sihuanaba% Tikbalang% Uchchaihshravas% Unicorn% Winged unicorn% Abath% Baku% Behemoth% Quugaarpak% Taweret% Calydonian Boar% Erymanthian Boar% Zhu Bajie% Mapinguari% Giants% Automaton% Blodeuwedd% Brazen head% Doll Woman% Frankenstein's monster% Galatea% Gingerbread man% Golem% Homunculus% Nephele% Shabti% Tokeloshe% Tilberi% Tsukumogami% Tulpa% Tupilaq% Ushabti% Alan% Dhampir% Preta% Golden Hind% Kappa% Kekkai% Lamia% Manananggal% Mandurugo% Redcap% Rokurokubi% Sigbin% Vampire% Werewolf% Yuki-onna% Bloody Bones% Gashadokuro% Grim Reaper% Skeleton% Argus Panoptes% Catoblepas% Cyclopes% Hitotsume-kozou% Lynx% Mokumokuren% Asura% Deva% Devi% Noppera-bō% Futakuchi-onna% Harionago% Medusa% Amphisbaena% Chimera% Chonchon% Double-headed eagle% Dullahan% Hekatonkheires% Hydra% Lernaean Hydra% Nine-headed Bird% Nukekubi% Orthrus% Shesha% Penanggalan% Wanyūdō% Xing Tian% Yacuruna% Yamata no Orochi% Asura% Deva% Devi% Hekatonkheires% Hinkypunk% Kui% O'nya:ten% Sleipnir% Three-legged bird% Futakuchi-onna% Kuchisake-onna% Selkie% Skin-walker% Swan maiden% Bakeneko% Kitsune% Kumiho% Hulder% Nguruvilu% Serpopard% Rokurokubi% Vampire% Manananggal% Geryon% Pixiu% Androktasiai% Erinyes% Hipag% Hysminai% Keres% Lemures% Makhai% Onryō% Phonoi% Valkyrie% Vengeful ghost% Phoenix% Ubume% Ammit% Banshee% Demon% Devil% Dullahan% Ghost% Grim Reaper% Ox-Head% Horse-Face% Phoenix% Undead% Valkyrie% Vampire% Alp% Baku% Carbuncle% Devil% Drude% Incubus% Succubus% Mermaid% Nightmare% Nue% Oni% Sandman% Satori% Zduhać% Balor% Catoblepas% Gorgon% Medusa% Euryale% Stheno% Abatwa% Alan% Boto% Faun% Gancanagh% Incubus% Succubus% Maenad% Nymph% Pombero% Popobawa% Satyr% Sileni% Silenus% Unicorn% Zemyna% Zemepatis% Simurgh% Dwarf% Egbere% Genie% Leib-Olmai%"
  2369.  
  2370. // Section 3
  2371. defaultRandomizerOptions.species["Mythological"] += "Leprechaun% Matagot% Pixiu% Sarimanok% Sigbin% Yaksha% Yakshini% Angel% Chalkydri% Deity% Lampetia% Will-o'-the-wisp% Dragon% Cupid% Eros% Cherub% Gancanagh% Madame White Snake% Melusine% Tennin% Undine% Banshee% Encantado% Fenghuang% Fossegrim% Mermaid% Nue% Siren% Gef% Satan% Chronos% Father Time% Gremlin% Baba Yaga% Bai Ze% Griffin% Salmon of Wisdom% Sphinx% Valravn% Ala% Feldgeister% Zduhać% Black dog% Bogeyman% Ghost% Grim Reaper% Wechuge% Wendigo% Shadow People% Vampire% Werewolf% Oni% Gashadokuro% Camazotz% Wild Hunt% Hell Hound% Asag% Bluecap% Elemental% Dwarf% Earth Dragon% Gargoyle% Giant% Gnome% Goblin% Golem% Knocker% Monopod% Nymph% Ogre% Oread% Ten Ten-Vilu% Troll% Basan% Bluecap% Cherufe% Chimera% Dragon% Ifrit% Hellhound% Lampad% Phoenix% Salamander% Chalkydri% Light Elf% Rainbow crow% Rainbow Serpent% Alicanto% Pixiu% Carbunclo% Chrysomallos% Cyclopes% Griffin% Gnome% Leprechaun% Bulgasari% Chinese dragon% Cyclopes% Feldgeister% Kitsune% Raijū% Thunderbird% Valkyrie% Afanc% Amefurikozō% Aspidochelone% Bloody Bones% Buggane% Bunyip% Camenae% Capricorn% Cetus% Charybdis% Cai Cai-Vilu% Crinaeae% Davy Jones' Locker% Draug% Each uisge% Fish People% Fossegrim% Fur-bearing trout% Gargouille% Grindylow% Haetae% Hippocamp% Hydra% Ichthyocentaur% Jasconius% Jengu% Kappa% Kelpie% Kraken% Lake monster% Lavellan% Leviathan% Loch Ness monster% Lorelei% Lusca% Makara% Melusine% Mami Wata% Mermaid% Merman% Merrow% Morgens% Muc-sheilch% Naiad% Näkki% Nereid% Nix% Nymph% Pisces% Ponaturi% Potamus% Rusalka% Samebito% Sea monster% Sea serpent% Selkie% Shen% Siren% Taniwha% Tiamat% Triton% Ondine% Vodyanoy% Water dragon% Water leaper% Water sprite% Yacuruna% Zaratan% Dwarf% European dragon% Gnome% Goblin% Golem% Grootslang% Leprechaun% Troll% Yaoguai% Angel% Asteriae% Chalkydri% Feathered serpent% Heavenly Llama% Pegasus% Grim Reaper% Swan Maiden% Tennin% Three-legged bird% Valkyrie% Amphisbaena% Basilisk% Cockatrice% Ghoul% Mongolian Death Worm% Sphinx% Ajatar% Bigfoot% Dryad% Elf% Green Man% Irrwurz% Leshy% Lindworm% Mavka% Moss people% Owlman% Satyr% Unicorn% Curupira% Dingonek% Mapinguari% Manticore% Saci% Fairy% Gnome% Ennedi tiger% Werehyena% Bak% Chinese dragon% Dobhar-chú% Encantado% Grootslang% Iara% Jiaolong% Kappa% Kelpie% Lake monster% Hydra% Loch Ness Monster% Mugwump% Naiad% Nixie% Ogopogo% Ondine% Rainbow serpent% Rusalka% Ryujin% Shellycoat% Warlock% Yacuruna% Alp% Dwarf% Fenghuang% Griffin% Hippogriff% Mountain Giant% Kamaitachi% Mavka% Oread% Patupaiarehe% Rübezahl% Satyr% Tengu% Yeti% Aspidochelone% Bishop-fish% Charybdis% Cai Cai-Vilu% Dragon King% Fish People% Hippocamp% Leviathan% Jormungand% Kraken% Mermaid% Merman% Nereid% Sea monk% Sea monster% Sea serpent% Selkie% Shen% Siren% Tritons% Umibōzu% Water Dragon% Yacuruna% Błudnik% Bunyip% Grootslang% Lernaean Hydra% Honey Island Swamp monster% Mokele-mbembe% Swamp monster% Will-o'-the-wisp% Cherufe% Phoenix% Salamander% Akhlut% Amarok% Hrimthurs% Ijiraq% Jotun% Qiqirn% Saumen Kar% Tizheruk% Wechuge% Wendigo% Yeti% Ymir% Yuki-onna% Banshee% Boggart% Brownie% Domovoi% Dvorovoi% Duende% Jinn% Kobold% Tomte% Vampire% Zashiki-warashi% Cerberus% Ammit% Cyclopes% Demon% Devil% Earth Dragon% Garm% Hekatonkheires% Hellhound% Ifrit% Ox-Head% Horse-Face% Preta%"
  2372.  
  2373. // Same thing had to happen with pokemon
  2374. // Section 1
  2375. defaultRandomizerOptions.species["Pokemon"] += "Bulbasaur%Ivysaur%Venusaur%Charmander%Charmeleon%Charizard%Squirtle%Wartortle%Blastoise%Caterpie%Metapod%Butterfree%Weedle%Kakuna%Beedrill%Pidgey%Pidgeotto%Pidgeot%Rattata%Raticate%Spearow%Fearow%Ekans%Arbok%Pikachu%Raichu%Sandshrew%Sandslash%Nidoran♀%Nidorina%Nidoqueen%Nidoran♂%Nidorino%Nidoking%Clefairy%Clefable%Vulpix%Ninetales%Jigglypuff%Wigglytuff%Zubat%Golbat%Oddish%Gloom%Vileplume%Paras%Parasect%Venonat%Venomoth%Diglett%Dugtrio%Meowth%Persian%Psyduck%Golduck%Mankey%Primeape%Growlithe%Arcanine%Poliwag%Poliwhirl%Poliwrath%Abra%Kadabra%Alakazam%Machop%Machoke%Machamp%Bellsprout%Weepinbell%Victreebel%Tentacool%Tentacruel%Geodude%Graveler%Golem%Ponyta%Rapidash%Slowpoke%Slowbro%Magnemite%Magneton%Farfetch'd%Doduo%Dodrio%Seel%Dewgong%Grimer%Muk%Shellder%Cloyster%Gastly%Haunter%Gengar%Onix%Drowzee%Hypno%Krabby%Kingler%Voltorb%Electrode%Exeggcute%Exeggutor%Cubone%Marowak%Hitmonlee%Hitmonchan%Lickitung%Koffing%Weezing%Rhyhorn%Rhydon%Chansey%Tangela%Kangaskhan%Horsea%Seadra%Goldeen%Seaking%Staryu%Starmie%Mr. Mime%Scyther%Jynx%Electabuzz%Magmar%Pinsir%Tauros%Magikarp%Gyarados%Lapras%Ditto%Eevee%Vaporeon%Jolteon%Flareon%Porygon%Omanyte%Omastar%Kabuto%Kabutops%Aerodactyl%Snorlax%Articuno%Zapdos%Moltres%Dratini%Dragonair%Dragonite%Mewtwo%Mew%";
  2376. // Section 2
  2377. defaultRandomizerOptions.species["Pokemon"] += "Chikorita%Bayleef%Meganium%Cyndaquil%Quilava%Typhlosion%Totodile%Croconaw%Feraligatr%Sentret%Furret%Hoothoot%Noctowl%Ledyba%Ledian%Spinarak%Ariados%Crobat%Chinchou%Lanturn%Pichu%Cleffa%Igglybuff%Togepi%Togetic%Natu%Xatu%Mareep%Flaaffy%Ampharos%Bellossom%Marill%Azumarill%Sudowoodo%Politoed%Hoppip%Skiploom%Jumpluff%Aipom%Sunkern%Sunflora%Yanma%Wooper%Quagsire%Espeon%Umbreon%Murkrow%Slowking%Misdreavus%Unown%Wobbuffet%Girafarig%Pineco%Forretress%Dunsparce%Gligar%Steelix%Snubbull%Granbull%Qwilfish%Scizor%Shuckle%Heracross%Sneasel%Teddiursa%Ursaring%Slugma%Magcargo%Swinub%Piloswine%Corsola%Remoraid%Octillery%Delibird%Mantine%Skarmory%Houndour%Houndoom%Kingdra%Phanpy%Donphan%Porygon2%Stantler%Smeargle%Tyrogue%Hitmontop%Smoochum%Elekid%Magby%Miltank%Blissey%Raikou%Entei%Suicune%Larvitar%Pupitar%Tyranitar%Lugia%Ho-oh%Celebi%";
  2378. // Section 3
  2379. defaultRandomizerOptions.species["Pokemon"] += "Treecko%Grovyle%Sceptile%Torchic%Combusken%Blaziken%Mudkip%Marshtomp%Swampert%Poochyena%Mightyena%Zigzagoon%Linoone%Wurmple%Silcoon%Beautifly%Cascoon%Dustox%Lotad%Lombre%Ludicolo%Seedot%Nuzleaf%Shiftry%Taillow%Swellow%Wingull%Pelipper%Ralts%Kirlia%Gardevoir%Surskit%Masquerain%Shroomish%Breloom%Slakoth%Vigoroth%Slaking%Nincada%Ninjask%Shedinja%Whismur%Loudred%Exploud%Makuhita%Hariyama%Azurill%Nosepass%Skitty%Delcatty%Sableye%Mawile%Aron%Lairon%Aggron%Meditite%Medicham%Electrike%Manectric%Plusle%Minun%Volbeat%Illumise%Roselia%Gulpin%Swalot%Carvanha%Sharpedo%Wailmer%Wailord%Numel%Camerupt%Torkoal%Spoink%Grumpig%Spinda%Trapinch%Vibrava%Flygon%Cacnea%Cacturne%Swablu%Altaria%Zangoose%Seviper%Lunatone%Solrock%Barboach%Whiscash%Corphish%Crawdaunt%Baltoy%Claydol%Lileep%Cradily%Anorith%Armaldo%Feebas%Milotic%Castform%Kecleon%Shuppet%Banette%Duskull%Dusclops%Tropius%Chimecho%Absol%Wynaut%Snorunt%Glalie%Spheal%Sealeo%Walrein%Clamperl%Huntail%Gorebyss%Relicanth%Luvdisc%Bagon%Shelgon%Salamence%Beldum%Metang%Metagross%Regirock%Regice%Registeel%Latias%Latios%Kyogre%Groudon%Rayquaza%Jirachi%Deoxys%";
  2380. // Section 4
  2381. defaultRandomizerOptions.species["Pokemon"] += "Turtwig%Grotle%Torterra%Chimchar%Monferno%Infernape%Piplup%Prinplup%Empoleon%Starly%Staravia%Staraptor%Bidoof%Bibarel%Kricketot%Kricketune%Shinx%Luxio%Luxray%Budew%Roserade%Cranidos%Rampardos%Shieldon%Bastiodon%Burmy%Wormadam%Mothim%Combee%Vespiquen%Pachirisu%Buizel%Floatzel%Cherubi%Cherrim%Shellos%Gastrodon%Ambipom%Drifloon%Drifblim%Buneary%Lopunny%Mismagius%Honchkrow%Glameow%Purugly%Chingling%Stunky%Skuntank%Bronzor%Bronzong%Bonsly%Mime Jr.%Happiny%Chatot%Spiritomb%Gible%Gabite%Garchomp%Munchlax%Riolu%Lucario%Hippopotas%Hippowdon%Skorupi%Drapion%Croagunk%Toxicroak%Carnivine%Finneon%Lumineon%Mantyke%Snover%Abomasnow%Weavile%Magnezone%Lickilicky%Rhyperior%Tangrowth%Electivire%Magmortar%Togekiss%Yanmega%Leafeon%Glaceon%Gliscor%Mamoswine%Porygon-Z%Gallade%Probopass%Dusknoir%Froslass%Rotom%Uxie%Mesprit%Azelf%Dialga%Palkia%Heatran%Regigigas%Giratina%Cresselia%Phione%Manaphy%Darkrai%Shaymin%Arceus%";
  2382. // Section 5
  2383. defaultRandomizerOptions.species["Pokemon"] += "Victini%Snivy%Servine%Serperior%Tepig%Pignite%Emboar%Oshawott%Dewott%Samurott%Patrat%Watchog%Lillipup%Herdier%Stoutland%Purrloin%Liepard%Pansage%Simisage%Pansear%Simisear%Panpour%Simipour%Munna%Musharna%Pidove%Tranquill%Unfezant%Blitzle%Zebstrika%Roggenrola%Boldore%Gigalith%Woobat%Swoobat%Drilbur%Excadrill%Audino%Timburr%Gurdurr%Conkeldurr%Tympole%Palpitoad%Seismitoad%Throh%Sawk%Sewaddle%Swadloon%Leavanny%Venipede%Whirlipede%Scolipede%Cottonee%Whimsicott%Petilil%Lilligant%Basculin%Sandile%Krokorok%Krookodile%Darumaka%Darmanitan%Maractus%Dwebble%Crustle%Scraggy%Scrafty%Sigilyph%Yamask%Cofagrigus%Tirtouga%Carracosta%Archen%Archeops%Trubbish%Garbodor%Zorua%Zoroark%Minccino%Cinccino%Gothita%Gothorita%Gothitelle%Solosis%Duosion%Reuniclus%Ducklett%Swanna%Vanillite%Vanillish%Vanilluxe%Deerling%Sawsbuck%Emolga%Karrablast%Escavalier%Foongus%Amoonguss%Frillish%Jellicent%Alomomola%Joltik%Galvantula%Ferroseed%Ferrothorn%Klink%Klang%Klinklang%Tynamo%Eelektrik%Eelektross%Elgyem%Beheeyem%Litwick%Lampent%Chandelure%Axew%Fraxure%Haxorus%Cubchoo%Beartic%Cryogonal%Shelmet%Accelgor%Stunfisk%Mienfoo%Mienshao%Druddigon%Golett%Golurk%Pawniard%Bisharp%Bouffalant%Rufflet%Braviary%Vullaby%Mandibuzz%Heatmor%Durant%Deino%Zweilous%Hydreigon%Larvesta%Volcarona%Cobalion%Terrakion%Virizion%Tornadus%Thundurus%Reshiram%Zekrom%Landorus%Kyurem%Keldeo%Meloetta%Genesect%";
  2384. // Section 6
  2385. defaultRandomizerOptions.species["Pokemon"] += "Chespin%Quilladin%Chesnaught%Fennekin%Braixen%Delphox%Froakie%Frogadier%Greninja%Bunnelby%Diggersby%Fletchling%Fletchinder%Talonflame%Scatterbug%Spewpa%Vivillon%Litleo%Pyroar%Flabébé%Floette%Florges%Skiddo%Gogoat%Pancham%Pangoro%Furfrou%Espurr%Meowstic%Honedge%Doublade%Aegislash%Spritzee%Aromatisse%Swirlix%Slurpuff%Inkay%Malamar%Binacle%Barbaracle%Skrelp%Dragalge%Clauncher%Clawitzer%Helioptile%Heliolisk%Tyrunt%Tyrantrum%Amaura%Aurorus%Sylveon%Hawlucha%Dedenne%Carbink%Goomy%Sliggoo%Goodra%Klefki%Phantump%Trevenant%Pumpkaboo%Gourgeist%Bergmite%Avalugg%Noibat%Noivern%Xerneas%Yveltal%Zygarde%Diancie%Hoopa%Volcanion%";
  2386. // Section 7
  2387. defaultRandomizerOptions.species["Pokemon"] += "Rowlet%Dartrix%Decidueye%Litten%Torracat%Incineroar%Popplio%Brionne%Primarina%Pikipek%Trumbeak%Toucannon%Yungoos%Gumshoos%Grubbin%Charjabug%Vikavolt%Crabrawler%Crabominable%Oricorio%Cutiefly%Ribombee%Rockruff%Lycanroc%Wishiwashi%Mareanie%Toxapex%Mudbray%Mudsdale%Dewpider%Araquanid%Fomantis%Lurantis%Morelull%Shiinotic%Salandit%Salazzle%Stufful%Bewear%Bounsweet%Steenee%Tsareena%Comfey%Oranguru%Passimian%Wimpod%Golisopod%Sandygast%Palossand%Pyukumuku%Type: Null%Silvally%Minior%Komala%Turtonator%Togedemaru%Mimikyu%Bruxish%Drampa%Dhelmise%Jangmo-o%Hakamo-o%Kommo-o%Tapu Koko%Tapu Lele%Tapu Bulu%Tapu Fini%Cosmog%Cosmoem%Solgaleo%Lunala%Nihilego%Buzzwole%Pheromosa%Xurkitree%Celesteela%Kartana%Guzzlord%Necrozma%Magearna%Marshadow%Poipole%Naganadel%Stakataka%Blacephalon%Zeraora%Meltan%Melmetal%";
  2388. // Section 8
  2389. defaultRandomizerOptions.species["Pokemon"] += "Grookey%Thwackey%Rillaboom%Scorbunny%Raboot%Cinderace%Sobble%Drizzile%Inteleon%Skwovet%Greedent%Rookidee%Corvisquire%Corviknight%Blipbug%Dottler%Orbeetle%Nickit%Thievul%Gossifleur%Eldegoss%Wooloo%Dubwool%Chewtle%Drednaw%Yamper%Boltund%Rolycoly%Carkol%Coalossal%Applin%Flapple%Appletun%Silicobra%Sandaconda%Cramorant%Arrokuda%Barraskewda%Toxel%Toxtricity%Sizzlipede%Centiskorch%Clobbopus%Grapploct%Sinistea%Polteageist%Hatenna%Hattrem%Hatterene%Impidimp%Morgrem%Grimmsnarl%Obstagoon%Perrserker%Cursola%Sirfetch'd%Mr. Rime%Runerigus%Milcery%Alcremie%Falinks%Pincurchin%Snom%Frosmoth%Stonjourner%Eiscue%Indeedee%Morpeko%Cufant%Copperajah%Dracozolt%Arctozolt%Dracovish%Arctovish%Duraludon%Dreepy%Drakloak%Dragapult%Zacian%Zamazenta%Eternatus%Kubfu%Urshifu%Zarude%Regieleki%Regidrago%Glastrier%Spectrier%Calyrex%Wyrdeer%Kleavor%Ursaluna%Basculegion%Sneasler%Overqwil%Enamorus%";
  2390. // Section 9
  2391. defaultRandomizerOptions.species["Pokemon"] += "Sprigatito%Floragato%Meowscarada%Fuecoco%Crocalor%Skeledirge%Quaxly%Quaxwell%Quaquaval%Lechonk%Oinkologne%Tarountula%Spidops%Nymble%Lokix%Pawmi%Pawmo%Pawmot%Tandemaus%Maushold%Fidough%Dachsbun%Smoliv%Dolliv%Arboliva%Squawkabilly%Nacli%Naclstack%Garganacl%Charcadet%Armarouge%Ceruledge%Tadbulb%Bellibolt%Wattrel%Kilowattrel%Maschiff%Mabosstiff%Shroodle%Grafaiai%Bramblin%Brambleghast%Toedscool%Toedscruel%Klawf%Capsakid%Scovillain%Rellor%Rabsca%Flittle%Espathra%Tinkatink%Tinkatuff%Tinkaton%Wiglett%Wugtrio%Bombirdier%Finizen%Palafin%Varoom%Revavroom%Cyclizar%Orthworm%Glimmet%Glimmora%Greavard%Houndstone%Flamigo%Cetoddle%Cetitan%Veluza%Dondozo%Tatsugiri%Annihilape%Clodsire%Farigiraf%Dudunsparce%Kingambit%Great Tusk%Scream Tail%Brute Bonnet%Flutter Mane%Slither Wing%Sandy Shocks%Iron Treads%Iron Bundle%Iron Hands%Iron Jugulis%Iron Moth%Iron Thorns%Frigibax%Arctibax%Baxcalibur%Gimmighoul%Gholdengo%Wo-Chien%Chien-Pao%Ting-Lu%Chi-Yu%Roaring Moon%Iron Valiant%Koraidon%Miraidon%Walking Wake%Iron Leaves%Dipplin%Poltchageist%Sinistcha%Okidogi%Munkidori%Fezandipiti%Ogerpon%Archaludon%Hydrapple%Gouging Fire%Raging Bolt%Iron Boulder%Iron Crown%Terapagos%Pecharunt";
  2392.  
  2393. // And also with the digital monsters too
  2394. // Section 1
  2395. defaultRandomizerOptions.species["Digimon"] += "Invisimon%Oblivimon%Dimetromon%Ryugumon%Cernumon%MarineBullmon%Elizamon%Nezhamon%Dinomon%Skadimon%PolarBearmon%Erlangmon: Blast Mode%(X Antibody) QueenBeemon%(X Antibody) Vespamon%(X Antibody) ForgeBeemon%HeavyMetaldramon%Loudmon%Punkmon%Takutoumon:Wrath Mode%Erlangmon%Cendrillmon%Chaperomon%Zephagamon%GrandGalemon%Fluffymon%Yolkmon%Callismon%Tlalocmon%ShoeShoemon%Shoemon%Galemon%Pteromon%Takutoumon%(X Antibody) Fenriloogamon: Takemikazuchi%BigUkkomon%Ukkomon%Dijiangmon%Xingtianmon%Lianpumon%Dominimon%ArkhaiAngemon%Luxmon%Fanglongmon: Ruin Mode%Moonmon%Sunmon%Brigadramon%Hi-Commandramon%(X Antibody) Fenriloogamon%(X Antibody) Helloogarmon%(X Antibody) Soloogarmon%Cargodramon%(X Antibody) Loogarmon%(X Antibody) Bowmon%(X Antibody) Fusamon%Quantumon%(X Antibody) Loogamon%Proximamon%Arcturusmon%GreyKnightsmon%Zanmetsumon%Bombermon%Ghilliedhumon%Fumamon%Cthyllamon%Regulusmon%Galacticmon%Destromon%Snatchmon%Vemmon%(X Antibody) Baalmon(X antibody)%Amphimon%Diarbbitmon%Siriusmon%ShineGreymon: Ruin Mode%Chamblemon%Publimon%Oleamon%Huankunmon%Xiangpengmon%Xiquemon%Imperialdramon: Fighter Mode (Black)%(X Antibody) Greymon (Blue)((X Antibody) )%Oboromon%Gyuukimon%ShinMonzaemon%HoverEspimon%Espimon%Jupitermon: Wrath Mode%Plutomon%Ceresmon%Bacchusmon%Junomon: Hysteric Mode%Junomon%Thetismon%Lamortmon%Canoweissmon%Sistermon Ciel (Awake.)%Sistermon Noir (Awake.)%Sistermon Blanc (Awake.)%GulusGammamon%TeslaJellymon%SymbareAngoramon%BlackGatomon Uver.%Weddinmon%Shortmon%BlackRapidmon%BlackGargomon%Omnimon Zwart Defeat%WezenGammamon%KausGammamon%BetelGammamon%Abbadomon Core%Luminamon (Nene Version)%(Xros Wars) Shoutmon X7: Superior Mode%Jellymon%Puyoyomon%Puyomon%Angoramon%Bosamon%Pyonmon%Gammamon%Gurimon%Curimon%Abbadomon%Negamon%PileVolcanomon%Raidenmon%Raijinmon%DoKunemon%ModokiBetamon%Hydramon%Bloomlordmon%Ajatarmon%Guardromon (Gold)%Burgamon%Piddomon%SandYanmamon%Yanmamon%MoriShellmon%BomberNanimon%(X Antibody) HerculesKabuterimon%(X Antibody) Starmon%(X Antibody) Growlmon%(X Antibody) Hagurumon%(X Antibody) Gaiomon: Itto Mode%Brachiomon%Kyukimon%Vermilimon%Assaultmon%Tekkamon%Vulturemon%Tumblemon%Sandmon%Burpmon%BlackKingNumemon%GoldNumemon%Calumon%Gladimon%(Xros Wars) Shoutmon X4K%KoDokugumon%Yoxtu!Yoxtu!mon%Minidekachimon%Atamadekachimon%Trailmon%KingSukamon%Shroudmon%FrosVelgrmon%Frozomon%Hiyarimon%BlackSeraphimon%RareRaremon%Climbmon%Divemon%ClearAgumon%BanchoLillymon%(X Antibody) MetalGreymon (Vaccine)%(X Antibody) IceLeomon%(X Antibody) MetalMamemon%(X Antibody) Thundermon%(X Antibody) Monochromon%(X Antibody) Dobermon%";
  2396.  
  2397. // Section 2
  2398. defaultRandomizerOptions.species["Digimon"] += "(X Antibody) Gesomon%(X Antibody) Gotsumon%(X Antibody) Crabmon%(X Antibody) Guilmon%Shoutmon EX6%TorikaraBallmon%Burgamon%Potamon%Achillesmon%Shivamon%Kazuchimon%Pistmon%Tempomon%Shootmon%Boutmon%Mitamamon%LovelyAngemon%Bibimon%Dokimon%Namakemon%Runnermon%Exermon%Bulkmon%Komondomon%Pulsemon%Omnimon: Merciful Mode%DoneDevimon%WereGarurumon: Sagittarius Mode%Imperialdramon: Dragon Mode (Black)%Manticoremon%Mimicmon%Baluchimon%DarkMaildramon%MetalGreymon: Alterous Mode%Rebellimon%Machmon%Junkmon%HeavyLeomon%Hopmon%Ketomon%Nidhoggmon%Entmon%Parasaurmon%Toropiamon%Pomumon%Eyesmon: Scatter Mode%Eyesmon%(X Antibody) Betamon%(X Antibody) Gazimon%Argomon%Ghostmon%Regalecusmon%Piranimon%Gusokumon%MarineChimairamon%Tobiumon%Sangomon%Gogmamon%Baboongamon%Sunarizamon%JewelBeemon%Hudiemon%(Xros Wars) Arresterdramon: Superior Mode%(X Antibody) Garurumon%(X Antibody) Greymon%Argomon%Argomon%Argomon%Spadamon%Morphomon%Eosmon%Gabumon (Bond of Friendship)%Agumon (Bond of Bravery)%Eosmon%Eosmon%TonosamaMamemon%Omnimon Zwart%Terriermon Assistant%CrysPaledramon%Metallicdramon%Jazarichmon%Jazardmon%Jazamon%Hexeblaumon%Paledramon%NoblePumpkinmon%(X Antibody) Ogudomon%(X Antibody) Jesmon GX%(X Antibody) Keramon%(X Antibody) OmniShoutmon%(X Antibody) Meramon%(X Antibody) Pegasusmon%(X Antibody) Rapidmon%(X Antibody) Monzaemon%(X Antibody) Gankoomon%(X Antibody) Examon%(X Antibody) Cyberdramon%(X Antibody) RizeGreymon%(X Antibody) Phoenixmon%(X Antibody) Justimon%(X Antibody) Wizardmon%(X Antibody) Seasarmon%(X Antibody) Cherubimon (Good)%(X Antibody) Ophanimon%(X Antibody) Angewomon%(X Antibody) Diaboromon%(X Antibody) Dynasmon%(X Antibody) Leopardmon%(X Antibody) Magnadramon%(X Antibody) Plesiomon%(X Antibody) Goldramon%(X Antibody) Garudamon%(X Antibody) Gatomon%(X Antibody) Togemon%(X Antibody) Tylomon%(X Antibody) Lopmon%(X Antibody) Syakomon%(X Antibody) Terriermon%(X Antibody) Palmon%(X Antibody) Pteramon%(X Antibody) Mamemon%(X Antibody) Ebemon%(X Antibody) Kentaurosmon%(X Antibody) Craniamon%(X Antibody) UlforceVeedramon%(X Antibody) Magnamon%(X Antibody) MegaSeadramon%(X Antibody) Triceramon%(X Antibody) Okuwamon%(X Antibody) Mantaraymon%(X Antibody) Nefertimon%(X Antibody) Seadramon%(X Antibody) Kuwagamon%(X Antibody) Allomon%(X Antibody) Gomamon%(X Antibody) Kokuwamon%(X Antibody) BlackWarGreymon%(X Antibody) LadyDevimon%(X Antibody) MetalGreymon (Virus)%(X Antibody) Ogremon%(X Antibody) Agumon (Black)%Rasenmon%Rasenmon: Fury Mode%Stefilmon%Filmon%(X Antibody) Rosemon%(X Antibody) PrinceMamemon%(X Antibody) SkullMammothmon%(X Antibody) Mammothmon%(X Antibody) Mephistomon%(X Antibody) Myotismon%(X Antibody) Salamon%(X Antibody) Otamamon%Yaamon%Keemon%(X Antibody) Impmon%(X Antibody) DarkTyrannomon%(X Antibody) Numemon%(X Antibody) Lucemon%(X Antibody) Lilithmon%(X Antibody) Leviamon%(X Antibody) Belphemon%";
  2399.  
  2400. // Section 3
  2401. defaultRandomizerOptions.species["Digimon"] += "(X Antibody) Barbamon%(X Antibody) Creepymon%(X Antibody) DarkKnightmon%(X Antibody) Cherubimon (Black)%(X Antibody) Ophanimon: Falldown Mode%UltimateChaosmon%Pusurimon%Pusumon%Surfimon%Bulucomon%Herissmon%MetalGarurumon (Black)%WereGarurumon (Black)%Garurumon (Black)%Justimon: Blitz Arm%Ginkakumon Promote%IceDevimon%Kuzuhamon Maid Mode%WarGrowlmon (Orange)%Greymon (Blue)%(Xros Wars) Shoutmon (King Version)%(X Antibody) WarGreymon%(X Antibody) MetalGarurumon%(X Antibody) Megidramon%(X Antibody) WereGarurumon%(X Antibody) Lillymon%(X Antibody) Sakuyamon%(X Antibody) Beelzemon%(X Antibody) WarGrowlmon%(X Antibody) Rhinomon%(X Antibody) Renamon%(X Antibody) Tokomon%(X Antibody) BeelStarmon%(X Antibody) Gallantmon%(X Antibody) Cerberusmon%(X Antibody) Tyrannomon%(X Antibody) Dracomon%(X Antibody) Gabumon%(X Antibody) Minervamon%(X Antibody) LordKnightmon%Sistermon Ciel%(X Antibody) MetalTyrannomon%(X Antibody) Leomon%(X Antibody) Agumon%(X Antibody) Jesmon%RagnaLoardmon%Rafflesimon%Ordinemon%(Xros Wars) RaptorSparrowmon%BanchoGolemon%Cerberusmon: Werewolf Mode%Growlmon (Orange)%ToyAgumon (Black)%(Xros Wars) ShootingStarmon%Justimon: Critical Arm%Crowmon%Dinohyumon%Rapidmon%(Xros Wars) JaegerDorulumon%Gundramon%Aegiochusmon: Holy%Gabumon (Black)%Pipimon%Raguelmon%BryweLudramon%Volcanicdramon%Lavogaritamon%Maycrackmon: Vicious Mode%Mastemon%Boltboutamon%RaijiLudomon%TiaLudomon%Lavorvomon%BushiAgumon%Ludomon%Kakkinmon%Cotsucomon%Antylamon%Meicoomon%Whamon (Champion)%Gekomon%Vorvomon%(Xros Wars) AtlurBallistamon%Omnimon Alter-B%BlackMachGaogamon%BlackGaogamon%Falcomon (2006 Anime Version)%Kudamon (2006 Anime Version)%NEO%(Xros Wars) Yakiimon%(Xros Wars) Monimon%(Xros Wars) Monitamon%(Xros Wars) Mervamon%(Xros Wars) MetalGreymon + Cyber Launcher%(Xros Wars) MetalGreymon (2010 Anime Version)%(Xros Wars) MailBirdramon%(Xros Wars) MusouKnightmon%(Xros Wars) MadLeomon: Armed Mode%(Xros Wars) MadLeomon%(Xros Wars) Bommon (2010 Anime Version)%(Xros Wars) Beelzemon (2010 Anime Version)%(Xros Wars) Blastmon%(Xros Wars) Footmon%(Xros Wars) Pillomon%(Xros Wars) Pickmon%(Xros Wars) Ballistamon%(Xros Wars) Bakomon%(Xros Wars) Bagramon%(Xros Wars) Hi-VisionMonitamon%(Xros Wars) Baalmon%(Xros Wars) NeoMyotismon%(Xros Wars) Dondokomon%(Xros Wars) DonShoutmon%(Xros Wars) Dorulumon%(Xros Wars) Dorbickmon%(Xros Wars) Troopmon%(Xros Wars) DeadlyAxemon%(Xros Wars) Deckerdramon%(Xros Wars) DeckerGreymon%(Xros Wars) Tuwarmon%(Xros Wars) ChuuChuumon%(Xros Wars) Chibickmon%(Xros Wars) Chikurimon%";
  2402.  
  2403. // Section 3
  2404. defaultRandomizerOptions.species["Digimon"] += "(Xros Wars) Damemon%(Xros Wars) Tactimon%(Xros Wars) DarknessBagramon%(Xros Wars) DarkKnightmon%(Xros Wars) Zenimon%(Xros Wars) Splashmon%(Xros Wars) Sparrowmon%(Xros Wars) Starmons%(Xros Wars) SkullKnightmon: Mighty Axe Mode%(Xros Wars) SkullKnightmon: Cavalier Mode%(Xros Wars) SkullKnightmon%(Xros Wars) Jokermon%(Xros Wars) Shanitamon%(Xros Wars) Shoutmon + Dorulu Cannon%(Xros Wars) Shoutmon + Star Sword%(Xros Wars) Shoutmon + Jet Sparrow%(Xros Wars) Shoutmon X7%(Xros Wars) Shoutmon X5B%(Xros Wars) Shoutmon X5%(Xros Wars) Shoutmon X4B%(Xros Wars) Shoutmon X4%(Xros Wars) Shoutmon X3%(Xros Wars) Shoutmon X2%(Xros Wars) Shoutmon DX%(Xros Wars) Shoutmon%(Xros Wars) JetMervamon%(Xros Wars) ZeigGreymon%(Xros Wars) Zamielmon%(Xros Wars) Soundbirdmon%(Xros Wars) Cyberdramon%(Xros Wars) Kozenimon%(Xros Wars) Greymon (2010 Anime Version)%(Xros Wars) Gravimon%(Xros Wars) Cutemon%(Xros Wars) Gumdramon%(Xros Wars) Ganemon%(Xros Wars) Gaossmon%(Xros Wars) Olegmon%(Xros Wars) OmniShoutmon%(Xros Wars) Ekakimon%(Xros Wars) Ignitemon%(Xros Wars) Arresterdramon%(Hybrid) Loweemon%(Hybrid) Lanamon%(Hybrid) Rhihimon%(Hybrid) RhinoKabuterimon%(Hybrid) Mercurymon%(Hybrid) MagnaGarurumon%(Hybrid) MetalKabuterimon%(Hybrid) Velgrmon%(Hybrid) Petaldramon%(Hybrid) Beowolfmon%(Hybrid) Flamemon%(Hybrid) Beetlemon%(Hybrid) Korikakumon%(Hybrid) Kazemon%(Hybrid) Kumamon%(Hybrid) Duskmon%(Hybrid) DaiPenmon%(Hybrid) Sephirothmon%(Hybrid) Strabimon%(Hybrid) Zephyrmon%(Hybrid) JetSilphymon%(Hybrid) Grumblemon%(Hybrid) Gigasmon%(Hybrid) KendoGarurumon%(Hybrid) Calmaramon%(Hybrid) EmperorGreymon%(Hybrid) JagerLoweemon%(Hybrid) BurningGreymon%(Hybrid) Lobomon%(Hybrid) Arbormon%(Hybrid) Aldamon%(Hybrid) Agunimon%Rinkmon%Lynxmon%Rhinomon%Lighdramon%Yasyamon%Mothmon%Maildramon%Moosemon%Manbomon%Mantaraymon%Magnamon%Ponchomon%Halsemon%Boarmon%Pegasusmon%Frogmon%Prairiemon%Flamedramon%FlameWizardmon%Bullmon%Flybeemon%Pteramon%Bucchiemon (Green)%Bucchiemon%Pipismon%Rabbitmon%Peacockmon%Baronmon%Honeybeemon%Butterflymon%Harpymon%Nohemon%Nefertimon%Togemogumon%Toucanmon%Depthmon%Tylomon%Digmon%Sepikmon%Sethmon%Swanmon%Stegomon%Shurimon%Shadramon%Seahomon%Sheepmon%Thunderbirdmon%Salamandermon%Submarimon%Sagittarimon%Searchmon%Kongoumon%GoldVeedramon%Goatmon%Kenkimon%Quetzalmon%Kangarumon%Chameleonmon%Kabukimon%Gargoylemon%Orcamon%Opossummon%Elephantmon%Allomon%Archelomon%Aurumon%LordKnightmon%Lotosmon%Rosemon: Burst Mode%Rosemon%Ravemon: Burst Mode%Ravemon%Lucemon: Larva%Lucemon: Satan Mode%Lilithmon%Leviamon%Lampmon%RustTyrannomon%Rasielmon%Jupitermon%Merukimon%MedievalGallantmon%(X Antibody) MetalPiranimon%MetalSeadramon%MetalGarurumon%MetalEtemon%Megidramon%MoonMillenniummon%Murmukusmon%Machinedramon%Millenniummon%";
  2405.  
  2406. // Section 4
  2407. defaultRandomizerOptions.species["Digimon"] += "MirageGaogamon: Burst Mode%MirageGaogamon%Minervamon%Marsmon%MarineAngemon%MagnaKidmon%Boltmon%Magnadramon%Phoenixmon%Belphemon: Rage Mode%Belphemon: Sleep Mode%Beelzemon: Blast Mode%Beelzemon%BeelStarmon%MaloMyotismon%HerculesKabuterimon%Plesiomon%Breakdramon%PrinceMamemon%BlitzGreymon%BlackSaintGargomon%BlackWarGreymon%PlatinumNumemon%Pukumon%Fujinmon%Fanglongmon%Pharaohmon%Puppetmon%VictoryGreymon%Piedmon%BanchoLeomon%BanchoMamemon%BanchoStingmon%Barbamon%Parasimon%Babamon%Baihumon%HiAndromon%Neptunemon%(X Antibody) Dorugoramon%Leopardmon: Leopard Mode%Leopardmon%Durandamon%Dynasmon%Gallantmon: Crimson Mode%Gallantmon%Devitamamon%Ghoulmon (Black)%Ghoulmon%DeathXmon%Creepymon%(X Antibody) DexDorugoramon%(X Antibody) Dinorexmon%(X Antibody) Dinotigermon%Diaboromon%Dianamon%Azulongmon%Darkdramon%TyrantKabuterimon%Titamon%(X Antibody) TigerVespamon%MegaGargomon%Seraphimon%Slayerdramon%Kentaurosmon%SlashAngemon%Spinomon%Susanomon%SkullMammothmon%Zhuqiaomon%Suijinmon%ZeedMillenniummon%ZeedGarurumon%JumboGamemon%Justimon: Accel Arm%Shakamon%ShineGreymon: Burst Mode%ShineGreymon%Jijimon%Ebonwumon%Jesmon%Zanbamon%Sakuyamon: Maid Mode%Sakuyamon%SaberLeomon%Goldramon%Reapermon%Cherubimon (Black)%Cherubimon (Good)%Eaglemon%Craniamon%GraceNovamon%Gryphonmon%GroundLocomon%GranDracmon%(X Antibody) GrandisKuwagamon%GranKuwagamon%ClavisAngemon%Kuzuhamon%Quartzmon%CresGarurumon%QueenChessmon%KingChessmon%KingEtemon%Cannondramon%(X Antibody) GigaSeadramon%Gankoomon%Gulfmon%Chaosmon: Valdur Arm%Chaosmon%(X Antibody) Chaosdramon%Chaosdramon%ChaosGallantmon%(X Antibody) Gaiomon%Omnimon Alter-S%(X Antibody) Omnimon%Omnimon%Ophanimon: Falldown Mode%Ophanimon%Ornismon%Ogudomon%(X Antibody) Ouryumon%AncientWisemon%AncientMegatheriummon%AncientMermaimon%AncientVolcanomon%AncientBeetlemon%AncientTroymon%AncientSphinxmon%AncientGreymon%AncientGarurumon%AncientKazemon%Eldradimon%Examon%Vulcanusmon%WarGreymon%VenomMyotismon%Venusmon%Varodurumon%Valkyrimon%Vikemon%Ebemon%Aegisdramon%Imperialdramon: Fighter Mode%Imperialdramon: Paladin Mode%Imperialdramon: Dragon Mode%King Drasil_7D6%UlforceVeedramon%(X Antibody) Alphamon: Ouryuken%(X Antibody) Alphamon%(X Antibody) UltimateBrachiomon%Argomon%Apollomon%Apocalymon%Anubismon%Armageddemon%AvengeKidmon%WaruMonzaemon%WaruSeadramon%Wisemon%WereGarurumon%Locomon%LoaderLeomon%LadyDevimon%Luminamon%Lucemon: Chaos Mode%RookChessmon%Lillymon%Rapidmon%Lilamon%RizeGreymon%Crowmon (2006 Anime Version)%Monzaemon%Mephistomon%MetalMamemon%";
  2408.  
  2409. // Section 5
  2410. defaultRandomizerOptions.species["Digimon"] += "(X Antibody) MetalPhantomon%MetalTyrannomon%MetalGreymon (Vaccine)%MetalGreymon (Virus)%MetallifeKuwagamon%WarGrowlmon%Megadramon%MegaSeadramon%Maycrackmon%Mihiramon%Mistymon%Mammothmon%MarineDevimon%(X Antibody) Mametyramon%Mamemon%Mummymon%MachGaogamon%Matadormon%MasterTyrannomon%Majiramon%Makuramon%Mermaimon%Volcanomon%MagnaAngemon%Whamon%Betsumon%(X Antibody) Vademon%Vademon%Blossomon%Flaremon%BlueMeramon%BlackWarGrowlmon%Feresmon%Phantomon%HippoGryphonmon%Piximon%BigMamemon%BishopChessmon%(X Antibody) Hisyaryumon%Pumpkinmon%Pandamon%IceLeomon%Divermon%Parrotmon%Bulbmon%Bastemon%Pajiramon%Paildramon%NeoDevimon%Datamon%Knightmon%(X Antibody) DoruGreymon%Triceramon%ShogunGekomon%Doumon%Deramon%Duramon%SkullMeramon%Digitamamon%(X Antibody) DexDoruGreymon%Dinobeemon%Cho-Hakkaimon%Caturamon%Chirinmon%Tankdramon%Dragomon%Taomon%DarkSuperStarmon%Sirenmon%SaviorHuckmon%Zudomon%SkullScorpiomon%(X Antibody) SkullBaluchimon%SkullSatamon%SkullGreymon%SuperStarmon%Sinduramon%Silphymon%Cherrymon%Shakkoumon%Jagamon%Shaujinmon%Sandiramon%Sanzomon%Sagomon%Cyberdramon%Gokuumon%Cerberusmon%Kumbhiramon%Crescemon%(X Antibody) Grademon%GrapLeomon%Groundramon%Giromon%CaptainHookmon%(X Antibody) CannonBeemon%CatchMamemon%Kimeramon%Gigadramon%Garudamon%Karatenmon%Garbagemon%Orochimon%Okuwamon%Angewomon%";
  2411.  
  2412. // Section 6
  2413. defaultRandomizerOptions.species["Digimon"] += "Etemon%ExTyrannomon%AeroVeedramon%Volcdramon%Wingdramon%Vikaralamon%Myotismon%Vajramon%Infermon%Indramon%Meteormon%Andromon%Antylamon (Deva)%Argomon%Arukenimon%(X Antibody) Scorpiomon%Scorpiomon%MegaKabuterimon (Red)%MegaKabuterimon (Blue)%Astamon%Asuramon%Aegiochusmon: Blue%Aegiochusmon: Dark%Aegiochusmon: Green%Aegiochusmon%(X Antibody) Waspmon%Reppamon%RedVegiemon%Lekismon%Leomon%Raremon%Dolphmon%Deputymon%(X Antibody) Raptordramon%Liamon%Youkomon%Unimon%Frigimon%Monochromon%Mojyamon%Meramon%Mekanorimon%Musyamon%Minotarumon%Mikemon%Porcupamon%Peckmon%Vegiemon%BladeKuwagamon%Flarerizamon%Blimpmon%BlackGatomon%BlackGrowlmon%PlatinumSukamon%Flymon%Hookmon%Boogiemon%Fugamon%Veedramon%Fangmon%Firamon%Hyogamon%Petermon%Apemon%Bakemon%BaoHuckmon%Birdramon%Numemon%NiseDrimogemon%Nanimon%KnightChessmon (White)%KnightChessmon (Black)%(X Antibody) Dorugamon%Drimogemon%(X Antibody) TobuCatmon%Doggymon%Togemon%Dokugumon%Dobermon%Tortomon%Turuiemon%Deltamon%Devimon%Devidramon%(X Antibody) DexDorugamon%Gatomon%Tyrannomon%Diatrymon%MudFrigimon%Tankmon%Darcmon%Tuskmon%Targetmon%DarkLizardmon%DarkTyrannomon%Sorcermon%Soulmon%Saberdramon%ZubaEagermon%Snimon%Strikedramon%Stingmon%Starmon%Sukamon%JungleMojyamon%ShimaUnimon%Sistermon Noir%GeoGreymon%Shellmon%Shademon%Sealsdramon%Coelamon%Seadramon%Seasarmon%Sunflowmon%Thundermon%Sangloupmon%Weedmon%Cyclonemon%Gorillamon%Roachmon%Golemon%Kokatorimon%Kogamon%Coredramon (Green)%Coredramon (Blue)%Centarumon%Geremon%Gesomon%Kuwagamon%Clockmon%Greymon%Gururumon%Grizzlymon%Kurisarimon%Growlmon%(X Antibody) Ginryumon%Ginkakumon%Kinkakumon%Kyubimon%Kiwimon%Gawappamon%Garurumon%Gargomon%ShellNumemon%Kabuterimon%Guardromon%Gaogamon%(X Antibody) Omekamon%Octomon%Ogremon%Angemon%Ebidramon%ExVeemon%Airdramon%Woodmon%Wendigomon%Witchmon%Wizardmon%Vilemon%Ikkakumon%Ninjamon%Ankylomon%Aquilamon%Akatorimon%Icemon%Aegiomon%Wormmon%Lopmon%Renamon%Liollmon%Lunamon%Lucemon%(X Antibody) Ryudamon%Lalamon%Labramon%SnowAgumon%Monodramon%Muchomon%Mushroomon%Bokomon%PawnChessmon (White)%";
  2414.  
  2415. // Section 7
  2416. defaultRandomizerOptions.species["Digimon"] += "PawnChessmon (Black)%Hawkmon%Penmon%Betamon%Bearmon%Salamon%Floramon%Veemon%(X Antibody) FunBeemon%Falcomon%Phascomon%Biyomon%DemiDevimon%Palmon%Huckmon%Patamon%Hagurumon%Tapirmon%Neemon%Fake Agumon Expert%(X Antibody) Dorumon%Dracomon%Dracmon%ToyAgumon%Tentomon%Terriermon%Tinkermon%Tsukaimon%Chuumon%Solarmon%Zubamon%SnowGoblimon%Swimmon%Syakomon%Shamamon%Sistermon Blanc%Psychemon%Coronamon%Commandramon%Gomamon%Goblimon%Kotemon%Gotsumon%Kokuwamon%KoKabuterimon%Monmon%Keramon%Kunemon%Kudamon%Guilmon%Candlemon%Gizamon%Kamemon%Gabumon%Crabmon%Gazimon%Gaomon%Otamamon (Red)%Otamamon%Elecmon (Violet)%Elecmon%EbiBurgamon%Impmon%Aruraumon%Armadillomon%Arcadiamon%Agumon Expert%Agumon (Black)%Agumon (2006 Anime Version)%Agumon%Wanyamon%Motimon%Minomon%Missimon%Poromon%Viximon%Bebydomon%(X Antibody) Puroromon%Frimon%DemiMeramon%Bukamon%Yokomon%Pinamon%Budmon%Pagumon%Nyaromon%(X Antibody) Dorimon%Tokomon%Tsumemon%Tsunomon%Kokomon%Chapmon%DemiVeemon%Tanemon%Xiaomon%Sakuttomon%Koromon%Gummymon%(X Antibody) Kyokyomon%Cupimon%Kyaromon%Gigimon%Kapurimon%Upamon%Relemon%Leafmon%Yuramon%YukimiBotamon%Mokumon%Poyomon%Bommon%Popomon%Botamon%Puwamon%Pururumon%(X Antibody) Pupumon%(X Antibody) Fufumon%Punimon%Puttimon%Petitmon%Pichimon%Pafumon%Pabumon%Paomon%Nyokimon%(X Antibody) Dodomon%Tsubumon%Choromon%Chibomon%Zerimon%Zurumon%Jyarimon%Sakumon%Conomon%Kuramon";
  2417.  
  2418. defaultRandomizerOptions.species['25% Human | 75% Animals'] = defaultRandomizerOptions.species.Animals + "%Human*923";
  2419. defaultRandomizerOptions.species['50% Human | 50% Animals'] = defaultRandomizerOptions.species.Animals + "%Human*2770";
  2420. defaultRandomizerOptions.species['75% Human | 25% Animals'] = defaultRandomizerOptions.species.Animals + "%Human*8310";
  2421. defaultRandomizerOptions.species['25% Human | 75% Mythos'] = defaultRandomizerOptions.species.Mythological + "%Human*317";
  2422. defaultRandomizerOptions.species['50% Human | 50% Mythos'] = defaultRandomizerOptions.species.Mythological + "%Human*951";
  2423. defaultRandomizerOptions.species['75% Human | 25% Mythos'] = defaultRandomizerOptions.species.Mythological + "%Human*2853";
  2424. defaultRandomizerOptions.species['Mythos + Animals'] = defaultRandomizerOptions.species.Animals + "," + defaultRandomizerOptions.species.Mythological;
  2425. defaultRandomizerOptions.species['25% Human | 75% Mythos + Animals'] = defaultRandomizerOptions.species['Mythos + Animals'] + "%Human*1240";
  2426. defaultRandomizerOptions.species['50% Human | 50% Mythos + Animals'] = defaultRandomizerOptions.species['Mythos + Animals'] + "%Human*3721";
  2427. defaultRandomizerOptions.species['75% Human | 25% Mythos + Animals'] = defaultRandomizerOptions.species['Mythos + Animals'] + "%Human*11163";
  2428. defaultRandomizerOptions.species['25% Human | 75% Pokemon'] = defaultRandomizerOptions.species.Pokemon + "%Human*512";
  2429. defaultRandomizerOptions.species['50% Human | 50% Pokemon'] = defaultRandomizerOptions.species.Pokemon + "%Human*1024";
  2430. defaultRandomizerOptions.species['75% Human | 25% Pokemon'] = defaultRandomizerOptions.species.Pokemon + "%Human*3072";
  2431. defaultRandomizerOptions.species['25% Human | 75% Digimon'] = defaultRandomizerOptions.species.Digimon + "%Human*614";
  2432. defaultRandomizerOptions.species['50% Human | 50% Digimon'] = defaultRandomizerOptions.species.Digimon + "%Human*1227";
  2433. defaultRandomizerOptions.species['75% Human | 25% Digimon'] = defaultRandomizerOptions.species.Digimon + "%Human*3681";
  2434. defaultRandomizerOptions.species['Pokemon + Digimon'] = defaultRandomizerOptions.species.Pokemon + "," + defaultRandomizerOptions.species.Digimon;
  2435. defaultRandomizerOptions.species['25% Human | 75% Pokemon + Digimon'] = defaultRandomizerOptions.species['Pokemon + Digimon'] + "%Human*1126";
  2436. defaultRandomizerOptions.species['50% Human | 50% Pokemon + Digimon'] = defaultRandomizerOptions.species['Pokemon + Digimon'] + "%Human*2251";
  2437. defaultRandomizerOptions.species['75% Human | 25% Pokemon + Digimon'] = defaultRandomizerOptions.species['Pokemon + Digimon'] + "%Human*6753";
  2438. defaultRandomizerOptions.species.Chaos = defaultRandomizerOptions.species.Realistic + "%" + defaultRandomizerOptions.species.Animals + "%" + defaultRandomizerOptions.species.Mythological + "%" + defaultRandomizerOptions.species.Pokemon + "%" + defaultRandomizerOptions.species.Digimon + "%" + defaultRandomizerOptions.species.Fantasy + "%" + defaultRandomizerOptions.species["Sci-fi"] + "%" + defaultRandomizerOptions.species.Things + "%" + defaultRandomizerOptions.species.Food;
  2439.  
  2440. // User's randomizer settings
  2441. const randomizerSettings = {
  2442. emoji: {
  2443. selectedOption: 'Smileys',
  2444. customList: ''
  2445. },
  2446. name: {
  2447. selectedOption: 'Common',
  2448. customList: ''
  2449. },
  2450. sex: {
  2451. selectedOption: 'Standard',
  2452. customList: ''
  2453. },
  2454. species: {
  2455. selectedOption: 'Realistic',
  2456. customList: ''
  2457. },
  2458. age: {
  2459. selectedOption: 'Adult',
  2460. customList: ''
  2461. },
  2462. bodyType: {
  2463. selectedOption: 'All',
  2464. customList: ''
  2465. },
  2466. personality: {
  2467. selectedOption: 'All',
  2468. customList: ''
  2469. },
  2470. bio: {
  2471. selectedOption: 'Standard',
  2472. customList: ''
  2473. },
  2474. };
  2475.  
  2476. // Function to generate a random character
  2477. function generateRandomCharacter() {
  2478. const character = {};
  2479.  
  2480. // Helper to get pronouns based on sex (male, female, other)
  2481. function getPronouns(sex) {
  2482. const lowerSex = sex.toLowerCase();
  2483. if (lowerSex === 'male') {
  2484. return { subject: 'he', object: 'him', possessive: 'his' };
  2485. } else if (lowerSex === 'female') {
  2486. return { subject: 'she', object: 'her', possessive: 'her' };
  2487. } else {
  2488. return { subject: 'they', object: 'them', possessive: 'their' };
  2489. }
  2490. }
  2491.  
  2492. // Helper to remove comments (>> ... <<) - only for string fields
  2493. function removeComments(text) {
  2494. if (typeof text === 'string') {
  2495. return text.replace(/>>.*?<<\s*/g, '');
  2496. }
  2497. return text; // If it's not a string, just return it unchanged
  2498. }
  2499.  
  2500. // Helper to process sub-lists (&!)
  2501. function processSubLists(items) {
  2502. const mainList = [];
  2503. const subLists = [];
  2504.  
  2505. items.forEach((item, index) => {
  2506. if (index === 0) {
  2507. // The first item is the main list
  2508. mainList.push(...item.split(/[%\n]+/).map(i => i.trim()).filter(Boolean));
  2509. } else {
  2510. // The remaining items are sub-lists, split by commas
  2511. const subListItems = item.split(/[%\n]+/).map(i => i.trim()).filter(Boolean);
  2512. subLists.push(subListItems);
  2513. }
  2514. });
  2515.  
  2516. return { mainList, subLists };
  2517. }
  2518.  
  2519. // Process weighting for string items with a "*n" suffix
  2520. function weightItems(list) {
  2521. return list.map(item => {
  2522. if (typeof item === 'string') {
  2523. const match = item.match(/^(.*)\*(\d+)$/);
  2524. if (match) {
  2525. return { value: match[1].trim(), weight: parseInt(match[2], 10) };
  2526. }
  2527. return { value: item.trim(), weight: 1 };
  2528. }
  2529. return { value: item, weight: 1 }; // For non-string items (e.g., age), no trimming
  2530. });
  2531. }
  2532.  
  2533. // Function to get random item based on weighted random
  2534. function getRandomFromWeightedList(list) {
  2535. const totalWeight = list.reduce((sum, item) => sum + item.weight, 0);
  2536. let random = Math.random() * totalWeight;
  2537. for (const item of list) {
  2538. if (random < item.weight) {
  2539. return item.value;
  2540. }
  2541. random -= item.weight;
  2542. }
  2543. return ''; // Fallback
  2544. }
  2545.  
  2546. // Function to handle recursive sub-list resolution and replace tokens
  2547. function resolveSubListReferences(text, subLists, character, recursionDepth = 0) {
  2548. const MAX_RECURSION_DEPTH = 10; // Prevent infinite recursion
  2549. if (recursionDepth > MAX_RECURSION_DEPTH) {
  2550. console.warn('Maximum recursion depth reached while resolving sub-lists.');
  2551. return text;
  2552. }
  2553.  
  2554. // Replace sub-list references like &?n with a random item from sub-list n
  2555. let resolvedText = text.replace(/&\?(\d+)/g, (match, listIndex) => {
  2556. const index = parseInt(listIndex, 10);
  2557. if (subLists[index] && subLists[index].length > 0) {
  2558. const subList = subLists[index];
  2559. const randomSubItem = getRandomFromWeightedList(weightItems(subList));
  2560. // Recursively resolve any sub-list references within the chosen sub-list item
  2561. return resolveSubListReferences(randomSubItem, subLists, character, recursionDepth + 1);
  2562. }
  2563. return ''; // If sub-list doesn't exist or is empty, return empty string
  2564. });
  2565.  
  2566. // After resolving sub-list references, replace tokens like #?a, #?b, etc.
  2567. const pronouns = getPronouns(character.sex || '');
  2568. resolvedText = resolvedText.replace(/#\?a/g, character.name || '')
  2569. .replace(/#\?b/g, pronouns.subject)
  2570. .replace(/#\?c/g, pronouns.object)
  2571. .replace(/#\?d/g, pronouns.possessive);
  2572.  
  2573. return resolvedText;
  2574. }
  2575.  
  2576. // In the `getRandomItem` function, update where `resolveSubListReferences` is called:
  2577. function getRandomItem(field) {
  2578. let items = [];
  2579. if (randomizerSettings[field].customList.trim() !== '') {
  2580. // Use custom list
  2581. items = randomizerSettings[field].customList
  2582. .split(/[,\n]+/)
  2583. .map(item => item.trim())
  2584. .filter(item => item !== '');
  2585. } else {
  2586. // Use default list
  2587. const selectedOption = randomizerSettings[field].selectedOption;
  2588. items = defaultRandomizerOptions[field][selectedOption]
  2589. .split(/[,\n]+/)
  2590. .map(item => item.trim())
  2591. .filter(item => item !== '');
  2592. }
  2593.  
  2594. // Handle cases where items might be undefined or empty
  2595. if (!items || items.length === 0) {
  2596. return '';
  2597. }
  2598.  
  2599. // Remove comments (only applies to string fields)
  2600. items = items.map(removeComments);
  2601.  
  2602. // First, process the entire string before splitting by commas or newlines
  2603. const inputString = items.join(',');
  2604.  
  2605. // Split the input string on "&!" to separate sub-lists from the main text
  2606. const mainTextAndSubLists = inputString.split('&!');
  2607.  
  2608. // The main text is everything before the first "&!"
  2609. const mainListString = mainTextAndSubLists.shift(); // Get the main text
  2610. const subListStrings = mainTextAndSubLists; // Remaining parts are sub-lists
  2611.  
  2612. // Now process the main list and any sub-lists
  2613. const { mainList, subLists } = processSubLists([mainListString, ...subListStrings]);
  2614.  
  2615. // Get the random item from the main list
  2616. let result = getRandomFromWeightedList(weightItems(mainList));
  2617.  
  2618. // If the field is 'bio', handle dynamic text insertion (e.g., pronouns, names)
  2619. if (field === 'bio' && typeof result === 'string') {
  2620. // Resolve sub-list references and replace tokens for bio
  2621. result = resolveSubListReferences(result, subLists, character);
  2622. } else {
  2623. // For non-bio fields, resolve sub-list references if they exist
  2624. result = resolveSubListReferences(result, subLists, character);
  2625. }
  2626.  
  2627. // For age, convert to numbers
  2628. if (field === 'age') {
  2629. result = parseInt(result, 10);
  2630. }
  2631. return result;
  2632. }
  2633.  
  2634. // Generate character fields
  2635. character.emoji = getRandomItem('emoji');
  2636. character.name = getRandomItem('name');
  2637. character.sex = getRandomItem('sex');
  2638. character.species = getRandomItem('species');
  2639. character.age = getRandomItem('age');
  2640. character.bodyType = getRandomItem('bodyType');
  2641. character.personality = getRandomItem('personality');
  2642. character.bio = getRandomItem('bio');
  2643.  
  2644. return character;
  2645. }
  2646.  
  2647. // Function to capitalize first letter
  2648. function capitalizeFirstLetter(string) {
  2649. return string.charAt(0).toUpperCase() + string.slice(1);
  2650. }
  2651.  
  2652. // Function to open Randomizer Settings dialog
  2653. function openRandomizerSettings() {
  2654. const settingsForm = document.createElement('form');
  2655. console.log(settingsForm);
  2656. settingsForm.innerHTML = `
  2657. <div style="display: flex; flex-direction: column; width: 100%; min-width: 500px; margin: 0 auto;">
  2658. <!-- Scrollable content container -->
  2659. <div class="scrollable-content" style="flex: 1; overflow-y: auto; max-height: 300px; padding-right: 10px;">
  2660. <!-- Fields will be inserted here dynamically -->
  2661. </div>
  2662. <!-- Buttons at the bottom -->
  2663. <div class="settings-buttons" style="text-align: center; padding-top: 10px;">
  2664. <button type="submit" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 8px 16px; border-radius: 5px; margin-right: 10px; cursor: pointer;">Save Settings</button>
  2665. <button type="button" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer;" id="cancel-button">Cancel</button>
  2666. </div>
  2667. </div>
  2668. `;
  2669.  
  2670. const fieldsContainer = settingsForm.querySelector('.scrollable-content');
  2671. // For each randomizable field, create settings UI
  2672. const fields = ['emoji', 'name', 'sex', 'species', 'age', 'bodyType', 'personality', 'bio'];
  2673. fields.forEach(field => {
  2674. const fieldDiv = document.createElement('div');
  2675. fieldDiv.style.marginBottom = '10px';
  2676.  
  2677. const label = document.createElement('label');
  2678. label.style.color = 'var(--text-color)';
  2679. label.style.fontSize = '12px';
  2680. label.textContent = `Randomizer for ${capitalizeFirstLetter(field)}:`;
  2681.  
  2682. // Dropdown for default options
  2683. const select = document.createElement('select');
  2684. select.name = field;
  2685. select.style.cssText = 'background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding:5px; width: 100%; box-sizing: border-box;';
  2686. // Populate options
  2687. const options = defaultRandomizerOptions[field];
  2688. for (const optionName in options) {
  2689. const option = document.createElement('option');
  2690. option.value = optionName;
  2691. option.textContent = optionName;
  2692. if (randomizerSettings[field].selectedOption === optionName && !randomizerSettings[field].customList.trim()) {
  2693. option.selected = true;
  2694. }
  2695. select.appendChild(option);
  2696. }
  2697.  
  2698. // Custom list textarea
  2699. const textarea = document.createElement('textarea');
  2700. textarea.name = field + '_custom';
  2701. textarea.rows = field != 'bio' ? 2 : 4;
  2702. textarea.placeholder = 'Enter custom list, separated by "%" or new lines.';
  2703. textarea.style.cssText = 'background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding:5px; width: 100%; box-sizing: border-box; margin-top: 5px;';
  2704. textarea.value = randomizerSettings[field].customList;
  2705.  
  2706. // If custom list is non-empty, disable the select
  2707. if (randomizerSettings[field].customList.trim() !== '') {
  2708. select.disabled = true;
  2709. }
  2710.  
  2711. // Event listener to disable select if textarea has content
  2712. textarea.addEventListener('input', (e) => {
  2713. if (textarea.value.trim() !== '') {
  2714. select.disabled = true;
  2715. } else {
  2716. select.disabled = false;
  2717. }
  2718. });
  2719.  
  2720. fieldDiv.appendChild(label);
  2721. fieldDiv.appendChild(select);
  2722. fieldDiv.appendChild(textarea);
  2723. fieldsContainer.appendChild(fieldDiv);
  2724. });
  2725.  
  2726. const helpLabel = document.createElement('label');
  2727. helpLabel.style.color = 'var(--text-color)';
  2728. helpLabel.style.fontSize = '18px';
  2729. helpLabel.textContent = `Text codes for advanced list creation;`;
  2730. fieldsContainer.appendChild(helpLabel);
  2731.  
  2732. // Create a paragraph element for the help text
  2733. const helpText = document.createElement('p');
  2734.  
  2735. // Apply styling to the paragraph element
  2736. helpText.style.color = 'var(--text-color)';
  2737. helpText.style.fontSize = '12px';
  2738.  
  2739. // Set the content of the help text
  2740. helpText.innerHTML = `
  2741. <em>"%" marks the end of an item.<br>
  2742. "*n" Multiply the odds an item is chosen.<br>
  2743. "&!" Create a new sub list, index is assigned by order.<br>
  2744. "&?n" Get a random item from a sublist.<br>
  2745. "#?a" Get Character name (bio only)<br>
  2746. "#?b" Get character pronoun like "he" (bio only)<br>
  2747. "#?c" Get character pronoun like "him" (bio only)<br>
  2748. "#?d" Get character pronoun like "his" (bio only)<br>
  2749. ">>" Comment start.<br>
  2750. "<<" Comment end.<br></em>
  2751. `;
  2752.  
  2753. // Append the paragraph to the container
  2754. fieldsContainer.appendChild(helpText);
  2755.  
  2756. const cancelButton = settingsForm.querySelector('#cancel-button');
  2757. cancelButton.addEventListener('click', () => {
  2758. closeModal(settingsModal);
  2759. });
  2760.  
  2761. const settingsModal = createModal('Randomizer Settings', settingsForm);
  2762. settingsForm.addEventListener('submit', (e) => {
  2763. e.preventDefault();
  2764. // Update randomizerSettings
  2765. fields.forEach(field => {
  2766. const select = settingsForm[field];
  2767. const textarea = settingsForm[field + '_custom'];
  2768. randomizerSettings[field].customList = textarea.value.trim();
  2769. if (randomizerSettings[field].customList !== '') {
  2770. randomizerSettings[field].selectedOption = '';
  2771. } else {
  2772. randomizerSettings[field].selectedOption = select.value;
  2773. }
  2774. });
  2775. closeModal(settingsModal);
  2776. });
  2777.  
  2778. settingsForm.parentNode.style.backgroundColor = 'var(--bg-color-full)';
  2779. }
  2780.  
  2781. // Function to edit a character
  2782. function editCharacter(character) {
  2783. const lockState = {
  2784. emoji: false,
  2785. name: false,
  2786. sex: false,
  2787. species: false,
  2788. age: false,
  2789. bodyType: false,
  2790. personality: false,
  2791. bio: false
  2792. };
  2793.  
  2794. const editForm = document.createElement('form');
  2795. editForm.innerHTML = `
  2796. <div style="width: 100%; min-width: 500px; margin: 0 auto; position: relative;">
  2797. ${createField('Emoji', 'emoji', character.emoji)}
  2798. ${createAvatarField('Avatar', 'avatar', character.avatar)}
  2799. ${createField('Name', 'name', character.name)}
  2800. ${createSexDropdown('Sex', 'sex', character.sex)}
  2801. ${createField('Species', 'species', character.species)}
  2802. ${createField('Age', 'age', character.age)}
  2803. ${createField('Body Type', 'bodyType', character.bodyType)}
  2804. ${createField('Personality', 'personality', character.personality)}
  2805. ${createTextareaField('Bio', 'bio', character.bio)}
  2806. <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">Character Type:
  2807. <select name="characterType" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;">
  2808. <option value="0" ${character.characterType === 0 ? 'selected' : ''}>Non-User</option>
  2809. <option value="1" ${character.characterType === 1 ? 'selected' : ''}>User</option>
  2810. <option value="2" ${character.characterType === 2 ? 'selected' : ''}>Collection</option>
  2811. </select>
  2812. </label>
  2813. <button type="submit" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 5px; border-radius: 5px; width: 100%; text-align: center; margin-top: 10px;">Save</button>
  2814. </div>
  2815. `;
  2816.  
  2817. // Helper functions to create fields
  2818. function createField(labelText, fieldName, value) {
  2819. return `
  2820. <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">
  2821. ${labelText}:
  2822. <div style="position: relative;">
  2823. <input type="text" name="${fieldName}" value="${value}" autocomplete="off" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: calc(100% - 35px); box-sizing: border-box;"/>
  2824. <button type="button" class="lock-button" data-field="${fieldName}" style="position: absolute; right: 0; top: 0; height: 100%; width: 35px; background-color: var(--button-bg-color); border: 1px solid var(--border-color); border-radius: 5px; cursor: pointer; color: var(--text-color);">${lockState[fieldName] ? '🔒' : '🔓'}</button>
  2825. </div>
  2826. </label>
  2827. `;
  2828. }
  2829.  
  2830. function createAvatarField(labelText, fieldName, value) {
  2831. return `
  2832. <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">
  2833. ${labelText}:
  2834. <div style="position: relative;">
  2835. <input type="text" name="${fieldName}" value="${value}" autocomplete="off" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: calc(100% - 35px); box-sizing: border-box;" />
  2836. <button type="button" class="open-avatar-button" data-field="${fieldName}" style="position: absolute; right: 0; top: 0; height: 100%; width: 35px; background-color: var(--button-bg-color); border: 1px solid var(--border-color); border-radius: 5px; cursor: pointer; color: var(--text-color);">🔗</button>
  2837. </div>
  2838. </label>
  2839. `;
  2840. }
  2841.  
  2842. function createSexDropdown(labelText, fieldName, value) {
  2843. return `
  2844. <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">
  2845. ${labelText}:
  2846. <div style="position: relative;">
  2847. <select name="${fieldName}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: calc(100% - 35px); box-sizing: border-box;">
  2848. <option value="male" ${value === 'male' ? 'selected' : ''}>Male</option>
  2849. <option value="female" ${value === 'female' ? 'selected' : ''}>Female</option>
  2850. <option value="other" ${value === 'other' ? 'selected' : ''}>Other</option>
  2851. </select>
  2852. <button type="button" class="lock-button" data-field="${fieldName}" style="position: absolute; right: 0; top: 0; height: 100%; width: 35px; background-color: var(--button-bg-color); border: 1px solid var(--border-color); border-radius: 5px; cursor: pointer; color: var(--text-color);">${lockState[fieldName] ? '🔒' : '🔓'}</button>
  2853. </div>
  2854. </label>
  2855. `;
  2856. }
  2857.  
  2858. function createTextareaField(labelText, fieldName, value) {
  2859. return `
  2860. <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">
  2861. ${labelText}:
  2862. <div style="position: relative;">
  2863. <textarea name="${fieldName}" autocomplete="off" rows="4" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: calc(100% - 35px); box-sizing: border-box;">${value}</textarea>
  2864. <button type="button" class="lock-button" data-field="${fieldName}" style="position: absolute; right: 0; top: 0; height: 100%; width: 35px; background-color: var(--button-bg-color); border: 1px solid var(--border-color); border-radius: 5px; cursor: pointer; color: var(--text-color);">${lockState[fieldName] ? '🔒' : '🔓'}</button>
  2865. </div>
  2866. </label>
  2867. `;
  2868. }
  2869.  
  2870. // Copy Tooltip button
  2871. const copyButton = document.createElement('button');
  2872. copyButton.textContent = '📋';
  2873. copyButton.title = 'Copy Tooltip';
  2874. copyButton.type = 'button'; // Prevent form submission
  2875. copyButton.style.cssText = `
  2876. position: absolute;
  2877. top: 15px;
  2878. right: 50px;
  2879. background: none;
  2880. border: none;
  2881. font-size: 20px;
  2882. cursor: pointer;
  2883. color: var(--text-color);
  2884. `;
  2885. copyButton.addEventListener('click', () => {
  2886. const tooltipContent = getCharacterTooltipContent(character);
  2887. const textWithNewlines = tooltipContent.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]*>/g, '');
  2888. navigator.clipboard.writeText(textWithNewlines);
  2889. });
  2890. editForm.appendChild(copyButton);
  2891.  
  2892. // Randomize button
  2893. const randomizeButton = document.createElement('button');
  2894. randomizeButton.textContent = '🎲';
  2895. randomizeButton.title = 'Randomize';
  2896. randomizeButton.type = 'button'; // Prevent form submission
  2897. randomizeButton.style.cssText = `
  2898. position: absolute;
  2899. top: 15px;
  2900. right: 90px;
  2901. background: none;
  2902. border: none;
  2903. font-size: 20px;
  2904. cursor: pointer;
  2905. color: var(--text-color);
  2906. `;
  2907. randomizeButton.addEventListener('click', () => {
  2908. const randomCharacter = generateRandomCharacter();
  2909. const fields = ['emoji', 'name', 'sex', 'species', 'age', 'bodyType', 'personality', 'bio'];
  2910. fields.forEach((field) => {
  2911. if (!lockState[field]) {
  2912. editForm[field].value = randomCharacter[field];
  2913. }
  2914. });
  2915. });
  2916. editForm.appendChild(randomizeButton);
  2917.  
  2918. // Settings button
  2919. const settingsButton = document.createElement('button');
  2920. settingsButton.textContent = '⚙️';
  2921. settingsButton.title = 'Randomizer Settings';
  2922. settingsButton.type = 'button'; // Prevent form submission
  2923. settingsButton.style.cssText = `
  2924. position: absolute;
  2925. top: 15px;
  2926. right: 130px;
  2927. background: none;
  2928. border: none;
  2929. font-size: 20px;
  2930. cursor: pointer;
  2931. color: var(--text-color);
  2932. `;
  2933. settingsButton.addEventListener('click', () => {
  2934. openRandomizerSettings();
  2935. });
  2936. editForm.appendChild(settingsButton);
  2937.  
  2938. // Append the form to a modal or the desired container
  2939. const modal = createModal('Edit Character', editForm);
  2940.  
  2941. // Lock button functionality
  2942. const lockButtons = editForm.querySelectorAll('.lock-button');
  2943. lockButtons.forEach((button) => {
  2944. const fieldName = button.getAttribute('data-field');
  2945. button.addEventListener('click', () => {
  2946. lockState[fieldName] = !lockState[fieldName];
  2947. // Update button icon based on state
  2948. button.textContent = lockState[fieldName] ? '🔒' : '🔓';
  2949. });
  2950. });
  2951.  
  2952. // Open Avatar button functionality
  2953. const avatarButton = editForm.querySelector('.open-avatar-button');
  2954. avatarButton.addEventListener('click', () => {
  2955. const avatarUrl = editForm.avatar.value;
  2956. if (isValidImageUrl(avatarUrl)) {
  2957. window.open(avatarUrl, '_blank');
  2958. } else {
  2959. alert('Invalid image URL');
  2960. }
  2961. });
  2962.  
  2963. editForm.addEventListener('submit', (e) => {
  2964. e.preventDefault();
  2965. character.emoji = editForm.emoji.value;
  2966. character.avatar = editForm.avatar.value;
  2967. character.name = editForm.name.value;
  2968. character.sex = editForm.sex.value;
  2969. character.species = editForm.species.value;
  2970. character.age = editForm.age.value;
  2971. character.bodyType = editForm.bodyType.value;
  2972. character.personality = editForm.personality.value;
  2973. character.bio = editForm.bio.value;
  2974. character.characterType = parseInt(editForm.characterType.value, 10);
  2975. renderCharacterGroups();
  2976. closeModal(modal);
  2977. });
  2978. }
  2979.  
  2980. // Function to delete a character
  2981. function deleteCharacter(characterId) {
  2982. if (confirm('Are you sure you want to delete this character?')) {
  2983. characterGroups.forEach(group => {
  2984. group.items = deleteCharacterRecursive(group.items, characterId);
  2985. });
  2986. renderCharacterGroups();
  2987. }
  2988. }
  2989.  
  2990. function deleteCharacterRecursive(items, characterId) {
  2991. return items.filter(item => {
  2992. if (item.id === characterId) {
  2993. return false;
  2994. }
  2995. if (item.children && item.children.length > 0) {
  2996. item.children = deleteCharacterRecursive(item.children, characterId);
  2997. }
  2998. return true;
  2999. });
  3000. }
  3001.  
  3002. // Function to export all characters with groups
  3003. function exportCharacters() {
  3004. const data = {
  3005. characterGroups: characterGroups,
  3006. settings: { c_radius, c_labelFontSize } // Save current settings
  3007. };
  3008. const json = JSON.stringify(data, null, 2);
  3009. const blob = new Blob([json], { type: 'application/json' });
  3010. const url = URL.createObjectURL(blob);
  3011. const link = document.createElement('a');
  3012. link.href = url;
  3013. link.download = `all_characters_with_groups.json`;
  3014. link.click();
  3015. }
  3016.  
  3017. // Function to import characters with settings
  3018. function importCharacters() {
  3019. const fileInput = document.createElement('input');
  3020. fileInput.type = 'file';
  3021. fileInput.accept = '.json';
  3022. fileInput.addEventListener('change', async (event) => {
  3023. const file = event.target.files[0];
  3024. const reader = new FileReader();
  3025. reader.onload = (e) => {
  3026. try {
  3027. const importedData = JSON.parse(e.target.result);
  3028. if (Array.isArray(importedData)) {
  3029. // Old format
  3030. characterGroups = importedData;
  3031. renderCharacterGroups();
  3032. } else if (importedData.characterGroups) {
  3033. characterGroups = importedData.characterGroups;
  3034.  
  3035. // Load settings
  3036. if (importedData.settings) {
  3037. // Assign settings
  3038. c_radius = importedData.settings.c_radius || c_radius;
  3039. c_labelFontSize = importedData.settings.c_labelFontSize || c_labelFontSize;
  3040. }
  3041.  
  3042. renderCharacterGroups();
  3043. } else {
  3044. alert('Invalid format for characters import.');
  3045. }
  3046. } catch (error) {
  3047. alert('Failed to import characters: ' + error.message);
  3048. }
  3049. };
  3050. reader.readAsText(file);
  3051. });
  3052. fileInput.click();
  3053. }
  3054.  
  3055. // Function to export a single character
  3056. function exportCharacter(character) {
  3057. const json = JSON.stringify(character, null, 2);
  3058. const blob = new Blob([json], { type: 'application/json' });
  3059. const url = URL.createObjectURL(blob);
  3060. const link = document.createElement('a');
  3061. link.href = url;
  3062. link.download = `${character.name}.json`;
  3063. link.click();
  3064. }
  3065.  
  3066. // Function to move character to a group (uncoupling from parent)
  3067. function moveCharacterToGroup(characterId, targetGroupIndex) {
  3068. let character = null;
  3069. // Remove character from current group or parent
  3070. characterGroups.forEach(group => {
  3071. group.items = removeCharacterRecursive(group.items, characterId, (item) => {
  3072. character = item;
  3073. });
  3074. });
  3075. // Add to target group
  3076. if (character) {
  3077. characterGroups[targetGroupIndex].items.push(character);
  3078. renderCharacterGroups();
  3079. }
  3080. }
  3081.  
  3082. function removeCharacterRecursive(items, characterId, callback) {
  3083. return items.filter(item => {
  3084. if (item.id === characterId) {
  3085. callback(item);
  3086. return false;
  3087. }
  3088. if (item.children && item.children.length > 0) {
  3089. item.children = removeCharacterRecursive(item.children, characterId, callback);
  3090. }
  3091. return true;
  3092. });
  3093. }
  3094.  
  3095. // Function to move character under another character (nesting)
  3096. function moveCharacterToParent(characterId, parentCharacter) {
  3097. let character = null;
  3098. // Remove character from current group or parent
  3099. characterGroups.forEach(group => {
  3100. group.items = removeCharacterRecursive(group.items, characterId, (item) => {
  3101. character = item;
  3102. });
  3103. });
  3104. if (character) {
  3105. parentCharacter.children.push(character);
  3106. renderCharacterGroups();
  3107. }
  3108. }
  3109.  
  3110. // Utility function to generate unique IDs
  3111. function generateId() {
  3112. return '_' + Math.random().toString(36).substr(2, 9);
  3113. }
  3114.  
  3115. // --- Location Sidebar with Groups and Nested Locations ---
  3116. const locationSidebar = document.createElement('div');
  3117. locationSidebar.id = 'location-sheet-sidebar';
  3118. locationSidebar.style.cssText = `
  3119. position: fixed;
  3120. top: 0;
  3121. left: -350px;
  3122. width: 350px;
  3123. height: 100%;
  3124. display: flex;
  3125. flex-direction: column;
  3126. background-color: var(--bg-color);
  3127. border-right: 2px solid var(--border-color);
  3128. box-shadow: 2px 0 5px var(--shadow-color);
  3129. box-sizing: border-box;
  3130. transition: left 0.3s;
  3131. z-index: 9999;
  3132. `;
  3133. document.body.appendChild(locationSidebar);
  3134.  
  3135. const locationToggleButton = document.createElement('button');
  3136. locationToggleButton.textContent = '☰';
  3137. locationToggleButton.style.cssText = `
  3138. position: fixed;
  3139. top: 10px;
  3140. left: 10px;
  3141. padding: 5px 10px;
  3142. border: none;
  3143. background-color: var(--button-bg-color);
  3144. color: var(--text-color);
  3145. border-radius: 5px;
  3146. cursor: pointer;
  3147. transition: left 0.3s;
  3148. z-index: 10000;
  3149. `;
  3150. locationToggleButton.addEventListener('click', () => {
  3151. const isOpen = locationSidebar.style.left === '0px';
  3152. locationSidebar.style.left = isOpen ? '-350px' : '0';
  3153. locationToggleButton.style.left = isOpen ? '10px' : '360px';
  3154. });
  3155. document.body.appendChild(locationToggleButton);
  3156.  
  3157. const locationHeader = document.createElement('div');
  3158. locationHeader.style.cssText = `
  3159. padding: 10px;
  3160. background-color: var(--border-color);
  3161. text-align: center;
  3162. font-weight: bold;
  3163. color: var(--text-color);
  3164. `;
  3165. locationHeader.textContent = 'Locations';
  3166. // Add context menu functionality
  3167. locationHeader.addEventListener('contextmenu', (e) => {
  3168. e.preventDefault();
  3169. CreateContextMenu(
  3170. 'var(--bg-tool)',
  3171. 'var(--text-color)',
  3172. 'var(--border-color)',
  3173. ['🌐 Radial', '🔍 Search','ℹ️ Status', '📁 Group', '🗃️ Import', '⬇️ Export'],
  3174. [
  3175. () => {
  3176. if (radialTreeOpen) {
  3177. document.getElementById('radial-tree-window').remove();
  3178. radialTreeOpen = false;
  3179. }
  3180. const data = structureData(locationGroups);
  3181. createRadialTree(data, 'location');
  3182. },
  3183. () => toggleSearchPanel('location'),
  3184. () => openGlobalStatusPanel(),
  3185. () => createNewLocationGroup(),
  3186. () => importLocations(),
  3187. () => exportLocations(),
  3188. ]
  3189. );
  3190. showMenu(e);
  3191. });
  3192. locationSidebar.appendChild(locationHeader);
  3193.  
  3194. // Add fifth button: "Load" for Location
  3195. function loadLocation(index = -1) {
  3196. const fileInput = document.createElement('input');
  3197. fileInput.type = 'file';
  3198. fileInput.accept = '.json';
  3199. fileInput.addEventListener('change', async (event) => {
  3200. const file = event.target.files[0];
  3201. const reader = new FileReader();
  3202. reader.onload = (e) => {
  3203. try {
  3204. const location = JSON.parse(e.target.result);
  3205. if (location && location.id) {
  3206. // Add to Ungrouped
  3207. const ungrouped = locationGroups.find(g => g.name === 'Ungrouped');
  3208. if (index < 0 && ungrouped) {
  3209. ungrouped.items.push(location);
  3210. } else if (index >= locationGroups.length && ungrouped) {
  3211. ungrouped.items.push(location);
  3212. } else {
  3213. locationGroups[index].items.push(location);
  3214. }
  3215. renderLocationGroups();
  3216. ensureActiveLocation();
  3217. } else {
  3218. alert('Invalid location format.');
  3219. }
  3220. } catch (error) {
  3221. alert('Failed to load location: ' + error.message);
  3222. }
  3223. };
  3224. reader.readAsText(file);
  3225. });
  3226. fileInput.click();
  3227. }
  3228.  
  3229. const locationGroupList = document.createElement('div');
  3230. locationGroupList.id = 'location-group-list';
  3231. locationGroupList.style.cssText = `
  3232. flex-grow: 1;
  3233. overflow-y: auto;
  3234. padding: 10px;
  3235. `;
  3236. locationSidebar.appendChild(locationGroupList);
  3237.  
  3238. // Initialize Location Groups with a single default location
  3239. let locationGroups = [
  3240. {
  3241. name: 'Ungrouped',
  3242. items: [
  3243. {
  3244. id: generateId(),
  3245. active: true,
  3246. emoji: '📍',
  3247. reference: 'Url goes here...',
  3248. name: 'Default Location',
  3249. description: 'Description of the default location.',
  3250. children: [],
  3251. collapsed: false
  3252. }
  3253. ],
  3254. collapsed: false,
  3255.  
  3256. }
  3257. ];
  3258.  
  3259. // Function to create a new Location
  3260. function createNewLocation(index = -1) {
  3261. const newLocation = {
  3262. id: generateId(),
  3263. active: false,
  3264. emoji: '📍',
  3265. reference: 'Url Goes here...',
  3266. name: 'New Location',
  3267. description: '',
  3268. children: [],
  3269. collapsed: false
  3270. };
  3271. // Add to Ungrouped
  3272. const ungrouped = locationGroups.find(g => g.name === 'Ungrouped');
  3273. if (index < 0 && ungrouped) {
  3274. ungrouped.items.push(newLocation);
  3275. } else if (index >= locationGroups.length && ungrouped) {
  3276. ungrouped.items.push(newLocation);
  3277. } else {
  3278. locationGroups[index].items.push(newLocation);
  3279. }
  3280. renderLocationGroups();
  3281. ensureActiveLocation();
  3282. }
  3283.  
  3284. // Function to create a new Location Group
  3285. function createNewLocationGroup() {
  3286. const groupName = prompt('Enter group name:', `Group ${locationGroups.length}`);
  3287. if (groupName && groupName.trim() !== '') {
  3288. locationGroups.push({ name: groupName.trim(), items: [], collapsed: false });
  3289. renderLocationGroups();
  3290. }
  3291. }
  3292.  
  3293. // Function to render Location Groups and Items
  3294. function renderLocationGroups() {
  3295. locationGroupList.innerHTML = '';
  3296.  
  3297. locationGroups.forEach((group, groupIndex) => {
  3298. // Sort items alphabetically by name
  3299. group.items.sort((a, b) => a.name.localeCompare(b.name));
  3300.  
  3301. const groupContainer = document.createElement('div');
  3302. groupContainer.className = 'location-group';
  3303. groupContainer.style.cssText = `
  3304. margin-bottom: 10px;
  3305. border: 1px solid var(--border-color);
  3306. border-radius: 5px;
  3307. background-color: var(--active-char-color);
  3308. `;
  3309.  
  3310. const groupHeader = document.createElement('div');
  3311. groupHeader.style.cssText = `
  3312. display: flex;
  3313. justify-content: space-between;
  3314. align-items: center;
  3315. padding: 5px 10px;
  3316. background-color: var(--button-bg-color);
  3317. color: var(--text-color);
  3318. cursor: pointer;
  3319. user-select: none;
  3320. position: relative;
  3321. `;
  3322. groupHeader.textContent = group.name;
  3323. groupHeader.addEventListener('click', () => {
  3324. group.collapsed = !group.collapsed;
  3325. renderLocationGroups();
  3326. });
  3327.  
  3328. // Add context menu functionality
  3329. groupHeader.addEventListener('contextmenu', (e) => {
  3330. e.preventDefault();
  3331. CreateContextMenu(
  3332. 'var(--bg-tool)',
  3333. 'var(--text-color)',
  3334. 'var(--border-color)',
  3335. ['✏️ Rename','📝 Create', '🗃️ Import', '🗑️ Delete'],
  3336. [
  3337. () => renameLocationGroup(groupIndex),
  3338. () => createNewLocation(groupIndex),
  3339. () => loadLocation(groupIndex),
  3340. () => deleteLocationGroup(groupIndex),
  3341. ]
  3342. );
  3343. showMenu(e);
  3344. });
  3345.  
  3346. groupContainer.appendChild(groupHeader);
  3347.  
  3348. if (!group.collapsed) {
  3349. const itemsContainer = document.createElement('div');
  3350. itemsContainer.style.cssText = `
  3351. padding: 5px 10px;
  3352. display: block;
  3353. `;
  3354. if (group.items.length === 0) {
  3355. const emptyInfo = document.createElement('div');
  3356. emptyInfo.className = 'location-slot';
  3357. emptyInfo.style.cssText = `
  3358. display: flex;
  3359. align-items: center;
  3360. padding: 10px;
  3361. margin-bottom: 5px;
  3362. border: 1px solid var(--border-color);
  3363. border-radius: 5px;
  3364. height: 20px;
  3365. background-color: var(--bg-color);
  3366. cursor: default;
  3367. `;
  3368.  
  3369. const infoText = document.createElement('span');
  3370. infoText.textContent = `Group is empty`;
  3371. infoText.style.cssText = `
  3372. color: var(--text-color);
  3373. `;
  3374.  
  3375. emptyInfo.appendChild(infoText);
  3376. itemsContainer.appendChild(emptyInfo);
  3377. } else {
  3378. group.items.forEach((location) => {
  3379. const locationSlot = createLocationSlot(location, groupIndex);
  3380. itemsContainer.appendChild(locationSlot);
  3381. });
  3382. }
  3383. groupContainer.appendChild(itemsContainer);
  3384. } else {
  3385. const collapsedInfo = document.createElement('div');
  3386. collapsedInfo.className = 'location-slot';
  3387. collapsedInfo.style.cssText = `
  3388. display: flex;
  3389. align-items: center;
  3390. padding: 10px;
  3391. margin-bottom: 10px;
  3392. border: 1px solid var(--border-color);
  3393. border-radius: 5px;
  3394. height: 20px;
  3395. margin-top: 5px;
  3396. background-color: var(--bg-color);
  3397. cursor: default;
  3398. margin-left: 10px;
  3399. margin-right: 10px;
  3400. `;
  3401. const infoText = document.createElement('span');
  3402. infoText.textContent = `${group.items.length} items hidden`;
  3403.  
  3404. if (findActiveLocation(group.items)) {
  3405. infoText.style.cssText = `color: rgba(36, 242, 0, 0.75);`;
  3406. } else {
  3407. infoText.style.cssText = `color: var(--text-color-darker);`;
  3408. }
  3409.  
  3410. collapsedInfo.appendChild(infoText);
  3411. groupContainer.appendChild(collapsedInfo);
  3412. }
  3413.  
  3414. // Add dragover and drop events for grouping
  3415. groupContainer.addEventListener('dragover', (e) => {
  3416. e.preventDefault();
  3417. groupContainer.style.border = `2px dashed var(--info-bg-color)`;
  3418. });
  3419.  
  3420. groupContainer.addEventListener('dragleave', (e) => {
  3421. groupContainer.style.border = `1px solid var(--border-color)`;
  3422. });
  3423.  
  3424. groupContainer.addEventListener('drop', (e) => {
  3425. e.preventDefault();
  3426. groupContainer.style.border = `1px solid var(--border-color)`;
  3427. const data = e.dataTransfer.getData('text/plain');
  3428. const { type, id } = JSON.parse(data);
  3429. if (type === 'location') {
  3430. moveLocationToGroup(id, groupIndex);
  3431. }
  3432. });
  3433.  
  3434. locationGroupList.appendChild(groupContainer);
  3435. });
  3436.  
  3437. // Ensure at least one group exists
  3438. if (locationGroups.length === 0) {
  3439. locationGroups.push({ name: 'Ungrouped', items: [], collapsed: false });
  3440. renderLocationGroups();
  3441. }
  3442.  
  3443. // Ensure at least one location is active
  3444. ensureActiveLocation();
  3445. }
  3446.  
  3447. // Function to generate tooltip content for a location
  3448. function getLocationTooltipContent(location, isRadial = false) {
  3449. if (isRadial && location.emoji === '📁') {
  3450. // If it's a group node in the radial tree, only show the name
  3451. return `<strong>"${location.name}"</strong>`;
  3452. }
  3453. let content = `<strong>"${location.name}"</strong>:<br><em>"${location.description || 'No description available.'}"</em>`;
  3454. return content;
  3455. }
  3456.  
  3457. // Function to create a Location Slot DOM element (recursive)
  3458. function createLocationSlot(location, groupIndex, parent = null, level = 0) {
  3459. const locationSlot = document.createElement('div');
  3460. locationSlot.className = 'location-slot';
  3461. locationSlot.draggable = true;
  3462. locationSlot.dataset.id = location.id;
  3463. locationSlot.style.cssText = `
  3464. display: flex;
  3465. align-items: center;
  3466. justify-content: space-between;
  3467. padding: 5px;
  3468. margin-bottom: 5px;
  3469. border: 1px solid var(--border-color);
  3470. border-radius: 5px;
  3471. background-color: var(--bg-color);
  3472. cursor: grab;
  3473. margin-left: ${level * 20}px;
  3474. `;
  3475.  
  3476. // Drag events
  3477. locationSlot.addEventListener('dragstart', (e) => {
  3478. e.stopPropagation();
  3479. e.dataTransfer.setData('text/plain', JSON.stringify({ type: 'location', id: location.id }));
  3480. e.currentTarget.style.opacity = '0.5';
  3481. });
  3482.  
  3483. locationSlot.addEventListener('dragend', (e) => {
  3484. e.currentTarget.style.opacity = '1';
  3485. });
  3486.  
  3487. locationSlot.addEventListener('dragover', (e) => {
  3488. e.preventDefault();
  3489. e.stopPropagation();
  3490. locationSlot.style.border = `2px dashed var(--info-bg-color)`;
  3491. });
  3492.  
  3493. locationSlot.addEventListener('dragleave', (e) => {
  3494. locationSlot.style.border = `1px solid var(--border-color)`;
  3495. });
  3496.  
  3497. locationSlot.addEventListener('drop', (e) => {
  3498. e.preventDefault();
  3499. e.stopPropagation();
  3500. locationSlot.style.border = `1px solid var(--border-color)`;
  3501. const data = e.dataTransfer.getData('text/plain');
  3502. const { type, id } = JSON.parse(data);
  3503. if (type === 'location' && id !== location.id) {
  3504. moveLocationToParent(id, location);
  3505. }
  3506. });
  3507.  
  3508. // Context menu (right-click)
  3509. locationSlot.addEventListener('contextmenu', (e) => {
  3510. e.preventDefault();
  3511. CreateContextMenu(
  3512. 'var(--bg-tool)',
  3513. 'var(--text-color)',
  3514. 'var(--border-color)',
  3515. ['✏️ Edit', '🗑️ Delete', '⬇️ Export'],
  3516. [
  3517. () => editLocation(location),
  3518. () => deleteLocation(location.id),
  3519. () => exportLocation(location),
  3520. ]
  3521. );
  3522. showMenu(e);
  3523. });
  3524.  
  3525. // Left Div (Emoji, Name)
  3526. const leftDiv = document.createElement('div');
  3527. leftDiv.style.cssText = `
  3528. display: flex;
  3529. align-items: center;
  3530. flex-grow: 1;
  3531. overflow: hidden;
  3532. `;
  3533.  
  3534. const emojiSpan = document.createElement('span');
  3535. emojiSpan.textContent = location.emoji;
  3536. emojiSpan.style.marginRight = '5px';
  3537. leftDiv.appendChild(emojiSpan);
  3538.  
  3539. const nameSpan = document.createElement('span');
  3540. nameSpan.textContent = location.name;
  3541. nameSpan.style.cssText = `
  3542. flex-grow: 1;
  3543. min-width: 0;
  3544. overflow: hidden;
  3545. white-space: nowrap;
  3546. text-overflow: ellipsis;
  3547. color: var(--text-color);
  3548. `;
  3549. leftDiv.appendChild(nameSpan);
  3550.  
  3551. // Remove Collapse/Expand Button
  3552. // (Not needed as per new requirement)
  3553.  
  3554. const activeButton = document.createElement('button');
  3555. activeButton.textContent = location.active ? '🟢' : '🔴';
  3556. activeButton.title = 'Toggle Active';
  3557. activeButton.style.cssText = `
  3558. padding: 3px;
  3559. border: none;
  3560. background: none;
  3561. cursor: pointer;
  3562. color: var(--text-color);
  3563. margin-right: 5px;
  3564. `;
  3565. activeButton.addEventListener('click', (e) => {
  3566. e.stopPropagation();
  3567. // Since only one active location is allowed, deactivate others
  3568. locationGroups.forEach(group => {
  3569. group.items.forEach(loc => {
  3570. deactivateLocationRecursive(loc);
  3571. });
  3572. });
  3573. location.active = !location.active;
  3574. renderLocationGroups();
  3575. });
  3576. leftDiv.appendChild(activeButton);
  3577.  
  3578. locationSlot.appendChild(leftDiv);
  3579.  
  3580. // Recursive rendering of children
  3581. const container = document.createElement('div');
  3582. container.appendChild(locationSlot);
  3583.  
  3584. if (location.children && location.children.length > 0) {
  3585. if (!location.collapsed) {
  3586. location.children.forEach((child) => {
  3587. const childSlot = createLocationSlot(child, groupIndex, location, level + 1);
  3588. container.appendChild(childSlot);
  3589. });
  3590. } else {
  3591. const collapsedInfo = document.createElement('div');
  3592. collapsedInfo.className = 'location-slot';
  3593. collapsedInfo.style.cssText = `
  3594. display: flex;
  3595. align-items: center;
  3596. padding: 10px;
  3597. margin-bottom: 5px;
  3598. border: 1px solid var(--border-color);
  3599. border-radius: 5px;
  3600. background-color: var(--bg-color);
  3601. height: 20px;
  3602. cursor: default;
  3603. margin-left: ${(level + 1) * 20}px;
  3604. `;
  3605. const infoText = document.createElement('span');
  3606. infoText.textContent = `${location.children.length} items hidden`;
  3607.  
  3608. if (findActiveLocation(location.children)) {
  3609. infoText.style.cssText = `color: rgba(36, 242, 0, 0.75);`;
  3610. } else {
  3611. infoText.style.cssText = `color: var(--text-color-darker);`;
  3612. }
  3613.  
  3614. collapsedInfo.appendChild(infoText);
  3615. container.appendChild(collapsedInfo);
  3616. }
  3617. }
  3618.  
  3619. // Add tooltip event listeners
  3620. nameSpan.addEventListener('mouseenter', (e) => {
  3621. showTooltip(e, getLocationTooltipContent(location), false, location.reference);
  3622. });
  3623. nameSpan.addEventListener('mouseleave', hideTooltip);
  3624. nameSpan.addEventListener('dragover', hideTooltip);
  3625.  
  3626. return container;
  3627. }
  3628.  
  3629. function deactivateLocationRecursive(location) {
  3630. location.active = false;
  3631. if (location.children && location.children.length > 0) {
  3632. location.children.forEach(child => {
  3633. deactivateLocationRecursive(child);
  3634. });
  3635. }
  3636. }
  3637.  
  3638. // Function to rename a location group
  3639. function renameLocationGroup(groupIndex) {
  3640. const newName = prompt('Enter new group name:', locationGroups[groupIndex].name);
  3641. if (newName && newName.trim() !== '') {
  3642. locationGroups[groupIndex].name = newName.trim();
  3643. renderLocationGroups();
  3644. }
  3645. }
  3646.  
  3647. // Function to delete a location group
  3648. function deleteLocationGroup(groupIndex) {
  3649. if (locationGroups.length === 1) {
  3650. alert('At least one group must exist.');
  3651. return;
  3652. }
  3653. if (confirm(`Are you sure you want to delete the group "${locationGroups[groupIndex].name}"? All locations in this group will be moved to "Ungrouped".`)) {
  3654. const group = locationGroups.splice(groupIndex, 1)[0];
  3655. const ungrouped = locationGroups.find(g => g.name === 'Ungrouped');
  3656. if (ungrouped) {
  3657. ungrouped.items = ungrouped.items.concat(group.items);
  3658. } else {
  3659. locationGroups.unshift({ name: 'Ungrouped', items: group.items, collapsed: false });
  3660. }
  3661. renderLocationGroups();
  3662. }
  3663. }
  3664.  
  3665. // Default randomizer options for locations
  3666. const defaultRandomizerOptionsAlt = {
  3667. emoji: {
  3668. 'Buildings': "🏠%🏡%🏫%🏭%🏢%🏰%🏩",
  3669. 'Nature': "🌲%🌳%🌴%🌵%🌾%🌊%🌋",
  3670. // Add more categories if needed
  3671. },
  3672. name: {
  3673. 'Common': "Park%Museum%Library%Coffee Shop%Restaurant%Beach%Mountain%Lake%Forest%City",
  3674. 'Fantasy': "Dragon’s Lair%Enchanted Forest%Mystic Mountain%Crystal Lake%Forbidden City",
  3675. // Add more categories if needed
  3676. },
  3677. description: {
  3678. 'Standard': "A beautiful place to visit.%Known for its stunning views.%A popular spot among locals.%Rich in history and culture.%Famous for its delicious cuisine.%Home to many rare species.%A tranquil escape from the city.%A bustling hub of activity.%A hidden gem waiting to be explored.%An iconic landmark.",
  3679. // Add more categories if needed
  3680. },
  3681. };
  3682.  
  3683. // User's randomizer settings for locations
  3684. const randomizerSettingsAlt = {
  3685. emoji: {
  3686. selectedOption: 'Buildings',
  3687. customList: ''
  3688. },
  3689. name: {
  3690. selectedOption: 'Common',
  3691. customList: ''
  3692. },
  3693. description: {
  3694. selectedOption: 'Standard',
  3695. customList: ''
  3696. },
  3697. };
  3698.  
  3699. // Function to generate a random location
  3700. function generateRandomLocationAlt() {
  3701. const location = {};
  3702.  
  3703. // Helper to remove comments (>> ... <<) - only for string fields
  3704. function removeComments(text) {
  3705. if (typeof text === 'string') {
  3706. return text.replace(/>>.*?<<\s*/g, '');
  3707. }
  3708. return text; // If it's not a string, just return it unchanged
  3709. }
  3710.  
  3711. // Helper to process sub-lists (&!)
  3712. function processSubLists(items) {
  3713. const mainList = [];
  3714. const subLists = [];
  3715.  
  3716. items.forEach((item, index) => {
  3717. if (index === 0) {
  3718. // The first item is the main list
  3719. mainList.push(...item.split(/[%\n]+/).map(i => i.trim()).filter(Boolean));
  3720. } else {
  3721. // The remaining items are sub-lists, split by commas
  3722. const subListItems = item.split(/[%\n]+/).map(i => i.trim()).filter(Boolean);
  3723. subLists.push(subListItems);
  3724. }
  3725. });
  3726.  
  3727. return { mainList, subLists };
  3728. }
  3729.  
  3730. // Process weighting for string items with a "*n" suffix
  3731. function weightItems(list) {
  3732. return list.map(item => {
  3733. if (typeof item === 'string') {
  3734. const match = item.match(/^(.*)\*(\d+)$/);
  3735. if (match) {
  3736. return { value: match[1].trim(), weight: parseInt(match[2], 10) };
  3737. }
  3738. return { value: item.trim(), weight: 1 };
  3739. }
  3740. return { value: item, weight: 1 }; // For non-string items (e.g., age), no trimming
  3741. });
  3742. }
  3743.  
  3744. // Function to get random item based on weighted random
  3745. function getRandomFromWeightedList(list) {
  3746. const totalWeight = list.reduce((sum, item) => sum + item.weight, 0);
  3747. let random = Math.random() * totalWeight;
  3748. for (const item of list) {
  3749. if (random < item.weight) {
  3750. return item.value;
  3751. }
  3752. random -= item.weight;
  3753. }
  3754. return ''; // Fallback
  3755. }
  3756.  
  3757. // Function to handle recursive sub-list resolution and replace tokens
  3758. function resolveSubListReferences(text, subLists, location, recursionDepth = 0) {
  3759. const MAX_RECURSION_DEPTH = 10; // Prevent infinite recursion
  3760. if (recursionDepth > MAX_RECURSION_DEPTH) {
  3761. console.warn('Maximum recursion depth reached while resolving sub-lists.');
  3762. return text;
  3763. }
  3764.  
  3765. // Replace sub-list references like &?n with a random item from sub-list n
  3766. let resolvedText = text.replace(/&\?(\d+)/g, (match, listIndex) => {
  3767. const index = parseInt(listIndex, 10);
  3768. if (subLists[index] && subLists[index].length > 0) {
  3769. const subList = subLists[index];
  3770. const randomSubItem = getRandomFromWeightedList(weightItems(subList));
  3771. // Recursively resolve any sub-list references within the chosen sub-list item
  3772. return resolveSubListReferences(randomSubItem, subLists, location, recursionDepth + 1);
  3773. }
  3774. return ''; // If sub-list doesn't exist, return empty string
  3775. });
  3776.  
  3777. // After resolving sub-list references, replace tokens like #?a, #?b, etc.
  3778. // In this case, we only use #?a for the location name, but you can extend this
  3779. resolvedText = resolvedText.replace(/#\?a/g, location.name || '');
  3780.  
  3781. return resolvedText;
  3782. }
  3783.  
  3784. // Function to get a random item from a list, including handling sub-lists and weighting
  3785. function getRandomItem(field) {
  3786. let items = [];
  3787. if (randomizerSettingsAlt[field].customList.trim() !== '') {
  3788. // Use custom list
  3789. items = randomizerSettingsAlt[field].customList
  3790. .split(/[,\n]+/)
  3791. .map(item => item.trim())
  3792. .filter(item => item !== '');
  3793. } else {
  3794. // Use default list
  3795. const selectedOption = randomizerSettingsAlt[field].selectedOption;
  3796. items = defaultRandomizerOptionsAlt[field][selectedOption]
  3797. .split(/[,\n]+/)
  3798. .map(item => item.trim())
  3799. .filter(item => item !== '');
  3800. }
  3801.  
  3802. // Handle cases where items might be undefined or empty
  3803. if (!items || items.length === 0) {
  3804. return '';
  3805. }
  3806.  
  3807. // Remove comments (only applies to string fields)
  3808. items = items.map(removeComments);
  3809.  
  3810. // First, process the entire string before splitting by commas or newlines
  3811. const inputString = items.join(',');
  3812.  
  3813. // Split the input string on "&!" to separate sub-lists from the main text
  3814. const mainTextAndSubLists = inputString.split('&!');
  3815.  
  3816. // The main text is everything before the first "&!"
  3817. const mainListString = mainTextAndSubLists.shift(); // Get the main text
  3818. const subListStrings = mainTextAndSubLists; // Remaining parts are sub-lists
  3819.  
  3820. // Now process the main list and any sub-lists
  3821. const { mainList, subLists } = processSubLists([mainListString, ...subListStrings]);
  3822.  
  3823. // Get the random item from the main list
  3824. let result = getRandomFromWeightedList(weightItems(mainList));
  3825.  
  3826. // If the field is 'description', handle dynamic text insertion (e.g., location name)
  3827. if (field === 'description' && typeof result === 'string') {
  3828. // Resolve sub-list references and replace tokens for the description
  3829. result = resolveSubListReferences(result, subLists, location);
  3830. } else {
  3831. // For non-description fields, resolve sub-list references if they exist
  3832. result = resolveSubListReferences(result, subLists, location);
  3833. }
  3834.  
  3835. return result;
  3836. }
  3837.  
  3838. // Generate location fields
  3839. location.emoji = getRandomItem('emoji');
  3840. location.name = getRandomItem('name');
  3841. location.description = getRandomItem('description');
  3842.  
  3843. return location;
  3844. }
  3845.  
  3846. // Function to open Randomizer Settings dialog for locations
  3847. function openRandomizerSettingsAlt() {
  3848. const settingsForm = document.createElement('form');
  3849. settingsForm.innerHTML = `
  3850. <div style="display: flex; flex-direction: column; width: 100%; min-width: 500px; margin: 0 auto;">
  3851. <!-- Scrollable content container -->
  3852. <div class="scrollable-content" style="flex: 1; overflow-y: auto; max-height: 300px; padding-right: 10px;">
  3853. <!-- Fields will be inserted here dynamically -->
  3854. </div>
  3855. <!-- Buttons at the bottom -->
  3856. <div class="settings-buttons" style="text-align: center; padding-top: 10px;">
  3857. <button type="submit" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 8px 16px; border-radius: 5px; margin-right: 10px; cursor: pointer;">Save Settings</button>
  3858. <button type="button" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer;" id="cancel-button-alt">Cancel</button>
  3859. </div>
  3860. </div>
  3861. `;
  3862.  
  3863. const fieldsContainer = settingsForm.querySelector('.scrollable-content');
  3864.  
  3865. // For each randomizable field, create settings UI
  3866. const fields = ['emoji', 'name', 'description'];
  3867. fields.forEach(field => {
  3868. const fieldDiv = document.createElement('div');
  3869. fieldDiv.style.marginBottom = '10px';
  3870.  
  3871. const label = document.createElement('label');
  3872. label.style.color = 'var(--text-color)';
  3873. label.style.fontSize = '12px';
  3874. label.textContent = `Randomizer for ${capitalizeFirstLetter(field)}:`;
  3875.  
  3876. // Dropdown for default options
  3877. const select = document.createElement('select');
  3878. select.name = field;
  3879. select.style.cssText = 'background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding:5px; width: 100%; box-sizing: border-box;';
  3880. // Populate options
  3881. const options = defaultRandomizerOptionsAlt[field];
  3882. for (const optionName in options) {
  3883. const option = document.createElement('option');
  3884. option.value = optionName;
  3885. option.textContent = optionName;
  3886. if (randomizerSettingsAlt[field].selectedOption === optionName && !randomizerSettingsAlt[field].customList.trim()) {
  3887. option.selected = true;
  3888. }
  3889. select.appendChild(option);
  3890. }
  3891.  
  3892. // Custom list textarea
  3893. const textarea = document.createElement('textarea');
  3894. textarea.name = field + '_custom';
  3895. textarea.rows = field != 'description' ? 2 : 4;
  3896. textarea.placeholder = 'Enter custom list, separated by "%" or new lines.';
  3897. textarea.style.cssText = 'background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding:5px; width: 100%; box-sizing: border-box; margin-top: 5px;';
  3898. textarea.value = randomizerSettingsAlt[field].customList;
  3899.  
  3900. // If custom list is non-empty, disable the select
  3901. if (randomizerSettingsAlt[field].customList.trim() !== '') {
  3902. select.disabled = true;
  3903. }
  3904.  
  3905. // Event listener to disable select if textarea has content
  3906. textarea.addEventListener('input', (e) => {
  3907. if (textarea.value.trim() !== '') {
  3908. select.disabled = true;
  3909. } else {
  3910. select.disabled = false;
  3911. }
  3912. });
  3913.  
  3914. fieldDiv.appendChild(label);
  3915. fieldDiv.appendChild(select);
  3916. fieldDiv.appendChild(textarea);
  3917. fieldsContainer.appendChild(fieldDiv);
  3918. });
  3919.  
  3920. const helpLabel = document.createElement('label');
  3921. helpLabel.style.color = 'var(--text-color)';
  3922. helpLabel.style.fontSize = '18px';
  3923. helpLabel.textContent = `Text codes for advanced list creation;`;
  3924. fieldsContainer.appendChild(helpLabel);
  3925.  
  3926. // Create a paragraph element for the help text
  3927. const helpText = document.createElement('p');
  3928.  
  3929. // Apply styling to the paragraph element
  3930. helpText.style.color = 'var(--text-color)';
  3931. helpText.style.fontSize = '12px';
  3932.  
  3933. // Set the content of the help text
  3934. helpText.innerHTML = `
  3935. <em>"%" marks the end of an item.<br>
  3936. "*n" Multiply the odds an item is chosen.<br>
  3937. "&!" Create a new sub list, index is assigned by order.<br>
  3938. "&?n" Get a random item from a sublist.<br>
  3939. "#?a" Get Location name (Description only)<br>
  3940. ">>" Comment start.<br>
  3941. "<<" Comment end.<br></em>
  3942. `;
  3943.  
  3944. // Append the paragraph to the container
  3945. fieldsContainer.appendChild(helpText);
  3946.  
  3947. const cancelButton = settingsForm.querySelector('#cancel-button-alt');
  3948. cancelButton.addEventListener('click', () => {
  3949. closeModal(settingsModal);
  3950. });
  3951.  
  3952. const settingsModal = createModal('Randomizer Settings', settingsForm);
  3953. settingsForm.addEventListener('submit', (e) => {
  3954. e.preventDefault();
  3955. // Update randomizerSettingsAlt
  3956. fields.forEach(field => {
  3957. const select = settingsForm[field];
  3958. const textarea = settingsForm[field + '_custom'];
  3959. randomizerSettingsAlt[field].customList = textarea.value.trim();
  3960. if (randomizerSettingsAlt[field].customList !== '') {
  3961. randomizerSettingsAlt[field].selectedOption = '';
  3962. } else {
  3963. randomizerSettingsAlt[field].selectedOption = select.value;
  3964. }
  3965. });
  3966. closeModal(settingsModal);
  3967. });
  3968.  
  3969. settingsForm.parentNode.style.backgroundColor = 'var(--bg-color-full)';
  3970. }
  3971.  
  3972. // Function to edit a location with randomize feature
  3973. function editLocation(location) {
  3974. const lockState = {
  3975. emoji: false,
  3976. name: false,
  3977. description: false
  3978. };
  3979.  
  3980. const editForm = document.createElement('form');
  3981. editForm.innerHTML = `
  3982. <div style="width: 100%; min-width: 500px; margin: 0 auto; position: relative;">
  3983. ${createFieldAlt('Emoji', 'emoji', location.emoji)}
  3984. ${createReferenceField('Reference', 'reference', location.reference)}
  3985. ${createFieldAlt('Name', 'name', location.name)}
  3986. ${createTextareaFieldAlt('Description', 'description', location.description)}
  3987. <button type="submit" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 5px; border-radius: 5px; width: 100%; text-align: center; margin-top: 10px;">Save</button>
  3988. </div>
  3989. `;
  3990.  
  3991. // Helper functions to create fields with lock buttons
  3992. function createFieldAlt(labelText, fieldName, value) {
  3993. return `
  3994. <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">
  3995. ${labelText}:
  3996. <div style="position: relative;">
  3997. <input type="text" name="${fieldName}" value="${value}" autocomplete="off" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: calc(100% - 35px); box-sizing: border-box;"/>
  3998. <button type="button" class="lock-button-alt" data-field="${fieldName}" style="position: absolute; right: 0; top: 0; height: 100%; width: 35px; background-color: var(--button-bg-color); border: 1px solid var(--border-color); border-radius: 5px; cursor: pointer; color: var(--text-color);">${lockState[fieldName] ? '🔒' : '🔓'}</button>
  3999. </div>
  4000. </label>
  4001. `;
  4002. }
  4003.  
  4004. function createReferenceField(labelText, fieldName, value) {
  4005. return `
  4006. <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">
  4007. ${labelText}:
  4008. <div style="position: relative;">
  4009. <input type="text" name="${fieldName}" value="${value}" autocomplete="off" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: calc(100% - 35px); box-sizing: border-box;" />
  4010. <button type="button" class="open-reference-button" data-field="${fieldName}" style="position: absolute; right: 0; top: 0; height: 100%; width: 35px; background-color: var(--button-bg-color); border: 1px solid var(--border-color); border-radius: 5px; cursor: pointer; color: var(--text-color);">🔗</button>
  4011. </div>
  4012. </label>
  4013. `;
  4014. }
  4015.  
  4016. function createTextareaFieldAlt(labelText, fieldName, value) {
  4017. return `
  4018. <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">
  4019. ${labelText}:
  4020. <div style="position: relative;">
  4021. <textarea name="${fieldName}" autocomplete="off" rows="4" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: calc(100% - 35px); box-sizing: border-box;">${value}</textarea>
  4022. <button type="button" class="lock-button-alt" data-field="${fieldName}" style="position: absolute; right: 0; top: 0; height: 100%; width: 35px; background-color: var(--button-bg-color); border: 1px solid var(--border-color); border-radius: 5px; cursor: pointer; color: var(--text-color);">${lockState[fieldName] ? '🔒' : '🔓'}</button>
  4023. </div>
  4024. </label>
  4025. `;
  4026. }
  4027.  
  4028. // Copy Tooltip button
  4029. const copyButton = document.createElement('button');
  4030. copyButton.textContent = '📋';
  4031. copyButton.title = 'Copy Tooltip';
  4032. copyButton.type = 'button'; // Prevent form submission
  4033. copyButton.style.cssText = `
  4034. position: absolute;
  4035. top: 15px;
  4036. right: 50px;
  4037. background: none;
  4038. border: none;
  4039. font-size: 20px;
  4040. cursor: pointer;
  4041. color: var(--text-color);
  4042. `;
  4043. copyButton.addEventListener('click', () => {
  4044. const tooltipContent = getLocationTooltipContent(location);
  4045. const textWithNewlines = tooltipContent.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]*>/g, '');
  4046. navigator.clipboard.writeText(textWithNewlines);
  4047. });
  4048. editForm.appendChild(copyButton);
  4049.  
  4050. // Randomize button
  4051. const randomizeButton = document.createElement('button');
  4052. randomizeButton.textContent = '🎲';
  4053. randomizeButton.title = 'Randomize';
  4054. randomizeButton.type = 'button'; // Prevent form submission
  4055. randomizeButton.style.cssText = `
  4056. position: absolute;
  4057. top: 15px;
  4058. right: 90px;
  4059. background: none;
  4060. border: none;
  4061. font-size: 20px;
  4062. cursor: pointer;
  4063. color: var(--text-color);
  4064. `;
  4065. randomizeButton.addEventListener('click', () => {
  4066. const randomLocation = generateRandomLocationAlt();
  4067. const fields = ['emoji', 'name', 'description'];
  4068. fields.forEach((field) => {
  4069. if (!lockState[field]) {
  4070. editForm[field].value = randomLocation[field];
  4071. }
  4072. });
  4073. });
  4074. editForm.appendChild(randomizeButton);
  4075.  
  4076. // Settings button
  4077. const settingsButton = document.createElement('button');
  4078. settingsButton.textContent = '⚙️';
  4079. settingsButton.title = 'Randomizer Settings';
  4080. settingsButton.type = 'button'; // Prevent form submission
  4081. settingsButton.style.cssText = `
  4082. position: absolute;
  4083. top: 15px;
  4084. right: 130px;
  4085. background: none;
  4086. border: none;
  4087. font-size: 20px;
  4088. cursor: pointer;
  4089. color: var(--text-color);
  4090. `;
  4091. settingsButton.addEventListener('click', () => {
  4092. openRandomizerSettingsAlt();
  4093. });
  4094. editForm.appendChild(settingsButton);
  4095.  
  4096. const modal = createModal('Edit Location', editForm);
  4097.  
  4098. // Lock button functionality
  4099. const lockButtons = editForm.querySelectorAll('.lock-button-alt');
  4100. lockButtons.forEach((button) => {
  4101. const fieldName = button.getAttribute('data-field');
  4102. button.addEventListener('click', () => {
  4103. lockState[fieldName] = !lockState[fieldName];
  4104. // Update button icon based on state
  4105. button.textContent = lockState[fieldName] ? '🔒' : '🔓';
  4106. });
  4107. });
  4108.  
  4109. // Open Reference button functionality
  4110. const referenceButton = editForm.querySelector('.open-reference-button');
  4111. referenceButton.addEventListener('click', () => {
  4112. const referenceUrl = editForm.reference.value;
  4113. if (isValidImageUrl(referenceUrl)) {
  4114. window.open(referenceUrl, '_blank');
  4115. } else {
  4116. alert('Invalid image URL');
  4117. }
  4118. });
  4119.  
  4120. editForm.addEventListener('submit', (e) => {
  4121. e.preventDefault();
  4122. location.emoji = editForm.emoji.value;
  4123. location.reference = editForm.reference.value;
  4124. location.name = editForm.name.value;
  4125. location.description = editForm.description.value;
  4126. renderLocationGroups();
  4127. ensureActiveLocation();
  4128. closeModal(modal);
  4129. });
  4130. }
  4131.  
  4132. // Function to delete a location
  4133. function deleteLocation(locationId) {
  4134. if (confirm('Are you sure you want to delete this location?')) {
  4135. locationGroups.forEach(group => {
  4136. group.items = deleteLocationRecursive(group.items, locationId);
  4137. });
  4138. renderLocationGroups();
  4139. ensureActiveLocation();
  4140. }
  4141. }
  4142.  
  4143. function deleteLocationRecursive(items, locationId) {
  4144. return items.filter(item => {
  4145. if (item.id === locationId) {
  4146. return false;
  4147. }
  4148. if (item.children && item.children.length > 0) {
  4149. item.children = deleteLocationRecursive(item.children, locationId);
  4150. }
  4151. return true;
  4152. });
  4153. }
  4154.  
  4155. // Function to export all locations with groups
  4156. function exportLocations() {
  4157. const data = {
  4158. locationGroups: locationGroups,
  4159. settings: { l_radius, l_labelFontSize } // Save current settings
  4160. };
  4161. const json = JSON.stringify(data, null, 2);
  4162. const blob = new Blob([json], { type: 'application/json' });
  4163. const url = URL.createObjectURL(blob);
  4164. const link = document.createElement('a');
  4165. link.href = url;
  4166. link.download = `all_locations_with_groups.json`;
  4167. link.click();
  4168. }
  4169.  
  4170. // Function to import locations with groups
  4171. function importLocations() {
  4172. const fileInput = document.createElement('input');
  4173. fileInput.type = 'file';
  4174. fileInput.accept = '.json';
  4175. fileInput.addEventListener('change', async (event) => {
  4176. const file = event.target.files[0];
  4177. const reader = new FileReader();
  4178. reader.onload = (e) => {
  4179. try {
  4180. const importedData = JSON.parse(e.target.result);
  4181. if (Array.isArray(importedData)) {
  4182. // Old format
  4183. locationGroups = importedData;
  4184. renderLocationGroups();
  4185. } else if (importedData.locationGroups) {
  4186. locationGroups = importedData.locationGroups;
  4187.  
  4188. // Load settings
  4189. if (importedData.settings) {
  4190. // Assign settings
  4191. l_radius = importedData.settings.l_radius || l_radius;
  4192. l_labelFontSize = importedData.settings.l_labelFontSize || l_labelFontSize;
  4193. }
  4194.  
  4195. renderLocationGroups();
  4196. } else {
  4197. alert('Invalid format for characters import.');
  4198. }
  4199. } catch (error) {
  4200. alert('Failed to import characters: ' + error.message);
  4201. }
  4202. };
  4203. reader.readAsText(file);
  4204. });
  4205. fileInput.click();
  4206. }
  4207.  
  4208. // Function to export a single location
  4209. function exportLocation(location) {
  4210. const json = JSON.stringify(location, null, 2);
  4211. const blob = new Blob([json], { type: 'application/json' });
  4212. const url = URL.createObjectURL(blob);
  4213. const link = document.createElement('a');
  4214. link.href = url;
  4215. link.download = `${location.name}.json`;
  4216. link.click();
  4217. }
  4218.  
  4219. // Function to move location to a group (uncoupling from parent)
  4220. function moveLocationToGroup(locationId, targetGroupIndex) {
  4221. let location = null;
  4222. // Remove location from current group or parent
  4223. locationGroups.forEach(group => {
  4224. group.items = removeLocationRecursive(group.items, locationId, (item) => {
  4225. location = item;
  4226. });
  4227. });
  4228. // Add to target group
  4229. if (location) {
  4230. locationGroups[targetGroupIndex].items.push(location);
  4231. renderLocationGroups();
  4232. ensureActiveLocation();
  4233. }
  4234. }
  4235.  
  4236. function removeLocationRecursive(items, locationId, callback) {
  4237. return items.filter(item => {
  4238. if (item.id === locationId) {
  4239. callback(item);
  4240. return false;
  4241. }
  4242. if (item.children && item.children.length > 0) {
  4243. item.children = removeLocationRecursive(item.children, locationId, callback);
  4244. }
  4245. return true;
  4246. });
  4247. }
  4248.  
  4249. // Function to move location under another location (nesting)
  4250. function moveLocationToParent(locationId, parentLocation) {
  4251. let location = null;
  4252. // Remove location from current group or parent
  4253. locationGroups.forEach(group => {
  4254. group.items = removeLocationRecursive(group.items, locationId, (item) => {
  4255. location = item;
  4256. });
  4257. });
  4258. if (location) {
  4259. if(parentLocation.children == undefined){
  4260. parentLocation.children = [];
  4261. }
  4262. parentLocation.children.push(location);
  4263. renderLocationGroups();
  4264. ensureActiveLocation();
  4265. }
  4266. }
  4267.  
  4268. // Function to create a modal
  4269. function createModal(title, content) {
  4270. const modalOverlay = document.createElement('div');
  4271. modalOverlay.style.cssText = `
  4272. position: fixed;
  4273. top: 0;
  4274. left: 0;
  4275. width: 100%;
  4276. height: 100%;
  4277. background-color: rgba(0,0,0,0.5);
  4278. display: flex;
  4279. justify-content: center;
  4280. align-items: center;
  4281. z-index: 10002;
  4282. `;
  4283.  
  4284. const modalBox = document.createElement('div');
  4285. modalBox.style.cssText = `
  4286. background-color: var(--bg-color);
  4287. padding: 20px;
  4288. border: 1px solid var(--border-color);
  4289. border-radius: 5px;
  4290. width: fit-content;
  4291. min-width: 500px;
  4292. max-width: 90%;
  4293. box-shadow: 0 0 10px var(--shadow-color);
  4294. position: relative;
  4295. `;
  4296.  
  4297. const modalTitle = document.createElement('h2');
  4298. modalTitle.textContent = title;
  4299. modalTitle.style.cssText = `
  4300. margin-top: 0;
  4301. color: var(--text-color);
  4302. `;
  4303. modalBox.appendChild(modalTitle);
  4304.  
  4305. const closeButton = document.createElement('button');
  4306. closeButton.textContent = '✖️';
  4307. closeButton.style.cssText = `
  4308. position: absolute;
  4309. top: 15px;
  4310. right: 20px;
  4311. background: none;
  4312. border: none;
  4313. font-size: 20px;
  4314. cursor: pointer;
  4315. color: var(--text-color);
  4316. `;
  4317. closeButton.addEventListener('click', () => {
  4318. closeModal(modalOverlay);
  4319. });
  4320. modalBox.appendChild(closeButton);
  4321.  
  4322. modalBox.appendChild(content);
  4323. modalOverlay.appendChild(modalBox);
  4324. document.body.appendChild(modalOverlay);
  4325.  
  4326. return modalOverlay;
  4327. }
  4328.  
  4329. // Function to close a modal
  4330. function closeModal(modal) {
  4331. if (modal && modal.parentNode) {
  4332. modal.parentNode.removeChild(modal);
  4333. }
  4334. }
  4335.  
  4336. // Function to ensure at least one location is active
  4337. function ensureActiveLocation() {
  4338. const activeLocations = [];
  4339. locationGroups.forEach(group => {
  4340. group.items.forEach(loc => {
  4341. collectActiveLocations(loc, activeLocations);
  4342. });
  4343. });
  4344. if (activeLocations.length === 0 && locationGroups.flatMap(g => g.items).length > 0) {
  4345. locationGroups[0].items[0].active = true;
  4346. }
  4347. }
  4348.  
  4349. function collectActiveLocations(location, activeLocations) {
  4350. if (location.active) {
  4351. activeLocations.push(location);
  4352. }
  4353. if (location.children && location.children.length > 0) {
  4354. location.children.forEach(child => {
  4355. collectActiveLocations(child, activeLocations);
  4356. });
  4357. }
  4358. }
  4359.  
  4360. // Initial render of Character and Location Groups
  4361. renderCharacterGroups();
  4362. renderLocationGroups();
  4363.  
  4364. // Load D3.js library
  4365. await new Promise((resolve, reject) => {
  4366. const d3Script = document.createElement('script');
  4367. d3Script.src = 'https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.4/d3.min.js';
  4368. d3Script.onload = resolve;
  4369. d3Script.onerror = reject;
  4370. document.head.appendChild(d3Script);
  4371. });
  4372.  
  4373. // --- Modifications Start Here ---
  4374.  
  4375. // Variables to keep track of open windows
  4376. let radialTreeOpen = false;
  4377. let searchPanelOpen = { character: false, location: false };
  4378.  
  4379. // Helper function to find an item by ID
  4380. function findItemById(groups, id) {
  4381. let result = null;
  4382. for (let group of groups) {
  4383. if (group.id === id) {
  4384. return group;
  4385. }
  4386. if (group.items) {
  4387. result = findItemInItems(group.items, id);
  4388. if (result) {
  4389. return result;
  4390. }
  4391. }
  4392. }
  4393. return null;
  4394. }
  4395.  
  4396. function findItemInItems(items, id) {
  4397. for (let item of items) {
  4398. if (item.id === id) {
  4399. return item;
  4400. } else if (item.children && item.children.length > 0) {
  4401. let found = findItemInItems(item.children, id);
  4402. if (found) return found;
  4403. }
  4404. }
  4405. return null;
  4406. }
  4407.  
  4408. function findGroupIndexByName(groups, name) {
  4409. for (let i = 0; i < groups.length; i++) {
  4410. if (groups[i].name === name) {
  4411. return i;
  4412. }
  4413. }
  4414. return null;
  4415. }
  4416.  
  4417. // Helper function to remove an item by ID
  4418. function removeItemById(groups, id) {
  4419. for (let group of groups) {
  4420. if (group.items) {
  4421. group.items = removeItemFromItems(group.items, id);
  4422. }
  4423. }
  4424. }
  4425.  
  4426. function removeItemFromItems(items, id) {
  4427. return items.filter(item => {
  4428. if (item.id === id) {
  4429. return false;
  4430. } else if (item.children && item.children.length > 0) {
  4431. item.children = removeItemFromItems(item.children, id);
  4432. }
  4433. return true;
  4434. });
  4435. }
  4436.  
  4437. // Helper function to check for circular references
  4438. function isAncestor(itemId, possibleAncestorId, type) {
  4439. let targetItem = null;
  4440. if (type === 'character') {
  4441. targetItem = findItemById(characterGroups, itemId);
  4442. } else {
  4443. targetItem = findItemById(locationGroups, itemId);
  4444. }
  4445.  
  4446. if (!targetItem) return false;
  4447.  
  4448. function searchChildren(item) {
  4449. if (item.id === possibleAncestorId) {
  4450. return true;
  4451. }
  4452. if (item.children && item.children.length > 0) {
  4453. for (let child of item.children) {
  4454. if (searchChildren(child)) {
  4455. return true;
  4456. }
  4457. }
  4458. }
  4459. if (item.items && item.items.length > 0) {
  4460. for (let child of item.items) {
  4461. if (searchChildren(child)) {
  4462. return true;
  4463. }
  4464. }
  4465. }
  4466. return false;
  4467. }
  4468.  
  4469. return searchChildren(targetItem);
  4470. }
  4471.  
  4472. // Function to create a radial tree visualization
  4473. function createRadialTree(data, type) {
  4474. // Variables to store settings and use them globally
  4475. c_radius = type === 'character' ? (window.c_radius || 300) : 300;
  4476. c_labelFontSize = type === 'character' ? (window.c_labelFontSize || 12) : 12;
  4477. c_nodeSize = type === 'character' ? (window.c_nodeSize || 24) : 24;
  4478. l_radius = type === 'location' ? (window.l_radius || 300) : 300;
  4479. l_labelFontSize = type === 'location' ? (window.l_labelFontSize || 12) : 12;
  4480. l_nodeSize = type === 'location' ? (window.l_nodeSize || 24) : 24;
  4481.  
  4482. ItemTransferTarget = null;
  4483.  
  4484. // Close any existing radial tree window
  4485. if (document.getElementById('radial-tree-window')) {
  4486. document.body.removeChild(document.getElementById('radial-tree-window'));
  4487. }
  4488.  
  4489. // Create the movable window container
  4490. const windowContainer = document.createElement('div');
  4491. windowContainer.id = 'radial-tree-window';
  4492. windowContainer.style.cssText = `
  4493. position: fixed;
  4494. top: 100px;
  4495. left: 100px;
  4496. width: 600px;
  4497. height: 600px;
  4498. background-color: var(--bg-color);
  4499. border: 1px solid var(--border-color);
  4500. box-shadow: 0 0 10px var(--shadow-color);
  4501. border-radius: 5px;
  4502. z-index: 10002;
  4503. display: flex;
  4504. flex-direction: column;
  4505. `;
  4506.  
  4507. // Add a header with the title and buttons
  4508. const header = document.createElement('div');
  4509. header.style.cssText = `
  4510. display: flex;
  4511. align-items: center;
  4512. padding: 5px;
  4513. background-color: var(--button-bg-color);
  4514. color: var(--text-color);
  4515. cursor: move;
  4516. `;
  4517.  
  4518. const title = document.createElement('span');
  4519. title.textContent = `${type === 'character' ? 'Character' : 'Location'} Radial Tree`;
  4520. title.style.flexGrow = '1';
  4521. header.appendChild(title);
  4522.  
  4523. const buttonContainer = document.createElement('div');
  4524. buttonContainer.style.display = 'flex';
  4525. buttonContainer.style.alignItems = 'center';
  4526.  
  4527. // New Refresh Button
  4528. const NItemButton = document.createElement('button');
  4529. NItemButton.textContent = '📝';
  4530. NItemButton.title = 'New Item';
  4531. NItemButton.style.cssText = `
  4532. background: none;
  4533. border: none;
  4534. color: var(--text-color);
  4535. cursor: pointer;
  4536. margin-right: 5px;
  4537. `;
  4538. buttonContainer.appendChild(NItemButton);
  4539.  
  4540. NItemButton.addEventListener('click', () => {
  4541. if(type === 'character'){createNewCharacter();}else{createNewLocation();}
  4542. ItemTransferTarget = null;
  4543. refreshTree(svgContainer, data, type);
  4544. });
  4545.  
  4546. // New Refresh Button
  4547. const NGroupButton = document.createElement('button');
  4548. NGroupButton.textContent = '🗄️';
  4549. NGroupButton.title = 'New Group';
  4550. NGroupButton.style.cssText = `
  4551. background: none;
  4552. border: none;
  4553. color: var(--text-color);
  4554. cursor: pointer;
  4555. margin-right: 5px;
  4556. `;
  4557. buttonContainer.appendChild(NGroupButton);
  4558.  
  4559. NGroupButton.addEventListener('click', () => {
  4560. if(type === 'character'){createNewCharacterGroup();}else{createNewLocationGroup();}
  4561. ItemTransferTarget = null;
  4562. refreshTree(svgContainer, data, type);
  4563. });
  4564.  
  4565. // New Refresh Button
  4566. const refreshButton = document.createElement('button');
  4567. refreshButton.textContent = '🔄';
  4568. refreshButton.title = 'Refresh Tree';
  4569. refreshButton.style.cssText = `
  4570. background: none;
  4571. border: none;
  4572. color: var(--text-color);
  4573. cursor: pointer;
  4574. margin-right: 5px;
  4575. `;
  4576. buttonContainer.appendChild(refreshButton);
  4577.  
  4578. // Settings Button
  4579. const settingsButton = document.createElement('button');
  4580. settingsButton.textContent = '⚙️';
  4581. settingsButton.title = 'Settings';
  4582. settingsButton.style.cssText = `
  4583. background: none;
  4584. border: none;
  4585. color: var(--text-color);
  4586. cursor: pointer;
  4587. margin-right: 5px;
  4588. `;
  4589. buttonContainer.appendChild(settingsButton);
  4590.  
  4591. // Close Button
  4592. const closeButton = document.createElement('button');
  4593. closeButton.textContent = '✖️';
  4594. closeButton.style.cssText = `
  4595. background: none;
  4596. border: none;
  4597. color: var(--text-color);
  4598. cursor: pointer;
  4599. `;
  4600. closeButton.addEventListener('click', () => {
  4601. document.body.removeChild(windowContainer);
  4602. radialTreeOpen = false;
  4603. ItemTransferTarget = null;
  4604. });
  4605. buttonContainer.appendChild(closeButton);
  4606.  
  4607. header.appendChild(buttonContainer);
  4608. windowContainer.appendChild(header);
  4609.  
  4610. // Create SVG container
  4611. const svgContainer = document.createElement('div');
  4612. svgContainer.style.cssText = `
  4613. flex-grow: 1;
  4614. width: 100%;
  4615. background-color: var(--bg-color);
  4616. overflow: hidden;
  4617. position: relative;
  4618. `;
  4619. windowContainer.appendChild(svgContainer);
  4620.  
  4621. // Append to body
  4622. document.body.appendChild(windowContainer);
  4623. radialTreeOpen = true;
  4624.  
  4625. // Make window draggable
  4626. makeElementDraggable(windowContainer, header);
  4627.  
  4628. // Create settings panel
  4629. const settingsPanel = document.createElement('div');
  4630. settingsPanel.style.cssText = `
  4631. position: absolute;
  4632. top: 50px;
  4633. right: 10px;
  4634. width: 200px;
  4635. background-color: var(--bg-color);
  4636. border: 1px solid var(--border-color);
  4637. box-shadow: 0 0 10px var(--shadow-color);
  4638. border-radius: 5px;
  4639. padding: 10px;
  4640. z-index: 10003;
  4641. display: none;
  4642. `;
  4643. settingsPanel.innerHTML = `
  4644. <label style="color: var(--text-color);">Node Distance: <input type="range" min="100" max="1000" value="${type === 'character' ? c_radius : l_radius}" id="nodeDistanceSlider" style="width: 100%;"></label>
  4645. <label style="color: var(--text-color);">Label Size: <input type="range" min="8" max="24" value="${type === 'character' ? c_labelFontSize : l_labelFontSize}" id="labelSizeSlider" style="width: 100%;"></label>
  4646. <label style="color: var(--text-color);">Node Size: <input type="range" min="8" max="48" value="${type === 'character' ? c_nodeSize : l_nodeSize}" id="nodeSizeSlider" style="width: 100%;"></label>
  4647. <button id="applySettingsButton" style="margin-top: 10px; width: 100%;">Apply</button>
  4648. `;
  4649. windowContainer.appendChild(settingsPanel);
  4650.  
  4651. settingsButton.addEventListener('click', () => {
  4652. settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'block' : 'none';
  4653. ItemTransferTarget = null;
  4654. refreshTree(svgContainer, data, type);
  4655. });
  4656.  
  4657. const nodeDistanceSlider = settingsPanel.querySelector('#nodeDistanceSlider');
  4658. const labelSizeSlider = settingsPanel.querySelector('#labelSizeSlider');
  4659. const nodeSizeSlider = settingsPanel.querySelector('#nodeSizeSlider');
  4660. const applySettingsButton = settingsPanel.querySelector('#applySettingsButton');
  4661.  
  4662. applySettingsButton.addEventListener('click', () => {
  4663. if (type === 'character') {
  4664. window.c_radius = parseInt(nodeDistanceSlider.value, 10);
  4665. window.c_labelFontSize = parseInt(labelSizeSlider.value, 10);
  4666. window.c_nodeSize = parseInt(nodeSizeSlider.value, 10);
  4667. } else {
  4668. window.l_radius = parseInt(nodeDistanceSlider.value, 10);
  4669. window.l_labelFontSize = parseInt(labelSizeSlider.value, 10);
  4670. window.l_nodeSize = parseInt(nodeSizeSlider.value, 10);
  4671. }
  4672.  
  4673. settingsPanel.style.display = 'none';
  4674.  
  4675. // Regenerate the tree with new settings
  4676. generateRadialTreeVisualization(svgContainer, data, type, type === 'character' ? window.c_radius : window.l_radius, type === 'character' ? window.c_labelFontSize : window.l_labelFontSize, type === 'character' ? window.c_nodeSize : window.l_nodeSize);
  4677. });
  4678.  
  4679. // Add event listener to refresh button
  4680. refreshButton.addEventListener('click', () => {
  4681. ItemTransferTarget = null;
  4682. refreshTree(svgContainer, data, type);
  4683. });
  4684.  
  4685. // Initial rendering of the tree
  4686. generateRadialTreeVisualization(svgContainer, data, type, type === 'character' ? c_radius : l_radius, type === 'character' ? c_labelFontSize : l_labelFontSize, type === 'character' ? c_nodeSize : l_nodeSize);
  4687. }
  4688.  
  4689. function refreshTree(container, data, type) {
  4690. // Clear the existing SVG container (to remove the old tree)
  4691. container.innerHTML = '';
  4692.  
  4693. // Re-use the same data, or if necessary, fetch new data here
  4694. const structuredData = structureData(type === 'character' ? characterGroups : locationGroups);
  4695.  
  4696. // Store the current zoom, pan, and rotation before refresh
  4697. const currentTransform = window.currentTransform || { x: container.clientWidth / 2, y: container.clientHeight / 2, k: 1 };
  4698. const currentRotation = window.currentRotation || 0;
  4699.  
  4700. // Regenerate the tree visualization with the existing settings
  4701. const radius = type === 'character' ? window.c_radius : window.l_radius;
  4702. const labelFontSize = type === 'character' ? window.c_labelFontSize : window.l_labelFontSize;
  4703. const nodeSize = type === 'character' ? window.c_nodeSize : window.l_nodeSize;
  4704.  
  4705. // Regenerate the tree and apply the stored transformation
  4706. generateRadialTreeVisualization(container, structuredData, type, radius, labelFontSize, nodeSize, currentTransform, currentRotation);
  4707. }
  4708.  
  4709. function generateRadialTreeVisualization(container, data, type, radius = 300, labelFontSize = 12, nodeSize = 24, initialTransform = null, initialRotation = 0) {
  4710. // Clear the container
  4711. container.innerHTML = '';
  4712.  
  4713. // Set dimensions
  4714. const width = container.clientWidth;
  4715. const height = container.clientHeight;
  4716.  
  4717. // Current zoom, pan, and rotation state
  4718. let currentRotation = initialRotation;
  4719. let currentTransform = initialTransform || { x: width / 2, y: height / 2, k: 1 };
  4720.  
  4721. // Create a D3 zoom behavior
  4722. const zoomBehavior = d3.zoom()
  4723. .scaleExtent([0.5, 5])
  4724. .on('zoom', zoomed);
  4725.  
  4726. // Create SVG element with zoom and pan functionality
  4727. const svg = d3.select(container)
  4728. .append('svg')
  4729. .attr('width', width)
  4730. .attr('height', height)
  4731. .call(zoomBehavior) // Apply the zoom behavior
  4732. .append('g')
  4733. .attr('transform', `translate(${currentTransform.x},${currentTransform.y}) scale(${currentTransform.k}) rotate(${currentRotation})`);
  4734.  
  4735. // Apply the initial zoom, pan, and rotation
  4736. d3.select(container).select('svg').call(zoomBehavior.transform, d3.zoomIdentity.translate(currentTransform.x, currentTransform.y).scale(currentTransform.k));
  4737.  
  4738. function zoomed(event) {
  4739. // Update the current pan and zoom values
  4740. currentTransform = event.transform;
  4741. // Apply the combined transformation (zoom, pan, and rotation)
  4742. svg.attr('transform', `translate(${currentTransform.x},${currentTransform.y}) scale(${currentTransform.k}) rotate(${currentRotation})`);
  4743.  
  4744. // Store the transformation globally so we can retain it after refresh
  4745. window.currentTransform = currentTransform;
  4746. }
  4747.  
  4748. // Convert data to D3 hierarchy
  4749. const root = d3.hierarchy({ name: type === 'character' ? 'Characters' : 'Locations', children: data })
  4750. .sum(d => d.children ? 0 : 1);
  4751.  
  4752. // Create the radial tree layout
  4753. const tree = d3.tree()
  4754. .size([2 * Math.PI, radius])
  4755. .separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth);
  4756.  
  4757. tree(root);
  4758.  
  4759. // Create links
  4760. const link = svg.append('g')
  4761. .selectAll('.link')
  4762. .data(root.links())
  4763. .join('path')
  4764. .attr('class', 'link')
  4765. .attr('d', d3.linkRadial()
  4766. .angle(d => d.x)
  4767. .radius(d => d.y))
  4768. .attr('stroke', 'var(--muted-bg-color)')
  4769. .attr('fill', 'none');
  4770.  
  4771. // Create nodes
  4772. const node = svg.append('g')
  4773. .selectAll('.node')
  4774. .data(root.descendants())
  4775. .join('g')
  4776. .attr('class', 'node')
  4777. .attr('transform', d => `
  4778. rotate(${d.x * 180 / Math.PI - 90})
  4779. translate(${d.y},0)
  4780. `);
  4781.  
  4782. // Use emoji as node symbols
  4783. node.append('text')
  4784. .attr('dy', '0.31em')
  4785. .attr('font-size', `${nodeSize}px`) // Use nodeSize for font size
  4786. .attr('text-anchor', 'middle')
  4787. .attr('transform', d => `rotate(0)`) // No flipping of text
  4788. .text(d => d.data.emoji || '⭕') // Use emoji or a default symbol
  4789. .style('font-size', `${nodeSize}px`)
  4790. .style('fill', 'var(--text-color)')
  4791. .on('mouseover', (event, d) => {
  4792. const content = type === 'character' ? getCharacterTooltipContent(d.data, true) : getLocationTooltipContent(d.data, true);
  4793. showTooltip_radial(event, content);
  4794. })
  4795. .on('mousemove', (event) => {
  4796. moveTooltip_radial(event);
  4797. })
  4798. .on('mouseout', hideTooltip_radial)
  4799. .on('contextmenu', (event, d) => {
  4800. event.preventDefault();
  4801. navigateToItem(d.data.id, type); // Navigate to the item in the sidebar
  4802. });
  4803.  
  4804. // Labels (without emoji)
  4805. node.append('text')
  4806. .attr('dy', '0.31em')
  4807. .attr('x', +nodeSize / 2 + 5)
  4808. .attr('text-anchor', 'center') // Always center the text
  4809. .attr('transform', d => `rotate(0)`) // No flipping of text
  4810. .text(d => d.data.name) // Do not include emoji in label
  4811. .style('font-size', `${labelFontSize}px`)
  4812. .style('fill', d => (ItemTransferTarget && d.data.id === ItemTransferTarget.id) ? 'var(--warning-bg-color)' : 'var(--text-color)')
  4813. .on('mouseover', (event, d) => {
  4814. const content = type === 'character' ? getCharacterTooltipContent(d.data, true) : getLocationTooltipContent(d.data, true);
  4815. showTooltip_radial(event, content);
  4816. })
  4817. .on('mousemove', (event) => {
  4818. moveTooltip_radial(event);
  4819. })
  4820. .on('mouseout', hideTooltip_radial)
  4821. .on('contextmenu', (event, d) => {
  4822. event.preventDefault();
  4823.  
  4824. if (ItemTransferTarget == null) {
  4825. // Check if it's an item (not a group)
  4826. if (d.data.description != undefined) { // items undefined means it's not a group
  4827. CreateContextMenu(
  4828. 'var(--bg-tool)',
  4829. 'var(--text-color)',
  4830. 'var(--border-color)',
  4831. ['📍 Locate', '✏️ Edit', '📦 Move', '🗑️ Delete'],
  4832. [
  4833. // Locate
  4834. function() { navigateToItem(d.data.id, type); },
  4835. // Edit
  4836. function() {
  4837. const itemId = d.data.id;
  4838. if (type === 'character') {
  4839. const character = findItemById(characterGroups, itemId);
  4840. if (character) {
  4841. editCharacter(character);
  4842. renderCharacterGroups();
  4843. refreshTree(container, data, type);
  4844. }
  4845. } else {
  4846. const location = findItemById(locationGroups, itemId);
  4847. if (location) {
  4848. editLocation(location);
  4849. renderLocationGroups();
  4850. refreshTree(container, data, type);
  4851. }
  4852. }
  4853. },
  4854. // Move
  4855. function() {
  4856. ItemTransferTarget = d.data;
  4857. refreshTree(container, data, type);
  4858. },
  4859. // Delete
  4860. function() {
  4861. const itemId = d.data.id;
  4862. if (type === 'character') {
  4863. deleteCharacter(itemId);
  4864. renderCharacterGroups();
  4865. refreshTree(container, data, type);
  4866. } else {
  4867. deleteLocation(itemId);
  4868. renderLocationGroups();
  4869. refreshTree(container, data, type);
  4870. }
  4871. }
  4872. ]
  4873. );
  4874. } else {
  4875. // It's a group
  4876. CreateContextMenu(
  4877. 'var(--bg-tool)',
  4878. 'var(--text-color)',
  4879. 'var(--border-color)',
  4880. ['✏️ Edit', '🗑️ Delete'],
  4881. [
  4882. // Edit Group
  4883. function() {
  4884. const groupName = d.data.name;
  4885. if (type === 'character') {
  4886. const groupIndex = findGroupIndexByName(characterGroups, groupName);
  4887. if (groupIndex !== null) {
  4888. renameGroup(groupIndex);
  4889. renderCharacterGroups();
  4890. refreshTree(container, data, type);
  4891. }
  4892. } else {
  4893. const groupIndex = findGroupIndexByName(locationGroups, groupName);
  4894. if (groupIndex !== null) {
  4895. renameLocationGroup(groupIndex);
  4896. renderLocationGroups();
  4897. refreshTree(container, data, type);
  4898. }
  4899. }
  4900. },
  4901. // Delete Group
  4902. function() {
  4903. const groupName = d.data.name;
  4904. if (type === 'character') {
  4905. const groupIndex = findGroupIndexByName(characterGroups, groupName);
  4906. if (groupIndex !== null) {
  4907. deleteGroup(groupIndex);
  4908. renderCharacterGroups();
  4909. refreshTree(container, data, type);
  4910. }
  4911. } else {
  4912. const groupIndex = findGroupIndexByName(locationGroups, groupName);
  4913. if (groupIndex !== null) {
  4914. deleteLocationGroup(groupIndex);
  4915. renderLocationGroups();
  4916. refreshTree(container, data, type);
  4917. }
  4918. }
  4919. }
  4920. ]
  4921. );
  4922. }
  4923. } else {
  4924. // Moving an item
  4925. if (ItemTransferTarget.id === d.data.id) {
  4926. // Stop moving
  4927. CreateContextMenu(
  4928. 'var(--button-bg-color)',
  4929. 'var(--text-color)',
  4930. ['Stop'],
  4931. [
  4932. function() {
  4933. ItemTransferTarget = null;
  4934. refreshTree(container, data, type);
  4935. }
  4936. ]
  4937. );
  4938. } else {
  4939. // Place the item
  4940. CreateContextMenu(
  4941. 'var(--button-bg-color)',
  4942. 'var(--text-color)',
  4943. ['Place'],
  4944. [
  4945. function() {
  4946. // Check for circular reference
  4947. if (isAncestor(ItemTransferTarget.id, d.data.id, type)) {
  4948. alert('Cannot move an item into its descendant.');
  4949. return;
  4950. }
  4951.  
  4952. if (d.data.description != undefined) {
  4953. if (type === 'character') {
  4954. moveCharacterToParent(ItemTransferTarget.id, findItemById(characterGroups, d.data.id));
  4955. } else {
  4956. moveLocationToParent(ItemTransferTarget.id, findItemById(locationGroups, d.data.id));
  4957. }
  4958. } else {
  4959. // New parent is a group
  4960. const groupName = d.data.name;
  4961. if (type === 'character') {
  4962. const groupIndex = findGroupIndexByName(locationGroups, groupName);
  4963. moveCharacterToGroup(ItemTransferTarget.id, groupIndex);
  4964. } else {
  4965. const groupIndex = findGroupIndexByName(locationGroups, groupName);
  4966. moveLocationToGroup(ItemTransferTarget.id, groupIndex);
  4967. }
  4968. }
  4969.  
  4970. // Clear transfer target
  4971. ItemTransferTarget = null;
  4972.  
  4973. // Refresh
  4974. if (type === 'character') {
  4975. renderCharacterGroups();
  4976. } else {
  4977. renderLocationGroups();
  4978. }
  4979.  
  4980. refreshTree(container, data, type);
  4981. }
  4982. ]
  4983. );
  4984. }
  4985. }
  4986. showMenu(event);
  4987. });
  4988.  
  4989. // --- Rotation Knob UI ---
  4990. const knobContainer = document.createElement('div');
  4991. knobContainer.style.cssText = `
  4992. position: absolute;
  4993. bottom: 10px;
  4994. right: 10px;
  4995. width: 100px;
  4996. height: 100px;
  4997. z-index: 10003;
  4998. background-color: var(--bg-color);
  4999. border: 1px solid var(--border-color);
  5000. border-radius: 50%;
  5001. display: flex;
  5002. justify-content: center;
  5003. align-items: center;
  5004. cursor: pointer;
  5005. `;
  5006.  
  5007. const knob = document.createElement('div');
  5008. knob.style.cssText = `
  5009. width: 60px;
  5010. height: 60px;
  5011. background-color: var(--button-bg-color);
  5012. border-radius: 50%;
  5013. position: relative;
  5014. transform: rotate(${currentRotation}deg); /* Apply initial rotation */
  5015. `;
  5016.  
  5017. // Add a small indicator inside the knob to show the starting point
  5018. const knobIndicator = document.createElement('div');
  5019. knobIndicator.style.cssText = `
  5020. position: absolute;
  5021. top: 5px;
  5022. left: 50%;
  5023. transform: translateX(-50%);
  5024. width: 10px;
  5025. height: 10px;
  5026. background-color: var(--text-color);
  5027. border-radius: 50%;
  5028. `;
  5029. knob.appendChild(knobIndicator);
  5030.  
  5031. knobContainer.appendChild(knob);
  5032. container.appendChild(knobContainer);
  5033.  
  5034. let isDragging = false;
  5035. let previousAngle = 0;
  5036.  
  5037. // Utility function to get the angle of the mouse relative to the center of the knob
  5038. function getAngleFromEvent(event, element) {
  5039. const rect = element.getBoundingClientRect();
  5040. const centerX = rect.left + rect.width / 2;
  5041. const centerY = rect.top + rect.height / 2;
  5042. const deltaX = event.clientX - centerX;
  5043. const deltaY = event.clientY - centerY;
  5044. const angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
  5045. return angle;
  5046. }
  5047.  
  5048. // Function to start dragging the knob
  5049. knob.addEventListener('mousedown', (event) => {
  5050. isDragging = true;
  5051. previousAngle = getAngleFromEvent(event, knob);
  5052. event.preventDefault();
  5053. });
  5054.  
  5055. // Function to handle dragging
  5056. document.addEventListener('mousemove', (event) => {
  5057. if (isDragging) {
  5058. const currentAngle = getAngleFromEvent(event, knob);
  5059. const deltaAngle = currentAngle - previousAngle;
  5060.  
  5061. // Update the current rotation
  5062. currentRotation = (currentRotation + deltaAngle) % 360;
  5063. if (currentRotation < 0) currentRotation += 360; // Ensure positive rotation
  5064.  
  5065. // Apply the combined transformation (rotation, zoom, and pan)
  5066. svg.attr('transform', `translate(${currentTransform.x},${currentTransform.y}) scale(${currentTransform.k}) rotate(${currentRotation})`);
  5067.  
  5068. // Rotate the knob visually
  5069. knob.style.transform = `rotate(${currentRotation}deg)`;
  5070.  
  5071. // Store the updated rotation globally so we can retain it after refresh
  5072. window.currentRotation = currentRotation;
  5073.  
  5074. // Update previous angle for the next move
  5075. previousAngle = currentAngle;
  5076. }
  5077. });
  5078.  
  5079. // Stop dragging when mouse is released
  5080. document.addEventListener('mouseup', () => {
  5081. isDragging = false;
  5082. });
  5083.  
  5084. // Resize listener
  5085. window.addEventListener('resize', () => {
  5086. svg.attr('width', container.clientWidth).attr('height', container.clientHeight);
  5087. });
  5088. }
  5089.  
  5090. function navigateToItem(id, type) {
  5091. if (type === 'character') {
  5092. // Expand groups and scroll to the character
  5093. characterGroups.forEach(group => {
  5094. expandItemAndFind(group, id, type);
  5095. });
  5096. renderCharacterGroups();
  5097.  
  5098. const itemElement = document.querySelector(`#character-group-list [data-id="${id}"]`);
  5099. if (itemElement) {
  5100. // Ensure the character menu stays open
  5101. if (characterSidebar.style.right === '-350px') {
  5102. characterToggleButton.click(); // Open the sidebar if it's closed
  5103. }
  5104. itemElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
  5105.  
  5106. // Add glow effect
  5107. addGlowEffectWithTransition(itemElement);
  5108. }
  5109. } else {
  5110. // Expand groups and scroll to the location
  5111. locationGroups.forEach(group => {
  5112. expandItemAndFind(group, id, type);
  5113. });
  5114. renderLocationGroups();
  5115.  
  5116. const itemElement = document.querySelector(`#location-group-list [data-id="${id}"]`);
  5117. if (itemElement) {
  5118. // Ensure the location menu stays open
  5119. if (locationSidebar.style.left === '-350px') {
  5120. locationToggleButton.click(); // Open the sidebar if it's closed
  5121. }
  5122. itemElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
  5123.  
  5124. // Add glow effect
  5125. addGlowEffectWithTransition(itemElement);
  5126. }
  5127. }
  5128. }
  5129.  
  5130. function addGlowEffectWithTransition(element) {
  5131. // Save the original styles to restore them later
  5132. const originalBoxShadow = element.style.boxShadow;
  5133. const originalTransition = element.style.transition;
  5134.  
  5135. // Set up the transition for box-shadow
  5136. element.style.transition = 'box-shadow 0.5s ease-in-out';
  5137.  
  5138. // Apply the glow effect using the CSS variable --warning-bg-color
  5139. element.style.boxShadow = `0 0 15px 5px var(--warning-bg-color)`;
  5140.  
  5141. // Remove the glow effect after 1 second
  5142. setTimeout(() => {
  5143. element.style.boxShadow = originalBoxShadow || ''; // Restore the original boxShadow
  5144. setTimeout(() => {
  5145. element.style.transition = originalTransition || ''; // Restore the original transition
  5146. }, 500); // Wait for the transition to finish before restoring
  5147. }, 1000);
  5148. }
  5149.  
  5150. // Function to expand groups and find item (recursive)
  5151. function expandItemAndFind(item, id, type) {
  5152. if (item.id === id) {
  5153. return true;
  5154. }
  5155.  
  5156. let found = false;
  5157.  
  5158. if (item.items && item.items.length > 0) {
  5159. for (let i = 0; i < item.items.length; i++) {
  5160. if (expandItemAndFind(item.items[i], id, type)) {
  5161. item.collapsed = false; // Expand parent
  5162. found = true;
  5163. }
  5164. }
  5165. }
  5166.  
  5167. if (item.children && item.children.length > 0) {
  5168. for (let i = 0; i < item.children.length; i++) {
  5169. if (expandItemAndFind(item.children[i], id, type)) {
  5170. item.collapsed = false; // Expand parent
  5171. found = true;
  5172. }
  5173. }
  5174. }
  5175.  
  5176. return found;
  5177. }
  5178.  
  5179. // Function to create structured data for D3.js
  5180. function structureData(groups) {
  5181. return groups.map(group => ({
  5182. name: group.name,
  5183. emoji: '📁', // Assign folder emoji to groups
  5184. id: group.id || generateId(),
  5185. children: group.items.map(item => mapItem(item))
  5186. }));
  5187. }
  5188.  
  5189. function mapItem(item) {
  5190. return {
  5191. id: item.id,
  5192. name: item.name,
  5193. emoji: item.emoji || '', // Use item's emoji, empty string if none
  5194. description: item.description || item.bio || '',
  5195. children: item.children ? item.children.map(child => mapItem(child)) : []
  5196. };
  5197. }
  5198.  
  5199. // Tooltips for Radial Tree
  5200. let radialTooltipElement = null;
  5201.  
  5202. function showTooltip_radial(event, content) {
  5203. hideTooltip_radial(); // Remove existing tooltip if any
  5204.  
  5205. // Create new tooltip element
  5206. radialTooltipElement = document.createElement('div');
  5207. radialTooltipElement.style.cssText = `
  5208. position: absolute;
  5209. z-index: 100003;
  5210. background-color: var(--bg-tool);
  5211. color: var(--text-color);
  5212. border: 1px solid var(--border-color);
  5213. padding: 5px;
  5214. border-radius: 5px;
  5215. font-size: 12px;
  5216. max-width: 600px;
  5217. white-space: pre-wrap;
  5218. box-shadow: 0 0 10px var(--shadow-color);
  5219. pointer-events: none;
  5220. `;
  5221. radialTooltipElement.innerHTML = content;
  5222.  
  5223. radialTooltipElement.style.left = `${event.pageX + 10}px`;
  5224. radialTooltipElement.style.top = `${event.pageY + 10}px`;
  5225.  
  5226. document.body.appendChild(radialTooltipElement);
  5227. }
  5228.  
  5229. function moveTooltip_radial(event) {
  5230. if (radialTooltipElement) {
  5231. radialTooltipElement.style.left = `${event.pageX + 10}px`;
  5232. radialTooltipElement.style.top = `${event.pageY + 10}px`;
  5233. }
  5234. }
  5235.  
  5236. function hideTooltip_radial() {
  5237. if (radialTooltipElement && radialTooltipElement.parentNode) {
  5238. radialTooltipElement.parentNode.removeChild(radialTooltipElement);
  5239. radialTooltipElement = null; // Set to null after removing
  5240. }
  5241. }
  5242. // Adding buttons to headers
  5243.  
  5244. // Character Header Modifications
  5245. characterHeader.innerHTML = '';
  5246.  
  5247. const characterHeaderContainer = document.createElement('div');
  5248. characterHeaderContainer.style.cssText = `
  5249. display: flex;
  5250. align-items: center;
  5251. justify-content: center;
  5252. `;
  5253. // Add context menu functionality
  5254. characterHeader.addEventListener('contextmenu', (e) => {
  5255. e.preventDefault();
  5256. CreateContextMenu(
  5257. 'var(--bg-tool)',
  5258. 'var(--text-color)',
  5259. 'var(--border-color)',
  5260. ['🌐 Radial', '🔍 Search', '📁 Group', '🗃️ Import', '⬇️ Export'],
  5261. [
  5262. () => {
  5263. if (radialTreeOpen) {
  5264. document.getElementById('radial-tree-window').remove();
  5265. radialTreeOpen = false;
  5266. }
  5267. const data = structureData(characterGroups);
  5268. createRadialTree(data, 'character');
  5269. },
  5270. () => toggleSearchPanel('character'),
  5271. () => createNewCharacterGroup(),
  5272. () => importCharacters(),
  5273. () => exportCharacters(),
  5274. ]
  5275. );
  5276. showMenu(e);
  5277. });
  5278.  
  5279. // Character Title
  5280. const characterTitle = document.createElement('span');
  5281. characterTitle.textContent = '═════════╣ Characters ╠═════════';
  5282. characterTitle.style.cssText = `
  5283. color: var(--text-color);
  5284. flex-grow: 1;
  5285. text-align: center;
  5286. `;
  5287. characterHeaderContainer.appendChild(characterTitle);
  5288.  
  5289. characterHeader.appendChild(characterHeaderContainer);
  5290.  
  5291. // Location Header Modifications
  5292. locationHeader.innerHTML = '';
  5293.  
  5294. const locationHeaderContainer = document.createElement('div');
  5295. locationHeaderContainer.style.cssText = `
  5296. display: flex;
  5297. align-items: center;
  5298. justify-content: center;
  5299. `;
  5300.  
  5301. // Location Title
  5302. const locationTitle = document.createElement('span');
  5303. locationTitle.textContent = '════════╣ Locations ╠════════';
  5304. locationTitle.style.cssText = `
  5305. color: var(--text-color);
  5306. flex-grow: 1;
  5307. text-align: center;
  5308. font-size: large;
  5309. text-overflow: clip;
  5310. overflow: hidden;
  5311. `;
  5312. locationHeaderContainer.appendChild(locationTitle);
  5313.  
  5314. locationHeader.appendChild(locationHeaderContainer);
  5315.  
  5316. // Function to open search panel (Integrated into Sidebar)
  5317. function toggleSearchPanel(type) {
  5318. if (type === 'character') {
  5319. if (searchPanelOpen.character) {
  5320. // Close search panel
  5321. characterSearchContainer.style.display = 'none';
  5322. searchPanelOpen.character = false;
  5323. } else {
  5324. // Open search panel
  5325. characterSearchContainer.style.display = 'block';
  5326. searchPanelOpen.character = true;
  5327. characterSearchInput.focus();
  5328. }
  5329. } else {
  5330. if (searchPanelOpen.location) {
  5331. // Close search panel
  5332. locationSearchContainer.style.display = 'none';
  5333. searchPanelOpen.location = false;
  5334. } else {
  5335. // Open search panel
  5336. locationSearchContainer.style.display = 'block';
  5337. searchPanelOpen.location = true;
  5338. locationSearchInput.focus();
  5339. }
  5340. }
  5341. }
  5342.  
  5343. // Character Search Panel
  5344. const characterSearchContainer = document.createElement('div');
  5345. characterSearchContainer.style.cssText = `
  5346. position: relative;
  5347. display: none;
  5348. padding: 10px;
  5349. background-color: var(--bg-color);
  5350. `;
  5351. const characterSearchInput = document.createElement('input');
  5352. characterSearchInput.type = 'text';
  5353. characterSearchInput.placeholder = 'Search...';
  5354. characterSearchInput.style.cssText = `
  5355. width: 100%;
  5356. margin-bottom: 0px;
  5357. padding: 5px;
  5358. border: 1px solid var(--border-color);
  5359. border-radius: 5px;
  5360. background-color: var(--bg-color);
  5361. color: var(--text-color);
  5362. `;
  5363.  
  5364. // Adjust the results container to match the search input width
  5365. const characterResultsContainer = document.createElement('div');
  5366. characterResultsContainer.style.cssText = `
  5367. position: absolute;
  5368. top: 100%; /* Position below the search input */
  5369. left: 0;
  5370. width: 100%;
  5371. max-height: 200px;
  5372. overflow-y: auto;
  5373. background-color: var(--bg-color);
  5374. border: 1px solid var(--border-color);
  5375. box-shadow: 0 0 10px var(--shadow-color);
  5376. z-index: 1000;
  5377. `;
  5378. characterSearchContainer.appendChild(characterSearchInput);
  5379. characterSearchContainer.appendChild(characterResultsContainer);
  5380. characterSidebar.insertBefore(characterSearchContainer, characterGroupList);
  5381.  
  5382. characterSearchInput.addEventListener('input', () => {
  5383. const query = characterSearchInput.value.trim().toLowerCase();
  5384. characterResultsContainer.innerHTML = '';
  5385. if (query.length > 0) {
  5386. const results = searchItems(query, 'character');
  5387. if (results.length > 0) {
  5388. results.forEach(result => {
  5389. const resultItem = document.createElement('div');
  5390. resultItem.style.cssText = `
  5391. padding: 5px;
  5392. border-bottom: 1px solid var(--border-color);
  5393. cursor: pointer;
  5394. color: var(--text-color);
  5395. overflow: hidden;
  5396. text-overflow: ellipsis;
  5397. white-space: nowrap;
  5398. `;
  5399. resultItem.addEventListener('click', () => {
  5400. navigateToItem(result.id, 'character');
  5401. characterSearchInput.value = '';
  5402. characterResultsContainer.innerHTML = '';
  5403. });
  5404. resultItem.innerHTML = formatSearchResult(result);
  5405. characterResultsContainer.appendChild(resultItem);
  5406. });
  5407. } else {
  5408. characterResultsContainer.textContent = 'No results found.';
  5409. }
  5410. }
  5411. });
  5412.  
  5413. // Location Search Panel
  5414. const locationSearchContainer = document.createElement('div');
  5415. locationSearchContainer.style.cssText = `
  5416. position: relative;
  5417. display: none;
  5418. padding: 10px;
  5419. background-color: var(--bg-color);
  5420. `;
  5421. const locationSearchInput = document.createElement('input');
  5422. locationSearchInput.type = 'text';
  5423. locationSearchInput.placeholder = 'Search...';
  5424. locationSearchInput.style.cssText = `
  5425. width: 100%;
  5426. margin-bottom: 0px;
  5427. padding: 5px;
  5428. border: 1px solid var(--border-color);
  5429. border-radius: 5px;
  5430. background-color: var(--bg-color);
  5431. color: var(--text-color);
  5432. `;
  5433.  
  5434. // Adjust the results container to match the search input width
  5435. const locationResultsContainer = document.createElement('div');
  5436. locationResultsContainer.style.cssText = `
  5437. position: absolute;
  5438. top: 100%; /* Position below the search input */
  5439. left: 0;
  5440. width: 100%;
  5441. max-height: 200px;
  5442. overflow-y: auto;
  5443. background-color: var(--bg-color);
  5444. border: 1px solid var(--border-color);
  5445. box-shadow: 0 0 10px var(--shadow-color);
  5446. z-index: 1000;
  5447. `;
  5448. locationSearchContainer.appendChild(locationSearchInput);
  5449. locationSearchContainer.appendChild(locationResultsContainer);
  5450. locationSidebar.insertBefore(locationSearchContainer, locationGroupList);
  5451.  
  5452. locationSearchInput.addEventListener('input', () => {
  5453. const query = locationSearchInput.value.trim().toLowerCase();
  5454. locationResultsContainer.innerHTML = '';
  5455. if (query.length > 0) {
  5456. const results = searchItems(query, 'location');
  5457. if (results.length > 0) {
  5458. results.forEach(result => {
  5459. const resultItem = document.createElement('div');
  5460. resultItem.style.cssText = `
  5461. padding: 5px;
  5462. border-bottom: 1px solid var(--border-color);
  5463. cursor: pointer;
  5464. color: var(--text-color);
  5465. overflow: hidden;
  5466. text-overflow: ellipsis;
  5467. white-space: nowrap;
  5468. `;
  5469. resultItem.addEventListener('click', () => {
  5470. navigateToItem(result.id, 'location');
  5471. locationSearchInput.value = '';
  5472. locationResultsContainer.innerHTML = '';
  5473. });
  5474. resultItem.innerHTML = formatSearchResult(result);
  5475. locationResultsContainer.appendChild(resultItem);
  5476. });
  5477. } else {
  5478. locationResultsContainer.textContent = 'No results found.';
  5479. }
  5480. }
  5481. });
  5482.  
  5483. // Function to search items
  5484. function searchItems(query, type) {
  5485. const results = [];
  5486. const groups = type === 'character' ? characterGroups : locationGroups;
  5487. groups.forEach(group => {
  5488. searchGroup(group, query, results, []);
  5489. });
  5490. return results;
  5491. }
  5492.  
  5493. function searchGroup(group, query, results, path) {
  5494. const newPath = [...path, group.name];
  5495. group.items.forEach(item => {
  5496. searchItem(item, query, results, newPath);
  5497. });
  5498. }
  5499.  
  5500. function searchItem(item, query, results, path) {
  5501. const newPath = [...path, item.name];
  5502. if (item.name.toLowerCase().includes(query)) {
  5503. results.push({ id: item.id, path: newPath });
  5504. }
  5505. if (item.children && item.children.length > 0) {
  5506. item.children.forEach(child => {
  5507. searchItem(child, query, results, newPath);
  5508. });
  5509. }
  5510. }
  5511.  
  5512. function formatSearchResult(result) {
  5513. let html = '';
  5514. const maxCharacters = 42; // Adjust as needed
  5515. let displayPath = result.path.join(' > ');
  5516.  
  5517. if (displayPath.length > maxCharacters) {
  5518. displayPath = '...' + displayPath.slice(-maxCharacters);
  5519. }
  5520.  
  5521. const pathSegments = displayPath.split(' > ');
  5522.  
  5523. for (let i = 0; i < pathSegments.length - 1; i++) {
  5524. html += `<span style="font-size: 12px; color: var(--text-color-darker); white-space: nowrap;">${pathSegments[i]} > </span>`;
  5525. }
  5526. html += `<strong style="white-space: nowrap;">${pathSegments[pathSegments.length - 1]}</strong>`;
  5527.  
  5528. return `<div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${html}</div>`;
  5529. }
  5530.  
  5531. function GetImage(url, callback) {
  5532. // Validate the URL (basic image URL validation)
  5533. if (!isValidImageUrl(url)) {
  5534. console.error("Invalid image URL:", url);
  5535. if (typeof callback === "function") {
  5536. callback(null);
  5537. }
  5538. return;
  5539. }
  5540.  
  5541. // Fetch the image
  5542. GM_xmlhttpRequest({
  5543. method: "GET",
  5544. url: url,
  5545. responseType: "arraybuffer", // Get the raw binary data
  5546. onload: function(response) {
  5547. if (response.status === 200) {
  5548. // Convert the binary data to a Base64 string
  5549. const base64Data = arrayBufferToBase64(response.response);
  5550.  
  5551. // Determine the MIME type from the URL or default to image/jpeg
  5552. const mimeType = getMimeTypeFromUrl(url) || "image/jpeg";
  5553. const dataUri = `data:${mimeType};base64,${base64Data}`;
  5554.  
  5555. // Call the callback with the Base64 data URI
  5556. if (typeof callback === "function") {
  5557. callback(dataUri);
  5558. }
  5559. } else {
  5560. console.error(`Failed to fetch image. HTTP Status: ${response.status}`);
  5561. if (typeof callback === "function") {
  5562. callback(null);
  5563. }
  5564. }
  5565. },
  5566. onerror: function(error) {
  5567. console.error("Error fetching the image:", error);
  5568. if (typeof callback === "function") {
  5569. callback(null);
  5570. }
  5571. }
  5572. });
  5573. }
  5574.  
  5575. function isValidImageUrl(url) {
  5576. return /^(https?:\/\/.*\.(?:png|jpg|jpeg|gif|webp|bmp|svg))$/i.test(url);
  5577. }
  5578.  
  5579. function arrayBufferToBase64(buffer) {
  5580. let binary = '';
  5581. const bytes = new Uint8Array(buffer);
  5582. const len = bytes.byteLength;
  5583. for (let i = 0; i < len; i++) {
  5584. binary += String.fromCharCode(bytes[i]);
  5585. }
  5586. return btoa(binary);
  5587. }
  5588.  
  5589. function getMimeTypeFromUrl(url) {
  5590. const extension = url.split('.').pop().toLowerCase();
  5591. switch (extension) {
  5592. case 'png': return 'image/png';
  5593. case 'jpg': case 'jpeg': return 'image/jpeg';
  5594. case 'gif': return 'image/gif';
  5595. case 'webp': return 'image/webp';
  5596. case 'bmp': return 'image/bmp';
  5597. case 'svg': return 'image/svg+xml';
  5598. default: return null;
  5599. }
  5600. }
  5601.  
  5602. // Function to open Global Status Panel
  5603. function openGlobalStatusPanel() {
  5604. const statusForm = document.createElement('form');
  5605. statusForm.innerHTML = `
  5606. <div style="display: flex; flex-direction: column; width: 100%; min-width: 500px; margin: 0 auto;">
  5607. <!-- Scrollable content container -->
  5608. <div class="scrollable-content" style="flex: 1; overflow-y: auto; max-height: 300px; padding-right: 10px;">
  5609. <!-- Fields will be inserted here dynamically -->
  5610. </div>
  5611. <!-- Buttons at the bottom -->
  5612. <div class="status-buttons" style="text-align: center; padding-top: 10px;">
  5613. <button type="submit" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 8px 16px; border-radius: 5px; margin-right: 10px; cursor: pointer;">Save Status</button>
  5614. <button type="button" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer;" id="cancel-button-status">Cancel</button>
  5615. </div>
  5616. </div>
  5617. `;
  5618.  
  5619. const fieldsContainer = statusForm.querySelector('.scrollable-content');
  5620.  
  5621. // Define the fields for the Global Status Panel
  5622. const fields = [
  5623. { name: 'time', label: 'Time', type: 'text', multiline: false },
  5624. { name: 'weather', label: 'Weather', type: 'text', multiline: false },
  5625. { name: 'plot', label: 'Plot', type: 'text', multiline: true },
  5626. ];
  5627.  
  5628. // Create UI for each field
  5629. fields.forEach(({ name, label, multiline }) => {
  5630. const fieldDiv = document.createElement('div');
  5631. fieldDiv.style.marginBottom = '10px';
  5632.  
  5633. const fieldLabel = document.createElement('label');
  5634. fieldLabel.style.color = 'var(--text-color)';
  5635. fieldLabel.style.fontSize = '12px';
  5636. fieldLabel.textContent = label + ':';
  5637.  
  5638. if (multiline) {
  5639. // Multi-line textarea
  5640. const textarea = document.createElement('textarea');
  5641. textarea.name = name;
  5642. textarea.rows = 4;
  5643. textarea.placeholder = `Enter ${label.toLowerCase()} here...`;
  5644. textarea.style.cssText = 'background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding:5px; width: 100%; box-sizing: border-box; margin-top: 5px;';
  5645. textarea.value = globalStatus[name]; // Pre-fill with current value
  5646. fieldDiv.appendChild(fieldLabel);
  5647. fieldDiv.appendChild(textarea);
  5648. } else {
  5649. // Single-line input
  5650. const input = document.createElement('input');
  5651. input.type = 'text';
  5652. input.name = name;
  5653. input.placeholder = `Enter ${label.toLowerCase()} here...`;
  5654. input.style.cssText = 'background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding:5px; width: 100%; box-sizing: border-box; margin-top: 5px;';
  5655. input.value = globalStatus[name]; // Pre-fill with current value
  5656. fieldDiv.appendChild(fieldLabel);
  5657. fieldDiv.appendChild(input);
  5658. }
  5659.  
  5660. fieldsContainer.appendChild(fieldDiv);
  5661. });
  5662.  
  5663. // Cancel button functionality
  5664. const cancelButton = statusForm.querySelector('#cancel-button-status');
  5665. cancelButton.addEventListener('click', () => {
  5666. closeModal(statusModal);
  5667. });
  5668.  
  5669. // Create modal and handle form submission
  5670. const statusModal = createModal('Global Status', statusForm);
  5671. statusForm.addEventListener('submit', (e) => {
  5672. e.preventDefault();
  5673. // Push form data to the globalStatus object
  5674. fields.forEach(({ name }) => {
  5675. const fieldElement = statusForm[name];
  5676. globalStatus[name] = fieldElement.value.trim(); // Save to global object
  5677. });
  5678. console.log('Updated Global Status:', globalStatus); // For debugging
  5679. closeModal(statusModal);
  5680. });
  5681.  
  5682. statusForm.parentNode.style.backgroundColor = 'var(--bg-color-full)';
  5683. }
  5684. })();