mirror of
https://github.com/Andre-Bzk/rin-mobile-userscript.git
synced 2026-11-15 09:33:08 +01:00
The forum nests each post body inside its own table, so ascending to the nearest enclosing row stopped within that inner table rather than at the actual post row. The author and profile columns were never in scope, and every post silently degraded to the placeholder name and the generated initial avatar instead of the real ones. Author lookup additionally keyed on a profile link attribute this forum does not emit at all, so even a correct row would have yielded nothing. Both conditions failed quietly, which made the result look like a styling problem rather than a broken DOM traversal.
303 lines
11 KiB
JavaScript
303 lines
11 KiB
JavaScript
// ==UserScript==
|
||
// @name RIN Mobile Thread
|
||
// @description Mobile Beitragsansicht fuer cs.rin.ru
|
||
// @version 2.2
|
||
// @match *://cs.rin.ru/forum/viewtopic.php*
|
||
// @run-at document-start
|
||
// ==/UserScript==
|
||
|
||
(function () {
|
||
'use strict';
|
||
|
||
var DEBUG = true;
|
||
var log = [];
|
||
|
||
function h() { return document.head || document.documentElement; }
|
||
|
||
if (!document.querySelector('meta[name="viewport"][data-rm]')) {
|
||
var m = document.createElement('meta');
|
||
m.name = 'viewport';
|
||
m.content = 'width=device-width, initial-scale=1, viewport-fit=cover';
|
||
m.setAttribute('data-rm', '1');
|
||
h().appendChild(m);
|
||
}
|
||
|
||
var st = document.createElement('style');
|
||
st.textContent = `
|
||
html, body { max-width:100% !important; overflow-x:hidden !important;
|
||
margin:0 !important; padding:0 !important; }
|
||
body { font-size:16px !important;
|
||
-webkit-text-size-adjust:100% !important; }
|
||
|
||
.rm-dbg { background:#222; color:#0f0; font:12px/1.45 monospace;
|
||
padding:8px 10px; white-space:pre-wrap; }
|
||
.rm-hidden { display:none !important; }
|
||
|
||
.rm-root { box-sizing:border-box !important; display:block !important;
|
||
width:100vw !important; max-width:100vw !important;
|
||
margin:0 !important; padding:0 0 64px 0 !important;
|
||
overflow:hidden !important; }
|
||
|
||
.rm-topic { box-sizing:border-box; padding:16px 14px 12px;
|
||
font-size:21px; font-weight:650; line-height:1.3;
|
||
overflow-wrap:break-word; }
|
||
|
||
.rm-post { box-sizing:border-box !important;
|
||
width:100% !important; max-width:100% !important;
|
||
padding:14px !important;
|
||
border-top:1px solid rgba(128,128,128,.25) !important; }
|
||
|
||
.rm-head { display:flex; align-items:center; gap:9px;
|
||
margin-bottom:10px; min-width:0; }
|
||
.rm-av { flex:none; width:28px; height:28px; border-radius:50%;
|
||
object-fit:cover; background:rgba(128,128,128,.25); }
|
||
.rm-who { min-width:0; flex:1; }
|
||
.rm-user { font-size:14px; font-weight:650; line-height:1.2;
|
||
overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||
.rm-sub { font-size:11px; opacity:.55; line-height:1.3;
|
||
overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||
|
||
.rm-body { font-size:17px !important; line-height:1.55 !important;
|
||
overflow-wrap:break-word !important;
|
||
word-break:normal !important; }
|
||
.rm-body * { max-width:100% !important; }
|
||
.rm-body img { height:auto !important; border-radius:6px; }
|
||
.rm-body a { overflow-wrap:anywhere !important; }
|
||
.rm-body table { display:block !important; overflow-x:auto !important;
|
||
width:auto !important; }
|
||
.rm-body pre, .rm-body code {
|
||
display:block; overflow-x:auto; white-space:pre;
|
||
font-size:13px; padding:10px; border-radius:8px;
|
||
background:rgba(128,128,128,.14); }
|
||
.rm-body blockquote, .rm-body .quote, .rm-body table.quote {
|
||
display:block !important; margin:12px 0 !important;
|
||
padding:8px 0 8px 12px !important;
|
||
border-left:3px solid rgba(140,140,255,.5) !important;
|
||
font-size:15px !important; opacity:.8 !important;
|
||
background:none !important; }
|
||
.rm-body ul, .rm-body ol { padding-left:22px !important; }
|
||
.rm-body hr { border:0; border-top:1px solid rgba(128,128,128,.3); }
|
||
|
||
.rm-sig { margin-top:12px; font-size:13px; opacity:.5; }
|
||
.rm-sig summary { cursor:pointer; opacity:.8; }
|
||
|
||
.rm-nav { position:fixed; left:0; right:0; bottom:0; z-index:9999;
|
||
display:flex; align-items:center; justify-content:space-between;
|
||
gap:8px; padding:10px 12px calc(10px + env(safe-area-inset-bottom));
|
||
background:rgba(28,28,30,.94);
|
||
-webkit-backdrop-filter:blur(12px);
|
||
backdrop-filter:blur(12px);
|
||
border-top:1px solid rgba(128,128,128,.3); }
|
||
.rm-nav a, .rm-nav span { font-size:14px; color:#eee !important;
|
||
text-decoration:none !important;
|
||
white-space:nowrap; }
|
||
.rm-nav a { padding:7px 13px; border-radius:99px;
|
||
border:1px solid rgba(160,160,160,.45); }
|
||
.rm-nav .rm-pos { opacity:.6; font-size:13px; }
|
||
.rm-nav .rm-off { opacity:.28; border-color:rgba(160,160,160,.2); }
|
||
`;
|
||
h().appendChild(st);
|
||
|
||
function txt(el) { return (el ? el.textContent || '' : '')
|
||
.replace(/\s+/g, ' ').trim(); }
|
||
|
||
// Der Beitragstext liegt in einer verschachtelten Tabelle. closest('tr')
|
||
// trifft deshalb die innere Zeile, der Autor- und Profilspalte fehlen.
|
||
// Aufsteigen, bis die Zeile eine Profil- oder Autorenspalte enthaelt.
|
||
function postRow(pb) {
|
||
var tr = pb.closest('tr');
|
||
while (tr) {
|
||
if (tr.querySelector(':scope > td.profile') ||
|
||
tr.querySelector(':scope > td .postauthor')) return tr;
|
||
var up = tr.parentElement ? tr.parentElement.closest('tr') : null;
|
||
if (!up) return tr;
|
||
tr = up;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function build() {
|
||
var bodies = document.querySelectorAll('span.postbody, .postbody');
|
||
log.push('postbodies=' + bodies.length);
|
||
if (!bodies.length) return;
|
||
|
||
var root = document.createElement('div');
|
||
root.className = 'rm-root';
|
||
|
||
var tt = document.querySelector('a.maintitle, h1, .maintitle');
|
||
if (tt) {
|
||
var th = document.createElement('div');
|
||
th.className = 'rm-topic';
|
||
th.textContent = txt(tt);
|
||
root.appendChild(th);
|
||
}
|
||
|
||
var built = 0, seen = new Set();
|
||
|
||
bodies.forEach(function (pb) {
|
||
var row = postRow(pb);
|
||
if (!row || seen.has(row)) return;
|
||
seen.add(row);
|
||
|
||
var card = document.createElement('div');
|
||
card.className = 'rm-post';
|
||
|
||
// --- Zellen dieser Zeile + der Kopfzeilen darueber ---
|
||
var cells = [];
|
||
Array.prototype.forEach.call(row.children, function (td) {
|
||
if (!td.contains(pb)) cells.push(td);
|
||
});
|
||
var pr = row.previousElementSibling, g = 0;
|
||
while (pr && g++ < 3) {
|
||
Array.prototype.forEach.call(pr.children, function (td) {
|
||
cells.push(td);
|
||
});
|
||
if (pr.querySelector('.postauthor, a[href*="profile.php"]')) break;
|
||
pr = pr.previousElementSibling;
|
||
}
|
||
|
||
// --- Username ---
|
||
var user = '';
|
||
for (var i = 0; i < cells.length && !user; i++) {
|
||
var ua = cells[i].querySelector(
|
||
'.postauthor, .username-coloured, a[href*="profile.php"]');
|
||
if (ua) user = txt(ua);
|
||
}
|
||
if (!user) {
|
||
for (var i2 = 0; i2 < cells.length && !user; i2++) {
|
||
var nm = cells[i2].querySelector('span.name b, span.name, b.name');
|
||
if (nm) user = txt(nm);
|
||
}
|
||
}
|
||
user = user.replace(/^Post subject:.*/i, '').trim();
|
||
|
||
// --- Avatar ---
|
||
var avImg = null, bestArea = 0;
|
||
cells.forEach(function (td) {
|
||
td.querySelectorAll('img').forEach(function (im) {
|
||
var s = im.getAttribute('src') || '';
|
||
if (/spacer|blank|icon|rank|star|folder|button|pm|email|quote|edit|report|www|msn|aim|yim|icq|topic/i.test(s))
|
||
return;
|
||
if (/avatar/i.test(s)) { avImg = im; bestArea = 1e9; return; }
|
||
var w = parseInt(im.getAttribute('width'), 10) || im.naturalWidth || 0;
|
||
var hh = parseInt(im.getAttribute('height'), 10) || im.naturalHeight || 0;
|
||
var a = w * hh;
|
||
if (w >= 40 && a > bestArea) { avImg = im; bestArea = a; }
|
||
});
|
||
});
|
||
|
||
// --- Rang, Postzahl, Zeitstempel ---
|
||
var allTxt = cells.map(txt).join(' ');
|
||
var rank = '';
|
||
var rr = allTxt.match(/\b(Administrator|Site Admin|Global Moderator|Moderator|VIP|Uploader|Supporter|Member|Newbie|Guest)\b/i);
|
||
if (rr) rank = rr[1];
|
||
var pc = allTxt.match(/Posts:\s*([\d.,]+)/i);
|
||
if (pc) rank = (rank ? rank + ' · ' : '') + pc[1] + ' Posts';
|
||
|
||
var when = '';
|
||
var wm = allTxt.match(/Posted:\s*([^|]+?)(?:\s{2,}|Post subject|$)/i);
|
||
if (wm) when = wm[1].trim();
|
||
|
||
// --- Kopfzeile ---
|
||
var head = document.createElement('div');
|
||
head.className = 'rm-head';
|
||
|
||
var av;
|
||
if (avImg) {
|
||
av = document.createElement('img');
|
||
av.src = avImg.src;
|
||
} else {
|
||
av = document.createElement('div');
|
||
av.textContent = (user || '?').charAt(0).toUpperCase();
|
||
av.style.cssText = 'display:flex;align-items:center;' +
|
||
'justify-content:center;font-size:13px;font-weight:700;' +
|
||
'color:rgba(255,255,255,.75);';
|
||
}
|
||
av.className = 'rm-av';
|
||
head.appendChild(av);
|
||
|
||
var who = document.createElement('div');
|
||
who.className = 'rm-who';
|
||
var un = document.createElement('div');
|
||
un.className = 'rm-user';
|
||
un.textContent = user || 'Gast';
|
||
who.appendChild(un);
|
||
var sub = document.createElement('div');
|
||
sub.className = 'rm-sub';
|
||
sub.textContent = [rank, when].filter(Boolean).join(' · ');
|
||
who.appendChild(sub);
|
||
head.appendChild(who);
|
||
card.appendChild(head);
|
||
|
||
// --- Signatur abtrennen ---
|
||
var sig = null, hr = pb.querySelector('hr');
|
||
if (hr) {
|
||
sig = document.createElement('details');
|
||
sig.className = 'rm-sig';
|
||
var sm = document.createElement('summary');
|
||
sm.textContent = 'Signatur';
|
||
sig.appendChild(sm);
|
||
var n = hr.nextSibling;
|
||
while (n) { var nx = n.nextSibling; sig.appendChild(n); n = nx; }
|
||
hr.remove();
|
||
}
|
||
|
||
var bodyWrap = document.createElement('div');
|
||
bodyWrap.className = 'rm-body';
|
||
bodyWrap.appendChild(pb);
|
||
card.appendChild(bodyWrap);
|
||
if (sig && sig.childNodes.length > 1) card.appendChild(sig);
|
||
|
||
root.appendChild(card);
|
||
built++;
|
||
});
|
||
|
||
log.push('posts=' + built);
|
||
if (!built) return;
|
||
|
||
// --- Navigationsleiste ---
|
||
var nav = document.createElement('div');
|
||
nav.className = 'rm-nav';
|
||
var prevA = null, nextA = null;
|
||
document.querySelectorAll('a').forEach(function (a) {
|
||
var t = txt(a);
|
||
if (!prevA && /^(Previous|Vorherige)$/i.test(t)) prevA = a;
|
||
if (!nextA && /^(Next|Weiter|Nächste)$/i.test(t)) nextA = a;
|
||
});
|
||
var pos = txt(document.body).match(/Page\s+(\d+)\s+of\s+(\d+)/i);
|
||
|
||
function navBtn(a, label) {
|
||
var e = document.createElement('a');
|
||
e.textContent = label;
|
||
if (a) e.href = a.href; else e.className = 'rm-off';
|
||
return e;
|
||
}
|
||
nav.appendChild(navBtn(prevA, '‹ Zurück'));
|
||
var ps = document.createElement('span');
|
||
ps.className = 'rm-pos';
|
||
ps.textContent = pos ? pos[1] + ' / ' + pos[2] : '';
|
||
nav.appendChild(ps);
|
||
nav.appendChild(navBtn(nextA, 'Weiter ›'));
|
||
root.appendChild(nav);
|
||
|
||
Array.prototype.forEach.call(document.body.children, function (el) {
|
||
if (el !== root) el.classList.add('rm-hidden');
|
||
});
|
||
document.body.appendChild(root);
|
||
}
|
||
|
||
function run() {
|
||
try { build(); } catch (e) { log.push('build: ' + e.message); }
|
||
if (DEBUG) {
|
||
var d = document.createElement('div');
|
||
d.className = 'rm-dbg';
|
||
d.textContent = 'RIN Thread v2.2\n' + log.join('\n');
|
||
document.body.insertBefore(d, document.body.firstChild);
|
||
}
|
||
}
|
||
|
||
if (document.readyState === 'loading')
|
||
document.addEventListener('DOMContentLoaded', run);
|
||
else run();
|
||
})();
|