1// Image Processing & Gallery Module 2// Extracted from main.js for better modularity 3 4/** 5 * Wikipedia Image Processing & Gallery Manager 6 * Handles image fetching, gallery creation, and image display functionality 7 */ 8class WikipediaImageGallery { 9 constructor() { 10 this.imageCache = new Map(); // Cache for fetched images 11 this.galleryCount = 0; // Counter for unique gallery IDs 12 } 13 14 /** 15 * Fetch Wikipedia images for multiple entities (Stage 2/3 progressive loading) 16 * @param {Array} entities - Array of entity objects with title/name properties 17 * @param {string} mainQuery - Main search query for context 18 * @returns {Promise<void>} 19 */ 20 async fetchWikipediaImagesForEntities(entities, mainQuery) { 21 if (!entities || entities.length === 0) { 22 console.log('[Stage 2/3 Images] No entities provided for image fetching'); 23 return; 24 } 25 26 console.log(`[Stage 2/3 Images] ð Starting image fetch for ${entities.length} entities`); 27 console.log('[Stage 2/3 Images] Entities to process:', entities.map(e => e.title || e.name || e.page_title || e)); 28 29 // Initialize global storage if not exists 30 if (!window.fetchedGalleryImages) { 31 window.fetchedGalleryImages = {}; 32 } 33 34 // Process entities in smaller batches to avoid overwhelming the API 35 const batchSize = 3; // Process 3 entities at a time 36 const batches = []; 37 for (let i = 0; i < entities.length; i += batchSize) { 38 batches.push(entities.slice(i, i + batchSize)); 39 } 40 41 console.log(`[Stage 2/3 Images] Processing ${batches.length} batches of ${batchSize} entities each`); 42 43 for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) { 44 const batch = batches[batchIndex]; 45 console.log(`[Stage 2/3 Images] ð¦ Processing batch ${batchIndex + 1}/${batches.length}:`, batch.map(e => e.title || e.name || e.page_title || e)); 46 47 // Process entities in parallel within each batch 48 const batchPromises = batch.map(async (entity, entityIndex) => { 49 const entityTitle = entity.title || entity.name || entity.page_title || entity; 50 if (!entityTitle || typeof entityTitle !== 'string') { 51 console.warn('[Stage 2/3 Images] Invalid entity title:', entity); 52 return; 53 } 54 55 // Skip if we already have images for this entity 56 if (window.fetchedGalleryImages[entityTitle]) { 57 console.log(`[Stage 2/3 Images] âï¸ Skipping ${entityTitle} - already have ${window.fetchedGalleryImages[entityTitle].length} images`); 58 return; 59 } 60 61 try { 62 console.log(`[Stage 2/3 Images] ð Fetching images for: ${entityTitle}`); 63 await this.fetchWikipediaImageForQuery(entityTitle); 64 console.log(`[Stage 2/3 Images] â Completed image fetch for: ${entityTitle}`); 65 } catch (error) { 66 console.error(`[Stage 2/3 Images] â Failed to fetch images for ${entityTitle}:`, error); 67 } 68 }); 69 70 // Wait for current batch to complete 71 await Promise.allSettled(batchPromises); 72 73 // Add delay between batches to be respectful to Wikipedia API 74 if (batchIndex < batches.length - 1) { 75 const delay = 2000; // 2 second delay between batches 76 console.log(`[Stage 2/3 Images] â³ Waiting ${delay}ms before next batch...`); 77 await new Promise(resolve => setTimeout(resolve, delay)); 78 } 79 } 80 81 console.log('[Stage 2/3 Images] ð Completed image fetching for all entities'); 82 console.log('[Stage 2/3 Images] Final global storage:', Object.keys(window.fetchedGalleryImages).map(key => `${key}: ${window.fetchedGalleryImages[key].length} images`)); 83 84 // Trigger gallery refresh if fancy UI is available 85 if (typeof window.createFancyUIPreview === 'function' && window.wikipediaMetadata) { 86 console.log('[Stage 2/3 Images] ð Triggering gallery refresh after Stage 2/3 image fetch'); 87 setTimeout(() => { 88 try { 89 const refreshData = { 90 wikipediaData: window.wikipediaMetadata?.wikipediaData || [], 91 wikipediaImages: window.wikipediaMetadata?.wikipediaImages || [], 92 realtimeFacts: window.wikipediaMetadata?.realtimeFacts || [], 93 factsByEntity: window.wikipediaMetadata?.factsByEntity || {}, 94 fullResponse: window.lastFullResponse || '' 95 }; 96 window.createFancyUIPreview(refreshData, document.querySelector('.chat-container')); 97 } catch (error) { 98 console.error('[Stage 2/3 Images] Error refreshing gallery:', error); 99 } 100 }, 1000); 101 } 102 } 103 104 /** 105 * Fetch Wikipedia image for query using improved topic extraction 106 * @param {string} query - Search query 107 * @returns {Promise<void>} 108 */ 109 async fetchWikipediaImageForQuery(query) { 110 // Enhanced fetch with retry logic and rate limiting 111 const fetchWithRetry = async (url, retries = 3) => { 112 for (let attempt = 1; attempt <= retries; attempt++) { 113 try { 114 // Add delay between requests to prevent rate limiting 115 if (attempt > 1) {
116 const delay = Math.min(1000 * Math.pow(2, attempt - 2), 5000); // 1s, 2s, 4s max 117 console.log(`[Portal Image] Retry ${attempt}/${retries} after ${delay}ms delay`); 118 await new Promise(resolve => setTimeout(resolve, delay)); 119 } 120 121 const response = await fetch(url, { 122 headers: { 123 'User-Agent': 'WikiLive/1.0 (Educational Tool)' 124 } 125 }); 126 127 // Handle specific HTTP status codes 128 if (response.status === 502 || response.status === 503 || response.status === 504) { 129 throw new Error(`HTTP ${response.status}: Server temporarily unavailable`); 130 } 131 132 if (response.status === 429) { 133 // Rate limited - wait longer 134 const retryAfter = response.headers.get('Retry-After') || 10; 135 console.warn(`[Portal Image] Rate limited, waiting ${retryAfter}s`); 136 await new Promise(resolve => setTimeout(resolve, retryAfter * 1000)); 137 throw new Error('Rate limited, retrying'); 138 } 139 140 if (!response.ok) { 141 throw new Error(`HTTP ${response.status}: ${response.statusText}`); 142 } 143 144 return response; 145 } catch (error) { 146 console.warn(`[Portal Image] Attempt ${attempt}/${retries} failed:`, error.message); 147 148 // Check if this is a retryable error 149 const isRetryableError = ( 150 error.message.includes('502') || 151 error.message.includes('503') || 152 error.message.includes('504') || 153 error.message.includes('429') || 154 error.message.includes('Rate limited') || 155 error.message.includes('ERR_CONNECTION_CLOSED') || 156 error.message.includes('ERR_NETWORK') || 157 error.message.includes('ERR_INTERNET_DISCONNECTED') || 158 error.message.includes('network error') || 159 error.message.includes('Failed to fetch') || 160 error.name === 'TypeError' && error.message.includes('fetch') 161 ); 162 163 if (attempt === retries) { 164 if (isRetryableError) { 165 console.error(`[Portal Image] Max retries (${retries}) reached for retryable error:`, error.message); 166 } 167 throw error; 168 } 169 170 // Only retry if it's a retryable error 171 if (!isRetryableError) { 172 console.error(`[Portal Image] Non-retryable error, failing immediately:`, error.message); 173 throw error; 174 } 175 176 // Don't retry on non-retryable errors 177 if (error.message.includes('400') || error.message.includes('401') || error.message.includes('403')) { 178 throw error; 179 } 180 } 181 } 182 }; 183 184 try { 185 console.log('[Portal Image] Fetching images for query:', query); 186 187 // Step 1: Use Wikipedia's search API to resolve natural language query to page title 188 const searchUrl = `https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${encodeURIComponent(query)}&format=json&origin=*`; 189 190 if (window.brainLoader) { 191 window.brainLoader.updateProcessingStep('Searching Wikipedia...', 'ð'); 192 } 193 194 const searchResponse = await fetch(searchUrl); 195 196 if (!searchResponse.ok) { 197 throw new Error(`Search API failed: ${searchResponse.status}`); 198 } 199 200 const searchData = await searchResponse.json(); 201 if (!searchData.query?.search?.length) { 202 console.log('[Portal Image] No Wikipedia pages found for query:', query); 203 if (window.brainLoader) { 204 window.brainLoader.updateProcessingStep('No Wikipedia content found', 'â'); 205 } 206 return; 207 } 208 209 const pageTitle = searchData.query.search[0].title; 210 console.log('[Portal Image] Resolved page title:', pageTitle); 211 212 // Step 2: First fetch the main page image for later use 213 if (window.brainLoader) { 214 window.brainLoader.updateProcessingStep('Fetching main image...', 'ð¼ï¸'); 215 } 216 217 let mainImageUrl = null; 218 try { 219 const mainImageApiUrl = `https://en.wikipedia.org/w/api.php?action=query&titles=${encodeURIComponent(pageTitle)}&prop=pageimages&format=json&pithumbsize=300&origin=*`; 220 const mainImageResponse = await fetchWithRetry(mainImageApiUrl); 221 222 if (mainImageResponse.ok) { 223 const mainImageData = await mainImageResponse.json(); 224 if (mainImageData.query && mainImageData.query.pages) { 225 const pages = Object.values(mainImageData.query.pages); 226 if (pages[0] && pages[0].thumbnail && pages[0].thumbnail.source) { 227 mainImageUrl = pages[0].thumbnail.source; 228 console.log('[Portal Image] Found main image:', mainImageUrl); 229 } 230 } 231 } 232 } catch (error) { 233 console.warn('[Portal Image] Failed to fetch main image:', error); 234 } 235
236 // Step 3: Fetch ALL images from this page using generator=images API 237 if (window.brainLoader) { 238 window.brainLoader.updateProcessingStep('Fetching all images...', 'ð¼ï¸'); 239 } 240 241 // Fetch ALL images for the page using generator=images with continuation 242 const allImagesUrl = `https://en.wikipedia.org/w/api.php?action=query&generator=images&titles=${encodeURIComponent(pageTitle)}&prop=imageinfo&iiprop=url&format=json&origin=*`; 243 244 let allImageUrls = []; 245 let continueToken = null; 246 let fetchCount = 0; 247 const maxFetches = 5; // Limit to prevent infinite loops 248 249 250 251 // Fetch all image batches using continuation 252 do { 253 let currentUrl = allImagesUrl; 254 if (continueToken) { 255 currentUrl += `&gimcontinue=${encodeURIComponent(continueToken)}`; 256 } 257 258 console.log(`[Portal Image] Fetching image batch ${fetchCount + 1}:`, currentUrl); 259 260 try { 261 const allImagesResponse = await fetchWithRetry(currentUrl); 262 263 if (allImagesResponse.ok) { 264 const allImagesData = await allImagesResponse.json(); 265 console.log(`[Portal Image] Batch ${fetchCount + 1} API response:`, allImagesData); 266 267 // Extract image URLs from the generator response 268 if (allImagesData.query && allImagesData.query.pages) { 269 Object.values(allImagesData.query.pages).forEach(page => { 270 if (page.imageinfo && page.imageinfo[0] && page.imageinfo[0].url) { 271 allImageUrls.push(page.imageinfo[0].url); 272 } 273 }); 274 } 275
276 // Check for continuation 277 continueToken = allImagesData.continue ? allImagesData.continue.gimcontinue : null; 278 fetchCount++; 279 280 console.log(`[Portal Image] Batch ${fetchCount} complete. Images so far: ${allImageUrls.length}, Continue: ${continueToken ? 'Yes' : 'No'}`); 281 } else { 282 console.error('[Portal Image] Failed to fetch image batch:', allImagesResponse.status); 283 break; 284 } 285 } catch (error) { 286 console.error(`[Portal Image] Connection error in batch ${fetchCount + 1}:`, error.message); 287 288 // Check if we have any images already - if so, continue with what we have 289 if (allImageUrls.length > 0) { 290 console.log(`[Portal Image] Continuing with ${allImageUrls.length} images already fetched`); 291 if (window.brainLoader) { 292 window.brainLoader.updateProcessingStep(`Connection issue, using ${allImageUrls.length} images`, 'â ï¸'); 293 } 294 break; // Exit the loop but continue processing with existing images 295 } else { 296 // No images yet, this is a more serious failure 297 console.error('[Portal Image] No images fetched and connection failed'); 298 if (window.brainLoader) { 299 window.brainLoader.updateProcessingStep('Connection failed, no images available', 'â'); 300 } 301 return; // Exit the function entirely 302 } 303 } 304 } while (continueToken && fetchCount < maxFetches); 305 306 console.log(`[Portal Image] Total images fetched: ${allImageUrls.length}`); 307 308 // Filter out unwanted images (logos, icons, etc.) and keep only photos 309 let imageUrls = allImageUrls.filter(url => { 310 const urlLower = url.toLowerCase(); 311 return url && 312 url.startsWith('http') && 313 (url.includes('.jpg') || url.includes('.png') || url.includes('.jpeg')) && 314 !urlLower.includes('commons-logo') && 315 !urlLower.includes('wikimedia-logo') && 316 !urlLower.includes('edit-icon') && 317 !urlLower.includes('flag') && 318 !urlLower.includes('.svg') && 319 !urlLower.includes('symbol') && 320 !urlLower.includes('icon') && 321 !urlLower.includes('protection') && 322 !urlLower.includes('vip.svg') && 323 !urlLower.includes('shackle'); 324 }); 325 326 // Remove duplicates 327 imageUrls = [...new Set(imageUrls)]; 328 329 console.log('[Portal Image] Filtered image URLs:', imageUrls); 330 331 if (imageUrls.length === 0) { 332 console.log('[Portal Image] No valid images found, falling back to main image API'); 333 334 // Fallback: Get main page image if no gallery images found 335 const mainImageUrlFallback = `https://en.wikipedia.org/w/api.php?action=query&titles=${encodeURIComponent(pageTitle)}&prop=pageimages&format=json&pithumbsize=600&origin=*`; 336 const mainImageResponseFallback = await fetchWithRetry(mainImageUrlFallback); 337 338 if (mainImageResponseFallback.ok) { 339 const mainImageDataFallback = await mainImageResponseFallback.json(); 340 if (mainImageDataFallback.query && mainImageDataFallback.query.pages) { 341 const pagesFallback = Object.values(mainImageDataFallback.query.pages); 342 if (pagesFallback[0] && pagesFallback[0].thumbnail && pagesFallback[0].thumbnail.source) { 343 imageUrls.push(pagesFallback[0].thumbnail.source); 344 console.log('[Portal Image] Found main image as fallback:', pagesFallback[0].thumbnail.source); 345 } 346 } 347 } 348 } 349 350 if (imageUrls.length === 0) { 351 console.log('[Portal Image] No valid images found for:', pageTitle); 352 if (window.brainLoader) { 353 window.brainLoader.updateProcessingStep('No images found', 'â'); 354 } 355 return; 356 } 357 358 // Step 4: Cache the images 359 this.imageCache.set(pageTitle, { 360 pageTitle: pageTitle, 361 imageUrls: imageUrls, 362 timestamp: Date.now() 363 }); 364 365 // Step 5: Store images globally for fancy UI gallery access 366 if (!window.fetchedGalleryImages) { 367 window.fetchedGalleryImages = {}; 368 }
369 window.fetchedGalleryImages[pageTitle] = { 370 pageTitle: pageTitle, 371 imageUrls: imageUrls, 372 mainImageUrl: mainImageUrl, 373 timestamp: Date.now(), 374 totalImages: imageUrls.length 375 }; 376 console.log(`[Portal Image] ð¯ Stored ${imageUrls.length} images globally for gallery:`, pageTitle); 377 378 // Step 6: Start the image slideshow in brain portal 379 if (window.brainLoader) { 380 window.brainLoader.updateProcessingStep(`Found ${imageUrls.length} images!`, 'â '); 381 window.brainLoader.startImageSlideshow(imageUrls, mainImageUrl); 382 } 383 384 console.log('[Portal Image] Successfully started image slideshow for:', pageTitle, 'with', imageUrls.length, 'images'); 385 386 } catch (error) { 387 console.error('[Portal Image] Error fetching Wikipedia images for query:', error); 388 if (window.brainLoader) { 389 window.brainLoader.updateProcessingStep('Processing query...', 'ð'); 390 } 391 } 392 } 393 394 /** 395 * Create gallery with AI response images 396 * @param {string} aiResponseText - AI response text containing Wikipedia URLs 397 * @param {HTMLElement} botDiv - Bot message container 398 * @returns {Promise<void>} 399 */ 400 async createGalleryWithAIResponseImages(aiResponseText, botDiv) { 401 console.log('ð¨ Creating new gallery for AI response URL images'); 402 403 const wikipediaUrls = window.URLHelpers ? 404 window.URLHelpers.extractWikipediaUrlsFromText(aiResponseText) : 405 this.extractWikipediaUrlsFromText(aiResponseText); 406 407 if (wikipediaUrls.length === 0) { 408 console.log('ð No Wikipedia URLs found in AI response for new gallery'); 409 return; 410 } 411 412 // Create gallery div 413 const galleryDiv = document.createElement('div'); 414 galleryDiv.className = 'wikipedia-image-gallery'; 415 galleryDiv.id = `gallery-${++this.galleryCount}`; 416 galleryDiv.style.cssText = ` 417 display: grid; 418 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); 419 gap: 15px; 420 margin: 20px 0; 421 padding: 15px; 422 background: #f8f9fa; 423 border-radius: 8px; 424 border: 1px solid #e9ecef; 425 `; 426 427 // Add gallery header 428 const headerDiv = document.createElement('div'); 429 headerDiv.style.cssText = ` 430 grid-column: 1 / -1; 431 font-weight: bold; 432 color: #495057; 433 margin-bottom: 10px; 434 font-size: 0.9em; 435 `; 436 headerDiv.textContent = `ð¸ Wikipedia Images (${wikipediaUrls.length} sources)`; 437 galleryDiv.appendChild(headerDiv); 438 439 // Process each URL for images 440 const imagePromises = wikipediaUrls.map(async (url, index) => { 441 try { 442 // Extract page title from URL 443 const titleMatch = url.match(/\/wiki\/(.+)$/); 444 if (!titleMatch) return null; 445 446 const pageTitle = decodeURIComponent(titleMatch[1]); 447 console.log(`ð Gallery: Fetching image ${index + 1} for: ${pageTitle}`); 448 449 // Check cache first 450 if (this.imageCache.has(pageTitle)) { 451 const cached = this.imageCache.get(pageTitle); 452 return { pageTitle, imageUrl: cached.imageUrls[0], pageUrl: url }; 453 } 454 455 // Fetch image from API 456 const imageResponse = await fetch(`/.netlify/functions/fetch-wikipedia-image?title=${encodeURIComponent(pageTitle)}`); 457 458 if (imageResponse.ok) { 459 const imageData = await imageResponse.json(); 460 if (imageData.imageUrl) { 461 // Cache the result 462 this.imageCache.set(pageTitle, { 463 pageTitle: pageTitle, 464 imageUrls: [imageData.imageUrl], 465 timestamp: Date.now() 466 }); 467 468 return { 469 pageTitle: pageTitle, 470 imageUrl: imageData.imageUrl, 471 pageUrl: url 472 }; 473 } 474 } 475 476 console.log(`â Gallery: No image found for ${pageTitle}`); 477 return null; 478 479 } catch (error) { 480 console.error(`â Gallery: Error fetching image ${index + 1}:`, error); 481 return null; 482 } 483 }); 484 485 // Wait for all image fetches to complete 486 const imageResults = await Promise.all(imagePromises); 487 const validImages = imageResults.filter(result => result !== null); 488 489 console.log(`â Gallery: Successfully fetched ${validImages.length}/${wikipediaUrls.length} images`); 490 491 // Create image elements
492 validImages.forEach((imageData, index) => { 493 const imageContainer = document.createElement('div'); 494 imageContainer.style.cssText = ` 495 position: relative; 496 border-radius: 6px; 497 overflow: hidden; 498 box-shadow: 0 2px 8px rgba(0,0,0,0.1); 499 transition: transform 0.2s ease; 500 cursor: pointer; 501 background: white; 502 `; 503 504 // Add hover effect 505 imageContainer.addEventListener('mouseenter', () => { 506 imageContainer.style.transform = 'scale(1.02)'; 507 }); 508 imageContainer.addEventListener('mouseleave', () => { 509 imageContainer.style.transform = 'scale(1)'; 510 }); 511 512 const img = document.createElement('img'); 513 img.src = imageData.imageUrl; 514 img.alt = imageData.pageTitle; 515 img.style.cssText = ` 516 width: 100%; 517 height: 150px; 518 object-fit: cover; 519 display: block; 520 `; 521 522 // Add click handler to open Wikipedia page 523 imageContainer.addEventListener('click', () => { 524 window.open(imageData.pageUrl, '_blank'); 525 }); 526 527 // Hide container if image fails to load 528 img.addEventListener('error', () => { 529 console.error(`â Failed to load gallery image: ${imageData.pageTitle}`); 530 imageContainer.style.display = 'none'; 531 }); 532 533 // Add loading and success handlers 534 img.addEventListener('load', () => { 535 console.log(`â Gallery image ${index + 1} loaded successfully: ${imageData.pageTitle}`); 536 }); 537 538 // Add image title overlay 539 const titleOverlay = document.createElement('div'); 540 titleOverlay.style.cssText = ` 541 position: absolute; 542 bottom: 0; 543 left: 0; 544 right: 0; 545 background: linear-gradient(transparent, rgba(0,0,0,0.7)); 546 color: white; 547 padding: 8px; 548 font-size: 0.8em; 549 font-weight: 500; 550 text-shadow: 1px 1px 2px rgba(0,0,0,0.8); 551 `; 552 titleOverlay.textContent = imageData.pageTitle.replace(/_/g, ' '); 553 554 imageContainer.appendChild(img); 555 imageContainer.appendChild(titleOverlay); 556 galleryDiv.appendChild(imageContainer); 557 }); 558 559 // Only add gallery if we have images and a valid container 560 if (validImages.length > 0) { 561 if (botDiv) { 562 botDiv.appendChild(galleryDiv); 563 console.log(`ð¸ Wikipedia image gallery added to bot message with ${validImages.length} images`); 564 } else { 565 console.warn('â ï¸ botDiv is null, cannot append gallery'); 566 } 567 } else { 568 console.log('ð No valid images found, gallery not added'); 569 } 570 } 571 572 /** 573 * Fallback function to extract Wikipedia URLs from text 574 * @param {string} text - Text to search for URLs 575 * @returns {Array<string>} - Array of Wikipedia URLs 576 */ 577 extractWikipediaUrlsFromText(text) { 578 if (!text || typeof text !== 'string') { 579 return []; 580 } 581 582 const wikipediaUrlRegex = /https?:\/\/[a-z]{2,3}\.wikipedia\.org\/wiki\/[^\s\)\]\"'\<\>]+/gi; 583 const urls = text.match(wikipediaUrlRegex) || []; 584 585 // Clean and deduplicate URLs 586 const cleanUrls = urls 587 .map(url => url.replace(/\/wiki\/([^:]+):(.*)$/, '/wiki/$1')) // Fix malformed URLs 588 .filter(url => url.length > 0) 589 .filter((url, index, arr) => arr.indexOf(url) === index); // Remove duplicates 590 591 return cleanUrls; 592 } 593 594 /** 595 * Fetch Wikipedia image from extracted URLs (enhanced version) 596 * @param {Array<string>} extractedUrls - Array of Wikipedia URLs 597 * @returns {Promise<void>} 598 */ 599 async fetchWikipediaImageFromExtractedUrls(extractedUrls) { 600 if (!extractedUrls || !Array.isArray(extractedUrls) || extractedUrls.length === 0) { 601 console.log('[Portal Image] No extracted Wikipedia URLs available'); 602 return; 603 } 604 605 try { 606 // Smart URL selection: prioritize person names and main
606subjects 607 let selectedUrl = extractedUrls[0]; 608 609 // Look for a main page (non-disambiguation) if available 610 for (const url of extractedUrls) { 611 const title = url.split('/wiki/')[1]; 612 if (title && !title.includes('(disambiguation)') && !title.includes('List_of')) { 613 selectedUrl = url; 614 break; 615 } 616 } 617 618 // Extract page title 619 const titleMatch = selectedUrl.match(/\/wiki\/(.+)$/); 620 if (!titleMatch) { 621 console.log('[Portal Image] Could not extract title from URL:', selectedUrl); 622 return; 623 } 624 625 const pageTitle = decodeURIComponent(titleMatch[1]); 626 console.log('[Portal Image] Fetching image for extracted URL:', pageTitle); 627 628 if (window.brainLoader) { 629 window.brainLoader.updateProcessingStep('Fetching main image...', 'ð¼ï¸'); 630 } 631 632 const imageResponse = await fetch(`/.netlify/functions/fetch-wikipedia-image?title=${encodeURIComponent(pageTitle)}`); 633 634 if (imageResponse.ok) { 635 const imageData = await imageResponse.json(); 636 if (imageData.imageUrl) { 637 // Cache the image 638 this.imageCache.set(pageTitle, { 639 pageTitle: pageTitle, 640 imageUrls: [imageData.imageUrl], 641 timestamp: Date.now() 642 }); 643 644 if (window.brainLoader) { 645 window.brainLoader.updateProcessingStep('Main image loaded!', 'â '); 646 } 647 console.log('[Portal Image] Successfully loaded image from OpenAI-extracted URL'); 648 } else { 649 console.log('[Portal Image] No image found for page:', pageTitle); 650 } 651 } else { 652 console.log('[Portal Image] Failed to fetch image, status:', imageResponse.status); 653 } 654 } catch (error) { 655 console.error('[Portal Image] Error fetching Wikipedia image from extracted URLs:', error); 656 } 657 } 658 659 /** 660 * Clear image cache 661 */ 662 clearCache() { 663 this.imageCache.clear(); 664 console.log('[Image Gallery] Cache cleared'); 665 } 666 667 /** 668 * Get cached image data 669 * @param {string} pageTitle - Wikipedia page title 670 * @returns {Object|null} - Cached image data or null 671 */ 672 getCachedImage(pageTitle) { 673 return this.imageCache.get(pageTitle) || null; 674 } 675 676 /** 677 * Get cache statistics 678 * @returns {Object} - Cache statistics 679 */ 680 getCacheStats() { 681 return { 682 size: this.imageCache.size, 683 galleries: this.galleryCount 684 }; 685 } 686} 687 688// Export for use in other modules 689if (typeof module !== 'undefined' && module.exports) { 690 // Node.js environment 691 module.exports = { 692 WikipediaImageGallery 693 }; 694} else { 695 // Browser environment - attach to window 696 window.WikipediaImageGallery = WikipediaImageGallery; 697 698 // Create global instance for backward compatibility 699 window.imageGallery = new WikipediaImageGallery(); 700 701 // Export functions to global scope for backward compatibility 702 window.fetchWikipediaImageForQuery = (query) => window.imageGallery.fetchWikipediaImageForQuery(query); 703 window.fetchWikipediaImagesForEntities = (entities, mainQuery) => window.imageGallery.fetchWikipediaImagesForEntities(entities, mainQuery); 704 window.createGalleryWithAIResponseImages = (aiResponseText, botDiv) => 705 window.imageGallery.createGalleryWithAIResponseImages(aiResponseText, botDiv); 706 window.fetchWikipediaImageFromExtractedUrls = (extractedUrls) => 707 window.imageGallery.fetchWikipediaImageFromExtractedUrls(extractedUrls); 708} 709 710console.log('Wikipedia Image Processing & Gallery Module - Loaded and Ready');
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.