
function TOCWidget(options) {
    this.entries = null;
    this.loading = false;
    this.numEntries = options.numEntries;
    this.curIndex = options.curIndex;
    this.viewStart = options.viewStart;
    var preload = options.preload || 2;
    this.viewSize = 5;
    this.loadedMin = Math.max(this.viewStart - preload, 0);
    this.loadedMax = Math.min(this.numEntries - 1, this.viewStart + preload - 1 + this.viewSize);
    this.scrolling = false;
    this.dataSourceURL = options.dataSourceURL;
    this.toc = $(options.tocId ? "#" + options.tocId : "#toc_bar");
    this.entryId = options.entryId;
    this.pendingClickObject = null
    var self = this;

    this.get_entries = function() {
        if (this.loading)         { return null; }
        if (this.entries != null) { return this.entries; }

        // Don't bother loading if we don't need to
        if (this.loadedMax == this.numEntries-1 && this.loadedMin == 0) {
            this.entries = [];
            if (this.pendingClickObject) {
                this.clickHandler(this.pendingClickObject);
            }
            return [];
        }

        this.loading = true;
        var toc_obj = this;
        $.ajax({
            url: this.dataSourceURL,
            type: "GET",
            data: { entryid: this.entryId },
            dataType: "json",
            success: function(data, status) {
                if (data.status) {
                    toc_obj.entries = data.entries;
                    if (!this.scrolling) toc_obj.update_toc();
                    if (toc_obj.pendingClickObject) {
                        toc_obj.clickHandler(toc_obj.pendingClickObject);
                    }
                }
                toc_obj.loading = false;
            },
            timeout: 10000,
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                toc_obj.loading = false;
            }
        });
        return null;
    };

    this.update_toc = function() {
        if (this.viewStart < this.loadedMin) {
            var loadStart = this.loadedMin-1;
            var loadEnd = Math.max(0, this.viewStart - this.viewSize);
            for (var i = loadStart; i >= loadEnd; i--) {
                if (this.loadedMin > i) this.loadedMin = i;
                var newLi = this.generateLi(this.entries[i], i).hide();
                this.toc.prepend(newLi)
                        .css("width", ((this.loadedMax-this.loadedMin+1)*170) + "px" )
                        .css("left", (new Number(this.toc.css("left").replace("px", "")) - 170) + "px");
                newLi.show();
            }
        }
        if (Math.min(this.numEntries-1, this.viewStart + this.viewSize) > this.loadedMax) {
            var loadStart = this.loadedMax+1;
            var loadEnd = Math.min(this.numEntries-1, this.viewStart + (this.viewSize*2));
            for (var i = loadStart; i <= loadEnd; i++) {
                if (this.loadedMax < i) this.loadedMax = i;
                var newLi = this.generateLi(this.entries[i], i);
                this.toc.append(newLi)
                        .css("width", ((this.loadedMax-this.loadedMin+1)*170) + "px" );
            }
        }
    };

    this.generateLi = function(entry, index) {
        var li = $("li:not(.empty):first", this.toc).clone();
        li.attr("id", "toc_" + index);
        if (index==this.curIndex) {
            li.addClass("current");
        } else {
            li.removeClass("current");
        }
        $("a:first", li).attr("href", entry.url);
        $("a:first strong", li).html(entry.title_or_date);
        $(".number", li).html(entry.number);
        $(".meta .icon-comments", li).attr("title", entry.comments_count + " comments").html(entry.comments_count);
        $(".meta .icon-photos", li).attr("title", entry.images_count + " photos").html( entry.images_count);
        $(".meta .date", li).html(entry.date_formatted);
        $(".meta a.toc_city", li).html(entry.entrylocation);
        $(".meta a.toc_city", li).attr("href", entry.city_url);
        $(".meta img.toc_cc", li).attr('src', '/bin/famfamfam_flags/png/' + entry.countrycode + '.png');
        $("b.line", li).attr("id", "line"+index);
        $("#line" + index, li).show();
        if (entry.draft)
            $(".meta .draft", li).html("Draft");
        else
            $(".meta .draft", li).html("");
        return li;
    };

    function now(){
        return +new Date;
    }

    this.animation = undefined;
    this.clickHandler = function (object) {
        var toc = $("#toc_bar");
        var clicked_id = $(object).attr("id");
        this.get_entries();
        var scroll = false;
        var scrollStep = 5;
        if (clicked_id == "toc-prev" && self.viewStart > 0) {
            if (self.viewStart - scrollStep >= self.loadedMin || self.entries) {
                if (!$("#toc_" + (self.viewStart-1)).hasClass('current'))
                {
                    $("#line" + (self.viewStart-1)).show();
                }

                self.viewStart = self.viewStart - scrollStep;
                if (self.viewStart < 0)
                    self.viewStart = 0;

                scroll = true;
            } else {
                self.pendingClickObject = object;
            }
        } else if (clicked_id == "toc-next" && self.viewStart + self.viewSize < self.numEntries) {
            if (self.viewStart + self.viewSize + (scrollStep-1) <= self.loadedMax || self.entries) {
                if (!$("#toc_" + (self.viewStart-1)).hasClass('current'))
                {
                    $("#line" + (self.viewStart-1)).show();
                }
                self.viewStart = self.viewStart + scrollStep;
                if (self.viewStart > self.numEntries - self.viewSize)
                    self.viewStart = self.numEntries - self.viewSize;
                scroll = true;
            } else {
                self.pendingClickObject = object;
            }
        }

        if (self.entries) { self.update_toc(); }
        if (scroll) {
            if (this.scrolling)
                this.toc.stop(true);
            this.scrolling = true;
            var that = this;
            this.toc.animate({ left: ((this.loadedMin-this.viewStart)*170) + "px" }, 1300, "swing", function() {
                if ((that.viewStart-1) >= 0)
                {
                    $("#line" + (that.viewStart-1)).hide();
                }
                that.scrolling = false;
            } );
        }
        var targetOpacity = self.viewStart > 0 ? 1 : 0;
        if (self.prevOpacityTarget != targetOpacity) {
            self.prevOpacityTarget = targetOpacity;
            $("#toc-prev").stop(true).animate( { opacity: targetOpacity } );
        }
        targetOpacity = self.viewStart + self.viewSize < self.numEntries ? 1 : 0;
        if (self.nextOpacityTarget != targetOpacity) {
            self.nextOpacityTarget = targetOpacity;
            $("#toc-next").stop(true).animate( { opacity: targetOpacity } );
        }
        return false;
    };
}

function ajax_submit_form(form, validator, error_div, result_container, failmsg, success_callback) {
    if (validator && !validator(form, error_div)) {
        return false;
    }
    if (error_div) {
        error_div.hide();
    }
    var buttons = $('#dialog form input:submit, #dialog form a.button:not(.secondary)');
    var button_enabler;

    buttons.each(function(i) {
        if (this.tagName == "INPUT") {
            this.disabled = true;
        } else {
            $(this).addClass("disabled");
        }
    });

    var url = unique_url(form.action);
    $.ajax({
        type: "POST",
        url: "" + D0(url),
        dataType: "html",
        data: $(form).serialize(),
        success: function(data) {
            if (success_callback)
            {
                if (success_callback(form, data))
                    result_container.html(data);
            }
            else
                result_container.html(data);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
           error_div.html('<p>' + failmsg + '<p>').show();
            buttons.each(function(i) {
                if (this.tagName == "INPUT") {
                    this.disabled = false;
                } else {
                    $(this).removeClass("disabled");
                }
            });
        },
        cache: false
    });
    return false;
}

function unique_url(url) {
    var d = new Date().getTime().toString();
    if (url.search(/_=\d+/) > -1) {
        return url.replace(/_=\d+/, '_=' + d);
    }
    var delim = url.indexOf('?') > -1 ? '&' : '?';
    return url + delim + "_=" + d;
}

function generate_overlay(title, window_src, height)
{
    if (height == undefined || height == "")
        height = 500;
    if (height > 600)
        height = 600;
    return function() {
        var url = unique_url(D0(window_src));
        $('#dialog').html("<div id='throbber'><img src='/bin/dashboard/throbber-big.gif' alt='loading'></div>").
        load(url).
        dialog({
                autoOpen: false,
                width: 600,
                height: 500,
                bgiframe: true,
                modal: true,
                beforeclose: function(event, ui) { $('div#player').show(); }
        }).dialog('option', 'title', title).dialog('open');
        $('#dialog').height(height);
        $('div#player').hide();
        return false;
    };
}

function redirect_overlay() {
    var message = Get_Cookie("r_message");
    if (message) {
        Delete_Cookie("r_message", "/");
        $('#dialog').html('<div class="message message-error">' + message + '</div>').
        dialog({
                autoOpen: true,
                width: 600,
                height: 300,
                bgiframe: true,
                modal: false,
                title: "Resource Not Found"
        });
    }
    return false;
}

function comment_overlay(anchor, href)
{
    return generate_overlay( anchor.title, href + "?login_for=" + anchor.id );
}


function private_trip(href, uid, tripid) {
    var trip_cookie = 'Guest-' + uid + '-' + tripid;

    if (Get_Cookie(trip_cookie))
    {
        G0(href);
    }
    else
    {
        generate_overlay("Private Trip", href)();
    }
    return false;
}

function G0(a){document.location=D0(a); return false;}

function D6(input) {
    var output = "";
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;

    var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

    while (i < input.length) {

        enc1 = _keyStr.indexOf(input.charAt(i++));
        enc2 = _keyStr.indexOf(input.charAt(i++));
        enc3 = _keyStr.indexOf(input.charAt(i++));
        enc4 = _keyStr.indexOf(input.charAt(i++));

        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;

        output = output + String.fromCharCode(chr1);

        if (enc3 != 64) {
            output = output + String.fromCharCode(chr2);
        }
        if (enc4 != 64) {
            output = output + String.fromCharCode(chr3);
        }

    }

    return output;
}

function D0(string) {
    var s;
    if (string.charAt(0) != ";") {
        return string;
    }
    var s = D6(string.substr(1));
    var out = "";
    var keys = ["add_favorite","comment","new","delete_comment",
    "donate","first-travel-photo","first-travelogue-entry",
    "forgot_password","log_hit","members","print-travel-blog",
    "send","send_postcard","slideshow","slideshow_xml","subscribe",
    "tools","send_message","travel-blog","travel-blog-entries",
    "travel-blog-slideshow","travel-photo","travelblogphotoalbums",
    "unsub","unsubscribe","unsubscribe_comment","update_header_links",
    "vote", "http:/", "like", "syndication", "rss", "travel-blog-city"];
    while (s.charAt(0) == "_") {
        var m = s.match(/^_(\d+)/);
        if (m) {
            var k = m[1] == 0 ? "_" : keys[parseInt(m[1])-1];
            out += (k.match(":") ? "" : "/") + k;
            s = s.substr(m[0].length);
        }
    }
    return out + s;
}

function Cookies_Enabled (){
    var cookieEnabled; //=(navigator.cookieEnabled)? true : false;

    //if navigator,cookieEnabled is not supported
    //if (typeof navigator.cookieEnabled=="undefined" && !cookieEnabled){
        //document.cookie="testcookie";
        Set_Cookie("testcookie","testcookie");
        cookieEnabled=(document.cookie.indexOf("testcookie")!=-1)? true : false;
        Delete_Cookie("testcookie");
    //}

    return cookieEnabled;
}
// To use, simple do: Get_Cookie('cookie_name');
// replace cookie_name with the real cookie name, '' are required
function Get_Cookie( check_name ) {
    // first we'll split this cookie up into name/value pairs
    // note: document.cookie only returns name=value, not the other components
    var a_all_cookies = document.cookie.split( ';' );
    var a_temp_cookie = '';
    var cookie_name = '';
    var cookie_value = '';
    var b_cookie_found = false; // set boolean t/f default f

    for ( i = 0; i < a_all_cookies.length; i++ )
    {
        // now we'll split apart each name=value pair
        a_temp_cookie = a_all_cookies[i].split( '=' );
        // and trim left/right whitespace while we're at it
        cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');

        // if the extracted name matches passed check_name
        if ( cookie_name == check_name )
        {
            b_cookie_found = true;
            cookie_value = a_temp_cookie[1];
            if (cookie_value) cookie_value = unescape( cookie_value.replace(/^\s+|\s+$/g, '') );
            return cookie_value;
            break;
        }
        a_temp_cookie = null;
        cookie_name = '';
    }
    if ( !b_cookie_found )
    {
        return null;
    }
}

/*
only the first 2 parameters are required, the cookie name, the cookie
value. Cookie time is in milliseconds, so the below expires will make the
number you pass in the Set_Cookie function call the number of days the cookie
lasts, if you want it to be hours or minutes, just get rid of 24 and 60.

Generally you don't need to worry about domain, path or secure for most applications
so unless you need that, leave those parameters blank in the function call.
*/
function Set_Cookie( name, value, expires, path, domain, secure ) {
    // set time, it's in milliseconds
    var today = new Date();
    today.setTime( today.getTime() );
    // if the expires variable is set, make the correct expires time, the
    // current script below will set it for x number of days, to make it
    // for hours, delete * 24, for minutes, delete * 60 * 24
    if ( expires )
    {
        expires = expires * 1000 * 60 * 60 * 24;
    }
    //alert( 'today ' + today.toGMTString() );// this is for testing purpose only
    var expires_date = new Date( today.getTime() + (expires) );
    //alert('expires ' + expires_date.toGMTString());// this is for testing purposes only

    document.cookie = name + "=" +escape( value ) +
        ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) + //expires.toGMTString()
        ( ( path ) ? ";path=" + path : "" ) +
        ( ( domain ) ? ";domain=" + domain : "" ) +
        ( ( secure ) ? ";secure" : "" );
}

// this deletes the cookie when called
function Delete_Cookie( name, path, domain ) {
    var cookie  = Get_Cookie( name );
    if (cookie != undefined)
        document.cookie = name + "=" +
            ( ( path ) ? ";path=" + path : "") +
            ( ( domain ) ? ";domain=" + domain : "" ) +
            ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
}

function Clear_TP_Cookies(domain)
{
    Delete_Cookie('tweb_token', '/', '.travelpod.com');
    Delete_Cookie('tweb_token', '/', '.www.travelpod.com');
    Delete_Cookie('tweb_token', '/' , domain);

    Delete_Cookie('pass_hash', '/', domain);
    Delete_Cookie('member_id', '/', domain);
    Delete_Cookie('session_id', '/', domain);
    Delete_Cookie('topicsread', '/', domain);
    Delete_Cookie('anonlogin', '/', domain);
    Delete_Cookie('forum_read', '/', domain);
}

function validate_search(field)
{
    if (field.value == null || field.value == "" || field.changed == undefined)
        return false;
    return true;
}

function validate_search_form(thisform,target) {
    if (validate_search(thisform.search)==false) {
      thisform.search.focus();
      return false;
    } else {
      if (target==null||target=="") {
          target = '/s/';
      }
      target += encodeURIComponent(thisform.search.value.replace(/^\s+|\s+$/g,"").replace(/  /g," ").replace(/\//g,"%2F")).replace(/%20/g,"+");
      document.location = target + "?st=user";
      return false;
    }
}

function go(url) {
    document.location = url;
}



function TripMap(options) {
    // DATA
    this.trip_data          = options.data;
    this.expanded           = options.start_expanded || false;
    var entry_to_show       = options.entryid;
    var num_non_pin_entries = options.num_non_pin_entries;
    var server_name         = options.server_name;
    var self                = this;
    this.is_loaded			= false;
    this.map_options_show 	= false;

    this.show = function (data) {
        if (data)
            self.trip_data = data;

        if (!GBrowserIsCompatible()) {
            return;
        }

        var map_canvas = document.getElementById("map_canvas");
        var height = map_canvas.clientHeight;
        var width = map_canvas.clientWidth;

        var earth = G_SATELLITE_3D_MAP;
            earth.getName = function(short) { return "3D Earth"; }
        var map = self.map = new GMap2(map_canvas, {
            size: new GSize(width, height),
            mapTypes: [G_NORMAL_MAP, G_PHYSICAL_MAP, G_SATELLITE_MAP, G_HYBRID_MAP, earth]
        });
        map.setCenter(new GLatLng(23, 15), 8);
        map.addControl(new GSmallMapControl(), new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(5,5)));
        map.setMapType(G_PHYSICAL_MAP);

        var defaultIcon = new GIcon();
        defaultIcon.image = "http://" + server_name + "/bin/blog/bg/timeline-dot-big.png";
        defaultIcon.iconSize = new GSize(12, 12);
        defaultIcon.iconAnchor = new GPoint(6, 6);
        defaultIcon.infoWindowAnchor = new GPoint(6, 2);

        var grayIcon = new GIcon(defaultIcon, "http://" + server_name + "/bin/blog/bg/timeline-dot-big-gray.png");

        var currentIcon = new GIcon();
        currentIcon.image = "http://" + server_name + "/bin/blog/bg/timeline-current.png";
        currentIcon.iconSize = new GSize(27, 30);
        currentIcon.iconAnchor = new GPoint(13, 30);
        currentIcon.infoWindowAnchor = new GPoint(10, 2);

        self.mapper = new blog_trip_map(map,
            {
                getOverlayContent: function(html_id, entry, cur, pin) {
                    var bold = pin.entries.length > 1 && cur;
                    var html = '<tr id="' + html_id + '" class="big"><td>' + (bold ? "<b>" : "") +
                        (entry.num ? entry.num + ". " : "") + entry.title +
                        (bold ? "</b>" : "") + '</td>';
                    if (!entry.mappin)
                    {
                        html += '<td width="85" valign="top">&nbsp;<span class="controls"><a href="/travel-blog-entries/'+ self.trip_data.uid + '/'+ self.trip_data.trip_id + '/' + entry.id + '/tpod.html" title="Read the blog entry">Read</a> ';
                        if (entry.images > 0)
                        {
                            html += '<span class="pipe">| </span>';
                            html += '<a href="/travel-blog-entries/'+ self.trip_data.uid + '/'+ self.trip_data.trip_id + '/' + entry.id + '/tpod.html#album" title="View the photos for this entry">Photos</a></span>';
                        }
                        html += '</td></tr>';
                    }
                    else
                    {
                        html += '<td><span style="color: #ccc">Map Pin Entry</span></td></tr>';
                    }
                    return html;
                },
                data: self.trip_data,
                defaultIcon: defaultIcon,
                grayIcon: grayIcon,
                currentIcon: currentIcon,
                currentEntry: entry_to_show
            });

        self.mapper.add_polyline();
        self.mapper.add_markers();

        if (self.trip_data.pin_count == 0)
            map.setCenter(new GLatLng(30, 5), 1);
        else if (self.expanded)
            self.setup_expanded();

        if (!entry_to_show)
            self.mapper.zoom_entire_trip();
        else
            self.mapper.pan_to_entry(entry_to_show, true);


        if (trip_map.trip_data.pin_count > 0) {
            GEvent.addListener(map, "click", function() { self.expand(); }),
            //GEvent.addListener(map, "singlerightclick", function() { expand(); }),
            GEvent.addListener(map, "dblclick", function() { self.expand(); }),
            GEvent.addListener(map, "zoomed", function() { self.expand(); })
        } else {
            if (map.getZoom() == 0)
                map.setCenter(new GLatLng(30, 5), 1);
        }

        // Just in case
        if (map.isLoaded() == 0)
            map.setCenter(new GLatLng(30, 5), 1);

        this.is_loaded = true;

    }


    // GOOGLE MAP set up
    this.load = function(callback) {
        $.getScript(options.load_script, callback);
        return;
    }

    this.expand = function() {
        if (this.expanded && !this.map_options_show)
        {
            this.map_options_show = true;
            $('.tp_map_controls').show();
            $('#map_options').hide();
            self.map.addControl(new GMenuMapTypeControl(), new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(5,5)));
            return;
        }
        if (this.expanded) return;
        if (!this.is_loaded) return;
        this.expanded = true;
        this.map_options_show = true;
        $('#tripslide #where-i-stayed').hide();
        $('#tripslide').css("margin-left","0px");
        $("#map_canvas")[0].style.width = "665px";
        $("#map_canvas")[0].style.height = "300px";
        $("#explore-blog").css("clear","both");

        $('.tp_map_controls').show();
        $('#map_options').hide();

        this.setup_expanded();

        $("#tripslide").animate({ width: "680px" }, 500);
        $("#tripslide .map").animate({ height: "300px" }, 200);

    }

    this.setup_expanded = function() {
        self.map.checkResize();
        self.map.addControl(new GMenuMapTypeControl(), new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(5,5)));
        var lat = 30;
        var lng = 5;
        var ent = self.mapper.currentEntry ? self.mapper.entries[self.mapper.currentEntry] : undefined;
        if (ent) {
            var array_lat_lng = ent.geo_id.split("\\\\");
            if (array_lat_lng[0])
                lat = array_lat_lng[0];
            if (array_lat_lng[1])
                lng = array_lat_lng[1];
            self.map.setCenter(new GLatLng(lat, lng));
        }

        if (self.map.getZoom() == 0)
            self.map.setZoom(1);
    }

}
