// catch console.log commands so debug doesn't have to get removed
if (window.console === undefined ) {
    window.console = {}; console.log = function(){};
}
(function($) {
    //Global vars (only global to us)
    var opts                = null;
    var our_map             = null;
    var racer_manager       = null;
    var checkpoint_manager  = null;
    var race_data           = {};
    var now                 = null;
    var cookie              = {};
        cookie.racers       = {};
    var cookie_name         = '';
    var racer_watch_array   = [];
    var racer_watch_list    = '';
    var racer_view_list     = [];
    var checkpoint_list     = [];
    var now_seconds         = 0;
    var day_of_week         = ['Sun','Mon','Tues','Wed','Thurs','Fri','Sat'];
    var month_of_year       = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    var encoding_strings    = ['?','@','A','B'];
    var left_nav_width      =0.56;
    var position_data_changed = false;
    var use_this_starttime  = 0;
    var map_load_starttime  = 0;
    var show_skipper        = false;
    var show_loa            = false; // Length OverAll (Boat Length)
    var starttime_offset    = 0;
    var race_time           = 0;
    var replay_speed        = 1;
    var show_labels         = true;
    var buoy_data_id        = 2267;
    var show_handicap       = false;
    var show_status         = false;
    var show_age	    = false;
    var show_trail_id_list  = [];
    var polyline_mousemove_handler = null;
    var trail_label         = null;
    var map_mouse_lat       = null;
    var map_mouse_lng       = null;
    var polyline_weight_highlight_weight = 2;
    var opacity_solid       = 1;
    var ui_playpause_button = null;
    var ui_time_slider = null;
    var ui_replay_active = false;
    var ui_previous_slider_value = null;
    var ui_current_slider_value = null;

    function getLastTweet(twitter_username)
    {
        var twit_url = 'http://twitter.com/status/user_timeline/transpac09.json?count=1&callback=?';
        jQuery.getJSON(twit_url, gotTweet);
    }
    function gotTweet(data)
    {
        // console.log('tweet received');
        jQuery('#tweets').html( data[0].text );
    }
    function updatePopupAge()
    {
        if (!show_age)
        {
            return false;
        }
    }
    function updateAge()
    {
	if (!show_age)
	{
		return false;
	}
        right_now = Math.round(new Date().getTime() / 1000) ;
        if (use_this_starttime > 0)
        {
            right_now -= starttime_offset;
        }
        right_now = Math.round(right_now / 10) * 10;
        for (var id in race_data.racers)
        {
            if (race_data.racers[id].last_time !== undefined)
            {
                var unit_age = right_now - race_data.racers[id].last_time; 
                if (unit_age > 28800)
                {
                    jQuery('td.' + id + '_time').text('> 8 hours');
                } else {
                    jQuery('td.' + id + '_time').text(sa(unit_age));
                }
            } else {
                jQuery('td.' + id + '_time').text('> 8 hours');
            }            
        }
    };
    

    updateRaceTime  = function()
    {
        var d = new Date();
        var local_utc_off = d.getTimezoneOffset();
        var race_utc_off = opts.race_settings.race_utc_minutes_offset; // 540;
        var race_tz_string = opts.race_settings.race_timezone_name;  // 'AK/HST'
        race_time = new Date( d.valueOf() + (local_utc_off - race_utc_off) * 60 * 1000 );
        if (use_this_starttime > 0)
        {
            race_time -= starttime_offset * 1000;
            var replay_seconds = 0;
            if (replay_speed > 1)
            {
                var uptime = d - map_load_starttime * 1000;
                replay_seconds = uptime * replay_speed;
                
            }
            race_time += replay_seconds;
            
        }

        $('div#local_time').text( dateFormat( d, "mmm dd h:MM:ss TT Z" ) );
        $('div#race_time').text( dateFormat( race_time, "mmm dd h:MM:ss TT" ) + " " + race_tz_string );
    };

    $.ionearth    = function (options)
    {
        //This is only to bring the settings from settings.js over, and should be considered
        //deprecated.  Eventually we should just include whatever specific race settings we
        //need in options.race_settings, not D
        options.race_settings   = $.extend({}, $.ionearth.defaults.race_settings, D, options.race_settings);
        opts                    = $.extend({}, $.ionearth.defaults, options);
        init();
    };


    //Any defaults we want go here!
    $.ionearth.defaults = {
        course_settings: {
            weight     : 2,
            color      : '#FF0000',
            opacity    : 0.8
        },
        urls: {
            racer_url_1: '/api/2009-02-06/load_race_view/',
            racer_url_2: '/racers6.js?reload=' + new Date().valueOf(),
            checkpoint_url_1: '/api/2009-04-16/get_course_features/',
            checkpoint_url_2: '/as/json'
        },
        show_gridlines : true,
        show_circles : true,
        race_settings : {
            racer_name              : "Name",
            race_mile_name          : "NMtF",
            show_temp               : false,
            show_altitude           : true,
            show_trails             : false,
            start_time              : '',
            stop_time               : '',
            start_lat               : '',
            start_lon               : '',
            start_zoom              : 7,
            start_tileset           : 's',
            checkpoint_icon         : '/images/checkpoints.png',
            trail_image             : '/images/checkpoint_clear.png',
            race_utc_minutes_offset : 240,
            race_timezone_name      : 'EDT',
            cluster_options         : null,
            checkpoints             : [],
            course_url              : null,
            cluster_icon            : 'http://static.ionearth.com/images/mushers_camping.png'
        },
        rMap                        : null,
        map_type                    : "basic",
        view_id                     : 0,
        race_id                     : 0,
        rose                        : ['N','NE','E','SE','S','SW','W','NW'],
        map_location                : 'content',
        clustering_on               : false,
        course_loaded               : false,
        cluster_options             : null
    };
    function initReplayUI()
    {
        slider_min = Math.round(race_data.telem_start / 60) * 60;
        slider_max = Math.round(race_data.telem_end / 60) * 60;
        var day = 28400;
        var hour = 3600;
        var minute = 60;
        slider_step = 1 * hour;
        console.log('Setting slider min to ' + slider_min);
        console.log('Setting slider max to ' + slider_max);
        jQuery('#replay_slider').css('width', '80%');
        ui_time_slider = jQuery('#replay_slider').slider(
            {min: slider_min, max: slider_max, step: slider_step, value: slider_max,
            slide: onSliderMove,
            start: onSliderStart, change: onSliderChange, stop: onSliderStop, animate: true }
        );
        ui_playpause_button = jQuery('#play_pause_button');
        ui_playpause_button.click(onPlayPauseClicked);
    }
    
    function onSliderStart(event, ui)
    {
        console.log('user grabbed the slider from ' + new Date(ui.value * 1000 ) );
        ui_replay_active = true;
        ui_time_slider.current_value = ui.value;
    }
    function onSliderMove(event, ui)
    {
        console.log('the slider has been moved to ' + new Date(ui.value * 1000)  );
        ui_time_slider_move_time = ui.value;
    }
    function onSliderChange(event, ui)
    {
        console.log('the slider has changed value to ' + new Date(ui.value * 1000) );
        ui_replay_active = true;
    }
    function onSliderStop(event, ui)
    {
        ui_replay_active = false;
        console.log('show race at ' + new Date(ui.value * 1000) );
    }
    function onPlayPauseClicked(event, ui)
    {
        console.log('the play / pause button has been clicked' );
    }
    function getRaceTime()
    {
        if (!slider_visible)
        {
            
        }
    }

    function initRaceData()
    {
        race_data.racers                    = {};
        race_data.polyline                  = {};
        race_data.checkpoints               = {};
        race_data.trail_weight              = 1;
        race_data.trail_opacity             = 1;
        race_data.checkpoints               = [];
        race_data.encoded_points            = '';
        if (opts.race_settings.show_trails)
        {
            race_data.default_trail_length      = -1;
            race_data.trail_length              = -1;
        } else {
            race_data.default_trail_length      = 0;
            race_data.trail_length              = 0;
        }
        race_data.show_trails               = opts.race_settings.show_trails;
        race_data.telem_start               = opts.race_settings.start_time;
        race_data.telem_end                 = opts.race_settings.stop_time;
        race_data.initial_load_completed    = false;
        race_data.old_now                   = 0;

    }

    function check_for_cookie()
    {
        cookie_name                     = 'race_' + opts.view_id;
        // var stored_cookie               = $.cookie(cookie_name);
        var stored_cookie = null;
        
        if (null !== stored_cookie)
        {
            try 
            {
                cookie                          = $.secureEvalJSON(stored_cookie);
                set_config_from_cookie();
            } catch(ex) {
                // something bad happened reading the cookie
                // delete the cookie from the users browser
                $.cookie(cookie_name, null);
                
            } finally {
                var x = 1; // nop
            }
        } else {
            $('.watch_cb').attr('checked', true);
            // $('.view_cb').attr('disabled', false).attr('checked', true);
            $('.view_cb').attr('disabled', false).attr('checked', false);
        }
    }
    function reload_page()
    {
        location.reload(true);
    }
    var seconds = 1000;
    var current_time = Math.round(new Date().getTime()/1000.0); // slider_value represents the current time
    var slider_min = current_time + 10; // race starts in 10 seconds
    var slider_max = current_time + 10; // slider max is same as min before race starts
    var slider_step = 5; // lock slider to 5 second increments
    var slider_increment = 5;
    var slider_update_race = 1 * seconds; // how often to calculate race state
    var slider_race_end = current_time + 60;
    function adjust_slider()
    {
        slider_value += slider_increment;
        jQuery('#replay_slider').slider('value', slider_value);
        if (slider_value > slider_max)
        {
            // The current time is after the end of the slider,
            // adjust the "max" value   
            if (slider_value > slider_race_end)
            {
            }
        } else {
            // slider value is in replay mode
            // or the race has not started yet
            if (slider_value > slider_min)
            {
                // the slider is in replay mode
                // and the race is currently active
            } else {
                // the slider
            }
        }
                
    }

    function init()
    {
        our_map                 = opts.rMap;
        now                     = new Date();
        map_load_starttime = Math.round(new Date().getTime() / 1000) ;
        // Force page refresh every 4 hours
        setTimeout( reload_page, 4 * 60 * 60 * 1000);
        if (use_this_starttime > 0)
        {
            starttime_offset = Math.round(new Date().getTime() / 1000 ) - use_this_starttime;
        }

        initRaceData();
        initReplayUI();
        updateRaceTime();
        jQuery('#boat_label_toggle').click(toggle_label_visibility);
        setup_age_of_data_checkbox();
        toggle_label_visibility();
        initMap();
        // loadCourse();
        reset_trails_and_markers(race_data.racers);
        $('#race_tabs').tabs();
        $('#left_nav').css('visibility','visible');
        $('#footer_container').css('visibility','visible');
        $('.fade_back').css('visibility','visible');
        $('#loading').hide();
        if (opts.show_gridlines)
        {
            var llGrat = new LatLonGraticule(true);
            our_map.addOverlay(llGrat);
        }
        if (opts.show_circles)
        {
            // Interval is in meters
            var radius_100_nautical_miles =  185200;
            var radius_25_nautical_miles= radius_100_nautical_miles / 4;
            var radius_labels = [];
            var radius_count = 30;
            var line_width = 1;
            for (x = 1; x <= radius_count; x++)
            {
                radius_labels.push((x * 25) + 'nm');
            }
            // HARD CODED - NOTE - WARNING - STOP - FIX
            rings = new BdccRangeRings( (new GLatLng(46.711031,-92.007194)), "#008000", line_width, 0.5, radius_25_nautical_miles, radius_count, radius_labels, 0, 100, 456 );
            our_map.addOverlay(rings);
        }
        setTimeout( initData, 500 );
        //setTimeout( getLastTweet, 1000 * 5 );
        //setInterval( getLastTweet, 1000 * 60 * 5 ); // update tweets every 5 minutes
    }
    function setup_age_of_data_checkbox()
    {
        jQuery('#show_age_toggle').click(toggle_age_of_data);
        jQuery('#show_age_toggle').attr('checked',show_age);
        toggle_age_of_data();
    }
    function toggle_age_of_data()
    {
        show_age = jQuery('#show_age_toggle').attr('checked');
        if (show_age)
        {
            jQuery('.show_age').css('visibility','visible');
            updateAge();
        } else {
            jQuery('.show_age').css('visibility','hidden');
        }
    }
    function toggle_label_visibility()
    {
            var labels_visible = jQuery('#boat_label_toggle').attr('checked');
            if (labels_visible)
            {
                jQuery('.boat_name_label').css('visibility','visible');
            } else {
                jQuery('.boat_name_label').css('visibility','hidden');
            }
    }
    function initData()
    {
        loadRacers();
        jQuery('#loading_progress').append('<li>racers loaded</li>');
        loadCheckpoints();
        jQuery('#loading_progress').append('<li>checkpoints loaded</li>');
        check_for_cookie();
        jQuery('#loading_progress').append('<li>user preferences loaded</li>');
        build_racers_accordion();
        build_racers_table();
        // get_buoys();
        // $('#left_nav').css('visibility','visible');

        // Update the age every X seconds
        age_interval = 10; // value of 10 means update age every 10 seconds
        race_data.age_interval  = setInterval(updateAge, age_interval * 1000);
        updateAge();

        $(window).bind('resize', function() {
            resize_map_div();
        });

        $('#configure_race').click(function() {
            $('#config_dialog').dialog('open');
        });

        $('#config_dialog').dialog({
            height: 500,
            width: 700,
            title: 'Configure Viewing Options',
            autoOpen: false,
            modal: true,
            buttons: {
                'Cancel': function() {
                    $(this).dialog('close');
                },
                'Apply': function() {
                    $(this).dialog('close');
                    set_options();
                }
            }
        });
        
        $('.class_zoom').click(function() {
            alert('This should show all boats in this class');
        });

        $('#zoom_racers').click(function() {
            reset_view('racers');
        });

        $('#zoom_course').click(function() {
            reset_view('course');
        });

        $('.button').hover(
        function(){
            $(this).addClass('ui-state-hover');
        },
        function(){
            $(this).removeClass('ui-state-hover');
        });

        race_data.time_update   = setInterval(updateRaceTime,1000);

        $('body').unload(function() {
            GUnload();
        });

        get_telemetry();
        // Get updates from the server once per minute
        setInterval(get_telemetry, 60 * 1000);
    };

    //Internal Functions here

    function set_config_from_cookie()
    {
        return ; // ignore cookies for now
        racer_watch_list                  = cookie.watch.join('');
        racer_watch_array                 = cookie.watch;
        racer_view_list                   = cookie.keep_in_view;
        race_data.default_trail_length    = cookie.trail_length;
        $('#trail_length').val(cookie.trail_length);
        var view_selecter;
        for (var watch_id = 0; watch_id < cookie.watch.length; watch_id++)
        {
            var watch_selector  = '#watch' + cookie.watch[watch_id];
            view_selector   = watch_selector.replace(/watch/, "view");
            $(watch_selector).attr('checked', true);
            $(view_selector).attr('disabled', false);
        }

        $('.view_cb').attr('checked', false);
        $('.view_cb').attr('disabled', true);

        for (var view_id = 0; view_id < cookie.keep_in_view.length; view_id++)
        {
            var view_selector  = '#view' + cookie.keep_in_view[view_id];
            $(view_selector).attr('disabled', false);
        }
        if (undefined !== cookie.racers)
        {
            for (var id in cookie.racers)
            {
                var trail_color_selector            = '#trail_color_' + id;
                race_data.racers[id].trail_color    = cookie.racers[id].trail_color;
                $(trail_color_selector).val(cookie.racers[id].trail_color);
            }
        }
    }

    function resize_map_div()
    {
        $('body,html').css('overflow', "auto");
        //$('#footer').width($(window).width() - $('#player_controls').width() - $('#left_nav').width() -0.5);
        // This seems to work - but wtf - why these magic numbers 
        $('#footer').width($(window).width() - 50 - left_nav_width -0.5);
        var original_map_width = $('#' + opts.map_location).width();
        var map_height  = get_map_height();
        var map_width   = get_map_width();
        $('#' + opts.map_location).css('position', 'absolute');
        $('#' + opts.map_location).css('top',$('#left_nav').top);
        $('#' + opts.map_location).height(map_height);
        $('#' + opts.map_location).width(map_width);
        $('body,html').css('overflow', "hidden");

        if (our_map && our_map.isLoaded())
        {
            our_map._lastCenter = our_map.getCenter();
            our_map._mapType    = our_map.getCurrentMapType();
            our_map.checkResize();
            our_map.setCenter(our_map._lastCenter);
            our_map.setMapType(our_map._mapType);
        }
    }

    function reset_trails_and_markers(data)
    {
        racer_manager.clearMarkers();
        for (var id in race_data.racers)
        {
            race_data.racers[id].head_marker        = null;
            race_data.racers[id].polyline_length    = -1;
            race_data.racers[id].encoded_points     = '';
            race_data.racers[id].zoom_levels        = '';
        }
    }

    //Map Loading / Initing
    function initMap()
    {
        resize_map_div();
        our_map             = new GMap2(document.getElementById(opts.map_location));
        our_map.setCenter(new GLatLng(opts.race_settings.start_lat, opts.race_settings.start_lon), opts.race_settings.start_zoom);
        our_map.setUIToDefault();
        // GEvent.addListener( our_map, "mouseover", onPolylineMouseOver);
        // GEvent.addListener( our_map, "mouseout", onPolylineMouseOut);
        GEvent.addListener( our_map, "mousemove", onMapMouseMove);
        // console.log( ' Using Google Maps Version ' +  G_API_VERSION );
        if (opts.map_type   == 'basic')
        {
            our_map.setMapType(G_PHYSICAL_MAP);
        } else {
            our_map.setMapType(G_HYBRID_MAP);
        }
        racer_manager       = new MarkerManager(our_map, {borderPadding: 50, trackMarkers: true});
        checkpoint_manager  = new MarkerManager(our_map, {borderPadding: 50, trackMarkers: false});
        // setTimeout('window.resizeBy(1,1);', 1000);
    }

    //Course Loading / Initing
    function loadCourse()
    {
        var race_course = new GGeoXml( opts.race_settings.course_url);
        our_map.addOverlay(race_course);
        /*
        $.ajax({
            type: "GET",
            dataType: "json",
            url: opts.race_settings.course_url,
            success: function(data, message) {
                var encodedCourse   = new GPolyline.fromEncoded({
                    color: opts.course_settings.color,
                    weight: opts.course_settings.weight,
                    opacity: opts.course_settings.opacity,
                    points: data.encoded_points,
                    levels: data.encoded_levels,
                    zoomFactor: 10,
                    numLevels: 4
                });

                our_map.addOverlay(encodedCourse);
                our_map.setCenter(new GLatLng(31.23, -115.1));
            },
            error: function(our_request, our_status, our_error) {
                alert("Error: " + our_error);
            }
        });
        */
    }

    function loadCheckpoints()
    {
        var checkpoint_url  = opts.urls.checkpoint_url_1 + opts.race_id + opts.urls.checkpoint_url_2;
        $.ajax({
            type: "GET",
            dataType: "json",
            url: checkpoint_url,
            success: function(data, message) {
                createCheckpointTableAndMarkers(data);
                // zoom to the whole course when the race starts
                // reset_view('course');
            },
            error: function(our_request, our_status, our_error) {
                alert("We've encountered an error retrieving checkpoint data.  Error: " + our_error + "Request: " + our_request + "Status: " + our_status);
            }
        });
    }
    function get_buoys()
    {
        // Get the buoy marker positions
        var telem_url   = '';
        right_now = Math.round(race_time / 1000 / 60) * 60;
        telem_url   = '/api/2009-04-16/where_was/' + buoy_data_id + '/' + 'aabb' + '/at/' + right_now + '/as/json';
        $.ajax({
            type: "GET",
            dataType: "json",
            url: telem_url,
            async: false,
            success: on_buoy_data,
            error: on_buoy_error
        });
    }
    function on_buoy_data(data, message)
    {
        // alert('Buoy Data obtained!');
    
    }
    function on_buoy_error(our_request, our_status, our_error)
    {
    }

    function update_trails(data)
    {
        // append the last received point to the existing trail instead of asking for all the points again
        // console.log('Updating trails!');
        for (var id in data)
        {
            // create_points(data[id], id);
            if ((undefined !== race_data.polyline[id]) &&  (null !== race_data.polyline[id])) 
            {
                // console.log('add to new trail');
                vertcount = race_data.polyline[id].getVertexCount();
                // console.log('vertext count is ' + vertcount);
                last_vert = race_data.polyline[id].getVertex(vertcount - 1);
                // console.log('last_vertex = (' + last_vert.lat() + ',' + last_vert.lng() + ')');
                if ((undefined !== data[id].pos) && (null !== data[id].pos))
                {
                    if (data[id].pos.length == 1)
                    {
                        // console.log('Last POINT = (' + data[id].pos[0].lat + ',' + data[id].pos[0].lon + ')' );
                        if ((data[id].pos[0].lat - last_vert.lat() > 0.00001) || (data[id].pos[0].lon - last_vert.lng() > 0.00001))
                        {
                            // console.log('APPEND NEW POINT');
                            var new_pt = new GLatLng( data[id].pos[0].lat, data[id].pos[0].lon );
                            race_data.polyline[id].insertVertex(vertcount, new_pt );
                        }
                    }
                }
            }
        }
    }
    function get_telemetry(use_trails )
    {
        right_now = Math.round(new Date().getTime() / 1000 / 60) * 60 ;
        //if (!race_data.initial_load_completed)
        //telem_url   = '/api/2009-04-16/where_was/' + opts.view_id + '/' + racer_watch_list + '/at/' + right_now + '/as/json3';
        telem_url   = '/api/2009-04-16/where_was/' + opts.view_id + '/' + racer_watch_list + '/at/' + right_now + '/as/json4';

        $.ajax({
            type: "GET",
            dataType: "json",
            url: telem_url,
            async: false,
            success: function(telem_data, message) {
                update_racers_table(telem_data);
                if (position_data_changed)
                {
                    display_racers(telem_data);
                    // get_trails();
                    update_trails(telem_data);
                    position_data_changed = false;
                }
                if (race_data.initial_load_completed)
                {
                    // console.log('reset_view A');
                    // reset_view('racers');
                } else {
                    setup_configuration();
                    $('.watch_cb').attr('checked', true);
                    $('.view_cb').attr('disabled', false).attr('checked', false);

                    if (race_data.trail_length  == -1)
                    {
                        //console.log('reset_view B');
                        // reset_view('racers');
                    }
                    race_data.initial_load_completed    = true;
                    if (show_age)
                    {
                        updateAge();
                    }
                    // $('#play_pause').click();
                    // console.log('reset_view C');
                    // reset_view('racers');
                }
            },
            error: function(our_request, our_status, our_error) {
                var alert_string    = "We've encountered an error retrieving the latest telemetry.  Unfortunately, this means you may not be able to watch the race at this time.";
                if (race_data.initial_load_completed)
                {
                    alert_string   += "We'll try again, but if you continue to receive this warning, you can try refreshing the page.  We're sorry.";
                } else {
                    alert_string   += "You can try refreshing the page, but if this continues, you may not be able to watch at all.  Please go INSERT LOCATION HERE to read our troubleshooting FAQ.";
                }
                alert_string        += "The error we received was: " + our_error;
                // People sometimes have connection problems.  
                // alert(alert_string);
           }
       });
    };

    function createRaceIcon(data, id)
    {
        if (undefined === data.pos)
        {
            return;
        }
        var racer_pos = data.pos;
        var racer_info = race_data.racers[id];
        var point           = new GLatLng(racer_pos[0].lat,racer_pos[0].lon);
        var racer_icon      = determine_image(racer_pos[0].hd, racer_info);
        var marker_opts     = {icon:racer_icon, title:racer_info.n};
        var marker          = new GMarker(point, marker_opts);
        
        var popup_html      = buildPopup(racer_pos[0], racer_info, id);
        marker._popup_html  = popup_html;
        marker.bindInfoWindowHtml(popup_html);
        if ((undefined != race_data.racers[id].head_marker) && (null != race_data.racers[id].head_marker))
        {
            // our_map.removeOverlay( race_data.racers[id].head_marker );
            race_data.racers[id].head_marker.setPoint( point );
            race_data.racers[id].head_marker.bindInfoWindowHtml(popup_html);
            if (show_labels)
            {
                racer_manager.removeMarker( race_data.racers[id].label );
                race_data.racers[id].label.setPoint( point );
            }
        } else {
            if (show_labels)
            {
                var label =  new ELabel(point, racer_info.n, 'boat_name_label', new GSize(16, 0) );
                race_data.racers[id].label = label;
            }
            race_data.racers[id].head_marker = marker;
        }
    }
    function display_racers(data)
    {
        if (race_data.trail_length  == 0 || race_data.sliding_stopped)
        {
            // console.log('resetting trail data from display_racers functions');
            reset_trails_and_markers(data);
        }

        for (var id in data)
        {
            // create_points(data[id], id);
            createRaceIcon(data[id],id);
            racer_manager.addMarkers([race_data.racers[id].head_marker], 0);
            if (show_labels )
            {
                racer_manager.addMarkers([race_data.racers[id].label], 0);
            }
        }
        racer_manager.refresh();

        if (race_data.sliding_stopped)
        {
            race_data.sliding_stopped = false;
        }
    };
    function zoom_to_racers()
    {
        // our_map.setZoom(8);
        // Hack - have to call this twice to get it to work
        zoom_to_racers_fn();
        zoom_to_racers_fn();
    }
    function zoom_to_racers_fn()
    {
        var watch_bounds = null;
        zoom_list = racer_view_list;
        for(var idx in zoom_list)
        {
            id = zoom_list[idx];
            if (race_data.racers[id] !== undefined)
            {
            
                if ((race_data.racers[id].head_marker !== undefined) && (race_data.racers[id].head_marker !== null))
                {
                    zoom_marker = race_data.racers[id].head_marker;
                } else {
                    // console.log('The Head Marker for this racer is undefined');
                }
            } else {
                // console.log('This racer is undefined, id = ' + id);
            }
            if (undefined !== zoom_marker) 
            {
                if (null !== zoom_marker)
                {
                    watch_latlng    = zoom_marker.getLatLng();
                    if (null === watch_bounds)
                    {
                        watch_bounds    = new GLatLngBounds(watch_latlng, watch_latlng);
                    } else {
                        watch_bounds.extend(watch_latlng);
                    }
                }
            }
        }
        if (null !== watch_bounds )
        {
            var bounds_center   = watch_bounds.getCenter();
            var bounds_zoom     = our_map.getBoundsZoomLevel(watch_bounds);
            // console.log('Zooming to level ' + bounds_zoom );
            our_map.setCenter(bounds_center, bounds_zoom);
        } else {
            // console.log('Watch Bounds is NULL!!');
        }
    }

    function zoom_to_course()
    {
        var watch_bounds = null;
        zoom_list = race_data.checkpoints;
        for (var id in zoom_list)
        {
            zoom_marker = zoom_list[id];
            if (null !== zoom_marker)
            {
                watch_latlng    = zoom_marker.getLatLng();
            }
            if (null    === watch_bounds)
            {
                watch_bounds    = new GLatLngBounds(watch_latlng, watch_latlng);
            } else {
                watch_bounds.extend(watch_latlng);
            }
        }
        if (null !== watch_bounds )
        {
            var bounds_center   = watch_bounds.getCenter();
            var bounds_zoom     = our_map.getBoundsZoomLevel(watch_bounds);
            // console.log('Zooming to level ' + bounds_zoom );
            our_map.setCenter(bounds_center, bounds_zoom);
        } else {
            // console.log('Watch Bounds is NULL!!');
        }
    }

    function reset_view(type)
    {
        if ( 'racers' == type)
        {
            zoom_to_racers();
        } else {
            zoom_to_course();
        }
    }


    function buildPopup(racer_pos_info, info, id)
    {
        var description = '<div class="popup"><table cellspacing="0", cellpadding="0">';
        description += '<tr><th> #' + info.num + ' ' + info.n + '</th></tr>';
        description += '<tr><th align="right">Class:</th><td>' + info.cls + '</td></tr>';
        description += '<tr><th align="right">Miles to Finish:</th><td class="rm' + id + '">' + racer_pos_info.rm + 'nm</td></tr>';
        description += '<tr><th align="right">Latitude:</th><td>' + getLatitudeInDecimalMinutes(racer_pos_info.lat) + '</td></tr>';
        description += '<tr><th align="right">Longitude:</th><td>' + getLongitudeInDecimalMinutes(racer_pos_info.lon) + '</td></tr>';
        if ( D.show_altitude && racer_pos_info.alt )
        {
          var elev = 'unkown';
          elev = racer_pos_info.alt;
          if (elev >0.5500)
          {
              elev = elev -0.5535;
          }
          description += '<tr><th align="right">Altitude:</th><td>' + elev + ' meters</td></tr>';
        }
        if ('baja' == opts.map_type)
        {
            var mph      = knotsToMPH((racer_pos_info.kts - 0).toFixed(1));
            var kph      = knotsToKPH((racer_pos_info.kts - 0).toFixed(1));
            description += '<tr><th align="right">Speed:</th><td>' + mph.toString() + ' MPH/' + kph.toString() + ' KPH</td></tr>';
        } else {
            description += '<tr><th align="right">Speed:</th><td>' + (racer_pos_info.kts - 0).toFixed(1).toString() + ' KTS</td></tr>';
        }
        description += '<tr><th align="right">CoG(t)</th><td>' +  racer_pos_info.hd + '&deg; ' + formatHeading(  racer_pos_info.hd ) + '</td></tr>';
        if ( D.show_temp )
        {
            description += '<tr><th align="right">Temperature</th><td>' + celsiusToFahrenheit(racer_pos_info.c) + '&deg;F / ' + racer_pos_info.c  + '&deg;C</td></tr>';
        }
        if (show_status)
        {
            description += '<tr><th align="right">Status:</th><td>' + racer_pos_info.st + '</td></tr>';
        }
        // description += '<td id="' + info.id + '_r_time" class="r_time">' + racer_pos_info.t + '</td></tr>';
        description += '<tr><th align="right">Boat:</th><td>' + info.boat + '</td></tr>';
        if (show_loa)
        {
            description += '<tr><th align="right">LOA:</th><td>' + info.ft + ' ft</td></tr>';
        }
        if (show_skipper)
        {
            description += '<tr><th align="right">Skipper:</th><td>' + info.skipper + '</td></tr>';
        }
        if (show_handicap)
        {
            description += '<tr><th align="right">Handicap Method:</th><td>' + info.hname + '</td></tr>';
            description += '<tr><th align="right">Handicap Rating:</th><td>' + info.hval + '</td></tr>';
        }
        if (show_age)
        {
            description += '<tr class="show_age"><th align="right">Time Since Report:</th><td class="' + info.id + '_time age"></td></tr>';
        }
        description += '<tr><th align="right">Time Reported:</th><td class="reported_' + info.id + '">'+ dateFormat( new Date( racer_pos_info.t * 1000), "mmm dd h:MM:ss TT Z" )  +'</td></tr>';
        description += '</table>';
        if (racer_pos_info.thm >= ' ')
        {
            description += '<img class="mugshot" align="left" src="' + info.thm + '" alt="' + info.n + '">';
        }
        description += '<div class="pop_desc">' + info.htm + '</div></div>';

        return description;
    };

    function determine_image(racer, info)
    {
        var imgHeading  = null;
        var point_icon  = new GIcon();

        if (info.rot == 'False')
        {
            point_icon.image    = info.i;
            point_icon.iconAnchor       = new GPoint(info.ix,info.iy);
            point_icon.infoWindowAnchor = new GPoint(info.ix,info.iy);
        } else {
            imgHeading  = ionMath_mod(Math.round((360 + racer) / 10) * 10, 360);
            imgHeading  = imgHeading.toString();
            while (imgHeading.length < 3)
            {
                imgHeading = '0' + imgHeading;
            }
            point_icon.image               = info.i + imgHeading + '.png';
            point_icon.iconAnchor          = new GPoint(info.ix,info.iy);
            point_icon.infoWindowAnchor    = new GPoint(info.ix,info.iy);
        }

        return point_icon;
    };

    function createCheckpointTableAndMarkers(checkpoint_data)
    {
        var num_checkpoints = checkpoint_data.length;
        var table_rows      = '';
        if (num_checkpoints > 0)
        {
            checkpoint_data.sort(sortOrder);

            for (x = 0; x < num_checkpoints; x++)
            {
                var checkpoint              = checkpoint_data[x];
                var point                   = new GLatLng(checkpoint.lat, checkpoint.lon);
                var point_icon              = new GIcon();
                if (checkpoint.img != '')
                {
                    point_icon.image            = checkpoint.img;
                } else {
                    point_icon.image            = opts.race_settings.checkpoint_icon;
                }
                var offset_x = 5;
                var offset_y = 5;
                if (checkpoint.offx > '0') 
                {
                    offset_x = checkpoint.offx * 1;
                }
                if (checkpoint.offy > '0') 
                {
                    offset_y = checkpoint.offy * 1;
                }
                point_icon.iconAnchor       = new GPoint(offset_x, offset_y);
                point_icon.infoWindowAnchor = new GPoint(offset_x, offset_y);
                var marker_opts             = {icon:point_icon};
                var marker_html             = '<div class="checkpoint_title">' + checkpoint.name + '</div>';
                    marker_html            += '<table cellspacing="0", cellpadding="0"><thead></thead><tbody><tr><th>Latitude:</th>';
                    marker_html            += '<td>' + getLatitudeInDecimalMinutes(checkpoint.lat) + '</td></tr><th>Longitude:</th>';
                    marker_html            += '<td>' + getLongitudeInDecimalMinutes(checkpoint.lon) + '</td></tr><th>Description:</th>';
                    marker_html            += '<td>' + checkpoint.desc + '</td></tr></tbody></table>';
                var marker                  = new GMarker(point, marker_opts);
                marker._popup_html          = marker_html;
                marker.bindInfoWindowHtml(marker_html);

                race_data.checkpoints.push(marker);
                // The addMarkers function takes a MINIMUM zoom level causing the map to 
                // not show the icons at all unless the map is at our desired zoom level
                checkpoint_manager.addMarkers([marker],1 ); // checkpoint.zoom);

                table_rows                 += '<tr id="checkpoint_' + x + '" class="checkpoint_item"><td>' + checkpoint.name + '</td>';
                table_rows                 += '<td align="right">' + checkpoint.rm + '</td></tr>';
            }
            $('#checkpoint_table tbody').append(table_rows);
            checkpoint_manager.refresh();
            
            // This zooms the map to the specified checkpoint when the name of the checkpoint 
            // is clicked in the checkpoint table
            $('.checkpoint_item').click(function() {
                var id          = $(this).attr('id');
                var id_num      = id.split('_');
                var our_id      = id_num[1];
                var marker      = race_data.checkpoints[our_id];
                if (null != marker)
                {
                    our_map.panTo(marker.getLatLng());
                    our_map.openInfoWindowHtml(marker.getLatLng(),marker._popup_html);
                } else {
                    our_map.openInfoWindowHtml(our_map.getCenter(),"<div>No data for this checkpoint at this time</div>");
                }
            });

            $('.checkpoint_item').hover(
            function(){
                $(this).addClass('ui-state-hover');
            },
            function(){
                $(this).removeClass('ui-state-hover');
            });
        }
        zoom_to_course();
    };

    function loadRacers()
    {
        var racer_url   = opts.urls.racer_url_1 + opts.view_id + opts.urls.racer_url_2;
        // var classes_url = 'http://race.ionearth.com/api/2009-04-16/get_view_classes/' + opts.view_id + '/as/json';
        var classes_url = '/api/2009-04-16/get_view_classes/' + opts.view_id + '/as/json';

        $.ajax({
            type: "GET",
            dataType: "json",
            url: classes_url,
            async: false,
            success: function (classes, message) {
                race_data.race_classes  = classes;
            },
            error: function (our_request, our_status, our_error) {
                // alert("Error Occurred!  Unfortunately, this likely means you won't be able to view the race.  Error was:" + our_error);
            }
        });

        $.ajax({
            type: "GET",
            dataType: "json",
            url: racer_url,
            async: false,
            success: function(racers, message) {
                var racer_ids   = '';
                for (i=0; i < racers.length; i++)
                {
                    var racer_id                                    = racers[i].id;
                    var trail_color                                 = parseInt(racers[i].r).toString(16) + parseInt(racers[i].g).toString(16) + parseInt(racers[i].b).toString(16);
                    racer_ids                                      += racer_id;
                    race_data.racers[racer_id]                      = {};
                    race_data.racers[racer_id].default_trail_color  = trail_color;
                    race_data.racers[racer_id].trail_color          = trail_color;

                    $.extend(race_data.racers[racer_id], racers[i]);
                    racer_view_list.push(racer_id);
                    racer_watch_array.push(racer_id);
                }
                racer_watch_list        = racer_ids;
            },
            error: function (our_request, our_status, our_error) {
                alert("Error Occurred!  Unfortunately, this likely means you won't be able to view the race.  Error was:" + our_error);
            }
        });
    };

    function setup_configuration()
    {
        var num_racers          = race_data.racers.length;
        var accordion_html      = '<div id="config_accordion">';
        var explanation_html    = '';

        for (var r_class in race_data.race_classes)
        {
            var class_name       = race_data.race_classes[r_class].toString();
            var class_name_id    = class_name.replace(/ /g, '_');
            accordion_html      += '<h3><a href="#">' + class_name + '</a></h3><div id="class_' + class_name_id + '">';
            accordion_html      += '<p>Which Boats would you like to watch in this race?</p>'
            accordion_html      += '<table cellspacing="0", cellpadding="0"><thead><tr><th>Icon</th><th width="20%">' + opts.race_settings.racer_name + '</th>'
            if (show_skipper)
            {
                accordion_html      += '<th>Skipper</th>';
            }
            accordion_html      += '<th>(ToD)</th>';
            accordion_html      += '<th width="20%">Display</th><th width="20%">Show Trails</th></tr></thead><tbody></tbody></table></div>';
        }
        if ((1 == 2) && ('baja' != opts.map_type))
        {
            explanation_html          += '<span class="trail_length">Select Trail Length:</span><select class="trail_length" id="trail_length"><option value="-1">All Positions</option>';
            explanation_html          += '<option value="0">Current Position Only</option></select>';
        }

        explanation_html          += '<div>Maps will resize automatically so as to always include all items marked as "Always on the Map"</div></div>';

        $('#configuration').prepend(accordion_html);
        $('#config_dialog').append(explanation_html);
        $('#config_accordion').accordion({
            header: 'h3',
            autoHeight: false
        });

        for (var id in race_data.racers)
        {
            var racer_class          = race_data.racers[id].cls.toString();
            var racer_class_id       = racer_class.replace(/ /g, '_');
            var append_selector      = '#class_' + racer_class_id + ' table tbody';
            var racer_config_info    = '<tr class="config_row">';
            racer_config_info       += '<td><img src="' + race_data.racers[id].i + '090.png"></td>';
            racer_config_info       += '<td align="left">' + race_data.racers[id].n + '</td>';
            if (show_skipper)
            {
                racer_config_info       += '<td>' + race_data.racers[id].skipper + '</td>';
            }
            racer_config_info       += '<td>' + race_data.racers[id].hval + '</td>';

            racer_config_info       += '<td><input class="watch_cb" id="watch' + id + '" type="checkbox" value=watch"' + id + '" name="watch_' + id + '"></td>';
            racer_config_info       += '<td><input class="view_cb" id="view' + id + '" type="checkbox" value="view' + id +'" name="keep_' + id + '_in_view"></td></tr>';

            $(append_selector).prepend(racer_config_info);
        }

        $('.view_cb').attr("disabled", true);

        $('.config_row').hover(
        function(){
            $(this).addClass('ui-state-hover');
        },
        function(){
            $(this).removeClass('ui-state-hover');
        });

        $('.watch_cb').click(function() {
            // Disable the keep in view / show trails checkbox if the boat is not visible
            var watch_id = $(this).attr('id');
            var view_id  = watch_id.replace(/watch/,"view");
            var view_selector   = '#' + view_id;
            if ($(this).attr('checked'))
            {
                $(view_selector).attr('disabled', false);
            } else {
                $(view_selector).attr('disabled', true);
            }
        });
    };

    function set_options(){
        var checkboxes          = $(':checkbox');
        cookie.keep_in_view     = [];
        cookie.watch            = [];
        racer_watch_array       = [];
        racer_view_list         = [];
        if ($('#trail_length option:selected'))
        {
            cookie.trail_length     = $('#trail_length option:selected').val();
        }
        racer_watch_list        = '';

        $.each(checkboxes, function() {
            if (!($(this).attr('disabled')))
            {
                var our_name    = $(this).attr('name');
                var config_info = our_name.split('_');
                var checked     = $(this).attr('checked');

                if (config_info[0] == 'watch' && checked)
                {
                    cookie.watch.push(config_info[1]);
                    racer_watch_list  += config_info[1];
                    racer_watch_array.push(config_info[1]);
                } else if (config_info[0]  == 'keep' && checked) {
                    cookie.keep_in_view.push(config_info[1]);
                    racer_view_list.push(config_info[1]);
                }
            }
        });

        $('.trail_color').each(function() {
            var our_id      = $(this).attr('id');
            var config_info = our_id.split('_');
            var color       = $(this).val();

            if ('N/A' != color)
            {
                cookie.racers[config_info[2]]                    = {};
                cookie.racers[config_info[2]].trail_color        = color;
                race_data.racers[config_info[2]].trail_color     = color;
            }
        });

        var encoded_cookie  = $.toJSON(cookie);
        $.cookie(cookie_name, encoded_cookie, {expires: 7, path: window.location.pathname, domain: 'race.ionearth.com', secure: false});

        race_data.trail_length          = cookie.trail_length;
        race_data.default_trail_length  = cookie.trail_length;
        race_data.sliding_stopped       = true;

        build_racers_table();
        position_data_changed = true;
        get_telemetry();
        $('#play_pause').click();
    };

    function build_racers_accordion()
    {
        var racers_html = '';
        var race_info   = opts.race_settings;
        for (var r_class in race_data.race_classes)
        {
            var class_name       = race_data.race_classes[r_class].toString();
            var class_name_id    = class_name.replace(/ /g, '_');
            racers_html         += '<h3><a href="#">' + class_name + '</a><span class="class_zoom ui-icon-circle-zoomin" alt="Zoom map to this class">&nbsp;</span></h3><div id="' + class_name_id + '_table_container" class="class_accordion">';
            racers_html         += '<table cellspacing="0", cellpadding="0" id="' + class_name_id + '_table"><thead><tr><th align="left">' + race_info.racer_name + '</th><th>&nbsp;Kts</th><th>&nbsp;' + race_info.race_mile_name + '</th>';
		// racers_html         += '<th class="show_age">&nbsp;Age</th>';
        racers_html     += '<th class="show_trails"><span id="show_trails_for_class_' + class_name_id + '">Trails</span></th>';
	    racers_html		+= '</tr></thead>';
            racers_html         += '<tbody></tbody></table><div id="none_for_' + class_name_id + '"> You have made no selections for this class.  To make selections for this class, click the "Configure Viewing Options" button above.</div></div>';
        }
        $('#results_tables').append(racers_html);
        $('#results_tables').accordion({
            header: 'h3',
            autoHeight: false
        });
        $('#results_tables').accordion('activate', 0);
        toggle_age_of_data();
    }
    function make_select_all_trails_button()
    {
    }


    function build_racers_table(data)
    {
        for (var class_id in race_data.race_classes)
        {
            var class_name           = race_data.race_classes[class_id];
            var class_name_id        = class_name.replace(/ /g, '_');
            var table_body_selector  = '#' + class_name_id + '_table tbody';
            var table_selector       = '#' + class_name_id + '_table';
            var none_selector        = '#none_for_' + class_name_id;
            $(table_body_selector).empty();
            $(table_selector).hide();
            $(none_selector).show();
        }

        for (var watch_id in racer_watch_array)
        {
            var id                    = racer_watch_array[watch_id];
            if (race_data.racers[id])
            {
                var class_name = race_data.racers[id].cls;
                var class_name_id         = class_name.replace(/ /g, '_');
                var table_body_selector   = '#' + class_name_id + '_table tbody';
                var table_selector        = '#' + class_name_id + '_table';
                var none_selector         = '#none_for_' + class_name_id;
                // var table_row_html        = '<tr id="' + id + '" class="select_racer"><td>' + race_data.racers[id].num + '</td><td>' + race_data.racers[id].n + '</td>';
                var table_row_html        = '<tr id="' + id + '" class="select_racer"><td>' + race_data.racers[id].n + '</td>';
                table_row_html       += '<td id="' + id + '_speed" align="right"></td>';
                table_row_html       += '<td id="' + id + '_rm" align="right"></td>';
                // table_row_html       += '<td class="show_age age ' + id + '_time"></td>';
                table_row_html          += '<td ><input class="show_trail" type="checkbox" id="show_trail_' + id + '" ></td>'; 
                // table_row_html       += '<td id="' + id + '_r_time" class="r_time"></td></tr>';

                $(table_body_selector).append(table_row_html);
                $(table_selector).show();
                $(none_selector).hide();
                jQuery('#show_trail_' + id ).click(toggle_trails);
            }
        }
        $('.select_racer').click(function() {
            var id      = $(this).attr('id');
            var marker  = race_data.racers[id].head_marker;

            if ((undefined !== marker ) && (null !== marker))
            {
                our_map.panTo(marker.getLatLng());
                our_map.openInfoWindowHtml(marker.getLatLng(),marker._popup_html);
            } else {
                // our_map.openInfoWindowHtml(our_map.getCenter(),"<div>No data for this point at this time</div>");
            }
            if ((undefined !== race_data.polyline[id]) && (null !== race_data.polyline[id]))
            {
                highlightPolyline( id );
            }
            updateAge();

        });

        $('.select_racer').hover(
            function() {
                $(this).addClass('ui-state-hover');
            },
            function() {
                $(this).removeClass('ui-state-hover');
            }
        );
    }
    function toggle_trails(evt, obj)
    {
        boat_id = this.id.substr(this.id.length - 2);
        // console.log('boat id is ' + boat_id);
        if (this.checked)
        {
            // add boat to trail list
            show_trail_id_list.push(boat_id);
            get_trail(boat_id);
        } else {
            // remove boat from trail list
            found_it = -1;
            for (var ix=0; ix < show_trail_id_list.length && -1 == found_it; ix++)
            {
                if (show_trail_id_list[ix] == boat_id)
                {
                    found_it = ix;
                }
            }
            if (-1 < found_it)
            {
                // remove the found item from the list
                show_trail_id_list.splice(found_it, 1);
            }
            remove_trail(boat_id);
            
        }
    }
    function on_get_trails(data, message)
    {   
        //console.log('DATA RECEIVED');
        //console.log(data);
        // console.log(message);
        if (message != 'success')
        {
            return;
        }
        position_data_changed = false;
        display_trails(data);
    }
    var highlighted_line = null;
    function onPolylineMouseOver(evt)
    {
        highlightPolyline( this.ionearth_racer_id );
        if (null === trail_label)
        {
            trail_label = new ELabel(new GLatLng( map_mouse_lat, map_mouse_lng ), race_data.racers[racer_id].n, "trail_label", new GSize( 6, 0) );
            our_map.addOverlay( trail_label );
        } else {
            trail_label.show();
            trail_label.setPoint( new GLatLng( map_mouse_lat, map_mouse_lng ) );
            trail_label.setContents( race_data.racers[racer_id].n );
        }
        // console.log('Mouse Over Line');
    }
    function highlightPolyline(racer_id)
    {
        // console.log('Highlighting line ' + racer_id );
        if (null !== highlighted_line )
        {
            unhighlightPolyline( highlighted_line );
        }
        if ((undefined === race_data.polyline[racer_id]) || (null === race_data.polyline[racer_id]))
        {
            highlighted_line = null;
            return;
        }
        highlighted_line = racer_id;
        if (null === polyline_mousemove_handler)
        {
            polyline_mousemove_handler = GEvent.addListener( our_map, "mousemove", onPolylineMouseMove);
        }
        race_data.polyline[racer_id].last_weight = race_data.polyline[racer_id].weight;
        race_data.polyline[racer_id].last_opacity = race_data.polyline[racer_id].opacity;
        race_data.polyline[racer_id].setStrokeStyle( {'weight': race_data.polyline[racer_id].opacity + polyline_weight_highlight_weight, 'opacity': opacity_solid } );
        if (null === trail_label)
        {
            trail_label = new ELabel(new GLatLng( map_mouse_lat, map_mouse_lng ), race_data.racers[racer_id].n, "trail_label", new GSize( 6, 0) );
            our_map.addOverlay( trail_label );
        } else {
            trail_label.show();
            trail_label.setPoint( new GLatLng( map_mouse_lat, map_mouse_lng ) );
            trail_label.setContents( race_data.racers[racer_id].n );
        }
        // console.log('Mouse Over Line');
    }
    function onPolylineMouseOut(evt, obj)
    {
        unhighlightPolyline( this.ionearth_racer_id );
    }
    function unhighlightPolyline( racer_id )
    {
        // console.log('UN-Highlighting line ' + racer_id );
        if ((undefined === race_data.polyline[racer_id]) || (null === race_data.polyline[racer_id]))
        {
            highlighted_line = null;
            return;
        }
        race_data.polyline[racer_id].setStrokeStyle( {'weight': race_data.polyline[racer_id].last_weight, 'opacity': race_data.polyline[racer_id].last_opacity } );
        GEvent.removeListener( polyline_mousemove_handler );
        polyline_mousemove_handler = null;
        //our_map.removeOverlay( trail_label );
        //trail_label = null;
        if (null !== trail_label)
        {
            trail_label.hide();
        }
        highlighted_line = null;
        // console.log('Mouse Out Line');
    }
    function onPolylineMouseMove(evt, obj)
    {
        trail_label.setPoint( new GLatLng(evt.y, evt.x) );
        // console.log('mouse is moving ' + evt.lat() + ',' + evt.lng() );
    }
    function onMapMouseMove(evt)
    {
        map_mouse_lat = evt.y;
        map_mouse_lng = evt.x;
    }
    var polylineEncoder = new PolylineEncoder();
    function display_trails(data)
    {
        // Wipe out existing trails and redraw them
        // reset_trails_and_markers();  // do whatever this does
        // console.log('drawing trails');
        var use_trails = false;
        var use_points = true;
        for (var id in data)
        {
            race_data.racers[id].trail_data = data[id];
            if (use_trails)
            {
                try 
                {
                    // console.log('encoding polyline');
                    var new_polyline = new GPolyline.fromEncoded( data[id].trail );
                    // console.log( 'Adding polyline to the map');
                    our_map.addOverlay( new_polyline );
                    GEvent.addListener( new_polyline, "mouseover", function() { console.log(' Mouse is over the line ' ); } );
                    if ((undefined !== race_data.polyline[id]) &&  (null !== race_data.polyline[id]))
                    {
                        // console.log( 'removing old polyline from the map');
                        our_map.removeOverlay(race_data.polyline[id]);
                    }
                    // console.log( 'adding new polying to the data array' );
                    race_data.polyline[id] = new_polyline;
                } catch( myError ) {
                    console.log('Error in display_trails - showing encoded polyling ' + myError );
                } finally {
                }
            }
            /***
                    color: '#FF0000',
                    weight: data[id].sz,
                    opacity: 0.8,
                    points: encoded_points,
                    zoomFactor: 16,
                    levels: polyLevels,
                    numLevels: 4
                }
            );
            **/
            if (use_points)
            {
                var points = [];
                for (var x = 0; x<data[id].pos.length; x++)
                {
                    points[x] = new GLatLng(data[id].pos[x].lat, data[id].pos[x].lon);
                }
                var polyline = polylineEncoder.dpEncodeToGPolyline(points);
                polyline.weight = data[id].sz;
                polyline.color = '#' + data[id].color;
                polyline.opacity = 0.8;
                
                GEvent.addListener( polyline, "mouseover", onPolylineMouseOver );
                GEvent.addListener( polyline, "mouseout", onPolylineMouseOut );
                polyline.ionearth_vehicle_name = race_data.racers[id].n;
                polyline.ionearth_racer_id = id;
                if (undefined != race_data.polyline[id])
                {
                    our_map.removeOverlay(race_data.polyline[id]);
                }
                our_map.addOverlay(polyline);
                race_data.polyline[id] = polyline;
            }
            // console.log('item ' + id + ' name = ' + race_data.racers[id].n);
        }
    }
    function on_got_trail(data)
    {
        // This assumes on_got_trail only has data for a single line
        for (var id in data)
        {
            remove_trail(id);
            display_trails(data);
        }
    }
    function get_trail(boat_id)
    {
        right_now = Math.round(new Date().getTime() / 1000 / 60) * 60;
        trail_start = race_data.telem_start;
        trail_units = boat_id;
        if (undefined !== race_data.racers[boat_id])
        {
            if (race_data.racers[boat_id].offst > '')
            {
                trail_start = race_data.racers[boat_id].offst;
            } 
            if (race_data.racers[boat_id].offfn > '') 
            {
                if (race_data.racers[boat_id].last_time < race_data.racers[boat_id].offfn)
                {
                    right_now = race_data.racers[boat_id].last_time;
                } else {
                    right_now = race_data.racers[boat_id].offfn ;
                }
            }
        }
        if (trail_units.length > 1)
        {
            telem_url = '/api/2009-04-16/sailing_telemetry/race/' + opts.view_id + '/units/' + trail_units + '/from/' + trail_start + '/to/' + right_now + '/as/json3';
            $.ajax({
                type: "GET",
                dataType: "json",
                url: telem_url,
                async: false,
                success: on_got_trail
            });
        }
    }
    function remove_trail(id)
    {
            if ((undefined !== race_data.polyline[id]) &&  (null !== race_data.polyline[id]))
            {
                // console.log( 'removing old polyline from the map');
                our_map.removeOverlay(race_data.polyline[id]);
            }
            race_data.racers[id].trail_data = null;
        
    }
    function get_trails()
    {
        // console.log('GETTING TRAILS -- WOOHOO!');
        // Download trails for all the boats in the land...
        right_now = Math.round(new Date().getTime() / 1000 / 60) * 60;
        trail_start = 1246305600;
        //  trail_start = right_now - 3600 * 22;
        //trail_units = racer_view_list.join('');
        trail_units = show_trail_id_list.join('');
        if (trail_units.length > 1)
        {
            telem_url = '/api/2009-04-16/sailing_telemetry/race/' + opts.view_id + '/units/' + trail_units + '/from/' + trail_start + '/to/' + right_now + '/as/json3';
            $.ajax({
                type: "GET",
                dataType: "json",
                url: telem_url,
                async: false,
                success: on_get_trails
            });
        }
    }

    function update_racers_table(data)
    {
        // popuplate the racer table with the latest data
        for (var id in data)
        {
            var speed_selector  = '#' + id + '_speed';
            var rm_selector     = '#' + id + '_rm';
            // var time_selector   = '#' + id + '_r_time';
            var data_length     = data[id].pos.length;
            if ( race_data.racers[id]['last_time'] != data[id].pos[data_length - 1].t)
            {
                position_data_changed = true;
            }
            race_data.racers[id].last_time = data[id].pos[data_length - 1].t;
            var spd = data[id].pos[data_length - 1].kts;
            jQuery('.reported_' + id).text( dateFormat( new Date( race_data.racers[id].last_time * 1000) , "mmm dd h:MM:ss TT Z" )  );
            $(speed_selector).text((spd - 0).toFixed(1).toString());
            $(rm_selector).html("&nbsp;" + data[id].pos[data_length - 1].rm + "&nbsp;");
            // $(rm_selector).text("&nbsp;" + data[id].pos[data_length - 1].rm + "&nbsp;");
        }
    }

    // Helper Functions
    function sortOrder(a,b)
    {
        try { return a.so - b.so; } catch(e) { }
        //try { return a._order_by - b._order_by; } catch(e) { };
    }

    // Due to what appears to be a bug in webkit, need to specify the height/width of the header
    // and nav divs since we do know them at the moment.
    function get_map_height()
    {
        //return ($(window).height() - $('#header').height() + 20);
        if (jQuery.browser == 'safari')
        {
        }
        return ($(window).height() - 102 - 30 + 20);
    }

    function get_map_width()
    {
        // return ($(window).width() - $('#left_nav').width() + 13);
        var scroll_bar_width = 16;
        return ($(window).width() - $('#left_nav').width() + scroll_bar_width);
        // return ($(window).width() -0.56 + 13);
        // Why is the magic numbers0.56 and 13 ?
        // return ($(window).width() - left_nav_width );
    }
    // Conversion Functions
    convert = function()
    {
        function celsiusToFahrenheit(tempC)
        {
            return Math.round(((212-32) / 100 * tempC + 32), 2);
        }

        function knotsToMPH(knots)
        {
            var mph = 0;
            if (knots >0.5)
            {
                mph = Math.round(knots * 1.15077945, 1);
            } else {
                mph = (Math.round(knots * 1.15077945 * 10, 1) / 10);
            }

            return mph;
        }
        function knotsToKPH(knots)
        {
            var kph = 0;
            if (knots >0.5)
            {
                kph = Math.round(knots * 1.85200, 1);
            } else {
                kph = (Math.round(knots * 1.85200 * 10, 1) / 10);
            }
            return kph;
        }
    };

    function formatHeading(degrees)
    {
        var rose    = opts.rose || $.ionearth.defaults.rose;
        var seg = 360/rose.length;
        var deg = degrees + seg/2;
        var deg360 = ionMath_Mod(deg,360);
        var dir = deg360 / seg;
        dir = Math.round(dir-0.5,0);
        return rose[dir];
    }

    function ionMath_mod(X,Y)
    {
        var t;
        t = X % Y;
        return t < 0 ? t + Y : t;
    }

    function ionMath_Mod(X,Y)
    {
        return X - Math.floor(X / Y) * Y;
    }


    function degreesToDecimalMinutes(degree,orientation)
    {
        if("NorthSouth" == orientation)
        {
            direction   = ['N','S'];
        } else {
            direction   = ['E','W'];
        }

        var current_direction   = direction[0];

        if (degree < 0)
        {
            current_direction   = direction[1];
            degree              = degree * -1;
        }

        var deg     = Math.round(degree - 0.5);
        var min     = (degree - deg) * 60;
            min     = Math.round(min * 1000) / 1000;
        var result  = current_direction + ' ' + deg + '&deg;' + min + "'";
        return result;
    }

    function formatDate(date_to_format) {
        var our_date                    = new Date(date_to_format);
        var our_display_date            = day_of_week[our_date.getDay()] + ', ' + month_of_year[our_date.getMonth()];
        our_display_date               += ' ' + our_date.getDate() + ', ' + our_date.getFullYear() + ' ' + sa((our_date.getTime() / 1000), true);

        return our_display_date;
    }
    var last_sa = '';
    var last_sa_t = '';
    function sa(seconds,time_only){
        if (last_sa_t == seconds) {
            return last_sa;
        }
        last_sa_t = seconds;
        var time_seconds        = Math.floor(seconds,1);
        var time_parts          = {};
        var time_conversions    = [{
                                        'num_seconds':604800, //seconds / week
                                        'label':'w'
                                    },
                                    {
                                        'num_seconds':86400,  //seconds / day
                                        'label':'d'
                                    },
                                    {
                                        'num_seconds':3600,   //seconds / hour
                                        'label':'h'
                                    },
                                    {
                                        'num_seconds':60,     // seconds / min
                                        'label':'m'
                                    }];

        for (var x = 0; x < time_conversions.length;x++)
        {
            var seconds_in_timeframe    = time_conversions[x]['num_seconds'];

            if (time_seconds >= seconds_in_timeframe)
            {
                var timeframe_label      = time_conversions[x]['label'];
                var num_timeframes       = Math.floor((time_seconds / seconds_in_timeframe), 1);
                time_seconds            -= num_timeframes * seconds_in_timeframe;
                time_parts[timeframe_label]       = num_timeframes;
            } else {
                time_parts[time_conversions[x]['label']] = 0;
            }
        }

        if (time_parts.h < 10)
        {
            time_parts.h  = '0' + time_parts.h.toString();
        }
        if (time_parts.m < 10)
        {
            time_parts.m  = '0' + time_parts.m.toString();
        }
        if (time_seconds < 10)
        {
            time_seconds = '0' + time_seconds.toString();
        }
        var result = ''
        if (time_parts.w > 0 && !time_only)
        {
            result = time_parts.w + 'w, ' + time_parts.d + 'd, ' + time_parts.h + ':' + time_parts.m + ':' + time_seconds;
        } else if (time_parts['d'] > 0 && !time_only) {
            result = time_parts.d + 'd, ' + time_parts.h + ':' + time_parts.m + ':' + time_seconds;
        } else if (time_parts.h == '00') {
            result = time_parts.m + ':' + time_seconds;
        } else {
            result = time_parts.h + ':' + time_parts.m + ':' + time_seconds;
        }
        last_sa = result;
        return result;
    }

    getLatitudeInDecimalMinutes = function(degree)
    {
        var direction = 'N';
        if (degree < 0)
           {
            direction = 'S';
            degree = degree * -1;
            }
        var deg = Math.round(degree - 0.5);
        var min = (degree - deg) * 60;
            min = Math.round( min * 1000 ) / 1000;
        var result = direction + ' ' + deg + '&deg;' + min + "'";
        return result;
    };

    function getLongitudeInDecimalMinutes(degree)
    {
        var direction = 'E';
        if (degree < 0)
           {
            direction = 'W';
            degree = degree * -1;
            }
        var deg = Math.round(degree - 0.5);
        var min = (degree - deg) * 60;
            min = Math.round( min * 1000 ) / 1000;
        var result = direction + ' ' + deg + '&deg;' + min + "'";
        return result;
    }

// This shows a lat/lon graticule on the map. Interval is automatic
// As first seen at www.bdcc.co.uk
// Bill Chadwick 2006 
//

function LatLonGraticule(sexagesimal) {
    this.sex_ = sexagesimal || false;//default is decimal intervals
}
LatLonGraticule.prototype = new GOverlay();

LatLonGraticule.prototype.initialize = function(map) {

  //save for later
  this.map_ = map;
  //array for divs used for lines and labels
  this.divs_ = [];
      
};

LatLonGraticule.prototype.remove = function() {

    try{
        var i = 0;
        var div = this.map_.getPane(G_MAP_MARKER_SHADOW_PANE);
        for(i=0; i< this.divs_.length; i++)
        {
            div.removeChild(this.divs_[i]);
        }
    } catch(e){
    }

};

LatLonGraticule.prototype.copy = function() {
  return new LatLonGraticule(this.sex_);
}

// Redraw the graticule based on the current projection and zoom level
LatLonGraticule.prototype.redraw = function(force) {

  //clear old
  this.remove();

  //best color for writing on the map
  this.color_ = this.map_.getCurrentMapType().getTextColor();

  //determine graticule interval
  var bnds = this.map_.getBounds();
  
  var l = bnds.getSouthWest().lng();
  var b = bnds.getSouthWest().lat();
  var t = bnds.getNorthEast().lat();
  var r = bnds.getNorthEast().lng();

  //sanity
  if (b < -90.0) {
    b = -90.0; }
  if(t > 90.0) {
    t = 90.0; }
  if(l < -180.0) {
    l = -180.0;  }
  if(r > 180.0) {
    r = 180.0; }
    
  if(l == r){
    l = -180.0;
    r = 180.0;
  }

  if(t == b){
    b = -90.0;
    t = 90.0;
  }

  //grid interval in minutes    
  var dLat = this.gridIntervalMins(t-b);
  var dLng; 
  if(r>l)
    dLng = this.gridIntervalMins(r-l);
  else
    dLng = this.gridIntervalMins((180-l)+(r+180));

  //round iteration limits to the computed grid interval
  l = Math.floor(l*60/dLng)*dLng/60;
  b = Math.floor(b*60/dLat)*dLat/60;
  t = Math.ceil(t*60/dLat)*dLat/60;
  r = Math.ceil(r*60/dLng)*dLng/60;

  //Sanity
  if (b <= -90.0) {
    b = -90;}
  if(t >= 90.0){
    t = 90;}
  if(l < -180.0){
    l = -180.0;  }
  if(r > 180.0){
    r = 180.0;}
    
  //to whole degrees
  dLat /= 60;
  dLng /= 60;
  
  //# digits after DP for decimal labels
  var latDecs = this.gridPrecision(dLat);
  var lonDecs = this.gridPrecision(dLng);
  
  this.divs_ = [];
  var i=0;//count inserted divs

  //min and max x and y pixel values for graticule lines
  var pbl = this.map_.fromLatLngToDivPixel(new GLatLng(b,l));
  var ptr = this.map_.fromLatLngToDivPixel(new GLatLng(t,r));
  
  this.maxX = ptr.x;
  this.maxY = pbl.y;
  this.minX = pbl.x;
  this.minY = ptr.y;
  
  var x;//coord for label
  //labels on second column to avoid peripheral controls
  var y = this.map_.fromLatLngToDivPixel(new GLatLng(b+dLat+dLat,l)).y + 2;//coord for label
  
  //pane/layer to write on
  var mapDiv = this.map_.getPane(G_MAP_MARKER_SHADOW_PANE);
  
  var lo = l;//copy to save original
  
  if(r<lo)
      r += 360.0;

  //vertical lines
  while(lo<=r){

    var p = this.map_.fromLatLngToDivPixel(new GLatLng(b,lo));

    //line
    this.divs_[i] = this.createVLine(p.x);
    mapDiv.insertBefore(this.divs_[i],null);
    i++;
    
    //label  
    var d = document.createElement("DIV");
    x = p.x + 3;
    d.style.position = "absolute";
    d.style.left = x.toString() + "px";
    d.style.top = y.toString() + "px";
    d.style.color = this.color_;
    d.style.fontFamily='Arial';
    d.style.fontSize='x-small';
    d.style.whiteSpace="nowrap";
    if(this.sex_){
        /**
        var degs = Math.floor(Math.abs(lo)); 
        var mins = ((Math.abs(lo)-degs)*60.0).toFixed(2);
        if(mins == "60.00"){
            degs += 1.0;
            mins = "0.00";
            }
        d.innerHTML= degs + ":" + mins; 
        **/
        d.innerHTML = getLongitudeInDecimalMinutes(lo);
        }
    else{
        d.innerHTML = (Math.abs(lo)).toFixed(lonDecs);// only significant digits
        }
    if(lo<0)
    d.title = "West (WGS84)";
    else 
    d.title = "East (WGS84)";
    mapDiv.insertBefore(d,null);
    
    this.divs_[i] = d;//save for remove
    
    i++;// next
    lo += dLng; 
    if (lo > 180.0){
        r -= 360.0;
        lo -= 360.0;
    }   
                
  }
  
  var j = 0;// count lines
      
  //labels on second row to avoid controls
  x = this.map_.fromLatLngToDivPixel(new GLatLng(b,l+dLng+dLng)).x + 3;
  
  //horizontal lines
  while(b<=t){

    var p = this.map_.fromLatLngToDivPixel(new GLatLng(b,l));

    //line
    if(r < l){ //draw lines across the dateline
        this.divs_[i] = this.createHLine3(b);
        mapDiv.insertBefore(this.divs_[i],null);
        i++;
        }
    else if (r == l){ //draw lines for world scale zooms
        this.divs_[i] = this.createHLine3(b);
        mapDiv.insertBefore(this.divs_[i],null);
        i++;
        }
    else{
        this.divs_[i] = this.createHLine(p.y);
        mapDiv.insertBefore(this.divs_[i],null);
        i++;
        }
            
    //label
    var d = document.createElement("DIV");
    y = p.y + 2;

    d.style.position = "absolute";
    d.style.left = x.toString() + "px";
    d.style.top = y.toString() + "px";
    d.style.color = this.color_;
    d.style.fontFamily='Arial';
    d.style.fontSize='x-small';
    d.style.whiteSpace="nowrap";
    if(this.sex_){
        /***
        var degs = Math.floor(Math.abs(b)); 
        var mins = ((Math.abs(b)-degs)*60.0).toFixed(2);
        if(mins == "60.00"){
            degs += 1.0;
            mins = "0.00";
            }
        d.innerHTML= degs + ":" + mins; 
        ***/
        d.innerHTML = getLatitudeInDecimalMinutes(b);
        }
    else{
        d.innerHTML = (Math.abs(b)).toFixed(latDecs);// only significant digits
        }
    if(b<0)
    d.title = "South (WGS84)";
    else 
    d.title = "North (WGS84)";

    if(j != 2)//dont put two labels in the same place
    {
        mapDiv.insertBefore(d,null);
        this.divs_[i] = d; // save for remove
        i++;
    }
    
    j++;//next
    b += dLat; 
  }
  
}

LatLonGraticule.prototype.gridIntervalMins = function(dDeg) {
    if(this.sex_)
        return this.gridIntervalSexMins(dDeg)
    else
        return this.gridIntervalDecMins(dDeg)
}

//calculate rounded graticule interval in decimals of degrees for supplied lat/lon span
//return is in minutes
LatLonGraticule.prototype.gridIntervalDecMins = function(dDeg) {

  var dDeg = dDeg/10;//want around 10 lines in the graticule
  dDeg *= 6000;//to minutes*100
  dDeg = Math.ceil(dDeg)/100;//minutes and hundredths of mins
  
  if(dDeg <= 0.06)
    dDeg = 0.06;//0.001 degrees
  else if(dDeg <= 0.12)
    dDeg = 0.12;//0.002 degrees
  else if(dDeg <= 0.3)
    dDeg = 0.3;//0.005 degrees
  else if(dDeg <= 0.6)
    dDeg = 0.6;//0.01 degrees
  else if (dDeg <=  1.2)
    dDeg = 1.2;//0.02 degrees
  else if(dDeg <= 3)
    dDeg = 3;//0.05 degrees
  else if(dDeg <= 6)
    dDeg = 6;//0.1 degrees
  else if (dDeg <=  12)
    dDeg = 12;//0.2 degrees
  else if (dDeg <=  30)
    dDeg = 30;//0.5
  else if (dDeg <=  60)
    dDeg = 60;//1
  else if (dDeg <=  (60*2))
    dDeg = 60*2;
  else if (dDeg <=  (60*5))
    dDeg = 60*5;
  else if (dDeg <=  (60*10))
    dDeg = 60*10;
  else if (dDeg <=  (60*20))
    dDeg = 60*20;
  else if (dDeg <=  (60*30))
    dDeg = 60*30;
  else
    dDeg = 60*45;
 
  return dDeg;
}

//calculate rounded graticule interval in Minutes for supplied lat/lon span
//return is in minutes
LatLonGraticule.prototype.gridIntervalSexMins = function(dDeg) {

  var dDeg = dDeg/10;//want around 10 lines in the graticule
  dDeg *= 6000;//to minutes*100
  dDeg = Math.ceil(dDeg)/100;//minutes and hundredths of mins
  
  if(dDeg <= 0.01) 
        dDeg = 0.01;//0.01 minutes 
  else if(dDeg <= 0.02) 
        dDeg = 0.02;//0.02 minutes 
  else if(dDeg <= 0.05) 
        dDeg = 0.05;//0.05 minutes 
  else if(dDeg <= 0.1) 
        dDeg = 0.1;//0.1 minutes 
  else if(dDeg <= 0.2) 
        dDeg = 0.2;//0.2 minutes 
  else if(dDeg <= 0.5) 
        dDeg = 0.5;//0.5 minutes 
  else if(dDeg <= 1.0) 
        dDeg = 1.0;//1.0 minute 
  else if(dDeg <= 3)
    dDeg = 3;//0.05 degrees
  else if(dDeg <= 6)
    dDeg = 6;//0.1 degrees
  else if (dDeg <=  12)
    dDeg = 12;//0.2 degrees
  else if (dDeg <=  30)
    dDeg = 30;//0.5
  else if (dDeg <=  60)
    dDeg = 60;//1
  else if (dDeg <=  (60*2))
    dDeg = 60*2;
  else if (dDeg <=  (60*5))
    dDeg = 60*5;
  else if (dDeg <=  (60*10))
    dDeg = 60*10;
  else if (dDeg <=  (60*20))
    dDeg = 60*20;
  else if (dDeg <=  (60*30))
    dDeg = 60*30;
  else
    dDeg = 60*45;
  
  return dDeg;

}

//calculate grid label precision from decimal grid interval in degrees
LatLonGraticule.prototype.gridPrecision = function(dDeg) {

if(dDeg < 0.01)
    return 3;
else if(dDeg < 0.1)
    return 2;
else if(dDeg < 1)
    return 1;
else return 0;

}
  
//returns a div that is a vertical single pixel line          
LatLonGraticule.prototype.createVLine = function(x) {

    var div = document.createElement("DIV");
    div.style.position = "absolute";
    div.style.overflow = "hidden";
    div.style.backgroundColor = this.color_;
    div.style.left = x + "px";
    div.style.top = this.minY + "px";
    div.style.width = "1px";
    div.style.height = (this.maxY-this.minY) + "px";
    return div;
    
}

//returns a div that is a horizontal single pixel line        
LatLonGraticule.prototype.createHLine = function(y) {

    var div = document.createElement("DIV");
    div.style.position = "absolute";
    div.style.overflow = "hidden";
    div.style.backgroundColor = this.color_;
    div.style.left = this.minX + "px";
    div.style.top = y + "px";
    div.style.width = (this.maxX-this.minX) + "px";
    div.style.height = "1px";
    return div;
    
}

//returns a div that is a horizontal single pixel line, across the dateline  
//we find the start and width of a 180 degree line and draw the same amount
//to its left and right   
LatLonGraticule.prototype.createHLine3 = function(lat) {

    var f = this.map_.fromLatLngToDivPixel(new GLatLng(lat,0));
    var t = this.map_.fromLatLngToDivPixel(new GLatLng(lat,180));       
    var div = document.createElement("DIV");
    div.style.position = "absolute";
    div.style.overflow = "hidden";
    div.style.backgroundColor = this.color_;
    var x1 = f.x;
    var x2 = t.x;
    if(x2 < x1){
        x2 = f.x;
        x1 = t.x;
    }
    div.style.left = (x1-(x2-x1)) + "px";
    div.style.top = f.y + "px";
    div.style.width = ((x2-x1)*3) + "px";
    div.style.height = "1px";
    return div;
    
}  


})(jQuery);

