PageSourceSearch

https://dynamic-salmiakki-095693.netlify.app/infobox-json-extractor.js

js dynamic-salmiakki-095693.netlify.app collected 2026-10-03 10:40:22 UTC 14,875 bytes, 362 lines download raw bytes

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, '&nbsp;');
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.