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();

function fadeElementSetup(theID, fdStart, fdEnd, fdSteps, fdClose) {

        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;
                if (fdClose == 1) {
                        fadeClose[theID] = true;
                } else {
                        fadeClose[theID] = false;
                }
        }
}

// Fade: Do the fade. This function will call itself, modifying the parameters, so
// many instances can run concurrently.

function fadeElement(theID, fadeCurrent, fadeAmount, fadeSteps) {
  
        if (fadeCurrent == fadeSteps) {

            // We're done, so clear.
            clearInterval(fadeTimer[theID]);
            fadeActive[theID] = false;
            
            // Should we close it?
            
            if (fadeClose[theID] == true) {
                document.getElementById(theID).style.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++;
                
                // 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: Set the opacity, compatible with a number of browsers. Value from 0 to 100.

function setOpacity(opacity, theID) {

        var object = document.getElementById(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 retarded
        }

        // Multi-browser opacity setting

        object.filter = "alpha(opacity=" + opacity + ")"; // IE/Win
        //object.KhtmlOpacity = (opacity / 100);            // Safari 1.1 or lower, Konqueror
        //object.MozOpacity = (opacity / 100);              // Older Mozilla+Firefox
        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;
        }
}

////////////////////////////
//
// ZOOM IMAGE functions
// (C) 2007 Cabel Sasser / Panic Inc.

// Settings

var includeCaption = 1;   // 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 fade           = 1;   // Fade images in / out
var minBorder      = 90;  // Amount of padding between large, scaled down images, and the window edges

var zoomImageURI   = '/php/im/zoom/'; // Location of the zoom images

// 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 theCap    = "ZoomCaption";
var theCapDiv = "ZoomCapDiv";

// Zoom: Setup The Page! Called onLoad();

function setupZoom() {
        prepZooms();
        insertZoomHTML();
        zoomdiv = document.getElementById(zoomID);  
        zoomimg = document.getElementById(theID);
}

// Zoom: Inject Javascript functions into zoomable href's, one by one.
// This is done at page load time via an onLoad() handler.

function prepZooms() {
        if (! document.getElementsByTagName) {
                return;
        }
        var links = document.getElementsByTagName("a");
        for (i = 0; i < links.length; i++) {
                if (links[i].getAttribute("href") && (links[i].getAttribute("rel"))) {
                        if (links[i].getAttribute("rel").indexOf("zoom:") == 0) {
                                links[i].onclick = function () { zoomClick(this); return false; };
                                links[i].onmouseover = function () { zoomPreload(this); };
                        }
                }
        }
}

// Zoom: Preload a zoom image when hovering over the thumbnail, then set the image once the preload is complete.
// Preloaded image is stored in imgPreload() and swapped out in the zoom function.

function zoomPreload(from) {

        var theimage = from.getAttribute("href");
        // console.log("PRELOAD START: "+theimage);

        // Only preload if we have to, i.e. the image isn't this image already

        if (imgPreload.src.indexOf(from.getAttribute("href").substr(from.getAttribute("href").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() {
                        // console.log("PRELOAD END");
                        preloadActive = false;
                }

                // Load it!
                imgPreload.src = theimage;
        }
}

// Zoom: Start the preloading animation cycle.

function preloadAnimStart() {
        preloadTime = new Date();
        document.getElementById("ZoomSpin").style.left = (myWidth / 2) + 'px';
        document.getElementById("ZoomSpin").style.top = ((myHeight / 2) + myScroll) + 'px';
        document.getElementById("ZoomSpin").style.visibility = "visible";       
        preloadFrame = 1;
        document.getElementById("SpinImage").src = zoomImageURI+'zoom-spin-'+preloadFrame+'.png';  
        preloadAnimTimer = setInterval("preloadAnim()", 100);
}

// Until we've been preloading for one second, just chill out in here.
// 
// function preloadAnimPending(from) {
//      if (preloadActive != false) {
//              if ((new Date() - preloadTime) > 1000) {
//                      document.getElementById("ZoomSpin").style.visibility = "visible";
//                      clearInterval(preloadAnimTimer);
//                      preloadAnimTimer = setInterval("preloadAnim()", 100);
//              }
//              else {
//                      // Stay in this loop and don't do anything while we wait one second
//              }
//      } else {
//              clearInterval(preloadAnimTimer);
//              zoomIn(preloadFrom);
//      }
// } 

// Zoom: Display and ANIMATE the jibber jabber widget. Once preloadActive is false, bail and zoom it up!

function preloadAnim(from) {
        if (preloadActive != false) {
                document.getElementById("SpinImage").src = zoomImageURI+'zoom-spin-'+preloadFrame+'.png';
                preloadFrame++;
                if (preloadFrame > 12) preloadFrame = 1;
        } else {
                document.getElementById("ZoomSpin").style.visibility = "hidden";    
                clearInterval(preloadAnimTimer);
                zoomIn(preloadFrom);
        }
}

// Zoom: We got a click! Should we do the zoom? Or wait for the preload to complete?

function zoomClick(from) {

        // TODO: Double check that imgPreload src = clicked src

        // Get browser dimensions
        getSize();

        if (preloadActive == true) {
                // Preloading is otherwise still going on. So wait.
                preloadFrom = from;
                preloadAnimStart();
        } else {
                // Otherwise, we're loaded: do the zoom!
                zoomIn(from);
        }
}

// 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) {

        zoomimg.src = from.getAttribute("href");

        // 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 (from.childNodes[0].width) {
                startW = from.childNodes[0].width;
                startH = from.childNodes[0].height;
                startPos = findElementPos(from.childNodes[0]);
        } else {
                startW = 50;
                startH = 12;
                startPos = findElementPos(from);
        }
                    
        hostX = startPos[0];
        hostY = startPos[1];

        // Make up for a scrolled containing div.
        // TODO: This HAS to move into findElementPos.
        
        if (document.getElementById('scroller')) {
                hostX = hostX - document.getElementById('scroller').scrollLeft;
        }

        // Determine the target zoom settings

        endW = imgPreload.width;
        endH = imgPreload.height;
        
        // TODO: need to get "rollover" setting somehow.
  
        // Don't act if we're already doing something.
  
        if (zoomActive[theID] != true) {
  
                // Clear everything out just in case something is already open
     
                document.getElementById("ShadowBox").style.visibility = "hidden";
                document.getElementById("ZoomClose").style.visibility = "hidden";     
     
                // Set the CAPTION if turned on
  
                if (includeCaption == 1) {
                        zoomcap  = document.getElementById(theCap);
                        zoomcapd = document.getElementById(theCapDiv);
       
                        if (from.getAttribute('title') && includeCaption == 1) {
                                zoomcapd.style.display = 'block';
                                zoomcap.innerHTML = from.getAttribute('title');
                        } else {
                                zoomcapd.style.display = 'none';
                        }
       
                }   
     
                // Store original position in an array for future zoomOut.

                zoomOrigW[theID] = startW;
                zoomOrigH[theID] = startH;
                zoomOrigX[theID] = hostX;
                zoomOrigY[theID] = hostY;
     
                // Now set the starting dimensions
     
                zoomimg.style.width = startW + 'px';
                zoomimg.style.height = startH + 'px';
                zoomdiv.style.left = hostX + 'px';
                zoomdiv.style.top = hostY + 'px';
     
                // Show the zoom box, make it invisible
     
                if (fade == 1) {
                        setOpacity(0, zoomID);
                }
                zoomdiv.style.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);
                
                // console.log("X: "+zoomChangeX+" Y: "+zoomChangeY+" W: "+zoomChangeW+" H: "+zoomChangeH);

                // Setup Zoom
     
                zoomCurrent = 0;
     
                // Setup Fade with Zoom, If Requested
     
                if (fade == 1) {
                        fadeCurrent = 0;
                        fadeAmount = (0 - 100) / zoomSteps;
                } else {
                        fadeAmount = 0;
                }
       
                // Do It!
                
                zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+hostX+", "+zoomChangeX+", "+hostY+", "+zoomChangeY+", "+zoomSteps+", "+fade+", "+fadeAmount+", 'zoomDoneIn(zoomID)')", zoomTime);           
                zoomActive[theID] = true; 
        }
}

// Zoom it back out.

function zoomOut() {

        // Check to see if something is happening/open
  
        if (zoomActive[theID] != true) {
     
                // First, get rid of the shadow if necessary
     
                document.getElementById("ShadowBox").style.visibility = "hidden";
                document.getElementById("ZoomClose").style.visibility = "hidden";
     
                // Now, figure out where we came from, to get back there

                startX = parseInt(zoomdiv.style.left);
                startY = parseInt(zoomdiv.style.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 (fade == 1) {
                        fadeCurrent = 0;
                        fadeAmount = (100 - 0) / zoomSteps;
                } else {
                        fadeAmount = 0;
                }
     
                // Do It!

                zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+startX+", "+zoomChangeX+", "+startY+", "+zoomChangeY+", "+zoomSteps+", "+fade+", "+fadeAmount+", 'zoomDone(zoomID, theID)')", zoomTime);    
                zoomActive[theID] = true;
   }
}

// Finished Zooming In

function zoomDoneIn(zoomdiv, theID) {

        // Note that it's open
  
        zoomOpen = true;

        // Make sure they are gone

        setOpacity(0, "ShadowBox");
        setOpacity(0, "ZoomClose");

        // Position the shadow behind the zoomed in image.

        zoomdiv = document.getElementById(zoomdiv);
        shadowdiv = document.getElementById("ShadowBox");
 
        shadowLeft = parseInt(zoomdiv.style.left) - 13;
        shadowTop = parseInt(zoomdiv.style.top) - 8;
        shadowWidth = zoomdiv.offsetWidth + 26;
        shadowHeight = zoomdiv.offsetHeight + 26; 

        shadowdiv.style.width = shadowWidth + 'px';
        shadowdiv.style.height = shadowHeight + 'px';
        shadowdiv.style.left = shadowLeft + 'px';
        shadowdiv.style.top = shadowTop + 'px';
  
        // Display Shadow and Zoom
  
        document.getElementById("ShadowBox").style.visibility = "visible";
        fadeElementSetup("ShadowBox", 0, 100, 5);
        document.getElementById("ZoomClose").style.visibility = "visible";
        fadeElementSetup("ZoomClose", 0, 100, 5);
  
}

// Finished Zooming Out

function zoomDone(zoomdiv, theID) {

        // No longer open
  
        zoomOpen = false;

        // Clear stuff out, clean up

        zoomOrigH[theID] = "";
        zoomOrigW[theID] = "";
        document.getElementById(zoomdiv).style.visibility = "hidden";
        zoomActive[theID] == false;
}

// Actually zoom the element

function zoomElement(zoomdiv, theID, zoomCurrent, zoomStartW, zoomChangeW, zoomStartH, zoomChangeH, zoomStartX, zoomChangeX, zoomStartY, zoomChangeY, zoomSteps, fade, fadeAmount, execWhenDone) {

        // console.log("Zooming Step #"+zoomCurrent+ " of "+zoomSteps+" (zoom " + zoomStartW + "/" + zoomChangeW + ") (zoom " + zoomStartH + "/" + zoomChangeH + ")  (zoom " + zoomStartX + "/" + zoomChangeX + ")  (zoom " + zoomStartY + "/" + zoomChangeY + ") Fade: "+fadeAmount);
    
        // 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 (fade != 0) {
                        if (fadeAmount < 0) {
                                setOpacity(Math.abs(zoomCurrent * fadeAmount), zoomdiv);
                        } else {
                                setOpacity(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);
        
                document.getElementById(zoomdiv).style.left = moveX + 'px';
                document.getElementById(zoomdiv).style.top = moveY + 'px';
                zoomimg.style.width = moveW + 'px';
                zoomimg.style.height = moveH + 'px';
        
                zoomCurrent++;
                
                clearInterval(zoomTimer[theID]);
                zoomTimer[theID] = setInterval("zoomElement('"+zoomdiv+"', '"+theID+"', "+zoomCurrent+", "+zoomStartW+", "+zoomChangeW+", "+zoomStartH+", "+zoomChangeH+", "+zoomStartX+", "+zoomChangeX+", "+zoomStartY+", "+zoomChangeY+", "+zoomSteps+", "+fade+", "+fadeAmount+", '"+execWhenDone+"')", zoomTime);
        }
}

// Zoom Rollover Functions
// To be re-added

function zoomMouseOver() {
//  if (rollOverImg) {
//     if (document.getElementById("ZoomImage").src != rollOverImg) {
//        document.getElementById("ZoomImage").src = rollOverImg;
//     }
//  }
}

function zoomMouseOut() {
//  if (rollOverImg) {
//     if (document.getElementById("ZoomImage").src != image) {
//        document.getElementById("ZoomImage").src = image;
//     }
//  }
}

// Get the size of the window, and set myWidth and myHeight

function getSize() {
        if (document.all) {
                // IE4+ or IE6+ in standards compliant 
                myWidth  = (document.documentElement.clientWidth) ? document.documentElement.clientWidth : document.body.clientWidth;
                myHeight = (document.documentElement.clientHeight) ? document.documentElement.clientHeight : document.body.clientHeight;
                myScroll = (document.documentElement.scrollTop) ? document.documentElement.scrollTop : document.body.scrollTop;
        } else {
                // Non-IE
                myWidth = window.innerWidth;
                myHeight = window.innerHeight;
                myScroll = window.pageYOffset;
        }
        
        // Core code from - quirksmode.org
    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;
        }
}

// Find the Y position of an element on a page
// Return Y and X as an array

function findElementPos(elemFind)
{
        var elemX = 0;
        var elemY = 0;
        do {
                elemX += elemFind.offsetLeft;
                elemY += elemFind.offsetTop;
        } while ( elemFind = elemFind.offsetParent )

        //console.log("Found element "+elemFind+" at "+elemY+"/"+elemX);

        return Array(elemX, elemY);
}