Subversion Repositories eFlore/Applications.cel

Rev

Rev 3869 | 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();
};