jQuery.noConflict();
var $j = jQuery;

function load_posters() {
	$j('img.mpdb').each(function() {
		var obj = $j(this); //To pass as an extra parameter for accessing this object during callback
		var arrayOfClasses = $j(this).attr('class').split(' '); //Get all classes into array
		var wClass = arrayOfClasses[1]; //The second class: the width of the image passed as a parameter
		$j.getJSON("http://api.movieposterdb.com/json.inc.php?imdb="+$j(this).attr('alt')+"&title=&width="+wClass+"&format=json&callback=?", {obj: obj, width: wClass, error: function() {	$j(obj).attr('src', '../images/image-not-available-'+wClass+'.jpg'); } },
			function(data) {
				if (data.errors) {
					$j(obj).attr('src', '../images/image-not-available-'+wClass+'.jpg');
				} else {
					$j(obj).attr('src', data.imageurl);
				}
		});
	});
}

function gripTextareas() {
	$j('textarea:not(.processed)').TextAreaResizer();
}

function popup(link, windowname) {
if (! window.focus)return true;
var href;
if (typeof(link) == 'string')
	href=link;
else
	href=link.href;
window.open(href, windowname, 'width=600,height=200,scrollbars=yes');
return false;
}


function roundToDec(value) { return Math.round(value*Math.pow(10, 1))/Math.pow(10,1); }

function roundToHalf(value) {
	var converted = parseFloat(value); // Make sure we have a number
	var decimal = (converted - parseInt(converted, 10));
	decimal = Math.round(decimal * 10);
	if (decimal == 5) { return (parseInt(converted, 10)+0.5); }
	if ( (decimal < 3) || (decimal > 7) ) {
		return Math.round(converted);
	} else {
		return (parseInt(converted, 10)+0.5);
	}
}

// This updates the slider graphics and values
// v: the array of values that this slider can be. e.g: 1,1.5,2,2.5,etc or 1.1,1.2,1.3,etc
// id: the id of the slider
// sync: if true, we are sycnronizing a child or parent to match a slider indirectly
function update(v, id, sync, context) {

	// Change quality image if one exists
	if (id < 12) {
		//$j('#'+categories[id-1].name+'-img').attr('className', descriptor_1[Math.round(v-.5)].toLowerCase()+'-m qual-img-m');
		$j('#'+categories[id-1].name+'-img').attr('className', descriptor_1[Math.round(v*2)].toLowerCase()+'-m qual-img-m');
	}
	// Set descriptor word based upon value
	// Old version when there were only 5 possible scoring words
	//if (id < 12) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_1[Math.round(v-.5)]); }

	// New version with 11 differnt scoring words for all categories sans Edge and Reality.
	if (id < 12) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_1[v*2]); }
	
	else if (id>11&&id<16) {
		if(v<0.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[0]); }
		else if (v>0.5&&v<1.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[1]); }
		//Is there a small bug here? Going up, Risque stays until > "3.6" or 2.6 and changes to Crude on "3.7"...
		//going down Crude stays until 3.6 and changes to Risque on 3.5. Hmm...this happens on FSM...does it also
		//happen on CAR? 
		else if (v>1.5&&v<2.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[2]); }
		else if (v>2.6&&v<3.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[3]); }
		else if(v>3.5) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[4]); }
	}
	else if (id>15) {
		if(v==0) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[0]); }
		else if (v>0&&v<1.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[1]); }
		else if (v>1.0&&v<2.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[2]); }
		else if (v>2.0&&v<3.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[3]); }
		else if (v>3.0) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[4]); }
	}

	// Set visible value of slider
	if(id < 12) {
		$j('#'+categories[id-1].name+'-value').text(v);
	} else { // Need to compensate for slider starting at 0 (for graphical issue) in case of Edge / Reality sets, add 1.
		$j('#'+categories[id-1].name+'-value').text(v+1);
	}

	// If sync is true, we are sycnronizing a child or parent to match a slider indirectly
	if (sync) { 
		eval(categories[id-1].name+'_slider').setValue(v);
	}
	
	//changed this.id to this
	if (!sync) {
		var avg = 0;

		// Parent sliders: we only need to set all children to same value as parent
		if (id==1||id==2||id==7||id==12||id==16) {
			$j.each(eval(categories[id-1].name+'_sub'), function() { update(v, this, 1, context); });
			if (context == 'fsm') {
				$j('#wrap'+id).siblings('.main').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.sub').attr('checked', false);
			}
			//Children sliders, the only difference between each of these is what name and id their parent is...maybe abstract?
		} else {
			if (id > 2 && id < 7) {
				$j.each(acting_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt));
				});
				update(roundToHalf(avg), 2, 1, context);
			} else if (id > 7 && id < 12) {
				$j.each(film_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt));
				});
				update(roundToHalf(avg), 7, 1, context);
			} else if (id > 12 && id < 16) {
				$j.each(edge_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt)) - 1;
				});
				update(roundToDec(avg/3), 12, 1, context);
			} else if (id > 16 && id <= 19) {
				$j.each(reality_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt)) - 1;
				});
				update(roundToDec(avg/3), 16, 1, context);
			}
			
			if (context == 'fsm') {
				$j('#wrap'+id).siblings('.sub').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.sub').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.main').attr('checked', false);
			}
		}
	}
}


//Update hidden field on change
//function updateInput(v, node, id) {
function updateInput(v, node) {
	//update the hidden value
	node.value = v;
/*
	sync = 0;
	context = 'car';

	//update the slider and synced sliders to match.
	// Change quality image if one exists
	if (id < 12) { $j('#'+categories[id-1].name+'-img').attr('className', descriptor_1[Math.round(v*2)].toLowerCase()+'-m qual-img-m'); }
	
	if (id < 12) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_1[v*2]); }

	else if (id>11&&id<16) {
		if(v<0.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[0]); }
		else if (v>0.5&&v<1.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[1]); }
		//Is there a small bug here? Going up, Risque stays until > "3.6" or 2.6 and changes to Crude on "3.7"...
		//going down Crude stays until 3.6 and changes to Risque on 3.5. Hmm...this happens on FSM...does it also
		//happen on CAR? 
		else if (v>1.5&&v<2.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[2]); }
		else if (v>2.6&&v<3.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[3]); }
		else if(v>3.5) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[4]); }
	}
	else if (id>15) {
		if(v==0) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[0]); }
		else if (v>0&&v<1.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[1]); }
		else if (v>1.0&&v<2.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[2]); }
		else if (v>2.0&&v<3.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[3]); }
		else if (v>3.0) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[4]); }
	}

	// Set visible value of slider
	if(id < 12) {
		$j('#'+categories[id-1].name+'-value').text(v);
	} else { // Need to compensate for slider starting at 0 (for graphical issue) in case of Edge / Reality sets, add 1.
		$j('#'+categories[id-1].name+'-value').text(v+1);
	}

	// If sync is true, we are sycnronizing a child or parent to match a slider indirectly
	if (sync) { 
		eval(categories[id-1].name+'_slider').setValue(v);
	}

	//changed this.id to this
	if (!sync) {
	
	var avg = 0;
	
		if (id==1||id==2||id==7||id==12||id==16) {
			$j.each(eval(categories[id-1].name+'_sub'), function() {
				//update(v, this, 1, context);
				var id = this;
				if (this < 12) { $j('#'+categories[this-1].name+'-img').attr('className', descriptor_1[Math.round(v*2)].toLowerCase()+'-m qual-img-m'); }
				if (this < 12) { $j('#'+categories[this-1].name+'-descriptor').text(descriptor_1[v*2]); }

				else if (id>11&&id<16) {
					if(v<0.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[0]); }
					else if (v>0.5&&v<1.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[1]); }
					else if (v>1.5&&v<2.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[2]); }
					else if (v>2.6&&v<3.6) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[3]); }
					else if(v>3.5) { $j('#'+categories[id-1].name+'-descriptor').text(eval('descriptor_'+id)[4]); }
				}
				else if (id>15) {
					if(v==0) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[0]); }
					else if (v>0&&v<1.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[1]); }
					else if (v>1.0&&v<2.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[2]); }
					else if (v>2.0&&v<3.1) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[3]); }
					else if (v>3.0) { $j('#'+categories[id-1].name+'-descriptor').text(descriptor_16[4]); }
				}

				// Set visible value of slider
				if(id < 12) {
					$j('#'+categories[id-1].name+'-value').text(v);
				} else { // Need to compensate for slider starting at 0 (for graphical issue) in case of Edge / Reality sets, add 1.
					$j('#'+categories[id-1].name+'-value').text(v+1);
				}

			if (context == 'fsm') {
				$j('#wrap'+id).siblings('.main').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.sub').attr('checked', false);
			}

			//Children sliders, the only difference between each of these is what name and id their parent is...maybe abstract?
			});
		} else {
			if (id > 2 && id < 7) {
				$j.each(acting_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt));
				});
				update(roundToHalf(avg), 2, 1, context);
			} else if (id > 7 && id < 12) {
				$j.each(film_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt));
				});
				update(roundToHalf(avg), 7, 1, context);
			} else if (id > 12 && id < 16) {
				$j.each(edge_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt)) - 1;
				});
				update(roundToDec(avg/3), 12, 1, context);
			} else if (id > 16 && id <= 19) {
				$j.each(reality_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt)) - 1;
				});
				update(roundToDec(avg/3), 16, 1, context);
			}
			if (context == 'fsm') {
				$j('#wrap'+id).siblings('.sub').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.sub').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.main').attr('checked', false);
			}
		}
/*
		var avg = 0;

		// Parent sliders: we only need to set all children to same value as parent
		if (id==1||id==2||id==7||id==12||id==16) {
			$j.each(eval(categories[id-1].name+'_sub'), function() { update(v, this, 1, context); });
			if (context == 'fsm') {
				$j('#wrap'+id).siblings('.main').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.sub').attr('checked', false);
			}
			//Children sliders, the only difference between each of these is what name and id their parent is...maybe abstract?
		} else {
			if (id > 2 && id < 7) {
				$j.each(acting_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt));
				});
				update(roundToHalf(avg), 2, 1, context);
			} else if (id > 7 && id < 12) {
				$j.each(film_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt));
				});
				update(roundToHalf(avg), 7, 1, context);
			} else if (id > 12 && id < 16) {
				$j.each(edge_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt)) - 1;
				});
				update(roundToDec(avg/3), 12, 1, context);
			} else if (id > 16 && id <= 19) {
				$j.each(reality_sub, function() {
					avg = parseFloat(avg + ($j('#'+categories[this-1].name+'-value').text() * categories[this-1].wt)) - 1;
				});
				update(roundToDec(avg/3), 16, 1, context);
			}
			if (context == 'fsm') {
				$j('#wrap'+id).siblings('.sub').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.sub').attr('checked', true);
				$j('#wrap'+id).parent().siblings().children('.main').attr('checked', false);
			}
		}
	}
	*/
}



/* 
	jQuery TextAreaResizer plugin
	Created on 17th January 2008 by Ryan O'Dell 
	Version 1.0.4
	
	Converted from Drupal -> textarea.js
	Found source: http://plugins.jquery.com/misc/textarea.js
	$Id: textarea.js,v 1.11.2.1 2007/04/18 02:41:19 drumm Exp $

	1.0.1 Updates to missing global 'var', added extra global variables, fixed multiple instances, improved iFrame support
	1.0.2 Updates according to textarea.focus
	1.0.3 Further updates including removing the textarea.focus and moving private variables to top
	1.0.4 Re-instated the blur/focus events, according to information supplied by dec

	
*/
(function($) {
	/* private variable "oHover" used to determine if you're still hovering over the same element */
	var textarea, staticOffset;  // added the var declaration for 'staticOffset' thanks to issue logged by dec.
	var iLastMousePos = 0;
	var iMin = 32;
	var grip;
	/* TextAreaResizer plugin */
	$.fn.TextAreaResizer = function() {
		return this.each(function() {
		    textarea = $(this).addClass('processed'), staticOffset = null;

			// 18-01-08 jQuery bind to pass data element rather than direct mousedown - Ryan O'Dell
		    // When wrapping the text area, work around an IE margin bug.  See:
		    // http://jaspan.com/ie-inherited-margin-bug-form-elements-and-haslayout
		    $(this).wrap('<div class="resizable-textarea"><span></span></div>')
		      .parent().append($('<div class="grippie"></div>').bind("mousedown",{el: this} , startDrag));

		    var grippie = $('div.grippie', $(this).parent())[0];
		    grippie.style.marginRight = (grippie.offsetWidth - $(this)[0].offsetWidth) +'px';

		});
	};
	/* private functions */
	function startDrag(e) {
		textarea = $(e.data.el);
		textarea.blur();
		iLastMousePos = mousePosition(e).y;
		staticOffset = textarea.height() - iLastMousePos;
		textarea.css('opacity', 0.25);
		$(document).mousemove(performDrag).mouseup(endDrag);
		return false;
	}

	function performDrag(e) {
		var iThisMousePos = mousePosition(e).y;
		var iMousePos = staticOffset + iThisMousePos;
		if (iLastMousePos >= (iThisMousePos)) {
			iMousePos -= 5;
		}
		iLastMousePos = iThisMousePos;
		iMousePos = Math.max(iMin, iMousePos);
		textarea.height(iMousePos + 'px');
		if (iMousePos < iMin) {
			endDrag(e);
		}
		return false;
	}

	function endDrag(e) {
		$(document).unbind('mousemove', performDrag).unbind('mouseup', endDrag);
		textarea.css('opacity', 1);
		textarea.focus();
		textarea = null;
		staticOffset = null;
		iLastMousePos = 0;
	}

	function mousePosition(e) {
		return { x: e.clientX + document.documentElement.scrollLeft, y: e.clientY + document.documentElement.scrollTop };
	};
})(jQuery);


(function($){$.fn.filestyle=function(options){var settings={width:250};if(options){$.extend(settings,options);};return this.each(function(){var self=this;var wrapper=$("<div>").css({"width":settings.imagewidth+"px","height":settings.imageheight+"px","background":"url("+settings.image+") 0 0 no-repeat","background-position":"right","display":"inline","position":"absolute","overflow":"hidden"});var filename=$('<input class="file">').addClass($(self).attr("class")).css({"display":"inline","position":"absolute","top":"30px","left":"-10px","width":settings.width+"px"});$(self).before(filename);$(self).wrap(wrapper);$(self).css({"position":"relative","height":settings.imageheight+"px","width":settings.width+"px","display":"inline","cursor":"pointer","opacity":"0.0"});if($.browser.mozilla){if(/Win/.test(navigator.platform)){$(self).css("margin-left","-142px");}else{$(self).css("margin-left","-168px");};}else{$(self).css("margin-left",settings.imagewidth-settings.width+"px");};$(self).bind("change",function(){filename.val($(self).val());});});};})(jQuery);


// Cycle Lite plugin for jQuery
(function(D){var A="Lite-1.0";D.fn.cycle=function(E){return this.each(function(){E=E||{};if(this.cycleTimeout){clearTimeout(this.cycleTimeout)}this.cycleTimeout=0;this.cyclePause=0;var I=D(this);var J=E.slideExpr?D(E.slideExpr,this):I.children();var G=J.get();if(G.length<2){if(window.console&&window.console.log){window.console.log("terminating; too few slides: "+G.length)}return }var H=D.extend({},D.fn.cycle.defaults,E||{},D.metadata?I.metadata():D.meta?I.data():{});H.before=H.before?[H.before]:[];H.after=H.after?[H.after]:[];H.after.unshift(function(){H.busy=0});var F=this.className;H.width=parseInt((F.match(/w:(\d+)/)||[])[1])||H.width;H.height=parseInt((F.match(/h:(\d+)/)||[])[1])||H.height;H.timeout=parseInt((F.match(/t:(\d+)/)||[])[1])||H.timeout;if(I.css("position")=="static"){I.css("position","relative")}if(H.width){I.width(H.width)}if(H.height&&H.height!="auto"){I.height(H.height)}var K=0;J.css({position:"absolute",top:0,left:0}).hide().each(function(M){D(this).css("z-index",G.length-M)});D(G[K]).css("opacity",1).show();if(D.browser.msie){G[K].style.removeAttribute("filter")}if(H.fit&&H.width){J.width(H.width)}if(H.fit&&H.height&&H.height!="auto"){J.height(H.height)}if(H.pause){I.hover(function(){this.cyclePause=1},function(){this.cyclePause=0})}D.fn.cycle.transitions.fade(I,J,H);J.each(function(){var M=D(this);this.cycleH=(H.fit&&H.height)?H.height:M.height();this.cycleW=(H.fit&&H.width)?H.width:M.width()});J.not(":eq("+K+")").css({opacity:0});if(H.cssFirst){D(J[K]).css(H.cssFirst)}if(H.timeout){if(H.speed.constructor==String){H.speed={slow:600,fast:200}[H.speed]||400}if(!H.sync){H.speed=H.speed/2}while((H.timeout-H.speed)<250){H.timeout+=H.speed}}H.speedIn=H.speed;H.speedOut=H.speed;H.slideCount=G.length;H.currSlide=K;H.nextSlide=1;var L=J[K];if(H.before.length){H.before[0].apply(L,[L,L,H,true])}if(H.after.length>1){H.after[1].apply(L,[L,L,H,true])}if(H.click&&!H.next){H.next=H.click}if(H.next){D(H.next).bind("click",function(){return C(G,H,H.rev?-1:1)})}if(H.prev){D(H.prev).bind("click",function(){return C(G,H,H.rev?1:-1)})}if(H.timeout){this.cycleTimeout=setTimeout(function(){B(G,H,0,!H.rev)},H.timeout+(H.delay||0))}})};function B(J,E,I,K){if(E.busy){return }var H=J[0].parentNode,M=J[E.currSlide],L=J[E.nextSlide];if(H.cycleTimeout===0&&!I){return }if(I||!H.cyclePause){if(E.before.length){D.each(E.before,function(N,O){O.apply(L,[M,L,E,K])})}var F=function(){if(D.browser.msie){this.style.removeAttribute("filter")}D.each(E.after,function(N,O){O.apply(L,[M,L,E,K])})};if(E.nextSlide!=E.currSlide){E.busy=1;D.fn.cycle.custom(M,L,E,F)}var G=(E.nextSlide+1)==J.length;E.nextSlide=G?0:E.nextSlide+1;E.currSlide=G?J.length-1:E.nextSlide-1}if(E.timeout){H.cycleTimeout=setTimeout(function(){B(J,E,0,!E.rev)},E.timeout)}}function C(E,F,I){var H=E[0].parentNode,G=H.cycleTimeout;if(G){clearTimeout(G);H.cycleTimeout=0}F.nextSlide=F.currSlide+I;if(F.nextSlide<0){F.nextSlide=E.length-1}else{if(F.nextSlide>=E.length){F.nextSlide=0}}B(E,F,1,I>=0);return false}D.fn.cycle.custom=function(K,H,I,E){var J=D(K),G=D(H);G.css({opacity:0});var F=function(){G.animate({opacity:1},I.speedIn,I.easeIn,E)};J.animate({opacity:0},I.speedOut,I.easeOut,function(){J.css({display:"none"});if(!I.sync){F()}});if(I.sync){F()}};D.fn.cycle.transitions={fade:function(F,G,E){G.not(":eq(0)").css("opacity",0);E.before.push(function(){D(this).show()})}};D.fn.cycle.ver=function(){return A};D.fn.cycle.defaults={timeout:4000,speed:1000,next:null,prev:null,before:null,after:null,height:"auto",sync:1,fit:0,pause:0,delay:0,slideExpr:null}})(jQuery);


//Smooth Marquee
/**
* 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; position: absolute; z-index: 10; right: 6px; top: 0; line-height: ' + height + 'px; 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));
