AAC Enhanced plus AAC Utils

Adds custom UI enhancements

目前为 2025-02-26 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name AAC Enhanced plus AAC Utils
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.10.1
  5. // @copyright 2025, Asriel (https://gf.qytechs.cn/de/users/1375984-asriel-aac) 2022, Reimu(https://github.com/ReiwuKleiwu/AAC-Utils)
  6. // @license MIT
  7. // @description Adds custom UI enhancements
  8. // @author Asriel / Nick S. aka. Slash
  9. // @icon https://i.ibb.co/z5CJ5zv/revanced.png
  10. // @supportURL https://gf.qytechs.cn/de/scripts/511351-aac-enhanced-plus-aac-utils/feedback
  11. // @include /^https?:\/\/(www\.)?anime\.academy\/chat/
  12. // @grant none
  13. // @require https://code.jquery.com/jquery-3.6.0.min.js
  14. // @require https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
  15. // ==/UserScript==
  16.  
  17.  
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22.  
  23.  
  24. function addGlobalStyle(css) {
  25. let head, style;
  26. head = document.getElementsByTagName('head')[0];
  27. if (!head) {
  28. return;
  29. }
  30. style = document.createElement('style');
  31. style.type = 'text/css';
  32. style.innerHTML = css;
  33. head.appendChild(style);
  34. }
  35.  
  36. addGlobalStyle(`
  37. '.autocomplete-flex {' +
  38. ' background-color: #484b52;' +
  39. ' font-weight: 600;' +
  40. ' display: flex;' +
  41. ' align-items: center;' +
  42. ' gap: 10px;' +
  43. ' padding: 5px;' +
  44. '}' +
  45. '.autocomplete-flex:hover {' +
  46. ' background-color: #5f646e;' +
  47. '}' +
  48. '' +
  49. '.autocomplete-flex:focus {' +
  50. ' background-color: #5f646e;' +
  51. '}' +
  52. '' +
  53. '.name-link:hover {' +
  54. ' text-decoration: none;' +
  55. '}' +
  56. '.autocomplete-icon {' +
  57. ' width: 45px !important;' +
  58. ' height: 45px !important;' +
  59. ' border-radius: 50%;' +
  60. '}' +
  61. '.chatMessage {' +
  62. ' transition: padding 0.4s ease 0s;' +
  63. '}' +
  64. '.chatMessage:hover {' +
  65. ' padding-top: 5px;' +
  66. ' padding-bottom: 5px;' +
  67. ' position: relative;' +
  68. ' background-color: #5f646e !important;' +
  69. ' border-radius: 3px;' +
  70. ' transition: padding 0.4s ease 0s;' +
  71. '}' +
  72. '.messageIcons {' +
  73. ' display: flex;' +
  74. ' justify-content: center;' +
  75. ' align-items: center;' +
  76. ' height: 32px;' +
  77. ' width: 32px;' +
  78. ' background-color: #484b52;' +
  79. ' position: absolute;' +
  80. ' right: 0;' +
  81. ' top: -20px;' +
  82. ' display: none;' +
  83. ' border-radius: 3px;' +
  84. '}' +
  85. '.messageIcons:hover {' +
  86. ' background-color: #5f646e;' +
  87. ' filter: drop-shadow(5px 7px 14px black);' +
  88. '}' +
  89. '' +
  90. '.collapsible-wrap {' +
  91. ' margin: 10px 0 10px 0;' +
  92. '}' +
  93. '' +
  94. '.collapsible-button {' +
  95. ' cursor: pointer;' +
  96. ' padding: 18px;' +
  97. ' width: 100%;' +
  98. ' text-align: left;' +
  99. ' outline: none;' +
  100. ' font-size: 15px;' +
  101. '}' +
  102. '' +
  103. '.collapsible-button:focus {' +
  104. ' transition: background-color 0.5s ease;' +
  105. ' background-color: #3e1e80 !important;' +
  106. '}' +
  107. '' +
  108. '.collapsible-button:hover {' +
  109. ' transition: background-color 0.5s ease;' +
  110. '}' +
  111. '' +
  112. '.active {' +
  113. ' border-bottom-right-radius: 0 !important;' +
  114. ' border-bottom-left-radius: 0 !important;' +
  115. ' background-color: #3e1e80 !important;' +
  116. '}' +
  117. '' +
  118. '.collapsible-button:after {' +
  119. " content: '\\002B';" +
  120. ' font-weight: bold;' +
  121. ' float: right;' +
  122. ' margin-left: 5px;' +
  123. '}' +
  124. '' +
  125. '.active:after {' +
  126. " content: '\\2212';" +
  127. '}' +
  128. '' +
  129. '.collapsible-content {' +
  130. ' padding: 0 10px;' +
  131. ' max-height: 0;' +
  132. ' overflow: hidden;' +
  133. ' transition: max-height 0.2s ease-out;' +
  134. ' background-color: #2c2f33;' +
  135. ' border-bottom-left-radius: 3px;' +
  136. ' border-bottom-right-radius: 3px;' +
  137. '}' +
  138. '' +
  139. '.avatar-grid {' +
  140. ' width: 100%;' +
  141. ' display: grid;' +
  142. ' grid-template-columns: repeat(auto-fill, 8em);' +
  143. ' grid-gap: 10px 15px;' +
  144. '}' +
  145. '' +
  146. '.avatar-div {' +
  147. ' height: 80px;' +
  148. ' width: 80px;' +
  149. ' display: flex;' +
  150. ' align-items: center;' +
  151. ' justify-content: center;' +
  152. ' gap: 5px;' +
  153. '}' +
  154. '' +
  155. '.avatar-div:hover {' +
  156. ' background-color: #484b52;' +
  157. ' transition: background-color 0.5s ease;' +
  158. ' border-radius: 3px;' +
  159. '}' +
  160. '' +
  161. '.avatarIcon {' +
  162. ' height: 20px;' +
  163. ' width: 20px;' +
  164. ' background-color: #3e1e80;' +
  165. ' border-radius: 50%;' +
  166. ' display: none;' +
  167. ' align-items: center;' +
  168. ' justify-content: center;' +
  169. '}' +
  170. '' +
  171. '.avatarIcon:hover {' +
  172. ' background-color: #6b36d9;' +
  173. ' filter: drop-shadow(5px 7px 14px #2c2f33);' +
  174. ' transition: background-color 0.5s ease;' +
  175. '}' +
  176. '' +
  177. '.ionicon {' +
  178. ' width: 13px;' +
  179. ' height: 13px;' +
  180. ' fill: #ddd;' +
  181. '}' +
  182. '' +
  183. '.collection-selection {' +
  184. ' position: absolute;' +
  185. ' z-index: 10000;' +
  186. ' margin-left: auto;' +
  187. ' margin-right: auto;' +
  188. ' right: 0;' +
  189. ' left: 0;' +
  190. ' top: 80px;' +
  191. ' width: 300px;' +
  192. ' background-color: #484b52;' +
  193. ' padding: 10px;' +
  194. ' text-align: center;' +
  195. ' overflow: hidden scroll;' +
  196. ' border-radius: 3px;' +
  197. ' max-height: 200px;' +
  198. '}' +
  199. '' +
  200. '.collection-selectable {' +
  201. ' font-weight: 700;' +
  202. ' padding: 10px 0;' +
  203. ' margin-bottom: 5px;' +
  204. '}' +
  205. '' +
  206. '.collection-selectable:hover {' +
  207. ' background-color: #3e1e80;' +
  208. ' border-radius: 3px;' +
  209. ' transition: background-color 0.5s ease;' +
  210. ' filter: drop-shadow(5px 7px 14px #2c2f33);' +
  211. '}' +
  212. '' +
  213. '#createCollectionBtn {' +
  214. ' font-weight: 700;' +
  215. '}' +
  216. '' +
  217. '.removeCategory-btn {' +
  218. ' background-color: #fd2f2f;' +
  219. '}' +
  220. '' +
  221. '.request-delete-collection {' +
  222. ' position: absolute;' +
  223. ' z-index: 10000;' +
  224. ' margin-left: auto;' +
  225. ' margin-right: auto;' +
  226. ' right: 0;' +
  227. ' left: 0;' +
  228. ' top: 80px;' +
  229. ' width: 300px;' +
  230. ' background-color: #484b52;' +
  231. ' padding: 10px;' +
  232. ' text-align: center;' +
  233. ' overflow: hidden scroll;' +
  234. ' border-radius: 3px;' +
  235. ' max-height: 200px;' +
  236. '}' +
  237. '' +
  238. '.message-image {' +
  239. ' width: 100%;' +
  240. ' height: auto;' +
  241. ' border-radius: 3px;' +
  242. '}' +
  243. '' +
  244. '.message-image-container {' +
  245. ' margin-top: 5px;' +
  246. '}' +
  247. '' +
  248. '#username-results {' +
  249. ' border: none;' +
  250. ' max-height: 200px;' +
  251. ' overflow: hidden scroll;' +
  252. ' max-width: 30%;' +
  253. ' position: absolute;' +
  254. ' left: 0;' +
  255. ' right: 0;' +
  256. ' bottom: calc(100% + 8px);' +
  257. ' margin-left: 8px;' +
  258. ' border-radius: 5px;' +
  259. '}' +
  260. ''
  261. `);
  262.  
  263. const scriptName = GM_info.script.name;
  264. const scriptVersion = GM_info.script.version;
  265. window.aacEnhancedScriptID = `${scriptName.replace(/\s+/g, '-')}-${scriptVersion}`;
  266.  
  267. console.log(window.aacEnhancedScriptID);
  268.  
  269. // Necessary for autoreconnect
  270. window.onbeforeunload = null;
  271.  
  272. // Access the AngularJS scope
  273. const scope = angular.element(document.getElementById('topbar')).scope();
  274.  
  275. // Global Socket Hook
  276. const globalSocketReady = new Event('globalSocketReady');
  277.  
  278. io.Socket.prototype.o_emit = io.Socket.prototype.o_emit || io.Socket.prototype.emit;
  279. io.Socket.prototype.emit = function (eventName, ...args) {
  280. if (!window.socket) {
  281. window.socket = this;
  282. window.dispatchEvent(globalSocketReady);
  283. }
  284.  
  285. window.dispatchEvent(new CustomEvent('socketEmit', { detail: { eventName: eventName, args: [...args] } }));
  286.  
  287. return this.o_emit(eventName, ...args);
  288. };
  289.  
  290. /************************************
  291. *
  292. *
  293. * Autoreconnect Modul
  294. *
  295. *
  296. ************************************/
  297.  
  298. const disconnectReasons = [
  299. 'transport error',
  300. 'transport close',
  301. 'io client disconnect',
  302. 'io server disconnect',
  303. 'ping timeout',
  304. ];
  305.  
  306. const disconnected = JSON.parse(localStorage.getItem('disconnected'));
  307.  
  308. if (disconnected) {
  309. localStorage.setItem('disconnected', JSON.stringify(false));
  310. window.addEventListener('globalSocketReady', () => {
  311. setTimeout(() => {
  312. window.socket.emit('moveAvatar', JSON.parse(localStorage.getItem('avatarPosition')));
  313. }, 1500);
  314. });
  315. }
  316.  
  317. window.addEventListener('socketEmit', (event) => {
  318. if (event.detail.eventName === 'moveAvatar') {
  319. localStorage.setItem('avatarPosition', JSON.stringify(event.detail.args[0]));
  320. }
  321. if (event.detail.eventName === 'disconnect' && disconnectReasons.includes(event.detail.args[0])) {
  322. localStorage.setItem('disconnected', JSON.stringify(true));
  323. location.reload();
  324. }
  325. });
  326. /************************************
  327. *
  328. *
  329. * Chatverlaufs Modul
  330. *
  331. *
  332. ************************************/
  333.  
  334. const maxStoredMessagesPerRoom = 50;
  335. const maxMessageAge = 1000 * 60 * 30; // 30 Minutes
  336.  
  337. window.addEventListener('globalSocketReady', () => {
  338. window.socket.on('updateChatLines', (data) => {
  339. const currentRoom = window.location.href.split('=')[1];
  340.  
  341. let roomData;
  342.  
  343. if (!localStorage.hasOwnProperty('rooms')) {
  344. localStorage.setItem('rooms', JSON.stringify({}));
  345. }
  346. if (!JSON.parse(localStorage.getItem('rooms')).hasOwnProperty(currentRoom)) {
  347. roomData = JSON.parse(localStorage.getItem('rooms'));
  348. roomData[currentRoom] = {
  349. messages: [],
  350. };
  351. roomData = JSON.stringify(roomData);
  352. localStorage.setItem('rooms', roomData);
  353. }
  354.  
  355. roomData = JSON.parse(localStorage.getItem('rooms'));
  356. if (data.user !== 'System') roomData[currentRoom].messages.push(data);
  357. if (roomData[currentRoom].messages.length > maxStoredMessagesPerRoom) roomData[currentRoom].messages.splice(0, 1);
  358.  
  359. roomData = JSON.stringify(roomData);
  360. localStorage.setItem('rooms', roomData);
  361. });
  362.  
  363. window.addEventListener('socketEmit', (event) => {
  364. if (event.detail.eventName === 'changeRoom') {
  365. setTimeout(() => {
  366. restoreChatlogs();
  367. }, 100);
  368. }
  369. });
  370.  
  371. restoreChatlogs();
  372. });
  373.  
  374. function restoreChatlogs() {
  375. let currentRoomChatlogs = undefined;
  376.  
  377. if (JSON.parse(localStorage.getItem('rooms'))) {
  378. if (JSON.parse(localStorage.getItem('rooms'))[window.location.href.split('=')[1]]) {
  379. currentRoomChatlogs = JSON.parse(localStorage.getItem('rooms'))[window.location.href.split('=')[1]].messages;
  380. }
  381. } else {
  382. return;
  383. }
  384.  
  385. if (currentRoomChatlogs) {
  386. setTimeout(() => {
  387. for (const messageData of currentRoomChatlogs) {
  388. if (messageData.user !== 'System' && Date.now() - messageData.timestamp < maxMessageAge) {
  389. const message = {
  390. hasPremium: messageData.hasPremium,
  391. msg: messageData.chatLine,
  392. festername: messageData.festername,
  393. house: messageData.house ? messageData.house : null,
  394. color: undefined,
  395. user: messageData.user + ': ',
  396. timestamp: `${new Date(messageData.timestamp).toLocaleDateString('de-DE')} - ${new Date(
  397. messageData.timestamp
  398. )
  399. .toLocaleTimeString('de-DE')
  400. .slice(0, -3)} Uhr`,
  401. };
  402.  
  403. scope.chatmsgs.push(message);
  404. document.getElementById('topbar').click(); // Refresh the chat
  405. }
  406. }
  407. }, 1500);
  408. }
  409. }
  410.  
  411.  
  412. /************************************
  413. *
  414. *
  415. * Autocomplete usernames Modul
  416. *
  417. *
  418. ************************************/
  419. const chatArea = document.getElementById('graphicChatArea');
  420. chatArea.style.border = 'none';
  421.  
  422. const messageForm = document.getElementsByName('chatMsgForm')[0];
  423. const messageInput = document.getElementById('chatline');
  424.  
  425. messageInput.setAttribute('onKeyUp', 'showResults(this.value)');
  426.  
  427. const resultDiv = document.createElement('div');
  428. resultDiv.setAttribute('id', 'username-results');
  429.  
  430. messageForm.appendChild(resultDiv);
  431.  
  432. function showResults(value) {
  433. const result = document.getElementById('username-results');
  434.  
  435. result.style.display = 'block';
  436.  
  437. if (!value.includes('@')) {
  438. result.style.display = 'none';
  439. }
  440.  
  441. result.innerHTML = '';
  442.  
  443. let list = '';
  444.  
  445. const users = window.autocompleteMatch(value);
  446.  
  447. for (const user in users) {
  448. list += `<a class="name-link"><div class="autocomplete-flex autocomplete-list" onclick="replaceName(this.children[1].innerHTML)" tabindex="0"><img class="autocomplete-icon" src="/img/publicimg/skinthumbnails/${
  449. users[user].imgthumb
  450. }"><div class="name-wrap">${users[user].username.split('@')[1]}</div></div></a>`;
  451. }
  452.  
  453. result.innerHTML = `<ul style="padding: 0px; margin: 0px;">${list}</ul>`;
  454.  
  455. const autocompleteListElements = document.getElementsByClassName('autocomplete-list');
  456.  
  457. for (const element of autocompleteListElements) {
  458. element.addEventListener('keyup', function (event) {
  459. if (event.keyCode === 13) {
  460. window.replaceName(this.children[1].innerHTML);
  461. }
  462. });
  463. }
  464. }
  465.  
  466. function autocompleteMatch(input) {
  467. if (input === '') {
  468. return [];
  469. }
  470.  
  471. const scope = angular.element(document.getElementById('topbar')).scope();
  472. const users = scope.chatterlist.filter((user) => user.room === scope.roomData.name);
  473. const userObjects = users.map((user) => {
  474. return { ...user, username: `@${user.username}` };
  475. });
  476.  
  477. const reg = new RegExp(`\\B@(${input.split('@')[1]}.*)$`, 'i');
  478. return userObjects.filter((user) => {
  479. if (user.username.match(reg)) {
  480. return user;
  481. }
  482. });
  483. }
  484.  
  485. function replaceName(username) {
  486. const scope = angular.element(document.getElementById('topbar')).scope();
  487. const messageInput = document.getElementById('chatline');
  488.  
  489. scope.chatline = scope.chatline.replace(/\B@(\w*)$/, `${username} `);
  490.  
  491. const resultDiv = document.getElementById('username-results');
  492. resultDiv.style.display = 'none';
  493.  
  494. messageInput.focus();
  495. }
  496.  
  497. window.replaceName = replaceName;
  498. window.showResults = showResults;
  499. window.autocompleteMatch = autocompleteMatch;
  500.  
  501. // Main Application Namespace
  502. const AACApp = {
  503. init: function () {
  504. console.log("[AAC Enhanced] Initializing...");
  505. this.loadModules();
  506. },
  507.  
  508. loadModules: function () {
  509. ChatLogger.init(); // 🔹 Chat Logger initialized seamlessly
  510. TopWrapperIcon.init();
  511. WallpaperChanger.init();
  512. ChatArea.init();
  513. }
  514. };
  515.  
  516. /************************************
  517. *
  518. *
  519. * Video Präferenzen Modul
  520. *
  521. *
  522. ************************************/
  523.  
  524.  
  525. const enableVideosInput = document.getElementById('checkAllowVideo');
  526. const enableVideos =
  527. JSON.parse(localStorage.getItem('enableVideos')) === undefined
  528. ? true
  529. : JSON.parse(localStorage.getItem('enableVideos'));
  530.  
  531. scope.allowVideos = enableVideos;
  532. enableVideosInput.checked = enableVideos;
  533.  
  534. enableVideosInput.addEventListener('change', function () {
  535. localStorage.setItem('enableVideos', JSON.stringify(this.checked));
  536. });
  537.  
  538.  
  539. /************************************
  540. *
  541. *
  542. * Microphone colorsaver Modul
  543. *
  544. *
  545. ************************************/
  546. const microphoneBackgroundColor = JSON.parse(localStorage.getItem('microphoneBackgroundColor'));
  547. const microphoneTextColor = JSON.parse(localStorage.getItem('microphoneTextColor'));
  548.  
  549. if (microphoneBackgroundColor && microphoneTextColor) {
  550. window.addEventListener('globalSocketReady', () => {
  551. setTimeout(() => {
  552. window.socket.emit('applyMicrophoneColor', {
  553. microphoneBackgroundColor: microphoneBackgroundColor,
  554. microphoneTextColor: microphoneTextColor,
  555. });
  556. }, 1500);
  557. });
  558. }
  559.  
  560. window.addEventListener('socketEmit', (event) => {
  561. if (event.detail.eventName === 'applyMicrophoneColor') {
  562. localStorage.setItem('microphoneBackgroundColor', JSON.stringify(event.detail.args[0].microphoneBackgroundColor));
  563. localStorage.setItem('microphoneTextColor', JSON.stringify(event.detail.args[0].microphoneTextColor));
  564. }
  565. });
  566. /************************************
  567. *
  568. *
  569. *DM loading Fix Modul
  570. *
  571. *
  572. ************************************/
  573.  
  574. window.addEventListener('globalSocketReady', () => {
  575. window.socket.on('getPNList', () => {
  576. const scope = angular.element(document.getElementById('topbar')).scope();
  577. scope.loading = false;
  578. });
  579. });
  580.  
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587. /************************************
  588. *
  589. *
  590. *Top Wrapper & Icon Modul
  591. *
  592. *
  593. ************************************/
  594.  
  595.  
  596. const TopWrapperIcon = {
  597. init: function() {
  598. this.addIconToTopWrapper();
  599. },
  600.  
  601. addIconToTopWrapper: function() {
  602. const topWrapper = document.querySelector('.area');
  603. if (topWrapper) {
  604. const iconButton = this.createIconButton();
  605. iconButton.addEventListener('click', () => Menu.createPlaceholderMenu());
  606. topWrapper.appendChild(iconButton);
  607. } else {
  608. console.warn('[AAC Enhanced] Top wrapper not found. Icon could not be added.');
  609. }
  610. },
  611.  
  612. createIconButton: function() {
  613. const iconButton = document.createElement('div');
  614. iconButton.id = 'aac-topWrapperIcon';
  615. iconButton.style.width = '40px';
  616. iconButton.style.height = '40px';
  617. iconButton.style.borderRadius = '50%';
  618. iconButton.style.display = 'flex';
  619. iconButton.style.alignItems = 'center';
  620. iconButton.style.justifyContent = 'center';
  621. iconButton.style.cursor = 'pointer';
  622. iconButton.style.marginRight = '10px';
  623. iconButton.style.boxShadow = '0 0 5px rgba(0, 0, 0, 0.2)';
  624.  
  625. const iconImage = document.createElement('img');
  626. iconImage.src = 'https://i.ibb.co/z5CJ5zv/revanced.png';
  627. iconImage.alt = 'Menu Icon';
  628. iconImage.style.width = '100%';
  629. iconImage.style.height = '100%';
  630. iconImage.style.borderRadius = '50%';
  631.  
  632. iconButton.appendChild(iconImage);
  633. return iconButton;
  634. }
  635. };
  636. /************************************
  637. *
  638. *
  639. *Menü Modul
  640. *
  641. *
  642. ************************************/
  643. const Menu = {
  644. createPlaceholderMenu: function() {
  645. const existingMenu = document.querySelector('#aac-placeholderMenu');
  646. if (existingMenu) {
  647. existingMenu.remove();
  648. return;
  649. }
  650.  
  651. const menu = this.createMenuElement();
  652. document.body.appendChild(menu);
  653.  
  654. ChatArea.updateToggleButton();
  655. this.updateToggleLeftbarButton();
  656. },
  657.  
  658. createMenuElement: function() {
  659. const menu = document.createElement('div');
  660. menu.id = 'aac-placeholderMenu';
  661. const iconButton = document.querySelector('#aac-topWrapperIcon');
  662. const iconButtonRect = iconButton.getBoundingClientRect();
  663. menu.style.position = 'absolute';
  664. menu.style.top = `${iconButtonRect.bottom + window.scrollY + 10}px`;
  665. menu.style.left = `${iconButtonRect.left + window.scrollX}px`;
  666. menu.style.zIndex = '10001';
  667. menu.style.padding = '20px';
  668. menu.style.backgroundColor = '#fff';
  669. menu.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
  670. menu.style.borderRadius = '10px';
  671. menu.style.textAlign = 'center';
  672.  
  673. const title = this.createTitleElement();
  674. const wallpaperSectionTitle = document.createElement('h4');
  675. wallpaperSectionTitle.innerText = 'Wallpaper Einstellungen';
  676. wallpaperSectionTitle.style.marginTop = '20px';
  677. wallpaperSectionTitle.style.marginBottom = '10px';
  678.  
  679. const wallpaperChangerButton = WallpaperChanger.createWallpaperChangerButton();
  680. const savedWallpapersButton = WallpaperChanger.createSavedWallpapersButton();
  681. const uiSettingsSectionTitle = document.createElement('h4');
  682. uiSettingsSectionTitle.innerText = 'UI Einstellungen';
  683. uiSettingsSectionTitle.style.marginTop = '20px';
  684. uiSettingsSectionTitle.style.marginBottom = '10px';
  685.  
  686. const toggleChatAreaButton = ChatArea.createToggleChatAreaButton();
  687. const toggleLeftbarButton = this.createToggleLeftbarButton();
  688.  
  689. // Add Reset Button to UI Settings
  690. const resetChatAreaButton = document.createElement('button');
  691. resetChatAreaButton.innerText = 'Chat Fenster Position zurücksetzen';
  692. resetChatAreaButton.style.padding = '10px';
  693. resetChatAreaButton.style.backgroundColor = '#573699';
  694. resetChatAreaButton.style.color = '#fff';
  695. resetChatAreaButton.style.border = 'none';
  696. resetChatAreaButton.style.borderRadius = '5px';
  697. resetChatAreaButton.style.cursor = 'pointer';
  698. resetChatAreaButton.style.marginBottom = '10px';
  699. resetChatAreaButton.addEventListener('click', () => ChatArea.resetChatAreaPosition());
  700.  
  701. // PNG Management Section
  702. const pngSectionTitle = document.createElement('h4');
  703. pngSectionTitle.innerText = 'PNG Management';
  704. pngSectionTitle.style.marginTop = '20px';
  705. pngSectionTitle.style.marginBottom = '10px';
  706.  
  707. const pngManagerButton = this.createPngManagerButton();
  708.  
  709. const closeButton = this.createCloseButton();
  710.  
  711. menu.appendChild(title);
  712. menu.appendChild(wallpaperSectionTitle);
  713. menu.appendChild(wallpaperChangerButton);
  714. menu.appendChild(document.createElement('br'));
  715. menu.appendChild(savedWallpapersButton);
  716. menu.appendChild(document.createElement('br'));
  717. menu.appendChild(uiSettingsSectionTitle);
  718. menu.appendChild(toggleChatAreaButton);
  719. menu.appendChild(document.createElement('br'));
  720. menu.appendChild(toggleLeftbarButton);
  721. menu.appendChild(document.createElement('br'));
  722. menu.appendChild(resetChatAreaButton); // Moved here
  723. menu.appendChild(document.createElement('br'));
  724.  
  725. // Append PNG Management section
  726. menu.appendChild(pngSectionTitle);
  727. menu.appendChild(pngManagerButton);
  728. menu.appendChild(document.createElement('br'));
  729.  
  730. menu.appendChild(closeButton);
  731.  
  732. return menu;
  733. },
  734.  
  735. createTitleElement: function() {
  736. const title = document.createElement('h3');
  737. title.innerText = 'AC-Enhanced Settings';
  738. title.style.marginBottom = '20px';
  739. return title;
  740. },
  741.  
  742. createToggleLeftbarButton: function() {
  743. const toggleLeftbarButton = document.createElement('button');
  744. toggleLeftbarButton.id = 'aac-toggleLeftbarButton';
  745. toggleLeftbarButton.style.padding = '10px';
  746. toggleLeftbarButton.style.color = '#fff';
  747. toggleLeftbarButton.style.border = 'none';
  748. toggleLeftbarButton.style.borderRadius = '5px';
  749. toggleLeftbarButton.style.cursor = 'pointer';
  750. toggleLeftbarButton.style.marginBottom = '10px';
  751.  
  752. toggleLeftbarButton.addEventListener('click', () => Menu.toggleLeftbarVisibility());
  753.  
  754. return toggleLeftbarButton;
  755. },
  756.  
  757. createResetChatAreaButton: function() {
  758. const resetButton = document.createElement('button');
  759. resetButton.innerText = 'Chat Fenster Position zurücksetzen';
  760. resetButton.style.padding = '10px';
  761. resetButton.style.backgroundColor = '#573699';
  762. resetButton.style.color = '#fff';
  763. resetButton.style.border = 'none';
  764. resetButton.style.borderRadius = '5px';
  765. resetButton.style.cursor = 'pointer';
  766. resetButton.style.marginBottom = '10px';
  767.  
  768. resetButton.addEventListener('click', () => ChatArea.resetChatAreaPosition());
  769.  
  770. return resetButton;
  771. },
  772.  
  773. createPngManagerButton: function() {
  774. const pngManagerButton = document.createElement('button');
  775. pngManagerButton.innerText = 'PNG Management';
  776. pngManagerButton.style.padding = '10px';
  777. pngManagerButton.style.backgroundColor = '#573699';
  778. pngManagerButton.style.color = '#fff';
  779. pngManagerButton.style.border = 'none';
  780. pngManagerButton.style.borderRadius = '5px';
  781. pngManagerButton.style.cursor = 'pointer';
  782. pngManagerButton.style.marginBottom = '10px';
  783.  
  784. pngManagerButton.addEventListener('click', () => {
  785. PngManager.createPngMenu();
  786. });
  787.  
  788. return pngManagerButton;
  789. },
  790.  
  791. createCloseButton: function() {
  792. const closeButton = document.createElement('button');
  793. closeButton.innerText = 'Schließen';
  794. closeButton.style.padding = '10px';
  795. closeButton.style.backgroundColor = '#ccc';
  796. closeButton.style.color = '#000';
  797. closeButton.style.border = 'none';
  798. closeButton.style.borderRadius = '5px';
  799. closeButton.style.cursor = 'pointer';
  800.  
  801. closeButton.addEventListener('click', () => {
  802. const menu = document.querySelector('#aac-placeholderMenu');
  803. if (menu) menu.remove();
  804. });
  805.  
  806. return closeButton;
  807. },
  808.  
  809. toggleLeftbarVisibility: function() {
  810. const leftbar = document.querySelector('#leftbar');
  811. const chatBox = document.querySelector('#chatverlaufbox');
  812. if (!leftbar) return;
  813.  
  814. if (leftbar.style.visibility === 'hidden') {
  815. leftbar.style.transition = 'visibility 0s, opacity 0.5s linear';
  816. leftbar.style.opacity = '1';
  817. leftbar.style.visibility = 'visible';
  818. leftbar.style.pointerEvents = 'auto';
  819. if (chatBox) chatBox.style.left = '250px';
  820. localStorage.setItem('aac-leftbarVisibility', 'visible');
  821. } else {
  822. leftbar.style.transition = 'visibility 0s 0.5s, opacity 0.5s linear';
  823. leftbar.style.opacity = '0';
  824. leftbar.style.visibility = 'hidden';
  825. leftbar.style.pointerEvents = 'none';
  826. if (chatBox) chatBox.style.left = '0';
  827. localStorage.setItem('aac-leftbarVisibility', 'hidden');
  828. }
  829.  
  830. this.updateToggleLeftbarButton();
  831. }
  832. };
  833.  
  834.  
  835.  
  836.  
  837.  
  838. Menu.updateToggleLeftbarButton = function() {
  839. const toggleLeftbarButton = document.querySelector('#aac-toggleLeftbarButton');
  840. const leftbarVisible = localStorage.getItem('aac-leftbarVisibility') !== 'hidden';
  841.  
  842. if (toggleLeftbarButton) {
  843. if (leftbarVisible) {
  844. toggleLeftbarButton.innerText = 'User Liste verstecken';
  845. toggleLeftbarButton.style.backgroundColor = '#573699';
  846. } else {
  847. toggleLeftbarButton.innerText = 'User Liste zeigen';
  848. toggleLeftbarButton.style.backgroundColor = '#FF0000';
  849. }
  850. }
  851. };
  852.  
  853.  
  854. // Initialize the leftbar visibility state on load
  855. document.addEventListener('DOMContentLoaded', () => {
  856. sessionStorage.removeItem('aac-wallpaperChanged');
  857. sessionStorage.removeItem('aac-chatAreaButtonState');
  858. const leftbar = document.querySelector('#leftbar');
  859. const chatBox = document.querySelector('#chatverlaufbox');
  860. const toggleLeftbarButton = document.querySelector('#aac-toggleLeftbarButton');
  861. if (leftbar) {
  862. const leftbarVisible = localStorage.getItem('aac-leftbarVisibility') !== 'hidden';
  863. leftbar.style.transition = 'visibility 0s, opacity 0.5s linear';
  864. leftbar.style.opacity = leftbarVisible ? '1' : '0';
  865. leftbar.style.visibility = leftbarVisible ? 'visible' : 'hidden';
  866. leftbar.style.pointerEvents = leftbarVisible ? 'auto' : 'none';
  867. if (chatBox) {
  868. chatBox.style.left = leftbarVisible ? '250px' : '0'; // Ensure consistent positioning for chatbox
  869. }
  870. if (toggleLeftbarButton) {
  871. toggleLeftbarButton.innerText = leftbarVisible ? 'User Liste verstecken' : 'User Liste zeigen';
  872. toggleLeftbarButton.style.backgroundColor = leftbarVisible ? '#573699' : '#FF0000';
  873. }
  874. }
  875. window.onbeforeunload = () => {}; // Disable confirmation popup when reloading the page
  876. });
  877.  
  878. /************************************
  879. *
  880. *
  881. * PNG "Sticker" Modul
  882. *
  883. *
  884. ************************************/
  885. const PngManager = {
  886. init: function() {
  887. $(document).ready(() => {
  888. this.loadSavedPngs();
  889. });
  890. },
  891.  
  892. loadSavedPngs: function() {
  893. const savedPngs = JSON.parse(localStorage.getItem('aac-savedPngs') || '[]');
  894. savedPngs.forEach((png, index) => {
  895. if (png.active) {
  896. this.createPngElement(png.url, index);
  897. }
  898. });
  899. },
  900.  
  901. createPngMenu: function() {
  902. const existingMenu = document.querySelector('#aac-pngMenu');
  903. if (existingMenu) {
  904. existingMenu.remove();
  905. return;
  906. }
  907.  
  908. const menu = document.createElement('div');
  909. menu.id = 'aac-pngMenu';
  910. menu.style.position = 'fixed';
  911. menu.style.top = '50%';
  912. menu.style.left = '50%';
  913. menu.style.transform = 'translate(-50%, -50%)';
  914. menu.style.zIndex = '10001';
  915. menu.style.padding = '20px';
  916. menu.style.backgroundColor = '#fff';
  917. menu.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
  918. menu.style.borderRadius = '10px';
  919. menu.style.textAlign = 'center';
  920.  
  921. const title = document.createElement('h3');
  922. title.innerText = 'PNG Management';
  923. title.style.marginBottom = '20px';
  924. menu.appendChild(title);
  925.  
  926. // Toggle Handles Button
  927. const toggleHandlesButton = document.createElement('button');
  928. toggleHandlesButton.innerText = 'Toggle Handles';
  929. toggleHandlesButton.style.padding = '10px';
  930. toggleHandlesButton.style.backgroundColor = '#573699';
  931. toggleHandlesButton.style.color = '#fff';
  932. toggleHandlesButton.style.border = 'none';
  933. toggleHandlesButton.style.borderRadius = '5px';
  934. toggleHandlesButton.style.cursor = 'pointer';
  935. toggleHandlesButton.style.marginBottom = '10px';
  936.  
  937. toggleHandlesButton.addEventListener('click', () => {
  938. const handles = document.querySelectorAll('.aac-png-handle');
  939. const areHandlesVisible = handles[0].style.display !== 'none';
  940.  
  941. handles.forEach(handle => {
  942. handle.style.display = areHandlesVisible ? 'none' : 'block';
  943. });
  944.  
  945. // Save state to localStorage
  946. localStorage.setItem('aac-png-handles-visible', !areHandlesVisible);
  947. });
  948.  
  949. menu.appendChild(toggleHandlesButton);
  950.  
  951. const savedPngs = JSON.parse(localStorage.getItem('aac-savedPngs') || '[]');
  952. savedPngs.forEach((png, index) => {
  953. const pngContainer = document.createElement('div');
  954. pngContainer.style.display = 'flex';
  955. pngContainer.style.alignItems = 'center';
  956. pngContainer.style.marginBottom = '10px';
  957.  
  958. const thumbnail = document.createElement('img');
  959. thumbnail.src = png.url;
  960. thumbnail.alt = png.name || `PNG ${index + 1}`;
  961. thumbnail.style.width = '50px';
  962. thumbnail.style.height = '50px';
  963. thumbnail.style.objectFit = 'cover';
  964. thumbnail.style.borderRadius = '5px';
  965. thumbnail.style.marginRight = '10px';
  966.  
  967. const activateButton = document.createElement('button');
  968. activateButton.innerText = png.active ? 'Ausblenden' : 'Einblenden';
  969. activateButton.style.padding = '10px';
  970. activateButton.style.marginRight = '5px';
  971. activateButton.style.backgroundColor = png.active ? '#FF0000' : '#573699';
  972. activateButton.style.color = '#fff';
  973. activateButton.style.border = 'none';
  974. activateButton.style.borderRadius = '5px';
  975. activateButton.style.cursor = 'pointer';
  976.  
  977. activateButton.addEventListener('click', () => {
  978. png.active = !png.active;
  979. savedPngs[index] = png;
  980. localStorage.setItem('aac-savedPngs', JSON.stringify(savedPngs));
  981. if (png.active) {
  982. this.createPngElement(png.url, index);
  983. } else {
  984. const existingPng = document.querySelector(`#aac-png-${index}`);
  985. if (existingPng) existingPng.remove();
  986. }
  987. activateButton.innerText = png.active ? 'Ausblenden' : 'Einblenden';
  988. activateButton.style.backgroundColor = png.active ? '#FF0000' : '#573699';
  989. });
  990.  
  991. const deleteButton = document.createElement('button');
  992. deleteButton.innerText = '✕';
  993. deleteButton.style.marginLeft = '5px';
  994. deleteButton.style.padding = '2px';
  995. deleteButton.style.width = '20px';
  996. deleteButton.style.height = '20px';
  997. deleteButton.style.backgroundColor = '#FF0000';
  998. deleteButton.style.color = '#fff';
  999. deleteButton.style.border = 'none';
  1000. deleteButton.style.borderRadius = '3px';
  1001. deleteButton.style.cursor = 'pointer';
  1002.  
  1003. deleteButton.addEventListener('click', () => {
  1004. savedPngs.splice(index, 1);
  1005. localStorage.setItem('aac-savedPngs', JSON.stringify(savedPngs));
  1006.  
  1007. // Remove the entire container (including handles)
  1008. const existingPngContainer = document.querySelector(`#aac-png-container-${index}`);
  1009. if (existingPngContainer) existingPngContainer.remove();
  1010.  
  1011. menu.remove();
  1012. this.createPngMenu();
  1013. });
  1014.  
  1015. pngContainer.appendChild(thumbnail);
  1016. pngContainer.appendChild(activateButton);
  1017. pngContainer.appendChild(deleteButton);
  1018. menu.appendChild(pngContainer);
  1019. });
  1020.  
  1021. const fileInput = document.createElement('input');
  1022. fileInput.type = 'file';
  1023. fileInput.accept = 'image/png';
  1024. fileInput.style.marginBottom = '10px';
  1025. fileInput.style.display = 'block';
  1026.  
  1027. fileInput.addEventListener('change', (event) => {
  1028. const file = event.target.files[0];
  1029. if (file) {
  1030. if (file.size > 2560 * 2560) {
  1031. alert('Die PNG-Datei ist zu groß. Bitte wählen Sie eine Datei, die kleiner als 2,5 MB ist.');
  1032. return;
  1033. }
  1034.  
  1035. const reader = new FileReader();
  1036. reader.onload = (e) => {
  1037. const pngUrl = e.target.result;
  1038. let savedPngs = JSON.parse(localStorage.getItem('aac-savedPngs') || '[]');
  1039. if (savedPngs.length >= 5) {
  1040. alert('Du kannst maximal 5 PNG-Dateien speichern. Bitte lösche eine, bevor du eine neue hinzufügst.');
  1041. return;
  1042. }
  1043. savedPngs.push({ url: pngUrl, name: file.name, active: false });
  1044. localStorage.setItem('aac-savedPngs', JSON.stringify(savedPngs));
  1045. menu.remove();
  1046. this.createPngMenu();
  1047. };
  1048. reader.readAsDataURL(file);
  1049. }
  1050. });
  1051.  
  1052. const closeButton = document.createElement('button');
  1053. closeButton.innerText = 'Schließen';
  1054. closeButton.style.marginTop = '20px';
  1055. closeButton.style.padding = '10px';
  1056. closeButton.style.backgroundColor = '#ccc';
  1057. closeButton.style.color = '#000';
  1058. closeButton.style.border = 'none';
  1059. closeButton.style.borderRadius = '5px';
  1060. closeButton.style.cursor = 'pointer';
  1061.  
  1062. closeButton.addEventListener('click', () => {
  1063. menu.remove();
  1064. });
  1065.  
  1066. menu.appendChild(fileInput);
  1067. menu.appendChild(closeButton);
  1068.  
  1069. document.body.appendChild(menu);
  1070. },
  1071.  
  1072. createResizeHandle: function() {
  1073. const resizeHandle = document.createElement('div');
  1074. resizeHandle.classList.add('aac-png-handle');
  1075. resizeHandle.style.width = '15px';
  1076. resizeHandle.style.height = '15px';
  1077. resizeHandle.style.backgroundColor = '#573699'; // Purple for resizing
  1078. resizeHandle.style.border = '2px solid #fff';
  1079. resizeHandle.style.position = 'absolute';
  1080. resizeHandle.style.right = '0';
  1081. resizeHandle.style.bottom = '0';
  1082. resizeHandle.style.cursor = 'se-resize';
  1083. resizeHandle.title = 'Resize'; // Tooltip
  1084. return resizeHandle;
  1085. },
  1086.  
  1087. createRotateHandle: function() {
  1088. const rotateHandle = document.createElement('div');
  1089. rotateHandle.classList.add('aac-png-handle');
  1090. rotateHandle.style.width = '15px';
  1091. rotateHandle.style.height = '15px';
  1092. rotateHandle.style.backgroundColor = '#FF0000'; // Red for rotating
  1093. rotateHandle.style.border = '2px solid #fff';
  1094. rotateHandle.style.position = 'absolute';
  1095. rotateHandle.style.top = '0';
  1096. rotateHandle.style.right = '0';
  1097. rotateHandle.style.cursor = 'grab';
  1098. rotateHandle.title = 'Rotate'; // Tooltip
  1099. return rotateHandle;
  1100. },
  1101.  
  1102. createPngElement: function(url, index) {
  1103. const pngElement = document.createElement('img');
  1104. pngElement.src = url;
  1105. pngElement.id = `aac-png-${index}`;
  1106.  
  1107. // Set initial styles for the PNG
  1108. pngElement.style.position = 'absolute'; // Position relative to the container
  1109. pngElement.style.width = '150px';
  1110. pngElement.style.height = '150px';
  1111. pngElement.style.cursor = 'move';
  1112. pngElement.style.transformOrigin = 'center center'; // For rotation
  1113.  
  1114. // Container for the PNG and handles
  1115. const pngContainer = document.createElement('div');
  1116. pngContainer.id = `aac-png-container-${index}`; // Unique ID for the container
  1117. pngContainer.style.position = 'fixed';
  1118. pngContainer.style.top = '100px';
  1119. pngContainer.style.left = '100px';
  1120. pngContainer.style.zIndex = '10002';
  1121. pngContainer.style.pointerEvents = 'auto';
  1122.  
  1123. // Append the PNG to the container
  1124. pngContainer.appendChild(pngElement);
  1125.  
  1126. // Create and append handles
  1127. const resizeHandle = this.createResizeHandle();
  1128. const rotateHandle = this.createRotateHandle();
  1129. pngContainer.appendChild(resizeHandle);
  1130. pngContainer.appendChild(rotateHandle);
  1131.  
  1132. // Append the container to the body
  1133. document.body.appendChild(pngContainer);
  1134.  
  1135. let isDragging = false;
  1136. let isResizing = false;
  1137. let isRotating = false;
  1138. let startX, startY, initialLeft, initialTop, initialWidth, initialHeight, initialAngle;
  1139.  
  1140. // Dragging logic
  1141. pngContainer.addEventListener('mousedown', (e) => {
  1142. if (e.target === resizeHandle || e.target === rotateHandle) {
  1143. return; // Ignore mousedown on handles for dragging
  1144. }
  1145.  
  1146. e.preventDefault();
  1147. isDragging = true;
  1148. startX = e.clientX;
  1149. startY = e.clientY;
  1150. initialLeft = parseInt(pngContainer.style.left, 10) || 0;
  1151. initialTop = parseInt(pngContainer.style.top, 10) || 0;
  1152. pngElement.style.opacity = '0.6';
  1153. });
  1154.  
  1155. // Resizing logic
  1156. resizeHandle.addEventListener('mousedown', (e) => {
  1157. e.preventDefault();
  1158. isResizing = true;
  1159. startX = e.clientX;
  1160. startY = e.clientY;
  1161. initialWidth = pngElement.offsetWidth;
  1162. initialHeight = pngElement.offsetHeight;
  1163. });
  1164.  
  1165. // Rotating logic
  1166. rotateHandle.addEventListener('mousedown', (e) => {
  1167. e.preventDefault();
  1168. isRotating = true;
  1169. startX = e.clientX;
  1170. startY = e.clientY;
  1171. const transform = pngElement.style.transform;
  1172. initialAngle = transform ? parseFloat(transform.replace('rotate(', '').replace('deg)', '')) : 0;
  1173. });
  1174.  
  1175. document.addEventListener('mousemove', (e) => {
  1176. if (isDragging) {
  1177. const deltaX = e.clientX - startX;
  1178. const deltaY = e.clientY - startY;
  1179. const newLeft = initialLeft + deltaX;
  1180. const newTop = initialTop + deltaY;
  1181.  
  1182. // Update container position
  1183. pngContainer.style.left = `${newLeft}px`;
  1184. pngContainer.style.top = `${newTop}px`;
  1185. }
  1186.  
  1187. if (isResizing) {
  1188. const deltaX = e.clientX - startX;
  1189. const deltaY = e.clientY - startY;
  1190. const newWidth = initialWidth + deltaX;
  1191. const newHeight = initialHeight + deltaY;
  1192.  
  1193. // Ensure minimum size
  1194. if (newWidth > 50 && newHeight > 50) {
  1195. pngElement.style.width = `${newWidth}px`;
  1196. pngElement.style.height = `${newHeight}px`;
  1197. }
  1198. }
  1199.  
  1200. if (isRotating) {
  1201. const deltaX = e.clientX - startX;
  1202. const deltaY = e.clientY - startY;
  1203. const angle = initialAngle + Math.atan2(deltaY, deltaX) * (180 / Math.PI);
  1204. pngElement.style.transform = `rotate(${angle}deg)`;
  1205. }
  1206. });
  1207.  
  1208. document.addEventListener('mouseup', () => {
  1209. isDragging = false;
  1210. isResizing = false;
  1211. isRotating = false;
  1212. pngElement.style.opacity = '1';
  1213. });
  1214. }
  1215. };
  1216.  
  1217. // Load handle visibility state on page load
  1218. document.addEventListener('DOMContentLoaded', () => {
  1219. const handlesVisible = localStorage.getItem('aac-png-handles-visible') === 'true';
  1220. const handles = document.querySelectorAll('.aac-png-handle');
  1221. handles.forEach(handle => {
  1222. handle.style.display = handlesVisible ? 'block' : 'none';
  1223. });
  1224. });
  1225.  
  1226.  
  1227. /************************************
  1228. *
  1229. *
  1230. * Wallpaper Modul
  1231. *
  1232. *
  1233. ************************************/
  1234.  
  1235.  
  1236. const WallpaperChanger = {
  1237. init: function() {
  1238. this.applySavedBackground();
  1239. },
  1240.  
  1241. applySavedBackground: function() {
  1242. const savedImageUrl = localStorage.getItem('aac-customBackgroundUrl');
  1243. if (savedImageUrl) {
  1244. this.applyCustomBackground(savedImageUrl);
  1245. } else {
  1246. this.resetToDefaultBackground();
  1247. }
  1248. },
  1249.  
  1250. applyCustomBackground: function(url) {
  1251. if (url) {
  1252. document.documentElement.style.cssText = `
  1253. background-image: url("${url}") !important;
  1254. background-color: #000 !important;
  1255. background-position: center center !important;
  1256. background-attachment: fixed !important;
  1257. background-size: cover !important;
  1258. background-repeat: no-repeat !important;
  1259. `;
  1260. document.body.style.cssText = `
  1261. background-image: url("${url}") !important;
  1262. background-color: #000 !important;
  1263. background-position: center center !important;
  1264. background-attachment: fixed !important;
  1265. background-size: cover !important;
  1266. background-repeat: no-repeat !important;
  1267. `;
  1268. }
  1269. },
  1270.  
  1271. resetToDefaultBackground: function() {
  1272. document.documentElement.style.cssText = '';
  1273. document.body.style.cssText = '';
  1274. localStorage.removeItem('aac-customBackgroundUrl');
  1275. },
  1276.  
  1277. createWallpaperChangerButton: function() {
  1278. const button = document.createElement('button');
  1279. button.innerText = 'Hintergrund ändern'; // Change Wallpaper -> Hintergrund ändern
  1280. button.style.padding = '10px';
  1281. button.style.backgroundColor = '#573699';
  1282. button.style.color = '#fff';
  1283. button.style.border = 'none';
  1284. button.style.borderRadius = '5px';
  1285. button.style.cursor = 'pointer';
  1286. button.style.marginBottom = '10px';
  1287.  
  1288. button.addEventListener('click', () => {
  1289. WallpaperChanger.createInputMenu();
  1290. const menu = document.querySelector('#aac-placeholderMenu');
  1291. if (menu) menu.remove();
  1292. });
  1293.  
  1294. return button;
  1295. },
  1296.  
  1297. createSavedWallpapersButton: function() {
  1298. const button = document.createElement('button');
  1299. button.innerText = 'Gespeicherte Hintergründe'; // Saved Wallpapers -> Gespeicherte Hintergründe
  1300. button.style.padding = '10px';
  1301. button.style.backgroundColor = '#573699';
  1302. button.style.color = '#fff';
  1303. button.style.border = 'none';
  1304. button.style.borderRadius = '5px';
  1305. button.style.cursor = 'pointer';
  1306. button.style.marginBottom = '10px';
  1307.  
  1308. button.addEventListener('click', () => {
  1309. WallpaperChanger.createSavedWallpapersMenu();
  1310. });
  1311.  
  1312. return button;
  1313. },
  1314.  
  1315. createSavedWallpapersMenu: function() {
  1316. const menu = document.createElement('div');
  1317. menu.style.position = 'fixed';
  1318. menu.style.top = '50%';
  1319. menu.style.left = '50%';
  1320. menu.style.transform = 'translate(-50%, -50%)';
  1321. menu.style.zIndex = '10001';
  1322. menu.style.padding = '20px';
  1323. menu.style.backgroundColor = '#fff';
  1324. menu.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
  1325. menu.style.borderRadius = '10px';
  1326. menu.style.textAlign = 'center';
  1327.  
  1328. const title = document.createElement('h3');
  1329. title.innerText = 'Gespeicherte Hintergründe';
  1330. title.style.marginBottom = '20px';
  1331. menu.appendChild(title);
  1332.  
  1333. const savedWallpapers = JSON.parse(localStorage.getItem('aac-savedWallpapers') || '[]');
  1334. savedWallpapers.forEach((wallpaper, index) => {
  1335. const wallpaperContainer = document.createElement('div');
  1336. wallpaperContainer.style.display = 'flex';
  1337. wallpaperContainer.style.alignItems = 'center';
  1338. wallpaperContainer.style.marginBottom = '10px';
  1339.  
  1340. const thumbnail = document.createElement('img');
  1341. thumbnail.src = wallpaper.url;
  1342. thumbnail.alt = wallpaper.name || `Wallpaper ${index + 1}`;
  1343. thumbnail.style.width = '50px';
  1344. thumbnail.style.height = '50px';
  1345. thumbnail.style.objectFit = 'cover';
  1346. thumbnail.style.borderRadius = '5px';
  1347. thumbnail.style.marginRight = '10px';
  1348.  
  1349. const wallpaperButton = document.createElement('button');
  1350. wallpaperButton.innerText = wallpaper.name || `Wallpaper ${index + 1}`;
  1351. wallpaperButton.style.padding = '10px';
  1352. wallpaperButton.style.backgroundColor = '#573699';
  1353. wallpaperButton.style.color = '#fff';
  1354. wallpaperButton.style.border = 'none';
  1355. wallpaperButton.style.borderRadius = '5px';
  1356. wallpaperButton.style.cursor = 'pointer';
  1357. wallpaperButton.style.marginRight = '5px';
  1358. wallpaperButton.addEventListener('click', () => {
  1359. WallpaperChanger.applyCustomBackground(wallpaper.url);
  1360. localStorage.setItem('aac-customBackgroundUrl', wallpaper.url); // Save the applied wallpaper URL
  1361. });
  1362.  
  1363. const deleteButton = document.createElement('button');
  1364. deleteButton.innerText = '✕';
  1365. deleteButton.style.marginLeft = '5px';
  1366. deleteButton.style.padding = '2px';
  1367. deleteButton.style.width = '20px';
  1368. deleteButton.style.height = '20px';
  1369. deleteButton.style.backgroundColor = '#FF0000';
  1370. deleteButton.style.color = '#fff';
  1371. deleteButton.style.border = 'none';
  1372. deleteButton.style.borderRadius = '3px';
  1373. deleteButton.style.cursor = 'pointer';
  1374. deleteButton.addEventListener('click', () => {
  1375. savedPngs.splice(index, 1);
  1376. localStorage.setItem('aac-savedPngs', JSON.stringify(savedPngs));
  1377.  
  1378. // Remove the entire container (including handles)
  1379. const existingPngContainer = document.querySelector(`#aac-png-container-${index}`);
  1380. if (existingPngContainer) existingPngContainer.remove();
  1381.  
  1382. menu.remove();
  1383. this.createPngMenu();
  1384. });
  1385.  
  1386. wallpaperContainer.appendChild(thumbnail);
  1387. wallpaperContainer.appendChild(wallpaperButton);
  1388. wallpaperContainer.appendChild(deleteButton);
  1389. menu.appendChild(wallpaperContainer);
  1390. });
  1391.  
  1392. // Add a button to reset to the default wallpaper
  1393. const resetButton = document.createElement('button');
  1394. resetButton.innerText = 'Standardhintergrund wiederherstellen'; // Reset to Default Background
  1395. resetButton.style.marginTop = '20px';
  1396. resetButton.style.padding = '10px';
  1397. resetButton.style.backgroundColor = '#ccc';
  1398. resetButton.style.color = '#000';
  1399. resetButton.style.border = 'none';
  1400. resetButton.style.borderRadius = '5px';
  1401. resetButton.style.cursor = 'pointer';
  1402. resetButton.addEventListener('click', () => {
  1403. WallpaperChanger.resetToDefaultBackground();
  1404. menu.remove();
  1405. });
  1406.  
  1407. menu.appendChild(resetButton);
  1408.  
  1409. const closeButton = document.createElement('button');
  1410. closeButton.innerText = 'Schließen';
  1411. closeButton.style.marginTop = '10px';
  1412. closeButton.style.padding = '10px';
  1413. closeButton.style.backgroundColor = '#ccc';
  1414. closeButton.style.color = '#000';
  1415. closeButton.style.border = 'none';
  1416. closeButton.style.borderRadius = '5px';
  1417. closeButton.style.cursor = 'pointer';
  1418. closeButton.addEventListener('click', () => {
  1419. menu.remove();
  1420. });
  1421.  
  1422. menu.appendChild(closeButton);
  1423. document.body.appendChild(menu);
  1424. },
  1425.  
  1426. createInputMenu: function() {
  1427. const menu = document.createElement('div');
  1428. menu.style.position = 'fixed';
  1429. menu.style.top = '50%';
  1430. menu.style.left = '50%';
  1431. menu.style.transform = 'translate(-50%, -50%)';
  1432. menu.style.zIndex = '10001';
  1433. menu.style.padding = '20px';
  1434. menu.style.backgroundColor = '#fff';
  1435. menu.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
  1436. menu.style.borderRadius = '10px';
  1437. menu.style.textAlign = 'center';
  1438.  
  1439. const input = document.createElement('input');
  1440. input.type = 'text';
  1441. input.placeholder = 'Bild-URL hier eingeben...'; // Enter image URL here...
  1442. input.value = localStorage.getItem('aac-customBackgroundUrl') || '';
  1443. input.style.width = '300px';
  1444. input.style.padding = '10px';
  1445. input.style.marginBottom = '10px';
  1446. input.style.border = '1px solid #ccc';
  1447. input.style.borderRadius = '5px';
  1448.  
  1449. const nameInput = document.createElement('input');
  1450. nameInput.type = 'text';
  1451. nameInput.placeholder = 'Geben Sie den Namen des Hintergrunds ein...'; // Enter wallpaper name...
  1452. nameInput.style.width = '300px';
  1453. nameInput.style.padding = '10px';
  1454. nameInput.style.marginBottom = '10px';
  1455. nameInput.style.border = '1px solid #ccc';
  1456. nameInput.style.borderRadius = '5px';
  1457.  
  1458. const fileInput = document.createElement('input');
  1459. fileInput.type = 'file';
  1460. fileInput.accept = 'image/*';
  1461. fileInput.style.marginBottom = '10px';
  1462.  
  1463. fileInput.addEventListener('change', (event) => {
  1464. const file = event.target.files[0];
  1465. if (file) {
  1466. // Check if file size exceeds 1 MB (1 MB = 1024 * 1024 bytes)
  1467. if (file.size > 2.5 * 1024 * 1024) {
  1468. alert('Die Bilddatei ist zu groß. Bitte wählen Sie eine Datei, die kleiner als 2,5 MB ist.');
  1469. return;
  1470. }
  1471.  
  1472. const reader = new FileReader();
  1473. reader.onload = (e) => {
  1474. const imageUrl = e.target.result;
  1475. input.value = imageUrl;
  1476. };
  1477. reader.readAsDataURL(file);
  1478. }
  1479. });
  1480.  
  1481. const applyButton = document.createElement('button');
  1482. applyButton.innerText = 'Hintergrund anwenden'; // Apply Background -> Hintergrund anwenden
  1483. applyButton.style.marginLeft = '10px';
  1484. applyButton.style.padding = '10px';
  1485. applyButton.style.backgroundColor = '#573699';
  1486. applyButton.style.color = '#fff';
  1487. applyButton.style.border = 'none';
  1488. applyButton.style.borderRadius = '5px';
  1489. applyButton.style.cursor = 'pointer';
  1490.  
  1491. applyButton.addEventListener('click', () => {
  1492. const imageUrl = input.value.trim();
  1493. const wallpaperName = nameInput.value.trim();
  1494. if (imageUrl && wallpaperName) {
  1495. localStorage.setItem('aac-customBackgroundUrl', imageUrl);
  1496. WallpaperChanger.applyCustomBackground(imageUrl);
  1497. sessionStorage.setItem('aac-wallpaperChanged', 'true');
  1498. menu.remove();
  1499. } else {
  1500. alert('Bitte geben Sie eine gültige URL und einen Namen ein.'); // Please enter a valid URL and name.
  1501. }
  1502. });
  1503.  
  1504. const saveButton = document.createElement('button');
  1505. saveButton.innerText = 'Hintergrund speichern'; // Save Wallpaper -> Hintergrund speichern
  1506. saveButton.style.marginTop = '10px';
  1507. saveButton.style.padding = '10px';
  1508. saveButton.style.backgroundColor = '#573699';
  1509. saveButton.style.color = '#fff';
  1510. saveButton.style.border = 'none';
  1511. saveButton.style.borderRadius = '5px';
  1512. saveButton.style.cursor = 'pointer';
  1513.  
  1514. saveButton.addEventListener('click', () => {
  1515. const imageUrl = input.value.trim();
  1516. const wallpaperName = nameInput.value.trim();
  1517. if (imageUrl && wallpaperName) {
  1518. let savedWallpapers = JSON.parse(localStorage.getItem('aac-savedWallpapers') || '[]');
  1519. if (savedWallpapers.length >= 5) {
  1520. alert('Du kannst maximal 5 Hintergründe speichern. Bitte lösche einen, bevor du einen neuen hinzufügst.'); // You can save a maximum of 5 wallpapers. Please delete one before adding a new one.
  1521. return;
  1522. }
  1523. WallpaperChanger.saveWallpaper(imageUrl, wallpaperName);
  1524. alert('Hintergrund erfolgreich gespeichert.'); // Wallpaper saved successfully.
  1525. } else {
  1526. alert('Bitte geben Sie eine gültige URL und einen Namen ein.'); // Please enter a valid URL and name.
  1527. }
  1528. });
  1529.  
  1530. const closeButton = document.createElement('button');
  1531. closeButton.innerText = 'Schließen'; // Close -> Schließen
  1532. closeButton.style.marginTop = '10px';
  1533. closeButton.style.padding = '10px';
  1534. closeButton.style.backgroundColor = '#ccc';
  1535. closeButton.style.color = '#000';
  1536. closeButton.style.border = 'none';
  1537. closeButton.style.borderRadius = '5px';
  1538. closeButton.style.cursor = 'pointer';
  1539.  
  1540. closeButton.addEventListener('click', () => {
  1541. menu.remove();
  1542. });
  1543.  
  1544. menu.appendChild(input);
  1545. menu.appendChild(document.createElement('br'));
  1546. menu.appendChild(nameInput);
  1547. menu.appendChild(document.createElement('br'));
  1548. menu.appendChild(fileInput);
  1549. menu.appendChild(applyButton);
  1550. menu.appendChild(document.createElement('br'));
  1551. menu.appendChild(saveButton);
  1552. menu.appendChild(document.createElement('br'));
  1553. menu.appendChild(closeButton);
  1554.  
  1555. document.body.appendChild(menu);
  1556. },
  1557.  
  1558. saveWallpaper: function(url, name) {
  1559. let savedWallpapers = JSON.parse(localStorage.getItem('aac-savedWallpapers') || '[]');
  1560. if (!savedWallpapers.some(wallpaper => wallpaper.url === url)) {
  1561. savedWallpapers.push({ url, name });
  1562. localStorage.setItem('aac-savedWallpapers', JSON.stringify(savedWallpapers));
  1563. }
  1564. },
  1565.  
  1566. deleteWallpaper: function(index) {
  1567. let savedWallpapers = JSON.parse(localStorage.getItem('aac-savedWallpapers') || '[]');
  1568. savedWallpapers.splice(index, 1);
  1569. localStorage.setItem('aac-savedWallpapers', JSON.stringify(savedWallpapers));
  1570. }
  1571. };
  1572.  
  1573. /************************************
  1574. *
  1575. *
  1576. * Chat Area Modul
  1577. *
  1578. *
  1579. ************************************/
  1580.  
  1581.  
  1582. const ChatArea = {
  1583. isDraggable: false,
  1584.  
  1585. init: function() {
  1586. this.chatArea = document.querySelector('#graphicChatArea');
  1587. if (this.chatArea) {
  1588. this.centerChatArea();
  1589. this.chatArea.style.position = 'absolute';
  1590.  
  1591. const savedState = localStorage.getItem('aac-isChatAreaDraggable');
  1592. if (savedState === 'true') {
  1593. this.enableDraggable();
  1594. }
  1595. }
  1596. },
  1597.  
  1598. centerChatArea: function() {
  1599. if (this.chatArea) {
  1600. const windowWidth = window.innerWidth;
  1601. const windowHeight = window.innerHeight;
  1602. const chatWidth = this.chatArea.offsetWidth;
  1603. const chatHeight = this.chatArea.offsetHeight;
  1604.  
  1605. this.chatArea.style.left = `${(windowWidth - chatWidth) / 2}px`;
  1606. this.chatArea.style.top = `${(windowHeight - chatHeight) / 2}px`;
  1607. }
  1608. },
  1609.  
  1610. resetChatAreaPosition: function() {
  1611. this.centerChatArea();
  1612. console.log('[AAC Enhanced] Chat area position reset to center.');
  1613. },
  1614.  
  1615. toggleDraggable: function() {
  1616. if (!this.chatArea) return;
  1617.  
  1618. if (this.isDraggable) {
  1619. this.disableDraggable();
  1620. } else {
  1621. this.enableDraggable();
  1622. }
  1623.  
  1624. this.updateToggleButton();
  1625. },
  1626.  
  1627. enableDraggable: function() {
  1628. this.isDraggable = true;
  1629. localStorage.setItem('aac-isChatAreaDraggable', 'true');
  1630. sessionStorage.setItem('aac-chatAreaButtonState', 'true');
  1631.  
  1632. $(this.chatArea).draggable({
  1633. containment: 'document',
  1634. start: function() {
  1635. ChatArea.chatArea.classList.add('ui-draggable-enabled');
  1636. },
  1637. stop: function() {
  1638. ChatArea.chatArea.classList.remove('ui-draggable-enabled');
  1639. }
  1640. });
  1641.  
  1642. console.log('[AAC Enhanced] Chat area is now draggable.');
  1643. },
  1644.  
  1645. disableDraggable: function() {
  1646. this.isDraggable = false;
  1647. localStorage.setItem('aac-isChatAreaDraggable', 'false');
  1648.  
  1649. if ($(this.chatArea).draggable('instance')) {
  1650. $(this.chatArea).draggable('destroy');
  1651. }
  1652.  
  1653. console.log('[AAC Enhanced] Draggable functionality has been disabled.');
  1654. },
  1655.  
  1656. updateToggleButton: function() {
  1657. const toggleButton = document.querySelector('#aac-toggleChatAreaButton');
  1658. if (toggleButton) {
  1659. if (this.isDraggable) {
  1660. toggleButton.innerText = 'Verschiebbare Chat-Bereich deaktivieren'; // Disable Draggable Chat Area
  1661. toggleButton.style.backgroundColor = '#FF0000';
  1662. } else {
  1663. toggleButton.innerText = 'Verschiebbare Chat-Bereich aktivieren'; // Enable Draggable Chat Area
  1664. toggleButton.style.backgroundColor = '#573699';
  1665. }
  1666. }
  1667. },
  1668.  
  1669. createToggleChatAreaButton: function() {
  1670. const button = document.createElement('button');
  1671. button.id = 'aac-toggleChatAreaButton';
  1672. button.style.padding = '10px';
  1673. button.style.color = '#fff';
  1674. button.style.border = 'none';
  1675. button.style.borderRadius = '5px';
  1676. button.style.cursor = 'pointer';
  1677. button.style.marginBottom = '10px';
  1678.  
  1679. button.addEventListener('click', () => ChatArea.toggleDraggable());
  1680.  
  1681. return button;
  1682. }
  1683. };
  1684.  
  1685. /************************************
  1686. *
  1687. *
  1688. *Reply Modul & Image in Chat Modul
  1689. *
  1690. *
  1691. ************************************/
  1692.  
  1693.  
  1694.  
  1695. const messageDiv = document.getElementsByClassName('chatverlauf')[0];
  1696.  
  1697. const messageObserverConfig = { childList: true };
  1698.  
  1699. const messagesMutationCallback = function (mutationsList) {
  1700. for (const mutation of mutationsList) {
  1701. // Reply-Message
  1702. if (mutation.addedNodes.length) {
  1703. const message = mutation.addedNodes[0];
  1704. const chatMessageDiv = message.querySelectorAll(`[ng-bind-html^='chatmsg.msg']`)[0];
  1705. const messageContent = chatMessageDiv.textContent;
  1706. const isSystemMessage = message.classList.contains('systemMsg');
  1707. const isEmojiMessage = message.querySelectorAll(`[ng-if='chatmsg.emojiImage']`).length > 0;
  1708. const isOwnMessage = messageContent.startsWith(': ');
  1709.  
  1710. message.style.paddingLeft = '1px';
  1711.  
  1712. if (isEmojiMessage) {
  1713. continue;
  1714. }
  1715.  
  1716. if (isSystemMessage) {
  1717. continue;
  1718. }
  1719.  
  1720. message.classList.add('chatMessage');
  1721.  
  1722. const messageIconsDiv = document.createElement('div');
  1723.  
  1724. messageIconsDiv.classList.add('messageIcons');
  1725. messageIconsDiv.innerHTML =
  1726. '<svg aria-hidden="true" class="svg-inline--fa fa-quote-right fa-w-16" focusable="false" data-prefix="fa" data-icon="quote-right" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" data-fa-i2svg=""><path fill="#d2d2d2" d="M464 32H336c-26.5 0-48 21.5-48 48v128c0 26.5 21.5 48 48 48h80v64c0 35.3-28.7 64-64 64h-8c-13.3 0-24 10.7-24 24v48c0 13.3 10.7 24 24 24h8c88.4 0 160-71.6 160-160V80c0-26.5-21.5-48-48-48zm-288 0H48C21.5 32 0 53.5 0 80v128c0 26.5 21.5 48 48 48h80v64c0 35.3-28.7 64-64 64h-8c-13.3 0-24 10.7-24 24v48c0 13.3 10.7 24 24 24h8c88.4 0 160-71.6 160-160V80c0-26.5-21.5-48-48-48z"></path></svg>';
  1727.  
  1728. message.appendChild(messageIconsDiv);
  1729.  
  1730. message.addEventListener('mouseover', function () {
  1731. const iconDiv = this.children[this.children.length - 1];
  1732. iconDiv.style.display = 'flex';
  1733. });
  1734.  
  1735. message.addEventListener('mouseout', function () {
  1736. const iconDiv = this.children[this.children.length - 1];
  1737. iconDiv.style.display = 'none';
  1738. });
  1739.  
  1740. messageIconsDiv.addEventListener('click', function () {
  1741. const scope = angular.element(document.getElementById('topbar')).scope();
  1742. const messageInput = document.getElementById('chatline');
  1743.  
  1744. let username = '';
  1745.  
  1746. if (!isOwnMessage) {
  1747. username = chatMessageDiv.previousElementSibling.textContent.slice(0, -1);
  1748. } else {
  1749. username = chatMessageDiv.previousElementSibling.textContent;
  1750. }
  1751.  
  1752. if (scope.chatline === undefined) {
  1753. scope.chatline = '';
  1754. }
  1755.  
  1756. if (!isOwnMessage) {
  1757. scope.chatline += ` @${username} "${messageContent}" `;
  1758. } else {
  1759. scope.chatline += ` @${username} "${messageContent.split(': ')[1]}" `;
  1760. }
  1761.  
  1762. messageInput.focus();
  1763. });
  1764.  
  1765. /************************************
  1766. *
  1767. *
  1768. *View Images in chat Modul
  1769. *
  1770. *
  1771. ************************************/
  1772.  
  1773. const imageUrlRegex = new RegExp(`(http)?s?:?(\/\/[^"']*\.(?:png|jpg|jpeg|gif|svg))`);
  1774. const isImageUrl = messageContent.match(imageUrlRegex);
  1775.  
  1776. if (isImageUrl) {
  1777. const imageUrl = isImageUrl[0];
  1778. const imageDiv = document.createElement('div');
  1779. const imageElement = document.createElement('img');
  1780.  
  1781. imageDiv.setAttribute('class', 'message-image-container');
  1782. imageElement.setAttribute('class', 'message-image');
  1783.  
  1784. imageElement.src = imageUrl;
  1785.  
  1786. imageDiv.appendChild(imageElement);
  1787.  
  1788. chatMessageDiv.appendChild(imageDiv);
  1789. }
  1790. }
  1791. }
  1792. };
  1793.  
  1794. const messagesObserver = new MutationObserver(messagesMutationCallback);
  1795.  
  1796. messagesObserver.observe(messageDiv, messageObserverConfig);
  1797.  
  1798.  
  1799. /************************************
  1800. *
  1801. *
  1802. * Avatar-Collections Modul
  1803. *
  1804. *
  1805. ************************************/
  1806.  
  1807. if (!localStorage.hasOwnProperty('collections')) {
  1808. localStorage.setItem('collections', JSON.stringify({ 'Keine Kategorie': { isDefault: true } }));
  1809. }
  1810. if (!localStorage.hasOwnProperty('avatars')) {
  1811. localStorage.setItem('avatars', JSON.stringify({}));
  1812. }
  1813.  
  1814. window.addEventListener('globalSocketReady', () => {
  1815. window.socket.on('getAvatarcase', (data) => {
  1816. const avatars = data.avatars;
  1817.  
  1818. const storageAvatars = JSON.parse(localStorage.getItem('avatars'));
  1819.  
  1820. for (const avatar of avatars) {
  1821. if (!storageAvatars[avatar.avatarid]) {
  1822. storageAvatars[avatar.avatarid] = {
  1823. id: avatar.avatarid,
  1824. img: avatar.img,
  1825. imgthumb: avatar.imgthumb,
  1826. collection: 'Keine Kategorie',
  1827. };
  1828. }
  1829. }
  1830.  
  1831. localStorage.setItem('avatars', JSON.stringify(storageAvatars));
  1832. });
  1833. window.socket.emit('getAvatarcase');
  1834. });
  1835.  
  1836. window.addEventListener('socketEmit', (event) => {
  1837. if (event.detail.eventName === 'deleteAvatar') {
  1838. // Remove Avatar properly in case it gets deleted
  1839. const avatarID = event.detail.args[0].avatarid;
  1840. const avatarDiv = document.querySelectorAll(`[id^='${avatarID}']`)[0];
  1841. avatarDiv.remove();
  1842.  
  1843. const storageAvatars = JSON.parse(localStorage.getItem('avatars'));
  1844.  
  1845. delete storageAvatars[avatarID];
  1846.  
  1847. localStorage.setItem('avatars', JSON.stringify(storageAvatars));
  1848. }
  1849. });
  1850.  
  1851. window.addAvatarToCollection = function (avatarID, collection) {
  1852. const collections = JSON.parse(localStorage.getItem('collections'));
  1853. if (!collections[collection]) {
  1854. console.log(`Collection '${collection}' doesn't exist!`);
  1855. return false;
  1856. }
  1857. const storageAvatars = JSON.parse(localStorage.getItem('avatars'));
  1858.  
  1859. if (!storageAvatars[avatarID]) {
  1860. console.log(`Avatar '${avatarID}' doesn't exist!`);
  1861. return false;
  1862. }
  1863.  
  1864. storageAvatars[avatarID].collection = collection;
  1865. localStorage.setItem('avatars', JSON.stringify(storageAvatars));
  1866. return true;
  1867. };
  1868.  
  1869. window.removeAvatarFromCollection = function (avatarID) {
  1870. const storageAvatars = JSON.parse(localStorage.getItem('avatars'));
  1871. if (!storageAvatars[avatarID]) {
  1872. console.log(`Avatar '${avatarID}' doesn't exist!`);
  1873. return false;
  1874. }
  1875. storageAvatars[avatarID].collection = 'Keine Kategorie';
  1876. localStorage.setItem('avatars', JSON.stringify(storageAvatars));
  1877. return true;
  1878. };
  1879.  
  1880. window.addCollection = function (collection) {
  1881. const collections = JSON.parse(localStorage.getItem('collections'));
  1882. if (collections[collection]) {
  1883. console.log(`Collection ${collection} already exists!`);
  1884. return false;
  1885. }
  1886. collections[collection] = {
  1887. isDefault: false,
  1888. };
  1889.  
  1890. localStorage.setItem('collections', JSON.stringify(collections));
  1891. return true;
  1892. };
  1893.  
  1894. window.deleteCollection = function (collection) {
  1895. const collections = JSON.parse(localStorage.getItem('collections'));
  1896. if (!collections[collection]) {
  1897. console.log(`Collection '${collection}' doesn't exist!`);
  1898. return false;
  1899. }
  1900. if (collections[collection].isDefault) {
  1901. console.log(`Default collections such as '${collection}' must not be deleted!`);
  1902. return false;
  1903. }
  1904.  
  1905. const storageAvatars = JSON.parse(localStorage.getItem('avatars'));
  1906.  
  1907. for (const avatar in storageAvatars) {
  1908. if (storageAvatars[avatar].collection === collection) {
  1909. storageAvatars[avatar].collection = 'Keine Kategorie';
  1910. }
  1911. }
  1912.  
  1913. localStorage.setItem('avatars', JSON.stringify(storageAvatars));
  1914.  
  1915. delete collections[collection];
  1916.  
  1917. localStorage.setItem('collections', JSON.stringify(collections));
  1918. return true;
  1919. };
  1920.  
  1921. // View
  1922.  
  1923. const avatarcaseObserverConfig = { childList: true };
  1924. const avatarcaseDiv = document.querySelector('[ng-hide="disconnectedClient"]').children[1];
  1925.  
  1926. const avatarcaseMutationCallback = function (mutationsList) {
  1927. for (const mutation of mutationsList) {
  1928. for (const node of mutation.addedNodes) {
  1929. if (node.id === 'avatarcase') {
  1930. const acSelfAvatarElements = document.querySelectorAll('[ng-repeat="ownedAva in ownedAvas track by $index"]');
  1931.  
  1932. setTimeout(() => {
  1933. for (const element of acSelfAvatarElements) {
  1934. element.remove();
  1935. }
  1936. }, 1200);
  1937.  
  1938. const collections = JSON.parse(localStorage.getItem('collections'));
  1939. const avatarCaseDiv = document.getElementsByClassName('avatarcase_main')[0];
  1940.  
  1941. for (const collection in collections) {
  1942. const collapsibleWrap = createCategoryElement(collection, node, avatarCaseDiv);
  1943. avatarCaseDiv.append(collapsibleWrap);
  1944. }
  1945.  
  1946. const newCollectionFormDiv = document.createElement('div');
  1947. const newCollectionInput = document.createElement('input');
  1948. const newCollectionSubmitButton = document.createElement('button');
  1949.  
  1950. newCollectionSubmitButton.textContent = 'Neue Kategorie erstellen';
  1951. newCollectionSubmitButton.id = 'createCollectionBtn';
  1952. newCollectionInput.id = 'createCollectionInput';
  1953.  
  1954. newCollectionFormDiv.appendChild(newCollectionInput);
  1955. newCollectionFormDiv.appendChild(newCollectionSubmitButton);
  1956.  
  1957. newCollectionSubmitButton.addEventListener('click', function () {
  1958. const input = document.getElementById('createCollectionInput');
  1959. if (input.value !== '') {
  1960. const successfullyAdded = window.addCollection(input.value);
  1961.  
  1962. if (successfullyAdded) {
  1963. const collapsbileWrap = createCategoryElement(input.value, node);
  1964. avatarCaseDiv.insertBefore(collapsbileWrap, newCollectionFormDiv);
  1965. }
  1966. }
  1967. });
  1968.  
  1969. avatarCaseDiv.appendChild(newCollectionFormDiv);
  1970. }
  1971. }
  1972. }
  1973. };
  1974.  
  1975. function createCategoryElement(collection, node) {
  1976. const scope = angular.element(document.getElementById('topbar')).scope();
  1977.  
  1978. const collapsibleWrap = document.createElement('div');
  1979. const collapsibleButton = document.createElement('button');
  1980. const collapsibleContent = document.createElement('div');
  1981. const avatarGrid = document.createElement('div');
  1982. const removeCategoryButton = document.createElement('button');
  1983.  
  1984. collapsibleWrap.setAttribute('class', 'collapsible-wrap');
  1985. collapsibleWrap.setAttribute('id', collection);
  1986. collapsibleButton.setAttribute('class', 'collapsible-button');
  1987. collapsibleContent.setAttribute('class', 'collapsible-content');
  1988. removeCategoryButton.setAttribute('class', 'removeCategory-btn');
  1989. avatarGrid.setAttribute('class', 'avatar-grid');
  1990. collapsibleButton.textContent = collection;
  1991. removeCategoryButton.textContent = 'Kategorie löschen';
  1992.  
  1993. removeCategoryButton.addEventListener('click', function () {
  1994. window.selectedCollection = this.parentElement.parentElement.id;
  1995.  
  1996. const requestDeleteCollectionDiv = document.createElement('div');
  1997. const cancelRequestBtn = document.createElement('button');
  1998. const acceptRequestBtn = document.createElement('button');
  1999.  
  2000. requestDeleteCollectionDiv.setAttribute('class', 'request-delete-collection');
  2001.  
  2002. requestDeleteCollectionDiv.textContent = `Kategorie "${window.selectedCollection}" wirklich löschen?`;
  2003. acceptRequestBtn.textContent = 'löschen';
  2004. cancelRequestBtn.textContent = 'abbrechen';
  2005.  
  2006. acceptRequestBtn.addEventListener('click', function () {
  2007. const deletionSuccessfull = window.deleteCollection(window.selectedCollection);
  2008.  
  2009. if (deletionSuccessfull) {
  2010. const defaultCategory = document.getElementById('Keine Kategorie');
  2011. const selecetedCategory = document.getElementById(window.selectedCollection);
  2012.  
  2013. for (const avatar of selecetedCategory.children[1].children[0].children) {
  2014. avatar.children[avatar.childElementCount - 1].remove();
  2015. createAddToCollectionIcon(avatar, node);
  2016. defaultCategory.children[1].children[0].appendChild(avatar);
  2017. }
  2018.  
  2019. selecetedCategory.remove();
  2020. }
  2021. this.parentElement.remove();
  2022. });
  2023.  
  2024. cancelRequestBtn.addEventListener('click', function () {
  2025. this.parentElement.remove();
  2026. });
  2027.  
  2028. requestDeleteCollectionDiv.appendChild(cancelRequestBtn);
  2029. requestDeleteCollectionDiv.appendChild(acceptRequestBtn);
  2030.  
  2031. node.appendChild(requestDeleteCollectionDiv);
  2032. });
  2033.  
  2034. collapsibleContent.appendChild(avatarGrid);
  2035.  
  2036. if (collection !== 'Keine Kategorie') {
  2037. collapsibleContent.appendChild(removeCategoryButton);
  2038. }
  2039.  
  2040. collapsibleWrap.appendChild(collapsibleButton);
  2041. collapsibleWrap.appendChild(collapsibleContent);
  2042.  
  2043. const avatars = JSON.parse(localStorage.getItem('avatars'));
  2044.  
  2045. for (const avatar in avatars) {
  2046. if (avatars[avatar].collection === collection) {
  2047. const avatarDiv = document.createElement('div');
  2048. const deleteIconDiv = document.createElement('div');
  2049. const selectIconDiv = document.createElement('div');
  2050.  
  2051. avatarDiv.id = `${avatars[avatar].id}-${avatars[avatar].img}`;
  2052. avatarDiv.setAttribute('class', 'avatar-div');
  2053. avatarDiv.style.backgroundImage = `url('/img/publicimg/skinthumbnails/${avatars[avatar].imgthumb}')`;
  2054.  
  2055. deleteIconDiv.setAttribute('class', 'avatarIcon');
  2056. selectIconDiv.setAttribute('class', 'avatarIcon');
  2057.  
  2058. deleteIconDiv.innerHTML =
  2059. '<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><title>Trash</title><path d="M296 64h-80a7.91 7.91 0 00-8 8v24h96V72a7.91 7.91 0 00-8-8z" fill="none"/><path d="M432 96h-96V72a40 40 0 00-40-40h-80a40 40 0 00-40 40v24H80a16 16 0 000 32h17l19 304.92c1.42 26.85 22 47.08 48 47.08h184c26.13 0 46.3-19.78 48-47l19-305h17a16 16 0 000-32zM192.57 416H192a16 16 0 01-16-15.43l-8-224a16 16 0 1132-1.14l8 224A16 16 0 01192.57 416zM272 400a16 16 0 01-32 0V176a16 16 0 0132 0zm32-304h-96V72a7.91 7.91 0 018-8h80a7.91 7.91 0 018 8zm32 304.57A16 16 0 01320 416h-.58A16 16 0 01304 399.43l8-224a16 16 0 1132 1.14z"/></svg>';
  2060.  
  2061. selectIconDiv.innerHTML =
  2062. '<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><title>Shirt</title><path d="M256 96c33.08 0 60.71-25.78 64-58 .3-3-3-6-6-6a13 13 0 00-4.74.9c-.2.08-21.1 8.1-53.26 8.1s-53.1-8-53.26-8.1a16.21 16.21 0 00-5.3-.9h-.06a5.69 5.69 0 00-5.38 6c3.35 32.16 31 58 64 58z"/><path d="M485.29 89.9L356 44.64a4 4 0 00-5.27 3.16 96 96 0 01-189.38 0 4 4 0 00-5.35-3.16L26.71 89.9A16 16 0 0016.28 108l16.63 88a16 16 0 0013.92 12.9l48.88 5.52a8 8 0 017.1 8.19l-7.33 240.9a16 16 0 009.1 14.94A17.49 17.49 0 00112 480h288a17.49 17.49 0 007.42-1.55 16 16 0 009.1-14.94l-7.33-240.9a8 8 0 017.1-8.19l48.88-5.52a16 16 0 0013.92-12.9l16.63-88a16 16 0 00-10.43-18.1z"/></svg>';
  2063.  
  2064. avatarDiv.appendChild(deleteIconDiv);
  2065. avatarDiv.appendChild(selectIconDiv);
  2066.  
  2067. selectIconDiv.addEventListener('click', function () {
  2068. scope.changeAvatarTexture(this.parentElement.id.split('-')[1]);
  2069. });
  2070.  
  2071. deleteIconDiv.addEventListener('click', function () {
  2072. scope.requestDeleteAvatar({ avatarid: this.parentElement.id.split('-')[0] });
  2073. });
  2074.  
  2075. if (collection === 'Keine Kategorie') {
  2076. createAddToCollectionIcon(avatarDiv, node);
  2077. } else {
  2078. createRemoveFromCollectionIcon(avatarDiv, node);
  2079. }
  2080.  
  2081. avatarDiv.addEventListener('mouseover', function () {
  2082. for (const child of this.children) {
  2083. child.style.display = 'flex';
  2084. }
  2085. });
  2086.  
  2087. avatarDiv.addEventListener('mouseout', function () {
  2088. for (const child of this.children) {
  2089. child.style.display = 'none';
  2090. }
  2091. });
  2092.  
  2093. avatarGrid.appendChild(avatarDiv);
  2094. }
  2095. }
  2096.  
  2097. collapsibleButton.addEventListener('click', function () {
  2098. this.classList.toggle('active');
  2099. const content = this.nextElementSibling;
  2100.  
  2101. if (content.style.maxHeight) {
  2102. content.style.maxHeight = null;
  2103. } else {
  2104. content.style.maxHeight = content.scrollHeight + 'px';
  2105. }
  2106. });
  2107.  
  2108. return collapsibleWrap;
  2109. }
  2110.  
  2111. function createAddToCollectionIcon(avatarDiv, node) {
  2112. const addToCollectionIcon = document.createElement('div');
  2113.  
  2114. addToCollectionIcon.setAttribute('class', 'avatarIcon');
  2115. addToCollectionIcon.innerHTML =
  2116. '<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><title>Bag Add</title><path d="M454.66 169.4A31.86 31.86 0 00432 160h-64v-16a112 112 0 00-224 0v16H80a32 32 0 00-32 32v216c0 39 33 72 72 72h272a72.22 72.22 0 0050.48-20.55 69.48 69.48 0 0021.52-50.2V192a31.78 31.78 0 00-9.34-22.6zM320 336h-48v48a16 16 0 01-32 0v-48h-48a16 16 0 010-32h48v-48a16 16 0 0132 0v48h48a16 16 0 010 32zm16-176H176v-16a80 80 0 01160 0z"/></svg>';
  2117.  
  2118. addToCollectionIcon.addEventListener('click', function () {
  2119. const collections = JSON.parse(localStorage.getItem('collections'));
  2120. window.selectedAvatar = this.parentElement.id.split('-')[0];
  2121.  
  2122. const collectionSelection = document.createElement('div');
  2123. const cancelSelectionBtn = document.createElement('button');
  2124.  
  2125. cancelSelectionBtn.textContent = 'abbrechen';
  2126.  
  2127. collectionSelection.setAttribute('class', 'collection-selection');
  2128. for (const collection in collections) {
  2129. const collectionSelectable = document.createElement('div');
  2130. collectionSelectable.setAttribute('class', 'collection-selectable');
  2131. collectionSelectable.textContent = collection;
  2132. collectionSelection.appendChild(collectionSelectable);
  2133. collectionSelectable.addEventListener('click', function () {
  2134. window.addAvatarToCollection(window.selectedAvatar, this.textContent);
  2135. const avatarDiv = document.querySelectorAll(`[id^='${window.selectedAvatar}']`)[0];
  2136. const newCollectionDiv = document.getElementById(this.textContent).children[1].children[0];
  2137.  
  2138. avatarDiv.children[avatarDiv.childElementCount - 1].remove();
  2139.  
  2140. createRemoveFromCollectionIcon(avatarDiv, node);
  2141.  
  2142. for (const child of avatarDiv.children) {
  2143. child.style.display = 'none';
  2144. }
  2145.  
  2146. newCollectionDiv.appendChild(avatarDiv);
  2147.  
  2148. this.parentElement.remove();
  2149. });
  2150. }
  2151.  
  2152. cancelSelectionBtn.addEventListener('click', function () {
  2153. this.parentElement.remove();
  2154. });
  2155.  
  2156. collectionSelection.appendChild(cancelSelectionBtn);
  2157. node.appendChild(collectionSelection);
  2158. });
  2159.  
  2160. avatarDiv.appendChild(addToCollectionIcon);
  2161. }
  2162.  
  2163. function createRemoveFromCollectionIcon(avatarDiv, node) {
  2164. const removeFromCollectionIcon = document.createElement('div');
  2165. removeFromCollectionIcon.setAttribute('class', 'avatarIcon');
  2166. removeFromCollectionIcon.innerHTML =
  2167. '<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><title>Bag Remove</title><path d="M454.66 169.4A31.86 31.86 0 00432 160h-64v-16a112 112 0 00-224 0v16H80a32 32 0 00-32 32v216c0 39 33 72 72 72h272a72.22 72.22 0 0050.48-20.55 69.48 69.48 0 0021.52-50.2V192a31.78 31.78 0 00-9.34-22.6zM320 336H192a16 16 0 010-32h128a16 16 0 010 32zm16-176H176v-16a80 80 0 01160 0z"/></svg>';
  2168.  
  2169. removeFromCollectionIcon.addEventListener('click', function () {
  2170. const avatarDiv = this.parentElement;
  2171. const newCollectionDiv = document.getElementById('Keine Kategorie').children[1].children[0];
  2172.  
  2173. avatarDiv.children[avatarDiv.childElementCount - 1].remove();
  2174.  
  2175. createAddToCollectionIcon(avatarDiv, node);
  2176.  
  2177. for (const child of avatarDiv.children) {
  2178. child.style.display = 'none';
  2179. }
  2180.  
  2181. window.removeAvatarFromCollection(avatarDiv.id.split('-')[0]);
  2182. newCollectionDiv.appendChild(avatarDiv);
  2183. });
  2184.  
  2185. avatarDiv.appendChild(removeFromCollectionIcon);
  2186. }
  2187.  
  2188. const avatarcaseObserver = new MutationObserver(avatarcaseMutationCallback);
  2189.  
  2190. avatarcaseObserver.observe(avatarcaseDiv, avatarcaseObserverConfig);
  2191.  
  2192. /************************************
  2193. *
  2194. *
  2195. * Marktplatz Popup Modul
  2196. *
  2197. *
  2198. ************************************/
  2199.  
  2200. function createMarktplatzPanel() {
  2201. // Create Marktplatz Panel box
  2202. const panelBox = document.createElement('div');
  2203. panelBox.id = 'marktplatzPanel';
  2204. panelBox.style.position = 'fixed';
  2205. panelBox.style.width = '600px';
  2206. panelBox.style.height = '700px';
  2207. panelBox.style.backgroundColor = '#1e1e1e';
  2208. panelBox.style.color = '#f0f0f0';
  2209. panelBox.style.border = '1px solid #333';
  2210. panelBox.style.borderRadius = '5px';
  2211. panelBox.style.zIndex = '10000';
  2212. panelBox.style.overflow = 'auto';
  2213. panelBox.style.resize = 'both';
  2214. panelBox.style.cursor = 'default';
  2215. panelBox.style.userSelect = 'text';
  2216.  
  2217. // Center the popup on the screen
  2218. panelBox.style.left = '50%';
  2219. panelBox.style.top = '50%';
  2220. panelBox.style.transform = 'translate(-50%, -50%)';
  2221.  
  2222. // Create a title bar for the panel box
  2223. const titleBar = document.createElement('div');
  2224. titleBar.style.cursor = 'move';
  2225. titleBar.style.padding = '5px';
  2226. titleBar.style.backgroundColor = '#333';
  2227. titleBar.style.color = '#f0f0f0';
  2228. titleBar.style.fontWeight = 'bold';
  2229. titleBar.innerHTML = 'Marktplatz Panel (Drag Title Bar to Move)';
  2230. titleBar.style.display = 'flex';
  2231. titleBar.style.justifyContent = 'space-between';
  2232. titleBar.style.alignItems = 'center';
  2233.  
  2234. // Create a close button
  2235. const closeButton = document.createElement('button');
  2236. closeButton.innerText = '×';
  2237. closeButton.style.background = '#ff4d4d';
  2238. closeButton.style.color = '#fff';
  2239. closeButton.style.border = 'none';
  2240. closeButton.style.fontSize = '16px';
  2241. closeButton.style.width = '25px';
  2242. closeButton.style.height = '25px';
  2243. closeButton.style.borderRadius = '50%';
  2244. closeButton.style.cursor = 'pointer';
  2245. closeButton.style.textAlign = 'center';
  2246. closeButton.style.lineHeight = '25px';
  2247. closeButton.style.fontWeight = 'bold';
  2248. closeButton.style.marginRight = '5px';
  2249. closeButton.addEventListener('click', function (event) {
  2250. event.stopPropagation(); // Prevent drag triggering when clicking close
  2251. panelBox.remove();
  2252. });
  2253.  
  2254. // Append close button and title bar
  2255. titleBar.appendChild(closeButton);
  2256. panelBox.appendChild(titleBar);
  2257.  
  2258. // Create a container to load Marktplatz content
  2259. const contentContainer = document.createElement('div');
  2260. contentContainer.style.width = '100%';
  2261. contentContainer.style.height = 'calc(100% - 30px)'; // Adjusting height for the title bar
  2262. contentContainer.style.overflow = 'auto';
  2263. contentContainer.style.background = '#282828';
  2264. contentContainer.style.padding = '5px';
  2265. panelBox.appendChild(contentContainer);
  2266.  
  2267. // Load the Marktplatz page inside an iframe to ensure full functionality
  2268. const iframe = document.createElement('iframe');
  2269. iframe.src = 'https://anime.academy/marktplatz';
  2270. iframe.style.width = '100%';
  2271. iframe.style.height = '100%';
  2272. iframe.style.border = 'none';
  2273. iframe.style.background = '#1e1e1e';
  2274. contentContainer.appendChild(iframe);
  2275.  
  2276. // Append the panel box to the document body
  2277. document.body.appendChild(panelBox);
  2278.  
  2279. // Drag functionality for the title bar only
  2280. let isDragging = false;
  2281. let offsetX, offsetY;
  2282.  
  2283. titleBar.addEventListener('mousedown', function (e) {
  2284. isDragging = true;
  2285. offsetX = e.clientX - panelBox.getBoundingClientRect().left;
  2286. offsetY = e.clientY - panelBox.getBoundingClientRect().top;
  2287. });
  2288.  
  2289. document.addEventListener('mousemove', function (e) {
  2290. if (isDragging) {
  2291. panelBox.style.left = `${e.clientX - offsetX}px`;
  2292. panelBox.style.top = `${e.clientY - offsetY}px`;
  2293. panelBox.style.transform = 'none';
  2294. }
  2295. });
  2296.  
  2297. document.addEventListener('mouseup', function () {
  2298. isDragging = false;
  2299. });
  2300. }
  2301.  
  2302. function attachMarktplatzListener() {
  2303. document.addEventListener('click', function (event) {
  2304. const target = event.target.closest('a[href="/marktplatz"]');
  2305. if (target) {
  2306. event.preventDefault(); // Prevent default opening in new tab
  2307. if (!document.getElementById('marktplatzPanel')) {
  2308. createMarktplatzPanel();
  2309. }
  2310. }
  2311. });
  2312. }
  2313.  
  2314. attachMarktplatzListener();
  2315.  
  2316. /************************************
  2317. *
  2318. *
  2319. * Chat Logger Module
  2320. *
  2321. *
  2322. ************************************/
  2323. const ChatLogger = {
  2324. CHAT_LOG_STORAGE_KEY: "aac_chat_logs",
  2325. MAX_LOG_SIZE: 500,
  2326.  
  2327. init: function () {
  2328. console.log("[Chat Logger] Initializing...");
  2329. this.setupSocketHook();
  2330. this.createDownloadButton();
  2331. },
  2332.  
  2333. setupSocketHook: function () {
  2334. if (!window.socket) {
  2335. console.warn("[Chat Logger] No socket detected. Hooking into system...");
  2336.  
  2337. io.Socket.prototype.o_emit = io.Socket.prototype.o_emit || io.Socket.prototype.emit;
  2338. io.Socket.prototype.emit = function (eventName, ...args) {
  2339. if (!window.socket) {
  2340. window.socket = this;
  2341. window.dispatchEvent(new Event('globalSocketReady'));
  2342. }
  2343. return this.o_emit(eventName, ...args);
  2344. };
  2345. }
  2346.  
  2347. let attempt = 0;
  2348. let checkInterval = setInterval(() => {
  2349. attempt++;
  2350.  
  2351. if (window.socket) {
  2352. console.log("[Chat Logger] Socket found, starting logger...");
  2353. clearInterval(checkInterval);
  2354. ChatLogger.listenForChatMessages();
  2355. return;
  2356. }
  2357.  
  2358. console.log(`[Chat Logger] Waiting for socket... (Attempt ${attempt})`);
  2359.  
  2360. if (attempt > 20) {
  2361. clearInterval(checkInterval);
  2362. console.error("[Chat Logger] Failed to detect socket.");
  2363. }
  2364. }, 1000);
  2365. },
  2366.  
  2367. listenForChatMessages: function () {
  2368. if (!window.socket) {
  2369. console.error("[Chat Logger] No socket connection found! Retrying...");
  2370. setTimeout(ChatLogger.listenForChatMessages, 1000);
  2371. return;
  2372. }
  2373.  
  2374. console.log("[Chat Logger] Attaching listener for chat messages...");
  2375.  
  2376. window.socket.on('updateChatLines', (data) => {
  2377. console.log("[Chat Logger] Received chat message event:", data);
  2378.  
  2379. if (!data || !data.chatLine) {
  2380. console.warn("[Chat Logger] Empty or undefined message received.");
  2381. return;
  2382. }
  2383.  
  2384. const currentRoom = window.location.href.split('=')[1];
  2385. ChatLogger.saveChatMessage(currentRoom, data.user, data.chatLine);
  2386. });
  2387. },
  2388.  
  2389. saveChatMessage: function (room, user, message) {
  2390. if (!message || message.trim() === "") return;
  2391.  
  2392. let chatLogs = JSON.parse(localStorage.getItem(ChatLogger.CHAT_LOG_STORAGE_KEY)) || {};
  2393.  
  2394. if (!chatLogs[room]) {
  2395. chatLogs[room] = [];
  2396. }
  2397.  
  2398. chatLogs[room].push({
  2399. timestamp: Date.now(),
  2400. user: user,
  2401. message: message
  2402. });
  2403.  
  2404. if (chatLogs[room].length > ChatLogger.MAX_LOG_SIZE) {
  2405. chatLogs[room].shift();
  2406. }
  2407.  
  2408. localStorage.setItem(ChatLogger.CHAT_LOG_STORAGE_KEY, JSON.stringify(chatLogs));
  2409. console.log(`[Chat Logger] Stored message: ${user}: ${message}`);
  2410. },
  2411.  
  2412. exportChatLogs: function () {
  2413. let chatLogs = JSON.parse(localStorage.getItem(ChatLogger.CHAT_LOG_STORAGE_KEY)) || {};
  2414.  
  2415. if (Object.keys(chatLogs).length === 0) {
  2416. alert("No chat logs available.");
  2417. console.warn("[Chat Logger] No logs found.");
  2418. return;
  2419. }
  2420.  
  2421. let logText = "";
  2422. for (let room in chatLogs) {
  2423. logText += `Room: ${room}\n====================\n`;
  2424. logText += chatLogs[room].map(log =>
  2425. `[${new Date(log.timestamp).toLocaleString()}] ${log.user}: ${log.message}`
  2426. ).join("\n") + "\n\n";
  2427. }
  2428.  
  2429. let now = new Date();
  2430. let formattedTime = now.toISOString().replace(/T/, '_').replace(/:/g, '-').split('.')[0];
  2431. let fileName = `chatlog_${formattedTime}.txt`;
  2432.  
  2433. let blob = new Blob([logText], { type: "text/plain" });
  2434. let link = document.createElement("a");
  2435.  
  2436. link.href = URL.createObjectURL(blob);
  2437. link.download = fileName;
  2438. document.body.appendChild(link);
  2439. link.click();
  2440. document.body.removeChild(link);
  2441.  
  2442. console.log(`[Chat Logger] Chat logs downloaded as: ${fileName}`);
  2443. },
  2444.  
  2445. createDownloadButton: function () {
  2446. setTimeout(() => {
  2447. let existingButton = document.getElementById("downloadChatLogsBtn");
  2448. if (existingButton) return;
  2449.  
  2450. let chatControls = document.querySelector(".controller");
  2451. if (!chatControls) {
  2452. console.warn("[Chat Logger] Could not find .controller div!");
  2453. return;
  2454. }
  2455.  
  2456. let button = document.createElement("span");
  2457. button.id = "downloadChatLogsBtn";
  2458. button.innerText = "💾 Logs";
  2459. button.style.padding = "5px 10px";
  2460. button.style.fontSize = "12px";
  2461. button.style.background = "#4CAF50";
  2462. button.style.color = "#fff";
  2463. button.style.borderRadius = "4px";
  2464. button.style.cursor = "pointer";
  2465. button.style.display = "inline-block";
  2466. button.style.marginLeft = "15px";
  2467. button.style.opacity = "0.85";
  2468.  
  2469. button.addEventListener("mouseenter", () => {
  2470. button.style.opacity = "1";
  2471. });
  2472.  
  2473. button.addEventListener("mouseleave", () => {
  2474. button.style.opacity = "0.85";
  2475. });
  2476.  
  2477. button.addEventListener("click", ChatLogger.exportChatLogs);
  2478. chatControls.appendChild(button);
  2479.  
  2480. console.log("[Chat Logger] Button added inside chat controls.");
  2481. }, 2000);
  2482. }
  2483. };
  2484.  
  2485. /************************************************************************************************************************************************************************************
  2486. *
  2487. *
  2488. ************************************************************************************************************************************************************************************
  2489. ************************************************************************************************************************************************************************************
  2490. *
  2491. *
  2492. *
  2493. * Mainpage Stuff
  2494. *
  2495. *
  2496. *
  2497. ************************************************************************************************************************************************************************************
  2498. ************************************************************************************************************************************************************************************
  2499. *
  2500. *
  2501. ************************************************************************************************************************************************************************************/
  2502.  
  2503. /************************************
  2504. *
  2505. *
  2506. * Show All Online Users Module
  2507. *
  2508. *
  2509. ************************************/
  2510.  
  2511.  
  2512.  
  2513. function fixOnlineUsers() {
  2514. console.log("🔍 Looking for .onlineUsers...");
  2515.  
  2516. const onlineUsersContainer = document.querySelector(".onlineUsers");
  2517. if (!onlineUsersContainer) {
  2518. console.warn("⚠️ .onlineUsers not found! Retrying in 1 second...");
  2519. setTimeout(fixOnlineUsers, 1000);
  2520. return;
  2521. }
  2522.  
  2523. console.log("✅ Found .onlineUsers! Removing limit...");
  2524.  
  2525. // Get AngularJS scope
  2526. const scope = angular.element(onlineUsersContainer).scope();
  2527. if (!scope || !scope.usersOnline) {
  2528. console.warn("⚠️ Angular scope not found! Retrying...");
  2529. setTimeout(fixOnlineUsers, 1000);
  2530. return;
  2531. }
  2532.  
  2533. // Remove the limit and force AngularJS to update
  2534. scope.$apply(function () {
  2535. scope.usersOnlineLimit = scope.usersOnline.length; // Set limit to total users
  2536. });
  2537.  
  2538. console.log(`✅ Showing all online users (${scope.usersOnline.length} total)`);
  2539. }
  2540.  
  2541. // Wait until AngularJS is ready
  2542. document.addEventListener("DOMContentLoaded", fixOnlineUsers);
  2543.  
  2544.  
  2545.  
  2546.  
  2547.  
  2548. AACApp.init();
  2549. })();
  2550.  
  2551. /*
  2552.  
  2553. ███████████████████████████████
  2554. ████████████████████████████████████████████
  2555. ████████████████████████████████████████████████████████
  2556. ████████████████████████████████████████████████████████████████
  2557. ████████████████████████████████████████████████████████████████████████
  2558. ███████████████████████████████████████████████████████████████████████████████
  2559. ████████████████████████████████████████████████████████████████████████████████████
  2560. ██████████████████████████████████████████████████████████████████████████████████████████
  2561. ███████████████████████████████████████████████████████████████████████████████████████████████
  2562. ████████████████████████████████████████████████████████████████████████████████████████████████████
  2563. ██████████████████████████████████████████ █████████████████████████████████████████
  2564. ████████████████████████████████████ ███████████████████████████████████
  2565. █████████████████████████████████ █████████████████████████████████
  2566. ██████████████████████████████ ███████████████████████████████
  2567. █████████████████████████████ ████████████████████████████
  2568. ███████████████████████████ ███████████████████████████
  2569. ██████████████████████████ ██████████████████████████
  2570. █████████████████████████ ██████████████████████████
  2571. █████████████████████████ ████████████████████████
  2572. ████████████████████████ ███████████████████████
  2573. ███████████████████████ ██ █████████████ ███████████████████████
  2574. ███████████████████████ ███████ ██████████████████████ ███████████████████████
  2575. ██████████████████████ ███████████ █████████████████████████████ ███████████████████████
  2576. ██████████████████████ ████████████████ ███████████████████████████████████ ██████████████████████
  2577. ██████████████████████ ███████████████████████ ████████████████████████████████████████████ █████████████████████
  2578. █████████████████████ █████████████████████████████████ █████████████████████████████████████████████████████████ █████████████████████
  2579. ████████████████████ ███████████████████████████████████████████████████████████████████████████████████████████████████ █████████████████████
  2580. ████████████████████ █████████████████████████████████████████████████████████████████████████████████████████████████████ █████████████████████
  2581. ████████████████████ █████████████████████████████████████████████████████████████████████████████████████████████████████████ ████████████████████
  2582. ███████████████████ ██████████████████████████████████████████████████████████████████████████████████████████████████████████ ████████████████████
  2583. ████████████████████ ██████████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2584. ███████████████████ ██████████████████████████████████████████████████████████████████████████████████████████████████████████ ████████████████████
  2585. ███████████████████ ██████████████████████████████████████████████████████████████████████████████████████████████████████████ ████████████████████
  2586. ███████████████████ ██████████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2587. ███████████████████ ███████████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2588. ███████████████████ ██████████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2589. ████████████████████ █████████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2590. ███████████████████ ██████████████████████████████████████████████████████████████████████████████████████████████████████████ ██████████████████
  2591. ███████████████████ █████████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2592. ███████████████████ ████████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2593. ███████████████████ ████████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2594. ██████████████████ ██████████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2595. ██████████████████ █ ███████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2596. ██████████████████ ████ ████████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2597. ██████████████████ ██████ █████████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2598. ███████████████████ █████████ █████████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2599. ███████████████████ ███████████ █████████████████████████████████████████████████████████████████████████████████████ ███████████████████
  2600. ███████████████████ █████████████ █████████████████████████████████████ ████████████████████████████ ███████████████████
  2601. ███████████████████ █████████████████ █████████████████████████ ██████████████████ ██████████████████████ ██████████████████
  2602. ███████████████████ ██████████████████████ █████████████████████████ ███████████████████ ███████████████████
  2603. ███████████████████ ████████████████████████████████████████████████████████████████████ ███████████████ ███████████████████
  2604. ███████████████████ ██████████████████████████████████████████████████████████████████ ████████████ ███████████████████
  2605. ███████████████████ ███████████████████████████████████████████████████████████████ ████████ ███████████████████
  2606. ████████████████████ ██████████████████████████████████████████████████████████████ ████████ ████████████████████
  2607. ███████████████████ ██████████████████████████████████████████████████████████ █████████ ████████████████████
  2608. ████████████████████ ███████████████████████████████████████████████████████ ████████ ███████████████████
  2609. ████████████████████ ████████████████████████████████████████████████████ ████████ ████████████████████
  2610. ████████████████████ ███████████████████████████████████████████████ ███████ ████████████████████
  2611. ████████████████████ ████████████████████████████████████████ ████████ █████████████████████
  2612. ████████████████████ █████████████ ████████ █████████████████████
  2613. ██████████████████████ ████████ ████████ █████████████████████
  2614. ██████████████████████ ████████ ████████ █████████████████████
  2615. ██████████████████████ ████████ ███████ █████████████████████
  2616. ██████████████████████ ████████ ████████ ██████████████████████
  2617. ███████████████████████ ███████ ████████ ██████████████████████
  2618. ████████████████████████ ████████ ███████ ███████████████████████
  2619. ████████████████████████ ███████ ████████ ████████████████████████
  2620. ████████████████████████ ███████ ███████ █████████████████████████
  2621. █████████████████████████ ████████ ███████ ██████████████████████████
  2622. ███████████████████████████ ███████ ███████ ██████████████████████████
  2623. ████████████████████████████ ███████ ███████ ████████████████████████████
  2624. █████████████████████████████ ████████ ███████ █████████████████████████████
  2625. ███████████████████████████████ ███████ ██████ ██████████████████████████████
  2626. █████████████████████████████████ ███████ ████ █████████████████████████████████
  2627. ████████████████████████████████████ ████████ ████████████████████████████████████
  2628. ███████████████████████████████████████████ ███████████████████████████████████████████
  2629. ███████████████████████████████████████████████████████████████████████████████████████████████████
  2630. ██████████████████████████████████████████████████████████████████████████████████████████████
  2631. █████████████████████████████████████████████████████████████████████████████████████████
  2632. ████████████████████████████████████████████████████████████████████████████████████
  2633. ██████████████████████████████████████████████████████████████████████████████
  2634. ███████████████████████████████████████████████████████████████████████
  2635. ███████████████████████████████████████████████████████████████
  2636. ██████████████████████████████████████████████████████
  2637. ███████████████████████████████████████████
  2638. ███████████████████████████
  2639.  
  2640.  
  2641. */

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址