Torn Logs Enhancer

Enhances the Torn Log page by allowing the user to select and copy logs with a total cost calculation. Currently supports the Bounty Place category. More for future work.

// ==UserScript==
// @name         Torn Logs Enhancer
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Enhances the Torn Log page by allowing the user to select and copy logs with a total cost calculation. Currently supports the Bounty Place category. More for future work.
// @author       Hesper [2924630]
// @match        https://www.torn.com/page.php?sid=log*
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==
(function () {
    'use strict';

    let currentLogType = null; 

    function initializeForLogType(logType) {
        if (logType === '6700') {
            console.log('Initializing for bounty place logs...');
            setupBountyPlaceLogs();
        }
        // Add more log types here in the future
    }

    function setupBountyPlaceLogs() {
        const style = document.createElement('style');
        style.textContent = `
            #floating-torn-log-enhancer {
                position: fixed;
                top: 300px;
                right: 0px;
                background-color: #333;
                color: #fff;
                border: 1px solid #ccc;
                padding: 5px;
                z-index: 1000;
                width: 30px;
                height: 25px;
                font-size: 1em;
                border-radius: 5px 0px 0px 5px;
                overflow: hidden;
                transition: all 0.3s ease;
                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
                cursor: pointer;
            }

            #floating-torn-log-enhancer #collapsed-view {
                display: flex;
                align-items: center;
                justify-content: space-between;
                height: 100%;
                font-size: 1em;
            }

            #floating-torn-log-enhancer #expanded-view {
                display: none;
                flex-direction: column;
                gap: 2px;
            }

            #floating-torn-log-enhancer button {
                margin-top: 10px;
                padding: 5px 10px;
                border: 1px solid #fff;
                background-color: #444;
                color: #fff;
                border-radius: 3px;
                cursor: pointer;
            }


            #log-list {
                margin-top: 10px;
                overflow-y: auto;
                max-height: 150px;
                scrollbar-width: thin; /* For Firefox */
                scrollbar-color: #fff #333; /* For Firefox */
            }

            /* For WebKit browsers (Chrome, Edge, Safari) */
            #log-list::-webkit-scrollbar {
                width: 8px;
            }

            #log-list::-webkit-scrollbar-track {
                background: #333;
            }

            #log-list::-webkit-scrollbar-thumb {
                background: #fff;
                border-radius: 4px;
            }

            #log-list::-webkit-scrollbar-thumb:hover {
                background: #ccc;
            }
        `;
        document.head.appendChild(style);

        const floatingDiv = document.createElement('div');
        floatingDiv.id = 'floating-torn-log-enhancer';
        floatingDiv.innerHTML = `
            <div id="collapsed-view">
                <span>◀</span> <span id="selected-count-collapsed">0</span>
            </div>
            <div id="expanded-view">
                <div>
                    <strong>Selected Logs:</strong> <span id="selected-count">0</span>
                </div>
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <span><strong>Total:</strong> $<span id="total-bounties">0</span></span>
                    <span>▶</span>
                </div>
                <div>
                    <strong>Category:</strong> Bounty place
                </div>
                <button id="copy-button">Copy Logs</button>
                <div id="log-list"></div>
            </div>
        `;
        document.body.appendChild(floatingDiv);

        const collapsedView = document.getElementById('collapsed-view');
        const expandedView = document.getElementById('expanded-view');
        const selectedCountCollapsed = document.getElementById('selected-count-collapsed');
        const selectedCount = document.getElementById('selected-count');
        const totalBounties = document.getElementById('total-bounties');
        const copyButton = document.getElementById('copy-button');
        const logList = document.getElementById('log-list');

        floatingDiv.addEventListener('click', () => {
            if (expandedView.style.display === 'none') {
                expandedView.style.display = 'flex';
                collapsedView.style.display = 'none';
                floatingDiv.style.width = '200px'; // Expand width
                floatingDiv.style.height = 'auto'; // Adjust height
            } else {
                expandedView.style.display = 'none';
                collapsedView.style.display = 'flex';
                floatingDiv.style.width = '30px'; // Collapse width
                floatingDiv.style.height = '25px'; // Collapse height
            }
        });

        function updateCollapsedViewCount() {
            selectedCountCollapsed.textContent = selectedLogs.length;
        }

        function updateLogList() {
            const sortedLogs = selectedLogs.sort((a, b) => {
                const dateRegex = /(\d{2}:\d{2}:\d{2}) - (\d{2}\/\d{2}\/\d{2})/;
                const dateA = new Date(a.match(dateRegex)[2] + ' ' + a.match(dateRegex)[1]);
                const dateB = new Date(b.match(dateRegex)[2] + ' ' + b.match(dateRegex)[1]);
                return dateB - dateA; // Descending order
            });

            logList.innerHTML = sortedLogs.map(log => `<div>${log}</div>`).join('');
            updateCollapsedViewCount();
        }

        let selectedLogs = [];
        let total = 0;

        function addCheckboxesToLogs(logEntries) {
            logEntries.forEach((row) => {
                const timeElement = row.querySelector('.time___CjMrZ');
                const logTextElement = row.querySelector('.log-text');
                if (!timeElement || !logTextElement || timeElement.querySelector('input[type="checkbox"]')) return;

                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.style.marginLeft = '10px';
                checkbox.addEventListener('change', () => {
                    const timeText = timeElement.textContent.trim();
                    const logText = logTextElement.textContent.trim();
                    const costMatch = logText.match(/cost of \$([\d,]+)/);

                    if (checkbox.checked) {
                        selectedLogs.push(`${timeText} ${logText}`);
                        if (costMatch) {
                            total += parseInt(costMatch[1].replace(/,/g, ''), 10);
                        }
                    } else {
                        selectedLogs = selectedLogs.filter(log => log !== `${timeText} ${logText}`);
                        if (costMatch) {
                            total -= parseInt(costMatch[1].replace(/,/g, ''), 10);
                        }
                    }

                    selectedCount.textContent = selectedLogs.length;
                    totalBounties.textContent = total.toLocaleString();
                    updateLogList();
                });

                timeElement.appendChild(checkbox);
            });
        }

        // Copy logs to clipboard
        copyButton.addEventListener('click', () => {
            const logsText = selectedLogs.join('\n') + `\n\nTotal: $${total.toLocaleString()}`;
            GM_setClipboard(logsText);
            alert('Logs copied to clipboard!');
        });

        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    const newRows = Array.from(mutation.addedNodes).filter(node => node.tagName === 'TR');
                    addCheckboxesToLogs(newRows);
                }
            });
        });

        const waitForTable = setInterval(() => {
            const logTable = document.querySelector('table');
            if (logTable) {
                observer.observe(logTable, { childList: true, subtree: true });
                const initialLogEntries = logTable.querySelectorAll('tr');
                addCheckboxesToLogs(initialLogEntries);
                clearInterval(waitForTable);
            }
        }, 500);
    }

    function monitorUrlChanges() {
        let previousUrl = null;

        const checkUrl = () => {
            const currentUrl = window.location.href;

            if (currentUrl !== previousUrl) {
                previousUrl = currentUrl;

                const urlParams = new URLSearchParams(window.location.search);
                const logType = urlParams.get('log');

                if (logType && logType !== currentLogType) {
                    const existingDiv = document.getElementById('floating-torn-log-enhancer');
                    if (existingDiv) {
                        existingDiv.remove();
                    }
                    currentLogType = logType;
                    initializeForLogType(logType);
                }
            }
        };

        // Initial check then monitor for changes
        checkUrl();
        setInterval(checkUrl, 500);
    }

    monitorUrlChanges();
})();