98 lines
3.3 KiB
JavaScript
98 lines
3.3 KiB
JavaScript
/* ============================================================
|
|
hipa — the family archive
|
|
|
|
The sign-in form is a placeholder. It has no backend: nothing is
|
|
validated, stored, logged, or sent anywhere. Every submit ends in the
|
|
same message. Wire it to a real endpoint before treating it as a login.
|
|
============================================================ */
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
var root = document.documentElement;
|
|
root.className += ' js';
|
|
|
|
/* -- develop sequence -------------------------------------------------
|
|
Prints come up first, staggered along the sheet. Each date stamp
|
|
flicks on after its own print has settled. */
|
|
|
|
var STAGGER = 90; // ms between prints
|
|
var LEAD = 260; // ms before the sheet starts
|
|
var SETTLE = 620; // ms a print takes to settle before its stamp lights
|
|
|
|
document.querySelectorAll('.frame').forEach(function (frame, i) {
|
|
var at = LEAD + i * STAGGER;
|
|
frame.querySelector('.frame-print').style.animationDelay = at + 'ms';
|
|
frame.querySelectorAll('.frame-stamp, .frame-run').forEach(function (el) {
|
|
el.style.animationDelay = (at + SETTLE) + 'ms';
|
|
});
|
|
});
|
|
|
|
[['.eyebrow', 0], ['.lede-title', 60], ['.lede-sub', 160],
|
|
['.gate', 220], ['.ticker', 320]].forEach(function (pair) {
|
|
var el = document.querySelector(pair[0]);
|
|
if (el) el.style.animationDelay = pair[1] + 'ms';
|
|
});
|
|
|
|
/* -- show / hide the passphrase --------------------------------------- */
|
|
|
|
var pass = document.getElementById('pass');
|
|
var reveal = document.querySelector('[data-reveal]');
|
|
|
|
reveal.addEventListener('click', function () {
|
|
var shown = pass.type === 'text';
|
|
pass.type = shown ? 'password' : 'text';
|
|
reveal.textContent = shown ? 'Show' : 'Hide';
|
|
reveal.setAttribute('aria-pressed', String(!shown));
|
|
pass.focus();
|
|
});
|
|
|
|
/* -- the gate --------------------------------------------------------- */
|
|
|
|
var form = document.getElementById('signin');
|
|
var gate = document.getElementById('gate');
|
|
var notice = document.getElementById('notice');
|
|
var unlock = form.querySelector('.unlock');
|
|
var email = document.getElementById('email');
|
|
|
|
function refuse(message) {
|
|
notice.textContent = message;
|
|
gate.classList.remove('is-refused');
|
|
void gate.offsetWidth; // restart the shake
|
|
gate.classList.add('is-refused');
|
|
}
|
|
|
|
form.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
|
|
if (!email.value.trim()) {
|
|
refuse('Enter the email address the invite was sent to.');
|
|
email.focus();
|
|
return;
|
|
}
|
|
if (!pass.value) {
|
|
refuse('Enter your passphrase to open the archive.');
|
|
pass.focus();
|
|
return;
|
|
}
|
|
|
|
unlock.classList.remove('is-firing');
|
|
void unlock.offsetWidth;
|
|
unlock.classList.add('is-firing');
|
|
notice.textContent = '';
|
|
|
|
window.setTimeout(function () {
|
|
refuse('That passphrase does not match this archive. Check the email address too.');
|
|
pass.value = '';
|
|
}, 520);
|
|
});
|
|
|
|
unlock.addEventListener('animationend', function () {
|
|
unlock.classList.remove('is-firing');
|
|
});
|
|
|
|
document.querySelector('[data-passkey]').addEventListener('click', function () {
|
|
refuse('No passkey is registered on this device. Sign in with your passphrase, then add one from Settings.');
|
|
});
|
|
})();
|