/*        aFrame cell content stuffer and Ajax utility
  					 by R. Sutcliffe
  					Arjay Enterprises
  					
  				version 1.0    2007 01 31 
   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.
   
   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 utilities and css file
   	It does not however, depand on the rounded corners module.
   
  */
var embedErrorMsgs = true; // if set to false, no error messages displayed in id'd box for content
 

// if the data cells specified are empty put id in as content
// this can be a nice debugging tool.
// depends on a routine in cellera
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
		}
}

// flesh out a partial url for requests
  // if the passed url already starts with http or we're running on the production site, do nothing
  // if running locally
   // first strip any local machine directory for using local server instead
   // But if that's not the reason we have a file protocol, just leave it
   // then modify to the test site or the full production site per above flag
function fullUrl(url) // requires locus file 
{ 	//if it's a full url and a file in the local directory, slice off the local directory down to the path, skipping the following step
if (url.slice (0,4)== 'file'&&url.indexOf (localMachineDirectory)==0 ) url = url.slice (localMachineDirectory.length)
	// otherwise if it's some other file or is already an http url, leave it alone
if (url.slice (0,4)== 'http' || location.protocol == 'http:' ||url.slice (0,4)== 'file') return url
	// we get here with a stripped file url; now have to make the siteURL
	// the messy jiggery pokery below to slice the local machine part out of the location is because
	// not all browsers return the same thing for a location.
re = /^.+:\/+(.+)$/
var lmdArray = re.exec (localMachineDirectory); //debug (lmdArray[1]) // lArray[1] has path with no slash at the beginning
var barePath = location.pathname.slice(1);//debug (barePath) // has full actual pathname including file
var midPath = barePath.slice (lmdArray[1].length, barePath.lastIndexOf("/")+1)
var siteURL = (location.protocol == 'file:'&&!forceUseProductionSite)? localTestURL+midPath : productionSiteURL	//top of directory hierarchy
//debug (location.pathname +"   "+ siteURL+"         "+url)
return siteURL+url;
}

//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
	{if (window.ActiveXObject)
			{try
				{return ActiveXObject("Microsoft.XMLHTTP");} // try the broken old MS way
			catch (e) { }
			}
	}
}

// the actual ajax request function for a targetEl obj cell and a passed url (must be complete)
function aFramebyEl (targetEl, url, defUrl)
{
	var xreq = makeRequestObj ()
	if (xreq) // all  set up OK so first attach the function to display the results when the state changes
		{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 = "Sorry, but I couldn't create an XMLHttpRequest.  Perhaps your browser is too old and needs updating (pre IE5.5 or Mozilla 6).";} else return
}

function aFrameD (xreq, targetEl, defUrl) // request the default file after a failure to get the original one
{
	var url = (defUrl)? fullUrl (defUrl): fullUrl(defaultUrl); // override the global default URL if one was passed
	xreq.onreadystatechange =
		function () {displayCallBackD(xreq, targetEl, url)}
		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)
{	if (xreq.readyState == 4) // request completed
		{if (xreq.status == 200 || xreq.status == 0) // file OK
			{var outMsg = (xreq.responseXML && xreq.responseXML.contentType=="text/xml") ? xreq.responseXML.getElementsByTagName("choices")[0].textContent : xreq.responseText;
			}
				// if first request fails, go fo a default file if there is one
			else if (xreq.status == 404&&tryDefaultFile) {aFrameD (xreq, targetEl, defUrl); return}
			else if (embedErrorMsgs) {var outMsg = "There was a problem reading the file "+url+".<BR>The HTML error is " + xreq.status} else return//;debug (outMsg)
			
		} // if xreq
	
	else if (embedErrorMsgs)
		{var outMsg = "There was a problem completing the AJAX request "} else return
	targetEl.innerHTML = outMsg; // attach result to the item id
	// the effect of this logic if no error messages are created 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)
{	if (xreq.readyState == 4)
		{if (xreq.status == 200 || xreq.status == 0)
			{var outMsg = (xreq.responseXML && xreq.responseXML.contentType=="text/xml") ? xreq.responseXML.getElementsByTagName("choices")[0].textContent : xreq.responseText;
			}
			else if (embedErrorMsgs) {var outMsg = "There was a problem reading the file "+url+".<BR>The HTML error is " + xreq.status;} else return
		}
	else if (embedErrorMsgs)
		{var outMsg = "There was a problem completing the AJAX request "} else return
	targetEl.innerHTML = outMsg;
}


// What follow are 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)
{
url = fullUrl(url)
var targetEl = document.getElementById(targetId);
aFramebyEl (targetEl, url, defUrl);
}

// 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)
{
	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 = fullUrl(targetId+fileSuffix);// debug (url)
		aFramebyEl (v[i], url, defUrl);
	}
}

// Next driver sets up link  selected content for the selected links into the cell specified 
// if the search string comes back 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
  // requres a function in cellera
  // if the optional parameter is set to "hover" the file is fetched on a hover instead of needing a click
function aFrameByLink (selector, targetId, mode)
{	
	var loc;
	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;  //debug (v[0])
		// and set up the click action for each
	for(i=0;i<l;i++)
	{ 
		if (mode == "hover")
		{
			v[i].onmouseover = function () {aFramebyEl (targetEl, fullUrl(this.href)); return false}
			v[i].onmouseout =  function () {targetEl.innerHTML= targetEl.origContent}
		}
	
	// set up the click action on all such links for furure clicks
		else v[i].onclick = function () {aFramebyEl (targetEl, fullUrl(this.href)); return false}//lastEntryByRedir = false; 
	}	
	
	// 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
			if (loc!= -1) {aFramebyEl (targetEl, fullUrl(location.search.slice(loc+targetId.length+1))); 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, fullUrl(location.search.slice(loc+defAFrameFolder.length+1))); return false}
}

// HANDLE PAGE SEQUENCES

var aFrameSeqs = new Array();
var lastSeq; // for keyboard control only the last one can be used
/* 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 aFrameByLink 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.
*/	
function initaFrameSeq (startURL, firstNum, lastNum, targetId, linkElType)
{
	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);

	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, fullUrl(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, fullUrl (url));
}
