In /_lib/navbar.jst use variables id_XXX instead of document.getElementById()
[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_search = await env.site.get_min_svg('/_svg/fa_search.svg')
7   let get_session = await _require('/_lib/get_session.jst')
8   //let icon_cart_small = await env.site.get_min_svg('/_svg/icon_cart_small.svg')
9   let logo_large = await env.site.get_min_svg('/_svg/logo_large.svg')
10   let menu = await env.site.get_menu('/_menu.json')
11   let page = await _require('/_lib/page.jst')
12
13   // initialize env.cart
14   //await cart(env)
15
16   let transaction = await env.site.database.Transaction()
17   let signed_in_as
18   let site_title, copyright
19   try {
20     let root = await transaction.get({})
21
22     let session = await get_session(env, root)
23     signed_in_as = await session.get_json('signed_in_as')
24
25     let globals = await root.get('globals', {})
26     site_title = await globals.get_json('site_title')
27     copyright = await globals.get_json('copyright')
28   }
29   finally {
30     transaction.rollback()
31   }
32
33   await page(
34     env,
35     // head
36     async _out => {
37       // extract top-level directory name
38       assert(env.parsed_url.pathname.slice(0, 1) === '/')
39       let index = env.parsed_url.pathname.indexOf('/', 1)
40       let dir = index === -1 ? '' : env.parsed_url.pathname.slice(1, index)
41
42       title {
43         `${site_title}: ${
44           dir.length === 0 ?
45             'Home' :
46             menu.entries[menu.index[dir]].name
47           }`
48       }
49
50       await head(_out)
51     },
52     // body
53     async _out => {
54       // extract top-level directory name
55       assert(env.parsed_url.pathname.slice(0, 1) === '/')
56       let index = env.parsed_url.pathname.indexOf('/', 1)
57       let dir = index === -1 ? '' : env.parsed_url.pathname.slice(1, index)
58
59       div.scrollbar-fix {
60         div.container {
61           div.row.align-items-center.py-3 {
62             div.col-sm-8 {
63               _out.push(logo_large)
64             }
65             div.'col-sm-4' {
66               div.'mb-1'.text-right {
67                 span#navbar-signed-in-status {
68                   if (signed_in_as !== undefined)
69                     'Signed in.' //`Signed in as ${signed_in_as}.`
70                   else
71                     'Browsing as guest.'
72                 }
73                 ' '
74                 if (signed_in_as !== undefined)
75                   a#navbar-sign-in(href="#" style="display: none;") {'Sign in'}
76                 else
77                   a#navbar-sign-in(href="#") {'Sign in'}
78                 ' '
79                 if (signed_in_as !== undefined)
80                   a#navbar-sign-up(href="/my_account/sign_up/index.html" style="display: none;") {'Sign up'}
81                 else
82                   a#navbar-sign-up(href="/my_account/sign_up/index.html") {'Sign up'}
83                 ' '
84                 if (signed_in_as !== undefined)
85                   a#navbar-sign-out(href="#") {'Sign out'}
86                 else
87                   a#navbar-sign-out(href="#" style="display: none;") {'Sign out'}
88               }
89
90               form(action="/search/index.html") {
91                 div.input-group {
92                   input.form-control(name="query" type="text" placeholder="Search" aria-describedby="search-button") {}
93                   div.input-group-append {
94                     button.btn.btn-outline-secondary#navbar-search-button(type="submit") {
95                       div.icon24-outer {
96                         div.icon24-inner {_out.push(fa_search)}
97                       }
98                     }
99                   }
100                 }
101               }
102             }
103
104             //div.'col-sm-1'.vbottom {
105             //  // a nested div is used to avoid hover colour on the padding
106             //  div.nav-li-a(style="text-align: center;") {
107             //    a(href="/online_store/view_cart/index.html") {
108             //      div.cart-icon {
109             //        _out.push(icon_cart_small)
110             //      }
111             //      div.cart-number {
112             //        div.cart-circle {
113             //          `${(env.cart.items || []).length}`
114             //        }
115             //      }
116             //    }
117             //  }
118             //}
119           }
120         }
121       }
122       nav.navbar.navbar-expand-lg.navbar-dark.bg-primary.scrollbar-fix {
123         div.container {
124           //a.navbar-brand(href="#") {'Navbar'}
125           //' '
126           button.navbar-toggler(type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation") {
127             span.navbar-toggler-icon {}
128           }
129           div.collapse.navbar-collapse#navbarSupportedContent {
130             ul.navbar-nav.mr-auto {
131               if (dir.length === 0)
132                 li.nav-item.active {
133                   a.nav-link(href="/index.html") {
134                     'Home'
135                     span.sr-only {' (current)'}
136                   }
137                 }
138               else
139                 li.nav-item {
140                   a.nav-link(href="/index.html") {'Home'}
141                 }
142               let entries = menu.entries
143               for (let i = 0; i < entries.length; ++i)
144                 if (entries[i].navbar)
145                   if (entries[i].dir === dir)
146                     li.nav-item.active {
147                       a.nav-link(href=`/${entries[i].dir}/index.html`) {
148                         `${entries[i].name}`
149                         span.sr-only {' (current)'}
150                       }
151                     }
152                   else
153                     li.nav-item {
154                       a.nav-link(href=`/${entries[i].dir}/index.html`) {
155                         `${entries[i].name}`
156                       }
157                     }
158               //li.nav-item.dropdown {
159               //  a.nav-link.dropdown-toggle#navbarDropdown(href="#" role="button" data-toggle="dropdown" aria-expanded="false") {
160               //    'Dropdown'
161               //  }
162               //  div.dropdown-menu(aria-labelledby="navbarDropdown") {
163               //    a.dropdown-item(href="#") {
164               //      'Action'
165               //    }
166               //    ' '
167               //    a.dropdown-item(href="#") {
168               //      'Another action'
169               //    }
170               //    div.dropdown-divider {}
171               //    a.dropdown-item(href="#") {
172               //      'Something else here'
173               //    }
174               //  }
175               //}
176               //li.nav-item {
177               //  a.nav-link.disabled {
178               //    'Disabled'
179               //  }
180               //}
181             }
182             ul.navbar-nav.ml-auto {
183               li.nav-item {
184                 a.nav-link#navbar-give-feedback(href="#") {'Give feedback'}
185               }
186             }
187           }
188         }
189       }
190       div.scrollbar-fix {
191         div.container {
192           await body(_out)
193         }
194       }
195       footer.scrollbar-fix {
196         div.container {
197           a(rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/") {
198             img(alt="Creative Commons License" style="border-width:0;" src="/images/by-sa_3.0_88x31.png") {}
199           }
200           p {
201             'This website is '
202             a(href="https://git.ndcode.org/public/ndcode_site.git") {
203               'open source'
204             }
205             ' and licensed under a '
206             a(rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/") {
207               'Creative Commons Attribution-ShareAlike 3.0 Unported License'
208             }
209             '.'
210           }
211
212           p {'Example code fragments embedded within the text are placed in the public domain unless otherwise noted.'}
213
214           p {`Copyright © ${new XDate(env.now).getUTCFullYear()} ${copyright}.`}
215         }
216       }
217
218       // hidden part
219       div#navbar-sign-in-modal.modal.fade(role="dialog") {
220         div.modal-dialog {
221           div.modal-content {
222             div.modal-header {
223               span.h4.modal-title {'Sign in'}
224             }
225             div.modal-body {
226               div.row {
227                 div.col-md-12 {
228                   div.form-group {
229                     label.form-label(for="sign-in-email") {'Email'}
230                     input.form-control#navbar-sign-in-email(type="text" placeholder="Account email address" required="required" maxlength=256) {}
231                   }
232                 }
233               }
234               div.row {
235                 div.col-md-12 {
236                   div.form-group {
237                     label.form-label(for="sign-in-password") {'Password'}
238                     input.form-control#navbar-sign-in-password(type="password" placeholder="Account password" required="required" minlength=8 maxlength=256) {}
239                   }
240                 }
241               }
242
243               p.mt-2 {
244                 'No account yet? '
245                 a(href="/my_account/sign_up/index.html") {'Sign up'}
246               }
247
248               p {
249                 'Forgot password? '
250                 a(href="/my_account/password_reset/index.html") {'Password reset'}
251               }
252             }
253             div.modal-footer {
254               button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
255                 'Cancel'
256               }
257               button.btn.btn-primary#navbar-sign-in-submit(type="button") {
258                 'Sign in'
259               }
260             }
261           }
262         }
263       }
264
265       div#navbar-feedback-modal.modal.fade(role="dialog") {
266         div.modal-dialog {
267           div.modal-content {
268             div.modal-header {
269               span.h4.modal-title {'Give feedback'}
270             }
271             div.modal-body {
272               p {
273                 'Did you notice something not quite right, or just want to share your impression of this page?'
274               }
275               div.row {
276                 div.col-md-12 {
277                   div.form-group {
278                     label.form-label(for="feedback-message") {'Message'}
279                     textarea.form-control#navbar-feedback-message(placeholder="Please tell us your thoughts" required="required" rows=4 maxlength=65536) {}
280                   }
281                 }
282               }
283             }
284             div.modal-footer {
285               button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
286                 'Cancel'
287               }
288               button.btn.btn-primary#navbar-feedback-submit(type="button") {
289                 'Submit'
290               }
291             }
292           }
293         }
294       }
295
296       div#navbar-message-modal.modal.fade(role="dialog") {
297         div.modal-dialog {
298           div.modal-content {
299             div.modal-header {
300               span.h4.modal-title {'Message'}
301             }
302             div.modal-body#navbar-message-modal-message {
303             }
304             div.modal-footer {
305               button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
306                 'Close'
307               }
308             }
309           }
310         }
311       }
312     },
313     // scripts
314     async _out => {
315       script(src="/js/utils.js") {}
316
317       script {
318         // this function can be overridden in a further script
319         function sign_in_out(status) {
320         }
321
322         document.addEventListener(
323           'DOMContentLoaded',
324           () => {
325             let id_navbar_feedback_message = document.getElementById('navbar-feedback-message')
326             let id_navbar_feedback_modal = document.getElementById('navbar-feedback-modal')
327             let id_navbar_feedback_submit = document.getElementById('navbar-feedback-submit')
328             let id_navbar_give_feedback = document.getElementById('navbar-give-feedback')
329             let id_navbar_message_modal = document.getElementById('navbar-message-modal')
330             let id_navbar_message_modal_message = document.getElementById('navbar-message-modal-message')
331             let id_navbar_search_button = document.getElementById('navbar-search-button')
332             let id_navbar_sign_in = document.getElementById('navbar-sign-in')
333             let id_navbar_sign_in_email = document.getElementById('navbar-sign-in-email')
334             let id_navbar_sign_in_modal = document.getElementById('navbar-sign-in-modal')
335             let id_navbar_sign_in_password = document.getElementById('navbar-sign-in-password')
336             let id_navbar_sign_in_submit = document.getElementById('navbar-sign-in-submit')
337             let id_navbar_sign_out = document.getElementById('navbar-sign-out')
338             let id_navbar_sign_up = document.getElementById('navbar-sign-up')
339             let id_navbar_signed_in_status = document.getElementById('navbar-signed-in-status')
340             //let id_navbarDropdown = document.getElementById('navbarDropdown')
341             //let id_navbarSupportedContent = document.getElementById('navbarSupportedContent')
342
343             // sign in form
344             id_navbar_sign_in.addEventListener(
345               'click',
346               () => {
347                 id_navbar_sign_in_email.value = ''
348                 id_navbar_sign_in_password.value = ''
349                 $('#navbar-sign-in-modal').modal('show')
350               }
351             )
352
353             $('#navbar-sign-in-modal').on(
354               'shown.bs.modal',
355               () => {
356                 console.log('bloo')
357                 $('#navbar-sign-in-email').focus()
358               }
359             )
360
361             id_navbar_sign_in_submit.addEventListener(
362               'click',
363               async () => {
364                 let email
365                 try {
366                   email = id_navbar_sign_in_email.value.slice(0, 256).toLowerCase()
367                   await api_call(
368                     '/api/account/sign_in.json',
369                     email,
370                     id_navbar_sign_in_password.value.slice(0, 256)
371                   )
372                 }
373                 catch (error) {
374                   let problem = Problem.from(error)
375
376                   if (problem.title === 'Email not yet verified') {
377                     location.href = `/my_account/send_verification_email?email=${encodeURIComponent(email)}`
378                     return
379                   }
380
381                   id_navbar_message_modal_message.textContent = problem.detail
382                   $('#navbar-sign-in-modal').modal('hide')
383                   $('#navbar-message-modal').modal('show')
384                   return
385                 }
386
387                 id_navbar_signed_in_status.textContent = 'Signed in.' //`Signed in as ${email}.`
388                 $('#navbar-sign-in').hide()
389                 $('#navbar-sign-up').hide()
390                 $('#navbar-sign-out').show()
391                 sign_in_out(true)
392
393                 id_navbar_message_modal_message.textContent = `You are now signed in as "${email}".`
394                 $('#navbar-sign-in-modal').modal('hide')
395                 $('#navbar-message-modal').modal('show')
396               }
397             )
398
399             // sign out button
400             id_navbar_sign_out.addEventListener(
401               'click',
402               async () => {
403                 try {
404                   await api_call(
405                     '/api/account/sign_out.json'
406                   )
407                 }
408                 catch (error) {
409                   let problem = Problem.from(error)
410
411                   id_navbar_message_modal_message.textContent = problem.detail
412                   $('#navbar-sign-in-modal').modal('hide')
413                   $('#navbar-message-modal').modal('show')
414                   return
415                 }
416
417                 id_navbar_signed_in_status.textContent = 'Browsing as guest.'
418                 $('#navbar-sign-in').show()
419                 $('#navbar-sign-up').show()
420                 $('#navbar-sign-out').hide()
421                 sign_in_out(false)
422
423                 id_navbar_message_modal_message.textContent = `You are now signed out.`
424                 $('#navbar-sign-in-modal').modal('hide')
425                 $('#navbar-message-modal').modal('show')
426               }
427             )
428
429             // feedback form
430             id_navbar_give_feedback.addEventListener(
431               'click',
432               () => {
433                 $('#navbar-feedback-message').text('')
434                 $('#navbar-feedback-modal').modal('show')
435                 return false
436               }
437             )
438
439             $('#navbar-feedback-modal').on(
440               'shown.bs.modal',
441               () => {
442                 $('#navbar-feedback-message').focus()
443               }
444             )
445
446             id_navbar_feedback_submit.addEventListener(
447               'click',
448               async () => {
449                 try {
450                   await api_call(
451                     '/api/feedback.json',
452                     location.href,
453                     id_navbar_feedback_message.value.slice(0, 65536)
454                   )
455                 }
456                 catch (error) {
457                   let problem = Problem.from(error)
458
459                   id_navbar_message_modal_message.textContent = problem.detail
460                   $('#navbar-feedback-modal').modal('hide')
461                   $('#navbar-message-modal').modal('show')
462                   return
463                 }
464
465                 id_navbar_message_modal_message.textContent = 'Thanks! We have received your feedback.'
466                 $('#navbar-feedback-modal').modal('hide')
467                 $('#navbar-message-modal').modal('show')
468               }
469             )
470           }
471         )
472       }
473
474       await scripts(_out)
475     }
476   )
477 }