/*
* Magnify portion of image where mouse is hovering over.
*
* Usage:
* Add
tags to your document which have a data-largesrc attribute identifying source of a
* large picture to use in magnifier.
*
*
* Manual usage:
* Setup existing image in document with magnifier.
* $('#image_id').imageMagnifier( large_image_url [, options ]);
*
* Replace existing image in document with a new image and large image in magnifier.
* $('#image_id').imageMagnifier( image_url, large_image_url );
*
* Setup existing image in document with a magnifier whose name is automatically derived from
* the image's src attribute.
* $('#image_id').imageMagnifier( {largesrc: function(src) { return src.replace(/\.jpg/,'-large.jpg'); }} );
*
* Replace existing image in document with a new image and with large image in magnifier
* automatically derived from image name. Simply changing the image src attribute triggers a change in magnifier.
* $('#image_id').prop('src','new_image_url');
*
* Setup existing image in document with a magnifier whose name is automatically derived from the
* surrounding tag's target. The image element is "this" inside of the largesrc function.
* $('#image_id').imageMagnifier( {largesrc: function() { return $(this).closest('a').prop('href'); }} );
*
* Define a button that turns magnifier(s) on/off.
*
*
* You can tell which image elements have been set up with a magnifier by whether they have a
* 'imageMagnifier' data object.
*
* jquery.imageMagnifier.js v0.65
* Copyright (c) 2012-2021 Mack Pexton (mackpexton.com)
* License: http://www.opensource.org/licenses/mit-license.php
*/
(function($){
var im = 'imageMagnifier',
// Class names of pieces and parts
im_image = im + '_image',
im_loader = im + '_loader',
im_magnifier = im + '_magnifier',
im_container = im + '_container',
fast = 'fast'; // actual fadeIn/Out speed
$.imageMagnifier = {
options: { // default settings for magnifier
image: '', // url of image displayed on page
magnifier: {
image: '', // url of image displayed through magnifier
width: 150,
height: 150,
top: 0, // initial top coordinate of magnifier midpoint
left: 0, // initial left coordinate of magnifier midpoint
zIndex: 2, // enough to hover over picture
shadow: 20, // drop shadow width
radius: null, // automatically computed to be circle if null
cursor: 'none', // cursor used when magnifier is moving
frozen: false // magnifier frozen in place till user clicks or drags it
},
loader: {
//image: 'images/loader.gif' // url of image displayed while images are loading
// Following is 32x32 animated spinner
image: 'data:image/gif;base64,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'
},
largesrc: null, // set to a function to automatically determine url from image src
callback: null, // function(top,left) called when magnifier is positioned on image
callbackKeyDown: null, // function(top,left) called when a key is pressed while positioning magnifier
enabled: null // automatically set to current global enabled state if null
},
autoinit: true, // set to true to automatically init() when document loads
init: function(opt) {
// Automatically set up images with a data-largesrc attribute.
$('img[data-largesrc]').imageMagnifier(opt);
},
enabled: true, // current global state
enable: function(img) {
if (img) {
$(img).each(function() {
if (! $.hasData(this)) return;
var $this = $(this),
current_settings = $this.data(im);
current_settings.enabled = true;
if (! current_settings.finished) {
$this.trigger('load'); // re-execute load handler in enabled state
}
else if (current_settings.frozen) {
$('.'+im_magnifier,$this.parent()).fadeIn(fast);
}
});
}
else {
// enable all
this.enable('.'+im_image);
this.enabled = true;
}
},
disable: function(img) {
if (img) {
$(img).each(function() {
var $this = $(this),
current_settings = $this.data(im);
current_settings.enabled = false;
$('.'+im_magnifier,$this.parent()).fadeOut(fast);
$('.'+im_loader,$this.parent()).hide();
});
}
else {
// disable all
this.disable('.'+im_image);
this.enabled = false;
}
},
toggle: function(img) {
var e=this.enable, d=this.disable;
if (img) {
$(img).each(function() {
if ($(this).data(im) && $(this).data(im).enabled) { d(img); } else { e(img); }
});
}
else {
if (this.enabled) { d(); } else { e(); }
}
}
};
$.fn.wrapTightly = function (wrapper) {
this.each(function(){
var $this = $(this),
$wrapper = $this.wrap(wrapper).parent();
// Transfer margins to wrapper
$wrapper.css({
'margin-left': $this.css('margin-left'),
'margin-right': $this.css('margin-right'),
'margin-top': $this.css('margin-top'),
'margin-bottom': $this.css('margin-bottom'),
width: $this.outerWidth() + 'px',
height: $this.outerHeight() + 'px'
});
$this.css({margin: 0});
});
return this;
};
function setup_image_magnifier($image,src,largesrc,settings) {
// Pieces and parts
var $container = null,
$loader = null,
$loaderImage = null,
$magnifier = null,
$magnifierImage = null,
// Reference coordinates
container_top, container_left,
// Required measurments
image_width, image_height,
magnifier_image_width, magnifier_image_height,
image_dist_left, image_dist_top, // padding and border width
// Position formula invariants
half_magnifier_width, half_magnifier_height,
image_magnifier_width_ratio, image_magnifier_height_ratio,
// Magnifier movement flags
frozen = false,
moved = false,
was_frozen,
// Saved variables
last_position,
current_settings;
// Functions
function is_enabled() {
return current_settings.enabled;
}
function is_ready() {
return current_settings && current_settings.imageReady && current_settings.magnifierReady;
}
function freeze(setting) {
var prev_setting = frozen;
frozen = setting === false ? false : true;
current_settings.frozen = frozen;
return prev_setting;
}
function mouse_loc(event) {
// Mouse location over image
return {
top: event.pageY - container_top - image_dist_top,
left: event.pageX - container_left - image_dist_left
};
}
function magnifier_loc(p) {
// Return position in large image corresponding to the p position.
return {
top: Math.floor(p.top / image_magnifier_width_ratio),
left: Math.floor(p.left / image_magnifier_height_ratio)
};
}
function compute_position_magnifier_invariants() {
// Compute invariant variables for position_magnifier()
container_top = $container.offset().top;
container_left = $container.offset().left;
half_magnifier_width = $magnifier.width() / 2;
half_magnifier_height = $magnifier.height() / 2;
image_dist_top = parseInt($image.css('padding-top'),10) + parseInt($image.css('border-top-width'),10);
image_dist_left = parseInt($image.css('padding-left'),10) + parseInt($image.css('border-left-width'),10);
image_magnifier_width_ratio = $image.width() / $magnifierImage.prop('width');
image_magnifier_height_ratio = $image.height() / $magnifierImage.prop('height');
}
function position_magnifier(top,left) {
magnifier_top = top - half_magnifier_height + image_dist_top,
magnifier_left = left - half_magnifier_width + image_dist_left,
magnifier_image_top = half_magnifier_height - (top / image_magnifier_height_ratio),
magnifier_image_left = half_magnifier_width - (left / image_magnifier_width_ratio);
$magnifier.css({
top: magnifier_top + 'px',
left: magnifier_left + 'px',
'background-position': magnifier_image_left + 'px ' + magnifier_image_top + 'px'
});
}
function position_magnifier_handler(event,force) {
if (frozen && ! force) return;
last_position = mouse_loc(event);
position_magnifier(last_position.top,last_position.left);
}
function callback_handler(event) {
var p = mouse_loc(event);
return current_settings.callback.call($image[0],p,magnifier_loc(p),event)
}
function callbackKeyDown_handler(event) {
if (frozen) return;
return current_settings.callbackKeyDown.call($image[0],last_position,magnifier_loc(last_position),event)
}
//
// Setup container around image for magnifer.
//
$container = $image.wrapTightly('').parent()
.addClass(im_container)
.css({
display: 'inline-block',
position: 'relative',
overflow: 'hidden'
})
.on('mouseenter.im',function(){
current_settings = $image.data(im);
if (is_enabled() && is_ready()) {
$container.off('mousemove.im');
$loader.hide();
$magnifier.show();
$container.on('mousemove.im',position_magnifier_handler);
if (current_settings.callbackKeyDown) $(document).on('keydown.im',callbackKeyDown_handler);
if (! frozen) $magnifier.css({cursor:current_settings.magnifier.cursor});
}
})
.on('mouseleave.im',function(){
if (! is_ready()) return;
if (! frozen) $magnifier.hide();
$container.off('mousemove.im',position_magnifier_handler);
if (current_settings.callbackKeyDown) $(document).off('keydown.im',callbackKeyDown_handler);
})
.on('mousemove.im',function(){
if (! is_enabled()) return;
if (! is_ready()) return;
$(this).trigger('mouseenter.im');
});
//
// Setup loader indicator.
//
$loader = $('')
.addClass(im_loader)
.css({
'position': 'absolute',
'margin-top': '30%',
'margin-left': '50%',
'text-align': 'left'
});
$loaderImage = $('
')
.attr({ src: settings.loader.image })
.on('load.im',function() {
$loader.append($loaderImage);
})
.on('error.im',function() {
$loader.text('Loading...');
});
$container.prepend($loader);
//
// Setup magnifier with large background image
//
$magnifier = $('')
.addClass(im_magnifier)
.css({
width: settings.magnifier.width + 'px',
height: settings.magnifier.height + 'px',
display: 'none',
position: 'absolute',
border: '1px solid #666',
'z-index': settings.magnifier.zIndex,
'box-shadow': '0 0 ' + settings.magnifier.shadow + 'px #333',
'border-radius': settings.magnifier.radius + 'px',
'background-repeat': 'no-repeat',
'background-position':
(settings.magnifier.left - (settings.magnifier.width/2)) + 'px ' +
(settings.magnifier.top - (settings.magnifier.height/2)) + 'px'
})
.on('mousedown.im',function(event) {
position_magnifier_handler(event,true);
was_frozen = freeze(false);
moved = false;
$(this).css({cursor:current_settings.magnifier.cursor});
event.stopImmediatePropagation();
})
.on('mousemove.im',function() {
moved = true;
})
.on('mouseup.im',function(event) {
if (moved) { freeze() }
else { freeze(!was_frozen) }
if (frozen) $(this).css({cursor:$image.css('cursor')});
if (frozen && event.which == 1 && current_settings.callback) { // left mouse button
if (callback_handler(event) === false) freeze(false);
}
})
.on('click.im',function(event) {
return false; // gobble event
});
$magnifierImage = $('
')
.on('load.im',function(event) {
current_settings = $image.data(im);
$magnifier.css('background-image','url('+this.src+')');
$loader.hide();
compute_position_magnifier_invariants();
if (is_enabled()) {
if (current_settings.magnifier.top || current_settings.magnifier.left) {
// Show magnifier when initial position is set.
position_magnifier(current_settings.magnifier.top, current_settings.magnifier.left);
current_settings.magnifier.top = current_settings.magnifier.left = 0; // reset
$magnifier.fadeIn(fast);
if (settings.magnifier.frozen !== false) freeze();
}
else {
// Initially position magnifier in center of picture so magnifier always appears.
position_magnifier(250,175);
current_settings.magnifier.top = current_settings.magnifier.left = 0; // reset
$magnifier.fadeIn(fast);
freeze(false);
}
}
else {
freeze(false);
}
current_settings.magnifierReady = true;
current_settings.finished = true;
})
.on('error.im',function() {
current_settings = $image.data(im);
current_settings.magnifierReady = current_settings.imageReady = false;
});;
$container.append($magnifier);
//
// Setup image to initiate magnifier upon load.
//
$image
.addClass(im_image)
.css({cursor:'default'})
.on('mousedown.im',function(event) {
if (is_enabled() && is_ready()) {
position_magnifier_handler(event,true);
if ($.browser && $.browser.webkit) freeze(false); // allow drag and drop
if (! frozen) $magnifier.css({cursor:current_settings.magnifier.cursor});
return false;
}
})
.on('click.im',function(event) {
// If computer too slow to position magnifier so it can receive the click event, it gets triggered here.
if (is_enabled()) return false; // gobble event, safety
})
.on('load.im',function() {
var $this = $(this);
current_settings = $this.data(im);
$this.fadeIn(fast);
$magnifier.hide();
// Resize surrounding container
$container.width($this.outerWidth());
$container.height($this.outerHeight());
$loader.width($this.outerWidth());
current_settings.imageReady = true;
// Avoid downloading large image if not enabled.
current_settings.finished = false;
if (! is_enabled()) {
$loader.hide();
return;
}
// Load magnifier image
$loader.show();
var magnifier_image_src = (typeof current_settings.largesrc === 'function')
? current_settings.largesrc.call(this,this.src)
: $this.data('largesrc');
if ($magnifierImage.prop('src') == magnifier_image_src) {
$magnifierImage.trigger('load'); // manually trigger load event
}
else {
$magnifierImage.prop('src',magnifier_image_src);
}
})
.on('error.im',function() {
current_settings = $(this).data(im);
current_settings.imageReady = current_settings.magnifierReady = false;
});
//
// Save settings and flags for magnifer with each image.
//
$image.data(im,$.extend({frozen:frozen,finished:false,imageReady:false,magnifierReady:false},settings));
$(window).resize(function(){ compute_position_magnifier_invariants(); });
}
$.fn.imageMagnifier = function() {
var settings = {},
// Arguments
src = null,
largesrc = null,
options = {},
nargs = arguments.length;
if (nargs > 0) {
if (typeof arguments[nargs-1] == 'object') {
options = arguments[--nargs];
}
if (nargs == 1) {
largesrc = arguments[0];
}
else if (nargs >= 2) {
src = arguments[0];
largesrc = arguments[1];
}
}
$.extend(true, settings, $.imageMagnifier.options, options);
settings.enabled = settings.enabled !== null ? settings.enabled : $.imageMagnifier.enabled;
settings.magnifier.radius = settings.magnifier.radius !== null ? settings.magnifier.radius : settings.magnifier.height/2;
return this.each(function() {
var $this = $(this),
image_src = src !== null
? src
: settings.image
? settings.image
: $this.prop('src'),
magnifier_image_src = largesrc
? largesrc
: settings.magnifier.image
? settings.magnifier.image
: typeof settings.largesrc === 'function'
? settings.largesrc.call(this,image_src)
: $this.data('largesrc')
? $this.data('largesrc')
: '';
if (! magnifier_image_src) return; // no large image to show in magnifier
$this.data('largesrc',magnifier_image_src); // save largesrc for later
if (! $this.data(im)) {
setup_image_magnifier($this,image_src,magnifier_image_src,settings);
if ($this.prop('src') == image_src) {
if ($this.prop('complete')) $this.trigger('load'); // manually trigger load event
}
else {
$this.fadeOut(fast);
$this.prop('src',image_src);
}
}
else {
// Switch images
$this.data(im,$.extend(true,{},$this.data(im),{magnifier:{top:0,left:0}},options));
$('.'+im_magnifier,$this.parent()).hide();
$('.'+im_loader,$this.parent()).show();
$this.fadeOut(fast, function(){
if ($this.prop('src') == image_src) {
// Manually trigger load event only if image was previously found
if ($this.data(im).imageReady) $this.trigger('load');
}
else {
// Trigger a reload of the magnifier by assigning image src
$this.prop('src',image_src);
}
});
}
});
return this;
};
// Search for images with data-largesrc attributes and auto-initialize them.
$(window).on('load.im',function() {
if ($.imageMagnifier.autoinit) {
$.imageMagnifier.init()
}
});
})(jQuery);