Advertisement
Guest User

Untitled

a guest
Jun 27th, 2024
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.09 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Lista
  3. // @namespace Violentmonkey Scripts
  4. // @match https://ylilauta.org/*
  5. // @grant none
  6. // @version 1.4
  7. // @description 6/27/2024, 8:22:20 PM
  8. // ==/UserScript==
  9.  
  10. (function() {
  11. 'use strict';
  12.  
  13. const isCatalogView = window.location.hash.includes('#catalog');
  14. const threadCache = new Map();
  15.  
  16. const boardPath = window.location.pathname.split('/').filter(Boolean)[0];
  17. const isUberboard = boardPath === 'thread';
  18. const listUrl = isUberboard ? `${window.location.origin}/thread/` : `${window.location.origin}/${boardPath}/`;
  19.  
  20. function addCatalogButton() {
  21. const navRight = document.querySelector('nav.right');
  22. if (navRight) {
  23. const button = document.createElement('button');
  24. button.textContent = isCatalogView ? 'Takaisin' : 'Katalogi';
  25. button.classList.add('button-dropdown');
  26. button.style.fontSize = '14px';
  27. button.addEventListener('click', () => {
  28. const url = isCatalogView ? window.location.href.split('#')[0] : `${window.location.origin}${window.location.pathname}#catalog`;
  29. window.location.href = url;
  30. window.location.reload();
  31. });
  32.  
  33. const sortButton = navRight.querySelector('.icon-sort-amount-desc');
  34. if (sortButton) {
  35. sortButton.before(button);
  36. } else {
  37. navRight.prepend(button);
  38. }
  39. }
  40. }
  41.  
  42. async function fetchThreadIDs() {
  43. try {
  44. const response = await fetch(listUrl, { credentials: 'same-origin' });
  45. const text = await response.text();
  46. const doc = new DOMParser().parseFromString(text, 'text/html');
  47. if (isUberboard) {
  48. return Array.from(doc.querySelectorAll('.thread')).map(thread => ({
  49. id: thread.dataset.threadId,
  50. board: thread.querySelector('.board-link').getAttribute('href').split('/')[1]
  51. }));
  52. } else {
  53. return Array.from(doc.querySelectorAll('.thread')).map(thread => thread.dataset.threadId);
  54. }
  55. } catch (error) {
  56. console.error(error);
  57. return [];
  58. }
  59. }
  60.  
  61. async function fetchThreadContent(threadID, board) {
  62. if (threadCache.has(threadID)) {
  63. return threadCache.get(threadID);
  64. }
  65.  
  66. const url = isUberboard ? `${window.location.origin}/${board}/${threadID}` : `${window.location.origin}/${boardPath}/${threadID}`;
  67.  
  68. try {
  69. const response = await fetch(url, { credentials: 'same-origin' });
  70. const text = await response.text();
  71. const doc = new DOMParser().parseFromString(text, 'text/html');
  72. const threadElement = doc.querySelector(`.thread[data-thread-id="${threadID}"]`);
  73.  
  74. if (threadElement) {
  75. const container = threadElement.cloneNode(false);
  76. const firstPost = threadElement.querySelector('.post');
  77. const threadReplies = threadElement.querySelector('.thread-replies');
  78. const replyPosts = threadReplies ? Array.from(threadReplies.querySelectorAll('.post')) : [];
  79. const lastThreeReplies = replyPosts.slice(-3);
  80. const threadMeta = threadElement.querySelector('.thread-meta');
  81.  
  82. if (firstPost) {
  83. const clonedFirstPost = firstPost.cloneNode(true);
  84. addReplyButton(clonedFirstPost, threadID, board);
  85. container.appendChild(clonedFirstPost);
  86. }
  87.  
  88. if (threadReplies) {
  89. const clonedReplies = threadReplies.cloneNode(false);
  90. clonedReplies.style.paddingBottom = '10px';
  91. lastThreeReplies.forEach(reply => {
  92. const clonedReply = reply.cloneNode(true);
  93. clonedReplies.appendChild(clonedReply);
  94. });
  95. container.appendChild(clonedReplies);
  96. }
  97.  
  98. if (threadMeta) {
  99. const clonedThreadMeta = threadMeta.cloneNode(true);
  100. container.appendChild(clonedThreadMeta);
  101. }
  102.  
  103. threadCache.set(threadID, container);
  104. return container;
  105. }
  106. return null;
  107. } catch (error) {
  108. console.error(error);
  109. return null;
  110. }
  111. }
  112.  
  113. function addReplyButton(postElement, threadID, board) {
  114. const postMeta = postElement.querySelector('.post-meta');
  115. if (postMeta) {
  116. const replyButton = document.createElement('button');
  117. replyButton.textContent = 'Vastaa';
  118. replyButton.classList.add('post-button');
  119. replyButton.style.fontSize = '14px';
  120. replyButton.addEventListener('click', () => {
  121. const url = isUberboard ? `${window.location.origin}/${board}/${threadID}` : `${window.location.origin}/${boardPath}/${threadID}`;
  122. window.location.href = url;
  123. });
  124.  
  125. const menuButton = postMeta.querySelector('.icon-menu');
  126. if (menuButton) {
  127. menuButton.after(replyButton);
  128. } else {
  129. postMeta.appendChild(replyButton);
  130. }
  131. }
  132. }
  133.  
  134. async function delay(ms) {
  135. return new Promise(resolve => setTimeout(resolve, ms));
  136. }
  137.  
  138. async function loadMoreThreads(observer) {
  139. const threadContainer = document.querySelector('.style-list');
  140. if (threadContainer) {
  141. const threadIDs = await fetchThreadIDs();
  142. for (const thread of threadIDs) {
  143. const threadContent = isUberboard ? await fetchThreadContent(thread.id, thread.board) : await fetchThreadContent(thread, boardPath);
  144. if (threadContent) {
  145. threadContainer.appendChild(threadContent);
  146. }
  147. await delay(4);
  148. }
  149.  
  150. const threads = document.querySelectorAll('.style-list .thread');
  151. if (threads.length !== 0) {
  152. observer.observe(threads[threads.length - 1]);
  153. }
  154. }
  155. }
  156.  
  157. function debounce(func, wait) {
  158. let timeout;
  159. return function(...args) {
  160. clearTimeout(timeout);
  161. timeout = setTimeout(() => func.apply(this, args), wait);
  162. };
  163. }
  164.  
  165. function initializeInfiniteScroll() {
  166. const observer = new IntersectionObserver(debounce(async (entries, observer) => {
  167. for (const entry of entries) {
  168. if (entry.isIntersecting) {
  169. observer.unobserve(entry.target);
  170. await loadMoreThreads(observer);
  171. }
  172. }
  173. }, 100), {
  174. root: null,
  175. rootMargin: '0px',
  176. threshold: 1.0
  177. });
  178.  
  179. const threads = document.querySelectorAll('.style-list .thread');
  180. if (threads.length !== 0) {
  181. observer.observe(threads[threads.length - 1]);
  182. }
  183. }
  184.  
  185. async function initializeListView() {
  186. const threadContainer = document.querySelector('.threads');
  187. if (threadContainer) {
  188. threadContainer.classList.remove('threads');
  189. threadContainer.classList.add('style-list');
  190. threadContainer.innerHTML = '';
  191.  
  192. const threadIDs = await fetchThreadIDs();
  193. for (const thread of threadIDs) {
  194. const threadContent = isUberboard ? await fetchThreadContent(thread.id, thread.board) : await fetchThreadContent(thread, boardPath);
  195. if (threadContent) {
  196. threadContainer.appendChild(threadContent);
  197. }
  198. await delay(4);
  199. }
  200.  
  201. initializeInfiniteScroll();
  202. }
  203. }
  204.  
  205. if (isCatalogView) {
  206. addCatalogButton();
  207. } else {
  208. addCatalogButton();
  209. initializeListView();
  210. }
  211. })();
  212.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement