您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a Location and Character System to JanitorAI with nested grouping functionality
当前为
// ==UserScript== // @name JanitorAI Context Maker // @namespace http://tampermonkey.net/ // @version 5.6 // @license MIT // @description Adds a Location and Character System to JanitorAI with nested grouping functionality // @match https://janitorai.com/chats/* // @icon https://www.google.com/s2/favicons?sz=64&domain=https://janitorai.com/ // @grant GM.setValue // @grant GM.getValue // @grant GM.xmlHttpRequest // @connect * // ==/UserScript== (async function() { 'use strict'; let c_radius = 300; let c_labelFontSize = 12; let c_nodeSize = 24; let l_radius = 300; let l_labelFontSize = 12; let l_nodeSize = 24; let customContextMenu; let initialMouseX; let initialMouseY; const hideDistance = 100; // Distance in pixels to hide the menu let ItemTransferTarget = null; //.addEventListener('contextmenu', showMenu); function CreateContextMenu(mainColor, textColor, optionNames, optionFunctions) { // Destroy existing menu if present DestroyContextMenu(); // Create a new custom menu customContextMenu = document.createElement('div'); customContextMenu.style.position = 'absolute'; customContextMenu.style.background = mainColor; customContextMenu.style.color = textColor; customContextMenu.style.border = '1px solid #ccc'; // Adjusted padding for 2/3 size customContextMenu.style.padding = '3.33px 6.67px'; customContextMenu.style.display = 'none'; customContextMenu.style.zIndex = '99999999'; // High z-index // Add menu items with dividers optionNames.forEach((name, index) => { const menuItem = document.createElement('div'); // Adjusted padding and font size for 2/3 size menuItem.style.padding = '3.33px 0'; menuItem.style.cursor = 'pointer'; menuItem.style.fontSize = '0.67em'; menuItem.textContent = name; menuItem.addEventListener('click', () => { optionFunctions[index](); hideMenu(); }); // Append menu item customContextMenu.appendChild(menuItem); // Add a divider except after the last item if (index < optionNames.length - 1) { const divider = document.createElement('div'); divider.style.borderTop = '1px solid #ccc'; customContextMenu.appendChild(divider); } }); // Append the menu to the body document.body.appendChild(customContextMenu); // Check mouse distance document.addEventListener('mousemove', trackMouseDistance); } function DestroyContextMenu() { if (customContextMenu) { customContextMenu.remove(); document.removeEventListener('contextmenu', showMenu); document.removeEventListener('click', hideMenu); document.removeEventListener('mousemove', trackMouseDistance); customContextMenu = null; } } function showMenu(event) { event.preventDefault(); initialMouseX = event.clientX; initialMouseY = event.clientY; if (customContextMenu) { customContextMenu.style.top = `${event.clientY}px`; customContextMenu.style.left = `${event.clientX}px`; customContextMenu.style.display = 'block'; } } function hideMenu() { if (customContextMenu) { customContextMenu.style.display = 'none'; } } function trackMouseDistance(event) { if (customContextMenu && customContextMenu.style.display === 'block') { const distance = Math.sqrt( Math.pow(event.clientX - initialMouseX, 2) + Math.pow(event.clientY - initialMouseY, 2) ); if (distance > hideDistance && !customContextMenu.contains(event.target)) { hideMenu(); } } } // Define Themes const themes = { // Default Dark Theme dark: { '--bg-color': 'rgba(34, 34, 34, var(--ui-transparency))', '--bg-color-full': 'rgba(34, 34, 34, 1)', '--bg-tool': 'rgba(34, 34, 34, 0.8)', '--text-color': '#ffffff', '--text-color-darker': '#cccccc', '--border-color': 'rgba(68, 68, 68, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))', '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))', '--success-bg-color': 'rgba(40, 167, 69, var(--ui-transparency))', '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))', '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)', '--link-color': '#8cb3ff', '--code-bg-color': 'rgba(0, 0, 0, 0.3)', '--code-text-color': '#ff9d00' }, // Default Light Theme light: { '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))', '--bg-color-full': 'rgba(255, 255, 255, 1)', '--bg-tool': 'rgba(255, 255, 255, 0.8)', '--text-color': '#000000', '--text-color-darker': '#333333', '--border-color': 'rgba(204, 204, 204, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))', '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))', '--success-bg-color': 'rgba(40, 167, 69, var(--ui-transparency))', '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))', '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.1)', '--link-color': '#007bff', '--code-bg-color': 'rgba(0, 0, 0, 0.05)', '--code-text-color': '#d63384' }, // Sepia Themes sepia_light: { '--bg-color': 'rgba(244, 232, 208, var(--ui-transparency))', '--bg-color-full': 'rgba(244, 232, 208, 1)', '--bg-tool': 'rgba(244, 232, 208, 0.8)', '--text-color': '#2e241c', '--text-color-darker': '#1a140f', '--border-color': 'rgba(193, 154, 107, var(--ui-transparency))', '--button-bg-color': 'rgba(160, 82, 45, var(--ui-transparency))', '--active-char-color': 'rgba(210, 180, 140, var(--ui-transparency))', '--success-bg-color': 'rgba(107, 68, 35, var(--ui-transparency))', '--info-bg-color': 'rgba(194, 148, 110, var(--ui-transparency))', '--warning-bg-color': 'rgba(215, 172, 116, var(--ui-transparency))', '--muted-bg-color': 'rgba(160, 130, 94, var(--ui-transparency))', '--danger-bg-color': 'rgba(168, 96, 50, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.3)', '--link-color': '#6c757d', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#a0522d' }, sepia_dark: { '--bg-color': 'rgba(60, 45, 31, var(--ui-transparency))', '--bg-color-full': 'rgba(60, 45, 31, 1)', '--bg-tool': 'rgba(60, 45, 31, 0.8)', '--text-color': '#d8c6b2', '--text-color-darker': '#b8a493', '--border-color': 'rgba(102, 75, 50, var(--ui-transparency))', '--button-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))', '--active-char-color': 'rgba(210, 180, 140, var(--ui-transparency))', '--success-bg-color': 'rgba(107, 68, 35, var(--ui-transparency))', '--info-bg-color': 'rgba(139, 101, 68, var(--ui-transparency))', '--warning-bg-color': 'rgba(205, 133, 63, var(--ui-transparency))', '--muted-bg-color': 'rgba(122, 91, 62, var(--ui-transparency))', '--danger-bg-color': 'rgba(165, 42, 42, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.6)', '--link-color': '#d2b48c', '--code-bg-color': 'rgba(0, 0, 0, 0.3)', '--code-text-color': '#deb887' }, // Solarized Themes solarized_light: { '--bg-color': 'rgba(253, 246, 227, var(--ui-transparency))', '--bg-color-full': 'rgba(253, 246, 227, 1)', '--bg-tool': 'rgba(253, 246, 227, 0.8)', '--text-color': '#47565c', '--text-color-darker': '#2c3438', '--border-color': 'rgba(238, 232, 213, var(--ui-transparency))', '--button-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))', '--active-char-color': 'rgba(133, 153, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(133, 153, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))', '--warning-bg-color': 'rgba(181, 137, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(147, 161, 161, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 50, 47, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.2)', '--link-color': '#2aa198', '--code-bg-color': 'rgba(0, 43, 54, 0.7)', '--code-text-color': '#cb4b16' }, solarized_dark: { '--bg-color': 'rgba(0, 43, 54, var(--ui-transparency))', '--bg-color-full': 'rgba(0, 43, 54, 1)', '--bg-tool': 'rgba(0, 43, 54, 0.8)', '--text-color': '#eee8d5', '--text-color-darker': '#cdc5b0', '--border-color': 'rgba(7, 54, 66, var(--ui-transparency))', '--button-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))', '--active-char-color': 'rgba(133, 153, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(133, 153, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))', '--warning-bg-color': 'rgba(181, 137, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(88, 110, 117, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 50, 47, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.4)', '--link-color': '#2aa198', '--code-bg-color': 'rgba(253, 246, 227, 0.1)', '--code-text-color': '#cb4b16' }, // Forest Themes (Green) forest_light: { '--bg-color': 'rgba(233, 245, 233, var(--ui-transparency))', '--bg-color-full': 'rgba(233, 245, 233, 1)', '--bg-tool': 'rgba(233, 245, 233, 0.8)', '--text-color': '#2f4f2f', '--text-color-darker': '#1c2f1c', '--border-color': 'rgba(209, 230, 209, var(--ui-transparency))', '--button-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))', '--active-char-color': 'rgba(34, 139, 34, var(--ui-transparency))', '--success-bg-color': 'rgba(144, 238, 144, var(--ui-transparency))', '--info-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))', '--warning-bg-color': 'rgba(240, 230, 140, var(--ui-transparency))', '--muted-bg-color': 'rgba(152, 251, 152, var(--ui-transparency))', '--danger-bg-color': 'rgba(205, 92, 92, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.2)', '--link-color': '#3cb371', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#8b4513' }, forest_dark: { '--bg-color': 'rgba(34, 49, 34, var(--ui-transparency))', '--bg-color-full': 'rgba(34, 49, 34, 1)', '--bg-tool': 'rgba(34, 49, 34, 0.8)', '--text-color': '#e0f7e9', '--text-color-darker': '#b0c7b9', '--border-color': 'rgba(46, 61, 46, var(--ui-transparency))', '--button-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))', '--active-char-color': 'rgba(144, 238, 144, var(--ui-transparency))', '--success-bg-color': 'rgba(34, 139, 34, var(--ui-transparency))', '--info-bg-color': 'rgba(144, 238, 144, var(--ui-transparency))', '--warning-bg-color': 'rgba(189, 183, 107, var(--ui-transparency))', '--muted-bg-color': 'rgba(85, 107, 47, var(--ui-transparency))', '--danger-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.4)', '--link-color': '#8fbc8f', '--code-bg-color': 'rgba(0, 0, 0, 0.3)', '--code-text-color': '#ffd700' }, // Ocean Themes (Blue) ocean_light: { '--bg-color': 'rgba(224, 244, 252, var(--ui-transparency))', '--bg-color-full': 'rgba(224, 244, 252, 1)', '--bg-tool': 'rgba(224, 244, 252, 0.8)', '--text-color': '#004766', '--text-color-darker': '#00334c', '--border-color': 'rgba(204, 232, 245, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))', '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))', '--success-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))', '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))', '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.2)', '--link-color': '#0077b6', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#ff7f50' }, ocean_dark: { '--bg-color': 'rgba(0, 30, 60, var(--ui-transparency))', '--bg-color-full': 'rgba(0, 30, 60, 1)', '--bg-tool': 'rgba(0, 30, 60, 0.8)', '--text-color': '#ffffff', '--text-color-darker': '#cccccc', '--border-color': 'rgba(0, 53, 102, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))', '--active-char-color': 'rgba(135, 206, 235, var(--ui-transparency))', '--success-bg-color': 'rgba(46, 139, 87, var(--ui-transparency))', '--info-bg-color': 'rgba(0, 96, 100, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 140, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(47, 79, 79, var(--ui-transparency))', '--danger-bg-color': 'rgba(139, 0, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)', '--link-color': '#00ffff', '--code-bg-color': 'rgba(0, 0, 0, 0.4)', '--code-text-color': '#ff7f50' }, // Sunset Themes (Red/Orange) sunset_light: { '--bg-color': 'rgba(255, 237, 219, var(--ui-transparency))', '--bg-color-full': 'rgba(255, 237, 219, 1)', '--bg-tool': 'rgba(255, 237, 219, 0.8)', '--text-color': '#5d1a1a', '--text-color-darker': '#3b0f0f', '--border-color': 'rgba(255, 214, 170, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 87, 34, var(--ui-transparency))', '--active-char-color': 'rgba(255, 152, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(255, 179, 71, var(--ui-transparency))', '--info-bg-color': 'rgba(255, 138, 101, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 112, 67, var(--ui-transparency))', '--muted-bg-color': 'rgba(255, 224, 178, var(--ui-transparency))', '--danger-bg-color': 'rgba(183, 28, 28, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.2)', '--link-color': '#e65100', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#d50000' }, sunset_dark: { '--bg-color': 'rgba(66, 28, 82, var(--ui-transparency))', '--bg-color-full': 'rgba(66, 28, 82, 1)', '--bg-tool': 'rgba(66, 28, 82, 0.8)', '--text-color': '#fce4ec', '--text-color-darker': '#f8bbd0', '--border-color': 'rgba(127, 63, 152, var(--ui-transparency))', '--button-bg-color': 'rgba(233, 30, 99, var(--ui-transparency))', '--active-char-color': 'rgba(255, 64, 129, var(--ui-transparency))', '--success-bg-color': 'rgba(186, 104, 200, var(--ui-transparency))', '--info-bg-color': 'rgba(142, 36, 170, var(--ui-transparency))', '--warning-bg-color': 'rgba(216, 27, 96, var(--ui-transparency))', '--muted-bg-color': 'rgba(123, 31, 162, var(--ui-transparency))', '--danger-bg-color': 'rgba(74, 20, 140, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.4)', '--link-color': '#d81b60', '--code-bg-color': 'rgba(0, 0, 0, 0.3)', '--code-text-color': '#f50057' }, // Sunshine Themes (Yellow) sunshine_light: { '--bg-color': 'rgba(255, 249, 196, var(--ui-transparency))', '--bg-color-full': 'rgba(255, 249, 196, 1)', '--bg-tool': 'rgba(255, 249, 196, 0.8)', '--text-color': '#795548', '--text-color-darker': '#5d4037', '--border-color': 'rgba(255, 241, 118, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 235, 59, var(--ui-transparency))', '--active-char-color': 'rgba(255, 179, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(253, 216, 53, var(--ui-transparency))', '--info-bg-color': 'rgba(255, 202, 40, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))', '--muted-bg-color': 'rgba(255, 224, 130, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 152, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.2)', '--link-color': '#ffab00', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#ff6f00' }, sunshine_dark: { '--bg-color': 'rgba(50, 50, 0, var(--ui-transparency))', '--bg-color-full': 'rgba(50, 50, 0, 1)', '--bg-tool': 'rgba(50, 50, 0, 0.8)', '--text-color': '#fff9c4', '--text-color-darker': '#fff59d', '--border-color': 'rgba(85, 85, 0, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 214, 0, var(--ui-transparency))', '--active-char-color': 'rgba(255, 171, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(255, 238, 88, var(--ui-transparency))', '--info-bg-color': 'rgba(255, 235, 59, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))', '--muted-bg-color': 'rgba(212, 175, 55, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 111, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.4)', '--link-color': '#ffab00', '--code-bg-color': 'rgba(0, 0, 0, 0.3)', '--code-text-color': '#ff6f00' }, // Twilight Themes (Indigo/Violet) twilight_light: { '--bg-color': 'rgba(230, 230, 250, var(--ui-transparency))', '--bg-color-full': 'rgba(230, 230, 250, 1)', '--bg-tool': 'rgba(230, 230, 250, 0.8)', '--text-color': '#4b0082', '--text-color-darker': '#2e0047', '--border-color': 'rgba(216, 191, 216, var(--ui-transparency))', '--button-bg-color': 'rgba(75, 0, 130, var(--ui-transparency))', '--active-char-color': 'rgba(138, 43, 226, var(--ui-transparency))', '--success-bg-color': 'rgba(111, 0, 255, var(--ui-transparency))', '--info-bg-color': 'rgba(153, 50, 204, var(--ui-transparency))', '--warning-bg-color': 'rgba(186, 85, 211, var(--ui-transparency))', '--muted-bg-color': 'rgba(148, 0, 211, var(--ui-transparency))', '--danger-bg-color': 'rgba(199, 21, 133, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.2)', '--link-color': '#8a2be2', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#9400d3' }, twilight_dark: { '--bg-color': 'rgba(18, 10, 30, var(--ui-transparency))', '--bg-color-full': 'rgba(18, 10, 30, 1)', '--bg-tool': 'rgba(18, 10, 30, 0.8)', '--text-color': '#d8bfd8', '--text-color-darker': '#dda0dd', '--border-color': 'rgba(49, 24, 73, var(--ui-transparency))', '--button-bg-color': 'rgba(138, 43, 226, var(--ui-transparency))', '--active-char-color': 'rgba(153, 50, 204, var(--ui-transparency))', '--success-bg-color': 'rgba(186, 85, 211, var(--ui-transparency))', '--info-bg-color': 'rgba(148, 0, 211, var(--ui-transparency))', '--warning-bg-color': 'rgba(221, 160, 221, var(--ui-transparency))', '--muted-bg-color': 'rgba(123, 104, 238, var(--ui-transparency))', '--danger-bg-color': 'rgba(199, 21, 133, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.4)', '--link-color': '#ba55d3', '--code-bg-color': 'rgba(0, 0, 0, 0.3)', '--code-text-color': '#ee82ee' }, // Terminal Themes terminal_light: { '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))', '--bg-color-full': 'rgba(255, 255, 255, 1)', '--bg-tool': 'rgba(255, 255, 255, 0.8)', '--text-color': '#00ff00', '--text-color-darker': '#00cc00', '--border-color': 'rgba(0, 200, 0, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 125, 0, var(--ui-transparency))', '--active-char-color': 'rgba(0, 75, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(0, 128, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(0, 125, 0, var(--ui-transparency))', '--warning-bg-color': 'rgba(100, 125, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(0, 128, 0, var(--ui-transparency))', '--danger-bg-color': 'rgba(100, 0, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0, 50, 0, 0.5)', '--link-color': '#00ffff', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#7fff00' }, terminal_dark: { '--bg-color': 'rgba(0, 0, 0, var(--ui-transparency))', '--bg-color-full': 'rgba(0, 0, 0, 1)', '--bg-tool': 'rgba(0, 0, 0, 0.8)', '--text-color': '#00ff00', '--text-color-darker': '#00cc00', '--border-color': 'rgba(0, 200, 0, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 125, 0, var(--ui-transparency))', '--active-char-color': 'rgba(0, 75, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(0, 128, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(0, 125, 0, var(--ui-transparency))', '--warning-bg-color': 'rgba(100, 125, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(0, 128, 0, var(--ui-transparency))', '--danger-bg-color': 'rgba(100, 0, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0, 50, 0, 0.5)', '--link-color': '#00ffff', '--code-bg-color': 'rgba(0, 0, 0, 0.8)', '--code-text-color': '#7fff00' }, // Retro Themes retro_light: { '--bg-color': 'rgba(196, 182, 187, var(--ui-transparency))', '--bg-color-full': 'rgba(196, 182, 187, 1)', '--bg-tool': 'rgba(196, 182, 187, 0.8)', '--text-color': '#191919', '--text-color-darker': '#000000', '--border-color': 'rgba(128, 128, 128, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 105, 180, var(--ui-transparency))', '--active-char-color': 'rgba(255, 255, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(50, 205, 50, var(--ui-transparency))', '--info-bg-color': 'rgba(135, 206, 235, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 165, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(128, 128, 128, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.3)', '--link-color': '#1e90ff', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#ff1493' }, retro_dark: { '--bg-color': 'rgba(34, 32, 52, var(--ui-transparency))', '--bg-color-full': 'rgba(34, 32, 52, 1)', '--bg-tool': 'rgba(34, 32, 52, 0.8)', '--text-color': '#c2c3c7', '--text-color-darker': '#8b8d90', '--border-color': 'rgba(69, 40, 60, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 0, 77, var(--ui-transparency))', '--active-char-color': 'rgba(255, 163, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(0, 232, 216, var(--ui-transparency))', '--info-bg-color': 'rgba(44, 232, 245, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 236, 39, var(--ui-transparency))', '--muted-bg-color': 'rgba(96, 86, 107, var(--ui-transparency))', '--danger-bg-color': 'rgba(172, 50, 50, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)', '--link-color': '#ff004d', '--code-bg-color': 'rgba(69, 40, 60, 0.8)', '--code-text-color': '#ff77a8' }, // Neon Themes neon_light: { '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))', '--bg-color-full': 'rgba(255, 255, 255, 1)', '--bg-tool': 'rgba(255, 255, 255, 0.8)', '--text-color': '#000000', '--text-color-darker': '#333333', '--border-color': 'rgba(0, 0, 0, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 0, 255, var(--ui-transparency))', '--active-char-color': 'rgba(0, 255, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(0, 255, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(0, 255, 255, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(128, 128, 128, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.5)', '--link-color': '#ff00ff', '--code-bg-color': 'rgba(255, 255, 255, 0.8)', '--code-text-color': '#00ffff' }, neon_dark: { '--bg-color': 'rgba(0, 0, 0, var(--ui-transparency))', '--bg-color-full': 'rgba(0, 0, 0, 1)', '--bg-tool': 'rgba(0, 0, 0, 0.8)', '--text-color': '#ffffff', '--text-color-darker': '#cccccc', '--border-color': 'rgba(255, 255, 255, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 0, 255, var(--ui-transparency))', '--active-char-color': 'rgba(0, 255, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(0, 255, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(0, 255, 255, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(255, 0, 255, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0, 255, 255, 0.5)', '--link-color': '#ff00ff', '--code-bg-color': 'rgba(0, 0, 0, 0.8)', '--code-text-color': '#00ffff' }, // Vintage Themes vintage_light: { '--bg-color': 'rgba(240, 230, 140, var(--ui-transparency))', '--bg-color-full': 'rgba(240, 230, 140, 1)', '--bg-tool': 'rgba(240, 230, 140, 0.8)', '--text-color': '#360505', '--text-color-darker': '#200303', '--border-color': 'rgba(139, 69, 19, var(--ui-transparency))', '--button-bg-color': 'rgba(128, 0, 0, var(--ui-transparency))', '--active-char-color': 'rgba(255, 215, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(50, 205, 50, var(--ui-transparency))', '--info-bg-color': 'rgba(70, 130, 180, var(--ui-transparency))', '--warning-bg-color': 'rgba(218, 165, 32, var(--ui-transparency))', '--muted-bg-color': 'rgba(128, 128, 0, var(--ui-transparency))', '--danger-bg-color': 'rgba(178, 34, 34, var(--ui-transparency))', '--shadow-color': 'rgba(139, 69, 19, 0.5)', '--link-color': '#8b0000', '--code-bg-color': 'rgba(245, 222, 179, 0.5)', '--code-text-color': '#8b0000' }, vintage_dark: { '--bg-color': 'rgba(60, 47, 34, var(--ui-transparency))', '--bg-color-full': 'rgba(60, 47, 34, 1)', '--bg-tool': 'rgba(60, 47, 34, 0.8)', '--text-color': '#e0d4b3', '--text-color-darker': '#c0b297', '--border-color': 'rgba(139, 69, 19, var(--ui-transparency))', '--button-bg-color': 'rgba(128, 0, 0, var(--ui-transparency))', '--active-char-color': 'rgba(218, 165, 32, var(--ui-transparency))', '--success-bg-color': 'rgba(107, 142, 35, var(--ui-transparency))', '--info-bg-color': 'rgba(70, 130, 180, var(--ui-transparency))', '--warning-bg-color': 'rgba(184, 134, 11, var(--ui-transparency))', '--muted-bg-color': 'rgba(128, 128, 0, var(--ui-transparency))', '--danger-bg-color': 'rgba(178, 34, 34, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)', '--link-color': '#8b0000', '--code-bg-color': 'rgba(245, 222, 179, 0.3)', '--code-text-color': '#ffa07a' }, // Pastel Themes pastel_light: { '--bg-color': 'rgba(255, 228, 225, var(--ui-transparency))', '--bg-color-full': 'rgba(255, 228, 225, 1)', '--bg-tool': 'rgba(255, 228, 225, 0.8)', '--text-color': '#4d4d4d', '--text-color-darker': '#333333', '--border-color': 'rgba(255, 192, 203, var(--ui-transparency))', '--button-bg-color': 'rgba(135, 206, 235, var(--ui-transparency))', '--active-char-color': 'rgba(175, 238, 238, var(--ui-transparency))', '--success-bg-color': 'rgba(152, 251, 152, var(--ui-transparency))', '--info-bg-color': 'rgba(135, 206, 235, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 228, 181, var(--ui-transparency))', '--muted-bg-color': 'rgba(238, 130, 238, var(--ui-transparency))', '--danger-bg-color': 'rgba(240, 128, 128, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.1)', '--link-color': '#ff69b4', '--code-bg-color': 'rgba(255, 250, 205, 0.8)', '--code-text-color': '#dc143c' }, pastel_dark: { '--bg-color': 'rgba(75, 75, 75, var(--ui-transparency))', '--bg-color-full': 'rgba(75, 75, 75, 1)', '--bg-tool': 'rgba(75, 75, 75, 0.8)', '--text-color': '#e6e6e6', '--text-color-darker': '#cccccc', '--border-color': 'rgba(105, 105, 105, var(--ui-transparency))', '--button-bg-color': 'rgba(176, 196, 222, var(--ui-transparency))', '--active-char-color': 'rgba(119, 136, 153, var(--ui-transparency))', '--success-bg-color': 'rgba(144, 238, 144, var(--ui-transparency))', '--info-bg-color': 'rgba(135, 206, 250, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 160, 122, var(--ui-transparency))', '--muted-bg-color': 'rgba(205, 133, 63, var(--ui-transparency))', '--danger-bg-color': 'rgba(205, 92, 92, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)', '--link-color': '#ff69b4', '--code-bg-color': 'rgba(255, 182, 193, 0.3)', '--code-text-color': '#dc143c' }, // Monokai Themes monokai_light: { '--bg-color': 'rgba(248, 248, 242, var(--ui-transparency))', '--bg-color-full': 'rgba(248, 248, 242, 1)', '--bg-tool': 'rgba(248, 248, 242, 0.8)', '--text-color': '#272822', '--text-color-darker': '#49483e', '--border-color': 'rgba(220, 220, 217, var(--ui-transparency))', '--button-bg-color': 'rgba(249, 38, 114, var(--ui-transparency))', '--active-char-color': 'rgba(166, 226, 46, var(--ui-transparency))', '--success-bg-color': 'rgba(166, 226, 46, var(--ui-transparency))', '--info-bg-color': 'rgba(102, 217, 239, var(--ui-transparency))', '--warning-bg-color': 'rgba(253, 151, 31, var(--ui-transparency))', '--muted-bg-color': 'rgba(117, 113, 94, var(--ui-transparency))', '--danger-bg-color': 'rgba(249, 38, 114, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.2)', '--link-color': '#ae81ff', '--code-bg-color': 'rgba(230, 230, 230, 0.8)', '--code-text-color': '#f92672' }, monokai_dark: { '--bg-color': 'rgba(39, 40, 34, var(--ui-transparency))', '--bg-color-full': 'rgba(39, 40, 34, 1)', '--bg-tool': 'rgba(39, 40, 34, 0.8)', '--text-color': '#f8f8f2', '--text-color-darker': '#e6e6dc', '--border-color': 'rgba(73, 72, 62, var(--ui-transparency))', '--button-bg-color': 'rgba(249, 38, 114, var(--ui-transparency))', '--active-char-color': 'rgba(166, 226, 46, var(--ui-transparency))', '--success-bg-color': 'rgba(102, 217, 239, var(--ui-transparency))', '--info-bg-color': 'rgba(102, 217, 239, var(--ui-transparency))', '--warning-bg-color': 'rgba(253, 151, 31, var(--ui-transparency))', '--muted-bg-color': 'rgba(117, 113, 94, var(--ui-transparency))', '--danger-bg-color': 'rgba(249, 38, 114, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)', '--link-color': '#ae81ff', '--code-bg-color': 'rgba(39, 40, 34, 0.8)', '--code-text-color': '#f92672' }, // Dracula Themes dracula_light: { '--bg-color': 'rgba(248, 248, 242, var(--ui-transparency))', '--bg-color-full': 'rgba(248, 248, 242, 1)', '--bg-tool': 'rgba(248, 248, 242, 0.8)', '--text-color': '#282a36', '--text-color-darker': '#44475a', '--border-color': 'rgba(211, 212, 219, var(--ui-transparency))', '--button-bg-color': 'rgba(98, 114, 164, var(--ui-transparency))', '--active-char-color': 'rgba(189, 147, 249, var(--ui-transparency))', '--success-bg-color': 'rgba(80, 250, 123, var(--ui-transparency))', '--info-bg-color': 'rgba(139, 233, 253, var(--ui-transparency))', '--warning-bg-color': 'rgba(241, 250, 140, var(--ui-transparency))', '--muted-bg-color': 'rgba(98, 114, 164, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 85, 85, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.2)', '--link-color': '#6272a4', '--code-bg-color': 'rgba(225, 225, 232, 0.8)', '--code-text-color': '#ff79c6' }, dracula_dark: { '--bg-color': 'rgba(40, 42, 54, var(--ui-transparency))', '--bg-color-full': 'rgba(40, 42, 54, 1)', '--bg-tool': 'rgba(40, 42, 54, 0.8)', '--text-color': '#f8f8f2', '--text-color-darker': '#e6e6dc', '--border-color': 'rgba(68, 71, 90, var(--ui-transparency))', '--button-bg-color': 'rgba(98, 114, 164, var(--ui-transparency))', '--active-char-color': 'rgba(189, 147, 249, var(--ui-transparency))', '--success-bg-color': 'rgba(80, 250, 123, var(--ui-transparency))', '--info-bg-color': 'rgba(139, 233, 253, var(--ui-transparency))', '--warning-bg-color': 'rgba(241, 250, 140, var(--ui-transparency))', '--muted-bg-color': 'rgba(98, 114, 164, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 85, 85, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)', '--link-color': '#6272a4', '--code-bg-color': 'rgba(68, 71, 90, 0.8)', '--code-text-color': '#ff79c6' }, // Gruvbox Themes gruvbox_light: { '--bg-color': 'rgba(251, 241, 199, var(--ui-transparency))', '--bg-color-full': 'rgba(251, 241, 199, 1)', '--bg-tool': 'rgba(251, 241, 199, 0.8)', '--text-color': '#282828', '--text-color-darker': '#1d2021', '--border-color': 'rgba(213, 196, 161, var(--ui-transparency))', '--button-bg-color': 'rgba(184, 187, 38, var(--ui-transparency))', '--active-char-color': 'rgba(184, 187, 38, var(--ui-transparency))', '--success-bg-color': 'rgba(121, 116, 14, var(--ui-transparency))', '--info-bg-color': 'rgba(7, 102, 120, var(--ui-transparency))', '--warning-bg-color': 'rgba(215, 153, 33, var(--ui-transparency))', '--muted-bg-color': 'rgba(146, 131, 116, var(--ui-transparency))', '--danger-bg-color': 'rgba(204, 36, 29, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.2)', '--link-color': '#458588', '--code-bg-color': 'rgba(235, 219, 178, 0.8)', '--code-text-color': '#9d0006' }, gruvbox_dark: { '--bg-color': 'rgba(40, 40, 40, var(--ui-transparency))', '--bg-color-full': 'rgba(40, 40, 40, 1)', '--bg-tool': 'rgba(40, 40, 40, 0.8)', '--text-color': '#ebdbb2', '--text-color-darker': '#d5c4a1', '--border-color': 'rgba(60, 56, 54, var(--ui-transparency))', '--button-bg-color': 'rgba(213, 196, 161, var(--ui-transparency))', '--active-char-color': 'rgba(184, 187, 38, var(--ui-transparency))', '--success-bg-color': 'rgba(121, 116, 14, var(--ui-transparency))', '--info-bg-color': 'rgba(7, 102, 120, var(--ui-transparency))', '--warning-bg-color': 'rgba(215, 153, 33, var(--ui-transparency))', '--muted-bg-color': 'rgba(146, 131, 116, var(--ui-transparency))', '--danger-bg-color': 'rgba(204, 36, 29, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.4)', '--link-color': '#83a598', '--code-bg-color': 'rgba(60, 56, 54, 0.8)', '--code-text-color': '#fb4934' }, // Nord Themes nord_light: { '--bg-color': 'rgba(236, 239, 244, var(--ui-transparency))', '--bg-color-full': 'rgba(236, 239, 244, 1)', '--bg-tool': 'rgba(236, 239, 244, 0.8)', '--text-color': '#2e3440', '--text-color-darker': '#3b4252', '--border-color': 'rgba(208, 211, 216, var(--ui-transparency))', '--button-bg-color': 'rgba(94, 129, 172, var(--ui-transparency))', '--active-char-color': 'rgba(136, 192, 208, var(--ui-transparency))', '--success-bg-color': 'rgba(163, 190, 140, var(--ui-transparency))', '--info-bg-color': 'rgba(129, 161, 193, var(--ui-transparency))', '--warning-bg-color': 'rgba(235, 203, 139, var(--ui-transparency))', '--muted-bg-color': 'rgba(186, 190, 194, var(--ui-transparency))', '--danger-bg-color': 'rgba(191, 97, 106, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.2)', '--link-color': '#5e81ac', '--code-bg-color': 'rgba(208, 211, 216, 0.8)', '--code-text-color': '#bf616a' }, nord_dark: { '--bg-color': 'rgba(46, 52, 64, var(--ui-transparency))', '--bg-color-full': 'rgba(46, 52, 64, 1)', '--bg-tool': 'rgba(46, 52, 64, 0.8)', '--text-color': '#d8dee9', '--text-color-darker': '#eceff4', '--border-color': 'rgba(59, 66, 82, var(--ui-transparency))', '--button-bg-color': 'rgba(94, 129, 172, var(--ui-transparency))', '--active-char-color': 'rgba(136, 192, 208, var(--ui-transparency))', '--success-bg-color': 'rgba(163, 190, 140, var(--ui-transparency))', '--info-bg-color': 'rgba(129, 161, 193, var(--ui-transparency))', '--warning-bg-color': 'rgba(235, 203, 139, var(--ui-transparency))', '--muted-bg-color': 'rgba(76, 86, 106, var(--ui-transparency))', '--danger-bg-color': 'rgba(191, 97, 106, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)', '--link-color': '#88c0d0', '--code-bg-color': 'rgba(59, 66, 82, 0.8)', '--code-text-color': '#bf616a' }, // High Contrast Themes high_contrast_light: { '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))', '--bg-color-full': 'rgba(255, 255, 255, 1)', '--bg-tool': 'rgba(255, 255, 255, 0.8)', '--text-color': '#000000', '--text-color-darker': '#333333', '--border-color': 'rgba(0, 0, 0, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))', '--active-char-color': 'rgba(255, 0, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(0, 255, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(0, 255, 255, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(128, 128, 128, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.5)', '--link-color': '#0000ff', '--code-bg-color': 'rgba(0, 0, 0, 0.1)', '--code-text-color': '#0000ff' }, high_contrast_dark: { '--bg-color': 'rgba(0, 0, 0, var(--ui-transparency))', '--bg-color-full': 'rgba(0, 0, 0, 1)', '--bg-tool': 'rgba(0, 0, 0, 0.8)', '--text-color': '#ffffff', '--text-color-darker': '#cccccc', '--border-color': 'rgba(255, 255, 255, var(--ui-transparency))', '--button-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))', '--active-char-color': 'rgba(255, 0, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(0, 255, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(0, 255, 255, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 255, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(128, 128, 128, var(--ui-transparency))', '--danger-bg-color': 'rgba(255, 0, 0, var(--ui-transparency))', '--shadow-color': 'rgba(255, 255, 255, 0.5)', '--link-color': '#00ffff', '--code-bg-color': 'rgba(255, 255, 255, 0.1)', '--code-text-color': '#ffff00' }, // Material Design Themes material_light: { '--bg-color': 'rgba(250, 250, 250, var(--ui-transparency))', '--bg-color-full': 'rgba(250, 250, 250, 1)', '--bg-tool': 'rgba(250, 250, 250, 0.8)', '--text-color': '#212121', '--text-color-darker': '#000000', '--border-color': 'rgba(224, 224, 224, var(--ui-transparency))', '--button-bg-color': 'rgba(33, 150, 243, var(--ui-transparency))', '--active-char-color': 'rgba(0, 188, 212, var(--ui-transparency))', '--success-bg-color': 'rgba(76, 175, 80, var(--ui-transparency))', '--info-bg-color': 'rgba(3, 169, 244, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 152, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(158, 158, 158, var(--ui-transparency))', '--danger-bg-color': 'rgba(244, 67, 54, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.2)', '--link-color': '#009688', '--code-bg-color': 'rgba(238, 238, 238, .8)', '--code-text-color': '#ff5722' }, material_dark: { '--bg-color': 'rgba(33, 33, 33, var(--ui-transparency))', '--bg-color-full': 'rgba(33, 33, 33, 1)', '--bg-tool': 'rgba(33, 33, 33, 0.8)', '--text-color': '#ffffff', '--text-color-darker': '#bdbdbd', '--border-color': 'rgba(66, 66, 66, var(--ui-transparency))', '--button-bg-color': 'rgba(33, 150, 243, var(--ui-transparency))', '--active-char-color': 'rgba(0, 188, 212, var(--ui-transparency))', '--success-bg-color': 'rgba(76, 175, 80, var(--ui-transparency))', '--info-bg-color': 'rgba(3, 169, 244, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 152, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(117, 117, 117, var(--ui-transparency))', '--danger-bg-color': 'rgba(244, 67, 54, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.5)', '--link-color': '#009688', '--code-bg-color': 'rgba(55, 71, 79, 0.8)', '--code-text-color': '#ff5722' }, // Desert Themes desert_light: { '--bg-color': 'rgba(250, 243, 221, var(--ui-transparency))', '--bg-color-full': 'rgba(250, 243, 221, 1)', '--bg-tool': 'rgba(250, 243, 221, 0.8)', '--text-color': '#5e4a1e', '--text-color-darker': '#3e300f', '--border-color': 'rgba(230, 216, 173, var(--ui-transparency))', '--button-bg-color': 'rgba(205, 133, 63, var(--ui-transparency))', '--active-char-color': 'rgba(244, 164, 96, var(--ui-transparency))', '--success-bg-color': 'rgba(218, 165, 32, var(--ui-transparency))', '--info-bg-color': 'rgba(210, 180, 140, var(--ui-transparency))', '--warning-bg-color': 'rgba(184, 134, 11, var(--ui-transparency))', '--muted-bg-color': 'rgba(222, 184, 135, var(--ui-transparency))', '--danger-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.2)', '--link-color': '#cd853f', '--code-bg-color': 'rgba(245, 222, 179, 0.8)', '--code-text-color': '#8b4513' }, desert_dark: { '--bg-color': 'rgba(74, 60, 42, var(--ui-transparency))', '--bg-color-full': 'rgba(74, 60, 42, 1)', '--bg-tool': 'rgba(74, 60, 42, 0.8)', '--text-color': '#f0e68c', '--text-color-darker': '#d2b48c', '--border-color': 'rgba(92, 64, 51, var(--ui-transparency))', '--button-bg-color': 'rgba(205, 133, 63, var(--ui-transparency))', '--active-char-color': 'rgba(244, 164, 96, var(--ui-transparency))', '--success-bg-color': 'rgba(218, 165, 32, var(--ui-transparency))', '--info-bg-color': 'rgba(210, 180, 140, var(--ui-transparency))', '--warning-bg-color': 'rgba(184, 134, 11, var(--ui-transparency))', '--muted-bg-color': 'rgba(210, 105, 30, var(--ui-transparency))', '--danger-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))', '--shadow-color': 'rgba(0, 0, 0, 0.4)', '--link-color': '#cd853f', '--code-bg-color': 'rgba(92, 64, 51, 0.8)', '--code-text-color': '#ffa07a' } }; // Initialize CSS variables function setTheme(themeName) { const theme = themes[themeName]; Object.keys(theme).forEach(key => { document.documentElement.style.setProperty(key, theme[key]); }); } // Retrieve saved settings or set defaults const defaultTransparency = await GM.getValue('transparency', '0.9'); const defaultTheme = await GM.getValue('theme', 'dark'); // Initialize transparency and theme document.documentElement.style.setProperty('--ui-transparency', defaultTransparency); setTheme(defaultTheme); // Add placeholder styles const style = document.createElement('style'); style.innerHTML = ` input::placeholder, textarea::placeholder { color: var(--text-color); } input:-ms-input-placeholder, textarea:-ms-input-placeholder { color: var(--text-color); } input::-ms-input-placeholder, textarea::-ms-input-placeholder { color: var(--text-color); } input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { color: var(--text-color); } `; document.head.appendChild(style); // --- Context Menu and Tool Buttons --- const menuButtonsContainer = document.createElement('div'); menuButtonsContainer.style.cssText = ` position: fixed; top: 10px; left: 50%; transform: translateX(-50%); display: flex; justify-content: center; z-index: 10000; `; document.body.appendChild(menuButtonsContainer); const contextMenuButton = document.createElement('button'); contextMenuButton.textContent = '⚙️'; contextMenuButton.title = 'Menu'; contextMenuButton.style.cssText = ` width: 40px; height: 40px; margin-right: 5px; padding: 0; border: none; background-color: var(--button-bg-color); color: var(--text-color); border-radius: 50%; font-size: 24px; cursor: pointer; z-index: 10000; `; menuButtonsContainer.appendChild(contextMenuButton); const toolButtonsData = [ { emoji: '🧮', title: 'Calculator', iframeSrc: 'https://www.desmos.com/fourfunction', id: 'calculator' }, { emoji: '🎲', title: 'Dice Roller', iframeSrc: '', id: 'dice' }, { emoji: '🎮', title: 'Game', iframeSrc: 'https://freepacman.org/', id: 'game' }, ]; toolButtonsData.forEach(tool => { const button = document.createElement('button'); button.textContent = tool.emoji; button.title = tool.title; button.style.cssText = ` width: 40px; height: 40px; margin-right: 5px; padding: 0; border: none; background-color: var(--button-bg-color); color: var(--text-color); border-radius: 50%; font-size: 24px; cursor: pointer; `; button.addEventListener('click', () => { openToolWindow(tool); }); menuButtonsContainer.appendChild(button); }); const openWindows = new Set(); function openToolWindow(tool) { if (openWindows.has(tool.id)) { return; } // Create the window container const windowContainer = document.createElement('div'); windowContainer.style.cssText = ` position: fixed; top: 100px; left: 100px; width: 500px; height: 400px; background-color: var(--bg-color); border: 1px solid var(--border-color); box-shadow: 0 0 10px var(--shadow-color); border-radius: 5px; z-index: 10002; display: flex; flex-direction: column; `; // Add a header with the title and close button const header = document.createElement('div'); header.style.cssText = ` display: flex; align-items: center; justify-content: space-between; padding: 5px; background-color: var(--button-bg-color); color: var(--text-color); cursor: move; `; const title = document.createElement('span'); title.textContent = tool.title; header.appendChild(title); const closeButton = document.createElement('button'); closeButton.textContent = '✖️'; closeButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; `; closeButton.addEventListener('click', () => { document.body.removeChild(windowContainer); openWindows.delete(tool.id); }); header.appendChild(closeButton); windowContainer.appendChild(header); // Add iframe const iframe = document.createElement('iframe'); iframe.style.cssText = ` flex-grow: 1; width: 100%; border: none; background-color: var(--bg-color); color: var(--text-color); `; let src = tool.iframeSrc; // Adjust dice URL based on theme if (tool.id === 'dice') { const dicehex = getComputedHexValue('--button-bg-color').slice(1); const chromahex = getComputedHexValue('--bg-color').slice(1); const labelhex = getComputedHexValue('--text-color').slice(1); src = `https://dice.bee.ac/?dicehex=${dicehex}&chromahex=${chromahex}&labelhex=${labelhex}`; } // Adjust calculator URL based on theme if (tool.id === 'calculator') { const bgColor = getComputedHexValue('--bg-color').slice(1); const textColor = getComputedHexValue('--text-color').slice(1); const buttonBgColor = getComputedHexValue('--button-bg-color').slice(1); src = `https://www.desmos.com/fourfunction?backgroundColor=${bgColor}&textColor=${textColor}&buttonBackgroundColor=${buttonBgColor}`; } iframe.src = src; windowContainer.appendChild(iframe); // Make window draggable makeElementDraggable(windowContainer, header); document.body.appendChild(windowContainer); openWindows.add(tool.id); document.body.appendChild(windowContainer); openWindows.add(tool.id); } function getComputedHexValue(variableName) { const computedStyle = getComputedStyle(document.documentElement); const colorValue = computedStyle.getPropertyValue(variableName).trim(); // Create a temporary div to get the computed color const tempDiv = document.createElement('div'); tempDiv.style.color = colorValue; document.body.appendChild(tempDiv); const computedColor = getComputedStyle(tempDiv).color; document.body.removeChild(tempDiv); // Now computedColor is in 'rgb(r, g, b)' or 'rgba(r, g, b, a)' format const rgba = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/); if (rgba) { const r = parseInt(rgba[1]).toString(16).padStart(2, '0'); const g = parseInt(rgba[2]).toString(16).padStart(2, '0'); const b = parseInt(rgba[3]).toString(16).padStart(2, '0'); return `#${r}${g}${b}`; } else { // Fallback to default return '#FFFFFF'; } } function makeElementDraggable(elmnt, handle) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; if (handle) { handle.onmousedown = dragMouseDown; } else { elmnt.onmousedown = dragMouseDown; } function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } // Helper functions for tooltip let tooltipElement = null; function showTooltip(event, content, set, url, imageMaxWidth = 300, imageMaxHeight = 200) { hideTooltip(); // Remove existing tooltip if any // Create new tooltip element tooltipElement = document.createElement('div'); tooltipElement.style.cssText = ` position: absolute; z-index: 10000; background-color: var(--bg-tool); color: var(--text-color); border: 1px solid var(--border-color); padding: 5px; border-radius: 5px; font-size: 12px; max-width: 600px; white-space: pre-wrap; box-shadow: 0 0 10px var(--shadow-color); `; tooltipElement.innerHTML = content; // Append tooltip to body first to calculate its size later document.body.appendChild(tooltipElement); // Get the parent node's bounding rectangle const parentRect = event.currentTarget.parentNode.parentNode.getBoundingClientRect(); if (!set) { // Tooltip to the right of the parent element tooltipElement.style.left = `${parentRect.right + 4}px`; tooltipElement.style.top = `${parentRect.top}px`; } else { // Tooltip to the left of the parent element const tooltipWidth = tooltipElement.offsetWidth; // Get the tooltip width after it's appended tooltipElement.style.left = `${parentRect.left - tooltipWidth + 15}px`; // Adjust to the left tooltipElement.style.top = `${parentRect.top}px`; } // Adjust position if the tooltip goes off screen const tooltipRect = tooltipElement.getBoundingClientRect(); if (tooltipRect.right > window.innerWidth) { tooltipElement.style.left = `${window.innerWidth - tooltipRect.width - 10}px`; } if (tooltipRect.left < 0) { tooltipElement.style.left = `10px`; // Move it a bit to the right if it goes off screen on the left } if (tooltipRect.bottom > window.innerHeight) { tooltipElement.style.top = `${window.innerHeight - tooltipRect.height - 10}px`; } // If a valid image URL is provided, attempt to load it if (url && isValidImageUrl(url)) { const img = document.createElement('img'); img.src = url; // When the image loads, call a function to add it to the tooltip img.onload = () => { addImageToTooltip(tooltipElement, url, imageMaxWidth, imageMaxHeight); }; // Optionally handle image loading errors img.onerror = () => { console.warn(`Failed to load image from URL: ${url}`); }; } } function addImageToTooltip(tooltipElement, url, imageMaxWidth, imageMaxHeight) { // Create a container for the image with a background color const imgContainer = document.createElement('div'); imgContainer.style.cssText = ` display: block; margin-top: 10px; width: 100%; /* Match the tooltip's width */ background-color: var(--shadow-color); border-radius: 5px; overflow: hidden; /* Ensures the image stays within the container's bounds */ padding: 5px; /* Optional: adds padding inside the background */ `; // Create the image element const img = document.createElement('img'); img.src = url; img.style.cssText = ` display: block; /* Behave like a block element */ margin: 0 auto; /* Center the image horizontally */ max-width: ${imageMaxWidth}px; /* Scale down if larger than max width */ max-height: ${imageMaxHeight}px; /* Scale down if larger than max height */ width: auto; /* Maintain aspect ratio based on max-width and max-height */ height: auto; /* Maintain aspect ratio */ border-radius: 5px; /* Optional: rounded corners for the image */ `; // Append the image to the container imgContainer.appendChild(img); // Append the container to the tooltip tooltipElement.appendChild(imgContainer); // Recalculate the tooltip position if necessary (e.g., if the image changes its size) const tooltipRect = tooltipElement.getBoundingClientRect(); if (tooltipRect.right > window.innerWidth) { tooltipElement.style.left = `${window.innerWidth - tooltipRect.width - 10}px`; } if (tooltipRect.left < 0) { tooltipElement.style.left = `10px`; // Move it a bit to the right if it goes off screen on the left } if (tooltipRect.bottom > window.innerHeight) { tooltipElement.style.top = `${window.innerHeight - tooltipRect.height - 10}px`; } } function hideTooltip() { if (tooltipElement && tooltipElement.parentNode) { tooltipElement.parentNode.removeChild(tooltipElement); tooltipElement = null; // Set to null after removing } } // --- Context Panel --- const contextPanel = document.createElement('div'); contextPanel.id = 'context-panel'; contextPanel.style.cssText = ` position: fixed; /* Keep position fixed to center the panel */ top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; padding: 20px; background-color: var(--bg-color); border: 1px solid var(--border-color); box-shadow: 0 0 10px var(--shadow-color); border-radius: 5px; display: none; z-index: 10001; /* Remove position: relative; */ `; document.body.appendChild(contextPanel); // Add close button to contextPanel const contextCloseButton = document.createElement('button'); contextCloseButton.textContent = '✖️'; contextCloseButton.style.cssText = ` position: absolute; top: 10px; right: 10px; background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-color); `; contextCloseButton.addEventListener('click', () => { contextPanel.style.display = 'none'; }); contextPanel.appendChild(contextCloseButton); const transparencyLabel = document.createElement('label'); transparencyLabel.textContent = 'UI Transparency'; transparencyLabel.style.cssText = ` color: var(--text-color); display: block; margin-bottom: 5px; `; contextPanel.appendChild(transparencyLabel); const transparencySlider = document.createElement('input'); transparencySlider.type = 'range'; transparencySlider.min = '0.1'; transparencySlider.max = '1'; transparencySlider.step = '0.1'; transparencySlider.value = defaultTransparency; transparencySlider.style.cssText = ` width: 100%; margin-bottom: 10px; `; transparencySlider.addEventListener('input', () => { document.documentElement.style.setProperty('--ui-transparency', transparencySlider.value); // Re-apply current theme to update transparency setTheme(themeDropdown.value); GM.setValue('transparency', transparencySlider.value); }); contextPanel.appendChild(transparencySlider); const dropperLabel = document.createElement('label'); dropperLabel.textContent = 'UI Theme'; dropperLabel.style.cssText = ` color: var(--text-color); display: block; margin-bottom: 5px; `; contextPanel.appendChild(dropperLabel); const themeDropdown = document.createElement('select'); themeDropdown.style.cssText = ` width: 100%; padding: 5px; margin-bottom: 10px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); `; Object.keys(themes).forEach(theme => { const option = document.createElement('option'); option.value = theme; option.textContent = theme.charAt(0).toUpperCase() + theme.slice(1); themeDropdown.appendChild(option); }); themeDropdown.value = defaultTheme; themeDropdown.addEventListener('change', () => { setTheme(themeDropdown.value); GM.setValue('theme', themeDropdown.value); }); contextPanel.appendChild(themeDropdown); // Add divider const divider = document.createElement('hr'); divider.style.cssText = ` border: none; border-top: 1px solid var(--border-color); margin: 10px 0; `; contextPanel.appendChild(divider); const contextLabel = document.createElement('label'); contextLabel.textContent = 'Primary Context'; contextLabel.style.cssText = ` color: var(--text-color); display: block; margin-bottom: 5px; `; contextPanel.appendChild(contextLabel); const primaryContextInput = document.createElement('textarea'); primaryContextInput.placeholder = 'Primary context goes here...'; primaryContextInput.style.cssText = ` width: 100%; height: 100px; margin-bottom: 10px; padding: 10px; border: 1px solid var(--border-color); border-radius: 5px; box-sizing: border-box; background-color: var(--bg-color); color: var(--text-color); `; contextPanel.appendChild(primaryContextInput); // Define the sleep function function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } const updateContextButton = document.createElement('button'); updateContextButton.textContent = 'Update Context'; updateContextButton.style.cssText = ` width: 100%; padding: 10px; border: none; background-color: var(--success-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; updateContextButton.addEventListener('click', async () => { const primaryContext = primaryContextInput.value; var fullContext = `Context;\n"` + primaryContext + `"\n`; fullContext += `\n` + window.getContext(); console.log("setting context; " + fullContext); window.manuClick('//*[@id="menu-button-:rb:"]'); await sleep(250); window.manuClick('//*[@id="menu-list-:rb:-menuitem-:ru:"]'); await sleep(250); window.manuWrite("/html/body/div[10]/div[3]/div/section/div/textarea", fullContext); await sleep(250); window.manuClick("/html/body/div[9]/div[3]/div/section/footer/button[2]"); }); window.manuWrite = function(xpath, text) { var result = document.evaluate( xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ); var node = result.singleNodeValue; if (node) { node.focus(); // Since the node is an HTMLTextAreaElement, get the value setter from its prototype const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor( window.HTMLTextAreaElement.prototype, 'value' ).set; nativeTextAreaValueSetter.call(node, text); // Dispatch the 'input' event to simulate user input var event = new Event('input', { bubbles: true }); node.dispatchEvent(event); } else { console.error("Node not found for XPath:", xpath); } } window.manuClick = function(xpath) { var result = document.evaluate( xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ); var node = result.singleNodeValue; if (node) { node.click(); } } contextPanel.appendChild(updateContextButton); contextMenuButton.addEventListener('click', () => { contextPanel.style.display = contextPanel.style.display === 'none' ? 'block' : 'none'; }); // Helper function to find the active location recursively function findActiveLocation(items) { for (const item of items) { if (item.active) { return item; } if (item.children && item.children.length > 0) { const activeChild = findActiveLocation(item.children); if (activeChild) { return activeChild; } } } return null; } // Helper function to collect all active characters recursively function collectActiveCharacters(items, result = []) { for (const item of items) { if (item.active) { result.push(item); } if (item.children && item.children.length > 0) { collectActiveCharacters(item.children, result); } } return result; } // Modify the getContext function to utilize the helper functions window.getContext = function() { const activeLocation = findActiveLocation(locationGroups.flatMap(g => g.items)); if (!activeLocation) return ''; let context = ``; if (plotInput && plotInput.value.trim() !== '') { context += `Plot:\n"${plotInput.value}"\n`; } else { context += `Plot:\n"No specific plot."\n`; } context += `\nSetting;\n"${activeLocation.name}" (${timeInput.value}, ${weatherInput.value}):\n` context += `\u0009"${activeLocation.description}"\n`; context += `\n{{user}}'s characters;\n`; const activeUserCharacters = collectActiveCharacters(characterGroups.flatMap(g => g.items)).filter(char => char.characterType == 1); activeUserCharacters.forEach(char => { context += `"${char.name}" (${char.sex}, ${char.species}, ${char.age}, ${char.bodyType}, ${char.personality}):\n`; context += `\u0009"${char.bio}"\n`; }); context += `\n{{char}}'s characters;\n`; const activeCharCharacters = collectActiveCharacters(characterGroups.flatMap(g => g.items)).filter(char => char.characterType == 0); activeCharCharacters.forEach(char => { context += `"${char.name}" (${char.sex}, ${char.species}, ${char.age}, ${char.bodyType}, ${char.personality}):\n`; context += `\u0009"${char.bio}"\n`; }); return context; }; // --- Character Sidebar with Groups and Nested Characters --- const characterSidebar = document.createElement('div'); characterSidebar.id = 'character-sheet-sidebar'; characterSidebar.style.cssText = ` position: fixed; top: 0; right: -350px; width: 350px; height: 100%; display: flex; flex-direction: column; background-color: var(--bg-color); border-left: 2px solid var(--border-color); box-shadow: -2px 0 5px var(--shadow-color); box-sizing: border-box; transition: right 0.3s; z-index: 9999; `; document.body.appendChild(characterSidebar); const characterToggleButton = document.createElement('button'); characterToggleButton.textContent = '☰'; characterToggleButton.style.cssText = ` position: fixed; top: 10px; right: 10px; padding: 5px 10px; border: none; background-color: var(--button-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; transition: right 0.3s; z-index: 10000; `; characterToggleButton.addEventListener('click', () => { const isOpen = characterSidebar.style.right === '0px'; characterSidebar.style.right = isOpen ? '-350px' : '0'; characterToggleButton.style.right = isOpen ? '10px' : '360px'; }); document.body.appendChild(characterToggleButton); const characterHeader = document.createElement('div'); characterHeader.style.cssText = ` padding: 10px; background-color: var(--border-color); text-align: center; font-weight: bold; color: var(--text-color); `; characterHeader.textContent = 'Characters'; characterSidebar.appendChild(characterHeader); const characterButtonBox = document.createElement('div'); characterButtonBox.style.cssText = ` display: flex; justify-content: space-between; padding: 10px; background-color: var(--bg-color); flex-shrink: 0; `; characterSidebar.appendChild(characterButtonBox); // New Character Button const newCharacterButton = document.createElement('button'); newCharacterButton.textContent = 'New'; newCharacterButton.title = 'Create New Character'; newCharacterButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--success-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; newCharacterButton.addEventListener('click', () => { createNewCharacter(); }); characterButtonBox.appendChild(newCharacterButton); // Load Character Button const loadCharacterButton = document.createElement('button'); loadCharacterButton.textContent = 'Load'; loadCharacterButton.title = 'Load a Character'; loadCharacterButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--info-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; loadCharacterButton.addEventListener('click', () => { loadCharacter(); }); characterButtonBox.appendChild(loadCharacterButton); // Group Character Button const groupCharacterButton = document.createElement('button'); groupCharacterButton.textContent = 'Group'; groupCharacterButton.title = 'Create New Group'; groupCharacterButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--info-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; groupCharacterButton.addEventListener('click', () => { createNewCharacterGroup(); }); characterButtonBox.appendChild(groupCharacterButton); // Import Character Button const importCharacterButton = document.createElement('button'); importCharacterButton.textContent = 'Import'; importCharacterButton.title = 'Import Characters'; importCharacterButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--warning-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; importCharacterButton.addEventListener('click', () => { importCharacters(); }); characterButtonBox.appendChild(importCharacterButton); // Export Character Button const exportCharacterButton = document.createElement('button'); exportCharacterButton.textContent = 'Export'; exportCharacterButton.title = 'Export Characters'; exportCharacterButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--muted-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; exportCharacterButton.addEventListener('click', () => { exportCharacters(); }); characterButtonBox.appendChild(exportCharacterButton); // Add fifth button: "Load" for Character function loadCharacter() { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.addEventListener('change', async (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { try { const character = JSON.parse(e.target.result); if (character && character.id) { // Add to Ungrouped const ungrouped = characterGroups.find(g => g.name === 'Ungrouped'); if (ungrouped) { ungrouped.items.push(character); } else { characterGroups[0].items.push(character); } renderCharacterGroups(); } else { alert('Invalid character format.'); } } catch (error) { alert('Failed to load character: ' + error.message); } }; reader.readAsText(file); }); fileInput.click(); } const characterGroupList = document.createElement('div'); characterGroupList.id = 'character-group-list'; characterGroupList.style.cssText = ` flex-grow: 1; overflow-y: auto; padding: 10px; `; characterSidebar.appendChild(characterGroupList); // Initialize Character Groups let characterGroups = [ { name: 'Ungrouped', items: [], collapsed: false } ]; // Function to create a new Character function createNewCharacter() { const newCharacter = { id: generateId(), active: true, emoji: '😀', avatar: 'Url goes here...', name: 'New Character', sex: '', species: '', age: '', bodyType: '', personality: '', bio: '', characterType: 0, children: [], collapsed: false }; // Add to Ungrouped const ungrouped = characterGroups.find(g => g.name === 'Ungrouped'); if (ungrouped) { ungrouped.items.push(newCharacter); } else { characterGroups[0].items.push(newCharacter); } renderCharacterGroups(); } // Function to create a new Character Group function createNewCharacterGroup() { const groupName = prompt('Enter group name:', `Group ${characterGroups.length}`); if (groupName && groupName.trim() !== '') { characterGroups.push({ name: groupName.trim(), items: [], collapsed: false }); renderCharacterGroups(); } } // Function to render Character Groups and Items function renderCharacterGroups() { characterGroupList.innerHTML = ''; characterGroups.forEach((group, groupIndex) => { // Sort items alphabetically by name group.items.sort((a, b) => a.name.localeCompare(b.name)); const groupContainer = document.createElement('div'); groupContainer.className = 'character-group'; groupContainer.style.cssText = ` margin-bottom: 10px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--active-char-color); `; const groupHeader = document.createElement('div'); groupHeader.style.cssText = ` display: flex; justify-content: space-between; align-items: center; padding: 5px 10px; background-color: var(--button-bg-color); color: var(--text-color); cursor: pointer; user-select: none; position: relative; `; groupHeader.textContent = group.name; groupHeader.addEventListener('click', () => { group.collapsed = !group.collapsed; renderCharacterGroups(); }); const groupActions = document.createElement('div'); groupActions.style.cssText = ` display: flex; align-items: center; `; // Activate/Deactivate All Button const toggleAllButton = document.createElement('button'); toggleAllButton.title = 'Activate/Deactivate All'; toggleAllButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; margin-right: 3px; font-size: 16px; `; updateToggleAllButton(toggleAllButton, group); toggleAllButton.addEventListener('click', (e) => { e.stopPropagation(); toggleAllGroupCharacters(group); updateToggleAllButton(toggleAllButton, group); renderCharacterGroups(); }); groupActions.appendChild(toggleAllButton); // Rename Group Button const renameGroupButton = document.createElement('button'); renameGroupButton.textContent = '✏️'; renameGroupButton.title = 'Rename Group'; renameGroupButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; margin-right: 2px; `; renameGroupButton.addEventListener('click', (e) => { e.stopPropagation(); renameGroup(groupIndex); }); groupActions.appendChild(renameGroupButton); // Delete Group Button const deleteGroupButton = document.createElement('button'); deleteGroupButton.textContent = '🗑️'; deleteGroupButton.title = 'Delete Group'; deleteGroupButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; `; deleteGroupButton.addEventListener('click', (e) => { e.stopPropagation(); deleteGroup(groupIndex); }); groupActions.appendChild(deleteGroupButton); groupHeader.appendChild(groupActions); groupContainer.appendChild(groupHeader); if (!group.collapsed) { const itemsContainer = document.createElement('div'); itemsContainer.style.cssText = ` padding: 5px 10px; display: block; `; if (group.items.length === 0) { const emptyInfo = document.createElement('div'); emptyInfo.className = 'character-slot'; emptyInfo.style.cssText = ` display: flex; align-items: center; padding: 10px; border: 1px solid var(--border-color); border-radius: 0px; height: 20px; margin-bottom: 5px; background-color: var(--bg-color); cursor: default; `; const infoText = document.createElement('span'); infoText.textContent = `Group is empty`; infoText.style.cssText = ` color: var(--text-color); `; emptyInfo.appendChild(infoText); itemsContainer.appendChild(emptyInfo); } else { group.items.forEach((character, charIndex) => { const characterSlot = createCharacterSlot(character, groupIndex); itemsContainer.appendChild(characterSlot); }); } groupContainer.appendChild(itemsContainer); } else { const collapsedInfo = document.createElement('div'); collapsedInfo.className = 'character-slot'; collapsedInfo.style.cssText = ` display: flex; align-items: center; padding: 10px; margin-bottom: 10px; border: 1px solid var(--border-color); border-radius: 5px; height: 20px; margin-top: 5px; background-color: var(--bg-color); cursor: default; margin-left: 10px; margin-right: 10px; `; const infoText = document.createElement('span'); infoText.textContent = `${group.items.length} items hidden`; //lime const activeCount = countActiveCharacters(group.items); const totalCount = countTotalCharacters(group.items); if (activeCount === totalCount && totalCount > 0) { // All active infoText.style.cssText = `color: rgba(36, 242, 0, 0.75);`; } else if (activeCount === 0) { // All inactive infoText.style.cssText = `color: var(--text-color-darker);`; } else { // Mixed infoText.style.cssText = `color: rgba(242, 198, 0, 0.75);`; } collapsedInfo.appendChild(infoText); groupContainer.appendChild(collapsedInfo); } // Add dragover and drop events for grouping groupContainer.addEventListener('dragover', (e) => { e.preventDefault(); groupContainer.style.border = `2px dashed var(--info-bg-color)`; }); groupContainer.addEventListener('dragleave', (e) => { groupContainer.style.border = `1px solid var(--border-color)`; }); groupContainer.addEventListener('drop', (e) => { e.preventDefault(); groupContainer.style.border = `1px solid var(--border-color)`; const data = e.dataTransfer.getData('text/plain'); const { type, id } = JSON.parse(data); if (type === 'character') { moveCharacterToGroup(id, groupIndex); } }); characterGroupList.appendChild(groupContainer); }); // Ensure at least one group exists if (characterGroups.length === 0) { characterGroups.push({ name: 'Ungrouped', items: [], collapsed: false }); renderCharacterGroups(); } } // Function to update the Toggle All Button appearance function updateToggleAllButton(button, group) { const activeCount = countActiveCharacters(group.items); const totalCount = countTotalCharacters(group.items); if (activeCount === totalCount && totalCount > 0) { // All active button.textContent = '🟢'; } else if (activeCount === 0) { // All inactive button.textContent = '🔴'; } else { // Mixed button.textContent = '🟡'; } } function countActiveCharacters(items) { let count = 0; items.forEach(item => { if (item.active) count++; if (item.children && item.children.length > 0) { count += countActiveCharacters(item.children); } }); return count; } function countTotalCharacters(items) { let count = items.length; items.forEach(item => { if (item.children && item.children.length > 0) { count += countTotalCharacters(item.children); } }); return count; } // Function to toggle all characters in a group function toggleAllGroupCharacters(group) { const allActive = countActiveCharacters(group.items) === countTotalCharacters(group.items); toggleCharacters(group.items, !allActive); } function toggleCharacters(items, state) { items.forEach(item => { item.active = state; if (item.children && item.children.length > 0) { toggleCharacters(item.children, state); } }); } // Function to generate tooltip content for a character function getCharacterTooltipContent(character, isRadial = false) { if (isRadial && character.emoji === '📁') { // If it's a group node in the radial tree, only show the name return `<strong>"${character.name}"</strong>`; } var content = `<strong>"${character.name}"</strong>`; if(character.characterType != 2){ content += ` <small>(${character.sex || 'Unknown'}, ${character.species || 'Unknown'}, ${character.age || 'Unknown'}, ${character.bodyType || 'Unknown'}, ${character.personality || 'Unknown'})</small>`; } content += `:<br><em>"${character.bio || 'No bio available.'}"</em>`; return content; } // Function to create a Character Slot DOM element (recursive) function createCharacterSlot(character, groupIndex, parent = null, level = 0) { const characterSlot = document.createElement('div'); characterSlot.className = 'character-slot'; characterSlot.draggable = true; characterSlot.dataset.id = character.id; characterSlot.style.cssText = ` display: flex; align-items: center; justify-content: space-between; padding: 5px; margin-bottom: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: ${character.characterType == 1 ? 'var(--active-char-color)' : 'var(--bg-color)'}; cursor: grab; margin-left: ${level * 20}px; `; // Drag events characterSlot.addEventListener('dragstart', (e) => { e.stopPropagation(); e.dataTransfer.setData('text/plain', JSON.stringify({ type: 'character', id: character.id })); e.currentTarget.style.opacity = '0.5'; }); characterSlot.addEventListener('dragend', (e) => { e.currentTarget.style.opacity = '1'; }); // Click event for collapsing/expanding if (character.children && character.children.length > 0) { characterSlot.style.cursor = 'pointer'; characterSlot.addEventListener('click', (e) => { e.stopPropagation(); character.collapsed = !character.collapsed; renderCharacterGroups(); }); } // Left Div (Active Toggle, Emoji, Name) const leftDiv = document.createElement('div'); leftDiv.style.cssText = ` display: flex; align-items: center; flex-grow: 1; overflow: hidden; `; // Remove Collapse/Expand Button // (Not needed as per new requirement) const activeButton = document.createElement('button'); activeButton.textContent = character.active ? '🟢' : '🔴'; activeButton.title = 'Toggle Active'; activeButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); margin-right: 5px; `; activeButton.addEventListener('click', (e) => { e.stopPropagation(); character.active = !character.active; updateAllToggleButtons(); renderCharacterGroups(); }); leftDiv.appendChild(activeButton); if(character.characterType == 2) { activeButton.style.display = 'none'; } const emojiSpan = document.createElement('span'); emojiSpan.textContent = character.emoji; emojiSpan.style.marginRight = '5px'; leftDiv.appendChild(emojiSpan); const nameSpan = document.createElement('span'); nameSpan.textContent = character.name; nameSpan.style.cssText = ` flex-grow: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--text-color); `; // Add tooltip event listeners nameSpan.addEventListener('mouseenter', (e) => { showTooltip(e, getCharacterTooltipContent(character), true, character.avatar); }); nameSpan.addEventListener('mouseleave', hideTooltip); nameSpan.addEventListener('dragover', hideTooltip); leftDiv.appendChild(nameSpan); characterSlot.appendChild(leftDiv); // Right Div (Edit, Delete, Export) const rightDiv = document.createElement('div'); rightDiv.style.display = 'flex'; rightDiv.style.alignItems = 'center'; const editButton = document.createElement('button'); editButton.textContent = '✏️'; editButton.title = 'Edit Character'; editButton.style.cssText = ` padding: 0px; border: none; background: none; cursor: pointer; color: var(--text-color); margin-right: 2px; `; editButton.addEventListener('click', (e) => { e.stopPropagation(); editCharacter(character); }); rightDiv.appendChild(editButton); const deleteButton = document.createElement('button'); deleteButton.textContent = '🗑️'; deleteButton.title = 'Delete Character'; deleteButton.style.cssText = ` padding: 0px; border: none; background: none; cursor: pointer; color: var(--text-color); margin-right: 2px; `; deleteButton.addEventListener('click', (e) => { e.stopPropagation(); deleteCharacter(character.id); }); rightDiv.appendChild(deleteButton); const exportButton = document.createElement('button'); exportButton.textContent = '⬇️'; exportButton.title = 'Export Character'; exportButton.style.cssText = ` padding: 0px; border: none; background: none; cursor: pointer; color: var(--text-color); `; exportButton.addEventListener('click', (e) => { e.stopPropagation(); exportCharacter(character); }); rightDiv.appendChild(exportButton); characterSlot.appendChild(rightDiv); // Dragover and Drop events characterSlot.addEventListener('dragover', (e) => { e.preventDefault(); e.stopPropagation(); characterSlot.style.border = `2px dashed var(--info-bg-color)`; }); characterSlot.addEventListener('dragleave', (e) => { characterSlot.style.border = `1px solid var(--border-color)`; }); characterSlot.addEventListener('drop', (e) => { e.preventDefault(); e.stopPropagation(); characterSlot.style.border = `1px solid var(--border-color)`; const data = e.dataTransfer.getData('text/plain'); const { type, id } = JSON.parse(data); if (type === 'character' && id !== character.id) { moveCharacterToParent(id, character); } }); // Prevent click on buttons from triggering slot collapse/expand activeButton.addEventListener('click', (e) => e.stopPropagation()); editButton.addEventListener('click', (e) => e.stopPropagation()); deleteButton.addEventListener('click', (e) => e.stopPropagation()); exportButton.addEventListener('click', (e) => e.stopPropagation()); // Recursive rendering of children const container = document.createElement('div'); container.appendChild(characterSlot); if (character.children && character.children.length > 0) { if (!character.collapsed) { character.children.forEach(child => { const childSlot = createCharacterSlot(child, groupIndex, character, level + 1); container.appendChild(childSlot); }); } else { const collapsedInfo = document.createElement('div'); collapsedInfo.className = 'character-slot'; collapsedInfo.style.cssText = ` display: flex; align-items: center; padding: 10px; margin-bottom: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); height: 20px; cursor: default; margin-left: ${(level + 1) * 20}px; `; const infoText = document.createElement('span'); infoText.textContent = `${character.children.length} items hidden`; //lime const activeCount = countActiveCharacters(character.children); const totalCount = countTotalCharacters(character.children); if (activeCount === totalCount && totalCount > 0) { // All active infoText.style.cssText = `color: rgba(36, 242, 0, 0.75);`; } else if (activeCount === 0) { // All inactive infoText.style.cssText = `color: var(--text-color-darker);`; } else { // Mixed infoText.style.cssText = `color: rgba(242, 198, 0, 0.75);`; } collapsedInfo.appendChild(infoText); container.appendChild(collapsedInfo); } } return container; } function updateAllToggleButtons() { characterGroups.forEach((group, groupIndex) => { const toggleAllButton = toggleAllButtonForGroup(groupIndex); if (toggleAllButton) { updateToggleAllButton(toggleAllButton, group); } }); } // Function to get the toggle all button for a group function toggleAllButtonForGroup(groupIndex) { const groupContainer = characterGroupList.children[groupIndex]; if (groupContainer) { const toggleAllButton = groupContainer.querySelector('button[title="Activate/Deactivate All"]'); return toggleAllButton; } return null; } // Function to rename a group function renameGroup(groupIndex) { const newName = prompt('Enter new group name:', characterGroups[groupIndex].name); if (newName && newName.trim() !== '') { characterGroups[groupIndex].name = newName.trim(); renderCharacterGroups(); } } // Function to delete a group function deleteGroup(groupIndex) { if (characterGroups.length === 1) { alert('At least one group must exist.'); return; } if (confirm(`Are you sure you want to delete the group "${characterGroups[groupIndex].name}"? All characters in this group will be moved to "Ungrouped".`)) { const group = characterGroups.splice(groupIndex, 1)[0]; const ungrouped = characterGroups.find(g => g.name === 'Ungrouped'); if (ungrouped) { ungrouped.items = ungrouped.items.concat(group.items); } else { characterGroups.unshift({ name: 'Ungrouped', items: group.items, collapsed: false }); } renderCharacterGroups(); } } // Default randomizer options const defaultRandomizerOptions = { emoji: { 'Smileys': "😀%😃%😄%😁%😆%😅%😂%🤣", 'People': "😊%😇%🙂%🙃%😉%😌%😍", // Add more categories if needed }, name: { 'Common': "Alex%Charlie%Sam%Jessie%Max%Taylor%Jordan%Casey%Jamie%Robin", 'Fantasy': "Aragorn%Legolas%Gandalf%Frodo%Bilbo%Galadriel%Sauron", // Add more categories if needed }, sex: { 'Standard': "male*48%female*48%other*4", // It's possible to have other options }, species: { 'Realistic': "Human", 'Fantasy': "Human%Elf%Dwarf%Orc%Demon", '25% Human | 75% Fantasy':"Human*2%Elf%Dwarf%Orc%Demon", '50% Human | 50% Fantasy':"Human*4%Elf%Dwarf%Orc%Demon", '75% Human | 25% Fantasy':"Human*12%Elf%Dwarf%Orc%Demon", 'Sci-fi': "Human%Synth%Alien%Hybrid%Cyborg", '25% Human | 75% Sci-fi':"Human*2%Synth%Alien%Hybrid%Cyborg", '50% Human | 50% Sci-fi':"Human*4%Synth%Alien%Hybrid%Cyborg", '75% Human | 25% Sci-fi':"Human*12%Synth%Alien%Hybrid%Cyborg", '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%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%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%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%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%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%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%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%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%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%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%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%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%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%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%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%Quagga%Quahog Clam%Quail%Queen Snake%Quetzal%Quetzalcoatlus northropi%Quokka%Quoll%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%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%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%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%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%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%X-Ray Tetra%Xeme (Sabine’s Gull)%Xenacanthus%Xenoceratops%Xenoposeidon%Xenotarsosaurus%Xerus%Xiaosaurus%Xiaotingia%Xingu River Ray%Xiphactinus%Xoloitzcuintli%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%Zebra%Zebra Finch%Zebra Mussels%Zebra Pleco%Zebra Shark%Zebra Snake%Zebra Spitting Cobra%Zebra Tarantula%Zebrafish (Zebra Fish)%Zebu%Zokor%Zonkey%Zorse%Zuchon", '25% Human | 75% Animals':"", '50% Human | 50% Animals':"", '75% Human | 25% Animals':"", '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% 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% 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%", '25% Human | 75% Mythos':"", '50% Human | 50% Mythos':"", '75% Human | 25% Mythos':"", 'Mythos + Animals':"", '25% Human | 75% Mythos + Animals':"", '50% Human | 50% Mythos + Animals':"", '75% Human | 25% Mythos + Animals':"", '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%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%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%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%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%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%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%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%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", '25% Human | 75% Pokemon':"", '50% Human | 50% Pokemon':"", '75% Human | 25% Pokemon':"", '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 WarsShoutmon 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 WarsShoutmon 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%(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 WarsArresterdramon: 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%(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 WarsShoutmon (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 WarsRaptorSparrowmon%BanchoGolemon%Cerberusmon: Werewolf Mode%Growlmon (Orange)%ToyAgumon (Black)%Xros WarsShootingStarmon%Justimon: Critical Arm%Crowmon%Dinohyumon%Rapidmon%Xros WarsJaegerDorulumon%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 WarsAtlurBallistamon%Omnimon Alter-B%BlackMachGaogamon%BlackGaogamon%Falcomon (2006 Anime Version)%Kudamon (2006 Anime Version)%NEO%Xros WarsYakiimon%Xros WarsMonimon%Xros WarsMonitamon%Xros WarsMervamon%Xros WarsMetalGreymon + Cyber Launcher%Xros WarsMetalGreymon (2010 Anime Version)%Xros WarsMailBirdramon%Xros WarsMusouKnightmon%Xros WarsMadLeomon: Armed Mode%Xros WarsMadLeomon%Xros WarsBommon (2010 Anime Version)%Xros WarsBeelzemon (2010 Anime Version)%Xros WarsBlastmon%Xros WarsFootmon%Xros WarsPillomon%Xros WarsPickmon%Xros WarsBallistamon%Xros WarsBakomon%Xros WarsBagramon%Xros WarsHi-VisionMonitamon%Xros WarsBaalmon%Xros WarsNeoMyotismon%Xros WarsDondokomon%Xros WarsDonShoutmon%Xros WarsDorulumon%Xros WarsDorbickmon%Xros WarsTroopmon%Xros WarsDeadlyAxemon%Xros WarsDeckerdramon%Xros WarsDeckerGreymon%Xros WarsTuwarmon%Xros WarsChuuChuumon%Xros WarsChibickmon%Xros WarsChikurimon%Xros WarsDamemon%Xros WarsTactimon%Xros WarsDarknessBagramon%Xros WarsDarkKnightmon%Xros WarsZenimon%Xros WarsSplashmon%Xros WarsSparrowmon%Xros WarsStarmons%Xros WarsSkullKnightmon: Mighty Axe Mode%Xros WarsSkullKnightmon: Cavalier Mode%Xros WarsSkullKnightmon%Xros WarsJokermon%Xros WarsShanitamon%Xros WarsShoutmon + Dorulu Cannon%Xros WarsShoutmon + Star Sword%Xros WarsShoutmon + Jet Sparrow%Xros WarsShoutmon X7%Xros WarsShoutmon X5B%Xros WarsShoutmon X5%Xros WarsShoutmon X4B%Xros WarsShoutmon X4%Xros WarsShoutmon X3%Xros WarsShoutmon X2%Xros WarsShoutmon DX%Xros WarsShoutmon%Xros WarsJetMervamon%Xros WarsZeigGreymon%Xros WarsZamielmon%Xros WarsSoundbirdmon%Xros WarsCyberdramon%Xros WarsKozenimon%Xros WarsGreymon (2010 Anime Version)%Xros WarsGravimon%Xros WarsCutemon%Xros WarsGumdramon%Xros WarsGanemon%Xros WarsGaossmon%Xros WarsOlegmon%Xros WarsOmniShoutmon%Xros WarsEkakimon%Xros WarsIgnitemon%Xros WarsArresterdramon%(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%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%(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%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)%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", '25% Human | 75% Digimon':"", '50% Human | 50% Digimon':"", '75% Human | 25% Digimon':"", 'Pokemon + Digimon':"", '25% Human | 75% Pokemon + Digimon':"", '50% Human | 50% Pokemon + Digimon':"", '75% Human | 25% Pokemon + Digimon':"", 'Chaos':"" }, age: { '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 'Child': "1%2%3%4%5%6%7%8%9%10%11%12%13%14%15%16%17", // ages 1 to 17 '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 '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+", '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+", }, bodyType: { '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", 'Slender': "Slim%Lean%Slender%Skinny%Scrawny%Gangly%Lanky%Wiry%Sinewy%Willowy%Lithe%Graceful%Svelte%Sylphlike", 'Fit': "Athletic%Toned%Fit%Muscular%Sinewy%Strapping%Brawny%Burly%Powerful%Robust%Vigorous%Mighty%Herculean", 'Average': "Average%Shapely%Stocky%Ordinary%Common%Typical%Regular%Unremarkable%Nondescript%Plain%Homely", 'Curvy': "Curvy%Voluptuous%Shapely%Buxom%Full-figured%Hourglass%Zaftig%Rubenesque%Lush%Ample%Curvaceous%Bodacious", 'Overweight': "Chubby%Overweight%Obese%Hefty%Flabby%Pudgy%Plump%Portly%Stout%Rotund%Paunchy%Potbellied%Corpulent%Fleshy%Bloated", 'Short': "Petite%Diminutive%Stubby%Squat%Pint-sized%Compact%Teeny%Miniature%Elfin%Dainty%Tiny%Wee", 'Tall': "Towering%Statuesque%Lofty%Gigantic%Colossal%Enormous%Huge%Immense%Mammoth%Gargantuan%Titanic%Mountainous", 'Ugly': "Deformed%Grotesque%Hideous%Unsightly%Repulsive%Revolting%Monstrous%Misshapen%Disfigured%Malformed%Unpleasant%Homely%Unappealing%Unattractive", 'Attractive': "Gorgeous%Stunning%Handsome%Beautiful%Attractive%Pleasing%Alluring%Captivating%Charming%Elegant%Exquisite%Magnificent%Dazzling%Radiant", }, personality: { '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", '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", 'Neutral': "Average%Analytical%Calm%Cautious%Diplomatic%Disciplined%Extroverted%Introverted%Logical%Mysterious%Observant%Philosophical%Practical%Stoic", 'Negative': "Serious%Shy%Snarky%Cautious%Pessimistic%Perfectionist%Sarcastic%Skeptical%Stubborn%Unpredictable", 'Extroverted': "Cheerful%Energetic%Outgoing%Adventurous%Charismatic%Confident%Enthusiastic%Extroverted%Friendly%Humorous%Sociable%Spontaneous%Witty", 'Introverted': "Serious%Calm%Shy%Analytical%Cautious%Independent%Introverted%Mysterious%Observant%Thoughtful", 'Thinking': "Analytical%Logical%Practical%Skeptical%Resourceful%Sensible%Wise%Philosophical%Perfectionist%Stoic%Disciplined%Persistent", 'Feeling': "Kind%Empathetic%Gentle%Romantic%Selfless%Sensitive%Sympathetic%Idealistic%Passionate%Sentimental%Compassionate%Nurturing", 'Judging': "Serious%Ambitious%Disciplined%Dependable%Honest%Loyal%Persistent%Practical%Responsible%Sensible%Trustworthy%Decisive%Organized%Punctual", 'Perceiving': "Energetic%Adventurous%Creative%Curious%Adaptable%Easygoing%Flexible%Open-minded%Spontaneous%Tolerant%Improvising%Relaxed", 'Assertive': "Confident%Independent%Ambitious%Decisive%Determined%Dominant%Firm%Strong-willed%Competitive%Bold%Daring%Fearless", 'Turbulent': "Snarky%Sarcastic%Unpredictable%Chaotic%Impulsive%Moody%Paranoid%Pessimistic%Quarrelsome%Resentful%Temperamental%Volatile", }, bio: { '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.", // Add more categories if needed }, }; defaultRandomizerOptions.species['25% Human | 75% Animals'] = defaultRandomizerOptions.species.Animals + "%Human*923"; defaultRandomizerOptions.species['50% Human | 50% Animals'] = defaultRandomizerOptions.species.Animals + "%Human*2770"; defaultRandomizerOptions.species['75% Human | 25% Animals'] = defaultRandomizerOptions.species.Animals + "%Human*8310"; defaultRandomizerOptions.species['25% Human | 75% Mythos'] = defaultRandomizerOptions.species.Mythological + "%Human*317"; defaultRandomizerOptions.species['50% Human | 50% Mythos'] = defaultRandomizerOptions.species.Mythological + "%Human*951"; defaultRandomizerOptions.species['75% Human | 25% Mythos'] = defaultRandomizerOptions.species.Mythological + "%Human*2853"; defaultRandomizerOptions.species['Mythos + Animals'] = defaultRandomizerOptions.species.Animals + "," + defaultRandomizerOptions.species.Mythological; defaultRandomizerOptions.species['25% Human | 75% Mythos + Animals'] = defaultRandomizerOptions.species['Mythos + Animals'] + "%Human*1240"; defaultRandomizerOptions.species['50% Human | 50% Mythos + Animals'] = defaultRandomizerOptions.species['Mythos + Animals'] + "%Human*3721"; defaultRandomizerOptions.species['75% Human | 25% Mythos + Animals'] = defaultRandomizerOptions.species['Mythos + Animals'] + "%Human*11163"; defaultRandomizerOptions.species['25% Human | 75% Pokemon'] = defaultRandomizerOptions.species.Pokemon + "%Human*512"; defaultRandomizerOptions.species['50% Human | 50% Pokemon'] = defaultRandomizerOptions.species.Pokemon + "%Human*1024"; defaultRandomizerOptions.species['75% Human | 25% Pokemon'] = defaultRandomizerOptions.species.Pokemon + "%Human*3072"; defaultRandomizerOptions.species['25% Human | 75% Digimon'] = defaultRandomizerOptions.species.Digimon + "%Human*614"; defaultRandomizerOptions.species['50% Human | 50% Digimon'] = defaultRandomizerOptions.species.Digimon + "%Human*1227"; defaultRandomizerOptions.species['75% Human | 25% Digimon'] = defaultRandomizerOptions.species.Digimon + "%Human*3681"; defaultRandomizerOptions.species['Pokemon + Digimon'] = defaultRandomizerOptions.species.Pokemon + "," + defaultRandomizerOptions.species.Digimon; defaultRandomizerOptions.species['25% Human | 75% Pokemon + Digimon'] = defaultRandomizerOptions.species['Pokemon + Digimon'] + "%Human*1126"; defaultRandomizerOptions.species['50% Human | 50% Pokemon + Digimon'] = defaultRandomizerOptions.species['Pokemon + Digimon'] + "%Human*2251"; defaultRandomizerOptions.species['75% Human | 25% Pokemon + Digimon'] = defaultRandomizerOptions.species['Pokemon + Digimon'] + "%Human*6753"; 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"]; // User's randomizer settings const randomizerSettings = { emoji: { selectedOption: 'Smileys', customList: '' }, name: { selectedOption: 'Common', customList: '' }, sex: { selectedOption: 'Standard', customList: '' }, species: { selectedOption: 'Realistic', customList: '' }, age: { selectedOption: 'Adult', customList: '' }, bodyType: { selectedOption: 'All', customList: '' }, personality: { selectedOption: 'All', customList: '' }, bio: { selectedOption: 'Standard', customList: '' }, }; // Function to generate a random character function generateRandomCharacter() { const character = {}; // Helper to get pronouns based on sex (male, female, other) function getPronouns(sex) { const lowerSex = sex.toLowerCase(); if (lowerSex === 'male') { return { subject: 'he', object: 'him', possessive: 'his' }; } else if (lowerSex === 'female') { return { subject: 'she', object: 'her', possessive: 'her' }; } else { return { subject: 'they', object: 'them', possessive: 'their' }; } } // Helper to remove comments (>> ... <<) - only for string fields function removeComments(text) { if (typeof text === 'string') { return text.replace(/>>.*?<<\s*/g, ''); } return text; // If it's not a string, just return it unchanged } // Helper to process sub-lists (&!) function processSubLists(items) { const mainList = []; const subLists = []; items.forEach((item, index) => { if (index === 0) { // The first item is the main list mainList.push(...item.split(/[%\n]+/).map(i => i.trim()).filter(Boolean)); } else { // The remaining items are sub-lists, split by commas const subListItems = item.split(/[%\n]+/).map(i => i.trim()).filter(Boolean); subLists.push(subListItems); } }); return { mainList, subLists }; } // Process weighting for string items with a "*n" suffix function weightItems(list) { return list.map(item => { if (typeof item === 'string') { const match = item.match(/^(.*)\*(\d+)$/); if (match) { return { value: match[1].trim(), weight: parseInt(match[2], 10) }; } return { value: item.trim(), weight: 1 }; } return { value: item, weight: 1 }; // For non-string items (e.g., age), no trimming }); } // Function to get random item based on weighted random function getRandomFromWeightedList(list) { const totalWeight = list.reduce((sum, item) => sum + item.weight, 0); let random = Math.random() * totalWeight; for (const item of list) { if (random < item.weight) { return item.value; } random -= item.weight; } return ''; // Fallback } // Function to handle recursive sub-list resolution and replace tokens function resolveSubListReferences(text, subLists, character, recursionDepth = 0) { const MAX_RECURSION_DEPTH = 10; // Prevent infinite recursion if (recursionDepth > MAX_RECURSION_DEPTH) { console.warn('Maximum recursion depth reached while resolving sub-lists.'); return text; } // Replace sub-list references like &?n with a random item from sub-list n let resolvedText = text.replace(/&\?(\d+)/g, (match, listIndex) => { const index = parseInt(listIndex, 10); if (subLists[index] && subLists[index].length > 0) { const subList = subLists[index]; const randomSubItem = getRandomFromWeightedList(weightItems(subList)); // Recursively resolve any sub-list references within the chosen sub-list item return resolveSubListReferences(randomSubItem, subLists, character, recursionDepth + 1); } return ''; // If sub-list doesn't exist or is empty, return empty string }); // After resolving sub-list references, replace tokens like #?a, #?b, etc. const pronouns = getPronouns(character.sex || ''); resolvedText = resolvedText.replace(/#\?a/g, character.name || '') .replace(/#\?b/g, pronouns.subject) .replace(/#\?c/g, pronouns.object) .replace(/#\?d/g, pronouns.possessive); return resolvedText; } // In the `getRandomItem` function, update where `resolveSubListReferences` is called: function getRandomItem(field) { let items = []; if (randomizerSettings[field].customList.trim() !== '') { // Use custom list items = randomizerSettings[field].customList .split(/[,\n]+/) .map(item => item.trim()) .filter(item => item !== ''); } else { // Use default list const selectedOption = randomizerSettings[field].selectedOption; items = defaultRandomizerOptions[field][selectedOption] .split(/[,\n]+/) .map(item => item.trim()) .filter(item => item !== ''); } // Handle cases where items might be undefined or empty if (!items || items.length === 0) { return ''; } // Remove comments (only applies to string fields) items = items.map(removeComments); // First, process the entire string before splitting by commas or newlines const inputString = items.join(','); // Split the input string on "&!" to separate sub-lists from the main text const mainTextAndSubLists = inputString.split('&!'); // The main text is everything before the first "&!" const mainListString = mainTextAndSubLists.shift(); // Get the main text const subListStrings = mainTextAndSubLists; // Remaining parts are sub-lists // Now process the main list and any sub-lists const { mainList, subLists } = processSubLists([mainListString, ...subListStrings]); // Get the random item from the main list let result = getRandomFromWeightedList(weightItems(mainList)); // If the field is 'bio', handle dynamic text insertion (e.g., pronouns, names) if (field === 'bio' && typeof result === 'string') { // Resolve sub-list references and replace tokens for bio result = resolveSubListReferences(result, subLists, character); } else { // For non-bio fields, resolve sub-list references if they exist result = resolveSubListReferences(result, subLists, character); } // For age, convert to numbers if (field === 'age') { result = parseInt(result, 10); } return result; } // Generate character fields character.emoji = getRandomItem('emoji'); character.name = getRandomItem('name'); character.sex = getRandomItem('sex'); character.species = getRandomItem('species'); character.age = getRandomItem('age'); character.bodyType = getRandomItem('bodyType'); character.personality = getRandomItem('personality'); character.bio = getRandomItem('bio'); return character; } // Function to capitalize first letter function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } // Function to open Randomizer Settings dialog function openRandomizerSettings() { const settingsForm = document.createElement('form'); console.log(settingsForm); settingsForm.innerHTML = ` <div style="display: flex; flex-direction: column; width: 100%; min-width: 500px; margin: 0 auto;"> <!-- Scrollable content container --> <div class="scrollable-content" style="flex: 1; overflow-y: auto; max-height: 300px; padding-right: 10px;"> <!-- Fields will be inserted here dynamically --> </div> <!-- Buttons at the bottom --> <div class="settings-buttons" style="text-align: center; padding-top: 10px;"> <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> <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> </div> </div> `; const fieldsContainer = settingsForm.querySelector('.scrollable-content'); // For each randomizable field, create settings UI const fields = ['emoji', 'name', 'sex', 'species', 'age', 'bodyType', 'personality', 'bio']; fields.forEach(field => { const fieldDiv = document.createElement('div'); fieldDiv.style.marginBottom = '10px'; const label = document.createElement('label'); label.style.color = 'var(--text-color)'; label.style.fontSize = '12px'; label.textContent = `Randomizer for ${capitalizeFirstLetter(field)}:`; // Dropdown for default options const select = document.createElement('select'); select.name = field; 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;'; // Populate options const options = defaultRandomizerOptions[field]; for (const optionName in options) { const option = document.createElement('option'); option.value = optionName; option.textContent = optionName; if (randomizerSettings[field].selectedOption === optionName && !randomizerSettings[field].customList.trim()) { option.selected = true; } select.appendChild(option); } // Custom list textarea const textarea = document.createElement('textarea'); textarea.name = field + '_custom'; textarea.rows = field != 'bio' ? 2 : 4; textarea.placeholder = 'Enter custom list, separated by "%" or new lines.'; 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;'; textarea.value = randomizerSettings[field].customList; // If custom list is non-empty, disable the select if (randomizerSettings[field].customList.trim() !== '') { select.disabled = true; } // Event listener to disable select if textarea has content textarea.addEventListener('input', (e) => { if (textarea.value.trim() !== '') { select.disabled = true; } else { select.disabled = false; } }); fieldDiv.appendChild(label); fieldDiv.appendChild(select); fieldDiv.appendChild(textarea); fieldsContainer.appendChild(fieldDiv); }); const helpLabel = document.createElement('label'); helpLabel.style.color = 'var(--text-color)'; helpLabel.style.fontSize = '18px'; helpLabel.textContent = `Text codes for advanced list creation;`; fieldsContainer.appendChild(helpLabel); // Create a paragraph element for the help text const helpText = document.createElement('p'); // Apply styling to the paragraph element helpText.style.color = 'var(--text-color)'; helpText.style.fontSize = '12px'; // Set the content of the help text helpText.innerHTML = ` <em>"%" marks the end of an item.<br> "*n" Multiply the odds an item is chosen.<br> "&!" Create a new sub list, index is assigned by order.<br> "&?n" Get a random item from a sublist.<br> "#?a" Get Character name (bio only)<br> "#?b" Get character pronoun like "he" (bio only)<br> "#?c" Get character pronoun like "him" (bio only)<br> "#?d" Get character pronoun like "his" (bio only)<br> ">>" Comment start.<br> "<<" Comment end.<br></em> `; // Append the paragraph to the container fieldsContainer.appendChild(helpText); const cancelButton = settingsForm.querySelector('#cancel-button'); cancelButton.addEventListener('click', () => { closeModal(settingsModal); }); const settingsModal = createModal('Randomizer Settings', settingsForm); settingsForm.addEventListener('submit', (e) => { e.preventDefault(); // Update randomizerSettings fields.forEach(field => { const select = settingsForm[field]; const textarea = settingsForm[field + '_custom']; randomizerSettings[field].customList = textarea.value.trim(); if (randomizerSettings[field].customList !== '') { randomizerSettings[field].selectedOption = ''; } else { randomizerSettings[field].selectedOption = select.value; } }); closeModal(settingsModal); }); settingsForm.parentNode.style.backgroundColor = 'var(--bg-color-full)'; } // Function to edit a character function editCharacter(character) { const lockState = { emoji: false, name: false, sex: false, species: false, age: false, bodyType: false, personality: false, bio: false }; const editForm = document.createElement('form'); editForm.innerHTML = ` <div style="width: 100%; min-width: 500px; margin: 0 auto; position: relative;"> ${createField('Emoji', 'emoji', character.emoji)} ${createAvatarField('Avatar', 'avatar', character.avatar)} ${createField('Name', 'name', character.name)} ${createSexDropdown('Sex', 'sex', character.sex)} ${createField('Species', 'species', character.species)} ${createField('Age', 'age', character.age)} ${createField('Body Type', 'bodyType', character.bodyType)} ${createField('Personality', 'personality', character.personality)} ${createTextareaField('Bio', 'bio', character.bio)} <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;">Character Type: <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;"> <option value="0" ${character.characterType === 0 ? 'selected' : ''}>Non-User</option> <option value="1" ${character.characterType === 1 ? 'selected' : ''}>User</option> <option value="2" ${character.characterType === 2 ? 'selected' : ''}>Collection</option> </select> </label> <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> </div> `; // Helper functions to create fields function createField(labelText, fieldName, value) { return ` <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;"> ${labelText}: <div style="position: relative;"> <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;"/> <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> </div> </label> `; } function createAvatarField(labelText, fieldName, value) { return ` <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;"> ${labelText}: <div style="position: relative;"> <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;" /> <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> </div> </label> `; } function createSexDropdown(labelText, fieldName, value) { return ` <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;"> ${labelText}: <div style="position: relative;"> <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;"> <option value="male" ${value === 'male' ? 'selected' : ''}>Male</option> <option value="female" ${value === 'female' ? 'selected' : ''}>Female</option> <option value="other" ${value === 'other' ? 'selected' : ''}>Other</option> </select> <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> </div> </label> `; } function createTextareaField(labelText, fieldName, value) { return ` <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;"> ${labelText}: <div style="position: relative;"> <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> <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> </div> </label> `; } // Copy Tooltip button const copyButton = document.createElement('button'); copyButton.textContent = '📋'; copyButton.title = 'Copy Tooltip'; copyButton.type = 'button'; // Prevent form submission copyButton.style.cssText = ` position: absolute; top: 15px; right: 50px; background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-color); `; copyButton.addEventListener('click', () => { const tooltipContent = getCharacterTooltipContent(character); const textWithNewlines = tooltipContent.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]*>/g, ''); navigator.clipboard.writeText(textWithNewlines); }); editForm.appendChild(copyButton); // Randomize button const randomizeButton = document.createElement('button'); randomizeButton.textContent = '🎲'; randomizeButton.title = 'Randomize'; randomizeButton.type = 'button'; // Prevent form submission randomizeButton.style.cssText = ` position: absolute; top: 15px; right: 90px; background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-color); `; randomizeButton.addEventListener('click', () => { const randomCharacter = generateRandomCharacter(); const fields = ['emoji', 'name', 'sex', 'species', 'age', 'bodyType', 'personality', 'bio']; fields.forEach((field) => { if (!lockState[field]) { editForm[field].value = randomCharacter[field]; } }); }); editForm.appendChild(randomizeButton); // Settings button const settingsButton = document.createElement('button'); settingsButton.textContent = '⚙️'; settingsButton.title = 'Randomizer Settings'; settingsButton.type = 'button'; // Prevent form submission settingsButton.style.cssText = ` position: absolute; top: 15px; right: 130px; background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-color); `; settingsButton.addEventListener('click', () => { openRandomizerSettings(); }); editForm.appendChild(settingsButton); // Append the form to a modal or the desired container const modal = createModal('Edit Character', editForm); // Lock button functionality const lockButtons = editForm.querySelectorAll('.lock-button'); lockButtons.forEach((button) => { const fieldName = button.getAttribute('data-field'); button.addEventListener('click', () => { lockState[fieldName] = !lockState[fieldName]; // Update button icon based on state button.textContent = lockState[fieldName] ? '🔒' : '🔓'; }); }); // Open Avatar button functionality const avatarButton = editForm.querySelector('.open-avatar-button'); avatarButton.addEventListener('click', () => { const avatarUrl = editForm.avatar.value; if (isValidImageUrl(avatarUrl)) { window.open(avatarUrl, '_blank'); } else { alert('Invalid image URL'); } }); editForm.addEventListener('submit', (e) => { e.preventDefault(); character.emoji = editForm.emoji.value; character.avatar = editForm.avatar.value; character.name = editForm.name.value; character.sex = editForm.sex.value; character.species = editForm.species.value; character.age = editForm.age.value; character.bodyType = editForm.bodyType.value; character.personality = editForm.personality.value; character.bio = editForm.bio.value; character.characterType = parseInt(editForm.characterType.value, 10); renderCharacterGroups(); closeModal(modal); }); } // Function to delete a character function deleteCharacter(characterId) { if (confirm('Are you sure you want to delete this character?')) { characterGroups.forEach(group => { group.items = deleteCharacterRecursive(group.items, characterId); }); renderCharacterGroups(); } } function deleteCharacterRecursive(items, characterId) { return items.filter(item => { if (item.id === characterId) { return false; } if (item.children && item.children.length > 0) { item.children = deleteCharacterRecursive(item.children, characterId); } return true; }); } // Function to export all characters with groups function exportCharacters() { const data = { characterGroups: characterGroups, settings: { c_radius, c_labelFontSize } // Save current settings }; const json = JSON.stringify(data, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `all_characters_with_groups.json`; link.click(); } // Function to import characters with settings function importCharacters() { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.addEventListener('change', async (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { try { const importedData = JSON.parse(e.target.result); if (Array.isArray(importedData)) { // Old format characterGroups = importedData; renderCharacterGroups(); } else if (importedData.characterGroups) { characterGroups = importedData.characterGroups; // Load settings if (importedData.settings) { // Assign settings c_radius = importedData.settings.c_radius || c_radius; c_labelFontSize = importedData.settings.c_labelFontSize || c_labelFontSize; } renderCharacterGroups(); } else { alert('Invalid format for characters import.'); } } catch (error) { alert('Failed to import characters: ' + error.message); } }; reader.readAsText(file); }); fileInput.click(); } // Function to export a single character function exportCharacter(character) { const json = JSON.stringify(character, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${character.name}.json`; link.click(); } // Function to move character to a group (uncoupling from parent) function moveCharacterToGroup(characterId, targetGroupIndex) { let character = null; // Remove character from current group or parent characterGroups.forEach(group => { group.items = removeCharacterRecursive(group.items, characterId, (item) => { character = item; }); }); // Add to target group if (character) { characterGroups[targetGroupIndex].items.push(character); renderCharacterGroups(); } } function removeCharacterRecursive(items, characterId, callback) { return items.filter(item => { if (item.id === characterId) { callback(item); return false; } if (item.children && item.children.length > 0) { item.children = removeCharacterRecursive(item.children, characterId, callback); } return true; }); } // Function to move character under another character (nesting) function moveCharacterToParent(characterId, parentCharacter) { let character = null; // Remove character from current group or parent characterGroups.forEach(group => { group.items = removeCharacterRecursive(group.items, characterId, (item) => { character = item; }); }); if (character) { parentCharacter.children.push(character); renderCharacterGroups(); } } // Utility function to generate unique IDs function generateId() { return '_' + Math.random().toString(36).substr(2, 9); } // --- Location Sidebar with Groups and Nested Locations --- const locationSidebar = document.createElement('div'); locationSidebar.id = 'location-sheet-sidebar'; locationSidebar.style.cssText = ` position: fixed; top: 0; left: -350px; width: 350px; height: 100%; display: flex; flex-direction: column; background-color: var(--bg-color); border-right: 2px solid var(--border-color); box-shadow: 2px 0 5px var(--shadow-color); box-sizing: border-box; transition: left 0.3s; z-index: 9999; `; document.body.appendChild(locationSidebar); const locationToggleButton = document.createElement('button'); locationToggleButton.textContent = '☰'; locationToggleButton.style.cssText = ` position: fixed; top: 10px; left: 10px; padding: 5px 10px; border: none; background-color: var(--button-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; transition: left 0.3s; z-index: 10000; `; locationToggleButton.addEventListener('click', () => { const isOpen = locationSidebar.style.left === '0px'; locationSidebar.style.left = isOpen ? '-350px' : '0'; locationToggleButton.style.left = isOpen ? '10px' : '360px'; }); document.body.appendChild(locationToggleButton); const locationHeader = document.createElement('div'); locationHeader.style.cssText = ` padding: 10px; background-color: var(--border-color); text-align: center; font-weight: bold; color: var(--text-color); `; locationHeader.textContent = 'Locations'; locationSidebar.appendChild(locationHeader); const locationButtonBox = document.createElement('div'); locationButtonBox.style.cssText = ` display: flex; justify-content: space-between; padding: 10px; background-color: var(--bg-color); flex-shrink: 0; `; locationSidebar.appendChild(locationButtonBox); // New Location Button const newLocationButton = document.createElement('button'); newLocationButton.textContent = 'New'; newLocationButton.title = 'Create New Location'; newLocationButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--success-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; newLocationButton.addEventListener('click', () => { createNewLocation(); }); locationButtonBox.appendChild(newLocationButton); // Load Location Button const loadLocationButton = document.createElement('button'); loadLocationButton.textContent = 'Load'; loadLocationButton.title = 'Load a Location'; loadLocationButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--info-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; loadLocationButton.addEventListener('click', () => { loadLocation(); }); locationButtonBox.appendChild(loadLocationButton); // Group Location Button const groupLocationButton = document.createElement('button'); groupLocationButton.textContent = 'Group'; groupLocationButton.title = 'Create New Group'; groupLocationButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--info-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; groupLocationButton.addEventListener('click', () => { createNewLocationGroup(); }); locationButtonBox.appendChild(groupLocationButton); // Import Location Button const importLocationButton = document.createElement('button'); importLocationButton.textContent = 'Import'; importLocationButton.title = 'Import Locations'; importLocationButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--warning-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; importLocationButton.addEventListener('click', () => { importLocations(); }); locationButtonBox.appendChild(importLocationButton); // Export Location Button const exportLocationButton = document.createElement('button'); exportLocationButton.textContent = 'Export'; exportLocationButton.title = 'Export Locations'; exportLocationButton.style.cssText = ` padding: 5px 10px; border: none; background-color: var(--muted-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; exportLocationButton.addEventListener('click', () => { exportLocations(); }); locationButtonBox.appendChild(exportLocationButton); // Add fifth button: "Load" for Location function loadLocation() { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.addEventListener('change', async (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { try { const location = JSON.parse(e.target.result); if (location && location.id) { // Add to Ungrouped const ungrouped = locationGroups.find(g => g.name === 'Ungrouped'); if (ungrouped) { ungrouped.items.push(location); } else { locationGroups[0].items.push(location); } renderLocationGroups(); ensureActiveLocation(); } else { alert('Invalid location format.'); } } catch (error) { alert('Failed to load location: ' + error.message); } }; reader.readAsText(file); }); fileInput.click(); } const locationGroupList = document.createElement('div'); locationGroupList.id = 'location-group-list'; locationGroupList.style.cssText = ` flex-grow: 1; overflow-y: auto; padding: 10px; `; locationSidebar.appendChild(locationGroupList); // Initialize Location Groups with a single default location let locationGroups = [ { name: 'Ungrouped', items: [ { id: generateId(), active: true, emoji: '📍', reference: 'Url goes here...', name: 'Default Location', description: 'Description of the default location.', children: [], collapsed: false } ], collapsed: false, } ]; // Function to create a new Location function createNewLocation() { const newLocation = { id: generateId(), active: false, emoji: '📍', reference: 'Url Goes here...', name: 'New Location', description: '', children: [], collapsed: false }; // Add to Ungrouped const ungrouped = locationGroups.find(g => g.name === 'Ungrouped'); if (ungrouped) { ungrouped.items.push(newLocation); } else { locationGroups[0].items.push(newLocation); } renderLocationGroups(); ensureActiveLocation(); } // Function to create a new Location Group function createNewLocationGroup() { const groupName = prompt('Enter group name:', `Group ${locationGroups.length}`); if (groupName && groupName.trim() !== '') { locationGroups.push({ name: groupName.trim(), items: [], collapsed: false }); renderLocationGroups(); } } // Function to render Location Groups and Items function renderLocationGroups() { locationGroupList.innerHTML = ''; locationGroups.forEach((group, groupIndex) => { // Sort items alphabetically by name group.items.sort((a, b) => a.name.localeCompare(b.name)); const groupContainer = document.createElement('div'); groupContainer.className = 'location-group'; groupContainer.style.cssText = ` margin-bottom: 10px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--active-char-color); `; const groupHeader = document.createElement('div'); groupHeader.style.cssText = ` display: flex; justify-content: space-between; align-items: center; padding: 5px 10px; background-color: var(--button-bg-color); color: var(--text-color); cursor: pointer; user-select: none; position: relative; `; groupHeader.textContent = group.name; groupHeader.addEventListener('click', () => { group.collapsed = !group.collapsed; renderLocationGroups(); }); const groupActions = document.createElement('div'); groupActions.style.cssText = ` display: flex; align-items: center; `; // Rename Group Button const renameGroupButton = document.createElement('button'); renameGroupButton.textContent = '✏️'; renameGroupButton.title = 'Rename Group'; renameGroupButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; margin-right: 2px; `; renameGroupButton.addEventListener('click', (e) => { e.stopPropagation(); renameLocationGroup(groupIndex); }); groupActions.appendChild(renameGroupButton); // Delete Group Button const deleteGroupButton = document.createElement('button'); deleteGroupButton.textContent = '🗑️'; deleteGroupButton.title = 'Delete Group'; deleteGroupButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; `; deleteGroupButton.addEventListener('click', (e) => { e.stopPropagation(); deleteLocationGroup(groupIndex); }); groupActions.appendChild(deleteGroupButton); groupHeader.appendChild(groupActions); groupContainer.appendChild(groupHeader); if (!group.collapsed) { const itemsContainer = document.createElement('div'); itemsContainer.style.cssText = ` padding: 5px 10px; display: block; `; if (group.items.length === 0) { const emptyInfo = document.createElement('div'); emptyInfo.className = 'location-slot'; emptyInfo.style.cssText = ` display: flex; align-items: center; padding: 10px; margin-bottom: 5px; border: 1px solid var(--border-color); border-radius: 5px; height: 20px; background-color: var(--bg-color); cursor: default; `; const infoText = document.createElement('span'); infoText.textContent = `Group is empty`; infoText.style.cssText = ` color: var(--text-color); `; emptyInfo.appendChild(infoText); itemsContainer.appendChild(emptyInfo); } else { group.items.forEach((location) => { const locationSlot = createLocationSlot(location, groupIndex); itemsContainer.appendChild(locationSlot); }); } groupContainer.appendChild(itemsContainer); } else { const collapsedInfo = document.createElement('div'); collapsedInfo.className = 'location-slot'; collapsedInfo.style.cssText = ` display: flex; align-items: center; padding: 10px; margin-bottom: 10px; border: 1px solid var(--border-color); border-radius: 5px; height: 20px; margin-top: 5px; background-color: var(--bg-color); cursor: default; margin-left: 10px; margin-right: 10px; `; const infoText = document.createElement('span'); infoText.textContent = `${group.items.length} items hidden`; //lime if (findActiveLocation(group.items)) { // All active infoText.style.cssText = `color: rgba(36, 242, 0, 0.75);`; } else { // All inactive infoText.style.cssText = `color: var(--text-color-darker);`; } collapsedInfo.appendChild(infoText); groupContainer.appendChild(collapsedInfo); } // Add dragover and drop events for grouping groupContainer.addEventListener('dragover', (e) => { e.preventDefault(); groupContainer.style.border = `2px dashed var(--info-bg-color)`; }); groupContainer.addEventListener('dragleave', (e) => { groupContainer.style.border = `1px solid var(--border-color)`; }); groupContainer.addEventListener('drop', (e) => { e.preventDefault(); groupContainer.style.border = `1px solid var(--border-color)`; const data = e.dataTransfer.getData('text/plain'); const { type, id } = JSON.parse(data); if (type === 'location') { moveLocationToGroup(id, groupIndex); } }); locationGroupList.appendChild(groupContainer); }); // Ensure at least one group exists if (locationGroups.length === 0) { locationGroups.push({ name: 'Ungrouped', items: [], collapsed: false }); renderLocationGroups(); } // Ensure at least one location is active ensureActiveLocation(); } // Function to generate tooltip content for a location function getLocationTooltipContent(location, isRadial = false) { if (isRadial && location.emoji === '📁') { // If it's a group node in the radial tree, only show the name return `<strong>"${location.name}"</strong>`; } let content = `<strong>"${location.name}"</strong>:<br><em>"${location.description || 'No description available.'}"</em>`; return content; } // Function to create a Location Slot DOM element (recursive) function createLocationSlot(location, groupIndex, parent = null, level = 0) { const locationSlot = document.createElement('div'); locationSlot.className = 'location-slot'; locationSlot.draggable = true; locationSlot.dataset.id = location.id; locationSlot.style.cssText = ` display: flex; align-items: center; justify-content: space-between; padding: 5px; margin-bottom: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); cursor: grab; margin-left: ${level * 20}px; `; // Drag events locationSlot.addEventListener('dragstart', (e) => { e.stopPropagation(); e.dataTransfer.setData('text/plain', JSON.stringify({ type: 'location', id: location.id })); e.currentTarget.style.opacity = '0.5'; }); locationSlot.addEventListener('dragend', (e) => { e.currentTarget.style.opacity = '1'; }); // Click event for collapsing/expanding if (location.children && location.children.length > 0) { locationSlot.style.cursor = 'pointer'; locationSlot.addEventListener('click', (e) => { e.stopPropagation(); location.collapsed = !location.collapsed; renderLocationGroups(); }); } // Left Div (Active Toggle, Emoji, Name) const leftDiv = document.createElement('div'); leftDiv.style.cssText = ` display: flex; align-items: center; flex-grow: 1; overflow: hidden; `; // Remove Collapse/Expand Button // (Not needed as per new requirement) const activeButton = document.createElement('button'); activeButton.textContent = location.active ? '🟢' : '🔴'; activeButton.title = 'Toggle Active'; activeButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); margin-right: 5px; `; activeButton.addEventListener('click', (e) => { e.stopPropagation(); // Since only one active location is allowed, deactivate others locationGroups.forEach(group => { group.items.forEach(loc => { deactivateLocationRecursive(loc); }); }); location.active = !location.active; renderLocationGroups(); }); leftDiv.appendChild(activeButton); const emojiSpan = document.createElement('span'); emojiSpan.textContent = location.emoji; emojiSpan.style.marginRight = '5px'; leftDiv.appendChild(emojiSpan); const nameSpan = document.createElement('span'); nameSpan.textContent = location.name; nameSpan.style.cssText = ` flex-grow: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--text-color); `; // Add tooltip event listeners nameSpan.addEventListener('mouseenter', (e) => { showTooltip(e, getLocationTooltipContent(location), false, location.reference); }); nameSpan.addEventListener('mouseleave', hideTooltip); nameSpan.addEventListener('dragover', hideTooltip); leftDiv.appendChild(nameSpan); locationSlot.appendChild(leftDiv); // Right Div (Edit, Delete, Export) const rightDiv = document.createElement('div'); rightDiv.style.display = 'flex'; rightDiv.style.alignItems = 'center'; const editButton = document.createElement('button'); editButton.textContent = '✏️'; editButton.title = 'Edit Location'; editButton.style.cssText = ` padding: 0px; border: none; background: none; cursor: pointer; color: var(--text-color); margin-right: 2px; `; editButton.addEventListener('click', (e) => { e.stopPropagation(); editLocation(location); }); rightDiv.appendChild(editButton); const deleteButton = document.createElement('button'); deleteButton.textContent = '🗑️'; deleteButton.title = 'Delete Location'; deleteButton.style.cssText = ` padding: 0px; border: none; background: none; cursor: pointer; color: var(--text-color); margin-right: 2px; `; deleteButton.addEventListener('click', (e) => { e.stopPropagation(); deleteLocation(location.id); }); rightDiv.appendChild(deleteButton); const exportButton = document.createElement('button'); exportButton.textContent = '⬇️'; exportButton.title = 'Export Location'; exportButton.style.cssText = ` padding: 0px; border: none; background: none; cursor: pointer; color: var(--text-color); `; exportButton.addEventListener('click', (e) => { e.stopPropagation(); exportLocation(location); }); rightDiv.appendChild(exportButton); locationSlot.appendChild(rightDiv); // Dragover and Drop events locationSlot.addEventListener('dragover', (e) => { e.preventDefault(); e.stopPropagation(); locationSlot.style.border = `2px dashed var(--info-bg-color)`; }); locationSlot.addEventListener('dragleave', (e) => { locationSlot.style.border = `1px solid var(--border-color)`; }); locationSlot.addEventListener('drop', (e) => { e.preventDefault(); e.stopPropagation(); locationSlot.style.border = `1px solid var(--border-color)`; const data = e.dataTransfer.getData('text/plain'); const { type, id } = JSON.parse(data); if (type === 'location' && id !== location.id) { moveLocationToParent(id, location); } }); // Prevent click on buttons from triggering slot collapse/expand activeButton.addEventListener('click', (e) => e.stopPropagation()); editButton.addEventListener('click', (e) => e.stopPropagation()); deleteButton.addEventListener('click', (e) => e.stopPropagation()); exportButton.addEventListener('click', (e) => e.stopPropagation()); // Recursive rendering of children const container = document.createElement('div'); container.appendChild(locationSlot); if (location.children && location.children.length > 0) { if (!location.collapsed) { location.children.forEach(child => { const childSlot = createLocationSlot(child, groupIndex, location, level + 1); container.appendChild(childSlot); }); } else { const collapsedInfo = document.createElement('div'); collapsedInfo.className = 'location-slot'; collapsedInfo.style.cssText = ` display: flex; align-items: center; padding: 10px; margin-bottom: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); height: 20px; cursor: default; margin-left: ${(level + 1) * 20}px; `; const infoText = document.createElement('span'); infoText.textContent = `${location.children.length} items hidden`; //lime if (findActiveLocation(location.children)) { // All active infoText.style.cssText = `color: rgba(36, 242, 0, 0.75);`; } else { // All inactive infoText.style.cssText = `color: var(--text-color-darker);`; } collapsedInfo.appendChild(infoText); container.appendChild(collapsedInfo); } } return container; } function deactivateLocationRecursive(location) { location.active = false; if (location.children && location.children.length > 0) { location.children.forEach(child => { deactivateLocationRecursive(child); }); } } // Function to rename a location group function renameLocationGroup(groupIndex) { const newName = prompt('Enter new group name:', locationGroups[groupIndex].name); if (newName && newName.trim() !== '') { locationGroups[groupIndex].name = newName.trim(); renderLocationGroups(); } } // Function to delete a location group function deleteLocationGroup(groupIndex) { if (locationGroups.length === 1) { alert('At least one group must exist.'); return; } if (confirm(`Are you sure you want to delete the group "${locationGroups[groupIndex].name}"? All locations in this group will be moved to "Ungrouped".`)) { const group = locationGroups.splice(groupIndex, 1)[0]; const ungrouped = locationGroups.find(g => g.name === 'Ungrouped'); if (ungrouped) { ungrouped.items = ungrouped.items.concat(group.items); } else { locationGroups.unshift({ name: 'Ungrouped', items: group.items, collapsed: false }); } renderLocationGroups(); } } // Default randomizer options for locations const defaultRandomizerOptionsAlt = { emoji: { 'Buildings': "🏠%🏡%🏫%🏭%🏢%🏰%🏩", 'Nature': "🌲%🌳%🌴%🌵%🌾%🌊%🌋", // Add more categories if needed }, name: { 'Common': "Park%Museum%Library%Coffee Shop%Restaurant%Beach%Mountain%Lake%Forest%City", 'Fantasy': "Dragon’s Lair%Enchanted Forest%Mystic Mountain%Crystal Lake%Forbidden City", // Add more categories if needed }, description: { '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.", // Add more categories if needed }, }; // User's randomizer settings for locations const randomizerSettingsAlt = { emoji: { selectedOption: 'Buildings', customList: '' }, name: { selectedOption: 'Common', customList: '' }, description: { selectedOption: 'Standard', customList: '' }, }; // Function to generate a random location function generateRandomLocationAlt() { const location = {}; // Helper to remove comments (>> ... <<) - only for string fields function removeComments(text) { if (typeof text === 'string') { return text.replace(/>>.*?<<\s*/g, ''); } return text; // If it's not a string, just return it unchanged } // Helper to process sub-lists (&!) function processSubLists(items) { const mainList = []; const subLists = []; items.forEach((item, index) => { if (index === 0) { // The first item is the main list mainList.push(...item.split(/[%\n]+/).map(i => i.trim()).filter(Boolean)); } else { // The remaining items are sub-lists, split by commas const subListItems = item.split(/[%\n]+/).map(i => i.trim()).filter(Boolean); subLists.push(subListItems); } }); return { mainList, subLists }; } // Process weighting for string items with a "*n" suffix function weightItems(list) { return list.map(item => { if (typeof item === 'string') { const match = item.match(/^(.*)\*(\d+)$/); if (match) { return { value: match[1].trim(), weight: parseInt(match[2], 10) }; } return { value: item.trim(), weight: 1 }; } return { value: item, weight: 1 }; // For non-string items (e.g., age), no trimming }); } // Function to get random item based on weighted random function getRandomFromWeightedList(list) { const totalWeight = list.reduce((sum, item) => sum + item.weight, 0); let random = Math.random() * totalWeight; for (const item of list) { if (random < item.weight) { return item.value; } random -= item.weight; } return ''; // Fallback } // Function to handle recursive sub-list resolution and replace tokens function resolveSubListReferences(text, subLists, location, recursionDepth = 0) { const MAX_RECURSION_DEPTH = 10; // Prevent infinite recursion if (recursionDepth > MAX_RECURSION_DEPTH) { console.warn('Maximum recursion depth reached while resolving sub-lists.'); return text; } // Replace sub-list references like &?n with a random item from sub-list n let resolvedText = text.replace(/&\?(\d+)/g, (match, listIndex) => { const index = parseInt(listIndex, 10); if (subLists[index] && subLists[index].length > 0) { const subList = subLists[index]; const randomSubItem = getRandomFromWeightedList(weightItems(subList)); // Recursively resolve any sub-list references within the chosen sub-list item return resolveSubListReferences(randomSubItem, subLists, location, recursionDepth + 1); } return ''; // If sub-list doesn't exist, return empty string }); // After resolving sub-list references, replace tokens like #?a, #?b, etc. // In this case, we only use #?a for the location name, but you can extend this resolvedText = resolvedText.replace(/#\?a/g, location.name || ''); return resolvedText; } // Function to get a random item from a list, including handling sub-lists and weighting function getRandomItem(field) { let items = []; if (randomizerSettingsAlt[field].customList.trim() !== '') { // Use custom list items = randomizerSettingsAlt[field].customList .split(/[,\n]+/) .map(item => item.trim()) .filter(item => item !== ''); } else { // Use default list const selectedOption = randomizerSettingsAlt[field].selectedOption; items = defaultRandomizerOptionsAlt[field][selectedOption] .split(/[,\n]+/) .map(item => item.trim()) .filter(item => item !== ''); } // Handle cases where items might be undefined or empty if (!items || items.length === 0) { return ''; } // Remove comments (only applies to string fields) items = items.map(removeComments); // First, process the entire string before splitting by commas or newlines const inputString = items.join(','); // Split the input string on "&!" to separate sub-lists from the main text const mainTextAndSubLists = inputString.split('&!'); // The main text is everything before the first "&!" const mainListString = mainTextAndSubLists.shift(); // Get the main text const subListStrings = mainTextAndSubLists; // Remaining parts are sub-lists // Now process the main list and any sub-lists const { mainList, subLists } = processSubLists([mainListString, ...subListStrings]); // Get the random item from the main list let result = getRandomFromWeightedList(weightItems(mainList)); // If the field is 'description', handle dynamic text insertion (e.g., location name) if (field === 'description' && typeof result === 'string') { // Resolve sub-list references and replace tokens for the description result = resolveSubListReferences(result, subLists, location); } else { // For non-description fields, resolve sub-list references if they exist result = resolveSubListReferences(result, subLists, location); } return result; } // Generate location fields location.emoji = getRandomItem('emoji'); location.name = getRandomItem('name'); location.description = getRandomItem('description'); return location; } // Function to open Randomizer Settings dialog for locations function openRandomizerSettingsAlt() { const settingsForm = document.createElement('form'); settingsForm.innerHTML = ` <div style="display: flex; flex-direction: column; width: 100%; min-width: 500px; margin: 0 auto;"> <!-- Scrollable content container --> <div class="scrollable-content" style="flex: 1; overflow-y: auto; max-height: 300px; padding-right: 10px;"> <!-- Fields will be inserted here dynamically --> </div> <!-- Buttons at the bottom --> <div class="settings-buttons" style="text-align: center; padding-top: 10px;"> <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> <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> </div> </div> `; const fieldsContainer = settingsForm.querySelector('.scrollable-content'); // For each randomizable field, create settings UI const fields = ['emoji', 'name', 'description']; fields.forEach(field => { const fieldDiv = document.createElement('div'); fieldDiv.style.marginBottom = '10px'; const label = document.createElement('label'); label.style.color = 'var(--text-color)'; label.style.fontSize = '12px'; label.textContent = `Randomizer for ${capitalizeFirstLetter(field)}:`; // Dropdown for default options const select = document.createElement('select'); select.name = field; 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;'; // Populate options const options = defaultRandomizerOptionsAlt[field]; for (const optionName in options) { const option = document.createElement('option'); option.value = optionName; option.textContent = optionName; if (randomizerSettingsAlt[field].selectedOption === optionName && !randomizerSettingsAlt[field].customList.trim()) { option.selected = true; } select.appendChild(option); } // Custom list textarea const textarea = document.createElement('textarea'); textarea.name = field + '_custom'; textarea.rows = field != 'description' ? 2 : 4; textarea.placeholder = 'Enter custom list, separated by "%" or new lines.'; 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;'; textarea.value = randomizerSettingsAlt[field].customList; // If custom list is non-empty, disable the select if (randomizerSettingsAlt[field].customList.trim() !== '') { select.disabled = true; } // Event listener to disable select if textarea has content textarea.addEventListener('input', (e) => { if (textarea.value.trim() !== '') { select.disabled = true; } else { select.disabled = false; } }); fieldDiv.appendChild(label); fieldDiv.appendChild(select); fieldDiv.appendChild(textarea); fieldsContainer.appendChild(fieldDiv); }); const helpLabel = document.createElement('label'); helpLabel.style.color = 'var(--text-color)'; helpLabel.style.fontSize = '18px'; helpLabel.textContent = `Text codes for advanced list creation;`; fieldsContainer.appendChild(helpLabel); // Create a paragraph element for the help text const helpText = document.createElement('p'); // Apply styling to the paragraph element helpText.style.color = 'var(--text-color)'; helpText.style.fontSize = '12px'; // Set the content of the help text helpText.innerHTML = ` <em>"%" marks the end of an item.<br> "*n" Multiply the odds an item is chosen.<br> "&!" Create a new sub list, index is assigned by order.<br> "&?n" Get a random item from a sublist.<br> "#?a" Get Location name (Description only)<br> ">>" Comment start.<br> "<<" Comment end.<br></em> `; // Append the paragraph to the container fieldsContainer.appendChild(helpText); const cancelButton = settingsForm.querySelector('#cancel-button-alt'); cancelButton.addEventListener('click', () => { closeModal(settingsModal); }); const settingsModal = createModal('Randomizer Settings', settingsForm); settingsForm.addEventListener('submit', (e) => { e.preventDefault(); // Update randomizerSettingsAlt fields.forEach(field => { const select = settingsForm[field]; const textarea = settingsForm[field + '_custom']; randomizerSettingsAlt[field].customList = textarea.value.trim(); if (randomizerSettingsAlt[field].customList !== '') { randomizerSettingsAlt[field].selectedOption = ''; } else { randomizerSettingsAlt[field].selectedOption = select.value; } }); closeModal(settingsModal); }); settingsForm.parentNode.style.backgroundColor = 'var(--bg-color-full)'; } // Function to edit a location with randomize feature function editLocation(location) { const lockState = { emoji: false, name: false, description: false }; const editForm = document.createElement('form'); editForm.innerHTML = ` <div style="width: 100%; min-width: 500px; margin: 0 auto; position: relative;"> ${createFieldAlt('Emoji', 'emoji', location.emoji)} ${createReferenceField('Reference', 'reference', location.reference)} ${createFieldAlt('Name', 'name', location.name)} ${createTextareaFieldAlt('Description', 'description', location.description)} <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> </div> `; // Helper functions to create fields with lock buttons function createFieldAlt(labelText, fieldName, value) { return ` <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;"> ${labelText}: <div style="position: relative;"> <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;"/> <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> </div> </label> `; } function createReferenceField(labelText, fieldName, value) { return ` <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;"> ${labelText}: <div style="position: relative;"> <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;" /> <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> </div> </label> `; } function createTextareaFieldAlt(labelText, fieldName, value) { return ` <label style="color: var(--text-color); font-size: 12px; margin-bottom: 8px; display: block;"> ${labelText}: <div style="position: relative;"> <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> <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> </div> </label> `; } // Copy Tooltip button const copyButton = document.createElement('button'); copyButton.textContent = '📋'; copyButton.title = 'Copy Tooltip'; copyButton.type = 'button'; // Prevent form submission copyButton.style.cssText = ` position: absolute; top: 15px; right: 50px; background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-color); `; copyButton.addEventListener('click', () => { const tooltipContent = getLocationTooltipContent(location); const textWithNewlines = tooltipContent.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]*>/g, ''); navigator.clipboard.writeText(textWithNewlines); }); editForm.appendChild(copyButton); // Randomize button const randomizeButton = document.createElement('button'); randomizeButton.textContent = '🎲'; randomizeButton.title = 'Randomize'; randomizeButton.type = 'button'; // Prevent form submission randomizeButton.style.cssText = ` position: absolute; top: 15px; right: 90px; background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-color); `; randomizeButton.addEventListener('click', () => { const randomLocation = generateRandomLocationAlt(); const fields = ['emoji', 'name', 'description']; fields.forEach((field) => { if (!lockState[field]) { editForm[field].value = randomLocation[field]; } }); }); editForm.appendChild(randomizeButton); // Settings button const settingsButton = document.createElement('button'); settingsButton.textContent = '⚙️'; settingsButton.title = 'Randomizer Settings'; settingsButton.type = 'button'; // Prevent form submission settingsButton.style.cssText = ` position: absolute; top: 15px; right: 130px; background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-color); `; settingsButton.addEventListener('click', () => { openRandomizerSettingsAlt(); }); editForm.appendChild(settingsButton); const modal = createModal('Edit Location', editForm); // Lock button functionality const lockButtons = editForm.querySelectorAll('.lock-button-alt'); lockButtons.forEach((button) => { const fieldName = button.getAttribute('data-field'); button.addEventListener('click', () => { lockState[fieldName] = !lockState[fieldName]; // Update button icon based on state button.textContent = lockState[fieldName] ? '🔒' : '🔓'; }); }); // Open Reference button functionality const referenceButton = editForm.querySelector('.open-reference-button'); referenceButton.addEventListener('click', () => { const referenceUrl = editForm.reference.value; if (isValidImageUrl(referenceUrl)) { window.open(referenceUrl, '_blank'); } else { alert('Invalid image URL'); } }); editForm.addEventListener('submit', (e) => { e.preventDefault(); location.emoji = editForm.emoji.value; location.reference = editForm.reference.value; location.name = editForm.name.value; location.description = editForm.description.value; renderLocationGroups(); ensureActiveLocation(); closeModal(modal); }); } // Function to delete a location function deleteLocation(locationId) { if (confirm('Are you sure you want to delete this location?')) { locationGroups.forEach(group => { group.items = deleteLocationRecursive(group.items, locationId); }); renderLocationGroups(); ensureActiveLocation(); } } function deleteLocationRecursive(items, locationId) { return items.filter(item => { if (item.id === locationId) { return false; } if (item.children && item.children.length > 0) { item.children = deleteLocationRecursive(item.children, locationId); } return true; }); } // Function to export all locations with groups function exportLocations() { const data = { locationGroups: locationGroups, settings: { l_radius, l_labelFontSize } // Save current settings }; const json = JSON.stringify(data, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `all_locations_with_groups.json`; link.click(); } // Function to import locations with groups function importLocations() { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.addEventListener('change', async (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { try { const importedData = JSON.parse(e.target.result); if (Array.isArray(importedData)) { // Old format locationGroups = importedData; renderLocationGroups(); } else if (importedData.locationGroups) { locationGroups = importedData.locationGroups; // Load settings if (importedData.settings) { // Assign settings l_radius = importedData.settings.l_radius || l_radius; l_labelFontSize = importedData.settings.l_labelFontSize || l_labelFontSize; } renderLocationGroups(); } else { alert('Invalid format for characters import.'); } } catch (error) { alert('Failed to import characters: ' + error.message); } }; reader.readAsText(file); }); fileInput.click(); } // Function to export a single location function exportLocation(location) { const json = JSON.stringify(location, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${location.name}.json`; link.click(); } // Function to move location to a group (uncoupling from parent) function moveLocationToGroup(locationId, targetGroupIndex) { let location = null; // Remove location from current group or parent locationGroups.forEach(group => { group.items = removeLocationRecursive(group.items, locationId, (item) => { location = item; }); }); // Add to target group if (location) { locationGroups[targetGroupIndex].items.push(location); renderLocationGroups(); ensureActiveLocation(); } } function removeLocationRecursive(items, locationId, callback) { return items.filter(item => { if (item.id === locationId) { callback(item); return false; } if (item.children && item.children.length > 0) { item.children = removeLocationRecursive(item.children, locationId, callback); } return true; }); } // Function to move location under another location (nesting) function moveLocationToParent(locationId, parentLocation) { let location = null; // Remove location from current group or parent locationGroups.forEach(group => { group.items = removeLocationRecursive(group.items, locationId, (item) => { location = item; }); }); if (location) { if(parentLocation.children == undefined){ parentLocation.children = []; } parentLocation.children.push(location); renderLocationGroups(); ensureActiveLocation(); } } // Function to create a modal function createModal(title, content) { const modalOverlay = document.createElement('div'); modalOverlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 10002; `; const modalBox = document.createElement('div'); modalBox.style.cssText = ` background-color: var(--bg-color); padding: 20px; border: 1px solid var(--border-color); border-radius: 5px; width: fit-content; min-width: 500px; max-width: 90%; box-shadow: 0 0 10px var(--shadow-color); position: relative; `; const modalTitle = document.createElement('h2'); modalTitle.textContent = title; modalTitle.style.cssText = ` margin-top: 0; color: var(--text-color); `; modalBox.appendChild(modalTitle); const closeButton = document.createElement('button'); closeButton.textContent = '✖️'; closeButton.style.cssText = ` position: absolute; top: 15px; right: 20px; background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-color); `; closeButton.addEventListener('click', () => { closeModal(modalOverlay); }); modalBox.appendChild(closeButton); modalBox.appendChild(content); modalOverlay.appendChild(modalBox); document.body.appendChild(modalOverlay); return modalOverlay; } // Function to close a modal function closeModal(modal) { if (modal && modal.parentNode) { modal.parentNode.removeChild(modal); } } // Function to ensure at least one location is active function ensureActiveLocation() { const activeLocations = []; locationGroups.forEach(group => { group.items.forEach(loc => { collectActiveLocations(loc, activeLocations); }); }); if (activeLocations.length === 0 && locationGroups.flatMap(g => g.items).length > 0) { locationGroups[0].items[0].active = true; } } function collectActiveLocations(location, activeLocations) { if (location.active) { activeLocations.push(location); } if (location.children && location.children.length > 0) { location.children.forEach(child => { collectActiveLocations(child, activeLocations); }); } } // Initial render of Character and Location Groups renderCharacterGroups(); renderLocationGroups(); // --- Plot Field --- const plotDiv = document.createElement('div'); plotDiv.style.cssText = ` display: flex; justify-content: space-between; padding: 10px; background-color: var(--bg-color); border-top: 1px solid var(--border-color); flex-shrink: 0; `; const plotInput = document.createElement('input'); plotInput.type = 'text'; plotInput.placeholder = 'Plot'; plotInput.style.cssText = ` width: 100%; padding: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); box-sizing: border-box; `; plotDiv.appendChild(plotInput); characterSidebar.appendChild(plotDiv); // --- Location Context Inputs --- const globalInputs = document.createElement('div'); globalInputs.style.cssText = ` display: flex; justify-content: space-between; padding: 10px; background-color: var(--bg-color); border-top: 1px solid var(--border-color); flex-shrink: 0; `; locationSidebar.appendChild(globalInputs); const timeInput = document.createElement('input'); timeInput.type = 'text'; timeInput.placeholder = 'Time'; timeInput.style.cssText = ` width: calc(50% - 5px); padding: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); `; globalInputs.appendChild(timeInput); const weatherInput = document.createElement('input'); weatherInput.type = 'text'; weatherInput.placeholder = 'Weather'; weatherInput.style.cssText = ` width: calc(50% - 5px); padding: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); `; globalInputs.appendChild(weatherInput); // Initial render of transparency and theme settings transparencySlider.value = defaultTransparency; themeDropdown.value = defaultTheme; // Load D3.js library await new Promise((resolve, reject) => { const d3Script = document.createElement('script'); d3Script.src = 'https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.4/d3.min.js'; d3Script.onload = resolve; d3Script.onerror = reject; document.head.appendChild(d3Script); }); // --- Modifications Start Here --- // Variables to keep track of open windows let radialTreeOpen = false; let searchPanelOpen = { character: false, location: false }; // Helper function to find an item by ID function findItemById(groups, id) { let result = null; for (let group of groups) { if (group.id === id) { return group; } if (group.items) { result = findItemInItems(group.items, id); if (result) { return result; } } } return null; } function findItemInItems(items, id) { for (let item of items) { if (item.id === id) { return item; } else if (item.children && item.children.length > 0) { let found = findItemInItems(item.children, id); if (found) return found; } } return null; } function findGroupIndexByName(groups, name) { for (let i = 0; i < groups.length; i++) { if (groups[i].name === name) { return i; } } return null; } // Helper function to remove an item by ID function removeItemById(groups, id) { for (let group of groups) { if (group.items) { group.items = removeItemFromItems(group.items, id); } } } function removeItemFromItems(items, id) { return items.filter(item => { if (item.id === id) { return false; } else if (item.children && item.children.length > 0) { item.children = removeItemFromItems(item.children, id); } return true; }); } // Helper function to check for circular references function isAncestor(itemId, possibleAncestorId, type) { let targetItem = null; if (type === 'character') { targetItem = findItemById(characterGroups, itemId); } else { targetItem = findItemById(locationGroups, itemId); } if (!targetItem) return false; function searchChildren(item) { if (item.id === possibleAncestorId) { return true; } if (item.children && item.children.length > 0) { for (let child of item.children) { if (searchChildren(child)) { return true; } } } if (item.items && item.items.length > 0) { for (let child of item.items) { if (searchChildren(child)) { return true; } } } return false; } return searchChildren(targetItem); } // Function to create a radial tree visualization function createRadialTree(data, type) { // Variables to store settings and use them globally c_radius = type === 'character' ? (window.c_radius || 300) : 300; c_labelFontSize = type === 'character' ? (window.c_labelFontSize || 12) : 12; c_nodeSize = type === 'character' ? (window.c_nodeSize || 24) : 24; l_radius = type === 'location' ? (window.l_radius || 300) : 300; l_labelFontSize = type === 'location' ? (window.l_labelFontSize || 12) : 12; l_nodeSize = type === 'location' ? (window.l_nodeSize || 24) : 24; ItemTransferTarget = null; // Close any existing radial tree window if (document.getElementById('radial-tree-window')) { document.body.removeChild(document.getElementById('radial-tree-window')); } // Create the movable window container const windowContainer = document.createElement('div'); windowContainer.id = 'radial-tree-window'; windowContainer.style.cssText = ` position: fixed; top: 100px; left: 100px; width: 600px; height: 600px; background-color: var(--bg-color); border: 1px solid var(--border-color); box-shadow: 0 0 10px var(--shadow-color); border-radius: 5px; z-index: 10002; display: flex; flex-direction: column; `; // Add a header with the title and buttons const header = document.createElement('div'); header.style.cssText = ` display: flex; align-items: center; padding: 5px; background-color: var(--button-bg-color); color: var(--text-color); cursor: move; `; const title = document.createElement('span'); title.textContent = `${type === 'character' ? 'Character' : 'Location'} Radial Tree`; title.style.flexGrow = '1'; header.appendChild(title); const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.alignItems = 'center'; // New Refresh Button const NItemButton = document.createElement('button'); NItemButton.textContent = '📝'; NItemButton.title = 'New Item'; NItemButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; margin-right: 5px; `; buttonContainer.appendChild(NItemButton); NItemButton.addEventListener('click', () => { if(type === 'character'){createNewCharacter();}else{createNewLocation();} ItemTransferTarget = null; refreshTree(svgContainer, data, type); }); // New Refresh Button const NGroupButton = document.createElement('button'); NGroupButton.textContent = '🗄️'; NGroupButton.title = 'New Group'; NGroupButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; margin-right: 5px; `; buttonContainer.appendChild(NGroupButton); NGroupButton.addEventListener('click', () => { if(type === 'character'){createNewCharacterGroup();}else{createNewLocationGroup();} ItemTransferTarget = null; refreshTree(svgContainer, data, type); }); // New Refresh Button const refreshButton = document.createElement('button'); refreshButton.textContent = '🔄'; refreshButton.title = 'Refresh Tree'; refreshButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; margin-right: 5px; `; buttonContainer.appendChild(refreshButton); // Settings Button const settingsButton = document.createElement('button'); settingsButton.textContent = '⚙️'; settingsButton.title = 'Settings'; settingsButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; margin-right: 5px; `; buttonContainer.appendChild(settingsButton); // Close Button const closeButton = document.createElement('button'); closeButton.textContent = '✖️'; closeButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; `; closeButton.addEventListener('click', () => { document.body.removeChild(windowContainer); radialTreeOpen = false; ItemTransferTarget = null; }); buttonContainer.appendChild(closeButton); header.appendChild(buttonContainer); windowContainer.appendChild(header); // Create SVG container const svgContainer = document.createElement('div'); svgContainer.style.cssText = ` flex-grow: 1; width: 100%; background-color: var(--bg-color); overflow: hidden; position: relative; `; windowContainer.appendChild(svgContainer); // Append to body document.body.appendChild(windowContainer); radialTreeOpen = true; // Make window draggable makeElementDraggable(windowContainer, header); // Create settings panel const settingsPanel = document.createElement('div'); settingsPanel.style.cssText = ` position: absolute; top: 50px; right: 10px; width: 200px; background-color: var(--bg-color); border: 1px solid var(--border-color); box-shadow: 0 0 10px var(--shadow-color); border-radius: 5px; padding: 10px; z-index: 10003; display: none; `; settingsPanel.innerHTML = ` <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> <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> <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> <button id="applySettingsButton" style="margin-top: 10px; width: 100%;">Apply</button> `; windowContainer.appendChild(settingsPanel); settingsButton.addEventListener('click', () => { settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'block' : 'none'; ItemTransferTarget = null; refreshTree(svgContainer, data, type); }); const nodeDistanceSlider = settingsPanel.querySelector('#nodeDistanceSlider'); const labelSizeSlider = settingsPanel.querySelector('#labelSizeSlider'); const nodeSizeSlider = settingsPanel.querySelector('#nodeSizeSlider'); const applySettingsButton = settingsPanel.querySelector('#applySettingsButton'); applySettingsButton.addEventListener('click', () => { if (type === 'character') { window.c_radius = parseInt(nodeDistanceSlider.value, 10); window.c_labelFontSize = parseInt(labelSizeSlider.value, 10); window.c_nodeSize = parseInt(nodeSizeSlider.value, 10); } else { window.l_radius = parseInt(nodeDistanceSlider.value, 10); window.l_labelFontSize = parseInt(labelSizeSlider.value, 10); window.l_nodeSize = parseInt(nodeSizeSlider.value, 10); } settingsPanel.style.display = 'none'; // Regenerate the tree with new settings 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); }); // Add event listener to refresh button refreshButton.addEventListener('click', () => { ItemTransferTarget = null; refreshTree(svgContainer, data, type); }); // Initial rendering of the tree generateRadialTreeVisualization(svgContainer, data, type, type === 'character' ? c_radius : l_radius, type === 'character' ? c_labelFontSize : l_labelFontSize, type === 'character' ? c_nodeSize : l_nodeSize); } function refreshTree(container, data, type) { // Clear the existing SVG container (to remove the old tree) container.innerHTML = ''; // Re-use the same data, or if necessary, fetch new data here const structuredData = structureData(type === 'character' ? characterGroups : locationGroups); // Store the current zoom, pan, and rotation before refresh const currentTransform = window.currentTransform || { x: container.clientWidth / 2, y: container.clientHeight / 2, k: 1 }; const currentRotation = window.currentRotation || 0; // Regenerate the tree visualization with the existing settings const radius = type === 'character' ? window.c_radius : window.l_radius; const labelFontSize = type === 'character' ? window.c_labelFontSize : window.l_labelFontSize; const nodeSize = type === 'character' ? window.c_nodeSize : window.l_nodeSize; // Regenerate the tree and apply the stored transformation generateRadialTreeVisualization(container, structuredData, type, radius, labelFontSize, nodeSize, currentTransform, currentRotation); } function generateRadialTreeVisualization(container, data, type, radius = 300, labelFontSize = 12, nodeSize = 24, initialTransform = null, initialRotation = 0) { // Clear the container container.innerHTML = ''; // Set dimensions const width = container.clientWidth; const height = container.clientHeight; // Current zoom, pan, and rotation state let currentRotation = initialRotation; let currentTransform = initialTransform || { x: width / 2, y: height / 2, k: 1 }; // Create a D3 zoom behavior const zoomBehavior = d3.zoom() .scaleExtent([0.5, 5]) .on('zoom', zoomed); // Create SVG element with zoom and pan functionality const svg = d3.select(container) .append('svg') .attr('width', width) .attr('height', height) .call(zoomBehavior) // Apply the zoom behavior .append('g') .attr('transform', `translate(${currentTransform.x},${currentTransform.y}) scale(${currentTransform.k}) rotate(${currentRotation})`); // Apply the initial zoom, pan, and rotation d3.select(container).select('svg').call(zoomBehavior.transform, d3.zoomIdentity.translate(currentTransform.x, currentTransform.y).scale(currentTransform.k)); function zoomed(event) { // Update the current pan and zoom values currentTransform = event.transform; // Apply the combined transformation (zoom, pan, and rotation) svg.attr('transform', `translate(${currentTransform.x},${currentTransform.y}) scale(${currentTransform.k}) rotate(${currentRotation})`); // Store the transformation globally so we can retain it after refresh window.currentTransform = currentTransform; } // Convert data to D3 hierarchy const root = d3.hierarchy({ name: type === 'character' ? 'Characters' : 'Locations', children: data }) .sum(d => d.children ? 0 : 1); // Create the radial tree layout const tree = d3.tree() .size([2 * Math.PI, radius]) .separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth); tree(root); // Create links const link = svg.append('g') .selectAll('.link') .data(root.links()) .join('path') .attr('class', 'link') .attr('d', d3.linkRadial() .angle(d => d.x) .radius(d => d.y)) .attr('stroke', 'var(--muted-bg-color)') .attr('fill', 'none'); // Create nodes const node = svg.append('g') .selectAll('.node') .data(root.descendants()) .join('g') .attr('class', 'node') .attr('transform', d => ` rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0) `); // Use emoji as node symbols node.append('text') .attr('dy', '0.31em') .attr('font-size', `${nodeSize}px`) // Use nodeSize for font size .attr('text-anchor', 'middle') .attr('transform', d => `rotate(0)`) // No flipping of text .text(d => d.data.emoji || '⭕') // Use emoji or a default symbol .style('font-size', `${nodeSize}px`) .style('fill', 'var(--text-color)') .on('mouseover', (event, d) => { const content = type === 'character' ? getCharacterTooltipContent(d.data, true) : getLocationTooltipContent(d.data, true); showTooltip_radial(event, content); }) .on('mousemove', (event) => { moveTooltip_radial(event); }) .on('mouseout', hideTooltip_radial) .on('contextmenu', (event, d) => { event.preventDefault(); navigateToItem(d.data.id, type); // Navigate to the item in the sidebar }); // Labels (without emoji) node.append('text') .attr('dy', '0.31em') .attr('x', +nodeSize / 2 + 5) .attr('text-anchor', 'center') // Always center the text .attr('transform', d => `rotate(0)`) // No flipping of text .text(d => d.data.name) // Do not include emoji in label .style('font-size', `${labelFontSize}px`) .style('fill', d => (ItemTransferTarget && d.data.id === ItemTransferTarget.id) ? 'var(--warning-bg-color)' : 'var(--text-color)') .on('mouseover', (event, d) => { const content = type === 'character' ? getCharacterTooltipContent(d.data, true) : getLocationTooltipContent(d.data, true); showTooltip_radial(event, content); }) .on('mousemove', (event) => { moveTooltip_radial(event); }) .on('mouseout', hideTooltip_radial) .on('contextmenu', (event, d) => { event.preventDefault(); if (ItemTransferTarget == null) { // Check if it's an item (not a group) if (d.data.description != undefined) { // items undefined means it's not a group CreateContextMenu( 'var(--button-bg-color)', 'var(--text-color)', ['Locate', 'Edit', 'Move', 'Delete'], [ // Locate function() { navigateToItem(d.data.id, type); }, // Edit function() { const itemId = d.data.id; if (type === 'character') { const character = findItemById(characterGroups, itemId); if (character) { editCharacter(character); renderCharacterGroups(); refreshTree(container, data, type); } } else { const location = findItemById(locationGroups, itemId); if (location) { editLocation(location); renderLocationGroups(); refreshTree(container, data, type); } } }, // Move function() { ItemTransferTarget = d.data; refreshTree(container, data, type); }, // Delete function() { const itemId = d.data.id; if (type === 'character') { deleteCharacter(itemId); renderCharacterGroups(); refreshTree(container, data, type); } else { deleteLocation(itemId); renderLocationGroups(); refreshTree(container, data, type); } } ] ); } else { // It's a group CreateContextMenu( 'var(--button-bg-color)', 'var(--text-color)', ['Edit', 'Delete'], [ // Edit Group function() { const groupName = d.data.name; if (type === 'character') { const groupIndex = findGroupIndexByName(characterGroups, groupName); if (groupIndex !== null) { renameGroup(groupIndex); renderCharacterGroups(); refreshTree(container, data, type); } } else { const groupIndex = findGroupIndexByName(locationGroups, groupName); if (groupIndex !== null) { renameLocationGroup(groupIndex); renderLocationGroups(); refreshTree(container, data, type); } } }, // Delete Group function() { const groupName = d.data.name; if (type === 'character') { const groupIndex = findGroupIndexByName(characterGroups, groupName); if (groupIndex !== null) { deleteGroup(groupIndex); renderCharacterGroups(); refreshTree(container, data, type); } } else { const groupIndex = findGroupIndexByName(locationGroups, groupName); if (groupIndex !== null) { deleteLocationGroup(groupIndex); renderLocationGroups(); refreshTree(container, data, type); } } } ] ); } } else { // Moving an item if (ItemTransferTarget.id === d.data.id) { // Stop moving CreateContextMenu( 'var(--button-bg-color)', 'var(--text-color)', ['Stop'], [ function() { ItemTransferTarget = null; refreshTree(container, data, type); } ] ); } else { // Place the item CreateContextMenu( 'var(--button-bg-color)', 'var(--text-color)', ['Place'], [ function() { // Check for circular reference if (isAncestor(ItemTransferTarget.id, d.data.id, type)) { alert('Cannot move an item into its descendant.'); return; } if (d.data.description != undefined) { if (type === 'character') { moveCharacterToParent(ItemTransferTarget.id, findItemById(characterGroups, d.data.id)); } else { moveLocationToParent(ItemTransferTarget.id, findItemById(locationGroups, d.data.id)); } } else { // New parent is a group const groupName = d.data.name; if (type === 'character') { const groupIndex = findGroupIndexByName(locationGroups, groupName); moveCharacterToGroup(ItemTransferTarget.id, groupIndex); } else { const groupIndex = findGroupIndexByName(locationGroups, groupName); moveLocationToGroup(ItemTransferTarget.id, groupIndex); } } // Clear transfer target ItemTransferTarget = null; // Refresh if (type === 'character') { renderCharacterGroups(); } else { renderLocationGroups(); } refreshTree(container, data, type); } ] ); } } showMenu(event); }); // --- Rotation Knob UI --- const knobContainer = document.createElement('div'); knobContainer.style.cssText = ` position: absolute; bottom: 10px; right: 10px; width: 100px; height: 100px; z-index: 10003; background-color: var(--bg-color); border: 1px solid var(--border-color); border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; `; const knob = document.createElement('div'); knob.style.cssText = ` width: 60px; height: 60px; background-color: var(--button-bg-color); border-radius: 50%; position: relative; transform: rotate(${currentRotation}deg); /* Apply initial rotation */ `; // Add a small indicator inside the knob to show the starting point const knobIndicator = document.createElement('div'); knobIndicator.style.cssText = ` position: absolute; top: 5px; left: 50%; transform: translateX(-50%); width: 10px; height: 10px; background-color: var(--text-color); border-radius: 50%; `; knob.appendChild(knobIndicator); knobContainer.appendChild(knob); container.appendChild(knobContainer); let isDragging = false; let previousAngle = 0; // Utility function to get the angle of the mouse relative to the center of the knob function getAngleFromEvent(event, element) { const rect = element.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const deltaX = event.clientX - centerX; const deltaY = event.clientY - centerY; const angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI); return angle; } // Function to start dragging the knob knob.addEventListener('mousedown', (event) => { isDragging = true; previousAngle = getAngleFromEvent(event, knob); event.preventDefault(); }); // Function to handle dragging document.addEventListener('mousemove', (event) => { if (isDragging) { const currentAngle = getAngleFromEvent(event, knob); const deltaAngle = currentAngle - previousAngle; // Update the current rotation currentRotation = (currentRotation + deltaAngle) % 360; if (currentRotation < 0) currentRotation += 360; // Ensure positive rotation // Apply the combined transformation (rotation, zoom, and pan) svg.attr('transform', `translate(${currentTransform.x},${currentTransform.y}) scale(${currentTransform.k}) rotate(${currentRotation})`); // Rotate the knob visually knob.style.transform = `rotate(${currentRotation}deg)`; // Store the updated rotation globally so we can retain it after refresh window.currentRotation = currentRotation; // Update previous angle for the next move previousAngle = currentAngle; } }); // Stop dragging when mouse is released document.addEventListener('mouseup', () => { isDragging = false; }); // Resize listener window.addEventListener('resize', () => { svg.attr('width', container.clientWidth).attr('height', container.clientHeight); }); } function navigateToItem(id, type) { if (type === 'character') { // Expand groups and scroll to the character characterGroups.forEach(group => { expandItemAndFind(group, id, type); }); renderCharacterGroups(); const itemElement = document.querySelector(`#character-group-list [data-id="${id}"]`); if (itemElement) { // Ensure the character menu stays open if (characterSidebar.style.right === '-350px') { characterToggleButton.click(); // Open the sidebar if it's closed } itemElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } else { // Expand groups and scroll to the location locationGroups.forEach(group => { expandItemAndFind(group, id, type); }); renderLocationGroups(); const itemElement = document.querySelector(`#location-group-list [data-id="${id}"]`); if (itemElement) { // Ensure the location menu stays open if (locationSidebar.style.left === '-350px') { locationToggleButton.click(); // Open the sidebar if it's closed } itemElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } } // Function to expand groups and find item (recursive) function expandItemAndFind(item, id, type) { if (item.id === id) { return true; } let found = false; if (item.items && item.items.length > 0) { for (let i = 0; i < item.items.length; i++) { if (expandItemAndFind(item.items[i], id, type)) { item.collapsed = false; // Expand parent found = true; } } } if (item.children && item.children.length > 0) { for (let i = 0; i < item.children.length; i++) { if (expandItemAndFind(item.children[i], id, type)) { item.collapsed = false; // Expand parent found = true; } } } return found; } // Function to create structured data for D3.js function structureData(groups) { return groups.map(group => ({ name: group.name, emoji: '📁', // Assign folder emoji to groups id: group.id || generateId(), children: group.items.map(item => mapItem(item)) })); } function mapItem(item) { return { id: item.id, name: item.name, emoji: item.emoji || '', // Use item's emoji, empty string if none description: item.description || item.bio || '', children: item.children ? item.children.map(child => mapItem(child)) : [] }; } // Tooltips for Radial Tree let radialTooltipElement = null; function showTooltip_radial(event, content) { hideTooltip_radial(); // Remove existing tooltip if any // Create new tooltip element radialTooltipElement = document.createElement('div'); radialTooltipElement.style.cssText = ` position: absolute; z-index: 100003; background-color: var(--bg-tool); color: var(--text-color); border: 1px solid var(--border-color); padding: 5px; border-radius: 5px; font-size: 12px; max-width: 600px; white-space: pre-wrap; box-shadow: 0 0 10px var(--shadow-color); pointer-events: none; `; radialTooltipElement.innerHTML = content; radialTooltipElement.style.left = `${event.pageX + 10}px`; radialTooltipElement.style.top = `${event.pageY + 10}px`; document.body.appendChild(radialTooltipElement); } function moveTooltip_radial(event) { if (radialTooltipElement) { radialTooltipElement.style.left = `${event.pageX + 10}px`; radialTooltipElement.style.top = `${event.pageY + 10}px`; } } function hideTooltip_radial() { if (radialTooltipElement && radialTooltipElement.parentNode) { radialTooltipElement.parentNode.removeChild(radialTooltipElement); radialTooltipElement = null; // Set to null after removing } } // Adding buttons to headers // Character Header Modifications characterHeader.innerHTML = ''; const characterHeaderContainer = document.createElement('div'); characterHeaderContainer.style.cssText = ` display: flex; align-items: center; justify-content: center; `; // Search Button for Characters (Left Side) const characterSearchButton = document.createElement('button'); characterSearchButton.textContent = '🔍'; characterSearchButton.title = 'Open Search Panel'; characterSearchButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; font-size: 18px; margin-right: 10px; `; characterHeaderContainer.appendChild(characterSearchButton); characterSearchButton.addEventListener('click', () => { toggleSearchPanel('character'); }); // Character Title const characterTitle = document.createElement('span'); characterTitle.textContent = 'Characters'; characterTitle.style.cssText = ` color: var(--text-color); flex-grow: 1; text-align: center; `; characterHeaderContainer.appendChild(characterTitle); // Radial Tree Button for Characters (Right Side) const characterRadialButton = document.createElement('button'); characterRadialButton.textContent = '🌐'; characterRadialButton.title = 'Open Radial Tree'; characterRadialButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; font-size: 18px; margin-left: 10px; `; characterHeaderContainer.appendChild(characterRadialButton); characterHeader.appendChild(characterHeaderContainer); // Location Header Modifications locationHeader.innerHTML = ''; const locationHeaderContainer = document.createElement('div'); locationHeaderContainer.style.cssText = ` display: flex; align-items: center; justify-content: center; `; // Radial Tree Button for Locations (Left Side) const locationRadialButton = document.createElement('button'); locationRadialButton.textContent = '🌐'; locationRadialButton.title = 'Open Radial Tree'; locationRadialButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; font-size: 18px; margin-right: 10px; `; locationHeaderContainer.appendChild(locationRadialButton); // Location Title const locationTitle = document.createElement('span'); locationTitle.textContent = 'Locations'; locationTitle.style.cssText = ` color: var(--text-color); flex-grow: 1; text-align: center; `; locationHeaderContainer.appendChild(locationTitle); // Search Button for Locations (Right Side) const locationSearchButton = document.createElement('button'); locationSearchButton.textContent = '🔍'; locationSearchButton.title = 'Open Search Panel'; locationSearchButton.style.cssText = ` background: none; border: none; color: var(--text-color); cursor: pointer; font-size: 18px; margin-left: 10px; `; locationHeaderContainer.appendChild(locationSearchButton); locationHeader.appendChild(locationHeaderContainer); // Function to open search panel (Integrated into Sidebar) function toggleSearchPanel(type) { if (type === 'character') { if (searchPanelOpen.character) { // Close search panel characterSearchContainer.style.display = 'none'; searchPanelOpen.character = false; } else { // Open search panel characterSearchContainer.style.display = 'block'; searchPanelOpen.character = true; characterSearchInput.focus(); } } else { if (searchPanelOpen.location) { // Close search panel locationSearchContainer.style.display = 'none'; searchPanelOpen.location = false; } else { // Open search panel locationSearchContainer.style.display = 'block'; searchPanelOpen.location = true; locationSearchInput.focus(); } } } // Character Search Panel const characterSearchContainer = document.createElement('div'); characterSearchContainer.style.cssText = ` position: relative; display: none; padding: 10px; background-color: var(--bg-color); `; const characterSearchInput = document.createElement('input'); characterSearchInput.type = 'text'; characterSearchInput.placeholder = 'Search...'; characterSearchInput.style.cssText = ` width: 100%; margin-bottom: 0px; padding: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); `; // Adjust the results container to match the search input width const characterResultsContainer = document.createElement('div'); characterResultsContainer.style.cssText = ` position: absolute; top: calc(100% + 5px); /* Position below the search input */ left: 0; width: 100%; max-height: 200px; overflow-y: auto; background-color: var(--bg-color); border: 1px solid var(--border-color); box-shadow: 0 0 10px var(--shadow-color); z-index: 1000; `; characterSearchContainer.appendChild(characterSearchInput); characterSearchContainer.appendChild(characterResultsContainer); characterSidebar.insertBefore(characterSearchContainer, characterGroupList); characterSearchInput.addEventListener('input', () => { const query = characterSearchInput.value.trim().toLowerCase(); characterResultsContainer.innerHTML = ''; if (query.length > 0) { const results = searchItems(query, 'character'); if (results.length > 0) { results.forEach(result => { const resultItem = document.createElement('div'); resultItem.style.cssText = ` padding: 5px; border-bottom: 1px solid var(--border-color); cursor: pointer; color: var(--text-color); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; `; resultItem.addEventListener('click', () => { navigateToItem(result.id, 'character'); characterSearchInput.value = ''; characterResultsContainer.innerHTML = ''; }); resultItem.innerHTML = formatSearchResult(result); characterResultsContainer.appendChild(resultItem); }); } else { characterResultsContainer.textContent = 'No results found.'; } } }); // Location Search Panel const locationSearchContainer = document.createElement('div'); locationSearchContainer.style.cssText = ` position: relative; display: none; padding: 10px; background-color: var(--bg-color); `; const locationSearchInput = document.createElement('input'); locationSearchInput.type = 'text'; locationSearchInput.placeholder = 'Search...'; locationSearchInput.style.cssText = ` width: 100%; margin-bottom: 0px; padding: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); `; // Adjust the results container to match the search input width const locationResultsContainer = document.createElement('div'); locationResultsContainer.style.cssText = ` position: absolute; top: calc(100% + 5px); /* Position below the search input */ left: 0; width: 100%; max-height: 200px; overflow-y: auto; background-color: var(--bg-color); border: 1px solid var(--border-color); box-shadow: 0 0 10px var(--shadow-color); z-index: 1000; `; locationSearchContainer.appendChild(locationSearchInput); locationSearchContainer.appendChild(locationResultsContainer); locationSidebar.insertBefore(locationSearchContainer, locationGroupList); locationSearchInput.addEventListener('input', () => { const query = locationSearchInput.value.trim().toLowerCase(); locationResultsContainer.innerHTML = ''; if (query.length > 0) { const results = searchItems(query, 'location'); if (results.length > 0) { results.forEach(result => { const resultItem = document.createElement('div'); resultItem.style.cssText = ` padding: 5px; border-bottom: 1px solid var(--border-color); cursor: pointer; color: var(--text-color); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; `; resultItem.addEventListener('click', () => { navigateToItem(result.id, 'location'); locationSearchInput.value = ''; locationResultsContainer.innerHTML = ''; }); resultItem.innerHTML = formatSearchResult(result); locationResultsContainer.appendChild(resultItem); }); } else { locationResultsContainer.textContent = 'No results found.'; } } }); locationSearchButton.addEventListener('click', () => { toggleSearchPanel('location'); }); // Radial Tree Button Event Listeners characterRadialButton.addEventListener('click', () => { if (radialTreeOpen) { document.getElementById('radial-tree-window').remove(); radialTreeOpen = false; } const data = structureData(characterGroups); createRadialTree(data, 'character'); }); locationRadialButton.addEventListener('click', () => { if (radialTreeOpen) { document.getElementById('radial-tree-window').remove(); radialTreeOpen = false; } const data = structureData(locationGroups); createRadialTree(data, 'location'); }); // Function to search items function searchItems(query, type) { const results = []; const groups = type === 'character' ? characterGroups : locationGroups; groups.forEach(group => { searchGroup(group, query, results, []); }); return results; } function searchGroup(group, query, results, path) { const newPath = [...path, group.name]; group.items.forEach(item => { searchItem(item, query, results, newPath); }); } function searchItem(item, query, results, path) { const newPath = [...path, item.name]; if (item.name.toLowerCase().includes(query)) { results.push({ id: item.id, path: newPath }); } if (item.children && item.children.length > 0) { item.children.forEach(child => { searchItem(child, query, results, newPath); }); } } function formatSearchResult(result) { let html = ''; const maxCharacters = 42; // Adjust as needed let displayPath = result.path.join(' > '); if (displayPath.length > maxCharacters) { displayPath = '...' + displayPath.slice(-maxCharacters); } const pathSegments = displayPath.split(' > '); for (let i = 0; i < pathSegments.length - 1; i++) { html += `<span style="font-size: 12px; color: var(--text-color-darker); white-space: nowrap;">${pathSegments[i]} > </span>`; } html += `<strong style="white-space: nowrap;">${pathSegments[pathSegments.length - 1]}</strong>`; return `<div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${html}</div>`; } function GetImage(url, callback) { // Validate the URL (basic image URL validation) if (!isValidImageUrl(url)) { console.error("Invalid image URL:", url); if (typeof callback === "function") { callback(null); } return; } // Fetch the image GM_xmlhttpRequest({ method: "GET", url: url, responseType: "arraybuffer", // Get the raw binary data onload: function(response) { if (response.status === 200) { // Convert the binary data to a Base64 string const base64Data = arrayBufferToBase64(response.response); // Determine the MIME type from the URL or default to image/jpeg const mimeType = getMimeTypeFromUrl(url) || "image/jpeg"; const dataUri = `data:${mimeType};base64,${base64Data}`; // Call the callback with the Base64 data URI if (typeof callback === "function") { callback(dataUri); } } else { console.error(`Failed to fetch image. HTTP Status: ${response.status}`); if (typeof callback === "function") { callback(null); } } }, onerror: function(error) { console.error("Error fetching the image:", error); if (typeof callback === "function") { callback(null); } } }); } function isValidImageUrl(url) { return /^(https?:\/\/.*\.(?:png|jpg|jpeg|gif|webp|bmp|svg))$/i.test(url); } function arrayBufferToBase64(buffer) { let binary = ''; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } function getMimeTypeFromUrl(url) { const extension = url.split('.').pop().toLowerCase(); switch (extension) { case 'png': return 'image/png'; case 'jpg': case 'jpeg': return 'image/jpeg'; case 'gif': return 'image/gif'; case 'webp': return 'image/webp'; case 'bmp': return 'image/bmp'; case 'svg': return 'image/svg+xml'; default: return null; } } })();