/**  
* 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)); 
