/*
Script: DualSlider.js
  This contains a slider implmentation that supports two knobs.
  Based loosely off the slider implementation at http://www.phatfusion.net/slider/ 
  Options: onChange, steps, offset, crossCheck
  
*/

var DualSlider = new Class({

  options: {
	onChange: Class.empty,
	onComplete: Class.empty,
	steps: 4,
	offset: 0,
	crossCheck: false,
	round: true,
	name: 'dualSlider1'
  },

  initialize: function(el, knobOne, knobTwo, options){
    this.element = $(el);
	this.z = 'x';
	this.p = 'left';
	var mod = {'x': 'left', 'y': false};
	var offset = 'offsetWidth';
	var lim = {};
	
	//knobs are the same dimensions, so max and half will be the same for both
	this.max = this.element[offset] - $(knobOne)[offset] + (this.options.offset * 2);
	this.half = $(knobOne)[offset]/2;
	lim[this.z] = [- this.options.offset, this.max];
		
	if(this.options.crossCheck)
	{
	  this.element.addEvent('mousedown', this.clickedElement.bindWithEvent(this));
	}
	this.getPos = this.element['get' + this.p.capitalize()].bind(this.element);
	this.setOptions(options);
	this.knobOne = this.initKnob($(knobOne), lim, mod, 1);
	this.knobTwo = this.initKnob($(knobTwo), lim, mod, 2);
	
	if (this.options.initialize) this.options.initialize.call(this);
	
  },
	
  initKnob: function(knob, lim, mod, num){
	//if a mousedown event is attached to the knob, remove it - bug 27330
	try{
	  if(typeof knob.drag != "undefined" && typeof knob.drag.detach != "undefined")
	  {
	    knob.drag.detach();
	  }
	}catch(e){ }
	
	knob.drag = this.setDragBase(knob, lim, mod);
	knob.step = -1;
	knob.previousChange = -1;
	knob.previousEnd = -1;
	knob.num = num;
	knob.setStyle('position', 'relative').setStyle(this.p, - this.options.offset);
	return knob;
  },
	
  setDragBase: function(knob, lim, mod){
	return new Drag.Base(knob, {
	  limit: lim,
	  modifiers: mod,
	  snap: 0,
	  onDrag: function(){
		this.draggedKnob(knob);
	  }.bind(this),
	  onComplete: this.fireEvent.bind(this, 'onComplete')
	});
  },
  
  setKnobFromValue: function(knob, newValue, min, max, totalSteps){
    
    var newStep = ( (newValue - min) * (totalSteps - 1) ) / (max - min);
    this.setKnob(knob, newStep, false);
  },
	
  setKnob: function(knob, newStep, doCrossCheck){
	newStep = newStep.limit(0, this.options.steps);
	if(doCrossCheck)
	{
	  if(knob.num == 1)
	  {
	    if (newStep >= this.knobTwo.step)
	  	  newStep = this.knobTwo.step - 1;
	  }
	  else if(knob.num == 2)
	  {
	    if (newStep <= this.knobOne.step)
	  	  newStep = this.knobOne.step + 1;
	  }
	}
	
	knob.step = newStep;
	this.checkStep(knob);
	knob.setStyle(this.p, this.toPosition(knob.step)+'px');
	return this;
  },

  /*
   * Reset the knobs to their original values
   * @returns - true if the knobs have changed, false else
   */
  resetKnobs:function()
  {
      //  grab the original state
      var was = [this.sliderOptions.knobs[0].step,
                 this.sliderOptions.knobs[1].step];
      
      //  reset the knobs
      this.setKnobFromValue(this.sliderOptions.knobs[0], this.sliderOptions.min, this.sliderOptions.min, this.sliderOptions.max, this.sliderOptions.step);
      this.setKnobFromValue(this.sliderOptions.knobs[1], this.sliderOptions.max, this.sliderOptions.min, this.sliderOptions.max, this.sliderOptions.step);
      
      //  check if they have changed
      return ((was[0] != this.sliderOptions.knobs[0].step) ||
              (was[1] != this.sliderOptions.knobs[1].step));
  },
  
  /*
   * Set the knobs to values given in a string with the format "min,max"
   */
  setKnobs:function(/* String */ str)
  {
      var prc = str.split(',');
      this.setKnobFromValue(this.sliderOptions.knobs[0], parseFloat(prc[0]), this.sliderOptions.min, this.sliderOptions.max, this.sliderOptions.step);
      this.setKnobFromValue(this.sliderOptions.knobs[1], parseFloat(prc[1]), this.sliderOptions.min, this.sliderOptions.max, this.sliderOptions.step);
  },

  clickedElement: function(event){
	var position = event.page[this.z] - this.getPos() - this.half;
    position = position.limit(-this.options.offset, this.max -this.options.offset);
    var newStep = this.toStep(position);
	
	if(this.knobOne.step >= newStep)
    {
      this.setKnob(this.knobOne, newStep, this.options.crossCheck);
    }
    else if(this.knobTwo.step <= newStep)
    {
      this.setKnob(this.knobTwo, newStep, this.options.crossCheck);
    } 
		
  },

  draggedKnob: function(knob){
  	var newStep = this.toStep(knob.drag.value.now[this.z]);
  	this.setKnob(knob, newStep, this.options.crossCheck);
  },

  checkStep: function(knob){
    if (knob.previousChange != knob.step){
	  knob.previousChange = knob.step;
	  this.fireEvent('onChange');
	}
	
  },

  toStep: function(position){
	return Math.round((position + this.options.offset) / this.max * this.options.steps);
  },

  toPosition: function(step){
	return this.max * step / this.options.steps;
  }

});

DualSlider.implement(new Events, new Options);
/*
 * Must match the order of items in CurrencyCriteria. used to translate
 * indexes to currency codes - so that changes to currency criteria can
 * repopulate prices in the max rate filter
 */
var currencyCodes = new Array ("USD", "EUR", "GBP", "CAD", "AUD", "CHF", "JPY", "RMB", "INR", "SEK", "BRL");

var lastCompleted = -1;
var requestsSinceCompletedChange = 0;
var updateRequestLimit = 5; // how many update requests will we make if 'complete' doesn't change

/*
 * Set the rates slider dimensions according to the currently selected
 * currency and periodicity
 */
function setRatesSliderDimensions()
{
  //  get the selected currency values from the currency hash
  var crn = $("l1currency");
  if (!crn) return; //  if there is no rate info then there is no slider, so just return
  var currencyVals = currencyHash[parseInt(crn.value)];

  //  if the weekly price period is selected then use the weekly hash instead
  var pricePeriodWeekly = $('pricePeriod_w');
  if ((pricePeriodWeekly != null) && pricePeriodWeekly.checked)
  {
      currencyVals = weeklyCurrencyHash[parseInt(crn.value)];
  }

  //  set the new slider dimensions
  var prc = $('priceSelect');
  if(prc)
  {
    var offset = /off(\d+)/.test(prc.className) ? RegExp.$1 : false;
    prc.setProperty('class', '')
      .addClass('s' + currencyVals.steps)
      .addClass('mn' + currencyVals.min)
      .addClass('mx' + currencyVals.max)
      .addClass('dualSliderTest')
      .addClass('round')
      .addClass('namepslider')
      .addClass('cur' + parseInt(crn.value));
    if(currencyVals.maxAllInclusive) {
        prc.addClass('maxAllInclusive');
    }

    // selected min and max may have been stored by location hash deserialization
    // if so, use those instead of server values - bug 37881
    if (ta.has('hacform.slider.price.min')) {
      prc.addClass('smin' + ta.remove('hacform.slider.price.min'));
    } else {
      prc.addClass('smin' + currencyVals.selMin);
    }
    if (ta.has('hacform.slider.price.max')) {
      prc.addClass('smax' + ta.remove('hacform.slider.price.max'));
    } else {
      prc.addClass('smax' + currencyVals.selMax);
    }

    if (offset) prc.addClass('off' + offset);
      
    //  apply the slider rule
    dualSliderRule(prc);
  }
}

var resultsUpdatedCallback;

var resultsUpdated = function(results) {
  if (window.filterFormUpdates && parseInt(filterFormUpdates.sequence) <= responseSequenceID) {
    window.filterFormUpdates = null;
    ta.servlet.HACSearch.getFilterWaitOverlay().hide();
    return; // old response, ignore
  }
  //  else if we have a filter change queued up no reason to update the screen
  //  until that one finishes
  else if (filtersChangedTimeout)
  {
      hacXhrInFlight = false;
      return;
  }

  if (window.filterFormUpdates) {
    responseSequenceID = parseInt(filterFormUpdates.sequence);
  }
  var resultsDiv = $('HAC_RESULTS') || $('PACKAGE_HOLIDAYS');
  behavior.apply(resultsDiv.empty().setContent(results));
  if (resultsUpdatedCallback) {
    resultsUpdatedCallback();
  }

  filterInProgress = true; // must be reset before updateData is called
  updateData();
  ta.servlet.HACSearch.filter.update();
  ta.servlet.HACSearch.getFilterWaitOverlay().hide();
  hacXhrInFlight = false;
}

var updateFilterCount = function(input, nCount, masterDisable, showDisabledCounts) {
    //  update the item's count
    var countElement = document.getElementById(input.id + "_count");
    countElement.innerHTML = '(' + nCount + ')';
    var enabled = (!masterDisable && (nCount > 0 || input.checked || input.hasClass('default')));

    //  enable or disable the check set
    countElement.style.display = showDisabledCounts || enabled ? 'inline' : 'none';
    input.disabled = !enabled;
    $(input.id + '_lbl')[enabled ? 'removeClass' : 'addClass']('disabled');
}

/*
 * Create a check set div
 */
var createCheckSet = function(/* String */ criteriaName,
                              /* String */ value,
                              /* String */ labelTxt,
                              /* String */ count)
{
    var inputName = criteriaName + "_" + value;
    var div = new Element('div', {'class':'chkSet'});

    var inputArgs = {
       type:"checkbox",
       "value":value,
       "name":inputName,
       "id":inputName
    };
    var input = new Element('input', inputArgs);
    div.appendChild(input);

    var myLabel = new Element('label', {
       "id":inputName + '_lbl',
       "for":inputName
    });
    myLabel.innerHTML = labelTxt;
    div.appendChild(myLabel);

    var span = new Element('span', {
       "id":inputName + '_count',
       "for":inputName
    });
    span.innerHTML = '(' + count + ')';
    myLabel.appendChild(span);

    return div;
};

/*
 * Update the filter counts, showing counts, map pins, etc.
 */
var updateData = function() {
  // update filter counts -- cost: ~180-250ms
  if (window.filterCounts) {
    filterCounts.each(function(criteria) {

      //  look for a field set with the same id as the criteria and check if the items
      //  should be forced (i.e. added/removed) to refect the counts)
      var fieldset = $('HOTEL_FILTERS').getElement('fieldset.' + criteria.name);
      var itemsMatchCounts = fieldset && fieldset.hasClass('itemsMatchCounts');
      var masterSwitch = fieldset ? fieldset.getElement('input.master') : null;
      var masterDisable = masterSwitch && !masterSwitch.checked;
      var showDisabledCounts = fieldset && fieldset.hasClass('showDisabledCounts');
      var processedValues = [];
      var uncheckedListElement = $(criteria.name + "Unchecked");
      var uncheckedList = uncheckedListElement ? uncheckedListElement.value.split(',') : null;

      //  go through each criteria item
      var lastInput = null;
      criteria.items.each(function(n)
      {
        if (!$defined(n))
        {
            return;
        }
        // update counts in tabs if present
        if (criteria.name == 'cat') 
        { 
          var tabContainer = $('HAC_TAB_CONTAINER');
          if (tabContainer) {
            tabContainer = $(tabContainer);
            var countSpan = tabContainer.getElement('.' + criteria.name + '_' + n.value + ' .label span');
            if (countSpan) {
              var newEl = new Element('span');
              newEl.setText(n.count);
              $(countSpan).replaceWith(newEl);
            }
          }
        }

        //  special handling for Brand
        if(criteria.name == 'zfb')
        {
          var inptI = $(criteria.name + "_" + n.value + '_i');
          if (inptI)
          {
              updateFilterCount(inptI, n.count, false, false);
          }
          var inptA = $(criteria.name + "_" + n.value + '_a');
          if (inptA)
          {
              updateFilterCount(inptA, n.count, false, false);
          }
          return;
        }

        //  add to the list of processed values
        processedValues.push(String(n.value));

        // counts are stored by id: $criteriaShortType_$value_count
        // NOTE! HIGHLY optimized code
        var input = $(criteria.name + "_" + n.value);
        if (!input)
        {
          //  if the item is missing and we need to match the counts
          if (itemsMatchCounts)
          {
            //  create a new item
            var newItemChecked = (uncheckedList == null) || (!uncheckedList.contains(String(n.value)));
            var checkSet = createCheckSet(criteria.name, n.value, n.label || 'undefined', n.count, newItemChecked);
            if (lastInput)
            {
              //  inject after the previous check set
              checkSet.injectAfter(lastInput.getParent('div.chkSet'));
            }
            else
            {
              //  inject before the first check set
              checkSet.injectBefore(fieldset.getElement('div.chkSet'));
            }

            //  now that the element is in the DOM, attach the event listener.  We need to
            //  do it in this order otherwise IE will blow chunks.
            var newCheckbox = checkSet.getElement('input[type=checkbox]');
            newCheckbox.onclick = function(event)
            {
                ta.call('ta.servlet.HACSearch.handleCheckSetOnClick', event);
            }.bindAsEventListener(newCheckbox);

            //  The checkbox must be checked after it is added to the DOM for IE to hold onto it
            ta.servlet.HACSearch.setCheckSetChecked(newCheckbox, newItemChecked)

            //  grab a reference to the new input
            input = $(criteria.name + "_" + n.value);
          }
          //  otherwise skip it
          else return;
        }

        //  update the filter counts
        updateFilterCount(input, n.count, masterDisable, showDisabledCounts);

        //  if the fieldset is open, make sure the check set is visible
        if (itemsMatchCounts && !fieldset.getElement('h3').hasClass('closed'))
        {
          input.getParent('div.chkSet').style.display = 'block';
        }

        //  grab a reference to the last input processed
        lastInput = input;
      });

      //  if items must match the counts then go and hide any items not included
      //  in the update
      if (itemsMatchCounts)
      {
        fieldset.getElements('div.chkSet').each(function(checkSet)
        {
          var input = checkSet.getElement('input');
          if (!processedValues.contains(input.value))
          {
              checkSet.style.display = 'none';
              input.disabled = true;
          }
        });
      }
    });

    window.filterCounts = null;
  }

  // update showing counts
  var count = $('RESULT_COUNT');
  if (window.filterProgress && count) {
    var countVal = count.getElement('i');
    var totalVal = count.getElement('b');
    if (countVal) countVal.setContent(filterProgress.shown);

    var totalDisplay = (filterProgress.totalDisplay && typeof(filterProgress.totalDisplay) == "number") ?
                       filterProgress.totalDisplay : filterProgress.total;
    if (totalVal) totalVal.setContent(totalDisplay);
    if (typeof(filterProgress.complete) == "number") {
        if(filterProgress.complete == lastCompleted)
        {
            requestsSinceCompletedChange++;
        }
        else
        {
            lastCompleted = filterProgress.complete;
            requestsSinceCompletedChange = 0;
        }
      if (filterProgress.total > filterProgress.complete && requestsSinceCompletedChange < updateRequestLimit)
      {
        if (!window.HACProgress) {
          window.HACProgress = new ta.widgets.ProgressBar($('HAC_PROGRESS'), {limit: filterProgress.total}).start();
          var hacSmry = $('HAC_SMRY');
          if (hacSmry)
            hacSmry.hide();
        }
        HACProgress.update(filterProgress.complete);
        // search in progress ... check for additional results in a bit
        filterInProgress = false;
        updateResults.continuation = true;
        filtersChanged.delay(updateResults.time);
      }
      else {
        if (window.HACProgress) HACProgress.stop();
        var hacSmry = $('HAC_SMRY');
        if (hacSmry) {
          hacSmry.show();
          if (!$('searchAll').checked && !$('searchAll').value == 'true') { // on HAC search
            $('RESULT_COUNT').getElements('.avlb .rdoSet').each(function(rdoSet){
              rdoSet.removeClass('disabled').getElement('input').disabled = false;
            });
          }
        }
      }
    }
    window.filterProgress = null;
  }

  // update best value count
  if ($defined(ta.retrieve('topvalue.count'))) {
    var count = ta.remove('topvalue.count');
    var countEl = $('tvCount');
    if (countEl) {
      countEl.setText("(" + count + ")");
      // if we have top values, remove disabled from parent label if it is present and enable checkbox
      if (count > 0) {
        countEl.getParent().removeClass('disabled');
        $('sortGroup').disabled = false;
      } else {
        countEl.getParent().addClass('disabled');
        $('sortGroup').disabled = true;
      }
      // make sure field set is visible - if we initially had no top value, it would be hidden
      countEl.getParent('fieldset').removeClass('hidden');
    }
  }

  // Update the thumbnail
  if ($('STATIC_MAP')) {
    var mapThumbE = $('STATIC_MAP');
    var markerData = ta.retrieve('maps.marker-data');
    if (markerData && markerData.hotel && markerData.hotel.markers) {
      markerData = markerData.hotel.markers;
      var markerStr = "";
      for(i = 0; i < markerData.length; i++) {
        if (markerStr.length > 0) {
          markerStr += "|";
        }
        markerStr = markerStr + markerData[i].lat + "," + markerData[i].lng;
      }
      var mapWxH = "268x133";
      if (window.mapOps) {
        var mapZoom = (mapOps.thumbZoom ? mapOps.thumbZoom : mapOps.zoom);
        var mapLat = (mapOps.thumbLat ? mapOps.thumbLat : mapOps.lat);
        var mapLng = (mapOps.thumbLng ? mapOps.thumbLng : mapOps.lng);
        var staticMapSrc = "http://maps.google.com/staticmap?key=" + gKey +
            "&center=" + mapLat + "," + mapLng + "&zoom=" + mapZoom + "&size=" + mapWxH + "&markers=" + markerStr;
        mapThumbE.src = staticMapSrc;
      }
    }
  }

  if (ta.has('maps.map') && ta.has('maps.marker-data')) {
    ta.util.pending.waitForFn('update_markers', 'ta.maps.Factory.updateMapMarkers');
  }

  var map = ta.retrieve('maps.map');
  if (window.mapOps && map) {
      map.move(mapOps.lat, mapOps.lng, mapOps.zoom);
  }

  // check if price range criteria was auto-broadened
  var priceField = $('l1price');
  if (window.filterFormUpdates && filterFormUpdates.priceRange && priceField && priceField.value != "") {
    var cur = priceField.value.split(',');
    if (parseInt(cur[0]) == parseInt(cur[1])) {
      var sldr = $('priceSelect').slider;
      sldr.setKnobFromValue(sldr.sliderOptions.knobs[0], filterFormUpdates.priceRange.min, sldr.sliderOptions.min, sldr.sliderOptions.max, sldr.sliderOptions.step);
      sldr.setKnobFromValue(sldr.sliderOptions.knobs[1], filterFormUpdates.priceRange.max, sldr.sliderOptions.min, sldr.sliderOptions.max, sldr.sliderOptions.step);
    }
  }
  
  // rebuild the PH Calendar if on that view
  var hphCal = $('HPHCAL');
  if (hphCal) ta.phac.setupCalendar(hphCal);
  
  //  look for slider updates
  if (window.filterFormUpdates)
  {
      for (member in window.filterFormUpdates)
      {
          var match = member.match(/slider_(.*)/);
          if (match && match.length > 1)
          {
              var slider = $(match[1]);
              if (slider)
              {
                  var changed = false;
                  var newDimensions = filterFormUpdates[member];
                  for (className in newDimensions)
                  {
                      var regex = new RegExp(className + '[\\d]+');
                      var oldClass = slider.className.match(regex)[0];
                      var newClass = newDimensions[className];
                      if (oldClass != newClass)
                      {
                          slider.removeClass(oldClass);
                          slider.addClass(newClass);
                          changed = true;
                      }
                  }

                  // if we had a change then reinitialize the slider
                  if (changed)
                  {
                      dualSliderRule(slider);
                  }
              }
          }
      }
  }

  //  clear the filter form updates
  window.filterFormUpdates = null;

  // update header and map link copy -- cost: ~30ms
  if (window.headerCopy && window.mapCopy) {
    $('HEADING').setContent(headerCopy);
    var map = $('SMALL_MAP');
    if (map) {
	var mapLink = map.getElement('.hvrIE6 a');
        if(mapLink) mapLink.setContent(mapCopy);
    }
    window.headerCopy = null;
    window.mapCopy = null;
    if(window.summaryCopy) {
        var smryTitle = $('hac_smry_title'); //only in hotels redesign
        if(smryTitle) smryTitle.setContent(summaryCopy);
        window.summaryCopy = null;
    }
  }

  ta.servlet.HACSearch.updateData();
}

var filterSequenceID = -1;// last sequence id sent
var responseSequenceID = -1;// last sequence id seen in response
var filterInProgress = true;// show 'filter in progress' box?
var hacXhrInFlight = false;//  if a HAC XHR is in flight

/**
  Get the url to post HAC data to.
  @returns the HAC form request uri
*/
var getHACFormURL = function() {
  //  The /Hotels and /VacationRentals servlets are crawlable and so get canonicalized which
  //  does not work with HAC type requests so use the /HACSearch and /VRACSearch servlets
  //  instead (respectively)
  var url;
  var sPageServlet = window["pageServlet"];
//   if (sPageServlet == "Hotels") url = '/HACSearch';
//   else if (sPageServlet == "VacationRentals") url = '/VRACSearch';
//   else if (sPageServlet == "BusinessCenter" || sPageServlet == "BCACSearch") url ='/BCACSearch';
//   else if (sPageServlet == "PHACSearch") url = $('PHAC_FORM').action;
//   else url = '/' + window["pageServlet"];

  if (sPageServlet == "Hotels") {
    url = '/HACSearch';
  }
  else if (sPageServlet == "VacationRentals") {
    url = '/VRACSearch';
  }
  else if (sPageServlet == "BusinessCenter" || sPageServlet == "BCACSearch") {
    url ='/BCACSearch';
  }
  else {
    url = '/' + window["pageServlet"];
  }

  if (ta.has('hac.filterOffset')) {
    url += '-oa' + ta.remove('hac.filterOffset');
  }

  return url + '?';
}

/**
  Get the HAC data to submit.
  @returns an array of forms and objects to be submitted
*/
var getHACFormData = function(newPage, formName) {
  var hacForm;
  if (formName) {
    hacForm = $(formName);
  }
  else {
    hacForm = $('HAC_FORM');
  }
  data = [];
  if (hacForm && (formName != 'PHAC_FORM')) data.push(hacForm);

  // category tab
  if (ta.has('sel.category')) {
    var param = new Object();
    param[ta.retrieve('sel.category')] = 1;
    data.push(param);
  }

  // filters
  if (newPage) {
    // keep filter only if geo didn't change
    if (hacForm && hacForm.geo.value == modelLocId) {
        data.push($('HOTEL_FILTERS'));
    }
    else if(!hacForm) {
        var hotelFilters = $('HOTEL_FILTERS');
        if(hotelFilters.geo.value == modelLocId) {
            data.push(hotelFilters);
        }
    }
    return data; // this is all we need for a new page
  }
  data.push($('HOTEL_FILTERS'));
  var poiMarkers = $('POI_MARKERS');
  if(poiMarkers) data.push(poiMarkers);//include new style nearby cities

  // result per page
  //var rpp = $('rpp');
  //if (rpp) data.push({'rpp': rpp.options[rpp.selectedIndex].value});

  // sponsorship
  var addSponsorship = $('addSponsorship');
  if (addSponsorship && addSponsorship.checked) data.push({'addSponsorship': addSponsorship.value});
  var addSponsorshipToggle = $('addSponsorshipToggle');
  if (addSponsorshipToggle && addSponsorshipToggle.value != "") data.push({'addSponsorshipToggle': addSponsorshipToggle.value});

  filterSequenceID++;
  data.push({
    seen: 0, // AJAX marker
    sequence: filterSequenceID // sequence number
  });

  var map = ta.retrieve('maps.map');
  if (map && (map.getMorphIndex || ta.has('maps.is_open'))) {
    if (ta.retrieve('maps.moved')) {
      data.push({
        mc: map.mapCenter().toUrlValue(),
        mz: map.getZoom()
      });
    }
    if ((map.getMorphIndex && map.getMorphIndex() == 1) || ta.has('maps.is_open')) { 
      data.push({map_open: 1}); 
    }
  }
  var tvState = ta.retrieve('topvalue.state');
  if (tvState) {
    if (tvState == 0) data.push({tvState: 'collapsed'});
  }

  if (ta.has('zft')) {
    data.push({zft: ta.retrieve('zft').join(",")});
  }

  if (ta.has('lma')) {
    data.push({lma: ta.remove('lma')});
  }

  if (ta.has('sel.nearby')) {
    data.push({'selNearby': ta.remove('sel.nearby').join(',')});
  }

  if (ta.has('sel.neighborhood')) {
    data.push({'selNeighborhood': ta.remove('sel.neighborhood').join(',')});
  }

  if (updateResults.continuation) {
    data.push({timed:true});
    updateResults.continuation = false;
  }
  else
  {
    requestsSinceCompletedChange = 0;
    lastCompleted = -1;
  }

  return data;
}

/*
 * Update the HAC results via XHR.  To prevent multiple XHRs from stacking up
 * call 'filtersChanged()' instead.
 */
var updateResults = function(newPage) {
  var form = $('HAC_FORM');
  if (!form) form = $('VRAC_FORM');
  if (!form) form = $('PHAC_FORM');
  updateResultsFromForm(newPage, form);
};

var updateResultsNarrow = function(newPage) {
  updateResultsFromForm(newPage, 'HAC_FORM_NARROW');
};


var updateResultsFromForm = function(newPage, formName) {
  var hacForm = $(formName);
  var goToHAC = ta.has('redesignEnabled') && window["pageServlet"] == 'Hotels'; // force new page when on Hotels servlet
  if (newPage || window.updateResultsNewPage || goToHAC) {
    if (goToHAC) {
      showUpdatingMessage();
    }
      
    var uri = getHACFormURL() + getHACFormData(true, formName).toQueryString();
    var map = ta.retrieve('maps.map');
    if (map && map.getMorphIndex && map.getMorphIndex() == 1) uri += "#map:S1";
    if (map && ta.retrieve('maps.is_open')) uri += "#map:$1"; // redesign version of above line

    // bug 35754: adding business center sidebar elements to HAC search if they are present
    if ($defined(ta.maps.Sidebar)) {
      ["hotels", "restaurants", "attractions", "coffee", "copy", "rail", "shipping"].each(function(x) {
        if ($('sidebar_' + x) && $('sidebar_' + x).checked) {
          uri += ",bc_" + x + ":-1";
        }
      });

      if (ta.has("maps.address")) {
        uri += ",bc_address:S" + ta.retrieve("maps.address");
      }

      if (ta.has("maps.airports")) {
        var airports = ta.retrieve("maps.airports");
        var ids = [];
        airports.each(function(x) {ids.push(x.locId);});
        uri += ",bc_airports:S" + ids.join("|");
      }

      if (ta.has("maps.conventionCenters")) {
        var conventionCenters = ta.retrieve("maps.conventionCenters");
        var ids = [];
        conventionCenters.each(function(x) {ids.push(x.locId);});
        uri += ",bc_convention_centers:S" + ids.join("|");
      }
    }

    window.location = uri;
  }
  else {
    removeDaoDaoHilite();
    if (filterInProgress) {
      showUpdatingMessage();
    }
    hacXhrInFlight = true;
    new Ajax(getHACFormURL(), {
      data: getHACFormData(false, formName),
      evalScripts: true,
      onComplete: resultsUpdated,
      onFailure: function(t) {
        ta.servlet.HACSearch.getFilterWaitOverlay().hide();
        //clean up progress if it is showing
        if (window.HACProgress) HACProgress.stop();
        $('HAC_SMRY').show();
      }
    }).request();
    ta.remove('maps.returnTo'); // forget previous state when updating
  }
};

var hideBCPromo = function() {
  ta.util.pending.lock('bcpromo.posthac.hidden', function() { // only do this once
    var elmt = $('POSTHAC_BCPROMO');
    if (elmt) {
      elmt.setStyle('display', 'none');
    }
  });
};

var showUpdatingMessage = function() {
  var box = ta.servlet.HACSearch.getFilterWaitOverlay(); 
  box.show();
	if (ta.remove('filter.map')) {
		box.container.setStyles({left:-9999, top:-9999});
		var sOuter = $(ta.retrieve('maps.container')).getCoordinates();
		var sInner = box.container.getCoordinates();
		box.container.setStyles({
			left: sOuter.left + (sOuter.width - sInner.width) / 2,
  		top:  sOuter.top + (sOuter.height - sInner.height) / 2
		});
	}
	// use lightbox to prevent further filtering while in flight
	if (ta.remove('filter.lockout')) box.enableBackdrop();
  hideBCPromo();
};
var removeDaoDaoHilite = function() {
    var ddLinkbar = $('DD_LINKBAR');
    if(typeof ddLinkbar  == 'undefined' || ddLinkbar == null) {
        return;
    }
    // clear all highlighting
    $each(ddLinkbar.getElements('a'), function(el) { el.className = ""; });
    $('DD_LINKBAR_FILTERS_ON').show();
}

// called when page first loads to update HAC results via ajax
var updateResultsInit = function() {
  window.slidersReady = true;
  updateResults.time = 10;

  //  SPECIAL CASE:  We need to update the rate periodicity radio inputs before we call setRatesSliderDimensions()
  //                 since it uses it to determine which currency hash to use (daily/weekly)
  var pricePeriod = window.location.hash.match(/pricePeriod-(\w)/);
  if (pricePeriod && pricePeriod.length > 0)
  {
    $('pricePeriod_' + pricePeriod[1]).checked = true;
  }

  //  set up the rates slider dimensions
  setRatesSliderDimensions();

  //  update the data (filter counts, showing counts, etc.)
  updateData();
  updateResults.time = 3000;
}

/*
 * Event handler for when the filters have changed.  Use this method instead of directly calling
 * 'updateResults()' to prevent multiple XHR's from stacking up
 */
var filtersChangedTimeout = null;
var filtersChanged = function(e)
{
  //  get the target element (if any)
  var elmt = (e ? $(new Event(e).target) : null);
  //  if they selected a checkbox
  if (elmt && elmt.type == 'checkbox')
  {
      //  the default checkbox is mutually exclusive with the rest of them
      var fieldset = elmt.getParent('fieldset')
      var defaultCheckboxes = fieldset.getElements('.default');  //  may be empty
      var allCheckboxes = fieldset.getElements('input[type=checkbox]');
      var toggledDefault = defaultCheckboxes.contains(elmt);
      var checked = elmt.checked;
      var brandCheckbox = elmt.id.match(/^zfb_.+/); //brand checkbox needs special handling
      var skipUpdate = false;

      //  if they checked the default then uncheck everything else
      if (toggledDefault && checked)
      {
          allCheckboxes.each(function(checkbox)
          {
              var checkboxChecked = (defaultCheckboxes.contains(checkbox) ? true : false);
              ta.servlet.HACSearch.setCheckSetChecked(checkbox, checkboxChecked);
          });
      }
      //  else if they checked a non-default make sure the default is unchecked
      else if (!toggledDefault && checked)
      {
          defaultCheckboxes.each(function(checkbox){
             ta.servlet.HACSearch.setCheckSetChecked(checkbox, false);
          });
      }
      //  else if they unchecked something, and everything is now unchecked,
      //  then check the default box (unless there is no default in case there
      //  is nothing to do)
      else if (!checked && defaultCheckboxes.length > 0)
      {
          var somethingChecked = false;
          allCheckboxes.each(function(checkbox)
          {
              if (checkbox.checked) somethingChecked = true;
          });
          if (!somethingChecked) {
              defaultCheckboxes.each(function(checkbox){
                 ta.servlet.HACSearch.setCheckSetChecked(checkbox, true);
              });
          }

          //  SPECIAL CASE:  If we just tried to uncheck the default box and we had to re-check it
          //                 since nothing else is checked, not need to update the form since the
          //                 two changes cancel out
          if (toggledDefault && !checked && !somethingChecked) {
              skipUpdate = true;
          }
      }

      //special behaviour for brand filter - set duplicate
      if(brandCheckbox)
      {
        var dupElmt = brandDup(elmt);
        if(dupElmt)
        {
            dupElmt.checked = elmt.checked;
        }
      }

      if(skipUpdate) return;
  }

  //  prevent XHRs from stacking up by clearing any existing timer
  //  and creating a new one
  if (hacXhrInFlight)
  {
      //  prevent multiple 'filtersChanged()' from stacking up
      if (filtersChangedTimeout)
      {
          $clear(filtersChangedTimeout);
          filtersChangedTimeout = null;
      }

      //  try again later
      filtersChangedTimeout = filtersChanged.delay(1000, [e]);
  }
  else
  {
      //  update our results
      $clear(filtersChangedTimeout);
      filtersChangedTimeout = null;
      updateResults();
  }
}

var sliderMoved = function() {
  if (!window.slidersReady) return;
  ta.servlet.HACSearch.adjustTopValueState();
  filtersChanged();
}

var hacGeoChanged = function(elmt, response) {
  $('HAC_FORM').geo.value = response.value;
}

var hacSortChanged = function() {
  var fld = $('SORT_FORM').sortOrder;
  var sort = fld.value;
  $('HOTEL_FILTERS').sortOrder.value = sort;
  filtersChanged();
}

var _newBestValueSortValue = function(checked, val) {
  var tmp = val;
  tmp = tmp.replace(/bv_/,"");
  if (checked) {
    tmp = "bv_" + tmp;
  }
  return tmp;
}

var bestValueChanged = function() {
  var sortForm = $('SORT_FORM');
  var bestValueForm = $('BEST_VALUE_FORM');
  var hotelFilters = $('HOTEL_FILTERS');
  // for hotels redesign, bv checkbox is part of HOTEL_FILTERS
  if (bestValueForm) {
    var checked = bestValueForm.sortGroup.checked;
  } else if(hotelFilters && hotelFilters.sortGroup) {
    var checked = hotelFilters.sortGroup.checked;
  } else {
    var checked = false;
  }

  var options = sortForm.getElements('option'); // may be empty if drop down not used
  for(var i = 0; i < options.length; i++) {
    var opt = options[i];
    opt.value = _newBestValueSortValue(checked, opt.value);
  }

  sortForm.sortOrder.value = _newBestValueSortValue(checked, sortForm.sortOrder.value);
}

var nonHacBestValueChanged = function() {
  bestValueChanged();
  $('SORT_FORM').submit();
}

var hacBestValueChanged = function() {
  bestValueChanged();
  hacSortChanged();
}


var dupeNav = function() {
  var real = $('REAL_NAV');
  var fake = $('FAKE_NAV').addClass('toggle').setProperty('id', 'TOGGLEME');
  var elmt = fake.getElement('span.show');
  var nav = real.getElement('.lhnHigh').clone();
  elmt.removeEvent('click', dupeNav); // only do this once...
  fake.adopt(nav);
  var hide = real.getElement('span.hide').clone().injectTop(nav);
  elmt.addEvent('click', toggle.bindAsEventListener(fake));
  hide.addEvent('click', toggle.bindAsEventListener(fake));
}

var brandDup = function(elmt) {
    if(!elmt) return null;
    var sid = elmt.id;
    if(sid.charAt(sid.length - 1) == 'a')
    {
        sid = sid.substring(0,sid.length - 1) + 'i';
    }
    else
    {
        sid = sid.substring(0,sid.length - 1) + 'a';
    }
    return $(sid);
}

var subBrandSelected = function() {
    var reset = false;
    var bfElmt = $('brand_full');
    if(!bfElmt) {
        return false;
    }
    var fullCheckboxes = bfElmt.getElements('input[type=checkbox]');
    fullCheckboxes.each(function(cb) {
       if(cb.checked && !brandDup(cb))
       {
           reset = true;
       }
    });
    return reset;
}

//switch to the short brands view
var brandsShort = function() {
    //if any brands are selected which don't appear in short view, select 'all'
    if(subBrandSelected())
    {
        $('zfb_0_i').click();
    }

    $('brand_initial').setStyle('display', 'block');
    $('brand_full').setStyle('display', 'none');
}

var brandsShortEvent = function(e) {
    new Event(e).preventDefault();
    brandsShort();
}

//switch to the full brands view
var brandsFull = function() {
    //simpler - current selection carries over
    $('brand_initial').setStyle('display', 'none');
    $('brand_full').setStyle('display', 'block');
}

var brandsFullEvent = function(e) {
    new Event(e).preventDefault();
    brandsFull();
}

//
// BEHAVIOR RULES
//

rules['#HAC_HOTELS form.lightning div.submit'] = function(elmt) {
  var input = elmt.getElement('input');
  var span = elmt.getElement('span.hvrIE6');
  if (input && span)
  {
    span.addEvent('click', function() {input.click();});
  }
}

rules['#LEFTNAV #TOGGLEME .show'] = function(elmt) {
  elmt.addEvent('click', function() {
    new Ajax('/ActionRecord?action=LHN_EXPANDED').request();
  });
}

rules['#FAKE_NAV span.show'] = function(elmt) {
  elmt.addEvent('click', dupeNav);
}

linkMap['js_HACpager'] = function(elmt, e) {
  new Event(e).preventDefault();
  filterSequenceID++;
  var data = null;
  if (ta.has('maps.map') && ta.retrieve('maps.moved')) {
    var map = ta.retrieve('maps.map');
    data = {
      mc: map.mapCenter().toUrlValue(),
      mz: map.getZoom()
    };
  }
  window.scrollTo(0,0);
  var ddArrowbar = $('DD_ARROWBAR');
  if(typeof ddArrowbar  != 'undefined' && ddArrowbar != null) {
      ddArrowbar.hide();
  }
  if ( filterInProgress ) {
    ta.servlet.HACSearch.getFilterWaitOverlay().show();
  }
  hideBCPromo();
  new Ajax(elmt.href.replace(/#ACCOM_OVERVIEW/,'') + '?seen=0&sequence=' + filterSequenceID, {
    'data': data,
    evalScripts: true,
    onComplete: resultsUpdated
  }).request();
}


// modify search button
var changeDatesnCity = function(elmt) {
  var elemForm = $(elmt).getParent('.modsrch').getElement('form');
  elmt.defaultGeo = elemForm.geo.value;
  elmt = $(elmt);
  new ta.overlays.CenteredOverlay({
    backdrop: ta.overlays.BACKDROP_ALWAYS,
    style: 'vrOverlay dg s4',
    showCloseButton: true,
    onShow: function() { 
      this.inner.adopt(elemForm);
      this.content = elemForm;
      this.position();
      new Ajax('/ActionRecord?action=HAC_MOD_SRCH').request(); 
    },
    onHide: function() {
      this.content.injectInside(elmt);
      var geo = elemForm.getElement('#hacGeo');
      geo.value = geo.defaultValue;
      geo = $(elemForm.geo);
      geo.value = elmt.defaultGeo;
    }
  }, elmt);
}

// show all PROPERTY_TYPE in GEO - resets all filters except property type
var clearFilters = function(elmt) {
  elmt.addEvent('click', function(e) {
    new Event(e).preventDefault();
    var checked = false;
    $$('#HOTEL_FILTERS input.default[type=checkbox]').each(function(cb) {
      if (!/^cat/.test(cb.name) && !cb.checked) {
        cb.click();
        checked = true;
      }
    });

    // uncheck all nearby city boxes
    var citiesContainer = ta.retrieve('nearbycities.container');
    $$(citiesContainer + ' fieldset.nearbyCities input[type=checkbox]').each(function(cb) {
      if (cb.checked) {
        cb.click();
        checked = true;
      }
    });


    //  if we have a nearby geos criteria
    var nearbyGeos = $$('fieldset.nearbyGeos')[0];
    if (nearbyGeos)
    {
        // uncheck the nearby geos master switch and check and disable all geo items
        nearbyGeos.getElements('input[type=checkbox]').each(function(checkbox)
        {
            var checkboxChecked = checkbox.disabled = !checkbox.hasClass('master');
            ta.servlet.HACSearch.setCheckSetChecked(checkbox, checkboxChecked);
        });

        //  disable the distance select
        nearbyGeos.getElement('select').disabled = true;
    }

    //  if we have a child geos criteria
    var childGeos = $$('fieldset.childGeos')[0];
    if (childGeos)
    {
        //  make sure all options are checked
        childGeos.getElements('input[type=checkbox]').each(function(checkbox)
        {
            ta.servlet.HACSearch.setCheckSetChecked(checkbox, true);
        });
    }

    var unear = $('unear');
    if (unear) unear.value = "";

    //  reset the sliders
    var changed = false;
    ['priceSelect', 'ratingSelect', 'sleepsSelect', 'bedroomsSelect'].each(function(id)
    {
      var sliderElmt = $(id);
      if (sliderElmt)
      {
          //  reset the knobs and check if they have changed
          if (sliderElmt.slider.resetKnobs())
          {
              changed = true;
          }
      }
    });

    //  reset bathrooms
    var bathrooms = $('bathrooms');
    if (bathrooms) bathrooms.selectedIndex = 0;

    //  reset suitability
    $$('#HOTEL_FILTERS .suitability input[type=checkbox]').each(function(cb)
    {
        ta.servlet.HACSearch.setCheckSetChecked(cb, false);
    });

    //reset name contains field
    var field = $('nameContains');
    if(field && field.value && field.value.length > 0)
    {
        field.value = '';
    }

    //  update the results
    filtersChanged();

    // only need to run this if check boxes didn't change
    if (!checked && changed) sliderMoved();
  });
};

rules['#RESULT_COUNT span.clear'] = clearFilters;

rules['#LARGE_MAP span.clear'] = clearFilters;

var toggleAccommodationDetails = function(id, bExpand) {
  var selector = '#hotel_' + id + ' .collapsible';
  $$(selector).each(function(div)
  {
    var displayStyle = div.getStyle('display');
    div.setStyle('display', ((displayStyle == 'block') ? 'none' : 'block'));
  });
};

/**
 * Hande an onclick event from the 'includeNearby' checkbox before update
 * @param {Event} event The Event
 * @param {Element} elmt the 'includeNearby' Checkbox
 */
function handleIncludeNearbyOnClick(event, includeNearby)
{
    //  if include nearby is not checked, disable the distance select
    var distanceSelect = $('distanceSelect');
    distanceSelect[includeNearby.checked ? 'removeClass' : 'addClass']('dis');
    distanceSelect.disabled = !includeNearby.checked;

    //  mark that the user modified the include nearby checkbox
    $('includeNearbyModified').value = true;
}

var updateBCPromo = function(calendar, overlay){
  if (overlay != calendar.after) return; // only when user selects a check out date
  var promo = $$('.promoBCLink');
  if (!promo || promo.length == 0) return;
  promo = promo[0];
  if (!promo.hidden()) return;
  var nDays = ta.util.date.getDaysInRange(calendar.before.calendar.selectedDate, calendar.after.calendar.selectedDate);
  var dayIn = calendar.before.calendar.selectedDate.getDay();
  var dayOut = calendar.after.calendar.selectedDate.getDay();
  // short trip, starting Sun-Thu, ending Tue-Fri
  var bBusinessTrip = nDays<5 && dayIn<5 && dayOut<6 && dayOut>1 && dayIn<dayOut;
  if (bBusinessTrip) {
    promo.show();
    var popup = promo.getElement('.popup');
    if (popup) popup.removeClass('hidden');
  }
}
