1 let assert = require('assert')
2 let XDate = require('xdate')
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')
13 // initialize env.cart
16 let transaction = await env.site.database.Transaction()
18 let site_title, copyright
20 let root = await transaction.get({})
22 let session = await get_session(env, root)
23 signed_in_as = await session.get_json('signed_in_as')
25 let globals = await root.get('globals', {})
26 site_title = await globals.get_json('site_title')
27 copyright = await globals.get_json('copyright')
30 transaction.rollback()
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)
46 menu.entries[menu.index[dir]].name
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)
61 div.row.align-items-center.py-3 {
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}.`
74 if (signed_in_as !== undefined)
75 a#navbar-sign-in(href="#" hidden) {'Sign in'}
77 a#navbar-sign-in(href="#") {'Sign in'}
79 if (signed_in_as !== undefined)
80 a#navbar-sign-up(href="/my_account/sign_up/index.html" hidden) {'Sign up'}
82 a#navbar-sign-up(href="/my_account/sign_up/index.html") {'Sign up'}
84 if (signed_in_as !== undefined)
85 a#navbar-sign-out(href="#") {'Sign out'}
87 a#navbar-sign-out(href="#" hidden) {'Sign out'}
90 form(action="/search/index.html") {
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") {
96 div.icon24-inner {_out.push(fa_search)}
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") {
109 // _out.push(icon_cart_small)
113 // `${(env.cart.items || []).length}`
122 nav.navbar.navbar-expand-lg.navbar-dark.bg-primary.scrollbar-fix {
124 //a.navbar-brand(href="#") {'Navbar'}
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 {}
129 div.collapse.navbar-collapse#navbarSupportedContent {
130 ul.navbar-nav.mr-auto {
131 if (dir.length === 0)
133 a.nav-link(href="/index.html") {
135 span.sr-only {' (current)'}
140 a.nav-link(href="/index.html") {'Home'}
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)
147 a.nav-link(href=`/${entries[i].dir}/index.html`) {
149 span.sr-only {' (current)'}
154 a.nav-link(href=`/${entries[i].dir}/index.html`) {
158 //li.nav-item.dropdown {
159 // a.nav-link.dropdown-toggle#navbarDropdown(href="#" role="button" data-toggle="dropdown" aria-expanded="false") {
162 // div.dropdown-menu(aria-labelledby="navbarDropdown") {
163 // a.dropdown-item(href="#") {
167 // a.dropdown-item(href="#") {
170 // div.dropdown-divider {}
171 // a.dropdown-item(href="#") {
172 // 'Something else here'
177 // a.nav-link.disabled {
182 ul.navbar-nav.ml-auto {
184 a.nav-link#navbar-give-feedback(href="#") {'Give feedback'}
195 footer.scrollbar-fix {
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") {}
202 a(href="https://git.ndcode.org/public/ndcode_site.git") {
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'
212 p {'Example code fragments embedded within the text are placed in the public domain unless otherwise noted.'}
214 p {`Copyright © ${new XDate(env.now).getUTCFullYear()} ${copyright}.`}
219 div#navbar-sign-in-modal.modal.fade(role="dialog") {
223 span.h4.modal-title {'Sign in'}
229 label.form-label(for="navbar-sign-in-email") {'Email'}
230 input.form-control#navbar-sign-in-email(type="text" placeholder="Account email address" required="required" maxlength=256) {}
237 label.form-label(for="navbar-sign-in-password") {'Password'}
238 input.form-control#navbar-sign-in-password(type="password" placeholder="Account password" required="required" minlength=8 maxlength=256) {}
245 a(href="/my_account/sign_up/index.html") {'Sign up'}
250 a(href="/my_account/password_reset/index.html") {'Password reset'}
254 button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
257 button.btn.btn-primary#navbar-sign-in-submit(type="button") {
265 div#navbar-feedback-modal.modal.fade(role="dialog") {
269 span.h4.modal-title {'Give feedback'}
273 'Did you notice something not quite right, or just want to share your impression of this page?'
278 label.form-label(for="navbar-feedback-message") {'Message'}
279 textarea.form-control#navbar-feedback-message(placeholder="Please tell us your thoughts" required="required" rows=4 maxlength=65536) {}
285 button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
288 button.btn.btn-primary#navbar-feedback-submit(type="button") {
296 div#navbar-message-modal.modal.fade(role="dialog") {
300 span.h4.modal-title {'Message'}
302 div.modal-body#navbar-message-modal-message {
305 button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
315 script(src="/js/utils.js") {}
318 // this function can be overridden in a further script
319 function sign_in_out(status) {
322 document.addEventListener(
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')
344 id_navbar_sign_in.addEventListener(
347 id_navbar_sign_in_email.value = ''
348 id_navbar_sign_in_password.value = ''
349 $('#navbar-sign-in-modal').modal('show')
353 $('#navbar-sign-in-modal').on(
355 () => {id_navbar_sign_in_email.focus()}
358 id_navbar_sign_in_submit.addEventListener(
363 email = id_navbar_sign_in_email.value.slice(0, 256).toLowerCase()
365 '/api/account/sign_in.json',
367 id_navbar_sign_in_password.value.slice(0, 256)
371 let problem = Problem.from(error)
373 if (problem.title === 'Email not yet verified') {
374 location.href = `/my_account/send_verification_email?email=${encodeURIComponent(email)}`
378 id_navbar_message_modal_message.textContent = problem.detail
379 $('#navbar-sign-in-modal').modal('hide')
380 $('#navbar-message-modal').modal('show')
384 id_navbar_signed_in_status.textContent = 'Signed in.' //`Signed in as ${email}.`
385 id_navbar_sign_in.hidden = true
386 id_navbar_sign_up.hidden = true
387 id_navbar_sign_out.hidden = false
390 id_navbar_message_modal_message.textContent = `You are now signed in as "${email}".`
391 $('#navbar-sign-in-modal').modal('hide')
392 $('#navbar-message-modal').modal('show')
397 id_navbar_sign_out.addEventListener(
402 '/api/account/sign_out.json'
406 let problem = Problem.from(error)
408 id_navbar_message_modal_message.textContent = problem.detail
409 $('#navbar-sign-in-modal').modal('hide')
410 $('#navbar-message-modal').modal('show')
414 id_navbar_signed_in_status.textContent = 'Browsing as guest.'
415 id_navbar_sign_in.hidden = false
416 id_navbar_sign_up.hidden = false
417 id_navbar_sign_out.hidden = true
420 id_navbar_message_modal_message.textContent = `You are now signed out.`
421 $('#navbar-sign-in-modal').modal('hide')
422 $('#navbar-message-modal').modal('show')
427 id_navbar_give_feedback.addEventListener(
430 id_navbar_feedback_message.value = ''
431 $('#navbar-feedback-modal').modal('show')
436 $('#navbar-feedback-modal').on(
438 () => {id_navbar_feedback_message.focus()}
441 id_navbar_feedback_submit.addEventListener(
446 '/api/feedback.json',
448 id_navbar_feedback_message.value.slice(0, 65536)
452 let problem = Problem.from(error)
454 id_navbar_message_modal_message.textContent = problem.detail
455 $('#navbar-feedback-modal').modal('hide')
456 $('#navbar-message-modal').modal('show')
460 id_navbar_message_modal_message.textContent = 'Thanks! We have received your feedback.'
461 $('#navbar-feedback-modal').modal('hide')
462 $('#navbar-message-modal').modal('show')