/**
 * @project talenthouse
 * @since   07.05.2009
 *
 * Data Functions for ajax/json requests:
 * 
 * asyncQueue / asyncQueueExecute:
 *		"asyncQueue" is used primarily in the 'Create' process to send background requests to the 
 *		server, without interrupting the user experience. 
 *
 *		An element li.saved is displayed while requests are active (the loading animation)
 *
 *
 * jsonLoad
 * 		Wrapper for jQuery ajax json data load. Used to load the project list on the create page
 * 
 * ajaxLoad
 * 		Wrapper for jQuery ajax data load 
 * 		- used extensively as it is the core fo the AJAX panel load implementation in the site
 * 		- executes several callback scripts after completion  
 * 
 * transitionLoad
 * 		Used for Explore and Artist overview pages' Invites panel, which automatically cycles through 
 * 		it's elements. Originally this had a fade out/fade in transition, but Firefox problems caused Flash 
 * 		to flicker so the effect was disabled. Has some caching of data (holds HTML in a JS array). 
 * 		
 * executeOnLoadCallbacks
 * 		This is called each time ajaxLoad completes, allowing us to apply JS bindings and effects to newly 
 * 		injected HTML. 
 * 		
 * xdLoad
 * 		This is used to retrieve data from S3, on a different domain to the core site. The only way to load
 * 		data in AJAX from another domain is via JSONP - where the file contains a function call - this 
 * 		function initiates the request for the file, whereas xdProcessData handles the response.
 * 
 * 		Used (among other places) to retrieve descriptions in the Creative Profile Edit section of Account Settings.
 * 
 * submitForm
 * 		Submits a form using the jQuery Form plugin (http://malsup.com/jquery/form/)
 * 
 */

th.ajax = {

	preloadLinkClass: 'thpreload',
	onLoadCallbacks: [],
	xdLoadTargets: [],
	ajaxQueueData: [],
	ajaxQueueUpdating: false,
	
	
	/**
	 * @method	asyncQueue
	 * @package	th.ajax	
	 * 
	 * used primarily in the 'Create' process to send background requests to the 
	 * server, without interrupting the user experience. 
 	 *
	 */
	asyncQueue: function( _key, _url, _data, _inputparams ) 
	{
		// parameter test:
		if ( typeof _key == 'undefined' 
				|| typeof _url == 'undefined' 
				|| typeof _data == 'undefined' 
				|| _key == '' || _url == '' || _data == null || _data == ''
			)
		{
			th.alert('Key, Url and data are required parameters for th.ajax.asyncQueue');
		}

		// Set default $.ajax parameters
		var params = {
			type: 'POST',
		    url: _url,
		    data: _data,
		    port: _key,
		    mode: 'abort',
		    complete:
				function() {
					th.ajax.ajaxQueueUpdating = false; 
					th.ajax.asyncQueueExecute();
				}
		};
		// Extend with input parameters
		var ajaxParams = $.extend(params, _inputparams || {});
		
		// Custom version of abort; remove any earlier requests to the same URL: 
		// For the 'Create' pages this makes sense, so that if several requests are 
		// made to reorder content, only the last request is delivered.  
		for (var i=0; i<th.ajax.ajaxQueueData.length; i++)
		{
			// remove any earlier entries pointing to the same url (don't queue 10 requests for 'order')
			if (th.ajax.ajaxQueueData[i].port == _key)
			{
				th.ajax.ajaxQueueData.splice(i, 1);
				break;
			}
		}
		
		// push it into the queue
		th.ajax.ajaxQueueData.push( ajaxParams );

		// Show In-Progress/Loading animation (hard coded selector = naughty!) 
		$('li.saving').css('display', 'block');
		
		// Execute update (setTimeout is used to prevent it blocking the UI action that initiated this)
		setTimeout( function(){ th.ajax.asyncQueueExecute(); },200);
	},
	
	
	/**
	 * @method	asyncQueueExecute
	 * @package	th.ajax	
	 * 
	 * Exec function for queued updates; runs the script and kills the entry from the queue 
 	 *
	 */
	asyncQueueExecute: function()
	{
		if ( th.ajax.ajaxQueueData.length > 0 ) 
		{ 
			// Synchronous operation:
			if ( th.ajax.ajaxQueueUpdating == false) 
			{
				th.ajax.ajaxQueueUpdating = true;
				
				// Get & remove first entry:
				var qParams = th.ajax.ajaxQueueData[0];
				th.ajax.ajaxQueueData.splice(0,1);
	
				$.ajax( qParams );
			}

			/*
			// Send immediately (asynchronous)
			while ( th.ajax.ajaxQueueData.length > 0 ) {
				var qParams = th.ajax.ajaxQueueData[0];
				th.ajax.ajaxQueueData.splice(0,1);
				$.ajax( qParams );
			}
			*/
			
		} else {
			// Nothing to do; hide animation
			$('li.saving').css('display', 'none');
		}
		
	},

	
	
	
	
	
	
	/**
	 * @method	asyncQueueExecute
	 * @package	th.ajax	
	 * 
	 * Wrapper for jQuery ajax json data load. Used to load the project list on the create page
 	 *
	 */
	jsonLoad: function( params , loadingContainerId ) {

		var defaults = {
			type: 'POST',
		    dataType: 'json',
		    url: '',
		    data: {},
		    beforeSend: function() {
		    		if (typeof loadingContainerId != 'undefined') {
		    			$(loadingContainerId).html("");
		    			$(loadingContainerId).addClass(th.ajax.preloadLinkClass);
		    			th.ui.loadingAnimation.start( loadingContainerId, false, false );
			    	}
			    },
		    complete: function(){
					if (typeof loadingContainerId != 'undefined') {
						$(loadingContainerId).removeClass(th.ajax.preloadLinkClass);
						th.ui.loadingAnimation.stop();
						th.ajax.executeOnLoadCallbacks();
					}
					
				},
		    success:  function() { },
		    error: function() { }
		};
		
		ajaxParams = $.extend(defaults, params || {});
		
		$.ajax( ajaxParams );
	},
	

	
	

	/**
	 * @method	asyncQueueExecute
	 * @package	th.ajax	
	 * 
	 * Wrapper for jQuery ajax data load 
	 * 		- used extensively as it is the core fo the AJAX panel load implementation in the site
	 * 		- executes several callback scripts after completion  
 	 *
	 */
	ajaxLoad: function ( _path , loadingContainerId , preload , callback , params ) 
	{
		try
		{
			var path = _path;
			
			// parameter test:
			if ( typeof path == 'undefined' 
					|| typeof loadingContainerId == 'undefined' 
					|| path == '' 
					|| loadingContainerId == ''
				)
			{
				throw('Path and target are required parameters for th.ajax.ajaxLoad!');
			}
			
			// Displays the loading animation
			if ( preload != false ) {
				$(loadingContainerId).addClass(this.preloadLinkClass);
				$(loadingContainerId).css('display','block');
				th.ui.loadingAnimation.start( loadingContainerId, false, false );
			}
	
			
			// We can only inject HTML in 1 container; this ensures we are inserting into
			// the first, visible occurrence of the loadingContainer selector class
			if ($(loadingContainerId+'').length > 1)
			{
				if ( $(loadingContainerId + ':reallyvisible').length > 0 ) {
					loadingContainerId = loadingContainerId + ':reallyvisible';
				}
	
				// if still > 1, get the first instance
				if ( $(loadingContainerId).length > 1 ) {
					loadingContainerId = loadingContainerId + ':first';
				}
			}
			
			var defaults = {
					// MODE and PORT are used by ajaxQueue plugin ... when a request is made 
					// any existing, in progress, requests with the same 'port' value are 
					// aborted.  
					mode: 'abort',
					port: loadingContainerId,
					//port: 'ajaxLoad',
					type: 'POST',
					url: path,
					success: function(html) {
						// Remove Preload Class
						$(loadingContainerId).removeClass(th.ajax.preloadLinkClass);
						th.ui.loadingAnimation.stop();
						// th.ui.loadingAnimation.stop();
						$(loadingContainerId).html(html);
					},
					error: function(html) {
						// Remove Preload Class
						$(loadingContainerId).removeClass(th.ajax.preloadLinkClass);
						th.ui.loadingAnimation.stop();
					},
					complete: function(xhr, status) {
						
						// Save path
						th.ui.panels.markPanelLoaded( path, loadingContainerId );
						
						th.ajax.executeOnLoadCallbacks();
						
						if (typeof callback == 'function') {
							callback(xhr);
						}
						
						// Attempt Google Log
						// 
						// XXXRobert useless if clause: 
						// if pageTracker is or is not defined or null is not important, 
						// because pageTracker will be defined in ga_trackpageview
						try {
							
							th.ga.trackPageview(path);
							
							//if ( typeof pageTracker != 'undefined' && pageTracker != null ) {
								//ga_trackpageview(path);
							//}
						}catch (e) { 
							th.log('th.ga.trackPageview(' + path + '); failed: ' + e.toString()); 
						};
					}
			
			};
			
			// Extend defaults with submitted overrides
			ajaxParams = $.extend(defaults, params || {});
			
			// Call ajax
			$.ajax( ajaxParams );
		
		} finally {
			//th.ui.loadingAnimation.stop();
			return false;
		}

	},
	
	
	
	/**
	 * @method	transitionLoad
	 * @package	th.ajax	
	 * 
	 * 		Used for Explore and Artist overview pages' Invites panel, which automatically cycles through 
	 * 		it's elements. Originally this had a fade out/fade in transition, but Firefox problems caused Flash 
	 * 		to flicker so the effect was disabled. Has some caching of data (holds HTML in a JS array).
	 * 
	 */
	transitionLoadCache: [],
	transitionLoad: function ( _url , _target , _intOut, _intIn , _cacheLimit , loadingTarget ) 
	{
		var url = _url; 
		var target = _target; 
		var intOut = _intOut; 
		var intIn = _intIn;
		var cacheLimit = _cacheLimit;
		
		try
		{
			// How full is the cache now 
			var keyCount = th.ajax.transitionLoadCacheKeyCount(target);
			
			// On first load, save the html that is already there
			if ( keyCount == 0 ) {
				th.ajax.transitionLoadCacheAdd( '#start#' , target, $(target).html(), cacheLimit );
			}
			
			// If cache enabled: 
			if ( typeof cacheLimit != 'undefined' && cacheLimit > 1 )
			{
				// Get html if there is a cache hit:
				var html = th.ajax.transitionLoadCacheHit( url, target );
				if ( html !== false ) {
					
					/*
					$(target).fadeOut( intOut, function() {
						$(target).html(html);
						$(target).fadeIn( intIn );
					});
					*/
					
					$(target).html(html);
					return true;

				} else {
					
					if ( keyCount >= cacheLimit ) {
					
						// Cache at capacity - go back to first entry: ! HALTS EXECUTION (we do not have cached script)
						var html = th.ajax.transitionLoadCacheFirstItem( target );
						if ( html !== false ) {
					
							/*
							$(target).fadeOut( intOut, function() {
								$(target).html(html);
								$(target).fadeIn( intIn );
							});
							*/
							
							$(target).html(html);
							return true;
						}
					}
				}
			}
			
			// Show target:
			$(loadingTarget).css('display','block');
			
			
			// Default ajax params:
			var params = {
					port: target,
					mode: 'abort',
					type: 'POST',
					url: url,
					success: function(html) {
						/*
						$(target).fadeOut( intOut, function() {
							th.ajax.transitionLoadCacheAdd(  url, target, html, cacheLimit );
							$(loadingTarget).hide();
							$(target).html(html);
							$(target).fadeIn( intIn );
						});
						*/

						th.ajax.transitionLoadCacheAdd(  url, target, html, cacheLimit );
						$(loadingTarget).hide();
						$(target).html(html);
					}
			};

			$.ajax( params );
		
		} finally {
			//th.ui.loadingAnimation.stop();
			return false;
		}

		return true;
	},

	/**
	 * @method	transitionLoadCacheHit
	 * @package	th.ajax	
	 * 
	 * 	Test if we have HTML cached for url & target; if so, return it, else false
	 */ 
	transitionLoadCacheHit: function( url , target ) 
	{
		for ( var i = 0; i < th.ajax.transitionLoadCache.length; i++) 
		{
			if ( th.ajax.transitionLoadCache[i].url == url && th.ajax.transitionLoadCache[i].target == target ) 
			{
				if ( th.ajax.transitionLoadCache[i].html != '') {
					return th.ajax.transitionLoadCache[i].html;
				}
			}
	    };
		
		return false;
	},
	
	/**
	 * @method	transitionLoadCacheFirstItem
	 * @package	th.ajax	
	 * 
	 * 	Inserts current target contents into the cache as the 'init' content
	 */ 
	transitionLoadCacheFirstItem: function( target ) {
		for ( var i = 0; i < th.ajax.transitionLoadCache.length; i++) 
		{
			if ( th.ajax.transitionLoadCache[i].target == target ) 
			{
				if ( th.ajax.transitionLoadCache[i].html != '') {
					return th.ajax.transitionLoadCache[i].html;
				}
			}
	    };
		
		return false;
	},
	
	transitionLoadCacheAdd: function( url , target, html, cacheLimit ) 
	{
		var keyCount = th.ajax.transitionLoadCacheKeyCount(target);
		
	    th.ajax.transitionLoadCache.push( { 'url': url, 'target': target, 'html': html } );
	    
	    if ( keyCount > cacheLimit ) {
	    	for ( var i = 0; i < th.ajax.transitionLoadCache.length; i++) 
			{
				if ( th.ajax.transitionLoadCache[i].target == target )  {
					th.ajax.transitionLoadCache.splice(i,1);
					return true;
				}
		    };
	    }
		
		return false;
	},
	
	transitionLoadCacheKeyCount: function( target ) 
	{
		var keyCount = 0;
		for ( var i = 0; i < th.ajax.transitionLoadCache.length; i++) 
		{
			if ( th.ajax.transitionLoadCache[i].target == target )  {
				keyCount++;
			}
	    };
	    return keyCount;
	},

	executeOnLoadCallbacks: function() 
	{
		// Execute global callbacks 
		for (var i = 0; i < th.ajax.onLoadCallbacks.length; i++) 
		{
			th.ajax.onLoadCallbacks[i]();
		}
	},

	addOnLoadCallback: function(callback, key) 
	{
		// Execute global callbacks 
		for (var i = 0; i < th.ajax.onLoadCallbacks.length; i++) 
		{
			if ( th.ajax.onLoadCallbacks[i] == callback )
			{
				return;
			}
		}

		th.ajax.onLoadCallbacks.push(callback);
	},
	
	
	
	/**
	 * @project talenthouse
	 * @package th.ajax.xdLoad
	 * @since   07.05.2009
	 * 
	 * @param string 	$_path			The url to retrieve data (HTTP GET only)
	 * @param string 	$_containerId	The jQuery ID where the response data will be shown
	 * @param string	$_uid			The UID matching the response data UID
	 * @param boolean	$_preload		Display loading animation (or not)
	 * @param function	$_callback		Callback function for after data has loaded
	 * 
	 * AJAX data load, using JSONP to enable cross-domain loading. 
	 * 
	 * Remote data (at the end of the _path url) must return a call to 
	 * the function 'th.ajax.xdProcessData' with data as follows: 
	 * 
	 	th.ajax.xdProcessData({
	 		uid:'unique_key_for_this_data',		// used as key to post parameters
	 		data: 'Html/script data for the target container goes in here'
	 	});
	 *
	 *
	 * This initiates a call to the response handler (th.ajax.xdProcessData) and 
	 * the data is inserted into the requested container
	 * 
	 */
	xdLoad: function ( _path , _containerId , _uid , _preload , _callback ) 
	{
		// Save Parameters for retrieval later in JSONP (.xdProcessData) callback
		th.ajax.xdLoadTargets.push({
				uid: _uid,
				containerId: _containerId,
				callback: _callback
			});
		
		// Show preload animation?
		if ( _preload != false ) {
			$(_containerId).css('display','block');
			$(_containerId).html("");
			$(_containerId).addClass(this.preloadLinkClass);
			th.ui.loadingAnimation.start( _containerId, false, false );
		}
		

		// Make the request; GET requests only are allowed (no POST data)
		$.ajax( {
			port: _containerId,
			mode: 'abort',
			type: 'GET',
		    dataType: 'jsonp',
		    url: _path ,
		    data: {},
		    error: function() { }
		});

	},
	
	
	
	/**
	 * @project talenthouse
	 * @package th.ajax.xdProcessData
	 * @since   07.05.2009
	 * 
	 * @param string	$x		JSON data returned by JSONP callback 
	 * 
	 * Callback function for responses initiated in the th.ajax.xdLoad function 
	 * above. 
	 * 
	 * Parameters cached earlier (ahead of the request) are retrieved and  
	 * used for data processing
	 * 
	 */
	xdProcessData: function(x) 
	{
		tragetData = null;
		
		// Retrieve target container and callback parameters
		for (var i = 0; i < th.ajax.xdLoadTargets.length; i++)
		{
			if (th.ajax.xdLoadTargets[i].uid == x.uid)
			{
				targetData = th.ajax.xdLoadTargets[i];
				th.ajax.xdLoadTargets.splice(i,1);
				break;
			}
		}

		if (targetData)
		{
			// Remove Preload Class (if it exists)
			$(targetData.containerId).removeClass(th.ajax.preloadLinkClass);
			th.ui.loadingAnimation.stop();
			
			// Load response HTML to target panel/container 
			$(targetData.containerId).html(unescape(x.data));
			$('textarea' + targetData.containerId + ',input' + targetData.containerId).val(unescape(x.data));
			
			// Execute global callbacks
			th.ajax.executeOnLoadCallbacks();
		
			// if callback passed; execute it
			if (typeof targetData.callback == 'function') {
				targetData.callback();
			}
		}
		
	},
	


	/**
	 * @project talenthouse
	 * @package th.ajax
	 * @method	xdProcessComplete
	 * 
	 * On complete, kill loading animations; looks to be unused
	 * 
	 */
	xdProcessComplete: function() 
	{

		var targetData = th.ajax.xdLoadTargets;
		if ( typeof targetData != 'undefined' )
		{
		    // Remove Preload Class (if it exists)
		    $(targetData.containerId).removeClass(th.ajax.preloadLinkClass);
		    th.ui.loadingAnimation.stop();

		}
	},
	
	
	
	
	/**
	 * @project talenthouse
	 * @package th.ajax
	 * @method	submitForm
	 * 
	 * Simple form submit wrapper for jQuery Forms plugin
	 * 
	 */
	submitForm: function(id , target, url)
	{
		var options = 
		{
			target: 	'#' + target,
			success:	th.ajax.executeOnLoadCallbacks(),
			url:		url,
			type:		'post'
		};

		$('#' + id ).ajaxForm();
		$('#' + id ).ajaxSubmit(options);
		return false;
		
	}
	
};



