Yeopardy for Stumblechat

The competetive game of knowledge

  1. // ==UserScript==
  2. // @name Yeopardy for Stumblechat
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.11
  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 parseWebSocketMessage(message) {
  20. const parsedMessage = JSON.parse(message);
  21. if (parsedMessage.stumble === "joined") {
  22. const userList = parsedMessage.userlist;
  23. if (userList && userList.length > 0) {
  24. userList.forEach(user => {
  25. const username = user.username || user.nick;
  26. handleUserMap[user.handle] = username;
  27. addUserToUserList({ handle: user.handle, username });
  28. });
  29. }
  30. const joinMessage = "A user or users joined the chat."; // Example join message
  31. displayWebSocketMessage(joinMessage);
  32. } else if (parsedMessage.stumble === "join") {
  33. const { handle, username } = parsedMessage;
  34. handleUserMap[handle] = username;
  35. addUserToUserList({ handle, username });
  36. const joinMessage = `${username} joined the chat.`;
  37. displayWebSocketMessage(joinMessage);
  38. } else if (parsedMessage.stumble === "quit") {
  39. const handle = parsedMessage.handle;
  40. const username = handleUserMap[handle];
  41. if (username) {
  42. delete handleUserMap[handle];
  43. removeUserFromUserList(handle);
  44. const quitMessage = `${username} left the chat.`;
  45. displayWebSocketMessage(quitMessage);
  46. }
  47. } else if (parsedMessage.stumble === "msg" && (parsedMessage.text === "#join" || parsedMessage.text === "#icon")) {
  48. // Handle additional changes to the user list for specific "msg" messages
  49. // This block will only execute for "msg" messages that contain "#join" or "#icon"
  50. // Adjust the logic as needed based on your specific requirements
  51. }
  52. }
  53.  
  54. // WebSocket Listener: Override WebSocket constructor to intercept WebSocket creation
  55. const originalWebSocket = window.WebSocket;
  56. window.WebSocket = function(url, protocols) {
  57. console.log('WebSocket URL:', url);
  58.  
  59. // Call original WebSocket constructor
  60. const ws = new originalWebSocket(url, protocols);
  61.  
  62. // Event listener for receiving messages
  63. ws.addEventListener('message', event => {
  64. const parsedMessage = JSON.parse(event.data);
  65. // Check if the message is a "joined" message
  66. if (parsedMessage.stumble === "joined") {
  67. // Extracting our own handle from the "self" object in the message
  68. const selfHandle = parsedMessage.self.handle;
  69. // Update handleUserMap and custom user list when a user joins
  70. const userList = parsedMessage.userlist;
  71. if (userList && userList.length > 0) {
  72. userList.forEach(user => {
  73. // Check if the user being added is ourselves
  74. const isSelf = user.handle === selfHandle;
  75. // If it's our own user, update handleUserMap and display our own handle in the user list
  76. if (isSelf) {
  77. // Update handleUserMap with our own handle and username
  78. handleUserMap[user.handle] = user.username || user.nick;
  79. // Add our own handle to the custom user list with purple icon
  80. addUserToUserList({
  81. username: user.username,
  82. handle: user.handle,
  83. active: true, // We just joined, so we're considered active
  84. icon: "🟣" // Purple icon for our own user entry
  85. }, "self");
  86. } else {
  87. // If it's not our own user, proceed as usual and add them to the user list
  88. updateUserListAndMapOnJoin(user);
  89. }
  90. });
  91. }
  92. } else if (parsedMessage.stumble === "join") {
  93. // Handle join messages
  94. const { handle, username } = parsedMessage;
  95. // Check if the user being added is not ourselves
  96. if (handle !== yourHandle) {
  97. handleUserMap[handle] = username;
  98. addUserToUserList({ handle, username }, "join");
  99. }
  100. } else if (parsedMessage.stumble === "quit") {
  101. // Handle quit messages
  102. const handle = parsedMessage.handle;
  103. const username = handleUserMap[handle];
  104. if (username) {
  105. delete handleUserMap[handle];
  106. removeUserFromUserList(handle);
  107. setTimeout(() => {
  108. removeUserFromUserList(handle);
  109. }, 30000); // 30 seconds delay
  110. addUserToUserList({ handle, username }, "quit");
  111. }
  112. } else if (parsedMessage.stumble === "msg") {
  113. // Handle message messages
  114. const { handle, text } = parsedMessage;
  115. const username = handleUserMap[handle] || handle;
  116. displayWebSocketMessage(event.data);
  117. }
  118. });
  119.  
  120. return ws;
  121. };
  122.  
  123.  
  124. // Function to create user list div
  125. function createUserListDiv() {
  126. const userListDiv = document.createElement("div");
  127. userListDiv.id = "userList";
  128. userListDiv.style.position = "absolute"; // Change to absolute positioning
  129. userListDiv.style.top = "100px"; // Adjust top position as needed
  130. userListDiv.style.left = "10px"; // Adjust left position as needed
  131. userListDiv.style.height = "calc(100% - 100px)"; // Adjust height to fill remaining space
  132. userListDiv.style.overflowY = "auto";
  133. userListDiv.style.color = "#ffffff";
  134. userListDiv.style.padding = "10px";
  135. userListDiv.style.zIndex = "2"; // Set a higher z-index value
  136. userListDiv.style.display = "none"; // Hide the custom user list by default
  137. return userListDiv;
  138. }
  139.  
  140. // Function to display WebSocket messages and update user list
  141. function displayWebSocketMessage(message) {
  142. const parsedMessage = JSON.parse(message);
  143. if (parsedMessage.stumble === "join") {
  144. // Handle join messages: Extract handle and username from the message
  145. const { handle, username } = parsedMessage;
  146. // Map handle to username in handleUserMap
  147. handleUserMap[handle] = username;
  148. // Add the user to the custom user list with the appropriate icon (join user)
  149. addUserToUserList({ handle, username }, "join");
  150. } else if (parsedMessage.stumble === "msg") {
  151. // Handle message messages: Extract handle and text from the message
  152. const { handle, text } = parsedMessage;
  153. // Retrieve username from handleUserMap or use handle if not found
  154. const username = handleUserMap[handle] || handle;
  155. // Display the message in the WebSocket messages div
  156. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  157. if (webSocketMessagesDiv) {
  158. // Append the message with a newline character
  159. webSocketMessagesDiv.textContent += `${username}: ${text}\n`;
  160. // Scroll to the bottom of the messages div
  161. webSocketMessagesDiv.scrollTop = webSocketMessagesDiv.scrollHeight;
  162. }
  163.  
  164. // Additional logic for handling commands
  165. if (text === "#join") {
  166. console.print("join");
  167. // Add your logic here
  168. } else if (text === "#icon") {
  169. console.print("icon");
  170. // Add your logic here
  171. }
  172. } else if (parsedMessage.stumble === "joined") {
  173. // Handle joined messages: Add users to handleUserMap and custom user list
  174. const userList = parsedMessage.userlist;
  175. if (userList && userList.length > 0) {
  176. userList.forEach(user => {
  177. // Extract username from either "username" or "nick"
  178. const username = user.username || user.nick;
  179. // Map handle to username in handleUserMap
  180. handleUserMap[user.handle] = username;
  181. // Add the user to the custom user list with the appropriate icon
  182. addUserToUserList({ handle: user.handle, username }, "joined");
  183. });
  184. }
  185. } else if (parsedMessage.stumble === "quit") {
  186. // Handle quit messages: Remove users from handleUserMap and custom user list
  187. const handle = parsedMessage.handle;
  188. const username = handleUserMap[handle];
  189. if (username) {
  190. // Remove the handle from handleUserMap
  191. delete handleUserMap[handle];
  192. // Remove the user from the custom user list
  193. removeUserFromUserList(handle);
  194. }
  195. }
  196. }
  197.  
  198.  
  199. // Function to add user to user list with appropriate icon based on user type
  200. function addUserToUserList(user, userType) {
  201. const userList = document.getElementById("userList");
  202. if (!userList) return;
  203.  
  204. const userItem = document.createElement("div");
  205. userItem.textContent = `${user.username}`;
  206.  
  207. // Define the default dot color and icon
  208. let dotColor = "red"; // Default dot color
  209. let icon = "🔴"; // Default icon for inactive users
  210.  
  211. // Set dot color and icon based on user type
  212. if (userType === "self") {
  213. dotColor = "purple"; // Purple for self user
  214. icon = "🟣"; // Purple circle icon
  215. } else if (userType === "join") {
  216. dotColor = "blue"; // Blue for join user
  217. icon = "🔵"; // Blue circle icon
  218. } else if (userType === "joined") { // "self" user type listener for user list"
  219. dotColor = "green"; // Green for joined user
  220. icon = "🟢"; // Green circle icon
  221. }
  222.  
  223. // Add colored dot based on user status
  224. const dot = document.createElement("span");
  225. dot.textContent = icon;
  226. dot.style.color = dotColor;
  227. userItem.appendChild(dot);
  228.  
  229. // Add custom icon for the user
  230. if (user.icon) {
  231. const customIcon = document.createElement("span");
  232. customIcon.textContent = user.icon;
  233. customIcon.style.marginLeft = "5px"; // Adjust margin as needed
  234. userItem.appendChild(customIcon);
  235. }
  236.  
  237. userList.appendChild(userItem); // Append user item to the user list
  238.  
  239. // If user is not active and not yourself, show popup for 5 seconds
  240. if (!user.active && user.handle !== yourHandle) {
  241. const popup = document.createElement("div");
  242. popup.textContent = "WELCOME TO STUMBLECHAT YEOPARDY!";
  243. popup.style.fontSize = "48px";
  244. popup.style.position = "fixed";
  245. popup.style.top = "50%";
  246. popup.style.left = "50%";
  247. popup.style.transform = "translate(-50%, -50%)";
  248. popup.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
  249. popup.style.color = "white";
  250. popup.style.padding = "10px";
  251. popup.style.borderRadius = "5px";
  252. document.body.appendChild(popup);
  253.  
  254. // Create SVG animation
  255. const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  256. svg.setAttribute("width", "200");
  257. svg.setAttribute("height", "100");
  258. svg.style.position = "fixed";
  259. svg.style.top = "50%";
  260. svg.style.left = "50%";
  261. svg.style.transform = "translate(-50%, -50%)";
  262.  
  263. // Create rectangle inside SVG
  264. const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  265. rect.setAttribute("width", "100");
  266. rect.setAttribute("height", "50");
  267. rect.setAttribute("fill", "blue");
  268. rect.setAttribute("id", "movingRect"); // ID to target for animation
  269. svg.appendChild(rect);
  270.  
  271. // Append SVG to body
  272. document.body.appendChild(svg);
  273.  
  274. // Animate SVG
  275. animateRect();
  276.  
  277. setTimeout(() => {
  278. popup.remove();
  279. svg.remove(); // Remove the SVG element along with the popup
  280. }, 5000); // 5 seconds delay
  281. }
  282.  
  283. }
  284.  
  285.  
  286. // Function to update handleUserMap and add users to custom user list
  287. function updateUserListAndMapOnJoin(user) {
  288. // Update handleUserMap with the new user
  289. handleUserMap[user.handle] = user.username || user.nick; // Derive username from handle or nick
  290. // Add the new user to the custom user list
  291. addUserToUserList(user);
  292. }
  293.  
  294. // Call the function to update the user list
  295. function updateUserListOnMessage(userList) {
  296. return function(message) {
  297. const parsedMessage = JSON.parse(message);
  298. if (parsedMessage.stumble === "join" || parsedMessage.stumble === "msg" || parsedMessage.stumble === "joined") {
  299. // Add user to user list
  300. addUserToUserList(parsedMessage);
  301. }
  302. };
  303. }
  304.  
  305. // Function to create WebSocket messages div
  306. function createWebSocketMessagesDiv() {
  307. const div = document.createElement("div");
  308. div.id = "webSocketMessages";
  309. div.style.position = "relative";
  310. div.style.height = "25%";
  311. div.style.paddingLeft = "2px";
  312. div.style.visibility = "visible"; // Ensure the div is visible
  313. div.style.willChange = "transform";
  314. div.style.boxSizing = "border-box";
  315. div.style.overflowX = "hidden";
  316. div.style.overflowY = "auto";
  317. div.style.color = "#ffffff"; // Set font color to white
  318. div.style.padding = "10px"; // Example padding
  319. div.style.zIndex = "2"; // Set a higher z-index value for the WebSocket messages div
  320.  
  321. // Additional styles for specific scenarios
  322. div.style.display = "flex";
  323. div.style.flexDirection = "column";
  324. div.style.justifyContent = "flex-end";
  325. div.style.fontSize = "18px";
  326.  
  327. div.style.whiteSpace = "pre-wrap"; // Allow text to wrap within the container
  328. div.style.wordWrap = "break-word"; // Allow long words to break and wrap
  329.  
  330. // Locate the chat-position div
  331. const chatPositionDiv = document.getElementById("chat-position");
  332. if (chatPositionDiv) {
  333. // Append custom div to the chat-position div
  334. chatPositionDiv.appendChild(div);
  335. } else {
  336. // If chat-position div not found, append to document body as fallback
  337. document.body.appendChild(div);
  338. }
  339. }
  340.  
  341. // Call the function to create the user list div
  342. const userListDiv = createUserListDiv();
  343. const chatContainer = document.getElementById("chat-container");
  344. if (chatContainer) {
  345. chatContainer.appendChild(userListDiv); // Append to chat container instead
  346. } else {
  347. document.body.appendChild(userListDiv);
  348. }
  349.  
  350. // Call the function to create the WebSocket messages div
  351. createWebSocketMessagesDiv();
  352.  
  353. // Function to remove user from custom user list
  354. function removeUserFromUserList(handle) {
  355. const userList = document.getElementById("userList");
  356. if (userList) {
  357. const userElements = userList.querySelectorAll("div");
  358. userElements.forEach(userElement => {
  359. if (userElement.textContent.includes(`(${handle})`)) {
  360. userElement.remove();
  361. }
  362. });
  363. }
  364. }
  365.  
  366. // Handle-username mapping
  367. let handleUserMap = {};
  368.  
  369. // Function to toggle visibility of custom user list
  370. function toggleCustomUserList() {
  371. const userListDiv = document.getElementById("userList");
  372. if (userListDiv) {
  373. userListDiv.style.display = userListDiv.style.display === "none" ? "block" : "none";
  374. }
  375. }
  376.  
  377. // Add a button to toggle visibility of custom user list
  378. const toggleButton = document.createElement("button");
  379. toggleButton.textContent = "U";
  380. toggleButton.style.position = "fixed";
  381. toggleButton.style.top = "10px";
  382. toggleButton.style.left = "10px";
  383. toggleButton.addEventListener("click", toggleCustomUserList);
  384. document.body.appendChild(toggleButton);
  385.  
  386. // Function to clear messages
  387. function clr() {
  388. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  389. if (webSocketMessagesDiv) {
  390. webSocketMessagesDiv.innerHTML = "";
  391. }
  392. }
  393.  
  394. // Function to create fadeaway popup text with "WELCOME" message
  395. function showWelcomePopup() {
  396. // Create popup text
  397. const popup = document.createElement("div");
  398. popup.textContent = "WELCOME";
  399. popup.classList.add("fadeaway-popup");
  400. document.body.appendChild(popup);
  401.  
  402. // Create SVG animation
  403. const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  404. svg.setAttribute("width", "200");
  405. svg.setAttribute("height", "100");
  406. svg.style.position = "fixed";
  407. svg.style.top = "50%";
  408. svg.style.left = "50%";
  409. svg.style.transform = "translate(-50%, -50%)";
  410.  
  411. // Create rectangle inside SVG
  412. const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  413. rect.setAttribute("width", "100");
  414. rect.setAttribute("height", "50");
  415. rect.setAttribute("fill", "blue");
  416. rect.setAttribute("id", "movingRect"); // ID to target for animation
  417. svg.appendChild(rect);
  418.  
  419. // Append SVG to body
  420. document.body.appendChild(svg);
  421.  
  422. // Animate SVG
  423. animateRect();
  424. }
  425.  
  426. // Function to animate the rectangle
  427. function animateRect() {
  428. const rect = document.getElementById("movingRect");
  429. if (!rect) return;
  430.  
  431. rect.setAttribute("x", "-100");
  432.  
  433. // Animation using CSS transitions
  434. rect.style.transition = "transform 5s linear";
  435. rect.style.transform = "translateX(calc(100% + 100px))";
  436.  
  437. // Remove the popup and SVG after 5 seconds
  438. setTimeout(() => {
  439. rect.parentNode.remove(); // Remove SVG
  440. document.querySelector(".fadeaway-popup").remove(); // Remove popup
  441. }, 5000); // 5 seconds delay
  442. }
  443.  
  444. // Call the function to show the welcome popup with SVG animation
  445. showWelcomePopup();
  446.  
  447. // Function to create fadeaway popup text with "WELCOME" message
  448. function showWelcomePopupText() {
  449. const popup = document.createElement("div");
  450. popup.textContent = "WELCOME";
  451. popup.classList.add("fadeaway-popup");
  452. document.body.appendChild(popup);
  453.  
  454. // Remove the popup after 3 seconds
  455. setTimeout(() => {
  456. popup.remove();
  457. }, 3000); // 3 seconds delay
  458. }
  459.  
  460. // Function to create SVG animation
  461. function createSVGAnimation() {
  462. // Create SVG element
  463. const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  464. svg.setAttribute("width", "100");
  465. svg.setAttribute("height", "100");
  466.  
  467. // Create rectangle inside SVG
  468. const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  469. rect.setAttribute("width", "100");
  470. rect.setAttribute("height", "100");
  471. rect.setAttribute("fill", "blue");
  472. svg.appendChild(rect);
  473.  
  474. // Append SVG to body
  475. document.body.appendChild(svg);
  476.  
  477. // Animate SVG
  478. const animation = document.createElementNS("http://www.w3.org/2000/svg", "animate");
  479. animation.setAttribute("attributeName", "x");
  480. animation.setAttribute("from", "-100");
  481. animation.setAttribute("to", "100%");
  482. animation.setAttribute("dur", "5s");
  483. animation.setAttribute("repeatCount", "indefinite");
  484. rect.appendChild(animation);
  485. }
  486.  
  487. // Call the function to create SVG animation
  488. createSVGAnimation();
  489.  
  490. function IrcMode() {
  491. const chatContent = document.getElementById("chat-content");
  492. if (chatContent) {
  493. // Remove the chat-content div from the DOM
  494. chatContent.remove();
  495. // Move the webSocketMessagesDiv and the form input to fixed position
  496. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  497. const formInput = document.getElementById("input");
  498. if (webSocketMessagesDiv && formInput) {
  499. webSocketMessagesDiv.style.position = "fixed";
  500. webSocketMessagesDiv.style.top = "0px";
  501. webSocketMessagesDiv.style.height = "90%"; // Adjust height to 90%
  502. webSocketMessagesDiv.style.overflowY = "auto"; // Add scrollbar
  503. formInput.style.position = "fixed";
  504. formInput.style.bottom = "0px";
  505. }
  506. // Create Save Text button
  507. createSaveTextButton();
  508. }
  509. // Disable the room.js functionality
  510. disableRoomJS();
  511. }
  512.  
  513. // Function to save text content without <br> elements
  514. async function saveText() {
  515. console.log("Save Text button clicked."); // Debugging: Log button click
  516. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  517. if (webSocketMessagesDiv) {
  518. console.log("webSocketMessagesDiv found:", webSocketMessagesDiv); // Debugging: Log webSocketMessagesDiv
  519. const textContent = webSocketMessagesDiv.textContent.replaceAll('\n', '\r\n');
  520. console.log("Text content:", textContent); // Debugging: Log extracted text content
  521. try {
  522. // Use File System Access API to prompt user to save text content to a file
  523. const handle = await window.showSaveFilePicker({
  524. types: [{
  525. description: 'Text Files',
  526. accept: {
  527. 'text/plain': ['.txt']
  528. }
  529. }]
  530. });
  531. const writable = await handle.createWritable();
  532. await writable.write(textContent);
  533. await writable.close();
  534. console.log("Text content saved."); // Debugging: Log text saving success
  535. } catch (error) {
  536. console.error("Error saving text content:", error); // Log error if saving fails
  537. }
  538. } else {
  539. console.log("webSocketMessagesDiv not found."); // Debugging: Log if webSocketMessagesDiv is not found
  540. }
  541. }
  542.  
  543. // Function to create Save Text button
  544. function createSaveTextButton() {
  545. const saveTextButton = document.createElement("button");
  546. saveTextButton.id = "saveTextButton";
  547. saveTextButton.textContent = "Save Text";
  548. saveTextButton.style.position = "fixed"; // Position fixed
  549. saveTextButton.style.bottom = "10px"; // Adjust bottom position
  550. saveTextButton.style.left = "10px"; // Adjust left position
  551. saveTextButton.style.background = "black";
  552. saveTextButton.style.color = "lime";
  553. saveTextButton.style.border = "none";
  554. saveTextButton.style.padding = "5px 10px";
  555. saveTextButton.style.cursor = "pointer";
  556. saveTextButton.type = "button"; // Specify that it's a button and not submit
  557. saveTextButton.addEventListener("click", saveText);
  558. document.body.appendChild(saveTextButton); // Append to document body
  559. }
  560.  
  561. // Function to remove Save Text button
  562. function removeSaveTextButton() {
  563. const saveTextButton = document.getElementById("saveTextButton");
  564. if (saveTextButton) {
  565. saveTextButton.remove();
  566. }
  567. }
  568.  
  569. // Call the function to remove the Save Text button initially
  570. removeSaveTextButton();
  571.  
  572. // Function to disable room.js functionality
  573. function disableRoomJS() {
  574. // Remove the event listener for message reception
  575. window.removeEventListener('messageReceived', handleMessageReceived);
  576. }
  577.  
  578. // Example function that handles incoming messages in room.js
  579. function handleMessageReceived(event) {
  580. // Logic to process incoming messages
  581. }
  582.  
  583. // Modify the handleKeyPress function to handle button clicks as well
  584. function handleKeyPress(event) {
  585. if ((event.key === 'Enter' || event.code === 'Enter') && !event.shiftKey) {
  586. event.preventDefault(); // Prevent the default behavior (creating a new line)
  587. // Call your message sending function here
  588. sendMessage();
  589. // Reset the input box's content
  590. resetInputBox();
  591. }
  592. }
  593.  
  594. // Function to insert predefined text and simulate Enter key press
  595. function insertPredefinedTextAndPressEnter() {
  596. // Insert predefined text
  597. const textArea = document.getElementById("textarea");
  598. textArea.value += "(╭☞ ͡ ͡°͜ ʖ ͡ ͡ )╭☞";
  599.  
  600. // Simulate Enter key press
  601. const event = new KeyboardEvent('keypress', {
  602. key: 'Enter',
  603. code: 'Enter',
  604. keyCode: 13,
  605. which: 13,
  606. bubbles: true
  607. });
  608. textArea.dispatchEvent(event);
  609. }
  610.  
  611. // Create a button to insert predefined text and press Enter
  612. function createInsertTextButton() {
  613. const insertTextButton = document.createElement("button");
  614. insertTextButton.textContent = "☞";
  615. insertTextButton.style.background = "black";
  616. insertTextButton.style.color = "lime";
  617. insertTextButton.style.border = "none";
  618. insertTextButton.style.padding = "5px 10px";
  619. insertTextButton.style.cursor = "pointer";
  620. insertTextButton.type = "button"; // Specify that it's a button and not submit
  621. insertTextButton.addEventListener("click", insertPredefinedTextAndPressEnter);
  622. const textArea = document.getElementById("textarea");
  623. textArea.parentElement.appendChild(insertTextButton);
  624. }
  625.  
  626. // Call the function to create the insert text button
  627. createInsertTextButton();
  628.  
  629. // Function to reset the input box's content
  630. function resetInputBox() {
  631. const textArea = document.getElementById("textarea");
  632. if (textArea) {
  633. textArea.value = ""; // Clear the textarea
  634. }
  635. }
  636.  
  637. function createPopup() {
  638. const popup = document.createElement("div");
  639. popup.id = "messagePopup";
  640. popup.style.position = "fixed";
  641. popup.style.top = "50%";
  642. popup.style.left = "50%";
  643. popup.style.transform = "translate(-50%, -50%)";
  644. popup.style.background = "#fff";
  645. popup.style.padding = "20px";
  646. popup.style.border = "1px solid #ccc";
  647. popup.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.1)";
  648. popup.style.zIndex = "9999";
  649.  
  650. const textarea = document.createElement("textarea");
  651. textarea.id = "popupTextarea";
  652. textarea.placeholder = "Type a message";
  653. textarea.maxLength = "500";
  654. textarea.style.width = "100%";
  655. textarea.style.marginBottom = "10px";
  656. popup.appendChild(textarea);
  657.  
  658. const sendButton = document.createElement("button");
  659. sendButton.textContent = "Send";
  660. sendButton.style.background = "black";
  661. sendButton.style.color = "lime";
  662. sendButton.style.border = "none";
  663. sendButton.style.padding = "5px 10px";
  664. sendButton.style.cursor = "pointer";
  665. sendButton.addEventListener("click", sendMessage);
  666. popup.appendChild(sendButton);
  667.  
  668. document.body.appendChild(popup);
  669. }
  670.  
  671. function openPopup() {
  672. const popup = document.getElementById("messagePopup");
  673. if (popup) {
  674. popup.style.display = "block";
  675. } else {
  676. createPopup();
  677. }
  678. }
  679.  
  680. function closePopup() {
  681. const popup = document.getElementById("messagePopup");
  682. if (popup) {
  683. popup.style.display = "none";
  684. }
  685. }
  686.  
  687. function sendMessage() {
  688. const textArea = document.getElementById("popupTextarea");
  689. if (textArea) {
  690. const message = textArea.value.trim();
  691. if (message !== "") {
  692. // Modify your logic here to match the behavior of their Message.send function
  693. // For example, if you're directly sending to the WebSocket:
  694. StumbleChat.WebSocket.send(JSON.stringify({
  695. "stumble": "msg",
  696. "text": message
  697. }));
  698. // Clear the textarea after sending the message
  699. textArea.value = "";
  700. closePopup();
  701. }
  702. }
  703. }
  704.  
  705. function clrall() {
  706. const chatContent = document.getElementById("chat-content");
  707. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  708. if (chatContent && webSocketMessagesDiv) {
  709. // Clear all child elements of chatContent
  710. chatContent.innerHTML = "";
  711. // Move webSocketMessagesDiv to the bottom
  712. chatContent.appendChild(webSocketMessagesDiv);
  713. // Adjust height of webSocketMessagesDiv
  714. webSocketMessagesDiv.style.height = "75%";
  715. }
  716. }
  717.  
  718. // Function to toggle compact view
  719. function toggleCompactView() {
  720. const messages = document.querySelectorAll('.message .content');
  721. messages.forEach(message => {
  722. message.classList.toggle('compact');
  723. });
  724. }
  725.  
  726. // Function to create and populate handle-username dropdown menu
  727. function createHandleUsernameDropdown() {
  728. const handleUsernameDropdown = document.createElement("select");
  729. handleUsernameDropdown.id = "handleUsernameDropdown";
  730. handleUsernameDropdown.style.margin = "0 5px";
  731. handleUsernameDropdown.innerHTML = '<option value="" disabled selected>who</option>';
  732. for (const handle in handleUserMap) {
  733. const option = document.createElement("option");
  734. option.value = handle;
  735. option.textContent = handleUserMap[handle];
  736. handleUsernameDropdown.appendChild(option);
  737. }
  738. return handleUsernameDropdown;
  739. }
  740.  
  741. // Create top buttons
  742. function createTopButtons() {
  743. const topButtonsDiv = document.createElement("div");
  744. topButtonsDiv.id = "topButtons";
  745. topButtonsDiv.style.position = "fixed";
  746. topButtonsDiv.style.top = "10px";
  747. topButtonsDiv.style.left = "50%";
  748. topButtonsDiv.style.transform = "translateX(-50%)";
  749. topButtonsDiv.style.zIndex = "9999";
  750.  
  751. // Clear WebSocket messages button
  752. const clrButton = document.createElement("button");
  753. clrButton.textContent = "clr";
  754. clrButton.style.background = "black";
  755. clrButton.style.color = "lime";
  756. clrButton.addEventListener("click", clr);
  757. topButtonsDiv.appendChild(clrButton);
  758.  
  759. // Clear WebSocket messages button
  760. const clrallButton = document.createElement("button");
  761. clrallButton.textContent = "clrall";
  762. clrallButton.style.background = "black";
  763. clrallButton.style.color = "lime";
  764. clrallButton.addEventListener("click", clr);
  765. topButtonsDiv.appendChild(clrallButton);
  766.  
  767. // Delete chat and switch to IRC only mode
  768. const IrcModeButton = document.createElement("button");
  769. IrcModeButton.textContent = "irc";
  770. IrcModeButton.style.background = "black";
  771. IrcModeButton.style.color = "lime";
  772. IrcModeButton.addEventListener("click", IrcMode);
  773. topButtonsDiv.appendChild(IrcModeButton);
  774.  
  775. // Dropdown menu for handle-username mapping
  776. const handleUsernameDropdown = createHandleUsernameDropdown();
  777. topButtonsDiv.appendChild(handleUsernameDropdown);
  778.  
  779. // Color picker button
  780. const colorPickerButton = document.createElement("button");
  781. colorPickerButton.textContent = "Color";
  782. colorPickerButton.style.background = "black";
  783. colorPickerButton.style.color = "lime";
  784. colorPickerButton.style.margin = "0 5px";
  785. colorPickerButton.addEventListener("click", () => {
  786. openColorPickerPopup();
  787. });
  788. topButtonsDiv.appendChild(colorPickerButton);
  789.  
  790. // Font size dropdown
  791. const fontSizeDropdown = document.createElement("select");
  792. fontSizeDropdown.id = "fontSizeDropdown";
  793. fontSizeDropdown.style.margin = "0 5px";
  794. for (let i = 1; i <= 20; i++) {
  795. const option = document.createElement("option");
  796. option.value = i;
  797. option.textContent = i;
  798. fontSizeDropdown.appendChild(option);
  799. }
  800. fontSizeDropdown.addEventListener("change", () => {
  801. const selectedFontSize = fontSizeDropdown.value;
  802. applyFontSize(selectedFontSize);
  803. });
  804. topButtonsDiv.appendChild(fontSizeDropdown);
  805.  
  806. // Append top buttons div to document body
  807. document.body.appendChild(topButtonsDiv);
  808. }
  809.  
  810. // Function to apply font size to WebSocket messages
  811. function applyFontSize(fontSize) {
  812. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  813. if (webSocketMessagesDiv) {
  814. webSocketMessagesDiv.style.fontSize = `${fontSize}px`;
  815. }
  816. }
  817.  
  818. // Call the function to create top buttons
  819. createTopButtons();
  820.  
  821. // Function to open color picker popup
  822. function openColorPickerPopup() {
  823. const popup = document.createElement("div");
  824. popup.id = "colorPickerPopup";
  825. popup.style.position = "fixed";
  826. popup.style.top = "50%";
  827. popup.style.left = "50%";
  828. popup.style.transform = "translate(-50%, -50%)";
  829. popup.style.background = "#1f1f1f";
  830. popup.style.padding = "20px";
  831. popup.style.border = "2px solid #ffffff";
  832. popup.style.zIndex = "99999";
  833.  
  834. const backgroundLabel = document.createElement("label");
  835. backgroundLabel.textContent = "Background Color:";
  836. backgroundLabel.style.color = "#ffffff";
  837. popup.appendChild(backgroundLabel);
  838.  
  839. const backgroundColorInput = document.createElement("input");
  840. backgroundColorInput.type = "color";
  841. backgroundColorInput.id = "backgroundColorInput";
  842. backgroundColorInput.style.marginRight = "10px";
  843. backgroundColorInput.value = "#000000";
  844. popup.appendChild(backgroundColorInput);
  845.  
  846. const fontColorLabel = document.createElement("label");
  847. fontColorLabel.textContent = "Font Color:";
  848. fontColorLabel.style.color = "#ffffff";
  849. popup.appendChild(fontColorLabel);
  850.  
  851. const fontColorInput = document.createElement("input");
  852. fontColorInput.type = "color";
  853. fontColorInput.id = "fontColorInput";
  854. fontColorInput.style.marginRight = "10px";
  855. fontColorInput.value = "#ffffff";
  856. popup.appendChild(fontColorInput);
  857.  
  858. const applyButton = document.createElement("button");
  859. applyButton.textContent = "Apply";
  860. applyButton.style.background = "black";
  861. applyButton.style.color = "lime";
  862. applyButton.style.marginTop = "10px";
  863. applyButton.addEventListener("click", () => {
  864. applyColors(backgroundColorInput.value, fontColorInput.value);
  865. popup.remove();
  866. });
  867. popup.appendChild(applyButton);
  868.  
  869. const closeButton = document.createElement("button");
  870. closeButton.textContent = "Close";
  871. closeButton.style.background = "black";
  872. closeButton.style.color = "lime";
  873. closeButton.style.marginTop = "10px";
  874. closeButton.style.marginLeft = "10px";
  875. closeButton.addEventListener("click", () => {
  876. popup.remove();
  877. });
  878. popup.appendChild(closeButton);
  879.  
  880. document.body.appendChild(popup);
  881. }
  882.  
  883. // Function to apply selected colors to WebSocket log
  884. function applyColors(backgroundColor, fontColor) {
  885. const webSocketMessagesDiv = document.getElementById("webSocketMessages");
  886. if (webSocketMessagesDiv) {
  887. webSocketMessagesDiv.style.backgroundColor = backgroundColor;
  888. webSocketMessagesDiv.style.color = fontColor;
  889. }
  890. }
  891.  
  892. /* Additional compacting styles */
  893. /*@-moz-document url-prefix("https://stumblechat.com/room/") {*/
  894. // Compact message styles
  895. const compactStyles = `
  896. .message .nickname ~ .content {
  897. display: inline-block;
  898. top: -7px;
  899. position: relative;
  900. margin-left: 2px;
  901. margin-right: 1em;
  902. }
  903. .content + .content {
  904. display: inline-block!important;
  905. margin-right: 1em;
  906. }
  907. .message .nickname ~ .content span {
  908. line-height: 1.5em;
  909. }
  910. `;
  911.  
  912. // Apply compact styles to the document
  913. const style = document.createElement('style');
  914. style.textContent = compactStyles;
  915. document.head.appendChild(style);
  916. /*}*/
  917.  
  918. function createGenericButtons() {
  919. // Define button configurations
  920. const buttonConfigurations = [
  921. { name: 'c', text: 'Compact', clickHandler: toggleCompactView },
  922. { name: 's', text: 'Save', clickHandler: () => {
  923. // Functionality to save handle-username map to memory or file
  924. console.log("Save button clicked");
  925. }}
  926. // Add more button configurations as needed
  927. ];
  928.  
  929. // Get the container for the buttons
  930. const container = document.getElementById('topButtons');
  931.  
  932. // Loop through each button configuration and generate a button
  933. buttonConfigurations.forEach(config => {
  934. // Create a button element
  935. const button = document.createElement('button');
  936. button.textContent = config.text; // Use button text as text content
  937. button.style.background = "black";
  938. button.style.color = "lime";
  939. button.style.width = "50px"; // Set button width
  940. button.style.height = "20px"; // Set button height
  941. button.style.margin = "0 5px"; // Set button margin
  942.  
  943. // Add event listener based on configuration
  944. button.addEventListener('click', config.clickHandler);
  945.  
  946. // Append the button to the container in the DOM
  947. container.appendChild(button);
  948. });
  949. }
  950.  
  951. // Call the function to create generic buttons
  952. createGenericButtons();
  953.  
  954.  
  955.  
  956.  
  957. })();
  958.  
  959.  
  960.  
  961.  
  962.  
  963.  
  964.  
  965.  
  966.