// 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 links to images will be updated to zoom inline.
// add rel="nozoom" to your 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
var zoomimagesuri = 'images-global/zoom/'; // location of the zoom and shadow 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 preloadanimtimer = 0;
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 zoomcaption = "zoomcaption";
var zoomcaptiondiv = "zoomcapdiv";
if (navigator.useragent.indexof("msie") != -1) {
var browserisie = true;
}
// zoom: setup the page! called in your 's onload handler.
function setupzoom() {
prepzooms();
insertzoomhtml();
zoomdiv = document.getelementbyid(zoomid);
zoomimg = document.getelementbyid(theid);
}
// zoom: inject javascript functions into hrefs pointing to images, one by one!
// skip any href that contains a rel="nozoom" tag.
// 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")) {
if (links[i].getattribute("href").search(/(.*)\.(jpg|jpeg|gif|png|bmp|tif|tiff)/gi) != -1) {
if (links[i].getattribute("rel") != "nozoom") {
links[i].onclick = function (event) { return zoomclick(this, event); };
links[i].onmouseover = function () { zoompreload(this); };
}
}
}
}
}
// 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(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() {
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 = zoomimagesuri+'zoom-spin-'+preloadframe+'.png';
preloadanimtimer = setinterval("preloadanim()", 100);
}
// 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 = zoomimagesuri+'zoom-spin-'+preloadframe+'.png';
preloadframe++;
if (preloadframe > 12) preloadframe = 1;
} else {
document.getelementbyid("zoomspin").style.visibility = "hidden";
clearinterval(preloadanimtimer);
preloadanimtimer = 0;
zoomin(preloadfrom);
}
}
// 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();
// if preloading still, wait, and display the spinner.
if (preloadactive == true) {
// but only display the spinner if it's not already being displayed!
if (preloadanimtimer == 0) {
preloadfrom = from;
preloadanimstart();
}
} 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");
// determine the zoom settings from where we came from, the element in the .
// if there's no element in the , 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 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 (document.getelementbyid("shadowbox")) {
document.getelementbyid("shadowbox").style.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;
}
document.getelementbyid("zoomimage").style.webkitboxshadow = shadowsettings + '0.0)';
}
document.getelementbyid("zoomclose").style.visibility = "hidden";
// setup the caption, if existing. hide it first, set the text.
if (includecaption) {
document.getelementbyid(zoomcaptiondiv).style.visibility = "hidden";
if (from.getattribute('title') && includecaption) {
// yes, there's a caption, set it up
document.getelementbyid(zoomcaption).innerhtml = from.getattribute('title');
} else {
document.getelementbyid(zoomcaption).innerhtml = "";
}
}
// 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 zooming image container, make it invisible
if (includefade == 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);
// shift key?
if (shift) {
tempsteps = zoomsteps * 7;
} else {
tempsteps = 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.
if (getshift(evt)) {
tempsteps = zoomsteps * 7;
} else {
tempsteps = zoomsteps;
}
// check to see if something is happening/open
if (zoomactive[theid] != true) {
// first, get rid of the shadow if necessary.
if (document.getelementbyid("shadowbox")) {
document.getelementbyid("shadowbox").style.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;
}
document.getelementbyid("zoomimage").style.webkitboxshadow = shadowsettings + '0.0)';
}
// ..and the close box...
document.getelementbyid("zoomclose").style.visibility = "hidden";
// ...and the caption if necessary!
if (includecaption && document.getelementbyid(zoomcaption).innerhtml != "") {
// fadeelementsetup(zoomcaptiondiv, 100, 0, 5, 1);
document.getelementbyid(zoomcaptiondiv).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 (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(zoomdiv, theid) {
// note that it's open
zoomopen = true;
zoomdiv = document.getelementbyid(zoomdiv);
// position the table shadow behind the zoomed in image, and display it
if (document.getelementbyid("shadowbox")) {
setopacity(0, "shadowbox");
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';
document.getelementbyid("shadowbox").style.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 && document.getelementbyid(zoomcaption).innerhtml != "") {
// setopacity(0, zoomcaptiondiv);
zoomcapd = document.getelementbyid(zoomcaptiondiv);
zoomcapd.style.top = parseint(zoomdiv.style.top) + (zoomdiv.offsetheight + 15) + 'px';
zoomcapd.style.left = (mywidth / 2) - (zoomcapd.offsetwidth / 2) + 'px';
zoomcapd.style.visibility = "visible";
// fadeelementsetup(zoomcaptiondiv, 0, 100, 5);
}
// display close box (fade it if it's not ie)
if (!browserisie) setopacity(0, "zoomclose");
document.getelementbyid("zoomclose").style.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] = "";
document.getelementbyid(zoomdiv).style.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) {
// 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 (includefade == 1) {
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+", "+includefade+", "+fadeamount+", '"+execwhendone+"')", zoomtime);
}
}
// zoom utility: get key press when image is open, and act accordingly
function getkey(evt) {
if (! evt) {
thekey = event.keycode;
} else {
thekey = evt.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) {
// alert("fading: "+theid+" steps: "+fdsteps+" mode: "+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;
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. 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) {
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++;
// 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) {
document.getelementbyid(theid).style.webkitboxshadow = shadowsettings + (math.abs(fadecurrent * fadeamount)) + ')';
} else {
document.getelementbyid(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 = 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 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;
}
// utility: 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 )
return array(elemx, elemy);
}