/**
* 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 = qas_form_id,

    //qasOk = false;

	// 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",		
		"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 = "../QasProWeb/prowebproxy.aspx",

	// 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": [qas_street_id],
		    "city": qas_city_id,
		    "region": qas_region_id,
		    "postcode": qas_zip_id,
			// Set to false if no country field exists
		    "country": qas_country_id,
			// Flag added to form by QasForm.init(); set to false to submit without flag
			"verifiedFlag": false
		}
	];


/**
* Initialization
*/

$( document ).ready( function() {
	QasForm.init();
	if( qasPromptUser ) QasDialog.init();
});


/**
* Main function
*/

function qasVerify(submitWhenDone, collection) {
    //if (qasOk) return true;    
	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();
	        alert("Sorry, we can't communicate with the address server at the last submit. Please try again or call us.");
	    }
	    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("../QasProWeb/qas-interactionrequired.html", dialogReady());
		$("#qas-streetpartial").load("../QasProWeb/qas-streetpartial.html", dialogReady());
		$("#qas-premisespartial").load("../QasProWeb/qas-premisespartial.html", dialogReady());
		$("#qas-multiple").load("../QasProWeb/qas-multiple.html", dialogReady());
		$("#qas-none").load("../QasProWeb/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[ id = " + 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[ id = " + qasElements[ collection ].street[ 0 ] + " ]" ).val( newStreet );
		}
		else {
			for( line in newAddress.street ) {
				$( "input[ id = " +
					qasElements[ collection ].street[ line ] +
					" ]" ).val( newAddress.street[ line ] );
			}
		}
		$( "input[ id = " + qasElements[ collection ].city + " ]" ).val( newAddress.city );
		$( "[ id = " + qasElements[ collection ].region + " ]" ).val( newAddress.region );
		$( "input[ id = " + 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[ id = " + 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[ id = " + duplicateCollection.street[ 0 ] + " ]" ).val( newStreet );
				}
				else {
					for( line in newAddress.street ) {
						$( "input[ id = " + duplicateCollection.street[ line ] + " ]" ).val( newAddress.street[ line ] );
					}
				}
				$( "input[ id = " + duplicateCollection.city + " ]" ).val( newAddress.city );
				$( "[ id = " + duplicateCollection.region + " ]" ).val( newAddress.region );
				$( "input[ id = " + duplicateCollection.postcode + " ]" ).val( newAddress.postcode );
			}
		}

	}

	else {
		var formAddress = new QasAddress();
		for( var line in qasElements[ collection ].street ) {
			formAddress.street.push(
				$( "input[ id = " + qasElements[ collection ].street[ line ] + " ]" ).val()
			);
		}
		formAddress.city = $( "input[ id = " + qasElements[ collection ].city + " ]" ).val();
		formAddress.region = $( "[ id = " + qasElements[ collection ].region + " ]" ).val();
		formAddress.postcode = $( "input[ id = " + 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
        // PH
        var ph_aId = $("select[ id = " + qasElements[collection].country + " ]").val();
        switch (ph_aId) {
            case "phsc":
                country.iso = "CAN";
                break;
            case "phsu":
                country.iso = "USA";
                break;
            case "ppls":
                country.iso = "GBR";
                break;
        }
        country.name = $("option[ value = " + ph_aId + " ]").html();
        //country.iso = $("select[ id = " + 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", "");
    //document.getElementById("aspnetForm").onsubmit = null;
    // Give the DOM a moment to clear the binding before clicking the submit button
qasOk = true;
QasDialog.close()    
    //setTimeout(function() {
    //$("#" + qasFormId)[0].ctl00_ContentPlaceHolder1_btnSubmit.click();
    $("#" + qas_submit_id).click();
    //$("#" + qasFormId)[0].submit();
    //WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("ctl00$ContentPlaceHolder1$btnSubmit", "", true, "", "", false, true));
    //}, 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
	}
}