// ==UserScript==
// @name			LesehilfeMapFrameBetaOO
// @namespace		tag:ifi.lmu.de,2008-23-02:Weblesehilfe
// @description		dritter Prototyp fr die Bedienelemente der Heatmapkomponente (objektorientierter Ansatz)
// @include 		*
// @exclude 		http://de.youtube.com/*
// ==/UserScript==

//resources
var buttonImages = new Array("data:image/gif,GIF89a%1A%00%11%00%A56%00%01%00%02%02%00%03%07%05%08%08%06%09%0A%08%0B%C9%C7%CA%CA%C8%CB%CC%CA%CD%CD%CB%CE%CE%CC%CF%CF%CD%D0%D0%CE%D1%D1%CF%D2%D2%D0%D3%D3%D1%D4%D4%D2%D5%D5%D3%D6%D6%D4%D7%D7%D5%D8%D8%D6%D9%D9%D7%DA%D9%D7%DC%DA%D8%DB%DA%D8%DD%DB%D9%DC%DC%DA%DD%DC%DA%DF%DB%DB%DD%DD%DB%DE%DD%DB%E0%DE%DC%DF%DE%DC%E1%DF%DD%E0%DF%DD%E2%E0%DE%E1%E0%DF%E3%E1%DF%E2%E2%E0%E3%E2%E1%E5%E3%E1%E4%E4%E2%E5%E5%E3%E6%E6%E4%E7%E7%E5%E8%E9%E7%EA%EC%EA%ED%ED%EB%EE%EE%EC%EF%EF%ED%F0%EE%EE%F0%F0%EE%F1%F0%F0%F2%F2%F0%F3%F3%F1%F4%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF!%FE%11Created%20with%20GIMP%00%2C%00%00%00%00%1A%00%11%00%00%06%FE%40%99%8C%E4%11e%40%1E%8E%05%E5YL%24%93%CAE%D3%F9%84%3E%D8%8F%90hD%AA%2C%A0%C8Dd%E0H%A9%D6%ACvX%3Cz%24%20%8C%87T%C8%3C%CE%D5kv%DBF%9E%90%18%08%16%12%19xi%7Bl%5Ds%0C'p%01%08fSyj%7C%8A%11-o%00%00%0Fw%92%87X%95n%24%0C%16%00%03%02%24%10%86z%A0%89n%1C%25%22%0E%9A%84%AB%94.'s%5CF%19%0A%11%04%1EQ%9E%ACZ%2F)%20%22%1Err%25%0C%0E*%0A%AA%C3j0%2F*%20%20%1C%18%D8%D8(%12%09%14%16%B6%7B%B8%1C%D8%1C%22%13%2C%13%07%1E%0E%14%18%E3X5%2F%25%DB%22%E6%1EG%2C%12%12%10%12%F2%3E%D0xq%E2H%1C%0E*%1A%90%08%D4%60%8204%C4V%B0%40!%02%9F%05%7D%24%2C%60Xp%22B%84%80%18N%A48A%22%03%06%12%24(%E4%A2P%C0%824%88jf%C4%D80%E2%83%09%9B%23r%EA%DC%C9Sg%01%10%00%3B",
							 "data:image/gif,GIF89a%1A%00%11%00%A56%00%01%00%02%02%00%03%07%05%08%08%06%09%0A%08%0B%C9%C7%CA%CA%C8%CB%CC%CA%CD%CD%CB%CE%CE%CC%CF%CF%CD%D0%D0%CE%D1%D1%CF%D2%D2%D0%D3%D3%D1%D4%D4%D2%D5%D5%D3%D6%D6%D4%D7%D7%D5%D8%D8%D6%D9%D9%D7%DA%D9%D7%DC%DA%D8%DB%DA%D8%DD%DB%D9%DC%DC%DA%DD%DC%DA%DF%DB%DB%DD%DD%DB%DE%DD%DB%E0%DE%DC%DF%DE%DC%E1%DF%DD%E0%DF%DD%E2%E0%DE%E1%E1%DF%E2%E1%DF%E4%E2%E0%E3%E3%E1%E4%E3%E2%E6%E4%E2%E5%E5%E3%E6%E6%E4%E7%E7%E5%E8%E9%E7%EA%EC%EA%ED%ED%EB%EE%EE%EC%EF%EF%ED%F0%EE%EE%F0%F0%EE%F1%F0%F0%F2%F2%F0%F3%F3%F1%F4%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%2C%00%00%00%00%1A%00%11%00%00%06%FE%40%99l%E4%11e%40%1E%8E%05%E5YL%24%93%CAE%D3%F9%84%AEW!%D1%88TY%40%91%89%C8%C0%91R%AD%D8%90%B6x%F4H%40%18%CF%A8%90y%98%ABi%F5%90%8D4!1%08%16%12%19whXk%5Cr%0C%26%01%20%12%08eSxi%88m%11-%1E%00%00%12%0Fv%92%86Y%7B%89%23%0C%16%02%03%00%23%10%85y%95H%1C%25%22%9A%0E%83%AC%94.%26r%5BF%19%04%11%0A%1EQ%9F%AD%2F)%20%22%1Eqq%25%0C%0E*%0A%AB%C3i0%2F*%20%20%1C%18%D7%D7(%12%09%14%16%B6%87%B8%1C%D7%1C%22%13%2C%13%07%1E%0E%14%18%E2W5%2F%25%DA%22%E5%1EG%2C%12%12%10%12%F1!h%BC0q%04%0E%07%15%0DF%00j0A%D8%99%3C%2BX%A0%10q%CFB%BE%11%160%2C0%11!%02%40%0C%26R%98%18%91%01%C3%88%11%14rQ(%60!%DA%C343bl%20%11%E2DM%128s%E2%3C%C9%B3%E7%02%88%20%00%3B");

/*
 * headingImages[0] = h1
 * headingImages[1] = h2
 * headingImages[2] = h3
 * headingImages[3] = h1(highlight, left)
 * headingImages[4] = h1(highlight, right)
 * headingImages[5] = h2/h3(highlight, left)
 * headingImages[6] = h2/h3(highlight, right)
 */
var headingImages = new Array("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%07%00%00%00%07%08%06%00%00%00%C4RW%D3%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%06%16%0D*%12%5C%81%E72%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00%60IDAT%08%D7%7D%8E%B1%0D%04!%0C%04%07%EA%20%B6%2C%D7F7P%9Be9%A6%0F%3E%F2%E9%2F%B9%C9f7%D8m%F7%5E%F6%DE7%22(T%959gkk%AD%3B%C6%C0%CC%10%112%13w%E7%9CC%8F%88%A7%00%10%11%CC%8C%88%A0W%F0Oy%07%C8%CCWY%DEU%15w%7F%82%DATU%DA%D7%DB%1F%D5i7%BE!%82%3A%81%00%00%00%00IEND%AEB%60%82",
							"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%05%06%0E%004%F4%18%3D%F1%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00%3DIDAT%08%D7m%CC%A1%01%001%08%C5%D0%D0E%18%10%C6a9%24%5B%E0~%CD%89%8A%8By.%26%89%CC%14_UeD%84%BA%5B%BB%AB%EEVD%E8%00%B8%3B%AF%07%60fx%B5%BF%E7%05G%E5%23%7C%80%F9%1F(%00%00%00%00IEND%AEB%60%82",
							"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%07%07%14%14%105x%1C%D6%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%006IDAT%08%D7m%8C%B1%0D%001%10%83%9C_%D7%B7%B0%BD%0C)%92*z%1A%24%0A%16%A0%97O%92%DAjfh%7B*%20%DB%00%5C%9F%98D%B6I%22%40%EB%EF%B9%01.%87)%24m%99I%FA%00%00%00%00IEND%AEB%60%82",
							"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%07%00%00%00%09%08%06%00%00%00%FEX6%A3%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%06%19%0E%05%03~%08Q%A2%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00lIDAT%18%D3u%8F%C1%0D%800%0C%03%1D%D4Y%FA%C8%1C%D9%A6Q%A7%C98%99%A3%0F%2FS%3E%14%A8%10~Y%3A%C7r%A4%B56q)%22%04%2F%1D%00%60f03%B8%FB%FC%40%00P%D5O%A0%2CSk%BD%2F%DC%7DF%84%94w%CD%0A%A8*%DC%7Dn%90%24%C6%18%C8LD%84%1C%7F%60%1B%B4%40%EF%5DH%3E%832s%FB%93%24H%E2%04%C3H6%C28%CE%5B%1B%00%00%00%00IEND%AEB%60%82",
							"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%07%00%00%00%09%08%06%00%00%00%FEX6%A3%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%06%19%0E%05%09%9E%DD%B8%BC%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00%5BIDAT%18%D3%7D%CF%C1%0D%C0%20%08%85%E1%9F%C6Y%3C8%07%E3%10%871%CC%E6%C1e%E8%A9F%9A%B4%1C%DF%07%C9C%22%82s%CCl%07%D7%1BT%15U%CD%F8%40k%8DtyB%ADuc1%B3%E8%BD%03%24%00%B8%DC%5D%C6%18%CC9Yk%25%2C%00%EE.g%CB%84_%0B%F2%F7%E7%0D%BEX*%80%3D%FB%B8%15%00%00%00%00IEND%AEB%60%82",
							"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%07%00%00%00%07%08%06%00%00%00%C4RW%D3%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%06%19%0D63%AE%FC%BAg%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00QIDAT%08%D7e%CDQ%0D%C00%08E%D1%D7fj%D0%81%00%84%10%D4TH%05%A0%03%3B%ECk%84%AE%7C%91%9CK%18%99%09UM%00Xk%0D%B4%99%DF%C2%CC%15%5D(%22W0%7BID0%B3%0A%9E%8E%11%01w%AF%DFu%B9%F7%3E%E0%C0%3F%00%C0%0B%E3%E3%22%8B%ED%E6%06%07%00%00%00%00IEND%AEB%60%82",
							"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%07%00%00%00%07%08%06%00%00%00%C4RW%D3%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%06%19%0D7%18%1B%5Brf%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00OIDAT%08%D7e%8E%C1%0D%C00%08%03M%94%D12%40%06A%0C%13y%1C%E6%C8%3A%F4%05j%A9_%B6%CE%02KD%E0-U%0D%00%20)%A3%83%B5V%E5%D1%C1%DE%BB%E0L%60f%E8%1Ay%FF%9C%83%7B%EF%07%CE4%24%25%C7%FC~f%C1%DD%2B%3FV%7D%1F%90ig%89%F7%00%00%00%00IEND%AEB%60%82");
	

//building the ui

var bigZ,ecButton,hmContainer,indicator,hIndex;
//GM_setValue("display", true);
window.addEventListener('load', addLesehilfe, true);

//global functions
/*
 * addLesehilfe adds the container and all the display Components of the Lesehilfe to the Page.
 */
function /*void*/ addLesehilfe(){
	if (checkRatio()){
		bigZ = maxZ();		
		hIndex = new HeadIndex();
		hIndex.init();
		hdDisplay = new HeadingDisplay();		
		hdDisplay.init();
		ecButton = new LHButton("lhECButton", buttonImages);
		ecButton.display();
		hmContainer = new HMContainer("lhHMContainer");
		hmContainer.heatmap.init();
		hmContainer.display();		
		if(!GM_getValue("display")) {
			ecButton.changeButtonImage();
			hmContainer.expcol();
			hdDisplay.removeList();
			ecButton.image.style.opacity = 0.5;
		}		
		window.addEventListener('scroll', pageScrolled, false);
		window.addEventListener('resize', pageResized, false);
		window.addEventListener('mousemove', mousemovewin, false);
		window.addEventListener('keypress', track, false);
		window.addEventListener('unload', exitpage, false);			
	}
}

/*
 * checkRatio()
 * @return	true, if the size of the Document is bigger than the available display space. else: false
 */
function /*boolean*/ checkRatio(){
	var hRatio = window.innerHeight/document.height;
	var wRatio = document.width/window.innerWidth;
	if(hRatio<0.5 && wRatio>0.2){
		return true;
	}else return false;
}

/*
 * maxZ() returns the documents highest zIndex
 * @return	maxZ	z-index :int
 */
function /*int*/maxZ(){
	var allElements, thisElement;
	var bigZ=0;	
	allElements = document.getElementsByTagName('*');
	for (var i = 0; i < allElements.length; i++) {
    	thisElement = allElements[i];
    	if(thisElement.style.zIndex!= "auto" && thisElement.style.zIndex>bigZ){
    		bigZ=thisElement.style.zIndex;
    	}
	}
	return bigZ;
}


//eventhandler functions

/*
 * buttonClicked() aggregates a list of functions that are executed when the expand/colapsebutton is clicked
 */
function buttonClicked(){
	ecButton.changeButtonImage();
	hmContainer.expcol();
	if (GM_getValue("display")){
		hmContainer.marker.image.style.display = "none";
	}else hmContainer.marker.image.style.display = "none";
	hdDisplay.removeList();
	GM_setValue("display", !GM_getValue("display"));
}

/*
 * mouseoverpanels() aggregates a list of functions that are executed when the mouse enters any Lesehilfecontainercomponent
 */
function mouseoverbutton(){
	ecButton.solidify();	
}

/*
 * mouseexitpanels() aggregates a list of functions that are executed when the mouse enters any Lesehilfecontainercomponent
 */
function mouseexitbutton(){
	ecButton.fade();	
}

/*
 * pageScrolled() aggregates a list of functions that are executed when the Page is scrolled
 */
function pageScrolled(){	
	hmContainer.marker.relocate();
	track();
}

/*
 * pageResized() aggregates a list of functions that are executed when the Browsers displaysize changes.
 */
function pageResized(){
	hmContainer.resize();
	hdDisplay.resize();	
}

/*
 * panelClicked() scrolls to to the corresponding part of the page wenn the heatmapcontainer is clicked.
 */
function panelClicked(event){
	hmContainer.marker.panelClicked();
	var panelY = event.pageY-window.pageYOffset-17;
	var targetY = panelY*(document.height/(window.innerHeight-17));
	var userFocusOffset = 0.2*window.innerHeight;
	window.scrollTo(0,(targetY-userFocusOffset));			
}

/*
 * mouseOverHeadmarker() is called when the mouse enters the Headguide. A complete list of Headings will be displayed.
 */
function mouseOverContainer(event){
	hdDisplay.displayList();
}

/*
 * mouseExitHeadmarker() removes the List of Headings
 */
 function mouseExitContainer(){
 	hdDisplay.removeList();
 	if(hmContainer.headings.lastFocus >= 0){
 		hmContainer.headings.updateStyle(hmContainer.headings.lastFocus);
 	}
 }
 
 /*
  * jumperClicked() scrolls back to the position the last jump-aktion originated from
  */
 function jumperClicked(event){
 	hmContainer.marker.image.style.display = "none";
	window.scrollTo(0,hmContainer.marker.lastPosition);	
	hmContainer.marker.lastPosition = -1;
} 

/*
 *  mouseOverJumper() displays a frame indicating the the position the last jump-aktion originated from
 */
function mouseOverJumper(event){
	hmContainer.marker.lastback.style.display ="";
	hdDisplay.displayList();
}

/*
 *  mousOverJumper() hides the frame indicating the the position the last jump-aktion originated from that was displayed by mouseOverJumper()
 */
function mouseExitJumper(event){
	hmContainer.marker.lastback.style.display ="none";
}  

/*
 * mouseTtackContainer() calls several functions highlighting a corresponding entry in the Heading-display-layer
 */
function mouseTrackContainer(calledY){
	var panelY = calledY-window.pageYOffset-17;
	var targetY = panelY*(document.height/(window.innerHeight-17));
	var target = hIndex.findHd(targetY);	
	hdDisplay.highLight(target);										
	hmContainer.headings.highLight(target);								
}

/*
 * track() collects the information used to compute the heatmap
 */
function track(){	
		var yT = window.pageYOffset;
		var yB = window.pageYOffset + window.innerHeight;
		hmContainer.heatmap.update(yT,yB)	
}

/*
 * exitpage() writes the logdata for the heatmap into a GM_cookie
 */
function exitpage(){
	var data = hmContainer.heatmap.stringData();	
	GM_setValue(location.href, data); 
}

/*
 * mousemovewin() is called on mousemove. It calls different funktions depending on the mousepointer position and state of the WebLH System 
 */
function mousemovewin(event){
	var capturedX = event.pageX;
	var capturedY = event.pageY;
	if(hdDisplay.displayed){
		if(capturedX<((window.innerWidth*0.8)-25)){
			mouseExitContainer();
			track();
		}else mouseTrackContainer(capturedY);
	}else{
		if(capturedX<(window.innerWidth-25)){
			track();			
		}
	}
}


/****************************************************************************************************************
 * The LHButton Class describes the Button which expands or collapses the Heatmapcontainer.						*
 * @param images 	an array of images formated as data urls :String[]											*
 * @param name 		id of the inserted element																	*
 ****************************************************************************************************************/
function LHButton(/*String*/ name, /*String[]*/ images){
	this.current = 1;
	this.images = images;
	this.image = document.createElement("img");
	this.image.id = name;
	this.image.src = images[this.current];
	this.image.style.position = "fixed";
	this.image.style.right = "0px";
	this.image.style.top = "0px";
	this.image.style.height = "17px";
	this.image.style.width = "26px";
	this.image.style.zIndex = bigZ+10;	
	this.image.addEventListener('mouseover', mouseoverbutton, false);
	this.image.addEventListener('mouseout', mouseexitbutton, false);
	this.image.addEventListener('click', buttonClicked, false);
	
	/**
	 * changeButtonImage() changes the buttons image. This funktion should be called wenn the button is clicked.
	 */
	this.changeButtonImage = /*void*/ function(){
		var nextImg = (ecButton.current+1)%this.images.length;
		this.current = nextImg;
		this.image.src = this.images[nextImg]; 	
	}
	
	/**
	 * display() adds the Button, that collapses or expands the Heatmap container, to the Page.
	 */	
	this.display = /*void*/ function(){
		var body;
		body = document.getElementsByTagName('body')[0];
		body.insertBefore(this.image, body.firstChild);
	}
	
	/**
	 * solidify() increases the components opacity to 0.8
	 */
	this.solidify = /*void*/ function(){
		this.image.style.opacity=1;
	}	
	
	/**
	 * fade() decreases the components opacity to 0.5
	 */
	this.fade = /*void*/ function(){	
		if(!hmContainer.expanded){	
			this.image.style.opacity=0.5;
		}		
	}		
}



/****************************************************************************************************************
 * The HMContainer Class describes the Heatmapcontainer which contains the Heatmap and the Heading index.		*
 * @param name id of the inserted element																		*
 ****************************************************************************************************************/
function HMContainer(/*String*/name){
	this.back = document.createElement("div");
	this.back.id = name;	
	this.back.style.position = "fixed";
	this.back.style.right = "0px";
	this.back.style.top = "17px"; 
	this.back.style.width = "25px";
	this.back.style.zIndex = bigZ+11;
	this.back.style.backgroundColor = "#dfdde2";
	this.back.style.borderLeftStyle = "solid";
	this.back.style.borderLeftWidth = "1px";
	this.back.style.borderLeftColor = "#f0eef1";
	this.heatmap = new Heatmap();	
	this.back.insertBefore(this.heatmap.back, this.back.firstChild);
	this.headings = new HeadGuide();
	this.back.insertBefore(this.headings.back, this.back.firstChild);
	this.expanded = true;		
	this.marker = new PosIndicator();
	this.back.insertBefore(this.marker.back, this.back.firstChild);
	this.back.insertBefore(this.marker.lastback, this.back.firstChild);
	this.back.insertBefore(this.marker.image, this.back.firstChild);
	this.back.addEventListener('mousedown', panelClicked, true);		
	this.back.addEventListener('mouseover', mouseOverContainer ,true);	
	
	/**
	 * resize() sets the height of the Heatmapcontainer element to a value that equals
	 * the innerHeight of the window minus the hight of the expand/collapse button.	  
	 */
	this.resize = /*void*/ function(){
		this.back.style.height = computeHeight();
		this.heatmap.resize();
		this.headings.resize();
		this.marker.resize();
		this.marker.relocate();
	}
	
	/**
	 * expcol() expands or collapses the component depending on its current "expandend" state.
	 */
	this.expcol = /*void*/ function(){
		var body;
		if(this.expanded){
			this.back.parentNode.removeChild(this.back);			
			this.expanded = false;
		}else{
			body = document.getElementsByTagName('body')[0];
			body.insertBefore(this.back, body.firstChild);		
			this.expanded = true;
		}
	}
	
	/**
	 * display() adds the Heatmap Container to the Page.
	 */	
	this.display = /*void*/ function(){
		var body;
		this.back.style.height = computeHeight();		
		body = document.getElementsByTagName('body')[0];
		body.insertBefore(this.back, body.firstChild);
		body.insertBefore(this.marker.image, body.firstChild);
		this.heatmap.resize();
		this.headings.resize();
		this.headings.fill();
		this.marker.relocate();
		this.marker.resize();
	}
	
	/*
 	* computeHeight computes the available height for the Heatmapcontainer. 
 	* @return window.innerHeight-buttonHeight(currently 17) and "px" appended, to be used as a css argument :String
 	*/
	function /*String*/ computeHeight(){
			var newheight = window.innerHeight-17;
			return newheight.toString()+"px";
	}
}


/****************************************************************************************************************
 * The PosIndicator Class describes a frame that indicates the readers position 								*
 * within the document on the heatmap.																			*
 ****************************************************************************************************************/
function PosIndicator(){
	var rahmenbreite = 2;	
	//jumpimg[0]=dot, jumpimg[1]=up, jumpimg[2]=down;
	this.jumpImgs = new Array("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%09%08%06%00%00%00%FA%AD%E6%9E%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%05%08%11(%01%08S%C4%06%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%004IDAT%08%D7%8D%8E%B1%09%00%20%10%C4r%E2%0E%BF%FFv%3AE%ECD%C4%C2%94%E1%02%17%95%9B%C6%83%0E0%93%3D%2FM%06%F8%95%BFeiNQ%9A%7C_Z%8D%9F%10%1FOa%B2%1E%00%00%00%00IEND%AEB%60%82",
							  "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%09%08%06%00%00%00%FA%AD%E6%9E%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%05%08%12%15(%22O%AA%8D%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00MIDAT%08%D7m%8C%C1%0D%C00%08%C4%5C%B6%CA%84a%1E%96%60%950%C5%F5Q%05%B5Q%F9%1Cga%90%84%24%D6%9C%DA%3B%0D%C7%F8%81%D0%D0%00%CA%5D%EFlu%81%F6%0B%2Bw%CE1%40d%3E-%93r%97%11%F1%3D%8B%E0Z%A0S%BF%01%7F1KY%CA%FF%3D%5B%00%00%00%00IEND%AEB%60%82",
							  "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%09%08%06%00%00%00%FA%AD%E6%9E%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%07tIME%07%D8%05%08%12%19%14%A1%95%99%06%00%00%00%19tEXtComment%00Created%20with%20GIMPW%81%0E%17%00%00%00DIDAT%08%D7m%8D%C1%0D%C00%0C%02%AF%DE%8A%25%BC%FF%08%DE%E2%F2%A8Z%25U%F9%80%90%0E%AE%01%F9%A8H%CE%26%A1%E8%3E%CBnP%99%C4%01'Q%A5%F8Q%BD%C8%EE%EA%3D%01%3E%B9%F6%D7%13%DFQ%60%01%96%00%20%C5b%AB%BC%98%00%00%00%00IEND%AEB%60%82");
	this.lastPosition = -1;
	
	
	//solid red frame	
	this.back = document.createElement("div");
	this.back.style.borderColor = "#E80000";
	this.back.style.borderStyle = "solid";
	this.back.style.borderWidth = "2px";
	this.back.style.borderRightStyle = "none";
	this.back.style.width = "26px";
	this.back.style.position ="fixed";
	this.back.style.right = "0px";
	this.back.style.zIndex= bigZ+11;
	this.back.style.overflow = "visible";
	this.back.style.backgroundColor = "transparent";
	
	//double red frame indicating last position
	this.lastback = document.createElement("div");
	this.lastback.style.borderColor = "#E80000";
	this.lastback.style.borderStyle = "double";
	this.lastback.style.borderRightStyle = "none";
	this.lastback.style.width = "26px";
	this.lastback.style.position ="fixed";
	this.lastback.style.right = "0px";
	this.lastback.style.zIndex= bigZ+11;
	this.lastback.style.overflow = "visible";
	this.lastback.style.display = "none";
	this.lastback.style.backgroundColor = "transparent";
	
	//arrow pointing to the last position
	this.image = document.createElement("img");
	this.image.src = this.jumpImgs[0];
	this.image.style.position = "fixed";
	this.image.style.right = "8px";
	this.image.style.overflow = "visible";	
	this.image.style.display = "none";	
	this.image.style.backgroundColor = "transparent";	
	this.image.style.zIndex= bigZ+14;
	this.image.addEventListener('mousedown', jumperClicked, false);	
	this.image.addEventListener('mouseout', mouseExitJumper, false);	
	this.image.addEventListener('mouseover', mouseOverJumper, false);	
	

	
	/*
 	* resize computes the proper height for the postionindicator on the heatmap and sets the height
 	* attribute of "back" to the computed value
 	*/
	this.resize = /*void*/ function(){		
		var cssHeight = Math.ceil(this.computeHeight()).toString()+"px";
		this.back.style.height = cssHeight;
		this.lastback.style.height = cssHeight;
		this.relocateJumper();
	}
	
	/*
 	* relocate() computes the y-coordinate of the postionindicator on the heatmap
 	* and assigns the value to the top attribute of "back" 
 	*/
	this.relocate = /* void*/ function(){		
		var cssOffset = this.computePosition(window.pageYOffset).toString()+"px";
		this.back.style.top = cssOffset;
		cssOffset = this.computePosition(this.lastPosition).toString()+"px";
		this.lastback.style.top = cssOffset;
		this.relocateJumper();
	}
	
	/*
 	* relocateJumper() computes the y-coordinate of the arrow pointing to the last Position 
 	*/
	this.relocateJumper = function(){
		var jumperOffset;		
		if(this.lastPosition>=0){
			if(window.pageYOffset>this.lastPosition && (this.computePosition(this.lastPosition)+this.computeHeight()+6)<this.computePosition(window.pageYOffset)){
				jumperOffset = this.computePosition(window.pageYOffset)-10;	
				if(GM_getValue("display"))
					this.image.style.display = "";		
				this.image.src = this.jumpImgs[1];
			}else if(window.pageYOffset<this.lastPosition && (this.computePosition(window.pageYOffset)+this.computeHeight()+4)<this.computePosition(this.lastPosition)){
				jumperOffset = this.computePosition(window.pageYOffset)+5+Math.floor(this.computeHeight());
				if(GM_getValue("display"))
					this.image.style.display = "";
				this.image.src = this.jumpImgs[2];				
			} else{
				this.image.style.display = "none";
				jumperOffset=0;
			}
			this.image.style.top = jumperOffset.toString()+"px";
		}
	}
	
	/*
	 * panelClicked() stores the last window possition when scrollaction was performed by clicking the heatmap
	 * or a Headmarker.
	 */
	this.panelClicked = /*void*/ function(){
		this.lastPosition = window.pageYOffset;		
		this.relocateJumper();
	}
			
	
	/*
	 * computeHeight() computes the height of the PosiMarker
	 */
	this.computeHeight = /*int*/ function(){
		var hmHeight = window.innerHeight-17;
		var newHeight = Math.ceil((Math.pow(hmHeight,2)/document.height));
		return newHeight;
	}
	
	/*
	 * computePosition() computes the position of the PosiMarker
	 */
	this.computePosition = /*int*/function(/*int*/ x){
		return Math.floor((((window.innerHeight-17)/document.height)*x))+17;
	}	
}


/****************************************************************************************************************
 * The HeadingGuide Class descibes the container, that contains the Headmarkers									*
 ****************************************************************************************************************/
function HeadGuide(){
	
	this.back = document.createElement("div");
	this.back.style.position = "fixed";
	this.back.style.right = "21px";	
	this.back.style.width = "1px";
	this.back.style.borderLeftStyle = "solid";
	this.back.style.borderLeftWidth = "1px";
	this.back.style.borderLeftColor = "#969396";
	this.back.style.borderRightStyle = "solid";
	this.back.style.borderRightWidth = "1px";
	this.back.style.borderRightColor = "#969396";
	this.back.style.backgroundColor = "#5f5d5f";
	this.factory = new HeadMarkerFactory();
	this.lastFocus = -1;
	
	
	/*
	 * resize() iss called when the page is resized. It resizes the Headguide and updates the markers postions
	 */
	this.resize = function(){
		this.back.style.height = this.back.parentNode.style.height;
		for(var i=0; i<this.back.childNodes.length; i++){
			this.back.childNodes[this.invertIndex(i)].style.top = this.markerYOffset(hIndex.recordStore[i]);
		}
	}
	
	/*
	 * invertIndex() utility funktion needed to address markers properly. The necessity to invert indices is due to proper visual stacking  
	 */
	this.invertIndex = function(y){
		var base =hIndex.recordStore.length-1;
		return base-y;
	}
	
	/*
	 * fill() adds Headmarkers to the Headguide
	 */
	this.fill = function(){		
		for(var i = hIndex.recordStore.length-1; i >= 0; i--){
			var record = hIndex.recordStore[i];			
			this.back.appendChild(this.factory.createMarker(record[0],this.markerYOffset(record),record[1]));
		}		
	}
	
	/*
 	* markerYOffset() computes the y-coordinate of the Headindicator on the headguide
 	* and assigns the value to the top attribute of the marker 
 	*/
	this.markerYOffset = /*String*/ function(/*record*/ r){
		var y = r[1];
		if (r[0]==1){
			var markerOffset = Math.floor((((window.innerHeight-17)/document.height)*y))+16;
		}	else {
			 var markerOffset = Math.floor((((window.innerHeight-17)/document.height)*y))+17;
		}
		var cssOffset = markerOffset.toString()+"px";
		return cssOffset;
	}	
	
	/*
	 * hightlight() highlights a marker in the Headguide
	 */
	this.highLight = /*void*/ function (/*int*/ stelle){
		if(stelle>=0){
			if(this.lastFocus>=0 && this.lastFocus!=stelle){
				this.updateStyle(this.lastFocus);
			}
			this.lastFocus = stelle;
			this.back.childNodes[this.invertIndex(stelle)].style.zIndex = bigZ+16;
			if (hIndex.recordStore[stelle][0]==1){
					this.back.childNodes[this.invertIndex(stelle)].src = headingImages[4];
					this.back.childNodes[this.invertIndex(stelle)].style.top = Math.floor((((window.innerHeight-17)/document.height)*hIndex.recordStore[stelle][1]))+15+"px";
					this.back.childNodes[this.invertIndex(stelle)].style.right="20px";
				}else{
					this.back.childNodes[this.invertIndex(stelle)].style.right="19px";
					this.back.childNodes[this.invertIndex(stelle)].style.top = Math.floor((((window.innerHeight-17)/document.height)*hIndex.recordStore[stelle][1]))+16+"px";
					this.back.childNodes[this.invertIndex(stelle)].src = headingImages[6];					
			}
		}else if(stelle==-1 && this.lastFocus>=0){
			this.updateStyle(this.lastFocus);
		}
	}
	
	/*
	 * updateStyle() assigns the proper styles to the y-th marker after it was highlighted
	 */
	this.updateStyle = /*void*/ function(/*int*/y){
		var elementFocus = this.back.childNodes[this.invertIndex(y)];
		if (hIndex.recordStore[y][0]==1){
				elementFocus.style.top = Math.floor((((window.innerHeight-17)/document.height)*hIndex.recordStore[y][1]))+16+"px";
				elementFocus.src = headingImages[0];
				elementFocus.style.right="19px";
				elementFocus.style.zIndex = bigZ+15;
			}else if (hIndex.recordStore[y][0]==2){
				elementFocus.style.right="20px";
				elementFocus.style.top = Math.floor((((window.innerHeight-17)/document.height)*hIndex.recordStore[y][1]))+17+"px";
				elementFocus.src = headingImages[1];	
				elementFocus.style.zIndex = bigZ+13;				
		}else if (hIndex.recordStore[y][0]==3){
			elementFocus.style.right="20px";
				elementFocus.style.top = Math.floor((((window.innerHeight-17)/document.height)*hIndex.recordStore[y][1]))+17+"px";
				elementFocus.src = headingImages[2];	
				elementFocus.style.zIndex = bigZ+12;
		}
	}	
}


/****************************************************************************************************************************
 * The HeadIndex Class provides a Datastructure, that allows all the "Weblesehilfe"'s Heading related components to work	*
 ****************************************************************************************************************************/
function HeadIndex(){
	
	//datastructure used to build HeadGuide and HeadDisplay	
	this.recordStore = new Array();
	
	/*
	 * addRecord() adds the description of a heading to the recordStore
	 * @param order	h1->1 h2->2 etc.
	 * @param yOff 	the Y-offset of the heading in the document
	 * @param text	the headings text
	 */
	this.addRecord = function(order, yOff, text){
		this.recordStore.push(new Array(order,yOff,text));
	}
	
	/*
	 * init searches the Document for Heading and adds their signatures to the recordstore (using addRecord())
	 */
	this.init = function(){
		var headings1, headings2;
		headings1 = document.getElementsByTagName("h1");
		headings2 = document.getElementsByTagName("h2");
		headings3 = document.getElementsByTagName("h3");
		
		for (var i = 0; i < headings1.length; i++) {
			if(this.elementYOffset(headings1[i])!=null)
				this.addRecord(1, this.elementYOffset(headings1[i]), this.destillHeading(headings1[i]));
		}		
		for (var i = 0; i < headings2.length; i++) {
			if(this.elementYOffset(headings2[i])!=null)				
	    		this.addRecord(2, this.elementYOffset(headings2[i]), this.destillHeading(headings2[i]));			
		}
		if (this.recordStore.length<20){
			for (var i = 0; i < headings3.length; i++) { 
				if(this.elementYOffset(headings3[i])!=null)
					this.addRecord(3, this.elementYOffset(headings3[i]), this.destillHeading(headings3[i]));
			}			
		}
		this.sort();		
	}
	
	/*
	 * removes entrys that contain invisible Headings
	 */
	this.prune = function(){
		for (var i=0; i<this.recordStore.length; i++){
			if(this.recordStore[i][1]==null){
				
			} 
		}
	}
	
	/*
	 * sort() sorts the recordStore by the yoffset attribute of the records
	 */
	this.sort = function() {	
		if(this.recordStore.length>0){	
		this.quicksort(0,this.recordStore.length-1);					
		}
	}
	
	/*
	 * destillHeading() extracts all textual information from a heading and puts in a div tag
	 */
	this.destillHeading = /*String*/ function(/*heading*/ hnode) {
		var sourcenode = hnode.cloneNode(true);
		var imgList = sourcenode.getElementsByTagName("img");		
		for (var k=0; k<imgList.length; k++){												
			var substitute = document.createTextNode(imgList[k].alt);
			imgList[k].parentNode.replaceChild(substitute, imgList[k]);									
		}				
		return sourcenode.textContent;							
	}
	
	
	/*
	 * elementYOffset gets the y offset of an element e
	 * @param 	e	an element within the web document  
	 */
	this.elementYOffset = function (e){
		var curtop = 0;
		if (e.offsetParent) {
			do {
				curtop += e.offsetTop;
			} while (e = e.offsetParent);
			return curtop;
		}
	}
	
	/*
	 * quicksort() is a simple qicksort implementation, that is caled by sort()
	 */
	this.quicksort = function (l,r){
		var i,j,m,x;
		i = l;
		j = r;
		m = this.recordStore[Math.round((l+r)/2)];
		do{
			while (this.recordStore[i][1]<m[1]){i++};
			while (m[1]<this.recordStore[j][1]) {j--};
			if (i<=j){
				x = this.recordStore[i];
				this.recordStore[i] = this.recordStore[j];
				this.recordStore[j] = x;
				i++;
				j--;
			}
		} while (i<=j);
		if (l<j) {this.quicksort(l,j)};
		if (i<r) {this.quicksort(i,r)};
	}
	
	/*
	 * findHd() returns the Heading corresponding to  y (y position of the mouse pointer)
	 */
	this.findHd = /*int*/ function(/*int*/ y){
		for(var i=0; i<this.recordStore.length; i++){
			if(y<this.recordStore[i][1]){
				return i-1;
			}			
		}
		return this.recordStore.length-1;
	}			
}


/**************************************************************************************************************
 * The HeadMarkerFactory Class produces the markers that indicate 												*		
 * the positions of the document's headings on the Heatmap.														*
 ****************************************************************************************************************/
function HeadMarkerFactory(){
	
	
	/*
	 * createMarker creates a marker to add to the Headguides back alement
	 * @param 	order	h1->1. h2->2 etc :int
	 * @param 	yOffset the markers yOffset :String 
	 */
	this.createMarker = /*img*/ function(/*int*/ order, /*String*/yOffset, /*int*/y){
		var image = document.createElement("img");
		image.style.position = "fixed";
		image.style.backgroundColor = "transparent";
		image.style.top = yOffset;	
		if(order == 1){
			image.src = headingImages[0];
			image.style.right = "19px";
			image.style.zIndex = bigZ+15;
		}else if(order == 2){
			image.src = headingImages[1];
			image.style.right = "20px";
			image.style.zIndex = bigZ+13;
		} else if(order == 3){
			image.src = headingImages[2];
			image.style.right = "20px";
			image.style.zIndex = bigZ+12;
		}			
		image.addEventListener('mousedown', function(){window.scrollTo(0,y)},false);
		return image;				
	}		
}

/**************************************************************************************************************
 * The HeadingDisplay describes the Layerthat contains the list of Headings displayed on mouseover 			  *
 **************************************************************************************************************/
function HeadingDisplay(){
	
	//layer containing heading entries 
	this.back = document.createElement("div");
	this.back.style.position = "fixed";
	this.back.style.zIndex = bigZ+10;
	this.back.style.right = "26px";
	this.back.style.top = "0px";
	this.back.style.backgroundColor = "#e5e5e5";
	this.back.style.overflow = "visible";
	this.back.style.borderLeft = "1px solid #000000";
	this.back.style.display = "none";
	this.displayed = false;
	this.back.addEventListener('mousedown', panelClicked, true);
	
	
	
	//last highlighted Position
	this.lastFocus = -1;	
	
	/*
	 * init() fills the layer (back) with entries
	 */
	this.init = /*void*/ function(){				
		for (var i = hIndex.recordStore.length-1; i >= 0; i--){
			var record = hIndex.recordStore[i];
			this.back.appendChild(this.createEntry(record[2], record[1], record[0]), i);						
		}		
		var body = document.getElementsByTagName('body')[0];
			body.insertBefore(this.back, body.firstChild);
	}
	
	
	
	/*
	 * displayList() displays a complete list of the documents Headings
	 */
	this.displayList = function(){	
		this.resize();	
		this.back.style.display = "";
		this.displayed =true;
	}
	
	
	
	/*
	 * removeList() removes the complete list of the documents Headings
	 */
	this.removeList = function(){
		if(hIndex.recordStore.length>0){
			this.back.style.display = "none";
			this.displayed = false;
		}
	}
	/*
	 * invertIndex() utility funktion needed to address markers properly. The necessity to invert indices is due to proper visual stacking  
	 */
	this.invertIndex = function(y){
		var base =hIndex.recordStore.length-1;
		return base-y;
	}
	
	/*
	 * resize() relocates the Headings on the display layer.
	 */
	this.resize = /*void*/ function(){
		if(hIndex.recordStore.length>0){
			var heightcss = window.innerHeight;
			heightcss = heightcss.toString()+"px";
			var widthcss = 0.2*window.innerWidth;
			widthcss = widthcss.toString()+"px";
			this.back.style.height = heightcss;
			this.back.style.width = widthcss;
			for(var i = 0; i<hIndex.recordStore.length; i++){				
				var tag = this.back.childNodes[this.invertIndex(i)];				
				tag.style.top = Math.floor((((window.innerHeight-17)/document.height)*hIndex.recordStore[i][1]))+11+ "px";
				if(hIndex.recordStore[i][0]==1){
					tag.childNodes[0].style.width = (0.2*window.innerWidth) - 19 + "px";
					tag.childNodes[1].style.width = "8px";
				}else if(hIndex.recordStore[i][0]==2){					
					tag.childNodes[0].style.maxWidth = (0.2*window.innerWidth) - 27 + "px";
					tag.childNodes[1].style.width = (0.2*window.innerWidth) - 32 + "px";					
				}else if(hIndex.recordStore[i][0]==3){					
					tag.childNodes[0].style.maxWidth = (0.2*window.innerWidth) - 39 + "px";
					tag.childNodes[1].style.width = (0.2*window.innerWidth) - 44 + "px";					
				}				
			}
		}	
	}
	
	/*
	 * createLink() creates a line between aheading and its corresponding headmarker
	 */
	this.createLink = function(/*int*/ yoffset, /*int*/ order){
		var link = document.createElement("div");
		link.style.height = "1px";
		link.style.position = "absolute";
		link.style.top = "8px";
		link.style.right = "0px";
		link.style.zIndex= bigZ+10;
		if(order == 1){			
			link.style.backgroundColor = "#969396";
		}else{			
			link.style.backgroundColor = "#c8c4c8";
		}
		return link;
	}
	
	/*
	 * createTextEntry() creates a the div  that contains a headings text
	 */
	this.createTextEntry = /*div*/ function(/*String*/ inhalt, yoffset, order, stelle){
		var kopfzeile = document.createElement("div");		
		kopfzeile.style.position = "absolute";
		kopfzeile.style.overflow = "hidden";	
		kopfzeile.style.height = "16px";	
		kopfzeile.style.paddingRight = "6px";	
		kopfzeile.style.backgroundColor = "transparent";	
				
		var textcont = document.createElement("div");
		textcont.style.font = "14px sans-serif";
		textcont.style.width = "auto";
		textcont.style.position	= "relative";			
		textcont.style.backgroundColor = "#e5e5e5";			
		textcont.style.textAlign = "left";
		textcont.style.whiteSpace = "nowrap";
		textcont.style.color = "#000000";
		textcont.style.height ="14px";		
		textcont.style.overflow = "visible";
		textcont.appendChild(document.createTextNode(inhalt));
		textcont.addEventListener('mouseover', function(){	document.body.style.cursor = "pointer";
														} ,true);
		textcont.addEventListener('mouseout', function(){	document.body.style.cursor = "";
														} ,true);
		textcont.addEventListener('mousedown', function(){window.scrollTo(0,yoffset);} ,true);
				
		var image = document.createElement("img");
		image.style.position = "absolute";
		image.style.top = "6px";
		image.style.left = "2px";
		image.style.backgroundColor = "transparent";
								
		kopfzeile.appendChild(image);		
		kopfzeile.appendChild(textcont);	
		if (order == 1){	
			image.style.top = "5px";
			kopfzeile.style.backgroundColor = "#e5e5e5";
			textcont.style.left = "11px";
			textcont.style.paddingRight = "11px"
			kopfzeile.style.left = "4px";
			kopfzeile.style.border = "1px solid #969396";				
			kopfzeile.style.zIndex = bigZ+15;
			image.src = headingImages[0]; 					
		}else if (order == 2){
			kopfzeile.style.zIndex = bigZ+13;
			kopfzeile.style.marginLeft = "12px";
			textcont.style.left = "9px";
			textcont.style.paddingRight = "9px";
			kopfzeile.style.width = "auto";				
			image.src = headingImages[1];			
		}else if (order == 3){
			kopfzeile.style.zIndex = bigZ+11;
			kopfzeile.style.marginLeft = "24px";
			textcont.style.left = "9px";
			textcont.style.paddingRight = "9px";
			kopfzeile.style.width = "auto";				
			image.src = headingImages[2];
		}
		return kopfzeile;
	}
	
	/*
	 * createEntry() wraps a line and a textentry into one div
	 */
	this.createEntry = /*div*/ function(/*String*/ inhalt, yoffset, order, stelle){
		var packer = document.createElement("div");
		packer.style.position = "absolute";
		packer.style.height = "16px";	
		packer.style.width = "100%";
		packer.appendChild(this.createTextEntry(inhalt, yoffset, order, stelle));
		packer.appendChild(this.createLink(yoffset, order));
		return packer;
	} 
	
	/*
	 * highlight() highlights the "stelle"th heading 
	 */
	this.highLight = /*void*/ function(/*int*/ stelle){
		if(stelle>=0){
			if(this.lastFocus>=0 && this.lastFocus!=stelle){
				this.updateStyle(this.lastFocus);
			}	
			var headFocus = this.back.childNodes[this.invertIndex(stelle)];			
			this.lastFocus = stelle; 		
			headFocus.childNodes[0].style.backgroundColor = "#c8c4c8";
			headFocus.childNodes[0].style.border = "1px solid #969396";
			headFocus.childNodes[0].style.zIndex = bigZ+17;
			headFocus.childNodes[1].style.zIndex = bigZ+16;
			headFocus.childNodes[0].childNodes[1].style.backgroundColor = "#c8c4c8";					
			if (hIndex.recordStore[stelle][0]==1){
					this.back.childNodes[this.invertIndex(stelle)].childNodes[0].childNodes[0].style.top = "4px";
					this.back.childNodes[this.invertIndex(stelle)].childNodes[0].childNodes[0].src = headingImages[3];
					headFocus.childNodes[0].childNodes[1].style.left = "13px";
				}else{
					this.back.childNodes[this.invertIndex(stelle)].childNodes[0].childNodes[0].style.top = "4px";
					headFocus.childNodes[0].childNodes[1].style.left = "11px";
					this.back.childNodes[this.invertIndex(stelle)].childNodes[0].childNodes[0].src = headingImages[5];					
			}						
		}else if(stelle==-1 && this.lastFocus>=0){
			this.updateStyle(this.lastFocus);
		}
	}
	
	/*
	 * updateStyle() reverts the "y"th entry to its initial layout
	 */
	this.updateStyle = /*void*/ function(/*int*/y){
		var elementFocus = this.back.childNodes[this.invertIndex(y)];		
		elementFocus.childNodes[0].childNodes[1].style.backgroundColor = "#e5e5e5";		
		elementFocus.childNodes[1].style.zIndex = bigZ+10;
		if (hIndex.recordStore[y][0]==1){
				elementFocus.childNodes[0].childNodes[0].style.top = "5px";
				elementFocus.childNodes[0].childNodes[0].src = headingImages[0];
				elementFocus.childNodes[0].style.backgroundColor = "#e5e5e5";
				elementFocus.childNodes[0].style.border = "1px solid #969396";
				elementFocus.childNodes[0].style.zIndex = bigZ+15;
			}else if (hIndex.recordStore[y][0]==2){
				elementFocus.childNodes[0].childNodes[0].style.top = "6px";
				elementFocus.childNodes[0].style.backgroundColor = "";
				elementFocus.childNodes[0].childNodes[0].src = headingImages[1];
				elementFocus.childNodes[0].style.border = "none";
				elementFocus.childNodes[0].style.zIndex = bigZ+13;			
		}else if (hIndex.recordStore[y][0]==3){
			elementFocus.childNodes[0].childNodes[0].style.top = "6px";
				elementFocus.childNodes[0].style.backgroundColor = "";
				elementFocus.childNodes[0].childNodes[0].src = headingImages[2];
				elementFocus.childNodes[0].style.border = "none";
				elementFocus.childNodes[0].style.zIndex = bigZ+11;
		}
	}
}

/****************************************************************************************************************
 * The Heatmap Class descibes the Heatmap, that visualizes the collected user information						*
 ****************************************************************************************************************/
function Heatmap(){
	this.back = document.createElement("div");
	this.back.style.overflow = "hidden";
	this.back.style.position = "fixed";
	this.back.style.right = "2px";	
	this.back.style.width = "17px";
	this.back.style.backgroundColor = "#000000";
	this.data = new Array(3000);
	this.factory = new HeatmapSliceFactory();
	this.lastStamp;
	
	/*
	 * resize() resizes the heatmap
	 */
	this.resize = function(){
		this.back.style.height = this.back.parentNode.style.height;

		while(this.back.hasChildNodes()){
			this.back.removeChild(this.back.lastChild);
		}
		this.fill();
		this.repaint(0,2999);
	}
	
	/*
	 * fill() fills the back element with heatmap slices
	 */
	this.fill = function(){
		var slicesTotal = Math.ceil((window.innerHeight-17)/3);
		for(var i=0; i<slicesTotal; i++){
			this.back.appendChild(this.factory.createSlice(i));
		}
	}
	
	/*
	 * update(topY,bottomY) updates the information used to compute the heatmap
	 * @param topY = screentop
	 * @param bottomY = end of screen
	 */
	this.update = function(topY, bottomY){
		var time = new Date().getTime();
		var elapsedTime = this.elapsed(time);
		this.lastStamp = time; 
		var from = Math.floor((topY/document.height)*3000);
		var upto = Math.floor((bottomY/document.height)*3000);
		var limiter = false;
		var max = 0;
		for(var i=from; i<upto;i++){
			this.data[i] = this.data[i]+(elapsedTime);
			if (this.data[i] > 256){
				 limiter = true;
				 max = this.data[i];				
			} 
		}
		if(limiter) {
			this.normalize(max);
			limiter = false;
		}
		this.repaint(0, 2999);
	}
	
	/*
	 * repaint() repaints heatmap sections
	 * @param topCell first datacell that contains displayable data
	 * @param bottomCell last datacell that contains displayable data
	 */
	this.repaint = function(topCell, bottomCell){
		var displayNum = this.back.childNodes.length*3;
		var cellPerDisp = 3000/displayNum;
		for(var i = 0; i<displayNum ; i++){
			var color=0.0;
			for(var j = 0; j<=Math.ceil(cellPerDisp); j++ ){
				color += this.data[Math.ceil(i*cellPerDisp+j)];
			}
			color = Math.ceil(color/cellPerDisp);
			this.colorCell(i,color);	
		}		
	}
	
	/*
	 * colorCell() assigns color values to the top/bottom-margins or background of the heatmap slices
	 */
	this.colorCell = function(/*int*/position, /*int*/ color){
		var cellnumber = Math.floor(position/3);
		var type = position%3;
		if(type==0){
			this.back.childNodes[cellnumber].style.borderTopColor = "rgb("+color+","+color+","+color+")";
		}else if(type==1){
			this.back.childNodes[cellnumber].style.backgroundColor = "rgb("+color+","+color+","+color+")";
		}else if(type==2){
			this.back.childNodes[cellnumber].style.borderBottomColor = "rgb("+color+","+color+","+color+")";
		}
	}
	
	/*
	 * normalize() is used to set the cap value for heatmap data to 256
	 */
	this.normalize = function(y){
		for(var i=0; i<this.data.length; i++){
			this.data[i] = (this.data[i]*256)/y;
		}
	}
	
	/*
	 * elapsed() computes the time between the last timestamp and now
	 * @param now a timestamp created by a Date object
	 */
	this.elapsed = function(now){
		var inter = now-this.lastStamp;
		inter = inter/1000;		
		if(inter>30){
			return 30;
		}else return inter;
	}
	
	/*
	 * clear() resets the collected data to the initial state (all values 0.0)
	 */
	this.clear = function(){
		alert("hiho");
		for(var i= 0; i < this.data.length; i++){
				this.data[i] = 0.0;
			}
	}
	
	/*
	 * init() loads a saved data collection if avaplable or creates a dataset at initial state (all values 0.0)
	 */
	this.init = function(){
		if(GM_getValue(location.href)){			
			this.oldDat = GM_getValue(location.href).split("#");	
			for(var i= 0; i < this.data.length; i++){
				this.data[i] = parseFloat(this.oldDat[i]);
			}
		}else{
			for(var i= 0; i < this.data.length; i++){
				this.data[i] = 0.0;
			}
		}
		this.lastStamp=new Date().getTime();
	}
	
	/*
	 * stringData() creates a the string contains the selected data 
	 */
	this.stringData = function(){
		var writeString = "";
		var first = true;
		for (var i=0; i<this.data.length; i++){
			if(!first)
				writeString += "#";
			writeString += this.data[i];
  			first = false 
		}
		return writeString; 
	}	
}


/****************************************************************************************************************
 * The HeatmapSlice Class descibes a line in the Heatmap, that visualizes the collected user information		*
 ****************************************************************************************************************/
function HeatmapSliceFactory(){	
	
	this.createSlice = function(/*int*/ count){
		var slice = document.createElement("div");	
		slice.style.position = "absolute";
		slice.style.left = "1px";
		slice.style.width = "15px";
		slice.style.height = "1px"	
		slice.style.border = "1px solid #000000";
		slice.style.borderLeft = "none";
		slice.style.borderRight = "none";	
		slice.style.top = count*3+"px";
		return slice;
	}
}
