/*
2012-01-20T09:45:00-08:00 - Sticky sidebar changes: Used compatibility mode for jQuery.
                    Fixed firstRun initialization. Change display mode of debug div
                    when it is enabled (css should default it to display: none). Added
                    some debug variables for WordPress, which started all of theses changes.
2011-12-13T20:16:03-08:00 - Add a jQuery area and code for sticky sidebar. All jq.*
                    methods require jQuery to be loaded. Replace all existing
                    dollar signs ($) with ns to avoid possible jq conficts.
                    Line 955 change to equivalent: ei.target.targetValue.length === null

2011-03-14T09:53:36-08:00 - Changed styles in #tocContent li items. Added hover.
2011-03-03T21:08:53-08:00 - Change buildPageSizeSelector.defaults to larger widths.
2011-01-26T09:26:41-08:00 - Modify buildToC2 to get all text in a heading instead of
					only the first text it finds. Makes links include text that is
                    wrapped as an anchor in in a span. Also encoded output for
                    htmlentities.
2010-09-18T14:31:02-08:00 - Modify buildPageSizeSelector to take a class as a target.
                    The class name string must start with a dot (".targetclass")
2010-08-21T10:59:46-08:00 - Add addRowClass for table formatting.
2010-06-23T13:08:02-08:00 - Cross-platform Security error in Firefox when trying to
                    modify rules. It was because the local style sheet was no
                    longer the last sheet.
                    nsresult: "0x805303e8 (NS_ERROR_DOM_SECURITY_ERR)"
                    Also updated to 4 space tabs.
2010-04-17T16:11:18-08:00 - Fixed note about usage for event.addListener(). Not
                    a functional change.
2010-03-01T16:08:11-08:00 - Added doc.appendText().
2010-02-27T21:35:59-08:00 - Added callback function to buildPageSizeSelector, which
                    gets called after any size change if it exists.
2010-02-27T09:48:41-08:00 - Fixed buildToC2 support for other than h1-h3 in options.
                    Changed the format use non-bulletted first level item and
                    moved first margin out by 1em (first is 0, was 1em), others
                    by .5em to compensate for bullets.
2010-02-26T13:15:08-08:00 - Add ability to pass elem ID to event.addListener
2010-02-17T11:58:35-08:00 - Update addLoadEvent to use current standards.
2010-01-31T14:25:04-08:00 - Fix layout on buildPageSizeSelector to go with
                    new CSS. Should have been done this way oritinally.
                    Fix reload problem displaying selector text twice.
2009-11-30T12:47:30PST - Minor JSlint fixes, mostly == to ===. Add global
                                 comment for JSlint.
2009-08-09T13:26:36PST - Change left margin on ToC2 from 0 to 1em.
2009-05-27T20:26:01PST - Add showToC option to buildToC2.
                                 Add event object with addListenr and removeListener
2009-04-15T15:14:14PST - Add buildPageSizeSelector.
2009-03-23T11:29:13PST - Add V2 of buildToc. Keep support for legacy version.
2009-02-11T19:26:41PST - fix widget.buildToC, problem with innerHTML on anchors,
                                 node.add findFirstText.
2009-02-09T19:56:53PST - add widget.buildToC
2009-01-23T12:59:55PST - add node function deleteNodes and deleteChildren
2008-12-27T15:40:37PST - initial posting

All code in this file runs in the com.YourFriendPaul.util namespace

// deprecated but convenient. Better to use event.addListener( window, 'load', functionname );
addLoadEvent( func )
getElementsByTagNames( elemList, inObj )

node.deleteNodes( node )
node.deleteChildren( node )
node.findFirstText( node )

widget.buildToC( div's-elemId ) -- this requires external CSS
widget.buildToC2( div's-elemId, [ optionsObject ] ) -- CSS generated on execution
widget.buildPageSizeSelector( selectorBoxId [, targetBoxId [, optionsObject ]] )

event.addListener( elem, type, func )
event.removeListener( elem, type, func )

doc.addRowClass( tableId, iter, cName )
doc.appendText( targetId, text, elemType, attrList )

addNamespace( ... ) -- accepts multiple args
nsUsedLevel( root, ns )
isNamespaceUsed( nsIn )

// All jq methods require jQuery.
jq.stickySidebar( options )

*/
// for JSlint:
/*global window alert */
/**********************************************************************/
// create the main namespace for everything
var com = com || {}; // "var" does nothing if com already exists
com.YourFriendPaul = com.YourFriendPaul || {}; // make the object if needed
// Useful for debug messages
if( com.YourFriendPaul.toString() != 'com.YourFriendPaul' ){
    com.YourFriendPaul.toString = function(){ return 'com.YourFriendPaul'; };
}

/**********************************************************************/
/**********************************************************************/
/**********************************************************************/
// Useful for debug messages
com.YourFriendPaul.util = com.YourFriendPaul.util || {};
if( com.YourFriendPaul.util.toString() != 'com.YourFriendPaul.util' ){
    com.YourFriendPaul.util.toString = function(){ return 'com.YourFriendPaul.util'; };
}


// orig.  concept from QuirksMode http://www.quirksmode.org/dom/getElementsByTagNames.html
// elList is an array of elements to search for, or a comma separate list.
// inObj is the parent object to search, document by default.
(function( ns ){

    ns.getElementsByTagNames = function( elList, inObj ) {

        // If the MS extension sourceIndex exists, do nothing.
        // If it doesn't, add one.
        // Adding this allows sorting elements into the order that they
        //  appear in the document.
        function forceSourceIndex(){
            var allElems, eCount;
            // return if it's already supported
            if( document.getElementsByTagName("body")[0].sourceIndex ) { return; }

            allElems = document.getElementsByTagName("*");

            for( eCount=0; eCount<allElems.length; eCount++ ){

                allElems[ eCount ].sourceIndex = eCount;
            }
        }

        // search entire document if object is not specified
        var obj = inObj || document;
        var tagNames;

        if( typeof elList === 'string' ){
            tagNames = elList.split(',');
        }

        else{

            tagNames = elList;
        }

        //alert( 'getElementsByTagNames elList is ' + tagNames );

        var resultArray = [], i, j, tags, testNode;
        // Need support for this IE function to do sorting; build our own index if needed
        forceSourceIndex();

        // for every tag name in list
        for ( i=0; i<tagNames.length; i++ ) {
            tags = obj.getElementsByTagName( tagNames[i] );

            // for every element of that tag type
            for ( j=0; j<tags.length; j++ ) {
                resultArray.push( tags[ j ] );
            }
        }

        testNode = resultArray[0];
        // if no results, return
        if (!testNode){ return []; }

        // alert if sourceIndex hasn't been built already, a debug thing
        if( !testNode.sourceIndex ){ alert( "couldn't make sourceIndex" ); }

        if ( testNode.sourceIndex ) {
            resultArray.sort( function (a,b) {
                    return a.sourceIndex - b.sourceIndex;
            } );
        }
        return resultArray;
    };

})( com.YourFriendPaul.util );

/**********************************************************************/
/**********************************************************************/
/**********************************************************************/
//
// The anonymous functions refered to in the return statement
// must have already been executed before they can be
// added here. That means this needs to go at the end of the file,
// after all the other functions.
//

(function ( ns ){

    /**********************************************************************/
    // Adds functions to window.onload. They will be executed in the order added.
    // use like this:
    //
    // addLoadEvent(nameOfSomeFunctionToRunOnPageLoad);
    //
    // or like this:
    //
    // addLoadEvent(function() {
    //      /* more code to run on page load */
    //    });
    //
    // Originally from http://simonwillison.net/2004/May/26/addLoadEvent/
    //
/*
function addLoadListener(fn)
{

*/
    function addLoadEvent( fn ) {

        var oldonload = null;

        if ( typeof window.addEventListener !== 'undefined' ){

            window.addEventListener('load', fn, false);
        }
        else if (typeof document.addEventListener !== 'undefined'){

            document.addEventListener('load', fn, false);
        } else if (typeof window.attachEvent !== 'undefined'){

            window.attachEvent('onload', fn);
        }
        else if ( typeof window.onload !== 'function' ) {

            window.onload = fn;
        }
        else {

            oldonload = window.onload;

            window.onload = function() {
                if ( oldonload ) { oldonload(); }
                fn();
            };
        }
    }

    ns.addLoadEvent = addLoadEvent;

//    return{
//        addNamespace: com.YourFriendPaul.addNamespace,
//        onLoadAdder: addLoadEvent
//    }

})( com.YourFriendPaul.util );

// ********************************************************
// node functions
//
// This is complicated because it tries to remove memory leaks in IE
//  by removing any function references before deleting the element.
//  It also deletes all children to make sure they are not left in memory.
// ********************************************************
com.YourFriendPaul.util.node = (function(){


    // deletes all children of a node or element
    function deleteChildren( node ){

        if( null === node ) { return; }
        if( node.hasChildNodes() ){

            while( node.lastChild ) {
                this.deleteNodes( node.lastChild );
            }
        }
    }

    // deletes all children and then the node
    function deleteNodes( node ){

        var a, i, l, n;
        if( null === node ) { return; }
        if( node.hasChildNodes() ){

            // find the last lastChild
            while( node.lastChild ) {
                deleteNodes( node.lastChild );
            }
        }

        // remove any functions that are attached
        a = node.attributes;
        if ( a ) {
            l = a.length;
            for ( i = 0; i < l; i += 1 ) {
                n = a[i].name;
                if ( typeof node[ n ] === "function" ) {
                    node.removeAttribute( n );
                    node[ n ] = null;
                }
            }
        }
        node.parentNode.removeChild( node );
    }

    // Finds all the text nodes attached to node
    // Returns null if no text nodes
    function findChildrenText( node ){

        if( null === node || typeof node === "undefined" ) { return null; }
        if( !node.hasChildNodes() ){ return null; }

        //alert( node.nodeName + ", " + node.nodeValue + ", has childern: " + node.childNodes.length );
        var iCount=0, tempNode, foundText='';

        for( iCount=0; iCount< node.childNodes.length; iCount++ ){

            //alert( "child" + iCount + " is type " + node.childNodes[ iCount ].nodeType );
            // append the child if it is a text node
            if( node.childNodes[ iCount ].nodeType === 3  ) {

                foundText += node.childNodes[ iCount ].nodeValue;
                //alert( "foundText: " + foundText );
            }

            else if( node.childNodes[ iCount ].hasChildNodes() ){

                tempNode = findChildrenText( node.childNodes[ iCount ] );
                // alert( "tempNode: " + tempNode );
                // append the child text if it exists
                if( tempNode !== null ) {

                    foundText += tempNode;
                }
            }

        }

        return foundText;
    }


    // Finds the first text node attached to node
    // Returns null if not found
    function findFirstText( node ){

        if( null === node || typeof node === "undefined" ) { return null; }
        if( !node.hasChildNodes() ){ return null; }

        //alert( node.nodeName + ", " + node.nodeValue + ", has childern: " + node.childNodes.length );
        var iCount=0, tempNode;

        for( iCount=0; iCount< node.childNodes.length; iCount++ ){

            //alert( "child" + iCount + " is type " + node.childNodes[ iCount ].nodeType );
            // return the child if it is a text node
            if( node.childNodes[ iCount ].nodeType === 3  ) {
                return node.childNodes[ iCount ];
            }

            if( node.childNodes[ iCount ].hasChildNodes() ){

                tempNode = findFirstText( node.childNodes[ iCount ] );
                // return the child if it is a text node
                if( tempNode !== null && tempNode.nodeType === 3  ) {
                    return tempNode;
                }
            }
        }
        // not found
        return null;
    }

    return {
    findFirstText: findFirstText,
    findChildrenText: findChildrenText,
    deleteChildren: deleteChildren,
    deleteNodes: deleteNodes
    };

})();

// ********************************************************


// replacement for deprecated target attribute
//
// To modify a link that would have used  target=_blank  before it was deprecated, use
// this attribute in the anchor tag:  rel="external".
// Run externalLinks() using onload() for the body element.
// Or better yet, use the "addLoadEvent" function to have
// more than one onload function.
// Origianlly from http://www.sitepoint.com/article/1041/3

com.YourFriendPaul.util.linkTargetBlank = function() {

    if ( !document.getElementsByTagName ) { return; }
    var anchors = document.getElementsByTagName( "a" );
    var anch;
    for ( var i=0; i<anchors.length; i++ ) {

        anch = anchors[ i ];
        if ( anch.getAttribute( "href" ) && anch.getAttribute( "rel" ) === "external" ){
            anch.target = "_blank";
        }
    }
};

com.YourFriendPaul.util.event = {};

(function( ns ){

    ns.addListener = function( elem, eType, funcName ){

        var oldE, onName = "on" + eType, elemName = elem;

        if( typeof elem === 'string' ){

            elem = document.getElementById( elemName );
            if( null === elem ){
                //alert( 'addListener: no name for ' + elemName );
                //alert( 'addListener :' + elem.id + ', ' + eType + ', ' + funcName );
                return elem;
            }
        }
        //alert( 'addListener :' + elem.id + ', ' + eType + ', ' + funcName );
        if( elem.addEventListener ){
            // add event on the 'bubbling' phase
            return elem.addEventListener( eType, funcName, false );
        }

        if( elem.attachEvent ){

            return elem.attachEvent( onName, funcName );
        }

        if( elem[ onName ] !== null ){

            oldE = elem[ onName ];
            elem[ onName ] = function(e){ oldE(e); funcName(e); };
        }

        else {

            elem[ onName ] = funcName;
        }

        return elem[ onName ];
    };

    ns.removeListener = function( elem, eType, funcName ){

        var onName = "on" + eType;

        if ( elem.removeEventListener ){
            return elem.removeEventListener( eType, funcName, false);
        }

        if( elem.detachEvent ){

            return elem.detachEvent( onName, funcName );
        }

        // don't even try with the functions added like onload=...
        return null;

    };


})( com.YourFriendPaul.util.event );


// ===============================================================================
// ===============================================================================

com.YourFriendPaul.util.widget = {};

if( com.YourFriendPaul.util.widget.toString() != 'com.YourFriendPaul.util.widget' ){
    com.YourFriendPaul.util.widget.toString = function(){ return 'com.YourFriendPaul.util.widget'; };
}


(function( ns ){

    //========================= first version ToC code ======================
    // build the ToC and attach it as a child to elemId
    // This first version is deprecated
    ns.buildToC = function( elemId ){

        var TOCstate = 'block';

        var text = {
            showContents: "show page contents",
            hideContents: "hide page contents",
            space: " ",
            tmpOnClickLoc: "tmpOnClickLoc"
        };

        function showhideTOC() {
            TOCstate = (TOCstate === 'none') ? 'block' : 'none';
            var newText = (TOCstate === 'none') ? text.showContents : text.hideContents;
            document.getElementById('contentheader').innerHTML = newText;
            document.getElementById('innertoc').lastChild.style.display = TOCstate;
        }


        // build the ToC and append it to z
        function createLinks( z, tocClassNameBase ){

            var toBeTOCced, tNode, i, headerId, tmp;

            toBeTOCced = com.YourFriendPaul.util.getElementsByTagNames('h1,h2,h3');
            // nothing to show
            if( toBeTOCced.length < 2 ){ return 0; }

            for ( i=0; i<toBeTOCced.length; i++ ) {

                // create an id for the element if one doesn't exist
                headerId = toBeTOCced[i].id || 'TocLink' + i;
                toBeTOCced[i].id = headerId;

                tmp = document.createElement('a');
                tmp.href = '#' + headerId;

                // find the text to use in the ToC
                tNode = com.YourFriendPaul.util.node.findFirstText( toBeTOCced[i] );

                tmp.innerHTML = ( tNode === null ) ? headerId : tNode.nodeValue;
                tmp.className = 'TocItem';
                z.appendChild(tmp);
                if ( toBeTOCced[i].nodeName === 'H1' ){ tmp.className += text.space + tocClassNameBase + 1; }
                if ( toBeTOCced[i].nodeName === 'H2' ){ tmp.className += text.space + tocClassNameBase + 2; }
                if ( toBeTOCced[i].nodeName === 'H3' ){ tmp.className += text.space + tocClassNameBase + 3; }
                if ( toBeTOCced[i].nodeName === 'H4' ){ tmp.className += text.space + tocClassNameBase + 4; }
                if ( toBeTOCced[i].nodeName === 'H5' ){ tmp.className += text.space + tocClassNameBase + 5; }
                if ( toBeTOCced[i].nodeName === 'H6' ){ tmp.className += text.space + tocClassNameBase + 6; }
            }
            // number of entries made
            return i;
        }

        // orig only looked at h2-h6
        function createTOC() {
            var y, x, a, z, entries;
            // create extra do-nothing div to fix display issue in IE6
            y = document.createElement('div');
            x = y.appendChild( document.createElement( "div" ) );
            x.id = 'innertoc';
            a = x.appendChild(document.createElement('span'));
            a.onclick = showhideTOC;
            a.id = 'contentheader';
            a.innerHTML = text.showContents;
            z = x.appendChild(document.createElement('div'));
            z.onclick = showhideTOC;
            entries = createLinks( z, text.tocClassNameBase );
            return entries ? y : null;
        }




        var tocDiv, elem;
        elem = document.getElementById( elemId );
        if( null === elem ) { return; }

        // text is specific to this version, so add it here
        text.tocClassNameBase = "TocIndent";

        tocDiv = createTOC();
        if( null !== tocDiv ){
            elem.appendChild( tocDiv );
            showhideTOC();
            // allow object to be deleted now that we're done with it
            tocDiv = null;
        }
    };
    // end of buildToC

    //========================= second version code ======================
    // build the ToC and attach it as a child to elemId
    // first arg is id of div to put the ToC
    // second arg is an options object

    ns.buildToC2 = function(){

        var TOCstate = 'block';

        var text = {
            showContents: "show page contents",
            hideContents: "hide page contents",
            space: " ",
            tocClassNameBase2: "toc2indent",
            tmpOnClickLoc: "tmpOnClickLoc"
        };


        // can set state with optional parameter, else toggle
        function showhideTOC2( show ) {
            TOCstate = (TOCstate === 'none') ? 'block' : 'none';
            if( show === true || show === false ){
                TOCstate = ( show === true ? 'block' : 'none' );
            }
            var newText = (TOCstate === 'none') ? text.showContents : text.hideContents;
            document.getElementById( "tocContentheader" ).innerHTML = newText;
            document.getElementById( "tocContent" ).style.display = TOCstate;
        }

        // handle clicks on the li elements in the ToC
        function liClick(){

            var attrVal = this.getAttribute( text.tmpOnClickLoc );
            location.replace( attrVal );
            return false;
        }

        // A comma separate sting will be separate into an array of those elements
        // A hyphenated string will fill in the items between the hyphen. Be lazy
        //  and assume this could only be a heading element because those are all
        //  that are numbered.
        function parseTagList( inList ){

            var minH, maxH, count, tagNum = 0;
            var tags = inList.split( /,\s*/ );

            // either no comma separated or less that 2 types
            if( tags.length < 2 ){

                // parse for hyphenated list
                tags = inList.split( /-\s*/ );

                // if only one element, give up and just use it, otherwise assume H list
                if( tags.length > 1 ){

                    minH = parseInt( tags[ 0 ].charAt( 1 ), 10 );
                    maxH = parseInt( tags[ 1 ].charAt( 1 ), 10 );
                    for( count = minH; count <= maxH; count++ ){

                        tags[ tagNum++ ] = 'h' + count;
                    }
                    //alert( inList + '. tags is ' + tags );
                }
            }

            // this will determines the priority of the tags
            //tags = tagList.split( /,\s*/ );
            return tags;
        }

        // build the ToC and append it to container
        // returns number of ToC entries
        function createLinks( container, tocClassNameBase, curSettings ){

            function htmlEntities(str) {
                return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
            }

            var toBeTOCced, tNode, i, headerId, tmp, tmpLi, tags = [], tp, bc;

            // this will determines the priority of the tags
            tags = parseTagList( curSettings.tagTypes );
            toBeTOCced = com.YourFriendPaul.util.getElementsByTagNames( tags );

            // nothing to show
            if( toBeTOCced.length < 2 ){ return 0; }

            for ( i=0; i<toBeTOCced.length; i++ ) {

                // create an id for the element if one doesn't exist
                headerId = toBeTOCced[i].id || 'TocLink' + i;
                toBeTOCced[i].id = headerId;

                tmpLi = document.createElement('li');
                container.appendChild( tmpLi );
                tmp = document.createElement('span');
                // don't modify browser history
                // tmp.href = 'javascript:location.replace("#' + headerId + '");';
                // tmp.href = "javascript:void()";
                // save the onclick handler target
                tmpLi.setAttribute( text.tmpOnClickLoc, "#" + headerId );
                tmpLi.onclick = liClick;

                // find the text to use in the ToC
                // This way only found the first text, so complex pharses were truncated
                // tNode = com.YourFriendPaul.util.node.findFirstText( toBeTOCced[i] );
                // tmp.innerHTML = ( tNode === null ) ? headerId : tNode.nodeValue;

                // Get all text nodes below the existing node
                tNode = com.YourFriendPaul.util.node.findChildrenText( toBeTOCced[i] );
                tmp.innerHTML = ( tNode === null ) ? headerId : htmlEntities( tNode );

                tmp.className = 'TocItem';
                tmpLi.appendChild( tmp );

                // go through the list and compare each element to the tag list
                for( tp = 0; tp < tags.length; tp++ ){

                    // add class for indent formatting; doesn't have to start with h1
                    if ( toBeTOCced[i].nodeName === tags[ tp ].toUpperCase() ){

                        bc = tp+1;
                        tmpLi.className += text.space + tocClassNameBase + bc;
                    }
                }
            }
            // number of entries made
            return i;
        }

        function createToC2( curSettings ){
            var y, x, a, z, entries;
            // create extra do-nothing div to fix display issue in IE6
            y = document.createElement( "div");
            x = y.appendChild( document.createElement( "div" ) );
            x.id = "tocInnertoc";
            a = x.appendChild( document.createElement( "div" ) );
            a.onclick = showhideTOC2;
            a.id = "tocContentheader";
            a.innerHTML = text.showContents;
            z = x.appendChild( document.createElement( "ul"));
            z.id = "tocContent";
            // z.onclick = showhideTOC2;
            entries = createLinks( z, text.tocClassNameBase2, curSettings );
            return entries ? y : null;
        }


        //  elemId  is the top level elem where this will be inserted
        function applyCSS( elemId, curSettings ){

            var lastStyleSheet, rules, sheetNum;
            var settings = curSettings;

            function getFromSettings( name ){

                return settings[ name ];
            }

            // all of this extra work is to allow overrides
            var objList = {
                "#ToCstyle": {
                    "float": "right",
                    "margin": null,
                    "font-size": null,
                    "width": null,
                    "border-style": "solid",
                    "border-width": null,
                    "border-color": null,
                    "background-color": null
                },

                "#tocContentheader": {
                    cursor: "pointer",
                    padding: ".5em",
                    "text-align": "center"
                },

                "#tocContent": {
                    //padding: "1em .5em 2em 1em",
                    margin: "0",
                    padding: ".5em .5em 1em 1em",
                    "border-color": null,
                    "border-style": "solid",
                    "border-width": "1px 0 1px 1px"
                }
            };

            // puts rules at the beginning of the sheet
            function insertRule( sheet, selector, styles ){

                if( sheet.insertRule ){
                    sheet.insertRule( selector + "{" + styles + "}", 0 );
                }
                else {
                    sheet.addRule( selector, styles, 0);
                }
            }

            function buildStylesText( obj ){

                var text = "";
                for ( var name in obj ) {
                    if ( obj.hasOwnProperty( name )) {

                        text += name + ": " + ( obj[ name ] ? obj[ name ] : getFromSettings( name ) ) + "; ";
                    }
                }

                return( text );
            }

            function insertRuleFromObj( sheet, objName ){
                insertRule( sheet, objName, buildStylesText( objList[ objName ] ) );
            }

            // lastStyleSheet = document.styleSheets[ document.styleSheets.length - 1 ];
            // rules = lastStyleSheet.cssRules ?  lastStyleSheet.cssRules : lastStyleSheet.rules;

            sheetNum = document.styleSheets.length - 1;

            while ( sheetNum >= 0 ){

                try {

                    lastStyleSheet = document.styleSheets[ sheetNum ];
                    // look for an access error
                    rules = lastStyleSheet.cssRules ? lastStyleSheet.cssRules : lastStyleSheet.rules;

                } catch( e ) {

                    if( sheetNum > 0 ){

                        sheetNum--;
                        continue;

                    } else {

                        throw( e );
                    }
                }

                // it worked
                break;
            }

            // add rules for each possible element in the list
            insertRule( lastStyleSheet, "." + text.tocClassNameBase2 + "1", "margin-left: 0em; list-style-type: none; font-weight: bold;" );
            insertRule( lastStyleSheet, "." + text.tocClassNameBase2 + "2", "margin-left: 1.5em" );
            insertRule( lastStyleSheet, "." + text.tocClassNameBase2 + "3", "margin-left: 2.5em" );
            insertRule( lastStyleSheet, "." + text.tocClassNameBase2 + "4", "margin-left: 3.5em" );
            insertRule( lastStyleSheet, "." + text.tocClassNameBase2 + "5", "margin-left: 4.5em" );
            insertRule( lastStyleSheet, "." + text.tocClassNameBase2 + "6", "margin-left: 5.5em" );

            insertRule( lastStyleSheet, "#tocContent li",
                        "display: list-item; margin-top: .5em; cursor: pointer; text-decoration: none" );
            insertRule( lastStyleSheet, "#tocContent li:hover",
                        "color: #444444; text-decoration: underline;" );
            insertRule( lastStyleSheet, "#" + elemId, buildStylesText( objList[ "#ToCstyle" ] )  );
            insertRuleFromObj( lastStyleSheet, "#tocContentheader" );
            insertRuleFromObj( lastStyleSheet, "#tocContent" );
        }


        function overrideDefaults( defaults, options ){

            var settings = defaults;
            for ( var oName in options ) {
                if (options.hasOwnProperty( oName )) {

                    for ( var sName in settings ) {
                        if ( settings.hasOwnProperty( sName )) {

                            if( sName === oName ){
                                settings[ sName ] = options[ oName ];
                            }
                        }
                    }
                }
            }
            return settings;
        }

        function run( args ){

            var elem, elemId, tocDiv, settings, options;

            elemId = args[0] || "ToC";
            options = args[1] || {};
            elem = document.getElementById( elemId );

            if( null === elem ) { return; }

            settings = overrideDefaults( ns.buildToC2.defaults, options );

            // if not enough entries, will return null, else the ToC fragment
            tocDiv = createToC2( settings );

            if( null !== tocDiv ){

                elem.appendChild( tocDiv );
                showhideTOC2( settings.showToC );
                applyCSS( elemId, settings );
                tocDiv = null;
            }
        }

        // do this stuff separately to localize variables
        run( arguments );
    };

    ns.buildToC2.defaults = {

        width: "180px",        // #ToC
        margin: "0 0 1em 0",        // #ToC
        "background-color": "#dddddd",    // #ToC
        "border-color": "#999999",    // #ToC
        "border-width": "5px 0",
        "font-size": "80%",    // #ToC
        tagTypes: "h1-h3",
        showToC: true
    };
})( com.YourFriendPaul.util.widget );

// ===============================================================================
// ===============================================================================
//
//    buildPageSizeSelector
//
// ===============================================================================
// build a page size selector box and attach it as a child to elemId
// first arg is id of div to put the selector box
// second arg is id of div to change the size of, or a class where every element
//   containing the class will be modified
// third arg is an options object

(function( ns ){

    function addListener( eType, elem, funcName ){

        com.YourFriendPaul.util.event.addListener( elem, eType, funcName );
    }


    // Determine which items to change if a class was passed instead of an id
    // The first char of the name should be a '.'
    function makeTargetList( className ){

        var targetList = [];
        var allElems = [];
        var eCount, elem;

        if( '.' !== className.charAt( 0 ) ){ return targetList; }

        // eliminate the dot
        className = className.slice( 1 );

        allElems = document.getElementsByTagName("*");

        for( eCount=0; eCount<allElems.length; eCount++ ){

            elem = allElems[ eCount ];

            if( elem.className ){

                if( elem.className.search( "\\b" + className + "\\b" ) != -1 ){

                    targetList.push( elem );
                }
            }
        }

        // alert( targetList.length );
        return targetList;
    }

    // this is what actually changes the size
    ns.sizeClick = function( e ){

        var count;
        //alert( "clicked " + (this === window) ? 'window' : this.id );
        var ei = {
            event: e || window.event,
            target: null,
            keycode: null
        };

        ei.keycode = ei.event.keyCode || ei.event.which;
        ei.target = ei.event.target || ei.event.srcElement;
        if( ei.target.nodeType === 3 ){ // defeat Safari bug
                ei.target = ei.target.parentNode;
        }
        //alert( "clicked " + ei.target.innerHTML + ": " + ei.target.clickValue );
        // targetValue is the element to change the style of. It was added
        // as a parameter of the clicked li element
        if( ei.target.targetValue.length === null ){

            ei.target.targetValue.style.width =  ei.target.clickValue;

        } else { // it's an array, do each element

            // alert( ei.target.targetValue + " - " + ei.target.targetValue.length );

            for( count=0; count<ei.target.targetValue.length; count++ ){

                ei.target.targetValue[ count ].style.width =  ei.target.clickValue;
            }
        }

        // execute a function now that we're done
        if( ei.target.callback !== null ){

            ei.target.callback();
        }
    };

    // this is the main public function
    ns.buildPageSizeSelector = function( ){


        var elemBox, elemTarget, settings, options, elemId, divToChangeId;
        var innerName = "innerWidthChanger";

        var exists = document.getElementById( innerName );
        // The box is there and the page is being reloaded, don't make two!
        if( null !== exists ) { return; }

        // build the html and append it to container
        // selections is an array
        // returns number of selection entries
        function createHtml( elemBox, elemTarget, settings ){

            var tNode, cNode, iSelNum, tmpLi;
            var selections = settings.selections;

            // Create extra do-nothing div to fix display issue in IE6
            // This also gives a way to see if the box already exists
            tNode = document.createElement( "div");
            tNode.id = innerName;
            elemBox.appendChild( tNode );
            cNode = document.createElement( "span" );
            tNode.appendChild( cNode );
            cNode.appendChild( document.createTextNode( "Page width: " ) );
            cNode = document.createElement( "ul" );
            tNode.appendChild( cNode );

            // now add the li buttons to the ul
            for ( iSelNum = 0; iSelNum < selections.length; iSelNum++ ) {

                tmpLi = document.createElement('li');
                cNode.appendChild( tmpLi );
                tmpLi.appendChild( document.createTextNode( selections[ iSelNum ].label ) );
                tmpLi.clickValue = selections[ iSelNum ].value;
                // the element to change
                tmpLi.targetValue = elemTarget;
                // add the callback, will be null if option wasn't used
                tmpLi.callback = settings.callback;
                // need to add the click event handler here
                // it should work because it's already attached to the document
                addListener( "click", tmpLi, ns.sizeClick );
            }
            // number of entries made
            return iSelNum;
        } // createHtml




        //  elemId is the top level container elem
        function applyStyles( elemId, curSettings ){

            var lastStyleSheet, sheetNum, rules;
            var settings = curSettings;

            // all of this extra work is to allow overrides
            // Each element of objList will be a new style rule.
            var objList = {
                "#WidthChanger": {
                    "float": "right",
                    "line-height": "1.25em",
                    "margin": null,
                    "font-size": null,
                    //"width": null,
                    "border-style": "solid",
                    "border-width": null,
                    "border-color": null,
                    "background-color": null
                },

                // essentially the box title
                "label": {
                    margin: "0 .5em 0 0",
                    padding: "0 .25em 0 0",
                    "float": "left",
                    "line-height": "1.25em",
                    color: "#333333"
                },

                "li": {
                    display: "inline",
                    "float": "left",
                    margin: "0 .5em",
                    padding: "0 .25em",
                    "list-style-type": "none",
                    cursor: "pointer",
                    "text-decoration": "underline",
                    "line-height": "1.25em",
                    color: "#333333"
                }
            };

            function applySettings( settings, objList ){

                objList[ "#WidthChanger" ].padding = settings.boxPadding;
                objList[ "#WidthChanger" ].margin = settings.boxMargin;
                objList[ "#WidthChanger" ][ "background-color" ] = settings[ "box-background-color" ];
                objList[ "#WidthChanger" ][ "border-color" ] = settings[ "box-border-color" ];
                objList[ "#WidthChanger" ][ "border-width" ] = settings[ "box-border-width" ];
                objList[ "#WidthChanger" ][ "font-size" ] = settings[ "font-size" ];
                objList.label.color = settings.color;
                objList.li.color = settings.color;
            }

            // puts rules at the beginning of the sheet
            function insertRule( sheet, selector, styles ){

                if( sheet.insertRule ){
                    sheet.insertRule( selector + "{" + styles + "}", 0 );
                }
                else {
                    sheet.addRule( selector, styles, 0);
                }
            }

            function buildStylesText( obj ){

                var text = "";
                for ( var name in obj ) {
                    if ( obj.hasOwnProperty( name )) {

                        //text += name + ": " + ( obj[ name ] ? obj[ name ] : settings[ name ] ) + "; ";
                        text += name + ": " + obj[ name ] + "; ";
                    }
                }

                return( text );
            }

            applySettings( settings, objList );
            //lastStyleSheet = document.styleSheets[ document.styleSheets.length - 1 ];
            //rules = lastStyleSheet.cssRules ?  lastStyleSheet.cssRules : lastStyleSheet.rules;

            // security error workaround for FF.
            sheetNum = document.styleSheets.length - 1;

            while ( sheetNum >= 0 ){

                try {

                    lastStyleSheet = document.styleSheets[ sheetNum ];
                    // look for an access error
                    rules = lastStyleSheet.cssRules ? lastStyleSheet.cssRules : lastStyleSheet.rules;

                } catch( e ) {

                    if( sheetNum > 0 ){

                        sheetNum--;
                        continue;

                    } else {

                        throw( e );
                    }
                }

                // it worked
                break;
            }


            insertRule( lastStyleSheet, "#" + elemId, buildStylesText( objList[ "#WidthChanger" ] ) );
            // need opposite float on innerWidthChanger to work in both IE and FF
            insertRule( lastStyleSheet, "#" + elemId + " #" + innerName, "float: left; line-height: 1.25em" );
            insertRule( lastStyleSheet, "#" + elemId + " #" + innerName + " span", buildStylesText( objList.label ) );
            insertRule( lastStyleSheet, "#" + elemId + " #" + innerName + " ul",
            "display: inline-table; list-style: none; margin: 0; padding: 0" );
            insertRule( lastStyleSheet, "#" + elemId + " #" + innerName + " li", buildStylesText( objList.li ) );
        }
        // end applyStyles

        // copy the value for any properties of options that are also found in defaults
        function overrideValues( defaults, options ){

            var settings = defaults;
            var matchFound = false;
            // loop through the options object
            for ( var oName in options ) {
                if ( options.hasOwnProperty( oName )) {

                    matchFound = false;
                    // look for a match in defaults object
                    for ( var sName in settings ) {
                        if ( settings.hasOwnProperty( sName )) {

                            if( sName === oName ){
                                settings[ sName ] = options[ oName ];
                                matchFound = true;
                            }
                        }
                        // not a failure, so pretend it worked
                        else { matchFound = true; }
                    }

                    if( matchFound !== true ){

                        // want to have a warning if something changes
                        alert( "Undefined or obsolete option used: " + oName );
                    }
                }
            }
            return settings;
        } // end overrideValues

        elemId = arguments[ 0 ] || "widthChanger";
        divToChangeId = arguments[ 1 ] || "allContentWrapper";
        options = arguments[ 2 ] || {};

        elemBox = document.getElementById( elemId );
        if( null === elemBox ) { return; }

        // if it starts with a dot, it's a class
        if( '.' == divToChangeId.charAt( 0 ) ){

            elemTarget = makeTargetList( divToChangeId );

        } else {

            elemTarget = document.getElementById( divToChangeId );
            if( null === elemTarget ) { return; }
        }

        settings = overrideValues( ns.buildPageSizeSelector.defaults, options );

        var tocDiv = createHtml( elemBox, elemTarget, settings );
        if( null !== tocDiv ){

            applyStyles( elemId, settings );
            tocDiv = null;
        }
    }; // end of buildPageSizeSelector()

    ns.buildPageSizeSelector.defaults = {

        boxPadding: "1em",
        boxMargin: "0 0 1.5em 1.5em",
        "box-background-color": "#dddddd",
        "box-border-color": "#999999",
        "box-border-width": "1px",
        "font-size": "83%",
        color: "#333333",    // font color
        selections: [ { label: "750px", value: "750px" }, { label: "900px", value: "900px" },
        { label: "90%", value: "90%" }, { label: "full width", value: "100%" } ],
        'callback': null
    };
})( com.YourFriendPaul.util.widget );

// ===============================================================================
// ===============================================================================
/*
 * This is adds the jq object if needed.
 * All methods attached to jq require jQuery.
 *
 */
com.YourFriendPaul.util.jq = com.YourFriendPaul.util.jq || {};

( function ( ns ) {

    /**
    * stickySidebar( options )
    *
    * Typical use is to wrap the last item in a sidebar with the sidebarStickyDiv.
    * Immediately above that in the HTML is an empty div, sidebarStickyPinDiv.
    * When the page scrolls down the contents of sidebarStickyDiv remain visible,
    * attached either to the top or bottom of the page depending on the size of
    * the sticky part. If a footer, or other bottom element, is specified, the
    * sticky sidebar will stay above that element.
    *
    * Set the maxBottomId to an empty string (or invalid ID) to not scroll up. If
    * the default ID does not exist in the document, that will suffice.
    *
    * If options is a number or string, it is used as the topOffset value.
    * If options is an object, assume it is an array, and parse the possible variables.
    *
    * The options only need to be set once. It is not necessary to do it on every
    *  resize or scroll.
    *
    * Using this options array would set the values the same as the defaults:
    *
    *   var opt = [];
    *   opt[ 'topOffset' ] = '20';
    *   opt[ 'stickyId' ] = 'sidebarStickyDiv';
    *   opt[ 'stickyPinId' ] = 'sidebarStickyPinDiv';
    *   opt[ 'maxBottomId' ] = 'footerContainer';
    *   opt[ 'debugOutId' ] = 'debugMsgs';
    *   opt[ 'useDebugOut' ] = 'false';
    *
    * Returns: nothing.
    */
    ns.stickySidebar = function( options ) {

        // Default values. This makes them act like static variables.
        if ( typeof this.topOffset == 'undefined' ) {

            // A default value for the offset.
            this.topOffset = 20;
            this.stickyId = 'sidebarStickyDiv';
            this.stickyPinId = 'sidebarStickyPinDiv';
            this.maxBottomId  = 'footerContainer';
            // no # for getElemById.
            this.debugOutId  = 'debugMsgs';
            this.useDebugOut  = false;

            // Used to display the debug messages.
            this.firstRun = true;
        }

        // Process any options passed in. If it's not an array, a string or number changes the topOffset.
        if ( typeof options != 'undefined' ) {

            // Change the offset variable if it was given as the only parameter.
            if ( typeof options == 'string' || typeof options == 'number' ) {

                this.topOffset = parseInt( options, 10 );

            } else if ( typeof options == 'object' ) {

                // options is probably an array, let's hope so.

                if ( typeof options[ 'topOffset' ] == 'string'  || typeof options[ 'topOffset' ] == 'number' ) {

                    this.topOffset = parseInt( options[ 'topOffset' ], 10 );
                }

                if ( typeof options[ 'stickyId' ] == 'string' ) {

                    this.stickyId = options[ 'stickyId' ];
                }

                if ( typeof options[ 'stickyPinId' ] == 'string' ) {

                    this.stickyPinId = options[ 'stickyPinId' ];
                }

                if ( typeof options[ 'maxBottomId' ] == 'string' ) {

                    this.maxBottomId = options[ 'maxBottomId' ];
                }

                if ( typeof options[ 'debugOutId' ] == 'string' ) {

                    this.debugOutId = options[ 'debugOutId' ];
                }

                if ( typeof options[ 'useDebugOut' ] == 'boolean' ) {

                    this.useDebugOut = options[ 'useDebugOut' ];

                } else if ( typeof options[ 'useDebugOut' ] == 'string' ) {

                    if ( options[ 'useDebugOut' ].toLowerCase() == 'true' ) {

                        this.useDebugOut = true;

                    } else if ( options[ 'useDebugOut' ].toLowerCase() == 'false' ) {

                        this.useDebugOut = false;
                    }
                }
            }
        }

        // Add # hash mark in front of IDs used by jQuery.
        var needHash = new Array ( 'stickyId', 'stickyPinId', 'maxBottomId' );
        var aCount = needHash.length;

        for ( var i=0; i<aCount; i++ ) {

           var name = needHash[ i ];

           if ( this[name].length > 0 && this[name].charAt( 0 ) != '#' ) {

               this[name] = '#' + this[name];
           }
        }

        // Needed for WordPress and others that use jQuery in compatibility mode.
        $ = jQuery;

        // Make sure the IDs exist.
        // Bottom is not required. || $( this.maxBottomId ).length == 0
        if (
                $( this.stickyId ).length == 0
             || $( this.stickyPinId ).length == 0 ) {

             this.firstRun = false;
             return;
        }

        // How far down to pin the fixed sticky.
        var fixedPosTopOffset = this.topOffset;

        // offset is relative to the document. It has top and left coordinates.
        var sidebarStickyDivOffsetTop = $( this.stickyId ).offset().top;

        // Borders may be used to containg the margins of the div; they must be included in the height calc.
        var sidebarStickyDivHeight = $( this.stickyId ).height()
                + parseInt( $( this.stickyId ).css( 'border-top-width' ), 10 )
                + parseInt( $( this.stickyId ).css( 'border-bottom-width' ), 10 );

    	var docScrollTop = $(document).scrollTop();
    	// This moves independent of the sticky div.
        var sidebarTopPinPoint = $( this.stickyPinId ).offset().top;
        //var sidebarStickyDivOffsetBot = (sidebarStickyDivOffsetTop + sidebarStickyDivHeight);
    	var winH = $( window ).height();
    	var docScrollBot = docScrollTop + winH;

        // If the bottom ID wasn't specified (or isn't valid), use the window bottom for the calculations.
        var pushupOffsetTop = ( $( this.maxBottomId ).length > 0 ) ? $( this.maxBottomId ).offset().top : docScrollBot;

        // How much of the window is not being used by the footer.
        var winHused = docScrollBot - pushupOffsetTop > 0 ? docScrollBot - pushupOffsetTop : 0;
        var winHavail = winH - winHused;
        var docScrollAvail = docScrollTop + winHavail;
        var pinAt = 'nothing';
        var divLarger = ''; // Was used for messages to the debug area.

        // If the sticky sidebar plus offset is bigger than the available window,
        // don't stick at the top so the bottom of sidebar can be visible.
        if ( sidebarStickyDivHeight + fixedPosTopOffset >= winHavail ) {


            // The current top position of the sticky + its height.
            if ( sidebarTopPinPoint + sidebarStickyDivHeight < docScrollAvail ) {

                // This will change the height of the surrounding contianer because
                // it removes it from the flow.
                pinAt = winHavail - sidebarStickyDivHeight + "px";
                $( this.stickyId ).css( 'top', pinAt );
                $( this.stickyId ).css( 'position', 'fixed' );


            } else {

                // Unpin the sticky sidebar, allow it to stay in it's normal position.
                $(  this.stickyId ).css( 'top', '' );
                $( this.stickyId ).css( 'position', '' );
            }
        }

        else { // Sidebar is shorter than winHavail

            if( sidebarTopPinPoint - fixedPosTopOffset >= docScrollTop ) {

                // Unpin the sticky sidebar, allow it to stay in it's normal position.
                $( this.stickyId ).css( 'top', '' );
                $( this.stickyId ).css( 'position', '' );

            } else {

            	// Pin it at the fixed offset location.
                $( this.stickyId ).css( 'position', 'fixed' );
                pinAt = fixedPosTopOffset + "px";
                $( this.stickyId ).css( 'top', pinAt );
            }
        }

        // Height after positioning.
        temp3 = $( '#sidebar' ).height();
        // For debugging this, create a div where this method can change the innerHTML.
        if ( this.useDebugOut === true ) {

            var elem = null;
            elem = document.getElementById( this.debugOutId );

            // If the debug display div is not found the first time, don't bother checking again.
            if ( this.firstRun === true ) {

                this.firstRun = false;

                if ( null === elem ) {

                    // No debug div, so skip this loop in the future.
                    this.useDebugOut = false;

                } else {

                    // Enable display of the debug area.
                    $( '#' + this.debugOutId ).css( 'display', 'block' );
                }
            }

            if ( null !== elem ) {

	            // These are for WP debug only.
	            temp = $( '#sidebar' ).height();
	            temp1 = $( '#content' ).height();
	            temp2 = $( '#mainContainer' ).height();

                elem.innerHTML =
                "sidebarStickyDiv.length = " +  $( this.stickyId ).length +
                "<br />divLarger = " + divLarger +

                "<br />fixedPosTopOffset = " + fixedPosTopOffset +
                "<br />pinAt = " + pinAt +
                "<br />sidebarStickyDivOffsetTop = " + $( this.stickyId ).offset().top +
                "<br />sidebarTopPinPoint = " + sidebarTopPinPoint +
                "<br />sidebarBot compute = " + ($( this.stickyId ).offset().top + sidebarStickyDivHeight) +
                "<br />pushupOffsetTop (footer top) = " + pushupOffsetTop +
                "<br />doc scrolled top = " + docScrollTop +
                "<br />doc scrolled bot = " + docScrollBot +
                "<br />doc scrolled avi = " + docScrollAvail +
                "<br />winH = " + winH  +
                "<br />winHused = " + winHused  +
                "<br />sidebarStickyDiv Height = " + sidebarStickyDivHeight +
                "<br />sidebarStickyDiv border top = " + $( this.stickyId ).css( 'border-top-width' ) +
                // "<br />sidebarSlidey Height = " + $('#sidebarSlidey').height()  +

                // These are for WP debug only.
                "<br />#sidebar height = " + temp  +
                "<br />#content height = " + temp1  +
                "<br />#mainContainer height = " + temp2  +

                "";
            }
        }
    };

} )( com.YourFriendPaul.util.jq );


// ===============================================================================
// ===============================================================================
/*
     This is adds the doc object if needed

     addRowClass()
     appendText()
*/
(function( base )
{

    base.doc = base.doc || {};
    var ns = base.doc;

    if( ns.toString() != base.toString() + '.doc' ){
        ns.toString = function(){ return base.toString() + '.doc'; };
    }

    /**
     * Used to add a class to a table's rows; class can change color of alternate rows
     */
    ns.addRowClass = function( tableId, iter, cName )
    {
        var elem=null, iC=0, rC=0, trList=[];

        if( typeof tableId === 'string' ){

            elem = document.getElementById( tableId );

            if( null === elem ){
                //alert( 'altRowClass: no elem found for ID "' + tableId + '"'; );
                return;
            }
        } else {

            // assume it's already an element?
            elem = tableId;
        }

        trList = elem.getElementsByTagName( "tr" );

        //var text = "count\n";

        rC = iter;
        for( iC = 0; iC < trList.length; iC++ ){

            if( rC === iter ){

                trList[ iC ].className = trList[ iC ].className + " " + cName;
                //text += "\nRow " + iC + " has class: " + trList[ iC ].className;
                rC = 0;
            }
            rC++;
        }
        //alert( text );

    };




    // find index position of {item}
    // in Array {arr} - return -1, if
    // item not found
    // case insensitive
    function findIndex( s, arr )
    {

        var i, idx;
        var last = arr.length;
        var searchFor = s.toLowerCase();

        for ( i = 0; i < last; i++) {

            idx = ( searchFor == arr[i].toLowerCase() ) ? i : -1;
            // quit on first "found"
            if( -1 != idx){ break; }
        }

        return idx;
    }

    function addAttributes( elem, attribs )
    {

        var count = 0;
        var attr = [];

        if( attribs.length <= 0 ){ return; }

        for( count=0; count<attribs.length; count++ ){

            attr = attribs[ count ].split( '=' );

            if( attr.length === 2 ){

                elem.setAttribute( attr[ 0 ], attr[ 1 ] );
            }
        }
    }

    /*
        Append text to a target element, surrounded by an optional elemType tag
        This is only intended as a debugging aid, so limited tags are supported.

        targetId - the id of the element where the text will be appended.
        If it is not a string, it is assumed to be an element instead of an id
        text - the text to append, it is appended to a new element if that
        parameter is included. The text is ignored if the elemType is 'ul'.
        elemType - an optional tag type to wrap the text in.
        attrList - an array of attributes to add to the tag
                    created by elemType. The format is 'attr=value'. Note
                    that the values are not quoted.
    */
    ns.appendText = function( targetId, text, elemType, attrList )
    {

        var targElem = null;
        var wrapElem = null;
        var validElemList = [ 'p', 'div', 'li', 'ul', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ];

        var surElem = elemType || "";
        attrList = attrList || [];

        surElem = surElem.toLowerCase();

        // assume that targetId is the element if it's not a string, otherwise get it from the string
        if( null !== targetId ){

            targElem = ( typeof targetId === 'string' ) ?
                    document.getElementById( targetId ) : targetId;
        }

        if( null === targElem ){
            throw Error( "Invalid target in " + ns.toString() + ".appendText: " + targetId );
        }

        // make the wrapping element if needed
        if( surElem !== '' ){

            if( findIndex( surElem, validElemList ) > -1 ){

                try {

                    wrapElem = document.createElement( surElem );
                }
                catch( e ) {

                    throw Error( "Invalid character in element type in " + ns.toString() + ".appendText: " + surElem );
                }

                targElem.appendChild( wrapElem );

                if( attrList.length > 0 ){

                    addAttributes( wrapElem, attrList );
                }
            }

            else {

                throw Error( "Invalid element type in " + ns.toString() + ".appendText: " + surElem );
            }
        }

        // attach to the wrapper if we made one
        if( null !== wrapElem ){

            // no text on a ul element
            if( surElem != 'ul' ){

                wrapElem.appendChild( document.createTextNode( text ) );
            }

            return wrapElem;
        }

        // append the text directly to the target
        else{

            targElem.appendChild( document.createTextNode( text ) );
            return targElem;
        }
    };

})( com.YourFriendPaul.util );


// ===============================================================================
// ===============================================================================
/*
The namespace stuff is really pretty useless, but it stays because
it's still be using it in some older code
*/

// can add one or multiple namespaces to 'com.YourFriendPaul'
com.YourFriendPaul.util.addNamespace = function(){
    var args = arguments;
    var root = null;	// returns null if nothing is done
    var nameParts, iCount, nameStart, iArg;

    for( iArg=0; iArg<args.length; iArg++ ){

        nameParts = args[ iArg ].split(".");
        root = com.YourFriendPaul;
        // skip over com.YourFriendPaul if it is there
        nameStart = ( nameParts[ 0 ] === "com" && nameParts[ 1 ] === "YourFriendPaul" ) ? 2 : 0;
        for( iCount=nameStart; iCount<nameParts.length; iCount++ ){
            // if root[ nameParts[ iCount ] ] is defined and not null, don't change it, else make it an object
            root[ nameParts[ iCount ] ] = root[ nameParts[ iCount ] ] || {};
            root = root[nameParts[ iCount ]];
        }
    }
    return root;
};

/**********************************************************************/
/**********************************************************************/
/**********************************************************************/
com.YourFriendPaul.util.nsUsedLevel = function( root, ns ){

    var prop;

    if( ns  ){
        for( prop in root ){
            if( null !== prop ){

                if( prop === ns ){
                    return true;
                }
            }
        }
    }

    return  false;
};

// returns true if nsIn exists in com.YourFriendPaul
com.YourFriendPaul.util.isNamespaceUsed = function( nsIn ){

    var root = com.YourFriendPaul;
    var found = false;
    var nameParts, iCount, nameStart;
    var ns = String( nsIn || "" );

    nameParts = ns.split(".");
    // skip over com.YourFriendPaul if it is there
    nameStart = ( nameParts[ 0 ] === "com" && nameParts[ 1 ] === "YourFriendPaul" ) ? 2 : 0;

    for( iCount=nameStart; iCount<nameParts.length; iCount++ ){

        if( nameParts[ iCount ] ){

            if( !root.util.nsUsedLevel( root, nameParts[ iCount ] ) ){

                return found;
            }

            root = root[ nameParts[ iCount ] ];
        }
    }

    found = true;
    return found;

};


