var status = {};
var prefetchHeight = 250; // number of pixels above base of field that triggers a 'next page' request
var pageSize = 20; // default number of records per page

var ringInit = {};
var prefsInit = { // default settings for initial layout of screen
		columns: 4, 
		height: 200 
};

var ring = {};
var prefs = {};


//___________________________________________________________________________ 
// domLoaded functions
function domLoaded() {
	
	log.debug('domLoaded: start: \n');

	// Set the action for the form to the javascript one
	$('oscSearchForm').action = 'index.php/oscp/search.json';
	
	// Set the preferences div to display
	$('oscPreferences').style.display = 'block';


	//___________________________________________________________________________
	// check for cookie to initialise user interface
	jar = new CookieJar({   
			expires:360000,   // seconds    
			path: '/'  
	});     
  //jar.empty();
	
	// display all cookies for debugging
	allCookies = jar.get();
	if(allCookies) {
		log.debug('domLoaded: allCookies: contents: ' + Object.toJSON(allCookies));
	} else {
		log.debug('domLoaded: allCookies: contents: null');
	}

	ring = getStartupCookie('ring', ringInit)

	// reset any 'start's in ring
	for (var source in ring) { 
		log.debug('domLoaded: reset ring ' + source + ' start to 1');
		ring[source].start = 1;   
	}
	jar.put('ring', ring);  

	prefs = getStartupCookie('prefs', prefsInit)
	setColumnWidths(prefs['columns']);
	setColumnHeights(prefs['height']);

	var cookies = jar.getKeys();
	log.debug('domLoaded: cookies in jar: ' + cookies);
	// end cookie user interface setup

	//___________________________________________________________________________ 
	// searchButton observer
	$('oscSearchForm').observe('submit', searchButton.bindAsEventListener(this, 'searchForm'));
	// end searchButton observer

	//___________________________________________________________________________ 
	// nextPage observer
	//$('oscSearchForm').observe('click', nextPage.bindAsEventListener(this, 'nextPage'));
	// end nextPage observer

	//___________________________________________________________________________ 
	// columns selector observer
	$('columnsSelector').observe('click', columnsSelector.bindAsEventListener(this, 'columns'));
	// end columns selecto observer
	function columnsSelector(e, reg) {
		e.stop();
  	log.debug('columnsSelector: Event type: ' + e.type + ' from: ' + Event.element(e).id + ' (reg: ' + reg + ')');
		// define the percentage column widths to go with each number of columns. 
		// zero gets treated same as single column.
		var srcElement = Event.element(e);
		//showProperties(srcElement);
		log.debug('columnsSelector: value:' + srcElement.innerHTML);
		log.debug('columnsSelector: prefs: ' + Object.toJSON(prefs));

		if (!srcElement.innerHTML) { 
			return 
		} else {
			var columns = parseInt(srcElement.innerHTML);
			// store selection in cookie
			prefs['columns'] = columns;
			log.debug('columnsSelector: prefs now: ' + Object.toJSON(prefs));
			jar.put('prefs', prefs);  
			var testPrefs = jar.get('prefs');
			// check that cookie was saved, otherwise no preferences will work
			if (!testPrefs) {
				log.debug('domLoaded: WARNING: no cookie prefs saved.');
			}
			setColumnWidths(columns);
		}
	}
	
	//___________________________________________________________________________ 
	// setColumns
	function setColumnWidths(columns) {
		log.info('setColumnWidths: start: columns ' + columns );
		var columnInfo = [100,100,49.9,33.2,24.8]; 
		log.info('setColumnWidths: columnwidth:' + columnInfo[columns]);
		$$('.oscResultsList').each( function(list) {
			//log.debug('columnsSelector list'  + Object.toJSON(list.id));
			list.setStyle('width: '+columnInfo[columns]+'%;');
		});
	}
	
	//___________________________________________________________________________ 
	// heights selector observer
	$('heightsSelector').observe('click', heightsSelector.bindAsEventListener(this, 'heights'));
	// end heights selecto observer
	function heightsSelector(e, reg) {
		e.stop();
  	log.debug('heightsSelector: Event type: ' + e.type + ' from: ' + Event.element(e).id + ' (reg: ' + reg + ')');
		// define the percentage height widths to go with each number of heights. 
		// zero gets treated same as single height.
		var srcElement = Event.element(e);
		//showProperties(srcElement);
		log.info('heightsSelector: value:' + srcElement.innerHTML);
		if (!srcElement.innerHTML) { 
			return 
		} else {
			var strColumnHeight = srcElement.innerHTML;
			var curColumnHeight = srcElement.getStyle('height');
			log.debug('heightsSelector: curColumnHeight:' + curColumnHeight);
			var columnHeight = curColumnHeight;
			log.debug('heightsSelector: columnHeight:' + columnHeight);
			
			// store selection in cookie
			prefs['height'] = strColumnHeight;
			log.debug('heightsSelector: prefs now: ' + Object.toJSON(prefs));
			jar.put('prefs', prefs);  
			var testPrefs = jar.get('prefs');
			// check that cookie was saved, otherwise no preferences will work
			if (!testPrefs) {
				log.debug('domLoaded: WARNING: no cookie prefs saved.');
			}

			if (strColumnHeight == '+') {
				columnHeight = columnHeight +100;
			} else {
				columnHeight = parseInt(srcElement.innerHTML);
			}
			
			log.debug('heightsSelector: columnHeight:' + columnHeight);
			setColumnHeights(columnHeight);
		}
	}
	
	//___________________________________________________________________________ 
	// setColumnHeights
	function setColumnHeights(height) {
		log.debug('setColumnHeights: start: height ' + height );
		$$('.resultsContent').each( function(list) {
				list.setStyle('height: '+height+'px;');
		});
	}
	
	//___________________________________________________________________________ 
	// getStartupCookie
	// 
	function getStartupCookie(cookieName, cookieInit) {
		// try to get cookieName
		// if it doesn't exist, save cookieInit as cookie, then return cookieInit
		
		log.debug('getStartupCookie: '+cookieName );
		// check if cookie exists
		var cookie = jar.get(cookieName);
		if (cookie) {
			log.debug('getStartupCookie: jar.get: ' + Object.toJSON(cookie));
		} else {
			log.debug('getCookie: no cookie: ');
			// initialise and save cookie
			cookie = cookieInit;
			log.debug('getStartupCookie: set to: ' + Object.toJSON(cookie));
			jar.put(cookieName, cookieInit);  
			// confirm that cookie was saved ok
			testCookie = jar.get(cookieName);
			if (testCookie) {
				log.debug('getStartupCookie: test cookie: jar.get: contents: ' + Object.toJSON(testCookie));
			} else {
				log.debug('getStartupCookie: WARNING: cookie not saved correctly.');
				cookie = {};
			}
		}
		return cookie;	
	}




//___________________________________________________________________________ 
	// results lists
  Sortable.create('oscResultsLists', {
		tag: 'div', 
		only: 'oscResultsList', 
		constraint: false, 
		handle: 'widgetHandle',
		onUpdate: function(list) {
			//$('updateNotification').update(Sortable.serialize(list).escapeHTML());
			//$('updateNotification').highlight({ startcolor: '#99ff99' });

			// update the indexes of the servers in ring
			//log.debug ('Sortable resultsLists: onUpdate: list length: ' + showProperties(list, 'list'));
			//for (i=0; length)
			
			resultsList = $$('.oscResultsList');
			//log.debug('Sortable resultsLists: onUpdate: resultsList: ' + show(resultsList));
			//log.debug('Sortable resultsLists: onUpdate: resultsList[0].id: ' + resultsList[0].id);
			//var server = getServerFromResultsListName(resultsList[0].id);
			//log.debug('Sortable resultsLists: onUpdate: server: ' + server);
			log.debug('Sortable resultsLists: onUpdate: length: ' + resultsList.size());
			
			log.debug('onUpdate: ring: contents before: \n' + Object.toJSON(ring));
			for (i=0; i<resultsList.size(); i++) {
				var server = getServerFromResultsListName(resultsList[i].id);
				ring[server].index = i;
				log.debug('Sortable resultsLists: onUpdate: set server: ' + server + ' to index ' + i);
				ring[server].index = i;
						
			}
			// save current state of ring
			jar.put('ring', ring);  
			log.debug('onUpdate: ring: contents after: \n' + Object.toJSON(ring));
			ring = jar.get('ring');
			log.debug('onUpdate: ring: contents back from cookie: \n' + Object.toJSON(ring));
						
		}

	});


	// put cursor in query field
	Field.activate('oscQuery');
	
	// set the ring searchForm check buttons
	ring = setRingInfo(ring);
	log.debug('domLoaded: after setRingInfo: ring is now: \n' + Object.toJSON(ring)); 
	
	// save current state of ring
	jar.put('ring', ring);  

	log.debug('domLoaded: at end: ring is now: \n' + Object.toJSON(ring)); 

	$('oscServers').observe('click', toggleServer.bindAsEventListener(this, 'servers'));

	
} // end domLoaded



//___________________________________________________________________________ 
// Event listeners

document.observe('dom:loaded', domLoaded);

Ajax.Responders.register({
	onCreate: 
		function() { 
			$('oscSpinner').show(); 
			//log.debug('responder create: request count: ' + Ajax.activeRequestCount);
		},
	onComplete:
		function() {
			log.debug('responder complete: request count: ' + Ajax.activeRequestCount);
			if (Ajax.activeRequestCount == 0) {
				$('oscSpinner').hide();
				window.title = 'test';
				historySnapshot();
			}
		}
});

//___________________________________________________________________________ 
// Action functions

//___________________________________________________________________________ 
// searchButton processing
function searchButton(e, reg) {
	// process a search request
	showEvent(e, reg);
	e.stop();
	var $servers = $$('.server');

	var srcElement = Event.element(e);
	show(srcElement);
	log.debug('searchButton: value:' + srcElement + ":" + srcElement.serialize());
	
	$servers.findAll(function(server) {if (server.getValue()) {return true}}).each( function(server, index) {
		var $value = server.getValue();
		if ($value) {
			log.info('domLoaded: servers.findAll: Index ' + index + ':' + $value);
			// we have a selected server, add it to the next window
			var $source = $value;
			$('hdnSource').setValue($source);
			$('hdnStart').setValue('1');
			//$('hdnStep').setValue(stepIndex++);
			// generate random number for nocache field to stop IE from caching results
			var $random = Math.floor(10000 * Math.random());
			$('hdnNoCache').setValue($random);
			// reset source totalResults
			ring[$source].totalResults = 999999999;   
			log.debug('searchButton:onComplete:ring[$source].totalResults:'+ring[$source].totalResults);
			// save current state of ring
			jar.put('ring', ring);  

			
			var $searchForm = $('oscSearchForm');
			//log.debug ("searchForm:" + $searchForm.inspect());
			//log.debug ("searchFormSerialize:" + $searchForm.serialize());
			//log.debug ("action:" + $searchForm.action);
			
			new Ajax.Request($searchForm.action, { 
					method: 'get', 
					parameters: $searchForm.serialize(),
					onCreate: function() {
						$($source).update('searching...');
					},
					onComplete: function(response) {
						// response now comes back in JSON format
						log.debug('searchButton:onComplete:response:'+response.responseText);
						var responseObj = response.responseText.evalJSON(true);
						log.debug('searchButton:onComplete:responseObj:'+responseObj);
					  //log.debug('searchButton:onComplete:responseObj.source:'+responseObj.source);
					  //log.debug('searchButton:onComplete:responseObj.totalResults:'+responseObj.totalResults);
						// save the totalResults in ring cookie
					  //log.debug('searchButton:onComplete:ring[responseObj.source]:'+ring[responseObj.source]);
						ring[responseObj.source].totalResults = responseObj.totalResults;   
					  log.debug('searchButton:onComplete:ring[responseObj.source].totalResults:'+ring[responseObj.source].totalResults);
						// save current state of ring
						jar.put('ring', ring);  

						$($source).update(responseObj.response);
						//$($source).update(response.responseText);
						
						// update the title with the totalResults num
						updateTotalResults($($source), ring[responseObj.source].totalResults, responseObj.start+responseObj.count-1);

						// check if we have now reached end of results
						if (responseObj.start + responseObj.count > responseObj.totalResults) {
							// if so, we set the status to true, so that no more 'next page' events get triggered.
							log.debug ("searchButton: have now reached end of results: ");
							status[responseObj.source] = true;
						} else {
							status[responseObj.source] = false;
						}

						// change external links to open in new page/tab
						externalLinks();

					}
				}
			);
			
		}
	});
} // end searchButton

//___________________________________________________________________________ 
// nextPage processing
function nextPage(e, reg) {
	// process a search request
	//showEvent(e, reg);

	var srcElement = Event.element(e);
	//log.debug('srcElement:' + show(srcElement));
	//log.debug('nextPage: id:' + srcElement.id);
	
	// check if this is a nextPage link click
	var srcId = [];

	srcId = srcElement.id.splitOnUnderscore();
	//log.debug('srcId:' + show(srcId));
	var linkType = srcId[0];
	if (srcId[0] != 'nextPage') {
		//log.debug('nextPage: no');
		return;
	} else {
		log.debug('nextPage: yes');
		var $value = srcId[1];
		log.debug('value: ' + $value);
		e.stop();

		var $source = $value;
		
		// increment page number
		//var page++;
		//$('hdnSource').setValue($source);

		$('hdnSource').setValue($source);
		// generate random number for nocache field to stop IE from caching results
		var random = Math.floor(10000 * Math.random());
		$('hdnNoCache').setValue(random);
		
		var $searchForm = $('oscSearchForm');
		//log.debug ("searchForm:" + $searchForm.inspect());
		//log.debug ("searchFormSerialize:" + $searchForm.serialize());
		//log.debug ("action:" + $searchForm.action);
		
		new Ajax.Request($searchForm.action, { 
				method: 'get', 
				
				parameters: $searchForm.serialize(),
				onCreate: function() {
					//log.debug("request created:");
					//$($source).update('searching...').setStyle({ background: '#fff' });
				},
				onComplete: function(response) {
					// response comes back in JSON format with HTML results in the 'response' field
					//log.debug('nextPage:onComplete:response:'+response.responseText);
					var responseObj = response.responseText.evalJSON(true);
					log.debug('nextPage:onComplete:responseObj.step:'+responseObj.step);
					//log.debug('nextPage:onComplete:responseObj.totalResults:'+responseObj.totalResults);
					$($source).insert(response.response);
					//$($source).up().down()
				}
			}
		);

	}	
	
} // end nextPage




//___________________________________________________________________________ 
// scroll processing
function scroll(e, reg) {
	
	showEvent(e, reg);
	var resultsList = $(Event.element(e).id);
	//var server = $(getServerFromResultsListName(Event.element(e).id));
	var server = $(Event.element(e).id);
	log.debug('scroll: resultsList:' + resultsList.id + ': server: ' + server.id);

	log.debug('scroll: status: ' + status[resultsList.id]);
	log.debug(
		'scroll: '
	+ '\nserver.scrollTop: ' + server.scrollTop
	+ '\nserver.getHeight: ' + server.getHeight()
	+ '\nserver.scrollHeight: ' + server.scrollHeight
	);
	
	if ( !(status[resultsList.id]) 
			&& (server.scrollTop + server.getHeight() > server.scrollHeight - prefetchHeight)) {
		status[resultsList.id] = true;
		log.debug('\nevent triggered: ');
		nextPageRequest(server);
	}
	
}

//___________________________________________________________________________ 
// nextPage processing
function nextPageRequest(server) {

	log.info('nextPageRequest:' + server.id);
	
	var $source = server.id;
	$('hdnSource').setValue($source);

	log.debug ("nextPageRequest: start in ring : " + $source + ': ' + ring[$source].start);
	var start = parseInt(ring[$source].start);
	//log.debug ("nextPageRequest: start was: " + start);
	start = start + pageSize;
	log.debug ("nextPageRequest: start is now: " + start);	
	
	$('hdnStart').setValue(start);
	ring[$source].start = start;
	jar.put('ring', ring);  

	// generate random number for nocache field to stop IE from caching results
	var random = Math.floor(10000 * Math.random());
	$('hdnNoCache').setValue(random);
	
	var $searchForm = $('oscSearchForm');
	//log.debug ("searchForm:" + $searchForm.inspect());
	//log.debug ("searchFormSerialize:" + $searchForm.serialize());
	//log.debug ("action:" + $searchForm.action);
	
	new Ajax.Request($searchForm.action, { 
			method: 'get', 
			
			parameters: $searchForm.serialize(),
			onCreate: function() {
				//log.debug("request created:");
				//$($source).update('searching...').setStyle({ background: '#fff' });
			},
			onComplete: function(response) {
				// response now comes back in JSON format
				log.debug('nextPageRequest:onComplete:response:'+response.responseText);
				var responseObj = response.responseText.evalJSON(true);
				log.debug('nextPageRequest:onComplete:responseObj.step:'+responseObj.step);
				$($source).insert(responseObj.response);
				// update the title with the totalResults num
				updateTotalResults($($source), ring[responseObj.source].totalResults, responseObj.start+responseObj.count-1);
				var resultsList = 'results_' + $source;
				// check if we have now reached end of results
				if (responseObj.start + responseObj.count > responseObj.totalResults) {
					// if so, we leave the status set to true, so that no more 'next page' events get triggered.
					log.debug ("nextPageRequest: have now reached end of results: ");
					return;
				} else {
					status[$source] = false;
					log.debug('nextPageRequest:request completed: status resultsList reset');
				}
				// change external links to open in new page/tab
				externalLinks();
			}
		}
	);
	
	
} // end nextPageRequest



//___________________________________________________________________________ 
// toggleServer checkbox processing
function toggleServer(e, reg) {
	showEvent(e, reg);
	var srcElement = Event.element(e);
	//showProperties(srcElement);
	if (!srcElement.value) { return };
	log.info('toggleServer: value:' + srcElement.value + ': checked: ' + srcElement.checked + ': ring: ' + Object.toJSON(ring));
	
	var resultsList = 'results_' + srcElement.value;

	if (srcElement.checked) {
		$(resultsList).grow({direction: 'top-left'});
		//$(resultsList).show();
		ring[srcElement.value].visible = true;
	} else {
		$(resultsList).squish();
		//$(resultsList).fade({to: 0.3});
		//$(resultsList).hide();
		ring[srcElement.value].visible = false;
	}
	
	saveCookie(ring, 'ring');
	log.debug('toggleServer: ring cookie saved: contents: \n' + Object.toJSON(ring));

}

//___________________________________________________________________________ 
// saveCookie
function saveCookie(cookie, cookieName) {
	//log.debug('saveCookie: ' + cookieName + ': contents: \n' + Object.toJSON(cookie));
	jar.put(cookieName, cookie);  
	//var cookies = jar.getKeys();
	//log.debug('saveCookie: cookies in jar: \n' + Object.toJSON(cookies));
}


//___________________________________________________________________________ 
// getServerFromResultsListName
// resultsLists are named as 'results_serverx'
// this function extracts the 'serverx' part
function getServerFromResultsListName(name) {
	//log.debug('getServerFromResultsListName: name: ' + name);
	name = name.toString();
	var start = name.indexOf('_') +1;
	var server = name.substr(start);
	//log.debug('getServerFromResultsListName: server: ' + server);
	return server;
}

//___________________________________________________________________________ 
// setRingInfo
// use the 'ring' object to set the value of all search checkboxes,
// and the position and visibility of the resultsLists
function setRingInfo(ring) {

	var servers = $$('.server');
	log.debug('setRingInfo: start: servers contents:\n' + Object.toJSON(servers)); 
	var resultsList = $$('.oscResultsList');
	log.debug('setRingInfo: start: ring contents:\n' + Object.toJSON(ring)); 
	
	// check that all servers in ring cookie match all servers in server list
	// if not, reset the ring to null
	var numServers = $$('.server').size();
	var mismatch = false;
	$$('.resultsContent').each(function(item, index) {
		// check if server in ring
		if (this[item.id]) {
			log.debug('setRingInfo: check ring matches list: item: exists in ring'); 
		} else {
			log.debug('setRingInfo: check ring matches list: item ' + item.id + ' does not exist in ring');
			mismatch = true;
		}
	}, ring); // we have specified the context
	
	// if number of servers is less than that in ring, reset the ring to null
	var numServersInRing=0;
	for (var server in ring) {numServersInRing++;}
	log.debug('setRingInfo: servers number: ' + numServers + ': servers in ring: ' + numServersInRing);
	//log.debug('setRingInfo: servers number: ' + numServers + ': servers: \n' + Object.toJSON(servers) + ': servers in ring: ' + numServersInRing + ': ring: \n' + Object.toJSON(ring));
	if (numServers != numServersInRing) {
		mismatch = true;
	}
	
	if (mismatch) {
			log.debug('setRingInfo: lists do not match, reset ring'); 
			ring = {};
	}

	// update ring to include extra servers, if any
	log.debug('setRingInfo: extend ring ');
	var i=0;
	$$('.resultsContent').each(function(item, index) {
		// check if server already in ring
		log.debug('setRingInfo: item: ' + item.id + ': this item: ' + Object.toJSON(this[item.id])); 
		if (this[item.id]) {
			log.debug('setRingInfo: item: exists in ring'); 
		} else {
			log.debug('setRingInfo: item ' + item.id + ' does not exist in ring');
			log.debug('setRingInfo: ring before:\n' + Object.toJSON(ring)); 
			// add it to ring
			this[item.id] = {};
			this[item.id].visible = true;
			this[item.id].index = i;
			this[item.id].start = 1;
			log.debug('setRingInfo: ring after:\n' + Object.toJSON(ring)); 
		}
		i++;
	}, ring); // we have specified the context
	log.debug('setRingInfo: after extension: ring is now:\n' + Object.toJSON(ring)); 


	// for each server in the ring, set the check-box, results visibility, etc
	i=0;
	for (var server in ring) { 
		log.debug("Initialize server: "+ server);   
		log.debug("ring["+ server +"].index: " + ring[server].index);   
		log.debug("ring["+ server +"].visible: " + ring[server].visible);

		// get the index of the current ring
		var resultsListIndex = ring[server].index;
		// get the visibility of current ring
		var visible = ring[server].visible;
		
		// set the results list container name
		var resultsListContainer = 'results_' + server;
		
		var curResultsList = resultsList[resultsListIndex];
				
		// change the id's and contents of the elements in resultsList[resultsListIndex] to match the current server details
		// resultsList container div is named as 'results_servername'
		curResultsList.id = resultsListContainer;
		//resultsList[resultsListIndex].id = server;
		
		log.debug('id before:'+curResultsList.down('.resultsContent').id);
		curResultsList.down('.resultsContent').id = server;
		log.debug('id after:'+curResultsList.down('.resultsContent').id);
		
		log.debug('title before:'+curResultsList.down('.searchTitle').inspect());
		curResultsList.down('.searchTitle').update(server + "&nbsp;<span class='resultsNum'></span>");
		log.debug('title after:'+curResultsList.down('.searchTitle').inspect());
		
		//curResultsList.down('.resultsContent').update('Results for '+server+' will appear here...');
		
		//curResultsList.down('.nextPage').id = 'nextPage_' + server;
		
		// reset the processing status for each server
		log.debug('init status for server ' + curResultsList.id);
		status[server] = false;

		if (visible) {
			log.debug(curResultsList.id + ' need to be shown');
			servers[i].checked = true;
			curResultsList.show();
		} else {
			log.debug(curResultsList.id + ' need to be hidden');
			servers[i].checked = false;
			curResultsList.hide();
		}
		
		// register a scroll observer for this list
		//$(resultsListContainer).observe('scroll', scroll.bindAsEventListener(this, 'scroll'));
		$(server).observe('scroll', scroll.bindAsEventListener(this, 'scroll'));

		
		
	i++;
	}
	log.debug('setRingInfo: at end: ring is now:\n' + Object.toJSON(ring)); 
	log.debug('setRingInfo: at end: status is now:\n' + Object.toJSON(status)); 
	
	return ring;
	
}

//___________________________________________________________________________ 
// updateTotalResults
// update a results Title with the totalResults number
// param: column, the results column element
// param: totalResults, the number of results in search
// param: curPos, the number of records currently in results
function updateTotalResults(column, totalResults, curPos) {
	if (curPos > totalResults) {curPos=totalResults};
	var resultsNum = column.up().down('.resultsNum');
	log.debug('updateTotalResults:start:column:'+column.up().down('.resultsNum').inspect());
	resultsNum.innerHTML = ' (1-' + curPos + ' of ' + totalResults + ')';
	log.debug('updateTotalResults:resultsNum:after:' + resultsNum.innerHTML);

}


//___________________________________________________________________________ 
// getServerIndex
// scan the ring to find the server's index
function getServerIndex(serverId) {
	//log.debug('getServerIndex: serverId: ' + serverId); 
	//log.debug('getServerIndex: ring: ' + Object.toJSON(ring)); 

	for (var server in ring) { 
		//log.debug("server: "+ Object.toJSON(server));
		if (server == serverId) {
			//log.debug("server found: "+ serverId + ': index: ' + ring[server].index);
			return ring[server].index;
		}
	}
	return -1;

}


//___________________________________________________________________________ 
// protoDump
function protoDump(obj) {
	//showProperties(obj, 'test')
	//$(obj).each( function (item, index) {
	//	console.log ('index:'+index+':item:' + item); 
	//});

}


//___________________________________________________________________________ 
// showProperties
function showProperties(o, name) {
	var props = '';
	for (x in o) {
        props += x + "; " 
	}
	log.debug("Properties of " + name + ":   \n" + props);
	log.debug(props);
}

//___________________________________________________________________________ 
// show
function show(o, name) {
	var props = '';
	for (x in o) {
				var valType = typeof x;
        props += '.' + x + ": type:" + valType + " = " + o[x] + "\n";
	}
	return ("Contents of " + name + ': ' + props);
}


//___________________________________________________________________________ 
// showEvent
function showEvent(e, reg) {
  log.debug('Event type: ' + e.type + ' from: ' + Event.element(e).id + ' (reg: ' + reg + ')');
}


//___________________________________________________________________________ 
// splitOnUnderscore
String.prototype.splitOnUnderscore = String.prototype.split.curry('_');

//___________________________________________________________________________ 
// withinViewport processing
function withinViewport(el) {
	log.debug('withinViewport: start: ');
	var elOffset = el.cumulativeOffset();
	var vpOffset = document.viewport.getScrollOffsets();
	var elDim = el.getDimensions();
	var vpDim = document.viewport.getDimensions();
	log.debug('withinViewport?: elOffset:'+ elOffset[1] + '+ elDim: ' + elDim.height + ':vs vpOffset: '+ vpOffset[1] + ': vpDim: ' + vpDim.height );
	if (elOffset[1] + elDim.height < vpOffset[1] || elOffset[1] > vpOffset[1] + vpDim.height )
		//elOffset[0] + elDim.width < vpOffset[0]  || elOffset[0] > vpOffset[0] + vpDim.width) 
		{
		return false;
	}
	return true;
}

function getNewRing() {
	// resultsList divs have been initialised by the server list in PHP
	// so use this as the base to create a new ring object
	log.debug('getNewRing: start:');
	var servers = $$('resultsList');
	servers.each( function(server, index) {
		var $value = server.getValue();
		log.debug('getNewRing: server: index: ' + index + ': value:' + $value);
	});
}

var newRing = {
};


//___________________________________________________________________________ 
// end of main Javascript

//___________________________________________________________________________ 
// logger setup
	// Create the logger
	var log = log4javascript.getLogger('OpenSearchClient'); 

	// Create a PopUpAppender with default options
	//var popUpAppender = new log4javascript.PopUpAppender();
	// Change the desired configuration options
	//popUpAppender.setFocusPopUp(true);
	//popUpAppender.setNewestMessageAtTop(true);
	// Add the appender to the logger
	//log.addAppender(popUpAppender);
	
	// Create an AjaxAppender with default options
	var ajaxAppender = new log4javascript.AjaxAppender(htmlBase + "/log4js");
	var log4jsLevelObj = eval(log4jsLevel);
	ajaxAppender.setThreshold(log4jsLevelObj);
	log.addAppender(ajaxAppender);
	
	log.info("oscp/index.ctp started: ");

var stepIndex = 1;

window.onload = function() {
	//initialize our DHTML history
	dhtmlHistory.initialize();
	log.debug("dhtmlHistory initialized");

	//subscribe to DHTML history change events
	dhtmlHistory.addListener(historyChange);
	log.debug("dhtmlHistory listener created");
	
	// change external links to open in new page/tab
	externalLinks();
};

//___________________________________________________________________________ 
// searchForm event handler to add searchForm history change events
historySnapshot = function() {
	log.debug('historySnapshot:start:');
	var stepIndex = $('hdnStep').getValue();
	stepIndex++;
	var key = 's_' + stepIndex;
	$('hdnStep').value = stepIndex;
	var status = {};
	status.searchForm = $('oscSearchForm').serialize(true);

	// get results windows
	var $servers = $$('.server');
	status['results'] = {};
	status['resultsTitle'] = {};
	$servers.findAll(function(server) {if (server.getValue()) {return true}}).each( function(server, index) {
		// save the complete results column, including the title and number of results.
		var results = server.getValue();
		log.debug('historySnapshot:servers:each:results:' + $(results).inspect());
		//log.debug('historySnapshot:servers:each:results window:' + $($results).innerHTML);
		// save the results content
		status['results'][results] = $(results).innerHTML;

		// save the results title including numbers
		var resultsTitle = $(results).up().down('.searchTitle');
		log.debug('historySnapshot:servers:each:resultsTitle:' + resultsTitle.inspect());
		log.debug('historySnapshot:servers:each:resultsTitle.innerHTML:' + resultsTitle.innerHTML);
		status['resultsTitle'][results] = resultsTitle.innerHTML;
	});

	dhtmlHistory.add(key, status);
}

//___________________________________________________________________________ 
// Callback function to process history change events
// This is called whenever the user hits 'back', 'forward' or returns to the page
// from another site, or enters the url with #anchor manually.
// Reset the form fields and results list to the values they were then.
function historyChange(newLocation, historyData) {
	log.debug('historyChange:start:newLocation: ' + newLocation + ' :historyData:' + Object.toJSON(historyData));
	var results = '';
	if (historyData == null) {
		log.debug('historyChange:historyData:null:');
		// reset fields and results
		//$('oscQuery').value = '';
		// clear out results fields
		$$('.server').findAll(function(server) {if (server.getValue()) {return true}}).each( function(server, index) {
			var results = server.getValue();
			$(results).update('');
			// set Title
			var resultsNum = $(results).up().down('.resultsNum');
			log.debug('historyChange:historyData:resultsNum.innerHTML:' + resultsNum.innerHTML);
			$(resultsNum).update('');
			log.debug('historyChange:historyData:resultsNum.innerHTML:after:' + resultsNum.innerHTML);
		});
	} else if (typeof historyData == "object" && historyData != null) {
		log.debug('historyChange:historyData:isObject:' + Object.toJSON(historyData));
		var searchForm = historyData["searchForm"];
		log.debug('historyChange:searchForm: ' + Object.toJSON(searchForm));
		$('oscQuery').value = searchForm["q"];
		
		// put results into appropriate results column
		var results = $H(historyData.results);
		//log.debug('historyChange:results: ' + Object.toJSON(results));
		results.each(function(pair) {
			log.debug('historyChange:results:key:' + pair[0] + ': value: ' + pair[1]);
			var key = pair[0];
			var value =  pair[1];
			$(key).update(value);
		});		
		// put resultTitles into appropriate results column
		var resultsTitles = $H(historyData.resultsTitle);
		log.debug('historyChange:resultsTitles: ' + Object.toJSON(resultsTitles));
		resultsTitles.each(function(pair) {
			log.debug('historyChange:resultsTitles:key:' + pair[0] + ': value: ' + pair[1]);
			var key = pair[0];
			var value =  pair[1];
			var resultsTitle = $(key).up().down('.searchTitle');
			resultsTitle.update(value);
		});		
	} else {
		log.debug('historyChange:historyData:string:' + historyData);
		//results = historyData;
	}
	//log.debug('historyChange:newLocation: ' + newLocation + ' :historyData:' + results);
		
	//var msg = "History change occured: | newLocation=" + newLocation + " | historyData=" + results + " |";
	//log.debug(msg);
};



/*instantiate our history object*/
window.dhtmlHistory.create({
	debugMode: false,
	toJSON: function(o) {
		return Object.toJSON(o);
	}, 
	fromJSON: function(s) {
			return s.evalJSON();
	}
});


//___________________________________________________________________________ 
// change cake field name to id
function idifyName(name) {
	log.debug('idifyName:start:name:' + name);
	var selector = 'name=['+name;
	name = name.replace(/^data/g, "");
	name = name.replace(/\[\]/g, "");
	name = name.replace(/\]\[/g, "-");
	name = name.replace(/\[/g, "");
	name = name.replace(/\]/g, "");
	name = name.replace(/_/g, "-");
	name = name.camelize(name);
	log.debug('idifyName:name:' + name);
	return name;
}

//___________________________________________________________________________ 
// change external links to go to new page
function externalLinks() {
	$$('a([href^="#"])').each(function(anchor){anchor.target = "_self";});
	$$('a:not([href^="#"])').each(function(anchor){anchor.target = "_blank";});
}



