window.dhtmlHistory = {initialize: function() {
if (this.isInternetExplorer() == false) {return;}
if (historyStorage.hasKey("DhtmlHistory_pageLoaded") == false) {
this.fireOnNewListener = false;
this.firstLoad = true;
historyStorage.put("DhtmlHistory_pageLoaded", true);}
else {this.fireOnNewListener = true;this.firstLoad = false;}},
addListener: function(callback) {
this.listener = callback;
if (this.fireOnNewListener == true) {
this.fireHistoryEvent(this.currentLocation);
this.fireOnNewListener = false;}},
add: function(newLocation, historyData) {
var self = this;
var addImpl = function() {
if (self.currentWaitTime > 0)
self.currentWaitTime = self.currentWaitTime - self.WAIT_TIME;
newLocation = self.removeHash(newLocation);
var idCheck = document.getElementById(newLocation);
if (idCheck != undefined || idCheck != null) {
var message ="Exception: History locations can not have the same value as _any_ id's that might be in the document, due to a bug in Internet Explorer; please ask the developer to choose a history location that does not match any HTML id's in this document. The following ID is already taken and can not be a location: " + newLocation;
throw message; }
historyStorage.put(newLocation, historyData);
self.ignoreLocationChange = true;
this.ieAtomicLocationChange = true;
self.currentLocation = newLocation;
window.location.hash = newLocation;
if (self.isInternetExplorer())
self.iframe.src = "blank.html?" + newLocation;
this.ieAtomicLocationChange = false;};
window.setTimeout(addImpl, this.currentWaitTime);
this.currentWaitTime = this.currentWaitTime + this.WAIT_TIME;},
isFirstLoad: function() {
if (this.firstLoad == true) {
return true;
}else {return false;}},
isInternational: function() {return false;},
getVersion: function() {return "0.03";},
getCurrentLocation: function() {
var currentLocation = this.removeHash(window.location.hash);
return currentLocation;},
currentLocation: null,
listener: null,
iframe: null,
ignoreLocationChange: null,
WAIT_TIME: 200,
currentWaitTime: 0,
fireOnNewListener: null,
firstLoad: null,
ieAtomicLocationChange: null,
create: function() {
var initialHash = this.getCurrentLocation();
this.currentLocation = initialHash;
if (this.isInternetExplorer()) {
document.write("<iframe style='border: 0px; width: 1px;height: 1px; position: absolute; bottom: 0px; right: 0px; visibility: visible;' name='DhtmlHistoryFrame' id='DhtmlHistoryFrame' src='blank.html?" + initialHash + "'></iframe>");
this.WAIT_TIME = 400;}
var self = this;
window.onunload = function() {
self.firstLoad = null;};
if (this.isInternetExplorer() == false) {
if (historyStorage.hasKey("DhtmlHistory_pageLoaded") == false) {
this.ignoreLocationChange = true;
this.firstLoad = true;
historyStorage.put("DhtmlHistory_pageLoaded", true);}
else {
this.ignoreLocationChange = false;
this.fireOnNewListener = true;}}
else {this.ignoreLocationChange = true;}
if (this.isInternetExplorer()) {
this.iframe = document.getElementById("DhtmlHistoryFrame");}
var self = this;
var locationHandler = function() {
self.checkLocation();};
setInterval(locationHandler, 100);},
fireHistoryEvent: function(newHash) {
var historyData = historyStorage.get(newHash);
this.listener.call(null, newHash, historyData);},
checkLocation: function() {
if (this.isInternetExplorer() == false && this.ignoreLocationChange == true) {
this.ignoreLocationChange = false; return;}
if (this.isInternetExplorer() == false && this.ieAtomicLocationChange == true) {return;}
var hash = this.getCurrentLocation();
if (hash == this.currentLocation) return;
this.ieAtomicLocationChange = true;
if (this.isInternetExplorer() && this.getIFrameHash() != hash) {
this.iframe.src = "blank.html?" + hash;}
else if (this.isInternetExplorer()) {return;}
this.currentLocation = hash;
this.ieAtomicLocationChange = false;
this.fireHistoryEvent(hash);},
getIFrameHash: function() {
var historyFrame = document.getElementById("DhtmlHistoryFrame");
var doc = historyFrame.contentWindow.document;
var hash = new 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) {
if (hashValue == null || hashValue == undefined) return null;
else if (hashValue == "") return "";
else if (hashValue.length == 1 && hashValue.charAt(0) == "#") return "";
else if (hashValue.length > 1 && hashValue.charAt(0) == "#") return hashValue.substring(1);
else return hashValue;},
iframeLoaded: function(newLocation) {
if (this.ignoreLocationChange == true) {
this.ignoreLocationChange = false; return;}
var hash = new String(newLocation.search);
if (hash.length == 1 && hash.charAt(0) == "?")
hash = "";
else if (hash.length >= 2 && hash.charAt(0) == "?")
hash = hash.substring(1);
if (this.pageLoadEvent != true) {window.location.hash = hash;}
this.fireHistoryEvent(hash);},
isInternetExplorer: function() {
var userAgent = navigator.userAgent.toLowerCase();
if (document.all && userAgent.indexOf('msie')!=-1) {return true;}
else {return false;}}};
window.historyStorage = {debugging: false, storageHash: new Object(),hashLoaded: false,put: function(key, value) {
this.assertValidKey(key);
if (this.hasKey(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) return null;
else return value;},
remove: function(key) {
this.assertValidKey(key);
this.loadHashTable();
delete this.storageHash[key];
this.saveHashTable();},
reset: function() {
this.storageField.value = "";
this.storageHash = new Object();},
hasKey: function(key) {
this.assertValidKey(key);
this.loadHashTable();
if (typeof this.storageHash[key] == "undefined") return false;
else return true;},
isValidKey: function(key) {
if (typeof key != "string")
key = key.toString();
var matcher = /^[a-zA-Z0-9_ \!\@\#\$\%\^\&\*\(\)\+\=\:\;\,\.\/\?\|\\\~\{\}\[\]]*$/;
return matcher.test(key); },
storageField: null,
init: function() {
var styleValue = "position: absolute; top: -1000px; left: -1000px;";
if (this.debugging == true) { styleValue = "width: 30em; height: 30em;";}
var newContent ="<form id='historyStorageForm' method='GET' style='" + styleValue + "'><textarea id='historyStorageField' style='" + styleValue + "'left: -1000px;' name='historyStorageField'></textarea></form>";
document.write(newContent);
this.storageField = document.getElementById("historyStorageField");},
assertValidKey: function(key) {
if (this.isValidKey(key) == false) {
throw "Please provide a valid key for window.historyStorage, key= "+ key; }},
loadHashTable: function() {
if (this.hashLoaded == false) {
var serializedHashTable = this.storageField.value;
if (serializedHashTable != "" && serializedHashTable != null) {
this.storageHash = eval('(' + serializedHashTable + ')');  }
this.hashLoaded = true;}},
saveHashTable: function() {
this.loadHashTable();
var serializedHashTable = JSON.stringify(this.storageHash);
this.storageField.value = serializedHashTable;}};
/* The JSON class is copyright 2005 JSON.org. */
Array.prototype.______array = '______array';
var JSON = {
org: 'http://www.JSON.org',
copyright: '(c)2005 JSON.org',
license: 'http://www.crockford.com/JSON/license.html',
stringify: function (arg) {
var c, i, l, s = '', v;
switch (typeof arg) {
case 'object':
if (arg) {
if (arg.______array == '______array') {
for (i = 0; i < arg.length; ++i) {
v = this.stringify(arg[i]);
if (s) { s += ',';}
s += v;}
return '[' + s + ']';} else if (typeof arg.toString != 'undefined') {
for (i in arg) {
v = arg[i];
if (typeof v != 'undefined' && typeof v != 'function') {
v = this.stringify(v);
if (s) {s += ',';}
s += this.stringify(i) + ':' + v;}}
return '{' + s + '}';}}
return 'null';
case 'number':
return isFinite(arg) ? String(arg) : 'null';
case 'string':
l = arg.length;
s = '"';
for (i = 0; i < l; i += 1) {
c = arg.charAt(i);
if (c >= ' ') {
if (c == '\\' || c == '"') {s += '\\';}
s += c;} else {
switch (c) {
case '\b':s += '\\b';break;
case '\f':s += '\\f';break;
case '\n':s += '\\n';break;
case '\r':s += '\\r';break;
case '\t':s += '\\t';break;
default:
c = c.charCodeAt();
s += '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);}}}
return s + '"';
case 'boolean':
return String(arg);
default:
return 'null';}},
parse: function (text) {
var at = 0;
var ch = ' ';
function error(m) {
throw {name: 'JSONError',message: m,at: at - 1,text: text};}
function next() {
ch = text.charAt(at);
at += 1;
return ch;}
function white() {
while (ch != '' && ch <= ' ') {next();}}
function str() {
var i, s = '', t, u;
if (ch == '"') {
outer: while (next()) {
if (ch == '"') {
next();
return s;
} else if (ch == '\\') {
switch (next()) {
case 'b':s += '\b';break;
case 'f':s += '\f';break;
case 'n':s += '\n';break;
case 'r':s += '\r';break;
case 't':s += '\t';break;
case 'u':
u = 0;
for (i = 0; i < 4; i += 1) {
t = parseInt(next(), 16);
if (!isFinite(t)) {
break outer;}
u = u * 16 + t;}
s += String.fromCharCode(u);
break;
default:
s += ch;}
} else {
s += ch;}}}
error("Bad string");}
function arr() {
var a = [];
if (ch == '[') {
next();
white();
if (ch == ']') {
next();
return a;}
while (ch) {
a.push(val());
white();
if (ch == ']') {
next();
return a;
} else if (ch != ',') {
break;}
next();
white();}}
error("Bad array");}
function obj() {
var k, o = {};
if (ch == '{') {
next();
white();
if (ch == '}') {
next();
return o;}
while (ch) {
k = str();
white();
if (ch != ':') {
break;}
next();
o[k] = val();
white();
if (ch == '}') {
next();
return o;
} else if (ch != ',') {
break;}
next();
white();}}
error("Bad object");}
function num() {
var n = '', v;
if (ch == '-') {
n = '-';
next();}
while (ch >= '0' && ch <= '9') {
n += ch;
next();}
if (ch == '.') {
n += '.';
while (next() && ch >= '0' && ch <= '9') {
n += ch;}}
if (ch == 'e' || ch == 'E') {
n += 'e';
next();
if (ch == '-' || ch == '+') {
n += ch;
next();}
while (ch >= '0' && ch <= '9') {
n += ch;
next();}}
v = +n;
if (!isFinite(v)) {
error("Bad number");} else {
return v;}}
function word() {
switch (ch) {
case 't':if (next() == 'r' && next() == 'u' && next() == 'e') {next();return true;}break;
case 'f': if (next() == 'a' && next() == 'l' && next() == 's' && next() == 'e') {next();return false;}break;
case 'n': if (next() == 'u' && next() == 'l' && next() == 'l') {next();return null;} break;}
error("Syntax error");}
function val() {
white();
switch (ch) {
case '{':return obj();
case '[':return arr();
case '"':return str();
case '-':return num();
default: return ch >= '0' && ch <= '9' ? num() : word();}}
return val();}};
window.historyStorage.init();
window.dhtmlHistory.create();