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