/*
Copyright (c) 2007, Goatstone. All rights reserved.
Code licensed under the BSD License:
http://goatstone.com/documentation/bsd/license.txt
version: .9
*/
/*
LiveSearch
Resides in /LiveSearch.js
An Object that has functionality to build a "live"/"active" search user interface, access a backend server to retreive results, formats and displays results.
Usage:
intstantiate
livesearch = new LiveSearch( targetDiv )
*/
function LiveSearch(args)
{
	this.pageIndexSelected=0
	this.inputPollerCount=0
	this.inputPollerIsRunning=false
	this.previousInputValue=''
	this.queryString  = ''
	this.requestURL = ''
	this.hostURL = location.protocol + '//'+ location.host + '/services/'
	this.targetDivId = args.targetDivId
	//	assign the object to LiveSearch so that the event handler will recoginze the this local propertie			
	this.domEl =  document.getElementById( args.targetDivId )
	this.aboutTextUrl = 'texts/about_live_search.txt'
	//	assign varables that will be used to build the UI									
	this.dataViewOptions = {  'title':'By Title', 'author':'By Author', 'about':'@' }
	this.dataViewOption = 'about'
	this.searchModes = [ 'title' , 'author' ]
	this.searchMode = this.searchModes[ 0 ]
	this.eventIteration = 0
	this.pageStart = 0
	this.currentItemStart = 0
	this.pageIdSelected = 0
	this.pageIndexStart = 0
	this.receivedDatum=[]
	//	draw the page
	this.draw()
	this.setFeedbackArea()
	document.getElementById('q').focus()
}
/*			startInputPoller		*/
LiveSearch.prototype.startInputPoller = function ()
{
	if(this.dataViewOption != 'title' && this.dataViewOption != 'author')this.dataViewOption='title'
	/*	wait a second before sending starting the poller, asuming the users will take at least a second to type anything relevant*/	
	if( this.inputPollerIsRunning != true )
	{
		this.inputPollerIsRunning = true
		this._runInputPoller()
	}
}
/*			stopInputPoller		*/
LiveSearch.prototype.stopInputPoller = function ()
{
	this.inputPollerIsRunning = false
}
/*			inputPoller		*/
LiveSearch.prototype._runInputPoller = function ()
{
	if( this.inputPollerIsRunning != true) return
	var runInputPollerInterval = 2000
	/* get the timer to work inside a class, http://www.bryceboe.com/2007/04/11/btimer-class-a-javascript-timer-class/ */
	var self=this
	setTimeout(function(){ self._runInputPoller() }, runInputPollerInterval )		
	this.inputPollerCount++
	/*	if the queryString is the same as pervious this.previousInputValue then stop:
	When the user has stoped typing for a period longer than the interval time then it indicateds that they have stopped typing */
	if( this.previousInputValue == this.queryString )
	{
		this.stopInputPoller()
	}
	//	if there are a certain amount of calls then stop, development limit
	if (this.inputPollerCount>=12)
	{
		this.stopInputPoller()
	}
	//set the input which will in turn set the queryURL
	this.setFeedbackArea()
	this.getSetBackendDatum()
	this.previousInputValue = this.queryString
}
/*			inputObserver		*/
LiveSearch.prototype.inputObserver = function (event)
{

	this.liveSearchObject.currentItemStart=0
	if(	this.liveSearchObject.dataViewOption == 'about'){
		document.getElementById( 'dataViewOptionabout').setAttribute ( 'href',  "#"  )
		document.getElementById( 'dataViewOptiontitle').removeAttribute ( 'href' )
	}
	var e = event || window.event
	var tempTarget = e.srcElement
	var queryInputValue = ( tempTarget )? e.srcElement.value : e.target.value	
	// 	trim this input
	this.liveSearchObject.queryString = queryInputValue.replace(/^\s+|\s+$/g, '') //tempQueryString
	/*	begin polling the input value		*/
	var self=this
	setTimeout(function(){ self.liveSearchObject.startInputPoller() }, 1000 )		
}
/*			optionsObserver	*/
LiveSearch.prototype.optionsObserver = optionsObserver
function optionsObserver(event)
{
	var e = event || window.event
	var tempTarget = e.srcElement
	var srcElement = ( tempTarget)? e.srcElement : e.target
	//	reset all the options
	for ( property in this.liveSearchObject.dataViewOptions ){
		document.getElementById( 'dataViewOption'+property).setAttribute ( 'href',  "#"  )
	}
	//	remove the anchor from the a tag
	srcElement.removeAttribute("href")
	//	user has selected the title or author option
	if( srcElement.id == 'dataViewOptiontitle' || srcElement.id == 'dataViewOptionauthor'){
		this.liveSearchObject.currentItemStart=0
		var searchModesKey = (srcElement.id == 'dataViewOptiontitle')? 'title' : 'author'
		this.liveSearchObject.searchMode = searchModesKey
		this.liveSearchObject.dataViewOption = searchModesKey
		//	call the backend and set the fields
		this.liveSearchObject.getSetBackendDatum()
		this.liveSearchObject.setFeedbackArea()
	}
	//	users has selected the about option
	else if(srcElement.id == 'dataViewOptionabout'){
		this.liveSearchObject.dataViewOption = 'about'
		this.liveSearchObject.getSetBackendDatum()
	}
	//	set the input which will in turn set the queryURL	
	document.getElementById('q').focus()
}
/*	 setSearchMode 	*/
LiveSearch.prototype.setRequestURL = function()
{
	this.requestURL = this.hostURL+'?'+ 'q='+this.queryString+ '&mode=' + this.searchMode +'&start=' + this.currentItemStart
}
/*	 getSetBackendDatum 	*/
LiveSearch.prototype.getSetBackendDatum = getSetBackendDatum
function getSetBackendDatum()
{
	//	set the varables that have been set with the UI with the function this.requestURL()
	this.setRequestURL()	
	// 	if title, switch mode, do search, show data  ::  if author set mode, do search, show data :: if about just show info
	if( (this.dataViewOption == 'title' || this.dataViewOption == 'author') && this.queryString != ''){
		var jklxml = new JKL.ParseXML.JSON( this.requestURL );
		var jsonObject = jklxml.parse();
		this.receivedDatum = jsonObject
		this.setNavigation()
		this.setDatumArea()
	}
	else if(this.dataViewOption == 'about' ){
		// 	set the search_data div with the information received  
		var http = new JKL.ParseXML.Text( this.aboutTextUrl );
		var text = http.parse();

		document.getElementById( 'search_data' ).innerHTML = text
		this.setNavigation()
		}
}
/*	 setDatumArea 		*/
LiveSearch.prototype.setDatumArea = setDatumArea
function setDatumArea( args )
{
		var jsonObject = this.receivedDatum 
		var datum = jsonObject.datum
		var datumHolderDiv = document.createElement('div')
		datumHolderDiv.className =  'datum_holder'
		for (var prop in datum){
			var tmpAnchor = document.createElement('a')
			//	use a regular expression to modify text color, use the qurey|query as is used on the server
			var tmpText = datum[ prop ].title
			var tmpRegExp = new RegExp("("+this.queryString+")","ig")
			tmpText = tmpText.replace(tmpRegExp,"<span style=\"color:orange\">$1</span>")
			tmpAnchor.innerHTML = tmpText 
			tmpAnchor.href = (this.searchMode=='title')?'http://www.gutenberg.org/etext/'+datum[ prop ].id:datum[ prop ].id
			tmpAnchor.target = 'blank'
			var tmpDiv = document.createElement('div')
			tmpDiv.className =  'data_row'
			tmpDiv.appendChild(tmpAnchor)
			datumHolderDiv.appendChild(tmpDiv)
		}
		document.getElementById( 'search_data' ).innerHTML = ''
		document.getElementById( 'search_data' ).appendChild(datumHolderDiv)
}
/*			navigationObserver		*/
LiveSearch.prototype.navigationObserver = navigationObserver
function navigationObserver(event)
{
	var pageNumber = ''
	var e = event || window.event
	//	is e.srcElement exist? then this is the IE target
	var tempTarget = e.srcElement
	//var srcElementId = ( tempTarget)? e.srcElement.id : e.target.id 
	var srcElement = ( tempTarget)? e.srcElement : e.target
	this.liveSearchObject.currentItemStart = srcElement.linkItemStart
	this.liveSearchObject.getSetBackendDatum()
	this.liveSearchObject.setFeedbackArea() 
	document.getElementById('q').focus()
}
/*	 setFeedbackArea 	*/
LiveSearch.prototype.setFeedbackArea = setFeedbackArea
function setFeedbackArea(arg)
{
	//document.getElementById( 'search_feedback' ).innerHTML = arg
}
/*      setNavigation   */
LiveSearch.prototype.setNavigation = setNavigation
function setNavigation()
{
	if( this.dataViewOption == 'about' )
	{
		document.getElementById( 'search_navigation' ).innerHTML = ''
		return
	}
	this.pageIndexSelected = ( this.receivedDatum.start / this.receivedDatum.perPage )
	var navigationHolder = document.createElement('span')
	var totalPages = Math.ceil(this.receivedDatum.count/this.receivedDatum.perPage)
	var maximumDelta = 11
	var delta = ( totalPages < maximumDelta )? totalPages : maximumDelta
	var navigationSymbol = ''
	var linkItemStart = 0
	var pageIdIndexEnd
	//	static pager: default 0 if it is a factor of 10 then jump the pager so that element is the first in the list
	if ( this.pageIndexSelected < 10 ){
		this.pageIndexStart = 0
	}
	//	jumping pager: if this.pageIndexSelected is greater than 10 jump selected page to middle the page is set not at the begining or at the end
	else if( this.pageIndexSelected >= 10  && ( (this.pageIndexSelected + 10) < totalPages )){
		 this.pageIndexStart = (this.pageIndexSelected-5)
	}
	//	static pager: set the page to the end where is will be static
	else {
		 this.pageIndexStart  = (totalPages - 10)
	}
	pageIdIndexEnd = ( this.pageIndexStart+delta>totalPages )? totalPages: this.pageIndexStart+delta
	/*	start to build the navigation links	*/
	if( this.pageIndexSelected != 0 ){
		navigationSymbol = '<'
		linkItemStart = ( this.currentItemStart - 10 )
		setNavigationLink( this,  true )
	}
	for ( var pageIdIndex = this.pageIndexStart ; pageIdIndex<pageIdIndexEnd ; pageIdIndex++ ){
		navigationSymbol =  (pageIdIndex + 1)
		linkItemStart =  ( pageIdIndex * this.receivedDatum.perPage)
		setNavigationLink( this,  (pageIdIndex != this.pageIndexSelected ) )
	}
	if( (this.pageIndexSelected+1) != totalPages &&  (totalPages!=0) ){
		navigationSymbol =  '>'
		linkItemStart = ( this.currentItemStart + 10 )
		setNavigationLink( this,  true )
	}
	/* 	setNavigationLink	*/
	function setNavigationLink( parentObj, isSelected ){
		var tempE = document.createElement('a')
		tempE.innerHTML = navigationSymbol
		if (isSelected == true)
		{
			tempE.linkItemStart = linkItemStart
			tempE.setAttribute( 'href',  "#"  )
			tempE.liveSearchObject = parentObj
			tempE.onclick = parentObj.navigationObserver
		}
		navigationHolder.appendChild( tempE )
		setFeedbackArea(navigationSymbol +":"+ pageIdIndex +  parentObj.hostURL )
	}
	var navigationMessage = document.createTextNode
	(
	this.receivedDatum.count  + ' items found, total Pages, '+totalPages+ ' for the query: "'+this.queryString  + '"'
	)
	var navigationMessageHolder = document.createElement('div')
	navigationMessageHolder.className = 'navigation_message_holder'
	navigationMessageHolder.appendChild( navigationMessage )
	//	set the div id =  search_navigation with the holder that was just built
	document.getElementById( 'search_navigation' ).innerHTML = ''
	document.getElementById( 'search_navigation' ).appendChild(navigationHolder)
	document.getElementById( 'search_navigation' ).appendChild(navigationMessageHolder)
}
/*	 draw 
build divs to display User Interface, Main Elements : navigation, user feed back / message box,  input form , data display initialize with short description  	*/
LiveSearch.prototype.draw = function ()
{
	var targetDiv = this.domEl
	/* build the document elements: holder, form,, input, form_holder, feedback, data */
	var form=document.createElement("form");
	form.className =  'search_form'
	var input = document.createElement("input");
	input.className =  'search_input'
	input.setAttribute ('size', 40 );
	input.setAttribute ('id', 'q' );
	input.setAttribute ('autocomplete', 'off' );	
	//		handle the events on the input field
	var feedback = document.createElement('span');
	feedback.className =  'search_feedback'
	feedback.id =  'search_feedback'
	var navigation = document.createElement('div');
	navigation.className = 'search_navigation'
	navigation.id = 'search_navigation'
	var data = document.createElement('div');
	data.id = 'search_data'
	data.className = 'search_data'
	//		anchors
	var options = document.createElement('div')
	var optionsText = document.createElement('span')
	optionsText.innerHTML = 'Search  '
	optionsText.className = 'search_options_text'
	options.className = 'search_options'
	for ( property in this.dataViewOptions ){
		var tempE = document.createElement('a');
		tempE.innerHTML = this.dataViewOptions[property];
		tempE.liveSearchObject=this
		tempE.onclick = optionsObserver
		tempE.setAttribute ('id', 'dataViewOption'+property);	
		if(property!=this.dataViewOption)tempE.setAttribute ( 'href',  "#"  )
		optionsText.appendChild( tempE )
	}
	//	start apending the elements together and to the targetDiv
	form.appendChild( input )
	//	set the targetDiv with the elements created
	options.appendChild( optionsText )
	options.appendChild(feedback)
	targetDiv.appendChild(options)	
	targetDiv.appendChild(form)
	targetDiv.appendChild(navigation)
	targetDiv.appendChild(data)
	input.focus()
	input.liveSearchObject=this
	input.onkeyup = this.inputObserver
	this.getSetBackendDatum()
}