/*
 * history 1.0 - Plugin for jQuery
 *
 *
 * IE8 is supporting onhashchange event
 * http://msdn.microsoft.com/en-us/library/cc288209(VS.85).aspx
 *
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Depends:
 *   jquery.js
 *
 *  Copyright (c) 2008 Oleg Slobodskoi (ajaxsoft.de)
 *
 *  Rewritten by M.Mennemeier (c) 2009 (http://www.global-communication.de/)
 */

;(function($){
    var eventName = "hashchange";
    var encodes = "|%";
    function decode0(c, s) {
        var parts = s.split(c);
        if (parts.length < 2) return s;
        s = parts[0];
        var cc;
        for (var i = 1; i < parts.length; i++) {
            if (parts[i].length < 2 || isNaN(cc = parseInt(parts[i].substring(0, 2), 16))) {
                s += c + parts[i];
            } else {
                s += String.fromCharCode(cc) + parts[i].substring(2);
            }
        }
        return s;
    }
    function code(c) {
        var r = c.charCodeAt(0).toString(16);
        if (r.length < 2) r = "0" + r;
        return "|" + r;
    }
    function encode(s) {
        if (!s) return $.History.emptyHash;
        for (var i = 0; i < encodes.length; i++) {
            var c = encodes.charAt(i);
            s = s.split(c).join(code(c));
        }
        if (s.indexOf('#') == 0) s = code("#") + s.substring(1);
        return s;
    }
    function decode(s) {
        s = decode0("%", s);
        s = decode0("|", s);
        return s;
    }
    $.extend(true, {
        fn: {
            /* @deprecated bind to "hashchange" event*/
            history: function(callback){
                $(window).bind(eventName, function() {callback($.locationHash())});
            }
        },
        locationHash: function(hash) {
            return $.History.hash.apply($.History, arguments);
        },

        // implementation with native event supported
        History: {
            emptyHash: "_",
            mode: "native-event",

            hash: function(hash) {
                if (arguments.length == 0) {
                    var s = window.location.hash;
                    if (s == "") return window.location.href.indexOf('#') < 0 ? undefined : "";
                    if (s == "#") return "";
                    if (s.indexOf('#') == 0) s = s.substring(1);
                    if (s == this.emptyHash) return undefined;
                    return decode(s);
                } else {
                    window.location.hash = encode(hash);
                }
            },

            /* @deprecated public: use $.locationHash() */
            add: function(value) {
                this.hash(value);
            },

            /* @deprecated use history direct */
            forward: function() {
                history.go(1);
            },

            /* @deprecated use history direct */
            back: function() {
                history.go(-1);
            },

            /* @deprecated */
            destroy: function() { }
        }
    });

    function isHashchangeSupported() {
        if ('documentMode' in document && document['documentMode'] < 8) return false; // IE8 compatibility
        var eventName = "onhashchange";
        if (eventName in window) return true;
        window[eventName] = "return;";
        if (typeof window[eventName] === "function") return true;
        try {
            delete window[eventName];
        } catch (ignore) {
            window[eventName] = "";
        }
        return false;
    }

    // modification with interval check
    function installInterval() {
        var oldHash = $.History.hash;
        $.extend($.History, {
            mode: "interval",
            interval: 0,
            value: $.History.hash(),
            trigger: function(v) {
                $(window).trigger(eventName, this.value = v);
            },
            intervalCallback: function(){
                var newHash = this.hash();
                if (newHash != this.value) this.trigger(newHash);
            },
            destroy: function() {
                clearInterval(this.interval);
            },
            hash: function(hash) {
                if (arguments.length == 0) return oldHash.call(this);
                if (hash == this.hash()) return;
                this.value = hash;
                oldHash.apply(this, arguments);
                this.trigger(hash);
            }
        });
    }

    function installIE() {
        encodes += "?";
        function encode(s) {
            s = s.replace(/&/g, "&amp;");
            s = s.replace(/</g, "&lt;");
            s = s.replace(/>/g, "&gt;");
            s = s.replace(/"/g, "&quot;");
            return s;
        }
        $.extend($.History, {
            mode: "IE pre 8",
            ihash: "",

            intervalCallback: function(){
                try {
                    var iDoc = this.iDoc();
                    var newHash = iDoc ? iDoc.body.innerText : "";
                    if (newHash != this.ihash) {
                        this.hash(this.ihash = newHash);
                        this.trigger(newHash);
                        return;
                    }
                } catch(ignore) {
                }

                newHash = this.hash();
                if (newHash != this.value) {
                    this.updateIFrame(newHash);
                    this.trigger(newHash);
                }
            },

            add: function(value) {
                if (value == this.value) return;
                this.hash(value);
                this.updateIFrame(value);
                this.trigger(value);
            },

            updateIFrame: function(value) {
                try {
                    var iDoc = this.iDoc(true);
                    iDoc.open();
                    iDoc.write('<html><head><title>' + document.title + '</title></head><body>' + encode(value) + '</body></html>');
                    iDoc.close();
                } catch(ignore) {
                    var me = this;
                    window.setTimeout(function(){me.updateIFrame(value)}, 10);
                }
                return value;
            },

            iDoc: function(create) {
                var iframe = $("#jgcs-history");
                if (!iframe.length) {
                    if (create) {
                        iframe = $('<iframe id="jgcs-history" style="display: none;"></iframe>').appendTo(document.body);
                    } else {
                        return false;
                    }
                }
                return iframe.get(0).contentWindow.document;
            },
            destroy: function() {
                clearInterval(this.interval);
                var iframe = this.iDoc(false);
                if (iframe) $(iframe).remove();
                $(window).unbind(eventName);
            }
        });
    }


    if (!isHashchangeSupported()) {
        installInterval();

        var userAgent = navigator.userAgent.toLowerCase();
        var msie = /msie/.test(userAgent) && !/opera/.test(userAgent);
        if (msie) {
            installIE();
        }
        $(function(){
            if (msie) $.History.iDoc(true);
            $.History.interval = window.setInterval(function(){ $.History.intervalCallback(); }, 50);            
        });
    }

})(jQuery);
