/* -*- java -*-
 * 
 * ----------------------------------------
 * |                                      |
 * |              header                  |
 * ----------------------------------------
 * |           |                          |
 * | index     |     ancesators           |
 * |           ---------------------------|c
 * |           |                          |a
 * |           |                          |
 * |           |                          |
 * |           |        cf                |
 * |           |                          |
 * ----------------------------------------
 *           body
*/

/****** public_html/frames.js
 * NAME
 * frames.js
 * SYNOPSIS
 * This Javascript file contains the functions for communication between frames, and the XJAX functions for loading a split screen view.
 * The file is loaded from inside the topmost frameset. This means it is only run once.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 ******
 */


// frameset loaded
var framesetLoaded = true;

// states

// is index frame shown
var indexFrameOn = true;
// is header frame shown
var headerFrameOn = true;
// initially other version not loaded
var otherVersionLoaded = false;
// screen is split
var splitOn = false;
// other type (ms or trans) of chunk
var otherType = "";
// filename of chunk
var fileName = "";
// reign taken from filename
var reign = "";
// name of other file
var otherFileName = "";
// id of root chunk
var rootID = "";

// images directory
var imageDir = 'icons/';

// interval for checking loading of index frame
var indexInterval = false;

// new window parameters
var window_params = 'width=400,height=400,resizable=no,scrollbars=no,toolbar=no,location=no,status=no,menubar=no';

/****f* frames.js/chunkOnLoad
 * NAME
 * chunkOnLoad
 * SYNOPSIS
 * Called when a new chunk (record/act) is loaded in the chunk frame.
 * Various bits of information about the chunk are stored in global variables.
 * Then, it checks to see if index and header (ancestor) frames need to be shown or hidden, and whether the split screen is on.
 * Finally, the frame manipulation buttons (which float in the top left corner of the chunk frame, are initialised.
 * ARGUMENTS
 * * ot - string - the other type of document (ms if the chunk calling the function is of type trans).
 * * fn - string - filename - made up of reign + underscore + type
 * * id - string - the ID of the chunk loaded
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * SEE ALSO
 * * toggleHeaderFrame
 * * toggleIndexFrame
 * * frameButtonsInit
 ******
 */
function chunkOnLoad(ot, fn, id) //{{{
{
	 // register other type and filename
	 otherType = ot;
	 fileName = fn;
	 rootID = id;
	 // get reign from filename
	 reign = fn.substring(0, fn.lastIndexOf('_'));
	 // make other file name from reign and other type
	 otherFileName = reign + '_' + ot;

	 // need to hide header frame
	 if (!headerFrameOn) 
		 {
				headerFrameOn = true;
				toggleHeaderFrame();
		 }

	 // need to hide index frame
	 if (!indexFrameOn) 
		 {
				indexFrameOn = true;
				toggleIndexFrame();
		 }
	 
	 // new page, so other version not loaded
	 otherVersionLoaded = false;
	 
	 // need to split screen
	 if (splitOn) 
		 {
				splitOn = false;
				toggleSplitScreen();
		 }
	 
	 // initialise frame buttons
	 frameButtonsInit();
}
//}}}

/****f* frames.js/toggleIndexFrame
 * NAME
 * toggleIndexFrame
 * SYNOPSIS
 * Usually called by clicking a button. The button image is modified using CSS to indicate the effect that the button will have.
 * Toggles the display of the index frame, containing all headings in the reign file.
 * If the frame is displayed (default), then the method will modify the frameset, so that the frame column is of width 1, and the button is modified by CSS.
 * If the frame is not being displayed, then the method will modify the frameset to give the index frame a column of width 200, and the button is modified by CSS.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * NOTES
 * Uses the global variable indexFrameOn
 ******
 */
function toggleIndexFrame() //{{{
{
	 var b = top.body.document;
	 var cf = top.body.ca.cf.document;
	 
	 if (b && cf) 
		 {
				if (indexFrameOn) {
					 b.body.cols = "1,*";
					 cf.getElementById('toggleIndexButton').className = "expand_h s";
				} else {
					 b.body.cols = "200,*";
					 cf.getElementById('toggleIndexButton').className = "shrink_h s";
				}
				indexFrameOn = !indexFrameOn;
		 }
}
//}}}

/****f* frames.js/toggleHeaderFrame
 * NAME
 * toggleHeaderFrame
 * SYNOPSIS
 * Usually called by clicking a button. The button image is modified using CSS to indicate the effect that the button will have.
 * Toggles the display of the ancestor frame, containing headings of each ancestor of the chunk.
 * If the frame is displayed (default), then the method will modify the frameset, so that the frame row is of height 1, and the button is modified by CSS.
 * If the frame is not being displayed, then the method will modify the frameset to give the ancestor frame a row of height 100, and the button is modified by CSS.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * NOTES
 * Uses the global variable headerFrameOn
 ******
 */
function toggleHeaderFrame() //{{{
{
	 var ca = top.body.ca.document;
	 var cf = top.body.ca.cf.document;

	 if (ca && cf) 
		 {
				if (headerFrameOn) {
					 ca.body.rows = "1,*";
					 cf.getElementById('toggleHeaderButton').className = "expand_v s";
				} else {
					 ca.body.rows = "100,*";
					 cf.getElementById('toggleHeaderButton').className = "shrink_v s";
				}
				headerFrameOn = !headerFrameOn;
		 }
}
//}}}

/****f* frames.js/toggleSplitScreen
 * NAME
 * toggleSplitScreen
 * SYNOPSIS
 * Toggles the split screen display of translation and manuscript versions of a chunk of data.
 * If the screen is to be split, and the other version hasn't just been loaded, then a XJAX request is made to retrieve the other version of the chunk.
 * The request is fed a URL to use, and has a callback function registered, for handling the loading of the chunk, when it arrives.
 * The CSS styles of the DIVs involved are toggled, so that the split screen is visible or hidden.
 * RETURN VALUE
 * boolean - returns true if the Javascript fails, so that the link's @href can be followed. False is returned if the Javascript DOM manipulation should work, so that the @href is ignored.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * NOTES
 * Uses the global variables otherType, otherVersionLoaded.
 * SEE ALSO
 * * createRequest
 * * loadDivs
 ******
 */
function toggleSplitScreen() //{{{
{
	 /*
		* return true to use @href on failure
		*/
	 if (!top.body.ca.cf.document) 
		 {
				return true;
		 }
	 
	 // get list of DIVs in document
	 var divs = top.body.ca.cf.document.getElementsByTagName("div");
	 // trans and ms styles - set for unsplitting
	 var trans_old = "trans_on";
	 var ms_old = "ms_on";
	 var trans_new = "ms" == otherType ? "trans" : "trans_off";
	 var ms_new = "trans" == otherType ? "ms" : "ms_off";
	 
	 // split screen
	 if (!splitOn) 
		 {
				// swap old and new styles
				var t = trans_new;
				trans_new = trans_old;
				trans_old = t;
				t = ms_new;
				ms_new = ms_old;
				ms_old = t;
				
				// havent loaded other version yet
				if (!otherVersionLoaded)
					{
						 // create request object
						 var req = createRequest();
						 if (req) 
							 {
									// construct URL to GET
									var url = top.body.ca.cf.location.pathname + 
										"?action=fetch_split&type=" + otherType + 
										"&filename=" + otherFileName +
										"&id=" + rootID;
									
									// register callback function
									req.onreadystatechange = function() {
										 loadDivs(req);
									};

									// make request
									req.open("GET", url, true);
									req.send(null);
							 }
						 
						 // have now loaded the other version
						 otherVersionLoaded = true;
					}
		 }

	 // loop over DIVs and toggle @trans and @ms DIVs
	 for (var i = 0; i < divs.length; ++i)
		 {
				if (trans_old == divs[i].className) 
					{
						 divs[i].className = trans_new;
					}
				else if (ms_old == divs[i].className) 
					{
					divs[i].className = ms_new;
					}
		 }
	 
	 // swap value of splitOn
	 splitOn = !splitOn;
	 
	 /*
		* return false to prevent @href loading
		*/
	 return false;
}
//}}}

/****f* frames.js/createRequest
 * NAME
 * createRequest
 * SYNOPSIS
 * Creates a XML HTTP request object - the type is dependent on the browser used.
 * RETURN VALUE
 * XMLHttpRequest or ActiveXObject, depending on the browser.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 ******
 */
function createRequest() //{{{
{
	 var req = false;
	 
	 if (window.XMLHttpRequest) 
		 {
				req = new XMLHttpRequest();
		 }
	 else if (window.ActiveXObject) 
		 {
				try 
					{
						 req = new ActiveXObject("MSxml2.XMLHTTP");
					}
				catch (e) 
					{
						 try 
							 {
									req = new ActiveXObject("Microsoft.XMLHTTP");
							 }
						 catch (e) {}
					}
		 }
	 
	 // try to set default mime type
	 if (req.overrideMimeType) 
		 {
				req.overrideMimeType("text/xml");
		 }
	 
	 return req;
}
//}}}

/****f* frames.js/loadDivs
 * NAME
 * loadDivs
 * SYNOPSIS
 * The AJAX request for the other type of a chunk of data has returned. If the readyState is 4, then this indicates a successful return of data.
 * For each DIV returned, the corresponding empty DIV in the already loaded document is found, and the returned DIV is loading into the empty DIV.
 * It is done DIV by DIV, so that different string lengths in the different types are caught at the end of each record, and new records start at the same place, horizontally.
 * Depending on the browser capabilities, the returned XML is brought into the loaded document, either as an imported node, or by setting the innerHTML.
 * ARGUMENTS
 * * req - XMLHttpRequest or ActiveXObject - the AJAX request object that is now returning results.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 ******
 */
function loadDivs(req) //{{{
{
	 if (4 != req.readyState) {
			return;
	 }
			
	 // get DIVs from xml sent back from request
	 var divs = req.responseXML.documentElement.getElementsByTagName("div");
	 // loop over DIVs
	 for (var i = 0; i < divs.length; i ++) 
		 {
				// look for DIVs with @class = split
				var d = divs[i];
				if ("split" != d.getAttribute("class")) 
					{
						 continue;
					}
				
				// get DIV from document with matching @id
				var id = d.getAttribute("id");
				id = id.substring(0, id.length - 6); // remove _split at end
				
				var div = top.body.ca.cf.document.getElementById(id);
				if (!div) 
					{
						 alert('no div with id ' + id);
						 continue;
					}
				
				// add other DIV to document
				if (document.importNode)
					{
						 var nd = top.body.ca.cf.document.importNode(d, true);
						 div.appendChild(nd);
					}
				else
					{
						 // just use innerHTML - appendChild doesnt work
						 div.innerHTML = d.xml || d.outerHTML;
					}
		 }
}
//}}}

// function for importing nodes in IE
// http://dhtmlkitchen.com/news/?permalink=811FD9713AEB64EC5FD183BF0ED39B29.txt
// not used anymore
function importNodeIE (oNode) //{{{
{
	 var nodeHTML = oNode.xml||oNode.outerHTML;
	 if(!nodeHTML) throw "importNodeIE: nodeHTML is null or undefined";
	 var tmpNode = document.createElement("div");
	 tmpNode.innerHTML = nodeHTML;
	 return tmpNode.firstChild.cloneNode(true);
}
//}}}

/****f* frames.js/frameButtonsInit
 * NAME
 * frameButtonsInit
 * SYNOPSIS
 * The buttons for manipulating the (index and header/ancestor) frames should be positioned at the top left of the chunk frame.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * SEE ALSO
 * * getDimensions
 ******
 */
function frameButtonsInit() //{{{
{
	 var cf = top.body.ca.cf;

	 var dH = cf.document.getElementById("headerButtonDiv");
	 var dI = cf.document.getElementById("indexButtonDiv");

	 var dimensions = getDimensions(cf);
	 
	 if (dimensions[0] && dimensions[1]) 
		 {
				if (dH) 
					{
						 //dH.style.left = "0px"; //(dimensions[0] * 0.75) + "px";
						 dH.style.top = dimensions[2] + "px";
						 dH.style.display = "block";
					}
				
				if (dI) 
					{
						 dI.style.top = ((dimensions[1] * 0.75) + dimensions[2]) + "px";
						 dI.style.left = "0px";
						 dI.style.display = "block";
					}
		 }
}
//}}}

/****f* frames.js/frameButtonsScroll
 * NAME
 * frameButtonsScroll
 * SYNOPSIS
 * Called when the chunk frame has scrolled, and the frame buttons need to be repositioned.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * SEE ALSO
 * * getDimensions
 ******
 */
function frameButtonsScroll() //{{{
{
	 var cf = top.body.ca.cf;
	 var ll = cf.document.getElementById("links_list");
	 var dimensions = getDimensions(cf);

	 if (ll) 
		 {
				ll.style.top = (dimensions[2] + 20) + "px";
		 }
}
//}}}

/****f* frames.js/getDimensions
 * NAME
 * getDimensions
 * SYNOPSIS
 * A cross browser function for getting the width and height of a document, and the offset from the top.
 * ARGUMENTS
 * * frame - document object - the frame to get dimensions/offset from.
 * RETURN VALUE
 * array - 3 integers, for width, height and offset of document.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * SEE ALSO
 * * getDimensions
 ******
 */
function getDimensions(frame) //{{{
{
	 var height = 0, width = 0, yoffset = 0;
	 
	 // not IE
	 if ("number" == typeof(frame.window.innerHeight) &&
			 "number" == typeof(frame.window.innerWidth) && 
			 "number" == typeof(frame.window.payeYOffset)) 
		 {
				height = frame.window.innerHeight;
				width = frame.window.innerWidth;
				yoffset= frame.window.pageYOffset;
		 }
	 // IE - standards compliant mode
	 else if (frame.document.documentElement && 
						frame.document.documentElement.clientWidth && 
						frame.document.documentElement.clientHeight &&
						"number" == typeof(frame.document.documentElement.scrollTop)) 
		 {
				height = frame.document.documentElement.clientHeight;
				width = frame.document.documentElement.clientWidth;
				yoffset = frame.document.documentElement.scrollTop;
		 }
	 // IE - quirks mode
	 else if (frame.document.body &&
						frame.document.body.clientWidth &&
						frame.document.body.clientHeight &&
						"number" == typeof(frame.document.body.scrollTop)) 
		 {
				height = frame.document.body.clientHeight;
				width = frame.document.body.clientWidth;
				yoffset = frame.document.body.scrollTop;
		 }
	 
	 return [width, height, yoffset];
}
//}}}

// change images on mouseover
// not used
function flipImage(id) //{{{
{
	 var cf = top.body.ca.cf;
	 var img = cf.document.getElementById(id);
	 var state = img.src.charAt(img.src.length-5) * 1;
	 var newstate = (state * -1) + 1;
	 
	 img.src = img.src.substring(0, img.src.length-5) + newstate + ".gif";
}
//}}}

/****f* frames.js/expandIndexList
 * NAME
 * expandIndexList
 * SYNOPSIS
 * Tries to expand the list that the current chunk is in. This means talking to functions loaded by the index document of the frameset.
 * An interval is used to repeatedly call this function every 500ms until the index document is opened and the list expanded.
 * This way, the code wont fail if one document is loaded before the other.
 * ARGUMENTS
 * * list - ID of list to expand.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * NOTES
 * Uses the global variable indexInterval.
 * SEE ALSO
 * * list.js
 ******
 */
function expandIndexList(list) //{{{
{
	 if (!indexInterval) // && top.body && top.body.index && top.body.index.expand)
		 {
				indexInterval = window.setInterval('expandIndexList(\'' + list + '\')', 500);
		 }
	 
	 // test for index frame loaded
	 if (top.body && top.body.index && top.body.index.expand) 
		 {
				d = top.body.index.expand(list);
				if (indexInterval) // && d > 0) 
					{
						 window.clearInterval(indexInterval);
						 indexInterval = false;
					}
		 }
}
//}}}

/****f* frames.js/transformLinks
 * NAME
 * transformLinks
 * SYNOPSIS
 * When the document passed as an arguemnt is clicked, this anonymous function here is called.
 * Starting at the source of the click, the node tree is followed, parent by parent, until a A tag is found.
 * If one is found, and it has a @rel attribute, the link (A tag) is handled by the handleLink function.
 * ARGUMENTS
 * * doc - document to transform links in
 * RETURN VALUE
 * boolean - not used
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * SEE ALSO
 * * handleLink
 ******
 */
function transformLinks(doc)  //{{{
{
	 doc.onclick = function(e) 
		 {
				/*
				 * get target element from event
				 */
				e = e || doc.parentWindow.event;
				var link = e.target || e.srcElement;
				
				/*
				 * navigate up tree until link is found
				 */
				while (link && 
							 link.tagName &&
							 "a" != link.tagName.toLowerCase() &&
							 "form" != link.tagName.toLowerCase())
					{
						 link = link.parentNode;
					}

				/*
				 * make sure link was found
				 */
				if (link &&
						link.tagName &&
						("a" == link.tagName.toLowerCase() ||
						 "form" == link.tagName.toLowerCase()) &&
						link.getAttribute('rel')) 
					{
						 return handleLink(link);
					}
		 }
	 
	 return true;
}
//}}}

/****f* frames.js/handleLink
 * NAME
 * handleLink
 * SYNOPSIS
 * Uses the @rel attribute to work out what to do with a link - either open the document in a new window (with params), or set the target of the link.
 * In XHTML, the @target attribute is not allowed. But the @rel attribute can store the target value, and JS used to set the @target.
 * If the @rel starts with _, or is 'new' or 'newwindow', then a new window is opened using the params set in window_params.
 * It was found that IE wouldn't set the HTTP_REFERER variable if the target was set before the window/tab existed.
 * So, the window has to be opened, and then, by setting the target value, the link is loaded into the window/tab, and the referer gets set.
 * ARGUMENTS
 * * link - DOM element node - the link to modify the behaviour of.
 * AUTHOR
 * Swithun Crowe
 * CREATION DATE
 * 20090218
 * NOTES
 * Needs access to the global variable window_params for opening document in new window.
 ******
 */
function handleLink(link) //{{{
{
	 var rel = link.getAttribute("rel");
	 if (rel) 
		 {
				/*
				 * new tab/window, so open first
				 * and then make it the target
				 * to avoid IE not setting the referrer with window.open
				 */
				if ("_" == rel.substring(0, 1) ||
						"new" == rel || 
						"newwindow" == rel) 
					{
						 window.open('', 
												 rel, 
												 'newwindow' == rel ? window_params : ''
												 );
					}
				
				link.target = rel;
		 }
	 
	 return true;
}
//}}}

