|
1 | 1 | /* |
2 | | - * Location Autocomplete plugin |
| 2 | + * Location Autocomplete control |
3 | 3 | * |
4 | 4 | * Data attributes: |
5 | | - * - data-control="location-autocomplete" - enables the plugin on an element |
| 5 | + * - data-control="location-autocomplete" - enables the control on an element |
6 | 6 | * - data-input-street="#locationStreet" - input to populate with street |
7 | 7 | * - data-input-city="#locationCity" - input to populate with city |
8 | 8 | * - data-input-county="#locationCounty" - input to populate with county |
|
14 | 14 | * - data-input-longitude="#locationLongitude" - input to populate with longitude |
15 | 15 | * |
16 | 16 | * JavaScript API: |
17 | | - * $('input#addressAutocomplete').locationAutocomplete({ inputCountry: '#locationCountry' }) |
| 17 | + * oc.fetchControl(element, 'location-autocomplete') |
18 | 18 | * |
19 | 19 | * 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 |
44 | 21 | */ |
| 22 | +oc.registerControl('location-autocomplete', class extends oc.ControlBase { |
| 23 | + init() { |
| 24 | + this.autocomplete = null; |
| 25 | + } |
45 | 26 |
|
| 27 | + connect() { |
| 28 | + this.initAutocomplete(); |
46 | 29 |
|
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); |
58 | 34 | } |
59 | 35 |
|
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 | + } |
71 | 41 | } |
72 | 42 |
|
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 | + |
74 | 50 | var autocompleteOptions = { |
75 | 51 | types: ['geocode', 'establishment'] |
76 | | - } |
77 | | - var countryRestriction = this.$el.data('countryRestriction') |
| 52 | + }; |
| 53 | + |
| 54 | + var countryRestriction = this.config.countryRestriction; |
78 | 55 | if (countryRestriction) { |
79 | 56 | var countryCodes = countryRestriction.split(',').map(function(code) { |
80 | 57 | return code.trim(); |
81 | 58 | }); |
82 | | - autocompleteOptions['componentRestrictions'] = { |
83 | | - 'country': countryCodes |
84 | | - } |
| 59 | + autocompleteOptions.componentRestrictions = { |
| 60 | + country: countryCodes |
| 61 | + }; |
85 | 62 | } |
86 | | - this.autocomplete = new map.places.Autocomplete(this.$el.get(0), autocompleteOptions) |
87 | 63 |
|
88 | | - map.event.addListener(this.autocomplete, 'place_changed', $.proxy(this.handlePlaceChanged, this)) |
| 64 | + this.autocomplete = new google.maps.places.Autocomplete(this.element, autocompleteOptions); |
89 | 65 |
|
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)); |
94 | 67 | } |
95 | 68 |
|
96 | | - LocationAutocomplete.prototype.getValueMap = function() { |
97 | | - var streetValueMap = ['street_number', 'route'] |
| 69 | + onSuppressEnter(ev) { |
| 70 | + if (ev.keyCode === 13) { |
| 71 | + ev.preventDefault(); |
| 72 | + } |
| 73 | + } |
98 | 74 |
|
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']; |
101 | 80 | } |
102 | 81 |
|
103 | 82 | return { |
104 | | - 'geometry': { |
| 83 | + geometry: { |
105 | 84 | inputLatitude: 'lat', |
106 | 85 | inputLongitude: 'lng' |
107 | 86 | }, |
108 | | - 'short': { |
| 87 | + short: { |
109 | 88 | inputStreet: streetValueMap, |
110 | 89 | inputCity: ['locality', 'postal_town'], |
111 | 90 | inputCounty: 'administrative_area_level_2', |
112 | 91 | inputState: 'administrative_area_level_1', |
113 | 92 | inputZip: 'postal_code', |
114 | 93 | inputCountry: 'country' |
115 | 94 | }, |
116 | | - 'long': { |
| 95 | + long: { |
117 | 96 | inputCountryLong: 'country' |
118 | 97 | }, |
119 | | - 'misc': { |
| 98 | + misc: { |
120 | 99 | inputVicinity: 'vicinity' |
121 | 100 | } |
122 | | - } |
| 101 | + }; |
123 | 102 | } |
124 | 103 |
|
125 | | - LocationAutocomplete.prototype.handlePlaceChanged = function() { |
126 | | - |
127 | | - var self = this, |
128 | | - place = this.autocomplete.getPlace(), |
| 104 | + handlePlaceChanged() { |
| 105 | + var place = this.autocomplete.getPlace(), |
129 | 106 | geoLocation = place.geometry.location, |
130 | | - valueMap = this.getValueMap() |
| 107 | + valueMap = this.getValueMap(); |
131 | 108 |
|
132 | | - var extractionFunction = function(standard, google, resultType) { |
133 | | - var value = [] |
| 109 | + var self = this; |
134 | 110 |
|
135 | | - if (!$.isArray(google)) |
136 | | - google = [google] |
| 111 | + var extractionFunction = function(standard, googleType, resultType) { |
| 112 | + var value = []; |
137 | 113 |
|
138 | | - $.each(google, function(index, _google) { |
139 | | - value.push(self.getValueFromAddressObject(place, _google, resultType)) |
140 | | - }) |
| 114 | + if (!Array.isArray(googleType)) { |
| 115 | + googleType = [googleType]; |
| 116 | + } |
141 | 117 |
|
142 | | - return value.join(' ') |
143 | | - } |
| 118 | + googleType.forEach(function(_googleType) { |
| 119 | + value.push(self.getValueFromAddressObject(place, _googleType, resultType)); |
| 120 | + }); |
144 | 121 |
|
145 | | - var elementFinderFunction = function(lookupKey) { |
146 | | - if (self.options[lookupKey] === undefined) |
147 | | - return |
| 122 | + return value.join(' '); |
| 123 | + }; |
148 | 124 |
|
149 | | - var element = $(self.options[lookupKey]) |
| 125 | + var elementFinderFunction = function(lookupKey) { |
| 126 | + var selector = self.config[lookupKey]; |
| 127 | + if (!selector) { |
| 128 | + return; |
| 129 | + } |
150 | 130 |
|
151 | | - if (element.length == 0) |
152 | | - return |
| 131 | + var element = document.querySelector(selector); |
| 132 | + if (!element) { |
| 133 | + return; |
| 134 | + } |
153 | 135 |
|
154 | | - return element |
155 | | - } |
| 136 | + return element; |
| 137 | + }; |
156 | 138 |
|
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; |
160 | 142 |
|
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 | + }); |
164 | 146 |
|
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; |
168 | 150 |
|
169 | | - $targetEl.val(extractionFunction(standard, google)) |
170 | | - }) |
| 151 | + targetEl.value = extractionFunction(standard, googleType); |
| 152 | + }); |
171 | 153 |
|
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; |
175 | 157 |
|
176 | | - $targetEl.val(extractionFunction(standard, google, 'long_name')) |
177 | | - }) |
| 158 | + targetEl.value = extractionFunction(standard, googleType, 'long_name'); |
| 159 | + }); |
178 | 160 |
|
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; |
182 | 164 |
|
183 | | - $targetEl.val(place[google]) |
184 | | - }) |
| 165 | + targetEl.value = place[googleType]; |
| 166 | + }); |
185 | 167 |
|
186 | | - this.$el.trigger('changedLocation') |
| 168 | + this.element.dispatchEvent(new Event('changedLocation')); |
187 | 169 | } |
188 | 170 |
|
189 | 171 | /* |
|
201 | 183 | * country = Country |
202 | 184 | * |
203 | 185 | */ |
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 | + } |
209 | 194 |
|
210 | | - if (!addressObj) |
211 | | - return null |
| 195 | + var result = null; |
212 | 196 |
|
213 | 197 | for (var i = 0; i < addressObj.address_components.length; i++) { |
214 | 198 | 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 | + } |
217 | 202 | } |
218 | 203 | } |
219 | 204 |
|
220 | | - return result |
| 205 | + return result; |
221 | 206 | } |
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