
/* these scripts add functionality to seriously old browsers.
	don't even bother asking where i got this stuff, i dont think anyone needs it anymore, but hey, it's here
	*/
if (!Function.apply) Function.prototype.apply = function(thisArg, argArray) {
	thisArg.____apply = this;
	var fnApplyString = "thisArg.____apply(";
	if (typeof(argArray) == "object" && typeof(argArray.length) != "undefined") {
		var quote, comma;
		for(i = 0; i < argArray.length; i++){
			quote = typeof(argArray[i]) == "string" ? "\"" : "";
			comma = (i+1) < argArray.length ? "," : "";
			fnApplyString += quote + argArray[i] + quote + comma;
		}
	} else if (typeof(argArray) != "undefined") {
		throw TypeError;
	}
	fnApplyString += ")";
	var retVal = eval(fnApplyString);
	delete thisArg.____apply;
	return retVal;
};
if (![].push) Array.prototype.push = function() {
	for(var i=0; i<arguments.length; i++) this[this.length-1] = arguments[i];
	return this.length;
};
if (![].pop) Array.prototype.pop = function() {
	var $item = this[this.length-1];
	this.length--;
	return $item;
};
if (![].shift) Array.prototype.shift = function () {
	return this.splice(0, 1)[0];
};
if (![].splice) Array.prototype.splice = function (s, d) {
	var max = Math.max, min = Math.min, a = [], e, i = max(arguments.length - 2, 0), k = 0, l = this.length, n, v, x;
	s = s || 0;
	if (s < 0) s += l;
	s = max(min(s, l), 0);
	d = max(min(isNumber(d) ? d : l, l - s), 0);
	v = i - d;
	n = l + v;
	while (k < d) {
		e = this[s + k];
		if (!isUndefined(e)) a[k] = e;
		k += 1;
	}
	x = l - s - d;
	if (v < 0) {
		k = s + i;
		while (x) {
			this[k] = this[k - v];
			k += 1;
			x -= 1;
		}
		this.length = n;
	} else if (v > 0) {
		k = 1;
		while (x) {
			this[n - k] = this[l - k];
			k += 1;
			x -= 1;
		}
	}
	for (k = 0; k < i; ++k) {
		this[s + k] = arguments[k + 2];
	}
	return a;
};
if (![].unshift) Array.prototype.unshift = function () {
	this.splice.apply(this, [0, 0].concat(Array.prototype.slice.apply(arguments)));
	return this.length;
};

/*
	unFocus.Utilities.EventManager, version 1.0b (beta) (2005/12/16)
	Copyright: 2005, Kevin Newman (http://www.unfocus.com/Projects/)
	License: http://creativecommons.org/licenses/LGPL/2.1/
*/
if (typeof unFocus == "undefined") var unFocus = {};
if (!unFocus.Utilities) unFocus.Utilities = {};

/* Class EventManager
	Provides the interface and functionality to a Subscriber/Subscriber Pattern interface for
	classes to easily inherit or use. Event types are passed in during instantiation. I'm not
	sure that's the best way to do it, but that's how it is for now (comments are welcome). */
unFocus.Utilities.EventManager = function(arg) {
	this._listeners = {};
	for (var i = 0; i < arguments.length; i++) {
		this._listeners[arguments[i]] = [];
	}
};
unFocus.Utilities.EventManager.prototype = {
	addEventListener: function($type, $listener) {
		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) {
		for (var i = 0; i < this._listeners[$type].length; i++) {
			if (this._listeners[$type][i] == $listener) {
				this._listeners.splice(i,1);
				return;
			}
		}
	},
	notifyListeners: function($type, $data) {
		for (var i = 0; i < this._listeners[$type].length; i++) this._listeners[$type][i]($data);
	}
}

/* Class Browser
	the Browser class contains a bunch of Booleans as to the setup of the browser */
unFocus.Utilities.Browser = (function () {
	var w = window;
	var d = w.document;
	var ua = navigator.userAgent.toLowerCase();
	var obj = new Object();
	if (!undefined) var undefined;
	obj.ie = w.VBArray!=undefined;
	obj.ie5 = (obj.ie && (!d.createEventObject || !d.namespaces));
	obj.ie55 = (obj.ie && !d.implementation);
	obj.ie6 = (obj.ie && d.implementation!=undefined && !w.XMLHttpRequest);
	obj.ie7 = (obj.ie && w.XMLHttpRequest!=undefined);
	obj.opera = w.opera!=undefined;
	obj.gecko = (w.netscape!=undefined && !this.opera);
	obj.khtml = (/safari/.test(ua) || /konqueror/.test(ua));
	obj.safari3 = (obj.khtml && w.devicePixelRatio!=undefined);
	return obj;
})();

/*
unFocus.History, version 1.9.2 (alpha) (2007/02/13)
Copyright: 2005-2007, Kevin Newman (http://www.unfocus.com/Projects/HistoryKeeper/)
License: http://creativecommons.org/licenses/LGPL/2.1/
*/

unFocus.History = (function() {
	// use a closure to avoid poluting the global scope, and to discourage reinstantiation (like a singleton)
	function Keeper() {
		if (!undefined) var undefined;
		var _this = this;
		var _pollInterval = 200; // set the poll interval here.
		var _currentHash = _getHash();
		var _intervalID = setInterval(_watchHash, _pollInterval);

		/**
		 * 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 _watchHash
		 * 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. Workarounds like the iframe method are used for those
		 * browsers (IE 5.0 will use an anchor creation hack).
		 *
		 * @see _pollInterval
		 */
		function _watchHash () {
			var hash = _getHash();
			if (_currentHash != hash) {
				_currentHash = hash;
				_this.notifyListeners("historyChange", hash);
			}
		}

		/**
		 * private _createAnchor
		 * Various browsers may need an achor to be present in the dom for the hash to actually be set,
		 * so we add one every time a history entry is made. This has a side effect in many browsers,
		 * where if the scroll position of the page is changed, in between history states, this causes
		 * most browsers to remember the position! It's a bonus.
		 *
		 * @param str:String
		 *	a string that sets the Hash on the location string (the current url).
		 */
		function _createAnchor (str) {
			if (!_checkAnchorExists(str)) {
				var $anchor;
				if (unFocus.Utilities.Browser.ie) anchor = document.createElement('<a name="' + str + '">' + str + '</a>');
				else {
					anchor = document.createElement("a");
					anchor.setAttribute("name", str);
				}
				with (anchor.style) {
					position = "absolute";
					display = "block";
					top = _getScrollY() + "px";
					left = _getScrollX() + "px";
				}
				document.body.insertBefore(anchor, document.body.firstChild);
			}
		}

		/**
		 * private _checkAnchorExists
		 * Various browsers may need an achor to be present in the dom for the hash to actually be set,
		 * so we add one every time a history entry is made. This has a side effect in many browsers,
		 * where if the scroll position of the page is changed, in between history states, this causes
		 * most browsers to remember the position! It's a bonus.
		 *
		 * @param str:String
		 *	the name of the anchor tag to check
		 *
		 * @returns Boolean
		 * 	whether or not the anchor tag exists
		 */
		function _checkAnchorExists (str) {
			if (document.getElementsByName(str).length > 0) return true;
			else return false;
		}

		/**
		 * private getScrollY
		 * Keeps IE 5.0 from scrolling to the top every time a new history is entered.
		 * Also retains the scroll position in the history (doesn't seem to work on IE 5.5+).
		 * http://www.quirksmode.org/viewport/compatibility.html
		 *
		 * @returns Number
		 * 	the current scroll position
		 */
		function _getScrollY () {
			var result;
			if (self.pageYOffset) result = self.pageYOffset; // all except Explorer
			else if (document.documentElement && document.documentElement.scrollTop) result = document.documentElement.scrollTop; // Explorer 6 Strict
			else if (document.body) result = document.body.scrollTop; // all other Explorers
		}
		function _getScrollX () {
			var result;
			if (self.pageXOffset) result = self.pageXOffset; // all except Explorer
			else if (document.documentElement && document.documentElement.scrollLeft) result = document.documentElement.scrollLeft; // Explorer 6 Strict
			else if (document.body) result = document.body.scrollLeft; // all other Explorers
		}

		/**
		 * private addHistory
		 * A public method to retrieve the current history string
		 *
		 * @param str:String
		 * 	The has to get the current History Hash
		 */
		function _addHistory(str) {
			if (_currentHash != str) {
				_createAnchor(str);
				_currentHash = str;
				_setHash(str);
				_this.notifyListeners("historyChange", str);
			}
			return true;
		}

		/**
		 * 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 (str) {
			_createAnchor(_currentHash);
			// replace with slimmer versions...
			_this.addHistory = _addHistory;
			// ...do first call
			return _this.addHistory(str);
		};

		/**
		 * public getCurrent
		 * A public method to retrieve the current history string
		 *
		 * @returns String
		 * 	The current History Hash
		 */
		_this.getCurrent = function () {
			return _currentHash;
		};

		/**
		 * 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.
		 */
		if (unFocus.Utilities.Browser.khtml==true&&unFocus.Utilities.Browser.safari3==false) { // Safari < 3

			var _unFocusHistoryLength = history.length;
			var _historyStates = new Object();
			var _recentlyAdded = false;
			var _form;

			// set initial history entry
			_historyStates[_unFocusHistoryLength] = _currentHash;
			// since it doesn't work, might as well cancel the location.hash check
			clearInterval(_intervalID);
			// watch the history.length prop for changes instead
			_intervalID = setInterval(_watchHistoryLength, _pollInterval);

			function _watchHistoryLength () {
				if (!_recentlyAdded) {
					var _historyLength = history.length;
					if (_historyLength != _unFocusHistoryLength) {
						_unFocusHistoryLength = _historyLength;
						var hash = _getHash();
						if (_currentHash != hash) {
							_currentHash = hash;
							_this.notifyListeners("historyChange", hash);
						}
					}
				}
			};

			// We'll use a form to submit to a #hash location instead. I'm assuming this works,
			// need to make sure and pass any query values as parameters to the form as well
			function _createSafariSetHashForm () {
				_form = document.createElement("form");
				_form.id = "unFocusHistoryForm";
				_form.method = "get";
				var query = window.location.search.substring(1);
				var blobs = query.split("&");
				for (var i=0; i<blobs.length; ++i) {
					var pair = blobs[i].split("=");
					var input = document.createElement("input");
					input.type = "hidden";
					input.name = pair[0];
					input.value = pair[1]==undefined ? "" : pair[1];
					_form.appendChild(input);
				}
				document.body.insertBefore(_form,document.body.firstChild);
			};

			_setHash = function (str) {
				_historyStates[_unFocusHistoryLength] = str;
				_form.action = "#" + _getHash();
				_form.submit();
			};

			_getHash = function () {
				return _historyStates[_unFocusHistoryLength];
			};

			_this.addHistory = function (str) {
				// on first call, make an anchor for the root history entry
				_createAnchor(_currentHash);
				// setup the form fix
				_createSafariSetHashForm();
				// replace with slimmer version...
				_this.addHistory = function(str) {
					if (_currentHash != str) {
						_createAnchor(str);
						_currentHash = str;
						_unFocusHistoryLength = history.length+1;
						_recentlyAdded = true;
						_setHash(str);
						_this.notifyListeners("historyChange", str);
						_recentlyAdded = false;
					}
					return true;
				};
				return _this.addHistory(str);
			};

		} else if (unFocus.Utilities.Browser.ie==true&&unFocus.Utilities.Browser.ie5==false) { // IE 5.5+ Windows

			/* iframe references */
			var _historyFrameObj;
			var _historyFrameRef;

			// subscribe to self to update the hash when the history is updated
			_this.addEventListener("historyChange", function(str) { _setHash(str) });

			function _createHistoryFrame () {
				var $historyFrameName = "unFocusHistoryFrame";
				_historyFrameObj = document.createElement("iframe");
				_historyFrameObj.setAttribute("name", $historyFrameName);
				_historyFrameObj.setAttribute("id", $historyFrameName);
				_historyFrameObj.setAttribute("src", 'javascript:;');
				_historyFrameObj.style.position = "absolute";
				_historyFrameObj.style.top = "-900px";
				document.body.insertBefore(_historyFrameObj,document.body.firstChild);
				_historyFrameRef = frames[$historyFrameName];
				_createHistoryHTML(_currentHash, true);
			}

			function _createHistoryHTML (str) {
				with (_historyFrameRef.document) {
					open("text/html");
					write("<html><head></head><body onl",
						'oad="parent.unFocus.History._updateFromHistory(\'' + str + '\');">',
						str + "</body></html>");
					close();
				}
			}

			_this._updateFromHistory = function () {
				// hides the first call to the method, and sets up the real method for the rest of the calls
				_this._updateFromHistory = function(str) {
					_currentHash = str;
					_this.notifyListeners("historyChange", str);
				};
			};

			_this.addHistory = function (str) {
				// do initialization stuff on first call
				_createHistoryFrame();
				// replace this function with a slimmer one on first call
				_this.addHistory = function(str) { // adds history and bookmark hash
					if (_currentHash != str) {
						// IE will create an entry if there is an achor on the page, but it
						// does not allow you to detect the state change, so we skip inserting an Anchor
						_currentHash = str;
						// sets hash and notifies listeners
						_createHistoryHTML(str);
					}
					return true;
				};
				// call the first call
				return _this.addHistory(str);
			};

		}
	}

	Keeper.prototype = new unFocus.Utilities.EventManager("historyChange");
	return new Keeper();

})();