Remove modal fade because of an annoying glitch during fade in/out (it seems to move...
[ndcode_site.git] / _lib / navbar.jst
1 let assert = require('assert')
2 let XDate = require('xdate')
3
4 return async (env, head, body, scripts) => {
5   //let cart = await _require('/online_store/cart.jst')
6   let fa_arrow_circle_left = await env.site.get_min_svg('/_svg/fa_arrow-circle-left.svg')
7   let fa_times_circle = await env.site.get_min_svg('/_svg/fa_times-circle.svg')
8   let fa_envelope = await env.site.get_min_svg('/_svg/fa_envelope.svg')
9   let fa_unlock_alt = await env.site.get_min_svg('/_svg/fa_unlock-alt.svg')
10   let fa_search = await env.site.get_min_svg('/_svg/fa_search.svg')
11   let get_session = await _require('/_lib/get_session.jst')
12   //let icon_cart_small = await env.site.get_min_svg('/_svg/icon_cart_small.svg')
13   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
14   //let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
15   let logo_large = await env.site.get_min_svg('/_svg/logo_large.svg')
16   let menu = await env.site.get_menu('/_menu.json')
17   let page = await _require('/_lib/page.jst')
18
19   // initialize env.cart
20   //await cart(env)
21
22   let transaction = await env.site.database.Transaction()
23   let signed_in_as
24   let site_title, copyright
25   let feedback_draft
26   try {
27     let root = await transaction.get({})
28
29     let session = await get_session(env, root)
30     signed_in_as = await session.get_json('signed_in_as')
31
32     let globals = await root.get('globals', {})
33     site_title = await globals.get_json('site_title')
34     copyright = await globals.get_json('copyright')
35
36     feedback_draft = await session.get_json('feedback_draft')
37     if (feedback_draft === undefined || env.now >= feedback_draft.expires)
38       feedback_draft = null
39   }
40   finally {
41     transaction.rollback()
42   }
43
44   await page(
45     env,
46     // head
47     async _out => {
48       // extract top-level directory name
49       assert(env.parsed_url.pathname.slice(0, 1) === '/')
50       let index = env.parsed_url.pathname.indexOf('/', 1)
51       let dir = index === -1 ? '' : env.parsed_url.pathname.slice(1, index)
52
53       title {
54         `${site_title}: ${
55           dir.length === 0 ?
56             'Home' :
57             menu.entries[menu.index[dir]].name
58           }`
59       }
60
61       await head(_out)
62     },
63     // body
64     async _out => {
65       // extract top-level directory name
66       assert(env.parsed_url.pathname.slice(0, 1) === '/')
67       let index = env.parsed_url.pathname.indexOf('/', 1)
68       let dir = index === -1 ? '' : env.parsed_url.pathname.slice(1, index)
69
70       div.scrollbar-fix {
71         div.container {
72           div.row.align-items-center.py-3 {
73             div.col-sm-8 {
74               _out.push(logo_large)
75             }
76             div.'col-sm-4' {
77               div.'mb-1'.text-right {
78                 span#navbar-signed-in-status {
79                   if (signed_in_as !== undefined)
80                     'Signed in.' //`Signed in as ${signed_in_as}.`
81                   else
82                     'Browsing as guest.'
83                 }
84                 ' '
85                 if (signed_in_as !== undefined)
86                   a#navbar-sign-in(href="#" hidden) {'Sign in'}
87                 else
88                   a#navbar-sign-in(href="#") {'Sign in'}
89                 ' '
90                 if (signed_in_as !== undefined)
91                   a#navbar-sign-up(href="/my_account/sign_up/index.html" hidden) {'Sign up'}
92                 else
93                   a#navbar-sign-up(href="/my_account/sign_up/index.html") {'Sign up'}
94                 ' '
95                 if (signed_in_as !== undefined)
96                   a#navbar-sign-out(href="#") {'Sign out'}
97                 else
98                   a#navbar-sign-out(href="#" hidden) {'Sign out'}
99               }
100
101               form(action="/search/index.html") {
102                 div.input-group {
103                   input.form-control(name="query" type="text" placeholder="Search" aria-describedby="search-button") {}
104                   div.input-group-append {
105                     button.btn.btn-outline-secondary#navbar-search-button(type="submit") {
106                       div.icon24-outer {
107                         div.icon24-inner {_out.push(fa_search)}
108                       }
109                     }
110                   }
111                 }
112               }
113             }
114
115             //div.'col-sm-1'.vbottom {
116             //  // a nested div is used to avoid hover colour on the padding
117             //  div.nav-li-a(style="text-align: center;") {
118             //    a(href="/online_store/view_cart/index.html") {
119             //      div.cart-icon {
120             //        _out.push(icon_cart_small)
121             //      }
122             //      div.cart-number {
123             //        div.cart-circle {
124             //          `${(env.cart.items || []).length}`
125             //        }
126             //      }
127             //    }
128             //  }
129             //}
130           }
131         }
132       }
133       nav.navbar.navbar-expand-lg.navbar-dark.bg-primary.scrollbar-fix {
134         div.container {
135           //a.navbar-brand(href="#") {'Navbar'}
136           //' '
137           button.navbar-toggler(type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation") {
138             span.navbar-toggler-icon {}
139           }
140           div.collapse.navbar-collapse#navbarSupportedContent {
141             ul.navbar-nav.mr-auto {
142               if (dir.length === 0)
143                 li.nav-item.active {
144                   a.nav-link(href="/index.html") {
145                     'Home'
146                     span.sr-only {' (current)'}
147                   }
148                 }
149               else
150                 li.nav-item {
151                   a.nav-link(href="/index.html") {'Home'}
152                 }
153               let entries = menu.entries
154               for (let i = 0; i < entries.length; ++i)
155                 if (entries[i].navbar)
156                   if (entries[i].dir === dir)
157                     li.nav-item.active {
158                       a.nav-link(href=`/${entries[i].dir}/index.html`) {
159                         `${entries[i].name}`
160                         span.sr-only {' (current)'}
161                       }
162                     }
163                   else
164                     li.nav-item {
165                       a.nav-link(href=`/${entries[i].dir}/index.html`) {
166                         `${entries[i].name}`
167                       }
168                     }
169               //li.nav-item.dropdown {
170               //  a.nav-link.dropdown-toggle#navbarDropdown(href="#" role="button" data-toggle="dropdown" aria-expanded="false") {
171               //    'Dropdown'
172               //  }
173               //  div.dropdown-menu(aria-labelledby="navbarDropdown") {
174               //    a.dropdown-item(href="#") {
175               //      'Action'
176               //    }
177               //    ' '
178               //    a.dropdown-item(href="#") {
179               //      'Another action'
180               //    }
181               //    div.dropdown-divider {}
182               //    a.dropdown-item(href="#") {
183               //      'Something else here'
184               //    }
185               //  }
186               //}
187               //li.nav-item {
188               //  a.nav-link.disabled {
189               //    'Disabled'
190               //  }
191               //}
192             }
193             ul.navbar-nav.ml-auto {
194               li.nav-item {
195                 a.nav-link#navbar-give-feedback(href="#") {'Give feedback'}
196               }
197             }
198           }
199         }
200       }
201       div.scrollbar-fix {
202         div.container {
203           await body(_out)
204         }
205       }
206       footer.scrollbar-fix {
207         div.container {
208           a(rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/") {
209             img(alt="Creative Commons License" style="border-width:0;" src="/images/by-sa_3.0_88x31.png") {}
210           }
211           p {
212             'This website is '
213             a(href="https://git.ndcode.org/public/ndcode_site.git") {
214               'open source'
215             }
216             ' and licensed under a '
217             a(rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/") {
218               'Creative Commons Attribution-ShareAlike 3.0 Unported License'
219             }
220             '.'
221           }
222
223           p {'Example code fragments embedded within the text are placed in the public domain unless otherwise noted.'}
224
225           p {`Copyright © ${new XDate(env.now).getUTCFullYear()} ${copyright}.`}
226         }
227       }
228
229       // hidden part
230       div.modal#navbar-sign-in-modal(tabindex="-1") {
231         div.modal-dialog {
232           div.modal-content {
233             div.modal-header {
234               span.h4.modal-title {'Sign in'}
235             }
236             div.modal-body {
237               form#navbar-sign-in-form {
238                 div.row {
239                   div.col-md-12 {
240                     div.form-group {
241                       label.form-label(for="navbar-sign-in-email") {'Email'}
242                       input.form-control#navbar-sign-in-email(type="email" required maxlength=256) {}
243                       div.invalid-feedback {'Please enter your account\'s email address.'}
244                     }
245                   }
246                 }
247                 div.row {
248                   div.col-md-12 {
249                     div.form-group {
250                       label.form-label(for="navbar-sign-in-password") {'Password'}
251                       input.form-control#navbar-sign-in-password(type="password" required minlength=8 maxlength=256) {}
252                       div.invalid-feedback {'Please enter at least 8 characters.'}
253                     }
254                   }
255                 }
256               }
257
258               p.mt-2 {
259                 'No account yet? '
260                 a(href="/my_account/sign_up/index.html") {'Sign up'}
261               }
262
263               p.mb-0 {
264                 'Forgot password? '
265                 a(href="/my_account/password_reset/index.html") {'Password reset'}
266               }
267
268               p.'mt-3'.mb-0#navbar-sign-in-message(hidden) {}
269             }
270             div.modal-footer {
271               button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
272                 div.icon24-outer.mr-2 {
273                   div.icon24-inner {_out.push(fa_arrow_circle_left)}
274                 }
275                 'Back'
276               }
277               button.btn.btn-primary#navbar-sign-in-sign-in(type="button") {
278                 div.icon24-outer.mr-2#navbar-sign-in-icon {
279                   div.icon24-inner {_out.push(fa_unlock_alt)}
280                 }
281                 //div.icon24-outer.mr-2#navbar-sign-in-tick(hidden) {
282                 //  div.icon24-inner {_out.push(icon_tick)}
283                 //}
284                 div.icon24-outer.mr-2#navbar-sign-in-cross(hidden) {
285                   div.icon24-inner {_out.push(icon_cross)}
286                 }
287                 div.icon24-outer.mr-2#navbar-sign-in-spinner(hidden) {
288                   div.icon24-inner {
289                     div.spinner-border.spinner-border-sm(role="status") {}
290                   }
291                 }
292                 'Sign in'
293               }
294             }
295           }
296         }
297       }
298
299       div.modal#navbar-feedback-modal(tabindex="-1") {
300         div.modal-dialog {
301           div.modal-content {
302             div.modal-header {
303               span.h4.modal-title {'Give feedback'}
304             }
305             div.modal-body {
306               p {
307                 'Did you notice something not quite right, or just want to share your impression of this page?'
308               }
309
310               form#navbar-feedback-form {
311                 div.row {
312                   div.col-md-12 {
313                     div.form-group {
314                       label.form-label(for="navbar-feedback-message1") {'Message'}
315                       textarea.form-control#navbar-feedback-message1(placeholder="I noticed that..." required rows=4 maxlength=65536) {
316                         if (feedback_draft)
317                           `${feedback_draft.message}`
318                       }
319                       div.invalid-feedback {'Please let us have your thoughts.'}
320                     }
321                   }
322                 }
323               }
324
325               p.'mt-3'.mb-0#navbar-feedback-message(hidden) {}
326             }
327             div.modal-footer {
328               button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
329                 div.icon24-outer.mr-2 {
330                   div.icon24-inner {_out.push(fa_arrow_circle_left)}
331                 }
332                 'Back'
333               }
334               if (feedback_draft)
335                 button.btn.btn-primary#navbar-feedback-send-message(type="button") {
336                   div.icon24-outer.mr-2#navbar-feedback-icon {
337                     div.icon24-inner {_out.push(fa_envelope)}
338                   }
339                   //div.icon24-outer.mr-2#navbar-feedback-tick(hidden) {
340                   //  div.icon24-inner {_out.push(icon_tick)}
341                   //}
342                   div.icon24-outer.mr-2#navbar-feedback-cross(hidden) {
343                     div.icon24-inner {_out.push(icon_cross)}
344                   }
345                   div.icon24-outer.mr-2#navbar-feedback-spinner(hidden) {
346                     div.icon24-inner {
347                       div.spinner-border.spinner-border-sm(role="status") {}
348                     }
349                   }
350                   'Send message'
351                 }
352               else
353                 button.btn.btn-primary#navbar-feedback-send-message(type="button" disabled) {
354                   div.icon24-outer.mr-2#navbar-feedback-icon {
355                     div.icon24-inner {_out.push(fa_envelope)}
356                   }
357                   //div.icon24-outer.mr-2#navbar-feedback-tick(hidden) {
358                   //  div.icon24-inner {_out.push(icon_tick)}
359                   //}
360                   div.icon24-outer.mr-2#navbar-feedback-cross(hidden) {
361                     div.icon24-inner {_out.push(icon_cross)}
362                   }
363                   div.icon24-outer.mr-2#navbar-feedback-spinner(hidden) {
364                     div.icon24-inner {
365                       div.spinner-border.spinner-border-sm(role="status") {}
366                     }
367                   }
368                   'Send message'
369                 }
370             }
371           }
372         }
373       }
374
375       div.modal#navbar-message-modal(tabindex="-1") {
376         div.modal-dialog {
377           div.modal-content {
378             div.modal-header {
379               span.h4.modal-title {'Message'}
380             }
381             div.modal-body#navbar-message-modal-message {
382             }
383             div.modal-footer {
384               button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
385                 div.icon24-outer.mr-2 {
386                   div.icon24-inner {_out.push(fa_times_circle)}
387                 }
388                 'Close'
389               }
390             }
391           }
392         }
393       }
394     },
395     // scripts
396     async _out => {
397       script(src="/js/utils.js") {}
398
399       script {
400         // this function can be overridden in a further script
401         function sign_in_out(status) {
402         }
403
404         document.addEventListener(
405           'DOMContentLoaded',
406           () => {
407             let id_navbar_feedback_cross = document.getElementById('navbar-feedback-cross')
408             let id_navbar_feedback_form = document.getElementById('navbar-feedback-form')
409             let id_navbar_feedback_icon = document.getElementById('navbar-feedback-icon')
410             let id_navbar_feedback_message = document.getElementById('navbar-feedback-message')
411             let id_navbar_feedback_message1 = document.getElementById('navbar-feedback-message1')
412             let id_navbar_feedback_modal = document.getElementById('navbar-feedback-modal')
413             let id_navbar_feedback_send_message = document.getElementById('navbar-feedback-send-message')
414             let id_navbar_feedback_spinner = document.getElementById('navbar-feedback-spinner')
415             let id_navbar_feedback_tick = document.getElementById('navbar-feedback-tick')
416             let id_navbar_give_feedback = document.getElementById('navbar-give-feedback')
417             let id_navbar_message_modal = document.getElementById('navbar-message-modal')
418             let id_navbar_message_modal_message = document.getElementById('navbar-message-modal-message')
419             let id_navbar_search_button = document.getElementById('navbar-search-button')
420             let id_navbar_sign_in = document.getElementById('navbar-sign-in')
421             let id_navbar_sign_in_cross = document.getElementById('navbar-sign-in-cross')
422             let id_navbar_sign_in_email = document.getElementById('navbar-sign-in-email')
423             let id_navbar_sign_in_form = document.getElementById('navbar-sign-in-form')
424             let id_navbar_sign_in_icon = document.getElementById('navbar-sign-in-icon')
425             let id_navbar_sign_in_message = document.getElementById('navbar-sign-in-message')
426             let id_navbar_sign_in_modal = document.getElementById('navbar-sign-in-modal')
427             let id_navbar_sign_in_password = document.getElementById('navbar-sign-in-password')
428             let id_navbar_sign_in_sign_in = document.getElementById('navbar-sign-in-sign-in')
429             let id_navbar_sign_in_spinner = document.getElementById('navbar-sign-in-spinner')
430             let id_navbar_sign_in_tick = document.getElementById('navbar-sign-in-tick')
431             let id_navbar_sign_out = document.getElementById('navbar-sign-out')
432             let id_navbar_sign_up = document.getElementById('navbar-sign-up')
433             let id_navbar_signed_in_status = document.getElementById('navbar-signed-in-status')
434             //let id_navbarDropdown = document.getElementById('navbarDropdown')
435             //let id_navbarSupportedContent = document.getElementById('navbarSupportedContent')
436
437             // sign in form
438             id_navbar_sign_in.addEventListener(
439               'click',
440               () => {
441                 id_navbar_sign_in_email.value = ''
442                 id_navbar_sign_in_password.value = ''
443                 id_navbar_sign_in_sign_in.disabled = true
444                 $('#navbar-sign-in-modal').modal('show')
445               }
446             )
447
448             $('#navbar-sign-in-modal').on(
449               'shown.bs.modal',
450               () => {id_navbar_sign_in_email.focus()}
451             )
452
453             let sign_in_edited = () => {
454               id_navbar_sign_in_sign_in.disabled =
455                 id_navbar_sign_in_email.value.length === 0 &&
456                   id_navbar_sign_in_password.value.length === 0
457               id_navbar_sign_in_icon.hidden = false
458               //id_navbar_sign_in_tick.hidden = true
459               id_navbar_sign_in_cross.hidden = true
460               id_navbar_sign_in_spinner.hidden = true
461               id_navbar_sign_in_message.hidden = true
462             }
463
464             id_navbar_sign_in_email.addEventListener(
465               'input',
466               sign_in_edited
467             )
468             id_navbar_sign_in_password.addEventListener(
469               'input',
470               sign_in_edited
471             )
472
473             id_navbar_sign_in_sign_in.addEventListener(
474               'click',
475               async () => {
476                 id_navbar_sign_in_icon.hidden = false
477                 //id_navbar_sign_in_tick.hidden = true
478                 id_navbar_sign_in_cross.hidden = true
479                 id_navbar_sign_in_spinner.hidden = true
480                 // the below causes an ugly flicker, so just keep the message
481                 //id_navbar_sign_in_message.hidden = true
482
483                 if (!id_navbar_sign_in_form.checkValidity()) {
484                   id_navbar_sign_in_form.classList.add('was-validated');
485
486                   id_navbar_sign_in_icon.hidden = true
487                   id_navbar_sign_in_cross.hidden = false
488                   return
489                 }
490                 id_navbar_sign_in_form.classList.remove('was-validated');
491
492                 let email = id_navbar_sign_in_email.value.slice(0, 256).toLowerCase()
493
494                 id_navbar_sign_in_icon.hidden = true
495                 id_navbar_sign_in_spinner.hidden = false
496                 try {
497                   await api_call(
498                     '/api/account/sign_in.json',
499                     email,
500                     id_navbar_sign_in_password.value.slice(0, 256)
501                   )
502                 }
503                 catch (error) {
504                   let problem = Problem.from(error)
505
506                   if (problem.title === 'Email not yet verified') {
507                     location.href = `/my_account/send_verification_email?email=${encodeURIComponent(email)}`
508                     return
509                   }
510
511                   id_navbar_sign_in_cross.hidden = false
512                   id_navbar_sign_in_spinner.hidden = true
513
514                   id_navbar_sign_in_message.textContent = problem.detail
515                   //id_navbar_sign_in_message.classList.remove('text-success')
516                   id_navbar_sign_in_message.classList.add('text-danger')
517                   id_navbar_sign_in_message.hidden = false
518                   return
519                 }
520                 //id_navbar_sign_in_tick.hidden = false
521                 //id_navbar_sign_in_spinner.hidden = true
522                 //id_navbar_sign_in_message.textContent = `You are now signed in as "${email}".`
523                 //id_navbar_sign_in_message.classList.add('text-success')
524                 //id_navbar_sign_in_message.classList.remove('text-danger')
525                 //id_navbar_sign_in_message.hidden = false
526
527                 id_navbar_signed_in_status.textContent = 'Signed in.' //`Signed in as ${email}.`
528                 id_navbar_sign_in.hidden = true
529                 id_navbar_sign_up.hidden = true
530                 id_navbar_sign_out.hidden = false
531                 sign_in_out(true)
532
533                 id_navbar_sign_in_icon.hidden = false
534                 id_navbar_sign_in_spinner.hidden = true
535                 id_navbar_sign_in_message.hidden = true
536                 id_navbar_message_modal_message.textContent = `You are now signed in as "${email}".`
537                 $('#navbar-sign-in-modal').modal('hide')
538                 $('#navbar-message-modal').modal('show')
539               }
540             )
541
542             // sign out button
543             id_navbar_sign_out.addEventListener(
544               'click',
545               async () => {
546                 try {
547                   await api_call(
548                     '/api/account/sign_out.json'
549                   )
550                 }
551                 catch (error) {
552                   let problem = Problem.from(error)
553
554                   id_navbar_message_modal_message.textContent = problem.detail
555                   $('#navbar-sign-in-modal').modal('hide')
556                   $('#navbar-message-modal').modal('show')
557                   return
558                 }
559
560                 id_navbar_signed_in_status.textContent = 'Browsing as guest.'
561                 id_navbar_sign_in.hidden = false
562                 id_navbar_sign_up.hidden = false
563                 id_navbar_sign_out.hidden = true
564                 sign_in_out(false)
565
566                 id_navbar_message_modal_message.textContent = `You are now signed out.`
567                 $('#navbar-sign-in-modal').modal('hide')
568                 $('#navbar-message-modal').modal('show')
569               }
570             )
571
572             // feedback form
573             id_navbar_give_feedback.addEventListener(
574               'click',
575               () => {
576                 // hack to move cursor to end of textarea
577                 let temp = id_navbar_feedback_message1.value
578                 id_navbar_feedback_message1.value = ''
579                 id_navbar_feedback_message1.value = temp
580
581                 $('#navbar-feedback-modal').modal('show')
582                 return false
583               }
584             )
585
586             $('#navbar-feedback-modal').on(
587               'shown.bs.modal',
588               () => {id_navbar_feedback_message1.focus()}
589             )
590
591             let feedback_input_semaphore = new BinarySemaphore(false)
592             ;(
593               async () => {
594                 while (true) {
595                   await feedback_input_semaphore.acquire()
596                   await new Promise(resolve => setTimeout(resolve, 3000))
597                   feedback_input_semaphore.try_acquire()
598                   await api_call(
599                     '/api/feedback/set_draft.json',
600                     id_navbar_feedback_message1.value.length === 0 ?
601                       null :
602                       {
603                         message: id_navbar_feedback_message1.value.slice(0, 65536)
604                       }
605                   )
606                 }
607               }
608             )() // ignore returned promise (start thread)
609
610             let feedback_edited = () => {
611               feedback_input_semaphore.release()
612
613               id_navbar_feedback_send_message.disabled =
614                 id_navbar_feedback_message1.value.length === 0
615               id_navbar_feedback_icon.hidden = false
616               //id_navbar_feedback_tick.hidden = true
617               id_navbar_feedback_cross.hidden = true
618               id_navbar_feedback_spinner.hidden = true
619               id_navbar_feedback_message.hidden = true
620             }
621
622             id_navbar_feedback_message1.addEventListener(
623               'input',
624               feedback_edited
625             )
626
627             id_navbar_feedback_send_message.addEventListener(
628               'click',
629               async () => {
630                 id_navbar_feedback_icon.hidden = false
631                 //id_navbar_feedback_tick.hidden = true
632                 id_navbar_feedback_cross.hidden = true
633                 id_navbar_feedback_spinner.hidden = true
634                 // the below causes an ugly flicker, so just keep the message
635                 //id_navbar_feedback_message.hidden = true
636
637                 if (!id_navbar_feedback_form.checkValidity()) {
638                   id_navbar_feedback_form.classList.add('was-validated');
639
640                   id_navbar_feedback_icon.hidden = true
641                   id_navbar_feedback_cross.hidden = false
642                   return
643                 }
644                 id_navbar_feedback_form.classList.remove('was-validated');
645
646                 id_navbar_feedback_icon.hidden = true
647                 id_navbar_feedback_spinner.hidden = false
648                 try {
649                   await api_call(
650                     '/api/feedback/send_message.json',
651                     location.href,
652                     id_navbar_feedback_message1.value.slice(0, 65536)
653                   )
654                 }
655                 catch (error) {
656                   let problem = Problem.from(error)
657
658                   id_navbar_feedback_cross.hidden = false
659                   id_navbar_feedback_spinner.hidden = true
660
661                   id_navbar_feedback_message.textContent = problem.detail
662                   //id_navbar_feedback_message.classList.remove('text-success')
663                   id_navbar_feedback_message.classList.add('text-danger')
664                   id_navbar_feedback_message.hidden = false
665                   return
666                 }
667                 //id_navbar_feedback_tick.hidden = false
668                 //id_navbar_feedback_spinner.hidden = true
669                 //id_navbar_feedback_message.textContent = 'We have received your message. We will be in touch as soon as possible.'
670                 //id_navbar_feedback_message.classList.add('text-success')
671                 //id_navbar_feedback_message.classList.remove('text-danger')
672                 //id_navbar_feedback_message.hidden = false
673
674                 id_navbar_feedback_icon.hidden = false
675                 id_navbar_feedback_spinner.hidden = true
676                 id_navbar_feedback_message.hidden = true
677                 id_navbar_message_modal_message.textContent = 'Thanks! We have received your feedback.'
678                 $('#navbar-feedback-modal').modal('hide')
679                 $('#navbar-message-modal').modal('show')
680               }
681             )
682           }
683         )
684       }
685
686       await scripts(_out)
687     }
688   )
689 }