WoTStatScript - ForumsExtendedStat

Adds stats and links for posts on official World of Tanks forums

As of 2015-06-26. See the latest version.

// ==UserScript==
// @name        WoTStatScript - ForumsExtendedStat
// @version     1.06
// @description Adds stats and links for posts on official World of Tanks forums
// @author      Orrie
// @contributor seriych
// @namespace   http://forum.worldoftanks.eu/index.php?/topic/263423-
// @icon        http://dl.dropboxusercontent.com/u/12497046/wot/projects/statscript/img/icon.png
// @include     /^http:\/\/forum\.worldoftanks\.(eu|com|ru|asia|kr)\/\S+(\/topic\/|\?showtopic=)/
// @include     /^http:\/\/forum\.worldoftanks\.(eu|com|ru|asia|kr)\/index\.php\?app=members&module=messaging&section=view&do=showConversation&topicID=\d+/
// @grant       GM_xmlhttpRequest
// @license     MIT License
// ==/UserScript==
(function() {
// server, API and cluster settings
var sc = {
	vers: "1.06",
	host: "http://greasyfork.org/scripts/660-wotstatscript-forumsextendedstat",
	top: {
		eu: "http://forum.worldoftanks.eu/index.php?showtopic=263423",
		na: "http://forum.worldoftanks.com/index.php?showtopic=404652"
	},
	srv: document.location.host.match(/\.(eu|ru|com|asia|kr)/)[1],
	api: {
		ru: "98ca7c4fb108175b67d6505b9c3f3ebd",
		eu: "a7595640a90bf2d19065f3f2683b171c",
		com: "bf5dba0efd444d75147b6222dd903fd2",
		asia: "95f8713eccd322e52dbf521dbd28b19c",
		kr: "ffea0f1c3c5f770db09357d94fe6abfb"
	},
	sym: ",",
	wn: "http://www.wnefficiency.net/exp/expected_tank_values_latest.json"
};
// script threadlink
	sc.link = "<div class='b-scriptlink'><a target='_blank' href="+sc.host+">Script</a> version "+sc.vers+" - <a target='_blank' href="+((sc.srv == "na") ? sc.top.na : sc.top.eu)+">Thread</a></div>";

// external site support - wotlabs, noobmeter, wot-news, wotstats, wotcs, wot-life, wotstats.com.au and wotreplays
var srv = {
	s: "en", wl: false, nm: false, ws: false, wr: false, wn: false, cs: false, wlife: false
};
switch(sc.srv) {
	case ("eu"): // eu server
		srv.wl = srv.nm = srv.ws = srv.wr = srv.wn = srv.cs = srv.wlife = sc.srv;
		break;
	case ("ru"): // ru server
		srv.wl = srv.nm = srv.ws = srv.wr = srv.wn = srv.cs = sc.srv;
		srv.s = "ru";
		sc.sym = " ";
		break;
	case ("com"): // na server
		srv.wl = srv.nm = srv.ws = srv.cs = "na"; srv.wr = "com"; srv.wn = "us";
		break;
	case ("asia"): // asia server
		srv.wl = srv.nm = srv.ws = srv.wn = srv.cs = "sea"; srv.wr = "com";
		break;
	case ("kr"): // korean server
		srv.wl = srv.nm = srv.ws = srv.cs = srv.ch = sc.srv; srv.wr = "com";
		break;
	default: break;
}

// data uri
var uri = {
	icon_arrow:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPwAAAAJCAMAAAAhKrH/AAAAV1BMVEUAAAD///+TKCf9AQmTLiX9AQmTMir9AQmTLif9AQmTMSz9AQmTMir9AQmTMyn9AQmTMirvGgKTMirsIAGTMirwGQOTMiqTMirsIAGaNDGgNzfqJwDrJABtE0DNAAAAGnRSTlMAABERIiIzM0REVVVmZnd3iIqZmrvDzN3g7thzQuwAAACHSURBVHja5dVLDsIwDEXRCySlP0IMoXXs7H+dDBASA1bQ3B0cPclm2Fe2wrfTQeO3qwqqAKWek80HxfO/l1/EBRgtU2tf+KkJ7TO9hdVSV3jU42f6ZClY6Qt/d4n+BGol29jTwQN1pE2QbA610BVefImuwL7xsNAXXhVpEYY8kNfO/rwsyA3ekEsOT2YDLE8AAAAASUVORK5CYII="
};

// fetch wnefficiency values - check if array exists in localStorage, otherwise fetch and reload page
var statObj = {},
	wnExpValues = JSON.parse(localStorage.getItem("wnExpValues")),
	wnExpDate = JSON.parse(localStorage.getItem("wnExpDate"))+1728e5 >= Date.now(), // true if timestamp is less than 2 days old, refresh list if false.
	wnExpVers = JSON.parse(localStorage.getItem("wnExpVers")) || "";
if (wnExpVers[0]==sc.vers && wnExpValues && wnExpDate) {
	statObj = wnExpValues.data;
}
else {
	reqHnd(sc.wn, wnHnd);
}

// style contents
var style = elem("style", "wotstatscript", "", "text/css"),
	styleText = [
		".b-scriptlink a {color: #E5B12E;}",
		".cm-link__get-gold {margin: 0 12px;}",
		".author_info {z-index: 1;}",
		".user_details > br {display: none;}",
		".basic_info {margin: 0 0 4px;}",
		".post_count.margin-bottom {margin: 0 0 5px;}",
		".battles_count {display: none;}",
		".i-xvm-lang {vertical-align: text-top;}",
		".t-table-stats {margin: 5px 0; width: auto;}",
		".t-table-stats td {padding: 0 5px 0 0; line-height: 16px;}",
		".t-table-stats td:last-of-type {font-weight: bold;}",
		".t-tables-links {background: #EDEDED; border: 1px solid #d5d5d5; border-radius: 3px; box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1), 0 0 3px 3px rgba(255, 255, 255, 0.2); display: inline-block; margin: 5px 0 5px -3px; overflow: hidden; padding: 3px 3px 0; text-overflow: ellipsis; white-space: nowrap; width: 90px;}",
		".t-tables-links:hover {border-color: #7D7D7D; width: 180px;}",
		".t-tables-links tbody {display: table; width: 180px;}",
		".t-tables-links td {padding: 0 0 2px; font-weight: bold;}",
		".t-tables-links td a { background: url("+uri.icon_arrow+") no-repeat scroll 0 2px rgba(0, 0, 0, 0); padding: 0 0 0 10px; vertical-align: middle;}",
		".t-tables-links td a:hover {background: url("+uri.icon_arrow+") no-repeat scroll -244px 2px rgba(0, 0, 0, 0);}",
		".t-tables-links tr:last-of-type td {}",
		".realm_kr .t-tables-links {width: 108px;}",
		".realm_kr .t-tables-links:hover {width: 215px;}",
		".realm_kr .t-tables-links tbody {width: 215px;}"
	];
	style.textContent = styleText.join("");
	document.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
var loc = {
	f00: { en: "Client", ru: "Клиент"},
	f01: { en: "Winrate:", ru: "Винрейт:"},
	f02: { en: "Battles:", ru: "Боев:"},
	f03: { en: "Rating:", ru: "Рейтинг:"},
	f04: { en: "WN8:", ru: "WN8:"}
	//f00: { en: "f00_en", ru: "f00_ru"},
};
for (var l in loc) {
	if (loc.hasOwnProperty(l)) {
		loc[l] = loc[l][srv.s];
	}
}

// script link
var clanHead_div = elem("div", "cm-parent-link", sc.link),
	navService_class = document.getElementsByClassName('cm-menu')[1];
	navService_class.insertBefore(clanHead_div, navService_class.firstChild);

// formula calculations and variables
// create global post variable
var postObj = {
	cls: document.querySelectorAll(".post_wrap, .post_wrap__wg"),
	ids: [],
	num: []
},
s = {},
id, users, index;

// 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++) {
	id = postObj.cls[_t].getElementsByClassName("ipsUserPhotoLink")[0].getAttribute('href').match(/\-(\d+)\//)[1];
	if (!isNaN(id)) {
		s[id] = {u:{},v:{frag:0,dmg:0,spot:0,def:0,win:0},wn8:""};
		users = postObj.ids.length;
		index = postObj.ids.indexOf(id);
		if (index>-1) {
			postObj.num[index][postObj.num[index].length] = _t;
		}
		else {
			postObj.ids[users] = id;
			postObj.num[users] = [];
			postObj.num[users][0] = _t;
		}
	}
}

// request and retrieve statistics from API
if (postObj.ids.length > 0) {
	sc.api.i = "http://api.worldoftanks."+sc.srv+"/wot/account/info/?application_id="+sc.api[sc.srv]+"&account_id="+postObj.ids.join(',');
	sc.api.v = "http://api.worldoftanks."+sc.srv+"/wot/account/tanks/?application_id="+sc.api[sc.srv]+"&account_id="+postObj.ids.join(',');
	reqHnd(sc.api.i, apiInfoHnd);
}

// processing information from player API
function apiInfoHnd(resp) {
	var data = JSON.parse(resp).data;
	for (var a in data) {
		if (data.hasOwnProperty(a)) {
			// store stats
			s[data[a].account_id].u = {
				name: data[a].nickname,
				id: data[a].account_id,
				cid: data[a].clan_id,
				bat: data[a].statistics.all.battles,
				win: (data[a].statistics.all.wins/data[a].statistics.all.battles)*100,
				dmg: data[a].statistics.all.damage_dealt/data[a].statistics.all.battles,
				frag: data[a].statistics.all.frags/data[a].statistics.all.battles,
				spot: data[a].statistics.all.spotted/data[a].statistics.all.battles,
				def: data[a].statistics.all.dropped_capture_points/data[a].statistics.all.battles,
				wgr: data[a].global_rating,
				lng: data[a].client_language
			};
		}
	}
	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 rWin, rDmg, rFrag, rSpot, rDef;
			if (s[p].u.bat > 0) {
				for (var v in data[p]) {
					if (data[p].hasOwnProperty(v)) {
						for (var _so=0, _so_len = statObj.length; _so<_so_len; _so++) {
							if (statObj[_so].IDNum == data[p][v].tank_id) {
								var vehStat = statObj[_so];
								s[p].v.frag += vehStat.expFrag    *data[p][v].statistics.battles;
								s[p].v.dmg  += vehStat.expDamage  *data[p][v].statistics.battles;
								s[p].v.spot += vehStat.expSpot    *data[p][v].statistics.battles;
								s[p].v.def  += vehStat.expDef     *data[p][v].statistics.battles;
								s[p].v.win  += vehStat.expWinRate *data[p][v].statistics.battles;
								break;
							}
						}
					}
				}
				rWin  = Math.max(((s[p].u.win /(s[p].v.win /s[p].u.bat)-0.71)/(1-0.71)),0);
				rDmg  = Math.max(((s[p].u.dmg /(s[p].v.dmg /s[p].u.bat)-0.22)/(1-0.22)),0);
				rFrag = Math.max(Math.min(rDmg+0.2,((s[p].u.frag/(s[p].v.frag/s[p].u.bat)-0.12)/(1-0.12))),0);
				rSpot = Math.max(Math.min(rDmg+0.1,((s[p].u.spot/(s[p].v.spot/s[p].u.bat)-0.38)/(1-0.38))),0);
				rDef  = Math.max(Math.min(rDmg+0.1,((s[p].u.def /(s[p].v.def /s[p].u.bat)-0.10)/(1-0.10))),0);
				s[p].wn8 = colStat(980*rDmg + 210*rDmg*rFrag + 155*rFrag*rSpot + 75*rDef*rFrag + 145*Math.min(1.8,rWin),"wn8",0);
			}
			else {
				s[p].wn8 = colStat(0,"wn8",0);
			}
		}
	}
	statInsert();
}

// insert stats and links to every post
function statInsert() {
	for (var y in s) {
		if (s.hasOwnProperty(y)) {
			var iPost = postObj.ids.indexOf(y),
			userStats = [
				"<td>"+loc.f01+"</td><td>"+colStat((s[y].u.bat > 0) ? s[y].u.win : 0,"wr",2,"%")+"</td>",
				"<td>"+loc.f02+"</td><td>"+colStat(s[y].u.bat,"bat",0,"")+"</td>",
				"<td>"+loc.f03+"</td><td>"+colStat(s[y].u.wgr,"pr",0,"")+"</td>",
				"<td>"+loc.f04+"</td><td>"+s[y].wn8+"</td>"
			],
			userLinks = [
				[
					["<a target='_blank' href='http://worldoftanks."+sc.srv+"/community/accounts/"+s[y].u.id+"-"+s[y].u.name+"/'>Player Profile</a>"],
					[srv.ws, "<a target='_blank' href='http://wotstats.org/stats/"+srv.ws+"/"+s[y].u.name+"/'>WoTstats</a>"]
				],
				[
					[srv.wl, "<a target='_blank' href='http://wotlabs.net/"+srv.wl+"/player/"+s[y].u.name+"'>WoTLabs</a>"],
					[srv.cs, "<a target='_blank' href='http://wotcs.com/player.php?wid="+s[y].u.id+"'>WoT-CS</a>"]
				],
				[
					[srv.nm, "<a target='_blank' href='http://noobmeter.com/player/"+srv.nm+"/"+s[y].u.name+"'>Noobmeter</a>"],
					[srv.wr, "<a target='_blank' href='http://wotreplays."+srv.wr+"/player/"+s[y].u.name+"'>WoTReplays</a>"]
				],
				[
					[srv.wn, "<a target='_blank' href='http://wot-news.com/stat/pstat/"+srv.wn+"/"+s[y].u.name+"/'>Wot-news</a>"],
					(sc.srv=="ru") ? [srv.wr, "<a target='_blank' href='http://wots.com.ua/user/stats/"+s[y].u.name+"'>WoTS.com.ua</a>"] : [srv.wlife, "<a target='_blank' href='http://en.wot-life.com/"+srv.wlife+"/player/"+s[y].u.name+"/'>WoT-Life</a>"]
				]
			];
			if (iPost >- 1) {
				for (var _i=0, _i_len = postObj.num[iPost].length; _i<_i_len; _i++) {
					var groupTitle = postObj.cls[postObj.num[iPost][_i]].getElementsByClassName("group_title")[0],
						basicInfo = postObj.cls[postObj.num[iPost][_i]].getElementsByClassName("basic_info")[0],
						infoClass = postObj.cls[postObj.num[iPost][_i]].getElementsByClassName("author_info")[0],
						statTable = elem("table", "t-table-stats", ""),
						urlTable = elem("table", "t-tables-links", "<tbody></tbody>"),
						flag = elem("img", "i-xvm-lang", "", "", "https://bytebucket.org/seriych/worldoftanksforumextendedstat.user.js/raw/tip/data/img/lang/"+s[y].u.lng+".png");
						flag.title = s[y].u.lng.toUpperCase()+" "+loc.f00;
					for (var _sr=0, sr_len = userStats.length; _sr<sr_len; ++_sr) {
						statTable.appendChild(elem("tr", "", userStats[_sr]));
					}
					for (var _l=0, _l_len = userLinks.length; _l<_l_len; ++_l) {
						var uRow = elem("tr", "", "");
						for (var _lr=0, _lr_len = userLinks[_l].length; _lr<_lr_len; ++_lr) {
							uRow.appendChild((userLinks[_l][_lr][0] && userLinks[_l][_lr][1]) ? elem("td", "", userLinks[_l][_lr][1]) : elem("td", "", userLinks[_l][_lr][0]));
						}
						urlTable.firstElementChild.appendChild(uRow);
					}
					if (sc.srv !== "ru") {
						groupTitle.appendChild(flag);
					}
					basicInfo.insertBefore(statTable, basicInfo.lastElementChild);
					infoClass.appendChild(urlTable);
				}
			}
		}
	}
}

// wnefficiency handler
function wnHnd(resp) {
	localStorage.setItem("wnExpValues", resp);
	localStorage.setItem("wnExpDate", Date.now());
	localStorage.setItem("wnExpVers", JSON.stringify([sc.vers, JSON.parse(resp).header.version]));
	location.reload();
}
// end wnefficiency handler

// request handler
function reqHnd(url, handler) {
	GM_xmlhttpRequest({
		method: "GET",
		url: url,
		onload: function(resp) {
			handler(resp.responseText);
		}
	});
}

// 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 = document.createElement(tag);
	if (name) {element.className = name;}
	if (html) {element.innerHTML = html;}
	if (type) {element.type = type;}
	if (src) {element.src = src;}
	return element;
}

}());