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)); });
	});
}

Gallery2 Website Option Sidebar Block

My website is Lightbox enabled for image viewing and slideshow viewing of Gallery2 contents. Although the default case is to enable these Lightbox services, many website viewers may not want this. Website visitors should have the option to configure their viewing experience to suit their preference. This introduces the idea of user specific customization or configuration of their profile on my website.

To enable customization I implemented a user options section as a new Gallery2 sidebar block. The first challenge was to create a new Gallery2 block. Fortunately there is a very simple HTML page custom plugin for Gallery2 available on the Internet, from Bharat Mediratta, that can be used to do this. I didn’t care about the intended capability to link to HTML pages but I did want the ability to establish a sidebar block. I wanted to display checkbox options in the sidebar that the user could modify. This sidebar block would be shown on all Gallery2 pages so I also needed some way to retain the settings of these checkboxes as the user traversed the gallery pages.

To do this, I had to maintain the user option state in session cookies. The cookies provide persistance to the option values across individual page changes within one session and also across different sessions. I had two Lightbox options custom configured in my Matrix theme that could be used for initial settings. One was a global Lightbox option that identified if Lightbox services were available, and the second identified if Lightbox slideshow capabilities were available. If these options were set in the theme and if cookie values did not exist then I could assume that the checkbox options should be set, otherwise the checkbox option value would be the cookie value. For persistance the cookies would be retained within the browser for five days.

The Gallery2 sidebar block contents, found in the HtmlBlock.tpl template file in the plugin blocks directory, were replaced with the following Javascript:

templates/blocks/HtmlBlock.tpl

{*
 * $Revision: 1.5 $
 * If you want to customize this file, do not edit it directly since future upgrades
 * may overwrite it.  Instead, copy it into a new directory called "local" and edit that
 * version.  Gallery will look for that file first and use it if it exists.
 *}
{if ($theme.params.lightbox == 1)} 
    <script type="text/javascript">
      // <![CDATA[
      function set(s) {ldelim}
        var x = readCookie(s) ;
        if (!x) return ;
        eraseCookie(s) ;
        (x.charAt(0)=='1') ? x = "0" : x = "1" ;
        createCookie(s,x,5) ;
      {rdelim}

	function createCookie(name,value,days) {ldelim}
	  if (days) {ldelim}
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	  {rdelim}
	  else var expires = "";
	  document.cookie = name+"="+value+expires+"; path=/";
	{rdelim}

	function readCookie(name) {ldelim}
	  var nameEQ = name + "=";
	  var ca = document.cookie.split(';');
	  for(var i=0;i < ca.length;i++) {ldelim}
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	  {rdelim}
        if (name == "cb1") return {if ($theme.params.lightbox == 1)} "1" ; {else} "0" ; {/if}
        if (name == "cb2") return {if ($theme.params.autoLightbox == 1)} "1" ; {else} "0" ; {/if}
	  return null;
	{rdelim}

	function eraseCookie(name) {ldelim}
	  createCookie(name,"",-1);
	{rdelim}


    document.write('<div class="{$class}">') ;
    if (readCookie('cb1') == "1")
      document.write('<input type="checkbox" id="cb1" checked="checked" onClick="set('+"'cb1'"+');"/><b>Photo Lightbox on</b><br />') ;
    else 
      document.write('<input type="checkbox" id="cb1" onClick="set('+"'cb1'"+');"/><b>Photo Lightbox on</b><br />') ;
  {if ($theme.params.autoLightbox == 1)} 
    if (readCookie('cb2') == "1")
      document.write('<input type="checkbox" id="cb2" checked="checked" onClick="set('+"'cb2'"+');"/><b>Gallery Slideshow on</b>') ;
    else 
      document.write('<input type="checkbox" id="cb2" onClick="set('+"'cb2'"+');"/><b>Gallery Slideshow on</b>') ;
  {/if}
    document.write('</div>') ;
      // ]]>
    </script>
{/if} 

Gallery2 Image Types

This shows the various types of Gallery2 thumbnail images that can be inserted into a post entry, including WPG2 tags and other thumbnails available from the G2Image module. The images are centered in a left justified table to show standard margin and border characteristics.

WPG2 tag to image with link to photo
 
Dsc03140
 

Zion National Park

Thumbnail with no link
Dsc03143
Thumbnail with link to photo
Dsc03144
Thumbnail with link to album
Dsc03160

 

This is another Gallery2 WPG2 tumbnail inserted in the text flow.  The image has been expanded to a 300 pixel size and inserted with ‘normal’ positioning.

 
Dsc_0035
 

Yowane

 

All WPG2 tag images are assigned the same Lightbox group for the post or page.  Thumbnails that are inserted specifically and Lightbox enabled are in a different group.

The following 300 pixel expanded thumbnails are also WPG2 tags but these tags have specified links to the gallery.  Link entry for WPG2 tags is a local code modification to the WPG2 WordPress plugin. These images have been positioned to float right and float left within a block container.

 
Dsc_0050
 

Yowane

 
Dsc_0079
 

Yowane