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 let input_semaphore = new BinarySemaphore(false)
199 await input_semaphore.acquire()
200 await new Promise(resolve => setTimeout(resolve, 3000))
201 input_semaphore.try_acquire()
203 '/api/account/sign_up/set_draft.json',
204 document.getElementById('card-1-create-account').disabled ?
207 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
208 given_names: document.getElementById('given-names').value.slice(0, 256),
209 family_name: document.getElementById('family-name').value.slice(0, 256),
210 contact_me: document.getElementById('contact-me').checked ? true : false
215 )() // ignore returned promise (start thread)
217 let card_1_edited = in_draft => {
219 input_semaphore.release()
221 document.getElementById('card-1-create-account').disabled =
222 document.getElementById('given-names').value.length === 0 &&
223 document.getElementById('family-name').value.length === 0 &&
224 document.getElementById('contact-me').checked &&
225 document.getElementById('email').value.length === 0 &&
226 document.getElementById('verification-code').value.length === 0
227 document.getElementById('card-1-tick').hidden = true
228 document.getElementById('card-1-cross').hidden = true
229 document.getElementById('card-1-spinner').hidden = true
230 document.getElementById('card-1-message').hidden = true
232 document.getElementById('card-2-resend-email').disabled = true
233 document.getElementById('card-2-tick').hidden = true
234 document.getElementById('card-2-cross').hidden = true
235 document.getElementById('card-2-spinner').hidden = true
236 document.getElementById('card-2-message').hidden = true
240 let card_1 = async () => {
241 document.getElementById('card-1-tick').hidden = true
242 document.getElementById('card-1-cross').hidden = true
243 document.getElementById('card-1-spinner').hidden = true
244 // the below causes an ugly flicker, so just keep the message
245 //document.getElementById('card-1-message').hidden = true
247 if (!document.getElementById('form').checkValidity()) {
248 document.getElementById('form').classList.add('was-validated');
249 document.getElementById('card-1-cross').hidden = false
252 document.getElementById('form').classList.remove('was-validated');
255 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
256 given_names: document.getElementById('given-names').value.slice(0, 256),
257 family_name: document.getElementById('family-name').value.slice(0, 256),
258 password: document.getElementById('password').value.slice(0, 256),
259 contact_me: document.getElementById('contact-me').checked ? true : false
262 document.getElementById('card-1-spinner').hidden = false
265 '/api/account/sign_up/create_account.json',
266 document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
271 let problem = Problem.from(error)
273 document.getElementById('card-1-cross').hidden = false
274 document.getElementById('card-1-spinner').hidden = true
276 document.getElementById('card-1-message').textContent = problem.detail
277 //document.getElementById('card-1-message').classList.remove('text-success')
278 document.getElementById('card-1-message').classList.add('text-danger')
279 document.getElementById('card-1-message').hidden = false
281 $('#card-1-collapse').collapse('show')
284 document.getElementById('card-1-tick').hidden = false
285 document.getElementById('card-1-spinner').hidden = true
286 document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
287 //document.getElementById('card-1-message').classList.add('text-success')
288 document.getElementById('card-1-message').classList.remove('text-danger')
289 document.getElementById('card-1-message').hidden = false
291 document.getElementById('card-2-resend-email').disabled = false
292 document.getElementById('card-2').scrollIntoView()
296 let card_2 = async () => {
297 document.getElementById('card-2-tick').hidden = true
298 document.getElementById('card-2-cross').hidden = true
299 document.getElementById('card-2-spinner').hidden = false
300 // the below causes an ugly flicker, so just keep the message
301 //document.getElementById('card-2-message').hidden = true
305 '/api/account/sign_up/send_email_verification_link.json',
310 let problem = Problem.from(error)
312 document.getElementById('card-2-cross').hidden = false
313 document.getElementById('card-2-spinner').hidden = true
315 document.getElementById('card-2-message').textContent = problem.detail
316 //document.getElementById('card-2-message').classList.remove('text-success')
317 document.getElementById('card-2-message').classList.add('text-danger')
318 document.getElementById('card-2-message').hidden = false
320 $('#card-2-collapse').collapse('show')
323 document.getElementById('card-2-tick').hidden = false
324 document.getElementById('card-2-spinner').hidden = true
326 document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
327 //document.getElementById('card-2-message').classList.add('text-success')
328 document.getElementById('card-2-message').classList.remove('text-danger')
329 document.getElementById('card-2-message').hidden = false
333 document.addEventListener(
336 document.getElementById('given-names').addEventListener(
338 () => {card_1_edited(true)}
340 document.getElementById('family-name').addEventListener(
342 () => {card_1_edited(true)}
344 document.getElementById('email').addEventListener(
346 () => {card_1_edited(true)}
348 document.getElementById('password').addEventListener(
350 () => {card_1_edited(false)}
352 document.getElementById('contact-me').addEventListener(
354 () => {card_1_edited(true)}
356 document.getElementById('verification-code').addEventListener(
358 () => {card_1_edited(false)}
362 document.getElementById('card-1-new-code').addEventListener(
365 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
370 document.getElementById('card-1-create-account').addEventListener(
373 if (await card_1() && await card_2())
374 $('#card-2-collapse').collapse('show')
378 document.getElementById('card-2-back').addEventListener(
380 () => {$('#card-1-collapse').collapse('show')}
383 document.getElementById('card-2-resend-email').addEventListener(
387 $('#card-2-collapse').collapse('show')