//
// Common JavaScript-functies
// BSL / 2006 - 2009 
//

// Hint(URL) toont de hint tekst bij een button in de menubar
function ShowHint(hint_url)
{
	document.getElementById("imageHint").src = "001/images/mouseovers/" + hint_url + ".jpg";
}

// ClearHint() maakt de hinttekst graphic leeg
function ClearHint()
{
	document.getElementById("imageHint").src = "001/images/mouseovers/algemeen.jpg";
}

function popUp(URL)
{
	var screenWidth	=	(screen.width - (screen.width-5));
	var screenHeight = (screen.height - (screen.height-100));
	day	=	new Date();
	id = day.getTime();
	eval("page" + id + " = window.open(URL, '" + id + "', 'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,resizable=1,width=450,height=325,top='+screenHeight+',left='+screenWidth+'');");
}

/**
 * Shows a dropdown-menu (sub) below an element (main)
 */
function showSubs(mainName, subName)
{
	var mainElement = getElement(mainName);
	var subElement  = getElement(subName);

	if (mainElement && subElement)
	{
		mainLeft = getLeft(mainElement);
		mainTop  = getTop(mainElement);

		subElement.style.top = mainTop + mainElement.offsetHeight;
		subElement.style.left = mainLeft;

		subElement.style.display = "";
	}
}

/**
 * Hides the dropdown-menu below an element
 */
function hideSubs(subName)
{
	var element = getElement(subName);
	if (element)
		element.style.display="none";
}

/**
 * Returns the element in a document which name is the input parameter
 */
function getElement(name)
{
	if (document.layers)
	{
		return document.layers[name];
	}
	else if (document.all)
	{
		return document.all[name];
	}
	else if (document.getElementById)
	{
		return document.getElementById(name);
	}
	return undefined;
}

/**
 * Gets the x-coordinate for an object
 */
function getLeft(obj)
{
	var curleft = 0;

	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curleft += obj.offsetLeft;
			obj = obj.offsetParent;
		}
	}
	curleft += obj.offsetLeft;
	
	return curleft;
}

/**
 * Get the y-coordinate for an object
 */
function getTop(obj)
{
	var curtop = 0;

	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curtop += obj.offsetTop;
			obj = obj.offsetParent;
		}
	}
	curtop += obj.offsetTop;

	return curtop;
}

/**
 * Remove a GET param from a URL
 *
 * @author Vincent
 *
 * @param url 		A URL including one or more GET params (e.g. "http://help.com?test=3&part=vier")
 * @param paramName	Name of the GET param to delete from the URL (e.g. "test")
 * @return 			The URL without the GET param specified
 */
function removeURLParam(url, paramName)
{
	var urlBegin, urlEnd, index, indexPast, paramString;

	paramString = "&" + paramName + "=";
	index = url.lastIndexOf(paramString);
	if (index == -1)
	{
		// Not found with "&"
		paramString = "?" + paramName + "=";
		index = url.lastIndexOf(paramString);
	}
	if (index == -1)
	{
		// Not found with either "&" or "?"
		return url;
	}
	urlBegin = url.substring(0, index);
	urlEnd = url.substring(index + paramString.length);
	// Find position past the _value_ of the param to remove
	indexPast = urlEnd.indexOf("&");
	if (indexPast == -1)
		urlEnd = "";
	else
		urlEnd = url.substring(index + paramString.length + indexPast);
	if (paramString.charAt(0) == "?" && urlEnd.length > 0)
	{
		// Make sure the first param starts with a "?"
		urlBegin += "?";
		// Remove the "&" at the start
		urlEnd = urlEnd.substring(1);
	}

	return urlBegin + urlEnd;
}

/**
 * Replace a GET param in a URL (or add it if not yet presented)
 *
 * @author Vincent
 *
 * @param url 			A URL (possibly already including one or more GET params) (e.g. "http://help.com?test=3&part=vier")
 * @param paramName		Name of the GET param to replace in the URL (e.g. "test")
 * @param replaceValue	Value for the GET param to replace in the URL (e.g. "5")
 * @return 				The URL containing the GET param with the specified value
 */
function replaceURLParam(url, paramName, replaceValue)
{
	url = removeURLParam(url, paramName);
	if (url.indexOf("?") == -1)
	{
		// Doesn't contain a "?"
		url += "?";
	}
	else
	{
		// Already contains a "?"
		url += "&";
	}
	url += paramName + "=" + replaceValue;

	return url;
}

/**
 * Create a new browser window for the URL in the 'href' property of the anchor
 *
 * Necessary in XHTML as a replacement for the 'target' param; just replace
 * 'target="_blank"' with 'onclick="return openTargetInNewWindow(this);"'
 *
 * @author Vincent
 *
 * @param anchorObject	The anchor DOM object for which we want to create a new window
 * @return				Always false in order to prevent the default action for the event
 */
function openTargetInNewWindow(anchorObject)
{
	window.open(anchorObject.href);
	// Prevent default action for 'onclick' event
	return false;
}

/**
 * Checks if a character is a digit
 * 
 * @param ch	The character to check
 * @return True if the character is a digit, false otherwise
 */
function isDigit(ch)
{
	return (ch >= '0' && ch <= '9');
}

function isNumeric(value)
{
	return value.match(/^[0-9]+$/);
}

/**
 * Tests if a string consists of just space chars
 */
function isEmpty(string)
{
	return (string.replace(/ /g, "") == "");
}

function getElementsByClassName(className, tag, elm)
{
    var testClass = new RegExp("(^|\\s)" + className + "(\\s|$)");
    var tag = tag || "*";
    var elm = elm || document;
    var elements = (tag == "*" && elm.all)? elm.all : elm.getElementsByTagName(tag);
    var returnElements = [];
    var current;
    var length = elements.length;
    for (var i=0; i<length; i++)
    {
        current = elements[i];
        if (testClass.test(current.className))
        {
            returnElements.push(current);
        }
    }

    return returnElements;
}

/**
 * Relaxed, but almost complete validity check of e-mail addresses.
 *
 * Copied from <a>http://en.wikibooks.org/wiki/JavaScript/Best_Practices</a>
 */
function isValidEmail(str)
{
	// These comments use the following terms from RFC2822:
	// local-part, domain, domain-literal and dot-atom.
	// Does the address contain a local-part followed an @ followed by a domain?
	// Note the use of lastIndexOf to find the last @ in the address
	// since a valid email address may have a quoted @ in the local-part.
	// Does the domain name have at least two parts, i.e. at least one dot,
	// after the @? If not, is it a domain-literal?
	// This will accept some invalid email addresses
	// BUT it doesn't reject valid ones. 
	var atSym = str.lastIndexOf("@");
	if (atSym < 1)
	{
		// no local-part
		return false;
	}
	if (atSym == str.length - 1)
	{
		// no domain
		return false;
	}
	if (atSym > 64)
	{
		// there may only be 64 octets in the local-part
		return false;
	}
	if (str.length - atSym > 255)
	{
		// there may only be 255 octets in the domain
		return false;
	}

	// Is the domain plausible?
	var lastDot = str.lastIndexOf(".");
	// Check if it is a dot-atom such as example.com
	if (lastDot > atSym + 1 &&
		lastDot < str.length - 1)
	{
		return true;
	}
	//  Check if could be a domain-literal.
	if (str.charAt(atSym + 1) == '[' &&
		str.charAt(str.length - 1) == ']')
	{
		return true;
	}
	return false;
}

/**
 * A simple password complexity validator.
 *
 * Uses (abuses?) the fact that ECMAScript will even split the string if
 * it starts or end with a token (it will add an empty string to the array).
 */
function isValidPassword(password, minLength, minNumberOfNumberChars, minNumberOfSpecialChars)
{
	if (password.length < minLength)
	{
		return false;
	}
	var numOfNumbers = 0;
	for (var i = 0; i < password.length; i++)
	{
		var currentChar = password.charAt(i);
		if (currentChar >= '0' &&
			currentChar <= '9')
		{
			numOfNumbers++;
		}
	}
	if (numOfNumbers < minNumberOfNumberChars)
	{
		return false;
	}
	if (password.split(/[,.\/?<>`~!@#$%^&*()\-_+={};:\|\\'"]/).length - 1 < minNumberOfSpecialChars)
	{
		return false;
	}

	return true;
}

/**
 * Ajax class for account related functionality.
 * 2008-12-29 VZ
 */
function Account()
{
	this.xhr = zXmlHttp.createRequest();
}

Account.prototype.login = function(name, passwd, captcha, callBack)
{
	// Param name must be equal to vob.BslGlobalUrlParams.ckACTION
	var requestParams = "action=login";
	// Param name must be equal to vob.litplein.BslUrlParams.ckEMAILADDRESS
	requestParams += "&email=" + encodeURIComponent(name);
	// Param name must be equal to vob.litplein.BslUrlParams.ckPASSWORD
	requestParams += "&password=" + encodeURIComponent(passwd);
	// Param name must be equal to vob.litplein.BslUrlParams.ckCAPTCHA
	requestParams += "&captchaResponse=" + encodeURIComponent(captcha);

	this.ajaxRequest("ajax/accountActions.jsp",
					 "post",
					 requestParams,
					 // Make sure it will be treated as a form post
					 "application/x-www-form-urlencoded",
					 callBack);
};

Account.prototype.logoff = function(callBack)
{
	this.ajaxRequest("ajax/accountActions.jsp",
					 "post",
					 "action=logout",
					 // Make sure it will be treated as a form post
					 "application/x-www-form-urlencoded",
					 callBack);
};

Account.prototype.checkCAPTCHA = function(captcha, callBack)
{
	// Param name must be equal to vob.BslGlobalUrlParams.ckACTION
	var requestParams = "action=checkCAPTCHA";
	// Param name must be equal to vob.litplein.BslUrlParams.ckCAPTCHAREACTIE
	requestParams += "&captchaReactieResponse=" + encodeURIComponent(captcha);

	this.ajaxRequest("ajax/accountActions.jsp",
					 "post",
					 requestParams,
					 // Make sure it will be treated as a form post
					 "application/x-www-form-urlencoded",
					 callBack);
};

/**
 * Generic method for Ajax requests.
 *
 * 2009-01-22 VZ
 *
 * @param url		URL that will process the request
 * @param method	POST or GET ("post" or "get"); defaults to GET when null or ""
 * @param data		POST data; may be empty for GET
 * @contentType		Will be put in 'Content=type' header if not empty
 * @callBack		Function that will be called when the response comes in; receives
 *					response data as a JS object in a single argument
 * returns			The response of the call back function
 */
Account.prototype.ajaxRequest = function(url, method, data, contentType, callBack)
{
	var oThis = this;
	var oXHR = this.xhr;

	if (oXHR.readyState != 0)
	{
		// Cancel any active requests (this is also always needed after a completed request!)
		oXHR.abort();
	}

	// Open connection to the server
	if (method == null ||
		method == "")
	{
		method = "get";
	}
	oXHR.open(method, url, true);
	if (contentType != "")
	{
		oXHR.setRequestHeader("Content-type", contentType);
	}
	oXHR.setRequestHeader("Connection", "close");
	oXHR.onreadystatechange = function()
	{
		// Request completely processed
		if (oXHR.readyState == 4)
		{
			// Firefox may throw an exception while getting XHR status
			try { var xhrStatus = oXHR.status; }
			catch (e) { return; }

			// No errors (TODO: Make sure we retry in case of an error?)
			if (xhrStatus == 200 || xhrStatus == 304)
			{
				// Evaluate the returned text and pass to call back function
				callBack(JSON.parse(oXHR.responseText));
			}
		}
	};

	// Send the request
	oXHR.send(data);
};

/**
 * Loads a new CAPTCHA image in the 'img' tag under the 'imageParentElement' (but you
 * have to place the CAPTCHA 'img' tag as the _first_ 'img' under this parent in the DOM).
 * Also clears the CAPTCHA input field and sets the focus to it.
 *
 * @param imageParentElement	Object representing the DOM element directly above the CAPTCHA 'img'
 * @param servletPath			Path to the CAPTCHA servlet
 * @param inputField			Object representing the DOM element of the input field for the
 *								CAPTCHA answer 
 */
function newCAPTCHA(captchaImageID, servletPath, inputField)
{
	// Clear previous CAPTCHA input
	inputField.value = "";
	// Force new CAPTCHA image
	var captchaImage = document.getElementById(captchaImageID);
	// Add a random request param in order to prevent hitting the cache
	captchaImage.src = servletPath + "?" + Math.random() * 10;
	inputField.focus();
}

/**
 * Simple class for a feedback element in a form.
 *
 * @param ID		DOM ID of the feedback element
 * @param textNode	Boolean declaring this as a text node (otherwise innerHTML will be used on the node)
 */
function FormFeedback(ID, textNode)
{
	if (isEmpty(ID))
	{
		return;
	}
	if (textNode == undefined)
	{
		return;
	}

	this.ID = ID;
	this.textNode = textNode;
	this.object = document.getElementById(ID);
}

FormFeedback.prototype.setText = function(string)
{
	if (this.textNode)
	{
		this.object.firstChild.nodeValue = string;
	}
	else
	{
		this.object.innerHTML = string;
	}
	this.object.style.display = "block";
};

FormFeedback.prototype.clearText = function()
{
	this.setText("");
	this.object.style.display = "none";
};

/**
 * A way to force a page reload from JS.
 */
function reloadCurrentPage()
{
	// Resetting the location doesn't work if the URL contains an anchor,
	// therefore we reset the location to the part without an anchor only
	var locationString = "" + window.location;
	var hashPos = locationString.indexOf("#");
	if (hashPos == -1)
	{
		// This is said to be more reliable than 'window.location.reload()'
		window.location = window.location;
	}
	else
	{
		window.location = locationString.substring(0, hashPos);
	}
}

function doAtWindowEvent(eventName, functionReference)
{
	if (window.addEventListener)
	{
		window.addEventListener(eventName.substring(2), functionReference, false);
	}
	else
	{
		window.attachEvent(eventName, functionReference);
	}
}

function doAfterPageLoad(functionReference)
{
	doAtWindowEvent("onload", functionReference);
}

/**
 * Methods extending the String object, copied from <http://www.somacon.com>.
 *
 * This set of Javascript functions trim or remove whitespace from the ends of strings. These
 * functions are attached as methods of the String object. They can left trim, right trim, or
 * trim from both sides of the string. Rather than using a clumsy loop, they use simple, elegant
 * regular expressions. The functions are granted to the public domain.
 */
String.prototype.trim = function()
{
	//return this.replace(/^\s+|\s+$/g, "");
	// Faster implementation from <http://blog.stevenlevithan.com/archives/faster-trim-javascript>
	return this.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
}

String.prototype.ltrim = function()
{
	return this.replace(/^\s+/, "");
}

String.prototype.rtrim = function()
{
	return this.replace(/\s+$/, "");
}
