681 lines
26 KiB
JavaScript
681 lines
26 KiB
JavaScript
// RapidAPI Complete Study Bible Integration Module
|
|||
|
|
// This module provides functions to query the Complete Study Bible API via RapidAPI
|
||
|
|
// for enhanced word information to display in popup cards
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Extract Strong's numbers from search results
|
||
|
|
*/
|
||
|
|
function extractStrongsFromSearchResult(searchResults) {
|
||
|
|
const strongsSet = new Set();
|
||
|
|
|
||
|
|
if (!Array.isArray(searchResults)) return [];
|
||
|
|
|
||
|
|
for (const result of searchResults) {
|
||
|
|
// Look for Strong's fields in various formats
|
||
|
|
if (result.strongs) {
|
||
|
|
const strongs = String(result.strongs).match(/([GH]\d+)/gi);
|
||
|
|
if (strongs) strongs.forEach(s => strongsSet.add(s.toUpperCase()));
|
||
|
|
}
|
||
|
|
if (result.strongs_greek) {
|
||
|
|
const strongs = String(result.strongs_greek).match(/(G\d+)/gi);
|
||
|
|
if (strongs) strongs.forEach(s => strongsSet.add(s.toUpperCase()));
|
||
|
|
}
|
||
|
|
if (result.strongs_hebrew) {
|
||
|
|
const strongs = String(result.strongs_hebrew).match(/(H\d+)/gi);
|
||
|
|
if (strongs) strongs.forEach(s => strongsSet.add(s.toUpperCase()));
|
||
|
|
}
|
||
|
|
// Only scan fields whose key indicates Strong's data. Scanning ALL string fields produced
|
||
|
|
// false positives (e.g. 'G20' from verse text, or 'H2' harvested from 'H2O') that were then
|
||
|
|
// sent to the strongs-detail endpoint and surfaced as unrelated lexical entries.
|
||
|
|
for (const [key, value] of Object.entries(result)) {
|
||
|
|
if (typeof value === 'string' && /strong/i.test(key)) {
|
||
|
|
const matches = value.match(/([GH]\d+)/gi);
|
||
|
|
if (matches) matches.forEach(m => strongsSet.add(m.toUpperCase()));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return Array.from(strongsSet);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Query RapidAPI Complete Study Bible for word information
|
||
|
|
* @param {string} word - The word to look up
|
||
|
|
* @param {string} reference - Optional Bible reference (e.g., "John 3:16")
|
||
|
|
* @returns {Promise<Object>} Enhanced word information from RapidAPI
|
||
|
|
*/
|
||
|
|
export async function queryRapidAPIWord(word, reference = null) {
|
||
|
|
try {
|
||
|
|
// Get API key from localStorage (user needs to set this)
|
||
|
|
const apiKey = typeof localStorage !== 'undefined'
|
||
|
|
? localStorage.getItem('rapidapi.key')
|
||
|
|
: null;
|
||
|
|
|
||
|
|
if (!apiKey) {
|
||
|
|
console.warn('[RapidAPI] No API key found. Set localStorage.setItem("rapidapi.key", "your-key")');
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
error: 'API key not configured',
|
||
|
|
word: word,
|
||
|
|
reference: reference
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get API host from localStorage (defaults to the Complete Study Bible API)
|
||
|
|
const apiHost = typeof localStorage !== 'undefined'
|
||
|
|
? localStorage.getItem('rapidapi.host') || 'complete-study-bible.p.rapidapi.com'
|
||
|
|
: 'complete-study-bible.p.rapidapi.com';
|
||
|
|
|
||
|
|
console.log('[RapidAPI] Querying word:', word, 'reference:', reference);
|
||
|
|
console.log('[RapidAPI] API Host:', apiHost);
|
||
|
|
console.log('[RapidAPI] API Key:', apiKey ? apiKey.substring(0, 10) + '...' : 'NOT SET');
|
||
|
|
|
||
|
|
// Try endpoints based on what we found in the API
|
||
|
|
// Priority: word search -> get Strong's -> get Strong's details
|
||
|
|
const normalizedWord = word.toLowerCase().replace(/\s+/g, ''); // Remove spaces for search-exact
|
||
|
|
const endpoints = [
|
||
|
|
// Word search endpoints (found in API)
|
||
|
|
`/search/${encodeURIComponent(word)}/`, // Search for word
|
||
|
|
`/search-exact/${encodeURIComponent(normalizedWord)}/`, // Exact word search
|
||
|
|
`/search/${encodeURIComponent(normalizedWord)}/`, // Search without spaces
|
||
|
|
// Fallback patterns
|
||
|
|
`/word/${encodeURIComponent(word.toLowerCase())}`,
|
||
|
|
`/words/${encodeURIComponent(word.toLowerCase())}`,
|
||
|
|
`/search?word=${encodeURIComponent(word.toLowerCase())}`
|
||
|
|
];
|
||
|
|
|
||
|
|
for (const endpoint of endpoints) {
|
||
|
|
try {
|
||
|
|
const url = `https://${apiHost}${endpoint}`;
|
||
|
|
console.log('[RapidAPI] Trying endpoint:', url);
|
||
|
|
|
||
|
|
const response = await fetch(url, {
|
||
|
|
method: 'GET',
|
||
|
|
headers: {
|
||
|
|
'X-RapidAPI-Key': apiKey,
|
||
|
|
'X-RapidAPI-Host': apiHost,
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('[RapidAPI] Response status:', response.status, 'for endpoint:', endpoint);
|
||
|
|
|
||
|
|
if (response.ok) {
|
||
|
|
const data = await response.json();
|
||
|
|
console.log('[RapidAPI] ✅ SUCCESS! Response data:', data);
|
||
|
|
|
||
|
|
// If this is a search result that might contain Strong's numbers, try to get details
|
||
|
|
let finalData = data;
|
||
|
|
if (Array.isArray(data) && data.length > 0) {
|
||
|
|
// Search result - try to extract Strong's numbers and get details
|
||
|
|
const strongsNumbers = extractStrongsFromSearchResult(data);
|
||
|
|
if (strongsNumbers.length > 0) {
|
||
|
|
console.log('[RapidAPI] Found Strong\'s numbers:', strongsNumbers);
|
||
|
|
// Get Strong's details
|
||
|
|
const detailsEndpoint = `/strongs-detail/${strongsNumbers.join(',')}/`;
|
||
|
|
try {
|
||
|
|
const detailsUrl = `https://${apiHost}${detailsEndpoint}`;
|
||
|
|
const detailsResponse = await fetch(detailsUrl, {
|
||
|
|
method: 'GET',
|
||
|
|
headers: {
|
||
|
|
'X-RapidAPI-Key': apiKey,
|
||
|
|
'X-RapidAPI-Host': apiHost,
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
}
|
||
|
|
});
|
||
|
|
if (detailsResponse.ok) {
|
||
|
|
const detailsData = await detailsResponse.json();
|
||
|
|
console.log('[RapidAPI] ✅ Got Strong\'s details:', detailsData);
|
||
|
|
finalData = { searchResults: data, strongsDetails: detailsData };
|
||
|
|
}
|
||
|
|
} catch (err) {
|
||
|
|
console.log('[RapidAPI] Could not fetch Strong\'s details:', err);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
data: finalData,
|
||
|
|
word: word,
|
||
|
|
reference: reference,
|
||
|
|
endpoint: endpoint
|
||
|
|
};
|
||
|
|
} else {
|
||
|
|
const errorText = await response.text();
|
||
|
|
console.log('[RapidAPI] ❌ Failed:', response.status, errorText.substring(0, 200));
|
||
|
|
|
||
|
|
// If it's a 401/403, stop trying - it's an auth issue
|
||
|
|
if (response.status === 401 || response.status === 403) {
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
error: `Authentication failed (${response.status}). Check your API key.`,
|
||
|
|
word: word,
|
||
|
|
reference: reference
|
||
|
|
};
|
||
|
|
}
|
||
|
|
// Continue to next endpoint
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.log('[RapidAPI] Error trying endpoint:', endpoint, error.message);
|
||
|
|
// Continue to next endpoint
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// If we get here, all endpoints failed
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
error: 'No working endpoint found. Check RapidAPI playground for correct endpoint structure.',
|
||
|
|
word: word,
|
||
|
|
reference: reference
|
||
|
|
};
|
||
|
|
} catch (error) {
|
||
|
|
console.warn('[RapidAPI] Failed to query word:', word, error);
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
error: error.message || 'Network error',
|
||
|
|
word: word,
|
||
|
|
reference: reference
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Removed - now trying all endpoints in main function
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get current Bible reference from active tab
|
||
|
|
* @returns {string|null} Current reference or null
|
||
|
|
*/
|
||
|
|
function getCurrentReference() {
|
||
|
|
try {
|
||
|
|
const activeTab = window.bibleApp?.tabs?.find(t => t.id === window.bibleApp?.activeTabId);
|
||
|
|
if (!activeTab) return null;
|
||
|
|
|
||
|
|
const book = activeTab.book || '';
|
||
|
|
const chapter = activeTab.chapter || '';
|
||
|
|
const verse = activeTab.verse || '';
|
||
|
|
|
||
|
|
if (book && chapter) {
|
||
|
|
return verse ? `${book} ${chapter}:${verse}` : `${book} ${chapter}`;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
} catch (_) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Parse RapidAPI response into structured word info format
|
||
|
|
* This function adapts the API response to match your popup structure
|
||
|
|
* Adjust based on actual API response format
|
||
|
|
*/
|
||
|
|
export function parseRapidAPIResponse(apiResponse, word) {
|
||
|
|
try {
|
||
|
|
console.log('[RapidAPI] Parsing response:', apiResponse);
|
||
|
|
const data = apiResponse.data || apiResponse;
|
||
|
|
console.log('[RapidAPI] Data structure:', Array.isArray(data) ? `Array[${data.length}]` : Object.keys(data || {}));
|
||
|
|
|
||
|
|
// Extract information based on common API response structures
|
||
|
|
// Adjust these mappings based on actual API response format
|
||
|
|
|
||
|
|
const result = {
|
||
|
|
greek: null,
|
||
|
|
hebrew: null,
|
||
|
|
strongs: null,
|
||
|
|
definition: null,
|
||
|
|
transliteration: null,
|
||
|
|
commentaries: [],
|
||
|
|
crossReferences: [],
|
||
|
|
usage: null,
|
||
|
|
morphology: null
|
||
|
|
};
|
||
|
|
|
||
|
|
// Handle Strong's details response (most useful!)
|
||
|
|
if (data.strongsDetails || (Array.isArray(data) && data.length > 0 && (data[0].strongs || data[0].number || data[0].greek || data[0].hebrew))) {
|
||
|
|
const strongsData = data.strongsDetails || data;
|
||
|
|
const strongsArray = Array.isArray(strongsData) ? strongsData : [strongsData];
|
||
|
|
|
||
|
|
for (const item of strongsArray) {
|
||
|
|
// Extract Strong's number - try many field names
|
||
|
|
if (!result.strongs) {
|
||
|
|
const strongs = item.strongs || item.number || item.strongs_number || item.strong || item.id;
|
||
|
|
if (strongs) {
|
||
|
|
result.strongs = String(strongs).toUpperCase();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract definition - try many field names
|
||
|
|
if (!result.definition) {
|
||
|
|
const def = item.definition || item.meaning || item.gloss || item.translation ||
|
||
|
|
item.english || item.text || item.description;
|
||
|
|
if (def) {
|
||
|
|
result.definition = String(def);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract Greek - try many field names and patterns
|
||
|
|
if (!result.greek || !result.greek.text || result.greek.text === '—') {
|
||
|
|
const greekText = item.greek || item.greek_text || item.original_greek || item.greek_word ||
|
||
|
|
item.greek_lemma || item.lemma || item.word_greek;
|
||
|
|
const greekTranslit = item.greek_transliteration || item.transliteration || item.translit ||
|
||
|
|
item.greek_translit || item.greek_trans;
|
||
|
|
|
||
|
|
if (greekText) {
|
||
|
|
result.greek = {
|
||
|
|
text: String(greekText),
|
||
|
|
transliteration: greekTranslit ? String(greekTranslit) : '',
|
||
|
|
meaning: result.definition || String(item.definition || item.meaning || '')
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract Hebrew - try many field names
|
||
|
|
if (!result.hebrew || !result.hebrew.text || result.hebrew.text === '—') {
|
||
|
|
const hebrewText = item.hebrew || item.hebrew_text || item.original_hebrew || item.hebrew_word ||
|
||
|
|
item.hebrew_lemma || item.word_hebrew;
|
||
|
|
const hebrewTranslit = item.hebrew_transliteration || item.transliteration || item.translit ||
|
||
|
|
item.hebrew_translit || item.hebrew_trans;
|
||
|
|
|
||
|
|
if (hebrewText) {
|
||
|
|
result.hebrew = {
|
||
|
|
text: String(hebrewText),
|
||
|
|
transliteration: hebrewTranslit ? String(hebrewTranslit) : '',
|
||
|
|
meaning: result.definition || String(item.definition || item.meaning || '')
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract transliteration
|
||
|
|
if (!result.transliteration) {
|
||
|
|
const translit = item.transliteration || item.translit || item.trans;
|
||
|
|
if (translit) {
|
||
|
|
result.transliteration = String(translit);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract morphology
|
||
|
|
if (!result.morphology) {
|
||
|
|
const morph = item.morphology || item.grammar || item.morph || item.grammatical;
|
||
|
|
if (morph) {
|
||
|
|
result.morphology = String(morph);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Also handle search results if present - calculate OT/NT usage
|
||
|
|
if (data.searchResults && Array.isArray(data.searchResults)) {
|
||
|
|
const OT_BOOKS = new Set(['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','Proverbs','Ecclesiastes','Song of Solomon','Isaiah','Jeremiah','Lamentations','Ezekiel','Daniel','Hosea','Joel','Amos','Obadiah','Jonah','Micah','Nahum','Habakkuk','Zephaniah','Haggai','Zechariah','Malachi']);
|
||
|
|
const NT_BOOKS = new Set(['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']);
|
||
|
|
|
||
|
|
let otCount = 0;
|
||
|
|
let ntCount = 0;
|
||
|
|
|
||
|
|
result.crossReferences = data.searchResults.map(verse => {
|
||
|
|
const book = verse.book__name || verse.book__abbrev || '';
|
||
|
|
if (OT_BOOKS.has(book)) otCount++;
|
||
|
|
if (NT_BOOKS.has(book)) ntCount++;
|
||
|
|
|
||
|
|
const chapter = verse.chapter || '';
|
||
|
|
const verseNum = verse.verse || '';
|
||
|
|
return `${book} ${chapter}:${verseNum}`;
|
||
|
|
});
|
||
|
|
|
||
|
|
result.usage = `Found in ${data.searchResults.length} verse(s)`;
|
||
|
|
result.usageOT = otCount;
|
||
|
|
result.usageNT = ntCount;
|
||
|
|
result.usageTotal = data.searchResults.length;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('[RapidAPI] Parsed Strong\'s details:', result);
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle array responses (like verse search results)
|
||
|
|
if (Array.isArray(data) && data.length > 0) {
|
||
|
|
// This looks like a verse search result - extract cross-references and calculate OT/NT usage
|
||
|
|
const OT_BOOKS = new Set(['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','Proverbs','Ecclesiastes','Song of Solomon','Isaiah','Jeremiah','Lamentations','Ezekiel','Daniel','Hosea','Joel','Amos','Obadiah','Jonah','Micah','Nahum','Habakkuk','Zephaniah','Haggai','Zechariah','Malachi']);
|
||
|
|
const NT_BOOKS = new Set(['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']);
|
||
|
|
|
||
|
|
let otCount = 0;
|
||
|
|
let ntCount = 0;
|
||
|
|
|
||
|
|
result.crossReferences = data.map(verse => {
|
||
|
|
const book = verse.book__name || verse.book__abbrev || '';
|
||
|
|
if (OT_BOOKS.has(book)) otCount++;
|
||
|
|
if (NT_BOOKS.has(book)) ntCount++;
|
||
|
|
|
||
|
|
const chapter = verse.chapter || '';
|
||
|
|
const verseNum = verse.verse || '';
|
||
|
|
return `${book} ${chapter}:${verseNum}`;
|
||
|
|
});
|
||
|
|
|
||
|
|
result.usage = `Found in ${data.length} verse(s)`;
|
||
|
|
result.usageOT = otCount;
|
||
|
|
result.usageNT = ntCount;
|
||
|
|
result.usageTotal = data.length;
|
||
|
|
|
||
|
|
// Try to extract Strong's from the endpoint if available
|
||
|
|
if (apiResponse.endpoint && apiResponse.endpoint.includes('strongs/')) {
|
||
|
|
const match = apiResponse.endpoint.match(/strongs\/([GH]\d+)/i);
|
||
|
|
if (match) {
|
||
|
|
result.strongs = match[1].toUpperCase();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('[RapidAPI] Parsed verse array:', result);
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Try to extract ANY text that might be useful
|
||
|
|
// Check for nested structures
|
||
|
|
const checkNested = (obj, paths) => {
|
||
|
|
for (const path of paths) {
|
||
|
|
const keys = path.split('.');
|
||
|
|
let value = obj;
|
||
|
|
for (const key of keys) {
|
||
|
|
if (value && typeof value === 'object' && key in value) {
|
||
|
|
value = value[key];
|
||
|
|
} else {
|
||
|
|
value = null;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (value) return value;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
};
|
||
|
|
|
||
|
|
// Extract definition - try many possible paths
|
||
|
|
const definition = checkNested(data, [
|
||
|
|
'definition', 'meaning', 'gloss', 'text', 'description',
|
||
|
|
'data.definition', 'result.definition', 'word.definition',
|
||
|
|
'definitions[0]', 'definitions.0'
|
||
|
|
]);
|
||
|
|
if (definition) result.definition = String(definition);
|
||
|
|
|
||
|
|
// Extract Strong's numbers
|
||
|
|
const strongs = checkNested(data, [
|
||
|
|
'strongs', 'strongsNumber', 'strong', 'strongNumber',
|
||
|
|
'data.strongs', 'result.strongs'
|
||
|
|
]);
|
||
|
|
if (strongs) result.strongs = String(strongs);
|
||
|
|
|
||
|
|
// Extract Greek
|
||
|
|
const greekText = checkNested(data, [
|
||
|
|
'greek', 'greekText', 'originalGreek', 'greek.word', 'greek.text'
|
||
|
|
]);
|
||
|
|
if (greekText) {
|
||
|
|
result.greek = {
|
||
|
|
text: String(greekText),
|
||
|
|
transliteration: checkNested(data, ['greekTransliteration', 'greek.transliteration']) || '',
|
||
|
|
meaning: result.definition || ''
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract Hebrew
|
||
|
|
const hebrewText = checkNested(data, [
|
||
|
|
'hebrew', 'hebrewText', 'originalHebrew', 'hebrew.word', 'hebrew.text'
|
||
|
|
]);
|
||
|
|
if (hebrewText) {
|
||
|
|
result.hebrew = {
|
||
|
|
text: String(hebrewText),
|
||
|
|
transliteration: checkNested(data, ['hebrewTransliteration', 'hebrew.transliteration']) || '',
|
||
|
|
meaning: result.definition || ''
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract transliteration
|
||
|
|
const transliteration = checkNested(data, [
|
||
|
|
'transliteration', 'translit', 'data.transliteration'
|
||
|
|
]);
|
||
|
|
if (transliteration) result.transliteration = String(transliteration);
|
||
|
|
|
||
|
|
// Extract commentaries
|
||
|
|
const commentaries = checkNested(data, [
|
||
|
|
'commentaries', 'commentary', 'comments', 'data.commentaries'
|
||
|
|
]);
|
||
|
|
if (Array.isArray(commentaries)) {
|
||
|
|
result.commentaries = commentaries.map(c => String(c));
|
||
|
|
} else if (commentaries) {
|
||
|
|
result.commentaries = [String(commentaries)];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract cross-references
|
||
|
|
const crossRefs = checkNested(data, [
|
||
|
|
'crossReferences', 'references', 'crossRefs', 'xrefs'
|
||
|
|
]);
|
||
|
|
if (Array.isArray(crossRefs)) {
|
||
|
|
result.crossReferences = crossRefs.map(r => String(r));
|
||
|
|
} else if (crossRefs) {
|
||
|
|
result.crossReferences = [String(crossRefs)];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract usage
|
||
|
|
const usage = checkNested(data, [
|
||
|
|
'usage', 'usageCount', 'frequency', 'count'
|
||
|
|
]);
|
||
|
|
if (usage) {
|
||
|
|
result.usage = typeof usage === 'number' ? `Used ${usage} times` : String(usage);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extract morphology
|
||
|
|
const morphology = checkNested(data, [
|
||
|
|
'morphology', 'grammar', 'morph', 'grammatical'
|
||
|
|
]);
|
||
|
|
if (morphology) result.morphology = String(morphology);
|
||
|
|
|
||
|
|
// If we got ANY data, return it
|
||
|
|
const hasData = result.definition || result.strongs || result.greek || result.hebrew ||
|
||
|
|
result.commentaries.length > 0 || result.crossReferences.length > 0;
|
||
|
|
|
||
|
|
if (hasData) {
|
||
|
|
console.log('[RapidAPI] ✅ Parsed data:', result);
|
||
|
|
return result;
|
||
|
|
} else {
|
||
|
|
console.log('[RapidAPI] ⚠️ No recognizable data found in response');
|
||
|
|
// Return raw data so user can see what we got
|
||
|
|
return {
|
||
|
|
definition: `Raw API Response: ${JSON.stringify(data).substring(0, 200)}...`,
|
||
|
|
rawData: data
|
||
|
|
};
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.warn('[RapidAPI] Error parsing response:', error);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Enhance word info object with RapidAPI Complete Study Bible data
|
||
|
|
* @param {Object} wordInfo - Base word info object
|
||
|
|
* @param {string} word - The word being looked up
|
||
|
|
* @param {string} reference - Optional Bible reference
|
||
|
|
* @returns {Promise<Object>} Enhanced word info with RapidAPI data
|
||
|
|
*/
|
||
|
|
export async function enhanceWordInfoWithRapidAPI(wordInfo, word, reference = null) {
|
||
|
|
try {
|
||
|
|
console.log('[RapidAPI] Enhancing word info for:', word, 'reference:', reference);
|
||
|
|
const rapidAPIData = await queryRapidAPIWord(word, reference);
|
||
|
|
console.log('[RapidAPI] Received data:', rapidAPIData);
|
||
|
|
|
||
|
|
// Merge RapidAPI data into word info
|
||
|
|
const enhanced = { ...wordInfo };
|
||
|
|
|
||
|
|
if (rapidAPIData.success && rapidAPIData.data) {
|
||
|
|
const parsed = parseRapidAPIResponse(rapidAPIData, word);
|
||
|
|
|
||
|
|
if (parsed) {
|
||
|
|
// CRITICAL: Only enhance if we have Greek definition (user requirement)
|
||
|
|
const hasGreekDefinition = parsed.greek && parsed.greek.text && parsed.greek.text !== '—' && parsed.greek.text.trim().length > 0;
|
||
|
|
|
||
|
|
if (hasGreekDefinition) {
|
||
|
|
// Enhance Greek data - this is required!
|
||
|
|
enhanced.greek = {
|
||
|
|
text: parsed.greek.text,
|
||
|
|
transliteration: parsed.greek.transliteration || '',
|
||
|
|
meaning: parsed.greek.meaning || parsed.definition || ''
|
||
|
|
};
|
||
|
|
|
||
|
|
// Mark as having valid data
|
||
|
|
enhanced.hasValidData = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Enhance Hebrew data if available (optional)
|
||
|
|
if (parsed.hebrew && parsed.hebrew.text && parsed.hebrew.text !== '—') {
|
||
|
|
enhanced.hebrew = {
|
||
|
|
text: parsed.hebrew.text,
|
||
|
|
transliteration: parsed.hebrew.transliteration || '',
|
||
|
|
meaning: parsed.hebrew.meaning || parsed.definition || ''
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update usage counts from RapidAPI search results
|
||
|
|
if (parsed.usageOT !== undefined) {
|
||
|
|
enhanced.frequencyOT = parsed.usageOT;
|
||
|
|
}
|
||
|
|
if (parsed.usageNT !== undefined) {
|
||
|
|
enhanced.frequencyNT = parsed.usageNT;
|
||
|
|
}
|
||
|
|
if (parsed.usageTotal !== undefined) {
|
||
|
|
enhanced.frequency = parsed.usageTotal;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add RapidAPI section with additional data
|
||
|
|
enhanced.rapidAPI = {
|
||
|
|
available: true,
|
||
|
|
hasGreekDefinition: hasGreekDefinition,
|
||
|
|
strongs: parsed.strongs || null,
|
||
|
|
definition: parsed.definition || null,
|
||
|
|
transliteration: parsed.transliteration || null,
|
||
|
|
commentaries: parsed.commentaries || [],
|
||
|
|
crossReferences: parsed.crossReferences || [],
|
||
|
|
usage: parsed.usage || null,
|
||
|
|
usageOT: parsed.usageOT || null,
|
||
|
|
usageNT: parsed.usageNT || null,
|
||
|
|
usageTotal: parsed.usageTotal || null,
|
||
|
|
morphology: parsed.morphology || null
|
||
|
|
};
|
||
|
|
} else {
|
||
|
|
enhanced.rapidAPI = {
|
||
|
|
available: true,
|
||
|
|
error: 'Could not parse API response',
|
||
|
|
rawData: rapidAPIData.data
|
||
|
|
};
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// API call failed or returned error
|
||
|
|
enhanced.rapidAPI = {
|
||
|
|
available: false,
|
||
|
|
error: rapidAPIData.error || 'Unknown error',
|
||
|
|
note: 'RapidAPI Complete Study Bible integration is configured but returned no data'
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return enhanced;
|
||
|
|
} catch (error) {
|
||
|
|
console.warn('[RapidAPI] Error enhancing word info:', error);
|
||
|
|
return {
|
||
|
|
...wordInfo,
|
||
|
|
rapidAPI: {
|
||
|
|
available: false,
|
||
|
|
error: error.message || 'Unknown error'
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if RapidAPI is available and configured
|
||
|
|
* @returns {Promise<boolean>} True if API is available
|
||
|
|
*/
|
||
|
|
export async function checkRapidAPIAvailability() {
|
||
|
|
try {
|
||
|
|
const apiKey = typeof localStorage !== 'undefined'
|
||
|
|
? localStorage.getItem('rapidapi.key')
|
||
|
|
: null;
|
||
|
|
|
||
|
|
if (!apiKey) {
|
||
|
|
console.log('[RapidAPI] Not configured - no API key found');
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Try a simple test query
|
||
|
|
const testResponse = await queryRapidAPIWord('test');
|
||
|
|
return testResponse.success === true;
|
||
|
|
} catch (error) {
|
||
|
|
console.warn('[RapidAPI] Availability check failed:', error);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Make functions globally available for testing
|
||
|
|
if (typeof window !== 'undefined') {
|
||
|
|
window.queryRapidAPIWord = queryRapidAPIWord;
|
||
|
|
window.enhanceWordInfoWithRapidAPI = enhanceWordInfoWithRapidAPI;
|
||
|
|
window.checkRapidAPIAvailability = checkRapidAPIAvailability;
|
||
|
|
|
||
|
|
// Simple test function
|
||
|
|
window.testRapidAPIDirect = async function(word = 'love') {
|
||
|
|
console.log('🧪 Testing RapidAPI directly for word:', word);
|
||
|
|
console.log('🧪 API Key:', localStorage.getItem('rapidapi.key') ? 'SET' : 'NOT SET');
|
||
|
|
console.log('🧪 API Host:', localStorage.getItem('rapidapi.host') || 'complete-study-bible.p.rapidapi.com');
|
||
|
|
|
||
|
|
const result = await queryRapidAPIWord(word);
|
||
|
|
console.log('🧪 Result:', result);
|
||
|
|
|
||
|
|
if (result.success) {
|
||
|
|
console.log('✅ API call succeeded!');
|
||
|
|
console.log('📦 Response data:', result.data);
|
||
|
|
} else {
|
||
|
|
console.log('❌ API call failed:', result.error);
|
||
|
|
console.log('💡 TIP: Check the RapidAPI playground to find the correct endpoint structure');
|
||
|
|
console.log('💡 TIP: Make sure you\'re subscribed to the API on RapidAPI');
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
};
|
||
|
|
|
||
|
|
// Test a custom endpoint (once you find it in the playground)
|
||
|
|
window.testRapidAPICustomEndpoint = async function(endpoint, word = 'love') {
|
||
|
|
const apiKey = localStorage.getItem('rapidapi.key');
|
||
|
|
const apiHost = localStorage.getItem('rapidapi.host') || 'complete-study-bible.p.rapidapi.com';
|
||
|
|
|
||
|
|
if (!apiKey) {
|
||
|
|
console.error('❌ API key not set!');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Replace {word} or similar placeholders
|
||
|
|
const url = `https://${apiHost}${endpoint.replace('{word}', word).replace('{WORD}', word)}`;
|
||
|
|
|
||
|
|
console.log('🧪 Testing custom endpoint:', url);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const response = await fetch(url, {
|
||
|
|
method: 'GET',
|
||
|
|
headers: {
|
||
|
|
'X-RapidAPI-Key': apiKey,
|
||
|
|
'X-RapidAPI-Host': apiHost,
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('📊 Response status:', response.status);
|
||
|
|
|
||
|
|
if (response.ok) {
|
||
|
|
const data = await response.json();
|
||
|
|
console.log('✅ SUCCESS! Response:', data);
|
||
|
|
return { success: true, data };
|
||
|
|
} else {
|
||
|
|
const errorText = await response.text();
|
||
|
|
console.log('❌ Failed:', response.status, errorText.substring(0, 200));
|
||
|
|
return { success: false, error: `HTTP ${response.status}` };
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('❌ Error:', error);
|
||
|
|
return { success: false, error: error.message };
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|