//                  Copyright (c) 2008, Lexmark International, Inc.
//      THIS IS AN UNPUBLISHED WORK CONTAINING CONFIDENTIAL AND PROPRIETARY
//         INFORMATION WHICH IS THE PROPERTY OF LEXMARK INTERNATIONAL, INC.
//    ANY DISCLOSURE, USE, OR REPRODUCTION, WITHOUT WRITTEN AUTHORIZATION FROM
//               LEXMARK INTERNATIONAL, INC., IS STRICTLY PROHIBITED


////////////////////////////////////////////////////////////////////////////////
// Control behaviors
////////////////////////////////////////////////////////////////////////////////

/**
* Function to retrieve the first element using a particular class name in the dom.
* @param   cl  The classname of the element being searched
*/
document.getElementByClassName = function(cl) {
    var retnode = null;
    var myclass = new RegExp('\\b'+cl+'\\b');
    var elem = this.getElementsByTagName('*');
    for (var i = 0; i < elem.length; i++) 
    {
        var classes = elem[i].className;
        if (myclass.test(classes)) 
        {
            retnode = elem[i];
            break;
        }
    }
    return retnode;
}; 

/**
* Mouseover handler for 'really big button's. Changes the button's CSS class to 
* add the 'rbb-hover' class.
*
* @param   id  The id of the button that was moused over.
*/
function onRbbMouseOver(id)
{
    var elem = document.getElementById(id);
    if( !window.event.srcElement.contains(window.event.fromElement) )
    {
        addClassName(elem, 'rbb-hover');
    }
}

/**
* Mouseout handler for 'really big button's. Changes the button's CSS class to 
* remove the 'rbb-hover' class.
*
* @param   id  The id of the button that was moused out of.
*/
function onRbbMouseOut(id)
{
    var elem = document.getElementById(id);
    if( !window.event.toElement || !elem.contains(window.event.toElement) )
    {
        removeClassName(elem, 'rbb-hover');
    }
}

/**
* Mouseover handler for tab headers. Changes the tab header's CSS class to add 
* the 'tab-hover' class, unless the tab is already the active tab.
*
* @param   id  The id of the tab that was moused over.
*/
function onTabMouseOver(id)
{
    var elem = document.getElementById(id);
    if( window.event.srcElement === elem && 
        !window.event.srcElement.contains(window.event.fromElement) )
    {
        // The active tab does not have a mouseover effect.
        if(!hasClassName(elem, 'tab-active'))
        {
            addClassName(elem, 'tab-hover');
        }
    }
}

/**
* Mouseout handler for tab headers. Changes the tab header's CSS class to remove 
* the 'tab-hover' class.
*
* @param   id  The id of the tab that was moused out of.
*/
function onTabMouseOut(id)
{
    var elem = document.getElementById(id);
    if( !window.event.toElement || !elem.contains(window.event.toElement) )
    {
        removeClassName(elem, 'tab-hover');
    }
}

/**
* Click handler for tabs. Activates the clicked tab.
*
* @param   id  The id of the tab that was clicked.
*/
function onTabClick(id)
{
    activateTab(id);
}

function onBackClick()
{
    breadtrail.style.display = 'none';
    mainchildnode.innerHTML = mainbody;
}

/**
* Shows a menu as a dropdown from a really big button.
*
* @param   id  The id of the button whose menu will be shown.
*/
function showMenu(id)
{
    var menu = document.getElementById('menu' + id);   
    var btn = document.getElementById(id);

    // Strip the hover effect from the button
    removeClassName(btn, 'rbb-hover');

    // Find the button's exact position and use it is a starting point to 
    // calculate the proper position for the menu.
    var offset = findAbsolutePos(btn);
    menu.style.left = offset.X;
    menu.style.top = btn.offsetHeight + offset.Y;

    // Make the menu visible
    menu.style.display = 'block';

    // Capture the mouse input, while allowing events to continue occurring on 
    // the child elements of the menu.
    if(!isIe5())
    {
        menu.setCapture(false);
    }
    else
    {
        menu.setCapture();
        
        // Ensure we only attach these events once. The first time through, 
        // 'menu' will not have an eventsInitialized property. For the purposes
        // of this test, not having the property is equivalent to the property
        // being false
        if(!menu.eventsInitialized)
        {
            menu.eventsInitialized = true;
            
            // Create a simple function to dispatch the event within the menu.
            // This will be added as a handler for any events that need to be
            // dispatched.
            var doDispatch = function()
            {
                // Due to the nature of javascript closures, 'menu' will 
                // continue to exist and contain the proper value after the
                // 'showMenu' method completes.
                dispatchEvent(menu);
            };
            
            // IE5's setCapture() behaves differently than newer versions. Work 
            // around this by dispatching the events manually.
            menu.attachEvent('onmouseover', doDispatch);
            menu.attachEvent('onmouseout', doDispatch);
            menu.attachEvent('onclick', doDispatch);
        }
    }
}

/**
* Hides a menu that is currently being shown as a dropdown from a really big 
* button.
*
* @param   id  The id of the menu that will be hidden.
*/
function hideMenu(id)
{
    var menu = document.getElementById(id);
    menu.style.display = 'none';
}

/**
* Click handler for the menu (NOT the menu items). Listens to all clicks that 
* occur while the menu is open. Releases the mouse capture.
*
* @param   id  The id of the menu.
*/
function onMenuClick(id)
{
    var menu = document.getElementById(id);
    menu.releaseCapture();
}

/**
* Handler for the 'losecapture' event. This event indicates that the menu has
* lost mouse capture. Closes the menu.
*
* @param   id  The id of the menu.
*/
function onMenuCaptureLost(id)
{
    hideMenu(id);
}

/**
* Mouseover handler for menu items. Adds a highlighted effect.
*
* @param   id  The id of the menu item that was moused over.
*/
function onMenuItemMouseOver(id)
{
    var elem = document.getElementById(id);
    addClassName(elem, 'menu-item-highlight');
    removeClassName(elem.childNodes[0], 'menu-item-icon-inactive');
}

/**
* Mouseout handler for menu items. Removes the highlighted effect.
*
* @param   id  The id of the menu item that was moused out of.
*/
function onMenuItemMouseOut(id)
{
    var elem = document.getElementById(id);
    removeClassName(elem, 'menu-item-highlight');
    addClassName(elem.childNodes[0], 'menu-item-icon-inactive');
}

////////////////////////////////////////////////////////////////////////////////
// App behaviors.
////////////////////////////////////////////////////////////////////////////////

/**
* Click handler for all tasks, regardless of presentation. 
*
* @param   actionID     The ID of the task being clicked.
*/

function onTaskClick(actionID)
{

    // Use try/catch block in case there's a Dispatch issue
    try
    {
       if(actionID == "ConfigPrinterDS")
            window.external.doActionAsync(actionID);
       else if(actionID == "CheckForFwUpdates")
            window.external.doActionAsync(actionID);
       else if(actionID == "ScanToNetworkFolderSettings")
            window.external.doActionAsync(actionID);
       else if(actionID == "AdditionalSoftware") 
            window.external.doActionAsync(actionID);
	   else if(actionID == "OnlineDoc") 
            window.external.doActionAsync(actionID);
       else if(actionID == "PrinterRegistration") 
            window.external.doActionAsync(actionID);
       else if(actionID == "OnLineSupport") 
            window.external.doActionAsync(actionID);
       else if(actionID == "LexmarkRewards") 
            window.external.doActionAsync(actionID);
       else if(actionID == "LearnPrinterFax") 
            window.external.doActionAsync(actionID);
       else if(actionID == "OrderSupplies") 
            window.external.doActionAsync(actionID);
       else if(actionID == "KnowAboutPrinter") 
            window.external.doActionAsync(actionID);
       else if(actionID == "LexmarkKoshi") 
            window.external.doActionAsync(actionID);
       else if(actionID == "ConfigureShortcuts") 
            window.external.doActionAsync(actionID);
       else
            window.external.doAction(actionID);
       
    }
    catch(e) {}

}

var selectedPrinterID = null; 
var xmlDocX;    //These global variables will be used by drawModelControl(), and onPrinterSelected(printerID)
                           
                            
function onloaded()
{
    //Top level try-catch block for the model combo related changes
    try
    {
        drawModelControl();//Draw the model combo
    }
    catch(e) {}
      
    for(var j=0; j < document.body.childNodes.length; j++)
    {
        child = document.body.childNodes[j];
        if( child.id=="breadtrail")
        {
          child.style.display = 'none';
          breadtrail = child;
          break;
        }
    }  
}

var modelComboFontSize = 11;
/**
* Top level function for drawing the model combo box or label
*/
function drawModelControl()
{ 
    var printerListXML = window.external.getPrinterList(); //get the printerlist xml
    
    //xmlDocX=new ActiveXObject("Microsoft.XMLDOM");
    try
    {
        xmlDocX=new ActiveXObject("Msxml2.DOMDocument.3.0");
    }
    catch(err)
    {
        alert('Unable to instantiate ActiveXObject Msxml2.DOMDocument.3.0. Printer Home will not run correctly. Please check system settings.');
        return;
    }
    xmlDocX.async="false";
    xmlDocX.loadXML(printerListXML);
        
    var xmlObj = xmlDocX;
    
   	var modelComboBox = document.createElement('select');
    modelComboBox.style.fontSize = modelComboFontSize;
	modelComboBox.id = 'modelComboid';
	modelComboBox.onchange = onModelSelect; //Register onchange handler for model combobox
	
	var labelcontrol = document.createElement('label');
    
    var labelstring = "";
	 
	var printerListNode = xmlObj.getElementsByTagName('*');
		
	var modelComboFlag = PopulateModelCombo(printerListNode,modelComboBox,labelcontrol);
	             
    var printerComboBox = document.getElementByClassName('printer-select'); //get hold of the printer combo
    
    if(null == printerComboBox)
    {
        return; //No point in going further 
    }
    
    printerComboBox.focus();    //We should have printer combo in focus just after launching
    
    AddToolTipForPrinterCombo(printerComboBox);
    AddEllipsesToPrinter(printerComboBox);  
    
    var comboParent = printerComboBox.parentNode;
    
    if(null != comboParent)
    {
       ArrangeGUI(labelcontrol, modelComboBox, printerComboBox, comboParent);
       
       comboParent.appendChild(modelComboBox); //add the combo
       comboParent.appendChild(labelcontrol);  //add the label         
        
       if(modelComboFlag == "true")
       {
           modelComboBox.style.visibility = "visible";
           labelcontrol.style.visibility = "hidden";
       }
       else if(modelComboFlag == "error")
       {
           modelComboBox.style.visibility = "hidden";
           labelcontrol.style.visibility = "hidden";
       }
       else
       {
           //show label        
           modelComboBox.style.visibility = "hidden";
           labelcontrol.style.visibility = "visible";
       }
    }
}

/**
* This function is responsible for adding tooltip to the printer combo
* @param   printerComboBox     The printer combo box identifier
*/
function AddToolTipForPrinterCombo(printerComboBox)
{
    for(var i=0; i<printerComboBox.options.length; i++)
    {
        printerComboBox.options[i].title = printerComboBox.options[i].text; //add tooltip for printer combo
    }
}

var maxLength = 58
/**
* This function is responsible for adding ellipses to print object name(if the string is big)
* @param   printerComboBox     The printer combo box identifier
*/
function AddEllipsesToPrinter(printerComboBox)
{
    for(var i=0; i<printerComboBox.options.length; i++)
    {
        var printObject = printerComboBox.options[i].text;
        
         if (printObject.length >=maxLength)
         {
            printObject = printObject.substring(0,maxLength-4);
            printObject = printObject + '...';
            printerComboBox.options[i].text = printObject;
         }
    }      
}

/**
* This function is responsible for populating the printer model combo box
* @param   printerListNode     The printerlist as received from the framework
* @param   modelComboBox       The printer model combo box control identifier
* @param   labelcontrol        The printer model label control identifier(in case we decide to draw a label and not a combo)
*/
function PopulateModelCombo(printerListNode,modelComboBox,labelcontrol)
{
    var modelComboFlag = "true";
    
    if((printerListNode == null) || (printerListNode[0] == null)) //printerList empty
	{
	    modelComboFlag = "error";
	    return modelComboFlag;
	}
    
    for ( var x = 0; x < printerListNode[0].childNodes.length; x++ )
    {
        var printerNode = printerListNode[0].childNodes[x];
        var isSel = printerNode.getAttribute('selected');
		if(isSel != null && isSel == "true")
		{
			selectedPrinterID = printerNode.getAttribute('id');
			
			if(printerNode.childNodes[0] == null) //modelList empty
	        {
	            modelComboFlag = "error";
	            return modelComboFlag;
	        }
	        
			//populate model combo
			var modelListNode = printerNode.childNodes[0];
			
			for ( var j = 0; j < modelListNode.childNodes.length; j++)
			{
				 var modelNode = modelListNode.childNodes[j];
				 var modelSel = modelNode.getAttribute('selected');
							 
				 var modelComboOptions = document.createElement('option'); 
        		 var modelText = modelNode.getAttribute('name'); 
        		 var modelComboOptionVal = modelNode.getAttribute('path');
				 modelComboOptions.appendChild( document.createTextNode(modelText)); 
				 modelComboOptions.value = modelComboOptionVal; 
				 modelComboOptions.title = modelText; //For the tooltip
				 		 				
				 if(modelSel != null && modelSel == "true")
				 {
				 	 modelComboOptions.selected = true;
				 	 var auto = modelNode.getAttribute('auto');
					 if(auto != null && auto == "true")
					 {
					    //Create the text to be displayed on the label
					    modelComboFlag = "false";
					    labelstring = document.createTextNode(modelText);
					    labelcontrol.appendChild(labelstring);
					 }
				 }
				 modelComboBox.appendChild(modelComboOptions);
			}
			break;//break as soon as we get one printer selected
		}
		else //No printer is selected
		{
		    selectedPrinterID = null;
		}			
    }
    return modelComboFlag;
}


var comboNmodelLabelLeftOffset = 115;
var comboTopOffset = 35;
var comboLabelLeftOffset = 25;
var comboNmodelLabelWidth = 330;
/**
* This function is responsible for poistioning the printer combo and model combo in the dashbaord dialog
* @param   labelcontrol        The printer model label control identifier(in case we decide to draw a label and not a combo)
* @param   modelComboBox       The printer model combo box control identifier
* @param   printerComboBox     The printer combo box control identifier
* @param   comboParent         Parent GUI element for the Combo boxes(or label)
*/
function ArrangeGUI(labelcontrol, modelComboBox, printerComboBox, comboParent)
{
    labelcontrol.style.position = modelComboBox.style.position = 'absolute';
    labelcontrol.style.width = modelComboBox.style.width = printerComboBox.style.width = comboNmodelLabelWidth;
    labelcontrol.style.top = modelComboBox.style.top = 0;
    labelcontrol.style.left = modelComboBox.style.left = printerComboBox.style.left + comboNmodelLabelLeftOffset;
    labelcontrol.style.color = 'white';
		
	printerComboBox.style.position = 'absolute';
	printerComboBox.style.top = printerComboBox.style.top - comboTopOffset;
	printerComboBox.style.left = printerComboBox.style.left + comboNmodelLabelLeftOffset;
		
	var printerlabelcontrol = document.createElement('label');
	printerlabelcontrol.style.position = 'absolute';
    printerlabelcontrol.style.top = printerComboBox.style.top;
	printerlabelcontrol.style.left = comboLabelLeftOffset;
	printerlabelcontrol.style.color = 'white';
	
	var modellabelcontrol = document.createElement('label');
	modellabelcontrol.style.position = 'absolute';
    modellabelcontrol.style.top = modelComboBox.style.top;
	modellabelcontrol.style.left = comboLabelLeftOffset;
	modellabelcontrol.style.color = 'white';

    var printerLabelLocalized = window.external.getLocalizedString('PrinterLabel');
    var modelLabelLocalized = window.external.getLocalizedString('ModelLabel');
	
	var printerlabelstring = document.createTextNode(printerLabelLocalized);
	printerlabelcontrol.appendChild(printerlabelstring);
	
	var modellabelstring = document.createTextNode(modelLabelLocalized);
	modellabelcontrol.appendChild(modellabelstring);

    comboParent.appendChild(modellabelcontrol);  
    comboParent.appendChild(printerlabelcontrol); 
}

/**
* handler for selecting a model in model combo
*/
function onModelSelect()
{
    var combo = document.getElementById("modelComboid"); 
    var selectedModelMTMN;
        
    if(null != combo)
    {     
       selectedModelMTMN = combo.options[combo.selectedIndex].value ;
       if((null != selectedModelMTMN) && (null != selectedPrinterID))
       {
            //Use try/catch block in case there's a Dispatch issue
            try
            {
                window.external.setSelectedPrinternModel(selectedPrinterID, selectedModelMTMN);
            }
            catch(e) {}  
       }
    }    
}

var mainbody;
var mainchildnode;
var breadtrail;
function onScreenClick(screenID,category)
{  
    for(var j=0; j < breadtrail.childNodes[0].cells.length; j++)
    {
        var child = breadtrail.childNodes[0].cells[j];
        if( child.id == "breadcategory" )
        {
            child.innerHTML = category.replace("&","");
        }
    }
 
    var xmlstring = window.external.showScreen(screenID);
    
    hidenseek(xmlstring);
       
    breadtrail.style.display = 'block';
    this.focus();
}

function OnLinkClick(screenID,category,stringPath)
{
    var translatedCategory;
    translatedCategory = getString(category,stringPath);
    
    onScreenClick(screenID,translatedCategory);
}

function getString( id, stringspath)
{
    var index = stringspath.indexOf(",");
    
    while(index != -1 )
    {
        stringspath = stringspath.replace(",","\\");
        index = stringspath.indexOf(",");
    }
      
    //var xdoc = new ActiveXObject("Microsoft.XMLDOM");
    var xdoc;
    try
    {
        xdoc=new ActiveXObject("Msxml2.DOMDocument.3.0");
    }
    catch(err)
    {
        alert('Unable to instantiate ActiveXObject Msxml2.DOMDocument.3.0. Printer Home will not run correctly. Please check system settings.');
        return;
    }

    xdoc.async = false;
    xdoc.load(stringspath);
    var strings = xdoc.getElementsByTagName("string");
    var index = 0;
    var scanButtonText;
    do
    {
      if( strings[index].attributes[0].name == "id" )
      {
          if( strings[index].attributes[0].value == id )
          {
              string = strings[index].firstChild.nodeValue;
              break;
          }
      }        
      index++ ;
    }while(index < strings.length);
      
    return string;
}

function hidenseek(xmlstring)
{
    var child;
    var printer;
    for(var j=0; j < document.body.childNodes.length; j++)
    {
        child = document.body.childNodes[j];
 
        if( child.className == "content")
        {
            mainchildnode = child;
            break;
         }
    }  
      
    //var xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
    var xmlDoc;
    try
    {
        xmlDoc=new ActiveXObject("Msxml2.DOMDocument.3.0");
    }
    catch(err)
    {
        alert('Unable to instantiate ActiveXObject Msxml2.DOMDocument.3.0. Printer Home will not run correctly. Please check system settings.');
        return;
    }
    xmlDoc.async="false";
    xmlDoc.loadXML(xmlstring);
    
    //var xslt = new ActiveXObject("Msxml2.XSLTemplate.3.0");
    var xslt;
    try
    {
        xslt = new ActiveXObject("Msxml2.XSLTemplate.3.0");
    }
    catch(err)
    {
        alert('Unable to instantiate ActiveXObject Msxml2.XSLTemplate.3.0. Printer Home will not run correctly. Please check system settings.');
        return;
    }
    //var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.3.0");
    var xslDoc; 
    try
    {
        xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.3.0");
    }
    catch(err)
    {
        alert('Unable to instantiate ActiveXObject Msxml2.FreeThreadedDOMDocument.3.0. Printer Home will not run correctly. Please check system settings.');
        return;
    }
    var xslProc;
    xslDoc.async = false;
    xslDoc.load("subcategory.xsl");
    xslt.stylesheet = xslDoc;
    
    xslProc = xslt.createProcessor();
    xslProc.input = xmlDoc;
    xslProc.transform();

    mainbody = child.innerHTML;
    child.innerHTML = "";
    child.innerHTML = xslProc.output;
}


/**
* Handler for changing the selected printer. 
*
* @param   printerID    The ID of the printer being selected.
*/
function onPrinterSelected(printerID)
{
    selectedPrinterID = printerID; //set the global variable
        
    var xmlObj = xmlDocX;
    var printerListNode = xmlObj.getElementsByTagName('printerList');
    
    if(null == printerListNode)
    {
        return;
    }
    
    var modelCombo = document.getElementById("modelComboid"); 
	var modelText = "";
	
    for ( var x = 0; x < printerListNode[0].childNodes.length; x++ ) 
    {
        var printerNode = printerListNode[0].childNodes[x];
        
        if(null != printerNode)
        { 
            var printerNameIter = printerNode.getAttribute('id');
            
            if(printerNameIter == printerID)
            {
                if((printerNode.childNodes!= null) && (printerNode.childNodes[0] != null))
                {
                    var modelListNode = printerNode.childNodes[0];
                    
                    if((modelListNode.childNodes != null) && (modelListNode.childNodes[0] != null))
                    {
			            for ( var j = 0; j < modelListNode.childNodes.length; j++)
			            {
			                var modelNode = modelListNode.childNodes[j];
            			                         	    
        	                var modelSel = modelNode.getAttribute('selected');
                    	    
        	                if((modelSel != null) && (modelSel == 'true'))
			                {
    		                    modelText = modelNode.getAttribute('name');   
        	                }			   					
        	            }
        	        }
        	    } 
            }  
        }     
    }   
    
    var i;
    for(i=modelCombo.options.length-1;i>=0;i--)
    {
        if((null!=modelText) && (modelCombo.options[i].text == modelText))
        {
            modelCombo.options[i].selected = true;
            if (null != modelCombo.onchange)
            {
                modelCombo.onchange();
                break;//we need to break here..otherwise the onchange() is not getting called!
            }
        }
    }            
}

/**
* Activates a tab. Makes the tab the active tab, and shows the associated tab 
* body. It does this by changing the tab's CSS class to add the 'tab-active' 
* class, removing the 'tab-active' and 'tab-hover' class from all tabs in the 
* same group, hiding the all tab bodies, and showing the active tab body.
*
* @param   id  The id of the tab to activate.
*/
function activateTab(id)
{
    var tab = document.getElementById(id);
    var tabBody = document.getElementById('tab' + id);

    // Make all tabs inactive / not hovered
    for(var i=0; i < tab.parentNode.childNodes.length; i++)
    {
        var child = tab.parentNode.childNodes[i];
        removeClassName(child, 'tab-active');
        removeClassName(child, 'tab-hover');
    }
    // Activate the clicked tab
    addClassName(tab, 'tab-active');

    // Hide all the tab bodies
    for(var j=0; j < tabBody.parentNode.childNodes.length; j++)
    {
        var child = tabBody.parentNode.childNodes[j];
        child.style.display = 'none';
    }
    // Show the selected tab body
    tabBody.style.display = 'block';
}


////////////////////////////////////////////////////////////////////////////////
// Utilities
////////////////////////////////////////////////////////////////////////////////

/**
* Check for a CSS class assigned to an element
*
* @param    element     The element to test.
*
* @param    className   The name of the class to look for.
*
* @return   true, if the element's CSS class includes the specified className.
*/
function hasClassName(element, className)
{
    if(element.className.length > 0)
    {
        if(element.className === className)
        {
            return true;
        }

        var re = new RegExp("(^|\\s)" + className + "(\\s|$)");

        if(re.test(element.className))
        {
            return true;
        }
    }

    return false;
}

/**
* Adds a CSS class to an element
*
* @param    element     The element to modify.
*
* @param    className   The name of the class to add.
*
* @return   Returns the supplied element.
*/
function addClassName(element, className) 
{
    if(!hasClassName(element, className))
    {
        if(element.className.length > 0)
        {
            element.className += " ";
        }

        element.className += className;
    }

    return element;
}

/**
* Removes a CSS class from an element
*
* @param    element     The element to modify.
*
* @param    className   The name of the class to remove.
*
* @return   Returns the supplied element.
*/
function removeClassName(element, className) 
{
    var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)");

    element.className = strip(element.className.replace(re, " "));

    return element;
}

/**
* Removes blanks from both ends of a string
*
* @param    src     The string to remove whitespace from.
*
* @return   Returns the supplied string, without any whitespace on either end.
*/
function strip(src)
{
    var s = src;

    s = s.replace(/^\s+/, "");
    s = s.replace(/\s+$/, "");

    return s;
}

/**
* Determines the absolute position of an element.
*
* @param    elem    The element whose position is to be determined
*
* @return   A new object, with the absolute position stored as X (horizontal) 
*           and Y (vertical).
*/
function findAbsolutePos(elem)
{
    var pos = 
        {
            X: elem.offsetLeft,
            Y: elem.offsetTop
        };
    
    while(elem.offsetParent)
    {
        elem = elem.offsetParent;
        pos.X += elem.offsetLeft;
        pos.Y += elem.offsetTop;
    }
    
    return pos; 
}

/**
* Determines if the browser is IE 5.0x 
*
* @return   true, if the current browser is IE 5.0x
*/
function isIe5()
{
    var verExtract = /MSIE (\d.\d+);/;
    var matches = verExtract.exec(navigator.appVersion);

    if(matches && matches[1])
    {
        var versionStr = matches[1];
        var ver = parseFloat(versionStr);
        if(ver < 5.5)
        {
            return true;
        }
    }
    
    return false;
}

/**
* Dispatches the current event to it's source element. The event is then bubbled 
* up to each containing element until the element with mouse capture is reached.
* This is a workaround for IE5, which does not automatically dispatch events to 
* the contents of a container that has captured the mouse input. 
*
* @param    container   The element that has captured the mouse input.
*/
function dispatchEvent(container)
{
    var srcElem = window.event.srcElement;

    // Only perform the dispatch to elements within the element that has 
    // captured the mouse input.
    if( container.contains(srcElem) )
    {
        var target = srcElem;

        // Bubble the event out until we hit this level.
        while(target && (target !== container))
        {
            // Build the handler name: "on" + "mouseover", for example.
            var eventName = "on" + event.type;
            
            // If the target has a handler registered for the specified event 
            // name, call it.
            if(target[eventName])
            {
                target[eventName]();
            }
            
            // Move up one level
            target = target.parentElement;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Ogre
////////////////////////////////////////////////////////////////////////////////
/**
* An array of parameterless functions that will, when called, cause a particular
* control element to display. These are used during Ogre testing to automate the
* display of menus and tabs.
*/
var ogreArray_ = [];

/**
* Registers a control for use with Ogre. 
*
* @param    id          The id of the control. This will be passed as a 
*                       parameter to the function indicated by funcName
*
* @param    funcName    The name of the function that will be called in order to
*                       show the specified control. 
*/
function registerOgreControl(id, funcName)
{
    // Construct a 0 param function that will cause the appropriate element to 
    // show.
    var func = function(){ window[funcName](id); };

    // Store the function for later use by ogre. (Array.push would be preferable
    // here, however it is not supported on IE5)
    ogreArray_[ogreArray_.length] = func;
}

/**
* Shows the UI element specified by the index.
*
* @param    index   The index into the ogreArray_ of the element that is to be 
*                   displayed.
*/
function showOgreControl(index)
{
    if(0 <= index && index < ogreArray_.length)
    {
        ogreArray_[index]();
    }
}
