// FancyZoom.js - v1.1 - http://www.fancyzoom.com
//
// Copyright (c) 2008 Cabel Sasser / Panic Inc
// All rights reserved.
//
//     Requires: FancyZoomHTML.js
// Instructions: Include JS files in page, call setupZoom() in onLoad. That's it!
//               Any <a href> links to images will be updated to zoom inline.
//               Add rel="nozoom" to your <a href> to disable zooming for an image.
//
// Redistribution and use of this effect in source form, with or without modification,
// are permitted provided that the following conditions are met:
//
// * USE OF SOURCE ON COMMERCIAL (FOR-PROFIT) WEBSITE REQUIRES ONE-TIME LICENSE FEE PER DOMAIN.
//   Reasonably priced! Visit www.fancyzoom.com for licensing instructions. Thanks!
//
// * Non-commercial (personal) website use is permitted without license/payment!
//
// * Redistribution of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Redistribution of source code and derived works cannot be sold without specific
//   written prior permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var includeCaption = true; // Turn on the "caption" feature, and write out the caption HTML
var zoomTime       = 5;    // Milliseconds between frames of zoom animation
var zoomSteps      = 15;   // Number of zoom animation frames
var includeFade    = 1;    // Set to 1 to fade the image in / out as it zooms
var minBorder      = 90;   // Amount of padding between large, scaled down images, and the window edges
var shadowSettings = '0px 5px 25px rgba(0, 0, 0, '; // Blur, radius, color of shadow for compatible browsers

// Init. Do not add anything below this line, unless it's something awesome.
var myWidth = 0, myHeight = 0, myScroll = 0; myScrollWidth = 0; myScrollHeight = 0;
var zoomOpen = false, preloadFrame = 1, preloadActive = false, preloadTime = 0, imgPreload = new Image();

var zoomActive = new Array(); var zoomTimer  = new Array();
var zoomOrigW  = new Array(); var zoomOrigH  = new Array();
var zoomOrigX  = new Array(); var zoomOrigY  = new Array();

var zoomID      = "ZoomBox";
var theID       = "ZoomImage";
var preloadFrom = false;

var browserIsIE = (navigator.userAgent.indexOf("MSIE") != -1);

// Zoom: Load an image into an image object. When done loading, function sets preloadActive to false,
// so other bits know that they can proceed with the zoom.
// Preloaded image is stored in imgPreload and swapped out in the zoom function.
function zoomPreload(from) {
    var theimage = from.getAttribute("href");

    // Only preload if we have to, i.e. the image isn't this image already
    if (imgPreload.src.indexOf(theimage.substr(theimage.lastIndexOf("/"))) == -1) {
        preloadActive = true;
        imgPreload = new Image();

        // Set a function to fire when the preload is complete, setting flags along the way.
        imgPreload.onload = function() {
            if (preloadFrom) {
//                $("ZoomSpin").setStyle({visibility: "hidden"});
                $('indicator1').setStyle({visibility: "hidden"});
                zoomIn(preloadFrom);
                preloadFrom = false;
            }    
            preloadActive = false;
        }

        // Load it!
        imgPreload.src = theimage;
    }
}

// ZOOM CLICK: We got a click! Should we do the zoom? Or wait for the preload to complete?
// todo?: Double check that imgPreload src = clicked src
function zoomClick(from, evt) {
    var shift = getShift(evt);

    // Check for Command / Alt key. If pressed, pass them through -- don't zoom!
    if (! evt && window.event && (window.event.metaKey || window.event.altKey)) {
        return true;
    } else if (evt && (evt.metaKey|| evt.altKey)) {
        return true;
    }

    // Get browser dimensions
    getSize();

    // make sure to preload in case mouseover event hasn't been fired
    if (!imgPreload.src) zoomPreload(from);
    
    // If preloading still, wait, and display the spinner.
    if (preloadActive) {
        preloadFrom = from;
        $("indicator1").setStyle({left: (myWidth / 2)+'px', top: ((myHeight / 2) + myScroll)+'px', visibility: "visible"});
    } else {
        // Otherwise, we're loaded: do the zoom!
        zoomIn(from, shift);
    }

    return false;
}

// Zoom: Move an element in to endH endW, using zoomHost as a starting point.
// "from" is an object reference to the href that spawned the zoom.
function zoomIn(from, shift) {
    zoomimg.src = from.getAttribute("href");

    srcimg = (from.getAttribute("srcimg")) ? $(from.getAttribute("srcimg")) : from.down('img');
	if (!srcimg) srcimg = from;

    // Determine the zoom settings from where we came from, the element in the <a>.
    // If there's no element in the <a>, or we can't get the width, make stuff up
    if (srcimg && srcimg.width) {
        startW = srcimg.width;
        startH = srcimg.height;
        startPos = srcimg.positionedOffset();
    } else {
        startW = 50;
        startH = 12;
        startPos = from.positionedOffset();
    }

    hostX = startPos.left;
    hostY = startPos.top;

    // Determine the target zoom settings from the preloaded image object
    endW = imgPreload.width;
    endH = imgPreload.height;

    // Start! But only if we're not zooming already!
    if (zoomActive[theID] != true) {
        // Clear everything out just in case something is already open
        if ($("ShadowBox")) {
            $("ShadowBox").setStyle({visibility: "hidden"});
        } 
        else if (! browserIsIE) {
            // Wipe timer if shadow is fading in still
            if (fadeActive["ZoomImage"]) {
                clearInterval(fadeTimer["ZoomImage"]);
                fadeActive["ZoomImage"] = false;
                fadeTimer["ZoomImage"] = false;
            }

            $("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)';
        }

        $("ZoomClose").setStyle({visibility: "hidden"});

        // Setup the CAPTION, if existing. Hide it first, set the text.
        if (includeCaption) {
            $("ZoomCapDiv").setStyle({visibility: "hidden"});
            $("ZoomCaption").update((from.getAttribute('title')) ? from.getAttribute('title') : '');
        }

        // Store original position in an array for future zoomOut.
        zoomOrigW[theID] = startW;
        zoomOrigH[theID] = startH;
        zoomOrigX[theID] = hostX;
        zoomOrigY[theID] = hostY;

        // Show the zooming image container, make it invisible
        if (includeFade == 1) setOpacity(0, zoomID);
        zoomimg.setStyle({width: startW+'px', height: startH+'px'});
        zoomdiv.setStyle({top: hostY+'px', left: hostX+'px', visibility: "visible"});

        // If it's too big to fit in the window, shrink the width and height to fit (with ratio).
        sizeRatio = endW / endH;
        if (endW > myWidth - minBorder) {
            endW = myWidth - minBorder;
            endH = endW / sizeRatio;
        }
        if (endH > myHeight - minBorder) {
            endH = myHeight - minBorder;
            endW = endH * sizeRatio;
        }

        zoomChangeX = ((myWidth / 2) - (endW / 2) - hostX);
        zoomChangeY = (((myHeight / 2) - (endH / 2) - hostY) + myScroll);
        zoomChangeW = (endW - startW);
        zoomChangeH = (endH - startH);

        // Shift key?
        tempSteps = (shift) ? zoomSteps * 7 : zoomSteps;

        // Setup Zoom
        zoomCurrent = 0;

        // Setup Fade with Zoom, If Requested
        if (includeFade == 1) {
            fadeCurrent = 0;
            fadeAmount = (0 - 100) / tempSteps;
        } else {
            fadeAmount = 0;
        }

        // Do It!
        zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+hostX+", "+zoomChangeX+", "+hostY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDoneIn(zoomID)')", zoomTime);
        zoomActive[theID] = true;
    }
}

// Zoom it back out.
function zoomOut(from, evt) {
    // Get shift key status.
    // IE events don't seem to get passed through the function, so grab it from the window.
    tempSteps = (getShift(evt)) ? zoomSteps * 7 : zoomSteps;

    // Check to see if something is happening/open
    if (zoomActive[theID] != true) {

        // First, get rid of the shadow if necessary.
        if ($("ShadowBox")) {
            $("ShadowBox").setStyle({visibility: "hidden"});
        }
        else if (! browserIsIE) {
            // Wipe timer if shadow is fading in still
            if (fadeActive["ZoomImage"]) {
                clearInterval(fadeTimer["ZoomImage"]);
                fadeActive["ZoomImage"] = false;
                fadeTimer["ZoomImage"] = false;
            }
            $("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)';
        }

        // ..and the close box...
        // ...and the caption if necessary!
        $("ZoomClose").setStyle({visibility: "hidden"});
        $("ZoomCapDiv").setStyle({visibility: "hidden"});

        // Now, figure out where we came from, to get back there
        xy = zoomdiv.positionedOffset();
        startX = xy.left;
        startY = xy.top;
        startW = zoomimg.width;
        startH = zoomimg.height;
        zoomChangeX = zoomOrigX[theID] - startX;
        zoomChangeY = zoomOrigY[theID] - startY;
        zoomChangeW = zoomOrigW[theID] - startW;
        zoomChangeH = zoomOrigH[theID] - startH;

        // Setup Zoom
        zoomCurrent = 0;

        // Setup Fade with Zoom, If Requested
        if (includeFade == 1) {
            fadeCurrent = 0;
            fadeAmount = (100 - 0) / tempSteps;
        } else {
            fadeAmount = 0;
        }

        // Do It!
        zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+startX+", "+zoomChangeX+", "+startY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDone(zoomID, theID)')", zoomTime);
        zoomActive[theID] = true;
    }
}

// Finished Zooming In
function zoomDoneIn(zoomID, theID) {
    // Note that it's open
    zoomOpen = true;
    zoomdiv = $(zoomID);
    zd_xy = zoomdiv.positionedOffset();

    // Position the table shadow behind the zoomed in image, and display it
    if ($("ShadowBox")) {
        setOpacity(0, "ShadowBox");
        $("ShadowBox").setStyle({top: (zd_xy.top - 8)+'px', left: (zd_xy.left - 13)+'px', 
                                 width: (zoomdiv.offsetWidth + 26)+'px', height: (zoomdiv.offsetHeight + 26)+'px', visibility: "visible"});
        fadeElementSetup("ShadowBox", 0, 100, 5);
    } else if (! browserIsIE) {
        // Or, do a fade of the modern shadow
        fadeElementSetup("ZoomImage", 0, .8, 5, 0, "shadow");
    }

    // Position and display the CAPTION, if existing
    if (includeCaption && $("ZoomCaption").innerHTML) {
        zoomcapd = $("ZoomCapDiv");
        zoomcapd.setStyle({top: (zd_xy.top + (zoomdiv.offsetHeight + 15))+'px', 
                           left: ((myWidth / 2) - (zoomcapd.offsetWidth / 2))+'px', visibility: "visible"});
        // fadeElementSetup("ZoomCapDiv", 0, 100, 5);
    }

    // Display Close Box (fade it if it's not IE)
    if (!browserIsIE) setOpacity(0, "ZoomClose");
    $("ZoomClose").setStyle({visibility: "visible"});
    if (!browserIsIE) fadeElementSetup("ZoomClose", 0, 100, 5);

    // Get keypresses
    document.onkeypress = getKey;
}

// Finished Zooming Out
function zoomDone(zoomdiv, theID) {
    // No longer open
    zoomOpen = false;

    // Clear stuff out, clean up
    zoomOrigH[theID] = "";
    zoomOrigW[theID] = "";
    $(zoomdiv).setStyle({visibility: "hidden"});
    zoomActive[theID] == false;

    // Stop getting keypresses
    document.onkeypress = null;
}

// Actually zoom the element
function zoomElement(zoomdiv, theID, zoomCurrent, zoomStartW, zoomChangeW, zoomStartH, zoomChangeH, zoomStartX, zoomChangeX, zoomStartY, zoomChangeY, zoomSteps, includeFade, fadeAmount, execWhenDone) {
    // Test if we're done, or if we continue
    if (zoomCurrent == (zoomSteps + 1)) {
        zoomActive[theID] = false;
        clearInterval(zoomTimer[theID]);

        if (execWhenDone) {
            eval(execWhenDone);
        }
    } else {
        // Do the Fade!
        if (includeFade == 1) {
            setOpacity((fadeAmount < 0) ? Math.abs(zoomCurrent * fadeAmount) : 100 - (zoomCurrent * fadeAmount), zoomdiv);
        }

        // Calculate this step's difference, and move it!
        moveW = cubicInOut(zoomCurrent, zoomStartW, zoomChangeW, zoomSteps);
        moveH = cubicInOut(zoomCurrent, zoomStartH, zoomChangeH, zoomSteps);
        moveX = cubicInOut(zoomCurrent, zoomStartX, zoomChangeX, zoomSteps);
        moveY = cubicInOut(zoomCurrent, zoomStartY, zoomChangeY, zoomSteps);

        $(zoomdiv).setStyle({top: moveY+'px', left: moveX+'px'});
        zoomimg.setStyle({width: moveW+'px', height: moveH+'px'});

        zoomCurrent++;

        clearInterval(zoomTimer[theID]);
        zoomTimer[theID] = setInterval("zoomElement('"+zoomdiv+"', '"+theID+"', "+zoomCurrent+", "+zoomStartW+", "+zoomChangeW+", "+zoomStartH+", "+zoomChangeH+", "+zoomStartX+", "+zoomChangeX+", "+zoomStartY+", "+zoomChangeY+", "+zoomSteps+", "+includeFade+", "+fadeAmount+", '"+execWhenDone+"')", zoomTime);
    }
}

// Zoom Utility: Get Key Press when image is open, and act accordingly
function getKey(evt) {
    theKey = (evt) ? evt.keyCode : event.keyCode;

    if (theKey == 27) { // ESC
        zoomOut(this, evt);
    }
}

////////////////////////////
//
// FADE Functions
//

function fadeOut(elem) {
    if (elem.id) {
        fadeElementSetup(elem.id, 100, 0, 10);
    }
}

function fadeIn(elem) {
    if (elem.id) {
        fadeElementSetup(elem.id, 0, 100, 10);
    }
}

// Fade: Initialize the fade function
var fadeActive = new Array();
var fadeQueue  = new Array();
var fadeTimer  = new Array();
var fadeClose  = new Array();
var fadeMode   = new Array();

function fadeElementSetup(theID, fdStart, fdEnd, fdSteps, fdClose, fdMode) {
    if (fadeActive[theID] == true) {
        // Already animating, queue up this command
        fadeQueue[theID] = new Array(theID, fdStart, fdEnd, fdSteps);
    } else {
        fadeSteps = fdSteps;
        fadeCurrent = 0;
        fadeAmount = (fdStart - fdEnd) / fadeSteps;
        fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15);
        fadeActive[theID] = true;
        fadeMode[theID] = fdMode;

        fadeClose[theID] = (fdClose == 1);
    }
}

// Fade: Do the fade. This function will call itself, modifying the parameters, so
// many instances can run concurrently. Can fade using opacity, or fade using a box-shadow.
function fadeElement(theID, fadeCurrent, fadeAmount, fadeSteps) {
    if (fadeCurrent == fadeSteps) {
        // We're done, so clear.
        clearInterval(fadeTimer[theID]);
        fadeActive[theID] = false;
        fadeTimer[theID] = false;

        // Should we close it once the fade is complete?
        if (fadeClose[theID] == true) {
            $(theID).setStyle({visibility: "hidden"});
        }

        // Hang on.. did a command queue while we were working? If so, make it happen now
        if (fadeQueue[theID] && fadeQueue[theID] != false) {
            fadeElementSetup(fadeQueue[theID][0], fadeQueue[theID][1], fadeQueue[theID][2], fadeQueue[theID][3]);
            fadeQueue[theID] = false;
        }
    } else {

        fadeCurrent++;

        // Now actually do the fade adjustment.
        if (fadeMode[theID] == "shadow") {

            // Do a special fade on the webkit-box-shadow of the object
            if (fadeAmount < 0) {
                $(theID).style.webkitBoxShadow = shadowSettings + (Math.abs(fadeCurrent * fadeAmount)) + ')';
            } else {
                $(theID).style.webkitBoxShadow = shadowSettings + (100 - (fadeCurrent * fadeAmount)) + ')';
            }

        } else {

            // Set the opacity depending on if we're adding or subtracting (pos or neg)
            if (fadeAmount < 0) {
                setOpacity(Math.abs(fadeCurrent * fadeAmount), theID);
            } else {
                setOpacity(100 - (fadeCurrent * fadeAmount), theID);
            }
        }

        // Keep going, and send myself the updated variables
        clearInterval(fadeTimer[theID]);
        fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15);
    }
}

////////////////////////////
//
// UTILITY functions
//

// Utility: Set the opacity, compatible with a number of browsers. Value from 0 to 100.
function setOpacity(opacity, theID) {
    var object = $(theID).style;

    // If it's 100, set it to 99 for Firefox.
    if (navigator.userAgent.indexOf("Firefox") != -1) {
        if (opacity == 100) { opacity = 99.9999; } // This is majorly awkward
    }

    // Multi-browser opacity setting
    object.filter = "alpha(opacity=" + opacity + ")"; // IE/Win
    object.opacity = (opacity / 100);                 // Safari 1.2, Firefox+Mozilla
}

// Utility: Math functions for animation calucations - From http://www.robertpenner.com/easing/
//
// t = time, b = begin, c = change, d = duration
// time = current frame, begin is fixed, change is basically finish - begin, duration is fixed (frames),

function linear(t, b, c, d)
{
    return c*t/d + b;
}

function sineInOut(t, b, c, d)
{
    return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}

function cubicIn(t, b, c, d) {
    return c*(t/=d)*t*t + b;
}

function cubicOut(t, b, c, d) {
    return c*((t=t/d-1)*t*t + 1) + b;
}

function cubicInOut(t, b, c, d)
{
    if ((t/=d/2) < 1) return c/2*t*t*t + b;
    return c/2*((t-=2)*t*t + 2) + b;
}

function bounceOut(t, b, c, d)
{
    if ((t/=d) < (1/2.75)){
        return c*(7.5625*t*t) + b;
    } else if (t < (2/2.75)){
        return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
    } else if (t < (2.5/2.75)){
        return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
    } else {
        return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
    }
}


// Utility: Get the size of the window, and set myWidth and myHeight
// Credit to quirksmode.org
function getSize() {

    // Window Size
    if (self.innerHeight) { // Everyone but IE
        myWidth = window.innerWidth;
        myHeight = window.innerHeight;
        myScroll = window.pageYOffset;
    } else if (document.documentElement && document.documentElement.clientHeight) { // IE6 Strict
        myWidth = document.documentElement.clientWidth;
        myHeight = document.documentElement.clientHeight;
        myScroll = document.documentElement.scrollTop;
    } else if (document.body) { // Other IE, such as IE7
        myWidth = document.body.clientWidth;
        myHeight = document.body.clientHeight;
        myScroll = document.body.scrollTop;
    }

    // Page size w/offscreen areas
    if (window.innerHeight && window.scrollMaxY) {
        myScrollWidth = document.body.scrollWidth;
        myScrollHeight = window.innerHeight + window.scrollMaxY;
    } else if (document.body.scrollHeight > document.body.offsetHeight) { // All but Explorer Mac
        myScrollWidth = document.body.scrollWidth;
        myScrollHeight = document.body.scrollHeight;
    } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
        myScrollWidth = document.body.offsetWidth;
        myScrollHeight = document.body.offsetHeight;
    }
}

// Utility: Get Shift Key Status
// IE events don't seem to get passed through the function, so grab it from the window.
function getShift(evt) {
    var shift = false;
    if (! evt && window.event) {
        shift = window.event.shiftKey;
    } else if (evt) {
        shift = evt.shiftKey;
        if (shift) evt.stopPropagation(); // Prevents Firefox from doing shifty things
    }
    return shift;
}

