/**
    JSP's using this Javascript will need to include some dwr dependencies:
    
      <script src='/esuite/dwr/interface/AjaxFacade.js'></script>
      <script src='/esuite/dwr/engine.js'></script>validate312
  
  */
/**
        Address validation and update on Country change
        
        To use on your jsp:
          
          <script src='/esuite/dwr/interface/AjaxFacade.js'></script>
        <script src='/esuite/dwr/engine.js'></script>
        <ic:javascript path="icentris/ajax4i.js"/>
        ...
        ...
          var myAddressVerifier = new AjaxAddressValidator( "myAddress" );
          <input type="..." name="address1" id="myAddressAddress1" ... onChange="myAddressVerifier.validate(this);">
        <input type="..." name="city" id="myAddressCity" ... onChange="myAddressVerifier.validate(this);">
          
      -  OR  -
          
          var myAddressVerifier = new AjaxAddressValidator( "my_address_" );
          <input type="..." name="address1" id="my_address_Address1" ... onChange="myAddressVerifier.validate(this);">
        <input type="..." name="city" id="my_address_City" ... onChange="myAddressVerifier.validate(this);">
          
      - OR ( if you have lower case id's already, for example... )
          
          var myAddressVerifier = new AjaxAddressValidator( "address.", { "address1" : "address1",
                                        "address2": "address2",
                                        "city" : "city",
                                        "state" : "stateProvinceGeoId",
                                        "validateAddresspostalCode" : "postalCodeGeoId",
                                        "country" : "countryGeoIdCodeValue" } );
          <input type="..." name="address1" id="address.address1" ... onChange="myAddressVerifier.validate(this);">
        <input type="..." name="countryGeoIdCodeValue" id="adress.countryGeoIdCodeValue" ... onChange="myAddressVerifier.validate(this);">
          
  **/

/** 
 * We never want a field to be sized larger than this number.  If you need to customize this, talk
 * to Spencer or Darren about the best solution.
 */
var MAX_FIELD_SIZE = 40;

function AjaxAddressValidator(fieldsPrefix, fieldMap, requiredFieldsList) {
  this._fieldsPrefix = fieldsPrefix;
  if (fieldMap) {
    this._fieldMap = fieldMap;
  } else {
      // Default field names 
    this._fieldMap = {"address1":"Address1",
                      "address2":"Address2",
                      "city":"City", 
                      "stateProvinceGeoId":"State", 
                      "postalCode":"PostalCode", 
                      "country":"Country",
                      "county":"County",
                      "geoCode":"GeoCode"};
  }
  if (requiredFieldsList) {
    this._requiredFieldsList = requiredFieldsList;
  } else {
    this._requiredFieldsList = ["address1", "city", "stateProvinceGeoId", "postalCode", "country"];
  }

	/**
	 * The control passed in will have its countryCallback method called whenever the value of Country is updated
	 */
	this.addCountryListener = function(control) {
		if(!this.countryListeners ){
			this.countryListeners = new Array();
		}
		if(control) {
			this.countryListeners[this.countryListeners.length] = control;
		}
	};

  this.validate = function (theFieldTriggeringValidation) {
    _fieldTriggeringValidation = theFieldTriggeringValidation;  // Try to use OOP instead of global
    var errMsg = "";
    var address1 = this._getAddressField("address1");
    var city = this._getAddressField("city");
    var county = this._getAddressField("county");
    var stateCode = this._getAddressField("stateProvinceGeoId");
    var postalCode = this._getAddressField("postalCode");
    var country = this._getAddressField("country");
    
    if ( country && country.value == "-1" ) {
      // Don't want to validate when country isn't set
      return;
    } 
    // If any of the required fields are blank, then don't make the AJAX call
    if (this._requiredFieldsArePopulated()) {
      //does ajax call for further validation
      var address = new Object();
      address.address1 = address1.value;
      address.city = city.value;
      if ( county ) {
        address.county = county.value;
      }
      address.stateProvinceGeoId = stateCode.value;
      address.postalCode = postalCode.value;
      if ( country ) {
        address.countryGeoId = country.value;
      }
      AjaxFacade.validateAddress(address, this._addressValidationCallback.bind(this));
    }
    
    setCoolDownTimer(3000);
  }

  this._requiredFieldsArePopulated = function () {
    for ( var i = 0; i < this._requiredFieldsList.length; i++ ) {
      var field = this._getAddressField(this._requiredFieldsList[i]);
      if ( !field || !field.value || field.value.length < 1 || field.value == "-1" ) {
        return false;
      }
    }
    return true;
  }

  this._getAddressField = function(fieldSuffix) {
    return $(this._fieldsPrefix + this._fieldMap[fieldSuffix]);
  }

  /**
    * results will be an instance of AddressVerificationResults
    */
  this._addressValidationCallback = function (results) {

        if(document.getElementById(this._fieldsPrefix + "lookup")){
          var zipLookup = document.getElementById(this._fieldsPrefix + "lookup"); 
          var queue = Effect.Queues.get('global');
          
          if(queue.size() <= 1){ 
            new Effect.Appear(zipLookup,{queue: 'end',  limit: 1});
            new Effect.Highlight(zipLookup,{queue: 'end',limit: 1});
          }
        }
        
        
        
    if (results.success == false) {
      for (var i = 0; i < results.errorMessages.length; i++) {
        alert(results.errorMessages[i]);
      }
      if ( results.suggestions ) {
        for (var i = 0; i < results.suggestions.length; i++ ) {
          var suggestion = results.suggestions[i];
          var field = $(this._fieldsPrefix + this._fieldMap[suggestion.field]);
          var hasPreviousValue = false;
          var previousValue = field.value;
          if (previousValue == "-1" && document.getElementById("current_"+this._fieldsPrefix + this._fieldMap[suggestion.field]) != undefined)
          {
            previousValue = document.getElementById("current_"+result._fieldsPrefix + this._fieldMap[suggestion.field]).value;  
          }
          if ( field.options ) { // if it is a drop-down
            //If we are a dropdown, make sure the field is showing
            field.show();
            
            //Destroy spans if they exist to ensure we show proper dropdowns.
              var destroySpans = field.parentNode.getElementsByTagName("SPAN");
              for (var x = 0; x < destroySpans.length; x++){
                if($(destroySpans[x]).hasClassName('addressTemp')) field.parentNode.removeChild($(destroySpans[x]));
              }
            
            if(suggestion.suggestions.length != 1){
              field.options.length = suggestion.suggestions.length + 1;
              field.options[0].value = -1;
              field.options[0].text = "Choose one...";
  
              for ( var j = 0; j < suggestion.suggestions.length; j++ ) {
                field.options[j+1].value = suggestion.suggestions[j];
                field.options[j+1].text = suggestion.suggestions[j];
                if ( !hasPreviousValue ) {
                  if ( previousValue == field.options[j+1].value ) {
                    hasPreviousValue = true;
                    field.value = previousValue;
                  }
                }
              }
              if ( !hasPreviousValue ) {
                field.value = "-1"
              }
            }else{

                field.options[0].value = suggestion.suggestions[0];
                field.options[0].text = suggestion.suggestions[0];
                if ( !hasPreviousValue ) {
                  if ( previousValue == field.options[0].value ) {
                    hasPreviousValue = true;
                    field.value = previousValue;
                  }
                }
              
              if ( !hasPreviousValue ) {
                field.value = "-1"
              }

              //Destroy spans if they exist to ensure we show proper dropdowns.
              var destroySpans = field.parentNode.getElementsByTagName("SPAN");

              for (var x = 0; x < destroySpans.length; x++){
                if($(destroySpans[x]).hasClassName('addressTemp')) field.parentNode.removeChild($(destroySpans[x]));
              }
              //We don't want the field to display if we're replacing it with text
              field.hide();
              
              //Create our text field and insert the value to show inside of it
              var singleField = document.createElement("span");
              singleField.className = "addressTemp";
              singleField.innerHTML = suggestion.suggestions[0];
              
              //Add our text field to the DOM
              field.parentNode.appendChild(singleField);
              field.value = suggestion.suggestions[0];
             }
          } else { // it is an input field
            if ( suggestion && suggestion.suggestions.length > 0 ) {
              field.value = suggestion.suggestions[0];
            }
          }
        }
      } else {
          // OBW-1668 we don't want to erase the users input
        //_fieldTriggeringValidation.value = "";
        _fieldTriggeringValidation.focus();
      }
    }
  };

  this.executeCountryListeners = function (country) {
    if(this.countryListeners) {
  		for(i=0;i<this.countryListeners.length;i++) {
  			this.countryListeners[i].countryCallback(country);
  		}
    }
  }

  this.changeCountry = function () {
    if ( this._getAddressField("country") ) {
      var value = this._getAddressField("country").value;
      this._currentStateDropdownToUpdate = $(this._fieldsPrefix + this._fieldMap["stateProvinceGeoId"]);
      AjaxFacade.getStatesByCountry(value, this._stateDropdownCallback.bind(this));
      AjaxFacade.getAddressConfiguration( value, this._reconfigureAddress.bind(this) );
    }
	this.executeCountryListeners(value);
  };
  
  this.abstractStateUpdateView = function () {
    //TODO: hack around using custom events. Need to change this when we add a library that supports custom events. 
  };
  
  this._stateDropdownCallback = function (results) {

    var previousValue = this._currentStateDropdownToUpdate.value;
	this._currentStateDropdownToUpdate.selectedIndex = 0;
    this._currentStateDropdownToUpdate.length = results.length + 1;
    this._currentStateDropdownToUpdate.options[0].value = -1;
    this._currentStateDropdownToUpdate.options[0].text = "Choose one...";
    
    for (i = 0; i < results.length; i++) {
      this._currentStateDropdownToUpdate.options[i+1].value = results[i].value;
      this._currentStateDropdownToUpdate.options[i+1].text = results[i].name;
      
      if ( previousValue == this._currentStateDropdownToUpdate.options[i+1].value ) {
        this._currentStateDropdownToUpdate.value = previousValue;
      }
    }
    this.abstractStateUpdateView();
  };
  
  this._reconfigureAddress = function( results ) {
    // TODO : update address configuration
    for ( var i = 0; i < results.fieldConfig.length; i++ ) {
      var f = results.fieldConfig[i]['fieldName'];
      var m = results.fieldConfig[i]['maxLength'];
      var control = $(this._fieldsPrefix + this._fieldMap[f] );
      if ( control == null ) continue;
      if ( m > 0 ) {
        if ( control.maxLength ) {
          control.maxLength = m;
          var controlValueLength = control.value.length
          if(controlValueLength > 0) {
          	if(controlValueLength > 0) {
          		control.value = control.value.substring(0,m);
          	}
          	if(control.hasClassName('validatableAddressField')) {
          		var addressFieldValidateFunction = Validation.attachAddressValidation.bind(control);
          		addressFieldValidateFunction();
          	}
          }
        }
        if ( control.size ) {
              if ( (m + 2) <= MAX_FIELD_SIZE ) {
            control.size = m + 2;
              } else {
          control.size = MAX_FIELD_SIZE;
              }
        }
      }
    }
  }

}

function updateDropDownFromNameValuePairs( control, results ) {
    var previousValue = control.value;
    	attachAddressValidation = function() {
  		var countryCode = "AU";
  		validateMe(this, countryCode);
	}
    
    control.length = results.length + 1;
    control.options[0].value = -1;
    control.options[0].text = "Choose one...";
    
    for (i = 0; i < results.length; i++) {
      control.options[i+1].value = results[i].value;
      control.options[i+1].text = results[i].name;
      
      if ( previousValue == control.options[i+1].value ) {
        control.value = previousValue;
      }
    }
}
/*
* Use this so that the address validation doesn't happen "back to back".
*  This makes it wait a few seconds to avoid bogus events to be fired off too rapidly.
*/
var coolDownTimer = 0;
function setCoolDownTimer(amtToCoolDown) {
  if(amtToCoolDown) {
    coolDownTimer = amtToCoolDown;
  }

  if(coolDownTimer > 0) {
    setTimeout("setCoolDownTimer()", 1000); // recursive call every second.
    coolDownTimer -= 1000;
  }
}

/*******************************************************************************/
/*                                                                             */
/*        Form Fields Validation Methods                                       */
/*                                                                             */
/*******************************************************************************/
/*
      call attachValidationMethods via Event.observe(window, "load", yourFunction); to automatically validate
      fields on your page that have a class attribute of "validatable".
      
      ex:	attachAddressValidation:function() {
  		var countryCode = "AU";
  		validateMe(this, countryCode);
	}
      
          Event.observe(window, "load", attachValidationMethods );
          ...
          ...
          <input type="text" name="blah" class="validatable required etc">
          ...
  */

/* You should pass a prototypeDOMelement if at all possible for performance reasons. */
function attachValidationMethods(parentToSearch) {
  /**
  
                                     onblur vs onchange
  We can change the event to "onchange" from "onblur". But there are couple of disadvantages, 
  1. if we are changing the value of the text field programatically (for example using Mask), 
  then IE won't fire on change event.
  2. If we choose from the form cached data, the browsers (neither IE not firefox) won't trigger
  the onchange event
  
  But onblur has problems too
  
  1. It is too aggressive, Even if you didn't change the value it will try to validate it as soon as
  the fields loses its focus.
  
  So, please change this carefully, if you need to
  
  **/
  
  if (parentToSearch && parentToSearch.nodeName) parentToSearch = $(parentToSearch); 
  
  
  // If a nodeName exists this is an element to search and not an event. :)
  var elements = (parentToSearch && parentToSearch.nodeName) ? parentToSearch.getElementsByClassName("validatable") : document.getElementsByClassName("validatable");  // Attach AJAX
  //Passing the second item to search for children to validate on further.
  
  for (var x = 0; x < elements.length; x++) {
    Event.observe(elements[x], "blur", Validation.attachValidation.bindAsEventListener(elements[x]));
  }
  elements = (parentToSearch && parentToSearch.nodeName) ? parentToSearch.getElementsByClassName("validatableAddressField") : document.getElementsByClassName("validatableAddressField"); // Attach AJAX
  
  for (var x = 0; x < elements.length; x++) {
    Event.observe(elements[x], "blur", Validation.attachAddressValidation.bindAsEventListener(elements[x]));
  }  
  elements = (parentToSearch && parentToSearch.nodeName) ? parentToSearch.getElementsByClassName("required") : document.getElementsByClassName("required"); // Attach AJAX

  for (var x = 0; x < elements.length; x++) {
    Event.observe(elements[x], "blur", Validation.attachRequiredValidation.bindAsEventListener(elements[x]));
  }
  elements = (parentToSearch && parentToSearch.nodeName) ? parentToSearch.getElementsByClassName("confirming") : document.getElementsByClassName("confirming"); // ?
  
  for (var x = 0; x < elements.length; x++) {
    var name = elements[x].name;
    var fieldsToConfirm = document.getElementsByName(name);
    if ( fieldsToConfirm && fieldsToConfirm.length == 2 ) {
      Event.observe(fieldsToConfirm[0], "blur", Confirmation.attachConfirmation.bindAsEventListener(fieldsToConfirm[0]));
      Event.observe(fieldsToConfirm[1], "blur", Confirmation.attachConfirmation.bindAsEventListener(fieldsToConfirm[1]));
    }
  }
}

var Validation = {
	
	attachValidation:function () {
              //HACK,No better way to send market name 
              // For Enrollment if we are doing cross country sponsorship , then we want validate the
             // the enrollment form based on the market they are enrolling to..
             // i am seting enrollment market attr in the form which i am looking for here
                var marketOverride = document.getElementById("enrollment.marketOverride");
                if(marketOverride){
 	          validateMe(this,marketOverride.value);

                } else {
		  validateMe(this);	
                }
	},
	
	attachAddressValidation:function() {
		var prefix = this.id.substring( 0, this.id.lastIndexOf("_"));
		var countryGeoId = "countryGeoId";
		var countryControlName = (prefix) ? prefix + "_" + countryGeoId : countryGeoId;
		var countryCode = $(countryControlName).value;
		validateMe(this, countryCode);
	},
	
	attachRequiredValidation:function() {
		resetError(this);
		if (!Validation.checkForRequired(this)) {
			var a = new Array(2);
            a[0] = this.id;
            a[1] = CMSText.isTaskSelected;
        	validateFieldCallback(a);
        }
	},
	
	checkForRequired:function(field) {
		var result=true;
		if ( field.value == "" || field.value == "-1" ) {
			result=false;
		}
		return result;
	}
};

var Confirmation = {
attachConfirmation:function () {
  var name = this.name;
  var fieldsToConfirm = document.getElementsByName(name);
  var thisField = $(this);
  if ( fieldsToConfirm && fieldsToConfirm.length == 2) {
    confirmFields(fieldsToConfirm[0], fieldsToConfirm[1], this);
  }
  thisField.addClassName("touched");
}

};
function validateMe(field, marketName) {
	// reset error in case of differences
	resetError(field);
	validateRequired(field, null, null, marketName);
}

function resetError(field) {
	var field = $(field.id);
  field.removeClassName("error");
  
	var messageElement = $(field.id + "-error");
  messageElement.hide();
	messageElement.update();
	
}

function validateRequired(field, variant, validationKey, marketName) {
	 var fieldValue = (field.value) ? field.value : null;
  AjaxFacade.validateField(field.id, fieldValue, variant, validationKey, marketName,validateFieldCallback);
}
function validateFieldCallback(fieldAndMessage) {
  var fieldId = fieldAndMessage[0];
  var errorMessage = fieldAndMessage[1];
  var field = $(fieldId);
  var errorField = $(fieldId + "-error");
  
  if ( errorMessage != "") {
    if (errorMessage != "") field.addClassName("error");
    if (errorField) {
      errorField.update(errorMessage);
		  errorField.show();
    } else {
      alert("Cannot find the XHTML Field : " + fieldId + "-error");
    }
  } else {
    field.removeClassName("error");
    errorField.hide();
    errorField.update();
  }
  
}
function validateFieldsCallback(valArray) {
 
  if (valArray[0] == "error") {
    $(valArray[1]).addClassName("error");
    $(valArray[3]).show();
    $(valArray[1] + "-error").update(valArray[2]);
  } else {
    
    $(valArray[1]).removeClassName("error");
    $(valArray[3]).hide();
    $(valArray[1] + "-error").update();
  }
}
var defaultIcFormErrorMessage = '';

function validateOnSubmit(form) {
		  var errorMessageContainer = $('validationErrorMessage');
		  //Sometimes pages do their validation inside validatePage function and change the content under <span id="validationErrorMessage"/>
		  //Store the default error message that was set in formtemplate file
		  if(defaultIcFormErrorMessage == '') {
		  	defaultIcFormErrorMessage = errorMessageContainer.innerHtml;
		  }
		  //Reset to the default message every time. If an individual page wants to change it,
		  //they usually do it in the validatePage function.
		  errorMessageContainer.innerHtml=defaultIcFormErrorMessage;
		  
		  var validatePageResult=(typeof validatePage != 'function') || validatePage(form);
		  
		  if ( validateGeneral(form) && validatePageResult ) {
		  	errorMessageContainer.hide();
		  	return true;
		  } else {
		  	errorMessageContainer.show();
		  	location.href = "#top";
		  	return false;
		  }
		  
		  //Since we are using jQuery dialog to show the esuite error msgs and information,
		  //if we try to reuse the same div to show ic form validation error msg, it is not working
		  
		  /**
		  //I am not sure why we are trying to show the message in a div that exists in errormsg.jsp.
		  //Why can't we show it in the <span id="validationErrorMessage"/> from formtemplate
		  
		  if ( validateGeneral(form) && validatePageResult ) {
		    errorMessageContainer.hide();
		   
		    var errorContainters = $$("div#eventMsgContainer.error", "div#eventMsgContainer.info", "div#eventMsgContainer.hide");
            if (errorContainters && errorContainters.length > 0) {
               var newErrorContainer = errorContainters[0];
               newErrorContainer.hide();
            }
		    return true;
		  } else {
		    var errorContainters = $$("div#eventMsgContainer.error", "div#eventMsgContainer.info", "div#eventMsgContainer.hide");
		    if (errorContainters && errorContainters.length > 0) {
		       var newErrorContainer = errorContainters[0];
		       newErrorContainer.update(errorMessageContainer.innerHTML);
		       newErrorContainer.className = 'error';
		       newErrorContainer.show();
		    } else {
		    	errorMessageContainer.show();
		    }
		  	location.href = "#top";
		  	return false;
		  }
		  */
}

function validateGeneral(form) {
  var isValid = true;
  if ( !form ) form = document;
  elements = $A(form.getElementsByClassName("required"));
  elements = elements.concat(form.getElementsByClassName("confirming"));
  for (var x = 0; x < elements.length; x++) {
    var element = $(elements[x]);
    if (!element.disabled && !element.readonly) {
      if (!Validation.checkForRequired(element)) {
                                var a = new Array(2);
                                a[0] = element.id;
                                a[1] = CMSText.isTaskSelected;
        validateFieldCallback(a);
                                isValid = false;
      }
    }
  }
  elements = form.getElementsByClassName("validatable");
  for (var x = 0; x < elements.length; x++) {
    var element = $(elements[x]);
    if (!element.disabled && !element.readonly) {
      if ( element.hasClassName("error") ) {  // find a better way to decide if there is an error there already without re-validating.
                                isValid = false;
      }
    }
  }
  elements = form.getElementsByClassName("validatableAddressField");
  for (var x = 0; x < elements.length; x++) {
    var element = $(elements[x]);
    if (!element.disabled && !element.readonly) {
      if ( element.hasClassName("error")) {
                                isValid = false;
      }
    }
  }
  elements = form.getElementsByClassName("confirming");
  for (var x = 0; x < elements.length; x++) {
    var element = $(elements[x]);
    if (!element.disabled && !element.readonly) {
      var confirmingelement = $(element.id.substring("confirming-".length));
      if ( element.hasClassName("error") || !confirmFieldsNoAjax(element, confirmingelement) ) {
        isValid = false;
      }
    }
  }

  return isValid;
}

function confirmFields(field1, field2, fieldBlurredFrom) {
  if (confirmFieldHasBeenTouched(fieldBlurredFrom, field1) ||
      confirmFieldHasBeenTouched(fieldBlurredFrom, field2) ) {     
	var fieldValue1 = (field1.value) ? field1.value : null;
	var fieldValue2 = (field2.value) ? field2.value : null;
	AjaxFacade.confirmFields(field2.id, fieldValue1, fieldValue2,confirmFieldsCallback);
  }
}

function confirmFieldsNoAjax(field1, field2) {
  var isValid = field1 && field2 && field1.value && field2.value && field1.value == field2.value;
  $(field1).addClassName("touched");
  $(field2).addClassName("touched");
  if ( !isValid ) {
    var fieldAndMessage = new Array();
    fieldAndMessage[0] = field1.id;
    fieldAndMessage[1] = "These fields must match.";
    confirmFieldsCallback(fieldAndMessage);
  }
  return isValid;
}

function confirmFieldHasBeenTouched(originalField, testField) {
  return originalField != testField && testField.hasClassName("touched");
}

function confirmFieldsCallback(fieldAndMessage) {
  validateFieldCallback(fieldAndMessage);
}

var temp_virtualProductId;
var temp_featureCategories;
var temp_dropDownIds;
var temp_virtualFieldId;

function FeatureDropDowns(virtualFieldId, virtualProductId, featureCategories) {
  this._virtualFieldId = virtualFieldId;
  this._virtualProductId = virtualProductId;
  this._featureCategories = featureCategories;
  this._dropDownIds = new Array(this._featureCategories.length);
  this._firstDropDown;

  for ( var i = 0; i < this._featureCategories.length; i++ ) {
    this._dropDownIds[i] = this._featureCategories[i] + "_" + this._virtualProductId;
  }

  this.selectFeature = function(dropDown) {
    temp_virtualProductId = this._virtualProductId;
    temp_featureCategories = this._featureCategories;
    temp_dropDownIds = this._dropDownIds;
    temp_virtualFieldId = this._virtualFieldId;

    if ( !this._firstDropDown ) this._firstDropDown = dropDown;

    // if they select "choose one...", then let's just choose the first selected drop-down as the first drop-down, in its stead.
    if ( this._firstDropDown && this._firstDropDown.value == "" ) {
      this._firstDropDown = null;
      for ( var j = 0; j < this._dropDownIds.length; j++ ) {
        var candidateDropDown = $(this._dropDownIds[j]);
	if ( candidateDropDown && candidateDropDown.value != "" ) {
          this._firstDropDown = candidateDropDown;
        }
      }
    }
    
    var _selectedFeatures = this._getSelectedFeatures();
    var _selectedFeatureCategories = this._getSelectedFeatureCategories();

    if ( _selectedFeatures.length == this._featureCategories.length ) {
      AjaxFacade.getVariantProductId(this._virtualFieldId, this._virtualProductId, _selectedFeatureCategories, _selectedFeatures, this._getVariantProductIdCallback);
    } else {
      AjaxFacade.getFeatures(this._virtualFieldId, this._virtualProductId, _selectedFeatureCategories, _selectedFeatures, this._getFeaturesCallback);
    }
  }

  this._getSelectedFeatureCategories = function() {
      var _selectedFeatureCategories = new Array();
      if ( this._firstDropDown )
        _selectedFeatureCategories[0] = this._firstDropDown.name;
      for ( var i = 0; i < this._dropDownIds.length; i++ ) {
        if ( (!this._firstDropDown || this._dropDownIds[i] != this._firstDropDown.id) && $(this._dropDownIds[i]) ) {
          _selectedFeatureCategories[_selectedFeatureCategories.length] = $(this._dropDownIds[i]).name;
        }
      }
      return _selectedFeatureCategories;
  }

  this._getSelectedFeatures = function() {
      var _selectedFeatures = new Array();
      if ( this._firstDropDown )
        _selectedFeatures[0] = this._firstDropDown.value;
      for ( var i = 0; i < this._dropDownIds.length; i++ ) {
        if ( (!this._firstDropDown || this._dropDownIds[i] != this._firstDropDown.id) && $(this._dropDownIds[i]) && $(this._dropDownIds[i]).value != "" ) {
          _selectedFeatures[_selectedFeatures.length] = $(this._dropDownIds[i]).value;
        }
      }
      return _selectedFeatures;
  }

  this._getFeaturesCallback = function(returnValues) {
    var temp_virtualFieldId = returnValues[0];
    var temp_featureCategories = returnValues[1];
    var temp_virtualProductId = returnValues[2];
    var newFeatureDropDowns = returnValues[3];

    for ( var i = 0; i < temp_featureCategories.length; i++ ) {
      var features = newFeatureDropDowns[temp_featureCategories[i]];
      var dropDownId = $(temp_featureCategories[i] + "_" + temp_virtualProductId);
      dropDownId.options.length = features.length + 1;
      dropDownId.options[0].value = "";
      dropDownId.options[0].text = "Choose " + temp_featureCategories[i] + "...";
      for ( var j = 0; j < features.length; j++ ) {
        dropDownId.options[j+1].value = features[j];
        dropDownId.options[j+1].text = features[j];
      }
    }

    $(temp_virtualFieldId).value = "";
  }

  this._getVariantProductIdCallback = function(returnValues) {
    var temp_virtualFieldId = returnValues[0];
    var variantProductId = returnValues[1];

    if ( !variantProductId ) {
      alert("We are currently not offering this combination at this time.  Please make another selection.");
      $(temp_virtualFieldId).value = "";
      $(temp_virtualFieldId).focus();
    } else {
      $(temp_virtualFieldId).value = variantProductId;
    }
  }
}

