2 let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
3 let fa_arrow_circle_left = await env.site.get_min_svg('/_svg/fa_arrow-circle-left.svg')
4 let fa_envelope = await env.site.get_min_svg('/_svg/fa_envelope.svg')
5 let fa_redo = await env.site.get_min_svg('/_svg/fa_redo.svg')
6 let fa_user_plus = await env.site.get_min_svg('/_svg/fa_user-plus.svg')
7 let get_placeholder = await _require('/_lib/get_placeholder.jst')
8 let get_session = await _require('/_lib/get_session.jst')
9 let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
10 let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
11 let navbar = await _require('/_lib/navbar.jst')
13 // preload draft details if any
14 let transaction = await env.site.database.Transaction()
18 let root = await transaction.get({})
19 let session = await get_session(env, root)
21 placeholder = await get_placeholder(env, session)
23 sign_up_draft = await session.get_json('sign_up_draft')
24 if (sign_up_draft === undefined || env.now >= sign_up_draft.expires)
29 transaction.rollback()
39 await breadcrumbs(env, _out)
41 p {'Signing up allows you to leave comments on our blog and receive communications from us.'}
43 p {'Your given names are visible to other users if you comment on our blog. Your email and family name remain private. If your name is one word or does not fit given names/family name pattern, then please enter given names only.'}
45 div.accordion#accordion(role="tablist" aria-multiselectable="true") {
47 div.card-header#card-1-heading(role="tab") {
48 div.icon32-outer.mr-2#card-1-tick(hidden) {
49 div.icon32-inner {_out.push(icon_tick)}
51 div.icon32-outer.mr-2#card-1-cross(hidden) {
52 div.icon32-inner {_out.push(icon_cross)}
54 div.icon32-outer.mr-2#card-1-spinner(hidden) {
56 div.spinner-border(role="status") {}
59 a.h5(data-toggle="collapse" data-parent="#accordion" href="#card-1-collapse" aria-expanded="true" aria-controls="card-1-collapse") {
63 div#card-1-collapse.collapse.show(role="tabpanel" aria-labelledby="card-1-heading" data-parent="#accordion") {
69 label.form-label(for="given-names") {'Given names *'}
70 input.form-control#given-names(type="text" value=sign_up_draft !== null ? sign_up_draft.given_names : '' placeholder=placeholder.given_names required maxlength=256) {}
71 div.invalid-feedback {'Please enter a name we can address you by.'}
76 label.form-label(for="family-name") {'Family name'}
77 input.form-control#family-name(type="text" value=sign_up_draft !== null ? sign_up_draft.family_name : '' placeholder=placeholder.family_name maxlength=256) {}
84 label.form-label(for="email") {'Email *'}
85 input.form-control#email(type="email" value=sign_up_draft !== null ? sign_up_draft.email : '' placeholder=placeholder.email required maxlength=256) {}
86 div.invalid-feedback {'Please enter an email address we can contact you on.'}
91 label.form-label(for="password") {'Password *'}
92 input.form-control#password(type="password" placeholder="Choose" required minlength=8 maxlength=256) {}
93 div.invalid-feedback {'Please choose a secure password of at least 8 characters.'}
99 div.custom-control.custom-checkbox {
100 if (sign_up_draft === null || sign_up_draft.contact_me)
101 input.custom-control-input#contact-me(type="checkbox" checked) {}
103 input.custom-control-input#contact-me(type="checkbox") {}
105 label.custom-control-label(for="contact-me") {
106 'Contact me by email with updates and special offers'
111 div.row.align-items-center {
114 label.form-label(for="verification-code") {'Verification code *'}
115 input.form-control#verification-code(type="text" placeholder='xxxxxx' required minlength=6 maxlength=6) {}
116 div.invalid-feedback {'Please enter the 6 characters from the verification image to right or below. We need this to protect us from spam and bots.'}
119 div.'col-md-6'.my-3 {
120 img#verification-image(src="/api/verification_image.png?seq=0" width=300 height=150) {}
125 button.btn.btn-outline-secondary#'card-1-new-code'(type="button") {
126 div.icon24-outer.mr-2 {
127 div.icon24-inner {_out.push(fa_redo)}
131 if (sign_up_draft !== null)
132 button.btn.btn-success.ml-3#card-1-create-account(type="button") {
133 div.icon24-outer.mr-2 {
134 div.icon24-inner {_out.push(fa_user_plus)}
139 button.btn.btn-success.ml-3#card-1-create-account(type="button" disabled) {
140 div.icon24-outer.mr-2 {
141 div.icon24-inner {_out.push(fa_user_plus)}
146 p.'mt-3'.mb-0#card-1-message(hidden) {}
151 div.card-header#card-2-heading(role="tab") {
152 div.icon32-outer.mr-2#card-2-tick(hidden) {
153 div.icon32-inner {_out.push(icon_tick)}
155 div.icon32-outer.mr-2#card-2-cross(hidden) {
156 div.icon32-inner {_out.push(icon_cross)}
158 div.icon32-outer.mr-2#card-2-spinner(hidden) {
160 div.spinner-border(role="status") {}
163 a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
164 'Send email verification link'
167 div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="card-2-heading" data-parent="#accordion") {
169 button.btn.btn-outline-secondary#card-2-back(type="button") {
170 div.icon24-outer.mr-2 {
171 div.icon24-inner {_out.push(fa_arrow_circle_left)}
175 button.btn.btn-outline-secondary.ml-3#card-2-resend-email(type="button" disabled) {
176 div.icon24-outer.mr-2 {
177 div.icon24-inner {_out.push(fa_envelope)}
182 p.'mt-3'.mb-0#card-2-message(hidden) {}
188 p.mt-3 {'* These fields are required.'}
192 //script(src="/js/utils.js") {}
195 document.addEventListener(
198 let id_accordion = document.getElementById('accordion')
199 let id_card_1 = document.getElementById('card-1')
200 let id_card_1_collapse = document.getElementById('card-1-collapse')
201 let id_card_1_create_account = document.getElementById('card-1-create-account')
202 let id_card_1_cross = document.getElementById('card-1-cross')
203 let id_card_1_heading = document.getElementById('card-1-heading')
204 let id_card_1_message = document.getElementById('card-1-message')
205 let id_card_1_new_code = document.getElementById('card-1-new-code')
206 let id_card_1_spinner = document.getElementById('card-1-spinner')
207 let id_card_1_tick = document.getElementById('card-1-tick')
208 let id_card_2 = document.getElementById('card-2')
209 let id_card_2_back = document.getElementById('card-2-back')
210 let id_card_2_collapse = document.getElementById('card-2-collapse')
211 let id_card_2_cross = document.getElementById('card-2-cross')
212 let id_card_2_heading = document.getElementById('card-2-heading')
213 let id_card_2_message = document.getElementById('card-2-message')
214 let id_card_2_resend_email = document.getElementById('card-2-resend-email')
215 let id_card_2_spinner = document.getElementById('card-2-spinner')
216 let id_card_2_tick = document.getElementById('card-2-tick')
217 let id_contact_me = document.getElementById('contact-me')
218 let id_email = document.getElementById('email')
219 let id_family_name = document.getElementById('family-name')
220 let id_form = document.getElementById('form')
221 let id_given_names = document.getElementById('given-names')
222 let id_password = document.getElementById('password')
223 let id_verification_code = document.getElementById('verification-code')
224 let id_verification_image = document.getElementById('verification-image')
226 let input_semaphore = new BinarySemaphore(false)
230 await input_semaphore.acquire()
231 await new Promise(resolve => setTimeout(resolve, 3000))
232 input_semaphore.try_acquire()
234 '/api/account/sign_up/set_draft.json',
235 id_given_names.value.length === 0 &&
236 id_family_name.value.length === 0 &&
237 id_contact_me.checked &&
238 id_email.value.length === 0 ?
241 email: id_email.value.slice(0, 256).toLowerCase(),
242 given_names: id_given_names.value.slice(0, 256),
243 family_name: id_family_name.value.slice(0, 256),
244 contact_me: id_contact_me.checked ? true : false
249 )() // ignore returned promise (start thread)
251 let card_1_edited = in_draft => {
253 input_semaphore.release()
255 id_card_1_create_account.disabled =
256 id_given_names.value.length === 0 &&
257 id_family_name.value.length === 0 &&
258 id_contact_me.checked &&
259 id_email.value.length === 0 &&
260 id_password.value.length === 0 &&
261 id_verification_code.value.length === 0
262 id_card_1_tick.hidden = true
263 id_card_1_cross.hidden = true
264 id_card_1_spinner.hidden = true
265 id_card_1_message.hidden = true
267 id_card_2_resend_email.disabled = true
268 id_card_2_tick.hidden = true
269 id_card_2_cross.hidden = true
270 id_card_2_spinner.hidden = true
271 id_card_2_message.hidden = true
275 let card_1 = async () => {
276 id_card_1_tick.hidden = true
277 id_card_1_cross.hidden = true
278 id_card_1_spinner.hidden = true
279 // the below causes an ugly flicker, so just keep the message
280 //id_card_1_message.hidden = true
282 if (!id_form.checkValidity()) {
283 id_form.classList.add('was-validated');
284 id_card_1_cross.hidden = false
287 id_form.classList.remove('was-validated');
290 email: id_email.value.slice(0, 256).toLowerCase(),
291 given_names: id_given_names.value.slice(0, 256),
292 family_name: id_family_name.value.slice(0, 256),
293 password: id_password.value.slice(0, 256),
294 contact_me: id_contact_me.checked ? true : false
297 id_card_1_spinner.hidden = false
300 '/api/account/sign_up/create_account.json',
301 id_verification_code.value.slice(0, 6).toLowerCase(),
306 let problem = Problem.from(error)
308 id_card_1_cross.hidden = false
309 id_card_1_spinner.hidden = true
311 id_card_1_message.textContent = problem.detail
312 //id_card_1_message.classList.remove('text-success')
313 id_card_1_message.classList.add('text-danger')
314 id_card_1_message.hidden = false
316 $('#card-1-collapse').collapse('show')
319 id_card_1_tick.hidden = false
320 id_card_1_spinner.hidden = true
321 id_card_1_message.textContent = `Your account with email "${details.email}" has been created.`
322 //id_card_1_message.classList.add('text-success')
323 id_card_1_message.classList.remove('text-danger')
324 id_card_1_message.hidden = false
326 id_card_2_resend_email.disabled = false
327 id_card_2.scrollIntoView()
331 let card_2 = async () => {
332 id_card_2_tick.hidden = true
333 id_card_2_cross.hidden = true
334 id_card_2_spinner.hidden = false
335 // the below causes an ugly flicker, so just keep the message
336 //id_card_2_message.hidden = true
340 '/api/account/sign_up/send_email_verification_link.json',
345 let problem = Problem.from(error)
347 id_card_2_cross.hidden = false
348 id_card_2_spinner.hidden = true
350 id_card_2_message.textContent = problem.detail
351 //id_card_2_message.classList.remove('text-success')
352 id_card_2_message.classList.add('text-danger')
353 id_card_2_message.hidden = false
355 $('#card-2-collapse').collapse('show')
358 id_card_2_tick.hidden = false
359 id_card_2_spinner.hidden = true
361 id_card_2_message.textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
362 //id_card_2_message.classList.add('text-success')
363 id_card_2_message.classList.remove('text-danger')
364 id_card_2_message.hidden = false
368 id_given_names.addEventListener(
370 () => {card_1_edited(true)}
372 id_family_name.addEventListener(
374 () => {card_1_edited(true)}
376 id_email.addEventListener(
378 () => {card_1_edited(true)}
380 id_password.addEventListener(
382 () => {card_1_edited(false)}
384 id_contact_me.addEventListener(
386 () => {card_1_edited(true)}
388 id_verification_code.addEventListener(
390 () => {card_1_edited(false)}
394 id_card_1_new_code.addEventListener(
397 id_verification_image.src = `/api/verification_image.png?seq=${image_seq}`
402 id_card_1_create_account.addEventListener(
405 if (await card_1() && await card_2())
406 $('#card-2-collapse').collapse('show')
410 id_card_2_back.addEventListener(
412 () => {$('#card-1-collapse').collapse('show')}
415 id_card_2_resend_email.addEventListener(
419 $('#card-2-collapse').collapse('show')