﻿/*
 * name: jQuery marquee
 *
 */
(function (window, document, $) {
    /*
        Function: $.fn.marquee

        Marquee is being used colloquially to define a slideshow-esque
        presentation layer to DOM content.

        Parameters:
            _left - Left control selector or an object collection with the
                named parameters without the preceding underscore
            _right - Right control selector
            _state - Progress control selector
            _display - Display control children selector
            _play - Play/pause button selector
            _startindex - Starting position for display images
            _active - Active elements className
            _speed - Speed at which the marquee should run
            _infinite - Marquee scrolls infinitely, defaults to false
            _increment - A number indicating how many items are moved per
                change, defaults to 1
            _start - Optional function to run before anything else
            _change - Optional animation function, will override default
                animation

        Return:
            jQuery chainables
    */
    $.fn.marquee = function (_left, _right, _state, _display, _play,
                            _startindex, _startdirection, _speed, _infinite,
                            _increment, _active, _start, _change,
                            _start_moving) {
        var test = (typeof _left === "object"),
            // Set defaults for plugin
            options = $.extend({
                // Selectors - required
                left: (test) ? "" : (_left || ""),
                right: _right || "",
                state: _state || "",
                display: _display || "",
                active: _active || "active",
                play: _play || null,
                start_moving: _start_moving || false,
                // Display optionsuration - optional
                startindex: _startindex || 0,
                startdirection: _startdirection || "left",
                speed: _speed || 0,
                infinite: _infinite || false,
                increment: _increment || 1,
                // Custom functions
                start: _start || $.noop,
                change: _change || null
            }, (test) ? _left : {});

        // Bind the change event
        if (options.change) {
            this.bind("change", options.change);
        }

        // Trigger options.start before anything else happens
        options.start.call(this, options);

        // Normalize left and right

        if (typeof options.left !== 'object' || !options.left.length) {
            options.left = $(options.left, this);
        }

        if (typeof options.right !== 'object' || !options.right.length) {
            options.right = $(options.right, this);
        }

        // Handle no left or right
        if (!options.left.length) {
            options.left = $('<div/>');
        }

        if (!options.right.length) {
            options.right = $('<div/>');
        }

        return this.each(function () {
            var $this = $(this),
                paused = !options.start_moving;
            // Cache state and display jQuery objects
            var $state = $(options.state, this),
                $display = $(options.display, this),
                $play = $(options.play, this),
                // Interval variable
                interval = null;

            var animate = function (direction, index) {
                var prev_position = $state.children("." + options.active)
                        .index(),
                    display_count = $display.length,
                    position;

                if (prev_position < 0) {
                    prev_position = 0;
                }

                if (direction === "left") {
                    position = prev_position - 1;
                } else {
                    position = prev_position + 1;
                }

                // Modulo bug fix for +/- numbers
                position = ((position % display_count) + display_count) %
                    display_count;


                if (!window.isNaN(index) && index !== null) {
                    position = index;
                }

                $display = $(options.display, $display.context);
                // Test if animation is in progress via psuedo class,
                // if not change position
                if (!$display.eq(prev_position).is(":animated")) {
                    $this.trigger("change", [ position, options ]);
                    if (!options.change) {
                        if (options.transition === "fade") {
                            $display.eq(prev_position).fadeOut("0");
                            $display.eq(position).fadeIn("slow");
                        } else if (options.transition === "slide") {
                            if (options.infinite) {
                                var $animated = $display.parent();
                                if (direction === "right") {
                                    $animated
                                        .stop(true, true)
                                        .animate({
                                            left: -(
                                                ($display.outerWidth() - 2) *
                                                (options.increment)
                                            )
                                        }, 500, function () {
                                            var $trim = $display
                                                .slice(0, options.increment)
                                                .detach();
                                            $trim.appendTo($animated);
                                            $animated.css("left", "0");
                                        });
                                } else {
                                    var $trim = $display
                                        .slice(-options.increment)
                                        .detach();
                                    $trim.prependTo($animated);

                                    var width = 0;
                                    $trim.each(function () {
                                        width += $(this).outerWidth();
                                    });
                                    $animated.css("left", -width +
                                        "px");

                                    $animated
                                        .stop(true, true)
                                        .animate({ left: "0" }, 500);
                                }
                            } else {
                                $display.parent().stop(true, true);
                                if (position in $display) {
                                    var animateEl = $display.eq(position);
                                    $display.parent().animate({
                                        left: -(animateEl.width() * position) +
                                            'px'
                                    });
                                }
                            }
                        }
                    }
                }

                // Set the new active class
                $state.children("." + options.active)
                    .removeClass(options.active);
                $state.children().eq(position).addClass(options.active);
            };

            if (options.transition === 'fade') {
                $.each($display.slice(1), function () {
                    $(this).hide();
                });
            }

            // External pausing
            $this.data("pause", function (forced) {
                if (typeof forced === 'undefined') {
                    paused = !paused;
                } else {
                    paused = forced;
                }
            });

            // Handle no state
            if (!$state.length) {
                $state = $("<ul/>");
            }

            $state.empty();

            for (var i = 0, len = $display.length; i < len; i += 1) {
                $state.prepend("<li class='inactive'>" + (len - i) + "</li>");
            }
            $state.find("li:last").addClass("last");

            if ($display.length === 1) {
                $state.hide();
            }

            // Set up the initial state and display controls
            $state.children().eq(options.start).addClass("active");
            $display.eq(options.start).show();

            $(options.state).children().eq(options.startindex)
                .addClass(options.active);

            // Auto start code
            if (options.speed > 0) {
                var startAnimate = function () {
                    if (!paused) {
                        animate(options.startdirection);

                        window.clearTimeout(interval);
                        interval = window.setTimeout(
                            startAnimate, options.speed
                        );
                    }
                };
                if ($display.length > 1) {
                    interval = window.setTimeout(startAnimate, options.speed);
                }
            }

            // TODO: implement this section to fill in elements if too small
            /*
            if (options.infinite) {
                if (
                    $display.parent().outerWidth() <=
                        $display.parent().parent().outerWidth()) { }
                else { }
            }
            */


            $play.bind("click", function () {
                paused = !paused;

                var $this = $(this);
                if ($this.hasClass("pause")) {
                    $this.addClass("play").removeClass("pause");
                }
                else {
                    $this.addClass("pause").removeClass("play");
                }
            });

            // Li clicks
            $state.delegate(
                "li:not(." + options.active + ")", "click", function () {
                    // Cancel interval
                    if (options.speed && !$play.is(".play")) {
                        $play.trigger("click");
                    }

                    // Cache local reference
                    var index = $(this).index(),
                        direction;

                    if (index > $state.find("." + options.active).index()) {
                        direction = "right";
                    } else {
                        direction = "left";
                    }

                    animate(direction, index);
                }
            );

            options.left.add(options.right).bind("mousedown", function () {
                // Cancel interval
                if (!paused) {
                    paused = true;
                }

                // Execute change image
                var direction = (options.left[0] === this) ? "left" : "right";
                animate(direction);
            });
        });

    };
}(this, this.document, this.jQuery));

