/* -*- C -*- */

/* --------------------------------------------------------------------
 * Shine: The Become Interactive Client Application Framework
 * @(#) $Id: shine.js 4308 2006-05-05 14:07:42Z mo $
 * --------------------------------------------------------------------
 * Copyright (c) 2006 Become Interactive
 * http://www.becomeinteractive.co.uk
 * All rights reserved.
 * --------------------------------------------------------------------
 * This software is the confidential and proprietary information of
 * Become Interactive ("Confidential Information").
 *
 * You shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement you
 * with the terms of the license agreement you entered into with
 * Become Interactive.
 * --------------------------------------------------------------------
 */

var Shine;

if(typeof com == "undefined") var com = new Object();
if(typeof com.uk == "undefined") com.uk = new Object();
if(typeof com.uk.become == "undefined") com.uk.become = new Object();
if(typeof com.uk.become.shine == "undefined") com.uk.become.shine = new Object();

/**
 * com.uk.become.shine.Browser
 *
 * Provides information about the host browser.
 */

com.uk.become.shine.Browser = function()
{
	var ua;
	
	ua = navigator.userAgent.toLowerCase();	
	this.userAgent = navigator.userAgent;
	this.isOpera = ((ua.indexOf("opera") != -1) || window.opera);
	this.isWebTV = (ua.indexOf("webtv") != -1);
	this.isIE = (ua.indexOf("msie") != -1) && !com.uk.become.shine.Browser.isOpera && !com.uk.become.shine.Browser.isWebTV;
	this.isWebKit = (ua.indexOf('applewebkit') != -1);
	this.ieVersion = false;
	if(document.createElementNS)
		{
			this.isDOM = true;
		}
	else
		{
			this.isDOM = false;
		}
	if(this.ieIE)
		{
			this.ieVersion = navigator.appVersion.match(/MSIE (\d.\d)/)[1];
		}
}

/**
 * com.uk.become.shine.Core
 *
 * A layer of cross-browser glue for event handling, class-triggered behaviours,
 * and other support functions. 
 */

com.uk.become.shine.Core = function(window, document)
{
	this.version = '$Id: shine.js 4308 2006-05-05 14:07:42Z mo $';
	this.basePath = '/';
	this.rootPath = '/';
	this._abort = false;
	this._window = window;
	this._document = document;
	this._loaded = false;
	this._loadHandlers = new Array();
	this._validating = false;
	this._fieldValidators = new Array();
	this._forms = new Array();
	this._validateInterval = false;
	this._popupCount = 0;
	this.browser = new com.uk.become.shine.Browser();
	if(this.browser.isIE && (this.browser.isVersion < 5 || this.browser.isVersion >= 7))
	{
		this._abort = true;
		return false;
	}
	this.debug('Shine:', this.version);
	if (this.browser.isDOM)	
		{
			this.addEventHandler(document, 'DOMContentLoaded', { host: this, handler: this.loaded });
		}
	return this;
}
com.uk.become.shine.Core.prototype.debug = function()
{
	var args, a;
			
	args = new Array();
	d = args.length;	
	for(c = 0; c < arguments.length; c++)
		{
			args[d] = arguments[c];
			d++;
		}
	args = args.join(' ');	
	if(!arguments.length)
		{
			return false;
		}
	if(this.browser.isWebKit)
		{
			/* WebKit raises a DOM exception if the below is executed */
			if(window.console)
				{
					window.console.log(args);
				}
		}
	else if(this.browser.isOpera && window.opera.postError)
		{
			window.opera.postError(args);
		}
	else if(document.createEvent && window.dispatchEvent)
		{
			printfire = new Object();
			printfire.args = new Array(args);
			var ev = document.createEvent('Events');
			ev.initEvent('printfire', false, true);
			try
				{
					dispatchEvent(ev);
				}
			catch(e)
				{
				}
		}
	return true;
}
com.uk.become.shine.Core.prototype.createRequest = function(method, uri)
{
	return new com.uk.become.shine.Request(method, uri);
}
com.uk.become.shine.Core.prototype._loadScript = function(path)
{
	var sel, heads;
	
	this.debug('Shine:', 'Loading script from ' + path);
	if(this.browser.isDOM)
	{
		heads = document.getElementsByTagName('head');
		if(heads && heads.length)
		{
			heads = heads[0];
		}
		else
		{
			return false;
		}
		sel = document.createElement('script');
		this.setAttribute(sel, 'type', 'text/javascript');
		this.setAttribute(sel, 'src', path);
		heads.appendChild(sel);
	}
	else
	{
		document.write('<' + 'script type="text/javascript" src="' + url + '"></script>');
	}
}
com.uk.become.shine.Core.prototype.require = function(path, base)
{
	var req;

	if(typeof path == 'undefined')
		{
			Shine.debug('Attempt to require undefined path');
			return false;
		}
	if(base == 1)
		{
			path = this.basePath + path;
		}
	else if(base == 2)
		{
			path = this.rootPath + path;
		}
	return this._loadScript(path);
}
com.uk.become.shine.Core.prototype.setAttribute = function(element, name, value)
{
	if(this.browser.isDOM)
		{
			element.setAttribute(name, value);
		}
	else
		{
			if(name == 'class')
				{
					name = 'className';
				}
			eval('element.' + name + '= value');
		}
}
com.uk.become.shine.Core.prototype.addEventHandler = function(element, eventName, handler)
{
	var force;
	
	force = false;
	if(typeof element == 'string')
		{
			element = document.getElementById(element);
		}
	if(!element)
		{
			return false;
		}
	if(!handler || !handler.handler)
		{
			Shine.debug('Shine:', 'Invalid handler passed for event on' + eventName);
			return false;
		}
	if(this.browser.isWebKit && eventName == 'click')
		{
			force = true;
		}
	if(this.browser.isDOM && element.addEventListener && !force)
		{
			element.addEventListener(eventName, 
									 function(ev) { 
										 if(!Shine.callEventHandler(element, ev, handler)) {
											 if(ev.stopPropagation) { ev.stopPropagation(); }
											 if(ev.preventDefault) { ev.preventDefault(); }
											 ev.cancelBubble = true; 
											 return false;
										 }
										 return true; 
									 }, false);
		}
	else if(element.attachEvent && element.attachEvent && !force)
		{
			element.attachEvent('on' + eventName, function(ev) { return Shine.callEventHandler(element, (ev ? ev : window.event), handler); }, false);
		}
	else
		{
			eval('element.on' + eventName + ' = function(ev) { if(!Shine.callEventHandler(element, (ev ? ev : window.event), handler)) { ev.cancelBubble = true; ev.returnValue = false; return false; } return true; }');
		}
}
com.uk.become.shine.Core.prototype.callEventHandler = function(sender, event, handler)
{
	if(!handler.host)
		{
			handler.host = window;
		}
	handler.host._shineHandler = handler.handler;
	return handler.host._shineHandler(this, sender, event, handler.data);
}
com.uk.become.shine.Core.prototype.getAttribute = function(element, name)
{
	if(this.browser.isDOM)
		{
			return element.getAttribute(name);
		}
	if(name == 'class')
		{
			name = 'className';
		}
	return eval('element.' + name);
}
com.uk.become.shine.Core.prototype.getElementClass = function(element, forceDirect)
{
	var cn;

	if(element.shineStyleTarget && !forceDirect)
		{
			element = element.shineStyleTarget;
		}
	cn = this.getAttribute(element, 'class');
	if(typeof cn != 'string')
		{
			return '';
		}
	return cn;
}

com.uk.become.shine.Core.prototype.setElementClass = function(element, classes, replace)
{
	var cn = this.getElementClass(element);
	if(element.shineStyleTarget)
		{
			element = element.shineStyleTarget;
		}
	if(typeof element.shineOriginalClass != 'string')
		{
			element.shineOriginalClass = this.getElementClass(element);
		}
	if(!classes)
		{
			classes = '';
		}
	if(!replace)
		{
			classes = element.shineOriginalClass + ' ' + classes;
		}
	if(cn == classes) return true;
	this.setAttribute(element, 'class', classes);
}
com.uk.become.shine.Core.prototype.hasElementClass = function(element, classname, forceDirect)
{
	var cn;

	if(!forceDirect)
		{
			forceDirect = false;
		}
	cn = ' ' + this.getElementClass(element, forceDirect) + ' ';
	if(cn.indexOf(' ' + classname + ' ') != -1)
		{
			return true;
		}
	return false;
}													
com.uk.become.shine.Core.prototype._inputHandler = function(shine, sender, event)
{
	var valid, empty, focussed, s;
	
	valid = true;
	s = false;
	prevalidated = false;
	if(typeof sender.defaultValue != 'undefined')
		{
			if(sender.value == sender.defaultValue && sender.acErrorField)
				{
					valid = false;
					prevalidated = true;
				}
		}
	empty = (sender.value ? false : true);	
	focussed = (sender.focussed ? true: false);
	if(!prevalidated && !empty && sender.shineValidator)
		{
			ev = new Object();
			ev.value = sender.value;
			ev.target = sender;
			ev.empty = empty;
			ev.focussed = focussed;
			valid = this.callEventHandler(sender, ev, sender.shineValidator);
		}
	s = 'shine-input ';
	if(sender.shineValidateInProgress)
	{
		s += ' shine-input-progress';
		sender.shineValidated = false;
		sender.shineIncomplete = false;		
	}
	else if(empty)
	{
		s += ' shine-input-empty';
		sender.shineValidated = false;
		sender.shineIncomplete = true;
	}
	else if(valid)
	{
		s += ' shine-input-ok';
		sender.shineValidated = true;
		sender.shineIncomplete = false;
	}
	else
	{
		s += ' shine-input-bad';
		sender.shineValidated = false;
		sender.shineIncomplete = false;
	}
	if(focussed)
	{
		 s += ' shine-input-focus';
	}
	this.setElementClass(sender, s, false);
	return true;
}
com.uk.become.shine.Core.prototype.validateField = function(sender, event)
{
	return this._inputHandler(this, sender, event);
}
com.uk.become.shine.Core.prototype._inputFocus = function(shine, sender, event)
{
	sender.focussed = true;
	this._inputHandler(shine, sender, event);
	return true;
}
com.uk.become.shine.Core.prototype._inputBlur = function(shine, sender, event)
{
	sender.focussed = false;
	this._inputHandler(shine, sender, event);
	return true;
}
com.uk.become.shine.Core.prototype.addInputHandlers = function(elements, validator, data)
{
	var c;
	
	for(c = 0; c < elements.length; c++)
		{
			this.addInputHandler(elements[c], validator, data);
		}
}
com.uk.become.shine.Core.prototype.addLinkHandlers = function(elements, handler)
{
	var c;
	
	for(c = 0; c < elements.length; c++)
		{
			this.addLinkHandler(elements[c], handler);
		}
}
com.uk.become.shine.Core.prototype.addSubmitReplacements = function(elements)
{
	var c;
	
	for(c = 0; c < elements.length; c++)
		{
			this.addSubmitReplacement(elements[c]);
		}
}
com.uk.become.shine.Core.prototype.addSubmitReplacement = function(element)
{
	var link, span, tn;
	
	link = document.createElement('a');
	span = document.createElement('span');
	link._shineForm = element.form;
	this.setAttribute(link, 'href', 'javascript:void(0);');
	this.setAttribute(span, 'class', 'shine-replaced-button');
	id = this.getAttribute(element, 'id');
	if(id)
	{
		this.setAttribute(span, 'id', 'sr-' + id);
	}
	this.addEventHandler(link, 'click', { host: this, handler: this._submitHandler });
	span.appendChild(link);
	tn = document.createTextNode(this.getAttribute(element, 'value'));
	link.appendChild(tn);
	element.parentNode.replaceChild(span, element);
}
com.uk.become.shine.Core.prototype._submitHandler = function(Shine, sender, ev, data)
{
	if(sender._shineForm)
	{
		sender._shineForm.submit();
	}
	return false;
}
com.uk.become.shine.Core.prototype.addLinkHandler = function(element, handler)
{
	element.shineLinkRef = this.getAttribute(element, 'href');
	return this.addEventHandler(element, 'click', handler);
}
com.uk.become.shine.Core.prototype._createStyleWrapper = function(element)
{
	var newel;
	
	/* Create a wrapper element around an element, and set the child's
	 * .shineStyleTarget property to point to the new element. Useful for
	 * elements which can't really be styled themselves (e.g., list/drop-down
	 * boxes)
	 */
	if(element.parentNode && element.parentNode.shineStyleSource)
		{
			element.shineStyleTarget = element.parentNode;
			return element.parentNode;
		}
	newel = document.createElement('span');
	element.parentNode.replaceChild(newel, element);
	newel.appendChild(element);
	newel.shineStyleSource = true;
	element.shineStyleTarget = newel;
	return newel;
}
com.uk.become.shine.Core.prototype.addInputHandler = function(element, handler)
{
	if(typeof element == 'string')
		{
			element = document.getElementById(element);
		}
	if(!element)
		{
			return false;
		}
	this._createStyleWrapper(element);
	if(element.form)
		{
			if(!element.form.shineFields)
				{
					element.form.shineFields = new Array();
				}
			element.form.shineFields[element.form.shineFields.length] = element;
			if(!element.form.shineForm)
				{
					element.form.shineForm = true;
					this._forms[this._forms.length] = element.form;
				}
		}
	this.setElementClass(element, 'shine-input');
	element.shineValidator = handler;
	element.shineValidatorData = handler.data;
	element.shineValidated = false;
	element.shineIncomplete = true;
	element.shineValidateInProgress = false;
	this._fieldValidators[this._fieldValidators.length] = element;
	this.addEventHandler(element, 'focus', { host: this, handler: this._inputFocus });
	this.addEventHandler(element, 'blur', { host: this, handler: this._inputBlur });
	return element;
}
com.uk.become.shine.Core.prototype._validateFields = function()
{
	var e, complete, valid;
	
	if(this._validating)
		{
			return true;
		}
	this._validating = true;
	for(e in this._fieldValidators)
		{
			this._inputHandler(this, this._fieldValidators[e], false);
		}
	for(f in this._forms)
		{
			complete = true;
			valid = true;			
			for(e in this._forms[f].shineFields)
				{
					if(this._forms[f].shineFields[e].shineIncomplete)
						{
							complete = false;
						}
					if(!this._forms[f].shineFields[e].shineValidated)
						{
							valid = false;
						}
				}
			s = '';
			if(complete)
				{
					s += ' form-complete';
				}
			else
				{
					s += ' form-incomplete';
				}
			if(valid)
				{
					s += ' form-valid';
				}
			else
				{
					s += ' form-invalid';
				}
			if(complete && valid)
				{
					s += ' form-valid-complete';
				}
			else
				{
					s += ' form-not-valid-complete';
				}
			this.setElementClass(this._forms[f], s, false);
		}
	this._validating = false;
}
com.uk.become.shine.Core.prototype.getElementsByClass = function(root, el, classname)
{
	var c, nl, nlc, nodes, node;
	
	nodes = root.getElementsByTagName(el);
	nl = new Array();
	nlc = 0;
	for(c = 0; c < nodes.length; c++)
		{
			node = nodes[c];
			if(this.hasElementClass(node, classname, true))
				{					
					nl[nlc] = node;
					nlc++;
				}
		}
	return nl;
}
com.uk.become.shine.Core.prototype.addLoadHandler = function(obj)
{
	this._loadHandlers[this._loadHandlers.length] = obj;
}
com.uk.become.shine.Core.prototype.createPopup = function(url, w, h, name)
{
	var win, x, y, settings;
	
	x = (screen.width - w) / 2;
	y = (screen.height - h) / 2;
	settings = 'height=' + h +',';
	settings += 'width=' + w + ',';
	settings += 'top=' + y + ',';
	settings += 'left=' + x + ',';
	settings += 'scrollbars=yes,';
	settings += 'resizable=yes,';
	settings += 'status=no,';
	settings += 'titlebar=no,';
	settings += 'toolbar=no,';
	if(typeof name == undefined || !name)
		{
			name = 'win' + this._popupCount;
		}
	this._popupCount++;
	win = window.open(url, name, settings);
	if(win)
		{
			try
				{
					if(win.focus) win.focus();
				}
			catch(e)
				{
				}
			return false;
		}
	return true;
}
com.uk.become.shine.Core.prototype.popupLinkHandler = function(Shine, sender, ev, data)
{
	if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey)
		{
			return true;
		}
	return Shine.createPopup(sender.shineLinkRef, 400, 500);
}
com.uk.become.shine.Core.prototype.largePopupLinkHandler = function(Shine, sender, ev, data)
{
	if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey)
		{
			return true;
		}	
	return Shine.createPopup(sender.shineLinkRef, 600, 600);
}
com.uk.become.shine.Core.prototype.contentPopupLinkHandler = function(Shine, sender, ev, data)
{
	if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey)
		{
			return true;
		}	
	return Shine.createPopup(sender.shineLinkRef, 800, 600);
}
com.uk.become.shine.Core.prototype.externalLinkHandler = function(Shine, sender, ev, data)
{
	var win;
	if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey)
		{
			return true;
		}	
	win = window.open(sender.shineLinkRef, '_blank');
	if(win && win.window && win.window.focus) win.window.focus();
	return (win ? false : true);
}
com.uk.become.shine.Core.prototype._createHandlers = function()
{
	nodes = this.getElementsByClass(document, 'a', 'shine-popup-link');
	this.addLinkHandlers(nodes, { host: Shine, handler: Shine.popupLinkHandler });
	nodes = this.getElementsByClass(document, 'a', 'shine-largepopup-link');
	this.addLinkHandlers(nodes, { host: Shine, handler: Shine.largePopupLinkHandler });
	nodes = this.getElementsByClass(document, 'a', 'shine-contentpopup-link');
	this.addLinkHandlers(nodes, { host: Shine, handler: Shine.largePopupLinkHandler });
	nodes = this.getElementsByClass(document, 'a', 'shine-external-link');
	this.addLinkHandlers(nodes, { host: Shine, handler: Shine.externalLinkHandler });
	nodes = this.getElementsByClass(document, 'a', 'shine-nonhtml-link');
	this.addLinkHandlers(nodes, { host: Shine, handler: Shine.externalLinkHandler });
	nodes = this.getElementsByClass(document, 'input', 'shine-submit-button');
	this.addSubmitReplacements(nodes);
	/* Set up form field validation */
	Shine._validatorInterval = window.setInterval(function() {  Shine._validateFields(); }, 150);
	this._validateFields();	
}
com.uk.become.shine.Core.prototype.loaded = function(Shine, sender, ev, data)
{
	var nodes;
	
	if(this._loaded)
		{
			return true;
		}
	else if(this._abort)
		{
			return false;
		}
	this._loaded = true;
	this._createHandlers();
	for(h in this._loadHandlers)
		{
			this._loadHandlers[h].ondocumentloaded(Shine, sender, ev, data);
		}
	return true;
}

Shine = new com.uk.become.shine.Core(window, document);

/**
 * com.uk.become.shine.Request
 *
 * Scripted HTTP requests on demand
 */
com.uk.become.shine.Request = function(method, uri)
{
	var r;
	var progids = [
				   'MSXML2.XMLHTTP.5.0',
				   'MSXML2.XMLHTTP.4.0',
				   'MSXML2.XMLHTTP.3.0',
				   'MSXML2.XMLHTTP',
				   'Microsoft.XMLHTTP'
				   ];
	
	this._method = method;
	this._uri = uri;
	this._req = false;
	this._loaded = false;
	this.onload = false;
	this.onerror = false;
	this.method = false;
	this.params = new Array();
	this.formVars = new Object();
	this._headers = new Object();
	try
		{
			r = new XMLHttpRequest();
			this._req = r;
		}
	catch(e)
		{
			for(i = 0; i < progids.length; i++)
				{
					try
						{
							r = new XMLHttpRequest();
							this._req = r;
						}
					catch(e)
						{
						}
				}
		}
	finally
		{
			return this;
		}
}
com.uk.become.shine.Request.prototype.header = function(n, v)
{
	this._headers[n] = v;
}
com.uk.become.shine.Request.prototype._readyStateChangeHandler = function(Shine, sender, ev, data)
{
	if(sender.readyState == 4)
		{
			if(this._loaded)
				{
					return false;
				}
			this._loaded = true;
			ev = new Object();
			ev.status = sender.status;
			ev.statusText = sender.statusText;
			
			if(sender.status == 200)
				{
					if(this.onload)
						{
							ev.responseXML = sender.responseXML;
							ev.responseText = sender.responseText;
							this.onload(ev);
						}
					else
						{
							Shine.debug('Request::_readyStateChangeHandler:', 'Success: status = ' + sender.status + ' (' + sender.statusText + ')');
						}
				}
			else
				{
					if(this.onerror)
						{
							this.onerror(ev);
						}
					else
						{
							Shine.debug('Request::_readyStateChangeHandler:', 'Failed: status = ' + sender.status + ' (' + sender.statusText + ')');
						}
				}
		}
}
com.uk.become.shine.Request.prototype.open = function()
{
	var h, payload, hfv, _this = this;
	
	if(!this._req)
		{
			Shine.debug('Request:', 'Request object has no XMLHttpRequest (' + this._method + ' ' + this._uri + ')');				
			return false;
		}
	hfv = false;
	payload = '';
	if(this.method)
		{
			this._method = 'POST';
			this.header('Content-Type', 'application/vnd.become.rpc+xml');
			payload = '<?xml version="1.0" encoding="UTF-8" ?>' + "\n";
			payload += '<methodCall>' + "\n";
			payload += ' <methodName>' + this.method + '</methodName>' + "\n";
			payload += '  <params>' + "\n";
			for(h = 0; h < this.params.length; h++)
				{
					payload += '   <param><value>';
					if(typeof this.params[h] == 'number')
						{
							payload += '<i4>' + this.params[h] + '</i4>';
						}
					else if(typeof this.params[h] == 'boolean')
						{
							payload += '<boolean>' + (this.params[h] ? '1' : '0') + '</boolean>';
						}
					else
						{
							payload += '<string>' + this.params[h] + '</string>';
						}
					payload += '</value></param>' + "\n";
				}
			payload += '  </params>' + "\n";
			payload += '</methodCall>' + "\n";
		}
	else
		{
			for(h in this.formVars)
				{
					hfv = true;					
					payload += encodeURIComponent(h) + '=' + encodeURIComponent(this.formVars[h]) + '&';					
				}
			if(hfv)
				{
					payload = payload.substr(0, payload.length - 1);
					this.header('Content-Type', 'application/x-www-form-urlencoded');
				}
		}
	Shine.debug('Request:', this._method + ' ' + this._uri);
	this._req.open(this._method, this._uri, true);	
	this._req.onreadystatechange = function() { _this._readyStateChangeHandler(Shine, _this._req, false, false); }
	/* Work around WebKit bug */
	this._req.setRequestHeader('If-Modified-Since', 'Wed, 15 Nov 1995 00:00:00 GMT');
	for(h in this._headers)
		{
			this._req.setRequestHeader(h, this._headers[h]);
		}
	if(payload)
		{
			this._req.send(payload);
		}
	else
		{
			this._req.send(null);
		}
}


/**
 * com.uk.become.shine.Validators
 *
 * Client-side form validation
 */
com.uk.become.shine.Validators = function()
{
}
com.uk.become.shine.Validators.prototype.validPassword = function(str)
{
	if(str.length <= 3)
	{
		return false;
	}
	return true;
}
com.uk.become.shine.Validators.prototype.validEmail = function(str)
{
	var ap;
	
	ap = str.indexOf('@');
	dp = str.lastIndexOf('.');
	if(ap != -1 && dp != -1 && dp > ap && dp < str.length - 2)
		{
			return true;
		}
	return false;	
}
com.uk.become.shine.Validators.prototype.validName = function(str)
{
	if(str.length > 1 && str.match(/^[^\s\d@:"$_#^&*()|]{2,}$/)) /* " */
	{
		return true;
	}
	return false;
}
/* Length-based validator */
com.uk.become.shine.Validators.prototype.lengthValidator = function(Shine, sender, event, data)
{
	if(event.value.length < data)
		{
			return false;
		}
	return true;
}
/* Name validator */
com.uk.become.shine.Validators.prototype.nameValidator = function(Shine, sender, event, data)
{
	return this.validName(sender.value);
}
/* Simple e-mail address validator */
com.uk.become.shine.Validators.prototype.emailInputValidator = function(Shine, sender, event, data)
{
	if(sender.shineValidatorPair)
	{
		Shine._inputHandler(Shine, sender.shineValidatorPair, event);
	}
	return this.validEmail(event.value);
}
com.uk.become.shine.Validators.prototype._emailCheckLoad = function(Shine, sender, event, data)
{
	if(!data || data._shineValidateRequest != sender)
	{
		return false;
	}
	if(event.responseText == 'nomatch')
	{
		data.shineValidatedOK = true;
	}
	else
	{
		data.shineValidatedOK = false;
		Shine.debug('Validators:', 'E-mail address already exists');
	}
	data.shineValidateInProgress = false;
	data.shineValidatedData = data._shineValidateValue;
	event.value = data._shineValidateValue;
	Shine.validateField(data, event);
}
/* Checking e-mail address validator */
com.uk.become.shine.Validators.prototype.emailCheckValidator = function(Shine, sender, event, data)
{
	var req;
	
	if(sender.shineValidatorPair)
	{
		Shine.validateField(sender.shineValidatorPair, event);
	}
	if(!this.validEmail(event.value))
	{
		sender.shineValidateInProgress = false;
		sender._shineValidateRequest = false;
		return false;
	}
	if(sender.shineValidateInProgress && sender._shineValidateValue == event.value) return false;
	if(!sender.shineValidatedInProgress)
	{
		if(sender.shineValidatedData == event.value)
		{
			if(sender.shineValidatedOK)
			{
				return true;
			}
			return false;
		}
	}
	sender._shineValidateValue = event.value;
	sender.shineValidateInProgress = true;
	req = Shine.createRequest('GET', Shine.rootPath + 'com.uk.become.login.component/emailcheck/?e=' + encodeURIComponent(event.value));
	Shine.addEventHandler(req, 'load', { host: this, handler: this._emailCheckLoad, data: sender })
	sender._shineValidateRequest = req;
	req.open();
	return false;
}
/* E-mail address confirmation */
com.uk.become.shine.Validators.prototype.emailConfirmInputValidator = function(Shine, sender, event, data)
{
	if(!this.validEmail(event.value))
	{
		return false;
	}
	if(!sender.shineValidatorPair && sender.form)
	{
		/* Look for a companion password field and pair with it */
		nodes = Shine.getElementsByClass(sender.form, 'input', 'shine-input-emailcheck');
		if(!nodes.length)
		{
			nodes = Shine.getElementsByClass(sender.form, 'input', 'shine-input-email');
		}
		for(c = 0; c < nodes.length; c++)
		{
			node = nodes[c];
			if(node == sender) continue;
			if(!node.shineValidatorPair)
			{
				node.shineValidatorPair = sender;
				sender.shineValidatorPair = node;
				data = node;
				break;
			}
		}
	}
	if(!sender.shineValidatorPair) return false;
	if(event.value == sender.shineValidatorPair.value)
	{
		return true;
	}
	return false;
}
/* Simple password validator */
com.uk.become.shine.Validators.prototype.passwordInputValidator = function(Shine, sender, event, data)
{
	if(sender.shineValidatorPair)
	{
		Shine.validateField(sender.shineValidatorPair, event);
	}
	return this.validPassword(event.value);
}
/* Simple password confirmation validator (simply compares the value with the first password field) */
com.uk.become.shine.Validators.prototype.passwordConfirmInputValidator = function(Shine, sender, event, data)
{
	var c, nodes, node;
	
	if(!this.validPassword(event.value))
	{
		return false;
	}
	if(!sender.shineValidatorPair && sender.form)
	{
		/* Look for a companion password field and pair with it */
		nodes = Shine.getElementsByClass(sender.form, 'input', 'shine-input-password');
		for(c = 0; c < nodes.length; c++)
		{
			node = nodes[c];
			if(node == sender) continue;
			if(!node.shineValidatorPair)
			{
				node.shineValidatorPair = sender;
				sender.shineValidatorPair = node;
				data = node;
				break;
			}
		}
	}
	if(!sender.shineValidatorPair) return false;
	if(event.value == sender.shineValidatorPair.value)
	{
		return true;
	}
	return false;
}
/* Integer input validator */
com.uk.become.shine.Validators.prototype.integerInputValidator = function(Shine, sender, event, data)
{
	if(event.value.length)
		{
			if(event.value.match(/^[0-9]+$/))
				{					
					return true;
				}
		}
	return false;
}
/* Floating-point input validator */
com.uk.become.shine.Validators.prototype.floatInputValidator = function(Shine, sender, event, data)
{
	if(event.value.length)
		{
			if(event.value.match(/^[0-9]*(\.[0-9]+)?$/))
				{
					return true;
				}
		}
	return false;
}
com.uk.become.shine.Validators.prototype.ondocumentloaded = function(Shine, sender, ev, data)
{
	var nodes;
	
	/* Add handlers for the various classes of Shine-enabled form fields */
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-email');
	Shine.addInputHandlers(nodes, { host: this, handler: this.emailInputValidator });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-emailcheck');
	Shine.addInputHandlers(nodes, { host: this, handler: this.emailInputValidator });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-emailconfirm');
	Shine.addInputHandlers(nodes, { host: this, handler: this.emailConfirmInputValidator });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-simple');
	Shine.addInputHandlers(nodes, { host: this, handler: this.lengthValidator, data: 1 });
	nodes = Shine.getElementsByClass(document, 'select', 'shine-input-simple');
	Shine.addInputHandlers(nodes, { host: this, handler: this.lengthValidator, data: 1 });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-md5');
	Shine.addInputHandlers(nodes, { host: this, handler: this.lengthValidator, data: 32 });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-name');
	Shine.addInputHandlers(nodes, { host: this, handler: this.nameValidator });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-password');
	Shine.addInputHandlers(nodes, { host: this, handler: this.passwordInputValidator });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-passconfirm');
	Shine.addInputHandlers(nodes, { host: this, handler: this.passwordConfirmInputValidator });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-integer');
	Shine.addInputHandlers(nodes, { host: this, handler: this.integerInputValidator });
	nodes = Shine.getElementsByClass(document, 'input', 'shine-input-float');
	Shine.addInputHandlers(nodes, { host: this, handler: this.floatInputValidator });	
}
Shine.addLoadHandler(new com.uk.become.shine.Validators());

/**
 * com.uk.become.shine.Expanders
 *
 * Deal with collapsible blocks of content.
 */
com.uk.become.shine.Expanders = function()
{
}
com.uk.become.shine.Expanders.prototype.addDefinitionHooks = function(nodes)
{
	var c;

	for(c = 0; c < nodes.length; c++)
		{
			this.addDefinitionHook(nodes[c]);
		}
}
com.uk.become.shine.Expanders.prototype._onclick = function(Shine, sender, ev, data)
{
	var c;
	
	for(c = 0; c < sender.shineExpanders.length; c++)
	{
		if(sender.shineCollapsed)
		{
			Shine.setElementClass(sender.shineExpanders[c], '', false);
		}
		else
		{
			Shine.setElementClass(sender.shineExpanders[c], 'shine-collapsed', false);
		}
	}
	sender.shineCollapsed = !sender.shineCollapsed;
}
com.uk.become.shine.Expanders.prototype.addDefinitionHook = function(element)
{
	var c, d, dt;
	
	/* Get all of the dt elements */
	for(c = 0; c < element.childNodes.length; c++)
	{
		if(element.childNodes[c].tagName && element.childNodes[c].tagName.toLowerCase() == 'dt')
		{
			element.childNodes[c].shineExpanders = new Array();
			for(d = c + 1; d < element.childNodes.length; d++)
			{
				if(!element.childNodes[d].tagName || element.childNodes[d].tagName.toLowerCase() != 'dd')
				{
					if(element.childNodes[d].tagName && element.childNodes[d].tagName.toLowerCase() == 'dt')
					{
						break;
					}
					continue;
				}
				element.childNodes[c].shineExpanders[element.childNodes[c].shineExpanders.length] = element.childNodes[d];
				Shine.setElementClass(element.childNodes[d], 'shine-collapsed', false);
			}
			element.childNodes[c].shineCollapsed = true;
			Shine.setElementClass(element.childNodes[c], 'shine-expander-node', false);
			Shine.addEventHandler(element.childNodes[c], 'click', { host: this, handler: this._onclick });
		}
	}
}
com.uk.become.shine.Expanders.prototype.ondocumentloaded = function(Shine, sender, ev, data)
{
	var nodes;
	
	nodes = Shine.getElementsByClass(document, 'dl', 'shine-expander');
	this.addDefinitionHooks(nodes);
}
Shine.addLoadHandler(new com.uk.become.shine.Expanders());
