File : viewsFactories/NoteHTMLViewsFactory.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 theHTMLElementsFactory from '../core/uiLib/HTMLElementsFactory.js';
26
import theHTMLSanitizer from '../core/htmlSanitizer/HTMLSanitizer.js';
27
import theTranslator from '../core/uiLib/Translator.js';
28
import theUtilities from '../core/uiLib/Utilities.js';
29
import theConfig from '../data/Config.js';
30
import theTravelNotesData from '../data/TravelNotesData.js';
31
32
import { ICON_DIMENSIONS, ZERO, ONE } from '../main/Constants.js';
33
34
/* ------------------------------------------------------------------------------------------------------------------------- */
35
/**
36
This class creates HTMLElements for notes
37
*/
38
/* ------------------------------------------------------------------------------------------------------------------------- */
39
40
class NoteHTMLViewsFactory {
41
42
    /**
43
    The max length for displayed links
44
    @type {Number}
45
    */
46
47
    // eslint-disable-next-line no-magic-numbers
48
    static get #LINKS_MAX_LENGTH ( ) { return 40; }
49
50
    /**
51
    // The minimal distance between note to display the 'Next note after' value
52
    @type {Number}
53
    */
54
55
    // eslint-disable-next-line no-magic-numbers
56
    static get #MIN_NOTES_DISTANCE ( ) { return 40; }
57
58
    /**
59
    The constructor
60
    */
61
62
    constructor ( ) {
63
        Object.freeze ( this );
64
    }
65
66
    /**
67
    Gives an HTMLElement with the note icon and sames values than the this.getNoteTextHTML method
68
    @param {String} classPrefix A string that will be added to all the className of the created HTMLElement
69
    @param {NoteAndRoute} noteAndRoute A NoteAndRoute object with the note and the route to witch the note is attached
70
    @return {HTMLElement} An HTMLElement with the icon and texts for a Note
71
    */
72
73
    getNoteTextAndIconHTML ( classPrefix, noteAndRoute ) {
74
        const NoteTextAndIconHTML = theHTMLElementsFactory.create (
75
            'div',
76
            {
77
                dataset : { ObjId : noteAndRoute.note.objId }
78
            }
79
        );
80
        const iconHTML = theHTMLElementsFactory.create (
81
            'div',
82
            {
83
                className : classPrefix + ( noteAndRoute.route ? 'Route-ManeuversAndNotes-IconCell' : 'Travel-Notes-IconCell' )
84
            },
85
            NoteTextAndIconHTML
86
        );
87
        let dimCoeficient = ONE;
88
        theHTMLSanitizer.sanitizeToHtmlElement ( noteAndRoute.note.iconContent, iconHTML );
89
        if ( 'TravelNotes-Roadbook-' === classPrefix && iconHTML.firstChild ) {
90
            if ( 'svg' === iconHTML.firstChild.tagName ) {
91
                iconHTML.firstChild.setAttributeNS (
92
                    null,
93
                    'viewBox',
94
                    '0 0 ' + ICON_DIMENSIONS.svgViewboxDim + ' ' + ICON_DIMENSIONS.svgViewboxDim
95
                );
96
                dimCoeficient = theConfig.note.svgIcon.roadbookFactor;
97
            }
98
            else if ( iconHTML?.firstChild?.classList?.contains ( 'TravelNotes-MapNoteCategory-0073' ) ) {
99
                dimCoeficient = theConfig.note.svgIcon.roadbookFactor;
100
            }
101
        }
102
103
        // when CSP is enabled, it's needed to set width and height with JS to avoid to add an 'unsafe-inline' for style in CSP
104
        // Adding tanWidth and tanHeight for the roadbook
105
        iconHTML.dataset.tanWidth = String ( noteAndRoute.note.iconWidth * dimCoeficient ) + 'px';
106
        iconHTML.dataset.tanHeight = String ( noteAndRoute.note.iconHeight * dimCoeficient ) + 'px';
107
108
        // and style.width and heigth for the icon preview
109
        iconHTML.style.width = String ( noteAndRoute.note.iconWidth * dimCoeficient ) + 'px';
110
        iconHTML.style.height = String ( noteAndRoute.note.iconHeight * dimCoeficient ) + 'px';
111
112
        const noteTextHTMLElement = this.getNoteTextHTML ( classPrefix, noteAndRoute );
113
        noteTextHTMLElement.className =
114
            classPrefix +
115
            ( noteAndRoute.route ? 'Route-ManeuversAndNotes-Cell' : 'Travel-Notes-Cell' );
116
        NoteTextAndIconHTML.appendChild ( noteTextHTMLElement );
117
118
        return NoteTextAndIconHTML;
119
    }
120
121
    /**
122
    Gives an HTMLElement with the tooltipContent (if any), popupContent (if any) address (if any), phone (if any),
123
    url (if any), latitude, longitude, distance since the start of the travel (if the note is attached to a chained node),
124
    distance since the start of the route (if the note is a route note) and distance till the next note(if the note
125
    is a route note)
126
    @param {String} classPrefix A string that will be added to all the className of the created HTMLElements
127
    @param {NoteAndRoute} noteAndRoute A NoteAndRoute object with the note and the route to witch the note is attached
128
    @return {HTMLElement} an HTMLElement
129
    */
130
131
    getNoteTextHTML ( classPrefix, noteAndRoute ) {
132
        const note = noteAndRoute.note;
133
        const noteHTMLElement = theHTMLElementsFactory.create ( 'div' );
134
        if ( ZERO !== note.tooltipContent.length ) {
135
            theHTMLSanitizer.sanitizeToHtmlElement (
136
                note.tooltipContent,
137
                theHTMLElementsFactory.create (
138
                    'div',
139
                    {
140
                        className : classPrefix + 'NoteHtml-TooltipContent'
141
                    },
142
                    noteHTMLElement
143
                )
144
            );
145
        }
146
147
        if ( ZERO !== note.popupContent.length ) {
148
            theHTMLSanitizer.sanitizeToHtmlElement (
149
                note.popupContent,
150
                theHTMLElementsFactory.create (
151
                    'div',
152
                    {
153
                        className : classPrefix + 'NoteHtml-PopupContent'
154
                    },
155
                    noteHTMLElement
156
                )
157
            );
158
        }
159
160
        if ( ZERO !== note.address.length ) {
161
            theHTMLSanitizer.sanitizeToHtmlElement (
162
                '<span>' + theTranslator.getText ( 'NoteHTMLViewsFactory - Address' ) + '</span>' +
163
                '\u00a0:\u00a0' + note.address,
164
                theHTMLElementsFactory.create (
165
                    'div',
166
                    {
167
                        className : classPrefix + 'NoteHtml-Address'
168
                    },
169
                    noteHTMLElement
170
                )
171
            );
172
        }
173
174
        if ( ZERO !== note.url.length ) {
175
            theHTMLSanitizer.sanitizeToHtmlElement (
176
                '<span>' + theTranslator.getText ( 'NoteHTMLViewsFactory - Link' ) +
177
                    '</span><a href=' +
178
                    note.url +
179
                    ' target="_blank" >' +
180
                    note.url.substring ( ZERO, NoteHTMLViewsFactory.#LINKS_MAX_LENGTH ) +
181
                    '...</a>',
182
                theHTMLElementsFactory.create ( 'div', { className : classPrefix + 'NoteHtml-Url' }, noteHTMLElement )
183
            );
184
        }
185
186
        if ( ZERO !== note.phone.length ) {
187
            let phoneText = note.phone;
188
            if ( note.phone.match ( /^\+[0-9, ,*,\u0023]*$/ ) ) {
189
                const phoneNumber = note.phone.replaceAll ( /\u0020/g, '' );
190
                const phoneNumberDisplay = note.phone.replaceAll ( /\u0020/g, '\u00a0' );
191
                phoneText =
192
                    theTranslator.getText ( 'NoteHTMLViewsFactory - Phone' ) + '\u00a0:\u00a0' +
193
                    theTranslator.getText ( 'NoteHTMLViewsFactory - call' ) +
194
                    '<a target="_blank" href="tel:' + phoneNumber + '" >' + phoneNumberDisplay + '</a>' +
195
                    theTranslator.getText ( 'NoteHTMLViewsFactory - Send a sms to' ) +
196
                    '<a target="_blank" href="sms:' + phoneNumber + '" >' + phoneNumberDisplay + '</a>';
197
            }
198
            else {
199
                phoneText = theTranslator.getText ( 'NoteHTMLViewsFactory - Phone' ) + '\u00a0:\u00a0' + note.phone;
200
            }
201
            theHTMLSanitizer.sanitizeToHtmlElement (
202
                phoneText,
203
                theHTMLElementsFactory.create (
204
                    'div',
205
                    {
206
                        className : classPrefix + 'NoteHtml-Phone'
207
                    },
208
                    noteHTMLElement
209
                )
210
            );
211
        }
212
213
        theHTMLSanitizer.sanitizeToHtmlElement (
214
            theUtilities.formatLatLng ( note.latLng ),
215
            theHTMLElementsFactory.create (
216
                'div',
217
                {
218
                    className : classPrefix + 'NoteHtml-LatLng'
219
                },
220
                noteHTMLElement
221
            )
222
        );
223
224
        if ( noteAndRoute.route ) {
225
            if ( noteAndRoute.route.chain ) {
226
                theHTMLSanitizer.sanitizeToHtmlElement (
227
                    '<span>' +
228
                    theTranslator.getText ( 'NoteHTMLViewsFactory - Distance from start of travel' ) +
229
                    '</span>\u00a0:\u00a0' +
230
                    theUtilities.formatDistance ( note.chainedDistance + note.distance ),
231
                    theHTMLElementsFactory.create (
232
                        'div',
233
                        {
234
                            className : classPrefix + 'NoteHtml-TravelDistance'
235
                        },
236
                        noteHTMLElement
237
                    )
238
                );
239
            }
240
241
            theHTMLSanitizer.sanitizeToHtmlElement (
242
                '<span>' +
243
                theTranslator.getText ( 'NoteHTMLViewsFactory - Distance from start of route' ) +
244
                '</span>\u00a0:\u00a0' +
245
                theUtilities.formatDistance ( note.distance ),
246
                theHTMLElementsFactory.create (
247
                    'div',
248
                    {
249
                        className : classPrefix + 'NoteHtml-RouteDistance'
250
                    },
251
                    noteHTMLElement
252
                )
253
            );
254
255
            const nextNote = noteAndRoute.route.notes.next ( note.objId );
256
            if ( nextNote ) {
257
                const nextDistance = nextNote.distance - note.distance;
258
                if ( NoteHTMLViewsFactory.#MIN_NOTES_DISTANCE < nextDistance ) {
259
                    theHTMLSanitizer.sanitizeToHtmlElement (
260
                        '<span>' +
261
                        theTranslator.getText ( 'NoteHTMLViewsFactory - Next note after' ) +
262
                        '</span>\u00a0:\u00a0' +
263
                        theUtilities.formatDistance ( nextDistance ),
264
                        theHTMLElementsFactory.create (
265
                            'div',
266
                            {
267
                                className : classPrefix + 'NoteHtml-NextDistance'
268
                            },
269
                            noteHTMLElement
270
                        )
271
                    );
272
                }
273
            }
274
        }
275
        return noteHTMLElement;
276
    }
277
278
    /**
279
    Gives an HTMLElement with all the travel notes
280
    @param {String} classPrefix A string that will be added to all the className of the created HTMLElements
281
    @return {HTMLElement} An HTMLElement with all the travel notes
282
    */
283
284
    getTravelNotesHTML ( classPrefix ) {
285
        const travelNotesHTML = theHTMLElementsFactory.create ( 'div', { className : classPrefix + 'Travel-Notes' } );
286
        const travelNotesIterator = theTravelNotesData.travel.notes.iterator;
287
        while ( ! travelNotesIterator.done ) {
288
            const noteTextAndIconHTML = this.getNoteTextAndIconHTML (
289
                classPrefix,
290
                { note : travelNotesIterator.value, route : null }
291
            );
292
            noteTextAndIconHTML.className = classPrefix + 'Travel-Notes-Row';
293
            travelNotesHTML.appendChild ( noteTextAndIconHTML );
294
        }
295
        return travelNotesHTML;
296
    }
297
298
}
299
300
/* ------------------------------------------------------------------------------------------------------------------------- */
301
/**
302
The one and only one instance of NoteHTMLViewsFactory  class
303
@type {NoteHTMLViewsFactory }
304
*/
305
/* ------------------------------------------------------------------------------------------------------------------------- */
306
307
const theNoteHTMLViewsFactory = new NoteHTMLViewsFactory ( );
308
309
export default theNoteHTMLViewsFactory;
310
311
/* --- End of file --------------------------------------------------------------------------------------------------------- */
312