2 let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
3 let get_placeholder = await _require('/_lib/get_placeholder.jst')
4 let get_session = await _require('/_lib/get_session.jst')
5 let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
6 let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
7 let navbar = await _require('/_lib/navbar.jst')
9 // preload draft details if any
10 let transaction = await env.site.database.Transaction()
14 let root = await transaction.get({})
15 let session = await get_session(env, root)
17 placeholder = await get_placeholder(env, session)
19 sign_up_draft = await session.get_json('sign_up_draft')
20 if (sign_up_draft === undefined || env.now >= sign_up_draft.expires)
25 transaction.rollback()
35 await breadcrumbs(env, _out)
37 p {'Signing up allows you to leave comments on our blog and receive communications from us.'}
39 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.'}
41 div.accordion#accordion(role="tablist" aria-multiselectable="true") {
43 div.card-header#card-1-heading(role="tab") {
44 span.icon32-outer#card-1-tick(hidden) {
45 span.icon32-inner {_out.push(icon_tick)}
47 span.icon32-outer#card-1-cross(hidden) {
48 span.icon32-inner {_out.push(icon_cross)}
50 span.icon32-outer#card-1-spinner(hidden) {
52 div.spinner-border(role="status") {}
55 a.h5(data-toggle="collapse" data-parent="#accordion" href="#card-1-collapse" aria-expanded="true" aria-controls="card-1-collapse") {
59 div#card-1-collapse.collapse.show(role="tabpanel" aria-labelledby="card-1-heading" data-parent="#accordion") {
65 label.form-label(for="given-names") {'Given names *'}
66 input.form-control#given-names(type="text" value=sign_up_draft !== null ? sign_up_draft.given_names : '' placeholder=placeholder.given_names required maxlength=256) {}
67 div.invalid-feedback {'Please enter a name we can address you by.'}
72 label.form-label(for="family-name") {'Family name'}
73 input.form-control#family-name(type="text" value=sign_up_draft !== null ? sign_up_draft.family_name : '' placeholder=placeholder.family_name maxlength=256) {}
80 label.form-label(for="email") {'Email *'}
81 input.form-control#email(type="email" value=sign_up_draft !== null ? sign_up_draft.email : '' placeholder=placeholder.email required maxlength=256) {}
82 div.invalid-feedback {'Please enter an email address we can contact you on.'}
87 label.form-label(for="password") {'Password *'}
88 input.form-control#password(type="password" placeholder="Choose" required minlength=8 maxlength=256) {}
89 div.invalid-feedback {'Please choose a secure password of at least 8 characters.'}
95 div.custom-control.custom-checkbox {
96 if (sign_up_draft === null || sign_up_draft.contact_me)
97 input.custom-control-input#contact-me(type="checkbox" checked) {}
99 input.custom-control-input#contact-me(type="checkbox") {}
101 label.custom-control-label(for="contact-me") {
102 'Contact me by email with updates and special offers'
107 div.row.align-items-center {
110 label.form-label(for="verification-code") {'Verification code *'}
111 input.form-control#verification-code(type="text" placeholder=placeholder.captcha_text required minlength=6 maxlength=6) {}
112 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.'}
115 div.'col-md-6'.my-3 {
116 img#verification-image(src="/api/verification_image.png?seq=0" width=300 height=150) {}
121 button.btn.btn-outline-secondary#'card-1-new-code'(type="button") {'New code'}
122 if (sign_up_draft !== null)
123 button.btn.btn-success.ml-3#card-1-create-account(type="button") {'Create account'}
125 button.btn.btn-success.ml-3#card-1-create-account(type="button" disabled) {'Create account'}
127 p.'mt-3'.mb-0#card-1-message(hidden) {}
132 div.card-header#card-2-heading(role="tab") {
133 span.icon32-outer#card-2-tick(hidden) {
134 span.icon32-inner {_out.push(icon_tick)}
136 span.icon32-outer#card-2-cross(hidden) {
137 span.icon32-inner {_out.push(icon_cross)}
139 span.icon32-outer#card-2-spinner(hidden) {
141 div.spinner-border(role="status") {}
144 a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
145 'Send email verification link'
148 div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="card-2-heading" data-parent="#accordion") {
150 button.btn.btn-outline-secondary#card-2-back(type="button") {'Back'}
151 button.btn.btn-outline-secondary.ml-3#card-2-resend-email(type="button" disabled) {'Re-send email'}
153 p.'mt-3'.mb-0#card-2-message(hidden) {}
159 p.mt-3 {'* These fields are required.'}
163 //script(src="/js/utils.js") {}
166 let input_semaphore = new BinarySemaphore(false)
170 await input_semaphore.acquire()
171 await new Promise(resolve => setTimeout(resolve, 3000))
172 input_semaphore.try_acquire()
174 '/api/account/sign_up/set_draft.json',
175 document.getElementById('card-1-create-account').disabled ?
178 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
179 given_names: document.getElementById('given-names').value.slice(0, 256),
180 family_name: document.getElementById('family-name').value.slice(0, 256),
181 contact_me: document.getElementById('contact-me').checked ? true : false
186 )() // ignore returned promise (start thread)
188 let card_1_edited = in_draft => {
190 input_semaphore.release()
192 document.getElementById('card-1-create-account').disabled =
193 document.getElementById('given-names').value.length === 0 &&
194 document.getElementById('family-name').value.length === 0 &&
195 document.getElementById('contact-me').checked &&
196 document.getElementById('email').value.length === 0 &&
197 document.getElementById('verification-code').value.length === 0
198 document.getElementById('card-1-tick').hidden = true
199 document.getElementById('card-1-cross').hidden = true
200 document.getElementById('card-1-spinner').hidden = true
201 document.getElementById('card-1-message').hidden = true
203 document.getElementById('card-2-resend-email').disabled = true
204 document.getElementById('card-2-tick').hidden = true
205 document.getElementById('card-2-cross').hidden = true
206 document.getElementById('card-2-spinner').hidden = true
207 document.getElementById('card-2-message').hidden = true
211 let card_1 = async () => {
212 document.getElementById('card-1-tick').hidden = true
213 document.getElementById('card-1-cross').hidden = true
214 document.getElementById('card-1-spinner').hidden = true
215 // the below causes an ugly flicker, so just keep the message
216 //document.getElementById('card-1-message').hidden = true
218 if (!document.getElementById('form').checkValidity()) {
219 document.getElementById('form').classList.add('was-validated');
220 document.getElementById('card-1-cross').hidden = false
223 document.getElementById('form').classList.remove('was-validated');
226 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
227 given_names: document.getElementById('given-names').value.slice(0, 256),
228 family_name: document.getElementById('family-name').value.slice(0, 256),
229 password: document.getElementById('password').value.slice(0, 256),
230 contact_me: document.getElementById('contact-me').checked ? true : false
233 document.getElementById('card-1-spinner').hidden = false
236 '/api/account/sign_up/create_account.json',
237 document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
242 let problem = Problem.from(error)
244 document.getElementById('card-1-cross').hidden = false
245 document.getElementById('card-1-spinner').hidden = true
247 document.getElementById('card-1-message').textContent = problem.detail
248 //document.getElementById('card-1-message').classList.remove('text-success')
249 document.getElementById('card-1-message').classList.add('text-danger')
250 document.getElementById('card-1-message').hidden = false
252 $('#card-1-collapse').collapse('show')
255 document.getElementById('card-1-tick').hidden = false
256 document.getElementById('card-1-spinner').hidden = true
257 document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
258 //document.getElementById('card-1-message').classList.add('text-success')
259 document.getElementById('card-1-message').classList.remove('text-danger')
260 document.getElementById('card-1-message').hidden = false
262 document.getElementById('card-2-resend-email').disabled = false
263 document.getElementById('card-2').scrollIntoView()
267 let card_2 = async () => {
268 document.getElementById('card-2-tick').hidden = true
269 document.getElementById('card-2-cross').hidden = true
270 document.getElementById('card-2-spinner').hidden = false
271 // the below causes an ugly flicker, so just keep the message
272 //document.getElementById('card-2-message').hidden = true
276 '/api/account/sign_up/send_email_verification_link.json',
281 let problem = Problem.from(error)
283 document.getElementById('card-2-cross').hidden = false
284 document.getElementById('card-2-spinner').hidden = true
286 document.getElementById('card-2-message').textContent = problem.detail
287 //document.getElementById('card-2-message').classList.remove('text-success')
288 document.getElementById('card-2-message').classList.add('text-danger')
289 document.getElementById('card-2-message').hidden = false
291 $('#card-2-collapse').collapse('show')
294 document.getElementById('card-2-tick').hidden = false
295 document.getElementById('card-2-spinner').hidden = true
297 document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
298 //document.getElementById('card-2-message').classList.add('text-success')
299 document.getElementById('card-2-message').classList.remove('text-danger')
300 document.getElementById('card-2-message').hidden = false
304 document.addEventListener(
307 document.getElementById('given-names').addEventListener(
309 () => {card_1_edited(true)}
311 document.getElementById('family-name').addEventListener(
313 () => {card_1_edited(true)}
315 document.getElementById('email').addEventListener(
317 () => {card_1_edited(true)}
319 document.getElementById('password').addEventListener(
321 () => {card_1_edited(false)}
323 document.getElementById('contact-me').addEventListener(
325 () => {card_1_edited(true)}
327 document.getElementById('verification-code').addEventListener(
329 () => {card_1_edited(false)}
333 document.getElementById('card-1-new-code').addEventListener(
336 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
341 document.getElementById('card-1-create-account').addEventListener(
344 if (await card_1() && await card_2())
345 $('#card-2-collapse').collapse('show')
349 document.getElementById('card-2-back').addEventListener(
351 () => {$('#card-1-collapse').collapse('show')}
354 document.getElementById('card-2-resend-email').addEventListener(
358 $('#card-2-collapse').collapse('show')