﻿/// <reference path="http://ajax.microsoft.com/ajax/jQuery/jquery-1.4.1-vsdoc.js" />
/** Scrollable 'carousel'
*   @author: James Diacono
*   @date: 25th of January, 2011
*   @requires: jQuery
*   @usage: jQuery extension to scroll items hozirontally
*   @description: Very similar in nature to the the Flowplayer jQuery Tools plugin, except straightforward.
*                 See example HTML.
*/

(function ($) {
    $.fn.scrollable = function (options) {
        ///<summary>
        ///		Scrollable horizontal carousel type thing. Very similar in nature to the the Flowplayer 
        ///     jQuery Tools plugin, except straightforward.
        ///</summary>
        ///<param name="options" type="Object">
        ///<returns type="jQuery" />

        var scrollable = function (wrapper, options) {
            var config = {
                // A jQuery.animation speed argument (can be 'slow', 'fast', or a number of milliseconds)
                speed: 'fast',
                // How many items next/previous jumps
                jumpSize: 1
            };

            // Config overrides
            $.extend(config, options);

            // jQuery selector caching
            var $wrapper = $(wrapper),
                $next = $wrapper.children(".next"),
                $previous = $wrapper.children(".prev"),
                $scrollable = $wrapper.children(".scrollable"),
                $items = $scrollable.children(".items");

            var viewportWidth = $scrollable.width(),
                itemCount = 0,
                currentIndex = 0,
                currentScrollablePosition = 0,
                maxScrollablePosition = 0,
                transitioning = false;

            // Set up metrics
            $items.children().each(function () {
                // The accumulative width of the scrollable items so far
                var widthSoFar = $(this).position().left + $(this).outerWidth();


                // Determine if the item requires scrolling from zero to be seen
                var scrollPosition;
                if (widthSoFar > viewportWidth) {
                    scrollPosition = widthSoFar - viewportWidth;
                } else {
                    scrollPosition = 0;
                }

                // Update the max position
                maxScrollablePosition = scrollPosition;

                // Store the position which will show this item
                $(this).data("scrollable-position", scrollPosition);

                itemCount++;
            });

            // The navigation function
            // Args: An index (though -1 means the end), or jQueryable of an item
            // Returns: If scrolling occurred
            var seekTo = function (item, callback) {
                ///<summary>
                ///   Navigates the scrollable to a given location
                ///</summary>
                ///<param name="item" type="Number">
                ///    Navigates to the index specified.  Any integer out of bounds is sent to the end.
                ///    (including negatives).
                ///</param>
                ///<param name="item" type="jQueryable">
                ///    Navigates to the element specified
                ///</param>
                ///<param name="callback" type="Function">
                ///    Callback for when the animation finishes
                ///</param>
                ///<returns type="Boolean">
                ///   'true' if visual scrolling occurred, 'false' otherwise.
                ///</returns>
                var left, $item;

                if (typeof item === 'number') {
                    // Check if index is out of range, if so send it to the end
                    if (item > itemCount - 1 || item < 0) {
                        item = itemCount - 1; // last item
                    }

                    $item = $($items.children()[item]);
                } else {
                    // Assume jQueryable argument
                    $item = $(item);
                }

                if ($item.length === 1) {
                    // Retrieve the position data
                    left = $item.data("scrollable-position");

                    // Animate the container if scrolling occurred
                    if (left !== currentScrollablePosition) {
                        transitioning = true;

                        $items.animate({
                            "left": -left
                        }, config.speed, function () {
                            transitioning = false;

                            if (typeof callback === 'function') {
                                callback();
                            }
                        });

                        currentIndex = $item.index();
                        currentScrollablePosition = left;

                        return true;
                    } else {
                        if (typeof callback === 'function') {
                            callback();
                        }

                        currentIndex = $item.index();

                        return false;
                    }
                }
            };

            var refreshControls = function () {
                // Disable control if at end
                if (currentScrollablePosition === maxScrollablePosition) {
                    $next.addClass("disabled");
                } else {
                    $next.removeClass("disabled");
                }

                // Disable control if at start
                if (currentScrollablePosition === 0) {
                    $previous.addClass("disabled");
                } else {
                    $previous.removeClass("disabled");
                }
            };

            // Handlers
            var next = function () {
                ///<summary>
                ///   Tries to shift forward (does nothing if at end)
                ///</summary>

                // Keep going to next item until the thing moves
                var didTransition = false;
                while (!didTransition && currentScrollablePosition < maxScrollablePosition) {
                    didTransition = seekTo(currentIndex + config.jumpSize);
                }

                refreshControls();
            };

            var previous = function () {
                ///<summary>
                ///   Tries to shift back (does nothing if at start)
                ///</summary>

                // Keep going to next item until the thing moves
                var didTransition = false;
                while (!didTransition && currentScrollablePosition > 0) {
                    var imminentIndex = currentIndex - config.jumpSize;

                    // Don't want to go lower than zero
                    if (imminentIndex < 0) {
                        imminentIndex = 0;
                    }

                    didTransition = seekTo(imminentIndex);
                }

                refreshControls();
            };

            // Register handlers
            $next.click(next);
            $previous.click(previous);

            refreshControls();

            var api = {
                seekTo: seekTo,
                next: next,
                previous: previous,
                transitioning: transitioning,
                itemCount: itemCount,
                currentIndex: currentIndex
            };

            $scrollable.data("scrollable", api);
        };

        return this.each(function () {
            scrollable(this, options);
        });
    };
})(jQuery);
