/*************************************************
*	Author: 	Matt Finholt
*	Company:	Seward Inc 
*	
*	Version:	1.0
*
*	Desctription: 	This set of functions is responsible for searching through
*					elements of a HTML page and checking the text against a 
*					predefined set of glossary terms. If a match is found, the
*					first instance of that text is made into a link that displays
*					a hidden layer filled with a definition upon mouse-over.
**************************************************/

//init page variables
var numSections, elementID, linkClassName, boxClassName, performSearch, docBody, pageEls;
var dispElement = document.createElement('div');
var termsUsed = new Array();


/*************************************************
*	Method: 		init()
*	Description:	initialize the variables and page elements
*					necessary for the glossary functionality
*************************************************/
function init(){
	
	//init the name of the elements to check within
	elementID = "smartGloss";
	linkClassName = "glossItem";
	boxClassName = "glossItemBox";
	
	//get the body of the current document
	docBody = document.body;
	docElement = document.documentElement;
	
	//ensure that the displayElement is hidden and has the ablsolute position attribute
	dispElement.className = boxClassName;
	dispElement.style.visibility='hidden';
	dispElement.style.position='absolute';
	docBody.appendChild(dispElement);
	
}//end function



/*************************************************
*	Method: 		doSearch()
*	Description:	This is the initial method thaqt is called to
*					intiate the search process
*************************************************/
function doSearch(){
	
	//init script vars
	init();
	
	//check to see if there are elements that need to be searched
	performSearch = checkDoSearch();
	
	//do the search if there are elements to be searched
	if(performSearch){
		
		//retrieve all the scanable page elements and loop through them
		pageEls = retElements();
		for(var i=0; i<pageEls.length; i++){
			scanNode(pageEls[i]);
		}//end for		
	}//end if	
}//end function


/*************************************************
*	Method: 		checkDoSearch()
*	Description:	Do a quick scan of the page and determine if there are any major elements
*					that need to be scanned for glossary terms
*************************************************/
function checkDoSearch(){
	//retrieve any searchable elements
	var searchElements = document.getElementsByName(elementID);
	
	//determine the number of elements to be searched
	if(searchElements!=null) numSearchElements=searchElements.length;
	else numSearchElements=0;
	
	//return true if there are searchable items
	if(numSearchElements>0){
		return true;
	}
	
	//return false is there aren't any searchable items
	return false;
}//end function


/*************************************************
*	Method: 		retElements()
*	Description:	Retrieve all the page element objects that are searchable and
*					return an array containing them.
*************************************************/
function retElements(){
	//retrieve all the page element objecvts that are searchable
	var elements = document.getElementsByName(elementID);
	
	//if the elements array is null, return an empty array to avoid a error being thrown
	if(elements==null)
		return new Array();
	
	//if the elements array is undefined, return an empty array to avoid an error being thrown
	if(typeof(elements.length)=="undefined"){
		var junk=new Array();
		junk[junk.length]=elements;
		return junk;
	}//end if
	
	//return the array of page element objects
	return elements;
}//end function


/*************************************************
*	Method: 		scanNode(cNode)
*	Description:	Scan through the current page element object and
*					determine if 1 or more glossary terms are present within
*					it.  If they are present, make it a link and set it up 
*					as a mouse-overable glossary term
*************************************************/
function scanNode(cNode){
	
	//init method variables and retrieve the html within the page element
	var body=cNode.innerHTML;
	var checkTerm;
	
	//loop through the list of glossary terms and check to see if the content contains any terms
	for(var f=0;f<gTerm.length;f++){
		
		//init the checkTerm value
		checkTerm = true;
		
		//loop througb the list of terms used on this page  and set the checkTerm flag to false if the term has already been used
		for(var g=0; g<termsUsed.length; g++){
			if(f == termsUsed[g]) checkTerm = false;
		}//end for
		
		//if the term hasn't already been used, search the node for it
		if(checkTerm){
			
			//init search vars
			var search1, search2, search3;
			var instanceEnd, instanceStart, matchLength;
			var leftCheck, rightCheck;
			var foundMatch = false;
			var reg1=new RegExp('\\b'+gTerm[f].replace(/\+/g,'\\+')+'(?:\x27s)?\\b','gim');
			var reg2=new RegExp('(</A>)|(<A )','i');
			var reg3=new RegExp('(</?[A-Z]+)|([^#]>)','i');
			
			//loop through all the items in the node that contain this particular glossary term
			while(((search1=reg1.exec(body))!=null) && (checkTerm)){
				
				//init foundmatch flag to true
				foundMatch = true;
				
				//determine the length of the matched string and it's location within the node
				instanceEnd = getLastInstance(reg1, search1);
				matchLength = search1[0].length;
				instanceStart = instanceEnd - matchLength;
				
				//check to ensure that the matched text string isn't inside an anchor tag. If it is, don't highlite the term
				if(search2=reg2.exec(body.substring(instanceEnd,instanceEnd+50))){
					if(search2[0]=='</A>'){
						foundMatch = false;
					}//end if
				}//end if
						
				//if the search hasn't run into a roadblock, check to ensure that the term isn't near any other HTML elements
				if(foundMatch){
					if(search3=reg3.exec(body.substring(instanceEnd-1,instanceEnd+50))){
						if(search3[0].substring(0,1)!='<'){
							foundMatch = false;
						}//end if
					}//end if
				}//end if
				
				//if the search hasn't run into a roadblock, check for any other special characters that can throw errors
				if(foundMatch){
					var leftCheck=body.substring(instanceEnd-matchLength-1,instanceEnd-matchLength);
					var rightCheck=body.substring(instanceEnd,instanceEnd+1);
										
					if((leftCheck=='-')||(leftCheck.charCodeAt(0)==92)||(leftCheck=='/')||(leftCheck=='.')||(rightCheck=='-'))
						foundMatch = false;
		
					if(leftCheck.charCodeAt(0)>127||rightCheck.charCodeAt(0)>127)
						foundMatch = false;
				}
				
				//if the search hasn't run into a roadblock, wrap the term with a link and add it to that page
				if(foundMatch){
					body = wrapTerm(body, instanceStart, instanceEnd, f);
					cNode.innerHTML = body;
					termsUsed[termsUsed.length] = f;
					checkTerm = false;
				}//end if				
			}//end while
		}//end if	
	}//end for
}//end function


/*************************************************
*	Method: 		getLastInstance(reg,search1)
*	Description:	return the character number of the end of the matched string
*					
*************************************************/
function getLastInstance(reg,search1){
	return(typeof(reg.lastIndex)=='undefined'?search1.lastIndex:reg.lastIndex);
}


/*************************************************
*	Method: 		wrapTerm(body, instanceStart, instanceEnd, termElementID)
*	Description:	Recieve the entire text node, with the start and end points of the 
*					linkable glossary term. Wrap the term in an anchor tag, place it
*					back into the text body and return that body
*************************************************/
function wrapTerm(body, instanceStart, instanceEnd, termElementID){
	//grab the term from the body of the text
	var linkText = body.substring(instanceStart, instanceEnd);
	var newLink = "";
	
	//create the newly wrapped term anchor
	newLink += '<a href="#" class="'+ linkClassName +'" target="_blank" onclick="openLink('+ termElementID +');return false;" onmouseover="showDef('+ termElementID +', this);"  onmouseout="hideDef('+ termElementID +', this);">';
	newLink += linkText;
	newLink += '</a>';
	
	//replace the old term in the text with the newly wrapped glossary term
	body = body.substr(0, (instanceStart)) + newLink + body.substr((instanceEnd), (body.length - (instanceEnd)));
	
	//return the content
	return body;
}//end function


/*************************************************
*	Method: 		openLink(termElementID)
*	Description:	This feature is not yet complete. It will allow for a link to be opened
*					when the user clicks on the term. If a link has been defined that is.
*************************************************/
function openLink(termElementID){
	if(gLink[termElementID] != null){
		alert(gLink[termElementID]);
	}//end if 
}//end function


/*************************************************
*	Method: 		showDef(termElementId, linkObj)
*	Description:	If a definition exists for the term, place the definition into the 
*					div and move it to it's proper location
*************************************************/
function showDef(termElementId, linkObj){
	if(gDef[termElementId] != null){
		dispElement.style.width=1000+"px";
//		dispElement.style.height=5+"px";
		dispElement.innerHTML = fillContent(termElementId);
		setLocation(linkObj);
		dispElement.style.visibility='visible';
	}//end if 	
}//end function


/*************************************************
*	Method: 		hideDef(termElementId, linkObj)
*	Description:	Rehide the definition layer upon mouse-off 
*					
*************************************************/
function hideDef(termElementId, linkObj){
	dispElement.innerHTML = "";
	dispElement.style.visibility='hidden';
}//end function


/*************************************************
*	Method: 		fillContent()
*	Description:	Place the glossary term and definition into the movable layer in an
*					aesthetically pleasing way
*************************************************/
function fillContent(termElementId){
	var boxContent = '';
	
	//The following code block is just another layout for presenting the term and content
	/*boxContent += '<table border="0" cellpadding="2" cellspacing="0"><tr>';
	boxContent += '<td class="glossItemBoxHeader" nowrap valign="top" align="left" style="BORDER-BOTTOM:black 1px solid; color:#999999;"> Glossary Term</td></tr>';
	boxContent += '<tr><td><table border="0">'
	boxContent += '<tr><td valign="top" align="left" class="glossItemBoxHeader" nowrap>'+ gTerm[termElementId] +': </td>';
	boxContent += '<td valign"top">'+ gDef[termElementId] +'</td></tr>';
	boxContent += '</table></td></tr>';
	boxContent += '</table>'; */
	
	boxContent += '<table border="0" cellpadding="2" cellspacing="0"><tr>';
	boxContent += '<td class="glossItemBoxHeader" nowrap valign="top" align="left" style="BORDER-BOTTOM:black 1px solid;"> '+ gTerm[termElementId] +'</td></tr>';
	boxContent += '<tr><td><table border="0">'
	boxContent += '<tr><td valign"top">'+ gDef[termElementId] +'</td></tr>';
	boxContent += '</table></td></tr>';
	boxContent += '</table>';
	
	return boxContent;	
}


/*************************************************
*	Method: 		setLocation()
*	Description:	Set the location of the definition display box relative to the
*					location of the terms loaction on the page
*************************************************/
function setLocation(linkObj){
	//init method links
	var linkX=0, linkY=0;
	var dispX=0, dispY=0;
	var parentObj = linkObj;
	
	//auto-resize the box to a specified ratio
	autoResize();
	
	//find the location of the link on the page
	while(parentObj){
		linkX+=parentObj.offsetLeft;
		linkY+=parentObj.offsetTop;
		parentObj=parentObj.offsetParent;
	}//end while
	
	//init the display box to the link x,y
	dispX = linkX;
	dispY = linkY;
	
	//if teh glossary term is too close to the top of the page or scrollable region, move the definition box below the term
	if((dispY - dispElement.clientHeight) <= docElement.scrollTop){
		dispY = (dispY + 30);
	} else {
		dispY = (dispY - dispElement.clientHeight);
	}//end if/else	
	
	//if the box definition box will display off the right side of the viewable content area, move it left till it displays completely onscreen
	if((dispX + dispElement.clientWidth) > docElement.clientWidth){
		dispX = ((docElement.clientWidth - dispElement.clientWidth)-20);
	} //end if
	
	//force the definition box to move to it's new location
	dispElement.style.left = dispX + "px";
	dispElement.style.top = dispY + "px";
		
}//end function


/*************************************************
*	Method: 		autoResize()
*	Description:	auto rezise the pop-up box to be a uniform height/width ratio
*					
*************************************************/
function autoResize(){	
	//auto size glossary box to best fit the content
	while(((parseInt(dispElement.style.width)/parseInt(dispElement.clientHeight))>1.95))
		dispElement.style.width = (parseInt(dispElement.style.width)-3) +"px";
}//end function



