/*        aFrame cell content stuffer and Ajax utility
  					 by R. Sutcliffe
  					Arjay Enterprises
  					
  				version 1.0    2007 03 26
  				version 1.1	2007 04 24 (arrange for a user callback parameter for aframes ) 
   An aFrame is a DOM element into which files can be framed.
   Unlike an iframe, the element does not have to have a fixed height.
   If the file is html, we use only the body, for faster loading.
   
   Ideas and even code snippets or logic from many products, though 
       there isn't much left of such borrowings here once additions and corrections were done.
       
   Dependencies: this is part of cellera, and depends on its css file
   	It does not however, depend on the rounded corners module.
   
  */ 

// if the data cells specified are empty put id in as content
// this can be a nice debugging tool.
// depends on a routine in getElementUtils
function putIdInEmptyCell(selector)
{
	var re = /^\s*$/
	var i;
	var v=getElementsBySelector(selector); // get all items designated by this selector
	var l=v.length;
	for(i=0;i<l;i++)
		{
			if (re.test(v[i].innerHTML)) v[i].innerHTML = v[i].id
		}
}

//allow the user to set a default URL

function setDefaultUrl (theUrl)
{
	defaultUrl = theUrl;
}

// handle simple ajax requests to fetch the result of the given url into the targeted id

// standard function that abstracts out the formation of the ajax request
function makeRequestObj ()
{
	if (window.XMLHttpRequest) { return new XMLHttpRequest(); } //  try the right way
	else
		{	
			var xmlhttpreq = null;
			try
				{xmlhttpreq = new ActiveXObject ("Msxml2.XMLHTTP")} // try the broken old MS 6 way
			catch (e)
				{ 
				try
					{xmlhttpreq = new ActiveXObject ("Microsoft.XMLHTTP")} // try the really broken older MS way
				catch (e) {}
				}
			return xmlhttpreq;
		}
}

// the actual ajax request function for a targetEl obj cell and a passed url (must be complete)
function aFramebyEl (targetEl, url, defUrl, usrCallback)
{
	var xreq = makeRequestObj ()
	if (xreq) // all  set up OK so first attach the function to display the results when the state changes
		{
		if (usrCallback) targetEl.usrCallback = usrCallback;
		xreq.onreadystatechange = 
			function () {displayCallBack(xreq, targetEl, url, defUrl)}
		// now having attached the display function for later, make the actual request
		try
			{xreq.open("GET", url, true);
			xreq.send(null);
			}
		catch (e)
			{if (embedErrorMsgs) targetEl.innerHTML=e;
			xreq.abort()
			}
		}
	else //  if (xreq) 
	if (embedErrorMsgs) {targetEl.innerHTML = cantCreateAjax} else return
}

function aFrameD (xreq, targetEl, defUrl) // request the default file after a failure to get the original one
{
	var url = (defUrl)? defUrl: defaultUrl; // override the global default URL if one is passed
	xreq.onreadystatechange =
		function () {displayCallBackD(xreq, targetEl, url)}
	// actual request
		try
			{xreq.open("GET", url, true);
			xreq.send(null);
			}
		catch (e)
			{if (embedErrorMsgs) targetEl.innerHTML=e;
			xreq.abort()
			}
}

// Next function is the base callback function for catching the returned data from the request and 
//		putting it into the cell object targetEl.
		
function displayCallBack(xreq, targetEl, url, defUrl)
{	var ok = false;
	var isHtml = url.indexOf ('.htm') > 0
	if (xreq.readyState == 4) // request completed
		{if (xreq.status == 200 || xreq.status == 0  ) // file OK || xreq.status == 302
			{
			ok = true;
			//if (xreq.status == 302) {debug (xreq.responseText)}
			var outMsg = (xreq.responseXML && xreq.responseXML.contentType=="text/xml") ? xreq.responseXML.getElementsByTagName("body")[0].textContent : (isHtml)? bodyOf(xreq.responseText):xreq.responseText;
			//debug (url+isHtml+"   "+outMsg)
			}
				// if first request fails, go for a default file if there is one
			else if (xreq.status == 404&&tryDefaultFile) {aFrameD (xreq, targetEl, defUrl); return}
			else if (embedErrorMsgs) {var outMsg = fileProb1+url+fileprob2 + xreq.status} else return//;debug (outMsg)
			
		} // if xreq
	
	else if (embedErrorMsgs)
		{var outMsg = ajaxCompErr}
	else return
	//targetEl.innerHTML = outMsg; // attach result to the item id
	textToInnerhtml (outMsg, targetEl);
	if (ok&& typeof (targetEl.usrCallback) =='function') targetEl.usrCallback(); //and initialize if the proc exists to do so
	if (ok && autoAdjustLocalLinks && pathDir(url).length>0)
	    adjustLocalLinksByDir (null, targetEl, url)
	  
	// the effect of this logic if embedded error messages are not enabled is to leave the original contents if there is an error and no default to read

}

// second version of this is to retry for the default file
function displayCallBackD(xreq, targetEl, url)
{	var ok = false;
	var isHtml = url.indexOf ('.htm') > 0
	if (xreq.readyState == 4)
		{if (xreq.status == 200 || xreq.status == 0)
			{
			ok = true;
			var outMsg = (xreq.responseXML && xreq.responseXML.contentType=="text/xml") ? xreq.responseXML.getElementsByTagName("body")[0].textContent : (isHtml)? bodyOf(xreq.responseText):xreq.responseText;
			}
			else if (embedErrorMsgs) {var outMsg = fileProb1+url+fileprob2 + xreq.status;} else return
		}
	else if (embedErrorMsgs)
		{var outMsg = ajayCompErr} else return
	//targetEl.innerHTML = outMsg; // attach result to the item id
	textToInnerhtml (outMsg, targetEl)
	if (ok&& typeof (targetEl.usrCallback) =='function') targetEl.usrCallback(); //and initialize if the proc exists
}

			// End aFrame basic apparatus
			
			
			// Start driver functions designed to control the above apparatus at a higher level
	// if a default url is supplied, a second effort will be made to fetch that one
	
// This version takes the request by item id and translates it into a request by object
function aFrame (targetId, url, defUrl, usrCallback)
{
	var targetEl = document.getElementById(targetId);
	aFramebyEl (targetEl, url, defUrl, usrCallback);
}

// This version takes the request by item id and translates it into a request by object
function aFrameQuery (targetId, url, searchBoxID, usrCallback)
{
	url += "?"+searchBoxID+"="+document.getElementById(searchBoxID).value
	var targetEl = document.getElementById(targetId);
	aFramebyEl (targetEl, url, "", usrCallback);
	return (false);
}


// Next driver sets up "auto content" for the selected cells by creating an url from the id of each
  // requres a function in cellera
function aFrameByDes (selector, defUrl, usrCallback)
{
	var v=getElementsBySelector(selector); // get all items designated by this selector
	var l=v.length;
		// and request file content for each one
		// by constructing a file name from the id and the default file suffix
	for(i=0;i<l;i++)
	{ 
		var targetId = v[i].id;
		var url = targetId+fileSuffix;
		aFramebyEl (v[i], url, defUrl, usrCallback);
	}
}


// Next driver sets up link selected content for the selected links (per their selector) into the cell specified 
  // requres a function in getElementUtils
  // if the optional parameter is set to "hover" the file is fetched on a hover instead of needing a click

function attachLinks2aFrame (selector, targetId, mode)
{	
	var targetEl = document.getElementById(targetId); // get the target cell as an obj
	targetEl.origContent = targetEl.innerHTML;
	var v=getElementsBySelector(selector); // get all links designated by this selector
	var l=v.length;
		// and set up the click action for each
	for(i=0;i<l;i++)
	{ 
		if (mode == "hover")
		{
			v[i].onmouseover = function () {aFramebyEl (targetEl, this.href); return false}
			v[i].onmouseout =  function () {targetEl.innerHTML= targetEl.origContent}
		}
	
	// set up the click action on all such links for future clicks
		else v[i].onclick = function () {aFramebyEl (targetEl, this.href); return false}
		 //addEventHandler (v[i], "click", function () {aFramebyEl (targetEl, this.href, defUrl, usrCallback); return false} )
	}	

}

		// load up the aFrame initial contents from the location string
// if the search string comes in as ?aFrameName=filename and if this aFrameName is the targetId, use that filename at once
// Since locus.js will return the directory name as the aFrameName, all you need is to have an id equal to the directory 
// and this file will be loaded
// purpose: allow search engines to see into those files, but when they're loaded we aFrame them
 
 function loadaFrameByLoc (targetId, defUrl, usrCallback)
{	var loc;
	var targetEl = document.getElementById(targetId); // get the target cell as an obj
	targetEl.origContent = targetEl.innerHTML;
	// now, we may have been passed a file to put into this target in the form of a search string
		// case 1: the search string is ?targetId=file for this particular targetId
			// put that file into the targetId, overriding what the setup told us
			loc = location.search.indexOf (targetId+"="); // see if relevant search string to this target
			//debug ("here")
			if (loc!= -1) {aFramebyEl (targetEl, location.search.slice(loc+targetId.length+1), defUrl, usrCallback); return false}

		// case 2: the search string is ?dir=file, where dir is the defAFrameFolder and the deftargetId is targetId
			// put that file into the defaultAframe
			loc = location.search.indexOf (defAFrameFolder+"="); // see if relevant search string	
			if (loc!= -1&&targetId==deftargetId) {aFramebyEl (targetEl, location.search.slice(loc+defAFrameFolder.length+1), defUrl, usrCallback); return false}
			
		// case 3: no search string, but there is a default file URL
			loc = location.pathname.lastIndexOf("/"); //debug (defUrl+"         "+location.pathname.slice(loc+1))
			//debug ("here1a     usrCallback="+usrCallback+"  its type =   "+typeof usrCallback + defUrl)
			if (defUrl) {aFramebyEl (targetEl, defUrl, null, usrCallback); return false}
			
	    // case 4: if none of the above, then the aframe gets, for now, whatever content it has defined in the html and css, but we do nothing here
	
}

							// PAGE SEQUENCES

/* These are aFrames populated by one of a sequence of files all with the same name but
	ending in an ordered sequence of numbers, such as filename0, filename1, etc.
   Ordinarily the first file to populate the aFrame will be passed in as a parameter, which also
    determines the sequence name. However, if someone navigates to a file directly, say
    from a search engine, a redirect is done bu locus.js using a search string, and we'll pop that file in the 
    sequence into the aFrame cell instead by parsing the search string "?seqname=n".
   see comments in attachLinks2aFrame for the details of catching a locus search string
	where seqname is derived from the starting file that has the form, say seqname4.html
	if we also passed in ?seqname=3, we would start with seqname3.html in the target aFrame.
	
	firstNum and lastNum define the (wrapping around) limits of the seqence of pages
	linkElType could be "A" if everything is attached to text links, but could also be "div", say.
		It could be left blank, in which case the selectors would be plain ids.
   a page sequence can be controlled in one of two ways
	1. set up elements whose selector is {seqname}First, {seqname}Next, {seqname}Last, {seqname}Prev
		These will get picked up here and bound to this apparatus.
	2. In any event the navigation arrow keys will page through the sequence, though the
		user will proibably have to be informed of this with a text message on the screen.
*/	

var aFrameSeqs = new Array();
var lastSeq; // for keyboard control only the last one can be used

function initaFrameSeq (startURL, firstNum, lastNum, targetId, linkElType, usrCallback)
{
	var re, seq, v, loc;
		// now  split the startURL into main pathname, a number for the sequence, and the file suffix
		// if the startURL is html/testfile2.htm the three parts become html/testfile  2  and .htm
	re = /^(.+)(\d+)(\.[a-zA-Z]*)$/ ;
	var uArray = re.exec(startURL); //debug (uArray)
	var path = uArray[1];
		// next split the path into its directory part and its filename part (without the number)
	var seqName = path.slice (path.lastIndexOf ('/')+1); 
		// create an object to hold all the data for this page sequence
	if (!aFrameSeqs [seqName]) aFrameSeqs [seqName] = new Object ()
		// and shorten its name for local use
	seq = aFrameSeqs [seqName];
		// now set the remaining attributes of this page sequence object
	seq.uPrefix = uArray[1];
	seq.uSuffix = uArray[3];
	seq.firstNum = firstNum; 
	seq.lastNum = lastNum;
		// poll any search string to see if the aFrameSeq name is there in the form seqName=n
		// and if so use n to define the initial page number for this sequence
	loc = location.search.indexOf (seqName+"=");
	if (loc!=-1) {seq.curNum = parseInt(location.search.slice (loc+seqName.length+1))}
	else seq.curNum = (uArray[2]!="")? parseInt (uArray[2]): 1; // parse off the number following and use it if there
			// set up the linkElement prefix for designators. Usually it will be "a."
	linkElPrefix = (linkElType)? linkElType+"." : "";
	seq.firstSel = linkElPrefix+seqName+"First"
	setupFirstPage (seq); //debug ("here")
	seq.lastSel = linkElPrefix+seqName+"Last";
	setupLastPage (seq);
	seq.nextSel = linkElPrefix+seqName+"Next";
	setupNextPage (seq);
	seq.prevSel = linkElPrefix+seqName+"Prev"
	setupPrevPage (seq);
	seq.linkSel = linkElPrefix+seqName+"Link";
	setupSeqLinks (seq);	
	seq.newPageMode = !targetId; // if no target, pages will be loaded in the window as independent units
	seq.targetId = targetId;
	seq.targetEl = document.getElementById (targetId);
	seq.usrCallback = usrCallback;

	loadCurPage (seq);
		// let the user control the pages by keys
		// we'll add a handler to any keydown handler already present, just in case
		// of course if a page has two sequences, this will run only the last one set up
	addOnkeydown (changeByKey);
	lastSeq = seq; // pass last one up to a global 
}

// function to add a keydown handler to any already there
function addOnkeydown (newFunc)
{	var oldOnKeyDown = document.onkeydown
	if (typeof oldOnKeyDown =="function")
		{document.onkeydown= function ()
				{if (oldOnKeyDown) oldOnKeyDown
				newFunc()
				}
		}
	else document.onkeydown = newFunc;
}

	// this function allows for any page sequences to be controlled by the arrow keys
function changeByKey (evt)
{	var seq = lastSeq;
	var ltArrow = 37;
	var upArrow = 38;
	var rtArrow = 39;
	var dnArrow = 40;
	evt = (evt) ? evt : (window.event) ? event : null;
		// following order is critical to ensure browsers get the right keycode
	var thisKey = (evt.keyCode) ? evt.keyCode :
                   ((evt.which) ? evt.which : 
                   (evt.charCode) ? evt.charCode : 0);
	if (thisKey == rtArrow) nextPage (seq)
	else if (thisKey == ltArrow) prevPage (seq)
	else if (thisKey == upArrow) firstPage (seq)
	else if (thisKey == dnArrow) lastPage (seq)
	return false
}

	// following functions set up actions for any controls having the appropriate classnames
	// as constructed above. This allows, for instance, two sets of controls, top and bottom of page.

function setupNextPage (seq) // e.g. control for going to next page in sequence
{
	var v=getElementsBySelector(seq.nextSel); // get all links designated by this selector
	var l=v.length; 
		// and set up the click action for each
	for(i=0;i<l;i++)
	{v[i].onclick = function () {nextPage (seq); return false}
	}
}

function setupPrevPage (seq)
{
	var v=getElementsBySelector(seq.prevSel); // get all links designated by this selector
	var l=v.length; 
		// and set up the click action for each
	for(i=0;i<l;i++)
	{v[i].onclick = function () {prevPage (seq); return false}
	}
}

function setupFirstPage (seq)
{
	var v=getElementsBySelector(seq.firstSel); // get all links designated by this selector
	var l=v.length; 
		// and set up the click action for each
	for(i=0;i<l;i++)
	{v[i].onclick = function () {firstPage (seq); return false}
	}
}

function setupLastPage (seq)
{
	var v=getElementsBySelector(seq.lastSel); // get all links designated by this selector
	var l=v.length; 
		// and set up the click action for each
	for(i=0;i<l;i++)
	{v[i].onclick = function () {lastPage (seq); return false}
	}
}

function setupSeqLinks (seq) // control for any links with the right class; action is use the link
{
	var v=getElementsBySelector(seq.linkSel); // get all links designated by this selector
	var l=v.length; 
		// and set up the click action for each
	for(i=0;i<l;i++)
	{
	v[i].onclick = function () {aFramebyEl (seq.targetEl, this.href); return false}//
	}
}

	// the next batch are the functions called by the handlers set up above
	// most manipulate the current sequence number and instruct the page load
function nextPage (seq)
{seq.curNum++;
loadCurPage (seq);
}

function prevPage (seq)
{seq.curNum--;
loadCurPage (seq);
}

function firstPage (seq)
{seq.curNum = seq.firstNum;
loadCurPage (seq);
}

function lastPage (seq)
{seq.curNum = seq.lastNum
loadCurPage (seq);
}

function loadCurPage (seq)
{	// first correct the page number if needed with a wraparound
	if (seq.curNum < seq.firstNum) seq.curNum = seq.lastNum;
	if (seq.curNum > seq.lastNum) seq.curNum = seq.firstNum;
	var url = seq.uPrefix+seq.curNum+seq.uSuffix; //debug (url)
	aFramebyEl (seq.targetEl, url, null, seq.usrCallback);
}

							//  END PAGE SEQUENCES SECTION
							
							
							// utils
function bodyOf (theText)
{ 
	if (theText.toLowerCase().indexOf ('body') == -1) return (theText)
	var re = /(<\s*body[^>]*>\s*)([\S\s]+)(<\s*\/body\s*>\s*)/
	var uArray = re.exec(theText); //debug (uArray)
	return (uArray[2]);
}

function textToInnerhtml (text, targetEl)
{
	// create a new span element to hold the stuff
	var newDiv = document.createElement ("div");
	// name it as a dependent of the container
	newId = targetEl.id+"-innerdiv";
	//give it the text/html passed in
	newDiv.innerHTML = text;
	newDiv.id = newId
	// remove child nodes from the container
	// note need to count backwards. Code on net from other sources is wrong.
	var len = targetEl.childNodes.length; 
		for (var i = len-1; i >=0 ; i--)
			{
				var node = targetEl.childNodes[i]
				node.parentNode.removeChild (node)
			}
	// finally drop in the new span as the child
	targetEl.appendChild(newDiv);
}