mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
c5e71d652e
- Add description blocks to verse, chapter, book, and study guide templates - Include "KJV" in descriptions for better SEO (commonly searched term) - Verse pages now show verse text in description (155 chars) - Chapter pages show book/chapter info with first verse excerpt - Book pages include book introduction excerpt when available - Homepage and other index pages have descriptive meta tags - All templates now have proper SEO-friendly meta descriptions Verified all other resource templates already had descriptions. All tests passing (268 passed, 1 skipped). 🤖 Generated with Claude Code https://claude.com/claude-code Co-Authored-By: Claude <noreply@anthropic.com>
503 lines
20 KiB
HTML
503 lines
20 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ book }} - KJV Bible{% endblock %}
|
|
|
|
{% block description %}Study the book of {{ book }} from the King James Bible (KJV). {% if book_intro and book_intro.introduction %}{{ book_intro.introduction[:110]|striptags }}...{% elif introduction %}{{ introduction[:110]|striptags }}...{% else %}Complete KJV text with chapters, commentary, and study resources.{% endif %}{% endblock %}
|
|
|
|
{% block head %}
|
|
<style>
|
|
.chapters-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>';
|
|
}
|
|
}
|
|
|
|
// Create a link for cross-chapter ranges (e.g., 2:17-3:5).
|
|
function createCrossChapterRangeLink(book, startChapter, startVerse, endChapter, endVerse, linkText) {
|
|
const normalizedBook = book.replace(/\s+/g, ' ');
|
|
return '<a href="/book/' + encodeURIComponent(normalizedBook) + '/chapter/' + startChapter + '#verse-' + startVerse + '" data-end-chapter="' + endChapter + '" data-end-verse="' + endVerse + '">' + 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 crossBookRangeRegex = new RegExp('^(' + bookPattern + ')\\s+(\\d+):(\\d+)-(\\d+):(\\d+)$', 'i');
|
|
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 cross-chapter range like "Malachi 2:17-3:5"
|
|
const crossBookRangeMatch = part.match(crossBookRangeRegex);
|
|
if (crossBookRangeMatch) {
|
|
currentBook = crossBookRangeMatch[1].replace(/\s+/g, ' ');
|
|
currentChapter = crossBookRangeMatch[4];
|
|
localChanged = true;
|
|
newParts.push(createCrossChapterRangeLink(currentBook, crossBookRangeMatch[2], crossBookRangeMatch[3], crossBookRangeMatch[4], crossBookRangeMatch[5], part));
|
|
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 cross-chapter range within the current book like "2:17-3:5"
|
|
const crossChapterRangeMatch = part.match(/^(\d+):(\d+)-(\d+):(\d+)$/);
|
|
if (crossChapterRangeMatch) {
|
|
currentBook = bookName;
|
|
currentChapter = crossChapterRangeMatch[3];
|
|
localChanged = true;
|
|
newParts.push(createCrossChapterRangeLink(currentBook, crossChapterRangeMatch[1], crossChapterRangeMatch[2], crossChapterRangeMatch[3], crossChapterRangeMatch[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 cross-chapter ranges outside parentheses
|
|
const standaloneCrossBookRangeRegex = new RegExp('(' + bookPattern + ')\\s+(\\d+):(\\d+)-(\\d+):(\\d+)', 'gi');
|
|
text = text.replace(standaloneCrossBookRangeRegex, function(match, book, startChapter, startVerse, endChapter, endVerse) {
|
|
changed = true;
|
|
return createCrossChapterRangeLink(book, startChapter, startVerse, endChapter, endVerse, match);
|
|
});
|
|
|
|
// Match standalone cross-book verse references outside parentheses (with colon)
|
|
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 cross-book chapter ranges like "Hebrews 5-7" (no colon = chapters, not verses)
|
|
const crossBookChapterRangeRegex = new RegExp('(' + bookPattern + ')\\s+(\\d+)-(\\d+)(?!:)', 'gi');
|
|
text = text.replace(crossBookChapterRangeRegex, function(match, book, chapterStart, chapterEnd) {
|
|
changed = true;
|
|
const normalizedBook = book.replace(/\s+/g, ' ');
|
|
return '<a href="/book/' + encodeURIComponent(normalizedBook) + '/chapter/' + chapterStart + '">' + match + '</a>';
|
|
});
|
|
|
|
// Match cross-book single chapter like "Hebrews 11" (no colon)
|
|
const crossBookChapterRegex = new RegExp('(' + bookPattern + ')\\s+(\\d+)(?![:\\d-])', 'gi');
|
|
text = text.replace(crossBookChapterRegex, function(match, book, chapter) {
|
|
changed = true;
|
|
const normalizedBook = book.replace(/\s+/g, ' ');
|
|
return '<a href="/book/' + encodeURIComponent(normalizedBook) + '/chapter/' + chapter + '">' + match + '</a>';
|
|
});
|
|
|
|
// Match remaining standalone cross-chapter ranges like "2:17-3:5"
|
|
text = text.replace(/(?<![>\d])(\d+):(\d+)-(\d+):(\d+)(?![<\d])/g, function(match, startChapter, startVerse, endChapter, endVerse) {
|
|
changed = true;
|
|
return createCrossChapterRangeLink(bookName, startChapter, startVerse, endChapter, endVerse, 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 class="chapters-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>
|
|
{{ book_intro.introduction|md|safe }}
|
|
</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|mdi|safe }}</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|mdi|safe }}</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|mdi|safe }}</p>
|
|
<footer>— {{ verse.reference }}{% if verse.significance %} <em>({{ verse.significance|mdi|safe }})</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>
|
|
{{ book_intro.historical_context|md|safe }}
|
|
</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>
|
|
{{ book_intro.literary_style|md|safe }}
|
|
</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 %}
|