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=placeholder.captcha_text 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.rm-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_card_1_create_account.disabled ?
238 email: id_email.value.slice(0, 256).toLowerCase(),
239 given_names: id_given_names.value.slice(0, 256),
240 family_name: id_family_name.value.slice(0, 256),
241 contact_me: id_contact_me.checked ? true : false
246 )() // ignore returned promise (start thread)
248 let card_1_edited = in_draft => {
250 input_semaphore.release()
252 id_card_1_create_account.disabled =
253 id_given_names.value.length === 0 &&
254 id_family_name.value.length === 0 &&
255 id_contact_me.checked &&
256 id_email.value.length === 0 &&
257 id_verification_code.value.length === 0
258 id_card_1_tick.hidden = true
259 id_card_1_cross.hidden = true
260 id_card_1_spinner.hidden = true
261 id_card_1_message.hidden = true
263 id_card_2_resend_email.disabled = true
264 id_card_2_tick.hidden = true
265 id_card_2_cross.hidden = true
266 id_card_2_spinner.hidden = true
267 id_card_2_message.hidden = true
271 let card_1 = async () => {
272 id_card_1_tick.hidden = true
273 id_card_1_cross.hidden = true
274 id_card_1_spinner.hidden = true
275 // the below causes an ugly flicker, so just keep the message
276 //id_card_1_message.hidden = true
278 if (!id_form.checkValidity()) {
279 id_form.classList.add('was-validated');
280 id_card_1_cross.hidden = false
283 id_form.classList.remove('was-validated');
286 email: id_email.value.slice(0, 256).toLowerCase(),
287 given_names: id_given_names.value.slice(0, 256),
288 family_name: id_family_name.value.slice(0, 256),
289 password: id_password.value.slice(0, 256),
290 contact_me: id_contact_me.checked ? true : false
293 id_card_1_spinner.hidden = false
296 '/api/account/sign_up/create_account.json',
297 id_verification_code.value.slice(0, 6).toLowerCase(),
302 let problem = Problem.from(error)
304 id_card_1_cross.hidden = false
305 id_card_1_spinner.hidden = true
307 id_card_1_message.textContent = problem.detail
308 //id_card_1_message.classList.remove('text-success')
309 id_card_1_message.classList.add('text-danger')
310 id_card_1_message.hidden = false
312 $('#card-1-collapse').collapse('show')
315 id_card_1_tick.hidden = false
316 id_card_1_spinner.hidden = true
317 id_card_1_message.textContent = `Your account with email "${details.email}" has been created.`
318 //id_card_1_message.classList.add('text-success')
319 id_card_1_message.classList.remove('text-danger')
320 id_card_1_message.hidden = false
322 id_card_2_resend_email.disabled = false
323 id_card_2.scrollIntoView()
327 let card_2 = async () => {
328 id_card_2_tick.hidden = true
329 id_card_2_cross.hidden = true
330 id_card_2_spinner.hidden = false
331 // the below causes an ugly flicker, so just keep the message
332 //id_card_2_message.hidden = true
336 '/api/account/sign_up/send_email_verification_link.json',
341 let problem = Problem.from(error)
343 id_card_2_cross.hidden = false
344 id_card_2_spinner.hidden = true
346 id_card_2_message.textContent = problem.detail
347 //id_card_2_message.classList.remove('text-success')
348 id_card_2_message.classList.add('text-danger')
349 id_card_2_message.hidden = false
351 $('#card-2-collapse').collapse('show')
354 id_card_2_tick.hidden = false
355 id_card_2_spinner.hidden = true
357 id_card_2_message.textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
358 //id_card_2_message.classList.add('text-success')
359 id_card_2_message.classList.remove('text-danger')
360 id_card_2_message.hidden = false
364 id_given_names.addEventListener(
366 () => {card_1_edited(true)}
368 id_family_name.addEventListener(
370 () => {card_1_edited(true)}
372 id_email.addEventListener(
374 () => {card_1_edited(true)}
376 id_password.addEventListener(
378 () => {card_1_edited(false)}
380 id_contact_me.addEventListener(
382 () => {card_1_edited(true)}
384 id_verification_code.addEventListener(
386 () => {card_1_edited(false)}
390 id_card_1_new_code.addEventListener(
393 id_verification_image.src = `/api/verification_image.png?seq=${image_seq}`
398 id_card_1_create_account.addEventListener(
401 if (await card_1() && await card_2())
402 $('#card-2-collapse').collapse('show')
406 id_card_2_back.addEventListener(
408 () => {$('#card-1-collapse').collapse('show')}
411 id_card_2_resend_email.addEventListener(
415 $('#card-2-collapse').collapse('show')