UNIT3D chatbox - enhanced ULCX

Chat functionalities: Reply, Message and Gift buttons. BBCode buttons. Toggle menu. Emojis.

  1. // ==UserScript==
  2. // @name UNIT3D chatbox - enhanced ULCX
  3. // @version 1.2
  4. // @description Chat functionalities: Reply, Message and Gift buttons. BBCode buttons. Toggle menu. Emojis.
  5. // @match https://upload.cx/
  6. // @grant none
  7. // @namespace https://greasyfork.org/users/1344404
  8. // ==/UserScript==
  9.  
  10. (function() {
  11. 'use strict';
  12. //Please do not alter this, It is for contributors to the project only.
  13. const USER_COLORS = {
  14. 'ace': '#ef008c',
  15. 'TheoneandonlyPook': '#ef008c',
  16. 'Demonic': '#ffac6b',
  17. }
  18.  
  19. // Function to extract the CSRF token
  20. function extractToken() {
  21. const tokenElement = document.querySelector('meta[name="csrf-token"]');
  22. return tokenElement ? tokenElement.content : 'Token not found';
  23. }
  24.  
  25. // Function to extract the user from the URL
  26. function extractUser() {
  27. const userLink = document.querySelector('a.top-nav__username--highresolution');
  28. if (userLink) {
  29. const url = new URL(userLink.href);
  30. return url.pathname.split('/').pop();
  31. }
  32. return 'User not found';
  33. }
  34.  
  35. // Function to remove the dialog from the page
  36. function removeDialog() {
  37. const existingDialog = document.querySelector('dialog');
  38. if (existingDialog) {
  39. existingDialog.remove();
  40. }
  41. }
  42.  
  43. const chatboxID = '#chatbox__messages-create';
  44. const chatMessagesClassName = '.chatroom__messages';
  45. const onlineUserListSelector = '.blocks__online .panel__body ul';
  46. const additionalUserListSelector = '.chatroom-users__list';
  47. const bbCodesPanelID = 'bbCodesPanel';
  48. const settingsButtonID = 'settingsButton';
  49. const settingsPanelID = 'settingsPanel';
  50. const BBCODES_PANEL_HTML = `
  51. <div id="${bbCodesPanelID}" style="position: relative; display: flex; flex-wrap: wrap; z-index: 9998;">
  52. <span id="imgButton" style="cursor: pointer; margin-right: 4px;" data-bbcode="[img][/img]">IMG</span>
  53. <span id="urlButton" style="cursor: pointer; margin-right: 4px;" data-bbcode="[url][/url]">URL</span>
  54. <span id="colorButton" style="cursor: pointer; margin-right: 4px;">Color</span>
  55. <input type="color" id="colorPicker" style="cursor: pointer; margin-left: 10px; display: none;" title="Select Color">
  56. <span style="cursor: pointer; margin: 4px;" id="emojiButton">😊</span>
  57. <div id="emojiMenu" style="display: none; position: absolute; left: 10px; top: 30px; background: #000000; border: 1px solid #ccc; z-index: 10000;">
  58. <span class="emoji" data-emoji="😊">😊</span>
  59. <span class="emoji" data-emoji="😂">😂</span>
  60. <span class="emoji" data-emoji="😍">😍</span>
  61. <span class="emoji" data-emoji="😇">😇</span>
  62. <span class="emoji" data-emoji="🤨">🤨</span>
  63. <span class="emoji" data-emoji="🥳">🥳</span>
  64. <span class="emoji" data-emoji="😏">😏</span>
  65. <span class="emoji" data-emoji="😞">😞</span>
  66. <span class="emoji" data-emoji="😤">😤</span>
  67. <span class="emoji" data-emoji="🤬">🤬</span>
  68. <span class="emoji" data-emoji="🤯">🤯</span>
  69. <span class="emoji" data-emoji="🥵">🥵</span>
  70. <span class="emoji" data-emoji="🥶">🥶</span>
  71. <span class="emoji" data-emoji="🤫">🤫</span>
  72. <span class="emoji" data-emoji="🤥">🤥</span>
  73. <span class="emoji" data-emoji="😴">😴</span>
  74. <span class="emoji" data-emoji="🤮">🤮</span>
  75. <span class="emoji" data-emoji="🤡">🤡</span>
  76. <span class="emoji" data-emoji="💩">💩</span>
  77. <span class="emoji" data-emoji="👻">👻</span>
  78. <span class="emoji" data-emoji="💀">💀</span>
  79. <span class="emoji" data-emoji="👽">👽</span>
  80. <span class="emoji" data-emoji="🎃">🎃</span>
  81. <span class="emoji" data-emoji="🤝">🤝</span>
  82. <span class="emoji" data-emoji="👍">👍</span>
  83. <span class="emoji" data-emoji="👎">👎</span>
  84. <span class="emoji" data-emoji="✌️">✌️</span>
  85. <span class="emoji" data-emoji="🖕">🖕</span>
  86. <span class="emoji" data-emoji="👮">👮</span>
  87. <span class="emoji" data-emoji="🕸️">🕸️</span>
  88. <span class="emoji" data-emoji="🐢">🐢</span>
  89. <span class="emoji" data-emoji="🐋">🐋</span>
  90. <span class="emoji" data-emoji="🐐">🐐</span>
  91. <span class="emoji" data-emoji="🐦‍🔥">🐦‍🔥</span>
  92. <span class="emoji" data-emoji="🌵">🌵</span>
  93. <span class="emoji" data-emoji="🎄">🎄</span>
  94. <span class="emoji" data-emoji="🔥">🔥</span>
  95. <span class="emoji" data-emoji="🌪️">🌪️</span>
  96. <span class="emoji" data-emoji="🌈">🌈</span>
  97. <span class="emoji" data-emoji="☀️">☀️</span>
  98. <span class="emoji" data-emoji="🌧️">🌧️</span>
  99. <span class="emoji" data-emoji="❄️">❄️</span>
  100. <span class="emoji" data-emoji="🍺">🍺</span>
  101. <span class="emoji" data-emoji="🩷">🩷</span>
  102. <span class="emoji" data-emoji="💔">💔</span>
  103. <span class="emoji" data-emoji="🛑">🛑</span>
  104. <span class="emoji" data-emoji="🏴‍☠️">🏴‍☠️</span>
  105. <!-- Add more emojis as needed -->
  106. </div>
  107. <div style="position: relative;">
  108. <span style="cursor: pointer;" id="bbCodeDropdown">➪</span>
  109. <div id="bbCodeDropdownMenu" style="display: none; position: absolute; left: 20px; top: -2px; flex-direction: row; z-index: 10000;">
  110. <span style="cursor: pointer; margin: 2px;" data-bbcode="[b][/b]">[B]</span>
  111. <span style="cursor: pointer; margin: 2px;" data-bbcode="[i][/i]">[I]</span>
  112. <span style="cursor: pointer; margin: 2px;" data-bbcode="[u][/u]">[U]</span>
  113. </div>
  114. </div>
  115. </div>`;
  116.  
  117. const SETTINGS_PANEL_HTML = `<div id="${settingsPanelID}" style="display: none; position: absolute; top: 20px; right: 0; background: rgba(0,0,0,0.9); border-radius: 8px; padding: 10px; color: white; z-index: 10001;">
  118. <label><input type="checkbox" id="toggleBBCodes" checked> Show BB Codes</label><br>
  119. <label><input type="checkbox" id="toggleMessageGift" checked> Show Message/Gift Buttons</label><br>
  120. <label><input type="checkbox" id="toggleReply" checked> Show Reply Button</label>
  121. </div>`;
  122. const TOGGLE_BUTTON_HTML = `<div id="${settingsButtonID}" style="cursor: pointer; color: #fff; display: inline-block; margin-left: 4px; font-size: 16px;">⚙️</div>`;
  123. // Create and add the style block
  124. const style = document.createElement('style');
  125. style.type = 'text/css';
  126. style.innerHTML = `
  127. .emoji {
  128. cursor: pointer;
  129. }
  130. .reply-icon, .message-icon, .gift-icon { cursor: pointer; margin-left: 5px; }
  131. .mention-dropdown { position: absolute; background: #333; border: 1px solid #ccc; max-height: 150px; overflow-y: auto; display: none; z-index: 10000; color: #ccc; }
  132. .mention-dropdown span { display: block; padding: 5px; cursor: pointer; color: #ccc; }
  133. .mention-dropdown span:hover { background: #555; }
  134. .panel__actions { position: relative; }
  135. `;
  136. document.head.appendChild(style);
  137. function saveSetting(key, value) {
  138. localStorage.setItem(key, value);
  139. }
  140. function loadSetting(key) {
  141. return localStorage.getItem(key);
  142. }
  143. function applySettings() {
  144. const toggleBBCodes = loadSetting('toggleBBCodes') === 'true';
  145. const toggleMessageGift = loadSetting('toggleMessageGift') === 'true';
  146. const toggleReply = loadSetting('toggleReply') === 'true';
  147. document.getElementById('toggleBBCodes').checked = toggleBBCodes;
  148. document.getElementById(bbCodesPanelID).style.display = toggleBBCodes ? 'flex' : 'none';
  149. document.getElementById('toggleMessageGift').checked = toggleMessageGift;
  150. const iconsMessageGift = document.querySelectorAll('.message-icon, .gift-icon');
  151. iconsMessageGift.forEach(icon => icon.style.display = toggleMessageGift ? 'inline' : 'none');
  152. document.getElementById('toggleReply').checked = toggleReply;
  153. const iconsReply = document.querySelectorAll('.reply-icon');
  154. iconsReply.forEach(icon => icon.style.display = toggleReply ? 'inline' : 'none');
  155. }
  156. function setupSettingsListeners() {
  157. document.getElementById('toggleBBCodes').addEventListener('change', (event) => {
  158. saveSetting('toggleBBCodes', event.target.checked);
  159. document.getElementById(bbCodesPanelID).style.display = event.target.checked ? 'flex' : 'none';
  160. });
  161. document.getElementById('toggleMessageGift').addEventListener('change', (event) => {
  162. saveSetting('toggleMessageGift', event.target.checked);
  163. const icons = document.querySelectorAll('.message-icon, .gift-icon');
  164. icons.forEach(icon => icon.style.display = event.target.checked ? 'inline' : 'none');
  165. });
  166. document.getElementById('toggleReply').addEventListener('change', (event) => {
  167. saveSetting('toggleReply', event.target.checked);
  168. const icons = document.querySelectorAll('.reply-icon');
  169. icons.forEach(icon => icon.style.display = event.target.checked ? 'inline' : 'none');
  170. });
  171. }
  172. function getOnlineUsernames() {
  173. const userElements = document.querySelectorAll(`${onlineUserListSelector} .user-tag__link`);
  174. const additionalUserElements = document.querySelectorAll(`${additionalUserListSelector} .user-tag__link`);
  175. const userSet = new Set([...Array.from(userElements), ...Array.from(additionalUserElements)].map(el => el.textContent.trim()));
  176. return Array.from(userSet);
  177. }
  178. function setupMentionFeature(chatbox) {
  179. const mentionDropdown = document.createElement('div');
  180. mentionDropdown.classList.add('mention-dropdown');
  181. document.body.appendChild(mentionDropdown);
  182. chatbox.addEventListener('input', function(event) {
  183. const cursorPosition = chatbox.selectionStart;
  184. const text = chatbox.value.substring(0, cursorPosition);
  185. const mentionMatch = text.match(/@(\w*)$/);
  186. if (mentionMatch) {
  187. const usernamePrefix = mentionMatch[1].toLowerCase();
  188. const users = getOnlineUsernames().filter(user => user.toLowerCase().startsWith(usernamePrefix));
  189. mentionDropdown.innerHTML = '';
  190. users.forEach(user => {
  191. const userElement = document.createElement('span');
  192. userElement.textContent = user;
  193. userElement.addEventListener('click', () => {
  194. chatbox.value = chatbox.value.substring(0, cursorPosition - usernamePrefix.length - 1) + '@' + user + ' ' + chatbox.value.substring(cursorPosition);
  195. chatbox.focus();
  196. mentionDropdown.style.display = 'none';
  197. });
  198. mentionDropdown.appendChild(userElement);
  199. });
  200. const rect = chatbox.getBoundingClientRect();
  201. mentionDropdown.style.left = `${rect.left}px`;
  202. mentionDropdown.style.top = `${rect.bottom}px`;
  203. mentionDropdown.style.display = 'block';
  204. } else {
  205. mentionDropdown.style.display = 'none';
  206. }
  207. });
  208. chatbox.addEventListener('keydown', (e) => {
  209. if (mentionDropdown.style.display === 'block' && e.key === 'Tab') {
  210. e.preventDefault();
  211. const firstUser = mentionDropdown.querySelector('span');
  212. if (firstUser) firstUser.click();
  213. }
  214. });
  215. document.addEventListener('click', function(event) {
  216. if (!mentionDropdown.contains(event.target)) mentionDropdown.style.display = 'none';
  217. });
  218. }
  219.  
  220. // Function to add the dialog element to the page
  221. function addDialog(token, user, username) {
  222. // Check if a dialog already exists and remove it
  223. removeDialog();
  224.  
  225. // Create dialog HTML
  226. const dialogHTML = `
  227. <dialog class="dialog" x-bind="dialogElement">
  228. <h3 class="dialog__heading">Gift BON to: ${username}</h3>
  229. <form class="dialog__form" method="POST" action="https://upload.cx/users/${user}/gifts" x-bind="dialogForm">
  230. <input type="hidden" name="_token" value="${token}" autocomplete="off">
  231. <input type="hidden" name="recipient_username" value="${username}">
  232. <p class="form__group">
  233. <input id="bon" class="form__text" name="bon" type="text" pattern="[0-9]*" inputmode="numeric" placeholder=" ">
  234. <label class="form__label form__label--floating" for="bon">Amount</label>
  235. </p>
  236. <p class="form__group">
  237. <textarea id="message" class="form__textarea" name="message" placeholder=" "></textarea>
  238. <label class="form__label form__label--floating" for="message">Message</label>
  239. </p>
  240. <p class="form__group">
  241. <button type="submit" class="form__button form__button--filled">Gift</button>
  242. <button type="button" class="form__button form__button--outlined">Cancel</button>
  243. </p>
  244. </form>
  245. </dialog>`;
  246.  
  247. // Create a container <div> if one does not exist
  248. let container = document.querySelector('#dialog-container');
  249. if (!container) {
  250. container = document.createElement('div');
  251. container.id = 'dialog-container';
  252. document.body.appendChild(container);
  253. }
  254.  
  255. // Add dialog HTML to the container
  256. container.innerHTML = dialogHTML;
  257.  
  258. // Add styling for the backdrop
  259. const style = document.createElement('style');
  260. style.innerHTML = `
  261. dialog::backdrop {
  262. background-color: rgba(0, 0, 0, 0.8);
  263. }`;
  264. document.head.appendChild(style);
  265.  
  266. // Get the dialog element and show it
  267. const dialog = container.querySelector('dialog');
  268. dialog.showModal();
  269.  
  270. // Add event listener to the "Cancel" button to remove the dialog
  271. const cancelButton = dialog.querySelector('button[type="button"]');
  272. if (cancelButton) {
  273. cancelButton.addEventListener('click', removeDialog);
  274. }
  275. }
  276.  
  277. function setupReplyFeatures(chatMessages) {
  278. //Please do not alter this, It is for contributors to the project only.
  279. const USER_COLORS = {
  280. 'ace': '#ef008c',
  281. 'TheoneandonlyPook': '#ef008c',
  282. 'Demonic': '#ffac6b'
  283.  
  284. // Add more users and colors as needed
  285. };
  286.  
  287. const newMessageTextArea = document.querySelector(chatboxID);
  288.  
  289. function rgbToHex(rgb) {
  290. // Convert RGB to HEX format
  291. const rgbArray = rgb.match(/\d+/g).map(Number);
  292. if (rgbArray.length === 3) {
  293. return `#${rgbArray.map(value => {
  294. const hex = value.toString(16).padStart(2, '0');
  295. return hex;
  296. }).join('')}`;
  297. }
  298. return rgb; // Return original if not in expected format
  299. }
  300.  
  301. function quoteMessage(username, message) {
  302. const chatbox = document.querySelector(chatboxID);
  303. if (!chatbox) {
  304. console.error(`Chatbox not found: ${chatboxID}`);
  305. return;
  306. }
  307.  
  308. // Default color if user is not found in USER_COLORS
  309. let userColor = '#ecc846'; // Default color
  310.  
  311. // Check USER_COLORS first
  312. if (USER_COLORS.hasOwnProperty(username)) {
  313. userColor = USER_COLORS[username];
  314. } else {
  315.  
  316. // If not in USER_COLORS, extract the color from the chat messages
  317. const messages = document.querySelectorAll('.chatbox-message, .message');
  318. for (const msg of messages) {
  319. const msgUsername = msg.querySelector(".chatbox-message__address.user-tag span, .message-username span");
  320. if (msgUsername && msgUsername.textContent.trim() === username) {
  321. const style = window.getComputedStyle(msgUsername);
  322. userColor = rgbToHex(style.color);
  323. break; // Stop searching once the user is found
  324. }
  325. }
  326. }
  327.  
  328. // Debugging
  329. console.log(`Username: ${username}, Color used: ${userColor}`);
  330.  
  331. // Create the quoted message
  332. const quoteText = `[color=${userColor}][b]${username}[/b][/color]: [color=#ffff80][i]"${message}"[/i][/color]`;
  333.  
  334. // Append the quoted message to the chat input
  335. chatbox.value += `${quoteText}\n\n`;
  336. chatbox.focus();
  337. chatbox.setSelectionRange(chatbox.value.length, chatbox.value.length);
  338. }
  339.  
  340. function addReplyIconToMessage(message) {
  341. const content = message.querySelector(".chatbox-message__content")?.innerText || message.querySelector(".message-content")?.innerText;
  342. const username = message.querySelector(".chatbox-message__address.user-tag span")?.innerText || message.querySelector(".message-username span")?.innerText;
  343. const header = message.querySelector(".chatbox-message__header") || message.querySelector(".message-header");
  344. if (!content || !username || !header) return;
  345. const replyIcon = document.createElement("i");
  346. replyIcon.classList.add("fa", "solid", "fa-reply", "reply-icon");
  347. replyIcon.style.color = "#d82c20";
  348. replyIcon.addEventListener("click", () => quoteMessage(username, content));
  349. const messageIcon = document.createElement("i");
  350. messageIcon.classList.add("fa", "solid", "fa-envelope", "message-icon");
  351. messageIcon.style.color = "#118DFF";
  352. const giftIcon = document.createElement("i");
  353. giftIcon.classList.add("fa", "solid", "fa-gift", "gift-icon");
  354. giftIcon.style.color = "#f3c911";
  355. messageIcon.addEventListener("click", () => {
  356. newMessageTextArea.value += `/msg ${username} `;
  357. newMessageTextArea.focus();
  358. });
  359. giftIcon.addEventListener("click", () => {
  360. const token = extractToken();
  361. const user = extractUser();
  362. addDialog(token, user, username);
  363. });
  364. header.appendChild(replyIcon);
  365. header.appendChild(messageIcon);
  366. header.appendChild(giftIcon);
  367. applySettings();
  368. }
  369. document.querySelectorAll(".chatbox-message, .message").forEach(addReplyIconToMessage);
  370. const observer = new MutationObserver(function(mutationsList, observer) {
  371. document.querySelectorAll(".reply-icon, .message-icon, .gift-icon").forEach((icon) => icon.remove());
  372. document.querySelectorAll(".chatbox-message, .message").forEach(addReplyIconToMessage);
  373. });
  374. observer.observe(chatMessages, { childList: true });
  375. }
  376. function setupChatFeatures(chatbox) {
  377. const container = document.createElement('div');
  378. container.style.position = 'relative';
  379. container.style.display = 'inline-flex';
  380. chatbox.parentNode.insertBefore(container, chatbox.nextSibling);
  381. const bbCodesPanel = document.createElement('div');
  382. bbCodesPanel.innerHTML = BBCODES_PANEL_HTML;
  383. container.appendChild(bbCodesPanel);
  384. bbCodesPanel.style.background = 'transparent';
  385. bbCodesPanel.style.border = 'none';
  386.  
  387. // BBCode Dropdown
  388. const bbCodeDropdown = document.getElementById('bbCodeDropdown');
  389. const bbCodeDropdownMenu = document.getElementById('bbCodeDropdownMenu');
  390. bbCodeDropdown.addEventListener('click', function() {
  391. bbCodeDropdownMenu.style.display = bbCodeDropdownMenu.style.display === 'flex' ? 'none' : 'flex';
  392. });
  393.  
  394. // Emoji Button
  395. const emojiButton = document.getElementById('emojiButton');
  396. const emojiMenu = document.getElementById('emojiMenu');
  397. emojiButton.addEventListener('click', function() {
  398. emojiMenu.style.display = emojiMenu.style.display === 'block' ? 'none' : 'block';
  399. });
  400.  
  401. // Emoji Menu
  402. emojiMenu.addEventListener('click', function(event) {
  403. if (event.target.classList.contains('emoji')) {
  404. const emoji = event.target.getAttribute('data-emoji');
  405. insertEmoji(emoji, chatbox);
  406. emojiMenu.style.display = 'none'; // Hide menu after selection
  407. }
  408. });
  409.  
  410. // BBCode Buttons
  411. bbCodesPanel.querySelectorAll('span').forEach(function(span) {
  412. span.addEventListener('click', function() {
  413. const bbCode = span.getAttribute('data-bbcode');
  414. if (bbCode === "[img][/img]") {
  415. insertImgBBCodeWithClipboard(bbCode, chatbox);
  416. } else if (bbCode === "[url][/url]") {
  417. insertBBCodeWithClipboard(bbCode, chatbox);
  418. } else if (span.id === 'colorButton') {
  419. document.getElementById('colorPicker').style.display = 'block'; // Show color picker
  420. } else {
  421. insertBBCode(chatbox, bbCode);
  422. }
  423. });
  424. });
  425.  
  426. // Color Picker
  427. document.getElementById('colorPicker').addEventListener('input', function(event) {
  428. const color = event.target.value;
  429. const colorBBCode = `[color=${color}][/color]`;
  430. chatbox.value += colorBBCode + " ";
  431. const pos = chatbox.value.length;
  432. chatbox.setSelectionRange(pos, pos);
  433. chatbox.focus();
  434. document.getElementById('colorPicker').style.display = 'none'; // Hide color picker after selection
  435. });
  436.  
  437. setupMentionFeature(chatbox);
  438. }
  439.  
  440. function insertEmoji(emoji, chatbox) {
  441. const pos = chatbox.selectionStart;
  442. chatbox.value = chatbox.value.substring(0, pos) + emoji + chatbox.value.substring(pos);
  443. chatbox.setSelectionRange(pos + emoji.length, pos + emoji.length);
  444. chatbox.focus();
  445. }
  446.  
  447. function insertBBCodeWithClipboard(tag, chatbox) {
  448. navigator.clipboard.readText().then(clipText => {
  449. const newContent = clipText.trim().length > 0
  450. ? tag.replace(/(\[.*?\])(.*?)(\[\/.*?\])/, `$1${clipText}$3`)
  451. : tag.replace(/(\[.*?\])(.*?)(\[\/.*?\])/, `$1$2$3`);
  452. chatbox.value += newContent + " ";
  453. const pos = chatbox.value.length;
  454. chatbox.setSelectionRange(pos, pos);
  455. chatbox.focus();
  456. }).catch((err) => {
  457. console.error('Failed to read clipboard contents:', err);
  458. chatbox.value += tag.replace(/(\[.*?\])(.*?)(\[\/.*?\])/, `$1$2$3`) + " ";
  459. const pos = chatbox.value.length;
  460. chatbox.setSelectionRange(pos, pos);
  461. chatbox.focus();
  462. });
  463. }
  464. function insertImgBBCodeWithClipboard(tag, chatbox) {
  465. navigator.clipboard.readText().then(clipText => {
  466. const newContent = clipText.trim().length > 0
  467. ? tag.replace(/(\[.*?\])(.*?)(\[\/.*?\])/, `$1${clipText}$3`)
  468. : tag.replace(/(\[.*?\])(.*?)(\[\/.*?\])/, `$1$2$3`);
  469. chatbox.value += newContent + "\n";
  470. const pos = chatbox.value.length;
  471. chatbox.setSelectionRange(pos, pos);
  472. chatbox.focus();
  473. }).catch((err) => {
  474. console.error('Failed to read clipboard contents:', err);
  475. chatbox.value += tag.replace(/(\[.*?\])(.*?)(\[\/.*?\])/, `$1$2$3`) + "\n";
  476. const pos = chatbox.value.length;
  477. chatbox.setSelectionRange(pos, pos);
  478. chatbox.focus();
  479. });
  480. }
  481. function insertBBCode(chatbox, bbCode) {
  482. const textSelected = chatbox.value.substring(chatbox.selectionStart, chatbox.selectionEnd);
  483. const startTag = bbCode.substring(0, bbCode.indexOf(']') + 1);
  484. const endTag = bbCode.substring(bbCode.lastIndexOf('['));
  485. if (textSelected.length > 0) {
  486. const newText = startTag + textSelected + endTag;
  487. chatbox.value = chatbox.value.substring(0, chatbox.selectionStart) + newText + " " + chatbox.value.substring(chatbox.selectionEnd);
  488. const newPos = chatbox.value.lastIndexOf(' ') + 1;
  489. chatbox.setSelectionRange(newPos, newPos);
  490. } else {
  491. const pos = chatbox.selectionStart + startTag.length;
  492. chatbox.value += startTag + endTag + " ";
  493. chatbox.setSelectionRange(pos, pos);
  494. }
  495. chatbox.focus();
  496. }
  497. function setupSettingsPanel() {
  498. const chatboxHeaderActions = document.querySelector('#chatbox_header .panel__actions');
  499. if (chatboxHeaderActions) {
  500. chatboxHeaderActions.insertAdjacentHTML('beforeend', TOGGLE_BUTTON_HTML);
  501. const settingsPanel = document.createElement('div');
  502. settingsPanel.innerHTML = `
  503. <div id="${settingsPanelID}" style="display: none; position: absolute; top: 30px; right: 0; background: rgba(0,0,0,0.9); border-radius: 8px; padding: 10px; color: white; z-index: 10001;">
  504. <label><input type="checkbox" id="toggleBBCodes" checked> Show BB Codes</label><br>
  505. <label><input type="checkbox" id="toggleMessageGift" checked> Show Message/Gift Buttons</label><br>
  506. <label><input type="checkbox" id="toggleReply" checked> Show Reply Button</label>
  507. </div>`;
  508. chatboxHeaderActions.appendChild(settingsPanel);
  509. const toggleSettingsButton = document.getElementById(settingsButtonID);
  510. const settingsPanelElement = document.getElementById(settingsPanelID);
  511. toggleSettingsButton.addEventListener('click', (event) => {
  512. event.stopPropagation(); // Prevent event from bubbling up
  513. const isPanelVisible = settingsPanelElement.style.display === 'block';
  514. settingsPanelElement.style.display = isPanelVisible ? 'none' : 'block';
  515. });
  516. document.addEventListener('click', (event) => {
  517. if (!settingsPanelElement.contains(event.target) && !toggleSettingsButton.contains(event.target)) {
  518. settingsPanelElement.style.display = 'none'; // Hide when clicking outside
  519. }
  520. });
  521. applySettings();
  522. setupSettingsListeners();
  523. } else {
  524. console.error("Failed to attach the settings button: '#chatbox_header .panel__actions' not found.");
  525. }
  526. }
  527. function checkAndSetup() {
  528. const chatbox = document.querySelector(chatboxID);
  529. const chatMessages = document.querySelector(chatMessagesClassName);
  530. if (chatbox) {
  531. setupChatFeatures(chatbox);
  532. setupSettingsPanel();
  533. } else {
  534. console.error('Chatbox not found: Ensure the chatbox ID is correct.');
  535. }
  536. if (chatMessages) {
  537. setupReplyFeatures(chatMessages);
  538. } else {
  539. console.error('Chat messages not found: Ensure the chatMessages class is correct.');
  540. }
  541. if (!chatbox || !chatMessages) {
  542. setTimeout(checkAndSetup, 1000);
  543. }
  544.  
  545. }
  546.  
  547. checkAndSetup();
  548.  
  549. })();