/*
* Exposure (http://http://exposure.blogocracy.org/)
* Copyright (c) 2010 Kristoffer Jelbring
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
(function($) {
/**
* @name Exposure
* @author Kristoffer Jelbring (kris@blogocracy.org)
* @version 0.9.1
*
* @type jQuery
* @cat plugins/Media
*
* @desc Turn a simple HTML list into a rich and smart photo viewer that handles very large amounts of photos.
*
* @example $('#images').exposure({options});
*
* @options
* target: (selector string) Where to insert the image being displayed. Defaults to '#exposure'. If no target is found, one will be created.
* showThumbs: (boolean) Display thumbnails or not. Defaults to true.
* showControls: (boolean) Display paging controls or not. Defaults to true, but will be set to false if missing controlsTarget or if carouselControl is set to true.
* imageControls: (boolean) Switch paging controls to use images instead of pages. Defaults to false.
* controls: (object) Display only certain paging controls. All controls default to true. Usage example: controls : { prevNext : true, pageNumbers : true, firstLast : false }
* carouselControls: (boolean) Enable carousel type controls instead of the classic paging type controls. Defaults to false, but will be set to false if showThumbs is also set to false.
* enableSlideshow: (boolean) Enable slideshow. Defaults to true.
* slideshowControlsTarget: (selector string) Where to insert the slideshow controls. Defaults to null.
* autostartSlideshow: (boolean) Automatically start the slideshow when the gallery is loaded. Defaults to false.
* slideshowDelay: (number) Delay for each slide in the slideshow (in milliseconds). Defauts to 3000.
* onSlideshowPlayed: (function) Callback funcation that is called when the slideshow is played.
* onSlideshowPaused: (function) Callback funcation that is called when the slideshow is paused.
* showCaptions: (boolean) Display captions or not. Captions are added by setting a title attribute on the items in the list.
* showExtraData: (boolean) Display extra image data or not. This data is added by inserting inner HTML to the items in the list.
* dataTarget: (selector string) Where to insert captions and extra image data. Defaults to null, in which case the data container will appended to the main Exposure target.
* controlsTarget: (selector string) Where to insert the paging controls. Defaults to null.
* onThumb: (function) Callback function that is called when a thumbnail is displayed.
* onImage: (function) Callback function that is called when an image is displayed. Defaults to removing the previous image.
* onCarousel: (function) Callback function that is called right before the image carousel is updated.
* onNext: (function) Callback function that is called when nextImage is called.
* onPrev: (function) Callback function that is called when prevImage is called.
* onPageChanged: (function) Callback function that is called when goToPage is called. Is not called when carouselControls is set. Defaults to showing all thumbnails on the current page.
* onPagingLink: (function) Callback function that is called when a new paging link has been added. Defaults to returning the link.
* separatePageBrowsing: (boolean) Enable separate page browsing (change page without changing the image being viewed). Defaults to false.
* loop: (boolean) Start over when last image is reached.
* onEndOfLoop: (function) Callback function that is called when the last image is reached and loop option is set to false.
* viewFirstImage: (boolean) Enable automatic showing of the first image in the gallery when the gallery is loaded. Defaults to true.
* pageSize: (number) Maximum number of images (thumbnails) per page. Defaults to 5.
* visiblePages: (number) Maxium number of pages visible in paging.
* preloadBuffer: (number) Maximum number of images to keep in load queue at any given time. Defaults to 3.
* keyboardNavigation: (boolean) Enable keyboard navigation. Defaults to true.
* clickingNavigation: (boolean) Enable browsing by clicking the image being shown. Defaults to true.
* fixedContainerSize: (boolean) Enable a fixed size target element (set the size using CSS) instead of one that adapts to the size of the current image. Defaults to false.
* maxWidth: (number) Maximum image width in the gallery (larger images will be downscaled). Defaults to null.
* maxHeight: (number) Maximum image height in the gallery (larger images will be downscaled). Defaults to null.
* stretchToMaxSize: (boolean) Stretch all images to maxWidth and maxHeight. Defaults to false.
* fullScreen: (boolean) Stretch all images to be viewn in full screen. Defaults to false.
* onEnterFullScreen: (function) Callback function that is called when entering full screen mode. Defaults to showing background mask.
* onExitFullScreen: (function) Callback function that is called when exiting full screen mode. Defaults to hiding target and background mask.
* showThumbToolTip: (boolean) Display captions as thumbnail tooltips or not. Defaults to true.
* onEmpty: (function) Called when the gallery is empty. Defaults to removing controls and targets and to hiding the list element that the plugin is called on.
* onInit: (function) Called when the gallery has been initialized.
* allowDuplicates: (boolean) Allow the same image to be added more than once. Defaults to true.
* jsonSource: (JSON data string/URL to JSON data/JSON object) Load additional images from an external source using JSON. Defaults to null.
*/
var $$ = $.fn.exposure = function($args) {
var v = "0.9.1";
var i;
var $defaults = {
target : '#exposure',
showThumbs : true,
showControls : true,
imageControls : false,
controls : {
prevNext : true,
firstLast : true,
pageNumbers : true
},
carouselControls: false,
enableSlideshow : true,
slideshowControlsTarget : null,
autostartSlideshow : true,
slideshowDelay : 3000,
onSlideshowPlayed : function() {},
onSlideshowPaused : function() {},
showCaptions : true,
showExtraData : true,
dataTarget : null,
controlsTarget : null,
onThumb : function(thumb) {},
onImage : function(image, imageData, thumb) {
$('.exposureWrapper > .exposureLastImage').remove();
},
onCarousel : function(firstImage, lastImage) {},
onNext : function() {},
onPrev : function() {},
onPageChanged : function() {
$('.exposureThumbs li.current').show().each(function(i) {
var imageHeight = $(this).find('img').height();
if (imageHeight > 0) {
$(this).height(imageHeight);
}
});
},
onPagingLink : function(link) {
return link;
},
separatePageBrowsing : false,
loop : true,
onEndOfLoop : function() {},
pageSize : 5,
viewFirstImage : true,
visiblePages : 2,
preloadBuffer : 3,
keyboardNavigation : true,
clickingNavigation : false,
fixedContainerSize : false,
maxWidth : null,
maxHeight : null,
stretchToMaxSize : false,
fullScreen : false,
onEnterFullScreen : function(mask) {
mask.show();
},
onExitFullScreen : function(target, mask) {
target.hide();
mask.hide();
},
showThumbToolTip : true,
onEmpty : function() {
$('.exposureThumbs').hide();
$($.exposure.target).remove();
if ($.exposure.showControls) {
$($.exposure.controlsTarget).remove();
}
if ($.exposure.slideshowControlsTarget) {
$($.exposure.slideshowControlsTarget).remove();
}
},
onInit : function() {},
allowDuplicates : true,
jsonSource : null
};
var opts = $.extend($defaults, $args);
for (i in opts) {
if ($$.defined($defaults[i])) {
$.exposure[i] = opts[i];
}
}
if (!$($.exposure.target).length) {
// The target element is missing so it needs to be created.
$('
').insertBefore($(this));
}
var wrapper = $('');
var target = $($.exposure.target).addClass('exposureTarget').append(wrapper);
if ($.exposure.showCaption || $.exposure.showExtraData) {
// Determine which image data to display (caption and/or additional data).
var dataElementsHtml = '';
if ($.exposure.showCaptions) {
dataElementsHtml += '';
}
if ($.exposure.showExtraData) {
dataElementsHtml += '';
}
// Append image data container.
var dataElements = $(dataElementsHtml);
if (dataElements.length) {
if ($.exposure.dataTarget && $($.exposure.dataTarget).length) {
$($.exposure.dataTarget).addClass('exposureData').append(dataElements);
} else {
$.exposure.dataTarget = null;
target.append($('').append(dataElements));
}
}
}
// Don't use carousel controls if not showing thumbs.
if (!$.exposure.showThumbs) {
$.exposure.carouselControls = false;
}
// Don't show paging controls if using carousel controls, or if there is no controls target or if all individual controls have been turned off.
if ($.exposure.carouselControls || !$.exposure.controlsTarget || (!$.exposure.controls.prevNext && !$.exposure.controls.firstLast && !$.exposure.controls.pageNumbers)) {
$.exposure.showControls = false;
}
// Render controls.
if ($.exposure.showControls) {
$($.exposure.controlsTarget).addClass('exposureControls').each(function() {
if ($.exposure.controls.firstLast) { $(this).append($('' + $.exposure.texts.first + '').click($.exposure.first)); }
if ($.exposure.controls.prevNext) { $(this).append($('' + $.exposure.texts.previous + '').click($.exposure.prev)); }
if ($.exposure.controls.pageNumbers) { $(this).append($('')); }
if ($.exposure.controls.prevNext) { $(this).append($('' + $.exposure.texts.next + '').click($.exposure.next)); }
if ($.exposure.controls.firstLast) { $(this).append($('' + $.exposure.texts.last + '').click($.exposure.last)); }
});
}
// Only render slideshow controls if there is a slideshow controls target.
if ($.exposure.enableSlideshow && $.exposure.slideshowControlsTarget) {
$($.exposure.slideshowControlsTarget).addClass('exposureSlideshowControls').each(function() {
$(this).append($('' + $.exposure.texts.play + '').click($.exposure.playSlideshow));
$(this).append($('' + $.exposure.texts.pause + '').hide().click($.exposure.pauseSlideshow));
});
}
// Bind keys for navigation (using Hotkeys Plugin).
if ($.exposure.keyboardNavigation) {
$(document).bind('keyup', 'left', $.exposure.prevImage);
$(document).bind('keyup', 'right', $.exposure.nextImage);
$(document).bind('keyup', 'ctrl+left', $.exposure.prevPage);
$(document).bind('keyup', 'ctrl+right', $.exposure.nextPage);
$(document).bind('keyup', 'up', $.exposure.lastImage);
$(document).bind('keyup', 'down', $.exposure.firstImage);
$(document).bind('keyup', 'ctrl+up', $.exposure.lastPage);
$(document).bind('keyup', 'ctrl+down', $.exposure.firstPage);
if ($.exposure.enableSlideshow) {
$(document).bind('keyup', 'space', $.exposure.toggleSlideshow);
}
}
if ($.exposure.fullScreen) {
$(window).resize($.exposure.fitToWindow);
$('').click($.exposure.exitFullScreen).insertAfter(target);
if ($.exposure.keyboardNavigation) {
$(document).bind('keyup', 'esc', $.exposure.exitFullScreen);
}
}
var jsonImages = null;
if ($.exposure.jsonSource) {
if ($$.object($.exposure.jsonSource)) {
jsonImages = $.exposure.jsonSource;
} else if ($.exposure.jsonSource.length) {
if ($$.startsWith($.exposure.jsonSource, "http://") || $$.startsWith($.exposure.jsonSource, "https://")) {
// Fetch JSON images using AJAX from specified URL source.
jsonImages = $.ajax({url : $.exposure.jsonSource,
type : 'GET',
async : false
}).responseText;
} else {
jsonImages = $.exposure.jsonSource;
}
}
}
// Return "this" to maintain chainability.
return this.addClass('exposureThumbs').each(function() {
var i;
if (jsonImages) {
var images = $$.object(jsonImages) ? jsonImages : $.parseJSON(jsonImages);
if (images && images.data) {
// Append images fetched from JSON source to the list of images.
for (i in images.data) {
var photo = images.data[i];
if (photo.source && photo.source.length) {
var item = $('');
var link = $('').attr('href', photo.source);
if (photo.thumb_source && photo.thumb_source.length) {
var thumb = $('
').attr('src', photo.thumb_source);
if (photo.caption && photo.caption.length) {
thumb.attr('title', photo.caption);
}
link.append(thumb);
} else if (photo.caption && photo.caption.length) {
link.attr('title', photo.caption);
}
item.append(link);
if (photo.extra_data && photo.extra_data.length) {
item.append($(photo.extra_data));
}
$(this).append(item);
}
}
}
}
var foundImage = false;
var foundThumb = false;
if ($(this).children('li').length) {
var selectedIndex = null;
$(this).show().children('li').each(function() {
foundImage = true;
// The a tag contains all the needed information about the image.
var a = $(this).find('a');
if (a.length) {
// Use only the first matching link.
a = $(a[0]);
var src = a.attr('href');
var img = a.find('img');
// Get caption and thumbnail source from either nested img tag or from rel attribute.
var thumbSrc = img.length ? img.attr('src') : a.attr('rel');
var caption = img.length ? img.attr('title') : a.attr('title');
var isSelected = a.hasClass('selected') && !selectedIndex;
// Remove link and extract additional image data.
a.remove();
var thumbData = $(this).html();
if (thumbSrc) {
foundThumb = true;
}
// All information extracted, remove original list entry.
$(this).remove();
// Add image to list of images.
var imageIndex = $$.newImage(src, thumbSrc, caption, thumbData);
if (imageIndex > -1) {
if (isSelected) {
selectedIndex = imageIndex;
}
if ($$.loadQueue.length < $.exposure.preloadBuffer) {
// Preload buffer hasn't been filled yet, add image to load queue.
$$.addToLoadQueue(imageIndex);
}
}
} else {
// Just remove this empty entry.
$(this).remove();
}
});
if (!$.exposure.showThumbs) {
// Thumbnails are turned off, change page size to 1.
$.exposure.pageSize = 1;
// Remove the thumbnails container.
$('.exposureThumbs').remove();
}
if (foundImage) {
// Start preloading the first image.
$$.preloadNextInQueue();
$$.createPaging();
if (selectedIndex) {
$.exposure.goToPage($.exposure.pageNumberForImage(selectedIndex));
$.exposure.viewImage(selectedIndex);
} else {
// View the first page (and the first image).
$.exposure.goToPage(1);
}
if ($.exposure.enableSlideshow && $.exposure.autostartSlideshow) {
$.exposure.playSlideshow();
}
} else {
$.exposure.onEmpty();
}
} else {
$.exposure.onEmpty();
}
$.exposure.onInit();
$$.initialized = true;
});
};
// Private functions and properties. These are only for internal use.
/**
* Check if a variable is defined.
*
* @param v Variable to check.
*/
$$.defined = function(v) {
return typeof v !== 'undefined';
};
/**
* Check if a variable is an object.
*
* @param v Variable to check.
*/
$$.object = function(v) {
return typeof v === 'object';
};
/**
* Check if a string starts with another string.
*
* @param s1 String to check.
* @param s2 String to look for.
*/
$$.startsWith = function(s1, s2) {
if (s1 && s2) {
return s1.match("^"+s2) === s2;
}
return false;
};
/**
* Calculate the differance in outerwidth and width of an element.
*
* @param el The element to check.
* @returns Width differance.
*/
$$.widthDiff = function(el) {
return el ? el.outerWidth(true)-el.width() : 0;
};
/**
* Calculate the differance in outerHeight and height of an element.
*
* @param el The element to check.
* @returns Height differance.
*/
$$.heightDiff = function(el) {
return el ? el.outerHeight(true)-el.height() : 0;
};
/**
* Value object representing an image in the viewer.
*
* @param src Source to the full size image.
* @param thumb Source to thumbnail version of the image.
* @param caption Image caption.
* @param data Extra image data.
*/
$$.Image = function(src, thumb, caption, data) {
this.src = src;
this.thumb = thumb;
this.caption = caption;
this.data = data;
this.loaded = false;
};
/**
* All the images in the viewer. Holds an array of Image objects that are filled up when the plugin is loaded.
*/
$$.images = [];
/**
* All the image sources that's been previously added to the viewer.
*/
$$.sources = {};
/**
* Create a new Image object and add it to images array.
*
* @param src Source to the full size image.
* @param thumb Source to thumbnail version of the image.
* @param caption Image caption.
* @param data Extra image data.
* @returns Index of the new image.
*/
$$.newImage = function(src, thumb, caption, data) {
var alreadyAdded = $$.defined($$.sources[src]);
if (alreadyAdded && !$.exposure.allowDuplicates) {
return -1;
}
var image = new $$.Image(src, thumb, caption, data);
var imageIndex = $$.images.push(image) - 1;
if (!alreadyAdded) {
$$.sources[src] = imageIndex;
}
return imageIndex;
};
/**
* Initialization flag.
*/
$$.initialized = false;
/**
* Index of the image currently being viewed.
*/
$$.current = -1;
/**
* Deselect the image currently being viewed.
*/
$$.deselectCurrentImage = function() {
$$.current = -1;
$('.exposureThumbs li.active').removeClass('active');
};
/**
* The load queue, holds an array of indices of images to load.
*/
$$.loadQueue = [];
/**
* Add an image to the load queue.
*
* @param index Index of image to add.
*/
$$.addToLoadQueue = function(index) {
if (!$$.loaded(index) && !$$.queued(index)) {
$$.loadQueue.push(index);
}
};
/**
* Check if a specific image exists in the load queue.
*
* @param index Index of image to check.
*/
$$.queued = function(index) {
return $.inArray(index, $$.loadQueue) > -1;
};
/**
* Check if a specific image has been loaded.
*
* @param index Index of image to check.
*/
$$.loaded = function(index) {
var image = $.exposure.getImage(index);
if (image !== null) {
return image.loaded;
}
return false;
};
/**
* Find the next, not already loaded image, in the load queue. This function is recursive and will continue until
* an image is found, or until the queue is empty.
*/
$$.nextInLoadQueue = function() {
var i;
if ($$.loadQueue.length > 0) {
var next = $$.loadQueue.shift();
if ($$.loaded(next)) {
// Image already loaded, remove from load queue.
i = $.inArray(index, $$.loadQueue);
$$.loadQueue.splice(i, 1);
// Find next in queue.
return $$.nextInLoadQueue();
}
return next;
}
return null;
};
/**
* Preload the next image in the load queue.
*/
$$.preloadNextInQueue = function() {
if ($$.loadQueue.length > 0) {
var nextIndex = $$.nextInLoadQueue();
if (nextIndex !== null) {
$$.loadImage(nextIndex, $$.preloadNextInQueue);
}
}
};
/**
* Load a specific page.
*
* @param page Number of the page to load.
* @param imageToView Index of the image to view (defaults to viewing first image on page if this parameter isn't set).
*/
$$.loadPage = function(page, imageToView) {
if ($$.validPage(page)) {
// Calculate first and last images on this page.
var last = page * $.exposure.pageSize;
var first = last - $.exposure.pageSize;
if (last > $$.images.length) {
last = $$.images.length;
}
$$.pageTransition = true;
$$.viewThumbs(first, last-1);
if (!$.exposure.separatePageBrowsing) {
if (imageToView) {
// Moving backwards, set the last image on the page as active.
$.exposure.viewImage(imageToView);
} else {
if (page > 1 || ((page === 1 && $.exposure.viewFirstImage) || $$.initialized)) {
// Set the first image on this page as active.
$.exposure.viewImage(first);
}
}
}
$$.pageTransition = false;
}
};
/**
* Views thumbnails for a specific set of images (and creates them if needed).
*
* @param first Index of the first image to view.
* @param last Index of the last image to view.
*/
$$.viewThumbs = function(first, last) {
var i;
if ($.exposure.showThumbs) {
// Go through images in set.
for (i = first; i <= last; i++) {
$$.viewThumb(i, i === first, i === last, true);
}
if (!$.exposure.carouselControls && $$.currentPage < $.exposure.numberOfPages()) {
// Preload next page of thumbnails.
var firstNext = last+1;
var lastNext = last+$.exposure.pageSize;
if (lastNext >= $$.images.length) {
lastNext = $$.images.length-1;
}
for (i = firstNext; i <= lastNext; i++) {
var container = $$.viewThumb(i, i === firstNext, i === lastNext, false);
if (container && container.length) {
container.hide();
}
}
}
}
};
/**
* View thumbnail for a specific image (and create it if needed).
*
* @param index Index of the image to view.
* @param first If the image is the first on the page.
* @param last If the image is the last on the page.
* @param current If the image is a part of the current page.
*/
$$.viewThumb = function(index, first, last, currentPage) {
// Make sure image index is in scope.
if (index < 0) {
index = $$.images.length + index;
} else if (index >= $$.images.length) {
index = index - $$.images.length;
}
var image = $$.images[index];
// Find thumbnail container.
var container = $.exposure.getThumb(index).parent();
if (!container.length) {
// Create a thumbnail if one doesn't already exist.
container = $$.createThumbForImage(image, index);
// Add page number as rel attribute.
container.attr('rel', $.exposure.pageNumberForImage(index));
}
if (container.length) {
// Append in the end of the container in order to save the ordering of the images.
container.parent().append(container);
if (first) {
// Decorate thumbnail container for first image on page.
container.addClass('first');
} else {
container.removeClass('first');
}
if (last) {
// Decorate thumbnail container for last image on page.
container.addClass('last');
} else {
container.removeClass('last');
}
if (currentPage) {
if ($.exposure.carouselControls) {
container.show();
} else {
container.addClass('current');
}
}
}
return container;
};
/**
* Load a specific image.
*
* @param index Index of image to load.
* @param onload Image onload callback function.
*/
$$.loadImage = function(index, onload) {
var image = $.exposure.getImage(index);
var img = $('
').addClass('exposureImage');
var i;
if (image !== null) {
image.loaded = true;
if ($$.queued(index)) {
// Since image already has been loaded, remove it from the load queue.
i = $.inArray(index, $$.loadQueue);
$$.loadQueue.splice(i, 1);
}
if (typeof onload === 'function') {
img.load(onload);
}
img.attr('src', image.src);
}
return img;
};
/**
* Create a thumbnail for a specific image.
*
* @param image Image object for the image.
* @param image Index of the image.
*/
$$.createThumbForImage = function(image, index) {
if ($.exposure.showThumbs) {
var thumb = $.exposure.getThumb(index);
if (thumb === null || !thumb.length) {
// Create thumbnail container.
var container = $('');
$('.exposureThumbs').append(container);
// Create thumbnail img element.
thumb = $('
');
if (image.thumb) {
thumb.attr('src', image.thumb);
} else {
// Create a thumbnail from the original image.
thumb.attr('src', image.src);
// Downscale the new thumbnail.
var imageWidth = Math.ceil(thumb.width() / thumb.height() * container.height());
var imageHeight = Math.ceil(thumb.height() / thumb.width() * container.width());
if (imageWidth < imageHeight) {
thumb.css({height: 'auto', maxWidth: container.width()});
} else {
thumb.css({width: 'auto', maxHeight: container.height()});
}
}
container.append(thumb.css('display', 'block'));
// Add image index and caption as attributes.
thumb.attr('rel', index);
if (image.caption && $.exposure.showThumbToolTip) {
thumb.attr('title', image.caption);
}
// Save extra image data in thumbnail data.
thumb.data('data', image.data);
thumb.click(function() {
// When a thumbnail is clicked, view full version of that image.
$.exposure.viewImage(Number($(this).attr('rel')));
});
thumb.load(function() {
// Set the height of the thumbnail container to the height of the thumbnail.
var imageHeight = $(this).height();
if (imageHeight > 0) {
$(this).parent().height(imageHeight);
}
});
$.exposure.onThumb(thumb);
return container;
}
}
return null;
};
/**
* Number of the page currently being viewed.
*/
$$.currentPage = 1;
/**
* Check if a specific page number is a valid page number.
*
* @param page Page number to check.
*/
$$.validPage = function(page) {
return page > 0 && page <= $.exposure.numberOfPages();
};
/**
* Create paging links.
*/
$$.createPaging = function() {
var i;
if ($.exposure.showControls && $.exposure.controls.pageNumbers) {
// Create paging links.
var stop = $.exposure.imageControls ? $.exposure.numberOfImages() : $.exposure.numberOfPages();
$('.exposurePaging').each(function() {
for (i = 1; i <= stop; i++) {
$(this).append($$.newPagingLink(i));
}
});
}
};
/**
* Update paging links.
*/
$$.updatePaging = function(newActivePage) {
if ($.exposure.showControls && $.exposure.controls.pageNumbers) {
var current = $.exposure.imageControls ? $$.current+1 : $$.currentPage;
$('.exposurePaging span.active').each(function() {
$(this).replaceWith($$.newPagingLink(current));
});
$('.exposurePaging a[rel="' + newActivePage + '"]').each(function() {
$(this).replaceWith($('' + newActivePage + '').addClass('active'));
});
var pageCount = $.exposure.imageControls ? $.exposure.numberOfImages() : $.exposure.numberOfPages();
if ($.exposure.visiblePages > 0 && pageCount > $.exposure.visiblePages) {
var firstVisiblePage = newActivePage;
var lastVisiblePage = $.exposure.visiblePages;
var flooredVisiblePages = Math.floor($.exposure.visiblePages/2);
if (newActivePage <= flooredVisiblePages) {
firstVisiblePage = 1;
} else if (newActivePage > (pageCount - flooredVisiblePages)) {
lastVisiblePage = pageCount;
firstVisiblePage = lastVisiblePage - $.exposure.visiblePages + 1;
} else {
firstVisiblePage -= flooredVisiblePages;
lastVisiblePage = firstVisiblePage + $.exposure.visiblePages - 1;
}
$('.exposurePaging').each(function() {
$(this).children().each(function(i) {
var currentPage = i+1;
if (currentPage >= firstVisiblePage && currentPage <= lastVisiblePage) {
$(this).show();
} else {
$(this).hide();
}
});
});
}
}
};
/**
* Create a new paging link for a specific page.
*
* @param page Index of the image/number of the page (depending on the imageControls setting) to create the link for.
*/
$$.newPagingLink = function(index) {
return $.exposure.onPagingLink($('' + index + '').click(function() {
// View the image/page defined in the rel attribute of the link.
var rel = Number($(this).attr('rel'));
if ($.exposure.imageControls) {
$.exposure.viewImage(rel-1);
} else {
$.exposure.goToPage(rel);
}
}));
};
/**
* Page transition state.
*/
$$.pageTransition = false;
/**
* Slideshow playing state.
*/
$$.playingSlideshow = false;
/**
* Holds the timer for the slideshow.
*/
$$.slideshowTimer = null;
/**
* Slideshow transition state.
*/
$$.slideshowTransition = false;
/**
* Recursive function that runs nextImage() after given delay. Don't use this directly, use playSlideshow() instead.
*/
$$.slideshow = function() {
$$.slideshowTimer = setTimeout(function() {
$$.slideshowTransition = true;
$.exposure.nextImage();
$$.slideshowTransition = false;
$$.slideshow();
}, $.exposure.slideshowDelay);
};
/**
* Full screen state.
*/
$$.infullScreen = false;
/**
* Calculate actual max width (subtract padding, margin and borders of image and container),
*/
$$.actualMaxWidth = function(image, target) {
return $.exposure.maxWidth ? $.exposure.maxWidth-($$.widthDiff(image)+$$.widthDiff(target)) : 0;
};
/**
* Calculate actual max height (subtract padding, margin and borders of image and container),
*/
$$.actualMaxHeight = function(image, target) {
return $.exposure.maxHeight ? $.exposure.maxHeight-($$.heightDiff(image)+$$.heightDiff(target)) : 0;
};
/**
* Fix image to max size.
*/
$$.fitToMaxSize = function(image) {
var target = $('.exposureTarget');
if ($.exposure.stretchToMaxSize) {
if ($.exposure.maxWidth) {
// Stretch to maxWidth.
image.width($$.actualMaxWidth(image, target));
}
if ($.exposure.maxHeight) {
// Stretch to maxHeight.
image.height($$.actualMaxHeight(image, target));
}
} else {
if (image.width() > image.height()) {
// Landscape format image, fit to width first.
$$.fitToMaxWidth(image, target);
$$.fitToMaxHeight(image, target);
} else if (image.height() > image.width()) {
// Portrait format image, fit to height first.
$$.fitToMaxHeight(image, target);
$$.fitToMaxWidth(image, target);
} else {
// Square format image.
var actualMaxHeight = $$.actualMaxHeight(image, target);
var smallest = $$.actualMaxWidth(image, target);
if (!smallest || (actualMaxHeight && smallest && actualMaxHeight < smallest)) {
smallest = actualMaxHeight;
}
if (smallest && image.width() > smallest) {
image.width(smallest);
image.height(smallest);
}
}
}
};
/**
* Center main image in window.
*/
$$.centerImageInWindow = function(image) {
var target = $('.exposureTarget');
target.width(image.width()).height(image.height());
target.css({
'top' : ($(window).height()-target.outerHeight(true))/2,
'left' : ($(window).width()-target.outerWidth(true))/2
});
$('.exposureLastImage').each(function() {
$(this).css({
'top' : (target.height()-$(this).height())/2,
'left' : (target.width()-$(this).width())/2
});
});
};
/**
* Fit image to maxWidth.
*/
$$.fitToMaxWidth = function(image, target) {
var actualMaxWidth = $$.actualMaxWidth(image, target);
if (actualMaxWidth && image.width() > actualMaxWidth) {
// Calculate new height to maintain aspect ratio.
var newHeight = Math.round(actualMaxWidth * image.height()/image.width());
image.height(newHeight);
// Shrink to maxWidth.
image.width(actualMaxWidth);
}
};
/**
* Fit image to maxHeight.
*/
$$.fitToMaxHeight = function(image, target) {
var actualMaxHeight = $$.actualMaxHeight(image, target);
if (actualMaxHeight && image.height() > actualMaxHeight) {
// Calculate new width to maintain aspect ratio.
var newWidth = Math.round(actualMaxHeight * image.width()/image.height());
image.width(newWidth);
// Shrink to maxHeight.
image.height(actualMaxHeight);
}
};
/**
* Resize target container element to fit image.
*/
$$.resizeContainer = function(img) {
// Resize image according to maxWidth and maxHeight settings.
$$.fitToMaxSize(img);
// Resize target element to fit image.
if (!$.exposure.fixedContainerSize) {
$('.exposureTarget').show().width(img.width()).height(img.height());
}
};
// Extend with public functions. These can be called from your gallery using $.exposure.nameOfFunction().
$.extend({exposure : {
/**
* Calculate the page number of a specific image.
*
* @param index Index of image to get page number for.
*/
pageNumberForImage : function(index) {
return Math.ceil((index + 1) / $.exposure.pageSize);
},
/**
* Calculate the total number of pages using the set page size.
*/
numberOfPages : function() {
// Calculate the page number for the last image.
return $.exposure.pageNumberForImage($$.images.length-1);
},
/**
* Check if the the page currently being viewed is the first page.
*/
atFirstPage : function() {
return $$.currentPage === 1;
},
/**
* Check if the the page currently being viewed is the last page.
*/
atLastPage : function() {
return $$.currentPage === $.exposure.numberOfPages();
},
/**
* Check if an image is the first image on its page.
*
* @param index Index of image to check. Will default to image currently being viewed if not set.
*/
firstImageOnPage : function(index) {
if (!index) {
index = $$.current;
}
return $.exposure.pageSize === 1 || (index % $.exposure.pageSize === 0);
},
/**
* Check if the an image is the last image on its page.
*
* @param index Index of image to check. Will default to image currently being viewed if not set.
*/
lastImageOnPage : function(index) {
if (!index) {
index = $$.current;
}
var imageCount = $$.images.length;
if ($.exposure.pageSize === 1 || imageCount === 1) {
return true;
}
if (index > 0) {
var currentPageSize = $.exposure.pageSize;
var currentPage = $.exposure.pageNumberForImage(index);
if (currentPage === $.exposure.numberOfPages()) {
// Calculate the size of the last page as it may differ from the set page size.
var newPageSize = imageCount % $.exposure.pageSize;
if (newPageSize > 0) {
currentPageSize = newPageSize;
}
}
var imageIndex = index;
if (currentPage > 1) {
imageIndex -= (currentPage-1) * $.exposure.pageSize;
}
// Check if the current image is the last image of the current page.
return (imageIndex+1) % currentPageSize === 0;
}
return false;
},
/**
* Get the number of the current page.
*/
currentPage : function() {
return $$.currentPage;
},
/**
* Get the number of images.
*/
numberOfImages : function() {
return $$.images.length;
},
/**
* Check if the image currently being viewed is the first image.
*/
atFirstImage : function() {
return $$.current === 0;
},
/**
* Check if the image currently being viewed is the last image.
*/
atLastImage : function() {
return $$.current === $.exposure.numberOfImages()-1;
},
/**
* Get a spefic image object from the images array.
*
* @param index Index of image to get.
*/
getImage : function(index) {
if (index !== null && index > -1 && index < $$.images.length) {
return $$.images[index];
}
return null;
},
/**
* Get the index of the image with the specified image source.
*
* @param src Source of the image to get index for.
*/
indexOfImage : function(src) {
if (src && $$.defined($$.sources[src])) {
return $$.sources[src];
}
return -1;
},
/**
* Get the index of the current image.
*/
currentImage : function() {
return $$.current;
},
/**
* Dynamically add an image to the gallery.
*
* @param src Source to the full size image.
* @param thumb Source to thumbnail version of the image.
* @param caption Image caption.
* @param data Extra image data.
*/
addImage : function(src, thumb, caption, data) {
var pageCount = $.exposure.numberOfPages();
var index = $$.newImage(src, thumb, caption, data);
if (index > -1) {
var pageNumber = $.exposure.pageNumberForImage(index);
var containers = $('.exposureThumbs li[rel="'+ pageNumber + '"]');
if (containers.length) {
containers.removeClass('last');
}
// Recreate paging if a new page needs to be added.
var newPageAdded = pageNumber > pageCount;
if (newPageAdded) {
// Make sure paging container is empty.
$('.exposurePaging').empty();
$$.createPaging();
}
if (newPageAdded || pageNumber === $$.currentPage) {
// Reload the current page.
$.exposure.goToPage($$.currentPage);
}
}
},
/**
* Dynamically remove a specific image from the gallery.
*
* @param index Index of image to remove.
*/
removeImage : function(index) {
if ($$.images.length === 1) {
$.exposure.removeAllImages();
} else {
if ($.exposure.enableSlideshow) {
$.exposure.pauseSlideshow();
}
var oldPageCount = $.exposure.numberOfPages();
// Remove the image from the list of images.
$$.images.splice(index, 1);
// Remove the image from the loadQueue.
var queueIndex = $.inArray(index, $$.loadQueue);
if (queueIndex > -1) {
$$.loadQueue.splice(queueIndex, 1);
}
// Remove thumbnail and container.
var container = $.exposure.getThumb(index).parent();
container.remove();
// Update thumbnail containers.
$('.exposureThumbs > li').each(function(i) {
if (i >= index) {
// Update page number in rel attribute.
var newRel = $.exposure.pageNumberForImage(i);
$(this).attr('rel', newRel);
// Update index number in rel attribute of image.
$(this).find('img').attr('rel', i);
// Update first/last classes
if ($.exposure.firstImageOnPage(i)) {
$(this).addClass('first');
} else {
$(this).removeClass('first');
}
if ($.exposure.lastImageOnPage(i)) {
$(this).addClass('last');
} else {
$(this).removeClass('last');
}
if ($$.currentPage === newRel) {
$(this).show();
} else {
$(this).hide();
}
}
});
// Recreate paging links.
var pageRemoved = $.exposure.numberOfPages < oldPageCount;
if (pageRemoved) {
// Make sure paging container is empty.
$('.exposurePaging').empty();
$$.createPaging();
}
if ($$.current === index) {
// Skip to next image if the deleting image was the currently viewed image.
$$.current = -1;
var nextIndex = index;
if (index === $.exposure.numberOfImages()) {
nextIndex = 0;
}
$.exposure.viewImage(nextIndex);
}
}
},
/**
* Removes all images from the gallery. Usable when dynamically rebuilding the gallery from scratch.
*/
removeAllImages : function() {
$$.images = [];
$$.sources = {};
$$.loadQueue = [];
if ($.exposure.enableSlideshow) {
$.exposure.pauseSlideshow();
}
$('.exposureThumbs').empty();
$('.exposurePaging').empty();
$.exposure.viewImage(0);
},
/**
* Get the thumbnail img element for a specific image.
*
* @param index Index of image to find thumbnail for.
*/
getThumb : function(index) {
return $('.exposureThumbs img[rel="'+index+'"]');
},
/**
* Get the index of the next image.
*/
getNextImage : function() {
if ($$.current === $$.images.length-1) {
// Is at last image, return first image.
if ($.exposure.loop) {
return 0;
} else {
// Loop ended callback.
$.exposure.onEndOfLoop();
}
} else {
// Return next image.
return $$.current+1;
}
return null;
},
/**
* Get the index of the previous image.
*/
getPrevImage : function() {
if ($$.current === 0) {
// Is at first image, return last image.
if ($.exposure.loop) {
return $$.images.length-1;
}
} else {
// Return previous image.
return $$.current-1;
}
return null;
},
/**
* View a specific page.
*
* @param page Number of the page to view.
* @param imageToView Index of the image to view (defaults to viewing first image on page if this parameter isn't set).
*/
goToPage : function(page, imageToView) {
if ($$.validPage(page)) {
// Hide all thumbnail containers.
$('.exposureThumbs li').removeClass('current').hide();
$$.loadPage(page, imageToView);
if (!$.exposure.imageControls) {
$$.updatePaging(page);
}
$$.currentPage = page;
if ($.exposure.showControls) {
if ($.exposure.atFirstPage()) {
// Disable first page button.
if ($.exposure.controls.firstLast) {
$('.exposureFirstPage').addClass('disabled');
}
// Hide previous page button.
if (!$.exposure.loop && $.exposure.controls.prevNext) {
$('.exposurePrevPage').hide();
}
} else {
// Enable first page button.
if ($.exposure.controls.firstLast) {
$('.exposureFirstPage').removeClass('disabled');
}
// Show previous page button.
if (!$.exposure.loop && $.exposure.controls.prevNext) {
$('.exposurePrevPage').show();
}
}
if ($.exposure.atLastPage()) {
// Disable last page button.
if ($.exposure.controls.firstLast) {
$('.exposureLastPage').addClass('disabled');
}
// Hide next page button.
if (!$.exposure.loop && $.exposure.controls.prevNext) {
$('.exposureNextPage').hide();
}
} else {
// Enable last page button.
if ($.exposure.controls.firstLast) {
$('.exposureLastPage').removeClass('disabled');
}
// Show next page button.
if (!$.exposure.loop && $.exposure.controls.prevNext) {
$('.exposureNextPage').show();
}
}
}
// Page changed callback.
if (!$.exposure.carouselControls) {
$.exposure.onPageChanged();
}
}
},
/**
* View the first page.
*/
firstPage : function() {
if (!$.exposure.atFirstPage()) {
$.exposure.goToPage(1);
}
},
/**
* View the last page.
*/
lastPage : function() {
if (!$.exposure.atLastPage()) {
$.exposure.goToPage($.exposure.numberOfPages());
}
},
/**
* View the previous page.
*/
prevPage : function() {
if (!$.exposure.atFirstPage()) {
// Go to previous page.
$.exposure.goToPage($$.currentPage-1);
} else if ($.exposure.loop) {
// At first page, go to last page.
$.exposure.goToPage($.exposure.numberOfPages());
}
},
/**
* View the next page.
*/
nextPage : function() {
if (!$.exposure.atLastPage()) {
// Go to next page.
$.exposure.goToPage($$.currentPage+1);
} else if ($.exposure.loop) {
// At last page, go back to first page.
$.exposure.goToPage(1);
}
},
/**
* View a specific image.
*
* @param Index of image to view.
*/
viewImage : function(index) {
if ($$.current !== index) {
if ($.exposure.enableSlideshow && !$$.slideshowTransition) {
$.exposure.pauseSlideshow();
}
var wrapper = $('.exposureWrapper');
var validImage = false;
var image = $$.images[index];
if (image) {
var src = image.src;
var caption = image.caption;
var extraImageData = image.data;
if (src) {
validImage = true;
var hasThumb = $.exposure.showThumbs;
var thumb = null;
if ($.exposure.showThumbs) {
thumb = $('.exposureThumbs img[rel="' + index + '"]');
hasThumb = thumb && thumb.length;
// Light up active thumbnail.
if (hasThumb) {
thumb.parent().siblings().removeClass('active');
thumb.parent().addClass('active');
} else {
$('.exposureThumbs li.active').removeClass('active');
}
}
// Show loading animation.
wrapper.parent().removeClass('exposureLoaded');
if ($$.loaded(index)) {
// Hide loading animation if image already loaded.
wrapper.parent().addClass('exposureLoaded');
}
var img = $$.loadImage(index, function() {
var lastImage = wrapper.find('.exposureImage');
if (lastImage.length) {
lastImage.removeClass('exposureCurrentImage');
lastImage.addClass('exposureLastImage');
}
$(this).addClass('exposureCurrentImage');
wrapper.append($(this));
// Enable browsing by clicking on the image.
if ($.exposure.clickingNavigation) {
$(this).click($.exposure.nextImage);
}
if (!$(this).width() || !$(this).height()) {
// Workaround for bug caused by AdBlock plugin for Chrome and Safari:
// http://code.google.com/p/adblockforchrome/issues/detail?id=3701
var i = $(this);
var delay = setInterval(function() {
$$.resizeContainer(i);
clearTimeout(delay);
}, 2);
} else {
$$.resizeContainer($(this));
}
// Image is supposed to be viewed in full screen.
if ($.exposure.fullScreen && !$$.infullScreen) {
$.exposure.onEnterFullScreen($('.exposureMask'));
$$.infullScreen = true;
}
// Add caption and additional image data.
var imageDataContainer = $.exposure.dataTarget ? $($.exposure.dataTarget) : wrapper.siblings('.exposureData');
if (imageDataContainer.length) {
if ($.exposure.showCaptions) {
// Add caption to image data container.
var captionContainer = imageDataContainer.find('.caption');
if (captionContainer.length) {
// Remove current caption from container.
captionContainer.empty();
if (!caption && hasThumb) {
// Extract caption from thumbnail.
caption = thumb.attr('title');
}
}
captionContainer.html(caption);
}
if ($.exposure.showExtraData) {
// Add extra image data to image data container.
var extraImageDataContainer = imageDataContainer.find('.extra');
if (extraImageDataContainer.length) {
// Remove current data from container.
extraImageDataContainer.empty();
if (!extraImageData && hasThumb) {
// Extract data from thumbnail.
extraImageData = thumb.data('data');
}
extraImageDataContainer.html(extraImageData);
}
}
}
// Image loaded callback.
$.exposure.onImage($(this), imageDataContainer, thumb);
// Preload next image.
$$.preloadNextInQueue();
});
}
}
if (!validImage) {
wrapper.siblings().andSelf().empty();
$('.exposureThumbs li.active').removeClass('active');
}
if ($.exposure.imageControls) {
var page = $.exposure.pageNumberForImage(index);
if ($$.currentPage !== page && !$$.pageTransition) {
$.exposure.goToPage(page, index);
}
$$.updatePaging(index+1);
}
$$.current = index;
// If using carousel controls make sure to properly update the thumbnails.
if ($.exposure.carouselControls && $$.images.length > $.exposure.pageSize) {
var firstVisibleImage = index;
var lastVisibleImage = $.exposure.pageSize-1;
var flooredVisibleImages = Math.floor($.exposure.pageSize/2);
if (!$.exposure.loop && index < flooredVisibleImages) {
firstVisibleImage = 0;
} else if (!$.exposure.loop && index >= ($$.images.length - flooredVisibleImages)) {
lastVisibleImage = $$.images.length-1;
firstVisibleImage = lastVisibleImage - $.exposure.pageSize;
} else {
firstVisibleImage -= flooredVisibleImages;
lastVisibleImage = firstVisibleImage + $.exposure.pageSize-1;
}
$.exposure.onCarousel(firstVisibleImage, lastVisibleImage);
$('.exposureThumbs li').removeClass('current').hide();
$$.viewThumbs(firstVisibleImage, lastVisibleImage);
$$.currentPage = $.exposure.pageNumberForImage(index);
}
}
},
/**
* View first image.
*/
firstImage : function() {
if (!$.exposure.atFirstImage()) {
if ($.exposure.separatePageBrowsing || $.exposure.atFirstPage()) {
$.exposure.viewImage(0);
} else {
$.exposure.goToPage(1);
}
}
},
/**
* View next image.
*/
nextImage : function() {
if (!$.exposure.separatePageBrowsing && $.exposure.lastImageOnPage()) {
if ($.exposure.atLastPage() && $.exposure.loop) {
// At the last page, go back to first page.
$.exposure.goToPage(1);
} else {
// Go to the next page.
$.exposure.goToPage($$.currentPage+1);
}
// Next image callback.
$.exposure.onNext();
} else {
var next = $.exposure.getNextImage();
if (next !== null) {
// Select next image.
$.exposure.viewImage(next);
// Next image callback.
$.exposure.onNext();
}
}
var nextNext = $.exposure.getNextImage();
if (nextNext !== null) {
// Add second next image to load queue.
$$.addToLoadQueue(nextNext);
}
},
/**
* View previous image.
*/
prevImage : function() {
if (!$.exposure.separatePageBrowsing && $.exposure.firstImageOnPage()) {
if ($.exposure.atFirstPage() && $.exposure.loop) {
// At the first page, go to the last page.
$.exposure.goToPage($.exposure.numberOfPages(), $.exposure.numberOfImages()-1);
} else {
// Go to the previous page.
var page = $$.currentPage-1;
$.exposure.goToPage(page, page * $.exposure.pageSize - 1);
}
// Previous image callback.
$.exposure.onPrev();
} else {
var prev = $.exposure.getPrevImage();
if (prev !== null) {
// Select next image.
$.exposure.viewImage(prev);
// Previous image callback.
$.exposure.onPrev();
}
}
var prevPrev = $.exposure.getPrevImage();
if (prevPrev !== null) {
// Add second previous image to load queue.
$$.addToLoadQueue(prevPrev);
}
},
/**
* View last image.
*/
lastImage : function() {
if (!$.exposure.atLastImage()) {
if ($.exposure.separatePageBrowsing || $.exposure.atLastPage()) {
$.exposure.viewImage($.exposure.numberOfImages()-1);
} else {
$.exposure.goToPage($.exposure.numberOfPages(), $.exposure.numberOfImages()-1);
}
}
},
/**
* Play the slideshow.
*/
playSlideshow : function() {
if (!$$.playingSlideshow) {
if ($.exposure.slideshowControlsTarget) {
$('.exposurePlaySlideshow').hide();
$('.exposurePauseSlideshow').show();
}
$$.slideshow();
$$.playingSlideshow = true;
}
$.exposure.onSlideshowPlayed();
},
/**
* Pause the slideshow.
*/
pauseSlideshow : function() {
if ($$.playingSlideshow) {
if ($.exposure.slideshowControlsTarget) {
$('.exposurePlaySlideshow').show();
$('.exposurePauseSlideshow').hide();
}
$$.playingSlideshow = false;
if ($$.slideshowTimer) {
clearTimeout($$.slideshowTimer);
}
$.exposure.onSlideshowPaused();
}
},
/**
* Toggle (play/pause)
*/
toggleSlideshow : function() {
if ($$.playingSlideshow) {
$.exposure.pauseSlideshow();
} else {
$.exposure.playSlideshow();
}
},
/**
* Go to first image/page depending on imageControls setting.
*/
first : function() {
if ($.exposure.imageControls) {
$.exposure.firstImage();
} else {
$.exposure.firstPage();
}
},
/**
* Go to previous image/page depending on imageControls setting.
*/
prev : function() {
if ($.exposure.imageControls) {
$.exposure.prevImage();
} else {
$.exposure.prevPage();
}
},
/**
* Go to next image/page depending on imageControls setting.
*/
next : function() {
if ($.exposure.imageControls) {
$.exposure.nextImage();
} else {
$.exposure.nextPage();
}
},
/**
* Go to last image/page depending on imageControls setting.
*/
last : function() {
if ($.exposure.imageControls) {
$.exposure.lastImage();
} else {
$.exposure.lastPage();
}
},
/**
* Leave full screen mode.
*/
exitFullScreen : function() {
if ($$.infullScreen) {
$.exposure.pauseSlideshow();
$$.deselectCurrentImage();
$.exposure.onExitFullScreen($('.exposureTarget'), $('.exposureMask'));
$$.infullScreen = false;
}
},
/**
* Fit images to window (used in full screen mode).
*/
fitToWindow : function() {
$.exposure.maxWidth = $(window).width();
$.exposure.maxHeight = $(window).height();
var image = $('.exposureCurrentImage').width('auto').height('auto');
$$.fitToMaxSize(image);
if (!image.width() || !image.height()) {
// Workaround for bug caused by AdBlock plugin for Chrome and Safari:
// http://code.google.com/p/adblockforchrome/issues/detail?id=3701
var delay = setInterval(function() {
$$.centerImageInWindow(image);
clearTimeout(delay);
}, 2);
} else {
$$.centerImageInWindow(image);
}
},
/**
* Default texts. Use the localization files to override these.
*/
texts : {
first : 'First',
previous : 'Prev',
next : 'Next',
last : 'Last',
play : 'Play slideshow',
pause : 'Pause slideshow'
}
}
});
})(jQuery);
/*
* jQuery Hotkeys Plugin
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Based upon the plugin by Tzury Bar Yochay:
* http://github.com/tzuryby/hotkeys
*
* Original idea by:
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
*/
(function(jQuery){jQuery.hotkeys={version:"0.8",specialKeys:{8:"backspace",9:"tab",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",191:"/",224:"meta"},shiftNums:{"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":": ","'":"\"",",":"<",".":">","/":"?","\\":"|"}};function keyHandler(handleObj){if(typeof handleObj.data!=="string"){return}var origHandler=handleObj.handler,keys=handleObj.data.toLowerCase().split(" ");handleObj.handler=function(event){if(this!==event.target&&(/textarea|select/i.test(event.target.nodeName)||event.target.type==="text")){return}var special=event.type!=="keypress"&&jQuery.hotkeys.specialKeys[event.which],character=String.fromCharCode(event.which).toLowerCase(),key,modif="",possible={};if(event.altKey&&special!=="alt"){modif+="alt+"}if(event.ctrlKey&&special!=="ctrl"){modif+="ctrl+"}if(event.metaKey&&!event.ctrlKey&&special!=="meta"){modif+="meta+"}if(event.shiftKey&&special!=="shift"){modif+="shift+"}if(special){possible[modif+special]=true}else{possible[modif+character]=true;possible[modif+jQuery.hotkeys.shiftNums[character]]=true;if(modif==="shift+"){possible[jQuery.hotkeys.shiftNums[character]]=true}}for(var i=0,l=keys.length;i