/**
* QAS Best Practices v4.0
* for Pro Web and Pro On Demand address verification
* (c) QAS Ltd, 2009
*/

/**
* Global variables
*/

	// Set to the id attribute of the form containing your address elements
var	qasFormId = "form",

	// Verification will occur passively (bad addresses will be flagged) if set to false
	qasPromptUser = true,
	
	// Best Practices only supports CAN, GBR, SGF, SGP and USA as of v4.0
	qasRegisteredDatasets = new Array(
		"CAN",
		"GBR",
		"SGF",
		"USA"
	),

	// Addresses without countries will be verified against this dataset
	qasDefaultDataset = "USA",

	// Leave set as "Database layout" unless using custom output formatting
	qasDefaultLayout = "Database layout",

	// E.g. "../prowebproxy.jsp" or "http://mydomain.com/prowebproxy.jsp"
	// NOTE: JavaScript can't make calls outside the domain from which it's loaded
	qasProxyPath = "/qas/prowebproxy.php",

	// AJAX timeout (in milliseconds)
	qasTimeout = 5500,

	// Height and width of the QAS dialog in pixels
	qasWidth = 820,
	qasHeight = 398,

	// All form elements are selected by name
	qasElements = [
//		{
//			"street": [ 'address1', 'address2' ],
//			"city": "city",
//			"region": "state",
//			"postcode": "zip",
//			// Set to false if no country field exists
//			"country": false,
//			// Flag added to form by QasForm.init(); set to false to submit without flag
//			"verifiedFlag": "address_verified"
//		},
//		{
//			"street": [ 'ship_address1', 'ship_address2' ],
//			"city": "ship_city",
//			"region": "ship_state",
//			"postcode": "ship_zip",
//			"country": false,
//			"verifiedFlag": "ship_address_verified"
//		}
	];


/**
* Initialization
*/

$( document ).ready( function() {
	QasForm.init();
	if( qasPromptUser ) QasDialog.init();
});



/**
* Main function
*/

function qasVerify( submitWhenDone, collection ) {
	//alert('in function');
	if( typeof submitWhenDone == "undefined" ) var submitWhenDone = false;
	if( typeof collection == "undefined" ) var collection = 0;
	if( collection == 0 ) QasForm.rememberDuplicates();
	if( QasForm.isDuplicate( collection ) || QasForm.country( collection ).dataset == null )
	{
		if( ++collection < qasElements.length ) {
			return qasVerify( submitWhenDone, collection );
		}
		else if( submitWhenDone ) {
			QasForm.submit();
			return false;
		}
		else {
			QasDialog.close();
			return false;
		}
	}
	var originalAddress = QasForm.address( collection );
	var originalCountry = QasForm.country( collection );
	var initialInput = new QasVerification( originalCountry );
	$( "body, input, select" ).css( "cursor", "wait" );
	initialInput.search( originalAddress, function( response ) {
		$( "body, input, select" ).css( "cursor", "auto" );
		switch( response.verificationLevel ) {
			// Expecting a single address
			case "Verified":
				QasForm.address( collection, response.address );
				if( qasElements[ collection ].verifiedFlag ) QasForm.flagVerified( collection, true );
				if( ++collection < qasElements.length ) {
					return qasVerify( submitWhenDone, collection );
				}
				else if( submitWhenDone ) {
					QasForm.submit();
				}
				break;
			// Expecting a single address
			case "InteractionRequired":
				if( qasPromptUser ) {
					QasDialog.promptInteractionRequired( response.address, originalAddress, function( userResponse ) {
						switch( userResponse ) {
							case "confirm":
								QasForm.address( collection, response.address );
								if( qasElements[ collection ].verifiedFlag ) {
									QasForm.flagVerified( collection, true );
								}
								if( ++collection < qasElements.length ) {
									return qasVerify( submitWhenDone, collection );
								}
								else if( submitWhenDone ) {
									QasForm.submit();
								}
								else {
									QasDialog.close();
								}
								break;
							case "edit":
								QasDialog.close();
								break;
							case "override":
								if( qasElements[ collection ].verifiedFlag ) {
									QasForm.flagVerified( collection, false );
								}
								if( ++collection < qasElements.length ) {
									return qasVerify( submitWhenDone, collection );
								}
								else if( submitWhenDone ) {
									QasForm.submit();
								}
								else {
									QasDialog.close();
								}
								break;
						}
					}, originalCountry );
				}
				else {
					if( qasElements[ collection ].verifiedFlag ) {
						QasForm.flagVerified( collection, false );
					}
					if( ++collection < qasElements.length ) {
						return qasVerify( submitWhenDone, collection );
					}
					else if( submitWhenDone ) {
						QasForm.submit();
					}
				}
				break;
			case "StreetPartial":
				if( qasPromptUser ) {
					QasDialog.promptStreetPartial( response.picklist, originalAddress, function( userResponse ) {
						if( typeof userResponse == "object" ) {
							QasForm.address( collection, userResponse );
							if( qasElements[ collection ].verifiedFlag ) {
								QasForm.flagVerified( collection, true );
							}
							if( ++collection < qasElements.length ) {
								return qasVerify( submitWhenDone, collection );
							}
							else if( submitWhenDone ) {
								QasForm.submit();
							}
							else {
								QasDialog.close();
							}
						}
						else {
							switch( userResponse ) {
								case "edit":
									QasDialog.close();
									break;
								case "override":
									if( qasElements[ collection ].verifiedFlag ) {
										QasForm.flagVerified( collection, false );
									}
									if( ++collection < qasElements.length ) {
										return qasVerify( submitWhenDone, collection );
									}
									else if( submitWhenDone ) {
										QasForm.submit();
									}
									else {
										QasDialog.close();
									}
									break;
							}
						}
					}, originalCountry );
				}
				else {
					if( qasElements[ collection ].verifiedFlag ) {
						QasForm.flagVerified( collection, false );
					}
					if( ++collection < qasElements.length ) {
						return qasVerify( submitWhenDone, collection );
					}
					else if( submitWhenDone ) {
						QasForm.submit();
					}
				}
				break;
			case "PremisesPartial":
				if( qasPromptUser ) {
					QasDialog.promptPremisesPartial( response.picklist, originalAddress, function( userResponse ) {
						if( typeof userResponse == "object" ) {
							QasForm.address( collection, userResponse );
							if( qasElements[ collection ].verifiedFlag ) {
								QasForm.flagVerified( collection, true );
							}
							if( ++collection < qasElements.length ) {
								return qasVerify( submitWhenDone, collection );
							}
							else if( submitWhenDone ) {
								QasForm.submit();
							}
							else {
								QasDialog.close();
							}
						}
						else {
							switch( userResponse ) {
								case "edit":
									QasDialog.close();
									break;
								case "override":
									if( qasElements[ collection ].verifiedFlag ) {
										QasForm.flagVerified( collection, false );
									}
									if( ++collection < qasElements.length ) {
										return qasVerify( submitWhenDone, collection );
									}
									else if( submitWhenDone ) {
										QasForm.submit();
									}
									else {
										QasDialog.close();
									}
									break;
							}
						}
					}, originalCountry );
				}
				else {
					if( qasElements[ collection ].verifiedFlag ) {
						QasForm.flagVerified( collection, false );
					}
					if( ++collection < qasElements.length ) {
						return qasVerify( submitWhenDone, collection );
					}
					else if( submitWhenDone ) {
						QasForm.submit();
					}
				}
				break;
			case "Multiple":
				if( qasPromptUser ) {
					QasDialog.promptMultiple( response.picklist, originalAddress, function( userResponse ) {
						if( typeof userResponse == "object" ) {
							QasForm.address( collection, userResponse );
							if( qasElements[ collection ].verifiedFlag ) {
								QasForm.flagVerified( collection, true );
							}
							if( ++collection < qasElements.length ) {
								return qasVerify( submitWhenDone, collection );
							}
							else if( submitWhenDone ) {
								QasForm.submit();
							}
							else {
								QasDialog.close();
							}
						}
						else {
							switch( userResponse ) {
								case "edit":
									QasDialog.close();
									break;
								case "override":
									if( qasElements[ collection ].verifiedFlag ) {
										QasForm.flagVerified( collection, false );
									}
									if( ++collection < qasElements.length ) {
										return qasVerify( submitWhenDone, collection );
									}
									else if( submitWhenDone ) {
										QasForm.submit();
									}
									else {
										QasDialog.close();
									}
									break;
							}
						}
					}, originalCountry );
				}
				else {
					if( qasElements[ collection ].verifiedFlag ) {
						QasForm.flagVerified( collection, false );
					}
					if( ++collection < qasElements.length ) {
						return qasVerify( submitWhenDone, collection );
					}
					else if( submitWhenDone ) {
						QasForm.submit();
					}
				}
				break;
			case "None":
				if( qasPromptUser ) {
					QasDialog.promptNone( originalAddress, function( userResponse ) {
						switch( userResponse ) {
							case "edit":
								QasDialog.close();
								break;
							case "override":
								if( ++collection < qasElements.length ) {
									return qasVerify( submitWhenDone, collection );
								}
								else if( submitWhenDone ) {
									QasForm.submit();
								}
								else {
									QasDialog.close();
								}
								break;
						}
					}, originalCountry );
				}
				else {
					if( qasElements[ collection ].verifiedFlag ) {
						QasForm.flagVerified( collection, false );
					}
					if( ++collection < qasElements.length ) {
						return qasVerify( submitWhenDone, collection );
					}
					else if( submitWhenDone ) {
						QasForm.submit();
					}
				}
				break;
		}
	}, function( request, textStatus, errorThrown ) {
		$( "body, input, select" ).css( "cursor", "auto" );
		if( window.console && console.log ) {
			console.error( Date() + ": " + request + ", " + textStatus + ", " + errorThrown );
		}
		if( qasElements[ collection ].verifiedFlag ) QasForm.flagVerified( collection, false );
		if( ++collection < qasElements.length ) {
			qasVerify( submitWhenDone, collection );
		}
		else if( submitWhenDone ) {
			QasForm.submit();
		}
		else {
			QasDialog.close();
		}
	});
	return false;
}


/**
* Static class: QasDialog
*/

var QasDialog = {};

QasDialog.init = function() {

	$( "body" ).append(
		'<div id="qas" style="display:none;">' +
			'<div id="qas-interactionrequired"></div>' +
			'<a id="qas-open-interactionrequired"' +
				'href="#TB_inline?inlineId=qas-interactionrequired&modal=true' +
				'&height=' + qasHeight + '&width=' + qasWidth + '" class="thickbox"></a>' +
			'<div id="qas-streetpartial"></div>' +
			'<a id="qas-open-streetpartial"' +
				'href="#TB_inline?inlineId=qas-streetpartial&modal=true' +
				'&height=' + qasHeight + '&width=' + qasWidth + '" class="thickbox"></a>' +
			'<div id="qas-premisespartial"></div>' +
			'<a id="qas-open-premisespartial"' +
				'href="#TB_inline?inlineId=qas-premisespartial&modal=true' +
				'&height=' + qasHeight + '&width=' + qasWidth + '" class="thickbox"></a>' +
			'<div id="qas-multiple"></div>' +
			'<a id="qas-open-multiple"' +
				'href="#TB_inline?inlineId=qas-multiple&modal=true' +
				'&height=' + qasHeight + '&width=' + qasWidth + '" class="thickbox"></a>' +
			'<div id="qas-none"></div>' +
			'<a id="qas-open-none"' +
				'href="#TB_inline?inlineId=qas-none&modal=true' +
				'&height=' + qasHeight + '&width=' + qasWidth + '" class="thickbox"></a>' +
		'</div>'
	);

	$( "#qas" ).ready( function() {

		var dialogReadyCount = 0;

		// Thickbox 3.1 doesn't allow for JavaScript-driven content, so load the dialogs into hidden divs
		$( "#qas-interactionrequired" ).load( "/qas/qas-interactionrequired.html", dialogReady() );
		$( "#qas-streetpartial" ).load( "/qas/qas-streetpartial.html", dialogReady() );
		$( "#qas-premisespartial" ).load( "/qas/qas-premisespartial.html", dialogReady() );
		$( "#qas-multiple" ).load( "/qas/qas-multiple.html", dialogReady() );
		$( "#qas-none" ).load( "/qas/qas-none.html", dialogReady() );
		
		// Re-initialize Thickbox once the dialogs are ready
		function dialogReady() {
			dialogReadyCount++;
			if( dialogReadyCount == 5 ) {
				$(
					"#qas .qas-input-feedback," +
					"#qas [ id $= 'linetwo' ]," +
					"#qas [ id $= 'linethree' ]," +
					"#qas [ id $= 'country' ] "
				).css( "display", "none" );
				tb_init( "a.thickbox" );
			}
		}

	});

}

QasDialog.promptInteractionRequired = function(
	newAddress,					// QasAddress
	originalAddress,			// QasAddress
	callback,					// function( string )
	originalCountry )			// (QasCountry)
{

	// Close any existing dialogs before opening a new one
	QasDialog.close( function() {

		// Reset dialog content before loading new content
		QasDialog.resetContent( function() {

			$( "#qas-interaction-required-lineone" ).html( newAddress.street[ 0 ] );
			if( newAddress.street.length > 1 ) {
				$( "#qas-interaction-required-linetwo" ).html( newAddress.street[ 1 ] );
				$( "#qas-interaction-required-linetwo" ).css( "display", "block" );
				if( newAddress.street.length > 2 ) {
					$( "#qas-interaction-required-linethree" ).html( newAddress.street[ 2 ] );
					$( "#qas-interaction-required-linethree" ).css( "display", "block" );
				}
			}
			$( "#qas-interaction-required-city-region-postcode" ).html(
				[ newAddress.city, newAddress.region, newAddress.postcode ].join( " " ) );
			$( "#qas-interaction-required-original-lineone" ).html( originalAddress.street[ 0 ] );
			if( originalAddress.street.length > 1 ) {
				$( "#qas-interaction-required-original-linetwo" ).html( originalAddress.street[ 1 ] );
				$( "#qas-interaction-required-original-linetwo" ).css( "display", "block" );
			}
			if( originalAddress.street.length > 2 ) {
				$( "#qas-interaction-required-original-linethree" ).html( originalAddress.street[ 2 ] );
				$( "#qas-interaction-required-original-linethree" ).css( "display", "block" );
			}
			$( "#qas-interaction-required-original-city" ).html( originalAddress.city );
			$( "#qas-interaction-required-original-region" ).html( originalAddress.region );
			$( "#qas-interaction-required-original-postcode" ).html( originalAddress.postcode );
			if( originalCountry ) {
				$( "#qas-interaction-required-country" ).css( "display", "block" );
				$( "#qas-interaction-required-country" ).html( originalCountry.name );
				$( "#qas-interaction-required-original-country" ).css( "display", "block" );
				$( "#qas-interaction-required-original-country" ).html( originalCountry.iso );
			}
	
			// Open Thickbox, allowing 10ms for existing Thickboxes to finish closing
			if( $( "#TB_window" ).length ) {
				setTimeout( function() {
					$( "#qas-open-interactionrequired" ).click();
					// $( ".qas-confirm" ).focus();
				}, 10 );
			}
			else {
				$( "#qas-open-interactionrequired" ).click();
				// $( ".qas-confirm" ).focus();
			}
	
			// Attach listners to user input
			QasDialog.attachStandardListeners( callback );
			$( ".qas-confirm" ).click( function() {
				callback( "confirm" );
			});
			$( document ).keyup( function( key ) {
				// IE and Mozilla seem to have different ideas about key events
				var keycode = key == null ? event.keyCode : key.which;
				// <return>
				if ( keycode == 13 ) callback( "confirm" );
			});

		});
		
	});

}

QasDialog.promptStreetPartial = function(
	picklist,					// Array of QasPicklistItems
	originalAddress,			// QasAddress
	callback,					// function( string )
	originalCountry )			// (QasCountry)
{
	
	// Close any existing dialogs before opening a new one
	QasDialog.close( function() {

		// Reset dialog content before loading new content
		QasDialog.resetContent( function() {
		
			$( "#qas-street-partial-original-lineone" ).html( originalAddress.street[ 0 ] );
			if( originalAddress.street.length > 1 ) {
				$( "#qas-street-partial-original-linetwo" ).html( originalAddress.street[ 1 ] );
				$( "#qas-street-partial-original-linetwo" ).css( "display", "block" );
			}
			if( originalAddress.street.length > 2 ) {
				$( "#qas-street-partial-original-linethree" ).html( originalAddress.street[ 2 ] );
				$( "#qas-street-partial-original-linethree" ).css( "display", "block" );
			}
			$( "#qas-street-partial-original-city" ).html( originalAddress.city );
			$( "#qas-street-partial-original-region" ).html( originalAddress.region );
			$( "#qas-street-partial-original-postcode" ).html( originalAddress.postcode );
			if( originalCountry ) {
				$( "#qas-street-partial-original-country" ).css( "display", "block" );
				$( "#qas-street-partial-original-country" ).html( originalCountry.iso );
			}

			// Trim original building number from the address and put it in the search box
			var trimmedLine = 0;
			for( var line in originalAddress.street ) {
				// Match dashes as well as numbers since Canadian subpremises get messy if we don't
				if( originalAddress.street[ line ].search( /^(\d|-)*/ ) > -1 ) {
					$( ".qas-change-street-number" ).val( originalAddress.street[ line ].match( /^(\d|-)*/ )[ 0 ] );
					originalAddress.street[ line ] = $.trim( originalAddress.street[ line ].replace( /^(\d|-)*/, "" ) );
					trimmedLine = line;
					break;
				}
			}
						
			var changeStreetNumber = function() {
				var inputConfirmation = originalCountry ?
					new QasVerification( originalCountry ) :
					new QasVerification();
				var newAddress = jQuery.extend( true, {}, originalAddress );
				newAddress.street[ trimmedLine ] = $( ".qas-change-street-number" ).val() +
					" " +
					newAddress.street[ trimmedLine ];
				inputConfirmation.search( newAddress, function( response ) {
					if( response.verificationLevel == "Verified" ||
						response.verificationLevel == "InteractionRequired" ) {
						callback( response.address );
					}
					else if( response.verificationLevel == "PremisesPartial" ) {
						if( originalCountry ) {
							QasDialog.promptPremisesPartial( response.picklist, newAddress, callback, originalCountry );
						}
						else {
							QasDialog.promptPremisesPartial( response.picklist, newAddress, callback );
						}
					}
					else {
						$( "#qas-street-input-feedback" ).show();
					}
				}, function( request, textStatus, errorThrown ) {
					if( window.console && console.log ) {
						console.error( Date() + ": " + request + ", " + textStatus + ", " + errorThrown );
					}
				});
			}
			
			// Open Thickbox, allowing 10ms for existing Thickboxes to finish closing
			if( $( "#TB_window" ).length ) {
				setTimeout( function() {
					$( "#qas-open-streetpartial" ).click();
					$( ".qas-change-street-number" ).select();
				}, 10 );
			}
			else {
				$( "#qas-open-streetpartial" ).click();
				$( ".qas-change-street-number" ).select();
			}
	
			// Attach listners to user input
			QasDialog.attachStandardListeners( callback );
			$( ".qas-change-street-number-button" ).click( changeStreetNumber );
			$( document ).keyup( function( key ) {
				// IE and Mozilla seem to have different ideas about key events
				var keycode = key == null ? event.keyCode : key.which;
				// <return>
				if ( keycode == 13 ) changeStreetNumber();
			});

			if( picklist != null ) {
				$( "#qas-street-partial-picklist-link" ).css( "display", "block" );
				$( "#qas-street-partial-picklist-link" ).click( function() {
					$( "#qas-street-partial-picklist-link" ).css( "display", "none" );
					$( ".qas-street-partial-list-scrollbox" ).css( "display", "block" );
				});
				// IE6 and QasPicklistItem don't play nice with for...in loops, so iterate traditionally
				for( var item = 0; item < picklist.length; item++ ) {
					if( picklist[ item ].addressText.length > 52 ) {
						picklist[ item ].addressText = picklist[ item ].addressText.substr( 0, 51 ) + " ...";
					}
					if( picklist[ item ].fullAddress ) {
						$( "#qas-street-partial-list" ).append(
							"<tr id=\"qas-street-partial-" + item + "\">" +
							"	<td style=\"text-decoration:underline;cursor:pointer;\">" +
							picklist[ item ].addressText + "</td>" +
							"	<td style=\"text-decoration:underline;cursor:pointer;\">" +
							picklist[ item ].postcode + "</td>" +
							"</tr>"
						);
						// The item var won't be evaluated correctly in $.click(), so remember monikers with an array
						picklist[ "qas-street-partial-" + item ] = picklist[ item ].moniker;
					}
					else {
						$( "#qas-street-partial-list" ).append(
							"<tr>" +
							"	<td>" + picklist[ item ].addressText + "</td>" +
							"	<td>" + picklist[ item ].postcode + "</td>" +
							"</tr>"
						);
					}
					$( "#qas-street-partial-" + item ).click( function() {
						var userSelection = new QasVerification( originalCountry );
						userSelection.refine( picklist[ $( this ).attr( "id" ) ], "", function( refineResponse ) {
							callback( refineResponse.address );
						}, function( request, textStatus, errorThrown ) {
							if( window.console && console.log ) {
								console.error( Date() + ": " + request + ", " + textStatus + ", " + errorThrown );
							}
						});
					});
				}
			}
			
		});
	
	});

}

QasDialog.promptPremisesPartial = function(
	picklist,					// Array of QasPicklistItems
	originalAddress,			// QasAddress
	callback,					// function( string )
	originalCountry )			// (QasCountry)
{

	// Close any existing dialogs before opening a new one
	QasDialog.close( function() {

		// Reset dialog content before loading new content
		QasDialog.resetContent( function() {
	
			$( "#qas-premises-partial-original-lineone" ).html( originalAddress.street[ 0 ] );
			if( originalAddress.street.length > 1 ) {
				$( "#qas-premises-partial-original-linetwo" ).html( originalAddress.street[ 1 ] );
				$( "#qas-premises-partial-original-linetwo" ).css( "display", "block" );
			}
			if( originalAddress.street.length > 2 ) {
				$( "#qas-premises-partial-original-linethree" ).html( originalAddress.street[ 2 ] );
				$( "#qas-premises-partial-original-linethree" ).css( "display", "block" );
			}
			$( "#qas-premises-partial-original-city" ).html( originalAddress.city );
			$( "#qas-premises-partial-original-region" ).html( originalAddress.region );
			$( "#qas-premises-partial-original-postcode" ).html( originalAddress.postcode );
			if( originalCountry ) {
				$( "#qas-premises-partial-original-country" ).css( "display", "block" );
				$( "#qas-premises-partial-original-country" ).html( originalCountry.iso );
			}
	
			// Open Thickbox, allowing 10ms for existing Thickboxes to finish closing
			if( $( "#TB_window" ).length ) {
				setTimeout( function() {
					$( "#qas-open-premisespartial" ).click();
					$( ".qas-add-subpremise" ).select();
				}, 10 );
			}
			else {
				$( "#qas-open-premisespartial" ).click();
				$( ".qas-add-subpremise" ).select();
			}
			
			var addSubpremise = function() {
				var inputConfirmation = originalCountry ?
					new QasVerification( originalCountry ) :
					new QasVerification();
				var newAddress = jQuery.extend( true, {}, originalAddress );
				if( originalCountry.dataset == "CAN" || originalCountry.dataset == "GBR" ) {
					newAddress.street[ 0 ] = $( ".qas-add-subpremise" ).val() +
						"-" + newAddress.street[ 0 ];
				}
				else if( originalCountry.dataset == "SGP" || originalCountry.dataset == "SGF" ) {
					newAddress.street[ 0 ] = "#" + $( ".qas-add-subpremise" ).val() +
						"-" + newAddress.street[ 0 ];
				}
				else {
					newAddress.street[ 0 ] = newAddress.street[ 0 ] + " #" +
						$( ".qas-add-subpremise" ).val();
				}
				inputConfirmation.search( newAddress, function( response ) {
					if( response.verificationLevel == "Verified" ||
						response.verificationLevel == "InteractionRequired" )
					{
						callback( response.address );
					}
					else {
						$( "#qas-subpremise-input-feedback" ).show();
					}
				}, function( request, textStatus, errorThrown ) {
					if( window.console && console.log ) {
						console.error( Date() + ": " + request + ", " + textStatus + ", " + errorThrown );
					}
				});
			}
			
			// Attach listners to user input
			QasDialog.attachStandardListeners( callback );
			$( ".qas-add-subpremise-button" ).click( addSubpremise );
			$( document ).keyup( function( key ) {
				// IE and Mozilla seem to have different ideas about key events
				var keycode = key == null ? event.keyCode : key.which;
				// <return>
				if ( keycode == 13 ) addSubpremise();
			});

			// IE6 and QasPicklistItem don't play nice with for...in loops, so iterate traditionally
			if( picklist != null ) {
				$( "#qas-premises-partial-picklist-link" ).css( "display", "block" );
				$( "#qas-premises-partial-picklist-link" ).click( function() {
					$( "#qas-premises-partial-picklist-link" ).css( "display", "none" );
					$( ".qas-premises-partial-list-scrollbox" ).css( "display", "block" );
				});
				for( var item = 0; item < picklist.length; item++ ) {
					if( picklist[ item ].addressText.length > 52 ) {
						picklist[ item ].addressText = picklist[ item ].addressText.substr( 0, 51 ) + " ...";
					}
					if( picklist[ item ].fullAddress ) {
						$( "#qas-premises-partial-list" ).append(
							"<tr id=\"qas-premises-partial-" + item + "\">" +
							"	<td style=\"text-decoration:underline;cursor:pointer;\">" +
							picklist[ item ].addressText + "</td>" +
							"	<td style=\"text-decoration:underline;cursor:pointer;\">" +
							picklist[ item ].postcode + "</td>" +
							"</tr>"
						);
						// The item var won't be evaluated correctly in $.click(), so remember monikers with an array
						picklist[ "qas-premises-partial-" + item ] = picklist[ item ].moniker;
					}
					else {
						$( "#qas-premises-partial-list" ).append(
							"<tr>" +
							"	<td>" + picklist[ item ].addressText + "</td>" +
							"	<td>" + picklist[ item ].postcode + "</td>" +
							"</tr>"
						);
					}
					$( "#qas-premises-partial-" + item ).click( function() {
						var userSelection = new QasVerification( originalCountry );
						userSelection.refine( picklist[ $( this ).attr( "id" ) ], "", function( refineResponse ) {
							callback( refineResponse.address );
						}, function( request, textStatus, errorThrown ) {
							if( window.console && console.log ) {
								console.error( Date() + ": " + request + ", " + textStatus + ", " + errorThrown );
							}
						});
					});
				}
			}
			
		});
	
	});
	
}

QasDialog.promptMultiple = function(
	picklist,					// array of QasPicklistItems
	originalAddress,			// QasAddress
	callback,					// function( string )
	originalCountry )			// (QasCountry)
{

	// Close any existing dialogs before opening a new one
	QasDialog.close( function() {

		// Reset dialog content before loading new content
		QasDialog.resetContent( function() {

			$( "#qas-multiple-original-lineone" ).html( originalAddress.street[ 0 ] );
			if( originalAddress.street.length > 1 ) {
				$( "#qas-multiple-original-linetwo" ).html( originalAddress.street[ 1 ] );
				$( "#qas-multiple-original-linetwo" ).css( "display", "block" );
			}
			if( originalAddress.street.length > 2 ) {
				$( "#qas-multiple-original-linethree" ).html( originalAddress.street[ 2 ] );
				$( "#qas-multiple-original-linethree" ).css( "display", "block" );
			}
			$( "#qas-multiple-original-city" ).html( originalAddress.city );
			$( "#qas-multiple-original-region" ).html( originalAddress.region );
			$( "#qas-multiple-original-postcode" ).html( originalAddress.postcode );
			if( originalCountry ) {
				$( "#qas-multiple-original-country" ).css( "display", "block" );
				$( "#qas-multiple-original-country" ).html( originalCountry.iso );
			}
	
			// Generate list of matches and attach listners to user input
			QasDialog.attachStandardListeners( callback );
			// IE6 and QasPicklistItem don't play nice with for...in loops, so iterate traditionally
			for( var item = 0; item < picklist.length; item++ ) {
				if( picklist[ item ].addressText.length > 45 ) {
					picklist[ item ].addressText = picklist[ item ].addressText.substr( 0, 44 ) + " ...";
				}
				$( "#qas-multiple-list" ).append(
					"<tr id=\"qas-multiple-" + item + "\">" +
					"	<td>" + picklist[ item ].addressText + "</td>" +
					"	<td>" + picklist[ item ].postcode + "</td>" +
					"</tr>"
				);
				// The item var won't be evaluated correctly in $.click(), so remember monikers with an array
				picklist[ "qas-multiple-" + item ] = picklist[ item ].moniker;
				$( "#qas-multiple-" + item ).click( function() {
					var userSelection = new QasVerification( originalCountry );
					userSelection.refine( picklist[ $( this ).attr( "id" ) ], "", function( refineResponse ) {
						callback( refineResponse.address );
					}, function( request, textStatus, errorThrown ) {
						if( window.console && console.log ) {
							console.error( Date() + ": " + request + ", " + textStatus + ", " + errorThrown );
						}
					});
				});
			}
									
			// Open Thickbox, allowing 10ms for existing Thickboxes to finish closing
			if( $( "#TB_window" ).length ) {
				setTimeout( function() {
					$( "#qas-open-multiple" ).click();		
				}, 10 );
			}
			else {
				$( "#qas-open-multiple" ).click();
			}
			
		});

	});
	
}

QasDialog.promptNone = function(
	originalAddress,			// QasAddress
	callback,					// function( string )
	originalCountry )			// (QasCountry)
{

	// Close any existing dialogs before opening a new one
	QasDialog.close( function() {

		// Reset dialog content before loading new content
		QasDialog.resetContent( function() {

			$( "#qas-none-original-linetwo" ).css( "display", "none" );
			$( "#qas-none-original-linethree" ).css( "display", "none" );
			$( "#qas-none-original-lineone" ).html( originalAddress.street[ 0 ] );
			if( originalAddress.street.length > 1 ) {
				$( "#qas-none-original-linetwo" ).html( originalAddress.street[ 1 ] );
				$( "#qas-none-original-linetwo" ).css( "display", "block" );
			}
			if( originalAddress.street.length > 2 ) {
				$( "#qas-none-original-linethree" ).html( originalAddress.street[ 2 ] );
				$( "#qas-none-original-linethree" ).css( "display", "block" );
			}
			$( "#qas-none-original-city" ).html( originalAddress.city );
			$( "#qas-none-original-region" ).html( originalAddress.region );
			$( "#qas-none-original-postcode" ).html( originalAddress.postcode );
			if( originalCountry ) {
				$( "#qas-none-original-country" ).css( "display", "block" );
				$( "#qas-none-original-country" ).html( originalCountry.name );
			}
	
			// Open Thickbox, allowing 10ms for existing Thickboxes to finish closing
			if( $( "#TB_window" ).length ) {
				setTimeout( function() {
					$( "#qas-open-none" ).click();
					// $( "#TB_window .qas-edit" ).focus();	
				}, 10 );
			}
			else {
				$( "#qas-open-none" ).click();
				// $( "#TB_window .qas-edit" ).focus();
			}
	
			// Attach listners to user input
			QasDialog.attachStandardListeners( callback );
			
		});
		
	});

}

QasDialog.attachStandardListeners = function( callback ) {
	$( document ).keyup( function( key ) {
		// IE and Mozilla have different interpretations of key events
		var keycode = key == null ? event.keyCode : key.which;
		// <esc>
		if ( keycode == 27 ) callback( "edit" );
	});
	$( ".qas-edit" ).click( function() {
		callback( "edit" );
	});
	$( ".qas-override" ).click( function() {
		callback( "override" );
	});
}

QasDialog.close = function( callback ) {
	$( document ).unbind( 'keyup' );
	if( $( "#TB_window" ).length ) {
		// callback() is called once we're sure Thickbox is closed
		$( "#TB_window" ).unload( function() {
			if( callback ) {
				callback();
			}
		});
		tb_remove();
	}
	else if( callback ) {
		callback();
	}
}

QasDialog.resetContent = function( callback ) {
	$(
		"#qas .qas-change-street-number," +
		"#qas .qas-add-subpremise"
	).val("");
	$(
		"#qas .qas-input-feedback," +
		"#qas [ id $= 'linetwo' ]," +
		"#qas [ id $= 'linethree' ]," +
		"#qas [ id $= 'country' ] "
	).css( "display", "none" );
	$(
		"#qas .qas-street," +
		"#qas .qas-city-region-postcode," +
		"#qas .qas-city," +
		"#qas .qas-region," +
		"#qas .qas-postcode," +
		"#qas .qas-country," +
		"#qas #qas-street-partial-list," +
		"#qas #qas-premises-partial-list," +
		"#qas #qas-multiple-list"
	).html( "" );

	$( "#qas" ).ready( function() {
		callback();
	});

}


/**
* Static class: QasForm
*/

var QasForm = {
	duplicates: new Array()
}

QasForm.init = function() {
	for( var collection in qasElements ) {
		if( qasElements[ collection ].verifiedFlag ) {
			$( "#" + qasFormId ).append(
				'<input name="' + qasElements[ collection ].verifiedFlag + '" type="hidden" value="false"  />' 
			);
		}
	}
}

QasForm.rememberDuplicates = function() {

	QasForm.duplicates = new Array();

	var areDuplicates = function( collectionOne, collectionTwo ) {
		if( QasForm.address( collectionOne ).street.toString() == QasForm.address( collectionTwo ).street.toString() &&
			QasForm.address( collectionOne ).city == QasForm.address( collectionTwo ).city &&
			QasForm.address( collectionOne ).region == QasForm.address( collectionTwo ).region &&
			QasForm.address( collectionOne ).postcode == QasForm.address( collectionTwo ).postcode )
		{
			return true;
		}
		else {
			return false;
		}
	}

	for( var collection in qasElements ) {
		var isOriginal = true;
		for( var original in QasForm.duplicates ) {
			if( areDuplicates( original, collection ) ) {
				QasForm.duplicates[ original ].push( collection );
				isOriginal = false;
				break;
			}
		}
		if( isOriginal ) {
			QasForm.duplicates[ collection ] = new Array();
		}
	}
	
}

QasForm.isDuplicate = function(
	collection )				// number
{
	for( var original in QasForm.duplicates ) {
		if( jQuery.inArray( collection.toString(), QasForm.duplicates[ original ] ) > -1 ) {
			return true;
		}
	}
	return false;
}

QasForm.address = function(
	collection,					// number
	newAddress )				// (QasAddress)
{
	if( newAddress ) {

		// Clear existing contents
		for( var line in qasElements[ collection ].street ) {
			$( "input[ name = " + qasElements[ collection ].street[ line ] + " ]" ).val( "" );
		}
		// It's unlikely that QAS will return any more than two street lines, but just in case...
		if( newAddress.street.length > qasElements[ collection ].street.length ) {
			var newStreet = newAddress.street.join(" ");
			$( "input[ name = " + qasElements[ collection ].street[ 0 ] + " ]" ).val( newStreet );
		}
		else {
			for( line in newAddress.street ) {
				$( "input[ name = " +
					qasElements[ collection ].street[ line ] +
					" ]" ).val( newAddress.street[ line ] );
			}
		}
		$( "input[ name = " + qasElements[ collection ].city + " ]" ).val( newAddress.city );
		$( "[ name = " + qasElements[ collection ].region + " ]" ).val( newAddress.region );
		$( "input[ name = " + qasElements[ collection ].postcode + " ]" ).val( newAddress.postcode );


		if( QasForm.duplicates[ collection ] && QasForm.duplicates[ collection ].length ) {
			for( var duplicateIndex in QasForm.duplicates[ collection ] ) {
				var duplicateCollection = qasElements[ QasForm.duplicates[ collection ][ duplicateIndex ] ];
				// Clear existing contents
				for( var line in duplicateCollection.street ) {
					$( "input[ name = " + duplicateCollection.street[ line ] + " ]" ).val( "" );
				}
				// It's unlikely that QAS will return any more than two street lines, but just in case...
				if( newAddress.street.length > duplicateCollection.street.length ) {
					var newStreet = newAddress.street.join(" ");
					$( "input[ name = " + duplicateCollection.street[ 0 ] + " ]" ).val( newStreet );
				}
				else {
					for( line in newAddress.street ) {
						$( "input[ name = " + duplicateCollection.street[ line ] + " ]" ).val( newAddress.street[ line ] );
					}
				}
				$( "input[ name = " + duplicateCollection.city + " ]" ).val( newAddress.city );
				$( "[ name = " + duplicateCollection.region + " ]" ).val( newAddress.region );
				$( "input[ name = " + duplicateCollection.postcode + " ]" ).val( newAddress.postcode );
			}
		}

	}

	else {
		var formAddress = new QasAddress();
		for( var line in qasElements[ collection ].street ) {
			formAddress.street.push(
				$( "input[ name = " + qasElements[ collection ].street[ line ] + " ]" ).val()
			);
		}
		formAddress.city = $( "input[ name = " + qasElements[ collection ].city + " ]" ).val();
		formAddress.region = $( "[ name = " + qasElements[ collection ].region + " ]" ).val();
		formAddress.postcode = $( "input[ name = " + qasElements[ collection ].postcode + " ]" ).val();
		return formAddress;
	}

}

QasForm.country = function(
	collection )				// number
{
	var country = new QasCountry();
		
	if( qasElements[ collection ].country ) {
		// We're assuming this is a select since otherwise we can't readily validate the user's input
		country.iso = $( "select[ name = " + qasElements[ collection ].country + " ]" ).val();
		country.name = $( "option[ value = " + country.iso + " ]").html();
		if( country.iso == "SGP" && jQuery.inArray( "SGF", qasRegisteredDatasets ) > -1 ) {
			country.dataset = "SGF";
		}
		else if( jQuery.inArray( country.iso, qasRegisteredDatasets ) > -1 ) {
			country.dataset = country.iso;
		}
		return country;
	}
	else {
		country.dataset = qasDefaultDataset;
		return country;
	}
}

QasForm.flagVerified = function(
	collection,					// number
	flag )						// boolean -or- string
{
	if( qasElements[ collection ].verifiedFlag ) {
		$( "input[ name = " + qasElements[ collection ].verifiedFlag + " ]" ).val( flag );
	}
	if( QasForm.duplicates[ collection ] && QasForm.duplicates[ collection ].length ) {
		for( var duplicateIndex in QasForm.duplicates[ collection ] ) {
			var duplicateCollection = qasElements[ QasForm.duplicates[ collection ][ duplicateIndex ] ];
			if( duplicateCollection.verifiedFlag ) {
				$( "input[ name = " + duplicateCollection.verifiedFlag + " ]" ).val( flag );
			}
		}
	}
}

//QasForm.submit = function() {
//	$( "#" + qasFormId ).attr( "onsubmit", "" );
//	// Give the DOM a moment to clear the binding before clicking the submit button
//	setTimeout( function() {
//		$( "#" + qasFormId )[ 0 ].submit.click();
//	}, 10 );
//}
QasForm.submit = function() {
	//$( "#" + qasFormId ).attr( "onsubmit", "" );
	// Give the DOM a moment to clear the binding before clicking the submit button
	//	setTimeout( function() {
	//		$( "#" + qasFormId )[ 0 ].submit.click();
	//	}, 10 );
	//document.frmNewAcct.onsubmit();

	setTimeout(document.getElementById (qasFormId).submit(), 10 );
//	setTimeout( document.frmNewAcct.submit(), 10 );
//	setTimeout( document.qasFormId.submit(), 10 );
//	setTimeout( function() {
//		$( "#" + qasFormId ).submit();
//	}, 10 );

}


/**
* Class: QasVerification
*/

function QasVerification(
	country,					// (string)
	layout )					// (string)
{

	if( layout == undefined ) layout = qasDefaultLayout;
	if( country == undefined ) {
		country = new QasCountry();
		country.dataset = qasDefaultDataset;
	}
	
	this.search = function( address, successCallback, errorCallback ) {
		address = address.replaceUnprintableCharacters();
		var parameters = {
			"action": "search",
			"addlayout": layout,
			"country": country.dataset,
			"promptset": "Default",
			// JavaScript doesn't let you call join on a method parameter, so we have to use an Array prototype
			"searchstring": [ Array.prototype.join.call( address.street, "|" ),
				address.city,
				address.region,
				address.postcode ].join( "|" )
		};
		$.ajax({
			type: "POST",
			url: qasProxyPath,
			data: parameters,
			dataType: "xml",
			success: preparseXml( processBestPractices( successCallback ) ),
			error: errorCallback,
			timeout: qasTimeout
		});
	}

	this.refine = function( moniker, refineText, successCallback, errorCallback ) {
		var parameters = {
			"action": "refine",
			"addlayout": layout,
			"country": country.dataset,
			"moniker": moniker,
			"refinetext": refineText
		};
		$.ajax({
			type: "POST",
			url: qasProxyPath,
			data: parameters,
			dataType: "xml",
			success: preparseXml( processBestPractices( successCallback ) ),
			error: errorCallback,
			timeout: qasTimeout
		});
	}
	
	var preparseXml = function( callback ) {
		return function( xml ) {
			var results = new QasResults();
			results.verificationLevel = $( xml ).find( "verifylevel" ).text();
			if( $( xml ).find( "picklist" ).length > 0 ) {
				results.picklist = new Array();
				$( xml ).find( "picklistitem" ).each( function() {
					var picklistItem = new QasPicklistItem();
					picklistItem.unresolvableRange = eval( $( this ).find( "unresolvablerange" ).text().toLowerCase() );
					picklistItem.fullAddress = eval( $( this ).find( "fulladdress" ).text().toLowerCase() );
					picklistItem.addressText = $( this ).find( "addresstext" ).text();
					picklistItem.postcode = $( this ).find( "postcode" ).text();
					picklistItem.moniker = $( this ).find( "moniker" ).text();
					results.picklist[ results.picklist.length ] = picklistItem;
				});
			}
			else if( $( xml ).find( "address" ).length > 0 ) {
				results.address = new QasAddress();
				results.address.street[ 0 ] = $( xml ).find( "lineone" ).text();
				if( $( xml ).find( "linetwo" ).text() != "" ) {
					results.address.street[ 1 ] = $( xml ).find( "linetwo" ).text();
				}
				if( $( xml ).find( "linethree" ).text() != "" ) {
					results.address.street[ 2 ] = $( xml ).find( "linethree" ).text();
				}
				results.address.city = $( xml ).find( "city" ).text();
				results.address.region = $( xml ).find( "state" ).text();
				results.address.postcode = $( xml ).find( "zip" ).text();
				results.address.subpremiseFlag = eval( $( xml ).find( "apartment" ).text().toLowerCase() );
				// Leave the DPV flag null if the proxy doesn't return one or indicates it isn't set up
				var dpvFlag = $( xml ).find( "DPVStatus" ).text();
				if( dpvFlag != "" && dpvFlag != "DPVNotConfigured" ) {
					results.address.dpvFlag = $( xml ).find( "DPVStatus" ).text();
				}
			}
			callback( results );
		}
	}

	var processBestPractices = function( callback ) {
		return function( qasResults ) {
			// Set ready to false if further searches are required to avoid flashing multiple dialogs
			var ready = true;
			var picklistItemsScreened = 0;
			var screenedPicklist = new Array();
			var screenedPicklistResults = new Array();
			// Screen picklists in Multiple dialogs before displaying them to avoid teasing users with
			// DPV invalid picklist items
			var picklistItemScreened = function( picklistItem, screeningResults, picklistSize ) {
				if( screeningResults.verificationLevel == "Verified" ||
					screeningResults.verificationLevel == "InteractionRequired" )
				{
					screenedPicklist.push( picklistItem );
					screenedPicklistResults.push( screeningResults );
				}
				if( ++picklistItemsScreened == picklistSize ) {
					if( screenedPicklist.length == 0 ) {
						processedResults.verificationLevel = "None";
						callback( processedResults );
					}
					else if( screenedPicklist.length == 1 ) {
						callback( screenedPicklistResults[ 0 ] );
					}
					else {
						for( item in processedResults.picklist ) {
							if( processedResults.picklist[ item ].unresolvableRange ) {
								processedResults.verificationLevel = "StreetPartial";
								break;
							}
						}
						processedResults.picklist = screenedPicklist;
						callback( processedResults );
					}
				}
			}
			if( qasResults.verificationLevel == null ) return false;
			var processedResults = jQuery.extend( true, {}, qasResults );
			if( processedResults.address ) {
				// You can replicate the kinds of artifacts we're removing here by doing a
				// single line search for "Apt" w/ USA or CAN data
				if( processedResults.address.hasBuildingArtifact() ) {
					processedResults.address.street.shift();
					processedResults.address.street.push( "" );
				}
				if( country.dataset == "USA" ) {
					switch( processedResults.address.dpvFlag ) {
						case "DPVNotConfirmed":
							processedResults.verificationLevel = "StreetPartial";
							break;
						case "DPVConfirmedMissingSec":
							processedResults.verificationLevel = "InteractionRequired";
							break;
					}
				}
				// Downgrade Canadian addresses with a subpremise flag (typically triggered by
				// the presence of QAS CAN elements P21, O11, O12, O13 or O21)
				if( processedResults.address.subpremiseFlag &&
					country.dataset == "CAN" &&
					!processedResults.address.containsDash() )
				{
					processedResults.verificationLevel = "PremisesPartial";
				}
			}
			switch( processedResults.verificationLevel ) {
				case "Verified":
					break;
				case "InteractionRequired":
					break;
				case "StreetPartial":
					if( country.dataset == "GBR" ||
						country.dataset == "SGF" ||
						country.dataset == "SGP" )
					{
						processedResults.verificationLevel = "Multiple";
					}
					break;
				case "PremisesPartial":
					// IE6 and QasPicklistItem don't play nice with for...in loops, so iterate traditionally
					if( processedResults.picklist &&
						processedResults.picklist.length == 1 &&
						processedResults.picklist[ 0 ].fullAddress )
					{
						ready = false;
						var singleFullAddress = new QasVerification( country );
						singleFullAddress.refine( processedResults.picklist[ 0 ].moniker, "",
							function( response ) {
								if( response.verificationLevel == "Verified" ) {
									response.verificationLevel = "InteractionRequired";
								}
								callback( response );
							}, function() {
								if( window.console && console.log ) {
									console.error( Date() + ": " + request + ", " + textStatus + ", " + errorThrown );
								}
							});
					}
					if( country.dataset == "GBR" ||
						country.dataset == "SGF" ||
						country.dataset == "SGP" )
					{
						processedResults.verificationLevel = "Multiple";
					}
					break;
				case "Multiple":
					// Picklist screening disabled for performance reasons
					/* if( country.dataset == "USA" ) {
						ready = false;
						var createCallback = function( callbackItem ) {
							return function( response ) {
								picklistItemScreened( processedResults.picklist[ callbackItem ],
									response,
									processedResults.picklist.length );
							}
						}
						for( item in processedResults.picklist ) {
							processedResults.picklist[ item ].screening = new QasVerification( country );
							processedResults.picklist[ item ].screening.refine(
								processedResults.picklist[ item ].moniker,
								"",
								// We have to create this function on the fly since "item" will evaluate to its
								// final value in the form loop by the time the callback is executed
								createCallback( item ),
								function() {
									if( window.console && console.log ) {
										console.error( Date() + ": " + request + ", " + textStatus + ", " +
											errorThrown );
									}
								});
						}
					}
					else { */
						for( var item = 0; item < processedResults.picklist.length; item++ ) {
							if( processedResults.picklist[ item ].unresolvableRange ) {
								processedResults.verificationLevel = "StreetPartial";
								break;
							}
						}
					/* } */
					break;
				case "None":
					break;
			}
			if( ready ) callback( processedResults );
		}
	}

}


/**
* Abstract data types
*/

function QasResults() {
	return {
		"verificationLevel": null,
		"picklist": null,
		"address": null
	}
}

function QasPicklistItem() {
	return {
		"unresolvableRange": null,
		"fullAddress": null,
		"addressText": null,
		"postcode": null,
		"moniker": null
	}
}

function QasAddress() {
	return {
		"street": new Array(),
		"city": null,
		"region": null,
		"postcode": null,
		"subpremiseFlag": null,
		"dpvFlag": null,
		"containsDash": function() {
			if( this.street.toString().search( /-/ ) > -1 ) {
				return true;
			}
			else {
				return false;
			}
		},
		// Canada Post and USPS data contain addresses with artifacts wherein the building
		// descriptors are on their own in line one
		"hasBuildingArtifact": function() {
			var buildingDescriptors = new Array(
				"Business",
				"Building",
				"Bldg",
				"Business Bldg",
				"Business Bldg.",
				"Business Building",
				"Business Complex",
				"Business Plaza",
				"Immeuble Commercial",
				"Apt",
				"Apartment",
				"Apartment Bldg",
				"Apartment Bldg.",
				"Apartment Building",
				"Immeuble a Appartements",
				"Townhomes",
				"Townhouses"
			);
			if( jQuery.inArray( this.street[ 0 ], buildingDescriptors ) > -1 ) {
				return true;
			}
			else {
				return false;
			}
		},
		// Match specific unicode characters and convert them to their codes to ensure that
		// they will be passed and interpreted correctly by QAS web services
		"replaceUnprintableCharacters": function() {
			var processString = function( string ) {
				if( typeof string == "string" ) {
					var unprintableCharacters = /[\u0026\u00bf-\u00ff]/;
					while( string.search( unprintableCharacters ) != -1 ) {
						var characterIndex = string.search( unprintableCharacters );
						string = string.substring( 0, characterIndex ) +
							'%' + string.charCodeAt( characterIndex ) +
							string.substring( characterIndex + 1, string.length );
					}
				}
				return string;
			}
			var processedAddress = new QasAddress();
			for( var line in this.street ) {
				processedAddress.street[ line ] = processString( this.street[ line ] );
			}
			processedAddress.city = processString( this.city );
			processedAddress.region = processString( this.region );
			processedAddress.postcode = processString( this.postcode );
			return processedAddress;
		}
	}
}

function QasCountry() {
	return {
		"iso": null,
		"name": null,
		"dataset": null
	}
}

