Update web/templates/entry_add.html

This commit is contained in:
Joshua Laymon 2025-09-02 01:53:34 +00:00
parent 908e8bd03e
commit e2a2b7239d

View File

@ -112,6 +112,10 @@
@media (max-width: 860px) { @media (max-width: 860px) {
.form-row.two, .form-row.three { grid-template-columns: 1fr; } .form-row.two, .form-row.three { grid-template-columns: 1fr; }
} }
/* --- Subtle validation colors for Scripture field --- */
.scripture-valid { background-color: hsl(140 80% 92% / 0.8); transition: background-color .15s ease; }
.scripture-invalid { background-color: hsl(0 80% 94% / 0.8); transition: background-color .15s ease; }
</style> </style>
<script> <script>
@ -169,11 +173,105 @@
dateAddedEl.value = `${yyyy}-${mm}-${dd}`; dateAddedEl.value = `${yyyy}-${mm}-${dd}`;
} }
})(); })();
// ----- Prefill Entry Code with current username ----- // ----- Prefill Entry Code with current username -----
const entryCodeEl = document.getElementById("id_entry_code"); const entryCodeEl = document.getElementById("id_entry_code");
if (entryCodeEl && !entryCodeEl.value) { if (entryCodeEl && !entryCodeEl.value) {
entryCodeEl.value = "{{ request.user.username }}"; entryCodeEl.value = "{{ request.user.username }}";
} }
/* ===========================
Live Scripture Validation
=========================== */
(function () {
const el = document.getElementById("id_scripture_raw");
if (!el) return;
// Exact WOL abbreviations (case-sensitive)
const WOL = new Set([
// OT
"Ge","Ex","Le","Nu","De","Jos","Jg","Ru","1Sa","2Sa","1Ki","2Ki","1Ch","2Ch","Ezr","Ne","Es","Job","Ps","Pr","Ec","Ca","Isa","Jer","La","Eze","Da","Ho","Joe","Am","Ob","Jon","Mic","Na","Hab","Zep","Hag","Zec","Mal",
// NT
"Mt","Mr","Lu","Joh","Ac","Ro","1Co","2Co","Ga","Eph","Php","Col","1Th","2Th","1Ti","2Ti","Tit","Phm","Heb","Jas","1Pe","2Pe","1Jo","2Jo","3Jo","Jude","Re"
]);
// Normalize some gentle user quirks:
// - Allow trailing dot after the book token: "Ps." -> "Ps"
// - Allow a space after the number in numbered books: "1 Pe" -> "1Pe"
function normalizeBookToken(raw) {
let t = raw.trim();
// collapse internal multiple spaces
t = t.replace(/\s+/g, " ");
// remove trailing period on the book token
t = t.replace(/\.$/, "");
// join patterns like "1 Pe" or "2 Co" -> "1Pe" / "2Co"
t = t.replace(/^([1-3])\s+([A-Za-z]+)/, (_, n, b) => n + b);
return t;
}
// Validate one reference like:
// "Ro 12" (chapter only)
// "Ro 12:1" (single verse)
// "Ro 12:1, 3, 5-7" (lists and ranges)
// "Joh 3:16-4:2" (cross-chapter range)
// "Ps. 23" (trailing dot on book handled by normalization)
function isValidSingleRef(ref) {
const s = ref.trim();
if (!s) return false;
// Split book token from the rest (book is everything before first space)
const firstSpace = s.indexOf(" ");
if (firstSpace < 0) return false;
const rawBook = s.slice(0, firstSpace);
const book = normalizeBookToken(rawBook);
if (!WOL.has(book)) return false;
const rest = s.slice(firstSpace + 1).trim();
if (!rest) return false;
// Accept "chapter" only (e.g., "Ps 23")
if (/^\d{1,3}$/.test(rest)) return true;
// Accept "chapter:verses" where verses can be:
// - n
// - n-m
// - n, m, p-q
// and allow a cross-chapter range like 3:16-4:2
// Pattern:
// chap: ( part(, part)* )
// where part is either:
// v -> \d{1,3}
// v-v -> \d{1,3}\s*-\s*\d{1,3}
// v-ch:v2 -> \d{1,3}\s*-\s*\d{1,3}:\d{1,3}
const re = /^(\d{1,3}):\s*(\d{1,3}(?:\s*-\s*(?:\d{1,3}|\d{1,3}:\d{1,3}))?(?:\s*,\s*\d{1,3}(?:\s*-\s*(?:\d{1,3}|\d{1,3}:\d{1,3}))?)*)$/;
if (re.test(rest)) {
return true;
}
return false;
}
function validateAll() {
const raw = el.value || "";
// split by ';' as multiple references
const parts = raw.split(";").map(t => t.trim()).filter(Boolean);
if (parts.length === 0) {
el.classList.remove("scripture-valid","scripture-invalid");
return;
}
const allValid = parts.every(isValidSingleRef);
el.classList.toggle("scripture-valid", allValid);
el.classList.toggle("scripture-invalid", !allValid);
}
// Wire events
el.addEventListener("input", validateAll);
el.addEventListener("change", validateAll);
// Run once on load (in case form is prefilled)
validateAll();
})();
</script> </script>
{% endblock %} {% endblock %}