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
    }
};
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()
    }
};
