File : toolbars/providersToolbar/ProvidersToolbar.js

1
/*
2
Copyright - 2017 2023 - wwwouaiebe - Contact: https://www.ouaie.be/
3
4
This  program is free software;
5
you can redistribute it and/or modify it under the terms of the
6
GNU General Public License as published by the Free Software Foundation;
7
either version 3 of the License, or any later version.
8
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU General Public License for more details.
13
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
*/
18
/*
19
Changes:
20
    - v4.0.0:
21
        - created from v3.6.0
22
Doc reviewed 202208
23
 */
24
25
import theTravelNotesData from '../../data/TravelNotesData.js';
26
import theHTMLElementsFactory from '../../core/uiLib/HTMLElementsFactory.js';
27
import TransitModeButton from './TransitModeButton.js';
28
import ProviderButton from './ProviderButton.js';
29
import theApiKeysManager from '../../core/ApiKeysManager.js';
30
import { NOT_FOUND, ZERO, TWO } from '../../main/Constants.js';
31
import theTranslator from '../../core/uiLib/Translator.js';
32
import theConfig from '../../data/Config.js';
33
import theDevice from '../../core/lib/Device.js';
34
35
/* ------------------------------------------------------------------------------------------------------------------------- */
36
/**
37
This class is the provider and transitModes toolbar at the bottom of the UI
38
*/
39
/* ------------------------------------------------------------------------------------------------------------------------- */
40
41
class ProvidersToolbar {
42
43
    /**
44
    The main HTMLElement of the toolbar
45
    @type {HTMLElement}
46
    */
47
48
    #toolbarHTMLElement;
49
50
    /**
51
    The top bar
52
    @type {HTMLElement}
53
    */
54
55
    #topBar;
56
57
    /**
58
    The toolbar HTMLElement
59
    @type {HTMLElement}
60
    */
61
62
    #buttonsHTMLElement;
63
64
    /**
65
    A JS map with the transit mode buttons, ordered by transitMode
66
    @type {Map}
67
    */
68
69
    #transitModeButtons;
70
71
    /**
72
    A JS map with the provider buttons, ordered by provider.name
73
    @type {Map}
74
    */
75
76
    #providerButtons;
77
78
    /**
79
    the active transit mode button
80
    @type {TransitModeButton}
81
    */
82
83
    #activeTransitModeButton;
84
85
    /**
86
    the active provider button
87
    @type {ProviderButton}
88
    */
89
90
    #activeProviderButton;
91
92
    /**
93
    A boolean saving the the current state of the toolbar
94
    @type {boolean}
95
     */
96
97
    #isShow;
98
99
    /**
100
    Timer id for the mouse leave event
101
    @type {Number}
102
    */
103
104
    #mouseLeaveTimerId;
105
106
    /**
107
    The delay needed for the timer that start the #removeHidden ( ) method
108
    @type {Number}
109
    */
110
111
    // eslint-disable-next-line no-magic-numbers
112
    static get #HIDDEN_DELAY ( ) { return 100; }
113
114
    /**
115
    The max delay between a mouseenter and a click event to consider the two events as a single event
116
    @type {Number}
117
    */
118
119
    /**
120
    Transit mode buttons creation
121
    */
122
123
    #createTransitModesButtons ( ) {
124
        [ 'bike', 'pedestrian', 'car', 'train', 'line', 'circle' ].forEach (
125
            transitMode => {
126
                const transitModeButton = new TransitModeButton ( this, transitMode );
127
                this.#transitModeButtons.set ( transitMode, transitModeButton );
128
                this.#buttonsHTMLElement.appendChild ( transitModeButton.buttonHTMLElement );
129
            }
130
        );
131
132
    }
133
134
    /**
135
    Provider buttons creation
136
    */
137
138
    #createProvidersButtons ( ) {
139
        theTravelNotesData.providers.forEach (
140
            provider => {
141
                if ( ! provider.providerKeyNeeded || theApiKeysManager.hasKey ( provider.name ) ) {
142
                    const providerButton = new ProviderButton ( this, provider );
143
                    this.#providerButtons.set ( provider.name, providerButton );
144
                    this.#buttonsHTMLElement.appendChild ( providerButton.buttonHTMLElement );
145
                }
146
            }
147
        );
148
    }
149
150
    /**
151
    Show the toolbar
152
    */
153
154
    #show ( ) {
155
156
        // this.centerToolbar ( );
157
        this.#isShow = true;
158
        setTimeout ( ( ) => this.#removeHidden ( ), ProvidersToolbar.#HIDDEN_DELAY );
159
160
    }
161
162
    /**
163
    Remove the TravelNotes-Hidden class on the toolbar. It's needed to use a timer (see the #show ( ) method) to
164
    remove the class, otherwise one of the button of the toolbar is clicked when the toolbar is show by clicking
165
    on the header on touch devices
166
    */
167
168
    #removeHidden ( ) {
169
170
        // we need first to move the toolbar on top, otherwise showing the toolbar will resize the screen.
171
        this.#toolbarHTMLElement.style.top = ZERO;
172
        this.#buttonsHTMLElement.classList.remove ( 'TravelNotes-Hidden' );
173
        this.centerToolbar ( );
174
    }
175
176
    /**
177
    Mouse click event listener
178
    */
179
180
    #onClick ( ) {
181
        if ( ! theDevice.isTouch ) {
182
            return;
183
        }
184
        if ( this.#isShow ) {
185
            this.hide ( );
186
        }
187
        else {
188
            this.#show ( );
189
        }
190
    }
191
192
    /**
193
    Mouse enter event listener
194
    */
195
196
    #onMouseEnter ( ) {
197
        if ( theDevice.isTouch ) {
198
            return;
199
        }
200
        if ( this.#mouseLeaveTimerId ) {
201
            clearTimeout ( this.#mouseLeaveTimerId );
202
            this.#mouseLeaveTimerId = null;
203
        }
204
        this.#show ( );
205
    }
206
207
    /**
208
    Mouse leave event listener
209
    */
210
211
    #onMouseLeave ( ) {
212
        if ( theDevice.isTouch ) {
213
            return;
214
        }
215
        this.#mouseLeaveTimerId = setTimeout ( ( ) => this.hide ( ), theConfig.toolbars.timeOut );
216
    }
217
218
    /**
219
    Hide the toolbar. Used as event listener for the timer
220
    */
221
222
    hide ( ) {
223
224
        // cleaning the timer
225
        if ( this.#mouseLeaveTimerId ) {
226
            clearTimeout ( this.#mouseLeaveTimerId );
227
            this.#mouseLeaveTimerId = null;
228
        }
229
        this.#buttonsHTMLElement.classList.add ( 'TravelNotes-Hidden' );
230
        this.centerToolbar ( );
231
        this.#isShow = false;
232
    }
233
234
    /**
235
    The constructor
236
    */
237
238
    constructor ( ) {
239
        Object.freeze ( this );
240
        this.#transitModeButtons = new Map ( );
241
        this.#providerButtons = new Map ( );
242
    }
243
244
    /**
245
    Center the toolbar on the lower side of the screen
246
    */
247
248
    centerToolbar ( ) {
249
        this.#topBar.textContent = theTranslator.getText (
250
            'ProvidersToolbar - Computed by {provider} for {transitMode}',
251
            {
252
                provider : theTravelNotesData.routing.provider,
253
                transitMode : theTranslator.getText (
254
                    'ProvidersToolbar - TransitMode ' + theTravelNotesData.routing.transitMode
255
                )
256
            }
257
        );
258
        this.#toolbarHTMLElement.style.left =
259
            String (
260
                ( window.visualViewport.width - this.#toolbarHTMLElement.clientWidth ) / TWO
261
            ) + 'px';
262
        this.#toolbarHTMLElement.style.top =
263
            String ( window.visualViewport.height - this.#toolbarHTMLElement.clientHeight - TWO ) + 'px';
264
    }
265
266
    /**
267
    Creation of the toolbar
268
    */
269
270
    createUI ( ) {
271
272
        // Toolbar container creation
273
        this.#toolbarHTMLElement = theHTMLElementsFactory.create (
274
            'div',
275
            {
276
                id : 'TravelNotes-ProvidersToolbar-Container'
277
            },
278
            document.body
279
        );
280
        this.#toolbarHTMLElement.addEventListener (
281
            'mouseenter',
282
            mouseEvent => this.#onMouseEnter ( mouseEvent ),
283
            false
284
        );
285
        this.#toolbarHTMLElement.addEventListener (
286
            'mouseleave',
287
            mouseEvent => this.#onMouseLeave ( mouseEvent ),
288
            false
289
        );
290
291
        // Topbar creation
292
        this.#topBar = theHTMLElementsFactory.create (
293
            'div',
294
            {
295
                className : 'TravelNotes-ProvidersToolbar-TopBar',
296
                textContent : theTranslator.getText ( 'TravelNotes-ProvidersToolbar - Providers' )
297
            },
298
            this.#toolbarHTMLElement
299
        );
300
        this.#topBar.addEventListener (
301
            'click',
302
            mouseEvent => this.#onClick ( mouseEvent ),
303
            false
304
        );
305
306
        // container for the buttons
307
        this.#buttonsHTMLElement = theHTMLElementsFactory.create (
308
            'div',
309
            {
310
                className : 'TravelNotes-ProvidersToolbar-ImgButtonsDiv TravelNotes-Hidden'
311
            },
312
            this.#toolbarHTMLElement
313
        );
314
315
        // buttons creation
316
        this.#createTransitModesButtons ( );
317
        this.#createProvidersButtons ( );
318
319
        // set the first provider in the map as active provider
320
        this.provider = this.#providerButtons.keys ().next ().value;
321
322
        this.centerToolbar ( );
323
    }
324
325
    /**
326
    set a provider as active provider
327
    */
328
329
    set provider ( providerName ) {
330
        theTravelNotesData.routing.provider = providerName;
331
332
        // removing previous provider
333
        if ( this.#activeProviderButton ) {
334
            this.#activeProviderButton.active = false;
335
        }
336
337
        // set the new provider
338
        this.#activeProviderButton = this.#providerButtons.get ( providerName );
339
        this.#activeProviderButton.active = true;
340
341
        // transit mode buttons activation
342
        const provider = theTravelNotesData.providers.get ( providerName.toLowerCase ( ) );
343
        this.#transitModeButtons.forEach (
344
            transitModeButton => {
345
                transitModeButton.visible = NOT_FOUND !== provider.transitModes.indexOf ( transitModeButton.transitMode );
346
            }
347
        );
348
349
        // transit mode button selection if the current one is not more valid
350
        if (
351
            ! this.#activeTransitModeButton
352
            ||
353
            NOT_FOUND === provider.transitModes.indexOf ( this.#activeTransitModeButton.transitMode )
354
        ) {
355
            this.#activeTransitModeButton = null;
356
            this.#transitModeButtons.forEach (
357
                transitModeButton => {
358
                    if (
359
                        ( ! this.#activeTransitModeButton )
360
                        &&
361
                        NOT_FOUND !== provider.transitModes.indexOf ( transitModeButton.transitMode )
362
                    ) {
363
                        this.#activeTransitModeButton = transitModeButton;
364
                        transitModeButton.active = true;
365
                        theTravelNotesData.routing.transitMode = transitModeButton.transitMode;
366
                    }
367
                    else {
368
                        transitModeButton.active = false;
369
                    }
370
                }
371
            );
372
373
        }
374
        this.centerToolbar ( );
375
    }
376
377
    /**
378
    set a transit mode as active transit mode
379
    */
380
381
    set transitMode ( transitMode ) {
382
        theTravelNotesData.routing.transitMode = transitMode;
383
        if ( this.#activeTransitModeButton ) {
384
            this.#activeTransitModeButton.active = false;
385
        }
386
        this.#activeTransitModeButton = this.#transitModeButtons.get ( transitMode );
387
        this.#activeTransitModeButton.active = true;
388
        this.centerToolbar ( );
389
    }
390
391
    /**
392
    Reset the toolbar when providers added ( see providersadded event )
393
    */
394
395
    providersAdded ( ) {
396
        while ( this.#buttonsHTMLElement.firstChild ) {
397
            this.#buttonsHTMLElement.removeChild ( this.#buttonsHTMLElement.firstChild );
398
        }
399
        this.#transitModeButtons.clear ( );
400
        this.#providerButtons.clear ( );
401
        this.#createTransitModesButtons ( );
402
        this.#createProvidersButtons ( );
403
        this.provider = this.#providerButtons.keys ().next ().value;
404
        const providerName = this.#providerButtons.keys ( ).next ( ).value;
405
        this.provider = providerName;
406
        this.transitMode = theTravelNotesData.providers.get ( providerName.toLowerCase ( ) ).transitModes [ ZERO ];
407
        this.centerToolbar ( );
408
    }
409
}
410
411
/* ------------------------------------------------------------------------------------------------------------------------- */
412
/**
413
The one and only one instance of ProvidersToolbar class
414
@type {ProvidersToolbar}
415
*/
416
/* ------------------------------------------------------------------------------------------------------------------------- */
417
418
const theProvidersToolbar = new ProvidersToolbar ( );
419
420
export default theProvidersToolbar;
421
422
/* --- End of file --------------------------------------------------------------------------------------------------------- */
423