//Random Roll'd: a script that creates a simulated version of a Comet'd transaction, i.e. a server push.

(function($) {
	

$.RandomRolld = function(options){
	
	var options = jQuery.extend({
									container: 'div#activity',
									interval: 5000,
									randomness: 'strong',
									bandwidth: 3,
									timesarrow: 'past',
									feedStartIndex: 1,
									stopAfter:0
								}, options);
	
/* * The function works by animating an html list to simulate the actions of a reverse Ajax server.
 *
 * By default, it serves a push every 5000 milliseconds up to 3 items and with 'strong' randomness
 * of these values.
 *
 * More about the randomness: the script can randomize both the amount of time between the pushes,
 * and the number of items to be included in each push. By leaving it at strong, you can create
 * a highly realistic impression of disorder. If, on the other hand, you would like a periodic
 * push interval and a predictable number of items for every push, then set this value to 'weak'.
 *
 *NOTE: strong randomness is still under construction. Use 'weak' instead.
 *
 * @example
 * $.RandomRolld({
* 					container: 'div#activity',
* 					interval: 5000,
* 					randomness: 'weak',
* 					bandwidth: 4,
* 					timesarrow: 'past',
* 					iterator: function(index){ return '/activity/list/modified/desc/page' + index;}
* 				});
 * 
 * @desc Starts rolling the list items it finds inside the activity element. Randomization factor is
 * set to weak, and this causes periodic updates every 5 seconds of 4 items. Loads new values via an
 * iterator that is submitted by the user. NOTE: if the iterator is not found, the script only cycles
 * the items in the container.
 *
 * @desc Note about extra options. There are some other options that I haven't talked about, such as
 * feedStartIndex. 
 * 
 *
 *   $.RandomRolld
 * @type   jQuery
 * @param  String	   container		ID of the container of the list to roll. Default is 'div#activity'.
 * @param  Number   interval  		max number of milliseconds to wait before pushing a new item. Default is 5000.
 * @param  Number   randomness	factor of randomness in the push interval. Possible values: strong, medium, weak. Default is 'strong.'
 * @param  Number   bandwidth		maximum number of items that can be shown by a single push. Default is 3.
 * @param  String    timesarrow	chronological direction of the items. Potential values: past, future. Default value is 'past'.
 * @param  Function iterator  		function that returns a url to the page to scrape the list of items from. Api must be 1-Indexed.
 * @param  Number  stopAfter  	number of times iterator should request a feed. 
 * @return jQuery
 * @author Volkan Unsal
 */
	$(function(){			 
		//Shortcuts for options variables.
		var timesarrow 			= options.timesarrow,
			 bandwidth 			= options.bandwidth,
			 randomness			= options.randomness,
			 interval			= options.interval,
			 container			= options.container,
			 iterator			= options.iterator,
			 stopafter			= options.stopAfter;
			 
		//Initialize some important j objects.
		var $container 		= $(container);
		
		//Global vars
		var feedExists			= $.isFunction(options.iterator),
			 IsPast 			= (timesarrow == 'past') 	&& feedExists,
			 IsFuture 			= (timesarrow == 'future') && feedExists,
			 ClockIsSet			= false,
			 elapse,
			 pushBandwidth,
			 RecursiveTimeout;
			 
		//Create loader objects
		if(IsFuture){
			var futureLoader			= "futureLoader" + Math.random() * 10,
				futureLoaderSelector 	= "#" + futureLoader,				 
				$futureLoader 			= $(futureLoaderSelector);
				 
			$('<div />').attr('id', futureLoader).appendTo('body');
		}
		
		//If there is an interator, iterate and load for all values of the array
		if(feedExists){
			var feedStartIndex 	= options.feedStartIndex,
				feedCurrentIndex= feedStartIndex, //the point we are in the feed 
				feedLength 		= 0,
				batchLength		= 0,
				feedIterator 	= options.iterator,
				LessThanOneBatch;
				 
			
			
			setStyles();
			//Loads the next batch. When only one batch is left, loads another one.
			//When there are no more batches, zeroes out the iterator index.
			setTimeout(getFeed, 4000);
		}
		
		/*getFeed
		 *
		 *@return  status of the ajax request.
		 **/			
		function getFeed(){
			$.get(feedIterator(feedCurrentIndex++), function(data, textStatus){
				//Add the result to the container's list
				$items = $(data).find('li');
				
				//Appends to different places based on the direction of the roll.
				if(timesarrow == 'past'){
					$items.appendTo(container + ' ul', function(){setStyles();});
				}
				else{
					$items.appendTo(futureLoaderSelector);
				}	
				//Initialize the batchLength for one time
				batchLength === 0 ? batchLength = feedLength : '';				
				//Keeps track of the length of items in the feed
				feedLength += $items.length;

				//Diminish the stopafter if it is set
				if(stopafter !== undefined)
					stopafter--;
				
				
				
				//Checks for failure. Returns the request status.
				return textStatus;
			});
		}
		
			
		//Sets the ball in motion		
		setRecursiveTimeout();

	
		function setRecursiveTimeout(){
			setRandomVars();
			roll();
			setTimeout(setRecursiveTimeout, elapse);
			setStyles();
		}

		function setStyles(){
						
			//Stabilizes the height of the container element.
			$(function(){
				$container.css({height:$container.height(), overflow:'hidden'});
				$container.find(':first').css({marginTop:'1em'});
			});
			
		}
		
		
		/*setRandomVars
		 *
		 *@desc uses the randomization settings to set the specific bandwidth
		 *and elapse time.
		 **/
		function setRandomVars(){
			//Sets the new values for elapsed time and push bandwidth
			//Then calls the roll function.
			
			var rand = Math.random();
			
			if(randomness == 'strong'){
				rand 			= rand < .3 ? .3 : rand;
				elapse 		= interval * rand;
				pushBandwidth 	= bandwidth * rand;
			}
			if(randomness == 'weak'){
				elapse 		= interval * 1;
			}
			elapse 			= elapse 		 < 2500  ? 2500 : Math.floor(elapse);
			pushBandwidth  = pushBandwidth < 1 		? 1 	 : Math.floor(bandwidth * rand);
		}	
		
		
		
		/*roll
		 *
		 *@desc checks for the timesarrow. Then it rolls the list items in a specific
		 *direction. If the feed doesn't exist, then it uses a simple, cyclical
		 *rotator. In cases where the batch chronology is reversed.
		 *
		 **/
		function roll(){
			//j objects
			var $ContainerListItems = $container.find('li'),
				 index = 0;
			
			//initialize vars
			feedLength = $ContainerListItems.length;		
		
			if(stopafter > 0)
				LessThanOneBatch = (feedLength - batchLength) < batchLength;
			else{
				LessThanOneBatch = false;
				IsPast = IsFuture = false;
			}
			
			
			
			//reset the current index if the feed comes down to its last batch.
			(feedLength < batchLength) ? feedCurrentIndex = feedStartIndex: '';
			
			
			if(IsPast){
			/*past
				diminishes the height of the items while moving up the items
				below them. Finally removes the affected list items from the top of
				the batch.
			*/
				while(index < pushBandwidth){
               $ContainerListItems
						.eq(index)
						.animate({opacity:"0", height: 0}, 500, "swing", function(){
										jQuery(this).remove();
										
									});
						
					if(feedCurrentIndex)feedCurrentIndex++;
					index++;
				}
				
				
			}else if(IsFuture){
			/*future
				removes the loaded list items from the feedLoader and prepends them
				to the container. Then it shifts the same number of items from the
				bottom of the container.
			*/
			
				var $FutureLoaderListItems 	= $futureLoader.find('li');
				
				while(index < pushBandwidth){
					$FutureLoaderListItems 
						.eq(index)
						.css({opacity:0, display:'none'})
						.prependTo('#activity ul')
						.show('fast')
						.animate({opacity:"1"}, 500, "swing", function(){
								//This blocks removes the last item from
								//the container.
								$ContainerListItems
									.find(':last')
									.remove();
							});
					
					feedCurrentIndex++;	
					index++;
				}
			
			}else{
			//default
				$container
					.find('li:first')
					.animate({opacity:"0", height: 0}, 500, "swing", function(){
											$(this).css('display','none');
											
											$container
												.find('ul')
												.append($(this).css({display:'block', height:'auto', opacity:'1'}));
											setStyles();
										});
					
				/*	.parent()
					.animate({backgroundColor: 'yellow', opacity:'.8'},
							 {duration:500, complete: function(){$(this).animate({opacity:'.8',backgroundColor:'black'},{duration:500})}})
				*/
				/*This thing adds a fading background to the container element of the layer above.*/
								
			}
			
			if(LessThanOneBatch){				
				//Loads another batch. Reset the current index if server returns an error. 
				getFeed();				
			}
		}

	
	});
	return this;
};//end RandomRolld


	$(function(){
		
		$.RandomRolld({
						  container: 'div#activity',
						  interval: 3000,
						  randomness: 'weak',
						  bandwidth: 1,
						  timesarrow: 'past',
						  feedStartIndex: 2,
						  stopAfter:3,
						  iterator: function(index){ return '/ajax/activity/list/modified/desc/page' + index + '/';}
					  });
	});

})(jQuery);


