// Copyright (c) 1998-2008 Araxis Ltd. All rights reserved.
//
// This Javascript file handles events associated with loading and unloading a HTML page
// and also contains common functions.

var g_i10n = new I10nCtor();

var g_sElementForceModifiedFormPrefix = "ktForceDirty";
var g_sElementOrigPrefix = "ktOrig";
var g_sElementSelectedPrefix = "ktSel";
var g_sElementButtonPrefix = "ktButton";
var g_sElementSubmitOnChangeActionPrefix = "ktSubmitOnChangeAction";
var g_sElementTransientMessagePrefix = "npChangesApplied";

var g_sDefaultButtonName = "ktDefaultButton";
var g_sUnsupportedBrowserErrorPage = "/Error/ErrorF.xml?error=-103";
var g_sXmlHttpRequestObjectUnsupportedByBrowserErrorPage = "/Error/ErrorF.xml?error=-110";
var g_sUnsupportedInternetExplorerVersionErrorPage = "/Error/ErrorF.xml?error=-111";
var g_dLowestSupportedInternetExplorerVersion = 5.0;
var g_fAlreadyDisplayedUnloadMessageToUser =  false;
var g_iTimerIdForChangesAppliedMessage = null;
var g_elementChangesAppliedMessage = null;
var g_pageStateTimerId = null;

// we'll treat this as an associative array storing XMLHTTPRequest objects
var g_aobjXmlHttpRequests = new Object();

// we'll treat this as an associative array storing tab id to tab content article URL mappings
var g_TabDynamicContentHref = new Object();

var g_TabDynamicContentCallback = new Object();

// run CSS switcher before the page body is loaded to prevent style flash
if (typeof(setBodyCssClass) != "undefined")
		setBodyCssClass();
		
// we'll treat this as an associative array storing a string of comma separated element names
// for bool fields whose checked state should be synced to the enabled state of other form
// elements, the array has the form:
//   bool_field_name -> bool_field_id -> comma_separated_element_names
var g_aBoolFieldSyncInfo = new Object();		

function onLoad()
{
	if (typeof(checkForUnsupportedBrowsers) != "undefined")
		checkForUnsupportedBrowsers();	

	if (typeof(setTabControlsFromPersistedState) != "undefined")
		setTabControlsFromPersistedState();
	
	if (typeof(initShowHide) != "undefined")
		initShowHide(); 

	if (typeof(setShowHidesFromPersistedState) != "undefined")
		setShowHidesFromPersistedState(); 

	if (typeof(setPageState) != "undefined" && typeof(clearModifiedMessageTimeout) != "undefined")
	{
		if (g_pageStateTimerId != null)
			window.clearTimeout(g_pageStateTimerId);
	
		// do this asynchronously
		g_pageStateTimerId = window.setTimeout("setPageState();", 100);
	}

	//if (typeof(stripeRowsInTablesOfClass) != "undefined")
	//    stripeRowsInTablesOfClass("results");		
	
	if (typeof(highlightCheckedTableRows) != "undefined")
	  highlightCheckedTableRows();
	
	if (typeof(initSearchFields) != "undefined")
		initSearchFields();
		
	if (typeof(syncEnabledStatesForAllBoolFields) != "undefined")
		syncEnabledStatesForAllBoolFields();
}


function onBeforeUnload()
{
	if (typeof(closePopupWindow) != "undefined")
	{
	  closePopupWindow();
	}
	
	if (typeof(okToLeave) != "undefined" && !okToLeave())
	{
		g_fAlreadyDisplayedUnloadMessageToUser = true;			
		return g_i10n.sMessageNavigateAway;		
	}
}


function onUnload()
{
	if (typeof(clearModifiedMessageTimeout) != "undefined" && g_pageStateTimerId != null)
	{
		window.clearTimeout(g_pageStateTimerId);
		g_pageStateTimerId = null;
	}

	if (typeof(saveTabControlStateChanges) != "undefined")
		saveTabControlStateChanges();

	if (typeof(saveShowHideStateChanges) != "undefined")
		saveShowHideStateChanges();
		
	if (typeof(clearModifiedMessageTimeout) != "undefined")
		clearModifiedMessageTimeout();

	if (typeof(okToLeave) != "undefined" && !okToLeave () && !g_fAlreadyDisplayedUnloadMessageToUser)
		alert(g_i10n.sMessageChangesNotApplied);
}


// massages the value of an element in the following way:
// 1. normalizes the line endings (due to cross platform/browser differences)
// 2. replaces ASCII spaces with non-breaking-spaces for Netscape based browsers
function getElementValue(element)
{
	var sValue = null;
	sValue = normalizeLineEndings(element.value.toString());
	sValue = sValue.replace( /\u0020/g,"\u00A0");
	return sValue;
}


// normalizes line endings
function normalizeLineEndings(sText)
{
	return sText.replace(/\r*\n/g,"\n");
}


// a cross browser way of getting an element by its id
function getElementUsingId(id)
{
	if (!document)
		return null;
		
	// if browser implements part of W3C DOM HTML
	if (document.getElementById) 
		return document.getElementById(id);
	
	// if browser is Internet Explorer 4 or Opera with IE user agent
	if (document.all)
		return document.all[id];
	
	// either the element does not exist, or browser does not support any
	//of the above means of getting an element by its id
	return null;
}


// Returns an array of elements of a given class name.
//
// param clsName the name of the class
// param parentEle the parent element
// param tagName the tag name
function getElementsByClassName(clsName, parentEle, tagName)
{
	var found = new Array();
	var elements = null;
	var re = new RegExp('\\b' + clsName + '\\b');

	if (!parentEle)
		return found;
		
	if (!parentEle)
		return found;

	if (!tagName)
		tagName = '*';

	if (parentEle.getElementsByTagName)
		elements = parentEle.getElementsByTagName(tagName);
	else if (document.all && document.all.tags)
		elements = document.all.tags(tagName);

	if (elements)
	{
		for (var i = 0; i < elements.length; ++i)
		{
			if (elements[i].className.search(re) != -1)
				found[found.length] = elements[i];
		}
	}

	return found;
}


// A cross browser function for returning elements that include a namespace by tag name.
//
// param local the element name minus the namespace prefix
// param elementParent the parent element
// returns elements by tag name that include a namespace, or null if the typeof guards fail
function getElementsByTagNameNS(local, elementParent) 
{
    if (local == null | elementParent == null || typeof local == "undefined" || typeof elementParent == "undefined")
        return null;

    if (typeof elementParent.getElementsByTagNameNS != "undefined")
    {
        // W3C DOM level 2, not supported by IE as of v6.0
        return elementParent.getElementsByTagNameNS("http://www.araxis.com/2000/Ketura", local);   
    }
    else
    {
        // fall back to DOM level 1, for IE
        return elementParent.getElementsByTagName("kt" + ":" + local)
    }
}


// Returns the the value of a query string parameter, or null if one does not exist.
//
// param sKey the name of the query string
function getQueryParameterValue(sKey)
{
	if (!location || !location.search)
		return null;
		
	var sQueryString = location.search.substring(1).split("&");
	for (var i = 0;i < sQueryString.length; i++)
	{
		var aNameValuePairs = sQueryString[i].split("=");
		if (sKey == aNameValuePairs[0])
			return unescape(aNameValuePairs[1]);
	}
	return null;
}


// gets the filename of the HTML page without its extension
function getPageFilenameWithoutExtension()
{
	if (!document || !document.URL)
		return "";

	var s = escape(document.URL.split("?")[0]);

	var ix = s.lastIndexOf('/');
	if (-1 != ix)
		s = s.substr(1 + ix);

	ix = s.indexOf(';');
	if (-1 != ix)
		s = s.substr(0, ix);

	ix = s.indexOf('?');

	if (-1 != ix)
		s = s.substr(0 ,ix);

	ix = s.indexOf('.');
	if (-1 != ix)
		s = s.substr(0, ix);

	return s;
}


// checks if the brower is unsupported and redirects to the appropriate error page
function checkForUnsupportedBrowsers()
{
	// if the application is configured to support NN4 then we assume that the other things that this fn checks are not relevant
	if (g_fSupportsNetscapeNavigator4)
		return;
	
	if (!browserSupportsXmlHttpRequest()) 
	{
		redirectClientToPageIfNotTheCurrentPage(g_sContextPath + g_sXmlHttpRequestObjectUnsupportedByBrowserErrorPage);
		return;
	}	
	
	if (typeof isNotIeOrIsIe5OrUp != "undefined" && !isNotIeOrIsIe5OrUp())
	{
		redirectClientToPageIfNotTheCurrentPage(g_sContextPath + g_sUnsupportedInternetExplorerVersionErrorPage);
		return;
	}
}


function redirectClientToPageIfNotTheCurrentPage(sUrl)
{
	// if we are already on the page, we don't want to redirect again as this will cause an endless loop
	if (window.location.href.substring(window.location.href.length -sUrl.length,window.location.href.length) != sUrl)
	window.location.href = sUrl;
}


// returns true if browser supports XmlHttpRequest object, otherwise false
function browserSupportsXmlHttpRequest()
{
	return newXMLHTTPRequest() != null;
}

// Initalizes a new XML HTTP request object in the associative array storing XML HTTP request objects iff the browser
// supports XML HTTP request objects and the browser security allows creation of the object. The given id is used as the index
// in the array. If an object exists at the index, an attampt is made to abort the request of the existing object before replacing it in 
// the array with a new object
function initXMLHTTPRequestObject(sId)
{
    // if the given XML HTTP request object has already been initalized,  abort any current request
    if (g_aobjXmlHttpRequests[sId] != null)
    {
        // abort() not supported by all browsers
        if (typeof g_aobjXmlHttpRequests[sId].abort != "undefined")
            g_aobjXmlHttpRequests[sId].abort();
    }

    g_aobjXmlHttpRequests[sId] = newXMLHTTPRequest();
}

// Returns a new XMLHTTPRequest object, or null if the browser does not support the object; this function should probably not be
// called directly, it is usually only called by the initXMLHTTPRequestObject(sId) method.
function newXMLHTTPRequest()
{
	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) 
	{
		try 
		{
			return new XMLHttpRequest();
		} 
		catch(e) 
		{
			return null;
		}
	} 
	else if (window.ActiveXObject) 
	{
		// branch for IE/Windows ActiveX version
		try 
		{
			return new ActiveXObject("Msxml2.XMLHTTP");
		} 
		catch(e) 
		{
			try 
			{
				return new ActiveXObject("Microsoft.XMLHTTP");
			}
			catch(e) 
			{
				return null;
			}
		}
	}
}


// gets an array of 'ktSel' elements
function getKtSelElements(iFormIndex)
{
	var parentEle = null;
	var elements = null;
	var found = new Array();
	var tagName = 'input';
	
	if (!document || !document.forms)
		return found; // definitely not W3C
	
	parentEle = document.forms[iFormIndex];
	if (parentEle.getElementsByTagName)
		elements = parentEle.getElementsByTagName(tagName);
	else if (document.all)
		elements = document.all.tags(tagName);
	if (elements && elements.length)
	{
		var iNumOfElements = elements.length;
		for (var i = 0; i < iNumOfElements; ++i)
		{
		  if (elements[i].name != null && elements[i].name.substring(0, g_sElementSelectedPrefix.length) == g_sElementSelectedPrefix)
		  {
			found[found.length] = elements[i];
		  }
		}
	}
	return found;
}


// shows a help page
function showHelp(url)
{
	if (!window.open)
		return false;
		
	var windowHelp = window.open(url, "keturaHelpWindow","toolbar=yes,location=yes,directories=no,status=no,menubar=yes,scrollbars=yes,resizable=yes,height=400,width=600");
	if (!windowHelp.focus)
		windowHelp.focus();
	return false;
}


// constructor for localizable strings object
function I10nCtor()
{
	this.sMessageNavigateAway = "If you navigate away, the changes you have made will be discarded.";  
	this.sMessageChangesNotApplied =  "You have made changes on this page that have not yet been applied.\n\nTo save these changes, use your browser's Back or Forward button to return to this page and apply the changes before navigating away again.";
	this.sMessageChangesMadeInOtherForms =	"Changes have been made in other forms on this page. If you submit this form, your other changes will be lost.\nPress OK to continue, or Cancel to stay on the current page."	
	this.sMessageJavaScriptErrorInCalendarUserAllocationsPopulateUserAllocationTable = "An error has occured in CalendarUserAllocations.populateUserAllocationTable. Please email support@araxis.com with this error message.";
	this.sMessageFetchingDataForOptionsField = "Fetching data...";
    
    this.asEnglishMonthNames = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ");
    this.asJapaneseMonthNames = "1 2 3 4 5 6 7 8 9 10 11 12".split(" ");
    this.asSwedishMonthNames = "jan feb mar apr maj jun jul aug sep okt nov dec".split(" ");
    this.asGermanMonthNames = "Jan Feb Mrz Apr Mai Jun Jul Aug Sep Okt Nov Dez".split(" ");
    this.asSpanishMonthNames = "ene feb mar abr may jun jul ago sep oct nov dic".split(" ");
}


// true if this is a Gecko based browser (Mozilla, Netscape 6+, etc)
function isGecko()
{
    return navigator.userAgent.toLowerCase().indexOf('gecko') != -1 && navigator.userAgent.toLowerCase().indexOf('khtml') == -1;
}


// checks the user agent, returns true if not IE or IE 5.0+
function isNotIeOrIsIe5OrUp()
{
	// This function Based on the Ultimate client-side JavaScript client sniff from Netscape. Version 3.03.
	// Copyright (c) Netscape Communications 1999-2001. Permission granted to reuse and distribute.

    // convert all characters to lowercase to simplify testing
    var agt = navigator.userAgent.toLowerCase();

    // Note: On IE5, this returns 4, so use is_ie5up to detect IE5.
    var is_major = parseInt(navigator.appVersion);

    var is_ie     = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
    var is_ie3    = (is_ie && (is_major < 4));
    var is_ie4    = (is_ie && (is_major == 4) && (agt.indexOf("msie 4")!=-1) );
    var is_ie4up  = (is_ie && (is_major >= 4));
    var is_ie5    = (is_ie && (is_major == 4) && (agt.indexOf("msie 5.0")!=-1) );
    var is_ie5_5  = (is_ie && (is_major == 4) && (agt.indexOf("msie 5.5") !=-1));
    var is_ie5up  = (is_ie && !is_ie3 && !is_ie4);
    
    return !is_ie || is_ie5up;
}


// true if this is an Apple WebKit based browser
function isSafari()
{
    return navigator.userAgent.toLowerCase().indexOf('applewebkit') != -1;
}


function isIE7()
{
    return navigator.userAgent.toLowerCase().indexOf('msie 7.0') != -1;
}


function initSearchFields()
{
	var aSearchFields = getElementsByClassName("searchField", document, "input");
	for (var i = 0; i < aSearchFields.length; i++)
	{
		// the following code inspired by http://tests.themasta.com/safari/
		if (isSafari())
		{
			aSearchFields[i].setAttribute("type", "search");
            aSearchFields[i].setAttribute("placeholder", g_defaultSearchFieldPlaceholderText);
            aSearchFields[i].setAttribute("autosave", "com.araxis.website");
            aSearchFields[i].setAttribute("results", 5);
			aSearchFields[i].setAttribute("value", "");
		}
		else
		{
			aSearchFields[i].onfocus = function() { if (this.value == g_defaultSearchFieldPlaceholderText) this.value = ""; };
            aSearchFields[i].onblur  = function() { if (this.value == "") this.value = g_defaultSearchFieldPlaceholderText; };
		}
	}
}


function setBodyCssClass()
{
	var elHtml = document.getElementsByTagName('html')[0];

	if (isSafari())
		elHtml.className += elHtml.className ? ' safari' : 'safari'; 
	else if (isGecko())
		elHtml.className += elHtml.className ? ' gecko' : 'gecko';
	else if (isIE7())
		elHtml.className += elHtml.className ? ' ie7' : 'ie7';		
}


// Trim string by ommiting leading and trailing white spaces.
//
// param sString, the string
function trimString(sString)
{
	if (!sString) 
		return ;
		
	var sTrimStringRegExp = new RegExp("^\\s*|\\s*$", "g");
	sString = sString.replace(sTrimStringRegExp, "");   // Remove leading or trailing white.
	return sString;
}