

function HistoryManager(){
	this.tc = null;
	this.lasthashstring = null;
	this.lasthasharray = new Object();
	this.comps = new Object();
	this.hash = new hashStateHandler();
	
	this.register = function(key,def,func){
		
		console.log("Registering history manager against key " + key);
		
		this.comps[key] = new Object();
		this.comps[key].onUpdate = func;
		
		//store default unless a real value is set
		var hinfo = this.hash.parseCurrentHash();
		
		if (hinfo[key]){
			def = hinfo[key];
		}else{
			this.hash.updateValue(key, def, true);
		}
		
		//store info to lasthasharray
		//this.lasthasharray[key] = def;
	};	
	
	this.update = function(key,val,batch){
		console.log("Updating key " + key + " with value " + val);
		
		this.hash.updateValue(key,val,batch);
		
		//sweep the changes across to the caches
		if (!batch){
			console.log("Not batch, checking..");
			this.check(true);
		}
	};
	
	this.start = function(){
		console.log("starting history");
		objtime = this.checkPeriodical.bind(this);
		this.tc = setTimeout(objtime,50);
		
		//save the current stored registers
		console.log("saving stored history (initial cold start)");
		this.hash.saveCurrentHash(null);
	};
	
	this.checkPeriodical = function(){
		objtime = this.checkPeriodical.bind(this);
		this.tc = setTimeout(objtime,300);
		this.check();
	};
	
	this.check = function(skipcallbacks){
		//has the hash changed at all?
		
		if (skipcallbacks == true){
			console.log("skipping callbacks");
		}
		
		var newIEContent = false;
		
		if (this.hash.iframe) {
			console.log("iframe detected - this is a IE session");
			
			var doc = this.hash.iframe.contentWindow.document;
			if (doc && doc.body.id == 'state') {
				var query = doc.body.innerText;
				
				if (query == this.lasthashstring){
					console.log("query matches hash, skipping");
					return true;
				}else{
					console.log("new IE content");
					newIEContent = true;
				}
			}else{
				console.log("doc unavailable or not a state");
			}
		}

		
		if ((window.location.hash.substring(1) == this.lasthashstring) && (!newIEContent)){
			//all's well
			return true;
		}else{
			//something has changed..
			console.log("Processing check - changed");
			
			//grab the hash
			var hnifo = this.hash.parseCurrentHash();
			
			//compare against the old hash..
			if (!newIEContent){
				//load from hash
			  var query = window.location.hash.substring(1);
			}
			
			  var vars = query.split("~");
			  for (var i=0;i<vars.length;i++) {
			    var pair = vars[i].split("=");
			    
			    //has this changed?
			    if ( this.lasthasharray[pair[0]] == pair[1]){
			    	//no change
			    	console.log("item has not changed - " + this.lasthasharray[pair[0]] + ' vs. ' + pair[1]);
			    }else{
			        //log change
			    	console.log("item has changed - " + pair[1]);
			    	this.lasthasharray[pair[0]] = pair[1];
			    	
			    	//call function
			    	if (this.comps[pair[0]] && this.comps[pair[0]].onUpdate){
			    		if (skipcallbacks == true){
			    			console.log("skip callbacks");
			    		}else{
			    			console.log("calling function..");
			    			this.comps[pair[0]].onUpdate(pair[1]);
			    		}
			    	}else{
			    		console.log("no callback");
			    	}
			    }
			  }
		  
			
			//cache new info
			  if (!newIEContent){
				  	this.lasthashstring = window.location.hash.substring(1);
			  }else{
				  this.lasthashstring = query;
			  }
		}
	}
}

					function hashStateHandler(){
						//updates the value (or creates if not set)
						this.hashCache = new Object();
						
						this.updateValue = function (key,val,deferSaving){
							//get current hash
							hashInfo = this.parseCurrentHash();
							hashInfo[key] = val;
							
							if (!deferSaving){
								this.saveCurrentHash(hashInfo);
							}else{
								//cache
								this.hashCache[key] = val;
							}
							
							return true;
						};
						
						this.saveCurrentHash = function(hashInfo){
							var tmphash = '';
							
							//handle saving cached items
							if (!hashInfo){
								hashInfo = this.parseCurrentHash();
								
								//merge info
								for (newItem in this.hashCache){
									hashInfo[newItem] = this.hashCache[newItem];
								}
							}
						
							for (hashInfoI in hashInfo){
								if (!hashInfo[hashInfoI]){
								}else{
									tmphash = tmphash + '~' + hashInfoI + '=' + escape(hashInfo[hashInfoI]);
								}
							}
							
							//insert the hash, supporting the various wacky browser requirements
							if (Prototype.Browser.WebKit) {
								if (!this.form) {
									this.form = new Element('form', {'method': 'get'});
									document.body.appendChild(this.form);;
								}	
								this.form.action = '#' + tmphash;
								this.form.submit();
							} else {
								top.location.hash = tmphash || '#';
							}	
							
							if (Prototype.Browser.IE) {
								if (!this.iframe) {
									this.iframe = new Element('iframe', {
										'src': 'js/index.html',
										'styles': 'display: none;',
										'width': '1',
										'height': '1'
									});
									document.body.appendChild(this.iframe);

								}
								try {
									var doc = this.iframe.contentWindow.document;
									doc.open();
									doc.write('<html><body id="state">' + tmphash + '</body></html>');
									doc.close();
								} catch(e) {};
							}

							
					
						};
						
						this.parseCurrentHash = function(){
						  var hashList = new Object();
						
						  var query = window.location.hash.substring(1);
						  
							if (this.iframe) {
								var doc = this.iframe.contentWindow.document;
								if (doc && doc.body.id == 'state') {
									var query = doc.body.innerText;
								}
							}

						  
						  var vars = query.split("~");
						  for (var i=0;i<vars.length;i++) {
						    var pair = vars[i].split("=");
						    if (typeof(pair[0]) == 'undefined'){
						    	
						    }else{
						    	hashList[pair[0]] = pair[1];
						    }
						  }
						  
						  return hashList;
						}
					
					}
