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()
196 document.getElementById('card-1-create-account').disabled =
197 document.getElementById('given-names').value.length === 0 &&
198 document.getElementById('family-name').value.length === 0 &&
199 document.getElementById('contact-me').checked &&
200 document.getElementById('email').value.length === 0 &&
201 document.getElementById('verification-code').value.length === 0
202 document.getElementById('card-1-message').hidden = true
204 document.getElementById('card-2-resend-email').disabled = true
205 document.getElementById('card-2-message').hidden = true
209 let card_1 = async () => {
210 document.getElementById('card-1-tick').hidden = true
211 document.getElementById('card-1-cross').hidden = true
212 document.getElementById('card-1-spinner').hidden = true
213 document.getElementById('card-1-message').hidden = true
214 document.getElementById('card-1').scrollIntoView()
216 if (!document.getElementById('form').checkValidity()) {
217 document.getElementById('form').classList.add('was-validated');
218 document.getElementById('card-1-cross').hidden = false
221 document.getElementById('form').classList.remove('was-validated');
224 email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
225 given_names: document.getElementById('given-names').value.slice(0, 256),
226 family_name: document.getElementById('family-name').value.slice(0, 256),
227 password: document.getElementById('password').value.slice(0, 256),
228 contact_me: document.getElementById('contact-me').checked ? true : false
231 document.getElementById('card-1-spinner').hidden = false
234 '/api/account/sign_up/create_account.json',
235 document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
240 let problem = Problem.from(error)
242 document.getElementById('card-1-cross').hidden = false
243 document.getElementById('card-1-spinner').hidden = true
245 document.getElementById('card-1-message').textContent = problem.detail
246 //document.getElementById('card-1-message').classList.remove('text-success')
247 document.getElementById('card-1-message').classList.add('text-danger')
248 document.getElementById('card-1-message').hidden = false
250 $('#card-1-collapse').collapse('show')
253 document.getElementById('card-1-tick').hidden = false
254 document.getElementById('card-1-spinner').hidden = true
255 document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
256 //document.getElementById('card-1-message').classList.add('text-success')
257 document.getElementById('card-1-message').classList.remove('text-danger')
258 document.getElementById('card-1-message').hidden = false
260 document.getElementById('card-2-resend-email').disabled = false
264 let card_2 = async () => {
265 document.getElementById('card-2-tick').hidden = true
266 document.getElementById('card-2-cross').hidden = true
267 document.getElementById('card-2-spinner').hidden = false
268 document.getElementById('card-2-message').hidden = true
269 document.getElementById('card-2').scrollIntoView()
273 '/api/account/sign_up/send_email_verification_link.json',
278 let problem = Problem.from(error)
280 document.getElementById('card-2-cross').hidden = false
281 document.getElementById('card-2-spinner').hidden = true
283 document.getElementById('card-2-message').textContent = problem.detail
284 //document.getElementById('card-2-message').classList.remove('text-success')
285 document.getElementById('card-2-message').classList.add('text-danger')
286 document.getElementById('card-2-message').hidden = false
288 $('#card-2-collapse').collapse('show')
291 document.getElementById('card-2-tick').hidden = false
292 document.getElementById('card-2-spinner').hidden = true
294 document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
295 //document.getElementById('card-2-message').classList.add('text-success')
296 document.getElementById('card-2-message').classList.remove('text-danger')
297 document.getElementById('card-2-message').hidden = false
301 document.addEventListener(
304 document.getElementById('given-names').addEventListener(
306 () => {card_1_edited(true)}
308 document.getElementById('family-name').addEventListener(
310 () => {card_1_edited(true)}
312 document.getElementById('email').addEventListener(
314 () => {card_1_edited(true)}
316 document.getElementById('password').addEventListener(
318 () => {card_1_edited(false)}
320 document.getElementById('contact-me').addEventListener(
322 () => {card_1_edited(true)}
324 document.getElementById('verification-code').addEventListener(
326 () => {card_1_edited(false)}
330 document.getElementById('card-1-new-code').addEventListener(
333 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
338 document.getElementById('card-1-create-account').addEventListener(
341 if (await card_1() && await card_2())
342 $('#card-2-collapse').collapse('show')
346 document.getElementById('card-2-back').addEventListener(
348 () => {$('#card-1-collapse').collapse('show')}
351 document.getElementById('card-2-resend-email').addEventListener(
355 $('#card-2-collapse').collapse('show')