/* ****************************** AddressField ****************************** */

function AddressField(fieldName, fieldCaption, maxLength)
{
  this.fieldName = fieldName;
  this.fieldCaption = fieldCaption;
  this.maxLength = maxLength;
}

AddressField.createAddressFields = function(data)
{
  var fields = [];
  for (var i = 0; i < data.length; i++)
  {
    var curData = data[i];
    fields.push(new AddressField(curData[0], curData[1], curData[2]));
  }
  return fields;
}

/* ****************************** Address ****************************** */

function Address(addressId, profileType, firstName, lastName, address1, address2, city, state, zip, country, company, dayPhone, eveningPhone, profileName, emailAddress)
{
  this.addressId = addressId;
  this.profileType = noNull(profileType);
  this.firstName = noNull(firstName);
  this.lastName = noNull(lastName);
  this.address1 = noNull(address1);
  this.address2 = noNull(address2);
  this.city = noNull(city);
  this.state = noNull(state);
  this.zip = noNull(zip);
  this.country = noNull(country);
  this.company = noNull(company);
  this.dayPhone = noNull(dayPhone);
  this.eveningPhone = noNull(eveningPhone);
  this.profileName = noNull(profileName);
  this.emailAddress = noNull(emailAddress);
  
  this.isAddress = true;
}

Address.FORM_FIELD_DATA = AddressField.createAddressFields([
  ['FirstName', 'First Name', 50],
  ['LastName', 'Last Name', 50],
  ['Company', 'Company', 100],
  ['DayPhone', 'Day Phone', 50],
  ['EveningPhone', 'Evening Phone', 50],
  ['Address1', 'Address line 1', 100],
  ['Address2', 'Address line 2', 100],
  ['City', 'City', 100],
  ['Zip', 'Zip', 50],
  ['EmailAddress', 'Email', 75],
  ['ProfileName', 'Profile Name', 30]
]);

Address.BILLING_FIELD_ORDER = ['FirstName', 'LastName', 'Company', 'EmailAddress', 'DayPhone', 'EveningPhone', 'Address1', 'Address2', 'City', 'State', 'Zip', 'Country'];
Address.SHIPPING_FIELD_ORDER = ['FirstName', 'LastName', 'Company', 'DayPhone', 'EveningPhone', 'ProfileName', 'Address1', 'Address2', 'City', 'State', 'Zip', 'Country'];

Address.getFieldIndex = function(fieldName)
{
  for (var i = 0; i < Address.FORM_FIELD_DATA.length; i++)
    if (Address.FORM_FIELD_DATA[i].fieldName == fieldName)
      return i;
  return -1;
}

Address.getField = function(fieldName)
{
  var fieldIndex = Address.getFieldIndex(fieldName);
  return (fieldIndex < 0) ? null : Address.FORM_FIELD_DATA[fieldIndex];
}

Address.createDisplay = function(domPrefix, isBilling)
{
  var t = '<table border=0 cellspacing=0 cellpadding=3 class="smalltext" width="100%" style="line-height: 140%">\n'
        + '  <tr>\n'
        + '    <td valign="top" width="33%">\n'
        + '      <b>Name:</b> <span id="' + domPrefix + 'FullName"></span><br>\n'
        + '      <b>Company:</b> <span id="' + domPrefix + 'Company"></span>';
  if (isBilling)
    t += '<br>\n'
       + '      <b>Email:</b> <span id="' + domPrefix + 'EmailAddress"></span>\n';
  else
    t += '\n';
  t += '    </td>\n'
     + '    <td valign="top" width="33%">\n'
     + '      <table border=0 cellspacing=0 cellpadding=0 class="smalltext" style="line-height: 140%">\n'
     + '        <tr>\n'
     + '          <td valign="top"><b>Address:</b></td>\n'
     + '          <td>&nbsp;&nbsp;</td>\n'
     + '          <td valign="top"><span id="' + domPrefix + 'FullAddress"></span></td>\n'
     + '        </tr>\n'
     + '      </table>\n'
     + '    </td>\n'
     + '    <td valign="top" width="33%">\n'
     + '      <b>Country:</b> <span id="' + domPrefix + 'Country"></span><br>\n'
     + '      <b>Phone (day):</b> <span id="' + domPrefix + 'DayPhone"></span><br>\n'
     + '      <b>Phone (evening):</b> <span id="' + domPrefix + 'EveningPhone"></span>\n'
     + '    </td>\n'
     + '  </tr>\n'
     + '</table>\n';
  return t;
}

Address.createForm = function(domPrefix, isBilling, defaultCountryName, defaultStateAbbrev, fieldList, baseTabIndex)
{
  var defaultCountry = null;
  var defaultState = null;
  if ((defaultCountryName != null) && (Address.COUNTRIES != null))
  {
    for (var i = 0; i < Address.COUNTRIES.length; i++)
    {
      var curCountry = Address.COUNTRIES[i];
      if (curCountry.countryName == defaultCountryName)
      {
        defaultCountry = curCountry;
        if ((defaultStateAbbrev != null) && (defaultCountry.states != null))
        {
          for (var j = 0; j < defaultCountry.states.length; j++)
          {
            var curState = defaultCountry.states[j];
            if (curState.abbrev == defaultStateAbbrev)
            {
              defaultState = curState;
              break;
            }
          }
        }
        break;
      }
    }
  }
  if ((defaultCountry == null) && (Address.COUNTRIES != null))
    defaultCountry = Address.COUNTRIES[0];
  
  var t = '<form name="' + domPrefix + 'MainForm" style="margin: 0">\n'
        + '<table border=0 cellspacing=0 cellpadding=3 class="smalltext">\n';
  var fieldOrder = (fieldList != null) ? fieldList : (isBilling ? Address.BILLING_FIELD_ORDER : Address.SHIPPING_FIELD_ORDER);
  var fieldsPerColumn = (fieldOrder.length >>> 1);
  if ((baseTabIndex == null) || (baseTabIndex <= 0))
    baseTabIndex = 1;
  for (var i = 0; i < fieldsPerColumn; i++)
  {
    t += '  <tr>\n';
    for (var j = 0; j < 2; j++)
    {
      var curFieldOrder = fieldOrder[fieldsPerColumn * j + i];
      var field = Address.getField(curFieldOrder);
      var curTabIndex = baseTabIndex + (fieldsPerColumn * j) + i;
      if (field != null)
      {
        t += '    <td valign="top"><b>' + field.fieldCaption + ':</b></td><td valign="top"><input type="text" name="' + domPrefix + field.fieldName + '" value="" maxlength="' + field.maxLength + '" tabindex="' + curTabIndex + '"></td>\n';
        continue;
      }
      if (curFieldOrder == 'Country')
      {
        t += '    <td valign="top"><b>Country:</b></td><td valign="top">';
        if (defaultCountry == null)
          t += 'Internal Error';
        else
        {
          t += '<select name="' + domPrefix + 'CountrySelect" size=1 tabindex="' + curTabIndex + '" onClick="Address.updateStateSelect(\'' + domPrefix + '\')" onChange="Address.updateStateSelect(\'' + domPrefix + '\')">\n';
          for (var countryIndex = 0; countryIndex < Address.COUNTRIES.length; countryIndex++)
          {
            var curCountry = Address.COUNTRIES[countryIndex];
            t += '<option value="' + curCountry.countryName + '"';
            if (curCountry == defaultCountry)
              t += ' SELECTED';
            t += '>' + curCountry.displayName + '</option>\n';
          }
          t += '</select>\n';
        }
        t + '</td>\n';
        continue;
      }
      if (curFieldOrder == 'State')
      {
        t += '    <td valign="top"><b>State:</b></td><td valign="top">';
        if (defaultCountry == null)
          t += 'Internal Error';
        else
        {
          t += '<select name="' + domPrefix + 'StateSelect" size=1 tabindex="' + curTabIndex + '" onClick="Address.clearBlankOption(this)" onChange="Address.clearBlankOption(this)" countryName="' + defaultCountry.countryName + '">\n';
          if (defaultCountry.hasStates())
          {
            if (defaultState == null)
              t += '<option value="">Select state</option>\n';
            for (var stateIndex = 0; stateIndex < defaultCountry.states.length; stateIndex++)
            {
              var curState = defaultCountry.states[stateIndex];
              t += '<option value="' + curState.abbrev + '"';
              if (curState == defaultState)
                t += ' SELECTED';
              t += '>' + curState.displayName + '</option>\n';
            }
          }
          else
            t += '<option value="">None</option>\n';
          t += '</select>\n';
        }
        t + '</td>\n';
        continue;
      }
      t += '    <td valign="top">&nbsp;</td><td valign="top">&nbsp;</td>\n';
    }
    t += '  </tr>\n';
  }
  t += '</table>\n';
     + '</form>\n';
  return t;
}

Address.updateStateSelect = function(domPrefix)
{
  var f = document.forms[domPrefix + 'MainForm'];
  if (!f)
    return;
  var countrySel = f[domPrefix + 'CountrySelect'];
  var stateSel = f[domPrefix + 'StateSelect'];
  if (!countrySel || !stateSel)
    return;
  var country = Address.COUNTRIES[countrySel.selectedIndex];
  
  var curStateCountryName = null;
  if (stateSel.attributes['countryName'])
    curStateCountryName = stateSel.attributes['countryName'].value;
  else
    if (stateSel.countryName)
      curStateCountryName = stateSel.countryName;
  if (country.countryName == curStateCountryName)
    return;
  var optionCount = 0;
  if (country.hasStates())
  {
    stateSel.options[optionCount++] = new Option('Select state', '');
    for (var i = 0; i < country.states.length; i++)
    {
      var curState = country.states[i];
      stateSel.options[optionCount++] = new Option(curState.displayName, curState.abbrev);
    }
  }
  else
    stateSel.options[optionCount++] = new Option('None', '');
  stateSel.selectedIndex = 0;
  stateSel.length = optionCount;
  if (stateSel.attributes['countryName'])
    stateSel.attributes['countryName'].value = country.countryName;
  else
    if (stateSel.countryName)
      stateSel.countryName = country.countryName;
  return;
}

Address.selectCountry = function(domPrefix, countryName)
{
  var f = document.forms[domPrefix + 'MainForm'];
  var countrySel = f[domPrefix + 'CountrySelect'];
  if (!countrySel)
    return false;
  for (var i = 0; i < countrySel.options.length; i++)
  {
    if (countrySel.options[i].value == countryName)
    {
      countrySel.selectedIndex = i;
      return true;
    }
  }
  return false;
}

Address.selectState = function(domPrefix, stateName)
{
  var f = document.forms[domPrefix + 'MainForm'];
  var stateSel = f[domPrefix + 'StateSelect'];
  if (!stateSel)
    return false;
  for (var i = 0; i < stateSel.options.length; i++)
  {
    if (stateSel.options[i].value == stateName)
    {
      stateSel.selectedIndex = i;
      Address.clearBlankOption(stateSel);
      return true;
    }
  }
  return false;
}

Address.clearBlankOption = function(s)
{
  var si = s.selectedIndex;
  if (si > 0)
  {
    if (s.options[0].value == '')
    {
      var slen = s.length;
      for (var i = 1; i < slen; i++)
      {
        s.options[i - 1].value = s.options[i].value;
        s.options[i - 1].text = s.options[i].text;
      }
      s.length = slen - 1;
      s.selectedIndex = si - 1;
    }
  }
  return;
}

Address.prototype.duplicate = function()
{
  return new Address(this.addressId, this.profileType, this.firstName, this.lastName, this.address1, this.address2, this.city, this.state, this.zip, this.country, this.company, this.dayPhone, this.eveningPhone, this.profileName, this.emailAddress);
}

Address.prototype.getFullName = function()
{
  if (this.firstName.length == 0)
    return this.lastName;
  if (this.lastName.length == 0)
    return this.firstName;
  return this.firstName + ' ' + this.lastName;
}

Address.prototype.getAddressDisplayHtml = function()
{
  var t = '';
  if (this.address1.length > 0)
    t += this.address1 + '<br>\n';
  if (this.address2.length > 0)
    t += this.address2 + '<br>\n';
  t += this.city + ', ' + this.state + ' ' + this.zip;
  return t;
}

Address.prototype.isBilling = function()
{
  return (this.profileType == 'B');
}

Address.isValidZip = function(zip, country)
{
  zip = trimString(zip);
  if (country == 'USA')
  {
    if (zip.length < 5)
      return false;
    var dashInd = zip.indexOf('-');
    var zipExt = '';
    if (dashInd < 0)
    {
      if (zip.length == 9)
      {
        zipExt = trimString(zip.substring(5, zip.length));
        zip = trimString(zip.substring(0, 5));
      }
    }
    else
    {
      zipExt = trimString(zip.substring(dashInd + 1, zip.length));
      zip = trimString(zip.substring(0, dashInd));
    }
    if (zip.length != 5)
      return false;
    if ((zipExt.length > 0) && (zipExt.length != 4))
      return false;
    for (var i = 0; i < zip.length; i++)
    {
      var c = zip.charAt(i);
      if ((c < '0') || (c > '9'))
        return false;
    }
    for (var i = 0; i < zipExt.length; i++)
    {
      var c = zipExt.charAt(i);
      if ((c < '0') || (c > '9'))
        return false;
    }
    return true;
  }
  if (country == 'CANADA')
  {
    // ANA-NAN
    zip = zip.toUpperCase();
    var alpha = true;
    var count = 0;
    for (var i = 0; i < zip.length; i++)
    {
      var c = zip.charAt(i);
      if ((c == '-') || (c == ' '))
      {
        if (count == 3)
          continue;
        return false;
      }
      if (alpha ? ((c < 'A') || (c > 'Z')) : ((c < '0') || (c > '9')))
        return false;
      alpha = !alpha;
      count++;
    }
    return (count == 6);
  }
  return true;
}

Address.prototype.isValidZip = function()
{
  return Address.isValidZip(this.zip, this.country);
}

Address.isValidEmailAddress = function(emailAddress)
{
  if (emailAddress == null)
    return false;
  emailAddress = trimString(emailAddress);
  if (emailAddress.length == 0)
    return false;
  if (emailAddress.indexOf(' ') >= 0)
    return false;

  var emMax = emailAddress.length - 1;
  var emAt = emailAddress.indexOf('@');
  if ((emAt <= 0) || (emAt >= emMax))
    return false;
  emAt++;
  var emDot = emailAddress.indexOf('.', emAt);
  return ((emDot > 0) && (emDot != emAt) && (emDot < emMax));
}

Address.prototype.isValidEmailAddress = function()
{
  return Address.isValidEmailAddress(this.emailAddress);
}

Address.isValidCountryAndState = function(countryName, stateAbbrev)
{
  if ((countryName == null) || (countryName.length == 0))
    return false;
  var curCountry = null;
  for (var i = 0; i < Address.COUNTRIES.length; i++)
  {
    if (Address.COUNTRIES[i].countryName == countryName)
    {
      curCountry = Address.COUNTRIES[i];
      break;
    }
  }
  if (curCountry == null)
    return false;
  if (!curCountry.hasStates())
    return true;
  if ((stateAbbrev == null) || (stateAbbrev.length == 0))
    return false;
  for (var i = 0; i < curCountry.states.length; i++)
    if (curCountry.states[i].abbrev == stateAbbrev)
      return true;
  return false;
}

Address.prototype.isValidCountryAndState = function()
{
  return Address.isValidCountryAndState(this.country, this.state);
}

Address.getFormValue = function(domPrefix, fieldName)
{
  var f = document.forms[domPrefix + 'MainForm'];
  if (!f)
    return '';
  f = f[domPrefix + fieldName];
  if (!f)
    return '';
  return trimString(f.value);
}

Address.getFormSelectedValue = function(domPrefix, fieldName)
{
  var f = document.forms[domPrefix + 'MainForm'];
  if (!f)
    return '';
  var s = f[domPrefix + fieldName];
  if (!s)
    return '';
  return s.options[s.selectedIndex].value;
}

Address.updateFormValue = function(domPrefix, fieldName, fieldValue)
{
  var f = document.forms[domPrefix + 'MainForm'];
  if (!f)
    return;
  f = f[domPrefix + fieldName];
  if (f)
    f.value = fieldValue;
  return;
}

Address.prototype.updateFromForm = function(domPrefix)
{
  var f = document.forms[domPrefix + 'MainForm'];
  if (!f)
    return false;
  this.firstName = Address.getFormValue(domPrefix, 'FirstName');

  this.lastName = Address.getFormValue(domPrefix, 'LastName');
  this.address1 = Address.getFormValue(domPrefix, 'Address1');
  this.address2 = Address.getFormValue(domPrefix, 'Address2');
  this.city = Address.getFormValue(domPrefix, 'City');
  this.zip = Address.getFormValue(domPrefix, 'Zip');
  this.company = Address.getFormValue(domPrefix, 'Company');
  this.dayPhone = Address.getFormValue(domPrefix, 'DayPhone');
  this.eveningPhone = Address.getFormValue(domPrefix, 'EveningPhone');
  this.country = Address.getFormSelectedValue(domPrefix, 'CountrySelect');
  this.state = Address.getFormSelectedValue(domPrefix, 'StateSelect');
  
  if (this.isBilling())
    this.emailAddress = Address.getFormValue(domPrefix, 'EmailAddress');
  else
    this.profileName = Address.getFormValue(domPrefix, 'ProfileName');
  return true;
}

Address.prototype.updateToForm = function(domPrefix)
{
  var f = document.forms[domPrefix + 'MainForm'];
  if (!f)
    return false;
  Address.updateFormValue(domPrefix, 'FirstName', trimString(this.firstName));
  Address.updateFormValue(domPrefix, 'LastName', trimString(this.lastName));
  Address.updateFormValue(domPrefix, 'Address1', trimString(this.address1));
  Address.updateFormValue(domPrefix, 'Address2', trimString(this.address2));
  Address.updateFormValue(domPrefix, 'City', trimString(this.city));
  Address.updateFormValue(domPrefix, 'Zip', trimString(this.zip));
  Address.updateFormValue(domPrefix, 'Company', trimString(this.company));
  Address.updateFormValue(domPrefix, 'DayPhone', trimString(this.dayPhone));
  Address.updateFormValue(domPrefix, 'EveningPhone', trimString(this.eveningPhone));
  Address.selectCountry(domPrefix, trimString(this.country));
  Address.updateStateSelect(domPrefix);
  Address.selectState(domPrefix, trimString(this.state));
  
  if (this.isBilling())
    Address.updateFormValue(domPrefix, 'EmailAddress', trimString(this.emailAddress));
  else
    Address.updateFormValue(domPrefix, 'ProfileName', trimString(this.profileName));
  return true;
}

Address.prototype.updateToDisplay = function(domPrefix)
{
  getById(domPrefix + 'FullName').innerHTML = trimString(this.getFullName());
  getById(domPrefix + 'Company').innerHTML = trimString(this.company);
  getById(domPrefix + 'FullAddress').innerHTML = trimString(this.getAddressDisplayHtml());
  getById(domPrefix + 'Country').innerHTML = trimString(this.country);
  getById(domPrefix + 'DayPhone').innerHTML = trimString(this.dayPhone);
  getById(domPrefix + 'EveningPhone').innerHTML = trimString(this.eveningPhone);
  if (this.isBilling())
    getById(domPrefix + 'EmailAddress').innerHTML = trimString(this.emailAddress);
  return;
}

Address.prototype.isValid = function()
{
  if ((this.firstName.length == 0) || (this.lastName.length == 0))
  {
    alert('Please fill in your first and last name.');
    return false;
  }
  if (this.isBilling())
  {
    if (this.emailAddress.length == 0)
    {
      alert('Please fill in your email address.');
      return false;
    }
    if (!this.isValidEmailAddress())
    {
      alert('Your email address appears to be invalid; please correct it.');
      return false;
    }
  }
  else
  {
    if (this.profileName.length == 0)
    {
      alert('Please enter a name for this shipping profile.');
      return false;
    }
  }
  if (this.address1.length == 0)
  {
    alert('Please fill in your address.');
    return false;
  }
  if (this.city.length == 0)
  {
    alert('Please fill in your city.');
    return false;
  }
  if ((this.dayPhone.length == 0) && (this.eveningPhone.length == 0))
  {
    alert('Please fill in your phone number.');
    return false;
  }
  if (!this.isValidCountryAndState())
  {
    alert('Please select your country and/or state.');
    return false;
  }
  if ((this.country == 'USA') || (this.country == 'CANADA'))
  {
    if (this.zip.length == 0)
    {
      alert('Please enter your zip/postal code.');
      return false;
    }
    if (!this.isValidZip())
    {
      alert('Your zip/postal code appears invalid; please correct it.');
      return false;
    }
  }
  return true;
}

Address.prototype.convertToString = function()
{
  return escape('' + this.addressId)
         + ',' + escape(this.profileType)
         + ',' + escape(this.firstName)
         + ',' + escape(this.lastName)
         + ',' + escape(this.address1)
         + ',' + escape(this.address2)
         + ',' + escape(this.city)
         + ',' + escape(this.state)
         + ',' + escape(this.zip)
         + ',' + escape(this.country)
         + ',' + escape(this.company)
         + ',' + escape(this.dayPhone)
         + ',' + escape(this.eveningPhone)
         + ',' + (this.isBilling() ? '' : escape(this.profileName))
         + ',' + (this.isBilling() ? escape(this.emailAddress) : '');
}

Address.fixPuertoRico = function(domPrefix)
{
  var country = Address.getFormSelectedValue(domPrefix, 'CountrySelect');
  if (country == 'PUERTO RICO')
  {
    Address.selectCountry(domPrefix, 'USA');
    Address.updateStateSelect(domPrefix);
    Address.selectState(domPrefix, 'PR');
  }
  return;
}

/* ****************************** Country ****************************** */

function Country(countryName, displayName, states)
{
  this.countryName = countryName;
  this.displayName = displayName;
  this.states = states;
  if (this.states != null)
    for (var i = 0; i < this.states.length; i++)
      this.states[i].country = this;
}

Country.prototype.hasStates = function()
{
  return (this.states != null) && (this.states.length > 0);
}

Country.createCountriesFromArray = function(data)
{
  var countries = [];
  for (var i = 0; i < data.length; i += 3)
    countries.push(new Country(data[i], data[i + 1], State.createStatesFromArray(data[i + 2])));
  return countries;
}

/* ****************************** State ****************************** */

function State(abbrev, displayName)
{
  this.abbrev = abbrev;
  this.displayName = displayName;
}

State.createStatesFromArray = function(data)
{
  var states = [];
  if (data != null)
    for (var i = 0; i < data.length; i += 2)
      states.push(new State(data[i], data[i + 1]));
  return states;
}

/* ****************************** DomesticAddressVerifier ****************************** */

function DomesticAddressVerifier(jsName, domPrefix, callbackFunction)
{
  this.jsName = jsName;
  this.domPrefix = domPrefix;
  this.callbackFunction = callbackFunction;

  var thisVerifier = this;
  this.requester = new BGRequest(this.jsName + '.requester');
  this.requester.normalHandler = function(txt, xml) {  thisVerifier.requestHandler(txt, xml);  }
  this.requester.timeoutHandler = function() {  thisVerifier.timeoutHandler();  }
  this.requester.errorHandler = function(status) {  thisVerifier.errorHandler(status);  }
  this.requester.reentrantHandler = function() {  thisVerifier.reentrantHandler();  }
  this.requester.noXMLHttpHandler = function() {  thisVerifier.seriousError();  }
  this.requester.timeoutInterval = 20000;
  this.lastError = null;
  this.clearStatus();
}

DomesticAddressVerifier.prototype.isBusy = function()
{
  return (this.requester.rqStarted > 0);
}

DomesticAddressVerifier.prototype.clearStatus = function()
{
  this.debugMode = false;
  this.status = null;
  this.message = null;
  this.isError = null;
  this.canIgnore = null;
  this.suggestions = null;
  return;
}

DomesticAddressVerifier.prototype.verifyAddress = function(address)
{
  var t = '/customer/checkDomesticAddressXml.jsp?city=' + escape(address.city) + '&state=' + escape(address.state) + '&zip=' + escape(address.zip) + '&addressId=' + address.addressId + '&isBilling=' + (address.isBilling() ? 'y' : 'n') + '&rnd=' + Math.random();
  this.requester.performRequest('GET', t, true, null);
  return;
}

DomesticAddressVerifier.prototype.processResults = function()
{
  if (this.status == null)
    this.status = 'INTERNAL_ERROR';
  if (this.message == null)
    this.message = 'Internal error.';
  if (this.isError == null)
    this.isError = true;
  if (this.canIgnore == null)
    this.canIgnore = true;
  this.updateSuggestionsMsg();
  this.updateSuggestions();
  this.callbackFunction(this);
  return;
}

DomesticAddressVerifier.prototype.requestHandler = function(txt, xml)
{
  this.clearStatus();
  try
  {
    if (!xml)
    {
      this.errorHandler(null);
      return;
    }
    var result = getFirstElementByTagName(xml, 'Result');
    if (!result)
    {
      this.errorHandler(null);
      return;
    }
    var status = getDataByTagName(result, 'Status');
    var message = getDataByTagName(result, 'Message');
    var isError = (getDataByTagName(result, 'IsError') == 'Y');
    var canIgnore = (getDataByTagName(result, 'CanIgnore') == 'Y');

    var suggestions = null;
    var xmlSuggestions = getFirstElementByTagName(result, 'Suggestions');
    if (xmlSuggestions)
    {
      var xmlEntries = xmlSuggestions.getElementsByTagName('Entry');
      suggestions = new Array(xmlEntries.length);
      for (var i = 0; i < xmlEntries.length; i++)
      {
        var curNode = xmlEntries.item(i);
        suggestions[i] = new DomesticAddressSuggestion(getDataByTagName(curNode, 'City'),
                                                       getDataByTagName(curNode, 'State'),
                                                       getDataByTagName(curNode, 'Zip'));
      }
    }
    this.status = status;
    this.message = message;
    this.isError = isError;
    this.canIgnore = canIgnore;
    this.suggestions = suggestions;
  }
  catch(e)
  {
    this.clearStatus();
    this.lastError = 'There was an error retrieving the verification result: ' + e.message;
    if (this.debugMode)
    {
      alert(this.lastError);
      throw e;
    }
  }
  this.processResults();
  return;
}

DomesticAddressVerifier.prototype.timeoutHandler = function()
{
  this.lastError = 'Timeout';
  this.processResults();
  return;
}

DomesticAddressVerifier.prototype.errorHandler = function(status)
{
  this.lastError = 'Server returned an error code: ' + status;
  this.processResults();
  return;
}

DomesticAddressVerifier.prototype.reentrantHandler = function()
{
  alert('Busy, please wait.');
  return;
}

DomesticAddressVerifier.prototype.seriousError = function()
{
  this.lastError = 'Serious error (browser compat)';
  this.processResults();
  return;
}

DomesticAddressVerifier.prototype.initSelectHtml = function()
{
  var d = getById(this.domPrefix + 'Suggestions');
  if (d)
  {
    d.innerHTML = '<select id="' + this.domPrefix + 'SuggestionsList" size=1 onChange="' + this.jsName + '.applySuggestion(this)"></select>';
    this.updateSuggestions();
  }
  return;
}

DomesticAddressVerifier.prototype.clearSuggestions = function()
{
  this.message = '';
  this.suggestions = [];
  return;
}

DomesticAddressVerifier.prototype.updateSuggestionsMsg = function()
{
  var d = getById(this.domPrefix + 'SuggestionsMsg');
  if (d)
    d.innerHTML = this.isError ? this.message : '';
  return;
}

DomesticAddressVerifier.prototype.updateSuggestions = function()
{
  var d = getById(this.domPrefix + 'Suggestions');
  if (d)
  {
    if (this.hasSuggestions())
    {
      this.fillSuggestionList(getById(this.domPrefix + 'SuggestionsList'));
      d.style.display = '';
    }
    else
      d.style.display = 'none';
  }
  return;
}

DomesticAddressVerifier.prototype.applySuggestion = function(s)
{
  var si = s.selectedIndex;
  if ((si < 0) || (si >= s.length))
    return;
  var f = document.forms[this.domPrefix + 'MainForm'];
  if (!f)
    return;
  var index = parseInt(s.options[si].value);
  if (index >= 0)
  {
    var suggestion = this.suggestions[index];
    if (Address.selectCountry(this.domPrefix, 'USA') && Address.selectState(this.domPrefix, suggestion.state))
    {
      f[this.domPrefix + 'City'].value = suggestion.city;
      var oldZip = trimString(f[this.domPrefix + 'Zip'].value);
      var plusFour = null;
      if (oldZip.length == 9)
      {
        if (DomesticAddressVerifier.allNumeric(oldZip))
          plusFour = oldZip.substring(5);
      }
      else
      {
        if ((oldZip.length > 9) && DomesticAddressVerifier.allNumeric(oldZip.substring(0, 5)) && DomesticAddressVerifier.allNumeric(oldZip.substring(oldZip.length - 4)))
        {
          var okay = true;
          for (var i = 5; i < oldZip.length - 4; i++)
          {
            var c = oldZip.charAt(i);
            if ((c != ' ') && (c != '-'))
            {
              okay = false;
              break;
            }
          }
          if (okay)
            plusFour = oldZip.substring(oldZip.length - 4);
        }
      }
      var newZip = suggestion.zip;
      if (plusFour != null)
        newZip += '-' + plusFour;
      f[this.domPrefix + 'Zip'].value = newZip;
      var d = getById(this.domPrefix + 'SuggestionsMsg');
      if (d)
        d.innerHTML = '';
      this.parentAddressUpdater.suggestionApplied(this);
    }
  }
  return;
}

DomesticAddressVerifier.allNumeric = function(t)
{
  for (var i = 0; i < t.length; i++)
  {
    var c = t.charAt(i);
    if ((c < '0') || (c > '9'))
      return false;
  }
  return true;
}

DomesticAddressVerifier.prototype.hasSuggestions = function()
{
  return (this.suggestions != null) && (this.suggestions.length > 0);
}

DomesticAddressVerifier.prototype.fillSuggestionList = function(s)
{
  if (!s)
    return;
  s.length = this.suggestions.length + 1;
  s.options[0] = new Option('Suggested corrections', '-1');
  s.selectedIndex = 0;
  for (var i = 0; i < this.suggestions.length; i++)
  {
    var entry = this.suggestions[i];
    s.options[i + 1] = new Option(entry.asString(), '' + i);
  }
  return;
}

DomesticAddressVerifier.prototype.getSuggestionByIndex = function(index)
{
  if (!this.hasSuggestions() || (index < 0) || (index >= this.suggestions.length))
    return null;
  return this.suggestions[index];
}


/* ****************************** DomesticAddressSuggestion ****************************** */

function DomesticAddressSuggestion(city, state, zip)
{
  this.city = city;
  this.state = state;
  this.zip = zip;
}

DomesticAddressSuggestion.prototype.asString = function()
{
  return this.city + ', ' + this.state + '  ' + this.zip;
}
