/**
* author Remy Sharp
* url http://remysharp.com/tag/marquee
*/

(function ($) {
    $.fn.marquee = function (klass) {
        var newMarquee = [],
        last = this.length;

        // works out the left or right hand reset position, based on scroll
        // behavior, current direction and new direction
        function getReset(newDir, marqueeRedux, marqueeState) {
            var behavior = marqueeState.behavior, width = marqueeState.width, dir = marqueeState.dir;
            var r = 0;
            if (behavior == 'alternate') {
                r = newDir == 1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : width;
            } else if (behavior == 'slide') {
                if (newDir == -1) {
                    r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] : width;
                } else {
                    r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : 0;
                }
            } else {
                r = newDir == -1 ? marqueeRedux[marqueeState.widthAxis] : 0;
            }
            return r;
        }

        // single "thread" animation
        function animateMarquee() {
            var i = newMarquee.length,
            marqueeRedux = null,
            $marqueeRedux = null,
            marqueeState = {},
            newMarqueeList = [],
            hitedge = false;
                
            while (i--) {
                marqueeRedux = newMarquee[i];
                $marqueeRedux = $(marqueeRedux);
                marqueeState = $marqueeRedux.data('marqueeState');
                
                if ($marqueeRedux.data('paused') !== true) {
                    // TODO read scrollamount, dir, behavior, loops and last from data
                    marqueeRedux[marqueeState.axis] += (marqueeState.scrollamount * marqueeState.dir);

                    // only true if it's hit the end
                    hitedge = marqueeState.dir == -1 ? marqueeRedux[marqueeState.axis] <= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState) : marqueeRedux[marqueeState.axis] >= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState);
                    
                    if ((marqueeState.behavior == 'scroll' && marqueeState.last == marqueeRedux[marqueeState.axis]) || (marqueeState.behavior == 'alternate' && hitedge && marqueeState.last != -1) || (marqueeState.behavior == 'slide' && hitedge && marqueeState.last != -1)) {                        
                        if (marqueeState.behavior == 'alternate') {
                            marqueeState.dir *= -1; // flip
                        }
                        marqueeState.last = -1;

                        $marqueeRedux.trigger('stop');

                        marqueeState.loops--;
                        if (marqueeState.loops === 0) {
                            if (marqueeState.behavior != 'slide') {
                                marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
                            } else {
                                // corrects the position
                                marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir * -1, marqueeRedux, marqueeState);
                            }

                            $marqueeRedux.trigger('end');
                        } else {
                            // keep this marquee going
                            newMarqueeList.push(marqueeRedux);
                            $marqueeRedux.trigger('start');
                            marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
                        }
                    } else {
                        newMarqueeList.push(marqueeRedux);
                    }
                    marqueeState.last = marqueeRedux[marqueeState.axis];

                    // store updated state only if we ran an animation
                    $marqueeRedux.data('marqueeState', marqueeState);
                } else {
                    // even though it's paused, keep it in the list
                    newMarqueeList.push(marqueeRedux);                    
                }
            }

            newMarquee = newMarqueeList;
            
            if (newMarquee.length) {
                setTimeout(animateMarquee, 25);
            }            
        }
        
        // TODO consider whether using .html() in the wrapping process could lead to loosing predefined events...
        this.each(function (i) {
            var $marquee = $(this),
            width = $marquee.attr('width') || $marquee.width(),
            height = $marquee.attr('height') || $marquee.height(),
            $marqueeRedux = $marquee.after('<div ' + (klass ? 'class="' + klass + '" ' : '') + 'style="display: block-inline; width: ' + width + 'px; height: ' + height + 'px; overflow: hidden;"><div style="float: left; white-space: nowrap;">' + $marquee.html() + '</div></div>').next(),
            marqueeRedux = $marqueeRedux.get(0),
            hitedge = 0,
            direction = ($marquee.attr('direction') || 'left').toLowerCase(),
            marqueeState = {
                dir : /down|right/.test(direction) ? -1 : 1,
                axis : /left|right/.test(direction) ? 'scrollLeft' : 'scrollTop',
                widthAxis : /left|right/.test(direction) ? 'scrollWidth' : 'scrollHeight',
                last : -1,
                loops : $marquee.attr('loop') || -1,
                scrollamount : $marquee.attr('scrollamount') || this.scrollAmount || 2,
                behavior : ($marquee.attr('behavior') || 'scroll').toLowerCase(),
                width : /left|right/.test(direction) ? width : height
            };
            
            // corrects a bug in Firefox - the default loops for slide is -1
            if ($marquee.attr('loop') == -1 && marqueeState.behavior == 'slide') {
                marqueeState.loops = 1;
            }

            $marquee.remove();
            
            // add padding
            if (/left|right/.test(direction)) {
                $marqueeRedux.find('> div').css('padding', '0 ' + width + 'px');
            } else {
                $marqueeRedux.find('> div').css('padding', height + 'px 0');
            }
            
            // events
            $marqueeRedux.bind('stop', function () {
                $marqueeRedux.data('paused', true);
            }).bind('pause', function () {
                $marqueeRedux.data('paused', true);
            }).bind('start', function () {
                $marqueeRedux.data('paused', false);
            }).bind('unpause', function () {
                $marqueeRedux.data('paused', false);
            }).data('marqueeState', marqueeState); // finally: store the state
            
            // todo - rerender event allowing us to do an ajax hit and redraw the marquee

            newMarquee.push(marqueeRedux);

            marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
            $marqueeRedux.trigger('start');
            
            // on the very last marquee, trigger the animation
            if (i+1 == last) {
                animateMarquee();
            }
        });            

        return $(newMarquee);
    };
}(jQuery));
//
////
//;(function($){
//    //jQuery proxy method fail, when bind an event to a proxy object
//    var proxy = function(__method){
//        var  args = Array.prototype.slice.call(arguments, 1), object = args.shift();
//        return function(event) {
//            return __method.apply(object, args.concat(Array.prototype.slice.call(arguments,0)));
//        }
//    };
//
//    function Marquee(element, config){
//
//        //save html element
//        this.element = element;
//        this.content = element.children();
//
//        //merge the configuration
//        $.extend(this, config);
//
//        //init css element
//        this.element.css({
//             'overflow' : 'hidden',
//            'position' : 'relative'
//        });
//	this.content.css('position', 'relative');
//        
//        //direction
//        this.inverseDirc = this.speed < 0;
//        this.dirc = this.vertical ? 'top' : 'left';
//        var offset = 'offset' + (this.vertical ? 'Height' : 'Width');
//
//        //dimension variables
//        var cookieStep;
//        if(this.enableCookie && this.element.attr('id')){
//            cookieStep = this.getCookie();
//            $(window).bind('unload', proxy(this.saveCookie, this));
//        }
//        if($.browser.webkit){
//            //fixe webkit bug, dimensions change strangely when the position change (and when there is lots of content)
//            this.startStep = this.maxStep = 0;
//            this.__defineGetter__('maxStep', function() {
//                return this.inverseDirc ? this.element.attr(offset) : -this.content.attr(offset);
//            });
//            this.__defineGetter__('startStep', function() {
//                return !this.inverseDirc ? this.element.attr(offset) : -this.content.attr(offset);
//            });
//            //!!! another solution fails.
//            if(cookieStep){
//                this.currentStep = cookieStep;
//            }else if(this.inverseDirc){
//                this.content.css(this.dirc, this.maxStep);
//                this.currentStep = this.maxStep
//            }else this.currentStep = this.startStep
//        }else{
//            var elementDim = this.element.attr(offset);
//            var contentDim = this.content.attr(offset);
//            this.maxStep = this.inverseDirc ? elementDim : -contentDim;
//            this.startStep = this.inverseDirc ? -contentDim : elementDim;
//            this.currentStep = cookieStep || this.startStep;
//        }
//
//
//        //init acceleration event listener
//        if(this.speedUp || this.speedDown){
//            this.eventAcc = this.eventAcc == 'over' ? ['mouseenter', 'mouseleave'] : ['mousedown', 'mouseup'];
//            this.oriSpeed = this.speed;
//            
//            if(this.speedUp){
//                this.speedUp = $(this.speedUp);
//                this.speedUp[this.eventAcc[0]](proxy(this.enableAcc, this, this.inverseDirc));
//                if(this.eventAcc != 'over'){
//                    this.disableSelection($(this.speedUp)[0]);
//                }
//            }
//            if(this.speedDown){
//                this.speedDown = $(this.speedDown);
//                this.speedDown[this.eventAcc[0]](proxy(this.enableAcc, this, !this.inverseDirc));
//                if(this.eventAcc != 'over'){
//                    this.disableSelection($(this.speedDown)[0]);
//                }
//            }
//        }
//
//        //init drag event listener
//        if(this.draggable){
//            this.element.bind('mousedown', null, proxy(this.enableDrag, this));
//            this.disableSelection(this.element[0]);
//        }
//
//        if(this.enableScroll){
//            this.element.mousewheel(proxy(this.scroll, this));
//        }
//
//        //init event for enable/disable timer
//        if(this.stopOnOver){
//            this.element.mouseenter(proxy(this.enter, this));
//            this.element.mouseleave(proxy(this.leave, this));
//        }
//
//        //enable timer
//        this.enableTimer();
//    }
//
//    Marquee.prototype = {
//
//        speed : 0.5,
//        coefAcc : 3,
//        vertical : true,
//        stopOnOver : true,
//        eventAcc : 'over',
//        stepScroll : 20,
//        animateScrollDuration : 100,
//        animateScrollEasing : 'easeOut',
//
//        scroll : function(event, delta){
//            var step, inverse;
//            if (delta < 0) {
//                step = -this.stepScroll;
//                if(!this.inverseDirc){
//                    inverse = true;
//                    this.toogleDirc();
//                }
//            }else if (delta > 0){
//                step = this.stepScroll;
//                if(this.inverseDirc){
//                    inverse = true;
//                    this.toogleDirc();
//                }
//            }
//            this.run(step, this.enableAnimateScroll);
//            if(inverse)
//                this.toogleDirc();
//        },
//
//        disableSelection : function (target){
//            target.onmousedown = target.onselectstart = target.ondragstart = function(){return false;};
//        },
//
//        enableDrag : function(event){
//            if(!this.stopOnOver)
//                disableTimer();
//            this.onDrag = true;
//            this.lastDragCoor = this.vertical ? event.pageY : event.pageX;
//            this.dragHandler = proxy(this.drag, this);
//            $(document).bind('mousemove', this.dragHandler);
//            this.toogleDragHandler = proxy(this.disableDrag, this);
//            $(document).mouseup(this.toogleDragHandler );
//            if(this.clsDrag){
//                $(document.body).addClass(this.clsDrag);
//                this.element.addClass(this.clsDrag);
//            }
//        },
//
//        disableDrag : function(){
//            this.onDrag = false;
//            if(this.inverseDirc != this.speed < 0)
//                this.toogleDirc();
//            this.inverseDirc = this.speed < 0;
//            $(document).unbind('mousemove', this.dragHandler);
//            $(document).unbind('mouseup', this.toogleDragHandler);
//            if(!this.isOver)
//                this.enableTimer();
//            if(this.clsDrag){
//                $(document.body).removeClass(this.clsDrag);
//                this.element.removeClass(this.clsDrag);
//            }
//        },
//
//        drag : function(event){
//            var coor = this.vertical ? event.pageY : event.pageX;
//            if((this.inverseDirc && this.lastDragCoor > coor)
//                || (!this.inverseDirc && this.lastDragCoor < coor)){
//                this.toogleDirc();
//            }
//            this.run(this.lastDragCoor - coor);
//            this.lastDragCoor = coor;
//        },
//
//        enableAcc : function(inverse){
//            this.speed *= inverse ? -this.coefAcc : this.coefAcc;
//            if(inverse)
//                this.toogleDirc();
//            this.toogleClsSpeed(inverse, false);
//            this.disableAccHandler = proxy(this.disableAcc, this, inverse);
//            (this.eventAcc[1] == 'mouseleave' ? $(this['speed' + (inverse ? 'Down' : 'Up')]) : $(document))
//                [this.eventAcc[1]](this.disableAccHandler);
//        },
//
//        disableAcc : function(inverse){
//            this.speed = this.oriSpeed;
//            if(inverse)
//                this.toogleDirc();
//            this.toogleClsSpeed(inverse, true);
//             (this.eventAcc[1] == 'mouseleave' ? $(this['speed' + (inverse ? 'Down' : 'Up')]) : $(document))
//                .unbind(this.eventAcc[1], this.disableAccHandler);
//        },
//
//        toogleDirc : function(){
//            this.inverseDirc = !this.inverseDirc;
//            if(!$.browser.webkit){
//                var tmp = this.startStep;
//                this.startStep = this.maxStep;
//                this.maxStep = tmp;
//            }
//        },
//
//        toogleClsSpeed : function(inverse, remove){
//            var cls = this[inverse ? 'clsSpeedDown' : 'clsSpeedUp'];
//            if(cls)
//                this[inverse ? 'speedDown' : 'speedUp'][remove ? 'removeClass' : 'addClass'](cls);
//        },
//
//        enter : function(){
//            this.isOver = true;
//            this.disableTimer();
//        },
//
//        leave : function(){
//            this.isOver = false;
//            if(!this.onDrag)
//                this.enableTimer();
//        },
//
//        run : function(step, animate){
//            step = step != undefined ? step : this.speed;
//            this.currentStep -= step;
//            if(animate){
//                this.content.stop();
//                var ani = {};
//                ani[this.dirc] = this.currentStep;
//                this.content.animate(ani, 250);
//            }else this.content.css(this.dirc, this.currentStep + 'px');
//            if((this.inverseDirc && this.currentStep >= this.maxStep)
//                || (!this.inverseDirc && this.currentStep <= this.maxStep)){
//                this.currentStep = this.startStep;
//            }
//        },
//
//        disableTimer : function(){
//            clearInterval(this.timer);
//            this.timer = null;
//        },
//
//        enableTimer : function(){
//            this.timer = setInterval(proxy(this.run, this, null, null), 35);
//        },
//
//        pause : function(){
//            if(this.timer) return;
//            this.enableTimer();
//        },
//
//        resume : function(){
//            if(this.timer) return;
//            this.enableTimer();
//        },
//
//        getCookie : function(){
//            var n = 'marquee_' + this.element.attr('id');
//            var i = document.cookie.indexOf(n + "=");
//            if (i > -1) {
//                i += n.length + 1
//                var j = document.cookie.indexOf(';', i);
//                if (j < 0)
//                    j = document.cookie.length;
//                return unescape(document.cookie.substring(i, j));
//            }
//            return '';
//        },
//        saveCookie : function(){           
//            document.cookie = 'marquee_' + this.element.attr('id') + "=" + this.currentStep + ';';
//        }
//    };
//
//
//    $.fn.marquee = function(options){
//        this.marquee = new Marquee(this, options);
//        return this;
//    };
//
//})(jQuery);

