(function($) {

	/*
	 * dfHistory, making ajax apps history-friendly
	 *
	 * Copyright (c) 2009 xNephilimx (Luciano Longo)
	 * 
	 * Licensed under the GNU GPL license.
	 * http://www.developersfactory.com.ar/license
	 *
	 */
	
function dfHistory() {
	
	var _hash = location.hash;
	var _listeners = [];
	
	var _ie_iframe;
	
	var _benchmark_data = {};
	var _bm_listener;
	
	var _last_exec;
	var _longest_exec = 0;
	
	//if the browser is IE
	if( $.browser.msie ) {
		//create a hidden iframe;
		$(function() {
			var new_hash = location.hash != '' ? location.hash : '';
			_ie_iframe = $('<iframe src="blank.html'+new_hash+'" id="dfhistory-frame" style="display:none;" />')
				.prependTo(document.body);
			
			//make all links that point to a hash, change the src attribute of the iframe
			$('a[href*="#"]').live('click', function(e) {
				$(this).attr('href').match(/\#(.*)$/i);
				_hash = RegExp.$1;
				
				var idoc = $('#dfhistory-frame')[0].contentWindow.document;
				idoc.open();
				idoc.close();
				idoc.location.hash = _hash;
				
				call_listeners( '#'+_hash );
			});
		});
	}
	
	/**
	 * Start dfHistory
	 * 
	 * This function must be the last one called, after all the listeners are added.
	 */
	this.start = function() {
		call_listeners( _hash );
		setInterval( observeHash, 20 );
		
		_benchmark_data.start_time = new Date().getTime();
		_benchmark_data.end_time = _benchmark_data.start_time + ( 1000 * 60 * $.dfHistory.benchmark.duration );
	};
	
	/**
	 * Sets the callback function for benchmarking
	 * 
	 * If no callback is specified (wether calling this function
	 * with an invalid parameter, or not calling it at all), no
	 * benchmarking will be performed.
	 * 
	 * @param function fn
	 * @return
	 */
	this.benchmarkListener = function( fn ) {
		if( typeof fn != 'function' ) return false;
		_bm_listener = fn;
	}
	
	/**
	 * Adds a hash listener
	 * 
	 * At least one listener must be added, otherwise
	 * it makes no sense to track the hash!
	 * 
	 * @param function fn
	 * @return int The listener ID
	 */
	this.addListener = function( fn ) {
		if( typeof fn != 'function' ) return false;
		
		return _listeners.push( fn ) - 1;
	};
	
	var observeHash = function() {
		
		/* benchmarking and optimizing 
		 ***********************************/
		
		_last_exec = _last_exec ? _last_exec : new Date().getTime();
		var curr_exec = new Date().getTime();
		
		if( curr_exec - _last_exec > 20 ) {
			if( curr_exec - _last_exec > _longest_exec ) _longest_exec = curr_exec - _last_exec;
			_last_exec = curr_exec;
			//return;
		}
		
		//run benchmark
		if( typeof _bm_listener == 'function' ) {
			_benchmark_data.last_exec = _last_exec;
			_benchmark_data.curr_exec = curr_exec - _last_exec;
			_benchmark_data.longest_exec = _longest_exec;
			
			if( _benchmark_data.end_time > new Date().getTime() )
				_bm_listener( _benchmark_data );
		}
		
		
		/* here starts the important part
		 ***********************************/
		
		if( $.browser.msie ) {
			
			var frame_hash = $('#dfhistory-frame')[0].contentWindow.document.location.hash;
			
			if( frame_hash != location.hash ) {
				_hash = frame_hash;
				location.hash = _hash;
				if( frame_hash == '' ) {
					location.hash = '';
					$('#dfhistory-frame')[0].contentWindow.document.location.hash = '';
				}
				
				call_listeners( _hash );
			}
			
		} else {
			var curr_hash = location.hash;
			
			if( curr_hash != _hash ) {
				_hash = curr_hash;
				call_listeners( _hash );
			}
		}
	};
	
	var call_listeners = function( new_hash ) {		
		$.each(_listeners, function( i, fn ) {
			fn( new_hash.substr(1) );
		});
	}
	
};

$.dfHistory = new dfHistory();
$.dfHistory.version = '0.9b.20090202';
$.dfHistory.benchmark = {
		duration:60
};

})(jQuery);
