// ==UserScript==
// @name Bitcointalk Mobile Enhancer v1.1
// @namespace Violentmonkey Scripts
// @version 1.1.1
// @description Tema moderno AMOLED, quote leggibili, pulsanti, sMerit via AJAX, toggle dark/light, barra progresso sotto avatar in thread (fix progress)
// @match https://bitcointalk.org/*
// @grant none
// @author *ace*
// @license MIT
// ==/UserScript==
(function () {
'use strict';
let sMerit = null;
// Tabella rank
const rankTable = [
{ name: "Brand New", merit: 0, activity: 0 },
{ name: "Newbie", merit: 0, activity: 1 },
{ name: "Jr. Member", merit: 1, activity: 30 },
{ name: "Member", merit: 10, activity: 60 },
{ name: "Full Member", merit: 100, activity: 120 },
{ name: "Sr. Member", merit: 250, activity: 240 },
{ name: "Hero Member", merit: 500, activity: 480 },
{ name: "Legendary", merit: 1000, activity: 775 }
];
// Carica sMerit via AJAX senza causare redirect visibili
function fetchSmerit() {
const meritPage = 'https://bitcointalk.org/index.php?action=merit';
fetch(meritPage, { credentials: 'include', redirect: 'manual' })
.then(res => {
if (res.type === 'opaqueredirect' || res.status === 0 || res.status === 302) {
sMerit = '?';
updateSmeritIndicator();
return null;
}
return res.text();
})
.then(html => {
if (!html) return;
const match = html.match(/You\s+have\s+(?:<b>)?(\d+)(?:<\/b>)?\s+sendable/i);
sMerit = match ? match[1] : '?';
updateSmeritIndicator();
})
.catch(() => {
sMerit = 'x';
updateSmeritIndicator();
});
}
// Indicatore sMerit
function updateSmeritIndicator() {
let indicator = document.getElementById('smerit-indicator');
if (!indicator) {
indicator = document.createElement('div');
indicator.id = 'smerit-indicator';
document.body.appendChild(indicator);
}
indicator.textContent = `🪙 sMerit: ${sMerit ?? '...'}`;
}
// Quote espandibili
function fixQuotes() {
document.querySelectorAll('.quote').forEach(quote => {
if (quote.classList.contains('enhanced')) return;
quote.classList.add('enhanced');
if (quote.scrollHeight > 140) {
const button = document.createElement('div');
button.className = 'show-more';
button.textContent = 'Mostra tutto';
button.onclick = () => {
quote.classList.add('expanded');
button.remove();
};
quote.appendChild(button);
}
});
}
// Pulsanti mobile
function addButtons() {
document.querySelectorAll('td[class^="windowbg"] > div:nth-child(2)').forEach(post => {
if (post.closest('.windowbg:first-child')) return;
if (post.querySelector('.mobile-buttons')) return;
const links = post.querySelectorAll('a');
let quote, report, merit;
links.forEach(a => {
const href = a.getAttribute('href') || '';
if (href.includes('quote')) quote = a;
if (href.includes('report')) report = a;
if (href.includes('merit')) merit = a;
});
const box = document.createElement('div');
box.className = 'mobile-buttons';
if (quote) {
const q = quote.cloneNode(true);
q.textContent = 'Quote';
box.appendChild(q);
}
if (merit) {
const m = merit.cloneNode(true);
m.textContent = '+Merit';
box.appendChild(m);
}
if (report) {
const r = report.cloneNode(true);
r.textContent = 'Report';
box.appendChild(r);
}
post.appendChild(box);
});
}
// Barra progresso sotto avatar nei thread (fix: parte da 0% a ogni cambio rank)
function addRankBarsInThreads() {
document.querySelectorAll('td.poster_info').forEach(avatarCell => {
if (avatarCell.querySelector('.rank-progress-bar')) return;
const text = avatarCell.textContent;
const meritMatch = text.match(/Merit:\s*(\d+)/i);
const activityMatch = text.match(/Activity:\s*(\d+)/i);
if (!meritMatch || !activityMatch) return;
const merit = parseInt(meritMatch[1], 10);
const activity = parseInt(activityMatch[1], 10);
let currentRankIndex = 0;
for (let i = 0; i < rankTable.length; i++) {
if (merit >= rankTable[i].merit && activity >= rankTable[i].activity) {
currentRankIndex = i;
}
}
const currentRank = rankTable[currentRankIndex];
const nextRank = rankTable[Math.min(currentRankIndex + 1, rankTable.length - 1)];
// Calcola progresso relativo tra rank attuale e successivo
const meritProgress = Math.min(
(merit - currentRank.merit) / (nextRank.merit - currentRank.merit || 1),
1
);
const activityProgress = Math.min(
(activity - currentRank.activity) / (nextRank.activity - currentRank.activity || 1),
1
);
// Percentuale finale = minimo tra merit e activity
const totalProgress = Math.min(meritProgress, activityProgress) * 100;
const bar = document.createElement('div');
bar.className = 'rank-progress-bar';
const fill = document.createElement('div');
fill.className = 'rank-progress-fill';
fill.style.width = totalProgress + '%';
bar.appendChild(fill);
avatarCell.appendChild(bar);
});
}
// Tema chiaro
const lightTheme = `
body {
font-family: "Segoe UI", sans-serif !important;
font-size: 16px;
background: #f9fafb !important;
color: #222;
}
table, .windowbg, .windowbg2 {
background: #fff !important;
border-radius: 10px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
td.poster_info {
width: 70px !important;
max-width: 70px !important;
background: #fff !important;
text-align: center;
padding: 4px !important;
}
td.poster_info img {
max-width: 48px !important;
height: auto;
border-radius: 6px;
}
td.td_headerandpost {
background: #fff !important;
color: #222 !important;
}
.quote {
background: #e0f2fe;
border-left: 4px solid #3b82f6;
}
.mobile-buttons a {
background: #3b82f6;
color: white !important;
}
.mobile-buttons a:hover {
background: #1d4ed8;
}
#smerit-indicator {
background: #10b981;
color: white;
}
.rank-progress-bar {
background: #ddd;
}
.rank-progress-fill {
background: #3b82f6;
height: 6px;
border-radius: 3px;
}
`;
// Tema dark AMOLED leggibile
const darkTheme = `
body {
font-family: "Segoe UI", sans-serif !important;
font-size: 16px;
background: #000000 !important;
color: #e5e7eb !important;
}
table, .windowbg, .windowbg2 {
background: #0a0a0a !important;
color: #f5f5f5 !important;
border-radius: 10px;
box-shadow: 0 0 8px rgba(0,0,0,0.8);
}
td.poster_info {
width: 70px !important;
max-width: 70px !important;
background: #0a0a0a !important;
text-align: center;
padding: 4px !important;
color: #f5f5f5 !important;
}
td.poster_info img {
max-width: 48px !important;
border-radius: 6px;
box-shadow: 0 0 6px rgba(0,0,0,0.8);
}
td.td_headerandpost {
background: #0a0a0a !important;
color: #f5f5f5 !important;
}
.quote {
background: #111111 !important;
border-left: 4px solid #00d4ff !important;
color: #ffffff !important;
}
.quote cite, .quote .quoteheader {
color: #00d4ff !important;
font-weight: bold;
}
.mobile-buttons a {
background: linear-gradient(135deg, #00d4ff, #0077ff);
color: white !important;
box-shadow: 0 0 6px rgba(0, 122, 255, 0.6);
}
.mobile-buttons a:hover {
background: linear-gradient(135deg, #00aaff, #0055cc);
}
#smerit-indicator {
background: linear-gradient(135deg, #10b981, #065f46);
color: white !important;
box-shadow: 0 0 6px rgba(16, 185, 129, 0.6);
}
.rank-progress-bar {
background: #1f1f1f;
}
.rank-progress-fill {
background: linear-gradient(90deg, #00d4ff, #0077ff);
height: 6px;
border-radius: 3px;
}
a.board {
color: #00d4ff !important;
font-weight: bold;
}
/* --- FIX PER MERIT E ACTIVITY --- */
.smerit_received, .smerit_given, .activity {
color: #00d4ff !important;
font-weight: bold;
background: #111111 !important;
padding: 2px 4px;
border-radius: 4px;
display: inline-block;
}
.smerit_received a, .smerit_given a, .activity a {
color: #00d4ff !important;
text-decoration: none !important;
}
.smerit_received:hover, .smerit_given:hover, .activity:hover {
color: #ffffff !important;
background: #0077ff !important;
}
.poster_info small {
color: #a0a0a0 !important;
font-size: 11px;
}
/* --- LINK NEI POST --- */
a:link, a:visited {
color: #00d4ff !important;
text-decoration: none !important;
}
a:hover {
color: #ffffff !important;
text-decoration: underline !important;
}
/* --- MESSAGGI DI SISTEMA E INFO --- */
.smalltext, .smalltext a {
color: #a0a0a0 !important;
}
.smalltext a:hover {
color: #00d4ff !important;
}
`;
const commonStyles = `
.quote {
max-height: 120px;
overflow: hidden;
padding: 8px;
border-radius: 6px;
position: relative;
margin: 6px 0;
}
.quote.expanded {
max-height: none !important;
}
.quote .show-more {
position: absolute;
bottom: 4px;
right: 6px;
font-size: 12px;
background: rgba(0,0,0,0.3);
color: #fff;
padding: 2px 6px;
border-radius: 4px;
cursor: pointer;
}
.mobile-buttons {
display: flex;
gap: 6px;
margin-top: 8px;
flex-wrap: wrap;
font-size: 14px;
}
.mobile-buttons a {
padding: 6px 10px;
border-radius: 20px;
text-decoration: none;
transition: background 0.2s;
}
#smerit-indicator {
position: fixed;
top: 14px;
left: 14px;
padding: 8px 14px;
font-size: 14px;
border-radius: 12px;
z-index: 9999;
font-weight: bold;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
#theme-toggle {
position: fixed;
top: 14px;
right: 14px;
background: #6b7280;
color: white;
padding: 6px 10px;
font-size: 13px;
border-radius: 10px;
z-index: 9999;
cursor: pointer;
}
.rank-progress-bar {
width: 100%;
margin-top: 4px;
}
`;
const style = document.createElement("style");
document.head.appendChild(style);
function applyTheme(theme) {
const css = (theme === 'dark' ? darkTheme : lightTheme) + commonStyles;
style.textContent = css;
localStorage.setItem('bitcointalk-theme', theme);
}
// Toggle tema
const toggle = document.createElement("div");
toggle.id = "theme-toggle";
toggle.textContent = "🌓 Tema";
toggle.onclick = () => {
const current = localStorage.getItem('bitcointalk-theme') === 'dark' ? 'light' : 'dark';
applyTheme(current);
};
document.body.appendChild(toggle);
// Applica tema
applyTheme(localStorage.getItem('bitcointalk-theme') || 'light');
// Avvio
updateSmeritIndicator();
// Controlla se loggato
if (document.querySelector('a[href*="action=profile"]')) {
fetchSmerit();
}
addButtons();
fixQuotes();
addRankBarsInThreads();
// Rilancia ogni 1.5s
setInterval(() => {
addButtons();
fixQuotes();
addRankBarsInThreads();
}, 1500);
})();