// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

// Toggle actions
var TOGGLE = 0;
var COOKIE = -99;
var OPAQUE = 1;
var TRANSLUSCENT = -1;
var EXPAND = 1;
var COLLAPSE = -1;

// ****************************************************
// Common Functions
// ****************************************************

// If debugging is on and condition is *not* true, throws up an alert.
// Otherwise, throws an exception.
var _debug = true;
function assert(condition, message) {
	if (condition) return;
	if (_debug) alert("Assertion: " + message);
	throw new Exception("Assertion: " + message);
}

// Clear all selected text/objects
function clearSelected() {
	var selected = null;
	try { selected = document.selection; } catch(x){ }
	if (selected == null) selected = window.getSelection();
	if (selected) {
		if (selected.empty) {
			selected.empty();
		} else if (selected.removeAllRanges) {
			selected.removeAllRanges();
		}
	}
}

function closeFloatWindow(id) {
	var windowElement = document.getElementById(id + "-window");
	if (windowElement) hide(windowElement);
}

// Creates and adds the header onto a float window
function createFloatWindowHeader(windowElement, id, title) {
	var header = document.createElement("div");
	header.id = id + "-header";
	header.className = "window-header";
	header.innerHTML = "<div style='text-align: right; width: 100%;'>" +
		"<a id=\"" + id + "-toggle\" href=\"javascript:toggleFloatWindow('" + id + "', '" + id + "-toggle');\">Opaque</a>&nbsp;&nbsp;" +
 		"<a href=\"javascript:closeFloatWindow('" + id + "');\">Close</a></div>" + 
		"<div style='text-align: center; width: 100%;'><strong>" + (title ? title : id) + "</strong> (shift-click to drag)</div>";
	// Allow this window to be dragged to a new location
	windowElement.onmousedown = function(e) {
		// Do not drag if something's already dragging
		if (_dragElement != null) return;
		// IE
		if (!e) e = window.event;
		// Require shift key to be down
		if (!e.shiftKey) return;
		startDrag(e.clientX, e.clientY, windowElement);
	}
	windowElement.appendChild(header);
	// Read the value from the cookie to set the opaque/transluscent bit.
	toggleFloatWindow(id, id + "-toggle", COOKIE);
	return header;
}

// Like document.getElementById, but asserts if it fails.
function getElementById(id) {
	var element = document.getElementById(id);
	assert(element != null, "Could not find element matching id '" + id + "'");
	return element;
}

// Returns the index of value in array, -1 if it doesn't exist.
function getIndex(array, value) {
	for (ii = 0; ii < array.length; ii++) {
		if (array[ii] == value) return ii;
	}
	return -1;
}

// Given a node, finds the first parent with the specified tag name.
// Matches the node itself if it has that tag name.
function getParentByTagName(element, tagName) {
	tagName = tagName.toUpperCase(); // As per W3C, tag name is always upper case
	while (element != null) {
		if (element.tagName == tagName) return element;
		element = element.parentNode;
	}
	assert(false, 'No parent found with a tag of ' + tagName);
	return null;
}

// Convenience method.  Returns the text of the first cell in the row.
function getRowText(row) {
	return trim(row.cells[0].innerHTML); // no innerText defined on TableCell, apparently
}

// Do something when the enter key is pressed.  Called from onKeyPress.
// event: Event received from onKeyPress
// functionToCall: Function to call if event indicated the enter key was pressed
function handleEnterKey(event, functionToCall) {
	try {
		var found = false;
		if (event.keyCode && event.keyCode == Event.KEY_RETURN) found = true;
		if (event.which && event.which == Event.KEY_RETURN) found = true;
		if (event.charCode && event.charCode == Event.KEY_RETURN) found = true;
		if (found) {
			// Cancel the event
			Event.stop(event);
			// Call our function
			if (functionToCall) functionToCall();
		}
	} catch (e) { 
		assert(false, "Failed to handle enter: " + e.message); 
	}
}

// Hides the element
function hide(element) {
	if (element.hide) {
		element.hide(); // Firefox
	} else {
		element.style.display = 'none'; // IE
	}
}

// Returns text, with occurrences of textToHighlight highlighted.
function highlightText(text, textToHighlight) {
	var index = text.toLowerCase().indexOf(textToHighlight.toLowerCase());
	if (index == -1) return text;
	return text.substr(0, index) + "<strong>" + text.substr(index, textToHighlight.length) + "</strong>" + text.substr(index + textToHighlight.length);
}

// Returns true if the event specified that a number key (or a nonprintable character) was entered.
// Usage: onkeypress="return isNumberKey(event)"
function isNumberKey(evt) {
	var c = (evt.which) ? evt.which : event.keyCode
  return (c < 31 || (c >= 48 && c <= 57));
}
      
// Move the _dragElement to the source of this mousemove event.
function moveElement(e){
	if (_dragElement == null) return false;

	// IE 
	if (!e) e = window.event;
	clearSelected();	
	var newLeft = (e.clientX + document.body.scrollLeft - _headOffsetX);
	if (newLeft < 0) newLeft = 0;
	var newTop = (e.clientY + document.body.scrollTop - _headOffsetY);
	if (newTop < 0) newTop = 0;
	_dragElement.style.top = newTop + "px";
	_dragElement.style.left = newLeft + "px";
}

// Creates a floating window element, and returns it.
function openFloatWindow(id, title) {
	var windowId = id + "-window";
	var windowElement = document.getElementById(windowId);
	if (windowElement != null) {
		// Show if hidden; return the body
		show(windowElement);
		return getElementById(id);
	}
	element = document.createElement("div");
	element.id = windowId;
	element.className = "window window-transluscent";
	var left = readCookie(element.id + "-left");
	var top = readCookie(element.id + "-top");
	if (left) element.style.left = left;
	if (top) element.style.top = top;
	document.body.appendChild(element);
	// Create the header
	var header = createFloatWindowHeader(element, id, title);
	// Create the body
	var bodyElement = document.createElement("div");
	bodyElement.id = id;
	element.appendChild(bodyElement);
	return bodyElement;
}

// Reads a cookie value.  Returns null if it doesn't exist.
// Taken from here: http://www.quirksmode.org/js/cookies.html
function readCookie(name, defaultValue) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return defaultValue;
}

// Removes the parent node with the specified tag.  May match
// the node itself, if it has the specified tag.
// node: Node to start the search at. This, and its parents, will be searched.
// tag: Tag name to look for.
function removeParent(node, tag) {
	var parent = getParentByTagName(node, tag);
	assert(parent != null, "getParentByTagName return null.");
	parent.parentNode.removeChild(parent);
}

// Given an element, replaces that element with the specified HTML.
// Returns the new element.
// element: Element to replace.
// html: string of HTML to replace it with.
function replaceElementWithHtml(element, html) {
	// Ideally, I could just use prototype's element.replace.  But that
	// fails when called on a TR, because IE does not allow that property
	// to be written.  
	// (Also, it does not return the new element, which makes it hard
	// to highlight.)
	// HOWEVER: Prototype's Insertion object *does* have a hack (anonymous
	// tables) to handle this.  So we use *that*, and handle the cleanup 
	// ourselves.
	new Insertion.After(element, html);
	var newElement = element.nextSibling;
	// It's possible the next sibling is text -- skip that.  We want elements only.
	while (newElement != null && newElement.nodeType != 1) {
		newElement = newElement.nextSibling;
	}
	element.parentNode.removeChild(element);
	return newElement;
}

// Shows the element
function show(element) {
	if (element.show) {
		element.show(); // Firefox
	} else {
		element.style.display = ''; // IE
	}
}

// Handle the dragging of an element
var _dragElement;
var _headOffsetX;
var _headOffsetY;
function startDrag(eventX, eventY, element) {
	// Do not drag if something's already dragging
	if (_dragElement != null || !element) return;
	_dragElement = element;
	_headOffsetY = eventY + document.body.scrollTop - _dragElement.offsetTop;
	_headOffsetX = eventX + document.body.scrollLeft - _dragElement.offsetLeft;	
	document.body.onmousemove = moveElement;
	document.body.onmouseup = stopDrag;
}

// Stop dragging an element
function stopDrag(){
	document.body.onmousemove = null;
	document.body.onmouseup = null;
	if (_dragElement != null) {
		writeCookie(_dragElement.id + "-left", _dragElement.style.left);
		writeCookie(_dragElement.id + "-top", _dragElement.style.top);
	}
	_dragElement = null;
}

// Adds the specified stylesheet, if it was not already added via this method
var _styleSheets = []
function styleSheetAdd(styleSheet) {
	for (var ii = 0; ii < _styleSheets.length; ii++) {
		if (_styleSheets[ii] == styleSheet) return;
	}
	
	// Code from: http://cse-mjmcl.cse.bris.ac.uk/blog/2005/08/18/1124396539593.html
	if(document.createStyleSheet) {
		document.createStyleSheet(styleSheet); // IE
	}	else {
		var styles = "@import url(' " + styleSheet + " ');";
		var link = document.createElement('link');
		link.rel='stylesheet';
		link.href='data:text/css,' + escape(styles);
		document.getElementsByTagName("head")[0].appendChild(link);
	}
	_styleSheets[_styleSheets.length] = styleSheet;
}

// Makes a float window opaque or transluscent
// id: id passed to the openFloatWindow call
// toggleId: id of the element that controls the toggle
// action: OPAQUE makes it opaque, TRANSLUSCENT makes it transluscent, and TOGGLE toggles between the two states.  Default is TOGGLE.
// COOKIE reads it from the cookie.
function toggleFloatWindow(id, toggleId, action) {
	var toggleElement = getElementById(toggleId);
	var windowElement = getElementById(id + "-window");
	if (!action || action == TOGGLE) action = windowElement.className.indexOf('window-transluscent') > -1 ? OPAQUE : TRANSLUSCENT;
	if (action == COOKIE) action = (readCookie("float-window-" +id, "transluscent") == "opaque" ? OPAQUE : TRANSLUSCENT);
	if (action == OPAQUE) {
		windowElement.className = windowElement.className.replace("transluscent", "opaque");
		toggleElement.innerHTML = "Transluscent";		
	} else {
		windowElement.className = windowElement.className.replace("opaque", "transluscent");
		toggleElement.innerHTML = "Opaque";		
	}
	writeCookie("float-window-" + id, action == OPAQUE ? "opaque" : "transluscent");
}

// Expands or collapses *all* toggles, as controlled by a master toggle.
// For this to work, assumes that the rows controlled by the individual toggles
// are named <toggleId>-row.
// toggleId: Id of the element that controls the master toggle (the image button)
// action: EXPAND expands, COLLAPSE collapses, and TOGGLE toggles between the two states.  Default is TOGGLE.
// Note that TOGGLE only means to toggle the state of the master toggle; all other toggles will be
// EXPANDed or COLLAPSEd based on that.
function toggleGroupMaster(toggleId, action) {
	var toggle = getElementById(toggleId);
	if (!action || action == TOGGLE) action = toggle.src.indexOf('collapse') > -1 ? COLLAPSE : EXPAND;
	toggles = document.getElementsByClassName('toggle');
	for (var ii = 0; ii < toggles.length; ii++) {
		if (toggles[ii].id == toggleId) continue;
		toggleGroup(toggles[ii].id + '-row', toggles[ii].id, action);
	}
	if (action == COLLAPSE) {
		toggle.src = toggle.src.replace("collapse", "expand")
		toggle.title = toggle.title.replace("Collapse", "Expand");
	} else {
		toggle.src = toggle.src.replace("expand", "collapse");
		toggle.title = toggle.title.replace("Expand", "Collapse");
	}
}

// Expands a collapsed group, or collapsed an expanded group
// rowId: Id of the element to toggle
// toggleId: Id of the element that controls the toggle (the image button)
// action: EXPAND expands, COLLAPSE collapses, and TOGGLE toggles between the two states.  Default is TOGGLE.
function toggleGroup(rowId, toggleId, action) {
	var toggle = getElementById(toggleId);
	if (!action || action == TOGGLE) action = Element.visible(rowId) ? COLLAPSE : EXPAND;
	if (action == COLLAPSE) {
		Effect.BlindUp(rowId, {duration:0.5});
		// hide($(rowId));
		toggle.src = toggle.src.replace("collapse", "expand")
		toggle.title = toggle.title.replace("Collapse", "Expand");
	} else {
		Effect.BlindDown(rowId, {duration:0.5});
		// show($(rowId));
		toggle.src = toggle.src.replace("expand", "collapse");
		toggle.title = toggle.title.replace("Expand", "Collapse");
	}
}

// Trims the text, removing trailling and prepended spaces.
function trim(s) {
   return s.replace(/^\s*|\s*$/g,"");
}

// Writes a cookie value.
// Taken from here: http://www.quirksmode.org/js/cookies.html
// name: Name of cookie
// value: Value of cookie
// days: Number of days cookie is valid for
function writeCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}






