Skip to content

Commit 0f2c513

Browse files
committed
refactor(js): convert auto complete to hot control
fixes #108
1 parent b2f57a6 commit 0f2c513

2 files changed

Lines changed: 111 additions & 163 deletions

File tree

formwidgets/AddressFinder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public function getReverseStreetNumber()
115115
public function loadAssets()
116116
{
117117
$apiKey = Setting::get('google_maps_key');
118-
$this->addJs('//maps.googleapis.com/maps/api/js?libraries=places&callback=initMap&key='.$apiKey);
118+
$this->addJs('//maps.googleapis.com/maps/api/js?libraries=places&key='.$apiKey, ['async' => true, 'defer' => true]);
119119
$this->addJs('js/location-autocomplete.js', 'core');
120120
}
121121
}
Lines changed: 110 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
2-
* Location Autocomplete plugin
2+
* Location Autocomplete control
33
*
44
* Data attributes:
5-
* - data-control="location-autocomplete" - enables the plugin on an element
5+
* - data-control="location-autocomplete" - enables the control on an element
66
* - data-input-street="#locationStreet" - input to populate with street
77
* - data-input-city="#locationCity" - input to populate with city
88
* - data-input-county="#locationCounty" - input to populate with county
@@ -14,176 +14,158 @@
1414
* - data-input-longitude="#locationLongitude" - input to populate with longitude
1515
*
1616
* JavaScript API:
17-
* $('input#addressAutocomplete').locationAutocomplete({ inputCountry: '#locationCountry' })
17+
* oc.fetchControl(element, 'location-autocomplete')
1818
*
1919
* Dependences:
20-
* - Google maps (http://maps.googleapis.com/maps/api/js?libraries=places&sensor=false&callback=initMap&key=...)
21-
*
22-
* Example markup:
23-
*
24-
<input
25-
type="text"
26-
class="form-control"
27-
data-control="location-autocomplete"
28-
data-country-restriction="us,ch"
29-
data-input-street="#inputStreet"
30-
data-input-city="#locationCity"
31-
data-input-county="#locationCounty"
32-
data-input-state="#locationState"
33-
data-input-zip="#locationZip"
34-
data-input-country="#locationCountry"
35-
/>
36-
37-
<input type="text" name="street" value="" id="inputStreet" />
38-
<input type="text" name="city" value="" id="locationCity" />
39-
<input type="text" name="county" value="" id="locationCounty" />
40-
<input type="text" name="state" value="" id="locationState" />
41-
<input type="text" name="zip" value="" id="locationZip" />
42-
<input type="text" name="country" value="" id="locationCountry" />
43-
*
20+
* - Google Maps Places API
4421
*/
22+
oc.registerControl('location-autocomplete', class extends oc.ControlBase {
23+
init() {
24+
this.autocomplete = null;
25+
}
4526

27+
connect() {
28+
this.initAutocomplete();
4629

47-
+function ($, map) { "use strict";
48-
49-
// LOCATION AUTOCOMPLETE CLASS DEFINITION
50-
// ============================
51-
52-
var LocationAutocomplete = function(element, options) {
53-
this.options = options
54-
this.$el = $(element)
55-
56-
// Init
57-
this.init()
30+
// Prevent ENTER from submitting form
31+
this.listen('keypress', this.onSuppressEnter);
32+
this.listen('keydown', this.onSuppressEnter);
33+
this.listen('keyup', this.onSuppressEnter);
5834
}
5935

60-
LocationAutocomplete.DEFAULTS = {
61-
inputLatitude: null,
62-
inputLongitude: null,
63-
inputStreet: null,
64-
inputCity: null,
65-
inputCounty: null,
66-
inputState: null,
67-
inputZip: null,
68-
inputVicinity: null,
69-
inputCountry: null,
70-
inputCountryLong: null
36+
disconnect() {
37+
if (this.autocomplete) {
38+
google.maps.event.clearInstanceListeners(this.autocomplete);
39+
this.autocomplete = null;
40+
}
7141
}
7242

73-
LocationAutocomplete.prototype.init = function() {
43+
initAutocomplete() {
44+
if (typeof google === 'undefined' || !google.maps || !google.maps.places) {
45+
// Google Maps not ready yet, retry
46+
setTimeout(this.proxy(this.initAutocomplete), 100);
47+
return;
48+
}
49+
7450
var autocompleteOptions = {
7551
types: ['geocode', 'establishment']
76-
}
77-
var countryRestriction = this.$el.data('countryRestriction')
52+
};
53+
54+
var countryRestriction = this.config.countryRestriction;
7855
if (countryRestriction) {
7956
var countryCodes = countryRestriction.split(',').map(function(code) {
8057
return code.trim();
8158
});
82-
autocompleteOptions['componentRestrictions'] = {
83-
'country': countryCodes
84-
}
59+
autocompleteOptions.componentRestrictions = {
60+
country: countryCodes
61+
};
8562
}
86-
this.autocomplete = new map.places.Autocomplete(this.$el.get(0), autocompleteOptions)
8763

88-
map.event.addListener(this.autocomplete, 'place_changed', $.proxy(this.handlePlaceChanged, this))
64+
this.autocomplete = new google.maps.places.Autocomplete(this.element, autocompleteOptions);
8965

90-
// Prevent ENTER from submitting form
91-
this.$el.bind('keypress keydown keyup', function(e){
92-
if (e.keyCode == 13) { e.preventDefault() }
93-
})
66+
google.maps.event.addListener(this.autocomplete, 'place_changed', this.proxy(this.handlePlaceChanged));
9467
}
9568

96-
LocationAutocomplete.prototype.getValueMap = function() {
97-
var streetValueMap = ['street_number', 'route']
69+
onSuppressEnter(ev) {
70+
if (ev.keyCode === 13) {
71+
ev.preventDefault();
72+
}
73+
}
9874

99-
if(this.$el.data('reverse-street-number')) {
100-
streetValueMap = ['route', 'street_number']
75+
getValueMap() {
76+
var streetValueMap = ['street_number', 'route'];
77+
78+
if (this.config.reverseStreetNumber) {
79+
streetValueMap = ['route', 'street_number'];
10180
}
10281

10382
return {
104-
'geometry': {
83+
geometry: {
10584
inputLatitude: 'lat',
10685
inputLongitude: 'lng'
10786
},
108-
'short': {
87+
short: {
10988
inputStreet: streetValueMap,
11089
inputCity: ['locality', 'postal_town'],
11190
inputCounty: 'administrative_area_level_2',
11291
inputState: 'administrative_area_level_1',
11392
inputZip: 'postal_code',
11493
inputCountry: 'country'
11594
},
116-
'long': {
95+
long: {
11796
inputCountryLong: 'country'
11897
},
119-
'misc': {
98+
misc: {
12099
inputVicinity: 'vicinity'
121100
}
122-
}
101+
};
123102
}
124103

125-
LocationAutocomplete.prototype.handlePlaceChanged = function() {
126-
127-
var self = this,
128-
place = this.autocomplete.getPlace(),
104+
handlePlaceChanged() {
105+
var place = this.autocomplete.getPlace(),
129106
geoLocation = place.geometry.location,
130-
valueMap = this.getValueMap()
107+
valueMap = this.getValueMap();
131108

132-
var extractionFunction = function(standard, google, resultType) {
133-
var value = []
109+
var self = this;
134110

135-
if (!$.isArray(google))
136-
google = [google]
111+
var extractionFunction = function(standard, googleType, resultType) {
112+
var value = [];
137113

138-
$.each(google, function(index, _google) {
139-
value.push(self.getValueFromAddressObject(place, _google, resultType))
140-
})
114+
if (!Array.isArray(googleType)) {
115+
googleType = [googleType];
116+
}
141117

142-
return value.join(' ')
143-
}
118+
googleType.forEach(function(_googleType) {
119+
value.push(self.getValueFromAddressObject(place, _googleType, resultType));
120+
});
144121

145-
var elementFinderFunction = function(lookupKey) {
146-
if (self.options[lookupKey] === undefined)
147-
return
122+
return value.join(' ');
123+
};
148124

149-
var element = $(self.options[lookupKey])
125+
var elementFinderFunction = function(lookupKey) {
126+
var selector = self.config[lookupKey];
127+
if (!selector) {
128+
return;
129+
}
150130

151-
if (element.length == 0)
152-
return
131+
var element = document.querySelector(selector);
132+
if (!element) {
133+
return;
134+
}
153135

154-
return element
155-
}
136+
return element;
137+
};
156138

157-
$.each(valueMap['geometry'], function(standard, google){
158-
var $targetEl = elementFinderFunction(standard)
159-
if (!$targetEl) return
139+
Object.entries(valueMap.geometry).forEach(function([standard, googleType]) {
140+
var targetEl = elementFinderFunction(standard);
141+
if (!targetEl) return;
160142

161-
if (google == 'lat') $targetEl.val(geoLocation.lat())
162-
else if (google == 'lng') $targetEl.val(geoLocation.lng())
163-
})
143+
if (googleType === 'lat') targetEl.value = geoLocation.lat();
144+
else if (googleType === 'lng') targetEl.value = geoLocation.lng();
145+
});
164146

165-
$.each(valueMap['short'], function(standard, google){
166-
var $targetEl = elementFinderFunction(standard)
167-
if (!$targetEl) return
147+
Object.entries(valueMap.short).forEach(function([standard, googleType]) {
148+
var targetEl = elementFinderFunction(standard);
149+
if (!targetEl) return;
168150

169-
$targetEl.val(extractionFunction(standard, google))
170-
})
151+
targetEl.value = extractionFunction(standard, googleType);
152+
});
171153

172-
$.each(valueMap['long'], function(standard, google){
173-
var $targetEl = elementFinderFunction(standard)
174-
if (!$targetEl) return
154+
Object.entries(valueMap.long).forEach(function([standard, googleType]) {
155+
var targetEl = elementFinderFunction(standard);
156+
if (!targetEl) return;
175157

176-
$targetEl.val(extractionFunction(standard, google, 'long_name'))
177-
})
158+
targetEl.value = extractionFunction(standard, googleType, 'long_name');
159+
});
178160

179-
$.each(valueMap['misc'], function(standard, google){
180-
var $targetEl = elementFinderFunction(standard)
181-
if (!$targetEl) return
161+
Object.entries(valueMap.misc).forEach(function([standard, googleType]) {
162+
var targetEl = elementFinderFunction(standard);
163+
if (!targetEl) return;
182164

183-
$targetEl.val(place[google])
184-
})
165+
targetEl.value = place[googleType];
166+
});
185167

186-
this.$el.trigger('changedLocation')
168+
this.element.dispatchEvent(new Event('changedLocation'));
187169
}
188170

189171
/*
@@ -201,59 +183,25 @@
201183
* country = Country
202184
*
203185
*/
204-
LocationAutocomplete.prototype.getValueFromAddressObject = function(addressObj, type, resultType) {
205-
var self = this
206-
var result = null
207-
if (!resultType)
208-
resultType = 'short_name'
186+
getValueFromAddressObject(addressObj, type, resultType) {
187+
if (!resultType) {
188+
resultType = 'short_name';
189+
}
190+
191+
if (!addressObj) {
192+
return null;
193+
}
209194

210-
if (!addressObj)
211-
return null
195+
var result = null;
212196

213197
for (var i = 0; i < addressObj.address_components.length; i++) {
214198
for (var j = 0; j < addressObj.address_components[i].types.length; j++) {
215-
if (addressObj.address_components[i].types[j] == type)
216-
result = addressObj.address_components[i][resultType]
199+
if (addressObj.address_components[i].types[j] === type) {
200+
result = addressObj.address_components[i][resultType];
201+
}
217202
}
218203
}
219204

220-
return result
205+
return result;
221206
}
222-
223-
// LOCATION AUTOCOMPLETE PLUGIN DEFINITION
224-
// ============================
225-
226-
var old = $.fn.locationAutocomplete
227-
228-
$.fn.locationAutocomplete = function (option) {
229-
var args = Array.prototype.slice.call(arguments, 1), result
230-
this.each(function () {
231-
var $this = $(this)
232-
var data = $this.data('ui.location-autocomplete')
233-
var options = $.extend({}, LocationAutocomplete.DEFAULTS, $this.data(), typeof option == 'object' && option)
234-
if (!data) $this.data('ui.location-autocomplete', (data = new LocationAutocomplete(this, options)))
235-
if (typeof option == 'string') result = data[option].apply(data, args)
236-
if (typeof result != 'undefined') return false
237-
})
238-
239-
return result ? result : this
240-
}
241-
242-
$.fn.locationAutocomplete.Constructor = LocationAutocomplete
243-
244-
// LOCATION AUTOCOMPLETE NO CONFLICT
245-
// =================
246-
247-
$.fn.locationAutocomplete.noConflict = function () {
248-
$.fn.locationAutocomplete = old
249-
return this
250-
}
251-
252-
// LOCATION AUTOCOMPLETE DATA-API
253-
// ===============
254-
255-
$(document).render(function() {
256-
$('[data-control="location-autocomplete"]').locationAutocomplete()
257-
})
258-
259-
}(window.jQuery, google.maps);
207+
});

0 commit comments

Comments
 (0)