Makes forum.hr easier to read on mobile
// ==UserScript==
// @name Forum.hr Mobile Friendly
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Makes forum.hr easier to read on mobile
// @author Riche
// @match https://www.forum.hr/*
// @match https://forum.hr/*
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
console.log('[Forum.hr Mobile] v6.1 starting...');
// === 1. VIEWPORT ===
if (!document.querySelector('meta[name="viewport"]')) {
const meta = document.createElement('meta');
meta.name = 'viewport';
meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=5.0';
(document.head || document.documentElement).appendChild(meta);
}
// === 2. BASE STYLES ===
GM_addStyle(`
html, body {
margin: 0 !important;
padding: 0 !important;
min-width: 0 !important;
overflow-x: hidden !important;
}
body {
width: 100% !important;
max-width: 100vw !important;
}
/* Constrain large media, but NEVER scale up small icons/smileys/buttons */
iframe, video, embed, object {
max-width: 100% !important;
height: auto !important;
}
img:not([src*="buttons/"]):not([src*="icon_"]):not([src*="smilies/"]):not(.inlineimg):not([alt*="Edit"]):not([alt*="Reply"]):not([alt*="Multi"]):not([alt*="Thanks"]):not([alt*="Warn"]) {
max-width: 100% !important;
height: auto !important;
}
*, *::before, *::after {
box-sizing: border-box !important;
}
* {
white-space: normal !important;
overflow-wrap: break-word !important;
word-wrap: break-word !important;
word-break: break-word !important;
}
pre, code, .code, .phpcode, .htmlcode {
white-space: pre-wrap !important;
word-break: break-all !important;
}
td.sidebar_column, td.sidebar_column_spacer, .sidebar_column {
display: none !important;
width: 0 !important;
padding: 0 !important;
margin: 0 !important;
}
/* Constrain ALL direct children of body */
body > div, body > table, body > form {
max-width: 100vw !important;
width: 100% !important;
}
.page {
width: 100% !important;
max-width: 100vw !important;
min-width: 0 !important;
padding: 0 !important;
margin: 0 !important;
overflow: hidden !important;
}
.page > div {
padding-left: 4px !important;
padding-right: 4px !important;
max-width: 100% !important;
}
#posts, #__xclaimwords_wrapper {
width: 100% !important;
max-width: 100vw !important;
overflow: hidden !important;
}
/* CRITICAL: constrain xclaimwords and any wide widgets inside posts */
#__xclaimwords_wrapper,
#__xclaimwords_wrapper > div,
#__xclaimwords_wrapper iframe,
#__xclaimwords_wrapper img {
max-width: 100% !important;
width: 100% !important;
overflow: hidden !important;
}
table {
max-width: 100% !important;
}
/* Aggressive ad/iframe constraining */
[id^="div-gpt-ad-"], #header_banner, [id^="google_ads_iframe"] {
max-width: 100% !important;
width: 100% !important;
overflow: hidden !important;
}
[id^="google_ads_iframe"] iframe, [id^="google_ads_iframe"] div {
max-width: 100% !important;
width: 100% !important;
}
td.alt1 div[style*="margin:5px"], td.alt1 div[style*="margin: 5px"] {
margin: 4px !important;
}
td.alt1 blockquote, td.alt1 .quote {
margin: 4px !important;
padding: 4px !important;
}
@media screen and (max-width: 768px) {
/* Post tables - use table-layout fixed instead of flexbox */
table[id^="post"] {
table-layout: fixed !important;
width: 100% !important;
max-width: 100% !important;
}
table[id^="post"] > tbody > tr {
width: 100% !important;
}
/* User column */
table[id^="post"] td:first-child {
width: 110px !important;
min-width: 110px !important;
max-width: 110px !important;
padding: 4px !important;
overflow: hidden !important;
}
/* Post content column */
table[id^="post"] td:last-child {
width: auto !important;
min-width: 0 !important;
padding: 4px !important;
overflow: hidden !important;
}
/* Constrain nested media */
table[id^="post"] td:last-child iframe,
table[id^="post"] td:last-child video,
table[id^="post"] td:last-child embed,
table[id^="post"] td:last-child div {
max-width: 100% !important;
}
/* User column */
table[id^="post"] td.alt2 {
font-size: 14px !important;
}
table[id^="post"] td.alt2 img {
max-width: 100px !important;
max-height: 100px !important;
}
/* Post content - MUCH larger font (compensates for viewport scale) */
#posts table[id^="post"] td.alt1 {
font-size: 26px !important;
line-height: 1.5 !important;
word-wrap: break-word !important;
overflow-wrap: break-word !important;
}
/* Quotes and nested tables - keep as tables, just make wide */
#posts table[id^="post"] td.alt1 table {
width: 100% !important;
max-width: 100% !important;
}
#posts table[id^="post"] td.alt1 table td {
font-size: 26px !important;
line-height: 1.5 !important;
word-break: normal !important;
overflow-wrap: normal !important;
}
/* Page nav tables - larger tappable font */
.pagenav table {
width: 100% !important;
max-width: 100% !important;
}
.pagenav table tr {
display: flex !important;
flex-wrap: wrap !important;
justify-content: center !important;
}
.pagenav table td {
display: block !important;
padding: 6px 10px !important;
width: auto !important;
}
.pagenav .smallfont, .pagenav a.smallfont, .pagenav td.vbmenu_control {
font-size: 22px !important;
}
}
`);
// === 3. MOBILE FIXES ===
function applyFixes() {
const vw = window.innerWidth;
if (vw > 768) return;
// Strip width attributes
document.querySelectorAll('[width]').forEach(el => {
if (!el.hasAttribute('data-fhm-width-removed')) {
el.removeAttribute('width');
el.setAttribute('data-fhm-width-removed', '1');
}
});
document.querySelectorAll('[cellpadding]').forEach(el => {
if (!el.hasAttribute('data-fhm-padding-removed')) {
el.setAttribute('cellpadding', '0');
el.setAttribute('data-fhm-padding-removed', '1');
}
});
document.querySelectorAll('[cellspacing]').forEach(el => {
if (!el.hasAttribute('data-fhm-spacing-removed')) {
el.setAttribute('cellspacing', '0');
el.setAttribute('data-fhm-spacing-removed', '1');
}
});
document.querySelectorAll('[style*="min-width"]').forEach(el => {
el.style.minWidth = '0';
});
document.querySelectorAll('[nowrap]').forEach(el => {
el.removeAttribute('nowrap');
});
// Page container - strip inline styles and force fit
document.querySelectorAll('.page').forEach(el => {
el.removeAttribute('style'); // strip vBulletin inline width
el.style.setProperty('width', '100%', 'important');
el.style.setProperty('max-width', '100vw', 'important');
el.style.setProperty('min-width', '0', 'important');
el.style.setProperty('padding', '0', 'important');
el.style.setProperty('margin', '0', 'important');
el.style.setProperty('overflow', 'hidden', 'important');
});
// Constrain body wrapper divs
document.querySelectorAll('body > div').forEach(el => {
el.removeAttribute('style');
el.style.setProperty('max-width', '100vw', 'important');
el.style.setProperty('width', '100%', 'important');
el.style.setProperty('overflow', 'hidden', 'important');
});
document.querySelectorAll('.page > div').forEach(el => {
el.style.paddingLeft = '4px';
el.style.paddingRight = '4px';
el.style.maxWidth = '100%';
});
// Posts container + xclaimwords
document.querySelectorAll('#posts, #__xclaimwords_wrapper').forEach(el => {
el.style.width = '100%';
el.style.maxWidth = '100vw';
el.style.overflow = 'hidden';
});
// Force xclaimwords children to fit
document.querySelectorAll('#__xclaimwords_wrapper *').forEach(el => {
el.style.maxWidth = '100%';
});
// Header table
document.querySelectorAll('body > table').forEach(table => {
table.style.width = '100%';
table.style.maxWidth = '100vw';
table.style.tableLayout = 'fixed';
table.querySelectorAll('td').forEach(td => {
td.style.maxWidth = '50%';
td.style.overflow = 'hidden';
});
});
// Ad banners - VERY aggressive
document.querySelectorAll('[id^="div-gpt-ad-"], #header_banner, [id^="google_ads_iframe"]').forEach(el => {
el.style.maxWidth = '100%';
el.style.width = '100%';
el.style.overflow = 'hidden';
el.querySelectorAll('iframe, div').forEach(child => {
child.style.maxWidth = '100%';
child.style.width = '100%';
});
});
// Also constrain all iframes globally
document.querySelectorAll('iframe').forEach(iframe => {
iframe.style.maxWidth = '100%';
iframe.style.width = '100%';
});
// Fix ALL images in posts aggressively
document.querySelectorAll('table[id^="post"] img, .inlineimg, img[src*="buttons/"], img[src*="smilies/"]').forEach(img => {
const src = (img.src || '').toLowerCase();
const alt = (img.alt || '').toLowerCase();
const isQuoteBtn = src.includes('quote.gif');
const isSmallIcon = !isQuoteBtn && (src.includes('buttons/') || src.includes('icon_') || src.includes('smilies/') ||
alt.includes('quote') || alt.includes('edit') || alt.includes('reply') ||
alt.includes('multi') || alt.includes('thanks') || alt.includes('warn') ||
img.classList.contains('inlineimg'));
if (isQuoteBtn) {
img.removeAttribute('width');
img.removeAttribute('height');
img.style.setProperty('max-width', '60px', 'important');
img.style.setProperty('width', 'auto', 'important');
img.style.setProperty('height', 'auto', 'important');
} else if (isSmallIcon) {
// Small icons: force tiny size with !important
img.removeAttribute('width');
img.removeAttribute('height');
img.style.setProperty('max-width', '24px', 'important');
img.style.setProperty('width', 'auto', 'important');
img.style.setProperty('height', 'auto', 'important');
}
});
// Fix post font sizes via JS
document.querySelectorAll('table[id^="post"] td.alt1').forEach(td => {
td.style.setProperty('font-size', '26px', 'important');
td.style.setProperty('line-height', '1.5', 'important');
});
document.querySelectorAll('table[id^="post"] td.alt2').forEach(td => {
td.style.setProperty('font-size', '14px', 'important');
});
// Fix quotes - keep as tables but ensure full width and font size
document.querySelectorAll('table[id^="post"] td.alt1 table').forEach(innerTable => {
innerTable.style.setProperty('width', '100%', 'important');
innerTable.style.setProperty('max-width', '100%', 'important');
innerTable.querySelectorAll('td').forEach(td => {
td.style.setProperty('font-size', '26px', 'important');
td.style.setProperty('line-height', '1.5', 'important');
td.style.setProperty('word-break', 'normal', 'important');
td.style.setProperty('overflow-wrap', 'normal', 'important');
});
});
// Toolbar tables
document.querySelectorAll('table.tborder').forEach(table => {
const hasThreadTools = table.querySelector('#threadtools, #threadsearch, #displaymodes');
if (!hasThreadTools) return;
table.style.width = '100%';
table.style.maxWidth = '100%';
const row = table.querySelector('tr');
if (row) {
row.style.display = 'flex';
row.style.flexWrap = 'wrap';
row.querySelectorAll('td').forEach(td => {
td.style.display = 'block';
td.style.padding = '4px 8px';
td.style.width = 'auto';
});
}
});
// Forum listings
document.querySelectorAll('table.tborder').forEach(table => {
if (!table.querySelector('thead .column_moderator')) return;
table.style.tableLayout = 'fixed';
table.style.width = '100%';
table.style.maxWidth = '100%';
const headerRow = table.querySelector('thead tr');
if (headerRow) headerRow.style.display = 'none';
table.querySelectorAll('tbody > tr[align="center"]').forEach(row => {
const titleCell = row.querySelector('td.field_title');
if (!titleCell) return;
row.style.borderBottom = '1px solid #555';
row.querySelectorAll('td.field_spacer, td.field_threads, td.field_posts, td.field_moderator').forEach(el => {
el.style.display = 'none';
});
const lastPost = row.querySelector('td.field_last_post');
if (titleCell) {
titleCell.style.display = 'table-cell';
titleCell.style.width = '55%';
titleCell.style.minWidth = '0';
titleCell.style.maxWidth = '55%';
titleCell.style.padding = '6px 4px';
titleCell.style.verticalAlign = 'top';
titleCell.style.overflow = 'hidden';
}
if (lastPost) {
lastPost.style.display = 'table-cell';
lastPost.style.width = '45%';
lastPost.style.minWidth = '0';
lastPost.style.maxWidth = '45%';
lastPost.style.padding = '6px 4px';
lastPost.style.verticalAlign = 'top';
lastPost.style.fontSize = '11px';
lastPost.style.overflow = 'hidden';
}
});
});
// Thread listings
document.querySelectorAll('table#threadslist').forEach(table => {
if (table.hasAttribute('data-fhm-processed')) return;
table.setAttribute('data-fhm-processed', '1');
console.log('[Forum.hr Mobile] Processing thread list');
table.style.display = 'block';
table.style.width = '100%';
table.style.maxWidth = '100%';
table.querySelectorAll('tr').forEach(row => {
if (row.querySelector('.thead')) {
row.style.display = 'none';
return;
}
const titleCell = row.querySelector('td.field_title');
if (!titleCell) return;
const lastPost = row.querySelector('td.field_last_post');
row.style.display = 'block';
row.style.width = '100%';
row.style.borderBottom = '1px solid #555';
row.style.padding = '6px 4px';
row.querySelectorAll('td').forEach(td => {
td.style.display = 'none';
});
if (titleCell) {
titleCell.style.display = 'block';
titleCell.style.width = '100%';
titleCell.style.padding = '2px 0';
const multipage = titleCell.querySelector('span.smallfont');
if (multipage) {
multipage.style.display = 'block';
multipage.style.fontSize = '10px';
}
}
if (lastPost) {
lastPost.style.display = 'block';
lastPost.style.width = '100%';
lastPost.style.padding = '2px 0';
lastPost.style.fontSize = '11px';
lastPost.style.color = '#888';
}
});
});
// Page nav - larger font for tappability
document.querySelectorAll('.pagenav .smallfont, .pagenav a.smallfont').forEach(el => {
el.style.setProperty('font-size', '22px', 'important');
});
// Nav buttons
document.querySelectorAll('table.tborder').forEach(table => {
const navRow = table.querySelector('tr[align="center"]');
if (navRow && navRow.querySelector('.vbmenu_control')) {
navRow.style.display = 'flex';
navRow.style.flexWrap = 'wrap';
navRow.style.justifyContent = 'center';
navRow.querySelectorAll('td').forEach(td => {
td.style.display = 'block';
td.style.padding = '4px 8px';
});
}
});
// Whats going on
const wgo = document.getElementById('whats_going_on');
if (wgo) {
wgo.style.display = 'block';
wgo.style.width = '100%';
wgo.style.maxWidth = '100%';
}
// NUCLEAR: use viewport meta to scale entire page
if (vw <= 768 && !window.__fhm_scaled) {
const page = document.querySelector('.page');
if (page) {
const pageW = page.scrollWidth;
if (pageW > vw) {
const scale = (vw - 4) / pageW;
const meta = document.querySelector('meta[name="viewport"]');
if (meta) {
meta.content = 'width=' + pageW + ', initial-scale=' + scale.toFixed(4) + ', maximum-scale=5.0';
console.log('[Forum.hr Mobile] VIEWPORT: width=' + pageW + ' scale=' + scale.toFixed(4));
window.__fhm_scaled = true;
}
}
}
}
}
// === 4. RUN ===
function init() {
applyFixes();
fixThreadLinks();
console.log('[Forum.hr Mobile] Initialized');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
window.addEventListener('load', function() { applyFixes(); fixThreadLinks(); });
let resizeTimer;
window.addEventListener('resize', function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(applyFixes, 300);
});
let checkCount = 0;
const interval = setInterval(function() {
applyFixes();
fixThreadLinks();
if (++checkCount > 20) clearInterval(interval);
}, 500);
// === 5. THREAD LINKS: goto=newpost ===
function fixThreadLinks() {
document.querySelectorAll('a[href*="showthread.php?t="]').forEach(link => {
const href = link.getAttribute('href') || '';
// Only fix links that don't already have goto=newpost
if (href.includes('goto=newpost')) return;
// Only fix links that have ?t= (thread ID links)
const match = href.match(/showthread\.php\?(\d+)?t=(\d+)/);
if (!match) return;
const threadId = match[2];
const newHref = 'showthread.php?goto=newpost&t=' + threadId;
link.setAttribute('href', newHref);
});
// Remove "go to first new post" icon since thread title now links there
document.querySelectorAll('a[id^="thread_gotonew_"]').forEach(el => el.remove());
}
// Aggressive MutationObserver: re-fix images whenever anything changes
const observer = new MutationObserver(function(mutations) {
let shouldFix = false;
mutations.forEach(function(mutation) {
if (mutation.type === 'attributes') {
const target = mutation.target;
if (target.tagName === 'IMG') {
const src = (target.src || '').toLowerCase();
const isQuoteBtn = src.includes('quote.gif');
if (src.includes('buttons/') || src.includes('smilies/') || target.classList.contains('inlineimg')) {
// Force fix this specific image immediately
target.removeAttribute('width');
target.removeAttribute('height');
if (isQuoteBtn) {
target.style.setProperty('max-width', '60px', 'important');
} else {
target.style.setProperty('max-width', '24px', 'important');
}
target.style.setProperty('width', 'auto', 'important');
target.style.setProperty('height', 'auto', 'important');
}
}
}
if (mutation.type === 'childList') {
shouldFix = true;
}
});
if (shouldFix) { applyFixes(); fixThreadLinks(); }
});
window.addEventListener('load', function() {
observer.observe(document.body, {
attributes: true,
attributeFilter: ['style', 'width', 'height'],
childList: true,
subtree: true
});
});
console.log('[Forum.hr Mobile] v6.1 loaded');
})();