
	var gblArrayCat = new Array(); // Linear array to hold all Categories
	var gblArrayCatLevel = new Array(); // Multidimensional array to hold all of the categories based on level (note the capitalized ALL)
	var gblArrayCatByID = new Array(); // Linear array where each item can be accessed by the unique identifier
	var gblArrayCatByAccessOrder = new Array(); // Linear array where items early in the array were accessed most recently
	var gblArrayCatBookmarked = new Array(); // Array of the categories that were bookmarked
	var gblSelectedCat = null; // Remember what Category object is currently selected.
	var GblBoolSuppressBoldLevel1 = false; // If true, then we do not want to automatically bold top level items
	var GblModuleSeparatorHeight = 0; // Height of an image to be used to separate modules.
	var GblMaxLevelToDisplay = 999; // Maximum level of content to display.  By default, display all levels.
	var GblBoolLastLevelSameBullets = false; // Whether or not we should use the same bullets if we've reached level GblMaxLevelToDisplay.  If true, then the bullet will be the same even if there are children underneath.
	if (!window.gblCoursePageName) {
		var gblCoursePageName = "CoursePage.asp"; // The pagename of the course page launcher.  For the first course it was "CoursePage.asp".  For the second, it's "CoursePagePopup.asp".
	}
	QQ = '"';
	
	// Initialize the arrays for gblArrayCatLevel
	for (var i=0;i<10;i++) { // gblArrayCatLevel will support 10 levels
		gblArrayCatLevel[i] = new Array();
	}
	
	// ALIASES
	// A - AddNewCategory
	// T - ToggleExpand
	// W - WriteCategories
	// E - LoadProductPage
	function A(strName, intLevel, boolExpanded, intID, strCGID, strStepNo, strDifficultyLevel, intHideFromNav) {AddNewCategory(strName, intLevel, boolExpanded, intID, strCGID, strStepNo, strDifficultyLevel, intHideFromNav);} // A - AddNewCategory
	function T(intIndex) {ToggleExpand(intIndex, false);} // T - ToggleExpand
	function W() {WriteCategories();} // W - WriteCategories
	function E(intIndex) {LoadProductPage(intIndex);} // E - LoadProductPage

	// Preload images
	function newImage(arg) {
		if (document.images) {
			rslt = new Image();
			rslt.src = arg;
			return rslt;
		}
	}		

	// Pre-cache the images
	if (document.images) {
		var image1 = newImage(GblImagesDir + GblImage_BulletSelectedDot);
		var image2 = newImage(GblImagesDir + GblImage_BulletUnselectedDot);
		var image3 = newImage(GblImagesDir + GblImage_BulletSelectedOpenedParent);
		var image4 = newImage(GblImagesDir + GblImage_BulletUnselectedOpenedParent);
		var image5 = newImage(GblImagesDir + GblImage_BulletSelectedClosedParent);
		var image6 = newImage(GblImagesDir + GblImage_BulletUnselectedClosedParent);
	}













	
	// Short name for adding a new Category to gblArrayCat
	function AddNewCategory(strName, intLevel, boolExpanded, intID, strCGID, strStepNo, strDifficultyLevel, intHideFromNav) {
		var objNew = new objCat(strName, intLevel, boolExpanded, intID, strCGID, strStepNo, strDifficultyLevel, intHideFromNav); // Create the new object
		var objParent;
		var arrayCat;
		var arrayParent;
		
		// Add the Cat to the main array
		gblArrayCat[gblArrayCat.length] = objNew; 
		
		// Add the Cat to the ID array
		gblArrayCatByID[intID] = objNew;
		
		// Add the Cat to the level
		arrayCat = gblArrayCatLevel[intLevel]; // Get the right level array
		arrayCat[arrayCat.length] = objNew;
		
		// If we have sub or subsub category, define the parent, set the arraySub...
		if (intLevel > 0) {
			arrayParent = gblArrayCatLevel[intLevel-1]; // Get parent array
			objParent = arrayParent[arrayParent.length-1]; // Get parent object
			objNew.Parent = objParent; // Set parent attribute for this object
			objParent.arraySub[objParent.arraySub.length] = objNew; // Set children pointer for the parent
		}
		// Set the expanded property to true if it is to be expanded.  Will also make this category Selected.
		if (boolExpanded) {
			ExpandByID(intID)

		}	
	}	
	
	// Expands this category and any parents.  The category object is located by the ID.
	function ExpandByID(intID) {
		var objC = gblArrayCatByID[intID];
		var objParent = objC.Parent;

		objC.Expanded = true;

		gblSelectedCat = objC; // Remember the most recently selected category
		objC.Selected = true; // Make this category selected
		objC.ChildSelected = true;
			
		// Expand all parents
		while (objParent) {
			objParent.ChildSelected = true;
			objParent.Expanded = true;
			objParent = objParent.Parent;
				
		}
	}
	
	// Accepts a comma separated list of IDs and sets the Visited attribute of each object to true.
	function SetVisitedByIDList(strIDList) {
		var arrayIDs = new Array();
		var objC;
		
		arrayIDs = strIDList.split(",");
		
		// For each ID given, get the object and set the Visited attribute to true
		for (var i=0;i<arrayIDs.length;i++) {
			objC = gblArrayCatByID[arrayIDs[i]];
			if (objC) {
				objC.Visited = true;
		
				// Add to the gblArrayCatByAccessOrder array.  Assumes that the list is sorted by more recent items first.
				gblArrayCatByAccessOrder[gblArrayCatByAccessOrder.length] = objC;
			}
		}
	}
	
	// Accepts a comma separated list of IDs and sets the Visited attribute of each object to true.
	function SetBookmarkedByIDList(strIDList) {
		var arrayIDs = new Array();
		var objC;
		
		arrayIDs = strIDList.split(",");
		
		// For each ID given, get the object and set the Visited attribute to true
		for (var i=0;i<arrayIDs.length;i++) {
			objC = gblArrayCatByID[arrayIDs[i]];
			if (objC) {
				objC.Bookmarked = true;
				
				// Add to the gblArrayCatBookmarked array.
				gblArrayCatBookmarked[gblArrayCatBookmarked.length] = objC;
			}
		}
	}



	
	// Determine the Threads Visited
	// Start with the lowest level items and then work the way up.
	function SetVisitedThread() {
		var arrayCat;
		var objC;
		var arraySub;
		var boolChildrenVisited;
		
		for (var i=gblArrayCatLevel.length-1; i>=0; i--) {
			arrayCat = gblArrayCatLevel[i];
			for (var j=0;j<arrayCat.length;j++) {
				objC = arrayCat[j];
				arraySub = objC.arraySub;
				boolChildrenVisited = true;
				
				if (objC.Visited) { // If this object has been visited, check if all the children have been visited as well.
					// Determine if all of the children have been visited.
					for (var k=0;k<arraySub.length;k++) {
						if (!(arraySub[k].ThreadVisited)) {
							boolChildrenVisited = false;
						}
					}
					
					if (boolChildrenVisited) {
						objC.ThreadVisited = true;
					} else {
						objC.ThreadVisited = false;
					}
				}

			}
		}
	}
	
	
	
	
	// Category object definition
	function objCat(strName, intLevel, boolExpanded, intID, strCGID, strStepNo, strDifficultyLevel, intHideFromNav) {
		this.Name = strName;
		this.Level = intLevel;
		this.Parent = null; // To be determined later.  Will be a Category object.
		this.ID = intID;
		this.arraySub = new Array();
		this.Selected = false;
		this.ChildSelected = false; // Whether or not a child of this category (or this category itself) is selected.
		this.Expanded = false;
		this.Visited = false;
		this.ThreadVisited = false;
		this.CGID = strCGID;
		this.StepNo = strStepNo;
		this.DifficultyLevel = strDifficultyLevel;
		this.Bookmarked = false;
		this.BookmarkDate = "";
		this.HideFromNav = intHideFromNav;
//		this.AccessOrderNo = 0; // The order that this item was accessed.  Lower order no's are more recent.

	}

	// Returns the HTML for the Categories section
	function GetCategoriesHTML() {
		var strHTML = "";
		var objParent;
		var intIndentWidth;
		var strIcon; // Either + or - depending on whether the item is Expanded
		var strCustomStyle1 = "";
		var strName = "";


		strHTML += "<table cellpadding=0 cellspacing=0 class='" + GblTableClass + "' border=0 width=" + GblMinOverallWidth + " >"; // NS4 needs border=0 or else some spacing between cells occurs
		strHTML += "<tr><td><IMG SRC='" + GblImagesDir + "spacer.gif' height=5 border=0></td></tr>"; // Needed for NS6
		if (GblHeaderImageName != "") {
			strHTML += "<tr><td><IMG SRC='" + GblImagesDir + GblHeaderImageName + "' border=0></td></tr>";
		}
		if (GblStrHeaderName != "") { // Text title (e.g. Course title)
			strHTML += "<tr><td align='center'>" + GblStrHeaderName + "</td></tr>";
		}
		strHTML += "<tr><td><IMG SRC='" + GblImagesDir + "spacer.gif' height=5 border=0></td></tr>"; // Needed for NS6
		for (var i=0;i<gblArrayCat.length;i++) {
			objC = gblArrayCat[i];
			objParent = objC.Parent;

			// Determine the bullet that we should use
			if (objC.Expanded) { // If we have an expanded item
				if (objC.ChildSelected) {
					strIcon = "<img src='" + GblImagesDir + GblImage_BulletSelectedOpenedParent + "' height=" + GblExpandHeight + " border=0>";
				} else {
					strIcon = "<img src='" + GblImagesDir + GblImage_BulletUnselectedOpenedParent + "' height=" + GblExpandHeight + " border=0>";
				}
			} else { // Item with no children
				if (objC.ChildSelected) {
					strIcon = "<img src='" + GblImagesDir + GblImage_BulletSelectedClosedParent + "' height=" + GblExpandHeight + " border=0>";
				} else {
					strIcon = "<img src='" + GblImagesDir + GblImage_BulletUnselectedClosedParent + "' height=" + GblExpandHeight + " border=0>";
				}
			}
//			strIcon = "<img src='" + GblImagesDir + GblImage_BulletSelectedOpenedParent + "' height=" + GblExpandHeight + " border=0>";

			if ((DetermineExpanded(objC)) && (objC.Level <= GblMaxLevelToDisplay)) { // Display if parent or any grandparent is expanded
				// Determine the level
//				intIndentWidth = 0;
				intIndentWidth = GblIndentWidth*objC.Level+1;
//				for (var j=0;j<objC.Level;j++) {
//					intIndentWidth += GblIndentWidth;
//				}
				strHTML += "<tr><td align='top' ";
				if (!document.layers) { // Add the indent style only if we're not dealing with Netscape 4
					strHTML += "style='padding-left:" + intIndentWidth + "px;'";
				}
				strHTML += ">";
				
				// Add space above top-level items.  This separates modules.
				if ((!objC.Parent) && (GblModuleSeparatorHeight > 0)) { // If this is the parent..
					strHTML += "<img src='" + GblImagesDir + "spacer.gif' height=" + GblModuleSeparatorHeight + " border=0><br>";
				}

				// Get the name
				strName = objC.Name;
				if (!objC.Parent) { // Bold top level items
					strName = "<b>" + strName + "</b>";
				}
				if (GblBoolSuppressBoldLevel1) { // If this value is true, then do not bold the name.
					strName = objC.Name;
				}
				
				// Here is the opportunity to alter the name of the category.  Each course can override the CustomizeName function.
				strName = CustomizeName(strName, objC);
				
				// Determine if this category is a Beginner or Advanced topic
//				if (objC.DifficultyLevel >= 2) {
//					strName += " <img src='" + GblImagesDir + GblImage_Visited + "' height=10 border=0>";
//				}


				strHTML += "<table cellpadding=0 cellspacing=0 border=0><tr><td align='right' nowrap valign='top'>"; // Each line will consist of its own table so that any text wrapped will align correctly

				if (document.layers) { // NS 4 (we have to manually indent)
					strHTML += "<img src='" + GblImagesDir + "spacer.gif' border=0 width=" + intIndentWidth + " height=" + GblExpandHeight + ">";
				}
				
				// Write out the bullet
				if (objC.arraySub.length) { // If we have children, show the expandable icon
					if (objC.Level < GblMaxLevelToDisplay) { // We're allowed to expand this one
						strHTML += "<a href='javascript:void' onclick='T(" + i + ");return false;'>" + strIcon + "</a>";
					} else { // This is the last level.  Don't allow this to be expanded
						if (GblBoolLastLevelSameBullets) { // We want all bullets to be the same for the last level even if we can expand
														if (objC.ChildSelected) {
															strHTML += "<img src='" + GblImagesDir + GblImage_BulletSelectedDot + "' height=" + GblExpandHeight + " border=0>";
														} else {
															strHTML += "<img src='" + GblImagesDir + GblImage_BulletUnselectedDot + "' height=" + GblExpandHeight + " border=0>";
														}
						} else { // Show different bullets for last level based on whether we can expand this item or not
							strHTML += strIcon;
						}
					}
				} else { // No children - not expandable.
					if (objC.ChildSelected) {
						strHTML += "<img src='" + GblImagesDir + GblImage_BulletSelectedDot + "' height=" + GblExpandHeight + " border=0>";
					} else {
						strHTML += "<img src='" + GblImagesDir + GblImage_BulletUnselectedDot + "' height=" + GblExpandHeight + " border=0>";
					}
				}
					
				strHTML += "</td><td valign='top'>";
				
				
				
				if (objC.Selected) { // This category is selected
					strHTML += "<span class='" + GetStyle(objC) + "'";
					strHTML += ">" + strName + "</span>";
				} else { // This category is not selected
					strHTML += "<a href='javascript:void;' onclick='E(" + i + ");return false;' class='" + GetStyle(objC) + "'";
					if ((objC.DifficultyLevel > 1) && (!objC.ThreadVisited)) { // Advanced pages will be in Red (if not visited before)
						strHTML += " id='RedFont'";
					}
					strHTML += ">" + strName + "</a>";
				}
				strHTML += "<br></td></tr></table>";
				strHTML += "</td></tr>";
			}

		}

		strHTML += "</table>";
				
		strHTML += "<IMG SRC='" + GblImagesDir + "spacer.gif' width=" + GblMinOverallWidth + " height=1 border=0>"; // Need something at the end so IE in a Mac doesn't screw up.  (Image also used to set minimum size for the div tag)
		
		

		
		
		return strHTML;
	}

	// Customizes the appearance of the category name.  Can be overridden by the individual course to suit needs
	function CustomizeName(strName, objC) {
		return strName;
	}


	// Returns the style that should be used to dislay the title of the category
	function GetStyle(objC) {
		if (objC.Selected) {
			if (objC.ThreadVisited) { // Selected, Visited
				return GblSelectedClassVisited;
			} else { // Selected, Unvisited
				return GblSelectedClass;
			}
		} else {
			if (objC.ThreadVisited) { // UnSelected, Visited
				return GblRegularClassVisited;
			} else { // UnSelected, Unvisited
				return GblRegularClass;
			}
		}
	}

	

	// Determines if the parent (AND any parent of parent) is expanded.
	function DetermineExpanded(objC) {
		var objParent = objC.Parent;
		var objChild = objC;
		var boolExpanded = true;
		
		while (objParent) {
			objParent = objChild.Parent;
			if (!objParent.Expanded) {
				boolExpanded = false;
			}
			objChild = objParent;
			objParent = objChild.Parent;
		}
		return boolExpanded;
	}
	
	// Writes out the HTML content 
	function WriteCategories() {
		if (document.all) { // IE 4+
			eval("document.all." + GblDivName1).innerHTML = GetCategoriesHTML();
		} else if (document.getElementById){ // NS 6 (supports new DOM)
			document.getElementById(GblDivName1).innerHTML = GetCategoriesHTML();
		} else if (document.layers) { // NS 4
//			eval("window.document." + GblDivName1 + ".document." + GblDivName2 + ".document.write(GetCategoriesHTML());");
//			eval("window.document." + GblDivName1 + ".document." + GblDivName2 + ".document.close(GetCategoriesHTML());");
//alert(GetCategoriesHTML());
			eval("window.document." + GblDivName1 + ".document.write(GetCategoriesHTML());");
			eval("window.document." + GblDivName1 + ".document.close();");
		}
		
		//alert(GetCategoriesHTML());
	}
	


	
	// intIndex in gblArrayCat
	function ToggleExpand(intIndex, boolExpand) {
		var boolValueToSet;
		var objC = gblArrayCat[intIndex];
		

		if (boolExpand) { // Expand this Category no matter what
			boolValueToSet = true;
		} else { // Toggle the expand
			boolValueToSet = !objC.Expanded;
		}
		
		SetAllCollapsed();
		
		objC.Expanded = boolValueToSet;
		ExpandParents(objC.Parent);
		
		// Refresh the display
		WriteCategories();
	}

	// Will set the Expanded attribute to true for all parents
	function ExpandParents(objC) {
		while (objC) {
			objC.Expanded = true;
			objC = objC.Parent;	
		}
	}

	// Will set the Expanded attribute for all categories to false
	function SetAllCollapsed() {
		var objC;
		
		for (var i=0;i<gblArrayCat.length;i++) {
			objC = gblArrayCat[i];
			objC.Expanded = false;
		}
	}
	
	
	// Navigates to the previous page
	// Assumes that boolCoursePlus has already been defined
	function NavPrev() {
		var objC = gblArrayCatByID[GblCurrentLOID];
		var intStepNo = objC.StepNo;
		
		// If we're at step 1, then exit because there is no previous page
		if (intStepNo <= 1) {
			return;
		}
		
		// Determine the previous StepNo
		if (boolCoursePlus) { 
			intStepNo = objC.StepNo - 1;
		} else { // Skip Course Plus pages
			boolKeepLooping = true;
			while ((intStepNo > 0) && (boolKeepLooping)) {
				intStepNo = objC.StepNo-1;
				objC = gblArrayCat[intStepNo-1]; // Remember, gblArrayCat is a zero based index, while StepNo starts at 1
				if (objC.DifficultyLevel <= 1) { // Found a normal page
					boolKeepLooping = false;
				}
			}
		}
		
		// Same StepNo as current page.  Exit.
		if (intStepNo == gblArrayCatByID[GblCurrentLOID].StepNo) {
			return;
		}
		
		window.document.location = gblCoursePageName + "?CGID=" + GblCGID + "&StepNo=" + intStepNo; 
	}
	
	// Navigates to the next page
	// Assumes that boolCoursePlus has already been defined
	function NavNext() {
		var objC = gblArrayCatByID[GblCurrentLOID];
		var intStepNo = objC.StepNo;
		// Determine the previous StepNo
		if (boolCoursePlus) { 
			intStepNo = objC.StepNo + 1;
		} else { // Skip Course Plus pages
			boolKeepLooping = true;
			while ((intStepNo < gblArrayCat.length) && (boolKeepLooping)) {
				intStepNo = objC.StepNo+1;
				objC = gblArrayCat[intStepNo-1]; // Remember, gblArrayCat is a zero based index, while StepNo starts at 1
				if (objC.DifficultyLevel <= 1) { // Found a normal page
					boolKeepLooping = false;
				}
			}
		}
		
		// StepNo greater than max StepNo -> exit.
		if (intStepNo > gblArrayCat[gblArrayCat.length-1].StepNo) {
			return;
		}

		
		window.document.location = gblCoursePageName + "?CGID=" + GblCGID + "&StepNo=" + intStepNo; 
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
		
	
	
	
	
		
	// Loads the products page
	// Will automatically expand the category that was selected
	function LoadProductPage(intIndex) {
		var objC = gblArrayCat[intIndex];
		
		// Remember what Category object is currently selected.
		if (gblSelectedCat) {
			gblSelectedCat.Selected = false;
		}
		objC.Selected = true;
		gblSelectedCat = objC;
		
		if (objC.StepNo > 2) {
			alert("Sorry, the page you have requested is not a part of this demo.");
			return;
		}
		
		// Load the page in the current window
//		alert(gblCoursePageName + DetermineQueryString(objC));
		window.document.location = "Page" + objC.StepNo + ".htm";
	}
	
	
	// Loads the course page according to the LOID given.  Will only load the page if the LOID is found
	function LoadPageByLOID(intLOID) {
		var objC;
		var boolLOIDFound = false;
		var objMatch;
		
		// Loop through each page to see if it is a match
		for (var i=0;i<gblArrayCat.length;i++) {
			objC = gblArrayCat[i];
			if (objC.ID == intLOID) { // Found a match
				boolLOIDFound = true;
				objMatch = objC;
			}
		}
	
		// If we've found the LOID, navigate to that page!
		if (boolLOIDFound) {
			window.document.location = gblCoursePageName + DetermineQueryString(objMatch);

		}
	}
	
	
	
	// Generates the Category filter to apply to the QueryString
	function DetermineQueryString(objC) {
		var objChild = objC;
		var strQS = "";
		
		strQS = "CGID=" + objC.CGID + "&StepNo=" + objC.StepNo;

		var objRegExp = new RegExp(" ","g");
		strQS = "?" + strQS.replace(objRegExp, "+");  // Replace all blank spaces with "+".  Required for Netscape 4 to work.
		
		return strQS;
	}
		
	
	
	
	
	
	
	
	
	
	
	
	
	
	
		
