    /*
    Original author: Sudarshan Murthy. http://www.cs.pdx.edu/~smurthy/

    Application developed using Mash-o-matic. http://sparce.cs.pdx.edu/mash-o-matic
    
    This application is provided 'as is', use at your own risk. No warranty is expressed or implied. All responsibilities disclaimed.

    You may use/customize this application for non-commercial purpose only without permission. 
    You must inlcude this comment block in its entirety.
    */
    
    var map;
    var mapCenter;
    var markers = [];
    var mashupXMLDoc;
    var markerTypesXML;
    var markersXML;
    var numMarkers;
    var landmarksXML;
    var dataSourcesTable = "";
    var initialZoom;
    var synchronizeMarkersAndLandmarks;
    var oneTimeImage = "";
    var oneTimeXSLT = "";

    
    //show coordinates of any point
    function showCoordinates(geoPoint) {
       //V2
       //var latLngHtml = "<font size=\"2\">" + point.y + ", " + point.x + "</font>";
       var latLngHtml = "<font size=\"2\">" + geoPoint.lat() + ", " + geoPoint.lng() + "</font>";
       document.getElementById("message").innerHTML = latLngHtml;
    }

    //show coordinates of the current map center
    function showCenterCoordinates() {
         //V2
    	 //showCoordinates(map.getCenterLatLng());
    	 showCoordinates(map.getCenter());
    }

    //relocate map to given coordinates and zoom; preserve zoom if zoom is negative
    function changeMapCenter(center, zoom) {
      if (zoom > -1) {
        map.setCenter(center, zoom);
        initialZoom = zoom;
      } else
        map.setCenter(center);
      mapCenter = center;
    }
    
    //set map's center and zoom to initial configuration
    //OPT: returnToSavedPosition
    function resetMap() {
      changeMapCenter(mapCenter, initialZoom);
    }
	 

    //find the address, if not coordinates, of the marker visited
    //-used to provide directions between markers
    var currentVisitedMarker = "";
    var lastVisitedMarker = "";
    function SetCurrentVisitedMarker(index) {
       var markerVisited = "";

       var landmarks = markersXML[index].getElementsByTagName("Landmark");
       if (landmarks.length > 0) {
          var addresses = landmarks[0].getElementsByTagName("Address");
          if (addresses.length > 0) {
             markerVisited = GXml.value(addresses[0]);
          }
       }

       //get the coords if we cannot get the address
       if (markerVisited.length == 0)
          markerVisited = markersXML[index].getAttribute("lat") + "," + markersXML[index].getAttribute("lng");

       //ignore consecutive visits to a marker
       if (markerVisited != currentVisitedMarker) {
          lastVisitedMarker = currentVisitedMarker;
          currentVisitedMarker = markerVisited;  
       }
    }

    //get directions to/from new  coords to the marker last visited
    function GetDirectionsToLastCoords(toLastMarker) {
       if (lastVisitedMarker.length > 0 && lastVisitedMarker != currentVisitedMarker) {
          if (toLastMarker==1) {
            window.open("http://maps.google.com/maps?daddr=" + lastVisitedMarker + "&saddr=" + currentVisitedMarker, "_blank");
          } else {
            window.open("http://maps.google.com/maps?saddr=" + lastVisitedMarker + "&daddr=" + currentVisitedMarker, "_blank");
          }
       }
    }


    //set the last visited marker information in title: used to help user figure out what the 'last visited marker' is
    function SetLastMarkerVisitedTooltip(element, toLastMarker) {
       if (lastVisitedMarker.length == 0)
          element.title = "No marker visited yet";
       else if (lastVisitedMarker == currentVisitedMarker)
          element.title = "The last visited marker is the same as this marker";
       else if (toLastMarker==1) {
          element.title = "To: " + lastVisitedMarker;
       } else {
          element.title = "From: " + lastVisitedMarker;
       }
    }

    //V2
    //use AjaXslt to transform Xml to Html
    //AjaXslt uses the variables xsltdebug and logging__ to output debug messages: turn them OFF
    var xsltDoc;
    var xsltdebug = false;
    var logging__ = false
	function loadXsltDoc() {
      var request = GXmlHttp.create();
      request.open("GET", "markerInfo.xslt", true);
      request.onreadystatechange = function() {
        if ((request.readyState == 4) && (request.status == 200)) {
          xsltDoc = request.responseXML;
        }
      }
      request.send(null);
	}

    //display the info window for marker with given index
    //if an image is defined, show just the image instead of the usual info window
    function showMarkerInfo(index) {

      //remember the markers visited: used in providing directions between two markers
      SetCurrentVisitedMarker(index);

      if (oneTimeImage != "") {
         markers[index].openInfoWindowHtml("<img src=\"" + oneTimeImage + "\"/>");
         oneTimeImage = "";
      } else if (oneTimeXSLT != "") {
         //xslt must come from the same host as the application
         //V2
         //markers[index].openInfoWindowXslt(markersXML[index], oneTimeXSLT);
         
         var request = GXmlHttp.create();
         request.open("GET", oneTimeXSLT, true);
         request.onreadystatechange = function() {
           if ((request.readyState == 4) && (request.status == 200)) {
             markers[index].openInfoWindowHtml(xsltProcess(markersXML[index], request.responseXML));
           }
         }
         request.send(null);
         
         oneTimeXSLT = "";
      } else {
         //V2
         //markers[index].openInfoWindowXslt(markersXML[index], "markerInfo.xslt");
         //use AjaXslt to transform Xml to Html
         markers[index].openInfoWindowHtml(xsltProcess(markersXML[index], xsltDoc));
      }
    }

    function showUsage() {
      var baseURL = "";
      var delimPos=location.href.indexOf("?");
      if(delimPos > -1) {
        baseURL= location.href.substr(0, delimPos);
      } else {
        baseURL = location.href;
      }
      var usage = "Link using a URL of this form: " + baseURL + "?&lt;landmarkAttrName&gt;=&lt;value&gt;";
      usage = usage + "<p>&lt;landmarkAttrName&gt; is the name of a landmark's attribute.";
      
      //try listing available attributes for a landmark: use the first landmark to get attribute list
      try {
        if (markersXML.length > 0) {
          var landmarks =markersXML[0].getElementsByTagName("Landmark");
          if (landmarks.length>0) {
            usage = usage + "&nbsp;The following attributes are defined for a landmark:"
            var attrs =landmarks[0].attributes;
            for (var i=0; i < attrs.length; i++) {
              usage = usage + "<div>" + attrs[i].nodeName + "</div>";
            }
          }
        }
      } catch (e) {
         usage = usage + "&nbsp;See the mash-up data for possible attribute names.";
      }
      
      document.getElementById("message").innerHTML = "<font size=\"2\">" + usage + "</p></font>";
    }

    //show marker for a landmark for attr name and value combinatiion
    function showMarkersForLandmarkAttribute(attrName, attrValue) {
      for (var i = 0; i < numMarkers; i++) {
        var landmarks = markersXML[i].getElementsByTagName("Landmark");
        for (var j = 0; j < landmarks.length; j++) {
          if (landmarks[j].getAttribute(attrName)==attrValue) {
            showMarkerInfo(i);
            return;
          }
        }
      }
      oneTimeImage = "";
      oneTimeXSLT = "";
    }

    // process query in the URL
    function processParameters() {
      var keyName = "";
      var keyValue;
      
      var delimPos=document.location.href.indexOf("?");
      if(delimPos > -1) {

        //retrieve query
        var query = location.href.substr(delimPos+1);

        //find key
        delimPos = query.indexOf("=");
        if (delimPos>-1) {
     	keyName = query.substr(0, delimPos);
	     keyValue = query.substr(delimPos+1);
	     
	     //find one time only image location
	     //--image allows people linking to show an image instead of the info window
	     delimPos=keyValue.indexOf("?img=");
	     if(delimPos > -1) {
	       var extraKeyValue = keyValue;
	       keyValue = extraKeyValue.substr(0, delimPos);
	       delimPos = extraKeyValue.indexOf("=");
	       oneTimeImage = extraKeyValue.substr(delimPos+1);
	     }
        }

	   //assume that query is always over an attribute of a landmark
        showMarkersForLandmarkAttribute(keyName, keyValue);
      }
    }
    
    //add tooltip to marker
    //technique borrowed from: http://www.econym.demon.co.uk/googlemaps/tooltips.htm
    function addToolTip(marker, markerXML) {
          
       //build tooltip text as a list of landmark names
       var landmarks = markerXML.getElementsByTagName("Landmark");
       var title = new String("");
       for (var j = 0; j < landmarks.length; j++) {
         title = title.concat(landmarks[j].getAttribute("name") + ", ");
       }
       
       //if we have a title, assign it to the top element of the marker
       if (title != "") {
       
       //V2
       //TODO
//         var topElement;
//         if (marker.transparentIcon) {
//           topElement = marker.transparentIcon;
//         } else if (marker.imageMap) {
//           topElement = marker.imageMap;
//         } else {
//            topElement = marker.iconImage;
//         }

//         //set tooltip after removing the last delimiter
//         topElement.setAttribute( "title" , title.substr(0, title.lastIndexOf(",")));
           marker.title = title.substr(0, title.lastIndexOf(","));
       }
    }

    //overlay many markers, add tooltip to each marker immediately
    function addOverlays(markers, markersXML) { 
      for (var i = 0; i < markers.length; i++) {
        map.addOverlay(markers[i]);
        addToolTip(markers[i], markersXML[i]);
      }
    };    

    //create a marker object from corresponding XML element
    function createMarker(index) {
      //find the icon location
      var iconBase = markersXML[index].getAttribute("iconBase");
            
      if (iconBase == "" || iconBase == null) {
        var typeName = markersXML[index].getAttribute("type");
        iconBase = "";
        for (var i = 0; i < markerTypesXML.length; i++) {
          if (markerTypesXML[i].getAttribute("type") == typeName) {
            iconBase = markerTypesXML[i].getAttribute("iconBase");
            break;
          }
        }
      }

      //icons based on Sam Kuhn's web hues: http://people.vanderbilt.edu/~sam.kuhn/gmaps/index.html
      //further processed using ImageMagick 6

      //create an icon based on the default icon (the custom markers are the same size as the default icon)
      //use the default icon if we do not have a location for the icon
      var icon = new GIcon(G_DEFAULT_ICON);
      if (iconBase != "") {
         //icons file names are expected to be of the form 'markerx' or 'markerxy' or 'markerxyz' where x, xy, and xyz are numbers greater than zero.
         icon.image = iconBase + "/marker" + (index+1) + ".png";
      }

      //create a marker and program to show info. window when clicked
      
      //V2
      //var point = new GPoint(parseFloat(markersXML[index].getAttribute("lng")), parseFloat(markersXML[index].getAttribute("lat")));
      var point = new GLatLng(parseFloat(parseFloat(markersXML[index].getAttribute("lat"))), parseFloat(markersXML[index].getAttribute("lng")));

      var marker = new GMarker(point, icon);
      markers.push(marker);
      GEvent.addListener(marker, "click", function() {
        showMarkerInfo(index);
      });
    }

    //tab functionality adapted from devx.com article by Tom Duffy: "Create A Tabbed User Interface"
    //http://gethelp.devx.com/techtips/dhtml_pro/10min/10min0102/td010902-4.asp
    var currentMarkerTypeTabId = "";
    var currentLandmarkTypeTabId = "";
    function setTabStateActive(tabId) {
      var tab = document.getElementById(tabId);
      if (typeof(tab) != "undefined") {
	   tab.style.backgroundColor = "#ddddff";
	   tab.style.color = "red";
	 }
    }

    function setTabStateInactive(tabId) {
      var tab = document.getElementById(tabId);
      if (typeof(tab) != "undefined") {
	   tab.style.backgroundColor = "#ffffff";
	   tab.style.color = "navy";
	 }
    }
    
    function setTabState(tabId, currentTabId) {
      if(tabId==currentTabId){
		setTabStateActive(tabId);
	 } else {
		setTabStateInactive(tabId);
      }
    }

    function setMarkerTypeTabState(tabId) {
      setTabState(tabId, currentMarkerTypeTabId);
    }
    
    function setLandmarkTypeTabState(tabId) {
      setTabState(tabId, currentLandmarkTypeTabId);
    }

    function hover(tab) {
      tab.style.backgroundColor = "#ddddff";
    }
    
    //create a cell that looks like a tab
    function tabFragment(actionName, stateFunctionName, tabName, tabType, iconBase) {

      //construct an image element if we have an icon base
      //an icon folder is expected to have a file "legend.png"
      var legendIcon = "";
      if (iconBase != "") {
        legendIcon = "&nbsp;<img src=\"" + iconBase + "/legend.png" + "\"/>";
      }
      
      var tabId = tabType + tabName;
      var column =  "<td align=\"center\" valign=\"bottom\">" + 
                           "<div id=\"" + tabId + "\" class=\"tab\" width=\"100%\" onClick = \"" + actionName + "(\'" + tabName + 
                           "\');\" onMouseOver=\"hover(this);\" onMouseOut=\"" + stateFunctionName + "(\'" + tabId + "\');\">&nbsp;" + tabName +
                            legendIcon +  
                           "&nbsp;</div>" +
                          "</td>";
      return column;
    }

    // load a table that lists landmarks of requested type
    function listLandmarks(typeName) {
      var html = "" ;
      var pHtml;
      for (i=0; i < landmarksXML.length; i++) {
        if (landmarksXML[i].getAttribute("type")==typeName) {
           //Can get serialized XML using the 'xml' property in IE, but need to use XMLSerializer in Firefox
           //--Firefox returns 'undefined' when a non-existent property is accessed
           pHtml = landmarksXML[i].xml;
           if (typeof(pHtml) == "undefined") {
             pHtml = new XMLSerializer().serializeToString(landmarksXML[i]);
           }
           document.getElementById("landmarks").innerHTML = pHtml;
           
           //change tab state
           if (currentLandmarkTypeTabId != "") {
             setTabStateInactive(currentLandmarkTypeTabId);
           }
	      currentLandmarkTypeTabId = "L" + typeName;
        	 setLandmarkTypeTabState(currentLandmarkTypeTabId);
        }
      }
    }

    //show tabs for landmark types
    function displayLandmarkTypeTabs() {
      if (landmarksXML.length > 1) {
        var html = "";
        for (var i = 0; i < landmarksXML.length; i++) {
          html = html  + tabFragment("listLandmarks", "setLandmarkTypeTabState", landmarksXML[i].getAttribute("type"), "L", "");
        }
        
        //add a tab to show all types of landmarks at once
        document.getElementById("landmarkTypes").innerHTML = "<table><tr>" + html + "</tr></table>";
      }
    }

    // Download the html tables of landmarks
    function loadLandmarks() {
      var request = GXmlHttp.create();
      request.open("GET", "landmarks.xml", true);
      request.onreadystatechange = function() {
        if (request.readyState == 4) {
          var xmlDoc = request.responseXML;
          
          //retrieve tables that list landmarks, build tabs, and list all by default
          landmarksXML = xmlDoc.documentElement.getElementsByTagName("table");
          displayLandmarkTypeTabs();
          
          //show the last type of landmarks (we expect that to be "All")
          if (landmarksXML.length>0) {
            listLandmarks(landmarksXML[landmarksXML.length-1].getAttribute("type"));
          } 
        }
      }
      request.send(null);
    }

    //show markers which contain at least one landmark of the given type: if type is "All" show all markers
    function showMarkers(typeName) {
    
      //remove markers shown currently
      map.clearOverlays();

      //show all markers or only appropriate markers
      //add tooltips for markers *after* they are overlaid
      if (typeName=="All") {
        addOverlays(markers, markersXML);
      } else {
      
        //if marker type has a specific center, recenter the map
        for (var i = 0; i < markerTypesXML.length; i++) {
          if (markerTypesXML[i].getAttribute("type") == typeName) {
             if (markerTypesXML[i].getAttribute("lat") != null) {
                changeMapCenter(new GLatLng(parseFloat(markerTypesXML[i].getAttribute("lat")), parseFloat(markerTypesXML[i].getAttribute("lng"))), parseFloat(markerTypesXML[i].getAttribute("zoom")));
             }
           break;
          }
        }
              
        //gather the markers to show and overlay them at once
        var markersToDisplay = [];
        var markersToDisplayXML = [];
        var landmarks;
        for (var i=0; i < numMarkers; i++) { 
          landmarks = markersXML[i].getElementsByTagName("Landmark");
          for (var j = 0; j < landmarks.length; j++) {
            if (landmarks[j].getAttribute("type")==typeName) {
              markersToDisplay.push(markers[i]);
              markersToDisplayXML.push(markersXML[i]);
              //no need to look at other landmarks for this marker
              break;
            }
          }
        }
        addOverlays(markersToDisplay, markersToDisplayXML);
      }

      //change tab state
      if (currentMarkerTypeTabId != "") {
        setTabStateInactive(currentMarkerTypeTabId);
      }
      currentMarkerTypeTabId = "M" + typeName;
    	 setMarkerTypeTabState(currentMarkerTypeTabId);
    }

    //display tabs for marker types
    function displayMarkerTypeTabs() {
      if (markerTypesXML.length > 1) {
        var html = "";
        for (var i = 0; i < markerTypesXML.length; i++) {
          html = html  + tabFragment("showMarkers", "setMarkerTypeTabState", markerTypesXML[i].getAttribute("type"), "M", markerTypesXML[i].getAttribute("iconBase"));
        }
        
        //add a tab to show all types of markers at once
        document.getElementById("markerTypes").innerHTML = "<table><tr>" + 
        													                      html + tabFragment("showMarkers", "setMarkerTypeTabState", "All", "M", "") + 
        													                 "</tr></table>";
      }
    }


    // resize map
    function resizeMap() {
      //V2
      //map.onResize();
      map.checkResize();
    }

    //retrieve marker data
    function loadMarkers() {
    
      // retrieve markers and show them on the map
      var request = GXmlHttp.create();
      request.open("GET", "markers.xml", true);
      request.onreadystatechange = function() {
        if (request.readyState == 4) {
          mashupXMLDoc = request.responseXML;
          
          //retrieve configuration
          var configXML = mashupXMLDoc.documentElement.getElementsByTagName("Config");

          //set title
          var xmlElements = configXML[0].getElementsByTagName("Title");
	      document.title = GXml.value(xmlElements[0]);

          //create a point at the map's center
          xmlElements = configXML[0].getElementsByTagName("Center");
          //V2
	      //mapCenter = new GPoint(xmlElements[0].getAttribute("lng"), xmlElements[0].getAttribute("lat"));
	      mapCenter = new GLatLng(parseFloat(xmlElements[0].getAttribute("lat")), parseFloat(xmlElements[0].getAttribute("lng")));
	      

          //get initial zoom
          xmlElements = configXML[0].getElementsByTagName("Zoom");
	      initialZoom = parseFloat(xmlElements[0].getAttribute("level"));
	      
          //get other options
          xmlElements = configXML[0].getElementsByTagName("Options");

          //see if marker and landmarker tabs must be synchronized
	      synchronizeMarkersAndLandmarks = xmlElements[0].getAttribute("synchronizeMarkersAndLandmarks");
	      
          //center the map and resize it
          resizeMap();
          resetMap();

          //get types of markers and populate list
          markerTypesXML = configXML[0].getElementsByTagName("Marker");
          displayMarkerTypeTabs();
          
          //retrieve markers
          markersXML = mashupXMLDoc.documentElement.getElementsByTagName("Markers");
          markersXML = markersXML[0].getElementsByTagName("Marker");
          numMarkers = markersXML.length;
          for (var i = 0; i < numMarkers; i++) {
            createMarker(i);
          }

          //show all markers by default
          showMarkers("All");

          //change map size when window size changes
          //code borrowed from: http://www.leapbeyond.com/ric/scuba/appletdemo/FullScreenMap.htm
          if (window.attachEvent) { 
            window.attachEvent("onresize", resizeMap);
          } else {
            window.addEventListener("resize", resizeMap, false);
          }

          //process any query parameters received
          processParameters();
        }
      }
      request.send(null);
    }

    // show table of data sources
    function listDataSources() {
      if (dataSourcesTable == "") {
        document.getElementById("message").innerHTML = "List of data sources is unavailable"; 
      } else {
        document.getElementById("message").innerHTML = dataSourcesTable;
        /*
        //Can get serialized XML using the 'xml' property in IE, but need to use XMLSerializer in Firefox
        //--Firefox returns 'undefined' when a non-existent property is accessed
        var html = dataSourcesTable.xml;
        if (typeof(html) == "undefined") {
          html = new XMLSerializer().serializeToString(landmarksXML[i]);
        }
        document.getElementById("message").innerHTML = html;
        */
      }
    }

    // Download the html table of data sources
    function loadDataSources() {
      var request = GXmlHttp.create();
      request.open("GET", "dataSources.xml", true);
      request.onreadystatechange = function() {
        //load data sources when request is completed *sucessfully*
        //--the file 'dataSources.xml' might not exist
        if ((request.readyState == 4) && (request.status == 200)) {
          //retrieve the table that lists data sources
          dataSourcesTable = request.responseText;
        }
      }
      request.send(null);
    }
    
    //initialize application
    function onLoad() {
    
      //proceed only if host is a compatible browser
      if (GBrowserIsCompatible()) {

        //V2
        //map = new GMap(document.getElementById("map"));
        map = new GMap2(document.getElementById("map"));
      
        //add controls to zoom and pan
        map.addControl(new GSmallMapControl());

        //Parts of the 'load*' functions execute "asynchronously"

        //V2
        loadXsltDoc();

        loadMarkers();
        loadLandmarks();
        loadDataSources();

      }  else {
        document.getElementById("map").innerHTML = "This browser is not compatible with this application, or the application key is invalid";
      }
    }
