F-List BBCode Tables Enabler

Adds support for [table], [tr], [th], [td] BBCode on F-List profiles and allows custom classes.

Verzia zo dňa 01.06.2025. Pozri najnovšiu verziu.

// ==UserScript==
// @name         F-List BBCode Tables Enabler
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Adds support for [table], [tr], [th], [td] BBCode on F-List profiles and allows custom classes.
// @author       Your Name
// @match        https://www.f-list.net/c/*
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // Function to convert custom table BBCode to HTML
    function convertBbcodeInBlock(blockElement) {
        if (!blockElement || typeof blockElement.innerHTML !== 'string') {
            return;
        }
        let html = blockElement.innerHTML;
        const originalHtml = html; // Keep a copy to see if changes were made

        // [table class="custom-class"]
        // Adds "fl-generated-table" for easier selection by general CSS rules from this script.
        html = html.replace(/\[table\s+class="([^"]+)"\]/gi, (match, className) => {
            return `<table class="${className} fl-generated-table">`;
        });
        // [table]
        html = html.replace(/\[table\]/gi, '<table class="fl-generated-table">');
        html = html.replace(/\[\/table\]/gi, '</table>');

        // [tr]
        html = html.replace(/\[tr\]/gi, '<tr>');
        html = html.replace(/\[\/tr\]/gi, '</tr>');

        // [th]
        html = html.replace(/\[th\]/gi, '<th>');
        html = html.replace(/\[\/th\]/gi, '</th>');

        // [td]
        html = html.replace(/\[td\]/gi, '<td>');
        html = html.replace(/\[\/td\]/gi, '</td>');

        // Only update innerHTML if changes were actually made by this function
        if (html !== originalHtml) {
            blockElement.innerHTML = html;
        }
    }

    // Function to process all relevant blocks on the page
    function processPageForTables() {
        const formattedBlocks = document.querySelectorAll('div.FormattedBlock');
        formattedBlocks.forEach(block => {
            convertBbcodeInBlock(block);
        });
    }

    // Inject some default CSS for the tables.
    // You can define your own styles for classes like "my-custom-table" here too,
    // or use a separate CSS manager like Stylus.
    GM_addStyle(`
        table.fl-generated-table {
            border-collapse: collapse;
            width: auto; /* Default: content width. Use '100%' for full width of container. */
            margin: 1em 0;
            font-size: 0.9em; /* Adjust as needed */
            border: 1px solid #666; /* A neutral border color */
            /* For dark themes, you might prefer:
               border: 1px solid #444;
               color: #ccc;
            */
        }
        table.fl-generated-table th,
        table.fl-generated-table td {
            border: 1px solid #666; /* Cell borders */
            padding: 6px 8px;      /* Cell padding */
            text-align: left;
            /* For dark themes:
               border: 1px solid #444;
            */
        }
        table.fl-generated-table th {
            background-color: #f0f0f0; /* Light header background */
            font-weight: bold;
            /* For dark themes:
               background-color: #282828;
               color: #eee;
            */
        }

        /* Example of how you could style a user-defined class */
        /* If you use [table class="my-stats-table"] in your BBCode: */
        table.fl-generated-table.my-stats-table {
            width: 50%;
            border: 2px solid purple !important; /* !important to override general if needed */
        }
        table.fl-generated-table.my-stats-table th {
            background-color: lightgoldenrodyellow;
            color: black;
        }
    `);

    // Run the conversion when the DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', processPageForTables);
    } else {
        processPageForTables(); // DOMContentLoaded has already fired
    }

    // Use a MutationObserver to handle content that might be loaded dynamically
    // or changed after the initial page load.
    const observer = new MutationObserver(function(mutations) {
        let needsProcessing = false;
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(function(node) {
                    // Check if the added node is a FormattedBlock or contains one
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        if (node.matches('div.FormattedBlock') || node.querySelector('div.FormattedBlock')) {
                            needsProcessing = true;
                        }
                    }
                });
            }
            // You might also observe 'characterData' if BBCode is modified within an existing text node
            // but that can be more complex to handle efficiently.
        });

        if (needsProcessing) {
            processPageForTables(); // Re-process if relevant nodes were added
        }
    });

    // Start observing the body for added child nodes and subtree modifications
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

})();