// ==UserScript==
// @name Company Enhancement Suite
// @namespace LordBusiness.CES
// @version 3.4.8
// @description Your one stop solution to managing your company.
// @author LordBusiness
// @match *.torn.com/companies.php
// @run-at document-end
// @license MIT
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(
`.last_action_icon {
cursor: pointer;
vertical-align: middle;
display: inline-block;
background-image: url(/images/v2/sidebar_icons_desktop_2017.png);
background-repeat: no-repeat;
background-position-y: -783px;
width: 34px;
height: 30px;
}
.stock-list li:not(.total) .delivery {
background-color: white;
}
.order.hidden ~ .fill-custom,
.order.hidden ~ .fill-daily {
display: none;
}`
);
const APIkeyName = 'TornApiKey';
let APIkey = localStorage.getItem(APIkeyName),
itemsStock = JSON.parse(localStorage.getItem("TornCustomStock") || '{}'),
keyUpEvent = new KeyboardEvent('keyup');
/* Credits to Mafia [610357] for the original snippet of code from his excellent library, tornlib.
Rewritten in Vanilla JS to stay safe from the jQuerypocalypse. */
const getAPIkey = () => {
localStorage.removeItem(APIkeyName);
document.querySelector('div.content-title').insertAdjacentHTML('afterend',
`<div id='apibox' class="m-top10">
<div class="title-gray top-round" role="heading" aria-level="5">
<i class="issue-attention-icon"></i>
<span id="title">API Key required</span>
</div>
<div class="bottom-round cont-gray p10" id="api-input-wrapper">
<fieldset class="submit-wrap">
<p>You are currently using <strong>${GM.info.script.name}</strong> that requires your API key. Please fill in your API key to use it.</p>
<br>
<div class="cont-quantity left">
<div class="input-money-group">
<a href="/preferences.php#tab=api" target="_blank" title="Fill with your correct API key" class="input-money-symbol">KEY</a><input id="api-input" class="quantity price input-money" type="text" value="">
</div>
</div>
<div class="cont-button left" id="apiSignIn" style="margin-left: 10px;">
<span class="btn-wrap silver">
<span class="btn c-pointer bold" style="padding: 0 15px 0 10px;"><span>SIGN IN</span></span>
</span>
</div>
<div class="clear"></div>
</fieldset>
</div>
<hr class="page-head-delimiter m-top10">
</div>`
);
const apiWrapper = document.getElementById("api-input-wrapper");
document.getElementById("apiSignIn").addEventListener('click', () => {
APIkey = document.getElementById("api-input").value.trim();
fetch(`https://api.torn.com/user/?selections=profile&key=${APIkey}`)
.then(response => response.json())
.then(data => {
if(data.error) {
alert(data.error.error);
apiWrapper.querySelector(".input-money-group").classList.add("error");
} else {
localStorage.setItem(APIkeyName, APIkey);
apiWrapper.innerHTML = `Hi ${data.name}, you have successfully signed in with your API key. This page will be refreshed in a moment.`;
// This reload is allowed. IceBlueFire: That's fine (Wed, 12:47 - TCT 05/06/19)
setTimeout(() => location.reload(), 3000);
}
});
});
}
// Add the t-red class to parent element
const makeRed = theElement => theElement.parentElement.classList.add('t-red');
// Check trains to make sure you don't lose a few tomorrow
const checkTrains = () => {
const trains = document.querySelector(".trains"),
stars = document.querySelector(".company-rating").querySelectorAll("li.active").length;
if(parseInt(trains.innerText) + stars > 20) {
makeRed(trains);
}
}
// Check if employee capacity is filled
const checkEmployees = () => {
const currentEmployees = document.querySelector(".total-employees"),
maxEmployees = document.querySelector(".limit-employees")
if(parseInt(maxEmployees.innerText) - parseInt(currentEmployees.innerText)) {
makeRed(maxEmployees);
}
}
/* Employee last action is a refined (AKA semi-colonified) version of tos [1976582]'s Faction Last Action script.
See https://www.torn.com/forums.php#/p=threads&f=67&t=16046522&b=0&a=0&to=18638360
I'm starting to wonder if my script is just a compilation of everybody else's scripts. */
const generateTimeColor = timeInterval => {
if (timeInterval.includes('minute')) return 'ftGreen';
else if (timeInterval.includes('hour')) return (parseInt(timeInterval.split(' ')[0]) <= 12) ? 't-green' : 'ftDarkGold';
return 't-red';
}
const toggleLastAction = (columnTitle, memberList) => {
if (columnTitle.innerText === 'Rank') {
columnTitle.childNodes[0].nodeValue = 'Last Action';
for (const li of memberList.children) {
const lastActionDIV = li.querySelector('.last-action');
const memberID = lastActionDIV.getAttribute('data-member-ID');
fetch(`https://api.torn.com/user/${memberID}?selections=profile&key=${APIkey}`)
.then(response => response.json())
.then(response => {
if(response.error) {
console.log(response.error.error)
if(response.error.code == 2) getAPIkey()
}
const lastAction = response.last_action.relative;
li.querySelector('.rank .employee-rank-drop-list').classList.toggle('hide');
lastActionDIV.innerText = lastAction;
lastActionDIV.classList.add(generateTimeColor(lastAction));
lastActionDIV.classList.toggle('hide');
});
}
}
else {
columnTitle.childNodes[0].nodeValue = 'Rank';
for (const li of memberList.children) {
li.querySelector('.rank .employee-rank-drop-list').classList.toggle('hide');
li.querySelector('.last-action').classList.toggle('hide');
}
}
}
const employeeCallback = node => {
const columnTitle = node.querySelector('.employee-list-title .rank');
const memberList = node.querySelector('.employee-list');
columnTitle.insertAdjacentHTML('beforeend', `<i class="last_action_icon right" title="Toggle Last Action"></i>`);
node.querySelector('.last_action_icon').addEventListener('click', () => toggleLastAction(columnTitle, memberList));
for (const li of memberList.children) {
const memberID = li.getAttribute('data-user');
li.querySelector('.rank .employee-rank-drop-list').insertAdjacentHTML('afterend', `<div class="last-action hide" data-member-id="${memberID}"></div>`);
}
}
/* So why did I decide to rewrite this old, obselete script?
Good question. When I saw Mathiaas [1918010]'s Stock Order script, I thought to myself,
"Hey, I've made a script just like this! Wonder what happened to that."
And thus, I started working on this one. You can start crying now */
const giveMeNumber = numberString => parseInt(numberString.replace(/[^0-9]/g, ''));
const fillInput = (element, value) => {
if(value <= 0) return;
element.value = value;
element.dispatchEvent(keyUpEvent); // Send keyboard event so that Torn correctly registers input.
}
const updateItemStocks = (item, itemName) => {
itemsStock[itemName] = giveMeNumber(item.innerText);
localStorage.setItem("TornCustomStock", JSON.stringify(itemsStock));
}
const stockCallback = node => {
let itemList = node.querySelectorAll('.stock-list li:not(.total)'),
items = new Map();
for(let item of itemList) {
let itemName = item.querySelector('.name').innerText;
items.set(itemName, {
input: item.querySelector(".quantity input[type=text]"),
stock: giveMeNumber(item.querySelector('.stock').innerText),
soldDaily: giveMeNumber(item.querySelector('.sold-daily').innerText)
});
let itemStock = item.querySelector('.delivery');
itemStock.innerHTML = itemsStock[itemName] || 0;
itemStock.contentEditable = 'true';
itemStock.addEventListener('blur', () => updateItemStocks(itemStock, itemName));
}
// Add "Fill daily" button to stock form
node.querySelector('span.order.btn-wrap.silver').insertAdjacentHTML('afterend',
`<span class="fill-custom btn-wrap silver"><span class="btn disable" role="button">FILL CUSTOM</span></span>
<span class="fill-daily btn-wrap silver"><span class="btn disable" role="button">FILL DAILY</span></span>`);
// Click Handler for fill-custom
node.querySelector('span.fill-custom').addEventListener('click', () => {
items.forEach((item, itemName, items) => {
let remainingStock = (itemsStock[itemName] || 0) - item.stock;
fillInput(item.input, remainingStock);
});
});
// Click Handler for fill-daily
node.querySelector('span.fill-daily').addEventListener('click', () => {
items.forEach((item, itemName, items) => {
fillInput(item.input, item.soldDaily);
});
});
// Hide delivered orders
let orders = document.querySelectorAll('#stock .order-list li');
for(let order of orders) {
if(order.innerText.includes('Delivered')) {
order.style.display = 'none';
} else {
// And add incoming orders to items map
let orderName = order.querySelector('.name').innerText;
let orderAmount = giveMeNumber(order.querySelector('.amount').innerText);
items.get(orderName).stock += orderAmount;
}
}
node.querySelector('.stock-list-title .delivery').innerHTML = `Total<div class="t-delimiter"></div>`;
}
const observe = (elementId, callback) => {
(new MutationObserver(mutations => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.tagName === 'FORM') {
callback(node);
return;
}
}
}
}))
.observe(document.getElementById(elementId), {
childList: true
});
}
checkTrains();
checkEmployees();
if(APIkey === null) {
getAPIkey();
} else {
observe('employees', employeeCallback);
}
observe('stock', stockCallback);
})();