//***************************************************************************************************
/*
This script was inspired by :
	type-ahead script at
	www.dhtmlgoodies.com
	Alf Magne Kalleland
	and:
	Ajax in Action, Crane Pascarello James, publ: Manning


*/
//***************************************************************************************************
/*
Change history
19-10-2009 ADD LUMC logo 
19-10-2009 CHG updated URLs CASK, dept Anatomy
07-04-2010 CHG adapted counting of nrs of structures in TAid (add nrExtraTaids) and adapted nrUniqueTermsInTa, both due to new insights
*/

//insert scripts & styles
document.write(""
+"<link rel=stylesheet type='text/css' href='anat_terms_styles.css'>"
)

//VARIABLE DECLARATION
//var requestFile="start.php" ; //local developing
//var requestFile="http://www.anatomicalterms.info/start.php"; //server

var ajaxCalls=new Array();
var now;
var bodyElem;
var textBoxes=new Object(); //contains refs to and info about state of the textboxes
var textBoxActive; //which textbox is active
var cachedLists=new Object(); //list of responses, for each matchString a response
var handledSpecialRequest=new Object(); //which specialRequests have already been handled, thus cached
var optionDiv; //contains the dropdown list of options
var hiddenDiv; //contains the FMAIDs of the options
var specialRequestDiv;
var synonymsBox,synonymsInfoBox;
var infoDiv; //contains the disclaimertext
var timeInfoSpan;
var logoDeptAnatomy,logoCaskSenser,logoCask,logoLumcSenser,logoLumc,linkCaskGroup,linkDeptAnatomy,linkFma,linkDisclaimer,linkNotify,linkImages,linkDocuments,linkAnatVariants;
var optionListFirstItem;
var optionListItemHighlighted; //ref to the blue option in the list
var lastUrlStringSent=""; 	//for the hidden debug info
var googleSearchTerms=""
var report=new Object() //used to report for comments

addEmphasisHtml.emphasisClassName="spanMatchText"; //className of emphasis on fragments in options
var urlCask="http://www.lumc.nl/con/3090/82525/903101130112533/?setlanguage=English&setcountry=en" //updated 19-10-2009
var urlLumc="http://www.lumc.nl/home/?setlanguage=English&setcountry=en" //ADD 19-10-2009
var urlDeptAnatomy="http://www.lumc.nl/con/3090/?setlanguage=English&setcountry=en" //updated 19-10-2009
var urlFma="http://sig.biostr.washington.edu/projects/fm/AboutFM.html"
var urlNotifyForm="anat_terms_comments_form.htm"

loc["iconInfoWiki"]="av/icon_info_wiki.png";

var nrUniqueTermsInTa=7635 //=  number mentioned in Kachlik
var nrExtraTaids=388 //=hand-counted nr of 'extra' TA-ids amongst listed TAids (this nr may be added to the nr unique TAIDs in db counted by SQL)
//Extra info: 6868 (nr unique TAIDs in db counted by SQL), 559 (nr missing hand counted)

//// STARTUP ///////////////////////////////////////////////////////////
window.onload=function()
	{loadDomReferences();
	addToLinksDb();
	enterStartTexts();
	connectEventhandlers();
	textBoxActive.focus();
	//setInterval(autoCheckMatchStringIsCurrent,500)
	getTermsStats();
	
	}

//Note: in function because script is loaded before body
function loadDomReferences()
	{
	textBoxes["entryTextBox"]=$("entryTextBox");
	textBoxes["entryTextBox"].outputDiv=$("synonymsBox");
	textBoxes["entryTextBox"].indexLastHandledMatchString=0;
	textBoxes["entryTextBox"].indexMatchStringOfDisplayedOptions=0;
	textBoxes["entryTextBox"].indexAtChoiceTerms=0;
	//textBoxes["entryTextBox"].matchStringCurrentOptions="";
	textBoxActive=textBoxes["entryTextBox"];
	synonymsBox=textBoxActive.outputDiv;
	optionDiv=$("optionDiv");
	hiddenDiv=$("hiddenDiv");
	infoDiv=$("infoDiv");
	logoCaskSenser=$("logoCaskSenser"); //19-10-2009 CHG logoSenser to logoCaskSenser
	logoCask=$("logoCaskNormal");
	logoLumcSenser=$("logoLumcSenser");	//19-10-2009 ADD
	logoLumc=$("logoLumcNormal");	
	logoDeptAnatomy=$("logoDeptAnatomy");
	bodyElem=document.documentElement;
	specialRequestDiv=$("specialRequestDiv");
	synonymsInfoBox=$("synonymsInfoBox");
	//debugging only
	//$("header").onclick=reportMatches;
	}

function addToLinksDb()
	{var prevDef="javascript:void(0)";
	
	links.push(["linkNrTermsPerLang",aAddBehvr,{eventType:"mouseover",ehFunction:ehs,ehArgument:"nrTermsPerLangDiv",href:prevDef}]);
	links.push(["linkNrTermsPerLang",aAddBehvr,{eventType:"mouseout",ehFunction:ehh,ehArgument:"nrTermsPerLangDiv",href:prevDef}]);
	links.push(["linkCaskGroup",aAddBehvr,{eventType:"click",ehFunction:goInfoCask,href:prevDef}]);
	links.push(["linkDeptAnatomy",aAddBehvr,{eventType:"click",ehFunction:goInfoDeptAnatomy,href:prevDef}]);
	links.push(["linkFma",aAddBehvr,{eventType:"click",ehFunction:showInfoFma,href:prevDef}]);
	links.push(["linkDisclaimer",aAddBehvr,{eventType:"click",ehFunction:showDisclaimer,href:prevDef}]);
	links.push(["linkNotify",aAddBehvr,{eventType:"click",ehFunction:goNotify,href:prevDef}]);
	links.push(["linkImages",aAddBehvr,{eventType:"click",ehFunction:searchImages,href:prevDef}]);
	links.push(["linkDocuments",aAddBehvr,{eventType:"click",ehFunction:searchDocuments,href:prevDef}]);
	links.push(["linkAnatVariants",aAddBehvr,{eventType:"click",ehFunction:searchAnatVariants,href:prevDef}]);
	links.push(["linkGoFma",aAddBehvr,{eventType:"click",ehFunction:goFma,className:"red",href:prevDef}]);
//	links.push(["",aAddBehvr,{eventType:"click",ehFunction:,href:prevDef}]);
//	links.push(["",aAddBehvr,{eventType:"click",ehFunction:,href:prevDef}]);

	}
	
		
function enterStartTexts()
	{ih("header",text["header"])
	ih("beta",text["beta"]);
	ih("entryHelp",text["entryHelp"]);
	ih("linkDemo",text["demo"]); //19-10-2009 ADD
	
	var textStatsInfo=text["wip"]+"<br />"+text["presCont"]+" <i><span id='nrTerms'></span>&nbsp;"+text["terms"]+" "+text["for"]+" <span id='nrStrucs'></span>&nbsp;"+text["structures"]+"<br />(<span id='pctOfTa'></span>"+text["pctOfTa"]+")<br /><a id='linkNrTermsPerLang'>"+text["nrTermsPerLang"]+"</a></i>";
	ih("statsDiv",textStatsInfo);

	var textAbout="<b><a id='linkCaskGroup'>"+text["caskGroup"]+"</a>, "
	+"<a id='linkDeptAnatomy'>"+text["deptAnatomy"]+"</a> "
	+text["copyright"]+"</b><br />"
	+"<a id='linkFma'>"+text["noticeFMA"]+text["FMA"]+"</a>.&nbsp;"
	+"<a href='links/index.html'>"+text["links"]+"</a>&nbsp;&nbsp;<a id='linkDisclaimer'>"+text["disclaimer"]+"</a>&nbsp;&nbsp;<span id='timeInfoSpan'></span>";
	ih("about",textAbout);
	
	ih("synonymsInfoBox","<a id='linkNotify'>"+text["notify2"]+"</a>");
	ih("linkImages",text["images"]);
	ih("linkDocuments",text["documents"]);
	ih("linkAnatVariants",text["anatVariants"]);
	
	for (thisBox in textBoxes)	{setEmptyTextBoxMessage(null,textBoxes[thisBox]);}	
	}	

	
//puts msg 'EnterTextHere' in textbox and sets style to grey
function setEmptyTextBoxMessage(e,textBoxRef)
	{e=(e)? e : ((window.event)? window.event : null);
	textBoxRef= (textBoxRef)? textBoxRef : getSourceElement(e);
	if (textBoxRef.value!="") {return;}
	textBoxRef.value=text["msgEnterName"];
	textBoxRef.className="ajaxTextBox ajaxTextBoxInstructionText";
	textBoxRef.inUse=false;  //nothing typed in yet
	}

function setEmptySynonymsBoxMessage()
	{synonymsBox.innerHTML="<span id='msgHereResults'>"+text["msgHereResults"]+"</span>";
	}

//clears msg 'EnterTextHere' and resets style to normal	
function clearTextBoxMessage()
	{var textBoxRef=this;
	if(!textBoxRef.inUse)
		{textBoxRef.value="";
		textBoxRef.lastHandledMatchString=""  ;
		textBoxRef.className="ajaxTextBox ajaxTextBoxActive";
		textBoxRef.inUse=true;
		textBoxRef.focus(); //neccessary for FF
		}
	}
		
function connectEventhandlers()
	{document.body.onkeydown=handleKeyDown;
	bodyElem.onclick=handleBodyClick;
	for (thisBox in textBoxes)
		{textBoxes[thisBox].onkeydown=clearTextBoxMessage;
		textBoxes[thisBox].onkeyup=function (e) {handleTextBoxEntry(e,this);}
		}
	searchAnchorsAndConnectBehaviours()
	$("debug").onclick=showDebugInfo;
	logoCaskSenser.onmouseover=lightDownCaskLogo; //19-10-2009 CHG logoSenser to logoCaskSenser
	logoCaskSenser.onmouseout=lightUpCaskLogo;
	logoCaskSenser.onclick=goInfoCask;
	logoLumcSenser.onmouseover=lightDownLumcLogo //19-10-2009 ADD
	logoLumcSenser.onmouseout=lightUpLumcLogo;
	logoLumcSenser.onclick=goInfoLumc;
	}	

function getTermsStats()
	{var ajaxIndex=ajaxCalls.length;
	ajaxCalls[ajaxIndex]=new sack();
	ajaxCalls[ajaxIndex].requestFile=locations.requestFileGetNames;
	ajaxCalls[ajaxIndex].method="GET";
	ajaxCalls[ajaxIndex].onCompletion=function() {displayTermsStats(ajaxIndex)};
	ajaxCalls[ajaxIndex].setVar("requestFor","termsStats");
	ajaxCalls[ajaxIndex].runAJAX();
	}

function displayTermsStats(ajaxIndex)
	{var response=ajaxCalls[ajaxIndex].response; //get ajax response
	var oStats=convertResponseStringToObj(response); 
	var nrStrucs=parseInt(oStats.nrTaids) + nrExtraTaids; //nrExtraTaids: see above for explanation
	var pctOfTa=Math.round(nrStrucs/nrUniqueTermsInTa*100);
	//general intro
	$("nrTerms").innerHTML=oStats.nrTerms;
	$("nrStrucs").innerHTML=nrStrucs;
	$("pctOfTa").innerHTML=pctOfTa;

	//listing languages
	var str="<ul>"
	str+="<li>"+text["eponyms"]+": "+oStats.nrEponyms+"</li>";
	var language;
	for(var entry in oStats)
    	{if(entry!="nrTerms" && entry!="nrTaids" && entry!="nrEponyms") //other entries are languages
			{language=(entry=="LatEngTA")? text["latEngTa"] : entry; //translate temp value 'LatEngTA'
			str+="<li>"+language+": "+oStats[entry]+"</li>";}
    	}
	str+="</ul>";
	$("nrTermsPerLangDiv").innerHTML=str;
	}
	
function handleKeyDown(e)
	{e=(e)? e : event;
	//var srcElem=getSourceElement(e)
	//alert(srcElem)
	if(optionDiv.style.display== "none") {return}
	
	var keyHit=getKeyHit(e)
	
	if(keyHit=="Up") {moveHighlightOptionFromKeyboard("Up");}
	else if(keyHit=="Down") {moveHighlightOptionFromKeyboard("Down");}
	else if(keyHit=="Enter") 
		{if(optionListItemHighlighted) {selectOption(optionListItemHighlighted)}
		return false
		}
	else if(keyHit=="Esc")  
		{hideOptionDiv(); 
		hideSpecialRequestDiv();
		setEmptySynonymsBoxMessage();
		}
	}	

function goInfoCask()
	{window.open(urlCask)}

function goInfoLumc()
	{window.open(urlLumc)}

function goInfoDeptAnatomy()
	{window.open(urlDeptAnatomy)}

function showInfoFma()
	{ih("infoDiv","<div class='smallGrayInfo'>"+text["infoFMA"]+"</div>");
	infoDiv.style.display="block";
	}

function goFma()
	{window.open(urlFma)}

function showDisclaimer()
	{ih("infoDiv","<div class='smallGrayInfo'>"+text["disclaimerText"]+"</div>");
	infoDiv.style.display="block"
	}
	
function hideInfoDiv()
	{infoDiv.style.display="none";
	infoDiv.innerHTML="";
	}
	
function goNotify()
	{window.open(urlNotifyForm)}

function lightUpCaskLogo() 				//19-10-2009 CHG lightUpLogo to lightUpCaskLogo
	{logoCask.style.visibility="visible"
	logoCaskSenser.style.cursor="default"
	}		

function lightDownCaskLogo()
	{logoCask.style.visibility="hidden"
	logoCaskSenser.style.cursor="pointer"
	}		

function lightUpLumcLogo()				//19-10-2009 ADD
	{logoLumc.style.visibility="visible"
	logoLumcSenser.style.cursor="default"
	}		

function lightDownLumcLogo()
	{logoLumc.style.visibility="hidden"
	logoLumcSenser.style.cursor="pointer"
	}		
	
function showInSynonymsInfoBox(text)
	{synonymsInfoBox.innerHTML=text}
	
function emptySynonymsInfoBox()
	{synonymsInfoBox.innerHTML=""}		
	

	
function handleBodyClick(e)
	{e=(e)? e : ((event)? event : null);
	var srcElem=getSourceElement(e)
	hideOptionDiv();
	hideSpecialRequestDiv();
	hidePopUp();
	if(!(srcElem.id=="linkDisclaimer" || srcElem.id=="linkFma" || srcElem.id=="infoDiv")) {hideInfoDiv();}
	}
/*not used, might come of handy one time, keep a a moment	
function autoCheckMatchStringIsCurrent()
	{//no textBox active: quit
	if(!textBoxActive) {return;}
	//get current entry in textbox
	var matchString=textBoxActive.value.toLowerCase();
	//is same as matchstring of options? Then all OK: no action needed
	if(matchString==matchStringCurrentOptions) {return;}
	//if no, still ajaxCall open? Then just wait for return of this ajax call: no action needed.
	if(lastUnreturnedCallAjaxIndex!=null) {return;}
	//call new Ajax
	getMatchingTerms(null,textBoxActive);
	}
*/

/////////////////////////////////////////////////////////////////////////////////////
//// TEXTBOX, HANDLING ENTRY OF TYPED LETTERS ////////////////////////////////////////////////		
function handleTextBoxEntry(e,textBoxRef)
	{
	e=(e)? e : ((event)? event : null);
	
	//if Enter or Tab typed
	if(e!=null && (e.keyCode==13 || e.keyCode==9)) {return false};

	//get current letters
	var matchString=textBoxRef.value.toLowerCase()
	//alert("textBoxRef.lastHandledMatchString= "+textBoxRef.lastHandledMatchString+", matchString= "+matchString);

	//normalize one or more spaces to one space
	matchString=singleSpaces(matchString);
	//trim of any leading or trailing spaces
	matchString=trimSurroundingSpaces(matchString);
	
	//no new *letters* typed, e.g. Up/Down keys typed or other non-char key
	if(textBoxRef.lastHandledMatchString==matchString) {return;}

	//repair IE behaviour that Esc empties textbox
	if(e && e.keyCode==27 && textBoxRef.lastHandledMatchString)
		{textBoxRef.value=textBoxRef.lastHandledMatchString; return;}

	//if matchString is empty, display empty textbox & empty synonyms message
	if(matchString=="") 
		{setEmptyTextBoxMessage(e,textBoxRef);
		setEmptySynonymsBoxMessage();
		hideOptionDiv(); hideSpecialRequestDiv();
		return;
		}
		
	//only start lookup with at least 2 non-space characters	
	if(matchString.length<2) {hideOptionDiv(); hideSpecialRequestDiv();hideResources(); return} 

	getMatchingTerms(e,textBoxRef,matchString);
	setEmptySynonymsBoxMessage();
	hideResources();
	}

//MAIN FUNCTION HANDLING TYPED IN LETTERS			
function getMatchingTerms(e,textBoxRef,matchString,specialRequest)
	{
	if( (typeof matchString=="undefined") || matchString=="" ) {return;}
	now=new Date();

	//store last handled matchString and generate an index number
	textBoxRef.lastHandledMatchString=matchString;
	textBoxRef.indexLastHandledMatchString++;
	//a local variable is needed to 'catch' the present value in the closure below for the ajax oncompletion function
	var copiedIndex=textBoxRef.indexLastHandledMatchString;
	textBoxActive=textBoxRef;

	//replace anatomical abbreviations
	if(matchString.indexOf(".")!=-1) {matchString=replaceAnatomicalAbbreviations(matchString)}
	//no searching on nor underlining general words like 'of' ...delete word 'of'
	matchString=matchString.replace(/ of /," ")

	//if this matchString was already retrieved before, display cached option list
	if(cachedLists[matchString])
		{if(!specialRequest || (specialRequest && handledSpecialRequest[specialRequest]))
			{displayItemsInOptionDiv(textBoxRef,matchString,cachedLists[matchString],copiedIndex);
			return;
			}
		}

	//save boolean that this specialRequest was already done
	if(specialRequest)
		{handledSpecialRequest[specialRequest]=true;}
		
	//else call ajax	
	var ajaxIndex=ajaxCalls.length;
	ajaxCalls[ajaxIndex]=new sack();
	ajaxCalls[ajaxIndex].requestFile=locations.requestFileGetNames;
	ajaxCalls[ajaxIndex].method="GET";
	ajaxCalls[ajaxIndex].onCompletion=function() {handleReturnedTerms(ajaxIndex,textBoxRef,matchString,copiedIndex)};
	ajaxCalls[ajaxIndex].setVar("requestFor","terms");
	ajaxCalls[ajaxIndex].setVar("letters",matchString);
	if(specialRequest)
		{ajaxCalls[ajaxIndex].setVar("specialRequest",specialRequest);}
	var srcElem=getSourceElement(e);
	//alert("keyCode="+e.keyCode+", from srcElem: "+srcElem.id+" called ajaxCalls["+ajaxIndex+"] with matchstring:"+matchString)
	ajaxCalls[ajaxIndex].runAJAX();
	ajaxCalls[ajaxIndex].start=now.getTime();
	//show temporary message 'retrieving' till response comes in
	showMsgRetrievingData(matchString,textBoxRef);

	//debugging
	//testSentLetters.push("ajaxIndex:("+ajaxIndex+") "+matchString+"["+copiedIndex+"]");
	}


function replaceAnatomicalAbbreviations(matchString)
	{matchString=matchString.replace(/[aA]a\./,"arteriae ");
	matchString=matchString.replace(/[aA]\./,"arteria ");
	matchString=matchString.replace(/[lL]igg\./,"ligamenta ");
	matchString=matchString.replace(/[lL]ig\./,"ligamentum ");
	matchString=matchString.replace(/[mM]m\./,"musculi ");
	matchString=matchString.replace(/[mM]\./,"musculus ");
	matchString=matchString.replace(/[nN]n\./,"nervi ");
	matchString=matchString.replace(/[nN]\./,"nervus ");
	matchString=matchString.replace(/[rR]r\./,"rami ");
	matchString=matchString.replace(/[rR]\./,"ramus ");
	matchString=matchString.replace(/[vV]v\./,"venae ");
	matchString=matchString.replace(/[vV]\./,"vena ");

	return matchString;
	}
	

	
//// AJAX CALLBACK FUNCTION FOR RETURNED TERMS ////////////////////////////////////////////////////

function handleReturnedTerms(ajaxIndex,textBoxRef,matchString,thisMatchStringIndex)
	{//for the hidden debug info
	lastUrlStringSent=ajaxCalls[ajaxIndex].URLString;
	//debugging
	//testReceivedLetters.push("ajaxIndex:("+ajaxIndex+") "+matchString+"["+thisMatchStringIndex+"]");
	
	var response=ajaxCalls[ajaxIndex].response; //get ajax response
	var arrOptionsInfo=convertResponseStringToArr(response); //convert ajax string to multidim. array
	cachedLists[matchString]=arrOptionsInfo; //cache it
	
	//1. prevent overwriting by delayed returns if newer matchStrings are already displayed
	//2. prevent again showing delayed returns options when already a choice has been made
	if(textBoxRef.indexMatchStringOfDisplayedOptions>thisMatchStringIndex || 	textBoxRef.indexAtChoiceTerms>thisMatchStringIndex) 
		{//testTooLateReceivedLetters.push("ajaxIndex:("+ajaxIndex+") "+matchString+"["+thisMatchStringIndex+"]");
		return;};

	//display the options in the dropdown
	displayItemsInOptionDiv(textBoxRef,matchString,arrOptionsInfo,thisMatchStringIndex); //display in optiondiv
		
	//show time used	
	now=new Date();
	var timepassed=(now.getTime()-ajaxCalls[ajaxIndex].start)/1000;
	ih("timeInfoSpan","("+timepassed+" sec)");
	}

	
function convertResponseStringToArr(response)	
	{/*expected format response:
	name:::value###name:::value###name:::value etc ...
	will be converted to: array with arrays 
	cont[0][0]=name0, cont[0][1]=value1
	cont[1][0]=name1, cont[1][1]=value2
	cont[2][0]=name2, cont[2][1]=value3
	etc...
	*/
	var arrOptions=response.split("###");
	var cont=new Array();
	var arrOfNameAndValue;
	
	for(var i=0;i<arrOptions.length;i++)
    	{arrOfNameAndValue=arrOptions[i].split(":::");
		cont[i]=[arrOfNameAndValue[0],arrOfNameAndValue[1]]; //0=FMAID, 1=term of structure
    	}
	return cont;
	}
		
function convertResponseStringToObj(response)	
	{/*expected format response:
	name:::value###name:::value###name:::value etc ...
	will be converted to: obj map
	*/
	var arrOptions=response.split("###");
	var arNameValue, o={};
	
	for(var i=0;i<arrOptions.length;i++)
    	{arNameValue=arrOptions[i].split(":::");
		o[arNameValue[0]]=arNameValue[1]; //0=name, 1=value
    	}
	return o;
	}		
		

//// DISPLAY OF OPTIONS IN DROPDOWN LIST ////////////////////////////////////////////
//MAIN FUNCTION
function displayItemsInOptionDiv(textBoxRef,matchString,arrOptionsInfo,thisMatchStringIndex)
	{
	if(!textBoxRef || !arrOptionsInfo) {hideOptionDiv(); hideSpecialRequestDiv();return;}

	//store matchStringIndex of the optionlist we're going to display now
	textBoxRef.indexMatchStringOfDisplayedOptions=thisMatchStringIndex;
	//debugging
	//testDisplayedLetters.push(matchString+"["+thisMatchStringIndex+"] ")
	
	var FMAID,strucTerm,optionHtmlElem,optionRef;
	var outputHtmlString="",hiddenHtmlString="",nrOptions=0;
	
	//build up optionlines 
	for(var i=0;i<arrOptionsInfo.length;i++)
    	{FMAID=arrOptionsInfo[i][0];
		strucTerm=arrOptionsInfo[i][1];
		if(typeof strucTerm =="undefined") {continue;}
		//alert("FMAID="+FMAID+", strucTerm="+strucTerm);
		optionHtmlElem= "<span id='option"+i+"' class='optionNormal'>"+strucTerm+"</span>"; 
		outputHtmlString+=optionHtmlElem;
		hiddenHtmlString+="<span id='FMAID"+i+"'>"+FMAID+"</span>";
		nrOptions++;
    	}
	
	//hide optionDiv when there are no options
	if (nrOptions==0) {hideOptionDiv(); hideSpecialRequestDiv();return;}
	//enter options in the optionDiv, position the optionDiv below the textEntry box
	positionOptionDiv(textBoxRef);
	setOptionDivForOptions();
	optionDiv.innerHTML=outputHtmlString;
	showOptionDiv();	
	//highlight the first option
	optionListFirstItem=$("option0");
	highlightOption(optionListFirstItem);
	
	//attach eventhandlers - must be done *after* innerHTML insert!
	for(var i=0;i<nrOptions;i++)
    	{optionRef=$("option"+i);
		optionRef.onmouseover=function(){highlightOption(this)};
		optionRef.onclick=function() {selectOption(this)};
    	}

	//EMPHASIZING matchString fragments	
	//equivalently as on the server split the matchString in its fragments on spaces or comma's and 
	//limit to 3, so only the strings that are sought & sorted on or are highlighted
	var arrMatchStringFragments=matchString.split(/[\s,]+/,3);
	//get rid of any empty string fragments (that will crash highlighting) - ocurring when nothing before/after delimiter in FF
	for (var i=0;i<arrMatchStringFragments.length;i++)
    	{if(arrMatchStringFragments[i]=="") {arrMatchStringFragments.splice(i,1)} }
	//store the parameters and start the highlighting process		
	runProcessEmphasizeFragmentsInOptions.optionListFirstItem=optionListFirstItem;
	runProcessEmphasizeFragmentsInOptions.arrMatchStringFragments=arrMatchStringFragments;
	runProcessEmphasizeFragmentsInOptions();

	//show any special request possibilities	
	hideSpecialRequestDiv();
	displaySpecialRequests(arrMatchStringFragments[0])
	
	//stores the corresponding FMAIDs	
	hiddenDiv.innerHTML=hiddenHtmlString;
	}

	
//OptionDiv, support functions
function showOptionDiv()
	{optionDiv.style.display= "block";
	}

function hideOptionDiv()
	{optionDiv.style.display= "none";
	}

function optionDivVisible()
	{return (optionDiv.style.display=="block")? true :false;
	}
	
//position OptionDiv below specified textbox	
function positionOptionDiv(textBoxRef)
	{var textBoxPos=getPositionElem(textBoxRef);
	var optionDivStyle=optionDiv.style; //for speed: look-up reference only once
	
	optionDivStyle.left= textBoxPos.left+"px";
	optionDivStyle.top= (textBoxPos.top + textBoxRef.offsetHeight)+"px";
	optionDivStyle.width= (textBoxRef.offsetWidth-2)+"px";
	}

function setOptionDivForMsg()	
	{setClassName(optionDiv,"optionDivForMsg")
	}
	
function setOptionDivForOptions()	
	{setClassName(optionDiv,"optionDivForOptions")
	}

function getOptionDivClassname()
	{return optionDiv.className;
	}

function showMsgRetrievingData(matchString,textBoxRef)
	{if(optionDivVisible() && (getOptionDivClassname()=="optionDivForOptions") ) {return;} //only show msg if no options yet
	positionOptionDiv(textBoxRef);
	setOptionDivForMsg();
	optionDiv.innerHTML="<span id='msgRetrievingData'>"+text["msgRetrieving1"]+"<b>"+matchString+"</b>"+text["msgRetrieving2"]+"</span>";
	showOptionDiv();
	}
	
function highlightOption(optionRef)		
	{if(optionListItemHighlighted) {optionListItemHighlighted.className="optionNormal";}
	if(optionRef)
		{optionRef.className="optionHighlighted";
		optionListItemHighlighted=optionRef;
		}
	}
	
function moveHighlightOptionFromKeyboard(direction)
	{if(direction=="Up")
		{if(!optionListItemHighlighted) {return;}
		if(!optionListItemHighlighted.previousSibling){return;}
		highlightOption(optionListItemHighlighted.previousSibling);
		}
	else if	(direction=="Down")
		{//alert("down optionListItemHighlighted= "+optionListItemHighlighted.id+", optionListFirstItem= "+optionListFirstItem.id);
		if(!optionListItemHighlighted) {highlightOption(optionListFirstItem);}
		if(!optionListItemHighlighted.nextSibling){return;}
		highlightOption(optionListItemHighlighted.nextSibling);
		}
	scrollOptionList()	
	}

		
function scrollOptionList()
	{if(optionListItemHighlighted!=null) {optionRef=optionListItemHighlighted} else return

	var selectedOptionTop=optionRef.offsetTop
	var selectedOptionBottom=selectedOptionTop + optionRef.offsetHeight;
	var boxBottom=optionDiv.offsetHeight;
	var scrollDownDistance=optionDiv.scrollTop

	//alert("selectedOptionTop="+selectedOptionTop+"\nselectedOptionBottom="+selectedOptionBottom+"\nboxBottom="+boxBottom+"\nscrollDownDistance="+scrollDownDistance)
	
	if(selectedOptionTop - scrollDownDistance < 0) //above top
		{optionDiv.scrollTop = selectedOptionTop;}
	else if(selectedOptionBottom >= boxBottom + scrollDownDistance - 2) //below bottom
		{optionDiv.scrollTop = selectedOptionBottom - boxBottom + 2;}
	}	


/// Special Requests ///////////////////////////////////////////////////////////////	
function displaySpecialRequests(term)
	{var request=null,requestText
	if(term=="arteria" && !handledSpecialRequest["allArteriaArteriae"]) 
		{request="allArteriaArteriae";
		requestText=text["allArteriaArteriae"];
		}
	else if(term=="arteriae" && !handledSpecialRequest["allArteriae"]) 
		{request="allArteriae";
		requestText=text["allArteriae"];
		}
	else if(term=="ligamentum" && !handledSpecialRequest["allLigamentumLigamenta"]) 
		{request="allLigamentumLigamenta";
		requestText=text["allLigamentumLigamenta"];
		}
	else if(term=="ligamenta" && !handledSpecialRequest["allLigamenta"]) 
		{request="allLigamenta";
		requestText=text["allLigamenta"];
		}
	else if(term=="musculus" && !handledSpecialRequest["allMusculusMusculi"]) 
		{request="allMusculusMusculi";
		requestText=text["allMusculusMusculi"];
		}
	else if(term=="musculi" && !handledSpecialRequest["allMusculi"]) 
		{request="allMusculi";
		requestText=text["allMusculi"];
		}
	else if(term=="nervus" && !handledSpecialRequest["allNervusNervi"]) 
		{request="allNervusNervi";
		requestText=text["allNervusNervi"];
		}
/*	not necessary because  <60 nervi
	else if(term=="nervi" && !handledSpecialRequest["allNervi"]) 
		{request="allNervi";
		requestText=text["allNervi"];
		}*/
	else if(term=="ramus" && !handledSpecialRequest["allRamusRami"]) 
		{request="allRamusRami";
		requestText=text["allRamusRami"];
		}
	else if(term=="rami" && !handledSpecialRequest["allRami"]) 
		{request="allRami";
		requestText=text["allRami"];
		}
	else if(term=="vena" && !handledSpecialRequest["allVenaVenae"]) 
		{request="allVenaVenae";
		requestText=text["allVenaVenae"];
		}
	else if(term=="venae" && !handledSpecialRequest["allVenae"]) 
		{request="allVenae";
		requestText=text["allVenae"];
		}
		
	if(request)	
		{specialRequestDiv.innerHTML=text["specRequest1"]+"<a id='specRequestLink' href='javascript:'>"+requestText+"</a>"+text["specRequest3"]
		$("specRequestLink").onclick= function(){getSpecialRequest(term,request)};
		showSpecialRequestDiv();
		}
	}

function showSpecialRequestDiv()
	{specialRequestDiv.style.display="block"}

function hideSpecialRequestDiv()
	{specialRequestDiv.style.display="none"}
	
function getSpecialRequest(term,specialRequest)	
	{//event=null, textBox=textBoxActive - we only have one here
	getMatchingTerms(null,textBoxActive,term,specialRequest)
	
	specialRequestDiv.innerHTML=text["processing"]
	}
		

/// EMPHASIZING FRAGMENTS FUNCTIONS ////////////////////////////////////////////////////////////
	
function runProcessEmphasizeFragmentsInOptions()
	{/*Note: because options can be destroyed any moment by a new incoming ajax response
	do all in a try, and test for existance of activeOption _again_ after time-consuming emphasizeFragments
	*/
	try
		{var optionText,optionWithEmphasizeHtml;
		//get the variables from the props stored on the function object self
		var activeOption=runProcessEmphasizeFragmentsInOptions.optionListFirstItem;
		var arrMatchStringFragments=runProcessEmphasizeFragmentsInOptions.arrMatchStringFragments;
		if(activeOption) 
			{//get text of option
			optionText=activeOption.firstChild.nodeValue;
			//insert underline html
			optionWithEmphasizeHtml=emphasizeFragments(optionText,arrMatchStringFragments);
			}
		else {return;}
		if(activeOption) 	
			{activeOption.innerHTML=optionWithEmphasizeHtml;
			//get next option
			activeOption=activeOption.nextSibling;
			}
		else {return;}
		if(!activeOption) {return;}
		else
			{//store new activeOption ref in the prop on the function self
			//because timeout doesn't allow arguments
			arguments.callee.optionListFirstItem=activeOption;
			//call execution for following option by a separated process (via timeout) to allow rendering of the page
			clearTimeout(arguments.callee.timeout);
			arguments.callee.timeout=setTimeout("runProcessEmphasizeFragmentsInOptions()",1);
			}
		}		
	catch(e)
		{return}
	}

	
function emphasizeFragments(word,arrMatchStringFragments)
	{if(typeof word=="undefined" || typeof arrMatchStringFragments=="undefined")
		{return word;}

	//flatten overlapping matches, returnvalue= array with true for all letters to emphasize
	var emphasisPosition=flattenMatches(word,arrMatchStringFragments)
	
	//insert html to emphasize matches
	return 	addEmphasisHtml(word,emphasisPosition)
	}


//var testLetters=new Array()
	
//flatten overlapping matches , returnvalue= array with true for all letters to emphasize, like: uuuTTTuuuuTTuuuuuuuuu (undefined, True)
function flattenMatches(word,arrMatchStringFragments)
	{var fragment,fragmentLength,regExp,result,location,matchLocations=new Array();
	//create an array with a position for every letter of the word
	var emphasisPosition=new Array(word.length);

//testLetters.push("<u>Word= "+word+"</u><br />");
		
	//main loop: search all matches of all fragments. The result is a filled in array emphasisPosition  
	for(var i=0;i<arrMatchStringFragments.length;i++)
    	{fragment=arrMatchStringFragments[i];
		//nr of chars of fragment
		fragmentLength=fragment.length;
		//convert to globalized regexp
		regExp=getGlobalizedRegExp(fragment);

		//find locations of matches, first reset for each fragment
		matchLocations.length=0
		while((result=regExp.exec(word)) != null)
			{matchLocations.push(result.index);}
			
		//for each matchlocation register the letterpositions to be emphasized in array emphasisPosition
		for(var j=0;j<matchLocations.length;j++)
        	{location1stLetter=matchLocations[j];
			locationLastLetter=location1stLetter+fragmentLength;
//testLetters.push("location1stLetter= "+location1stLetter+", fragmentLength= "+fragmentLength+", locationLastLetter= "+locationLastLetter+"<br />")
			for(var k=location1stLetter;k<locationLastLetter;k++)
             	{emphasisPosition[k]=true;}
        	} //end registering letterpositions to be emphasized
    	}//end for loop searching all matches of all fragments

//testLetters.push("<br />Final Highlight letters: "+emphasisPosition+"<br />")
	return emphasisPosition;
	}
	
//function to enable matching despite diacritics
function getGlobalizedRegExp(string)
	{var regExp;
	//escape regExp special characters
	string=string.replace(/\^/gi,"\\^");
	string=string.replace(/\$/gi,"\\$");
	string=string.replace(/\./gi,"\\.");
	string=string.replace(/\*/gi,"\\*");
	string=string.replace(/\+/gi,"\\+");
	string=string.replace(/\?/gi,"\\?");
	string=string.replace(/\=/gi,"\\=");
	string=string.replace(/\!/gi,"\\!");
	string=string.replace(/\:/gi,"\\:");
	string=string.replace(/\|/gi,"\\|");
	string=string.replace(/\\/gi,"\\\\");
	string=string.replace(/\/{1}/gi,"\\/");
	string=string.replace(/\(/gi,"\\(");
	string=string.replace(/\)/gi,"\\)");
	string=string.replace(/\[/gi,"\\[");
	string=string.replace(/\]/gi,"\\]");
	string=string.replace(/\{/gi,"\\{");
	string=string.replace(/\}/gi,"\\}");

	var globalizedMatchString = string.replace(/[aàáâãäåæ]/gi,"[aàáâãäåæ]"); // AdM : expand diacritics (both ways)
	globalizedMatchString = globalizedMatchString.replace(/[eèéêë]/gi,"[eèéêë]"); // AdM
	globalizedMatchString = globalizedMatchString.replace(/[iìíîï]/gi,"[iìíîï]"); // AdM
	globalizedMatchString = globalizedMatchString.replace(/[cç]/gi,"[cç]"); // AdM 
	globalizedMatchString = globalizedMatchString.replace(/[nñ]/gi,"[nñ]"); // AdM
	globalizedMatchString = globalizedMatchString.replace(/[oòóôõöø]/gi,"[oòóôõöø]"); // AdM
	globalizedMatchString = globalizedMatchString.replace(/[uùúûü]/gi,"[uùúûü]"); // AdM
		
	return regExp=new RegExp(globalizedMatchString,"gi"); // AdM
	}

function addEmphasisHtml(string,emphasisPosition)
	{var emphStart="<span class='"+addEmphasisHtml.emphasisClassName+"'>";
	var emphEnd="</span>";
	var htmlString="";

	for(var i=0;i<string.length;i++)
    	{//alert("Letter="+string.charAt(i)+", emphasisPosition[i-1]="+emphasisPosition[i-1]+", emphasisPosition[i]="+emphasisPosition[i])
		//insert emphasis-start-span if emphasisPosition[previousLetter]==F/undef & thisLetter==T
		if(!emphasisPosition[i-1] && emphasisPosition[i])
			{htmlString+=emphStart+string.charAt(i);}
		//insert emphasis-end-span if emphasisPosition[previousLetter]==T & thisLetter==F/undef
		else if(emphasisPosition[i-1] && !emphasisPosition[i])
			{htmlString+=emphEnd+string.charAt(i);}
		//else simply copy the letter	
		else
			{htmlString+=string.charAt(i);}	
    	}
	//end tag for any emphasized fragments that run to end of string	
	if(emphasisPosition[string.length-1]){htmlString+=emphEnd;}

	return htmlString;
	}	


	
/////////////////////////////////////////////////////////////////////////////////////
/// HANDLE TERM CHOSEN, GET THE SYNONYMS ////////////////////////////////////////////

function selectOption(optionRef)
	{optionRef= (optionRef==null)? this : optionRef;
	//get clean text from dropdown
	var termChosen=getCleanTerm(optionRef);
	report.selected=termChosen //for comment reporting
	//copy text in the entry field
	textBoxActive.value=termChosen;
	//get FMAID of the termchosen
	var fmaidChosen=getFmaidOfOption(optionRef)
	//get synonyms and put them in the synonymsbox
	getSynonyms(fmaidChosen,textBoxActive)
	//hide optionlist
	optionDiv.style.display="none"
	//return focus to entry box, timeout uncoupling necessary for some reason
	setTimeout("giveTextBoxFocus()",1);
	textBoxActive.indexAtChoiceTerm=textBoxActive.indexLastHandledMatchString;
	}

function giveTextBoxFocus()
	{textBoxActive.focus();
	}
	
function getCleanTerm(optionRef)	
	{var termChosen=optionRef.innerHTML;
	//get rid of spans surrounding the matches
	var startPattern=/<span class="?spanmatchtext"?>/gi
	var endPattern=/<\/span>/gi
	
	termChosen=termChosen.replace(startPattern,"");
	termChosen=termChosen.replace(endPattern,"");
	return termChosen;
	}
	
function getFmaidOfOption(optionRef)
	{//get ID of termchosen
	var idTermChosen=optionRef.id;
	//replace "option", by "FMAID" to get the hidden FMAID field id
	var idFmaidChosen=idTermChosen.replace("option","FMAID")
	//get the FMAID of the chosen structure
	var FMAID=$(idFmaidChosen).firstChild.nodeValue;
	return FMAID
	}	

//AJAX CALL FOR THE SYNONYMS	
function getSynonyms(fmaidChosen,whichTextBox)	
	{
	var ajaxIndex=ajaxCalls.length;
	ajaxCalls[ajaxIndex]=new sack();
	ajaxCalls[ajaxIndex].requestFile=locations.requestFileGetNames;
	ajaxCalls[ajaxIndex].method="GET";
	ajaxCalls[ajaxIndex].onCompletion=function() {handleReturnedSynonyms(ajaxIndex,whichTextBox);};
	ajaxCalls[ajaxIndex].setVar("requestFor","synonyms");
	ajaxCalls[ajaxIndex].setVar("termId",fmaidChosen);
	ajaxCalls[ajaxIndex].runAJAX();
	}

//Ajax callback function for the synonyms	
function handleReturnedSynonyms(ajaxIndex,whichTextBox)
	{var response=ajaxCalls[ajaxIndex].response
	//for the hidden debug info
	lastUrlStringSent=ajaxCalls[ajaxIndex].URLString;
	
	var synonymsBox=whichTextBox.outputDiv

	if(response=="" || response==null || response.search(/Could not perform/)!=-1)
		{synonymsBox.innerHTML=""}
	else
		{//create inserttext from response
		var o =handleResponseString(response,ajaxIndex)
	//	var synonymsHTML=o.synonymsHTML; //for ease of reading, read out return object
	//	var arTermInfoHtml=o.arTermInfoHtml
		
		//console.log("l. 856 arTermIndex",arTermIndex,"synonymsHTML="+synonymsHTML+", arTermInfoHtml=",arTermInfoHtml)
		//insert into synonymsBox
		synonymsBox.innerHTML=o.synonymsHTML

		var oData, elemIdOrRef;		
		var arTermIndex=o.arTermIndex;
		//connect eventhandlers on terms
		for (var i=0;i<arTermIndex.length;i++)
        	{termIndex=arTermIndex[i]; //for ease of reading
			oData={term:o.arTerms[termIndex],termInfo:o.arTermInfoHtml[termIndex]};
			elemIdOrRef="term_"+termIndex;
			addEventHandler(elemIdOrRef,"click",ehHandleTermClick,oData);
			addEventHandler(elemIdOrRef,"mouseover",ehAddClass,{className:"termHover"});
			addEventHandler(elemIdOrRef,"mouseout",ehRemoveClass,{className:"termHover"});
        	}	
		//trigger image search
		//searchImages()		
		//
		displayGoogleLinksBar();
		}
	}

function ehHandleTermClick(e,oData)
	{var termInfo="<div class='termInfo'><div class='termInfoHeader'>"+oData.term+"</div><div class='termInfoContent'>"+oData.termInfo+"</div></div>";
	ih("infoDiv",termInfo);
	infoDiv.style.display="block";
	googleSearchTerms=oData.term;
	searchImages();
	//stopPropagation 
	if(e.stopPropagation) {e.stopPropagation()} //W3C
	e.cancleBubble=true; //IE
	
	}

/*function ehEvokePopUp(e,oData)
	{clearTimeout(toPopUp)
	var callingElem=this;
	showPopUp(callingElem,oData);
	}

var toPopUp;
function delayedHidePopup()	
	{clearTimeout(toPopUp);
	toPopUp=setTimeout("hidePopUp()",1500)
	}
*/	
function handleResponseString(response,ajaxIndex)
	{var strLatinTA="",strLatinNonTA="";
	var TAID="";
	var strEpnLatinTA="", strEpnLatinNonTA="", strEpnEnglishTA="", strEpnEnglishNonTA="", strEpnOtherLang="";
	var arEpnOtherLang=new Array();
	var strLatEngTA="",strEnglishTA="",strEnglishPrefName="",strEnglish="";
	var arOtherLang=new Array();
	var strOtherLang="";
	var arTermBlocks,arTermInfoFields,arInfoKeys;
	var oTerm=new Object();
	var oTermOtherLang;
	var termIndex,term,lang,elemId;
	var arTerms=[];arTermIndex=[],arTermInfoHtml=[];
	var termLatinTA="",termLatinNonTA="";
	var termEpnLatinTA="",termEpnLatinNonTA="",termEpnEnglishTA="",termEpnEnglishNonTA="";
	var termLatEngTA="",termEnglishTA="",termEnglishPrefName="",termEnglish="";
	report.synonyms="";

	//split on terms
	arTermBlocks=response.split("###");

	//for each term...
	for (var i=0;i<arTermBlocks.length;i++)
    	{//split on termInfofields (term, language, prefName, eponym, authority, etc.)
		arTermInfoFields=arTermBlocks[i].split("|||");
		//empty oTerm
		oTerm={};
		//for each infofield... (term, language, prefName, eponym, authority, etc.)
		for(var j=0;j<arTermInfoFields.length;j++)
	       	{//split each infofield in its key and value and store in Object
			arInfoKeys=arTermInfoFields[j].split(":::");
			oTerm[arInfoKeys[0]]=arInfoKeys[1];
        	}
		termIndex=oTerm["termIndex"];
		arTermIndex[arTermIndex.length]=termIndex; //keep termIndexs to bind eventhandlers
		term=oTerm["term"];
		//TAID=oTerm["TAID"];
		arTermInfoHtml[termIndex]=fCreateTermInfoHtml(oTerm); //stores complete popUp termInfo per term
		arTerms[termIndex]=term; //store term separately for sending to termInfo panel
		elemId="term_"+termIndex; //to prevent repetition
		//console.log("handling term "+term+" with termIndex: "+termIndex)
		//LatinTA
		if(isLatin(oTerm) && !isEponym(oTerm) && isFromTA(oTerm)) 
			{strLatinTA+="<span id='"+elemId+"' class='termLatin'>"+term+"&nbsp;<i>(Lat/Gr - Term. Anat.)</i></span><br />"
			termLatinTA=(termLatinTA!="")? termLatinTA : term; //for Google search
			report.synonyms+=term+" (Lat/Gr - Term. Anat.)\n" //for comment reporting
			}
		//LatinNonTA
		else if(isLatin(oTerm) && !isEponym(oTerm) && !isFromTA(oTerm)) 
			{strLatinNonTA+="<span id='"+elemId+"'  class='termLatin'>"+term+"&nbsp;<i>(Lat/Gr)</i></span><br />"
			termLatinNonTA=(termLatinNonTA!="")? termLatinNonTA : term; //for Google search
			report.synonyms+=term+" (Lat/Gr)\n" //for comment reporting
			}
		//EpnLatinTA
		else if(isLatin(oTerm) && isEponym(oTerm) && isFromTA(oTerm)) 
			{strEpnLatinTA+="<span id='"+elemId+"' class='termEponym'>"+term+"&nbsp;<i>(Eponym Lat/Gr - Term. Anat.)</i></span><br />"
			termEpnLatinTA=(termEpnLatinTA!="")? termEpnLatinTA : term; //for Google search
			report.synonyms+=term+" (Eponym Lat/Gr - Term. Anat.)\n" //for comment reporting
			}
		//EpnLatinNonTA
		else if(isLatin(oTerm) && isEponym(oTerm) && !isFromTA(oTerm)) 
			{strEpnLatinNonTA+="<span id='"+elemId+"' class='termEponym'>"+term+"&nbsp;<i>(Eponym Lat/Gr)</i></span><br />"
			termEpnLatinNonTA=(termEpnLatinNonTA!="")? termEpnLatinNonTA : term; //for Google search
			report.synonyms+=term+" (Eponym Lat/Gr - Term. Anat.)\n" //for comment reporting
			}
		//EpnEnglishTA			
		else if(isEnglish(oTerm) && isEponym(oTerm) && isFromTA(oTerm)) 
			{strEpnEnglishTA+="<span id='"+elemId+"' class='termEponym'>"+term+"&nbsp;<i>(Eponym Eng - Term. Anat.)</i></span><br />"
			termEpnEnglishTA=(termEpnEnglishTA!="")? termEpnEnglishTA : term; //for Google search
			report.synonyms+=term+" (Eponym Lat/Gr - Term. Anat.)\n" //for comment reporting
			}
		//EpnEnglishNonTA	
		else if(isEnglish(oTerm) && isEponym(oTerm) && !isFromTA(oTerm)) 
			{strEpnEnglishNonTA+="<span id='"+elemId+"' class='termEponym'>"+term+"&nbsp;<i>(Eponym Eng)</i></span><br />"
			termEpnEnglishNonTA=(termEpnEnglishNonTA!="")? termEpnEnglishNonTA : term; //for Google search
			report.synonyms+=term+" (Eponym Lat/Gr - Term. Anat.)\n" //for comment reporting
			}
		//EpnOtherLang
		else if(isEponym(oTerm)) 
			{arEpnOtherLang[arEpnOtherLang.length]=getCopyObject(oTerm);
			}
		//LatEngTA
		else if(isLatEngTA(oTerm)) 
			{strLatEngTA+="<span id='"+elemId+"' class='termEnglish'>"+term+"&nbsp;<i>(Term. Anat.)</i></span><br />"
			termLatEngTA=(termLatEngTA!="")? termLatEngTA : term; //for Google search
			report.synonyms+=term+" (Term. Anat.)\n" //for comment reporting
			}
			
		//EnglishTA
		else if(isEnglish(oTerm) && isFromTA(oTerm)) 
			{strEnglishTA+="<span id='"+elemId+"' class='termEnglish'>"+term+"&nbsp;<i>(Eng - Term. Anat.)</i></span><br />"
			termEnglishTA=(termEnglishTA!="")? termEnglishTA : term; //for Google search
			report.synonyms+=term+" (English - Term. Anat.)\n" //for comment reporting
			}
		//EnglishPrefName
		else if(isEnglish(oTerm) && isPrefName(oTerm)) 
			{strEnglishPrefName+="<span id='"+elemId+"' class='termEnglish'>"+term+"&nbsp;<i>(Eng)</i></span><br />"
			termEnglishPrefName=(termEnglishPrefName!="")? termEnglishPrefName : term; //for Google search
			report.synonyms+=term+" (English)\n" //for comment reporting
			}
		//English
		else if(isEnglish(oTerm)) 
			{strEnglish+="<span id='"+elemId+"' class='termEnglish'>"+term+"&nbsp;<i>(Eng)</i></span><br />"
			termEnglish=(termEnglish!="")? termEnglish : term; //for Google search
			report.synonyms+=term+" (English)\n" //for comment reporting
			}
		//Other Languages
		else
			{arOtherLang[arOtherLang.length]=getCopyObject(oTerm);
			}
		}//end for loop traversing terms
	
	//handle other languages - 
	//first sort on language alphabetically
	arEpnOtherLang=fSortOnLanguageOTerm(arEpnOtherLang);//sort eponyms
	arOtherLang=fSortOnLanguageOTerm(arOtherLang); //sort non-eponym terms

	//console.log("l. 1005, arOtherLang=",arOtherLang)
	//for each Eponym other language term...
	for (var i=0;i<arEpnOtherLang.length;i++)
		{//get the term info
		oTermOtherLang=arEpnOtherLang[i];
		term=oTermOtherLang["term"];
		termIndex=oTermOtherLang["termIndex"];
		elemId="term_"+termIndex; 
		lang=fGiveLanguageAbbrev(oTermOtherLang["language"]);
		strEpnOtherLang+="<span id='"+elemId+"' class='termEponym'>"+term+"&nbsp;<i>(Eponym "+lang+")</i></span><br />"
		report.synonyms+=term+" (Eponym "+lang+")\n" //for comment reporting
		}	
	
	//for each other language (non-eponym) term...
	for (var i=0;i<arOtherLang.length;i++)
		{//get the term info
		oTermOtherLang=arOtherLang[i];
		term=oTermOtherLang["term"];
		termIndex=oTermOtherLang["termIndex"];
		elemId="term_"+termIndex; 
		lang=fGiveLanguageAbbrev(oTermOtherLang["language"]);
		lang=(exists(lang))? "<i>("+lang+")</i>" : ""
		strOtherLang+="<span id='"+elemId+"' class='termOtherLanguage'>"+term+"&nbsp;"+lang+"</span><br />"
		report.synonyms+=term+" (Eponym "+lang+")\n" //for comment reporting
		}	
	
	

	function isLatin(oTerm)
		{return (oTerm["language"] && oTerm["language"].toLowerCase()=="latin")? true : false;}
	function isEnglish(oTerm)
		{return (oTerm["language"] && oTerm["language"].toLowerCase()=="english")? true : false;}		
	function isLatEngTA(oTerm)
		{return (oTerm["language"] && oTerm["language"].toLowerCase()=="latengta")? true : false;}
	function isUnknownLanguage(oTerm)
		{return (oTerm["language"] && oTerm["language"]=="")? true : false;}		
	function isEponym(oTerm)
		{return (oTerm["eponym"] && oTerm["eponym"].toLowerCase()=="1")? true : false;}
	function isFromTA(oTerm)
		{return (oTerm["fromTA"] && oTerm["fromTA"].toLowerCase()=="1")? true : false;}
	function isPrefName(oTerm)
		{return (oTerm["prefName"] && oTerm["prefName"].toLowerCase()=="1")? true : false;}
		
	function fSortOnLanguageOTerm(arContainingOTerms)
		{arContainingOTerms.sort(function(oTermA,oTermB)
				{return (oTermA["language"] >= oTermB["language"]);});
		return arContainingOTerms;
		}
		
		
	//select and store search term for Google search
	var termLatinSel= (termLatinTA!="")? termLatinTA : termLatinNonTA;	//termLatEngTA not used
	var termEpnEnglish= (termEpnEnglishTA!="")? termEpnEnglishTA : termEpnEnglishNonTA;
	var termEpnLatin= (termEpnLatinTA!="")? termEpnLatinTA : termEpnLatinNonTA;
	var termEpnSel= (termEpnEnglish!="")? termEpnEnglish : termEpnLatin;
	var termEnglishSel= (termEnglishPrefName!="")? termEnglishPrefName : ((termEnglishTA!="")? termEnglishTA : termEnglish);

	googleSearchTerms= (termEnglishSel!="")? termEnglishSel : ((termEpnSel!="")? termEpnSel : termLatinSel);

	//create whole string to be inserted in synonymsbox
	var returnString= strLatinTA+strLatinNonTA+"<br />";
	var epnString=strEpnLatinTA+strEpnLatinNonTA+strEpnEnglishTA+strEpnEnglishNonTA+strEpnOtherLang;
	returnString+=(epnString!="")? epnString+"<br />" : "";
	returnString+=strLatEngTA+strEnglishTA+strEnglishPrefName+strEnglish;
	returnString+=strOtherLang;
	returnString+="<div id='instrucInfoTerms'><img id='iconInfo' src="+loc["iconInfoWiki"]+" alt='Information'>&nbsp;"+text["infoMouseOverTerms"]+"</div>";
	
	//returnObject
	var o=new Object();
	o.synonymsHTML=returnString;
	o.arTerms=arTerms;
	o.arTermIndex=arTermIndex;
	o.arTermInfoHtml=arTermInfoHtml;
	return o;
	}

function fGiveLanguageAbbrev(language)
	{if(!exists(language)) {return;}
	var lang=language.toLowerCase();
	if(lang=="french") {return "Fra"}
	else if(lang=="german") {return "D"}
	else if(lang=="spanish") {return "Esp"}
	else if(lang=="dutch") {return "Nl"}
	else return language;
	}

function fCreateTermInfoHtml(oTerm)
	{var description=(oTerm["description"])? oTerm["description"] : "";
	var authorityString=(oTerm["authority"])? fExtractFromBrackets(oTerm["authority"]) : "";
	var authorString=(oTerm["author"])? fExtractFromBrackets(oTerm["author"]) : "";
	var history=(oTerm["history"])? oTerm["history"] : "";
	var status=(oTerm["status"])? oTerm["status"] : "";
	var amdAuthority=fSplitAuthrAuthyInfo(authorityString);
	var amdAuthor=fSplitAuthrAuthyInfo(authorString);
	 
	var descriptionHTML=(description!="")? "<div class='termInfoSectionHeader'>"+text["description"]+"</div><div class='description'>"+description+"</div><br />" : "";
	var authorityHTML=(amdAuthority.length!=0)? "<div class='termInfoSectionHeader'>"+text["source"]+"</div><div>"+fCreateAuthrAuthyHtml(amdAuthority)+"</div><br />" : "";
	var authorHTML=(amdAuthor.length!=0)? "<div class='termInfoSectionHeader'>"+text["contributor"]+"</div><div>"+fCreateAuthrAuthyHtml(amdAuthor)+"</div><br />" : "";
	var historyHTML=(history!="")? "<div class='termInfoSectionHeader'>"+text["history"]+ "</div><div class='history'>"+fReadHistory(history)+"</div><br />" : "";
	var statusHTML=(status!="")? "<div class='termInfoSectionHeader'>"+text["status"]+"</div><div class='status'>"+fStatusCodeToText(status)+"</div><br />" : "";
	
	return descriptionHTML+authorityHTML+authorHTML+historyHTML+statusHTML;
	}
	
function fExtractFromBrackets(string)
	{return string.substring(3,(string.length-3));	
	}	
	
//splits authority-info, author-info
function fSplitAuthrAuthyInfo(string)
	{var amdInfo=new Array(); //multi-dim array for info
	var arEntityInfoElems,arInfoElemsKeyValues;
	var key, value;
	//split into separate authors or authorities
	var arEntity=string.split("#&#");
	//for each author/ity...
	for (var i=0;i<arEntity.length;i++)
    	{amdInfo[i]=new Array(); //1st level: entry per indiv. author, authority
		//split info per author/ity in its info-elements, (i.e. publName, persName, publYear, publAuth, etc)
		arEntityInfoElems=arEntity[i].split("|&|");
		//for each info-element (i.e. publName, persName, publYear, publAuth, etc)...
		for (var j=0;j<arEntityInfoElems.length;j++)
        	{//split each info-element into its key and value
			arInfoElemsKeyValues=arEntityInfoElems[j].split(":is:");
			key=arInfoElemsKeyValues[0];
			value=arInfoElemsKeyValues[1];
			//store
			amdInfo[i][key]=value;
        	}//end loop entityyInfoElems		
    	}//end loop entities
	return amdInfo;
	}//end function fSplitAuthrAuthyInfo()

	
function fCreateAuthrAuthyHtml(amdInfo)
	{var o, TAID, htmlString="<ul class='termInfoList'>"
	//for each author/ity...
	for (var i=0;i<amdInfo.length;i++)
		{o=amdInfo[i]; //shorthand alias
		htmlString+="<li>";
		htmlString+=(o.publName)? "<span class='publName'>"+o.publName+"</span>&nbsp;" : "";
		htmlString+=(o.publAuth)? "<span class='publAuth'>"+o.publAuth+"</span>&nbsp;" : "";
		htmlString+=(o.publYear)? "<span class='publYear'>"+o.publYear+"</span>&nbsp;" : "";
		htmlString+=(o.publPblr)? "<span class='publPblr'>"+o.publPblr+"</span>&nbsp;" : "";
		if(o.TAID)
			{//replace # by , if more TAIDs present
			TAID=o.TAID.replace(/#/g,", ");
			report.synonyms+=TAID+" (TAID)\n" //for comment reporting
			htmlString+="<br /><span class='taId'>["+TAID+"]</span><br />";
			}
		htmlString+=(o.persName)? "<span class='persName'>"+o.persName+"</span>&nbsp;" : "";
		htmlString+=(o.persTtl)? "<span class='persTtl'>"+o.persTtl+"</span>&nbsp;" : "";	
		htmlString+=(o.persName)? "<br />" : "";	
		htmlString+=(o.persAff)? "<span class='persAff'>"+o.persAff+"</span>&nbsp;" : "";		
		htmlString+=(o.persCntry)? "<span class='persCntry'>"+o.persCntry+"</span>&nbsp;" : "";
		htmlString+="</li>"
		}
	//chop off last <br />
	htmlString=htmlString.substring(0,htmlString.length-6);
	htmlString+="</ul>";
	return htmlString;
	}
	
	
function fStatusCodeToText(status)
	{switch(status)
		{case "fmaReviewed":return text["statusFmaReviewed"];
		case "tbr":return text["statusTbr"];
		case "ok": return text["statusOk"];
		}
	}	
	
function fReadHistory(history)
	{var arHistory=history.split(",");
	var str="";
	for(var i=0;i<arHistory.length;i++)
    	{str+=fHistoryCodeToText(arHistory[i])+",";
    	}
	//trim trailing comma	
	return str.substring(0,str.length-1);;	
	}		

function fHistoryCodeToText(historyEntry)
	{switch(historyEntry)
		{case "fromFma":return text["unmodFromFma"];
		}
	}		
////////////////////////////////////////////////////////////////////////////////////////////
//diverse
	
//signals the use of the iFrame fallback when no XMLHttprequest present (IE6 with Active-X disabled)	
function signalUseFallBack()	
	{$('signal').style.visibility="visible";
	}

//hides resources pane
function hideResources()
	{h("resources")
	}

function searchImages()	
	{highlightSelector("linkImages");
	//var sites="site:http://www.bartleby.com/107/ OR site:http://www.anatomyatlases.org/"
	customSearch("imgForm","",googleSearchTerms)
	}

function searchDocuments()
	{highlightSelector("linkDocuments");
	customSearch("docForm","",googleSearchTerms)
	}
	
function searchAnatVariants()
	{highlightSelector("linkAnatVariants");
	customSearch("docForm","site:http://www.anatomyatlases.org/AnatomicVariants/",googleSearchTerms)
	}
		
function highlightSelector(whichId)
	{removeClass($("linkImages"),"activeGoogleSelector")
	removeClass($("linkDocuments"),"activeGoogleSelector")
	removeClass($("linkAnatVariants"),"activeGoogleSelector")
	addClass($(whichId),"activeGoogleSelector")
	}

function displayGoogleLinksBar()
	{
	//insert Google search term in Google links bar
	$("googleSearchTermDisplay").innerHTML= googleSearchTerms;
	s("resources");
	}	
	
function customSearch(formId,sites,subjects)	
	{
	sites=(exists(sites))? sites : "";
	var form=$(formId);
	var input=form.elements[0];
	input.setAttribute("value",subjects+" "+sites);
	
	$("googleSearchTermDisplay").innerHTML= googleSearchTerms;
	//Note: all remarks re. IFrame are obsolete since change to links bar and disabling of iFrame, due to Google not inside frame policy
	try{ //try because IE6 sec setting 'Navigate subframes across differ. domains' incorrectly throws an error when a google page is already loaded in iframe and hitting link (eg. documents or anatvariants) invokes new page targeted to the iframe.
		form.submit();
	}
	catch(e){//workaround for ie6: first load dummy page in frame, next (uncoupled via timeout) target the google page in the frame.
		/*
		window.frames["googleFrame"].location="dummy.htm";
		var reTry=enclose(formId,sites,subjects)
		function enclose(formId,sites,subjects)	
			{return (function() {customSearch(formId,sites,subjects)})}
		setTimeout(reTry,250);	
		*/	
		/*var userInfoDiv=document.getElementById("userInfoDiv"); //the userInfoDiv
		userInfoDiv.innerHTML=text["ie6iframeErrorInfo"]+counter;
		showUserInfoDiv();
		*/
		}	
	s("resources")
	}
	

/// GENERAL (SUPPORT) FUNCTIONS ///////////////////////////////////

function getKeyHit(e)
	{e=(e)? e : event;
	var key=e.keyCode;
	if(key==13) {return "Enter"}
	else if(key==38) {return "Up"}
	else if(key==40) {return "Down"}
	else if(key==27) {return "Esc"}
	
	/*
	   case 38: //Up arrow  
       case 40: //Down arrow 
       case 37: //left arrow 
       case 39: //right arrow 
       case 33: //page up  
       case 34: //page down  
       case 36: //home  
       case 35: //end                  
       case 13: //Enter  
       case 9: //tab  
       case 27: //esc  
       case 16: //shift  
       case 17: //ctrl  
       case 18: //alt  
       case 20: //caps lock 
       case 8: //backspace  
       case 46: //delete 

	*/
	}
	
	
function getSourceElement(evt) //
    {//W3C/NN: event=object passed to func/ IE: event= prop. of window
	var evt= (evt) ? evt : ((window.event)? window.event : null); 	
	if(evt)
		{// target=W3C/srcElement=IE
		return (evt.target)? evt.target : ((evt.srcElement)? evt.srcElement : null); 
		}
    }	

function getPositionElem(elemRef)
	{var cont= new Object();
	cont.left=0;
	cont.top=0;
	while(elemRef!=null)
		{cont.left+=elemRef.offsetLeft;
		cont.top+=elemRef.offsetTop;
		elemRef=elemRef.offsetParent;
		}
	//alert("left: "+cont.left+"\ntop: "+cont.top);		
	return cont;
	}



////////////////////////////////////////////////////////////////////////////////////	
////////////////////////////////////////////////////////////////////////////////////	
///debugging only
function informAjaxStatus(ajaxIndex)
	{var ajax=ajaxCalls[ajaxIndex];
	if(!ajax) {return;}
	if (ajax.responseStatus){
			var string = "<div>Status Code: " + ajax.responseStatus[0] + "</div><div>Status Message: " + ajax.responseStatus[1] + "</div><div>URLString Sent: " + ajax.URLString + "</div>";
		} else {
			var string = "<div>URLString Sent: " + ajax.URLString + "</div>";
		}
	return string
	}		

function showDebugInfo(e)
	{e= (e) ? e : ((window.event)? window.event : null); 
	//if(e.altKey==true) {alert("<b>Response</b><br />"+ajaxCalls[ajaxCalls.length-1].response)}
	if(e.altKey==false) {return;}  //protection against user accidentally getting debug info
	var ajaxIndex=ajaxCalls.length-1;
	if(ajaxCalls[ajaxIndex])
		{var string="<b>Response</b><br />"+ajaxCalls[ajaxIndex].response;
		string+="<br /><br /><b>Status</b><br />"+informAjaxStatus(ajaxIndex)+lastUrlStringSent;
		var debugWin=window.open("","debug");
		debugWin.document.open();
		debugWin.document.write(string);
		debugWin.document.close();
		}
	}

/*
var testSentLetters=new Array()
var testReceivedLetters=new Array()
var testTooLateReceivedLetters=new Array()
var testDisplayedLetters=new Array()

function reportMatches()
	{alert("testSentLetters=\n"+testSentLetters+"\n\ntestReceivedLetters= \n"+testReceivedLetters+"\n\ntestTooLateReceivedLetters= \n"+testTooLateReceivedLetters+"\n\ntestDisplayedLetters= \n"+testDisplayedLetters)
	}
*/
	
/*
//function that used matchInfo sent by server
function emphasizeFragments(word,matchInfo)
	{//matchInfo=e.g= "3-6,8-12,17-19"
	//Hltest+="<br /><u>matchstringfragments="+arrMatchStringFragments+", WORD="+word+"</u><br />"
	var htmlString="",charPointer=0;
	var spanStart="<span class='spanMatchText'>";
	var spanEnd="</span>";
	//info about individual matches
	var matches=matchInfo.split(",");
	var matchPositions,matchStart,matchEnd;
	for(var i=0;i<matches.length;i++)
    	{matchPositions=matches[i].split("-");
		matchStart=parseInt(matchPositions[0]);
		matchEnd=parseInt(matchPositions[1]);
//Hltest+="i"+i+", charPointer="+charPointer+", pre-part="+word.substring(charPointer,matchStart)+" , matchStart="+matchStart+", matchEnd="+matchEnd+",Highlight-part=<b>"+word.substring(matchStart,matchEnd)+"</b><br />"
		if(matchStart>charPointer)
			{htmlString+=word.substring(charPointer,matchStart);}
		htmlString+=spanStart+word.substring(matchStart,matchEnd)+spanEnd;	
		charPointer=matchEnd;
    	}
	if(charPointer<word.length)
		{htmlString+=word.substring(charPointer,word.length);
		//Hltest+="End-part="+word.substring(charPointer,word.length)
		}
	return htmlString;		
	}
*/	
/*function showHighLetters()
	{var ltr=window.open("","ltr");
	ltr.document.open();
	ltr.document.write(Hltest);
	ltr.document.close();
	
	}	
*/
	
