/*******************************

 HelpSystem.js, Version 4.8.1, 30-Mar-2009
 Copyright Northgate Information Solutions UK Limited, 2002-2009

 *******************************/

/********************
 HelpSystem class
 
 Constructor:
	HelpSystem() {
 Properties:
	configuration
	currentNavTool
	dataFrame
	exampleWindow
	hash
	loading
	navbarFrame
	navFrame
	optFrame
	rootFolder
	topicFrame
	url
 Methods:
	loadNavTool(obj)
	unloadHelpSystem()
	initNavTool()
	resizeNavTool()
	syncContents()
	mouseEvent(event)
	navigationEvent(event)
	getTopicUrl(local)
	getNavigationUrl()
	getLinksUrl()
	getLinksFrame()
	getLocalRoot()
	normaliseUrl(href)
	relativeToRoot(href)
	relativeToTopic(href, relTo)
	relativeUrl(url, relTo)
	toggleDisplay(targetId, source, displayedPrompt)	
 Class methods:
	parseQueryString(queryString)
	
 ********************/
 
// *** Constructor for HelpSystem class ***
function HelpSystem() {
	/* If the document in the parent window is part of the
	   help system... */
	if (parent.nisHelpWindow)
		// Get the URL of the parent document.
		this.topWindow = parent;
	else
		// Otherwise get the URL of this document.
		this.topWindow = self;
	
	this.url = this.topWindow.location.href;
	var temp = this.normaliseUrl(this.url);
	this.rootFolder = temp.split("/").slice(0, -1).join("/") + "/";
	this.pathname = this.topWindow.location.pathname;
	this.pathname = this.pathname.replace(/[\/\\][^\/\\]*$/, "");
	this.topUrl = temp.split("/").slice(-1)[0];
	this.query = this.topWindow.location.search;
	this.hash = this.topWindow.location.hash;

	// Get any parameters passed on the command line.
	this.urlParams = HelpSystem.parseQueryString(this.query);
	
	this.configuration = new Configuration(this);

	// References to frames.
	//	Most of the frames are static.
	/*	There are two possible layouts: one where the main frameset
		contains navbar, topic and data frames, and the navbar frame
		contains the options and navigation frames; and another where
		the main frameset contains options and contents frames, and
		the content frame contains navigation, topic and data frames.
	*/
	this.navbarFrame = self.frames[this.configuration.navbarFrameId];
	this.contentFrame = self.frames[this.configuration.contentFrameId];
	if (typeof this.navbarFrame != "undefined") {
		this.topicFrame = self.frames[this.configuration.topicFrameId];
		this.dataFrame = self.frames[this.configuration.dataFrameId];
		this.optFrame = this.navbarFrame.frames[this.configuration.optFrameId];
		this.navFrame = this.navbarFrame.frames[this.configuration.navFrameId];
	}
	else if (typeof this.contentFrame != "undefined") {
		this.topicFrame = this.contentFrame.frames[this.configuration.topicFrameId];
		this.dataFrame = this.contentFrame.frames[this.configuration.dataFrameId];
		this.navFrame = this.contentFrame.frames[this.configuration.navFrameId];
		this.optFrame = self.frames[this.configuration.optFrameId];
	}
	else {
//		alert("Invalid frame layout!");
		this.topicFrame = self.frames[this.configuration.topicFrameId];
		this.dataFrame = self.frames[this.configuration.dataFrameId];
		this.optFrame = self.frames[this.configuration.optFrameId];
		this.navFrame = self.frames[this.configuration.navFrameId];
	}
	
	// Default topic (specified relative to topics folder).
	this.defaultTopic = (self.defaultTopic != null
		? self.defaultTopic 
		: (self.topicDirectory == null ? "/" : self.topicDirectory + "/")
			 + this.topUrl);
	
	this.navigationTools = new Array();
	this.navigationTools["none"] = null;
	this.navigationTools["contents"] = new Contents(this);
	this.navigationTools["index"] = new Index(this);
	this.navigationTools["search"] = new Search(this);
	this.navigationTools["links"] = new Links(this);
	this.navigationTools["glossary"] = new Glossary(this);
	this.currentNavTool = null;

	this.topic = new Topic(this);
	this.topicId = this.getTopicId();

	// If we are refreshing, a topic will already be loaded.
	this.refreshing = (this.topicFrame.document.location.href.search(HelpSystem.RE_BLANKPAGE) == -1);

	this.loading = true;

	// Set up data objects.
	var c = this.configuration;
	// Create an instance of the DataObjects class.
	this.dataObjects = new DataObjects(this);

	// For each element in the dataObjects list...
	for (var i = 0; i < c.dataObjects.length; i++) {
		var d = c.dataObjects[i];
		// Create the object and assign it to a property.
		this[d.name] = eval("new " + d.constructor + "()");
		this[d.name].setup(d.filename);
		// Add it to the dataObjects collection.
		this.dataObjects.addNext(this[d.name]);
	}
	
	if (!this.refreshing) {
		// Start loading the data.
		this.dataObjects.initialise();
		this.loadInitialTopic();
	}
	else {
		// Start loading the data.
		this.dataObjects.load();
		/* If we are refreshing, the topic has already been loaded,
		   but the onLoad event will have fired prematurely. We therefore
		   need to simulate the onLoad event. */
		this.loadNavTool();
	}

	this.refreshing = false;

	if (typeof Breadcrumbs == "function")
		this.breadcrumbs = new Breadcrumbs(this, this.topicFrame, this.optFrame);
		
	this.ready = true;
}


// *** HelpSystem instance methods ***
HelpSystem.prototype.loadInitialTopic = function hsLoadInitialTopic() {
	/* Determine which topic is to be loaded and, if necessary,
	   load it.
	   1. Get the topic ID of the required topic.
	   2. Convert the topic ID into a path.
	   3. Load the topic or, if already loaded, load the associated
	      navigation tool.
	   */
	var topic = "";
	var temp = "";

	/* Get the name of the topic specified by the user, if any.
	   Otherwise, get the name of the default topic file. */
	topic = (this.topicId != "" ? this.topicId : this.defaultTopic);

	// *** Convert the topic ID into a path... ***
	// If refreshing with no restore, get the filename of the current topic.
	if (this.refreshing) {
		topic = this.topicFrame.location.pathname.split(/[\/\\]/).slice(-1)[0];
	}
	// Otherwise, convert the topic id.
	else if ((topic = this.getTopicFilename(topic)) == "") {
		// If topic id not found, get the  default topic.
		topic = this.getTopicFilename(this.defaultTopic)
	}

	// Topic is relative to topics folder - needs to be relative to root.
	topic = this.configuration.topicFolder + topic;

	// *** Load the topic... ***
		// If topic is not dynamically generated...
    if (topic != this.topUrl) {
		// If the topic URL has no bookmark...
        if (topic.indexOf("#") == -1)
			// Append any base URL bookmark.
			topic += this.hash;
		// If the topic is not already loaded...
		if (topic != this.getTopicUrl()) {
		    // Load the topic file.
			this.topicFrame.location.replace(unescape(topic));
		}
		else // Otherwise (already loaded)...
			// Load the associated navigation tool.
		    this.loadNavTool();
	}
		/* Otherwise, topic is dynamically generated, so must be
		   reloading a previously saved state... */
	else {
		// Load the associated navigation tool.
		this.loadNavTool();
	} // End if
}

HelpSystem.prototype.loadNavTool = function hsLoadNavTool(obj) {
	var type;
	
	/* If no parameter was supplied or the parameter refers to the
	   topic frame... */
	if (obj == null || obj == this.topicFrame) {
		// Update the topic object.
		this.topic.update();
		/* If we are refreshing... */
		if (this.refreshing) {
			// Get the URL of the current navigation tool.
			var nav = this.navFrame.location.pathname.split(/[\/\\]/).slice(-1)[0];
			var navIx = nav.substring(0, 2).isIn("co", "in", "se", "li", "gl");
			if (navIx >= 0)
				type = ["contents","index","search","links","glossary"][navIx];
			else
				alert("Invalid navigation tool: " + nav);
		}
		else {
			// Get the name of the default navigation tool for this topic.
			type = this.topic.navTool;
			// If the navigation tool doesn't change...
			if (type == "*")
				// Get the name of the current navigation tool.
				type = this.currentNavTool.toString().toLowerCase();
		}
	}
	// Else if the parameter refers to the data frame...
	else if (obj == this.dataFrame) {
		// Initialise the current data object.
		this.dataObjects.initialise();
		return;
	}
	// Else if the parameter refers to a button...
	// test that there is a name property.
	else if (obj.name != null) {
		// Get the name of the button.
		type = obj.name.toLowerCase();
		/* If the links button was clicked, but no links file is
		   specified for this topic... */
		if (type == "links" && this.topic.linksFile == "") {
			/* Get the name of the default navigation tool for
			   this topic. */
			type = this.topic.navTool;
		}
	}
	// Any thing else is an error...
	else {
		alert("Invalid navigation tool selector");
		type = this.topic.navTool;
	}

	// Set up onClick events for this topic.	
	this.topicFrame.document.onclick = self.mouseEvent;

	if (this.currentNavTool != null) {
		// If we are searching, find the search text.
		if (this.currentNavTool.searching) {
			this.currentNavTool.textSearch(HelpSystem.parseQueryString(this.topic.document.location.search).searchtext);
		}
	}

	// Set up the navigation tools.	
	var t = this.navigationTools;
	t.contents.url = this.topic.contentsPage;
	t.index.url = this.topic.indexPage;
	t.search.url = this.topic.searchPage;
	t.search.searchData = this.topic.searchData;
	t.links.frameUrl = this.topic.linksFrame;
	t.links.linksPage = this.topic.linksFile;
	t.glossary.url = this.topic.glossaryFrameset;

	// Shut down the current navigation tool before loading the new one.
	if (this.currentNavTool)
		this.currentNavTool.terminate();
	this.currentNavTool = this.navigationTools[type];
	this.currentNavTool.load();
	if (this.refreshing) {
		this.currentNavTool.initialise(this);
		this.loading = false;
	}

	// Select the appropriate navigation tool button or link.
	this.selectNavTool(type);
	
	if (this.breadcrumbs)
		this.breadcrumbs.insert();
}

HelpSystem.prototype.initNavTool = function hsInitNavTool() {
	this.currentNavTool.initialise();
}

HelpSystem.prototype.resizeNavTool = function hsResizeNavTool() {
	this.currentNavTool.resize();
}

HelpSystem.prototype.syncContents = function hsSyncContents() {
	this.navigationTools.contents.synchronise();
}

HelpSystem.prototype.unloadHelpSystem = function hsUnloadHelpSystem() {
	if (!!this.currentNavTool) {
		this.currentNavTool.terminate();
	}
}

HelpSystem.prototype.mouseEvent = function hsMouseEvent(e) {
	if (typeof e == "undefined")
		e = this.topicFrame.event;
	
	// If the event was not handled by any data object...
	if (!this.dataObjects.mouseEvent(e)) {
		return(this.jumpToHyperLink(e));
	}
	else
		return(false);
}

HelpSystem.prototype.navigationEvent = function hsNavigationEvent(e) {
	if (typeof e == "undefined")
		e = this.navFrame.event;

	// If the event was not handled by any navigation object...
	if (this.currentNavTool.handleEvent(e))
		return(true);
	else
		return(this.jumpToHyperLink(e));
}

HelpSystem.prototype.jumpToHyperLink = function hsJumpToHyperLink(e) {
	var obj = (document.all ? e.srcElement : e.target.parentNode);
		
	// If the source was a hyperlink and the target is a new window...
/*	if (obj.tagName.toLowerCase() == "a" 
			&& obj.target.toLowerCase() == "_blank") {
		// Open a new window.
		var w = window.open(obj.href, "example");
		w.focus();
		return(false);
	}
	else */
		// If the source was a hyperlink, jump to the target.
		return(true);
}

HelpSystem.prototype.getTopicUrl = function hsGetTopicUrl(local) {
	/* Return the path of the current topic. If local is false, the path
	   is relative to the help system root; otherwise, if local sitemaps
	   are enabled, it is relative to the local root. */
	return(this.makeLocalPath(this.topicFrame.location.href, local));
}

HelpSystem.prototype.getNavigationUrl = function hsGetNavigationUrl(local) {
	/* Return the path of the current navigation file. If local is false,
	   the path is relative to the help system root; otherwise, if local
	   sitemaps are enabled, it is relative to the local root. */
	return(this.makeLocalPath(this.navFrame.location.href, local));
}

HelpSystem.prototype.makeLocalPath = function hsMakeLocalPath(p, local) {
	// Return a URL, relative to the help system root or the local root.
	local = (local != true ? false : true);
	// Make the path relative to the help system root.
	var pt = this.relativeToRoot(p);
	// If required, make it relative to the local root.
	if (local && this.configuration.navigationFiles.siteMap)
		pt = pt.split("/").slice(2).join("/");
	return(pt);
}

HelpSystem.prototype.getLinksFrame = function hsGetLinksFrame() {
	// Return a reference to the current links frame.
	return(this.navFrame.frames[this.configuration.linksFrameId]);
}

HelpSystem.prototype.getLinksUrl = function hsGetLinksUrl() {
	// Return the relative path of the current links file.
	var f = this.getLinksFrame();
	if (f != null)
		return(this.relativeToRoot(f.location.href));
	else
		return(null);
}

HelpSystem.prototype.absoluteUrl = function hsAbsoluteUrl(href) {
	// Convert a URL containing relative movement to an absolute path...
	// Regular expression matches "/dirname/.."
    var re = /\/[^\/]*\/\.\./;

		// The string "/.." represents movement up the directory tree.
		// While there are moves up the directory tree...
    while (href.search(re) != -1)
		href = href.replace(re, "");
		
	return(href);
}

HelpSystem.prototype.getLocalRoot = function hsGetLocalRoot(url) {
	/* Returns the path to the local root of the supplied URL,
	   relative to the root of the help system.
	   If local site maps are not used, returns a null string. */
	   
	if (this.configuration.navigationFiles.siteMap) {
		// Make the path relative to the help system root.
//		var lr = this.relativeToRoot(url);
		// Discard all but the local root.
		var lr = url.split("/").slice(0, 2).join("/");
		return(lr);
	}
	else {
		return("");
	}
}

HelpSystem.prototype.normaliseUrl = function hsNormaliseUrl(href) {
	// Convert a URL to a standard format.
	// Save any query string and/or bookmark.
	var hash = (href.indexOf("#") != -1 ? href.match(/#[^\?]*/)[0] : "");
	var query = (href.indexOf("?") != -1 ? href.match(/\?[^#]*/)[0] : "");
	// Remove any query string and/or bookmark.
	href = href.split("?")[0].split("#")[0];
	
	// If no protocol, use file:.
	if (href.match(/^(?:[a-z]:\\|\/)/i)) {
		if (DHTML.browserType == "IE"
				|| navigator.platform.startsWith("Win", true))
			href = "/" + href;
		href = "file://" + href;
	}
	
	// Convert backslashes to slashes.
	href = href.split("\\\\").join("\\");
	href = href.split("\\").join("/");
	href = href.split(/%5[Cc]/).join("/");
	
	/* If file protocol on non-Windows platform, make sure that there
	   are the correct number of slashes folowing the protocol. */
	// If not Windows...
	if (href.search(/^file:/i) == -1
			&& navigator.platform.search(/Win/i) == -1) {
		href = href.replace(/^file:\/+(?![\/])/i, "file:////");
	}

	/* If file protocol, make sure that there are the correct 
	   number of slashes folowing the protocol. */
/*	if (href.search(/^file:/i) != -1) {
		var slashes = "file:///";
		if (navigator.platform.search(/Win/i) == -1)
			slashes += "/";
		href = href.replace(/^file:\/+([^\/])/i, slashes + "$1");
	} */

	// Escape any spaces.
	href = href.split(" ").join("%20");
	
	href = this.absoluteUrl(href);
	
	// Put the query string and bookmark back.
	href += query + hash;
	
	return(href);
}

HelpSystem.prototype.relativeToRoot = function hsRelativeToRoot(href) {
	// Returns a normalised URL, relative to the root of the help system.
	href = this.normaliseUrl(href);
	return(href.slice(this.rootFolder.length));
}

HelpSystem.prototype.relativeToTopic = function hsRelativeToTopic(href) {
	/* Returns a normalised URL, relative to the folder containing the
	   current topic.
		Parameters:
			href  - A URL.
	*/
	href = this.normaliseUrl(href);
	
	var topicFolder = this.normaliseUrl(this.topicFrame.location.href);
	topicFolder = topicFolder.substring(0, topicFolder.lastIndexOf("/"));
	
	return(this.relativeUrl(href, topicFolder));
}

HelpSystem.prototype.relativeUrl = function hsRelativeUrl(url, relTo) {
	/* Returns url expressed relative to relTo, where both are full URLs.
	   If the drive letters are different, returns Path.
	   Parameters:
			url - a URL.
			relTo - another URL.
	*/
	// Convert urls to arrays.
	var aUrl = url.split("/");
	var aRt = relTo.split("/");
	if (aRt[aRt.length - 1] == "")
		aRt = aRt.slice(0, aRt.length -1);

	// Initialise the field counter.
	var i = 0;

	// Loop while neither of the urls are empty and the fields are the same...
	do {
		if (!aUrl[i].isEqualIgnoreCase(aRt[i]))
			break;
		i++;
	} while (i < aUrl.length && i < aRt.length);
	
	// Extract the remaining fields in url.
	aUrl = aUrl.slice(i);
	url = aUrl.join("/");
	
	// While not past the end of relTo, for each remaining field in relTo...
	while (i < aRt.length) {
		// url must move up the folder tree one level.
		url = "../" + url;
		i++;
	}
	
	return(url);
}

HelpSystem.prototype.getTopicId = function hsGetTopicId() {
	// Get the topic id, if any.
	var topic = this.urlParams[HelpSystem.TOPICID];
	// Make sure topic is a string.
	topic = (!topic ? "" : topic);
	// Strip off any leading slashes.
	while (topic.charAt(0) == "/")
	    topic = topic.substring(1);
	topic = topic.replace(/\"/g, "")
	return(topic);
}

HelpSystem.prototype.getTopicFilename = function hsGetTopicFilename(topic) {
	// Get the file extension, if any.
    var ext = topic.substring(topic.length - 4).toLowerCase();
	// If there is a data frame...
    if (this.dataFrame != null) {
		var temp = null;

	    // If the topic name does not end .htm or .html...
		if (topic.search(/.html?$/) == -1) {
	        // Look in the map for a matching context id.
	        temp = this[this.configuration.topicMap].findTopicId(topic);
	        // If the context id was not found...
            if (temp == null && this.configuration.allowFilenameSearch)
	            // Add a .htm extension.
	            topic += ".htm";
	    }
	    // Otherwise, if filename searches are not allowed, tell the user...
	    else if (!this.configuration.allowFilenameSearch) {
			// Tell the user and abort.
            alert("Invalid topic id! - " + topic);
            topic = "";
            return(topic);
	    }

	    if (temp == null && this.configuration.allowFilenameSearch)
		    // Look in the map for a matching file name.
	        temp = this[this.configuration.topicMap].findTopicFilename(topic);

	    if (temp != null)
			// Use the returned file name.
			topic = temp;
	    else {
			// Tell the user and abort.
            alert("Topic not found! \nId: " + topic);
            topic = "";
	    }
	}
	else // Otherwise (no data frame) ...
	    // If the topic name does not end .htm, add the .htm extension.
	    topic += (topic.search(/.html?$/) == -1 ? ".htm" : "");
	    
	return(topic);
}

HelpSystem.prototype.toggleDisplay = function hsToggleDisplay(targetId, source, prompt) {
	// Event handler for toggle display events.
	this.topic.toggleDisplay(targetId, source, prompt);
}

HelpSystem.prototype.initialiseSearchApplet = function hsInitialiseSearchApplet() {
	// Search applet initialisation event handler.
	this.currentNavTool.initialise();
}

HelpSystem.prototype.setSearching = function hsSetSearching() {
	this.currentNavTool.searching = true;
}

HelpSystem.prototype.selectNavTool = function hsSelectNavTool(type) {
	var tool;
	var selector;
	
	var doc = this.optFrame.document;
	for (i = 0; i < 5; i++) {
		tool = ["Contents","Index","Search","Links","Glossary"][i];
		selector = doc.getElementById(tool);
		if (!selector) {
			selector = doc.getElementById(tool.toLowerCase());
		}
		if (selector) {
			if (selector.href != null) {
				selector.className  = (tool.isEqualIgnoreCase(type) ? "selected" : "");
			}
			else if (selector.name != null) {
				selector.disabled = (tool.isEqualIgnoreCase(type) ? true : false);
			}
		}
	}
}

HelpSystem.prototype.loadPdf = function hsLoadPdf() {
	/* Requires that the href of the first link in the document contains 
	   the name of the pdf file to load. */ 
	
	// In this frame, go back to the previous topic.
	this.topicFrame.history.back();
	// Open a new window and display the associated Acrobat file in it.
	if (typeof this.topicFrame.document.links[0] != "undefined")
	{
		var w = this.topicFrame.open(this.topicFrame.document.links[0].href, "pdf");
		if (w)
			w.focus();
	}
	else
	{
		alert("No PDF topic - please report this error to Northgate.");
	}
}


// *** HelpSystem class methods ***
HelpSystem.parseQueryString = function hsParseQueryString(queryString) {
	/* Splits a query string into name/value pairs and stores them as
	   properties of an	object. If the string contains an unnamed value,
	   this is stored in the topicid property.
	   
	   Returns a reference to the object.
	   	
	   Parameters:
		   queryString - the query string to check.
		
	   Return value:
		   None.
	*/

	var qp;
	var i;
	var name;
	var value;

	// Get the query string.	
	var qs = queryString;
	// Strip off the initial question mark.
	if (qs.substring(0, 1) == "?")
		qs = qs.substring(1);
	
	// Create an object to hold the command line parameters.
	var urlParams = new Object();
	// Separate out the different parameters.
	var q = qs.split("&");
	// For each parameter...
	for (i = 0; i < q.length; i++) {
		// Separate the name and the value.
		qp = q[i].split("=");
		// If there is a name...
		if (qp.length > 1) {
			// Get the name and value.
			name = qp[0].toLowerCase();
			value = qp[1];
		}
		else {	// No name...
			// Use topicid as the name.
			name = "topicid";
			// Get the value
			value = qp[0];
		}
		// Remove any quotes from around the value.
		value = value.replace(/^(?:"|%22)/, "").replace(/(?:"|%22)$/, "");
		// Create a property with that name and value.
		urlParams[name] = value;
	}
	
	return urlParams;
}


// HelpSystem class variables.
HelpSystem.RE_BLANKPAGE = /^(javascript:'<html><\/html>'|about:blank)$/i;
HelpSystem.BLANKPAGE = "javascript:\'<html></html>\'";
HelpSystem.NAVTOOL = "navtool";
HelpSystem.NAVINDEX = "navix";
HelpSystem.TOPICID = "topicid";
HelpSystem.INDEXREF = "indexref";
HelpSystem.TXTSEARCH = "txtsearch";

