// ==UserScript==
// @name          Geocaching Thumbnails
// @namespace     http://benchmarks.org.uk/geothumbs/
// @version       3.1
// @description   (v3.1) Changes the default picture icons on cache pages to image thumbnails.  New version for geocaching.com January 2010 update.
// @include       http://www.geocaching.com/seek/cache*
// ==/UserScript==

// Copyright (C) 2010 Gary Player <dev@benchmarks.org.uk>
// Released under the GPL http://www.gnu.org/copyleft/gpl.html
// This is a Greasemonkey user script, see http://greasemonkey.mozdev.org/.

// Number of images per row - firefox can change this via a by menu option
var imagesPerRow = 2;

// Image sizes - firefox can change these via a by menu option 
var imageSize = "thumb";
var ownerImageSize = 100;

// Show spoilers (true to always show) - firefox can change this via a by menu option 
var showSpoilers = false;

//regexp to use to check for spoilers
var spoilerPattern=/(spoil|spiol|spoli|It'?s here)/i;

// Show owner images (true to show) - firefox can change this via a by menu option 
var showOwnerImages = true;

// Show descriptions with thumbnails (true to show) - firefox can change this via a by menu option
var showDescriptions = true;

// Change to true to cause owner image table to span the full page width
var ownerImagesSpanPage = false;

// rel id to use for all anchors to images
var lightboxRelId = "lightbox[geothumbs]";

// change string to set image box background colour e.g. #fcfcfc
var imageBoxColour = "";


///////////////////////// functions //////////////////////////////

// gets all elements of the specified tag with the specified regexp class name
function getElementByTagAndClass(tag, classNamePattern)
{
    var collection=new Array();
    var found=0;
    var tags=document.getElementsByTagName(tag);

    for (i=0; i<tags.length; i++)
    {
        if (tags[i].className.match(classNamePattern))
        {
            collection[found++]=tags[i];
        }
    }
    return collection;
}

// inserts newNode after referenceNode
function insertAfter( referenceNode, newNode )
{
    referenceNode.parentNode.insertBefore( newNode, referenceNode.nextSibling );
}

// climbs to tagname from specified element
function climbTo(element, tagname)
{
    var parentElement = null;
    var element;

    tagname = tagname.toUpperCase();
    while((element = element.parentNode) && !parentElement)
    {
        if (element.tagName == tagname)
        {
            parentElement = element;
        }
    }
    return parentElement;
}

// returns the required node name sibling of the startNode 
function getSibling(startNode, findNodeName)
{
	var foundNode = null;
	var node = startNode;
	
	findNodeName = findNodeName.toUpperCase();
	while ((node = node.nextSibling) && !foundNode)
	{
	    if (node.nodeName == findNodeName)
	    {
	    	foundNode = node;
	    }
	}
	return foundNode;
}


// removes leading and trailing spaces, quotes and jpg extensions from received title
function tidyTitle(title)
{
    return title.replace(/^[\s\"']*/, "").replace(/[\s\"']*$/, "").replace(/\.jpg$/i, "");
}

// returns true if we want to see spoilers or the title indicates the image isn't a spoiler
// as determined by the spoiler regexp
function showImage(title)
{
    return (showSpoilers || !title.match(spoilerPattern));
}

// toggles whether spoiler images are shown or not
function showSpoilersMenuAction()
{
    showSpoilers == true ? showSpoilers = false : showSpoilers = true;
    GM_setValue("showSpoilers", showSpoilers);
    document.location.reload();
}

// toggles whether owner images are shown or not
function showOwnerImagesMenuAction()
{
    showOwnerImages == true ? showOwnerImages = false : showOwnerImages = true;
    GM_setValue("showOwnerImages", showOwnerImages);
    document.location.reload();
} 

//toggles whether owner images are shown or not
function showDescriptionsMenuAction()
{
    showDescriptions == true ? showDescriptions = false : showDescriptions = true;
    GM_setValue("showDescriptions", showDescriptions);
    document.location.reload();
}

// cycles number of images per row between 2 and 4
function imagesPerRowMenuAction()
{
    imagesPerRow++;
    if (imagesPerRow>4) imagesPerRow = 2;
    GM_setValue("imagesPerRow", imagesPerRow);
    document.location.reload();
}

// toggles image size between thumbnail (100px wide) and larger (300px wide) images 
function imageSizeMenuAction()
{
    imageSize == "thumb" ? imageSize = "display" : imageSize = "thumb";
    GM_setValue("imageSize", imageSize);
    document.location.reload();
}

///////////////////////////// main /////////////////////////////////

// obtain agent as we can run in Opera without menu options (do not use 'opera' as a var name - it breaks opera 10!)
var oopera = window.navigator.userAgent.match("Opera");

if (!oopera)
{
    // set up user defined settings
    imagesPerRow = GM_getValue("imagesPerRow", 2);
    showOwnerImages == true ? current = "Yes" : current = "No";
    GM_registerMenuCommand("Geothumbs - Images per row : " +  imagesPerRow, imagesPerRowMenuAction);
    
    imageSize = GM_getValue("imageSize", "thumb");
    if (imageSize == "thumb")
    {
        current = "Thumbnail";
        ownerImageSize = 100;
    }
    else
    {
        current = "Large";
        ownerImageSize = 300;
    }
    GM_registerMenuCommand("Geothumbs - Image size : " +  current, imageSizeMenuAction);
    
    showSpoilers = GM_getValue("showSpoilers", false);
    showSpoilers == true ? current = "Yes" : current = "No";
    GM_registerMenuCommand("Geothumbs - Show spoilers : " +  current, showSpoilersMenuAction);
    
    showDescriptions = GM_getValue("showDescriptions", true);
    showDescriptions == true ? current = "Yes" : current = "No";
    GM_registerMenuCommand("Geothumbs - Show image descriptions : " +  current, showDescriptionsMenuAction);

    showOwnerImages = GM_getValue("showOwnerImages", true);
    showOwnerImages == true ? current = "Yes" : current = "No";
    GM_registerMenuCommand("Geothumbs - Show cache owner images : " +  current, showOwnerImagesMenuAction);
}


///////////////////////////// process cache owner images //////////////////////////////

if (showOwnerImages)
{
    // get owner name
    var ownerName = document.getElementById("ctl00_ContentBody_CacheOwner").lastChild.text;

    var ownerImages=null;
    var imagesElement = document.getElementById("ctl00_ContentBody_Images");
    if (imagesElement)
    {
        // get all the cache owner images
        ownerImages = imagesElement.getElementsByTagName("a");
    }
    
    // only process if we have some owner images
    if (ownerImages.length!=0)
    {
        // create a table for our title row and image rows
        t = document.createElement('table');
        t.cellSpacing = 0;
        t.style.cssText = "border:1px solid #D7D7D7;";
        t.width="100%";

        // create a title row with cell
        titleRow = t.insertRow(-1);
        titleCell = titleRow.insertCell(-1);
        titleCell.style.cssText="border-bottom:1px solid #D7D7D7; padding: 4px; padding-left: 6px; background: url(../images/masters/sprite_table.gif);";
        titleCell.colSpan = imagesPerRow * 2; // *2 as we have 2 cells per image

        titleCell.innerHTML = "Cache Owner Images";
        titleRow.appendChild(titleCell);
        
    
        // force new row
        var col=imagesPerRow;
        
        for (var i = 0; i < ownerImages.length; i++)
        {
            // process the non-edit image anchors
            if (ownerImages[i].text != "Edit")
            {
                // create a new row if required
                var imageRow;
                if (col==imagesPerRow)
                {
                    // create an image row
                    imageRow = t.insertRow(-1);
                    
                    if (imageBoxColour)
                    {
                        imageRow.style.cssText = "background:"+imageBoxColour;
                    }
                    col=0;
                }
    
                // create a copy of the original anchor to modify - the original
                // is going to be removed later when the original row is removed
                var imageAnchor = ownerImages[i].cloneNode(true);
                
                // get anchor details
                var pictureTitle = imageAnchor.text;
                var largePicture = imageAnchor.href;                

                // clean up the picture title
                var pictureTitle = tidyTitle(pictureTitle);
    
                // use the same lightbox rel id for all images on the page
                imageAnchor.rel=lightboxRelId;

                // look for a description (in text node after second <br> after anchor) 
                var description="";
                var lightboxDescription="";
                var node = getSibling(ownerImages[i], "br");
                if (node)
                {
                    if (node = getSibling(node, "br"))
                    {
                    	if ((node = node.nextSibling) && node.nodeName == "#text")
                        {
                            description = node.textContent;
                        }
                    }
                }

                // if description exists create image and lightbox formats
                var lightboxDescription="";
                var imageTitleDescription="";
                if (description != "")
                {
                	lightboxDescription = "<span style=\"font-weight: normal\">"+description+"</span><br>";
                	imageTitleDescription = " - " + description;
                }                
                
                // create anchor title - this is used by lightbox
                var title = "\""+pictureTitle+"\" by "+ownerName+"<br>" + lightboxDescription +
                    "<a href=\"javascript:pp('"+largePicture+"')\;\">Print Picture</a>";
               	imageAnchor.title=title;

                // clean up the tooltip
                var img = imageAnchor.firstChild;    
                img.title = pictureTitle + imageTitleDescription;
                img.alt = pictureTitle;
    
                // change camera image to picture unless we want to hide spoilers
                if (showImage(pictureTitle))
                {
                    img.src = largePicture;
                    img.width = ownerImageSize;    // shrink it
                }
                else
                {
                    img.title = "** SPOILER ** " + pictureTitle;  // do not show description
                    img.alt = "** SPOILER ** " + pictureTitle;
                }
                
                // remove the non image anchor text
                imageAnchor.removeChild(imageAnchor.lastChild);
                
                // create a new cell in the row and add the anchor
                imageCell = imageRow.insertCell(-1);
                imageCell.align="center";
                imageCell.width="100px";
                imageCell.height="81px";
                imageCell.style.padding="8px";
                imageCell.style.lineHeight = "0pt";  // remove space under image
                imageCell.appendChild(imageAnchor);
                
                // create new anchor for text link to image or edit image page
                var textAnchor;
                if ( (ownerImages[i+1]) && (ownerImages[i+1].text == "Edit") )
                {
                    // use existing edit image anchor and modify title
                    textAnchor = ownerImages[i+1].cloneNode(true);
                    
                    // fix bug in anchor link (remove erroneous '.')
                    textAnchor.href = textAnchor.href.replace(/\.image/, "image");

                    pictureTitle = pictureTitle + " [Edit]";
                }
                else
                {
                    // new anchor for link to image
                    textAnchor = document.createElement('a');
                    textAnchor.href = largePicture;
                }
                pictureTitle = pictureTitle;
                textAnchor.textContent = pictureTitle;
                textAnchor.title = pictureTitle; // sets tooltip
                textAnchor.style.cssText = "text-decoration: none;"; // remove link underline
    
                // add a cell for the anchor
                textCell = imageRow.insertCell(-1);
                textCell.width="1200px";
                textCell.style.cssText="padding-left: 0"; // force text close to image
                
                // add anchor to cell
                textCell.appendChild(textAnchor);
                
                // add description if required
                if (showDescriptions)
                {
	                textCell.appendChild(document.createElement('br'));
	                span = document.createElement('span');
	                span.style.cssText = "font-size: smaller";
	                textCell.appendChild(span);
	                
	                descriptionNode = document.createTextNode(description);
	                span.appendChild(descriptionNode);
                }
                
                // increment column count
                col++;
            }
        }
        
        // create replacement span with new image table
        span = document.createElement('span');
        span.id = "ct100_ContentBody_Images";
        span.appendChild(t);
        
        // remove original span - go to parent and remove it
        p = imagesElement.parentNode;
        p.removeChild(imagesElement);

        // bodge to move table below map
        br = document.createElement('br');
        p.appendChild(br);
        br = document.createElement('br');
	p.appendChild(br);

        // add new span
        p.appendChild(span);
        
    }
}

/////////////////////////// process cache log images //////////////////////////////

// get every table row containing a log
var pattern = /Nothing|AlternatingRow/;
var logRows = getElementByTagAndClass("td", pattern);

// for each log row
for (var li=0;li<logRows.length;li++)
{
    var logRow = logRows[li];
    var dateAndOwner = logRow.firstChild.textContent.replace(/^\s+/,"");
    var logDate = dateAndOwner.replace(/ by .*$/,"");
    var owner = dateAndOwner.replace(/^.*by /,"");
    
    // log images are held in a child table
    var imageTable = logRow.getElementsByTagName('table');

    // process the images for this log
    if (imageTable.length != 0)
    {
        // force new row
        var col=imagesPerRow;

        var it = imageTable[0];
        
        // remove colored border and increase size
    	it.style.padding = 0;
    	it.width="95%";
        
        // get all the image anchors
        var anchors = it.getElementsByTagName('a');

        // process each image anchor
        var noAnchors = anchors.length;
        for (var j=0;j<noAnchors;j++)
        {
            // create a new row if required
            var imageRow;
            if (col==imagesPerRow)
            {
                // add row
                imageRow=it.insertRow(-1);
                col=0;
            }

            // create a copy of the original anchor to modify - the original
            // is going to be removed later when the original row is removed
            var imageAnchor = anchors[j].cloneNode(true);
            
            // get anchor details
            var pictureTitle = imageAnchor.text;
            var largePicture = imageAnchor.href;
            var anchorTitle = imageAnchor.title; // contains title for lightbox

            // clean up picture title
            var pictureTitle = tidyTitle(pictureTitle);

            // extract log and possible description from title
            var log="";
            var description="";
            var parts=anchorTitle.match(/(log.aspx.*?LID=\d+).*>\s*(.*)\s*$/);
            if (parts)
            {
                log=parts[1];
                description=parts[2];
            }

            // use the same lightbox rel id for all images on the page
            imageAnchor.rel=lightboxRelId;

            // if description exists create image and lightbox formats
            var lightboxDescription="";
            var imageTitleDescription="";
            if (description != "")
            {
            	lightboxDescription = "<span style=\"font-weight: normal\">"+description+"</span><br>";
            	imageTitleDescription = " - " + description;
            }
            
            // create anchor title - this is used by lightbox
            var title = "\""+pictureTitle+"\""+
                        " by "+owner+" on "+logDate+"<br>"+lightboxDescription+
                        "<a href=\""+log+"\" target=\"_blank\">View Log</a>&nbsp;"+
                        "<a href=\"javascript:pp(\'"+largePicture+"\');\">Print Picture</a>";
            imageAnchor.title=title;

            // clean up the image tooltip
            var img = imageAnchor.firstChild;
            img.title = pictureTitle + imageTitleDescription;
            img.alt = pictureTitle;

            // change default photo.png image to thumbnail
            // unless we want to hide spoilers!
            if (showImage(pictureTitle))
            {
                // get href to log image and convert to thumbnail or display href
                var thumbPicture = largePicture.replace(/log/, "log\/"+imageSize);
                img.src = thumbPicture;
            }
            else
            {
                img.title = "** SPOILER ** " + pictureTitle;
                img.alt = "** SPOILER ** " + pictureTitle;
            }        
            // remove the non image anchor text
            imageAnchor.removeChild(imageAnchor.lastChild);
        
            // create a new cell in the row and add the anchor
            var imageCell = imageRow.insertCell(-1);
            imageCell.align="center";
            imageCell.width="100px";
            imageCell.height="81px";
            imageCell.style.border = "0px";
            imageCell.style.padding = "8px";
            imageCell.style.lineHeight = "0pt";  // remove space under image
            if (imageBoxColour)
            {
                imageCell.style.backgroundColor = imageBoxColour;
            }
	    imageCell.appendChild(imageAnchor);
            
            // create new anchor for text link to image
            var textAnchor = document.createElement('a');
            textAnchor.href = largePicture;
            textAnchor.title = pictureTitle; // sets tooltip
            textAnchor.textContent = pictureTitle;
            textAnchor.style.cssText = "text-decoration: none;";

            // create a new cell in the row and add the new anchor
            var textCell = imageRow.insertCell(-1);
            textCell.width="1200px";
            textCell.style.border = "0px";
            if (imageBoxColour)
            {
                textCell.style.backgroundColor = imageBoxColour;
            }

            // add anchor to cell
            textCell.appendChild(textAnchor);

            // add description if required
            if (showDescriptions)
            {
                textCell.appendChild(document.createElement('br'));
                span = document.createElement('span');
                span.style.cssText = "font-size: smaller";
                textCell.appendChild(span);
                
                descriptionNode = document.createTextNode(description);
                span.appendChild(descriptionNode);
            }
            
            // increment column count
            col++;
        }
        
        // span last cell if more than one row and row not fully populated
        // this ensures table background colour fills last row
        if ((noAnchors>imagesPerRow) && (col!=imagesPerRow))
        {
            textCell.colSpan = (imagesPerRow-col)*2+1;
        }
        

        // delete original row containing un-enhanced links 
        it.deleteRow(0);
    }

}