	/*************************************************
	 * XMLMENU
	 * An HTML drop-down-menu, based in a XML file
	 *
	 * @version: 1.0
	 * @author: Ricardo González (ricardo_agp@hotmail.com)
	 * @note: If you are going to use this component or modify it, 
	 * please write me an e-mail, in order to know which new 
	 * features could be included in future versions.
	 ************************************************/
	/*************************************************
	 * Attributes
	 ************************************************/
	/**
	 * Variable to contains the DOM object for the XML Menu.
	 */
	var xmlDoc;
	
	/**
	 * Variable to contains the DOM object for the XSL file.
	 */
	var xslDoc;

	/*************************************************
	 * Constants
	 ************************************************/
	/**
	 * Contains the name of the XSL file used to generated the HTML menu from an XML document.
	 */
	var XSL_FILE_NAME = "styles/xmlmenu.xsl";
	
	/**
	 * Contains the name of the style class used in the menu items when they have the focus on it.
	 */
	var MENU_ITEM_GET_FOCUS_CLASS_NAME = "menu_item_get_focus";

	/**
	 * Contains the name of the style class used in the menu items when they don't have the focus on it.
	 */
	var MENU_ITEM_LOST_FOCUS_CLASS_NAME = "menu_item_lost_focus";

	/**
	 * Contains the name of the style class used in the item's subitems when they have the focus on it.
	 */
	var MENU_SUBITEM_GET_FOCUS_CLASS_NAME = "menu_subitem_get_focus";

	/**
	 * Contains the name of the style class used in the item's subitems when they don't have the focus on it.
	 */
	var MENU_SUBITEM_LOST_FOCUS_CLASS_NAME = "menu_subitem_lost_focus";

	/**
	 * Indicates the horizontal space between the left side of the item's cell and the left border 
	 * of its subitems group.
	 */
	var LEFT_SPACE_SUBITEMS_GROUP = 10;

	/**
	 * Indicates the vertical space between the bottom side of the item's cell and the top border 
	 * of its subitems group.
	 */
	var VERTICAL_SPACE_SUBITEMS_GROUP = 2;

	/**
	 * Error message presented when trying to load the XMLMenu
	 * inside an element which name begins with the follow values.
	 */
	var RESERVED_NAMES = new Array("xmlMenu", "xmlMenuRow", "item", "subitemGroup", "subitem");
	
	/**
	 * Error message presented when trying to access to an HTML element
	 * that doesn't exist in the page.
	 */
	var ERROR_ELEMENT_NOT_FOUND = "The element was not found";

	/*************************************************
	 * METHODS
	 ************************************************/
	/**
	 * Loads the XML Menu from a XML text into the
	 * client page element.
	 *
	 * @param xmlMenuText A String, 
	 *		Represents the text of the XML with the XMLMenu
	 * @param elementId A String, 
	 *		Represents the page element's name where the XML Menu will be inserted
	 */
	function loadXMLMenuFromText(xmlMenuText, elementId) {
		return loadXMLMenu(xmlMenuText, elementId, 1);
	}
	
	/**
	 * Loads the XML Menu from a XML file into the
	 * client page element.
	 *
	 * @param nameXMLMenuFile A String, 
	 *		Represents the path of the XML file with the XMLMenu
	 * @param elementId A String, 
	 *		Represents the page element's name where the XML Menu will be inserted
	 */
	function loadXMLMenuFromFile(nameXMLMenuFile, elementId) {
		return loadXMLMenu(nameXMLMenuFile, elementId, 0);
	}
	 
	/**
	 * Loads the XML Menu from a XML file into the
	 * client page element.
	 *
	 *
	 * @param sourceXMLMenu A String, 
	 *		Represents the path of the XML file with the XMLMenu or the XML text
	 * @param elementId A String, 
	 *		Represents the page element's name where the XML Menu will be inserted
	 * @param sourceType An int to indentify if the XML document will be loaded from a File or a Text.
	 *		Use 0 to Indicate to load the XML document from a File
	 *		Use 1 to Indicate to load the XML document from a Text
	 */
	function loadXMLMenu(sourceXMLMenu, elementId, sourceType) {
		// Check the input parameters
		if (sourceXMLMenu == null) {
			alert("Error loading the XML Menu: the input parameter for the XML's file name cannot be null.");
			return;
		}
		else if (elementId == null) {
			alert("Error loading the XML Menu: the input parameter for the page client element cannot be null.");
			return;
		}
		else if (sourceType < 0 || sourceType > 1) {
			alert("Error loading the XML Menu: the input parameter source type is different than 0 or 1:\nUse 0 to indicate a source file.\nUse 1 to indicate a source text.");
			return;
		}
		
		// Check if the page client element has a valid name (a named not contained in the RESERVED_NAMES constant) 
		for (var i = 0; i < RESERVED_NAMES.length; i++) {
			if (elementId.indexOf(RESERVED_NAMES[i]) == 0) {
				alert("Error loading the XML Menu: the page client element ('" + elementId + "') cannot begins with the string '" + RESERVED_NAMES[i] + "'.");
				return;
			}
		}
	
		// Get the client element to insert the XML Menu
		var element = document.getElementById(elementId);

		// Check if the element exists
		if (element == null) {
			alert("Error getting the client element to insert the XML Menu: " + ERROR_ELEMENT_NOT_FOUND + " ('" + elementId + "').");
			return;
		}

		// Get the XML DOM object from a File
		if (sourceType == 0) {
			xmlDoc = getXMLDomFromFile(sourceXMLMenu);
		}
		else if (sourceType == 1) { //From a Text
			xmlDoc = getXMLDomFromText(sourceXMLMenu);
		}

		// Get the XSL DOM object from a File
		xslDoc = getXMLDomFromFile(XSL_FILE_NAME);

		// Get the generated HTML code of teh XML document using the XSL file
		var generatedXMLMenu = getXMLTransformedWithXSL(xmlDoc, xslDoc);

		// Write the HTML code inside the client element which will contain the enerated XML Menu
		element.innerHTML = generatedXMLMenu;
	}
	
	/**
	 * Marks a menu item (used when the mouse is over him).
	 *
	 * Sets the name of the style class in items to the one defined in
	 * MENU_ITEM_GET_FOCUS_CLASS_NAME constant
	 *
	 * @param itemNumber An int, Represents the number of the menu item to mark
	 */
	function markItem(itemNumber) {
		// Get the element
		var itemName = "item" + itemNumber;
		var itemElement = document.getElementById(itemName);

		// Check if the element exists
		if (itemElement == null) {
			alert("Error marking an item: " + ERROR_ELEMENT_NOT_FOUND + " ('" + itemName + "').");
			return;
		}

		// Change the class of the element
		itemElement.className = MENU_ITEM_GET_FOCUS_CLASS_NAME;

		// Display the item's subitems group
		showSubitemsGroup(itemNumber);
	}

	/**
	 * Unmarks a menu item (used when the mouse is out  him).
	 *
	 * Sets the name of the style class in items to the one defined in
	 * MENU_ITEM_LOST_FOCUS_CLASS_NAME constant
	 *
	 * @param itemNumber An int, Represents the number of the menu item to mark
	 */
	function unmarkItem(itemNumber) {
		// Get the element
		var itemName = "item" + itemNumber;
		var itemElement = document.getElementById(itemName);

		// Check if the element exists
		if (itemElement == null) {
			alert("Error unmarking an item: " + ERROR_ELEMENT_NOT_FOUND + " ('" + itemName + "').");
			return;
		}

		// Change the style class of the element
		itemElement.className = MENU_ITEM_LOST_FOCUS_CLASS_NAME;

		// Hide the item's subitems group
		hideSubitemGroup(itemNumber);
	}

	/**
	 * Marks a item's subitem (used when the mouse is over him).
	 *
	 * Sets the name of the style class in items to the one defined in
	 * MENU_SUBITEM_GET_FOCUS_CLASS_NAME constant
	 *
	 * @param itemNumber An int, Represents the number of the menu item
	 * @param subitemNumber An int, Represents the number of the item's subitem to mark
	 */
	function markSubitem(itemNumber, subitemNumber) {
		// Get the element
		var subitemName = "subitem" + itemNumber + "-" + subitemNumber;
		var subitemElement = document.getElementById(subitemName);

		// Check if the element exists
		if (subitemElement == null) {
			alert("Error marking a subitem: " + ERROR_ELEMENT_NOT_FOUND + " ('" + subitemName + "').");
			return;
		}

		// Change the style class of the element
		subitemElement.className = MENU_SUBITEM_GET_FOCUS_CLASS_NAME;
	}

	/**
	 * Unmarks a item's subitem (used when the mouse is over him).
	 *
	 * Sets the name of the style class in items to the one defined in
	 * MENU_SUBITEM_LOST_FOCUS_CLASS_NAME constant
	 *
	 * @param itemNumber An int, Represents the number of the menu item
	 * @param subitemNumber An int, Represents the number of the item's subitem to mark
	 */
	function unmarkSubitem(itemNumber, subitemNumber) {
		// Get the element
		var subitemName = "subitem" + itemNumber + "-" + subitemNumber;
		var subitemElement = document.getElementById(subitemName);

		// Check if the element exists
		if (subitemElement == null) {
			alert("Error unmarking a subitem: " + ERROR_ELEMENT_NOT_FOUND + " ('" + subitemName + "').");
			return;
		}

		// Change the style class of the element
		subitemElement.className = MENU_SUBITEM_LOST_FOCUS_CLASS_NAME;
	}

	/**
	 * Shows the box with the subitems for the given menu item number
	 *
	 * @param itemNumber An int, Represents the number of the menu item
	 */
	function showSubitemsGroup(itemNumber) {
		// Get the subitemgroup element
		var subitemGroupElement = document.getElementById("subitemGroup" + itemNumber);

		// If the item doesn't have a subitem group then exit from the method.
		if (subitemGroupElement == null)
			return;

		// Get the menu and item objects
		var xmlMainMenuElement = document.getElementById("xmlMenu");
		var itemElement = document.getElementById("item" + itemNumber);
		var textItemElement = document.getElementById("textItem" + itemNumber);

		// Check if the elements exist
		if (xmlMainMenuElement == null) {
			alert("Error getting the XML Main Menu: " + ERROR_ELEMENT_NOT_FOUND + " ('" + xmlMainMenuElement + "').'n Please, check the XSL file: " + XSL_FILE_NAME);
			return;
		}
		else if (itemElement == null) {
			alert("Error getting menu item: " + ERROR_ELEMENT_NOT_FOUND + " ('" + itemElement + "'). Please, check the XSL file: '" + XSL_FILE_NAME + "'.");
			return;
		}
		else if (textItemElement == null) {
			alert("Error getting the menu item's text: " + ERROR_ELEMENT_NOT_FOUND + " ('" + textItemElement + "'). Please, check the XSL file: '" + XSL_FILE_NAME + "'.");
			return;
		}		

		// Variable to store the bottom of the Item's position
		// Equal to the top position of the XML Main Menu plus the top position and the height of the Item's text element.
		var bottomItemPosition = xmlMainMenuElement.offsetTop + textItemElement.offsetTop + textItemElement.offsetHeight;
		// Variable to store the top position for the subitems group's box
		var topPosition = bottomItemPosition + VERTICAL_SPACE_SUBITEMS_GROUP;

		// Variable to store the left of the Item's position
		// Equal to the left position of the XML Main Menu plus the left position of the Item element.
		var leftItemPosition = xmlMainMenuElement.offsetLeft + itemElement.offsetLeft;
		// Variable to store the left position for the subitems group's box
		var leftPosition = leftItemPosition + LEFT_SPACE_SUBITEMS_GROUP;

		// Show the subitems group's box
		//subitemGroupElement.style.left = leftPosition;
		//subitemGroupElement.style.top = topPosition;
		subitemGroupElement.style.position = "absolute";
		subitemGroupElement.style.display = "block";
	}

	/**
	 * Hides the box with the subitems group for the given menu item number.
	 *
	 * @param itemNumber An int, Represents the number of the menu item
	 */
	function hideSubitemGroup(itemNumber) {
		// Get the element
		var subitemGroupElement = document.getElementById("subitemGroup" + itemNumber);

		// If the Item has subitems group the hide is subitem group's box
		if (subitemGroupElement != null)
			subitemGroupElement.style.display = "none";
	}

	/*****************************************************
	 * Functions to get the XML DOM object and do the transformation
	 * of a XML using a XSL file
	 * Based from the code found in: http://www.bigbold.com/snippets/posts/show/649
	 *****************************************************/
	/**
	 * Gets a DOM object from the XML file received as paratemer
	 * 
	 * @param xmlFileName A String, contains the path of the XML file
	 */
	function getXMLDomFromFile(xmlFileName) {
		// Check if the file name is not null
		if (xmlFileName == null) {
			alert("Error getting the XML Dom object: The file name cannot be null.");
			return;
		}

		// Variable to store the DOM object from the XML document received.
		var xmlDOM;

		// Load XML for Internet Explorer
		if (typeof ActiveXObject != 'undefined') {
			xmlDOM = new ActiveXObject("Microsoft.XMLDOM");
				xmlDOM.async = false;
				xmlDOM.load(xmlFileName);
		}
		else { // Others browsers
			var xmlRequest = new XMLHttpRequest();
			xmlRequest.open("GET", xmlFileName, false);
			xmlRequest.send(null);
			xmlDOM = xmlRequest.responseXML;
		}

		return xmlDOM;
	}

	/**
	 * Gets a DOM object from the XML text received as parameter.
	 *
	 * @param xmlText A String, contains the XML document as string value.
	 */
	function getXMLDomFromText(xmlText) {
		// Check if the file name is not null
		if (xmlText == null) {
			alert("Error getting the XML Dom object: The source text is null.");
			return;
		}

		// Variable to store the DOM object from the XML document received.
		var xmlDOM;

		// Load XML for Internet Explorer
		if (typeof ActiveXObject != 'undefined') {
			xmlDOM = new ActiveXObject("Microsoft.XMLDOM");
			xmlDOM.async = false;
			xmlDOM.loadXML(xmlText);
		}
		else { // Others browsers
			parser = new DOMParser();
			xmlDOM = parser.parseFromString(xmlText, "text/xml");
		}

		return xmlDOM;
	}

	/**
	 * Gets the result from the XML conversion using a XSL document.
	 *
	 * @param xmlDoc a DOM object that contains the XML to transform.
	 * @param xslDoc a DOM object that contains the XSL to transform the XML document.
	 */
	function getXMLTransformedWithXSL(xmlDoc, xslDoc) {
		// Check if the parameters are null
		if (xmlDoc == null) {
			alert("Error transforming the XML document: The xml document is null.");
			return;
		}
		else if (xmlDoc == null) {
			alert("Error transforming the XML document: The xsl document is null.");
			return;
		}

		// Variable to store the generated result of the transformation
		// of the XML document using the XSL document
		var generatedHTML;

		// Internet Explorer
		if (typeof ActiveXObject != 'undefined') {
			generatedHTML = xmlDoc.transformNode(xslDoc);
		}
		else { // Other browsers
			// Create an XSLT Processor
			var xsl = new XSLTProcessor();
			xsl.importStylesheet(xslDoc);

			// Get the HTML DOM object generated by the transformation
			var generatedHTMLDOM = xsl.transformToDocument(xmlDoc);

			// If the HTML DOM object has childs then get its HTML text
			if (generatedHTMLDOM.childNodes.length > 0) {
				generatedHTML = generatedHTMLDOM.documentElement.innerHTML;

				// To obtain the same generated HTML table (as Internet Explorer does), 
				// it is necessary to put  the obtained table inside a DIV element
				generatedHTML = "<div>" + generatedHTML + "</div>";

				// Constants for the HTML code elements to replace
				// Because the attribute "disable-output-escaping" in the XSL document
				// works only with Internet Explorer
				var HTML_LOWER_THAN = "&lt;";
				var HTML_GREATER_THAN = "&gt;";

				// Replace all the HTML lower than element with "<"
				while  (generatedHTML.indexOf(HTML_LOWER_THAN) > -1)
					generatedHTML = generatedHTML.replace(HTML_LOWER_THAN, "<");

				// Replace all the HTML greater than element with ">"
				while  (generatedHTML.indexOf(HTML_GREATER_THAN) > -1)
					generatedHTML = generatedHTML.replace(HTML_GREATER_THAN, ">");

			}
			else {
				alert("Error transforming the XML document: The generated HTML DOM object is empty.");
			}
		}

		return generatedHTML;
	}
