/*
 * == jQuery hash plugin ==
 * Plugin for detecting changes to the hash and for adding history support for
 * hashes to certain browsers.
 *
 * Example:
 *     function myCallback(hash) { alert(hash); }
 *     $.hash.init(myCallback, 'blank.html');
 *     $('#my-anchor').hash('abc123');
 * 
 * WARNING for Internet Explorer 7 and below:
 * If an element on the page has the same ID as the hash used, the history will
 * get messed up.
 *
 * Copyright (c) 2009 Andreas Blixt
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 */

(function () {
var
// Plugin settings
iframeId = 'jquery-history',
eventDataName = 'hash.fn',
// Import globals
jQuery = window.jQuery,
documentMode = document.documentMode,
history = window.history,
location = window.location,
undefined,
// Plugin variables
callback, hash,
// IE-specific
iframe,
// Safari-specific
historyLength, states = [],

getHash = function () {
    return location.hash.substr(1);
},

// Used by all browsers except Internet Explorer 7 and below.
poll = function () {
    var
    curHash = getHash(),
    curHistoryLength = history.length;

    if (curHash != hash) {
        hash = curHash;
        callback(curHash);
    } else if (jQuery.browser.safari && curHistoryLength != historyLength) {
        hash = states[curHistoryLength - 1];
        callback(hash);
    }

    historyLength = curHistoryLength;
},

// Used to create a history entry with a value in the iframe.
setIframe = function (newHash) {
    try {
        var doc = iframe.contentWindow.document;
        doc.open();
        doc.write('<html><body>' + newHash + '</body></html>');
        doc.close();
    } catch (e) {
        setTimeout(function () { setIframe(newHash); }, 10);
    }
},

// Used by Internet Explorer 7 and below to create an iframe
// that keeps track of history changes.
setUpIframe = function () {
    // Don't run until access to the iframe is allowed.
    try {
        iframe.contentWindow.document;
    } catch (e) {
        setTimeout(setUpIframe, 10);
        return;
    }

    // Create a history entry for the initial state.
    setIframe(hash);
    var data = hash;

    setInterval(function () {
        var curData, curHash;

        try {
            curData = iframe.contentWindow.document.body.innerText;
            if (curData != data) {
                data = curData;
                if (curData != hash) location.hash = hash = curData;
                callback(curData);
            } else {
                curHash = getHash();
                if (curHash != hash) {
                    hash = curHash;
                    setIframe(curHash);
                }
            }
        } catch (e) {
        }
    }, 50);
};

jQuery.hash = {
    init: function (cb, src) {
        callback = cb;

        // Keep track of the hash value.
        hash = getHash();
        callback(hash);

        if (jQuery.browser.msie) {
            if (!documentMode || documentMode < 8) {
                // Internet Explorer 5.5/6/7 need an iframe for history support.
                jQuery('body').prepend('<iframe id="' + iframeId + '" style="display:none;"' +
                    (src ? ' src="' + src + '"' : '') + '></iframe>');
                iframe = jQuery('#' + iframeId)[0];
                setUpIframe();
            } else {
                // Internet Explorer 8 has onhashchange event.
                window.attachEvent('onhashchange', poll);
            }
        } else {
            if (jQuery.browser.opera && history.navigationMode !== undefined)
                history.navigationMode = 'compatible';

            // For tracking history changes in older Safari browsers.
            historyLength = history.length;

            setInterval(poll, 50);
        }
    },

    go: function (newHash) {
        if (iframe) {
            setIframe(newHash);
        } else {
            location.hash = hash = newHash;
            callback(newHash);
            if (jQuery.browser.safari) {
                states[historyLength] = newHash;
            }
        }
    }
};

jQuery.fn.hash = function (newHash) {
    var fn = this.data(eventDataName);
    if (fn) this.unbind('click', fn);

    if (typeof newHash == 'string') {
        fn = function () { jQuery.hash.go(newHash); return false; };
        this.data(eventDataName, fn);
        this.click(fn);
    }
};
})();
