/*
    HistoryKeeper, version 1.8 (alpha) (2005/12/16)
    Copyright: 2005, Kevin Newman (http://www.unfocus.com/Projects/)
    License: http://creativecommons.org/licenses/LGPL/2.1/
*/
if (typeof unFocus == 'undefined') var unFocus = {};
if (!unFocus.History) unFocus.History = {};
/*
   Class: unFocus.History.Keeper
   A singleton with subscriber interface (<unFocus.Utilities.EventManager>) that keeps a history and provides deep links for Flash and AJAX apps
*/
unFocus.History.Keeper = (function () {
var Keeper = function() {
    // bool: initialize - whether or not the class has been initialized
    var _self = this,
        // IFrame references
        _historyFrameObj, _historyFrameRef,
        // set the poll interval here.
        _pollInterval = 200, _intervalID,
        // get the initial Hash state
        _currentHash = _getHash();
    /*
    method: _getHash
        A private method that gets the Hash from the location.hash property.
     
    returns:
        a string containing the current hash from the url
    */
    function _getHash() {
        return location.hash.substring(1);
    }
    /*
    method: _setHash
        A private method that sets the Hash on the location string (the current url).
    */
    function _setHash($newHash) {
        window.location.hash = $newHash;
    }
    
    /*
    method: _watchHash
        A private method that is called every n miliseconds (<_pollInterval>) to check if the hash has changed.
        This is the primary Hash change detection method for most browsers. It doesn't work to detect the hash
        change in IE 5.5+ or various other browsers. Workarounds like the iframe method are used for those 
        browsers (IE 5.0 will use an anchor creation hack).
    */
    function _watchHash() {
        var $newHash = _getHash();
        if (_currentHash != $newHash) {
            _currentHash = $newHash;
            _self.notifyListeners('historyChange', $newHash);
        }
    }
    // set the interval
    if (setInterval) _intervalID = setInterval(_watchHash, _pollInterval);
    /*
    method: _updateFromHistory
        A private method that is meant to be called only from HistoryFrame.html.
        It is not meant to be used by an end user even though it is accessable as public.
    */
    _self._updateFromHistory = function($hash) {
        _currentHash = $hash;
        _self.notifyListeners('historyChange', $hash);
    };
    
    /*
    method: getCurrent
        A public method to retrieve the current history string
    
    returns:
        The current History Hash
    */
    _self.getCurrentHistory = function() {
        return _currentHash;
    };
    
    /*
    _self.setHistoryFrame = function($frameName) {
        if (!$frameName)
            $frameName = 'unFocusHistoryFrame';
        // _self requires the iframe to have a name attribute, in addition to the id attribute
        // http://www.quirksmode.org/js/iframe.html
        if (!window.frames[$frameName])
            _createHistoryFrame($frameName);
        // reset the reference to the frame using the frames array - for some reason 
        // the other reference can't be used with document.open
        _historyFrame = window.frames[$frameName];
    };*/
    
    /* Method: _createAnchor
        Various browsers may need an achor to be present in the dom for the hash to actually be set,
        so we add one every time a history entry is made.
    */
    function _createAnchor($newHash) {
        if (!_checkAnchorExists($newHash)) {
            var $anchor = document.createElement("a");
            $anchor.setAttribute("name", $newHash);
            if (/MSIE/.test(navigator.userAgent) && !window.opera)
                $anchor = document.createElement('<a name="'+$newHash+'">'+$newHash+"</a>");
            $anchor.style.position = "absolute";
            $anchor.style.top = getScrollY()+"px";
            $anchor.style.left = getScrollX()+"px";
            //$anchor.style.display = 'none';
            //$anchor.innerHTML = $newHash;
            document.body.insertBefore($anchor,document.body.firstChild);
        }
    }
    function _checkAnchorExists($name) {
        var $anchors = document.anchors;
        for (var i = 0; i < $anchors.length; i++)
            if ($anchors[i].name == $name)
                return true;
        return false;
    }
    // Keeps IE 5.0 from scrolling to the top every time a new history is entered.
    // Also retains the scroll position in the history (not on IE 5.5+).
    function getScrollY() {
        var $scrollPos = 0;
        if(typeof window.pageYOffset == 'number' )
            //Netscape compliant (and Omniweb and Safari)
            $scrollPos = window.pageYOffset;
        else if (!window.document.compatMode || window.document.compatMode == 'BackCompat')
            // IE and IE 6
            $scrollPos = document.body.scrollTop;
        else if ( document.documentElement && document.documentElement.scrollTop )
            // standards compliant mode
            $scrollPos = document.documentElement.scrollTop;
        return $scrollPos;
    };
    // clone getScrollY to getScrollX
    eval(String(getScrollY).toString().replace(/Top/g,'Left').replace(/Y/g,'X'));
    
    /**
     * These are the platform specific interface methods. Since some platforms (most notably, IE 5.5+)
     * require almost completely different techniques to create history entries, browser detection is
     * used and the appropriate method is created. It would be nice to use object or feature detection
     * here, but these workarounds deal mostly with very specific bugs and other oddities in the 
     * various implementations. So browser sniffing it is.
     */
    if (navigator.appVersion.indexOf("Safari") != -1 && navigator.userAgent.match(/Version\/([0-9])/)[1] < 3) {
        var _windowHistoryLength = history.length;
            //alert("history length on init: "+history.length);
            _historyArray = [],
            _recentlyAdded = false;
        // set initial history entry
        _historyArray[_windowHistoryLength] = location.hash;
        //alert("hash: "+location.hash);
        //alert("_windowHistoryLength: "+_windowHistoryLength);
        //alert("array: "+_historyArray[_windowHistoryLength]);
        
        _self.addHistory = function($newHash) { // adds history and bookmark hash
            if (_currentHash != $newHash) {
                _createAnchor($newHash);
                _currentHash = $newHash;
                _setHash($newHash); // :NOTE: this doesn't update history.length right away
                _windowHistoryLength = history.length+1;
                //alert("history length: "+history.length);
                _historyArray[_windowHistoryLength] = $newHash;
                _recentlyAdded = true;
                //alert("history change called with :"+ $newHash);
                _self.notifyListeners("historyChange",$newHash);
            }
        };
        
        var _watchHistoryLength = function() {
            if (!_recentlyAdded) { // :NOTE: for some reason the first time this is called, it can't tell that anything has changed.
                var _historyLength = history.length;
                if (_historyLength != _windowHistoryLength) {// && _historyArray[_historyLength]
                    _windowHistoryLength = _historyLength;
                    
                    var $newHash = _historyArray[_windowHistoryLength];
                    if (_currentHash != $newHash) {
                        _currentHash = $newHash;
                        _self.notifyListeners("historyChange", $newHash);
                    }
                }
            } else _recentlyAdded = false;
        };
        
        // since it doesn't work, might as well cancel the location.hash check (checking location.href might work)
        clearInterval(_intervalID);
        // watch the history.length prop for changes
        _intervalID = setInterval(_watchHistoryLength, _pollInterval);
        
    // IE 5.5+ Windows
    } else    if (typeof ActiveXObject != 'undefined' && !window.opera && navigator.userAgent.match(/MSIE (\d\.\d)/)[1] >= 5.5) {
        /*
        method: _createHistoryFrame
            This will create an iframe is none is found (called from <initialize>), and the time 
            has come (if <initialize> has been called.)
            
            This is for IE only for now.
        */
        function _createHistoryFrame() {
            _historyFrameObj = document.createElement('iframe');
            //_historyFrameObj.setAttribute('name', 'HistoryFrame');
            _historyFrameObj.setAttribute('id', 'HistoryFrame');
            _historyFrameObj.style.position = 'absolute';
            _historyFrameObj.style.top = '-900px';
            document.body.insertBefore(_historyFrameObj,document.body.firstChild);
            // get reference to the frame from frames array (needed for document.open)
            // :NOTE: there might be an issue with this according to quirksmode.org
            // http://www.quirksmode.org/js/iframe.html
            _historyFrameRef = frames['HistoryFrame'];
            
            // add base history entry
            _createHistoryHTML(_currentHash);
        }
        
        /*
        method: _createHistoryHTML
            This is an alternative to <_setHistoryHTML> that is used by IE (and others if I can get it to work).
            This method will create the history page completely in memory, with no need to download a new file
            from the server.
        */
        function _createHistoryHTML($newHash/*, $replace*/) {
            /*if (typeof $replace != 'undefined')
                $replace = 'replace';
            else $replace = false; // make sure*/
            _historyFrameRef.document.open('text/html');
            _historyFrameRef.document.write('<html><head><!-- PageID 59 - published by RedDot 7.5 - 7.5.0.27 - 17168 --><!-- PageID 59 - published by RedDot 7.1 - 7.1.1.14 - 17168 --></head><body onl',
                'oad="parent.unFocus.History.Keeper._updateFromHistory(\''+$newHash+'\');">',
                $newHash+'</body></html>');
            _historyFrameRef.document.close();
            if (window.console){console.log("_historyFrameRef.document.close();");}
        }
        
        _self.addHistory = function($newHash) {
            if (window.console){console.log('first addHistory('+$newHash+')');}
            // do initialization stuff on first call
            _createHistoryFrame();
            // replace this function with a new one on first call
            _self.addHistory = function($newHash) { // adds history and bookmark hash
                if (window.console){console.log('second addHistory('+$newHash+')');}
                
                if (_currentHash != $newHash) {
                    // IE will create an entry if there is an achor on the page, but it
                    // does not allow you to detect the state change
                    _currentHash = $newHash;
                    if (window.console){console.log(_currentHash+'='+$newHash);}
                    // sets hash and notifies listeners
                    _createHistoryHTML($newHash);
                }
            };
            // call the first call
            _self.addHistory($newHash);
        };
        
        // anonymouse method - subscribe to self to update the hash when the history is updated
        _self.addEventListener('historyChange', function($hash) { _setHash($hash) });
        
    } else /*if (!/Safari/.test(navigator.userAgent))*/ {
        _self.addHistory = function($newHash) { // adds history and bookmark hash
            if (_currentHash != $newHash) {
                _createAnchor($newHash);
                _currentHash = $newHash;
                _setHash($newHash);
                _self.notifyListeners('historyChange',$newHash);
            }
        };
    }
};
Keeper.prototype = new unFocus.Utilities.EventManager('historyChange');
return  new Keeper();
})();