Rev 3867 | Blame | Compare with Previous | Last modification | View Log | RSS feed
import {debounce} from "../lib/debounce.js";
export const NOMINATIM_OSM_URL = 'https://nominatim.openstreetmap.org/';
const NOMINATIM_OSM_DEFAULT_PARAMS = {
'format': 'json',
'addressdetails': 1,
'limit': 10
};
const ESC_KEY_STRING = /^Esc(ape)?/;
export function TbPlaces(clientCallback) {
/**
* used in this.onSuggestionSelected()
*
* @callback clientCallback
* @param {String} locality
* @param {{lat: number, lng: number }} coordinates
*/
this.clientCallback = clientCallback;
this.searchResults = [];
}
TbPlaces.prototype.init = function() {
this.initForm();
this.initEvts();
};
TbPlaces.prototype.initForm = function() {
this.places = $('#tb-places');
this.placeLabel = this.places.siblings('label');
this.placesResults = $('.tb-places-results');
this.placesResultsContainer = $('.tb-places-results-container');
this.placesCloseButton = $('.tb-places-close');
};
TbPlaces.prototype.initEvts = function() {
if (0 < this.places.length) {
this.toggleCloseButton(false);
this.places.off('input').on('input', debounce(this.launchSearch.bind(this), 500));
this.places.off('keydown').on('keydown', debounce(this.handlePlacesKeydown.bind(this), 500));
}
};
TbPlaces.prototype.handlePlacesKeydown = function(evt) {
const suggestionEl = $('.tb-places-suggestion'),
isEscape = 27 === evt.keyCode || ESC_KEY_STRING.test(evt.key),
isArrowDown = 40 === evt.keyCode || 'ArrowDown' === evt.key,
isEnter = 13 === evt.keyCode || 'Enter' === evt.key;
if (isEscape || isArrowDown || isEnter) {
evt.preventDefault();
if (isEscape) {
this.placesCloseButton.trigger('click');
this.places.focus();
} else if(isArrowDown || isEnter) {
if ( 0 < suggestionEl.length) {
suggestionEl.first().focus();
} else {
this.launchSearch();
}
}
}
};
TbPlaces.prototype.launchSearch = function () {
if (!!this.places.val()) {
const url = NOMINATIM_OSM_URL+'search',
params = {
'q': this.places.val(),
'format': 'json',
'polygon_geojson': 1,
'zoom': 17
};
this.placeLabel.addClass('loading');
$.ajax({
method: "GET",
url: url,
data: {...NOMINATIM_OSM_DEFAULT_PARAMS, ...params},
success: this.nominatimOsmResponseCallback.bind(this),
error: () => {
this.placeLabel.removeClass('loading');
this.handleSearchError();
}
});
}
};
TbPlaces.prototype.nominatimOsmResponseCallback = function(data) {
this.places.siblings('label').removeClass('loading');
if (0 < data.length) {
this.searchResults = data;
this.setSuggestions();
this.toggleCloseButton();
this.resetOnClick();
this.onSuggestionSelected();
} else {
this.handleSearchError();
}
};
TbPlaces.prototype.setSuggestions = function() {
const lthis = this,
acceptedSuggestions = [];
this.placesResults.empty();
this.searchResults.forEach(suggestion => {
if(lthis.validateSuggestionData(suggestion)) {
const locality = suggestion['display_name'];
if (locality && !acceptedSuggestions.includes(locality)) {
acceptedSuggestions.push(locality);
lthis.placesResults.append(
'<li class="tb-places-suggestion" data-place-id="'+suggestion['place_id']+'" tabindex="-1">' +
locality +
'</li>'
);
}
}
});
if(0 < acceptedSuggestions.length) {
this.placesResultsContainer.removeClass('hidden');
} else {
this.resetPlacesSearch();
}
};
TbPlaces.prototype.validateSuggestionData = function(suggestion) {
const validGeometry = undefined !== suggestion.lat && undefined !== suggestion.lon,
typeLocalisation = this.places.data('typeLocalisation') || '',
validGeometryType = 'rue' === typeLocalisation ? 'LineString' === suggestion?.geojson.type : true,
validAddressData = undefined !== suggestion.address,
validDisplayName = undefined !== suggestion['display_name'];
return (validGeometry && validGeometryType && validAddressData && validDisplayName);
};
TbPlaces.prototype.onSuggestionSelected = function() {
const lthis = this;
$('.tb-places-suggestion').off('click').on('click', function (evt) {
const $thisSuggestion = $(this),
suggestion = lthis.searchResults.find(suggestion => suggestion['place_id'] === $thisSuggestion.data('placeId'));
evt.preventDefault();
lthis.places.val($thisSuggestion.text());
lthis.clientCallback(suggestion);
lthis.placesCloseButton.trigger('click');
}).off('keydown').on('keydown', function (evt) {
evt.preventDefault();
const $thisSuggestion = $(this);
if (13 === evt.keyCode || 'Enter' === evt.key) {
$thisSuggestion.trigger('click');
} else if (38 === evt.keyCode || 'ArrowUp'=== evt.key) {
if(0 < $thisSuggestion.prev().length) {
$thisSuggestion.prev().focus();
} else {
lthis.places.focus();
}
} else if((40 === evt.keyCode || 'ArrowDown' === evt.key) && 0 < $thisSuggestion.next().length) {
$thisSuggestion.next().focus();
} else if (27 === evt.keyCode || ESC_KEY_STRING.test(evt.key)) {
lthis.placesCloseButton.trigger('click');
lthis.places.focus();
}
});
};
TbPlaces.prototype.resetOnClick = function () {
const lthis = this;
this.placesCloseButton.off('click').on('click', function (event) {
event.preventDefault();
lthis.resetPlacesSearch();
});
};
TbPlaces.prototype.toggleCloseButton = function(isShow = true) {
this.placesCloseButton.toggleClass('hidden', !isShow);
$('.tb-places-search-icon').toggleClass('hidden', isShow);
};
TbPlaces.prototype.handleSearchError = function() {
this.resetPlacesSearch();
if (0 === $('#tb-places-error').length) {
this.places.closest('#tb-places-zone').after(
`<span id="tb-places-error" class="error mb-3 mt-3">
Votre recherche n’a pas donné de résultat pour le moment.<br>Vous pouvez soit poursuivre ou modifier votre recherche,<br>soit rechercher votre station directement sur la carte.
</span>`
);
setTimeout(function() {
$('#tb-places-error').remove();
}, 10000);
}
};
TbPlaces.prototype.resetPlacesSearch = function() {
this.toggleCloseButton(false);
this.placesResultsContainer.addClass('hidden');
this.placesResults.empty();
};