// ==UserScript==
// @name Chain watcher mod
// @namespace http://tampermonkey.net/
// @version 1
// @description Watch the chain
// @author Jox [1714547], Creed [1758059], Heart
// @match https://www.torn.com/factions.php?step=your*
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(`.jox-member-your {text-decoration: line-through !important; cursor: not-allowed !important;}
.jox-faction-your {margin-left: 0px !important}
.jox-faction-your:after {content: " â›”"; text-decoration: none;}
.jox-member-enemy {color:red !important; animation: ring2 4s .7s ease-in-out infinite; display:inline-block;}
.jox-attack-enemy {animation: ring 4s .7s ease-in-out infinite; display:inline-block;}
.chain-box-timeleft {
font-size: 100px !important;
}
.chain-box-stats-list {
display: none !important;
}
.chain-box-stats-block {
justify-content: center !important;
}
.inactive{display: none !important;}
.first-in-row {
height: 100% !important;
width: 100% !important;}
@keyframes ring {
0% { transform: rotate(0); }
1% { transform: rotate(15deg); }
3% { transform: rotate(-14deg); }
5% { transform: rotate(17deg); color:red;}
7% { transform: rotate(-16deg); }
9% { transform: rotate(15deg); }
11% { transform: rotate(-14deg); }
13% { transform: rotate(13deg); }
15% { transform: rotate(-12deg);}
17% { transform: rotate(11deg); }
19% { transform: rotate(-10deg); }
21% { transform: rotate(9deg); }
23% { transform: rotate(-8deg); }
25% { transform: rotate(7deg); }
27% { transform: rotate(-6deg); }
29% { transform: rotate(5deg); }
31% { transform: rotate(-4deg); }
33% { transform: rotate(3deg); }
35% { transform: rotate(-2deg); }
37% { transform: rotate(1deg); color:#0092d8;}
39% { transform: rotate(0); }
100% { transform: rotate(0); }
}
@keyframes ring2 {
0% { transform: translateX(0); }
% { transform: translateX(7.5px); }
3% { transform: translateX(-7px); }
5% { transform: translateX(6.5px); color:red;}
7% { transform: translateX(-6px); }
9% { transform: translateX(5.5px); }
11% { transform: translateX(-5px); }
13% { transform: translateX(4.5px); }
15% { transform: translateX(-4px);}
17% { transform: translateX(3.5px); }
19% { transform: translateX(-3px); }
21% { transform: translateX(2.5px); }
23% { transform: translateX(-2px); }
25% { transform: translateX(1.5px); }
27% { transform: translateX(-1px); }
29% { transform: translateX(0.5px); color:#0092d8;}
39% { transform: rotate(0); }
100% { transform: rotate(0); }
}
`);
var ctx = null;
var controls = {};
var started = false;
start();
function start(){
loadData();
watchForChainTimer();
setInterval(markWallMembers, 500);
}
function markWallMembers(){
if(controls.markWallTargets){
addCustomCss();
}
else{
removeCustomCss();
}
}
function addCustomCss(){
var memersYour = document.querySelectorAll('.descriptions .faction-war .members-cont .members-list .row-animation.your .member .user.name');
var factionsYour = document.querySelectorAll('.descriptions .faction-war .members-cont .members-list .row-animation.your .member .user.faction');
var memersEnemy = document.querySelectorAll('.descriptions .faction-war .members-cont .members-list .row-animation.enemy .member .user.name');
var attacksEnemy = document.querySelectorAll('.descriptions .faction-war .members-cont .members-list .row-animation.enemy .attack a');
memersYour.forEach(function(element) {
element.classList.add('jox-member-your');
});
/*
factionsYour.forEach(function(element) {
element.classList.add('jox-faction-your');
});
*/
memersEnemy.forEach(function(element) {
element.classList.add('jox-member-enemy');
});
attacksEnemy.forEach(function(element) {
element.classList.add('jox-attack-enemy');
});
}
function removeCustomCss(){
var memersYour = document.querySelectorAll('.descriptions .faction-war .members-cont .members-list .row-animation.your .member .user.name');
var factionsYour = document.querySelectorAll('.descriptions .faction-war .members-cont .members-list .row-animation.your .member .user.faction');
var memersEnemy = document.querySelectorAll('.descriptions .faction-war .members-cont .members-list .row-animation.enemy .member .user.name');
var attacksEnemy = document.querySelectorAll('.descriptions .faction-war .members-cont .members-list .row-animation.enemy .attack a');
memersYour.forEach(function(element) {
element.classList.remove('jox-member-your');
});
factionsYour.forEach(function(element) {
element.classList.remove('jox-faction-your');
});
memersEnemy.forEach(function(element) {
element.classList.remove('jox-member-enemy');
});
attacksEnemy.forEach(function(element) {
element.classList.remove('jox-attack-enemy');
});
}
function addCustomCss2(){
GM_addStyle(`.descriptions .faction-war .members-cont .members-list .row-animation.your .member .user.name {text-decoration: line-through;}
.descriptions .faction-war .members-cont .members-list .row-animation.your .member .user.faction:after {content: " â›” "; text-decoration: none;}
.descriptions .faction-war .members-cont .members-list .row-animation.enemy .member .user.name {color:red;}
.descriptions .faction-war .members-cont .members-list .row-animation.enemy .attack a {animation: ring 4s .7s ease-in-out infinite; display:inline-block;}
@keyframes ring {
0% { transform: rotate(0); }
1% { transform: rotate(15deg); }
3% { transform: rotate(-14deg); }
5% { transform: rotate(17deg); color:red;}
7% { transform: rotate(-16deg); }
9% { transform: rotate(15deg); }
11% { transform: rotate(-14deg); }
13% { transform: rotate(13deg); }
15% { transform: rotate(-12deg);}
17% { transform: rotate(11deg); }
19% { transform: rotate(-10deg); }
21% { transform: rotate(9deg); }
23% { transform: rotate(-8deg); }
25% { transform: rotate(7deg); }
27% { transform: rotate(-6deg); }
29% { transform: rotate(5deg); }
31% { transform: rotate(-4deg); }
33% { transform: rotate(3deg); }
35% { transform: rotate(-2deg); }
37% { transform: rotate(1deg); color:#0092d8;}
39% { transform: rotate(0); }
100% { transform: rotate(0); }
}
`);
}
function watchForChainTimer() {
let target = document.getElementById('factions');
let observer = new MutationObserver(function(mutations) {
let doApply = false;
mutations.forEach(function(mutation) {
for (let i = 0; i < mutation.addedNodes.length; i++) {
//console.log(mutation.addedNodes.item(i));
if (document.querySelector('.chain-box-timeleft')) {
doApply = true;
//console.log('Have List of players');
break;
}
else{
//console.log('Not a List of players');
}
}
if (doApply) {
applyTracker();
observer.disconnect();
}
});
});
// configuration of the observer:
//let config = { childList: true, subtree: true };
let config = { childList: true, subtree: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
}
function applyTracker(){
//var timer = document.querySelector('.chain-box-timeleft');
if(!started){
started = true;
showForm();
alertMe();
}
}
function alertMe(){
var chainInfo = document.querySelector('.chain-box-title');
var timer = document.querySelector('.chain-box-timeleft');
var data = timer.innerHTML.split(':');
var blinkTarget = document.querySelector('.content');
if((chainInfo.innerHTML == 'Chain active' && controls.watchChain) || controls.test){
timer.style.backgroundColor = "transparent";
timer.style.color = 'red';
var currentTime = Number(data[0]) * 60 + Number(data[1]);
var alertTIme = controls.minuteAlert * 60 + controls.secundeAlert;
if((currentTime < alertTIme && currentTime > 0) || controls.test){
blinkTarget.classList.toggle('chainWatcherPing');
if(blinkTarget.classList.contains('chainWatcherPing')){
blinkTarget.style.backgroundColor = controls.colorAlert;
if(controls.beepAlert){
beep(controls.beepLength, controls.beepType, controls.beepVolume, function(){});
}
}
else{
blinkTarget.style.backgroundColor = null;
}
}
else{
blinkTarget.classList.remove('chainWatcherPing');
blinkTarget.style.backgroundColor = null;
}
}
else{
timer.style.backgroundColor = null;
timer.style.color = null;
blinkTarget.classList.remove('chainWatcherPing');
blinkTarget.style.backgroundColor = null;
}
setTimeout(alertMe, controls.interval);
}
var beep = (function () {
if(ctx && ctx.state && ctx.state !== "running"){
ctx.resume();
}
else{
ctx = new AudioContext();
}
return function (duration, type, volume, finishedCallback) {
duration = +duration;
// Only 0-3 are valid types.
type = (type % 4) || 0;
var types = ['sine', 'square', 'sawtooth', 'triangle'];
if (typeof finishedCallback != "function") {
finishedCallback = function () {};
}
var osc = ctx.createOscillator();
var gainNode = ctx.createGain();
osc.type = types[type];
gainNode.gain.value = volume || gainNode.gain.defaultValue;
//osc.type = "sine";
osc.connect(gainNode);
gainNode.connect(ctx.destination);
osc.start();
setTimeout(function () {
osc.stop();
finishedCallback();
}, duration);
};
})();
/*****************************************************************************/
function showForm(){
//Create container div for widget
var container = document.createElement('div');
container.id = 'JoxDiv';
//Create header
var header = document.createElement('div');
header.classList.add('title-black', 'm-top10', 'title-toggle', 'active', 'top-round', 'chain-watcher-header');
header.innerHTML = 'Chain Watcher'
header.style.color = 'lime';
header.onclick = function(e){
var body = document.querySelector('.chain-watcher-body');
controls.watcherMinimized = !controls.watcherMinimized;
if(controls.watcherMinimized){
body.style.display = 'none';
}
else{
body.style.display = 'flex';
}
saveData();
}
//Create body
var body = document.createElement('div');
body.classList.add('cont-gray10', 'bottom-round', 'cont-toggle', 'unreset', 'chain-watcher-body');
if(controls.watcherMinimized){
body.style.display = 'none';
}
else{
body.style.display = 'flex';
}
body.style.flexWrap = 'wrap';
//Add header and body elements
container.appendChild(header);
container.appendChild(body);
//Add items
var cbWatchChain = document.createElement('input');
var lblWatchChain = document.createElement('label');
lblWatchChain.innerHTML = 'Watch Chain';
lblWatchChain.setAttribute('for','cbWatchChain');
cbWatchChain.type = 'checkbox';
cbWatchChain.id = 'cbWatchChain';
cbWatchChain.name = 'cbWatchChain';
cbWatchChain.style.margin = '0 5px';
cbWatchChain.checked = controls.watchChain;
cbWatchChain.onclick = function(e){
if(e.target.checked){
controls.watchChain = true;
}
else{
controls.watchChain = false;
}
saveData();
}
var p1 = document.createElement('p');
p1.style.margin = '10px';
body.appendChild(p1);
p1.appendChild(cbWatchChain);
p1.appendChild(lblWatchChain);
////////////////////////////////
var txtAlertTime = document.createElement('input');
var lblAlertTime = document.createElement('label');
lblAlertTime.innerHTML = 'Alert time:';
lblAlertTime.setAttribute('for','txtAlertTime');
//lblAlertTime.style.margin = '0px 0px 0px 25px';
txtAlertTime.type = 'text';
txtAlertTime.id = 'txtAlertTime';
txtAlertTime.name = 'txtAlertTime';
txtAlertTime.style.margin = '0 5px';
txtAlertTime.value = controls.minuteAlert + ':' + controls.secundeAlert;
txtAlertTime.onchange = function(e){
var data = txtAlertTime.value.match(/^$|^([0-4]):([0-5][0-9])$/);
if(data && data.length && data.length == 3){
controls.minuteAlert = Number(data[1]);
controls.secundeAlert = Number(data[2]);
saveData();
}
else{
alert('Set time is in wrong format, it must me in format m:ss in valuse between 0:00 and 4:59');
txtAlertTime.value = controls.minuteAlert + ':' + controls.secundeAlert;
}
}
var p2 = document.createElement('p');
p2.style.margin = '10px';
body.appendChild(p2);
p2.appendChild(lblAlertTime);
p2.appendChild(document.createElement('br'));
p2.appendChild(txtAlertTime);
////////////////////////////////
var cbBeepAlert = document.createElement('input');
var lblBeepAlert = document.createElement('label');
lblBeepAlert.innerHTML = 'Beep Alert';
lblBeepAlert.setAttribute('for','cbBeepAlert');
cbBeepAlert.type = 'checkbox';
cbBeepAlert.id = 'cbBeepAlert';
cbBeepAlert.name = 'cbBeepAlert';
//cbBeepAlert.style.margin = '0px 5px 0px 25px';
cbBeepAlert.checked = controls.beepAlert;
cbBeepAlert.onclick = function(e){
if(e.target.checked){
controls.beepAlert = true;
}
else{
controls.beepAlert = false;
}
saveData();
}
var p3 = document.createElement('p');
p3.style.margin = '10px';
body.appendChild(p3);
p3.appendChild(cbBeepAlert);
p3.appendChild(lblBeepAlert);
////////////////////////////////
var sldBeepVolume = document.createElement('input');
var lblBeepVolume = document.createElement('label');
lblBeepVolume.innerHTML = 'Beep Volume';
lblBeepVolume.setAttribute('for','sldBeepVolume');
//lblBeepVolume.style.margin = '0px 5px 0px 25px';
sldBeepVolume.type = 'range';
sldBeepVolume.min = 1;
sldBeepVolume.max = 100;
sldBeepVolume.value = controls.beepVolume * 100;
sldBeepVolume.id = 'sldBeepVolume';
sldBeepVolume.name = 'sldBeepVolume';
sldBeepVolume.onchange = function(e){
controls.beepVolume = Number(sldBeepVolume.value) / 100;
saveData();
}
var p4 = document.createElement('p');
p4.style.margin = '10px';
body.appendChild(p4);
p4.appendChild(lblBeepVolume);
p4.appendChild(document.createElement('br'));
p4.appendChild(sldBeepVolume);
////////////////////////////////
var clrAlertColor = document.createElement('input');
var lblAlertColor = document.createElement('label');
lblAlertColor.innerHTML = 'Alert color';
lblAlertColor.setAttribute('for','clrAlertColor');
//lblAlertColor.style.margin = '0px 5px 0px 25px';
clrAlertColor.type = 'color';
clrAlertColor.value = controls.color;
clrAlertColor.id = 'clrAlertColor';
clrAlertColor.name = 'clrAlertColor';
clrAlertColor.onchange = function(e){
controls.color = clrAlertColor.value;
saveData();
}
var p6 = document.createElement('p');
p6.style.margin = '10px';
body.appendChild(p6);
p6.appendChild(lblAlertColor);
p6.appendChild(document.createElement('br'));
p6.appendChild(clrAlertColor);
////////////////////////////////
var sldColorOpacity = document.createElement('input');
var lblColorOpacity = document.createElement('label');
lblColorOpacity.innerHTML = 'Color opacity';
lblColorOpacity.setAttribute('for','sldColorOpacity');
//lblColorOpacity.style.margin = '0px 5px 0px 25px';
sldColorOpacity.type = 'range';
sldColorOpacity.min = 1;
sldColorOpacity.max = 100;
sldColorOpacity.value = controls.opacity * 100;
sldColorOpacity.id = 'sldColorOpacity';
sldColorOpacity.name = 'sldColorOpacity';
sldColorOpacity.onchange = function(e){
controls.opacity = Number(sldColorOpacity.value) / 100;
saveData();
}
var p7 = document.createElement('p');
p7.style.margin = '10px';
body.appendChild(p7);
p7.appendChild(lblColorOpacity);
p7.appendChild(document.createElement('br'));
p7.appendChild(sldColorOpacity);
////////////////////////////////
var sldInterval = document.createElement('input');
var lblInterval = document.createElement('label');
lblInterval.innerHTML = 'Check/Alert Intrval';
lblInterval.setAttribute('for','sldInterval');
//lblInterval.style.margin = '0px 5px 0px 25px';
sldInterval.type = 'range';
sldInterval.min = 500;
sldInterval.max = 2000;
sldInterval.step = 500;
sldInterval.value = controls.interval;
sldInterval.id = 'sldInterval';
sldInterval.name = 'sldInterval';
sldInterval.onchange = function(e){
controls.interval = Number(sldInterval.value);
saveData();
}
var p5 = document.createElement('p');
p5.style.margin = '10px';
body.appendChild(p5);
p5.appendChild(lblInterval);
p5.appendChild(document.createElement('br'));
p5.appendChild(sldInterval);
////////////////////////////////
var cbTest = document.createElement('input');
var lblTest = document.createElement('label');
lblTest.innerHTML = 'Test';
lblTest.setAttribute('for','cbTest');
cbTest.type = 'checkbox';
cbTest.id = 'cbTest';
cbTest.name = 'cbTest';
cbTest.style.margin = '0 5px';
cbTest.checked = controls.test;
cbTest.onclick = function(e){
if(e.target.checked){
controls.test = true;
}
else{
controls.test = false;
}
saveData();
}
var p8 = document.createElement('p');
p8.style.margin = '10px';
body.appendChild(p8);
p8.appendChild(cbTest);
p8.appendChild(lblTest);
////////////////////////////////
var cbMartWallTargets = document.createElement('input');
var lblMartWallTargets = document.createElement('label');
lblMartWallTargets.innerHTML = 'Mark wall targets';
lblMartWallTargets.setAttribute('for','cbMartWallTargets');
cbMartWallTargets.type = 'checkbox';
cbMartWallTargets.id = 'cbMartWallTargets';
cbMartWallTargets.name = 'cbMartWallTargets';
cbMartWallTargets.style.margin = '0 5px';
cbMartWallTargets.checked = controls.markWallTargets;
cbMartWallTargets.onclick = function(e){
if(e.target.checked){
controls.markWallTargets = true;
}
else{
controls.markWallTargets = false;
}
saveData();
}
var p9 = document.createElement('p');
p9.style.margin = '10px';
body.appendChild(p9);
p9.appendChild(cbMartWallTargets);
p9.appendChild(lblMartWallTargets);
////////////////////////////////
//Add message container
var msgContainer = document.createElement('div');
msgContainer.id = "JoxMsgContainer";
msgContainer.classList.add('cont-gray10', 'bottom-round');
msgContainer.style.backgroundColor = '#e6e6e6';
msgContainer.style.borderTop = '1px solid #cdcdcd';
msgContainer.style.marginTop = '-5px';
msgContainer.style.display = 'none';
container.appendChild(msgContainer);
//Add div to page
insertAfter(container, document.querySelector('#react-root ul.f-war-list'));
}
function saveData(){
//fix colors
var rgba = hexToRgb(controls.color)
rgba.a = controls.opacity;
controls.colorAlert = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`;
//Save data
localStorage.chainWarcher = JSON.stringify(controls);
}
function loadData(){
var savedData = JSON.parse(localStorage.chainWarcher || '{}');
controls.watcherMinimized = savedData.watcherMinimized || false;
//Is chain watcher active
controls.watchChain = (savedData.watchChain === false ? false : true); //to see is chain watcher active or not
//Time tiriger (set minutes and seconds)
controls.minuteAlert = savedData.minuteAlert || 2;
controls.secundeAlert = savedData.secundeAlert || 30;
//Beep notification
controls.beepAlert = savedData.beepAlert || false; //true for beep alerts, false to not beep alert
controls.beepLength = savedData.beepLength || 400; //duration of beep in milisenonds (set more then 50 and 100 less of alert interval value //Default value 400, less annoying value 50 combined with interval 1000
controls.beepType = savedData.beepType || 0; //valid values 0,1,2,3 (0-sine, 1-square, 2-sawtooth, 3-triangle) //Default value 0
controls.beepVolume = savedData.beepVolume || 0.5; //0.5 is 50% volume, use 0.01 for really (1%) low volume and 1 for 100% volume
//What color to display
controls.color = savedData.color || '#ff0000';
controls.opacity = savedData.opacity || 0.2;
var rgba = hexToRgb(controls.color)
rgba.a = controls.opacity;
controls.colorAlert = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`; //color pitcker - https://htmlcolors.com/rgba-color //Default color rgba(255, 0, 0, 0.2), less annoying colour rgba(255, 180, 180, 0.2)
//Alert interval
controls.interval = savedData.interval || 1000; //time in miliseconts (1 secont = 1000 miliseconds) //Default setup 500, less annoying setup 1000, value over 2000 is not recomanded and that value is not that annoying
//for test - alway alert no matter of cahin
controls.test = savedData.test || false;
//for easy target pick
controls.markWallTargets = (savedData.markWallTargets === false ? false : true);
}
//Helper function for more readability
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function hexToRgb(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
})();