    var gmarker = null;

function isNumeric(strString)
   //  check for valid numeric strings  
   {
   var strValidChars = "0123456789.-";
   var strChar;
   var blnResult = true;

   if (strString.length == 0) return false;

   //  test strString consists of valid characters listed above
   for (i = 0; i < strString.length && blnResult == true; i++)
      {
      strChar = strString.charAt(i);
      if (strValidChars.indexOf(strChar) == -1)
         {
         blnResult = false;
         }
      }
   return blnResult;
   }
   
   
    function addNode(form)
    {
        var req;
        var mac="";
        var ip = "5";

        //
        // check the IP field to see if user entered an IP or MAC address
        //
        var ip = "5";
      	var items = form.ip.value.split(".");
      	var items2 = form.ip.value.split(":");
      
        if (items.length == 1 && items2.length == 1)
        {
//        	mac = "00:12:CF";
        	
            //
            // user entered MAC (no colons) so convert it...
            //
            for (var i = 0; i < 12; i+=2)
            {
                var hex = form.ip.value.substr(i, 2);
                if (i >= 6)
									ip = ip + '.' + parseInt(hex,16);
								if (i > 0)
	                mac = mac + ':' + hex;
								else mac = hex;
            }           
            form.ip.value = ip;
 //     		alert (mac + ", " + ip);
        } else if (items2.length == 6) {
            //
            // User entered MAC with colons (Meraki Mini)
            //
            for (var i = 9; i < 17; i+=3)
            {
                var hex = form.ip.value.substr(i, 2);
                ip = ip + '.' + parseInt(hex,16);
            }
            mac = form.ip.value.toUpperCase();
            form.ip.value = ip;
        } else if (items2.length == 3) {
            //
            // User entered only 3-digit MAC (NetEquality Wallplug)
            //
            for (var i = 0; i < 8; i+=3)
            {
                var hex = form.ip.value.substr(i, 2);
                ip = ip + '.' + parseInt(hex,16);
            }
            mac = "00:12:CF:" + form.ip.value.toUpperCase();
            form.ip.value = ip;
        }
        
        if (mac.length == 0)
        {
        	// User entered IP, so we need to build a MAC
        	while (quad = items.pop()) {
            var base16 = parseInt(quad).toString(16).toUpperCase();
//            alert (base16.length + ", " + base16);
            if (base16.length == 1) base16 = "0" + base16;
            mac = ':' + base16 + mac;
          }
//          alert(mac + "  " + mac.slice(2));
        	mac = "00:12:CF" + mac.slice(3);
        }

        if (window.XMLHttpRequest)
            req = new XMLHttpRequest(); //newRequest();
        else if (window.ActiveXObject)
            req = new ActiveXObject("Microsoft.XMLHTTP");
        req.open("POST", "addnode.php", false);
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.setRequestHeader('Cache-Control', 'private');
        var encoded = "ip=" + form.ip.value + "&mac=" + mac + "&account=" + form.account.value + "&name=" + form.name.value +
            "&description=" + form.description.value + "&lat=" + form.lat.value + "&long=" +form.long.value;
//      alert (encoded);
        req.send(encoded);
        if (req.status != 200) {
            alert("There was a communications error: " + req.responseText);
        } else if (req.responseText.length > 5) { //.search("Error:") == 0){  <===== MIKE:  NOT SHOWING AN ERROR WHEN UPDATE HIT ON INVALID ACCOUNT
            alert(req.responseText);
        }else
        {
            // good, so add 
            var point = new GPoint(form.long.value, form.lat.value);

            var marker = createMarker2(map, form.account.value, point, form.name.value, form.ip.value, "", "", "", "<P>Refresh this page to see node data.<BR>Nodes check-in every 5 minutes.", "", "", "", true, "", "", "");
            map.addOverlay(marker);
            //GEvent.trigger(marker, "click");
        }
        map.closeInfoWindow();
    }

    function deleteNode(form)
    {
        var req;
        if (window.XMLHttpRequest)
            req = new XMLHttpRequest(); //newRequest();
        else if (window.ActiveXObject)
            req = new ActiveXObject("Microsoft.XMLHTTP");
        req.open("POST", "deletenode.php", false);
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.setRequestHeader('Cache-Control', 'private');
        var encoded = "ip=" + form.ip.value + "&account=" + form.account.value;
//      alert (encoded);
        req.send(encoded);
        map.closeInfoWindow();
        if (req.status != 200) {
            alert("There was a communications error: " + req.responseText);

        } else {
          if (req.responseText.length > 5)
              alert(req.responseText);
          else
            // Must remove the marker now too!
            map.removeOverlay(gmarker);  // global - last clicked marker
        }
        // Must remove the marker now too!
//        map.removeOverlay(gmarker);  // global - last clicked marker
    }

    function createIcon(metric, gateway, up, users, checkinPercent, hops, spare)
    {   
        var baseIcon = new GIcon();
		
		if (spare == "1")
			spareName = "spare-";
		else 
			spareName = "";
        
        if (hops > 0)
            metric = metric / hops;
        if (!isNumeric(users))
          users = 0;
        if (users > 9) users = 10;
		if (up > 1800)
			users = 0;  // for down nodes, always use small base size - don't show users.
              
        switch (users)
        {
	    case "-1":
		baseIcon.iconSize = new GSize(12, 12);
	        baseIcon.iconAnchor = new GPoint(1, 1);
	        baseIcon.infoWindowAnchor = new GPoint(1, 1);
		break;

            case "0":
                baseIcon.iconSize = new GSize(24, 24);
                break;
                        
            case "1":
            case "2":
            case "3":
                baseIcon.iconSize = new GSize(26, 26);
                break;
                                            
            case "4":
            case "5":
            case "6":
                baseIcon.iconSize = new GSize(28, 28);
                break;
                                            
            case "7":
            case "8":
            case "9":
                baseIcon.iconSize = new GSize(30, 30);
                break;

            case 10:
                baseIcon.iconSize = new GSize(34, 34);
                break;
                
            default:
                baseIcon.iconSize = new GSize(24, 24);
                break;                      
        }
        {   
            //
            // The order of these is important!
            //
            // DOWN is no data is last 25 minutes   
	    if (users == "-1")
	    {
		baseIcon.image = "images/tiny.png";
		baseIcon.transparent = "images/tiny.png";
	    }
            else if (up > 1800)
            {
            	if (gateway == 1 || gateway=="true")
            	{
                	baseIcon.image = "images/rr-" + spareName + "gw-down.png";
                	baseIcon.transparent = "images/rr-" + spareName + "gw-down.png";                
                }
                else
            	{
                	baseIcon.image = "images/rr-" + spareName + "gy-" + users + ".png";
                	baseIcon.transparent = "images/rr-" + spareName + "gy-" + users + ".png";                
                }
            }
            
            //
            // New:  Mike temp fix
            //
            else if (up < 2000 && (gateway == 1 || gateway=="true"))
            {
                baseIcon.image = "images/rr-" + spareName + "gw-" + users + ".png";
                baseIcon.transparent = "images/rr-" + spareName + "gw-" + users + ".png";                
            }
            else if (up < 2000)
            {
                baseIcon.image = "images/rr-" + spareName + "gr-" + users + ".png";
                baseIcon.transparent = "images/rr-" + spareName + "gr-" + users + ".png";                
            }
            // End Temp Fix
            
            // BAD (RED) 
            else if (checkinPercent < 80)
            {
                baseIcon.image = "images/rr-rd-" + users + ".png";
                baseIcon.transparent = "images/rr-rd-" + users + ".png";                
            }
            
            // CAUTION (YELLOW)
            else if (checkinPercent < 90)
            {
                baseIcon.image = "images/rr-yw-" + users + ".png";
                baseIcon.transparent = "images/rr-yw-" + users + ".png";                
            }
            
            // GOOD
            else if (gateway == 1 || gateway=="true")
            {
                baseIcon.image = "images/rr-" + spareName + "gw-" + users + ".png";
                baseIcon.transparent = "images/rr-" + spareName + "gw-" + users + ".png";                
            }
            else
            {
                baseIcon.image = "images/rr-" + spareName + "gr-" + users + ".png";
                baseIcon.transparent = "images/rr-" + spareName + "gr-" + users + ".png";                
            }
        }
        
        baseIcon.shadow=null;
        
//      baseIcon.iconSize = new GSize(34, 34);
//      baseIcon.shadowSize = new GSize(32, 26);
	if (users >= 0)
	{
	        baseIcon.iconAnchor = new GPoint(10, 10);
	        baseIcon.infoWindowAnchor = new GPoint(10, 1);
	}

        var icon = new GIcon(baseIcon);
        return icon;
    }

    function setRouteColor(metric)
    {
        var RGB;
        if (metric < 8000) 
            RGB = "#00FF00";  // green
        else if (metric < 12000)
            RGB = "#F39C04";  // yellow
        else
            RGB = "E01D49";  // red
//      else RGB = 0; // flag for no polyline 
    
        return RGB;
    }

    function drawRoutePolyline(lat, long, lat2, long2, metric)
    {
        var RGB="#1e2f10"; //setRouteColor(metric);
        var width;

        if (metric < 10)
            return 0;
                    
        if (metric < 17)
            width = 1;
        if (metric < 22)
          width = 3;
        else
          width = 7;

//      alert (metric + " " + width);
         
        var polyline = (new GPolyline([new GLatLng(lat, long), new GLatLng(lat2, long2)], RGB, width, 0.1));    
        
        return polyline;
    }
            
    var count=0;
    var j=0;
    var polyline = new Array();
    var markers = new Array();
    var ips = new Array();

    function createMarker(map, account, lat, long, icon)
    {
	point = new GPoint(long, lat);
	marker = new GMarker(point, {icon:icon, draggable:false, title:account});
	map.addOverlay(marker);
  
//        GEvent.addListener(marker, "click", function() 
//        {
//            url="overview.php?id=" + account + "&showall=0";
//	    window.open(url, account);
//        });
    }

    // Creates a marker whose info window displays the given number. 
    function createMarker2(map, account, point, name, ip, gateway, metric, up, infoTab1, infoTab2, gw_route, hops, draggable, users, checkinPercent, spare) 
    {       
//      var string = "pct_missing: " + pct_missing + ".  pct_missing_hour: " + pct_missing_hour + ". Hops: " . hops;
//      alert(string);
        var icon = createIcon(metric, gateway, up, users, checkinPercent, hops, spare);
        var marker = new GMarker(point, {icon:icon, draggable:draggable, title:name});

    markers[j] = marker;
        gmarker = marker;
    ips[j++] = ip;
    
//      var marker = new PdMarker(new GLatLng(point.y,point.x), icon);
//      marker.setOpacity(62);
//      marker.setTooltip(name);

        GEvent.addListener(marker, "dragstart", function() {
            map.closeInfoWindow();
        });
        
        var req;
        GEvent.addListener(marker, "dragend", function() {
        var pDrop = marker.getPoint();
        if (window.XMLHttpRequest)
            req = new XMLHttpRequest(); //newRequest();
        else if (window.ActiveXObject)
            req = new ActiveXObject("Microsoft.XMLHTTP");
        req.open("POST", "addnode.php", false);
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.setRequestHeader('Cache-Control', 'private');
        var encoded = "ip=" + ip + "&account=" + account + "&lat=" + pDrop.y + "&long=" + pDrop.x;
//      alert (encoded);
        req.send(encoded);
        if (req.status != 200) {
            alert("There was a communications error: " + req.responseText);
        } else {
//          alert(req.responseText);
        }
//      var url = "http://www.roofnet.net/add.php?movenode=1&ip=" + ip + "&account=" + account + "&lat=" + pDrop.y + "&long=" + pDrop.x;
//      window.location.replace( url );
        });
        
        // Show this marker's location in the info window when it is clicked    
        GEvent.addListener(marker, "click", function() 
        {
            gmarker = marker;
            if (infoTab2.length)
            {
                infoTabs = [
                    new GInfoWindowTab("General", infoTab1),
                    new GInfoWindowTab("Neighbors", infoTab2)
                ];
          marker.openInfoWindowTabsHtml(infoTabs);
            }
            else
                marker.openInfoWindowHtml(infoTab1);
            
      var items = gw_route.split("+");

      dynamicTableInit(document.getElementById('neighbors'));

      for (var i=0; i<count; i++)
      {
          map.removeOverlay(polyline[i]);
      }

      if (gateway != 1 && items.length >= 6)
            {
         // remove old polylines
        count = -1;
        
            for (var i=0; i<(items.length-1); i+=5)  
                {
                    if (i > 0)
                    {
                        var RGB=setRouteColor(items[i+1]);
                        polyline[count] = (new GPolyline([new GLatLng(mpoint.y, mpoint.x), new GLatLng(items[i+2], items[i+3])], RGB, 5, 1.0));
                        if (mpoint)
                        map.addOverlay(polyline[count]);  // on map, this fails and aborts the loop!  Works on add!
                    }
                    var mpoint = new GPoint(items[i+3], items[i+2]);
          count++;
                }
            }

        });
        
        GEvent.addListener(marker, "mouseout", function() 
        {
      var items = gw_route.split("+");
      
      for (var i=0; i<count; i++)
      {
//          map.removeOverlay(polyline[i]);
      }
        });

        GEvent.addListener(marker, "mouseover", function() 
        {

      var items = gw_route.split("+");
      
      for (var i=0; i<count; i++)
      {
          map.removeOverlay(polyline[i]);
      }

      if (gateway != 1 && items.length >= 6)
            {
         // remove old polylines
        count = -1;
        
            for (var i=0; i<(items.length-1); i+=5)  
                {
                    if (i > 0)
                    {
                        var RGB=setRouteColor(items[i+1]);
                        polyline[count] = (new GPolyline([new GLatLng(mpoint.y, mpoint.x), new GLatLng(items[i+2], items[i+3])], RGB, 5, 0.85));    
                        if (mpoint)
                        map.addOverlay(polyline[count]);  // on map, this fails and aborts the loop!  Works on add!
                    }
                    var mpoint = new GPoint(items[i+3], items[i+2]);
          count++;
                }
            }

        });

        return marker;
    }

  function myClick(ip)
  {
    for (var i=0; i<ips.length; i++) 
    {
        s = ips[i].replace(/\./g, "0");
//        alert ("s: " + s + ", ip: " + ip);
        if (s == ip)
        {
          GEvent.trigger(markers[i],"click");
//          alert ("sent trigger: " + ip);
          break;
        }
    }
  }

    function setMapSizePos() 
    {
      var hBody = document.body.clientHeight;
//        var hTop = document.getElementById("top").offsetHeight; 
//        var left = document.getElementById("top").offsetLeft;
                
        document.getElementById("map").style.height = hBody + "px";      
        document.getElementById("map").style.marginLeft = 0 + "px";
    }

  function showAddress(map, address, overlay, imgRatio) 
  {
    if (geocoder) 
    {
//      alert ("overlay: " + overlay);
      geocoder.getLatLng(address, function(point)
      {
          if (!point)
          {
//            alert(address + " not found");
               var prompt = address + " not found.  Please try entering city, state or city, country.  You will be able to scroll and zoom the map to pinpoint your exact location.";
            var location=window.prompt(prompt);
                showAddress(map, location, overlay, imgRatio);
          }
          else
          {
            map.setCenter(point, 19);  // >==== Mike:  Problem is here.  Map not defined?
//            alert("overlay: " + overlay);
            if (overlay) {
              var bounds = map.getBounds();

              var southWest = bounds.getSouthWest();
              var northEast = bounds.getNorthEast();
              var lngSpan = northEast.lng() - southWest.lng();
              var latSpan = northEast.lat() - southWest.lat();

              var myWidth = 0, myHeight = 0;
              if( typeof( window.innerWidth ) == 'number' ) {
                myWidth = window.innerWidth;
                myHeight = window.innerHeight;
              } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
                myWidth = document.documentElement.clientWidth;
                myHeight = document.documentElement.clientHeight;
              } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
                myWidth = document.body.clientWidth;
                myHeight = document.body.clientHeight;
              }

//              var imgRatio = ($width / $height);
              var brwRatio = myHeight / myWidth;
              var ovlWidth = lngSpan * imgRatio * brwRatio;

              var ovlStartLng = southWest.lng() + (lngSpan - ovlWidth) / 2;
              var ovlEndLng = ovlStartLng + ovlWidth;

              var boundaries = new GLatLngBounds(new GLatLng(southWest.lat(),ovlStartLng), new GLatLng(northEast.lat(),ovlEndLng));  // <==== southWest.lat() is to high!!!!

              var oldmap = new GGroundOverlay(overlay, boundaries);
              map.addOverlay(oldmap);
            }
          }
      }
      );
    }
  }



    function myCenterAndZoom(map, minY, maxY, minX, maxX, nodeloc) 
    {
        setMapSizePos();
        if ((minX + maxX + minY + maxY) != 0)
        {
            var rectBounds = new GLatLngBounds(new GLatLng(minY, minX), new GLatLng(maxY, maxX));
            map.setCenter(rectBounds.getCenter(), map.getBoundsZoomLevel(rectBounds));  //+1
        }
        else
        {
//          var address = prompt("You have no nodes defined yet.\nPlease enter an approximate address or zip code so we can get you close on the map", "97229")         
            showAddress(map, nodeloc);
        }
    }


    


