/**
* smartupdater - jQuery Plugin
*  
* Version - 4.0.beta 
* Copyright (c) 2010 - 2011 Vadim Kiryukhin
* vkiryukhin @ gmail.com
* 
* http://www.eslinstructor.net/smartupdater/
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
*
* USAGE:
*
*	$("#myObject").smartupdater({
*			url : "foo.php"
*			}, function (data) {
*				//process data here;
*			}
*		);
*		
*	Public functions:
*		$("#myObject").smartupdater("stop")
*		$("#myObject").smartupdater("restart");
*		$("#myObject").smartupdater("setTimeout",timeout);
*		$("#myObject").smartupdater("alterUrl"[,"foo.php"[,data]]); 
*		$("#myObject").smartupdater("alterCallback"[, foo]); 
*
*	Public Attributes:
*		var status  = $("#myObject").smartupdater("getState"); 
*		var timeout = $("#myObject").smartupdater("getTimeout");
*
**/

(function($) {


		var methods = {
		
			init : function( options, callback) {
				return this.each(function () {
					var elem = this,
					es = {};

					elem.settings = jQuery.extend(true,{
						url					: '',		// see jQuery.ajax for details
						type				: 'get', 	// see jQuery.ajax for details
						data				: '',   	// see jQuery.ajax for details
						dataType			: 'text', 	// see jQuery.ajax for details
								
						minTimeout			: 60000, 	// 1 minute
						maxFailedRequests 	: 10, 		// max. number of consecutive ajax failures
						maxFailedRequestsCb	: false, 	// falure callback function
						httpCache 			: false,	// http cache 
						rCallback			: false,	// remote callback functions
						selfStart			: true,		// start automatically after initializing
						smartStop			: { active:			false, 	//disabled by default
												monitorTimeout:	2500, 	// 2.5 seconds
												minHeight:		1,	  	// 1px
												minWidth:		1	  	// 1px	
											  } 

					}, options);
						
					elem.smartupdaterStatus = {state:'',timeout:0};

					es = elem.settings;
						
					es.prevContent 		= '';
					es.failedRequests	= 0;
					es.etag 			= '0';
					es.lastModified 	= '0';
					es.callback 		= callback;
					es.origReq = {url:es.url,data:es.data,callback:callback};
					es.stopFlag = false;
				
					
					function start() {
					
					/* check if element has been deleted and clean it up  */
						if(!$(elem).parents('body').length) {
								clearInterval(elem.smartupdaterStatus.smartStop);
								clearTimeout(elem.settings.h);
								elem = {};
								return;
						} 
					
						$.ajax({
							url		: es.url,
							type	: es.type,
							data	: es.data,
							dataType: es.dataType,
							cache	: false, // MUST be set to false to prevent IE caching issue.

							success: function (data, statusText, xhr) {
							
								var dataNotModified = false, 
									rCallback = false, 
									xSmart = jQuery.parseJSON(xhr.getResponseHeader("X-Smartupdater")),
									xhrEtag, xhrLM;
								
								if(xSmart) { // remote control 
								
									/* remote timeout */
									es.minTimeout = xSmart.timeout ? xSmart.timeout : es.minTimeout;
									
									/* remote callback */
									rCallback = xSmart.callback ? xSmart.callback : false;
								}
								
								if(es.httpCache) { // http cache process here
								
									xhrEtag = xhr.getResponseHeader("ETag");
									xhrLM = xhr.getResponseHeader("Last-Modified");
									
									dataNotModified = (es.etag ==  xhrEtag || es.lastModified == xhrLM) ? true : false;
									es.etag 		=  xhrEtag ? xhrEtag : es.etag;
									es.lastModified =  xhrLM   ? xhrLM   : es.lastModified;
								}
								
								if ( 	dataNotModified || 
										es.prevContent == xhr.responseText || 
										xhr.status == 304 ) { // data is not changed 
										
										if(!es.stopFlag) {
											clearTimeout(es.h);
											es.h = setTimeout(start, es.minTimeout);
										}
											
								} else { // data is changed 

								/* cache response data */
									es.prevContent = xhr.responseText;
									
								/* reset timeout */
									if(!es.stopFlag) {
										clearTimeout(es.h);
										es.h = setTimeout(start, es.minTimeout);
									}
									
								/* run callback function */
									if(es.rCallback && rCallback && es.rCallback.search(rCallback) != -1) {
										window[rCallback](data);
									} else  { 
										es.callback(data);
									}
								}
								
								elem.smartupdaterStatus.timeout = es.minTimeout;
								es.failedRequests = 0;
							}, 
									
							error: function(xhr, textStatus, errorThrown) { 
								if ( ++es.failedRequests < es.maxFailedRequests ) {
								
								/* increment falure counter and reset timeout */
									if(!es.stopFlag) {
										clearTimeout(es.h);
										es.h = setTimeout(start, es.minTimeout);
										elem.smartupdaterStatus.timeout = es.minTimeout;
									}
									
								} else {
								
								/* stop smartupdater */
									clearTimeout(es.h);
									elem.smartupdaterStatus.state = 'OFF';
									if( typeof(es.maxFailedRequestsCb)==='function') {
										es.maxFailedRequestsCb(xhr, textStatus, errorThrown);
									}
								}
							},
							
							beforeSend: function(xhr, settings) {
							
								if(es.httpCache) {
								
								/* set http cache-related headers */
									xhr.setRequestHeader("If-None-Match", es.etag );
									xhr.setRequestHeader("If-Modified-Since", es.lastModified );
								}
								
							/* Feedback: Smartupdater sends it's current timeout to server */
								xhr.setRequestHeader("X-Smartupdater", '{"timeout":"'+elem.smartupdaterStatus.timeout+'"}');
							}
						});
						
						elem.smartupdaterStatus.state = 'ON';
					} 
						
					es.fnStart = start;
					
					if(es.selfStart) {
						start();
					}
					
					if(es.smartStop.active) {
					
						elem.smartupdaterStatus.smartStop = setInterval(function(){

							// check if object has been deleted 
							if(!$(elem).parents('body').length) {
								clearInterval(elem.smartupdaterStatus.smartStop);
								clearTimeout(elem.settings.h);
								elem = {};
								return;
							} 

							var $elem = $(elem);
							var width =  $elem.width(),
								height = $elem.height(),
								hidden = $elem.is(":hidden");
								
							//element has been expanded, so smartupdater should be re-started.
							if(!hidden && height > es.smartStop.minHeight && width > es.smartStop.minWidth 
								 && elem.smartupdaterStatus.state=="OFF") {
								$elem.smartupdater("restart");
							} else 	
							//element has been minimized, so smartupdater should be stopped.
							if( (hidden || height <= es.smartStop.minHeight || width <= es.smartStop.minWidth) 
									&& elem.smartupdaterStatus.state=="ON") {
								$elem.smartupdater("stop");

							} 
							
						},es.smartStop.monitorTimeout);
					}
					
				});

			},// init()
			
			stop : function () {
				return this.each(function () {
					this.settings.stopFlag = true;
					clearTimeout(this.settings.h);
					this.smartupdaterStatus.state = 'OFF';
				});
			}, 
			
			restart : function () {        
				return this.each(function () {
					this.settings.stopFlag = false;
					clearTimeout(this.settings.h);
					this.settings.failedRequests = 0;
					this.settings.etag = "0";
					this.settings.lastModified = "0";
					this.settings.fnStart();
				})
			},
			
			setTimeout : function (period) {
				return this.each(function () {
					clearTimeout(this.settings.h);
					this.settings.minTimeout = period;
					this.settings.fnStart();
				});
			}, 
			
			alterCallback : function (callback) {
				return this.each(function () {
					this.settings.callback  = callback	? callback 	: this.settings.origReq.callback;
				});
			}, 
			
			alterUrl : function (url,data) {
				return this.each(function () {
					this.settings.url 	= url 	? url : this.settings.origReq.url;
					this.settings.data	= data 	? data : this.settings.origReq.data;
				});
			},
			
			getTimeout : function () {
				return this[0].smartupdaterStatus.timeout;
			},
			
			getState : function () {
				return this[0].smartupdaterStatus.state;
			}
			
		}; //methods
		
	jQuery.fn.smartupdater = function (options, callback) {	
	
		if ( methods[options] ) {
			return methods[ options ].apply( this, Array.prototype.slice.call( arguments, 1 ));
		} else if ( typeof options === 'object' || ! method ) {
			return methods.init.apply( this, arguments );
		} else {
			$.error( 'Method ' +  options + ' does not exist on jQuery.tooltip' );
		}
	}; 

	
})(jQuery);

