/*
  (c) Copyright 2006, ClickRSVP
  Javascript for all Steps
  Requires enumeraAjax.js
  Requires prototype.js (version 1.3.1 adjusted by JMH on 2005-11-04)
  Requires prototypeExt.js
  Requires validations.js
  camelize function from Effects.js, Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
*/

// TO DO: centralize all form onsubmit functions into one?

if (!window.Hvc) window.Hvc = {};
if (!window.controller) window.controller = {};

(new Image()).src = "images/loading.gif"; // pre-load loading animation gif

// Hvc object

Object.extend(Hvc, {
  setProductHtml: function (descriptionsHtml, descriptionsPopupHtml) {
    var elDesc = this.controls.descriptionsArea,
        elPopups = this.controls.descriptionPopupsArea;
    if (elDesc) elDesc.innerHTML = descriptionsHtml;
    if (elPopups) elPopups.innerHTML = descriptionsPopupHtml;
  },
  findProduct: function (productId) {
    if (this.products)
      for (var i = 0; i < this.products.length; i++) 
        if (this.products[i].ProductId == productId)
          return this.products[i];
    return null;
  },
  formatPercent: function (amount, precision, forceDigits) {
    precision = precision || 2;
    var precExp = Math.pow(10, precision);
    amount = parseFloat(amount);
    if (isNaN(amount)) 
      amount = '0';
    else
      amount = '' + Math.round(amount * precExp * 100) / precExp;
    if (forceDigits) {
      var dotPos = amount.indexOf('.');
      amount += (dotPos == -1 ? '.' : '') + '00000000000000000000'; // only works to precision == 20
      amount = amount.substr(0, 1 + Math.abs(dotPos) + precision);
    }
    return amount + '%';
  },
  formatRate: function (rateObj) {
    if (rateObj.Abbrev)
      return rateObj.Abbrev + (rateObj.Rate >= 0 ? '+' : '-') + this.formatPercent(Math.abs(rateObj.Rate), 2, true);
    else
      return this.formatPercent(rateObj.Rate, 2, true);
  },
  formatDollars: function (amount, showCents) {
    if (typeof showCents == 'undefined') showCents = 'auto';
    var result = "";
    amount = this.unformatDollars(amount);
    var isNeg = amount != Math.abs(amount);
    amount = isNaN(amount) ? "0.00" : "" + Math.abs(Math.round(amount * 100) / 100);
    amount = amount.split('.');
    for (var i = 1; i <= amount[0].length; i++) {
      result = amount[0].substr(amount[0].length - i, 1) + (i != 1 && i % 3 == 1 ? "," : "") + result;
    }
    if ((showCents == 'auto' || showCents) && amount.length > 1)
      result += '.' + (amount[1] + '00').substr(0, 2);
    else if (showCents)
      result += '.00';
    return (isNeg ? "-$" : "$") + result;
  },
  unformatDollars: function (text) {
    text = '' + text;
    var amount = parseFloat(text.replace(/[\$\,]/g, ''));
    return isNaN(amount) ? 0 : amount;
  },
  getRateRange: function (rate) { // gets a rate's range, including ltv constraints
    var range = {'AmountMin': rate.AmountMin || 0, 'AmountMax': rate.AmountMax || Number.MAX_VALUE};
    range.AmountMin = Math.max(range.AmountMin, this.params.minAmount);
    range.AmountMax = Math.min(range.AmountMax, this.params.maxAmount);
    if (this.step3Handlers && this.step3Handlers.getRateRange) {
      var range2 = this.step3Handlers.getRateRange(rate);
      range.AmountMin = Math.max(range.AmountMin, range2.AmountMin);
      range.AmountMax = Math.min(range.AmountMax, range2.AmountMax);
    }
    var minByLtv = Math.round(this.homeValue.HvbHiValue * (rate.LtvMin || 0) - this.homeValue.Mort1Amount),
        maxByLtv = Math.round(this.homeValue.HvbHiValue * (rate.LtvMax || 1) - this.homeValue.Mort1Amount);
    range.AmountMin = Math.min(Math.max(range.AmountMin, minByLtv), maxByLtv);
    range.AmountMax = Math.min(Math.max(range.AmountMax, minByLtv), maxByLtv);
    return range;
  },
  findFirstRate: function (rates, amount, years) {
    rates = rates || this.rates;
    var rate = null;
    // search for first rate that satisfies requirements
    if (rates) 
      for (var i = 0; i < rates.length && rate == null; i++) {
        var range = this.getRateRange(rates[i]),
            isTopRange = range.AmountMax == amount && this.params.maxAmount == amount;
        if ((this.ifNull(rates[i].TermMax, Number.MAX_VALUE) >= years && this.ifNull(rates[i].TermMin, -Number.MAX_VALUE) < years)
            && ((range.AmountMax > amount || isTopRange) && range.AmountMin <= amount)
            && this.isValidRate(rates[i], amount, years))
          rate = rates[i];
      }
    return rate;
  },
  findOptimalRate: function (rates, amount) {
    // Calculate Combined LTV
    var ltv = Math.min(1.0, (amount + this.homeValue.Mort1Amount) / this.homeValue.HvbHiValue);
    rates = rates || this.rates;
    var rate = null;
    // search for best rate for given amount
    if (rates) 
      for (var i = 0; i < rates.length; i++) {
        if ((this.ifNull(rates[i].AmountMax, Number.MAX_VALUE) > amount && this.ifNull(rates[i].AmountMin, -Number.MAX_VALUE) <= amount) 
            && (this.ifNull(rates[i].LtvMax, Number.MAX_VALUE) >= ltv && this.ifNull(rates[i].LtvMin, -Number.MAX_VALUE) < ltv)
            && this.isValidRate(rates[i], amount))
          if (!rate || rates[i].Rate < rate.Rate)
            rate = rates[i];
      }
    return rate;
  },
  ifNull: function (value, defValue) {
    return (value == null) ? defValue : value;
  },
  isValidRate: function (rate, amount) {
    if (!this.step3Handlers || !this.step3Handlers.isValidRate)
      return true;
    else
      return this.step3Handlers.isValidRate(rate, amount);
  },
  calcEquity: function () {
    // This is for the equity calculated and sent to the back-end
    var mort = this.unformatDollars($('MortgageBalance').value), mort2 = this.unformatDollars($('SecondMortgageBalance').value);
    mort = Math.max(0, mort || 0); // fix negative values or nulls
    mort2 = Math.max(0, mort2 || 0);
    this.homeValue.Mort1Amount = mort;
    this.homeValue.Mort2Amount = mort2;
    this.homeValue.EquityHi = Math.round(Math.max(0, this.homeValue.HvbHiValue - mort));
    this.homeValue.EquityLow = Math.round(Math.max(0, this.homeValue.HvbLowValue - mort));
    this.homeValue.Equity = this.homeValue.EquityHi; // use upper value
    if (this.products.selectedProduct) {
      this.homeValue.Borrowable = Math.floor((this.products.selectedProduct.LtvMax || 1.0) * this.homeValue.HvbHiValue - this.homeValue.Mort1Amount),
      this.homeValue.CashBack = Math.max(this.homeValue.Borrowable - this.homeValue.Mort2Amount, 0);
    }
    return Hvc.homeValue.Equity;
  }

});


// Navigation:

Object.extend(controller, {
  stepSections: ['sectionGetHV', 'sectionGetEquity', 'sectionGetPayments'],
  stepValidations: [null, null, null, null],
  stepImages: ['imageCHVStep1', 'imageCHVStep1A', 'imageCHVStep1B', null, 'imageCHVStep2', 'imageCHVStep2A', null, null, 'imageCHVStep3'],
  currStepName: "", 
  largestValidStep: "",
  initStepSections: function () {
    for (var i = 0; i < this.stepSections.length; i++) 
      this.stepSections[this.stepSections[i]] = i; // make double-associative
  },
  startPage: function () {
    this.initStepSections();
    this.currStepName = this.stepSections[0];
    this.largestValidStep = this.currStepName;
    Hvc.homeValue = null; // this will reset the progress summary
    this.showSection(this.currStepName);
  },
  showSection: function (sectionName) {
    for (var i = 0; i < this.stepSections.length; i++) {
      var el = $(this.stepSections[i]), img = $(this.stepImages[i]);
      if (el) 
        el.style.display = this.stepSections[i] == sectionName ? 'block' : 'none';
    }
    this.updateStepNavigator();
    //this.showProgressSummary();
    ProductDescriptionPopup.closeAllVisibleDialogs();
  },
  updateStepNavigator: function () {
    var posCurr = this.stepSections[this.currStepName], // get positions
        posValid = this.stepSections[this.largestValidStep];
    for (var i = 0; i < this.stepImages.length; i++) {
      var img = $(this.stepImages[i]),
          posImg = posCurr * this.stepSections.length + posValid; // this works as long as 0 <= posValid - posCurr <= 1
      if (img)
        img.style.display = posImg == i ? 'block' : 'none';
    }
  },
  showProgressSummary: function () {
    var showSummary = function (html) {
      var el = document.getElementById('SummaryArea');
      if (el) el.innerHTML = html;
      AjaxBase.executeScripts(html);
    }
    var url = 'AjaxSummary.aspx?'+ Math.random(1000) + (Hvc.homeValue ? '' : '&reset=true');
    AjaxBase.getHTML(url, 'GET', null, showSummary);
  },
  jumpToSection: function (nextSectionName) { // used for going back with alternate navigation (header menu)
    var pos = this.stepSections[nextSectionName];
    if (pos >= 0) {
      this.currStepName = nextSectionName;
      this.showSection(this.stepSections[pos]);
    }
    return false;
  },
  goToNextSection: function (currSectionName, dir) {
    if (!currSectionName)
      currSectionName = this.currStepName ? this.currStepName : this.stepSections[0];
    try {
      dir = (dir < 0) ? -1 : 1; // dir is optional, default = +1
      var currPos = this.stepSections[currSectionName];
      var nextPos = currPos + dir; 
      // Put special navigation logic here (adjust dir accordingly) 
      if ((dir < 0) || !this.stepValidations[currPos] || this.stepValidations[currPos]()) {
        if (this.stepSections[nextPos]) {
          this.currStepName = this.stepSections[nextPos];
          if (nextPos > this.stepSections[this.largestValidStep]) 
            this.largestValidStep = this.stepSections[nextPos];
          this.showSection(this.stepSections[nextPos]);
          //this.updateStepNavigator();
          //this.showProgressSummary();
        }
        return false;
      }
      else
        return false;
    }
    catch (ex) {
      alert('An error occurred: ' + ex.description);
      return false;
    }
  },
  goToPrevSection: function (currSectionName) {
    return this.goToNextSection(currSectionName, -1);
  }
});

// Step 1:

var AjaxHV = {
  url: 'AjaxHV.aspx', 
  method: 'POST', 
  focusElement: 'buttonCHAStep1', // set focus to this element
  anchor: '', //'anchorHV', // anchor to where to scroll page
  errorClass: 'ErrorMessage',
  idWait: 'sectionHVWait', // element to show loading animation
  idFill: 'sectionShowHV', // element to receive html
  idShow: 'sectionShowHV', // element to show
  getWaitElement: function () {
    return this._elWait || (this._elWait = $(this.idWait));
  },
  getFocusElement: function () {
    return $(this.focusElement);
  },
  getFillElement: function () {
    return this._elFill || (this._elFill = $(this.idFill));
  },
  getShowElement: function () {
    return this._elShow || (this._elShow = $(this.idShow));
  }
}

function processHomeValue(form) {
  var args1 = {IsValid: false}, args2 = {IsValid: false}, 
      args3 = {IsValid: false}, args4 = {IsValid: false};
  validateCityStateOrZipSpecified(null, args1);
  validateAddressSpecified(null, args2);
  validateHomeImproveYear(null, args3);
  validateHomeImproveAmount(null, args4);
  if (args1.IsValid && args2.IsValid && args3.IsValid && args4.IsValid) {
    try {
      var qData = Form.serialize(form);
      clearHomeValueArea();
      clearEquityArea();
      clearEquityForm();
      showHVWait();
      with (AjaxHV) AjaxBase.getHTML(url, method, qData, showHomeValueHTML, showHomeValueError);
      handled = true;
    }
    catch (ex) {
      // TO DO: error handling
      //alert(ex.description);
    }
  }
  return false;
}

function clearHomeValueArea() {
    fillElement(AjaxHV.getFillElement(), "");
}

function showSaleInfoArea(isVisible) { // set isVisible to null to set back to default
  var elSaleInfoArea = $('SaleInfoArea');
  if (elSaleInfoArea) {
    elSaleInfoArea.style.display = isVisible == true ? 'block' : isVisible == false ? 'none' : ''; 
    if (isVisible == false) {
      var elSalePrice = $('SalePrice'), elSaleDate = $('SaleDate');
      if (elSalePrice) elSalePrice.value = '';
      if (elSaleDate) elSaleDate.value = '';
    }
  }
}

function clearAllHomeValueAreas() {
  clearHomeValueArea();
  showSaleInfoArea();
}

function clearEntireApplication(){
  var forms = document.getElementsByTagName('form');
  for (var i = 0; i < forms.length; i++)
    forms[i].reset();
  clearHomeValueArea();
  clearEquityArea();
  clearEquityForm();
  showSaleInfoArea(null);
  Hvc.homeValue = null; // resets side-bar
  controller.showProgressSummary();
}

// Address Validation
function validateAddressSpecified (source, args) {
  if (Hvc.controls) {
    if (!source)
      source = Hvc.controls.Address1;
    if (!args)
      args = new Object();
    args.IsValid = (source && source.value);
    // TO DO: remove if we get validators working:
    var el = Hvc.controls.Address1Validator;
    if (el)
        el.style.visibility = args.IsValid ? 'hidden' : 'visible';
  }
}

// CityStatOrZip Validation
function validateCityStateOrZipSpecified (source, args) {
  if (Hvc.controls) {
    if (!source)
      source = Hvc.controls.CityStateOrZip;
    if (!args)
      args = new Object();
    if (source && source.value) {
      args.IsValid = isValidUSZipCode(source.value) || isValidCityState(source.value);
    }
    else
      args.IsValid = false;
    // TO DO: remove if we get validators working:
    var el = Hvc.controls.CityStateOrZipValidator;
    if (el)
        el.style.visibility = args.IsValid ? 'hidden' : 'visible';
  }
}

// Home Improvement Year Validation
function validateHomeImproveYear (source, args) {
  if (Hvc.controls) {
    if (!source)
      source = Hvc.controls.ImproveYear;
    if (!args)
      args = new Object();
    if (Hvc.controls.ImproveCost && Hvc.controls.ImproveCost.value)
      args.IsValid = source.value.length == 2 || source.value.length == 4
        && isAllNumericCharacters(source.value)
        && parseInt(source.value) <= (new Date()).getFullYear();
    else
      args.IsValid = true;
    // TO DO: remove if we get validators working:
    var el = Hvc.controls.ImproveYearValidator;
    if (el)
        el.style.display = args.IsValid ? 'none' : 'inline';
  }
}

// Home Improvement Year Validation
function validateHomeImproveAmount (source, args) {
  if (Hvc.controls) {
    if (!args)
      args = new Object();
    if (Hvc.controls.ImproveCost && Hvc.controls.ImproveCost.value) {
      var val = Hvc.unformatDollars(Hvc.controls.ImproveCost.value);
      args.IsValid = isFinite(val) && val <= Hvc.lender.HomeImproveMax;
      if (args.IsValid) Hvc.controls.ImproveCost.value = val;
    }
    else
      args.IsValid = true;
    // TO DO: remove if we get validators working:
    var el = Hvc.controls.ImproveCostValidator;
    if (el)
        el.style.display = args.IsValid ? 'none' : 'inline';
  }
}

function showHVWait() {
  AjaxHV.getWaitElement().style.display = 'block';
}

function hideHVWait() {
  AjaxHV.getWaitElement().style.display = 'none';
}

function connectProductOnChangeHandler() {
  var radios = document.getElementsByName('productId');
  for (var i = 0; i < radios.length; i++)
    radios[i].onchange = clearEquityArea;
}

function showHomeValueHTML(html) {
  var success = true;
  Hvc.homeValue = null; // clear old values!
  hideHVWait();
  AjaxHV.getFillElement().style.visibility = 'hidden';
  fillElement(AjaxHV.getFillElement(), html);
  AjaxBase.executeScripts(html); // declares Hvc.homeValue, etc.
  //scrollToAnchor(AjaxHV.anchor);
  showSaleInfoArea(Hvc.showSaleInfo);
  success = Hvc.homeValue.HvbHiValue > 0; // successfully got a value!
  shadeInElement(AjaxHV.getShowElement());
  Hvc.controls.EquityButton.disabled = !(Hvc.products && Hvc.products.length > 0);
  connectProductOnChangeHandler();
  controller.largestValidStep = success ? 'sectionGetEquity' : 'sectionGetHV';
  controller.updateStepNavigator();
  controller.showProgressSummary();
  setFocus(AjaxHV.getFocusElement());
  scrollToAnchor($('containerShowHV'), true);
}

function showHomeValueError(error) {
  hideHVWait();
  injectError(error, AjaxHV.getFillElement(), AjaxHV.errorClass);
  //scrollToAnchor(AjaxHV.anchor); 
  shadeInElement(AjaxHV.getShowElement());
  setFocus(AjaxHV.getFocusElement());
  scrollToAnchor($('containerShowHV'), true);
}


// Step 2:

var AjaxEquity = {
  focusElement: 'buttonCHAStep2', // set focus to this element
  anchor: '', //'anchorEquity', // anchor to where to scroll page
  method: 'POST',
  errorClass: 'ErrorMessage',
  idFill: 'sectionEquity', // element to receive html
  idShow: 'sectionShowEquity', // element to show
  getFillElement: function () {
    return this._elFill || (this._elFill = $(this.idFill));
  },
  getShowElement: function () {
    return this._elShow || (this._elShow = $(this.idShow));
  },
  getFocusElement: function () {
    return $(this.focusElement);
  },
  getUrl: function () {
    // Compass-specific logic:
    if (Hvc.products && Hvc.products.selectedProduct && Hvc.products.selectedProduct.ProductCode == 'EO')
      return "AjaxEORates.aspx";
    else
      return "AjaxPayments.aspx";
  }
}

function processEquity(form) {
  var args1 = {IsValid: false}, args2 = {IsValid: false}, args3 = {IsValid: false};
  validateProductSelected(null, args1);
  validateMortgage1(null, args2);
  validateMortgage2(null, args3);
  if (args1.IsValid && args2.IsValid && args3.IsValid) {
    Hvc.products.selectedProduct = Hvc.findProduct(getProductSelectedValue()); 
    processLoanType(form);
  }
  return false;
}

function clearEquityArea() {
  AjaxEquity.getShowElement().style.visibility = 'hidden';
}

function clearEquityForm() {
  var equityForm = $('formEquity');
  if (equityForm) equityForm.reset();
}

function getProductSelectedValue () {
  var radios = document.getElementsByName('productId'),
      value = null;
  for (var i = 0; i < radios.length && !value; i++)
    if (radios[i].checked) value = radios[i].value;
  return value;
}

function validateProductSelected (source, args) {
  if (!args)
    args = new Object();
  var value = getProductSelectedValue();
  args.IsValid = value != null;
  // TO DO: remove if we get validators working:
  var el = Hvc.controls.ProductValidator;
  if (el)
      el.style.display = args.IsValid ? 'none' : 'block';
}

function validateMortgage1 (source, args) {
  if (Hvc.controls) {
    if (!source)
      source = Hvc.controls.MortgageBalance;
    if (!args)
      args = new Object();
    if (source)
      args.IsValid = source.value && isUSDollars(source.value);
    // TO DO: remove if we get validators working:
    var el = Hvc.controls.Mortgage1Validator;
    if (el)
        el.style.visibility = args.IsValid ? 'hidden' : 'visible';
  }
}

function validateMortgage2 (source, args) {
  if (Hvc.controls) {
    if (!source)
      source = Hvc.controls.SecondMortgageBalance;
     if (!args)
      args = new Object();
   if (source)
      args.IsValid = !source.value || isUSDollars(source.value);
    // TO DO: remove if we get validators working:
    var el = Hvc.controls.Mortgage2Validator;
    if (el)
        el.style.visibility = args.IsValid ? 'hidden' : 'visible';
  }
}

function showDescriptionPopup(source, popupId) {
  var el = $(popupId);
  if (el) {
    if (!Hvc.descriptionPopupDialog) {
      Hvc.descriptionPopupDialog = new ProductDescriptionPopup($('divDescriptionPopupDialog'), $('divDescriptionPopupTitle'), 
        $('divDescriptionPopupTitle'), [$('imgDescriptionPopupClose')], source, $('divDescriptionPopupPlaceholder'), 
        'images/clear.gif', $('divDescriptionPopupShadow'));
      Hvc.descriptionPopupDialog.isReusable = true;
    }
    with (Hvc.descriptionPopupDialog) {
      srcElement = source;
      show(el.innerHTML);
    }
  }
}

function processLoanType(form) {
  if (!form) form = $('formLoanType');
  // display loading message 
  Hvc.controls.sectionShowEquity.style.visibility = 'hidden';
  Element.show(Hvc.controls.sectionEquityWait);
  // use AJAX to get html for last page (and JSON)
  var equity = Hvc.calcEquity(), // for storing in the back-end
      cashBack = Hvc.homeValue.CashBack;
  var elEquityCalc = Hvc.controls.EquityCalculated,
      elCashBack = Hvc.controls.CashBackCalculated,
      elLocationCode = Hvc.controls.LocationCode;
  if (elEquityCalc) elEquityCalc.value = equity;
  if (elCashBack) elCashBack.value = cashBack;
  if (elLocationCode) elLocationCode.value = Hvc.homeAddress.StdState;
  var qData = Form.serialize(form);
  with (AjaxEquity) AjaxBase.getHTML(getUrl(), method, qData, showPayments, showPaymentsError);
  return false;
}

// Step 4:

var AjaxPayments = {
  anchor: '', //'_', // anchor to where to scroll page
  method: 'POST',
  errorClass: 'ErrorMessage',
  idFill: 'sectionPayments', // element to receive html
  getFillElement: function () {
    return this._elFill || (this._elFill = $(this.idFill));
  }
}


function showPayments(html) {
  var el = AjaxPayments.getFillElement();
  fillElement(el, html);
  // clear any Hvc special handlers
  Hvc.step3Handlers = {};
  // this next line should declare Hvc.rates, Hvc.periods, and Hvc.sliderParams
  AjaxBase.executeScripts(html);
  if (typeof Hvc.rates == 'undefined') throw "Rates not found!";
  if (typeof Hvc.sliderParams == 'undefined') throw "Slider Params not found!";
  if (typeof Hvc.periods == 'undefined') throw "Periods not found!";
  // show equity
  Element.hide(Hvc.controls.sectionEquityWait);
  if (Hvc.step3Handlers.onCalcBorrowable) Hvc.step3Handlers.onCalcBorrowable();
  var equity = Hvc.homeValue.Equity, 
      cashBack = Hvc.homeValue.CashBack,
      borrow = Hvc.homeValue.Borrowable,
      minToBorrow = Hvc.products.selectedProduct.AmountMin,
      elButton = $('sectionShowEquityButton'),
      elError = $('sectionShowEquityError');
  fillElement(AjaxEquity.getFillElement(), Hvc.formatDollars(borrow, false));
  fillElement($('sectionCashBack'), Hvc.formatDollars(cashBack, false));
  if (borrow >= minToBorrow && cashBack > 0) {
    if (elButton) Element.show(elButton);
    if (elError) Element.hide(elError);
    controller.largestValidStep = 'sectionGetPayments'; 
  }
  else {
    if (elButton) Element.hide(elButton);
    if (elError) Element.show(elError);
    controller.largestValidStep = 'sectionGetEquity'; 
  }
  //scrollToAnchor(AjaxEquity.anchor);
  shadeInElement(AjaxEquity.getShowElement());
  controller.updateStepNavigator();
  controller.showProgressSummary();
  setFocus(AjaxEquity.getFocusElement());
  scrollToAnchor($('containerShowEquity'), true);
  // render slider
  var params = Hvc.params = new SliderParams(Hvc.sliderParams);
  if (Hvc.products && Hvc.products.selectedProduct) {
    // Compute min and max amount on slider
    params.minAmount = Math.max(Hvc.homeValue.Mort2Amount || 0, Hvc.products.selectedProduct.AmountMin || 0);
    params.maxAmount = Math.min(Hvc.homeValue.Borrowable, Hvc.products.selectedProduct.AmountMax || Number.MAX_VALUE);
  }
  if (Hvc.step3Handlers.beforeCreateSlider) Hvc.step3Handlers.beforeCreateSlider();
  var slider = new Slider('sliderBackground', 'sliderRates', 'sliderMain', 'sliderAxisLabel',
    'sliderArrow', Hvc);
  slider.formatDollars = Hvc.formatDollars.bind(Hvc);
  slider.formatRate = Hvc.formatRate.bind(Hvc);
  slider.findBestRate = Hvc.findOptimalRate.bind(Hvc);
  slider.getRateRange = Hvc.getRateRange.bind(Hvc);
  slider.render(); // Note: render() alters params.maxAmount and params.minAmount -> TO DO: change this somehow?
  Hvc.slider = slider;
  // attach onchange events to input boxes
  var input = $('textLoanAmount'), 
      btnUpdate = $('btnUpdateSlider');
  input.onkeypress = updateSliderFromAmount.bindAsEventListener(input);
  //input.onchange = input.onkeypress; // JMH: removed since new button
  if (btnUpdate) btnUpdate.onclick = input.onkeypress;
  // Initialize text box and grid
  if (Hvc.periods.defValue == 'auto') {
    if (Hvc.homeValue.Mort2Amount > 0)
      input.value = Math.round(Hvc.homeValue.Mort2Amount);
    else if (Hvc.slider.bestRate)
      input.value = Hvc.slider.bestRate.sliderMax;
  }
  else
    input.value = Math.round(Hvc.periods.defValue * params.maxAmount / Hvc.periods.precision) * Hvc.periods.precision;
  if (Hvc.step3Handlers.onInitialize) Hvc.step3Handlers.onInitialize();
  updateSliderFromAmount();
}

function showPaymentsError(error) {
  Element.hide(Hvc.controls.sectionEquityWait);
  // TO DO: this never shows to user:
  injectError(error, AjaxEquity.getFillElement(), AjaxEquity.errorClass);
  //scrollToAnchor(AjaxEquity.anchor); 
  shadeInElement(AjaxEquity.getShowElement());
  setFocus(AjaxEquity.getFocusElement());
  scrollToAnchor($('containerShowEquity'), true);
}

function HideTemporalValidator () {
  Floater.hide(this);
  this.showTimeout = null;
}

function updateSliderFromAmount(event) {
  //event = event || window.event;
  if (event) {
    var key = event.keyCode || event.charCode;
    if (key > 0 && key != 13) // can be called by keypress or change
      return true;
    else
      Event.stop(event); // stop form submittal by Enter (#13)
  }
  var target = $('textLoanAmount');
  var params = Hvc.params;
  var amount = Hvc.unformatDollars(target.value), userAmount = amount;
  amount = isNaN(amount) 
    ? params.minAmount 
    : Math.min(Math.max(Math.round(amount, 0), params.minAmount), params.maxAmount);
  var validator = $('LoanAmountValidator');
  if (validator && validator.showTimeout) {
    clearTimeout(validator.showTimeout);
    validator.showTimeout = null;
  }
  if (validator && userAmount != amount) {
    Floater.show(validator);
    validator.showTimeout = setTimeout(HideTemporalValidator.bind(validator), 5000);
  }
  target.value = amount;
  Hvc.slider.moveArrow(amount);
  updateCashBack(amount);
  updateResultsGrid(amount);
  if (Hvc.step3Handlers.onUpdateAmount) Hvc.step3Handlers.onUpdateAmount(amount);
}

function updateCashBack(lineAmount) {
  var el = $('textCashBack');
  if (el)
    el.innerHTML = Hvc.formatDollars(Math.max(lineAmount - (Hvc.homeValue.Mort2Amount || 0), 0), true);
}

function updateResultsGrid(amount) {
  if (Hvc.step3Handlers.updateResultsGrid) { // override
    return Hvc.step3Handlers.updateResultsGrid(amount);
  }
  else
    return updateLoanResultsGrid(amount);
}

function updateLoanResultsGrid(amount) {
  var grid = $('tableResults');
  if (grid && Hvc.periods && Hvc.periods.periods) {
    for (var i = 0; i < Hvc.periods.periods.length; i++) {
      var year = Hvc.periods.periods[i];
      var rateObj = Hvc.findFirstRate(Hvc.rates, amount, year);
      var row = $('trResult' + i);
      var button = $('buttonApply' + i);
      button.payment = null;
      if (rateObj && rateObj.PaymentPer1000[year]) {
        button.payment = rateObj.PaymentPer1000[year] * amount / 1000.0;
        if (Hvc.products.selectedProduct && Hvc.products.selectedProduct.PaymentMin)
          button.payment = Math.max(Hvc.products.selectedProduct.PaymentMin, button.payment);
      }
      var rate = rateObj ? Hvc.formatPercent(rateObj.Rate, 2, true) : null;
      button.rateDesc = rateObj ? Hvc.formatRate(rateObj) : null;
      button.term = year;
      var paymentFmt = button.payment ? Hvc.formatDollars(button.payment, true) : null;
      button.rateObj = rateObj;
      button.loanAmount = amount;
      button.drawAmount = null;
      button.disabled = !rateObj;
      if (button.disabled) {
        Element.setCursor(button, 'default');
        Element.setOpacity(button, 0.5);
        button.onclick = function () {return false;}
      }
      else {
        Element.setCursor(button, 'pointer');
        Element.setOpacity(button, 1.0);
        button.onclick = doApplyForLoan.bind(button);
      }
      // set span data
      var spans = row.getElementsByTagName('SPAN');
      for (var s = 0; s < spans.length; s++) {
        var output = 'N/A';
        var className = spans[s].className;
        if (className.indexOf('paymentTarget') >= 0)
          output = paymentFmt || output;
        else if (className.indexOf('rateTarget') >= 0)
          output = rate || output;
        else if (className.indexOf('periodTarget') >= 0)
          output = year;
        spans[s].innerHTML = output;
      }
      if (Browser.isIE) 
        row.style.display = 'block';
      else
        row.style.display = 'table-row';
    } // for (var i = 0; i < Hvc.periods.periods.length; i++)
  }
}

// Step 4:

function processLoan(form) {
  return false; // nothing to do here
}

function doApplyForLoan() {
  var button = this,
      elAmount = $('textLoanAmount'),
      amount = button.loanAmount,
      ltvHi = 0,
      ltvLow = 0,
      equityHi = 0,
      equityLow = 0;
  if (elAmount && elAmount.value >= 0 && Hvc.homeValue.Mort1Amount >= 0) {
    ltvLow = Math.min(1.0, (amount + Hvc.homeValue.Mort1Amount) / Hvc.homeValue.HvbLowValue);
    ltvHi = Math.min(1.0, (amount + Hvc.homeValue.Mort1Amount) / Hvc.homeValue.HvbHiValue);
    equityLow = Hvc.homeValue.HvbLowValue - Hvc.homeValue.Mort1Amount - (Hvc.homeValue.Mort2Amount || 0);
    equityHi = Hvc.homeValue.HvbHiValue - Hvc.homeValue.Mort1Amount - (Hvc.homeValue.Mort2Amount || 0);
  }
  // build url
  var href = 'Apply.aspx';
  var rateObj = button.rateObj,
      payment = button.payment,
      rateDesc = button.rateDesc,
      term = button.term,
      draw = button.drawAmount;
  if (rateObj)
    href = Url.appendParams(href, "Payment", payment, 
      "RateId", rateObj.RateId, "RatePct", rateObj.Rate,
      "RateDesc", rateDesc, "Term", term,
      "EquityLow", equityLow, "EquityHi", equityHi,
      "LtvLow", ltvLow, "LtvHi", ltvHi,
      "textLoanAmount", amount, "textDrawAmount", draw);
  window.open(href, "applywindow");
  return false;
}

function showPrintingPopup(source) {
  if (!Hvc.printingPopupDialog) {
    Hvc.printingPopupDialog = new PrintingPopup($('divPrintingPopupDialog'), $('divPrintingPopupTitle'), 
      $('divPrintingPopupTitle'), [$('imgPrintingPopupClose'), $('btnPrintingPopupClose')], source, $('divPrintingPopupPlaceholder'), 
      'images/clear.gif', $('divPrintingPopupShadow'));
    Hvc.printingPopupDialog.isReusable = true;
  }
  with (Hvc.printingPopupDialog) {
    srcElement = source;
    show();
  }
}

// Other functions

function shadeInElement(el) {
  new ShadeEffect(el);
}

function scrollToAnchor(anchor, ifNecessary) {
  if (ifNecessary) {
    var el = $(anchor),
        body = document.documentElement || document.body;
    if (el)  { 
      var posEl = Floater.getClientPos(el),
          posBody = Page.getClientSize();
          if (posEl.Y < 0)
        body.scrollTop += posEl.Y;
      else if (posEl.X < 0)
        body.scrollLeft += posEl.X;
      else if (posEl.Y + el.offsetHeight > posBody.height)
        body.scrollTop += posEl.Y + el.offsetHeight - posBody.height;
      else if (posEl.X + el.offsetWidth > posBody.width)
        body.scrollLeft += posEl.X + el.offsetWidth - posBody.width;
    }
  } 
  else if (anchor) window.location.href = '#' + anchor;
}

function setFocus(el) {
  if (el) 
    try {
      el.focus();
    }
    catch (ex) {}
}

function injectError(msg, element, className) {
  var el = $(element);
  var span = document.createElement('SPAN');
  span.innerHTML = msg;
  span.className = className;
  el.appendChild(span);
}

function injectLoadingAnimation(element) {
  var img = new Image(), el = $(element);
  fillElement(el, "");
  img.src = "images/loading.gif"; // TO DO: don't hard-code
  el.appendChild(img);
}

function fillElement(element, html) {
  element = $(element);
  if (element) element.innerHTML = html;
  if (element && html == '') { // IE does not remove everything, it leaves blank lines
    for (var i = 0; i < element.childNodes.length; i++)
      element.removeChild(element.childNodes[i]);
  }
}

// Shade-in

/* From Effects.js */
String.prototype.camelize = function() {
  var strings = this.split('-');
  var camelizedString;
  if (strings.length == 1) 
    camelizedString = strings[0];
  else {
    strings[0] = this.charAt(0) == '-'
      ? strings[0].charAt(0).toUpperCase() + strings[0].substring(1)
      : strings[0];
    for (var i = 1; i < strings.length; i++) {
      var s = strings[i];
      strings[i] = s.charAt(0).toUpperCase() + s.substring(1);
    }
    camelizedString = strings.join('');
  }
  return camelizedString;
}
  
Object.extend(Element, {
  setStyle: function(element, style) {
    element = $(element);
    for(k in style) element.style[k.camelize()] = style[k];
  }
});

var ShadeOptions = { // defaults
  delayedStart: false,
  duration: 0.5, // seconds
  interval: 0.03, // seconds
  startStyles: {visibility: 'visible'}
};

var ShadeEffect = Class.create();
ShadeEffect.prototype.extend({
  initialize: function (element, options) {
    this._element = element;
    this.extend(ShadeOptions); // set defaults
    this.extend(options || {});
    if (!this.delayedStart) this.start();
  },
  start: function () {
    this._origHeight = this._getHeight();
    this._origTop = this._getTop(); 
    this._setTop(-this._origHeight);
    Element.setStyle(this._element, this.startStyles);
    Element.setOpacity(this._element, 0.0);
    this._startTime = new Date();
    this._thread = setInterval(this.loop.bind(this), this.interval * 1000);
  },
  finish: function () {
    this._setTop(this._origTop);
    this._setOpacity(Browser.isFF10 ? 0.9999 : 1.0); // don't use 1.0 since it causes FF 1.0 to flicker
  },
  loop: function () {
    var delta = ((new Date()).getTime() - this._startTime.getTime()) / this.duration / 1000.0;
    if (delta >= 0.9999)
      this.stop();
    else {
      this._setTop(this._origHeight * (delta - 1));
      this._setOpacity(Math.min(delta, 0.9999)); 
    }
  },
  stop: function () {
    clearInterval(this._thread);
    this.finish();
  },
  _setTop: function (top) { 
    this._element.style.top = Element.intToPx(top);
  },
  _getTop: function () { 
    return this._element.offsetTop; 
  },
  _setHeight: function (height) { 
    this._element.style.height = Element.intToPx(height);
  },
  _getHeight: function () { 
    return this._element.offsetHeight; 
  },
  _setOpacity: function (factor) {
    var opacity = factor * factor * factor; // stays transparent longer
    Element.setOpacity(this._element, opacity);
  }
});

/* class ProductDescriptionPopup */
var ProductDescriptionPopup = Class.create();
Object.extend(ProductDescriptionPopup, {
  addVisibleDialog: function (dialog) {
    if (!this.firstDialog) {
      this.firstDialog = dialog;
      this.lastDialog = dialog;
      dialog._nextDialog = null;
      dialog._prevDialog = null;
    }
    else if (!dialog._isInDialogList) { // not already added
      this.lastDialog._nextDialog = dialog;
      dialog._prevDialog = this.lastDialog;
      dialog._nextDialog = null;
      this.lastDialog = dialog;
    }
    dialog._isInDialogList = true;
  },
  removeVisibleDialog: function (dialog) {
    if (dialog == this.firstDialog)
      this.firstDialog = dialog._nextDialog;
    if (dialog == this.lastDialog)
      this.lastDialog = dialog._prevDialog;
    if (dialog._nextDialog) dialog._nextDialog._prevDialog = dialog._prevDialog;
    if (dialog._prevDialog) dialog._prevDialog._nextDialog = dialog._nextDialog;      
    dialog._nextDialog = dialog._prevDialog = null;
    dialog._isInDialogList = false;
  },
  closeAllVisibleDialogs: function () {
    var dialog;
    while (dialog = this.firstDialog) // yes, this is an assignment, not an equality!
      dialog.hide(); // calls removeVisibleDialog
  }
});
//Object.extend(ProductDescriptionPopup.prototype, ModalDialog.prototype);
Object.extend(ProductDescriptionPopup.prototype, DraggableDialog.prototype);
Object.extend(ProductDescriptionPopup.prototype, MacZoomDialog.prototype);
Object.extend(ProductDescriptionPopup.prototype, {
  initialize: function (draggedElement, grabElement, titleElement, cancelElements, srcElement, placeholderElement, loadingImage, shadowElement) {
    this.loadingImage = loadingImage;
    this.placeholderElement = placeholderElement;
    this._maxTop = Element.pxToInt(Element.findStyleProperty(draggedElement, 'top'));
    //ModalDialog.prototype.initialize.call(this, draggedElement, null, 0.2, 'silver');
    DraggableDialog.prototype.initialize.call(this, draggedElement, grabElement, titleElement, [], cancelElements, shadowElement);
    MacZoomDialog.prototype.initialize.call(this, draggedElement, titleElement, [], cancelElements, srcElement, loadingImage, shadowElement);
  },
  show: function (html) {
    //ProductDescriptionPopup.addVisibleDialog(this);
    MacZoomDialog.prototype.show.call(this);
    if (html) this.placeholderElement.innerHTML = html;
  },
  hide: function () {
    MacZoomDialog.prototype.hide.call(this);
    //ModalDialog.prototype.hide.call(this);
    //ProductDescriptionPopup.removeVisibleDialog(this);
  },
  showDialog: function () {
    ProductDescriptionPopup.addVisibleDialog(this);
    MacZoomDialog.prototype.showDialog.call(this);
  },
  hideDialog: function () {
    MacZoomDialog.prototype.hideDialog.call(this);
    ProductDescriptionPopup.removeVisibleDialog(this);
/*  },
  _moveElementTo: function (x, y) {
    if (Browser.isIE)
      y = Math.max(y, this._maxTop); // don't go above drop-downs (IE bug)
    Floater.setOffset(this.draggedElement, x, y);
*/  }
});
ProductDescriptionPopup.prototype.constructor = ProductDescriptionPopup;

/* Printing popup */
var PrintingPopup = Class.create();
Object.extend(PrintingPopup.prototype, ProductDescriptionPopup.prototype);
PrintingPopup.prototype.constructor = PrintingPopup;

// Link tracking

function addLinkTrackingInfo (baseUrl, linkUrl) {
  var params = '';
  baseUrl = Url.appendParams(baseUrl, 'Link', linkUrl);
  if (Hvc.lender)
    baseUrl = Url.appendParams(baseUrl, 'LenderId', Hvc.lender.ID);
  if (Hvc.homeValue)
    baseUrl = Url.appendParams(baseUrl, 'RunID', Hvc.homeValue.RunID);
  return baseUrl;
}

function trackLink(link) {
  link.href = addLinkTrackingInfo('Click.aspx', link.href);
  return true;
}
