/* Author: Anton K. Nikitin, 2005
 *
 * Script file includes layout-specific scripts for G3 UI.
 *
 * Keep the scripts cross-browser!
 */

/* shortcut function to access element by id */
function elem(name) {
	return document.getElementById(name);
}

var LEFT_PANE_HEIGHT; //set in onload event; used in onresize event
var allowRecreateInput; //dummy var; must be set in a component that whants to recreate user input; see parseAndSub() comments
window.onbeforeunload = function() {
	if(typeof allowPageLoadWarningLabel != 'undefined') {
		try{if(allowPageLoadWarningLabel) elem('pageLoadWarningLabel').className = "";}
		catch(e){}
	}
}
window.onload = function() {
	LEFT_PANE_HEIGHT = elem("leftPane").offsetHeight;

	//make sure task menu is positioned right,
	//and scroll buttons are in place
	window.onresize();

	//call function to get the network indicator image and title, if it exists
	if(typeof getNetworkIndicator == 'function') {
		getNetworkIndicator();
	}

	//show task menu after it's been positioned correctly
	//so there's no perception of a "jump"
	//that means that initially visibility is set to 'hidden' in CSS for #taskMenu
	try{elem("taskMenu").style.visibility = "visible";}
	catch(e) {//task menu isn't defined on page
	}

	//store all form elements with required and invalid CSS class
	//for quick navigation via ctrl+q and ctrl+shift+q
	findAllRequireds();
	focusOnDemand(); //focus on the first element that demands it

	//if page contained validation/action errors, recreate user input
	//both allowRecreateInput and parseAndSub() must be defined inside the component
	//if the page requires additional processing required after parseAndSub, then call
	//performPostParseAndSub and if it is defined in the page, it will be performed
	if(allowRecreateInput) {
		try{parseAndSub();} catch(e){}

	  if (typeof performPostParseAndSub == 'function') {
			performPostParseAndSub();
		}
	}
	//if page defined an array that outlines the sequence of automatic focussing,
	//then perform the logic. See EWMS-1514 for details
	stepThroughFocusItems();
}

/**
 * The function will iterate through input, textarea, and select elements
 * on the page looking for the attribute (as specified by ATTRIBUTE_NEED_FOCUS)
 * that indicates that the element request caret focus; and will focus that
 * element accordingly. The attribute need simply be defined in the tag,
 * the value is ignored.
 */
var ATTRIBUTE_NEED_FOCUS = "wantfocus";
var exportInProgress = false;
function focusOnDemand() {    
    // if the report is being generated, don't try to set focus on the main window
    // since the popup window will be buried underneath it.
    if (reportInProgress) return;
    if (exportInProgress) return;

    var inputElements = new Array();
	inputElements[0] = document.getElementsByTagName("input");
	inputElements[1] = document.getElementsByTagName("textarea");
	inputElements[2] = document.getElementsByTagName("select");
	inputElements[3] = document.getElementsByTagName("a");
	for(var i = 0; i < inputElements.length; ++i) {
		for(var j = 0; j < inputElements[i].length; ++j) {
			if(inputElements[i][j].getAttribute(ATTRIBUTE_NEED_FOCUS)) {
				try{
					inputElements[i][j].focus();
					inputElements[i][j].select();
				}
				catch(e) {}
				return;
			}
		}
	}
}

var REQUIREDS; //array of all elements whose CSS class is 'required' or 'invalid'
var lastRequired = -1; //the last one selected by user
/**
 * Finds all input, textarea, and select elements with 'required' and 'invalid'
 * CSS class for quick navigation using ctrl+q and ctrl+shift+q hotkeys
 */
function findAllRequireds() {
	REQUIREDS = new Array();

	pushRequireds(document.getElementsByTagName("input"), REQUIREDS);
	pushRequireds(document.getElementsByTagName("textarea"), REQUIREDS);
	pushRequireds(document.getElementsByTagName("select"), REQUIREDS);
}
/**
 * Helper method to append the destination array with specific
 * elements from the source array. In this case, className is 'required' or 'invalid'
 */
function pushRequireds(source, destination) {
	for(var i = 0; i < source.length; ++i) {
		if(source[i].className.indexOf("required") != -1 || source[i].className.indexOf("invalid") != -1) {
			destination.push(source[i]);
		}
	}
}
/**
 * Navigates to the next element in the REQUIREDS array,
 * relative to the lastRequred position. If new position
 * out of bounds for this array, logic wraps around.
 *
 * @param iterForward indicates whether to iterate
 * forwards (true) or backwards (false).
 */
function nextRequired(iterForward) {
	var increment = iterForward ? 1 : -1;
	lastRequired += increment + REQUIREDS.length;
	lastRequired %= REQUIREDS.length;

	try {
		REQUIREDS[lastRequired].focus();
		REQUIREDS[lastRequired].select();
	}
	catch(e) {}
}
/**
 * Event handler checking for ctrl+q and ctrl+shift+q hotkeys
 * to navigate caret to the next or previous required form field.
 */
document.onkeypress = function(nsEvent) {
	var this_event = !nsEvent ? window.event : nsEvent;
	var key_code = !nsEvent ? this_event.keyCode : this_event.which;
	var source = !nsEvent ? this_event.srcElement : this_event.target;

	//for mozilla: 113 & ctrl, for IE: 17 & !shift
	if(key_code == 113 && this_event.ctrlKey || key_code == 17 && !this_event.shiftKey) { //control + 'q'
		nextRequired(true);
		return false;
	}
	//for mozilla: 81 & ctrl, for IE: 17 & shift
	else if(key_code == 17 && this_event.shiftKey || key_code == 81 && this_event.ctrlKey) { //control + shift + 'q'
		nextRequired(false);
		return false;
	}
}

/* leftPaneTop is to keep track of the "top" property
 * of leftPane, rather than calculate it each time.
 * This is cross-browser and more efficient.
 */
var leftPaneTop = 0;
window.onresize = function() {
	var leftSidebarHeight = elem("left-sidebar").offsetHeight -
							elem("buttonBar").offsetHeight -
							elem("footer").offsetHeight -
							elem("header").offsetHeight;
	var tmp = leftSidebarHeight - LEFT_PANE_HEIGHT;
	var DEFAULT_MARGIN = 10;	//smallest top-margin for Task Menu
	var taskMenuTopMargin = tmp >= DEFAULT_MARGIN ? tmp : DEFAULT_MARGIN;

	//increase top margin to have Task Menu trail on bottom of left sidebar
	try{elem("taskMenu").style.marginTop = taskMenuTopMargin;}
	catch(e){//task menu isn't defined on page
	}

	//show scroll buttons when window height is too small
	if(leftSidebarHeight < LEFT_PANE_HEIGHT) {
		var diff = leftSidebarHeight - elem("leftPane").offsetHeight;// - elem("buttonDown").offsetHeight;
		//display top scroll button only
		//if menu isn't all the way at the top
		elem("buttonUp").style.display = leftPaneTop == 0 ? "none" : "block";
		elem("buttonDown").style.display = leftPaneTop <= diff ? "none" : "block";//"block";
		//elem("leftPane").style.top = leftPaneTop = elem("buttonUp").offsetHeight;

		//pull the menu down to avoid gap between scroll-down
		//button and the menu when window height is increased
		if(leftPaneTop < diff) {
			elem("leftPane").style.top = leftPaneTop = diff;
			//elem("buttonDown").style.display = "none";
		}
	}
	else {	//hide scroll buttons when menu is fully shown
		elem("buttonUp").style.display = "none";
		elem("buttonDown").style.display = "none";
		elem("leftPane").style.top = leftPaneTop = 0;
	}
}

/* Function scrollOut() is a preparation function for scrolling components in/out of view.
 * Actual scrolling is done by scrollOutTimed(). This function simply determines which
 * element to scroll and whether element is expanding or contracting.
 * scrollOut() is called by onclick from either div.compHead (child of div.comp)
 * or from a.close (child of div.options).
 *
 * @param scrollTrigger is the element that the call came from
 */
var scrollOutPeriod = 50;	//used in scrollOutTimed(); defines period
var scrollOutSteps = 5;		//used in scrollOut() and scrollOutTimed(); defines number of steps
function scrollOut(scrollTrigger) {
	/* we can only scroll one thing at any given time;
	'document.scrolling' global property will be our sentinel;
	set back to false in scrollOutTimed, when scrolling done */
	if(!document.scrolling)
		document.scrolling = true;
	else
		return false; //don't let the href of the link to be executed

	var PADDINGS_AND_STUFF = 9;	//to accomodate for margins and paddings inside compContent
	var comp = document.comp2scroll = findParent(scrollTrigger, "comp"); //comp div
	var content = document.content2scroll = findDescendant(comp, "compContent"); //compContent div
	var expanding = content.style.display == "none"; //if div hidden, then we're expanding it

	/* remember original height of this component */
	if(!content.proposedHeight)
		content.proposedHeight = content.offsetHeight - PADDINGS_AND_STUFF;

	scrollOutTimed(scrollOutSteps, expanding); //do actual scrolling
	return false; //don't let the href of the link to be executed
}

/* Function scrollOutTimed() scrolls components in/out of view.
 * Calls itself with delay of scrollOutPeriod, scrollOutSteps times.
 *
 * @param stepsLeft how many steps left
 * @param expanding whether expanding or contracting
 */
function scrollOutTimed(stepsLeft, expanding) {
	var comp = document.comp2scroll; //set in scrollOut
	var content = document.content2scroll; //set in scrollOut
	var hed = findDescendant(comp, "compHead");
	var fraction = stepsLeft / scrollOutSteps; //fraction of visible component this "iteration"

	//when expanding....
	if(expanding) {
		fraction = 1 - fraction; //...fraction is reverse of that when contracting

		//display component, when we just started expanding
		if(stepsLeft == scrollOutSteps) {
			content.style.display = "block";
		}
	}
	//+1 to safeguard against a case where new height is 0 (visually 1px won't make a difference)
	var newHeight = fraction * content.proposedHeight + 1;
	content.style.height = newHeight + "px";
	if(stepsLeft != 0) {
		setTimeout("scrollOutTimed(" + (stepsLeft - 1) + ", " + expanding + ")", scrollOutPeriod / scrollOutSteps);
	}
	else {	//when this is the last 'iteration'...
		content.style.display = expanding ? "block" : "none"; //set appropriate display css property
		content.style.height = "auto"; //make sure component is the right size
		document.scrolling = false; //update sentinel (see scrollOut())

		//switch close-button css class to update picture
		var btn = findDescendant(findDescendant(hed, "options"), "close");
		if(!btn)	//if button of class "close" couldn't be found...
			btn = findDescendant(findDescendant(hed, "options"), "open");
		if(btn) //do this only if button is there at all
			btn.className = expanding?"close":"open";
	}
}

/* Helper function, used in scrollOut() and scrollOutTimed(). */
function findDescendant(node, childClass) {
	var i=0;
	for(;node.childNodes[i]!=null;++i) {
		if(node.childNodes[i].className==childClass)
			break;
	}
	return node.childNodes[i];
}
/* Helper function, used in scrollOut() and scrollOutTimed(). */
function findParent(node, parentClass) {
	while(node.className != parentClass)
		node = node.parentNode;
	return node;
}

/* Function toggleLeft() sets up toggleLeftTimed to slide
 * left menu in/out of view.
 */
 /*
var periodToggle = 200; //period to toggle left menu; used in toggleLeftTimed()
var stepsToggle = 10; //steps to toggle left menu; used in toggleLeft() and toggleLeftTimed()
function toggleLeft() {
	//set the sentinel to indicate whether we're in the process already
	if(!document.toggling)
		document.toggling = true;
	else
		return;

	var toggle = document.getElementById('toggle');
	var menu = document.getElementById('left-sidebar');

	//store original width of the menu
	if(!menu.proposedWidth)
		menu.proposedWidth = menu.offsetWidth;
	toggleLeftTimed(stepsToggle, toggle.expanding);
}*/

/* Function toggleLeftTimed() slides left menu in/out of view
 * in stepsToggle steps with periodToggle delay.
 *
 * @param stepsLeft iterations left to slide
 * @param expanding whether menu is sliding in or out of view
 */
 /*
function toggleLeftTimed(stepsLeft, expanding) {
	var toggle = document.getElementById('toggle');
	var menu = document.getElementById('left-sidebar');
	var fraction = stepsLeft / stepsToggle;
	fraction = toggle.expanding ? 1 - fraction : fraction;
	var newWidth = fraction * menu.proposedWidth + 'px';

	menu.style.width = newWidth;

	if(stepsLeft != 0) {
		setTimeout("toggleLeftTimed("+ --stepsLeft +","+expanding+")", periodToggle/stepsToggle);
	}
	else {
		document.toggling = false;
		toggle.parentNode.className = toggle.expanding ? 'close' : '';
		toggle.className = toggle.expanding ? 'close' : 'open';
		document.getElementById('content').style.marginLeft = newWidth;
		toggle.expanding = !toggle.expanding;
	}
}*/

/* Function scrolls left menu down. As long as 'scrolling' property
 * is set to true by #buttonDown, this will iterate indefinately.
 * So it should be set to false in onmouseout.
 */
var SCROLL_DISTANCE = 17; //left menu scrolling speed
var SCROLL_DELAY = 50; //left menu scrolling delay
function scrollMenuDown() {
	//first, show scroll-up button, if it was hidden
	if(leftPaneTop == 0) {
		elem("buttonUp").style.display = "block";
	}
	var diff = elem("left-sidebar").offsetHeight -
			   elem("buttonBar").offsetHeight -
			   elem("footer").offsetHeight -
			   elem("header").offsetHeight -
			   elem("leftPane").offsetHeight;// -
			   //elem("buttonDown").offsetHeight;
	leftPaneTop -= SCROLL_DISTANCE;

	//we don't want to scroll too far
	if(leftPaneTop < diff) {
		elem("leftPane").style.top = leftPaneTop = diff;
		elem("buttonDown").style.display = "none";
		return;
	}
	elem("leftPane").style.top = leftPaneTop;

	//the scrolling property should be set to true by button
	//for mouseover, and to false for mouseout
	if(elem("buttonDown").scrolling)
		setTimeout("scrollMenuDown()", SCROLL_DELAY);
}

/* Function scrolls left menu up. As long as 'scrolling' property
 * is set to true by #buttonUp, this will iterate indefinately.
 * So it should be set to false in onmouseout.
 */
function scrollMenuUp() {
	var diff = elem("left-sidebar").offsetHeight -
			   elem("buttonBar").offsetHeight -
			   elem("footer").offsetHeight -
			   elem("header").offsetHeight -
			   elem("leftPane").offsetHeight;
	//first, show scroll-down button, if it was hidden
	if(leftPaneTop == diff) {
		elem("buttonDown").style.display = "block";
	}
	leftPaneTop += SCROLL_DISTANCE;

	//we don't want to scroll too far, so set the limit at 0
	if(leftPaneTop >= 0) {
		elem("buttonUp").style.display = "none";
		elem("leftPane").style.top = leftPaneTop = elem("buttonUp").offsetHeight;
		return;
	}
	elem("leftPane").style.top = leftPaneTop;

	//the scrolling property should be set to true by button
	//for mouseover, and to false for mouseout
	if(elem("buttonUp").scrolling)
		setTimeout("scrollMenuUp()", SCROLL_DELAY);
}

/**
 * Function used to set IDP user preferences without having to reload the page.
 * This is achieved by creating an image object, and assigning it a URL which
 * links to the preference-setting IDP Action. While the image itself is invalid,
 * the effect is that we successfully relay the passed parameters to the preference action.
 *
 * IMPORTANT: make sure that the URL used in this function is mapped in the action
 * mapping to the com.univeris.idp.module.actions.SaveSitePreferenceAction
 *
 * @param servletPath - the path to which current servlet is mapped to; every component has that available
 * @param parentID - parent ID of the preference (IDP site code, action mapping, component UID, etc.)
 * @param preferenceName - name of the preference to set
 * @param preferenceValue - new value to set
 */
function saveSitePreference(servletPath, parentID, preferenceName, preferenceValue) {
	var prefLink = servletPath+"/system/save-site-pref?parentID="+parentID+"&preferenceName="+preferenceName+"&preferenceValue="+preferenceValue;
	var prefImage = new Image();
	prefImage.src = prefLink;
}

/**
 * Used by validatorMarker/ActionError components to recreate user input on pages
 * so that the submitted form data is not lost after the page reloads with an error.
 * Called from parseAndSub() method of the component's javascript.
 *
 * @param names - array of parameter names; may contain duplicates (e.g. checkboxes share name, but have different values)
 * @param values - array of corresponding field values; e.g. names[n] has value values[n].
 */
function recreateInput(names, values) {
	for(var i = 0; i < names.length; ++i) {
		var currs = document.getElementsByName(names[i]);
		for(var j = 0; j < currs.length; ++j) {
			var curr = currs[j];
			if(curr.tagName == "INPUT" && (curr.getAttribute("type") == "text" || curr.getAttribute("type") == "hidden") || curr.tagName == "TEXTAREA") {
				if(typeof values[i][j] == 'undefined') continue; //ignore field if it's null for any reason
				curr.value = values[i][j];
			} else if(curr.tagName == "INPUT" && (curr.getAttribute("type") == "checkbox" || curr.getAttribute("type") == "radio")) {
				for(var k = 0; k < values[i].length; ++k) {
					if(curr.value == values[i][k]) {
						if (!curr.checked) {
							curr.click();
						}
					}
				}
			} else if(curr.tagName == "SELECT") {
				for(var k = 0; k < curr.options.length;++k) {
					if(curr.options[k].value == values[i][j]) {
						curr.options[k].selected = true;
						break;
					}
				}
			}
		}
	}
}

function submitViaReportWindow(targetForm) {
	targetForm.target = 'popUpReportWin';
	window.open('', 'popUpReportWin', 'width=800,height=350,resizable=yes,scrollbars=yes,status=no');
}

/**
 * Function cycles through the named elements and sets focus on them
 * based on their visibility. A static array "focusOrderArray" is expected
 * to be defined on the page (if not, the function is not executed), which
 * sets the proper sequence.
 * E.g.: var focusOrderArray = [['step1', 're-test', 'rng-test', 'num-test'], ['step2', 'num-test', 'rng-test', 're-test']];
 * Combined with a definition of a variable "currentFocusStep", which in this case could correspond
 * to either 'step1' or 'step2', this will result in focus being set iteratively to
 * elements listed in the sub-array with the name of the step. Focus is not set if element is
 * not visible. EWMS-1514.
 */
function stepThroughFocusItems() {
	try{!focusOrderArray}
	catch(e){return;} //quit if the focusOrderArray array is not defined
	for(var i = 0; i < focusOrderArray.length; ++i) {
		if(currentFocusStep == focusOrderArray[i][0]) {
			for(var j = 1; j < focusOrderArray[i].length; ++j) { //set focus to each visible field for the selected step
				var currentElement = document.getElementsByName(focusOrderArray[i][j])[0];
				if(!currentElement) continue; //if field is not on the page, go on to next
				var isVisible;
				if(document.defaultView) {isVisible = document.defaultView.getComputedStyle(currentElement, "").getPropertyValue("display") != "none";} //mozilla's check
				else if(currentElement.currentStyle) {isVisible = currentElement.currentStyle["display"] != "none";} //IE's check
				if(isVisible) {
					if(currentElement.getAttribute("type") != "hidden") {
						currentElement.focus();
					}
					if(currentElement.getAttribute("type") == "text") { //only select if element is a textbox
						currentElement.select();
					}
				}
			}
		}
	}
}

/**
 * Function used in components with lists w/ hidden details (see C6 in Style Reference).
 * Script is called from an onclick event of the image that causes the toggle. The image's @id
 * attribute should be a concatenation of the following: "img", its sequential number, servlet name, and
 * the uniqueID of the component. E.g. id="img155abcd", where '155' is the seqNum, and 'abcd' is the componentUID.
 * The corresponding <tr> element should have a similar @id, except with a "row" instead of "img".
 * @param seqNum - sequential number
 * @param servlet - servlet name. E.g. "/upm"
 * @param compID - component's unique ID; used so that image ID's do not duplicate
 * 					on the page if used in several components on the page.
 */
function toggleHiddenDetails(seqNum, servlet, compID) {
	var downArrow = '/files/images/arrow_down.gif';
	var upArrow = '/files/images/arrow_up.gif';
	var image = elem('img'+seqNum+compID);
	var row = elem('row'+seqNum+compID);
	var pos = image.src.indexOf(servlet);
	var src = image.src.substring(pos);
	if(src == servlet + downArrow) {
		row.style.display = (!document.all)?'table-row':'block'; //block in IE, table-row in Mozilla
		image.src = servlet + upArrow;
	} else {
		row.style.display='none';
		image.src = servlet + downArrow;
	}
}


/**
 * This function will iterate through all input, textarea and select elements
 * on the page and change their state to read only/disabled.  This is used to lock a
 * page before entering a refresh (ie update called, secondary screen, etc)
 */
function disableEdit() {
	//don't do anything if user preference suppresses this functionality.
	//see UtilityFooter.xsl for details.
	if(typeof allowFormDisableOnReload == 'undefined' || allowFormDisableOnReload == false) {
		return;
	}
   try {
	//Text Areas first
	var taElem = document.getElementsByTagName("textarea");
	for(var j = 0; j < taElem.length; ++j) {
		try{
			taElem[j].readOnly=true;
		}
		catch(ex) {
			//alert(ex.message);
		}
	}
	//Then inputs, text & passwords go read only, buttons are disable
	var inputs = document.getElementsByTagName("input");
	for(j = 0; j < inputs.length; ++j) {
		try{
			type = inputs[j].type.toLowerCase();
			if(type == 'text' || type == 'password') {
				inputs[j].readOnly=true;
			}
			else if(type == 'button' || type == 'submit'  || type == 'reset') {
				inputs[j].disabled=true;
			}
			else if(type == 'checkbox' || type == 'radio') {
				insertDisabledClone(inputs[j]);
			}
		}
		catch(ex) {
			//alert(ex.message);
		}
	}
	//Selects, we have to just retarget focus
	var selectElems = document.getElementsByTagName("select");
	for(j = 0; j < selectElems.length; ++j) {
		try{
			insertDisabledClone(selectElems[j]);
		}
		catch(ex) {
			//alert(ex.message);
		}
	}
   } catch(ex) {
		//alert(ex.message);
   }
}
function insertDisabledClone(elem) {
	if(elem.isCloned==null)  {
		var cloned = elem.cloneNode(true);
		elem.style.visibility='hidden';
		elem.style.display='none';
		cloned.disabled='true';
		cloned.isCloned='true'
		cloned.id = cloned.id + '-cloned';
		elem.insertAdjacentElement('afterEnd', cloned);
		//Need to set the selected item for select lists
		if(cloned.tagName.toLowerCase='select') {
			cloned.value = elem.value;
		}
	}
}
/**
 * Object encapsulates session timeout message related UI logic. Collaborating code
 * can be found in UtilityFooter component template.
 */
var mainSessionTimeoutManager = {
        /*set in UtilityFooter*/
        _expiryBuffer: null,
        /*set in UtilityFooter*/
        _timeTillTimeout: null,
        /*set in UtilityFooter*/
        _servletPath: null,

        _timeoutLeft: null,
        _timeoutLastCalled: null,
        _postponeTimeoutTime: null,
        _countingTimeout: false,

        /**
         * Starts countdown on immediate page load; see UtilityFooter for initial call
         */
        startTimeoutCounter: function() {
            this._timeoutLeft = this._expiryBuffer;
            var timeoutPeriod = (this._timeTillTimeout - this._expiryBuffer) * 1000;

            if(this._timeTillTimeout >= this._expiryBuffer) {
                setTimeout("mainSessionTimeoutManager._displaySessionTimeout()", timeoutPeriod);
            }
        },

        /**
         * Displays session timeout message, and starts countdown till actual expiration
         */
        _displaySessionTimeout: function() {
            //since this is called regardless of external state, we have to check
            //if session postpone logic was initiated; quit if so
            if(this._checkTimeoutPostponed()) {
                return;
            }

            elem('sessionTimeoutLabel').className = 'sessionTimeout';
            elem('sessionTimeoutIFrame').className = 'sessionTimeout';
            this._countingTimeout = true;
            this._countDownTimeout();
            window.focus();
        },

        /**
         * A check function to confirm whether postponeTimeout() was called anywhere in between
         * the point in time where startTimeoutCounter() was called, and _displaySessionTimeout()
         * was actuall invoked.
         */
        _checkTimeoutPostponed: function() {
            return (this._postponeTimeoutTime != null) &&
                   (this._postponeTimeoutTime > new Date().getTime() - (this._timeTillTimeout - this._expiryBuffer) * 1000);
        },

        /**
         * This function can be called to postpone session expiry countdown;
         * Used to reset the timer when XHR communications are being performed
         *
         * @see mainEventManager._sessionResetHandler() for invoking code
         * @jira EWMS-7767
         */
        postponeTimeout: function() {
            this._postponeTimeoutTime = new Date();

            if(this._countingTimeout) {
                this.hideTimeoutMessage();
            }
            this.startTimeoutCounter();
        },

        _countDownTimeout: function() {
            if(!this._countingTimeout) return; //don't do anything if we cancelled counting down: reviveClientSession() was called-->
            //some safeguards when (timeTillTimeout-expiryBuffer) is close to 0-->
            if(new Date().getTime() - this._timeoutLastCalled < 900) {
                return;
            }

            var tmp = elem('timeoutLeft'); //display time left-->
            try{tmp.removeChild(tmp.firstChild);}catch(e){}
            tmp.appendChild(document.createTextNode(this._timeoutLeft));

            if(this._timeoutLeft > 0) { //if still time left, do again in 1 second-->
                this._timeoutLeft--;
                setTimeout("mainSessionTimeoutManager._countDownTimeout()", 1000);
            }
            else { //otherwise stop counting down, and show session expiration notice-->
                elem('timeoutMessage1').className = 'hidden';
                elem('timeoutMessage2').className = '';
                elem('reviveSessionLink').className = 'hidden';
                this._countingTimeout = false;

                var logoutUrl = this._servletPath + "/system/logout-user";
                var dummyImage = new Image();
                dummyImage.src = logoutUrl;
                if(typeof UpdateHrefPrintPage == 'function')
                {
                    UpdateHrefPrintPage();
                }
            }
            this._timeoutLastCalled = new Date().getTime();
        },

        /**
         * Ping the server to keep session alive
         */
        reviveClientSession: function() {
            this._countingTimeout = false; //stop this countdown

            //call server to revive session -->
            var pingURL = this._servletPath + "/system/ping-server";
            var dummyImage = new Image();
            dummyImage.src = pingURL;

            //and then start the new countdown
            this.startTimeoutCounter();
            this.hideTimeoutMessage();

            return false;
        },

        /**
         * Hides session expiry message
         */
        hideTimeoutMessage: function() {
            elem("sessionTimeoutLabel").className = 'hidden';
            elem("sessionTimeoutIFrame").className = 'hidden';
            return false;
        }
}
