// alert("Module xmlParserLib.js [v20030906] has loaded.");
//  <!-- to use this module, copy following (commented) line to your html file -->
//	<!-- <script src="/{server_alias}/includes/xmlParserLib.js"></script> -->

/* 
	name:	xmlParserLib.js
	dir:	htdocs/includes
	by: 	OpenAsia Solutions Pte Ltd
	func:	this library provides mechanisms to load an XML presentation script, 
			and to receive the name of that script as a URL parameter;
			the 'parseURL' function retrieves all CGI parameters passed in the URL, 
			and then loads them into a new 'keyVals' property in the current window;
			the 'openXMLfile' function is designed to receive the filename, and attempt
			to load the XML file, assuming the browser is capable (IE5 or NS6 or later);
			the loading may be asynchronous, especially if the XML file is very large,
			so we have to wait for an 'onload' event before doing any parsing of the XML;
			once the XML is loaded, we can parse the metadata into a presentation object,
			and parse the events into an array, which is sorted based on start times;
			at this point, the other presentation functions may be launched;
			support for Javascript 1.3 is required
	rev: 	20030906, wmc, created this module 
			20031009, wmc, fixed bug in parseXML to permit nodes with blank entries
		20031011, wmc, added check for null XML object in 'parseXML' function, and added
			more detailed error message for parsing errors (such as ampersand errors) 
	
	--------------------------------------------------------------------------------
	Following are the common client-side functions defined in this module.
	--------------------------------------------------------------------------------
	parseURL		retrieves CGI params as name/value pairs, stores them in window frame
	openXMLfile		loads an XML file from local drive of URL, invokes handler
	parseXML		parses the script XML object to extract metadata and events list
					(this is a special-purpose function for handling presentation scripts)
	countChildren	utility function to count the valid child nodes under some XML node
	peekInside		utility function to view the contents of some object or array
	createTable		utility function to create a browsable table of XML contents
	sortArray		utility function to sort array content numerically, any column
	compare			utility function to compare two array entries numerically
*/

var xmlParserLib = this;
var xmlDoc;						// create XML document object
var pObj = new Object;			// create a presentation object
var cgiParams = new Object;		// create an object to contain the CGI parameters

function parseURL (url) {
		// adds a new 'keyVals' property to the window.location object
		// note that this implementation is case-sensitive when retrieving by key
		// for example, where 'name=value', 'keyVals["name"]' returns 'value';
		// you may also access the parmeter pairs in order, as keyVals[0], etc.
	if (!url) return (null);
	var urlSearchStr = url.substring(url.indexOf("?") + 1, url.length);
	if (urlSearchStr == "") return (null);

	cgiParams.keyVals = urlSearchStr.split('&');
	for (var i = 0, num = cgiParams.keyVals.length; i < num; i++) {
		var pair = cgiParams.keyVals[i].split('=');
		cgiParams.keyVals[pair[0].toLowerCase()] = unescape(pair[1]);
	}
	return (cgiParams.keyVals);
}

function openXMLfile (filename, callback_str) {
	var loadStatus = false;
		// try reading the file using Navigator 6.x and IE 5.x object model (others not supported)
	if (document.implementation && document.implementation.createDocument) {
		xmlDoc = document.implementation.createDocument("", "", null);
		xmlDoc.onload = callback_str;		// Netscape loads asynchronously
		var errMsg = "Error - the XML file '" + filename + "' could not be found, or was invalid.";
		safeTryCatch ("xmlDoc.load('" + filename + "?rev=20031102_02');", errMsg);
		loadStatus = true;
	} else if (window.ActiveXObject) {
		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");		
		xmlDoc.async = true;	// if set to 'false', script execution is blocked until load completes
		xmlDoc.onreadystatechange = function () {
			if (xmlDoc.readyState == 4) 
				eval(callback_str());
		};		// need semicolon, you're declaring a function
		xmlDoc.load(filename + "?rev=20031102_02");		// change revision date to clear any caches
		if (xmlDoc.parseError.errorCode != 0) 
			alert("Error - the XML file '" + filename + "' could not be found, or was invalid.\n" +
				"Reason: " + xmlDoc.parseError.reason + 
				"Line: " + xmlDoc.parseError.line + ", position: " + xmlDoc.parseError.linepos + "\n" +
				"Source text: " + trim(xmlDoc.parseError.srcText) + "\n");
		else
			loadStatus = true;
 	} else {
		alert("Error - Your browser is not able to read XML, required for this application. " + 
		  "\nPlease try using Internet Explorer 5+ or Netscape 6+.");
	}
	return (loadStatus);
}

function safeTryCatch (functionDeclaration, errMsg) {
		// this is necessary to prevent 'try' syntax errors in NS4 browsers
		// if we simply lock out browsers that don't support JS1.4, we omit IE5
	if (document.implementation && document.implementation.createDocument) 
		eval ("try {" + functionDeclaration + "} catch(err){ if (errMsg) alert(\"" + errMsg + "\");}");
}

function parseXML () {
	var str = "";
	var node;
	var presentation = xmlDoc.documentElement;
	if (presentation == null) return;
	
		// get all the metadata
	pObj.version = ((node = presentation.getElementsByTagName('version')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.playerType = ((node = presentation.getElementsByTagName('playerType')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.seriesTitle = ((node = presentation.getElementsByTagName('seriesTitle')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.title = ((node = presentation.getElementsByTagName('title')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.presenterName = ((node = presentation.getElementsByTagName('presenterName')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.description = ((node = presentation.getElementsByTagName('description')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.author = ((node = presentation.getElementsByTagName('author')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.copyright = ((node = presentation.getElementsByTagName('copyright')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.recordingDate = ((node = presentation.getElementsByTagName('recordingDate')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.publicationDate = ((node = presentation.getElementsByTagName('publicationDate')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.mediaSource = ((node = presentation.getElementsByTagName('mediaSource')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.slideSource = ((node = presentation.getElementsByTagName('slideSource')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.duration = ((node = presentation.getElementsByTagName('duration')[0]) != null ? node.firstChild.nodeValue : "")
	pObj.eventListSize = countChildren(presentation.getElementsByTagName('events')[0]);
	pObj.eventListArray_index_id = 0;
	pObj.eventListArray_index_time = 1;
	pObj.eventListArray_index_filename = 2;
	pObj.eventListArray_index_topic = 3;
	pObj.eventListArray_index_caption = 4;
	// alert(peekInside(pObj, 'presentation'));
	
		// get the information for each event
	events = presentation.getElementsByTagName('events')[0];
	if (events && events.hasChildNodes()) {
		var eventListArray = new Array();
		eventList = events.getElementsByTagName('event');
		for (var i = 0; i < eventList.length; i++) {
			var event = eventList[i];
			eventListArray[i] = new Array();
			eventListArray[i][pObj.eventListArray_index_id] = event.getAttribute('id');
			eventListArray[i][pObj.eventListArray_index_time] = event.getAttribute('startTime');
			if (event.hasChildNodes()) {
				for (var j = 0; j < event.childNodes.length; j++) {
					if (event.childNodes[j].nodeType != 1) continue;	// for Netscape to skip over formatting
					if (!event.childNodes[j].firstChild) continue;		// skip if entry is blank
					switch (event.childNodes[j].nodeName) {
						case "imageSlide":
							eventListArray[i][pObj.eventListArray_index_filename] = event.childNodes[j].firstChild.nodeValue;
							break;
						case "topic":
							eventListArray[i][pObj.eventListArray_index_topic] = event.childNodes[j].firstChild.nodeValue;
							break;
						case "caption":
							eventListArray[i][pObj.eventListArray_index_caption] = event.childNodes[j].firstChild.nodeValue;
							break;
						default:
					}
				}
			}
		}
		sortArray(eventListArray, 1);
		pObj.eventListArray = eventListArray;
		pObj.isLoaded = true;
		// alert(peekInside(eventListArray, 'eventListArray'));
	} else {
		alert("Error - no scripted events were found.");
	}
}

function countChildren (node) {
	if (!node) return (0);
	var i = 0;
	var j = 0;
	while (i < node.childNodes.length) 
		if (node.childNodes[i++].nodeType == 1) j++;
	return (j);
}

	// use to view contents of arrays or objects
function peekInside (obj, name) {
	var str = "";
	var objname = (obj.name) ? obj.name : name;
	for (var i in obj)
		str += i + ": " + obj[i] + "\n";
	return ("object '" + objname + "' contains:\n" + str);
}

function createTable (displayElement, pageLocation) {
	if (!document.getElementById(pageLocation)) {
		alert("Error - unable to find page location: '" + pageLocation + "'.\t");
	} else {
		var x = xmlDoc.getElementsByTagName(displayElement);
		var newEl = document.createElement('TABLE');
		newEl.setAttribute('cellPadding', 5);
		var tmp = document.createElement('TBODY');
		newEl.appendChild(tmp);
		var row = document.createElement('TR');
		for (j=0; j < x[0].childNodes.length; j++) {
			if (x[0].childNodes[j].nodeType != 1) 
				continue;
			var container = document.createElement('TH');
			var theData = document.createTextNode(x[0].childNodes[j].nodeName);
			container.appendChild(theData);
			row.appendChild(container);
		}
		tmp.appendChild(row);
		for (i = 0; i < x.length; i++) {
			var row = document.createElement('TR');
			for (j=0; j < x[i].childNodes.length; j++) {
				if (x[i].childNodes[j].nodeType != 1) 
					continue;
				var container = document.createElement('TD');
				var theData = document.createTextNode(x[i].childNodes[j].firstChild.nodeValue);
				container.appendChild(theData);
				row.appendChild(container);
			}
			tmp.appendChild(row);
		}
		document.getElementById(pageLocation).appendChild(newEl);
	}
}

function sortArray (arr, sortcol, order_asc, type_isnumeric, case_sensitive) {
		// implements a bubble sort; order is 'true' for ascending, 'false' for descending
	if (sortcol == null) sortcol = 0;
	if (arr == null || sortcol > arr.length) return (new Array());
	if (order_asc == null) order_asc = true;
	if (type_isnumeric == null) type_isnumeric = true;
	if (case_sensitive == null) case_sensitive = true;
	for (var i = arr.length - 1; i >= 0; i--) { 
	  	for (var j = 0; j < i; j++) { 
			if (order_asc == (compare(arr[j][sortcol], arr[j+1][sortcol], type_isnumeric, 
			  case_sensitive) > 0)) {
				for (var k = 0; k < arr[0].length; k++) {
					temp = arr[j][k];
			        arr[j][k] = arr[j+1][k];
		    	    arr[j+1][k] = temp; 
				}					
			
			}
		}
	}
	return (arr);
}

function compare (var1, var2, type_isnumeric, case_sensitive) {
		// returns positive if var1 > var2; type is 'true' for numeric, 'false' for alphanumeric
		// numbers sort higher than letters, caps sort higher than lowercase
	
	if (type_isnumeric == null) type_isnumeric = true;
	if (case_sensitive == null) case_sensitive = true;
	if (type_isnumeric) {
		return (var1 - var2);
	} else {
		if (!case_sensitive) {
			var1 = var1.toLowerCase();
			var2 = var2.toLowerCase();
		}
		if (var1 > var2)
			return (1);
		else if (var1 < var2)
			return (-1);
		else return (0);
	}
}

function replace (str, from, to) {
	if (str == null || from == null || to == null) return ("");
	var strcopy = str;
	var ptr = str.indexOf(from);
	while (ptr != -1) {
		strcopy = strcopy.substr(0, ptr) + to + strcopy.substr(ptr + from.length);
		ptr = strcopy.indexOf(from, ptr + 1);
	}
	return (strcopy);
}

function trim (str) {
	if (!str || typeof str != "string") return ("");
	var i = 0;
	var c;
	while ((c = str.charAt(i)) == ' ' || c == '\t') i++;
	if (i == str.length) return ("");
	var j = str.length - 1;
	while (j >= 0 && (c = str.charAt(i)) == ' ' || c == '\t') j--;
	return (str.substr(i, j + 1));
}

