Skip to content

Instantly share code, notes, and snippets.

@bowheart
Last active October 20, 2016 15:10
Show Gist options
  • Save bowheart/4c66607ab1a6ff8de9fb to your computer and use it in GitHub Desktop.
Save bowheart/4c66607ab1a6ff8de9fb to your computer and use it in GitHub Desktop.
Filling out forms can be tedious stuff. This library will autofill city and state fields in a form when the user enters their zipcode. Makes use of the Google Maps API.
/**
* Another library by Joshua Claunch
* https://github.com/bowheart
* https://gist.github.com/bowheart
*
* A jQuery-dependent secret intelligence library. :O
*/
(function() {
var Autofiller = function(zipInput, cityInput, stateInput, phoneInput) {
this.zipInput = zipInput;
this.cityInput = cityInput;
this.stateInput = stateInput;
this.phoneInput = phoneInput;
this.geocoder = new google.maps.Geocoder();
this.bindInput();
};
Autofiller.prototype = {
autofill: function(cityVal, stateVal) {
this.autofillCity(cityVal);
this.autofillState(stateVal);
this.autoFocus();
},
autofillCity: function(cityVal) {
this.cityInput
.val(cityVal)
.trigger('validate');
},
autofillState: function(stateVal) {
this.stateInput
.val(this.statePrefix + stateVal)
.trigger('validate');
},
autoFocus: function() {
if (!this.jumpFocus) return;
this.focusInput.focus();
},
bindInput: function() {
this.zipInput[0].addEventListener('input', this.handleInput.bind(this));
},
fetchZipInfo: function() {
var self = this;
self.geocoder.geocode({address: self.val}, function(results, status) {
if (!results || status.toLowerCase() !== 'ok') return; // It didn't work. Don't try to do anything.
var result = self.parseResults(results); // reduce to a single result
if (!result) return; // No correct-country result. Stop execution.
self.validateZipInfo(result);
});
},
handleInput: function() {
if (!this.zipIsValid()) return;
this.fetchZipInfo();
},
parseResults: function(results) {
var self = this;
return results.filter(function(nextResult) {
return nextResult.address_components.pop().short_name === self.country; // filter out non-correct-country results
})[0]; // assume we want the first correct-country result (a pretty safe assumption, I think?)
},
parseCityVal: function(zipInfo) {
return zipInfo.address_components[1].short_name;
},
parseStateVal: function(zipInfo) {
return zipInfo.address_components.pop().short_name;
},
validCondition: function(cityVal, stateVal) {
return cityVal && stateVal;
},
validateZipInfo: function(zipInfo) {
var cityVal = this.parseCityVal(zipInfo),
stateVal = this.parseStateVal(zipInfo);
if (!this.validCondition(cityVal, stateVal)) return; // The result isn't what's expected. Stop execution.
this.autofill(cityVal, stateVal);
},
zipIsValid: function() {
return this.val.length >= this.length;
},
get focusInput() {
return this.newFocusInput || this.phoneInput;
},
get val() {
return this.zipInput.val();
},
jumpFocus: true,
length: 5,
statePrefix: ''
};
var USAutofiller = function() { Autofiller.apply(this, arguments); };
USAutofiller.prototype = Object.create(Autofiller.prototype, {
country: { value: 'US' }
});
var CAAutofiller = function() { Autofiller.apply(this, arguments); };
CAAutofiller.prototype = Object.create(Autofiller.prototype, {
country: { value: 'CA' },
length: { value: 7 }
});
var GBAutofiller = function() { Autofiller.apply(this, arguments); };
GBAutofiller.prototype = Object.create(Autofiller.prototype, {
country: { value: 'GB' },
jumpFocus: { value: false },
length: { value: 6 },
parseStateVal: { value: function(zipInfo) {
var optionText = zipInfo.address_components.pop().short_name.trim().toLowerCase(),
stateOption = this.stateInput.find('option').filter(function() {
return this.innerHTML.trim().toLowerCase() === optionText;
});
if (!stateOption.length) {
this.validCondition = function(cityVal) {
return !!cityVal;
};
this.autofillState = function() {}; // do nothing
return '';
}
return stateOption[0].value;
} },
});
var AUAutofiller = function() { Autofiller.apply(this, arguments); };
AUAutofiller.prototype = Object.create(Autofiller.prototype, {
country: { value: 'AU' },
length: { value: 4 },
statePrefix: { value: 'AU-' },
autofillCity: { value: function(cityVal) {
// do nothing.
} },
focusInput: { get: function() {
return this.cityInput;
} }
});
var autofillersMap = {
USAutofiller: USAutofiller,
CAAutofiller: CAAutofiller,
GBAutofiller: GBAutofiller,
AUAutofiller: AUAutofiller
};
var factory = function() {
// create the correct Autofiller descendant (one for each form on the page) based on the site's country.
var zipInputs = $('input').filter(function() { return /zip/i.test(this.name); }),
tempStateInput = zipInputs.first().parents('form').find('select').filter(function() { return /state/i.test(this.name); }),
country = tempStateInput.find('option')[1].value.slice(0, 2).toUpperCase()
if (country === 'AB') country = 'CA'; // A hack... AB == Alberta ... Canada
else if (country === 'AL') country = 'US'; // A hack... AL == Alabama ... US
var countryAutofiller = autofillersMap[country + 'Autofiller'];
zipInputs.each(function() {
var zipInput = $(this),
cityInput = zipInput.parents('form').find('input').filter(function() { return /city/i.test(this.name); }),
stateInput = zipInput.parents('form').find('select').filter(function() { return /state/i.test(this.name); }),
phoneInput = zipInput.parents('form').find('input[name="phone"]').add(zipInput.parents('form').find('input[name="card"]'));
new countryAutofiller(zipInput, cityInput, stateInput, phoneInput);
});
};
var loadGoogleMaps = function(onload) {
var script = document.createElement('script');
$('head').append(script);
script.onload = onload;
script.src = 'https://maps.google.com/maps/api/js?key=AIzaSyCKz4Jc4FO96-0oLCWxLM5_aQhWhWayg3o';
};
$(function() {
if ($('script').filter(function() { return this.src.match(/google\.com\/maps\/api\/js/); }).length) {
return factory();
}
loadGoogleMaps(factory.bind(factory));
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment