1/* @preserve 2 * Leaflet Control Geocoder 1.8.3 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 '&': '&', 25 '<': '<', 26 '>': '>', 27 '"': '"', 28 "'": ''', 29 '`': '`' 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: 'http://geocoder.api.here.com/6.2/geocode.json', 362 reverseGeocodeUrl: 'http://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 Pelias = L.Class.extend({ 962 options: { 963 serviceUrl: 'https://api.geocode.earth/v1', 964 geocodingQueryParams: {}, 965 reverseQueryParams: {} 966 }, 967 968 initialize: function(apiKey, options) { 969 L.Util.setOptions(this, options); 970 this._apiKey = apiKey; 971 this._lastSuggest = 0; 972 }, 973 974 geocode: function(query, cb, context) { 975 var _this = this; 976 getJSON( 977 this.options.serviceUrl + '/search', 978 L.extend( 979 { 980 api_key: this._apiKey, 981 text: query 982 }, 983 this.options.geocodingQueryParams 984 ), 985 function(data) { 986 cb.call(context, _this._parseResults(data, 'bbox')); 987 } 988 ); 989 }, 990 991 suggest: function(query, cb, context) { 992 var _this = this; 993 getJSON( 994 this.options.serviceUrl + '/autocomplete', 995 L.extend( 996 { 997 api_key: this._apiKey, 998 text: query 999 }, 1000 this.options.geocodingQueryParams 1001 ), 1002 L.bind(function(data) { 1003 if (data.geocoding.timestamp > this._lastSuggest) { 1004 this._lastSuggest = data.geocoding.timestamp; 1005 cb.call(context, _this._parseResults(data, 'bbox')); 1006 } 1007 }, this) 1008 ); 1009 }, 1010 1011 reverse: function(location, scale, cb, context) { 1012 var _this = this; 1013 getJSON( 1014 this.options.serviceUrl + '/reverse', 1015 L.extend( 1016 { 1017 api_key: this._apiKey, 1018 'point.lat': location.lat, 1019 'point.lon': location.lng 1020 }, 1021 this.options.reverseQueryParams 1022 ), 1023 function(data) { 1024 cb.call(context, _this._parseResults(data, 'bounds')); 1025 } 1026 ); 1027 }, 1028 1029 _parseResults: function(data, bboxname) { 1030 var results = []; 1031 L.geoJson(data, { 1032 pointToLayer: function(feature, latlng) { 1033 return L.circleMarker(latlng); 1034 }, 1035 onEachFeature: function(feature, layer) { 1036 var result = {}, 1037 bbox, 1038 center; 1039 1040 if (layer.getBounds) { 1041 bbox = layer.getBounds(); 1042 center = bbox.getCenter(); 1043 } else if (layer.feature.bbox) { 1044 center = layer.getLatLng(); 1045 bbox = L.latLngBounds( 1046 L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(0, 2)), 1047 L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(2, 4)) 1048 ); 1049 } else { 1050 center = layer.getLatLng(); 1051 bbox = L.latLngBounds(center, center); 1052 } 1053 1054 result.name = layer.feature.properties.label; 1055 result.center = center; 1056 result[bboxname] = bbox; 1057 result.properties = layer.feature.properties; 1058 results.push(result); 1059 } 1060 }); 1061 return results; 1062 } 1063 }); 1064 1065 function pelias(apiKey, options) { 1066 return new Pelias(apiKey, options); 1067 } 1068 var GeocodeEarth = Pelias; 1069 var geocodeEarth = pelias; 1070 1071 var Mapzen = Pelias; // r.i.p. 1072 var mapzen = pelias; 1073 1074 var Openrouteservice = Mapzen.extend({ 1075 options: { 1076 serviceUrl: 'https://api.openrouteservice.org/geocode' 1077 } 1078 }); 1079 function openrouteservice(apiKey, options) { 1080 return new Openrouteservice(apiKey, options); 1081 } 1082 1083 var Photon = L.Class.extend({ 1084 options: { 1085 serviceUrl: 'https://photon.komoot.de/api/', 1086 reverseUrl: 'https://photon.komoot.de/reverse/', 1087 nameProperties: ['name', 'street', 'suburb', 'hamlet', 'town', 'city', 'state', 'country'] 1088 }, 1089 1090 initialize: function(options) { 1091 L.setOptions(this, options); 1092 }, 1093 1094 geocode: function(query, cb, context) { 1095 var params = L.extend( 1096 { 1097 q: query 1098 }, 1099 this.options.geocodingQueryParams 1100 ); 1101 1102 getJSON( 1103 this.options.serviceUrl, 1104 params, 1105 L.bind(function(data) { 1106 cb.call(context, this._decodeFeatures(data)); 1107 }, this) 1108 ); 1109 }, 1110 1111 suggest: function(query, cb, context) { 1112 return this.geocode(query, cb, context); 1113 }, 1114 1115 reverse: function(latLng, scale, cb, context) { 1116 var params = L.extend( 1117 { 1118 lat: latLng.lat, 1119 lon: latLng.lng 1120 }, 1121 this.options.reverseQueryParams 1122 ); 1123 1124 getJSON( 1125 this.options.reverseUrl, 1126 params, 1127 L.bind(function(data) { 1128 cb.call(context, this._decodeFeatures(data)); 1129 }, this) 1130 ); 1131 }, 1132 1133 _decodeFeatures: function(data) { 1134 var results = [], 1135 i, 1136 f, 1137 c, 1138 latLng, 1139 extent, 1140 bbox; 1141 1142 if (data && data.features) { 1143 for (i = 0; i < data.features.length; i++) { 1144 f = data.features[i]; 1145 c = f.geometry.coordinates; 1146 latLng = L.latLng(c[1], c[0]); 1147 extent = f.properties.extent; 1148 1149 if (extent) { 1150 bbox = L.latLngBounds([extent[1], extent[0]], [extent[3], extent[2]]); 1151 } else { 1152 bbox = L.latLngBounds(latLng, latLng); 1153 } 1154 1155 results.push({ 1156 name: this._decodeFeatureName(f), 1157 html: this.options.htmlTemplate ? this.options.htmlTemplate(f) : undefined, 1158 center: latLng, 1159 bbox: bbox, 1160 properties: f.properties 1161 }); 1162 } 1163 } 1164 1165 return results; 1166 }, 1167 1168 _decodeFeatureName: function(f) { 1169 return (this.options.nameProperties || []) 1170 .map(function(p) { 1171 return f.properties[p]; 1172 }) 1173 .filter(function(v) { 1174 return !!v; 1175 }) 1176 .join(', '); 1177 } 1178 }); 1179 1180 function photon(options) { 1181 return new Photon(options); 1182 } 1183 1184 var What3Words = L.Class.extend({ 1185 options: { 1186 serviceUrl: 'https://api.what3words.com/v2/' 1187 }, 1188 1189 initialize: function(accessToken) { 1190 this._accessToken = accessToken; 1191 }, 1192 1193 geocode: function(query, cb, context) { 1194 //get three words and make a dot based string 1195 getJSON( 1196 this.options.serviceUrl + 'forward', 1197 { 1198 key: this._accessToken, 1199 addr: query.split(/\s+/).join('.') 1200 }, 1201 function(data) { 1202 var results = [], 1203 latLng, 1204 latLngBounds; 1205 if (data.geometry) { 1206 latLng = L.latLng(data.geometry['lat'], data.geometry['lng']); 1207 latLngBounds = L.latLngBounds(latLng, latLng); 1208 results[0] = { 1209 name: data.words, 1210 bbox: latLngBounds, 1211 center: latLng 1212 }; 1213 } 1214 1215 cb.call(context, results); 1216 } 1217 ); 1218 }, 1219 1220 suggest: function(query, cb, context) { 1221 return this.geocode(query, cb, context); 1222 }, 1223 1224 reverse: function(location, scale, cb, context) { 1225 getJSON( 1226 this.options.serviceUrl + 'reverse', 1227 { 1228 key: this._accessToken, 1229 coords: [location.lat, location.lng].join(',') 1230 }, 1231 function(data) { 1232 var results = [], 1233 latLng, 1234 latLngBounds; 1235 if (data.status.status == 200) { 1236 latLng = L.latLng(data.geometry['lat'], data.geometry['lng']); 1237 latLngBounds = L.latLngBounds(latLng, latLng); 1238 results[0] = { 1239 name: data.words, 1240 bbox: latLngBounds, 1241 center: latLng 1242 }; 1243 } 1244 cb.call(context, results); 1245 } 1246 ); 1247 } 1248 }); 1249 1250 function what3words(accessToken) { 1251 return new What3Words(accessToken); 1252 } 1253 1254 1255 1256 var geocoders = /*#__PURE__*/Object.freeze({ 1257 ArcGis: ArcGis, 1258 arcgis: arcgis, 1259 Bing: Bing, 1260 bing: bing, 1261 Google: Google, 1262 google: google, 1263 HERE: HERE, 1264 here: here, 1265 LatLng: LatLng, 1266 latLng: latLng, 1267 Mapbox: Mapbox, 1268 mapbox: mapbox, 1269 MapQuest: MapQuest, 1270 mapQuest: mapQuest, 1271 Neutrino: Neutrino, 1272 neutrino: neutrino, 1273 Nominatim: Nominatim, 1274 nominatim: nominatim, 1275 OpenLocationCode: OpenLocationCode, 1276 openLocationCode: openLocationCode, 1277 Pelias: Pelias, 1278 pelias: pelias, 1279 GeocodeEarth: GeocodeEarth, 1280 geocodeEarth: geocodeEarth, 1281 Mapzen: Mapzen, 1282 mapzen: mapzen, 1283 Openrouteservice: Openrouteservice, 1284 openrouteservice: openrouteservice, 1285 Photon: Photon, 1286 photon: photon, 1287 What3Words: What3Words, 1288 what3words: what3words 1289 }); 1290 1291 var Geocoder = L.Control.extend({ 1292 options: { 1293 showUniqueResult: true, 1294 showResultIcons: false, 1295 collapsed: true, 1296 expand: 'touch', // options: touch, click, anythingelse 1297 position: 'topright', 1298 placeholder: 'Search...', 1299 errorMessage: 'Nothing found.', 1300 queryMinLength: 1, 1301 suggestMinLength: 3, 1302 suggestTimeout: 250, 1303 defaultMarkGeocode: true 1304 }, 1305 1306 includes: L.Evented.prototype || L.Mixin.Events, 1307 1308 initialize: function(options) { 1309 L.Util.setOptions(this, options); 1310 if (!this.options.geocoder) { 1311 this.options.geocoder = new Nominatim(); 1312 } 1313 1314 this._requestCount = 0; 1315 }, 1316 1317 addThrobberClass: function() { 1318 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber'); 1319 }, 1320 1321 removeThrobberClass: function() { 1322 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber'); 1323 }, 1324 1325 onAdd: function(map) { 1326 var className = 'leaflet-control-geocoder', 1327 container = L.DomUtil.create('div', className + ' leaflet-bar'), 1328 icon = L.DomUtil.create('button', className + '-icon', container), 1329 form = (this._form = L.DomUtil.create('div', className + '-form', container)), 1330 input; 1331 1332 this._map = map; 1333 this._container = container; 1334 1335 icon.innerHTML = ' '; 1336 icon.type = 'button'; 1337 1338 input = this._input = L.DomUtil.create('input', '', form); 1339 input.type = 'text'; 1340 input.placeholder = this.options.placeholder; 1341 L.DomEvent.disableClickPropagation(input); 1342 1343 this._errorElement = L.DomUtil.create('div', className + '-form-no-error', container); 1344 this._errorElement.innerHTML = this.options.errorMessage; 1345 1346 this._alts = L.DomUtil.create( 1347 'ul', 1348 className + '-alternatives leaflet-control-geocoder-alternatives-minimized', 1349 container 1350 ); 1351 L.DomEvent.disableClickPropagation(this._alts); 1352
1353 L.DomEvent.addListener(input, 'keydown', this._keydown, this); 1354 if (this.options.geocoder.suggest) { 1355 L.DomEvent.addListener(input, 'input', this._change, this); 1356 } 1357 L.DomEvent.addListener( 1358 input, 1359 'blur', 1360 function() { 1361 if (this.options.collapsed && !this._preventBlurCollapse) { 1362 this._collapse(); 1363 } 1364 this._preventBlurCollapse = false; 1365 }, 1366 this 1367 ); 1368 1369 if (this.options.collapsed) { 1370 if (this.options.expand === 'click') { 1371 L.DomEvent.addListener( 1372 container, 1373 'click', 1374 function(e) { 1375 if (e.button === 0 && e.detail !== 2) { 1376 this._toggle(); 1377 } 1378 }, 1379 this 1380 ); 1381 } else if (L.Browser.touch && this.options.expand === 'touch') { 1382 L.DomEvent.addListener( 1383 container, 1384 'touchstart mousedown', 1385 function(e) { 1386 this._toggle(); 1387 e.preventDefault(); // mobile: clicking focuses the icon, so UI expands and immediately collapses 1388 e.stopPropagation(); 1389 }, 1390 this 1391 ); 1392 } else { 1393 L.DomEvent.addListener(container, 'mouseover', this._expand, this); 1394 L.DomEvent.addListener(container, 'mouseout', this._collapse, this); 1395 this._map.on('movestart', this._collapse, this); 1396 } 1397 } else { 1398 this._expand(); 1399 if (L.Browser.touch) { 1400 L.DomEvent.addListener( 1401 container, 1402 'touchstart', 1403 function() { 1404 this._geocode(); 1405 }, 1406 this 1407 ); 1408 } else { 1409 L.DomEvent.addListener( 1410 container, 1411 'click', 1412 function() { 1413 this._geocode(); 1414 }, 1415 this 1416 ); 1417 } 1418 } 1419 1420 if (this.options.defaultMarkGeocode) { 1421 this.on('markgeocode', this.markGeocode, this); 1422 } 1423 1424 this.on('startgeocode', this.addThrobberClass, this); 1425 this.on('finishgeocode', this.removeThrobberClass, this); 1426 this.on('startsuggest', this.addThrobberClass, this); 1427 this.on('finishsuggest', this.removeThrobberClass, this); 1428 1429 L.DomEvent.disableClickPropagation(container); 1430 1431 return container; 1432 }, 1433 1434 _geocodeResult: function(results, suggest) { 1435 if (!suggest && this.options.showUniqueResult && results.length === 1) { 1436 this._geocodeResultSelected(results[0]); 1437 } else if (results.length > 0) { 1438 this._alts.innerHTML = ''; 1439 this._results = results; 1440 L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); 1441 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-open'); 1442 for (var i = 0; i < results.length; i++) { 1443 this._alts.appendChild(this._createAlt(results[i], i)); 1444 } 1445 } else { 1446 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-error'); 1447 L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error'); 1448 } 1449 }, 1450 1451 markGeocode: function(result) { 1452 result = result.geocode || result; 1453 1454 this._map.fitBounds(result.bbox); 1455 1456 if (this._geocodeMarker) { 1457 this._map.removeLayer(this._geocodeMarker); 1458 } 1459 1460 this._geocodeMarker = new L.Marker(result.center) 1461 .bindPopup(result.html || result.name) 1462 .addTo(this._map) 1463 .openPopup(); 1464 1465 return this; 1466 }, 1467 1468 _geocode: function(suggest) { 1469 var value = this._input.value; 1470 if (!suggest && value.length < this.options.queryMinLength) { 1471 return; 1472 } 1473 1474 var requestCount = ++this._requestCount, 1475 mode = suggest ? 'suggest' : 'geocode', 1476 eventData = { input: value }; 1477 1478 this._lastGeocode = value; 1479 if (!suggest) { 1480 this._clearResults(); 1481 } 1482 1483 this.fire('start' + mode, eventData); 1484 this.options.geocoder[mode]( 1485 value, 1486 function(results) { 1487 if (requestCount === this._requestCount) { 1488 eventData.results = results; 1489 this.fire('finish' + mode, eventData); 1490 this._geocodeResult(results, suggest); 1491 } 1492 }, 1493 this 1494 ); 1495 }, 1496 1497 _geocodeResultSelected: function(result) { 1498 this.fire('markgeocode', { geocode: result }); 1499 }, 1500 1501 _toggle: function() { 1502 if (L.DomUtil.hasClass(this._container, 'leaflet-control-geocoder-expanded')) { 1503 this._collapse(); 1504 } else { 1505 this._expand(); 1506 } 1507 }, 1508 1509 _expand: function() { 1510 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded'); 1511 this._input.select(); 1512 this.fire('expand'); 1513 }, 1514 1515 _collapse: function() { 1516 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-expanded'); 1517 L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); 1518 L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error'); 1519 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open'); 1520 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error'); 1521 this._input.blur(); // mobile: keyboard shouldn't stay expanded 1522 this.fire('collapse'); 1523 }, 1524 1525 _clearResults: function() { 1526 L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); 1527 this._selection = null; 1528 L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error'); 1529 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open'); 1530 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error'); 1531 }, 1532 1533 _createAlt: function(result, index) { 1534 var li = L.DomUtil.create('li', ''), 1535 a = L.DomUtil.create('a', '', li), 1536 icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null, 1537 text = result.html ? undefined : document.createTextNode(result.name), 1538 mouseDownHandler = function mouseDownHandler(e) { 1539 // In some browsers, a click will fire on the map if the control is
1540 // collapsed directly after mousedown. To work around this, we 1541 // wait until the click is completed, and _then_ collapse the 1542 // control. Messy, but this is the workaround I could come up with 1543 // for #142. 1544 this._preventBlurCollapse = true; 1545 L.DomEvent.stop(e); 1546 this._geocodeResultSelected(result); 1547 L.DomEvent.on( 1548 li, 1549 'click', 1550 function() { 1551 if (this.options.collapsed) { 1552 this._collapse(); 1553 } else { 1554 this._clearResults(); 1555 } 1556 }, 1557 this 1558 ); 1559 }; 1560 1561 if (icon) { 1562 icon.src = result.icon; 1563 } 1564 1565 li.setAttribute('data-result-index', index); 1566 1567 if (result.html) { 1568 a.innerHTML = a.innerHTML + result.html; 1569 } else { 1570 a.appendChild(text); 1571 } 1572 1573 // Use mousedown and not click, since click will fire _after_ blur, 1574 // causing the control to have collapsed and removed the items 1575 // before the click can fire. 1576 L.DomEvent.addListener(li, 'mousedown touchstart', mouseDownHandler, this); 1577 1578 return li; 1579 }, 1580 1581 _keydown: function(e) { 1582 var _this = this, 1583 select = function select(dir) { 1584 if (_this._selection) { 1585 L.DomUtil.removeClass(_this._selection, 'leaflet-control-geocoder-selected'); 1586 _this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling']; 1587 } 1588 if (!_this._selection) { 1589 _this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild']; 1590 } 1591 1592 if (_this._selection) { 1593 L.DomUtil.addClass(_this._selection, 'leaflet-control-geocoder-selected'); 1594 } 1595 }; 1596 1597 switch (e.keyCode) { 1598 // Escape 1599 case 27: 1600 if (this.options.collapsed) { 1601 this._collapse(); 1602 } else { 1603 this._clearResults(); 1604 } 1605 break; 1606 // Up 1607 case 38: 1608 select(-1); 1609 break; 1610 // Up 1611 case 40: 1612 select(1); 1613 break; 1614 // Enter 1615 case 13: 1616 if (this._selection) { 1617 var index = parseInt(this._selection.getAttribute('data-result-index'), 10); 1618 this._geocodeResultSelected(this._results[index]); 1619 this._clearResults(); 1620 } else { 1621 this._geocode(); 1622 } 1623 break; 1624 default: 1625 return; 1626 } 1627 1628 L.DomEvent.preventDefault(e); 1629 }, 1630 _change: function() { 1631 var v = this._input.value; 1632 if (v !== this._lastGeocode) { 1633 clearTimeout(this._suggestTimeout); 1634 if (v.length >= this.options.suggestMinLength) { 1635 this._suggestTimeout = setTimeout( 1636 L.bind(function() { 1637 this._geocode(true); 1638 }, this), 1639 this.options.suggestTimeout 1640 ); 1641 } else { 1642 this._clearResults(); 1643 } 1644 } 1645 } 1646 }); 1647 1648 function geocoder(options) { 1649 return new Geocoder(options); 1650 } 1651 1652 L.Util.extend(Geocoder, geocoders); 1653 1654 L.Util.extend(L.Control, { 1655 Geocoder: Geocoder, 1656 geocoder: geocoder 1657 }); 1658 1659 return Geocoder; 1660 1661}(L)); 1662//# sourceMappingURL=Control.Geocoder.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.