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