/*
  Common (parent) class for the pricing page classes.

  Each class controls a set of UI components (identified by their ID)
  prefixed by its "_prefix" property (e.g., SimpleQuote class has
  "_prefix" set to "Simple", meaning that it can control components with
  id like "Simple_div_price").  UI components controlled by each class are
  organized as a hierachical map property using "_" in the id as the separator
  (excluding the prefix part); e.g., the DOM node for a UI element identified
  by "Simple_div_price" can be accessed as "this.div.price" in the code in
  SimpleQuote class, and "this.select.rmsid" in AdvQuote class refers to
  "Adv_select_rmsid" UI component (as a convention, the second part of the ID
  usually is the component type (e.g., "div", "text", "select", etc.)).  This
  mapping is set up by "_buildComponents()" that takes an array of subnames
  separated by "#" ('#' is used there instead of '_' to avoid javascript
  obfuscators from getting confused).

  Each component DOM node under control has "_quoteUI" property that links
  back to the controlling class (e.g., "this.div.price._quoteUI" is equal to
  "this" when "this" is SimpleQuote).

  Constructor is given the current 'pageState'. 'pageState' has 'init'
  property set to true when the page is loaded the first time (not 'back').

  Each UI can be initialized by calling 'initializeFromContract' method
  that takes a map (JS Object) 'contract' with any of the following properties:
    startDate ("mm/dd/yy" format) or ('s_mo', 's_dy', 's_yr') as in pricing
    endDate ("mm/dd/yy" format) or ('e_mo', 'e_dy', 'e_yr') as in pricing
    week
    rmsid
    cap
    dv
    dv_t
    dvop
    dvop_type
    tick
    strike
    meas

  Only those properties defined in the 'contract' map are initialized.
  Note that a field is initialized as if its value were set by the user; e.g.,
  if endDate is initialized, then the location pane is opened.
  Out-of-range values for startDate/endDate are auto-adjusted.
*/

/* set to true for AJAX pricing; false for location change */
var SUBMITASYNC = false;

/* the latest permitted date when contracts can begin */
var MAX_FUTURE_STARTDATE = 185;

/* rainy events permitted up to one year in advance. */
var MAX_RAINY_EVENT_FUTURE_STARTDATE = 365;

/* the last permitted date for endDate, starting from startDate */
var MAX_CONTRACT_LENGTH = 185;

/* start date shift for snow contracts */
var SNOW_STARTDAY_SHIFT = 10;

/* default value for cap (must be less than true MAXCAP) */
var DEFAULT_CAP = 25000;

/* number of years to be displayed in the historic payout table */
var HIST_PAYOUT_YEARS = 30;
function CommonQuote(config) {
  /* page state map */

  this._pageState = config.pageState;

  /* if set to true, then an alert box is displayed on pricing */

  this._isDebug = config.isDebug;

  /* Quote object (defined in Quote.js) used to price */

  this._quote = config.quote;

  /* popup calendar used */

  this._calendarPopup = config.calendarPopup;

  /* google map popup used */

  this._mapPopup = config.mapPopup;

  /* URL called to get the price */

  this._getPriceURL = config.getPriceURL;

  /* URL called to "buy now" */

  this._buyNowURL = config.buyNowURL;

  /* currency */
  this._currency = config.currency;

  /* country */
  this._country = config.country;


  /* sets temp unit by name */

  this._setTempUnit = function(name) {
    for (var i = 0; i < this._units.temp.length; i ++) {
      var def = this._units.temp[i];
      if (def.name == name) {
        this._tempUnit = def;
        UNITS.defaultTemp = def.name;
      }
    }
  };

  /* sets precip unit by name */

  this._setPrecipUnit = function(name) {
    for (var i = 0; i < this._units.precip.length; i ++) {
      var def = this._units.precip[i];
      if (def.name == name) {
        this._precipUnit = def;
        UNITS.defaultPrecip = def.name;
      }
    }
  };

  /* sets snowfall unit by name */

  this._setSnowfallUnit = function(name) {
    for (var i = 0; i < this._units.snowfall.length; i ++) {
      var def = this._units.snowfall[i];
      if (def.name == name) {
        this._snowfallUnit = def;
        UNITS.defaultSnowfall = def.name;
      }
    }
  };
  /* sets units supported */

  this._setUnits = function(units) {
    this._units = config.units;
    this._setTempUnit(units.defaultTemp);
    this._setPrecipUnit(units.defaultPrecip);
    this._setSnowfallUnit(units.defaultSnowfall);
  };

  this._setUnits(config.units);

  /* state of the UI (for ajax); 'idle', 'ready', 'invalid', 'priced' */

  this._state = 'idle';

  /* price to be displayed */

  this._price = '';

  /* contract id to be displayed */

  this._contractId = '';

  /* set to true if end date is selected */

  this._isEndDateSelected = false;

  /* sets debug mode */

  this.setDebug = function(isDebug) {
    this._isDebug = isDebug;
  };

  /* toggles _isDebug */

  this.toggleDebug = function() {
    this.setDebug(!(this._isDebug));
  };

  /* parses float, removing commas */

  this.parseFloat = function(str) {
    if ((typeof str) == 'string') {
      str = str.replace(/,/g, '');
    }

    return parseFloat(str);
  }

  /* formats number as Dollar */

  this.formatDollar = function(value) {
    value = this.parseFloat(value);
    var sign = (value < 0) ? '-' : '';
    var hundreds = parseInt(Math.round(Math.abs(value) * 100));
    var integral = parseInt(hundreds / 100);
    var fraction = (new String(hundreds % 100 + 100)).substring(1);

    var withComma = '';

    while (integral >= 1000) {
      withComma = ',' + (new String(integral % 1000 + 1000)).substring(1) +
                  withComma;
      integral = parseInt(integral / 1000);
    }

    return sign + integral + withComma + '.' + fraction;
  }

  /* returns this class's state */

  this._getState = function() {
    return getProperty(this._pageState, this._prefix);
  };

  /* removes dummy option, saving state */

  this._removeDummyOption = function(select) {
    if (select.options[0].value == '') {
      select.options[0] = null;
    }

    var state = this._getState();
    var dummyRemoved = getProperty(state, 'removeDummyOption');
    dummyRemoved[select.id] = 0;
  };

  /* shows alert message */

  this._showAlert = function(message) {
    this._setInnerHTML(this.div.alert, message);
    this._setDisplay(this.div.alert, 'block');
    saveState();
  };
  /* returns a tabular string displaying years and payouts.
     @param: comma separated key=value pair of year and payout
     TODO(Jinal) change this to use json instead of splitting
  */
  this._getPayoutTable = function(payout_text) {
    var tableString = "<table id='payout_table'>";
    tableString = tableString + "<tr>" +
                 "<td id='payout_td_header'> Year </td>" +
                 "<td id='payout_td_header'> Payout </td>" +
                 "<td id='payout_td_header'> Year </td>" +
                 "<td id='payout_td_header'> Payout </td></tr>";
    var historicValues = payout_text.split("#");

    /* Displaying all historical payouts in descending order, incrementing them
       column-by-column. This means the first row will have values for the year
       and (year - 15) ..and so on..*/

    /* total years available */
    var numYears = historicValues.length-1;

    for (var count = 1; count <= HIST_PAYOUT_YEARS/2; count++) {
      var yearPayout =
          historicValues[numYears - count - HIST_PAYOUT_YEARS/2].split("=");
      var yearPayout2 = historicValues[numYears - count].split("=");
      var tdColor2 = "payout_td_data";
      var tdColor1 = "payout_td_data";
      var trColor = "payout_tr_normal";
      /* if payout is zero then color it differently */
      /* XXX: move this code to server for a cleaner approach*/
      if (yearPayout2[1] == "$0" || yearPayout2[1] == "EUR0" ||
          yearPayout2[1] == "CAD0" || yearPayout2[1] == "GBP0") {
        tdColor2 = "payout_td_zero";
      }
      if (yearPayout[1] == "$0" || yearPayout[1] == "EUR0" ||
          yearPayout[1] == "CAD0" || yearPayout[1] == "GBP0") {
        tdColor1 = "payout_td_zero";;
      }

      /* give a light bg color to alternate rows */
      if (i % 2  == 0) {
        trColor = "payout_tr_bold";
      }
      tableString = tableString +
                    "<tr id='" + trColor + "'>" +
                      "<td id = 'payout_td_year'>" +
                        yearPayout2[0] +
                      "</td>" +
                      "<td id = '" + tdColor2 + "'>" +
                        yearPayout2[1] +
                      "</td>" +
                      "<td id = 'payout_td_year'>" +
                        yearPayout[0] +
                      "</td>" +
                      "<td id = '" + tdColor1 + "'>" +
                        yearPayout[1] +
                      "</td>" +
                    "</tr>";
    }
    tableString = tableString + "</table>";
    return tableString;
  }

  /* displays suggestion text if strike is too high or too low
     @param text generated by strike suggestor */
  this._showSuggestions = function(tip_text) {
    this._setInnerHTML(this.div.suggest_txt, tip_text);
    this._setDisplay(this.div.suggest, 'block');
    saveState();
  };

  /* displays historical payouts table for a priced contract */
  this._showHistPayouts = function(hist_payout_text) {
    this._setInnerHTML(this.div.hist_payouts_txt,
                       this._getPayoutTable(hist_payout_text));
    this._setDisplay(this.div.hist_payouts, 'block');
    saveState();
  };

  /* shows/hides daylight saving transition message */
  this._showCrossesDst = function( crossesDst ) {
    if ( crossesDst ) {
      this._setInnerHTML(this.span.crosses_dst_message,
                            "Contract crosses daylight savings transition.");
      this._setDisplay(this.span.crosses_dst_message, 'block');
    } else {
      this._setDisplay(this.span.crosses_dst_message, 'none');
    }
  }

  /* hides alert message */
  this._hideAlert = function() {
    this._setDisplay(this.div.alert, 'none');
    saveState();
  };

  /* hides hist payouts */
  this._hideHistPayouts = function() {
    this._setDisplay(this.div.hist_payouts, 'none');
    saveState();
  };

  /* hides suggestion */
  this._hideSuggestion = function() {
    this._setDisplay(this.div.suggest, 'none');
    saveState();
  };

  /* calls Quote class's addDays() */
  this._addDays = function(date, days) {
    return this._quote._addDays(date, days);
  };

  /* handles keydown event for components (note that 'this' in the code
  in "_onXXX" methods is the component).  Keeps current value to check
  for changes. */

  this._onKeyDown = function(e) {
    this.valueBefore = this.value;
  };

  /* handles keyup event for a component.  If this component has 'pattern'
  property set, then only accepts inputs that matches the pattern (regexp).
  Fires 'onchange' event if the value changes by this key event */

  this._onKeyUp = function(e) {
    var code = null;

    if (window.event) {
      code = window.event.keyCode;
    }
    else if (e.which) {
      code = e.which;
    }

    if (code != 9) {
      if (this.valueBefore != this.value) {
        if (this.pattern) {
          var newValue = this.value.match(this.pattern);
          if (newValue != this.value) {
            this.value = newValue;
          }
        }

        this.onchange(e);
      }
    }
  };

  this._onFocusDate = function(e) {
    if (this.value == DATEFORMAT) {
      this.value = formatDate(this.firstDate);
      this.onclick();
    }

    this.select();
  };

  this._onKeyDownDate = function(e) {
    if ((typeof this.valueOriginal) == 'undefined') {
      this.valueOriginal = this.value;
    }

    this.valueBefore = this.value;

    this._quoteUI._hideCalendarPopup();
  }

  this._onKeyUpDate = function(e) {
    if (this.value != this.valueBefore) {
      var doesMatchesPattern = true;

      if (this.pattern) {
        if (this.value.match(this.pattern) != this.value) {
          doesMatchesPattern = false;
        }
      }

      if (doesMatchesPattern) {
        var message = this._quoteUI._validateDate(this);

        if (message == null) {
          this._quoteUI._hideAlert();
          this.valueOriginal = undefined;
          this.onchange(e);
        }
        else {
          this._quoteUI._showAlert(message);
        }
      }
    }
  };

  this._onBlurDate = function(e) {
    if ((typeof this.valueOriginal) != 'undefined') {
      this._quoteUI._setValue(this, this.valueOriginal);
      this.valueOriginal = undefined;
    }

    this._quoteUI._hideAlert();
  }

  /* Makes XMLHTTP request. */

  this._xmlhttpPost = function(strURL, callback, queryString) {
    var self = this;
    var xmlHttpReq = false;
    if (window.XMLHttpRequest) {
      xmlHttpReq = new XMLHttpRequest();
    }
    else if (window.ActiveXObject) {
      xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlHttpReq.open('POST', strURL, true);
    xmlHttpReq.setRequestHeader('Content-Type',
      'application/x-www-form-urlencoded');
    xmlHttpReq.onreadystatechange = function() {
      if (xmlHttpReq.readyState == 4) {
        callback(xmlHttpReq, self);
      }
    }
    xmlHttpReq.send(queryString);
  };

  /* displays UI actions (price/buyNow) according to the current state */

  this._showActions = function() {
    this._setDisplay(this.img.wait, 'none');
    this._setInnerHTML(this.span.price, this._price);

    if ((this._state == 'idle') || (this._state == 'invalid')) {
      this._setDisplay(this.img.getPrice, 'none');
      this._setDisplay(this.img.buyNow, 'none');
      this.form.main.onsubmit = null;
    }
    else if (this._state == 'ready') {
      this._setDisplay(this.img.getPrice, 'inline');
      this._setDisplay(this.img.buyNow, 'none');
      this.form.main.onsubmit = this.img.getPrice.onclick;
    }
    else if (this._state == 'priced') {
      this._setDisplay(this.img.getPrice, 'none');
      this._setDisplay(this.img.buyNow, 'inline');
      this.form.main.onsubmit = this.img.buyNow.onclick;
    }

    saveState();
  };

  /* passes _onModify() on component to _modified() */

  this._onModify = function() {
    this._quoteUI._modified();
  };

  /* called when a UI component is modified to update the actions */

  this._modified = function() {
    if ((this._state != 'idle') && (this._state != 'invalid'))
    {
      this._state = 'ready';
    }

    if (this._state != 'idle') {
      if ((this.date.sd.value == DATEFORMAT) ||
          (this.date.ed.value == DATEFORMAT) ||
          (this.select.rmsid.value == '')) {
        this._state = 'invalid';
      }
    }

    this._price = '';
    this._contractId = '';
    /* hide the suggestion and historical payout */
    this._hideSuggestion();
    this._hideHistPayouts();
    this._showActions();

    /* saves data state */

    var state = this._getState();
    var quoteValues = {};

    var keyList = ['_week', '_zip', '_rmsid', '_country', '_tick', '_strike',
                   '_cap', '_meas', '_dv', '_dv_t', '_dvop', '_dvop_type',
                   '_currency'];

    for (var i = 0; i < keyList.length; i ++) {
      var key = keyList[i];
      quoteValues[key] = this._quote[key];
    }

    state['quote'] = quoteValues;
    state['state'] = this._state;
    state['isEndDateSelected'] = this._isEndDateSelected;

    state['tempUnitName'] = this._tempUnit.name;
    state['precipUnitName'] = this._precipUnit.name;
    state['snowfallUnitName'] = this._snowfallUnit.name;

    saveState();
  };

  /* called when contract term is changed */

  this._onContractTermChange = function() {
    var quoteUI = this._quoteUI;

    quoteUI._contractTermChanged(this);

    return false;
  }

  /* hide calendar popup */

  this._hideCalendarPopup = function() {
    this._calendarPopup.hidePopup();
  }

  /* shows calendar popup */
  this._showCalendarPopup = function() {
    var quoteUI = this._quoteUI;
    // this was a fix for ie6 but it looks weird in other browsers so
    // disabling it.
    // quoteUI.select.country.style.visibility = 'hidden';
    // quoteUI.select.rmsid.style.visibility = 'hidden';
    //    quoteUI._calendarPopup.popupHidden = function() {
    //  this.target._quoteUI.select.rmsid.style.visibility = 'visible';
    //  this.target._quoteUI.select.country.style.visibility = 'visible';
    // };

    quoteUI._calendarPopup.target = this;

    quoteUI._calendarPopup.offsetX = 0;
    quoteUI._calendarPopup.source = this;
    this.value = formatDate(this.selectedDate);
    quoteUI._calendarPopup.disabledDatesExpression = '';
    quoteUI._calendarPopup.addDisabledDates(null,
      formatDate(this._quoteUI._addDays(this.firstDate, -1)));
    quoteUI._calendarPopup.addDisabledDates(
                          formatDate(this._quoteUI._addDays(this.lastDate, 1)),
                          null);
    quoteUI._calendarPopup.select(this, this.id);
    this.select();

    return false;
  };


  /* shows map popup */
  this._showMap = function() {
    var quoteUI = this._quoteUI;
    var zipCode = quoteUI.text.zip.value;
    var geocoder = new GClientGeocoder();
    country = quoteUI.select.country.value.toUpperCase();
    /* Only loading the stations for a selected country */
    // TODO - cache the maps for every country-datatype combination.
    // if (!map) {
    loadGmap(country);
    //}
    map.setZoom(6);

    geocoder.getLatLng (
      zipCode + ' ' + quoteUI.select.country.value.toUpperCase(),
      function(point) {
        if (point) {
          if (isSnowContract()) {
            map.setCenter(point, 3); // lower zoom level for snow as
                                     // as we have fewer snow stations.
          } else {
            map.setCenter(point, 6);
          }
        }
      }
    );


    map.closeInfoWindow();
    map.target = quoteUI.select.rmsid;

    quoteUI._mapPopup.offsetX = 0;
    quoteUI._mapPopup.offsetY = -316;
    quoteUI._mapPopup.showPopup(quoteUI.span.locationOr.id);

    return false;
  }

  /* calls _getPrice() from a component */

  this._onGetPrice = function() {
    if (this._quoteUI._state == 'ready') {
      this._quoteUI._setDisplay(this, 'none');
      this._quoteUI._setDisplay(this._quoteUI.img.wait, 'inline');
      this._quoteUI._getPrice();
    }
    return false;
  };

  /* calls _buyNow() from a component */

  this._onBuyNow = function() {
    this._quoteUI._buyNow();
    return false;
  };

  /* called when price is returned from the server */

  this._doneGetPrice = function(r, requestor) {
    var quoteUI = requestor;
    var returnValue = r.responseText;
    var value = eval('(' + returnValue + ')');

    if ((value.status == undefined) || (value.status == 0)) {
      quoteUI._price = value.price;
      quoteUI._contractId = value.cid;
      quoteUI._state = 'priced';

      /* need to save 'priced' as state for restore() to display button correctly */
      var state = quoteUI._getState();
      state['state'] = quoteUI._state;
      saveState();

      var addlText = "";
      if (value.price2 != undefined) {
        quoteUI._price = value.price2;
        addlText = "v1: " + value.price + "<br>";
        addlText += "v2: <a href='" + value.payout_array + "'>" +
                    value.price2 +"</a><br>";
      }

      /* display suggestion if value is not null or blank */
      if (value.suggestion != undefined && value.suggestion != '') {
        quoteUI._showSuggestions(addlText + value.suggestion);
      } else if (addlText != "") {
        quoteUI._showSuggestions(addlText);
      }

      /* display historic payouts if value is not null or blank */
      if (value.historic_payouts != undefined && value.historic_payouts != '') {
        quoteUI._showHistPayouts(value.historic_payouts);
      }

      /* For HOURLY contracts, call daylight savings transition handler */
      if (quoteUI.span.crosses_dst_message != undefined) {
        quoteUI._showCrossesDst( value.crossesDst );
      }

      quoteUI._hideAlert();
    }
    else {
      quoteUI._price = '';
      quoteUI._contractId = '';
      quoteUI._showAlert(value.msg);
    }

    var state = quoteUI._getState();

    state['price'] = quoteUI._price;
    state['contractId'] = quoteUI._contractId;

    quoteUI._showActions();

    if (quoteUI._isDebug) {
      var debugDiv = document.getElementById('Common_div_debug');
      if ((typeof debugDiv) != 'undefined') {
        var startTime = quoteUI._STARTED;
        var endTime = (new Date()).getTime();
        debugDiv.innerHTML = debugDiv.innerHTML + '<br />' +
                                                  (endTime - startTime) + 'ms';
      }
    }
  };

  /* initiates ajax call for current _quote object to get price */

  this._getPrice = function() {
    var params = this.createParameterString();

    if (this._isDebug) {
      var debugDiv = document.getElementById('Common_div_debug');
      if ((typeof debugDiv) != 'undefined') {
        this._STARTED = (new Date()).getTime();
        var url = this._getPriceURL + '?' + params.replace(/&/g, '&amp;');
        debugDiv.innerHTML = '<a href="' + url + '">' + url + '</a>';
      }
    }

    if (SUBMITASYNC) {
      this._xmlhttpPost(this._getPriceURL, this._doneGetPrice, params);
    }
    else {
      document.location.href = this._getPriceURL + '?' + params;
    }
  }

  /* creates parameter string needed for call to
     current _quote object to get price */

  this.createParameterString = function() {
    var startDate = this._quote._startDate;

    var s_mo = getMonthName(startDate);
    var s_dy = startDate.getDate();
    var s_yr = startDate.getFullYear();

    var endDate = this._quote._endDate;

    var e_mo = getMonthName(endDate);
    var e_dy = endDate.getDate();
    var e_yr = endDate.getFullYear();

    var week = this.select.dayType.value;
    var zip = this.text.zip.value;
    var rmsid = this.select.rmsid.value;

    var meas = this._quote._meas;
    var dv = this._quote._dv;
    var dv_t = this._quote._dv_t;
    var dvop = this._quote._dvop;
    var dvop_type = this._quote._dvop_type;

    var tick = this._quote._tick;
    var strike = this._quote._strike;
    var cap = this._quote._cap;

    var unit = '';

    if (meas == 1) {
      unit = this.select.precipunit.value;
    } else if (meas == 5) {
      unit = this.select.snowfallunit.value;
    } else {
      unit = this.select.tempunit.value;
    }
    var currency = this._currency;

    var params = 's_mo=' + s_mo + '&s_dy=' + s_dy + '&s_yr=' + s_yr +
                 '&e_mo=' + e_mo + '&e_dy=' + e_dy + '&e_yr=' + e_yr +
                 '&week=' + week + '&postcode=' + zip +
                 '&rmsid=' + rmsid + '&meas=' + meas + '&dv=' + dv +
                 '&dv_t=' + dv_t + '&dvop=' + dvop +
                 '&dvop_type=' + dvop_type + '&tick=' + tick +
                 '&strike=' + strike + '&cap=' + cap +
                 '&portfolioid=1' +
                 '&type=' + this._prefix + '&unit=' + unit +
                 '&currency=' + currency;

    return params;
  }

  /* does "buy now" */

  this._buyNow = function() {
    var params = 'cid=' + this._contractId;
        top.location.href = document.location.href =
                            this._buyNowURL + '?' + params;
  }

  /* global onFocus */

  this._onFocus = function() {
    this._quoteUI._calendarPopup.hidePopup();
  }

  this._onTempUnitChanged = function() {
    this._quoteUI._setTempUnit(this.value);
    this._quoteUI._tempUnitChanged(this);
  }

  this._onPrecipUnitChanged = function() {
    this._quoteUI._setPrecipUnit(this.value);
    this._quoteUI._precipUnitChanged(this);
  }

  this._onSnowfallUnitChanged = function() {
    this._quoteUI._setSnowfallUnit(this.value);
    this._quoteUI._snowfallUnitChanged(this);
  }

  this._tempUnitChanged = function(source) {
    this._contractTermChanged(source);
  }

  this._precipUnitChanged = function(source) {
    this._contractTermChanged(source);
  }

   this._snowfallUnitChanged = function(source) {
    this._contractTermChanged(source);
  }

  /* builds hierarchical DOM node map from given array of subcomponent names
  (separated by '#' instead of '_' for obfuscation).  Sets _quoteUI property
  on all components.  Also, sets keydown/keyup handlers for components of
  type "text". */

  this._buildComponents = function(components) {
    var tempCount = this._units.temp.length;
    var precipCount = this._units.precip.length;
    var snowfallCount = this._units.snowfall.length;

    for (var i = 0; i < components.length; i ++) {
      var name = components[i];
      var names = name.split('#');
      var category = names[0];
      var key = names[1];
      var target = this[category];
      if (!target) {
        target = new Object();
        this[category] = target;
      }
      var id = this._prefix + '_' + category + '_' + key;
      var element = document.getElementById(id);
      if (element != undefined) {
        element._quoteUI = this;
        target[key] = element;

        if (category == 'text') {
          element.onkeydown = this._onKeyDown;
          element.onkeyup = this._onKeyUp;
        }
        else if (category == 'date') {
          element.onfocus = this._onFocusDate;
          element.onkeyup = this._onKeyUpDate;
          element.onkeydown = this._onKeyDownDate;
          element.onblur = this._onBlurDate;
        }

        if (key == 'tempunit') {
          if (category == 'select') {
            for (var j = 0; j < tempCount; j ++) {
              var def = this._units.temp[j];
              element.options[j] = new Option('', def.name);
              element.options[j].innerHTML = def.label;
            }

            element.value = this._tempUnit.name;
            element.onchange = this._onTempUnitChanged;

            if (tempCount < 2) {
              this._setDisplay(element, 'none');
            }
          }
          else if (category == 'span') {
            if (tempCount == 1) {
              this._setInnerHTML(element, this._units.temp[0].label);
              this._setDisplay(element, 'inline');
            }
          }
        } else if (key.indexOf('precipunit') != -1) {
          /* indexOf for precipunit allows lookup for 'precipunit'
           * OR for 'precipunit2' which is used by for RainyEventBinary.
           */
          if (category == 'select') {
            for (var j = 0; j < precipCount; j++) {
              var def = this._units.precip[j];
              element.options[j] = new Option('', def.name);
              element.options[j].innerHTML = def.label;
              /* explicitly select the option with precipUnit.name */
              if (def.name == this._precipUnit.name) {
                element.options[j].selected = true;
              }
            }
            element.onchange = this._onPrecipUnitChanged;

            if (precipCount < 2) {
              this._setDisplay(element, 'none');
            }
          } else if (category == 'span') {
            if (precipCount == 1) {
              this._setInnerHTML(element, this._units.precip[0].label);
              this._setDisplay(element, 'inline');
            }
          }
        } else if (key == 'snowfallunit') {
          if (category == 'select') {
            for (var j = 0; j < snowfallCount; j ++) {
              var def = this._units.snowfall[j];
              element.options[j] = new Option('', def.name);
              element.options[j].innerHTML = def.label;
          }
          element.value = this._snowfallUnit.name;
          element.onchange = this._onSnowfallUnitChanged;

          if (snowfallCount < 2) {
             this._setDisplay(element, 'none');
          }
        } else if (category == 'span') {
              if (snowfallCount == 1) {
                this._setInnerHTML(element, this._units.snowfall[0].label);
                this._setDisplay(element, 'inline');
              }
            }
        }

        if (category != 'date') {
          element.onfocus = this._onFocus;
        }
      }
    }
  };

  /* check if given date is valid (using calendar values) */

  this._validateDate = function(source) {
    try {
      var firstDateJulian = this._quote._toJulianDate(source.firstDate);
      var lastDateJulian = this._quote._toJulianDate(source.lastDate);

      var dateJulian = this._quote._toJulianDate(parseDate(source.value));

      if ((dateJulian >= firstDateJulian) && (dateJulian <= lastDateJulian))
      {
        return null;
      }
    }
    catch (error) {
    }

    return "Date must be between " + formatDate(source.firstDate) + " and " +
           formatDate(source.lastDate);
  }

  /* sets node value iff different */

  this._setValue = function(node, value) {
    if (node.value != value) {
      node.value = value;
    }
  }

  /* called when date is changed (on a component) */

  this._onDateChange = function() {
    if ((typeof this.valueOriginal) != 'undefined') {
      this.value = this.valueOriginal;
      this.valueOriginal = undefined;
    }

    var quoteUI = this._quoteUI;

    if (this == quoteUI.date.sd) {
      quoteUI._quote._startDate = parseDate(this.value);
      this.selectedDate = quoteUI._quote._startDate;
      quoteUI.date.ed.firstDate = quoteUI._quote._startDate;
      /*
       * when startDate is changed, the last allowed endDate to be
       * MAX_CONTRACT_LENGTH days from startDate
       */
      quoteUI.date.ed.lastDate = quoteUI._addDays(this.selectedDate,
                                                  MAX_CONTRACT_LENGTH - 1);
      quoteUI._setValue(quoteUI.date.sd, formatDate(quoteUI._quote._startDate));

      if (!quoteUI._isEndDateSelected) {
        quoteUI.date.ed.selectedDate = this.selectedDate;
        return;
      } else {
          if (quoteUI._quote._endDate < quoteUI._quote._startDate) {
            quoteUI._quote._endDate = quoteUI._quote._startDate;
            quoteUI.date.ed.selectedDate = quoteUI._quote._endDate;

            if (this.value != quoteUI.date.ed.value) {
              quoteUI.date.ed.value = this.value;
            }
          }
      }
    } else if (this == quoteUI.date.ed) {
      quoteUI._isEndDateSelected = true;
      quoteUI._quote._endDate = parseDate(this.value);
      this.selectedDate = quoteUI._quote._endDate;
      quoteUI._setDisplay(quoteUI.div.location, 'block');
      quoteUI._setValue(quoteUI.date.sd, formatDate(quoteUI._quote._startDate));
      quoteUI._setValue(quoteUI.date.ed, formatDate(quoteUI._quote._endDate));
    } else if (this == quoteUI.select.dayType) {
      quoteUI._quote._week = this.value;
      if (!quoteUI._isEndDateSelected) {
        return;
      }
    }

    var days = quoteUI._quote._computeDays();

    quoteUI._setInnerHTML(quoteUI.span.days, '(' + days + ' day' +
                          ((days > 1) ? 's' : '') + ')');
    quoteUI._contractTermChanged(this);
  };

  /* called when the currency is changed */
  this._onCurrencyChange = function(currency) {
    this._quoteUI._currency = this.value;
    this._quoteUI._modified();
  }

  /* called when a country is changed on a component */
  this._onCountryChange = function(country) {
    /* where is being used for adding a new station */
    var where =
        (navigator.appName == "Microsoft Internet Explorer") ? -1 : null;
    var quoteUI = this._quoteUI;
    cValue = quoteUI.select.country.value;
    stationCB = quoteUI.select.rmsid;
    /* Enable the station now */
    quoteUI.select.rmsid.disabled=false;

    /* Empty the stations select box */
    stationCB.options.length = 0;

    /* add the default option  */
    newStation = document.createElement("option");
    newStation.text ="Select your nearest weather station..." ;
    newStation.value = "";
    stationCB.add(newStation, where);
    var country_id = quoteUI.select.country.id;
    if (country_id == 'SnowDay_select_country' ||
        country_id == 'SnowSeason_select_country' ||
        country_id == 'SnowSeasonBinary_select_country' ) {
      stations = getSnowStations(cValue);
    } else if (country_id == 'RainySeason_select_country' ||
               country_id == 'RainySeasonBinary_select_country' ||
               country_id == 'DrySeason_select_country' ||
               country_id == 'DrySeasonBinary_select_country' ||
               country_id == 'DryDay_select_country' ||
               country_id == 'RainyDay_select_country') {
      stations = getPrecipStations(cValue);
    } else if (country_id == 'RainyEventBinary_select_country') {
      stations = getHourlyPrecipStations(cValue);
    } else {
      stations = getTempStations(cValue);
    }

    for (var i = 0; i < stations.length; i++) {
      var station   = stations[i];
      var state     = station[3];
      var shortName = station[6];
      var cnt       = station[7]
      if (cnt == cValue) {
        newStation = document.createElement("option");
        newStation.text = state + " - " + shortName;
        newStation.value = station[1];
        stationCB.add(newStation, where);
      }
    }
    quoteUI._modified();
    GUnload();
    loadGmap(cValue);
  };


  /* called when station is changed on a component */

  this._onStationChange = function() {
    var quoteUI = this._quoteUI;

    quoteUI._quote._zip = quoteUI.text.zip.value;
    quoteUI._quote._rmsid = quoteUI.select.rmsid.value;

    quoteUI._stationChanged();

    /* for all contract types, station changes should always re-validate */
    quoteUI._contractTermChanged(this);
  };

  /* sets/records node display state */

  this._setDisplay = function(node, display) {
    node.style.display = display;

    var state = this._getState();
    var displayState = getProperty(state, 'style.display');
    displayState[node.id] = display;
  };

  /* sets/records node innerHTML */

  this._setInnerHTML = function(node, innerHTML) {
    node.innerHTML = innerHTML;

    var state = this._getState();
    var innerHTMLState = getProperty(state, 'innerHTML');
    innerHTMLState[node.id] = innerHTML;
  };

  /* restores state */

  this._restore = function() {
    var state = this._getState();
    var displayState = getProperty(state, 'style.display');

    for (var key in displayState) {
      document.getElementById(key).style.display = displayState[key];
    }

    var innerHTMLState = getProperty(state, 'innerHTML');

    for (var key in innerHTMLState) {
      document.getElementById(key).innerHTML = innerHTMLState[key];
    }

    var quoteValues = getProperty(state, 'quote');

    for (var key in quoteValues) {
      this._quote[key] = quoteValues[key];
    }

    if (state['state']) {
      this._state = state['state'];
    }

    if (state['isEndDateSelected']) {
      this._isEndDateSelected = state['isEndDateSelected'];
    }

    var dummySelects = getProperty(state, 'removeDummyOption');

    for (var key in dummySelects) {
      this._removeDummyOption(document.getElementById(key));
    }

    if ((typeof state['price']) != 'undefined') {
      this._price = state['price'];
    }

    if ((typeof state['contractId']) != 'undefined') {
      this._contractId = state['contractId'];
    }

    if ((typeof state['tempUnitName']) != 'undefined') {
      this._setTempUnit(state['tempUnitName']);
      var element = this.select.tempunit;
      if ((typeof element) != 'undefined') {
        element.value = this._tempUnit.name;
      }
    }

    /**
     * For precip unit, use the state['precipUnitName'] property directly.
     * Calling setPrecipUnit() first and accessing this._precipUnit.name has the
     * side effect of changing the default precipUnit name for ALL precip views.
     */
    if ((typeof state['precipUnitName']) != 'undefined') {

      var element = this.select.precipunit;
      if ((typeof element) != 'undefined' && !element.disabled) {
        element.value = state['precipUnitName'];
      }

      /* RainyEventBinary ( hourly ) needs to set precipunit2 */
      var element = this.select.precipunit2;
      if ((typeof element) != 'undefined' && !element.disabled) {
        element.value = state['precipUnitName'];
      }
    }

    if ((typeof state['snowfallUnitName']) != 'undefined') {
      this._setSnowfallUnit(state['snowfallUnitName']);
      var element = this.select.snowfallunit;
      if ((typeof element) != 'undefined') {
        element.value = this._snowfallUnit.name;
      }
    }
  };

  /* utility to check if x is null or empty string */

  this._isEmpty = function(x) {
    return ((x == null) || (x == ''));
  };

  /* validated temperature; returns null if ok, or an error message.  If
  error message is "", it is not displayed. */

  this._validateTemp = function(value) {
    value = '' + value;

    if (!value.match('.*[0-9].*')) {
      return "";
    }

    value = this.parseFloat(value);

    if (isNaN(value) || (value < this._tempUnit.min) ||
       (value > this._tempUnit.max)) {
      return "Temperature must be between " + this._tempUnit.min + " and " +
             this._tempUnit.max + this._tempUnit.label;
    }

    return null;
  };

  /* validates precip */

  this._validatePrecip = function(value) {
    value = '' + value;

    if (!value.match('.*[0-9].*')) {
      return "";
    }

    var value = this.parseFloat(value);

    if (isNaN(value) || (value < this._precipUnit.min) ||
       (value > this._precipUnit.max)) {
         return "Precipitation must be between " + this._precipUnit.min +
                " and " + this._precipUnit.max + " " + this._precipUnit.label;
    }

    return null;
  };

  /* validates snowfall */

  this._validateSnowfall = function(value) {
    value = '' + value;

    if (!value.match('.*[0-9].*')) {
      return "";
    }

    var value = this.parseFloat(value);

    if (isNaN(value) || (value < this._snowfallUnit.min) ||
       (value > this._snowfallUnit.max)) {
      return "Snowfall must be between " + this._snowfallUnit.min +
             " and " + this._snowfallUnit.max + " " + this._snowfallUnit.label;
    }

    return null;
  };

  // common component setup

  this._setup = function() {
    var components = ['div#date', 'div#location', 'div#type',
                      'div#hist_payouts', 'div#suggest','div#suggest_txt',
                      'div#hist_payouts_txt', 'date#sd','date#ed',
                      'select#dayType', 'span#days', 'text#zip',
                      'submit#showMap', 'select#rmsid','select#country',
                      'select#currency', 'span#locationOr','span#price',
                      'img#wait', 'img#getPrice', 'img#buyNow','form#location',
                      'form#main', 'select#tempunit', 'span#tempunit',
                      'select#precipunit', 'span#precipunit',
                      'select#snowfallunit', 'span#snowfallunit',
                      'select#dailyvalue'];

    this._buildComponents(components);

    /* uses common alert instead of individual ones */

    this.div.alert = document.getElementById('Common_div_alert');
    this.div.alert.onclick = function(){this._quoteUI._hideAlert()};

    this.date.sd.onclick = this._showCalendarPopup;
    this.date.sd.pattern = '[0-9][0-9]/[0-9][0-9]/[0-9][0-9]';
    /*this.date.sd.onfocus = this._showCalendarPopup;*/
    this.date.sd.onchange  = this._onDateChange;
    this.date.ed.onclick = this._showCalendarPopup;
    this.date.ed.pattern = '[0-9][0-9]/[0-9][0-9]/[0-9][0-9]';
    /*this.date.ed.onfocus = this._showCalendarPopup*/
    this.date.ed.onchange  = this._onDateChange;
    this.select.dayType.onchange = this._onDateChange;
    this.submit.showMap.onclick = this._showMap;
    if (this.form.location) { this.form.location.onsubmit = this._showMap; }
    this.text.zip.onchange = function(){};
    this.text.zip.pattern = '[0-9A-Za-z ]*';
    this.select.country.onchange = this._onCountryChange;
    this.select.currency.onchange = this._onCurrencyChange;
    this.select.rmsid.onchange = this._onStationChange;

    var today = this._quote._getToday();
    var firstDate = this._addDays(today, this._quote.getStartDateShift());
    this.date.sd.firstDate = firstDate;
    /* last permitted date for startDate is MAX_FUTURE_STARTDATE from today */
    this.date.sd.lastDate = this._addDays(today, MAX_FUTURE_STARTDATE - 1);
    this.date.ed.firstDate = this.date.sd.firstDate;

    /* last permitted date for endDate is MAX_CONTRACT_LENGTH days from sd*/
    this.date.ed.lastDate = this._addDays(this.date.ed.firstDate,
                                          MAX_CONTRACT_LENGTH - 1);

    this.img.getPrice.onclick = this._onGetPrice;
    this.img.buyNow.onclick = this._onBuyNow;

    this.form.main.onsubmit = null;
  };

  /** sets default values of UI controls controlled by this class */

  this._setDefault = function() {
    this.date.sd.value = DATEFORMAT;

    /* end date not present for Rainy Event Binary ( hourly )*/
    if(this.date.ed != null ) {
      this.date.ed.value = DATEFORMAT;
    }
    /* same issue for dayType */
    if( this.select.dayType != null ) {
      this.select.dayType.value = '3';
    }

    this.text.zip.value =
        ((typeof(defaultPostalCode) != 'undefined') ? defaultPostalCode
                                                    : '94108');
    this.select.rmsid.value = '';
  };

  /** changes target value iff defined and different, returns true if changed,
   * otherwise false; if func is given then it is applied to value before
   * assignment. */

  this.overwrite = function(target, value, func) {
    if ((typeof target) != 'undefined') {
      if (((typeof value) != 'undefined') && (target.value != value)) {
        if ((typeof func) != 'undefined') {
          value = func.call(this, value);
        }

        target.value = value;
        target.onchange();
        return true;
      }
    }

    return false;
  }

  this.getMonth = function(monthName)
  {
    var monthNames = {"Jan":1, "Feb":2, "Mar":3, "Apr":4, "May":5, "Jun":6,
                      "Jul":7, "Aug":8, "Sep":9, "Oct":10, "Nov":11, "Dec":12};

    return monthNames[monthName];
  }

  this.makeDate = function(mo, dy, yr)
  {
    var month = this.getMonth(mo);

    if ((typeof month) == 'undefined')
    {
      return undefined;
    }

    if (yr > 2000)
    {
      yr = yr - 2000;
    }
    else if (yr > 1900)
    {
      yr = yr - 1900;
    }

    return '' + month + '/' + dy + '/' + yr;
  }

  /** initialize UI from contract properties */

  this.initializeFromContract = function(contract)
  {
    if ((typeof contract.startDate) == 'undefined')
    {
      contract.startDate = this.makeDate(contract.s_mo,
                                         contract.s_dy,
                                         contract.s_yr);
    }

    if ((typeof contract.startDate) != 'undefined')
    {
      try
      {
        var startDateFirstJulian = this._quote._toJulianDate(this.date.sd.firstDate);
        var startDateLastJulian = this._quote._toJulianDate(this.date.sd.lastDate);
        var contractStartDateJulian = this._quote._toJulianDate(parseDate(contract.startDate));

        if (contractStartDateJulian < startDateFirstJulian)
        {
          contractStartDateJulian = startDateFirstJulian;
        }
        else if (contractStartDateJulian > startDateLastJulian)
        {
          contractStartDateJulian = startDateLastJulian;
        }

        this.overwrite(this.date.sd, formatDate(this._quote._fromJulianDate(contractStartDateJulian)));
      }
      catch (error)
      {
      }
    }

    if ((typeof contract.endDate) == 'undefined')
    {
      contract.endDate = this.makeDate(contract.e_mo, contract.e_dy, contract.e_yr);
    }

    if ((typeof contract.endDate) != 'undefined')
    {
      try
      {
        var endDateFirstJulian = this._quote._toJulianDate(this.date.ed.firstDate);
        var endDateLastJulian = this._quote._toJulianDate(this.date.ed.lastDate);

        var contractEndDateJulian = this._quote._toJulianDate(parseDate(contract.endDate));

        if (contractEndDateJulian < endDateFirstJulian)
        {
          contractEndDateJulian = endDateFirstJulian;
        }
        else if (contractEndDateJulian > endDateLastJulian)
        {
          contractEndDateJulian = endDateLastJulian;
        }

        this.overwrite(this.date.ed, formatDate(this._quote._fromJulianDate(contractEndDateJulian)));
      }
      catch (error)
      {
      }
    }

    this.overwrite(this.select.dayType, contract.week);
    this.overwrite(this.select.rmsid, contract.rmsid);

    this.overwrite(this.select.tempunit, contract.unit);
    this.overwrite(this.select.precipunit, contract.unit);
    this.overwrite(this.select.snowfallunit, contract.unit);

  };

  /** initialization */

  this.init = function() {
    this._setup();

    if (this._pageState[this._prefix].init) {
      this._setDefault();
    }

    if (this.date.sd.value == DATEFORMAT) {
      this.date.sd.selectedDate = this._quote._startDate;
    } else {
      this.date.sd.selectedDate = parseDate(this.date.sd.value);
      this._quote._startDate = this.date.sd.selectedDate;
    }

    /* there is no end date for RainyEventBinary, so check that date.ed exists
     * before setting end.selectedDate.*/
    if (this.date.ed) {
      if (this.date.ed.value == DATEFORMAT) {
        this.date.ed.selectedDate = this._quote._endDate;
      } else {
        this.date.ed.selectedDate = parseDate(this.date.ed.value);
        this._quote._endDate = this.date.ed.selectedDate;
      }
    }

    this._restore();

    this._pageState[this._prefix].init = false;

    return this;
  };
} /* end of common quote */

/* Adv */

function AdvQuote(config)
{
  CommonQuote.apply(this, arguments);
  this._CommonQuote_setup = this._setup;
  this._CommonQuote_setDefault = this._setDefault;
  this._CommonQuote_initializeFromContract = this.initializeFromContract;

  this._prefix = 'Adv';

  this._setDefault = function()
  {
    this._CommonQuote_setDefault();

    this.select.measurement.value = '';
    this.select.dailyValue.value = '';
    this.text.threshold.value = '0.00';
    this.select.index.value = '';
    this.select.payoutType.value = '';
    this.text.incrementalTick.value = '100.00';
    this.text.incrementalStrike.value = '0';
    this.text.binaryTick.value = '100.00';
    this.text.binaryStrike.value = '0';
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };

  this.initializeFromContract = function(contract)
  {
    this._CommonQuote_initializeFromContract(contract);

    this.overwrite(this.text.cap, contract.cap, this.formatDollar);

    this.overwrite(this.text.threshold, contract.dv_t);

    this.overwrite(this.select.payoutType, contract.dvop_type);

    if (this.select.payoutType.value == 3 || this.select.payoutType.value == 4){
      this.overwrite(this.text.incrementalTick, contract.tick,
                     this.formatDollar);
      this.overwrite(this.text.incrementalStrike, contract.strike);
    } else {
      this.overwrite(this.text.binaryTick, contract.tick, this.formatDollar);
      this.overwrite(this.text.binaryStrike, contract.strike);
    }

    this.overwrite(this.select.measurement, contract.meas);
    this.overwrite(this.select.dailyValue, contract.dv);
    this.overwrite(this.select.index, contract.dvop);
  }

  this._setup = function()
  {
    this._CommonQuote_setup();

    var components = ['div#dailyValue', 'div#index', 'div#rules',
                      'div#incremental', 'div#binary', 'text#threshold',
                      'select#payoutType', 'text#incrementalTick',
                      'text#incrementalStrike', 'text#binaryTick',
                      'text#binaryStrike', 'text#cap', 'span#price',
                      'select#measurement', 'select#dailyValue',
                      'select#index'];

    this._buildComponents(components);

    this.select.payoutType.onchange = this._onPayoutTypeChange;

    this.text.incrementalTick.onchange = this._onContractTermChange;
    this.text.incrementalTick.pattern = '[0-9]*[.]?[0-9]*';
    this.text.incrementalStrike.onchange = this._onContractTermChange;
    this.text.incrementalStrike.pattern = '[0-9]*[.]?[0-9]*';
    this.text.binaryTick.onchange = this._onContractTermChange;
    this.text.binaryTick.pattern = '[0-9]*[.]?[0-9]*';
    this.text.binaryStrike.onchange = this._onContractTermChange;
    this.text.binaryStrike.pattern = '[0-9]*[.]?[0-9]*';
    this.text.cap.onchange = this._onContractTermChange;
    this.text.cap.pattern = '[0-9,]*[.]?[0-9]*';
    this.text.threshold.onchange = this._onContractTermChange;
    this.text.threshold.pattern = '[-]?[0-9]*[.]?[0-9]*';
    this.select.measurement.onchange = this._onContractTermChange;
    this.select.dailyValue.onchange = this._onContractTermChange;
    this.select.index.onchange = this._onContractTermChange;
  };

  this._stationChanged = function()
  {
    this._setDisplay(this.div.dailyValue, 'block');
  };

  this._onContractTermChange = function()
  {
    var quoteUI = this._quoteUI;

    if (this == quoteUI.select.measurement) {
      quoteUI._removeDummyOption(this);
      if (quoteUI.select.dailyValue.options[0].value != '')
      {
        quoteUI._setDisplay(quoteUI.div.index, 'block');
      }

      if (this.value == 1) {
        if (quoteUI._units.precip.length < 2)
        {
          quoteUI._setDisplay(quoteUI.select.precipunit, 'none');
          quoteUI._setDisplay(quoteUI.span.precipunit, 'inline');
        }
        else
        {
          quoteUI._setDisplay(quoteUI.select.precipunit, 'inline');
          quoteUI._setDisplay(quoteUI.span.precipunit, 'none');
        }
        quoteUI._setDisplay(quoteUI.select.tempunit, 'none');
        quoteUI._setDisplay(quoteUI.span.tempunit, 'none');
        quoteUI._setDisplay(quoteUI.select.snowfallunit, 'none');
        quoteUI._setDisplay(quoteUI.span.snowfallunit, 'none');

      } else if (this.value == 5) {
          if (quoteUI._units.snowfall.length < 2) {
            quoteUI._setDisplay(quoteUI.select.snowfallunit, 'none');
            quoteUI._setDisplay(quoteUI.span.snowfallunit, 'inline');
          } else {
            quoteUI._setDisplay(quoteUI.select.snowfallunit, 'inline');
            quoteUI._setDisplay(quoteUI.span.snowfallunit, 'none');
          }
          quoteUI._setDisplay(quoteUI.select.tempunit, 'none');
          quoteUI._setDisplay(quoteUI.span.tempunit, 'none');
          quoteUI._setDisplay(quoteUI.select.precipunit, 'none');
          quoteUI._setDisplay(quoteUI.span.precipunit, 'none');
      } else {
          if (quoteUI._units.temp.length < 2) {
            quoteUI._setDisplay(quoteUI.select.tempunit, 'none');
            quoteUI._setDisplay(quoteUI.span.tempunit, 'inline');
          } else {
            quoteUI._setDisplay(quoteUI.select.tempunit, 'inline');
            quoteUI._setDisplay(quoteUI.span.tempunit, 'none');
          }
          quoteUI._setDisplay(quoteUI.select.precipunit, 'none');
          quoteUI._setDisplay(quoteUI.span.precipunit, 'none');
          quoteUI._setDisplay(quoteUI.select.snowfallunit, 'none');
          quoteUI._setDisplay(quoteUI.span.snowfallunit, 'none');
      }
    } else if (this == quoteUI.select.dailyValue) {
        quoteUI._removeDummyOption(this);
        if (quoteUI.select.measurement.options[0].value != '') {
          quoteUI._setDisplay(quoteUI.div.index, 'block');
        }
    } else if (this == quoteUI.select.index) {
      quoteUI._removeDummyOption(this);
      quoteUI._setDisplay(quoteUI.div.rules, 'block');
    }

    quoteUI._contractTermChanged(this);
  };

  this._validate = function(source) {
    var quote = this._quote;

    var days = quote._computeDays();

    if (days <= 0) {
      return "Contract must include at least one day";
    }

    var cap = this.parseFloat(this.text.cap.value);

    if (isNaN(cap)) {
      return "";
    } else if (cap > quote.getMaxCap()) {
      return "Maximum Payout can't exceed " + this.formatDollar(quote.getMaxCap());
    }

    quote._cap = cap;

    quote._meas = this.parseFloat(this.select.measurement.value);

    if (isNaN(quote._meas)) {
      return "";
    }

    quote._dv = this.parseFloat(this.select.dailyValue.value);

    if (isNaN(quote._dv)) {
      return "";
    }

    quote._dv_t = this.parseFloat(this.text.threshold.value);

    if (isNaN(quote._dv_t)) {
      return "";
    }

    quote._dvop = this.parseFloat(this.select.index.value);
    quote._dvop_type = this.parseFloat(this.select.payoutType.value);

    if (quote._dvop_type == '3' || quote._dvop_type == '4') {
      quote._tick = this.parseFloat(this.text.incrementalTick.value);
      quote._strike = this.parseFloat(this.text.incrementalStrike.value);
    } else {
      quote._tick = this.parseFloat(this.text.binaryTick.value);
      quote._strike = this.parseFloat(this.text.binaryStrike.value);
      quote._cap = quote.getMaxCap();
    }

    if (isNaN(quote._tick)) {
      return "";
    }

    if (isNaN(quote._strike)) {
      return "";
    }

    if (quote._tick <= 0.0) {
      if (quote._dvop_type == 3 || quote._dvop_type == 4) {
         return "Payment per Tick cannot be 0.";
      } else {
         return "Payout Amount cannot be 0.";
      }
    }

    if (quote._cap <= 0.0) {
      return "Maximum payout cannot be 0.";
    }

    if (quote._meas == 1) {
      var status = this._validatePrecip(quote._dv_t);

      if (status != null) {
        return status;
      }
    } else if(quote._meas == 5) {
      var status = this._validateSnowfall(quote._dv_t);

      if (status != null) {
        return status;
      }
    } else {
      var status = this._validateTemp(quote._dv_t);

      if (status != null) {
        return status;
      }
    }

    return null;
  };

  this._contractTermChanged = function(source)
  {
    this._hideAlert();

    var message = this._validate(source);
    var isValid = (message == null);

    if ((!isValid) && (message != ""))
    {
      this._showAlert(message);
    }

    if (this._state != 'idle')
    {
      this._state = (isValid) ? 'ready' : 'invalid';
    }

    this._modified();
  };

  this._onPayoutTypeChange = function()
  {
    var quoteUI = this._quoteUI;

    if (this.value == '3' || this.value == '4')
    {
      quoteUI._setDisplay(quoteUI.div.incremental, 'block');
      quoteUI._setDisplay(quoteUI.div.binary, 'none');
    }
    else
    {
      quoteUI._setDisplay(quoteUI.div.incremental, 'none');
      quoteUI._setDisplay(quoteUI.div.binary, 'block');
    }

    quoteUI._removeDummyOption(this);
    quoteUI._state = 'ready';

    quoteUI._contractTermChanged(this);
  };
}; /* end of Adv quote */

/* DrySeason */

function DrySeasonQuote(config)
{
  /* import all function from CommonQuote */

  CommonQuote.apply(this, arguments);
  this._CommonQuote_setup = this._setup;
  this._CommonQuote_setDefault = this._setDefault;
  this._CommonQuote_initializeFromContract = this.initializeFromContract;

  this._prefix = 'DrySeason';

  /* initializes UI component values for reloading */

  this._setDefault = function()
  {
    this._CommonQuote_setDefault();

    this.text.tick.value = '100.00';
    this.text.strike.value = this._precipUnit.defaultSeasonal;

    /* no text.cap for RainyEventBinary */
    if(this.text.cap != null) {
      this.text.cap.value = this.formatDollar(DEFAULT_CAP);
    }
  };

  this.initializeFromContract = function(contract)
  {
    this._CommonQuote_initializeFromContract(contract);

    this.overwrite(this.text.tick, contract.tick, this.formatDollar);

    this.overwrite(this.text.strike, contract.strike);

    this.overwrite(this.text.cap, contract.cap, this.formatDollar);
  }

  this._setup = function()
  {
    this._CommonQuote_setup();

    var components = ['text#tick', 'text#strike', 'text#cap'];

    this._buildComponents(components);

    this.text.tick.onchange = this._onContractTermChange;
    this.text.tick.pattern = '[0-9,]*[.]?[0-9]*';
    this.text.strike.onchange = this._onContractTermChange;
    this.text.strike.pattern = '[0-9]*[.]?[0-9]*';
    this.text.cap.onchange = this._onContractTermChange;
    this.text.cap.pattern = '[0-9,]*[.]?[0-9]*';
  };

  /* set fixed quote parameters */

  this._fixQuoteParams = function(quote)
  {
    quote._meas = 1;  // data
    quote._dv = 1; // dailyvalue
    quote._dv_t = 0; // dailyref
    quote._dvop = 1;  // operation
    quote._dvop_type = 4;
    quote._week = 3;
  };

  /* called when station is changed */

  this._stationChanged = function()
  {
    this._setDisplay(this.div.type, 'block');
    this._state = 'ready';
  };

  /* called when contract term is changed (on each related component) */

  this._onContractTermChange = function()
  {
    var quoteUI = this._quoteUI;

    quoteUI._contractTermChanged(this);
  }

  /* performs input validation for pricing.  'source' is the UI component
     that caused validation. */

  this._validate = function(source)
  {
    var quote = this._quote;

    this._fixQuoteParams(quote);

    quote._strike = this.parseFloat(this.text.strike.value);
    quote._tick = this.parseFloat(this.text.tick.value);
    quote._cap = this.parseFloat(this.text.cap.value);

    var days = quote._computeDays();

    if (days <= 0)
    {
      return "Contract must include at least 1 day.";
    }

    var tick = quote._tick;

    if (isNaN(tick))
    {
      return false;
    }
    else
    {
      if (tick <= 0)
      {
        return "Payment must be greater than 0.";
      }
    }

    var strike = quote._strike;

    if (isNaN(strike))
    {
      return "";
    }
    else
    {
      var value = this.text.strike.value

      if (!value.match('.*[0-9].*'))
      {
        return "";
      }

      value = this.parseFloat(value);

      if (isNaN(value) || (value < 0))
      {
        return "Precipitation must be a non-negative number.";
      }
    }

    if (isNaN(quote._cap))
    {
      return "";
    }
    else if (quote._cap > quote.getMaxCap())
    {
      return "Maximum Payout can't exceed " + this.formatDollar(quote.getMaxCap());
    }

    if (this._prefix == 'DrySeason')
    {
      if (quote._strike <= 0)
      {
        return "";
      }

      var maxDryCap = Math.round((quote._tick * quote._strike) * 100) / 100;

      if (maxDryCap > quote.getMaxCap())
      {
        maxDryCap = quote.getMaxCap();
      }

      if (source != this.text.cap)
      {
        quote._cap = maxDryCap;
        this.text.cap.value = this.formatDollar(maxDryCap);
      }
      else if (quote._cap > maxDryCap)
      {
         return "The maximum payout of this contract can't exceed " +
                this.formatDollar(maxDryCap) + " (" + quote._strike +
                " inches x " + this.formatDollar(quote._tick) + " per inch)";
      }
    }

    return null;
  };

  /* called when contract term is changed */

  this._contractTermChanged = function(source)
  {
    this._hideAlert();

    var message = this._validate(source);
    var isValid = (message == null);

    if ((!isValid) && (message != ""))
    {
      this._showAlert(message);
    }

    if (this._state != 'idle')
    {
      this._state = (isValid) ? 'ready' : 'invalid';
    }

    this._modified();
  };
} /* end of dry season quote */

/* RainySeason */

function RainySeasonQuote(config)
{
  DrySeasonQuote.apply(this, arguments);
  this._prefix = 'RainySeason';

  this._fixQuoteParams = function(quote)
  {
    quote._meas = 1;  // data
    quote._dv = 1; // dailyvalue
    quote._dv_t = 0; // dailyref
    quote._dvop = 1;  // operation
    quote._dvop_type = 3;
    quote._week = 3;
  };
}

/* RainySeasonBinary */

function RainySeasonBinaryQuote(config)
{
  RainySeasonQuote.apply(this, arguments);
  this._prefix = 'RainySeasonBinary';

  /* keep a handle on the superclass's validate because we'll need it below */
  this._rainySeasonValidate = this._validate;

  this._fixQuoteParams = function(quote)
  {
    quote._meas = 1;      /* data */
    quote._dv = 1;        /* dailyvalue */
    quote._dv_t = 0;      /* dailyref */
    quote._dvop = 1;      /* operation */
    quote._dvop_type = 1; /* binary above */
    quote._week = 3;
  };

  /**
    When a quote is done for Binary Season contracts, set the cap == tick
    (since binary contracts pay out a single tick or 0).
  */
  this._validate = function(source) {
    this._rainySeasonValidate();
    this._quote._cap = this._quote._tick;
  }

}

/* DrySeasonBinary */

function DrySeasonBinaryQuote(config) {
  DrySeasonQuote.apply(this, arguments);
  this._prefix = 'DrySeasonBinary';

  /* keep a handle on the superclass's validate because we'll need it below */
  this._drySeasonValidate = this._validate;

  this._fixQuoteParams = function(quote) {
    quote._meas = 1;  // data
    quote._dv = 1; // dailyvalue
    quote._dv_t = 0; // dailyref
    quote._dvop = 1;  // operation
    quote._dvop_type = 2; // binary below
    quote._week = 3;
  };

  /**
    When a quote is done for Binary Season contracts, set the cap == tick
    (since binary contracts pay out a single tick or 0).
  */
  this._validate = function(source) {
    this._drySeasonValidate();
    this._quote._cap = this._quote._tick;
  }
}



/* HotSeason */

function HotSeasonQuote(config) {
  /* import all function from CommonQuote */

  CommonQuote.apply(this, arguments);
  this._CommonQuote_setup = this._setup;
  this._CommonQuote_setDefault = this._setDefault;
  this._CommonQuote_initializeFromContract = this.initializeFromContract;

  this._prefix = 'HotSeason';

  /* initializes UI component values for reloading */

  this._setDefault = function() {
    this._CommonQuote_setDefault();

    this.select.meas.value = 4; // avgtemp
    this.text.tick.value = '100.00';
    this.text.strike.value = this._tempUnit.defaultValue;
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };

  this.initializeFromContract = function(contract) {
    this._CommonQuote_initializeFromContract(contract);

    this.overwrite(this.text.tick, contract.tick, this.formatDollar);

    this.overwrite(this.text.strike, contract.strike);

    this.overwrite(this.text.cap, contract.cap, this.formatDollar);

    this.overwrite(this.select.meas, contract.meas);
  };

  this._setup = function() {
    this._CommonQuote_setup();

    var components = ['select#meas', 'text#tick', 'text#strike', 'text#cap'];

    this._buildComponents(components);

    this.text.tick.onchange = this._onContractTermChange;
    this.text.tick.pattern = '[0-9,]*[.]?[0-9]*';
    this.text.strike.onchange = this._onContractTermChange;
    this.text.strike.pattern = '[-]?[0-9]*[.]?[0-9]*';
    this.text.cap.onchange = this._onContractTermChange;
    this.text.cap.pattern = '[0-9,]*[.]?[0-9]*';
    this.select.meas.onchange = this._onContractTermChange;
  };

  /* called when station is changed */

  this._stationChanged = function() {
    this._setDisplay(this.div.type, 'block');
    this._state = 'ready';
  };

  /* called when conract term is changed (on each related component) */

  this._onContractTermChange = function() {
    var quoteUI = this._quoteUI;

    quoteUI._contractTermChanged(this);
  }

  /* set fixed quote parameters */

  this._fixQuoteParams = function(quote) {
    quote._dv = 1; // dailyvalue
    quote._dv_t = 0; // dailyref
    quote._dvop = 2;  // operation
    quote._dvop_type = 3;
    quote._week = 3;
  };

  /* performs input validation for pricing.  'source' is the UI component
     that caused validation. */

  this._validate = function(source) {
    var quote = this._quote;

    this._fixQuoteParams(quote);

    quote._meas = this.select.meas.value;
    quote._strike = this.parseFloat(this.text.strike.value);
    quote._tick = this.parseFloat(this.text.tick.value);
    quote._cap = this.parseFloat(this.text.cap.value);

    var days = quote._computeDays();

    if (days <= 0) {
      return "Contract must include at least 1 day.";
    }

    var tick = quote._tick;

    if (isNaN(tick)) {
      return false;
    } else {
      if (tick <= 0) {
        return "Payment must be greater than 0.";
      }
    }

    var strike = quote._strike;

    if (isNaN(strike)) {
      return "";
    } else {
       var status = this._validateTemp(this.text.strike.value);
       if (status) {
         return status;
       }
    }

    if (isNaN(quote._cap)) {
      return "";
    } else if (quote._cap > quote.getMaxCap()) {
      return "Maximum Payout can't exceed $" + this.formatDollar(quote.getMaxCap());
    }

    return null;
  };

  /* called when contract term is changed */

  this._contractTermChanged = function(source) {
    this._hideAlert();

    var message = this._validate(source);
    var isValid = (message == null);

    if ((!isValid) && (message != "")) {
      this._showAlert(message);
    }

    if (this._state != 'idle') {
      this._state = (isValid) ? 'ready' : 'invalid';
    }

    this._modified();
  };
}

/* ColdSeason */

function ColdSeasonQuote(config) {
  HotSeasonQuote.apply(this, arguments);

  this._prefix = 'ColdSeason';

  this._fixQuoteParams = function(quote) {
    quote._dv = 1; // dailyvalue
    quote._dv_t = 0; // dailyref
    quote._dvop = 2;  // operation
    quote._dvop_type = 4;
    quote._week = 3;
  };
};

/* HDD */

function HDDQuote(config) {
  /* import all function from CommonQuote */

  CommonQuote.apply(this, arguments);
  this._CommonQuote_setup = this._setup;
  this._CommonQuote_setDefault = this._setDefault;
  this._CommonQuote_initializeFromContract = this.initializeFromContract;

  this._prefix = 'HDD';

  /* initializes UI component values for reloading */

  this._setDefault = function() {
    this._CommonQuote_setDefault();

    this.select.meas.value = 4; // avgtemp
    this.text.dailyref.value = this._tempUnit.defaultValue;
    this.text.tick.value = '100.00';
    this.text.strike.value = '0';
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };

  this.initializeFromContract = function(contract) {
    this._CommonQuote_initializeFromContract(contract);

    this.overwrite(this.text.dailyref, contract.dv_t);
    this.overwrite(this.text.tick, contract.tick, this.formatDollar);
    this.overwrite(this.text.strike, contract.strike);
    this.overwrite(this.text.cap, contract.cap, this.formatDollar);
    this.overwrite(this.select.meas, contract.meas);

    if ((typeof this.select.dvoptype) != 'undefined') {
      this.overwrite(this.select.dvoptype, contract.dvop_type);
    }
  }

  this._setup = function() {
    this._CommonQuote_setup();

    var components = ['select#meas', 'text#tick', 'text#strike', 'text#cap',
                      'text#dailyref', 'select#dvoptype'];

    this._buildComponents(components);

    this.text.tick.onchange = this._onContractTermChange;
    this.text.tick.pattern = '[0-9,]*[.]?[0-9]*';
    this.text.strike.onchange = this._onContractTermChange;
    this.text.strike.pattern = '[0-9]*[.]?[0-9]*';
    this.text.dailyref.onchange = this._onContractTermChange;
    this.text.dailyref.pattern = '[-]?[0-9]*[.]?[0-9]*';
    this.text.cap.onchange = this._onContractTermChange;
    this.text.cap.pattern = '[0-9,]*[.]?[0-9]*';
    this.select.meas.onchange = this._onContractTermChange;
    if ((typeof this.select.dvoptype) != 'undefined') {
      this.select.dvoptype.onchange = this._onContractTermChange;
    }
  };

  /* called when station is changed */

  this._stationChanged = function() {
    this._setDisplay(this.div.type, 'block');
    this._state = 'ready';
  };

  /* called when conract term is changed (on each related component) */

  this._onContractTermChange = function() {
    var quoteUI = this._quoteUI;

    quoteUI._contractTermChanged(this);
  }

  /* set fixed quote parameters */

  this._fixQuoteParams = function(quote) {
    quote._dv = 2; // dailyvalue
    quote._dvop = 1;  // operation
    quote._dvop_type = 3;
  };

  /* performs input validation for pricing.  'source' is the UI component
     that caused validation. */

  this._validate = function(source) {
    var quote = this._quote;

    this._fixQuoteParams(quote);

    quote._meas = this.select.meas.value;
    quote._dv_t = this.parseFloat(this.text.dailyref.value);
    quote._strike = this.parseFloat(this.text.strike.value);
    quote._tick = this.parseFloat(this.text.tick.value);
    quote._cap = this.parseFloat(this.text.cap.value);
    if ((typeof this.select.dvoptype) != 'undefined') {
      quote._dvop_type = this.select.dvoptype.value;
    }

    var days = quote._computeDays();

    if (days <= 0) {
      return "Contract must include at least 1 day.";
    }

    var tick = quote._tick;

    if (isNaN(tick)) {
      return false;
    }
    else {
      if (tick <= 0) {
        return "Payment must be greater than 0.";
      }
    }

    var strike = quote._strike;

    if (isNaN(strike)) {
      return "";
    }

    if (isNaN(quote._cap)) {
      return "";
    } else if (quote._cap > quote.getMaxCap()) {
       return "The maximum payout of this contract can't exceed " +
              this.formatDollar(quote.getMaxCap());
    }

    if (isNaN(quote._dv_t)) {
      return "";
    } else {
      var status = this._validateTemp(this.text.dailyref.value);
      if (status) {
        return status;
      }
    }

    return null;
  };

  /* called when contract term is changed */

  this._contractTermChanged = function(source) {
    this._hideAlert();

    var message = this._validate(source);
    var isValid = (message == null);

    if ((!isValid) && (message != "")) {
      this._showAlert(message);
    }

    if (this._state != 'idle') {
      this._state = (isValid) ? 'ready' : 'invalid';
    }

    this._modified();
  };
}

/* Frost */

function FrostQuote(config) {
  /* import all function from HDDQuote */

  HDDQuote.apply(this, arguments);
  this._HDDQuote_setDefault = this._setDefault;
  this._HDDQuote_validate = this._validate;

  this._prefix = 'Frost';

  /* set fixed quote parameters */

  this._fixQuoteParams = function(quote) {
    quote._dv = 5; // dailyvalue
    quote._dvop = 1;  // operation
    quote._dvop_type = 1;
    quote._week = 3;
  };

  this._setDefault = function() {
    this._HDDQuote_setDefault();

    this.select.meas.value = 2; // maxtemp
    this.text.dailyref.value = this._tempUnit.zero;
    this.text.tick.value = '100.00';
    this.text.strike.value = '0';
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };

  this._validate = function() {
    this.text.cap.value = this.formatDollar(this.text.tick.value);
    return this._HDDQuote_validate();
  }
}

/* CDD */

function CDDQuote(config) {
  /* import all function from HDDQuote */

  HDDQuote.apply(this, arguments);

  this._prefix = 'CDD';

  this._fixQuoteParams = function(quote) {
    quote._dv = 1; // dailyvalue
    quote._dvop = 1;  // operation
    quote._dvop_type = 3;
  };
}

/* HotDay */

function HotDayQuote(config) {
  HDDQuote.apply(this, arguments);
  this._HDDQuote_setDefault = this._setDefault;

  this._prefix = 'HotDay';

  this._fixQuoteParams = function(quote) {
    quote._dv = 4;
    quote._dvop = 1;
    quote._dvop_type = 3;
  };

  this._setDefault = function() {
    this._HDDQuote_setDefault();

    this.select.meas.value = 4; // avgtemp
    this.text.dailyref.value = this._tempUnit.defaultValue;
    this.text.tick.value = '100.00';
    this.text.strike.value = '0';
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };

  this._validateDailyRef = function() {
    return this._validateTemp(this.text.dailyref.value);
  };

  this._validate = function(source) {
    var quote = this._quote;

    this._fixQuoteParams(quote);

    quote._strike = this.parseFloat(this.text.strike.value);
    quote._tick = this.parseFloat(this.text.tick.value);
    quote._cap = this.parseFloat(this.text.cap.value);
    quote._meas = this.select.meas.value;

    var days = quote._computeDays();

    if (days <= 0) {
      return "Contract must include at least 1 day.";
    }

    var tick = quote._tick;

    if (isNaN(tick)) {
      return false;
    } else {
      if (tick <= 0) {
        return "Payment increment must be greater than 0.";
      }
    }

    var strike = quote._strike;

    if (isNaN(strike)) {
      return "";
    } else {
      if (strike < 0) {
        return "Days before payout begins cannot be negative.";
      } else if (strike >= days) {
        return "Number of days before payout begins must be less than total" +
               " number of days in contract. (" + days + " day" +
               ((days > 1) ? "s" : "") + ")";
      }
    }

    var maxCap = Math.round((days - strike) * tick * 100) / 100;

    if (maxCap >= quote.getMaxCap()) {
      maxCap = quote.getMaxCap();
    }

    if (source == this.text.cap) {
      if (isNaN(quote._cap) || isNaN(maxCap)) {
        return "";
      }
      else if (quote._cap > maxCap)
      {
        var returnVal = "The maximum payout of this contract can't exceed "
        + this.formatDollar(maxCap);

        if (maxCap < quote.getMaxCap()) {
          returnVal += " (" + days + " days x $" + this.formatDollar(tick) +
                       " per day"
          /*optional strike message*/
          + ((strike > 0)?
            ", beginning after " + strike + ((strike>1)? " days" : " day")
            : "")
          + ")";
        }
        return returnVal;
      }
    } else {
      if (isNaN(maxCap)) {
        maxCap = '';
      }
      quote._cap = maxCap;
      this.text.cap.value = this.formatDollar(maxCap);
    }

    quote._dv_t = this.parseFloat(this.text.dailyref.value);

    return this._validateDailyRef();
  };
}

/* ColdDay */

function ColdDayQuote(config) {
  HotDayQuote.apply(this, arguments);
  this._prefix = 'ColdDay';

  this._fixQuoteParams = function(quote) {
    quote._dv = 5;
    quote._dvop = 1;
    quote._dvop_type = 3;
  };
}

/* RainyDay */

function RainyDayQuote(config) {
  HotDayQuote.apply(this, arguments);
  this._HotDayQuote_setDefault = this._setDefault;
  this._HotDayQuote_setup = this._setup;

  this._prefix = 'RainyDay';

  this._setup = function() {
    this._HotDayQuote_setup();
    this.text.dailyref.pattern = '[0-9]*[.]?[0-9]*';
  }

  this._setDefault = function() {
    this._HotDayQuote_setDefault();

    this.select.meas.value = 1; // precip
    this.text.dailyref.value = this._precipUnit.defaultDaily;
    this.text.tick.value = '100.00';
    this.text.strike.value = '0';
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };

  this._fixQuoteParams = function(quote) {
    quote._dv = 4;
    quote._dvop = 1;
    quote._dvop_type = 3;
    quote._meas = 1;
  };

  this._validateDailyRef = function() {
    return this._validatePrecip(this.text.dailyref.value);
  };
}

/* DryDay */

function DryDayQuote(config) {
  RainyDayQuote.apply(this, arguments);

  this._prefix = 'DryDay';

  this._fixQuoteParams = function(quote) {
    quote._dv = 5;
    quote._dvop = 1;
    quote._dvop_type = 3;
    quote._meas = 1;
  };

  this._setDefault = function() {
    this._HotDayQuote_setDefault();

    this.select.meas.value = 1; // precip
    this.text.dailyref.value = this._precipUnit.defaultDaily / 5;
    this.text.tick.value = '100.00';
    this.text.strike.value = '0';
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };
}

function SnowSeasonQuote(config) {

  CommonQuote.apply(this, arguments);
  this._CommonQuote_setup = this._setup;
  this._CommonQuote_setDefault = this._setDefault;
  this._CommonQuote_initializeFromContract = this.initializeFromContract;
  this._CommonQuote_showCalendarPopup = this._showCalendarPopup

  this._prefix = 'SnowSeason';

  /* initializes UI component values for reloading */

  this._setDefault = function() {
    this._CommonQuote_setDefault();
    this.text.tick.value = '100.00';
    this.text.strike.value = 5;
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };

  this.initializeFromContract = function(contract) {
    this._CommonQuote_initializeFromContract(contract);
    this.overwrite(this.text.tick, contract.tick, this.formatDollar);
    this.overwrite(this.text.strike, contract.strike);
    this.overwrite(this.text.cap, contract.cap, this.formatDollar);

  }

  this._setup = function() {
    this._CommonQuote_setup();

    var components = ['text#tick', 'text#strike', 'text#cap',
                      'text#dailyref', 'select#payoutType'];
    this._buildComponents(components);
    this.text.tick.onchange = this._onContractTermChange;
    this.text.tick.pattern = '[0-9,]*[.]?[0-9]*';
    this.text.strike.onchange = this._onContractTermChange;
    this.text.strike.pattern = '[0-9]*[.]?[0-9]*';
    this.text.cap.onchange = this._onContractTermChange;
    this.text.cap.pattern = '[0-9,]*[.]?[0-9]*';
    this.select.payoutType.onchange = this._onContractTermChange;

    /* Push the start date to 10 days out for snow contracts. */
    var today = this._quote._getToday();
    var firstDate = this._addDays(today, SNOW_STARTDAY_SHIFT);

    /* note that this variable with underscore is required for setting the
    selected date on the calendar */
    this._quote._startDate = firstDate;

    this.date.sd.firstDate = firstDate;
    /* last permitted date for startDate is MAX_FUTURE_STARTDATE from today */
    this.date.sd.lastDate = this._addDays(today, MAX_FUTURE_STARTDATE - 1);
    this.date.ed.firstDate = this.date.sd.firstDate;

    /* last permitted date for endDate is MAX_CONTRACT_LENGTH days from sd*/
    this.date.ed.lastDate = this._addDays(this.date.ed.firstDate,
                                          MAX_CONTRACT_LENGTH - 1);

  };

  /* called when station is changed */

  this._stationChanged = function() {
    this._setDisplay(this.div.type, 'block');
    this._state = 'ready';
  };

  /* called when contract term is changed (on each related component) */

  this._onContractTermChange = function() {
    var quoteUI = this._quoteUI;
    quoteUI._dvop_type = quoteUI.select.payoutType.value;
    quoteUI._strike = quoteUI.text.strike.value;
    quoteUI._contractTermChanged(this);
  }

  /* set fixed quote parameters */

  this._fixQuoteParams = function(quote) {
    quote._meas = 5; /* data */
    quote._dv = 1; /* daily value */
    quote._dv_t = 0; /* dailyref */
    quote._dvop = 1; /* operation */
    quote._dvop_type = 3;
    quote._week = 3;
  };

  /* performs input validation for pricing.  'source' is the UI component
     that caused validation. */

  this._validate = function(source) {
    var quote = this._quote;

    this._fixQuoteParams(quote);
    quote._dvop_type = this.parseFloat(this.select.payoutType.value);
    quote._strike = this.parseFloat(this.text.strike.value);
    quote._tick = this.parseFloat(this.text.tick.value);
    quote._cap = this.parseFloat(this.text.cap.value);
    var days = quote._computeDays();

    if (days <= 0) {
      return "Contract must include at least 1 day.";
    }

    var tick = quote._tick;
    if (isNaN(tick)) {
      return false;
    } else if (tick <= 0) {
      return "Payment must be greater than 0.";
    }
    var strike = quote._strike;

    var validateStrike = this._validateStrike(strike);
    if (! validateStrike) {
      return validateStrike;
    }

    if (isNaN(quote._cap)) {
      return "";
    } else if (quote._cap > quote.getMaxCap()) {
      return "The maximum payout of this contract can't exceed " +
              this.formatDollar(quote.getMaxCap());
    }

  };

  /* called when contract term is changed */

  this._contractTermChanged = function(source) {
    this._hideAlert();

    var message = this._validate(source);
    var isValid = (message == null);

    if ((!isValid) && (message != "")) {
      this._showAlert(message);
    }

    if (this._state != 'idle') {
      this._state = (isValid) ? 'ready' : 'invalid';
    }

    this._modified();
  };

  /* function to validate strike */
  this._validateStrike = function(strike) {
    if (isNaN(strike)) {
      return "";
    } else {
      return strike >= 0;
    }
  };

}

/* SnowSeasonBinary */

function SnowSeasonBinaryQuote(config) {
  SnowSeasonQuote.apply(this, arguments);
  this._prefix = 'SnowSeasonBinary';

  /* keep a handle on the superclass's validate because we'll need it below */
  this._snowSeason_validate = this._validate;

  this._fixQuoteParams = function(quote) {
    quote._meas = 5;      /* data */
    quote._dv = 1;        /* dailyvalue */
    quote._dv_t = 0;      /* dailyref */
    quote._dvop = 1;      /* operation */
    quote._dvop_type = 1; /* binary above */
    quote._week = 3;
  };

  this._validate = function(source) {
    this._snowSeason_validate();
    this._quote._cap = this._quote._tick;
  }


}

/* Snow Day */

function SnowDayQuote(config) {
  SnowSeasonQuote.apply(this, arguments);
  this._SnowSeasonQuote_setDefault = this._setDefault;
  this._SnowSeasonQuote_setup = this._setup;
  this._SnowSeasonQuote_validateStrike = this._validateStrike;
  this._SnowSeasonQuote_validate = this._validate;

  this._prefix = 'SnowDay';

  /* general setup function*/
  this._setup = function() {
    this._SnowSeasonQuote_setup();
    this.text.dailyref.value = this._snowfallUnit.defaultDaily;
    this.text.dailyref.onchange = this._onContractTermChange;
    this.text.dailyref.pattern = '[-]?[0-9]*[.]?[0-9]*';
  }

  /* setting defaults */
  this._setDefault = function() {
    this._SnowSeasonQuote_setDefault();
    this.text.dailyref.value = this._snowfallUnit.defaultDaily;
    this.text.tick.value = '100.00';
    this.text.strike.value = '0';
    this.text.cap.value = this.formatDollar(DEFAULT_CAP);
  };

  /* fixed params for snowday contracts*/
  this._fixQuoteParams = function(quote) {
    quote._dv = 4;
    quote._dvop = 1;
    quote._dvop_type = 3;
    quote._meas = 5;
  };

  this._validateDailyRef = function() {
    return this._validateSnowfall(this.text.dailyref.value);
  };

  this._onContractTermChange = function() {
    var quoteUI = this._quoteUI;
    quoteUI._dv_t = quoteUI.text.dailyref.value;
    quoteUI._strike = quoteUI.text.strike.value;
    quoteUI._contractTermChanged(this);
  }

  /* function to validate strike - note this will be different for
   * snowday and snowseason contracts*/
  this._validateStrike = function(strike, days) {
    if (isNaN(strike)) {
      return "";
    } else if (strike < 0) {
      return "";
    } else if (strike >= days) {
        return "Number of days before payout begins must be less than total" +
               " number of days in contract. (" + days + " day" +
               ((days > 1) ? "s" : "") + ")";
    } else {
      return "true";
    }
  };

  /* performs input validation for pricing.'source' is the UI component
     that caused validation. */

  this._validate = function(source) {
    var quote = this._quote;

    this._fixQuoteParams(quote);

    quote._dv_t = this.parseFloat(this.text.dailyref.value);
    quote._dv = this.parseFloat(this.select.payoutType.value);
    quote._strike = this.parseFloat(this.text.strike.value);
    quote._tick = this.parseFloat(this.text.tick.value);
    quote._cap = this.parseFloat(this.text.cap.value);
    var days = quote._computeDays();

    if (days <= 0) {
      return "Contract must include at least 1 day.";
    }

    var tick = quote._tick;

    if (isNaN(tick)) {
      return false;
    } else if (tick <= 0) {
      return "Payment must be greater than 0.";
    }
    var strike = quote._strike;

    var validateStrike = this._validateStrike(strike, days);
    if (validateStrike != "true") {
      return validateStrike;
    }

    if (isNaN(quote._cap)) {
      return "";
    } else if (quote._cap > quote.getMaxCap()) {
      return "The maximum payout of this contract can't exceed " +
              this.formatDollar(quote.getMaxCap());
    }

    var maxCap = Math.round((days - strike) * tick * 100) / 100;

    if (maxCap >= quote.getMaxCap()) {
      maxCap = quote.getMaxCap();
    }

    if (source == this.text.cap) {
      if (isNaN(quote._cap) || isNaN(maxCap)) {
        return "";
      }
      else if (quote._cap > maxCap)
      {
        var returnVal = "The maximum payout of this contract can't exceed "
        + this.formatDollar(maxCap);

        if (maxCap < quote.getMaxCap()) {
          returnVal += " (" + days + " days x $" + this.formatDollar(tick) +
                       " per day"
          /*optional strike message*/
          + ((strike > 0)?
            ", beginning after " + strike + ((strike>1)? " days" : " day")
            : "")
          + ")";
        }
        return returnVal;
      }
    } else {
      if (isNaN(maxCap)) {
        maxCap = '';
      }
      quote._cap = maxCap;
      this.text.cap.value = this.formatDollar(maxCap);
    }

    return this._validateDailyRef();
  };
}

/* RainyEventBinary ( Hourly ) */

function RainyEventBinaryQuote(config) {

  RainySeasonQuote.apply(this, arguments);
  this._prefix = 'RainyEventBinary';

  this._setup = function() {
    var components = ['div#date', 'div#location', 'div#type',
                      'div#hist_payouts', 'div#suggest', 'div#suggest_txt',
                      'div#hist_payouts_txt',
                      'date#sd', 'select#sh', 'select#eh', 'span#hours',
                      'span#next_day_message', 'span#crosses_dst_message',
                      'text#zip', 'submit#showMap', 'select#rmsid',
                      'select#country', 'select#currency', 'span#locationOr',
                      'span#price', 'img#wait', 'img#getPrice', 'img#buyNow',
                      'form#location', 'form#main', 'text#tick',
                      'radio#totalRainfall', 'text#strike', 'select#precipunit',
                      'span#precipunit', 'radio#rainyHours',
                      'text#hourlyStrike', 'text#hv_t', 'select#precipunit2'];

    this._buildComponents(components);

    /* uses common alert instead of individual ones */

    this.div.alert = document.getElementById('Common_div_alert');
    this.div.alert.onclick = function(){this._quoteUI._hideAlert()};

    this.date.sd.onclick = this._showCalendarPopup;
    this.date.sd.pattern = '[0-9][0-9]/[0-9][0-9]/[0-9][0-9]';

    this.date.sd.onchange  = this._onDateTimeChange;
    this.select.sh.onchange = this._onDateTimeChange;
    this.select.eh.onchange = this._onDateTimeChange;

    this.submit.showMap.onclick = this._showMap;
    if (this.form.location) { this.form.location.onsubmit = this._showMap; }
    this.text.zip.onchange = function(){};
    this.text.zip.pattern = '[0-9A-Za-z ]*';

    this.select.country.onchange = this._onCountryChange;
    this.select.currency.onchange = this._onCurrencyChange;
    this.select.rmsid.onchange = this._onStationChange;

    var today = this._quote._getToday();
    var firstDate = this._addDays(today, this._quote.getStartDateShift());

    /* _startDate is required for setting the selected date on the calendar */
    this._quote._startDate = firstDate;
    this.date.sd.firstDate = firstDate;

    /* last permitted startDate is MAX_RAINY_EVENT_FUTURE_STARTDATE from today*/
    this.date.sd.lastDate = this._addDays(today,
                                          MAX_RAINY_EVENT_FUTURE_STARTDATE - 1);

    this.text.tick.onchange = this._onContractTermChange;
    this.text.tick.pattern = '[0-9,]*[.]?[0-9]*';

    this.radio.totalRainfall.onclick = this._onRainfallRadioClick;

    /* strike must be >= 0 if not empty */
    this.text.strike.regex = /^[0-9]*[.]?[0-9]*$/;
    this.text.strike.onchange = this._onContractTermChange;

    this.radio.rainyHours.onclick = this._onRainfallRadioClick;

    this.text.hourlyStrike.onchange = this._onContractTermChange;
    /* hourlyStrike must be gte 0 if not empty */
    this.text.hourlyStrike.regex = /^(([0-9])+[0-9]*)*$/;

    this.text.hv_t.onchange = this._onContractTermChange;
    /* hv_t must be >= 0 if not empty */
    this.text.hv_t.regex = /^[0-9]*[.]?[0-9]*$/;

    this.img.getPrice.onclick = this._onGetHourlyPrice;
    this.img.buyNow.onclick = this._onBuyNow;

    this.form.main.onsubmit = null;

    /* pre-select totalRainfall radio if neither payout radio is selected */
    if (!this.radio.rainyHours.checked && !this.radio.totalRainfall.checked) {
      this.radio.totalRainfall.checked = true;
    }
    /* update payout field display */
    this._updateRainfallFields();
  };


  this._setDefault = function()
  {
    this._CommonQuote_setDefault();

    this.text.tick.value = '100.00';
    this.text.hourlyStrike.value = '0';
    this.text.strike.value = '0.5';
    this.text.hv_t.value = '0.25';
  };


  /**
   * @method _ computerHours
   * @param int | string start
   * @param int | string end
   * @return int hours
   * Calculates difference between start and end hours for given day.  Parses
   * int values from start and end params which may be string values.
   * Where start is greater than end, assumes end hours are time for next day.
   */
  this._computeHours = function(start, end) {
    var hours = null;
    start = parseInt(start, 10);
    end = parseInt(end, 10);
    hours = end - start;
    if(hours <=0) {
      hours += 24;
    }
    return hours;
  }

  this._onDateTimeChange = function() {

    if ((typeof this.valueOriginal) != 'undefined') {
      this.value = this.valueOriginal;
      this.valueOriginal = undefined;
    }

    var quoteUI = this._quoteUI;

    /* date text element */
    if (this == quoteUI.date.sd) {
      quoteUI._quote._startDate = parseDate(this.value);
      this.selectedDate = quoteUI._quote._startDate;
      quoteUI._setValue(quoteUI.date.sd, formatDate(quoteUI._quote._startDate));
    }

    /* start/end hours select elements */
    if(this == quoteUI.select.sh) {
      quoteUI._quote._shour = quoteUI.select.sh.value;
      if(quoteUI._quote._ehour == undefined) {

        /* set end hours if not yet set by user using end = start + 1hour */
        var end = parseInt(quoteUI._quote._shour, 10) + 1;

        /* if greater than 11 PM, subtract 24 hours to span into next day */
        if(end > 23 ){
          end = end - 24;
        }
        quoteUI.select.eh.value = end.toString();
      }
    }
    if(this == quoteUI.select.eh) {
      quoteUI._quote._ehour = quoteUI.select.eh.value;
    }

    /* call helper method for hours (duration) */
    var hours = this._quoteUI._computeHours(quoteUI.select.sh.value,
                                            quoteUI.select.eh.value);

    /* write span and save duration if hours are numeric */
    if(!isNaN(hours)) {
      quoteUI._setInnerHTML(quoteUI.span.hours, '(' + hours + ' hour' +
                            ((hours != 1) ? 's' : '') + ')');
      quoteUI._quote._hdur = hours;

      /* write next day span message if end hour is lte start hour */
      /* use base 10 in parseInt to insure against octal conversion of "08" */
      if(parseInt(quoteUI.select.eh.value, 10) <=
         parseInt(quoteUI.select.sh.value, 10)) {
        quoteUI._setInnerHTML(quoteUI.span.next_day_message,
                              "Contract spans into next day.");
        quoteUI._setDisplay(quoteUI.span.next_day_message, 'block');
      } else {
        /* hide next day span if end hour is gt start hour */
        quoteUI._setDisplay(quoteUI.span.next_day_message, 'none');
      }
    }

    /**
     * Show the Location div if:
     *  start date has been selected by user
     *  hourly duration is > 0,
     *  start date field value is valid.
     */
    var dateMessage = quoteUI._validateDate(quoteUI.date.sd);
    if (this.selectedDate != null && quoteUI._quote._hdur > 0 &&
        !dateMessage ) {
      quoteUI._setDisplay(quoteUI.div.location, 'block');
    }

    /* this calls DrySeason::RainySeason._contractTermChanged */
    quoteUI._contractTermChanged(this);
  };


  /* disable/enable fields based on radio states */

  this._updateRainfallFields = function() {

    var totalRainfall = this.radio.totalRainfall;
    var rainyHours = this.radio.rainyHours;

    /* fields in rainyHours */
    this.text.hourlyStrike.disabled = !rainyHours.checked;
    this.text.hv_t.disabled = !rainyHours.checked;
    this.select.precipunit2.disabled = !rainyHours.checked;

    /* fields in totalRainfall */
    this.text.strike.disabled = !totalRainfall.checked;
    this.select.precipunit.disabled = !totalRainfall.checked;
  }


  this._onRainfallRadioClick = function() {

    var quoteUI = this._quoteUI;

    quoteUI._updateRainfallFields();

    /* this calls DrySeason::RainySeason._contractTermChanged */
    quoteUI._contractTermChanged(this);
  }


 /**
    When a quote is done for Binary Season contracts, set the cap == tick
    (since binary contracts pay out a single tick or 0).
  */
  this._validate = function(source) {

    var quote = this._quote;
    var valid = null;

  /* set this explicitly for restore() to work correctly */
    quote._currency = this.select.currency.value;

    /* validate start date if user has selected or changed it. */
    if (this.date.sd.selectedDate != null) {
      var message = this._validateDate(this.date.sd);
      if (message!=null) {
        return message;
      } else {
        /* save _startDate explicitly for better code readability */
        quote._startDate = parseDate(this.date.sd.value);
      }
    }

    /* save start hour explicitly for restore() to work correctly */
    quote._shour = this.select.sh.value;

    /* get hours - duration */
    if (quote._hdur != null && quote._hdur <= 0) {
      return "Contract duration must be least one hour.";
    }

    /* tick */
    quote._tick = this.parseFloat(this.text.tick.value);
    if (isNaN(quote._tick)) {
      return "";
    } else if (quote._tick > quote.getMaxCap()) {
      return "Maximum Payout can't exceed " +
             this.formatDollar(quote.getMaxCap());
    } else if (quote._tick <= 0.0) {
      return "Maximum payout cannot be 0.";
    }

    /* if total rainfall radio is checked, get strike and precipunit */
    if (this.radio.totalRainfall.checked) {
      /* validate the precipunit select for IE7 -- must have a selected value*/
      quote._unit = this.select.precipunit.value;
      if(quote._unit == null || quote._unit == "") {
        return "";
      }
      quote._hv_t = 0;
      quote._hv = 1;
      valid = this.text.strike.regex.test(this.text.strike.value);
      if (!valid) {
        return "Total rainfall must be greater than or equal to 0.";
      }
      if (isNaN(this.text.strike.value) ||
          this.text.strike.value=="" ) {
        /* bail out of validation if non-numeric value passes regex */
        return "";
      }
      /* ok to set strike */
      quote._strike = this.text.strike.value;
    }

    /* if rainy hours radio checked, get hourlyStrike, hv_t and precipunit2 */
    if (this.radio.rainyHours.checked) {
      quote._unit = this.select.precipunit2.value;
      quote._hv = 4;
      var hourlyStrike = this.text.hourlyStrike.value;
      valid =
          this.text.hourlyStrike.regex.test(hourlyStrike);
      if (!valid) {
        return "Rainy hours must be a whole number greater than or equal to 0.";
      }
      if(hourlyStrike >= quote._hdur) {
        return "Rainy hours must be less than contract duration " +
            "(" + quote._hdur + " hour" + (quote._hdur != 1 ? "s" : "") + ")."
      }
      if (isNaN(this.text.hourlyStrike.value) ||
          this.text.hourlyStrike.value=="" ) {
        /* bail out of validation if non-numeric value passes regex */
        return "";
      }
      /* ok to set strike */
      quote._strike = this.text.hourlyStrike.value;

      /* hourly threshold value */
      valid = this.text.hv_t.regex.test(this.text.hv_t.value);
      if(!valid) {
        return "Hourly rainfall must be greater than or equal to 0.";
      }
      if( valid && isNaN(this.text.hv_t.value) ||
          this.text.hv_t.value=="" ) {
        /* bail out of validation if non-numeric value passes regex */
        return "";
      }
      /* ok to set hv_t */
      quote._hv_t = this.text.hv_t.value;
    }

    /* null means success */
    return null;
  };


  this._modified = function() {
    if (this._state != 'idle') {
      if (this._state != 'invalid') {
        this._state = 'ready';
      } else {
        if ((this.date.sd.value == DATEFORMAT) ||
            /*(this.date.ed.value == DATEFORMAT) ||*/
            (this.select.rmsid.value == '')) {
          this._state = 'invalid';
        }
      }
    }

    /* empty out contract and price */
    this._price = '';
    this._contractId = '';

    /* hide the suggestion and historical payout */
    this._hideSuggestion();
    this._hideHistPayouts();
    this._showActions();

    /* save data state */
    var state = this._getState();
    var quoteValues = {};
    var keyList = [ '_startDate', '_shour', '_hdur', '_zip', '_rmsid',
                    '_currency', '_tick', '_strike', '_hv_t', '_hv', '_unit'];

    for (var i = 0; i < keyList.length; i ++) {
      var key = keyList[i];
      quoteValues[key] = this._quote[key];
    }

    state['quote'] = quoteValues;
    state['state'] = this._state;

    /* save this explicitly for restore() to set precip value correctly */
    state['precipUnitName'] = this._quote['_unit'];

    saveState();
  }


  this._onGetHourlyPrice = function() {
    if (this._quoteUI._state == 'ready') {
      this._quoteUI._setDisplay(this, 'none');
      this._quoteUI._setDisplay(this._quoteUI.img.wait, 'inline');
      this._quoteUI._getHourlyPrice();
    }
    return false;
  }


  /* initiates ajax call for current _quote object to get price */

  this._getHourlyPrice = function() {

    try {
        var params = this.createParameterString();
    } catch(err) {
      this._showAlert( "Error during request process:\n" + err.message );
      return false;
    }

    if (this._isDebug) {
      var debugDiv = document.getElementById('Common_div_debug');
      if ((typeof debugDiv) != 'undefined') {
        this._STARTED = (new Date()).getTime();
        var url = this._getPriceURL + '?' + params.replace(/&/g, '&amp;');
        debugDiv.innerHTML = '<a href="' + url + '">' + url + '</a>';
      }
    }

    if (SUBMITASYNC) {
      this._xmlhttpPost(this._getPriceURL, this._doneGetPrice, params);
    }
    else {
      document.location.href = this._getPriceURL + '?' + params;
    }
  }

  this.createParameterString = function() {
    var startDate = this._quote._startDate;
    var s_mo = getMonthName(startDate);
    var s_dy = startDate.getDate();
    var s_yr = startDate.getFullYear();

    var shour = this._quote._shour;

    /* hourly duration */
    var hdur = this._quote._hdur;

    var zip = this._quote._zip
    var rmsid = this._quote._rmsid;

    /* payout terms */
    var currency = this._currency;
    var tick = this._quote._tick;
    var strike = this._quote._strike;
    var hv_t = this._quote._hv_t;
    var hv = this._quote._hv;
    var unit = this._quote._unit;

    var params = 's_mo=' + s_mo + '&s_dy=' + s_dy + '&s_yr=' + s_yr +
                 '&shour=' + shour + '&hdur=' + hdur +
                 '&postcode=' + zip + '&rmsid=' + rmsid +
                 '&currency=' + currency +
                 '&tick=' + tick + '&strike=' + strike + '&hv=' + hv +
                 '&hv_t=' + hv_t + '&unit=' + unit +
                 '&portfolioid=1' +
                 '&type=' + this._prefix + '&interval=hourly';

    return params;
  }

}
