Displays a virus research timer toggle in the status bar with long-press reset for badge position.
// ==UserScript==
// @name Virus Timer-PDA (Enhanced)
// @namespace TornPDA
// @version 5.2
// @description Displays a virus research timer toggle in the status bar with long-press reset for badge position.
// @author Pholder : mod by Pint-Shot-Riot
// @match https://www.torn.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const apiKey = "###PDA-APIKEY###";
const defaultPos = { top: "60px", left: "10px" };
let virusUntil = 0;
let isVisible = localStorage.getItem('pda_virus_visible') !== 'false';
const styleSheet = document.createElement("style");
styleSheet.innerText = `
.pda-virus-status-li {
display: inline-flex;
align-items: center;
justify-content: center;
margin: 0 2px;
}
.pda-virus-status-icon {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 16px;
opacity: 0.75;
transition: opacity 0.15s, transform 0.15s;
text-decoration: none;
filter: grayscale(1);
user-select: none;
-webkit-user-drag: none;
}
.pda-virus-status-icon:hover {
opacity: 1;
transform: scale(1.15);
}
.pda-virus-active {
filter: grayscale(0) !important;
opacity: 1 !important;
}
#pda-virus-badge {
position: fixed !important;
z-index: 2147483647 !important;
background: rgba(34, 34, 34, 0.5) !important;
color: rgba(136, 176, 49, 0.8) !important;
font-family: "Segoe UI", Arial, sans-serif !important;
font-weight: bold !important;
text-transform: uppercase !important;
box-shadow: 2px 2px 5px rgba(0,0,0,0.3);
border: 1px solid rgba(68, 68, 68, 0.5) !important;
border-left: 3px solid #88b031 !important;
padding: 8px 14px !important;
font-size: 13px !important;
border-radius: 2px;
white-space: nowrap !important;
touch-action: none;
user-select: none;
display: none;
}
.pda-drag-ready {
transform: scale(1.1) !important;
background: rgba(136, 176, 49, 0.4) !important;
border: 1px solid #88b031 !important;
}
.pda-pulse {
animation: pda-glow 2.5s infinite ease-in-out;
}
.pda-warning-pulse {
animation: pda-warning-glow 1.5s infinite ease-in-out !important;
border-left-color: #ff3b3b !important;
color: #ff3b3b !important;
}
@keyframes pda-glow {
0% { border-color: rgba(68, 68, 68, 0.5); }
50% { border-color: rgba(136, 176, 49, 0.8); }
100% { border-color: rgba(68, 68, 68, 0.5); }
}
@keyframes pda-warning-glow {
0% { border-color: rgba(255, 59, 59, 0.3); }
50% { border-color: rgba(255, 59, 59, 1); }
100% { border-color: rgba(255, 59, 59, 0.3); }
}
`;
document.head.appendChild(styleSheet);
function syncUI() {
const badge = document.getElementById('pda-virus-badge');
const icon = document.getElementById('pda-virus-status-btn');
if (badge) {
const targetDisplay = isVisible ? 'block' : 'none';
if (badge.style.display !== targetDisplay) {
badge.style.setProperty('display', targetDisplay, 'important');
}
badge.classList.toggle('pda-pulse', isVisible);
}
if (icon) {
icon.classList.toggle('pda-virus-active', isVisible);
}
}
function handleToggle() {
isVisible = !isVisible;
localStorage.setItem('pda_virus_visible', isVisible);
syncUI();
}
function resetPosition() {
const badge = document.getElementById('pda-virus-badge');
localStorage.removeItem('pda_virus_pos');
if (badge) {
Object.assign(badge.style, { top: defaultPos.top, left: defaultPos.left });
badge.style.backgroundColor = "rgba(136, 176, 49, 0.6)";
setTimeout(() => badge.style.backgroundColor = "", 500);
}
}
function createUI() {
const statusUl = document.querySelector('ul[class*="status-icons"]');
let created = false;
if (statusUl && !document.getElementById('pda-virus-status-btn')) {
const li = document.createElement('li');
li.className = 'pda-virus-status-li';
const a = document.createElement('a');
a.id = 'pda-virus-status-btn';
a.className = 'pda-virus-status-icon';
a.innerHTML = '🦠';
let pressTimer;
const startPress = () => {
pressTimer = setTimeout(() => {
resetPosition();
pressTimer = null;
}, 1500);
};
const endPress = (e) => {
if (pressTimer) {
clearTimeout(pressTimer);
handleToggle();
}
e.preventDefault();
};
a.addEventListener('mousedown', startPress);
a.addEventListener('mouseup', endPress);
a.addEventListener('touchstart', (e) => { e.preventDefault(); startPress(); });
a.addEventListener('touchend', (e) => { e.preventDefault(); endPress(e); });
li.appendChild(a);
statusUl.appendChild(li);
created = true;
}
if (!document.getElementById('pda-virus-badge')) {
const badge = document.createElement('div');
badge.id = 'pda-virus-badge';
const pos = JSON.parse(localStorage.getItem('pda_virus_pos') || JSON.stringify(defaultPos));
Object.assign(badge.style, { top: pos.top, left: pos.left });
badge.onclick = (e) => {
e.stopPropagation();
if (!badge.classList.contains('pda-drag-ready')) handleToggle();
};
document.body.appendChild(badge);
setupDrag(badge);
created = true;
}
if (created) syncUI();
}
function setupDrag(el) {
let pressTimer, isDragging = false, dragEnabled = false;
let startX, startY, initialX, initialY;
const onStart = (e) => {
const b = e.type.includes('touch') ? e.touches[0] : e;
startX = b.clientX; startY = b.clientY;
const rect = el.getBoundingClientRect();
initialX = b.clientX - rect.left;
initialY = b.clientY - rect.top;
pressTimer = setTimeout(() => {
dragEnabled = true;
el.classList.add('pda-drag-ready');
}, 600);
};
const onMove = (e) => {
const b = e.type.includes('touch') ? e.touches[0] : e;
if (!dragEnabled) {
if (Math.abs(b.clientX - startX) > 10 || Math.abs(b.clientY - startY) > 10) clearTimeout(pressTimer);
return;
}
if (e.cancelable) e.preventDefault();
isDragging = true;
el.style.left = (b.clientX - initialX) + "px";
el.style.top = (b.clientY - initialY) + "px";
};
const onEnd = () => {
clearTimeout(pressTimer);
if (dragEnabled && isDragging) {
localStorage.setItem('pda_virus_pos', JSON.stringify({ top: el.style.top, left: el.style.left }));
setTimeout(() => el.classList.remove('pda-drag-ready'), 100);
isDragging = false;
} else {
el.classList.remove('pda-drag-ready');
}
dragEnabled = false;
};
el.addEventListener('touchstart', onStart, {passive: false});
window.addEventListener('touchmove', onMove, {passive: false});
window.addEventListener('touchend', onEnd);
el.addEventListener('mousedown', onStart);
window.addEventListener('mousemove', onMove);
window.addEventListener('mouseup', onEnd);
}
async function updateData() {
if (apiKey.includes("PDA-APIKEY")) return;
try {
const res = await fetch(`https://api.torn.com/v2/user/virus?key=${apiKey}`);
const data = await res.json();
virusUntil = data.virus?.until || 0;
} catch (e) {}
}
function render() {
const b = document.getElementById('pda-virus-badge');
if (!b || !isVisible) return;
const now = Math.floor(Date.now() / 1000);
let text = "VIRUS: IDLE";
let isWarning = false;
if (virusUntil > now) {
const s = virusUntil - now;
isWarning = (s <= 1200);
const d = Math.floor(s / 86400);
const h = Math.floor((s % 86400) / 3600);
const m = Math.floor((s % 3600) / 60);
text = `VIRUS: ${d > 0 ? d + 'D ' : ''}${h}H ${m}M`;
}
if (b.innerText !== text) b.innerText = text;
b.classList.toggle('pda-warning-pulse', isWarning);
}
updateData();
createUI();
setInterval(updateData, 30000);
setInterval(render, 1000);
setInterval(createUI, 2000);
})();