﻿// JScript File

/**
* 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 = "theForm",
	
	// 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 = "USA3 TitleCase Retention DPV",

	// 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 = "/prowebproxy.ashx",

	// AJAX timeout (in milliseconds)
	qasTimeout = 55000,

	// All form elements are selected by name
	qasElements = [
		{
			"street": [ 'ShippingAddress' ],
			"city": "ShippingCity",
			"region": "ShippingState",
			"postcode": "ZipCode",
			// 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": "shipping_address_verified"
		}
	];
	
	qasEmail = "EmailAddress";
	
	var flagHideSubmitButtonRatherThanDisable = false;

/**
* Initialization
*/

$( document ).ready( function() {
	QasForm.init();
});

function qasVerify( submitWhenDone, collection ) {
	if( typeof submitWhenDone == "undefined" ) submitWhenDone = false;
	if( typeof collection == "undefined" ) 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 ) {
	    if ( window.EnableSubmitButton ) EnableSubmitButton();
	    var form = document.getElementById(qasFormId);
	    var qasElement = qasElements[0];
	    var address2 = form['Address2'];
	    switch (response.verifylevel)
	    {
	        case "Verified":
	            if ( response.address ) copyUpdatedAddressToForm(response);
	            QasForm.submit();
	            break;
	        case "InteractionRequired":
	            if ( response.address )
	            {
	                copyUpdatedAddressToForm(response);
	                alert('The address you entered has been altered to match the address on file with the US Postal service.  ' +
	                    'Please verify and resubmit.');
	            }
	            break;
	        case "PremisesPartial":
	        case "StreetPartial":
	        case "Multiple":
	        case "VerifiedPlace":
	        case "VerifiedStreet":
	            if ( address2 && address2.value == '' )
	            {
	                alert('Please specify additional information in the second address line such as a suite #, building # or name, apartment #, etc.');
	                address2.focus();
	                return;
	            }
	            if (response.address) copyUpdatedAddressToForm(response);
	            QasForm.submit();
	            break;
	        default:
	            alert('The address you specified is unknown.');
	            break;
	    }
	    form[qasElement['street']].focus();
	    }, 
	    function( request, textStatus, errorThrown ) {
	        if ( window.EnableSubmitButton ) EnableSubmitButton();
	        alert('An unknown error occurred.  Please try again.');
	    });
	$( "body, input, select" ).css( "cursor", "auto" );
	return false;
}

function copyUpdatedAddressToForm(response)
{
    var form = document.getElementById(qasFormId);
    var qasElement = qasElements[0];
    form[qasElement['street']].value = response.address.lineone;
    form[qasElement['city']].value = response.address.city;
    form[qasElement['region']].value = response.address.state;
    form[qasElement['postcode']].value = response.address.zip;
}

/**
* 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() {
		$( "#SubmitButton" )[ 0 ].click();
	}, 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: "json",
			success: 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: "json",
			success: successCallback,
			error: errorCallback,
			timeout: qasTimeout
		});
	}
}

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
	}
}

function validateWithServer()
{
    DisableSubmitButton();
    var form = document.getElementById(qasFormId);
    var address = form[qasEmail].value;
 		$.ajax({
			type: "POST",
			url: "/checkEmail.ashx",
			data: {'address': address},
			dataType: "json",
			success: function(response)
			{
			    if ( response.valid )
			    {
			        qasVerify(true);
			    }
			    else
			    {
			        if ( window.EnableSubmitButton ) EnableSubmitButton();
			        alert('Email address not valid.  Reason: ' + response.status_message);
			        var form = document.getElementById(qasFormId);
			        form[qasEmail].focus();
			    }
			},
			error: function()
			{
			    if ( window.EnableSubmitButton ) EnableSubmitButton();
			    alert('An unknown error occurred.  Please try again.');
			},
			timeout: qasTimeout
		});
    return false;
}

function EnableSubmitButton()
{
    var buttonName = flagHideSubmitButtonRatherThanDisable ? 'SubmitButtonUser' : 'SubmitButton';
    var button = document.getElementById(buttonName);
    if ( flagHideSubmitButtonRatherThanDisable )
    {
        if ( button )
        {
            button.style.visibility = "visible";
        }  
    }
    else
    {
        if ( button )
        {
            button.value = button.original_value
            button.disabled = false;
        }
    }
}

function DisableSubmitButton()
{
    var buttonName = flagHideSubmitButtonRatherThanDisable ? 'SubmitButtonUser' : 'SubmitButton';
    var button = document.getElementById(buttonName);
    if ( flagHideSubmitButtonRatherThanDisable )
    {
        button.style.visibility = "hidden";
    }
    else
    {
        button.original_value = button.value;
        button.value = 'Please wait...';
        button.disabled = true;
    }
}

