WoTStatScript - Tournament Teams

More info for World of Tanks tournament teams

Ekde 2016/03/20. Vidu La ĝisdata versio.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        WoTStatScript - Tournament Teams
// @version     0.9.14.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/*/tournaments/*/team/*
// @include     http://worldoftanks.com/*/tournaments/*/team/*
// @include     http://worldoftanks.ru/*/tournaments/*/team/*
// @include     http://worldoftanks.asia/*/tournaments/*/team/*
// @include     http://worldoftanks.kr/*/tournaments/*/team/*
// @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.14.0.5",
		host: "http://greasyfork.org/en/scripts/13064-wotstatscript-tournament-teams",
		user: {
			wl: "http://forum.wotlabs.net/index.php?/user/1618-orrie/",
			wot: "http://worldoftanks.eu/community/accounts/505838943-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"
		},
		wn: "http://www.wnefficiency.net/exp/expected_tank_values_latest.json",
		col: {
			//      col        wr  wgr   wn8
			sUni: [ "#5A3175", 65, 9900, 2900 ], // 99.99% super unicum
			uni:  [ "#83579D", 60, 9000, 2450 ], // 99.90% unicum
			gr8:  [ "#3972C6", 56, 8500, 2000 ], // 99.00% great
			vGud: [ "#4099BF", 54, 6500, 1600 ], // 95.00% very good
			good: [ "#4D7326", 52, 5000, 1200 ], // 82.00% good
			aAvg: [ "#849B24", 50, 4000,  900 ], // 63.00% above average
			avg:  [ "#CCB800", 48, 3000,  650 ], // 40.00% average
			bAvg: [ "#CC7A00", 47, 2000,  450 ], // 20.00% below average
			bas:  [ "#CD3333", 46, 1500,  300 ], //  6.00% basic
			beg:  [ "#930D0D",  0,    0,    0 ], //  0.00% beginner
			dft:  [ "#6B6B6B" ], // default
			id: { col: 0, wr: 1,  wgr: 2, wn8: 3 }  // type identifier
		},
		loc: c.match(/hllang=(\w+)/)[1],
		locSup: ["en", "ru", "cs", "de", "fr", "pl", "es", "tr"],
		date: Date.now()
	};

	// script functions
	var sf = {
		apiInfoHnd: function (resp) { // processing information from player API
			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;
					}
				}
			}
			sf.request(sc.api.v, sf.apiVehHnd);
		},
		apiVehHnd: function (resp) { // processing information from vehicle API and calculate WN8
			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 = sf.color(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;
			sf.statInsert();
		},
		statInsert: function () { // insert stats and links to every post
			var teamWrpr = d.getElementById("team_management"),
			teamHead = d.getElementsByClassName("tournament-table_th")[0];
			teamWrpr.insertBefore(sf.elem("div", "b-stat-total", "<span><span>WN8:</span>"+sf.color(s.clan.wn8,"wn8",0)+"</span><span><span>WR:</span>"+sf.color(s.clan.win,"wr",2,"%")+"</span>"), teamWrpr.firstElementChild.nextSibling);
			teamHead.appendChild(sf.elem("div", "b-stat-head", "<span>WN8</span><span>WR</span>"));
			for (var y in s.user) {
				if (s.user.hasOwnProperty(y)) {
					var userCheck = teamObj.ids.indexOf(y);
					if (userCheck >- 1) {
						var row = d.getElementById(y),
						cellFragment = d.createDocumentFragment(),
						infoHolder = sf.elem("div", "tournament-table_info"),
						infoFlag = sf.elem("img", "i-xvm-lang", "", "", "https://bytebucket.org/seriych/worldoftanksforumextendedstat.user.js/raw/tip/data/img/lang/"+s.user[y].u.lng+".png");
						infoHolder.appendChild(row.cells[0].firstElementChild);
						infoFlag.title = s.user[y].u.lng.toUpperCase()+" Client";
						infoHolder.appendChild(infoFlag);
						cellFragment.appendChild(infoHolder);
						cellFragment.appendChild(sf.elem("div", "tournament-table_stat", "<span class='b-player-stat'>"+s.user[y].wn8+"</span><span class='b-player-stat'>"+sf.color((s.user[y].u.bat > 0) ? s.user[y].u.win : 0,"wr",2,"%")+"</span"));
						row.cells[0].nextElementSibling.innerHTML = "<span class='b-player-stat'>"+sf.color(s.user[y].u.wgr,"wgr",0)+"</span>";
						row.cells[0].appendChild(cellFragment);
					}
				}
			}
			// hide animated loading gear
			loadGif.classList.add("js-hidden");
			// remove dynamic table width
			var teamTable = d.getElementsByClassName("tournament-table")[0];
			teamTable.appendChild(teamTable.firstElementChild.cloneNode(true));
			teamTable.removeChild(teamTable.firstElementChild);
		},
		color: function (input, type, dec, sym) { // color formatting
		var color = sc.col.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"+loc[0]);
			}
			for (var c in sc.col) {
				if (sc.col.hasOwnProperty(c)) {
					if (input >= sc.col[c][sc.col.id[type]]) {
						color = sc.col[c][0]; break;
					}
				}
			}
			return "<font color='"+color+"'>"+output+"</font>";
		},
		elem: function (tag, name, html, type, src) { // element creation
		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;
		},
		settings: function (name, text) { // script menu handler
			var setItem = sf.elem("li", "b-settingItem"),
			setDiv = sf.elem("div", "b-settingParent b-"+name, "<a>"+text+"</a>");
			switch(name) {
				case ("wnRefresh"):
					setDiv.addEventListener('click', function() {localStorage.removeItem("wnExpValues"); location.reload();}, false);
				break;
				default: break;
			}
			setItem.appendChild(setDiv);
			return setItem;
		},
		links: function (parent, links, type) { // statistic links handler
			var linksFragment = d.createDocumentFragment();
			for (var _l=0, _l_len = links.length; _l<_l_len; ++_l) {
				switch(type) {
					case ("table"):
						var link = sf.elem("tr");
						for (var _lr=0, _lr_len = links[_l].length; _lr<_lr_len; ++_lr) {
							link.appendChild((links[_l][_lr][0] && links[_l][_lr][1]) ? sf.elem("td", "", links[_l][_lr][1]) : sf.elem("td", "", links[_l][_lr][0]));
						}
						linksFragment.appendChild(link);
					break;
					case ("list"):
						if (links[_l] instanceof HTMLElement) {
							linksFragment.appendChild(links[_l]);
						}
						else {
							linksFragment.appendChild((links[_l][0] && links[_l][1]) ? sf.elem("li", "", links[_l][1]) : sf.elem("li", "statname", links[_l][0]));
						}
					break;
					default: break;
				}
			}
			parent.appendChild(linksFragment);
		},
		storage: function (name, data, type, mode) { // localstorage handler
			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;
		},
		wn: { // wnefficiency handler
			hnd: function (resp) {
				sf.storage("wnExpValues", resp, "set");
				sf.storage("wnExpDate", sc.date, "set");
				sf.storage("wnExpVers", [sc.vers, JSON.parse(resp).header.version], "set", "string");
				location.reload();
			},
			error: function (error) {
				console.error("Error accessing WNEfficiency.net", error);
			}
		},
		request: function (url, handler, error) { // request handler
			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);
				}
			});
		}
	};

	// add animated loading icon for progress indication
	var pageWrpr = d.getElementsByClassName('page-wrapper')[0],
	loadGif = sf.elem("div", "processing", "<div class='processing_loader'><img src='http://eu.wargaming.net/clans/static/2.2.8/images/processing/loader.gif' alt='Processing...'></div>");
	pageWrpr.appendChild(loadGif);

	// fetch wnefficiency values - check if array exists in localStorage, otherwise fetch and reload page
	var wn = {
		values: sf.storage("wnExpValues", "", "get", "parse"),
		date: sf.storage("wnExpDate", "", "get", "parse")+12096e5 >= sc.date, // true if timestamp is less than 2 weeks old, refresh list if false.
		vers: sf.storage("wnExpVers", "", "get", "parse") || ""
	}, statArr = [];
	if (wn.vers[0]==sc.vers && wn.values && wn.date) {
		statArr = wn.values.data;
	}
	else {
		sf.request(sc.wn, sf.wn.hnd, sf.wn.error);
	}

	// style contents
	var style = sf.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-link_cutted-text {max-width: unset;}",
			"#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-settingParent {line-height: 26px;}",
			"#common_menu .menu-settings .b-settingParent a {cursor: pointer; color: #B1B2B3; text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.5);}",
			"#common_menu .menu-settings .b-settingParent a:hover {color: #FFFFFF; text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.75);}",
			"#common_menu .menu-settings .b-settingParent.b-wnRefresh {text-align: center;}",
			"#common_menu .menu-settings .settingCredits {margin: 2px 0px; text-align: center;}",
			"#common_menu .menu-settings .settingCredits.settingSepertor {border-top: 1px dashed #212123; margin-top: 6px; padding-top: 12px;}",
			"#common_menu .menu-settings .settingCredits p {font-size: 12px;}",
			"#common_menu .menu-settings .settingCredits .b-orange-arrow {color: #F25322; line-height: 14px; padding-right: 9px;}",
			"#common_menu .menu-settings .settingCredits .b-orange-arrow:hover {color: #FF7432;}",
			// processing loader rules
			".processing {width: 100%; height: 100%; position: fixed; top: 0; left: 0; z-index: 1000; background: url(http://eu.wargaming.net/clans/static/2.2.9/images/processing/processing_overlay-pattern.png);}",
			".processing_loader {width: 56px; height: 54px; position: absolute; top: 50%; left: 50%; margin-top: -27px; margin-left: -28px;}",
			// team main rules
			".tournament-heading {display: inline-block;}",
			".b-stat-total {display: inline-block; position: absolute; left: 40%;}",
			".b-stat-total span {font-size: 20px; margin: 0 10px;}",
			".b-stat-total span span {color: #E9E2Bf; font-weight: 400; margin: 0 5px;}",
			// member table rules
			".tournament-table th:first-child .tournament-table_ico-holder, .tournament-table th:first-child .tournament-table_heading-text {float: left;}",
			".b-stat-head {float: right;}",
			".b-stat-head span {font-size: 12px;}",
			".b-stat-head span:first-of-type {margin: 0 20px;}",
			".b-stat-head span:last-of-type {margin: 0 34px;}",
			".tournament-table_td {padding: 8px 2%;}",
			".tournament-table_td .i-xvm-lang {margin-left: 10px; vertical-align: middle;}",
			".tournament-table_td .tournament-table_info {float: left;}",
			".tournament-table_td .tournament-table_stat {float: right;}",
			".tournament-table_td .b-player-stat {font-size: 18px; margin: 0 15px;}",
			// hide elements
			"#team_management > div:last-of-type {display: none;}"
		];
		style.textContent = styleText.join("");
		d.head.appendChild(style);
	// end style

	// localization
	var loc = [
		// thousands separator
		{ en: ",", ru: " ", cs: " ", de: ".", fr: " ", pl: " ", es:".", tr: "."},
		{ 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"},
		{ en: "Script Author:", ru: "Script Author:", cs: "Script Author:", de: "Script Author:", fr: "Script Author:", pl: "Script Author:", es:"Script Author:", tr: "Script Author:"}
	];
	// 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 userSet_div = sf.elem("div", "menu-settings menu-top_item", "<a class='cm-user-menu-link' href='#' onClick='return false;'><span class='cm-user-menu-link_cutted-text'>"+loc[1]+"</span><span class='cm-arrow'></span></span>"),
	userSet_list = sf.elem("ul", "cm-user-menu"),
	userSet_list_items = [
		sf.settings("wnRefresh", loc[2]+" [v"+wn.vers[1]+"]"),
		sf.elem("li", "b-settingItem settingCredits settingSepertor", "<p>"+loc[3]+" <a class='b-orange-arrow' href='"+sc.user.wot+"'>Orrie</a></p>"),
		sf.elem("li", "b-settingItem settingCredits", "<p>Version "+sc.vers+"</p>"),
		sf.elem("li", "b-settingItem settingCredits", "<p><a class='b-orange-arrow' href='"+sc.host+"'>Greasy Fork</a></p>"),
		sf.elem("li", "b-settingItem settingCredits", "<p><a class='b-orange-arrow' href='"+((wg.srv == "na") ? sc.top.na : sc.top.eu)+"'>Support Thread</a></p>")
	],
	navMenu = d.getElementById('common_menu'),
	navLook = new MutationObserver(function() {
		var navUser = navMenu.getElementsByClassName('cm-menu__user')[0];
		navUser.insertBefore(userSet_div, navUser.firstChild);
		navLook.disconnect();
	});
	sf.links(userSet_list, userSet_list_items, "list");
	userSet_div.firstElementChild.addEventListener('click', function() {this.classList.toggle('cm-user-menu-link__opened'); this.nextSibling.classList.toggle('cm-user-menu__opened');}, false);
	userSet_div.appendChild(userSet_list);
	navLook.observe(navMenu, {childList: true});

	// create global post variable
	var teamObj = {
		cls: d.getElementsByClassName("tournament-table_team"),
		ids: []
	},
	s = {clan:{},user:{}};

	// fetch userids and store all posts into one obj for later use
	var teamTable = d.getElementsByClassName("tournament-table")[0].firstElementChild,
	teamLook = new MutationObserver(function(m) {
		var teamCheck = d.getElementsByClassName("tournament-table_team")[0].getAttribute('href');
		if (teamCheck !== "#") {
			teamObj.cls = d.getElementsByClassName("tournament-table_team");
			for (var _t=0, _t_len = teamObj.cls.length; _t<_t_len; _t++) {
				if (teamObj.cls[_t]) {
					var id = teamObj.cls[_t].getAttribute('href').match(/\/(\d+)\-/)[1];
					if (!isNaN(id)) {
						teamObj.cls[_t].parentNode.parentNode.id = id;
						teamObj.ids.push(id);
						s.user[id] = {u:{},v:{frag:0,dmg:0,spot:0,def:0,win:0},wn8:""};
					}
				}
			}
			s.clan = {wn8:0, win:0, mem: teamObj.ids.length};
			// request and retrieve statistics from API
			if (teamObj.ids.length > 0) {
				sc.api.i = "http://api.worldoftanks."+wg.srv+"/wot/account/info/?application_id="+sc.api[wg.srv]+"&account_id="+teamObj.ids.join(',');
				sc.api.v = "http://api.worldoftanks."+wg.srv+"/wot/account/tanks/?application_id="+sc.api[wg.srv]+"&account_id="+teamObj.ids.join(',');
				sf.request(sc.api.i, sf.apiInfoHnd);
			}
			else {
				console.error("No post IDs found or not logged in");
			}
			teamLook.disconnect();
		}
	});
	teamLook.observe(teamTable, {childList: true});
}(window));