//-------------------------------------------------------------------------------
// Description: This class generates an html form options menu
//              for a javascript application.
// Dependencies: jQuery is needed, and must be included before this file
//-------------------------------------------------------------------------------
function OptionsMenu(call_back,           // Callback function called when option changes
		             menu_style,          // Style in which to add the menu items
		             options,             // Pointer to client data to be updated in menu
		             option_names,        // Ordered names for the client data properties
		             menuParentId,        // id of menu parent
		             client_name,         // Name of client program/class
		             client_description,  // Text describing the client program
		             menu_options,        // Options to be displayed in menu
		             toggle_key){         // Key that toggles menu (if undefined no binding occurs)

	var mCallBack = call_back,
	    menuStyle = menu_style || 1,
	    settings = options,
	    pID = menuParentId,
	    optionNames = option_names,
	    clientName = client_name || ("defaultMenu" + Math.round((Math.random()*10000))),
	    clientDescription = client_description || "",
	    moptions = menu_options,
	    menuToggleKey = toggle_key;
	
	var internalName = clientName.replace(" ", "");     // ID's can't have spaces
	var formId = internalName + "Form";
	var closeButtonId = internalName + "close";
	var createXMLButtonId = internalName + "createXml";
	var xmlSettingsClass = internalName + "Settings";
	var active = false;          // Has Menu information been generated already?
	var visible = false;         // Is Menu currently displayed on screen?
	var thisref = this;          // Alternate reference to "this" object
	var keyBound = false;        // Has menu toggle key been bound?
	var formHtml = "";           // Html text for the entire menu form
	var inputs = [];             // Id and html information for menu inputs
	var menuItems = [];          // All menu items, including inputs and static text
	var inputsCreated = false;   // Have inputs been generated yet?
	
	var ready = function(){
	    return mCallBack && options && optionNames && pID && moptions;
	};

	var createCheckBox = function(i, id, text){
	    return '<td><input type = "checkbox" id = "' + 
	           id + '" /> ' + 
	           text + '</td>';
	};
	var createTextEntry = function(i, id, text){
	    return '<td align="center"><input type = "text" id = "' + 
	           id + '" /> ' + 
	           text + '</td>';
	};
	var createPairedText = function(text1, text2){
		return '<td style = "text-align: center;">' +
		        text1 + '</td><td>' + text2 + '</td>';
	};
	var createTextWithDropdown = function(id, text1, text2, dropValues){
		var dropHtml = '<td style = "text-align: center;">' +
		               text1 + '</td><td>' + text2 + '<select id = "' + id +
		               '" title = "Not supported by all browsers">';
		
		for (var i = 0; i < dropValues.length; i++){
		   dropHtml += '<option id="' + internalName + dropValues[i] + '" value="' +
		               dropValues[i] + '">' + dropValues[i] + '</option>';
		}
		dropHtml += '</select></td>';
		return dropHtml;
	};
	
	// Creates the input html elements in the menu form
	var createMenuItems = function(){
		var func_table = {"checkbox": createCheckBox,
				          "textEntry": createTextEntry,
				          "pairedText": createPairedText,
				          "textWithDropdown": createTextWithDropdown};
		var n = 0;
	    for (var i = 0; i < moptions.length; i++){
	    	if (moptions[i].type === "checkbox" || moptions[i].type === "textEntry"){
	    	   inputs[n] = {};
	    	   inputs[n].type = moptions[i].type;
	    	   inputs[n].xml = moptions[i].xmlTag;
	    	   inputs[n].id = internalName + "input" + (n + 1).toString();
	    	   inputs[n].html = func_table[moptions[i].type](n, inputs[n].id, moptions[i].text);
	    	   menuItems[i] = inputs[n];
	    	   n++;
	    	}
	    	else if (moptions[i].type === "textWithDropdown"){
	    	   inputs[n] = {};
	    	   inputs[n].type = moptions[i].type;
	    	   inputs[n].xml = moptions[i].xmlTag;
	    	   inputs[n].id = internalName + "input" + (n + 1).toString();
	    	   inputs[n].html = func_table["textWithDropdown"](inputs[n].id, moptions[i].text1, moptions[i].text2, moptions[i].dropValues);
	    	   menuItems[i] = inputs[n];
	    	   n++;
	    	}
	    	else if (moptions[i].type === "pairedText"){
	    	   menuItems[i] = {};
	    	   menuItems[i].type = moptions[i].type;
	    	   menuItems[i].xml = moptions[i].xmlTag;
	    	   menuItems[i].html = func_table[moptions[i].type](moptions[i].text1, moptions[i].text2);
	    	} else {
	    		window.alert("Not a valid menu Item: " + moptions.type);
	    	}
	    }
	    inputsCreated = true;
	};
	
	var createInputString = function(){
		
		if (!inputsCreated) createMenuItems();
		
	    var str = "";
	    for (var i = 0; i < menuItems.length; i++){
	    	str += ((i % 2) == 0) ? "<tr>" : "";
	    	str += menuItems[i].html;
	    	str += ((i % 2) == 1) ? '</tr>' : "";
	    }
	    str += ((menuItems.length % 2) == 1) ? "</tr>" : "";
	    return str;
	};
	
	var createInputColumn = function(start_index, stop_index){
		if (!inputsCreated) createMenuItems();
		if (!stop_index) stop_index = menuItems.length;
		
	    var str = "";
	    for (var i = start_index; i < stop_index && i < menuItems.length; i++){
	    	str += "<tr>" + menuItems[i].html + "</tr>";
	    }
	    return str;
	}
	
    // Binds event handlers to all input elements in the menu form
	var bindInputs = function(){
		
	   var bindMode = {"checkbox": "click",
			           "textEntry": "keyup",
			           "textWithDropdown": "change"};
		
       for (var i = 0; i < inputs.length; i++){
	       $("#" + inputs[i].id).bind(bindMode[inputs[i].type], function(event){
	    	  for (var y = 0; y < inputs.length; y++){
	    	     if (inputs[y].type === "checkbox"){
	    	         settings[optionNames[y]] = $("#" + inputs[y].id).get(0).checked;
	    	     }
	    	     else if (inputs[y].type === "textEntry" || inputs[y].type === "textWithDropdown"){
	    	    	 settings[optionNames[y]] = $("#" + inputs[y].id).get(0).value;
	    	     }
	    	  }
	     	    
	     	  // Uses callback function to update the options menu client
	     	  mCallBack();
	       });
       }
	}

	// Returns the textual representation of key code
	var getKeyName = function(k){
		var keyText = {112: "F1", 113: "F2", 114: "F3", 115: "F4",
				       116: "F5", 117: "F6", 118: "F7", 119: "F8",
			           120: "F9", 121: "F10", 122: "F11", 123: "F12"};
		var closeButtonKey = "";
		
		// Does key exist and does an entry for it exist in name table
		if (k){
		    if (keyText[k]){
			   closeButtonKey = " [" + keyText[k] + "]";
			}
		}
		return closeButtonKey;
	};
	
	var createMenuHTML = function(){
		
		createMenuItems();  // Generates html and id information for the input elements in the form
		formHtml += '<form id  = "' + formId + '"> ' +
		              '<table align = "center"><tr><td><p style = "text-align: left;">' + clientDescription +
                      '</p></td></tr></table>' +
                      '<table align = "center" style = "background-color: #C8C8C8; border-style: solid; border-width: 0.5em; border-color: #727272;">' +
                      '<th style = "text-align: center; background-color: #A8A8A8;">' + clientName + ' Options</th>';

		if (menuStyle == 1){
           formHtml +=
              '<tr><td style = "background-color: #E8E8E8;">' +
              '<table cellpadding = "2" id = "' + internalName + 'OptionsTableArea" align = "center" style = "background-color: #E8E8E8;">' +
              createInputString() +
              '</table>' +
              '</tr>';
		}
		else{
			formHtml +=
			             '<th />' +
			             '<th style = "background-color: #A8A8A8; text-align: center;">Key Bindings</th>' +
			             '<tr><td style = "background-color: #E8E8E8;">' +
			             '<table align = "center" style = "background-color: #E8E8E8;">' +
			                createInputColumn(0, Math.floor(menuItems.length/2)) +
			             '</table>' +
			       
			             '<td style = "width: 0.2em;" />' +
			             '<td style = "background-color: #E8E8E8;">' +
			             '<table align = "center" style= "background-color: #E8E8E8;">' +
			                '<th style = "background-color: #D8D8D8; text-align: center;">Key</th>' +
			                '<th style = "background-color: #D8D8D8; text-align: center;">Function</th>' +
			                createInputColumn(Math.floor(menuItems.length/2)) +
			             '</table>' +
			             '</td></tr>';
		}
		
		// This part of the menu is the same regardless of the template chosen
		formHtml += '<tr><td></td></tr>' +
                    '<tr><td colspan = "3" style = "background-color: #A8A8A8;">' +
                      '<table align = "center" style = "background-color: #A8A8A8;">' +
                        '<tr><td><input type = "button" id = "' + createXMLButtonId + '" value = "Save Settings" /></td>' +
                        '<td><input type = "button" id = "' + closeButtonId + '" value = "Close' + getKeyName(menuToggleKey) + '" /></td></tr>' +
                      '</table>' +
                    '</td></tr>' +
                    '<tr align = "center"><td colspan = "3" style = "text-align: center;">' +
                      '<table align = "center" style = "width: 100%; background-color: #C8C8C8; text-align: left;">' +
                        '<tr><td id = "' + internalName + 'Explanation"></td></tr>' +
                        '<tr><td id = "' + internalName + 'TextArea"></td></tr>' +
                      '</table>' +
                    '</tr>' +
                    '</td></tr></table>' +
                  '</form>';
	};
	
	var createTextArea = function(XMLcode){
	    var TextField =  '<p style = "text-align: left;" id = "presentSaveExplanation">' +
                         "The above settings have been applied.  To make the "+ 
                         "settings persist after a page reload, edit this "+
                         "page and <br>copy the following into the top part of the text area:</p>" +
	    	             '<textArea id = "' + internalName + 'TextAreaElement" cols = "65" rows = "12"></textArea>';
	    
	    if (!($("#" + internalName + "TextArea *").is("#" + internalName + "TextAreaElement"))){
	        $("#" + internalName + "TextArea").prepend(TextField);
	    }
	     
	    $("#" + internalName + "TextAreaElement").attr("value", XMLcode);
	};
    
    //-----------------------------------------------------------
	// The createXML function is used to generate the XML
	// settings structure that is sent to the textArea in the
	// options menu.  
	//-----------------------------------------------------------
	var createXML = function(){
	    var XMLcode = '<span class = "' + xmlSettingsClass + '" style = "display: none;">\n' +
	                  '<' + internalName + 'Options> \n';
	    for (var i = 0; i < inputs.length; i++){
	       XMLcode += "<" + inputs[i].xml + ">" + 
	       settings[optionNames[i]] + "</" +
	       inputs[i].xml + ">\n";
	    }
	    XMLcode += "</" + internalName + "Options>\n</span>";
	    
	    return XMLcode;
	};
	
	//-----------------------------------------------------------
	// Obtains default settings from xml embedded on the page.
	//-----------------------------------------------------------
	this.getXML = function(){
	
	    var xmlObject = $("." + xmlSettingsClass),
	        xmlProperties = $("." + xmlSettingsClass + " *");

	    if (xmlObject.length > 0){
	       
	       // Fixes xml code if it has been escaped
	       if (!($("." + xmlSettingsClass + " *").is(internalName + "Options"))){
	           xmlObject.html(xmlObject.text());
	           xmlProperties = xmlObject.find("*");
	       }
	       
	       // Loops through every combination of valid XML tags and the
	       // actual XML tags found in the settings structure and updates
	       // the settings variables if it can find their matches in the XML.  
	       for (var i = 0; i < optionNames.length; i++){
	          for (var y = 0; y < xmlProperties.length; y++){
	             if (xmlProperties.get(y).nodeName.toUpperCase() == inputs[i].xml.toUpperCase()){
	                if (xmlProperties.get(y).textContent){
	                   if (inputs[i].type === "checkbox"){
	                       settings[optionNames[i]] = 
	                           (xmlProperties.get(y).textContent.toLowerCase() == "true") ? true : false;
	                   }
	                   else if (inputs[i].type === "textEntry" || inputs[i].type === "textWithDropdown"){
	                       settings[optionNames[i]] = xmlProperties.get(y).textContent;
	                   }
	                }
	             }
	          }
	       }
	    }
	};
    
    //-----------------------------------------------------------
	// Sets initial values of the input elements of the form.
	//-----------------------------------------------------------
	var setInputDefaults = function(){
		for (var i = 0; i < inputs.length; i++){
			if (inputs[i].type === "checkbox"){
		       $("#" + inputs[i].id).attr("checked", settings[optionNames[i]]);
			}
			else if (inputs[i].type === "textEntry"){
			   $("#" + inputs[i].id).attr("value", settings[optionNames[i]]);
			}
			else if (inputs[i].type === "textWithDropdown"){
			   $("#" + inputs[i].id).attr("value", settings[optionNames[i]]);
			}
		}
	};
	
	// Toggles the menu between visible and not visible
    this.displayMenu = function(){
	    if (ready() && $(pID + " *").is("#" + formId) === false){
	       
	       $(pID).prepend(formHtml);   // Inserts html menu code onto page
	       visible = true;
	       
	       window.scrollTo(0, 0);      // Scrolls to page top
	       
	       setInputDefaults();         // Set initial input values
	       
           bindInputs();               // Attaches handlers to all form inputs
	       
	       $("#" + closeButtonId).bind("click", function(event){
	           thisref.displayMenu();
	       });
	       $("#" + createXMLButtonId).bind("click", function(event){
	           createTextArea(createXML);
	       });
	    }
	    else{
	        $("#" + formId).remove();   // Removes menu from page
	        visible = false;
	    }
    };
    
    // Binds chosen key to perform menu display/hide operation
    var menuDisplayBinding = function(e){
		if (e.which === menuToggleKey){
	        thisref.displayMenu();
	        return false;
	    }
	};
    this.initialize = function(){
        if (ready() && !active){
            active = true;
            createMenuHTML();
            if (menuToggleKey){
               keyBound = true;
               $(document).bind("keydown", menuDisplayBinding);
            }
        }
    };
    
    // Setters
    this.setToggleKey = function(k){
        if (keyBound) $(document).unbind("keydown", menuDisplayBinding);
        keyBound = false;
        menuToggleKey = k;
        if (ready()){
            keyBound = true;
            $(document).bind("keydown", menuDisplayBinding);
        }
    };
    this.setOptionDescriptions = function(o){
        moptions = o;
        thisref.initialize();
    };
    this.setCallback = function(r){
        mCallBack = r;
    };
    this.setClientName = function(n){
    	
    	var oldId = formId;
    	
    	clientName = n || ("defaultMenu" + Math.round((Math.random()*10000)));
	    internalName = clientName.replace(" ", "");     // ID's can't have spaces
	    formId = internalName + "Form";
	    closeButtonId = internalName + "close";
	    createXMLButtonId = internalName + "createXml";
	    
        if (visible){
           document.getElementById(oldId).id = formId;
        }
        createMenuHTML();
    };
    this.setParentElement = function(p){
        pID = p;
        if (!/[\.#]+/.test(pID)){
    	    pID = "#" + pID;
    	}
        if (active && visible){
            thisref.displayMenu();
            thisref.displayMenu();
        }
    };
    this.setDescription = function(d){
    	clientDescription = d;
    };
    
    // Getters
    this.getOptionDescriptions = function(){
        return moptions;
    ;}
    this.getOptions = function(){
        return settings;
    };
    this.getCallBack = function(){
        return mCallBack;
    };
    this.getParentId = function(){
        return pID;
    };
    this.getClientName = function(){
        return clientName;
    };
    this.getMenuId = function(){
        return formId;
    };
    this.getToggleKey = function(){
        return menuToggleKey;
    };
	
	if (!/[\.#]+/.test(pID)){  // Puts id into correct format for jQuery
	    pID = "#" + pID;
	}
	thisref.initialize();      // Sets up menu if it has all needed information
};
