Mori / Agent Stats Calendar Maker

// ==UserScript==
// @name		 Agent Stats Calendar Maker
// @namespace	http://ingress.agent-stats.operuserjs/
// @version	  0.9.0
// @description  See your daily progress in a more clean way
// @author	   Mori
// @match		https://www.agent-stats.com/export.php
// @require	  https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js
// @require	  https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js
// @grant		none
// ==/UserScript==

(function(_, $) {
	// Taken from http://stackoverflow.com/a/2901298
	var numberWithCommas = function(x) {
		if(!x) {
			x = 0;
		}
		return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
	};

	var userData = {
		'level': 1,
		'medalCount': {
			'medals': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'unofficialMedals': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0}
		},
		'medals': {
			'explorer': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'seer': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'trekker': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'builder': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'connector': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'mind-controller': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'illuminator': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'recharger': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'liberator': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'pioneer': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'engineer': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'purifier': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'guardian': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'specops': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'hacker': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'recruiter': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'translator': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'sojourner': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0}
		},
		'unofficialMedals': {
			'collector': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'mastermind': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'binder': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'country-master': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'neutralizer': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'disruptor': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'salvator': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'smuggler': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'link-master': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'controller': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
			'field-master': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0},
            'hackathon': {'bronze': 0, 'silver': 0, 'gold': 0, 'platinum': 0, 'black': 0}
		}
	};

	var requirements = {
		'levels': [
			{'ap': 0, 'medals': null},
			{'ap': 2500, 'medals': null},
			{'ap': 20000, 'medals': null},
			{'ap': 70000, 'medals': null},
			{'ap': 150000, 'medals': null},
			{'ap': 300000, 'medals': null},
			{'ap': 600000, 'medals': null},
			{'ap': 1200000, 'medals': null},
			{'ap': 2400000, 'medals': {'silver': 4, 'gold': 1}},
			{'ap': 4000000, 'medals': {'silver': 5, 'gold': 2}},
			{'ap': 6000000, 'medals': {'silver': 6, 'gold': 4}},
			{'ap': 8400000, 'medals': {'silver': 7, 'gold': 6}},
			{'ap': 12000000, 'medals': {'platinum': 1, 'gold': 7}},
			{'ap': 17000000, 'medals': {'platinum': 2}},
			{'ap': 24000000, 'medals': {'platinum': 3}},
			{'ap': 40000000, 'medals': {'platinum': 4, 'black': 2}}
		],
		'medals': {
			'explorer': {'bronze': 100, 'silver': 1000, 'gold': 2000, 'platinum': 10000, 'black': 30000},
			'seer': {'bronze': 10, 'silver': 50, 'gold': 200, 'platinum': 500, 'black': 5000},
			'trekker': {'bronze': 10, 'silver': 100, 'gold': 300, 'platinum': 1000, 'black': 2500},
			'builder': {'bronze': 2000, 'silver': 10000, 'gold': 30000, 'platinum': 100000, 'black': 200000},
			'connector': {'bronze': 50, 'silver': 1000, 'gold': 5000, 'platinum': 25000, 'black': 100000},
			'mind-controller': {'bronze': 100, 'silver': 500, 'gold': 2000, 'platinum': 10000, 'black': 40000},
			'illuminator': {'bronze': 5000, 'silver': 50000, 'gold': 250000, 'platinum': 1000000, 'black': 4000000},
			'recharger': {'bronze': 100000, 'silver': 1000000, 'gold': 3000000, 'platinum': 10000000, 'black': 25000000},
			'liberator': {'bronze': 100, 'silver': 1000, 'gold': 5000, 'platinum': 15000, 'black': 40000},
			'pioneer': {'bronze': 20, 'silver': 200, 'gold': 1000, 'platinum': 5000, 'black': 20000},
			'engineer': {'bronze': 150, 'silver': 1500, 'gold': 5000, 'platinum': 20000, 'black': 50000},
			'purifier': {'bronze': 2000, 'silver': 10000, 'gold': 30000, 'platinum': 100000, 'black': 200000},
			'guardian': {'bronze': 3, 'silver': 10, 'gold': 20, 'platinum': 90, 'black': 150},
			'specops': {'bronze': 5, 'silver': 25, 'gold': 100, 'platinum': 200, 'black': 500},
			'hacker': {'bronze': 2000, 'silver': 10000, 'gold': 30000, 'platinum': 100000, 'black': 200000},
            'recruiter': {'bronze': 2, 'silver': 10, 'gold': 25, 'platinum': 50, 'black': 100},
			'translator': {'bronze': 200, 'silver': 2000, 'gold': 6000, 'platinum': 20000, 'black': 50000},
			'sojourner': {'bronze': 15, 'silver': 30, 'gold': 60, 'platinum': 180, 'black': 360}
		},
		'unofficialMedals': {
			'collector': {'bronze': 100000, 'silver': 1000000, 'gold': 10000000, 'platinum': 100000000, 'black': 200000000},
			'mastermind': {'bronze': 100000, 'silver': 1000000, 'gold': 10000000, 'platinum': 50000000, 'black': 100000000},
			'binder': {'bronze': 10, 'silver': 200, 'gold': 800, 'platinum': 1300, 'black': 1800},
			'country-master': {'bronze': 5000, 'silver': 30000, 'gold': 100000, 'platinum': 1000000, 'black': 5000000},
			'neutralizer': {'bronze': 100, 'silver': 1000, 'gold': 5000, 'platinum': 15000, 'black': 40000},
			'disruptor': {'bronze': 50, 'silver': 1000, 'gold': 5000, 'platinum': 25000, 'black': 100000},
			'salvator': {'bronze': 100, 'silver': 500, 'gold': 2000, 'platinum': 10000, 'black': 40000},
			'smuggler': {'bronze': 1, 'silver': 5, 'gold': 10, 'platinum': 45, 'black': 75},
			'link-master': {'bronze': 1, 'silver': 10, 'gold': 50, 'platinum': 200, 'black': 1000},
			'controller': {'bronze': 1, 'silver': 3, 'gold': 8, 'platinum': 30, 'black': 50},
			'field-master': {'bronze': 5000, 'silver': 40000, 'gold': 150000, 'platinum': 400000, 'black': 1000000},
            'hackathon': {'bronze': 5, 'silver': 30, 'gold': 75, 'platinum': 150, 'black': 200}
		}
	};

	var medalCategories = {
		'medals': 'Official',
		'unofficialMedals': 'Unofficial'
	};

	var specialMedals = {
		'engineer': '2014-12-15',
		'specops': '2014-12-15',
		'trekker': '2014-12-15',
		'innovator': '2014-11-15',
		'recharger': '2014-02-06',
        'recruiter': '2015-01-15',
		'translator': '2015-01-29',
		'illuminator': '2015-02-26',
		'sojourner': '2015-03-06'
	};

	var medalTypes = ['bronze', 'silver', 'gold', 'platinum', 'black'];
	
	var dataHarverster = function () {
		var fieldNames = _.map($('#StatsExport thead tr th'), function(element) { return $(element).text().trim(); });
		fieldNames.splice(0, 1);

		var points = _.map($('#StatsExport tbody tr'), function(line) { return $(line); });
		var dates = {};

		_.each(points, function(point) {
			var cells = $(point).find('td');
			var date = $(cells[0]).text().trim().split(' ')[0];
			cells.splice(0, 1);
			dates[date] = {};
			
			_.each(cells, function(pointValue, index) {
				dates[date][fieldNames[index]] = $(pointValue).text().trim();
			});
		});
		
		drawCalendar(dates);
	};
	
	var congratulateOnMedal = function(medalName, medalType) {
		return $('<span>Congratulations on gaining '+medalType+' '+cleanName(medalName)+' medal!</span><br />');
	};

	var congratulateOnLevel = function(level) {
		return $('<span>Congratulations on reaching '+level+' level!</span><br />');
	};

	var calculateDifference = function(data, previousDay, index) {
		return previousDay ? numberWithCommas(data[index] - previousDay[index]) : numberWithCommas(data[index]);
	};
	
	var calculateLevelsAndMedals = function(data, day) {
		var updates = $('<div></div>');
		updates.addClass('levelsAndMedalsUpdates');

		if(userData.innovator && day >= '2014-11-15') {
			_.each(medalTypes, function(medalType) {
				userData.medalCount.medals[medalType]++;
				if(medalType === userData.innovator) {
					updates.append(congratulateOnMedal('innovator', medalType));
					delete userData.innovator;
					return false;
				}
				return true;
			});
		}

		_.each(medalCategories, function(medalCategoryName, medalCategoryIndex) {
			_.each(Object.keys(requirements[medalCategoryIndex]), function(medalName) {
				if(specialMedals[medalName] && specialMedals[medalName] > day) {
					return true;
				}

				_.each(medalTypes, function(medalType) {
					if(userData[medalCategoryIndex][medalName][medalType] !== 0) {
						return true;
					} else {
						if(data[medalName] >= requirements[medalCategoryIndex][medalName][medalType]) {
							userData[medalCategoryIndex][medalName][medalType] = 1;
							userData.medalCount[medalCategoryIndex][medalType]++;
							updates.append(congratulateOnMedal(medalName, medalType));
							return true;
						} else {
							return false;
						}
					}
				});
			});
		});

		_.each(requirements.levels, function(levelRequirements, level) {
			if(level > 7 && day < '2014-05-08') {
				return false;
			}

			if(userData.level < (level+1)) {
				if(data.ap >= levelRequirements.ap) {
					if(levelRequirements.medals === null) {
						updates.append(congratulateOnLevel(level+1));
						userData.level++;
						return true;
					} else {
						var eligible = true;
						_.each(levelRequirements.medals, function(medalCount, medalType) {
							if(userData.medalCount.medals[medalType] < medalCount) {
								eligible = false;
								return false;
							}
						});

						if(eligible) {
							updates.append(congratulateOnLevel(level+1));
							userData.level++;
							return true;
						} else {
							return false;
						}
					}
				}
			} else {
				return true;
			}
			return false;
		});

		return updates;
	};

	var cleanName = function(name) {
		return name.replace('-', ' ').replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g, function($1) {
		  return $1.toUpperCase();
		});
	};
	
	var drawCalendar = function (dates) {
		var calendar = $('<div id="calendar"><h2>Calendar</h2></div>');
		calendar.attr('style', 'box-sizing: border-box;');
		var previousDay = null;
		
		_.each(dates, function(data, day) {
			var singleDay = $('<fieldset></fieldset>');
			singleDay.attr('id', 'day-'+day).addClass('float').attr('style', 'box-sizing: border-box; width: 48%; float: left; margin: 0px 1%; padding: 0px 5px;');
			singleDay.append('<legend>'+day+'</legend>');
			

			_.each(medalCategories, function(medalCategoryName, medalCategoryIndex) {
				var medalFieldsetHolder = $('<fieldset></fieldset>');
				medalFieldsetHolder.addClass('float').attr('style', 'box-sizing: border-box; float: left; width: 48%; margin: 0 1% 30px;');
				medalFieldsetHolder.append('<legend>'+medalCategoryName+'</legend>');

				var medalTable = $('<table></table>');
				medalTable.attr('style', 'width: 90%');
				medalTable.append('<thead><tr><th>Stat</th><th>Value</th><th>Change</th></tr></thead>');
				if(medalCategoryIndex === 'medals') {
					medalTable.append('<tr><td>AP</td> <td>'+numberWithCommas(data.ap)+'</td> <td>'+calculateDifference(data, previousDay, 'ap')+'</td></tr>');
				}
				_.each(Object.keys(requirements[medalCategoryIndex]), function(medalName) {
					medalTable.append('<tr><td>'+cleanName(medalName)+'</td> <td>'+numberWithCommas(data[medalName])+'</td> <td>'+calculateDifference(data, previousDay, medalName)+'</td></tr>');
				});
				medalFieldsetHolder.append(medalTable);
				singleDay.append(medalFieldsetHolder);
			});

			singleDay.append(calculateLevelsAndMedals(data, day));
			calendar.append(singleDay);

			previousDay = data;
		});

		var medalSummary = $('<fieldset></fieldset>');
		medalSummary.attr('id', 'medalSummary').addClass('float').attr('style', 'box-sizing: border-box; width: 48%; float: left; margin: 0px 1%; padding: 0px 5px;');
		medalSummary.append('<legend>Medal summary</legend>');
		
		_.each(medalCategories, function(medalCategoryName, medalCategoryIndex) {
			var medalFieldsetHolder = $('<fieldset></fieldset>');
			medalFieldsetHolder.addClass('float').attr('style', 'box-sizing: border-box;');
			medalFieldsetHolder.append('<legend>'+medalCategoryName+'</legend>');

			var medalsSummaryTable = $('<table></table>');
			medalsSummaryTable.attr('style', 'width: 90%');
			medalsSummaryTable.append('<thead><tr><th>Stat</th><th>Count</th></tr></thead>');
			_.each(medalTypes, function(medalType) {
				medalsSummaryTable.append('<tr><td>'+cleanName(medalType)+'</td> <td>'+numberWithCommas(userData.medalCount[medalCategoryIndex][medalType])+'</td></tr>');
			});
			medalFieldsetHolder.append(medalsSummaryTable);
			medalSummary.append(medalFieldsetHolder);
		});

        calendar.append(medalSummary);
		calendar.append('<div style="clear: both;"></div>');
		calendar.insertAfter('#myheader');
	};

	var parseInnovatorMedal = function() {
		var innovator = window.localStorage.innovator;

		if(innovator) {
			userData.innovator = innovator;
		}
	};

	$(document).ready(function() {
		parseInnovatorMedal();
		dataHarverster();
	});
})(_, jQuery);