// Unless otherwise noted, methods are usable from any page.

// Constants for cart cookie format
var IMAGE_SEPARATOR = 'i';
var OPTION_SEPARATOR = 'o';
var FORMAT_SEPARATOR = 'f';

// The following global variables are used by the Search Results page
var imageAspect = 1;
var originalZoomSize = 0;
var detailZoomSize = 150;
var detailZoomGuidelinesOpacity = 0.2;
var originalZoomUrl = "";
var selectedStoryId;
var selectedImageId;

// rootUrl is used by javascript functions that navigate to other pages.
// Its value is set to the root url of the web application in MasterPage.master.
var rootUrl;

// resizeThresholdFactor is used by resizeImage to determine when to request a
// larger version of the loaded image from the server.
var resizeThresholdFactor = 1.1;

// scrollTarget and scrollContainer are used by scrollBy and doScrollBy
// to animate scrolling of the map and story list.
var scrollTarget;
var scrollContainer;

// Utility method to return the largest of two numbers.
function max(arg1, arg2) {
    return (arg1 > arg2) ? arg1 : arg2;
}

// Utility method to return the smallest of two numbers.
function min(arg1, arg2) {
    return (arg1 < arg2) ? arg1 : arg2;
}

// Utility method to scroll a control's parent so that the control is in the middle (vertically).
function scrollIntoView(controlRef) {
    var controlContainer = controlRef.parentNode;
    var overflowHeight = controlContainer.scrollHeight - controlContainer.clientHeight;
    
    if (overflowHeight > 0) {
        var idealControlHeight = (controlContainer.clientHeight - controlRef.offsetHeight) / 2;
        var actualControlHeight = controlRef.offsetTop - controlContainer.scrollTop;
        
        if (actualControlHeight > idealControlHeight) {
            var availableScroll = overflowHeight - controlContainer.scrollTop;
            var requiredScroll = min(availableScroll, actualControlHeight - idealControlHeight);
            scrollBy(controlContainer, requiredScroll);
        } else if (actualControlHeight < idealControlHeight) {
            var availableScroll = controlContainer.scrollTop;
            var requiredScroll = min(availableScroll, idealControlHeight - actualControlHeight);
            scrollBy(controlContainer, -requiredScroll);
        }
    }
}

function scrollBy(containerRef, increment) {
    scrollTarget = containerRef.scrollTop + increment;
    scrollContainer = containerRef;
    doScrollBy();
}

function doScrollBy() {
    var distToScroll = scrollTarget - scrollContainer.scrollTop;
    var step = 0;
    
    if (distToScroll > 1) {
        step = max(distToScroll * 0.3, 1);
    } else if (distToScroll < -1) {
        step = min(distToScroll * 0.3, -1);
    }
    
    if (0 != step) {
        setTimeout("doScrollBy()", 30);
        scrollContainer.scrollTop += step;
    }
}

// Utility method to render a currency value (without currency symbol).
function renderCurrency(value)
{
    var pounds = Math.floor(value);
    var pence = (value * 100) % 100;
    
    if (pence < 10) {
        return pounds + ".0" + pence;
    } else {
        return pounds + "." + pence;
    }
}

// Utility function for setting a session cookie's value.
function setCookie(cookieName, cookieValue) {
    document.cookie = cookieName + "=" + cookieValue;
}

// Utility function for retrieving a (session) cookie's value.
function getCookie(cookieName) {
    if (null != document.cookie) {
        var cookies = document.cookie.split(";");
        for (var i = 0; i < cookies.length; i++) {
            if (null != cookies[i]) {
                var cookieParts = cookies[i].split("=");
                if (null != cookieParts
                        && cookieParts.length >= 2
                        && null != cookieParts[0]
                        && cookieParts[0].replace(" ", "") == cookieName) {
                    return cookieParts[1]
                }
            }
        }
    }
    
    return "";
}

// Adds the specified image to the cart, by adding its id to the "c"
// session cookie. Will automatically warn the user if session cookies
// are not enabled by their browser.
// Usable from the Search Page only.
function addToCart(imageId) {
    var newCookie = getCookie("c");
    var oldCookie = newCookie;
    
    if (null == newCookie || newCookie.length == 0) {
        newCookie = imageId;
    } else {
        newCookie += (IMAGE_SEPARATOR + imageId);
    }
    
    setCookie("c", newCookie);
    var checkCookie = getCookie("c");
    
    if (checkCookie != newCookie) {
        if (checkCookie != null && checkCookie.length > 0)
        {
            setCookie("c", oldCookie);
            alert("Your cart is full.\n\n"
                + "If you would like to order more items, you can\n"
                + "either go to the cart page to submit your order\n"
                + "now and then begin a new order, or alternatively\n"
                + "you can email us the reference numbers of the\n"
                + "items you would like, and we will handle your\n"
                + "order manually.");
        }
        else
        {
            alert("Session cookies are required for the cart to\n"
                + "work, but your browser does not support this.\n\n"
                + "Please adjust your browser's privacy settings.\n\n"
                + "Alternatively, you can place an order by emailing\n"
                + "us the reference numbers of the items you would\n"
                + "like, and we will handle your order manually.");
        }
    } else {
        updateCartLinkVisibility(true);
    }
}

function setCartOptions(imageId, options) {
    var cartImages = getCookie("c").split(IMAGE_SEPARATOR);
    
    if (null != cartImages && cartImages.length > 0) {
        var newCookie = "";
        
        for (var i = 0; i < cartImages.length; i++) {
            if (newCookie.length > 0) {
                newCookie += IMAGE_SEPARATOR;
            }
            
            if (!isImage(imageId, cartImages[i])) {
                newCookie += cartImages[i];
            } else {
                newCookie += imageId;
                
                for (var j = 0; j < options.length; j++) {
                    var option = options[j];
                    if (option.quantity || option.size) {
                        newCookie += OPTION_SEPARATOR + (option.quantity || "") + (option.size ? FORMAT_SEPARATOR + option.size : "");
                    }
                }
            }
        }
        
        setCookie("c", newCookie);
    }
}

function isImage(imageId, cartImage) {
    if (cartImage == imageId)
    {
        return true;
    }
    else
    {
        var imageDesc = imageId + OPTION_SEPARATOR;
        return (cartImage.substr(0, imageDesc.length) == imageDesc)
    }
}

// Removes the specified image from the cart, by removing its id
// from the "c" session cookie.
// Usable from the Search and Cart pages only.
function removeFromCart(imageId, isCartView) {
    var cartImages = getCookie("c").split(IMAGE_SEPARATOR);
    
    if (null != cartImages && cartImages.length > 0) {
        var newCookie = "";
        
        for (var i = 0; i < cartImages.length; i++) {
            if (!isImage(imageId, cartImages[i])) {
                if (newCookie.length > 0) {
                    newCookie += IMAGE_SEPARATOR;
                }
                
                newCookie += cartImages[i];
            }
        }
        
        setCookie("c", newCookie);
    }

    if (isCartView) {
        imageElement = document.getElementById("image-" + imageId);
        imageElement.parentNode.removeChild(imageElement);
    } else {
        updateCartLinkVisibility(false);
    }
}

// Removes all images from the cart.
function clearCart() {
    setCookie("c", "");
}

// Checks if the specified image is currently in the user's cart.
function isInCart(imageId) {
    var cartImages = getCookie("c").split(IMAGE_SEPARATOR);

    if (cartImages && cartImages.length > 0) {
        for (var i = 0; i < cartImages.length; i++) {
            if (isImage(imageId, cartImages[i])) {
                return true;
            }
        }
    }

    return false;
}

function cartContentCount() {
    return getCookie("c").split(IMAGE_SEPARATOR).length;
}

// Checks whether there is anything in the user's cart.
function isCartEmpty() {
    var cartImages = getCookie("c").split(IMAGE_SEPARATOR);

    if (null != cartImages && cartImages.length > 0) {
        for (var i = 0; i < cartImages.length; i++) {
            if (cartImages[i] != "") {
                return false;
            }
        }
    }
    
    return true;
}

// Updates the visibility of the "Add to Cart" and "Remove from Cart" links.
// Usable from the Search Page only.
function updateCartLinkVisibility(isInCart) {
    var addToCartLink = document.getElementById("addToCartLink");
    var removeFromCartLink = document.getElementById("removeFromCartLink");

    if (addToCartLink != null && removeFromCartLink != null)
    {
        if (isInCart) {
            addToCartLink.style.display = "none";
            removeFromCartLink.style.display = "block";
        } else {
            addToCartLink.style.display = "block";
            removeFromCartLink.style.display = "none";
        }
    }
}

// If present, this function displays the repaintOverlay pane, which sits
// on top of everything and thus forces a complete repaint when it is hidden again.
// Required when working with Opera, which tries to optimize page refreshes and
// succeeds in messing up the page when our dynamic resizing code kicks in...
function startResize() {
    try {
        var repaintOverlay = document.getElementById("repaintOverlay");
        if (null != repaintOverlay) {
            repaintOverlay.style.display = "block";
        }
    } catch (e) {
        // Ignore
    }
}

// If present, this function hids the repaintOverlay pane. See startResize().
function endResize() {
    try {
        var repaintOverlay = document.getElementById("repaintOverlay");
        if (null != repaintOverlay) {
            repaintOverlay.style.display = "none";
        }
    } catch (e) {
        // Ignore
    }
}

// Submits a new keyword search.
function submitSearch() {
    document.getElementById("SearchForm").submit();
}

// Searches for images from a specified story.
function findByStoryId(storyId) {
    window.location.href = rootUrl + "Gallery/Search.aspx?storyId=" + storyId;
}

// Searches for images in a specified gallery.
function findByGalleryId(galleryId) {
    window.location.href = rootUrl+ "Gallery/Search.aspx?galleryId=" + galleryId;
}

// Searches for images that are similar to the specified image.
function findByExample(imageId) {
    window.location.href = rootUrl + "Gallery/Search.aspx?exampleImageId=" + imageId;
}

// Loads a new image into the main viewer, resizing and
// moving the detail information if required.
// Only usable from the search page (of course).
function loadSearchResultsImage(imageId, aspect, zoomHeight, ZoomCentreX, ZoomCentreY) {
    var detailImage = document.getElementById("detailImage");
    var zoomImage = document.getElementById('zoomImage');
    var zoomObjectsContainer = document.getElementById('zoomObjectsContainer');
    
    zoomObjectsContainer.style.display = "none";
    zoomImage.src = detailImage.src = "../Images/wait.gif";
    removeZoomHighlight();
    
    setTimeout(function() {_loadSearchResultsImage(imageId, aspect, zoomHeight, ZoomCentreX, ZoomCentreY);}, 1000);
}

function _loadSearchResultsImage(imageId, aspect, zoomHeight, ZoomCentreX, ZoomCentreY) {
    startResize();
    try {
        var isZoomAvailable = (zoomHeight > 0);
        var imageIdControl = document.getElementById("imageId");
        var storyIdControl = document.getElementById("storyId");
        var imageTitlePane = document.getElementById("imageTitlePane");
        var storyTitlePane = document.getElementById("storyTitlePane");
        var storyBodyPane = document.getElementById("storyBodyPane");
        var findByStoryLink = document.getElementById("findByStoryLink");
        var findByExampleLink = document.getElementById("findByExampleLink");
        var detailImage = document.getElementById("detailImage");
        var zoomImage = document.getElementById('zoomImage');
        var zoomObjectsContainer = document.getElementById('zoomObjectsContainer');
        var zoomToggleLink = document.getElementById("zoomToggleLink");

        var imageTitleDiv = document.getElementById("imageTitle-" + imageId);
        var imageRefDiv = document.getElementById("imageRef-" + imageId);
        var storyIdDiv = document.getElementById("storyId-" + imageId);
        var storyTitleDiv = document.getElementById("storyTitle-" + imageId);
        var imageDateDiv = document.getElementById("imageDate-" + imageId);
        var storyBodyDiv = document.getElementById("storyBody-" + imageId);

        var imageTitle = imageTitleDiv.innerHTML;
        var imageRef = imageRefDiv.innerHTML;
        var storyId = storyIdDiv.innerHTML;
        var storyTitle = storyTitleDiv.innerHTML;
        var imageDate = imageDateDiv.innerHTML;
        var storyBody = storyBodyDiv.innerHTML;
        var storyTitleSeparator = (null == storyTitle || "" == storyTitle || null == imageDate || "" == imageDate) ? "" : ", ";

        if (null == storyBody || "" == storyBody) {
            storyBody = "No further information is available for this image.";
        }

        // Bug with IE 5.2 on Mac - set innerHTML to "" before
        // setting it to a new value.
        imageTitlePane.innerHTML = "";
        storyTitlePane.innerHTML = "";
        storyBodyPane.innerHTML = "";

        imageTitlePane.innerHTML = imageTitle + " <strong class=\"imageRef\">[item&nbsp;reference:&nbsp;</strong>" + imageRef + "<strong class=\"imageRef\">]</strong>";
        storyTitlePane.innerHTML = storyTitle + storyTitleSeparator + imageDate;
        storyBodyPane.innerHTML = storyBody;

        imageAspect = aspect;
        originalZoomUrl = rootUrl + "ImageServer.ashx?id=" + imageId;
        originalZoomSize = 0;
        
        if (isZoomAvailable) {
            zoomToggleLink.style.display = "block";
            zoomImage.src = originalZoomUrl + "&type=Zoom&size=" + detailZoomSize + "&zoom=true";
            zoomImage.style.height = detailZoomSize + "px";
            zoomImage.style.width = detailZoomSize + "px";
			zoomImage.style.border = "";
            zoomObjectsContainer.style.display = "";
			zoom = {size: zoomHeight, centre: {x: ZoomCentreX, y: ZoomCentreY}};
        } else {
            zoomToggleLink.style.display = "none";
            zoomImage.style.display = "none";
			zoomImage.style.border = "none";
			zoom = null;
        }

        resizeSearchPage(false);

        selectedStoryId = storyId;
        selectedImageId = imageId;
        
        if (null != findByStoryLink) {
            if (storyId != null && storyId != "") {
                findByStoryLink.style.display = "block";
            } else {
                findByStoryLink.style.display = "none";
            }
        }
        
        findByExampleLink.style.display = "block";
        updateCartLinkVisibility(isInCart(selectedImageId));        
    } catch (e) {
    // Ignore errors. Tends to happen in WebbIE, presumably since it doesn't render anything.
    } finally {
        endResize();
    }
}

// Resizes the various parts of the search page after a new image is loaded or
// when the browser window is resized. If suppressReload is false, the main
// image is re-loaded if the resize causes it to be significantly larger than
// its "real" size - see resizeThresholdFactor.
// Only usable from the search page (of course).
function resizeSearchPage(suppressReload) {
    startResize();
    try {
        var contentArea = document.getElementById("contentArea");
        var resultsContainer = document.getElementById("resultsContainer");
        var sidebarContainer = document.getElementById("sidebarContainer");
        var imageContainer = document.getElementById("imageContainer");
        var descriptionContainer = document.getElementById("descriptionContainer");
        var storyBodyContainer = document.getElementById("storyBodyContainer");
        var storyBodyPane = document.getElementById("storyBodyPane");
        var imageTitlePane = document.getElementById("imageTitlePane");
        var storyTitlePane = document.getElementById("storyTitlePane");
        var storyBodyPane = document.getElementById("storyBodyPane");
        var pageControlContainer = document.getElementById("pageControlContainer");
        var detailImage = document.getElementById("detailImage");

        if (null != pageControlContainer) {
          resultsContainer.style.height = (pageControlContainer.offsetTop - resultsContainer.offsetTop) + "px";
        } else {
          resultsContainer.style.height = (contentArea.clientHeight - resultsContainer.offsetTop - 1) + "px";
        }
        
        var maxImageWidth;
        var maxImageHeight;
        
        if (imageAspect > 1) {
            sidebarContainer.style.width = "100px";
            sidebarContainer.style.height = "200px";
            descriptionContainer.style.height = "120px";
            descriptionContainer.style.width = max(resultsContainer.offsetLeft - 6, 0) + "px";
            maxImageHeight = max(descriptionContainer.offsetTop - 3, 0);
        } else {
            sidebarContainer.style.height = "100px";
            descriptionContainer.style.width = sidebarContainer.style.width = "180px";
            descriptionContainer.style.height = max(contentArea.clientHeight - sidebarContainer.offsetHeight - 20, 0) + "px";
            maxImageHeight = max(contentArea.clientHeight - 3, 100);
        }

        imageContainer.style.height = maxImageHeight + "px";
        imageContainer.style.width = (maxImageWidth = max(sidebarContainer.offsetLeft - 6, 100)) + "px";
        storyTitlePane.style.top = (imageTitlePane.offsetTop + imageTitlePane.offsetHeight) + "px";
        storyBodyPane.style.top = (storyTitlePane.offsetTop + storyTitlePane.offsetHeight) + "px";
        storyBodyPane.style.height = (descriptionContainer.clientHeight - storyBodyPane.offsetTop - 1) + "px";
        storyBodyPane.style.width = (descriptionContainer.clientWidth) + "px";

        var imageSize = resizeImage(imageAspect, detailImage, maxImageWidth, maxImageHeight, originalZoomUrl, originalZoomSize, suppressReload, false, imageContainer);
        
        if (null != imageSize) {
            originalZoomSize = imageSize;
        }
        
        centerResultsContainer();
        drawZoomHighlight();
    } catch (e) {
    // Ignore errors - tends to happen in WebbIE, presumably since it doesn't render graphics at all.
    } finally {
        endResize();
    }
}

function isZoomVisible() {
    return zoom && document.getElementById("zoomImageContainer").style.display != "none";
}

function initZoom() {
    if (getCookie("z")=="n") {
        hideZoom();
    } else {
        showZoom();
    }
}

function toggleZoom() {
    if (isZoomVisible()) {
        hideZoom();
    } else {
        showZoom();
    }
}

function showZoom() {
    var zoomImageContainer = document.getElementById("zoomImageContainer");
    var showZoomLink = document.getElementById("showZoom");
    var hideZoomLink = document.getElementById("hideZoom");

    zoomImageContainer.style.display = "";
    showZoomLink.style.display = "none";
    showZoomLink.accessKey = "";
    hideZoomLink.style.display = "";
    hideZoomLink.accessKey = "z";
    drawZoomHighlight();
    setCookie("z", "y");
}

function hideZoom() {
    var zoomImageContainer = document.getElementById("zoomImageContainer");
    var showZoomLink = document.getElementById("showZoom");
    var hideZoomLink = document.getElementById("hideZoom");

    zoomImageContainer.style.display = "none";
    showZoomLink.style.display = "";
    showZoomLink.accessKey = "z";
    hideZoomLink.style.display = "none";
    hideZoomLink.accessKey = "";
    removeZoomHighlight();
    setCookie("z", "n");
}

function removeZoomHighlight() {
    var zoomHighlightCanvas = document.getElementById("zoomHighlightCanvas");
    
    if (zoomHighlightCanvas && zoomHighlightCanvas.getContext) {
        var imageContainer = document.getElementById("imageContainer");
        var graphicsContext = zoomHighlightCanvas.getContext("2d");
        graphicsContext.clearRect(0, 0, imageContainer.offsetWidth, imageContainer.offsetHeight);
    }
}

function drawZoomHighlight() {
    removeZoomHighlight();
    
    if (!isZoomVisible()) {
        return;
    }
    
    var zoomHighlightCanvas = document.getElementById("zoomHighlightCanvas");
    
    if (zoomHighlightCanvas && zoomHighlightCanvas.getContext) {
        var imageContainer = document.getElementById("imageContainer");
        var detailImage = document.getElementById("detailImage");
        var zoomImage = document.getElementById('zoomImage');
    
        zoomHighlightCanvas.width = imageContainer.offsetWidth;
        zoomHighlightCanvas.height = imageContainer.offsetHeight;
        
        var graphicsContext = zoomHighlightCanvas.getContext("2d");
        graphicsContext.globalAlpha = detailZoomGuidelinesOpacity;
        graphicsContext.strokeStyle = "rgb(255, 255, 255)";
        
        var imageTop = detailImage.offsetTop;
        var imageLeft = detailImage.offsetLeft;
        var imageHeight = detailImage.offsetHeight;
        var imageWidth = detailImage.offsetWidth;
        
        var zoomAreaX = imageLeft + imageWidth * zoom.centre.x;
        var zoomAreaY = imageTop + imageHeight * zoom.centre.y;
        var zoomAreaSize = zoom.size * imageHeight;
        var zoomAreaLeft = zoomAreaX - (zoomAreaSize / 2);
        var zoomAreaTop = zoomAreaY - (zoomAreaSize / 2);
        var zoomAreaRight = zoomAreaLeft + zoomAreaSize;
        var zoomAreaBottom = zoomAreaTop + zoomAreaSize;
        
        /*graphicsContext.fillStyle = "rgb(0, 0, 0)";
        graphicsContext.beginPath();
        graphicsContext.fillRect(imageLeft, imageTop, imageWidth, (zoomAreaTop - imageTop));
        graphicsContext.fillRect(imageLeft, (zoomAreaTop + zoomAreaSize), imageWidth, ((imageTop + imageHeight) - (zoomAreaTop + zoomAreaSize)));
        graphicsContext.fillRect(imageLeft, zoomAreaTop, (zoomAreaLeft - imageLeft), zoomAreaSize);
        graphicsContext.fillRect((zoomAreaLeft + zoomAreaSize), zoomAreaTop, ((imageLeft + imageWidth) - (zoomAreaLeft + zoomAreaSize)), zoomAreaSize);
        graphicsContext.closePath();*/
        
        /*var zoomViewerLeft = zoomImage.offsetLeft;
        var zoomViewerTop = zoomImage.offsetTop;
        var zoomViewerRight = zoomViewerLeft + zoomImage.offsetWidth;
        var zoomViewerBottom = zoomViewerTop + zoomImage.offsetHeight;

        // top
        graphicsContext.fillStyle = "rgb(0, 0, 0)";
        graphicsContext.beginPath();
        graphicsContext.moveTo(zoomViewerLeft, zoomViewerTop);
        graphicsContext.lineTo(zoomAreaLeft, zoomAreaTop);
        graphicsContext.lineTo(zoomAreaRight, zoomAreaTop);
        graphicsContext.lineTo(zoomViewerRight, zoomViewerTop);
        graphicsContext.stroke();
        
        if (zoomViewerTop < zoomAreaTop) {
            graphicsContext.fill();
        }
        
        graphicsContext.closePath();
        
        // bottom
        graphicsContext.fillStyle = "rgb(100, 100, 100)";
        graphicsContext.beginPath();
        graphicsContext.moveTo(zoomViewerLeft, zoomViewerBottom);
        graphicsContext.lineTo(zoomAreaLeft, zoomAreaBottom);
        graphicsContext.lineTo(zoomAreaRight, zoomAreaBottom);
        graphicsContext.lineTo(zoomViewerRight, zoomViewerBottom);
        graphicsContext.stroke();
        
        if (zoomViewerBottom > zoomAreaBottom) {
            graphicsContext.fill();
        }
        
        graphicsContext.closePath();
        
        // left
        if (zoomViewerLeft < zoomAreaLeft) {
	        graphicsContext.fillStyle = "rgb(50, 50, 50)";
            graphicsContext.beginPath();
            graphicsContext.moveTo(zoomViewerLeft, zoomViewerTop);
            graphicsContext.lineTo(zoomAreaLeft, zoomAreaTop);
            graphicsContext.lineTo(zoomAreaLeft, zoomAreaBottom);
            graphicsContext.lineTo(zoomViewerLeft, zoomViewerBottom);
            graphicsContext.stroke();
            graphicsContext.fill();
            graphicsContext.closePath();
        }
        
        // right
        if (zoomViewerRight > zoomAreaRight) {
	        graphicsContext.fillStyle = "rgb(50, 50, 50)";
            graphicsContext.beginPath();
            graphicsContext.moveTo(zoomViewerRight, zoomViewerTop);
            graphicsContext.lineTo(zoomAreaRight, zoomAreaTop);
            graphicsContext.lineTo(zoomAreaRight, zoomAreaBottom);
            graphicsContext.lineTo(zoomViewerRight, zoomViewerBottom);
            graphicsContext.stroke();
            graphicsContext.fill();
            graphicsContext.closePath();
        }*/
        
        graphicsContext.beginPath();
        graphicsContext.rect(zoomAreaLeft, zoomAreaTop, zoomAreaSize, zoomAreaSize);
	    graphicsContext.stroke();
        graphicsContext.closePath();
    }
}

function centerResultsContainer() {
    var containerRef = document.getElementById("resultsContainer");
    var overflow = containerRef.scrollWidth - containerRef.clientWidth;
    containerRef.scrollLeft = max(overflow/2, 0);
}

// Resizes the various parts of the home page after a browser resize event.
// Only usable from the home page (of course).
function resizeHomePage(suppressReload) {
    startResize();
    try {
        var contentArea = document.getElementById("contentArea");
        var landscapeImageWidth = Math.round(contentArea.offsetWidth * 0.19);
        var landscapeImageHeight = Math.round(contentArea.offsetHeight * 0.24);
        var portraitImageWidth = Math.round(contentArea.offsetWidth * 0.16);
        var portraitImageHeight = Math.round(contentArea.offsetHeight * 0.40);
        resizeImage(1.5, document.getElementById("gallery-0"), landscapeImageWidth, landscapeImageHeight, null, null, true, true, document.getElementById("gallery-0-container"));
        resizeImage(1.5, document.getElementById("gallery-1"), landscapeImageWidth, landscapeImageHeight, null, null, true, true, document.getElementById("gallery-1-container"));
        resizeImage(1.5, document.getElementById("gallery-2"), landscapeImageWidth, landscapeImageHeight, null, null, true, true, document.getElementById("gallery-2-container"));
        resizeImage(1.5, document.getElementById("gallery-3"), landscapeImageWidth, landscapeImageHeight, null, null, true, true, document.getElementById("gallery-3-container"));
        resizeImage(1.5, document.getElementById("pick1"), landscapeImageWidth, landscapeImageHeight, null, null, true, true, document.getElementById("pick1Container"));
        resizeImage(1.5, document.getElementById("pick2"), landscapeImageWidth, landscapeImageHeight, null, null, true, true, document.getElementById("pick2Container"));
        resizeImage(1.5, document.getElementById("pick3"), landscapeImageWidth, landscapeImageHeight, null, null, true, true, document.getElementById("pick3Container"));
        resizeImage(2/3, document.getElementById("pick4"), portraitImageWidth, portraitImageHeight, null, null, true, true, document.getElementById("pick4Container"));
        resizeImage(2/3, document.getElementById("map"), portraitImageWidth, portraitImageHeight, null, null, true, false, document.getElementById("mapContainer"));
        resizeImage(2140/872, document.getElementById("logo"), Math.round(contentArea.offsetWidth * 0.6), Math.round(contentArea.offsetHeight * 0.4), null, null, true, false);
    } catch (e) {
    // Ignore errors. Tends to happen in WebbIE, presumably since it doesn't render anything.
    } finally {
        endResize();
    }
}

// Resizes the various parts of the story (map) page after a browser resize event.
// Only usable from the story page (of course).
function resizeStoryPage() {
    startResize();
    
    try {
        var contentArea = document.getElementById("contentArea");
        var mapContainer = document.getElementById("mapContainer");
        var storyListContainer = document.getElementById("storyListContainer");
        mapContainer.style.height = (contentArea.clientHeight - 5) + "px";
        storyListContainer.style.height = (contentArea.clientHeight - 5) + "px";
        storyListContainer.style.width = max(contentArea.clientWidth - mapContainer.offsetWidth - 6, 0) + "px";
        var mapOverflowHeight = mapContainer.scrollHeight - mapContainer.clientHeight;
        if (mapOverflowHeight > 0) {
            mapContainer.scrollTop = mapOverflowHeight / 2;
        }
    } catch (e) {
    // Ignore errors. Tends to happen in WebbIE, presumably since it doesn't render anything.
    } finally {
        endResize();
    }
}

// Resizes the various parts of the cart page after a browser resize event.
// Only usable from the cart page (of course).
function resizeCartPage() {
    startResize();
    
    try {
        var contentArea = document.getElementById("contentArea");
        var thumbnailContainer = document.getElementById("thumbnailContainer");
        var orderFormContainer = document.getElementById("orderFormContainer");
        thumbnailContainer.style.height = (contentArea.clientHeight - 5) + "px";
        orderFormContainer.style.height = (contentArea.clientHeight - 5) + "px";
        orderFormContainer.style.width = max(contentArea.clientWidth - thumbnailContainer.offsetWidth - 6, 0) + "px";
    } catch (e) {
    // Ignore errors. Tends to happen in WebbIE, presumably since it doesn't render anything.
    } finally {
        endResize();
    }
}

// Adds an additional format for an image on the cart page
function addFormat(imageId) {
    var formatParentRef = document.getElementById("image-" + imageId + "-options");
    var formatTemplateRef = document.getElementById("image-" + imageId + "-option-0");
    var newFormatRef = formatTemplateRef.cloneNode(true);
    var selectRefs = newFormatRef.getElementsByTagName("SELECT");
    for (var i = 0; i < selectRefs.length; i++) {
        selectRefs[i].selectedIndex = 0;
    }
    formatParentRef.appendChild(newFormatRef);
}

function optionsChanged(imageId) {
    options = getImageOptions(imageId);
    setCartOptions(imageId, options);
    updateTotalPrice();
}

function value(selectRef) {
    return (selectRef && selectRef.selectedIndex && selectRef.options) ? selectRef.options[selectRef.selectedIndex].value : null;
}

function getImageOptions(imageId) {
    var quantityRefs = document.getElementsByName("image-" + imageId + "-quantity");
    var result = [];
    
    for (var i = 0; i < quantityRefs.length; i++) {
        var quantityRef = quantityRefs[i];
        var formatRef = null;
        var thisFormatContainer = quantityRef.parentNode.parentNode.parentNode;
        var siblings = thisFormatContainer.getElementsByTagName("SELECT");
        
        for (var j = 0; j < siblings.length; j++) {
            if (siblings[j].name == "image-" + imageId + "-format") {
                formatRef = siblings[j];
            }
        }
        
        result[result.length] = {quantity: value(quantityRef), size: value(formatRef)};
    }
    
    return result;
}

// Fired when one of the formats of an image changes. Triggers a cookie update
// and a price calculation.
function updateImagePrice(imageId) {
    var options = getImageOptions(imageId);
    var priceDiv = document.getElementById("image-" + imageId + "-price");
    var imagePrice = 0;
    
    for (var i = 0; i < options.length; i++) {
        imagePrice += calculateImagePrice(imageId, options[i], true);
    }
   
    var imagePriceText = renderCurrency(imagePrice);
    
    // Bug with IE 5.2 on Mac - set innerHTML to "" before
    // setting it to a new value.
    priceDiv.innerHTML = "";
    priceDiv.innerHTML = imagePriceText;
    
    return imagePrice;
}

// Updates the total price on the cart page, following a change
// to the quantity or size field of an image.
// Only usable from the cart page (of course).
function updateTotalPrice()
{
    var priceDiv = document.getElementById("totalPriceDiv");
    var totalPrice = 0;
    
    for (var i = 0; i < uniqueImageIds.length; i++) {
        if (uniqueImageIds[i]) {
            totalPrice += updateImagePrice(uniqueImageIds[i]);
        }
    }

    var totalPriceText = renderCurrency(totalPrice);
    priceDiv.innerHTML = "";
    priceDiv.innerHTML = totalPriceText;
    
    return totalPrice;
}

// Utility method used by updateImagePrice and updateTotalPrice to calculate
// the price of a specified order item.
// Only usable (naturally) from the cart page.
function calculateImagePrice(imageId, option, alertErrors) {
    if (!(option && option.quantity)) {
        return 0;
    }

    var perItemPrice = getPerItemPrice(imageId);
    
    if (perItemPrice) {
        // This image is a standard product with a per-item price
        return perItemPrice * option.quantity;
    } else if (option.size) {
        var unitPrice = document.getElementById("format-" + option.size + "-price").value;
        return unitPrice * option.quantity;
    } else {
        // Not a standard product and no format selected
        return 0;
    }
}

function getPerItemPrice(imageId) {
    var perItemPriceElement = document.getElementById("image-" + imageId + "-price-each");
    if (perItemPriceElement) {
        return perItemPriceElement.value;
    } else {
        return null;
    }
}

// Validates and submits an order from the checkout.
// Only usable (funnily enough) from the checkout.
function placeOrder() {
    var agreedToTermsRef = document.getElementById("agreeToTerms");

    if (!agreedToTermsRef.checked) {
        alert("Please read and indicate your agreement to the terms and conditions.");
        // EARLY RETURN - no agreement to terms and conditions
        return;
    }

    var skipPersonalConfirmation = false;
    var numFullySpecifiedItems = 0;
    var numUnspecifiedItems = 0;

    for (var i = 0; i < uniqueImageIds.length; i++) {
        if (uniqueImageIds[i]) {
            var perItemPrice = getPerItemPrice(uniqueImageIds[i]);
            var options = getImageOptions(uniqueImageIds[i]);
            for (var j = 0; j < options.length; j++) {
                var option = options[j];
                if (option.quantity && (perItemPrice || option.size)) {
                    numFullySpecifiedItems++;
                } else {
                    numUnspecifiedItems++;
                }
            }
        }
    }

    if (numFullySpecifiedItems == 0) {
        alert("You have not specified anything to order. Please review print sizes and quantities on the left of the screen.");
        // EARLY RETURN - No images selected for ordering.
        return;
    } else if (numUnspecifiedItems > 0) {
        var pluralBit = (numUnspecifiedItems == 1) ? "1 item has" : numUnspecifiedItems + " items have"
        if (!confirm(pluralBit + " no size or quantity specified, and will not be included in your order.\n\nDo you want to order the other items anyway?")) {
            // EARLY RETURN - One or more images not selected, and user decides to review.
            return;
        }
    }

    // Submit the order form at last!
    var orderForm = document.getElementById("OrderForm");
    orderForm.style.display = "none";
    document.getElementById("submitOrder").value = "true";
    orderForm.submit();
}

// Reloads the specified order cookie and reloads the current url.
function reloadOrder(orderCookie) {
    setCookie("c", orderCookie);
    window.history.go(0);
}

// Resizes the specified image (in imageRef, with natural aspect of imageAspect) to fit in
// its container (whose dimensions are imageCellWidth and imageCellHeight). If suppressReload
// is false, the image is re-requested from originalUrl at the new size if originalSize
// (which should be the natural size of the current copy of the image) is significantly
// smaller than the new size.
function resizeImage(imageAspect, imageRef, imageCellWidth, imageCellHeight, originalUrl, originalSize, suppressReload, cropToFit, containerRef) {
    try {
        var imageCellAspect = imageCellWidth / imageCellHeight;
        var imageWidth;
        var imageHeight;
        imageRef.parentNode.style.display = "";

        if (cropToFit) {
            if (imageAspect < imageCellAspect) {
              imageWidth = imageCellWidth;
              imageHeight = Math.round(imageWidth / imageAspect);
            } else {
              imageHeight = imageCellHeight;
              imageWidth = Math.round(imageHeight * imageAspect);
            }
        } else {
            if (imageAspect > imageCellAspect) {
              imageWidth = imageCellWidth;
              imageHeight = Math.round(imageWidth / imageAspect);
            } else {
              imageHeight = imageCellHeight;
              imageWidth = Math.round(imageHeight * imageAspect);
            }
        }
        
        imageRef.style.height = imageHeight + "px";
        imageRef.style.width = imageWidth + "px";

        if (null != containerRef) {
            imageRef.style.position = "relative";
            imageRef.style.top = Math.round((imageCellHeight - imageHeight) / 2.0) + "px";
            imageRef.style.left = Math.round((imageCellWidth - imageWidth) / 2.0) + "px";
        }
        
        if (null != containerRef) {
            containerRef.style.width = imageCellWidth + "px";
            containerRef.style.height = imageCellHeight + "px";
        }
        
        if ((originalUrl != null) && (originalUrl != "") && !suppressReload) {
            var imageSize = (imageAspect > 1) ? imageWidth : imageHeight;
            
            if ((imageSize > (resizeThresholdFactor * originalSize))) {
                originalSize = imageSize;
                imageRef.src = originalUrl + "&size=" + originalSize;
                return imageSize;
            }
        }
        
        return null;
    } catch (e) {
    // Ignore errors. Tends to happen in WebbIE, presumably since it doesn't render anything.
    }
}

// Moves to the specified page of the search results.
// Usable only from the search page.
function moveSearchResultsPage(pageNumber) {
    try {
        var PageForm = document.getElementById("PageForm");
        PageForm.page.value = pageNumber;
        PageForm.submit();
    } catch (e) {
    // Ignore errors. Tends to happen in WebbIE, presumably since it doesn't render anything.
    }
}

// Highlights the appropriate story and marker, and ensures that both are
// scrolled into view.
// Usable only from the stories (map) page.
function mouseOverStory(storyId, scrollToMarker, scrollToLink) {
    var marker = document.getElementById("story-" + storyId + "-marker");
    var storyLink = document.getElementById("story-" + storyId + "-link");
    
    storyLink.className = "highlight";
    
    if (scrollToLink) {
        scrollIntoView(storyLink);
    }
    
    if (null != marker) {
        marker.firstChild.firstChild.className = "markerHighlight";
        marker.style.zIndex = 1;
        
        if (scrollToMarker) {
            scrollIntoView(marker);
        }
    }
}

// Removes the highlight from the appropriate story and marker.
// Usable only from the stories (map) page.
function mouseOutStory(storyId) {
    var marker = document.getElementById("story-" + storyId + "-marker");
    var storyLink = document.getElementById("story-" + storyId + "-link");
    
    storyLink.className = "";

    if (null != marker) {
        marker.firstChild.firstChild.className = "marker";
        marker.style.zIndex = "";
    }
}

// Resizes a static page following a browser resize event.
// Used from InfoPage.master for rendering all static pages.
function resizeTextPage() {
    startResize();
    try {
        var contentArea = document.getElementById("contentArea");
        var textPane = document.getElementById("textPane");
        
        textPane.style.width = (contentArea.clientWidth - 6) + "px";
        textPane.style.height = (contentArea.clientHeight - 4) + "px";
    } catch (e) {
    // Ignore errors. Tends to happen in WebbIE, presumably since it doesn't render anything.
    } finally {
        endResize();
    }
}

