var first_run = true;

;(function (jQuery) {

	jQuery.fn.postcoder = function(service, options) {

		var opts = jQuery.extend({}, jQuery.fn.postcoder.defaults, options);

		if (!eval('jQuery.fn.postcoder.'+service))
		{
			alert("The service '"+service+"' doesn't exist!");
			return false;
		}
		else
		{
			opts = jQuery.extend({}, eval('jQuery.fn.postcoder.'+service+'.defaults'), opts);
			opts.xmlmap = jQuery.extend({}, eval('jQuery.fn.postcoder.'+service+'.defaults.xmlmap'), opts.xmlmap);
		}

		if (opts.output == 'thickbox')
		{
			opts = jQuery.extend({}, eval('jQuery.fn.postcoder.thickbox.defaults'), opts);
		}

		opts.service = service;

		opts.out = jQuery('#'+opts.output);

		return this.each(function () {
			opts.search_btn = jQuery(this);
			opts.form = jQuery(this).parents('form');

			opts.search = jQuery('input[id='+opts.search_id+']', opts.form).val();

			if (opts.output == 'thickbox')
			{
				opts.out = jQuery.fn.postcoder.thickbox.prepare_out(opts.service, opts.form);
			}

			opts.search_btn.data('postcoder', opts);

			//opts.search_btn.trigger('pc_setup');

			opts.search_btn.click(function () {

				opts.search_btn.trigger('search');

				var this_data = jQuery(this).data('postcoder');

				if (first_run==true)
				{
					first_run=false;
					this_data.search = '';
				}

				var search = jQuery('input[id='+this_data.search_id+']', this_data.form).val();

				if (search!='')
				{
					jQuery.each(this_data.formmap, function (index, item) {
						jQuery('[name='+index+']').val('');
					});

					if (this_data.groupmap)
					{
						jQuery.each(this_data.groupmap, function (index, item) {
							var item = item[0].split(',');
							jQuery.each(item, function (index, item) {
								var item = item.split('|');
								jQuery('[name='+item[0]+']').val('');
							});
						});
					}

					if (this_data.output=='thickbox')
					{
						tb_show();
						jQuery("body").append("<div id='TB_load'><img src='"+imgLoader.src+"' /></div>");
						jQuery('#TB_load').show();
					}

					this_data.search_length = search.length;

					if (this_data.search == search && this_data.results != undefined)
					{
						jQuery.fn.postcoder.parse_xml(this_data.results.xml, this_data);
					}
					else
					{
						this_data.search = search;
						jQuery(this).data('postcoder', this_data);

						jQuery.fn.postcoder.run(search, this_data);
					}
				}

				return false;
			});
		});

	};

	jQuery.fn.postcoder.premise = function () {};
	jQuery.fn.postcoder.street = function () {};
	jQuery.fn.postcoder.match = function () {};
	jQuery.fn.postcoder.grid = function () {};
	jQuery.fn.postcoder.postzon = function () {};

	jQuery.fn.postcoder.defaults = {
		search_id: 'pc_search',
		search_btn_id: 'pc_btn',
		output: 'thickbox',
		server_script: 'postcoder_xml.php',
		wsdl: 'http://www.postcoderwebsoap.co.uk/websoap/websoap.php?wsdl',
		identifier: 'Example - WebSOAPTest',
		span_class: 'thoroughfare',
		span_class_a: 'premise',
		street_num: true,
		search_length: 0,
		result_length: 0
	};

	jQuery.fn.postcoder.check_postcode = function (postcode)
	{
		var pc_regexp = new RegExp("^(gir 0aa|((b|e|g|l|m|n|s|w)[0-9]([0-9]|[a-hjks-uw]){0,1}|(ab|al|ba|bb|bd|bh|bl|bn|br|bs|bt|ca|cb|cf|ch|cm|co|cr|ct|cv|cw|da|dd|de|dg|dh|dl|dn|dt|dy|ec|eh|en|ex|fk|fy|gl|gu|gy|ha|hd|hg|hp|hr|hs|hu|hx|ig|im|ip|iv|je|ka|kt|kw|ky|la|ld|le|ll|ln|ls|lu|me|mk|ml|ne|ng|nn|np|nr|nw|ol|ox|pa|pe|ph|pl|po|pr|rg|rh|rm|sa|se|sg|sk|sl|sm|sn|so|sp|sr|ss|st|sw|sy|ta|td|tf|tn|tq|tr|ts|tw|ub|wa|wc|wd|wf|wn|wr|ws|wv|xa|xb|xc|xd|xe|xf|xg|xh|xi|xj|yo|ze)[0-9]([0-9]|[abehmnprv-y]){0,1})(\\s+)([0-9])([abd-hjlnp-uw-z]){2})$","i");
		var pc_s_regexp = new RegExp("^([^\\s]+)(\\s+)([^\\s]+)$","i");

		postcode = jQuery.trim(postcode.toUpperCase());

		if (pc_s_regexp.test(postcode))
		{
			pc_s_regexp.exec(postcode);
			postcode = RegExp.$1 + ' ' + RegExp.$3;
		}
		else
		{
			postcode = postcode.replace(/ /g,'');
			postcode = postcode.substr(0,postcode.length-3)+' '+postcode.substr(postcode.length-3,3);
		}

		if (pc_regexp.test(postcode))
		{
			return postcode;
		}
		else
		{
			return false;
		}
	}

	jQuery.fn.postcoder.house_num = function (my_string)
	{
		// returns true if my_string is in the following form: 23, 23A, 22-23, 23/24. This allows
		// a premise number such as 72A to appear on the same line as the street name.
		if (my_string.length == 0)
			return false;

		var tmp_string = my_string.substr(0,my_string.length-1);

		var is_house_num = true;

		tmp_string = tmp_string.split('');

		jQuery.each(tmp_string, function (index, item) {
			var testchar = item[index];
			if (((testchar < '0') || (testchar > '9')) && (testchar != '-' ) && (testchar != '/' ))
			{
				is_house_num = false;
			}
		});

		return is_house_num;
	};

	jQuery.fn.postcoder.array_impl = function(my_array, delimiter) {
		// behaves a bit like php implode function converting an array into a string but
		// only includes the element of the array if it isn't an empty string or null
		if (delimiter == null)
			delimiter = ", ";

		var out='';
		var add_out='';

		jQuery.each(my_array, function (index, item) {
			add_out = my_array[index];

			if (add_out!='' && add_out!=undefined)
			{
				if (out!='')
					out+=delimiter;

				out+=add_out;
			}
		});

		return out;
	};

	jQuery.fn.postcoder.wrap_string = function(my_string, pre_string, post_string)
	{
		// if my_string isn't null then it will be wrapped by pre_string and post_string.
		// this becomes the return value
		if (my_string != null && my_string != '')
			my_string = pre_string+my_string+post_string;

		return my_string;
	};

	jQuery.fn.postcoder.is_array = function(obj)
	{
		// returns true if the parameter supplied is an array
		if (obj.constructor.toString().indexOf('Array') == -1)
			return false;
		else
			return true;
	};

	jQuery.fn.postcoder.in_array = function(find_item, arr)
	{
		var result = false;
		jQuery.each(arr, function (index, item) {
			if (item == find_item)
				result = index;
		});

		return result;
	};

	jQuery.fn.postcoder.behaviour = function (out, opts) {

		jQuery('ul:first', out).addClass('treeview-postcoder');

		jQuery('ul:first > li > ul', out).each(function () {
			if (jQuery(this).children('li').length == 1)
			{
				jQuery(this).find('a').append(', '+jQuery(this).parent('li').children('span').text());
				jQuery(this).parent('li').html(jQuery(this).children('li').html());
			}
		});

		if (jQuery('ul ul', out).length==1)
		{
			jQuery('li:first', out).addClass('open');
		}

		jQuery('ul:first', out).treeview({
			collapsed: true
		});

		return jQuery('a', out).length;
	};

	jQuery.fn.postcoder.out = function (out, content, title, opts) {
		out.html(content);

		var num_results = jQuery.fn.postcoder.behaviour(out, opts);

		if (num_results == 1)
		{
			opts.search_btn.trigger('one_result');
			jQuery.fn.postcoder.populate(opts.search_btn.data('count_0'), opts);
		}
		else
		{

			if (opts.output=='thickbox')
			{
				jQuery.fn.postcoder.thickbox(title, out);
			}

			jQuery('a', out).click(function () {

				var a_title = jQuery(this).attr('title');

				a_title = a_title.split('#');

				var address_data = jQuery('#'+a_title[0]).data(a_title[1]);
				var opts = jQuery('#'+a_title[0]).data('postcoder');


				jQuery.fn.postcoder.populate(address_data, opts);

				return false;
			});

		}
	};

	function debug(debug_text) {
		if (window.console && window.console.log)
			window.console.log(debug_text);
	};

	jQuery.fn.postcoder.populate_fields = function (address_data, formmap, opts)
	{
		var fieldorder = {};

		if (opts.linefields)
		{
			var count = 1;
			jQuery.each(opts.linefields, function (index, item) {
				var linefields = item.split(',');
				jQuery.each(linefields, function (index, item) {
					if (!eval('fieldorder.'+item))
					{
						eval('fieldorder.'+item+' = count;');
						count++;
					}
				});
			});

			var lineorder_sort = function (a, b)
			{
				return (b.seq < a.seq) - (a.seq < b.seq);
			}

			jQuery.each(formmap2, function (index, item) {
				var line = item[1];
				line = line.split(',');

				/*var lineorder = [];
				var lineobj = {};
				var count = 0;
				var formlines = 1;
				jQuery.each(line, function (index, item) {
					if (eval('fieldorder.'+item) && eval('address_data.'+item))
					{
						if (!eval('lineobj.'+item))
						{
							eval('lineobj.'+item+' = true;');
							lineorder[count] = {seq: eval('fieldorder.'+item), name: item, val: eval('address_data.'+item)};
							count++;
						}
					}
				});

				var linecount = count;
				lineorder.sort(lineorder_sort);
				var new_lineorder = {};
				jQuery.each(lineorder, function (index, item) {
					eval('new_lineorder.'+item.name+' = item.val;');
				});
				lineorder = new_lineorder;

				if (linecount > 1)
				{

					formlines = 0;
					jQuery.each(opts.linefields, function (index, item) {
						var linefields = item.split(',');
						var instance_found = false;
						jQuery.each(linefields, function (index, item) {
							if (eval('lineorder.'+item))
							{
								instance_found = true;
							}
						});
						if (instance_found)
							formlines++;
					});

					//console.log(formlines);

				}

				var fields = item[0];
				fields = fields.split(',');
				var fieldlist = {};
				var fieldcount = 0;
				count = 0;
				jQuery.each(fields, function (index, item) {
					alert(item);
					var field = item.split('__x');
					if (field.length>1)
						fieldcount+=parseInt(field[1]);
					else
						fieldcount++;

					count+=fieldcount;

					eval('fieldlist.'+field[0]+' = '+fieldcount+';');
				});

				console.log(fieldlist);*/
			});

		}
	};

	jQuery.fn.postcoder.populate = function (address_data, opts)
	{
		//debug(address_data);

		opts.results_length = 0;

		if (opts.output == 'thickbox')
			tb_remove();

		var premise = [];

		var formmap = opts.formmap;

		if (address_data.premise)
		{
			var premise = address_data.premise.split(',');
			if (jQuery.fn.postcoder.house_num(premise[premise.length-1])!==false)
			{
				address_data.building_number = premise[premise.length-1];

				// If requested the building number will appear before the dependent thoroughfare or thoroughfare if dependent throughfare is blank.
				// If both are blank then the building number will occupy the dependent thoroughfare element.


				if (opts.building_num)
				{
					address_data.premise = address_data.premise.substr(0,address_data.premise.length-address_data.building_number.length-1);

					if (address_data.dependent_street!='' || address_data.street=='')
						address_data.dependent_street = jQuery.fn.postcoder.array_impl([address_data.building_number,address_data.dependent_street], ' ');
					else
						address_data.street = jQuery.fn.postcoder.array_impl([address_data.building_number,address_data.street], ' ');
				}
			}

			premise = address_data.premise.split(',');

			jQuery.each(premise, function (index, item) {
				eval('address_data.premise_'+index+' = item;');
			});

			var formmap = {};

			// go through the formmap and for each instance of premise replace with each separate premise element
			jQuery.each(opts.formmap, function (index, item) {
				var i_old = item.split(',');
				var i_new = [];
				var count = 0;
				jQuery.each(i_old, function (index, item) {
					if (item == 'premise')
					{
						var c = 0;
						jQuery.each(premise, function(index, item) {
							i_new[count] = 'premise_'+c;
							count++;
							c++;
						});
					}
					else
					{
						i_new[count] = item;
						count++;
					}
				});

				eval('formmap.'+index+' = jQuery.fn.postcoder.array_impl(i_new, ",");');
			});

			address_data.premise = premise.length;
		}
		else
		{
			address_data.premise = 0;
		}

		//jQuery.fn.postcoder.populate_fields(address_data, formmap, opts);

		jQuery.each(formmap, function (index, item) {
			var field_value = jQuery.fn.postcoder.field_value(address_data, item);
			opts.results_length += field_value.length;
			jQuery('[name='+index+']', opts.form).val(field_value);
		});

		if (opts.groupmap)
		{
			var groupmap = opts.groupmap;

			jQuery.each(groupmap, function (index, item) {
				var fields = item[0].split(',');
				var all_fields = {};
				var line_count = 0;
				var values = item[1].split(',');
				var all_values = {};
				jQuery.each(fields, function (index, item) {
					var field = item.split('|');

					if (field.length == 1)
						field[1] = 1;

					eval('all_fields.'+field[0]+' = '+parseInt(field[1])+';');

					line_count += parseInt(field[1]);
				});

				var fieldorder = opts.fieldorder.split(',');

				var tmp_values = [];
				var count = 0;

				jQuery.each(values, function (index, item) {
					if (item == 'premise')
					{
						var c = 0;
						jQuery.each(premise, function(index, item) {
							tmp_values[count] = 'premise_'+c;
							count++;
							c++;
						});
					}
					else
					{
						tmp_values[count] = item;
						count++;
					}
				});

				values = tmp_values;
				var tmp_line_count = 0;

				jQuery.each(fieldorder, function (index, item) {
					if (jQuery.fn.postcoder.in_array(item, values)!==false && eval('address_data.'+item)!=undefined && eval('address_data.'+item)!='')
					{
						eval('all_values.'+item+' = address_data.'+item+';');
						tmp_line_count++;
					}
				});

				count = 0;

				if (tmp_line_count < line_count)
					line_count = tmp_line_count;

				var group_address = jQuery.fn.postcoder.group_prepare(all_values, line_count, opts.linefields);
				var group_count = 0;

				jQuery.each(all_fields, function (index, item) {
					if (group_count < group_address.length)
					{
						if (item > 1)
						{
							var tmp_lines = [];
							var tmp_count = 0;

							for (var i = 0; i < item; i++)
							{
								if (group_count < group_address.length)
								{
									tmp_lines[tmp_count] = group_address[group_count];
									tmp_count++;
									group_count++;
								}
							}


							tmp_lines = jQuery.fn.postcoder.array_impl(tmp_lines, '\n');
							opts.results_length += tmp_lines.length;
							jQuery('[name='+index+']', opts.form).val(tmp_lines);
						}
						else
						{
							//alert(opts.results_length);
							//alert(group_address[group_count].length);
							opts.results_length += group_address[group_count].length;
							jQuery('[name='+index+']', opts.form).val(group_address[group_count]);
							group_count++;
						}
					}
				});

			});

		}

		opts.search_btn.trigger('populate');

	};

	jQuery.fn.postcoder.group_prepare = function (values, line_count, linefields)
	{
		var save_values = values;

		var address_lines = [];

		var min_lines = 1;

		if (line_count > 1)
		{
			min_lines = 0;

			jQuery.each(linefields, function (index, item) {
				var line = item.split(',');
				var line_found = false;

				jQuery.each(line, function (index, item) {
					if (eval('values.'+item) && line_found == false)
					{
						min_lines++;
						line_found = true;
					}
				});
			});

			if (min_lines > line_count)
			{
				debug('You do not have sufficent lines for the requested address fields.');
			}

			var wrap_fields = [];
			var address_count = 0;
			var organisation = false;

			if (values.postcode)
			{
				address_lines[address_count] = values.postcode;
				address_count++;

				delete values.postcode;
			}

			var unset_town = false;

			if (address_count < line_count-1 && (values.county || values.post_town))
			{
				var tmp = [];
				var tmp_co = 0;
				if (values.post_town)
				{
					tmp[tmp_co] = values.post_town;
					tmp_co++;
				}
				if (values.county)
				{
					tmp[tmp_co] = values.county;
				}

				address_lines[address_count] = jQuery.fn.postcoder.array_impl(tmp, ', ');
				address_count++;
				unset_town = true;
			}

			if (address_count < line_count-1 && values.county && values.post_town)
			{
				address_lines[address_count-1] = values.county;
				address_lines[address_count] = values.post_town;
				address_count++;
			}

			if (unset_town)
			{
				delete values.county;
				delete values.post_town;
			}

			if (address_count < line_count-2 && values.organisation)
			{
				organisation = values.organisation;
				delete values.organisation;
				line_count--;
			}

			var else_lines = line_count - address_count;

			var else_rows = jQuery.fn.postcoder.line_split(values, else_lines);

			for (var i = else_rows.length-1; i >= 0; i--)
			{
				address_lines[address_count] = jQuery.fn.postcoder.array_impl(else_rows[i], ', ');
				address_count++;
			}

			if (organisation !== false)
				address_lines[address_count] = organisation;
		}
		else
		{
			address_lines[0] = jQuery.fn.postcoder.array_impl(values, ', ');
		}

		var tmp_address_lines = [];
		var tmp_count = 0;

		for (var i = address_lines.length-1; i >= 0; i--)
		{
			tmp_address_lines[tmp_count] = address_lines[i];
			tmp_count++;
		}

		return tmp_address_lines;
	}

	jQuery.fn.postcoder.line_split = function (data, rows)
	{
		var line_arr = [];
		var lines_arr = [];
		var total_length = 0;
		var avg_length = 0;
		var count = 0;

		jQuery.each(data, function (index, item) {
			total_length += item.length;
			count++;
		});
		avg_length = total_length/rows;

		var line_count = 0;
		var line_length = 0;
		var lines_count = 0;
		var tmp_count = 0;

		jQuery.each(data, function (index, item) {
			tmp_count++;
			line_arr[line_count] = item;
			line_count++;
			line_length += item.length;

			var remaining_items = count - tmp_count;
			var remaining_lines = (rows-lines_count-1);

			if (line_length >= avg_length || tmp_count == count || remaining_items <= remaining_lines)
			{
				lines_arr[lines_count] = line_arr;
				lines_count++;
				line_arr = [];
				line_count = 0;
				line_length = 0;

				total_length -= line_length;
				avg_length = total_length/(rows-line_count);
			}
		});

		return lines_arr;
	}

	jQuery.fn.postcoder.field_value = function (results, fields)
	{
		var field_split = fields.split(',');

		jQuery.each(field_split, function (index, item) {
			field_split[index] = eval('results.'+field_split[index]);
		});

		var field_value = jQuery.fn.postcoder.array_impl(field_split);

		if (field_value==undefined || field_value==null)
			field_value = '';

		return field_value;
	};

	jQuery.fn.postcoder.thickbox = function (title, out) {
		var tb_opts = jQuery.fn.postcoder.thickbox.defaults;
		tb_show(title, tb_opts.url+out.parents('div').attr('id'));
	};

	jQuery.fn.postcoder.thickbox.prepare_out = function (service, jQueryform) {
		var id_out = service+jQuery.fn.postcoder.thickbox.defaults.id_append;
		jQuery.fn.postcoder.thickbox.defaults.inline_id = id_out;

		while (jQuery('#'+id_out).html()!=null)
		{
			id_out+='_1';
		}

		jQuery(jQueryform).append('<div id="'+id_out+'"><div class="'+jQuery.fn.postcoder.thickbox.defaults.inner+'"></div></div>');

		jQuery('#'+id_out, jQueryform).css({'display': 'none'});

		var out = jQuery('#'+id_out+' .'+jQuery.fn.postcoder.thickbox.defaults.inner, jQueryform);

		return out;
	};

	jQuery.fn.postcoder.thickbox.defaults = {
		id_append: '_thickbox',
		inner: 'thickbox_content',
		url: '#TB_inline?height=300&width=400&inlineId='
	};

	jQuery.fn.postcoder.run = function (search, opts) {

		var post = '';

		if (opts.post)
		{
			jQuery.each(opts.post, function(index, item) {
				post += '&'+index+'='+item;
			});

			//debug(post);
		}

		//alert(opts.server_script);

		/*if (opts.post.fid != undefined)
		{
			jQuery.ajax({
				type: 'POST',
				url: '/online/test_demo_xml',
				data: 'search='+search+'&identifier='+opts.identifier+'&wsdl='+opts.wsdl+'&func='+opts.func+post,
				success: function (msg) {
					opts.remaining = parseInt(msg);
					//alert(opts.remaining);
					jQuery.ajax({
						type: 'POST',
						url: opts.server_script,
						data: 'search='+search+'&identifier='+opts.identifier+'&wsdl='+opts.wsdl+'&func='+opts.func+post,
						dataType: 'xml',
						error: function (error_mess) {
							jQuery.fn.postcoder.out(opts.out, opts.error_mess, search, opts);
						},
						success: function (xml) {
							jQuery.fn.postcoder.parse_xml(xml, opts);
						}
					});
				}
			});
		}
		else
		{
			jQuery.ajax({
				type: 'POST',
				url: opts.server_script,
				data: 'search='+search+'&identifier='+opts.identifier+'&wsdl='+opts.wsdl+'&func='+opts.func+post,
				dataType: 'xml',
				error: function (error_mess) {
					jQuery.fn.postcoder.out(opts.out, opts.error_mess, search, opts);
				},
				success: function (xml) {
					//opts.json = jQuery.xmlToJSON(xml);
					jQuery.fn.postcoder.parse_xml(xml, opts);
				}
			});
		}*/


		jQuery.ajax({
			type: 'POST',
			url: opts.server_script,
			data: 'search='+search+'&identifier='+opts.identifier+'&wsdl='+opts.wsdl+'&func='+opts.func+post,
			dataType: 'xml',
			error: function (error_mess) {
				jQuery.fn.postcoder.out(opts.out, opts.error_mess, search, opts);
			},
			success: function (xml) {
				//opts.json = jQuery.xmlToJSON(xml);
				jQuery.fn.postcoder.parse_xml(xml, opts);
			}
		});
	};

	jQuery.fn.postcoder.parse_xml = function (xml, opts)
	{
		opts.results_xml = jQuery.fn.postcoder.xml_array(jQuery('return', xml), opts.selectmap);

		opts.results = {};
		opts.results.xml = xml;
		opts.search_btn.data('postcoder', opts);
		jQuery.fn.postcoder.selection(opts);
	};

	jQuery.fn.postcoder.selection = function (opts)
	{
		var results = opts.results_xml;
		var output = '';

		var display_data = results.data;

		if (parseInt(jQuery('number_results', opts.results.xml).text())>0)
		{
			opts.search_btn.trigger('results_found');
		}

		if (results.children.length==0)
		{
			jQuery.fn.postcoder.populate(display_data, opts);
		}
		else if (opts.selectmap)
		{
			output = jQuery.fn.postcoder.selection_out(results.children, opts, display_data, 0, 'true');
			//alert(output);
			jQuery.fn.postcoder.out(opts.out, output, 'Results for '+display_data.search_string, opts);
		}

		opts.search_btn.trigger('selection');

	};

	jQuery.fn.postcoder.selection_out = function (children, opts, parent_data, count, newlist) {

		if (count == undefined)
			count = 0;

		if (newlist == 'true'){
			var output = '<ul>\n';
		}
		else{
			var output = '';
		}
		
		var prev_thrfare = '';
		
		jQuery.each(children, function (index, item) {

			var children_len = children[index].children.length;
			var span_class = opts.span_class;

			var next_parent_data = jQuery.extend({}, parent_data, children[index].data);
			
			// If option is set to combine user catagories, remove </li></ul> from output and process next child without starting a new <ul>	
			if (opts.post.usercombine == 'true' && children_len != 0 && prev_thrfare == jQuery.fn.postcoder.array_impl(children[index].data)){				
				output = output.substring(0,output.lastIndexOf("</ul>")); 
				output += jQuery.fn.postcoder.selection_out(children[index].children, opts, next_parent_data, count, 'false');
			}
			else{
			
				if (children_len == 0)
					span_class = opts.span_class_a;
	
				output += '<li>\n';
	
				output += '<span class="'+span_class+'">';
	
				
				if (children_len == 0)
				{
					output += '<a href="" title="'+opts.search_btn.attr('id')+'#count_'+count+'">';				
					opts.search_btn.data('count_'+count, next_parent_data);
					count++;
				}
				else{
					// if has children, i.e. is thrfare level, store info for later testing for user cat.
					prev_thrfare = jQuery.fn.postcoder.array_impl(children[index].data);
				}
	
				output += jQuery.fn.postcoder.array_impl(children[index].data);
	
				
				
				
				if (children_len == 0)
				{
					output += '</a>';
				}
	
				output += '</span>\n';
	
				if (children[index].children.length > 0)
				{
					output += jQuery.fn.postcoder.selection_out(children[index].children, opts, next_parent_data, count, 'true');
					count += children[index].children.length;
				}
			}
			
			output += '</li>\n';
		});

		output += '</ul>\n';

		return output;
	};

	jQuery.fn.postcoder.xml_array = function (xml, selectmap, level, fields)
	{
		var results = {};
		results.data = {};
		results.children = [];

		if (level == undefined)
			level = -1;

		if (fields == undefined)
			fields = [];

		level++;

		var count = 0;
		var tagname;

		xml.children().each(function () {

			tagname = this.tagName;

			if (jQuery(this).children().length == 0)
			{
				if (fields.length == 0 || jQuery.fn.postcoder.in_array(tagname, fields)!==false)
				{
					eval('results.data.'+tagname+' = jQuery(this).text();');
				}
			}
			else if (selectmap != undefined && selectmap[level])
			{
				count = 0;

				var selectmap_level = selectmap[level];

				var next_fields = [];

				if (jQuery.fn.postcoder.is_array(selectmap_level))
				{
					next_fields = selectmap_level[1].split(',');
					selectmap_level = selectmap_level[0];
				}

				jQuery(selectmap_level, xml).each(function () {
					results.children[count] = jQuery.fn.postcoder.xml_array(jQuery(this), selectmap, level, next_fields);
					count++;
				});
			}
		});

		return results;
	}

	jQuery.fn.postcoder.premise.defaults = {
		func: 'getPremiseList',
		error_mess: 'An error has occured, please enter your address manually.',
		selectmap: [
			['addresses > item','dependent_street,street,double_dependent_locality,dependent_locality,post_town,county,postcode'],
			['premise > item','organisation,premise']
		],
		building_num: true,
		formmap: {
			'organisation': 'organisation',
			'premise': 'premise',
			'dependent_street': 'dependent_street',
			'street': 'street',
			'double_dependent_locality': 'double_dependent_locality',
			'dependent_locality': 'dependent_locality',
			'post_town': 'post_town',
			'county': 'county',
			'postcode': 'postcode'
		},
		linefields: ['organisation','premise_0,premise_1,premise_2,premise_3','dependent_street,street,double_dependent_locality,dependent_locality','post_town,county','postcode'],
		fieldorder: 'organisation,premise_0,premise_1,premise_2,premise_3,dependent_street,street,double_dependent_locality,dependent_locality,post_town,county,postcode',
		format: true
	};

	jQuery.fn.postcoder.street.defaults = {
		func: 'getThrfareAddresses',
		error_mess: 'An error has occured, please enter your address manually.',
		selectmap: [
			['addresses > item','dependent_street,street,double_dependent_locality,dependent_locality,post_town,county,postcode']
		],
		formmap: {
			'dependent_street': 'dependent_street',
			'street': 'street',
			'double_dependent_locality': 'double_dependent_locality',
			'dependent_locality': 'dependent_locality',
			'post_town': 'post_town',
			'county': 'county',
			'postcode': 'postcode'
		},
		fieldorder: 'dependent_street,street,double_dependent_locality,dependent_locality,post_town,county,postcode',
		linefields: ['dependent_street,street,double_dependent_locality,dependent_locality','post_town,county','postcode']
	};

	jQuery.fn.postcoder.match.defaults = {
		func: 'getMatchAddress',
		error_mess: 'An error has occured, please enter your address manually.',
		selectmap: [
			['addresses > item','dependent_street,street,double_dependent_locality,dependent_locality,post_town,county,postcode'],
			['premise > item','organisation,premise']
		],
		building_num: true,
		formmap: {
			'organisation': 'organisation',
			'premise': 'premise',
			'dependent_street': 'dependent_street',
			'street': 'street',
			'double_dependent_locality': 'double_dependent_locality',
			'dependent_locality': 'dependent_locality',
			'post_town': 'post_town',
			'county': 'county',
			'postcode': 'postcode'
		},
		fieldorder: 'organisation,premise_0,premise_1,premise_2,premise_3,dependent_street,street,double_dependent_locality,dependent_locality,post_town,county,postcode',
		linefields: ['organisation','premise','dependent_street,street,double_dependent_locality,dependent_locality','post_town,county','postcode']
	};

	jQuery.fn.postcoder.grid.defaults = {
		func: 'getGrids',
		error_mess: 'An error has occured, please enter your address manually.',
		formmap: {
			'grideast': 'grideast',
			'gridnorth': 'gridnorth',
			'latitude_osgb36': 'latitude_osgb36',
			'longitude_osgb36': 'longitude_osgb36',
			'latitude_etrs89': 'latitude_etrs89',
			'longitude_etrs89': 'longitude_etrs89'
		},
		fieldorder: 'grideast,gridnorth,latitude_osgb36,longitude_osgb36,latitude_etrs89,longitude_etrs89',
		linefields: ['grideast,gridnorth,latitude_osgb36,longitude_osgb36,latitude_etrs89,longitude_etrs89']
	};

	jQuery.fn.postcoder.postzon.defaults = {
		func: 'getPostzon',
		error_mess: 'An error has occured, please enter your address manually.',
		formmap: {
			'grideasting': 'grideasting',
			'gridnorthing': 'gridnorthing',
			'grideastingstatus': 'grideastingstatus',
			'gridnorthingstatus': 'gridnorthingstatus',
			'localauthoritywardcode': 'localauthoritywardcode',
			'localauthoritywardname': 'localauthoritywardname',
			'localunitaryauthorityname': 'localunitaryauthorityname',
			'nhshealthauthoritycode': 'nhshealthauthoritycode',
			'nhshealthauthorityname': 'nhshealthauthorityname',
			'primarycaretrustcode': 'primarycaretrustcode',
			'primarycaretrustname': 'primarycaretrustname',
			'primarycaretrustha': 'primarycaretrustha'
		},
		fieldorder: 'grideasting,gridnorthing,grideastingstatus,gridnorthingstatus,localauthoritywardcode,localauthoritywardname,localunitaryauthorityname,nhshealthauthoritycode,nhshealthauthorityname,primarycaretrustcode,primarycaretrustname,primarycaretrustha',
		linefields: ['grideasting,gridnorthing,grideastingstatus,gridnorthingstatus,localauthoritywardcode,localauthoritywardname,localunitaryauthorityname,nhshealthauthoritycode,nhshealthauthorityname,primarycaretrustcode,primarycaretrustname,primarycaretrustha']
	};

})(jQuery);