// ==UserScript==
// @name          Geocaching Thumbnails
// @namespace     http://benchmarks.org.uk/geothumbs/
// @version       2.0
// @description   Changes the default picture icons on cache pages to image thumbnails
// @include       http://www.geocaching.com/seek/cache*
// ==/UserScript==

// Copyright (C) 2009 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;

// 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]";

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

// gets all elements of the specified tag with the specifed 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.toLowerCase();
    while((element = element.parentNode) && !parentElement)
    {
        if (element.tagName.toLowerCase() == tagname)
        {
            parentElement = element;
        }
    }
    return parentElement;
}

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

// 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();
} 

// 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);
    
    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("CacheOwner").lastChild.text;

    var ownerImages=null;
    var imagesElement = document.getElementById("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)
    {
        // go to enclosing row
        parentRow = climbTo(imagesElement, 'tr');
        
        // add a new row after this one
        r = document.createElement('tr');
        insertAfter(parentRow, r);
    
        // add a cell to hold our owner image table 
        d = r.insertCell(-1);
        if (ownerImagesSpanPage)
        {
            // make cell cross the 2 main page columns to give full page width
            d.colSpan = "2";
            d.style.cssText = "padding-right: 15px;";
            d.bgcolor="#ffffff";
        }
        
        // create a table for our title row and image rows
        t = document.createElement('table');
        t.cellPadding = "8";
        t.cellSpacing = "0";
        t.style.cssText = "border:1px solid #6A97B1; background:#F1F5F8";
        t.width="100%";
    
        // put the table in the new cell
        d.appendChild(t);
    
        // create a title row with cell
        titleRow = t.insertRow(-1);
        titleCell = titleRow.insertCell(-1);
        titleCell.className = "containerHeader";
        titleCell.style.cssText="padding-top: 5px; padding-bottom: 5px;";
        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);
                    imageRow.style.cssText = "background:#F1F5F8";
    
                    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;
                
                // create anchor title - this is used by lightbox
                var title = "\""+pictureTitle+"\" by "+ownerName+"<br>" +
                    "<a href=\"javascript:pp('"+largePicture+"')\;\">Print Picture</a>";
               	imageAnchor.title=title;
               	
                // clean up the tooltip
                var img = imageAnchor.firstChild;    
                img.title = pictureTitle;
                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;
                    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.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);                
                    pictureTitle = pictureTitle + " [edit]";
                }
                else
                {
                    // new anchor for link to image
                    textAnchor = document.createElement('a');
                    textAnchor.href = largePicture;
                }    
                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="600px";
                textCell.style.cssText="padding-left: 0"; // force text close to image
                
                // add anchor to cell
                textCell.appendChild(textAnchor);

                col++;
            }
        }
        
        // remove original images - go to enclosing <font> and remove it
        originalPara = climbTo(imagesElement, 'font');
        originalPara.parentNode.removeChild(originalPara); 
    }
}

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

// get every table row containing a log
var pattern = /logContainer(Alt)?Row/;
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.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];
        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];  // not currently used
            }

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

            // create anchor title - this is used by lightbox
            var title = "\""+pictureTitle+"\""+
                        " by "+owner+" on "+logDate+"<br>"+
                    "<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;
            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.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="600px";

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

            col++;
        }

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

}