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