var FailPngIE = false;

    var pageTracker = 
    {
       _trackEvent: function() {}
    };

    var UpImgs =
          [ { Ht:300, Hi:2, Lo:162, Url:"images/CurveUp_0.png" },
            { Ht:300, Hi:2, Lo:82,  Url:"images/CurveUp_1.png" },
            { Ht:300, Hi:2, Lo:42,  Url:"images/CurveUp_2.png" },
            { Ht:300, Hi:2, Lo:22,  Url:"images/CurveUp_3.png" },
            { Ht:300, Hi:2, Lo:12,  Url:"images/CurveUp_4.png" },
            { Ht:300, Hi:2, Lo:7,   Url:"images/CurveUp_5.png" }
          ];
    var DnImgs = 
          [ { Ht:300, Hi:2, Lo:162, Url:"images/CurveDn_0.png" },
            { Ht:300, Hi:2, Lo:82,  Url:"images/CurveDn_1.png" },
            { Ht:300, Hi:2, Lo:42,  Url:"images/CurveDn_2.png" },
            { Ht:300, Hi:2, Lo:22,  Url:"images/CurveDn_3.png" },
            { Ht:300, Hi:2, Lo:12,  Url:"images/CurveDn_4.png" },
            { Ht:300, Hi:2, Lo:7,   Url:"images/CurveDn_5.png" }
          ];
          
    var Scales = [0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0];
    
    var GraphicHeight = 200;  // Matches css CurveOuter height
    var GraphicWidth  = 360;  // Matches css CurveOuter clip rect.
      
    function MinToString (minuteOfDay)
    {
        if (minuteOfDay < 0)
        {
           return "-";
        }
        
        var mins = (minuteOfDay + 1440) % 1440;
        var min  = (mins % 60).toFixed(0);
        
        if (UserOptions.Time24)
        { 
            var out = Math.floor((mins / 60)).toFixed(0);
            if (out.length == 1) out = "0" + out;
        }
        else
        {
            var out = Math.floor(((mins / 60) % 12)).toFixed(0); 
            if (out == "0") out = "12";
            out += ":";
        }
        if (min.length == 1)
            out += "0" + min;
        else
            out += min;
            
        if (!UserOptions.Time24)
        {
            if (mins >= 720)
                out += "pm";
            else
                out += "am";
        }
        return out;
    }  
    
    //====================================================================
    
    function GraphGenerator (tabArray, units, localMinute, background, timeInfo)
    {    
        // Find the first tide for today (tide place local time), and also min and max heights.
        var Ymax  = -99;
        var Ymin  = 99;
        var first = -1;
        
        // Find the first turn of the day and establish min and max heights.
        for (var tx in tabArray)
        {
            var min = tabArray[tx].Min;
            
            if (first < 0 && min >= 0) first = tx;
            
            if (min > -180 && min < 1620)     // Exclude turn points that are out of view.
            {
                var ht = tabArray[tx].Hgt;
                Ymax = Math.max (Ymax, ht);
                Ymin = Math.min (Ymin, ht);
            }
        }
                                                                                                                            
        if (first < 0) 
        {
            return "<br><br><center>Sorry, there is insufficient tide data to display a graph</centre>";
        }
        
        var YOrigin;
        
        if (Ymin >= 0.0)
        {
            YOrigin = 0;
        }
        else
        {
            YOrigin = (Math.floor (Ymin * 10.0 - 0.1) / 10.0 - 0.1);    // Reduce to 0.1's
        }
        
        var YRange   = Math.ceil (Ymax) - YOrigin;
        var sx       = 0;
        var ScaleDiv;
        
        while (true)
        {
            ScaleDiv = Scales[sx++];
            if (Math.floor (YRange / ScaleDiv + 0.01) < 8) break;
        }
        
        var img;
        var imgStyle;
        var divStyle;
        var imgWidth;
        var imgHeight;
        var t1initialY;
        var html = "<div id='CurveOuter' style='background:transparent;'>";
        
        html += "<div style='position:absolute; z-index:-100; left:0; top:0'>" + background + "</div>";

        for (var tx = first - 1; (tx+1) < tabArray.length; tx++)
        {
            var t1 = tabArray[tx];
            var t2 = tabArray[tx + 1];
            
            if (typeof(t1) == 'undefined' || t1.Min > 1440) break;
            
            if (Math.abs(t1.Hgt - t2.Hgt) < 0.08)     // Prevent excessive image scaling
            {
               if (t2.Hgt < t1.Hgt)
                  t2.Hgt = t1.Hgt - 0.08;
               else
                  t2.Hgt = t1.Hgt + 0.08;
            }
            
            imgStyle = "";
            divStyle = "";
            
            if (tx < first)
            {
                var mLeft = (t1.Min / 4).toFixed(0);
                imgStyle += 'margin-left:' + mLeft + 'px;';
            }
            else if (t2.Min > 2100)         // Avoid overfilling the outer div...
            {
                // This is a hack: img won't fit across div so we modify t2 time and height to half of the usual.
                t2.Min = (t2.Min + t1.Min) / 2;
                t2.Hgt = (t2.Hgt + t1.Hgt) / 2;
            }
            
            imgWidth = (t2.Min - t1.Min) / 4;
            imgStyle += "width:" + imgWidth.toFixed(0) + "px;";
            
            var curveYt1 = GraphicHeight - ((t1.Hgt - YOrigin) * GraphicHeight / YRange);  // Reqd t1 pixel pos on curve.
            var curveYt2 = GraphicHeight - ((t2.Hgt - YOrigin) * GraphicHeight / YRange);
            var curveRange = Math.abs(curveYt2 - curveYt1);              // Reqd low to high pixels.
            
            for (ix = 0; ix < UpImgs.length; ix++)
            {
                if (t2.Hgt < t1.Hgt) 
                    img = DnImgs[ix];
                else
                    img = UpImgs[ix];
            
                imgScale  = curveRange / (img.Lo - img.Hi);
                imgHeight = imgScale * img.Ht;
                
                imgLowY = img.Lo * imgScale;
                
                if (t1.Hgt < t2.Hgt)
                    margTop = curveYt1 - imgLowY;
                else
                    margTop = curveYt2 - imgLowY;
                
                if (margTop + imgHeight >= GraphicHeight || ix == UpImgs.length-1)
                    break;
            }
            
            //alert("cyt1:"+curveYt1+", cyt2:"+curveYt2+", ily:"+imgLowY+", mt:"+margTop);
            
            divStyle += "margin-top:" + margTop.toFixed(0) + "px;";
            imgStyle += "height:" + imgHeight.toFixed(0) + "px;";
            
            if (FailPngIE) 
            {
                html += '<div id="CurveSection" style="' + divStyle + imgStyle +
                           'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' +
                           img.Url + '\',sizingMethod=\'scale\');"></div>';
            }
            else
            {
                var title = (t2.Hgt > t1.Hgt) ? "High: " : "Low: ";
                title += t2.Hgt.toFixed(2) + units + " at " + MinToString(t2.Min);
                
                html += '<div id="CurveSection" style="'+divStyle+'"><img style="'+imgStyle+'" src="'+img.Url+
                        '" title="' + title + '"/></div>';
            }
        }
        
        html += "</div>";  // End of CurveOuter
        
        var dp     = (ScaleDiv < 1.0) ? 1 : 0;
        var Yvalue = YOrigin;
        var gstr   = "";
        
        while (Yvalue <= YOrigin + YRange)
        {
            var ypix = GraphicHeight - ((Yvalue - YOrigin) / YRange * GraphicHeight) + 1;
            
            var dp1 = (Yvalue < 0 && Yvalue > -1) ? 1 : dp;
            
            gstr += "<div class='HGrid' style='top:" + ypix.toFixed(0) + "px'></div>";
            
            if (Yvalue > -0.01 || Yvalue < (-YRange / 12))    // Stops small -ve's overlaying the zero.
            {
                gstr += "<div class='YScaleNum' style='top:" + (ypix - 8).toFixed(0) + "px;'>" + Yvalue.toFixed(dp1) + "</div>";
            }
            
            if (Yvalue < 0 && Yvalue > (ScaleDiv * -1.4))
            {
                Yvalue = 0;
            }
            else if (YOrigin < 0 && Yvalue == YOrigin)
            {
                Yvalue = -Math.floor((-Yvalue - ScaleDiv / 2)  / ScaleDiv) * ScaleDiv;
            }
            else
            {
                Yvalue += ScaleDiv;
            }
        }
        gstr += "<div class='YScaleNum' style='top:-24px;'>" + units + "</div>";
        
        var timeLabels;
        
        if (UserOptions.Time24)
            timeLabels = ["0000", "0300", "0600", "0900", "1200", "1500", "1800", "2100", "2400"];
        else
            timeLabels = ["midnt", "3am", "6am", "9am", "noon", "3pm", "6pm", "9pm", "midnt"];
           
        var pixPerHour = 361.0 / 24.0;
        var srScale = (timeInfo.Sunset - timeInfo.Sunrise) / 720.0;
        var srWidth = (srScale * 361.0).toFixed(0);
        var srLeft  = ((pixPerHour * 6.0 * (1 - srScale)) +
                      ((timeInfo.Sunrise - 360.0) * (361.0 / 1440.0))).toFixed(0);
        
        if (!FailPngIE) 
        {
            gstr += "<div class='TimeBack'></div><div class='TimeScale'><img style='margin-left:" + srLeft + "px;' src='/images/SunStripe2.png' " + 
                    "width='" + srWidth + "' height='121'></div>";
        }
                
        for (var t = 0; t <= 8; t++)
        {
            var xpos = (t * GraphicWidth / 8) + 2;
            gstr += "<div class='VGrid' style='left:" + xpos.toFixed(0) + "px'></div>";
            gstr += "<div class='Times' style='left:" + (xpos-20).toFixed(0) + "px'>" + timeLabels[t] + "</div>";
        }
        
        if (!FailPngIE && DayOffset == 0 && localMinute >= -60 && localMinute <= 1500)
        {
            var minX = (localMinute * GraphicWidth / 1440 - 10).toFixed(0);      // -10 is half the image width.
            gstr += "<div id='TimeArrow' style='left:" + minX + "px;'><img src='images/TimeArrow.png'/></div>"; 
        }
        
        return '<div id="GraphOuter">' + html + gstr + "</div>";
    }
    
    function CompareGridz (a, b)
    {
        return a.ypix - b.ypix;
    }
        
    function InformationTab (place, marker, bgImage, timeInfo)
    {
        // Expand pic to fill the panel.
        var bg   = bgImage.replace('360', '410').replace('200','226').replace('/>', " class='InfoTabBody' />") +
                   '<img src="/images/InfoTabBack.png" width="410" height="226" class="InfoTabBack"/>';
        
        var sunrs = "<table class='RSTable' cellspacing='0' cellpadding='0'><tr><td colspan='2'>Sun</td></tr>" +
                    "<tr><td class='RSTime'>Rise</td><td class='RSTime'>" + MinToString(timeInfo.Sunrise) + "</td></tr>" +
                    "<tr><td class='RSTime'>Set</td><td class='RSTime'>" + MinToString(timeInfo.Sunset) + "</td></tr>" +
                    "</table>";
        
        var moon = "<a href='http://www.pa.msu.edu/people/frenchj/moon' target='_blank' title='Moon phase image. Click for real photos'>" +
                   "<img src='" + timeInfo.Moon.Thumb + "' /></a>";
                   
        var moolume = '<span title="Percentage of moon\'s disk currently illuminated.">' + timeInfo.Moon.Name + '</span>';
        
        var moonrs = "<table class='RSTable' cellspacing='0' cellpadding='0'><tr><td colspan='2'>" + moolume + "</td></tr>";
        
        for (var mrs in timeInfo.Moon) // Loop thru 1, 2 or 3 moon rise/sets
        {
           if (mrs >= 0 && mrs <= 3 && (timeInfo.Moon[mrs].RS == "Rise" || timeInfo.Moon[mrs].RS == "Set"))
           {
              moonrs += "<tr><td class='RSTime'>" + timeInfo.Moon[mrs].RS + "</td><td class='RSTime'>" + MinToString(timeInfo.Moon[mrs].Min) + "</td></tr>";
           }
        }
        moonrs += "</table>";

        var moonNp = "<a href='http://www.pa.msu.edu/people/frenchj/moon' target='_blank' title='Next moon phase. Click for real photos'>" +
                     "<img src='" + timeInfo.Moon.NextImage + "' /></a>";
                   
        var moonNpd = timeInfo.Moon.NextPh + "<br>" + timeInfo.Moon.NextTime;
        
        var bestFishAm = "";
        var bestFishPm = "";

        for (var mrs in timeInfo.Moon) // Loop thru Zenith / Nadir
        {
           if (timeInfo.Moon[mrs].RS == "Nadir" || timeInfo.Moon[mrs].RS == "Zenith")
           {
              var bf = timeInfo.Moon[mrs].Min;
              if (bf < 720)
                 bestFishAm = MinToString(bf);
              else
                 bestFishPm = MinToString(bf);
           }
        }

        var bestFish = "<hr>Best fishing<br>" + bestFishAm + "<br>" + bestFishPm + 
                        "<br><small><a class='DarkLink' href='/faq.php?o=BestFish' target='_blank'>(Explanation here)</a><small>";
        
        table = '<center><table><tr>' +
                '<td class="SunMoon">&nbsp;</td>' +
                '<td class="SunMoon">' + moon + '</td>' +
                '<td class="SunMoon">' + moonNp + '</td></tr><tr>' +
                '<td class="Astrono">' + sunrs + '</td>' +
                '<td class="Astrono">' + moonrs + '</td>' +
                '<td class="Astrono">' + moonNpd + '</td></tr><tr><td></td>' +
                '<td class="Astrono">' + bestFish + '</td></tr></table></center></p>';

        return bg + table;
    }
     
    function LinkLink (href)
    {
       window.open(href, 'LinkHere', 'width=640, height=340'); 
    }   
    

    
    //====================================================================
    
    function Debug (text)
    {
        var node = document.getElementById("debug");
        node.innerHTML = node.innerHTML + text + "<br/>";
    }

        
    //-------------------

    var map = null;

    var AllPlaces = [];   // All the places we have loaded.
    var NewPlaces = [];
    var PlaceLoad = new PlacesLoader();

    var TimeOfLastPlacesList = new Date();
    var DayOffset = 0;

    var countryIcon;
    var stateIcon;
    var webcamIcon;
    var placeIcons    = [];
    var PVAnchor      = "";
    var WCVAnchor     = function() {return ""};
    var RecentPlaces  = [];

    var FlyingTo      = null;
    var TideInfoWinMarker  = null;
    var WebcamInfoMarker   = null;

    var InfoTabFullPage    = 3;
    var InfoTabFullNewPage = 4;
    var InfoTabPlanner     = 5;
    var PopOverlay         = null;
    
    var UserOptions  = {InitialInfoTab:1};    // Show pretty graph initially.
    var ShowTabNext  = 1;
    var AngFormat    = "DMS";

    var RefreshRate     = 1.0;
    var TabWarningPage  = "";
    

    function load()
    {
        if (GBrowserIsCompatible())
        {
            map = new GMap2(document.getElementById("TheMap"));
            map.setCenter(new GLatLng(14.0, -140.0), 2);
            map.addControl(new GLargeMapControl());
            map.addControl(new GMapTypeControl());
            //map.addControl(new TimeShiftControl());
            map.enableScrollWheelZoom();
            //GEvent.addListener(map, "load", function() { map_OnLoad() } );
            GEvent.addListener(map, "dblclick", function(overlay, point) { map_DoubleClick(map, point) } );
            GEvent.addListener(map, "click", function(overlay, ll, oll) { map_Click(overlay) } );
            GEvent.addListener(map, "singlerightclick", function(point, src, overlay) { map_RightClick(point,src,overlay) } );
            GEvent.addListener(map, "mousemove", function(latlong) { map_MouseMove(latlong) } );
            GEvent.addListener(map, "mouseout", function() { map_MouseOut() } );
            GEvent.addListener(map, "dragstart", function() { map_DragStart() } );
            GEvent.addListener(map, "moveend", function() { map_MoveEnd() } );
            GEvent.addListener(map, "zoomstart", function() { map_ZoomStart() } );
            GEvent.addListener(map, "zoomend", function(oldLevel,newLevel) { map_ZoomEnd(oldLevel,newLevel) } );
            GEvent.addListener(map, "infowindowclose", function() { map_InfoWinClosed() } );

            window.setTimeout(map_OnLoad, 50);  // This because the GMap2.load event fails to fire.

            countryIcon = new GIcon(G_DEFAULT_ICON);
            countryIcon.image = "http://tidespy.com/images/country-marker.png";

            stateIcon = new GIcon(G_DEFAULT_ICON);
            stateIcon.image = "http://tidespy.com/images/state-marker.png";

            webcamIcon = new GIcon(G_DEFAULT_ICON);
            webcamIcon.image = "http://tidespy.com/images/webcam-marker.png";

            for (i = 0; i <= 4; i++)
            {
                mi = "" + i + "u";
                placeIcon = new GIcon(G_DEFAULT_ICON); 
                placeIcon.image = "http://tidespy.com/images/tmark-" + mi + ".png";
                placeIcons[mi] = placeIcon;
            }  
          
            for (i = 0; i <= 4; i++)
            {
                mi = "" + i + "d";
                placeIcon = new GIcon(G_DEFAULT_ICON); 
                placeIcon.image = "http://tidespy.com/images/tmark-" + mi + ".png";
                placeIcons[mi] = placeIcon;
            }  
            placeIcon = new GIcon(G_DEFAULT_ICON); 
            placeIcon.image = "http://tidespy.com/images/tmark-unk.png";
            placeIcons["unk"] = placeIcon;
            
            window.onfocus = FocusIn;
            window.onblur = FocusOut;

            InitialiseUserOptions();
            InitialiseMru();
            InitialRolls();

            setInterval ("RefreshTimer()", 10000);
        }
    }
    
    /*
    *   This timer runs every 10s to possibly refresh the markers, since tide figures may have changed.
    *   The refresh rate lengthens over time but shortens up onwindow focus or activity.
    */
    function RefreshTimer()
    {
        if (RefreshRate <= 15) RefreshRate *= 1.02;
        
        var now = new Date();
        
        if ((now.getTime() - TimeOfLastPlacesList.getTime() + 1000) > (RefreshRate * 60000))
        {                                               
            PlaceLoad.BeginRequest (map.getBounds(), map.getZoom());   
        }
    }
    
    function StartTabWarning(page)
    {
        TabWarningPage = page;
        setTimeout ("TabWarnStart()", 800);
    }
    
    function TabWarnStart()
    {
        setTimeout ("TabWarnStop()", 4000);
        document.getElementById("TabWarningPage").innerHTML = TabWarningPage;
        document.getElementById("TabWarning").style.display = "block";
    }
    
    function TabWarnStop()
    {
        document.getElementById("TabWarning").style.display = "none";
    }
    
    function FocusIn ()
    {
       RefreshRate = 1.0;
       PlaceLoad.BeginRequest (map.getBounds(), map.getZoom()); 
    }
    
    function FocusOut()
    {
       RefreshRate = 10.0;
    }
    
    
    function ShowMapDebug(op) 
    {
        Debug (op + ", zoom=" + map.getZoom());
    }

    
    function map_InfoWinClosed()
    {
        // These stupid things close and open.  Set a timer to make damn sure it's closed.
        // This happens when the window is panning to fit the InfoWin in and markers get refreshed  
        // at the same time.
        InfoWindowCloseTimer = setInterval ("InfoWinClosedTimeout();", 250);
        InfoWinCloseCounter = 0;
    }
    
    function InfoWinClosedTimeout(g)
    {
        if (map.getInfoWindow().isHidden())
        {
            InfoWinCloseCounter++;
            
            if (InfoWinCloseCounter >= 4)
            {
                // Been gone for a whole second, I think it's finally closed.
                TideInfoWinMarker = null;
                clearInterval (InfoWindowCloseTimer);
            }
        }
        else
        {
            // The original close was bogus (ie, it got opened again).
            clearInterval (InfoWindowCloseTimer);
        }
    }    
    
    function map_OnLoad()
    {
        if (UrlPosition != null)
        {
           if (UrlPosition.Place)
           {
              var initTab = (UserOptions.InitialInfoTab < InfoTabFullPage) ? UserOptions.InitialInfoTab : 1;
              var pname   = (UrlPosition.Name) ? UrlPosition.Name : UrlPosition.Place;

              Flyto ('', UrlPosition.Place, initTab, UrlPosition.Lat, UrlPosition.Lng, UrlPosition.Zoom, pname);

              pageTracker._trackEvent ("Url", "Url", "Place: " + pname);
           }
           else
           {
              var latLng = new GLatLng(UrlPosition.Lat, UrlPosition.Lng);
              map.setZoom (UrlPosition.Zoom);
              map.panTo(latLng);

              PlaceLoad.BeginRequest (map.getBounds(), UrlPosition.Zoom);
           }
        }
    }
    
    function map_RightClick(point, src, overlay)
    {                    
       CancelPopover();
       
       if (overlay != null)
       {
          var emap = document.getElementById('TheMap');                            
          var pop  = document.getElementById('MkrPop');
          
          PopOverlay = {overlay:overlay, popup:pop};
          
          var webcam = "";
          var bookmk = "";
          var fullPageItems = '';
          
          if (!FailPngIE)
          {
              fullPageItems = '<div class="MkrPopItem" onclick="PopShowPlace(InfoTabFullPage)">Full page</div>' + 
                              '<div class="MkrPopItem" onclick="PopShowPlace(InfoTabFullNewPage)">Full, new window</div>' +
                              '<div class="MkrPopItem" onclick="PopShowPlace(InfoTabPlanner)">Tide planner</div>';
          }
          
          
          if (window.sidebar || window.external)
          {
              bookmk = '<div class="MkrPopItem" onclick="PopBookmark()">Bookmark</div>';
          }
          pName = overlay.t_Name.substring(0,overlay.t_Name.indexOf(';'));
          
          pop.innerHTML = '<div style="position:relative;">' +
                            '<div class="MkrPopHead">' + pName + '</div>' +
                            '<div class="MkrPopItem" onclick="PopShowPlace(0)">Tide table</div>' + 
                            '<div class="MkrPopItem" onclick="PopShowPlace(1)">Tide graph</div>' + 
                            '<div class="MkrPopItem" onclick="PopShowPlace(2)">Sun, Moon</div>' +
                            fullPageItems +
                            bookmk + 
                            webcam +
                            '<div class="MkrPopItemSep"></div>' +
                            '<div class="MkrPopItem" onclick="PopShowPlace(-1)">Cancel</div>'
                          '</div>'; 
                          
          var left = point.x + emap.offsetLeft - 150;
          var top  = point.y + emap.offsetTop - 120;
          
          if (top < 0) top = 0;
        
          pop.style.left = left + "px"
          pop.style.top  = top + "px";
          
          pop.style.display = "block";
       }
           }
    
    function CancelPopover()
    {
       if (PopOverlay != null)
       {
          PopOverlay.popup.style.display = "none";                            
          PopOverlay = null;
       }
    }
    
    function map_Click(overlay)
    {
       CancelPopover();
    }
    
    function PopShowPlace(initTab)
    {
       if (initTab >= 0)
       { 
          portMarker_Click(PopOverlay.overlay, initTab);
       }
       else
       {
          CancelPopover();
       }
    }

    function PopBookmark()
    {
        var url = "http://tidespy.com?place=" + PopOverlay.overlay.t_Number;

        var name  = PopOverlay.overlay.t_Name;
        var scpos = name.indexOf(';');

        if (scpos > 0) name = name.substring(0, scpos);

        CancelPopover();

        if (navigator.userAgent.toLowerCase().indexOf('chrome') >= 0)
        {
        }
        else if (window.sidebar)      // Mozilla
        {
            window.sidebar.addPanel("TideSpy: " + name, url,"");
            return;
        }
        else if (window.external)   // MSIE
        {
            window.external.AddFavorite (url, "TideSpy: " + name);
            return;
        }
        
        alert('Your browser doesn\'t allow automatic creation of bookmarks, but you can create a new bookmark yourself ' +
              'from the following URL:\n' + url);
    }
    
    function PopAddWebcamClick(popOver)
    {
        CancelPopover();
        
        if (confirm ("Please confirm : \r\nDo you wish to add a new webcam marker here? "))
        {
            var latlng = popOver.overlay.getLatLng();
            
            link = "/include/AddWebcam.php?la=" + latlng.lat() + "&lo=" + latlng.lng();
            
            window.open(link, 'PopupPage',
                        'height=560, width=700, scrollbars=yes, resizable=yes');
        }
    }

    function PopRefreshWebcamClick(popOver)
    {
        CancelPopover();
        window.open('/wcdata.php?wcn=' + popOver.overlay.t_Number + '&force', 'PopupPage',
                    'height=560, width=700, scrollbars=yes, resizable=yes');
    }
    

    function map_DoubleClick (map, point)
    {
        CancelPopover();
        map.panTo(point);
        map.setZoom(map.getZoom() + 2);
    }
    
    function map_ZoomStart()
    {
        if (!TideInfoWinMarker && !map.getInfoWindow().isHidden())
        {
            //map.closeInfoWindow();   // probably a blowup.
        }
        RefreshRate = 1.0;
    }
    
    function map_ZoomEnd(oldLevel, newLevel)
    {
        PlaceLoad.BeginRequest (map.getBounds(), newLevel);
        SetOptionsOnMapChange();
        RefreshRate = 1.0;
    }
    
    function map_DragStart()
    {
        CancelPopover()
        if (!map.getInfoWindow().isHidden() && !TideInfoWinMarker)
        {
         //   map.closeInfoWindow();   // probably a blowup.
        }
    }
    
    function map_MoveEnd()
    {
        PlaceLoad.BeginRequest (map.getBounds(), map.getZoom());
        SetOptionsOnMapChange();
        RefreshRate = 1.0;
    }
    
    function map_MouseMove(latLong)
    {
      var node = document.getElementById("LatLongDisp");
      node.innerHTML = LatToStr(latLong) + "<br>" + LngToStr(latLong);
    }

    function map_MouseOut()
    {
      var node = document.getElementById("LatLongDisp");
      node.innerHTML = "&nbsp;<br>&nbsp;";
    }


    function areaMarker_Click(marker)
    {
       CancelPopover();
       map.setCenter(marker.getLatLng());
       
       longExt = marker.t_LngExt;
       
       zoom = map.getZoom();
       ext  = map.getBounds().getNorthEast().lng() - map.getBounds().getSouthWest().lng();
       if (ext > 180) ext = 360 - ext;  // Crossing 180th meridian.
       
       if (zoom < 4 || ext < 0)
       {
          zoom = 4;
          ext  = 53;
       }
       
       while (ext > longExt)
       {
          zoom++;
          ext /= 2;
       }
       if (zoom > 10) zoom = 10;  // Some maps don't go closer than this.
       
       map.setZoom(zoom - 1);
       RefreshRate = 1.0;
       pageTracker._trackEvent ("AreaMarker", "Click", "Area: " + marker.t_Name);
    }
    
    function webcamMarker_Click(marker)
    {
        CancelPopover(); 
        now = new Date();
        imgAtts = marker.t_ImgAtts.replace(/\#/g, now.getTime());
        
        var webcamHtml =
                 "<div class='CamInfoBox' width='" + marker.t_Iwid + "' height='" + marker.t_Ihgt +
                 "'><a target='webcam' href='" + marker.t_Site + "'>" +
                 "<b>Webcam: " + marker.t_Name + "</b></a><br><br>" +
                 "<img " + imgAtts + "/>" + WCVAnchor(marker.t_Number) + "</div>";
                 
        marker.openInfoWindowHtml (webcamHtml);
        
        WebcamInfoMarker = marker;
        
        if (marker.t_AskSvr)
        {
            // Call up the server to count usage, and also in case it needs to work out the image URL.
            RequestWebcamImageData (marker);
        }
        RefreshRate = 1.0;
        pageTracker._trackEvent ("CamMarker", "Click", "Cam: " + marker.t_Name);
    }
    
    function portMarker_Click(marker, initialTab)
    {
        CancelPopover();
        
        if (initialTab == InfoTabPlanner)
        {
           StartTabWarning("Planner");
           window.open("/Planner.php" + "?p=" + marker.t_Number, 'tidespyTwo');
           pageTracker._trackEvent ("PlaceMarker", "Click", "Place: " + marker.t_Name);
        }
        else if (initialTab == InfoTabFullNewPage)
        {
           StartTabWarning("place detail");
           window.open("/OnePlace.php" + "?p=" + marker.t_Number, 'tidespyOne');
           pageTracker._trackEvent ("PlaceMarker", "Click", "Place: " + marker.t_Name);
        }
        else if (initialTab == InfoTabFullPage)
        {
           window.location = "http://" + window.location.hostname + "/OnePlace.php?p=" + marker.t_Number;
           pageTracker._trackEvent ("PlaceMarker", "Click", "Place: " + marker.t_Name);
        }
        else
        {
           TideInfoWinMarker = marker;
        
           var infoString1 = "<div class='InfoBox'><div class='InfoPlaceName'>" + marker.t_InfoName + "</div>" +
                             "<div class='InfoPlaceInfo'><br><small>" +
                             LatToStr(marker.getLatLng()) + ", " +
                             LngToStr(marker.getLatLng()) +
                             "</small></div>" +
                             "<center><br><br>Please wait,<br>downloading tide data...<br><br>(This may take several seconds longer<br>the very first time a place is accessed.)</center><br>" +
                             "</div></div>";

           marker.openInfoWindowHtml (infoString1);
        
           DayOffset = 0;

           pageTracker._trackEvent ("PlaceMarker", "Click", "Place: " + marker.t_Name);

           LoadTideScript (marker.t_Number, initialTab);
        }
    }
    
    // This is called by the loaded tide data script.
    function onTideTabsResponse (place, tabString, tabArray, units, imgInfo, timeInfo)
    {
        var localDay = timeInfo.QueryDow;
        var localMinute = timeInfo.MinuteNow;
        var localTime = timeInfo.DisplayTime;
        var timeZone = timeInfo.TimeZone;
         
        if (!UserOptions.Units)
        {
            UserOptions.Units = units;  // Start the user's units from the first place s/he visits.
            SaveUserOptions (UserOptions);
        }
        
        var background = "<img src='" + imgInfo.Url + "' " + imgInfo.ImgAtts + " />";
        
        var headHtml =  "<div class='HeadBox'>" +
                          "<div class='InfoPlaceName'>" + TideInfoWinMarker.t_InfoName + "</div>" +
                          "<div class='InfoPlaceInfo'>" + localTime + "<br>" +
                            "Zone: " + timeZone + "<br/><small>" +
                            LatToStr(TideInfoWinMarker.getLatLng()) + ", " +
                            LngToStr(TideInfoWinMarker.getLatLng()) + "</small>" +
                          "</div>" +
                        "</div>";
                        
        var navHtml =  "<div class='InfoNav'>" + 
                         "<div class='Disclaimer'>";
                         
        if (imgInfo.Panoramio)
        {
            DisclObj = imgInfo;
            navHtml +=     "<div style='float:left'>" +
                           " <a href='javascript:void(0)' onclick='Disclaim_Click(" + imgInfo.Number + ")'>" +
                           " Photo and copyright notice</a>" +
                           "</div>" +
                           "<div style='float:left;margin:-3px 0 -6px 6px'><a href='http://panoramio.com'>" +
                           "<img src='/images/Panoramio.png' /></a></div>";
        }
        else
        {                         
            navHtml +=     "<a href='javascript:void(0)' onclick='Disclaim_Click(" + imgInfo.Number + ")'>" +
                           "Copyright &bull; Disclaimer &bull; Photo</a>";
        }
        navHtml +=       "</div>" +
                         FeetOrMetres (units) + Time12or24() + PrevDayNextDay (localDay) +
                       "</div>";
             
        var ll   = TideInfoWinMarker.getLatLng();
        var par  = "http://www.panoramio.com/map/#lt=" + ll.lat() + "&ln=" + ll.lng() + "&z=5&k=2";
        var plnk = "<a class='InfoTab' href='" + par + "' target='Panoramio'>Panoramio photos</a> near this place. <small>(In a new window)</small>";

        var name = TideInfoWinMarker.t_InfoName.replace ("<br>", " ");
        var shortName = name.split (',');
        
        var link = '/include/YourWebpageLink.php?pn=' + place + '&name=' + encodeURIComponent (name) + '&ps=' + shortName[0]
        var here = '<a class="InfoTab" href="javascript:void(0)" onclick="LinkLink(\'' + link + '\')">' +
                   'Link your web page</a> to the tides for ' + shortName[0];        
                        
        var comment = (tabString.indexOf("*") >= 0) 
                       ? "<div class='dlsStarBlurb'><b>Important</b><br>Only the times marked * are corrected for daylight saving.</div><br>"
                       : '<div style="font-size: 12px; margin-top:2px;">' + plnk + '<br>' + here + "</div>";
                          
        var tideHtml = "<div class='InfoBox'>" + headHtml +          
                         "<div class='InfoBody'><br><div class='TidesList'><pre id='TidesList'>" + tabString + "</pre></div>" + 
                         comment + "</div>" +
                         navHtml + "</div>";

        var graphHtml = "<div class='InfoBox'>" + headHtml + 
                          "<div class='InfoBody'>" + GraphGenerator (tabArray, units, localMinute, background, timeInfo) + "</div>" +
                          navHtml +
                        "</div>";
                        
        var tabs = [ new GInfoWindowTab ("Tabular", tideHtml), new GInfoWindowTab ("Graph", graphHtml)];
        

        var infoHtml = "<div class='InfoBox'>" + headHtml +
                          "<div class='InfoTabBody'>" + InformationTab (place, TideInfoWinMarker, background, timeInfo) + "</div>" +                        
                          navHtml +
                        "</div>";
                        
        tabs.push (new GInfoWindowTab ("Sun Moon etc", infoHtml));
        

        TideInfoWinMarker.openInfoWindowTabsHtml (tabs, {selectedTab:ShowTabNext});
    }
    
    function LoadTideScript(place, showTab)
    {
        if (showTab == null)
        {
            ShowTabNext = map.getInfoWindow().getSelectedTab();  // ie, return to same tab.
        }
        else
        {
            ShowTabNext = showTab;
        }
       
       var base = window.location.href;
       base = base.substr (0, base.lastIndexOf('/'));
       
       TidesUri = base + "/tdata.php?pn=" + place + 
             "&do=" + DayOffset + "&s=8&z=" + map.getZoom();
             
       TidesUri += (UserOptions.Time24) ? "&t=24" : "&t=12";
             
       if (UserOptions.Units)
       {
           TidesUri += "&u=" + UserOptions.Units;
       }
             
       var head = document.getElementsByTagName("head")[0];
       script = document.createElement('script');
       script.id = 'placeuploadScript';
       script.type = 'text/javascript';
       script.src = TidesUri;
       head.appendChild(script);
    }
    
    function Time12or24 ()
    {
        if (UserOptions.Time24)
        {
            return "<div class='Time12or24'>" +
                    "<a href='JavaScript:void(0)' onclick='SwitchTime(0);'>" +
                    "<img src='images/Time24h12.png'>" +
                    "</a></div>";
        }
        else
        {
            return "<div class='Time12or24'>" +
                    "<a href='JavaScript:void(0)' onclick='SwitchTime(1);'>" +
                    "<img src='images/Time12h24.png'>" +
                    "</a></div>";
        }      
    }
    
    function SwitchTime (new24)
    {
        if (UserOptions.Time24 != new24)
        {
            UserOptions.Time24 = new24;
            SaveUserOptions (UserOptions);
            
            if (TideInfoWinMarker != null)
            {
                var showTab = map.getInfoWindow().getSelectedTab();     // Return to same tab.
                LoadTideScript (TideInfoWinMarker.t_Number, showTab);
            }
            TimeOfLastPlacesList = new Date();
            TimeOfLastPlacesList.setFullYear(2000); // Make it OLD to force all markers to update.
            PlaceLoad.BeginRequest (map.getBounds(), map.getZoom());   

        }
    }
    
    function FeetOrMetres (displayUnits)
    {
        if (displayUnits == "m")
        {
            return "<div class='FeetOrMetres'>" + 
                    "<a href='JavaScript:void(0)' onclick='SwitchUnits(\"ft\");'>" +
                    "<img src='images/MetresFt.png' />" +
                    "</a></div>";
        }
        else
        {
            return "<div class='FeetOrMetres'>" + 
                    "<a href='JavaScript:void(0)' onclick='SwitchUnits(\"m\");'>" +
                    "<img src='images/FeetMtrs.png' />" + 
                    "</a></div>";
        }      
    }
    
    function SwitchUnits(newUnit)
    {
        UserOptions.Units = newUnit;
        SaveUserOptions (UserOptions);
        
        if (TideInfoWinMarker != null)
        {
            LoadTideScript (TideInfoWinMarker.t_Number, null);
        }
    }
    
    function PrevDayNextDay (dow)
    {
        var daze = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
        var html = "<div class='InfoNavDays'>";
        
        for (var d = -4; d <= 4; d++)
        {
            var nday  = daze[(dow + d + 7) % 7];
            var sep   = (d != -4) ? " &nbsp; &bull; &nbsp; " : "";
            var style = (d + DayOffset == 0) ? "style='font-weight:bold;' " : "";
                
            if (d != 0)
                html += sep + "<a href='javascript:void(0)' " + style + "onclick='OnNavDayClick(" + d + ")'>" + nday + "</a>"
            else
                html += sep + "<span " + style + ">" + nday + "</span>";
            
        }
        return html + "</div>";
    }
    
    function OnNavDayClick(dayOff)
    {
        DayOffset += dayOff;
        LoadTideScript (TideInfoWinMarker.t_Number, null);
    }
    
    
    function Disclaim_Click(imgNum)
    {
        if (TideInfoWinMarker != null)
        {
            var content = document.getElementById("DisWinIframe");
            var pn = TideInfoWinMarker.t_Number;
            content.src = "http://tidespy.com/include/DisWinIframe.php?pn=" + pn + "&img=" + imgNum;
            var shade = document.getElementById("Grayout");
            shade.style.display = "block";
        }
    }
    
    function DisWinClose_Click ()
    {
        var shade = document.getElementById("Grayout");
        shade.style.display = "none";
        
        var content = document.getElementById("DisWinIframe");  
        content.src = "";
    }    
    
    
    //-------- Places and markers --------
    
    function PlacesLoader ()
    {
        this.Zoom    = null;
        this.Bounds  = null;
        this.Running = false;
        
        var me = this;
            
        this.BeginRequest = function (bounds, zoomLevel)
        {
            if (!bounds) throw "No bounds in PlacesLoader";
            this.Bounds = bounds;
            this.Zoom   = zoomLevel;
            
            if (!this.Running)
            {
                setTimeout (function () {me.RequestScript()}, 200);
                this.Running = true;
            }
        };
        
        this.EndRequest = function ()
        {
            this.Running = false;
        };
    
        // Loads and runs a script to load up a new batch of places.  Each place is defined by the script
        // calling C() for a country or state, P() for a tide place, or WC() for a webcam.
        this.RequestScript = function ()
        {
           var base = window.location.href;
           base = base.substr (0, base.lastIndexOf('/'));
           
           uri = base + "/include/places.js.php?q=y&zoom=" + this.Zoom +
                 "&be=" + this.Bounds.getNorthEast().lng().toString() +
                 "&bw=" + this.Bounds.getSouthWest().lng().toString() +
                 "&bn=" + this.Bounds.getNorthEast().lat().toString() +
                 "&bs=" + this.Bounds.getSouthWest().lat().toString();
                
           if (UserOptions.Time24) uri += "&t24=y";
                 
           var head = document.getElementsByTagName("head")[0];
           script = document.createElement('script');
           script.id = 'tideuploadScript';
           script.type = 'text/javascript';
           script.src = uri;
           head.appendChild(script);  // This will start the load and execute script when loaded.
        };
    }
        
    
           
    // This function is called by the places.js.php script, when it's about to add the places.
    function OnPlacesScriptStarting ()
    {
        PlaceLoad.EndRequest();
    }
      
    // This function is called by the places.js.php script, when it's done adding the places.
    function OnPlacesScriptFinished ()
    {
        TimeOfLastPlacesList = new Date();
        
        var mapZoom = map.getZoom();
        var mapBnds = map.getBounds();
        
        var crem = cadd = cleft = ctot = crepl = cnew = 0;
        
        for (var npn in NewPlaces)
        {
            var newpm = NewPlaces[npn];
            var oldpm = AllPlaces[npn];

            if (newpm && !oldpm)
            {
                AllPlaces[npn] = newpm;
                cnew++;
            }           
            else if (oldpm && newpm && newpm.t_Hash != oldpm.t_Hash && oldpm != TideInfoWinMarker)
            {
                map.removeOverlay(oldpm);
                AllPlaces[npn] = newpm;
                crepl++;
            }  
        }         
           
        for (var pn in AllPlaces)
        {
            var pmkr = AllPlaces[pn];
    
            if (pmkr.t_MinZoom <= mapZoom && pmkr.t_MaxZoom >= mapZoom &&
                mapBnds.contains(pmkr.getLatLng()))
            {
                if (!pmkr.t_Shown)
                {
                    pmkr.t_Shown = true;
                    map.addOverlay(pmkr);
                    cadd++;
                }
            }
            else if (pmkr.t_Shown)
            {
                map.removeOverlay(pmkr);
                pmkr.t_Shown = false;
                crem++;
            }
            ctot++;
        }
        NewPlaces = [];
       
        // If we're flying to a marker, show the tide info there.
        if (FlyingTo != null && AllPlaces[FlyingTo.Place] != null)
        {
            var minZ = AllPlaces[FlyingTo.Place].MinZoom;
            
            if (map.getZoom() < minZ)
            {
                map.setZoom(AllPlaces[placeNum].MinZoom);
            }
            
            if (map.getZoom() < 12)
            {
                // AllPlaces[FlyingTo.Place].showMapBlowup ( {zoomLevel:13} );
            }
            portMarker_Click (AllPlaces[FlyingTo.Place], FlyingTo.initTab);
            
            FlyingTo = null;
        }
    }
    
    // Called from places script, to add each place.
    function P(zoomLev, number, hash, name, lat, longi, whichMark, nextTide)
    {
       var title = name.replace(/;/g,",\r\n");
       
       if (nextTide != "") title += " :: " + nextTide;
       
       var markerOptions = { title:title, icon:placeIcons[whichMark] };
       
       var placeMkr = new GMarker (new GLatLng(lat, longi), markerOptions);
       
       placeMkr.t_Type     = 'place';
       placeMkr.t_Name     = name;
       placeMkr.t_Number   = number;
       placeMkr.t_Hash     = hash;
       placeMkr.t_MinZoom  = zoomLev;
       placeMkr.t_MaxZoom  = 99;
       placeMkr.t_Icon     = whichMark;
       placeMkr.t_InfoName = name.replace(";USA","").replace(/\;/g, ",<br>");
       
       GEvent.addListener(placeMkr, 'click',    function() { portMarker_Click(placeMkr, UserOptions.InitialInfoTab); } );
       
                     
       NewPlaces[number] = placeMkr;
    }
    
    // Called from places script, to add each area record (country or state).
    function CorS(type, zoomLev, number, name, mlat, mlng, zlat, zlng, longExtent)
    {
        if (type == 'C') // Country
        {
            var markerOptions = { icon:countryIcon, title: name.replace(/\;/g,"") };
        }
        else
        {
            var markerOptions = { icon:stateIcon, title: name.replace(/\;/g,", ") };
        }

        var placeMkr = new GMarker (new GLatLng(mlat, mlng), markerOptions);

        placeMkr.t_Type    = type;
        placeMkr.t_Name    = name;
        placeMkr.t_LngExt  = longExtent;
        placeMkr.t_Zlat    = zlat;
        placeMkr.t_Zlng    = zlng;
        placeMkr.t_MinZoom = zoomLev;
        placeMkr.t_MaxZoom = zoomLev + 3;
        placeMkr.t_Icon    = -1;
        placeMkr.t_Hash    = 0;

        GEvent.addListener(placeMkr, 'click', function() { areaMarker_Click(placeMkr); } );
                     
        NewPlaces[number] = placeMkr;
    }
    
    
    // Called from places script, to add a webcam marker.
    function WC(zoomLev, number, hash, name, imgAttribs, siteLink, mlat, mlng, width, height, askServer)
    {
        markerOptions = 
        { icon  : webcamIcon,
          title : "Webcam " + number + " : " + name  };

        var camMarker = new GMarker (new GLatLng(mlat, mlng), markerOptions);
      
        camMarker.t_Type    = 'cam';
        camMarker.t_Name    = name;
        camMarker.t_Number  = number;
        camMarker.t_ImgAtts = imgAttribs;
        camMarker.t_Site    = siteLink;
        camMarker.t_Iwid    = width;
        camMarker.t_Ihgt    = height;
        camMarker.t_MinZoom = zoomLev;
        camMarker.t_MaxZoom = 20;
        camMarker.t_Icon    = -2;
        camMarker.t_Hash    = hash;
        camMarker.t_AskSvr  = askServer;
        
        GEvent.addListener(camMarker, 'click', function() { webcamMarker_Click(camMarker); } );
                     
                     
        NewPlaces["WC_" + number] = camMarker;
    }
    
    // ----------- Webcam URL stuff ---------------
    // Loads and runs a script to obtain a webcam image URL.
    function RequestWebcamImageData (marker)
    {
       var base = window.location.href;
       base = base.substr (0, base.lastIndexOf('/'));
       
       uri = base + "/wcdata.php?wcn=" + marker.t_Number; 
             
       var head = document.getElementsByTagName("head")[0];
       script = document.createElement('script');
       script.id = 'webcamUrlScript';
       script.type = 'text/javascript';
       script.src = uri;
       head.appendChild(script);  // This will start the load and execute script when loaded.
    }
    
    function OnWebcamUrlReceived (imgAttribs)
    {
        var marker = WebcamInfoMarker;
        marker.t_ImgAtts = imgAttribs;
        
        var webcamHtml =
         "<div class='CamInfoBox'><a target='webcam' href='" + marker.t_Site + "'>" +
         "<b>Webcam: " + marker.t_Name + "</b><br>&nbsp;<br>" +
         "<img " + marker.t_ImgAtts + "/></a>" + WCVAnchor(marker.t_Number) + "</div>";
       
        marker.openInfoWindowHtml (webcamHtml);
    }
    
    //------------ User settings ---------------
    
    function GetUserOptionsCookie ()
    {
        var results = document.cookie.match (/(^|;) ?UsrOpt=([^;]+)(;|$)/);
        
        if (results)
        {
            var uopts = eval (unescape(results[2]));
            if (!uopts.InitialInfoTab) uopts.InitialInfoTab = 1;  // Is known to have got lost.
            return uopts;
        }
        return UserOptions;    // Pre-initialised value.
    }
    
    function SaveUserOptions (uopts)
    {
        var newc = "";
        
        for (uopt in uopts)
        {
            if (newc != "") newc += ",";
            
            if (typeof (uopts[uopt]) == "string")
            {
                newc += uopt + ':"' + uopts[uopt] + '"';
            }
            else
            {
                newc += uopt + ':' + uopts[uopt];
            }
        }
        newc = "({" + newc + "})";
        expires = new Date();
        expires.setMonth (expires.getMonth() + 12);
            
        document.cookie = "UsrOpt=" + escape(newc) + "; path=/; expires=" + expires.toGMTString();
    }
    
    function SetOptionsOnMapChange()
    {
        var ch = false;
        
        if (UserOptions.InitialMapZm)
        {
            if (UserOptions.InitialMapZm == map.getZoom())
            {
                gLL = map.getCenter();
                
                if ((Math.abs(UserOptions.InitialMapLat - gLL.lat()) < 0.001)   &&
                    (Math.abs(UserOptions.InitialMapLng - gLL.lng()) < 0.001))
                {
                    ch = true;
                }
            }
        }
        document.PosnOptions.Here.checked = ch;
        OnUserOptCheckChange ("LastView");
    }
    
    function OnUserOptCheckChange (setting)
    {
        if (setting == "InitialInfoTab")
        {
            var init = 1;
            for (var ix = 0; ix < document.TideOptions.InitialTab.length; ix++)
            {
               // This assumes that the five radio buttons are in the same order as info tabs.
               if (document.TideOptions.InitialTab[ix].checked) init = ix;
            }
            UserOptions.InitialInfoTab = init;
        }
        else if (setting == "Time24")
        {
            SwitchTime (document.TimeOptions.Time24.checked ? 1 : 0);
        }
        else if (setting == "InitialMap")
        {
            if (document.PosnOptions.Here.checked)
            {
                gLL = map.getCenter();
                UserOptions.InitialMapLat = gLL.lat();
                UserOptions.InitialMapLng = gLL.lng();
                UserOptions.InitialMapZm  = map.getZoom();
            }
            else
            {
                UserOptions.InitialMapLat = false;
                UserOptions.InitialMapLng = false;
                UserOptions.InitialMapZm  = false;
            }
        }
        else if (setting == "AngFormat")
        {
            UserOptions.AngFormat = AngFormat;
        }
                        
        else if (setting == "LastView")
        {
            gLL = map.getCenter();
            UserOptions.LastMapLat = gLL.lat();
            UserOptions.LastMapLng = gLL.lng();
            UserOptions.LastMapZm  = map.getZoom();
        }
                        
        SaveUserOptions (UserOptions);
    }
     
     function InitialiseUserOptions()
     {
        UserOptions = GetUserOptionsCookie();
        
        // This assumes that the five radio buttons are in the same order as info tabs.
        document.TideOptions.InitialTab[UserOptions.InitialInfoTab].checked = true;
        
        document.TimeOptions.Time24.checked = UserOptions.Time24 != 0;
        MapToDefault(false);
                        
        AngFormat = UserOptions.AngFormat;
                        
        for (var i = 0; i < document.AngleFormat.AngFormat.length; i++)
        {
           if (document.AngleFormat.AngFormat[i].value == AngFormat)
           {
              document.AngleFormat.AngFormat[i].checked = true;
              break;
           }
        }
     }
     
     function MapToDefault(showAlert)
     {
        if (UrlPosition != null)
        {
            map.setCenter (new GLatLng(UrlPosition.Lat, UrlPosition.Lng), UrlPosition.Zoom);
        }
        else if (UserOptions.InitialMapLat)
        {                                 
            map.setCenter (new GLatLng(UserOptions.InitialMapLat, UserOptions.InitialMapLng), UserOptions.InitialMapZm);
        }
        else if (UserOptions.LastMapLat)
        {                                 
            map.setCenter (new GLatLng(UserOptions.LastMapLat, UserOptions.LastMapLng), UserOptions.LastMapZm);
        }
        else if (showAlert)
        {
            alert("You have not set a default map place to go to.  To set one up, pan and zoom the map to your favourite view and click the check box in the Position panel on the left.");
        }
     }  

     function AngleFormatClick (format)
     {
         AngFormat = format;
         OnUserOptCheckChange ("AngFormat");        

         if (TideInfoWinMarker != null)
         {
             var showTab = map.getInfoWindow().getSelectedTab();     // Return to same tab.
             LoadTideScript (TideInfoWinMarker.t_Number, showTab);
         }
     }
    
    //------------ Recently Used Places Stuff ----------------
    
    function ClearRecentPlaces()
    {
        RecentPlaces = [];
    }
    
    function AddRecentPlace(number, name, area, zoom, lat, lng)
    {
        RecentPlaces.push ({Name:name, Area:area, Zoom:zoom, Num:number, Lat:lat, Lng:lng});
    }
    
    function OnRecentPlacesDone ()
    {
        rps = "";
        
        if (RecentPlaces.length == 0)
        {
            rps = "There are no recently used places.  Click a blue marker to add the place to this list.<p/>" +
                  "The most used places will tend to stay on the list.  Click a name to fly there at roughly the speed of light.";
        }
        else
        {
            for (i=0; i < RecentPlaces.length; i++)
            {
                rp = RecentPlaces[i];
                rps += "<a alt='" + rp.Name + "' href='javascript:Flyto(\"Fave\"," + 
                          rp.Num + "," +
                          UserOptions.InitialInfoTab  + "," +  
                          rp.Lat + "," + 
                          rp.Lng + "," +
                          rp.Zoom + ",\"" +
                          rp.Name + "\")' title='" +
                          rp.Area + "'>" + 
                          rp.Name.replace("'","&apos;") +"</a><br>";
            }
        }

        mru = document.getElementById("mruPlaceList");
        mru.innerHTML = rps;
    }
    
    function Flyto (event, placeNum, initInfoTab, lat, lng, zoom, placeName)
    {
        FlyingTo = {Place:placeNum, initTab:initInfoTab};
        
        if (zoom < 8) zoom = 8;
        
        if (map.getZoom() < zoom)
        {
            map.setZoom (zoom);
        }
        map.panTo(new GLatLng(lat,lng));
        
        // Update the cookie usage counter, since they clicked it.
        results = document.cookie.match (/(^|;) ?MRU=([^;]+)(;|$)/);
        if (results)
        {
            parts = unescape(results[2]).split (",");
            newc  = "";
            
            for (i = 0; i < parts.length - 1; i++)    // -1 to drop the last empty one.
            {
                if (i % 3 == 0 && parts[i] == placeNum)
                {
                    parts[i+2]++;
                }
                newc += parts[i] + ",";   
            }
            expires = new Date();
            expires.setMonth (expires.getMonth() + 12);
            document.cookie = "MRU=" + escape(newc) + "; path=/; expires=" + expires.toGMTString();
        }
        if (event != '')
        {
           pageTracker._trackEvent (event, "Click", event + ": " + placeName);
        }
    }
    
    

    function RollMouseIn(id)
    {
       RollPanel (id, "in");
    }
    
    function RollClick(id)
    {
        RollPanel (id, "invert");
    }

    
    function RollMouseOutAll ()
    {
       for (var panel in Panels)
       {
          RollPanel(Panels[panel], "out");
       }
    }
    
    function InitialRolls()
    {
       cookieStr = document.cookie.match (/(^|;) ?Rolled=([^;]+)(;|$)/); 
       
       if (cookieStr)
       {
          rolled = eval (unescape(cookieStr[2]));                  
          for (var id in rolled)
          {
              if (rolled[id] != "none")
              {
                 RollPanel (id, "pin");
              }
          }
       }
       else
       { 
           RollPanel ("tidespyRoll", "pin");
           RollPanel ("tidesRoll", "pin");
       }
    }

    function RollPanel (id, state)   // out, in, pin (block), unpin (none), invert
    {
        var newCookie = "";
        var style     = document.getElementById(id).style;

        var cookieStr = document.cookie.match (/(^|;) ?Rolled=([^;]+)(;|$)/);     
       
        if (cookieStr !=  null)
            rolled = eval (unescape(cookieStr[2])); 
        else    
            rolled = [];

        if (state == "invert")  state = (rolled[id] == "none") ? "block" : "none";

        if (state == "in")
        {
           style.display = "block";
        }
        else if (state == "out")
        {
           if (rolled[id] != "block") style.display = "none";
        }
        else if (state == "block" || state == "pin")
        {
           style.display = "block";
           document.getElementById(id + "_I").src = "/images/Pinned.png";

           if (rolled[id] != "block")
           {
              newCookie = "block";
           }
        }
        else if (state == "none" || state == "unpin")
        {
           if (rolled[id] != "none")
           {
              document.getElementById(id + "_I").src = "/images/Unpinned.png";
              newCookie = "none";
           }
        }

        if (newCookie != "")
        {
           rolled[id] = newCookie;

           var cookieStr = "({";
       
           for (var item in rolled)
           {
               if (cookieStr.length > 2) cookieStr += ",";
               cookieStr += item + ":'" + rolled[item] + "'";
           }
           cookieStr += "})";
       
           expires = new Date();
           expires.setMonth (expires.getMonth() + 3);
           document.cookie = "Rolled=" + escape(cookieStr) + "; path=/; expires=" + expires.toGMTString();
        }
    }
    
    function LatToStr (latLng)
    {
       if (latLng.lat() < 0)
       {
          return "S " + AngToStr(latLng.lat());
       }
       else
       {
          return "N " + AngToStr(latLng.lat());
       }
    }
    
    function LngToStr (latLng)
    {
       if (latLng.lng() < 0)
       {
          return "W " + AngToStr(latLng.lng());
       }
       else
       {
          return "E " + AngToStr(latLng.lng());
       }
    }
           
    function AngToStr (degrees)
    {
        var ang = Math.abs(degrees);
        var str;
           
        if (AngFormat == "DMS")
        {
            lsu = '"';
            ang += 0.00001388888889;    // 0.05"
            var deg  = Math.floor(ang);
            var mins = (ang - deg) * 60;
            var min  = Math.floor(mins);
            var sec  = (mins - min) * 60;
            str = deg.toString() + "&deg; " + min.toString() + "' " + sec.toFixed(4);
        }
        else if (AngFormat == "DM")
        {
            lsu = "'";
            ang += 0.00008333333333;    // 0.005'
            var deg = Math.floor(ang);
            str = deg.toString() + "&deg; " + ((ang - deg) * 60).toFixed(5);
        }
        else
        {
            lsu = "&deg;";
            ang += 0.00005;   // 0.00005 deg
            str = ang.toFixed(7)
        } 
        return str.substring(0, str.length - 3) + lsu;
    }
    
