/*!
* Marquee jQuery Plug-in
*
* Copyright 2009 Giva, Inc. (http://www.givainc.com/labs/) 
* 
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* 
* 	http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Date: 2009-05-20
* Rev:  1.0.01
*/
; (function($) {
    // set the version of the link select
    $.marquee = { version: "1.0.01" };

    $.fn.marquee = function(options) {
        var method = typeof arguments[0] == "string" && arguments[0];
        var args = method && Array.prototype.slice.call(arguments, 1) || arguments;
        // get a reference to the first marquee found
        var self = (this.length == 0) ? null : $.data(this[0], "marquee");

        // if a method is supplied, execute it for non-empty results
        if (self && method && this.length) {

            // if request a copy of the object, return it			
            if (method.toLowerCase() == "object") return self;
            // if method is defined, run it and return either it's results or the chain
            else if (self[method]) {
                // define a result variable to return to the jQuery chain
                var result;
                this.each(function(i) {
                    // apply the method to the current element
                    var r = $.data(this, "marquee")[method].apply(self, args);
                    // if first iteration we need to check if we're done processing or need to add it to the jquery chain
                    if (i == 0 && r) {
                        // if this is a jQuery item, we need to store them in a collection
                        if (!!r.jquery) {
                            result = $([]).add(r);
                            // otherwise, just store the result and stop executing
                        } else {
                            result = r;
                            // since we're a non-jQuery item, just cancel processing further items
                            return false;
                        }
                        // keep adding jQuery objects to the results
                    } else if (!!r && !!r.jquery) {
                        result = result.add(r);
                    }
                });

                // return either the results (which could be a jQuery object) or the original chain
                return result || this;
                // everything else, return the chain
            } else return this;
            // initializing request
        } else {
            // create a new marquee for each object found
            return this.each(function() {
                new $.Marquee(this, options);
            });
        };
    };

    $.Marquee = function(marquee, options) {
        options = $.extend({}, $.Marquee.defaults, options);

        var self = this, $marquee = $(marquee), $lis = $marquee.find("> li"), current = -1, hard_paused = false, paused = false, loop_count = 0;

        // store a reference to this marquee
        $.data($marquee[0], "marquee", self);

        // pause the marquee
        this.pause = function() {
            // mark as hard pause (no resume on hover)
            hard_paused = true;
            // pause scrolling
            pause();
        }

        // resume the marquee
        this.resume = function() {
            // mark as hard pause (no resume on hover)
            hard_paused = false;
            // resume scrolling
            resume();
        }

        // update the marquee
        this.update = function() {
            var iCurrentCount = $lis.length;

            // update the line items
            $lis = $marquee.find("> li");

            // if we only have one item, show the next item by resuming playback (which will scroll to the next item)
            if (iCurrentCount <= 1) resume();
        }

        // code to introduce the new marquee message
        function show(i) {
            // if we're already scrolling an item, stop processing
            if ($lis.filter("." + options.cssShowing).length > 0) return false;

            var $li = $lis.eq(i);

            // run the beforeshow callback
            if ($.isFunction(options.beforeshow)) options.beforeshow.apply(self, [$marquee, $li]);

            var params = {
                top: (options.yScroll == "top" ? "-" : "+") + $li.outerHeight() + "px"
				, left: 0
            };

            $marquee.data("marquee.showing", true);
            $li.addClass(options.cssShowing);

            $li.css(params).animate({ top: "0px" }, options.showSpeed, options.fxEasingShow, function() {
                // run the show callback
                if ($.isFunction(options.show)) options.show.apply(self, [$marquee, $li]);
                $marquee.data("marquee.showing", false);
                scroll($li);
            });
        }

        // keep the message on the screen for the user to read, scrolling long messages
        function scroll($li, delay) {
            // if paused, stop processing
            if (paused == true) return false;

            // get the delay speed
            delay = delay || options.pauseSpeed;
            // if	item is wider than marquee, then scroll
            if (doScroll($li)) {
                setTimeout(function() {
                    // if paused, stop processing (we need to check to see if the pause state has changed)
                    if (paused == true) return false;

                    var width = $li.outerWidth(), endPos = width * -1, curPos = parseInt($li.css("left"), 10);

                    // scroll the message to the left					
                    $li.animate({ left: endPos + "px" }, ((width + curPos) * options.scrollSpeed), options.fxEasingScroll, function() { finish($li); });
                }, delay);
            } else if ($lis.length > 1) {
                setTimeout(function() {
                    // if paused, stop processing (we need to check to see if the pause state has changed)
                    if (paused == true) return false;

                    // scroll the message down
                    $li.animate({ top: (options.yScroll == "top" ? "+" : "-") + $marquee.innerHeight() + "px" }, options.showSpeed, options.fxEasingScroll);
                    // finish showing this message
                    finish($li);
                }, delay);
            }

        }

        function finish($li) {
            // run the aftershow callback, only after we've displayed the first option
            if ($.isFunction(options.aftershow)) options.aftershow.apply(self, [$marquee, $li]);

            // mark that we're done scrolling this element
            $li.removeClass(options.cssShowing);

            // show the next message
            showNext();
        }

        // this function will pause the current animation
        function pause() {
            // mark the message as paused
            paused = true;
            // don't stop animation if we're just beginning to show the marquee message
            if ($marquee.data("marquee.showing") != true) {
                // we must dequeue() the animation to ensure that it does indeed stop animation
                $lis.filter("." + options.cssShowing).dequeue().stop();
            }
        }

        // this function will resume the previous animation
        function resume() {
            // mark the message as resumed
            paused = false;
            // don't resume animation if we haven't completed introducing the message
            if ($marquee.data("marquee.showing") != true) scroll($lis.filter("." + options.cssShowing), 1);
        }

        // determine if we should pause on hover
        if (options.pauseOnHover) {
            $marquee.hover(
				function() {
				    // if hard paused, prevent hover events
				    if (hard_paused) return false;
				    // pause scrolling
				    pause();
				}
				, function() {
				    // if hard paused, prevent hover events
				    if (hard_paused) return false;
				    // resume scrolling
				    resume();
				}
			);
        }

        // determines if the message needs to be scrolled to read
        function doScroll($li) {
            return ($li.outerWidth() > $marquee.innerWidth());
        }

        // show the next message in the queue		
        function showNext() {
            // increase the current counter (starts at -1, to indicate a new marquee beginning)
            current++;

            // if we only have 1 entry and it doesn't need to scroll, just cancel processing
            if (current >= $lis.length) {
                // if we've reached our loop count, cancel processing
                if (!isNaN(options.loop) && options.loop > 0 && (++loop_count >= options.loop)) return false;
                current = 0;
            }

            // show the next message
            show(current);
        }

        // run the init callback
        if ($.isFunction(options.init)) options.init.apply(self, [$marquee, options]);

        // show the first item
        showNext();
    };

    $.Marquee.defaults = {
        yScroll: "top"                          // the position of the marquee initially scroll (can be either "top" or "bottom")
		, showSpeed: 850                          // the speed of to animate the initial dropdown of the messages
		, scrollSpeed: 12                         // the speed of the scrolling (keep number low)
		, pauseSpeed: 5000                        // the time to wait before showing the next message or scrolling current message
		, pauseOnHover: true                      // determine if we should pause on mouse hover
		, loop: -1                                // determine how many times to loop through the marquees (#'s < 0 = infinite)
		, fxEasingShow: "swing"                   // the animition easing to use when showing a new marquee
		, fxEasingScroll: "linear"                // the animition easing to use when showing a new marquee

        // define the class statements
		, cssShowing: "marquee-showing"

        // event handlers
		, init: null                              // callback that occurs when a marquee is initialized
		, beforeshow: null                        // callback that occurs before message starts scrolling on screen
		, show: null                              // callback that occurs when a new marquee message is displayed
		, aftershow: null                         // callback that occurs after the message has scrolled
    };

})(jQuery);

