<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Website change monitor</title>
<script>
"use strict";

var urls=[];

function pad(s,n,c){
	s=s+"";
	if(!c)c="0";
	var fill=c,filllen=n-s.length;
	if(filllen<=0)return s;
	while(fill.length<filllen)fill+=c;
	return fill+s;
}

function dateformat(date){
	return date.getFullYear()+"/"+pad(date.getMonth()+1,2)+"/"+pad(date.getDate(),2)+" "+
	       pad(date.getHours(),2)+":"+pad(date.getMinutes(),2)+":"+pad(date.getSeconds(),2);
}

function fetch(method,url,data/*?*/,cb){
	if(!cb){
		cb=data;
		data=undefined;
		if(!cb)throw new Error("No callback passed to fetch");
	}
	var xhr=new XMLHttpRequest();
	xhr.onreadystatechange=function(ev){
		if(xhr.readyState<4)return;
		cb(xhr.status,xhr.responseText);
	};
	xhr.open(method,url);
	xhr.send(data);
}

function focusData(data){
	document.getElementById("timelinecontainer").classList.add("visible");
	var header=document.getElementById("timelineheader");
	var tbody=document.getElementById("timelinetbody");
	if(!header.firstChild)header.appendChild(document.createTextNode(data.url));
	else header.firstChild.nodeValue=data.url;
	header.setAttribute("href",data.url);

	var l=tbody.children,i;
	for(i=l.length-1;i>=0;i--)tbody.removeChild(l[i]);

	var tr,td,e,s,date;
	for(i=data.timeline.length-1;i>=0;i--){
		tr=document.createElement("tr");
		if(i>0&&data.timeline[i][1]==data.timeline[i-1][1]){
			tr.classList.add("repeated");
		}

		td=document.createElement("td");
		date=new Date(data.timeline[i][0]);
		td.appendChild(document.createTextNode(dateformat(date)));
		tr.appendChild(td);

		td=document.createElement("td");
		s=data.timeline[i][1];
		if(s==null)s="(error while retrieving page)";
		td.appendChild(document.createTextNode(s));
		tr.appendChild(td);

		td=document.createElement("td");
		e=document.createElement("a");
		e.href="javascript:void(0)";
		e.appendChild(document.createTextNode("Delete till here"));
		e.addEventListener("click",(function(url,date){return function(ev){
			if(!confirm(
				"This will delete history up to and including the selected date "+
				"("+dateformat(date)+"), for the following url:\n"+data.url+
				"\nIs this OK?")){
				return;
			}

			fetch("DELETE","/changes/url/history",JSON.stringify({url:data.url,todate:date}),function(status,body){
				if(status!=200){
					alert("Error deleting history: ("+status+") "+body);
					return;
				}
				focusURL(data.url);
			});
		};})(data.url,date));
		td.appendChild(e);
		tr.appendChild(td);

		tbody.appendChild(tr);
	}
}

function focusURL(url){
	fetch("GET","/changes/url?url="+encodeURIComponent(url),function(status,body){
		if(status!=200){
			alert("Error getting data: ("+status+") "+body);
			return;
		}
		var data;
		try {
			data=JSON.parse(body);
		} catch(e){
			alert("Server sent invalid data!");
			console.log("Invalid data:",data);
			return;
		}
		focusData(data);
	});
}

function makeURLtr(url){
	var tr=document.createElement("tr");
	var td=document.createElement("td");

	td.appendChild(document.createTextNode(url));

	var e=document.createElement("input");
	e.type="button";
	e.classList.add("rowdeletebutton");
	e.value="X";
	e.setAttribute("title","Delete URL");
	e.addEventListener("click",function(ev){
		ev.stopPropagation();
		if(!confirm("Delete the following url?\n"+url))return;
		fetch("DELETE","/changes/url",url,function(status,body){
			if(status!=200){
				alert("Error deleting: ("+status+") "+body);
				return;
			}
			var tlh=document.getElementById("timelineheader");
			if(tlh.firstChild&&tlh.firstChild.nodeValue==url){
				document.getElementById("timelinecontainer").classList.remove("visible");
			}
			if(status!=200){
				alert("Error deleting url: ("+status+") "+body);
				return;
			}
			tr.parentNode.removeChild(tr);
			updateURLs();
		});
	});
	td.appendChild(e);

	tr.addEventListener("click",function(ev){
		focusURL(url);
	});

	tr.appendChild(td);
	return tr;
}

function updateURLs(){
	fetch("GET","/changes/urls",function(status,body){
		urls=JSON.parse(body);
		if(status!=200||!urls){
			urls=[];
			alert("Error retrieving URLs!");
			return;
		}
		var tbody=document.getElementById("urlstable").firstElementChild;
		var l=tbody.children,i,tr,td;
		for(i=l.length-1;i>=0;i--)tbody.removeChild(l[i]);
		for(i=0;i<urls.length;i++){
			tbody.appendChild(makeURLtr(urls[i]));
		}
	});
}

function addURLbutton(){
	var e=document.getElementById("urladdinput");
	var url=e.value;
	fetch("POST","/changes/url",url,function(status,body){
		if(status==200){
			e.value="";
		} else {
			alert("URL submission error: ("+status+") "+body);
		}
		updateURLs();
	});
}

function addURLkeypress(ev){
	if(ev.keyCode==10||ev.keyCode==13)addURLbutton();
}

function sendRefresh(){
	fetch("POST","/changes/refresh",function(status,body){});
}

window.addEventListener("load",function(){
	updateURLs();
	//setInterval(updateURLs,10*1000);
});
</script>
<style>
body{
	font-family:Georgia,Times,serif;
	font-size:14px;
}
h1{
	/*font-size:28px;*/
	/*margin:19px 0 19px 0;*/
}
#urlscontainer{
	width:500px;
}
#urlstablecontainer{
	height:200px;
	border:1px #888 solid;
	overflow-y:scroll;
}
#urlstable{
	width:100%;
	border-collapse:collapse;
}
#urlstable tbody, #urlstable tr, #urlstable td{
	width:100%;
}
#urlstable tr{
	cursor:pointer;
}
#urlstable tr:nth-child(2n){
	background-color:#eee;
}
#urlstable tr:hover{
	background-color:#ddd;
}
#urlstable td{
	height:100%;
	vertical-align:middle;
}
#urladdinput{
	width:300px;
}
.rowdeletebutton{
	color:red;
	font-family:Monospace;
	font-weight:bold;
	float:right;
	cursor:pointer;
	padding:0 3px 0 3px;
	background-color:rgba(255,0,0,0.1);
	border-radius:2px;
	border-width:0;
}

#timelinetbl{
	border-collapse:collapse;
}
#timelinetbl td, #timelinetbl th{
	border:1px #888 solid;
}
#timelinetbl td:nth-child(3){
	font-size:10px;
}

tr.repeated{
	color:#aaa;
	font-size:10px;
}

#timelinecontainer{
	display:none;
}
#timelinecontainer.visible{
	display:block;
}
</style>
</head>
<body>
<h1>Website change monitor</h1>
<b>URLs:</b>
<input type="button" onclick="sendRefresh();" value="Redownload sites">
<br>
<div>
	<div id="urlscontainer">
		<div id="urlstablecontainer">
			<table id="urlstable"><tbody></tbody></table>
		</div>
		<input type="text" id="urladdinput" onkeypress="addURLkeypress(event);">
		<input type="button" onclick="addURLbutton();" value="Add URL" title="Add URL to watch list">
	</div>
</div>
<div id="timelinecontainer">
	<h2><a id="timelineheader"></a></h2>
	<table id="timelinetbl">
		<thead><tr><th>Date</th><th>Hash</th></tr></thead>
		<tbody id="timelinetbody"></tbody>
	</table>
</div>
</body>
</html>