
   var resultsPerPage = 10;      // number of search results to display per page
   var results = [];             // a list of search results
   var markers = [];             // array of map markers on the Google map
   var showMap = true;           // flag indicating whether or not to display the google map
   var jumpToFirstResult = false;// flag that determines whether to show the first result of the search on the map
   var currentSearch = null;     // reference to the current search object
   var currentQuadKeys;          // the list of the most recently searched quad keys
   var currentResultId;          // the id of the currently selected result in the result set
   var currentPage = 1;          // indicates the current page of results to display
   var currentMarker;            // reference to the currently selected map marker
   var suppressSearching = false;// keeps track of whether to temporarily suppress auto-searching
   var suppressSearchTimeout;    // object that will cancel search suppression after a delay
   var infoWindowDelay = 2000;   // number of milliseconds to suppress auto-searching after opening an info window

   /**********************/
   /*Parent search object*/
   /**********************/
   function Search() {
      this.request = null;    // the AJAX request associated with this search
      this.aborted = false;   // a flag indicating whether this search has been cancelled
   }
   
   Search.prototype.OnComplete = function(request) {
      $("searchProgress").hide();

      results = decodeURIComponent(request.responseText).split(';');

      if (results != "" && results.length > 0) {
         // Primary results
         ShowResults(1);
         $("googleMap").show();
         map.checkResize();
         
         // Add Google map markers
         var initialLocation = null;

         for (var i = 0; i < results.length; i++) {
            var result = results[i].split(",");
            var point = new GLatLng(result[1], result[2]);
            map.addOverlay(CreateMarker(result));
            if (initialLocation == null) {
               initialLocation = point;
            }
         }
         
         if (jumpToFirstResult == true && initialLocation != null) {
            map.setCenter(initialLocation, 10);
         }
      } else {
         $("searchResults").update("");
         $("resultsStatus").update("No results found.");
      }
   }

   Search.prototype.Cancel = function() {
      this.aborted = true;
      if (request.transport != null) {
         request.transport.abort();
         request.transport = null;
      }
   }

   Search.prototype.ShowError = function(request) {
      $("searchProgress").hide();
      $("resultsStatus").update("Search could not be completed at this time. Please try again later.");
   }
   /**************************/
   /*End parent search object*/
   /**************************/

   /********************/
   /*Trip search object*/
   /********************/
   function TripSearch() {}
   TripSearch.prototype = new Search;
     
   TripSearch.prototype.DoSearch = function() {   
      if (this.aborted) {
         $("searchProgress").hide();
         $("resultsStatus").update("");
         return false;
      }
      
      // If we already performed this same search last time, don't do it again
      var quadKeys = GetCurrentQuadKeys();
      if (quadKeys == currentQuadKeys)
         return false;
         
      currentQuadKeys = quadKeys;
      
      ClearSearchResults();
         
      currentResultId = -1;
      var params = "ajaxMethod=MapSearch&quadKeys=" + quadKeys +
         //"&distanceMin=" + $F("minDist") + "&distanceMax=" + $F("maxDist") + 
         "&activities=";
         
      var activities = [];
      var activityNames = [];
      ParseInputNodes(activities, $("activityList"));
      ParseInputNodes(activityNames, $("activityList"), true);

      SetCookie("selectedActivities", activityNames, 9999, "/Search");
      params = params + activities;
           
      request = new Ajax.Request(
         "/Search/default.aspx", 
         {
            method: "post",
            parameters: params,
            onSuccess: this.OnComplete,
            onFailure: this.ShowError
         }
      );
      
      if (this.query != "") {
         urchinTracker("/Search/default.aspx?query=" + this.query);
      }
   }
   /************************/
   /*End trip search object*/
   /************************/
   
   function ShowResults(page) {
      if (!results || results.length == 0)
         return;

      var pages = Math.ceil(results.length / resultsPerPage);
      if (page < 1 || page > pages)
         return;
         
      currentPage = page;
      
      $("searchResults").update("");
      $("resultsPanel").show(); 
      $("resultsPanel").scrollTop = 0;
   
      var currentPageResults = [];
      var startResult = (page - 1) * resultsPerPage;
      var endResult;
      for (var i = startResult; currentPageResults.length < resultsPerPage && i < results.length; i++) {
         currentPageResults.push(results[i]);
         endResult = i;
      }
      
      $("resultsStatus").update("Showing " + (startResult + 1) + "-" + (endResult + 1) + " of " + results.length + " results.");
      
      // Pagination
      if (pages > 1) {
         var pageLinks = "";
         for (var i = 1; i <= pages; i++) {
            if (page == i) {
               pageLinks = pageLinks + "<b><a href='#'>" + i + "</a></b>&nbsp;";
            } else {
               pageLinks = pageLinks + "<a href='#' onClick='ShowResults(" + i + ");return false;'>" + i + "</a>&nbsp;";
            }
         }
         $("pageNumbers").update(pageLinks);
         if ($("pageNumbersPanel") != null) {
            $("pageNumbersPanel").show();
         }
      }
      
      // Add list results
      var resultsContainer = document.getElementById("searchResults");
      for (var i = 0; i < currentPageResults.length; i++) {
         resultsContainer.appendChild(GetResultHtml(currentPageResults[i].split(","), false));
      }
   }
   
   function GetResultHtml(result, fullDetails) {
      /* Result components:
      // [0] Id
      // [1] Latitude
      // [2] Longitude
      // [3] Name
      // [4] Activity Icon
      // [5] Owner Id
      // [6] Owner Name
      // [7] Group Id
      // [8] Group Name
      // [9] Created Date
      // [10] Activity Name
      // [11] Media Count
      // [12] Distance
      // [13] Highlight Photo Id
      // [14] Summary Text
      */
      
      var resultDiv = document.getElementById("resultTemplate").cloneNode(true);
      RemoveTextNodes(resultDiv);
      resultDiv.id = "result_" + result[0];
      resultDiv.style.display = "block";
      resultDiv.className = fullDetails ? "searchResultSimple" : "tripSearchResult";
      if (!fullDetails) {
         resultDiv.onclick = function (e){ShowMarkerInfo(e, result[0])};
      }
      resultDiv.style.cursor = fullDetails ? "" : "pointer";
     
      var resultLink = resultDiv.childNodes[0];
      resultLink.href = "/ViewTrip/" + result[0];
      resultLink.innerHTML = decodeURIComponent(result[3]);
      resultLink.className = "resultName";
      resultLink.style.fontSize = fullDetails ? "medium" : "";
      
      var ownerLink = resultDiv.childNodes[1].childNodes[1];
      if (result[7] != "") {
         ownerLink.href = "/Groups/GroupProfile.aspx?groupId=" + result[7];
         ownerLink.innerHTML = decodeURIComponent(result[8]);
      } else {
         ownerLink.href = "/Users/" + result[5];
         ownerLink.innerHTML = decodeURIComponent(result[6]);
      }
      
      if (result[13] != "") {
         var highlightImg = resultDiv.childNodes[4];
         highlightImg.style.display = "block";
         highlightImg.src = "/DrawMediaObjectData.aspx?mediaObjectId=" + result[13] + "&size=Size97x97";
      }
      
      var metaData = resultDiv.childNodes[3];
      metaData.innerHTML = result[10] + ": " + result[9];
      if (result[11] != "" && result[11] != "0") {
         metaData.innerHTML += "&nbsp;|&nbsp;" + result[11] + "&nbsp;media points"
      }
      metaData.innerHTML += "&nbsp;|&nbsp;" + result[12];

      if (result[14] != "") {
         var summary = resultDiv.childNodes[5];
         summary.innerHTML = decodeURIComponent(result[14]);
      }
      
      return resultDiv;
   }

   function LoadingIndicator() {}
   LoadingIndicator.prototype = new GControl();

   LoadingIndicator.prototype.initialize = function(map) {
     var container = $("searchProgress");
     map.getContainer().appendChild(container);
     return container;
   }

   LoadingIndicator.prototype.getDefaultPosition = function() {
     return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(map.getSize().width/2-50, map.getSize().height/2-20));
   }
   
   function ZoomForResultsMessage() {}
   ZoomForResultsMessage.prototype = new GControl();

   ZoomForResultsMessage.prototype.initialize = function(map) {
     var container = $("zoomMessage");
     map.getContainer().appendChild(container);
     return container;
   }

   ZoomForResultsMessage.prototype.getDefaultPosition = function() {
     return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(map.getSize().width/2-88, map.getSize().height/2-120));
   }
   
   function SuppressSearching() {
      suppressSearching = true;
      if (suppressSearchTimeout != null) {
         clearTimeout(suppressSearchTimeout);
      }
      suppressSearchTimeout = setTimeout("suppressSearching = false", infoWindowDelay);
   }

   function HighlightResult(id) {
      var currentResult = $("result_" + currentResultId);
      if (currentResult != null) {
         currentResult.className = "tripSearchResult";
         currentResult.style.display = "inline-block";
      }
      currentResultId = id;
      var newResult = $("result_" + currentResultId);
      if (newResult != null) {
         newResult.className = "selectedTripSearchResult";
         newResult.style.display = "inline-block";
      }
   }
   
   function ShowMarkerInfo(e, id) {
      // If a link was clicked instead of just the div area, don't show the info window
      e = e || window.event;
      if (e.target && e.target.tagName == "A") {
         return false;
      } else if (e.srcElement && e.srcElement.tagName == "A") {
         return false;
      }
   
      SuppressSearching();
      map.closeInfoWindow();
      
      for (var i = 0; i < markers.length; i++) {
         var marker = markers[i];
         if (marker.value == id) {
            currentMarker = marker;
            GEvent.trigger(marker, "click");
         }
      }
      HighlightResult(id);
   }

   function CreateMarker(result) {
      /* Result components:
      // [0] Id
      // [1] Latitude
      // [2] Longitude
      // [3] Name
      // [4] Activity Icon
      // [5] Owner Id
      // [6] Owner Name
      // [7] Group Id
      // [8] Group Name
      // [9] Created Date
      // [10] Activity Name
      // [11] Media Count
      // [12] Distance
      // [13] Highlight Photo Id
      // [14] Summary Text
      */
      
      var id = result[0];
      var lat = result[1];
      var lng = result[2];
      var name = decodeURIComponent(result[3]);
      var icon = result[4];
      var activityName = result[10];
   
      var marker;
      var markerIcon = new GIcon(G_DEFAULT_ICON);
      var point = new GLatLng(lat, lng);
      var markerOptions = new Object();
      markerOptions.title = name + " (" + activityName + ")";
      
      if (icon && icon != "") {
         markerIcon.image = "/images/map_icons/iconBackground.png";
         markerIcon.shadow = "/images/map_icons/iconBackground_s.png";
         markerIcon.iconSize = new GSize(32, 32);
         markerIcon.iconAnchor = new GPoint(11, 32);
         markerIcon.infoWindowAnchor = new GPoint(12, 10);
         markerOptions.labelText = "<img src='" + icon + "' title='" + name + "' />";
         markerOptions.labelOffset = new GSize(-6, -28);
         markerOptions.icon = markerIcon;
         marker = new LabeledMarker(point, markerOptions);
      } else {
         markerOptions.icon = markerIcon;
         marker = new GMarker(point, markerOptions);
      }
      
      marker.value = id;
      marker.lat = lat;
      marker.lng = lng;
      marker.html = "";

      GEvent.addListener(marker, "click", function() {
         SuppressSearching();
         
         map.closeInfoWindow();
         if (marker.html == "") {
            marker.html = GetResultHtml(result, true);
         }
         marker.openInfoWindowHtml(marker.html, {maxWidth:300});
      });

      GEvent.addListener(marker, "infowindowclose", function() {
         currentResult = $("result_" + id);
         if (currentResult != null) {
            currentResult.className = "tripSearchResult";
            currentResult.style.display = "inline-block";
         }
      });
      markers.push(marker);
      return marker;
   } 

   function ClearSearchResults() {
      $("searchProgress").show();
      $("resultsStatus").update("<br/>");
      if ($("pageNumbersPanel") != null) {
         $("pageNumbersPanel").hide();
      }
      $("pageNumbers").update("");
      $("searchResults").update("");
      map.closeInfoWindow();
      map.clearOverlays();
      markers = [];
   }
   
   function ClearCurrentQuadKeys() {
      currentQuadKeys = "";
   }

   function ToggleMap() {
      if (showMap == true) {
         $("googleMap").hide();
         $("mapToggle").update("Show Map");
         showMap = false;
         map.checkResize();
         SetCookie("showMap", "0", 9999, "/");
      } else {
         $("googleMap").show();
         $("mapToggle").update("Hide Map");
         showMap = true;
         map.checkResize();
         SetCookie("showMap", "1", 9999, "/");
      }
   }
   
   function GetCurrentQuadKeys()
   {
      var bounds = map.getBounds();
      var projection = map.getCurrentMapType().getProjection();
      var tileCount = 0;
      var tileQuadKeys = [];
      
      // Prevent wrapping around the International Date Line. See Trimble.Outdoors.MappingServices.AreaBoundingBox.PreventInternationalDateLineWrapping,
      // with the exception of not extending or pushing back one side of the bounds.
      if (bounds.getSouthWest().lng() > bounds.getNorthEast().lng())
      {
         var degreesEast = 180.0 - bounds.getSouthWest().lng();
         var degreesWest = 180.0 - Math.abs(bounds.getNorthEast().lng());
         if (degreesEast > degreesWest)
         {
            bounds = new GLatLngBounds(new GLatLng(bounds.getSouthWest().lat(), bounds.getSouthWest().lng()/* - degreesWest*/), new GLatLng(bounds.getNorthEast().lat(), 179.0)); // Need to force Google to give us a pixel value well within the last tile
         }
         else
         {
            bounds = new GLatLngBounds(new GLatLng(bounds.getSouthWest().lat(), -179.9), new GLatLng(bounds.getNorthEast().lat(), bounds.getNorthEast().lng()/* + degreesEast*/));
         }
      }
      
      // Again, need to force Google to give us a pixel value well within the last tile.
      bounds = new GLatLngBounds(new GLatLng(Math.max(bounds.getSouthWest().lat(), -85.0), Math.max(bounds.getSouthWest().lng(), -179.9)), new GLatLng(Math.min(bounds.getNorthEast().lat(), 85.0), Math.min(bounds.getNorthEast().lng(), 179.0)));
      
      var nePixel = projection.fromLatLngToPixel(bounds.getNorthEast(), map.getZoom());
      var swPixel = projection.fromLatLngToPixel(bounds.getSouthWest(), map.getZoom());
      
      var neTileX = Math.floor(nePixel.x / 256);
      var neTileY = Math.floor(nePixel.y / 256);
      var swTileX = Math.floor(swPixel.x / 256);
      var swTileY = Math.floor(swPixel.y / 256);
      var tilesX  = (neTileX - swTileX) + 1;
      var tilesY  = (swTileY - neTileY) + 1;
      
      tileCount += (tilesX * tilesY);
      
      for (var x = swTileX; x <= neTileX; x++)
      {
         for (var y = neTileY; y <= swTileY; y++)
         {
            tileQuadKeys.push(MapTileXYToQuadKey(x, y, map.getZoom()));
         }
      }
      
      return tileQuadKeys.join(',');
   }
   
   function MapTileXYToQuadKey(x, y, z)
   {
      var quadKey = "";
      for (var i = z; i > 0; i--)
      {
         var digit = '0';
         var mask  = 1 << (i - 1);

         if ((x & mask) != 0)
         {
            digit++;
         }

         if ((y & mask) != 0)
         {
            digit++;
            digit++;
         }

         quadKey += digit;
      }
      
      return quadKey;
   }

   function SetCookie(name, value, expdays, path, domain)
   {
      var expdate = new Date();
      expdate.setDate(expdate.getDate() + expdays);
      var cookieString = name + "=" + encodeURIComponent(value) +
         ((expdays == null) ? "" : ";expires=" + expdate.toGMTString()) +
         ((path    == null) ? "" : ";path="    + path                 ) +
         ((domain  == null) ? "" : ";domain="  + domain               );
         
      document.cookie = cookieString;
   }

   function GetCookie(name)
   {
      if (document.cookie.length > 0)
      {
         var start = document.cookie.indexOf(name + "=");
         if (start != -1)
         {
            start = start + name.length + 1;
            var end = document.cookie.indexOf(";", start);
            
            if (end == -1) end = document.cookie.length;
            
            return decodeURIComponent(document.cookie.substring(start, end));
         }
      }
      return null;
   }

   function ParseInputNodes(inputNodeList, node, useName) {
      for (var i = 0; i < node.childNodes.length; i++) {
         var childNode = node.childNodes[i];
         if (childNode.tagName == "INPUT" && childNode.checked) {
            if (useName) {
               inputNodeList.push(childNode.name);
            } else {
               inputNodeList.push(childNode.value);
            }
         }
         if (childNode.childNodes.length > 0) {
            ParseInputNodes(inputNodeList, childNode, useName);
         }
      }
   }
   
   function RemoveTextNodes(node) {
      for (var i = 0; i < node.childNodes.length; i++) {
         var childNode = node.childNodes[i];
         if (childNode.nodeName == "#text") {
            node.removeChild(node.childNodes[i]);
         } else if (childNode.childNodes.length > 0) {
            //RemoveTextNodes(childNode);
         }
      }
   }
