Add "Offtopic" discussion list on main page.

Добавляет дополнительный блок с темами с раздела "Оффтоп".

目前为 2024-10-10 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Add "Offtopic" discussion list on main page.
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Добавляет дополнительный блок с темами с раздела "Оффтоп".
  6. // @author Yowori
  7. // @match https://lolz.live/
  8. // @match https://zelenka.guru/
  9. // @match https://lolz.guru/
  10. // @icon https://i.imgur.com/xnJeB3f.png
  11. // @grant none
  12. // @run-at document-end
  13. // @license MIT
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. function addGlobalStyle(css) {
  20. const head = document.getElementsByTagName('head')[0];
  21. if (!head) { return; }
  22. const style = document.createElement('style');
  23. style.type = 'text/css';
  24. style.innerHTML = css;
  25. head.appendChild(style);
  26. }
  27.  
  28. addGlobalStyle(`
  29. .customDiscussionContainer {
  30. display: flex;
  31. gap: 20px;
  32. box-sizing: border-box;
  33. max-width: 1200px;
  34. margin: 0 auto;
  35. flex-wrap: nowrap;
  36. }
  37.  
  38. body.index .discussionList {
  39. max-width: 520px;
  40. flex: 0 0 400px;
  41. }
  42.  
  43. .customDiscussionList {
  44. max-height: 1040px;
  45. max-width: 400px;
  46. flex: 0 0 400px;
  47. height: auto;
  48. overflow: hidden;
  49. }
  50.  
  51. .customDiscussionList .loading,
  52. .customDiscussionList .error {
  53. text-align: center;
  54. padding: 20px;
  55. font-size: 16px;
  56. color: #555;
  57. }
  58.  
  59. .customDiscussionList .ForumViewMoreButton {
  60. display: none;
  61. }
  62.  
  63. .customDiscussionList .discussionListItems {
  64. display: flex;
  65. flex-direction: column;
  66. gap: 10px;
  67. }
  68.  
  69. @media (max-width: 900px) {
  70. .customDiscussionContainer {
  71. flex-direction: column;
  72. align-items: center;
  73. }
  74.  
  75. body.index .discussionList,
  76. .customDiscussionList {
  77. flex: 0 0 90%;
  78. max-width: 90%;
  79. }
  80. }
  81. `);
  82.  
  83. function createNewDiscussionList() {
  84. let container = document.querySelector('.customDiscussionContainer');
  85. if (!container) {
  86. container = document.createElement('div');
  87. container.className = 'customDiscussionContainer';
  88.  
  89. const existingDiscussionList = document.querySelector('body.index .discussionList');
  90. if (existingDiscussionList) {
  91. existingDiscussionList.parentNode.insertBefore(container, existingDiscussionList);
  92. container.appendChild(existingDiscussionList);
  93. } else {
  94. const bodyIndex = document.querySelector('body.index');
  95. if (bodyIndex) {
  96. bodyIndex.appendChild(container);
  97. }
  98. }
  99. }
  100.  
  101.  
  102. const newDiscussionList = document.createElement('div');
  103. newDiscussionList.className = 'discussionList customDiscussionList';
  104. newDiscussionList.innerHTML = `
  105. <div class="aboveThreadList">
  106. <form action="https://zelenka.guru/forums/8/" method="post" class="DiscussionListOptions">
  107. <input type="hidden" name="node_id" value="8">
  108.  
  109. <div class="_universalSearchForm universalSearchForm">
  110. <input name="title" value="" class="SearchInputQuery _universalSearchInput universalSearchInput textCtrl" placeholder="Поиск тем" autocomplete="off">
  111. <i class="inputRelativeIcon fas fa-times" style="display: none;"></i>
  112. </div>
  113.  
  114. <input type="hidden" name="_xfToken" value="2312422,1728548767,49aac0543425624fb3896cd9087e7579a503d4c1">
  115. </form>
  116. </div>
  117.  
  118. <div class="discussionListItems" id="discussionListItems_8">
  119. <div class="loading">Загрузка...</div>
  120. </div>
  121. `;
  122.  
  123. container.appendChild(newDiscussionList);
  124. loadDiscussionList(8, newDiscussionList.querySelector('#discussionListItems_8'), true);
  125. addFilterHandlers(newDiscussionList);
  126.  
  127. const updateButton = document.querySelector('.UpdateFeedButton');
  128. if (updateButton) {
  129. updateButton.addEventListener('click', () => {
  130. const mainDiscussionList = document.querySelector('body.index .discussionList .discussionListItems');
  131. loadDiscussionList(1, mainDiscussionList, false); // Замените 1 на нужный nodeId для основного списка
  132. loadDiscussionList(8, newDiscussionList.querySelector('#discussionListItems_8'), true);
  133. });
  134. }
  135. }
  136.  
  137. function loadDiscussionList(nodeId, container, limit = false) {
  138.  
  139. const xhr = new XMLHttpRequest();
  140. xhr.open('GET', `https://zelenka.guru/forums/${nodeId}/`, true);
  141. xhr.onreadystatechange = function() {
  142. if (xhr.readyState === 4) {
  143. if (xhr.status === 200) {
  144. const parser = new DOMParser();
  145. const doc = parser.parseFromString(xhr.responseText, 'text/html');
  146. const discussionItems = doc.querySelector('.discussionListItems');
  147.  
  148. if (discussionItems) {
  149. let itemsHTML = discussionItems.innerHTML;
  150.  
  151. if (limit) {
  152. const tempDiv = document.createElement('div');
  153. tempDiv.innerHTML = itemsHTML;
  154.  
  155. const topics = tempDiv.querySelectorAll('.discussionListItem');
  156.  
  157. let limitedHTML = '';
  158. for (let i = 0; i < Math.min(10, topics.length); i++) {
  159. limitedHTML += topics[i].outerHTML;
  160. }
  161.  
  162. itemsHTML = limitedHTML;
  163. }
  164.  
  165. container.innerHTML = itemsHTML;
  166.  
  167. } else {
  168. return;
  169. }
  170. } else {
  171. return;
  172. }
  173. }
  174. };
  175. xhr.send();
  176. }
  177.  
  178. function addFilterHandlers(discussionList) {
  179. const form = discussionList.querySelector('.DiscussionListOptions');
  180. if (!form) return;
  181.  
  182. form.addEventListener('submit', function(e) {
  183. e.preventDefault();
  184. const formData = new FormData(form);
  185. const params = new URLSearchParams();
  186.  
  187. for (const pair of formData.entries()) {
  188. params.append(pair[0], pair[1]);
  189. }
  190.  
  191. const nodeId = formData.get('node_id') || 8;
  192.  
  193. loadFilteredDiscussionList(nodeId, params, discussionList.querySelector('.discussionListItems'), true);
  194. });
  195. }
  196.  
  197.  
  198. function loadFilteredDiscussionList(nodeId, params, container, limit = false) {
  199.  
  200. const xhr = new XMLHttpRequest();
  201. xhr.open('POST', `https://zelenka.guru/forums/${nodeId}/`, true);
  202. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  203. xhr.onreadystatechange = function() {
  204. if (xhr.readyState === 4) {
  205. if (xhr.status === 200) {
  206. const parser = new DOMParser();
  207. const doc = parser.parseFromString(xhr.responseText, 'text/html');
  208. const discussionItems = doc.querySelector('.discussionListItems');
  209.  
  210. if (discussionItems) {
  211. let itemsHTML = discussionItems.innerHTML;
  212.  
  213. if (limit) {
  214. const tempDiv = document.createElement('div');
  215. tempDiv.innerHTML = itemsHTML;
  216.  
  217. const topics = tempDiv.querySelectorAll('.discussionListItem');
  218.  
  219. let limitedHTML = '';
  220. for (let i = 0; i < Math.min(10, topics.length); i++) {
  221. limitedHTML += topics[i].outerHTML;
  222. }
  223.  
  224. itemsHTML = limitedHTML;
  225. }
  226.  
  227. container.innerHTML = itemsHTML;
  228. } else {
  229. return;
  230. }
  231. } else {
  232. return;
  233. }
  234. }
  235. };
  236. xhr.send(params.toString());
  237. }
  238.  
  239. function init() {
  240. createNewDiscussionList();
  241. }
  242.  
  243. window.addEventListener('load', function() {
  244. init();
  245. });
  246.  
  247. })();