1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | import theTranslator from './uiLib/Translator.js'; |
26 | import theTravelNotesData from '../data/TravelNotesData.js'; |
27 | import NoteDialog from '../dialogs/notesDialog/NoteDialog.js'; |
28 | import Note from '../data/Note.js'; |
29 | import theDataSearchEngine from '../data/DataSearchEngine.js'; |
30 | import theEventDispatcher from './lib/EventDispatcher.js'; |
31 | import theGeometry from './lib/Geometry.js'; |
32 | import theConfig from '../data/Config.js'; |
33 | import WaitUI from '../uis/waitUI/WaitUI.js'; |
34 | import theErrorsUI from '../uis/errorsUI/ErrorsUI.js'; |
35 | import theNoteDialogToolbarData from '../dialogs/notesDialog/toolbar/NoteDialogToolbarData.js'; |
36 | import GeoCoder from './lib/GeoCoder.js'; |
37 | |
38 | import { DISTANCE, INVALID_OBJ_ID } from '../main/Constants.js'; |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | class NoteEditor { |
48 | |
49 | |
50 | |
51 | |
52 | |
53 | |
54 | #note = null; |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | #route = null; |
62 | |
63 | |
64 | |
65 | |
66 | |
67 | |
68 | #isNewNote = true; |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | #showSearchNoteDialog = null; |
77 | |
78 | |
79 | |
80 | |
81 | |
82 | #addNote ( ) { |
83 | if ( this.#isNewNote ) { |
84 | if ( this.#route ) { |
85 | this.#route.notes.add ( this.#note ); |
86 | this.#note.chainedDistance = this.#route.chainedDistance; |
87 | this.#route.notes.sort ( |
88 | ( first, second ) => first.distance - second.distance |
89 | ); |
90 | } |
91 | else { |
92 | theTravelNotesData.travel.notes.add ( this.#note ); |
93 | theEventDispatcher.dispatch ( 'updatetravelnotes' ); |
94 | } |
95 | } |
96 | else if ( ! this.#route ) { |
97 | |
98 | theEventDispatcher.dispatch ( 'updatetravelnotes' ); |
99 | } |
100 | |
101 | theEventDispatcher.dispatch ( |
102 | 'noteupdated', |
103 | { |
104 | removedNoteObjId : this.#note.objId, |
105 | addedNoteObjId : this.#note.objId |
106 | } |
107 | ); |
108 | theEventDispatcher.dispatch ( 'updateroadbook' ); |
109 | } |
110 | |
111 | |
112 | |
113 | |
114 | |
115 | #noteDialog ( ) { |
116 | new NoteDialog ( this.#note, this.#route ) |
117 | .show ( ) |
118 | .then ( ( ) => this.#addNote ( ) ) |
119 | .catch ( |
120 | err => { |
121 | console.error ( err ); |
122 | } |
123 | ); |
124 | } |
125 | |
126 | |
127 | |
128 | |
129 | |
130 | |
131 | #newNote ( latLng ) { |
132 | this.#isNewNote = true; |
133 | this.#note = new Note ( ); |
134 | this.#note.latLng = latLng; |
135 | this.#note.iconLatLng = latLng; |
136 | } |
137 | |
138 | |
139 | |
140 | |
141 | |
142 | |
143 | async #newSearchNote ( osmElement ) { |
144 | |
145 | |
146 | if ( osmElement.tags.rcn_ref ) { |
147 | this.#note.iconContent = |
148 | '<div class=\'TravelNotes-MapNote TravelNotes-MapNoteCategory-0073\'>' + |
149 | '<svg viewBox=\'0 0 20 20\'><text x=\'10\' y=\'14\'>' + |
150 | osmElement.tags.rcn_ref + |
151 | '</text></svg></div>'; |
152 | } |
153 | else { |
154 | this.#note.iconContent = theNoteDialogToolbarData.preDefinedIconDataFromName ( osmElement.description ); |
155 | } |
156 | |
157 | |
158 | this.#note.url = osmElement.tags.website || ''; |
159 | this.#note.phone = osmElement.tags.phone || ''; |
160 | this.#note.tooltipContent = osmElement.description || ''; |
161 | this.#note.popupContent = osmElement.tags.name || ''; |
162 | |
163 | |
164 | if ( |
165 | osmElement.tags [ 'addr:street' ] |
166 | && |
167 | osmElement.tags [ 'addr:city' ] |
168 | ) { |
169 | |
170 | |
171 | this.#note.address = |
172 | ( osmElement.tags [ 'addr:housenumber' ] ? osmElement.tags [ 'addr:housenumber' ] + ' ' : '' ) + |
173 | osmElement.tags [ 'addr:street' ] + |
174 | ' <span class="TravelNotes-NoteHtml-Address-City">' + osmElement.tags [ 'addr:city' ] + '</span>'; |
175 | } |
176 | else { |
177 | |
178 | |
179 | const waitUI = new WaitUI ( ); |
180 | waitUI.createUI ( ); |
181 | waitUI.showInfo ( 'Creating address' ); |
182 | let geoCoderData = null; |
183 | try { |
184 | geoCoderData = await new GeoCoder ( ).getAddressAsync ( [ osmElement.lat, osmElement.lon ] ); |
185 | } |
186 | catch ( err ) { |
187 | console.error ( err ); |
188 | } |
189 | waitUI.close ( ); |
190 | this.#note.address = geoCoderData.street; |
191 | if ( '' !== geoCoderData.city ) { |
192 | this.#note.address += |
193 | ' <span class="TravelNotes-NoteHtml-Address-City">' + geoCoderData.city + '</span>'; |
194 | } |
195 | } |
196 | |
197 | |
198 | if ( this.osmSearchNoteDialog || '' === this.#note.iconContent ) { |
199 | this.#noteDialog ( ); |
200 | } |
201 | else { |
202 | this.#addNote ( ); |
203 | } |
204 | } |
205 | |
206 | |
207 | |
208 | |
209 | |
210 | constructor ( ) { |
211 | Object.freeze ( this ); |
212 | } |
213 | |
214 | |
215 | |
216 | |
217 | |
218 | |
219 | get osmSearchNoteDialog ( ) { |
220 | if ( null === this.#showSearchNoteDialog ) { |
221 | |
222 | |
223 | this.#showSearchNoteDialog = theConfig.osmSearch.showSearchNoteDialog; |
224 | } |
225 | return this.#showSearchNoteDialog; |
226 | } |
227 | |
228 | |
229 | |
230 | |
231 | |
232 | changeOsmSearchNoteDialog ( ) { |
233 | this.#showSearchNoteDialog = ! this.osmSearchNoteDialog; |
234 | } |
235 | |
236 | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | newRouteNote ( routeObjId, latLng ) { |
243 | |
244 | this.#route = theDataSearchEngine.getRoute ( routeObjId ); |
245 | |
246 | |
247 | const latLngDistance = theGeometry.getClosestLatLngDistance ( this.#route, latLng ); |
248 | |
249 | |
250 | this.#newNote ( latLngDistance.latLng ); |
251 | this.#note.distance = latLngDistance.distance; |
252 | |
253 | |
254 | this.#noteDialog ( ); |
255 | } |
256 | |
257 | |
258 | |
259 | |
260 | |
261 | |
262 | newSearchRouteNote ( osmElement ) { |
263 | const nearestRouteData = theDataSearchEngine.getNearestRouteData ( [ osmElement.lat, osmElement.lon ] ); |
264 | if ( ! nearestRouteData.route ) { |
265 | theErrorsUI.showError ( theTranslator.getText ( 'NoteEditor - No route was found' ) ); |
266 | return; |
267 | } |
268 | this.#newNote ( nearestRouteData.latLngOnRoute ); |
269 | this.#note.iconLatLng = [ osmElement.lat, osmElement.lon ]; |
270 | this.#note.distance = nearestRouteData.distanceOnRoute; |
271 | this.#route = nearestRouteData.route; |
272 | this.#newSearchNote ( osmElement ); |
273 | } |
274 | |
275 | |
276 | |
277 | |
278 | |
279 | |
280 | newSearchTravelNote ( osmElement ) { |
281 | this.#route = null; |
282 | this.#newNote ( [ osmElement.lat, osmElement.lon ] ); |
283 | this.#newSearchNote ( osmElement ); |
284 | } |
285 | |
286 | |
287 | |
288 | |
289 | |
290 | |
291 | newTravelNote ( latLng ) { |
292 | this.#route = null; |
293 | this.#newNote ( latLng ); |
294 | this.#noteDialog ( ); |
295 | } |
296 | |
297 | |
298 | |
299 | |
300 | |
301 | |
302 | editNote ( noteObjId ) { |
303 | this.#isNewNote = false; |
304 | const noteAndRoute = theDataSearchEngine.getNoteAndRoute ( noteObjId ); |
305 | this.#route = noteAndRoute.route; |
306 | this.#note = noteAndRoute.note; |
307 | this.#noteDialog ( ); |
308 | } |
309 | |
310 | |
311 | |
312 | |
313 | |
314 | |
315 | removeNote ( noteObjId ) { |
316 | |
317 | |
318 | const noteAndRoute = theDataSearchEngine.getNoteAndRoute ( noteObjId ); |
319 | if ( noteAndRoute.route ) { |
320 | |
321 | |
322 | noteAndRoute.route.notes.remove ( noteObjId ); |
323 | } |
324 | else { |
325 | |
326 | |
327 | theTravelNotesData.travel.notes.remove ( noteObjId ); |
328 | theEventDispatcher.dispatch ( 'updatetravelnotes' ); |
329 | } |
330 | theEventDispatcher.dispatch ( |
331 | 'noteupdated', |
332 | { |
333 | removedNoteObjId : noteObjId, |
334 | addedNoteObjId : INVALID_OBJ_ID |
335 | } |
336 | ); |
337 | theEventDispatcher.dispatch ( 'updateroadbook' ); |
338 | } |
339 | |
340 | |
341 | |
342 | |
343 | |
344 | hideNotes ( ) { |
345 | let notesIterator = theTravelNotesData.travel.notes.iterator; |
346 | while ( ! notesIterator.done ) { |
347 | theEventDispatcher.dispatch ( 'removeobject', { objId : notesIterator.value.objId } ); |
348 | } |
349 | const routesIterator = theTravelNotesData.travel.routes.iterator; |
350 | while ( ! routesIterator.done ) { |
351 | notesIterator = routesIterator.value.notes.iterator; |
352 | while ( ! notesIterator.done ) { |
353 | theEventDispatcher.dispatch ( 'removeobject', { objId : notesIterator.value.objId } ); |
354 | } |
355 | } |
356 | if ( INVALID_OBJ_ID !== theTravelNotesData.editedRouteObjId ) { |
357 | notesIterator = theTravelNotesData.travel.editedRoute.notes.iterator; |
358 | while ( ! notesIterator.done ) { |
359 | theEventDispatcher.dispatch ( 'removeobject', { objId : notesIterator.value.objId } ); |
360 | } |
361 | } |
362 | } |
363 | |
364 | |
365 | |
366 | |
367 | |
368 | showNotes ( ) { |
369 | this.hideNotes ( ); |
370 | const notesIterator = theTravelNotesData.travel.notes.iterator; |
371 | while ( ! notesIterator.done ) { |
372 | theEventDispatcher.dispatch ( |
373 | 'noteupdated', |
374 | { |
375 | removedNoteObjId : INVALID_OBJ_ID, |
376 | addedNoteObjId : notesIterator.value.objId |
377 | } |
378 | ); |
379 | } |
380 | const routesIterator = theTravelNotesData.travel.routes.iterator; |
381 | while ( ! routesIterator.done ) { |
382 | if ( ! routesIterator.value.hidden ) { |
383 | theEventDispatcher.dispatch ( |
384 | 'routeupdated', |
385 | { |
386 | removedRouteObjId : routesIterator.value.objId, |
387 | addedRouteObjId : routesIterator.value.objId |
388 | } |
389 | ); |
390 | } |
391 | } |
392 | } |
393 | |
394 | |
395 | |
396 | |
397 | |
398 | |
399 | |
400 | attachNoteToRoute ( noteObjId ) { |
401 | const note = theDataSearchEngine.getNoteAndRoute ( noteObjId ).note; |
402 | const nearestRouteData = theDataSearchEngine.getNearestRouteData ( note.latLng ); |
403 | |
404 | if ( nearestRouteData.route ) { |
405 | theTravelNotesData.travel.notes.remove ( noteObjId ); |
406 | note.distance = nearestRouteData.distanceOnRoute; |
407 | note.latLng = nearestRouteData.latLngOnRoute; |
408 | note.chainedDistance = nearestRouteData.route.chainedDistance; |
409 | nearestRouteData.route.notes.add ( note ); |
410 | nearestRouteData.route.notes.sort ( |
411 | ( first, second ) => first.distance - second.distance |
412 | ); |
413 | |
414 | theEventDispatcher.dispatch ( |
415 | 'noteupdated', |
416 | { |
417 | removedNoteObjId : noteObjId, |
418 | addedNoteObjId : noteObjId |
419 | } |
420 | ); |
421 | theEventDispatcher.dispatch ( 'updatetravelnotes' ); |
422 | theEventDispatcher.dispatch ( 'updateroadbook' ); |
423 | } |
424 | } |
425 | |
426 | |
427 | |
428 | |
429 | |
430 | |
431 | detachNoteFromRoute ( noteObjId ) { |
432 | const noteAndRoute = theDataSearchEngine.getNoteAndRoute ( noteObjId ); |
433 | noteAndRoute.route.notes.remove ( noteObjId ); |
434 | noteAndRoute.note.distance = DISTANCE.invalid; |
435 | noteAndRoute.note.chainedDistance = DISTANCE.defaultValue; |
436 | theTravelNotesData.travel.notes.add ( noteAndRoute.note ); |
437 | |
438 | theEventDispatcher.dispatch ( 'updatetravelnotes' ); |
439 | theEventDispatcher.dispatch ( 'updateroadbook' ); |
440 | } |
441 | |
442 | |
443 | |
444 | |
445 | |
446 | |
447 | |
448 | |
449 | |
450 | travelNoteDropped ( draggedNoteObjId, targetNoteObjId, draggedBefore ) { |
451 | theTravelNotesData.travel.notes.moveTo ( draggedNoteObjId, targetNoteObjId, draggedBefore ); |
452 | theEventDispatcher.dispatch ( 'updatetravelnotes' ); |
453 | theEventDispatcher.dispatch ( 'updateroadbook' ); |
454 | } |
455 | } |
456 | |
457 | |
458 | |
459 | |
460 | |
461 | |
462 | |
463 | |
464 | const theNoteEditor = new NoteEditor ( ); |
465 | |
466 | export default theNoteEditor; |
467 | |
468 | |
469 | |