mirror of
https://github.com/Andre-Bzk/rin-mobile-userscript.git
synced 2026-11-15 09:33:08 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7f3f56456 | ||
|
|
a8ca211805 | ||
|
|
21820b88d0 |
@@ -13,6 +13,8 @@ die `.user.js`-Datei ist das Produkt.
|
||||
| `dev/index.php` | — | Fixture: Forumwurzel, zwei Kategorien mit Forenzeilen, keine Paginierung |
|
||||
| `dev/ucp.php` | — | Fixture: Login-Formular (`mode=login`), nur ausgeloggter Zustand |
|
||||
| `dev/after_login/ucp.php` | — | Fixture: dieselbe URL nach erfolgreichem Login, "Information"-Vorlage statt Formular |
|
||||
| `dev/search.php` | — | Fixture: Suchformular ohne Treffer, eingeloggt aufgenommen |
|
||||
| `dev/search2.php` | — | Fixture: Suchtreffer im Modus "Posts" (15 von 1438 Treffern, `t=63042`, `per_page=15`, 96 Seiten) |
|
||||
|
||||
Die Fixtures sind ueber `.gitignore` (`dev/*.php`) ausgeschlossen. Nach einem
|
||||
frischen Klon sind sie nicht da und muessen nach der Anleitung in der README neu
|
||||
@@ -203,6 +205,90 @@ Original-Zurueck/Weiter-Links, das Sheet verschwindet.
|
||||
einer verschachtelten Tabelle, `closest('tr')` trifft die innere Zeile ohne
|
||||
Autor- und Profilspalte.
|
||||
|
||||
**Die Lupe und die Login-Pille haengen an einer gemeinsamen `margin-left:auto`
|
||||
auf einem Wrapper (`.rm-auth-group`), nicht je an sich selbst.** Saesse die
|
||||
Regel wie zuvor direkt auf `.rm-auth`, wuerde nur die Pille an den Rand
|
||||
geschoben und die Lupe bliebe links davon kleben — mit sichtbarer Luecke
|
||||
dazwischen statt „direkt links neben der Pille". Die Lupe existiert im Markup
|
||||
ohnehin nur bei bestehender Sitzung; ausgeloggt bleibt der Wrapper mit der
|
||||
Pille als einzigem Kind und verhaelt sich exakt wie vorher.
|
||||
|
||||
**`#topic-search` steht dreifach identisch im Thread-Markup** (Kopf, Fuss,
|
||||
ueber der Beitragsliste), jedes Mal mit derselben, eigentlich unzulaessigen
|
||||
`id`. `document.querySelector('#topic-search')` trifft einfach das erste und
|
||||
verschiebt es unveraendert in die zweite Leiste — alle drei tragen dieselben
|
||||
versteckten Felder (`t`, `sf`, `terms`), welches der drei benutzt wird, ist
|
||||
gleichgueltig.
|
||||
|
||||
**`select[name="fid[]"]` ist der einzige verlaessliche Marker fuer das grosse
|
||||
Suchformular auf `search.php`.** Sowohl `viewforum.php` als auch
|
||||
`viewtopic.php` betten ein eigenes Mini-Suchformular ein (`#forum-search` /
|
||||
`#topic-search`, „Search this forum…" / „Search this topic…"), beide mit
|
||||
`action="./search.php"` und einem Feld `name="keywords"` — nach diesen Merkmalen
|
||||
allein liesse sich das grosse Formular nicht von den kleinen unterscheiden.
|
||||
Der mehrfach waehlbare Forenwaehler existiert dagegen nur im grossen Formular.
|
||||
|
||||
**Die Textfelder des Suchformulars tragen `style="width: 300px"` direkt im
|
||||
Markup.** Eine externe Regel ohne `!important` sticht dagegen nicht, egal wie
|
||||
spezifisch der Selektor ist — `buildSearchForm()` entfernt das
|
||||
`style`-Attribut deshalb, statt gegen es anzuschreiben.
|
||||
|
||||
**Eine abgeschickte Suche liefert keine Themenliste, sondern einen ganz
|
||||
anderen Aufbau.** Die anfaengliche Annahme, Trefferzeilen tragen dieselben
|
||||
`topictitle`-Links wie eine Themenliste und laufen unveraendert durch
|
||||
`buildList()`, stimmte nicht — zumindest nicht im Modus "Posts" (`sr=posts`),
|
||||
der ueber die zweite Leiste im Thread entsteht, weil deren Formular kein `sr`
|
||||
setzt. Jeder Treffer ist stattdessen ein Beitragsauszug: Autor, Zeitstempel,
|
||||
Forum/Thema-Kontext, ein Stueck `postbody`. `buildSearchResults()` erkennt
|
||||
diesen Modus an `p.topictitle` — als `<p>`, nicht als `<a>` wie das
|
||||
gleichnamige Klassenpendant einer Themenzeile in `viewforum.php` — und baut
|
||||
daraus eigene Karten (`buildHit()`), bevor `buildList()`s allgemeines
|
||||
Zeileneinsammeln ueberhaupt anfaengt und die enthaltenen `viewtopic.php`-Links
|
||||
falsch klassifizieren wuerde. Ungeprueft bleibt der Modus "Topics" (`sr=topics`,
|
||||
Vorauswahl im grossen Suchformular) — dafuer liegt keine Fixture vor, dort
|
||||
sehen Treffer laut phpBB-Doku tatsaechlich wie topictitle-Zeilen aus und
|
||||
wuerden vermutlich durch den regulaeren Listenzweig laufen, das ist aber nicht
|
||||
verifiziert.
|
||||
|
||||
**Ein Treffer im Modus "Posts" besteht aus drei `<tr>`, unterscheidbar nur an
|
||||
der Position, nicht an einer Klasse.** Titelzeile (`p.topictitle`), Autor-
|
||||
und Betreffzeile, dann Antworten/Views samt Beitragsauszug — alle drei
|
||||
tragen row1/row2 wahllos, die einzige verlaessliche Zuordnung ist
|
||||
`nextElementSibling` von der Titelzeile aus.
|
||||
|
||||
**Der Beitragsauszug haengt in `<td class="postbody">`, nicht in
|
||||
`div`/`span.postbody` wie im echten Thread.** `buildHit()` verschiebt das
|
||||
`<td>` trotzdem unveraendert per `appendChild()` in `.rm-body` — das ist reine
|
||||
DOM-Manipulation, keine Neuparse, darum meckert der Browser nicht ueber ein
|
||||
`<td>` ausserhalb einer Tabelle. Es rendert danach aber weiterhin als
|
||||
`display:table-cell`, wenn `.rm-root.rm-hits .rm-body > td` das nicht
|
||||
ausdruecklich auf `block` zwingt.
|
||||
|
||||
**"Replies: 5403" und "Views: 951837" stehen im Markup nur durch ein `<br />`
|
||||
getrennt, ohne Leerzeichen.** `txt()` normalisiert Whitespace, erzeugt aber
|
||||
keinen wo im Original-Markup keiner steht — `txt(meta)` liefert deshalb
|
||||
`5403Views:951837` statt zwei getrennter Werte. `buildHit()` greift beide
|
||||
Zahlen einzeln per Regex heraus und reicht sie an dieselbe `metaRow()` wie bei
|
||||
Themenkarten weiter, die den Trenner per CSS (`::before`) selbst einfuegt.
|
||||
|
||||
**`input[type="submit"]`/`input[type="reset"]` setzen eigenes Padding und
|
||||
Zentrierung in iOS Safari unzuverlaessig um — auch mit `-webkit-appearance:
|
||||
none`.** Zuerst aufgefallen bei Suchen/Zuruecksetzen im Suchformular
|
||||
nebeneinander, wo die feste Flex-Aufteilung die Verzerrung besonders sichtbar
|
||||
machte: zu schmale Buttons, Text nicht mittig. `-webkit-appearance:none` plus
|
||||
`box-sizing:border-box`/`line-height:normal` (erster Versuch) reichte laut
|
||||
Test am Geraet nicht aus — das legt einen tieferen WebKit-Bug nahe, bei dem
|
||||
`input[type=submit/reset]` die native Knopfoptik trotz `appearance:none` nicht
|
||||
vollstaendig ablegt. Der tragfaehige Fix ist `inputToButton()`: die
|
||||
Original-`<input>`-Elemente werden vor dem Verschieben durch echte
|
||||
`<button type="submit/reset">` ersetzt (Name und Value wandern mit, am
|
||||
uebermittelten Formularfeld aendert sich nichts) — `<button>` wird von WebKit
|
||||
unter `appearance:none` zuverlaessig respektiert. Betrifft grundsaetzlich
|
||||
jeden Submit-Button im Script: Suchformular, Login, Thread-Suchleiste nutzen
|
||||
alle `inputToButton()`. Ungeprueft, ob `-webkit-appearance:none` allein bei
|
||||
einer anderen iOS-Version genuegt haette — der Wechsel auf `<button>` umgeht
|
||||
die Frage, statt sie zu beantworten.
|
||||
|
||||
**`DEBUG` steht auf `true`** und muss vor dem Ausliefern auf `false`. Die
|
||||
Versionsnummer steht doppelt: im `@version`-Header und im Text des
|
||||
Debug-Overlays.
|
||||
@@ -267,6 +353,27 @@ Kategorien mit zusammen 16 Forenkarten, keine Paginierung. Zu pruefen ist
|
||||
zusaetzlich eine `ucp.php?mode=login&redirect=index.php`-URL — sie muss trotz
|
||||
des `index.php`-Substrings im Login-Zweig landen, nicht im Listen-Zweig.
|
||||
|
||||
Fuer das Suchformular genuegt `dev/search.php` unveraendert, URL
|
||||
`search.php`: geprueft werden der Forenwaehler als Erkennungsmerkmal, das
|
||||
verschobene Formular samt versteckter Felder (`t`, `sid`), die entfernten
|
||||
inline-`style`-Attribute der Textfelder und die Schnellzugriffs-Chips aus
|
||||
`p.searchbar`. Zu pruefen ist ausserdem `dev/viewforum.php` auf derselben
|
||||
Weiche: dessen eingebettetes `#forum-search`-Mini-Formular teilt sich Aktion
|
||||
und `keywords`-Feld mit dem grossen Suchformular und darf `buildSearchForm()`
|
||||
nicht faelschlich auslösen, die Forenliste muss wie gewohnt Karten liefern.
|
||||
|
||||
Fuer Suchtreffer genuegt `dev/search2.php` unveraendert, URL beliebig (die
|
||||
Erkennung haengt nicht an `location`, sondern an `p.topictitle` im Markup):
|
||||
geprueft werden alle 15 Trefferkarten, die verschobene "Searched topic /
|
||||
Search term used"-Zelle samt ihrer beiden Links, Autor und Zeitstempel pro
|
||||
Treffer, die per Regex getrennten Antworten-/Views-Zahlen, der verschobene
|
||||
`td.postbody`-Beitragsauszug und die Seitennavigation (`per_page=15`, 96
|
||||
Seiten). Zu pruefen ist ausserdem, dass `dev/search.php` (das leere Formular)
|
||||
weiterhin den Formular-Zweig trifft und nicht in die Treffer-Erkennung faellt,
|
||||
und dass `dev/viewforum.php` weiterhin normale Karten liefert — beide haben
|
||||
kein `p.topictitle`. Der Modus "Topics" (`sr=topics`) ist mangels Fixture
|
||||
ungeprueft.
|
||||
|
||||
Am Geraet zu pruefen bleibt, was Blink nicht zeigt: `env(safe-area-inset-*)`,
|
||||
fixierte Leisten gegen Safaris ein- und ausfahrende Toolbar,
|
||||
`-webkit-backdrop-filter`, Schriftskalierung. Details und die
|
||||
|
||||
@@ -72,6 +72,14 @@ account name accordingly. The name is parsed from the link text with a
|
||||
if it ever fails to match — a different language, a different bracket style —
|
||||
the pill still shows "Logout", just without a name, rather than breaking.
|
||||
|
||||
Once a session exists, the login pill also carries a magnifier button
|
||||
directly to its left, so it does not compete with the breadcrumb trail for
|
||||
space and only shows up where cs.rin.ru's own search is actually reachable.
|
||||
Outside a thread it links straight to `search.php`; inside one it instead
|
||||
reveals a second bar under the crumbs with a search field, reusing the
|
||||
original "Search this topic…" form in place rather than rebuilding it, so the
|
||||
hidden topic-id and scope fields stay intact.
|
||||
|
||||
The login form itself is not rebuilt from scratch: the original `<form>`
|
||||
element is moved into the new layout and restyled, table rows collapsed to a
|
||||
vertical stack. Reusing the element rather than reconstructing it keeps the
|
||||
@@ -87,6 +95,27 @@ header followed by a message cell), not by its wording, and restyles it the
|
||||
same way as the form: the original cell — message and link included — is
|
||||
reused in place rather than rebuilt.
|
||||
|
||||
`search.php` gets its own treatment when it shows the query form rather than
|
||||
results. It is recognised by its forum picker — a `select[name="fid[]"]`
|
||||
multi-select that only exists on this form, not on the small "Search this
|
||||
forum…" / "Search this topic…" widgets embedded elsewhere — and then moved
|
||||
into the new layout the same way the login form is: table rows collapsed to a
|
||||
vertical stack, radio groups and selects restyled for touch, and the quick
|
||||
links above it ("View new posts", "View your posts", …) turned into a row of
|
||||
chips. "Search in forums" defaults to Main Forum and "Search subforums"
|
||||
defaults to No, overriding the server's own blank/Yes defaults, since that is
|
||||
the scope most searches from the mobile view actually want.
|
||||
|
||||
A submitted search lands back on `search.php`, now with results — which, in
|
||||
the "Posts" display mode the in-thread search bar produces, look nothing like
|
||||
a topic list: each hit is a post excerpt with its own author, timestamp, and
|
||||
forum/topic context. Recognised by a `p.topictitle` marker unique to this
|
||||
mode, these get their own cards, built before the regular list view's row
|
||||
scanning would otherwise misread the topic and profile links inside them. The
|
||||
"Topics" display mode is not handled specially — no fixture shows what it
|
||||
looks like, so it still falls through to the regular list view, which may or
|
||||
may not render it sensibly.
|
||||
|
||||
The script runs at `document-start`, injects a mobile viewport meta tag, and
|
||||
carries a `DEBUG` flag. While `DEBUG` is `true`, a green diagnostic overlay is
|
||||
prepended to the page showing which view was chosen and how many rows were
|
||||
@@ -291,6 +320,15 @@ gegen das Markup einer echten eingeloggten Sitzung bestätigt ist; greift es
|
||||
einmal nicht — andere Sprache, andere Klammerform — bleibt die Pille bei
|
||||
„Logout" stehen, nur ohne Namen, statt zu reißen.
|
||||
|
||||
Besteht eine Sitzung, trägt die Login-Pille zusätzlich einen Lupen-Button
|
||||
direkt links neben sich, damit er nicht mit der Brotkrumenleiste um Platz
|
||||
konkurriert, und erscheint nur dort, wo cs.rins eigene Suche überhaupt
|
||||
erreichbar ist. Außerhalb eines Threads führt er direkt zu `search.php`;
|
||||
innerhalb eines Threads blendet er stattdessen eine zweite Leiste unter den
|
||||
Brotkrumen mit einem Suchfeld ein — das ist das Original-Formular „Search this
|
||||
topic…", unverändert an Ort und Stelle wiederverwendet statt neu gebaut, damit
|
||||
die versteckten Felder für Themen-Id und Suchbereich erhalten bleiben.
|
||||
|
||||
Das Login-Formular selbst wird nicht neu gebaut: das Original-`<form>`-Element
|
||||
wandert unverändert in das neue Layout und wird umgestylt, Tabellenzeilen
|
||||
werden zu einer senkrechten Abfolge zusammengeklappt. Die Wiederverwendung
|
||||
@@ -308,6 +346,29 @@ Nachrichtenzelle), und stylt sie wie das Formular um: die Original-Zelle —
|
||||
Nachricht und Link inklusive — wandert unverändert an ihren Platz statt neu
|
||||
gebaut zu werden.
|
||||
|
||||
`search.php` bekommt eine eigene Behandlung, wenn es das Suchformular statt
|
||||
Treffern zeigt. Erkannt wird das am Forenwähler — einem Mehrfachauswahlfeld
|
||||
`select[name="fid[]"]`, das es nur in diesem Formular gibt, nicht in den
|
||||
kleinen „Search this forum…" / „Search this topic…"-Widgets an anderer
|
||||
Stelle — und dann wie das Login-Formular in das neue Layout verschoben:
|
||||
Tabellenzeilen zu einer senkrechten Abfolge zusammengeklappt, Radio-Gruppen
|
||||
und Auswahlfelder für Touch umgestylt, die Schnellzugriffe darüber („View new
|
||||
posts", „View your posts", …) zu einer Reihe aus Chips. „Search in forums"
|
||||
ist dabei standardmäßig auf Main Forum vorausgewählt, „Search subforums" auf
|
||||
No — statt der leeren/„Yes"-Vorgabe des Servers, weil das dem Suchumfang
|
||||
entspricht, der von der mobilen Ansicht aus meistens gewollt ist.
|
||||
|
||||
Eine abgeschickte Suche landet wieder auf `search.php`, diesmal mit Treffern —
|
||||
die im Anzeigemodus „Posts", den die Suchleiste im Thread erzeugt, aber
|
||||
überhaupt nicht wie eine Themenliste aussehen: jeder Treffer ist ein
|
||||
Beitragsauszug mit eigenem Autor, Zeitstempel und Forum-/Thema-Kontext.
|
||||
Erkannt an einem Merkmal (`p.topictitle`), das es nur in diesem Modus gibt,
|
||||
bekommen sie eigene Karten, gebaut bevor das Zeileneinsammeln der gewöhnlichen
|
||||
Listenansicht die enthaltenen Themen- und Profillinks falsch deuten würde.
|
||||
Der Anzeigemodus „Topics" bekommt keine eigene Behandlung — keine Fixture
|
||||
zeigt, wie er aussieht, er läuft also weiterhin durch die gewöhnliche
|
||||
Listenansicht, was passend sein kann oder auch nicht.
|
||||
|
||||
Das Script läuft bei `document-start`, setzt ein mobiles Viewport-Meta-Tag und
|
||||
hat ein `DEBUG`-Flag. Solange `DEBUG` auf `true` steht, wird ein grünes
|
||||
Diagnose-Overlay über die Seite gelegt, das zeigt, welche Ansicht gewählt wurde
|
||||
|
||||
+416
-7
@@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name RIN Mobile
|
||||
// @description Mobile Ansicht fuer cs.rin.ru: Themenlisten als Karten, Threads als Beitragsstrom
|
||||
// @version 3.8
|
||||
// @version 3.14
|
||||
// @match *://cs.rin.ru/forum/*
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
@@ -17,6 +17,23 @@
|
||||
function txt(el) { return (el ? el.textContent || '' : '')
|
||||
.replace(/\s+/g, ' ').trim(); }
|
||||
|
||||
// input[type=submit]/[type=reset] behalten in iOS Safari trotz
|
||||
// -webkit-appearance:none teils ihre native Knopfoptik und setzen eigenes
|
||||
// Padding und Zentrierung nur unzuverlaessig um — ein echtes <button> wird
|
||||
// unter appearance:none dagegen zuverlaessig respektiert. name und value
|
||||
// wandern mit, damit sich am uebermittelten Formularfeld nichts aendert.
|
||||
function inputToButton(input) {
|
||||
if (!input) return null;
|
||||
var btn = document.createElement('button');
|
||||
btn.type = input.type;
|
||||
if (input.name) btn.name = input.name;
|
||||
if (input.hasAttribute('value')) btn.value = input.value;
|
||||
btn.className = input.className;
|
||||
btn.textContent = input.value;
|
||||
input.replaceWith(btn);
|
||||
return btn;
|
||||
}
|
||||
|
||||
// Der Seitentyp entscheidet, welche Ansicht gebaut wird. Beide Ansichten
|
||||
// liegen in einem Script, weil sie sich Grundstile und Klassennamen teilen.
|
||||
// Als getrennte Scripts waren auf Threadseiten beide Stylesheets aktiv und
|
||||
@@ -86,10 +103,21 @@
|
||||
color:#eee !important; text-decoration:none !important; }
|
||||
.rm-crumbs a:last-of-type { font-weight:650; }
|
||||
.rm-crumbs .rm-sep { flex:none; font-size:13px; color:#eee; opacity:.45; }
|
||||
.rm-crumbs a.rm-auth { flex:none; margin-left:auto; padding:6px 12px !important;
|
||||
/* Traegt die Lupe und die Login/Logout-Pille zusammen als eine Einheit
|
||||
rechts — nur eine gemeinsame margin-left:auto, statt jedes Element
|
||||
einzeln an den Rand zu schieben, sonst klafft zwischen beiden eine
|
||||
Luecke statt "direkt links neben der Pille". */
|
||||
.rm-crumbs .rm-auth-group { flex:none; margin-left:auto;
|
||||
display:flex; align-items:center; gap:6px; }
|
||||
.rm-crumbs a.rm-auth { flex:none; padding:6px 12px !important;
|
||||
font-size:13px !important; border-radius:99px;
|
||||
background:rgba(128,128,128,.18);
|
||||
border:1px solid rgba(160,160,160,.4); }
|
||||
.rm-crumbs a.rm-search { flex:none; min-width:44px; height:29px;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
padding:0 16px !important; font-size:14px; border-radius:99px;
|
||||
background:rgba(128,128,128,.18);
|
||||
border:1px solid rgba(160,160,160,.4); }
|
||||
/* Nur gesetzt, wenn die Leiste wirklich gebaut wurde — ohne Brotkrumen
|
||||
im Original-DOM bleibt der Platz sonst leer reserviert. */
|
||||
body.rm-crumbed .rm-root {
|
||||
@@ -97,6 +125,38 @@
|
||||
body.rm-crumbed .rm-dbg {
|
||||
margin-top:calc(45px + env(safe-area-inset-top)); }
|
||||
|
||||
/* Zweite Leiste unter den Brotkrumen, nur im Thread: Ersatz fuer
|
||||
"Search this topic..." der Originalseite. Das dortige Formular wird
|
||||
unveraendert hierher verschoben statt neu gebaut, wie das
|
||||
Login-Formular in .rm-login form. */
|
||||
.rm-search-bar { box-sizing:border-box;
|
||||
position:fixed; left:0; right:0; z-index:9997;
|
||||
top:calc(44px + env(safe-area-inset-top));
|
||||
display:flex; align-items:center;
|
||||
padding:8px 12px;
|
||||
background:rgba(28,28,30,.96);
|
||||
-webkit-backdrop-filter:blur(12px);
|
||||
backdrop-filter:blur(12px);
|
||||
border-bottom:1px solid rgba(128,128,128,.3);
|
||||
transform:translateY(-110%);
|
||||
transition:transform .18s ease; }
|
||||
.rm-search-bar.rm-open { transform:translateY(0); }
|
||||
.rm-search-bar form { flex:1; min-width:0;
|
||||
display:flex; align-items:center; gap:8px; }
|
||||
.rm-search-bar input[type="text"] {
|
||||
flex:1; min-width:0; box-sizing:border-box;
|
||||
font-size:16px; padding:9px 12px; border-radius:10px;
|
||||
color:#eee; background:rgba(120,120,120,.18);
|
||||
border:1px solid rgba(160,160,160,.35); }
|
||||
.rm-search-bar button[type="submit"] {
|
||||
-webkit-appearance:none; appearance:none;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
box-sizing:border-box; line-height:normal; text-align:center;
|
||||
flex:none; min-height:44px; font-size:14px; font-weight:650;
|
||||
padding:12px 16px;
|
||||
border-radius:10px; color:#fff; border:0;
|
||||
background:rgba(120,170,255,.85); }
|
||||
|
||||
/* === Themen- und Forenliste === */
|
||||
|
||||
.rm-sec { padding:18px 14px 6px; font-size:12px; font-weight:700;
|
||||
@@ -258,10 +318,104 @@
|
||||
text-decoration:none !important; color:inherit !important; }
|
||||
.rm-login form input[type="checkbox"] { width:18px; height:18px;
|
||||
vertical-align:middle; margin-right:8px; }
|
||||
.rm-login form input[type="submit"] {
|
||||
width:100%; font-size:16px; font-weight:650; padding:13px;
|
||||
margin-top:20px; border-radius:10px; color:#fff; border:0;
|
||||
.rm-login form button[type="submit"] {
|
||||
-webkit-appearance:none; appearance:none;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
box-sizing:border-box; line-height:normal; text-align:center;
|
||||
width:100%; min-height:48px; font-size:16px; font-weight:650;
|
||||
padding:16px 13px; margin-top:20px; border-radius:10px; color:#fff;
|
||||
border:0; background:rgba(120,170,255,.85); }
|
||||
|
||||
/* === Suchformular (search.php ohne Treffer) === */
|
||||
|
||||
.rm-search-shortcuts { display:flex; flex-wrap:wrap; gap:8px;
|
||||
padding:0 14px 16px; }
|
||||
.rm-search-shortcuts a { font-size:13px; padding:6px 12px; border-radius:99px;
|
||||
border:1px solid rgba(128,128,128,.4);
|
||||
text-decoration:none !important; color:inherit !important; }
|
||||
|
||||
/* Derselbe Tabelle-zu-Block-Trick wie bei .rm-login form, eigener Name
|
||||
statt Wiederverwendung: das Formular hier ist ein anderes und hat
|
||||
andere Feldtypen (radio, select statt checkbox). */
|
||||
.rm-searchform form { display:block;
|
||||
padding:4px 14px calc(80px + env(safe-area-inset-bottom)); }
|
||||
.rm-searchform form table, .rm-searchform form tbody,
|
||||
.rm-searchform form tr, .rm-searchform form td {
|
||||
display:block !important; width:100% !important; }
|
||||
.rm-searchform form td { padding:0 !important; }
|
||||
/* Bleibt sichtbar statt wie beim Login versteckt: die beiden th-Zeilen
|
||||
("Search query", "Search options") sind hier Abschnittstrenner, keine
|
||||
Formular-Kopfzelle ohne eigenen Inhalt. */
|
||||
.rm-searchform form th { display:block !important; padding:16px 0 4px;
|
||||
font-size:12px; font-weight:700;
|
||||
text-transform:uppercase; letter-spacing:.06em;
|
||||
opacity:.55; }
|
||||
.rm-searchform form b.genmed { display:block; margin:14px 0 4px;
|
||||
font-size:14px; font-weight:650; }
|
||||
.rm-searchform form span.gensmall { display:block; margin-bottom:6px;
|
||||
font-size:12px; opacity:.6; }
|
||||
.rm-searchform form input[type="text"] {
|
||||
box-sizing:border-box; width:100%; font-size:16px; padding:11px 12px;
|
||||
border-radius:10px; color:#eee; background:rgba(120,120,120,.18);
|
||||
border:1px solid rgba(160,160,160,.35); }
|
||||
.rm-searchform form select {
|
||||
box-sizing:border-box; width:100%; font-size:16px; padding:9px 12px;
|
||||
margin-top:4px; border-radius:10px; color:#eee;
|
||||
background:rgba(120,120,120,.18); border:1px solid rgba(160,160,160,.35); }
|
||||
.rm-searchform form select[multiple] { font-size:15px; padding:4px; }
|
||||
.rm-searchform form input[type="radio"] { width:18px; height:18px;
|
||||
vertical-align:middle; margin-right:6px; }
|
||||
/* Einzige Zeile ohne Block-Stapelung: Suchen/Zuruecksetzen sollen
|
||||
nebeneinander stehen, nicht wie der Rest des Formulars untereinander. */
|
||||
.rm-searchform form td.cat { display:flex !important; gap:10px;
|
||||
padding-top:10px !important; }
|
||||
/* input[type=submit]/[type=reset] behalten in iOS Safari trotz
|
||||
-webkit-appearance:none teils die native Knopfoptik und setzen Padding,
|
||||
Rahmen und Zentrierung nur unzuverlaessig um — deshalb echte <button>
|
||||
statt der Original-<input>-Elemente, siehe inputToButton(). */
|
||||
.rm-searchform form button[type="submit"],
|
||||
.rm-searchform form button[type="reset"] {
|
||||
-webkit-appearance:none; appearance:none;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
box-sizing:border-box; line-height:normal; text-align:center;
|
||||
min-height:48px; font-size:16px; font-weight:650;
|
||||
padding:16px 13px; border-radius:10px;
|
||||
border:0; }
|
||||
.rm-searchform form button[type="submit"] { flex:1; color:#fff;
|
||||
background:rgba(120,170,255,.85); }
|
||||
.rm-searchform form button[type="reset"] { flex:none; color:#eee;
|
||||
background:rgba(120,120,120,.18);
|
||||
border:1px solid rgba(160,160,160,.45) !important; }
|
||||
|
||||
/* === Suchtreffer (search.php mit Ergebnissen, Modus "Posts") === */
|
||||
|
||||
.rm-hits-info { padding:0 14px 14px; font-size:13px; opacity:.75;
|
||||
overflow-wrap:break-word; }
|
||||
.rm-hits-info a { color:inherit !important; }
|
||||
|
||||
.rm-hit { box-sizing:border-box !important; display:block !important;
|
||||
width:100% !important; padding:14px !important;
|
||||
border-top:1px solid rgba(128,128,128,.25) !important; }
|
||||
.rm-hit-ctx { display:block; margin-bottom:8px; font-size:12px;
|
||||
font-weight:650; text-transform:uppercase; letter-spacing:.03em;
|
||||
opacity:.6; text-decoration:none !important;
|
||||
color:inherit !important; overflow-wrap:break-word; }
|
||||
.rm-hit-head { display:flex; align-items:baseline; justify-content:space-between;
|
||||
gap:8px; }
|
||||
.rm-hit-user { font-size:14px; font-weight:650; }
|
||||
.rm-hit-when { flex:none; font-size:12px; opacity:.55; }
|
||||
/* Wiederverwendet: dieselbe .rm-meta-Regel aus der Themenliste, gleiche
|
||||
"X Antworten · Y Views"-Optik. */
|
||||
.rm-hit .rm-meta { margin-top:4px; }
|
||||
/* .rm-body wird 1:1 vom Thread uebernommen (gleiches Beitragsmarkup),
|
||||
nur der verschobene Container ist hier ein td statt div/span und
|
||||
braucht die Umstellung auf Block, die .rm-body selbst nicht mitbringt. */
|
||||
.rm-root.rm-hits .rm-body { margin-top:8px; }
|
||||
.rm-root.rm-hits .rm-body > td { display:block !important; padding:0 !important; }
|
||||
.rm-hit-go { display:inline-block; margin-top:10px; font-size:12px;
|
||||
padding:5px 10px; border:1px solid rgba(128,128,128,.4);
|
||||
border-radius:99px; text-decoration:none !important;
|
||||
color:inherit !important; }
|
||||
|
||||
/* === Seitennavigation, beide Ansichten === */
|
||||
|
||||
@@ -388,11 +542,31 @@
|
||||
if (!n) return;
|
||||
|
||||
var st = loginState();
|
||||
var group = document.createElement('div');
|
||||
group.className = 'rm-auth-group';
|
||||
|
||||
// Lupe nur bei bestehender Sitzung. Ausserhalb von viewtopic fuehrt sie
|
||||
// direkt auf search.php, im Thread blendet sie stattdessen die zweite
|
||||
// Leiste ein — Ersatz fuer "Search this topic...", das mit dem
|
||||
// Original-DOM ausgeblendet wurde.
|
||||
if (st.in) {
|
||||
var search = document.createElement('a');
|
||||
search.className = 'rm-search';
|
||||
search.href = 'https://cs.rin.ru/forum/search.php';
|
||||
search.textContent = '🔍';
|
||||
search.setAttribute('aria-label', 'Suche');
|
||||
search.addEventListener('click', function (e) {
|
||||
if (view === 'thread') { e.preventDefault(); toggleThreadSearch(); }
|
||||
});
|
||||
group.appendChild(search);
|
||||
}
|
||||
|
||||
var auth = document.createElement('a');
|
||||
auth.className = 'rm-auth';
|
||||
auth.href = st.href;
|
||||
auth.textContent = st.in ? (st.name || 'Logout') : 'Login';
|
||||
bar.appendChild(auth);
|
||||
group.appendChild(auth);
|
||||
bar.appendChild(group);
|
||||
|
||||
document.body.appendChild(bar);
|
||||
document.body.classList.add('rm-crumbed');
|
||||
@@ -946,7 +1120,199 @@
|
||||
return n;
|
||||
}
|
||||
|
||||
// search.php ohne Treffer zeigt nur das Suchformular — keine topictitle-
|
||||
// oder forumlink-Zeilen, an denen buildList() sonst seine Karten festmacht.
|
||||
// Erkannt wird das Formular am Forenwaehler select[name="fid[]"]: der
|
||||
// steckt nur im grossen Suchformular, nicht in den kleinen "Search this
|
||||
// forum/topic..."-Formularen auf Foren- und Themenseiten, die denselben
|
||||
// Formularnamen "keywords" verwenden wuerden und sonst faelschlich
|
||||
// getroffen werden koennten.
|
||||
function buildSearchForm() {
|
||||
var picker = document.querySelector('select[name="fid[]"]');
|
||||
var form = picker && picker.closest('form');
|
||||
if (!form) return false;
|
||||
|
||||
// Vorbelegung fuer die mobile Nutzung: Main Forum (Forum-ID 10, siehe
|
||||
// dev/viewforum.php) vorausgewaehlt statt "kein Forum", Unterforen dabei
|
||||
// nicht automatisch mitgesucht — das serverseitige Formular startet sonst
|
||||
// mit leerem Forenwaehler und "Search subforums: Yes".
|
||||
var mainForum = picker.querySelector('option[value="10"]');
|
||||
if (mainForum) mainForum.selected = true;
|
||||
var noSubforums = form.querySelector('input[name="sc"][value="0"]');
|
||||
if (noSubforums) noSubforums.checked = true;
|
||||
|
||||
var root = document.createElement('div');
|
||||
root.className = 'rm-root rm-searchform';
|
||||
|
||||
var title = document.createElement('div');
|
||||
title.className = 'rm-login-title';
|
||||
title.textContent = 'Suche';
|
||||
root.appendChild(title);
|
||||
|
||||
// Schnellzugriffe (View new posts, View your posts, ...) stehen als
|
||||
// eigene Linkliste ausserhalb des Formulars und würden sonst mit dem Rest
|
||||
// der Originalseite verschwinden.
|
||||
var quick = document.querySelector('p.searchbar');
|
||||
if (quick) {
|
||||
var links = quick.querySelectorAll('a');
|
||||
if (links.length) {
|
||||
var chips = document.createElement('div');
|
||||
chips.className = 'rm-search-shortcuts';
|
||||
links.forEach(function (a) {
|
||||
var c = document.createElement('a');
|
||||
c.href = a.href;
|
||||
c.textContent = txt(a);
|
||||
chips.appendChild(c);
|
||||
});
|
||||
root.appendChild(chips);
|
||||
}
|
||||
}
|
||||
|
||||
// Die Textfelder tragen ein inline style="width: 300px", das jede externe
|
||||
// Breitenregel ohne !important sticht.
|
||||
form.querySelectorAll('input[type="text"]').forEach(function (inp) {
|
||||
inp.removeAttribute('style');
|
||||
});
|
||||
|
||||
form.querySelectorAll('input[type="submit"], input[type="reset"]')
|
||||
.forEach(inputToButton);
|
||||
|
||||
root.appendChild(form);
|
||||
replaceBody(root);
|
||||
addCrumbs();
|
||||
log.push('suche=formular');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Eine Suche im Modus "Posts" (Standard beim Suchen ueber die zweite Leiste
|
||||
// im Thread, da deren Formular kein sr setzt) liefert etwas ganz anderes
|
||||
// als eine Themenliste: pro Treffer Autor, Zeitstempel und ein Auszug aus
|
||||
// dem Beitrag selbst, keine topictitle-/forumlink-Zeilen. buildList() wuerde
|
||||
// die darin enthaltenen viewtopic.php-Links trotzdem einsammeln und an
|
||||
// rowKind() falsch klassifizieren, ohne diese eigene Behandlung davor.
|
||||
// Erkannt wird der Treffer-Modus an p.topictitle — als <p>, nicht als <a>
|
||||
// wie das gleichnamige forumlink-Gegenstueck einer Themenzeile.
|
||||
function buildSearchResults() {
|
||||
var hits = document.querySelectorAll('p.topictitle');
|
||||
if (!hits.length) return false;
|
||||
|
||||
var root = document.createElement('div');
|
||||
root.className = 'rm-root rm-hits';
|
||||
|
||||
var summary = document.querySelector('span.titles');
|
||||
var title = document.createElement('div');
|
||||
title.className = 'rm-login-title';
|
||||
title.textContent = summary ? txt(summary) : 'Suchergebnisse';
|
||||
root.appendChild(title);
|
||||
|
||||
// "Searched topic: ... / Search term used: ..." traegt funktionierende
|
||||
// Links (erneute Suche, Sprung zum Thema) und wird deshalb wie das
|
||||
// Formular selbst uebernommen statt aus dem Text nachgebaut.
|
||||
var form = hits[0].closest('form');
|
||||
var infoCell = form && form.querySelector('td.genmed');
|
||||
if (infoCell) {
|
||||
infoCell.classList.add('rm-hits-info');
|
||||
root.appendChild(infoCell);
|
||||
}
|
||||
|
||||
var built = 0;
|
||||
hits.forEach(function (p) {
|
||||
try {
|
||||
var card = buildHit(p);
|
||||
if (card) { root.appendChild(card); built++; }
|
||||
} catch (e) { log.push('treffer: ' + e.message); }
|
||||
});
|
||||
log.push('treffer=' + built);
|
||||
if (!built) return false;
|
||||
|
||||
// Muss vor replaceBody() laufen: pageState() liest das Original-DOM.
|
||||
var pg = pageState();
|
||||
if (pg.total > 1) pageNav(root, pg);
|
||||
|
||||
replaceBody(root);
|
||||
addCrumbs();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ein Treffer besteht aus drei aufeinanderfolgenden <tr> derselben Tabelle:
|
||||
// Titelzeile (Forum/Thema), Autor- und Betreffzeile, Antworten/Views samt
|
||||
// Beitragsauszug. Die Zuordnung laeuft ueber nextElementSibling statt ueber
|
||||
// Klassen, weil alle drei row1/row2-Zeilen sonst gleich aussehen.
|
||||
function buildHit(p) {
|
||||
var titleRow = p.closest('tr');
|
||||
var infoRow = titleRow && titleRow.nextElementSibling;
|
||||
var bodyRow = infoRow && infoRow.nextElementSibling;
|
||||
|
||||
var forumA = p.querySelector('a[href*="viewforum.php"]');
|
||||
var topicA = p.querySelector('a[href*="viewtopic.php"]');
|
||||
if (!topicA) return null;
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.className = 'rm-hit';
|
||||
|
||||
var ctx = document.createElement('a');
|
||||
ctx.className = 'rm-hit-ctx';
|
||||
ctx.href = topicA.href;
|
||||
ctx.textContent = breakable((forumA ? txt(forumA) + ' › ' : '') + txt(topicA));
|
||||
el.appendChild(ctx);
|
||||
|
||||
var author = '', posted = '', subjectA = null;
|
||||
if (infoRow) {
|
||||
var ua = infoRow.querySelector('.postauthor');
|
||||
author = ua ? txt(ua) : '';
|
||||
subjectA = infoRow.querySelector('a[href*="viewtopic.php"]');
|
||||
var pm = txt(infoRow).match(/Posted:\s*(.+)$/i);
|
||||
if (pm) posted = pm[1].trim();
|
||||
}
|
||||
|
||||
var head = document.createElement('div');
|
||||
head.className = 'rm-hit-head';
|
||||
var who = document.createElement('span');
|
||||
who.className = 'rm-hit-user';
|
||||
who.textContent = author || 'Gast';
|
||||
head.appendChild(who);
|
||||
if (posted) {
|
||||
var when = document.createElement('span');
|
||||
when.className = 'rm-hit-when';
|
||||
when.textContent = posted;
|
||||
head.appendChild(when);
|
||||
}
|
||||
el.appendChild(head);
|
||||
|
||||
if (bodyRow) {
|
||||
// txt() haengt "Replies:" und "Views:" ohne Trennzeichen aneinander,
|
||||
// weil das <br /> dazwischen keinen Leerraum hinterlaesst — deshalb
|
||||
// einzeln herausgegriffen und ueber dieselbe metaRow() wie bei
|
||||
// Themenkarten mit " · " verbunden.
|
||||
var metaTxt = txt(bodyRow.querySelector('.postdetails'));
|
||||
var reM = metaTxt.match(/Replies:\s*([\d.,]+)/i);
|
||||
var viM = metaTxt.match(/Views:\s*([\d.,]+)/i);
|
||||
var metaEl = metaRow([reM ? reM[1] + ' Antworten' : '',
|
||||
viM ? viM[1] + ' Views' : '']);
|
||||
if (metaEl) el.appendChild(metaEl);
|
||||
|
||||
var pb = bodyRow.querySelector('.postbody');
|
||||
if (pb) {
|
||||
var wrap = document.createElement('div');
|
||||
wrap.className = 'rm-body';
|
||||
wrap.appendChild(pb);
|
||||
el.appendChild(wrap);
|
||||
}
|
||||
}
|
||||
|
||||
var jump = document.createElement('a');
|
||||
jump.className = 'rm-hit-go';
|
||||
jump.href = (subjectA || topicA).href;
|
||||
jump.textContent = 'Zum Beitrag ↓';
|
||||
el.appendChild(jump);
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function buildList() {
|
||||
if (buildSearchForm()) return;
|
||||
if (buildSearchResults()) return;
|
||||
|
||||
// Dokumentreihenfolge entscheidet: in einer Forenzeile steht der Forenname
|
||||
// vor dem Link auf den letzten Beitrag. Ohne diese Reihenfolge faellt die
|
||||
// Zeile als Thema an, der Forenname verschwindet und nur die Beschreibung
|
||||
@@ -1032,6 +1398,47 @@
|
||||
// Thread
|
||||
// ==========================================================================
|
||||
|
||||
// "Search this topic..." steht dreifach identisch im Original-DOM (Kopf,
|
||||
// Fuss, ueber der Beitragsliste), samt versteckter Felder fuer Themen-Id
|
||||
// und Scope (sf=msgonly). replaceBody() versteckt das nur per Klasse, das
|
||||
// erste Formular bleibt im DOM erreichbar und wird beim ersten Antippen
|
||||
// der Lupe unveraendert in die zweite Leiste verschoben statt neu gebaut —
|
||||
// wie das Login-Formular in buildLogin().
|
||||
var threadSearchBar = null;
|
||||
|
||||
function buildThreadSearch() {
|
||||
var form = document.querySelector('#topic-search');
|
||||
if (!form) return null;
|
||||
|
||||
// Die Originalseite emuliert einen Platzhalter per onclick/onblur statt
|
||||
// per placeholder-Attribut.
|
||||
var inp = form.querySelector('input[name="keywords"]');
|
||||
if (inp) {
|
||||
inp.removeAttribute('onclick');
|
||||
inp.removeAttribute('onblur');
|
||||
if (inp.value === 'Search this topic…') inp.value = '';
|
||||
inp.placeholder = 'In diesem Thema suchen';
|
||||
}
|
||||
var submit = form.querySelector('input[type="submit"]');
|
||||
if (submit) { submit.value = 'Suchen'; inputToButton(submit); }
|
||||
|
||||
var bar = document.createElement('div');
|
||||
bar.className = 'rm-search-bar';
|
||||
bar.appendChild(form);
|
||||
document.body.appendChild(bar);
|
||||
return bar;
|
||||
}
|
||||
|
||||
function toggleThreadSearch() {
|
||||
if (!threadSearchBar) threadSearchBar = buildThreadSearch();
|
||||
if (!threadSearchBar) return;
|
||||
var open = threadSearchBar.classList.toggle('rm-open');
|
||||
if (open) {
|
||||
var inp = threadSearchBar.querySelector('input[type="text"]');
|
||||
if (inp) inp.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -1234,6 +1641,8 @@
|
||||
if (!td.children.length && !txt(td)) td.classList.add('rm-hidden');
|
||||
});
|
||||
|
||||
inputToButton(form.querySelector('input[type="submit"]'));
|
||||
|
||||
root.appendChild(form);
|
||||
replaceBody(root);
|
||||
addCrumbs();
|
||||
@@ -1287,7 +1696,7 @@
|
||||
if (DEBUG) {
|
||||
var d = document.createElement('div');
|
||||
d.className = 'rm-dbg';
|
||||
d.textContent = 'RIN Mobile v3.8\n' + log.join('\n');
|
||||
d.textContent = 'RIN Mobile v3.13\n' + log.join('\n');
|
||||
document.body.insertBefore(d, document.body.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user