﻿jQuery.fn.infiniteCarousel = function()
{
    var pauseDelay = 5000;
    var initialDelay = 4000;
    var animationSpeed = 1750;
    var easingMethod = 'easeInOutQuint';

    function repeat(str, num)
    {
        return new Array(num + 1).join(str);
    }

    return this.each(function()
    {
        var $wrapper = jQuery('> div', this),
            $slider = $wrapper.find('> ul'),
            $items = $slider.find('> li'),
            $single = $items.filter(':first'),

            singleWidth = $single.outerWidth(),
            visible = Math.ceil($wrapper.innerWidth() / singleWidth), // note: doesn't include padding or border
            currentPage = 1,
            pages = Math.ceil($items.length / visible),
            fadeTimer = null,
            autoScrollTimer = null,
            isAnimating = false;

        // 1. Pad so that 'visible' number will always be seen, otherwise create empty items
        if (($items.length % visible) != 0)
        {
            $slider.append(repeat('<li class="empty" />', visible - ($items.length % visible)));
            $items = $slider.find('> li');
        }

        // 2. Top and tail the list with 'visible' number of items, top has the last section, and tail has the first
        $items.filter(':first').before($items.slice(-visible).clone().addClass('cloned'));
        $items.filter(':last').after($items.slice(0, visible).clone().addClass('cloned'));
        $items = $slider.find('> li'); // reselect

        // 3. Set the left position to the first 'real' item
        $wrapper.scrollLeft(singleWidth * visible);

        //
        // 4. Paging functions
        //
        function gotoPage(page)
        {
            try
            {
                if (!isAnimating)
                {
                    isAnimating = true;

                    var dir = page < currentPage ? -1 : 1,
                        n = Math.abs(currentPage - page),
                        left = singleWidth * dir * visible * n;

                    highlightButtonForPage(page);

                    // Fade images out
                    $items.stop().animate({ opacity: 0.33 }, 350);

                    // ... then back in again just before the scrolling stops
                    if (fadeTimer) clearInterval(fadeTimer);
                    fadeTimer = setTimeout(function() { $items.stop().animate({ opacity: 1 }, 500); }, animationSpeed - 350);

                    // Animate carousel
                    $wrapper.filter(':not(:animated)').animate(
                        { scrollLeft: '+=' + left },
                        { duration: animationSpeed, easing: easingMethod, complete:
                        function()
                        {
                            if (page == 0)
                            {
                                $wrapper.scrollLeft(singleWidth * visible * pages);
                                page = pages;
                            }
                            else if (page > pages)
                            {
                                $wrapper.scrollLeft(singleWidth * visible);
                                // reset back to start position
                                page = 1;
                            }

                            currentPage = page;
                            isAnimating = false;
                        }
                        });
                }
            }
            catch (e) {}
            
            return false;
        }

        function gotoNextPage()
        {
            gotoPage(currentPage + 1);
        }

        function gotoPreviousPage()
        {
            gotoPage(currentPage - 1);
        }

        function highlightButtonForPage(page)
        {
            if (page == 0) page = pages;

            jQuery('.big-carousel .buttons > .button').removeClass('active');

            var className = jQuery('#carousel-image-' + page).attr('className');
            if (className)
            {
                var pairedButtonId = className.replace('for-', '').replace(' cloned', '').replace(' empty', '');
                jQuery('#' + pairedButtonId).addClass('active');
            }

            Cufon.refresh('.big-carousel .buttons > .button > a');
        }

        $wrapper.after('<a class="arrow back">&lt;</a><a class="arrow next">&gt;</a>');

        // 5. Bind to the forward and back buttons
        jQuery('.arrow.back', this).click(function()
        {
            return gotoPreviousPage();
        });

        jQuery('.arrow.next', this).click(function()
        {
            return gotoNextPage();
        });

        // Create a public interface to move to a specific page
        jQuery(this).bind('goto', function(event, page)
        {
            gotoPage(page);
        });

        // Auto-scrolling functions
        function autoScroll()
        {
            autoScrollTimer = setTimeout(function() { doAutoScroll(); }, pauseDelay + animationSpeed);
        }
        function doAutoScroll()
        {
            gotoNextPage();
            autoScroll();
        }
        function stopAutoScroll()
        {
            if (autoScrollTimer) clearTimeout(autoScrollTimer);
        }

        // 6. Commence auto-scroll
        autoScrollTimer = setTimeout(function() { doAutoScroll(); }, initialDelay);

        // 7. Pause auto-scroll when mouse enters the carousel control
        $wrapper.parent().parent().hover(
        function()
        {
            stopAutoScroll();
        },
        function()
        {
            autoScroll();
        }
        );
    });
};
