Displays hospital timers on tab title.
// ==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...
})();