Guest User

Wykop - losowe położenie plusów

a guest
Oct 15th, 2026
20
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name Wykop - losowe położenie plusów
  3. // @namespace wykop-losowe-plusy
  4. // @version 1.1.0
  5. // @description Umieszcza plusy wpisów i komentarzy w losowych miejscach na mikroblogu, stronach wpisów i profilach użytkowników.
  6. // @match https://wykop.pl/*
  7. // @run-at document-idle
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (() => {
  12. 'use strict';
  13.  
  14. const positions = ['author', 'content', 'actions', 'top-right', 'bottom-right'];
  15. const placements = new WeakMap();
  16. const observerOptions = {
  17. childList: true,
  18. subtree: true,
  19. attributes: true,
  20. attributeFilter: ['class'],
  21. };
  22. let scheduled = false;
  23.  
  24. const style = document.createElement('style');
  25. style.textContent = `
  26. :is(.microblog, .entry-page, .profile-page) .tm-plus-item .tm-random-plus {
  27. display: inline-flex !important;
  28. flex: 0 0 auto !important;
  29. width: max-content !important;
  30. margin: 0 !important;
  31. transform: none !important;
  32. }
  33.  
  34. :is(.microblog, .entry-page, .profile-page) .tm-plus-item .tm-plus-floating {
  35. position: absolute !important;
  36. inset: auto !important;
  37. left: var(--tm-plus-left) !important;
  38. top: var(--tm-plus-top) !important;
  39. z-index: 2;
  40. }
  41.  
  42. :is(.microblog, .entry-page, .profile-page) .tm-plus-header {
  43. position: relative !important;
  44. }
  45.  
  46. :is(.microblog, .entry-page, .profile-page) .tm-plus-author,
  47. :is(.microblog, .entry-page, .profile-page) .tm-plus-actions {
  48. flex-wrap: wrap !important;
  49. height: auto !important;
  50. }
  51.  
  52. :is(.microblog, .entry-page, .profile-page) .tm-plus-actions {
  53. display: flex !important;
  54. }
  55.  
  56. .profile-page .tm-plus-author > .tm-plus-slot {
  57. margin-inline-end: 8px;
  58. vertical-align: middle;
  59. }
  60.  
  61. :is(.microblog, .entry-page, .profile-page) .tm-plus-slot {
  62. display: inline-block;
  63. flex: 0 0 auto;
  64. pointer-events: none;
  65. }
  66.  
  67. :is(.microblog, .entry-page, .profile-page) .tm-plus-item .tm-plus-row {
  68. display: flex !important;
  69. align-items: center;
  70. margin: 8px 0 0 34px;
  71. }
  72.  
  73. :is(.microblog, .entry-page, .profile-page) .tm-plus-slot.tm-plus-top-right,
  74. :is(.microblog, .entry-page, .profile-page) .tm-plus-slot.tm-plus-bottom-right {
  75. margin-inline-start: auto !important;
  76. }
  77. `;
  78. document.head.append(style);
  79.  
  80. function placeVoting(item) {
  81. const voting = item.querySelector('.voting, .rating-box');
  82. const username = item.querySelector(':scope > header .username');
  83. const footer = item.querySelector(':scope > footer, :scope > .edit-wrapper > footer');
  84.  
  85. if (!voting || !username || !footer) {
  86. return;
  87. }
  88.  
  89. const author = username.closest('.tooltip-slot') || username;
  90. const actions = footer.querySelector(':scope > .actions > ul') || footer;
  91. item.classList.add('tm-plus-item');
  92.  
  93. let placement = placements.get(item);
  94.  
  95. if (!placement) {
  96. const position = positions[Math.floor(Math.random() * positions.length)];
  97. placement = { position, voting: null, slot: null };
  98. placements.set(item, placement);
  99. resizeObserver.observe(item);
  100. }
  101.  
  102. if (placement.voting !== voting) {
  103. if (placement.voting) {
  104. resizeObserver.unobserve(placement.voting);
  105. }
  106.  
  107. placement.voting = voting;
  108. resizeObserver.observe(voting);
  109. }
  110.  
  111. voting.classList.add('tm-random-plus');
  112.  
  113. if (placement.position === 'author') {
  114. author.parentElement.classList.add('tm-plus-author');
  115. }
  116.  
  117. if (placement.position === 'actions' || placement.position === 'bottom-right') {
  118. actions.classList.add('tm-plus-actions');
  119. }
  120.  
  121. if (!placement.slot?.isConnected) {
  122. const inActions = placement.position === 'actions' || placement.position === 'bottom-right';
  123. const slot = document.createElement(inActions && actions.tagName === 'UL' ? 'li' : 'span');
  124. slot.className = 'tm-plus-slot';
  125. slot.setAttribute('aria-hidden', 'true');
  126. placement.slot = slot;
  127.  
  128. switch (placement.position) {
  129. case 'author':
  130. author.before(slot);
  131. break;
  132.  
  133. case 'top-right':
  134. slot.classList.add('tm-plus-top-right');
  135. const headerRight = item.querySelector(':scope > header > .right') || author.parentElement;
  136. headerRight.append(slot);
  137. break;
  138.  
  139. case 'content': {
  140. const row = document.createElement('div');
  141. row.className = 'tm-plus-row';
  142. row.append(slot);
  143.  
  144. footer.before(row);
  145. break;
  146. }
  147.  
  148. case 'bottom-right':
  149. slot.classList.add('tm-plus-bottom-right');
  150. actions.append(slot);
  151. break;
  152.  
  153. case 'actions':
  154. actions.prepend(slot);
  155. break;
  156. }
  157. }
  158.  
  159. // Oryginalny element pozostaje na miejscu, żeby nie zakłócać aktualizacji Vue
  160. item.querySelector(':scope > header').classList.add('tm-plus-header');
  161. voting.classList.add('tm-plus-floating');
  162.  
  163. const parent = voting.offsetParent;
  164.  
  165. if (!parent) {
  166. return;
  167. }
  168.  
  169. const { slot } = placement;
  170. const votingRect = voting.getBoundingClientRect();
  171. slot.style.width = `${votingRect.width}px`;
  172. slot.style.height = `${votingRect.height}px`;
  173.  
  174. if (!slot.getClientRects().length) {
  175. voting.classList.remove('tm-plus-floating');
  176. return;
  177. }
  178.  
  179. const slotRect = slot.getBoundingClientRect();
  180. const parentRect = parent.getBoundingClientRect();
  181. const left = slotRect.left - parentRect.left - parent.clientLeft + parent.scrollLeft;
  182. const top = slotRect.top - parentRect.top - parent.clientTop + parent.scrollTop;
  183.  
  184. voting.style.setProperty('--tm-plus-left', `${left}px`);
  185. voting.style.setProperty('--tm-plus-top', `${top}px`);
  186. }
  187.  
  188. function updateVoting() {
  189. scheduled = false;
  190.  
  191. if (!/^\/(?:mikroblog|wpis|ludzie)(?:\/|$)/.test(location.pathname)) {
  192. return;
  193. }
  194.  
  195. observer.disconnect();
  196.  
  197. try {
  198. const items = document.querySelectorAll(
  199. ':is(.microblog, .entry-page, .profile-page) .thread > .item, .profile-page .entry > article',
  200. );
  201.  
  202. for (const item of items) {
  203. placeVoting(item);
  204. }
  205. } finally {
  206. observer.observe(document.body, observerOptions);
  207. }
  208. }
  209.  
  210. function scheduleUpdate() {
  211. if (scheduled) {
  212. return;
  213. }
  214.  
  215. scheduled = true;
  216. requestAnimationFrame(updateVoting);
  217. }
  218.  
  219. const observer = new MutationObserver(scheduleUpdate);
  220. const resizeObserver = new ResizeObserver(scheduleUpdate);
  221.  
  222. observer.observe(document.body, observerOptions);
  223. updateVoting();
  224. })();
  225.  
Advertisement
Comments
  • User was banned
Add Comment
Please, Sign In to add comment