window.dhtmlHistory ={
isIE:false,
isOpera:false,
isSafari:false,
isKonquerer:false,
isGecko:false,
isSupported:false,
create:function(options){
var that = this;
var UA = navigator.userAgent.toLowerCase();
var platform = navigator.platform.toLowerCase();
var vendor = navigator.vendor || "";
if (vendor === "KDE"){
this.isKonqueror = true;
this.isSupported = false;
} else if (typeof window.opera !== "undefined"){
this.isOpera = true;
this.isSupported = true;
} else if (typeof document.all !== "undefined"){
this.isIE = true;
this.isSupported = true;
} else if (vendor.indexOf("Apple Computer, Inc.") > -1){
this.isSafari = true;
this.isSupported = (platform.indexOf("mac") > -1);
} else if (UA.indexOf("gecko") != -1){
this.isGecko = true;
this.isSupported = true;
}
window.historyStorage.setup(options);
if (this.isSafari){
this.createSafari();
} else if (this.isOpera){
this.createOpera();
}
var initialHash = this.getCurrentLocation();
this.currentLocation = initialHash;
if (this.isIE){
this.createIE(initialHash);
}
var unloadHandler = function(){
that.firstLoad = null;
};

this.addEventListener(window,'unload',unloadHandler);
if (this.isIE){
this.ignoreLocationChange = true;
} else{
if (!historyStorage.hasKey(this.PAGELOADEDSTRING)){
this.ignoreLocationChange = true;
this.firstLoad = true;
historyStorage.put(this.PAGELOADEDSTRING, true);
} else{
this.ignoreLocationChange = false;
this.fireOnNewListener = true;
}
}
var locationHandler = function(){
that.checkLocation();
};
setInterval(locationHandler, 100);
},
initialize:function(){
if (this.isIE){
if (!historyStorage.hasKey(this.PAGELOADEDSTRING)){
this.fireOnNewListener = false;
this.firstLoad = true;
historyStorage.put(this.PAGELOADEDSTRING, true);
}
else{
this.fireOnNewListener = true;
this.firstLoad = false;   
}
}
},
addListener:function(listener){
this.listener = listener;
if (this.fireOnNewListener){
this.fireHistoryEvent(this.currentLocation);
this.fireOnNewListener = false;
}
},
addEventListener:function(o,e,l){
if (o.addEventListener){
o.addEventListener(e,l,false);
} else if (o.attachEvent){
o.attachEvent('on'+e,function(){
l(window.event);
});
}
},
add:function(newLocation, historyData){

if (this.isSafari){
newLocation = this.removeHash(newLocation);
historyStorage.put(newLocation, historyData);
this.currentLocation = newLocation;
window.location.hash = newLocation;
this.putSafariState(newLocation);

} else{
var that = this;
var addImpl = function(){
if (that.currentWaitTime > 0){
that.currentWaitTime = that.currentWaitTime - that.waitTime;
}
newLocation = that.removeHash(newLocation);
if (document.getElementById(newLocation) && that.debugMode){
var e = "Exception:History locations can not have the same value as _any_ IDs that might be in the document,"
+ " due to a bug in IE; please ask the developer to choose a history location that does not match any HTML"
+ " IDs in this document. The following ID is already taken and cannot be a location:" + newLocation;
throw new Error(e); 
}
historyStorage.put(newLocation, historyData);
that.ignoreLocationChange = true;
that.ieAtomicLocationChange = true;
that.currentLocation = newLocation;
window.location.hash = newLocation;
if (that.isIE){
that.iframe.src = "blank.html?" + newLocation;
}
that.ieAtomicLocationChange = false;
};
window.setTimeout(addImpl, this.currentWaitTime);
this.currentWaitTime = this.currentWaitTime + this.waitTime;
}
},
del: function(Location) {
			/*Store the history data into history storage*/
			historyStorage.remove(Location);
	},
isFirstLoad:function(){
return this.firstLoad;
},
getVersion:function(){
return "0.6";
},
getCurrentLocation:function(){
var r = (this.isSafari
? this.getSafariState()
:this.getCurrentHash()
);
return r;
},
    getCurrentHash:function(){
var r = window.location.href;
var i = r.indexOf("#");
return (i >= 0
? r.substr(i+1)
:""
);
    },
PAGELOADEDSTRING:"DhtmlHistory_pageLoaded",
listener:null,
waitTime:200,
currentWaitTime:0,
currentLocation:null,
iframe:null,
safariHistoryStartPoint:null,
safariStack:null,
safariLength:null,
ignoreLocationChange:null,
fireOnNewListener:null,
firstLoad:null,
ieAtomicLocationChange:null,
createIE:function(initialHash){
this.waitTime = 400;
var styles = (historyStorage.debugMode
? 'width:800px;height:80px;border:1px solid black;'
:historyStorage.hideStyles
);
var iframeID = "rshHistoryFrame";
var iframeHTML = '<iframe frameborder="0" id="' + iframeID + '" style="' + styles + '" src="blank.html?' + initialHash + '"></iframe>';
document.write(iframeHTML);
this.iframe = document.getElementById(iframeID);
},
createOpera:function(){
this.waitTime = 400;
var imgHTML = '<img src="javascript:location.href=\'javascript:dhtmlHistory.checkLocation();\';" style="' + historyStorage.hideStyles + '" />';
document.write(imgHTML);
},
createSafari:function(){
var formID = "rshSafariForm";
var stackID = "rshSafariStack";
var lengthID = "rshSafariLength";
var formStyles = historyStorage.debugMode ? historyStorage.showStyles :historyStorage.hideStyles;
var inputStyles = (historyStorage.debugMode
? 'width:800px;height:20px;border:1px solid black;margin:0;padding:0;'
:historyStorage.hideStyles
);
var safariHTML = '<form id="' + formID + '" style="' + formStyles + '">'
+ '<input type="text" style="' + inputStyles + '" id="' + stackID + '" value="[]"/>'
+ '<input type="text" style="' + inputStyles + '" id="' + lengthID + '" value=""/>'
+ '</form>';
document.write(safariHTML);
this.safariStack = document.getElementById(stackID);
this.safariLength = document.getElementById(lengthID);
if (!historyStorage.hasKey(this.PAGELOADEDSTRING)){
this.safariHistoryStartPoint = history.length;
this.safariLength.value = this.safariHistoryStartPoint;
} else{
this.safariHistoryStartPoint = this.safariLength.value;
}
},
getSafariStack:function(){
var r = this.safariStack.value;
return historyStorage.fromJSON(r);
},
getSafariState:function(){
var stack = this.getSafariStack();
var state = stack[history.length - this.safariHistoryStartPoint - 1];
return state;
},
putSafariState:function(newLocation){
    var stack = this.getSafariStack();
    stack[history.length - this.safariHistoryStartPoint] = newLocation;
    this.safariStack.value = historyStorage.toJSON(stack);
},
fireHistoryEvent:function(newHash){
var historyData = historyStorage.get(newHash);
this.listener.call(null, newHash, historyData);
},
checkLocation:function(){
if (!this.isIE && this.ignoreLocationChange){
this.ignoreLocationChange = false;
return;
}
if (!this.isIE && this.ieAtomicLocationChange){
return;
}
var hash = this.getCurrentLocation();
if (hash == this.currentLocation){
return;
}
this.ieAtomicLocationChange = true;

if (this.isIE && this.getIframeHash() != hash){
this.iframe.src = "blank.html?" + hash;
}
else if (this.isIE){
return;
}
this.currentLocation = hash;
this.ieAtomicLocationChange = false;
this.fireHistoryEvent(hash);
},
getIframeHash:function(){
var doc = this.iframe.contentWindow.document;
var hash = String(doc.location.search);
if (hash.length == 1 && hash.charAt(0) == "?"){
hash = "";
}
else if (hash.length >= 2 && hash.charAt(0) == "?"){
hash = hash.substring(1);
}
return hash;
},
removeHash:function(hashValue){
var r;
if (hashValue === null || hashValue === undefined){
r = null;
}
else if (hashValue === ""){
r = "";
}
else if (hashValue.length == 1 && hashValue.charAt(0) == "#"){
r = "";
}
else if (hashValue.length > 1 && hashValue.charAt(0) == "#"){
r = hashValue.substring(1);
}
else{
r = hashValue;
}
return r;
},
iframeLoaded:function(newLocation){
if (this.ignoreLocationChange){
this.ignoreLocationChange = false;
return;
}
var hash = String(newLocation.search);
if (hash.length == 1 && hash.charAt(0) == "?"){
hash = "";
}
else if (hash.length >= 2 && hash.charAt(0) == "?"){
hash = hash.substring(1);
}
window.location.hash = hash;
this.fireHistoryEvent(hash);
}

};
window.historyStorage ={
setup:function(options){
if (typeof options !== "undefined"){
if (options.debugMode){
this.debugMode = options.debugMode;
}
if (options.toJSON){
this.toJSON = options.toJSON;
}
if (options.fromJSON){
this.fromJSON = options.fromJSON;
}
}
var formID = "rshStorageForm";
var textareaID = "rshStorageField";
var formStyles = this.debugMode ? historyStorage.showStyles :historyStorage.hideStyles;
var textareaStyles = (historyStorage.debugMode
? 'width:800px;height:80px;border:1px solid black;'
:historyStorage.hideStyles
);
var textareaHTML = '<form id="' + formID + '" style="' + formStyles + '">'
+ '<textarea id="' + textareaID + '" style="' + textareaStyles + '"></textarea>'
+ '</form>';
document.write(textareaHTML);
this.storageField = document.getElementById(textareaID);
if (typeof window.opera !== "undefined"){
this.storageField.focus();
}
},
put:function(key, value){
this.assertValidKey(key);
this.remove(key);
this.storageHash[key] = value;
this.saveHashTable();
},
get:function(key){
this.assertValidKey(key);
this.loadHashTable();
var value = this.storageHash[key];
if (value === undefined){
value = null;
}
return value;
},
remove:function(key){
this.assertValidKey(key);
this.loadHashTable();
delete this.storageHash[key];
this.saveHashTable();
},
reset:function(){
this.storageField.value = "";
this.storageHash ={};
},
hasKey:function(key){
this.assertValidKey(key);
this.loadHashTable();
return (typeof this.storageHash[key] !== "undefined");
},
isValidKey:function(key){
return (typeof key === "string");
},
showStyles:'border:0;margin:0;padding:0;',
hideStyles:'left:-1000px;top:-1000px;width:1px;height:1px;border:0;position:absolute;',
debugMode:false,
storageHash:{},
hashLoaded:false, 
storageField:null,
assertValidKey:function(key){
var isValid = this.isValidKey(key);
if (!isValid && this.debugMode){
throw new Error("Please provide a valid key for window.historyStorage. Invalid key = " + key + ".");
}
},
loadHashTable:function(){
if (!this.hashLoaded){
var serializedHashTable = this.storageField.value;
if (serializedHashTable !== "" && serializedHashTable !== null){
this.storageHash = this.fromJSON(serializedHashTable);
this.hashLoaded = true;
}
}
},
saveHashTable:function(){
this.loadHashTable();
var serializedHashTable = this.toJSON(this.storageHash);
this.storageField.value = serializedHashTable;
},
toJSON:function(o){
return o.toJSONString();
},
fromJSON:function(s){
return s.parseJSON();
}
};

