AAC Enhanced plus AAC Utils

Adds custom UI enhancements

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

QingJ © 2025

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