WoTStatScript - Tournament Teams

More info for World of Tanks tournament teams

目前為 2016-02-19 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        WoTStatScript - Tournament Teams
// @version     0.9.13.0.5
// @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.5",
		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):not(.js-cm-dropdown-for-mobile-only):not(.cm-user-unauthorized) {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);}",
			"#common_menu .cm-menu__user .cm-parent-link {font-size: 13px; padding: 0 8px 0 10px;}",
			// settings menu rules
			"#common_menu .menu-settings {text-align: left;}",
			"#common_menu .menu-settings .cm-user-menu-link {margin: 0;}",
			"#common_menu .menu-settings .cm-user-menu {padding: 15px; right: unset;}",
			"#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", "<a class='cm-user-menu-link' href='#'><span class='cm-user-menu-link_cutted-text'>"+loc[0]+"</span><span class='cm-arrow'></span></span>"),
	userSet_list = elem("ul", "cm-user-menu", "<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-user-menu-link__opened'); this.nextSibling.classList.toggle('cm-user-menu__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));