//	alert("Module commonLib.js [v20020916.1] has loaded.");
//  <!-- to use this module, copy following (commented) line to your html file -->
//	<!-- <script src="/{server_alias}/includes/commonLib.js"></script> -->

/*
	name:	commonLib.js
	dir:	\htdocs\includes
	by: 	OpenAsia Solutions
	func:	provides common javascript functions
	rev: 	19990805, wmc, created this module
			19990820, wmc, added new math functions 'convert', 'roundOff', 'isNumber'
			19990823, wmc, fixed bug in convert function for small negative numbers
			19990915, wmc, fixed bug in convert function for zero values
			19990919, wmc, fixed bug in winPopUp (declaration of infowin as local),
				added killPopUp function to close the window
			20000225, wmc, added teal background color
			20000808, wmc, debugged problems with getJsVar and setJsVar, and added support
				for storing global variables in the session cookie (if cookies are enabled);
				consolidated session managment functions
			20001008, wmc, added support for countdown timer 'timeOut', and verified that
				parameters in callback method can be passed as arguments
			20010909, wmc, modified the browser detection to support latest browsers;
				added support for 'getBrowser' info
			20011125, wmc, added support for global or user-defined stack operations
			20020421, wmc, added support for link rollovers to display text, rather than url
			20020427, wmc, added support for display of array or object contents
			20020503, wmc, added 'getFileName' functions, added 'getFileExt'
			20020917, wmc, added 'makeFullURL' function
			20021226, wmc, added 'getMajorVersion' and 'getMinorVersion' functions
			20030110, wmc, added 'openDoc' to open an abitrarily sized page or image
				in a popup window
			
	--------------------------------------------------------------------------------
	Following are the common client-side functions defined in this module.
	--------------------------------------------------------------------------------
	is					checks browser version
	winAlert			presents simple message in a pop-up window (ie- like JS alert) 
	winPopUp			presents html page in a pop-up window (eg- for JS dialogs)
	killPopUp			removes pop-up window (eg- for JS dialogs)
	searchAndReplace	performs string search and replace
	replaceStr			performs character substitution on a string
	convert				converts to currency format with 2-digit precision
	roundOff			rounds and truncates number to any precision, returns string
	isNumber			checks if supplied string is a numeric value
	isJSVarSet			checks if supplied string has a corresponding variable with a non-null value
	setJsVar			sets a global variable, and tries to save in a non-persistent cookie
	getJsVar			retrieves global variable, and if not found, tries to retrieve
						it from the session cookie (which is not persistent between sessions)
	getCookie			retrieves variable from session cookie, if cookies are supported
	getSessionID		retrieves session ID from global variable s_id, preset to -1
	setSessionID		sets the global variable s_id according to assigned session id
	getUserID			retrieves user ID from global variable u_id, preset to -1
	setUserID			sets the global variable u_id according to u_id returned from database
	timeOut				creates timer to countdown by seconds, then invokes callback method
	checkCounter		supporting function for timeout, which calls itself every second
	stopTimer			kills the timer if caller wants to abandon countdown defined in 'timeOut'
	push				supports global or user-defined stack operations  
	pop					stack operator, useful for saving and recalling referrer links
	showLinkStatus		presents text rather than URL as the mouseover status for links,
						and includes a useful 'inspector' for form elements
	peekInside			show contents of array or object
	getFileName 		gets the filename without the path of the directory
	getFileExt	 		gets the filename extension only
	makeFullURL			using baseURL and relative path, make a fully-formed URL
	getMajorVersion		returns major version from decimal string like '1.2' (eg- '1')
	getMinorVersion		returns minor version from decimal string like '1.2' (eg- '2')
	
	Notes on Session Management:
			The session management functions use the 'mwsession' tag to issue session ids
			from the database, and to validate supplied ids as corresponding to existing 
			sessions which have not expired.  The session id must be supplied in a form 
			post or other CGI call, and once assigned, must be retained in the client's 
			session state.  This is handled by declaring a javascript variable.  If cookies
			are enabled, the javascript variable is backed up with a variable stored in a
			cookie.  Variables saved in a session cookie will be available even after a
			browser refresh, or if the user moves to another site and returns (without closing
			the browser).  The cookie implementation here does not write to the user's hard
			disk, and it is not persistent - it will expire when the browser is closed.

			To implement session management, add the session id variable 's_id' to your
			form post, and add the following lines to your server side scripts:
				<!-- <mwremark>
				var global = top.dataframe;		// use keyword 'global' to reference dataframe
				<mwif cond="'<mwvar name=s_id>' != ''">
					<mwsetvar name=s_id value="<mwsession key='<mwvar name=key>' s_id='<mwvar name=s_id>'>" >	
					if (global != null) global.setSessionID( <mwvar name=s_id> );
				</mwif>
				</mwremark> -->

			Note that the 'key' variable must be explicitly referenced in your 'mwsession' tag,
			(unlike other tags where the key value is passed implicitly). If you use a database
			other than MS Access, you must specify an additional value to indicate the database 
			type.  For MS SQL, add "db_type = 'mssql'". For Oracle, add "db_type = 'oracle'".
			
			To properly set the session id in your form posts, add the following lines:
				<!-- <mwremark>
				if (form.s_id.value == '') form.s_id.value = global.getSessionID();
				<input type="hidden" name="s_id" value="<mwvar name=s_id>">
				</mwremark> -->

			Note that if you fail to define an 's_id' variable in your form post, session 
			management will be skipped, and that if the value of 's_id' is -1 (the global
			default value), a new session will be created.
	
	--------------------------------------------------------------------------------
*/

var commonLib = this;

		// session management variables
var COOKIES_OK = true;		// really depends on settings in the client browser
var s_id = -1;
var u_id = -1;

	// detect browser version
var is;
var isIE3Mac = false;
	// add dummy character to avoid comparisons like "str.indexOf('match') != -1"
var agt = ' ' + navigator.userAgent.toLowerCase();	
if ((agt.indexOf("mac") > 0) && (agt.indexOf("msie") > 0) && (parseInt(navigator.appVersion) == 3)) 
	isIE3Mac = true;	// IE3 on Mac will crash if you create an IS object
else
	is = new Is();		// otherwise it is more elegant to use the IS object below

function Is () {
    this.major = parseInt(navigator.appVersion);
    this.minor = parseFloat(navigator.appVersion);

    this.nav  = ((agt.indexOf('mozilla') > 0) && ((agt.indexOf('spoofer') == -1)
                && (agt.indexOf('compatible') == -1)));
    this.nav2 = (this.nav && (this.major == 2));
    this.nav3 = (this.nav && (this.major == 3));
    this.nav4 = (this.nav && (this.major == 4));
    this.nav4up = this.nav && (this.major >= 4);
    this.navonly = (this.nav && (agt.indexOf(";nav") > 0));
    this.nav6 = (this.nav && (this.major == 5));
    this.nav6up = (this.nav && (this.major >= 5));
    this.gecko = (agt.indexOf('gecko') > 0);

    this.ie     = ((agt.indexOf("msie") > 0) && (agt.indexOf("opera") == -1));
    this.ie3    = (this.ie && (this.major < 4));
    this.ie4    = (this.ie && (this.major == 4) && (agt.indexOf("msie 5") == -1));
    this.ie4up  = (this.ie && (this.major >= 4));
    this.ie5    = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.0") > 0));
    this.ie5_5  = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.5") > 0));
    this.ie5up  = (this.ie && !this.ie3 && !this.ie4);
    this.ie6up 	= (this.ie && !this.ie3 && !this.ie4 && !this.ie5);

	this.mac = agt.indexOf('mac') > 0;
	this.mac_ie5up = (this.mac && this.ie5up);

	this.opera = (agt.indexOf("opera") > 0)
}

var infowin = null;

		// present simple winAlert window
function winAlert (message) {
	if (!isIE3Mac && (is.nav4up || is.ie4up)) {

		var mystyle = "toolbar=no,location=no,directories=no,status=no,menubar=no," +
			"scrollbars=yes,resizable=no," +
			"width=430,height=260,left=220,top=150,screenX=220,screenY=150";
			// no whitespace allowed in this parameter!

		var logo = "";
		var heading = "";
		var bg_white = "#ffffff"
		var bg_gold = "#ffd700";
		var bg_sky = "#87cefa";
		var bg_linen = "linen";
		var bg_wine = "#f8f8ff";
		var bg_teal = "#5F9F9F"
		message = searchAndReplace(message, '\n', '<br>', 0);
		message = searchAndReplace(message, '\t', '<dd>', 0);
		var ptr = message.indexOf("<br>");
		if (ptr >= 0) {
			heading = message.substr(0, ptr);
			message = message.substr(ptr + 4, message.length);
		}
				
		if (!infowin || infowin.closed) {
			infowin = window.open('', 'Alert', mystyle);
	
			var content = "<html><head><title>Alert</title></head>";
			content += "<body bgcolor=" + bg_white + " onblur=this.window.focus()>";
			content += "<center><table width=90% border=0><tr>";
			if (logo != "")
				content += "<td valign=top><img src=" + logo + " height='' width=''>&nbsp;&nbsp;</td>";
			if (heading != "") {
				content += "<td><font face=arial size=3>" + heading + "</font><br>";
				content += "<br><font face=arial size=2>" + message + "</font></td></tr>";
			} else {
				content += "<td><font face=arial size=3>" + message + "</font></td></tr>";
			}
			content += "<tr align=center><form name=theForm><td colspan=2><br><font face=arial size=2>";
			content += "<input type=button name=b_ok value='   Ok   ' onclick=window.close()></font></td>";
			content += "</form></tr></table></center><br></body></html>";
			infowin.document.write(content);
			infowin.document.close();
			infowin.document.theForm.b_ok.focus();
		} else {
			infowin.focus();
		}
	} else {
		alert(message);
	}
}

		// present dialog window
function winPopUp (url) {
	var mystyle = "toolbar=no,location=no,directories=no,status=no,menubar=no," +
		"scrollbars=yes,resizable=no," +
		"width=400,height=250,left=220,top=150,screenX=220,screenY=150";
		// no whitespace allowed in this parameter!

	if (!infowin || infowin.closed) {
		infowin = window.open(url, 'PopUp', mystyle);
		// alert ("infowin:" + infowin.name + ", parent: "+ infowin.opener.name);	 
	} else {
		infowin.focus();
	}
}

		// present dialog window
function openDoc (url) {
	var mystyle = "toolbar=no,location=no,directories=no,status=no,menubar=no," +
		"scrollbars=yes,resizable=yes," +
		"left=20,top=20,screenX=20,screenY=20";
		// no whitespace allowed in this parameter!

	if (!infowin || infowin.closed) {
		infowin = window.open(url, 'PopUp', mystyle);
		// alert ("infowin:" + infowin.name + ", parent: "+ infowin.opener.name);	 
	} else {
		infowin.focus();
	}
}


function killPopUp () {
	if (infowin && !infowin.closed)
		infowin.close();
}

function searchAndReplace(strMultipleLines, strLookFor, strReplaceWith, nStartIdx) {
	var strProcessed = strMultipleLines;	
	
	while((nReplacePos = strProcessed.indexOf(strLookFor, nStartIdx)) > -1) {
		strProcessed = replaceStr(strProcessed, strReplaceWith, strLookFor.length, nReplacePos);	
		nStartIdx = nReplacePos  + strReplaceWith.length;
	}
	return strProcessed;
}

function replaceStr(strTarget, strReplace, nCharToReplace, nReplacePos) {
	var nTargetStrLen = strTarget.length;			
	var strFinal = strTarget.substring(0, nReplacePos) + strReplace + strTarget.substring(nCharToReplace + nReplacePos, nTargetStrLen);
	return strFinal;
}

function convert (number, precision) {
	var numstr;
	if (number == 0)
		numstr = "0.00"
	else
		numstr = number.toString();		// bug, doesn't handle zero properly
	var buffer = '';
	if (isNumber(numstr)) {
		var c = '';
		for (var i = 0; i < numstr.length; i++) {
			c = numstr.charAt(i);
			if (c != ',') buffer += c;
		}
		numstr = buffer;					// strip out all the commas
		buffer = '';
		
		var sign = numstr.charAt(0);
		if (sign == "-" || sign == "+")
			numstr = numstr.substr(1);		// strip off leading sign
		else
			sign = '';
		if (sign == "+") sign = '';			// save minus sign for later append

		i = 0;
		while (i < numstr.length && numstr.charAt(i) == "0") i ++;
		numstr = numstr.substr(i);			// strip off leading zeros
	
		numstr = roundOff(numstr, precision);
		var dot = numstr.indexOf('.')
		if (dot == -1)
			dot = numstr.length;
		else
			buffer = numstr.substr(dot);	// save decimal value
	
		var j = 0;
		while (j < dot) {
			c = numstr.charAt(dot - j - 1);
			if ((j != 0) && (j % 3 == 0)) buffer = "," + buffer;
			buffer = c + buffer;
			j++;
		}
		buffer = sign + buffer;				// append the minus sign if present
		/*		
		alert("processing: " + number + "\n" +
		 	"as string: " + numstr + "\n" +
		 	"sign: " + sign + "\n" +
		 	"length: " + numstr.length + "\n" +
		 	"dot: " + dot + "\n" +
		 	"buffer: " + buffer + "\n" +
			"\n");
		*/
	} else {
		buffer = 'n/a';
	}
	return (buffer);
}

						// round numeric value to required precision, return string
function roundOff (value, precision) {
	var result;
	precision = parseInt(precision);
	var whole = "" + Math.round(eval (value) * Math.pow(10, precision));
	while (whole.length <= precision) 
		whole = "0" + whole;
	var decPoint = whole.length - precision;
	result = whole.substring (0, decPoint) + "." + whole.substring (decPoint, whole.length);
		/*
		// note: rounding fails with 2 leading zeros or small negative numbers (eg: '-.01')		
		alert("" + 
		"whole: '" + whole + "'\n" +
		"math: '" + Math.pow(10,precision) + "'\n" +
		"decimal: '" + decPoint + "'\n" +
		"first part: '" + whole.substring(0,decPoint) + "'\n" +
		"second part: '" + whole.substring(decPoint, whole.length) + "'\n" +
		"\n");
		*/
	return result;
}

						// validate input, be sure input is numeric
function isNumber (str) {
  	if (str == "") return false;
	var buffer = '';
	for (var i = 0; i < str.length; i++) {	// strip out all the commas
		c = str.charAt(i);
		if (c != ',') buffer += c;
	}
	str = buffer;
	var sign = str.charAt(0);
	if (sign == "-" || sign == "+")
		str = str.substr(1);		// strip off leading sign
  	for (i = 0; i < str.length; i++) {
    	var ch = str.substring (i, i+1);
    	if ((ch < "0" || ch > "9") && ch != ".") 
       		return false;
  	}
  	return true;
}

function isJsVarSet (varName) {			// use isJsVarSet('name')
	return (getJsVar(varName) != '');
}

function getJsVar (varName) {			// use getJsVar('name')
	var result = "";
	if (eval("this." + varName)) result = eval(varName);
	if (COOKIES_OK && result == "") {
		result = getCookie (varName);
		if (result != '')
			setJsVar(varName, result);	// save in session state variable
	}
	return (result);
}

function setJsVar (varName, value) { 	// use setJsVar('name', 'value')
	eval(varName + "='" + value + "'");
	if (COOKIES_OK) document.cookie = varName + "=" + escape(value);
}

function getSessionID() {
	var result = "";
	if (eval("this.s_id")) result = eval(s_id);
	if (COOKIES_OK && result == '-1') {
		result = getCookie ('s_id');
		if (result == '')
			result = -1;
		else 
			this.s_id = result;				// save in session state variable
	}
	return (result);
}

function setSessionID (supplied_id) {
	if (supplied_id == '') {
		winAlert("Error - Attempt to set session id to null.");
	} else {
		var found_id = -1;
		if (eval("this.s_id")) found_id = eval(s_id);
		if (supplied_id != found_id) {
			setUserID(-1);	// force new user session
			this.s_id = supplied_id;
		}
		if (COOKIES_OK) document.cookie = "s_id=" + this.s_id;
	}
}

function getUserID() {
	var result = "";
	if (eval("this.u_id")) result = u_id;
	if (COOKIES_OK && result == '-1') {
		result = getCookie ('u_id');
		if (result == '')
			result = -1;
		else 
			setUserID(result);				// save in session state variable
	}
	return (result);
}

function setUserID(u_id) {
	if (u_id == '') {
		alert("Error - Attempt to set user id to null.");
	} else {
		this.u_id = u_id;
		if (COOKIES_OK) document.cookie = "u_id=" + u_id;
	}
}

function cookiesEnabled () {
	result = false;
	if (COOKIES_OK) {
		document.cookie = "test_cookie=saved";
		result = (getCookie('test_cookie') == 'saved');
		document.cookie = "test_cookie=; expires=Thu, 01-Jan-70 00:00:01 GMT";
	}
	return (result);
}

function getCookie(varName) {				// use getCookie('name')
	var index = document.cookie.indexOf(varName + "=");
	if (index == -1) return "";
	index = document.cookie.indexOf("=", index) + 1;
	var endstr = document.cookie.indexOf(";", index);
	if (endstr == -1) endstr = document.cookie.length;
	return unescape(document.cookie.substring(index, endstr));
}

var seconds_counter = 0;					// initialize global seconds counter
var timerID;
function timeOut (status_message, duration, callback_string) {
		// function may be invoked with a callback string that contains parameters, 
		// eg- 	timeOut("Counting down seconds... ", 3, "test('test_param', counter, 3)");
		// note: it is up to the implementer to insure that callback can be evaluated
		// and finally, be sure to call 'stopTimer' as a page unload event, that is,
		// in case the user doesn't wait for the timer and simply links off to another page!
	seconds_counter = duration + 1;			// set global seconds counter
	checkCounter(status_message, callback_string);
}

function checkCounter (status_message, callback_string) {
		// self-reference must take care of any apostrophes ("'") in the input
	var self_reference = "checkCounter(\"" + status_message + "\", \"" + callback_string + "\")";
	if (--seconds_counter > 0) {
		window.status = status_message + seconds_counter;
		timerID = setTimeout(self_reference, 1000);
	} else {
		window.status = '';
		eval(callback_string);
	}
}

function stopTimer () {
	clearTimeout(timerID);
	window.status = '';
}

function summaryPopUp(width, height, left, top, url){
	var mystyle = "toolbar=no,location=no,directories=no,status=no,menubar=no," +
				  "scrollbars=yes,resizable=no," +
				  "width="+width+",height="+height+",left="+left+",top="+top+",screenX=220,screenY=150";
	// no whitespace allowed in this parameter!
	if (!infowin || infowin.closed) {
		infowin = window.open(url, 'PopUp', mystyle);
		// alert ("infowin:" + infowin.name + ", parent: "+ infowin.opener.name);	 
	} else {
		infowin = window.open(url, 'PopUp', mystyle);
		infowin.focus();
	}
}

function log_out() {
	//var form = document.logout_form;
	//form.target = "_top";
	//form.action = "/ps_global_htdocs/index.htm";
	//alert(form.action);
	//alert("User id is: " + getUserID());
	//alert("Session id is: " + getSessionID());
	if (isLogin()) {
		// form.logout.value = "true";
		// clear_materials();
		setUserID(-1);
		winAlert("You have successfully logged out");
	}	
	//document.location.target="_top";
	// parent.location.href="/ps_global_htdocs/index.htm";
	//form.submit();
}

function isLogin(){
	var id = getUserID();
	return (id != '' && id != -1);
}

function getSessionData(){
	var str = "Global Session Data\n";
	str += "````````````````````````````````````\n";
	str += "s_id:\t\t\t'" + getSessionID() + "'\n";
	str += "u_id:\t\t\t'" + getUserID() + "'\n";
	str += "fullname:\t\t\t'" + getJsVar('u_fullname') + "'\n";
	str += "admin role:\t\t'" + getJsVar('u_role') + "'\n";
	str += "last login:\t\t\t'" + getJsVar('u_last_login') + "'\n";
	str += "cookies enabled:\t\t'" + cookiesEnabled() + "'\n";
	if (cookiesEnabled && document.cookie != '')
		str+= "\ngot cookie: '" + unescape(document.cookie) + "'\n";
	alert (str);
}

  	// declare global stack and constructors (IE v5.5 does not need this)
var stack = new Array();
if (!is.ie6up && !is.ie5_5) {
  Array.prototype.push = push;
  Array.prototype.pop = pop;
}

	// supports pushing a comma-delimited list onto an internal stack (eg- 'stack.push("value")')
function push() {
	var sub = this.length;
	for (var i = 0; i < push.arguments.length; ++i) {
		this[sub] = push.arguments[i];
		sub++;
	}
}

	// supports retrieving next item from stack (eg- 'stack.pop()')
function pop() {
	if (this.length > 0) {
		var lastElement = this[this.length - 1];
		this.length--;
		return lastElement;
	}
}

	// modifies link mouseover status to display the text of the link rather than it's target
var LINKS_ONLY = true;		// note: 'links[0]' limits event handling to anchor tags only
var targetframe = self;
function showLinkStatus (targetframe) {
	this.targetframe = targetframe;
	if (this.is.nav4up)
		targetframe.document.captureEvents(Event.MOUSEOVER | Event.MOUSEOUT);
	targetframe.document.onmouseover = mouseOver;
	targetframe.document.onmouseout = mouseOut;
}

function mouseOver (e) {
	var trigger = '';
			// Internet Explorer events
	if (!e && targetframe.event.srcElement) {
		trigger = targetframe.event.srcElement;
		if (trigger.nodeType && trigger.nodeType == 3)
			trigger = trigger.parentNode;
		text = trigger.innerText;
		attributes = trigger.name;		// attributes pretty limited in IE
	} else {
			// Netscape 4.x events
		if (e.target) {
			trigger = e.target;
			text = trigger.text;
			attributes = trigger;
		}
	}
	if (!LINKS_ONLY) {
			// the trigger objects are now in hand, show the type and attributes
		if (trigger.type)
			self.status = trigger.type + ", " + attributes;			// debugger
	}
			// if trigger is a hypertext link, display the text of the anchor
    if (trigger.href) {
		self.status = text;
		// alert(peekInside(document.links));
	}
	return true;
}

function mouseOut (e) {
	self.status = "";
}

	// 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);
}

	// retrieve the filename without the path specification
function getFileName (str) {
	var delims = "\\/:";
	var i = str.length - 1;
	while (i >= 0 && delims.indexOf(str.charAt(i)) == -1) i--;
	return (i >= 0 ? str.substr(i + 1, str.length) : '');
}

	// retrieve extension without the filename or path specification
function getFileExt (str) {
	var delims = ".";
	var i = str.length - 1;
	while (i >= 0 && delims.indexOf(str.charAt(i)) == -1) i--;
	return (i >= 0 ? str.substr(i + 1, str.length) : '');
}

	/*
	This function makes an absolute pathname from a baseURL and a string representing a relative 
	path or filename.  It supports both URL and file system syntax, works with Windows or Unix 
	file naming conventions, removes extra delimiters, and resolves references to '/./' and 
	'/../' in the path. Any base path can be provided, but often this will be the browser's 
	reference to the current page (eg- 'document.location.href'). 

	The function returns the resolved name on success, or an empty string on failure.  Note that 
	the function *does not* translate a URL into a file system reference.  When the base path 
	originates on the server side, the returned value will use URL syntax.  When the base path 
	originates on the client side (eg- a page loaded from the client machine), the returned value 
	will use file system syntax, with the URL prefix "file:///".
	
	Tested with IE 5.x and Netscape 4.x browsers.  Be sure that if 'relPath' parameter uses
	Windows path delimiters, you provide a double backslash to avoid the backslashes from being 
	interpreted as control characters (eg- '\n' signals a carriage return).
	
	If 'relPath' is already a fully formed URL, the function ignores 'basePath' and returns 
	'relPath' as the absolute pathname.
	
	*/

function makeFullURL (basePath, relPath) {
	var i = 0;
	var fullPath = "";
	var prefix = "";
	var pathDelim = "/";		// URLs are always delimited with forward slash, regardless of OS!
	var relPathSafe = relPath;
	var basePathSafe = basePath;
	
	if (relPath.indexOf('://') != -1) {
		fullPath = relPath;		// relative path is already an absolute path!
	} else {
			// normalize the delimiters
		basePath = searchAndReplace(basePath, "\\", "/");	// force to forward slash for parsing
		relPath = searchAndReplace(relPath, "\\", "/");		// force to forward slash for parsing
		relPath = searchAndReplace(relPath, "//", "/");		// remove extra delimiters (if any)
	
			// split the prefix and basepath, and ignore the trailing filename and slash
		prefix = basePath.substring(0, basePath.indexOf('://') + 3);
		basePath = basePath.substring(prefix.length, basePath.lastIndexOf('/'));	
	
		var relPathArray = relPath.split ("/");				// tokenize paths into arrays
		var basePathArray = basePath.split ("/");			// if 'file', first entry is blank
	
			// adjust pointers to compensate for '..' parent directory references
		var rpPtr = relPathArray.length - 1;
		var bpPtr = basePathArray.length - 1;
		for (i = relPathArray.length; i >= 0; i--) {
			if (relPathArray[i] == "..") 
				bpPtr--;									// parent directory
			else if (rpPtr == 0 && relPathArray[rpPtr] == "")
				bpPtr = (prefix == "file://" ? 1 : 0);		// root directory
			else if (rpPtr > 0 && relPathArray[rpPtr - 1] != "..")
				rpPtr--;									// standard directory name
		}
	
			// marshall the array elements into a full path string, ignoring '.' references
		if (bpPtr >= 0) {
			for (i = 0; i <= bpPtr; i++) 
				fullPath += basePathArray[i] + pathDelim;
			for (i = rpPtr; i < relPathArray.length; i++) {
				if (relPathArray[i] != "." && relPathArray[i] != "")
					fullPath += relPathArray[i] + pathDelim;
			}
		}
		if (fullPath.length > 0)
			fullPath = prefix + fullPath.substr(0, fullPath.length - 1);  // trim trailing slash
	
		// alert("basePath: '" + basePathSafe + "'\n\nprefix: '" + prefix + "'\n\n" + peekInside(basePathArray, "basePathArray") + "\n\npointer: " + bpPtr);
		// alert("relPath: '" + relPathSafe + "'\n\n" + peekInside(relPathArray, "relPathArray") + "\n\npointer: " + rpPtr);
		// alert("got: '" + fullPath + "'");
	}
	return (fullPath); 
}


	// returns true if the string is an absolute or relative file specification
function isFileSpec (str) {
	var patternFound = false;
	patternFound |= (str.indexOf(":") == 1);
	return (patternFound);
}

	// returns true if the string is an absolute or relative URL specification
function isURL (str) {
	var patternFound = false;
	patternFound |= (str.indexOf("://") > 0);
	patternFound |= (str.indexOf("/") == 0);
	return (patternFound);
}

	// retrieve path specification without the filename (works for URLs and files)
function getPathSpec (str) {
	var ptr1 = str.lastIndexOf('/');
	var ptr2 = (str.substring(0, ptr1)).lastIndexOf('/');
	ptr1 = (str.indexOf("file:///") == 0 ? 8 : 0);
	if (str.indexOf("|") == 9) 
		str = str.substring(0, 9) + ":" + str.substring(10);	// handle Netscape peculiarity
	return (str.substring(ptr1, ptr2));
}

	// retrieve folder name without the filename or path (works for URLs and files)
function getFolderName () {
	var baseURL = document.location.href;
	var ptr2 = baseURL.lastIndexOf('/');
	var ptr1 = (baseURL.substring(0, ptr2)).lastIndexOf('/') + 1;
	return (baseURL.substring(ptr1, ptr2));
}

function getMajorVersion (str) {
	if (str.indexOf('.') == -1)
		num = parseFloat(str);
	else
		num = parseFloat(str.substring(0, str.indexOf('.')));
	return (isNaN(num) ? 0 : num);
}

function getMinorVersion (str, bNormalize) {
	if (bNormalize == null) bNormalize = false;
	if (str.indexOf('.') == -1) {
		num = 0;
	} else {
		str = str.substring(str.indexOf('.') + 1);  // focus on part after decimal
			// following block is a special handler for normalization, it
			// returns a normalized number for version numbers like '6.14.1.999',
			// ie- expands each segment to 3 digits left padded with zeros,
			// which is necessary for comparing minor versions (eg- '14001999')
		if (bNormalize && str.indexOf('.') != -1) {
			var str2 = str + "";
			var arr = str2.split('.');
			str = "";
			for (i = 0; i < arr.length; i++)
				str += ("000" + arr[i]).substring(arr[i].length);
		}
		num = parseFloat(str);
	}
	return (isNaN(num) ? 0 : num);
}

function round (number, precision) {
		// rounds number to X decimal places, defaults to precision of 2
	precision = (!precision ? 2 : precision);
	return (Math.round(number * Math.pow(10, precision)) / Math.pow(10, precision));
}
