/**
 *
 * This is the central core of the bookmarking/histroy state
 * It is based directly off of Kevin Newman's unfocus History Keeper, but
 * Modified to allow for what we needed.
 *
 * http://www.unfocus.com/Projects/
 *
 * @author: Michael Sablone
 * @version: 1.1
 *
 */

if (typeof itdr == "undefined") var itdr = new Object();
if (typeof itdr.classes == "undefined") itdr.classes = new Object();
if (typeof itdr.utilities == "undefined") itdr.utilities = new Object();

/**
 *
 * EventManager Class
 *
 */
itdr.classes.Broadcaster = function () {
	this._listeners = {};
}
itdr.classes.Broadcaster.prototype = {
	addEventListener: function(type, listener) {
		if (!this._listeners[type]) this._listeners[type] = new Array();
		for (var i=0; i<this._listeners[type].length; i++) if (this._listeners[type][i] == listener) return;
		this._listeners[type].push(listener);
	},
	removeEventListener: function(type, listener) {
		if (!this._listeners[type]) return;
		for (var i=0; i<this._listeners[type].length; i++) {
			if (this._listeners[type][i] == listener) {
				this._listeners.splice(i,1);
				return;
			}
		}
	},
	broadcastMessage: function(type, data) {
		if (!this._listeners[type]) return;
		for (var i=0; i<this._listeners[type].length; i++) this._listeners[type][i](data);
	}
}

/**
 *
 * Browser Class
 *
 */
itdr.utilities.Browser = (function () {
	var w = window;
	var d = w.document;
	var n = navigator;
	var ua = n.userAgent.toLowerCase();
	var obj = new Object();
	var undef;
	obj.ie = w.VBArray!=undef;
	obj.ie5 = (obj.ie && (!d.createEventObject || !d.namespaces));
	obj.ie55 = (obj.ie && !d.implementation);
	obj.ie6 = (obj.ie && d.implementation!=undef && !w.XMLHttpRequest);
	obj.ie7 = (obj.ie && w.XMLHttpRequest!=undef);
	obj.opera = w.opera!=undef;
	obj.gecko = (w.netscape!=undef && !this.opera);
	obj.khtml = (/safari/.test(ua) || /konqueror/.test(ua));
	obj.safari3 = (obj.khtml && w.devicePixelRatio!=undef);
	obj.firefox3 = n.oscpu && d.getElementsByClassName;
	return obj;
})();

/**
 *
 * EventManager Class
 *
 */
itdr.classes.Unfocus = (function() {
	/**
	 * use closere to emulate a singleton
	 */
	function Constructor() {
		/**
		 * private properties
		 */
		var undef;
		var self = this;
		var interval = 200;
		var current = getHash();
		var watcher = setInterval(checkHash, interval);
		/**
		 * private getHash
		 *
		 * @returns String
		 *	a string containing the current hash from the url
		 */
		function getHash () {
			return location.hash.substring(1);
		};
		/**
		 * private _getHash
		 *
		 * @param str:String
		 *	a string that sets the Hash on the location string (the current url).
		 */
		function setHash (str) {
			window.location.hash = str;
		};
		/**
		 * private checkHash
		 * A private method that is called every n miliseconds to check if the hash has changed.
		 * This is the primary Hash change detection method for most browsers. It doesn't work to detect the hash
		 * change in IE 5.5+ or various other browsers.
		 *
		 * @see interval
		 */
		function checkHash () {
			var hash = getHash();
			if (current!=hash) {
				current = hash;
				self.broadcastMessage("onHistory", hash);
			}
		};
		/**
		 * private addHistory
		 * A public method to retrieve the current history string
		 *
		 * @param str:String
		 * 	The has to get the current History Hash
		 */
		function addHistory(hash) {
			if (current!=hash) {
				current = hash;
				setHash(hash);
				self.broadcastMessage("onHistory", hash);
			}
		};
		/**
		 * public addHistory
		 * A public method to retrieve the current history string
		 *
		 * @param str:String
		 * 	The has to get the current History Hash
		 */
		this.addHistory = function (hash) {
			addHistory(hash);
		};
		/**
		 * public getCurrent
		 * A public method to retrieve the current history string
		 *
		 * @returns String
		 * 	The current History Hash
		 */
		this.getCurrent = function () {
			return current;
		};
		/**
		 * These are the platform specific override methods. Since some platforms (IE 5.5+, Safari)
		 * require almost completely different techniques to create history entries, browser detection is
		 * used and the appropriate method is created. The bugs these fixes address are very tied to the
		 * specific implementations of these browsers, and not necessarily the underlying html engines.
		 * Sometimes, bugs related to history management can be tied even to a specific skin in browsers
		 * like Opera.
		 */
		var Browser = itdr.utilities.Browser;
		if (Browser.khtml==true&&Browser.safari3==false) { // Safari < 3
			// safari specific variables
			var safari_count = history.length;
			var safari_states = new Object();
			var safari_recent = false;
			var safari_form;
			var original_checkHash = checkHash;
			// set the initial state
			safari_states[safari_count] = current;
			// overwrite the private check hash method
			checkHash = function () {
				if (!safari_recent) {
					var count = history.length;
					if (count!=safari_count) {
						safari_count = count;
						original_checkHash();
					}
				}
			};
			// overwrite the private set hash method
			setHash = function (hash) {
				safari_states[safari_count] = hash;
				safari_form.action = "#" + getHash();
				safari_form.submit();
			};
			// overwrite the private get hash method
			getHash = function () {
				return safari_states[safari_count];
			};
			// private function to create the form
			function create_safari_form () {
				safari_form = document.createElement("form");
				safari_form.id = "itdr_classes_historykeeper_safari_history_form";
				safari_form.method = "get";
				var query = window.location.search.substring(1);
				var pairs = query.split("&");
				for (var i=0; i<pairs.length; ++i) {
					var pair = pairs[i].split("=");
					var input = document.createElement("input");
					input.type = "hidden";
					input.name = pair.shift();
					input.value = pair.join("=");
					safari_form.appendChild(input);
				}
				document.body.insertBefore(safari_form, document.body.firstChild);
			};
			// overwrite the public add history method
			this.addHistory = function (hash) {
				// on the first call, create the form
				// and overwrite the method
				create_safari_form();
				self.addHistory = function(hash) {
					if (current!=hash) {
						current = hash;
						safari_count = history.length+1;
						safari_recent = true;
						setHash(hash);
						self.broadcastMessage("onHistory", hash);
						safari_recent = false;
					}
				};
				self.addHistory();
			};
		} else if (Browser.ie==true&&Browser.ie5==false) { // IE 5.5+ Windows
			// ie specific variables
			var ie_frame_object;
			var ie_frame_reference;
			// create an event function
			function create_ie_frame () {
				var name = "itdr_classes_historykeeper_ie_history_frame";
				ie_frame_object = document.createElement("iframe");
				ie_frame_object.setAttribute("name", name);
				ie_frame_object.setAttribute("id", name);
				ie_frame_object.setAttribute("src", 'javascript:;');
				ie_frame_object.style.position = "absolute";
				ie_frame_object.style.top = "-900px";
				document.body.insertBefore(ie_frame_object, document.body.firstChild);
				ie_frame_reference = frames[name];
				create_ie_frame_html(current);
			}
			function create_ie_frame_html (hash) {
				with (ie_frame_reference.document) {
					open("text/html");
					write("<html><head></head><body onl", 'oad="parent.itdr.classes.Unfocus.ie_update_history(\'' + hash + '\');">', hash + "</body></html>");
					close();
				}
			}
			function onHistory (hash) {
				setHash(hash);
			};
			// listen for our own events
			this.addEventListener("onHistory", onHistory);
			// create a public method for the virtual frame to call
			this.ie_update_history = function (hash) {
				// ignore the first call by overwriteing the method
				self.ie_update_history = function(hash) {
					current = hash;
					self.broadcastMessage("onHistory", hash);
				};
			}
			// overwrite the public add history method
			this.addHistory = function (hash) {
				// on the first call, create the frame
				// and overwrite the method
				create_ie_frame();
				self.addHistory = function (hash) {
					if (current!=hash) {
						current = hash;
						create_ie_frame_html(hash);
					}
				};
				self.addHistory(hash);
			};
		}
	}
	// inherit from the Broadcaster
	Constructor.prototype = new itdr.classes.Broadcaster();
	return new Constructor();
})();
