/*  FORE Fracture Risk Calculator Main Page (Pro editions)
    Russ Walker, Walker Associates

    Common JavaScript for fracture risk calculator editions

    NOTE: Version numbering restarted when code factored out of original page; see
    comments in FRCPro.aspx for revisions prior to 3/3/2009.
    
    Version 1.3 1/22/2012
        To work with latest versions of Chrome and Safari browsers, changed
        how XML help messages are loaded, and also changed call to setTimeout
        to display delayed help messages. 

    Version 1.2 6/15/2010
        Restored upper limit on age validation, set to 120 to prevent people entering birth year
        instead of age

    Version 1.1  4/16/2010
        Removed upper limit on age validation

    Version 1.0, 3/3/2009

    Developed by Walker Associates under contract to the Foundation for
    Osteoporosis and Education (FORE).

    Copyright (C) 2009 Foundation for Osteoporosis Research and Education.
    All rights reserved.


*/

    //page data
    var helpXML;    //XML documuent with field help messages
    var helpTimer;  //timer for display of field help after a time delay
    var mouseXPos;     //mouse position on mouseover for field help
    var mouseYPos;     //mouse position on mouseover for field help
    var helpMsgId;    //message ID to be displayed in mouseover help

    //cross-browser function to load an XML doc (from http://w3schools.com/xml/xml_dom.asp)
    function loadXMLDoc(dname)
    {
        var xmlDoc;
        var xmlhttp;
        if (window.XMLHttpRequest)
          {// code for IE7+, Firefox, Chrome, Opera, Safari
              xmlhttp=new XMLHttpRequest();
          }
        else
          {// code for IE6, IE5
              xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
          }
        xmlhttp.open("GET",dname,false);
        xmlhttp.send();
        xmlDoc=xmlhttp.responseXML;        
        return(xmlDoc);
    }

    //initialize page
    function init(helpfile) {

        //load XML help messages
        helpXML = loadXMLDoc(helpfile);

        //put cursor into first field
        document.getElementById("txtAge").focus();

        //scroll to bottom of page if graph is displayed
        if (document.getElementById("riskchart")) {
            scrollToBottom();
        }
    }


    //display help for input field
    // this function just sets a timer to call the showDelayedHelp() function after a short delay
    function showFieldHelp(e,tgt, messageId) {
        tgt.style.backgroundColor = "lightblue";   // highlight help box
        mouseXPos = mouseX(e);                  // save mouse position
        mouseYPos = mouseY(e);
        helpMsgId = messageId;     //pass message ID thru page global var
        helpTimer = setTimeout("showDelayedHelp()", 1000); //1 second delay
    }

    //show help for input field after a time delay
    //this function is called by a timeout set by the showFieldHelp() function
    function showDelayedHelp() {
        var messageId = helpMsgId;  //get the message id to be displayed
       var messageDiv = document.getElementById("fieldhelp");    //get the message display div
        var positionDiv = document.getElementById(messageId);   //get the div to position on top of
       var messages=helpXML.getElementsByTagName("message");   //get array of all help messages from XML doc
       var i;
        var messageText = "Sorry, no help available";           //default help message
        for (i=0; i<messages.length; i++) {                     //search for message with this ID in the XML help doc
            if (messages[i].getAttribute("id") == messageId) {
                messageText = messages[i].childNodes[0].nodeValue;
                break;
            }
        }
        if (typeof document.body.style.maxHeight != "undefined") {
  		    // IE 7, mozilla, safari, opera 9
  		    // get position from the error message div for the field
		    var messagePosition = findPos(positionDiv); //get position for this message [left, top]
            messageDiv.style.left = messagePosition[0] + 'px';       //move to location and display
            messageDiv.style.top = messagePosition[1] + 'px' ;
		} else {
  		    // IE6, older browsers
  		    // can't rely on positioning so use mouse position
           messageDiv.style.left = mouseXPos + 'px';       //move to location and display
            messageDiv.style.top = mouseYPos + 'px' ;
	    }
        messageDiv.style.display = "block";
        messageDiv.innerHTML = messageText;

    }

    //hide help for input field
    function hideFieldHelp(tgt, messageId) {
        clearTimeout(helpTimer);        // cancel any pending help display if user moved the cursor away quickly
        var messageDiv = document.getElementById("fieldhelp");    //get the div for displaying the message
        messageDiv.innerHTML = "";
        messageDiv.style.display = "none";
        tgt.style.backgroundColor = "";   // un-highlight help box

    }

    //validate form inputs client-side
    function validate() {

        // get input controls to validate
        var txtAge = document.getElementById("txtAge");
        var txtHeight = document.getElementById("txtHeight");
        var txtHeightFeet, txtHeightInches;
        if (!txtHeight) {
            txtHeightFeet = document.getElementById("txtHeightFeet");
            txtHeightInches = document.getElementById("txtHeightInches");

        }
        var txtWeight = document.getElementById("txtWeight");
        var txtHipTScore = document.getElementById("txtHipTScore");
        var lstEthnicity = document.getElementById("lstEthnicity");

        // get message divs
        var divAgeMessage = document.getElementById("ageMessage");
        var divHeightMessage = document.getElementById("heightMessage");
        var divWeightMessage = document.getElementById("weightMessage");
        var divHipTScoreMessage = document.getElementById("hipTScoreMessage");
        var divEthnicityMessage = document.getElementById("ethnicityMessage");

        clearFieldMessages();       //clear any previous field messages

        if (validAge(txtAge, divAgeMessage) &&
            validHeight(txtHeight, txtHeightFeet, txtHeightInches, divHeightMessage) &&
            validWeight(txtWeight, divWeightMessage) &&
            validTScore(txtHipTScore, divHipTScoreMessage) &&
            validEthnicity(lstEthnicity, divEthnicityMessage) ) {

            return true;        //all valid

        } else {

            return false;       //validation error

        }

    }

    //display general page message
    function showPageMessage(messageText) {
        var messageDiv = document.getElementById("pagemessage");
        messageDiv.innerHTML = messageText;
        messageDiv.style.display = "block";
    }

    //clear field messages
    function clearFieldMessages() {
        //find and clear all divs in class fieldmessage
        var divs = document.getElementsByTagName("div");
        var i;
        for (i=0; i<divs.length; i++) {
            if (divs[i].className=="fieldmessage") {
                divs[i].innerHTML = "";
            }
        }
    }

// validate age entry

 function validAge(txtAge, divMsg) {

    var minAge = 45;    // minimum age
    var maxAge = 120;    // maximum age
    var valid;

    if (trim(txtAge.value).length == 0) {
        divMsg.innerHTML = "You must enter a number for age.";
        divMsg.style.color = "#FF0000";
        valid = false;
    } else if (isNaN(txtAge.value)) {
        divMsg.innerHTML = "Age must be a whole number.";
        divMsg.style.color = "#FF0000";
        valid = false;
    } else {

        // it's a number, convert it and check range
        var value = parseInt(txtAge.value);
        if (value < minAge || value > maxAge) {
            divMsg.innerHTML = "Age must be a whole number between " + minAge +
                                " and " + maxAge + ".";
            divMsg.style.color = "#FF0000";
            valid = false;
        } else {
            // entry valid
            divMsg.innerHTML = "";
            valid = true;
        }
    }

    if (!valid) {
        txtAge.focus();
    }
    return valid;


 }

 // validate height entry

 function validHeight(txtHeight, txtHeightFeet, txtHeightInches, divMsg) {


    var minHeight = 54;    // minimum height
    var maxHeight = 76;    // maximum height

    // min, max height in feet and inches so info in error messages will correspond
    // to how it's entered for interfaces that accept separate feet, inches values
    var minHeightFeet = Math.floor(minHeight/12);
    var minHeightInches = minHeight%12;
    var maxHeightFeet = Math.floor(maxHeight/12);
    var maxHeightInches = maxHeight%12;

    var valid = false;
    var feet, inches, value;


    if (txtHeight) {

        //check height in inches as a single field
        if (trim(txtHeight.value).length == 0) {
            divMsg.innerHTML = "You must enter the patient's height.";
            divMsg.style.color = "#FF0000";
            valid = false;
        } else if (isNaN(txtHeight.value)) {
            divMsg.innerHTML = "Height must be a whole number.";
            divMsg.style.color = "#FF0000";
            valid = false;
        } else {

            // it's a number, convert it and check range
            value = parseInt(txtHeight.value);
            if (value < minHeight || value > maxHeight) {
                divMsg.innerHTML = "Height must be between " + minHeight +
                                    " and " + maxHeight + " inches.";
                divMsg.style.color = "#FF0000";
                valid = false;
            } else {
                // entry valid
                divMsg.innerHTML = "";
                valid = true;
            }
        }
        if (!valid) {
            txtHeight.focus();
        }


    } else {

        //check separate feet, inches entries for height
        if (trim(txtHeightFeet.value).length == 0) {
            divMsg.innerHTML = "You must enter your height in feet and inches.";
            divMsg.style.color = "#FF0000";
            txtHeightFeet.focus();
            valid = false;
        } else if (isNaN(txtHeightFeet.value) || (trim(txtHeightInches.value).length > 0 && isNaN(txtHeightInches.value))) {
            divMsg.innerHTML = "Height must be entered as two whole numbers, feet and inches.";
            divMsg.style.color = "#FF0000";
            txtHeightFeet.focus();
            valid = false;
        } else {
            // have feet alone or feet + inches, convert it and check range
            feet = parseInt(txtHeightFeet.value);
            if (trim(txtHeightInches.value).length > 0) {
                inches = parseInt(txtHeightInches.value);
            } else {
                inches = 0;
            }
            value = feet*12 + inches;
            if (inches < 0 || inches > 11) {
                divMsg.innerHTML = "Inches must be a whole number between 0 and 11";
                divMsg.style.color = "#FF0000";
                txtHeightInches.focus();
                valid = false;
            } else if (value < minHeight || value > maxHeight) {
                divMsg.innerHTML = "Height must be a whole number between " + minHeightFeet +
                                    " feet " + minHeightInches + " inches " +
                                    " and " + maxHeightFeet + " feet " + maxHeightInches + " inches.";
                divMsg.style.color = "#FF0000";
                txtHeightFeet.focus();
                valid = false;
            } else {
                // entry valid
                divMsg.innerHTML = "";
                valid = true;
            }
        }

    }


    return valid;

 }

 // validate weight entry

 function validWeight(txtWeight, divMsg) {
    var minWeight = 70;    // minimum weight
    var maxWeight = 300;    // maximum weight
    var valid;

    if (trim(txtWeight.value).length == 0) {
        divMsg.innerHTML = "You must enter a number for weight.";
        divMsg.style.color = "#FF0000";
        valid = false;
    } else if (isNaN(txtWeight.value)) {
        divMsg.innerHTML = "Weight must be a whole number.";
        divMsg.style.color = "#FF0000";
        valid = false;
    } else {

        // it's a number, convert it and check range
        var value = parseInt(txtWeight.value);
        if (value < minWeight || value > maxWeight) {
            divMsg.innerHTML = "Weight must be a whole number between " + minWeight +
                                " and " + maxWeight + ".";
            divMsg.style.color = "#FF0000";
            valid = false;
        } else {
            // entry valid
            divMsg.innerHTML = "";
            valid = true;
        }
    }

    if (!valid) {
        txtWeight.focus();
    }
    return valid;
 }

 // validate T-score

 function validTScore(txtTScore, divMsg) {
    var minTScore = -3.9;    // minimum t-score
    var maxTScore = 10;    // maximum t-score
    var valid;

    if (!txtTScore) {
        //TScore field does not exist on this interface, so skip checking
        valid = true;
    } else if (trim(txtTScore.value).length == 0) {
        if (confirm("You did not enter the patient's T-score." +
                    "If you do not know the patient's T-score, click OK to use " +
                    "the average T-score for the patient's age. If you know the " +
                    "patient's T-score, click Cancel, enter the T-score, and retry " +
                    "the calculation.")) {
            //user confirmed, proceed without T-score (server-side code will assume
            //a Z-score of 0
                divMsg.innerHTML = "";
                valid = true;
        } else {
            //user cancelled, prompt for T-score entry
            divMsg.innerHTML = "Please enter the patient's t-score.";
            divMsg.style.color = "#FF0000";
             valid = false;
        }
    } else if (isNaN(txtTScore.value)) {
        divMsg.innerHTML = "T-score must be a number.";
        divMsg.style.color = "#FF0000";
        valid = false;
    } else {

        // it's a number, convert it and check range
        var value = parseFloat(txtTScore.value);
        if (value < minTScore || value > maxTScore) {
            divMsg.innerHTML = "T-score must be a number between " + minTScore +
                                " and " + maxTScore + ".";
            if (value < minTScore) {
                divMsg.innerHTML += " This model's predictions are inaccurate when BMD T-score is -4 or lower."
            }
            divMsg.style.color = "#FF0000";
            valid = false;
        } else if (value > 0) {
            if (confirm("You entered a t-score greater than zero. " +
                        "If this is correct, click OK.  If this is not correct, click Cancel.")) {
                // user confirmed, so proceed
                divMsg.innerHTML = "";
                valid = true;
            } else {
                // user cancelled
                divMsg.innerHTML = "Re-enter the t-score, using the minus sign for a negative number.";
                divMsg.style.color = "#FF0000";
                valid = false;
            }
        } else {
            // entry valid
            divMsg.innerHTML = "";
            valid = true;
        }
    }

    if (!valid) {
        txtTScore.focus();
    }
    return valid;
 }

// validate ethnicity

function validEthnicity(lstEthnicity, divMsg) {

    if (lstEthnicity.options[lstEthnicity.selectedIndex].text == "--Please select--") {
        divMsg.innerHTML = "You must select a race or ethnicity."
        divMsg.style.color = "#FF0000";
        valid = false;
    } else {
        valid = true;
    }

    if (!valid) {
        lstEthnicity.focus();
    }

    return valid;


}


// trim spaces from string

function trim(inputString) {
   // Removes leading and trailing spaces from the passed string. Also removes
   // consecutive spaces and replaces it with one space. If something besides
   // a string is passed in (null, custom object, etc.) then return the input.
   if (typeof inputString != "string") { return inputString; }
   var retValue = inputString;
   var ch = retValue.substring(0, 1);
   while (ch == " ") { // Check for spaces at the beginning of the string
      retValue = retValue.substring(1, retValue.length);
      ch = retValue.substring(0, 1);
   }
   ch = retValue.substring(retValue.length-1, retValue.length);
   while (ch == " ") { // Check for spaces at the end of the string
      retValue = retValue.substring(0, retValue.length-1);
      ch = retValue.substring(retValue.length-1, retValue.length);
   }
   while (retValue.indexOf("  ") != -1) { // Note that there are two spaces in the string - look for multiple spaces within the string
      retValue = retValue.substring(0, retValue.indexOf("  ")) + retValue.substring(retValue.indexOf("  ")+1, retValue.length); // Again, there are two spaces in each of the strings
   }
   return retValue; // Return the trimmed string back to the user
} // Ends the "trim" function

// reset input form

function resetForm() {

    //NOTE: Input fields will be cleared on server side;
    // this client-side function just takes care of the
    // UI aspects that are managed on the client side only

    clearFieldMessages();       //clear help/error messages for input fields

    return true;

}

// determine mouse X-position in document from mouse event (from http://javascript.about.com/library/blmousepos.htm)

function mouseX(evt) {
if (evt.pageX) return evt.pageX;
else if (evt.clientX)
   return evt.clientX + (document.documentElement.scrollLeft ?
   document.documentElement.scrollLeft :
   document.body.scrollLeft);
else return null;
}

// determine mouse Y-position in document from mouse event (from http://javascript.about.com/library/blmousepos.htm)
function mouseY(evt) {
if (evt.pageY) return evt.pageY;
else if (evt.clientY)
   return evt.clientY + (document.documentElement.scrollTop ?
   document.documentElement.scrollTop :
   document.body.scrollTop);
else return null;
}

//determine absolute position of an object relative to the entire document
// returns an array with [0] = left and [1] = top
// from http://www.quirksmode.org/js/findpos.html

function findPos(obj) {
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		curleft = obj.offsetLeft
		curtop = obj.offsetTop
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
	}
	return [curleft,curtop];
}

//scroll to bottom of page

function scrollToBottom() {
    window.location="#after_results";
}

// highlight input field when it has the focus
function highlightField(field, hasFocus) {
    if (hasFocus) {
        field.style.backgroundColor = "lightblue";
    } else {
        field.style.backgroundColor = "white";
    }
}




