<commit_before><!DOCTYPE html><!-- (c) 2015 Briskat, http://www.briskat.com, License: MIT -->
<html lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="max-age=0">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="expires" content="Tue, 01 Jan 1970 1:00:00 GMT">
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Git dashboard">
    <meta name="author" content="Briskat">
    <meta property="og:image" content="http://www.briskat.com/img/posts/FIXME.png">
    <meta name="og:description" content="Git analytics">
    <meta name="og:title" content="Git analytics">
    <meta name="og:type" content="website">
    <title>Briskat - gitcharts</title>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
	<script type="text/javascript" src="https://www.google.com/jsapi"></script>
	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
	<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
	<script type="text/javascript">
	google.load('visualization', '1.0', {'packages':['corechart']});
	google.setOnLoadCallback(initMe);

	var GS = new Array(); // reuse graph
	var S = { tab : 1,
			days : 10,
		};

	function initMe() {
		var tab=parseInt(getUrlParameter('tab'));
		if (tab > 1 && tab < 8) { S['tab']=tab; }
		if (S['tab'] < 4 ) { $("#daybtn").hide(); }
			else { $("#daybtn").show(); }
		var days=parseInt(getUrlParameter('days'));
		if (days > 0 && days < 366) { S['days']=days; }
		loadProjects();

		$("#dashtabs li:eq("+(S['tab']-1)+") a").tab('show');
		$(window).resize(function(){ refresh(S['days'],0); });
		$("#day_minus").click(function() { S['days']=refresh(S['days'],-10); });
		$("#day_plus").click(function() { S['days']=refresh(S['days'],10); });
		$("#project_div").change(function() { refresh(S['days'],0); });
		$("#link_btn").click(function() { prompt('Paste current URL', getCurUrl()) });

		$('.nav a').on('click', function(){ $('.navbar-toggle').click() });

		$(document).ready(function(){
			$('a[data-toggle="tab"]').on('shown.bs.tab', function(e){
				var act = $(e.target).attr('href'); // Get the name of active tab
				S['tab'] = act.substring(4, act.length);
				if (S['tab'] < 4) {
					$("#daybtn").hide();
				} else {
					$("#daybtn").show();
				}
				GS = null;
				GS = new Array();
				refresh(S['days'],0);
			});
		});
		refresh(S['days'],0);
	//	jQuery(document).ready(function(e) { jQuery('#help_btn').trigger('click'); });
	}

	function getCurUrl() {
		var cUrl = "?proj=" + encodeURIComponent(getActiveProject()) + "&days=" + S['days'] + "&tab=" + S['tab'];

		var base = window.location.href
		if (base.indexOf("?")>-1){
			base = base.substr(0,base.indexOf("?"));
		}
		return base + cUrl.substring(0, cUrl.length);
	}

	function dump(obj) {
		prompt('Dump', JSON.stringify(obj));
	}

	function getUrlParameter(sParam,Url) {
		var sPageURL = window.location.search.substring(1);
		if ( Url ) { sPageURL = Url; }
		var sURLVariables = sPageURL.split('&');
		for (var i = 0; i < sURLVariables.length; i++) {
			var sParameterName = sURLVariables[i].split('=');
			if (sParameterName[0] == sParam) {
				return  decodeURIComponent(sParameterName[1]);
			}
		}
	}

	function loadProjects() {
		var jsonData = getData("/api/p/");
		var options = $("#project_div");
		var proj=getUrlParameter('proj');
		$.each(jsonData['projects'], function() {
			if ( this.name == proj ) {
				options.append($("<option selected/>").val(this.name).text(this.name));
			} else {
				options.append($("<option />").val(this.name).text(this.name));
			}
		});
	}

	function getData(url) {
		return $.ajax({
			url: url,
			dataType: "json",
			async: false
		}).responseJSON;
	}

    function order_by_col(idx) {
        return function (a, b) {
            return b[idx] == a[idx] ? 0
                : b[idx] > a[idx] ? 1
                    : -1;
        }
    }
	function order_abs(a, b) {
		return Math.abs(b[1]) == Math.abs(a[1]) ? 0
			: Math.abs(b[1]) > Math.abs(a[1]) ? 1
			: -1;
	}
	function order_2val(a, b) {
		return ( b[1] == a[1] && b[2] == a[2] )  ? 0
			: (b[1] >= b[2] && b[1] > a[1] && b[1] > a[2] ) ? 1
			: (b[2] > b[1] && b[2] > a[1] && b[2] > a[2] ) ? 1
			: -1;
	}
	function chop(body,limit) {
		return limit > 0 ? body.slice(0,limit)
			: limit < 0 ? body.slice(limit,body.length)
			: body;
	}

	function runSum(body) {
		if (body.length > 0) {
			for (i = 1; i < body.length; i++) {
				for (j = 1; j < body[i].length; j++) {
					body[i][j]+=body[i-1][j];
				}
			}
			return body;
		} else {
			return body;
		}
	}
	function empty_data (header) {
		var data =[['']];
		for (i = 1; i < header[0].length; i++) {
			data[0][i] = 0;
		}
		return data;
	}

	function metric2Data(jsonData,header,fields) {
		if (jsonData.length == 0) return empty_data(header);

		return $.map(jsonData, function(n,i){
				var row = new Array();
				row.push(n[0]);
				for ( var idx in fields ) {
					// round makes 0 from null values
					row.push(Math.round(n[fields[idx]]));
				}
			   return [row];
			});
	}

	function data2Table(header, data) {
		// var dataTable = google.visualization.arrayToDataTable(header.concat(data));
		var dataTable = new google.visualization.DataTable();
		dataTable.addColumn('string', header[0][0]);
		for (i = 1; i < header[0].length; i++) {
			dataTable.addColumn('number',header[0][i]);
		}
		dataTable.addRows(data);
		var formatter = new google.visualization.NumberFormat(
		    { negativeColor: 'red', negativeParens: true, pattern: '###,###.#'});
		for (i = 0; i < data[0].length; i++) {
			formatter.format(dataTable, i);
		}
		return dataTable;
	}

	function format1024(val) {
		var unit = '';
		if ( Math.abs(val) > 1073741824 ) {
			val = Math.round(val*10/1073741824)/10;
			unit = 'GB'
		}
		else if ( Math.abs(val) > 1048576 ) {
			val = Math.round(val*10/1048576)/10;
			unit = 'MB'
		}
		else if ( Math.abs(val) > 1024 ) {
			val = Math.round(val*10/1024)/10;
			unit = 'kB'
		}

		return val+unit;
	}

	function tHeadSum1(body,span) {
		$(span[0]).text(body[0].toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, '$1,'));
		$(span[1]).text(body[3].toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, '$1,'));
	}
	function tHeadSum2(body,span) {
		$(span[0]).text(body[2].toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, '$1,'));
	}
	function tHeadSum3(body,span) {
		$(span[0]).text(format1024(body[1]));
	}

	function tMerge(body,span) {
		$(span[0]).text(body[0][0] === "M" ? body[0][1] : body[1] ? body[1][1] : '0');
	}

	function newCommiters(body,span) {
		var authors="";
		var url = 'https://' + getProjectGurl(getActiveProject()) + '/commit/';
		body.sort(order_by_col(0));
		body.reverse();
		for (i = 0; i < body.length; i++) {
			if ( body[i][2] ) {
				authors = authors + '<a href="' + url + body[i][1]  + '">' + body[i][0] + '</a> (' + body[i][2] + ') ';
			}
		}
		$(span[0]).html( authors );
	}

	function zeroes(cnt,type,zero) {
		return Array.apply(null, Array(cnt)).map(type,zero);
	}
	function areaStacked(data) {
		var pos = {}, cnt = 0;
		for ( var i in data ) {
			if ( !pos[data[i][1]] ) {
				cnt++;
				pos[data[i][1]] = cnt;
			}
		}
		if ( cnt == 0 ) {
			return [[["",""]],[["",0]]];
		}
		cnt++;

		var header = zeroes(cnt, String.prototype.valueOf,"");
		Object.keys(pos).forEach(function (key) {
				header[pos[key]] = key;
		})

		var lastX, stackData=[], row = zeroes(cnt,Number.prototype.valueOf,0);
		for ( var i in data ) {
			if ( lastX !== undefined && lastX !== data[i][0] || i==data.length ) {
				row[0]=lastX;
				stackData.push(row);
				row = zeroes(cnt,Number.prototype.valueOf,0);
			}
			row[pos[data[i][1]]] = Math.round(100*data[i][2])/100; // cannot be null - problem in Google graph?
			lastX = data[i][0];
		}
		row[0]=lastX;
		stackData.push(row);
		return [[header],stackData];
	}

	function drawDashboard(Project,days) {
			gv = google.visualization;
			var Iurl = 0, Igrid = 1, actTID = S['tab']-1;
			var tab = [
				[
					[
						"/r/summary_mon",
						[
							[ gv.LineChart, 'commits_m_div', { vAxis: {minValue: 0} }, [['Month','Commits']], [1], runSum ],
							[ gv.LineChart, 'commiters_y_div', {}, [['Date','Commiters']], [4] ]
						]
					],
					[
						"/r/summary",
						[ [ 'oneNumber', tHeadSum1, [ "span#one_commits","span#one_authors" ] ] ]
					],
					[
						"/r/summary_comtype",
						[ [ 'oneNumber', tMerge, [ "span#merge_commits" ] ] ]
					],
					[
						"/r/summary_a_comtype",
						[ [ 'oneNumber', tMerge, [ "span#merge_authors" ] ] ]
					],
					[
						"/r/new_commiters?days=30",
						[ [ 'oneNumber', newCommiters, [ "span#new_authors" ] ] ]
					]
				],
				[
					[
						"/r/summary_mon",
						[
							[ gv.LineChart, 'files_m_div', {vAxis: {minValue: 0}}, [['Date','Files']], [3], runSum, fileListener ]
						]
					],
					[
						"/r/summary",
						[ [ 'oneNumber', tHeadSum2, [ "span#one_files" ] ] ]
					],
				],
				[
					[
						"/r/summary",
						[ [ 'oneNumber', tHeadSum3, [ "span#one_size" ] ] ]
					],
					[
						"/r/summary_ext_size",
						[
							[ gv.PieChart, 'ext_size_div', {}, [['Suffix','Size']], [1],
								function (mydata) { var col1=order_by_col(1);mydata.sort(col1); return mydata } ]
						]
					],
					[
						"/r/summary_mon",
						[
							[ gv.LineChart, 'month_ft_size_div', {vAxis: {minValue: 0}}, [['Date','Size']], [2], runSum, fileListener ]
						]
					],
					[
						"/r/summary_ft_size",
						[
							[ gv.PieChart, 'ft_size_div', {}, [['File Type','Size']], [1],
								function (mydata) { var col1=order_by_col(1);mydata.sort(col1); return mydata } ]
						]
					],
					[
						"/r/summary_mon_ft_size",
						[
							[ gv.AreaChart, 'yr_ft_size_div', {isStacked : true},,,runSum ]
						]
					]
				],
				[
					[
						"/r/last_x_days?days=" + days,
						[
							[ gv.ColumnChart, 'commits_div', {isStacked: true}, [['Date','Regular','Merge']], [7,8] ],
							[ gv.ColumnChart, 'size_div', {}, [['Date','Size']], [1] ],
							[ gv.LineChart,   'files_div', {}, [['Date','Modified Files','Deleted Files','Added Files']], [6,5,4] ],
							[ gv.LineChart,   'lines_div', {}, [['Date','Lines Added','Lines Deleted']], [2,3] ]
						]
					]
				],
				[
					[
						"/r/last_x_days_c?days=" + days,
						[
							[ gv.ColumnChart, 'big_commit_div',
								{ title : 'Click on column for the detail.' },
								[['Date','Size']],
								[1],
								function (mydata) { mydata.sort(order_abs); return chop(mydata,12) },
								commitListener
							]
						],
					]
				],
				[
					[
						"/r/last_x_days_a?days=" + days,
						[
							[ gv.PieChart, 'rcommit_chart_div', {},
								[['Author','Commits']],
								[5],
								function (mydata) { var col1=order_by_col(1);mydata.sort(col1); return mydata }
							],
							[ gv.PieChart, 'mcommit_chart_div', {},
								[['Author','Commits']],
								[6],
								function (mydata) { var col1=order_by_col(1);mydata.sort(col1); return mydata }
							],
							[ gv.ColumnChart, 'commit_size_div', {},
								[['Author','Size']],
								[1],
								function (mydata) { mydata.sort(order_abs); return chop(mydata,8) }
							],
							[ gv.ColumnChart, 'author_lines', {},
								[['Author','Lines Added','Lines Deleted']],
								[2,3],
								function (mydata) { mydata.sort(order_2val); return chop(mydata,8) }
							]
						]
					]
				],
				[
					[
						"/r/last_x_days_a?days=" + days,
						[
							[ gv.BubbleChart, 'bubble_div',
								{
									hAxis: {title: 'Lines Added'},
									vAxis: {title: 'Lines Removed'},
									explorer: { maxZoomOut: 20 }
								},
								[['Author','Lines Added','Lines Deleted','Size','Commits']],
								[2,3,1,4]
							]
						]
					]
				]
			];

			if ( GS[actTID] == undefined ) {
				var GST = new Array(); // reuse graph
				for ( var i in tab[actTID] ) {
					var tabs = tab[actTID][i][Igrid];
					for ( var j in tabs ) {
						if ( tabs[j][0] === 'oneNumber' ) {
							GST.push(0);
						} else {
							GST.push(new tabs[j][0](document.getElementById(tabs[j][1])));
						}
					}
				}
				GS[actTID] = GST;
			}

			var g_idx=-1,data,transform,header;
			for ( var i in tab[actTID] ) {
				var Data = getData("/api/p/" + Project + tab[actTID][i][Iurl]);
				for ( var j in tab[actTID][i][Igrid] ) {
					g_idx++;
					var report = tab[actTID][i][Igrid][j]
					if ( report[0] == 'oneNumber' ) {
						report[1](Data,report[2]);
						break;
					}
					transform = report[5];
					if ( report[4] ) {
						header = report[3];
						data = metric2Data(Data, header, report[4]);
					} else if ( transform && report[2] && "isStacked" in report[2] ) {
						var stacked = areaStacked(Data);
						header = stacked[0];
						data = stacked[1];
					} else {
						header = report[3];
						data = Data.length == 0 ? empty_data(header) : Data;
					}
					if ( transform ) { data=transform(data); }
					var Table = data2Table(header, data);
					var chart=GS[actTID][g_idx];
					if ( report[6] ) {
						report[6](chart,Table,g_idx);
					}
					chart.draw(Table, report[2]);
				}
			}
	}

	function githubSucc( response ) {
		var meta = response.meta;
		var data = response.data;
		var Msg = "No detail available.";
		if ( meta["X-RateLimit-Remaining"] == 0 ) {
			var d = new Date(0);
			d.setUTCSeconds(meta["X-RateLimit-Reset"]);
			Msg = 'No commit detail available from GitHub until ' + d.toString();
		} else {
			Msg = data['message'].replace(/\n\n/, "</h3><br /></br>");
			Msg = Msg.replace(/\n/g, "<br />");
			Msg = '<h4> '+ data['author']['name'] + ', <small>' + data['author']['date'] + '</small></h4><h3> ' + Msg;
			//</h3>
		}
		$( "div#com_detail" ).html( Msg );
	}

	function getInfo(proj,value) {
		var url=proj.split(' (');
		var gurl = "https://api.github.com/repos/" + url[1].substring(0, url[1].length - 1)
			+ '/' + url[0] + "/git/commits/" + value;
		$.ajax({
			url: gurl,
			jsonp: "callback",
			dataType: "jsonp",
			success: githubSucc,
			error: function( e ) {
				$( "div#com_detail" ).html( 'Error to get commit detail' );
			}
		});
	}

	function commitListener(c,t) {
		var chart=c;
		var Table=t;
		google.visualization.events.removeAllListeners(c);
		function selectHandler() {
			var selectedItem = chart.getSelection()[0];
			if (selectedItem) {
				var value = Table.getValue(selectedItem.row, 0);
				var proj = getActiveProject();
				var url = 'https://' + getProjectGurl(proj) + '/commit/' + value;
				getInfo(proj,value);
				//githubSucc(1);
				$( "span#com_sha1" ).text( value.substring(0, 12) );
				$( "span#com_url" ).html( '<a class="btn btn-success" href="' + url
					+ '"> View detail on GitHub</a>' );
				$( "span#comnew_url" ).html( '<a class="btn btn-success" href="' + url
					+ '" target="_blank"><span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>' );
				$('#com_dialog').modal("show");
			}
		}
		google.visualization.events.addListener(chart, 'select', selectHandler);
	}

	function getActiveProject () {
		var e = document.getElementById("project_div");
		return e.options[e.selectedIndex].value;
	}

	function getProjectGurl(Proj) {
		var url=Proj.split(' (');
		return 'github.com/' + url[1].substring(0, url[1].length - 1) + '/' + url[0];
	}

	function refresh(days,delta) {
		if ( days+delta < 1 || days+delta > 366 )
			return days;
		var Project = getActiveProject();
		days=days+delta;
		drawDashboard(Project, days);
		$( "span#last_days" ).text( days );
		$( "span#github_url" ).html( '<a class="btn btn-lg btn-success" href="https://' + getProjectGurl(Project)
			+ '"><span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>' );
		return days;
	}
</script>
<style>
.navbar {
	background-color:#000;
}

.vh45{ height: 45vh }
.vh50{ height: 50vh }
.vh78{ height: 78vh }
</style>
</head>

<body style="padding: 56px 0px 0px 0px;">
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
	<div class="navbar-header">
	  <select id="project_div" class="btn btn-success btn-lg dropdown-toggle"></select>
	  <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
		<span class="sr-only">Toggle navigation</span>
		<span class="icon-bar"></span>
		<span class="icon-bar"></span><commit_msg>Allow to exclude reports<commit_after><!DOCTYPE html><!-- (c) 2015 Briskat, http://www.briskat.<EMAIL>, License: MIT -->
<html lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="max-age=0">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="expires" content="Tue, 01 Jan 1970 1:00:00 GMT">
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Git dashboard">
    <meta name="author" content="Briskat">
    <meta property="og:image" content="http://www.briskat.com/img/posts/FIXME.png">
    <meta name="og:description" content="Git analytics">
    <meta name="og:title" content="Git analytics">
    <meta name="og:type" content="website">
    <title>Briskat - gitcharts</title>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
	<script type="text/javascript" src="https://www.google.com/jsapi"></script>
	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
	<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
	<script type="text/javascript">
	google.load('visualization', '1.0', {'packages':['corechart']});
	google.setOnLoadCallback(initMe);

	var GS = new Array(); // reuse graph
	var S = { tab : 1,
			days : 10,
		};

	function initMe() {
		var tab=parseInt(getUrlParameter('tab'));
		if (tab > 1 && tab < 8) { S['tab']=tab; }
		if (S['tab'] < 4 ) { $("#daybtn").hide(); }
			else { $("#daybtn").show(); }
		var days=parseInt(getUrlParameter('days'));
		if (days > 0 && days < 366) { S['days']=days; }
		loadProjects();

		$("#dashtabs li:eq("+(S['tab']-1)+") a").tab('show');
		$(window).resize(function(){ refresh(S['days'],0); });
		$("#day_minus").click(function() { S['days']=refresh(S['days'],-10); });
		$("#day_plus").click(function() { S['days']=refresh(S['days'],10); });
		$("#project_div").change(function() { refresh(S['days'],0); });
		$("#link_btn").click(function() { prompt('Paste current URL', getCurUrl()) });

		$('.nav a').on('click', function(){ $('.navbar-toggle').click() });

		$(document).ready(function(){
			$('a[data-toggle="tab"]').on('shown.bs.tab', function(e){
				var act = $(e.target).attr('href'); // Get the name of active tab
				S['tab'] = act.substring(4, act.length);
				if (S['tab'] < 4) {
					$("#daybtn").hide();
				} else {
					$("#daybtn").show();
				}
				GS = null;
				GS = new Array();
				refresh(S['days'],0);
			});
		});
		refresh(S['days'],0);
	//	jQuery(document).ready(function(e) { jQuery('#help_btn').trigger('click'); });
	}

	function getCurUrl() {
		var cUrl = "?proj=" + encodeURIComponent(getActiveProject()) + "&days=" + S['days'] + "&tab=" + S['tab'];

		var base = window.location.href
		if (base.indexOf("?")>-1){
			base = base.substr(0,base.indexOf("?"));
		}
		return base + cUrl.substring(0, cUrl.length);
	}

	function dump(obj) {
		prompt('Dump', JSON.stringify(obj));
	}

	function getUrlParameter(sParam,Url) {
		var sPageURL = window.location.search.substring(1);
		if ( Url ) { sPageURL = Url; }
		var sURLVariables = sPageURL.split('&');
		for (var i = 0; i < sURLVariables.length; i++) {
			var sParameterName = sURLVariables[i].split('=');
			if (sParameterName[0] == sParam) {
				return  decodeURIComponent(sParameterName[1]);
			}
		}
	}

	function loadProjects() {
		var jsonData = getData("/api/p/");
		var options = $("#project_div");
		var proj=getUrlParameter('proj');
		$.each(jsonData['projects'], function() {
			if ( this.name == proj ) {
				options.append($("<option selected/>").val(this.name).text(this.name));
			} else {
				options.append($("<option />").val(this.name).text(this.name));
			}
		});
	}

	function getData(url) {
		return $.ajax({
			url: url,
			dataType: "json",
			async: false
		}).responseJSON;
	}

    function order_by_col(idx) {
        return function (a, b) {
            return b[idx] == a[idx] ? 0
                : b[idx] > a[idx] ? 1
                    : -1;
        }
    }
	function order_abs(a, b) {
		return Math.abs(b[1]) == Math.abs(a[1]) ? 0
			: Math.abs(b[1]) > Math.abs(a[1]) ? 1
			: -1;
	}
	function order_2val(a, b) {
		return ( b[1] == a[1] && b[2] == a[2] )  ? 0
			: (b[1] >= b[2] && b[1] > a[1] && b[1] > a[2] ) ? 1
			: (b[2] > b[1] && b[2] > a[1] && b[2] > a[2] ) ? 1
			: -1;
	}
	function chop(body,limit) {
		return limit > 0 ? body.slice(0,limit)
			: limit < 0 ? body.slice(limit,body.length)
			: body;
	}

	function runSum(body) {
		if (body.length > 0) {
			for (i = 1; i < body.length; i++) {
				for (j = 1; j < body[i].length; j++) {
					body[i][j]+=body[i-1][j];
				}
			}
			return body;
		} else {
			return body;
		}
	}
	function empty_data (header) {
		var data =[['']];
		for (i = 1; i < header[0].length; i++) {
			data[0][i] = 0;
		}
		return data;
	}

	function metric2Data(jsonData,header,fields) {
		if (jsonData.length == 0) return empty_data(header);

		return $.map(jsonData, function(n,i){
				var row = new Array();
				row.push(n[0]);
				for ( var idx in fields ) {
					// round makes 0 from null values
					row.push(Math.round(n[fields[idx]]));
				}
			   return [row];
			});
	}

	function data2Table(header, data) {
		// var dataTable = google.visualization.arrayToDataTable(header.concat(data));
		var dataTable = new google.visualization.DataTable();
		dataTable.addColumn('string', header[0][0]);
		for (i = 1; i < header[0].length; i++) {
			dataTable.addColumn('number',header[0][i]);
		}
		dataTable.addRows(data);
		var formatter = new google.visualization.NumberFormat(
		    { negativeColor: 'red', negativeParens: true, pattern: '###,###.#'});
		for (i = 0; i < data[0].length; i++) {
			formatter.format(dataTable, i);
		}
		return dataTable;
	}

	function format1024(val) {
		var unit = '';
		if ( Math.abs(val) > 1073741824 ) {
			val = Math.round(val*10/1073741824)/10;
			unit = 'GB'
		}
		else if ( Math.abs(val) > 1048576 ) {
			val = Math.round(val*10/1048576)/10;
			unit = 'MB'
		}
		else if ( Math.abs(val) > 1024 ) {
			val = Math.round(val*10/1024)/10;
			unit = 'kB'
		}

		return val+unit;
	}

	function tHeadSum1(body,span) {
		$(span[0]).text(body[0].toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, '$1,'));
		$(span[1]).text(body[3].toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, '$1,'));
	}
	function tHeadSum2(body,span) {
		$(span[0]).text(body[2].toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, '$1,'));
	}
	function tHeadSum3(body,span) {
		$(span[0]).text(format1024(body[1]));
	}

	function tMerge(body,span) {
		$(span[0]).text(body[0][0] === "M" ? body[0][1] : body[1] ? body[1][1] : '0');
	}

	function newCommiters(body,span) {
		var authors="";
		var url = 'https://' + getProjectGurl(getActiveProject()) + '/commit/';
		body.sort(order_by_col(0));
		body.reverse();
		for (i = 0; i < body.length; i++) {
			if ( body[i][2] ) {
				authors = authors + '<a href="' + url + body[i][1]  + '">' + body[i][0] + '</a> (' + body[i][2] + ') ';
			}
		}
		$(span[0]).html( authors );
	}

	function zeroes(cnt,type,zero) {
		return Array.apply(null, Array(cnt)).map(type,zero);
	}
	function areaStacked(data) {
		var pos = {}, cnt = 0;
		for ( var i in data ) {
			if ( !pos[data[i][1]] ) {
				cnt++;
				pos[data[i][1]] = cnt;
			}
		}
		if ( cnt == 0 ) {
			return [[["",""]],[["",0]]];
		}
		cnt++;

		var header = zeroes(cnt, String.prototype.valueOf,"");
		Object.keys(pos).forEach(function (key) {
				header[pos[key]] = key;
		})

		var lastX, stackData=[], row = zeroes(cnt,Number.prototype.valueOf,0);
		for ( var i in data ) {
			if ( lastX !== undefined && lastX !== data[i][0] || i==data.length ) {
				row[0]=lastX;
				stackData.push(row);
				row = zeroes(cnt,Number.prototype.valueOf,0);
			}
			row[pos[data[i][1]]] = Math.round(100*data[i][2])/100; // cannot be null - problem in Google graph?
			lastX = data[i][0];
		}
		row[0]=lastX;
		stackData.push(row);
		return [[header],stackData];
	}

	function drawDashboard(Project,days,excl_to) {
			gv = google.visualization;
			var Iurl = 0, Igrid = 1, actTID = S['tab']-1;
			var tab = [
				[
					[
						"/r/summary_mon",
						[
							[ gv.LineChart, 'commits_m_div', { vAxis: {minValue: 0} }, [['Month','Commits']], [1], runSum ],
							[ gv.LineChart, 'commiters_y_div', {}, [['Date','Commiters']], [4] ]
						]
					],
					[
						"/r/summary",
						[ [ 'oneNumber', tHeadSum1, [ "span#one_commits","span#one_authors" ] ] ]
					],
					[
						"/r/summary_comtype",
						[ [ 'oneNumber', tMerge, [ "span#merge_commits" ] ] ]
					],
					[
						"/r/summary_a_comtype",
						[ [ 'oneNumber', tMerge, [ "span#merge_authors" ] ] ]
					],
					[
						"/r/new_commiters?days=30",
						[ [ 'oneNumber', newCommiters, [ "span#new_authors" ] ] ]
					]
				],
				[
					[
						"/r/summary_mon",
						[
							[ gv.LineChart, 'files_m_div', {vAxis: {minValue: 0}}, [['Date','Files']], [3], runSum, fileListener ]
						]
					],
					[
						"/r/summary",
						[ [ 'oneNumber', tHeadSum2, [ "span#one_files" ] ] ]
					],
				],
				[
					[
						"/r/summary",
						[ [ 'oneNumber', tHeadSum3, [ "span#one_size" ] ] ]
					],
					[
						"/r/summary_ext_size",
						[
							[ gv.PieChart, 'ext_size_div', {}, [['Suffix','Size']], [1],
								function (mydata) { var col1=order_by_col(1);mydata.sort(col1); return mydata } ]
						]
					],
					[
						"/r/summary_mon",
						[
							[ gv.LineChart, 'month_ft_size_div', {vAxis: {minValue: 0}}, [['Date','Size']], [2], runSum, fileListener ]
						]
					],
					[
						"/r/summary_ft_size",
						[
							[ gv.PieChart, 'ft_size_div', {}, [['File Type','Size']], [1],
								function (mydata) { var col1=order_by_col(1);mydata.sort(col1); return mydata } ]
						]
					],
					[
						"/r/summary_mon_ft_size",
						[
							[ gv.AreaChart, 'yr_ft_size_div', {isStacked : true},,,runSum ]
						]
					]
				],
				[
					[
						"/r/last_x_days?days=" + days,
						[
							[ gv.ColumnChart, 'commits_div', {isStacked: true}, [['Date','Regular','Merge']], [7,8] ],
							[ gv.ColumnChart, 'size_div', {}, [['Date','Size']], [1] ],
							[ gv.LineChart,   'files_div', {}, [['Date','Modified Files','Deleted Files','Added Files']], [6,5,4] ],
							[ gv.LineChart,   'lines_div', {}, [['Date','Lines Added','Lines Deleted']], [2,3] ]
						]
					]
				],
				[
					[
						"/r/last_x_days_c?days=" + days,
						[
							[ gv.ColumnChart, 'big_commit_div',
								{ title : 'Click on column for the detail.' },
								[['Date','Size']],
								[1],
								function (mydata) { mydata.sort(order_abs); return chop(mydata,12) },
								commitListener
							]
						],
					]
				],
				[
					[
						"/r/last_x_days_a?days=" + days,
						[
							[ gv.PieChart, 'rcommit_chart_div', {},
								[['Author','Commits']],
								[5],
								function (mydata) { var col1=order_by_col(1);mydata.sort(col1); return mydata }
							],
							[ gv.PieChart, 'mcommit_chart_div', {},
								[['Author','Commits']],
								[6],
								function (mydata) { var col1=order_by_col(1);mydata.sort(col1); return mydata }
							],
							[ gv.ColumnChart, 'commit_size_div', {},
								[['Author','Size']],
								[1],
								function (mydata) { mydata.sort(order_abs); return chop(mydata,8) }
							],
							[ gv.ColumnChart, 'author_lines', {},
								[['Author','Lines Added','Lines Deleted']],
								[2,3],
								function (mydata) { mydata.sort(order_2val); return chop(mydata,8) }
							]
						]
					]
				],
				[
					[
						"/r/last_x_days_a?days=" + days,
						[
							[ gv.BubbleChart, 'bubble_div',
								{
									hAxis: {title: 'Lines Added'},
									vAxis: {title: 'Lines Removed'},
									explorer: { maxZoomOut: 20 }
								},
								[['Author','Lines Added','Lines Deleted','Size','Commits']],
								[2,3,1,4]
							]
						]
					]
				]
			];

			if ( GS[actTID] == undefined ) {
				var GST = new Array(); // reuse graph
				for ( var i in tab[actTID] ) {
					var tabs = tab[actTID][i][Igrid];
					for ( var j in tabs ) {
						if ( tabs[j][0] === 'oneNumber' ) {
							GST.push(0);
						} else {
							GST.push(new tabs[j][0](document.getElementById(tabs[j][1])));
						}
					}
				}
				GS[actTID] = GST;
			}

			var g_idx=-1,data,transform,header,Data;
			for ( var i in tab[actTID] ) {
				for ( var j in tab[actTID][i][Igrid] ) {
					g_idx++;
					if ( excl_to && g_idx < excl_to ) {
						continue;
					}
					if ( j == 0 ) {
					    Data = getData("/api/p/" + Project + tab[actTID][i][Iurl]);
					}
					var report = tab[actTID][i][Igrid][j]
					if ( report[0] == 'oneNumber' ) {
						report[1](Data,report[2]);
						break;
					}
					transform = report[5];
					if ( report[4] ) {
						header = report[3];
						data = metric2Data(Data, header, report[4]);
					} else if ( transform && report[2] && "isStacked" in report[2] ) {
						var stacked = areaStacked(Data);
						header = stacked[0];
						data = stacked[1];
					} else {
						header = report[3];
						data = Data.length == 0 ? empty_data(header) : Data;
					}
					if ( transform ) { data=transform(data); }
					var Table = data2Table(header, data);
					var chart=GS[actTID][g_idx];
					if ( report[6] ) {
						report[6](chart,Table,g_idx);
					}
					chart.draw(Table, report[2]);
				}
			}
	}

	function githubSucc( response ) {
		var meta = response.meta;
		var data = response.data;
		var Msg = "No detail available.";
		if ( meta["X-RateLimit-Remaining"] == 0 ) {
			var d = new Date(0);
			d.setUTCSeconds(meta["X-RateLimit-Reset"]);
			Msg = 'No commit detail available from GitHub until ' + d.toString();
		} else {
			Msg = data['message'].replace(/\n\n/, "</h3><br /></br>");
			Msg = Msg.replace(/\n/g, "<br />");
			Msg = '<h4> '+ data['author']['name'] + ', <small>' + data['author']['date'] + '</small></h4><h3> ' + Msg;
			//</h3>
		}
		$( "div#com_detail" ).html( Msg );
	}

	function getInfo(proj,value) {
		var url=proj.split(' (');
		var gurl = "https://api.github.com/repos/" + url[1].substring(0, url[1].length - 1)
			+ '/' + url[0] + "/git/commits/" + value;
		$.ajax({
			url: gurl,
			jsonp: "callback",
			dataType: "jsonp",
			success: githubSucc,
			error: function( e ) {
				$( "div#com_detail" ).html( 'Error to get commit detail' );
			}
		});
	}

	function commitListener(c,t) {
		var chart=c;
		var Table=t;
		google.visualization.events.removeAllListeners(c);
		function selectHandler() {
			var selectedItem = chart.getSelection()[0];
			if (selectedItem) {
				var value = Table.getValue(selectedItem.row, 0);
				var proj = getActiveProject();
				var url = 'https://' + getProjectGurl(proj) + '/commit/' + value;
				getInfo(proj,value);
				//githubSucc(1);
				$( "span#com_sha1" ).text( value.substring(0, 12) );
				$( "span#com_url" ).html( '<a class="btn btn-success" href="' + url
					+ '"> View detail on GitHub</a>' );
				$( "span#comnew_url" ).html( '<a class="btn btn-success" href="' + url
					+ '" target="_blank"><span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>' );
				$('#com_dialog').modal("show");
			}
		}
		google.visualization.events.addListener(chart, 'select', selectHandler);
	}

	function getActiveProject () {
		var e = document.getElementById("project_div");
		return e.options[e.selectedIndex].value;
	}

	function getProjectGurl(Proj) {
		var url=Proj.split(' (');
		return 'github.com/' + url[1].substring(0, url[1].length - 1) + '/' + url[0];
	}

	function refresh(days,delta,excl_to) {
		if ( days+delta < 1 || days+delta > 366 )
			return days;
		var Project = getActiveProject();
		drawDashboard(Project,days,excl_to);
		days=days+delta;
		$( "span#last_days" ).text( days );
		$( "span#github_url" ).html( '<a class="btn btn-lg btn-success" href="https://' + getProjectGurl(Project)
			+ '"><span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>' );
		return days;
	}
</script>
<style>
.navbar {
	background-color:#000;
}

.vh45{ height: 45vh }
.vh50{ height: 50vh }
.vh78{ height: 78vh }
</style>
</head>

<body style="padding: 56px 0px 0px 0px;">
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
	<div class="navbar-header">
	  <select id="project_div" class="btn btn-success btn-lg dropdown-toggle"></select>
	  <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
		<span class="sr-only">Toggle navigation</span>
		<span class="icon-bar"></span>
		<span class="icon-bar"></span>
{'fraction_non_alphanumeric': 0.2179525488555549, 'fraction_numerical': 0.021245443136317457, 'mean_word_length': 2.987964728312679, 'pattern_counts': {'":': 0, '<': 169, '<?xml version=': 0, '>': 176, 'https://': 16, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3469128', 'n_tokens_mistral': 15576, 'n_tokens_neox': 12495, 'n_words': 2234}