/**
 * @fileoverview  This script provides an easy to implement HTML tab solution
 *                that doesn't sacrifice dynamic control. It's based on
 *                TabPane 1.02 code created by Erik Arvidsson
 *                (http://webfx.eae.net/contact.html#erik) or WebFX
 *                (http://webfx.eae.net/). When included in the Head section of
 *                an HTML document, this script will attach its setup routine to
 *                the tail-end of the body onLoad event handler. If you need it
 *                to setup before other functions execute, use the 'rt_exec.js'
 *                library and register the 'setupAllTabs' function before any of
 *                the other JavaScript functions.
 * @author  J.D. Pace <jd@aiminstitute.org>
 * @author  George Neil <george@aiminstitute.org>
 * @author  James Swanson <james@aiminstitute.org>
 * @version 2007-05-30
 */

/*global navigator, document, window, setCookie, _xhr, _container, _dataType, _onload, XMLHttpRequest, escape, Date, Number, RegExp, TabContentAsync, TabPageObject, TabPaneObject, disposeAllTabs, hasSupport, isNaN, setupAllTabs */

/**
 * Determine if the Web browser supports the necessary features
 * @returns A boolean indicating whether the Web browser has the necessary
 *          feature support
 * @version 2007-05-30
 */
function hasSupport() // -------------------------------------------- hasSupport
{
    if (typeof( hasSupport.support ) != 'undefined') {
        return( hasSupport.support );
    }
    var ie55 = /msie 5\.[56789]/i.test( navigator.userAgent );
    hasSupport.support = (typeof( document.implementation ) != 'undefined' && document.implementation.hasFeature( 'html', '1.0' ) || ie55 );
    // IE55 has a serious DOM1 bug... Patch it!
    if (ie55) {
        document._getElementsByTagName = document.getElementsByTagName;
        document.getElementsByTagName = function( sTagName ) {
            if (sTagName == '*') {
                return( document.all );
            } else {
                return( document._getElementsByTagName( sTagName ) );
            }
        };
    }
    return( hasSupport.support );
}

/**
 * @constructor
 * @param   {Object}  DOMNode HTML element containing tab content for this pane.
 * @param   {Boolean} bUseCookie optional boolean indicating whether or not to
                      remember selected tab using cookies or not.  Default is
                      true.
 * @returns The newly constructed TabPaneObject.
 * @type    TabPaneObject
 * @see     GLOBALS#hasSupport
 * @version 2007-05-30
 */
function TabPaneObject( DOMNode, bUseCookie ) // ----------------- TabPaneObject
{
    if (!hasSupport() || !DOMNode) {
        return;
    }
/**
 * Each element of this array is a reference to the DOMNode containing the
 * content for an individual tab.
 * @type Array
*/
    this.pages           = [];
/**
 * If any contained "tab-page" nodes use an "id" attribute, the value of the
 * "id" attribute is used as a key, and the hash value is a reference to the
 * DOMNode containing the tab-page content
 * @type Hash
 * @see TabPaneObject#addTabPage
 * @see TabPaneObject#duplicate
 * @see TabPaneObject#remove
 * @see TabPaneObject#setTabById
 */
    this.pageHash        = {};
/**
 * Not yet implemented. This is to support special wrapping behavior so that
 * when there are more than one line of tabs, the current tab will always
 * appear on the bottom row
 * @type Array
 * @see TabPaneObject#adjustTabRows
*/
    this.tabRows         = [];
/**
 * The index of the currently selected tab. Later in the tab-pane initialization
 * it will be set to the value of the cookie, if "useCookies" is set to true.
 * @type Unsigned Integer
 */
    this.selectedIndex   = 0;
/**
 * Reference to the DOMNode containing the content of the currently selected
 * tab-page.
 * @type DOMNode
 */
    this.selectedChild = null;
/**
 * Reference to the DOMNode of the currently selected tab.
 * @type DOMNode
 */
    this.selectedTab     = null;
/**
 * This is kept from the original codebase. The cookies don't play nicely with
 * nested tab-panes. If using cookies, the associated tab-pane MUST HAVE an
 * "id" attribute.
 * @type Boolean
 */
    this.useCookie       = (bUseCookie ? bUseCookie : true);
/**
 * Stores a reference to the DOMNode defined by the "tab-pane" class.
 * @type DOMNode
 */
    this.element         = DOMNode;
/**
 * Used to guarantee that a dynamically resized tab-pane will never be smaller
 * than the number of pixels defined here.
 * @type Unsigned Integer
 */
    this.minHeight       = 72;   /* default: 72; tab-page will never be smaller
                                       than minHeight pixels */
/**
 * Not implemented. This could be used by the dynamicResizeable method to make
 * sure the tab-pane is never smaller than a specific amount
 * @type Unsigned Integer
 * @see TabPaneObject@dynamicResizeable
 */
    this.minWidth        = 0; // not implemented

/**
 * The tab-pane calculates it's full height, and if the tab-pane causes the
 * browser window to scroll, it will adjust itself so that one of the following
 * conditions is met: the browser window does not scroll, or the rendered
 * tab-pane is "minHeight" pixels tall.
 * @type Unsigned Integer
 * @see TabPaneObject@dynamicResizeable
 */
    this.heightAdjust    = 0; // used by the dynamicResizeable method

    this.element.tabPane = this;
    // add class name tag to class name
    this.element.className = this.classNameTag + ' ' + this.element.className;

/**
 * A reference to the dynamically created row of tabs. All children of class
 * "tab" are wrapped in an anchor tag and stuffed in this container.
 * @type DOMNode
 */
    this.tabRow = document.createElement( 'div' );
    this.tabRow.className = 'tab-row';
    this.element.insertBefore( this.tabRow, this.element.firstChild );

    if (this.useCookie) {
        this.selectedIndex = Number( TabPaneObject.getCookie( 'tabpaneobject_' + this.element.id ) );
        if (isNaN( this.selectedIndex )) {
            this.selectedIndex = 0;
        }
    }

    // remove nodes that aren't tab-pages
    var node = this.element.firstChild;
    while (node) {
        var nextNode = node.nextSibling;
        if (node.className != 'tab-page' && node.className != 'tab-row') {
            node.parentNode.removeChild( node );
        }
        node = nextNode;
    }

    // loop through child nodes and add them
    var cs = this.element.childNodes;
    for (var i = 0; i < cs.length; i++) {
        if (cs[i].nodeType == 1 && cs[i].className == 'tab-page') {
            this.addTabPage( cs[i] );
        }
    }

    // selectAppropriate Child
    this.setSelectedIndex( this.selectedIndex );

    return( this );
}

/**
 * Object prototype for getting the height of a tab-pane.
 * @returns The window height of the tab-pane or NULL.
 * @type    Integer
 * @version 2007-05-30
 */
TabPaneObject.prototype.getWindowHeight = function() {
    if (window.innerHeight) {
        return( window.innerHeight );
    } else if (document.documentElement && document.documentElement.clientHeight) {
        return( document.documentElement.clientHeight );
    } else if (document.body) {
        return( document.body.clientHeight );
    }
    return( null );
};

/**
 * Object prototype for getting the height of a tab-page.
 * @returns The window height of the tab-page or NULL.
 * @type    Integer
 * @version 2007-05-30
 */
TabPaneObject.prototype.getPageHeight = function() {
    if (window.innerHeight) {
        return( window.innerHeight );
    } else if (document.documentElement && document.documentElement.clientHeight) {
        return( document.documentElement.clientHeight );
    } else if (document.body) {
        return( document.body.clientHeight );
    }
    return( null );
};

/**
 * Object prototype for getting the window offset of a tab-pane.
 * @returns The left and top integer offsets of the tab-pane.
 * @type    Array
 * @version 2007-05-30
 */
TabPaneObject.prototype.getTROffset = function() {
    var valueT = 0, valueL = 0;
    var element = this.tabRow;
    do {
        valueT += element.offsetTop  || 0;
        valueL += element.offsetLeft || 0;
        element = element.offsetParent;
    } while (element);
    return( [valueL, valueT] );
};

/**
 * Object prototype for dynamically resizing a tab-pane. This method should
 * be called when the document loads or the window is resized. This method
 * should be renamed to "resize", and take a width and height parameter.
 * @returns The TabPaneObject operated upon.
 * @type    TabPaneObject
 * @version 2007-05-30
 */
TabPaneObject.prototype.dynamicResizeable = function() {
    var tabRow = this.tabRow;
    var arrTL = this.getTROffset( this.tabRow );
    arrTL[1] += this.tabRow.offsetHeight || 20;
    if (!this.heightAdjust) {
        this.heightAdjust = -110;
    }
    var intHeight = this.getWindowHeight() - arrTL[1] + this.heightAdjust;
    if (intHeight < this.minHeight) {
        intHeight = this.minHeight;
    }
    function _setHeight( tpn_div, intHeight )
    {
        var strHeight = intHeight + 'px';
        var cs = tpn_div.childNodes;
        for (var i = 0; i < cs.length; i++) {
            if (cs[i].nodeType == 1 && cs[i].className == 'tab-page') {
                cs[i].style.overflow = 'scroll';
                cs[i].style.height   = strHeight;
            }
        }
    }
    _setHeight( this.element, intHeight );
    // adjust height so window doesn't scroll
    var iter = 0;
    if (document.body.scrollHeight != document.body.clientHeight) {
        this.heightAdjust += document.body.clientHeight - document.body.scrollHeight;
        intHeight = this.getWindowHeight() - arrTL[1] + this.heightAdjust;
        if (intHeight < this.minHeight) {
            intHeight = this.minHeight;
        }
        _setHeight( this.element, intHeight );
    }
    return( this );
};

/**
 * Initialized to 'dynamic-tab-pane-control'
 * @type String
 */
TabPaneObject.prototype.classNameTag = 'dynamic-tab-pane-control';

/**
 * TabPaneObject prototype for setting the selected tab-page index.
 * @version 2007-05-30
 */
TabPaneObject.prototype.setSelectedIndex = function( n ) {
    // find tab-page element
    var cs  = this.element.childNodes;
    var idx = 0;
    for (var iii = 0; iii < cs.length; iii++) {
        if (cs[iii].nodeType == 1 && cs[iii].className == 'tab-page') {
            if (idx == n) {
                if (this.selectedChild) {
                    this.selectedChild.tabPage.hide();
                    this.selectedIndex = null;
                }
                cs[iii].tabPage.show();
                this.selectedChild = cs[iii];
                this.selectedIndex = idx;
                break;
            }
            ++idx;
        }
    }
};

/**
 * TabPaneObject prototype for getting the selected tab-page index.
 * @returns The index of the selected tab-page.
 * @type    Integer
 * @version 2007-05-30
 */
TabPaneObject.prototype.getSelectedIndex = function() {
    return( this.selectedIndex );
};

/**
 * TabPaneObject prototype for selecting a tab-page by ID.
 * @param   {String} strId the identifier of the tab-page to be selected.
 * @returns ? or NULL.
 * @type    Object
 * @version 2007-05-30
 */
TabPaneObject.prototype.setTabById = function( strId ) {
    if (strId) {
        var cs  = this.element.childNodes;
        var idx = 0;
         for (var iii = 0; iii < cs.length; iii++) {
            if ( cs[iii].nodeType == 1 && cs[iii].className == 'tab-page' && cs[iii].id == strId ) {
                if (this.selectedChild) {
                    this.selectedChild.tabPage.hide();
                    this.selectedIndex = null;
                }
                cs[iii].tabPage.show();
                this.selectedChild = cs[iii];
                this.selectedIndex = iii;
                return( cs[iii].tabPage );
            }
        }
    }
    return( null );
};

/**
 * TabPaneObject prototype for adding a tab-page to a tab-pane.
 * @param   {Object} oElement the TabPaneObject to add the tab-page to.
 * @param   {String} strLabel the name for new tab-page.
 * @returns The newly created tab-page object.
 * @type    TabPageObject
 * @see     GLOBALS#hasSupport
 * @version 2007-05-30
 */
TabPaneObject.prototype.addTabPage = function( oElement, strLabel ) {
    if (!hasSupport()) {
        return;
    }
    if (oElement.tabPane == this) {   // already added
        return( oElement.tabPage );
    }
    var n = this.pages.length;
    var tp = new TabPageObject( oElement, this, strLabel );
    tp.tabPane = this;
    this.pages[n]  = tp;
    this.pageHash[ oElement.id ] = tp;
    // Move the tab out of the box
    this.tabRow.appendChild( tp.tab );
    tp.hide();
    if (oElement == this.selectedChild) {
        tp.show();
    }
    return( oElement.tabPage );
};

/**
 * TabPaneObject prototype for duplicating a tab-page.
 * @param   {String} strLabel the name for new tab-page.
 * @param   {String} strId the ID of the tab-page to be duplicated.
 * @returns The newly created TabPageObject, or NULL if the tab-pane does not
 *          have a selected child.
 * @type    TabPageObject
 * @version 2007-05-30
 */
TabPaneObject.prototype.duplicateSelectedTab = function( strLabel, strId ) {
    if (this.selectedChild) {
        return( this.selectedChild.tabPage.duplicate( strLabel, strId ) );
    }
    return( null );
};

/**
 * TabPaneObject prototype for demoting (moving to the right) the currently
 * selected tab and tab-page in the tab-pane.
 * @returns The result of the tab-page demotion method or NULL.
 * @type    TabPageObject
 * @see     TabPageObject#demote
 * @version 2007-05-30
 */
TabPaneObject.prototype.demoteSelectedTab = function() {
    if (this.selectedChild) {
        return( this.selectedChild.tabPage.demote() );
    }
    return( null );
};

/**
 * TabPaneObject prototype for promoting a tab-page in the tab list.
 * @returns The result of the tab-page promotion method or NULL if the tab-pane
 *          has no selected child.
 * @type    TabPageObject
 * @version 2007-05-30
 */
TabPaneObject.prototype.promoteSelectedTab = function() {
    if (this.selectedChild) {
        return( this.selectedChild.tabPage.promote() );
    }
    return( null );
};

/**
 * TabPaneObject prototype for removing a tab-page from the tab list.
 * @returns NULL.
 * @type    void
 * @version 2007-05-30
 */
TabPaneObject.prototype.removeSelectedTab = function() {
    if (this.selectedChild) {
        return( this.selectedChild.tabPage.remove() );
    }
    return( null );
};

/**
 * TabPaneObject prototype for renaming a tab-page in the tab list.
 * @param   {String} str the new name for the tab-page.
 * @returns The result of the tab-page renaming method or NULL.
 * @type    Method
 * @version 2007-05-30
 */
TabPaneObject.prototype.renameSelectedTab = function( str ) {
    if (this.selectedChild) {
        return( this.selectedChild.tabPage.rename( str ) );
    }
    return( null );
};

/**
 * TabPaneObject prototype for selecting the previous tab in the tab list.
 * @returns The result of the tab-page index set method.
 * @type    Method
 * @version 2007-05-30
 */
TabPaneObject.prototype.selectPreviousTab = function() {
    return( this.setSelectedIndex( this.selectedIndex - 1 ) );
};

/**
 * TabPaneObject prototype for selecting the next tab in the tab list.
 * @returns The result of the tab-page index set method.
 * @type    Method
 * @version 2007-05-30
 */
TabPaneObject.prototype.selectNextTab = function() {
    return( this.setSelectedIndex( this.selectedIndex + 1 ) );
};

/**
 * Not yet implemented. This is to support special wrapping behavior so that
 * when there are more than one line of tabs, the current tab will always
 * appear on the bottom row
 * @type Boolean
 * @returns false
 * @see TabPaneObject#tabRows
 * @version 2007-05-30
*/
TabPaneObject.prototype.adjustTabRows = function() {
    var intMax = this.element.getWidth();
    var intCur = 0;
    for (var iii = 0; iii < this.pages.length; iii++) {
        this.pages[iii].tab.getWidth();
    }
    return( false );
};

/**
 * TabPaneObject prototype for disposing of a tab in the tab list.
 * @version 2007-05-30
 */
TabPaneObject.prototype.dispose = function() {
    this.element.tabPane = null;
    this.element = null;
    this.tabRow = null;

    for (var i = 0; i < this.pages.length; i++) {
        this.pages[i].dispose();
        this.pages[i] = null;
    }
    this.pages = null;
};

/**
 * TabPaneObject prototype for setting a tab-pane cookie.
 * @param   {String} sName the key name for the cookie.
 * @param   {String} sValue the value of for the cookie.
 * @param   {Number} nDays the cookie expiration time in days.
 * @version 2007-05-30
 */
TabPaneObject.setCookie = function( sName, sValue, nDays ) {
    var expires = '';
    if (nDays) {
        var d = new Date();
        d.setTime( d.getTime() + nDays * 24 * 60 * 60 * 1000 );
        expires = '; expires=' + d.toGMTString();
    }
    document.cookie = sName + '=' + sValue + expires + '; path=/';
};

/**
 * TabPaneObject prototype for getting a tab-pane cookie value.
 * @param   {String} sName the key name for the cookie.
 * @returns The value of the cookie or NULL.
 * @type    String
 * @version 2007-05-30
 */
TabPaneObject.getCookie = function( sName ) {
    var re = new RegExp( '(;|^)[^;]*(' + sName + ')=([^;]*)(;|$)' );
    var res = re.exec( document.cookie );
    return( (res ? res[3] : null) );
};

/**
 * TabPaneObject prototype for removing a tab-pane cookie.
 * @param   {String} Name the key name for the cookie.
 * @see     #setCookie
 * @version 2007-05-30
 */
TabPaneObject.removeCookie = function( name ) {
    setCookie( name, '', -1 );
};

/**
 * Places asynchronous content in a tab-page.
 * @constructor 
 * @param   {Object} anchorNode a tab-page object.
 * @author  J.D. Pace <jd@aiminstitute.org>
 * @version 2007-05-30
 */
function TabContentAsync( anchorNode ) // ---------------------- TabContentAsync
{
    function _displayContent() {
        if (_xhr.readyState == 4) {
            while (_container.firstChild) {
                _container.removeChild( _container.firstChild );
            }
            if (_dataType == 'text/javascript') {
                eval( _xhr.responseText );
            } else {
                _container.innerHTML = _xhr.responseText;
            }

            if (_onload) {
                switch (typeof( _onload )) {
                case 'string':
                    eval( _onload );
                    break;
                case 'function':
                    _onload();
                    break;
                default:
                    break;
                } // switch ( typeof( _onload ) )
            }
            _active = false;
            _loaded = true;
        }
        return( false );
    }

    function _getHTTPObject() {
        var xmlhttp = false;
        /*@cc_on
        @if (@_jscript_version >= 5)
           try {
              xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
           } catch (e) {
              try {
                 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
              } catch (E) {
                 xmlhttp = false;
              }
           }
        @end @*/
        if (!xmlhttp && ( typeof XMLHttpRequest != 'undefined' )) {
            try {
                xmlhttp = new XMLHttpRequest();
            } catch( e ) {
                xmlhttp = false;
            }
        }
        return( xmlhttp );
    } // _getHTTPObject

    if (anchorNode && ( anchorNode.nodeName == 'A' )) {
        var _href       = anchorNode.href;
        var _cached     = true;
        var _xhr        = null;
        var _active     = false;
        var _container  = null;
        var _loaded     = false;
        var _onload     = null;
        var _dataType   = ( anchorNode.type ? anchorNode.type : 'text/plain' );

        if (anchorNode.id == 'nocache') {
            _cached = false;
        }
        if (anchorNode.onload) {
            _onload = anchorNode.onload;
        }

        this.loadContent = function() {
            if (!_xhr) {
                _xhr = _getHTTPObject();
            }
            if (_xhr && !_active && !( _loaded && _cached )) {
                _active = true;
            _xhr.open( "GET", ( _href
               + (
                  _href.search( /\?/ ) != -1
                     ? '&' + escape( new Date() )
                     : ''
                 ) ), true );
                _xhr.onreadystatechange = _displayContent;
                _xhr.send();
            }
            return( false );
        };

        // build _container node
        if (anchorNode.parentNode) {
            _container = document.createElement( 'div' );
            _container.className = anchorNode.className;
            _container.appendChild( anchorNode.firstChild );
            anchorNode.parentNode.replaceChild( _container, anchorNode );
        }

        return( this );
    }

    return( null );
}

/**
 * Creates a tab-page object.
 * @constructor 
 * @param   {Object} el the HTML element used to represent the tab-page.
 * @param   {Object} tabPane the parent TabPaneObject for the tab-page.
 * @param   {String} strLabel the label for the tab-page.
 * @param   {String} contentURL the URL for the content of the tab-page.
 * @see     GLOBALS#hasSupport
 * @version 2007-05-30
 */
function TabPageObject( el, tabPane, strLabel, contentURL ) // --- TabPageObject
{
    if (!hasSupport() || !el) {
        return;
    }
    this.closeable = false;
    this.enabled   = true;
    this.async     = [];

    this.element = el;
    this.element.tabPage = this;
    this.element.tabPane = tabPane;

    if (strLabel && (strLabel !== '')) {
        var spanLabel = document.createElement( 'span' );
        spanLabel.appendChild( document.createTextNode( strLabel ) );
        spanLabel.className = 'tab';
        el.insertBefore( spanLabel, el.firstChild );
        this.tab = spanLabel;
    } else {
        var cs = el.childNodes;
        for (var i = 0; i < cs.length; i++) {
            if (cs[i].nodeType == 1) { // ELEMENT_NODE
                switch (cs[i].className) {
                case 'tab':
                    this.tab = cs[i];
                    break;
                case 'tab-content-async':
                    var cscn = cs[i].childNodes;
                    // check the domain so we don't cause a security violation.
                    for (var j = 0; j < cscn.length; j++) {
                        if (( cscn[j].nodeName == 'A' ) && ( cscn[j].href )) {
                            var dom = (cscn[j].href.split( '/' ))[2];
                            if (dom == document.domain) {
                                this.async.push( new TabContentAsync( cscn[j] ) );
/*
                            } else {
                                null;
                                // should we warn about different domains?
*/
                            }
                        }
                    }
                    break;
                default:
                    break;
                } // switch className
            } // cs[i] nodeType
        } // for
    }
    // insert a tag around content to support keyboard navigation
    var a = document.createElement( 'A' );
    this.aElement = a;
    a.href = '#';
    a.onclick = function() {
        return( false );
    };
    while (this.tab.hasChildNodes()) {
        a.appendChild( this.tab.firstChild );
    }
    this.tab.appendChild( a );

    // hook up events, using DOM0
    var oThis = this;
    this.tab.onclick = function() {
        oThis.select();
    };
    this.tab.onmouseover = function() {
        TabPageObject.tabOver( oThis );
    };
    this.tab.onmouseout = function() {
        TabPageObject.tabOut( oThis );
    };

    // create inline command center container
    this.commandCenter = document.createElement( 'span' );
    this.commandCenter.className = 'command-center';
    this.tab.appendChild( this.commandCenter );

    return( this );
}

/**
 * tab-page object prototype for duplicating a tab-page.
 * @param   {String} strLabel the label for the duplicated tab-page.
 * @param   {String} strId the identifier for the duplicated tab-page.
 * @returns The duplicated tab-page object.
 * @type    Object
 * @version 2007-05-30
 */
TabPageObject.prototype.duplicate = function( strLabel, strId ) {
    if (!strId || strId === '') {
        var counter = 0;
        do {
            strId = this.element.id + ( ++counter );
        } while (this.tabPane.pageHash[strId]);
    }
    if (!strLabel || strLabel === '') {
        strLabel = this.tab.firstChild.firstChild.data;
    }

    var newNode = this.element.cloneNode( true );
    newNode.id = strId;
    if (this.element.nextSibling) {
        this.element.parentNode.insertBefore( newNode, this.element.nextSibling );
    } else {
        this.element.parentNode.appendChild( newNode, this.element );
    }
    var tp = this.tabPane.addTabPage( newNode, strLabel );

    if (this.tab.nextSibling) {
        this.tabPane.tabRow.insertBefore( tp.tab, this.tab.nextSibling );
    }

    return( tp );
};

/**
 * tab-page object prototype for adding a close tab button to a tab-page.
 * @version 2007-05-30
 */
TabPageObject.prototype.addClose = function() {
    // See if it's already there
    var cs = this.commandCenter.childNodes;
    var hasClose = false;
    for (var iii = 0; iii < cs.length; iii++) {
        if (cs[iii].nodeType == 1 && cs[iii].className == 'close') {
            hasClose = true;
            break;
        }
    }
    if (!hasClose) {
        var oThis = this;
        var closeNode = document.createElement( 'a' );
        closeNode.className = 'close';
        closeNode.href = '#';
        closeNode.onclick = function() {
            oThis.remove();
            return( false );
        };
        closeNode.appendChild( document.createTextNode( 'X' ) );
        this.commandCenter.appendChild( closeNode );
    }
    return( this );
};

/**
 * tab-page object prototype for grabbing an HTTP object.
 * @version 2007-05-30
 */
TabPageObject.prototype.getHTTPObject = function() {
    var xmlhttp = false;
    /*@cc_on
    @if (@_jscript_version >= 5)
       try {
          xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
       } catch (e) {
          try {
             xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
          } catch (E) {
             xmlhttp = false;
          }
       }
    @end @*/
    if (!xmlhttp && ( typeof XMLHttpRequest != 'undefined' )) {
        try {
            xmlhttp = new XMLHttpRequest();
        } catch( e ) {
            xmlhttp = false;
        }
    }
    return( xmlhttp );
}; // getHTTPObject

/**
 * tab-page object prototype for showing a tab-page.
 * @version 2007-05-30
 */
TabPageObject.prototype.show = function() {
    var el = this.tab;
    var s = el.className + ' selected';
    s = s.replace(/ +/g, ' ');
    el.className = s;
    this.element.style.display = 'block';

    // AJAX Awareness
    for (var iii = 0; iii < this.async.length; iii++) {
        this.async[iii].loadContent();
    }
    return( this );
};

/**
 * tab-page object prototype for hiding a tab-page.
 * @version 2007-05-30
 */
TabPageObject.prototype.hide = function() {
    var el = this.tab;
    var s = el.className;
    s = s.replace(/ selected/g, '');
    el.className = s;

    this.element.style.display = 'none';
    return( this );
};

/**
 * tab-page object prototype for selecting a tab-page.
 * @version 2007-05-30
 */
TabPageObject.prototype.select = function() {
    var offset = 0;
    var node = this.tab;
    while (node.previousSibling) {
        node = node.previousSibling;
        offset++;
    }
    return( this.tabPane.setSelectedIndex( offset ) );
};

/**
 * tab-page object prototype for removing a tab-page.
 * @version 2007-05-30
 * @returns NULL.
 */
TabPageObject.prototype.remove = function() {
    // select appropriate tab if deleting currently selected tab
    if (this.tabPane.selectedChild == this.element) {
        this.hide();
        var arrNodes = [];
        var cs = this.tabPane.element.childNodes;
        var nextNode = null;
        var prevNode = -1;
        for (var iii = 0; iii < cs.length; iii++) {
            if (cs[iii].nodeType == 1 && cs[iii].className == 'tab-page') {
                arrNodes.push( cs[iii] );
                if (cs[iii] == this.element) {
                    prevNode = arrNodes.length - 2;
                    nextNode = arrNodes.length;
                }
            }
        }
        if (nextNode < arrNodes.length) {
            this.tabPane.setSelectedIndex( this.tabPane.setSelectedIndex + 1 );
        } else if (prevNode > -1) {
            this.tabPane.setSelectedIndex( this.tabPane.setSelectedIndex - 1 );
        } else {
            this.tabPane.selectedChild = null;
            this.tabPane.selectedIndex = null;
        }
    }
    // maintain and collapse tabPane.tabPage array
    var arrNewPages = [];
    for (var jjj = 0; jjj < this.tabPane.pages.length; jjj++) {
        if (this.tabPane.pages[jjj] != this) {
            arrNewPages.push( this.tabPane.pages[jjj] );
        }
    }
    this.tabPane.pages = arrNewPages;
    this.tabPane.pageHash[ this.element.id ] = null;
    // Clean up DOM
    this.tab.parentNode.removeChild( this.tab );
    this.element.parentNode.removeChild( this.element );
    return( null );
};

/**
 * tab-page object prototype for moving a tab-page to the start of the tab list.
 * @version 2007-05-30
 */
TabPageObject.prototype.moveToStart = function() {
    // firstChild refers to the tabRow DIV
    this.element.parentNode.insertBefore( this.element,
                                          this.element.parentNode.firstChild.nextSibling );
    this.tab.parentNode.insertBefore( this.tab, this.tab.parentNode.firstChild );
};

/**
 * tab-page object prototype for moving a tab-page to the end of the tab list.
 * @version 2007-05-30
 */
TabPageObject.prototype.moveToEnd = function() {
    this.element.parentNode.appendChild( this.element );
    this.tab.parentNode.appendChild( this.tab );
};

/**
 * tab-page object prototype for renaming a tab-page.
 * @param   {String} str the new name for the tab-page.
 * @version 2007-05-30
 */
TabPageObject.prototype.rename = function( str ) {
    // find text node contained by <A> in tab, as that will contain the label
    if (str && (str !== '')) {
        this.tab.firstChild.firstChild.data = str;
    }
    return( this );
};

/**
 * tab-page object prototype for moving a tab-page to the start of the tab list.
 * @version 2007-05-30
 */
TabPageObject.prototype.promote = function() {
    if (this.element.previousSibling) {
        this.swap( this.element.previousSibling );
    }
    return( this );
};

/**
 * tab-page object prototype for demoting a tab-page in the tab list.
 * @version 2007-05-30
 */
TabPageObject.prototype.demote = function() {
    if (this.element.nextSibling) {
        this.element.nextSibling.tabPage.swap( this.element );
    }
    return( this );
};

/**
 * tab-page object prototype for swapping tab-pages in the tab list.  This
 * function only swaps appropriately among siblings.
 * @param   {Object} objElem the tab-page to swap.
 * @version 2007-05-30
 */
TabPageObject.prototype.swap = function( objElem ) {
    if (objElem && (objElem.className == this.element.className)) {
        objElem.parentNode.insertBefore( this.element, objElem );
        objElem.tabPage.tab.parentNode.insertBefore( this.tab, objElem.tabPage.tab );
    }
    return( this );
};

/**
 * tab-page object prototype for selecting the next tab-page in the tab list.
 * @returns The result of the select next tab method.
 * @type    Method
 * @version 2007-05-30
 */
TabPageObject.prototype.selectNext = function() {
    return( this.tabPane.selectNextTab() );
};

/**
 * tab-page object prototype for selecting the previous tab-page in the tab
 * list.
 * @returns The result of the select previous tab method.
 * @type    Method
 * @version 2007-05-30
 */
TabPageObject.prototype.selectPrevious = function() {
    return( this.tabPane.selectPreviousTab() );
};

/**
 * tab-page object prototype for disposing of a tab-page.
 * @version 2007-05-30
 */
TabPageObject.prototype.dispose = function() {
    this.aElement.onclick = null;
    this.aElement = null;
    this.element.tabPage = null;
    this.tab.onclick = null;
    this.tab.onmouseover = null;
    this.tab.onmouseout = null;
    this.tab = null;
    this.tabPane = null;
    this.element = null;
};

/**
 * tab-page object prototype for changing mouseOver style of a tab-page label.
 * @param   {Object} tabpage the tab-page object.
 * @version 2007-05-30
 */
TabPageObject.tabOver = function( tabpage ) {
    var el = tabpage.tab;
    var s = el.className + ' hover';
    s = s.replace(/ +/g, ' ');
    el.className = s;
};

/**
 * tab-page object prototype for changing mouseOut style of a tab-page label.
 * @param   {Object} tabpage the tab-page object.
 * @version 2007-05-30
 */
TabPageObject.tabOut = function( tabpage ) {
    var el = tabpage.tab;
    var s = el.className;
    s = s.replace(/ hover/g, '');
    el.className = s;
};

/**
 * Initializes all tab-panes and tab-pages.
 * @see GLOBALS#hasSupport
 * @version 2007-05-30
 */
function setupAllTabs() // ---------------------------------------- setupAllTabs
{
    if (!hasSupport()) {
        return;
    }
    var all = document.getElementsByTagName( '*' );
    var l = all.length;
    var tabPaneRe = /tab\-pane/;
    var tabPageRe = /tab\-page/;
    var cn, el;
    var parentTabPane;
    for (var i = 0; i < l; i++) {
        el = all[i];
        cn = el.className;
        // no className
        if (cn === '') {
            continue;
        }
        // uninitiated tab-pane
        if (tabPaneRe.test( cn ) && !el.tabPane) {
            var tp = new TabPaneObject( el );
//            new TabPaneObject( el );
        }
        // unitiated tab-page with a valid tab-pane parent
        else if (tabPageRe.test( cn ) && !el.tabPage && tabPaneRe.test( el.parentNode.className )) {
            el.parentNode.tabPane.addTabPage( el );
        }
    }
}

/**
 * Disposes of all tab-panes and tab-pages.
 * @see GLOBALS#hasSupport
 * @version 2007-05-30
 */
function disposeAllTabs() // ------------------------------------ disposeAllTabs
{
    if (!hasSupport()) {
        return;
    }
    var all = document.getElementsByTagName( '*' );
    var l = all.length;
    var tabPaneRe = /tab\-pane/;
    var cn, el;
    var tabPanes = [];
    for (var i = 0; i < l; i++) {
        el = all[i];
        cn = el.className;
        // no className
        if (cn === '') {
            continue;
        }
        // tab-pane
        if (tabPaneRe.test( cn ) && el.tabPane) {
            tabPanes[tabPanes.length] = el.tabPane;
        }
    }
    for (var j = tabPanes.length - 1; j >= 0; j--) {
        tabPanes[j].dispose();
        tabPanes[j] = null;
    }
}

// initialization hook up
if (typeof window.addEventListener != 'undefined') {
    // DOM2
    window.addEventListener( 'load', setupAllTabs, false );
} else if (typeof window.attachEvent != 'undefined') {
    // IE
    window.attachEvent( 'onload', setupAllTabs );
    window.attachEvent( 'onunload', disposeAllTabs );
} else {
    if (window.onload) {
        var oldOnload = window.onload;
        window.onload = function( e ) {
                            oldOnload( e );
                            setupAllTabs();
                        };
    } else {
        window.onload = setupAllTabs;
    }
}
