Subversion Repositories eFlore/Applications.cel

Rev

Rev 3849 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3845 idir 1
import {debounce} from "../lib/debounce.js";
2
 
3
export const NOMINATIM_OSM_URL = 'https://nominatim.openstreetmap.org/';
4
const NOMINATIM_OSM_DEFAULT_PARAMS = {
5
    'format': 'json',
6
    'countrycodes': 'fr',
7
    'addressdetails': 1,
8
    'limit': 10
9
};
10
const ESC_KEY_STRING = /^Esc(ape)?/;
11
 
12
export function TbPlaces(clientCallback) {
13
 
14
    /**
15
     * used in this.onSuggestionSelected()
16
     *
17
     * @callback clientCallback
18
     * @param {String} locality
19
     * @param {{lat: number, lng: number }} coordinates
20
     */
21
    this.clientCallback = clientCallback;
22
    this.searchResults = [];
23
}
24
 
25
TbPlaces.prototype.init = function() {
26
    this.initForm();
27
    this.initEvts();
28
};
29
 
30
TbPlaces.prototype.initForm = function() {
31
    this.places = $('#tb-places');
32
    this.placeLabel = this.places.siblings('label');
33
    this.placesResults = $('.tb-places-results');
34
    this.placesResultsContainer = $('.tb-places-results-container');
35
    this.placesCloseButton = $('.tb-places-close');
36
};
37
 
38
TbPlaces.prototype.initEvts = function() {
39
    if (0 < this.places.length) {
40
 
41
        this.toggleCloseButton(false);
42
        this.places.off('input').on('input', debounce(this.launchSearch.bind(this), 500));
43
        this.places.off('keydown').on('keydown', evt => {
44
            const suggestionEl = $('.tb-places-suggestion');
45
 
46
            if (27 === evt.keyCode || ESC_KEY_STRING.test(evt.key)) {
47
                evt.preventDefault();
48
 
49
                this.placesCloseButton.trigger('click');
50
                this.places.focus();
51
            } else if((40 === evt.keyCode || 'ArrowDown' === evt.key) && 0 <  suggestionEl.length) {
52
                evt.preventDefault();
53
 
54
                suggestionEl.first().focus();
55
            }
56
        });
57
    }
58
};
59
 
60
TbPlaces.prototype.launchSearch = function (evt) {
61
    if (!!this.places.val()) {
62
        const url = NOMINATIM_OSM_URL+'search',
63
            params = {'q':  this.places.val()};
64
 
65
        this.placeLabel.addClass('loading');
66
        $.ajax({
67
            method: "GET",
68
            url: url,
69
            data: {...NOMINATIM_OSM_DEFAULT_PARAMS, ...params},
70
            success: this.nominatimOsmResponseCallback.bind(this),
71
            error: () => {
72
                this.placeLabel.removeClass('loading');
73
            }
74
        });
75
    }
76
};
77
 
78
TbPlaces.prototype.nominatimOsmResponseCallback = function(data) {
79
    this.places.siblings('label').removeClass('loading');
80
    if (0 < data.length) {
81
        this.searchResults = data;
82
        this.setSuggestions();
83
        this.toggleCloseButton();
84
        this.resetOnClick();
85
        this.onSuggestionSelected();
86
    }
87
};
88
 
89
TbPlaces.prototype.setSuggestions = function() {
90
    const lthis = this,
91
        acceptedSuggestions = [];
92
 
93
    this.placesResults.empty();
94
    this.searchResults.forEach(suggestion => {
95
        if(lthis.validateSuggestionData(suggestion)) {
96
            const locality = suggestion['display_name'];
97
 
98
            if (locality && !acceptedSuggestions.includes(locality)) {
99
                acceptedSuggestions.push(locality);
100
                lthis.placesResults.append(
101
                    '<li class="tb-places-suggestion" data-place-id="'+suggestion['place_id']+'" tabindex="-1">' +
102
                        locality +
103
                    '</li>'
104
                );
105
            }
106
        }
107
    });
108
    this.placesResultsContainer.removeClass('hidden');
109
};
110
 
111
TbPlaces.prototype.validateSuggestionData = function(suggestion) {
112
    const validGeometry = undefined !== suggestion.lat && undefined !== suggestion.lon,
113
        validAddressData = undefined !== suggestion.address,
114
        validDisplayName = undefined !== suggestion['display_name'];
115
 
116
    return (validGeometry && validAddressData && validDisplayName);
117
};
118
 
119
TbPlaces.prototype.onSuggestionSelected = function() {
120
    const lthis = this;
121
 
122
    $('.tb-places-suggestion').off('click').on('click', function (evt) {
123
        const $thisSuggestion = $(this),
124
            suggestion = lthis.searchResults.find(suggestion => suggestion['place_id'] === $thisSuggestion.data('placeId'));
125
 
126
        evt.preventDefault();
127
 
128
        lthis.places.val($thisSuggestion.text());
129
        lthis.clientCallback(suggestion);
130
        lthis.placesCloseButton.trigger('click');
131
 
132
    }).off('keydown').on('keydown', function (evt) {
133
        evt.preventDefault();
134
 
135
        const $thisSuggestion = $(this);
136
 
137
        if (13 === evt.keyCode || 'Enter' === evt.key) {
138
            $thisSuggestion.trigger('click');
139
        } else if (38 === evt.keyCode || 'ArrowUp'=== evt.key) {
140
            if(0 < $thisSuggestion.prev().length) {
141
                $thisSuggestion.prev().focus();
142
            } else {
143
                lthis.places.focus();
144
            }
145
        } else if((40 === evt.keyCode || 'ArrowDown' === evt.key) && 0 < $thisSuggestion.next().length) {
146
            $thisSuggestion.next().focus();
147
        } else if (27 === evt.keyCode || ESC_KEY_STRING.test(evt.key)) {
148
            lthis.placesCloseButton.trigger('click');
149
            lthis.places.focus();
150
        }
151
    });
152
};
153
 
154
TbPlaces.prototype.resetOnClick = function () {
155
    const lthis = this;
156
 
157
    this.placesCloseButton.off('click').on('click', function (event) {
158
        event.preventDefault();
159
        lthis.resetPlacesSearch();
160
    });
161
};
162
 
163
TbPlaces.prototype.toggleCloseButton = function(isShow = true) {
164
    this.placesCloseButton.toggleClass('hidden', !isShow);
165
    $('.tb-places-search-icon').toggleClass('hidden', isShow);
166
};
167
 
168
TbPlaces.prototype.resetPlacesSearch = function() {
169
    this.places.val('');
170
    this.toggleCloseButton(false);
171
    this.placesResultsContainer.addClass('hidden');
172
    this.placesResults.empty();
173
};
174