    var displaySet = new Array();
    var displayIdx = -1;
    var displayRegion = 'us';
    var imageSet   = new Array();
    var looping    = false;
    var paused     = false;
    var timerID    = -1;
    var timeline;
    var reloadTimer;
    var requested  = '';
    var setloop    = 0;
    var currSvrTime = new Date();

    var frameDelay  = 500;              // Delay between frames
    var lastDwell   = 750;              // Additional delay after last frame

    var update_interval  = 600000;      // Time between checks for new images

    var imgCache    = new ImageCache();

    var fcstbuttons = new Array();

    var months = [ ' Jan ', ' Feb ', ' Mar ', ' Apr ', ' May ', ' Jun ', ' Jul ', ' Aug ', ' Sep ', ' Oct ', ' Nov ', ' Dec ' ];
    var days   = [ 'Sun ', 'Mon ', 'Tue ', 'Wed ', 'Thu ', 'Fri ', 'Sat ' ];
    var params = ['ifr', 'turbhi', 'turblo', 'ice', 'obsc', 'llws', 'sfcwnd', 'fzlvl'];
    var ceil   = ['ifr', 'obsc'];
    var ceilf  = ['IFR', 'MT_OBSC'];
    var ceilfg = ['#FFFFFF', '#FFFFFF'];
    var ceilbg = ['#990099', '#FF00FF'];
    var turb   = ['turbhi', 'turblo', 'llws', 'sfcwnd'];
    var turbf  = ['TURB-HI', 'TURB-LO', 'LLWS', 'SFC_WND'];
    var turbfg = ['#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF'];
    var turbbg = ['#FF6600', '#CC3300', '#993333', '#CC6600'];
    var ice    = ['ice', 'fzlvl'];
    var icef   = ['ICE', 'FZLVL'];
    var icefg  = ['#FFFFFF', '#FFFFFF'];
    var icebg  = ['#0000FF', '#6666CC'];

    var times;

    currSvrTime.setTime(0);

    if (! window.times) { times = new Array(); }


    // If its already marked complete (the image was in cache)
    // call countLoaded by hand since it might not get there
    // on its own thanks to caching and the JS implementation 
    // of some browsers.  Sadly, Safari doesn't implement the
    // img.complete 
    function checkLoaded(img) {
        if ((img.complete == null || img.loaded == 0) && img.onload) {
            img.onload();
        }
    }

    // Reset the loop speed settings back to the defaults
    function resetSpeed() {
        frameDelay  = 500;
        lastDwell   = 750;
    }

    // Reset the loop speed settings back to the defaults
    function changeSpeed(newspeed) {
        frameDelay  = 1500 - newspeed;
        lastDwell   = frameDelay * 1.5;
    }

    // Adjust the loop speed/first dwell/last dwell as requested
    // things read opposite, increasing speed means to subtract 
    // from the delay..., so to increase dwell we need to 
    function adjustSpeed(direction) {
        var increment;
        var which;
        if (direction < 0) {
           increment = 80;
        } else {
           increment = -80;
        }
        
        which = document.time.speed.options[document.time.speed.selectedIndex].value;
        if (which == 'loop') {
            frameDelay += increment;
            // We are not going to let this get to 0 just so we have time between
            // timer events to 'do' other stuff, like cancel them
            if (frameDelay < 150) {
                frameDelay = 150;
            }
        } else if (which == 'dwell') {
            // This can go to 0 since its just added onto the frameDelay
            lastDwell -= 2*increment;
            if (lastDwell < 0) {
                lastDwell = 0;
            }
        } else {
            // no idea what we are talking about
        }
    }

    function delElement(ename, child) {
        var ni = document.getElementById(ename);
        ni.removeChild(child);
    }

    function addElement(ename, child) {
        var ni = document.getElementById(ename);
        ni.appendChild(child);
    }

    // Is the time from the timelist index, valid w.r.t. the current time
    // given from the server
    function timeValid(timeval) {
        if (timeval) {
            var yyyy = timeval.substr(0,4);
            var mon  = timeval.substr(4,2) - 1;
            var day  = timeval.substr(6,2);
            var hr   = timeval.substr(8,2);
            var issue = new Date(Date.UTC(yyyy,mon,day,hr,0,0,0));
            if (issue.getTime() >= currSvrTime.getTime() - 10800000) {
                return true;
            }
        }
        return false;
    }

    function toggleButton(ename, which) {
        var ni = document.getElementById(ename);
        if (ni) {
            var d_fg = '#0000CC';
            var d_bg = '#99CCFF';
            var bg;
            var fg;

            if (which) {
                bg = d_fg;
                fg = d_bg;
                for (var i = 0; i < ceil.length; i++) {
                    if (ename == ceil[i]) {
                        bg = ceilbg[i];
                        fg = ceilfg[i];
                    }
                }
                for (var i = 0; i < turb.length; i++) {
                    if (ename == turb[i]) {
                        bg = turbbg[i];
                        fg = turbfg[i];
                    }
                }
                for (var i = 0; i < ice.length; i++) {
                    if (ename == ice[i]) {
                        bg = icebg[i];
                        fg = icefg[i];
                    }
                }

                ni.style.backgroundColor = bg;
                ni.style.color           = fg;
            } else {
                ni.style.backgroundColor = d_bg;
                ni.style.color           = d_fg;
            }
        } else {
            window.alert('Element not found: ' + ename);
        }
    }

    function imgDone() {
    }

    function imgReady() {
        this.loaded = 1;
        this.onload = imgDone;
        this.style.display = 'block';
        this.zIndex = this.dispLevel;
    }

    function jumpTo(indx) {
        var curr; 
        var prev = displayIdx;

        if (indx == 'first') {
            curr = 0;
        } else if (indx == 'last') {
            curr = times.length - 1;
        }  else {
            curr = indx;
        }

        if (curr > 0) {
            if (looping) {
            } else {
                if (curr > times.length - 1) {
                    curr = times.length - 1;
                } else if (curr < 0) {
                    curr = 0;
                }
            }
        } else {
            if (looping) {
                while (curr < 0) {
                    curr = times.length + curr;
                }
            } else {
                if (curr > times.length - 1) {
                    curr = times.length - 1;
                } else if (curr < 0) {
                    curr = 0;
                }
            }
        }

        displayIdx = curr;

        if (typeof(document.time.when) == undefined) {
            document.time.when.selectedIndex = curr;
        }

        updateDisp();

        if (timeline && prev >= 0) {
            toggleButton(fcstbuttons[prev], 0);
        }
        if (timeline && displayIdx >= 0) {
            toggleButton(fcstbuttons[displayIdx], 1);
        }
    }

    function stepTime(dir) {
        var curr;

        if (dir == 'first') {
            curr = 0;
        } else if (dir == 'last') {
            curr = times.length - 1;
        }  else {
            curr = displayIdx + dir;
            if (curr > 0) {
                curr = curr % times.length;
            } else {
                curr = curr + times.length;
            }
        }

        if (timeline) {
            timeline.setValue(curr);
        }
    }

    function moveTo(indx) {
        var curr;

        if (looping) {
            stopLoop();
        }

        if (indx == 'first') {
            curr = 0;
        } else if (indx == 'last') {
            curr = times.length - 1;
        }  else {
            curr = indx;
        }

        if (timeline) {
            timeline.setValue(curr);
        }
    }

    function updateDisp() {
        for (var i = 0 ; i < imageSet.length; i++) {
            if (imageSet[i] != null) {
                imageSet[i].style.display = 'none';
            }
        }

        if (displaySet.length == 0) {
            if (looping) {
                paused = true;
                stopLoop();
            }

            indx = 0;
            imageSet[indx]  = imgCache.getImg('empty.gif', 'content');

            imageSet[indx].loaded         = 0;
            imageSet[indx].onload         = imgReady;
            imageSet[indx].useMap         = '#zoommap';
            imageSet[indx].alt            = 'blank image'; 
            imageSet[indx].border         = '0px';
            imageSet[indx].style.position = 'absolute';
            imageSet[indx].style.top      = '0px';
            imageSet[indx].style.left     = '0px';
            imageSet[indx].style.opacity  = 0.5;
            imageSet[indx].style.filter   = 'alpha(opacity=50)';
            imageSet[indx].dispLevel      = 200;
            imageSet[indx].display        = 'block';
            checkLoaded(imageSet[indx]);

            indx = 1;

            if (typeof(document.time.when) == undefined) {
                imageSet[indx]  = imgCache.getImg('instructions.gif', 'content');
                imageSet[indx].alt        = 'Instructions image'; 
            } else {
                imageSet[indx]  = imgCache.getImg('splash.gif', 'content');
                imageSet[indx].alt        = 'Splash image'; 
            }
            imageSet[indx].loaded         = 0;
            imageSet[indx].onload         = imgReady;
            imageSet[indx].useMap         = '#zoommap';

            imageSet[indx].border         = '0px';
            imageSet[indx].style.position = 'absolute';
            imageSet[indx].style.top      = '0px';
            imageSet[indx].style.left     = '0px';
            imageSet[indx].style.opacity  = 1.0;
            imageSet[indx].style.filter   = 'alpha(opacity=100)';
            imageSet[indx].dispLevel      = 204;
            imageSet[indx].display        = 'block';
            checkLoaded(imageSet[indx]);
        } else {
            if (paused) {
                paused = false;
                startLoop();
            }

            var when = "";
            var info = new Array();

            if (timeValid(times[displayIdx])) {
                info = times[displayIdx];
            } else {
                info = 'unknown';
            }

            var images_needed = displaySet.length;

            for (var i = imageSet.length - 1; i >= images_needed; i--) {
                imageSet[i].style.display = 'none';
            }

            for (var i = 0, indx = (images_needed - displaySet.length); i < displaySet.length; i++, indx++) {
                // 200703020900_nc_TURB-HI.gif
                var file = "/data/products/gairmet/" + info + "_" + displayRegion + "_" + displaySet[i] + ".gif";
                imageSet[indx]                 = imgCache.getImg(file, 'content', 'missing_' + displaySet[i] + '.gif');
                imageSet[indx].loaded          = 0; 
                imageSet[indx].onload          = imgReady;
                imageSet[indx].useMap          = '#zoommap';
                if (info != 'unknown') {
                    var yyyy = info.substr(0,4);
                    var mon  = info.substr(4,2) - 1;
                    var day  = info.substr(6,2);
                    var hr   = info.substr(8,2);
                    var issue = new Date(Date.UTC(yyyy,mon,day,hr,0,0,0));
                    var dayofweek = issue.getUTCDay();
                    var entry = hr + '00 UTC ' + days[dayofweek] + day + months[mon] + yyyy;
                    imageSet[indx].alt             = displaySet[i] + " image valid at " + entry; 
                } else {
                    imageSet[indx].alt             = "Missing " + displaySet[i] + " image"; 
                }
                imageSet[indx].border          = '0px';
                imageSet[indx].style.position  = 'absolute';
                imageSet[indx].style.top       = '0px';
                imageSet[indx].style.left      = '0px';
                imageSet[indx].style.opacity   = 1.0;
                imageSet[indx].style.filter    = 'alpha(opacity=100)';
                imageSet[indx].style.dispLevel = (images_needed - i) * 2 + 200;
                imageSet[indx].style.display   = 'block';
                checkLoaded(imageSet[indx]);
            }
        }
    }

    function onDisp(which) {
        for (var i = 0; i < displaySet.length; i++) {
            if (displaySet[i] == which) {
                return i;
            }
        }
        
        return -1;
    }

    function adjustDisplay(ename, which) {
        var where = onDisp(which);

        if (where == -1) {
            displaySet.push(which);
            toggleButton(ename, 1);
        } else {
            displaySet.splice(where,1);
            toggleButton(ename, 0);
        }
    }

    function showType(ename) {
        var gname = "";
        var which = "";
        for (var i = 0; which == "" && i < ceil.length; i++) {
            if (ename == ceil[i]) {
                which = ceilf[i];
                gname = 'ceil';
            }
        }
        for (var i = 0; which == "" && i < turb.length; i++) {
            if (ename == turb[i]) {
                which = turbf[i];
                gname = 'turb';
            }
        }
        for (var i = 0; which == "" && i < ice.length; i++) {
            if (ename == ice[i]) {
                which = icef[i];
                gname = '';
            }
        }
        adjustDisplay(ename, which);
        updateDisp();
    }

    function showMe(element) {
        var ename = element.id;
        showType(ename);
    }

    function showAll() {
        for (var i = 0; i < ceil.length; i++) {
            toggleButton(ceil[i],1);
            displaySet.push(ceilf[i]);
        }
        for (var i = 0; i < turb.length; i++) {
            toggleButton(turb[i],1);
            displaySet.push(turbf[i]);
        }
        for (var i = 0; i < ice.length; i++) {
            toggleButton(ice[i],1);
            displaySet.push(icef[i]);
        }

        updateDisp();
    }

    function showNone() {
        for (var i = 0; i < ceil.length; i++) {
            toggleButton(ceil[i],0);
        }
        for (var i = 0; i < turb.length; i++) {
            toggleButton(turb[i],0);
        }
        for (var i = 0; i < ice.length; i++) {
            toggleButton(ice[i],0);
        }

        displaySet.length = 0;
        stopLoop();
        updateDisp();
    }

    function loopImages() {
        var delay = frameDelay;

        timerID = -1;
        if (looping == 1) {
            stepTime(1);

            if (displayIdx == (times.length - 1)) {
                delay += lastDwell;
            }

            var newID = setTimeout('loopImages()', delay);
            if (timerID == -1) {
                timerID = newID;
            } else {
                clearTimeout(newID);
            }
        }
    }

    function toggleZoom(region) {
        var back = document.getElementById('backmap');
        if (displayRegion != 'us') {
            displayRegion = 'us';
            back.src = 'blank_us_map.gif';
        } else {
            displayRegion = region;
            back.src = 'blank_' + region + '_map.gif';
        }
        updateDisp();
    }

    function stopLoop() {
        if (looping == 1) {
            looping = 0;
            if (timerID != -1) {
                clearTimeout(timerID);
                timerID = -1;
            }
            if (paused) {
                document.time.loop.value = 'Paused';
            } else {
                document.time.loop.value = 'Play';
            }
        }

        return true;
    }

    function startLoop() {
        if (looping != 1) {
            // be sure to set looping first or loopImages() won't start
            looping = 1;
            if (timerID != -1) {
                clearTimeout(timerID);
                timerID = -1;
            }
            loopImages();
            document.time.loop.value = 'Stop';
        }
        return true;
    }

    function toggleLoop() {
        if (looping == 1) {
            stopLoop();
            updateDisp();
        } else {
            if (displaySet.length != 0) {
                startLoop();
            }
            if (paused) {
                paused = false;
                document.time.loop.value = 'Play';
            }
        }
        return true;
    }

    function updateTimes() {
        var last_elm = null;
        var first_elm = null;
        var index = 0;

        var base_width = 0;
        
        if (times.length > 0) {
            base_width = (110.0 / times.length);
        }
        
        for (var i=0; i < fcstbuttons.length; i++) {
            var ename = 'b' + i;

            var element = document.getElementById(ename);
            if (element) {
                while (index < times.length && !timeValid(times[index])) {
                    index = index + 1;
                }

                if (times[index] && timeValid(times[index])) {
                    var yyyy = times[index].substr(0,4);
                    var mon  = times[index].substr(4,2) - 1;
                    var day  = times[index].substr(6,2);
                    var hr   = times[index].substr(8,2);
                    var issue = new Date(Date.UTC(yyyy,mon,day,hr,0,0,0));

                    var when = issue.getTime();

                    issue.setTime(when);

                    yyyy = issue.getUTCFullYear();
                    mon  = issue.getUTCMonth();
                    day  = issue.getUTCDate();
                    hr   = issue.getUTCHours();
                    var dayofweek = issue.getUTCDay();

                    if (hr < 10) {
                        hr = '0' + hr;
                    }

                    if (day < 10) {
                        day = '0' + day;
                    }

                    var alt_text = 'Jump to ' + hr + 'Z forecast'; 
                    element.alt = alt_text;
                    element.title = alt_text;

                    var entry = hr + '00 UTC ' + days[dayofweek] + day + months[mon] + yyyy;

                    if (typeof(document.time.when) == undefined) {
                        document.time.when.options[i].text = entry;
                    }

                    element.value = hr + "Z";
                    last_elm = element;
                    if (first_elm == null) {
                        first_elm = element;
                        first_elm.parentNode.align = 'left';
                        first_elm.parentNode.width = base_width - 5.0 + '%';
                    } else {
                        element.parentNode.align = 'center';
                        element.parentNode.width = base_width + '%';
                    }
                    element.style.display  = 'block';
                    index = index + 1;
                } else {
                    element.style.display  = 'none';
                }
            }
        }

        timeline.setMinimum(0);
        // The slider doesn't like it if the start and end are the same value
        if (times.length > 1) {
            timeline.setMaximum(times.length - 1);
        } else {
            timeline.setMaximum(0.1);
        }

        if (last_elm != null && last_elm != first_elm) {
            last_elm.parentNode.align = 'right';
            last_elm.parentNode.width = base_width - 5.0 + '%';
        }

        if (arguments.length > 0) {
            updateDisp();
        }
    }

    function setupStart() {
        var element = document.getElementById('timebuttons');
        if (element) {
            fcstbuttons = new Array();
            for (var i=0; i < element.childNodes.length; i++) {
                for (var j=0; j < element.childNodes[i].childNodes.length; j++) {
                    if (element.childNodes[i].childNodes[j].id) {
                        fcstbuttons.push(element.childNodes[i].childNodes[j].id);
                    }
                }
            }
        } else {
           window.alert('Invalid reference page');
        }

        updateImageTimes();
        moveTo(0);
        var hazards = requested.split("");
        while (hazards.length > 0) {
            var pset = hazards.pop();
            pset = parseInt(pset, 16);
            if (pset && pset < params.length) {
                pset = pset - 1;
                showType(params[pset]);
            }
        }

        if (setloop && displaySet.length != 0) {
            startLoop();
        }

        // In case we are called upon again after the page is loaded
        requested = "";
        setloop = 0;
    }


    // Set the cache timestamp so we know if we need to update images
    // and use updateTimes() to reassign the time values based on the
    // info in the array
    function refreshImageTimes(newtimes, timestamp, current) {
        var toRemove = 0;

        // Set this before testing for valid
        currSvrTime.setTime(current*1000);

        for (var i=0; i < newtimes.length; i++) {
            if (!timeValid(newtimes[i])) {
                toRemove += 1;
            }
        }

        for (var i=0; i < toRemove; i++) {
            newtimes.shift();
        }

        times = newtimes;
        imgCache.setTime(timestamp);
        updateTimes();
        if (displayIdx >= times.length) {
            var waslooping = looping;
            if (waslooping) {
                stopLoop();
                moveTo(0);
            }

            if (waslooping) {
                startLoop();
            }
        } else {
            if (displayIdx < 0) {
                jumpTo(0);
            } else {
                updateDisp();
            }
        }
    }


    function updateImageTimes() {
        // Call to the server to get updated list of images, from the rpc.js file
        // it calls the refreshImageTimes() function to bring in the info
        callToServer('/products/gairmet/updatetimes.php');
    
        // reset timer to check for updates
        reloadTimer = setTimeout('updateImageTimes()', update_interval);
    }

    // Set up the page once the window loads
    // It has to be in a function or the prevLoad variable will
    // cause infinite recursion
    function addInitFunction() {
        var prevLoad = window.onload;
        window.onload = function() {
            if (prevLoad) {
                prevLoad();
            }
            setupStart();
        }
    }
