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.'}
97 div.row/*.align-items-center*/ {
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'
112 label.form-label(for="verification-code") {'Verification code *'}
114 img#verification-image(src="/api/verification_image.png?seq=0" width="300px" height="150px") {}
116 div.input-group#valid-workaround(style="width: 300px;") {
117 input.form-control#verification-code(type="text" placeholder='xxxxxx' required minlength=6 maxlength=6) {}
118 div.input-group-append {
119 button.btn.btn-outline-secondary#'card-1-new'(type="button") {
120 div.icon24-outer.mr-2 {
121 div.icon24-inner {_out.push(fa_redo)}
127 div.invalid-feedback(for="verification-code") {'Please enter the 6 characters from the verification image above. We need this to protect us from spam and bots.'}
133 if (sign_up_draft !== null)
134 button.btn.btn-success#card-1-create-account(type="button") {
135 div.icon24-outer.mr-2 {
136 div.icon24-inner {_out.push(fa_user_plus)}
141 button.btn.btn-success#card-1-create-account(type="button" disabled) {
142 div.icon24-outer.mr-2 {
143 div.icon24-inner {_out.push(fa_user_plus)}
148 p.'mt-3'.mb-0#card-1-message(hidden) {}
153 div.card-header#card-2-heading(role="tab") {
154 div.icon32-outer.mr-2#card-2-tick(hidden) {
155 div.icon32-inner {_out.push(icon_tick)}
157 div.icon32-outer.mr-2#card-2-cross(hidden) {
158 div.icon32-inner {_out.push(icon_cross)}
160 div.icon32-outer.mr-2#card-2-spinner(hidden) {
162 div.spinner-border(role="status") {}
165 a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
166 'Send email verification link'
169 div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="card-2-heading" data-parent="#accordion") {
171 button.btn.btn-outline-secondary#card-2-back(type="button") {
172 div.icon24-outer.mr-2 {
173 div.icon24-inner {_out.push(fa_arrow_circle_left)}
177 button.btn.btn-outline-secondary.ml-3#card-2-resend-email(type="button" disabled) {
178 div.icon24-outer.mr-2 {
179 div.icon24-inner {_out.push(fa_envelope)}
184 p.'mt-3'.mb-0#card-2-message(hidden) {}
190 p.text-muted.mt-3 {'* These fields are required.'}
194 //script(src="/js/utils.js") {}
197 document.addEventListener(
200 let id_accordion = document.getElementById('accordion')
201 let id_card_1 = document.getElementById('card-1')
202 let id_card_1_collapse = document.getElementById('card-1-collapse')
203 let id_card_1_create_account = document.getElementById('card-1-create-account')
204 let id_card_1_cross = document.getElementById('card-1-cross')
205 let id_card_1_heading = document.getElementById('card-1-heading')
206 let id_card_1_message = document.getElementById('card-1-message')
207 let id_card_1_new = document.getElementById('card-1-new')
208 let id_card_1_spinner = document.getElementById('card-1-spinner')
209 let id_card_1_tick = document.getElementById('card-1-tick')
210 let id_card_2 = document.getElementById('card-2')
211 let id_card_2_back = document.getElementById('card-2-back')
212 let id_card_2_collapse = document.getElementById('card-2-collapse')
213 let id_card_2_cross = document.getElementById('card-2-cross')
214 let id_card_2_heading = document.getElementById('card-2-heading')
215 let id_card_2_message = document.getElementById('card-2-message')
216 let id_card_2_resend_email = document.getElementById('card-2-resend-email')
217 let id_card_2_spinner = document.getElementById('card-2-spinner')
218 let id_card_2_tick = document.getElementById('card-2-tick')
219 let id_contact_me = document.getElementById('contact-me')
220 let id_email = document.getElementById('email')
221 let id_family_name = document.getElementById('family-name')
222 let id_form = document.getElementById('form')
223 let id_given_names = document.getElementById('given-names')
224 let id_password = document.getElementById('password')
225 let id_valid_workaround = document.getElementById('valid-workaround')
226 let id_verification_code = document.getElementById('verification-code')
227 let id_verification_image = document.getElementById('verification-image')
229 let input_semaphore = new BinarySemaphore(false)
233 await input_semaphore.acquire()
234 await new Promise(resolve => setTimeout(resolve, 3000))
235 input_semaphore.try_acquire()
237 '/api/account/sign_up/set_draft.json',
238 id_given_names.value.length === 0 &&
239 id_family_name.value.length === 0 &&
240 id_contact_me.checked &&
241 id_email.value.length === 0 ?
244 email: id_email.value.slice(0, 256).toLowerCase(),
245 given_names: id_given_names.value.slice(0, 256),
246 family_name: id_family_name.value.slice(0, 256),
247 contact_me: id_contact_me.checked ? true : false
252 )() // ignore returned promise (start thread)
254 let card_1_edited = in_draft => {
256 input_semaphore.release()
258 id_card_1_create_account.disabled =
259 id_given_names.value.length === 0 &&
260 id_family_name.value.length === 0 &&
261 id_contact_me.checked &&
262 id_email.value.length === 0 &&
263 id_password.value.length === 0 &&
264 id_verification_code.value.length === 0
265 id_card_1_tick.hidden = true
266 id_card_1_cross.hidden = true
267 id_card_1_spinner.hidden = true
268 id_card_1_message.hidden = true
270 id_card_2_resend_email.disabled = true
271 id_card_2_tick.hidden = true
272 id_card_2_cross.hidden = true
273 id_card_2_spinner.hidden = true
274 id_card_2_message.hidden = true
278 let card_1 = async () => {
279 id_card_1_tick.hidden = true
280 id_card_1_cross.hidden = true
281 id_card_1_spinner.hidden = true
282 // the below causes an ugly flicker, so just keep the message
283 //id_card_1_message.hidden = true
285 if (!id_form.checkValidity()) {
286 // workaround for https://github.com/twbs/bootstrap/issues/23454
287 if (id_verification_code.validity.valid) {
288 id_valid_workaround.classList.add('is-valid')
289 id_valid_workaround.classList.remove('is-invalid')
292 id_valid_workaround.classList.remove('is-valid')
293 id_valid_workaround.classList.add('is-invalid')
295 id_form.classList.add('was-validated');
297 id_card_1_cross.hidden = false
300 // workaround for https://github.com/twbs/bootstrap/issues/23454
301 id_valid_workaround.classList.remove('is-valid')
302 id_valid_workaround.classList.remove('is-invalid')
303 id_form.classList.remove('was-validated');
306 email: id_email.value.slice(0, 256).toLowerCase(),
307 given_names: id_given_names.value.slice(0, 256),
308 family_name: id_family_name.value.slice(0, 256),
309 password: id_password.value.slice(0, 256),
310 contact_me: id_contact_me.checked ? true : false
313 id_card_1_spinner.hidden = false
316 '/api/account/sign_up/create_account.json',
317 id_verification_code.value.slice(0, 6).toLowerCase(),
322 let problem = Problem.from(error)
324 id_card_1_cross.hidden = false
325 id_card_1_spinner.hidden = true
327 id_card_1_message.textContent = problem.detail
328 //id_card_1_message.classList.remove('text-success')
329 id_card_1_message.classList.add('text-danger')
330 id_card_1_message.hidden = false
332 $('#card-1-collapse').collapse('show')
335 id_card_1_tick.hidden = false
336 id_card_1_spinner.hidden = true
337 id_card_1_message.textContent = `Your account with email "${details.email}" has been created.`
338 //id_card_1_message.classList.add('text-success')
339 id_card_1_message.classList.remove('text-danger')
340 id_card_1_message.hidden = false
342 id_card_2_resend_email.disabled = false
343 id_card_2.scrollIntoView()
347 let card_2 = async () => {
348 id_card_2_tick.hidden = true
349 id_card_2_cross.hidden = true
350 id_card_2_spinner.hidden = false
351 // the below causes an ugly flicker, so just keep the message
352 //id_card_2_message.hidden = true
356 '/api/account/send_email_verification_link.json',
361 let problem = Problem.from(error)
363 id_card_2_cross.hidden = false
364 id_card_2_spinner.hidden = true
366 id_card_2_message.textContent = problem.detail
367 //id_card_2_message.classList.remove('text-success')
368 id_card_2_message.classList.add('text-danger')
369 id_card_2_message.hidden = false
371 $('#card-2-collapse').collapse('show')
374 id_card_2_tick.hidden = false
375 id_card_2_spinner.hidden = true
377 id_card_2_message.textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
378 //id_card_2_message.classList.add('text-success')
379 id_card_2_message.classList.remove('text-danger')
380 id_card_2_message.hidden = false
384 id_given_names.addEventListener(
386 () => {card_1_edited(true)}
388 id_family_name.addEventListener(
390 () => {card_1_edited(true)}
392 id_email.addEventListener(
394 () => {card_1_edited(true)}
396 id_password.addEventListener(
398 () => {card_1_edited(false)}
400 id_contact_me.addEventListener(
402 () => {card_1_edited(true)}
404 id_verification_code.addEventListener(
406 () => {card_1_edited(false)}
410 id_card_1_new.addEventListener(
413 id_verification_image.src =
414 `/api/verification_image.png?seq=${image_seq}`
419 id_card_1_create_account.addEventListener(
422 if (await card_1() && await card_2())
423 $('#card-2-collapse').collapse('show')
427 id_card_2_back.addEventListener(
429 () => {$('#card-1-collapse').collapse('show')}
432 id_card_2_resend_email.addEventListener(
436 $('#card-2-collapse').collapse('show')