recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
+171
View File
@@ -0,0 +1,171 @@
document.addEventListener('DOMContentLoaded', () => {
/* ---------- helpers ---------- */
const form = document.getElementById('registrationForm'); // <— pick the specific form
if (!form) { // <— bail out if not on this page
console.debug('validation.js: no #registrationForm — skipping init');
return;
}
const el = name => form.querySelector(`[name="${name}"]`);
const allInputs = form.querySelectorAll('input, select');
/* ---------- regex rules ---------- */
const nameRE = /^[A-Za-z\s-]{2,30}$/;
const phoneRE = /^\(?\d{3}\)?[-\s]?\d{3}[-\s]?\d{4}$/;
const emailRE = /^(?=.{2,50}$)[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
const streetRE = /^[A-Za-z0-9.\s-]{2,30}$/;
const aptRE = /^[A-Za-z0-9.\s-]{0,15}$/;
const cityRE = /^[A-Za-z0-9\s.'-]{2,15}$/;
const zipRE = /^\d{5}$/;
const captchaRE= /^[A-Za-z0-9]{4,10}$/;
const formatHints = {
firstname: "Only letters, spaces, or dashes",
lastname: "Only letters, spaces, or dashes",
email: "Format: user@example.com",
confirm_email: "Must match the email exactly",
cellphone: "Exactly 10 digits (e.g., 1234567890 , (123) 456-4789)",
address_street: "Letters, numbers, dots, dashes",
apt: "Optional - Only letters, spaces, or dashes",
city: "Letters and spaces",
zip: "Exactly 5 digits (e.g., 12345)",
captcha: "410 letters or digits"
};
const validateField = (input) => {
const v = input.value.trim();
let ok = true;
switch (input.name) {
case 'firstname':
case 'lastname':
case 'second_firstname':
case 'second_lastname':
ok = nameRE.test(v); break;
case 'cellphone':
case 'second_cellphone':
ok = phoneRE.test(v); break;
case 'email':
case 'second_email':
ok = emailRE.test(v); break;
case 'confirm_email':
ok = v === (el('email')?.value ?? ''); break; // <— guard
case 'address_street':
ok = streetRE.test(v); break;
case 'apt':
ok = !v || aptRE.test(v); break;
case 'city':
ok = cityRE.test(v); break;
case 'zip':
ok = zipRE.test(v); break;
case 'captcha':
ok = captchaRE.test(v); break;
}
input.classList.toggle('is-invalid', !ok);
return ok;
};
/* ---------- live validation & re-check confirm_email ---------- */
allInputs.forEach(inp => {
const hint = document.getElementById(`${inp.name}-hint`);
inp.addEventListener('input', () => {
if (inp.disabled) return;
validateField(inp);
// Show hint while typing
if (hint) hint.classList.remove('d-none');
// Special revalidation for confirm_email
if (inp.name === 'email') {
const conf = el('confirm_email');
if (conf && !conf.disabled) validateField(conf);
}
});
inp.addEventListener('blur', () => {
const hint = document.getElementById(`${inp.name}-hint`);
if (hint) hint.classList.add('d-none');
});
});
/* ---------- copy / paste blocking with inline warnings ---------- */
const blockPasteConfig = [
{ id: 'confirm_email', warnId: 'copy-warning-email',
msg: 'Copy/paste is disabled for this field. Please type the answer.' },
{ id: 'captcha', warnId: 'captcha-warning',
msg: 'Copy/paste is disabled for this field. Please type the answer.' }
];
blockPasteConfig.forEach(({ id, warnId, msg }) => {
const input = document.getElementById(id);
if (!input) return;
let warnBox = document.getElementById(warnId);
if (!warnBox) {
warnBox = document.createElement('div');
warnBox.id = warnId;
warnBox.className = 'invalid-feedback d-none';
warnBox.textContent = msg;
input.parentElement.appendChild(warnBox);
}
const showWarn = (e) => {
e.preventDefault();
warnBox.classList.remove('d-none');
input.classList.add('is-invalid');
setTimeout(() => {
warnBox.classList.add('d-none');
input.classList.remove('is-invalid');
}, 3000);
};
['paste', 'copy', 'cut'].forEach(evt => input.addEventListener(evt, showWarn));
});
/* ---------- second-parent toggle logic ---------- */
const isParentCheckbox = document.getElementById('is_parent');
const secondSection = document.getElementById('second-parent-section');
const noSecondToggle = document.getElementById('no_second_parent_info');
const secondParentFields = document.getElementById('second-parent-fields');
const setSecondDisabled = (dis) => {
if (!secondParentFields) return; // <— guard
secondParentFields.querySelectorAll('input, select').forEach(f => {
f.disabled = dis;
if (dis) f.classList.remove('is-invalid');
});
};
const refreshSecondUI = () => {
if (!secondSection) return; // <— guard
const parent = !!isParentCheckbox?.checked;
const noInfo = !!noSecondToggle?.checked;
secondSection.style.display = parent ? 'block' : 'none';
setSecondDisabled(!parent || noInfo);
};
isParentCheckbox?.addEventListener('change', refreshSecondUI);
noSecondToggle?.addEventListener('change', refreshSecondUI);
refreshSecondUI();
/* ---------- submit event ---------- */
form.addEventListener('submit', e => {
let ok = true;
allInputs.forEach(inp => {
if (inp.disabled) return;
const inSecond = secondParentFields && inp.closest('#second-parent-fields');
if (inSecond && (!isParentCheckbox?.checked || noSecondToggle?.checked)) return;
if (!validateField(inp)) ok = false;
});
if (!ok) {
e.preventDefault();
form.querySelector('.is-invalid')?.focus({ preventScroll: true });
}
});
});