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 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')
13 // initialize env.cart
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)
27 globals.site_title + ': ' + (
30 menu.entries[menu.index[dir]].name
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)
46 div.row.align-items-center.py-3 {
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}.`
59 if (env.signed_in_as !== null)
60 a#sign-in(href="#" style="display: none;") {'Sign in'}
62 a#sign-in(href="#") {'Sign in'}
64 if (env.signed_in_as !== null)
65 a#sign-out(href="#") {'Sign out'}
67 a#sign-out(href="#" style="display: none;") {'Sign out'}
70 form(action="/search/index.html") {
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)
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") {
87 // _out.push(icon_cart_small)
91 // `${(env.cart.items || []).length}`
100 nav.navbar.navbar-expand-lg.navbar-dark.bg-primary.scrollbar-fix {
102 //a.navbar-brand(href="#") {'Navbar'}
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 {}
107 div.collapse.navbar-collapse#navbarSupportedContent {
108 ul.navbar-nav.mr-auto {
109 if (dir.length === 0)
111 a.nav-link(href="/index.html") {
113 span.sr-only {' (current)'}
118 a.nav-link(href="/index.html") {'Home'}
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)
125 a.nav-link(href=`/${entries[i].dir}/index.html`) {
127 span.sr-only {' (current)'}
132 a.nav-link(href=`/${entries[i].dir}/index.html`) {
136 //li.nav-item.dropdown {
137 // a.nav-link.dropdown-toggle#navbarDropdown(href="#" role="button" data-toggle="dropdown" aria-expanded="false") {
140 // div.dropdown-menu(aria-labelledby="navbarDropdown") {
141 // a.dropdown-item(href="#") {
145 // a.dropdown-item(href="#") {
148 // div.dropdown-divider {}
149 // a.dropdown-item(href="#") {
150 // 'Something else here'
155 // a.nav-link.disabled {
160 ul.navbar-nav.ml-auto {
162 a.nav-link#give-feedback(href="#") {'Give feedback'}
173 footer.scrollbar-fix {
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") {}
180 a(href="https://git.ndcode.org/public/ndcode_site.git") {
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'
190 p {'Example code fragments embedded within the text are placed in the public domain unless otherwise noted.'}
192 p {`Copyright © ${new XDate().getUTCFullYear()} ${globals.copyright}.`}
197 div#sign-in-modal.modal.fade(role="dialog") {
201 span.h4.modal-title {'Sign in'}
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) {}
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) {}
223 a(href="/my_account/sign_up/index.html") {'Sign up'}
228 a(href="/my_account/password_reset/index.html") {'Password reset'}
232 button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
235 button.btn.btn-primary#sign-in-submit(type="button") {
243 div#feedback-modal.modal.fade(role="dialog") {
247 span.h4.modal-title {'Give feedback'}
251 'Did you notice something not quite right, or just want to share your impression of this page?'
256 label(for="feedback-message") {'Message *'}
257 textarea.form-control#feedback-message(placeholder="Please tell us your thoughts" rows="4" required="required" data-error="Please, leave us a message.") {}
258 div.help-block.with-errors {}
262 p {} // fix this later
267 'These fields are required.'
268 //'Contact form template by '
269 //a(href="https://bootstrapious.com/p/how-to-build-a-working-bootstrap-feedback-form" target="_blank") {'Bootstrapious'}
276 button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
279 button.btn.btn-primary#feedback-submit(type="button") {
287 div#message-modal.modal.fade(role="dialog") {
291 span.h4.modal-title {'Message'}
293 div.modal-body#message-modal-message {
296 button.btn.btn-outline-secondary(type="button" data-dismiss="modal") {
306 script(src="/js/api_call.js") {}
309 let sign_in = async (...arguments) => api_call(
313 let sign_out = async (...arguments) => api_call(
314 '/api/sign_out.json',
318 // this function can be overridden in a further script
319 function sign_in_out(status) {
322 document.addEventListener(
326 document.getElementById('sign-in').addEventListener(
329 document.getElementById('sign-in-email').value = ''
330 document.getElementById('sign-in-password').value = ''
331 $('#sign-in-modal').modal('show')
335 $('#sign-in-modal').on(
339 $('#sign-in-email').focus()
343 document.getElementById('sign-in-submit').addEventListener(
348 email = document.getElementById('sign-in-email').value.slice(0, 256).toLowerCase()
351 document.getElementById('sign-in-password').value.slice(0, 256)
356 error instanceof Problem ?
362 (error.stack || error.message).toString()
367 if (problem.title === 'Email not yet verified') {
368 location.href = `/my_account/send_verification_email?email=${encodeURIComponent(email)}`
372 document.getElementById('message-modal-message').textContent = problem.detail
373 $('#sign-in-modal').modal('hide')
374 $('#message-modal').modal('show')
378 document.getElementById('signed-in-status').textContent = `Signed in as ${email}.`
380 $('#sign-out').show()
383 document.getElementById('message-modal-message').textContent = `You are now signed in as "${email}".`
384 $('#sign-in-modal').modal('hide')
385 $('#message-modal').modal('show')
390 document.getElementById('sign-out').addEventListener(
398 error instanceof Problem ?
404 (error.stack || error.message).toString()
409 document.getElementById('message-modal-message').textContent = problem.detail
410 $('#sign-in-modal').modal('hide')
411 $('#message-modal').modal('show')
415 document.getElementById('signed-in-status').textContent = 'Browsing as guest.'
417 $('#sign-out').hide()
420 document.getElementById('message-modal-message').textContent = `You are now signed out.`
421 $('#sign-in-modal').modal('hide')
422 $('#message-modal').modal('show')
427 document.getElementById('give-feedback').addEventListener(
430 $('#feedback-message').text('')
431 $('#feedback-modal').modal('show')
436 $('#feedback-modal').on(
439 $('#feedback-message').focus()
443 document.getElementById('feedback-submit').addEventListener(
448 url: '/api/feedback.html',
451 page: window.location.href,
452 message: $('#feedback-message').val()
454 success: (data, textStatus, jqXHR) => {
455 $('#feedback-modal').modal('hide')
456 document.getElementById('message-modal-message').textContent = data
457 $('#message-modal-message').text(data)
458 $('#message-modal').modal('show')
460 error: (jqXHR, textStatus, errorThrown) => {
461 $('#feedback-modal').modal('hide')
462 document.getElementById('message-modal-message').textContent = errorThrown
463 $('#message-modal').modal('show')