//
//    searchPage-sweet.js
//
// ----------------------------------------------------------------------
var thePage = {

  init: function () {
    allSearchTerms = new AllSearchTerms();
    categoryList.init();
    displayAllResults2([]);
    amis.search.printDslist();
    this.addObservers();
  },

  resetPage: function () {
    $('searchTermSets').update();    
    $('searchResultsDiv').update();
    $('categorySpan').update();
    allSearchTerms.initialize();
    categoryList.init();
    displayAllResults2([]);
    amis.search.printDslist();
// this is a hack; architecturally this belongs elsewhere
    // removing dataset results
    theHtml = '<div><h1>No Matching Datasets.</h1></div>';
    $('datasetSearchResults').update(theHtml);
    // clearing the map
    var theMapContainer = thePage.mapOutput.mapContainer;
    theMapContainer.update();
    thePage.mapOutput.initialize(theMapContainer);
//
  },

  addObservers: function () {
    $('clearSetButton').observe('click',allSearchTerms.clearSet.bind(allSearchTerms));
    $('addSetButton').observe('click',allSearchTerms.addSet.bind(allSearchTerms));
    $('deleteSetButton').observe('click',allSearchTerms.deleteSet.bind(allSearchTerms));
    $('clearAllButton').observe('click',this.resetPage.bind(this));
    $('submitSearchButton').observe('click',allSearchTerms.submitSearch.bind(allSearchTerms));
  }
}

// ----------------------------------------------------------------------
Ajax.Responders.register({
  onCreate: function () { 
    $('busySpinner').show(); 
  },
  onComplete: function () {
    if (Ajax.activeRequestCount == 0) {
      $('busySpinner').hide();
    }
  }
});

// ----------------------------------------------------------------------
var searchStatusDisplay = {
// created for future use; not currently integrated
  init: function () {
    var myContainer = $('searchStatus');
    var searchStates = new Object();
    // searchStates: 0 = not yet searched, null = search underway, 1 = search completed
    searchStates.propj = 0;
    searchStates.data = 0;
    searchStates.dataXpropj = 0;
    searchStates.propjXdata = 0;
  },

  makeFrame: function() {
    
  },

  setStatus: function () {
    
  }
}

// ----------------------------------------------------------------------
// created for future use; not currently integrated
var SearchTerm = Class.create();
Object.extend(SearchTerm.prototype,{
  initialize: function(theInfo) {
    this.whatAmI = 'SearchTerm';
    this.category = theInfo.category;
    this.value = theInfo.value;
    this.forDisplay = theInfo.forDisplay;
  },

  returnString: function () {
  }
});

// ----------------------------------------------------------------------
var SearchTermSet = Class.create();
Object.extend(SearchTermSet.prototype,{

  initialize: function(theContainer) {
   this.whatAmI = 'SearchTermSet';
   this.terms = new Array();
   this.container = theContainer || null;
  },

  addTerm: function (theTerm) {
    // receives a string 
    this.terms.push(theTerm);
  },

  showTerms: function () {
    if (this.container == null) { return; }
    var thisTermContainer = $(this.container).down('fieldset');
    var theTemplate = new Template('#{category}:  #{forDisplay}');
    for (var iterator = 0, last = this.terms.length; iterator < last; ++iterator  ) {
      var pruneLink = new Element('span',{'class':'dellink'});
      pruneLink.indexValue = iterator;
      pruneLink.update('delete ');
      $(pruneLink).observe('click',this.pruneTerm.bindAsEventListener(this,this.container));
      $(thisTermContainer).insert(pruneLink);
      var textSpan = new Element('span',{});
      textSpan.update(theTemplate.evaluate(this.terms[iterator].evalJSON()));
      $(thisTermContainer).insert(textSpan);
      $(thisTermContainer).insert('<br>');
    }
  },

  clearAll: function () {
    this.terms = new Array();
    allSearchTerms.showSubmitButton();
  },

  pruneTerm: function (theEvent) {
    // this is a modified version that only deletes the selected term.
    // the way things have developed it makes more sense than pruning the 
    // the whole remaining list.
    var theElement = theEvent.element();
    this.deleteTerm(theElement.indexValue,this.container);
  },

  deleteTerm: function (indexValue) {
    this.terms.splice(indexValue,1);
    // kind of kludgy part here; can't just empty theContainer without destroying
    // the fieldset legend, so remove the items one at a time
    for (var iterator = $(this.container).descendants().length - 1, last = 1; iterator > last; --iterator){
      $(this.container).descendants()[iterator].remove();
    }
    this.showTerms();
  },

// perhaps already deprecated
  modifyTermValue: function (theTermIndex,newInfo) {
    var theTermObject = this.terms[theTermIndex].evalJSON();
    theTermObject.value = newInfo.value;
    theTermObject.forDisplay = newInfo.forDisplay;
    this.terms[theTermIndex] = Object.toJSON(theTermObject);
  },

  findCategoryTerms: function (theCategory) {
    // returns an array of JSON objects representing each existing search terms
    // of the category type and their postition index
    var theTermsArray = new Array();
    var theParts = theCategory.split('::');
    var mainCategory = theParts[0];
    if (theParts[1]) {
      var subCategory = theParts[1];
    } else {
      var subCategory = null;
    }
    for (var iterator = 0, last = this.terms.length; iterator < last; iterator++) {
      var thisTermObject = this.terms[iterator].evalJSON();
      var thisTermCategory = thisTermObject.category.split('::');
      if (thisTermCategory[0] == mainCategory) {
        if ((subCategory == null) || (thisTermCategory[1] == subCategory)) {
          theTermsArray.push(iterator);
        }
      }
    } 
    return theTermsArray;
  }
})

// ----------------------------------------------------------------------
var AllSearchTerms = Class.create();
Object.extend(AllSearchTerms.prototype,{

  initialize: function () {
    this.whatAmI = 'AllSearchTerms';
    this.currentSet= null;
    this.container = $('searchTermSets');
    this.container.update('');
    this.termSets = new Array();  // holds the actual set
    this.addSet();                // set 0 is for the data search terms
    this.addSet();                // set 1 is for the first project search terms
  },

  addSet: function () {
    (this.currentSet != null) ? this.currentSet++ : this.currentSet = 0;
    var theSetContainer = new Element('div');
    this.termSets.push(new SearchTermSet(theSetContainer));
    (this.currentSet < 2) ? $('deleteSetButton').disable() : $('deleteSetButton').enable();
    this.showAllSets();
    this.showSubmitButton();
  },

  //clearSet: function (setNumber) {
    // empties a set without deleting it
    //var mySetNumber = setNumber || this.currentSet;
  clearSet: function () {
    // empties a set without deleting it
    var mySetNumber = this.currentSet;
    this.termSets[mySetNumber].clearAll();
    this.showAllSets();
  },

  deleteSet: function (setNumber) {
    var mySetNumber = setNumber || this.currentSet;
    if (mySetNumber > 1) {
      this.termSets[mySetNumber].container.remove();
      this.termSets.pop();
      --this.currentSet;
    } else {
      this.clearSet();
    }
    if (this.currentSet < 1) {
      this.currentSet = null;
      $('deleteSetButton').disable();
      this.showSubmitButton();
    }
  },

  addTerm: function (theTerm,theSet) {
    // adds the submitted term to the current term set.
    if (( theSet != 0) && !theSet) { theSet = this.currentSet; }
    if (theSet > this.currentSet) { theSet = this.currentSet; }
    this.termSets[theSet].addTerm(theTerm);
    this.showAllSets();
    this.showSubmitButton();
  },

  overloadRegionSearch: function (newRegionTerm) {
    // find and removes all region search terms in all sets,
    // adds the new region term to each set.  receives new term from
    // the RegionOutputMap 
    for (var thisSet = 0, lastSet = this.termSets.length; thisSet < lastSet; ++thisSet) {
      var myDeleteTerm = this.termSets[thisSet].deleteTerm.bind(this.termSets[thisSet]);
      var theRegionTerms = (this.termSets[thisSet].findCategoryTerms('region')).reverse();
      theRegionTerms.each(function (thisRegionTerm) {
        myDeleteTerm(thisRegionTerm);
      });
      this.termSets[thisSet].addTerm(newRegionTerm);
    }
    this.showAllSets();
  },

  showAllSets: function () {
    for (var thisSet = 0, lastSet = this.termSets.length; thisSet < lastSet; ++thisSet) {
      this.showSet(thisSet);
    }
  },

  showSet: function (theSet) {
    var setIndex = (theSet || 0);  // if "theSet" is not supplied, assume the first
    //thisDiv = this.setDivs[setIndex];    
    thisDiv = this.termSets[setIndex].container;    
    thisDiv.update(" ");
    thisDiv.insert(new Element('fieldset'));
    thisDiv.down('fieldset').insert(new Element('legend'));
    if (setIndex) {
      thisDiv.down('legend').insert('Project Term set ' + (setIndex));
    } else {
      thisDiv.down('legend').insert('Data Term set');
    }
    this.termSets[setIndex].showTerms();
    this.container.insert(thisDiv);
  },

  showSubmitButton: function () {
    // checks if there are any existing search terms.  
    // if not, hides the submit search button.
    $('searchButtonDiv').hide();
    if (!this.termSets[1]) { return; } // minimum necessary construct isn't built yet
    if ((this.termSets[0].terms.length > 0) || (this.termSets[1].terms.length > 0)) {
      $('searchButtonDiv').show();
    }
  },

  makePropjSearchString: function () {
    // called by the SearchManager
    var searchString = '{"searchNumber":"1","termSets":[';
    for (var setIndex = 0, numberOfSets = allSearchTerms.termSets.length; setIndex < numberOfSets; ++setIndex) {
      searchString += '{"setNumber":"'+(setIndex+1)+'","terms":[';
      if (allSearchTerms.termSets[setIndex].terms.length == 0 ) {
        searchString += '{"termNumber":"'+(termIndex+1)+'","termValue":"empty"},';
      } else {
        for (var termIndex = 0, numberOfTerms = allSearchTerms.termSets[setIndex].terms.length; termIndex < numberOfTerms; ++termIndex) {
          searchString += '{"termNumber":"'+(termIndex+1)+'","termValue":';
          searchString += allSearchTerms.termSets[setIndex].terms[termIndex];
          searchString += '},';
        }
      }
      searchString = searchString.substring(0, searchString.length-1);
      searchString += ']},';
    }
    searchString = searchString.substring(0, searchString.length-1);
    searchString += ']}';

    return searchString;
  },

  makeDataSearchParams: function () {
    var params = new Object();
    var dataTermSet = allSearchTerms.termSets[0];
    for (var termIndex = 0, numberOfTerms = dataTermSet.terms.length; termIndex < numberOfTerms; ++termIndex) {
      var thisTerm = dataTermSet.terms[termIndex].evalJSON();
      switch (thisTerm.category) {
        case 'dataset':
          if (params.dsid) {
            params.dsid += ','+thisTerm.forDisplay;
          } else {
            params.dsid = thisTerm.forDisplay;
          }
          break;
        case 'region::predef':
          // have to do a lookup for the abbrev name
          for (var iterator = 0, last = region.regions.length; iterator < last; ++iterator) {
            if (region.regions[iterator].uid == thisTerm.value) {
              params.region = region.regions[iterator].abbrev;
            }
          }
          break;
        case 'region::custom':
          params.bbox = thisTerm.value;
          params.bbox = params.bbox.replace(/ /g,',');
          params.srs = 'EPSG:3338';
          break;
        case 'text::data-metadata':
          params.searchstring = thisTerm.value;
          break;
        case 'timeBounds':
          var tmpString = thisTerm.value;
          tmpString = tmpString.replace(/\//g,'T');
          tmpString = tmpString.replace(/::/,'Z/');
          params.timeextent = tmpString + 'Z';
          break;
        case 'variable':
          if (params.cvlist) {
            params.cvlist += ','+thisTerm.value;
          } else {
            params.cvlist = thisTerm.value;
          }
          break;
        case 'projectuid': 
          if (params.projectuid) {
            params.projectuid += ','+thisTerm.value;
          } else {
            params.projectuid = thisTerm.value;
          }
          break;
      }
    }
    // params is formed
    return params;
  },

  doSearchTmp: function () {
    // ==> 2008-12-12 : called 'Tmp' because I am reworking the search procedure and I wanted
    //                  to avoid any possible namespace clashes that would disrupt current function.
    // building json string from term objects
    // leaving the 1st term set (ie, the data search) for now
    var searchString = '{"searchNumber":"1","termSets":[';
    for (var setIndex = 0, numberOfSets = allSearchTerms.termSets.length; setIndex < numberOfSets; ++setIndex) {
      searchString += '{"setNumber":"1","terms":[';
      if (allSearchTerms.termSets[setIndex].terms.length == 0 ) {
        searchString += '{"termNumber":"'+(termIndex+1)+'","termValue":"empty"},';
      } else {
        for (var termIndex = 0, numberOfTerms = allSearchTerms.termSets[setIndex].terms.length; termIndex < numberOfTerms; ++termIndex) {
          searchString += '{"termNumber":"'+(termIndex+1)+'","termValue":';
          searchString += allSearchTerms.termSets[setIndex].terms[termIndex];
          searchString += '},';
        }
      }
      searchString = searchString.substring(0, searchString.length-1);
      searchString += ']},';
    }
    searchString = searchString.substring(0, searchString.length-1);
    searchString += ']}';

    var searchJSON = Object.toJSON(searchString.evalJSON());
    var myDoDataSearch = this.doDataSearch;
    var myPropjDataCrossSearchParams = this.propjDataCrossSearchParams;
    new Ajax.Request('lib/amis/search/search.php', {
                        asynchronous: false,
                        parameters: 'theSearch='+searchJSON,
                        onComplete: function (theResponse) {
                          // receives back the JSON encoded matching proposal information
                          thePage.ResultSet = (theResponse.responseText.strip()).evalJSON();
                          var existingParamsSearchstring = myPropjDataCrossSearchParams();
                          if (existingParamsSearchstring) {
                            var existingParams = new Object();
                            existingParams.projectuid = existingParamsSearchstring;
                            myDoDataSearch(existingParams);
                          } else {
                            myDoDataSearch();
                          }
			  displayAllResults2(thePage.ResultSet); // show project and data results;
			  amis.search.printDslist();
			  document.getElementById('amis.exploreTab').style.backgroundImage="url(theme/img/B-exploreresults.gif)";
			  document.getElementById('amis.accessTab').style.backgroundImage="url(theme/img/B-requestdata.gif)";
                        }
                    });
  },

  doDataSearch: function (existingParams) {
    // do the data search here and fill a separate div
    // building the 'params' object to submit to amis.search.dataSearch()
    // uses only the first termset
    var params = existingParams || new Object();
    var dataTermSet = allSearchTerms.termSets[0];
    for (var termIndex = 0, numberOfTerms = dataTermSet.terms.length; termIndex < numberOfTerms; ++termIndex) {
      var thisTerm = dataTermSet.terms[termIndex].evalJSON();
      switch (thisTerm.category) {
        case 'dataset':
          if (params.dsid) {
            params.dsid += ','+thisTerm.forDisplay;
          } else {
            params.dsid = thisTerm.forDisplay;
          }
          break;
        case 'region::predef':
          // have to do a lookup for the abbrev name
          for (var iterator = 0, last = region.regions.length; iterator < last; ++iterator) {
            if (region.regions[iterator].uid == thisTerm.value) {
              params.region = region.regions[iterator].abbrev;
              params.bbox = region.regions[iterator].bounds;
              params.srs = 'EPSG:3338';
            }
          }
          break;
        case 'region::custom':
          params.srs = 'EPSG:3338';
          var theCorners = thisTerm.value.split(',');
          var WS = theCorners[0].split(' ');
          var EN = theCorners[1].split(' ');
          params.bbox = WS[0]+','+WS[1]+','+EN[0]+','+EN[1];
          break;
        case 'text::data-metadata':
          params.searchstring = thisTerm.value;
          break;
        case 'timeBounds':
          var tmpString = thisTerm.value;
          tmpString = tmpString.replace(/\//g,'T');
          tmpString = tmpString.replace(/::/,'Z/');
          params.timeextent = tmpString + 'Z';
          break;
        case 'variable':
          if (params.cvlist) {
            params.cvlist += ','+thisTerm.value;
          } else {
            params.cvlist = thisTerm.value;
          }
          break;
      }
    }
    // 'params' is built
    if (Object.keys(params).length > 0) {
      var dataDerivedPropjUids = amis.search.dataSearch(params);
      // the above creates an object "amis.search.dslist" which describes the dataset
    } else {
      var theHtml = '<div><h1>No Matching Datasets.</h1></div>';
      $('datasetSearchResults').update(theHtml);
    }
  },

  propjDataCrossSearchParams: function() {
    // harvests uid data from propj search results and appends it to the data search
    var theUids = thePage.ResultSet.pluck('uid');
//    theUids.each(function (thisUid,index) {theUids[index] = 'project_number:'+thisUid});
    return (theUids.join(','));
  },

  submitSearch: function () {
    this.doSearchTmp();
  }
})

// ----------------------------------------------------------------------
var categoryList = {
  initialText: 'select category',
  templateString: '<option value=#{value}> #{label}',
  theContainer: 'selectCategoryDiv',
  categories: [{value:1,label:'Agency',call:'agency'},
               {value:4,label:'People',call:'person'},
               {value:5,label:'Program',call:'program'},
               {value:6,label:'Region',call:'region'},
               {value:7,label:'Time range',call:'timeRange'},
               {value:8,label:'Variable',call:'variable'},
               {value:9,label:'Words in ...',call:'freeText'}],

//               {value:2,label:'Cruise',call:'cruises'},
//               {value:3,label:'Data Set',call:'dsid'},

  init: function () {
    categoryList.makeSelect();
    categoryList.addObservers();
  },

  makeSelect: function () {
    new SelectMenu(this.initialText,this.categories,this.templateString,this.theContainer);
  },

  processSelection: function () {
    // 20090203 working around arming issues with IE
    /*
    if (this.armed) {
      // the "arming" is done to allow observation of the "click" event instead of the "change"
      // event.  The "change" event is problematic because the select boxes are created with the
      // first "option" selected.  If that is the desired entry, there is no "change" and the
      // call back is never fired.  Using "arming" and the "click" observer, the first "click"
      // arms the select box and the second "click" chooses.
      this.armed = false;
    } else {
      if (!($('categorySpan').empty())) {
        $('categorySpan').update();
      }
      this.armed = true;
      return;
    }
    */
    if (!($('categorySpan').empty())) {
      $('categorySpan').update();
    }
    theSelection = $(categoryList.theContainer).down().getValue();
    // 20090203 working around arming issues with IE
    if (theSelection == -10) return;
    //
    theCall = (categoryList.categories.find(function(n) {return n.value == theSelection})).call;
    eval(theCall + '.init()');
  },

  resetList: function () {
    $('categorySpan').update(" ");
    categoryList.init();
  },

  addObservers: function () {
    // 20090203 working around arming issues with IE
    // $(categoryList.theContainer).down().observe('click',categoryList.processSelection.bind(this));
    $(categoryList.theContainer).down().observe('change',categoryList.processSelection.bind(this));
  }
};

//----------------------------------------------------------------
var region = {
/*
  regions:    [{"uid":"12","name":"Alaska","abbrev":"AK","bounds":"154.4 49.4 255.0 76.7"},
               {"uid":"5","name":"Alaska Penninsula","abbrev":"AKPEN","bounds":"191.9 53.9 204.8 59.5"},
               {"uid":"6","name":"Aleutians","abbrev":"ALEUT","bounds":"173.4 45.2 198.2 58.4"},
               {"uid":"1","name":"Arctic","abbrev":"ARC","bounds":"193.6 68.1 223.1 75.0"},
               {"uid":"14","name":"Barrow","abbrev":"BRW","bounds":"196.8 69.8 210.1 73.1"},
               {"uid":"3","name":"Bering Sea","abbrev":"BSEA","bounds":"162.3 50.5 204.7 68.6"},
               {"uid":"13","name":"Bering Strait","abbrev":"SEW","bounds":"178.0 60.3 203.4 69.2"},
               {"uid":"2","name":"Chuckchi Sea","abbrev":"CHUK","bounds":"173.9 63.1 205.8 73.1"},
               {"uid":"8","name":"Cook Inlet","abbrev":"COOK","bounds":"202.9 57.8 212.9 61.6"},
               {"uid":"15","name":"Gulf of Alaska","abbrev":"GOA","bounds":"200.8 53.2 229.3 64.3"},
               {"uid":"7","name":"Kodiak","abbrev":"KOD","bounds":"200.8 54.4 212.4 59.0"},
               {"uid":"4","name":"Pribilof Islands","abbrev":"STP","bounds":"180.2 54.2 202.0 63.5"},
               {"uid":"9","name":"Prince William Sound","abbrev":"PWS","bounds":"210.1 58.9 217.1 61.7"},
               {"uid":"11","name":"Southeast Alaska","abbrev":"SE","bounds":"217.4 52.2 235.0 60.5"},
               {"uid":"10","name":"Yakutat","abbrev":"YAK","bounds":"214.4 57.2 225.3 61.7"},
               {"uid":"-1","name":" -- Custom -- ","abbrev":"Other","bounds":"0 0 0 0"}],
*/
  regions: null,

  init: function () {
    this.getPreDefRegions();
    this.makeRegionSelect();
    this.keyIsPressed = false;
  },

  makeRegionSelect: function () {
    var mySpan = new Element('span');
    $('categorySpan').insert($(mySpan));
    var templateString =  '<option value=#{uid}> #{name}';
    new SelectMenu('select region',this.regions,templateString,$(mySpan));
    // 20090203 working around arming issues with IE
    //$(mySpan).down().observe('click',this.processSelection.bindAsEventListener(this));
    $(mySpan).down().observe('change',this.processSelection.bindAsEventListener(this));
    //
    $(mySpan).observe('mouseover', this.selectRegion.bindAsEventListener(this));
    $(mySpan).observe('mouseout', this.unselectRegion.bindAsEventListener(this));
    $(mySpan).observe('select', this.unselectRegion.bindAsEventListener(this));
    $(mySpan).observe('keydown', this.keyDown.bindAsEventListener(this));
    $(mySpan).observe('keyup', this.selectRegion.bindAsEventListener(this));
  },

  makeCustomRegionInput: function () {
    this.boundingBox = new BoundingBoxField($('categorySpan'));
  },

  processSelection: function(theEvent) {
    // 20090203 working around arming issues with IE
/*
    if (this.armed) {
      this.armed = false;
    } else {
      theEvent.stop();
      this.armed = true;
      return;
    }
*/
    var theValue = theEvent.element().value;
    // 20090203 working around arming issues with IE
    if (theValue == -10) return;
    //
    if (theValue == -1) {  // a custom region was selected
      this.makeCustomRegionInput();
    } else {
      // add the term to the current set
      this.addTerm(theValue);
      this.unselectRegion(theEvent);
    };
  },

  addTerm: function (theValue) {
      var theName = this.uid2name(theValue);
      var theTerm = '{"category":"region::predef","value":"' +theValue+'","forDisplay":"' +theName+'"}';
      allSearchTerms.addTerm(theTerm,0);
      allSearchTerms.addTerm(theTerm);
      categoryList.resetList();
  },

  uid2name: function (theUid) {
    var theName ='';
    region.regions.each(function(n) {
      if (n.uid == theUid) theName=n.name;
    });
    return theName;
  },

  getPreDefRegions: function (agencyId) {
    var myAgencyId = agencyId || '5||1';  // default to AOOS
    dbRequest2('region','region.regions',myAgencyId);

  },

  keyDown: function(theEvent) {
      this.keyIsPressed = true;
  },

  selectRegion: function(theEvent) {
    if (this.keyIsPressed || theEvent.button != null) {
      var theName = null, theValue = theEvent.element().value;
      if (theValue != -1) {
        theName = this.uid2name(theValue);
      }
      thePage.mapOutput.selectRegionWFSFeature(theName,false);
      this.keyIsPressed = false;
    }
  },

  unselectRegion: function(theEvent) {
     var theName = null, theValue = theEvent.element().value;
     if (theValue != -1) {
       theName = this.uid2name(theValue);
     }
     thePage.mapOutput.selectRegionWFSFeature(theName,true);
  }
}

//----------------------------------------------------------------
var variable = {

  init: function () {
    this.makeSelect();
  }, 

  getVariableList: function () {
    var requestData = 'category=variable';
    new Ajax.Request('lib/amis/search/getList.php', {
                        asynchronous: false,
                        parameters: requestData,
                        onComplete: function (theResponse) {
                          variable.variables = theResponse.responseText.strip().evalJSON();
                        }
                    });
  },

  makeSelect: function (theContainer) {
    if (!variable.variables) { variable.getVariableList() };
    var mySpan = new Element('span');
    $('categorySpan').insert($(mySpan));
    var isActiveTemplate = '<option value=#{uid}> #{cvname}';
//    var notActiveTemplate = '<option value=#{uid} class=notActiveOption> #{cvname}';
    var notActiveTemplate = '<option value=#{uid} disabled> #{cvname}';
    new SelectMenu('select variable type',this.variables,isActiveTemplate,$(mySpan),notActiveTemplate);
    // 20090203 working around arming issues with IE
    //$(mySpan).down().observe('click',this.processSelection.bindAsEventListener(this));
    $(mySpan).down().observe('change',this.processSelection.bindAsEventListener(this));
    //
  },

  processSelection: function(theEvent) {
    // 20090203 working around arming issues with IE
/*
    if (this.armed) {
      this.armed = false;
    } else {
      theEvent.stop();
      this.armed = true;
      return;
    }
*/
    var theValue = theEvent.element().value;
    // 20090203 working around arming issues with IE
    if (theValue == -10) return;
    //
    this.addTerm(theValue);
  },

  addTerm: function (theValue) {
      var theName = this.uid2name(theValue);
      var theTerm = '{"category":"variable","value":"' +theValue+'","forDisplay":"' +theName+'"}';
      allSearchTerms.addTerm(theTerm,0);
      categoryList.resetList();
  },

  uid2name: function (theUid) {
    var theName = null;
    this.variables.each(function (thisObject) {
                       if (thisObject.uid == theUid) {
                         theName = thisObject.cvname;
                       }
                    });
    return theName;

  }
};

//----------------------------------------------------------------
var dsid = {

  init: function() {
    this.makeSelect();
  },

  getDsidList: function () {
    var requestData = 'category=dsid';
    new Ajax.Request('lib/amis/search/getList.php', {
                        asynchronous: false,
                        parameters: requestData,
                        onComplete: function (theResponse) {
                          dsid.dsids = theResponse.responseText.strip().evalJSON();
                        }
                    });
  },

  makeSelect: function (theContainer) {
    if (!dsid.dsids) { dsid.getDsidList(); }
    var mySpan = new Element('span');
    $('categorySpan').insert($(mySpan));
    var templateString = '<option value=#{uid}> #{dsid}';
    new SelectMenu('select data set',this.dsids,templateString,$(mySpan));
    // 20090203 working around arming issues with IE
    //$(mySpan).down().observe('click',this.processSelection.bindAsEventListener(this));
    $(mySpan).down().observe('change',this.processSelection.bindAsEventListener(this));
    //
  },

  processSelection: function(theEvent) {
    if (this.armed) {
      this.armed = false;
    } else {
      theEvent.stop();
      this.armed = true;
      return;
    }
    var theValue = theEvent.element().value;
    // 20090203 working around arming issues with IE
    if (theValue == -10) return;
    //
    this.addTerm(theValue);

  },

  addTerm: function (theValue) {
      var theName = this.uid2name(theValue);
      var theTerm = '{"category":"dataset","value":"' +theValue+'","forDisplay":"' +theName+'"}';
      allSearchTerms.addTerm(theTerm,0);
      categoryList.resetList();
  },


  uid2name: function (theUid) {
    // doing this as a loop rather than creating a specific hash or lookup table
    // because of the relative infrequency of the invocation compared to the
    // memory footprint of an additional dedicated object.  don't know if thats
    // a good trade-off or not.
    var theName = null;
    dsid.dsids.each(function (thisObject) {
                       if (thisObject.uid == theUid) {
                         theName = thisObject.dsid;
                       }
                    });
    return theName;
  }
}

//----------------------------------------------------------------
var agency = {

  init: function () {
    this.makeAgencySelect();
  },

  getList: function () {
    dbRequest('agency','agency.agencies');
  },

  makeAgencySelect: function (theContainer) {
    if (!agency.agencies) { agency.getList() };
    var mySpan = new Element('span');
    $('categorySpan').insert($(mySpan));
    var templateString = '<option value=#{uid}> #{name}';
    new SelectMenu('select agency',this.agencies,templateString,$(mySpan));
    // 20090203 working around arming issues with IE
    //$(mySpan).down().observe('click',this.processSelection.bindAsEventListener(this));
    $(mySpan).down().observe('change',this.processSelection.bindAsEventListener(this));
    //
  },

  processSelection: function(theEvent) {
    // 20090203 working around arming issues with IE
/*
    if (this.armed) {
      this.armed = false;
    } else {
      theEvent.stop();
      this.armed = true;
      return;
    }
*/
    var theValue = theEvent.element().value;
    // 20090203 working around arming issues with IE
    if (theValue == -10) return;
    //
    this.addTerm(theValue);
  },

  addTerm: function (theValue) {
      var theName = this.uid2name(theValue);
      var theTerm = '{"category":"agency","value":"' +theValue+'","forDisplay":"' +theName+'"}';
      allSearchTerms.addTerm(theTerm);
      categoryList.resetList();
  },

  uid2name: function (theUid) {
    agency.agencies.each(function (thisObject) {
                       if (thisObject.uid == theUid) {
                         theName = thisObject.name;
                       }
                    });
    return theName;
  }

}

//----------------------------------------------------------------
/// needs to be updated
var cruises = {
  getList: function () {
    dbRequest('cruise','cruises.json');
    cruises.json.unshift({"uid":"0","cruiseCode":"Select Cruise"});
  },

  makeSelect: function (theContainer) {
  }
}

//----------------------------------------------------------------
var person = {

  init: function () {
    this.makePersonSelect();
  },
   
  getList: function () {
    dbRequest('people','person.people');
  },

  makePersonSelect: function () {
    person.getList();
    var mySpan = new Element('span');
    $('categorySpan').insert($(mySpan));
    var templateString = '<option value=#{uid}> #{name}';
    new SelectMenu('select person',this.people,templateString,$(mySpan));
    // 20090203 working around arming issues with IE
    //$(mySpan).down().observe('click',this.processSelection.bindAsEventListener(this));
    $(mySpan).down().observe('change',this.processSelection.bindAsEventListener(this));
    //
  },

  processSelection: function(theEvent) {
    // 20090203 working around arming issues with IE
/*
    if (this.armed) {
      this.armed = false;
    } else {
      theEvent.stop();
      this.armed = true;
      return;
    }
*/
    var theValue = theEvent.element().value;
    // 20090203 working around arming issues with IE
    if (theValue == -10) return;
    //
    if (theValue == -1) {  // a custom region was selected
      this.makeCustomPersonInput();
    } else {
      // add the term to the current set
      this.addTerm(theValue);
    };
  },

  addTerm: function (theValue) {
      var theName = this.uid2name(theValue);
//      var theTerm = '{"category":"person::PI","value":"' +theValue+'","forDisplay":"' +theName+'"}';
      var theTerm = '{"category":"person","value":"' +theValue+'","forDisplay":"' +theName+'"}';
      allSearchTerms.addTerm(theTerm);
      categoryList.resetList();
  },

  uid2name: function (theUid) {
    // doing this as a loop rather than creating a specific hash or lookup table
    // because of the relative infrequency of the invocation compared to the
    // memory footprint of an additional dedicated object.  don't know if thats
    // a good trade-off or not.
    var theName = null;
    person.people.each(function (thisObject) { 
                       if (thisObject.uid == theUid) {
                         theName = thisObject.name;
                       }
                    });
    return theName;
  }

}

//----------------------------------------------------------------
var program = {

  init: function() {
    this.makeProgramSelect();
  },

  getProgramList: function () {
    dbRequest('program','program.programs');
  },

  makeProgramSelect: function (theContainer) {
    if (!program.programs) { program.getProgramList(); }
    var mySpan = new Element('span');
    $('categorySpan').insert($(mySpan));
    var templateString = '<option value=#{uid}> #{name}';
    new SelectMenu('select program',this.programs,templateString,$(mySpan));
    // 20090203 working around arming issues with IE
    //$(mySpan).down().observe('click',this.processSelection.bindAsEventListener(this));
    $(mySpan).down().observe('change',this.processSelection.bindAsEventListener(this));
    //
  },

  processSelection: function(theEvent) {
    // 20090203 working around arming issues with IE
/*
    if (this.armed) {
      this.armed = false;
    } else {
      theEvent.stop();
      this.armed = true;
      return;
    }
*/
    var theValue = theEvent.element().value;
    // 20090203 working around arming issues with IE
    if (theValue == -10) return;
    //
    this.addTerm(theValue);
  },

  addTerm: function (theValue) {
      var theName = this.uid2name(theValue);
      var theTerm = '{"category":"program","value":"' +theValue+'","forDisplay":"' +theName+'"}';
      allSearchTerms.addTerm(theTerm);
      categoryList.resetList();
  },


  uid2name: function (theUid) {
    var theName = null;
    program.programs.each(function (thisObject) {
                       if (thisObject.uid == theUid) {
                         theName = thisObject.name;
                       }
                    });
    return theName;
  }

}

//----------------------------------------------------------------
var timeRange = {

  init: function () {
    this.makeTimeInput();
  },

  makeTimeInput: function () {
    this.timeBoundsField = new TimeBoundsField($('categorySpan'));

  }
}   

//----------------------------------------------------------------
var freeText = {

  textTypes:  [{"type":"-1","name":"title"},
               {"type":"-2","name":"abstract"},
               {"type":"-3","name":"notes"},
               {"type":"-4","name":"data-metadata"}],
  defaultText: 'enter text',
  textType: null,

  init: function () {
    this.makeTextTypeSelect();
  },

  makeTextTypeSelect: function () {
    var mySpan = new Element('span');
    $('categorySpan').insert($(mySpan));
    var templateString =  '<option value=#{type}> #{name}';
    new SelectMenu('select text type',this.textTypes,templateString,$(mySpan));
    // 20090203 working around arming issues with IE
    //$(mySpan).down().observe('click',this.processSelection.bindAsEventListener(this));
    $(mySpan).down().observe('change',this.processSelection.bindAsEventListener(this));
    //
  },

  processSelection: function(theEvent) {
    // 20090203 working around arming issues with IE
/*
    if (this.armed) {
      this.armed = false;
    } else {
      theEvent.stop();
      this.armed = true;
      return;
    }
*/
    var theValue = theEvent.element().value;
    // 20090203 working around arming issues with IE
    if (theValue == -10) return;
    //
    if (theValue < 0 ) {  // text type selection
      this.textType = theValue;
      this.makeTextInput();
    } else {
      // add the term to the current set
      this.addTerm(theValue);
    };
  },

  makeTextInput: function () {
    this.textInputSpan = new Element('span');
    $(this.textInputSpan).update(new Element('input',{'type':'text','value':this.defaultText}));
    $('categorySpan').insert($(this.textInputSpan));
    $(this.textInputSpan).insert('<input type=button value=Submit />');
    $(this.textInputSpan).down('input',0).observe('focus',this.clearDefault.bindAsEventListener(this));
    $(this.textInputSpan).down('input',1).observe('click',this.submitValues.bindAsEventListener(this));
  },

  clearDefault: function (theEvent) {
    var theText = theEvent.element().value;
    if (theText == this.defaultText) theEvent.element().value = '';
  },

  findType: function () {
    var theType = null;
    for (var iterator = 0,last = this.textTypes.length; iterator < last; ++iterator) {
      if (this.textTypes[iterator].type == this.textType) theType = this.textTypes[iterator].name;
    }
    return theType;
  },

  submitValues: function() {
    this.findType();
    var theText = $(this.textInputSpan).down('input',0).value;
    if (theText == this.defaultText) {
      categoryList.resetList();
      return;
    }
    var theType = this.findType(); 
    var theTerm = '{"category":"text::'+theType+'","value":"'+theText+'","forDisplay":"'+theText+'"}';
    if (theType == 'data-metadata') {
      allSearchTerms.addTerm(theTerm,0);
    } else {
      allSearchTerms.addTerm(theTerm);
    }
    categoryList.resetList();
  },

  makeTextBox: function (theContainer) {
    //var originalInnerHTML = $(theContainer).innerHTML;
    var originalSelection = $(theContainer).descendants()[0].value
    var originalSelectionText = $(theContainer).descendants()[0].options[originalSelection].text;
    var callBack = ' theSearch.addTerm(\'freeText:'+ originalSelectionText +'\',\'freeTextString\')';
    var theHtml = '<input type=text id=freeTextString ></input>';
    theHtml += '<input type=button value="add" onClick=' + callBack;
    $(theContainer).insert(theHtml);
    $(theContainer).descendants()[0].value = originalSelection;

  }
}

//----------------------------------------------------------------
function restRequest(server,request,format,holderAttribute) {
  var requestData = 'server=' + server + '&format=' + format + '&request=' + request;
  new Ajax.Request('restRequest.php', {
                    asynchronous: false,
                    parameters: requestData,
                    onComplete: function(theResponse) {
                      var theJsonString = theResponse.responseText.strip();
                      eval(holderAttribute + ' = theJsonString.evalJSON()');
                    }
                  });
}

//----------------------------------------------------------------
var SelectMenu = Class.create();
Object.extend(SelectMenu.prototype,{
  initialize: function(initialText,theList,isActiveTemplate,theContainer,notActiveTemplate) {
    this.whatAmI = 'SelectMenu';
    this.initialText = (initialText || '');
    this.theList = theList;
    this.isActiveTemplate = new Template(isActiveTemplate);
    if (notActiveTemplate) {
      this.notActiveTemplate = new Template(notActiveTemplate);
    } else {
      this.notActiveTemplate = this.isActiveTemplate;
    }
    this.container = theContainer;
    this.myDOM = '';
    // 20090203 working around arming issues with IE
    // this.listHtml = '';
    this.listHtml = '<option value=-10>'+this.initialText;
    //
    for (var iterator = 0, last = this.theList.length; iterator < last; ++iterator) {
      thisValue = this.theList[iterator];
      if (thisValue.matches) {
        this.listHtml += this.isActiveTemplate.evaluate(thisValue);
      } else {
        this.listHtml += this.notActiveTemplate.evaluate(thisValue);
      }
    }
    this.makeMenu();
    $(this.myDOM).observe('focus',this.menuOnFocus.bind(this));
  },

  makeMenu: function () {
    var theHtml = '<select><option>'+this.initialText+'</select>';
    $(this.container).update(theHtml);
    this.myDOM = $(this.container).down();
  },

  menuOnFocus: function () {
    $(this.myDOM).update(this.listHtml);
  }
})

//----------------------------------------------------------------
function dbRequest(theList,holderAttribute) {
  var requestData = 'list=' + theList;
  new Ajax.Request('lib/amis/search/dbRequest.php', {
                    asynchronous: false,
                    parameters: requestData,
                    onComplete: function(theResponse) {
                      var theString = theResponse.responseText.strip();
                      eval(holderAttribute + ' = theString.evalJSON()');
                    }
                  });
}

//----------------------------------------------------------------
function dbRequest2(theList,holderAttribute,detailInfo) {
  var requestData = 'list=' + theList;
  if (detailInfo) { requestData += '&detailInfo=' + detailInfo; }
  new Ajax.Request('lib/amis/search/dbRequest.php', {
                    asynchronous: false,
                    parameters: requestData,
                    onComplete: function(theResponse) {
                      var theString = theResponse.responseText.strip();
                      eval(holderAttribute + ' = theString.evalJSON()');
                    }
                  });
}

//----------------------------------------------------------------
function zeroPad(num, width) {
  num = num.toString();
  while (num.length < width)
  num = "0" + num;
  return num;
}

//----------------------------------------------------------------
// NOTE: this version has been modified from the version in amisEntry.js
var GeoSpatialPointField = Class.create();
Object.extend(GeoSpatialPointField.prototype,{

  initialize: function (container,parentObject) {
    this.whatAmI = 'GeoSpatialPointField';
    this.container = $(container);
    this.parentObject = ( parentObject || null );
    this.myField = this.container.id;
    this.makeField();
    this.lonField = this.container.down('input',0);
    this.latField = this.container.down('input',1);
    this.warningsField = this.container.down('span',0);
    this.thePoint = null;
    this.addObservers();
  },

  makeField: function() {
    var theHtml = "<table>";
    theHtml += "<tr><td><label>longitude</label></td>";
    theHtml += "<td><input type=text size=6 value='' maxlength=8 /></td></tr>";
    theHtml += "<tr><td><label>latitude</label></td>";
    theHtml += "<td><input type=text size=6 value='' maxlength=8 /></td></tr>";
    theHtml += "<tr><td><span class=warnings style='color:red'></span></td></tr>";
    theHtml += "</table>";
    this.container.update(theHtml);
  },

  fillPoint: function(thePoint) {
    // expects "thePoint" to be a string of "lon lat" with the only
    // space being the delimiter between the two values
    if (thePoint.length > 0) {
      this.thePoint = thePoint.strip();
      var thePoints = this.thePoint.split(" ");
      this.lonField.value = thePoints[0];
      this.latField.value = thePoints[1];
    } else {
      this.thePoint = null;
      this.lonField.value = '';
      this.latField.value = '';
    }
  },

  reportValue: function () {
    var thePoint = this.lonField.value + " " + this.latField.value;
    this.thePoint = thePoint;
    return thePoint;
  },

  validateLon: function (theElement) {
    var myValue = theElement.value;
    if (myValue < -180 || myValue > 180) {
      this.lonField.value = '';
      $(this.warningsField).update('valid longitude range<br>is -180 -> +180');
      this.lonField.focus();
      return false;
    } else {
      $(this.warningsField).update('');
      return true;
    }
  },

  validateLat: function (theElement) {
    var myValue = theElement.value;
    if (myValue < -90 || myValue > 90) {
      this.latField.value = '';
      $(this.warningsField).update('valid latitude range<br>is -90 -> +90');
      this.latField.focus();
      return false;
    } else {
      $(this.warningsField).update('');
      return true;
    }
  },

  lonBlur: function (theEvent) {
    var theElement = theEvent.element();
    if (checkEmpty(theElement)) return;
    if (this.validateLon(theElement)) { 
      this.parentObject.updateFields(null,'form'); 
    }
  },

  latBlur: function (theEvent) {
    var theElement = theEvent.element();
    if (checkEmpty(theElement)) return;
    if (this.validateLat(theElement)) {
      this.parentObject.updateFields(null,'form');
    }
  },

  addObservers: function() {
    $(this.lonField).observe('blur',this.lonBlur.bindAsEventListener(this));
    $(this.latField).observe('blur',this.latBlur.bindAsEventListener(this));
  },

  checkNotEmpty: function (theValue) {
    // having an empty field causes a loop ...
    if (theValue.value == '') theValue.value=myDefaultValue;
  },

  dummy: function () {}
})

//----------------------------------------------------------------
var BoundingBoxField = Class.create();
Object.extend(BoundingBoxField.prototype,{

  initialize: function (theContainer) {
    this.container = theContainer;
    this.NWPointSpan = new Element('span');
    this.SEPointSpan = new Element('span');
    this.MapInputSpan = new Element('span');
    this.NWPoint = new GeoSpatialPointField($(this.NWPointSpan),this);
    this.SEPoint = new GeoSpatialPointField($(this.SEPointSpan),this);
    this.MapInput = new RegionInputMap($(this.MapInputSpan),this);
    this.makeField();
    this.addObservers();
  },

  makeField: function () {
    var theHtml = '<p>Enter region corners OR <span></span></p>';
    theHtml += '<table border="1"><tr><td><B>Northwest corner</B></td>';
    theHtml += '<td><B>Southeast corner</B></td>';
    //theHtml += '<td></td></tr>';
    theHtml += '</tr>';
    //theHtml += '<tr><td></td><td></td><td></td></tr></table>';
    theHtml += '<tr><td></td><td></td></tr></table>';
    theHtml += '<table><tr><td>';
    theHtml += '<input type=button value=Submit />';
    theHtml += '<input type=button value=Clear />';
    theHtml += '</td></tr></table>';
    var mySpan = new Element('span');
    $(mySpan).update(theHtml);
    $(mySpan).down('td',3).insert($(this.SEPointSpan));
    $(mySpan).down('td',2).insert($(this.NWPointSpan));
    $(mySpan).down('span',0).insert($(this.MapInputSpan));
    var theMapDiv = new Element('div',{id:'mapDiv', float:'left'});
    $(theMapDiv).hide();
    $(mySpan).insert(theMapDiv);
    var theMapPanelDiv = new Element('div',{id:'mapPanelDiv', 'class': 'olControlPanel', float:'left'});
    $(theMapPanelDiv).hide();
    $(mySpan).insert(theMapPanelDiv);
    $(mySpan).insert('<br clear="all"/>');
    $(this.container).update(mySpan);
  },

  addObservers: function () {
    $(this.container).down('input',4).observe('click',this.submitValues.bind(this));
    $(this.container).down('input',5).observe('click',this.clearFields.bind(this));
  },

  submitValues: function () {
    var NWPoint =  this.NWPoint.reportValue().split(' ');
    var SEPoint =  this.SEPoint.reportValue().split(' ');
    var forDisplay = '('+NWPoint+'),('+SEPoint+')';
    // these are lon lat points, convert to albers for the database
    var convertedCorners = lonlatCornersToAlbers(NWPoint,SEPoint)[0];
    var minCorner = convertedCorners[0].toString().replace(/,/g,' ');
    var maxCorner = convertedCorners[1].toString().replace(/,/g,' ');
    var theCorners = minCorner + ',' +maxCorner;
    var theTerm = '{"category":"region::custom","value":"' +theCorners+'","forDisplay":"' +forDisplay+'"}';
    allSearchTerms.addTerm(theTerm,0);
    allSearchTerms.addTerm(theTerm);
    categoryList.resetList();
  }, 
 
  updateFields: function (thePoints,theSource) {
    /* notifies each subobject to update its display. 
     * theSource: a string, 'form' or 'map'.  used to
     * keep the notifying object from being updated -> loop.
    */
    switch (theSource) {
      case 'map':
        this.NWPoint.fillPoint(thePoints[0].join(' '));
        this.SEPoint.fillPoint(thePoints[1].join(' '));
        break;
      case 'form':
        var theNWPoint = this.NWPoint.reportValue().strip();
        var NWPointArray =  theNWPoint.split(' ');
        var theSEPoint =  this.SEPoint.reportValue().strip();
        var SEPointArray =  theSEPoint.split(' ');
        var thePoints =[ [parseInt(NWPointArray[0]), 
                          parseInt(NWPointArray[1])], 
                         [parseInt(SEPointArray[0]), 
                          parseInt(SEPointArray[1])] ];
        this.MapInput.addBoxFromForm(thePoints);
        break;
      default:
        console.log('BoundingBoxField::updateFields -> invalid switch case');
    }
    
  },

  clearFields: function () {
    this.NWPoint.fillPoint(null);
    this.SEPoint.fillPoint(null);
  }
});

//----------------------------------------------------------------
function checkEmpty(theElement) {
  // returns a boolean
  if (theElement.value.length==0) return true;
  return false;
}

//----------------------------------------------------------------
var DateTimeField = Class.create();
// NOTE: this has been modified from the version in amisEntry.js
Object.extend(DateTimeField.prototype,{

  initialize: function (container,defaultEnd) {
    this.whatAmI = 'DateTimeField';
    this.container = $(container);
    this.defaultEnd = defaultEnd || 'start';  // 'start' or 'end'
    // if day,month,hour,min,second field are skipped, whether to
    // fill to the start or the end of the period
    this.myField = this.container.id;
    this.makeFields();
    this.monthField = $(container).down('input',0);
    this.dayField = $(container).down('input',1);
    this.yearField = $(container).down('input',2);
    this.hourField = $(container).down('input',3);
    this.minuteField = $(container).down('input',4);
    this.secondField = $(container).down('input',5);
    this.warningsField = $(container).down('span',0);
    this.theDateTime = null;
    this.yearChecked = false;
    this.addObservers();
  },

  makeFields: function () {
    var theHtml = "<table>";
    theHtml += "<td><input type='text' name='month' size='2' value='' maxlength='2' /></td>";
    theHtml += "<td><input type='text' name='day' size='2' value='' maxlength='2' /></td>";
    theHtml += "<td><input type='text' name='year' size='4' value='' maxlength='4' /></td>";
    theHtml += "<td>MM/DD/YYYY</td></tr>";
    theHtml += "<td><input type='text' name='hour' size='2' value='' maxlength='2' /></td>";
    theHtml += "<td><input type='text' name='minute' size='2' value='' maxlength='2' /></td>";
    theHtml += "<td><input type='text' name='second' size='2' value='' maxlength='2' /></td>";
    theHtml += "<td>HH:MM:SS (GMT)</td></tr>";
    theHtml += "<tr><td colspan='6'><span class=warnings style='color: red;'></span></td></tr>";
    theHtml += "</table>";
    this.container.update(theHtml);
  },

  fillDateTime: function (theDateTime) {
    this.theDateTime = theDateTime;
    this.secondField.value = theDateTime.substring(11,13);
    this.hourField.value = theDateTime.substring(14,16);
    this.minuteField.value = theDateTime.substring(17,19);
    this.yearField.value = theDateTime.substring(0,4);
    this.monthField.value = theDateTime.substring(5,7);
    this.dayField.value = theDateTime.substring(8,10);
  },

  reportValue: function () {
    var theDateTime = this.yearField.value+"-"+this.monthField.value+"-"+this.dayField.value;
    theDateTime += "/"+this.hourField.value+":"+this.minuteField.value+":"+this.secondField.value;
    this.theDateTime = theDateTime;
    return theDateTime;
  },

  addObservers: function () {
    $(this.minuteField).observe('keydown',this.catchAdvancing.bindAsEventListener(this));
    $(this.minuteField).observe('focus',this.minuteFocus.bindAsEventListener(this));
    $(this.minuteField).observe('blur',this.minuteBlur.bindAsEventListener(this));
    $(this.hourField).observe('keydown',this.catchAdvancing.bindAsEventListener(this));
    $(this.hourField).observe('focus',this.hourFocus.bindAsEventListener(this));
    $(this.hourField).observe('blur',this.hourBlur.bindAsEventListener(this));
    $(this.secondField).observe('keydown',this.catchAdvancing.bindAsEventListener(this));
    $(this.secondField).observe('focus',this.secondFocus.bindAsEventListener(this));
    $(this.secondField).observe('blur',this.secondBlur.bindAsEventListener(this));
    $(this.dayField).observe('keydown',this.catchAdvancing.bindAsEventListener(this));
    $(this.dayField).observe('focus',this.dayFocus.bindAsEventListener(this));
    $(this.dayField).observe('blur',this.dayBlur.bindAsEventListener(this));
    $(this.monthField).observe('keydown',this.catchAdvancing.bindAsEventListener(this));
    $(this.monthField).observe('focus',this.monthFocus.bindAsEventListener(this));
    $(this.monthField).observe('blur',this.monthBlur.bindAsEventListener(this));
    $(this.yearField).observe('keydown',this.catchAdvancing.bindAsEventListener(this));
    $(this.yearField).observe('focus',this.yearFocus.bindAsEventListener(this));
    $(this.yearField).observe('blur',this.yearBlur.bindAsEventListener(this));
    $(this.yearField).observe('change',this.setYearChecked.bindAsEventListener(this,false));
  },

  minuteFocus: function(theEvent) {
    var myElement = theEvent.element();
    this.highlightField(myElement);
  },

  minuteBlur: function(theEvent) {
    var myElement = theEvent.element();
    this.checkNotEmpty(myElement);
    this.prependZero(myElement);
    if (this.validateMinute(myElement)) $(this.warningsField).update('');
  },

  hourFocus: function(theEvent) {
    var myElement = theEvent.element();
    this.highlightField(myElement);
  },

  hourBlur: function(theEvent) {
    var myElement = theEvent.element();
    this.checkNotEmpty(myElement);
    this.prependZero(myElement);
    if (this.validateHour(myElement)) $(this.warningsField).update('');
  },

  secondFocus: function(theEvent) {
    var myElement = theEvent.element();
    this.highlightField(myElement);
  },

  secondBlur: function(theEvent) {
    var myElement = theEvent.element();
    this.checkNotEmpty(myElement);
    this.prependZero(myElement);
    if (this.validateSecond(myElement)) $(this.warningsField).update('');
  },

  dayFocus: function(theEvent) {
    var myElement = theEvent.element();
    this.highlightField(myElement);
  },

  dayBlur: function(theEvent) {
    var myElement = theEvent.element();
    this.checkNotEmpty(myElement,'1');
    this.prependZero(myElement);
    if (this.validateDay(myElement)) $(this.warningsField).update('');
  },

  monthFocus: function(theEvent) {
    var myElement = theEvent.element();
    this.highlightField(myElement);
  },

  monthBlur: function(theEvent) {
    var myElement = theEvent.element();
    this.checkNotEmpty(myElement,'1');
    this.prependZero(myElement);
    if (this.validateMonth(myElement)) $(this.warningsField).update('');
  },

  yearFocus: function(theEvent) {
    var myElement = theEvent.element();
    this.highlightField(myElement);
  },

  yearBlur: function(theEvent) {
    var myElement = theEvent.element();
    this.checkNotEmpty(myElement);
    this.prependCentury(myElement);
    if (this.validateYear(myElement)) $(this.warningsField).update('');
  },

  catchAdvancing: function (theEvent) {
    // how to make this work with anonymous fields ...
    var me = theEvent.element();
    if (theEvent.keyCode == Event.KEY_RETURN || theEvent.keyCode == Event.KEY_TAB) {
      switch (me.name) {
        case 'day' :
          if (!this.validateDay(me)) Event.stop(theEvent);
          break;
       case 'month' :
          if (!this.validateMonth(me)) Event.stop(theEvent);
          break;
       case 'year' :
          break;
        case 'minute' :
          if (!this.validateMinute(me)) Event.stop(theEvent);
          break;
       case 'hour' :
          if (!this.validateHour(me)) Event.stop(theEvent);
          break;
       case 'second' :
          if (!this.validateSecond(me)) Event.stop(theEvent);
          break;
       default:
          alert('invalid switch case: ' + me.name);
          break;
      }
    }
  },

  checkNotEmpty: function (theValue,defaultValue) {
    var myDefaultValue = defaultValue || '0';
    if (theValue.value == '') theValue.value=myDefaultValue;
  },

  prependZero: function (theValue) {
    if (theValue.value.length < 2) {
      theValue.value = '0' + theValue.value;
    }
  },

  prependCentury: function (theYear) {
    if (theYear.value.length < 2) {
      theYear.value = '200' + theYear.value;
    }
    if (theYear.value.length < 3) {
      if (theYear.value < 50) {
        theYear.value = '20' + theYear.value;
      } else if (theYear.value > 49) {
        theYear.value = '19' + theYear.value;
      }
    }
  },

  autoTab: function(theEvent,theTarget) {
    var myEvent = theEvent.element();
    if (myEvent.value.length == myEvent.maxLength) {
      $(theTarget).focus();
    }
  },

  validateMinute: function(minuteField) {
    // just screens for ridiculous values; full date validation is later.
    // zero is considered acceptable as a 'missing value' marker
    if (minuteField.value < 0 || minuteField.value > 59) {
      minuteField.value = '';
      minuteField.focus();
      $(this.warningsField).update('invalid minute entered');
      return false;
    }
    return true;
  },

  validateHour: function(hourField) {
    // zero is considered acceptable as a 'missing value' marker
    if (hourField.value < 0 || hourField.value > 23) {
      hourField.value = '';
      hourField.focus();
      $(this.warningsField).update('invalid hour entered');
      return false;
    }
    return true;
  },

  validateSecond: function(secondField) {
    // just screens for ridiculous values; full date validation is later.
    // zero is considered acceptable as a 'missing value' marker
    if (secondField.value < 0 || secondField.value > 59) {
      secondField.value = '';
      secondField.focus();
      $(this.warningsField).update('invalid second entered');
      return false;
    }
    return true;
  },

  validateDay: function(dayField) {
    // just screens for ridiculous values; full date validation is later.
    // zero is considered acceptable as a 'missing value' marker
    if (dayField.value < 0 || dayField.value > 31) {
      dayField.value = '';
      dayField.focus();
      $(this.warningsField).update('invalid day entered');
      return false;
    }
    return true;
  },

  validateMonth: function(monthField) {
    // zero is considered acceptable as a 'missing value' marker
    if (monthField.value < 0 || monthField.value > 12) {
      monthField.value = '';
      monthField.focus();
      $(this.warningsField).update('invalid month entered');
      return false;
    }
    return true;
  },

  validateYear: function(yearField) {
    // will need a flag to make sure this is done only once.
    // zero is considered acceptable as a 'missing value' marker
    this.yearChecked = true;
    if (yearField.value == 0) return true;
    if ((yearField.value < 1900 || yearField.value > 2100) && !this.yearChecked) {
      $(this.warningsField).update('year value seems extreme; please verify');
    }
    return true;
  },

  setYearChecked: function(theState) {
    // theState is true or false
    this.yearChecked = theState;
  },

  clearDefaultField: function(theField) {
    // call when text field gets focus
    if ((theField.defaultValue == theField.value) && (theField.defaultValue != '')) {
      theField.value = '';
    }
  },

  highlightField: function(theField) {
    // can be done inline
    theField.select();
  }
})

//----------------------------------------------------------------
var TimeBoundsField = Class.create();
Object.extend(TimeBoundsField.prototype,{

  initialize: function (theContainer) {
    this.container = $(theContainer);
    this.startDateTimeSpan = new Element('span');
    this.endDateTimeSpan = new Element('span');
    this.startDateTime = new DateTimeField($(this.startDateTimeSpan));
    this.endDateTime = new DateTimeField($(this.endDateTimeSpan));
    this.makeField();
    this.addObservers();
  },

  makeField: function () {
    var mySpan = new Element('span');
    $(mySpan).update('<table><tr><td>Starting date-time</td><td>');
    $(mySpan).insert($(this.startDateTimeSpan));
    $(mySpan).insert('</td></tr><tr><td>Ending date-time</td><td>');
    $(mySpan).insert($(this.endDateTimeSpan));
    $(mySpan).insert('</td></tr></table>');
    $(mySpan).insert('<table><tr><td>');
    $(mySpan).insert('<input type=button value=Submit />');
    $(mySpan).insert('<input type=button value=Clear />');
    $(mySpan).insert('</td></tr></table>');
    $(this.container).update(mySpan);
  },

  submitValues: function () {
    var startDateTime = this.startDateTime.reportValue();
    var endDateTime = this.endDateTime.reportValue();
    var forDisplay = startDateTime+'Z to '+endDateTime+'Z';
    var theValue = startDateTime+'::'+endDateTime;
    var theTerm = '{"category":"timeBounds","value":"' +theValue+'","forDisplay":"' +forDisplay+'"}'
    allSearchTerms.addTerm(theTerm,0);  // data search
    allSearchTerms.addTerm(theTerm);    // project search
    categoryList.resetList();
  },

  clearFields: function () {
    this.startDateTime.fillDateTime('');
    this.endDateTime.fillDateTime('');
  },

  addObservers: function () {
    $(this.container).down('input',12).observe('click',this.submitValues.bind(this));
    $(this.container).down('input',13).observe('click',this.clearFields.bind(this));
  }
});

//----------------------------------------------------------------
var RegionInputMap = Class.create();
Object.extend(RegionInputMap.prototype,{

  initialize: function (theContainer,parentObject) {
    this.whatAmI = 'RegionInputMap';
    this.container = theContainer;
    this.parentObject = ( parentObject || null );
    this.makeLink();
    this.addObservers();
    this.theMap = this.makeMapObject();
  },

  makeLink: function() {
    var theHtml = '<span class=alink>Select polygon on map<img src="/amis/theme/img/draw_rect_off.gif"></span>';
    $(this.container).update(theHtml);
  },

  makeMapObject: function () {
    theMap = new Object();
    theMap.baseMap = null;
    theMap.controlPanel = null;
    theMap.drawLayer = null;
    theMap.container ='mapDiv';
    theMap.panelContainer ='mapPanelDiv';
    theMap.width = 500;
    theMap.height = 400;
    theMap.reportTo = this.parentObject;
    return theMap;
  },

  makeMap: function () {
    if( this.theMap.baseMap==undefined ) {
      Proj4js.defs["EPSG:3338"] = "+proj=aea +lat_1=55 +lat_2=65 +lat_0=50 +lon_0=-154 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs";
      aoos_wms = "http://ak.aoos.org/op/mapserver/ows.php?";
      options = {
        maxExtent: new OpenLayers.Bounds(-1833104.77, 289608.57, 1698564.68, 2938360.66),
        width: theMap.width,
        height: theMap.height,
        controls: [],
        units: 'm',
        projection: new OpenLayers.Projection("EPSG:3338"),
        displayProjection: new OpenLayers.Projection("EPSG:4326")
      };
      options.maxResolution = Math.round((options.maxExtent.right-options.maxExtent.left)/theMap.width);
      this.makeBaseMap(this.theMap);
      this.makeBasicControls(this.theMap);
      this.makeDrawBoxLayer(this.theMap);
      this.addControlPanel(this.theMap);
      $(this.theMap.container).show();
      $(this.theMap.panelContainer).show();
    }
  },

  makeBaseMap: function(theMap) {
    // creating base map
    var baseMap = new OpenLayers.Map(theMap.container, options );
    var layer = new OpenLayers.Layer.WMS.Untiled( "Coastlines", aoos_wms, {layers: 'coastlines_l'});
    layer.setIsBaseLayer(true);
    layer.setVisibility(true);
    baseMap.addLayer(layer);
    if (!baseMap.getCenter()) baseMap.zoomToMaxExtent();
    theMap.baseMap = baseMap;
  },

  makeBasicControls: function(theMap) {
    theMap.baseMap.addControl(new OpenLayers.Control.MousePosition());
    zoom_box = new OpenLayers.Control.ZoomBox({title:"Zoom box"}),
    zoom_out = new OpenLayers.Control.ZoomToMaxExtent({title:"Zoom out"});
      theControlPanel = new OpenLayers.Control.Panel({defaultControl: zoom_box, div: $('mapPanelDiv')});
    theControlPanel.addControls([ new OpenLayers.Control.MouseDefaults({title:"Pan"}), zoom_box, zoom_out ]);
    theMap.controlPanel = theControlPanel;
  },

  makeExistingBoundsLayer: function (theMap) {
    // the layer for showing any existing region boundaries.
    // no event handling required.
    existingBoundsLayer = new OpenLayers.Layer.Vector("existingBoundsLayer");
    existingBoundsLayer.setVisibility(true);
    theMap.baseMap.addLayers([existingBoundsLayer]);
  },
// perhaps already deprecated

  addExistingBounds: function (theCorners) {
    // "theCorners" is a two element array [NWCorner, SECorner]
    // of two element arrays [lon lat]
    var albersCorners = lonlatToAlbers(theCorners);
    var cornerPoints = new Array (new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[0][1]),
                                  new OpenLayers.Geometry.Point(albersCorners[1][0], albersCorners[0][1]),
                                  new OpenLayers.Geometry.Point(albersCorners[1][0], albersCorners[1][1]),
                                  new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[1][1]),
                                  new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[0][1]));
    var ring = new OpenLayers.Geometry.LinearRing(cornerPoints);
    var theBox = new OpenLayers.Geometry.Polygon(ring);
    var theFeature = new OpenLayers.Feature.Vector(theBox);
    var theLayer = this.theMap.baseMap.getLayersByName('existingBoundsLayer')[0];
    theLayer.addFeatures([theFeature]);
  },

  makeDrawBoxLayer: function(theMap) {
    selectLayer = new OpenLayers.Layer.Vector("select_layer");
    selectLayer.setVisibility(true);
    // ------------- drawing event handlers
    // when starting to draw a box, first remove any box that had already been drawn
    selectLayer.events.register("beforefeatureadded", selectLayer, function(event) {
      if (selectLayer.features.length > 1) { selectLayer.removeFeatures(selectLayer.features[0]); }
    });
    // after a box is drawn, get the corners, transform the coordinates, and update the form fields
    selectLayer.events.register("featureadded", selectLayer, function(event) {
      // if featureadded triggered through form value change, then abort to avoid overwriting values
      // "this" is "selectLayer" OpenLayers.Layer.Vector
      if (this.features[0].attributes.lock) { return; }

      // get the corners of the box; note: in epsg:102006
      var bounds = this.features[0].geometry.getBounds();
      var albersCorners = new Array( new Array(bounds.left, bounds.top),new Array(bounds.right, bounds.bottom));
      var lonlatCorners = albers2lonlat(albersCorners);
      // here: update the form fields with the lon and lat values
      theMap.reportTo.updateFields(lonlatCorners,'map');
    });
    // -------------- end of event handlers
    theMap.baseMap.addLayers([selectLayer]);
    rectControl = new OpenLayers.Control.DrawFeature(selectLayer, OpenLayers.Handler.RegularPolygon,
                                                  {handlerOptions: {sides: 4, irregular: true},
                                                   displayClass: 'olControlDrawRect', title:'Select Region'});
    theMap.controlPanel.addControls(rectControl);
  },

  addControlPanel: function(theMap) {
    theMap.baseMap.addControl(theMap.controlPanel);
  },

  addBoxFromForm: function(theCorners) {
    // "theCorners" is a two element array [NWCorner, SECorner]
    // of two element arrays [lon lat]
    var albersCorners = lonlatToAlbers(theCorners);
    var cornerPoints = new Array (new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[0][1]),
                                  new OpenLayers.Geometry.Point(albersCorners[1][0], albersCorners[0][1]),
                                  new OpenLayers.Geometry.Point(albersCorners[1][0], albersCorners[1][1]),
                                  new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[1][1]),
                                  new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[0][1]));
    var ring = new OpenLayers.Geometry.LinearRing(cornerPoints);
    var theBox = new OpenLayers.Geometry.Polygon(ring);
    var theLayer = this.theMap.baseMap.getLayersByName('select_layer')[0];
    var theFeature = new OpenLayers.Feature.Vector(theBox,{'lock':1});
    // lock attribute will be checked to identify changes made in the form
    theLayer.addFeatures([theFeature]);
  },

  addObservers: function (theMapContainer) {
    $(this.container).down('span',0).observe('click',this.makeMap.bindAsEventListener(this));
  }
})


//----------------------------------------------------------------
function displayAllResults(theResultsJSON) {
  var theContainer = $('searchResultsDiv');
  theContainer.update();
  var theResults = theResultsJSON.evalJSON();
  var theHtml = '<table width=50%><tbody><tr><td valign=top>';
  // left column is project results, right column is data results
  // the graphic is shown in a div below the table
  theHtml += '<table><tbody><thead><font color=green>Proposal/Project Results</font></thead>';
  if (theResults.length < 1) {
    theHtml += '<h1>No Matching Proposals or Projects found.</h1>';
  } else {
    for (var iterator=0, last=theResults.length; iterator<last; ++iterator) {
      var theText = displayResult(theResults[iterator]);
      theHtml += '<tr><td style="border-top: 2px; color:#069">'+theText+'</td></tr>';
    }
  }
  theHtml += '</tbody></table>';
  theHtml += '</td><td valign=top>';
  theHtml += '<table><tbody><thead><font color=green>Dataset Results</font></thead>';
    //  the data results table
  theHtml += '<tr><td><form id=amis.search.form action="javascript:{}"><div id=datasetSearchResults /></form>';
  theHtml += '</td></tr></table>';
  theHtml += '</td></tr></table>';
  $(theContainer).insert(theHtml);

  var mapDiv = new Element('div',{id:'outputMapDiv', width:'500', height:'400'});
  $(theContainer).insert(mapDiv);
  var thisMapOutput = new RegionOutputMap($(mapDiv));
  thePage.mapOutput = thisMapOutput;
  $(theContainer).insert('</div>');
  $(theContainer).show();
  var theUids = theResults.pluck('uid');
  thisMapOutput.updateProjectsWFSLayer(theUids.join(','));

}


//----------------------------------------------------------------
function displayAllResults2(theResults) {
  var theContainer = $('searchResultsDiv');
  theContainer.update();  // remove previous results

  var reportLabels = new Object();
  reportLabels.uid = null;   // null label value indicates do not display value
  reportLabels.fullTitle = "Full Title";
  reportLabels.subDate = "Submission Date";
  reportLabels.leadSci = "Principle Investigator"
  reportLabels.mainContact = null;
  reportLabels.abstract = "Abstract Text";
  reportLabels.notes = "Notes";
  reportLabels.startDate = "Funding Start Date";
  reportLabels.endDate = "Funding End Date";
  reportLabels.detailsLink = "Full Details";
//  reportLabels.dataLink = "Access Data";

  var outerTable = new Element('table',{width:'95%'});
  outerTable.insert(new Element('thead'));

  var leftInnerTable = new Element('table');
  leftInnerTable.insert((new Element('thead')).insert('Proposal/Project Results'));
  var leftInnerTableBody = new Element('tbody');
  if (theResults.length < 1) {
    leftInnerTableBody.insert((new Element('tr')).insert((new Element('td')).insert('<h1>No Matching Proposals or Projects found.</h1>')))
  } else {
    for (var iterator=0, last=theResults.length; iterator<last; ++iterator) {
      // create the link to directly access the data
      leftInnerTableBody.insert((new Element('tr')).insert((new Element('td')).insert(displayResult(theResults[iterator],reportLabels))));
    }
  }
  leftInnerTable.insert(leftInnerTableBody);
  leftInnerTable.insert(new Element('tfoot'));

  var rightInnerTable = new Element('table');
  rightInnerTable.insert((new Element('thead')).insert('Dataset Results'));
  var theForm = new Element('form',{id:'amis.search.form',action:'javascript:{}'});
  theForm.insert(new Element('div',{id:'datasetSearchResults'}));
  var rightInnerTableBody = new Element('tbody');
  rightInnerTableBody.insert((new Element('tr')).insert((new Element('td')).insert(theForm)))
  rightInnerTable.insert(rightInnerTableBody);
  rightInnerTable.insert(new Element('tfoot'));

  var outerTableBody = new Element('tbody');
  var leftInnerTableDiv = (new Element('div',{style:'overflow:auto;height:350px'})).insert(leftInnerTable);
  var rightInnerTableDiv = (new Element('div',{style:'overflow:auto;height:350px'})).insert(rightInnerTable);
  var td1 = (new Element('td',{valign:'top'})).insert(leftInnerTableDiv);
  var td2 = (new Element('td',{valign:'top'})).insert(rightInnerTableDiv);
  var td3 = (new Element('td',{valign:'top'}));
  var tr1 = new Element('tr');
  tr1.insert(td1);
  tr1.insert(td2);
  tr1.insert(td3);
  outerTableBody.insert(tr1);
  outerTable.insert(outerTableBody);
  outerTable.insert(new Element('tfoot'));
  $(theContainer).insert(outerTable);

  var mapDiv = new Element('div',{id:'outputMapDiv', style: 'width: 400px; height: 320px; float: left; border: 1px solid black;'});
  $(td3).insert(mapDiv);
  var panelDiv = new Element('div',{id:'outputMapPanelDiv', 'class': 'olControlPanel', style: 'margin-top: -320px; margin-left: 0px; width: 100px; z-index: 900; float: left; position: relative;'});
  $(td3).insert(panelDiv);
  var thisMapOutput = new RegionOutputMap($(mapDiv));
  thePage.mapOutput = thisMapOutput;
  $(theContainer).insert('</div>');
  $(theContainer).show();
  var theUids = theResults.pluck('uid');
  thisMapOutput.updateProjectsWFSLayer(theUids.join(','));
  thisMapOutput.updateDataWFSLayer(amis.search.dslist);
}

//----------------------------------------------------------------
function displayResult(aResult,reportLabels) {
  var tmpString = 'http://ak.aoos.org/amis/projectInfo.php?uids='+aResult.uid;
  aResult.detailsLink = '<a href="'+tmpString+'" target="_blank">'+tmpString+'</a>'; 
  var theEntryInfo = new Object();
  theEntryInfo.title = aResult.fullTitle;
  theEntryInfo.entries = aResult;
  theEntryInfo.labels = reportLabels || null;
  theEntry = new InflatingEntry(theEntryInfo);
  return (theEntry.returnEntry());
}

//----------------------------------------------------------------
function albers2lonlat (albersPoints) {
  // expects an array of 2 element arrays [[lon lat],[lon lat],[lon lat]]
  // expects albers (EPSG:3338), returns lon lat (EPSG:4326)
  var lonlatPoints = new Array();
  var albersProj = new OpenLayers.Projection('EPSG:3338');
  var lonlatProj = new OpenLayers.Projection('EPSG:4326');
  for (var pairIterator = 0, lastPair = albersPoints.length; pairIterator < lastPair; pairIterator++ ) {
    var thisPair = new OpenLayers.Geometry.Point(albersPoints[pairIterator][0],albersPoints[pairIterator][1]);
    OpenLayers.Projection.transform(thisPair, albersProj, lonlatProj);
    lonlatPoints.push(new Array(thisPair.x, thisPair.y));
  }
  return lonlatPoints;
}

//----------------------------------------------------------------
function lonlatToAlbers (lonlatPoints) {
  // expects an array of 2 element arrays [[x y],[x y],[x y]]
  //  expects lon lat (EPSG:4326), returns albers (EPSG:3338)
  var albersPoints = new Array();
  var albersProj = new OpenLayers.Projection('EPSG:3338');
  var lonlatProj = new OpenLayers.Projection('EPSG:4326');
  for (var pairIterator = 0, lastPair = lonlatPoints.length; pairIterator < lastPair; pairIterator++ ) {
    var thisPair = new OpenLayers.Geometry.Point(lonlatPoints[pairIterator][0],lonlatPoints[pairIterator][1]);
    OpenLayers.Projection.transform(thisPair, lonlatProj, albersProj);
    albersPoints.push(new Array(thisPair.x, thisPair.y));
  }
  return albersPoints;
}

//----------------------------------------------------------------
function lonlatCornersToAlbers(NWPoint,SEPoint) {
  // expects each point to be an two element array of form [lon lat].
  // constructs the polygon, converts all points, then finds the
  // minimum and maximum of both northing and easting.
  // returns two arrays both in albers coordinates:
  // [[minEasting minNorthing], [maxEasting maxNorthing], [NWPoint, NEPoint, SEPoint, SWPoint, NWPoint]
  var NEPoint = new Array(SEPoint[0],NWPoint[1]);
  var SWPoint = new Array(NWPoint[0],SEPoint[1]);
  var thePolygon = new Array(NWPoint,NEPoint,SEPoint,SWPoint,NWPoint);
  var albersPolygon = lonlatToAlbers(thePolygon);
  var eastings = new Array();
  var northings = new Array();
  albersPolygon.each(function(thisPair) {
    eastings.push(thisPair[0]);
    northings.push(thisPair[1]);
  });
  var albersMins = new Array(eastings.min(),northings.min());
  var albersMaxs = new Array(eastings.max(),northings.max());
  var toReturn = new Array();
  toReturn.push(new Array(albersMins,albersMaxs));
  toReturn.push(albersPolygon);
  return toReturn;  
}

//----------------------------------------------------------------
var RegionOutputMap = Class.create();
Object.extend(RegionOutputMap.prototype,{

  initialize: function (theMapContainer) {
    this.whatAmI = 'RegionOutputMap';
    this.mapContainer = theMapContainer;
    this.addObservers();
    this.theMap = this.makeMapObject();
    this.makeMap();
  },

  makeMapObject: function () {
    theMap = new Object();
    theMap.regionOutputMap = this;
    theMap.baseMap = null;
    theMap.controlPanel = null;
    theMap.drawLayer = null;
    theMap.container =this.mapContainer.identify();
    theMap.width = 500;
    theMap.height = 400;
    return theMap;
  },

  makeMap: function () {
    Proj4js.defs["EPSG:3338"] = "+proj=aea +lat_1=55 +lat_2=65 +lat_0=50 +lon_0=-154 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs";
    aoos_wms = "http://ak.aoos.org/op/mapserver/ows.php?";
    options = {
      maxExtent: new OpenLayers.Bounds(-1833104.77, 289608.57, 1698564.68, 2938360.66),
      width: theMap.width,
      height: theMap.height,
      controls: [],
      units: 'm',
      projection: new OpenLayers.Projection("EPSG:3338"),
      displayProjection: new OpenLayers.Projection("EPSG:4326")
    };
    options.maxResolution = Math.round((options.maxExtent.right-options.maxExtent.left)/theMap.width);
    this.makeBaseMap(this.theMap,options);
    this.makeBasicControls(this.theMap);
    this.makeProjectWFSLayer(this.theMap);
    this.makeDataWFSLayer(this.theMap);
    this.makeDrawBoxLayer(this.theMap);
    this.makeRegionsWFSLayer(this.theMap);
    this.addControlPanel(this.theMap);
    $(this.theMap.container).show();
  },

  makeBaseMap: function(theMap,theOptions) {
    // creating base map
    var baseMap = new OpenLayers.Map(theMap.container, theOptions );
    var layer = new OpenLayers.Layer.WMS.Untiled( "Coastlines", aoos_wms, {layers: 'coastlines_l'});
    layer.setIsBaseLayer(true);
    layer.setVisibility(true);
    baseMap.addLayer(layer);
    if (!baseMap.getCenter()) baseMap.zoomToMaxExtent();
    theMap.baseMap = baseMap;
  },

  makeBasicControls: function(theMap) {
    theMap.baseMap.addControl(new OpenLayers.Control.MousePosition());
    zoom_box = new OpenLayers.Control.ZoomBox({title:"Zoom box"});
    zoom_out = new OpenLayers.Control.ZoomToMaxExtent({title:"Zoom out"});
    theControlPanel = new OpenLayers.Control.Panel({defaultControl: zoom_box, div: $('outputMapPanelDiv')});
    theControlPanel.addControls([ new OpenLayers.Control.MouseDefaults({title:"Pan"}), zoom_box, zoom_out ]);
    theMap.controlPanel = theControlPanel;
  },

  resetMap: function() {

  },

  addExistingBounds: function (theCorners,theProjection) {
    // "theCorners" is an array [lon/westing, lat/northing]
    // of two element arrays [lon lat]
    theProjection = ( theProjection || 'lonlat' );
    if (theProjection != 'albers') {
      var albersCorners = lonlatToAlbers(theCorners);
    } else {
      var albersCorners = theCorners;
    }
    switch (albersCorners.length) {
      case 0: 
        // somethin' just ain't right
        break;
      case 1:
        // its a point; not dealt with at this time
        break;
      case 2:
        // bounding box; assume points are NW corner and SE corner
        var cornerPoints = new Array (new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[0][1]),
                                      new OpenLayers.Geometry.Point(albersCorners[1][0], albersCorners[0][1]),
                                      new OpenLayers.Geometry.Point(albersCorners[1][0], albersCorners[1][1]),
                                      new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[1][1]),
                                      new OpenLayers.Geometry.Point(albersCorners[0][0], albersCorners[0][1]));
        break;
      default:
        // assume its an arbitrary, closed polygon
        var cornerPoints = new Array();
        for (var iterator=0, last=albersCorners.length; iterator < last; ++iterator) {
          cornerPoints.push(new OpenLayers.Geometry.Point(albersCorners[iterator][0], albersCorners[iterator][1]));
        }
    }
    var ring = new OpenLayers.Geometry.LinearRing(cornerPoints);
    var theBox = new OpenLayers.Geometry.Polygon(ring);
    var theFeature = new OpenLayers.Feature.Vector(theBox);
    var theLayer = this.theMap.baseMap.getLayersByName('existingBoundsLayer')[0];
    theLayer.addFeatures([theFeature]);
  },

  makeDrawBoxLayer: function(theMap) {
    selectLayer = new OpenLayers.Layer.Vector("select_layer");
    selectLayer.setVisibility(true);
    // ------------- drawing event handlers
    // when starting to draw a box, first remove any box that had already been drawn
    selectLayer.events.register("beforefeatureadded", selectLayer, function(event) {
      if (selectLayer.features.length > 1) { selectLayer.removeFeatures(selectLayer.features[0]); }
    });
    // after a box is drawn, get the corners and transform the coordinates
    selectLayer.events.register("featureadded", selectLayer, function(event) {
      // get the corners of the box; note: in epsg:102006
      var bounds = this.features[0].geometry.getBounds();
      var albersCorners = new Array( new Array(bounds.left, bounds.top),new Array(bounds.right, bounds.bottom));
      var lonlatCorners = albers2lonlat(albersCorners);
      // use the entered values to update the search parameters
      theMap.regionOutputMap.updateSearchByRegion(lonlatCorners,albersCorners);
      // theMap.reportTo.updateFields(lonlatCorners,'map');
    });
    // -------------- end of event handlers
    theMap.baseMap.addLayers([selectLayer]);
    rectControl = new OpenLayers.Control.DrawFeature(selectLayer, OpenLayers.Handler.RegularPolygon,
                                                  {handlerOptions: {sides: 4, irregular: true},
                                                   displayClass: 'olControlDrawRect', title:'Select Region'});
    theMap.controlPanel.addControls(rectControl);
  },

  addControlPanel: function(theMap) {
    theMap.baseMap.addControl(theMap.controlPanel);
  },

  updateSearchByRegion: function (lonlatCorners,albersCorners) {
    var newTermObject = new Object();
    newTermObject.category = 'region::custom';
    newTermObject.value = albersCorners[0][0] + " " + albersCorners[0][1] +","+ albersCorners[1][0] + " " + albersCorners[1][1];
//    newTermObject.forDisplay = "("+lonlatCorners[0][0]+","+lonlatCorners[0][1]+"),("+lonlatCorners[1][0]+" "+lonlatCorners[1][1]+")";
    newTermObject.forDisplay = "(" + (Math.round(lonlatCorners[0][0]*1000)/1000);
    newTermObject.forDisplay += "," + (Math.round(lonlatCorners[0][1]*1000)/1000);
    newTermObject.forDisplay += "),(" + (Math.round(lonlatCorners[1][0]*1000)/1000);
    newTermObject.forDisplay += "," + (Math.round(lonlatCorners[1][1]*1000)/1000) + ")";
    allSearchTerms.overloadRegionSearch(Object.toJSON(newTermObject));
  },

  addObservers: function () {
  }
})

//----------------------------------------------------------------
// Steve Gaffigan's extensions to RegionInputMap, applied to RegionOutputMap
Object.extend(RegionOutputMap.prototype,{
  makeProjectWFSLayer: function(theMap) {
      // Add WFS layer for project bounds
      // Add control for selecting features
      var styleMap = new OpenLayers.StyleMap({strokeColor: "red",strokeWidth: 2,fillOpacity: 0});
      var layer = new OpenLayers.Layer.WFS("wfs_projects", 'http://ak.aoos.org/cgi-bin/sumplots_cgi.py?',
                                           {request2: 'projectShapes', PROJECTUID: ''},
                                           {styleMap: styleMap, isBaseLayer: false, visibility: true, extractAttributes: true});
      theMap.baseMap.addLayer(layer);
      var selectStyle = OpenLayers.Util.applyDefaults({strokeColor: '#ff0000', strokeWidth: 4, strokeOpacity: 0.9},
                                                       OpenLayers.Feature.Vector.style["select"]);
      theMap.controlPanel.addControls([new OpenLayers.Control.SelectFeature(layer,
                                      {hover: true, selectStyle: selectStyle, title: 'wfs_projects', displayClass: 'olControlSelectProject'})]);
  },

  updateProjectsWFSLayer: function(projectuid) {
      // Update WFS projects layer with given projectuid
      // Argument should be a comma-delimited list
      this.theMap.baseMap.getLayersByName("wfs_projects")[0].mergeNewParams({projectuid: projectuid});
  },

  getProjectWFSFeature: function(projectuid) {
      // Return feature from WFS project layer corresponding to given (single) projectuid
      var layer = this.theMap.baseMap.getLayersByName("wfs_projects")[0];
      for (var i=0; i<layer.features.length; i++) {
          if (layer.features[i].attributes.projectUid==projectuid) {
              return layer.features[i];
          }
      }
  },

  selectProjectWFSFeature: function(projectuid) {
      // Select feature from WFS project layer corresponding to given (single) projectuid
      var feature = this.getProjectWFSFeature(projectuid);
      if (feature != null) {
          this.theMap.controlPanel.getControlsBy('title', 'wfs_projects')[0].select(feature);
      }
  },

  unselectProjectWFSFeature: function(projectuid) {
      // Unselect feature from WFS project layer corresponding to given (single) projectuid
      var feature = this.getProjectWFSFeature(projectuid);
      if (feature != null) {
          this.theMap.controlPanel.getControlsBy('title', 'wfs_projects')[0].unselect(feature);
      }
  },

  makeDataWFSLayer: function(theMap) {
      // Add WFS layer for data bounds
      // Add control for selecting features
      var styleMap = new OpenLayers.StyleMap({strokeColor: "red",strokeWidth: 2,fillOpacity: 0});
      var layer = new OpenLayers.Layer.WFS("wfs_datafiles", 'http://ak.aoos.org/cgi-bin/sumplots_cgi.py?',
                                           {request2: 'datafilesWFS'},
                                           {styleMap: styleMap, isBaseLayer: false, visibility: true, extractAttributes: true});
      theMap.baseMap.addLayer(layer);
      var selectStyle = OpenLayers.Util.applyDefaults({strokeColor: '#ff0000', strokeWidth: 4, strokeOpacity: 0.9},
                                                       OpenLayers.Feature.Vector.style["select"]);
      theMap.controlPanel.addControls([new OpenLayers.Control.SelectFeature(layer,
                                      {hover: true, selectStyle: selectStyle, title: 'wfs_datafiles', displayClass: 'olControlSelectDataset'})]);
  },

  updateDataWFSLayer: function(dslist) {
      // Update WFS data layer with given datasets
      if (dslist==null) return;
      var dslist2 = new Array();
      var keys = ['id','id2'];
      for (var i=0; i<dslist.length; i++) {
	  dslist2[i] = {dsid: dslist[i].dsid};
	  for (var j=0; j<keys.length; j++) {
              if (dslist[i][keys[j]] != null) {
                  dslist2[i][keys[j]] = dslist[i][keys[j]];
              }
          }
      }
      // this breaks in IE with large urls due to GET limitation
      try {
	  this.theMap.baseMap.getLayersByName("wfs_datafiles")[0].mergeNewParams({dslist: escape(Object.toJSON(dslist2))});
      } catch(err) {
      }
  },

  getDataWFSFeature: function(item) {
      // Return feature from WFS data layer corresponding to given (single) dataset
      var a,layer = this.theMap.baseMap.getLayersByName("wfs_datafiles")[0];
      for (var i=0; i<layer.features.length; i++) {
	  a = layer.features[i].attributes;
          if (a.dsid==item.dsid && a.id==item.id && a.id2==item.id2) {
              return layer.features[i];
          }
      }
  },

  selectDataWFSFeature: function(itemstr) {
      // Select feature from WFS data layer corresponding to given (single) dataset
      var item = unescape(itemstr).evalJSON();
      var feature = this.getDataWFSFeature(item);
      if (feature != null) {
          this.theMap.controlPanel.getControlsBy('title', 'wfs_datafiles')[0].select(feature);
      }
  },

  unselectDataWFSFeature: function(itemstr) {
      // Unselect feature from WFS data layer corresponding to given (single) dataset
      var item = unescape(itemstr).evalJSON();
      var feature = this.getDataWFSFeature(item);
      if (feature != null) {
          this.theMap.controlPanel.getControlsBy('title', 'wfs_datafiles')[0].unselect(feature);
      }
  },

  makeRegionsWFSLayer: function(theMap) {
      // Add WFS layer for regions
      // Add control for selecting features
      var styleMap = new OpenLayers.StyleMap({strokeWidth: 1, strokeColor: 'yellow', strokeOpacity: 0, fillOpacity: 0});
      var layer = new OpenLayers.Layer.WFS("wfs_regions", 'http://ak.aoos.org/cgi-bin/sumplots_cgi.py?',
                                           {request2: 'regionsWFS'},
                                           {styleMap: styleMap, isBaseLayer: false, visibility: true, extractAttributes: true});
      theMap.baseMap.addLayer(layer);
      var selectStyle = OpenLayers.Util.applyDefaults({strokeColor: 'red', strokeWidth: 2, strokeOpacity: 0.9, fillColor: 'yellow'},
                                                       OpenLayers.Feature.Vector.style["select"]);
      theMap.controlPanel.addControls([new OpenLayers.Control.SelectFeature(layer,
                                      {hover: true, selectStyle: selectStyle, title: 'wfs_regions', displayClass: 'olControlSelectRegion'})]);
  },

  getRegionWFSFeature: function(name) {
      var layer = this.theMap.baseMap.getLayersByName("wfs_regions")[0];
      for (var i=0; i<layer.features.length; i++) {
	  if (layer.features[i].attributes.name == name) {
              return layer.features[i];
          }
      }
  },

  selectRegionWFSFeature: function(name, unselect) {
      var layer, control, feature;

      layer = this.theMap.baseMap.getLayersByName("wfs_regions")[0];
      control = this.theMap.controlPanel.getControlsBy('title', 'wfs_regions')[0];
      for (var i=0; i<layer.features.length; i++) {
	  feature = layer.features[i];
	  if (feature.attributes.name == name && !unselect) {
	      control.select(feature);
	  } else {
	      control.unselect(feature);
	  }
      }
  }
});

//--------------------------------------------------------------
function onElementFocused(e)
{
    if (e && e.target)
        document.activeElement = (e.target == document ? null : e.target);
} 

// -------------------------------------------------------
var InflatingEntry = Class.create();
Object.extend(InflatingEntry.prototype,{

  initialize: function (info) {
    this.whatAmI = 'listEntry';
    this.container = new Element('span');
    this.title = info.title;
    this.entries = info.entries;  // object
    this.labels = info.labels;    // object
    this.state = 'isClosed';      // isClosed or isOpen
    this.openImage = new Element('img',{src:'/amis/theme/img/open_item.gif'});
    this.closeImage = new Element('img',{src:'/amis/theme/img/close_item.gif'});
    this.makeFramework();
    this.makeTable();
    this.addObservers();
  },

  returnEntry: function () {
    return this.container;
  },

  makeFramework: function () {
    this.titleSpan = new Element('span');
    this.titleSpan.insert('<i style="font-size:110%; color:#960">'+this.title+'</i>');
    this.iconSpan = new Element('span');
    this.iconSpan.insert(this.openImage);
    this.bodySpan = new Element('span');
    this.container.insert(this.iconSpan);
    this.container.insert(this.titleSpan);
    this.container.insert('<br>');
    this.container.insert(this.bodySpan);
    this.bodySpan.hide();
  },

  makeTable: function () {
    var myEntries = this.entries;
    var myLabels = this.labels;
    var theTable = new Element('table');
    theTable.insert(new Element('thead'));
    theTable.insert(new Element('tbody'));
    theTable.insert(new Element('tfoot'));
    var theValues = Object.values(this.entries);
    Object.keys(this.entries).each(function(theKey,iterator) {
      if (!Object.isArray(myEntries[theKey])) {
        if ((myEntries[theKey] != null) && (myEntries[theKey] != 'null')) { // ignoring keys where the value is null 
          if (myLabels[theKey] != null) { // and ignoring entries where the label has been set to null
            if (myLabels[theKey] != 'Abstract Text:') {
              var theTd = new Element('td').insert('<b>'+myLabels[theKey]+'</b>: '+myEntries[theKey]);
            } else {
              var theTdText = '<b>'+myLabels[theKey]+'</b>: ';
              theTdText += '<p style="font-family:Georgia; color:#069; font-size:"85%",font-stretch:"wider">'+myEntries[theKey]+'</p>';
              var theTd = new Element('td').insert(theTdText);
            }
            theTable.down('tbody').insert((new Element('tr')).insert(theTd));
          }
        }
      } else {
        if (myEntries[theKey].length > 0) {
          var theTd = new Element('td').insert(myLabels[theKey]+': '+myEntries[theKey]);
          theTable.down('tbody').insert((new Element('tr')).insert(theTd));
        }
      }
    })
    this.bodySpan.insert(theTable);
  },

  toggleState: function () {
    switch (this.state) {
      case 'isClosed':
        this.bodySpan.show();
        this.iconSpan.update(this.closeImage);
        this.state = 'isOpen';
        break;
      case 'isOpen':
        this.bodySpan.hide();
        this.iconSpan.update(this.openImage);
        this.state = 'isClosed';
        break;
      default:
        console.log('InflatingEntry::toggleState: invalid state');
    }
  },

  myMouseOver: function () {
    var myUid = this.entries.uid;
    thePage.mapOutput.selectProjectWFSFeature(myUid);
  },

  myMouseOut: function () {
    var myUid = this.entries.uid;
    thePage.mapOutput.unselectProjectWFSFeature(myUid);
  },

  addObservers: function () {
    $(this.iconSpan).observe('click',this.toggleState.bind(this));
    $(this.container).observe('mouseover',this.myMouseOver.bind(this));
    $(this.container).observe('mouseout',this.myMouseOut.bind(this));
  }

})

// -------------------------------------------------------
/*
var theSearch = {

  theSearchString: null,

  buildSearchString: function (searchTermsObject) {
    // building json string from term objects
    // leaving the 1st term set (ie, the data search) for now
    var searchString = '{"searchNumber":"1","termSets":[';
    for (var setIndex = 0, numberOfSets = searchTermsObject.termSets.length; setIndex < numberOfSets; ++setIndex) {
      searchString += '{"setNumber":"1","terms":[';
      if (searchTermsObject.termSets[setIndex].terms.length == 0 ) {
        searchString += '{"termNumber":"'+(termIndex+1)+'","termValue":"empty"},';
      } else {
        for (var termIndex = 0, numberOfTerms = searchTermsObject.termSets[setIndex].terms.length; termIndex < numberOfTerms; ++termIndex) {
          searchString += '{"termNumber":"'+(termIndex+1)+'","termValue":';
          searchString += searchTermsObject.termSets[setIndex].terms[termIndex];
          searchString += '},';
        }
      }
      searchString = searchString.substring(0, searchString.length-1);
      searchString += ']},';
    }
    searchString = searchString.substring(0, searchString.length-1);
    searchString += ']}';

    this.theSearchString = searchString;
  },

  doPropjSearch: function () {
    var searchJSON = Object.toJSON(searchString.evalJSON());
    var myDoDataSearch = this.doDataSearch;
    var myPropjDataCrossSearchParams = this.propjDataCrossSearchParams;
    new Ajax.Request('lib/amis/search/search.php', {
                        asynchronous: false,
                        parameters: 'theSearch='+searchJSON,
                        onComplete: function (theResponse) {
                          // receives back the JSON encoded matching proposal information
                          thePage.ResultSet = (theResponse.responseText.strip()).evalJSON();
                          var existingParamsSearchstring = myPropjDataCrossSearchParams();
                          if (existingParamsSearchstring) {
                            var existingParams = new Object();
                            existingParams.projectuid = existingParamsSearchstring;
                            myDoDataSearch(existingParams);
                          } else {
                            myDoDataSearch();
                          }
                          displayAllResults2(thePage.ResultSet); // show project and data results;
                          amis.search.printDslist();

                        }
                    });
  },

  doDataSearch: function (existingParams) {
    // do the data search here and fill a separate div
    // building the 'params' object to submit to amis.search.dataSearch()
    // uses only the first termset
    var params = existingParams || new Object();
    var dataTermSet = allSearchTerms.termSets[0];
    for (var termIndex = 0, numberOfTerms = dataTermSet.terms.length; termIndex < numberOfTerms; ++termIndex) {
      var thisTerm = dataTermSet.terms[termIndex].evalJSON();
      switch (thisTerm.category) {
        case 'dataset':
          if (params.dsid) {
            params.dsid += ','+thisTerm.forDisplay;
          } else {
            params.dsid = thisTerm.forDisplay;
          }
          break;
        case 'region::predef':
          // have to do a lookup for the abbrev name
          for (var iterator = 0, last = region.regions.length; iterator < last; ++iterator) {
            if (region.regions[iterator].uid == thisTerm.value) {
              params.region = region.regions[iterator].abbrev;
            }
          }
          break;
        case 'region::custom':
          params.bbox = thisTerm.value;
          params.bbox = params.bbox.replace(/ /g,',');
          params.srs = 'EPSG:3338';
          break;
        case 'text::data-metadata':
          params.searchstring = thisTerm.value;
          break;
        case 'timeBounds':
          var tmpString = thisTerm.value;
          tmpString = tmpString.replace(/\//g,'T');
          tmpString = tmpString.replace(/::/,'Z/');
          params.timeextent = tmpString + 'Z';
          break;
        case 'variable':
          if (params.cvlist) {
            params.cvlist += ','+thisTerm.value;
          } else {
            params.cvlist = thisTerm.value;
          }
          break;
      }
    }
    // 'params' is built
    if (Object.keys(params).length > 0) {
      amis.search.dataSearch(params);
      // the above creates an object "amis.search.dslist" which describes the dataset
    } else {
      var theHtml = '<div><h1>No Matching Datasets.</h1></div>';
      $('datasetSearchResults').update(theHtml);
    }
  },

  propjDataCrossSearchParams: function() {
    // harvests uid data from propj search results and appends it to the data search
    var theUids = thePage.ResultSet.pluck('uid');
    theUids.each(function (thisUid,index) {theUids[index] = 'project_number:'+thisUid});
    return (theUids.join(' OR '));
  },

  dummy: function () {}
}
*/
