1/* @preserve 2 * Leaflet Control Geocoder 1.13.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 '&': '&', 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: '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 reverseGeocodeProxRadius: null 368 }, 369 initialize: function(options) { 370 L.setOptions(this, options); 371 }, 372 geocode: function(query, cb, context) { 373 var params = { 374 searchtext: query, 375 gen: 9, 376 app_id: this.options.app_id, 377 app_code: this.options.app_code, 378 jsonattributes: 1 379 }; 380 params = L.Util.extend(params, this.options.geocodingQueryParams); 381 this.getJSON(this.options.geocodeUrl, params, cb, context); 382 }, 383 reverse: function(location, scale, cb, context) { 384 var _proxRadius = this.options.reverseGeocodeProxRadius 385 ? this.options.reverseGeocodeProxRadius 386 : null; 387 var proxRadius = _proxRadius ? ',' + encodeURIComponent(_proxRadius) : ''; 388 var params = { 389 prox: encodeURIComponent(location.lat) + ',' + encodeURIComponent(location.lng) + proxRadius, 390 mode: 'retrieveAddresses', 391 app_id: this.options.app_id, 392 app_code: this.options.app_code, 393 gen: 9, 394 jsonattributes: 1 395 }; 396 params = L.Util.extend(params, this.options.reverseQueryParams); 397 this.getJSON(this.options.reverseGeocodeUrl, params, cb, context); 398 }, 399 getJSON: function(url, params, cb, context) { 400 getJSON(url, params, function(data) { 401 var results = [], 402 loc, 403 latLng, 404 latLngBounds; 405 if (data.response.view && data.response.view.length) { 406 for (var i = 0; i <= data.response.view[0].result.length - 1; i++) { 407 loc = data.response.view[0].result[i].location; 408 latLng = L.latLng(loc.displayPosition.latitude, loc.displayPosition.longitude); 409 latLngBounds = L.latLngBounds( 410 L.latLng(loc.mapView.topLeft.latitude, loc.mapView.topLeft.longitude), 411 L.latLng(loc.mapView.bottomRight.latitude, loc.mapView.bottomRight.longitude) 412 ); 413 results[i] = { 414 name: loc.address.label, 415 properties: loc.address, 416 bbox: latLngBounds, 417 center: latLng 418 }; 419 } 420 } 421 cb.call(context, results); 422 }); 423 } 424 }); 425 function here(options) { 426 return new HERE(options); 427 } 428 429 var LatLng = L.Class.extend({ 430 options: { 431 // the next geocoder to use 432 next: undefined, 433 sizeInMeters: 10000 434 }, 435 436 initialize: function(options) { 437 L.Util.setOptions(this, options); 438 }, 439 440 geocode: function(query, cb, context) { 441 var match; 442 var center; 443 // regex from https://github.com/openstreetmap/openstreetmap-website/blob/master/app/controllers/geocoder_controller.rb 444 if ((match = query.match(/^([NS])\s*(\d{1,3}(?:\.\d*)?)\W*([EW])\s*(\d{1,3}(?:\.\d*)?)$/))) { 445 // [NSEW] decimal degrees 446 center = L.latLng( 447 (/N/i.test(match[1]) ? 1 : -1) * parseFloat(match[2]), 448 (/E/i.test(match[3]) ? 1 : -1) * parseFloat(match[4]) 449 ); 450 } else if ( 451 (match = query.match(/^(\d{1,3}(?:\.\d*)?)\s*([NS])\W*(\d{1,3}(?:\.\d*)?)\s*([EW])$/)) 452 ) { 453 // decimal degrees [NSEW] 454 center = L.latLng( 455 (/N/i.test(match[2]) ? 1 : -1) * parseFloat(match[1]), 456 (/E/i.test(match[4]) ? 1 : -1) * parseFloat(match[3]) 457 ); 458 } else if ( 459 (match = query.match( 460 /^([NS])\s*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['â²]?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['â²]?$/ 461 )) 462 ) { 463 // [NSEW] degrees, decimal minutes 464 center = L.latLng( 465 (/N/i.test(match[1]) ? 1 : -1) * (parseFloat(match[2]) + parseFloat(match[3] / 60)), 466 (/E/i.test(match[4]) ? 1 : -1) * (parseFloat(match[5]) + parseFloat(match[6] / 60)) 467 ); 468 } else if ( 469 (match = query.match( 470 /^(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['â²]?\s*([NS])\W*(\d{1,3})°?\s*(\d{1,3}(?:\.\d*)?)?['â²]?\s*([EW])$/ 471 )) 472 ) { 473 // degrees, decimal minutes [NSEW] 474 center = L.latLng( 475 (/N/i.test(match[3]) ? 1 : -1) * (parseFloat(match[1]) + parseFloat(match[2] / 60)), 476 (/E/i.test(match[6]) ? 1 : -1) * (parseFloat(match[4]) + parseFloat(match[5] / 60)) 477 ); 478 } else if ( 479 (match = query.match( 480 /^([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*)?)?["â³]?$/ 481 )) 482 ) { 483 // [NSEW] degrees, minutes, decimal seconds 484 center = L.latLng( 485 (/N/i.test(match[1]) ? 1 : -1) * 486 (parseFloat(match[2]) + parseFloat(match[3] / 60 + parseFloat(match[4] / 3600))), 487 (/E/i.test(match[5]) ? 1 : -1) * 488 (parseFloat(match[6]) + parseFloat(match[7] / 60) + parseFloat(match[8] / 3600)) 489 ); 490 } else if ( 491 (match = query.match( 492 /^(\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])$/ 493 )) 494 ) { 495 // degrees, minutes, decimal seconds [NSEW] 496 center = L.latLng( 497 (/N/i.test(match[4]) ? 1 : -1) * 498 (parseFloat(match[1]) + parseFloat(match[2] / 60 + parseFloat(match[3] / 3600))), 499 (/E/i.test(match[8]) ? 1 : -1) * 500 (parseFloat(match[5]) + parseFloat(match[6] / 60) + parseFloat(match[7] / 3600)) 501 ); 502 } else if ( 503 (match = query.match(/^\s*([+-]?\d+(?:\.\d*)?)\s*[\s,]\s*([+-]?\d+(?:\.\d*)?)\s*$/)) 504 ) { 505 center = L.latLng(parseFloat(match[1]), parseFloat(match[2])); 506 } 507 if (center) { 508 var results = [ 509 { 510 name: query, 511 center: center, 512 bbox: center.toBounds(this.options.sizeInMeters) 513 } 514 ]; 515 cb.call(context, results); 516 } else if (this.options.next) { 517 this.options.next.geocode(query, cb, context); 518 } 519 } 520 }); 521 522 function latLng(options) {
523 return new LatLng(options); 524 } 525 526 var Mapbox = L.Class.extend({ 527 options: { 528 serviceUrl: 'https://api.mapbox.com/geocoding/v5/mapbox.places/', 529 geocodingQueryParams: {}, 530 reverseQueryParams: {} 531 }, 532 533 initialize: function(accessToken, options) { 534 L.setOptions(this, options); 535 this.options.geocodingQueryParams.access_token = accessToken; 536 this.options.reverseQueryParams.access_token = accessToken; 537 }, 538 539 geocode: function(query, cb, context) { 540 var params = this.options.geocodingQueryParams; 541 if ( 542 params.proximity !== undefined && 543 params.proximity.lat !== undefined && 544 params.proximity.lng !== undefined 545 ) { 546 params.proximity = params.proximity.lng + ',' + params.proximity.lat; 547 } 548 getJSON(this.options.serviceUrl + encodeURIComponent(query) + '.json', params, function(data) { 549 var results = [], 550 loc, 551 latLng, 552 latLngBounds; 553 if (data.features && data.features.length) { 554 for (var i = 0; i <= data.features.length - 1; i++) { 555 loc = data.features[i]; 556 latLng = L.latLng(loc.center.reverse()); 557 if (loc.bbox) { 558 latLngBounds = L.latLngBounds( 559 L.latLng(loc.bbox.slice(0, 2).reverse()), 560 L.latLng(loc.bbox.slice(2, 4).reverse()) 561 ); 562 } else { 563 latLngBounds = L.latLngBounds(latLng, latLng); 564 } 565 566 var properties = { 567 text: loc.text, 568 address: loc.address 569 }; 570 571 for (var j = 0; j < (loc.context || []).length; j++) { 572 var id = loc.context[j].id.split('.')[0]; 573 properties[id] = loc.context[j].text; 574 575 // Get country code when available 576 if (loc.context[j].short_code) { 577 properties['countryShortCode'] = loc.context[j].short_code; 578 } 579 } 580 581 results[i] = { 582 name: loc.place_name, 583 bbox: latLngBounds, 584 center: latLng, 585 properties: properties 586 }; 587 } 588 } 589 590 cb.call(context, results); 591 }); 592 }, 593 594 suggest: function(query, cb, context) { 595 return this.geocode(query, cb, context); 596 }, 597 598 reverse: function(location, scale, cb, context) { 599 getJSON( 600 this.options.serviceUrl + 601 encodeURIComponent(location.lng) + 602 ',' + 603 encodeURIComponent(location.lat) + 604 '.json', 605 this.options.reverseQueryParams, 606 function(data) { 607 var results = [], 608 loc, 609 latLng, 610 latLngBounds; 611 if (data.features && data.features.length) { 612 for (var i = 0; i <= data.features.length - 1; i++) { 613 loc = data.features[i]; 614 latLng = L.latLng(loc.center.reverse()); 615 if (loc.bbox) { 616 latLngBounds = L.latLngBounds( 617 L.latLng(loc.bbox.slice(0, 2).reverse()), 618 L.latLng(loc.bbox.slice(2, 4).reverse()) 619 ); 620 } else { 621 latLngBounds = L.latLngBounds(latLng, latLng); 622 } 623 results[i] = { 624 name: loc.place_name, 625 bbox: latLngBounds, 626 center: latLng 627 }; 628 } 629 } 630 631 cb.call(context, results); 632 } 633 ); 634 } 635 }); 636 637 function mapbox(accessToken, options) { 638 return new Mapbox(accessToken, options); 639 } 640 641 var MapQuest = L.Class.extend({ 642 options: { 643 serviceUrl: 'https://www.mapquestapi.com/geocoding/v1' 644 }, 645 646 initialize: function(key, options) { 647 // MapQuest seems to provide URI encoded API keys, 648 // so to avoid encoding them twice, we decode them here 649 this._key = decodeURIComponent(key); 650 651 L.Util.setOptions(this, options); 652 }, 653 654 _formatName: function() { 655 var r = [], 656 i; 657 for (i = 0; i < arguments.length; i++) { 658 if (arguments[i]) { 659 r.push(arguments[i]); 660 } 661 } 662 663 return r.join(', '); 664 }, 665 666 geocode: function(query, cb, context) { 667 getJSON( 668 this.options.serviceUrl + '/address', 669 { 670 key: this._key, 671 location: query, 672 limit: 5, 673 outFormat: 'json' 674 }, 675 L.bind(function(data) { 676 var results = [], 677 loc, 678 latLng; 679 if (data.results && data.results[0].locations) { 680 for (var i = data.results[0].locations.length - 1; i >= 0; i--) { 681 loc = data.results[0].locations[i]; 682 latLng = L.latLng(loc.latLng); 683 results[i] = { 684 name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1), 685 bbox: L.latLngBounds(latLng, latLng), 686 center: latLng 687 }; 688 } 689 } 690 691 cb.call(context, results); 692 }, this) 693 ); 694 }, 695 696 reverse: function(location, scale, cb, context) { 697 getJSON( 698 this.options.serviceUrl + '/reverse', 699 { 700 key: this._key, 701 location: location.lat + ',' + location.lng, 702 outputFormat: 'json' 703 }, 704 L.bind(function(data) { 705 var results = [], 706 loc, 707 latLng; 708 if (data.results && data.results[0].locations) { 709 for (var i = data.results[0].locations.length - 1; i >= 0; i--) { 710 loc = data.results[0].locations[i]; 711 latLng = L.latLng(loc.latLng); 712 results[i] = { 713 name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1), 714 bbox: L.latLngBounds(latLng, latLng), 715 center: latLng 716 }; 717 } 718 } 719 720 cb.call(context, results); 721 }, this) 722 ); 723 } 724 }); 725 726 function mapQuest(key, options) { 727 return new MapQuest(key, options); 728 } 729 730 var Neutrino = L.Class.extend({ 731 options: { 732 userId: '<insert your userId here>', 733 apiKey: '<insert your apiKey here>', 734 serviceUrl: 'https://neutrinoapi.com/' 735 }, 736 737 initialize: function(options) { 738 L.Util.setOptions(this, options); 739 }, 740
741 // https://www.neutrinoapi.com/api/geocode-address/ 742 geocode: function(query, cb, context) { 743 getJSON( 744 this.options.serviceUrl + 'geocode-address', 745 { 746 apiKey: this.options.apiKey, 747 userId: this.options.userId, 748 //get three words and make a dot based string 749 address: query.split(/\s+/).join('.') 750 }, 751 function(data) { 752 var results = [], 753 latLng, 754 latLngBounds; 755 if (data.locations) { 756 data.geometry = data.locations[0]; 757 latLng = L.latLng(data.geometry['latitude'], data.geometry['longitude']); 758 latLngBounds = L.latLngBounds(latLng, latLng); 759 results[0] = { 760 name: data.geometry.address, 761 bbox: latLngBounds, 762 center: latLng 763 }; 764 } 765 766 cb.call(context, results); 767 } 768 ); 769 }, 770 771 suggest: function(query, cb, context) { 772 return this.geocode(query, cb, context); 773 }, 774 775 // https://www.neutrinoapi.com/api/geocode-reverse/ 776 reverse: function(location, scale, cb, context) { 777 getJSON( 778 this.options.serviceUrl + 'geocode-reverse', 779 { 780 apiKey: this.options.apiKey, 781 userId: this.options.userId, 782 latitude: location.lat, 783 longitude: location.lng 784 }, 785 function(data) { 786 var results = [], 787 latLng, 788 latLngBounds; 789 if (data.status.status == 200 && data.found) { 790 latLng = L.latLng(location.lat, location.lng); 791 latLngBounds = L.latLngBounds(latLng, latLng); 792 results[0] = { 793 name: data.address, 794 bbox: latLngBounds, 795 center: latLng 796 }; 797 } 798 cb.call(context, results); 799 } 800 ); 801 } 802 }); 803 804 function neutrino(accessToken) { 805 return new Neutrino(accessToken); 806 } 807 808 var Nominatim = L.Class.extend({ 809 options: { 810 serviceUrl: 'https://nominatim.openstreetmap.org/', 811 geocodingQueryParams: {}, 812 reverseQueryParams: {}, 813 htmlTemplate: function(r) { 814 var a = r.address, 815 className, 816 parts = []; 817 if (a.road || a.building) { 818 parts.push('{building} {road} {house_number}'); 819 } 820 821 if (a.city || a.town || a.village || a.hamlet) { 822 className = parts.length > 0 ? 'leaflet-control-geocoder-address-detail' : ''; 823 parts.push( 824 '<span class="' + className + '">{postcode} {city} {town} {village} {hamlet}</span>' 825 ); 826 } 827 828 if (a.state || a.country) { 829 className = parts.length > 0 ? 'leaflet-control-geocoder-address-context' : ''; 830 parts.push('<span class="' + className + '">{state} {country}</span>'); 831 } 832 833 return template(parts.join('<br/>'), a, true); 834 } 835 }, 836 837 initialize: function(options) { 838 L.Util.setOptions(this, options); 839 }, 840 841 geocode: function(query, cb, context) { 842 getJSON( 843 this.options.serviceUrl + 'search', 844 L.extend( 845 { 846 q: query, 847 limit: 5, 848 format: 'json', 849 addressdetails: 1 850 }, 851 this.options.geocodingQueryParams 852 ), 853 L.bind(function(data) { 854 var results = []; 855 for (var i = data.length - 1; i >= 0; i--) { 856 var bbox = data[i].boundingbox; 857 for (var j = 0; j < 4; j++) bbox[j] = parseFloat(bbox[j]); 858 results[i] = { 859 icon: data[i].icon, 860 name: data[i].display_name, 861 html: this.options.htmlTemplate ? this.options.htmlTemplate(data[i]) : undefined, 862 bbox: L.latLngBounds([bbox[0], bbox[2]], [bbox[1], bbox[3]]), 863 center: L.latLng(data[i].lat, data[i].lon), 864 properties: data[i] 865 }; 866 } 867 cb.call(context, results); 868 }, this) 869 ); 870 }, 871 872 reverse: function(location, scale, cb, context) { 873 getJSON( 874 this.options.serviceUrl + 'reverse', 875 L.extend( 876 { 877 lat: location.lat, 878 lon: location.lng, 879 zoom: Math.round(Math.log(scale / 256) / Math.log(2)), 880 addressdetails: 1, 881 format: 'json' 882 }, 883 this.options.reverseQueryParams 884 ), 885 L.bind(function(data) { 886 var result = [], 887 loc; 888 889 if (data && data.lat && data.lon) { 890 loc = L.latLng(data.lat, data.lon); 891 result.push({ 892 name: data.display_name, 893 html: this.options.htmlTemplate ? this.options.htmlTemplate(data) : undefined, 894 center: loc, 895 bounds: L.latLngBounds(loc, loc), 896 properties: data 897 }); 898 } 899 900 cb.call(context, result); 901 }, this) 902 ); 903 } 904 }); 905 906 function nominatim(options) { 907 return new Nominatim(options); 908 } 909 910 var OpenLocationCode = L.Class.extend({ 911 options: { 912 OpenLocationCode: undefined, 913 codeLength: undefined 914 }, 915 916 initialize: function(options) { 917 L.setOptions(this, options); 918 }, 919 920 geocode: function(query, cb, context) {
921 try { 922 var decoded = this.options.OpenLocationCode.decode(query); 923 var result = { 924 name: query, 925 center: L.latLng(decoded.latitudeCenter, decoded.longitudeCenter), 926 bbox: L.latLngBounds( 927 L.latLng(decoded.latitudeLo, decoded.longitudeLo), 928 L.latLng(decoded.latitudeHi, decoded.longitudeHi) 929 ) 930 }; 931 cb.call(context, [result]); 932 } catch (e) { 933 console.warn(e); // eslint-disable-line no-console 934 cb.call(context, []); 935 } 936 }, 937 reverse: function(location, scale, cb, context) { 938 try { 939 var code = this.options.OpenLocationCode.encode( 940 location.lat, 941 location.lng, 942 this.options.codeLength 943 ); 944 var result = { 945 name: code, 946 center: L.latLng(location.lat, location.lng), 947 bbox: L.latLngBounds( 948 L.latLng(location.lat, location.lng), 949 L.latLng(location.lat, location.lng) 950 ) 951 }; 952 cb.call(context, [result]); 953 } catch (e) { 954 console.warn(e); // eslint-disable-line no-console 955 cb.call(context, []); 956 } 957 } 958 }); 959 960 function openLocationCode(options) { 961 return new OpenLocationCode(options); 962 } 963 964 var OpenCage = L.Class.extend({ 965 options: { 966 serviceUrl: 'https://api.opencagedata.com/geocode/v1/json', 967 geocodingQueryParams: {}, 968 reverseQueryParams: {} 969 }, 970 971 initialize: function(apiKey, options) { 972 L.setOptions(this, options); 973 this._accessToken = apiKey; 974 }, 975 976 geocode: function(query, cb, context) { 977 var params = { 978 key: this._accessToken, 979 q: query 980 }; 981 params = L.extend(params, this.options.geocodingQueryParams); 982 getJSON(this.options.serviceUrl, params, function(data) { 983 var results = [], 984 latLng, 985 latLngBounds, 986 loc; 987 if (data.results && data.results.length) { 988 for (var i = 0; i < data.results.length; i++) { 989 loc = data.results[i]; 990 latLng = L.latLng(loc.geometry); 991 if (loc.annotations && loc.annotations.bounds) { 992 latLngBounds = L.latLngBounds( 993 L.latLng(loc.annotations.bounds.northeast), 994 L.latLng(loc.annotations.bounds.southwest) 995 ); 996 } else { 997 latLngBounds = L.latLngBounds(latLng, latLng); 998 } 999 results.push({ 1000 name: loc.formatted, 1001 bbox: latLngBounds, 1002 center: latLng 1003 }); 1004 } 1005 } 1006 cb.call(context, results); 1007 }); 1008 }, 1009 1010 suggest: function(query, cb, context) { 1011 return this.geocode(query, cb, context); 1012 }, 1013 1014 reverse: function(location, scale, cb, context) { 1015 var params = { 1016 key: this._accessToken, 1017 q: [location.lat, location.lng].join(',') 1018 }; 1019 params = L.extend(params, this.options.reverseQueryParams); 1020 getJSON(this.options.serviceUrl, params, function(data) { 1021 var results = [], 1022 latLng, 1023 latLngBounds, 1024 loc; 1025 if (data.results && data.results.length) { 1026 for (var i = 0; i < data.results.length; i++) { 1027 loc = data.results[i]; 1028 latLng = L.latLng(loc.geometry); 1029 if (loc.annotations && loc.annotations.bounds) { 1030 latLngBounds = L.latLngBounds( 1031 L.latLng(loc.annotations.bounds.northeast), 1032 L.latLng(loc.annotations.bounds.southwest) 1033 ); 1034 } else { 1035 latLngBounds = L.latLngBounds(latLng, latLng); 1036 } 1037 results.push({ 1038 name: loc.formatted, 1039 bbox: latLngBounds, 1040 center: latLng 1041 }); 1042 } 1043 } 1044 cb.call(context, results); 1045 }); 1046 } 1047 }); 1048 1049 function opencage(apiKey, options) { 1050 return new OpenCage(apiKey, options); 1051 } 1052 1053 var Pelias = L.Class.extend({ 1054 options: { 1055 serviceUrl: 'https://api.geocode.earth/v1', 1056 geocodingQueryParams: {}, 1057 reverseQueryParams: {} 1058 }, 1059 1060 initialize: function(apiKey, options) { 1061 L.Util.setOptions(this, options); 1062 this._apiKey = apiKey; 1063 this._lastSuggest = 0; 1064 }, 1065 1066 geocode: function(query, cb, context) { 1067 var _this = this; 1068 getJSON( 1069 this.options.serviceUrl + '/search', 1070 L.extend( 1071 { 1072 api_key: this._apiKey, 1073 text: query 1074 }, 1075 this.options.geocodingQueryParams 1076 ), 1077 function(data) { 1078 cb.call(context, _this._parseResults(data, 'bbox')); 1079 } 1080 ); 1081 }, 1082 1083 suggest: function(query, cb, context) { 1084 var _this = this; 1085 getJSON( 1086 this.options.serviceUrl + '/autocomplete', 1087 L.extend( 1088 { 1089 api_key: this._apiKey, 1090 text: query 1091 }, 1092 this.options.geocodingQueryParams 1093 ), 1094 L.bind(function(data) { 1095 if (data.geocoding.timestamp > this._lastSuggest) { 1096 this._lastSuggest = data.geocoding.timestamp; 1097 cb.call(context, _this._parseResults(data, 'bbox')); 1098 } 1099 }, this) 1100 ); 1101 }, 1102 1103 reverse: function(location, scale, cb, context) { 1104 var _this = this; 1105 getJSON( 1106 this.options.serviceUrl + '/reverse', 1107 L.extend( 1108 { 1109 api_key: this._apiKey, 1110 'point.lat': location.lat, 1111 'point.lon': location.lng 1112 }, 1113 this.options.reverseQueryParams 1114 ), 1115 function(data) { 1116 cb.call(context, _this._parseResults(data, 'bounds')); 1117 } 1118 ); 1119 }, 1120 1121 _parseResults: function(data, bboxname) { 1122 var results = []; 1123 L.geoJson(data, { 1124 pointToLayer: function(feature, latlng) { 1125 return L.circleMarker(latlng); 1126 }, 1127 onEachFeature: function(feature, layer) { 1128 var result = {}, 1129 bbox, 1130 center; 1131 1132 if (layer.getBounds) { 1133 bbox = layer.getBounds(); 1134 center = bbox.getCenter(); 1135 } else if (layer.feature.bbox) { 1136 center = layer.getLatLng(); 1137 bbox = L.latLngBounds( 1138 L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(0, 2)), 1139 L.GeoJSON.coordsToLatLng(layer.feature.bbox.slice(2, 4)) 1140 ); 1141 } else { 1142 center = layer.getLatLng(); 1143 bbox = L.latLngBounds(center, center); 1144 } 1145 1146 result.name = layer.feature.properties.label; 1147 result.center = center; 1148 result[bboxname] = bbox; 1149 result.properties = layer.feature.properties; 1150 results.push(result); 1151 } 1152 }); 1153 return results; 1154 } 1155 }); 1156 1157 function pelias(apiKey, options) { 1158 return new Pelias(apiKey, options); 1159 } 1160 var GeocodeEarth = Pelias; 1161 var geocodeEarth = pelias; 1162 1163 var Mapzen = Pelias; // r.i.p. 1164 var mapzen = pelias; 1165 1166 var Openrouteservice = Mapzen.extend({ 1167 options: { 1168 serviceUrl: 'https://api.openrouteservice.org/geocode' 1169 } 1170 }); 1171 function openrouteservice(apiKey, options) { 1172 return new Openrouteservice(apiKey, options); 1173 } 1174 1175 var Photon = L.Class.extend({ 1176 options: { 1177 serviceUrl: 'https://photon.komoot.de/api/', 1178 reverseUrl: 'https://photon.komoot.de/reverse/', 1179 nameProperties: ['name', 'street', 'suburb', 'hamlet', 'town', 'city', 'state', 'country'] 1180 }, 1181 1182 initialize: function(options) { 1183 L.setOptions(this, options); 1184 }, 1185 1186 geocode: function(query, cb, context) { 1187 var params = L.extend( 1188 { 1189 q: query 1190 }, 1191 this.options.geocodingQueryParams 1192 ); 1193 1194 getJSON( 1195 this.options.serviceUrl, 1196 params, 1197 L.bind(function(data) { 1198 cb.call(context, this._decodeFeatures(data)); 1199 }, this) 1200 ); 1201 }, 1202 1203 suggest: function(query, cb, context) { 1204 return this.geocode(query, cb, context); 1205 }, 1206 1207 reverse: function(latLng, scale, cb, context) { 1208 var params = L.extend( 1209 { 1210 lat: latLng.lat, 1211 lon: latLng.lng 1212 }, 1213 this.options.reverseQueryParams 1214 ); 1215 1216 getJSON( 1217 this.options.reverseUrl, 1218 params, 1219 L.bind(function(data) { 1220 cb.call(context, this._decodeFeatures(data)); 1221 }, this) 1222 ); 1223 }, 1224 1225 _decodeFeatures: function(data) { 1226 var results = [], 1227 i, 1228 f, 1229 c, 1230 latLng, 1231 extent, 1232 bbox; 1233 1234 if (data && data.features) { 1235 for (i = 0; i < data.features.length; i++) { 1236 f = data.features[i]; 1237 c = f.geometry.coordinates; 1238 latLng = L.latLng(c[1], c[0]); 1239 extent = f.properties.extent; 1240 1241 if (extent) { 1242 bbox = L.latLngBounds([extent[1], extent[0]], [extent[3], extent[2]]); 1243 } else { 1244 bbox = L.latLngBounds(latLng, latLng); 1245 } 1246 1247 results.push({ 1248 name: this._decodeFeatureName(f), 1249 html: this.options.htmlTemplate ? this.options.htmlTemplate(f) : undefined, 1250 center: latLng, 1251 bbox: bbox, 1252 properties: f.properties 1253 }); 1254 } 1255 } 1256 1257 return results; 1258 }, 1259 1260 _decodeFeatureName: function(f) { 1261 return (this.options.nameProperties || []) 1262 .map(function(p) { 1263 return f.properties[p]; 1264 }) 1265 .filter(function(v) { 1266 return !!v; 1267 }) 1268 .join(', '); 1269 } 1270 }); 1271 1272 function photon(options) { 1273 return new Photon(options); 1274 } 1275 1276 var What3Words = L.Class.extend({ 1277 options: { 1278 serviceUrl: 'https://api.what3words.com/v2/' 1279 }, 1280 1281 initialize: function(accessToken) { 1282 this._accessToken = accessToken; 1283 }, 1284 1285 geocode: function(query, cb, context) { 1286 //get three words and make a dot based string 1287 getJSON( 1288 this.options.serviceUrl + 'forward', 1289 { 1290 key: this._accessToken, 1291 addr: query.split(/\s+/).join('.') 1292 }, 1293 function(data) { 1294 var results = [], 1295 latLng, 1296 latLngBounds; 1297 if (data.geometry) { 1298 latLng = L.latLng(data.geometry['lat'], data.geometry['lng']); 1299 latLngBounds = L.latLngBounds(latLng, latLng); 1300 results[0] = { 1301 name: data.words, 1302 bbox: latLngBounds, 1303 center: latLng 1304 }; 1305 } 1306 1307 cb.call(context, results); 1308 } 1309 ); 1310 }, 1311 1312 suggest: function(query, cb, context) { 1313 return this.geocode(query, cb, context); 1314 }, 1315 1316 reverse: function(location, scale, cb, context) { 1317 getJSON( 1318 this.options.serviceUrl + 'reverse', 1319 { 1320 key: this._accessToken, 1321 coords: [location.lat, location.lng].join(',') 1322 }, 1323 function(data) { 1324 var results = [], 1325 latLng, 1326 latLngBounds; 1327 if (data.status.status == 200) { 1328 latLng = L.latLng(data.geometry['lat'], data.geometry['lng']); 1329 latLngBounds = L.latLngBounds(latLng, latLng); 1330 results[0] = { 1331 name: data.words, 1332 bbox: latLngBounds, 1333 center: latLng 1334 }; 1335 } 1336 cb.call(context, results); 1337 } 1338 ); 1339 } 1340 }); 1341 1342 function what3words(accessToken) { 1343 return new What3Words(accessToken); 1344 } 1345 1346 1347 1348 var geocoders = /*#__PURE__*/Object.freeze({ 1349 ArcGis: ArcGis, 1350 arcgis: arcgis, 1351 Bing: Bing, 1352 bing: bing, 1353 Google: Google, 1354 google: google, 1355 HERE: HERE, 1356 here: here, 1357 LatLng: LatLng, 1358 latLng: latLng, 1359 Mapbox: Mapbox, 1360 mapbox: mapbox, 1361 MapQuest: MapQuest, 1362 mapQuest: mapQuest, 1363 Neutrino: Neutrino, 1364 neutrino: neutrino, 1365 Nominatim: Nominatim, 1366 nominatim: nominatim, 1367 OpenLocationCode: OpenLocationCode, 1368 openLocationCode: openLocationCode, 1369 OpenCage: OpenCage, 1370 opencage: opencage, 1371 Pelias: Pelias, 1372 pelias: pelias, 1373 GeocodeEarth: GeocodeEarth, 1374 geocodeEarth: geocodeEarth, 1375 Mapzen: Mapzen, 1376 mapzen: mapzen, 1377 Openrouteservice: Openrouteservice, 1378 openrouteservice: openrouteservice, 1379 Photon: Photon, 1380 photon: photon, 1381 What3Words: What3Words, 1382 what3words: what3words 1383 }); 1384 1385 var Geocoder = L.Control.extend({ 1386 options: { 1387 showUniqueResult: true, 1388 showResultIcons: false, 1389 collapsed: true, 1390 expand: 'touch', // options: touch, click, anythingelse 1391 position: 'topright', 1392 placeholder: 'Search...', 1393 errorMessage: 'Nothing found.', 1394 iconLabel: 'Initiate a new search', 1395 queryMinLength: 1, 1396 suggestMinLength: 3, 1397 suggestTimeout: 250, 1398 defaultMarkGeocode: true 1399 }, 1400 1401 includes: L.Evented.prototype || L.Mixin.Events, 1402 1403 initialize: function(options) { 1404 L.Util.setOptions(this, options); 1405 if (!this.options.geocoder) { 1406 this.options.geocoder = new Nominatim(); 1407 } 1408 1409 this._requestCount = 0; 1410 }, 1411 1412 addThrobberClass: function() { 1413 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber'); 1414 }, 1415 1416 removeThrobberClass: function() { 1417 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber'); 1418 }, 1419 1420 onAdd: function(map) { 1421 var className = 'leaflet-control-geocoder', 1422 container = L.DomUtil.create('div', className + ' leaflet-bar'), 1423 icon = L.DomUtil.create('button', className + '-icon', container), 1424 form = (this._form = L.DomUtil.create('div', className + '-form', container)), 1425 input; 1426 1427 this._map = map; 1428 this._container = container; 1429 1430 icon.innerHTML = ' '; 1431 icon.type = 'button'; 1432 icon.setAttribute('aria-label', this.options.iconLabel); 1433 1434 input = this._input = L.DomUtil.create('input', '', form); 1435 input.type = 'text'; 1436 input.value = this.options.query || ''; 1437 input.placeholder = this.options.placeholder; 1438 L.DomEvent.disableClickPropagation(input); 1439 1440 this._errorElement = L.DomUtil.create('div', className + '-form-no-error', container); 1441 this._errorElement.innerHTML = this.options.errorMessage; 1442 1443 this._alts = L.DomUtil.create( 1444 'ul', 1445 className + '-alternatives leaflet-control-geocoder-alternatives-minimized', 1446 container 1447 ); 1448 L.DomEvent.disableClickPropagation(this._alts); 1449
1450 L.DomEvent.addListener(input, 'keydown', this._keydown, this); 1451 if (this.options.geocoder.suggest) { 1452 L.DomEvent.addListener(input, 'input', this._change, this); 1453 } 1454 L.DomEvent.addListener( 1455 input, 1456 'blur', 1457 function() { 1458 if (this.options.collapsed && !this._preventBlurCollapse) { 1459 this._collapse(); 1460 } 1461 this._preventBlurCollapse = false; 1462 }, 1463 this 1464 ); 1465 1466 if (this.options.collapsed) { 1467 if (this.options.expand === 'click') { 1468 L.DomEvent.addListener( 1469 container, 1470 'click', 1471 function(e) { 1472 if (e.button === 0 && e.detail !== 2) { 1473 this._toggle(); 1474 } 1475 }, 1476 this 1477 ); 1478 } else if (this.options.expand === 'touch') { 1479 L.DomEvent.addListener( 1480 container, 1481 L.Browser.touch ? 'touchstart mousedown' : 'mousedown', 1482 function(e) { 1483 this._toggle(); 1484 e.preventDefault(); // mobile: clicking focuses the icon, so UI expands and immediately collapses 1485 e.stopPropagation(); 1486 }, 1487 this 1488 ); 1489 } else { 1490 L.DomEvent.addListener(container, 'mouseover', this._expand, this); 1491 L.DomEvent.addListener(container, 'mouseout', this._collapse, this); 1492 this._map.on('movestart', this._collapse, this); 1493 } 1494 } else { 1495 this._expand(); 1496 if (L.Browser.touch) { 1497 L.DomEvent.addListener( 1498 container, 1499 'touchstart', 1500 function() { 1501 this._geocode(); 1502 }, 1503 this 1504 ); 1505 } else { 1506 L.DomEvent.addListener( 1507 container, 1508 'click', 1509 function() { 1510 this._geocode(); 1511 }, 1512 this 1513 ); 1514 } 1515 } 1516 1517 if (this.options.defaultMarkGeocode) { 1518 this.on('markgeocode', this.markGeocode, this); 1519 } 1520 1521 this.on('startgeocode', this.addThrobberClass, this); 1522 this.on('finishgeocode', this.removeThrobberClass, this); 1523 this.on('startsuggest', this.addThrobberClass, this); 1524 this.on('finishsuggest', this.removeThrobberClass, this); 1525 1526 L.DomEvent.disableClickPropagation(container); 1527 1528 return container; 1529 }, 1530 1531 setQuery: function(string) { 1532 this._input.value = string; 1533 return this; 1534 }, 1535 1536 _geocodeResult: function(results, suggest) { 1537 if (!suggest && this.options.showUniqueResult && results.length === 1) { 1538 this._geocodeResultSelected(results[0]); 1539 } else if (results.length > 0) { 1540 this._alts.innerHTML = ''; 1541 this._results = results; 1542 L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); 1543 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-open'); 1544 for (var i = 0; i < results.length; i++) { 1545 this._alts.appendChild(this._createAlt(results[i], i)); 1546 } 1547 } else { 1548 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-options-error'); 1549 L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error'); 1550 } 1551 }, 1552 1553 markGeocode: function(result) { 1554 result = result.geocode || result; 1555 1556 this._map.fitBounds(result.bbox); 1557 1558 if (this._geocodeMarker) { 1559 this._map.removeLayer(this._geocodeMarker); 1560 } 1561 1562 this._geocodeMarker = new L.Marker(result.center) 1563 .bindPopup(result.html || result.name) 1564 .addTo(this._map) 1565 .openPopup(); 1566 1567 return this; 1568 }, 1569 1570 _geocode: function(suggest) { 1571 var value = this._input.value; 1572 if (!suggest && value.length < this.options.queryMinLength) { 1573 return; 1574 } 1575 1576 var requestCount = ++this._requestCount, 1577 mode = suggest ? 'suggest' : 'geocode', 1578 eventData = { input: value }; 1579 1580 this._lastGeocode = value; 1581 if (!suggest) { 1582 this._clearResults(); 1583 } 1584 1585 this.fire('start' + mode, eventData); 1586 this.options.geocoder[mode]( 1587 value, 1588 function(results) { 1589 if (requestCount === this._requestCount) { 1590 eventData.results = results; 1591 this.fire('finish' + mode, eventData); 1592 this._geocodeResult(results, suggest); 1593 } 1594 }, 1595 this 1596 ); 1597 }, 1598 1599 _geocodeResultSelected: function(result) { 1600 this.fire('markgeocode', { geocode: result }); 1601 }, 1602 1603 _toggle: function() { 1604 if (L.DomUtil.hasClass(this._container, 'leaflet-control-geocoder-expanded')) { 1605 this._collapse(); 1606 } else { 1607 this._expand(); 1608 } 1609 }, 1610 1611 _expand: function() { 1612 L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded'); 1613 this._input.select(); 1614 this.fire('expand'); 1615 }, 1616 1617 _collapse: function() { 1618 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-expanded'); 1619 L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); 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 this._input.blur(); // mobile: keyboard shouldn't stay expanded 1624 this.fire('collapse'); 1625 }, 1626 1627 _clearResults: function() { 1628 L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); 1629 this._selection = null; 1630 L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error'); 1631 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-open'); 1632 L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-options-error'); 1633 }, 1634 1635 _createAlt: function(result, index) { 1636 var li = L.DomUtil.create('li', ''), 1637 a = L.DomUtil.create('a', '', li), 1638 icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null, 1639 text = result.html ? undefined : document.createTextNode(result.name), 1640 mouseDownHandler = function mouseDownHandler(e) { 1641 // In some browsers, a click will fire on the map if the control is
1642 // collapsed directly after mousedown. To work around this, we 1643 // wait until the click is completed, and _then_ collapse the 1644 // control. Messy, but this is the workaround I could come up with 1645 // for #142. 1646 this._preventBlurCollapse = true; 1647 L.DomEvent.stop(e); 1648 this._geocodeResultSelected(result); 1649 L.DomEvent.on( 1650 li, 1651 'click touchend', 1652 function() { 1653 if (this.options.collapsed) { 1654 this._collapse(); 1655 } else { 1656 this._clearResults(); 1657 } 1658 }, 1659 this 1660 ); 1661 }; 1662 1663 if (icon) { 1664 icon.src = result.icon; 1665 } 1666 1667 li.setAttribute('data-result-index', index); 1668 1669 if (result.html) { 1670 a.innerHTML = a.innerHTML + result.html; 1671 } else { 1672 a.appendChild(text); 1673 } 1674 1675 // Use mousedown and not click, since click will fire _after_ blur, 1676 // causing the control to have collapsed and removed the items 1677 // before the click can fire. 1678 L.DomEvent.addListener(li, 'mousedown touchstart', mouseDownHandler, this); 1679 1680 return li; 1681 }, 1682 1683 _keydown: function(e) { 1684 var _this = this, 1685 select = function select(dir) { 1686 if (_this._selection) { 1687 L.DomUtil.removeClass(_this._selection, 'leaflet-control-geocoder-selected'); 1688 _this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling']; 1689 } 1690 if (!_this._selection) { 1691 _this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild']; 1692 } 1693 1694 if (_this._selection) { 1695 L.DomUtil.addClass(_this._selection, 'leaflet-control-geocoder-selected'); 1696 } 1697 }; 1698 1699 switch (e.keyCode) { 1700 // Escape 1701 case 27: 1702 if (this.options.collapsed) { 1703 this._collapse(); 1704 } else { 1705 this._clearResults(); 1706 } 1707 break; 1708 // Up 1709 case 38: 1710 select(-1); 1711 break; 1712 // Up 1713 case 40: 1714 select(1); 1715 break; 1716 // Enter 1717 case 13: 1718 if (this._selection) { 1719 var index = parseInt(this._selection.getAttribute('data-result-index'), 10); 1720 this._geocodeResultSelected(this._results[index]); 1721 this._clearResults(); 1722 } else { 1723 this._geocode(); 1724 } 1725 break; 1726 default: 1727 return; 1728 } 1729 1730 L.DomEvent.preventDefault(e); 1731 }, 1732 _change: function() { 1733 var v = this._input.value; 1734 if (v !== this._lastGeocode) { 1735 clearTimeout(this._suggestTimeout); 1736 if (v.length >= this.options.suggestMinLength) { 1737 this._suggestTimeout = setTimeout( 1738 L.bind(function() { 1739 this._geocode(true); 1740 }, this), 1741 this.options.suggestTimeout 1742 ); 1743 } else { 1744 this._clearResults(); 1745 } 1746 } 1747 } 1748 }); 1749 1750 function geocoder(options) { 1751 return new Geocoder(options); 1752 } 1753 1754 L.Util.extend(Geocoder, geocoders); 1755 1756 L.Util.extend(L.Control, { 1757 Geocoder: Geocoder, 1758 geocoder: geocoder 1759 }); 1760 1761 return Geocoder; 1762 1763}(L)); 1764//# 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.