Files
kjvstudy.org/kjvstudy_org/templates/verse.html
T
kennethreitz d1fc66291a Make cross references collapsed by default on verse pages
User preference is still saved in localStorage - if expanded, stays expanded on subsequent visits.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 12:40:02 -05:00

1305 lines
39 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ book }} {{ chapter }}:{{ verse_num }} - KJV Bible{% endblock %}
{% block description %}{{ verse_text[:155] if verse_text else 'Study the Authorized King James Version Bible' }}{% endblock %}
{% block og_type %}article{% endblock %}
{% block og_title %}{{ book }} {{ chapter }}:{{ verse_num }} - KJV Bible{% endblock %}
{% block og_description %}{{ verse_text[:150] if verse_text else '' }}...{% endblock %}
{% block structured_data %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": {{ (book ~ " " ~ chapter|string ~ ":" ~ verse_num|string) | tojson }},
"articleBody": {{ verse_text | tojson }},
"author": {
"@type": "Organization",
"name": "KJV Study"
},
"publisher": {
"@type": "Organization",
"name": "KJV Study",
"url": "https://kjvstudy.org"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://kjvstudy.org/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num }}"
},
"isPartOf": {
"@type": "Book",
"name": {{ book | tojson }},
"@id": "https://kjvstudy.org/book/{{ book }}"
}
}
</script>
{% endblock %}
{% block head %}
<style>
/* Red Letter Edition - Words of Christ */
.words-of-christ {
color: #c41e3a;
}
[data-theme="dark"] .words-of-christ {
color: #ff6b6b;
}
/* Links inside red letter text should also be red */
.words-of-christ a {
color: #c41e3a;
text-decoration: underline;
}
[data-theme="dark"] .words-of-christ a {
color: #ff6b6b;
}
/* Prevent sidenotes from inheriting red color */
.words-of-christ .sidenote,
.words-of-christ .marginnote,
.words-of-christ .sidenote-number,
.words-of-christ label.sidenote-number {
color: var(--text-color);
}
[data-theme="dark"] .words-of-christ .sidenote,
[data-theme="dark"] .words-of-christ .marginnote,
[data-theme="dark"] .words-of-christ .sidenote-number,
[data-theme="dark"] .words-of-christ label.sidenote-number {
color: var(--text-color);
}
/* Prevent word definitions from inheriting red color */
.words-of-christ .word-original,
.words-of-christ .word-english,
.words-of-christ .word-definition {
color: #111;
}
.words-of-christ .word-transliteration,
.words-of-christ .word-parsing {
color: #666;
}
.words-of-christ .word-strongs {
color: #0066cc;
}
[data-theme="dark"] .words-of-christ .word-original,
[data-theme="dark"] .words-of-christ .word-english,
[data-theme="dark"] .words-of-christ .word-definition {
color: #ddd;
}
[data-theme="dark"] .words-of-christ .word-transliteration,
[data-theme="dark"] .words-of-christ .word-parsing {
color: #999;
}
[data-theme="dark"] .words-of-christ .word-strongs {
color: #6699ff;
}
.verse-text {
font-size: 1.8rem;
line-height: 2.4rem;
margin: 2rem 0;
font-style: italic;
}
.verse-reference {
color: #666;
font-size: 1.2rem;
margin-bottom: 0.5rem;
}
.share-container {
max-width: 60%;
margin: 1.5rem 0;
padding: 1rem;
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
}
/* Generic collapsible section toggle */
.section-toggle {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.5rem 0;
background: none;
border: none;
cursor: pointer;
text-align: left;
font-family: inherit;
transition: opacity 0.2s;
}
.section-toggle:hover {
opacity: 0.7;
}
.section-toggle:focus {
outline: 2px solid var(--link-color);
outline-offset: 2px;
}
.section-toggle .toggle-icon {
font-size: 0.8rem;
color: var(--text-secondary);
transition: transform 0.2s;
}
.section-heading {
font-size: 0.9rem;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
}
.share-label {
font-size: 0.9rem;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.75rem;
font-weight: 600;
}
.cross-references-section .section-heading {
font-size: 1.4rem;
text-transform: none;
letter-spacing: normal;
color: var(--text-color);
}
.share-buttons {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}
.share-btn {
padding: 0.5rem 1rem;
font-size: 0.9rem;
font-weight: 600;
border: 1px solid var(--border-color-darker);
border-radius: 4px;
background: var(--bg-color);
color: var(--text-color);
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.share-btn:hover {
background: var(--code-bg);
border-color: var(--link-color);
color: var(--link-color);
}
.share-btn.copied {
border-color: var(--link-color);
color: var(--link-color);
}
/* Cross-reference tooltip styles */
.cross-ref-link {
position: relative;
cursor: help;
}
.cross-ref-tooltip {
display: none;
position: absolute;
bottom: 100%;
left: 0;
min-width: 300px;
max-width: 400px;
padding: 0.75rem 1rem;
margin-bottom: 0.5rem;
background: var(--bg-color);
border: 1px solid var(--border-color-darker);
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
font-size: 0.9rem;
line-height: 1.6;
color: var(--text-color);
}
.cross-ref-link:hover .cross-ref-tooltip {
display: block;
}
.tooltip-verse-text {
font-weight: 600;
font-style: italic;
color: var(--text-secondary);
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid var(--border-color);
}
[data-theme="dark"] .cross-ref-tooltip {
background: var(--bg-color);
border-color: var(--border-color-darker);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
/* Interlinear styles - inline flow design */
.interlinear-container {
max-width: 100%;
margin: 2rem 0;
}
/* Collapsible toggle button */
.interlinear-toggle {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.75rem 0;
background: none;
border: none;
border-bottom: 1px solid var(--border-color);
cursor: pointer;
text-align: left;
font-family: inherit;
transition: background 0.2s;
}
.interlinear-toggle:hover {
background: var(--code-bg);
}
.interlinear-toggle:focus {
outline: 2px solid var(--link-color);
outline-offset: 2px;
}
.toggle-icon {
font-size: 0.8rem;
color: var(--text-secondary);
transition: transform 0.2s;
}
.interlinear-toggle.expanded .toggle-icon {
transform: rotate(0deg);
}
.toggle-hint {
font-size: 0.85rem;
color: var(--text-tertiary);
font-weight: normal;
}
.interlinear-heading {
font-size: 1.1rem;
font-weight: 600;
margin: 0;
color: var(--text-color);
}
.interlinear-flow {
line-height: 2.8;
font-size: 1rem;
margin: 1rem 0;
padding: 0.5rem 0;
}
.word-unit {
display: inline-block;
text-align: center;
margin: 0 0.05rem 0.5rem 0.05rem;
vertical-align: top;
cursor: pointer;
position: relative;
padding: 0.25rem 0.3rem;
border-radius: 4px;
transition: all 0.2s ease;
min-width: auto;
border: 1px solid transparent;
}
.word-unit:hover {
background: #f8f9fa;
border-color: #e0e0e0;
transform: translateY(-2px);
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.word-unit.expanded {
background: #f0f7f4;
border-color: #4a7c59;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(74,124,89,0.15);
z-index: 101;
}
.word-original {
display: block;
font-size: 2rem;
font-weight: 400;
color: #222;
margin-bottom: 0.35rem;
line-height: 1.4;
transition: color 0.2s;
letter-spacing: 0.02em;
}
/* Hebrew text (RTL) */
.word-original.hebrew {
direction: rtl;
font-family: "SBL Hebrew", "Ezra SIL", "Times New Roman", "Noto Serif Hebrew", serif;
font-size: 2.2rem;
letter-spacing: 0.04em;
}
/* Greek text */
.word-original.greek {
font-family: "SBL Greek", "Gentium Plus", "Times New Roman", "Noto Serif", serif;
font-size: 2rem;
letter-spacing: 0.03em;
}
.word-unit:hover .word-original {
color: #111;
}
.word-english {
display: block;
font-size: 0.85rem;
color: #4a7c59;
font-weight: 600;
line-height: 1.3;
}
.word-strongs {
display: block;
font-size: 0.65rem;
color: #999;
font-family: monospace;
margin-top: 0.2rem;
opacity: 0.7;
transition: opacity 0.2s;
}
.word-unit:hover .word-strongs {
opacity: 1;
}
.word-strongs a {
color: inherit;
text-decoration: none;
}
.word-strongs a:hover {
color: #4a7c59;
}
/* Hover tooltip - full definition preview */
.word-tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(-4px);
background: #333;
color: #fff;
padding: 0.65rem 1rem;
border-radius: 4px;
font-size: 0.95rem;
white-space: normal;
max-width: 380px;
min-width: 200px;
text-align: center;
line-height: 1.5;
opacity: 0;
pointer-events: none;
transition: opacity 0.15s, transform 0.15s;
z-index: 999999999;
}
.word-tooltip::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 5px solid transparent;
border-top-color: #333;
}
.word-unit:hover .word-tooltip {
opacity: 1;
transform: translateX(-50%) translateY(-8px);
}
.word-unit.expanded .word-tooltip {
opacity: 0;
}
/* Full detail panel */
.word-detail {
display: none;
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%) translateY(8px);
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem 1.25rem;
min-width: 260px;
max-width: 320px;
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
z-index: 100;
text-align: left;
line-height: 1.5;
opacity: 0;
transition: opacity 0.2s, transform 0.2s;
}
.word-unit.expanded .word-detail {
display: block;
opacity: 1;
transform: translateX(-50%) translateY(12px);
}
.word-detail-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.75rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid #eee;
}
.word-detail-original {
font-size: 2.2rem;
font-weight: 400;
color: #222;
line-height: 1.3;
}
.word-detail-original.hebrew {
direction: rtl;
font-family: "SBL Hebrew", "Ezra SIL", "Times New Roman", "Noto Serif Hebrew", serif;
font-size: 2.4rem;
}
.word-detail-original.greek {
font-family: "SBL Greek", "Gentium Plus", "Times New Roman", "Noto Serif", serif;
font-size: 2.2rem;
}
.word-detail-english {
font-size: 1rem;
color: #4a7c59;
font-weight: 600;
}
.word-detail-row {
display: flex;
margin-bottom: 0.35rem;
font-size: 0.85rem;
}
.word-detail-row:last-child {
margin-bottom: 0;
}
.word-detail-label {
color: #888;
min-width: 85px;
flex-shrink: 0;
}
.word-detail-value {
color: #333;
}
.word-detail-definition {
margin-top: 0.75rem;
padding-top: 0.75rem;
border-top: 1px solid #eee;
font-size: 0.85rem;
color: #555;
line-height: 1.6;
}
.interlinear-link {
display: inline-block;
margin-top: 0.5rem;
font-size: 0.9rem;
color: #4a7c59;
text-decoration: none;
}
.interlinear-link:hover {
text-decoration: underline;
}
/* Dark mode for interlinear */
[data-theme="dark"] .word-original {
color: #eee;
}
[data-theme="dark"] .word-unit:hover .word-original {
color: #fff;
}
[data-theme="dark"] .word-unit:hover {
background: #2a2a2a;
border-color: #444;
}
[data-theme="dark"] .word-unit.expanded {
background: #1a2e1a;
border-color: #4a7c59;
}
[data-theme="dark"] .word-detail {
background: #222;
border-color: #444;
}
[data-theme="dark"] .word-detail-original {
color: #eee;
}
[data-theme="dark"] .word-detail-value {
color: #ddd;
}
[data-theme="dark"] .word-detail-definition {
color: #bbb;
border-top-color: #444;
}
[data-theme="dark"] .word-detail-header {
border-bottom-color: #444;
}
[data-theme="dark"] .interlinear-heading {
color: #ddd;
border-bottom-color: #444;
}
/* Only apply OS dark mode when explicit theme is not set to light */
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) .word-original {
color: #eee;
}
html:not([data-theme="light"]) .word-unit:hover .word-original {
color: #fff;
}
html:not([data-theme="light"]) .word-unit:hover {
background: #2a2a2a;
border-color: #444;
}
html:not([data-theme="light"]) .word-unit.expanded {
background: #1a2e1a;
border-color: #4a7c59;
}
html:not([data-theme="light"]) .word-detail {
background: #222;
border-color: #444;
}
html:not([data-theme="light"]) .word-detail-original {
color: #eee;
}
html:not([data-theme="light"]) .word-detail-value {
color: #ddd;
}
html:not([data-theme="light"]) .word-detail-definition {
color: #bbb;
border-top-color: #444;
}
html:not([data-theme="light"]) .word-detail-header {
border-bottom-color: #444;
}
html:not([data-theme="light"]) .interlinear-heading {
color: #ddd;
border-bottom-color: #444;
}
}
@media (max-width: 768px) {
.interlinear-flow {
line-height: 4.5;
}
.word-unit {
margin: 0 0.2rem 1.5rem 0.2rem;
min-width: 50px;
padding: 0.5rem 0.6rem;
}
.word-original {
font-size: 1.8rem;
}
.word-original.hebrew {
font-size: 2rem;
}
.word-original.greek {
font-size: 1.8rem;
}
.word-tooltip {
display: none;
}
.word-detail {
position: fixed;
top: auto;
bottom: 0;
left: 0;
right: 0;
transform: none;
max-width: none;
border-radius: 16px 16px 0 0;
padding: 1.5rem;
box-shadow: 0 -4px 24px rgba(0,0,0,0.15);
}
.word-unit.expanded .word-detail {
transform: none;
}
}
@media print {
.share-container,
nav {
display: none !important;
}
.verse-text {
max-width: 100% !important;
}
section {
page-break-inside: avoid;
}
.sidenote,
.marginnote {
display: block;
float: none;
width: 100%;
margin: 0.5rem 0;
font-size: 0.9rem;
color: var(--text-color);
}
.interlinear-container,
.word-tooltip,
.word-detail {
display: none !important;
}
}
</style>
{% endblock %}
{% block content %}
<h1>{{ book }} {{ chapter }}:{{ verse_num }}</h1>
<p class="subtitle"><a href="/books">Authorized King James Version</a></p>
<section>
<p class="verse-text">{{ verse_text | format_lists | red_letter(book, chapter, verse_num) | link_names | safe }}</p>
{% if interlinear_words %}
<section class="interlinear-container" id="interlinear">
<button class="interlinear-toggle expanded" onclick="toggleInterlinear()" aria-expanded="true" aria-controls="interlinear-content">
<span class="toggle-icon"></span>
<span class="interlinear-heading">Original Language Analysis</span>
<span class="toggle-hint">({{ interlinear_words|length }} words)</span>
</button>
<div class="interlinear-flow" id="interlinear-content">
{% for word in interlinear_words %}
<div class="word-unit" onclick="toggleWord(this)">
<span class="word-original {% if is_old_testament %}hebrew{% else %}greek{% endif %}">{{ word.original }}</span>
<span class="word-english">{{ word.english }}</span>
<span class="word-strongs">
{% if word.strongs %}
<a href="/strongs/{{ word.strongs }}" onclick="event.stopPropagation()">{{ word.strongs }}</a>
{% endif %}
</span>
<div class="word-detail">
<div class="word-detail-header">
<span class="word-detail-original {% if is_old_testament %}hebrew{% else %}greek{% endif %}">{{ word.original }}</span>
<span class="word-detail-english">{{ word.english }}</span>
</div>
{% if word.transliteration %}
<div class="word-detail-row">
<span class="word-detail-label">Pronunciation:</span>
<span class="word-detail-value">{{ word.transliteration }}</span>
</div>
{% endif %}
<div class="word-detail-row">
<span class="word-detail-label">Strong's:</span>
<span class="word-detail-value">
{% if word.strongs %}
<a href="/strongs/{{ word.strongs }}" style="color: #4a7c59;">{{ word.strongs }}</a>
{% else %}
{% endif %}
</span>
</div>
{% if word.parsing %}
<div class="word-detail-row">
<span class="word-detail-label">Grammar:</span>
<span class="word-detail-value">{{ word.parsing }}</span>
</div>
{% endif %}
<div class="word-detail-row">
<span class="word-detail-label">Word #:</span>
<span class="word-detail-value">{{ word.position }} of {{ interlinear_words|length }}</span>
</div>
{% if word.definition %}
<div class="word-detail-definition">
{{ word.definition }}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</section>
<script>
function toggleInterlinear() {
var content = document.getElementById('interlinear-content');
var toggle = document.querySelector('.interlinear-toggle');
var icon = toggle.querySelector('.toggle-icon');
var isExpanded = toggle.getAttribute('aria-expanded') === 'true';
if (isExpanded) {
content.hidden = true;
toggle.setAttribute('aria-expanded', 'false');
icon.textContent = '▶';
toggle.classList.remove('expanded');
localStorage.setItem('interlinear-expanded', 'false');
} else {
content.hidden = false;
toggle.setAttribute('aria-expanded', 'true');
icon.textContent = '▼';
toggle.classList.add('expanded');
localStorage.setItem('interlinear-expanded', 'true');
}
}
// Apply user preference on load
(function() {
var pref = localStorage.getItem('interlinear-expanded');
if (pref === 'false') {
var content = document.getElementById('interlinear-content');
var toggle = document.querySelector('.interlinear-toggle');
var icon = toggle.querySelector('.toggle-icon');
content.hidden = true;
toggle.setAttribute('aria-expanded', 'false');
icon.textContent = '▶';
toggle.classList.remove('expanded');
}
})();
function toggleWord(el) {
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
if (word !== el) word.classList.remove('expanded');
});
el.classList.toggle('expanded');
}
document.addEventListener('click', function(e) {
if (!e.target.closest('.word-unit')) {
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
word.classList.remove('expanded');
});
}
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
word.classList.remove('expanded');
});
}
});
</script>
{% endif %}
<nav style="margin: 1.5rem 0;">
<p>
<a href="/book/{{ book }}">← {{ book }}</a> |
<a href="/book/{{ book }}/chapter/{{ chapter }}#verse-{{ verse_num }}">View in Chapter {{ chapter }}</a>
{% if verse_num > 1 %} | <a href="/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num - 1 }}">← Verse {{ verse_num - 1 }}</a>{% endif %}
{% if verse_num < total_verses %} | <a href="/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num + 1 }}">Verse {{ verse_num + 1 }} →</a>{% endif %}
</p>
</nav>
<div class="share-container">
<div class="share-label">Share This Verse</div>
<div class="share-buttons">
<button class="share-btn" onclick="copyToClipboard()" id="copy-btn">
<span>📋</span> Copy
</button>
<a class="share-btn twitter"
href="https://twitter.com/intent/tweet?text={{ verse_text | striptags | urlencode }}%20—%20{{ book }}%20{{ chapter }}:{{ verse_num }}%20(KJV)&url={{ request.url }}"
target="_blank"
rel="noopener">
<span>🐦</span> Tweet
</a>
<a class="share-btn facebook"
href="https://www.facebook.com/sharer/sharer.php?u={{ request.url }}"
target="_blank"
rel="noopener">
<span>f</span> Share
</a>
{% if pdf_available and pdf_url %}
<a class="share-btn pdf" href="{{ pdf_url }}">
<span>📄</span> PDF
</a>
{% endif %}
</div>
</div>
<script>
function copyToClipboard() {
const verseText = {{ verse_text | striptags | tojson }};
const reference = "{{ book }} {{ chapter }}:{{ verse_num }} (KJV)";
const fullText = verseText + " — " + reference;
navigator.clipboard.writeText(fullText).then(function() {
const btn = document.getElementById('copy-btn');
const originalHTML = btn.innerHTML;
btn.classList.add('copied');
btn.innerHTML = '<span>✓</span> Copied!';
setTimeout(function() {
btn.classList.remove('copied');
btn.innerHTML = originalHTML;
}, 2000);
}, function(err) {
alert('Failed to copy: ' + err);
});
}
function toggleSection(sectionId) {
var contentId = sectionId + '-content';
var content = document.getElementById(contentId);
var toggle = content.previousElementSibling;
while (toggle && !toggle.classList.contains('section-toggle')) {
toggle = toggle.previousElementSibling;
}
if (!toggle) return;
var icon = toggle.querySelector('.toggle-icon');
var isExpanded = toggle.getAttribute('aria-expanded') === 'true';
if (isExpanded) {
content.hidden = true;
toggle.setAttribute('aria-expanded', 'false');
icon.textContent = '▶';
toggle.classList.remove('expanded');
localStorage.setItem(sectionId + '-expanded', 'false');
} else {
content.hidden = false;
toggle.setAttribute('aria-expanded', 'true');
icon.textContent = '▼';
toggle.classList.add('expanded');
localStorage.setItem(sectionId + '-expanded', 'true');
}
}
</script>
</section>
{% if cross_references %}
<div class="cross-references-section" id="cross-references">
<button class="section-toggle" onclick="toggleSection('crossrefs')" aria-expanded="false" aria-controls="crossrefs-content">
<span class="toggle-icon"></span>
<span class="section-heading">Cross References</span>
<span class="toggle-hint">({{ cross_references|length }} references)</span>
</button>
<div id="crossrefs-content" hidden>
<p>Related verses that illuminate this passage:</p>
<ul style="max-width: 65%; list-style: none; padding: 0;">
{% for ref in cross_references %}
<li style="margin: 1.25rem 0; padding-left: 1.5rem; border-left: 2px solid var(--border-color-dark);">
{% set ref_parts = ref.ref.rsplit(' ', 1) %}
{% if ref_parts|length == 2 %}
{% set book_name = ref_parts[0] %}
{% set chapter_verse = ref_parts[1] %}
{% if ':' in chapter_verse %}
{% set ch = chapter_verse.split(':')[0] %}
{% set v = chapter_verse.split(':')[1] %}
<strong><a href="/book/{{ book_name }}/chapter/{{ ch }}/verse/{{ v }}">{{ ref.ref }}</a></strong>
{% else %}
<strong>{{ ref.ref }}</strong>
{% endif %}
{% else %}
<strong>{{ ref.ref }}</strong>
{% endif %}
{% if ref.note %} — <em style="color: var(--text-secondary);">{{ ref.note }}</em>{% endif %}
{% if ref.text %}
<p style="margin: 0.5rem 0 0 0; font-style: italic; color: var(--text-secondary); line-height: 1.6;">{{ ref.text }}</p>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
<script>
(function() {
var pref = localStorage.getItem('crossrefs-expanded');
if (pref === 'true') {
var content = document.getElementById('crossrefs-content');
var toggle = document.querySelector('.cross-references-section .section-toggle');
if (content && toggle) {
var icon = toggle.querySelector('.toggle-icon');
content.hidden = false;
toggle.setAttribute('aria-expanded', 'true');
icon.textContent = '▼';
toggle.classList.add('expanded');
}
}
})();
</script>
{% endif %}
{% if commentary %}
<div id="commentary"></div>
{% if commentary.analysis %}
<div class="commentary-section" id="analysis">
<h2>Analysis</h2>
{{ commentary.analysis|split_paragraphs|safe }}
</div>
{% endif %}
{% if commentary.historical %}
<div class="commentary-section" id="historical">
<h2>Historical Context</h2>
{{ commentary.historical|split_paragraphs|safe }}
</div>
{% endif %}
{% if commentary.theological %}
<div class="commentary-section" id="theological">
<h2>Theological Significance</h2>
{{ commentary.theological|split_paragraphs|safe }}
</div>
{% endif %}
{% if commentary.questions %}
<div class="commentary-section" id="questions">
<h2>Questions for Reflection</h2>
<ul>
{% for question in commentary.questions %}
<li>{{ question }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endif %}
<!-- Keyboard Navigation - Paragraph-based with drill-down -->
<script>
(function() {
// Collect all readable elements: paragraphs, list items, cross-ref items
function getElements() {
return Array.from(document.querySelectorAll(
'section > p.verse-text, ' +
'.interlinear-container, ' +
'#crossrefs-content > p, ' +
'.cross-references-section li, ' +
'.commentary-section p, ' +
'.commentary-section li'
)).filter(function(el) {
// Filter out empty or very short elements, and UI elements
if (el.closest('.word-detail') || el.closest('.share-container') || el.closest('nav')) return false;
// Skip elements inside collapsed sections
var hiddenParent = el.closest('[hidden]');
if (hiddenParent) return false;
return el.textContent.trim().length > 10 || el.classList.contains('interlinear-container');
});
}
var elements = getElements();
var selectedIndex = -1;
var inWordMode = false;
var selectedWordIndex = -1;
function getWords() {
var container = document.querySelector('.interlinear-container');
if (!container) return [];
return Array.from(container.querySelectorAll('.word-unit'));
}
function clearWordSelection() {
document.querySelectorAll('.word-unit.keyboard-selected').forEach(function(word) {
word.classList.remove('keyboard-selected');
word.style.outline = '';
word.style.outlineOffset = '';
});
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
word.classList.remove('expanded');
});
selectedWordIndex = -1;
inWordMode = false;
}
function clearSelection() {
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
elements[selectedIndex].classList.remove('selected');
}
clearWordSelection();
}
function selectElement(index) {
clearSelection();
// Refresh elements list in case sections were toggled
elements = getElements();
if (elements.length === 0) return;
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
elements[selectedIndex].style.outline = '2px solid #4a7c59';
elements[selectedIndex].style.outlineOffset = '4px';
elements[selectedIndex].classList.add('selected');
elements[selectedIndex].scrollIntoView({ behavior: 'auto', block: 'center' });
}
function selectWord(index) {
var words = getWords();
if (words.length === 0) return;
// Clear previous word selection
document.querySelectorAll('.word-unit.keyboard-selected').forEach(function(word) {
word.classList.remove('keyboard-selected');
word.style.outline = '';
word.style.outlineOffset = '';
});
selectedWordIndex = Math.max(0, Math.min(index, words.length - 1));
var word = words[selectedWordIndex];
word.classList.add('keyboard-selected');
word.style.outline = '2px solid #4a7c59';
word.style.outlineOffset = '2px';
word.scrollIntoView({ behavior: 'auto', block: 'center' });
}
function isInterlinearSelected() {
return selectedIndex >= 0 && elements[selectedIndex] &&
elements[selectedIndex].classList.contains('interlinear-container');
}
document.addEventListener('keydown', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
return;
}
// Don't handle if sidebar navigation is active
if (KJVNav.sidebarActive) return;
// Escape: Exit word mode or clear selection
if (e.key === 'Escape') {
e.preventDefault();
if (inWordMode) {
clearWordSelection();
// Re-highlight the interlinear container
if (isInterlinearSelected()) {
elements[selectedIndex].style.outline = '2px solid #4a7c59';
elements[selectedIndex].style.outlineOffset = '4px';
}
} else {
clearSelection();
selectedIndex = -1;
}
return;
}
// In word mode - navigate words
if (inWordMode) {
var words = getWords();
if (e.key === 'ArrowRight' || e.key === 'l') {
e.preventDefault();
selectWord(selectedWordIndex + 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
if (selectedWordIndex > 0) {
selectWord(selectedWordIndex - 1);
} else {
// Exit word mode
clearWordSelection();
if (isInterlinearSelected()) {
elements[selectedIndex].style.outline = '2px solid #4a7c59';
elements[selectedIndex].style.outlineOffset = '4px';
}
}
} else if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
// Exit word mode and go to next element
clearWordSelection();
selectElement(selectedIndex + 1);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
// Exit word mode and go to previous element
clearWordSelection();
if (selectedIndex > 0) selectElement(selectedIndex - 1);
} else if (e.key === 'Enter') {
e.preventDefault();
// Toggle word expansion
if (selectedWordIndex >= 0 && words[selectedWordIndex]) {
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
if (word !== words[selectedWordIndex]) {
word.classList.remove('expanded');
}
});
words[selectedWordIndex].classList.toggle('expanded');
}
}
return;
}
// Normal navigation mode
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
if (KJVNav.isSelectionOffScreen(elements, selectedIndex)) {
selectElement(KJVNav.findFirstVisibleIndex(elements));
} else {
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
}
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (KJVNav.isSelectionOffScreen(elements, selectedIndex)) {
selectElement(KJVNav.findFirstVisibleIndex(elements));
} else if (selectedIndex <= 0) {
selectElement(0);
} else {
selectElement(selectedIndex - 1);
}
} else if (e.key === 'Enter') {
e.preventDefault();
// If on interlinear, toggle expand or enter word mode
if (isInterlinearSelected()) {
var toggle = document.querySelector('.interlinear-toggle');
var content = document.getElementById('interlinear-content');
if (content && content.hidden) {
// First expand the section
toggleInterlinear();
} else {
// Already expanded, enter word mode
inWordMode = true;
elements[selectedIndex].style.outline = '';
selectWord(0);
}
} else if (selectedIndex >= 0) {
// If on a cross-reference, navigate to it
var link = elements[selectedIndex].querySelector('a');
if (link) window.location.href = link.href;
}
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
history.back();
} else if (e.key === 'ArrowRight' || e.key === 'l') {
e.preventDefault();
{% if verse_num < total_verses %}
window.location.href = '/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num + 1 }}';
{% endif %}
} else if (e.key === '[') {
// Previous verse
e.preventDefault();
{% if verse_num > 1 %}
window.location.href = '/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num - 1 }}';
{% endif %}
} else if (e.key === ']') {
// Next verse
e.preventDefault();
{% if verse_num < total_verses %}
window.location.href = '/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num + 1 }}';
{% endif %}
}
});
})();
</script>
{% if related_content and (related_content.topics or related_content.people or related_content.resources or related_content.stories) %}
<div style="border-top: 1px solid var(--border-color); padding-top: 2rem; margin-top: 3rem;">
<h2>Related Resources</h2>
<p style="font-size: 0.95rem; color: var(--text-secondary); margin-bottom: 1.5rem;">
Explore related topics, people, and study resources to deepen your understanding of this passage.
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1.5rem;">
{% if related_content.topics %}
<div>
<h3 style="font-size: 1rem; margin-bottom: 0.75rem; color: var(--text-secondary);">Topics</h3>
<ul style="list-style: none; padding: 0; margin: 0;">
{% for topic in related_content.topics %}
<li style="margin-bottom: 0.5rem;">
<a href="{{ topic.url }}" style="font-size: 0.95rem;">{{ topic.name }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if related_content.people %}
<div>
<h3 style="font-size: 1rem; margin-bottom: 0.75rem; color: var(--text-secondary);">People</h3>
<ul style="list-style: none; padding: 0; margin: 0;">
{% for person in related_content.people %}
<li style="margin-bottom: 0.5rem;">
<a href="{{ person.url }}" style="font-size: 0.95rem;">{{ person.name }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if related_content.resources %}
<div>
<h3 style="font-size: 1rem; margin-bottom: 0.75rem; color: var(--text-secondary);">Study Resources</h3>
<ul style="list-style: none; padding: 0; margin: 0;">
{% for resource in related_content.resources %}
<li style="margin-bottom: 0.5rem;">
<a href="{{ resource.url }}" style="font-size: 0.95rem;">{{ resource.name }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if related_content.stories %}
<div>
<h3 style="font-size: 1rem; margin-bottom: 0.75rem; color: var(--text-secondary);">Bible Stories</h3>
<ul style="list-style: none; padding: 0; margin: 0;">
{% for story in related_content.stories %}
<li style="margin-bottom: 0.5rem;">
<a href="{{ story.url }}" style="font-size: 0.95rem;">{{ story.name }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}