@fleetbase/leaflet-routing-machine
Version:
ESM module Routing for Leaflet
216 lines (186 loc) • 7.37 kB
JavaScript
export default L.Class.extend({
options: {
timeout: 500,
blurTimeout: 100,
noResultsMessage: 'No results found.',
},
initialize: function (elem, callback, context, options) {
L.setOptions(this, options);
this._elem = elem;
this._resultFn = options.resultFn ? L.Util.bind(options.resultFn, options.resultContext) : null;
this._autocomplete = options.autocompleteFn ? L.Util.bind(options.autocompleteFn, options.autocompleteContext) : null;
this._selectFn = L.Util.bind(callback, context);
this._container = L.DomUtil.create('div', 'leaflet-routing-geocoder-result');
this._resultTable = L.DomUtil.create('table', '', this._container);
// TODO: looks a bit like a kludge to register same for input and keypress -
// browsers supporting both will get duplicate events; just registering
// input will not catch enter, though.
L.DomEvent.addListener(this._elem, 'input', this._keyPressed, this);
L.DomEvent.addListener(this._elem, 'keypress', this._keyPressed, this);
L.DomEvent.addListener(this._elem, 'keydown', this._keyDown, this);
L.DomEvent.addListener(
this._elem,
'blur',
function () {
if (this._isOpen) {
this.close();
}
},
this
);
},
close: function () {
L.DomUtil.removeClass(this._container, 'leaflet-routing-geocoder-result-open');
this._isOpen = false;
},
_open: function () {
var rect = this._elem.getBoundingClientRect();
if (!this._container.parentElement) {
// See notes section under https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX
// This abomination is required to support all flavors of IE
var scrollX = window.pageXOffset !== undefined ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
var scrollY = window.pageYOffset !== undefined ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
this._container.style.left = rect.left + scrollX + 'px';
this._container.style.top = rect.bottom + scrollY + 'px';
this._container.style.width = rect.right - rect.left + 'px';
document.body.appendChild(this._container);
}
L.DomUtil.addClass(this._container, 'leaflet-routing-geocoder-result-open');
this._isOpen = true;
},
_setResults: function (results) {
var i, tr, td, text;
delete this._selection;
this._results = results;
while (this._resultTable.firstChild) {
this._resultTable.removeChild(this._resultTable.firstChild);
}
for (i = 0; i < results.length; i++) {
tr = L.DomUtil.create('tr', '', this._resultTable);
tr.setAttribute('data-result-index', i);
td = L.DomUtil.create('td', '', tr);
if (this.options.formatGeocoderResult) {
text = this.options.formatGeocoderResult(results[i]);
} else {
text = document.createTextNode(results[i].name);
}
td.appendChild(text);
// mousedown + click because:
// http://stackoverflow.com/questions/10652852/jquery-fire-click-before-blur-event
L.DomEvent.addListener(td, 'mousedown', L.DomEvent.preventDefault);
L.DomEvent.addListener(td, 'click', this._createClickListener(results[i]));
}
if (!i) {
tr = L.DomUtil.create('tr', '', this._resultTable);
td = L.DomUtil.create('td', 'leaflet-routing-geocoder-no-results', tr);
td.innerHTML = this.options.noResultsMessage;
}
this._open();
if (results.length > 0) {
// Select the first entry
this._select(1);
}
},
_createClickListener: function (r) {
var resultSelected = this._resultSelected(r);
return L.bind(function () {
this._elem.blur();
resultSelected();
}, this);
},
_resultSelected: function (r) {
return L.bind(function () {
this.close();
this._elem.value = r.name;
this._lastCompletedText = r.name;
this._selectFn(r);
}, this);
},
_keyPressed: function (e) {
var index;
if (this._isOpen && e.keyCode === 13 && this._selection) {
index = parseInt(this._selection.getAttribute('data-result-index'), 10);
this._resultSelected(this._results[index])();
L.DomEvent.preventDefault(e);
return;
}
if (e.keyCode === 13) {
L.DomEvent.preventDefault(e);
this._complete(this._resultFn, true);
return;
}
if (this._autocomplete && document.activeElement === this._elem) {
if (this._timer) {
clearTimeout(this._timer);
}
this._timer = setTimeout(
L.Util.bind(function () {
this._complete(this._autocomplete);
}, this),
this.options.timeout
);
return;
}
this._unselect();
},
_select: function (dir) {
var sel = this._selection;
if (sel) {
L.DomUtil.removeClass(sel.firstChild, 'leaflet-routing-geocoder-selected');
sel = sel[dir > 0 ? 'nextSibling' : 'previousSibling'];
}
if (!sel) {
sel = this._resultTable[dir > 0 ? 'firstChild' : 'lastChild'];
}
if (sel) {
L.DomUtil.addClass(sel.firstChild, 'leaflet-routing-geocoder-selected');
this._selection = sel;
}
},
_unselect: function () {
if (this._selection) {
L.DomUtil.removeClass(this._selection.firstChild, 'leaflet-routing-geocoder-selected');
}
delete this._selection;
},
_keyDown: function (e) {
if (this._isOpen) {
switch (e.keyCode) {
// Escape
case 27:
this.close();
L.DomEvent.preventDefault(e);
return;
// Up
case 38:
this._select(-1);
L.DomEvent.preventDefault(e);
return;
// Down
case 40:
this._select(1);
L.DomEvent.preventDefault(e);
return;
}
}
},
_complete: function (completeFn, trySelect) {
var v = this._elem.value;
function completeResults(results) {
this._lastCompletedText = v;
if (trySelect && results.length === 1) {
this._resultSelected(results[0])();
} else {
this._setResults(results);
}
}
if (!v) {
return;
}
if (v !== this._lastCompletedText) {
completeFn(v, completeResults, this);
} else if (trySelect) {
completeResults.call(this, this._results);
}
},
});