Tab Hospital Timer

Displays hospital timers on tab title.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Tab Hospital Timer
// @license      MIT
// @namespace    https://www.torn.com/
// @version      v2.5
// @description  Displays hospital timers on tab title.
// @author       AngryGod
// @match        https://www.torn.com/profiles.php?XID=*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function waitForElementMutation(selector, callback) {
        const observer = new MutationObserver((mutationsList, observer) => {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    const element = document.querySelector(selector);
                    if (element) {
                        callback(element);
                        observer.disconnect(); // Stop observing once the element is found
                        return;
                    }
                }
            }
        });

        observer.observe(document.body, { childList: true, subtree: true });

    };
    function secondsTimeSpanToHMS(s) {
        var h = Math.floor(s / 3600); //Get whole hours
        s -= h * 3600;
        var m = Math.floor(s / 60); //Get remaining minutes
        s -= m * 60;
        return h + ":" + (m < 10 ? '0' + m : m) + ":" + (s < 10 ? '0' + s : s); //zero padding on minutes and seconds
    };

    /* function ServertimeMs(timeString) {
        const [hours, minutes, seconds] = timeString.split(':').map(Number);
        const totalSeconds = (hours * 3600) + (minutes * 60) + seconds;
        return totalSeconds * 1000;
    };
    function getservertime(servertimeclass){
        //const datetimeString = servertimeclass.text();
        //const time = datetimeString.split(' ')[1];
        //return ServertimeMs(time);
        return performance.now();
    };*/

    function convertHmsToSeconds(timeString) {
        // Use a regular expression to extract hours, minutes, and seconds
        // The expression looks for one or more digits followed by 'h', 'm', or 's'
        const h = timeString.match(/(\d+)h/);
        const m = timeString.match(/(\d+)m/);
        const s = timeString.match(/(\d+)s/);

        // Initialize total seconds
        let totalSeconds = 0;

        // Convert hours to seconds if found
        if (h) {
            totalSeconds += parseInt(h[1]) * 60 * 60;
        }

        // Convert minutes to seconds if found
        if (m) {
            totalSeconds += parseInt(m[1]) * 60;
        }

        // Add seconds if found
        if (s) {
            totalSeconds += parseInt(s[1]);
        }
        return totalSeconds;
    };
    $.fn.redraw = function() {
        $(this).each(function() {
            var redraw = this.offsetHeight; // Accessing offsetHeight forces a repaint
        });
    };
    function HospitalTimeMs(){
      //  $(".profile-container").redraw();//v1.2 forces redraw so it can recalculate hospital timer if they med out.
        var Hospitaltext = $('.main-desc').text();
        var removetext = 'In hospital for  ';
        var shortenhours = Hospitaltext.replace("hours", "h");
        shortenhours = shortenhours.replace("hour","h");
        var shortenmins = shortenhours.replace("minutes", "m");
        shortenmins = shortenmins.replace("minute","m");
        var shortensecs = shortenmins.replace("seconds", "s");
        var removeand = shortensecs.replace("and", "");
        var removewhitespace = removeand.replace('   ','');
        var Hospitalshorttext = removewhitespace.replace(removetext, "");
        var HSTNowhiteSpace = Hospitalshorttext.replace(new RegExp(' ', 'g'), '');
        var timeString = HSTNowhiteSpace;
        return convertHmsToSeconds(timeString)*1000;
    };
    /* function accurateCountdown(durationInMs,Stime, callback) {
        const endTime = getservertime(Stime) + durationInMs;
        const starttime = getservertime(Stime);

        function tick() {
            const remaining = endTime - getservertime(Stime);

            if (starttime > getservertime(Stime)) { //might cause problems later comment for later.
                //console.log(starttime+' '+getservertime(Stime));
                console.log('Server time Reset Re-load tab');
                callback(0);
                return;
            };


            if (remaining <= 0 || (HospitalTimeMs() === 0)) { //v1.2 added if med out.
                callback(0); // Timer is complete
                return;
            };

            callback(remaining);
            const nextTickDelay = 1000 - (remaining % 1000); // Adjust delay to land near the next second
            setTimeout(tick, nextTickDelay);
        }

        tick();
    };*/
    /*function createAndStartSilentAudio() {
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        if (!window.AudioContext) {
            console.log('Web Audio API not supported. Throttling may occur.');
            return;
        }

        try {
            const context = new AudioContext();
            const buffer = context.createBuffer(1, context.sampleRate, context.sampleRate);
            const source = context.createBufferSource();
            source.buffer = buffer;
            source.loop = true;
            source.connect(context.destination);

            if (context.state === 'suspended') {
                context.resume().then(() => {
                    console.log('AudioContext unlocked and resumed.');
                    source.start(0);
                });
            } else {
                source.start(0);
            }
            console.log('Silent audio started.');
        } catch (e) {
            console.error('Error creating silent audio:', e);
        }
    };*/

    $(document).ready(function() {

        waitForElementMutation('.profile-container', function(element) {
            if ($(".hospital")[0]){
                var pageTitle = $(document).attr('title');
                var removeT = pageTitle.replace(' | Torn','');
                var Servertime = $('.server-date-time');
                var Timerchanged = HospitalTimeMs();
                const checkCondition = setInterval(() => {
                    if (HospitalTimeMs() < Timerchanged)  {
                        console.log("Time changed proceeding with timer");
                        clearInterval(checkCondition);
                        var hospitalins = HospitalTimeMs()/1000;

                        /* var newButton = '<button style="color:whitesmoke;background-color:#333;cursor: pointer; " id="playSilentAudioButton">Play Silent Audio to prevent throttling</button>';
                        $('.description').append(newButton);
                        $('#playSilentAudioButton').on('click', function() {
                            createAndStartSilentAudio();
                            console.log('playing silent sound');
                            $('#playSilentAudioButton').remove();
                        });*/


                        // 1. Define the worker code as a string
                        const workerCode = `

                        self.addEventListener('message', function(e) {
                        if (e.data === 'run the timer') {
                        const tickInterval = 1000; // 1000 milliseconds
                        let expectedTick = performance.now() + tickInterval;

                      function accurateTick() {
                      // Calculate the drift
                      const drift = performance.now() - expectedTick;

                       // Post the message to the main thread
                      self.postMessage('tick');

                      // Schedule the next tick, adjusting for any drift
                      expectedTick += tickInterval;
                     setTimeout(accurateTick, Math.max(0, tickInterval - drift));
                     };

                     // Start the first tick
                    setTimeout(accurateTick, tickInterval);
                    }
                    });

                      `;

                        // 2. Create a Blob from the worker code
                        const blob = new Blob([workerCode], { type: 'application/javascript' });

                        // 3. Create a Blob URL for the worker
                        const workerUrl = URL.createObjectURL(blob);

                        // 4. Create a new Worker instance using the Blob URL
                        const myWorker = new Worker(workerUrl);

                        // 5. Listen for messages from the worker
                        myWorker.addEventListener('message', function(e) {
                            if (e.data === 'tick') {
                                var Changeontick = hospitalins--;
                                //console.log('Timer ticked in the main thread!');
                                document.title = secondsTimeSpanToHMS(Changeontick)+" "+ removeT;
                                if(HospitalTimeMs()/1000 === 0 || Changeontick === 0){
                                    myWorker.terminate();
                                    document.title = '0:00:00 '+removeT;
                                    console.log('Worker has been terminated.');
                                    URL.revokeObjectURL(workerUrl);
                                };
                            }
                        });

                        myWorker.postMessage('run the timer');
                        window.onbeforeunload = function() {
                            myWorker.terminate();
                            console.log('Worker terminated due to page unload/refresh.');
                            URL.revokeObjectURL(workerUrl);
                        };

                        /* accurateCountdown(HospitalTimeMs()-1000, Servertime,(remainingTime) => {
                            const seconds = Math.ceil(remainingTime / 1000);
                            document.title = secondsTimeSpanToHMS(seconds) +" "+ removeT;
                            if (seconds === 0) {
                                console.log("Countdown finished!");
                            }
                        });*/


                    } else {
                        console.log("Waiting for time to change for a more acurate timer");
                    }
                }, 1000); // Check every 1000 milliseconds (1 second)

            } else {
                console.log('not in hospital');
            };


        });
    });
    // Your code here...
})();