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.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

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