Yeopardy for Stumblechat

The competetive game of knowledge

目前为 2024-03-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Yeopardy for Stumblechat
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description The competetive game of knowledge
  6. // @author MeKLiN
  7. // @match https://stumblechat.com/room/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=stumblechat.com
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // Define yourHandle variable
  17. const yourHandle = "myHandle"; // Replace "myHandle" with the actual handle
  18.  
  19. // Function to create user list div
  20. function createUserListDiv() {
  21. const userListDiv = document.createElement("div");
  22. userListDiv.id = "userList";
  23. userListDiv.style.position = "absolute"; // Change to absolute positioning
  24. userListDiv.style.top = "100px"; // Adjust top position as needed
  25. userListDiv.style.left = "10px"; // Adjust left position as needed
  26. userListDiv.style.height = "calc(100% - 100px)"; // Adjust height to fill remaining space
  27. userListDiv.style.overflowY = "auto";
  28. userListDiv.style.color = "#ffffff";
  29. userListDiv.style.padding = "10px";
  30. userListDiv.style.zIndex = "2"; // Set a higher z-index value
  31. userListDiv.style.display = "none"; // Hide the custom user list by default
  32. return userListDiv;
  33. }
  34.  
  35. // Function to add user to user list
  36. function addUserToUserList(user) {
  37. const userList = document.getElementById("userList");
  38. if (!userList) return;
  39.  
  40. const userItem = document.createElement("div");
  41. userItem.textContent = `${user.username}`;
  42.  
  43. // Add colored dot based on user status
  44. const dot = document.createElement("span");
  45. dot.textContent = user.active ? "🟢" : "🔴";
  46. dot.style.color = user.active ? "green" : "red";
  47. userItem.appendChild(dot);
  48.  
  49. // Add custom icon for the user
  50. if (user.icon) {
  51. const customIcon = document.createElement("span");
  52. customIcon.textContent = user.icon;
  53. userItem.appendChild(customIcon);
  54. }
  55.  
  56. userList.appendChild(userItem);
  57.  
  58. // If user is not active and not yourself, schedule removal after 30 seconds
  59. if (!user.active && user.handle !== yourHandle) {
  60. setTimeout(() => {
  61. userItem.remove();
  62. }, 30000); // 30 seconds delay
  63. }
  64. }
  65.  
  66. // Function to update handleUserMap and add users to custom user list
  67. function updateUserListAndMapOnJoin(user) {
  68. // Update handleUserMap with the new user
  69. handleUserMap[user.handle] = user.username || user.nick;
  70. // Add the new user to the custom user list
  71. addUserToUserList(user);
  72. }
  73.  
  74. // Function to parse WebSocket message and update user list accordingly
  75. function parseWebSocketMessage(message) {
  76. const parsedMessage = JSON.parse(message);
  77. if (parsedMessage.stumble === "joined") {
  78. const selfHandle = parsedMessage.self.handle;
  79. const userList = parsedMessage.userlist;
  80. if (userList && userList.length > 0) {
  81. userList.forEach(user => {
  82. addUserToUserList({
  83. username: user.username,
  84. handle: user.handle,
  85. active: user.handle === selfHandle, // Check if it's your own user
  86. icon: user.icon // Custom icon for the user
  87. });
  88. });
  89. }
  90. } else if (parsedMessage.stumble === "msg") {
  91. const text = parsedMessage.text;
  92. if (text && text.startsWith("#icon")) {
  93. // Extract the Unicode character after "#icon "
  94. const icon = text.charAt(text.indexOf(" ") + 1);
  95. // Set custom icon for the user
  96. addUserIcon(icon);
  97. }
  98. }
  99. }
  100.  
  101. // Function to add custom icon for the user
  102. function addUserIcon(icon) {
  103. const userItem = document.querySelector(`#userList div:contains('${yourHandle}')`);
  104. if (userItem) {
  105. const customIcon = document.createElement("span");
  106. customIcon.textContent = icon;
  107. customIcon.style.marginLeft = "5px"; // Adjust margin as needed
  108. userItem.appendChild(customIcon);
  109. }
  110. }
  111.  
  112. // Call the function to update the user list
  113. function updateUserListOnMessage(userList) {
  114. return function(message) {
  115. const parsedMessage = JSON.parse(message);
  116. if (parsedMessage.stumble === "join" || parsedMessage.stumble === "msg" || parsedMessage.stumble === "joined") {
  117. // Add user to user list
  118. addUserToUserList(parsedMessage);
  119. }
  120. };
  121. }
  122.  
  123. // Function to create WebSocket messages div
  124. function createWebSocketMessagesDiv() {
  125. const div = document.createElement("div");
  126. div.id = "webSocketMessages";
  127. div.style.position = "relative";
  128. div.style.height = "25%";
  129. div.style.paddingLeft = "2px";
  130. div.style.visibility = "visible"; // Ensure the div is visible
  131. div.style.willChange = "transform";
  132. div.style.boxSizing = "border-box";
  133. div.style.overflowX = "hidden";
  134. div.style.overflowY = "auto";
  135. div.style.color = "#ffffff"; // Set font color to white
  136. div.style.padding = "10px"; // Example padding
  137. div.style.zIndex = "2"; // Set a higher z-index value for the WebSocket messages div
  138.  
  139. // Additional styles for specific scenarios
  140. div.style.display = "flex";
  141. div.style.flexDirection = "column";
  142. div.style.justifyContent = "flex-end";
  143. div.style.fontSize = "18px";
  144.  
  145. div.style.whiteSpace = "pre-wrap"; // Allow text to wrap within the container
  146. div.style.wordWrap = "break-word"; // Allow long words to break and wrap
  147.  
  148. // Locate the chat-position div
  149. const chatPositionDiv = document.getElementById("chat-position");
  150. if (chatPositionDiv) {
  151. // Append custom div to the chat-position div
  152. chatPositionDiv.appendChild(div);
  153. } else {
  154. // If chat-position div not found, append to document body as fallback
  155. document.body.appendChild(div);
  156. }
  157. }
  158.  
  159. // Call the function to create the user list div
  160. const userListDiv = createUserListDiv();
  161. const chatContainer = document.getElementById("chat-container");
  162. if (chatContainer) {
  163. chatContainer.appendChild(userListDiv); // Append to chat container instead
  164. } else {
  165. document.body.appendChild(userListDiv);
  166. }
  167.  
  168. // Call the function to create the WebSocket messages div
  169. createWebSocketMessagesDiv();
  170.  
  171. // Override WebSocket constructor to intercept WebSocket creation
  172. const originalWebSocket = window.WebSocket;
  173. window.WebSocket = function(url, protocols) {
  174. console.log('WebSocket URL:', url);
  175.  
  176. // Call original WebSocket constructor
  177. const ws = new originalWebSocket(url, protocols);
  178.  
  179. // Event listener for receiving messages
  180. ws.addEventListener('message', event => {
  181. const parsedMessage = JSON.parse(event.data);
  182. if (parsedMessage.stumble === "joined") {
  183. // Update handleUserMap and custom user list when a user joins
  184. const userList = parsedMessage.userlist;
  185. if (userList && userList.length > 0) {
  186. userList.forEach(user => {
  187. updateUserListAndMapOnJoin(user);
  188. });
  189. }
  190. } else {
  191. // Display WebSocket message
  192. displayWebSocketMessage(event.data);
  193. }
  194. });
  195.  
  196. return ws;
  197. };
  198.  
  199. // Function to remove user from custom user list
  200. function removeUserFromUserList(handle) {
  201. const userList = document.getElementById("userList");
  202. if (userList) {
  203. const userElements = userList.querySelectorAll("div");
  204. userElements.forEach(userElement => {
  205. if (userElement.textContent.includes(`(${handle})`)) {
  206. userElement.remove();
  207. }
  208. });
  209. }
  210. }
  211.  
  212. // Handle-username mapping
  213. let handleUserMap = {};
  214.  
  215. // Function to display WebSocket messages and update user list
  216. function displayWebSocketMessage(message) {
  217. const parsedMessage = JSON.parse(message);
  218. if (parsedMessage.stumble === "join") {
  219. // Handle join messages: Extract handle and username from the message
  220. const { handle, username } = parsedMessage;
  221. // Map handle to username in handleUserMap
  222. handleUserMap[handle] = username;
  223. // Add the user to the custom user list
  224. addUserToUserList({ handle, username });
  225. } else if (parsedMessage.stumble === "msg") {
  226. // Handle message messages: Extract handle and text from the message
  227. const { handle, text } = parsedMessage;
  228. // Retrieve username from handleUserMap or use handle if not found
  229. const username = handleUserMap[handle] || handle;
  230. // Display the message in the WebSocket messages div
  231. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  232. if (webSocketMessagesDiv) {
  233. // Append the message with a newline character
  234. webSocketMessagesDiv.textContent += `${username}: ${text}\n`;
  235. // Scroll to the bottom of the messages div
  236. webSocketMessagesDiv.scrollTop = webSocketMessagesDiv.scrollHeight;
  237. }
  238. } else if (parsedMessage.stumble === "joined") {
  239. // Handle joined messages: Add users to handleUserMap and custom user list
  240. const userList = parsedMessage.userlist;
  241. if (userList && userList.length > 0) {
  242. userList.forEach(user => {
  243. // Extract username from either "username" or "nick"
  244. const username = user.username || user.nick;
  245. // Map handle to username in handleUserMap
  246. handleUserMap[user.handle] = username;
  247. // Add the user to the custom user list
  248. addUserToUserList({ handle: user.handle, username });
  249. });
  250. }
  251. } else if (parsedMessage.stumble === "quit") {
  252. // Handle quit messages: Remove users from handleUserMap and custom user list
  253. const handle = parsedMessage.handle;
  254. const username = handleUserMap[handle];
  255. if (username) {
  256. // Remove the handle from handleUserMap
  257. delete handleUserMap[handle];
  258. // Remove the user from the custom user list
  259. removeUserFromUserList(handle);
  260. }
  261. }
  262. }
  263.  
  264. // Function to toggle visibility of custom user list
  265. function toggleCustomUserList() {
  266. const userListDiv = document.getElementById("userList");
  267. if (userListDiv) {
  268. userListDiv.style.display = userListDiv.style.display === "none" ? "block" : "none";
  269. }
  270. }
  271.  
  272. // Add a button to toggle visibility of custom user list
  273. const toggleButton = document.createElement("button");
  274. toggleButton.textContent = "U";
  275. toggleButton.style.position = "fixed";
  276. toggleButton.style.top = "10px";
  277. toggleButton.style.left = "10px";
  278. toggleButton.addEventListener("click", toggleCustomUserList);
  279. document.body.appendChild(toggleButton);
  280.  
  281. // Call the function to create the WebSocket messages div
  282. createWebSocketMessagesDiv();
  283.  
  284. // Function to clear messages
  285. function clr() {
  286. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  287. if (webSocketMessagesDiv) {
  288. webSocketMessagesDiv.innerHTML = "";
  289. }
  290. }
  291.  
  292. function IrcMode() {
  293. const chatContent = document.getElementById("chat-content");
  294. if (chatContent) {
  295. // Remove the chat-content div from the DOM
  296. chatContent.remove();
  297. // Move the webSocketMessagesDiv and the form input to fixed position
  298. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  299. const formInput = document.getElementById("input");
  300. if (webSocketMessagesDiv && formInput) {
  301. webSocketMessagesDiv.style.position = "fixed";
  302. webSocketMessagesDiv.style.top = "0px";
  303. webSocketMessagesDiv.style.height = "90%"; // Adjust height to 90%
  304. webSocketMessagesDiv.style.overflowY = "auto"; // Add scrollbar
  305. formInput.style.position = "fixed";
  306. formInput.style.bottom = "0px";
  307. }
  308. // Create Save Text button
  309. createSaveTextButton();
  310. }
  311. // Disable the room.js functionality
  312. disableRoomJS();
  313. }
  314.  
  315. // Function to save text content without <br> elements
  316. async function saveText() {
  317. console.log("Save Text button clicked."); // Debugging: Log button click
  318. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  319. if (webSocketMessagesDiv) {
  320. console.log("webSocketMessagesDiv found:", webSocketMessagesDiv); // Debugging: Log webSocketMessagesDiv
  321. const textContent = webSocketMessagesDiv.textContent.replaceAll('\n', '\r\n');
  322. console.log("Text content:", textContent); // Debugging: Log extracted text content
  323. try {
  324. // Use File System Access API to prompt user to save text content to a file
  325. const handle = await window.showSaveFilePicker({
  326. types: [{
  327. description: 'Text Files',
  328. accept: {
  329. 'text/plain': ['.txt']
  330. }
  331. }]
  332. });
  333. const writable = await handle.createWritable();
  334. await writable.write(textContent);
  335. await writable.close();
  336. console.log("Text content saved."); // Debugging: Log text saving success
  337. } catch (error) {
  338. console.error("Error saving text content:", error); // Log error if saving fails
  339. }
  340. } else {
  341. console.log("webSocketMessagesDiv not found."); // Debugging: Log if webSocketMessagesDiv is not found
  342. }
  343. }
  344.  
  345. // Function to create Save Text button
  346. function createSaveTextButton() {
  347. const saveTextButton = document.createElement("button");
  348. saveTextButton.id = "saveTextButton";
  349. saveTextButton.textContent = "Save Text";
  350. saveTextButton.style.position = "fixed"; // Position fixed
  351. saveTextButton.style.bottom = "10px"; // Adjust bottom position
  352. saveTextButton.style.left = "10px"; // Adjust left position
  353. saveTextButton.style.background = "black";
  354. saveTextButton.style.color = "lime";
  355. saveTextButton.style.border = "none";
  356. saveTextButton.style.padding = "5px 10px";
  357. saveTextButton.style.cursor = "pointer";
  358. saveTextButton.type = "button"; // Specify that it's a button and not submit
  359. saveTextButton.addEventListener("click", saveText);
  360. document.body.appendChild(saveTextButton); // Append to document body
  361. }
  362.  
  363. // Function to remove Save Text button
  364. function removeSaveTextButton() {
  365. const saveTextButton = document.getElementById("saveTextButton");
  366. if (saveTextButton) {
  367. saveTextButton.remove();
  368. }
  369. }
  370.  
  371. // Call the function to remove the Save Text button initially
  372. removeSaveTextButton();
  373.  
  374. // Function to disable room.js functionality
  375. function disableRoomJS() {
  376. // Remove the event listener for message reception
  377. window.removeEventListener('messageReceived', handleMessageReceived);
  378. }
  379.  
  380. // Example function that handles incoming messages in room.js
  381. function handleMessageReceived(event) {
  382. // Logic to process incoming messages
  383. }
  384.  
  385. // Modify the handleKeyPress function to handle button clicks as well
  386. function handleKeyPress(event) {
  387. if ((event.key === 'Enter' || event.code === 'Enter') && !event.shiftKey) {
  388. event.preventDefault(); // Prevent the default behavior (creating a new line)
  389. // Call your message sending function here
  390. sendMessage();
  391. // Reset the input box's content
  392. resetInputBox();
  393. }
  394. }
  395.  
  396. // Function to insert predefined text and simulate Enter key press
  397. function insertPredefinedTextAndPressEnter() {
  398. // Insert predefined text
  399. const textArea = document.getElementById("textarea");
  400. textArea.value += "(╭☞ ͡ ͡°͜ ʖ ͡ ͡ )╭☞";
  401.  
  402. // Simulate Enter key press
  403. const event = new KeyboardEvent('keypress', {
  404. key: 'Enter',
  405. code: 'Enter',
  406. keyCode: 13,
  407. which: 13,
  408. bubbles: true
  409. });
  410. textArea.dispatchEvent(event);
  411. }
  412.  
  413. // Create a button to insert predefined text and press Enter
  414. function createInsertTextButton() {
  415. const insertTextButton = document.createElement("button");
  416. insertTextButton.textContent = "☞";
  417. insertTextButton.style.background = "black";
  418. insertTextButton.style.color = "lime";
  419. insertTextButton.style.border = "none";
  420. insertTextButton.style.padding = "5px 10px";
  421. insertTextButton.style.cursor = "pointer";
  422. insertTextButton.type = "button"; // Specify that it's a button and not submit
  423. insertTextButton.addEventListener("click", insertPredefinedTextAndPressEnter);
  424. const textArea = document.getElementById("textarea");
  425. textArea.parentElement.appendChild(insertTextButton);
  426. }
  427.  
  428. // Call the function to create the insert text button
  429. createInsertTextButton();
  430.  
  431. // Function to reset the input box's content
  432. function resetInputBox() {
  433. const textArea = document.getElementById("textarea");
  434. if (textArea) {
  435. textArea.value = ""; // Clear the textarea
  436. }
  437. }
  438.  
  439. function createPopup() {
  440. const popup = document.createElement("div");
  441. popup.id = "messagePopup";
  442. popup.style.position = "fixed";
  443. popup.style.top = "50%";
  444. popup.style.left = "50%";
  445. popup.style.transform = "translate(-50%, -50%)";
  446. popup.style.background = "#fff";
  447. popup.style.padding = "20px";
  448. popup.style.border = "1px solid #ccc";
  449. popup.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.1)";
  450. popup.style.zIndex = "9999";
  451.  
  452. const textarea = document.createElement("textarea");
  453. textarea.id = "popupTextarea";
  454. textarea.placeholder = "Type a message";
  455. textarea.maxLength = "500";
  456. textarea.style.width = "100%";
  457. textarea.style.marginBottom = "10px";
  458. popup.appendChild(textarea);
  459.  
  460. const sendButton = document.createElement("button");
  461. sendButton.textContent = "Send";
  462. sendButton.style.background = "black";
  463. sendButton.style.color = "lime";
  464. sendButton.style.border = "none";
  465. sendButton.style.padding = "5px 10px";
  466. sendButton.style.cursor = "pointer";
  467. sendButton.addEventListener("click", sendMessage);
  468. popup.appendChild(sendButton);
  469.  
  470. document.body.appendChild(popup);
  471. }
  472.  
  473. function openPopup() {
  474. const popup = document.getElementById("messagePopup");
  475. if (popup) {
  476. popup.style.display = "block";
  477. } else {
  478. createPopup();
  479. }
  480. }
  481.  
  482. function closePopup() {
  483. const popup = document.getElementById("messagePopup");
  484. if (popup) {
  485. popup.style.display = "none";
  486. }
  487. }
  488.  
  489. function sendMessage() {
  490. const textArea = document.getElementById("popupTextarea");
  491. if (textArea) {
  492. const message = textArea.value.trim();
  493. if (message !== "") {
  494. // Modify your logic here to match the behavior of their Message.send function
  495. // For example, if you're directly sending to the WebSocket:
  496. StumbleChat.WebSocket.send(JSON.stringify({
  497. "stumble": "msg",
  498. "text": message
  499. }));
  500. // Clear the textarea after sending the message
  501. textArea.value = "";
  502. closePopup();
  503. }
  504. }
  505. }
  506.  
  507. function clrall() {
  508. const chatContent = document.getElementById("chat-content");
  509. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  510. if (chatContent && webSocketMessagesDiv) {
  511. // Clear all child elements of chatContent
  512. chatContent.innerHTML = "";
  513. // Move webSocketMessagesDiv to the bottom
  514. chatContent.appendChild(webSocketMessagesDiv);
  515. // Adjust height of webSocketMessagesDiv
  516. webSocketMessagesDiv.style.height = "75%";
  517. }
  518. }
  519.  
  520. // Function to toggle compact view
  521. function toggleCompactView() {
  522. const messages = document.querySelectorAll('.message .content');
  523. messages.forEach(message => {
  524. message.classList.toggle('compact');
  525. });
  526. }
  527.  
  528. // Function to create and populate handle-username dropdown menu
  529. function createHandleUsernameDropdown() {
  530. const handleUsernameDropdown = document.createElement("select");
  531. handleUsernameDropdown.id = "handleUsernameDropdown";
  532. handleUsernameDropdown.style.margin = "0 5px";
  533. handleUsernameDropdown.innerHTML = '<option value="" disabled selected>who</option>';
  534. for (const handle in handleUserMap) {
  535. const option = document.createElement("option");
  536. option.value = handle;
  537. option.textContent = handleUserMap[handle];
  538. handleUsernameDropdown.appendChild(option);
  539. }
  540. return handleUsernameDropdown;
  541. }
  542.  
  543. // Create top buttons
  544. function createTopButtons() {
  545. const topButtonsDiv = document.createElement("div");
  546. topButtonsDiv.id = "topButtons";
  547. topButtonsDiv.style.position = "fixed";
  548. topButtonsDiv.style.top = "10px";
  549. topButtonsDiv.style.left = "50%";
  550. topButtonsDiv.style.transform = "translateX(-50%)";
  551. topButtonsDiv.style.zIndex = "9999";
  552.  
  553. // Clear WebSocket messages button
  554. const clrButton = document.createElement("button");
  555. clrButton.textContent = "clr";
  556. clrButton.style.background = "black";
  557. clrButton.style.color = "lime";
  558. clrButton.addEventListener("click", clr);
  559. topButtonsDiv.appendChild(clrButton);
  560.  
  561. // Clear WebSocket messages button
  562. const clrallButton = document.createElement("button");
  563. clrallButton.textContent = "clrall";
  564. clrallButton.style.background = "black";
  565. clrallButton.style.color = "lime";
  566. clrallButton.addEventListener("click", clr);
  567. topButtonsDiv.appendChild(clrallButton);
  568.  
  569. // Delete chat and switch to IRC only mode
  570. const IrcModeButton = document.createElement("button");
  571. IrcModeButton.textContent = "irc";
  572. IrcModeButton.style.background = "black";
  573. IrcModeButton.style.color = "lime";
  574. IrcModeButton.addEventListener("click", IrcMode);
  575. topButtonsDiv.appendChild(IrcModeButton);
  576.  
  577. // Dropdown menu for handle-username mapping
  578. const handleUsernameDropdown = createHandleUsernameDropdown();
  579. topButtonsDiv.appendChild(handleUsernameDropdown);
  580.  
  581. // Color picker button
  582. const colorPickerButton = document.createElement("button");
  583. colorPickerButton.textContent = "Color";
  584. colorPickerButton.style.background = "black";
  585. colorPickerButton.style.color = "lime";
  586. colorPickerButton.style.margin = "0 5px";
  587. colorPickerButton.addEventListener("click", () => {
  588. openColorPickerPopup();
  589. });
  590. topButtonsDiv.appendChild(colorPickerButton);
  591.  
  592. // Font size dropdown
  593. const fontSizeDropdown = document.createElement("select");
  594. fontSizeDropdown.id = "fontSizeDropdown";
  595. fontSizeDropdown.style.margin = "0 5px";
  596. for (let i = 1; i <= 20; i++) {
  597. const option = document.createElement("option");
  598. option.value = i;
  599. option.textContent = i;
  600. fontSizeDropdown.appendChild(option);
  601. }
  602. fontSizeDropdown.addEventListener("change", () => {
  603. const selectedFontSize = fontSizeDropdown.value;
  604. applyFontSize(selectedFontSize);
  605. });
  606. topButtonsDiv.appendChild(fontSizeDropdown);
  607.  
  608. // Append top buttons div to document body
  609. document.body.appendChild(topButtonsDiv);
  610. }
  611.  
  612. // Function to apply font size to WebSocket messages
  613. function applyFontSize(fontSize) {
  614. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  615. if (webSocketMessagesDiv) {
  616. webSocketMessagesDiv.style.fontSize = `${fontSize}px`;
  617. }
  618. }
  619.  
  620. // Call the function to create top buttons
  621. createTopButtons();
  622.  
  623. // Function to open color picker popup
  624. function openColorPickerPopup() {
  625. const popup = document.createElement("div");
  626. popup.id = "colorPickerPopup";
  627. popup.style.position = "fixed";
  628. popup.style.top = "50%";
  629. popup.style.left = "50%";
  630. popup.style.transform = "translate(-50%, -50%)";
  631. popup.style.background = "#1f1f1f";
  632. popup.style.padding = "20px";
  633. popup.style.border = "2px solid #ffffff";
  634. popup.style.zIndex = "99999";
  635.  
  636. const backgroundLabel = document.createElement("label");
  637. backgroundLabel.textContent = "Background Color:";
  638. backgroundLabel.style.color = "#ffffff";
  639. popup.appendChild(backgroundLabel);
  640.  
  641. const backgroundColorInput = document.createElement("input");
  642. backgroundColorInput.type = "color";
  643. backgroundColorInput.id = "backgroundColorInput";
  644. backgroundColorInput.style.marginRight = "10px";
  645. backgroundColorInput.value = "#000000";
  646. popup.appendChild(backgroundColorInput);
  647.  
  648. const fontColorLabel = document.createElement("label");
  649. fontColorLabel.textContent = "Font Color:";
  650. fontColorLabel.style.color = "#ffffff";
  651. popup.appendChild(fontColorLabel);
  652.  
  653. const fontColorInput = document.createElement("input");
  654. fontColorInput.type = "color";
  655. fontColorInput.id = "fontColorInput";
  656. fontColorInput.style.marginRight = "10px";
  657. fontColorInput.value = "#ffffff";
  658. popup.appendChild(fontColorInput);
  659.  
  660. const applyButton = document.createElement("button");
  661. applyButton.textContent = "Apply";
  662. applyButton.style.background = "black";
  663. applyButton.style.color = "lime";
  664. applyButton.style.marginTop = "10px";
  665. applyButton.addEventListener("click", () => {
  666. applyColors(backgroundColorInput.value, fontColorInput.value);
  667. popup.remove();
  668. });
  669. popup.appendChild(applyButton);
  670.  
  671. const closeButton = document.createElement("button");
  672. closeButton.textContent = "Close";
  673. closeButton.style.background = "black";
  674. closeButton.style.color = "lime";
  675. closeButton.style.marginTop = "10px";
  676. closeButton.style.marginLeft = "10px";
  677. closeButton.addEventListener("click", () => {
  678. popup.remove();
  679. });
  680. popup.appendChild(closeButton);
  681.  
  682. document.body.appendChild(popup);
  683. }
  684.  
  685. // Function to apply selected colors to WebSocket log
  686. function applyColors(backgroundColor, fontColor) {
  687. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  688. if (webSocketMessagesDiv) {
  689. webSocketMessagesDiv.style.backgroundColor = backgroundColor;
  690. webSocketMessagesDiv.style.color = fontColor;
  691. }
  692. }
  693.  
  694. /* Additional compacting styles */
  695. /*@-moz-document url-prefix("https://stumblechat.com/room/") {*/
  696. // Compact message styles
  697. const compactStyles = `
  698. .message .nickname ~ .content {
  699. display: inline-block;
  700. top: -7px;
  701. position: relative;
  702. margin-left: 2px;
  703. margin-right: 1em;
  704. }
  705. .content + .content {
  706. display: inline-block!important;
  707. margin-right: 1em;
  708. }
  709. .message .nickname ~ .content span {
  710. line-height: 1.5em;
  711. }
  712. `;
  713.  
  714. // Apply compact styles to the document
  715. const style = document.createElement('style');
  716. style.textContent = compactStyles;
  717. document.head.appendChild(style);
  718. /*}*/
  719.  
  720. function createGenericButtons() {
  721. // Define button configurations
  722. const buttonConfigurations = [
  723. { name: 'c', text: 'Compact', clickHandler: toggleCompactView },
  724. { name: 's', text: 'Save', clickHandler: () => {
  725. // Functionality to save handle-username map to memory or file
  726. console.log("Save button clicked");
  727. }}
  728. // Add more button configurations as needed
  729. ];
  730.  
  731. // Get the container for the buttons
  732. const container = document.getElementById('topButtons');
  733.  
  734. // Loop through each button configuration and generate a button
  735. buttonConfigurations.forEach(config => {
  736. // Create a button element
  737. const button = document.createElement('button');
  738. button.textContent = config.text; // Use button text as text content
  739. button.style.background = "black";
  740. button.style.color = "lime";
  741. button.style.width = "50px"; // Set button width
  742. button.style.height = "20px"; // Set button height
  743. button.style.margin = "0 5px"; // Set button margin
  744.  
  745. // Add event listener based on configuration
  746. button.addEventListener('click', config.clickHandler);
  747.  
  748. // Append the button to the container in the DOM
  749. container.appendChild(button);
  750. });
  751. }
  752.  
  753. // Call the function to create generic buttons
  754. createGenericButtons();
  755.  
  756.  
  757.  
  758.  
  759. })();
  760.  
  761.  
  762.  
  763.  
  764.  
  765.  
  766.  
  767.  
  768.