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