/************************************************************
JBE General JavaScript Utilities

Platforms: IE 4.0+ / Netscape 4.5+

Dependencies
- trace() function must be declared (used for debugging - usually in config_system.js for course pages))

To Do
- None at this time

Revision History
Keys: [NEW] New Feature
      [FIX] Bug fix
      [COM] Comment
      [IMP] Improvement

01/12/06 Rick Englert
[NEW]  Added Macromedia's new (for version Flash 8) flash player detection script.

05/02/05 Rick Englert
[NEW]  Added randomize_array() function which provides multiple ways to randomize array elements

04/28/05 Rick Englert
[NEW] Added jbe_find_obj() function which can find an object by searching on more properties than just id
[IMP] added blnContains parameter to in_array() function

02/05/04 Rick Englert
[IMP] Added iMin argument to get_random() function. It can now generate a random number 
	between any two values, not just 0 and iMax.

11/18/03 Rick Englert
[NEW] Added jbeMM_FindObj() and jbeMM_PreloadImages() to JBE copies of MM image functions section.
[IMP] changed toggle_over() function to reference jbeMM_SwapImage() instead of MM_SwapImage()
[COM] No JBE functions should now be dependent on MM image functions being hard-coded on page.

11/12/02 Rick Englert
[IMP] get_querystring_value function now extracts querystring data pairs from any string/URL instead
	of assuming that the first character is the ?
	
11/04/02 Rick Englert
[FIX] get_tablearray_data function now correctly initializes sInputValue only if it is null and NOT 0.

09/04/02 Rick Englert
[FIX] get_string_element() function now returns the desired string element 
correctly when the element is more than 1 character long and blnReverse is true.

07/03/02 Rick Englert
[IMP] Added sFromThisURL argument to get_filename() function.  Now you can get 
	the filename out of any URL, not just one of the ones currently loaded in a window

06/15/02 Rick Englert
[NEW] Added get_tablearray_data function

06/14/02 12:25 Rick Englert
[IMP] added iSearchBlock and iStartAtIndex arguments to in_array() function
***************************************************************/

//-----------------------------------------------------------------------------------------------------
// String Functions
//-----------------------------------------------------------------------------------------------------
function replace_charAt(sString, iPosition, sCharacter, iFirstIndex) {   
//In string sString, replace character at position iPosition with sCharacter.
//use iFirstIndex to adjust start position if first character should not be called index 0.
//Usually this value will be 1
	if (!iFirstIndex) {iFirstIndex = 0}
	var iPos = parseInt(iPosition) - parseInt(iFirstIndex);
	return sString.substring(0, iPos) + sCharacter + sString.substring(iPos + 1)
}

function get_string_element(sString, iIndex, sDelimiter, blnReverse) {   
//gets the iIndexth element of a string delimited by sDelimiter (ex.get_string_element("5_4_1_7", 2, "_") returns "1")
//if blnReverse is true, it iIndex is counted from the end of the string, rather than the beginning
	if (sString) {
		if (sString.length > 0) {
			if (blnReverse) sString = reverse_string(sString)
			var aString = sString.split(sDelimiter)
			var sReturnValue = aString[iIndex];  //get the desired string element - remember, if blnReverse is true, the entire string is backwards at this point
			if (blnReverse) sReturnValue = reverse_string(sReturnValue)  //reverse the string element again back to its original format
			return sReturnValue;
		}
	}
//	log_item('ERROR: get_string_element(), string passed in is either undefined or 0 length.');
}

function num_string_elements(sString, sDelimiter) {   
//returns the number of elements of a string delimited by sDelimiter (ex.num_string_elements("5_4_1_7", "_") returns 4)
	if (sString) {
		if (sString.length > 0) {
			var aString = sString.split(sDelimiter)
			return aString.length;
		}
	}
	log_item('ERROR: get_string_element(), string passed in is either undefined or 0 length.');
}

// pad a string
function pad_string(sStringToPad, sMode, sChar, iLength) {
	var i, sNewString = ''
	
	for (i = 0; i < iLength - sStringToPad.length; i++) {
		sNewString += sChar
	}
	
	if (sMode == 'left') {
		return (sNewString + sStringToPad)
	}
	else if (sMode == 'right') {
		return (sStringToPad + sNewString)
	}
	else {
		return sStringToPad
	}
}

function reverse_string(sString) {
	var sStringNew = ''
	for (var i = sString.length - 1; i >= 0; i--) {
		sStringNew += sString.charAt(i)
	}
	return sStringNew
}


//-----------------------------------------------------------------------------------------------------
// URL Functions
//-----------------------------------------------------------------------------------------------------
function get_querystring_value(sQueryString, sParameter)
{
	var sParameters
	var aParametersArray

	var sSingleParameter
	var aSingleParameterArray

	var sParameterName
	var sParameterValue

	// get parameters from querystring
	sParameters = sQueryString.toString();

	//strip off starting question mark and everything preceding it, if found
	if (sParameters.indexOf('?') > -1) {
		sParameters = sParameters.substr(sParameters.indexOf('?')+1);
	}
	
	//convert parameters into parameter array
	aParametersArray = sParameters.split('&');

	//loop through all parameters looking for one named sParameter
	for (var iCntr = 0; iCntr <= (aParametersArray.length - 1); iCntr++)
	{
		//get this parameter
		sSingleParameter = aParametersArray[iCntr];

		//split this parameter into name and value
		aSingleParameterArray = sSingleParameter.split('=');
		sParameterName = aSingleParameterArray[0];
		sParameterValue = aSingleParameterArray[1];

		//check if this is the parameter passed in, if so
		//return its value
		if (sParameterName.toLowerCase() == sParameter.toLowerCase())
		{
			return unescape(sParameterValue);
		}
	}

	return '';
}

function querystring_to_csv_names(sQueryString, numElementsToOmit, isFromFront) {  //convenience/readable interface for querystring_to_csv
	return querystring_to_csv(sQueryString, true, numElementsToOmit, isFromFront);
}
function querystring_to_csv_values(sQueryString, numElementsToOmit, isFromFront) {  //convenience/readable interface for querystring_to_csv
	return querystring_to_csv(sQueryString, false, numElementsToOmit, isFromFront);
}
function querystring_to_csv(sQueryString, isName, numElementsToOmit, isFromFront) {
	//numElementsToOmit lets you omit a number of querystring elements from the returnvalue
	//isFromFront lets you omit from the front or back of querystring
	//use these where querystring has extra elements you don't want
	var sParameters
	var aParametersArray

	var aSingleParameterArray
	
	var aNames = new Array();  //holds names output
	var aValues = new Array();  //holds values output
	
	//if querystring not specified, get it from current url
	if (!sQueryString) sQueryString = document.location.search;
	//if numElementsToOmit not specified, default it
	if (!numElementsToOmit) numElementsToOmit = 0;

	// get parameters from querystring
	sParameters = sQueryString.toString();

	//strip off starting question mark and everything preceding it, if found
	if (sParameters.indexOf('?') > -1) {
		sParameters = sParameters.split('?')[1];
	}
	
	//convert parameters into parameter array
	aParametersArray = sParameters.split('&');

	//loop through all parameters looking for one named sParameter
	for (var iCntr=0; iCntr<aParametersArray.length; iCntr++)
	{
		//get this parameter and split this parameter into name and value
		aSingleParameterArray = aParametersArray[iCntr].split('=');
		if ((isFromFront&&iCntr>=numElementsToOmit) || (!isFromFront&&iCntr<aParametersArray.length-numElementsToOmit)) {
			aNames[iCntr] = aSingleParameterArray[0];
			aValues[iCntr] = aSingleParameterArray[1];
		}
	}
	if (isName) {
		return aNames.join(",");
	} else {
		return aValues.join(",");
	}
}

function get_path_delimiter(sProtocol) {
	var sPathDelimiter;
	if (!sProtocol) sProtocol = document.location.protocol;
	(sProtocol == 'http:') ? sPathDelimiter = '/' : (is_ie) ? sPathDelimiter = '\\' : sPathDelimiter = '/';
	return sPathDelimiter;
}

function get_filename(blnWithExtension, oWin, sFromThisURL) {
	if (!oWin) {
		oWin = self;
	}
	var sURL, sReturnVal;
	if (!sFromThisURL) { sURL = oWin.document.location.href	}
	else { sURL = sFromThisURL; }
	if (blnWithExtension) {
		sReturnVal = sURL.substring(sURL.lastIndexOf('/')+1);
	}
	else {
		sReturnVal = sURL.substring(sURL.lastIndexOf('/')+1);
		sReturnVal = sReturnVal.substring(0,sReturnVal.lastIndexOf('.'));
	}
	return sReturnVal;
}

function get_path(bWithFilename, oWin) {
	if (!oWin) {
		oWin = self;
	}
	var sPathname = oWin.document.location.pathname, sFilename = get_filename(oWin);
	if (bWithFilename) {
		return sPathname;
	}
	else {
		var re = new RegExp(sFilename, 'gi');
		sPathname = sPathname.replace(re,'');
		return sPathname;
	}
}

//This function probably does not work.  Nice idea, but abandoned because not needed any more.
//May get developed further if needed on a later project
function get_root_folder(iNumFoldersHigher, oWin) {
//Returns the folder containing the file executing this function if iNumFoldersHigher not set
//iNumFoldersHigher allows you to get a folder iNumFoldersHigher higher than the folder 
//containing the file calling the function
	if (!iNumFoldersHigher) {
		var sReturnValue = get_path(0, oWin)
	}
	else {
		for (var i=1; i<=iNumFoldersHigher; i++) {
			sReturnValue = sReturnValue.substr(0, sReturnValue.length);
			sReturnValue = sReturnValue.substring(0, sReturnValue.lastIndexOf('/')+1);
		}
	}
	return sReturnValue;
}

//-----------------------------------------------------------------------------------------------------
// Array Functions
//-----------------------------------------------------------------------------------------------------

// A tablearray is a single array where the data is arranged in rows and columns, like a database table.
// It must be accompanied by a ColumnHeading array which names the column headings used to access the data.
// sInputField must match one of these column headings.  sOutputField must match one of these column headings
// or one of the following reserved words: "~firstRecord", "~lastRecord", "~prevRecord", "~nextRecord"
function get_tablearray_data(sOutputField, sArrayName, sInputField, sInputValue) {
	if (!sInputValue && sInputValue != 0) sInputValue = '';
//trace('1 start get_tablearray_data(): ' + sOutputField  + ', ' +  sArrayName + ', ' +  sInputField + ', ' + sInputValue);
	var iNumColumns = eval(sArrayName + 'ColumnHeadings.length');
	var iInputColumnIndex = eval('in_array(' + sArrayName + 'ColumnHeadings, sInputField)');
	if (iInputColumnIndex == -1) {alert('jbe_jslib.js: get_tablearray_data(): ' + sInputField + ' not in ' + sArrayName + 'ColumnHeadings array'); return}
	
	if (sOutputField == "~firstRecord") {
		return eval(sArrayName + '[iInputColumnIndex]');
	}
	else if (sOutputField == "~lastRecord") {
		var iArrayLength = eval(sArrayName + '.length');
//		return eval(sArrayName + '[iArrayLength-1 - iNumColumns + iInputColumnIndex]');    //not sure what happened.  I'm sure this used to work and then stopped on 6/21/02
		return eval(sArrayName + '[iArrayLength - iNumColumns + iInputColumnIndex]');
	}
	else if (sOutputField == "~prevRecord") {
		var iIndexOffset = 0-iNumColumns;
	}
	else if (sOutputField == "~nextRecord") {
		var iIndexOffset = iNumColumns;
	}
	else {
		var iOutputColumnIndex = eval('in_array(' + sArrayName + 'ColumnHeadings, sOutputField)');
		if (iOutputColumnIndex == -1) {alert('jbe_jslib.js: get_tablearray_data(): ' + sOutputField + ' is not in ' + sArrayName + 'ColumnHeadings array and does not match the four reserved words ~firstRecord ~lastRecord ~prevRecord ~nextRecord'); return}
		var iIndexOffset = iOutputColumnIndex - iInputColumnIndex;
	}
	var iInputValueIndex = eval('in_array(' + sArrayName + ', sInputValue, 0, iNumColumns, iInputColumnIndex)');
//trace('iInputValueIndex: ' + iInputValueIndex + ', iIndexOffset: '+iIndexOffset);
	if (iInputValueIndex == -1) {trace('jbe_jslib.js: get_tablearray_data(): ' + sInputValue + ' not in ' + sArrayName + ' array'); return}
//trace('2 start get_tablearray_data(): ' + sOutputField  + ', ' +  sArrayName + ', ' +  sInputField + ', ' + sInputValue);

	if (iInputValueIndex + iIndexOffset < 0) {  //sInputField is beginning of file
		return '~bof';
	}
	else if (iInputValueIndex + iIndexOffset > eval(sArrayName + '.length-1')) {  //sInputField is end of file
		return '~eof';
	}
	else {
//trace(sOutputField + ': ' + iInputValueIndex + iIndexOffset);
		return eval(sArrayName + '[iInputValueIndex + iIndexOffset]');
	}
}

// search the needle in the haystack and return the index, -1 if not found
//    iSearchBlock allows you to search every nth record to increase speed where appropriate.
//  Use this on tablearrays to search only within one column.  To do that, iSearchBlock must equal
//  the number of column headings.  It's a good idea to use aHaystackColumnHeadings.length instead of 
//  hard coding the number in case new columns get added or removed to/from aHaystack.  Defaults to 1 (search every array element)
//    iStartAtIndex works in conjunction with iSearchBlock and sets the index to start the search at.
//  ie which column to search.  Defaults to 0.
//    blnContains allows a match to be made if an element of aHaystack contains sNeedle (i.e. if 
//  sNeedle can be found within an element, but not equal the entire element.
function in_array(aHaystack, sNeedle, blnCaseSensitive, iSearchBlock, iStartAtIndex, blnContains) {
	if (sNeedle!=undefined) sNeedle = sNeedle.toString();
	if (!blnCaseSensitive && sNeedle) sNeedle = sNeedle.toLowerCase();
	if (!iSearchBlock) {iSearchBlock = 1}
	if (!iStartAtIndex) {iStartAtIndex = 0}
	for (var i = iStartAtIndex; i < aHaystack.length; i += iSearchBlock) {
		if (aHaystack[i]!=undefined) {
			var sElement = blnCaseSensitive ? aHaystack[i].toString() : aHaystack[i].toString().toLowerCase();
			if (blnContains) {
				if (sElement.indexOf(sNeedle) > -1) return i;
			}
			else {
//alert('in_array - matching whole element: \''+	sElement+sElement.length + '\', \''+sNeedle+sNeedle.length+'\', sElement==sNeedle='+ismatch);
				if (sElement == sNeedle) return i;
			}
		}
	}
//alert(sNeedle+' not found in '+aHaystack);				
	return -1
}
/*  old version - just in case - 4/28/05
function in_array(aHaystack, sNeedle, blnCaseSensitive, iSearchBlock, iStartAtIndex) {
	if (!iSearchBlock) {iSearchBlock = 1}
	if (!iStartAtIndex) {iStartAtIndex = 0}
	for (var i = iStartAtIndex; i < aHaystack.length; i = i + iSearchBlock) {
		if (blnCaseSensitive) {
			if (aHaystack[i].toString() == sNeedle.toString()) {
				return i
			}
		}
		else {
			if (aHaystack[i].toString().toLowerCase() == sNeedle.toString().toLowerCase()) {
				return i
			}
		}
	}
	return -1
}
*/

//randomize_array:  This function randomizes an array in one of two ways:
//the argument ArrayOrInteger, as you can deduce, can be either an array 
//or an integer.  If it is an array, it returns the actual array elements
//in random order.  If it is an integer, it returns a randomized list of 
//indices of length ArrayOrInteger.  This is useful for creating a router for
//access to two or more complementary arrays (test choices and feedback,
//for example)
//iOutputLength is optional.  It determines how many of the input array elements
//to return as output if not the whole array.
function randomize_array(ArrayOrInteger, iOutputLength) {
	var aInput = new Array();
	var aOutput = new Array();
	//check if array or integer and setup aInput accordingly
	if (ArrayOrInteger.length == undefined || ArrayOrInteger.length == 1) {// && ArrayOrInteger.indexOf(/\D/gi) == -1) { //integer
		for (var i=0; i<ArrayOrInteger; i++) aInput[i] = i;  //populate source array
	} else { //array
		aInput = ArrayOrInteger;
	}
	if (!iOutputLength) iOutputLength = aInput.length;
	for (var i=0; i<=iOutputLength; i++) {
		var j = random(aInput.length);
		aOutput.push(aInput[j])
		aInput.splice(j, 1);
//trace("Input:"+aInput)
//trace("Output:"+aOutput)
	}
	return aOutput;
}

//dhtmlLoadScript:  dynamically load javascript file
//see http://www.codehouse.com/javascript/articles/external/
//they called it dhtmlLoadScript()
function loadJavaScript(url)
{
   var e = document.createElement("script");
   e.src = url;
   e.type="text/javascript";
   document.getElementsByTagName("head")[0].appendChild(e);
}

//-----------------------------------------------------------------------------------------------------
// JBE copies of MM image functions
//-----------------------------------------------------------------------------------------------------
function jbeMM_swapImgRestore() { //v3.0
  var i,x,a=document.jbeMM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
}

function jbeMM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
    var i,j=d.MM_p.length,a=jbeMM_preloadImages.arguments; for(i=0; i<a.length; i++)
    if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}

function jbeMM_swapImage() { //v3.0
  var i,j=0,x,a=jbeMM_swapImage.arguments; document.jbeMM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
   if ((x=jbeMM_findObj(a[i]))!=null){document.jbeMM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
}

function jbeMM_findObj(n, d) { //v4.01
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=jbeMM_findObj(n,d.layers[i].document);
  if(!x && d.getElementById) x=d.getElementById(n); return x;
}
//-----------------------------------------------------------------------------------------------------
// Miscellaneous Functions
//-----------------------------------------------------------------------------------------------------

function show_props(obj, objName) {
	var sResult = ''
	for (var i in obj) {
		sResult += objName + '.' + i + ' = ' + obj[i] + '\n'
	}
	return sResult;
}
function open_window(sURL, sWinName, sFeatures) {
	var oWin, sFullWinFeatures = 'fullscreen=1,toolbar=no,location=no,status=no,menubar=no,scrollbars=no';
	if (sFeatures != 'full') {
		oWin = window.open(sURL, sWinName, sFeatures);
	}
	else {
		oWin = window.open(sURL, sWinName, sFullWinFeatures);
	}
	oWin.focus();
	return oWin;
}

function toggle_over(sButtonName, bToggleState, sONStateImg, sOFFStateImg) {
	if (bToggleState) {
		jbeMM_swapImage(sButtonName,'',sONStateImg,1);
	}
	else {
		jbeMM_swapImage(sButtonName,'',sOFFStateImg,1);
	}
}

function toggle_click(sButtonName, bToggleState, sONStateImg, sOFFStateImg) {
	bNewToggleState = ~bToggleState
	toggle_over(sButtonName, bNewToggleState, sONStateImg, sOFFStateImg);
	return bNewToggleState;
}

//Returns a random integer between iMin and iMax. iMin is optional and defaults to 0
function get_random(iMax, iMin) {
	if (!iMin) iMin = 0;  //this argument is optional
	if (iMin > iMax) {  //defensive programming, swap values if iMin is greater than iMax
		var temp = iMax;
		iMax = iMin;
		iMin = temp;
	}
	return Math.round(Math.random() * (iMax-iMin)) + iMin
//	return Math.round(Math.random()*iMax)
}

//a variation on MMFindObj - use this to find object by any attribute, 
//but you must know the tagname.  By specifying sAttribute as 'innerHTML' or 
//'innerText', you can search by these as well, even though they're not technically 
//attributes.  By default, sValue must be an exact, complete match of the attribute
//being searched for.  To find elements where sAttribute CONTAINS sValue, set bContains
//to true.  This applies mostly to innerText/innerHTML searches
function jbe_find_obj(sTagName, sAttribute, sValue, bContains) {  
	var a = document.getElementsByTagName(sTagName.toUpperCase());
	for (var i=0; i<a.length; i++) {
//alert('function jbe_find_obj: '+ i+ ': '+a[i].outerHTML);
		if (sAttribute == 'innerText' || sAttribute == 'innerHTML') {
//alert(i+': '+a[i].outerHTML);
//alert('a[i].'+sAttribute);
//alert(eval('a[i].'+sAttribute));
			if (bContains && eval('a[i].'+sAttribute)) { if (eval('a[i].'+sAttribute).indexOf(sValue) > -1) return a[i] }
			else { if (eval('a[i].'+sAttribute) == sValue) return a[i] }
		}
		else {
			if (bContains && a[i].getAttribute(sAttribute)) { if (a[i].getAttribute(sAttribute).indexOf(sValue) > -1) return a[i] }
			else { if (a[i].getAttribute(sAttribute)==sValue) return a[i] }
		}
	}
	return false;
}



//-----------------------------------------------------------------------------------------------------
// Flash player version detection
//-----------------------------------------------------------------------------------------------------
function MM_CheckFlashVersion(reqVerStr,msg){
  with(navigator){
    var isIE  = (appVersion.indexOf("MSIE") != -1 && userAgent.indexOf("Opera") == -1);
    var isWin = (appVersion.toLowerCase().indexOf("win") != -1);
    if (!isIE || !isWin){  
      var flashVer = -1;
      if (plugins && plugins.length > 0){
        var desc = plugins["Shockwave Flash"] ? plugins["Shockwave Flash"].description : "";
        desc = plugins["Shockwave Flash 2.0"] ? plugins["Shockwave Flash 2.0"].description : desc;
        if (desc == "") flashVer = -1;
        else{
          var descArr = desc.split(" ");
          var tempArrMajor = descArr[2].split(".");
          var verMajor = tempArrMajor[0];
          var tempArrMinor = (descArr[3] != "") ? descArr[3].split("r") : descArr[4].split("r");
          var verMinor = (tempArrMinor[1] > 0) ? tempArrMinor[1] : 0;
          flashVer =  parseFloat(verMajor + "." + verMinor);
        }
      }
      // WebTV has Flash Player 4 or lower -- too low for video
      else if (userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 4.0;

      var verArr = reqVerStr.split(",");
      var reqVer = parseFloat(verArr[0] + "." + verArr[2]);
  
      if (flashVer < reqVer){
        if (confirm(msg))
          window.location = "http://www.macromedia.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash";
      }
    }
  } 
}

//-----------------------------------------------------------------------------------------------------
// Protocol sniffing
//-----------------------------------------------------------------------------------------------------
var is_http = (location.protocol == 'http:') ? true : false;
var is_file = (location.protocol == 'file:/') ? true : false;

var gsPathDelimiter = get_path_delimiter(location.protocol);
var psFilename = get_filename(false), psFullFilename = get_filename(true);

//--------------------------------------------------------------------------------------------
// JavaScript Browser Sniffer
// Eric Krok, Andy King, Michel Plungjan 010329
// see
// http://www.webreference.com/ for more information
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
// please send any improvements to aking@internet.com and we'll
// roll the best ones in
//
// adapted from Netscape's Ultimate client-side JavaScript client sniff.
// and andy's sniffer
// Revised May 7 99 to add is.nav5up and is.ie5up (see below). (see below).
// Revised June 11 99 to add addit props, checks
// Revised June 23 99 added screen props - gecko m6 doesn't support yet - abk
//                    converted to var is_ from is object to work everywhere
// 990624 - added cookie forms links frames checks - abk
// 001031 - ie4 mod 5.0 -> 5. (ie5.5 mididentified - abk)
//          is_ie4 mod tp work with ie6+ - abk
// 001120 - ns6 released, document.layers false, put back in
//        - is_nav6 test added - abk
// 001121 - ns6+ added, used document.getElementById, better test, dom-compl
// 010117 - actual version for ie3-5.5 by Michel Plungjan
// 010118 - actual version for ns6 by Michel Plungjan
// 010217 - netscape 6/mz 6 ie5.5 onload defer bug docs - abk
//
//
// Everything you always wanted to know about your JavaScript client
// but were afraid to ask. Creates "is_" variables indicating:
// (1) browser vendor:
//     is_nav, is_ie, is_opera
// (2) browser version number:
//     is_major (integer indicating major version number: 2, 3, 4 ...)
//     is_minor (float   indicating full  version number: 2.02, 3.01, 4.04 ...)
// (3) browser vendor AND major version number
//     is_nav2, is_nav3, is_nav4, is_nav4up, is_nav5, is_nav5up, is_ie3, is_ie4, is_ie4up
// (4) JavaScript version number:
//     is_js (float indicating full JavaScript version number: 1, 1.1, 1.2 ...)
// (5) OS platform and version:
//     is_win, is_win16, is_win32, is_win31, is_win95, is_winnt, is_win98
//     is_os2
//     is_mac, is_mac68k, is_macppc
//     is_unix
//        is_sun, is_sun4, is_sun5, is_suni86
//        is_irix, is_irix5, is_irix6
//        is_hpux, is_hpux9, is_hpux10
//        is_aix, is_aix1, is_aix2, is_aix3, is_aix4
//        is_linux, is_sco, is_unixware, is_mpras, is_reliant
//        is_dec, is_sinix, is_freebsd, is_bsd
//     is_vms
//
// See http://www.it97.de/JavaScript/JS_tutorial/bstat/navobj.html and
// http://www.it97.de/JavaScript/JS_tutorial/bstat/Browseraol.html
// for detailed lists of userAgent strings.

//
// Note: you don't want your Nav4 or IE4 code to "turn off" or
// stop working when Nav5 and IE5 (or later) are released, so
// in conditional code forks, use is_nav4up ("Nav4 or greater")
// and is_ie4up ("IE4 or greater") instead of is_nav4 or is_ie4
// to check version in code which you want to work on future
// versions.
//

    // convert all characters to lowercase to simplify testing
    var agt=navigator.userAgent.toLowerCase();
    var appVer = navigator.appVersion.toLowerCase();

    // *** BROWSER VERSION ***

    var is_minor = parseFloat(appVer);
    var is_major = parseInt(is_minor);

    // Note: On IE, start of appVersion return 3 or 4
    // which supposedly is the version of Netscape it is compatible with.
    // So we look for the real version further on in the string

    var iePos  = appVer.indexOf('msie');
    if (iePos !=-1) {
       is_minor = parseFloat(appVer.substring(iePos+5,appVer.indexOf(';',iePos)))
       is_major = parseInt(is_minor);
    }

    // Netscape6 is mozilla/5 + Netscape6/6.0!!!
    // Mozilla/5.0 (Windows; U; Win98; en-US; m18) Gecko/20001108 Netscape6/6.0
    var nav6Pos = agt.indexOf('netscape6');
    if (nav6Pos !=-1) {
       is_minor = parseFloat(agt.substring(nav6Pos+10))
       is_major = parseInt(is_minor)
    }

    var is_getElementById   = (document.getElementById) ? "true" : "false"; // 001121-abk
    var is_getElementsByTagName = (document.getElementsByTagName) ? "true" : "false"; // 001127-abk
    var is_documentElement = (document.documentElement) ? "true" : "false"; // 001121-abk

    var is_nav  = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
                && (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
                && (agt.indexOf('webtv')==-1));
    var is_nav2 = (is_nav && (is_major == 2));
    var is_nav3 = (is_nav && (is_major == 3));
    var is_nav4 = (is_nav && (is_major == 4));
    var is_nav4up = (is_nav && (is_major >= 4));
    var is_navonly      = (is_nav && ((agt.indexOf(";nav") != -1) ||
                          (agt.indexOf("; nav") != -1)) );

/*	var is_nav6 = (is_nav && (agt.indexOf('netscape6') != -1)); // new 001120 - abk
	var is_nav6up = (is_nav && is_getElementById);              // new 001121 - abk
*/
	var is_nav6   = (is_nav && is_major==6);    // new 010118 mhp
	var is_nav6up = (is_nav && is_minor >= 6) // new 010118 mhp

    var is_nav5   = (is_nav && is_major == 5 && !is_nav6); // checked for ns6
    var is_nav5up = (is_nav && is_minor >= 5);

    var is_ie   = (iePos!=-1);
    var is_ie3  = (is_ie && (is_major < 4));

    var is_ie4   = (is_ie && is_major == 4);
    var is_ie4up = (is_ie && is_minor >= 4);
    var is_ie5   = (is_ie && is_major == 5);
    var is_ie5up = (is_ie && is_minor >= 5);

// KNOWN BUG: On AOL4, returns false if IE3 is embedded browser
    // or if this is the first browser window opened.  Thus the
    // variables is_aol, is_aol3, and is_aol4 aren't 100% reliable.

    var is_aol   = (agt.indexOf("aol") != -1);
    var is_aol3  = (is_aol && is_ie3);
    var is_aol4  = (is_aol && is_ie4);

    var is_opera = (agt.indexOf("opera") != -1);
    var is_webtv = (agt.indexOf("webtv") != -1);

    // *** JAVASCRIPT VERSION CHECK ***
    // Useful to workaround Nav3 bug in which Nav3
    // loads <SCRIPT LANGUAGE="JavaScript1.2">.
    var is_js;
    if (is_nav2 || is_ie3) is_js = 1.0
    else if (is_nav3 || is_opera) is_js = 1.1
    else if ((is_nav4 && (is_minor <= 4.05)) || is_ie4) is_js = 1.2
    else if ((is_nav4 && (is_minor > 4.05)) || is_ie5) is_js = 1.3
    else if (is_nav5 && !(is_nav6)) is_js = 1.4
    else if (is_nav6) is_js = 1.5

    // NOTE: In the future, update this code when newer versions of JS
    // are released. For now, we try to provide some upward compatibility
    // so that future versions of Nav and IE will show they are at
    // *least* JS 1.x capable. Always check for JS version compatibility
    // with > or >=.

    else if (is_nav && (is_major > 5)) is_js = 1.4
    else if (is_ie && (is_major > 5)) is_js = 1.3
    // HACK: no idea for other browsers; always check for JS version with > or >=
    else is_js = 0.0;

    // *** PLATFORM ***
    var is_win   = ( (agt.indexOf("win")!=-1) || (agt.indexOf("16bit")!=-1) );
    // NOTE: On Opera 3.0, the userAgent string includes "Windows 95/NT4" on all
    //        Win32, so you can't distinguish between Win95 and WinNT.
    var is_win95 = ((agt.indexOf("win95")!=-1) || (agt.indexOf("windows 95")!=-1));

    // is this a 16 bit compiled version?
    var is_win16 = ((agt.indexOf("win16")!=-1) ||
               (agt.indexOf("16bit")!=-1) || (agt.indexOf("windows 3.1")!=-1) ||
               (agt.indexOf("windows 16-bit")!=-1) );

    var is_win31 = ((agt.indexOf("windows 3.1")!=-1) || (agt.indexOf("win16")!=-1) ||
                    (agt.indexOf("windows 16-bit")!=-1));

    // NOTE: Reliable detection of Win98 may not be possible. It appears that:
    //       - On Nav 4.x and before you'll get plain "Windows" in userAgent.
    //       - On Mercury client, the 32-bit version will return "Win98", but
    //         the 16-bit version running on Win98 will still return "Win95".
    var is_win98 = ((agt.indexOf("win98")!=-1) || (agt.indexOf("windows 98")!=-1));
    var is_winnt = ((agt.indexOf("winnt")!=-1) || (agt.indexOf("windows nt")!=-1));
    var is_win32 = (is_win95 || is_winnt || is_win98 ||
                    ((is_major >= 4) && (navigator.platform == "Win32")) ||
                    (agt.indexOf("win32")!=-1) || (agt.indexOf("32bit")!=-1));

    var is_os2   = ((agt.indexOf("os/2")!=-1) ||
                    (navigator.appVersion.indexOf("OS/2")!=-1) ||
                    (agt.indexOf("ibm-webexplorer")!=-1));

    var is_mac    = (agt.indexOf("mac")!=-1);
    var is_mac68k = (is_mac && ((agt.indexOf("68k")!=-1) ||
                               (agt.indexOf("68000")!=-1)));
    var is_macppc = (is_mac && ((agt.indexOf("ppc")!=-1) ||
                                (agt.indexOf("powerpc")!=-1)));

    var is_sun   = (agt.indexOf("sunos")!=-1);
    var is_sun4  = (agt.indexOf("sunos 4")!=-1);
    var is_sun5  = (agt.indexOf("sunos 5")!=-1);
    var is_suni86= (is_sun && (agt.indexOf("i86")!=-1));
    var is_irix  = (agt.indexOf("irix") !=-1);    // SGI
    var is_irix5 = (agt.indexOf("irix 5") !=-1);
    var is_irix6 = ((agt.indexOf("irix 6") !=-1) || (agt.indexOf("irix6") !=-1));
    var is_hpux  = (agt.indexOf("hp-ux")!=-1);
    var is_hpux9 = (is_hpux && (agt.indexOf("09.")!=-1));
    var is_hpux10= (is_hpux && (agt.indexOf("10.")!=-1));
    var is_aix   = (agt.indexOf("aix") !=-1);      // IBM
    var is_aix1  = (agt.indexOf("aix 1") !=-1);
    var is_aix2  = (agt.indexOf("aix 2") !=-1);
    var is_aix3  = (agt.indexOf("aix 3") !=-1);
    var is_aix4  = (agt.indexOf("aix 4") !=-1);
    var is_linux = (agt.indexOf("inux")!=-1);
    var is_sco   = (agt.indexOf("sco")!=-1) || (agt.indexOf("unix_sv")!=-1);
    var is_unixware = (agt.indexOf("unix_system_v")!=-1);
    var is_mpras    = (agt.indexOf("ncr")!=-1);
    var is_reliant  = (agt.indexOf("reliantunix")!=-1);
    var is_dec   = ((agt.indexOf("dec")!=-1) || (agt.indexOf("osf1")!=-1) ||
           (agt.indexOf("dec_alpha")!=-1) || (agt.indexOf("alphaserver")!=-1) ||
           (agt.indexOf("ultrix")!=-1) || (agt.indexOf("alphastation")!=-1));
    var is_sinix = (agt.indexOf("sinix")!=-1);
    var is_freebsd = (agt.indexOf("freebsd")!=-1);
    var is_bsd = (agt.indexOf("bsd")!=-1);
    var is_unix  = ((agt.indexOf("x11")!=-1) || is_sun || is_irix || is_hpux ||
                 is_sco ||is_unixware || is_mpras || is_reliant ||
                 is_dec || is_sinix || is_aix || is_linux || is_bsd || is_freebsd);

    var is_vms   = ((agt.indexOf("vax")!=-1) || (agt.indexOf("openvms")!=-1));
// additional checks, abk
	var is_anchors = (document.anchors) ? "true":"false";
	var is_regexp = (window.RegExp) ? "true":"false";
	var is_option = (window.Option) ? "true":"false";
	var is_all = (document.all) ? "true":"false";
// cookies - 990624 - abk
	document.cookie = "cookies=true";
	var is_cookie = (document.cookie) ? "true" : "false";
	var is_images = (document.images) ? "true":"false";
	var is_layers = (document.layers) ? "true":"false"; // gecko m7 bug?
// new doc obj tests 990624-abk
	var is_forms = (document.forms) ? "true" : "false";
	var is_links = (document.links) ? "true" : "false";
	var is_frames = (window.frames) ? "true" : "false";
	var is_screen = (window.screen) ? "true" : "false";

// java
	var is_java = (navigator.javaEnabled());

