/*
CHANGES:
Fix bug in ValidateMaxSelectionNbr_checkbox, where arrSess_group_id[k] won't grow, if only "min number of selections within block"
is selected for certain session block; in other words, if "max number of selections within block" is not set, arrSess_group_id array
won't grow, even if checkbox is checked; the bug is, if only "min number ..." is set ("max number ..." is not set at the same time),
"min number ..." won't work. Now it is fixed.
Bo Ling, 9/7/2007

This file has been removed from modules/includes/functions.asp page, since <script> has added an 
empty line to html body, which screw up the page appearance. This file will be included into
any client-specific including which use any of functions inside.
2/13/2006

//this page is included into modules/includes/functions.asp page, so that all the mxweb pages is
//able to call js function in this page.
//12/30/2005
*/
//<!--To call ^^ActionURL^^ like "AnyPageName.asp" -->
function SetActionAndSubmit(ActionURL)
{

document.FRMSEND.action=ActionURL;
document.FRMSEND.submit();

}

/*
code borrow from http://www.vermontsoftware.com/Javascript/trim.html
*/
/*
==================================================================
LTrim(string) : Returns a copy of a string without leading spaces.
==================================================================
*/
function LTrim(str)
/*
   PURPOSE: Remove leading blanks from our string.
   IN: str - the string we want to LTrim
*/
{
   var whitespace = new String(" \t\n\r");

   var s = new String(str);

   if (whitespace.indexOf(s.charAt(0)) != -1) {
      // We have a string with leading blank(s)...

      var j=0, i = s.length;

      // Iterate from the far left of string until we
      // don't have any more whitespace...
      while (j < i && whitespace.indexOf(s.charAt(j)) != -1)
         j++;

      // Get the substring from the first non-whitespace
      // character to the end of the string...
      s = s.substring(j, i);
   }
   return s;
}

/*
==================================================================
RTrim(string) : Returns a copy of a string without trailing spaces.
==================================================================
*/
function RTrim(str)
/*
   PURPOSE: Remove trailing blanks from our string.
   IN: str - the string we want to RTrim

*/
{
   // We don't want to trip JUST spaces, but also tabs,
   // line feeds, etc.  Add anything else you want to
   // "trim" here in Whitespace
   var whitespace = new String(" \t\n\r");

   var s = new String(str);

   if (whitespace.indexOf(s.charAt(s.length-1)) != -1) {
      // We have a string with trailing blank(s)...

      var i = s.length - 1;       // Get length of string

      // Iterate from the far right of string until we
      // don't have any more whitespace...
      while (i >= 0 && whitespace.indexOf(s.charAt(i)) != -1)
         i--;

      // Get the substring from the front of the string to
      // where the last non-whitespace character is...
      s = s.substring(0, i+1);
   }

   return s;
}

/*
=============================================================
Trim(string) : Returns a copy of a string without leading or trailing spaces
=============================================================
*/
function Trim(str)
/*
   PURPOSE: Remove trailing and leading blanks from our string.
   IN: str - the string we want to Trim

   RETVAL: A Trimmed string!
*/
{
   return RTrim(LTrim(str));
}

//return a random number between lowerLimit and upperLimit
function getRandomNumber(upperLimit, lowerLimit)
{	
	if (upperLimit==null || typeof(upperLimit) == "undefined")
	{
		upperLimit = 1;
	}
	
	if (lowerLimit==null || typeof(lowerLimit) == "undefined")
	{
		lowerLimit = 0;
	}
	
	return lowerLimit  + (upperLimit-lowerLimit) * Math.random();
}

// this function is created for membership enrol/renewal and meeting registration page, so that
// if payment type other than "online credit card payment" is selected, the "continue>>>" button will
// be changed to "Finalize Order".
//
// isOnlinePayment = true/false
function payment_type_click (isOnlinePayment)
{
	//the following elements are from mem_dues_calc.asp page.
	var el = document.getElementById("continue_mem_dues_calc");
	var el2 = document.getElementById("instruction_mem_dues_calc");
			
	//if the calling page is not mem_dues_calc.asp page, then check mtg_regsummary.asp and multi_reg_checkout.asp page.
	if (el==null)
	{
		//the following elements are from mtg_regsummary.asp and multi_reg_checkout.asp page.
		var el = document.getElementById("continue_mtg_checkout");
		var el2 = document.getElementById("instruction_mtg_checkout");
	}
			
	if ( el != null)
	{
		if (isOnlinePayment== true)	
		{							
			el.value = " Continue>>>";	
			el2.innerHTML = "";
		}
		else
		{
			el.value = " Finalize Order>>>";
			el2.innerHTML = "(Last step)";
		}
	}
				
}
//this function is called in regform.asp and regguest.asp pages, to alert the member, if user wants to
//check more-than-allowed-max-number within any given session group.
// this function is filed for onclick event, for both checkbox and radio button
//
// this is the function called by checkbox
// we create different functions for checkbox and radio, based on the following thoughts:
// 1. checkboxes have on and off, while radio button only have on,
// 2. checkboxes are stand alone, while radio buttons are in group, where max only one of the radio is checked,
// therefore, if one of the radio button in the group is checked, we would not increment by one, if
// any one inside of the group is clicked again, actually, no action should be filed, either increment or decrement; 
// while for checkbox, as long as checked=true, we increment, and checked=false, we decrement;
// 3. for radio button, we check the selected_nbr of direct parent of radio button group; if zero, we know
// group is pre-checked, else, it is not, and we therefore incremtent one if clicked.
// 4. for checkbox, if "max_selection_nbr_" + sess_group_id does not exist, then we skip all the action
// for that sess_group_id totally, while for radio button, we could not, we would have to calculate
// selected_nbr_sess_group_id conditionlessly, since this number would be used to calculate: radioGroupPrechecked
//
function ValidateMaxSelectionNbr_checkbox (thisForm, thisCheckOrRadioBox, thisChecked, sess_group_id){
    
    // call function, to esclate current selection nbr, to the *indirect* parent groups.
    CorrectCurrentSelectionNbr (thisForm);
    
	var i = 0; k=0;
	var counter;
	var max_nbr;
	
	// this var is used to compose err msg.
    var sess_name_prefix = "sess_name_";
	
	// since we are not changing selected_counter_ of session group along the line (during the loop, for fear
	// that no way to rollback), we are creating these 2 arrays, to store sess_group_id and counter; these
	// 2 arrays are strictly related - one-to-one-parallelly. 
	var arrSess_group_id = new Array ();
	var arrSelected_counter = new Array ();
	
	//
	// we are using do ... while loop, so that we absolutely do the 1st round of check, which
	// is checking for the *direct* parent of the checkbox or radio button.
	//
	do
    { 
       //for the first round, we are skipping this logic, so that we check the direct parent of 
       // checkbox or radio button; for the afterwards round, we will assign the *next* level parent's
       // id to sess_group_id, so that we achieve the *bubble-up* route until the very top level 
       // session group.
        if (i>0)
            sess_group_id = thisForm['sess_id_parent_of_' + sess_group_id].value;
        
        // this is the element of max_nbr of a session group, if exists!; if it does not exist
        // , then we know this current group does not have a max_nbr, then we will skip the following.
        var element = thisForm["max_selection_nbr_" + sess_group_id];
        if (typeof(element) != "undefined")
        {
            max_nbr = parseInt(element.value, 10); 
        }//end of if (typeof(element) != "undefined")     
        else
        {
            // give an unreal number, singnal not-applicable.
            max_nbr = -1;
        }   
        
        // we have to check to see if selected_counter_session_group_id hidden field exist, since for
        // simple regform, where there is no Session Block at all, this hidden field was not created;
        // if so, we skip the whole function.
        if (!thisForm["selected_counter_" + sess_group_id])
        {
            break;
        }
        counter = parseInt(thisForm["selected_counter_" + sess_group_id].value, 10);
        // default current sess_group_id counter to zero
        if (isNaN(counter))
            counter = 0;
                         
        if (thisChecked == true)
	    {
	    	counter++;
	    	if (max_nbr > 0 && counter>max_nbr)
	    	{
	    	     var sess_label;
	            if (max_nbr ==1)
		            sess_label = " item "
		        else
		            sess_label = " items "
		                
	    		alert ("Sorry, you cannot select more than " + max_nbr + sess_label + "from: " + StripHtmlTag(thisForm [sess_name_prefix + sess_group_id].value) + ".");
	    		thisCheckOrRadioBox.checked = false;
	    		thisCheckOrRadioBox.focus();
	    		return false;
	    	}
	    }
	    else
	    {
	    	if (counter>0)
	    		counter--;
	    }

	    // we cannot change selected_counter_ for radio button along the line, since we have no
	    // way to ROLLBACK the selected_counter_, if there is error happens later in the loop;
	    // we will add sess_group_id into an array instead, and if everything goes well,
	    // we will change every sess_group_id's selected_counter at once.
	    //thisForm["selected_counter_" + sess_group_id].value = counter;
	    arrSess_group_id[k] = "selected_counter_" + sess_group_id;
	    arrSelected_counter[k] = counter;
	        	        
	    // k is to keep counter for arrSess_group_id and arrSelected_counter;
	    k++;	    
	    
	    // i is to keep track of how many loops we have gone thru.
	    i++;
    } //end of do
    while (typeof(thisForm['sess_id_parent_of_' + sess_group_id]) != "undefined")
	
	// at this point, no error happens; then we assign counters to the corresponding box
	AssignSelected_counter (thisForm, arrSess_group_id, arrSelected_counter);
	return true;	
}

//this function is called in regform.asp and regguest.asp pages, to alert the member, if user wants to
//check more-than-allowed-max-number within any given session group.
// this function is filed for onclick event, for both checkbox and radio button
//
// this is the function called by radio
function ValidateMaxSelectionNbr_radio (thisForm, thisCheckOrRadioBox, thisChecked, sess_group_id){

    // call function, to esclate current selection nbr, to the *indirect* parent groups.
    CorrectCurrentSelectionNbr (thisForm);
    
	var i = 0;
	var counter;
	var max_nbr;
	
	// this var is used to compose err msg.
    var sess_name_prefix = "sess_name_";
	
	//
	// we are using do ... while loop, so that we absolutely do the 1st round of check, which
	// is checking for the *direct* parent of the checkbox or radio button.
	//
	
	// whether radio button group was checked, before this function files;
	// this var is used inside of the loop, if current box is radio button.
	var radioGroupPrechecked;
	
	// since we are not changing selected_counter_ of session group along the line (during the loop, for fear
	// that no way to rollback), we are creating these 2 arrays, to store sess_group_id and counter; these
	// 2 arrays are strictly related - one-to-one-parallelly. 
	var arrSess_group_id = new Array ();
	var arrSelected_counter = new Array ();
		
	do
    { 
       
       //for the first round, we are skipping this logic, so that we check the direct parent of 
       // checkbox or radio button; for the afterwards round, we will assign the *next* level parent's
       // id to sess_group_id, so that we achieve the *bubble-up* route until the very top level 
       // session group.
        if (i>0)
            sess_group_id = thisForm['sess_id_parent_of_' + sess_group_id].value;
        
        // we have to check to see if selected_counter_session_group_id hidden field exist, since for
        // simple regform, where there is no Session Block at all, this hidden field was not created;
        // if so, we skip the whole function.
        if (!thisForm["selected_counter_" + sess_group_id])
        {
            break;
        }
        counter = parseInt(thisForm["selected_counter_" + sess_group_id].value, 10);
        if (isNaN(counter))
            counter = 0;
         
        if (i==0)
        {
            // we will only initialize radioGroupPrechecked in the first loop; all the rest of the loop would 
	        // just inherit the value from the 1st loop.
	        //
	        // if the direct parent of radio button has counter == 0, we know neither of the button of the
	        // same radio group was checked previously.
	        if (counter == 0)
	            radioGroupPrechecked = false;
	        else
	            radioGroupPrechecked = true;
        }  
        
        // since for radio button, thisChecked is always true, we skips "if (thisChecked == true)";
		// and if radio group has been pre-checked before this function files, we do nothing!
		// and we *never* do decrement for radio button!
        // if radioGroupPrechecked == true, we jump out of the loop, do absolutely nothing - and if we are
        // doing checking-on action;
        // for clear off radio button action (while clearRadioButtons being called, thisChecked == false), we would continue.        
        if (radioGroupPrechecked && thisChecked == true)
		    break;  
		          
        // this is the element of max_nbr of a session group, if exists!; if it does not exist
        // , then we know this current group does not have a max_nbr, then we will skip the following.
        var element = thisForm["max_selection_nbr_" + sess_group_id];
        if (typeof(element) != "undefined")          
            max_nbr = parseInt(element.value, 10);        
        else
        {
            // give an unreal number, singnal not-applicable.
            max_nbr = -1;
        }
        
        // we have to take consider "Clear Radio Buttons" button on regform.asp and regguest.asp pages, where
        // there would be "check offs".
		if (thisChecked == true)
	    {
		    counter++;
	        if (max_nbr > 0 && counter>max_nbr)
	        {
	            var sess_label;
	            if (max_nbr ==1)
		            sess_label = " item "
		        else
		            sess_label = " items "
	        	alert ("Sorry, you cannot select more than " + max_nbr + sess_label + "from: " + StripHtmlTag(thisForm [sess_name_prefix + sess_group_id].value) + ".");
	        	thisCheckOrRadioBox.checked = false;
	        	thisCheckOrRadioBox.focus();
	        	return false;
	        }
	    }
	    else
	    {
	    	if (counter>0)
	    		counter--;
	    }
	    	
	    // we cannot change selected_counter_ for radio button along the line, since we have no
	    // way to ROLLBACK the selected_counter_, if there is error happens later in the loop;
	    // we will add sess_group_id into an array instead, and if everything goes well,
	    // we will change every sess_group_id's selected_counter at once.
	    //thisForm["selected_counter_" + sess_group_id].value = counter;
	    arrSess_group_id[i] = "selected_counter_" + sess_group_id;
	    arrSelected_counter[i] = counter;
	    
	    // i is to keep track of how many loops we have gone thru.
	    i++;
    } //end of do
    while (typeof(thisForm['sess_id_parent_of_' + sess_group_id]) != "undefined")
	
	// at this point, no error happens; then we assign counters to the corresponding box
	AssignSelected_counter (thisForm, arrSess_group_id, arrSelected_counter);
	
	return true;	
}

// this function is call at end of ValidateMaxSelectionNbr to assign counters, to hidden fields of selected_counter_sess_group_id,
// on the condition that no error in the whole process.
//
// arr1 hold sess_group_ids, arr2 hold counters
function AssignSelected_counter (thisForm, arr1, arr2)
{
    for (i=0; i<arr1.length; i++)
    {
        thisForm[arr1[i]].value = arr2[i];
    }
}

//this function is called in regform.asp and regguest.asp pages, to alert the member, if user wants to
//continue with page without meet the minimum-selection-nbr any given session group.
// this function is filed while form is submitted.
function ValidateMinSelectionNbr (thisForm){    
    // call function, to esclate current selection nbr, to the *indirect* parent groups.
    CorrectCurrentSelectionNbr (thisForm);
    
    var min_nbr_prefix = "min_selection_nbr_";
    var length1 = min_nbr_prefix.length;
    
    var selected_counter_prefix = "selected_counter_";
    var sess_group_id;
    
    // this var is used to compose err msg.
    var sess_name_prefix = "sess_name_";
    
    var min_nbr, counter;
    
    var err_msg_delimiter = "\n";
    var msg = "", err_nbr = 0;
    for (var i=0;i<thisForm.elements.length;i++)
	{
	    e = thisForm.elements[i];
		if (e.name.substring(0, length1)==min_nbr_prefix)
		{
		    
		    sess_group_id = e.name.substring(length1);		    
		    min_nbr = parseInt(e.value, 10);
		    counter = parseInt(thisForm [selected_counter_prefix + sess_group_id].value, 10);
		     
		    //if "min_selection_nbr_" box does not give a good number, skip the check.
		    if (isNaN(min_nbr))
		        break;
		    
		    // if the counter for a session group is nan, then it must be zero at this point - no session selected yet.
		    if (isNaN(counter))
                counter = 0;
            
            // if the counter is less than the minimum-nbr, then we cry out.     
		    if (counter < min_nbr)
		    {
		        var sess_label;
		        if (min_nbr ==1)
		            sess_label = " session "
		        else
		            sess_label = " sessions "
		            
		        msg += err_msg_delimiter + " at least " + min_nbr + sess_label + "for group: " + StripHtmlTag(thisForm [sess_name_prefix + sess_group_id].value);
		        err_nbr ++;
		    }
		}   
    }	
    
    // if we only have one error msg, then we cut off the first one character of \n; otherwise, leave it as is.
    if (err_nbr ==1)
        msg = msg.substring(1);
    
    if (err_nbr >0)
    {
        alert ("Please select" + msg);
        return false;
    }
    else
        return true;	
}

// function to strip off html tags, from the passed string.
function StripHtmlTag (s1)
{
    // use RegEx to strip off all Html Tags.
    var regex=/(<(.|\n)+?>)/ig;
    return s1.replace(regex, "");
}

// we have a field: selected_counter_sess_ID store current selection nbr for *each* session group; 
// However, this counter is not totally accurate, since it only cover a session group's *direct* children, - does
// not count in the children of sub-session groups; we will process the chidren of sub or sub-sub, or ... session
// groups in a js function.
//
// this function is called, to populate the super-session groups' selected_counter, based on their children session
// groups.
//
// this function is called at the beginning of ValidateMinSelectionNbr and ValidateMaxSelectionNbr for *only once*.
function CorrectCurrentSelectionNbr (thisForm)
{   
    // make sure we only call CorrectCurrentSelectionNbr once, on one page.
    if (GetCorrectCurrentSelectionNbrCalled () == false)
    {
        var prefixLevelOfBox = "level_of_";
        var lenLevelOfBox = prefixLevelOfBox.length;
        var arrSess_id = new Array();
        var arrLevel = new Array();
        var j = 0;
        
        for (var i=0;i<thisForm.elements.length;i++)
	    {
	        e = thisForm.elements[i];
	    	if (e.name.substring(0, lenLevelOfBox)==prefixLevelOfBox)				    
	    	{
	    	    arrSess_id [j] = e.name.substring(lenLevelOfBox);
	    	    arrLevel [j] = e.value;
	    	    
	    	    j++;
	    	}
        }
        
        // after decrement, j becomes the array's UBound
        j--;
        
        // only if there are really elements in the array, we will proceed.
        if (j >= 0)
        {
            // 1. call function to sort both array, simutaneously.
            // we put arrLevel as arr1, since we would order arrLevel and arrSess_id based on level.
            heapSortNumber(arrLevel, arrSess_id);
                        
            var sess_id, counter, target_sess_group_id, target_sess_group_counter;      
            // 2. then, we look thru the arrays, to add children's selected_nbr_ to their direct parent's;
            // since we are looping from the lowest level session group (level has the max number), 
            // at end of the loop, we would be adding all the sub, sub-sub, sub-sub-sub, etc. session groups'
            // number to that all direct, or indirect parents.
            for (var i=j;i>=0;i--)
	        {   
	            sess_id = arrSess_id [i];
	            
	            // 2.1
	            // only if the sess_id has selected counter, we will add the number to that of its direct parent.
	            e = thisForm["selected_counter_" + sess_id];
	            
	            if (typeof(e) != "undefined")
	            {
	                counter = parseInt(e.value, 10);
	                
	                if (isNaN(counter))
                        continue;
	            }   
	            else
	                continue;
                
	            // 2.2
	            // find the current session group's direct parent.    
                e = thisForm["sess_id_parent_of_" + sess_id];
                target_sess_group_id = e.value;
                
                // 2.3
                // find the selected counter of the direct parent;
                e = thisForm["selected_counter_" + target_sess_group_id];
                if (typeof(e) != "undefined")
	            {
	                target_sess_group_counter = parseInt(e.value, 10);
	                
	                // default to 0
	                if (isNaN(target_sess_group_counter))
                        target_sess_group_counter = 0;
	            }   
	            else
	                continue;
	            
	            // 2.4 finally, add the children's number to its parent's    
	            target_sess_group_counter += counter;	            
	            e.value = target_sess_group_counter;                
            }  
        }
        
        // after calling this function, set a variable so that this function won't be called once again - no need.
        SetCorrectCurrentSelectionNbrCalled (true);
    }
}

var CorrectCurrentSelectionNbrCalled = false;
function SetCorrectCurrentSelectionNbrCalled (val)
{
    CorrectCurrentSelectionNbrCalled = val;
}

function GetCorrectCurrentSelectionNbrCalled ()
{
    return CorrectCurrentSelectionNbrCalled;
}

// called by onblur event, of text box
// if the text box is not empty, then "check" the target checkbox;
// do not do anything if textbox is empty, to allow "not-giving value for the textbox".
// sess_group_id is the session group's id (direct parent), which would be used
// to envoke function call: ValidateMaxSelectionNbr_checkbox
function mtgform_textbox_onblur (thisForm, thisBox, targetChkboxName, sess_group_id) {	       
    if (thisBox.value != "") 	        
    {
        var e = thisForm[targetChkboxName];
	            
        // only applies to checkbox, not for radio button, since the group of radio buttons all have the same name;
        // because we have follow the way to name a checkbox, we need to see if such element exists; after all,
        // if the corresponding box is a radiobutton, e would not exist.
        if (e)
        {
            // only if the corresponding checkbox is unchecked, we do the following:
            if (!e.checked)
            {
                e.checked = true;
            
                // would have to call ValidateMaxSelectionNbr, since it is not envoked automatically.
                ValidateMaxSelectionNbr_checkbox (thisForm, e, e.checked, sess_group_id);
            }
        }
    }
}
	
// called by onclick event, of checkbox 
// if thisChecked = true, then cursor to the target textbox.
// for now, we only have 2 possible textbox in mind: qty and open amount; 
// we will check in the order of qty, and  then open amount; add targetTextbox3Name for future.
function mtgform_checkbox_onclick (thisForm,
    thisChecked, 
    targetTextbox1Name, 
    targetTextbox2Name, 
    targetTextbox3Name) {
    if (thisChecked == true)
    {
        var e = thisForm[targetTextbox1Name];
	            
        if (e)
        {
            e.focus();
            e.select();
        }
        else
        {
            e = thisForm[targetTextbox2Name];
            if (e)
            {
                e.focus();	
                e.select();
            }
        }
    }
}


// quick sort an array of numbers.
// arr1 and arr2 could be sorted simutaneously.
//
// this function does not return anything, however, arr1 and arr2 would return by ref.
function heapSortNumber(arr1, arr2)
{
  var array_size = arr1.length;
  heapSort(arr1, arr2, array_size);
}

//heap sort
function heapSort(arr1, arr2, array_size)
{
  var i, temp, temp2;

  for (i = (array_size / 2)-1; i >= 0; i--)
    siftDown(arr1, arr2, i, array_size);

  for (i = array_size-1; i >= 1; i--)
  {
    temp = arr1[0];
    arr1[0] = arr1[i];
    arr1[i] = temp;
    
    temp2 = arr2[0];
    arr2[0] = arr2[i];
    arr2[i] = temp2;
    
    siftDown(arr1, arr2, 0, i-1);
  }
}


function siftDown(arr1, arr2, root, bottom)
{
  var done, maxChild, temp;

  done = 0;
  while ((root*2 <= bottom) && (!done))
  {
    if (root*2 == bottom)
      maxChild = root * 2;
    else if (arr1[root * 2] > arr1[root * 2 + 1])
      maxChild = root * 2;
    else
      maxChild = root * 2 + 1;

    if (arr1[root] < arr1[maxChild])
    {
      temp = arr1[root];
      arr1[root] = arr1[maxChild];
      arr1[maxChild] = temp;
      
      temp2 = arr2[root];
      arr2[root] = arr2[maxChild];
      arr2[maxChild] = temp2;
      
      root = maxChild;
    }
    else
      done = 1;
  }
}
