1// ð¯ WikiLive Infobox JSON Extraction System for Fancy UI 2// This module extracts infobox data into structured JSON for visual UI layouts 3 4// Main function to extract all infobox data as structured JSON 5async function extractInfoboxDataAsJSON(chatContainer) { 6 console.log('ð¨ [extractInfoboxDataAsJSON] Starting infobox data extraction for fancy UI...'); 7 8 // Try multiple selectors to find infoboxes 9 const selectors = [ 10 '.infoboxes-container', 11 '.wikipedia-infobox', 12 '[class*="infobox"]', 13 '.chat-container .wikipedia-infobox' 14 ]; 15 16 let infoboxes = []; 17 let containerFound = false; 18 19 for (const selector of selectors) { 20 const elements = chatContainer.querySelectorAll(selector); 21 if (elements.length > 0) { 22 console.log(`ð¨ [extractInfoboxDataAsJSON] Found ${elements.length} elements with selector: ${selector}`); 23 24 if (selector === '.infoboxes-container') { 25 // Found container, look for infoboxes inside 26 infoboxes = elements[0].querySelectorAll('.wikipedia-infobox'); 27 containerFound = true; 28 } else if (selector === '.wikipedia-infobox' || selector.includes('infobox')) { 29 // Found infoboxes directly 30 infoboxes = elements; 31 containerFound = true; 32 } 33 34 if (infoboxes.length > 0) { 35 break; 36 } 37 } 38 } 39 40 if (!containerFound || infoboxes.length === 0) { 41 console.log('ð¨ [extractInfoboxDataAsJSON] No infoboxes found, waiting and retrying...'); 42 43 // Wait a bit longer and try again 44 setTimeout(() => { 45 extractInfoboxDataAsJSON(chatContainer); 46 }, 3000); 47 return null; 48 } 49 50 console.log(`ð¨ [extractInfoboxDataAsJSON] Found ${infoboxes.length} infoboxes to extract`); 51 52 try { 53 // Extract search term from the most recent user message 54 const userMessages = chatContainer.querySelectorAll('.message.user'); 55 const lastUserMessage = userMessages[userMessages.length - 1]; 56 const searchTerm = lastUserMessage ? lastUserMessage.textContent.trim() : 'Search Results'; 57 58 // Extract all infobox data 59 const extractedData = { 60 searchTerm: searchTerm, 61 timestamp: new Date().toISOString(), 62 totalResults: infoboxes.length, 63 infoboxes: [] 64 }; 65 66 infoboxes.forEach((infobox, index) => { 67 const infoboxData = extractSingleInfoboxData(infobox, index); 68 if (infoboxData) { 69 extractedData.infoboxes.push(infoboxData); 70 } 71 }); 72 73 // Create and display the JSON structure 74 // displayInfoboxJSON(extractedData, chatContainer); // Hidden from UI as requested 75 76 // Trigger fancy UI preview directly without showing JSON structure 77 setTimeout(() => { 78 if (typeof createFancyUIPreview === 'function') { 79 // ð¥ CRITICAL FIX: Include rich Wikipedia metadata for fancy UI 80 const enrichedData = { 81 ...extractedData, 82 realtime_facts: window.wikipediaMetadata?.realtime_facts || [], 83 factsByEntity: window.wikipediaMetadata?.facts_by_entity || {}, 84 wikipediaImages: window.wikipediaMetadata?.wikipediaImages || [], 85 wikipediaData: window.wikipediaMetadata?.wikipediaData || [], 86 extracted_wikipedia_urls: window.wikipediaMetadata?.extracted_wikipedia_urls || [] 87 }; 88 89 console.log('ð¥ [FancyUI Data Fix] Enriched data for fancy UI:', { 90 originalInfoboxes: extractedData.infoboxes?.length || 0, 91 realtimeFactsCount: enrichedData.realtime_facts.length, 92 factsByEntityKeys: Object.keys(enrichedData.factsByEntity).length, 93 wikipediaImagesCount: enrichedData.wikipediaImages.length, 94 wikipediaDataCount: enrichedData.wikipediaData.length 95 }); 96 97 // Store enriched data globally for relationship map access 98 window.currentSearchData = enrichedData; 99 console.log('ð¾ [Global Storage] Stored current search data for relationship map access'); 100 101 createFancyUIPreview(enrichedData, chatContainer); 102 } 103 }, 500); // Small delay to ensure DOM is ready 104 105 // Store the extracted data globally for relationship map and other features 106 if (typeof storeInfoboxData === 'function') { 107 storeInfoboxData(extractedData);
108 console.log('ð¨ [extractInfoboxDataAsJSON] Updated global data store with fresh search results'); 109 } 110 111 console.log('ð¨ [extractInfoboxDataAsJSON] Successfully extracted data for', extractedData.infoboxes.length, 'infoboxes'); 112 return extractedData; 113 114 } catch (error) { 115 console.error('ð¨ [extractInfoboxDataAsJSON] Error:', error); 116 return null; 117 } 118} 119 120// Helper function to extract data from a single infobox 121function extractSingleInfoboxData(infoboxElement, index) { 122 try { 123 const data = { 124 id: `infobox-${index}`, 125 rank: index + 1, 126 isTopRanked: index < 3 127 }; 128 129 // Extract title and URL - improved extraction with multiple fallbacks 130 const titleLink = infoboxElement.querySelector('a[href*="wikipedia.org"]'); 131 if (titleLink) { 132 data.title = titleLink.textContent.trim(); 133 data.url = titleLink.href; 134 } else { 135 // Fallback: look for title in bold div and try to construct Wikipedia URL 136 const titleDiv = infoboxElement.querySelector('div[style*="font-weight: bold"]'); 137 if (titleDiv) { 138 data.title = titleDiv.textContent.trim(); 139 // Try to construct Wikipedia URL from title 140 const encodedTitle = encodeURIComponent(data.title.replace(/\s+/g, '_')); 141 data.url = `https://en.wikipedia.org/wiki/${encodedTitle}`; 142 } else { 143 data.title = `Entity ${index + 1}`; 144 data.url = null; 145 } 146 } 147 148 // Additional fallback: check for data attributes that might contain Wikipedia URL 149 const wikipediaUrl = infoboxElement.getAttribute('data-url') || 150 infoboxElement.getAttribute('data-wikipedia-url'); 151 if (wikipediaUrl && !data.url) { 152 data.url = wikipediaUrl; 153 } 154 155 // Extract image 156 const image = infoboxElement.querySelector('.wikipedia-infobox-image'); 157 if (image) { 158 data.image = { 159 url: image.src, 160 alt: image.alt || data.title 161 }; 162 } 163 164 // Extract summary/description - try multiple approaches 165 let summaryText = null; 166 167 // First, try to get summary from data attribute (if available) 168 summaryText = infoboxElement.getAttribute('data-summary'); 169 170 if (!summaryText) { 171 // Try multiple selectors to find summary text 172 const summaryDiv = infoboxElement.querySelector('div[style*="color: #656d76"]') || 173 infoboxElement.querySelector('div[style*="color: rgba(101, 109, 118"]') || 174 infoboxElement.querySelector('div[style*="line-height: 1.5"]'); 175 176 if (summaryDiv) { 177 summaryText = summaryDiv.textContent.trim(); 178 } else { 179 // Fallback: look for any div that contains substantial text content (likely summary) 180 const allDivs = infoboxElement.querySelectorAll('div'); 181 for (const div of allDivs) { 182 const text = div.textContent.trim(); 183 // Look for divs with substantial text that aren't titles or links 184 if (text.length > 50 && text.length < 500 && 185 !div.querySelector('a') && 186 !div.style.fontWeight?.includes('bold') && 187 !text.includes('Read more') && 188 !text.includes('related links')) { 189 summaryText = text; 190 break; 191 } 192 } 193 } 194 } 195 196 if (summaryText) { 197 data.summary = summaryText; 198 } 199 200 // Extract related links 201 data.relatedLinks = []; 202 const relatedLinksContainer = infoboxElement.querySelector('div[style*="margin-top: 10px"] div:last-child'); 203 if (relatedLinksContainer) { 204 const links = relatedLinksContainer.querySelectorAll('a[href*="wikipedia.org"]'); 205 links.forEach(link => { 206 data.relatedLinks.push({ 207 title: link.textContent.trim(), 208 url: link.href 209 }); 210 }); 211 } 212 213 // Extract relevance indicator if present 214 const relevanceIndicator = infoboxElement.querySelector('div[style*="position: absolute"][style*="top: 8px"]'); 215 if (relevanceIndicator) { 216 const rankText = relevanceIndicator.textContent.trim(); 217 data.relevanceRank = rankText; 218 data.isHighlighted = true; 219 } 220 221 return data; 222 223 } catch (error) { 224 console.error('ð¨ [extractSingleInfoboxData] Error extracting data from infobox', index, ':', error); 225 return null; 226 } 227} 228 229// Function to display the extracted JSON in a beautiful format 230function displayInfoboxJSON(data, chatContainer) { 231 console.log('ð¨ [displayInfoboxJSON] Creating JSON visualization...'); 232 233 // Create JSON display container 234 const jsonContainer = document.createElement('div'); 235 jsonContainer.className = 'infobox-json-display'; 236 jsonContainer.style.cssText = `
237 margin: 20px 0; 238 padding: 20px; 239 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 240 border-radius: 12px; 241 box-shadow: 0 8px 32px rgba(0,0,0,0.1); 242 color: white; 243 font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; 244 `; 245 246 // Create header 247 const header = document.createElement('div'); 248 header.style.cssText = ` 249 display: flex; 250 align-items: center; 251 margin-bottom: 15px; 252 font-size: 1.2em; 253 font-weight: bold; 254 `; 255 header.innerHTML = ` 256 <span style="margin-right: 10px;">ð¨</span> 257 <span>Infobox Data JSON Structure</span> 258 <span style="margin-left: auto; font-size: 0.8em; opacity: 0.8;">${data.totalResults} results</span> 259 `; 260 jsonContainer.appendChild(header); 261 262 // Create collapsible JSON display 263 const jsonDisplay = document.createElement('div'); 264 jsonDisplay.style.cssText = ` 265 background: rgba(0,0,0,0.2); 266 border-radius: 8px; 267 padding: 15px; 268 max-height: 400px; 269 overflow-y: auto; 270 border: 1px solid rgba(255,255,255,0.1); 271 `; 272 273 // Format JSON with syntax highlighting 274 const formattedJSON = formatJSONWithHighlighting(data); 275 jsonDisplay.innerHTML = formattedJSON; 276 jsonContainer.appendChild(jsonDisplay); 277 278 // Create copy button 279 const copyButton = document.createElement('button'); 280 copyButton.textContent = 'ð Copy JSON'; 281 copyButton.style.cssText = ` 282 margin-top: 10px; 283 padding: 8px 16px; 284 background: rgba(255,255,255,0.2); 285 border: 1px solid rgba(255,255,255,0.3); 286 border-radius: 6px; 287 color: white; 288 cursor: pointer; 289 font-size: 0.9em; 290 transition: all 0.2s ease; 291 `; 292 293 copyButton.addEventListener('click', () => { 294 navigator.clipboard.writeText(JSON.stringify(data, null, 2)).then(() => { 295 copyButton.textContent = 'â Copied!'; 296 setTimeout(() => { 297 copyButton.textContent = 'ð Copy JSON'; 298 }, 2000); 299 }); 300 }); 301 302 copyButton.addEventListener('mouseenter', () => { 303 copyButton.style.background = 'rgba(255,255,255,0.3)'; 304 }); 305 306 copyButton.addEventListener('mouseleave', () => { 307 copyButton.style.background = 'rgba(255,255,255,0.2)'; 308 }); 309 310 jsonContainer.appendChild(copyButton); 311 312 // Automatically trigger fancy UI preview as part of the flow 313 setTimeout(() => { 314 // ð¥ CRITICAL FIX: Include rich Wikipedia metadata for fancy UI 315 const enrichedData = { 316 ...data, 317 realtime_facts: window.wikipediaMetadata?.realtime_facts || [], 318 factsByEntity: window.wikipediaMetadata?.facts_by_entity || {}, 319 wikipediaImages: window.wikipediaMetadata?.wikipediaImages || [], 320 wikipediaData: window.wikipediaMetadata?.wikipediaData || [], 321 extracted_wikipedia_urls: window.wikipediaMetadata?.extracted_wikipedia_urls || [] 322 }; 323 324 console.log('ð¥ [FancyUI Data Fix] Enriched data for fancy UI (flow 2):', { 325 originalInfoboxes: data.infoboxes?.length || 0, 326 realtimeFactsCount: enrichedData.realtime_facts.length, 327 factsByEntityKeys: Object.keys(enrichedData.factsByEntity).length, 328 wikipediaImagesCount: enrichedData.wikipediaImages.length, 329 wikipediaDataCount: enrichedData.wikipediaData.length 330 }); 331 332 createFancyUIPreview(enrichedData, chatContainer); 333 }, 500); // Small delay to ensure DOM is ready 334 335 // Add to chat container 336 chatContainer.appendChild(jsonContainer); 337 338 // Log the JSON structure to console for development 339 console.log('ð¨ [displayInfoboxJSON] Complete JSON structure:', JSON.stringify(data, null, 2)); 340} 341 342// Helper function to format JSON with syntax highlighting 343function formatJSONWithHighlighting(obj) { 344 const jsonString = JSON.stringify(obj, null, 2); 345 return jsonString 346 .replace(/(".*?"):/g, '<span style="color: #87CEEB;">$1</span>:') 347 .replace(/: (".*?")/g, ': <span style="color: #98FB98;">$1</span>') 348 .replace(/: (\d+)/g, ': <span style="color: #FFB6C1;">$1</span>') 349 .replace(/: (true|false|null)/g, ': <span style="color: #DDA0DD;">$1</span>') 350 .replace(/\n/g, '<br>') 351 .replace(/ /g, ' '); 352} 353 354// Export functions for use in main.js 355if (typeof module !== 'undefined' && module.exports) { 356 module.exports = {
357 extractInfoboxDataAsJSON, 358 extractSingleInfoboxData, 359 displayInfoboxJSON, 360 formatJSONWithHighlighting 361 }; 362}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.