A cross-browser implementation of jCarousel Lite (non-circular)

I've been working with jQuery and Ganeshji Marwaha's jCarousel Lite to produce a non-circular dynamic galley using images pulled from a folder using the FSO (File System Object). The images are of different dimensions so I re-sized them on the fly and centered them horizontally and vertically in divs (to look like slides).

After incorporating a modified version of Jeremy Thibeaux's non-circular carousel fix (where scrolling to the end does not work if the number of items is not divisible by the scroll amount), I browser tested the carousel in Firefox 2 & 3, Opera 9, Safari 3 and IE6 & 7.

It functioned as expected in everything except IE6 where the last item was rendered below the first item, meaning the last strip of the carousel misses it's last element.

The easiest way to get IE6 to play ball was to add an additional 'dummy' list item after the last real item (inserted using a conditional comment) and, using browser sniffing in the JavaScript, give adjusted values to IE6 so that the 'dummy' item is ignored in the scroll.

The completed project

Code inserted in the document's head (save jQuery as /scripts/jquery.js):

<script type="text/javascript" src="/scripts/jquery.js"></script>
<script type="text/javascript" src="/scripts/jcarousel-lite.js"></script>
<link href="/styles/carousel.css" rel="stylesheet" type="text/css" />
<!--[if lte IE 6]>
<link href="/styles/carousel_IE6.css" rel="stylesheet" type="text/css" />
<![endif]-->
<script type="text/javascript">
$(document).ready(function(){
$("#jcarousel").jCarouselLite({
});
});
</script>

The markup:

<%
Response.CacheControl = "no-cache"
Dim strImageFolderPath, fso, mainfolder, filecollection, file
strImageFolderPath = "d:\domains\domain.co.uk\wwwroot\image_folder"
Set fso = CreateObject("Scripting.FileSystemObject")
Set mainfolder = fso.GetFolder(strImageFolderPath)
Set filecollection = mainfolder.Files
If filecollection.Count > 0 Then
%>
<div id="jcarouselwrapper">
<a class="prev">&nbsp;</a>
<div id="jcarousel">
<ul>
<% For Each file In filecollection %>
<li><div class="jcarouselbox"><img src="/image_folder/<%= file.name %>" /></div></li>
<% Next %>
<!--[if lte IE 6]>
<li><div class="jcarouselboxdummy">&nbsp;</div></li>
<![endif]-->
</ul>
</div>
<a class="next">&nbsp;</a>
</div>
<% End If %>

The Modified jCarousel Lite JavaScript file (saved as /scripts/jcarousel-lite.js):

// Copyright (c) 2007 Ganeshji Marwaha (gmarwaha.com)
// Dual licensed under the MIT and GPL licenses:
// http://www.opensource.org/licenses/mit-license.php
// http://www.gnu.org/licenses/gpl.html
(function($) {
$.fn.jCarouselLite = function(o) {
o = $.extend({
btnNext: ".next",
btnPrev: ".prev",
btnGo: null,
mouseWheel: false,
auto: null,
speed: 600,
easing: null,
vertical: false,
circular: false,
visible: 3,
start: 0,
scroll: 3,
beforeStart: null,
afterEnd: null
}, o || {});
return this.each(function() {
var running = false, animCss=o.vertical?"top":"left", sizeCss=o.vertical?"height":"width";
var div = $(this), ul = $("ul", div), tLi = $("li", ul), tl = tLi.size(), v = o.visible;
if(o.circular) {
ul.prepend(tLi.slice(tl-v-1+1).clone())
.append(tLi.slice(0,v).clone());
o.start += v;
}
var li = $("li", ul), itemLength = li.size(), curr = o.start;
if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)){
var ieversion=new Number(RegExp.$1)
}
div.css("visibility", "visible");
li.css({overflow: "hidden", float: o.vertical ? "none" : "left"});
ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
div.css({overflow: "hidden", position: "relative", "z-index": "2", left: "0px"});
var liSize = o.vertical ? height(li) : width(li);
var ulSize = liSize * itemLength;
var divSize = liSize * v;
li.css({width: li.width(), height: li.height()});
ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
div.css(sizeCss, divSize+"px");
if(!o.circular) {
$(o.btnPrev + ',' + o.btnNext).removeClass("disabled");
if (o.btnPrev && curr == 0){
$(o.btnPrev).addClass("disabled");
}
if (o.btnNext && curr >= itemLength-v){
$(o.btnNext).addClass("disabled");
}
if (ieversion<=6)
if (o.btnNext && curr >= itemLength-v-1){
$(o.btnNext).addClass("disabled");
}
}
if(o.btnPrev)
$(o.btnPrev).click(function() {
prev = curr-o.scroll;
if (!o.circular && prev < 0){
prev = 0;
}
return go(prev);
});
if(o.btnNext)
$(o.btnNext).click(function() {
next = curr+o.scroll;
if (!o.circular && next > itemLength-v-1){
if (ieversion<=6)
next = itemLength-v-1;
else
next = itemLength-v;
}
return go(next);
});
if(o.btnGo)
$.each(o.btnGo, function(i, val) {
$(val).click(function() {
return go(o.circular ? o.visible+i : i);
});
});
if(o.mouseWheel && div.mousewheel)
div.mousewheel(function(e, d) {
return d>0 ? go(curr-o.scroll) : go(curr+o.scroll);
});
if(o.auto)
setInterval(function() {
go(curr+o.scroll);
}, o.auto+o.speed);
function vis() {
return li.slice(curr).slice(0,v);
};
function go(to) {
if(!running) {
if(o.beforeStart)
o.beforeStart.call(this, vis());
if(o.circular) {
if(to<=o.start-v-1) {
ul.css(animCss, -((itemLength-(v*2))*liSize)+"px");
curr = to==o.start-v-1 ? itemLength-(v*2)-1 : itemLength-(v*2)-o.scroll;
} else if(to>=itemLength-v+1) {
ul.css(animCss, -( (v) * liSize ) + "px" );
curr = to==itemLength-v+1 ? v+1 : v+o.scroll;
} else curr = to;
} else {
if(to<0 || to>itemLength-v) return;
else curr = to;
}
running = true;
ul.animate(
animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
function() {
if(o.afterEnd)
o.afterEnd.call(this, vis());
running = false;
}
);
if(!o.circular) {
$(o.btnPrev + ',' + o.btnNext).removeClass("disabled");
if (o.btnPrev && curr == 0){
$(o.btnPrev).addClass("disabled");
}
if (o.btnNext && curr >= itemLength-v){
$(o.btnNext).addClass("disabled");
}
if (ieversion<=6)
if (o.btnNext && curr >= itemLength-v-1){
$(o.btnNext).addClass("disabled");
}
}
}
return false;
};
});
};
function css(el, prop) {
return parseInt($.css(el[0], prop)) || 0;
};
function width(el) {
return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
};
function height(el) {
return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
};
})($);

The CSS file (saved as /styles/carousel.css):

/* jCarousel Lite CSS */
#jcarousel ul {margin:0;}
#jcarousel li {margin:4px;}
#jcarousel {
margin: 18px 0 0 0;
float:left;
}
#jcarousel {
position: relative;
visibility: hidden;
left: -5000px;
}
#jcarouselwrapper {
-moz-border-radius:10px;
background:#fff;
border:1px solid #ccc;
height:128px;
width:353px;
}
#jcarouselwrapper a.prev {
display: block;
float: left;
margin:48px 0 0 6px;
width: 32px;
height: 32px;
text-decoration: none;
background: url(prev.png) left top no-repeat;
}
#jcarouselwrapper a.next {
display: block;
float: left;
margin:48px 0 0 0;
width: 32px;
height: 32px;
text-decoration: none;
background: url(next.png) left top no-repeat;
}
#jcarouselwrapper a.prev:hover, #jcarouselwrapper a.prev:focus,
#jcarouselwrapper a.next:hover, #jcarouselwrapper a.next:focus {
background-position: -32px top;
}
#jcarouselwrapper a.prev:active, #jcarouselwrapper a.next:active {
background-position: -64px top;
}
#jcarouselwrapper a.prev.disabled, #jcarouselwrapper a.next.disabled {
background-position: -96px top;
cursor: default;
}
#jcarouselwrapper a:hover, #jcarouselwrapper a:active, #jcarouselwrapper a:focus {
border: none;
outline: none;
}
.jcarouselbox {
width:82px;
height:82px;
background-color:#f5f5f5;
border:1px solid #ccc;
text-align:center;
}
.jcarouselboxdummy {
width:84px;
height:84px;
background-color:#fff;
}
/* end of jCarousel Lite CSS */

The IE6 CSS file (saved as /styles/carousel_IE6.css):

/* jCarousel Lite IE6 CSS */
#jcarouselwrapper a.prev {
margin-left:3px; /* to fix IE's double margin float bug */
}
#jcarousel {
height:88px;
overflow:hidden;
}
/* end of jCarousel Lite IE6 CSS */

The completed carousel:

I borrowed the styling and buttons from jCarousel and also incorporated Highslide JS so that images can be enlarged when clicked on.

Comments on this article can be left here.