Files
kjvstudy.org/kjvstudy_org/templates/book.html
T
kennethreitz 49fb98c3ee Fix single verse links to use /verse/ path for tooltip support
Single verses now use /book/{book}/chapter/{ch}/verse/{v} format
which matches the tooltip parser pattern in base.html.
Verse ranges continue to use #verse-{start}-{end} anchor format.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 15:09:00 -05:00

451 lines
16 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ book }} - KJV Bible{% endblock %}
{% block head %}
<style>
section h2 + p a {
font-size: 1.8rem;
font-weight: 500;
}
.popular-chapter {
font-weight: bold;
}
.book-meta {
color: var(--text-secondary, #666);
font-size: 0.95rem;
margin-top: -0.5rem;
margin-bottom: 1.5rem;
}
section blockquote {
margin: 1rem 0 1.5rem;
padding-left: 1rem;
border-left: 3px solid var(--border-color, #ddd);
}
section blockquote p {
font-style: italic;
margin-bottom: 0.5rem;
}
section blockquote footer {
font-size: 0.9rem;
color: var(--text-secondary, #666);
}
section blockquote footer em {
display: block;
margin-top: 0.25rem;
font-size: 0.85rem;
}
section ul li {
margin-bottom: 0.75rem;
}
section ul li strong {
color: var(--text-color, #111);
}
.book-actions {
margin: 1.5rem 0 1.5rem;
}
.print-btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.95rem;
color: var(--text-secondary, #666);
background: var(--code-bg, #f8f8f8);
border: 1px solid var(--border-color, #ddd);
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.print-btn:hover {
background: var(--bg-color, #fff);
border-color: var(--link-color);
color: var(--link-color);
text-decoration: none;
}
.print-btn svg {
width: 16px;
height: 16px;
}
@media print {
.book-actions,
.print-btn {
display: none;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const bookName = "{{ book }}";
// Known Bible book names and abbreviations for cross-references
const bibleBooks = [
'Genesis', 'Exodus', 'Leviticus', 'Numbers', 'Deuteronomy',
'Joshua', 'Judges', 'Ruth', '1 Samuel', '2 Samuel', '1 Kings', '2 Kings',
'1 Chronicles', '2 Chronicles', 'Ezra', 'Nehemiah', 'Esther',
'Job', 'Psalms', 'Psalm', 'Proverbs', 'Ecclesiastes', 'Song of Solomon',
'Isaiah', 'Jeremiah', 'Lamentations', 'Ezekiel', 'Daniel',
'Hosea', 'Joel', 'Amos', 'Obadiah', 'Jonah', 'Micah', 'Nahum',
'Habakkuk', 'Zephaniah', 'Haggai', 'Zechariah', 'Malachi',
'Matthew', 'Mark', 'Luke', 'John', 'Acts', 'Romans',
'1 Corinthians', '2 Corinthians', 'Galatians', 'Ephesians', 'Philippians',
'Colossians', '1 Thessalonians', '2 Thessalonians', '1 Timothy', '2 Timothy',
'Titus', 'Philemon', 'Hebrews', 'James', '1 Peter', '2 Peter',
'1 John', '2 John', '3 John', 'Jude', 'Revelation'
];
// Build regex pattern for book names (escaped and sorted by length desc to match longer names first)
const bookPattern = bibleBooks
.sort((a, b) => b.length - a.length)
.map(b => b.replace(/\s+/g, '\\s+'))
.join('|');
// Function to create a link for a verse reference
// Single verses use /verse/ path (for tooltip support), ranges use #verse- anchor
function createVerseLink(book, chapter, verseStart, verseEnd, linkText) {
const normalizedBook = book.replace(/\s+/g, ' ');
if (verseEnd && verseEnd !== verseStart) {
return '<a href="/book/' + encodeURIComponent(normalizedBook) + '/chapter/' + chapter + '#verse-' + verseStart + '-' + verseEnd + '">' + linkText + '</a>';
} else {
return '<a href="/book/' + encodeURIComponent(normalizedBook) + '/chapter/' + chapter + '/verse/' + verseStart + '">' + linkText + '</a>';
}
}
// Function to link verse references in text
function linkVerseReferences(element) {
if (!element) return;
// Get all text nodes
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
const textNodes = [];
let node;
while (node = walker.nextNode()) {
textNodes.push(node);
}
textNodes.forEach(function(textNode) {
let text = textNode.textContent;
let changed = false;
// Match chapter ranges in parentheses like "(chapters 1-11)", but NOT verse references
// Only match if there's no colon (which would indicate verses)
text = text.replace(/\((?:chapters?\s+)?(\d+)(?:-(\d+))?\)(?![^(]*:)/gi, function(match, chapterStart, chapterEnd) {
// Check if this looks like a verse reference context - skip if so
if (/\d+:\d+/.test(text.substring(Math.max(0, text.indexOf(match) - 20), text.indexOf(match) + match.length + 20))) {
return match;
}
changed = true;
if (chapterEnd) {
const hasChapterWord = /chapters?\s+/i.test(match);
const prefix = hasChapterWord ? match.match(/chapters?\s+/i)[0] : '';
return '(<a href="/book/' + bookName + '/chapter/' + chapterStart + '">' + prefix + chapterStart + '-' + chapterEnd + '</a>)';
} else {
const hasChapterWord = /chapters?\s+/i.test(match);
const prefix = hasChapterWord ? match.match(/chapters?\s+/i)[0] : '';
return '(<a href="/book/' + bookName + '/chapter/' + chapterStart + '">' + prefix + chapterStart + '</a>)';
}
});
// Build cross-book regex for use in parenthetical processing
const crossBookRegex = new RegExp('^(' + bookPattern + ')\\s+(\\d+):(\\d+)(?:-(\\d+))?$', 'i');
// Process parenthetical groups containing verse references
// This handles patterns like "(Romans 5:12-21; 1 Corinthians 15:21-22, 45-49)"
// and "(27:27-29, 39-40; 48:15-16; 49:1-27)"
text = text.replace(/\(([^)]+)\)/g, function(match, inner) {
// Skip if already has anchor tags (already processed)
if (inner.includes('<a ')) return match;
// Check if this contains verse references (has colons with numbers)
if (!/\d+:\d+/.test(inner)) return match;
let currentBook = bookName;
let currentChapter = null;
let localChanged = false;
// Split by semicolons and commas, preserving delimiters
const parts = inner.split(/([;,])/);
let newParts = [];
for (let i = 0; i < parts.length; i++) {
const part = parts[i].trim();
// If it's a delimiter, keep it
if (part === ';' || part === ',') {
newParts.push(parts[i]); // Keep original spacing
continue;
}
if (!part) {
newParts.push(parts[i]);
continue;
}
// Check for cross-book reference like "Romans 5:12-21" or "1 Corinthians 15:21-22"
const crossBookMatch = part.match(crossBookRegex);
if (crossBookMatch) {
currentBook = crossBookMatch[1].replace(/\s+/g, ' ');
currentChapter = crossBookMatch[2];
localChanged = true;
newParts.push(createVerseLink(currentBook, currentChapter, crossBookMatch[3], crossBookMatch[4], part));
continue;
}
// Check for full chapter:verse-verse pattern (current book)
const fullRangeMatch = part.match(/^(\d+):(\d+)-(\d+)$/);
if (fullRangeMatch) {
currentBook = bookName;
currentChapter = fullRangeMatch[1];
localChanged = true;
newParts.push(createVerseLink(currentBook, currentChapter, fullRangeMatch[2], fullRangeMatch[3], part));
continue;
}
// Check for full chapter:verse pattern (single verse, current book)
const fullSingleMatch = part.match(/^(\d+):(\d+)$/);
if (fullSingleMatch) {
currentBook = bookName;
currentChapter = fullSingleMatch[1];
localChanged = true;
newParts.push(createVerseLink(currentBook, currentChapter, fullSingleMatch[2], null, part));
continue;
}
// Check for abbreviated verse-verse pattern (inherits book and chapter)
const abbrevRangeMatch = part.match(/^(\d+)-(\d+)$/);
if (abbrevRangeMatch && currentChapter) {
localChanged = true;
newParts.push(createVerseLink(currentBook, currentChapter, abbrevRangeMatch[1], abbrevRangeMatch[2], part));
continue;
}
// Check for abbreviated single verse (inherits book and chapter)
const abbrevSingleMatch = part.match(/^(\d+)$/);
if (abbrevSingleMatch && currentChapter && parts[i-1] && parts[i-1].trim() === ',') {
localChanged = true;
newParts.push(createVerseLink(currentBook, currentChapter, abbrevSingleMatch[1], null, part));
continue;
}
// Not a verse reference, keep as-is
newParts.push(parts[i]);
}
if (localChanged) {
changed = true;
return '(' + newParts.join('') + ')';
}
return match;
});
// Match standalone cross-book references outside parentheses
const standaloneCrossBookRegex = new RegExp('(' + bookPattern + ')\\s+(\\d+):(\\d+)(?:-(\\d+))?', 'gi');
text = text.replace(standaloneCrossBookRegex, function(match, book, chapter, verseStart, verseEnd) {
changed = true;
return createVerseLink(book, chapter, verseStart, verseEnd, match);
});
// Match remaining standalone verse ranges like "1:1-5" not in parentheses
text = text.replace(/(?<![>\d])(\d+):(\d+)-(\d+)(?![<\d])/g, function(match, chapter, verseStart, verseEnd) {
changed = true;
return createVerseLink(bookName, chapter, verseStart, verseEnd, match);
});
// Match remaining standalone single verses like "1:1" not in parentheses
text = text.replace(/(?<![>\d])(\d+):(\d+)(?![-<\d])/g, function(match, chapter, verse) {
changed = true;
return createVerseLink(bookName, chapter, verse, null, match);
});
if (changed) {
const span = document.createElement('span');
span.innerHTML = text;
textNode.parentNode.replaceChild(span, textNode);
// Replace the span's children with its contents
while (span.firstChild) {
span.parentNode.insertBefore(span.firstChild, span);
}
span.parentNode.removeChild(span);
}
});
}
// Link verse references in all sections
document.querySelectorAll('section').forEach(function(section) {
linkVerseReferences(section);
});
});
</script>
{% endblock %}
{% block content %}
<h1>{{ book }}</h1>
<p class="subtitle"><a href="/books">Authorized King James Version</a></p>
{% if book_intro %}
<p class="book-meta">
{% if book_intro.author %}<strong>Author:</strong> {{ book_intro.author }}{% endif %}
{% if book_intro.date_written %} · <strong>Written:</strong> {{ book_intro.date_written }}{% endif %}
{% if book_intro.category %} · <strong>Category:</strong> {{ book_intro.category }}{% endif %}
</p>
{% endif %}
{% if pdf_available %}
<div class="book-actions">
<a href="/book/{{ book }}/pdf" class="print-btn">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Download Book PDF
</a>
</div>
{% endif %}
<section>
<h2>Chapters</h2>
<p>
<label for="mn-popular" class="margin-toggle"></label>
<input type="checkbox" id="mn-popular" class="margin-toggle"/>
<span class="marginnote">Chapters in <strong>bold</strong> are among the most frequently read and studied passages.</span>
{% for chapter in chapters %}
<a href="/book/{{ book }}/chapter/{{ chapter }}" {% if chapter_popularity[chapter] >= 7 %}class="popular-chapter"{% endif %}>{{ chapter }}</a>{% if not loop.last %} · {% endif %}
{% endfor %}
</p>
</section>
{% if book_intro and book_intro.introduction %}
<section>
<h2>Introduction</h2>
{% for paragraph in book_intro.introduction.split('\n\n') %}
<p>{{ paragraph }}</p>
{% endfor %}
</section>
{% elif introduction %}
<section>
<h2>Introduction</h2>
{{ introduction|safe }}
</section>
{% endif %}
{% if book_intro and book_intro.outline %}
<section>
<h2>Book Outline</h2>
<ul>
{% for item in book_intro.outline %}
<li><strong>{{ item.section }}</strong> ({{ item.chapters }}) — {{ item.description }}</li>
{% endfor %}
</ul>
</section>
{% endif %}
{% if book_intro and book_intro.key_themes %}
<section>
<h2>Key Themes</h2>
<ul>
{% for theme in book_intro.key_themes %}
{% if theme is mapping %}
<li><strong>{{ theme.theme }}</strong>: {{ theme.description }}</li>
{% else %}
<li>{{ theme }}</li>
{% endif %}
{% endfor %}
</ul>
</section>
{% elif themes %}
<section>
<h2>Major Themes</h2>
{{ themes|safe }}
</section>
{% endif %}
{% if book_intro and book_intro.key_verses %}
<section>
<h2>Key Verses</h2>
{% for verse in book_intro.key_verses %}
<blockquote>
<p>{{ verse.text }}</p>
<footer>— {{ verse.reference }}{% if verse.significance %} <em>({{ verse.significance }})</em>{% endif %}</footer>
</blockquote>
{% endfor %}
</section>
{% elif key_passages %}
<section>
<h2>Key Passages</h2>
<ul>
{% for passage in key_passages %}
<li><a href="{{ passage.url }}">{{ passage.reference }}</a> — {{ passage.description }}</li>
{% endfor %}
</ul>
</section>
{% endif %}
{% if book_intro and book_intro.historical_context %}
<section>
<h2>Historical Context</h2>
{% for paragraph in book_intro.historical_context.split('\n\n') %}
<p>{{ paragraph }}</p>
{% endfor %}
</section>
{% elif historical_context %}
<section>
<h2>Historical Context</h2>
{{ historical_context|safe }}
</section>
{% endif %}
{% if book_intro and book_intro.literary_style %}
<section>
<h2>Literary Style</h2>
{% for paragraph in book_intro.literary_style.split('\n\n') %}
<p>{{ paragraph }}</p>
{% endfor %}
</section>
{% endif %}
{% if book_intro and book_intro.theological_significance %}
<section>
<h2>Theological Significance</h2>
{{ book_intro.theological_significance|md|safe }}
</section>
{% endif %}
{% if book_intro and book_intro.christ_in_book %}
<section>
<h2>Christ in {{ book }}</h2>
{{ book_intro.christ_in_book|md|safe }}
</section>
{% endif %}
{% if book_intro and book_intro.relationship_to_new_testament %}
<section>
<h2>Relationship to the New Testament</h2>
{{ book_intro.relationship_to_new_testament|md|safe }}
</section>
{% endif %}
{% if book_intro and book_intro.practical_application %}
<section>
<h2>Practical Application</h2>
{{ book_intro.practical_application|md|safe }}
</section>
{% endif %}
<nav>
<p><a href="/">← All Books</a></p>
</nav>
{% endblock %}