UNPKG

51.1 kB JavaScript View Raw
1/* @preserve
2 * Leaflet Control Geocoder 1.9.0
3 * https://github.com/perliedman/leaflet-control-geocoder
4 *
5 * Copyright (c) 2012 sa3m (https://github.com/sa3m)
6 * Copyright (c) 2018 Per Liedman
7 * All rights reserved.
8 */
9
10this.L = this.L || {};
11this.L.Control = this.L.Control || {};
12this.L.Control.Geocoder = (function (L) {
13 'use strict';
14
15 L = L && L.hasOwnProperty('default') ? L['default'] : L;
16
17 var lastCallbackId = 0;
18
19 // Adapted from handlebars.js
20 // https://github.com/wycats/handlebars.js/
21 var badChars = /[&<>"'`]/g;
22 var possible = /[&<>"'`]/;
23 var escape = {
24 '&': '&amp;',
25 '<': '&lt;',
26 '>': '&gt;',
27 '"': '&quot;',
28 "'": '&#x27;',
29 '`': '&#x60;'
30 };
31
32 function escapeChar(chr) {
33 return escape[chr];
34 }
35
36 function htmlEscape(string) {
37 if (string == null) {
38 return '';
39 } else if (!string) {
40 return string + '';
41 }
42
43 // Force a string conversion as this will be done by the append regardless and
44 // the regex test will do this transparently behind the scenes, causing issues if
45 // an object's to string has escaped characters in it.
46 string = '' + string;
47
48 if (!possible.test(string)) {
49 return string;
50 }
51 return string.replace(badChars, escapeChar);
52 }
53
54 function jsonp(url, params, callback, context, jsonpParam) {
55 var callbackId = '_l_geocoder_' + lastCallbackId++;
56 params[jsonpParam || 'callback'] = callbackId;
57 window[callbackId] = L.Util.bind(callback, context);
58 var script = document.createElement('script');
59 script.type = 'text/javascript';
60 script.src = url + getParamString(params);
61 script.id = callbackId;
62 document.getElementsByTagName('head')[0].appendChild(script);
63 }
64
65 function getJSON(url, params, callback) {
66 var xmlHttp = new XMLHttpRequest();
67 xmlHttp.onreadystatechange = function() {
68 if (xmlHttp.readyState !== 4) {
69 return;
70 }
71 var message;
72 if (xmlHttp.status !== 200 && xmlHttp.status !== 304) {
73 message = '';
74 } else if (typeof xmlHttp.response === 'string') {
75 // IE doesn't parse JSON responses even with responseType: 'json'.
76 try {
77 message = JSON.parse(xmlHttp.response);
78 } catch (e) {
79 // Not a JSON response
80 message = xmlHttp.response;
81 }
82 } else {
83 message = xmlHttp.response;
84 }
85 callback(message);
86 };
87 xmlHttp.open('GET', url + getParamString(params), true);
88 xmlHttp.responseType = 'json';
89 xmlHttp.setRequestHeader('Accept', 'application/json');
90 xmlHttp.send(null);
91 }
92
93 function template(str, data) {
94 return str.replace(/\{ *([\w_]+) *\}/g, function(str, key) {
95 var value = data[key];
96 if (value === undefined) {
97 value = '';
98 } else if (typeof value === 'function') {
99 value = value(data);
100 }
101 return htmlEscape(value);
102 });
103 }
104
105 function getParamString(obj, existingUrl, uppercase) {
106 var params = [];
107 for (var i in obj) {
108 var key = encodeURIComponent(uppercase ? i.toUpperCase() : i);
109 var value = obj[i];
110 if (!L.Util.isArray(value)) {
111 params.push(key + '=' + encodeURIComponent(value));
112 } else {
113 for (var j = 0; j < value.length; j++) {
114 params.push(key + '=' + encodeURIComponent(value[j]));
115 }
116 }
117 }
118 return (!existingUrl || existingUrl.indexOf('?') === -1 ? '?' : '&') + params.join('&');
119 }
120
121 var ArcGis = L.Class.extend({
122 options: {
123 service_url: 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer'
124 },
125
126 initialize: function(accessToken, options) {
127 L.setOptions(this, options);
128 this._accessToken = accessToken;
129 },
130
131 geocode: function(query, cb, context) {
132 var params = {
133 SingleLine: query,
134 outFields: 'Addr_Type',
135 forStorage: false,
136 maxLocations: 10,
137 f: 'json'
138 };
139
140 if (this._key && this._key.length) {
141 params.token = this._key;
142 }
143
144 getJSON(
145 this.options.service_url + '/findAddressCandidates',
146 L.extend(params, this.options.geocodingQueryParams),
147 function(data) {
148 var results = [],
149 loc,
150 latLng,
151 latLngBounds;
152
153 if (data.candidates && data.candidates.length) {
154 for (var i = 0; i <= data.candidates.length - 1; i++) {
155 loc = data.candidates[i];
156 latLng = L.latLng(loc.location.y, loc.location.x);
157 latLngBounds = L.latLngBounds(
158 L.latLng(loc.extent.ymax, loc.extent.xmax),
159 L.latLng(loc.extent.ymin, loc.extent.xmin)
160 );
161 results[i] = {
162 name: loc.address,
163 bbox: latLngBounds,
164 center: latLng
165 };
166 }
167 }
168
169 cb.call(context, results);
170 }
171 );
172 },
173
174 suggest: function(query, cb, context) {
175 return this.geocode(query, cb, context);
176 },
177
178 reverse: function(location, scale, cb, context) {
179 var params = {
180 location: encodeURIComponent(location.lng) + ',' + encodeURIComponent(location.lat),
181 distance: 100,
182 f: 'json'
183 };
184
185 getJSON(this.options.service_url + '/reverseGeocode', params, function(data) {
186 var result = [],
187 loc;
188
189 if (data && !data.error) {
190 loc = L.latLng(data.location.y, data.location.x);
191 result.push({
192 name: data.address.Match_addr,
193 center: loc,
194 bounds: L.latLngBounds(loc, loc)
195 });
196 }
197
198 cb.call(context, result);
199 });
200 }
201 });
202
203 function arcgis(accessToken, options) {
204 return new ArcGis(accessToken, options);
205 }
206
207 var Bing = L.Class.extend({
208 initialize: function(key) {
209 this.key = key;
210 },
211
212 geocode: function(query, cb, context) {
213 jsonp(
214 'https://dev.virtualearth.net/REST/v1/Locations',
215 {
216 query: query,
217 key: this.key
218 },
219 function(data) {
220 var results = [];
221 if (data.resourceSets.length > 0) {
222 for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {
223 var resource = data.resourceSets[0].resources[i],
224 bbox = resource.bbox;
225 results[i] = {
226 name: resource.name,
227 bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),
228 center: L.latLng(resource.point.coordinates)
229 };
230 }
231 }
232 cb.call(context, results);
233 },
234 this,
235 'jsonp'
236 );
237 },
238
239 reverse: function(location, scale, cb, context) {
240 jsonp(
241 '//dev.virtualearth.net/REST/v1/Locations/' + location.lat + ',' + location.lng,
242 {
243 key: this.key
244 },
245 function(data) {
246 var results = [];
247 for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {
248 var resource = data.resourceSets[0].resources[i],
249 bbox = resource.bbox;
250 results[i] = {
251 name: resource.name,
252 bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),
253 center: L.latLng(resource.point.coordinates)
254 };
255 }
256 cb.call(context, results);
257 },
258 this,
259 'jsonp'
260 );
261 }
262 });
263
264 function bing(key) {
265 return new Bing(key);
266 }
267
268 var Google = L.Class.extend({
269 options: {
270 serviceUrl: 'https://maps.googleapis.com/maps/api/geocode/json',
271 geocodingQueryParams: {},
272 reverseQueryParams: {}
273 },
274
275 initialize: function(key, options) {
276 this._key = key;
277 L.setOptions(this, options);
278 // Backwards compatibility
279 this.options.serviceUrl = this.options.service_url || this.options.serviceUrl;
280 },
281
282 geocode: function(query, cb, context) {
283 var params = {
284 address: query
285 };
286
287 if (this._key && this._key.length) {
288 params.key = this._key;
289 }
290
291 params = L.Util.extend(params, this.options.geocodingQueryParams);
292
293 getJSON(this.options.serviceUrl, params, function(data) {
294 var results = [],
295 loc,
296 latLng,
297 latLngBounds;
298 if (data.results && data.results.length) {
299 for (var i = 0; i <= data.results.length - 1; i++) {
300 loc = data.results[i];
301 latLng = L.latLng(loc.geometry.location);
302 latLngBounds = L.latLngBounds(
303 L.latLng(loc.geometry.viewport.northeast),
304 L.latLng(loc.geometry.viewport.southwest)
305 );
306 results[i] = {
307 name: loc.formatted_address,
308 bbox: latLngBounds,
309 center: latLng,
310 properties: loc.address_components
311 };
312 }
313 }
314
315 cb.call(context, results);
316 });
317 },
318
319 reverse: function(location, scale, cb, context) {
320 var params = {
321 latlng: encodeURIComponent(location.lat) + ',' + encodeURIComponent(location.lng)
322 };
323 params = L.Util.extend(params, this.options.reverseQueryParams);
324 if (this._key && this._key.length) {
325 params.key = this._key;
326 }
327
328 getJSON(this.options.serviceUrl, params, function(data) {
329 var results = [],
330 loc,
331 latLng,
332 latLngBounds;
333 if (data.results && data.results.length) {
334 for (var i = 0; i <= data.results.length - 1; i++) {
335 loc = data.results[i];
336 latLng = L.latLng(loc.geometry.location);
337 latLngBounds = L.latLngBounds(
338 L.latLng(loc.geometry.viewport.northeast),
339 L.latLng(loc.geometry.viewport.southwest)
340 );
341 results[i] = {
342 name: loc.formatted_address,
343 bbox: latLngBounds,
344 center: latLng,
345 properties: loc.address_components
346 };
347 }
348 }
349
350 cb.call(context, results);
351 });
352 }
353 });
354
355 function google(key, options) {
356 return new Google(key, options);
357 }
358
359 var HERE = L.Class.extend({
360 options: {
361 geocodeUrl: 'https://geocoder.api.here.com/6.2/geocode.json',
362 reverseGeocodeUrl: 'https://reverse.geocoder.api.here.com/6.2/reversegeocode.json',
363 app_id: '<insert your app_id here>',
364 app_code: '<insert your app_code here>',
365 geocodingQueryParams: {},
366 reverseQueryParams: {}
367 },
368
369 initialize: function(options) {
370 L.setOptions(this, options);
371 },
372
373 geocode: function(query, cb, context) {
374 var params = {
375 searchtext: query,
376 gen: 9,
377 app_id: this.options.app_id,
378 app_code: this.options.app_code,
379 jsonattributes: 1
380 };
381 params = L.Util.extend(params, this.options.geocodingQueryParams);
382 this.getJSON(this.options.geocodeUrl, params, cb, context);
383 },
384
385 reverse: function(location, scale, cb, context) {
386 var params = {
387 prox: encodeURIComponent(location.lat) + ',' + encodeURIComponent(location.lng),
388 mode: 'retrieveAddresses',
389 app_id: this.options.app_id,
390 app_code: this.options.app_code,
391 gen: 9,
392 jsonattributes: 1
393 };
394 params = L.Util.extend(params, this.options.reverseQueryParams);
395 this.getJSON(this.options.reverseGeocodeUrl, params, cb, context);
396 },
397
398 getJSON: function(url, params, cb, context) {
399 getJSON(url, params, function(data) {
400 var results = [],
401 loc,
402 latLng,
403 latLngBounds;
404 if (data.response.view && data.response.view.length) {
405 for (var i = 0; i <= data.response.view[0].result.length - 1; i++) {
406 loc = data.response.view[0].result[i].location;
407 latLng = L.latLng(loc.displayPosition.latitude, loc.displayPosition.longitude);
408 latLngBounds = L.latLngBounds(
409 L.latLng(loc.mapView.topLeft.latitude, loc.mapView.topLeft.longitude),
410 L.latLng(loc.mapView.bottomRight.latitude, loc.mapView.bottomRight.longitude)
411 );
412 results[i] = {
413 name: loc.address.label,
414 bbox: latLngBounds,
415 center: latLng
416 };
417 }
418 }
419 cb.call(context, results);
420 });
421 }
422 });
423
424 function here(options) {
425 return new HERE(options);
426 }
427
428 var LatLng = L.Class.extend({
429 options: {
430 // the next geocoder to use
431 next: undefined,
432 sizeInMeters: 10000
433 },
434
435 initialize: function(options) {
436 L.Util.setOptions(this, options);
437 },
438
439 geocode: function(query, cb, context) {
440 var match;
441 var center;
442 // regex from https://github.com/openstreetmap/openstreetmap-website/blob/master/app/controllers/geocoder_controller.rb
443 if ((match = query.match(/^([NS])\s*(\d{1,3}(?:\.\d*)?)\W*([EW])\s*(\d{1,3}(?:\.\d*)?)$/))) {
444 // [NSEW] decimal degrees
445 center = L.latLng(
446 (/N/i.test(match[1]) ? 1 : -1) * parseFloat(match[2]),
447 (/E/i.test(match[3]) ? 1 : -1) * parseFloat(match[4])
448 );
449 } else if (
450 (match = query.match(/^(\d{1,3}(?:\.\d*)?)\s*([NS])\W*(\d{1,3}(?:\.\d*)?)\s*([EW])$/))
451 ) {
452 // decimal degrees [NSEW]
453 center = L.latLng(
454 (/N/i.test(match[2]) ? 1 : -1) * parseFloat(match[1]),
455 (/E/i.test(match[4]) ? 1 : -1) * parseFloat(match[3])
456 );
457 } else if (
458 (match = query.match(
459 /^([NS])\s*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?$/
460 ))
461 ) {
462 // [NSEW] degrees, decimal minutes
463 center = L.latLng(
464 (/N/i.test(match[1]) ? 1 : -1) * (parseFloat(match[2]) + parseFloat(match[3] / 60)),
465 (/E/i.test(match[4]) ? 1 : -1) * (parseFloat(match[5]) + parseFloat(match[6] / 60))
466 );
467 } else if (
468 (match = query.match(
469 /^(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\s*([NS])\W*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['′]?\s*([EW])$/
470 ))
471 ) {
472 // degrees, decimal minutes [NSEW]
473 center = L.latLng(
474 (/N/i.test(match[3]) ? 1 : -1) * (parseFloat(match[1]) + parseFloat(match[2] / 60)),
475 (/E/i.test(match[6]) ? 1 : -1) * (parseFloat(match[4]) + parseFloat(match[5] / 60))
476 );
477 } else if (
478 (match = query.match(
479 /^([NS])\s*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?$/
480 ))
481 ) {
482 // [NSEW] degrees, minutes, decimal seconds
483 center = L.latLng(
484 (/N/i.test(match[1]) ? 1 : -1) *
485 (parseFloat(match[2]) + parseFloat(match[3] / 60 + parseFloat(match[4] / 3600))),
486 (/E/i.test(match[5]) ? 1 : -1) *
487 (parseFloat(match[6]) + parseFloat(match[7] / 60) + parseFloat(match[8] / 3600))
488 );
489 } else if (
490 (match = query.match(
491 /^(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]\s*([NS])\W*(\d{1,3})°?\s*(\d{1,2})['′]?\s*(\d{1,3}(?:\.\d*)?)?["″]?\s*([EW])$/
492 ))
493 ) {
494 // degrees, minutes, decimal seconds [NSEW]
495 center = L.latLng(
496 (/N/i.test(match[4]) ? 1 : -1) *
497 (parseFloat(match[1]) + parseFloat(match[2] / 60 + parseFloat(match[3] / 3600))),
498 (/E/i.test(match[8]) ? 1 : -1) *
499 (parseFloat(match[5]) + parseFloat(match[6] / 60) + parseFloat(match[7] / 3600))
500 );
501 } else if (
502 (match = query.match(/^\s*([+-]?\d+(?:\.\d*)?)\s*[\s,]\s*([+-]?\d+(?:\.\d*)?)\s*$/))
503 ) {
504 center = L.latLng(parseFloat(match[1]), parseFloat(match[2]));
505 }
506 if (center) {
507 var results = [
508 {
509 name: query,
510 center: center,
511 bbox: center.toBounds(this.options.sizeInMeters)
512 }
513 ];
514 cb.call(context, results);
515 } else if (this.options.next) {
516 this.options.next.geocode(query, cb, context);
517 }
518 }
519 });
520
521 function latLng(options) {
522 return new LatLng(options);
523 }
524
525 var Mapbox = L.Class.extend({
526 options: {
527 serviceUrl: 'https://api.mapbox.com/geocoding/v5/mapbox.places/',
528 geocodingQueryParams: {},
529 reverseQueryParams: {}
530 },
531
532 initialize: function(accessToken, options) {
533 L.setOptions(this, options);
534 this.options.geocodingQueryParams.access_token = accessToken;
535 this.options.reverseQueryParams.access_token = accessToken;
536 },
537
538 geocode: function(query, cb, context) {
539 var params = this.options.geocodingQueryParams;
540 if (
541 params.proximity !== undefined &&
542 params.proximity.lat !== undefined &&
543 params.proximity.lng !== undefined
544 ) {
545 params.proximity = params.proximity.lng + ',' + params.proximity.lat;
546 }
547 getJSON(this.options.serviceUrl + encodeURIComponent(query) + '.json', params, function(data) {
548 var results = [],
549 loc,
550 latLng,
551 latLngBounds;
552 if (data.features && data.features.length) {
553 for (var i = 0; i <= data.features.length - 1; i++) {
554 loc = data.features[i];
555 latLng = L.latLng(loc.center.reverse());
556 if (loc.bbox) {
557 latLngBounds = L.latLngBounds(
558 L.latLng(loc.bbox.slice(0, 2).reverse()),
559 L.latLng(loc.bbox.slice(2, 4).reverse())
560 );
561 } else {
562 latLngBounds = L.latLngBounds(latLng, latLng);
563 }
564
565 var properties = {
566 text: loc.text,
567 address: loc.address
568 };
569
570 for (var j = 0; j < (loc.context || []).length; j++) {
571 var id = loc.context[j].id.split('.')[0];
572 properties[id] = loc.context[j].text;
573 }
574
575 results[i] = {
576 name: loc.place_name,
577 bbox: latLngBounds,
578 center: latLng,
579 properties: properties
580 };
581 }
582 }
583
584 cb.call(context, results);
585 });
586 },
587
588 suggest: function(query, cb, context) {
589 return this.geocode(query, cb, context);
590 },
591
592 reverse: function(location, scale, cb, context) {
593 getJSON(
594 this.options.serviceUrl +
595 encodeURIComponent(location.lng) +
596 ',' +
597 encodeURIComponent(location.lat) +
598 '.json',
599 this.options.reverseQueryParams,
600 function(data) {
601 var results = [],
602 loc,
603 latLng,
604 latLngBounds;
605 if (data.features && data.features.length) {
606 for (var i = 0; i <= data.features.length - 1; i++) {
607 loc = data.features[i];
608 latLng = L.latLng(loc.center.reverse());
609 if (loc.bbox) {
610 latLngBounds = L.latLngBounds(
611 L.latLng(loc.bbox.slice(0, 2).reverse()),
612 L.latLng(loc.bbox.slice(2, 4).reverse())
613 );
614 } else {
615 latLngBounds = L.latLngBounds(latLng, latLng);
616 }
617 results[i] = {
618 name: loc.place_name,
619 bbox: latLngBounds,
620 center: latLng
621 };
622 }
623 }
624
625 cb.call(context, results);
626 }
627 );
628 }
629 });
630
631 function mapbox(accessToken, options) {
632 return new Mapbox(accessToken, options);
633 }
634
635 var MapQuest = L.Class.extend({
636 options: {
637 serviceUrl: 'https://www.mapquestapi.com/geocoding/v1'
638 },
639
640 initialize: function(key, options) {
641 // MapQuest seems to provide URI encoded API keys,
642 // so to avoid encoding them twice, we decode them here
643 this._key = decodeURIComponent(key);
644
645 L.Util.setOptions(this, options);
646 },
647
648 _formatName: function() {
649 var r = [],
650 i;
651 for (i = 0; i < arguments.length; i++) {
652 if (arguments[i]) {
653 r.push(arguments[i]);
654 }
655 }
656
657 return r.join(', ');
658 },
659
660 geocode: function(query, cb, context) {
661 getJSON(
662 this.options.serviceUrl + '/address',
663 {
664 key: this._key,
665 location: query,
666 limit: 5,
667 outFormat: 'json'
668 },
669 L.bind(function(data) {
670 var results = [],
671 loc,
672 latLng;
673 if (data.results && data.results[0].locations) {
674 for (var i = data.results[0].locations.length - 1; i >= 0; i--) {
675 loc = data.results[0].locations[i];
676 latLng = L.latLng(loc.latLng);
677 results[i] = {
678 name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),
679 bbox: L.latLngBounds(latLng, latLng),
680 center: latLng
681 };
682 }
683 }
684
685 cb.call(context, results);
686 }, this)
687 );
688 },
689
690 reverse: function(location, scale, cb, context) {
691 getJSON(
692 this.options.serviceUrl + '/reverse',
693 {
694 key: this._key,
695 location: location.lat + ',' + location.lng,
696 outputFormat: 'json'
697 },
698 L.bind(function(data) {
699 var results = [],
700 loc,
701 latLng;
702 if (data.results && data.results[0].locations) {
703 for (var i = data.results[0].locations.length - 1; i >= 0; i--) {
704 loc = data.results[0].locations[i];
705 latLng = L.latLng(loc.latLng);
706 results[i] = {
707 name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),
708 bbox: L.latLngBounds(latLng, latLng),
709 center: latLng
710 };
711 }
712 }
713
714 cb.call(context, results);
715 }, this)
716 );
717 }
718 });
719
720 function mapQuest(key, options) {
721 return new MapQuest(key, options);
722 }
723
724 var Neutrino = L.Class.extend({
725 options: {
726 userId: '<insert your userId here>',
727 apiKey: '<insert your apiKey here>',
728 serviceUrl: 'https://neutrinoapi.com/'
729 },
730
731 initialize: function(options) {
732 L.Util.setOptions(this, options);
733 },
734
735 // https://www.neutrinoapi.com/api/geocode-address/
736 geocode: function(query, cb, context) {
737 getJSON(
738 this.options.serviceUrl + 'geocode-address',
739 {
740 apiKey: this.options.apiKey,
741 userId: this.options.userId,
742 //get three words and make a dot based string
743 address: query.split(/\s+/).join('.')
744 },
745 function(data) {
746 var results = [],
747 latLng,
748 latLngBounds;
749 if (data.locations) {
750 data.geometry = data.locations[0];
751 latLng = L.latLng(data.geometry['latitude'], data.geometry['longitude']);
752 latLngBounds = L.latLngBounds(latLng, latLng);
753 results[0] = {
754 name: data.geometry.address,
755 bbox: latLngBounds,
756 center: latLng
757 };
758 }
759
760 cb.call(context, results);
761 }
762 );
763 },
764
765 suggest: function(query, cb, context) {
766 return this.geocode(query, cb, context);
767 },
768
769 // https://www.neutrinoapi.com/api/geocode-reverse/
770 reverse: function(location, scale, cb, context) {
771 getJSON(
772 this.options.serviceUrl + 'geocode-reverse',
773 {
774 apiKey: this.options.apiKey,
775 userId: this.options.userId,
776 latitude: location.lat,
777 longitude: location.lng
778 },
779 function(data) {
780 var results = [],
781 latLng,
782 latLngBounds;
783 if (data.status.status == 200 && data.found) {
784 latLng = L.latLng(location.lat, location.lng);
785 latLngBounds = L.latLngBounds(latLng, latLng);
786 results[0] = {
787 name: data.address,
788 bbox: latLngBounds,
789 center: latLng
790 };
791 }
792 cb.call(context, results);
793 }
794 );
795 }
796 });
797
798 function neutrino(accessToken) {
799 return new Neutrino(accessToken);
800 }
801
802 var Nominatim = L.Class.extend({
803 options: {
804 serviceUrl: 'https://nominatim.openstreetmap.org/',
805 geocodingQueryParams: {},
806 reverseQueryParams: {},
807 htmlTemplate: function(r) {
808 var a = r.address,
809 parts = [];
810 if (a.road || a.building) {
811 parts.push('{building} {road} {house_number}');
812 }
813
814 if (a.city || a.town || a.village || a.hamlet) {
815 parts.push(
816 '<span class="' +
817 (parts.length > 0 ? 'leaflet-control-geocoder-address-detail' : '') +
818 '">{postcode} {city} {town} {village} {hamlet}</span>'
819 );
820 }
821
822 if (a.state || a.country) {
823 parts.push(
824 '<span class="' +
825 (parts.length > 0 ? 'leaflet-control-geocoder-address-context' : '') +
826 '">{state} {country}</span>'
827 );
828 }
829
830 return template(parts.join('<br/>'), a, true);
831 }
832 },
833
834 initialize: function(options) {
835 L.Util.setOptions(this, options);
836 },
837
838 geocode: function(query, cb, context) {
839 getJSON(
840 this.options.serviceUrl + 'search',
841 L.extend(
842 {
843 q: query,
844 limit: 5,
845 format: 'json',
846 addressdetails: 1
847 },
848 this.options.geocodingQueryParams
849 ),
850 L.bind(function(data) {
851 var results = [];
852 for (var i = data.length - 1; i >= 0; i--) {
853 var bbox = data[i].boundingbox;
854 for (var j = 0; j < 4; j++) bbox[j] = parseFloat(bbox[j]);
855 results[i] = {
856 icon: data[i].icon,
857 name: data[i].display_name,
858 html: this.options.htmlTemplate ? this.options.htmlTemplate(data[i]) : undefined,
859 bbox: L.latLngBounds([bbox[0], bbox[2]], [bbox[1], bbox[3]]),
860 center: L.latLng(data[i].lat, data[i].lon),
861 properties: data[i]
862 };
863 }
864 cb.call(context, results);
865 }, this)
866 );
867 },
868
869 reverse: function(location, scale, cb, context) {
870 getJSON(
871 this.options.serviceUrl + 'reverse',
872 L.extend(
873 {
874 lat: location.lat,
875 lon: location.lng,
876 zoom: Math.round(Math.log(scale / 256) / Math.log(2)),
877 addressdetails: 1,
878 format: 'json'
879 },
880 this.options.reverseQueryParams
881 ),
882 L.bind(function(data) {
883 var result = [],
884 loc;
885
886 if (data && data.lat && data.lon) {
887 loc = L.latLng(data.lat, data.lon);
888 result.push({
889 name: data.display_name,
890 html: this.options.htmlTemplate ? this.options.htmlTemplate(data) : undefined,
891 center: loc,
892 bounds: L.latLngBounds(loc, loc),
893 properties: data
894 });
895 }
896
897 cb.call(context, result);
898 }, this)
899 );
900 }
901 });
902
903 function nominatim(options) {
904 return new Nominatim(options);
905 }
906
907 var OpenLocationCode = L.Class.extend({
908 options: {
909 OpenLocationCode: undefined,
910 codeLength: undefined
911 },
912
913 initialize: function(options) {
914 L.setOptions(this, options);
915 },
916
917 geocode: function(query, cb, context) {
918 try {
919 var decoded = this.options.OpenLocationCode.decode(query);
920 var result = {
921 name: query,
922 center: L.latLng(decoded.latitudeCenter, decoded.longitudeCenter),
923 bbox: L.latLngBounds(
924 L.latLng(decoded.latitudeLo, decoded.longitudeLo),
925 L.latLng(decoded.latitudeHi, decoded.longitudeHi)
926 )
927 };
928 cb.call(context, [result]);
929 } catch (e) {
930 console.warn(e); // eslint-disable-line no-console
931 cb.call(context, []);
932 }
933 },
934 reverse: function(location, scale, cb, context) {
935 try {
936 var code = this.options.OpenLocationCode.encode(
937 location.lat,
938 location.lng,
939 this.options.codeLength
940 );
941 var result = {
942 name: code,
943 center: L.latLng(location.lat, location.lng),
944 bbox: L.latLngBounds(
945 L.latLng(location.lat, location.lng),
946 L.latLng(location.lat, location.lng)
947 )
948 };
949 cb.call(context, [result]);
950 } catch (e) {
951 console.warn(e); // eslint-disable-line no-console
952 cb.call(context, []);
953 }
954 }
955 });
956
957 function openLocationCode(options) {
958 return new OpenLocationCode(options);
959 }
960
961 var OpenCage = L.Class.extend({
962 options: {
963 serviceUrl: 'https://api.opencagedata.com/geocode/v1/json'
964 },
965
966 initialize: function(apiKey) {
967 this._accessToken = apiKey;
968 },
969
970 geocode: function(query, cb, context) {
971 getJSON(
972 this.options.serviceUrl,
973 {
974 key: this._accessToken,
975 q: query
976 },
977 function(data) {
978 var results = [],
979 latLng,
980 latLngBounds,
981 loc;
982 if (data.results && data.results.length) {
983 for (var i = 0; i < data.results.length; i++) {
984 loc = data.results[i];
985 latLng = L.latLng(loc.geometry);
986 if (loc.annotations && loc.annotations.bounds) {
987 latLngBounds = L.latLngBounds(
988 L.latLng(loc.annotations.bounds.northeast),
989 L.latLng(loc.annotations.bounds.southwest)
990 );
991 } else {
992 latLngBounds = L.latLngBounds(latLng, latLng);
993 }
994 results.push({
995 name: loc.formatted,
996 bbox: latLngBounds,
997 center: latLng
998 });
999 }
1000 }
1001 cb.call(context, results);
1002 }
1003 );
1004 },
1005
1006 suggest: function(query, cb, context) {
1007 return this.geocode(query, cb, context);
1008 },
1009
1010 reverse: function(location, scale, cb, context) {
1011 getJSON(
1012 this.options.serviceUrl,
1013 {
1014 key: this._accessToken,
1015 q: [location.lat, location.lng].join(',')
1016 },
1017 function(data) {
1018 var results = [],
1019 latLng,
1020 latLngBounds,
1021 loc;
1022 if (data.results && data.results.length) {
1023 for (var i = 0; i < data.results.length; i++) {
1024 loc = data.results[i];
1025 latLng = L.latLng(loc.geometry);
1026 if (loc.annotations && loc.annotations.bounds) {
1027 latLngBounds = L.latLngBounds(
1028 L.latLng(loc.annotations.bounds.northeast),
1029 L.latLng(loc.annotations.bounds.southwest)
1030 );
1031 } else {
1032 latLngBounds = L.latLngBounds(latLng, latLng);
1033 }
1034 results.push({
1035 name: loc.formatted,
1036 bbox: latLngBounds,
1037 center: latLng
1038 });
1039 }
1040 }
1041 cb.call(context, results);
1042 }
1043 );
1044 }
1045 });
1046
1047 function opencage(apiKey) {
1048 return new OpenCage(apiKey);
1049 }
1050
1051 var Pelias = L.Class.extend({
1052 options: {
1053 serviceUrl: 'https://api.geocode.earth/v1',
1054 geocodingQueryParams: {},
1055 reverseQueryParams: {}
1056 },
1057
1058 initialize: function(apiKey, options) {
1059 L.Util.setOptions(this, options);
1060 this._apiKey = apiKey;
1061 this._lastSuggest = 0;
1062 },
1063
1064 geocode: function(query, cb, context) {
1065 var _this = this;
1066 getJSON(
1067 this.options.serviceUrl + '/search',
1068 L.extend(
1069 {
1070 api_key: this._apiKey,
1071 text: query
1072 },
1073 this.options.geocodingQueryParams
1074 ),
1075 function(data) {
1076 cb.call(context, _this._parseResults(data, 'bbox'));
1077 }
1078 );
1079 },
1080
1081 suggest: function(query, cb, context) {
1082 var _this = this;
1083 getJSON(
1084 this.options.serviceUrl + '/autocomplete',
1085 L.extend(
1086 {
1087 api_key: this._apiKey,
1088 text: query
1089 },
1090 this.options.geocodingQueryParams
1091 ),
1092 L.bind(function(data) {
1093 if (data.geocoding.timestamp > this._lastSuggest) {
1094 this._lastSuggest = data.geocoding.timestamp;
1095 cb.call(context, _this._parseResults(data, 'bbox'));
1096 }
1097 }, this)
1098 );
1099 },
1100
1101 reverse: function(location, scale, cb, context) {
1102 var _this = this;
1103 getJSON(
1104 this.options.serviceUrl + '/reverse',
1105 L.extend(
1106 {
1107 api_key: this._apiKey,
1108 'point.lat': location.lat,
1109 'point.lon': location.lng
1110 },
1111 this.options.reverseQueryParams
1112 ),
1113 function(data) {
1114 cb.call(context, _this._parseResults(data, 'bounds'));
1115 }
1116 );
1117 },
1118
1119 _parseResults: function(data, bboxname) {
1120 var results = [];
1121 L.geoJson(data, {
1122 pointToLayer: function(feature, latlng) {
1123 return L.circleMarker(latlng);
1124 },
1125 onEachFeature: function(feature, layer) {
1126 var result = {},
1127 bbox,
1128 center;
1129
1130 if (layer.getBounds) {
1131 bbox = layer.getBounds();
1132 center = bbox.getCenter();
1133 } else if (layer.feature.bbox) {
1134 center = layer.getLatLng();
1135 bbox = L.latLngBounds(
1136 L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(0, 2)),
1137 L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(2, 4))
1138 );
1139 } else {
1140 center = layer.getLatLng();
1141 bbox = L.latLngBounds(center, center);
1142 }
1143
1144 result.name = layer.feature.properties.label;
1145 result.center = center;
1146 result[bboxname] = bbox;
1147 result.properties = layer.feature.properties;
1148 results.push(result);
1149 }
1150 });
1151 return results;
1152 }
1153 });
1154
1155 function pelias(apiKey, options) {
1156 return new Pelias(apiKey, options);
1157 }
1158 var GeocodeEarth = Pelias;
1159 var geocodeEarth = pelias;
1160
1161 var Mapzen = Pelias; // r.i.p.
1162 var mapzen = pelias;
1163
1164 var Openrouteservice = Mapzen.extend({
1165 options: {
1166 serviceUrl: 'https://api.openrouteservice.org/geocode'
1167 }
1168 });
1169 function openrouteservice(apiKey, options) {
1170 return new Openrouteservice(apiKey, options);
1171 }
1172
1173 var Photon = L.Class.extend({
1174 options: {
1175 serviceUrl: 'https://photon.komoot.de/api/',
1176 reverseUrl: 'https://photon.komoot.de/reverse/',
1177 nameProperties: ['name', 'street', 'suburb', 'hamlet', 'town', 'city', 'state', 'country']
1178 },
1179
1180 initialize: function(options) {
1181 L.setOptions(this, options);
1182 },
1183
1184 geocode: function(query, cb, context) {
1185 var params = L.extend(
1186 {
1187 q: query
1188 },
1189 this.options.geocodingQueryParams
1190 );
1191
1192 getJSON(
1193 this.options.serviceUrl,
1194 params,
1195 L.bind(function(data) {
1196 cb.call(context, this._decodeFeatures(data));
1197 }, this)
1198 );
1199 },
1200
1201 suggest: function(query, cb, context) {
1202 return this.geocode(query, cb, context);
1203 },
1204
1205 reverse: function(latLng, scale, cb, context) {
1206 var params = L.extend(
1207 {
1208 lat: latLng.lat,
1209 lon: latLng.lng
1210 },
1211 this.options.reverseQueryParams
1212 );
1213
1214 getJSON(
1215 this.options.reverseUrl,
1216 params,
1217 L.bind(function(data) {
1218 cb.call(context, this._decodeFeatures(data));
1219 }, this)
1220 );
1221 },
1222
1223 _decodeFeatures: function(data) {
1224 var results = [],
1225 i,
1226 f,
1227 c,
1228 latLng,
1229 extent,
1230 bbox;
1231
1232 if (data && data.features) {
1233 for (i = 0; i < data.features.length; i++) {
1234 f = data.features[i];
1235 c = f.geometry.coordinates;
1236 latLng = L.latLng(c[1], c[0]);
1237 extent = f.properties.extent;
1238
1239 if (extent) {
1240 bbox = L.latLngBounds([extent[1], extent[0]], [extent[3], extent[2]]);
1241 } else {
1242 bbox = L.latLngBounds(latLng, latLng);
1243 }
1244
1245 results.push({
1246 name: this._decodeFeatureName(f),
1247 html: this.options.htmlTemplate ? this.options.htmlTemplate(f) : undefined,
1248 center: latLng,
1249 bbox: bbox,
1250 properties: f.properties
1251 });
1252 }
1253 }
1254
1255 return results;
1256 },
1257
1258 _decodeFeatureName: function(f) {
1259 return (this.options.nameProperties || [])
1260 .map(function(p) {
1261 return f.properties[p];
1262 })
1263 .filter(function(v) {
1264 return !!v;
1265 })
1266 .join(', ');
1267 }
1268 });
1269
1270 function photon(options) {
1271 return new Photon(options);
1272 }
1273
1274 var What3Words = L.Class.extend({
1275 options: {
1276 serviceUrl: 'https://api.what3words.com/v2/'
1277 },
1278
1279 initialize: function(accessToken) {
1280 this._accessToken = accessToken;
1281 },
1282
1283 geocode: function(query, cb, context) {
1284 //get three words and make a dot based string
1285 getJSON(
1286 this.options.serviceUrl + 'forward',
1287 {
1288 key: this._accessToken,
1289 addr: query.split(/\s+/).join('.')
1290 },
1291 function(data) {
1292 var results = [],
1293 latLng,
1294 latLngBounds;
1295 if (data.geometry) {
1296 latLng = L.latLng(data.geometry['lat'], data.geometry['lng']);
1297 latLngBounds = L.latLngBounds(latLng, latLng);
1298 results[0] = {
1299 name: data.words,
1300 bbox: latLngBounds,
1301 center: latLng
1302 };
1303 }
1304
1305 cb.call(context, results);
1306 }
1307 );
1308 },
1309
1310 suggest: function(query, cb, context) {
1311 return this.geocode(query, cb, context);
1312 },
1313
1314 reverse: function(location, scale, cb, context) {
1315 getJSON(
1316 this.options.serviceUrl + 'reverse',
1317 {
1318 key: this._accessToken,
1319 coords: [location.lat, location.lng].join(',')
1320 },
1321 function(data) {
1322 var results = [],
1323 latLng,
1324 latLngBounds;
1325 if (data.status.status == 200) {
1326 latLng = L.latLng(data.geometry['lat'], data.geometry['lng']);
1327 latLngBounds = L.latLngBounds(latLng, latLng);
1328 results[0] = {
1329 name: data.words,
1330 bbox: latLngBounds,
1331 center: latLng
1332 };
1333 }
1334 cb.call(context, results);
1335 }
1336 );
1337 }
1338 });
1339
1340 function what3words(accessToken) {
1341 return new What3Words(accessToken);
1342 }
1343
1344
1345
1346 var geocoders = /*#__PURE__*/Object.freeze({
1347 ArcGis: ArcGis,
1348 arcgis: arcgis,
1349 Bing: Bing,
1350 bing: bing,
1351 Google: Google,
1352 google: google,
1353 HERE: HERE,
1354 here: here,
1355 LatLng: LatLng,
1356 latLng: latLng,
1357 Mapbox: Mapbox,
1358 mapbox: mapbox,
1359 MapQuest: MapQuest,
1360 mapQuest: mapQuest,
1361 Neutrino: Neutrino,
1362 neutrino: neutrino,
1363 Nominatim: Nominatim,
1364 nominatim: nominatim,
1365 OpenLocationCode: OpenLocationCode,
1366 openLocationCode: openLocationCode,
1367 OpenCage: OpenCage,
1368 opencage: opencage,
1369 Pelias: Pelias,
1370 pelias: pelias,
1371 GeocodeEarth: GeocodeEarth,
1372 geocodeEarth: geocodeEarth,
1373 Mapzen: Mapzen,
1374 mapzen: mapzen,
1375 Openrouteservice: Openrouteservice,
1376 openrouteservice: openrouteservice,
1377 Photon: Photon,
1378 photon: photon,
1379 What3Words: What3Words,
1380 what3words: what3words
1381 });
1382
1383 var Geocoder = L.Control.extend({
1384 options: {
1385 showUniqueResult: true,
1386 showResultIcons: false,
1387 collapsed: true,
1388 expand: 'touch', // options: touch, click, anythingelse
1389 position: 'topright',
1390 placeholder: 'Search...',
1391 errorMessage: 'Nothing found.',
1392 queryMinLength: 1,
1393 suggestMinLength: 3,
1394 suggestTimeout: 250,
1395 defaultMarkGeocode: true
1396 },
1397
1398 includes: L.Evented.prototype || L.Mixin.Events,
1399
1400 initialize: function(options) {
1401 L.Util.setOptions(this, options);
1402 if (!this.options.geocoder) {
1403 this.options.geocoder = new Nominatim();
1404 }
1405
1406 this._requestCount = 0;
1407 },
1408
1409 addThrobberClass: function() {
1410 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');
1411 },
1412
1413 removeThrobberClass: function() {
1414 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');
1415 },
1416
1417 onAdd: function(map) {
1418 var className = 'leaflet-control-geocoder',
1419 container = L.DomUtil.create('div', className + ' leaflet-bar'),
1420 icon = L.DomUtil.create('button', className + '-icon', container),
1421 form = (this._form = L.DomUtil.create('div', className + '-form', container)),
1422 input;
1423
1424 this._map = map;
1425 this._container = container;
1426
1427 icon.innerHTML = '&nbsp;';
1428 icon.type = 'button';
1429
1430 input = this._input = L.DomUtil.create('input', '', form);
1431 input.type = 'text';
1432 input.placeholder = this.options.placeholder;
1433 L.DomEvent.disableClickPropagation(input);
1434
1435 this._errorElement = L.DomUtil.create('div', className + '-form-no-error', container);
1436 this._errorElement.innerHTML = this.options.errorMessage;
1437
1438 this._alts = L.DomUtil.create(
1439 'ul',
1440 className + '-alternatives leaflet-control-geocoder-alternatives-minimized',
1441 container
1442 );
1443 L.DomEvent.disableClickPropagation(this._alts);
1444
1445 L.DomEvent.addListener(input, 'keydown', this._keydown, this);
1446 if (this.options.geocoder.suggest) {
1447 L.DomEvent.addListener(input, 'input', this._change, this);
1448 }
1449 L.DomEvent.addListener(
1450 input,
1451 'blur',
1452 function() {
1453 if (this.options.collapsed && !this._preventBlurCollapse) {
1454 this._collapse();
1455 }
1456 this._preventBlurCollapse = false;
1457 },
1458 this
1459 );
1460
1461 if (this.options.collapsed) {
1462 if (this.options.expand === 'click') {
1463 L.DomEvent.addListener(
1464 container,
1465 'click',
1466 function(e) {
1467 if (e.button === 0 && e.detail !== 2) {
1468 this._toggle();
1469 }
1470 },
1471 this
1472 );
1473 } else if (L.Browser.touch && this.options.expand === 'touch') {
1474 L.DomEvent.addListener(
1475 container,
1476 'touchstart mousedown',
1477 function(e) {
1478 this._toggle();
1479 e.preventDefault(); // mobile: clicking focuses the icon, so UI expands and immediately collapses
1480 e.stopPropagation();
1481 },
1482 this
1483 );
1484 } else {
1485 L.DomEvent.addListener(container, 'mouseover', this._expand, this);
1486 L.DomEvent.addListener(container, 'mouseout', this._collapse, this);
1487 this._map.on('movestart', this._collapse, this);
1488 }
1489 } else {
1490 this._expand();
1491 if (L.Browser.touch) {
1492 L.DomEvent.addListener(
1493 container,
1494 'touchstart',
1495 function() {
1496 this._geocode();
1497 },
1498 this
1499 );
1500 } else {
1501 L.DomEvent.addListener(
1502 container,
1503 'click',
1504 function() {
1505 this._geocode();
1506 },
1507 this
1508 );
1509 }
1510 }
1511
1512 if (this.options.defaultMarkGeocode) {
1513 this.on('markgeocode', this.markGeocode, this);
1514 }
1515
1516 this.on('startgeocode', this.addThrobberClass, this);
1517 this.on('finishgeocode', this.removeThrobberClass, this);
1518 this.on('startsuggest', this.addThrobberClass, this);
1519 this.on('finishsuggest', this.removeThrobberClass, this);
1520
1521 L.DomEvent.disableClickPropagation(container);
1522
1523 return container;
1524 },
1525
1526 _geocodeResult: function(results, suggest) {
1527 if (!suggest && this.options.showUniqueResult && results.length === 1) {
1528 this._geocodeResultSelected(results[0]);
1529 } else if (results.length > 0) {
1530 this._alts.innerHTML = '';
1531 this._results = results;
1532 L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
1533 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-open');
1534 for (var i = 0; i < results.length; i++) {
1535 this._alts.appendChild(this._createAlt(results[i], i));
1536 }
1537 } else {
1538 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-error');
1539 L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');
1540 }
1541 },
1542
1543 markGeocode: function(result) {
1544 result = result.geocode || result;
1545
1546 this._map.fitBounds(result.bbox);
1547
1548 if (this._geocodeMarker) {
1549 this._map.removeLayer(this._geocodeMarker);
1550 }
1551
1552 this._geocodeMarker = new L.Marker(result.center)
1553 .bindPopup(result.html || result.name)
1554 .addTo(this._map)
1555 .openPopup();
1556
1557 return this;
1558 },
1559
1560 _geocode: function(suggest) {
1561 var value = this._input.value;
1562 if (!suggest && value.length < this.options.queryMinLength) {
1563 return;
1564 }
1565
1566 var requestCount = ++this._requestCount,
1567 mode = suggest ? 'suggest' : 'geocode',
1568 eventData = { input: value };
1569
1570 this._lastGeocode = value;
1571 if (!suggest) {
1572 this._clearResults();
1573 }
1574
1575 this.fire('start' + mode, eventData);
1576 this.options.geocoder[mode](
1577 value,
1578 function(results) {
1579 if (requestCount === this._requestCount) {
1580 eventData.results = results;
1581 this.fire('finish' + mode, eventData);
1582 this._geocodeResult(results, suggest);
1583 }
1584 },
1585 this
1586 );
1587 },
1588
1589 _geocodeResultSelected: function(result) {
1590 this.fire('markgeocode', { geocode: result });
1591 },
1592
1593 _toggle: function() {
1594 if (L.DomUtil.hasClass(this._container, 'leaflet-control-geocoder-expanded')) {
1595 this._collapse();
1596 } else {
1597 this._expand();
1598 }
1599 },
1600
1601 _expand: function() {
1602 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');
1603 this._input.select();
1604 this.fire('expand');
1605 },
1606
1607 _collapse: function() {
1608 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-expanded');
1609 L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
1610 L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
1611 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open');
1612 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error');
1613 this._input.blur(); // mobile: keyboard shouldn't stay expanded
1614 this.fire('collapse');
1615 },
1616
1617 _clearResults: function() {
1618 L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
1619 this._selection = null;
1620 L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
1621 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open');
1622 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error');
1623 },
1624
1625 _createAlt: function(result, index) {
1626 var li = L.DomUtil.create('li', ''),
1627 a = L.DomUtil.create('a', '', li),
1628 icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null,
1629 text = result.html ? undefined : document.createTextNode(result.name),
1630 mouseDownHandler = function mouseDownHandler(e) {
1631 // In some browsers, a click will fire on the map if the control is
1632 // collapsed directly after mousedown. To work around this, we
1633 // wait until the click is completed, and _then_ collapse the
1634 // control. Messy, but this is the workaround I could come up with
1635 // for #142.
1636 this._preventBlurCollapse = true;
1637 L.DomEvent.stop(e);
1638 this._geocodeResultSelected(result);
1639 L.DomEvent.on(
1640 li,
1641 'click',
1642 function() {
1643 if (this.options.collapsed) {
1644 this._collapse();
1645 } else {
1646 this._clearResults();
1647 }
1648 },
1649 this
1650 );
1651 };
1652
1653 if (icon) {
1654 icon.src = result.icon;
1655 }
1656
1657 li.setAttribute('data-result-index', index);
1658
1659 if (result.html) {
1660 a.innerHTML = a.innerHTML + result.html;
1661 } else {
1662 a.appendChild(text);
1663 }
1664
1665 // Use mousedown and not click, since click will fire _after_ blur,
1666 // causing the control to have collapsed and removed the items
1667 // before the click can fire.
1668 L.DomEvent.addListener(li, 'mousedown touchstart', mouseDownHandler, this);
1669
1670 return li;
1671 },
1672
1673 _keydown: function(e) {
1674 var _this = this,
1675 select = function select(dir) {
1676 if (_this._selection) {
1677 L.DomUtil.removeClass(_this._selection, 'leaflet-control-geocoder-selected');
1678 _this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];
1679 }
1680 if (!_this._selection) {
1681 _this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild'];
1682 }
1683
1684 if (_this._selection) {
1685 L.DomUtil.addClass(_this._selection, 'leaflet-control-geocoder-selected');
1686 }
1687 };
1688
1689 switch (e.keyCode) {
1690 // Escape
1691 case 27:
1692 if (this.options.collapsed) {
1693 this._collapse();
1694 } else {
1695 this._clearResults();
1696 }
1697 break;
1698 // Up
1699 case 38:
1700 select(-1);
1701 break;
1702 // Up
1703 case 40:
1704 select(1);
1705 break;
1706 // Enter
1707 case 13:
1708 if (this._selection) {
1709 var index = parseInt(this._selection.getAttribute('data-result-index'), 10);
1710 this._geocodeResultSelected(this._results[index]);
1711 this._clearResults();
1712 } else {
1713 this._geocode();
1714 }
1715 break;
1716 default:
1717 return;
1718 }
1719
1720 L.DomEvent.preventDefault(e);
1721 },
1722 _change: function() {
1723 var v = this._input.value;
1724 if (v !== this._lastGeocode) {
1725 clearTimeout(this._suggestTimeout);
1726 if (v.length >= this.options.suggestMinLength) {
1727 this._suggestTimeout = setTimeout(
1728 L.bind(function() {
1729 this._geocode(true);
1730 }, this),
1731 this.options.suggestTimeout
1732 );
1733 } else {
1734 this._clearResults();
1735 }
1736 }
1737 }
1738 });
1739
1740 function geocoder(options) {
1741 return new Geocoder(options);
1742 }
1743
1744 L.Util.extend(Geocoder, geocoders);
1745
1746 L.Util.extend(L.Control, {
1747 Geocoder: Geocoder,
1748 geocoder: geocoder
1749 });
1750
1751 return Geocoder;
1752
1753}(L));
1754//# sourceMappingURL=Control.Geocoder.js.map