//set the events
window.store('hashchange:interval',300);
window.store('hashchange:ieframe-src','./blank.html');
window.store('hashchange:implemented',!!('onhashchange' in window));

Element.Events.hashchange = {
	onAdd:function(fn) {
		//clear the event
		Element.Events.hashchange.onAdd = $empty;

		//check the element
		var self = $(this);
		var checker = $empty;
		if(self != $(window)) {
			return; //the window object only supports this
		}

		//this will prevent the browser from firing the url when the page loads (native onhashchange doesn't do this)
		window.store('hashchange:changed',false);

		//this global method gets called when the hash value changes for all browsers
		var hashchanged = function(hash,tostore) {
			window.store('hashchange:current',tostore || hash);
			if(window.retrieve('hashchange:changed')) {
				hash = hash.trim();
				if(hash.length==0) {
					var url = new String(window.location);
					if(url.indexOf('#')>=0)
					hash = '#';
				}
				window.fireEvent('hashchange',[hash]);
			}
			else {
				window.store('hashchange:changed',true);
			}
		};

		//this is used for when a hash change method has already been defined (futureproof)
		if(typeof window.onhashchange == 'function' && fn !== window.onhashchange) {
			//bind the method to the mootools method stack
			window.addEvent('hashchange',window.onhashchange);

			//remove the event
			window.onhashchange = null;
		}

		//Oldschool IE browsers
		if(Browser.Engine.trident4 || Browser.Engine.trident5) {

			//IE6 and IE7 require an empty frame to relay the change (back and forward buttons)
			//custom IE method
			checker = function(url,frame) {

				//clear the timer
				var checker = window.retrieve('hashchange:checker');
				var timer = window.retrieve('hashchange:timer');
				$clear(timer); //just incase
				timer = null;

				//IE may give a hash value, a path value or a url
				var isNull = frame && url.length == 0;
				var isEmpty = url == '#';
				var hash, compare, cleanurl = unescape(new String(window.location));

				if(isEmpty) {
					compare = hash = '#';
				}
				else if(isNull) {
					compare = hash = '';
				}
				else {

					//setup the url
					url = url != null ? url : cleanurl;
					hash = url;

					if(url.length>0) { //not an empty hash
						var index = url.indexOf('#');
						if(index>=0)
						hash = url.substr(index);
					}

					//check the hash
					compare = hash.toLowerCase();
				}

				//if the hash value is different, then it has changed
				var current = window.retrieve('hashchange:current');
				if(current != compare) {

					//update the url
					if(frame) {
						url = cleanurl;
						if(current) {
							url = url.replace(current,hash);
						}
						else {
							url += hash;
						}
						window.location = url;
					}

					//check the flag
					var hasChanged = !frame && window.retrieve('hashchange:changed');

					//change the hash
					hashchanged(hash,compare);

					if(hasChanged) {
						//this will prevent the frame from changing the first time
						window.retrieve('hashchange:ieframe').setPath(hash);
					}
				}

				//reset the timer
				timer = checker.delay(window.retrieve('hashchange:interval'));
				window.store('hashchange:timer',timer);

			};

			//create the frame
			var src = window.retrieve('hashchange:ieframe-src');
			var ieframe = new IFrame({
				'id':'hashchange-ie-frame',
				'src':src+'?start',
				'styles':{
					'width':0,
					'height':0,
					'position':'absolute',
					'top':-9999,
					'left':-9999
				},
				'onload':function() {
					//this shouldn't exist when a hash is changed, if it does then the frame has just loaded
					var self = $('hashchange-ie-frame');
					if(self.retrieve('loaded')) {
						//examine the url
						var url = unescape(new String(self.contentWindow.location));
						var index = url.indexOf('?');
						if(index>=0) {
							var path = '', empty = false;
							if(url.indexOf('?empty')>=0) {
								path = '#';
							}
							else {
								index = url.indexOf('?!');
								if(index>=0) {
									path = url.substr(index+2);
									path = '#' + path;
								}
							}
							window.retrieve('hashchange:checker')(path,true);
						}
					}
					else {
						self.store('loaded',true);
					}
				}.bind(window)
			});

			//save the frame
			window.store('hashchange:ieframe',ieframe);
			ieframe.injectInside(document.body);

			var doc = ieframe.contentWindow;
			ieframe.setPath = function(path) {
				if(path.charAt(0)=='#') {
					path = path.substr(1);
					if(path.length==0) {
						this.contentWindow.location = src + '?empty';
						return;
					}
				}
				this.contentWindow.location = src + '?!' + escape(path);
			}.bind(ieframe);
		}
		else if(window.retrieve('hashchange:implemented')) { //Firefox 3.6, Chrome 5, IE8 all support the event natively

			//check the hashcheck
			checker = window.onhashchange = function(hash) {

				//make sure the hash is a string
				hash = hash && typeof hash == 'string' ? hash : new String(window.location.hash);

				//this is important so that the URL hash has changed BEFORE this is fired
				hashchanged.delay(1,window,[hash]);

			}
		}
		else { //Others
			//opera requires a history mode to be set so that #hash values are recorded in history (back and forward buttons)
			if(Browser.Engine.presto) {
				history.navigationMode='compatible';
			}

			//set the inteval method
			checker = function(hash) {

				//clear the timer
				var checker = window.retrieve('hashchange:checker');
				var timer = window.retrieve('hashchange:timer');
				$clear(timer); //just incase
				timer = null;

				//compare the hash
				var hash = hash || new String(window.location.hash);
				var compare = hash.toLowerCase();
				if(hash.length==0 && new String(window.location).indexOf('#')>=0) {
					compare = '#';
				}
				var current = window.retrieve('hashchange:current');
				if(current != compare) {
					hashchanged(hash,compare);
				}

				//reset the timer
				timer = checker.delay(window.retrieve('hashchange:interval'));
				window.store('hashchange:timer',timer);

			}
		}

		//run the loop
		window.store('hashchange:checker',checker);
		checker();

		//setup a custom go event
		window.sethash = function(hash) {
			if(hash.charAt(0)!='#')
			hash = '#' + hash;
			checker(hash);
		}
	},

	onDelete:function() {
		if($(this) == $(window)) {
			var timer = window.retrieve('hashchange:timer');
			if(timer) {
				$clear(timer); timer = null;
				window.store('hashchange:timer',null);
			}
		}
	}
}
