﻿// --------------------------------------------------------
// KNOWLEDGE MAP DYNAMIC "ON THIS PAGE..." NAVIGATION MENUS
// --------------------------------------------------------
// Copyright (c) 2004 The Salamander Organization Ltd.  All Rights Reserved.
//
// THIS WORK IS SUBJECT TO U.K. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
//
// DISCLAIMER:
//
// The Salamander Organization Ltd. (hereto referred as "Salamander") makes no representations or warranties
// with respect to the contents or use of this code, and specifically disclaims any express or implied
// warranties of merchantability or fitness for any particular purpose.  Further, Salamander reserves the right
// to revise this publication and to make changes to its content, at any time, without obligation to notify any
// person or entity of such revisions or changes.
//
// Further, Salamander makes no representations or warranties with respect to any software, and specifically
// disclaims any express or implied warranties of merchantability or fitness for any particular purpose.  Salamander
// reserves the right to make changes to any and all parts of the software, at any time, without obligation to notify
// any person or entity of such changes.
//
// Inclusion of the above notice does not necessarily imply publication.


// ########################
// ### SYSTEM VARIABLES ###

var pageTopName = "Top of the page";
var sectionCount = 0;
var sectionArray = new Array();
var havePageTopSingleton = false;
var sectionPrefix = "section_";
var contentPrefix = "content_";
var navPrefix = "nav_";
var navLinkPrefix = "navtext_";
var alwaysDisplayName = "lockDisplay";
var initialSectionDisplayCount = 0;  // number of sections to display initially
var gotFirstListItem = false;
var displayedNavCount = 0;  // the number of nav links -actually- displayed
var lastNavSection;  // push-pop single item list for the last processed NavSection
var moodPluginsCount = 0;  // number of mood plugins objects on this page (thus far discovered)
var moodPluginsCheckCount = 0;  // number of plugins we've already checked for


// ##################################
// ### PAGE NAV PUBLIC INTERFACES ###

function AddNavLink(sectionName, isPageTop)
{
	if (!isPageTop) isPageTop = false;
	
	var navSection = CreateNavSection(sectionName, "");
	
	// check page top singleton
	if (isPageTop && !havePageTopSingleton) 
	{
		pageTopName = navSection.uniqueName;
		havePageTopSingleton = true;
	}

	AddNavLinkBySection(navSection);
}

function AddNavLinkBySection(navSection)
{
	var sectionHtml = "<span id='" + navSection.id + "' style='display:none;'></span>";
	document.write(sectionHtml);
}

// start a new section to allow show/hide of that section
// must close section using WriteSectionFooter()
// hideTitle is a boolean as to whether to hide the title for that section or not
function WriteSectionHeader(sectionName, displayType, hideTitle)
{
	var navSection = CreateNavSection(sectionName, displayType, hideTitle);
	
	// add a navigation link to allow direct scroll-to this section
	AddNavLinkBySection(navSection);  // pass the name not the ID

	// write out the section header
	document.write(navSection.sectionHeader);
	
	// record this nav section for checking during the WriteSectionFooter() stage
	lastNavSection = navSection;
}

// [factory] create a new page nav section object and add it to the global array of all page nav sections
function CreateNavSection(sectionName, displayType, hideTitle)
{
	// create the nav section
	var navSection = new PageNavSection(sectionName, displayType, hideTitle);

	// add it to the global collection
	sectionArray[sectionCount] = navSection;
	sectionCount++;
	
	// return it
	return navSection;
}

// end a section to allow show/hide of that section
function WriteSectionFooter()
{
	// add two closing markers - one for the section and one for the content
	var sectionFooter = "</div>\n";	
	sectionFooter += "</div>\n";

	document.write(sectionFooter);
	
	// finally check to see if this section is empty
	if (lastNavSection) lastNavSection.isEmpty = IsSectionEmpty(lastNavSection.id)
}

// clear the any gotFirstListItem singletons on beginning a new list
// used in conjunction with a WriteSectionHeader(... , "showfirst")
function SetListStart()
{
	gotFirstListItem = false;
}

// clear the any gotFirstListItem singletons on completing a list
// used in conjunction with a WriteSectionHeader(... , "showfirst")
function SetListEnd()
{
	gotFirstListItem = false;
}





// #######################################
// ### PAGE NAV USER INTERFACE DISPLAY ###

// [builder] write out the navigation area
function BuildOnThisPageBlock()
{
	// make sure we're viewing the top of the page on a page refresh
	ScrollToPageTop();
	
	if ((sectionCount > 1) || ((sectionCount == 1) && (sectionArray[0].uniqueName != pageTopName)))
	{	
		for (var i = 0; i < sectionCount; i++)
		{
			var currSection = sectionArray[i];
			var sectionID = ConvertNameToID(currSection.uniqueName);
			
			if (currSection.isEmpty == false || showEmptySections)
			{
				if (!(currSection.uniqueName == pageTopName) || displayTopNav)
				{
					AddNavigationLink_OnThisPage(currSection.name, sectionPrefix + currSection.id, navPrefix + currSection.id, navLinkPrefix + currSection.id);

					displayedNavCount++;
				}
			}
		}
	}
	
	if (displayedNavCount == 0)
	{
		// when no navigation links are displayed then indicate that there are none in friendly words
		AddBlockItem_OnThisPage(noNavText);
	}
}


// ########################
// ### PAGE NAV OBJECTS ###

// [constructor] page nav section object
// SUMMARY:
//		string sectionName		- string for the section's name
//		string displayType		- string to indicate how this section should be displayed
//		bool hideTitle			- boolean for whether to hide the title for this section
function PageNavSection(sectionName, displayType, hideTitle)
{
	this.name = sectionName;
	this.displayType = displayType;
	this.hideTitle = hideTitle;
	
	this.uniqueName = MakeUniqueName(StripChars(this.name, "parentheses"), 1);
	this.id = ConvertNameToID(this.uniqueName);
	
	this.sectionHeader = GetSectionHeader(this.name, this.displayType, this.hideTitle);
	
	this.isEmpty = false;
}


// ###########################################
// ### PAGE NAV USER INTERFACE INTERACTION ###

// displays a section, and optionally scrolls the page around that element
function ShowSection(sectionID, doScrolling)
{
	var navSection = GetSectionById(sectionID);

	var sectionTextID = sectionPrefix + sectionID;

	var templateSection = document.getElementById(sectionTextID);

	if (templateSection)
	{
		// show this section if it is currently hidden
		if (templateSection.style.display == 'none')
		{
			if (navSection.isEmpty == false || showEmptySections)
			{
				// only show content if there's actually something to display
				templateSection.style.display = 'block';
				
				HilightSectionNav(sectionID);
				
				SavePageState(sectionID, "show", doScrolling);
			}
		} 

		if (navSection.isEmpty == false || showEmptySections)
		{
			// regardless of the section's display, scroll it into view if requested
			if (doScrolling)
			{
				templateSection.scrollIntoView(true);
			}
		}
	}
}

// hides a section, and optionally scrolls the page around that element, allowing the option to override the "Always Display"
function HideSection(sectionID, doScrolling, overrideAlwaysDisplay)
{
	var navSection = GetSectionById(sectionID);
	
	var sectionTextID = sectionPrefix + sectionID;
	
	var templateSection = document.getElementById(sectionTextID);

	if (templateSection)
	{
		// make sure we're allowed to toggle the display of this element
		if (templateSection.name != alwaysDisplayName || overrideAlwaysDisplay)
		{
			// hide this section if it is currently displayed
			if (templateSection.style.display == 'block')
			{
				templateSection.style.display = 'none';
				
				DimSectionNav(sectionID);
				
				SavePageState(sectionID, "hide", doScrolling);
			}
		}
		
		// if this is a sticky section and we're not forcing it to hide then try to scroll to it, regardless of doScrolling
		if (templateSection.name == alwaysDisplayName && !overrideAlwaysDisplay)
		{
			templateSection.scrollIntoView(true);
		}
		
		// even if we can't hide the section 
		if (doScrolling)
		{
			
			ScrollToPageTop();
		}
	}
}

// [adapter to ShowSection() and HideSection()] toggles the display (show/hide) of a section, and optionally scrolls the page around that element
function ToggleSection(sectionTextID, doScrolling)
{
	
	var templateSection = document.getElementById(sectionTextID);
	
	// break the sectionTextID up to get the actual sectionID
	var sectionID = StripChars(sectionTextID, "sectionprefix");

	if (templateSection)
	{
		// toggle state for this section - if displayed, then hide, but if hidden, display
		if (templateSection.style.display == 'none')
		{
			ShowSection(sectionID, doScrolling);
		} 
		else
		{
			HideSection(sectionID, false, false);  // don't scroll back to top on hiding
		}
	}
}

// [adapter to ToggleSection()] allows names to be used to toggle section display
function ToggleSectionByName(sectionName, doScrolling)
{
	var sectionTextID = sectionPrefix + ConvertNameToID(sectionName);  // DEV NOTE: this name is not necessarily unique...
	ToggleSection(sectionTextID, doScrolling);
}

// [adapter to HideSection()] allows names to be used to hide a section
function HideSectionByName(sectionName, doScrolling, overrideAlwaysDisplay)
{
	var sectionID = ConvertNameToID(sectionName);  // DEV NOTE: this name is not necessarily unique...
	HideSection(sectionID, doScrolling, overrideAlwaysDisplay);
}

// [adapter to ShowSection()] allows names to be used to show a section
function ShowSectionByName(sectionName, doScrolling)
{
	var sectionID = ConvertNameToID(sectionName);  // DEV NOTE: this name is not necessarily unique...
	ShowSection(sectionID, doScrolling);
}

// [adapter to ShowSection()] allows full section IDs to be used to show a section
function ShowSectionByFullId(sectionTextID, doScrolling)
{
	// break the sectionTextID up to get the actual sectionID
	sectionID = StripChars(sectionTextID, "sectionprefix");
	
	ShowSection(sectionID, doScrolling);
}

// [adapter to ShowSectionByFullId()] hides all sections before showing just one section
function ShowOnlySectionByFullId(sectionTextID, doScrolling)
{
	HideAllSections(true);	
	ShowSectionByFullId(sectionTextID, doScrolling)
}

// [builder] create the initial display of sections view
// uses a count of number of sections to display by default (var initialSectionDisplayCount)
function SetInitialSectionView()
{
	if ((sectionCount > 1) || ((sectionCount == 1) && (sectionArray[0] != pageTopName)))
	{	
		var displayCount = 0
		for (pass = 0; displayCount < initialSectionDisplayCount; pass++)
		{
			if (!(sectionArray[pass] == pageTopName) || displayTopNav)
			{
				ShowSectionByName(sectionArray[pass], false);
				displayCount++;  // only increment success loop if we're actually displaying an item
			}
		}
	}
}

// [builder] shows all sections on the page
function ShowAllSections()
{
	for (var i = 0; i < sectionCount; i++)
	{
		ShowSectionByName(sectionArray[i].uniqueName, false);
	}
}

// [builder] hides all hideable sections on the page, allowing possibility to override the "always display" option
function HideAllSections(overrideAlwaysDisplay)
{
	for (var i = 0; i < sectionCount; i++)
	{
		HideSectionByName(sectionArray[i].uniqueName, false, overrideAlwaysDisplay);
	}
}

// hides all empty sections on the page
function HideEmptySections()
{
	for (var i = 0; i < sectionCount; i++)
	{
		var navSection = sectionArray[i];
		var sectionName = navSection.name;
		var sectionID = GetUniqueSectionID(sectionName);
		
		if (navSection.isEmpty)
		{
			HideSectionByName(sectionName, false, true);
		}
	}
}

// highlight a section's navigation link - usually done as part of ShowSection()
function HilightSectionNav(sectionID)
{
	var sectionNavID = navPrefix + sectionID;
	var sectionLinkID = navLinkPrefix + sectionID;
	
	var navSection = document.getElementById(sectionNavID);
	
	if (navSection)
	{
		navSection.className = "navHilight";
	}
	
	var navLink = document.getElementById(sectionLinkID);
	
	if (navLink)
	{
		navLink.className = "navLinkHilight";
	}
}

// dim a section's navigation link - usually done as part of HideSection()
function DimSectionNav(sectionID)
{
	var sectionNavID = navPrefix + sectionID;
	var sectionLinkID = navLinkPrefix + sectionID;
	
	var navSection = document.getElementById(sectionNavID);
	
	if (navSection)
	{
		navSection.className = "navDim";
	}
	
	var navLink = document.getElementById(sectionLinkID);
	
	if (navLink)
	{
		navLink.className = "navLinkDim";
	}
}

// set the initial highlighting on the navigation "on this page..." section
function InitNavHilights()
{
	if ((sectionCount > 1) || ((sectionCount == 1) && (sectionArray[0].uniqueName != pageTopName)))
	{	
		for (i=0; i < sectionCount; i++)
		{
			var currSection = sectionArray[i];
			var sectionID = ConvertNameToID(currSection.uniqueName);
			
			if (currSection.isEmpty == false || showEmptySections)
			{
				if (!(currSection.uniqueName == pageTopName) || displayTopNav)
				{
					if (IsSectionShown(sectionID) == true)
					{
						// section is currently displayed, so highlight
						HilightSectionNav(sectionID);
					}
					else
					{
						// section is currently hidden, so dim
						DimSectionNav(sectionID);
					}
				}
			}
		}
	}
}

// scrolls to the position of the "page top" element
function ScrollToPageTop()
{
	var pageTopID = ConvertNameToID(pageTopName);
	if (pageTopID != "")
	{
		var pageTop = document.getElementById(pageTopID);
		if (pageTop) pageTop.scrollIntoView(true);
	}
}


// ##################################
// ### PAGE NAV SUPPORT FUNCTIONS ###

function GetSectionHeader(sectionName, displayType, hideTitle)
{
	var sectionID = GetUniqueSectionID(sectionName);
	var displayHTML = "";
	var contentHTML = "";

	var showStyle = " style='display:block;'";
	var hideStyle = " style='display:none;'";
	var clearfix = " class=\"clearfix\"";
	
	var displayLock = " name='" + alwaysDisplayName + "'";
	
	var lock = false;
	var useClear = false;
		
	displayType = displayType.toLowerCase();  // case protection

	// check for the "clear" option on displayType
	var clearStringTest = /clear/;
	if (clearStringTest.test(displayType))
	{
		useClear = true;
		var displayTypeParts = displayType.split("_");
		displayType = displayTypeParts[0];  // discard the "_clear" part
	}

	switch (displayType)
	{
		case "showlock" :
			// start with this item displayed, and lock it as displaying
			displayHTML = displayLock + showStyle;
			lock = true;
			break;
		case "hidelock" :
			// start with this item hidden, and lock it so that it can't be displayed
			displayHTML = displayLock + hideStyle;
			lock = true;
			break;
		case "show" :
			// start with this item displayed
			displayHTML = showStyle;
			break;
		case "showfirst" :
			// if this is the first item of a list, start with it displayed, else hide it
			if (IsFirstListItem())
			{
				displayHTML = showStyle;
			}
			else
			{
				displayHTML = hideStyle;
			}
			break;
		case "showfirstlock" :
			// if this is the first item of a list, start with it displayed, and lock it as displaying, else hide it
			if (IsFirstListItem())
			{
				displayHTML = displayLock + showStyle;
				lock = true;
			}
			else
			{
				displayHTML = hideStyle;
			}
			break;
		case "hide" :
			// start with this item hidden
			displayHTML = hideStyle;
			break;
		default :
			// start with this item hidden
			displayHTML = hideStyle;
			break;
	}

	if (useClear)
	{
		contentHTML = contentHTML + clearfix;
	}

	// open the section block
	var sectionHeader = "<div id='" + sectionPrefix + sectionID + "'" + displayHTML + ">\n";
	
	if (!hideTitle)
	{
		// if we're displaying the section title then add that now
		var sectionTitle = LayoutSectionTitle(sectionName, lock);
		
		sectionHeader += sectionTitle;
	}
	
	// open the content block for this section
	sectionHeader += "<div id='" + contentPrefix + sectionID + "'" + contentHTML + ">\n";
	
	return sectionHeader;
}

// from the collection of all sections, return the collection that matches the provided name
// returns null where it couldn't find the named section
function GetSectionByName(sectionName)
{
	var navSection;

	for (var i = 0; i < sectionCount; i++)
	{
		if (sectionArray[i].sectionName = sectionName)
		{
			navSection = sectionArray[i];
			break;
		}
	}
	
	return navSection;
}

// from the collection of all sections, return the collection that matches the provided id
// returns null where it couldn't find the named section
function GetSectionById(sectionID)
{
	var navSection;

	for (var i = 0; i < sectionCount; i++)
	{
		if (sectionArray[i].id = sectionID)
		{
			navSection = sectionArray[i];
			break;
		}
	}
	
	return navSection;
}


// [singleton] is this the first item in the list?
function IsFirstListItem()
{
	if (!gotFirstListItem)
	{
		gotFirstListItem = true;
		return true;
	}
	else
	{
		return false;
	}
}

// check whether the contents for this section are empty
// this checks the innerHTML of the -content- of a section, not including its title
// this will check to see if it is looking at a model section and always return false if it is
// -- this is because for 2006 publications viewed with a 2005SE plugin IE will crash
function IsSectionEmpty(sectionID)
{
	var isEmpty = false;
	
	var contentArea = document.getElementById(contentPrefix + sectionID);
	
	if (contentArea)
	{
		var sectionContainsModel = false;
		
		// check for object tags in this content
		var moodPlugins = document.getElementsByTagName("OBJECT");
		moodPluginsCount = moodPlugins.length;
		
		if (moodPluginsCount > moodPluginsCheckCount)
		{
			// there are mood plugins that we haven't checked for, assume this count differs by just 1
			if (contentArea.contains(moodPlugins[moodPluginsCount - 1]))
			{
				sectionContainsModel = true;  // this section contains a model
				moodPluginsCheckCount++;  // we've checked another, increment the check count
			}
		}
		
		// FIX for 2006 publish crashing when viewing with 2005SE plugin
		// --- do not call contentArea.innerHTML if the section contains a model!
		if (!sectionContainsModel)
		{
			// the contents of this section is the innerHTML
			// --- stripped of white space (as spaces, newlines, tabs, etc are not really content)
			// --- stripped of the section footer javascript which appears for some reason I can't fathom
			// --- stripped of simple HTML tags, e.g. <p> and </p>, not <p class="body">
			var contents = StripChars(StripChars(StripChars(contentArea.innerHTML, "whitespace"), "sectionfooter"), "simplehtmltags");

			//alert("For section '" + sectionID + "' the contents are:\n\n'" + contents + "'");

			if (contents == "")
			{
				// if the contents of this section are now blank (i.e. was empty or just spaces) then this section is empty
				isEmpty = true;
			}
		}
	}
	
	return isEmpty;
}

// is the section identified by this section ID currently displayed (true) or hidden (false)
function IsSectionShown(sectionID)
{
	var isShown = false;
	var sectionTextID = sectionPrefix + sectionID;
	
	var section = document.getElementById(sectionTextID);
	
	if (section)
	{
		if (section.style.display == 'block')
		{
			isShown = true;
		}
		else
		{
			isShown = false;
		}
	}
	
	return isShown;
}


// #############################
// ### PAGE MEMORY FUNCTIONS ###

var lastShownSectionId = "";

var doingPageStateLoad = false;

// save the current page state
// SUMMARY:
//		sectionId			- string of the section id with no prefix
//		sectionState		- string of either "show" or "hide"
//		doScrolling			- boolean to indicate whether scrolling is enabled
function SavePageState(sectionId, sectionState, doScrolling)
{
	// don't save page state if we're loading states and not if we're initialising the page
	if (!doingPageStateLoad && !doingPageInit)
	{
		var cookieVar = GetCookieVar("pagesection", true);

		if (cookieVar != "")
		{
			var cookieData = "";
			var state = "";

			// get the old cookie data
			var oldCookieData = GetFromCookie(cookieVar);

			switch (sectionState)
			{
				case "show" :

					// section is displayed
					if (doScrolling)
					{
						state = "2";
					}
					else
					{
						state = "1";
					}

					break;

				case "hide" :

					// section is hidden
					state = "0";
					break;

				default :

					// no valid state indicated - error condition
					break;
			}

			// modify the old cookie data to get new cookie data
			cookieData = ModifyCookieData(oldCookieData, sectionId, state);

			// save the new cookie data
			SaveToCookie(cookieVar, cookieData);
		}
	}
}

// load the page state from cookie, if available
function LoadPageState()
{
	doingPageStateLoad = true;  // mark start of state load (to prevent saves occuring too)
	
	var cookieVar = GetCookieVar("pagesection");
	
	if (cookieVar != "")
	{
		var stateData = GetFromCookie(cookieVar);
		
		// start initialising the page based on the cookie
		SetPageState(stateData);
	}
	
	doingPageStateLoad = false;  // mark end of state load
}

// initialise the page sections based on a set of state data
function SetPageState(stateData)
{
	if (stateData != "")
	{
		var stateString = new String(stateData);
		var stateParts = stateString.split(varSplit);
		
		if (stateParts.length > 0)
		{
			for (var i = 0; i < stateParts.length; i++)
			{
				if (i == 0)
				{
					// ignore the page path
				}
				else if (i == 1)
				{
					// ignore the last modified details
				}
				else
				{
					// process the val-value pairs
					var pairString = new String(stateParts[i]);
					var pairParts = pairString.split(pairSplit);

					if (pairParts.length == 2)
					{
						// got an expected pair, process it
						var sectionId = pairParts[0];
						var sectionState = pairParts[1];
						
						if (sectionId != "")
						{							
							switch (sectionState)
							{
								case "2" :
									
									ShowSection(sectionId, false);
									
									lastShownSectionId = sectionId;
									
									break;
									
								case "1" :
								
									// show this section									
									ShowSection(sectionId, false);
									break;
									
								case "0" :
								
									// hide this section
									HideSection(sectionId, false, true);
									break;
									
								default :
								
									// unrecognised state - do nothing
									break;
							}
						}
					}
				}
			}
		}
	}	
}

// scroll to the last page section that was displayed and was enabled for scrolling
// usually this sort of scrolling would have more protection...
// ...but if this is called we already know the user interacted with the section so it's visible, safe, etc
function ScrollToLastShown()
{
	if (lastShownSectionId != "")
	{
		var sectionTextID = sectionPrefix + lastShownSectionId;
		
		var templateSection = document.getElementById(sectionTextID);
		
		if (templateSection)
		{
			templateSection.scrollIntoView(true);
		}
	}
}



// #####################################
// ### PAGE MATRIX SUPPORT FUNCTIONS ###

// check for fragment identifiers on the page that might indicate navigation to a matrix on this page
function CheckForMatrixFragment()
{
	var fragment = window.location.hash;
	
	if (fragment != "" && fragment != null)
	{
		// got fragment, strip leading #
		fragment = fragment.substr(1, fragment.length - 1);
		
		// find the associated name block that this would link to
		var namedAnchors = document.getElementsByName(fragment);
		var targetAnchor;
		
		if (namedAnchors != null && namedAnchors.length > 0)
		{
			targetAnchor = namedAnchors[0];  // should only be one named thing with this name
		}
		
		// find the associated section to this anchor
		if (targetAnchor != null)
		{
			var section = targetAnchor.nextSibling.nextSibling;  // two siblings on from the named anchor
			
			if (section != null)
			{
				var sectionId = section.id;
				
				// show the section and scroll to it (scroll happens in PostProcessPage() in init_page_func.js).
				ShowSection(sectionId, true);
			}
		}
	}
}
