// ==UserScript==
// @name WoTStatScript - Tournament Teams
// @version 0.9.13.0.3
// @description More info for World of Tanks tournament teams
// @author Orrie
// @namespace http://forum.worldoftanks.eu/index.php?/topic/263423-
// @icon http://dl.dropboxusercontent.com/u/12497046/wot/projects/statscript/img/icon.png
// @include http://worldoftanks.eu/*/teams/*/
// @include http://worldoftanks.com/*/teams/*/
// @include http://worldoftanks.ru/*/teams/*/
// @include http://worldoftanks.asia/*/teams/*/
// @include http://worldoftanks.kr/*/teams/*/
// @grant GM_xmlhttpRequest
// @connect www.wnefficiency.net
// @connect api.worldoftanks.eu
// @connect api.worldoftanks.ru
// @connect api.worldoftanks.com
// @connect api.worldoftanks.asia
// @connect api.worldoftanks.kr
// @license MIT License
// ==/UserScript==
(function () {
// global vars
var d = document, c = d.cookie;
// get server info and webpage
var wg = {
srv: d.location.host.match(/\.(eu|ru|com|asia|kr)/)[1]
};
// server, API and cluster settings
var sc = {
vers: "0.9.13.0.3",
host: "http://greasyfork.org/en/scripts/13064-wotstatscript-tournament-teams",
user: "http://forum.wotlabs.net/index.php?/user/1618-orrie/",
top: {
eu: "http://forum.worldoftanks.eu/index.php?showtopic=263423",
na: "http://forum.worldoftanks.com/index.php?showtopic=404652"
},
api: {
ru: "98ca7c4fb108175b67d6505b9c3f3ebd",
eu: "a7595640a90bf2d19065f3f2683b171c",
com: "bf5dba0efd444d75147b6222dd903fd2",
asia: "95f8713eccd322e52dbf521dbd28b19c",
kr: "ffea0f1c3c5f770db09357d94fe6abfb"
},
sym: ",",
wn: "http://www.wnefficiency.net/exp/expected_tank_values_latest.json",
loc: c.match(/hllang=(\w+)/)[1],
locSup: ["en", "ru", "cs", "de", "fr", "pl", "es", "tr"]
};
// script threadlink
sc.link = "<div class='b-scriptlink'><a target='_blank' href="+sc.host+">Script</a> v"+sc.vers+" - <a target='_blank' href="+((wg.srv == "na") ? sc.top.na : sc.top.eu)+">Thread</a></div>";
// fetch wnefficiency values - check if array exists in localStorage, otherwise fetch and reload page
var statArr = [],
wnExpValues = locStorage("wnExpValues", "", "get", "parse"),
wnExpDate = locStorage("wnExpDate", "", "get", "parse")+12096e5 >= Date.now(), // true if timestamp is less than 2 weeks old, refresh list if false.
wnExpVers = locStorage("wnExpVers", "", "get", "parse") || "";
if (wnExpVers[0]==sc.vers && wnExpValues && wnExpDate) {
statArr = wnExpValues.data;
}
else {
reqHnd(sc.wn, wnHnd);
}
// style contents
var style = elem("style", "wotstatscript", "", "text/css"),
styleText = [
// script header rules
"#common_menu .cm-menu__user > *:not(.cm-notifications):not(.js-cm-user-menu-dropdown) {display: inline-block}",
"#common_menu .cm-parent-link:hover {cursor: inherit; color: #707273 !important;}",
"#common_menu .b-scriptlink a {color: #E5B12E;}",
"#common_menu .b-scriptlink a:hover {color: #FFBE4C; text-shadow: 0px 0px 7px rgba(255, 126, 0, 0.7);}",
// settings menu rules
"#common_menu .menu-settings {text-align: left;}",
"#common_menu .menu-settings .cm-parent-link:hover {cursor: pointer;}",
"#common_menu .menu-settings label {display: table; line-height: normal; cursor: pointer;}",
"#common_menu .menu-settings .l-box {display: none;}",
"#common_menu .menu-settings .b-checkbox {height: 16px; width: 16px; float: left; margin-right: 5px;}",
"#common_menu .menu-settings .b-checkbox span {height: 16px; width: 16px;}",
"#common_menu .menu-settings .b-combobox-label__checked {color: #DCDCDC;}",
"#common_menu .menu-settings .settingItem .b-combobox-label:hover {color: #DCDCDC;}",
"#common_menu .menu-settings .settingItem .b-combobox-label:hover .b-checkbox {background-position: 0px -34px; box-shadow: 0px 0px 10px 1px rgba(191, 166, 35, 0.15), 0px 0px 3px 1px rgba(191, 166, 35, 0.25);}",
"#common_menu .menu-settings .settingItem .b-combobox-label:hover .b-checkbox.b-checkbox__checked {background-position: 0px -68px;}",
"#common_menu .menu-settings textarea.l-textarea {background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 2px; color: #FFFFFF; line-height: normal; padding: 5px; min-height: 50px; margin: 5px 0 5px 0; min-width: 175px;}",
"#common_menu .menu-settings textarea::-webkit-input-placeholder {color: #FFFFFF;}",
"#common_menu .menu-settings textarea::-moz-placeholder {color: #FFFFFF;}",
"#common_menu .menu-settings .b-settingLink {line-height: 26px;}",
"#common_menu .menu-settings .b-settingLink a {cursor: pointer; color: #B1B2B3; text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.5);}",
"#common_menu .menu-settings .b-settingLink a:hover {color: #FFFFFF; text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.75);}",
// profile main rules
".profile__main {margin: 0; padding: 0;}",
".b-profile-header {font-family: Arial;}",
// member table rules
".t-table th.stat-total {text-align: center;}",
".t-table th.stat-total span {margin: 0 15px;}",
".b-user {width: 50%;}",
".b-user .xvm-lang {margin: 0 0 -1px 3px;}",
".b-user .stat {float: right;}",
".b-user .stat-win {display: inline-block; width: 60px;}",
".b-user .stat-rat {display: inline-block; text-align: right; width: 32px;}"
];
style.textContent = styleText.join("");
d.head.appendChild(style);
// end style
// colour scale array
var colArr = {
// col wr bat wn8 wn7 eff nm pr
sUni: [ "#5A3175", 65, 30000, 2900, 2050, 2050, 2000 ], // 99.99% super unicum
uni: [ "#83579D", 60, 25000, 2450, 1850, 1800, 1950, 9930 ], // 99.90% unicum
gr8: [ "#3972C6", 56, 21000, 2000, 1550, 1500, 1750, 8525 ], // 99.00% great
vGud: [ "#4099BF", 54, 17000, 1600, 1350 ], // 95.00% very good
good: [ "#4D7326", 52, 13000, 1200, 1100, 1200, 1450, 6340 ], // 82.00% good
aAvg: [ "#849B24", 50, 10000, 900 ], // 63.00% above average
avg: [ "#CCB800", 48, 7000, 650, 900, 900, 1250, 4185 ], // 40.00% average
bAvg: [ "#CC7A00", 47, 3000, 450, 700, 600, 1150, 2020 ], // 20.00% below average
bas: [ "#CD3333", 46, 1000, 300, 500 ], // 6.00% basic
beg: [ "#930D0D", 0, 0, 0, 0, 0, 0, 0 ], // 0.00% beginner
dft: [ "#6B6B6B" ], // default
id: { "col": 0, "wr": 1, "bat": 2, "wn8": 3, "wn7": 4, "eff": 5, "nm": 6, "pr": 7 } // type identifier
};
// localization
// cz-czech - Crabtr33 and Ragnarocek
// de-german - ArtiOpa, Crakker and multimill
// fr-french - SuperPommeDeTerre
// pl-polish - KeluMocy and pokapokami
// es-spanish - Frodo45127
// tr-turkish - Ufuko
// ru-russian - dimon222
var loc = [
{ en: "Script Menu", ru: "Script Menu", cs: "Nastavení scriptu", de: "Script Menu", fr: "Script Menu", pl: "Script Menu", es:"Script Menu", tr: "Script Menu"},
{ en: "Refresh WN8 Table", ru: "Refresh WN8 Table", cs: "Obnov WN8 Tabulku", de: "Refresh WN8 Table", fr: "Refresh WN8 Table", pl: "Refresh WN8 Table", es: "Refresh WN8 Table", tr: "Refresh WN8 Table"}
];
// process localization
if (sc.locSup.indexOf(sc.loc) == -1) {
sc.loc = "en";
}
for (var _l=0, l_len = loc.length; _l<l_len; _l++) {
loc[_l] = loc[_l][sc.loc];
}
// add script info if user menu exists, else wait
var clanHead_div = elem("div", "cm-parent-link", sc.link),
userSet_div = elem("div", "menu-settings menu-top_item", "<span class='cm-parent-link js-cm-dropdown-link'>"+loc[0]+"<span class='cm-arrow'></span></span>"),
userSet_list = elem("ul", "cm-sublist", "<li class='settingItem'><div class='b-settingLink'><a>"+loc[1]+" [v"+wnExpVers[1]+"]</a></div></li>"),
navMenu = d.getElementById('common_menu'),
navLook = new MutationObserver(function() {
var navUser = navMenu.getElementsByClassName('cm-menu__user')[0];
navUser.insertBefore(clanHead_div, navUser.firstChild);
navUser.insertBefore(userSet_div, navUser.firstChild);
navLook.disconnect();
});
userSet_div.firstElementChild.addEventListener('click', function() {this.classList.toggle('cm-parent-link__opened'); this.nextSibling.classList.toggle('cm-sublist__opened');}, false);
userSet_list.firstElementChild.firstElementChild.addEventListener('click', function() {localStorage.removeItem("wnExpValues"); location.reload();}, false);
userSet_div.appendChild(userSet_list);
navLook.observe(navMenu, {childList: true});
// create global post variable
var postObj = {
cls: d.getElementsByClassName("b-user"),
ids: [],
num: []
},
s = {clan:{},user:{}};
// fetch userids and store all posts into one obj for later use
for (var _t=0, _t_len = postObj.cls.length; _t<_t_len; _t++) {
if (postObj.cls[_t].firstElementChild) {
var id = postObj.cls[_t].getElementsByTagName('a')[0].getAttribute('href').match(/\/(\d+)\-/)[1];
if (!isNaN(id)) {
var users = postObj.ids.length,
index = postObj.ids.indexOf(id);
s.user[id] = {u:{},v:{frag:0,dmg:0,spot:0,def:0,win:0},wn8:""};
if (index>-1) {
postObj.num[index][postObj.num[index].length] = _t;
}
else {
postObj.ids[users] = id;
postObj.num[users] = [_t];
}
}
}
}
s.clan = {wn8:0, win:0, mem: postObj.ids.length};
// request and retrieve statistics from API
if (postObj.ids.length > 0) {
sc.api.i = "http://api.worldoftanks."+wg.srv+"/wot/account/info/?application_id="+sc.api[wg.srv]+"&account_id="+postObj.ids.join(',');
sc.api.v = "http://api.worldoftanks."+wg.srv+"/wot/account/tanks/?application_id="+sc.api[wg.srv]+"&account_id="+postObj.ids.join(',');
reqHnd(sc.api.i, apiInfoHnd);
}
else {
console.error("No post IDs found or not logged in");
}
// processing information from player API
function apiInfoHnd(resp) {
var data = JSON.parse(resp).data;
for (var a in data) {
if (data.hasOwnProperty(a)) {
var pData = data[a];
if (pData !== null) {
// store stats
var pDataStats = pData.statistics.all;
s.user[pData.account_id].u = {
name: pData.nickname,
id: pData.account_id,
cid: pData.clan_id,
bat: pDataStats.battles,
win: (pDataStats.wins/pDataStats.battles)*100,
dmg: pDataStats.damage_dealt/pDataStats.battles,
frag: pDataStats.frags/pDataStats.battles,
spot: pDataStats.spotted/pDataStats.battles,
def: pDataStats.dropped_capture_points/pDataStats.battles,
wgr: pData.global_rating,
lng: pData.client_language
};
s.clan.win += (!isNaN(s.user[pData.account_id].u.win)) ? s.user[pData.account_id].u.win : 0;
}
}
}
reqHnd(sc.api.v, apiVehHnd);
}
// processing information from vehicle API and calculate WN8
function apiVehHnd(resp) {
var data = JSON.parse(resp).data;
for (var p in data) {
if (data.hasOwnProperty(p)) {
var vData = data[p];
if (vData !== null) {
var rWin, rDmg, rFrag, rSpot, rDef, wn8 = 0;
if (s.user[p].u.bat > 0) {
for (var v in vData) {
if (vData.hasOwnProperty(v)) {
for (var _so=0, _so_len = statArr.length; _so<_so_len; _so++) {
if (statArr[_so].IDNum == vData[v].tank_id) {
var vehStat = statArr[_so],
dataBattles = vData[v].statistics.battles;
s.user[p].v.frag += vehStat.expFrag * dataBattles;
s.user[p].v.dmg += vehStat.expDamage * dataBattles;
s.user[p].v.spot += vehStat.expSpot * dataBattles;
s.user[p].v.def += vehStat.expDef * dataBattles;
s.user[p].v.win += vehStat.expWinRate * dataBattles;
break;
}
}
}
}
rWin = Math.max(((s.user[p].u.win /(s.user[p].v.win /s.user[p].u.bat)-0.71)/(1-0.71)),0);
rDmg = Math.max(((s.user[p].u.dmg /(s.user[p].v.dmg /s.user[p].u.bat)-0.22)/(1-0.22)),0);
rFrag = Math.max(Math.min(rDmg+0.2,((s.user[p].u.frag/(s.user[p].v.frag/s.user[p].u.bat)-0.12)/(1-0.12))),0);
rSpot = Math.max(Math.min(rDmg+0.1,((s.user[p].u.spot/(s.user[p].v.spot/s.user[p].u.bat)-0.38)/(1-0.38))),0);
rDef = Math.max(Math.min(rDmg+0.1,((s.user[p].u.def /(s.user[p].v.def /s.user[p].u.bat)-0.10)/(1-0.10))),0);
wn8 = 980*rDmg + 210*rDmg*rFrag + 155*rFrag*rSpot + 75*rDef*rFrag + 145*Math.min(1.8,rWin);
}
// store wn8 and add to clan total
s.user[p].wn8 = colStat(wn8,"wn8",0);
s.clan.wn8 += wn8;
}
}
}
// calculate average wn8 / winrate
s.clan.wn8 = s.clan.wn8/s.clan.mem;
s.clan.win = s.clan.win/s.clan.mem;
statInsert();
}
// insert stats and links to every post
function statInsert() {
var teamTable = d.getElementsByClassName("t-table")[0];
teamTable.rows[0].cells[0].colSpan = "1";
teamTable.rows[0].appendChild(elem("th", "stat-total", "<span>WN8: "+colStat(s.clan.wn8,"wn8",0)+"</span><span>WR: "+colStat(s.clan.win,"wr",2,"%")+"</span>"));
for (var y in s.user) {
if (s.user.hasOwnProperty(y)) {
var iCell = postObj.ids.indexOf(y);
if (iCell >- 1) {
for (var _i=0, _i_len = postObj.num[iCell].length; _i<_i_len; _i++) {
var cell = postObj.cls[postObj.num[iCell][_i]],
flag = elem("img", "xvm-lang", "", "", "https://bytebucket.org/seriych/worldoftanksforumextendedstat.user.js/raw/tip/data/img/lang/"+s.user[y].u.lng+".png");
flag.title = s.user[y].u.lng.toUpperCase()+" Client";
if (wg.srv !== "ru") {
cell.appendChild(flag);
}
cell.appendChild(elem("div", "stat", "<span class='stat-win'>"+colStat((s.user[y].u.bat > 0) ? s.user[y].u.win : 0,"wr",2,"%")+"</span><span class='stat-rat'>"+s.user[y].wn8+"</span"));
}
}
}
}
}
// helper functions
// colouring
function colStat(input, type, dec, sym) {
var color = colArr.dft[0],
output = input.toFixed(dec);
if (sym) {
output += sym;
}
if (input >= 1000) {
output = input.toFixed(dec).toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1"+sc.sym);
}
for (var c in colArr) {
if (colArr.hasOwnProperty(c)) {
if (input >= colArr[c][colArr.id[type]]) {
color = colArr[c][0]; break;
}
}
}
return "<font color='"+color+"'>"+output+"</font>";
}
// quick creation of element
function elem(tag, name, html, type, src) {
var element = d.createElement(tag);
if (name) {element.className = name;}
if (html) {
if (/</.test(html)) {
element.innerHTML = html;
}
else {
element.textContent = html;
}
}
if (type) {element.type = type;}
if (src) {element.src = src;}
return element;
}
// localStorage handler
function locStorage(name, data, type, mode) {
var storage;
switch(type) {
case ("set"):
if (mode == "string") {
data = JSON.stringify(data);
}
storage = localStorage.setItem(name, data);
break;
case ("get"):
storage = localStorage.getItem(name);
if (mode == "parse") {
storage = JSON.parse(storage);
}
break;
default: break;
}
return storage;
}
// end helper functions
// wnefficiency handler
function wnHnd(resp) {
locStorage("wnExpValues", resp, "set");
locStorage("wnExpDate", Date.now(), "set");
locStorage("wnExpVers", [sc.vers, JSON.parse(resp).header.version], "set", "string");
location.reload();
}
// end wnefficiency handler
// request handler
function reqHnd(url, handler, error) {
GM_xmlhttpRequest({
method: "GET",
url: url,
headers: {
"Accept": "text/xml"
},
onload: function(resp) {
if (resp.readyState == 4 && resp.status == 200 && resp.statusText == "OK") {
handler(resp.responseText);
}
else {
error(resp.responseText);
}
},
onerror: function(resp) {
error(resp.responseText);
}
});
}
}(window));