Subversion Repositories eFlore/Applications.cel

Rev

Rev 3857 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 3857 Rev 3869
1
import {debounce} from "../lib/debounce.js";
1
import {debounce} from "../lib/debounce.js";
2
 
2
 
3
export const NOMINATIM_OSM_URL = 'https://nominatim.openstreetmap.org/';
3
export const NOMINATIM_OSM_URL = 'https://nominatim.openstreetmap.org/';
4
const NOMINATIM_OSM_DEFAULT_PARAMS = {
4
const NOMINATIM_OSM_DEFAULT_PARAMS = {
5
    'format': 'json',
5
    'format': 'json',
6
    'countrycodes': 'fr',
6
    'countrycodes': 'fr',
7
    'addressdetails': 1,
7
    'addressdetails': 1,
8
    'limit': 10
8
    'limit': 10
9
};
9
};
10
const ESC_KEY_STRING = /^Esc(ape)?/;
10
const ESC_KEY_STRING = /^Esc(ape)?/;
11
 
11
 
12
export function TbPlaces(clientCallback) {
12
export function TbPlaces(clientCallback) {
13
 
13
 
14
    /**
14
    /**
15
     * used in this.onSuggestionSelected()
15
     * used in this.onSuggestionSelected()
16
     *
16
     *
17
     * @callback clientCallback
17
     * @callback clientCallback
18
     * @param {String} locality
18
     * @param {String} locality
19
     * @param {{lat: number, lng: number }} coordinates
19
     * @param {{lat: number, lng: number }} coordinates
20
     */
20
     */
21
    this.clientCallback = clientCallback;
21
    this.clientCallback = clientCallback;
22
    this.searchResults = [];
22
    this.searchResults = [];
23
}
23
}
24
 
24
 
25
TbPlaces.prototype.init = function() {
25
TbPlaces.prototype.init = function() {
26
    this.initForm();
26
    this.initForm();
27
    this.initEvts();
27
    this.initEvts();
28
};
28
};
29
 
29
 
30
TbPlaces.prototype.initForm = function() {
30
TbPlaces.prototype.initForm = function() {
31
    this.places = $('#tb-places');
31
    this.places = $('#tb-places');
32
    this.placeLabel = this.places.siblings('label');
32
    this.placeLabel = this.places.siblings('label');
33
    this.placesResults = $('.tb-places-results');
33
    this.placesResults = $('.tb-places-results');
34
    this.placesResultsContainer = $('.tb-places-results-container');
34
    this.placesResultsContainer = $('.tb-places-results-container');
35
    this.placesCloseButton = $('.tb-places-close');
35
    this.placesCloseButton = $('.tb-places-close');
36
};
36
};
37
 
37
 
38
TbPlaces.prototype.initEvts = function() {
38
TbPlaces.prototype.initEvts = function() {
39
    if (0 < this.places.length) {
39
    if (0 < this.places.length) {
40
 
40
 
41
        this.toggleCloseButton(false);
41
        this.toggleCloseButton(false);
42
        this.places.off('input').on('input', debounce(this.launchSearch.bind(this), 500));
42
        this.places.off('input').on('input', debounce(this.launchSearch.bind(this), 500));
43
        this.places.off('keydown').on('keydown', debounce(this.handlePlacesKeydown.bind(this), 500));
43
        this.places.off('keydown').on('keydown', debounce(this.handlePlacesKeydown.bind(this), 500));
44
    }
44
    }
45
};
45
};
46
 
46
 
47
TbPlaces.prototype.handlePlacesKeydown = function(evt) {
47
TbPlaces.prototype.handlePlacesKeydown = function(evt) {
48
    const suggestionEl = $('.tb-places-suggestion'),
48
    const suggestionEl = $('.tb-places-suggestion'),
49
        isEscape = 27 === evt.keyCode || ESC_KEY_STRING.test(evt.key),
49
        isEscape = 27 === evt.keyCode || ESC_KEY_STRING.test(evt.key),
50
        isArrowDown = 40 === evt.keyCode || 'ArrowDown' === evt.key,
50
        isArrowDown = 40 === evt.keyCode || 'ArrowDown' === evt.key,
51
        isEnter = 13 === evt.keyCode || 'Enter' === evt.key;
51
        isEnter = 13 === evt.keyCode || 'Enter' === evt.key;
52
 
52
 
53
    if (isEscape || isArrowDown || isEnter) {
53
    if (isEscape || isArrowDown || isEnter) {
54
        evt.preventDefault();
54
        evt.preventDefault();
55
 
55
 
56
        if (isEscape) {
56
        if (isEscape) {
57
            this.placesCloseButton.trigger('click');
57
            this.placesCloseButton.trigger('click');
58
            this.places.focus();
58
            this.places.focus();
59
        } else if(isArrowDown || isEnter) {
59
        } else if(isArrowDown || isEnter) {
60
            if ( 0 <  suggestionEl.length) {
60
            if ( 0 <  suggestionEl.length) {
61
                suggestionEl.first().focus();
61
                suggestionEl.first().focus();
62
            } else {
62
            } else {
63
                this.launchSearch();
63
                this.launchSearch();
64
            }
64
            }
65
        }
65
        }
66
    }
66
    }
67
};
67
};
68
 
68
 
69
TbPlaces.prototype.launchSearch = function () {
69
TbPlaces.prototype.launchSearch = function () {
70
    if (!!this.places.val()) {
70
    if (!!this.places.val()) {
71
        const url = NOMINATIM_OSM_URL+'search',
71
        const url = NOMINATIM_OSM_URL+'search',
-
 
72
            params = {
72
            params = {'q':  this.places.val()};
73
                'q': this.places.val(),
-
 
74
                'format': 'json',
-
 
75
                'polygon_geojson': 1,
-
 
76
                'zoom': 17
-
 
77
            };
73
 
78
 
74
        this.placeLabel.addClass('loading');
79
        this.placeLabel.addClass('loading');
75
        $.ajax({
80
        $.ajax({
76
            method: "GET",
81
            method: "GET",
77
            url: url,
82
            url: url,
78
            data: {...NOMINATIM_OSM_DEFAULT_PARAMS, ...params},
83
            data: {...NOMINATIM_OSM_DEFAULT_PARAMS, ...params},
79
            success: this.nominatimOsmResponseCallback.bind(this),
84
            success: this.nominatimOsmResponseCallback.bind(this),
80
            error: () => {
85
            error: () => {
81
                this.placeLabel.removeClass('loading');
86
                this.placeLabel.removeClass('loading');
82
                this.handleSearchError();
87
                this.handleSearchError();
83
            }
88
            }
84
        });
89
        });
85
    }
90
    }
86
};
91
};
87
 
92
 
88
TbPlaces.prototype.nominatimOsmResponseCallback = function(data) {
93
TbPlaces.prototype.nominatimOsmResponseCallback = function(data) {
89
    this.places.siblings('label').removeClass('loading');
94
    this.places.siblings('label').removeClass('loading');
90
    if (0 < data.length) {
95
    if (0 < data.length) {
91
        this.searchResults = data;
96
        this.searchResults = data;
92
        this.setSuggestions();
97
        this.setSuggestions();
93
        this.toggleCloseButton();
98
        this.toggleCloseButton();
94
        this.resetOnClick();
99
        this.resetOnClick();
95
        this.onSuggestionSelected();
100
        this.onSuggestionSelected();
96
    } else {
101
    } else {
97
        this.handleSearchError();
102
        this.handleSearchError();
98
    }
103
    }
99
};
104
};
100
 
105
 
101
TbPlaces.prototype.setSuggestions = function() {
106
TbPlaces.prototype.setSuggestions = function() {
102
    const lthis = this,
107
    const lthis = this,
103
        acceptedSuggestions = [];
108
        acceptedSuggestions = [];
104
 
109
 
105
    this.placesResults.empty();
110
    this.placesResults.empty();
106
    this.searchResults.forEach(suggestion => {
111
    this.searchResults.forEach(suggestion => {
107
        if(lthis.validateSuggestionData(suggestion)) {
112
        if(lthis.validateSuggestionData(suggestion)) {
108
            const locality = suggestion['display_name'];
113
            const locality = suggestion['display_name'];
109
 
114
 
110
            if (locality && !acceptedSuggestions.includes(locality)) {
115
            if (locality && !acceptedSuggestions.includes(locality)) {
111
                acceptedSuggestions.push(locality);
116
                acceptedSuggestions.push(locality);
112
                lthis.placesResults.append(
117
                lthis.placesResults.append(
113
                    '<li class="tb-places-suggestion" data-place-id="'+suggestion['place_id']+'" tabindex="-1">' +
118
                    '<li class="tb-places-suggestion" data-place-id="'+suggestion['place_id']+'" tabindex="-1">' +
114
                        locality +
119
                        locality +
115
                    '</li>'
120
                    '</li>'
116
                );
121
                );
117
            }
122
            }
118
        }
123
        }
119
    });
124
    });
-
 
125
    if(0 < acceptedSuggestions.length) {
120
    this.placesResultsContainer.removeClass('hidden');
126
        this.placesResultsContainer.removeClass('hidden');
-
 
127
    } else {
-
 
128
        this.resetPlacesSearch();
-
 
129
    }
121
};
130
};
122
 
131
 
123
TbPlaces.prototype.validateSuggestionData = function(suggestion) {
132
TbPlaces.prototype.validateSuggestionData = function(suggestion) {
124
    const validGeometry = undefined !== suggestion.lat && undefined !== suggestion.lon,
133
    const validGeometry = undefined !== suggestion.lat && undefined !== suggestion.lon,
-
 
134
        typeLocalisation = this.places.data('typeLocalisation') || '',
-
 
135
        validGeometryType = 'rue' === typeLocalisation ? 'LineString' === suggestion?.geojson.type : true,
125
        validAddressData = undefined !== suggestion.address,
136
        validAddressData = undefined !== suggestion.address,
126
        validDisplayName = undefined !== suggestion['display_name'];
137
        validDisplayName = undefined !== suggestion['display_name'];
127
 
138
 
128
    return (validGeometry && validAddressData && validDisplayName);
139
    return (validGeometry && validGeometryType && validAddressData && validDisplayName);
129
};
140
};
130
 
141
 
131
TbPlaces.prototype.onSuggestionSelected = function() {
142
TbPlaces.prototype.onSuggestionSelected = function() {
132
    const lthis = this;
143
    const lthis = this;
133
 
144
 
134
    $('.tb-places-suggestion').off('click').on('click', function (evt) {
145
    $('.tb-places-suggestion').off('click').on('click', function (evt) {
135
        const $thisSuggestion = $(this),
146
        const $thisSuggestion = $(this),
136
            suggestion = lthis.searchResults.find(suggestion => suggestion['place_id'] === $thisSuggestion.data('placeId'));
147
            suggestion = lthis.searchResults.find(suggestion => suggestion['place_id'] === $thisSuggestion.data('placeId'));
137
 
148
 
138
        evt.preventDefault();
149
        evt.preventDefault();
139
 
150
 
140
        lthis.places.val($thisSuggestion.text());
151
        lthis.places.val($thisSuggestion.text());
141
        lthis.clientCallback(suggestion);
152
        lthis.clientCallback(suggestion);
142
        lthis.placesCloseButton.trigger('click');
153
        lthis.placesCloseButton.trigger('click');
143
 
154
 
144
    }).off('keydown').on('keydown', function (evt) {
155
    }).off('keydown').on('keydown', function (evt) {
145
        evt.preventDefault();
156
        evt.preventDefault();
146
 
157
 
147
        const $thisSuggestion = $(this);
158
        const $thisSuggestion = $(this);
148
 
159
 
149
        if (13 === evt.keyCode || 'Enter' === evt.key) {
160
        if (13 === evt.keyCode || 'Enter' === evt.key) {
150
            $thisSuggestion.trigger('click');
161
            $thisSuggestion.trigger('click');
151
        } else if (38 === evt.keyCode || 'ArrowUp'=== evt.key) {
162
        } else if (38 === evt.keyCode || 'ArrowUp'=== evt.key) {
152
            if(0 < $thisSuggestion.prev().length) {
163
            if(0 < $thisSuggestion.prev().length) {
153
                $thisSuggestion.prev().focus();
164
                $thisSuggestion.prev().focus();
154
            } else {
165
            } else {
155
                lthis.places.focus();
166
                lthis.places.focus();
156
            }
167
            }
157
        } else if((40 === evt.keyCode || 'ArrowDown' === evt.key) && 0 < $thisSuggestion.next().length) {
168
        } else if((40 === evt.keyCode || 'ArrowDown' === evt.key) && 0 < $thisSuggestion.next().length) {
158
            $thisSuggestion.next().focus();
169
            $thisSuggestion.next().focus();
159
        } else if (27 === evt.keyCode || ESC_KEY_STRING.test(evt.key)) {
170
        } else if (27 === evt.keyCode || ESC_KEY_STRING.test(evt.key)) {
160
            lthis.placesCloseButton.trigger('click');
171
            lthis.placesCloseButton.trigger('click');
161
            lthis.places.focus();
172
            lthis.places.focus();
162
        }
173
        }
163
    });
174
    });
164
};
175
};
165
 
176
 
166
TbPlaces.prototype.resetOnClick = function () {
177
TbPlaces.prototype.resetOnClick = function () {
167
    const lthis = this;
178
    const lthis = this;
168
 
179
 
169
    this.placesCloseButton.off('click').on('click', function (event) {
180
    this.placesCloseButton.off('click').on('click', function (event) {
170
        event.preventDefault();
181
        event.preventDefault();
171
        lthis.resetPlacesSearch();
182
        lthis.resetPlacesSearch();
172
    });
183
    });
173
};
184
};
174
 
185
 
175
TbPlaces.prototype.toggleCloseButton = function(isShow = true) {
186
TbPlaces.prototype.toggleCloseButton = function(isShow = true) {
176
    this.placesCloseButton.toggleClass('hidden', !isShow);
187
    this.placesCloseButton.toggleClass('hidden', !isShow);
177
    $('.tb-places-search-icon').toggleClass('hidden', isShow);
188
    $('.tb-places-search-icon').toggleClass('hidden', isShow);
178
};
189
};
179
 
190
 
180
TbPlaces.prototype.handleSearchError = function() {
191
TbPlaces.prototype.handleSearchError = function() {
181
    this.resetPlacesSearch();
192
    this.resetPlacesSearch();
182
    if (0 === $('#tb-places-error').length) {
193
    if (0 === $('#tb-places-error').length) {
183
        this.places.closest('#tb-places-zone').after(
194
        this.places.closest('#tb-places-zone').after(
184
            `<span id="tb-places-error" class="error mb-3 mt-3">
195
            `<span id="tb-places-error" class="error mb-3 mt-3">
185
                Votre recherche n’a rien donné.<br>veuillez modifier votre recherche ou rechercher votre station directement sur la carte.
196
                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.
186
            </span>`
197
            </span>`
187
        );
198
        );
188
        setTimeout(function() {
199
        setTimeout(function() {
189
            $('#tb-places-error').remove();
200
            $('#tb-places-error').remove();
190
        }, 5000);
201
        }, 10000);
191
    }
202
    }
192
};
203
};
193
 
204
 
194
TbPlaces.prototype.resetPlacesSearch = function() {
205
TbPlaces.prototype.resetPlacesSearch = function() {
195
    this.toggleCloseButton(false);
206
    this.toggleCloseButton(false);
196
    this.placesResultsContainer.addClass('hidden');
207
    this.placesResultsContainer.addClass('hidden');
197
    this.placesResults.empty();
208
    this.placesResults.empty();
198
};
209
};
199
 
210