SHOW:
|
|
- or go back to the newest paste.
| 1 | // ==UserScript== | |
| 2 | // @name Wykop - losowe położenie plusów | |
| 3 | // @namespace wykop-losowe-plusy | |
| 4 | - | // @version 1.0.0 |
| 4 | + | // @version 1.1.0 |
| 5 | - | // @description Umieszcza plusy wpisów i komentarzy w losowych miejscach na mikroblogu i stronach pojedynczych wpisów. |
| 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) .item .voting.tm-random-plus {
|
| 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) .item .voting.tm-plus-floating {
|
| 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) .tm-plus-header {
|
| 42 | + | :is(.microblog, .entry-page, .profile-page) .tm-plus-header {
|
| 43 | position: relative !important; | |
| 44 | } | |
| 45 | ||
| 46 | - | :is(.microblog, .entry-page) .tm-plus-author, |
| 46 | + | :is(.microblog, .entry-page, .profile-page) .tm-plus-author, |
| 47 | - | :is(.microblog, .entry-page) .tm-plus-actions {
|
| 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) .tm-plus-slot {
|
| 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 | - | :is(.microblog, .entry-page) .tm-plus-row {
|
| 58 | + | vertical-align: middle; |
| 59 | - | display: flex; |
| 59 | + | |
| 60 | ||
| 61 | :is(.microblog, .entry-page, .profile-page) .tm-plus-slot {
| |
| 62 | display: inline-block; | |
| 63 | flex: 0 0 auto; | |
| 64 | - | :is(.microblog, .entry-page) .tm-plus-row-right {
|
| 64 | + | |
| 65 | - | justify-content: flex-end; |
| 65 | + | |
| 66 | ||
| 67 | :is(.microblog, .entry-page, .profile-page) .tm-plus-item .tm-plus-row {
| |
| 68 | - | :is(.microblog, .entry-page) .tm-plus-slot.tm-plus-top-right {
|
| 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 | - | const voting = item.querySelector('.voting');
|
| 75 | + | |
| 76 | } | |
| 77 | - | const footer = item.querySelector(':scope > footer');
|
| 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 | - | username.parentElement.classList.add('tm-plus-author');
|
| 104 | + | |
| 105 | } | |
| 106 | ||
| 107 | - | if (placement.position === 'actions') {
|
| 107 | + | |
| 108 | - | footer.classList.add('tm-plus-actions');
|
| 108 | + | |
| 109 | } | |
| 110 | ||
| 111 | voting.classList.add('tm-random-plus');
| |
| 112 | - | const slot = document.createElement('span');
|
| 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 | - | username.before(slot); |
| 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 | - | username.parentElement.append(slot); |
| 124 | + | |
| 125 | slot.setAttribute('aria-hidden', 'true');
| |
| 126 | placement.slot = slot; | |
| 127 | - | case 'content': |
| 127 | + | |
| 128 | - | case 'bottom-right': {
|
| 128 | + | |
| 129 | case 'author': | |
| 130 | author.before(slot); | |
| 131 | break; | |
| 132 | ||
| 133 | - | if (placement.position === 'bottom-right') {
|
| 133 | + | |
| 134 | - | row.classList.add('tm-plus-row-right');
|
| 134 | + | |
| 135 | - | footer.after(row); |
| 135 | + | const headerRight = item.querySelector(':scope > header > .right') || author.parentElement;
|
| 136 | - | } else {
|
| 136 | + | headerRight.append(slot); |
| 137 | - | footer.before(row); |
| 137 | + | |
| 138 | - | } |
| 138 | + | |
| 139 | case 'content': {
| |
| 140 | const row = document.createElement('div');
| |
| 141 | row.className = 'tm-plus-row'; | |
| 142 | row.append(slot); | |
| 143 | ||
| 144 | - | footer.prepend(slot); |
| 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 | - | if (!/^\/(?:mikroblog|wpis)(?:\/|$)/.test(location.pathname)) {
|
| 176 | + | |
| 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 | - | const items = document.querySelectorAll(':is(.microblog, .entry-page) .thread > .item');
|
| 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 |