
/* Provide the major functionality behind Nestor
 * 
 * Author: Rajnish Bhaskar <raj@lordofthemoon.com>, 09 Feb 2008
 */

var NESTOR_VERSION= "1.0";

var gIsReady= [false , false, false, false, false];
var gSearchRequest;
var gCancelledRequest= false;

/* check if we have the "currentStyle" object available (by checking if it's present for the <html>
 * element) and if not, define it.  This gives us access to the computed style of any object, rather
 * than only if it's been explicitly set.
 */
if (!document.getElementsByTagName("html")[0].currentStyle)
{
	HTMLElement.prototype.__defineGetter__("currentStyle", function() {
		return document.defaultView.getComputedStyle(this, null);
	});
} // end if




function init()
{
	// load the school information
	
	new Ajax.Request("/nestor/Dispatcher.php?action=getAllSchools",
		{
			method: "post",
			onSuccess: loadSchools,
			onFailure: function(){alert("Unable to init (failure loading schools)");}
		});
		
	// load the subschool information
	new Ajax.Request("/nestor/Dispatcher.php?action=getAllSubschools",
		{
			method: "post",
			onSuccess: loadSubschools,
			onFailure: function(){alert("Unable to init (failure loading subschools)");}
		});
		
	// load the descriptor information
	new Ajax.Request("/nestor/Dispatcher.php?action=getAllDescriptors",
		{
			method: "post",
			onSuccess: loadDescriptors,
			onFailure: function(){alert("Unable to init (failure loading descriptors)");}
		});
		
	// load the levels
	new Ajax.Request("/nestor/Dispatcher.php?action=getAllLevels",
		{
			method: "post",
			onSuccess: loadLevels,
			onFailure: function(){alert("Unable to init (failure loading levels)");}
		});

	// load the classes
	new Ajax.Request("/nestor/Dispatcher.php?action=getAllSpellClasses",
		{
			method: "post",
			onSuccess: loadSpellClasses,
			onFailure: function(){alert("Unable to init (failure loading spell classes)");}
		});

	// load the autocompleter
	new Ajax.Autocompleter("search", "search_autocomplete_choices", "/nestor/Dispatcher.php?action=namesearch", 
		{minChars: 2, indicator: "autocompleteIndicator", callback: updateAutocompleteParams});

	// add the version
	if ($('nestorVersion')) $('nestorVersion').innerHTML= NESTOR_VERSION;
	
} // end function init()

/**
 * Takes a (full) spell name, gets it from the database, and displays it 
 */
function doSearch()
{
	$('pageoverlay').show();
	$('largespinner').show();
	gSearchRequest= new Ajax.Request("/nestor/Dispatcher.php",
		{
			method: "post",
			parameters: $('nestorForm').serialize() + "&action=spellsearch",
			onSuccess: loadSearch,
			onFailure: function() {alert("Unable to display spell " + $('search').value);},
			onException: searchRequestExceptionHandler
		});
} // end function doSearch()

function cancelRequest()
{
	gCancelledRequest= true;
	gSearchRequest.transport.abort();
	$('pageoverlay').hide();
	$('largespinner').hide();
} // end function cancelRequest()

function toggleSearchOptions()
{
	var searchOptionsBody= $('searchOptionsBodyContainer');
	
	if (searchOptionsBody.currentStyle.display == "none")
	{
		Effect.BlindDown('searchOptionsBodyContainer');
	}
	else
	{
		Effect.BlindUp('searchOptionsBodyContainer');
	}

} // end function toggleSearchOptions()

function toggleBlind(elementID)
{
	var element= $(elementID);
	if (element.currentStyle.display == "none") {Effect.BlindDown(elementID);}
	else {Effect.BlindUp(elementID);}
}

function resetAll()
{
	removeSpellResults();
	
	// reset the form elements
	var descriptors= document.getElementsByName("descriptorID[]");
	for (var i=0; i<descriptors.length; i++) {descriptors[i].checked= false;}

	$('magicSchool').options[0].selected= true;
	for (var i=1; i<$('magicSchool').options.length; i++) {$('magicSchool').options[i].selected= false;}

	$('magicSubschool').options[0].selected= true;
	for (var i=1; i<$('magicSubschool').options.length; i++) {$('magicSubschool').options[i].selected= false;}
	
	for (var i=0; i<$('level').options.length; i++) {$('level').options[i].selected= false;}
	for (var i=0; i<$('class').options.length; i++) {$('class').options[i].selected= false;}

	$('search').value= '';
} // end function resetAll()

// ==== Private functions ==== //


/** Callback function for autocompleter */
function updateAutocompleteParams(inputField, querystring)
{
	return $('nestorForm').serialize();
} // end function updateAutocompleteParams()


/* Checks that all the dynamic elements of the page have loaded and it is 
 * ready for user interaction
 */
function isPageReady()
{
	var readyToGo= true;
	for (var i=0; i<gIsReady.length; i++)
	{
		if (!gIsReady[i])
		{
			readyToGo= false;
		}
	} // end loop i
	
	if (readyToGo)
	{
		$('pageoverlay').hide();
		$('largespinner').hide();

		// and make the "cancel search" button visible
		$('cancelSearchLink').style.display= "inline";
	
	} // end if
	 
} // end function checkReadyState()


function loadSearch(transport)
{
	var spell= transport.responseText.evalJSON();
	 
	var spellContainer= $('searchResultsContainer');
	 
	// if we already have a spell loaded, remove it
	removeSpellResults();
	 
	var clearDiv= new Element("div", {"class": "clear"});
	if (spell != "")
	{
		if (spell.length > 1)
		{
			var rowCountDiv= new Element("div", {"id": "rowCount"}).update(spell.length + " spells found");
			spellContainer.appendChild(rowCountDiv);
		} // end if
	
		for (var x=0; x<spell.length; x++)
		{	
			var outerSpellDiv= new Element("div", {"class": "aSpell", "id": "spellID" + spell[x]["spellID"]});
			var spellBodyDiv= new Element("div", {"class": "spellBody", "id": "spellBodyID" + spell[x]["spellID"], "style": "display: " + (spell.length == 1 ? "block" : "none")});
			var spellNameRow= new Element("div", {"class": "spellNameRow"});
			var levelDiv= new Element("div", {"class": "spellLevelDiv"});
			
			var levelString= "";
			for (var levelClassName in spell[x]["level"])
			{
				levelString += levelClassName + ": " + spell[x]["level"][levelClassName] + "<br>";
			} // end loop
			levelDiv.update(levelString);
			
			var nameDiv= new Element("div", {"class": "spellNameDiv", "style": "cursor: pointer;", "onclick": "toggleBlind('spellBodyID" + spell[x]["spellID"] + "')"}).update(spell[x]["spellName"]);
			var bookDiv= new Element("div", {"class": "spellBookDiv"}).update(spell[x]["book"]);
	 		
			spellNameRow.appendChild(levelDiv); spellNameRow.appendChild(nameDiv); spellNameRow.appendChild(bookDiv);
			spellNameRow.appendChild(new Element("div", {"class": "clear"}));
			outerSpellDiv.appendChild(spellNameRow);
			outerSpellDiv.appendChild(clearDiv);
	 		
			var spellAttrsRow= new Element("div", {"class": "spellAttributesRow"});
			var schoolDiv= new Element("div", {"class": "spellAttribute even"});
			schoolDiv.appendChild(document.createTextNode("School: " ));
			var schoolAcr= new Element("acronym", {"title": spell[x]["school"]["schoolName"]}).update(spell[x]["school"]["shortSchoolName"]);
			schoolDiv.appendChild(schoolAcr);
			if (spell[x]["subschool"] != null)
			{
				schoolDiv.appendChild(document.createTextNode(" / "));
				var subschoolAcr= new Element("acronym", {"title": spell[x]["subschool"]["subschoolName"]}).update(spell[x]["subschool"]["shortSubschoolName"]);
				schoolDiv.appendChild(subschoolAcr);
			}
		
			// insert the descriptors
			var descriptorsDiv= new Element("div", {"class": "spellAttribute odd"});
			descriptorsDiv.appendChild(document.createTextNode("Descriptors: "));
			var descriptors= "";
			if (spell[x]["descriptors"] != "")
			{
				for (var i=0; i<spell[x]["descriptors"].length; i++)
				{
					descriptorsDiv.appendChild(new Element("acronym", {"title": spell[x]["descriptors"][i]["descriptorName"]}).update(spell[x]["descriptors"][i]["shortDescriptorName"]));
					if (i < spell[x]["descriptors"].length -1) descriptorsDiv.appendChild(document.createTextNode(", "));
				} // end loop i
			} // end if
			else descriptorsDiv.appendChild(document.createTextNode("none"));
		
			var componentsDiv= new Element("div", {"class": "spellAttribute even"}).update("Components: " + spell[x]["components"]);
			var castingTimeDiv= new Element("div", {"class": "spellAttribute odd"}).update("Casting time: " + spell[x]["castingTime"]);
			var rangeDiv= new Element("div", {"class": "spellAttribute even"}).update("Range: " + spell[x]["range"]);
			var durationDiv= new Element("div", {"class": "spellAttribute odd"}).update("Duration: " + spell[x]["duration"]);
			var savesDiv= new Element("div", {"class": "spellAttribute even"}).update("Saves: " + (spell[x]["saves"] == null ? "n/a" : spell[x]["saves"]));
			var srDDiv= new Element("div", {"class": "spellAttribute odd"});
			if (spell[x]["hasSR"] == "1")
			{
 				var sr= new Element("acronym", {"title": "Spell Resistance applies"}).update("SR applies");
 				srDDiv.appendChild(sr);
	 		} // end if
	 		srDDiv.appendChild(document.createTextNode(" "));
	 		if (spell[x]["isDispellable"] == "1")
	 		{
	 			var dis= new Element("acronym", {"title": "Spell is dispellable"}).update("(D)");
	 			srDDiv.appendChild(dis);
	 		} // end if
	 	
	 		spellAttrsRow.appendChild(schoolDiv); spellAttrsRow.appendChild(descriptorsDiv);
	 		spellAttrsRow.appendChild(componentsDiv); spellAttrsRow.appendChild(castingTimeDiv); spellAttrsRow.appendChild(rangeDiv);
	 		spellAttrsRow.appendChild(durationDiv); spellAttrsRow.appendChild(savesDiv); spellAttrsRow.appendChild(srDDiv);
	 		spellAttrsRow.appendChild(new Element("div", {"class": "clear"}));
	 		
		 	var spellTextDiv= new Element("div", {"class": "spellDescription"}).update(spell[x]["spellText"]);
	 	
		 	spellBodyDiv.appendChild(spellAttrsRow);
	 		spellBodyDiv.appendChild(spellTextDiv);
	 	
		 	outerSpellDiv.appendChild(spellBodyDiv);
		 	spellContainer.appendChild(outerSpellDiv);
	 		
		} // end loop x
	} // end if spells found
	$('pageoverlay').hide();
	$('largespinner').hide();
	 	
} // end function loadSearch()


function loadSchools(transport)
{
	var schoolData= transport.responseText.evalJSON();
	
	var schoolDropdown= $('magicSchool');
	for (var i=0; i<schoolData.length; i++)
	{
		var option= new Element("option", {"value": schoolData[i]["schoolID"]}).update(schoolData[i]["schoolName"]);
		schoolDropdown.appendChild(option);
	} // end loop i

	gIsReady[0]= true;
	isPageReady();
	
} // end function loadSchools()

function loadSubschools(transport)
{
	var subschoolData= transport.responseText.evalJSON();
	
	var subschoolDropdown= $('magicSubschool');
	for (var i=0; i<subschoolData.length; i++)
	{
		var option= new Element("option", {"value": subschoolData[i]["subschoolID"]}).update(subschoolData[i]["subschoolName"]);
		subschoolDropdown.appendChild(option);
	} // end loop i
	
	gIsReady[1]= true;
	isPageReady();
} // end fuunction loadSubschools()


function loadDescriptors(transport)
{
	var descriptorData= transport.responseText.evalJSON();
	
	var descriptorDiv= $('descriptorContainer');
	for (var i=0; i<descriptorData.length; i++)
	{
		var checkbox= new Element("input", {"type": "checkbox", "name": "descriptorID[]", 
			"value": descriptorData[i]["descriptorID"], "id": "descriptor" + descriptorData[i]["descriptorID"]});
		descriptorDiv.appendChild(checkbox);
		var label= new Element("label", {"for" : "descriptor" + descriptorData[i]["descriptorID"]}).update(descriptorData[i]["descriptorName"]);
		descriptorDiv.appendChild(label);
	} // end loop i
	
	gIsReady[2]= true;
	isPageReady();

} // end function loadDescriptors()


function loadLevels(transport)
{
	var levelData= transport.responseText.evalJSON();
	
	var levelDropdown= $('level');
	for (var i=0; i<levelData.length; i++)
	{
		var option= new Element("option", {"value": levelData[i]}).update("Level " + levelData[i]);
		levelDropdown.appendChild(option);
	} // end loop i
	
	gIsReady[3]= true;
	isPageReady();
} // end function loadLevels()


function loadSpellClasses(transport)
{
	var classData= transport.responseText.evalJSON();
	
	var classDropdown= $('class');
	for (var i=0; i<classData.length; i++)
	{
		var option= new Element("option", {"value": classData[i]["classID"]}).update(classData[i]["className"]);
		classDropdown.appendChild(option);
	} // end loop i
	
	gIsReady[4]= true;
	isPageReady();
	
} // end function loadSchools()

/**
 * @param requester: the Ajax.Request request object that made the request
 * @param exception: the exception that was thrown
 */
function searchRequestExceptionHandler(requester, aException)
{
	if (!gCancelledRequest) {
		alert("Unable to process request");
		throw aException;
	}
	else {gCancelRequest= false;}
	
	removeSpellResults(); 
	$('pageoverlay').hide();
	$('largespinner').hide();
} // end function searchRequestExceptionHandler

function removeSpellResults()
{
	// if we already have a spell loaded, remove it
	var spells= $$('div.aSpell');
	if (spells.length > 0) 
	{
		for (var i=0; i<spells.length; i++) {
			spells[i].remove();
		} // end loop i
	} // end if
	
	if ($('rowCount')) $('rowCount').remove();
	
} // end function removeSpellResults()


/**
 * Insert a new node into a dom after a given node
 *
 * @param existingNode -- the node to insert the new one after
 * @param newNode -- the new node to insert into the DOM
 */
function appendAfter(existingNode, newNode) 
{
	// If the existing node has a following sibling, insert the current
 	// node before it. otherwise appending it to the parent node
 	// will correctly place it just after the existing node.
	if (existingNode.nextSibling) // there is a next sibling. insert before it
	{
		existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
	} 
	else 
	{
		existingNode.parentNode.appendChild(newNode); // there is no next sibling, append to the end of the parent's node list
	} // end else

} // end function appendAfter()


/**
 * Cookie handling functions (createCookie, readCookie, eraseCookie), all taken from 
 * http://www.quirksmode.org/js/cookies.html
 */
function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/nestor";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}

