// *** prototype extensions ***

/*
 * returns true if the string ends with the given suffix.
 */
String.prototype.endsWith = function(suffix) {
    return this.indexOf(suffix) + suffix.length == this.length;
}

/*
 * returns true if the string starts with the given prefix.
 */
String.prototype.startsWith = function(prefix) {
    return this.indexOf(prefix) == 0;
}

/*
 * Pushes the contents of the supplied array onto the end of this one.
 *
 * Example [1,2,3].append([4,5,6]) = [1,2,3,4,5,6]
 */
Array.prototype.append = function(array) {
    for (var i = 0; i < array.length; i++)
	this.push(array[i]);
}

/*
 * Returns a partially fixed representation of this function. Any arguments
 * supplied to this method will automatically be supplied when the curried
 * function is called, from left to right.
 *
 * i.e. f.curry(a)(b) = f(a, b)
 *
 * Example:
 * function multiply(a, b) { return a * b }
 * var double = multiply.curry(2);
 * multiply(2, 3) = double(3)
 */
Function.prototype.curry = function() {
    var args = new Array();
    for (var i = 0; i < arguments.length; i++)
	args.push(arguments[i]);
    var curriedFunction = function() {
	var me = arguments.callee;
	var callArgs = new Array();
	callArgs.append(me.curriedArgs);
	callArgs.append(arguments);
	return me.func.apply(null, callArgs);
    }
    curriedFunction.curriedArgs = args;
    curriedFunction.func = this;
    return curriedFunction;
}

/*
 * Unfixes the rightmost fixed argument on a curried function.
 * If the function is not curried, then this method errors.
 */
Function.prototype.uncurry = function() {
    var args = this.curriedArgs;
    if (args) {
	if (args.length == 1)
	    return this.func;
	return this.func.curry(args.slice(0, -1));
    }
    throw new TypeError("function is not curried");
}

/*
 * Returns an array where the contents are the results of a call to the
 * supplied function with each element in the array in turn.
 *
 * This function must only take a single argument, which must be a function.
 *
 * Example [1,2,3].map(function(o){return pow(o,2)}) = [1,4,9]
 */
Array.prototype.map = function(func) {
    if (this.length == 0)
	return this;
    var result = new Array(this.length);
    for (var i = 0; i < this.length; i++)
	result[i] = func.call(null, this[i]);
    return result;
}

/*
 * Returns a new array containing only the elements where f(e) is true.
 *
 * Example [1,2,3].filter(function(o){return o > 1}) = [2,3] 
 */
Array.prototype.filter = function(func) {
    var result = new Array();
    for (var i = 0; i < this.length; i++) {
	if (func.call(null, this[i]))
	    result.push(this[i]);
    }
    return result;
}

/*
 * Reduces an array of elements down to a single value, using repeated
 * applications of the supplied function between the elements.
 * i.e. [a,b,c].reduce(f) = f(f(a, b), c)
 * 
 * This is only of real use with associative operations, in other words where
 *   (a op b) op c = a op (b op c)
 *
 * Example a sum function; [1,2,3].reduce(function(a,b){return a+b}) = 6
 *
 * This function can take multiple arguments. The first must be the function,
 * any subsequent arguments will be prepended to the array as initialisers
 * before reducing begins.
 */
Array.prototype.reduce = function(func) {
    if (this.length == 0)
	return null;
    var elements;
    for (var i = 1; i < arguments.length; i++)
	this.unshift(arguments[i]);
    var reduced = this[0];
    for (var i = 1; i < this.length; i++)
	reduced = func.call(null, reduced, this[i]);
    for (var i = 1; i < arguments.length; i++)
	this.shift();
    return reduced;
}

// *** core functions ***

/*
 * Finds the first parameter with the specified name
 */
function getPageParam(name) {
    var re = new RegExp(name + "=([^&=]*)(&|$)?");
    var match = re.exec(window.location.search);
    return match ? decodeURIComponent(match[1]) : null;
}

/*
 * Returns an array of all the parameters that have that name
 */
function getPageParams(name) {
    var re = new RegExp(name + "=([^&=]*)(&|$)?", "g");
    var result = new Array();
    var match;
    while (match = re.exec(window.location.search))
	result.push(decodeURIComponent(match[1]));
    return result.length == 0 ? null : result;
}

/*
 * Creates a popup window.
 *
 * Any of the arguments to this function can be null or omitted.
 * If url is null, an empty popup will be created.
 * If no name is specified, a randomly generated name will be assigned.
 */
function popup(url, width, height, features, name) {
    if (!url)
	url = "";
    var f = "";
    if (width)
	f += "width=" + width;
    if (height) {
	if (f.length > 0)
	    f += ",";
	f += "height=" + height;
    }
    if (features) {
	if (f.length > 0)
	    f += ",";
	f += features;
    }
    if (!name)
        name = "popup" + Math.round(Math.random() * 1000);
    var popup = window.open(url, name, f);
    if (!popup.opener)
	popup.opener = self;
    return popup;
}

/*
 * Browser independent way of obtaining an HTTP request for use by Ajax
 */
function httpRequest() {
    var req;
    if (typeof XMLHttpRequest != "undefined") {
	req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
	var ua = navigator.userAgent.toLowerCase();
	if (ua.indexOf("msie 5") == -1)
	    req = new ActiveXObject("Msxml2.XMLHTTP");
	else if (ua.indexOf("mac") == -1)
	    req = new ActiveXObject("Microsoft.XMLHTTP");
    }
    return req;
}

// returns the last non-false result from a queued function, or false if one
// is encountered
function processHandlerQueue(event) {
    event = event || window.event;
    var elem = event.target || event.srcElement;
    // annoying bug in moz, it seems to dispatch onsubmit events with the
    // wrong source element if no inputs have been changed, and enter pressed
    // in input element
    if (event.type == "submit" && elem.form)
	elem = elem.form;
    var queue = elem.handlerQueue[event.type];
    var result;
    for (var i = 0; i < queue.length; i++) {
	result = queue[i].call(elem, event);
	// !result not good enough here, if handler returns undefined we want
	// to continue processing queue
	if (result == false)
	    return false;
    }
    return result;
}

// note, if func returns false, queue processing is aborted
// ignoring the case of image map onmouseover where returning true == abort
function queueEventHandler(obj, event, func) {
    if (!obj.handlerQueue)
	obj.handlerQueue = new Array();
    if (obj.handlerQueue[event]) {
	obj.handlerQueue[event].push(func);
    } else {
	var handlers = new Array();
	obj.handlerQueue[event] = handlers;
	var existingHandler = eval("obj.on" + event);
	if (existingHandler)
	    handlers.push(existingHandler);
	handlers.push(func);
	// bit of an ugly hack, but can't find any other way of aborting form
	// submission. seems to ignore retval if using addEventListener
	eval("obj.on" + event + " = processHandlerQueue");
    }
}

function update_message_body ()
{
	var Group_Name = document . input_form . Group_Name . value;
	var Contact_Name  = document . input_form . Contact_Name  . value;
	var Position  = document . input_form . Position  . value;
	var Group_Type  = document . input_form . Group_Type  . value;
	var Address1  = document . input_form . Address1  . value;
	var Address2  = document . input_form . Address2  . value;
	var Address3  = document . input_form . Address3  . value;
	var City  = document . input_form . City  . value;
	var Postcode  = document . input_form . Postcode  . value;
	var Telephone  = document . input_form . Telephone  . value;
	var Email  = document . input_form . Email  . value;

	document . proxy_form . message_body . value =
		"A web membership has been requested with the following details:-\n"
	+	"\n"
	+	"Group Name: " + Group_Name + "\n"
	+	"Contact Name: " + Contact_Name + "\n"
	+	"Position: " + Position + "\n"
	+	"Group Type: " + Group_Type + "\n"
	+	"Address: " + Address1 + "\n"
	+	"         " + Address2 + "\n"
	+	"         " + Address3 + "\n"
	+	"City: " + City + "\n"
	+	"Post Code: " + Postcode + "\n"
	+	"E-Mail Address: " + Email + "\n";

	return true;
}
