Hi, I tried this script & it wasn't working for me. (Most probably coz Ched & team have changed the class names to various elements).
I tried resolving it.
// ==UserScript==
// @name Chain Timer
// @namespace sleek.script.chain_timer
// @version 3.2
// @description Enlarges chain timer and colors from green to red as time runs out.
// @author sleek
// @match https://www.torn.com/*
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant none
// ==/UserScript==
// Changelog:
// v1.0 - Initial release. Hardcoded class names for chain timer elements. (By Annosz [2896714] from Github)
// v2.0 - Added dynamic colors to better visualize the chain timer. (By Vassilios [2276659])
// v3.0 - Replaced hardcoded class names with structural DOM traversal anchored on Chain:
// v3.1 - Added check to reset elements if chain count is < 10
// v3.2 - Added cooldown detection via progress div classList and absent speed element.
// resetStyles() now fires when chain enters cooldown state.
(function () {
'use strict';
let initialized = false;
function resolveChainElements() {
const chainLabel = [...document.querySelectorAll('p')].find(
el => el.textContent.trim() === 'Chain:'
);
if (!chainLabel) return null;
const barStats = chainLabel.parentElement;
const allP = barStats.querySelectorAll('p');
const barValue = allP[1];
const timeLeft = allP[allP.length - 1];
const chainBar = barStats.parentElement;
const progressDiv = [...chainBar.children].find(el =>
el !== barStats &&
[...el.classList].some(cls => cls.toLowerCase().startsWith('progress'))
) ?? null;
const speed = [...chainBar.children].find(el =>
el !== barStats &&
el.tagName === 'DIV' &&
el.querySelector('span')?.textContent.trim().startsWith('x')
) ?? null;
const tickList = chainBar.querySelector('ul');
return { barStats, barValue, timeLeft, speed, progressDiv, tickList };
}
function getChainCount(barValue) {
return parseInt(barValue.textContent.trim()) || 0;
}
function isInCooldown(els) {
const hasCooldownClass = els.progressDiv
? [...els.progressDiv.classList].some(cls => cls.toLowerCase().includes('cooldown'))
: false;
return hasCooldownClass || els.speed === null;
}
function applyStyles(els) {
const { barStats, timeLeft, speed, tickList } = els;
barStats.style.display = 'block';
timeLeft.style.fontSize = '60px';
timeLeft.style.height = '62px';
if (speed) speed.style.top = 'unset';
if (tickList) tickList.style.height = '8px';
const text = timeLeft.textContent.trim();
if (!text.includes(':')) return;
const [m, s] = text.split(':').map(Number);
const ratio = Math.max(0, Math.min(1, ((m * 60 + s) || 0) / 300));
timeLeft.style.color = `rgb(${Math.round(255 * (1 - ratio))},${Math.round(255 * ratio)},0)`;
}
function resetStyles(els) {
const { barStats, timeLeft, speed, tickList } = els;
barStats.style.display = '';
timeLeft.style.fontSize = '';
timeLeft.style.height = '';
timeLeft.style.color = '';
if (speed) speed.style.top = '';
if (tickList) tickList.style.height = '';
}
function actionFunction(jNode) {
if (jNode[0].textContent.trim() !== 'Chain:') return;
if (initialized) return;
initialized = true;
const els = resolveChainElements();
if (!els) return;
const tick = () => {
if (isInCooldown(els) || getChainCount(els.barValue) < 10) {
resetStyles(els);
} else {
applyStyles(els);
}
};
tick();
setInterval(tick, 1000);
}
waitForKeyElements('p', actionFunction);
})();