function Browser()
{
	var ua, s, i;
	this.isIE    = false;
	this.isNS    = false;
	this.isSafari = false;
	this.version = null;
	ua = navigator.userAgent;
	
	// Need to do this before the other browsers, because Safari's navigator.userAgent contains MSIE and Gecko. Also note that the version number for
	// Safari will be an Apple internal version number, and not the version number the user sees.
	// Warning! This has not yet been tested in Safari! (The information above is taken from http://www.quirksmode.org/js/detect.html)
	s = 'Safari';
	if((i = ua.indexOf(s)) >= 0)
	{
		this.isSafari = true;
		this.version = parseFloat(ua.substr(i + s.length));
		return;
	}
	
	s = "MSIE";
	if ((i = ua.indexOf(s)) >= 0) {
		this.isIE = true;
		this.version = parseFloat(ua.substr(i + s.length));
		return;
	}
	s = "Netscape6/";
	if ((i = ua.indexOf(s)) >= 0) {
		this.isNS = true;
		this.version = parseFloat(ua.substr(i + s.length));
		return;
	}
	s = "Gecko"; // Treat any other "Gecko" browser as NS 6.1.
	if ((i = ua.indexOf(s)) >= 0) {
		this.isNS = true;
		this.version = 6.1;
		return;
	}
}
/**
 * XMLHTTP Request object
 *  - returns a XMLHTTP request object
 * @component AJAX
 */
function RequestObject()
{
	if(browser.isIE)
		return new ActiveXObject("Microsoft.XMLHTTP");
	else
		return new XMLHttpRequest();
}
/**
 * HTTP Request processor
 * - processes requests
 * @component AJAX
 */
function RequestProcessor()
{
	this.queue = new Array();
	this.position = 0;
	this.busy = false;
	this.async = true;
	this.showLoadingNotice = false; //option to show loading notices. NOTE: This may not work very reliably with simultanious requests
	
	// the async parameter is depreciated, please specify the parameter in the request object
	this.add = function(request,async)
	{
		// Use a timeout to put in new requests or strange errors will occur if the previous request was aborted, possibly because of some sort of
		// strange internal browser timing issue.
		var ref = this;
		setTimeout
		(
			function()
			{
				request.requestProcess = ref; //add reference to this to request obj
				ref.queue[ref.queue.length] = request;
				ref.process();
			},
			10
		);
		return this.queue.length;
	}
	this.abort = function(ticket)
	{
		if(this.position == ticket)
		{
			http.abort();
		}
		this.queue[ticket].abort = true;
	}
	
	this.abortCurrent = function()
	{
		http.abort();
		if(typeof(this.queue[this.position - 1]) != 'undefined')
		{
			this.queue[this.position - 1].abort = true;
		}
	}
	
	this.process = function()
	{
		if(this.busy||http.readyState!=0&&http.readyState!=4)
		{
			if(this.queue[this.position+1])
			{
				setTimeout(function(){requestProcess.process();},50);
			}
			return;
		}
		this.busy = true;
		
		//draw loading message if we are showing loading notices.
		if(this.showLoadingNotice) notifier.loading(notifier.LOADING_TRUE);
		
		if(!this.queue[this.position])
		{
			this.position--;
		}
		var current = this.queue[this.position];
		this.position++;
		if(current.abort)
		{
			this.done();
			return;
		}
		
		http.open(current.method, current.address, current.async);
		if(current.method == 'POST')
		{
			http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
			http.setRequestHeader('Content-length', current.data.length);
		}
		
		//NOTE: for an SYNCHRONOUS request, this shouldnt really be called, though IE and Firebug makes Firefox call it.
		http.onreadystatechange = current.onStateChange;

		http.send(current.data);
		
		//if a SYNCHRONOUS request, then we dont need to use busy, as execution will be halted on http.send() until a response is received.
		if(this.async == false)
		{
			//enable this line if you want to use callbacks with SYNCHRONOUS requests..
			//current.onStateChange();
			//this.busy = false;
		}
	}
	this.done = function()
	{
		this.busy = false;
		if(this.position < this.queue.length)
		{
			setTimeout(function(){requestProcess.process();},50);
		}
	}
}
/**
 * Request object
 * - holds all information for a request
 * @component AJAX
 */
function Request(method, address, data, onStateChange, onFail, output)
{
	var ref = this;
	
	this.async = true;
	this.method = method;
	this.address = address;
	this.data = data;

	this.requestProcess = false; //to reference RequestProcess object that is handling this request
	this.output = output;

	//this may not be called in synchronous requests...
	this.onStateChange = function()
	{
		if(http.readyState == 4)
		{
			//HIDE loading message if we are showing loading notices.
			if(ref.requestProcess && ref.requestProcess.showLoadingNotice)
			{
				notifier.loading(notifier.LOADING_FALSE);
			}
			
			try
			{
				if(http.status != 200)
				{
					throw http.status;
				}
			}
			catch(e)
			{
				// If the request was aborted, then we will get a value of 0 here in IE, and a long and detailed error string containing NS_ERROR_NOT_AVAILABLE
				// in Firefox.
				if(e != 0 && !e.toString().match('NS_ERROR_NOT_AVAILABLE'))
				{
					switch(e)
					{
						case 404:
						{
							notifier.notify('error','404 Not found, please try again');
							break;
						}
						case 500:
						{
							notifier.notify('error','500 Internal error, please try again');
						}
						default:
						{
							notifier.notify('error','Communication error, please try again');
						}
					}
				}
				onFail();
				requestProcess.done();
				return;
			}
			
			if(ref.output==true)
			{
				onStateChange(http.responseText);
			}
			else
			{
				try
				{
					var response = eval('('+http.responseText+')');
				}
				catch(e)
				{
					notifier.notify('error','Unable to interpret server response');
					onFail();
					requestProcess.done();
					return;
				}
				
				try
				{
					switch(response['status'])
					{
						case 'ok':
						{
							onStateChange(response['data']);
							break;
						}
						case 'info':
						{
							notifier.notify('info', response['info']);
							onStateChange(response['data']);
							break;
						}
						case 'warning':
						{
							notifier.notify('warning', response['warning']);
							onStateChange(response['data']);
							break;
						}
						case 'error':
						{
							notifier.notify('error', response['error']);
							onFail(response['error']);
							break;
						}
						case 'kick':
						{
							createCookie('error', response['error'], 0);
							window.location = 'index.php?logout=1';
							break;
						}
						default:
						{
							notifier.notify('error', 'Garbage? '+http.responseText);
							break;
						}
					}
				}
				catch(e)
				{
					notifier.notify('error',e.message);
					//alert('Request.onStateChange():: '+e.message);
				}
			}
			requestProcess.done();
		}
	}
	this.abort = false;
}
/**
 * JSON encoder
 */
function toJSONString(x)
{
  return s.object(x);
};

var m = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        }
var s = {
            array: function (x) {
                var a = ['['], b, f, i, l = x.length, v;
                for (i = 0; i < l; i += 1) {
                    v = x[i];
                    f = s[typeof v];
                    if (f) {
                        v = f(v);
                        if (typeof v == 'string') {
                            if (b) {
                                a[a.length] = ',';
                            }
                            a[a.length] = v;
                            b = true;
                        }
                    }
                }
                a[a.length] = ']';
                return a.join('');
            },
            'boolean': function (x) {
                return String(x);
            },
            'null': function (x) {
                return "null";
            },
            number: function (x) {
                return isFinite(x) ? String(x) : 'null';
            },
            object: function (x) {
                if (x) {
                    if (x instanceof Array) {
                        return s.array(x);
                    }
                    var a = ['{'], b, f, i, v;
                    for (i in x) {
                        v = x[i];
                        f = s[typeof v];
                        if (f) {
                            v = f(v);
                            if (typeof v == 'string') {
                                if (b) {
                                    a[a.length] = ',';
                                }
                                a.push(s.string(i), ':', v);
                                b = true;
                            }
                        }
                    }
                    a[a.length] = '}';
                    return a.join('');
                }
                return 'null';
            },
            string: function (x) {
                if (/["\\\x00-\x1f]/.test(x)) {
                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                        var c = m[b];
                        if (c) {
                            return c;
                        }
                        c = b.charCodeAt();
                        return '\\u00' +
                            Math.floor(c / 16).toString(16) +
                            (c % 16).toString(16);
                    });
                }
                return '"' + x + '"';
            }
        };var Notifier = function()
{
	this.LOADING_TRUE = true;
	this.LOADING_FALSE = false;

	this._notification_loading = new Notification();
	this._dom_root = createElement('div','notifier');
	this._dom_root.id = 'notifier';
	
	this.notify = function(type, message)
	{
		var not = new Notification();
		not.setType(type);
		not.setMessage(message);
		this._dom_root.appendChild(not.draw());
		setTimeout(not.remove, 3500);
	}

	/**
	* Status is constant, this.LOADING_TRUE or this.LOADING_FALSE
	* callback should be a function to execute after the loading 
	* message has been displayed
	*/
	this.loading = function(status, callback)
	{
		try
		{
			switch(status)
			{
				case this.LOADING_TRUE:
				{
					this._notification_loading._int_opacity =90;
					this._notification_loading.fade();
					break;
				}
				case this.LOADING_FALSE:
				{
					this._notification_loading.remove();
					break;
				}
			}
			//set callback timeout if supplied
			if(callback && typeof callback == 'function')
			{
				setTimeout(function(){callback();}, 50);
			}
		}
		catch(e)
		{
			// exception thrown if laoding is called (e.g. from ajax object)
			// and the notifier hasn't been drawn to the screen yet
			callback();
		}
	}

	this.draw = function()
	{
		this._notification_loading.setType(Notification.TYPE_LOADING);
		this._notification_loading.setMessage('Loading...');
		this._dom_root.appendChild(this._notification_loading.draw());
		this._int_step = 90;
		this._notification_loading.remove();
		return this._dom_root;
	}
}
var Notification = function()
{
	this.TYPE_ERROR = 'error';
	this.TYPE_INFO = 'info';
	this.TYPE_WARNING = 'warning';
	this.TYPE_STATUS = 'status';
	this.TYPE_LOADING = 'loading';
	
	var ref = this;
	this._str_message;
	this._str_type;
	this._dom_root;
	this._int_opacity = 100;
	this._int_step = 2;
	
	this.setType = function(type){ this._str_type = type;}
	this.getType = function(){ return this._str_type;}
	this.setMessage = function(message){this._str_message = message;}
	this.getMessage = function(){ return this._str_message;}

	this.remove = function()
	{
		var speed = Math.round(1000 / 100);
		var timer = 0;
		for(var opacity = ref._int_opacity; opacity >= 0; opacity-=ref._int_step)
		{
			setTimeout(ref.fade, (timer * speed));
			timer+=ref._int_step;
		}
	}
	this.fade = function()
	{
		try
		{
			var object = ref._dom_root.style;
			object.opacity = (ref._int_opacity / 100);
			// folowing not needed for firefox 2, and causes problems in IE 7?!
			//object.MozOpacity = (ref._int_opacity / 100);
			object.KhtmlOpacity = (ref._int_opacity / 100);
			object.filter = "alpha(opacity=" + ref._int_opacity + ")";
			ref._int_opacity -=ref._int_step;
			if(ref._int_opacity <= 0 )
			{
				object.display = 'none';
			}
			else
			{
				object.display = 'block';
			}
		}
		catch(err)
		{
			// do nothing
		}
	}
	this.draw = function()
	{
		this._dom_root = createElement('div', this.getType());
		this._dom_root.innerHTML = this.getMessage();
		this._dom_root.onclick = this.remove;
		this.fade();
		return this._dom_root;
	}
}
var createElement = function(type, classname)
{
	var tmp = document.createElement(type);
	tmp.className = classname;
	return tmp;
}
var createTextElement = function(type, classname, text)
{
	var tmp = createElement(type, classname);
	tmp.innerHTML = text;
	return tmp;
}
var getElement = function(id)
{
	return document.getElementById(id);
}
var removeAllChildren = function(dom)
{
	if(dom)
	{
	  while(dom.hasChildNodes())
	  {
	    dom.removeChild(dom.firstChild);
	  }
	}
}
if ( typeof DOM == "undefined" ) DOM = {};

/**
* Creates DOM element and sets classname
*/
DOM.createElement = function()
{
	var tmp = document.createElement(type);
	tmp.className = classname;
	return tmp;
}

/**
* Creates a DOM element with a classname and inner text
*/
DOM.createTextElement = function(type, classname, text)
{
	var tmp = createElement(type, classname);
	tmp.innerHTML = text;
	return tmp;
}

/**
* Gets a DOM element based on ID
*/
DOM.getElement = function(id)
{
	return document.getElementById(id);
}

/**
* Removes all children from a DOM element
*/
DOM.removeAllChildren = function(dom)
{
  if(dom)
  {
    while(dom.hasChildNodes())
    {
      dom.removeChild(dom.firstChild);
    }
  }
}

/**
* Executes a function when the DOM has been sycned
*/
DOM.Ready = {};

DOM.Ready.finalTimeout = 15;
DOM.Ready.timerInterval = 50;

/* This works for Mozilla */
if ( document.addEventListener ) {
    document.addEventListener
        ( "DOMContentLoaded", function () { DOM.Ready._isDone = 1; }, false );
}

DOM.Ready._checkDOMReady = function () {
    if ( DOM.Ready._isReady ) return DOM.Ready._isReady;

    if (    typeof document.getElementsByTagName != 'undefined'
         && typeof document.getElementById != 'undefined' 
         && ( document.getElementsByTagName('body')[0] != null
              || document.body != null ) ) {

        DOM.Ready._isReady = 1;
    }

    return DOM.Ready._isReady;

};

DOM.Ready._checkDOMDone = function () {
    /* IE (and Opera?) only */
    if ( document.readyState
         && ( document.readyState == "interactive"
              || document.readyState == "complete" ) ) {
        return 1;
    }

    return DOM.Ready._isDone;
};

DOM.Ready.onDOMReady = function (callback) {
    if ( DOM.Ready._checkDOMReady() ) {
        callback();
    }
    else {
    	try
	{
        DOM.Ready._onDOMReadyCallbacks.push(callback);
	}catch(e)
	{

	}
    }
}

DOM.Ready.onDOMDone = function (callback) {
    if ( DOM.Ready._checkDOMDone() ) {
        callback();
    }
    else {
        DOM.Ready._onDOMDoneCallbacks.push(callback);
    }
}

DOM.Ready.onIdReady = function ( id, callback ) {
    if ( DOM.Ready._checkDOMReady() ) {
        var elt = document.getElementById(id);
        if (elt) {
            callback(elt);
            return;
        }
    }

    var callback_array = DOM.Ready._onIdReadyCallbacks[id];
    if ( ! callback_array ) {
        callback_array = [];
    }
    callback_array.push(callback);

    DOM.Ready._onIdReadyCallbacks[id] = callback_array;
}

DOM.Ready._runDOMReadyCallbacks = function () {
    for ( var i = 0; i < DOM.Ready._onDOMReadyCallbacks.length; i++ ) {
        DOM.Ready._onDOMReadyCallbacks[i]();
    }

    DOM.Ready._onDOMReadyCallbacks = [];
}

DOM.Ready._runDOMDoneCallbacks = function () {
    for ( var i = 0; i < DOM.Ready._onDOMDoneCallbacks.length; i++ ) {
        DOM.Ready._onDOMDoneCallbacks[i]();
    }

    DOM.Ready._onDOMDoneCallbacks = [];
}

DOM.Ready._runIdCallbacks = function () {
    for ( var id in DOM.Ready._onIdReadyCallbacks ) {
        // protect against changes to Object (ala prototype's extend)
        if ( ! DOM.Ready._onIdReadyCallbacks.hasOwnProperty(id) ) {
            continue;
        }

        var elt = document.getElementById(id);

        if (elt) {
            for ( var i = 0; i < DOM.Ready._onIdReadyCallbacks[id].length; i++) {
                DOM.Ready._onIdReadyCallbacks[id][i](elt);
            }

            delete DOM.Ready._onIdReadyCallbacks[id];
        }
    }
}

DOM.Ready._runReadyCallbacks = function () {
    if ( DOM.Ready._inRunReadyCallbacks ) return;

    DOM.Ready._inRunReadyCallbacks = 1;

    if ( DOM.Ready._checkDOMReady() ) {
        DOM.Ready._runDOMReadyCallbacks();

        DOM.Ready._runIdCallbacks();
    }

    if ( DOM.Ready._checkDOMDone() ) {
        DOM.Ready._runDOMDoneCallbacks();
    }

    DOM.Ready._timePassed += DOM.Ready._lastTimerInterval;

    if ( ( DOM.Ready._timePassed / 1000 ) >= DOM.Ready.finalTimeout ) {
        DOM.Ready._stopTimer();
    }

    DOM.Ready._inRunReadyCallbacks = 0;
}

DOM.Ready._startTimer = function () {
    DOM.Ready._lastTimerInterval = DOM.Ready.timerInterval;
    DOM.Ready._intervalId = setInterval( DOM.Ready._runReadyCallbacks, DOM.Ready.timerInterval );
};

DOM.Ready._stopTimer = function () {
    clearInterval( DOM.Ready._intervalId );
    DOM.Ready._intervalId = null;
}

DOM.Ready._resetClass = function () {
    DOM.Ready._stopTimer();

    DOM.Ready._timePassed = 0;

    DOM.Ready._isReady = 0;
    DOM.Ready._isDone = 0;

    DOM.Ready._onDOMReadyCallbacks = [];
    DOM.Ready._onDOMDoneCallbacks = [];
    DOM.Ready._onIdReadyCallbacks = {};

    DOM.Ready._startTimer();
}

DOM.Ready._resetClass();

DOM.Ready.runCallbacks = function () { DOM.Ready._runReadyCallbacks() };
/**
* Basic form object (editing and adding)
*/
var Form = function(action, onreturn, title, oncancel, description)
{
	// string
	this.action = action;
	// array
	this.fieldset = new Array();
	// array
	this.buttons = new Array();
	// function(data)
	this.onreturn = onreturn;
	this.oncancel = oncancel;

  this.title = title;
	// submit button label
	this.label_submit = 'Submit';
  
  this.summarise = false;

	this.dom_root = createElement('form', action.replace('/', '_'));
	this.dom_root.isSubmitted = false;
	this.dom_root.id = 'mainform';

  if(title)
  {
		var dom_legend = createElement('div');
		dom_legend.className = 'legend';
    dom_legend.innerHTML = title;
    this.dom_root.appendChild(dom_legend);
  }
	
	if(description)
	{
		var dom_description = createElement('div','formdescription');
		dom_description.innerHTML = description;
		this.dom_root.appendChild(dom_description);
	}

	// Button
	this.button;
  
	if(oncancel!=null)
	{
		this.cancelButton;
	}
	
	this.getData = function()
	{
		// Save any TinyMCE content that might exist on the page.
		if(typeof(tinyMCE) !== 'undefined')
		{
			tinyMCE.triggerSave();
		}
		var data = '';
		for(fieldset in this.fieldset)
		{
			var fields = this.fieldset[fieldset].getFields();
			for(field in fields)
			{
				//special handling for multiple select field.
				if(fields[field].type == 'SelectField' && fields[field].mode == 'multi')
				{
					var selectedOptions = fields[field].getSelected();
					for(itemkey in selectedOptions)
					{
						data += '&' + fields[field].getName() + '[]=' + selectedOptions[itemkey];
					}
				}
				else if (fields[field].dom_input || fields[field].type == 'SeperatedDateField')
				{
					data += '&' + fields[field].getName() + '=' + fields[field].getValue();
				}
			}
		}
		return data.substring(1);
	}
	
	var ref = this;
	this.onfail = function()
	{
		ref.button.enable();
    
    if(oncancel!=null)
    {
      ref.cancelButton.enable();
    }
	}

  this.setSubmitLabel = function(label)
  {
    this.label_submit = label;
  }

	// If this form contains a file field that is currently uploading, returns this field, or false otherwise
	this.hasActiveUpload = function()
	{
		// Go through the form looking for file upload fields
		for(fieldset in ref.fieldset)
		{
			var fields = ref.fieldset[fieldset].getFields();
			for(field in fields)
			{
				if(fields[field].dom_input && fields[field].dom_input.type == 'file')
				{
					// See if an upload is in progress for this field
					if(fields[field].uploadInProgress())
					{
						return fields[field];
					}
				}
			}
		}
		
		// If we got here, there are no active uploads
		return false;
	}
	
	this.submit = function()
	{
		ref.button.disable ();
		var field;
		if(field == ref.hasActiveUpload())
		{
			// Don't submit until the upload is complete.
			ref.dom_root.isSubmitted = true;
			field.dom_root.hook_complete = function()
			{
				ref.submit();
			}
			return false;
		}

		// If a field is marked as required and the field does not have a value, the form cannot be submitted.
		for(fieldset in ref.fieldset)
		{
			var fields = ref.fieldset[fieldset].getFields();
			for(field in fields)
			if
			(
				(fields[field].getType() == 'DateField' && fields[field].getRequired() && fields[field].getValue() == 0) ||
				(fields[field].getRequired() && fields[field].getValue() === '')
			)
			{
				notifier.notify('error', 'You must supply a value for the ' + fields[field].getLabel() + ' field');
				ref.button.enable();
				return false;
			}
		}
		var ticket = requestProcess.add(new Request ('POST', '/api/'+ref.action, ref.getData(), ref.onreturn, ref.onfail));
		return false;
	}
	
	// I think both of these are needed to get this working in Firefox and Internet Explorer
	this.dom_root.onsubmit = this.submit;
	window.onsubmit = this.submit;

	// doesn't work
	this.setOnFail = function(onFail)
	{
		this.onFail = onFail;
	}
	
	this.addFieldset = function(fieldset)
	{
		fieldset.ref_parent = this;
		this.fieldset[this.fieldset.length] = fieldset;
	}
	
	this.addField = function(field)
	{
		if(this.fieldset.length == 0)
		{
			this.fieldset[0] = new Fieldset();
			this.fieldset[0].ref_parent = this;
		}
		this.fieldset[0].addField(field);
	}

	this.addFields = function(fields)
	{
		if(this.fieldset.length == 0)
		{
			this.fieldset[0] = new Fieldset();
			this.fieldset[0].ref_parent = this;
		}
		this.fieldset[0].addFields(fields);
	}
	
	this.getField = function(name)
	{
		// Need to go through all of the fieldsets in the form, then through each of the fields in these
		for(fieldset in this.fieldset)
		{
			for(field in this.fieldset[fieldset].fields)
			{
				if(this.fieldset[fieldset].fields[field].getName() == name)
				{
					return this.fieldset[fieldset].fields[field];
				}
			}
		}
		return false;
	}
	
	this.removeField = function(name)
	{
		for(field in this.fields)
		{
			if(this.fields[field].getName() == name)
			{
				this.fields[field] = null;
				return true;
			}
		}
		return false;
	}
	
	this.reset = function(oncancel) //depreciated ... i think.
	{
		for(field in this.fields)
		{
			this.fields[field].clear();
		}
		oncancel();
		return false;
	}
  
  // I think both of these are needed to get this working in Firefox and Internet Explorer
  this.dom_root.onreset = this.reset;
  window.onreset = this.reset;

	this.draw = function()
	{
		removeAllChildren(this.dom_root);
		for(fieldset in this.fieldset)
		{
			this.dom_root.appendChild(this.fieldset[fieldset].draw());
			this.dom_root.appendChild(createElement('div','clear'));

		}

		var buttons = createElement('div','buttons');
		for(button in this.buttons)
		{
			buttons.appendChild(this.buttons[button].draw());
		}
		this.button = new ImageButton(this.label_submit, this.submit, true, 'button submit');
		buttons.appendChild(this.button.draw());
		if(oncancel!=null)
		{
			this.cancelButton = new ImageButton('Cancel', this.oncancel, true, 'button cancel');
			buttons.appendChild(this.cancelButton.draw());
		}
		this.dom_root.appendChild(buttons);
		this.dom_root.appendChild(createElement('div','clear'));

		return this.dom_root;
	}
	
	//set whether to summarise entered data - will css hide all fields that are empty..
	this.setSummariseMode = function(value)
	{
		this.summarise = value;
	}

	/*
	 * Hides blank fields so that the order can be reviewed before submitting
	 * Should set summarise mode to true, rather than calling this function from outside the object
	 */
	this.summariseOrder = function()
	{
		//hide all the blank fields..
		
		var i = 0;
		for (field in ref.fields)
		{
			var regExpHasValue = new RegExp("[A-Za-z0-9]+", "i");

			if(this.fields[field].dom_input.type == 'text')
			{
				if(regExpHasValue.test(this.fields[field].dom_input.value, "i") == true)
				{
					
					this.fields[field].dom_root.style.border = '2px solid #87AFBF';
				}
				else
				{
					this.fields[field].dom_root.style.display = 'none';
				}
			}
		}
		notifier.notify('info','Please review your order before submitting.');
		this.setSummariseMode(false);
	}
	this.showField = function(name)
	{
		// Go through all of the fieldsets trying to show the field
		for(var x in this.fieldset)
		{
			if(this.fieldset[x].showField(name))
			{
				// Field found and marked as visible, redraw the form, then there's nothing more to do
				this.draw();
				return;
			}
		}
	}
	
	this.hideField = function(name)
	{
		for(var x in this.fieldset)
		{
			if(this.fieldset[x].hideField(name))
			{
				// Field found and marked as hidden, redraw the form, then there's nothing more to do
				this.draw();
				return;
			}
		}
	}
	
	this.getDomRoot = function()
	{
		return this.dom_root;
	}
}

/**
* Prototype form input, implements basic form input functions
* Note that values that don't have a coresponding override in the extending JS function will be static
*/
var FormField = function()
{
	// string
	this.label;
	// string
	this.description;
	// string
	this.name;
  // string
  this.id;
	// string (default, could be something else)
	this.value = '';
	// string
	this.hint = '';
	// boolean
	this.required = false;
	// dom element
	this.dom_label;
	// tab order
	this._tab_order;
	
	// Should the control be drawn or not.
	this.hidden = false;
	
	this.type;
	
	this.getLabel = function()
	{
		return this.label;
	}
	this.setLabel = function(label)
	{
		this.label = label;
	}
	this.getHint = function()
	{
		return this.hint;
	}
	this.setHint = function(hint)
	{
		this.hint = hint
	}

	this.setTabOrder = function(tab_order)
	{
		this._tab_order = tab_order;
	}
	this.getTabOrder = function()
	{
		return this._tab_order;
	}

  this.setID = function(id)
  {
    this.id = id;
  }
  this.getID = function()
  {
    return this.id;
  }
	
	this.setName = function(name)
	{
		this.name = name;
	}
	this.getName = function()
	{
		return this.name;
	}
	
  this.setDescription = function(description)
  {
      this.description = description;
  }
  this.getDescription = function()
  {
       return this.description;
  }
  
	this.getType = function()
	{
		return this.type;
	}

	this.clear = function()
	{
		this.value = '';
	}

	this.getRequired = function()
	{
		return this.required;
	}
	
	this.setRequired = function(required)
	{
		this.required = required;
	}
	
	this.getHidden = function()
	{
		return this.hidden;
	}
	
	this.setHidden = function(hidden)
	{
		this.hidden = hidden;
	}
	
	this.getDomRoot = function()
	{
		return this.dom_root;
	}
}
// A collection of form fields
var Fieldset = function(id)
{
	this.ref_parent;
	this.dom_root = createElement('div', 'fieldset');
  if(id)
  {
    this.dom_root.id=id;
  }
  
	this.fields = new Array();
	
	this.addField = function(field)
	{
		this.fields[this.fields.length] = field;
	}
	this.addFields = function(fields)
	{
		for(name in fields)
		{
			var fieldData = fields[name];

		try
		{
			if(typeof(fieldData.type) == 'undefined')
			{
				continue;
			}
			eval('var field = new '+fieldData.type+'();');
		}
		catch(e)
		{
			notifier.notify('error','Unknown form field');
			continue;
		}

		field.setLabel(fieldData.label);
		field.setName(name);
		field.setID(name);
		if(fieldData.disabled)
		{
			field.disable();
		}

		if(fieldData.required)
		{
			field.setRequired(true);
		}
		
		if(fieldData.mode)
		{
			field.setMode(fieldData.mode);
		}
		
		if(fieldData.hint)
		{
			field.setHint(fieldData.hint);
		}
		
		if(fieldData.overlabel)
		{
			field.setOverlabel(fieldData.overlabel);
		}
		
		if(fieldData.tabOrder !== undefined)
		{
			field.setTabOrder(fieldData.tabOrder);
		}
		
		// Select boxes, buttons and date fields need special treatment
		switch(fieldData.type)
		{
			case 'AutoSelectField':
			{
				if(fieldData.editinplace)
				{
					field.setEditInPlace(true);
				}
				field.setDescription (fieldData.description);
				field.setMode('local');
				eval('field.setCache('+fieldData.cache+')');
				if(fieldData.value!=null)
				{
					field.setValue(fieldData.value);
				}
				this.addField(field);
				break;
			}
			case 'ContainerField':
			{
				if(fieldData.id != undefined)
				{
					field.setID(fieldData.id);
				}
				this.addField(field);
				break;
			}
			case 'NumberField':
			{
				if(fieldData.precision != undefined)
				{
					field.setPrecision(fieldData.precision);
				}
				if(fieldData.prefix != undefined)
				{
					field.setPrefix(fieldData.prefix);
				}
				field.setValue(fieldData.value);
				field.setDescription (fieldData.description);
				if(fieldData.hidden != undefined)
				{
					field.setHidden(fieldData.hidden);
				}
				else
				{
					field.setHidden(false);
				}
				this.addField(field);
				break;
			}
			case 'SelectField':
			{
				field.setMode (fieldData.mode);
				field.setOptions (fieldData.options);
				field.setSelected (fieldData.selectedOption);
				field.setDescription (fieldData.description);
				if(fieldData.hook_select)
				{
					field.hook_select = fieldData.hook_select;
				}
				if(fieldData.hidden != undefined)
				{
					field.setHidden(fieldData.hidden);
				}
				else
				{
					field.setHidden(false);
				}
				this.addField(field);
				break;
			}
			case 'RadioField':
			{
				field.setValue(fieldData.value);
				field.setOptions(fieldData.options);
				field.setDescription(fieldData.description);
				if(fieldData.hook_select)
				{
					field.hook_select = fieldData.hook_select;
				}
				this.addField(field);
				break;
			}
			case 'FileField':
			{
				if(fieldData.orginalname)
				{
					field.setOriginalName(fieldData.orginalname);
				}
				field.setValue(fieldData.value);
				field.setDescription (fieldData.description);
				this.addField(field);
				break;
			}
			case 'ButtonField':
			{
				field.setOnclick (fieldData.onclick);
				field.setValue (fieldData.value);
				field.setDescription (fieldData.description);
				if(fieldData.mode != undefined)
				{
					field.setMode(fieldData.mode);
				}
				if(fieldData.showAtCurrent)
				{
					this.addField(field);
				}
				else
				{
					this.ref_parent.buttons[this.ref_parent.buttons.length] = field;
				}
				break;
			}
			case 'CheckboxField':
			{
				if(fieldData.hook_change)
				{
					field.hook_change = fieldData.hook_change;
				}
				field.setValue(fieldData.value);
				field.setDescription (fieldData.description);
				if(fieldData.hidden != undefined)
				{
					field.setHidden(fieldData.hidden);
				}
				else
				{
					field.setHidden(false);
				}
				this.addField(field);
				break;
			}
			case 'DateField':
			{
				if(fieldData.dateType != undefined)
				{
					field.setDateType(fieldData.dateType);
				}
				if(fieldData.minimum != undefined)
				{
					field.setMinimum(fieldData.minimum);
				}
				field.setDescription(fieldData.description);
				field.setValue(fieldData.value);
				if(fieldData.hidden != undefined)
				{
					field.setHidden(fieldData.hidden);
				}
				else
				{
					field.setHidden(false);
				}
				this.addField(field);
				break;
			}
			case 'SeperatedDateField':
			{
				if(fieldData.dateType != undefined)
				{
					field.setDateType(fieldData.dateType);
				}
				if(fieldData.yearType != undefined)
				{
					field.setYearType(fieldData.yearType);
				}
				if(fieldData.minimum != undefined)
				{
					field.setMinimum(fieldData.minimum);
				}
				field.setDescription(fieldData.description);
				if(fieldData.hidden != undefined)
				{
					field.setHidden(fieldData.hidden);
				}
				else
				{
					field.setHidden(false);
				}
				
				if(fieldData.mode !== undefined)
				{
					field.setMode(fieldData.mode);
				}
				
				if(fieldData.dateTabOrder !== undefined)
				{
					field.setDateTabOrder(fieldData.dateTabOrder);
				}
				
				if(fieldData.timeTabOrder !== undefined)
				{
					field.setTimeTabOrder(fieldData.timeTabOrder);
				}
				
				if(fieldData.dayTabOrder !== undefined)
				{
					field.setDayOrder(fieldData.dayTabOrder);
				}
				
				if(fieldData.monthTabOrder !== undefined)
				{
					field.setMonthOrder(fieldData.monthTabOrder);
				}
				
				if(fieldData.yearTabOrder !== undefined)
				{
					field.setYearOrder(fieldData.yearTabOrder);
				}
				
				if(fieldData.hourTabOrder !== undefined)
				{
					field.setHourOrder(fieldData.hourTabOrder);
				}
				
				if(fieldData.minuteTabOrder !== undefined)
				{
					field.setMinuteOrder(fieldData.minuteTabOrder);
				}
				
				field.setValue(fieldData.value);
				
				this.addField(field);
				break;
			}
			default:
			{
				if(fieldData.value!=null)
				{
					field.setValue(fieldData.value);
				}
				field.setDescription (fieldData.description);

				if(fieldData.hidden != undefined)
				{
					field.setHidden(fieldData.hidden);
				}
				else
				{
					field.setHidden(false);
				}
				this.addField(field);
				break;
			}
		}
	}
    // redraw fieldset if it is already drawn to add fields
    this.drawFields();
	}
	
	this.getField = function(name)
	{
		for(field in this.fields)
		{
			if(this.fields[field].getName() == name)
			{
				return this.fields[field];
			}
		}
		return false;
	}
	
	this.getFields = function()
	{
		return this.fields;
	}
	
	this.removeField = function(name)
	{
		for(field in this.fields)
		{
			if(this.fields[field].getName() == name)
			{
				this.fields[field] = null;
				return true;
			}
		}
		return false;
	}

  this.drawFields = function()
  {
    removeAllChildren(this.dom_root);
    for(field in this.fields)
    {
			if(typeof this.fields[field] == 'function') continue;
			
      if(this.fields[field].dom_root.className == 'PasswordField')
      {
        if(this.ref_parent)
        {
          this.fields[field].hook_submit = this.ref_parent.submit;
        }
      }
      this.dom_root.appendChild(this.fields[field].draw());
    }
    this.dom_root.appendChild(createElement('div','clear'));
  }
	
	this.draw = function()
	{
    this.drawFields();
		return this.dom_root;
	}
	
	this.showField = function(name)
	{
		for(var field in this.fields)
		{
			if(this.fields[field].getName() == name)
			{
				// Found the field
				this.fields[field].setHidden(false);
				return true;
			}
		}
		return false;
	}
	
	this.hideField = function(name)
	{
		for(var field in this.fields)
		{
			if(this.fields[field].getName() == name)
			{
				// Found the field
				this.fields[field].setHidden(true);
				return true;
			}
		}
		return false;
	}
}
var TextField = function()
{
	var ref = this;
		
	this.overlabel = '';
	this.dom_label = createElement('div', 'label');
	this.dom_root = createElement('div', 'TextField');
	this.dom_input = createElement('input');
	this.dom_hint = createElement('div','overlabel');
	this.dom_description = createElement('div','description');
	this.hint = '';
	
	this.getOverlabel = function()
	{
		return this.overlabel;
	}
	
	this.setOverlabel = function(overlabel)
	{
		this.overlabel = overlabel;
	}
	
	this.hook_submit = function()
	{
		// This function is required, even though it is empty. Do not remove it.
	}
	
	this.onkeydown = function(event)
	{
		var keynum;
		if(!event) // IE (doesnt pass event object, but uses global window.event object instead)
		{
			keynum = window.event.keyCode;
		}
		else if(event.which) // Netscape/Firefox/Opera
		{
			keynum = event.which;
		}
		if(keynum == 13)
		{
			ref.hook_submit();
			return false;
		}
		return true;
	}
	
	this.draw = function()
	{
		if(this.getHidden())
		{
			return createElement('div', 'hiddenControl');
		}
		this.dom_label.innerHTML = this.label;
		this.dom_input.type = 'text';
		this.dom_input.name = this.name;
		this.dom_input.value = this.value;
		if(this.getTabOrder() !== null && this.getTabOrder() !== undefined)
		{
			this.dom_input.tabIndex = this.getTabOrder();
		}
		this.dom_input.onkeydown = this.onkeydown;
		
		
		this.dom_input.onchange = function()
		{
			ref.setValue(this.value);
		}
		
		if(this.description)
		{
			this.dom_description.innerHTML = this.description;
		}
		
		this.dom_hint.innerHTML=this.overlabel;
		this.dom_hint.className = 'overlabel-apply';
		
		if (this.dom_input.value !== '') {
			this.dom_hint.style.display == 'none';
		}
    // Set handlers to show and hide labels.
		this.dom_input.onfocus = function () {
			ref.dom_hint.style.display = 'none';
			return false;
		};
		
		this.dom_input.onblur = function () {
			if (this.value === '') {
				ref.dom_hint.style.display = 'block';
			}
			return false;
		};

		// Handle clicks to label elements.
		this.dom_hint.onclick = function () {
			ref.dom_input.focus();
		};
		
		this.dom_root.appendChild(this.dom_label);
		this.dom_root.appendChild(this.dom_hint);
		this.dom_root.appendChild(this.dom_input);
		this.dom_root.appendChild(this.dom_description);
		
		return this.dom_root;
	}
	
	this.setValue = function(value)
	{
		value = ''+value;
		this.value = value.replace(/\%/g,'&#37;'); // replace percent signs - to prevent malformed string error in decodeURIComponent()
		this.value = decodeURIComponent(this.value);
		this.value = Html2Ascii(this.value);
		this.dom_input.value = this.value;
	}
	this.getValue = function()
	{		
		var value = this.dom_input.value;
		value = ''+value;
		value = value.replace(/\%/g,'&#37;'); // percent signs are used for funny characters in IE
		value = encodeURIComponent(value);
		return value;
	}
	
	this.disable = function()
	{
		this.dom_input.disabled = true;
		this.dom_input.className = 'disabled';
	}
	
	this.enable = function()
	{
		this.dom_input.disabled = false;
		this.dom_input.className = 'enabled';
	}
}
TextField.prototype = new FormField();
/*!	SWFObject v2.0 <http://code.google.com/p/swfobject/>
	Copyright (c) 2007 Geoff Stearns, Michael Williams, and Bobby van der Sluis
	This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/

var swfobject = function() {
	
	var UNDEF = "undefined",
		OBJECT = "object",
		SHOCKWAVE_FLASH = "Shockwave Flash",
		SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
		FLASH_MIME_TYPE = "application/x-shockwave-flash",
		EXPRESS_INSTALL_ID = "SWFObjectExprInst",
		
		win = window,
		doc = document,
		nav = navigator,
		
		domLoadFnArr = [],
		regObjArr = [],
		timer = null,
		storedAltContent = null,
		storedAltContentId = null,
		isDomLoaded = false,
		isExpressInstallActive = false;
	
	/* Centralized function for browser feature detection
		- Proprietary feature detection (conditional compiling) is used to detect Internet Explorer's features
		- User agent string detection is only used when no alternative is possible
		- Is executed directly for optimal performance
	*/	
	var ua = function() {
		var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF && typeof doc.appendChild != UNDEF && typeof doc.replaceChild != UNDEF && typeof doc.removeChild != UNDEF && typeof doc.cloneNode != UNDEF,
			playerVersion = [0,0,0],
			d = null;
		if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
			d = nav.plugins[SHOCKWAVE_FLASH].description;
			if (d) {
				d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
				playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
				playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
				playerVersion[2] = /r/.test(d) ? parseInt(d.replace(/^.*r(.*)$/, "$1"), 10) : 0;
			}
		}
		else if (typeof win.ActiveXObject != UNDEF) {
			var a = null, fp6Crash = false;
			try {
				a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".7");
			}
			catch(e) {
				try { 
					a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".6");
					playerVersion = [6,0,21];
					a.AllowScriptAccess = "always";  // Introduced in fp6.0.47
				}
				catch(e) {
					if (playerVersion[0] == 6) {
						fp6Crash = true;
					}
				}
				if (!fp6Crash) {
					try {
						a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
					}
					catch(e) {}
				}
			}
			if (!fp6Crash && a) { // a will return null when ActiveX is disabled
				try {
					d = a.GetVariable("$version");  // Will crash fp6.0.21/23/29
					if (d) {
						d = d.split(" ")[1].split(",");
						playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
				catch(e) {}
			}
		}
		var u = nav.userAgent.toLowerCase(),
			p = nav.platform.toLowerCase(),
			webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
			ie = false,
			windows = p ? /win/.test(p) : /win/.test(u),
			mac = p ? /mac/.test(p) : /mac/.test(u);
		/*@cc_on
			ie = true;
			@if (@_win32)
				windows = true;
			@elif (@_mac)
				mac = true;
			@end
		@*/
		return { w3cdom:w3cdom, pv:playerVersion, webkit:webkit, ie:ie, win:windows, mac:mac };
	}();

	/* Cross-browser onDomLoad
		- Based on Dean Edwards' solution: http://dean.edwards.name/weblog/2006/06/again/
		- Will fire an event as soon as the DOM of a page is loaded (supported by Gecko based browsers - like Firefox -, IE, Opera9+, Safari)
	*/ 
	var onDomLoad = function() {
		if (!ua.w3cdom) {
			return;
		}
		addDomLoadEvent(main);
		if (ua.ie && ua.win) {
			try {  // Avoid a possible Operation Aborted error
				doc.write("<scr" + "ipt id=__ie_ondomload defer=true src=//:></scr" + "ipt>"); // String is split into pieces to avoid Norton AV to add code that can cause errors 
				var s = getElementById("__ie_ondomload");
				if (s) {
					s.onreadystatechange = function() {
						if (this.readyState == "complete") {
							this.parentNode.removeChild(this);
							callDomLoadFunctions();
						}
					};
				}
			}
			catch(e) {}
		}
		if (ua.webkit && typeof doc.readyState != UNDEF) {
			timer = setInterval(function() { if (/loaded|complete/.test(doc.readyState)) { callDomLoadFunctions(); }}, 10);
		}
		if (typeof doc.addEventListener != UNDEF) {
			doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, null);
		}
		addLoadEvent(callDomLoadFunctions);
	}();
	
	function callDomLoadFunctions() {
		if (isDomLoaded) {
			return;
		}
		if (ua.ie && ua.win) { // Test if we can really add elements to the DOM; we don't want to fire it too early
			var s = createElement("span");
			try { // Avoid a possible Operation Aborted error
				var t = doc.getElementsByTagName("body")[0].appendChild(s);
				t.parentNode.removeChild(t);
			}
			catch (e) {
				return;
			}
		}
		isDomLoaded = true;
		if (timer) {
			clearInterval(timer);
			timer = null;
		}
		var dl = domLoadFnArr.length;
		for (var i = 0; i < dl; i++) {
			domLoadFnArr[i]();
		}
	}
	
	function addDomLoadEvent(fn) {
		if (isDomLoaded) {
			fn();
		}
		else { 
			domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
		}
	}
	
	/* Cross-browser onload
		- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
		- Will fire an event as soon as a web page including all of its assets are loaded 
	 */
	function addLoadEvent(fn) {
		if (typeof win.addEventListener != UNDEF) {
			win.addEventListener("load", fn, false);
		}
		else if (typeof doc.addEventListener != UNDEF) {
			doc.addEventListener("load", fn, false);
		}
		else if (typeof win.attachEvent != UNDEF) {
			win.attachEvent("onload", fn);
		}
		else if (typeof win.onload == "function") {
			var fnOld = win.onload;
			win.onload = function() {
				fnOld();
				fn();
			};
		}
		else {
			win.onload = fn;
		}
	}
	
	/* Main function
		- Will preferably execute onDomLoad, otherwise onload (as a fallback)
	*/
	function main() { // Static publishing only
		var rl = regObjArr.length;
		for (var i = 0; i < rl; i++) { // For each registered object element
			var id = regObjArr[i].id;
			if (ua.pv[0] > 0) {
				var obj = getElementById(id);
				if (obj) {
					regObjArr[i].width = obj.getAttribute("width") ? obj.getAttribute("width") : "0";
					regObjArr[i].height = obj.getAttribute("height") ? obj.getAttribute("height") : "0";
					if (hasPlayerVersion(regObjArr[i].swfVersion)) { // Flash plug-in version >= Flash content version: Houston, we have a match!
						if (ua.webkit && ua.webkit < 312) { // Older webkit engines ignore the object element's nested param elements
							fixParams(obj);
						}
						setVisibility(id, true);
					}
					else if (regObjArr[i].expressInstall && !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac)) { // Show the Adobe Express Install dialog if set by the web page author and if supported (fp6.0.65+ on Win/Mac OS only)
						showExpressInstall(regObjArr[i]);
					}
					else { // Flash plug-in and Flash content version mismatch: display alternative content instead of Flash content
						displayAltContent(obj);
					}
				}
			}
			else {  // If no fp is installed, we let the object element do its job (show alternative content)
				setVisibility(id, true);
			}
		}
	}
	
	/* Fix nested param elements, which are ignored by older webkit engines
		- This includes Safari up to and including version 1.2.2 on Mac OS 10.3
		- Fall back to the proprietary embed element
	*/
	function fixParams(obj) {
		var nestedObj = obj.getElementsByTagName(OBJECT)[0];
		if (nestedObj) {
			var e = createElement("embed"), a = nestedObj.attributes;
			if (a) {
				var al = a.length;
				for (var i = 0; i < al; i++) {
					if (a[i].nodeName.toLowerCase() == "data") {
						e.setAttribute("src", a[i].nodeValue);
					}
					else {
						e.setAttribute(a[i].nodeName, a[i].nodeValue);
					}
				}
			}
			var c = nestedObj.childNodes;
			if (c) {
				var cl = c.length;
				for (var j = 0; j < cl; j++) {
					if (c[j].nodeType == 1 && c[j].nodeName.toLowerCase() == "param") {
						e.setAttribute(c[j].getAttribute("name"), c[j].getAttribute("value"));
					}
				}
			}
			obj.parentNode.replaceChild(e, obj);
		}
	}
	
	/* Fix hanging audio/video threads and force open sockets and NetConnections to disconnect
		- Occurs when unloading a web page in IE using fp8+ and innerHTML/outerHTML
		- Dynamic publishing only
	*/
	function fixObjectLeaks(id) {
		if (ua.ie && ua.win && hasPlayerVersion("8.0.0")) {
			win.attachEvent("onunload", function () {
				var obj = getElementById(id);
				if (obj) {
					for (var i in obj) {
						if (typeof obj[i] == "function") {
							obj[i] = function() {};
						}
					}
					obj.parentNode.removeChild(obj);
				}
			});
		}
	}
	
	/* Show the Adobe Express Install dialog
		- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
	*/
	function showExpressInstall(regObj) {
		isExpressInstallActive = true;
		var obj = getElementById(regObj.id);
		if (obj) {
			if (regObj.altContentId) {
				var ac = getElementById(regObj.altContentId);
				if (ac) {
					storedAltContent = ac;
					storedAltContentId = regObj.altContentId;
				}
			}
			else {
				storedAltContent = abstractAltContent(obj);
			}
			if (!(/%$/.test(regObj.width)) && parseInt(regObj.width, 10) < 310) {
				regObj.width = "310";
			}
			if (!(/%$/.test(regObj.height)) && parseInt(regObj.height, 10) < 137) {
				regObj.height = "137";
			}
			doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
			var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
				dt = doc.title,
				fv = "MMredirectURL=" + win.location + "&MMplayerType=" + pt + "&MMdoctitle=" + dt,
				replaceId = regObj.id;
			// For IE when a SWF is loading (AND: not available in cache) wait for the onload event to fire to remove the original object element
			// In IE you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
			if (ua.ie && ua.win && obj.readyState != 4) {
				var newObj = createElement("div");
				replaceId += "SWFObjectNew";
				newObj.setAttribute("id", replaceId);
				obj.parentNode.insertBefore(newObj, obj); // Insert placeholder div that will be replaced by the object element that loads expressinstall.swf
				obj.style.display = "none";
				win.attachEvent("onload", function() { obj.parentNode.removeChild(obj); });
			}
			createSWF({ data:regObj.expressInstall, id:EXPRESS_INSTALL_ID, width:regObj.width, height:regObj.height }, { flashvars:fv }, replaceId);
		}
	}
	
	/* Functions to abstract and display alternative content
	*/
	function displayAltContent(obj) {
		if (ua.ie && ua.win && obj.readyState != 4) {
			// For IE when a SWF is loading (AND: not available in cache) wait for the onload event to fire to remove the original object element
			// In IE you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
			var el = createElement("div");
			obj.parentNode.insertBefore(el, obj); // Insert placeholder div that will be replaced by the alternative content
			el.parentNode.replaceChild(abstractAltContent(obj), el);
			obj.style.display = "none";
			win.attachEvent("onload", function() { obj.parentNode.removeChild(obj); });
		}
		else {
			obj.parentNode.replaceChild(abstractAltContent(obj), obj);
		}
	}	

	function abstractAltContent(obj) {
		var ac = createElement("div");
		if (ua.win && ua.ie) {
			ac.innerHTML = obj.innerHTML;
		}
		else {
			var nestedObj = obj.getElementsByTagName(OBJECT)[0];
			if (nestedObj) {
				var c = nestedObj.childNodes;
				if (c) {
					var cl = c.length;
					for (var i = 0; i < cl; i++) {
						if (!(c[i].nodeType == 1 && c[i].nodeName.toLowerCase() == "param") && !(c[i].nodeType == 8)) {
							ac.appendChild(c[i].cloneNode(true));
						}
					}
				}
			}
		}
		return ac;
	}
	
	/* Cross-browser dynamic SWF creation
	*/
	function createSWF(attObj, parObj, id) {
		var r, el = getElementById(id);
		if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
			attObj.id = id;
		}
		if (ua.ie && ua.win) { // IE, the object element and W3C DOM methods do not combine: fall back to outerHTML
			var att = "";
			for (var i in attObj) {
				if (attObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries, like Object.prototype.toJSONString = function() {}
					if (i == "data") {
						parObj.movie = attObj[i];
					}
					else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
						att += ' class="' + attObj[i] + '"';
					}
					else if (i != "classid") {
						att += ' ' + i + '="' + attObj[i] + '"';
					}
				}
			}
			var par = "";
			for (var j in parObj) {
				if (parObj[j] != Object.prototype[j]) { // Filter out prototype additions from other potential libraries
					par += '<param name="' + j + '" value="' + parObj[j] + '" />';
				}
			}
			el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
			fixObjectLeaks(attObj.id); // This bug affects dynamic publishing only
			r = getElementById(attObj.id);	
		}
		else if (ua.webkit && ua.webkit < 312) { // Older webkit engines ignore the object element's nested param elements: fall back to the proprietary embed element
			var e = createElement("embed");
			e.setAttribute("type", FLASH_MIME_TYPE);
			for (var k in attObj) {
				if (attObj[k] != Object.prototype[k]) { // Filter out prototype additions from other potential libraries
					if (k == "data") {
						e.setAttribute("src", attObj[k]);
					}
					else if (k.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
						e.setAttribute("class", attObj[k]);
					}
					else if (k != "classid") { // Filter out IE specific attribute
						e.setAttribute(k, attObj[k]);
					}
				}
			}
			for (var l in parObj) {
				if (parObj[l] != Object.prototype[l]) { // Filter out prototype additions from other potential libraries
					if (l != "movie") { // Filter out IE specific param element
						e.setAttribute(l, parObj[l]);
					}
				}
			}
			el.parentNode.replaceChild(e, el);
			r = e;
		}
		else { // Well-behaving browsers
			var o = createElement(OBJECT);
			o.setAttribute("type", FLASH_MIME_TYPE);
			for (var m in attObj) {
				if (attObj[m] != Object.prototype[m]) { // Filter out prototype additions from other potential libraries
					if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
						o.setAttribute("class", attObj[m]);
					}
					else if (m != "classid") { // Filter out IE specific attribute
						o.setAttribute(m, attObj[m]);
					}
				}
			}
			for (var n in parObj) {
				if (parObj[n] != Object.prototype[n] && n != "movie") { // Filter out prototype additions from other potential libraries and IE specific param element
					createObjParam(o, n, parObj[n]);
				}
			}
			el.parentNode.replaceChild(o, el);
			r = o;
		}
		return r;
	}
	
	function createObjParam(el, pName, pValue) {
		var p = createElement("param");
		p.setAttribute("name", pName);	
		p.setAttribute("value", pValue);
		el.appendChild(p);
	}
	
	function getElementById(id) {
		return doc.getElementById(id);
	}
	
	function createElement(el) {
		return doc.createElement(el);
	}
	
	function hasPlayerVersion(rv) {
		var pv = ua.pv, v = rv.split(".");
		v[0] = parseInt(v[0], 10);
		v[1] = parseInt(v[1], 10);
		v[2] = parseInt(v[2], 10);
		return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
	}
	
	/* Cross-browser dynamic CSS creation
		- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
	*/	
	function createCSS(sel, decl) {
		if (ua.ie && ua.mac) {
			return;
		}
		var h = doc.getElementsByTagName("head")[0], s = createElement("style");
		s.setAttribute("type", "text/css");
		s.setAttribute("media", "screen");
		if (!(ua.ie && ua.win) && typeof doc.createTextNode != UNDEF) {
			s.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
		}
		h.appendChild(s);
		if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
			var ls = doc.styleSheets[doc.styleSheets.length - 1];
			if (typeof ls.addRule == OBJECT) {
				ls.addRule(sel, decl);
			}
		}
	}
	
	function setVisibility(id, isVisible) {
		var v = isVisible ? "visible" : "hidden";
		if (isDomLoaded) {
			getElementById(id).style.visibility = v;
		}
		else {
			createCSS("#" + id, "visibility:" + v);
		}
	}
	
	return {
		/* Public API
			- Reference: http://code.google.com/p/swfobject/wiki/SWFObject_2_0_documentation
		*/ 
		registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr) {
			if (!ua.w3cdom || !objectIdStr || !swfVersionStr) {
				return;
			}
			var regObj = {};
			regObj.id = objectIdStr;
			regObj.swfVersion = swfVersionStr;
			regObj.expressInstall = xiSwfUrlStr ? xiSwfUrlStr : false;
			regObjArr[regObjArr.length] = regObj;
			setVisibility(objectIdStr, false);
		},
		
		getObjectById: function(objectIdStr) {
			var r = null;
			if (ua.w3cdom && isDomLoaded) {
				var o = getElementById(objectIdStr);
				if (o) {
					var n = o.getElementsByTagName(OBJECT)[0];
					if (!n || (n && typeof o.SetVariable != UNDEF)) {
				    	r = o;
					}
					else if (typeof n.SetVariable != UNDEF) {
						r = n;
					}
				}
			}
			return r;
		},
		
		embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj) {
			if (!ua.w3cdom || !swfUrlStr || !replaceElemIdStr || !widthStr || !heightStr || !swfVersionStr) {
				return;
			}
			widthStr += ""; // Auto-convert to string to make it idiot proof
			heightStr += "";
			if (hasPlayerVersion(swfVersionStr)) {
				setVisibility(replaceElemIdStr, false);
				var att = (typeof attObj == OBJECT) ? attObj : {};
				att.data = swfUrlStr;
				att.width = widthStr;
				att.height = heightStr;
				var par = (typeof parObj == OBJECT) ? parObj : {};
				if (typeof flashvarsObj == OBJECT) {
					for (var i in flashvarsObj) {
						if (flashvarsObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries
							if (typeof par.flashvars != UNDEF) {
								par.flashvars += "&" + i + "=" + flashvarsObj[i];
							}
							else {
								par.flashvars = i + "=" + flashvarsObj[i];
							}
						}
					}
				}
				addDomLoadEvent(function() {
					createSWF(att, par, replaceElemIdStr);
					if (att.id == replaceElemIdStr) {
						setVisibility(replaceElemIdStr, true);
					}
				});
			}
			else if (xiSwfUrlStr && !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac)) {
				setVisibility(replaceElemIdStr, false);
				addDomLoadEvent(function() {
					var regObj = {};
					regObj.id = regObj.altContentId = replaceElemIdStr;
					regObj.width = widthStr;
					regObj.height = heightStr;
					regObj.expressInstall = xiSwfUrlStr;
					showExpressInstall(regObj);
				});
			}
		},
		
		getFlashPlayerVersion: function() {
			return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
		},
		
		hasFlashPlayerVersion:hasPlayerVersion,
		
		createSWF: function(attObj, parObj, replaceElemIdStr) {
			if (ua.w3cdom && isDomLoaded) {
				return createSWF(attObj, parObj, replaceElemIdStr);
			}
			else {
				return undefined;
			}
		},
		
		createCSS: function(sel, decl) {
			if (ua.w3cdom) {
				createCSS(sel, decl);
			}
		},
		
		addDomLoadEvent:addDomLoadEvent,
		
		addLoadEvent:addLoadEvent,
		
		getQueryParamValue: function(param) {
			var q = doc.location.search || doc.location.hash;
			if (param == null) {
				return q;
			}
		 	if(q) {
				var pairs = q.substring(1).split("&");
				for (var i = 0; i < pairs.length; i++) {
					if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
						return pairs[i].substring((pairs[i].indexOf("=") + 1));
					}
				}
			}
			return "";
		},
		
		// For internal usage only
		expressInstallCallback: function() {
			if (isExpressInstallActive && storedAltContent) {
				var obj = getElementById(EXPRESS_INSTALL_ID);
				if (obj) {
					obj.parentNode.replaceChild(storedAltContent, obj);
					if (storedAltContentId) {
						setVisibility(storedAltContentId, true);
						if (ua.ie && ua.win) {
							storedAltContent.style.display = "block";
						}
					}
					storedAltContent = null;
					storedAltContentId = null;
					isExpressInstallActive = false;
				}
			} 
		}
		
	};

}();
function PopupLink()
{
	/**
	 * The text for the link. If no image is specified, this text will be displayed, otherwise it will be the alternate text for the image.
	 */
	this._text;
	
	/**
	 * The URL of an image to display for the link. If this is not specified, the link text will be displayed instead of this image.
	 */
	this._imageSource;
	
	/**
	 * The URL the popup window should load.
	 */
	this._url;
	
	this.dom_root;
	this.dom_message;
	
	this.getText = function()
	{
		return this._text;
	}
	
	this.setText = function(text)
	{
		this._text = text;
	}
	
	this.getImageSource = function()
	{
		return this._imageSource;
	}
	
	this.setImageSource = function(imageSource)
	{
		this._imageSource = imageSource;
	}
	
	this.getUrl = function()
	{
		return this._url;
	}
	
	this.setUrl = function(url)
	{
		this._url = url;
	}
	
	this.draw = function()
	{
		try
		{

		this.dom_root = createElement('div', 'popupLink');
		this.dom_message = createElement('div','popupMessage');
		this.dom_message.innerHTML = this.getText();
		this.dom_message.style.display = 'none';
		this.dom_message.style.position = 'absolute';
		this.dom_root.appendChild(this.dom_message);
		var dom_link = createElement('a', 'popupLink');
		dom_link.href = '#';
		var ref = this;
		dom_link.onmouseover = function()
		{
			ref.dom_message.style.display = 'block';
			return false;
		}
		dom_link.onmouseout = function()
		{
			ref.dom_message.style.display = 'none';
			return false;
		}
		if(this.getImageSource())
		{
			var dom_linkimage = createElement('img', 'popupImage');
			dom_linkimage.src = this.getImageSource();
			dom_linkimage.alt = this.getText();
			
			// Set a blank title on the image, or IE will "helpfully" display the alt text as a browser tooltip.
			dom_linkimage.title = '';
			dom_link.appendChild(dom_linkimage);
		}
		else
		{
			dom_link.appendChild(document.createTextNode(this.getText()));
		}
		this.dom_root.appendChild(dom_link);
		}
		catch(e)
		{
alert(e);
		}
		return this.dom_root;
	}
}
/**
 * Base Dom Model class. Used as a base class for other classes/widgets.
 */
var DomModel = function()
{
	this.dom_root; //Root of this object in the DOM
	this.mode;
	this.children = new Object();
	
	
	this.setMode = function(mode) { this.mode = mode; }	
	this.getMode = function() { return this.mode; }	
	
	this.setDomRoot = function(dom_root)
	{
		this.dom_root = dom_root;
	}
	
	this.getDomRoot = function()
	{
		return this.dom_root;
	}
	
	this.setChild = function(name, obj)
	{
		this.children[name] = obj;
	}
	this.setChildren = function(children)
	{
		this.children = children;
	}
	
	this.getChild = function(name)
	{
		return this.children[name];
	}
	this.getChildren = function()
	{
		return this.children;
	}
	
	this.countChildren = function()
	{
		var x = 0;
		for(key in this.getChildren())
		{
			x++;
		}
		return x;
	}
	
	//clear all children of a DomModel object
	this.clear = function()
	{
		this.children = new Object();
		if(this.getDomRoot())
		{
			while(this.getDomRoot().hasChildNodes())
			{
				this.getDomRoot().removeChild(this.getDomRoot().firstChild);
			}
		}
	}
	
	
	//clear a child of a DomModel object by name/ref
	this.clearChild = function(name)
	{
		// remove all children
		if(this.getChild(name))
		{
			this.getChild(name).clear();
			this.getDomRoot().removeChild(this.getChild(name).getDomRoot());
			this.children[name] = null;
			
		}
		
	}
	
/****
	 * Get the current rendered offsets of a dom element.
	 * NOTE: Only works if the element has ALREADY BEEN RENDERED!
	 * return: Object => ["X"], ["Y"]
	 */
	this.getCurrentOffsets = function()
	{
		var domelement = this.getDomRoot();
		var offsetX = offsetY = 0;
		if (domelement.offsetParent) {
			offsetX = domelement.offsetLeft
			offsetY = domelement.offsetTop
			while (domelement = domelement.offsetParent) {
				offsetX += domelement.offsetLeft
				offsetY += domelement.offsetTop
			}
		}
		var offsets = new Object();
		offsets['X'] = offsetX;
		offsets['Y'] = offsetY;
		return offsets;
	}
}

/**
 * Part request page and functionality
 */
var partrequest = function()
{
	var ref = this;
	this.optionsCache = new Object();
	this.vehicle = new Object();
	this.constructCacheQuery =function(name, value)
	{
		var query = '';
		switch(name)
		{
			case 'vehicle_trim':
			case 'vehicle_body':
			{
				query = this.vehicle.vehicle_body +'-'+ query;
			}
			case 'vehicle_year':
			{
				query = this.vehicle.vehicle_year +'-'+ query;
			}
			case 'vehicle_model':
			{
				query = this.vehicle.vehicle_model +'-'+ query;
			}
			case 'vehicle_make':
			{
				query = this.vehicle.vehicle_make +'-'+ query;
			}
		}
		return query.substr(0, query.length-1).toLowerCase();
	}
	
	/**
	 * Get all current values of the form, as an object that can be easily
	 */
	this.getFormValues = function()
	{
		/*
		yourplate
		vehicle_make
		vehicle_model
		vehicle_year
		vehicle_body
		vehicle_trim

		//possibly multiple...
		Part Category
		-- a text area.. ?
		yourplate

		//need to set these up
		contact_name
		contact_phone
		contact_email
		contact_email_confirm
		receivetextquotes
		typepostcode
		//address?
		agreeterms
		*/
		var getFieldValue = function(fieldid, type)
		{
			switch(type)
			{
				case 'radio':
				{
					var radios = document.getElementsByName(fieldid);
					if(radios)
					{
						for(var x = 0; x < radios.length; x++)
						{
							if(radios[x].checked == true)
							{
								return radios[x].value;
							}
						}
					}
					break;
				}
				case 'check':
				{
					var fieldEl = document.getElementById(fieldid);
					if(fieldEl)
					{
						if(fieldEl.checked)
						{
							return 1;
						}
						else
						{
							return 0;
						}
					}
					break;
				}
				case 'select':
				{
					var fieldEl = document.getElementById(fieldid);
					if(fieldEl)
					{
						//console.log(fieldEl);
						var opts = fieldEl.options;
						if(fieldEl.selectedIndex >= 0)
						{
							var selectedOpt = opts[fieldEl.selectedIndex];
							if(selectedOpt)	return selectedOpt.value;
						}
					}
					break;
				}
				case 'text':
				case 'hidden':
				default:
				{
					var fieldEl = document.getElementById(fieldid);
					if(fieldEl) return fieldEl.value;
					break;
				}
			}
			
			return '';
			
		}
		var formValues = new Object();
		formValues['vrm'] = getFieldValue('vrm');
		//vehicle select drop downs.
		formValues['vehicle_make'] = getFieldValue('vehicle_make', 'select');
		formValues['vehicle_model'] = getFieldValue('vehicle_model', 'select');
		formValues['vehicle_year'] = getFieldValue('vehicle_year', 'select');
		formValues['vehicle_body'] = getFieldValue('vehicle_body', 'select');
		formValues['vehicle_trim'] = getFieldValue('vehicle_trim', 'select');
		//contact info
		formValues['contact_name'] = getFieldValue('contact_name');
		formValues['contact_phone'] = getFieldValue('contact_phone');
		formValues['contact_email'] = getFieldValue('contact_email');
		formValues['contact_email_confirm'] = getFieldValue('contact_email_confirm');
		formValues['contact_location'] = getFieldValue('contact_location');
		//hidden postcode fields
		formValues['postcode_postcode'] = getFieldValue('postcode_postcode');
		formValues['postcode_town'] = getFieldValue('postcode_town');
		formValues['postcode_county'] = getFieldValue('postcode_county');
		
		//SMS radio field...
		formValues['contact_sms'] = getFieldValue('contact_sms', 'radio');
		
		formValues['agreeterms'] = getFieldValue('agreeterms', 'check');
		
		return formValues;
	}
	/** 
	 * Save current form state
	 */
	this.saveFormState = function()
	{
		var formvals = ref.getFormValues();
		formvals = toJSONString(formvals);
		var ticket = requestProcess.add(new Request('POST', '/api/partrequest/saveformstate', 'formValues='+formvals, function(){}, function(){}));
		//var ticket = requestProcess.add(new Request('POST', '/api/partrequest/saveFormState','', onsave, function(){}));
	}
}
/**
 * Vehicle
 */
partrequest.prototype.vehicle = function()
{
	this.vrm = null;
	this.make = null;
	this.model = null;
	this.year = null;
	this.engine = null;
	this.fuel = null;
	this.gearbox = null;
	this.body = null;
	this.trim = null;

	this.parts = new Array();
}
/**
 * Vehicle part, used for saving parts
 */
partrequest.prototype.part = function()
{
	this.category = null;
	this.notes = null;
	this.image = null;
}
/**
 * Update options in a select list from a remote 
 */
partrequest.prototype.updateSelectOptions = function(name, value, field, callback)
{
	try
	{
	partrequest.vehicle[name] = value;
	var hash = partrequest.constructCacheQuery(name, value);

	if(partrequest.optionsCache[hash] && partrequest.optionsCache[hash][value])
	{
		var dom = document.getElementById(field);
		dom.options.length = 0;
		var options = partrequest.optionsCache[hash][value];
		var i=0;
		for(index in options)
		{
			dom.options[i] = new Option(options[index], index);
			if(index == '')
			{
				dom.options[i].disabled = true;
				dom.options[i].selected = true;
			}
			i++;
		}
		//dom.innerHTML = partrequest.optionsCache[hash][value];
		if(dom.options.length == 1)
		{
			if(field == 'vehicle_model')
			{
				partrequest.updateSelectOptions('vehicle_model', dom.options[0].value, 'vehicle_year');
			}
			if(field == 'vehicle_year')
			{
				partrequest.updateSelectOptions('vehicle_year', dom.options[0].value, 'vehicle_body');
			}
			if(field == 'vehicle_body')
			{
				partrequest.updateSelectOptions('vehicle_body', dom.options[0].value, 'vehicle_trim');
			}
		}
		else
		{
			switch(field)
			{
				case 'vehicle_model':
				{
					document.getElementById('vehicle_year').innerHTML = '<option value=""> - </option>';
				}
				case 'vehicle_year':
				{
					document.getElementById('vehicle_body').innerHTML = '<option value=""> - </option>';
				}
				case 'vehicle_body':
				{
					document.getElementById('vehicle_trim').innerHTML = '<option value=""> - </option>';
				}
			}
			if(callback && typeof callback == 'function') callback();
		}
	}
	else
	{
		partrequest.loadSelectOptions(name, value, field, hash, callback);
	}
	}
	catch(e)
	{

	}
}
/**
 * Load options from remote cached JSON
 */
partrequest.prototype.loadSelectOptions = function(name, value, field, hash, callback)
{
	var onreturn = function(data)
	{
		if(!partrequest.optionsCache[hash])
		{
			partrequest.optionsCache[hash] = new Object();
		}
		if(!partrequest.optionsCache[hash][value])
		{
			partrequest.optionsCache[hash][value] = new Object();
		}
		partrequest.optionsCache[hash][value] = data;
		partrequest.updateSelectOptions(name, value, field);
		
		//run callback after loading..
		if(callback && typeof callback == 'function') callback();
	}
	var ticket = requestProcess.add(new Request('GET', '/res/cache/'+field+'/'+hash.replace('/','-')+'.json','', onreturn, function(){}));
}
partrequest.prototype.saveRequest = function()
{

}
var strtoupper = function(dom)
{
	dom.value = dom.value.toUpperCase();
}
/**
 * Lookup area by postcode, and display a list of selectable areas.
 */
var lookupAreasByPostCode = function(postcode, result_dom)
{
	var onsuccess = function(data)
	{
		removeAllChildren(result_dom);
		var result_dom_inner = createElement('div', 'results');
		var resultCount = 0;
		for(var areakey in data)
		{
			resultCount++;
			var pcl = new PostCodeLocation();
			
			var setAreaText = function(pc, town, county)
			{
				postcodeField.dom_input.value = county+', '+town;
				removeAllChildren(result_dom);
				document.getElementById("postcode_postcode").value = pc;
				document.getElementById("postcode_town").value = town;
				document.getElementById("postcode_county").value = county;
				return false;
			}
			pcl.hook_onselect = setAreaText;
			pcl.setPostCode(data[areakey]['postcode']);
			pcl.setTown(data[areakey]['town']);
			pcl.setCounty(data[areakey]['county']);
			pcl.setDomRoot(createElement('div', 'arearesult'));
			result_dom_inner.appendChild(pcl.draw());
		}
		
		var closeLinkContainer = createElement('div', 'close');
		var closeLink = createElement('a');
		closeLink.href="#";
		closeLink.onclick = function(){removeAllChildren(result_dom); return false;};
		closeLink.innerHTML = "Close";
		closeLinkContainer.appendChild(closeLink);
		result_dom_inner.appendChild(closeLinkContainer);
		//if some results..
		if(resultCount > 0)
		{
			result_dom.appendChild(result_dom_inner);
		}
		else //if no results
		{
			var noresults = createElement('div', 'noresults');
			noresults.innerHTML = 'Please ensure you enter your correct postcode, or clearly describe your location.';
			result_dom.appendChild(noresults);
		}
		
	}
	var onfail = function()
	{
		//fail
		removeAllChildren(result_dom);
	}
	
	//display a loading message
	removeAllChildren(result_dom);
	var loadingresults = createElement('div', 'loadingresults');
	loadingresults.innerHTML = 'Loading....';
	if(result_dom)
	{
	result_dom.appendChild(loadingresults);
	}
	var ticket = requestProcess.add(new Request('POST', '/api/area/getLocationsByPostCode', 'postcode='+postcode, onsuccess, onfail));
	return false;
}

/** 
 * Selectable item in postcode list.
 */
var PostCodeLocation = function()
{
	var ref = this;
	this.postcode;
	this.town;
	this.county;
	this.hook_onselect = function(pc, town, county){return false;};
	
	this.setPostCode = function(postcode)
	{
		this.postcode = postcode;
	}
	this.getPostCode = function()
	{
		return this.postcode;
	}
	this.setTown = function(town)
	{
		this.town = town;
	}
	this.getTown = function()
	{
		return this.town;
	}
	this.setCounty = function(county)
	{
		this.county = county;
	}
	this.getCounty = function()
	{
		return this.county;
	}
	
	this.select = function()
	{
		ref.hook_onselect(ref.getPostCode(), ref.getTown(), ref.getCounty());
		return false;
	}
	
	this.draw = function()
	{
		var link = createElement('a');
		link.href="#";
		link.onclick = this.select;
		link.innerHTML = this.getPostCode()+' - '+this.getTown()+', '+this.getCounty();
		this.getDomRoot().appendChild(link);
		return this.getDomRoot();
	}
}
PostCodeLocation.prototype = new DomModel();

function correctVRM()
{
	if(advancedSearchAttempted)
	{
		//The customer has already had one chance to correct the vehicle's details. Second time round
		//we just clear out any VRM data that might be in the session and display the non-VRM'ed vehicle
		//selector.
		window.location = '?vrm=N&clearVRM=1';
	}
	else
	{
		if(advancedSearchAttempted)
		{
			//The customer has already had one chance to correct the vehicle's details. Second time round
			//we just clear out any VRM data that might be in the session and display the non-VRM'ed vehicle
			//selector.
			window.location = '?vrm=N&clearVRM=1';
		}
		else
		{
			$('#basket-vrm-lookup-dialog .vrm-lookup, #basket-vrm-lookup-dialog .change-vehicle-mode').css('display', 'none');
			$('#basket-vrm-lookup-dialog, #basket-vrm-lookup-dialog .vehicle-details-entry').css('display', 'block');
			
			$('#confirm_vrm').dialog
			(
					{
						bgiframe: true,
						modal: true,
						title: 'Advanced Vehicle Registration Lookup',
						width: 450,
						height: 300,
						buttons:
						{
							'Continue': function()
							{
								// Make sure all fields have been filled in.
								var errors = new Array();
								
								if(!$('#basket-vrm-lookup-dialog #basket-make-entry').val())
								{
									errors.push('Please select your vehicle\'s make');
								}
								
								if(!$('#basket-vrm-lookup-dialog #basket-model-entry').val())
								{
									errors.push('Please select your vehicle\'s model');
								}
								
								if(!$('#basket-vrm-lookup-dialog #basket-year-entry').val())
								{
									errors.push('Please select your vehicle\'s year');
								}
								
								if(!$('#basket-vrm-lookup-dialog #basket-body-entry').val())
								{
									errors.push('Please select your vehicle\'s body');
								}
								
								if(!$('#basket-vrm-lookup-dialog #basket-trim-entry').val())
								{
									errors.push('Please select your vehicle\'s trim');
								}
								
								if(errors.length == 0)
								{
									$('#confirm_vrm').submit();
								}
								else
								{
									var dom_errors = createElement('div', 'errors');
									dom_errors.style.textAlign = 'left';
									
									for(var x = 0; x < errors.length; x++)
									{
										var dom_error = createElement('div', 'errorMessage');
										dom_error.appendChild(document.createTextNode(errors[x]));
										dom_errors.appendChild(dom_error);
									}
									
									$(dom_errors).dialog
									(
										{
											modal: true,
											buttons:
											{
												'OK': function()
												{
													$(this).dialog('close');
												}
											},
											width: 250,
											height: 180,
											bgiframe: true
										}		
									);
								}
							}
						}
					}
			);
			$('#dialog').dialog('close');
		}
	}
}

function initVRMLookupDialog()
{
	$('#basket-vrm-lookup-dialog').dialog
	(
		{
			bgiframe: true,
			buttons:
			{
				'OK': function()
				{	
					var vrmInput = $('#basket-vrm-lookup-dialog .vrm-lookup .plate input').val();
					
					//if vrm has not been changed by user, do live lookup straight away
					if(vrmText == vrmInput)
					{
						vrmStep = 1;
						lookupVRMLive(vrmInput, processPartrequestVRMResult, $(this));
					}
					else	
					{
						vrmStep = 0;
						lookupVRM(vrmInput, processPartrequestVRMResult, $(this));
					}

					$(this).dialog('close');
				},
				'Cancel': function()
				{
					//redirect to VRM input screen?
					$(this).dialog('close');
				}
			},
			modal: true,
			title: 'Vehicle details',
			width: 550,
			autoOpen: false
		}
	);
}

function partrequestIncorrect(vrmText)
{
	//document.getElementsByName('vrm')[0].value = vrmText;
	
	$('#basket-vrm-lookup-dialog').dialog('open');
	
	$('#basket-vrm-lookup-dialog').parent().find('button:first').addClass('continue_part');
	$('#basket-vrm-lookup-dialog').parent().find('button:last').addClass('cancel_part');
	
	if(vrmText != '') 
	{
		$('#basket-vrm-lookup-dialog .vrm-lookup .plate input').val(vrmText);
	}
}

function processPartrequestVRMResult(data, textStatus, dialog)
{
	if(typeof(data.data.content) != 'undefined')
	{
		confirmPartrequestVRMResult(data, dialog);
	}
	else if(typeof(data.data.message) != 'undefined' && data.data.message == 'Not Found')
	{
		//VRM not found.
		$('#basket-vrm-error').text('Sorry, but we cannot find your registration number in our database.');
	}
	else
	{
		//Something's gone wrong.
		$('#basket-vrm-error').text('Sorry, but we are unable to look up your registration number at the moment.');
	}
}

var vrmStep = 0;

function confirmPartrequestVRMResult(data, dialog)
{
	var dom_confirmVRM = createElement('div', 'confirmVRM');
	
	var dom_plate = createElement('div', 'ukplate');
	var dom_ybg = createElement('span', 'ybg');
	dom_ybg.appendChild(document.createTextNode(data.data.vrm));
	dom_plate.appendChild(dom_ybg);
	dom_confirmVRM.appendChild(dom_plate);
	
	var dom_table = document.createElement('table');
	var dom_tbody = document.createElement('tbody');
	
	var dom_tr = document.createElement('tr');
	
	var dom_td = document.createElement('td');
	dom_td.appendChild(document.createTextNode('Make'));
	dom_tr.appendChild(dom_td);
	
	dom_td = createElement('td', 'value');
	dom_td.appendChild(document.createTextNode(data.data.make));
	dom_tr.appendChild(dom_td);
	
	dom_tbody.appendChild(dom_tr);
	
	var dom_tr = document.createElement('tr');
	
	var dom_td = document.createElement('td');
	dom_td.appendChild(document.createTextNode('Model'));
	dom_tr.appendChild(dom_td);
	
	dom_td = createElement('td', 'value');
	dom_td.appendChild(document.createTextNode(data.data.model));
	dom_tr.appendChild(dom_td);
	
	dom_tbody.appendChild(dom_tr);
	
	var dom_tr = document.createElement('tr');
	
	var dom_td = document.createElement('td');
	dom_td.appendChild(document.createTextNode('Colour'));
	dom_tr.appendChild(dom_td);
	
	dom_td = createElement('td', 'value');
	dom_td.appendChild(document.createTextNode(data.data.colour));
	dom_tr.appendChild(dom_td);
	
	dom_tbody.appendChild(dom_tr);
	
	var dom_tr = document.createElement('tr');
	
	var dom_td = document.createElement('td');
	dom_td.appendChild(document.createTextNode('Body type'));
	dom_tr.appendChild(dom_td);
	
	dom_td = createElement('td', 'value');
	dom_td.appendChild(document.createTextNode(data.data.body));
	dom_tr.appendChild(dom_td);
	
	dom_tbody.appendChild(dom_tr);
	
	var dom_tr = document.createElement('tr');
	
	var dom_td = document.createElement('td');
	dom_td.appendChild(document.createTextNode('Engine size'));
	dom_tr.appendChild(dom_td);
	
	dom_td = createElement('td', 'value');
	dom_td.appendChild(document.createTextNode(data.data.engine + ' cc'));
	dom_tr.appendChild(dom_td);
	
	dom_tbody.appendChild(dom_tr);
	
	var dom_tr = document.createElement('tr');
	
	var dom_td = document.createElement('td');
	dom_td.appendChild(document.createTextNode('Year'));
	dom_tr.appendChild(dom_td);
	
	dom_td = createElement('td', 'value');
	dom_td.appendChild(document.createTextNode(data.data.year));
	dom_tr.appendChild(dom_td);
	
	dom_tbody.appendChild(dom_tr);
	
	dom_table.appendChild(dom_tbody);
	dom_confirmVRM.appendChild(dom_table);
	
	if(data.data.source == 'live')
	{
		showManualEntry = true;
	}
	
	if(vrmStep == 0)
	{
		$(dom_confirmVRM).dialog
		(
			{
				bgiframe: true,
				buttons:
				{
					'Yes, correct': function()
					{
						$(this).dialog('close');
						
						$('.part-from-price').hide();
						dialog.dialog('close');
						
						updateVehiclePreview(data);
						
						// We should also send off another AJAX request to indicate to the server that the
						// customer has already confirmed the vehicle details are correct. This prevents them
						// seeing another vehicle confirmation dialog on the part requests form.
						// We can just send this request off and forget about it.
						$.post('/api/vehicle/confirmdetails', '', function(data, textStatus){window.location.reload();}, 'json');
						vrmStep = 0;
					},
					'No, check again': function()
					{
						$(this).dialog('close');
						// if not confirmed (wrong car) clear form and start again
						$.post('/api/vehicle/remove', '', function(data, textStatus){}, 'json');
						
						vrmStep = 1;
						//force live lookup
						lookupVRMLive(data.data.vrm, processPartrequestVRMResult, dialog);
					}
				},
				modal: true,
				title: 'Confirm Vehicle Details',
				width: 345
			}
		);
	}
	else if(vrmStep == 1)
		{
			$(dom_confirmVRM).dialog
			(
				{
					bgiframe: true,
					buttons:
					{
						'Yes, correct': function()
						{
							$(this).dialog('close');
							
							$('.part-from-price').hide();
							dialog.dialog('close');
							
							// The dialog will be replaced in the basket content.
							//$('#basket-vrm-lookup-dialog').remove();
							updateVehiclePreview(data);
							
							// We should also send off another AJAX request to indicate to the server that the
							// customer has already confirmed the vehicle details are correct. This prevents them
							// seeing another vehicle confirmation dialog on the part requests form.
							// We can just send this request off and forget about it.
							$.post('/api/vehicle/confirmdetails', '', function(data, textStatus){window.location.reload();}, 'json');
							vrmStep = 0;
						},
						'No, incorrect': function()
						{
							$(this).dialog('close');
							// if not confirmed (wrong car) clear form and start again
							$.post('/api/vehicle/remove', '', function(data, textStatus){}, 'json');
							
							vrmStep = 2;

							//if still incorrect show manual input dialog
							showPartrequestVehicleEntry(false);
							vrmStep = 0;
						}
					},
					modal: true,
					title: 'Confirm Vehicle Details',
					width: 345
				}
			);
		}
	
	$(dom_confirmVRM).parent().find('button:first').addClass('vrm-correct');
	$(dom_confirmVRM).parent().find('button:last').addClass('vrm-incorrect');
}

function showPartrequestVehicleEntry(vrmMode, vrmText)
{


	
	if(vrmMode === undefined ) 
	{
		vrmMode = true;
	}
	
	try{
	if(vrmText != '') 
	{
		$('#basket-vrm-lookup-dialog .vrm-lookup .plate input').val(vrmText);
	}
	}catch(e)
	{
		//console.log(e);
		
	}
	
	if(vrmMode)
	{
		$('.vrm-lookup').show();
		$('.vehicle-details-entry').hide();
	}
	else
	{
		$('.vehicle-details-entry').show();
		$('.vrm-lookup').hide();
	}
	
	//var clone = $('#basket-vrm-lookup-dialog').clone(); 

	$('.change-mode-link').click(function()
	{
		$('#basket-vrm-error').text('');
		vrmMode = !vrmMode;
		
		if(vrmMode)
		{
			$('.vrm-lookup').show();
			$('.vehicle-details-entry').hide();
		}
		else
		{
			$('.vehicle-details-entry').show();
			$('.vrm-lookup').hide();
		}
	});
	
	$('#basket-vrm-lookup-dialog').show();
	$('#basket-vrm-lookup-dialog').dialog('option', 'buttons', {
		'OK': function()
		{
			if(vrmMode)
			{
				var vrmInput = $('#basket-vrm-lookup-dialog .vrm-lookup .plate input').val();
				lookupVRM(vrmInput, processPartrequestVRMResult, $(this));
			}
			else
			{
				savePartrequestVehicle($(this));
			}
		},
		'Cancel': function()
		{
			$(this).dialog('close');
		}
	});
	$('#basket-vrm-lookup-dialog').dialog('open');
	
	$('#basket-vrm-lookup-dialog').parent().find('button:first').addClass('continue_part');
	$('#basket-vrm-lookup-dialog').parent().find('button:last').addClass('cancel_part');
	

}

function savePartrequestVehicle(dialog)
{
	if(!$('#basket-make-entry').val())
	{
		$('#basket-vrm-error').text('Please select your make');
		return;
	}
	
	if(!($('#basket-model-entry').val()))
	{
		$('#basket-vrm-error').text('Please select your model');
		return;
	}
	
	if(!($('#basket-year-entry').val()))
	{
		$('#basket-vrm-error').text('Please select your vehicle\'s year');
		return;
	}
	
	if(!($('#basket-body-entry').val()))
	{
		$('#basket-vrm-error').text('Please select your vehicle\'s body type');
		return;
	}
	
	if(!$('#basket-trim-entry').val())
	{
		$('#basket-vrm-error').text('Please select your vehicle\'s trim level');
		return;
	}
	
	// Still here? Good. All the data's ok then.
	var data =
	{
		make: $('#basket-make-entry').val(),
		model: $('#basket-model-entry').val(),
		year: $('#basket-year-entry').val(),
		body: $('#basket-body-entry').val(),
		trim: $('#basket-trim-entry').val()
	}

	$.post
	(
		'/api/vehicle/set',
		data,
		function(data, textStatus)
		{
			dialog.dialog('close');
			
			//remove '?vrm=Y' before refreshing page
			newURL = window.location.pathname;
			window.location = newURL;
		},
		'json'
	);
}


function showPartRequestLookupSelector() {
	$('#main_partrequest_container').hide();
	$('#partrequest_vehiclepicker #basket-vrm-lookup-dialog-static').show();
}

$(document).ready(function(){
	$('#mastercat-parts a:not(.nojs)').click(function(event){
		event.preventDefault();
		var details = $(this).attr('id').split('-');
		var category = details[1];
		var partID = details[2];
		var part;
		
		if(details.length == 3)
		{
			part = $(this).text();
		}
		else
		{
			part = $('#' + details[0] + '-' + details[1] + '-' + details[2]).text();
		}
		
		$.post('/api/basket/addmc', {category: category, part: part}, updateBasket, 'json');
	});
});

