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