Torn Chat Timestamp

Displays the time a chat message was sent right by it.

目前為 2022-06-07 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Torn Chat Timestamp
// @namespace    https://www.torn.com
// @version      1.2.6
// @description  Displays the time a chat message was sent right by it.
// @author       PhilMe [2590086]
// @match        *.torn.com/*
// @grant        none
// ==/UserScript==
/*jshint esversion: 6 */


(function() {
    'use strict';
    let timestampColor = {red: 169, green: 169, blue: 169};

    let allClasses = [];
    let allElements = document.querySelectorAll('*');
    let chatBoxClass;
    let overviewClass;
    let chatBoxContentClass;
    let messageClass;
    let lastMessageLabelClass;

    /*
    Since class names end with a few random letters and numbers (ex. chat-box-content_3wOdX), and those can change over time,
    this iterates over all class names on the page and finds the full class name for them.
    */
    for (let i = 0; i < allElements.length; i++) {
        let classes = allElements[i].className.toString().split(/\s+/);
        for (let j = 0; j < classes.length; j++) {
            let cls = classes[j];
            if (cls.startsWith("_chat-box_") && !chatBoxClass) {
                chatBoxClass = cls;
            }
            else if (cls.startsWith("_overview_") && !overviewClass) {
                overviewClass = cls;
            }
            else if (cls.startsWith("_chat-box-content_") && !chatBoxContentClass) {
                chatBoxContentClass = cls;
            }
            else if (cls.startsWith("_message_") && !messageClass) {
                messageClass = cls;
            }
            else if (cls.startsWith("_chat-last-message-label_") && !lastMessageLabelClass) {
                lastMessageLabelClass = cls;
            }
        if (chatBoxClass && overviewClass && chatBoxContentClass && messageClass && lastMessageLabelClass) {break;} // leave loop early if these are all found
        }
    }

    function parseDate (datestring) {
        let re = new RegExp('^([0-2][0-9]):([0-5][0-9]):([0-5][0-9]) - ([0-3][0-9])\/([0-1][0-9])\/([0-9]{2})', 'g');
        let data = re.exec(datestring);
        return `${data[1]}:${data[2]}:${data[3]}`;
    }

    function formatDate(date) {
        let hour = date.getHours().toString().padStart(2, "0");
        let minute = date.getMinutes().toString().padStart(2, "0");
        let second = date.getSeconds().toString().padStart(2, "0");
        return `${hour}:${minute}:${second}`;
    }

    function addTimestamp (element) {
        if (element.getAttribute("class").trim() !== messageClass) {return;} // skip non-messages

        let span = element.querySelector('span');
        let timeString = span.getAttribute("title");

        let timeSpan = document.createElement('span');
        timeSpan.innerHTML = parseDate(timeString) + " ";
        timeSpan.style.color = `rgb(${timestampColor.red}, ${timestampColor.green}, ${timestampColor.blue})`;

        let a = element.querySelector('a');
        element.prepend(timeSpan, a);
    }

    function addChatMessageObserver(element) {
        let messageDiv = element.querySelector(`.${overviewClass}`);

        if (messageDiv !== null) {
            if (!messageClass || !lastMessageLabelClass) {
                messageDiv.childNodes.forEach(node => {
                    if (messageClass && lastMessageLabelClass) {return;}
                    if (node.className.startsWith("_message_")) {
                        messageClass = node.className.trim();
                        return;
                    } else if (node.className.startsWith("_chat-last-message-label_")) {
                        lastMessageLabelClass = node.className.trim();
                        return;
                    }
                });
            }
            messageDiv.querySelectorAll(`.${messageClass}`).forEach(msg => {
                addTimestamp(msg);
            });
            chatMessageObserver.observe(messageDiv, {childList: true});

            // checks for the last message label at the bottom of chat to load, then scrolls the chatbox to it
            let interval = setInterval(() => {
                let lastMessageLabel = element.querySelector(`.${lastMessageLabelClass}`);
                if (lastMessageLabel) {
                    lastMessageLabel.scrollIntoView();
                    clearInterval(interval);
                }
            }, 100);
        }
    }

    // Watches for if any new chats are created
    const chatObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            for (let i = 0; i < mutation.addedNodes.length; i++) {
                chatOpenedObserver.observe(mutation.addedNodes[i], {childList: true});
            }
        })
    });

    // When a chat is opened, timestamps are added
    const chatOpenedObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            for (let i = 0; i < mutation.addedNodes.length; i++) {
                let element = mutation.addedNodes[i];
                if (element.getAttribute("class").startsWith(chatBoxContentClass)) {
                    addChatMessageObserver(element);
                }
            }
        })
    });

    // When a new message is sent, add timestamp
    const chatMessageObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            for (let i = 0; i < mutation.addedNodes.length; i++) {
                addTimestamp(mutation.addedNodes[i]);

            }
        })
    });

    const chatRoot = document.querySelector('#chatRoot').firstElementChild;

    Array.from(chatRoot.getElementsByClassName(chatBoxClass)).forEach(element => {
        addChatMessageObserver(element);
        chatOpenedObserver.observe(element, {childList: true});
    });
    chatObserver.observe(chatRoot, {childList: true}); // watches for new chats

})();