/*************************  Expanding Menu *************************
2-level DHTML navigation menu.
The menu toggles superlink display of sublinks, with specialized styles, and passes individualized responses to menu items.
It can be instantiated as a single (columnar) clamshell menu or as (a horizontal navbar of) multiple drop-down menus.
Can be used in conjunction with ThoughtMesh.js to enable tag-based navigation and remote content selection via pseudo-GET variables in the url.
Version 4.8, modified by Jon Ippolito to improve backward compatibility with multiple menu sites.
UPDATE: enable processing of only a tag link.

************************** Directions for Use **********************
CREATE AN HTML MENU.
Menulinks go in <div>s wrapped in a <div id="yourChoiceOfMenuId">, eg:
<div id="fruit-menu">
	<div id="apples">Apples</div>
		<div id="apples-granny">Granny Smith</div>
		<div id="apples-red">Red Delicious</div>
		<div id="apples-mcintosh">McIntosh</div>
	<div id="oranges">Oranges</div>
		<div id="oranges-valencia">Valencia</div>
		<div id="oranges-navel">Navel</div>
		...
		
CREATE HTML CONTENT.
Content goes in <div>s with ids; this obviates annoying escaped characters in dynamically written content. 
Set the initial style of these <div>s to display: none; clicking on the items in your menu will reveal them.

ADD THE SCRIPT.
Include this script in the <head> of the Web page:
<script type="text/javascript" language="JavaScript" src="tech/ExpandingMenu.js"></script>
Then instantiate the menu with your preferences onload, eg:
<body onload="fruitMenu = new ExpandingMenu({
		menuId: 'fruit-menu', 
		isTwoLevelMenu: true, 
		collapseOnParentClick: true,
		collapseOnOtherParentClick: false ,
		useDefaultContentManagement: true,
		autogenerateInlineNavigation: 'all'/'allButFirst'/'none',
		hasTags: false
	}); 
	fruitMenu.prepareMenu() ;
	loadMesh( ... ) ; // Here's where you can invoke ThoughtMesh.
	addToMenuResponses() ; // Optional; useful for Thoughtmesh.
	fruitMenu.displayInitialCondition(); // Manages anchors. "
>
(Set hasTags to true only if you are using this in combination with ThoughtMesh.js)
You can also instantiate ExpandingMenu with different ids to create multiple menus.

CUSTOMIZE BEHAVIOR.
The easiest approach is to set useDefaultContentManagement to true when you instantiate the menu.
This option requires:
* Menu ids are associated by hyphens, eg the "apples-red" menulink is a child of the "apples" category.
* Content ids must look like "content-apples", "content-apples-granny", etc.
If you prefer to customize ids and behaviors, set useDefaultContentManagement to false and use these methods:
* associateChildren()
* associateContent()
* setInitialCondition()
To trigger menu actions from other links, you can use:
	respondAsIf( "myMenuLinkElementId" )
	OR
	respondAsIf( document.getElementById( "myMenuLinkElementId" ) )
	OR
    document.getElementById( "menulink-fun-3" ).menuItem.respond( {  menuItemEle: document.getElementById(    "menulink-fun-3"    )  } )

STYLE THE MENU.
You can customize these classnames for menu items in your stylesheet:
"menulink-bachelor-unselected" 
"menulink-bachelor-selected" 
"menulink-parent-collapsed-unselected"
"menulink-parent-collapsed-selected"
"menulink-parent-expanded-unselected"
"menulink-parent-expanded-selected"
"menulink-child-unselected"
"menulink-child-selected"
Note that some styles must currently hard-coded into setMenuResponse(). [UPGRADE: extract styles from ExpandingMenu.js.]

ENABLE INLINE NAVIGATION GENERATION.
Enable this via a flag in the *new ExpandingMenu* call. Each paragraph in the div containing the navigation will be given the classname "next-lexia-top". 

ENABLE REMOTE CONTENT SELECTION.
You can enable lexia selection via anchors in the url (eg, http://mysite.com/index.html#introduction).
You can also enable tag selection via the url when ThoughtMesh is active (eg, http://mysite.com/index.html?community&defect#introduction )

ENABLE PRINTING.
Requires this file:
<script type="text/javascript" src="tech/sniff.js"></script>
You must also put an empty file in the directory called empty.html to print.
Styles are assumed to be in a file called tech/print.css.

ENABLE DEBUGGING.
Link to the script debug.js by Jon Ippolito from your HTML file, and follow its instructions to open a debugging window.
*************************************************************************/
// Test for debugging script, and disable the debug function if not found.
if (typeof isDebugOn == "undefined") {
	debug = function() {} ; // Do nothing.
	debugIsOn = false ;
}
function MenuItem(argObj) {
	 // {htmlElement:document.getElementById("myListElement"), responseFunction:myFunction}
	this.htmlElement = argObj.htmlElement;
	this.responseFunction = (argObj.responseFunction)? argObj.responseFunction : "none" ;
	this.htmlElement.style.display = "block";
	this.htmlElement.onclick = this.respond ;
	this.htmlElement.menuItem = this ;
	this.linkType = "bachelor" ; // Default--to be modified later based on associateChildren calls.
	this.htmlElement.onmouseover = actOnMouseover ;
	this.htmlElement.onmouseout = actOnMouseout ;
}
function actOnMouseover(e) {
    return eval( this.menuItem.funMouseover ) ;
}
function actOnMouseout(e) {
    return eval( this.menuItem.funMouseout ) ;
}
MenuItem.prototype.setClasses = function(classObj) {
    this.funMouseover = classObj.argMouseover;
    this.funMouseout = classObj.argMouseout;
	this.classBachelorUnselected = classObj.argBachelorUnselected ;
	this.classBachelorSelected = classObj.argBachelorSelected ;
	if (this.htmlElement.menu.isTwoLevelMenu) { // This argument can be omitted for 1-level menus.
        this.classParentCollapsedUnselected = classObj.argParentCollapsedUnselected ;
        this.classParentCollapsedSelected = classObj.argParentCollapsedSelected ;
        this.classParentExpandedUnselected = classObj.argParentExpandedUnselected ;
        this.classParentExpandedSelected = classObj.argParentExpandedSelected ;
        this.classChildUnselected = classObj.argChildUnselected ;
        this.classChildSelected = classObj.argChildSelected ;
    }
	/* UPGRADE: DO FOLLOWING LATER...MAYBE.
	//You only have to set the selections you want; otherwise they default to the last defined one.
	if (classObj.selected0) {
		this.classSelected0 = classObj.selected0;
		this.classSelected1 = classObj.selected0;
		if (classObj.selected1) {
			this.classSelected1 = classObj.selected1;
		}
	}
	*/
}
MenuItem.prototype.associateContent = function(contentEle){
    // "this" is the JS object whose name is the same as the id for the menulink in question.
	this.content = contentEle ; // The content div (htmlElement) associated with that menulink.
	this.content.menuItem = this ; // Allows you to identify link if you know the content.
	// UPGRADE: Is it a problem that it's the same name as the handle to the menuItem object from the menuItem htmlElement?
}
MenuItem.prototype.associateChildren = function(childrenArr) { //[petMenu.itemManager.itemDante , petMenu.itemManager.itemCritter]
    this.linkType = "parent" ;
	// .children is an array of  JS menuItem objects.
	// You cannot simply assign one array to be equal to another--you must write it out in a loop.
	// I'm trying to write this so you can call associateChildren more than once for the same parent.
	if (!this.children) this.children = new Array() ;
	for (var childNum=0; childNum<childrenArr.length; childNum++) {
		this.children.push( childrenArr[childNum] ) ;
		this.children[this.children.length-1].dad = this ;
		this.children[this.children.length-1].linkType = "child" ;
	}
}
MenuItem.prototype.areChildrenOpen = function() {
	// "this" is the menuItem, ie the JS wrapper of the div in the menu.
	_this = this.htmlElement.id ; debug("_this")
	debug("'areChildrenOpen called:'",1,"start")
    _parentItem = this.htmlElement.id ; debug("_parentItem", 3) ;
    var childItems = this.children ;
	for (var childCounter=0; childCounter < childItems.length; childCounter++) {
		_childItem = childItems[childCounter].htmlElement.id ; debug("_childItem", 2) ;
		_childDisplayStatus = this.children[0].htmlElement.style.display; debug("_childDisplayStatus") ;
	};
	debug("'areChildrenOpen done.'",1,"end")
    return (this.children[0].htmlElement.style.display != "none")? true : false ;
}
MenuItem.prototype.respond = function(argObj) {
	 // argObj can be empty or { menuItemEle: document.getElementById("my-link")}
    debug("'beginning response'",1,"start")
    // If there's no argument, assume "this" is the menuItem's html element, because you clicked on it.
    if (typeof argObj != "undefined") {
        // Firefox will assume argObj is the mouse event if you don't specify it more precisely:
        var menuItemEle = ( typeof argObj.menuItemEle == "undefined" )? this : argObj.menuItemEle ;
    }
    else {
        // IE won't recognize argObj if it's not specified in the function call.
        var menuItemEle = this ;
    }
	// DESELECT ALL STYLES
	menuItemEle.menu.deselectAllItems(); // menuItemEle does not hide children, just deselects them.
	//*********************** SET DISPLAY OF CONTENT *********************** 
	if (menuItemEle.menu.useDefaultContentManagement) {
        //Display only the content of menuItemEle item.
        menuItemEle.menu.hideAllContent();
        menuItemEle.menuItem.content.style.display = "block";
	}
	//*********************** SET DISPLAY AND STYLE OF menuItemEle ITEM'S CHILDREN *********************** 
	//If the item is a superlink, reset the style and/or display of all children.
	// __________ YOU CLICKED ON A PARENT _________
	if ( menuItemEle.menuItem.linkType == "parent" ) {
        debug("'it is a parent'")
		if ( menuItemEle.menu.collapseOnOtherParentClick ) {
			for (var itemCounter=0; itemCounter < menuItemEle.menu.itemManager.length; itemCounter++) {
				var notMyChild = ( menuItemEle.menuItem != menuItemEle.menu.itemManager[itemCounter].dad ) ;
				if ( menuItemEle.menu.itemManager[itemCounter].linkType == "child" && notMyChild ) {
					menuItemEle.menu.itemManager[itemCounter].htmlElement.style.display = "none" ;
				}
			};
		}
        _kidsOpen = menuItemEle.menuItem.areChildrenOpen(); debug("_kidsOpen", 4)
		if ( !menuItemEle.menuItem.areChildrenOpen() ) {		//If the superlink's children are not already open.
            debug("'children are not open'")
            // Display the children and give them unselected style.
			for (var itemNum=0; itemNum < menuItemEle.menuItem.children.length; itemNum++) {
                debug("'displaying child'")
				menuItemEle.menuItem.children[itemNum].htmlElement.style.display = "block";
				menuItemEle.menuItem.children[itemNum].htmlElement.className = menuItemEle.menuItem.children[itemNum].classChildUnselected;
			}
			// Set the style of menuItemEle parent link to expanded and selected.
			debug("'setting className'")
			menuItemEle.menuItem.htmlElement.className = menuItemEle.menuItem.classParentExpandedSelected ;
		}
		else { // If one or more children are already displayed.
            // Hide the children only if appropriate for this implementation.
            _collapseOnParentClick = menuItemEle.menu.collapseOnParentClick ; debug("_collapseOnParentClick", 4)
            if (menuItemEle.menu.collapseOnParentClick) {
                for (var itemNum=0; itemNum < menuItemEle.menuItem.children.length; itemNum++) {
                    menuItemEle.menuItem.children[itemNum].htmlElement.style.display = "none";
                }
                //Set the style of menuItemEle parent link to collapsed and selected.
                menuItemEle.menuItem.htmlElement.className = menuItemEle.menuItem.classParentCollapsedSelected ;
            }
            // If you're using this as a dropdown menu, then you'll want to show all children.
            else {
                for (var itemNum=0; itemNum < menuItemEle.menuItem.children.length; itemNum++) {
                    menuItemEle.menuItem.children[itemNum].htmlElement.style.display = "block";
                }
            }
		}
	}
    // __________ YOU CLICKED ON A CHILD _________
	else if (menuItemEle.menuItem.linkType == "child") {
		//If it's a sublink, reset the style of the sibling sublinks and the superlink.
        // Set the superlink to medium selectedness. UPGRADE: I THINK FOLLOWING DESELECTS ARE REDUNDANT.
        menuItemEle.menuItem.dad.htmlElement.className = menuItemEle.menuItem.dad.classParentExpandedUnselected;
        //Set the siblings to medium selectedness. Display them, just in case the link was not called from the menu.
        for (var itemNum=0; itemNum < menuItemEle.menuItem.dad.children.length; itemNum++) {
            menuItemEle.menuItem.dad.children[itemNum].htmlElement.style.display = "block" ;        
            menuItemEle.menuItem.dad.children[itemNum].htmlElement.className = menuItemEle.menuItem.dad.children[itemNum].classChildUnselected;
        }
        // Set menuItemEle child to selected.
        menuItemEle.menuItem.htmlElement.className = menuItemEle.menuItem.classChildSelected;        
    }
    // __________ YOU CLICKED ON A BACHELOR _________    
    else {
		//If the item is a childless parent.
        menuItemEle.menuItem.htmlElement.className = menuItemEle.menuItem.classBachelorSelected ;	
	}
	//*********************** HANDLE OTHER FUNCTIONS *********************** 
	debug("'handling other functions'")
	if (debugIsOn) {
        try {
            if (menuItemEle.menuItem.responseFun) eval(menuItemEle.menuItem.responseFun) ;
        }
        catch(e) {
            _choked_on_responseFun = menuItemEle.menuItem.responseFun; debug("_choked_on_responseFun", 9)
        }
    }
    else {
        if (menuItemEle.menuItem.responseFun) eval(menuItemEle.menuItem.responseFun) ;    
    }
    debug("'ending response'",1,"end")
	return false;
}
MenuItem.prototype.changeToRolloverStyle = function() {
    this.htmlElement.className = this.classRollover ;
}
MenuItem.prototype.associateResponse = function(responseObj) {
	this.responseFun = responseObj.responseFun;
}
function ExpandingMenu(argObj) { 
	// Use different names for div id and js obj. Sample: {menuId: "navMenuBlock"}
    // Hide menu until you call setInitialCondition later.
    // You probably want to hide it in the css too.
    // This prevents ugly page load.
    this.htmlElement = document.getElementById(argObj.menuId) ;
    this.htmlElement.menu = this ;
    this.id = argObj.menuId + "Obj" ;
    // this.htmlElement.style.display="none";
    // Grab item anchors dynamically.
	this.itemArr = document.getElementById(argObj.menuId).getElementsByTagName("div"); // Was "a".
	this.itemManager = new Array(); // Contains JS wrappers of HTML elements.
	for (var itemNum=0; itemNum < this.itemArr.length; itemNum++) {
		this.itemManager[itemNum] = new MenuItem({ htmlElement:this.itemArr[itemNum] });
		this.itemManager[this.itemArr[itemNum].id] = this.itemManager[itemNum];
		this.itemManager[itemNum].itemNumber = itemNum ;
		this.itemManager[itemNum].htmlElement.menu = this; // I'm not sure it's a good idea to make the handle from the html element...?
	}
	// Is this a two-level menu?
	this.isTwoLevelMenu = (typeof argObj.isTwoLevelMenu == "undefined")? true : argObj.isTwoLevelMenu ;
	// Do you want sublinks to collapse when user clicks on a parent?
	this.collapseOnParentClick = (typeof argObj.collapseOnParentClick == "undefined")? true : argObj.collapseOnParentClick ;
	this.collapseOnOtherParentClick = (typeof argObj.collapseOnOtherParentClick == "undefined")? true : argObj.collapseOnOtherParentClick ;
	// Do you want default content divs, or customized content management?
	this.useDefaultContentManagement = (typeof argObj.useDefaultContentManagement == "undefined")? true : argObj.useDefaultContentManagement ; // Boolean.
	if (this.useDefaultContentManagement) {
		this.prepareMenu() ;
	}
	// Do you want automatic "next" links and permalinks?
	this.autogenerateInlineNavigation = (typeof argObj.autogenerateInlineNavigation == "undefined")? "all" : argObj.autogenerateInlineNavigation ;
	// If there are tags, wait for ThoughtMesh to generate inline navigation. Else generate them here.
	if (this.autogenerateInlineNavigation != "none" && !argObj.hasTags) {
		addInlineNavigationForAll({ menuItem: this.itemManager[0], menuEleId: this.htmlElement.id, hasTags: false }) ;
	}
}
ExpandingMenu.prototype.hideAllContent = function() {
	for (var itemNum=0; itemNum < this.itemManager.length; itemNum++) {
		this.itemManager[itemNum].content.style.display = "none";
	}
}
ExpandingMenu.prototype.setInitialCondition = function(itemObjArg) {
	// UPGRADE: This is being called twice on load :P
	// Set initial menu item based on function argument--it may be overwritten later by remotely selected item.
	this.initialItemObj = itemObjArg ;
	initialId = this.initialItemObj.htmlElement.id; debug("initialId")
	// "this" means the menu.
	/* ___________________ Deselect everything. ___________________ */
	// Hide all child items.
	for (var itemNum=0; itemNum < this.itemManager.length; itemNum++) {
		if ( this.itemManager[itemNum].linkType == "parent" ) {
			for (var childNum=0; childNum < this.itemManager[itemNum].children.length; childNum++) {
				this.itemManager[itemNum].children[childNum].htmlElement.style.display = "none";
			}
		}
    }
    //Deselect all items.
    this.deselectAllItems() ;
	/* ___________________ Check for ThoughtMesh. ___________________ */
	// Check if ThoughtMesh implemented.
	this.hasThoughtMesh = false ;
	for (var i=0; i < document.getElementsByTagName("script").length; i++) {
		script_path = document.getElementsByTagName("script")[i].src ;
		if ( script_path.indexOf("ThoughtMesh") != -1 ) {
			this.hasThoughtMesh = true ;
		}
	};
	/* ___________________ Check for remote content request. ___________________ */
	// Using globals so I can package check in another fun called in correct order onload.
	debug("'Check for remote content request'")
	// Url should look like http://mysite.com/index.html?community&defect#introduction
	this.remoteContentRequested = false ;
	/* ___________________ Check for tags and/or anchor in url. ___________________ */
	this.tagsAreRequested = ( document.location.href.indexOf("?") != -1 );
	this.tagsAreNotAtEnd = ( document.location.href.indexOf("?") != document.location.href.length -1 );
	this.anchorExists = ( document.location.href.indexOf("#") != -1 ) ; 
	this.anchorIsNotAtEnd = ( document.location.href.indexOf("#") != document.location.href.length -1 );
	// Check if tags are requested, and subtract any anchor afterward. ___________________ */
	if (this.tagsAreRequested && this.tagsAreNotAtEnd) {
		this.remotelyRequestedContent = document.location.href.split("?")[1] ;
		_this_remoteContentRequest =	this.remotelyRequestedContent ; debug("_this_remoteContentRequest") ;
		// If url contains an anchor, ExpandingMenu will use that to override which content to show first.
		// The anchor name must be the id of the appropriate menu item (such as "bio-cv").
		if (this.anchorExists && this.anchorIsNotAtEnd) {
			// Subtract tags from anchor string.
			this.remoteTagsAsString = this.remotelyRequestedContent.split("#")[0] ;
			this.remotelyRequestedTags = this.remoteTagsAsString.split("&") ;
			this.remotelyRequestedAnchor = this.remotelyRequestedContent.split("#")[1] ;
		}
		else { // Tags but no anchor.
			this.remotelyRequestedTags = this.remotelyRequestedContent.split("&") ;
		}
		this.remoteContentRequested = true ;
	}
	// Check if anchor present without tags.
	if (this.anchorExists && this.anchorIsNotAtEnd) {
		debug("'Url has an anchor without tags.'")
		// Subtract tags from anchor string.
		this.remotelyRequestedAnchor = this.remotelyRequestedContent = document.location.href.split("#")[1] ;
		this.remoteContentRequested = true ;
	}
}
ExpandingMenu.prototype.displayInitialCondition = function() {
	debug("'displayInitialCondition'")
	_anchorExists = this.anchorExists; debug("_anchorExists")
	_anchorIsNotAtEnd=this.anchorIsNotAtEnd; debug("_anchorIsNotAtEnd")
	_tagsAreRequested=this.tagsAreRequested; debug("_tagsAreRequested")
	_hasThoughtMesh=this.hasThoughtMesh; debug("_hasThoughtMesh")
	/* ___________________ Set remotely requested item. ___________________ */
	if ( typeof this.remotelyRequestedAnchor != "undefined" ) {
		debug("'Remote content requested.'")
		var itemCandidateEle = document.getElementById( this.remotelyRequestedAnchor )
		if ( itemCandidateEle != null ) { // Anchor corresponds to menu item.
			this.initialItemObj = itemCandidateEle.menuItem  ; // Overwrites default initial item from setInitialCondition().
			requestedItem = this.initialItemObj; debug("requestedItem")
		}
	}
	/* ___________________ Set remotely requested tags. ___________________ */
	if ( this.hasThoughtMesh &&  typeof this.remotelyRequestedTags != "undefined" ) {
		remotelyRequestedTags =		this.remotelyRequestedTags ; debug("remotelyRequestedTags") ;
		respondToTag({tagArg: this.remotelyRequestedTags[0], linkType: "replace"}) ;
		remotelyRequestedTags_0 =			this.remotelyRequestedTags[0] ; debug("remotelyRequestedTags_0", 3) ;
		for (var tagCounter=1; tagCounter < this.remotelyRequestedTags.length; tagCounter++) {
			respondToTag({tagArg: this.remotelyRequestedTags[tagCounter], linkType: "plus"}) ;
			remotelyRequestedTags_tagCounter =			this.remotelyRequestedTags[tagCounter] ; debug("remotelyRequestedTags_tagCounter") ;
		};
	}
	/* ___________________ Show appropriate menu item and content. ___________________ */
    // Set a selected item if user requests one.
    if (typeof this.initialItemObj != "undefined") {
        if ( this.initialItemObj.linkType == "bachelor" ) {
            this.initialItemObj.htmlElement.className = this.initialItemObj.classBachelorSelected ;
        }
        else if ( this.initialItemObj.linkType == "parent" ) {
            this.initialItemObj.htmlElement.className = this.initialItemObj.classParentCollapsedSelected ;
        }
        else { // Must be a child.
            // Set this item's style.
            this.initialItemObj.htmlElement.className = this.initialItemObj.classChildSelected ;
            // Show the child's siblings, but leave their style unselected.
			for (var childNum=0; childNum < this.initialItemObj.dad.children.length; childNum++) {
				this.initialItemObj.dad.children[childNum].htmlElement.style.display = "block";
			}
			// Change the parent's style.
            this.initialItemObj.dad.htmlElement.className = this.initialItemObj.dad.classParentExpandedSelected ;			
        }
    }
	else { // No remotely requested content--default to first menu item.
		this.initialItemObj = this.itemManager[0] ;
	}
    // Turn on display of the menu (initially hidden by ExpandingMenu constructor).
    this.htmlElement.style.display="block";
    // Show content div if user chooses the menu's default content management.
    if ( this.useDefaultContentManagement ) {
        this.initialItemObj.content.style.display = "block" ;
    }
	this_hasThoughtMesh =	this.hasThoughtMesh ; debug("this_hasThoughtMesh") ;
	if (this.hasThoughtMesh && this.remoteContentRequested) {
		hideCloud( document.getElementById('thoughtmesh-header-id'), true ); 
		document.getElementById('content').style.display='block'
	}
	return false;
}
ExpandingMenu.prototype.deselectAllItems = function() {
    // Deselects styles of all menu items, without affecting whether children are hidden or shown. 
	for (var itemNum=0; itemNum < this.itemManager.length; itemNum++) {
        if ( this.itemManager[itemNum].linkType == "bachelor" ) {
            this.itemManager[itemNum].htmlElement.className = this.itemManager[itemNum].classBachelorUnselected;
        }
        if ( this.itemManager[itemNum].linkType == "parent" ) {
            if ( this.itemManager[itemNum].areChildrenOpen() ) {
                this.itemManager[itemNum].htmlElement.className = this.itemManager[itemNum].classParentExpandedUnselected ;
            }
            else { // No children open for this parent.
                this.itemManager[itemNum].htmlElement.className = this.itemManager[itemNum].classParentCollapsedUnselected ;
            }
        }
        if ( this.itemManager[itemNum].linkType == "child" ) {
            this.itemManager[itemNum].htmlElement.className = this.itemManager[itemNum].classChildUnselected;
        }
	}
}
ExpandingMenu.prototype.generatePrintableText = function() { // UPGRADE: Add to argObj to customize title, pagebreaks, styles, stylesheet name, etc.
    // UPGRADE: Make the printable version into an object of its own?
    this.printableTextVar = "<h1>" + document.title + "</h1>" ;
    // this.printableTextVar += "<div>" + document.getElementById("image-print").innerHTML + "</div>" ;
    this.printableTextVar += "<h4>This is a dynamically generated synopsis of the site '" + document.title + "' in printable form. " ;
    // UPGRADE: INSERT CUSTOM URL OPTION.
    this.printableTextVar += "You can find the interactive version at " + document.location.href + ".</h4>" ;
	for (var itemNum=0; itemNum < this.itemManager.length; itemNum++) {
            /* UPGRADE: ENABLE PAGEBREAK CODE BELOW:
                // Set any desired pagebreaks manually.
                contentDivs[contentDivs.length-1].extraStyle = "" ;
                var idsWithPageBreaksArr = new Array("desc_var", "desc_ope", "desc_min", "desc_mar")
                for ( var idNum = 0; idNum < idsWithPageBreaksArr.length; idNum++ ) {
                    if ( contentDivs[contentDivs.length-1].id == idsWithPageBreaksArr[idNum] ) {
                        contentDivs[contentDivs.length-1].extraStyle = " style='page-break-before: always;' "
                    }
                }
            */
        // Handle headings.
        // Use this for auto headings: // UPGRADE: ADD TEST FOR PARENT LINKS TO GIVE DIFFERENT h's.
        if (this.itemManager[itemNum].linkType == "child") {
            this.printableTextVar += "<h3>" + this.itemManager[itemNum].htmlElement.innerHTML + "</h3><br /><br />" ;
        }
        else {
            this.printableTextVar += "<h2>" + this.itemManager[itemNum].htmlElement.innerHTML + "</h2><br /><br />" ;
        }
            /* UPGRADE: ENABLE CUSTOM HEADING CODE BELOW?
                // Or set headings manually:
                if ( link == menuHeadingsArr[0].getElementsByTagName("a").length ) {
                    printableTextVar += "<div class='emph2'>r e c e n t &nbsp; S t i l l &nbsp; W a t e r &nbsp; p r o j e c t s</div>";
                }
                if ( link == menuHeadingsArr[0].getElementsByTagName("a").length + menuHeadingsArr[1].getElementsByTagName("a").length ) {
                    printableTextVar += "<div class='emph2'>o n g o i n g &nbsp; S t i l l &nbsp; W a t e r &nbsp; p r o j e c t s</div>";
                }
                if ( link == menuHeadingsArr[0].getElementsByTagName("a").length + menuHeadingsArr[1].getElementsByTagName("a").length + menuHeadingsArr[2].getElementsByTagName("a").length ) {
                    printableTextVar += "<div class='emph2'>r e s e a r c h &nbsp; i n t e r e s t s</div>";
                }
            */
            // Capture what goes under the headings.
            this.itemManager[itemNum].extraStyle = "" ; // UPGRADE: ENABLE THIS?
            this.printableTextVar += "<div " + this.itemManager[itemNum].extraStyle + ">" + this.itemManager[itemNum].content.innerHTML + "</div>" ;
        }
}
ExpandingMenu.prototype.openPrintableWindow = function() {
        if ( (browsers.mac && browsers.ie) || (browsers.ns4) || (browsers.saf) ) {
            alert("We're sorry, your browser does not support this action.\n\nPlease consider upgrading to a standards-compliant browser like Firefox.") ;
            return false;
        }
        // YOU HAVE TO PUT AN IMAGE (POSSIBLY HIDDEN) IN YOUR HTML CALLED image-print.
		this.htmlVar = this.printableTextVar ;
		this.printableWindowVar = window.open('empty.html','printableWindowName','status=yes,scrollbars=yes,resizable=yes,width=750,height=550,top=0,left=200') ;
        this.htmlHeader = "<html><head>" ;
        this.htmlHeader += "<link rel=stylesheet href='tech/print.css' type='text/css' />" ;
        // UNUSED: htmlHeader+= "<link rel=stylesheet href='tech/print_styles.css' type='text/css' media='print' />" ;
        this.htmlHeader += "</head><body>" ;
		this.htmlVar = this.htmlHeader + this.htmlVar + "</body></html>" ;
		this.printableWindowVar.document.open() ;
		this.printableWindowVar.document.write(this.htmlVar) ;
		this.printableWindowVar.document.close() ;
}
//____________________ Methods called by useDefaultContentManagement ___________________ //
ExpandingMenu.prototype.prepareMenu = function() {
    //Associate superlinks with sublinks.
    // Assume the first link may be a parent.
    menuParentCandidateObj = this.itemManager[0] ;
    for (var item=1; item < this.itemManager.length; item++) {
        // If the link id has no hyphen, then assume it may be a parent.
        if (this.itemManager[item].htmlElement.id.indexOf("-") == -1 ) {
            menuParentCandidateObj = this.itemManager[item] ;
        }
        // If the link id has a hyphen, associate it with the parent.
        else {
            menuParentCandidateObj.associateChildren(  [ this.itemManager[item] ]  ) ;
        }
    }
    //Here we automate these assignments for each particular application.
    //Associate all the content using a formulaic approach.
    for (var counter=0; counter < this.itemManager.length; counter++) {
        var contentIdStr = "content-" + this.itemManager[counter].htmlElement.id;
		try {
	        this.itemManager[counter].associateContent( document.getElementById(contentIdStr) );
		}
		catch (e) { // There's a content mismatch in your divs.
			_mismatchError = "ExpandingMenu could not associate menu and content divs for id = <em>" + contentIdStr + "</em>";
			debug("_mismatchError", 6) ;
		}
    }
    // Set classes and custom menu response using formulaic approach based on id nomenclature.
    for (var item=0; item < this.itemManager.length; item++) {
        var itemClassesObj = {
            argBachelorUnselected: "menulink-bachelor-unselected", 
            argBachelorSelected: "menulink-bachelor-selected", 
            argParentCollapsedUnselected: "menulink-parent-collapsed-unselected",
            argParentCollapsedSelected: "menulink-parent-collapsed-selected",
            argParentExpandedUnselected: "menulink-parent-expanded-unselected",
            argParentExpandedSelected: "menulink-parent-expanded-selected",
            argChildUnselected: "menulink-child-unselected",
            argChildSelected: "menulink-child-selected",
            argMouseover: "this.style.cursor = 'pointer'; " ,
            argMouseout: "this.style.cursor = 'default'; "
        }
        /* Use if you want a special style for parent menu items.
        if (this.itemManager[item].linkType != "child") {
            // WAS itemClassesObj.argMouseover = "this.style.cursor = 'pointer'; this.style.border = 'dashed 2px orange'; " ;
            itemClassesObj.argMouseover = "this.style.cursor = 'pointer'; " ;
            itemClassesObj.argMouseout = "this.style.cursor = 'default'; " ;
        }
        */
        this.itemManager[item].setClasses( itemClassesObj ) ;
    }
    // Initialize the menu and choose an initial item to select.
    this.setInitialCondition( this.itemManager[0] );   
}
//____________________ Inline navigation functions ___________________ //
function addInlineNavigationForAll(argObj) { // DEPRECATED? Still used by some older sites.
	// Trap for backward compatibility.
	if ( typeof menuObj == "undefined" ) return ;
	if ( typeof menuObj.autogenerateInlineNavigation == "undefined") menuObj.autogenerateInlineNavigation = "none" ;
	// If ExpandingMenu call requests "all", include the first content visible.
	if (menuObj.autogenerateInlineNavigation == "all") {
		addInlineNavigation({ menuItem: menuObj.itemManager[0], menuEleId: menuObj.htmlElement.id, hasTags: hasTags }) ;
	}
	// Following is true whether "all" or "allButFirst" is requested.
	for (var itemCounter=1; itemCounter < menuObj.itemManager.length; itemCounter++) {
		addInlineNavigation({ menuItem: menuObj.itemManager[itemCounter], menuEleId: menuObj.htmlElement.id, hasTags: hasTags }) ;
	}
}
function addInlineNavigation(argObj) { // Auto-generates "next" links in content. ThoughtMesh will set argObj.hasTags to true.
	// Register which menu this is functioning for. UPGRADE: will this cause problems for multiple menus? nextContent currently requires it :/
	var menuObj = (typeof argObj.menuEleId == "undefined")? document.getElementById("home-menu").menu : document.getElementById(argObj.menuEleId).menu ; // home-menu is the most common ThoughtMesh name. UPGRADE: make this more dynamic.
	var hasTags = (typeof argObj.hasTags == "undefined")? false : argObj.hasTags ;
	
	/*________________________ Determine base url for permalink.________________________*/
	var permalinkURL = location.href.substring( 0, location.href.indexOf("#") ) ;
	permalinkURL += '#' + argObj.menuItem.htmlElement.id ;
	/*__________ 1) Add navigation elements to top. __________*/
	// Create container for permalink and "next" link at top of content div.
	var topNavigationEle = document.createElement("div") ;
	topNavigationEle.paragraph = document.createElement("p") ;
	topNavigationEle.paragraph.className = "next-lexia-top" ; // Note that customization of this class has to happen in the HTML doc because of weird cascading priorities.
	// Add "previous" link.
	topNavigationEle.paragraph.previousLink = document.createElement("a") ;
	topNavigationEle.paragraph.previousLink.href = "#" ;
	topNavigationEle.paragraph.previousLink.onclick = function() { menuObj.nextContent( {menuContentEle: this, menuEleId: argObj.menuEleId, wantPrevious: true } ) } ;
	topNavigationEle.paragraph.appendChild( topNavigationEle.paragraph.previousLink ) ;
	topNavigationEle.paragraph.previousLink.linkText = document.createTextNode("< Previous") ;
	topNavigationEle.paragraph.previousLink.appendChild( topNavigationEle.paragraph.previousLink.linkText ) ;
	// Add spacer.
	topNavigationEle.paragraph.extraText = document.createTextNode(". . . ") ;
	topNavigationEle.paragraph.appendChild( topNavigationEle.paragraph.extraText ) ;
	// Add permalink.
	topNavigationEle.appendChild( topNavigationEle.paragraph ) ;
	topNavigationEle.paragraph.permalink = document.createElement("a") ;
	topNavigationEle.paragraph.permalink.href = permalinkURL ;
	topNavigationEle.paragraph.appendChild( topNavigationEle.paragraph.permalink ) ;
	topNavigationEle.paragraph.permalink.linkText = document.createTextNode("Permalink") ;
	topNavigationEle.paragraph.permalink.appendChild( topNavigationEle.paragraph.permalink.linkText ) ;
	// Add spacer.
	topNavigationEle.paragraph.extraText = document.createTextNode(" for this section. . . ") ;
	topNavigationEle.paragraph.appendChild( topNavigationEle.paragraph.extraText ) ;
	// Add "next" link.
	topNavigationEle.paragraph.nextLink = document.createElement("a") ;
	topNavigationEle.paragraph.nextLink.href = "#" ;
	topNavigationEle.paragraph.nextLink.onclick = function() {  menuObj.nextContent( {menuContentEle: this, menuEleId: argObj.menuEleId, wantPrevious: false } ) } ;
	topNavigationEle.paragraph.appendChild( topNavigationEle.paragraph.nextLink ) ;
	topNavigationEle.paragraph.nextLink.linkText = document.createTextNode("Next >") ;
	topNavigationEle.paragraph.nextLink.appendChild( topNavigationEle.paragraph.nextLink.linkText ) ;
	// Add all top navigation to content div.
	argObj.menuItem.content.insertBefore( topNavigationEle, argObj.menuItem.content.firstChild ) ;
	/*________________________ 2) Add tag and "next" links to bottom of content div.________________________*/
	if ( argObj.hasTags ) {
		var bottomNavigationEle = document.createElement("div") ;
		bottomNavigationEle.className = "next-lexia" ; // Note that customization of this class has to happen in the HTML doc because of weird cascading priorities.
		
		bottomNavigationEle.tagParagraph = document.createElement("p") ;
		bottomNavigationEle.appendChild( bottomNavigationEle.tagParagraph ) ;
		
		bottomNavigationEle.tagParagraph.initialText = document.createTextNode("Tags: ") ;
		bottomNavigationEle.tagParagraph.appendChild( bottomNavigationEle.tagParagraph.initialText ) ;
		/*__________ 2a) Build tag links to bottom of content div. __________*/
		// thoughtMesh.tagsFromItem["print-gift"] is an array of tags associated with that menuItem.
		bottomNavigationEle.tagParagraph.tagLinks = [] ;
		for (var tagCounter=0; tagCounter < thoughtMesh.tagsFromItem[ argObj.menuItem.htmlElement.id ].length; tagCounter++) {
			bottomNavigationEle.tagParagraph.tagLinks[tagCounter] = document.createElement("a") ;
			bottomNavigationEle.tagParagraph.tagLinks[tagCounter].href = "#" ;
			bottomNavigationEle.tagParagraph.tagLinks[tagCounter].onclick = function() { respondToTagInContent(this) } ;
			bottomNavigationEle.tagParagraph.tagLinks[tagCounter].linkText = document.createTextNode( thoughtMesh.tagsFromItem[ argObj.menuItem.htmlElement.id ][tagCounter] );
			bottomNavigationEle.tagParagraph.tagLinks[tagCounter].appendChild( bottomNavigationEle.tagParagraph.tagLinks[tagCounter].linkText ) ;
			bottomNavigationEle.tagParagraph.tagLinks[tagCounter].inBetweenText = document.createTextNode(", ") ;
			bottomNavigationEle.tagParagraph.appendChild( bottomNavigationEle.tagParagraph.tagLinks[tagCounter] ) ;
			if ( tagCounter != thoughtMesh.tagsFromItem[ argObj.menuItem.htmlElement.id ].length -1 ) {
				// Add comma unless you're at the end of the tag list.
				bottomNavigationEle.tagParagraph.appendChild( bottomNavigationEle.tagParagraph.tagLinks[tagCounter].inBetweenText ) ;
			}
		}
		/*__________ 2b) Build a "next" link to the bottom of content div. __________*/
		// Note that there's already a "next" button on top--only tagged sections will be long enough to deserve a bottom one too.
		bottomNavigationEle.nextParagraph = document.createElement("p") ;
		bottomNavigationEle.appendChild( bottomNavigationEle.nextParagraph ) ;
	
		bottomNavigationEle.nextParagraph.nextLink = document.createElement("a") ;
		bottomNavigationEle.nextParagraph.nextLink.href = "#" ;
		bottomNavigationEle.nextParagraph.nextLink.onclick = function() {  menuObj.nextContent( {menuContentEle: this, menuEleId: argObj.menuEleId, wantPrevious: false} )  } ;
		bottomNavigationEle.nextParagraph.appendChild( bottomNavigationEle.nextParagraph.nextLink ) ;
	
		bottomNavigationEle.nextParagraph.nextLink.linkText = document.createTextNode("Next >") ;
		bottomNavigationEle.nextParagraph.nextLink.appendChild( bottomNavigationEle.nextParagraph.nextLink.linkText ) ;
		// Add the entire bottom navigation element.
		argObj.menuItem.content.appendChild( bottomNavigationEle ) ;
	} ;
}
ExpandingMenu.prototype.nextContent = function(argObj) { 
	// argObj looks like { menuContentEle: this [, menuEleId: "home-menu", wantPrevious: true] }. Adds a "next slide" button in your content <div>s.
	var menuObj = (typeof argObj.menuEleId == "undefined")? document.getElementById("home-menu").menu : document.getElementById(argObj.menuEleId).menu ; // home-menu is the most common ThoughtMesh name. UPGRADE: make this more dynamic. Repeated above.
	var wantPrevious = (typeof argObj.wantPrevious == "undefined")? false : argObj.wantPrevious ;
	var menuContentEle = argObj.menuContentEle ;
    // nextContent should be called from <a> in content div. anchorEle is typically "this"; we'll back up the DOM ladder until we get to the content div.
	if ( menuContentEle.nodeName.toLowerCase() != "a" ) { // For backward compatibility. UPGRADE: DEPRECATED?
		window.status = "WARNING: incorrect argument for nextContent function." ;
	}
	while ( menuContentEle.id.indexOf("content-") == -1 ) { // Go up to a parent until the div's id looks like "content".
		menuContentEle = menuContentEle.parentNode ;
	}
	if (!wantPrevious) { // Go to next section.
		menuItemObj = menuObj.itemManager[ (menuContentEle.menuItem.itemNumber + 1) % menuObj.itemManager.length ] ; // Mod tells call at the end of the HTML file to go back to beginning.		
	}
	else { // Go to previous section.
		if ( menuContentEle.menuItem.itemNumber == 0 ) { // Modulo of a negative seems to break.
			menuItemObj = menuObj.itemManager[ menuObj.itemManager.length - 1 ] ;
		}
		else {
			menuItemObj = menuObj.itemManager[ (menuContentEle.menuItem.itemNumber - 1) ] ;
		}
	}
    menuItemObj.respond({ menuItemEle: menuItemObj.htmlElement }) ;
}
function respondAsIf(menuItemArg) {
	if (typeof menuItemArg == "string") { // An id was passed.
		document.getElementById(menuItemArg).menuItem.respond(  { menuItemEle: document.getElementById(menuItemArg) }  ) ;
	}
	else { // The element itself was passed.
	    menuItemArg.menuItem.respond({menuItemEle: menuItemArg}) ;
	}
}
/*__________ Cross-menu functions. __________*/
ExpandingMenu.prototype.setMenuResponse = function(argObj) { // Controls cross-menu behavior when you have instantiated more than one.
    // This particular response function must be called after parent-child relationships established.
	// UPGRADE: Not tested with ThoughtMesh!
    var menuSelected = this.htmlElement.id + "Obj" ;
    var responseFunVar = " " ;
    for ( var menuNum = 0; menuNum < argObj.menusNotSelected.length ; menuNum++ ) {
        responseFunVar += " " + argObj.menusNotSelected[menuNum] + ".setInitialCondition( " + argObj.menusNotSelected[menuNum] + ".itemManager[0] ); " ;
    }
    // Collapse menu selected if you clicked on a child (expand if you didn't).
    if ( this.itemManager[argObj.item].linkType == "child" ) {
        responseFunVar += " " + menuSelected + ".setInitialCondition(" + menuSelected + ".itemManager[0] );" ;
        responseFunVar += " " + menuSelected + ".itemManager[" + argObj.item + "].htmlElement.style.display = 'block' ; " ;
        responseFunVar += " " + menuSelected + ".itemManager[" + argObj.item + "].htmlElement.style.color = 'paleturquoise' ; "; // Emulates visited link color.
        // WAS responseFunVar += " " + menuSelected + ".itemManager[" + argObj.item + "].htmlElement.style.border = 'solid 2px darkslategray' ; " ;        
    }
    for ( var menuNum = 0; menuNum < argObj.menusNotSelected.length ; menuNum++ ) {
        responseFunVar += " " + argObj.menusNotSelected[menuNum] + ".hideAllContent(); " ;
    }
    return responseFunVar ;
}

