/*
Class: AddyAjax
Class that takes a form and validates the address against the USPS.
Author:
Eric Clemmons
License:
MIT-style license
Copyright:
Copyright (c) 2007 Eric Clemmons & WhiteFence
Script Dependencies:
- JsonP.js
Mootools Dependencies (v963):
- Source/Core/Core.js
- Source/Core/Browser.js
- Source/Native/Array.js
- Source/Native/String.js
- Compatibility/Class/Class.js
Example #1:
For backwards-compatible use on a site calling "ZipCheck()" inside of fields.
Validation occurs when "onKeyUp" is triggered in the 'zipcode" field by default.
(start code)
(end code)
This automatically instantiates and validates the following fields.
o streetAddress
o apartmentNumber
o city
o state
o zipcode
Example #2:
For backwards-compatible use on a site calling "ZipCheck()" and passing the fields.
(start code)
(end code)
This is primarily for fields that don't use the default names.
Example #3:
This is the proper use of where you pass in a form to validate.
(start code)
// Pass the form name ...
checkIt = new AddyAjax('selfReg');
// or pass the object ...
checkIt = new AddyAjax(document.selfReg);
// or pass by ID
checkIt = new AddyAjax('selfReg');
(end code)
This will automatically pass all of the form fields to the validator.
Example #4:
Another example of proper use, but with a custom onComplete function to run
after completion.
(start code)
checkIt = new AddyAjax('selfReg', {
onStart: function() {
alert("I just started!");
},
onComplete: function() {
alert("I Successfully Completed!");
}
});
(end code)
This is useful for beginning/ending visual indicators that the validation is occurring.
*/
window.addEvent('domready', AutoAddyAjax);
function AutoAddyAjax() {
var form = $E('form[name=selfReg]');
if(!form) return false;
var fields = ['streetAddress', 'apartmentNumber', 'city', 'state', 'zipcode'];
var address = new Hash();
$A(fields).each(function(field) {
var input = form.getElement('[name='+field+']');
if(!input) {
input = new Element('input', {
'type': 'hidden',
'name': field
});
form.adopt(input);
}
address.set(field, input);
});
address.zipcode.addEvent('keyup', ZipCheck.pass([address.streetAddress,
address.apartmentNumber,
address.city,
address.state,
address.zipcode]));
}
var AddyAjax = new Class({
form: null,
address: null,
options: {
trustUSPS: false,
timeOut: 5000,
animate: true,
autoValidate: true,
url: 'http://www.whitefence.com/scripts/server/gateway.php',
onStart: function() {},
onComplete: function() {},
onFailure: function() {},
progressURL: 'http://www.whitefence.com/images/loaders/arrows/green_on_white.gif',
progressPosition: [-18, 2]
},
/*
Function: initialize
Internal function (as referenced in Example #1, Example #2, Example #3, and Example #4).
Parameters:
form - This may either be empty (in conjunction with _options_), a name/id, or an element.
If empty, form is automatically set to _selfReg_ (if it exists). If an object is passed,
the object is treated as _options_.
options - If you pass the options as the first parameter for , this must be *empty*!
Otherwise, this passes _onStart_ and _onComplete_, which are both functions.
See Also:
<_setForm>
*/
initialize: function(form, options) {
if(!!form && $type(form) == "object") this.setOptions(form);
if(!!options) this.setOptions(options);
if(!!form && $type(form) != "object") this._setForm(form);
},
/*
Function: _setForm
Internal function used find the form referenced by .
Parameters:
form - Name/ID of form, or direct object.
See Also:
<_findFields>
*/
_setForm: function(form) {
switch($type(form)) {
case 'string':
this.form = $(form) || document.getElementsByName(form)[0];
break;
case 'element':
this.form = form;
break;
}
$(this.form); // Ensures we extend with Mootools
if(!this._findFields()) return false;
},
/*
Function: addFields
Function used to manually assign event handlers to specified fields.
This is really used when the user assigns the address fields names
that don't contain _street, apartment, city, state, or zip_.
Parameters:
streetAddress - Street Address to be validated.
apartmentNumber - Apartment/Suite to be validated.
city - City to be validated.
state - 2-letter abbreviation (TX, WA, etc.)
zipcode - 5-digit zipcode without spcaes or dashes.
See Also:
<_watchField>
*/
addFields: function(streetAddress, apartmentNumber, city, state, zipcode) {
var array = [streetAddress, apartmentNumber, city, state, zipcode];
this.address = {};
for(var i = 0; i < array.length; i++) $(array[i]).removeEvents();
this.address.streetAddress = this._watchField($(streetAddress), 'blur');
this.address.apartmentNumber = this._watchField($(apartmentNumber), 'blur');
this.address.city = this._watchField($(city), 'blur');
this.address.state = this._watchField($(state), 'change');
this.address.zipcode = this._watchField($(zipcode), 'keyup');
},
/*
Function: _findFields
Internal function called by <_setForm> to automatically locate address fields.
See Also:
<_findFieldWith>, <_watchField>, & <_addRequired>
*/
_findFields: function() {
if(!!!this.form) return false;
this.address = {};
this.address.streetAddress = this._watchField(this._findFieldWith('street') || this._findFieldWith('address'), 'blur');
this.address.apartmentNumber = this._watchField(this._findFieldWith('apartment') || this._findFieldWith('apt'), 'blur');
this.address.city = this._watchField(this._findFieldWith('city'), 'blur');
this.address.state = this._watchField(this._findFieldWith('state'), 'change');
this.address.zipcode = this._watchField(this._findFieldWith('zip'), 'keyup');
return true;
},
/*
Function: _findFieldWith
Internal function used to find a field with the name matching a string.
Parameters:
text - String to match against the _name_ attribute of input fields.
Returns:
First match is returned, otherwise *false* is returned.
*/
_findFieldWith: function(text) {
var inputs = this.form.getElements('input');
return this.find(text, inputs);
},
/*
Function: _watchField
Internal function to assign an event handler to a particular object.
is called when the event is triggered. All existing events are
removed prior to assign the handler, to ensure there's no doubling.
Parameters:
input - The element with the event handler
trigger - Event trigger, such as _blur_, _change_, _keyup_, etc.
*/
_watchField: function(input, trigger) {
if(!!!input) return false;
if(this.options.autoValidate)
//input.removeEvents().addEvent(trigger, this.validate.bind(this));
input.addEvent(trigger, this.validate.bind(this));
return input;
},
/*
Function: _checkFields
Internal function to ensure all required fields have at least *5* characters.
See Also:
<_addRequired>
Returns:
Returns *true* if all required fields have at least *5* characters and there
has been a value change in at least *1* input since previous check. Otherwise,
*false*.
*/
_changed: function() {
var changed = false;
for(var field in this.address) {
if(this.address[field].oValue != this.address[field].value) {
var changed = true; this.address[field].oValue = this.address[field].value;
}
}
if(this.address.zipcode.value.length < 5 || $empty(this.address.streetAddress.value))
return false;
else
return changed;
},
/*
Function: _updateFields
Internal function to update form fields with validated address.
Parameters:
addy - Object passed by Skinner containing _streetAddress, aptNumber,
city, state, & zipcode_.
*/
_updateFields: function(addy) {
//if(!this._changed()) return false; // Fields have been changed since AJAX
for(var field in addy.fields)
if(!$defined(addy.fields[field])) addy.fields[field] = '';
this.address.streetAddress.value = addy.fields.streetAddress.value;
this.address.apartmentNumber.value = addy.fields.aptNumber.value;
this.address.city.value = addy.fields.city.value;
this.address.state.value = addy.fields.state.value;
this.address.zipcode.value = addy.fields.zipcode.value;
if(this.options.animate)
for(input in this.address)
if(!!this.address[input])
new Fx.Style(this.address[input], 'background-color', {duration: 750}).start("#88FF88", "#FFFFFF");
},
/*
Function: _showProgress
Called internally to display a progress indicator while validating. Creates
an image with the ID *addyajax_progress* position relative to the zipcode.
Parameters:
Relies on defaults set in Options when instantiating.
progressURL - Image URL (relative to calling page). Default is
*common/scripts/ajax-loader.gif*.
progressPosition - Array of X & Y values to add/subtract to position
of progress indicator. Defaults to *[0, 0]*.
See Also:
<_hideProgress>,
*/
_showProgress: function() {
this.options.onStart();
if(!this.options.animate) return false;
if($('addyajax_progress')) $('addyajax_progress').remove();
var zip = this.address.zipcode; //this.find("zip", this.fields)
if(!zip) return false;
var coords = zip.getCoordinates();
var image = new Element('img', {
'src': this.options.progressURL,
'id': 'addyajax_progress'
});
image.setStyles({
'position': 'absolute',
'top': coords.top + this.options.progressPosition[1],
'left': coords.left + coords.width + this.options.progressPosition[0],
'z-index': 1000
});
image.injectInside(document.body);
if(this.options.animate)
var fade = new Fx.Style(image, 'opacity').set(0).start(1);
},
find: function(needle, haystack) {
for(var i = 0; i < haystack.length; i++)
if(haystack[i].getProperty('name').toLowerCase().contains(needle) ||
haystack[i].getProperty('id').toLowerCase().contains(needle) ||
haystack[i].getProperty('class').contains(needle)) return haystack[i];
return false;//new Element('input', { 'class': needle });
},
/*
Function: _hideProgress
Called internally by to remove the image with ID *addyajax_progress*
created by <_showProgress>.
*/
_hideProgress: function() {
if(!this.options.animate) return false;
var fade = new Fx.Style('addyajax_progress', 'opacity', {
onComplete: function() {
$('addyajax_progress').remove.attempt();
}
}).start(0);
return true;
},
/*
Function: validate
Called internally & externally to pass an address to _Skinner_ and update the
form's fields accordingly.
See Also:
<_checkFields> & <_updateFields>.
*/
validate: function() {
var addy = {
'streetAddress': this.address.streetAddress.value,
'apartmentNumber': this.address.apartmentNumber.value,
'zipcode': this.address.zipcode.value,
'json': 'true'
}
if(!this._changed()) return false;
this._showProgress();
var query = {
url: 'http://skinner2.qcorpssecure.com/cgi-bin/skinner.dll',
query: addy,
method: 'get'
};
var req = new JsonP(this.options.url, {
method: 'get',
autoCancel: true,
queryString: "gateway_call="+Json.encode(query),
onFailure: function() {
this._hideProgress();
this.options.onFailure("Failed to Validate Address.");
}.bind(this),
onComplete: function(addy) {
this._hideProgress();
if(addy.fields.errorcode.value != 0 && this.options.trustUSPS) return this.options.onFailure(responses.response.guess);
this._updateFields(addy);
this.options.onComplete(addy);
}.bind(this)
}).request();
}
}).implement(new Options);
/*
Function: ZipCheck
Function called by javascript in older web applications to validate address.
It can be called with or without parameters. If ZipCheck is called, it
instantiates on the first attempt. If _streetAddress_ is specified,
it calls .
>
or
> onkeyup="ZipCheck('address', 'apartment', 'city', 'state', 'zipcode');"
> />
Parameters:
streetAddress - Street Address to be validated.
apartmentNumber - Apartment/Suite to be validated.
city - City to be validated.
state - 2-letter abbreviation (TX, WA, etc.)
zipcode - 5-digit zipcode without spcaes or dashes.
See Also:
& .
*/
function ZipCheck(streetAddress, apartmentNumber, city, state, zipcode) {
var street = $(streetAddress);
if($defined(street.addyCheck)) return;
street.addyCheck = true;
var tmp = new AddyAjax({ animate: false }).addFields(streetAddress, apartmentNumber, city, state, zipcode);
}