/* --------------------------------------------------------------------
 * Shine: The Become Interactive Client Application Framework
 * @(#) $Id: rpc.js 66525 2008-10-15 10:09:41Z mo $
 * --------------------------------------------------------------------
 * Copyright (c) 2006, 2007 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 BECOME=(typeof BECOME=='undefined'?{}:BECOME);
BECOME.classes=(typeof BECOME.classes=='undefined'?{}:BECOME.classes);

/**
 * BECOME.classes.Request
 *
 * Scripted HTTP requests on demand
 */
BECOME.classes.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._rsInterval = false;
	this.onload = false;
	this.onerror = false;
	this.method = false;
	this.throbber = null;
	this.form = null;
	this.params = new Array();
	this.formVars = new Object();
	this._headers = new Object();
	this.authSecret = null;
	try
	{
		r = new XMLHttpRequest();
		this._req = r;
	}
	catch(e)
	{
		for(i = 0; i < progids.length; i++)
		{
			try
			{
				r = new ActiveXObject(progids[i]);
				this._req = r;
				break;
			}
			catch(e)
			{
			}
		}
	}
	finally
	{
		return this;
	}
}
BECOME.classes.Request.prototype.header = function(n, v)
{
	this._headers[n] = v;
}
BECOME.classes.Request.prototype._throbOn = function()
{
	if(!this.throbber) return;
	if(typeof this.throbber.shineThrobDepth == 'undefined')
	{
		this.throbber.shineThrobDepth = 0;
	}
	this.throbber.shineThrobDepth++;
	if(typeof this.throbber.throbOn != 'undefined')
	{
		this.throbber.throbOn();
	}
	else
	{
		this.throbber.style.display = 'block';
	}
}
BECOME.classes.Request.prototype._throbOff = function()
{
	if(!this.throbber || typeof this.throbber.shineThrobDepth == 'undefined') return;
	if(this.throbber.shineThrobDepth)
	{
		this.throbber.shineThrobDepth--;
	}
	if(!this.throbber.shineThrobDepth)
	{
		if(typeof this.throbber.throbOff != 'undefined')
		{
			this.throbber.throbOff();
		}
		else
		{
			this.throbber.style.display = 'none';
		}
	}
}
BECOME.classes.Request.prototype._readyStateChangeHandler = function(Shine, sender, ev, data)
{
	if(sender.readyState == 4)
	{
		window.clearInterval(this._rsInterval);
		if(this._loaded)
		{
			return false;
		}
		this._loaded = true;
		this._throbOff();
		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;
				if(typeof ev.responseXML != 'undefined' && ev.responseXML != null && typeof ev.responseXML.lastChild != 'undefined' && ev.responseXML.lastChild != null && typeof ev.responseXML.lastChild.tagName != 'undefined')
				{
					ev.responseRoot = ev.responseXML.lastChild;
				}
				else
				{
					ev.responseRoot = null;
				}
				this.onload(ev);
			}
			else
			{
//				console.log('Request::_readyStateChangeHandler:', 'Success: status = ' + sender.status + ' (' + sender.statusText + ')');
			}
		}
		else
		{
			if(this.onerror)
			{
				this.onerror(ev);
			}
			else
			{
//				console.log('Request::_readyStateChangeHandler:', 'Failed: status = ' + sender.status + ' (' + sender.statusText + ')');
			}
		}
	}
}
BECOME.classes.Request.prototype.onRPCLoadProxy = function(Shine, sender, ev, data)
{
	var args, pp, v, p;
	
	if(!ev.responseRoot) return;
	args = null;
	for(p = ev.responseRoot.firstChild; p; p = p.nextSibling)
	{
		if(p.tagName && p.tagName.toLowerCase() == 'params')
		{
			for(pp = p.firstChild; pp; pp = pp.nextSibling)
			{
				if(pp.tagName && pp.tagName.toLowerCase() == 'param')
				{
					for(v = pp.firstChild; v; v = v.nextSibling)
					{
						if(v.tagName && v.tagName.toLowerCase() == 'value')
						{
							args = this.parseValue(v);
							break;
						}
					}
					break;
				}
			}
			break;
		}
	}
	data(args);
}
BECOME.classes.Request.prototype.parseValue = function(vNode)
{
	var v, p;
	
	v = null;
	for(p = vNode.firstChild; p; p = p.nextSibling)
	{
		if(!p.tagName) continue;
//		console.log('parseValue: ', p.tagName);
		if(p.tagName.toLowerCase() == 'array')
		{
			v = this.parseArray(p);
		}
		else if(p.tagName.toLowerCase() == 'struct')
		{
			v = this.parseStruct(p);
		}
		else if(p.tagName.toLowerCase() == 'string')
		{
			v = Shine.getText(p);
		}
		else if(p.tagName.toLowerCase() == 'boolean')
		{
			v = Number(Shine.getText(p));
		}
		else
		{
			console.log('Request:', 'Unknown value type ' + p.tagName.toLowerCase());
		}
	}
	return v;
}
BECOME.classes.Request.prototype.parseArray = function(arrayNode)
{
	var v = [], d, vn;
	
	for(d = arrayNode.firstChild; d; d = d.nextSibling)
	{
		if(d.tagName && d.tagName.toLowerCase() == 'data')
		{
			for(vn = d.firstChild; vn; vn = vn.nextSibling)
			{
				if(vn.tagName.toLowerCase() == 'value')
				{
					v.push(this.parseValue(vn));
				}
			}
			break;
		}
	}
	return v;
}
BECOME.classes.Request.prototype.parseStruct = function(structNode)
{
	var v = {}, d, vn;
	
	for(d = structNode.firstChild; d; d = d.nextSibling)
	{
		name = false;
		if(d.tagName && d.tagName.toLowerCase() == 'member')
		{
			for(vn = d.firstChild; vn; vn = vn.nextSibling)
			{
				if(!vn.tagName) continue
				if(vn.tagName.toLowerCase() == 'name')
				{
					name = Shine.getText(vn);
//					console.log('name = ', name);
				}
				else if(vn.tagName.toLowerCase() == 'value')
				{
					if(name)
					{
//						console.log('adding member:', name);
						v[name] = this.parseValue(vn);
					}
					break;
				}
			}
		}
	}
	return v;
}
BECOME.classes.Request.prototype.encode = function(v)
{
	var b, i;
	
	if(typeof v == 'number')
	{
		return '<i4>' . v + '</i4>';
	}
	if(typeof v == 'boolean')
	{
		return '<boolean>' + (v ? '1' : '0') + '</boolean>';
	}
	if(typeof v == 'object')
	{
		b = ['<struct>'];
		for(i in v)
		{
			b.push('<member><name>' + i + '</name><value>');
			b.push(this.encode(v[i]));
			b.push('</value></member>');
		}
		b.push('</struct>');
		return b.join('');
	}
 	return'<string>' + v + '</string>';
}
BECOME.classes.Request.prototype.call = function(params, onSuccess, onFailure)
{
	var d, k, i;
	
	this.params = params;
	if(this.authSecret)
	{
		d = [this.authSecret];
		if(params.length == 1)
		{
			k = [];
			for(i in params[0])
			{
				k.push(i);
			}
			k.sort();
			for(i in k)
			{
				d.push(k[i]);
				d.push(this.params[0][k[i]]);
			}
			this.params[0].api_sig = hex_md5(d.join(''));
		}
		else
		{
			this.params = [{api_sig: hex_md5(d.join(''))}];
		}
	}
	if(onSuccess) Shine.addEventHandler(this, 'load', { host: this, handler: this.onRPCLoadProxy, data: onSuccess});
//	if(onFailure) Shine.addEventHandler(this, 'error', { host: this, handler: this.onRPCFailProxy, data: onFailure});
	this.open();
}
BECOME.classes.Request.prototype.open = function()
{
	var h, payload, hfv, _this = this, uri, d;
	
	if(!this._req)
		{
			console.log('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>' + this.encode(this.params[h]) + '</value></param>';
			}
		payload += '  </params>' + "\n";
		payload += '</methodCall>' + "\n";
	}
	else
	{
		for(h in this.formVars)
		{
			hfv = true;					
			payload += encodeURIComponent(h) + '=' + encodeURIComponent(this.formVars[h]) + '&';					
		}
		if(this.form)
		{
			hfv = true;
//			console.log('Request:', 'Processing form with ' + this.form.elements.length + ' elements');
			for(i = 0; i < this.form.elements.length; i++)
			{
//				console.log('Request:', 'Processing form element ' + i + ': ' + this.form.elements[i]);
				if(typeof this.form.elements[i].name == 'undefined' ||
					typeof this.form.elements[i].value == 'undefined' ||
					this.form.elements[i].name.length == 0)
				{
					continue;
				}
				if(this.form.elements[i].type == 'radio' || this.form.elements[i].type == 'checkbox')
				{
					if(!this.form.elements[i].checked) continue;
				}
				payload += encodeURIComponent(this.form.elements[i].name) + '=' + encodeURIComponent(this.form.elements[i].value) + '&';
			}
		}
		if(hfv)
		{
			payload = payload.substr(0, payload.length - 1);
			this.header('Content-Type', 'application/x-www-form-urlencoded');
		}
	}
	d = new Date();
	uri = this._uri;
	if(uri.indexOf('?') != -1)
	{
		uri += '&__ts=' + encodeURIComponent(d.getTime());
	}
	else
	{
		uri += '?__ts=' + encodeURIComponent(d.getTime());
	}
//	console.log('Request:', this._method + ' ' + uri);
//	console.log('Request:', payload);
	_this = this;
	_this._req.open(this._method, 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]);
	}
	_this._throbOn();
	if(payload)
	{
		_this._req.send(payload);
	}
	else
	{
		_this._req.send(null);
	}
	this._rsInterval = window.setInterval(function() { _this._readyStateChangeHandler(Shine, _this._req, false, false); }, 50);
}
/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}

/*
 * Calculate the MD5 of an array of little-endian words, and a bit length
 */
function core_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);

}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Calculate the HMAC-MD5, of a key and some data
 */
function core_hmac_md5(key, data)
{
  var bkey = str2binl(key);
  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
  return core_md5(opad.concat(hash), 512 + 128);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
  return bin;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
  return str;
}

/*
 * Convert an array of little-endian words to a hex string.
 */
function binl2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
  }
  return str;
}

/*
 * Convert an array of little-endian words to a base-64 string
 */
function binl2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}
var BECOME=(typeof BECOME=='undefined'?{}:BECOME);
BECOME.NS_XHTML = 'http://www.w3.org/1999/xhtml';
BECOME.DOM = {};
BECOME.DOM.init = 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) && !this.isOpera && !this.isWebTV;
	this.isWebKit = (ua.indexOf('applewebkit') != -1);
	if(this.isWebKit)
	{
		this.isSafari3 = (ua.indexOf('version/3') != -1);
	}
	else
	{
		this.isSafari3 = false;
	}
	this.ieVersion = false;
	if(document.createElementNS)
	{
		this.isDOM = true;
	}
	else
	{
		this.isDOM = false;
	}
	if(this.isIE)
	{
		this.ieVersion = navigator.appVersion.match(/MSIE (\d.\d)/)[1];
	}
	if(typeof document.createHTMLElement=='undefined') document.createHTMLElement=BECOME.DOM.createHTMLElement;
}
/* Elements and fragments */
BECOME.DOM.createHTMLElement = function(tagName, attrs)
{
	var html, i, el;

	if(BECOME.DOM.isDOM)
	{
		el = document.createElementNS(BECOME.NS_XHTML, tagName);
		if(el)
		{
			if(typeof attrs !== 'undefined')
			{
				for(i in attrs)
				{
					this.setAttribute(el, i, attrs[i]);
				}
			}
		}
		return el;
	}
	if(typeof attrs !== 'undefined')
	{
		html = '<' + tagName;
		for(i in attrs)
		{
			if((i == 'checked' || i == 'selected') && attrs[i])
			{
				html += i;
			}
			else
			{
				html += ' ' + i + '="' + attrs[i] + '"'
			}
		}
		html += '>';
		return document.createElement(html);
	}
	return document.createElement(tagName);
}
BECOME.DOM.getElementsByClass = function(root, el, classname)
{
	var c, nl = [], nlc, nodes, node;
	el = el.toLowerCase();
	if(typeof root.getElementsByClassName!='undefined')
	{
		nodes = root.getElementsByClassName(classname);
		for(c = 0; c < nodes.length; c++)
		{
			if(el != '*' && nodes[c].tagName.toLowerCase() != el) continue;
			nl.push(nodes[c]);
		}
	}
	else
	{
		nodes = root.getElementsByTagName(el);
		for(c = 0; c < nodes.length; c++)
		{
			node = nodes[c];
			if(BECOME.DOM.hasElementClass(node, classname, true))
			{
				nl.push(node);
			}
		}
	}
	return nl;
}
BECOME.DOM.insertAfter = function(parent, newChild, refChild)
{ 
	if(refChild.nextSibling) 
	{ 
		return parent.insertBefore(newChild, refChild.nextSibling); 
	} 
	return parent.appendChild(newChild); 
}
BECOME.DOM.appendFirst = function(parent, newChild)
{
	if(parent.firstChild)
	{
		return parent.insertBefore(newChild, parent.firstChild);
	}
	return parent.appendChild(newChild);
}
BECOME.DOM.removeElement = function(element)
{ 
	if(element.parentNode) 
	{ 
		return element.parentNode.removeChild(element); 
	} 

	return false; 
}

/* Attributes and classes */
BECOME.DOM.getAttribute = function(element, name, ns)
{
	var nsprefix, r;
	
	if(typeof element.getAttribute == 'undefined')
	{
		return null;
	}
	if(typeof ns != 'undefined')
	{
		if(this.browser.isDOM && (!this.browser.isWebKit || this.browser.isSafari3))
		{
			if((r = element.getAttributeNS(ns, name)))
			{
				return r;
			}
		}
		nsprefix = this._namespaces[ns];
		return element.getAttribute(nsprefix + ':' + name);
	}
	if(BECOME.DOM.isDOM)
	{
		return element.getAttribute(name);
	}
	if(name == 'class')
	{
		name = 'className';
	}
	return element[name];
}
BECOME.DOM.setAttribute = function(element, name, value, ns)
{
	if(BECOME.DOM.isDOM)
	{
		if(name == 'className') name = 'class';
		if(name == 'htmlFor') name = 'for';
		if(ns)
		{
			element.setAttributeNS(ns, this._namespaces[ns] + ':' + name, value);
		}
		else
		{
			element.setAttribute(name.toLowerCase(), value);
		}
	}
	else
	{
		if(ns) name = this._namespaces[ns] + ':' + name;
		if(name == 'class')
		{
			name = 'className';
		}
		else if(name == 'for')
		{
			name = 'htmlFor';
		}
		else if(name == 'style' && this.browser.isIE)
		{
			element.style.cssText = value;
			return;
		}
		else if(name == 'checked' || name == 'selected')
		{
			if(value)
			{
				value = true;
			}
			else
			{
				value = false;
			}
		}
		element[name] = value;
	}
}
BECOME.DOM.getElementClass = function(element, forceDirect)
{
	var cn;

	if(!element) return null;
	if(element.shineStyleTarget && !forceDirect)
		{
			element = element.shineStyleTarget;
		}
	cn = BECOME.DOM.getAttribute(element, 'class');
	if(typeof cn != 'string')
		{
			return '';
		}
	return cn;
}
BECOME.DOM.beginUpdate = function(element)
{
	element = BECOME.DOM._initStyles(element);
	element._shineUpdating = true;
	return true;
}
BECOME.DOM.endUpdate = function(element)
{
	element = BECOME.DOM._initStyles(element);
	element._shineUpdating = false;
	return BECOME.DOM._updateElementClass(element);
}
BECOME.DOM.resetElementClass = function(element)
{
	element = BECOME.DOM._initStyles(element);
	element.shineClasses = new Array();
	if(element._shineUpdating)
		{
			return true;
		}
	return BECOME.DOM._updateElementClass(element);
}
BECOME.DOM.addElementClass = function(element, classname)
{
	var c;
		
	element = BECOME.DOM._initStyles(element);
	for(c in element.shineClasses)
	{
		if(element.shineClasses[c] == classname)
		{
			return false;
		}
	}
	element.shineClasses[element.shineClasses.length] = classname;
	if(element._shineUpdating)
		{
			return true;
		}
	return BECOME.DOM._updateElementClass(element);
}
BECOME.DOM.removeElementClass = function(element, classname)
{
	var c;
	
	element = BECOME.DOM._initStyles(element);
	for(c in element.shineClasses)
	{
		if(element.shineClasses[c] == classname)
		{
			element.shineClasses.splice(c, 1);
		}
	}
	if(element._shineUpdating)
		{
			return true;
		}	
	return BECOME.DOM._updateElementClass(element);
}
BECOME.DOM.hasElementClass = function(element, classname, forceDirect)
{
	var cn;

	if(!forceDirect)
		{
			forceDirect = false;
		}
	cn = ' ' + BECOME.DOM.getElementClass(element, forceDirect) + ' ';
	if(cn.indexOf(' ' + classname + ' ') != -1)
		{
			return true;
		}
	return false;
}

BECOME.DOM._initStyles = function(element)
{
	if(element.shineStyleTarget)
	{
		element = element.shineStyleTarget;
	}
	if(typeof element.shineOriginalClass != 'string')
	{
		element.shineOriginalClass = BECOME.DOM.getElementClass(element);
	}
	if(typeof element.shineClasses == 'undefined')
	{
		element.shineClasses = new Array();
	}
	return element;
}
BECOME.DOM._updateElementClass = function(element)
{
	var cn, cn2;
	
	cn = BECOME.DOM.getAttribute(element, 'class');
	cn2 = element.shineOriginalClass + ' ' + element.shineClasses.join(' ');
	if(cn != cn2)
		{
			BECOME.DOM.setAttribute(element,  'class', cn2);
		}
	return true;
}

/* Events */
BECOME.DOM.addEventHandler = function(element, eventName, handler)
{
	var force = false;

	if(typeof element == 'string') element = document.getElementById(element);
	if(!element) return false;
	if(!handler || !handler.handler)
	{
		console.log('Shine:', 'Invalid handler passed for event on' + eventName);
		return false;
	}
	if(!element.shineEventHandlers) element.shineEventHandlers = [];
	if(!element.shineEventHandlers[eventName])
	{
		element.shineEventHandlers[eventName] = new Array();
		if(this.browser.isWebKit && eventName == 'click')
		{
			force = true;
		}
		if(BECOME.DOM.isDOM && element.addEventListener && !force)
		{
			element.addEventListener(eventName, 
			function(ev) { 
				return BECOME.DOM._eventHost(eventName, element, ev);
			}, false);
		}
		else if(element.attachEvent && element.attachEvent && !force)
		{
			element.attachEvent('on' + eventName, function(ev) { return BECOME.DOM._eventHost(eventName, element, (ev ? ev : window.event)); });
		}
		else
		{
			element['on' + eventName] = function(ev) { return BECOME.DOM._eventHost(eventName, element, (ev ? ev : window.event)); };
		}
	}
	element.shineEventHandlers[eventName].push(handler);
}
BECOME.DOM.dispatchEvent = function(element, eventType, event)
{
	return BECOME.DOM._eventHost(eventType, element, event);
}
BECOME.DOM.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);
}
BECOME.DOM._eventHost = function(evtype, sender, ev)
{
	var handlers, h;
	
	if(typeof sender.shineEventHandlers == 'undefined' || typeof sender.shineEventHandlers[evtype] == 'undefined') return true;
	if(typeof ev.keyCode != 'number')
	{
		if(typeof ev.which == 'number')
		{
			ev.keyCode = ev.which;
		}
		else if(typeof ev.charCode == 'number')
		{
			ev.keyCode = ev.charCode;
		}
	}
	ev.stopped = false;
	for(c in sender.shineEventHandlers[evtype])
	{
		h = sender.shineEventHandlers[evtype][c];
		h.host['shine_' + evtype] = h.handler;
		if(!h.host['shine_' + evtype](Shine, sender, ev, h.data))
		{
			if(ev.stopPropagation)
			{
				ev.stopPropagation();
			}
			if(ev.preventDefault)
			{
				ev.preventDefault();
			}
			ev.cancelBubble = true;
			return false;
		}
	}
	if(ev.stopped)
	{
		if(ev.stopPropagation)
		{
			ev.stopPropagation();
		}
		if(ev.preventDefault)
		{
			ev.preventDefault();
		}
		ev.cancelBubble = true;
		return false;
	}
	return true;
}

BECOME.DOM.init();
var BECOME=(typeof BECOME=='undefined'?{}:BECOME);
BECOME.classes=(typeof BECOME.classes=='undefined'?{}:BECOME.classes);
BECOME.classes.Table = function()
{
}
BECOME.classes.Table.prototype.td = function(content, attribs)
{
	var e = Shine.createHTMLElement('td', attribs);
	if(content) e.appendChild(content);
	return e;
}
BECOME.classes.Table.prototype.th = function(content, attribs)
{
	var e = Shine.createHTMLElement('th', attribs);
	if(content) e.appendChild(content);
	return e;
}
BECOME.classes.Table.prototype.tr = function(content, attribs)
{
	var i, e = Shine.createHTMLElement('tr', attribs);
	for(i in content)
	{
		e.appendChild(content[i]);
	}
	return e;
}
BECOME.classes.Table.prototype.table = function(head, foot, body, attribs)
{
	var i, e = Shine.createHTMLElement('table', attribs);
	if(head)
	{
		var h = Shine.createHTMLElement('thead');
		for(i=0;i<head.length;i++) e.appendChild(head[i]);
		e.appendChild(h);
	}
	if(foot)
	{
		var h = Shine.createHTMLElement('tfoot');
		for(i=0;i<foot.length;i++) e.appendChild(foot[i]);
		e.appendChild(h);
	}
	if(body)
	{
		var h = Shine.createHTMLElement('tbody');
		for(i=0;i<body.length;i++) e.appendChild(body[i]);
		e.appendChild(h);
	}
	return e;
}
/* --------------------------------------------------------------------
 * Shine: The Become Interactive Client Application Framework
 * @(#) $Id: forms.js 66525 2008-10-15 10:09:41Z mo $
 * --------------------------------------------------------------------
 * Copyright (c) 2006, 2007 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.
 * --------------------------------------------------------------------
 */

/**
 * BECOME.classes.Forms
 *
 * Client-side form validation
 */
if(typeof BECOME == 'undefined') BECOME = new Object();
if(typeof BECOME.classes == 'undefined') BECOME.classes = new Object();

BECOME.classes.Forms = function()
{
}
BECOME.classes.Forms.prototype.validPassword = function(str)
{
	if(str.length <= 3)
	{
		return false;
	}
	return true;
}
BECOME.classes.Forms.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;	
}
BECOME.classes.Forms.prototype.validName = function(str)
{
	if(str.length > 1 && str.match(/^[^\s\d@:"$_#^&*()|]{2,}$/)) /* " */
	{
		return true;
	}
	return false;
}
/* Length-based validator */
BECOME.classes.Forms.prototype.lengthValidator = function(Shine, sender, event, data)
{
	if(event.value.length < data)
		{
			return false;
		}
	return true;
}
/* Name validator */
BECOME.classes.Forms.prototype.nameValidator = function(Shine, sender, event, data)
{
	return this.validName(sender.value);
}
/* Simple e-mail address validator */
BECOME.classes.Forms.prototype.emailInputValidator = function(Shine, sender, event, data)
{
	if(sender.shineValidatorPair)
	{
		Shine._inputHandler(Shine, sender.shineValidatorPair, event);
	}
	return this.validEmail(event.value);
}
BECOME.classes.Forms.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 */
BECOME.classes.Forms.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 + '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 */
BECOME.classes.Forms.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 */
BECOME.classes.Forms.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) */
BECOME.classes.Forms.prototype.passwordConfirmInputValidator = function(Shine, sender, event, data)
{
	var c, targ, node;
	
	if(!this.validPassword(event.value))
	{
		return false;
	}
	if(!sender.shineValidatorPair && sender.form)
	{
		/* Look for a companion password field and pair with it */
		if((targ = Shine.getAttribute(sender, 'target', 'http://www.becomeinteractive.co.uk/ns/shine#')))
		{
			if((node = document.getElementById(targ)) && (node != sender) && !(node.shineValidatorPair))
			{
				node.shineValidatorPair = sender;
				sender.shineValidatorPair = node;
				data = node;
			}
		}
	}
	if(!sender.shineValidatorPair) return false;
	if(event.value == sender.shineValidatorPair.value)
	{
		return true;
	}
	return false;
}
/* Integer input validator */
BECOME.classes.Forms.prototype.integerInputValidator = function(Shine, sender, event, data)
{
	if(event.value.length)
		{
			if(event.value.match(/^[0-9]+$/))
				{					
					return true;
				}
		}
	return false;
}
/* ISO7812 (credit card, smart card, ID card) input validator */
BECOME.classes.Forms.prototype.iso7812InputValidator = function(Shine, sender, event, data)
{
	var ccnum, sum, i, digit;
	
	if(event.value.length)
		{
			/* Strip spaces from the number */
			ccnum = event.value.replace(/\s/g, '');
			if(ccnum.length < 9 || !ccnum.match(/^[0-9]+$/))
			{
				return false;
			}
			/* Calculate the Luhn check digit */
			sum = 0;
			parity = ccnum.length % 2;
			for(i = 0; i < ccnum.length; i++) 
			{
			    digit = ccnum.charAt(i);
			    if(i % 2 == parity)
			 	{
					digit *= 2;
				}
				if(digit > 9)
				{
					digit -= 9;
				}
			    sum += parseInt(digit);
			  }
			  return (sum % 10 == 0);
		}
	return false;
}
BECOME.classes.Forms.prototype.cv2InputValidator = function(Shine, sender, event, data)
{
	var ccnum, sum, i, digit;
	
	if(event.value.length == 3)
	{
		return event.value.match(/^[0-9]+$/) ? true : false;
	}
	return false;
}
BECOME.classes.Forms.prototype.issueInputValidator = function(Shine, sender, event, data)
{
	var ccnum, sum, i, digit;
	
	if(event.value.length == 2)
	{
		return event.value.match(/^[0-9]+$/) ? true : false;
	}
	return false;
}
/* Floating-point input validator */
BECOME.classes.Forms.prototype.floatInputValidator = function(Shine, sender, event, data)
{
	if(event.value.length)
		{
			if(event.value.match(/^[0-9]*(\.[0-9]+)?$/))
				{
					return true;
				}
		}
	return false;
}
BECOME.classes.Forms.prototype.attach = function(root)
{
	var nodes, type, c;
	
	nodes = root.getElementsByTagName('input');
	for(c = 0; c < nodes.length; c++)
	{
		type = Shine.getAttribute(nodes[c], 'field', 'http://www.becomeinteractive.co.uk/ns/shine#');
		if(!type)
		{
			continue;
		}
		if(type == 'email' || type == 'emailcheck')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.emailInputValidator });
		}
		else if(type == 'emailconfirm')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.emailConfirmInputValidator });
		}
		else if(type == 'simple')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.lengthValidator, data: 1 });
		}
		else if(type == 'md5')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.lengthValidator, data: 32 });
		}
		else if(type == 'name')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.nameValidator });
		}
		else if(type == 'password')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.passwordInputValidator });
		}
		else if(type == 'passconfirm')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.passwordConfirmInputValidator });
		}
		else if(type == 'integer')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.integerInputValidator });
		}
		else if(type == 'float')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.floatInputValidator });
		}
		else if(type == 'iso7812')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.iso7812InputValidator });
		}
		else if(type == 'cv2')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.cv2InputValidator });
		}
		else if(type == 'issue')
		{
			Shine.addInputHandler(nodes[c], { host: this, handler: this.issueInputValidator });
		}
	}
	nodes = root.getElementsByTagName('select');
	for(c = 0; c < nodes.length; c++)
	{
		type = Shine.getAttribute(nodes[c], 'field', 'http://www.becomeinteractive.co.uk/ns/shine#');
		if(!type)
		{
			continue;
		}
		Shine.addInputHandler(nodes[c], { host: this, handler: this.lengthValidator, data: 1});
	}
	nodes = root.getElementsByTagName('textarea');
	for(c = 0; c < nodes.length; c++)
	{
		type = Shine.getAttribute(nodes[c], 'field', 'http://www.becomeinteractive.co.uk/ns/shine#');
		if(!type)
		{
			continue;
		}
		Shine.addInputHandler(nodes[c], { host: this, handler: this.lengthValidator, data: 1});
	}
	nodes = root.getElementsByTagName('div');
	for(c = 0; c < nodes.length; c++)
	{
		if(Shine.getAttribute(nodes[c], 'selftitled', 'http://www.becomeinteractive.co.uk/ns/shine#'))
		{
			this.hookSelfTitled(nodes[c]);
		}
	}
	Shine.validateFields();
}
BECOME.classes.Forms.prototype.hookSelfTitled = function(node)
{
	var label, inp, l;
	
	label = node.getElementsByTagName('label');
	inp = node.getElementsByTagName('input');
	if(!label || !label.length) return;
	if(!inp || !inp.length) return;
	if(label[0].innerText)
	{
		l = label[0].innerText;
	}
	else if(label[0].textContent)
	{
		l = label[0].textContent;
	}
	else
	{
		return;
	}
	l = l.replace(/:$/, '');
	label[0].style.display = 'none';
	inp[0].shineTitleText = l;
}
if(typeof document.loadHooks == 'undefined') document.loadHooks = [];
BECOME.forms = new BECOME.classes.Forms();
document.loadHooks.push(function() { BECOME.forms.attach(document); });
/* --------------------------------------------------------------------
 * Shine: The Become Interactive Client Application Framework
 * @(#) $Id: expanders.js 66525 2008-10-15 10:09:41Z mo $
 * --------------------------------------------------------------------
 * Copyright (c) 2006, 2007 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.
 * --------------------------------------------------------------------
 */

/**
 * BECOME.classes.Expanders
 *
 * Deal with collapsible blocks of content.
 */
if(typeof BECOME == 'undefined') BECOME = {};
if(typeof BECOME.classes == 'undefined') BECOME.classes = {};
BECOME.classes.Expanders = function()
{
}
BECOME.classes.Expanders.prototype.addDefinitionHooks = function(nodes)
{
	var c;

	for(c = 0; c < nodes.length; c++)
		{
			this.addDefinitionHook(nodes[c]);
		}
}
BECOME.classes.Expanders.prototype._onclick = function(Shine, sender, ev, data)
{
	var c;
	
	for(c = 0; c < sender.shineExpanders.length; c++)
	{
		if(sender.shineCollapsed)
		{
			Shine.removeElementClass(sender.shineExpanders[c], 'shine-collapsed');
		}
		else
		{
			Shine.addElementClass(sender.shineExpanders[c], 'shine-collapsed');
		}
	}
	sender.shineCollapsed = !sender.shineCollapsed;
}
BECOME.classes.Expanders.prototype._ontreeclick = function(Shine, sender, ev, data)
{
	if(sender.shineExpansionTarget)
	{
		sender = sender.shineExpansionTarget;
	}
	if(sender.shineCollapsed)
	{
		Shine.removeElementClass(sender, 'shine-collapsed');
		Shine.addElementClass(sender, 'shine-expanded');
	}
	else
	{
		Shine.addElementClass(sender, 'shine-collapsed');
		Shine.removeElementClass(sender, 'shine-expanded');
	}
	sender.shineCollapsed = !sender.shineCollapsed;
	return false;
}
BECOME.classes.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.addElementClass(element.childNodes[d], 'shine-collapsed');
			}
			element.childNodes[c].shineCollapsed = true;
			Shine.addElementClass(element.childNodes[c], 'shine-expander-node');
			Shine.addEventHandler(element.childNodes[c], 'click', { host: this, handler: this._onclick });
		}
	}
}
BECOME.classes.Expanders.prototype.addTreeHook = function(element)
{
	var c, node, ind, i;
	
	for(c = 0; c < element.childNodes.length; c++)
	{
		if(element.childNodes[c].tagName && element.childNodes[c].tagName.toLowerCase() == 'li')
		{
			node = element.childNodes[c];
			if(!Shine.hasElementClass(node, 'branch'))
			{
				continue;
			}
			if(Shine.getAttribute(node, 'ind', 'http://www.becomeinteractive.co.uk/ns/shine#'))
			{
				ind = Shine.createHTMLElement('span');
				ind.shineExpansionTarget = node;
				ind.appendChild(document.createTextNode("\u00A0\u00A0")); 
				Shine.addElementClass(ind, 'shine-indicator');
				Shine.appendFirst(node, ind);
				Shine.addElementClass(ind, 'shine-expander-node');
				Shine.addEventHandler(ind, 'click', { host: this, handler: this._ontreeclick });
			}
			else
			{
				x = node.getElementsByTagName('span');
				for(i = 0; i < x.length; i++)
				{
					if(x[i].parentNode == node)
					{
						x[i].shineExpansionTarget = node;
						Shine.addEventHandler(x[i], 'click', { host: this, handler: this._ontreeclick });
						break;
					}
				}
//				Shine.addEventHandler(node, 'click', { host: this, handler: this._ontreeclick });
			}
			if(!Shine.hasElementClass(node, 'shine-expanded'))
			{
				Shine.addElementClass(node, 'shine-collapsed');
				node.shineCollapsed = true;
			}
			else
			{
				node.shileCollapsed = false;
			}
		}
	}
}
BECOME.classes.Expanders.prototype.addTreeHooks = function(nodes)
{
	var c;

	for(c = 0; c < nodes.length; c++)
		{
			this.addTreeHook(nodes[c]);
		}
}
BECOME.classes.Expanders.prototype.attach = function(root)
{
	var nodes;
	
	nodes = Shine.getElementsByClass(root, 'dl', 'shine-expander');
	this.addDefinitionHooks(nodes);
	nodes = Shine.getElementsByClass(root, 'ul', 'shine-expand-tree');
	this.addTreeHooks(nodes);
}
if(typeof document.loadHooks == 'undefined') document.loadHooks = [];
BECOME.expanders = new BECOME.classes.Expanders();
document.loadHooks.push(function() { BECOME.expanders.attach(document) });
/* --------------------------------------------------------------------
 * Shine: The Become Interactive Client Application Framework
 * @(#) $Id: tabs.js 66525 2008-10-15 10:09:41Z mo $
 * --------------------------------------------------------------------
 * Copyright (c) 2006, 2007 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 BECOME=(typeof BECOME=='undefined'?{}:BECOME);
BECOME.classes=(typeof BECOME.classes=='undefined'?{}:BECOME.classes);

BECOME.classes.Tabs = function()
{	
	this.ontabchanged = false;
	this.useTabAnchor = true;
}
BECOME.classes.Tabs.prototype._activatePage = function(page)
{
	var ev;

	if (!page.shineTabGroup.shineTabHide)
	{
		while(page.shineTabGroup.shinePageContainer.firstChild)
		{
			page.shineTabGroup.shinePageContainer.removeChild(page.shineTabGroup.shinePageContainer.firstChild);
		}		
		page.shineTabGroup.shinePageContainer.appendChild(page);
	}

	if(page.shineTab)
	{
		Shine.addElementClass(page.shineTab, 'shine-active-tab');
	}
	page.shineTabGroup.shineActivePage = page;
	Shine.addElementClass(page, 'shine-active-sheet');
	ev = new Object();
	ev.tabGroup = page.shineTabGroup;
	ev.tabSheet = page;
	if(this.ontabchanged)
	{
		this.ontabchanged(ev);
	}
}
BECOME.classes.Tabs.prototype._deactivatePage = function(group)
{
	if(group.shineActivePage)
	{
		Shine.removeElementClass(group.shineActivePage, 'shine-active-sheet');
		if(group.shineActivePage.shineTab)
		{
			Shine.removeElementClass(group.shineActivePage.shineTab, 'shine-active-tab')
		}
		if (!group.shineTabHide)
		{
			group.shinePageContainer.removeChild(group.shineActivePage);
		}
		group.shineActivePage = false;
	}
}
BECOME.classes.Tabs.prototype.switchPage = function(page)
{
	var _this = this, oldId, h, i, nh = [], p = false;
	
	if(page == page.shineTabGroup.shineActivePage)
	{
		return false;
	}
	if(page.shineTabGroup.shineActivePage)
	{
		if(page.shineTabGroup.shineActivePage.id)
		{
			oldId = page.shineTabGroup.shineActivePage.id;
		}
		_this._deactivatePage(page.shineTabGroup);
	}
	h = document.location.hash.replace(/^#/, '').split(',');
	for(i = 0; i < h.length; i++)
	{
		if(!h[i]) continue;
		if(h[i] != oldId)
		{
			nh.push(h[i]);
		}
		else if(page.id)
		{
			nh.push(page.id);
			p = true;
		}
	}
	if(!p && page.id)
	{
		nh.push(page.id);
	}
	_this._activatePage(page);
	if(_this.useTabAnchor)
	{
		document.location.href = '#' + nh.join(',');
		if(page.shineTabGroup && page.shineTabGroup.scrollIntoView)
		{
			page.shineTabGroup.scrollIntoView(true);
			return false;
		}
	}
	return false;
}
BECOME.classes.Tabs.prototype.ontabfocus = function(Shine, sender, event, data)
{
	if(sender.shineSheet && sender.shineTabGroup)
	{
		this.switchPage(sender.shineSheet);
	}
	return true;
}
BECOME.classes.Tabs.prototype.ontabclick = function(Shine, sender, event, data)
{
	if(sender.shineSheet && sender.shineTabGroup)
	{
		this.switchPage(sender.shineSheet);
	}
	return false;
}
BECOME.classes.Tabs.prototype.addFocusTab = function(group, element, event)
{
	var sheet;
	
	sheet = document.getElementById(element.id + '-sheet');
	if(!sheet)
	{
		Shine.debug('Tabs:', 'Can\'t locate sheet for tab ' + element.id);
		return false;
	}
	element.shineSheet = sheet;
	element.shineTabGroup = group;
	sheet.shineTab = element;
	Shine.addEventHandler(element, event, { host: this, handler: this.ontabfocus });
	return true;
}
BECOME.classes.Tabs.prototype.addClickTab = function(group, element, event)
{
	var sheet, href;
	
	href = String(Shine.getAttribute(element, 'href'));
	href = href.replace(/^.*(#[^#]*)$/, '$1');
	if(href.substr(0, 1) == '#' && href.length > 1)
	{
		href = href.substr(1);
		sheet = document.getElementById(href);
	}
	else
	{
		sheet = document.getElementById(element.id + '-sheet');
	}
	if(!sheet)
	{
		Shine.debug('Tabs:', 'Can\'t locate sheet for tab ' + element.id);
		return false;
	}
	element.shineSheet = sheet;
	element.shineTabGroup = group;
	sheet.shineTab = element;
	sheet.shineTabGroup = group;
	Shine.addElementClass(sheet, 'shine-tab-sheet');
	Shine.setAttribute(element, 'href', '#');
	Shine.addEventHandler(element, event, { host: this, handler: this.ontabclick });
	return true;
}
BECOME.classes.Tabs.prototype.initTabGroup = function(element, hideMode)
{
	var containers, tabs, first, c, h, i;
	
	if(element.getAttribute('data-notabanchor'))
	{
		this.useTabAnchor = false;
	}
	element.shineTabHide = hideMode;
	element.shineTabSheets = Shine.getElementsByClass(element, 'div', 'shine-tab-sheet');
	if(element.shineTabSheets.length == 0)
	{
		return false;
	}
	element.shineActivePage = false;
	containers = Shine.getElementsByClass(element, 'div', 'shine-tab-pages');
	if(containers.length == 0)
	{
		containers = Shine.getElementsByClass(element, 'span', 'shine-tab-pages');
	}
	if(containers.length == 0)
	{
		return false;
	}
	element.shinePageContainer = containers[0];
	first = false;
	tabs = Shine.getElementsByClass(element, 'input', 'shine-tab');
	/* You have to loop the tabs first -- before all of the sheets are removed
	 * from the DOM.
     */
	for(c in tabs)
	{
		this.addFocusTab(element, tabs[c], 'focus');
	}
	tabs = Shine.getElementsByClass(element, 'a', 'shine-tab');
	for(c in tabs)
	{
		this.addClickTab(element, tabs[c], 'click');
	}
	tabs = Shine.getElementsByClass(element, 'span', 'shine-tab');
	for(c in tabs)
	{
		this.addClickTab(element, tabs[c], 'click');
	}
	h = document.location.hash.replace(/^#/, '').split(',');
	for(c in element.shineTabSheets)
	{
		if(!first)
		{
			first = element.shineTabSheets[c];
		}
		if(!element.shineActiveTab && element.shineTabSheets[c].id)
		{
			for(i = 0; i < h.length; i++)
			{
				if(h[i] == element.shineTabSheets[c].id)
				{
					element.shineActivePage = element.shineTabSheets[c];
					break;
				}
			}
		}
		element.shineTabSheets[c].shineTabGroup = element;
		if (!element.shineTabHide)
		{
			element.shineTabSheets[c].parentNode.removeChild(element.shineTabSheets[c]);
		}
	}
	if(!element.shineActivePage)
	{
		for(c in element.shineTabSheets)
		{
			if(Shine.hasElementClass(element.shineTabSheets[c], 'active'))
			{
				element.shineActivePage = element.shineTabSheets[c];
				break;
			}
		}
	}
	if(!element.shineActivePage)
	{
		element.shineActivePage = first;
	}
	this._activatePage(element.shineActivePage);
	Shine.addElementClass(element, 'shine-tabs-enabled');
}
BECOME.classes.Tabs.prototype.attach = function(root)
{
	var tabgroups, c;

	tabgroups = Shine.getElementsByClass(root, 'div', 'shine-tab-group');
	for(c in tabgroups)
	{	
		this.initTabGroup(tabgroups[c], false);
	}
	tabgroups = Shine.getElementsByClass(root, 'div', 'shine-tab-group-hide');
	for(c in tabgroups)
	{	
		this.initTabGroup(tabgroups[c], true);
	}
	return true;
}
if(typeof document.loadHooks == 'undefined') document.loadHooks = [];
BECOME.Tabs = new BECOME.classes.Tabs();
document.loadHooks.push(function() {BECOME.Tabs.attach(document);});
/* --------------------------------------------------------------------
 * Shine: The Become Interactive Client Application Framework
 * @(#) $Id: shine.js 66919 2008-10-27 10:49:31Z mo $
 * --------------------------------------------------------------------
 * Copyright (c) 2006, 2007 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 BECOME == 'undefined') BECOME = {};
if(typeof BECOME.classes == 'undefined') BECOME.classes = {};

BECOME.NS_SHINE = 'http://www.becomeinteractive.co.uk/ns/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.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 66919 2008-10-27 10:49:31Z mo $';
	this.basePath = '/';
	this.rootPath = '/';
	this.tplPath = '/';
	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._formDirty = 0;
	this._requiredModules = new Object();
	this._requiredModules['com.uk.become.shine.core'] = true;
	this._namespaces = new Object();
	this._currentPage = null;
	this._helpPanel = null;
	this._helpPanelElement = null;
	this._helpPanelError = null;
	this._helpPanelInfo = null;
	this._pageChanging = false;
	this._errorFieldSection = null;
	this.body = false;	
	this.emptyRequiredBad = false;
	this.browser = BECOME.DOM;
	if(this.browser.isIE && (this.browser.isVersion < 5 || this.browser.isVersion >= 7))
	{
		this._abort = true;
		return false;
	}
	if(typeof window.console == 'undefined') window.console = {};
	if(typeof window.console.log == 'undefined') window.console.log = function() {};
	if(typeof window._shineDebugMode != 'undefined' && window._shineDebugMode)
	{
		this._debug = true;
	}
	else
	{
		this._debug = false;
	}
	this.debug = console.log;

	this.createHTMLElement = BECOME.DOM.createHTMLElement;
	this.getElementsByClass = BECOME.DOM.getElementsByClass;
	this.insertAfter = BECOME.DOM.insertAfter;
	this.appendFirst = BECOME.DOM.appendFirst;
	this.removeElement = BECOME.DOM.removeElement;
	this.getAttribute = BECOME.DOM.getAttribute;
	this.setAttribute = BECOME.DOM.setAttribute;
	
	this.addEventHandler = BECOME.DOM.addEventHandler;
	this.dispatchEvent = BECOME.DOM.dispatchEvent;
	this.callEventHandler = BECOME.DOM.callEventHandler;

	this.getElementClass = BECOME.DOM.getElementClass;
	this.beginUpdate = BECOME.DOM.beginUpdate;
	this.endUpdate = BECOME.DOM.endUpdate;
	this.resetElementClass = BECOME.DOM.resetElementClass;
	this.addElementClass = BECOME.DOM.addElementClass;
	this.removeElementClass = BECOME.DOM.removeElementClass;
	this.hasElementClass = BECOME.DOM.hasElementClass;
	
	if(this._debug)
	{
		console.log('Shine:', '$Id: shine.js 66919 2008-10-27 10:49:31Z mo $');
	}
	return this;
}
com.uk.become.shine.Core.prototype.createRequest = function(method, uri)
{
	return new BECOME.classes.Request(method, uri);
}
com.uk.become.shine.Core.prototype.createRPC = function(endpoint, method)
{
	var args, c, d;

	args = new Array();
	d = 0;
	for(c = 2; c < arguments.length; c++)
	{
		args[d] = arguments[c];
		d++;
	}
	m = new BECOME.classes.Request('POST', this.rootPath + 'api/rpc/' + endpoint);
	m.method = method;
	m.params = args;
	return m;
}
com.uk.become.shine.Core.prototype.createTable = function()
{
	return new BECOME.classes.Table();
}
com.uk.become.shine.Core.prototype._loadScript = function(path)
{
	var sel, heads;
	
	//if(this.browser.isDOM)
	//{
		heads = document.getElementsByTagName('head');
		if(heads && heads.length)
		{
			heads = heads[0];
		}
		else
		{
			return false;
		}
		sel = this.createHTMLElement('script');
		this.setAttribute(sel, 'type', 'text/javascript');
		this.setAttribute(sel, 'src', path);
		heads.appendChild(sel);
		this.debug('Shine:', 'Loading script from ' + path);
	//}
	//else
	//{
	//	document.write('<' + 'script type="text/javascript" src="' + path + '"></script>');
	//}
}
com.uk.become.shine.Core.prototype.importScript = function(path, objectName, handler)
{
	var i, _this = this;
	
	if(objectName && handler)
	{
		if(typeof window[objectName] != 'undefined')
		{
			handler.handler(this, window, {}, handler.data);
			return;
		}
		i = window.setInterval(function()
		{
			if(typeof window[objectName] != 'undefined')
			{
				console.log('Script is ready');
				window.clearInterval(i);
				handler.handler(this, window, {}, handler.data);
				return;
			}
		}, 150);
	}
	this._loadScript(path);
}
com.uk.become.shine.Core.prototype.importStylesheet = function(path, media)
{
	var sel, heads;
	
	this.debug('Shine:', 'Loading stylesheet from ' + path);
	//if(this.browser.isDOM)
	//{
		heads = document.getElementsByTagName('head');
		if(heads && heads.length)
		{
			heads = heads[0];
		}
		else
		{
			return false;
		}
		sel = this.createHTMLElement('link');
		this.setAttribute(sel, 'type', 'text/css');
		this.setAttribute(sel, 'media', media);
		this.setAttribute(sel, 'href', path);
		this.setAttribute(sel, 'rel', 'stylesheet');
		heads.appendChild(sel);
	//}
	//else
	//{
		//document.write('<' + 'link rel="stylesheet" type="text/css" href="' + path + '" media="screen,projection" />');
	//}
}
com.uk.become.shine.Core.prototype.importFragment = function(oNode, loadScripts)
{
	var scripts, i, p, head, node, wait, _this = this;
	
	node = this.importNode(oNode, true);
	head = document.getElementsByTagName('head');
	if(head && head.length)
	{
		head = head[0];
	}
	else
	{
		head = null;
	}
	nscripts = new Array();
	oscripts = new Array();
	if(loadScripts)
	{
		scripts = oNode.getElementsByTagName('script');
		if(!scripts.length)
		{
			scripts = oNode.getElementsByTagName('SCRIPT');
		}
		for(i = 0; i < scripts.length; i++)
		{
			p = new Object();
			if((wait = this.getAttribute(scripts[i], 'waitUntil', BECOME.NS_SHINE)))
			{
				p.waitUntil = wait;
			}
			if((src = this.getAttribute(scripts[i], 'src')))
			{
				p.scriptSource = src;
			}
			else if(typeof scripts[i].getAttribute != 'undefined' && (src = scripts[i].getAttribute('src')))
			{
				p.scriptSource = src;
			}
			else if(scripts[i].text)
			{
				p.text = scripts[i].text;
			}
			else
			{
				continue;
			}
			nscripts.push(p);
		}
	}
	scripts = node.getElementsByTagName('script')
	for(i = 0; i < scripts.length; i++)
	{
		oscripts.push(scripts[i]);
	}
	for(i = 0; i < oscripts.length; i++)
	{
		oscripts[i].parentNode.removeChild(oscripts[i]);
	}
	if(head)
	{
		for(i = 0; i < nscripts.length; i++)
		{
			p = this.createHTMLElement('script');
			this.setAttribute(p, 'type', 'text/javascript');
			if(nscripts[i].scriptSource)
			{
				this.setAttribute(p, 'src', nscripts[i].scriptSource);
				head.appendChild(p);
				continue;
			}
			else
			{
				if(this.browser.isDOM)
				{
					p.appendChild(document.createTextNode(nscripts[i].text));
				}
				if(nscripts[i].waitUntil)
				{
					p.waitUntil = nscripts[i].waitUntil;
					p.scriptText = nscripts[i].text;
					p.ticker = function()
					{
						if(eval('typeof ' + p.waitUntil) != 'undefined')
						{
							console.log('Script class ' + p.waitUntil + ' is now available');
							window.clearInterval(p.interval);
							if(_this.browser.isDOM)
							{
								head.appendChild(p);
							}
							else
							{
								eval(p.scriptText);
							}
						}
					}
					p.interval = window.setInterval(p.ticker, 150);
					continue;
				}
				else if(!this.browser.isDOM)
				{
					eval(nscripts[i].text);
				}
				else
				{
					head.appendChild(p);
				}
			}
		}
	}
	return node;
}

com.uk.become.shine.Core.prototype.resetValid = function(field)
{
	var i;
	
	for(i = 0; i < arguments.length; i++)
	{
		arguments[i].acErrorField = false;
		this.removeElementClass(arguments[i], 'errorField');
		if(arguments[i].shineSurround)
		{
			arguments[i].shineSurround.acErrorSurround = false;
			this.removeElementClass(arguments[i].shineSurround, 'errorSurround');
		}
	}
	this._formDirty++;
}
com.uk.become.shine.Core.prototype.invalidateField = function(field)
{
	var p;
	
	field.defaultValue = field.value;
	field.acErrorField = true;
	this.addElementClass(field, 'errorField');
	for(p = field.parentNode; p; p = p.parentNode)
	{
		if(p.tagName.toLowerCase() == 'div' || p.tagName.toLowerCase() == 'dl' || p.tagName.toLowerCase() == 'tr')
		{
			p.acErrorSurround = true;
			field.shineSurround = p;
			this.addElementClass(p, 'errorSurround');
			break;
		}
	}
	this._formDirty++;
}
com.uk.become.shine.Core.prototype._inputHandler = function(shine, sender, event)
{
	var state, valid, empty, focussed, s, bad, ok;
	
	/* State:
	   1-Focussed
	   2-Valid
	   4-Prevalidated
	   8-Empty
	  16-Self-titled
	  32-Show title text
	 */
	valid = true;
	s = false;
	prevalidated = false;
	if(typeof sender.defaultValue != 'undefined' && sender.defaultValue != '')
	{
		if(sender.value == sender.defaultValue && sender.acErrorField)
		{
			valid = false;
			prevalidated = true;
		}
	}
	empty = (sender.value ? false : true);
	if(sender.shineTitleText && sender.shineTitleText == sender.value)
	{
		empty = true;
	}
	focussed = (sender.focussed ? true: false);
	state = (focussed ? 1 : 0) | 2 | (prevalidated ? 4 : 0) | (empty ? 8 : 0) | (sender.shineTitleText ? 16 : 0) | (sender.shineHideTitleText ? 0 : 32);
	if(!prevalidated && !empty && sender.shineValidator)
	{
		if(!sender.shineValidatedValue || sender.shineValidatedValue != sender.value)
		{
			ev = new Object();
			ev.value = sender.value;
			ev.target = sender;
			ev.empty = empty;
			ev.focussed = focussed;
			valid = this.callEventHandler(sender, ev, sender.shineValidator);
			sender.shineValidatedValue = sender.value;
		}
		else
		{
			valid = sender.shineValidated;
		}
	}
	state &= ~2;
	state |= (valid ? 2 : 0);
	if(typeof sender.shinePrevValue == 'undefined' || sender.value != sender.shinePrevValue)
	{
		this._formDirty++;
		sender.shinePrevValue = sender.value;
	}
	bad = 'Please check the information provided as this field appears to be incorrect or incomplete.';
	ok = '';
	if(sender.shineFieldState != state)
	{
		Shine.setAttribute(sender.shineStyleTarget, 'title', '');
		sender.shineFieldState = state;
		this.beginUpdate(sender);
		this.addElementClass(sender, 'shine-input');
		if(sender.shineSubField)
		{
			this.addElementClass(sender, 'shine-subfield');
		}
		if(sender.shineTitleText)
		{
			this.addElementClass(sender, 'shine-titled');
		}
		this.removeElementClass(sender, 'shine-input-progress');
		this.removeElementClass(sender, 'shine-input-ok');
		this.removeElementClass(sender, 'shine-input-bad');
		this.removeElementClass(sender, 'shine-input-empty');
		this.removeElementClass(sender, 'shine-input-focus');
		this.removeElementClass(sender, 'shine-titled-empty');
		if(sender.shineValidateInProgress)
		{
			this.addElementClass(sender, 'shine-input-progress');
			sender.shineValidated = false;
			sender.shineIncomplete = false;
		}
		else if(empty)
		{
			if(sender.shineTitleText && !sender.shineHideTitleText)
			{
				if(focussed)
				{
					sender.value = '';
				}
				else
				{
					if(sender.value != sender.shineTitleText)
					{
						sender.value = sender.shineTitleText;
					}
				}
				this.addElementClass(sender, 'shine-titled-empty');
			}
			else
			{
				if(sender.value != '') sender.value = '';
			}
			this.addElementClass(sender, 'shine-input-empty');
			if(Shine.getAttribute(sender, 'required', BECOME.NS_SHINE))
			{
				if(this.emptyRequiredBad)
				{
					this.addElementClass(sender, 'shine-input-bad');
					Shine.setAttribute(sender.shineStyleTarget, 'title', bad);
				}
				sender.shineValidated = false;
			}
			else
			{
				sender.shineValidated = true;
			}
			sender.shineIncomplete = true;
		}
		else if(valid)
		{
			this.addElementClass(sender, 'shine-input-ok');
			Shine.setAttribute(sender.shineStyleTarget, 'title', ok);
			sender.shineValidated = true;
			sender.shineIncomplete = false;
		}
		else
		{
			this.addElementClass(sender, 'shine-input-bad');
			Shine.setAttribute(sender.shineStyleTarget, 'title', bad);
			sender.shineValidated = false;
			sender.shineIncomplete = false;
		}
		if(focussed)
		{
			this.addElementClass(sender, 'shine-input-focus');
		}
		this.endUpdate(sender);
	}
	return true;
}
com.uk.become.shine.Core.prototype.highlightForms = function()
{
	this.emptyRequiredBad = true;
	this._formDirty++;
}
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)
{
	if(sender.focussed) return true;
	this._updateHelpPanel(sender);
	sender.focussed = true;
	this._inputHandler(shine, sender, event);
	return true;
}
com.uk.become.shine.Core.prototype._inputBlur = function(shine, sender, event)
{
	if(!sender.focussed) return true;
	sender.focussed = false;
	this._updateHelpPanel(null);
	this._inputHandler(shine, sender, event);
	return true;
}
com.uk.become.shine.Core.prototype._inputClick = function(shine, sender, event)
{
	var tstate = (8|16|32);
	this._inputHandler(shine, sender, event);
	if((sender.shineFieldState & tstate) == tstate && sender.select && sender.focus)
	{
		sender.focus();
		sender.select();
	}
	return true;
}
com.uk.become.shine.Core.prototype._inputKeyUp = function(shine, sender, event)
{
	sender.shineHideTitleText = false;
	this._inputHandler(shine, sender, event);
	return true;
}
com.uk.become.shine.Core.prototype._formSubmit = function(Shine, sender, ev, data)
{
	Shine.debug('Form is being submitted');
	return false;
}
com.uk.become.shine.Core.prototype.addInputHandler = function(element, handler)
{
	var p;
	
	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.addElementClass(element, 'shine-input');
	if(this.hasElementClass(element, 'subfield', true))
	{
		element.shineSubField = true;
		this.addElementClass(element, 'shine-subfield');
	}
	else
	{
		element.shineSubField = false;
	}
	element.shineValidator = handler;
	element.shineValidatorData = handler.data;
	element.shineValidated = false;
	element.shineIncomplete = true;
	element.shineValidateInProgress = false;
	if(!this._errorFieldSection)
	{
		if(this.getAttribute(element, 'invalid', BECOME.NS_SHINE))
		{
			for(p = element; p; p = p.parentNode)
			{
				if(this.getAttribute(p, 'section', BECOME.NS_SHINE))
				{
					this._errorFieldSection = p;
					break;
				}
			}
		}
	}
	this._fieldValidators[this._fieldValidators.length] = element;
	this.addEventHandler(element, 'focus', { host: this, handler: this._inputFocus });
	this.addEventHandler(element, 'blur', { host: this, handler: this._inputBlur });
	this.addEventHandler(element, 'click', { host: this, handler: this._inputClick });
	this.addEventHandler(element, 'keyup', { host: this, handler: this._inputKeyUp });
	return element;
}
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, cn, id;
	
	link = this.createHTMLElement('a');
	span = this.createHTMLElement('span');
	link._shineForm = element.form;
	link._shineSubmitButton = element;
	span._shineLink = link;
	this.setAttribute(link, 'href', 'javascript:void(0);');
	this.setAttribute(span, 'class', 'shine-replaced-button');
	cn = this.getAttribute(element, 'replaceclass', BECOME.NS_SHINE);
	if(cn)
	{
		this.setAttribute(span, 'class', 'shine-replaced-button shine-replaced-' + cn);
	}
	id = this.getAttribute(element, 'id');
	if(id)
	{
		this.setAttribute(span, 'id', 'sr-' + id);
	}
	link.inhibitSubmit = false;
	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)
{
	var inputs;

	if(sender._shineForm)
	{
		inputs = sender._shineForm.getElementsByTagName('input');
		for(var i = 0; i < inputs.length; i++)
		{
			if(inputs[i].shineTitleText && inputs[i].value == inputs[i].shineTitleText)
			{
				inputs[i].value = '';
			}
		}
	}
	if(!this.dispatchEvent(sender, 'beforesubmit', ev))
	{
		ev.stopped = true;
		return false;
	}
	if(sender.inhibitSubmit)
	{
		ev.stopped = true;
		return true;
	}
	if(sender._shineForm && typeof sender._shineForm.submit != 'undefined')
	{
		node = this.createHTMLElement('input');
		this.setAttribute(node, 'type', 'hidden');
		this.setAttribute(node, 'name' , this.getAttribute(sender._shineSubmitButton, 'name'));
		this.setAttribute(node, 'value', this.getAttribute(sender._shineSubmitButton, 'value'));
		sender._shineForm.appendChild(node);
		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;
	}
	if(element.parentNode && element.parentNode.nodeName.toLowerCase() == 'span')
	{
		element.parentNode.shineStyleSource = true;
		element.shineStyleTarget = element.parentNode;
		return element.parentNode;
	}
	newel = this.createHTMLElement('span');
	element.parentNode.replaceChild(newel, element);
	newel.appendChild(element);
	newel.shineStyleSource = true;
	element.shineStyleTarget = newel;
	return newel;
}
com.uk.become.shine.Core.prototype._validateFields = function()
{
	var e, complete, valid;
	
	if(this._validating)
		{
			return true;
		}
	if(!this._formDirty)
		{
			if(this._validatorInterval)
			{
				window.clearInterval(this._validatorInterval);
			}
			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 = '';
			this.beginUpdate(this._forms[f]);
			this.removeElementClass(this._forms[f], 'form-complete');
			this.removeElementClass(this._forms[f], 'form-incomplete');
			this.removeElementClass(this._forms[f], 'form-valid');
			this.removeElementClass(this._forms[f], 'form-invalid');
			this.removeElementClass(this._forms[f], 'form-valid-complete');
			this.removeElementClass(this._forms[f], 'form-not-valid-complete');
			this._forms[f].shineValidComplete = false;
			this._forms[f].shineValid = false;
			this._forms[f].shineComplete = false;
			if(complete)
				{
					this.addElementClass(this._forms[f], 'form-complete');
					this._forms[f].shineComplete = true;
				}
			else
				{
					this.addElementClass(this._forms[f], 'form-incomplete');
				}
			if(valid)
				{
					this.addElementClass(this._forms[f], 'form-valid');
					this._forms[f].shineValid = true;
				}
			else
				{
					this.addElementClass(this._forms[f], 'form-invalid');
				}
			if(complete && valid)
				{
					this.addElementClass(this._forms[f], 'form-valid-complete');
					this._forms[f].shineValidComplete = true;
				}
			else
				{
					this.addElementClass(this._forms[f], 'form-not-valid-complete');
				}
			this.endUpdate(this._forms[f]);
		}
	this._validating = false;
	if(this._formDirty > 0)
		{
			this._formDirty--;
		}
}
com.uk.become.shine.Core.prototype.addLoadHandler = function(obj, data)
{
	var ev;
	
	if(this._loaded)
	{
		ev = new Object();
		if(typeof obj.ondocumentloaded == 'undefined')
		{
			Shine.debug('Shine:', "Warning: Load handler doesn't implement ondocumentloaded");
		}
		else
		{
			obj.ondocumentloaded(Shine, Shine, ev, data)
		}
	}
	else
	{
		this._loadHandlers[this._loadHandlers.length] = new Array(obj, data);
	}
}
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.smallPopupLinkHandler = function(Shine, sender, ev, data)
{
	if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey)
		{
			return true;
		}
	return Shine.createPopup(sender.shineLinkRef, 500, 300);
}
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.debugPopupLinkHandler = 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._navigatePage = function(Shine, sender, ev, data)
{
	this.debug(new Date() + ': Navigating to ' + data);
	this.setPage(data);
	this.debug(new Date() + ': Done');
	return false;
}
com.uk.become.shine.Core.prototype.attach = function(root)
{
	var nodes, _this = this, i, href;
	
	window.Shine = this;
	nodes = root.getElementsByTagName('a');
	for(i = 0; i < nodes.length; i++)
	{
		if(this.hasElementClass(nodes[i], 'shine-popup-link', true))
		{
			this.addLinkHandler(nodes[i], { host: Shine, handler: Shine.popupLinkHandler });
		}
		else if(this.hasElementClass(nodes[i], 'shine-smallpopup-link', true))
		{
			this.addLinkHandler(nodes[i], { host: Shine, handler: Shine.smallPopupLinkHandler });
		}
		else if(this.hasElementClass(nodes[i], 'shine-largepopup-link', true))
		{
			this.addLinkHandler(nodes[i], { host: Shine, handler: Shine.largePopupLinkHandler });
		}
		else if(this.hasElementClass(nodes[i], 'shine-contentpopup-link', true))
		{
			this.addLinkHandler(nodes[i], { host: Shine, handler: Shine.largePopupLinkHandler });
		}
		else if(this.hasElementClass(nodes[i], 'shine-debugpopup-link', true))
		{
			this.addLinkHandler(nodes[i], { host: Shine, handler: Shine.debugPopupLinkHandler });
		}
		else if(this.hasElementClass(nodes[i], 'shine-external-link', true))
		{
			this.addLinkHandler(nodes[i], { host: Shine, handler: Shine.externalLinkHandler });
		}
		else if(this.hasElementClass(nodes[i], 'shine-nonhtml-link', true))
		{
			this.addLinkHandler(nodes[i], { host: Shine, handler: Shine.externalLinkHandler });
		}
/*		else
		{
			href = String(Shine.getAttribute(nodes[i], 'href'));
			href = href.replace(/^.*(#[^#]*)$/, '$1');
			if(href.substr(0, 1) == '#' && href.length > 1)
			{
				this.addLinkHandler(nodes[i], { host: Shine, handler: Shine._navigatePage, data: href.substr(1) });
			}
		} */
	}
	nodes = this.getElementsByClass(root, 'input', 'shine-submit-button');
	this.addSubmitReplacements(nodes);
	/* Set up form field validation */
	if(root == document)
	{
		this._validatorInterval = window.setInterval(function() {  _this._validateFields(); }, 250);
	}
	this._formDirty++;
	this._validateFields();
}
com.uk.become.shine.Core.prototype.validateFields = function()
{
	this._formDirty++;
	this._validateFields();
}
com.uk.become.shine.Core.prototype.registerNamespace = function(ns, prefix)
{
	this._namespaces[ns] = prefix;
}
com.uk.become.shine.Core.prototype._getNamespaces = function()
{
	var root = document.documentElement;
	var e, i, a, attrs = false;
	
	this._namespaces[BECOME.NS_SHINE] = 'shine';
	try
	{
		i = root.attributes.length;
		attrs = true;
	}
	catch(e)
	{
	}
	if(attrs)
	{
		for(i = 0; i < root.attributes.length; i++)
		{
			a = root.attributes.item(i);
			if(a.name.indexOf('xmlns:') != 0)
			{
				continue;
			}
			this._namespaces[a.value] = a.name.substring(6);
		}
	}
}
com.uk.become.shine.Core.prototype.loaded = function(Shine, sender, ev, data)
{
	var nodes, d, _this = this;

	if(this._loaded)
		{
			return true;
		}
	else if(this._abort)
		{
			return false;
		}
	d = new Date();
	d.setTime(d.getTime() + (24 * 60 * 60 * 1000));
	document.cookie = 'jsEnabled=1; expires=' + d.toGMTString() + '; path=' + this.rootPath;
	this._getNamespaces();
	this.body = document.getElementsByTagName('body').item(0);
	this._loaded = true;
	this.attach(document);
	for(h in this._loadHandlers)
	{
		this._loadHandlers[h][0].ondocumentloaded(Shine, sender, ev, this._loadHandlers[h][1]);
	}
	Shine.addEventHandler(window, 'hashchange', { host: this, handler: this._checkPages });
	this._pageTimer = window.setInterval(function() { _this._checkPages(); }, 300);
	return true;
}
com.uk.become.shine.Core.prototype._checkPages = function()
{
	var h, cp;
	
	if(this._pageChanging) return true;
	if(this._errorFieldSection)
	{
		h = this.getAttribute(this._errorFieldSection, 'id');
		this._errorFieldSection = null;
		this.setPage(h);
		return;
	}
	if(window.location.hash != this._currentPage)
	{
		this._pageChanging = true;
		ev = new Object();
		ev.pageName = window.location.hash.substring(1);
		BECOME.DOM.dispatchEvent(document, 'PageChange', ev);
		this._currentPage = window.location.hash;
		this._pageChanging = false;
	}
	return true;
}
com.uk.become.shine.Core.prototype.setPage = function(name)
{
	if(name && name != window.location.hash)
	{
		window.location.hash = name;
	}
	else
	{
		window.location.hash = '';
	}
	this._checkPages();
	return true;
}
com.uk.become.shine.Core.prototype._updateHelpPanel = function(el)
{
	var el;
	
	this._helpPanelElement = el;
	if(!this._helpPanel) return false;
	while(this._helpPanel.firstChild)
	{
		this._helpPanel.removeChild(this._helpPanel.firstChild);
	}
	this._helpPanel.style.display = 'none';
	if(el)
	{
		this.removeElementClass(this._helpPanel, 'error');
		if((h = this.getAttribute(el, 'help', 'http://www.becomeinteractive.co.uk/ns/shine#')))
		{
			p = this.createHTMLElement('p');
			p.appendChild(document.createTextNode(h));
			this._helpPanel.appendChild(p);
			this._helpPanel.style.display = 'block';
		}
	}
	else if(this._helpPanelError)
	{
		this.addElementClass(this._helpPanel, 'error');
		p = this.createHTMLElement('p');
		p.appendChild(document.createTextNode(this._helpPanelError));
		this._helpPanel.appendChild(p);
		this._helpPanel.style.display = 'block';
		
	}
	else if(this._helpPanelInfo)
	{
		this.removeElementClass(this._helpPanel, 'error');
		p = this.createHTMLElement('p');
		p.appendChild(document.createTextNode(this._helpPanelInfo));
		this._helpPanel.appendChild(p);
		this._helpPanel.style.display = 'block';
		
	}
}
com.uk.become.shine.Core.prototype.setHelpPanelInfo = function(text)
{
	this._helpPanelInfo = text;
	this._updateHelpPanel(this._helpPanelElement);
}
com.uk.become.shine.Core.prototype.setHelpPanelError = function(text)
{
	this._helpPanelError = text;
	this._updateHelpPanel(this._helpPanelElement);
}
com.uk.become.shine.Core.prototype.setHelpPanel = function(panel)
{
	this._helpPanel = panel;
	this._updateHelpPanel(this._helpPanelElement);
}
com.uk.become.shine.Core.prototype.importNode = function(oNode, bChildren)
{
	var tmp;
	
	if(document.importNode) return document.importNode(oNode, bChildren);
        if (oNode.nodeName=='#text') {
            return document.createTextElement(oNode.data);
        }
        else {
            if(oNode.nodeName == "tbody" || oNode.nodeName == "tr"){
                tmp = document.createElement("table");
            }
            else if(oNode.nodeName == "td"){
                tmp = document.createElement("tr");
            }
            else if(oNode.nodeName == "option"){
                tmp = document.createElement("select");
           }
            else{
                tmp = document.createElement("div");
            };
            if(bChildren){
                tmp.innerHTML = oNode.xml ? oNode.xml : oNode.outerHTML;
            }else{
                tmp.innerHTML = oNode.xml ? oNode.cloneNode(false).xml : oNode.cloneNode(false).outerHTML;
            };
            return tmp.getElementsByTagName("*")[0];
        };
}
com.uk.become.shine.Core.prototype.rand = function(min, max)
{
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
com.uk.become.shine.Core.prototype.getText = function(el)
{
	if(typeof el.textContent != 'undefined')
	{
		return el.textContent;
	}
	if(typeof el.innerText != 'undefined')
	{
		return el.innerText;
	}
	if(typeof el.text != 'undefined')
	{
		return el.text;
	}
	return true;
}
window.Shine = new com.uk.become.shine.Core(window, document);
