onload = InitTimeline;
var entriesdiv, timelinediv, entriesbelt;
var pageheight = 500;
var slidespeed = 40;
var slidestep = 200;
var slidetarget = 0;
var currentzoom = 1;
var itemheight = 20;
var minspace = 10;
var maxzoom = 15;
var zoommethod = 'log'; // 'geom' or 'log'
var zoomincrement = 0.5;
var zoombuttons = {};

// Preload images from the source CSS file, without knowing the image urls.
var loadclasses = ['#timeline_zoomout:hover','#timeline_zoomreset:hover','#timeline_zoomin:hover'];
if(document.styleSheets){
	var ss = document.styleSheets[2];
	var rs = ss.cssRules?ss.cssRules:ss.rules;
	for(var i=0;i<rs.length;i++){
		if(indexOf(loadclasses,rs[i].selectorText) >= 0 && rs[i].style.background){
			var reg = /url\(\'?([^.]+\.(png|jpg|gif))\'?\)/i;
			var res = reg.exec(rs[i].style.background);
			if(res){
				var img = new Image;
				img.src = foldername + '/' + res[1];
			}
		}
	}
}

function InitTimeline(){
	entriesdiv = document.getElementById('timelineentries');
	entriesbelt = document.getElementById('entrybelt');
	timelinediv = document.getElementById('timeline');
	
	// normalise the zoombutton object into an array
	var temp = zoombuttons;
	var ind = 0;
	zoombuttons = [];
	for(key in temp){
		zoombuttons[ind] = temp[key];
		zoombuttons[ind].index = ind;
		ind++;
	}
	
	// build the initial timeline
	BuildTimeline(1);
}

function BuildTimeline(zoom){
	if(zoom >= 1 && zoom <= maxzoom){
		var currentScrollHeight = timelinediv.scrollHeight;
		var currentScrollTop = timelinediv.scrollTop;
		currentzoom = zoom;
		if(timelinedates && timelinedates.length){
			var last = timelinedates[timelinedates.length-1];
			var zoomlevel = zoommethod=='log'?(Math.pow((1 + zoomincrement),(zoom-1))):(1 + (zoomincrement*(zoom-1)));
			var pixeldistance = pageheight * zoomlevel;
			var distanceratio = last.days?last.days/(pixeldistance-itemheight):1;
			
			// clean palatte
			timelinediv.innerHTML = '';
			
			// draw line
			var lineobj = document.createElement('div');
			lineobj.style.width = '4px';
			lineobj.style.height = pixeldistance + 'px';
			lineobj.style.left = '118px';
			lineobj.style.position = 'absolute';
			lineobj.style.zIndex = 2;
			lineobj.className = 'pointline';
			timelinediv.appendChild(lineobj);
			
			// show points
			var index = 0;
			var currentzoombutton = null;
			var inzoom = false;
			var lastpoint = null;
			for(key in timelinedates){
				var point = timelinedates[key];
				point.Init(distanceratio,index);
				index++;
			}
			
			// reset scroll position
			if(currentScrollHeight > 0){
				timelinediv.scrollTop = ((timelinediv.scrollTop / currentScrollHeight) * timelinediv.scrollHeight);
			}
			
			// draw Zoom buttons
			for(i=0;i<zoombuttons.length;i++){
				zoombuttons[i].draw();
			}
		}
	}
}

function TimelineItem(days,day,month,year,category){
	this.days = days;
	this.day = day;
	this.month = month;
	this.year = year;
	this.category = category;
	this.top = 0;
	
	// add to zoombutton, if needed
	if(category){
		if(!zoombuttons[category]){
			zoombuttons[category] = new ZoomButton();
		}
		zoombuttons[category].add(this);
	}
}

TimelineItem.prototype.Init = function(ratio,index){
	this.top = this.days / ratio;
	this.div = document.createElement('div');
	this.div.id = 'point_' + this.days;
	this.div.style.top = this.top + 'px';
	this.div.style.width = '130px';
	this.div.style.height = itemheight + 'px';
	this.div.style.left = index%2==0?'0px':'110px';
	this.div.style.position = 'absolute';
	this.div.style.zIndex = 3;
	this.div.className = 'point';
	eval('this.div.onmouseover = function(){highlightEntry('+this.days+');}');
	eval('this.div.onmouseout = function(){restoreEntry('+this.days+');}');
	eval('this.div.onclick = function(){slideToEntry('+this.days+');}');
	
	this.img = document.createElement('div');
	this.img.style.top = '0px';
	this.img.style.left = index%2==0?'110px':'0px';
	this.img.style.width = '20px';
	this.img.style.height = '20px';
	this.img.style.position = 'absolute';
	this.img.className = 'pointimage';
	this.div.appendChild(this.img);
	this.txt = document.createElement('div');
	if(this.month){
		var span = '<span class="pointdays">'
		if(this.day) span += this.day + ' ';
		span += this.month + '</span> ';
		this.txt.innerHTML = span;
	}
	this.txt.innerHTML += '<span class="pointyears">' + this.year + '</span>';
	this.txt.style.top = '0px';
	if(index%2==0){
		this.txt.style.left = '0px';
		this.txt.style.textAlign = 'right';
	}else{
		this.txt.style.left = '30px';
	}
	this.txt.style.width = '100px';
	this.txt.style.position = 'absolute';
	this.txt.className = 'pointdate';
	
	this.div.appendChild(this.txt);
	timelinediv.appendChild(this.div);
}

function ZoomButton(){
	this.index = 0;
	this.top = 0;
	this.height = itemheight - 2;
	this.zoomlevel = 1;
	this.items = [];
}

ZoomButton.prototype.add = function(item){
	this.items[this.items.length] = item;
}

ZoomButton.prototype.draw = function(){
	if(this.items.length){
		this.top = Math.floor(this.items[0].top);
		this.height = Math.floor(this.items[this.items.length-1].top - this.top + itemheight - 2);
		this.category = this.items[0].category;
		
		var shouldzoom = false;
		var lastpoint = null;
		var zoomlevel = zoommethod=='log'?(Math.pow((1 + zoomincrement),(currentzoom-1))):(1 + (zoomincrement*(currentzoom-1)));
		for(key in this.items){
			var point = this.items[key];	
			if(currentzoom < maxzoom){
				if(lastpoint != null){
					if(point.top - lastpoint.top < minspace){
						var level = 0;
						if(zoommethod == 'log'){
							level = Math.ceil(Math.log(minspace / (point.top - lastpoint.top)) / Math.log(1+zoomincrement));
						}else{
							// calculate the zoom using the distance at zoom level 1
							var x = (point.top - lastpoint.top) / zoomlevel;
							level = Math.ceil(((minspace - x) / (zoomincrement * x)) + 1) - currentzoom;
						}
						shouldzoom = true;
						this.zoomlevel = Math.min(Math.max(this.zoomlevel, level + currentzoom),maxzoom);
					}
				}
				lastpoint = point;
			}
		}
		
		shouldzoom?this.drawOver():this.drawUnder();
	}
}

ZoomButton.prototype.drawOver = function(){
	this.div = document.createElement('div');
	this.div.style.top = this.top + 'px';
	this.div.id = 'zoombutton_' + this.index;
	this.div.style.height = this.height + 'px';
	this.div.style.left = '0px';
	this.div.style.zIndex = 5;
	this.div.style.position = 'absolute';
	this.div.className = 'zoombuttonover zoombutton' + this.index;	
	eval('this.div.onclick = function(){zoomTo('+this.index+');}');
	
	// Add title
	this.title = document.createElement('div');
	this.title.className = 'zoombuttonovertitle';
	this.title.innerHTML = this.category;
	this.div.appendChild(this.title);
	timelinediv.appendChild(this.div);
}

ZoomButton.prototype.drawUnder = function(){
	this.div = document.createElement('div');
	this.div.style.top = this.top + 'px';
	this.div.id = 'zoombutton_' + this.index;
	this.div.style.height = this.height + 'px';
	this.div.style.left = '0px';
	this.div.style.zIndex = 1;
	this.div.style.position = 'absolute';
	this.div.className = 'zoombuttonunder zoombutton' + this.index;	
	timelinediv.appendChild(this.div);
}

function slideToEntry(days){
	var entries = entriesbelt.childNodes;
	var pos,height=0;
	var timepointpos = timelinedates[days].top - timelinediv.scrollTop;
	for(i=0;i<entries.length;i++){
		var entry = entries[i];
		if(entry.nodeType == 1 && entry.getAttribute('days') == days){
			if(pos == undefined){
				pos = entry.offsetTop - timepointpos;
			}
			height += entry.offsetHeight;
		}
	}
	pos +=  height / 2;
	// check top
	if(timepointpos - (height/2) < 0){
		pos -= (height/2) - timepointpos;
	}
	// check bottom
	if(timepointpos + (height/2) > pageheight){
		pos += (height/2) - (pageheight - timepointpos);
	}
	slideTo(pos);
}

function highlightEntry(days){
	var entries = entriesbelt.childNodes;
	for(i=0;i<entries.length;i++){
		var entry = entries[i];
		if(entry.nodeType == 1 && entry.getAttribute('days') == days){
			entry.className = entry.className.replace(/entry/,'entryOver');
		}
	}
	highlightPoint(days);
}

function restoreEntry(days){
	var entries = entriesbelt.childNodes;
	for(i=0;i<entries.length;i++){
		var entry = entries[i];
		if(entry.nodeType == 1 && entry.getAttribute('days') == days){
			entry.className = entry.className.replace(/entryOver/,'entry');
		}
	}
	restorePoint(days);
}

function highlightPoint(days,entry){
	var point = document.getElementById('point_' + days);
	if(point){
		point.className = point.className.replace(/point/,'pointOver');
	}
	if(entry){
		var entry = document.getElementById('entry_' + entry);
		if(entry){
			entry.className = entry.className.replace(/entry/,'entryOver');
		}
	}
}

function restorePoint(days,entry){
	var point = document.getElementById('point_' + days);
	if(point){
		point.className = point.className.replace(/pointOver/,'point');
	}
	if(entry){
		var entry = document.getElementById('entry_' + entry);
		if(entry){
			entry.className = entry.className.replace(/entryOver/,'entry');
		}
	}
}

function slideTo(pos){
	clearInterval(window.interval);
	slidetarget = pos;
	window.interval = setInterval('slide_stepincrement()',slidespeed);
}

function slide_stepincrement(){
	var current = entriesdiv.scrollTop;
	var step = (slidetarget - current) / 2;
	if(step > slidestep) step = slidestep;
	if(step < -slidestep) step = -slidestep;
	if(Math.abs(step) < 10){
		entriesdiv.scrollTop = slidetarget;
		clearInterval(window.interval);
	}else{
		entriesdiv.scrollTop = (current + step);
	}
}

function zoomTo(index){
	var button = zoombuttons[index];
	var zoom = button.zoomlevel;
	BuildTimeline(zoom);
	var firstpoint = button.items[0];
	var lastpoint = button.items[button.items.length-1];
	// remove the comments to zoom to the center of the block, instead of the top.
	var position = firstpoint.top;// + ((lastpoint.top-firstpoint.top) / 2);
	timelinediv.scrollTop = position;// - (pageheight / 2);
	slideToEntry(firstpoint.days);
}