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#card-1-tick(hidden) {
45 span.icon-color.pr-3 {_out.push(icon_tick)}
47 span#card-1-cross(hidden) {
48 span.icon-color.pr-3 {_out.push(icon_cross)}
50 span#card-1-spinner(hidden) {
51 span.icon-color.pr-3 {
52 div.spinner-border(role="status") {
53 span.sr-only {'Loading...'}
57 a.h5(data-toggle="collapse" data-parent="#accordion" href="#card-1-collapse" aria-expanded="true" aria-controls="card-1-collapse") {
61 div#card-1-collapse.collapse.show(role="tabpanel" aria-labelledby="card-1-heading" data-parent="#accordion") {
67 label.form-label(for="given-names") {'Given names *'}
68 input.form-control#given-names(type="text" value=sign_up_draft !== null ? sign_up_draft.given_names : '' placeholder=placeholder.given_names required maxlength=256) {}
69 div.invalid-feedback {'Please enter a name we can address you by.'}
74 label.form-label(for="family-name") {'Family name'}
75 input.form-control#family-name(type="text" value=sign_up_draft !== null ? sign_up_draft.family_name : '' placeholder=placeholder.family_name maxlength=256) {}
82 label.form-label(for="email") {'Email *'}
83 input.form-control#email(type="email" value=sign_up_draft !== null ? sign_up_draft.email : '' placeholder=placeholder.email required maxlength=256) {}
84 div.invalid-feedback {'Please enter an email address we can contact you on.'}
89 label.form-label(for="password") {'Password *'}
90 input.form-control#password(type="password" placeholder="Choose" required minlength=8 maxlength=256) {}
91 div.invalid-feedback {'Please choose a secure password of at least 8 characters.'}
97 div.custom-control.custom-checkbox {
98 if (sign_up_draft === null || sign_up_draft.contact_me)
99 input.custom-control-input#contact-me(type="checkbox" checked) {}
101 input.custom-control-input#contact-me(type="checkbox") {}
103 label.custom-control-label(for="contact-me") {
104 'Contact me by email with updates and special offers'
109 div.row.align-items-center {
112 label.form-label(for="verification-code") {'Verification code *'}
113 input.form-control#verification-code(type="text" placeholder=placeholder.captcha_text required minlength=6 maxlength=6) {}
114 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.'}
117 div.'col-md-6'.my-3 {
118 img#verification-image(src="/api/verification_image.png?seq=0" width=300 height=150) {}
123 button.btn.btn-outline-secondary#'card-1-new-code'(type="button") {'New code'}
124 if (sign_up_draft !== null)
125 button.btn.btn-success.ml-3#card-1-create-account(type="button") {'Create account'}
127 button.btn.btn-success.ml-3#card-1-create-account(type="button" disabled) {'Create account'}
129 p.'mt-3'.mb-0#card-1-message(hidden) {}
134 div.card-header#card-2-heading(role="tab") {
135 span#card-2-tick(hidden) {
136 span.icon-color.pr-3 {_out.push(icon_tick)}
138 span#card-2-cross(hidden) {
139 span.icon-color.pr-3 {_out.push(icon_cross)}
141 span#card-2-spinner(hidden) {
142 span.icon-color.pr-3 {
143 div.spinner-border(role="status") {
144 span.sr-only {'Loading...'}
148 a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
149 'Send email verification link'
152 div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="card-2-heading" data-parent="#accordion") {
154 button.btn.btn-outline-secondary#card-2-back(type="button") {'Back'}
155 button.btn.btn-outline-secondary.ml-3#card-2-resend-email(type="button" disabled) {'Re-send email'}
157 p.'mt-3'.mb-0#card-2-message(hidden) {}
163 p.mt-3 {'* These fields are required.'}
167 //script(src="/js/utils.js") {}
170 let input_semaphore = new BinarySemaphore(false)
174 await input_semaphore.acquire()
175 await new Promise(resolve => setTimeout(resolve, 3000))
176 input_semaphore.try_acquire()
178 '/api/account/sign_up/set_draft.json',
179 document.getElementById('card-1-create-account').disabled ?
182 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
183 given_names: document.getElementById('given-names').value.slice(0, 256),
184 family_name: document.getElementById('family-name').value.slice(0, 256),
185 contact_me: document.getElementById('contact-me').checked ? true : false
190 )() // ignore returned promise (start thread)
192 let card_1_edited = in_draft => {
194 input_semaphore.release()
195 document.getElementById('card-1-create-account').disabled =
196 document.getElementById('given-names').value.length === 0 &&
197 document.getElementById('family-name').value.length === 0 &&
198 document.getElementById('contact-me').checked &&
199 document.getElementById('email').value.length === 0 &&
200 document.getElementById('verification-code').value.length === 0
204 let card_1 = async () => {
205 if (!document.getElementById('form').checkValidity()) {
206 document.getElementById('form').classList.add('was-validated');
207 document.getElementById('card-1-tick').hidden = true
208 document.getElementById('card-1-cross').hidden = false
209 //document.getElementById('card-1-spinner').hidden = true
212 document.getElementById('form').classList.remove('was-validated');
215 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
216 given_names: document.getElementById('given-names').value.slice(0, 256),
217 family_name: document.getElementById('family-name').value.slice(0, 256),
218 password: document.getElementById('password').value.slice(0, 256),
219 contact_me: document.getElementById('contact-me').checked ? true : false
224 '/api/account/sign_up/create_account.json',
225 document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
230 let problem = Problem.from(error)
232 document.getElementById('card-1-tick').hidden = true
233 document.getElementById('card-1-cross').hidden = false
234 document.getElementById('card-1-spinner').hidden = true
236 document.getElementById('card-1-message').textContent = problem.detail
237 //document.getElementById('card-1-message').classList.remove('text-success')
238 document.getElementById('card-1-message').classList.add('text-danger')
239 document.getElementById('card-1-message').hidden = false
241 $('#card-1-collapse').collapse('show')
244 document.getElementById('card-1-tick').hidden = false
245 document.getElementById('card-1-cross').hidden = true
246 document.getElementById('card-1-spinner').hidden = true
247 document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
248 //document.getElementById('card-1-message').classList.add('text-success')
249 document.getElementById('card-1-message').classList.remove('text-danger')
250 document.getElementById('card-1-message').hidden = false
252 document.getElementById('card-2-resend-email').disabled = false
256 let card_2 = async () => {
257 document.getElementById('card-2-tick').hidden = true
258 document.getElementById('card-2-cross').hidden = true
259 document.getElementById('card-2-spinner').hidden = false
260 document.getElementById('card-2').scrollIntoView()
264 '/api/account/sign_up/send_email_verification_link.json',
269 let problem = Problem.from(error)
271 document.getElementById('card-2-tick').hidden = true
272 document.getElementById('card-2-cross').hidden = false
273 document.getElementById('card-2-spinner').hidden = true
275 document.getElementById('card-2-message').textContent = problem.detail
276 //document.getElementById('card-2-message').classList.remove('text-success')
277 document.getElementById('card-2-message').classList.add('text-danger')
278 document.getElementById('card-2-message').hidden = false
280 $('#card-2-collapse').collapse('show')
283 document.getElementById('card-2-tick').hidden = false
284 document.getElementById('card-2-cross').hidden = true
285 document.getElementById('card-2-spinner').hidden = true
287 document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
288 //document.getElementById('card-2-message').classList.add('text-success')
289 document.getElementById('card-2-message').classList.remove('text-danger')
290 document.getElementById('card-2-message').hidden = false
294 document.addEventListener(
297 document.getElementById('given-names').addEventListener(
299 () => {card_1_edited(true)}
301 document.getElementById('family-name').addEventListener(
303 () => {card_1_edited(true)}
305 document.getElementById('email').addEventListener(
307 () => {card_1_edited(true)}
309 document.getElementById('password').addEventListener(
311 () => {card_1_edited(false)}
313 document.getElementById('contact-me').addEventListener(
315 () => {card_1_edited(true)}
317 document.getElementById('verification-code').addEventListener(
319 () => {card_1_edited(false)}
323 document.getElementById('card-1-new-code').addEventListener(
326 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
331 document.getElementById('card-1-create-account').addEventListener(
334 if (await card_1() && await card_2())
335 $('#card-2-collapse').collapse('show')
339 document.getElementById('card-2-back').addEventListener(
341 () => {$('#card-1-collapse').collapse('show')}
344 document.getElementById('card-2-resend-email').addEventListener(
348 $('#card-2-collapse').collapse('show')