
/* ---------------------------------------------------------------------------
 *
 *   JavaScript for NED TOC servlet.
 *
 * --------------------------------------------------------------------------
 */

/* Handler for resize event. */
window.onresize = disableButtonsInNN4;


/* ---------------------------------------------------------------------------
 *   Global variables and constants
 * ---------------------------------------------------------------------------
 */

/* Browser information. */
var is = new Is();
/* Name of the ToC content frame. */
var TOC_CONTENT_FRAME = "toc_content";
/* Name of the ToC navigation frame. */
var TOC_NAVIGATION_FRAME = "toc_navigation";
/* Name of the ToC frameset frame. */
var TOC_FRAMESET_FRAME = "toc";
/* Value of the name attribute of the Next button. */
var NEXT_BUTTON_NAME = "next";
/* Value of the name attribute of the Prev button. */
var PREV_BUTTON_NAME = "prev";

/* Value of the src attribute when Next button is enabled. */
var NEXT_BUTTON_ENABLED_SRC = "NED?action=retrieve&identifier=dn01159505&edition=1&language=en&coverage=global&encoding=gif&component=data&item=data";

/* Value of the src attribute when Prev button is enabled. */
var PREV_BUTTON_ENABLED_SRC = "NED?action=retrieve&identifier=dn01159517&edition=1&language=en&coverage=global&encoding=gif&component=data&item=data";

/* Value of the src attribute when Next button is disabled. */
var NEXT_BUTTON_DISABLED_SRC = "NED?action=retrieve&identifier=dn01162883&edition=1&language=en&coverage=global&encoding=gif&component=data&item=data";

/* Value of the src attribute when Prev button is disabled. */
var PREV_BUTTON_DISABLED_SRC = "NED?action=retrieve&identifier=dn01162895&edition=1&language=en&coverage=global&encoding=gif&component=data&item=data";




/* ---------------------------------------------------------------------------
 *   Functions
 * ---------------------------------------------------------------------------
 */

/*
 * Gets browser information and version.
 *
 * This is the same function as in NED JavaScript and should be updated
 * at the same time.
 */
function Is () {
    // convert all characters to lowercase to simplify testing
    var agt=navigator.userAgent.toLowerCase();

    // detecting browser version
    this.major = parseInt(navigator.appVersion);

	// For Netscape Navigator 4.x.
    this.nav  = ((agt.indexOf('mozilla')!=-1) && ((agt.indexOf('spoofer')==-1)
                        && (agt.indexOf('compatible') == -1)));
    this.nav4 = (this.nav && (this.major == 4));

    // For Mozilla and Netscape version 6 or newer.
    this.nav6up = this.nav && (this.major >= 5);

	// For Internet Explorer.
    this.ie   = (agt.indexOf("msie") != -1);
    this.ie4up  = (this.ie  && (this.major >= 4));

	// For Opera.
	this.opera = (agt.indexOf("opera") != -1);
	this.opera6up = ((agt.indexOf("opera 6") != -1) || (agt.indexOf("opera/6") != -1 ));
	if(this.opera6up) {
		// Force Opera to act like NN4.
		this.ie = false;
		this.ie4up = false;
		this.nav4 = true;
	}
}



// -- TOC synchronization ----------------------------------------------------

/*
 * Sets the class attribute of the given A element to 'selected'. 
 * This indicates that the element is selected by the user.
 *
 * The ToC update have to be to done for every browser, otherwise 
 * Next->Prev->Next->Prev combination doesn't work.
 *
 * Tested with: Mozilla 2002030604, NN4.75, IE4, IE5.5
 *
 * Parameters:
 * - elem: Selected A element (not used at the moment).
 * - query: NED ToC query that selects an element.
 *
 * Returns: true.
 */
function select(elem, query) {
	window.open(query, TOC_CONTENT_FRAME);
	return true;
}

/*
 * Same as the function above.
 * This is used with the old NED 5.1 ToC servlet, for example in NEMU 
 * when document set has JavaScript from 5.1.1 but the ToC servlet itself 
 * is from 5.1.  NED 5.1.1 uses the function above.
 */
function select(elem) {
	var links = document.links;		// All links in the document.

	// Remove previously selected elements.
	for(var i = 0 ; i < links.length ; i++) {
		if(links[i].className == 'selected') {
			links[i].className = null;
		}
	}
	// Set current element selected.
	elem.className = 'selected';

	// Update Next/Prev buttons.
	updateButtons(elem);

	return true;
}

/*
 * Updates TOC document.
 *
 * Parameters:
 * - query: TOC query from the current element.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function updateTOC(query, elem) {
	window.open(query, TOC_CONTENT_FRAME);

	// Update Next/Prev buttons.
	// But don't do the update if the navigation frame doesn't exist. 
	// This happens with PDF ToCs.
	if(window.name != TOC_FRAMESET_FRAME) {
		updateButtons(elem);
	}
}



// -- Next/Prev buttons ------------------------------------------------------

/*
 * Activates the next link in the TOC.
 *
 * The function is meant to be used from ToC navigation frame.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function moveTOCNext() {
	if(!is.nav4) {
		var tocLinks = parent.toc_content.document.links;
		// List of links in the TOC.

		for(var i = 0 ; i < tocLinks.length ; i++) {
			if(tocLinks[i].className == 'selected') {
				// Active next link only if there is a one.
				if((i + 1) < tocLinks.length) {
					activateLink(tocLinks[i+1]);
					// Next link has been activated so there is 
					// always a possibility to active the previous link.
					enablePrevButton();
					// Disable Next button if the current link is 
					// the last link in the TOC.
					if((i + 2) == tocLinks.length) {
						// i+1 is the last link, so i+2 is the max lenght.
						// Disable button only when it isn't a branch.
						if(!isBranchNode(tocLinks[i+1])) {
							disableNextButton();
						}
					}
				} else {
					// Current node is the last visible node and
					// Next button is enabled, so call servlet to
					// open next node.
					moveNextDirection(tocLinks[i]);
				}
				return;
			}
		}

		// No selected link node, activate the first node in the TOC:
		if(tocLinks.length > 0) {
			activateLink(tocLinks[0]);
			disablePrevButton();
		}
	}
}

/*
 * Actives the previous link in the TOC.
 *
 * The function is meant to be used from ToC navigation frame.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function moveTOCPrev() {
	if(!is.nav4) {
		var tocLinks = parent.toc_content.document.links;
		// List of links in the TOC.

		for(var i = 0 ; i < tocLinks.length ; i++) {
			if(tocLinks[i].className == 'selected') {
				// Active previous link only if there is a one.
				if((i - 1) >= 0) {
					movePrevDirection(tocLinks[i]);
					// Previous link has been activated so there is 
					// always a possibility to active the next link.
					enableNextButton();
					// Disable Prev button if the current link is 
					// the first link in the TOC.
					if((i - 1) == 0) {
						disablePrevButton();
					}
				}
				return;
			}
		}
	}
}

/*
 * Simulates the click event to the target link.
 *
 * Parameters:
 * - targetLink: Target link as a Link object.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function activateLink(targetLink) {
	if(is.nav6up) {
		// For Mozilla and Netscape 6.
		// Open document in the document window.
		window.open(targetLink.getAttribute("href"), targetLink.getAttribute("target"));
		// Perform click action.
		if(targetLink.getAttribute("onclick") != null) {
			var newClick = document.createEvent("MouseEvents");
			newClick.initEvent("click", false, true);
			targetLink.onclick(newClick);
		}
	} else if(is.ie4up) {
		// For IE 4/5.
		targetLink.click();
	} else {
		// Other browsers.
		window.alert("Next and Prev buttons are not supported in your browser");
	}
}

/*
 * Take request from the current element and uses it to call ToC servlet 
 * to show next node.
 *
 * This function is called only when the current node is the last node
 * of the visible ToC. The results may be interesting, if you call it 
 * sometimes else.
 *
 * Side effects: ToC frame is updated.
 *
 * Tested with: Mozilla 2002030604, IE4, IE5.5
 *
 * Parameters:
 * - elem: Current link element.
 */
function moveNextDirection(elem) {
	if(is.nav6up || is.ie4up) {
		if(!isBranchNode(elem)) {
			// Don't do anything if this is node contains only document 
			// reference.
			return;
		}
		makeMoveCall(elem, "next");
	}
}

/*
 * Generates Prev call to the ToC servlet. The given link is used as
 * a base for the call.
 *
 * Side effects: ToC frame is updated.
 *
 * Tested with: Mozilla 2002030604, IE4, IE5.5
 *
 * Parameters:
 * - elem: A link element.
 */
function movePrevDirection(elem) {
	if(is.nav6up || is.ie4up) {
		makeMoveCall(elem, "prev");
	}
}

/*
 * Generates a move call to the servlet.
 *
 * Side effects: ToC frame is updated.
 *
 * Tested with: Mozilla 2002030604, IE4, IE5.5
 *
 * Parameters:
 * - elem: A link element. This element is used when generating the call.
 * - direction: move parameter value, "next" or "prev".
 */
function makeMoveCall(elem, direction) {
	if(elem.getAttribute("href").indexOf("TOC?") == -1) {
		// href="NED?..."
		if(elem.getAttribute("onclick") != null) {
			var value = removeQueryParameter(getOnClickValue(elem), "move=");
			var result = value.substr(value.indexOf("?")+1);
			result = result.substr(0, result.indexOf("'"));
			window.open("TOC?move=" + direction + "&" + result, TOC_CONTENT_FRAME);
		}
	} else {
		// href="TOC?..."
		var value = removeQueryParameter(elem.getAttribute("href"), "move=");
		var result = value.substr(value.indexOf("?")+1);
		window.open("TOC?move=" + direction + "&" + result, elem.getAttribute("target"));
	}
}

/*
 * Checks the given element if it is a branch node.
 *
 * Tested with: Mozilla 2002030604, IE4, IE5.5
 *
 * Parameters:
 * - elem: A link element.
 *
 * Returns: true if the link is a branch and false otherwise.
 */
function isBranchNode(elem) {
	if(elem.getAttribute("href").indexOf("TOC?") != -1) {
		// href attribute contains TOC request.
		return true;
	}
	if((getOnClickValue(elem).indexOf("TOC?") != -1) && (getOnClickValue(elem).indexOf("select(") == -1)) {
		// oclick attribute conteins TOC request and 
		// it is not in a select function.
		return true;
	}
	return false;
}

/*
 * Returns onclick attribute value of the given node.
 *
 * Returns: onclick attribute contents as a string or an empty string 
 * if the attribute doesn't exist.
 *
 * Tested with: Mozilla 2002030604, NN4.75, IE4, IE5.5
 */
function getOnClickValue(elem) {
	if(is.ie4up) {
		return elem.getAttribute("onclick").toString();
	}
	if(is.nav6up) {
		return elem.getAttribute("onclick");
	}
	return "";
}

/*
 * Opens a document that is marked with 'selected' in class attribute 
 * to the document window.
 *
 * This function is supposed to be called from onload event when Prev
 * request has been executed.
 *
 * Tested with: Mozilla 2002030604, NN4.75, IE4, IE5.5, Opera 6.01
 */
function openSelectedDocument() {
	if(!is.nav4) {
		var tocLinks = parent.toc_content.document.links;
		// List of links in the TOC.

		for(var i = 0 ; i < tocLinks.length ; i++) {
			if(tocLinks[i].className == 'selected') {
				openDocument(tocLinks[i]);
				return;
			}
		}
	}
}

/*
 * Executes NED query to a window set in the target attribute.
 *
 * Tested with: Mozilla 2002030604, NN4.75, IE4, IE5.5, Opera 6.01
 */
function openDocument(elem) {
	if(elem.getAttribute("href").indexOf("NED?") != -1) {
		window.open(elem.getAttribute("href"), elem.getAttribute("target"));
	}
}

/*
 * Sets Next and/or Prev buttons if necessary.
 *
 * Context: This function is meant to be used from frameset containing ToC 
 * navigation (Next/Prev buttons) and ToC contents frames or from
 * ToC contents frame. The value of the tocLinks variable have be modified 
 * if the function is used from somewhere else.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function setButtons() {
	if(is.nav4) {
		// Netscape Navigator 4.x is not supported. Yet, at least.
		disableNextButton();
		disablePrevButton();
		return;
	}

	if(window.name == TOC_CONTENT_FRAME) {
		if(parent.toc_navigation.document.images.length <= 0) {
			// Navigation frame is not yet fully loaded, so don't try to
			// set buttons. Let the navigation frame do the setting.
			return;
		}
	}

	if(window.name != TOC_FRAMESET_FRAME) {
		// Don't execute when ToC is in toc frame (PDF ToCs, for example).
		var root = parent.toc_content;
		if(root == null) {
			root = toc_content;
		}
		var tocLinks = root.document.links;
		for(var i = 0 ; i < tocLinks.length ; i++) {
			if(tocLinks[i].className == 'selected') {
				// The first link of the TOC is active, disable Prev.
				if(i == 0) {
					disablePrevButton();
				} else {
					enablePrevButton();
				}
				// The last link of the TOC is active.
				// Disable Next if it isn't a branch.
				if((i + 1) == tocLinks.length) {
					if(!isBranchNode(tocLinks[i])) {
						disableNextButton();
					} else {
						enableNextButton();
					}
				} else {
					enableNextButton();
				}
				return;
			}
		}

		// None of the TOC items is 'selected'.
		// The first link is assumed so disable Prev.
		disablePrevButton();
		enableNextButton();
	}
}

/*
 * Disables Next and Prev buttons in NN4.x.
 *
 * This function is also used from onresize event to keep buttons
 * disabled when the frameset is resized by the user.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function disableButtonsInNN4() {
	if(is.nav4) {
		if(window.name == TOC_NAVIGATION_FRAME) {
			// Netscape Navigator 4.x is not supported. Yet, at least.
			disableNextButton();
			disablePrevButton();
		}
	}
}

/*
 * Updates the state of the Next/Prev buttons.
 *
 * Parameters:
 * - linkElem: Link element which is used to determine the action for buttons.
 *             Normally this is the element the user has clicked.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function updateButtons(linkElem) {
	if(!is.nav4) {
		// Don't process further if the parameter is invalid.
		if(linkElem == null) {
			return;
		}
		if(linkElem.href == null) {
			return;
		}
		if(window.name == TOC_FRAMESET_FRAME) {
			return;
		}

		var tocLinks = document.links;	// All links in the ToC.

		if(tocLinks[0].href == linkElem.href) {
			// linkElem is the first link in ToC.
			disablePrevButton();
		} else {
			enablePrevButton();
		}
		if(tocLinks[tocLinks.length-1].href == linkElem.href) {
			// linkElem is the last link in ToC.
			// Disable Next if the last node is not a branch.
			if(!isBranchNode(tocLinks[tocLinks.length-1])) {
				disableNextButton();
			} else {
				enableNextButton();
			}
		} else {
			enableNextButton();
		}
	}
}

/*
 * Disables Next button.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function disableNextButton() {

	var root = parent.toc_navigation;	// Root object.

	if(root == null) {
		root = toc_navigation;
	}
	if(root.document.images.length <= 0) {
		// Frame is not loaded yet, so don't do anything.
		return;
	}
	root.document.images[NEXT_BUTTON_NAME].src = NEXT_BUTTON_DISABLED_SRC;
	changeCursor(root.document.images[NEXT_BUTTON_NAME]);
}

/*
 * Disables Prev button.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function disablePrevButton() {

	var root = parent.toc_navigation;	// Root object.

	if(root == null) {
		root = toc_navigation;
	}
	if(root.document.images.length <= 0) {
		// Frame is not loaded yet, so don't do anything.
		return;
	}
	root.document.images[PREV_BUTTON_NAME].src = PREV_BUTTON_DISABLED_SRC;
	changeCursor(root.document.images[PREV_BUTTON_NAME]);
}

/*
 * Enables Next button.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function enableNextButton() {

	var root = parent.toc_navigation;	// Root object.

	if(root == null) {
		root = toc_navigation;
	}
	if(root.document.images.length <= 0) {
		// Frame is not loaded yet, so don't do anything.
		return;
	}
	if(root.document.images[NEXT_BUTTON_NAME].src.indexOf(NEXT_BUTTON_ENABLED_SRC) == -1) {
		root.document.images[NEXT_BUTTON_NAME].src = NEXT_BUTTON_ENABLED_SRC;
		changeCursor(root.document.images[NEXT_BUTTON_NAME]);
	}
}

/*
 * Enables Prev button.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function enablePrevButton() {

	var root = parent.toc_navigation;	// Root object.

	if(root == null) {
		root = toc_navigation;
	}
	if(root.document.images.length <= 0) {
		// Frame is not loaded yet, so don't do anything.
		return;
	}
	if(root.document.images[PREV_BUTTON_NAME].src.indexOf(PREV_BUTTON_ENABLED_SRC) == -1) {
		root.document.images[PREV_BUTTON_NAME].src = PREV_BUTTON_ENABLED_SRC;
		changeCursor(root.document.images[PREV_BUTTON_NAME]);
	}
}

/*
 * Changes mouse cursor when it is over Next or Prev button.
 *
 * Parameters:
 * - elem: Next or Prev IMG element.
 *
 * Tested with: Mozilla 2001102309, NN4.75, IE4, IE5.5
 */
function changeCursor(elem) {
	if(!is.nav4 && !is.opera6up) {
		// cursor is not supported in Opera 6.
		if((elem.src.indexOf(PREV_BUTTON_ENABLED_SRC) != -1) || 
			(elem.src.indexOf(NEXT_BUTTON_ENABLED_SRC) != -1)) {
			if(is.ie) {
				// IE doesn't follow the CSS2 specification, but uses
				// "hand" instead of "pointer".
				elem.style.cursor = "hand";
			} else if(is.nav6up) {
				elem.style.cursor = "pointer";
			}
		} else {
			elem.style.cursor = "default";
		}
	}
}




// -- Utilities --------------------------------------------------------------

/*
 * Removes the defined parameter and its value from the request.
 *
 * Tested with: Mozilla 2002030604, NN4.75, IE4, IE5.5, Opera 6.01
 *
 * Returns: Query without the defined parameter.
 */
function removeQueryParameter(q, parameter) {

	var result = "";
	var queryStart = q.substr(0, (q.indexOf("?") + 1));
	var parameters = q.substr(q.indexOf("?") + 1);
	var splitQ = parameters.split("&");  // Query as an array of tokens.

	// Generate the result query by concatenating all tokens except the one
	// given in the parameters.
	for(var i = 0 ; i < splitQ.length ; i++) {
		if(splitQ[i].indexOf(parameter) < 0) {
			result = result + (result != "" ? "&" : "") + splitQ[i];
		} else {
			// Add pointer back to query if it exists.
			if(((i+1) == splitQ.length) && (splitQ[i].indexOf("#") != -1)) {
				result = result + splitQ[i].substr(splitQ[i].indexOf("#"));
			}
		}
	}

	return (queryStart + result);
}
