UNPKG

leaflet-control-geocoder

Version:

Extendable geocoder with builtin support for Nominatim, Bing, Google, Mapbox, Photon, What3Words, MapQuest, Mapzen

231 lines (195 loc) 6.51 kB
var L = require('leaflet'), Nominatim = require('./geocoders/nominatim').class; module.exports = { class: L.Control.extend({ options: { showResultIcons: false, collapsed: true, expand: 'click', position: 'topright', placeholder: 'Search...', errorMessage: 'Nothing found.' }, _callbackId: 0, initialize: function (options) { L.Util.setOptions(this, options); if (!this.options.geocoder) { this.options.geocoder = new Nominatim(); } }, onAdd: function (map) { var className = 'leaflet-control-geocoder', container = L.DomUtil.create('div', className + ' leaflet-bar'), icon = L.DomUtil.create('a', 'leaflet-control-geocoder-icon', container), form = this._form = L.DomUtil.create('form', className + '-form', container), input; icon.innerHTML = '&nbsp;'; icon.href = 'javascript:void(0);'; this._map = map; this._container = container; input = this._input = L.DomUtil.create('input'); input.type = 'text'; input.placeholder = this.options.placeholder; L.DomEvent.addListener(input, 'keydown', this._keydown, this); //L.DomEvent.addListener(input, 'onpaste', this._clearResults, this); //L.DomEvent.addListener(input, 'oninput', this._clearResults, this); this._errorElement = document.createElement('div'); this._errorElement.className = className + '-form-no-error'; this._errorElement.innerHTML = this.options.errorMessage; this._alts = L.DomUtil.create('ul', className + '-alternatives leaflet-control-geocoder-alternatives-minimized'); form.appendChild(input); this._container.appendChild(this._errorElement); container.appendChild(this._alts); L.DomEvent.addListener(form, 'submit', this._geocode, this); if (this.options.collapsed) { if (this.options.expand === 'click') { L.DomEvent.addListener(icon, 'click', function(e) { // TODO: touch if (e.button === 0 && e.detail !== 2) { this._toggle(); } }, this); } else { L.DomEvent.addListener(icon, 'mouseover', this._expand, this); L.DomEvent.addListener(icon, 'mouseout', this._collapse, this); this._map.on('movestart', this._collapse, this); } } else { L.DomEvent.addListener(icon, 'click', function(e) { this._geocode(e); }, this); this._expand(); } L.DomEvent.disableClickPropagation(container); return container; }, _geocodeResult: function (results) { L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber'); if (results.length === 1) { this._geocodeResultSelected(results[0]); } else if (results.length > 0) { this._alts.innerHTML = ''; this._results = results; L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); for (var i = 0; i < results.length; i++) { this._alts.appendChild(this._createAlt(results[i], i)); } } else { L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error'); } }, markGeocode: function(result) { this._map.fitBounds(result.bbox); if (this._geocodeMarker) { this._map.removeLayer(this._geocodeMarker); } this._geocodeMarker = new L.Marker(result.center) .bindPopup(result.html || result.name) .addTo(this._map) .openPopup(); return this; }, _geocode: function(event) { L.DomEvent.preventDefault(event); L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber'); this._clearResults(); this.options.geocoder.geocode(this._input.value, this._geocodeResult, this); return false; }, _geocodeResultSelected: function(result) { if (this.options.collapsed) { this._collapse(); } else { this._clearResults(); } this.markGeocode(result); }, _toggle: function() { if (this._container.className.indexOf('leaflet-control-geocoder-expanded') >= 0) { this._collapse(); } else { this._expand(); } }, _expand: function () { L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded'); this._input.select(); }, _collapse: function () { this._container.className = this._container.className.replace(' leaflet-control-geocoder-expanded', ''); L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error'); }, _clearResults: function () { L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized'); this._selection = null; L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error'); }, _createAlt: function(result, index) { var li = L.DomUtil.create('li', ''), a = L.DomUtil.create('a', '', li), icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null, text = result.html ? undefined : document.createTextNode(result.name), clickHandler = function clickHandler(e) { L.DomEvent.preventDefault(e); this._geocodeResultSelected(result); }; if (icon) { icon.src = result.icon; } li.setAttribute('data-result-index', index); if (result.html) { a.innerHTML = result.html; } else { a.appendChild(text); } L.DomEvent.addListener(li, 'click', clickHandler, this); return li; }, _keydown: function(e) { var _this = this, select = function select(dir) { if (_this._selection) { L.DomUtil.removeClass(_this._selection, 'leaflet-control-geocoder-selected'); _this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling']; } if (!_this._selection) { _this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild']; } if (_this._selection) { L.DomUtil.addClass(_this._selection, 'leaflet-control-geocoder-selected'); } }; switch (e.keyCode) { // Escape case 27: if (this.options.collapsed) { this._collapse(); } break; // Up case 38: select(-1); L.DomEvent.preventDefault(e); break; // Up case 40: select(1); L.DomEvent.preventDefault(e); break; // Enter case 13: if (this._selection) { var index = parseInt(this._selection.getAttribute('data-result-index'), 10); this._geocodeResultSelected(this._results[index]); this._clearResults(); L.DomEvent.preventDefault(e); } } return true; } }), factory: function(options) { return new L.Control.Geocoder(options); } };