function deEntity ( s )
{
	s = s.replace ( /&#32;|&sp;/gi, " " );
	s = s.replace ( /&#33;|&excl;/gi, "!" );
	s = s.replace ( /&#34;|&quot;/gi, "\"" );
	s = s.replace ( /&#35;|&num;/gi, "#" );
	s = s.replace ( /&#36;|&dollar;/gi, "$" );
	s = s.replace ( /&#37;|&percnt;/gi, "%" );
	s = s.replace ( /&#38;|&amp;/gi, "&" );
	s = s.replace ( /&#39;|&apos;/gi, "\'" );
	s = s.replace ( /&#40;|&lpar;/gi, "(" );
	s = s.replace ( /&#41;|&rpar;/gi, ")" );
	s = s.replace ( /&#42;|&ast;/gi, "*" );
	s = s.replace ( /&#43;|&plus;/gi, "+" );
	s = s.replace ( /&#44;|&comma;/gi, "," );
	s = s.replace ( /&#45;|&hyphen;/gi, "-" );
	s = s.replace ( /&#46;|&period;/gi, "." );
	s = s.replace ( /&#47;|&sol;/gi, "/" );
	s = s.replace ( /&#58;|&colon;/gi, ":" );
	s = s.replace ( /&#59;|&semi;/gi, ";" );
	s = s.replace ( /&#60;|&lt;/gi, "<" );
	s = s.replace ( /&#61;|&equals;/gi, "=" );
	s = s.replace ( /&#62;|&gt;/gi, ">" );
	s = s.replace ( /&#63;|&quest;/gi, "?" );
	s = s.replace ( /&#64;|&commat;/gi, "@" );
	s = s.replace ( /&#91;|&lsqb;/gi, "[" );
	s = s.replace ( /&#92;|&bsol;/gi, "\\" );
	s = s.replace ( /&#93;|&rsqb;/gi, "]" );
	s = s.replace ( /&#94;|&circ;/gi, "^" );
	s = s.replace ( /&#96;|&grave;/gi, "`" );
	s = s.replace ( /&#123;|&lcub;/gi, "{" );
	s = s.replace ( /&#124;|&verbar;/gi, "|" );
	s = s.replace ( /&#125;|&rcub;/gi, "}" );
	s = s.replace ( /&#126;|&tilde;/gi, "~" );
	s = s.replace ( /&#160;|&nbsp;/gi, " " );
	s = s.replace ( /&#161;|&iexcl;/gi, "Á" );
	s = s.replace ( /&#162;|&cent;/gi, "¢" );
	s = s.replace ( /&#163;|&pound;/gi, "£" );
	s = s.replace ( /&#165;|&yen;/gi, "´" );
	s = s.replace ( /&#169;|&copy;/gi, "©" );
	s = s.replace ( /&#174;|&reg;/gi, "¨" );
	s = s.replace ( /&#176;|&deg;/gi, "¡" );
	s = s.replace ( /&#187;|&raquo;/gi, "È" );
	s = s.replace ( /&#191;|&iquest;/gi, "À" );
	s = s.replace ( /&#8217;/gi, "'" );	
	s = s.replace ( /&#8230;/gi, "...");
	
	return s;
}

var ImageFetcher = Class.create({
initialize: function(url, callback)
{
    this.url            = url;
    this.callback       = callback;
    this.images         = new Array();
    this.unloadedImages = 0;
},

inProgress: function()
{
    return this.request != null || this.unloadedImages != 0;
},

fetchImages: function(page)
{
    // abort the previous fetch if necessary
    this.abortFetch();

    _IG_FetchContent(this.url + "?page=" + page, function (responseText)
    {
        this.onSuccess(responseText.evalJSON());
    }.bind(this));
},

abortFetch: function()
{   
    this.images         = new Array();
    this.unloadedImages = 0;
},

onSuccess: function(images)
{    
    this.unloadedImages   = images.length;
    this.waitingForImages = true;

    images.each(function(image)
    {
        image.data = new Image();

        var data = image.data;

        // not using Event.observe here as it doesn't work in Safari 2
        data.onload = function()
        {
            this.onImageLoaded(this, image);
        }.bind(this);
         
        this.images.push(image);

        image.data.src = image.url;
    }.bind(this));

    this.request = null;
},

onFailure: function()
{
    this.request = null;
},

onImageLoaded: function(e, image)
{
    // ensure that the image is in the list
    if (!this.containsImage(image))
    {
        return;
    }

    --this.unloadedImages;

    if (this.unloadedImages === 0)
    {
        this.callback(this.images);
        this.images = new Array();
    }
},

containsImage: function(rhs)
{
    var containsImage = false;

    this.images.each(function(lhs)
    {
        if (lhs == rhs)
        {
            containsImage = true;
            return;
        }
    });

    return containsImage;
}
});

function truncateElementWidth(element, width)
{
    if (element.offsetWidth == undefined || element.offsetWidth <= width)
    {
        // element must be visible to be truncated
        return;
    }

    var title = element.innerHTML;

 	while (element.offsetWidth > width)
 	{
 	    var text = element.innerHTML;

 	    element.innerHTML = text.substring(0, text.length - 1);
 	}

 	element.innerHTML = element.innerHTML.replace(/\s*$/, '') + '...';	
 	element.title     = deEntity(title);
}
 
var currentPage        = 1;
var displayOnFetch     = true;
var images             = new Array();
var imageIndex         = 0;
var imageFetcher       = null;
var imageUpdatePending = false;
 
function activatePanelButton(name)
{
    var element = $(name);
    
    Element.addClassName(element, name + '-normal');
    
    Event.observe(element, 'mouseover', function()
    {
        Element.addClassName(element, name + '-hover');
        Element.removeClassName(element, name + '-normal');
    });
    
    Event.observe(element, 'mouseout', function()
    {
        Element.addClassName(element, name + '-normal');
        Element.removeClassName(element, name + '-hover');
    });
}

function onLoad()
{    
    var panelButtons = ["previous", "next", "comment", "mail"];
    
    for (var i = 0; i < panelButtons.length; ++i)
    {
        activatePanelButton(panelButtons[i]);        
    }
    
    Event.observe($('image'), 'load', onImageLoad);

    Event.observe(document, 'keydown', function(e)
    {
        var key = e.which || e.keyCode;

        switch (key)
        {
            case Event.KEY_PAGEUP:
            case Event.KEY_LEFT:
                previousImage();
                break;
            case Event.KEY_PAGEDOWN:
            case Event.KEY_RIGHT:
                nextImage();
                break;
        }
    });

    Event.observe($('image'), 'click', linkToPost);

    $('previous').observe('click', previousImage);
    $('next').observe('click', nextImage);    

    $('titlebar-text').observe('click', function()
    {
        window.open("http://icanhascheezburger.com");
    });

    $('comment').observe('click', function()
    {
        if (imageIndex < images.length)
        {
            window.open(images[imageIndex].permalink + "#comment");
        }
    });

    $('mail').observe('click', function()
    {
        var displayingAnImage = imageIndex < images.length;

        var emailContent = "";

        if (displayingAnImage)
        {
            emailContent = escape(
                "Hey,\n\n" + 
                "I found this great picture I think you'll enjoy:\n\n" +
                images[imageIndex].permalink + "\n\n" +
                "(Don't worry, it's safe for work.)\n\n" +
                "I found it using the I Can Has Cheezburger? widget from:\n\n" +
                "http://icanhascheezburger.com/widgets/");
        }
        else
        {
            emailContent = escape(
                "Hey,\n\n" +
                "Take a look at this I Can Has Cheezburger? widget:\n\n" +
                "http://icanhascheezburger.com/widgets/");
        }
         
        document.location.href = "mailto:?subject=I Can Has Cheezburger&body=" + emailContent;
    })

    imageFetcher = new ImageFetcher('http://icanlol.com/cheezburger.php', imageFetcherCallback);
     
    onShow();
}

function onShow()
{   
    if (imageFetcher.inProgress())
    {
        imageFetcher.abortFetch();
    }

    currentPage    = 1;
    displayOnFetch = true;
    images         = new Array();
    imageIndex     = 0;

    setCaption('Plz wait...');
     
    $('image').style.cursor = "";
    $('image').src = "http://icanlol.com/igoogle/images/loading.png";
    $('spinner').show();

    imageFetcher.fetchImages(currentPage);
}

function previousImage()
{
    if (imageIndex == 0)
    {
        return;
    }

    --imageIndex;
     
    $('spinner').hide();

    updateImage();
}

function nextImage()
{    
    if (imageIndex == images.length)
    {
        // we're waiting on the batch to be fetched
        // it will be displayed when it's ready
        return;
    }

    ++imageIndex;

    if (imageIndex == images.length)
    {
        if (!imageFetcher.inProgress())
        {            
            ++currentPage;
            displayOnFetch = true;            
            imageFetcher.fetchImages(currentPage);
        }

        setCaption('Plz wait...');
        
        $('image').style.cursor = '';
        $('image').src = "http://icanlol.com/igoogle/images/loading.png";
        Element.show('spinner');        
    }
    else
    {
        updateImage();
    }
}

function _setCaption(text)
{
    document.getElementById('caption-text').innerHTML = text;
    $('caption-text').title = '';
    $('image').alt = deEntity(text);
     
    truncateElementWidth($('caption-text'), 240);  
}

function setCaption(text)
{
    setTimeout(function() { _setCaption(text); }, 0);
}

function linkToPost()
{
    if (images.length != 0 && imageIndex < images.length)
    {
        window.open(images[imageIndex].permalink);
    }
}

function onImageLoad()
{
    if (images.length != 0 && imageIndex < images.length)
    {
        $('image').style.cursor = 'pointer';
        setCaption(images[imageIndex].title);
        
        imageLink = images[imageIndex].url;
    }
    
    resize();     
}

function imageFetcherCallback(newImages)
{    
    newImages.each(function(image)
    {
        images.push(image);
    });

    Element.hide($('spinner'));

    if (displayOnFetch)
    {
        imageUpdatePending = false;
        updateImage();
    }
}

function updateImage()
{
    $('image').src = images[imageIndex].url;
}
 
function resize()
{ 
    var image = $('image');    
    
    var targetSize = image.height;

    var titlebarHeight = 20;
    var controlHeight  = 40;
    var padding        = 0;
    var captionHeight  = 20;

    var newHeight = targetSize + titlebarHeight + controlHeight + captionHeight + padding;

    _IG_AdjustIFrameHeight(newHeight);
}

_IG_RegisterOnloadHandler(onLoad);