/** * * zoomimage * author: stefan petre www.eyecon.ro * */ (function($){ eye.extend({ zoomimage: { libs: {}, types: /\.jpg|\.jpeg|\.png|\.gif|\.bmp/g, current: null, moved: false, pointer: {x:0,y:0}, diff: {x:0, y:0}, trackkey: false, //default options (many options are controled via css) defaults: { opacity: 0.3, //caption opacity border: 0, // border arround the image shadow: 6, // shadow size duration: 300, // animation duration prevent: 14, // pixels to move the mouse before the image is dragged controls: true, // display controls caption: true, // display caption hidesource: false, centered: false, classname: false, onload: function(){return false}, beforezoomin: function(){return false}, onzoomin: function(){return false}, beforezoomout: function(){return false}, onzoomout: function(){return false}, onfocus: function(){return false}, controlstrigger: 'focus', easing: 'linear', preload: 'click' }, // the template for the image's box template: [ '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '
', '', '
', '', '', '
', '
', '
', '
' ], // handle click on the trigger click: function(e) { var el = this; el.blur(); // if the image was not preloaded yet then wait if (el.zoomimagecfg.loading === true) { return false; } //zoom it in if not zoomed already if (el.zoomimagecfg.zoomed == false) { eye.zoomimage.zoomin(el); //else zoom it out } else { eye.zoomimage.zoomout(el, false); } return false; }, // zoom in the image zoomin: function(el) { //if the image was not loaded yet then wait if (el.zoomimagecfg.loaded === false) { //if the image is not preloading then start preloading if (el.zoomimagecfg.loading != true) { el.zoomimagecfg.loading = true; eye.zoomimage.preload(el); } return; } //if the image is zoomed in then just focus it if (el.zoomimagecfg.zoomed == true) { eye.zoomimage.focus(el); return; } el.zoomimagecfg.beforezoomin.apply(el,[el.zoomimagecfg.box]); var elpos = eye.getposition(el, true); var elheight = el.offsetheight; var elwidth = el.offsetwidth; var pos = eye.getscroll(); var borderandshadow = el.zoomimagecfg.border + el.zoomimagecfg.shadow; var width = el.zoomimagecfg.width + borderandshadow * 2; var height = el.zoomimagecfg.height + borderandshadow * 2; var screenratio = pos.iw/pos.ih; var imageratio = el.zoomimagecfg.width/el.zoomimagecfg.height; // if the image is bigger then the viewport then resize the image to fit if (screenratio > imageratio) { if (height > pos.ih) { height = pos.ih; width = parseint(height * imageratio,10); } } else if (width > pos.iw) { width = pos.iw; height = parseint(width / imageratio, 10); } //if the image should be centered then do that, else center to trigger's position but do not leave the viewport var top = el.zoomimagecfg.centered ? pos.t + parseint((pos.ih - height)/2, 10) : math.min( math.max( pos.t, elpos.y + (elheight - height)/2 - borderandshadow ), pos.t + pos.ih - height ); var left = el.zoomimagecfg.centered ? pos.l + parseint((pos.iw - width)/2, 10) : math.min( math.max( pos.l, elpos.x + (elwidth - width)/2 - borderandshadow ), pos.l + pos.iw - width ); var imgwidth = width - borderandshadow * 2; var imgheight = height - borderandshadow * 2; if(el.zoomimagecfg.hidesource === true) { el.style.visibility = 'hidden'; } //move the image's box and animated it $('#' + el.zoomimagecfg.box) .css({ top: elpos.y + 'px', left: elpos.x + 'px', width: elwidth + 'px', height: elheight + 'px' }) .find('>div') .hide() .end() .find('img') .attr('src', el.zoomimagecfg.src) .css({ top: 0, left: 0, width: '100%', height: '100%', display: 'block', borderwidth: '0px' }) .end() .animate({ width: imgwidth, height: imgheight, top: top + borderandshadow, left: left + borderandshadow }, el.zoomimagecfg.duration, el.zoomimagecfg.easing, function(){ $(this) .css({ top: top + 'px', left: left + 'px', width: width + 'px', height: height + 'px' }) .find('img') .css({ top: el.zoomimagecfg.shadow + 'px', left: el.zoomimagecfg.shadow + 'px', width: imgwidth + 'px', height: imgheight + 'px', borderwidth: el.zoomimagecfg.border + 'px' }) .end() .find('>div:first') .find('div.zoomimage_sc') .css('height', height - el.zoomimagecfg.shadow*2 + 'px') .end() .show(); el.zoomimagecfg.zoomed = true; eye.zoomimage.focus(el); el.zoomimagecfg.onzoomin.apply(el,[el.zoomimagecfg.box]); }); }, //focus image and show gallery controls if it is part of a gallery showcontrols: function(el) { if(el == undefined) return; if (el.zoomimagecfg == undefined) { el = $('#' + $(el).attr('zoomimage')).get(0); } var height, imgwidth, borderandshadow = el.zoomimagecfg.border + el.zoomimagecfg.shadow; $('#' + el.zoomimagecfg.box) .find('img') .each(function(){ imgwidth = parseint($.curcss(this, 'width'),10); }) .end() .get(0).zoomimagecontrols = true; // if it has caption then display it if(el.zoomimagecfg.caption) { $('#' + el.zoomimagecfg.box) .find('>div:eq(2)') .stop() .css({ bottom: borderandshadow + 'px', left: borderandshadow + 'px', width: imgwidth + 'px' }) .show() .each(function() { this.style.height = 'auto'; height = this.offsetheight; this.style.height = '0'; }) .animate({height: height}, el.zoomimagecfg.duration); } //if it has controls then show them if(el.zoomimagecfg.controls) { // show controls only if it is part of a gallery if (eye.zoomimage.libs[el.zoomimagecfg.lib] > 1) { $('#' + el.zoomimagecfg.box) .find('>div:eq(1)') .show() .each(function(){ if (!el.zoomimagecfg.controlsheight) { el.zoomimagecfg.controlsheight = this.offsetheight; } this.style.height = '0'; }) .css({ top: borderandshadow + 'px', left: borderandshadow + 'px', width: imgwidth + 'px' }) .animate({height: el.zoomimagecfg.controlsheight}, el.zoomimagecfg.duration); } } }, //zoom out the image and go to the next/previous if any zoomout: function(el, gotonext) { var boxel, elpos, borderandshadow, elsize; // if the action was started by the trigger if (el.zoomimagecfg) { if (el.zoomimagecfg.zoomed === false) { return; } el.zoomimagecfg.beforezoomout.apply(el,[el.zoomimagecfg.box]); boxel = document.getelementbyid(el.zoomimagecfg.box); // else try to find a link that has the same href as the image src } else { boxel = el; el = $('a[href=' + $('img',boxel).attr('src') + ']').get(0); } // the trigger was found so scale to image to trigger's size if (el) { elpos = eye.getposition(el, true); el.zoomimagecfg.zoomed = false; borderandshadow = el.zoomimagecfg.border + el.zoomimagecfg.shadow; elsize = { width: el.offsetwidth, height: el.offsetheight }; // the trigger was not found so scale the image to its center } else { borderandshadow = eye.zoomimage.defaults.border + eye.zoomimage.defaults.shadow; elsize = { width: 0, height: 0 }; elpos = eye.getposition(boxel, true); elpos.y += parseint(boxel.offsetheight/2, 10); elpos.x += parseint(boxel.offsetwidth/2, 10); } $(boxel) .css({ top: boxel.offsettop + borderandshadow + 'px', left: boxel.offsetleft + borderandshadow + 'px', width: boxel.offsetwidth - borderandshadow*2 + 'px', height: boxel.offsetheight - borderandshadow*2 + 'px' }) .find('>div') .stop() .hide() .end() .find('img') .css({ top: 0, left: 0, width: '100%', height: '100%', borderwidth: '0px' }) .end() .animate( { top: elpos.y + 'px', left: elpos.x + 'px', width: elsize.width + 'px', height: elsize.height + 'px' }, // if the trigger was not found the use the default duration el ? el.zoomimagecfg.duration : eye.zoomimage.defaults.duration, el.zoomimagecfg.easing, function() { //hide image and remove focus eye.zoomimage.blur(); $(this).hide(); // if the trigger was found then aply callback and try to focus the next one zoomed if (el) { if(el.zoomimagecfg.hidesource === true) { el.style.visibility = 'visible'; } el.zoomimagecfg.onzoomout.apply(el,[el.zoomimagecfg.box]); if (!gotonext) { eye.zoomimage.focus($('div.zoomimage:visible:last').not(':animated').get(0)); } //the trigger was not found so remove the image since no trigger is present in the page } else { $(boxel).stop().remove(); } } ); }, mouseover: function(e) { var triggerel = document.getelementbyid($(this).attr('zoomimage')); if (triggerel.zoomimagecfg.zoomed === true && this.zoomimagecontrols == false) { eye.zoomimage.showcontrols(triggerel); } return false; }, mouseout: function(e) { if ( !eye.ischildof(this, e.relatedtarget, this) ) { $(this) .find('>div:not(:first)') .stop() .hide(); this.zoomimagecontrols = false; } return false; }, // prepare for possible drag mousedown: function(e) { // find the trigger var triggerel = document.getelementbyid($(this).attr('zoomimage')); //if the trigger was found then prepare informations for drag if (triggerel) { $.extend(eye.zoomimage,{ current: this, prevent: triggerel.zoomimagecfg.prevent, moved: false, diff: { x: e.pagex - this.offsetleft, y: e.pagey - this.offsettop }, pointer: { x: e.pagex , y: e.pagey } }); $(document) .bind('mousemove', eye.zoomimage.mousemove) .bind('mouseup', eye.zoomimage.mouseup); // if the trigger was not found then it is an orphan and zoom it out } else { $(this).zoomimageclear(); } return false; }, //do the drag if prevent distance was overtake mousemove: function(e) { var diffx = math.abs(eye.zoomimage.pointer.x - e.pagex); var diffy = math.abs(eye.zoomimage.pointer.y - e.pagey); //the prevent distance was not reached yet so we check if it is reached already if (eye.zoomimage.moved === false) { if ( diffx > eye.zoomimage.prevent|| diffy > eye.zoomimage.prevent) { eye.zoomimage.moved = true; $(eye.zoomimage.current).addclass('zoomimage_move'); if (!$(eye.zoomimage.current).is('.zoomimage_focused')) { eye.zoomimage.focus(eye.zoomimage.current); } } // the prevent distance was overtake so the element can be moved } else { eye.zoomimage.current.style.top = e.pagey - eye.zoomimage.diff.y + 'px'; eye.zoomimage.current.style.left = e.pagex - eye.zoomimage.diff.x + 'px'; } return false; }, //the drag stops mouseup: function (e) { $(eye.zoomimage.current).removeclass('zoomimage_move'); eye.zoomimage.current = null; $(document) .unbind('mousemove', eye.zoomimage.mousemove) .unbind('mouseup', eye.zoomimage.mouseup); return false; }, // click on image imageclick: function(e) { $(document) .unbind('mousemove', eye.zoomimage.mousemove) .unbind('mouseup', eye.zoomimage.mouseup); var el = document.getelementbyid($(this).attr('zoomimage')); // if the trigger was found if (el) { //if the image was not moved but was focused if (eye.zoomimage.moved === false && $(this).is('.zoomimage_focused')) { // if the event target is a link then it was a click on one of the controls and go to the next image if ($(e.target).is('a')) { eye.zoomimage.zoomnext(el, e.target.classname == 'zoomimage_next' ? 1 : -1); var gotonext = true; // else just zoom it out } else { eye.zoomimage.zoomout(el, gotonext||false); } // just focus the image } else if(!$(this).is('.zoomimage_focused')) { eye.zoomimage.focus(this); } //the trigger was not found so the image is orphan and zoom it out } else { $(this).zoomimageclear(); } return false; }, //zoom out any opened image and clear orphan images clear: function() { var subject = this; if (subject.size() == 0) { subject = $('div.zoomimage'); } return subject.each(function(){ var triggerel = document.getelementbyid($(this).attr('zoomimage')); if (triggerel) { eye.zoomimage.zoomout(triggerel, false); } else { eye.zoomimage.zoomout(this, false); } }); }, // zoom the next image in gallery zoomnext: function(el, dir) { if(el.zoomimagecfg.zoomed === false) { return; } eye.zoomimage.zoomout(el, true); var nextimg = el.zoomimagecfg.iteration + dir; var lib = $(el).attr('zoomimage'); var maximg = eye.zoomimage.libs[lib]; if (nextimg < 0) { nextimg = maximg - 1; } else if (nextimg >= maximg) { nextimg = 0; } eye.zoomimage.zoomin($('a[zoomimage="' + lib + '"]').get(nextimg)); }, //hande any key pressed keypressed: function(e) { var el = $('div.zoomimage_focused'); if (el.size() == 1) { var pressedkey = e.charcode || e.keycode || -1; el = $('#' + $(el).attr('zoomimage')).get(0); var lib = $(el).attr('zoomimage'); switch (pressedkey) { //end case 35: // go to the last image in the gallery if (eye.zoomimage.libs[lib] > 1 && eye.zoomimage.libs[lib] - 1 != el.zoomimagecfg.iteration) { eye.zoomimage.zoomnext(el, eye.zoomimage.libs[lib] - el.zoomimagecfg.iteration - 1); return false; } break; //home case 36: // go to the first image in the gallery if (eye.zoomimage.libs[lib] > 1 && el.zoomimagecfg.iteration != 0) { eye.zoomimage.zoomnext(el, - el.zoomimagecfg.iteration); return false; } break; //down; case 40: //left case 37: //backspace case 8: //page up case 33: //p case 80: case 112: // go to the previous image in the gallery if (eye.zoomimage.libs[lib] > 1) { eye.zoomimage.zoomnext(el, -1); return false; } break; //up case 38: //right case 39: //page down case 34: //space case 32: //n case 110: case 78: // go to the next image in the gallery if (eye.zoomimage.libs[lib] > 1) { eye.zoomimage.zoomnext(el, 1); return false; } break; //escape case 27: // well zoome out the curent image eye.zoomimage.zoomout(el, false); return false; break; } } }, // focus on image focus: function(el) { if(el == undefined) return; if (el.zoomimagecfg == undefined) { el = $('#' + $(el).attr('zoomimage')).get(0); } else { var showcontrols = true; } // if another image is focused then remove focus eye.zoomimage.blur(el); $('#' + el.zoomimagecfg.box) .not('.zoomimage_focused') .addclass('zoomimage_focused'); el.zoomimagecfg.onfocus.apply(el,[el.zoomimagecfg.box]); if (el.zoomimagecfg.controlstrigger == 'focus' || showcontrols) { eye.zoomimage.showcontrols(el); } }, //blur image blur: function(el) { $('div.zoomimage_focused') .not('#' + (el == undefined ? 'fakezoomimage' : el.zoomimagecfg.box)) .removeclass('zoomimage_focused') .each(function(){ this.zoomimagecontrols = false; }) .find('>div:not(:first)') .stop() .hide(); }, preload: function(el) { // place the loading aimation on top var boxel = $('#' + el.zoomimagecfg.box).show(); boxel.find('>div, img').hide(); var elpos = eye.getposition(el, true); boxel .find('>div:last') .show() .end() .css({ top: elpos.y + 'px', left: elpos.x + 'px', width: el.offsetwidth + 'px', height: el.offsetheight + 'px' }); // preload the image var preld= new image(); preld.src = el.href; //if the image was laoded already if (preld.complete) { eye.zoomimage.markpreloaded(preld, el); // else place a callback } else { preld.onload = function() { eye.zoomimage.markpreloaded(preld, el); }; } }, markpreloaded: function(preld, el) { //mark image as loaded and remember the size and source $.extend(el.zoomimagecfg,{ loaded: true, width: preld.width, height: preld.height, src: preld.src }); // hide loading animation $('#' + el.zoomimagecfg.box) .find('div.zoomimage_loading') .hide(); //if the image waits to be enlarged then zoom in if (el.zoomimagecfg.loading) { el.zoomimagecfg.loading = false; eye.zoomimage.zoomin(el); } el.zoomimagecfg.onload.apply(el,[el.zoomimagecfg.box]); }, //constructor init: function(opt) { //generate a library key var libkey = parseint(math.random()*2000,10); //store the number of images in the library eye.zoomimage.libs[libkey] = 0; opt = $.extend({lib:libkey}, eye.zoomimage.defaults, opt||{}); return this.each(function(){ var jqel = $(this); var el = this; //consider only the links pointing to an image if (el.href && el.href.tolowercase().match(eye.zoomimage.types) != null) { //store library options el.zoomimagecfg = $.extend({}, opt, { zoomed: false, loading: false, loaded: false, animated: false, src: el.href, iteration: eye.zoomimage.libs[libkey], box: 'zoomimage_' + parseint(math.random() * 2000, 10) + '' }); //increment the number of images in the library eye.zoomimage.libs[libkey]++; jqel .bind('click', eye.zoomimage.click) .attr('zoomimage', libkey) .attr('zoomimagebox', el.zoomimagecfg.box); var currid = jqel.attr('id'); if (!currid) { currid = el.zoomimagecfg.box + '_trigger'; jqel.attr('id', currid); } var titleattr = $(el).attr('title'); if (titleattr == '' || titleattr == false) { el.zoomimagecfg.caption = false; } // generate the html for the image's box $(eye.zoomimage.template.join('')) .attr('id', el.zoomimagecfg.box) .attr('zoomimage', currid) .addclass(el.zoomimagecfg.classname) .appendto(document.body) .bind('mousedown', eye.zoomimage.mousedown) .bind('click', eye.zoomimage.imageclick) .each(function(){ this.zoomimagecontrols = false; if (el.zoomimagecfg.controlstrigger != 'focus') { $(this) .bind('mouseover', eye.zoomimage.mouseover) .bind('mouseout', eye.zoomimage.mouseout); } }) .find('>div') .not(':first') .css('opacity', el.zoomimagecfg.opacity) .end() .filter('div:eq(2)') .html('

' + titleattr + '

'); if (el.zoomimagecfg.preload == 'load') { eye.zoomimage.preload(el); } if (eye.zoomimage.trackkey === false) { eye.zoomimage.trackkey = true; $(document).bind('keydown', eye.zoomimage.keypressed); } } }); } } }); $.fn.extend({ /** * open all images found in 'href' attribute from each element specified in the selection. the images are grouped in galleries. the images are preloaded before any user interation. * @name zoomimage * @description open all images found in 'href' attribute from each element specified in the selection. the images are grouped in galleries * @param hash options a hash of parameters. all parameters are optional. * @option float opacity the opacity for the caption and controls. default: 0.3 * @option int border image's border. default: 0 * @option int duration animation duration. default 300 * @option int prevent pixes to move the mouse before the images is dragged (prevents accidental dragging). default: 14 * @option boolean controls whatever if the controls are displayed (if the image is not part of an libriry then the controls are not displayed) * @option boolean caption whatever if the caption is displayed (the caption text is the text from 'title' atribute. default: true * @option boolean centered whatever if the image should be centered in the viewport or to the trigger. default: false * @option string easing animation easing. default: linear * @option boolean hidesource whatever to hide source when the image is opened. default: false * @option string classname css class to add to image's box. default: false * @option string controlstrigger 'focus' to show caption and controls when the box is focused or 'mouseover' to show controls and caption on mouse over. default: 'focus' * @option string preload 'click' to preload the image when the trigger is clicked or 'load' to preload the image on document load. default: 'click' * @option function onload callback function triggered when the image was loaded * @option function beforezoomin callback function triggered before the image is zoomed in * @option function onzoomin callback function triggered when the image is zooms in * @option function beforezoomout callback function triggered before the image is zoomed out * @option function onzoomout callback function triggered when the image is zooms out * @option function onfocus callback function triggered when the image is focused */ zoomimage: eye.zoomimage.init, /** * zooms out all opened images and removes orphans (when the trigger was not found) * to clear specific images use for slector 'div.zooimage[whatever]', else all the images are processed */ zoomimageclear: eye.zoomimage.clear }); })(jquery);