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)
16 placeholder = await get_placeholder(env, session)
17 sign_up_draft = await session.get_json('sign_up_draft')
18 if (sign_up_draft === undefined || env.now >= sign_up_draft.expires)
23 transaction.rollback()
33 await breadcrumbs(env, _out)
35 p {'Signing up allows you to leave comments on our blog and receive communications from us.'}
37 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.'}
39 div.accordion#accordion(role="tablist" aria-multiselectable="true") {
41 div.card-header#card-1-heading(role="tab") {
42 span#card-1-tick(style="display: none;") {
43 span.icon-color.pr-3 {_out.push(icon_tick)}
45 span#card-1-cross(style="display: none;") {
46 span.icon-color.pr-3 {_out.push(icon_cross)}
48 //span#card-1-spinner(style="display: none;") {
49 // span.icon-color.pr-3 {
50 // div.spinner-border(role="status") {
51 // span.sr-only {'Loading...'}
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.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.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.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.contact_me !== false)
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 button.btn.btn-success.ml-3#card-1-create-account(type="button") {'Create account'}
124 p.'mt-3'.mb-0#card-1-message(style="display: none;") {}
129 div.card-header#card-2-heading(role="tab") {
130 span#card-2-tick(style="display: none;") {
131 span.icon-color.pr-3 {_out.push(icon_tick)}
133 span#card-2-cross(style="display: none;") {
134 span.icon-color.pr-3 {_out.push(icon_cross)}
136 span#card-2-spinner(style="display: none;") {
137 span.icon-color.pr-3 {
138 div.spinner-border(role="status") {
139 span.sr-only {'Loading...'}
143 a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
144 'Send email verification link'
147 div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="card-2-heading" data-parent="#accordion") {
149 button.btn.btn-outline-secondary#card-2-back(type="button") {'Back'}
150 button.btn.btn-outline-secondary.ml-3#card-2-resend-email(type="button") {'Re-send email'}
152 p.'mt-3'.mb-0#card-2-message(style="display: none;") {}
158 p.mt-3 {'* These fields are required.'}
162 //script(src="/js/utils.js") {}
165 let input_semaphore = new BinarySemaphore(false)
169 await input_semaphore.acquire()
170 await new Promise(resolve => setTimeout(resolve, 3000))
171 input_semaphore.try_acquire()
173 '/api/account/sign_up/set_draft.json',
175 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
176 given_names: document.getElementById('given-names').value.slice(0, 256),
177 family_name: document.getElementById('family-name').value.slice(0, 256),
178 contact_me: document.getElementById('contact-me').checked ? true : false
183 )() // ignore returned promise (start thread)
186 let card_1 = async () => {
187 if (!document.getElementById('form').checkValidity()) {
188 document.getElementById('form').classList.add('was-validated');
189 $('#card-1-tick').hide()
190 $('#card-1-cross').show()
191 //$('#card-1-spinner').hide()
194 document.getElementById('form').classList.remove('was-validated');
197 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
198 given_names: document.getElementById('given-names').value.slice(0, 256),
199 family_name: document.getElementById('family-name').value.slice(0, 256),
200 password: document.getElementById('password').value.slice(0, 256),
201 contact_me: document.getElementById('contact-me').checked ? true : false
206 '/api/account/sign_up/create_account.json',
207 document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
212 let problem = Problem.from(error)
214 $('#card-1-tick').hide()
215 $('#card-1-cross').show()
216 $('#card-1-spinner').hide()
218 document.getElementById('card-1-message').textContent = problem.detail
219 //document.getElementById('card-1-message').classList.remove('text-success')
220 document.getElementById('card-1-message').classList.add('text-danger')
221 $('#card-1-message').show()
223 $('#card-1-collapse').collapse('show')
226 $('#card-1-tick').show()
227 $('#card-1-cross').hide()
228 $('#card-1-spinner').hide()
229 document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
230 //document.getElementById('card-1-message').classList.add('text-success')
231 document.getElementById('card-1-message').classList.remove('text-danger')
232 $('#card-1-message').show()
236 let card_2 = async () => {
237 $('#card-2-tick').hide()
238 $('#card-2-cross').hide()
239 $('#card-2-spinner').show()
240 document.getElementById('card-2').scrollIntoView()
244 '/api/account/sign_up/send_email_verification_link.json',
249 let problem = Problem.from(error)
251 $('#card-2-tick').hide()
252 $('#card-2-cross').show()
253 $('#card-2-spinner').hide()
255 document.getElementById('card-2-message').textContent = problem.detail
256 //document.getElementById('card-2-message').classList.remove('text-success')
257 document.getElementById('card-2-message').classList.add('text-danger')
258 $('#card-2-message').show()
260 $('#card-2-collapse').collapse('show')
263 $('#card-2-tick').show()
264 $('#card-2-cross').hide()
265 $('#card-2-spinner').hide()
267 document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
268 //document.getElementById('card-2-message').classList.add('text-success')
269 document.getElementById('card-2-message').classList.remove('text-danger')
270 $('#card-2-message').show()
274 document.addEventListener(
277 document.getElementById('given-names').addEventListener(
279 () => {input_semaphore.release()}
281 document.getElementById('family-name').addEventListener(
283 () => {input_semaphore.release()}
285 document.getElementById('email').addEventListener(
287 () => {input_semaphore.release()}
289 //document.getElementById('password').addEventListener(
291 // () => {input_semaphore.release()}
293 document.getElementById('contact-me').addEventListener(
295 () => {input_semaphore.release()}
297 //document.getElementById('verification-code').addEventListener(
299 // () => {input_semaphore.release()}
303 document.getElementById('card-1-new-code').addEventListener(
306 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
311 document.getElementById('card-1-create-account').addEventListener(
314 if (await card_1() && await card_2())
315 $('#card-2-collapse').collapse('show')
319 document.getElementById('card-2-back').addEventListener(
321 () => {$('#card-1-collapse').collapse('show')}
324 document.getElementById('card-2-resend-email').addEventListener(
328 $('#card-2-collapse').collapse('show')