Slimbox 2 Inline Slideshow

Slimbox 2 is a lightweight jQuery implementation of the famous Lightbox 2 script that is often used to overlay images on a web page. Slimbox 2 is being used on Kisekae World.

It is possible to extend and modify the Slimbox script to automatically show all images in a set much like a slideshow. Slimbox 2 uses the jQuery Library, a CSS file, an associated Javascript file, and a few images. This new version uses the same jQuery library with the jQuery Timer extension, a slightly modified CSS file, a modified Javascript file, and two additional images.

Try the example below. Click on any image to invoke Slimbox. Start the slide show.

Dsc_0100 Dsc_0101 Dsc_0102

The modified CSS file is shown below. All modified and new lines are shown in a different color.

slimbox2.css

/* SLIMBOX */

#lbOverlay {
	position: fixed;
	z-index: 9999;
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
	background-color: #000;
	cursor: pointer;
}

#lbCenter, #lbBottomContainer {
	position: absolute;
	z-index: 9999;
	overflow: hidden;
	background-color: #fff;
}

.lbLoading {
	background: #fff url(loading.gif) no-repeat center;
}

#lbImage {
	position: absolute;
	left: 0;
	top: 0;
	border: 10px solid #fff;
	background-repeat: no-repeat;
}

#lbPrevLink, #lbNextLink {
	display: block;
	position: absolute;
	top: 0;
	width: 50%;
	outline: none;
}

#lbPrevLink {
	left: 0;
}

#lbPrevLink:hover {
	background: transparent url(prevlabel.gif) no-repeat 0 15%;
}

#lbNextLink {
	right: 0;
}

#lbNextLink:hover {
	background: transparent url(nextlabel.gif) no-repeat 100% 15%;
}

#lbBottom {
	font-family: Verdana, Arial, Geneva, Helvetica, sans-serif;
	font-size: 10px;
	color: #666;
	line-height: 1.4em;
	text-align: left;
	border: 10px solid #fff;
	border-top-style: none;
}

#lbCloseLink {
	display: block;
	float: right;
	width: 66px;
	height: 22px;
	background: transparent url(closelabel.gif) no-repeat center;
	margin: 5px 0;
	outline: none;
}

#lbShowLink { 
        display: block; 
        float: right; 
        width: 66px; 
        height: 33px; 
        background: transparent url(showlabel.gif) no-repeat center; 
        margin: 5px 0; 
        outline: none; 
} 

#lbShowLink:hover { 
        background: transparent url(showlink.gif) no-repeat center; 
} 

#lbShow { 
        display: block; 
        float: right; 
        width: 22px; 
        height: 22px; 
        background: transparent url(showactive.gif) no-repeat center; 
        margin: 5px 0; outline: none; 
} 

#lbCaption, #lbNumber {
	margin-right: 71px;
}

#lbCaption {
	font-weight: bold;
}

 

The extended Slimbox 2 Javascript file is shown below. All modified and new lines are shown in a different color.

slimbox2.js

/*!
	Slimbox v2.04 - The ultimate lightweight Lightbox clone for jQuery
	(c) 2007-2010 Christophe Beyls 
	MIT-style license.
*/

(function($) {

	// Global variables, accessible to Slimbox only
	var win = $(window), options, images, activeImage = -1, activeURL, prevImage, nextImage, compatibleOverlay, middle, centerWidth, centerHeight,
		ie6 = !window.XMLHttpRequest, hiddenElements = [], documentElement = document.documentElement,

	// Preload images
	preload = {}, preloadPrev = new Image(), preloadNext = new Image(),

	// DOM elements
	overlay, center, image, sizer, prevLink, nextLink, bottomContainer, bottom, caption, number, slideshow, show;

	/*
		Initialization
	*/

	$(function() {
		// Append the Slimbox HTML code at the bottom of the document
		$("body").append(
			$([
				overlay = $('<div id="lbOverlay" />')[0],
				center = $('<div id="lbCenter" />')[0],
				bottomContainer = $('<div id="lbBottomContainer" />')[0]
			]).css("display", "none")
		);

		image = $('<div id="lbImage" />').appendTo(center).append(
			sizer = $('<div style="position: relative;" />').append([
				prevLink = $('<a id="lbPrevLink" href="#" />').click(previous)[0],
				nextLink = $('<a id="lbNextLink" href="#" />').click(next)[0]
			])[0]
		)[0];

		bottom = $('<div id="lbBottom" />').appendTo(bottomContainer).append([
			$('<a id="lbCloseLink" href="#" />').add(overlay).click(close)[0],
			slideshow = $('<a id="lbShowLink" href="#" />').click(show)[0],
			show = $('<div id="lbShow" />').hide()[0],
			caption = $('<div id="lbCaption" />')[0],
			number = $('<div id="lbNumber" />')[0],
			$('<div style="clear: both;" />')[0]
		])[0];
	});

	/*
		API
	*/

	// Open Slimbox with the specified parameters
	$.slimbox = function(_images, startImage, _options) {
		options = $.extend({
			loop: false,				// Allows to navigate between first and last images
			overlayOpacity: 0.8,			// 1 is opaque, 0 is completely transparent (change the color in the CSS file)
			overlayFadeDuration: 400,		// Duration of the overlay fade-in and fade-out animations (in milliseconds)
			resizeDuration: 400,			// Duration of each of the box resize animations (in milliseconds)
			resizeEasing: "swing",			// "swing" is jQuery's default easing
			initialWidth: 250,			// Initial width of the box (in pixels)
			initialHeight: 250,			// Initial height of the box (in pixels)
			imageFadeDuration: 400,			// Duration of the image fade-in animation (in milliseconds)
			captionAnimationDuration: 400,		// Duration of the caption animation (in milliseconds)
			slideShowDuration: 5000, // Duration of the slide show display (in milliseconds)
			counterText: "Image {x} of {y}",	// Translate or change as you wish, or set it to false to disable counter text for image groups
			closeKeys: [27, 88, 67],		// Array of keycodes to close Slimbox, default: Esc (27), 'x' (88), 'c' (67)
			previousKeys: [37, 80],			// Array of keycodes to navigate to the previous image, default: Left arrow (37), 'p' (80)
			nextKeys: [39, 78]			// Array of keycodes to navigate to the next image, default: Right arrow (39), 'n' (78)
		}, _options);

		// The function is called for a single image, with URL and Title as first two arguments
		if (typeof _images == "string") {
			_images = [[_images, startImage]];
			startImage = 0;
		}

		middle = win.scrollTop() + (win.height() / 2);
		centerWidth = options.initialWidth;
		centerHeight = options.initialHeight;
		$(center).css({top: Math.max(0, middle - (centerHeight / 2)), width: centerWidth, height: centerHeight, marginLeft: -centerWidth/2}).show();
		compatibleOverlay = ie6 || (overlay.currentStyle && (overlay.currentStyle.position != "fixed"));
		if (compatibleOverlay) overlay.style.position = "absolute";
		$(overlay).css("opacity", options.overlayOpacity).fadeIn(options.overlayFadeDuration);
		position();
		setup(1);

		images = _images;
		options.loop = options.loop && (images.length > 1);
		return changeImage(startImage);
	};

	/*
		options:	Optional options object, see jQuery.slimbox()
		linkMapper:	Optional function taking a link DOM element and an index as arguments and returning an array containing 2 elements:
				the image URL and the image caption (may contain HTML)
		linksFilter:	Optional function taking a link DOM element and an index as arguments and returning true if the element is part of
				the image collection that will be shown on click, false if not. "this" refers to the element that was clicked.
				This function must always return true when the DOM element argument is "this".
	*/
	$.fn.slimbox = function(_options, linkMapper, linksFilter) {
		linkMapper = linkMapper || function(el) {
			return [el.href, el.title];
		};

		linksFilter = linksFilter || function() {
			return true;
		};

		var links = this;

		return links.unbind("click").click(function() {
			// Build the list of images that will be displayed
			var link = this, startIndex = 0, filteredLinks, i = 0, length;
			filteredLinks = $.grep(links, function(el, i) {
				return linksFilter.call(link, el, i);
			});

			// We cannot use jQuery.map() because it flattens the returned array
			for (length = filteredLinks.length; i < length; ++i) { 				if (filteredLinks[i] == link) startIndex = i; 				filteredLinks[i] = linkMapper(filteredLinks[i], i); 			} 			return $.slimbox(filteredLinks, startIndex, _options); 		}); 	}; 	/* 		Internal functions 	*/ 	function position() { 		var l = win.scrollLeft(), w = win.width(); 		$([center, bottomContainer]).css("left", l + (w / 2)); 		if (compatibleOverlay) $(overlay).css({left: l, top: win.scrollTop(), width: w, height: win.height()}); 	} 	function setup(open) { 		if (open) { 			$("object").add(ie6 ? "select" : "embed").each(function(index, el) { 				hiddenElements[index] = [el, el.style.visibility]; 				el.style.visibility = "hidden"; 			}); 		} else { 			$.each(hiddenElements, function(index, el) { 				el[0].style.visibility = el[1]; 			}); 			hiddenElements = []; 		} 		var fn = open ? "bind" : "unbind"; 		win[fn]("scroll resize", position); 		$(document)[fn]("keydown", keyDown); 	} 	function keyDown(event) { 		var code = event.keyCode, fn = $.inArray; 		// Prevent default keyboard action (like navigating inside the page) 		return (fn(code, options.closeKeys) >= 0) ? close()
			: (fn(code, options.nextKeys) >= 0) ? next()
			: (fn(code, options.previousKeys) >= 0) ? previous()
			: false;
	}

	function previous() {
		return changeImage(prevImage);
	}

	function next() {
		return changeImage(nextImage);
	}

	function show() { 
                $(show).toggle(); 
                $(slideshow).stopTime(); 
                if ( $(show).is(':visible') ) { 
                     next(); 
                     $(slideshow).everyTime(options.slideShowDuration, "slideshow", function() { next(); }); 
                } 
                return false; 
         }

	function changeImage(imageIndex) {
		if (imageIndex >= 0) {
			activeImage = imageIndex;
			activeURL = images[activeImage][0];
			prevImage = (activeImage || (options.loop ? images.length : 0)) - 1;
			nextImage = ((activeImage + 1) % images.length) || (options.loop ? 0 : -1);

			stop();
			center.className = "lbLoading";

			preload = new Image();
			preload.onload = animateBox;
			preload.src = activeURL;
		}

		return false;
	}

	function animateBox() {
		center.className = "";
		$(image).css({backgroundImage: "url(" + activeURL + ")", visibility: "hidden", display: ""});
		$(sizer).width(preload.width);
		$([sizer, prevLink, nextLink]).height(preload.height);

		$(caption).html(images[activeImage][1] || "");
		$(number).html((((images.length > 1) && options.counterText) || "").replace(/{x}/, activeImage + 1).replace(/{y}/, images.length));

		if (prevImage >= 0) preloadPrev.src = images[prevImage][0];
		if (nextImage >= 0) preloadNext.src = images[nextImage][0];

		centerWidth = image.offsetWidth;
		centerHeight = image.offsetHeight;
		var top = Math.max(0, middle - (centerHeight / 2));
		if (center.offsetHeight != centerHeight) {
			$(center).animate({height: centerHeight, top: top}, options.resizeDuration, options.resizeEasing);
		}
		if (center.offsetWidth != centerWidth) {
			$(center).animate({width: centerWidth, marginLeft: -centerWidth/2}, options.resizeDuration, options.resizeEasing);
		}
		$(center).queue(function() {
			$(bottomContainer).css({width: centerWidth, top: top + centerHeight, marginLeft: -centerWidth/2, visibility: "hidden", display: ""});
			$(image).css({display: "none", visibility: "", opacity: ""}).fadeIn(options.imageFadeDuration, animateCaption);
		});
	}

	function animateCaption() {
		if (prevImage >= 0) $(prevLink).show();
		if (nextImage >= 0) $(nextLink).show();
		if (images.length > 1) $(slideshow).show();
		$(bottom).css("marginTop", -bottom.offsetHeight).animate({marginTop: 0}, options.captionAnimationDuration);
		bottomContainer.style.visibility = "";
	}

	function stop() {
		preload.onload = null;
		preload.src = preloadPrev.src = preloadNext.src = activeURL;
		$([center, image, bottom]).stop(true);
		$([prevLink, nextLink, image, bottomContainer, slideshow]).hide();
	}

	function close() {
		if (activeImage >= 0) {
			stop();
			activeImage = prevImage = nextImage = -1;
			$(center).hide();
			$(overlay).stop().fadeOut(options.overlayFadeDuration, setup);
			$(slideshow).stopTime();
			$(show).hide();
		}

		return false;
	}

})(jQuery);

 

The image files referenced in the CSS are shown below. These images should be stored in the same directory as the CSS file.

The jQuery Library and the jQuery Timer extension can be downloaded from the jQuery distribution site. If you are running WordPress then jQuery is included in the distribution. The jQuery Timer extension is a small javascript file that you need to load.

 

jquery.timers-1.2.js

/**
 * jQuery.timers - Timer abstractions for jQuery
 * Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com)
 * Licensed under the WTFPL (http://sam.zoy.org/wtfpl/).
 * Date: 2009/10/16
 *
 * @author Blair Mitchelmore
 * @version 1.2
 *
 **/

jQuery.fn.extend({
	everyTime: function(interval, label, fn, times) {
		return this.each(function() {
			jQuery.timer.add(this, interval, label, fn, times);
		});
	},
	oneTime: function(interval, label, fn) {
		return this.each(function() {
			jQuery.timer.add(this, interval, label, fn, 1);
		});
	},
	stopTime: function(label, fn) {
		return this.each(function() {
			jQuery.timer.remove(this, label, fn);
		});
	}
});

jQuery.extend({
	timer: {
		global: [],
		guid: 1,
		dataKey: "jQuery.timer",
		regex: /^([0-9]+(?:.[0-9]*)?)s*(.*s)?$/,
		powers: {
			// Yeah this is major overkill...
			'ms': 1,
			'cs': 10,
			'ds': 100,
			's': 1000,
			'das': 10000,
			'hs': 100000,
			'ks': 1000000
		},
		timeParse: function(value) {
			if (value == undefined || value == null)
				return null;
			var result = this.regex.exec(jQuery.trim(value.toString()));
			if (result[2]) {
				var num = parseFloat(result[1]);
				var mult = this.powers[result[2]] || 1;
				return num * mult;
			} else {
				return value;
			}
		},
		add: function(element, interval, label, fn, times) {
			var counter = 0;

			if (jQuery.isFunction(label)) {
				if (!times) 
					times = fn;
				fn = label;
				label = interval;
			}

			interval = jQuery.timer.timeParse(interval);

			if (typeof interval != 'number' || isNaN(interval) || interval < 0)
				return;

			if (typeof times != 'number' || isNaN(times) || times < 0)  				times = 0; 			 			times = times || 0; 			 			var timers = jQuery.data(element, this.dataKey) || jQuery.data(element, this.dataKey, {}); 			 			if (!timers[label]) 				timers[label] = {}; 			 			fn.timerID = fn.timerID || this.guid++; 			 			var handler = function() { 				if ((++counter > times && times !== 0) || fn.call(element, counter) === false)
					jQuery.timer.remove(element, label, fn);
			};

			handler.timerID = fn.timerID;

			if (!timers[label][fn.timerID])
				timers[label][fn.timerID] = window.setInterval(handler,interval);

			this.global.push( element );

		},
		remove: function(element, label, fn) {
			var timers = jQuery.data(element, this.dataKey), ret;

			if ( timers ) {

				if (!label) {
					for ( label in timers )
						this.remove(element, label, fn);
				} else if ( timers[label] ) {
					if ( fn ) {
						if ( fn.timerID ) {
							window.clearInterval(timers[label][fn.timerID]);
							delete timers[label][fn.timerID];
						}
					} else {
						for ( var fn in timers[label] ) {
							window.clearInterval(timers[label][fn]);
							delete timers[label][fn];
						}
					}

					for ( ret in timers[label] ) break;
					if ( !ret ) {
						ret = null;
						delete timers[label];
					}
				}

				for ( ret in timers ) break;
				if ( !ret ) 
					jQuery.removeData(element, this.dataKey);
			}
		}
	}
});

jQuery(window).bind("unload", function() {
	jQuery.each(jQuery.timer.global, function(index, item) {
		jQuery.timer.remove(item);
	});
});

 

Download the Slimbox 2 distribution. Modify it as shown or replace the distribution files with the code shown above. Put all javascript files in the ‘js’ directory and the CSS file in the ‘css’ directory. Store the new image files in the ‘css’ directory. Upload the directories to your web server. Include the following HTML in the <head> section of your web page.

 

<link rel="stylesheet" href="css/slimbox2.css" type="text/css" media="screen" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery.timers-1.2.js"></script>
<script type="text/javascript" src="js/slimbox2.js"></script>

 

To include the Lightbox or Slimbox effect in your images you must include a ‘rel’ tag in a hyperlink as shown below. See the Slimbox 2 documentation for more information.

 

<a href="images/image-1.jpg" rel="lightbox-cats">image #1</a>
<a href="images/image-2.jpg" rel="lightbox-cats">image #2</a>
<a href="images/image-3.jpg" rel="lightbox-cats">image #3</a>

 

If you have extensions or improvements to this work please post a comment. It would be more efficient to use text blocks for the ‘Slide Show’ images. This would eliminate the need for server image downloads.

 

Errata – December 21, 2011.

Ooops, I forgot to include the fact that you need to include another little javascript file (autoload.js) to start the Slimbox2 function on the page. It is part of the standard Slimbox2 distribution, but I didn’t include it in the code above. So, you need this as well. Make sure you add a <script type=”text/javascript” src=”js/autoload.js”></script> in the <head> section of your web page along with the others shown above.

Or, if it makes it easier, just download this slimbox2ss.zip file where everything is packaged and there is an example HTML file that you can open in a browser.

autoload.js

// AUTOLOAD CODE BLOCK (MAY BE CHANGED OR REMOVED)
if (!/android|iphone|ipod|series60|symbian|windows ce|blackberry/i.test(navigator.userAgent)) {
	jQuery(function($) {
		$("a[rel^='lightbox']").slimbox({loop: true}, 
				function(el) { return [el.rev || el.href, el.title]; }, 
				function(el) { return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel)); });
	});
}

6 thoughts on “Slimbox 2 Inline Slideshow

  1. great addition to the slimbox utility and nicely explained. I am very new at jQuery and javascript. Do you know if there is a way of counting the number of times an image is viewed?

  2. One way might be to look at the image access statistics recorded in your web server logs.

  3. This is how Slimbox should work “out of the box” IMO. Thanks for doing this and making it available.

    The trouble is that it fails on my site, although Slimbox2 unmodified works fine. I replace the CSS and scripts and call them, and just get a static next page of the image clicked – no Slimbox, as if the script hadn’t loaded.

    CSS & JS are loading corrrectly according to page source.

    If I replace your modified Slimbox2.js with the original, it works again (though of course without the slideshow facility I so much want 🙂

    I’m on Jquery 1.6.2, also tried Jquery 1.4.1. The Jquery.timers.js you list seems quite different from the only one I could find at the author’s site – the Jquery Plugin site is unwell and the archive doesn’t appear to have any d/l code

    Any suggestions please?

  4. Well that is weird. I have been running the minified version of Slimbox2, working fine. After hours of staring at your code, I tried running the longform version of the standard version on a hunch… and it no longer worked. So I minified your slideshow version and WTH… it now works perfectly! Very strange, but I am running this inside the Textpattern CMS via a plugin which stores JS in the DB, so perhaps there’s something about lots of white space that doesn’t agree with the plugin.

    Thanks anyhow, a brilliant enhancement that really ought to be part of Slimbox.

    Minified version in case anyone needs it:

    [Edit – sorry Tony, the minified code listing with quotes and other characters isn’t rendering very well on this page, so I have had to remove it. But thanks for your contribution, it’s appreciated.]

  5. Hmmm, it looks like I forgot to include a note about the ‘autoload’ function that is included in the Slimbox2 javascript distribution. Sorry. Technology is sometimes much too complicated.

    See the Errata that I have added above. Or, download the slimbox2ss.zip file where I have packaged everything up as an example.

Comments are closed.