/*! * jCarousel Lite - v1.9.5 - 2026-1-24 * http://kswedberg.github.com/jquery-carousel-lite/ * Copyright (c) 2026 Karl Swedberg * based on the original by Ganeshji Marwaha (gmarwaha.com) * Licensed MIT (http://kswedberg.github.com/jquery-carousel-lite/blob/master/LICENSE-MIT) */ (function($) { $.jCarouselLite = { version: '1.9.5', curr: 0 }; $.fn.anim = typeof $.fn.velocity !== 'undefined' ? $.fn.velocity : $.fn.animate; var isArray = function(arr) { if (Array && Array.isArray) { return Array.isArray(arr); } return typeof arr === 'object' && Object.prototype.toString.call(arr) === '[object Array]'; }; $.fn.jCarouselLite = function(options) { var o = $.extend(true, {}, $.fn.jCarouselLite.defaults, options); var ceil = Math.ceil; var mabs = Math.abs; this.each(function() { var beforeCirc, afterCirc, pageNav, pageNavCount, resize, li, itemLength, curr, prepResize, touchEvents, $btnsGo; var isTouch = 'ontouchend' in document; var styles = {div: {}, ul: {}, li: {}}; var running = false; var animCss = o.vertical ? 'top' : 'left'; var aniProps = {}; var sizeProp = o.vertical ? 'height' : 'width'; var outerMethod = o.vertical ? 'outerHeight' : 'outerWidth'; var self = this; var div = $(this); var ul = div.find(o.containerSelector).eq(0); var tLi = ul.children(o.itemSelector); var tl = tLi.length; var visibleNum = o.visible; // need visibleCeil and visibleFloor in case we want a fractional number of visible items at a time var visibleCeil = ceil(visibleNum); var visibleFloor = Math.floor(visibleNum); var start = Math.min(o.start, tl - 1); var direction = 1; var activeBtnOffset = 0; var activeBtnTypes = {}; var startTouch = {}; var endTouch = {}; var axisPrimary = o.vertical ? 'y' : 'x'; var axisSecondary = o.vertical ? 'x' : 'y'; var init = o.init.call(this, o, tLi); // bail out for this carousel if the o.init() callback returns `false` if (init === false) { return; } var fixIds = function(i) { if (this.id) { this.id += i; } }; var makeCircular = function() { if (beforeCirc && beforeCirc.length) { beforeCirc.remove(); afterCirc.remove(); } tLi = ul.children(o.liSelector); tl = tLi.length; beforeCirc = tLi.slice(tl - visibleCeil).clone(true).each(fixIds); afterCirc = tLi.slice(0, visibleCeil).clone(true).each(fixIds); ul.prepend(beforeCirc) .append(afterCirc); li = ul.children(o.liSelector); itemLength = li.length; }; var iterations = function(itemLength, options) { return options.autoStop && (options.circular ? options.autoStop : Math.min(itemLength, options.autoStop)); }; div.data('dirjc', direction); div.data(animCss + 'jc', div.css(animCss)); if (o.circular) { makeCircular(); start += visibleCeil; activeBtnOffset = visibleCeil; } else { li = ul.children(o.liSelector); itemLength = li.length; } var toggleDisabled = function(toggleLis, disabled) { var props = {disabled: disabled, hidden: disabled}; toggleLis.find('a, button, input, [tabindex]').not('[data-disabled]').prop(props); }; var vis = function vis() { return li.slice(curr).slice(0, visibleCeil); }; $.jCarouselLite.vis = vis; var setActive = function(i, types) { i = ceil(i); // Set active class on the appropriate carousel item li .filter('.' + o.activeClass) .removeClass(o.activeClass) .removeAttr('tabindex'); li .eq(i) .addClass(o.activeClass) .attr('tabindex', '0'); var visibleItems = vis(); visibleItems.prop({disabled: false}).removeAttr('aria-hidden'); toggleDisabled(visibleItems, false); toggleDisabled(li.not(visibleItems), true); var activeBtnIndex = (i - activeBtnOffset) % tl; var visEnd = activeBtnIndex + visibleFloor; if (types.go) { // remove active and visible classes from all the go buttons $btnsGo.removeClass(o.activeClass).removeClass(o.visibleClass); // add active class to the go button corresponding to the first visible slide $btnsGo.eq(activeBtnIndex).addClass(o.activeClass); // add visible class to go buttons corresponding to all visible slides $btnsGo.slice(activeBtnIndex, activeBtnIndex + visibleFloor).addClass(o.visibleClass); if (visEnd > $btnsGo.length) { $btnsGo .slice(0, visEnd - $btnsGo.length) .addClass(o.visibleClass); } } if (types.pager) { pageNav.removeClass(o.activeClass); pageNav.eq(ceil(activeBtnIndex / visibleNum)).addClass(o.activeClass); } return activeBtnIndex; }; curr = start; $.jCarouselLite.curr = curr; // li.filter(':disabled').attr('data-disabled', 'disabled'); // li.find(':disabled').attr('data-disabled', 'disabled'); var getDimensions = function(reset) { var liSize, ulSize, divSize; if (reset) { styles.div[sizeProp] = ''; styles.li = { width: '', height: '' }; // bail out with the reset styles return styles; } // Full li size(incl margin)-Used for animation liSize = li[outerMethod](true); // size of full ul(total length, not just for the visible items) ulSize = liSize * itemLength; // size of entire div(total length for just the visible items) divSize = liSize * visibleNum; styles.div[sizeProp] = divSize + 'px'; styles.ul[sizeProp] = ulSize + 'px'; styles.ul[animCss] = -(curr * liSize) + 'px'; styles.li = { width: li.width(), height: li.height() }; styles.liSize = liSize; return styles; }; var setDimensions = function(reset) { var css, tmpDivSize; var prelimCss = { div: {visibility: 'visible', position: 'relative', zIndex: 2, left: '0'}, ul: {margin: '0', padding: '0', position: 'relative', listStyleType: 'none', zIndex: 1}, li: {overflow: o.vertical ? 'hidden' : 'visible', float: o.vertical ? 'none' : 'left'} }; if (reset) { css = getDimensions(true); div.css(css.div); ul.css(css.ul); li.css(css.li); } css = getDimensions(); if (o.autoCSS) { $.extend(true, css, prelimCss); // firstCss = false; } if (o.autoWidth) { tmpDivSize = parseInt(div.css(sizeProp), 10); styles.liSize = tmpDivSize / o.visible; css.li[sizeProp] = styles.liSize - (li[outerMethod](true) - parseInt(li.css(sizeProp), 10)); // Need to adjust other settings to fit with li width css.ul[sizeProp] = (styles.liSize * itemLength) + 'px'; css.ul[animCss] = -(curr * styles.liSize) + 'px'; css.div[sizeProp] = tmpDivSize; } if (o.autoCSS) { li.css(css.li); ul.css(css.ul); div.css(css.div); } }; setDimensions(); var go = function(to, settings) { if (running) { return false; } settings = settings || {}; var prev = curr; var direction = to > curr; var speed = typeof settings.speed !== 'undefined' ? settings.speed : o.speed; // offset appears if touch moves slide; var offset = settings.offset || 0; if (o.beforeStart) { o.beforeStart.call(div, vis(), direction, settings); } // If circular and we are in first or last, then go to the other end if (o.circular) { if (to > curr && to > itemLength - visibleCeil) { // temporarily set "to" as the difference to = to - curr; curr = curr % tl; // use the difference to make "to" correct relative to curr to = curr + to; ul.css(animCss, (-curr * styles.liSize) - offset); } else if (to < curr && to < 0) { curr += tl; to += tl; ul.css(animCss, (-curr * styles.liSize) - offset); } curr = to + (to % 1); // If non-circular and "to" points beyond first or last, we change to first or last. } else { if (to < 0) { to = 0; } else if (to > itemLength - visibleFloor) { to = itemLength - visibleFloor; } curr = to; if (curr === 0 && o.first) { o.first.call(this, vis(), direction); } if (curr === itemLength - visibleFloor && o.last) { o.last.call(this, vis(), direction); } // Disable buttons when the carousel reaches the last/first, and enable when not if (o.btnPrev) { o.$btnPrev.toggleClass(o.btnDisabledClass, curr === 0); } if (o.btnNext) { o.$btnNext.toggleClass(o.btnDisabledClass, curr === itemLength - visibleFloor); } } // if btnGo, set the active class on the btnGo element corresponding to the first visible carousel li // if autoPager, set active class on the appropriate autopager element setActive(curr, activeBtnTypes); $.jCarouselLite.curr = curr; if (prev === curr && !settings.force) { if (o.afterEnd) { o.afterEnd.call(div, vis(), direction, settings); } return curr; } running = true; aniProps[animCss] = -(curr * styles.liSize); ul.anim(aniProps, speed, o.easing, function() { if (o.afterEnd) { o.afterEnd.call(div, vis(), direction, settings); } running = false; }); return curr; }; // end go function if (o.btnGo && o.btnGo.length) { if (isArray(o.btnGo) && typeof o.btnGo[0] === 'string') { $btnsGo = $(o.btnGo.join()); } else { $btnsGo = $(o.btnGo); } $btnsGo.each(function(i) { $(this).bind('click.jc', function(event) { event.preventDefault(); var btnInfo = { btnGo: this, btnGoIndex: i }; return go(o.circular ? visibleNum + i : i, btnInfo); }); }); activeBtnTypes.go = 1; } // set up timed advancer var advanceCounter = 0; var autoStop = iterations(tl, o); var autoScrollBy = typeof o.auto === 'number' ? o.auto : o.scroll; var advancer = function() { self.setAutoAdvance = setTimeout(function() { if (!autoStop || autoStop > advanceCounter) { direction = div.data('dirjc'); go(curr + (direction * autoScrollBy), {auto: true}); advanceCounter++; advancer(); } }, o.timeout); }; // bind click handlers to prev and next buttons, if set $.each(['btnPrev', 'btnNext'], function(index, btn) { if (o[btn]) { o['$' + btn] = typeof o[btn] === 'function' ? o[btn].call(div[0]) : $(o[btn]); o['$' + btn].bind('click.jc', function(event) { event.preventDefault(); var step = index === 0 ? curr - o.scroll : curr + o.scroll; if (o.directional) { // set direction of subsequent scrolls to: // 1 if "btnNext" clicked // -1 if "btnPrev" clicked div.data('dirjc', index ? 1 : -1); } return go(step); }); } }); if (!o.circular) { if (o.btnPrev && start === 0) { o.$btnPrev.addClass(o.btnDisabledClass); } if (o.btnNext && start + visibleFloor >= itemLength) { o.$btnNext.addClass(o.btnDisabledClass); } } if (o.autoPager) { pageNavCount = ceil(tl / visibleNum); pageNav = []; for (var i = 0; i < pageNavCount; i++) { pageNav.push('