Menidan / Grepolis Stats Eroberungsstatistiken

// ==UserScript==
// @name        Grepolis Stats Eroberungsstatistiken
// @namespace   GrepolisStats
// @description Erstellt Statistiken für die Eroberungen von Allianzen.
// @include     http://*.grepostats.com/world/*/alliance/*
// @exclude     http://*.grepostats.com/world/*/alliance/*/members
// @exclude     http://*.grepostats.com/world/*/alliance/*/towns
// @exclude     http://*.grepostats.com/world/*/alliance/*/alliancechanges
// @exclude     http://*.grepostats.com/world/*/alliance/*/colonitations
// @version     1.2
// @grant       none
// ==/UserScript==

function error (wrappedFunction) {
	return function () {
		try {
			wrappedFunction.apply (this, arguments);
		} catch (ex) {
			console.log ("error:", ex);
			throw ex;
		}
	}
}

var creationFormHeader =
	'<div class="table_header smaller">\
		<table style="width: 100%">\
			<tbody>\
				<tr>\
					<td>Eroberungsstatistik</td>\
				</tr>\
			</tbody>\
		</table>\
	</div>';

var creationFormTemplate =
	'<div class="table_content">\
		<table style="width: 100%">\
			<tbody>\
				<tr class="even">\
					<td>\
						Verbündete:\
						<input type="text" value="6500,2516" />\
						<br />\
						Feinde:\
						<input type="text" value="678" />\
						<br />\
						<input type="checkbox" id="eo-stats-count-points" />\
						<label for="eo-stats-count-points">Punkte statt Städte zählen</label>\
						<br />\
						<a href="#">» Statistik erstellen</a>\
						<br />\
						<div style="text-align: center;">0 / 0</div>\
					</td>\
				</tr>\
			</tbody>\
		</table>\
	</div>';

var world = location.pathname.match (/world\/(.*)\/alliance/) [1];
var allianceId = location.pathname.match (/alliance\/(\d+)/) [1];

var statisticsDiv;
var countPointsCheckbox;

function start () {
	var creationFormContainer = $ ("<div>").css ({
		width: "350px",
		float: "left",
		margin: "10px 5px 0"
	}).appendTo (".main > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(1) > table:nth-child(2) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(1)");
	$ (creationFormHeader).appendTo (creationFormContainer);
	var creationForm = $ (creationFormTemplate).appendTo (creationFormContainer);

	var creationFormCreate = creationForm.find ("a").click (error (function () {
		var enemyIdList = creationForm.find ("input").get (1).value.replace (/\s/g, "");
		enemyIdList = enemyIdList.length ? enemyIdList.split (",") : [];
		var allyIdList = creationForm.find ("input").get (0).value.replace (/\s/g, "");
		allyIdList = allyIdList.length ? allyIdList.split (",") : [];

		function testId (id) {
			return /^\d+$/.test (id);
		}

		if (! enemyIdList.every (testId) || ! allyIdList.every (testId)) {
			alert ("Falsches Eingabeformat. Durch Kommata getrennte Liste von Zahlen erwartet.");
			return;
		}

		if (enemyIdList.length)
			createStatistics (enemyIdList, allyIdList);
		else
			alert ("Keine Feinde angegeben");
	}));

	loadingBar = creationForm.find ("div").get (0);
	statisticsDiv = $ ("<div>").appendTo (creationForm.find ("td"));
	countPointsCheckbox = $ ("#eo-stats-count-points");
}


var visitedPagesCount = 0;
var pagesToVisitCount = 0;
var loadingBar;

function onPageVisited () {
	++visitedPagesCount;
	refreshLoadingBar ();
}

function onVisitPage () {
	++pagesToVisitCount;
	refreshLoadingBar ();
}

function refreshLoadingBar () {
	loadingBar.innerHTML = visitedPagesCount + " / " + pagesToVisitCount;
}


function createStatistics (enemyIdList, allyIdList) {
	visitedPagesCount = pagesToVisitCount = 0;
	refreshLoadingBar ();

	//   month     id
	//{"2012-11": {623: {won: 19, lost: 7, wonPoints: 201354, lostPoints: 68108}}};
	var monthTable = {};

	allyIdList.push (allianceId);

	for (var i = 0; i < allyIdList.length; ++i) {
		for (var j = 0; j < enemyIdList.length; ++j) {
			visitPage (allyIdList [i], enemyIdList, monthTable, enemyIdList [j], "win", 0);
			visitPage (allyIdList [i], enemyIdList, monthTable, enemyIdList [j], "lose", 0);
		}
	}

	allyIdList.pop ();
}

function statisticsCreated (enemyIdList, monthTable) {
	getAllianceNames (enemyIdList, error (function (nameTable) {
		var styleElement =
			'<style type="text/css" scoped="scoped">\
				td:not(:first-child) {\
					text-align: right;\
				}\
			</style>';
		var html = "<table border=1>" + styleElement + "<tr><th>Monat</th>";
		var bb = "[table]\n[**]Monat";

		if (enemyIdList.length == 1) {
			html += "<th>Erobert</th><th>Verloren</th>";
			bb += "[||]Erobert[||]Verloren";
		} else {
			for (var i = 0; i < enemyIdList.length; ++i) {
				html += "<th>Erobert von " + nameTable [enemyIdList [i]] + "</th><th>Verloren an " + nameTable [enemyIdList [i]] + "</th>";
				bb += "[||]Erobert von " + nameTable [enemyIdList [i]] + "[||]Verloren an " + nameTable [enemyIdList [i]];
			}
			html += "</tr>";
			bb += "[/**]\n";
		}

		var data = [];
		var sum = createMonth (enemyIdList);
		for (var month in monthTable) {
			var monthParts = month.match (/(\d+)-(\d+)/);
			var year = parseInt (monthParts [1]);
			data.push ({year: year, month: parseInt (monthParts [2]), data: monthTable [month]});
		}
		data.sort (function (month1, month2) { return (month1.year * 12 + month1.month) - (month2.year * 12 + month2.month); });

		monthNameList = ["", "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];

		for (var i = 0; i < data.length; ++i) {
			html += "<tr><td>" + monthNameList [data [i].month] + " " + data [i].year + "</td>";
			bb += "[*]" + monthNameList [data [i].month] + " " + data [i].year;
			for (var j = 0; j < enemyIdList.length; ++j) {
				if (countPointsCheckbox.get (0).checked) {
					html += "<td>" + formatNumber (data [i].data [enemyIdList [j]].wonPoints) + "</td><td>" + formatNumber (data [i].data [enemyIdList [j]].lostPoints) + "</td>";
					bb += "[|]" + formatNumber (data [i].data [enemyIdList [j]].wonPoints) + "[|]" + formatNumber (data [i].data [enemyIdList [j]].lostPoints);
				} else {
					html += "<td>" + formatNumber (data [i].data [enemyIdList [j]].won) + "</td><td>" + formatNumber (data [i].data [enemyIdList [j]].lost) + "</td>";
					bb += "[|]" + formatNumber (data [i].data [enemyIdList [j]].won) + "[|]" + formatNumber (data [i].data [enemyIdList [j]].lost);
				}
				sum [enemyIdList [j]].won += data [i].data [enemyIdList [j]].won;
				sum [enemyIdList [j]].lost += data [i].data [enemyIdList [j]].lost;
				sum [enemyIdList [j]].wonPoints += data [i].data [enemyIdList [j]].wonPoints;
				sum [enemyIdList [j]].lostPoints += data [i].data [enemyIdList [j]].lostPoints;
			}
			html += "</tr>";
			bb += "[/*]\n";
		}

		html += '<tr><td style="font-weight: bold;">Gesamt</td>';
		bb += "[*][b]Gesamt[/b]";

		for (var i = 0; i < enemyIdList.length; ++i) {
			if (countPointsCheckbox.get (0).checked) {
				html += '<td style="font-weight: bold;">' + formatNumber (sum [enemyIdList [i]].wonPoints) + '</td><td style="font-weight: bold">' + formatNumber (sum [enemyIdList [i]].lostPoints) + "</td>";
				bb += "[|][b]" + formatNumber (sum [enemyIdList [i]].wonPoints) + "[/b][|][b]" + formatNumber (sum [enemyIdList [i]].lostPoints) + "[/b]";
			} else {
				html += '<td style="font-weight: bold;">' + formatNumber (sum [enemyIdList [i]].won) + '</td><td style="font-weight: bold">' + formatNumber (sum [enemyIdList [i]].lost) + "</td>";
				bb += "[|][b]" + formatNumber (sum [enemyIdList [i]].won) + "[/b][|][b]" + formatNumber (sum [enemyIdList [i]].lost) + "[/b]";
			}
		}

		html += "</tr></table>";
		bb += "[/*][/table]";

		$ (statisticsDiv).html (html + "<textarea>" + bb + "</textarea>");
	}));
}

function formatNumber (number) {
	// s. http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript/2901298#2901298
	return number.toString ().replace (/\B(?=(\d{3})+(?!\d))/g, ".");
}

function visitPage (allianceId, enemyIdList, monthTable, enemyId, colonizationType, pageNumber) {
	onVisitPage ();

	var request = new XMLHttpRequest ();
	request.addEventListener ("load", error (pageVisited).bind (null, allianceId, enemyIdList, monthTable, enemyId, colonizationType, pageNumber, request));
	request.url = "/world/" + world + "/alliance/" + allianceId + "/colonizations?type=" + colonizationType + "&enemy_alliance=" + enemyId + "&page=" + pageNumber;
	request.open ("get", request.url, true);
	request.send ();
}

function pageVisited (allianceId, enemyIdList, monthTable, enemyId, colonizationType, pageNumber, request) {
	if (request.readyState == 4) {
		if (request.status != 200) {
			alert ("error while visiting page " + request.url);
			return;
		}

		onPageVisited ();

		var html = request.responseText;
		var parser = new DOMParser ();
		var xmlDocument = parser.parseFromString (html, "text/html");
		var contentTable = xmlDocument.querySelectorAll (".table_content") [1];

		if (pageNumber == 0) {
			var pages = contentTable.querySelectorAll (".footer a");
			var pageCount = pages.length ? parseInt (pages [pages.length - 1].href.match (/page=(\d+)/) [1]) : 0;
			for (var i = 1; i < pageCount; ++i)
				visitPage (allianceId, enemyIdList, monthTable, enemyId, colonizationType, i);
		}

		var lines = contentTable.querySelectorAll ("tr");

		for (var i = 0; i < lines.length - 1; ++i) {
			var line = lines [i];
			var points = parseInt (line.children [2].innerHTML.replace (/\D/g, ""));
			var month = line.children [7].innerHTML.substring (0, 7);
			monthTable [month] = monthTable [month] || createMonth (enemyIdList);
			if (colonizationType == "win") {
				monthTable [month] [enemyId].won += 1;
				monthTable [month] [enemyId].wonPoints += points;
			} else {
				monthTable [month] [enemyId].lost += 1;
				monthTable [month] [enemyId].lostPoints += points;
			}
		}


		if (visitedPagesCount == pagesToVisitCount)
			statisticsCreated (enemyIdList, monthTable);
	}
}

function createMonth (enemyIdList) {
	var data = {};
	for (var i = 0; i < enemyIdList.length; ++i)
		data [enemyIdList [i]] = {won: 0, lost: 0, wonPoints: 0, lostPoints: 0};
	return data;
}

function getAllianceNames (allianceIdList, callback) {
	var nameTable = {};
	for (var i = 0; i < allianceIdList.length; ++i)
		visitAlliancePage (allianceIdList, allianceIdList [i], nameTable, callback);
}

function visitAlliancePage (allianceIdList, allianceId, nameTable, callback) {
	onVisitPage ();

	var request = new XMLHttpRequest ();
	request.addEventListener ("load", error (alliancePageVisited).bind (null, allianceIdList, allianceId, nameTable, callback, request));
	request.url = "/world/" + world + "/alliance/" + allianceId;
	request.open ("get", request.url, true);
	request.send ();
}

function alliancePageVisited (allianceIdList, allianceId, nameTable, callback, request) {
	if (request.readyState == 4) {
		if (request.status != 200) {
			alert ("error while visiting page " + request.url);
			return;
		}

		onPageVisited ();

		var html = request.responseText;
		var parser = new DOMParser ();
		var xmlDocument = parser.parseFromString (html, "text/html");

		var nameElement = xmlDocument.querySelector (".main > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(1) > h2:nth-child(1) > a:nth-child(1)");
		nameTable [allianceId] = nameElement ? nameElement.innerHTML : "(Fehler, ID: " + allianceId + ")";

		if (visitedPagesCount == pagesToVisitCount)
			callback (nameTable);
	}
}


error (start) ();