(function($) { // compliant with jquery.noconflict() $.jcarousellite = { version: '1.1' }; $.fn.jcarousellite = function(options) { options = $.extend({}, $.fn.jcarousellite.options, options || {}); return this.each(function() { // returns the element collection. chainable. var running, animcss, sizecss, div = $(this), ul, initialli, li, lisize, ulsize, divsize, numvisible, initialitemlength, itemlength, calculatedto, autotimeout; initvariables(); // set the above variables after initial calculations initstyles(); // set the appropriate styles for the carousel div, ul and li initsizes(); // set appropriate sizes for the carousel div, ul and li attacheventhandlers(); // attach event handlers for carousel to respond function go(to) { if(!running) { cleartimeout(autotimeout); // prevents multiple clicks while auto-scrolling - edge case calculatedto = to; if(options.beforestart) { // call the beforestart() callback options.beforestart.call(this, visibleitems()); } if(options.circular) { // if circular, and "to" is going oob, adjust it adjustoobforcircular(to); } else { // if non-circular and "to" is going oob, adjust it. adjustoobfornoncircular(to); } // if neither overrides "calculatedto", we are not in edge cases. animatetoposition({ // animate carousel item to position based on calculated values. start: function() { running = true; }, done: function() { if(options.afterend) { options.afterend.call(this, visibleitems()); } if(options.auto) { setupautoscroll(); } running = false; } }); if(!options.circular) { // enabling / disabling buttons is applicable in non-circular mode only. disableorenablebuttons(); } } return false; } function initvariables() { running = false; animcss = options.vertical ? "top" : "left"; sizecss = options.vertical ? "height" : "width"; ul = div.find(">ul"); initialli = ul.find(">li"); initialitemlength = initialli.size(); // to avoid a scenario where number of items is just 1 and visible is 3 for example. numvisible = initialitemlength < options.visible ? initialitemlength : options.visible; if(options.circular) { var $lastitemset = initialli.slice(initialitemlength-numvisible).clone(); var $firstitemset = initialli.slice(0,numvisible).clone(); ul.prepend($lastitemset) // prepend the lis with final items so that the user can click the back button to start with .append($firstitemset); // append the lis with first items so that the user can click the next button even after reaching the end options.start += numvisible; // since we have a few artificial lis in the front, we will have to move the pointer to point to the real first item } li = $("li", ul); itemlength = li.size(); calculatedto = options.start; } function initstyles() { div.css("visibility", "visible"); // if the div was set to hidden in css, make it visible now li.css({ overflow: "hidden", "float": options.vertical ? "none" : "left" // some minification tools fail if "" is not used }); ul.css({ margin: "0", padding: "0", position: "relative", "list-style": "none", "z-index": "1" }); div.css({ overflow: "hidden", position: "relative", "z-index": "2", left: "0px" }); // for a non-circular carousel, if the start is 0 and btnprev is supplied, disable the prev button if(!options.circular && options.btnprev && options.start == 0) { $(options.btnprev).addclass("disabled"); } } function initsizes() { lisize = options.vertical ? // full li size(incl margin)-used for animation and to set ulsize li.outerheight(true) : li.outerwidth(true); ulsize = lisize * itemlength; // size of full ul(total length, not just for the visible items) divsize = lisize * numvisible; // size of entire div(total length for just the visible items) // generally, li's dimensions should be specified explicitly in a style-sheet // but in the case of img (with width and height attr), we can derive li's dimensions and set here // may be applicable for other types of li children if their dimensions are explicitly specified // individual li dimensions li.css({ width: li.width(), height: li.height() }); // size of the entire ul. including hidden and visible elements // will include li's (width + padding + border + margin) * itemlength - using outerwidth(true) ul.css(sizecss, ulsize+"px") .css(animcss, -(calculatedto * lisize)); // width of the div. only the width of the visible elements // will include li's (width + padding + border + margin) * numvisible - using outerwidth(true) div.css(sizecss, divsize+"px"); } function attacheventhandlers() { if(options.btnprev) { $(options.btnprev).click(function() { return go(calculatedto - options.scroll); }); } if(options.btnnext) { $(options.btnnext).click(function() { return go(calculatedto + options.scroll); }); } if(options.btngo) { $.each(options.btngo, function(i, val) { $(val).click(function() { return go(options.circular ? numvisible + i : i); }); }); } if(options.mousewheel && div.mousewheel) { div.mousewheel(function(e, d) { return d > 0 ? go(calculatedto - options.scroll) : go(calculatedto + options.scroll); }); } if(options.auto) { setupautoscroll(); } } function setupautoscroll() { autotimeout = settimeout(function() { go(calculatedto + options.scroll); }, options.auto); } function visibleitems() { return li.slice(calculatedto).slice(0,numvisible); } function adjustoobforcircular(to) { var newposition; // if first, then goto last if(to <= options.start - numvisible - 1) { newposition = to + initialitemlength + options.scroll; ul.css(animcss, -(newposition * lisize) + "px"); calculatedto = newposition - options.scroll; console.log("before - positioned at: " + newposition + " and moving to: " + calculatedto); } // if last, then goto first else if(to >= itemlength - numvisible + 1) { newposition = to - initialitemlength - options.scroll; ul.css(animcss, -(newposition * lisize) + "px"); calculatedto = newposition + options.scroll; console.log("after - positioned at: " + newposition + " and moving to: " + calculatedto); } } function adjustoobfornoncircular(to) { // if user clicks "prev" and tries to go before the first element, reset it to first element. if(to < 0) { calculatedto = 0; } // if "to" is greater than the max index that we can use to show another set of elements // it means that we will have to reset "to" to a smallest possible index that can show it else if(to > itemlength - numvisible) { calculatedto = itemlength - numvisible; } console.log("item length: " + itemlength + "; " + "to: " + to + "; " + "calculatedto: " + calculatedto + "; " + "num visible: " + numvisible); } function disableorenablebuttons() { $(options.btnprev + "," + options.btnnext).removeclass("disabled"); $( (calculatedto-options.scroll<0 && options.btnprev) || (calculatedto+options.scroll > itemlength-numvisible && options.btnnext) || [] ).addclass("disabled"); } function animatetoposition(animationoptions) { running = true; ul.animate( animcss == "left" ? { left: -(calculatedto*lisize) } : { top: -(calculatedto*lisize) }, $.extend({ duration: options.speed, easing: options.easing }, animationoptions) ); } }); }; $.fn.jcarousellite.options = { btnprev: null, // css selector for the previous button btnnext: null, // css selector for the next button btngo: null, // css selector for the go button mousewheel: false, // set "true" if you want the carousel scrolled using mouse wheel auto: null, // set to a numeric value (800) in millis. time period between auto scrolls speed: 200, // set to a numeric value in millis. speed of scroll easing: null, // set to easing (bounceout) to specify the animation easing vertical: false, // set to "true" to make the carousel scroll vertically circular: true, // set to "true" to make it an infinite carousel visible: 3, // set to a numeric value to specify the number of visible elements at a time start: 0, // set to a numeric value to specify which item to start from scroll: 1, // set to a numeric value to specify how many items to scroll for one scroll event beforestart: null, // set to a function to receive a callback before every scroll start afterend: null // set to a function to receive a callback after every scroll end }; })(jquery);