Forum.hr Mobile Friendly

Makes forum.hr easier to read on mobile

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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');
})();