mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
Add universal search with dropdown to sidebar and homepage
- Add /api/universal-search endpoint that searches across books, verses, topics, stories, and reading plans - Update sidebar search with live dropdown showing categorized results - Add same dropdown functionality to homepage search - Support smart verse reference parsing (e.g., "gen 1:1" → Genesis 1:1) - Include keyboard navigation (arrow keys, Enter, Escape) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -104,6 +104,77 @@ def search_api(
|
||||
}
|
||||
|
||||
|
||||
@router.get("/universal-search")
|
||||
def universal_search_api(
|
||||
q: str = Query(..., description="Search query", examples=["love"]),
|
||||
limit: int = Query(5, description="Max results per category", examples=[5])
|
||||
):
|
||||
"""Universal search across all content types."""
|
||||
if not q or len(q.strip()) < 2:
|
||||
return {"query": q, "results": {}}
|
||||
|
||||
query = q.strip().lower()
|
||||
results = {}
|
||||
|
||||
# Search Bible books
|
||||
all_books = bible.get_books()
|
||||
matching_books = [
|
||||
{"name": book, "url": f"/book/{book}"}
|
||||
for book in all_books
|
||||
if query in book.lower()
|
||||
][:limit]
|
||||
if matching_books:
|
||||
results["books"] = matching_books
|
||||
|
||||
# Search Bible verses (limit to top results for speed)
|
||||
verse_results = perform_full_text_search(q.strip(), limit)
|
||||
if verse_results:
|
||||
results["verses"] = [
|
||||
{
|
||||
"reference": r["reference"],
|
||||
"text": r["text"][:100] + "..." if len(r.get("text", "")) > 100 else r.get("text", ""),
|
||||
"url": f"/book/{r['book']}/chapter/{r['chapter']}/verse/{r['verse']}"
|
||||
}
|
||||
for r in verse_results
|
||||
]
|
||||
|
||||
# Search topics
|
||||
all_topics = get_all_topics()
|
||||
matching_topics = [
|
||||
{"name": name.replace("_", " ").title(), "url": f"/topics/{name}"}
|
||||
for name, data in all_topics.items()
|
||||
if query in name.lower() or query in data.get("description", "").lower()
|
||||
][:limit]
|
||||
if matching_topics:
|
||||
results["topics"] = matching_topics
|
||||
|
||||
# Search stories
|
||||
all_stories = get_all_stories_flat()
|
||||
matching_stories = [
|
||||
{
|
||||
"title": s["title"],
|
||||
"url": f"/stories/{s['slug']}",
|
||||
"category": s.get("category_name", "")
|
||||
}
|
||||
for s in all_stories
|
||||
if query in s.get("title", "").lower() or query in s.get("description", "").lower()
|
||||
][:limit]
|
||||
if matching_stories:
|
||||
results["stories"] = matching_stories
|
||||
|
||||
# Search reading plans
|
||||
from ..reading_plans import READING_PLANS
|
||||
matching_plans = [
|
||||
{"name": plan["name"], "url": f"/reading-plans/{plan_id}"}
|
||||
for plan_id, plan in READING_PLANS.items()
|
||||
if query in plan["name"].lower() or query in plan.get("description", "").lower()
|
||||
][:limit]
|
||||
if matching_plans:
|
||||
results["plans"] = matching_plans
|
||||
|
||||
return {"query": q, "results": results}
|
||||
|
||||
|
||||
@router.get("/verse-of-the-day")
|
||||
def verse_of_the_day_api():
|
||||
"""API endpoint for verse of the day."""
|
||||
|
||||
@@ -463,7 +463,111 @@
|
||||
border-color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
/* Universal search dropdown */
|
||||
.search-results-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--bg-color);
|
||||
border: 1px solid var(--border-color-dark);
|
||||
border-top: none;
|
||||
border-radius: 0 0 3px 3px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-results-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-results-category {
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.search-results-category:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.search-results-category-title {
|
||||
font-size: 0.6rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--text-tertiary);
|
||||
margin-bottom: 0.3rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.search-result-item {
|
||||
display: block;
|
||||
padding: 0.3rem 0.4rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
border-bottom: none;
|
||||
border-radius: 2px;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
|
||||
.search-result-item:hover,
|
||||
.search-result-item.selected {
|
||||
background: var(--border-color);
|
||||
color: var(--link-hover);
|
||||
}
|
||||
|
||||
.search-result-item .result-title {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.search-result-item .result-meta {
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-tertiary);
|
||||
display: block;
|
||||
margin-top: 0.1rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.search-no-results {
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 0.75rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.search-loading {
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.search-view-all {
|
||||
display: block;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
font-size: 0.7rem;
|
||||
background: var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.search-view-all:hover {
|
||||
background: var(--border-color-dark);
|
||||
color: var(--link-hover);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-results-dropdown {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* Resource grouping */
|
||||
.resource-group {
|
||||
@@ -1096,9 +1200,10 @@
|
||||
|
||||
<!-- Floating Navigation Sidebar -->
|
||||
<nav class="nav-sidebar">
|
||||
<!-- Search Box -->
|
||||
<!-- Universal Search Box -->
|
||||
<div class="sidebar-search">
|
||||
<input type="text" id="sidebar-search-input" placeholder="Search..." autocomplete="off">
|
||||
<input type="text" id="sidebar-search-input" placeholder="Search everything..." autocomplete="off">
|
||||
<div id="search-results-dropdown" class="search-results-dropdown"></div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Access -->
|
||||
@@ -1234,47 +1339,213 @@
|
||||
});
|
||||
})();
|
||||
|
||||
// Sidebar search functionality
|
||||
// Universal search functionality with smart verse navigation
|
||||
(function() {
|
||||
var searchInput = document.getElementById('sidebar-search-input');
|
||||
if (!searchInput) return;
|
||||
var dropdown = document.getElementById('search-results-dropdown');
|
||||
if (!searchInput || !dropdown) return;
|
||||
|
||||
var oldTestament = document.getElementById('old-testament');
|
||||
var newTestament = document.getElementById('new-testament');
|
||||
var searchTimeout = null;
|
||||
var selectedIndex = -1;
|
||||
var currentResults = [];
|
||||
|
||||
searchInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter' && this.value.trim()) {
|
||||
window.location.href = '/search?q=' + encodeURIComponent(this.value.trim());
|
||||
// Book name mapping (same as homepage)
|
||||
var bookMap = {
|
||||
'genesis': 'Genesis', 'exodus': 'Exodus', 'leviticus': 'Leviticus', 'numbers': 'Numbers',
|
||||
'deuteronomy': 'Deuteronomy', 'joshua': 'Joshua', 'judges': 'Judges', 'ruth': 'Ruth',
|
||||
'1 samuel': '1 Samuel', '2 samuel': '2 Samuel', '1 kings': '1 Kings', '2 kings': '2 Kings',
|
||||
'1 chronicles': '1 Chronicles', '2 chronicles': '2 Chronicles', 'ezra': 'Ezra', 'nehemiah': 'Nehemiah',
|
||||
'esther': 'Esther', 'job': 'Job', 'psalms': 'Psalms', 'psalm': 'Psalms', 'proverbs': 'Proverbs',
|
||||
'ecclesiastes': 'Ecclesiastes', 'song of solomon': 'Song of Solomon', 'isaiah': 'Isaiah',
|
||||
'jeremiah': 'Jeremiah', 'lamentations': 'Lamentations', 'ezekiel': 'Ezekiel', 'daniel': 'Daniel',
|
||||
'hosea': 'Hosea', 'joel': 'Joel', 'amos': 'Amos', 'obadiah': 'Obadiah', 'jonah': 'Jonah',
|
||||
'micah': 'Micah', 'nahum': 'Nahum', 'habakkuk': 'Habakkuk', 'zephaniah': 'Zephaniah',
|
||||
'haggai': 'Haggai', 'zechariah': 'Zechariah', 'malachi': 'Malachi', 'matthew': 'Matthew',
|
||||
'mark': 'Mark', 'luke': 'Luke', 'john': 'John', 'acts': 'Acts', 'romans': 'Romans',
|
||||
'1 corinthians': '1 Corinthians', '2 corinthians': '2 Corinthians', 'galatians': 'Galatians',
|
||||
'ephesians': 'Ephesians', 'philippians': 'Philippians', 'colossians': 'Colossians',
|
||||
'1 thessalonians': '1 Thessalonians', '2 thessalonians': '2 Thessalonians',
|
||||
'1 timothy': '1 Timothy', '2 timothy': '2 Timothy', 'titus': 'Titus', 'philemon': 'Philemon',
|
||||
'hebrews': 'Hebrews', 'james': 'James', '1 peter': '1 Peter', '2 peter': '2 Peter',
|
||||
'1 john': '1 John', '2 john': '2 John', '3 john': '3 John', 'jude': 'Jude', 'revelation': 'Revelation',
|
||||
'gen': 'Genesis', 'ex': 'Exodus', 'lev': 'Leviticus', 'num': 'Numbers', 'deut': 'Deuteronomy',
|
||||
'josh': 'Joshua', 'judg': 'Judges', 'ru': 'Ruth', '1sam': '1 Samuel', '2sam': '2 Samuel',
|
||||
'1ki': '1 Kings', '2ki': '2 Kings', '1chr': '1 Chronicles', '2chr': '2 Chronicles',
|
||||
'neh': 'Nehemiah', 'est': 'Esther', 'ps': 'Psalms', 'prov': 'Proverbs', 'eccl': 'Ecclesiastes',
|
||||
'isa': 'Isaiah', 'jer': 'Jeremiah', 'lam': 'Lamentations', 'ezek': 'Ezekiel', 'dan': 'Daniel',
|
||||
'hos': 'Hosea', 'mic': 'Micah', 'hab': 'Habakkuk', 'zech': 'Zechariah', 'mal': 'Malachi',
|
||||
'matt': 'Matthew', 'mk': 'Mark', 'lk': 'Luke', 'jn': 'John', 'rom': 'Romans',
|
||||
'1cor': '1 Corinthians', '2cor': '2 Corinthians', 'gal': 'Galatians', 'eph': 'Ephesians',
|
||||
'phil': 'Philippians', 'col': 'Colossians', '1thess': '1 Thessalonians', '2thess': '2 Thessalonians',
|
||||
'1tim': '1 Timothy', '2tim': '2 Timothy', 'tit': 'Titus', 'heb': 'Hebrews', 'jas': 'James',
|
||||
'1pet': '1 Peter', '2pet': '2 Peter', '1jn': '1 John', '2jn': '2 John', '3jn': '3 John', 'rev': 'Revelation'
|
||||
};
|
||||
|
||||
function capitalizeBook(name) {
|
||||
return bookMap[name.toLowerCase()] || name;
|
||||
}
|
||||
|
||||
// Try to parse as verse reference and return URL, or null
|
||||
function parseVerseReference(input) {
|
||||
// Book Chapter:Verse
|
||||
var match = input.match(/^(.+)\s+(\d+):(\d+)$/i);
|
||||
if (match) {
|
||||
var book = capitalizeBook(match[1].trim());
|
||||
return '/book/' + encodeURIComponent(book) + '/chapter/' + match[2] + '/verse/' + match[3];
|
||||
}
|
||||
// Book Chapter
|
||||
match = input.match(/^(.+)\s+(\d+)$/i);
|
||||
if (match) {
|
||||
var book = capitalizeBook(match[1].trim());
|
||||
return '/book/' + encodeURIComponent(book) + '/chapter/' + match[2];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Category labels
|
||||
var categoryLabels = {
|
||||
books: 'Books',
|
||||
verses: 'Verses',
|
||||
topics: 'Topics',
|
||||
stories: 'Stories',
|
||||
plans: 'Reading Plans'
|
||||
};
|
||||
|
||||
// Render search results
|
||||
function renderResults(data) {
|
||||
var results = data.results;
|
||||
var html = '';
|
||||
currentResults = [];
|
||||
|
||||
// Check if query looks like a verse reference
|
||||
var verseUrl = parseVerseReference(data.query);
|
||||
if (verseUrl) {
|
||||
html += '<div class="search-results-category">';
|
||||
html += '<div class="search-results-category-title">Go to</div>';
|
||||
currentResults.push(verseUrl);
|
||||
html += '<a href="' + verseUrl + '" class="search-result-item selected">';
|
||||
html += '<span class="result-title">' + data.query + '</span>';
|
||||
html += '<span class="result-meta">Press Enter to navigate</span>';
|
||||
html += '</a></div>';
|
||||
selectedIndex = 0;
|
||||
}
|
||||
|
||||
if (Object.keys(results).length === 0 && !verseUrl) {
|
||||
html = '<div class="search-no-results">No results found</div>';
|
||||
} else {
|
||||
for (var category in results) {
|
||||
if (results[category].length > 0) {
|
||||
html += '<div class="search-results-category">';
|
||||
html += '<div class="search-results-category-title">' + (categoryLabels[category] || category) + '</div>';
|
||||
|
||||
results[category].forEach(function(item) {
|
||||
var title = item.name || item.title || item.reference;
|
||||
var meta = '';
|
||||
if (item.text) meta = item.text;
|
||||
else if (item.category) meta = item.category;
|
||||
|
||||
currentResults.push(item.url);
|
||||
html += '<a href="' + item.url + '" class="search-result-item">';
|
||||
html += '<span class="result-title">' + title + '</span>';
|
||||
if (meta) html += '<span class="result-meta">' + meta + '</span>';
|
||||
html += '</a>';
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// Add "View all results" link
|
||||
html += '<a href="/search?q=' + encodeURIComponent(data.query) + '" class="search-view-all">View all verse results →</a>';
|
||||
}
|
||||
|
||||
dropdown.innerHTML = html;
|
||||
dropdown.classList.add('show');
|
||||
if (!verseUrl) selectedIndex = -1;
|
||||
}
|
||||
|
||||
// Perform search
|
||||
function doSearch(query) {
|
||||
if (query.length < 2) {
|
||||
dropdown.classList.remove('show');
|
||||
return;
|
||||
}
|
||||
|
||||
dropdown.innerHTML = '<div class="search-loading">Searching...</div>';
|
||||
dropdown.classList.add('show');
|
||||
|
||||
fetch('/api/universal-search?q=' + encodeURIComponent(query) + '&limit=4')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(renderResults)
|
||||
.catch(function() {
|
||||
dropdown.innerHTML = '<div class="search-no-results">Search error</div>';
|
||||
});
|
||||
}
|
||||
|
||||
// Input handler with debounce
|
||||
searchInput.addEventListener('input', function() {
|
||||
var query = this.value.trim();
|
||||
clearTimeout(searchTimeout);
|
||||
|
||||
if (query.length < 2) {
|
||||
dropdown.classList.remove('show');
|
||||
return;
|
||||
}
|
||||
|
||||
searchTimeout = setTimeout(function() {
|
||||
doSearch(query);
|
||||
}, 150);
|
||||
});
|
||||
|
||||
// Keyboard navigation
|
||||
searchInput.addEventListener('keydown', function(e) {
|
||||
var items = dropdown.querySelectorAll('.search-result-item');
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
selectedIndex = Math.min(selectedIndex + 1, items.length - 1);
|
||||
updateSelection(items);
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
selectedIndex = Math.max(selectedIndex - 1, -1);
|
||||
updateSelection(items);
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex >= 0 && currentResults[selectedIndex]) {
|
||||
window.location.href = currentResults[selectedIndex];
|
||||
} else {
|
||||
// Try verse reference first, then search
|
||||
var verseUrl = parseVerseReference(this.value.trim());
|
||||
if (verseUrl) {
|
||||
window.location.href = verseUrl;
|
||||
} else if (this.value.trim()) {
|
||||
window.location.href = '/search?q=' + encodeURIComponent(this.value.trim());
|
||||
}
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
dropdown.classList.remove('show');
|
||||
this.blur();
|
||||
}
|
||||
});
|
||||
|
||||
// Filter sidebar items as user types
|
||||
searchInput.addEventListener('input', function() {
|
||||
var query = this.value.toLowerCase();
|
||||
var sidebar = document.querySelector('.nav-sidebar');
|
||||
var links = sidebar.querySelectorAll('a');
|
||||
|
||||
// Auto-expand Bible book sections when searching
|
||||
if (query !== '') {
|
||||
if (oldTestament) oldTestament.open = true;
|
||||
if (newTestament) newTestament.open = true;
|
||||
} else {
|
||||
// Collapse when search is cleared
|
||||
if (oldTestament) oldTestament.open = false;
|
||||
if (newTestament) newTestament.open = false;
|
||||
}
|
||||
|
||||
links.forEach(function(link) {
|
||||
var text = link.textContent.toLowerCase();
|
||||
var listItem = link.closest('li');
|
||||
if (listItem) {
|
||||
if (query === '' || text.includes(query)) {
|
||||
listItem.style.display = '';
|
||||
} else {
|
||||
listItem.style.display = 'none';
|
||||
}
|
||||
}
|
||||
function updateSelection(items) {
|
||||
items.forEach(function(item, i) {
|
||||
item.classList.toggle('selected', i === selectedIndex);
|
||||
});
|
||||
}
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.sidebar-search')) {
|
||||
dropdown.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
// Reopen on focus if there's a query
|
||||
searchInput.addEventListener('focus', function() {
|
||||
if (this.value.trim().length >= 2) {
|
||||
doSearch(this.value.trim());
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
@@ -110,6 +110,75 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Search Dropdown */
|
||||
.verse-lookup {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lookup-results-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--bg-color);
|
||||
border: 1px solid var(--border-color-darker);
|
||||
border-top: none;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.lookup-results-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lookup-result-category {
|
||||
padding: 0.5rem 1rem 0.25rem;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-tertiary);
|
||||
background: var(--code-bg);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.lookup-result-item {
|
||||
display: block;
|
||||
padding: 0.6rem 1rem;
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.lookup-result-item:hover,
|
||||
.lookup-result-item.selected {
|
||||
background: var(--code-bg);
|
||||
}
|
||||
|
||||
.lookup-result-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.lookup-result-title {
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.lookup-result-subtitle {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-tertiary);
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .lookup-results-dropdown {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Navigation Links */
|
||||
.nav-links {
|
||||
text-align: center;
|
||||
@@ -266,6 +335,7 @@
|
||||
placeholder="Search scripture or jump to verse..." autocomplete="off" />
|
||||
<button type="submit" class="lookup-btn">Go</button>
|
||||
</form>
|
||||
<div id="lookup-results-dropdown" class="lookup-results-dropdown"></div>
|
||||
<div class="lookup-help">Navigate: John 3:16, Romans 8, Genesis · Search: love, faith, salvation</div>
|
||||
</section>
|
||||
|
||||
@@ -392,120 +462,307 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var bookMap = {
|
||||
'genesis': 'Genesis', 'exodus': 'Exodus', 'leviticus': 'Leviticus', 'numbers': 'Numbers',
|
||||
'deuteronomy': 'Deuteronomy', 'joshua': 'Joshua', 'judges': 'Judges', 'ruth': 'Ruth',
|
||||
'1 samuel': '1 Samuel', '2 samuel': '2 Samuel', '1 kings': '1 Kings', '2 kings': '2 Kings',
|
||||
'1 chronicles': '1 Chronicles', '2 chronicles': '2 Chronicles', 'ezra': 'Ezra', 'nehemiah': 'Nehemiah',
|
||||
'esther': 'Esther', 'job': 'Job', 'psalms': 'Psalms', 'psalm': 'Psalms', 'proverbs': 'Proverbs',
|
||||
'ecclesiastes': 'Ecclesiastes', 'song of solomon': 'Song of Solomon', 'isaiah': 'Isaiah',
|
||||
'jeremiah': 'Jeremiah', 'lamentations': 'Lamentations', 'ezekiel': 'Ezekiel', 'daniel': 'Daniel',
|
||||
'hosea': 'Hosea', 'joel': 'Joel', 'amos': 'Amos', 'obadiah': 'Obadiah', 'jonah': 'Jonah',
|
||||
'micah': 'Micah', 'nahum': 'Nahum', 'habakkuk': 'Habakkuk', 'zephaniah': 'Zephaniah',
|
||||
'haggai': 'Haggai', 'zechariah': 'Zechariah', 'malachi': 'Malachi', 'matthew': 'Matthew',
|
||||
'mark': 'Mark', 'luke': 'Luke', 'john': 'John', 'acts': 'Acts', 'romans': 'Romans',
|
||||
'1 corinthians': '1 Corinthians', '2 corinthians': '2 Corinthians', 'galatians': 'Galatians',
|
||||
'ephesians': 'Ephesians', 'philippians': 'Philippians', 'colossians': 'Colossians',
|
||||
'1 thessalonians': '1 Thessalonians', '2 thessalonians': '2 Thessalonians',
|
||||
'1 timothy': '1 Timothy', '2 timothy': '2 Timothy', 'titus': 'Titus', 'philemon': 'Philemon',
|
||||
'hebrews': 'Hebrews', 'james': 'James', '1 peter': '1 Peter', '2 peter': '2 Peter',
|
||||
'1 john': '1 John', '2 john': '2 John', '3 john': '3 John', 'jude': 'Jude', 'revelation': 'Revelation',
|
||||
'gen': 'Genesis', 'ge': 'Genesis', 'exo': 'Exodus', 'ex': 'Exodus', 'lev': 'Leviticus',
|
||||
'le': 'Leviticus', 'num': 'Numbers', 'nu': 'Numbers', 'deut': 'Deuteronomy', 'dt': 'Deuteronomy',
|
||||
'josh': 'Joshua', 'jos': 'Joshua', 'judg': 'Judges', 'jdg': 'Judges', 'ru': 'Ruth',
|
||||
'1 sam': '1 Samuel', '1sam': '1 Samuel', '1s': '1 Samuel', '2 sam': '2 Samuel', '2sam': '2 Samuel', '2s': '2 Samuel',
|
||||
'1 ki': '1 Kings', '1ki': '1 Kings', '1k': '1 Kings', '2 ki': '2 Kings', '2ki': '2 Kings', '2k': '2 Kings',
|
||||
'1 chr': '1 Chronicles', '1chr': '1 Chronicles', '1ch': '1 Chronicles', '2 chr': '2 Chronicles', '2chr': '2 Chronicles', '2ch': '2 Chronicles',
|
||||
'ezr': 'Ezra', 'neh': 'Nehemiah', 'ne': 'Nehemiah', 'est': 'Esther', 'ps': 'Psalms', 'psa': 'Psalms',
|
||||
'prov': 'Proverbs', 'pr': 'Proverbs', 'eccl': 'Ecclesiastes', 'ec': 'Ecclesiastes',
|
||||
'song': 'Song of Solomon', 'sos': 'Song of Solomon', 'ss': 'Song of Solomon',
|
||||
'isa': 'Isaiah', 'is': 'Isaiah', 'jer': 'Jeremiah', 'je': 'Jeremiah', 'lam': 'Lamentations', 'la': 'Lamentations',
|
||||
'ezek': 'Ezekiel', 'eze': 'Ezekiel', 'ezk': 'Ezekiel', 'dan': 'Daniel', 'da': 'Daniel',
|
||||
'hos': 'Hosea', 'ho': 'Hosea', 'joe': 'Joel', 'jl': 'Joel', 'am': 'Amos', 'ob': 'Obadiah',
|
||||
'jon': 'Jonah', 'mic': 'Micah', 'mi': 'Micah', 'nah': 'Nahum', 'na': 'Nahum',
|
||||
'hab': 'Habakkuk', 'hb': 'Habakkuk', 'zep': 'Zephaniah', 'zph': 'Zephaniah',
|
||||
'hag': 'Haggai', 'hg': 'Haggai', 'zech': 'Zechariah', 'zec': 'Zechariah', 'zch': 'Zechariah',
|
||||
'mal': 'Malachi', 'mat': 'Matthew', 'mt': 'Matthew', 'mar': 'Mark', 'mk': 'Mark', 'mrk': 'Mark',
|
||||
'luk': 'Luke', 'lk': 'Luke', 'joh': 'John', 'jn': 'John', 'act': 'Acts', 'ac': 'Acts',
|
||||
'rom': 'Romans', 'ro': 'Romans', '1 cor': '1 Corinthians', '1cor': '1 Corinthians', '1co': '1 Corinthians',
|
||||
'2 cor': '2 Corinthians', '2cor': '2 Corinthians', '2co': '2 Corinthians',
|
||||
'gal': 'Galatians', 'ga': 'Galatians', 'eph': 'Ephesians', 'ep': 'Ephesians',
|
||||
'phil': 'Philippians', 'php': 'Philippians', 'ph': 'Philippians',
|
||||
'col': 'Colossians', 'co': 'Colossians', '1 thess': '1 Thessalonians', '1thess': '1 Thessalonians', '1th': '1 Thessalonians',
|
||||
'2 thess': '2 Thessalonians', '2thess': '2 Thessalonians', '2th': '2 Thessalonians',
|
||||
'1 tim': '1 Timothy', '1tim': '1 Timothy', '1ti': '1 Timothy', '2 tim': '2 Timothy', '2tim': '2 Timothy', '2ti': '2 Timothy',
|
||||
'tit': 'Titus', 'ti': 'Titus', 'phm': 'Philemon', 'pm': 'Philemon',
|
||||
'heb': 'Hebrews', 'he': 'Hebrews', 'jam': 'James', 'jas': 'James', 'jm': 'James',
|
||||
'1 pet': '1 Peter', '1pet': '1 Peter', '1pe': '1 Peter', '1p': '1 Peter',
|
||||
'2 pet': '2 Peter', '2pet': '2 Peter', '2pe': '2 Peter', '2p': '2 Peter',
|
||||
'1 joh': '1 John', '1joh': '1 John', '1jn': '1 John', '2 joh': '2 John', '2joh': '2 John', '2jn': '2 John',
|
||||
'3 joh': '3 John', '3joh': '3 John', '3jn': '3 John', 'jud': 'Jude',
|
||||
'rev': 'Revelation', 're': 'Revelation'
|
||||
};
|
||||
|
||||
function capitalizeBook(bookName) {
|
||||
const bookMap = {
|
||||
'genesis': 'Genesis', 'exodus': 'Exodus', 'leviticus': 'Leviticus', 'numbers': 'Numbers',
|
||||
'deuteronomy': 'Deuteronomy', 'joshua': 'Joshua', 'judges': 'Judges', 'ruth': 'Ruth',
|
||||
'1 samuel': '1 Samuel', '2 samuel': '2 Samuel', '1 kings': '1 Kings', '2 kings': '2 Kings',
|
||||
'1 chronicles': '1 Chronicles', '2 chronicles': '2 Chronicles', 'ezra': 'Ezra', 'nehemiah': 'Nehemiah',
|
||||
'esther': 'Esther', 'job': 'Job', 'psalms': 'Psalms', 'psalm': 'Psalms', 'proverbs': 'Proverbs',
|
||||
'ecclesiastes': 'Ecclesiastes', 'song of solomon': 'Song of Solomon', 'isaiah': 'Isaiah',
|
||||
'jeremiah': 'Jeremiah', 'lamentations': 'Lamentations', 'ezekiel': 'Ezekiel', 'daniel': 'Daniel',
|
||||
'hosea': 'Hosea', 'joel': 'Joel', 'amos': 'Amos', 'obadiah': 'Obadiah', 'jonah': 'Jonah',
|
||||
'micah': 'Micah', 'nahum': 'Nahum', 'habakkuk': 'Habakkuk', 'zephaniah': 'Zephaniah',
|
||||
'haggai': 'Haggai', 'zechariah': 'Zechariah', 'malachi': 'Malachi', 'matthew': 'Matthew',
|
||||
'mark': 'Mark', 'luke': 'Luke', 'john': 'John', 'acts': 'Acts', 'romans': 'Romans',
|
||||
'1 corinthians': '1 Corinthians', '2 corinthians': '2 Corinthians', 'galatians': 'Galatians',
|
||||
'ephesians': 'Ephesians', 'philippians': 'Philippians', 'colossians': 'Colossians',
|
||||
'1 thessalonians': '1 Thessalonians', '2 thessalonians': '2 Thessalonians',
|
||||
'1 timothy': '1 Timothy', '2 timothy': '2 Timothy', 'titus': 'Titus', 'philemon': 'Philemon',
|
||||
'hebrews': 'Hebrews', 'james': 'James', '1 peter': '1 Peter', '2 peter': '2 Peter',
|
||||
'1 john': '1 John', '2 john': '2 John', '3 john': '3 John', 'jude': 'Jude', 'revelation': 'Revelation',
|
||||
'gen': 'Genesis', 'ge': 'Genesis', 'exo': 'Exodus', 'ex': 'Exodus', 'lev': 'Leviticus',
|
||||
'le': 'Leviticus', 'num': 'Numbers', 'nu': 'Numbers', 'deut': 'Deuteronomy', 'dt': 'Deuteronomy',
|
||||
'josh': 'Joshua', 'jos': 'Joshua', 'judg': 'Judges', 'jdg': 'Judges', 'ru': 'Ruth',
|
||||
'1 sam': '1 Samuel', '1sam': '1 Samuel', '1s': '1 Samuel', '2 sam': '2 Samuel', '2sam': '2 Samuel', '2s': '2 Samuel',
|
||||
'1 ki': '1 Kings', '1ki': '1 Kings', '1k': '1 Kings', '2 ki': '2 Kings', '2ki': '2 Kings', '2k': '2 Kings',
|
||||
'1 chr': '1 Chronicles', '1chr': '1 Chronicles', '1ch': '1 Chronicles', '2 chr': '2 Chronicles', '2chr': '2 Chronicles', '2ch': '2 Chronicles',
|
||||
'ezr': 'Ezra', 'neh': 'Nehemiah', 'ne': 'Nehemiah', 'est': 'Esther', 'ps': 'Psalms', 'psa': 'Psalms',
|
||||
'prov': 'Proverbs', 'pr': 'Proverbs', 'eccl': 'Ecclesiastes', 'ec': 'Ecclesiastes',
|
||||
'song': 'Song of Solomon', 'sos': 'Song of Solomon', 'ss': 'Song of Solomon',
|
||||
'isa': 'Isaiah', 'is': 'Isaiah', 'jer': 'Jeremiah', 'je': 'Jeremiah', 'lam': 'Lamentations', 'la': 'Lamentations',
|
||||
'ezek': 'Ezekiel', 'eze': 'Ezekiel', 'ezk': 'Ezekiel', 'dan': 'Daniel', 'da': 'Daniel',
|
||||
'hos': 'Hosea', 'ho': 'Hosea', 'joe': 'Joel', 'jl': 'Joel', 'am': 'Amos', 'ob': 'Obadiah',
|
||||
'jon': 'Jonah', 'mic': 'Micah', 'mi': 'Micah', 'nah': 'Nahum', 'na': 'Nahum',
|
||||
'hab': 'Habakkuk', 'hb': 'Habakkuk', 'zep': 'Zephaniah', 'zph': 'Zephaniah',
|
||||
'hag': 'Haggai', 'hg': 'Haggai', 'zech': 'Zechariah', 'zec': 'Zechariah', 'zch': 'Zechariah',
|
||||
'mal': 'Malachi', 'mat': 'Matthew', 'mt': 'Matthew', 'mar': 'Mark', 'mk': 'Mark', 'mrk': 'Mark',
|
||||
'luk': 'Luke', 'lk': 'Luke', 'joh': 'John', 'jn': 'John', 'act': 'Acts', 'ac': 'Acts',
|
||||
'rom': 'Romans', 'ro': 'Romans', '1 cor': '1 Corinthians', '1cor': '1 Corinthians', '1co': '1 Corinthians',
|
||||
'2 cor': '2 Corinthians', '2cor': '2 Corinthians', '2co': '2 Corinthians',
|
||||
'gal': 'Galatians', 'ga': 'Galatians', 'eph': 'Ephesians', 'ep': 'Ephesians',
|
||||
'phil': 'Philippians', 'php': 'Philippians', 'ph': 'Philippians',
|
||||
'col': 'Colossians', 'co': 'Colossians', '1 thess': '1 Thessalonians', '1thess': '1 Thessalonians', '1th': '1 Thessalonians',
|
||||
'2 thess': '2 Thessalonians', '2thess': '2 Thessalonians', '2th': '2 Thessalonians',
|
||||
'1 tim': '1 Timothy', '1tim': '1 Timothy', '1ti': '1 Timothy', '2 tim': '2 Timothy', '2tim': '2 Timothy', '2ti': '2 Timothy',
|
||||
'tit': 'Titus', 'ti': 'Titus', 'phm': 'Philemon', 'pm': 'Philemon',
|
||||
'heb': 'Hebrews', 'he': 'Hebrews', 'jam': 'James', 'jas': 'James', 'jm': 'James',
|
||||
'1 pet': '1 Peter', '1pet': '1 Peter', '1pe': '1 Peter', '1p': '1 Peter',
|
||||
'2 pet': '2 Peter', '2pet': '2 Peter', '2pe': '2 Peter', '2p': '2 Peter',
|
||||
'1 joh': '1 John', '1joh': '1 John', '1jn': '1 John', '2 joh': '2 John', '2joh': '2 John', '2jn': '2 John',
|
||||
'3 joh': '3 John', '3joh': '3 John', '3jn': '3 John', 'jud': 'Jude',
|
||||
'rev': 'Revelation', 're': 'Revelation'
|
||||
};
|
||||
return bookMap[bookName.toLowerCase()] || bookName;
|
||||
}
|
||||
|
||||
// Parse verse reference and return URL or null
|
||||
function parseVerseReference(input) {
|
||||
// Book Chapter:Verse
|
||||
var match = input.match(/^(.+)\s+(\d+):(\d+)$/i);
|
||||
if (match) {
|
||||
var book = capitalizeBook(match[1].trim());
|
||||
return '/book/' + encodeURIComponent(book) + '/chapter/' + match[2] + '/verse/' + match[3];
|
||||
}
|
||||
// Book Chapter
|
||||
match = input.match(/^(.+)\s+(\d+)$/i);
|
||||
if (match) {
|
||||
var book = capitalizeBook(match[1].trim());
|
||||
return '/book/' + encodeURIComponent(book) + '/chapter/' + match[2];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Format verse reference for display
|
||||
function formatVerseRef(input) {
|
||||
var match = input.match(/^(.+)\s+(\d+):(\d+)$/i);
|
||||
if (match) {
|
||||
return capitalizeBook(match[1].trim()) + ' ' + match[2] + ':' + match[3];
|
||||
}
|
||||
match = input.match(/^(.+)\s+(\d+)$/i);
|
||||
if (match) {
|
||||
return capitalizeBook(match[1].trim()) + ' ' + match[2];
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
// Dropdown state
|
||||
var lookupDropdown = null;
|
||||
var lookupInput = null;
|
||||
var currentResults = [];
|
||||
var selectedIndex = -1;
|
||||
var searchTimeout = null;
|
||||
|
||||
var categoryLabels = {
|
||||
books: 'Books',
|
||||
verses: 'Verses',
|
||||
topics: 'Topics',
|
||||
stories: 'Stories',
|
||||
plans: 'Reading Plans'
|
||||
};
|
||||
|
||||
function initDropdown() {
|
||||
lookupDropdown = document.getElementById('lookup-results-dropdown');
|
||||
lookupInput = document.getElementById('verse-lookup-input');
|
||||
|
||||
if (!lookupInput || !lookupDropdown) return;
|
||||
|
||||
lookupInput.addEventListener('input', function() {
|
||||
clearTimeout(searchTimeout);
|
||||
var query = this.value.trim();
|
||||
|
||||
if (query.length < 2) {
|
||||
hideDropdown();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a verse reference first
|
||||
var verseUrl = parseVerseReference(query);
|
||||
if (verseUrl) {
|
||||
showVerseRefResult(query, verseUrl);
|
||||
}
|
||||
|
||||
// Debounce API call
|
||||
searchTimeout = setTimeout(function() {
|
||||
performUniversalSearch(query);
|
||||
}, 200);
|
||||
});
|
||||
|
||||
lookupInput.addEventListener('keydown', function(e) {
|
||||
if (!lookupDropdown.classList.contains('show')) return;
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
selectedIndex = Math.min(selectedIndex + 1, currentResults.length - 1);
|
||||
updateSelection();
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
selectedIndex = Math.max(selectedIndex - 1, -1);
|
||||
updateSelection();
|
||||
} else if (e.key === 'Escape') {
|
||||
hideDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
// Click outside to close
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!lookupDropdown.contains(e.target) && e.target !== lookupInput) {
|
||||
hideDropdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showVerseRefResult(query, url) {
|
||||
var html = '<div class="lookup-result-category">Go to</div>';
|
||||
currentResults = [url];
|
||||
selectedIndex = 0;
|
||||
html += '<a href="' + url + '" class="lookup-result-item selected">';
|
||||
html += '<div class="lookup-result-title">' + formatVerseRef(query) + '</div>';
|
||||
html += '</a>';
|
||||
lookupDropdown.innerHTML = html;
|
||||
lookupDropdown.classList.add('show');
|
||||
}
|
||||
|
||||
function performUniversalSearch(query) {
|
||||
fetch('/api/universal-search?q=' + encodeURIComponent(query) + '&limit=5')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
renderResults(data, query);
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Search error:', err);
|
||||
});
|
||||
}
|
||||
|
||||
function renderResults(data, query) {
|
||||
var results = data.results;
|
||||
var html = '';
|
||||
var newResults = [];
|
||||
|
||||
// If it's a verse reference, show that first
|
||||
var verseUrl = parseVerseReference(query);
|
||||
if (verseUrl) {
|
||||
html += '<div class="lookup-result-category">Go to</div>';
|
||||
newResults.push(verseUrl);
|
||||
html += '<a href="' + verseUrl + '" class="lookup-result-item' + (newResults.length === 1 ? ' selected' : '') + '">';
|
||||
html += '<div class="lookup-result-title">' + formatVerseRef(query) + '</div>';
|
||||
html += '</a>';
|
||||
}
|
||||
|
||||
// Render each category
|
||||
var categories = ['books', 'verses', 'topics', 'stories', 'plans'];
|
||||
categories.forEach(function(cat) {
|
||||
if (results[cat] && results[cat].length > 0) {
|
||||
html += '<div class="lookup-result-category">' + categoryLabels[cat] + '</div>';
|
||||
results[cat].forEach(function(item) {
|
||||
var url = item.url;
|
||||
newResults.push(url);
|
||||
var isSelected = newResults.length === 1 && !verseUrl;
|
||||
html += '<a href="' + url + '" class="lookup-result-item' + (isSelected ? ' selected' : '') + '">';
|
||||
if (cat === 'verses') {
|
||||
html += '<div class="lookup-result-title">' + item.reference + '</div>';
|
||||
html += '<div class="lookup-result-subtitle">' + item.text + '</div>';
|
||||
} else if (cat === 'stories') {
|
||||
html += '<div class="lookup-result-title">' + item.title + '</div>';
|
||||
if (item.category) {
|
||||
html += '<div class="lookup-result-subtitle">' + item.category + '</div>';
|
||||
}
|
||||
} else {
|
||||
html += '<div class="lookup-result-title">' + item.name + '</div>';
|
||||
}
|
||||
html += '</a>';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
currentResults = newResults;
|
||||
selectedIndex = newResults.length > 0 ? 0 : -1;
|
||||
|
||||
if (html) {
|
||||
lookupDropdown.innerHTML = html;
|
||||
lookupDropdown.classList.add('show');
|
||||
} else {
|
||||
hideDropdown();
|
||||
}
|
||||
}
|
||||
|
||||
function updateSelection() {
|
||||
var items = lookupDropdown.querySelectorAll('.lookup-result-item');
|
||||
items.forEach(function(item, i) {
|
||||
if (i === selectedIndex) {
|
||||
item.classList.add('selected');
|
||||
} else {
|
||||
item.classList.remove('selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function hideDropdown() {
|
||||
if (lookupDropdown) {
|
||||
lookupDropdown.classList.remove('show');
|
||||
}
|
||||
currentResults = [];
|
||||
selectedIndex = -1;
|
||||
}
|
||||
|
||||
function handleSearch(event) {
|
||||
event.preventDefault();
|
||||
const input = document.getElementById('verse-lookup-input').value.trim();
|
||||
var input = document.getElementById('verse-lookup-input').value.trim();
|
||||
|
||||
if (!input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to match: Book Chapter:Verse
|
||||
let match = input.match(/^(.+)\s+(\d+):(\d+)$/i);
|
||||
if (match) {
|
||||
const book = capitalizeBook(match[1].trim());
|
||||
const chapter = match[2];
|
||||
const verse = match[3];
|
||||
window.location.href = `/book/${encodeURIComponent(book)}/chapter/${chapter}/verse/${verse}`;
|
||||
// If dropdown is showing and we have a selection, go there
|
||||
if (selectedIndex >= 0 && currentResults[selectedIndex]) {
|
||||
window.location.href = currentResults[selectedIndex];
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to match: Book Chapter
|
||||
match = input.match(/^(.+)\s+(\d+)$/i);
|
||||
if (match) {
|
||||
const book = capitalizeBook(match[1].trim());
|
||||
const chapter = match[2];
|
||||
window.location.href = `/book/${encodeURIComponent(book)}/chapter/${chapter}`;
|
||||
// Try verse reference first
|
||||
var verseUrl = parseVerseReference(input);
|
||||
if (verseUrl) {
|
||||
window.location.href = verseUrl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if input looks like a book name
|
||||
const hasNumberPrefix = /^[123]\s+/i.test(input);
|
||||
const hasMultipleWords = /\s+/.test(input);
|
||||
const commonBooks = ['genesis', 'exodus', 'leviticus', 'numbers', 'deuteronomy',
|
||||
'joshua', 'judges', 'ruth', 'samuel', 'kings', 'chronicles',
|
||||
'ezra', 'nehemiah', 'esther', 'job', 'psalms', 'proverbs',
|
||||
'ecclesiastes', 'isaiah', 'jeremiah', 'lamentations', 'ezekiel',
|
||||
'daniel', 'hosea', 'joel', 'amos', 'obadiah', 'jonah', 'micah',
|
||||
'nahum', 'habakkuk', 'zephaniah', 'haggai', 'zechariah', 'malachi',
|
||||
'matthew', 'mark', 'luke', 'john', 'acts', 'romans', 'corinthians',
|
||||
'galatians', 'ephesians', 'philippians', 'colossians', 'thessalonians',
|
||||
'timothy', 'titus', 'philemon', 'hebrews', 'james', 'peter',
|
||||
'jude', 'revelation'];
|
||||
const matchesCommonBook = commonBooks.includes(input.toLowerCase());
|
||||
var hasNumberPrefix = /^[123]\s+/i.test(input);
|
||||
var hasMultipleWords = /\s+/.test(input);
|
||||
var commonBooks = ['genesis', 'exodus', 'leviticus', 'numbers', 'deuteronomy',
|
||||
'joshua', 'judges', 'ruth', 'samuel', 'kings', 'chronicles',
|
||||
'ezra', 'nehemiah', 'esther', 'job', 'psalms', 'proverbs',
|
||||
'ecclesiastes', 'isaiah', 'jeremiah', 'lamentations', 'ezekiel',
|
||||
'daniel', 'hosea', 'joel', 'amos', 'obadiah', 'jonah', 'micah',
|
||||
'nahum', 'habakkuk', 'zephaniah', 'haggai', 'zechariah', 'malachi',
|
||||
'matthew', 'mark', 'luke', 'john', 'acts', 'romans', 'corinthians',
|
||||
'galatians', 'ephesians', 'philippians', 'colossians', 'thessalonians',
|
||||
'timothy', 'titus', 'philemon', 'hebrews', 'james', 'peter',
|
||||
'jude', 'revelation'];
|
||||
var matchesCommonBook = commonBooks.includes(input.toLowerCase());
|
||||
|
||||
if (hasNumberPrefix || hasMultipleWords || matchesCommonBook) {
|
||||
const book = capitalizeBook(input);
|
||||
window.location.href = `/book/${encodeURIComponent(book)}`;
|
||||
var book = capitalizeBook(input);
|
||||
window.location.href = '/book/' + encodeURIComponent(book);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, treat it as a search query
|
||||
window.location.href = `/search?q=${encodeURIComponent(input)}`;
|
||||
window.location.href = '/search?q=' + encodeURIComponent(input);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize on DOM ready
|
||||
document.addEventListener('DOMContentLoaded', initDropdown);
|
||||
|
||||
// Press '/' to focus the search input
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === '/' && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
||||
const activeElement = document.activeElement;
|
||||
var activeElement = document.activeElement;
|
||||
if (activeElement.tagName !== 'INPUT' && activeElement.tagName !== 'TEXTAREA') {
|
||||
e.preventDefault();
|
||||
document.getElementById('verse-lookup-input')?.focus();
|
||||
document.getElementById('verse-lookup-input').focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user