Actions

MediaWiki

MediaWiki:Gadget-ResourcePage.js

From Dune Awakening DB

Revision as of 01:45, 4 June 2025 by Operator (talk | contribs)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
 * ResourcePage Gadget - Complete Fixed Version
 * Handles recipe popups and search functionality for resource pages
 * 
 * @requires jquery
 * @requires mediawiki.api
 * @requires mediawiki.util
 */

(function($, mw) {
    'use strict';
    
    // Only run on pages with resource page elements
    if (!$('.crafting-section, .recipe-table').length) {
        return;
    }
    
    console.log('[ResourcePage] Gadget loading...');
    
    // Cache for storing recipe data
    var recipeCache = {
        craftedWith: null,
        craftedFrom: null
    };
    
    // Initialize module
    function init() {
        console.log('[ResourcePage] Initializing...');
        
        // Add required styles
        injectRequiredStyles();
        
        // Add table search functionality
        initTableSearch();
        
        // Add popup handlers - FIXED VERSION
        initPopupHandlers();
        
        // Format tables
        formatTables();
    }
    
    // Inject required CSS
    function injectRequiredStyles() {
        if ($('#resourcePageGadgetStyles').length === 0) {
            var styles = `<style id="resourcePageGadgetStyles">
                /* Span button styles */
                .dune-action-button {
                    display: inline-block !important;
                    cursor: pointer !important;
                    user-select: none !important;
                    -webkit-user-select: none !important;
                    -moz-user-select: none !important;
                    -ms-user-select: none !important;
                }
                
                /* Loading spinner */
                .loading-spinner {
                    font-size: 48px;
                    animation: spin 1s linear infinite;
                }
                
                @keyframes spin {
                    from { transform: rotate(0deg); }
                    to { transform: rotate(360deg); }
                }
                
                /* Sort indicators */
                .recipe-popup-table th {
                    cursor: pointer;
                    user-select: none;
                }
                
                .recipe-popup-table th.sort-asc::after {
                    content: " ↑";
                    color: #fce7c8;
                }
                
                .recipe-popup-table th.sort-desc::after {
                    content: " ↓";
                    color: #fce7c8;
                }
                
                /* Popup note */
                .popup-note {
                    background: rgba(252,231,200,.1);
                    border: 1px solid rgba(252,231,200,.3);
                    padding: 10px;
                    margin-bottom: 15px;
                    color: #E3BB7A;
                    text-align: center;
                }
                
                /* Recipe count */
                .recipe-count {
                    position: absolute;
                    right: 60px;
                    top: 50%;
                    transform: translateY(-50%);
                    color: #E3BB7A;
                    font-size: 14px;
                }
            </style>`;
            
            $('head').append(styles);
        }
    }
    
    // Format tables to ensure proper styling
    function formatTables() {
        $('.recipe-table').each(function() {
            var $table = $(this);
            
            // Ensure proper header styling
            $table.find('thead tr').addClass('tr-dark');
        });
    }
    
    // Initialize table search functionality
    function initTableSearch() {
        // Crafted With search
        $('#craftedWithSearch').on('input', function() {
            filterTable('#craftedWithTable', $(this).val());
        });
        
        // Crafted From search
        $('#craftedFromSearch').on('input', function() {
            filterTable('#craftedFromTable', $(this).val());
        });
    }
    
    // Filter table rows based on search input
    function filterTable(tableId, searchTerm) {
        var $table = $(tableId);
        var term = searchTerm.toLowerCase();
        
        $table.find('tbody tr').each(function() {
            var $row = $(this);
            var text = $row.text().toLowerCase();
            
            if (text.indexOf(term) > -1) {
                $row.show();
            } else {
                $row.hide();
            }
        });
    }
    
    // FIXED: Initialize popup handlers with event delegation
    function initPopupHandlers() {
        console.log('[ResourcePage] Setting up popup handlers...');
        
        // View All Crafted With - using event delegation
        $(document).on('click', '.view-all-crafted-with', function(e) {
            e.preventDefault();
            e.stopPropagation();
            console.log('[ResourcePage] View All Crafted With clicked');
            
            var $element = $(this);
            var resourceName = $element.data('resource') || $element.attr('data-resource');
            
            if (!resourceName) {
                console.error('[ResourcePage] No resource name found!');
                return false;
            }
            
            showRecipePopup('craftedWith', resourceName);
            return false;
        });
        
        // View All Crafted From - using event delegation
        $(document).on('click', '.view-all-crafted-from', function(e) {
            e.preventDefault();
            e.stopPropagation();
            console.log('[ResourcePage] View All Crafted From clicked');
            
            var $element = $(this);
            var resourceName = $element.data('resource') || $element.attr('data-resource');
            
            if (!resourceName) {
                console.error('[ResourcePage] No resource name found!');
                return false;
            }
            
            showRecipePopup('craftedFrom', resourceName);
            return false;
        });
        
        // Close popup - overlay click
        $(document).on('click', '.recipe-popup-overlay', function(e) {
            if (e.target === this) {
                console.log('[ResourcePage] Overlay clicked - closing popup');
                closeRecipePopup();
            }
        });
        
        // Close popup - close button
        $(document).on('click', '.popup-close', function(e) {
            e.preventDefault();
            e.stopPropagation();
            console.log('[ResourcePage] Close button clicked');
            closeRecipePopup();
            return false;
        });
        
        // Escape key to close popup
        $(document).on('keydown', function(e) {
            if (e.key === 'Escape' && $('.recipe-popup-overlay').length > 0) {
                console.log('[ResourcePage] Escape key pressed - closing popup');
                closeRecipePopup();
            }
        });
        
        // Prevent text selection on span buttons
        $(document).on('selectstart', '.dune-action-button', function(e) {
            e.preventDefault();
            return false;
        });
    }
    
    // Show recipe popup
    function showRecipePopup(type, resourceName) {
        console.log('[ResourcePage] Showing popup for:', type, resourceName);
        
        var title = type === 'craftedWith' 
            ? 'All Recipes Using ' + resourceName
            : 'All Recipes Creating ' + resourceName;
        
        // Show loading state
        showLoadingPopup();
        
        // First, check for full data in hidden elements
        var dataSelector = '.' + type.replace('craftedWith', 'crafted-with').replace('craftedFrom', 'crafted-from') + '-full-data';
        var $fullDataElement = $(dataSelector);
        
        console.log('[ResourcePage] Looking for data in:', dataSelector);
        
        if ($fullDataElement.length && $fullDataElement.text().trim()) {
            try {
                // Parse JSON data from hidden element
                var jsonText = $fullDataElement.text().trim();
                console.log('[ResourcePage] Found JSON data, parsing...');
                
                var jsonData = JSON.parse(jsonText);
                var rows = jsonData.results || jsonData || [];
                
                console.log('[ResourcePage] Parsed', rows.length, 'rows');
                
                // Transform to expected format
                var formattedRows = rows.map(function(row) {
                    return {
                        item: row.OutputItem || row.output_item || '',
                        resources: formatResourceList(row.Resources || row.resources || ''),
                        stations: row.Stations || row.station || row.stations || '',
                        water: row.WaterML || row.water_ml || 'N/A',
                        craftTime: row.CraftTime || row.craft_time || 'N/A',
                        schematic: row.Schematic || row.schematic || 'N/A'
                    };
                });
                
                displayPopup(title, formattedRows, false);
                return;
            } catch (e) {
                console.error('[ResourcePage] Failed to parse full recipe data:', e);
            }
        }
        
        // Fallback to API call
        console.log('[ResourcePage] No cached data found, making API call...');
        fetchRecipeDataViaAPI(type, resourceName, title);
    }
    
    // Format resource list for display
    function formatResourceList(resources) {
        if (!resources) return '';
        
        // If it's already HTML formatted, return as is
        if (resources.indexOf('<br') > -1) {
            return resources;
        }
        
        // Otherwise, split by semicolons and format
        return resources.split(';').map(function(r) {
            return r.trim();
        }).filter(Boolean).join('<br>');
    }
    
    // Fetch recipe data via API
    function fetchRecipeDataViaAPI(type, resourceName, title) {
        $.ajax({
            url: mw.util.wikiScript('api'),
            data: {
                action: 'parse',
                format: 'json',
                page: mw.config.get('wgPageName'),
                prop: 'text'
            },
            success: function(response) {
                if (response.parse && response.parse.text) {
                    var html = response.parse.text['*'];
                    var $html = $('<div>').html(html);
                    
                    // Find the appropriate table
                    var tableId = type === 'craftedWith' ? '#craftedWithTable' : '#craftedFromTable';
                    var $table = $html.find(tableId);
                    
                    var rows = [];
                    $table.find('tbody tr').each(function() {
                        var $cells = $(this).find('td');
                        if ($cells.length >= 3) {
                            rows.push({
                                item: $cells.eq(0).html(),
                                resources: $cells.eq(1).html(),
                                stations: $cells.eq(2).html(),
                                water: 'N/A',
                                craftTime: 'N/A',
                                schematic: 'N/A'
                            });
                        }
                    });
                    
                    console.log('[ResourcePage] Found', rows.length, 'rows via API');
                    displayPopup(title, rows, false);
                }
            },
            error: function() {
                console.error('[ResourcePage] API call failed, using visible rows');
                var rows = getVisibleRows(type);
                displayPopup(title, rows, true);
            }
        });
    }
    
    // Get visible rows as fallback
    function getVisibleRows(type) {
        var $table = $(type === 'craftedWith' ? '#craftedWithTable' : '#craftedFromTable');
        var rows = [];
        
        $table.find('tbody tr:visible').each(function() {
            var $row = $(this);
            var $cells = $row.find('td');
            
            if ($cells.length >= 3) {
                rows.push({
                    item: $cells.eq(0).html(),
                    resources: $cells.eq(1).html(),
                    stations: $cells.eq(2).html(),
                    water: 'N/A',
                    craftTime: 'N/A',
                    schematic: 'N/A'
                });
            }
        });
        
        return rows;
    }
    
    // Show loading popup
    function showLoadingPopup() {
        var loadingHtml = '<div class="recipe-popup-overlay active">' +
            '<div class="recipe-popup">' +
                '<div class="popup-content" style="text-align: center; padding: 60px;">' +
                    '<div style="font-size: 24px; color: #fce7c8; margin-bottom: 20px;">Loading recipes...</div>' +
                    '<div class="loading-spinner">⟳</div>' +
                '</div>' +
            '</div>' +
        '</div>';
        
        $('body').append(loadingHtml);
    }
    
    // Display popup with data
    function displayPopup(title, rows, isLimited) {
        // Remove any existing popup
        $('.recipe-popup-overlay').remove();
        
        // Build table rows HTML
        var tableRowsHtml = '';
        rows.forEach(function(row) {
            tableRowsHtml += '<tr>' +
                '<td>' + row.item + '</td>' +
                '<td>' + row.resources + '</td>' +
                '<td>' + row.stations + '</td>' +
                '<td>' + row.water + '</td>' +
                '<td>' + row.craftTime + '</td>' +
                '<td>' + row.schematic + '</td>' +
            '</tr>';
        });
        
        // Note if data is limited
        var limitedNote = isLimited ? 
            '<div class="popup-note">Showing only visible recipes. Full data may require page reload.</div>' : 
            '';
        
        // Build popup HTML
        var popupHtml = '<div class="recipe-popup-overlay">' +
            '<div class="recipe-popup">' +
                '<div class="popup-header">' +
                    '<h3 class="popup-title">' + title + '</h3>' +
                    '<span class="recipe-count">' + rows.length + ' recipes</span>' +
                    '<button class="popup-close">×</button>' +
                '</div>' +
                '<div class="popup-content">' +
                    limitedNote +
                    '<div class="popup-search-container">' +
                        '<input type="text" class="popup-search-input" placeholder="Search recipes..." id="popupSearchInput">' +
                    '</div>' +
                    '<div class="popup-table-wrapper">' +
                        '<table class="infobox-dune-standard-table recipe-popup-table" id="popupRecipeTable">' +
                            '<thead>' +
                                '<tr class="tr-dark">' +
                                    '<th data-sort="item">Item Created</th>' +
                                    '<th data-sort="resources">Resources Needed</th>' +
                                    '<th data-sort="stations">Stations</th>' +
                                    '<th data-sort="water">Water (mL)</th>' +
                                    '<th data-sort="time">Craft Time</th>' +
                                    '<th data-sort="schematic">Schematic</th>' +
                                '</tr>' +
                            '</thead>' +
                            '<tbody>' + tableRowsHtml + '</tbody>' +
                        '</table>' +
                    '</div>' +
                '</div>' +
            '</div>' +
        '</div>';
        
        // Add to page
        $('body').append(popupHtml);
        
        // Activate popup
        setTimeout(function() {
            $('.recipe-popup-overlay').addClass('active');
        }, 10);
        
        // Initialize popup functionality
        initPopupFunctionality();
    }
    
    // Initialize popup functionality (search and sort)
    function initPopupFunctionality() {
        // Search functionality
        $('#popupSearchInput').on('input', function() {
            var searchTerm = $(this).val().toLowerCase();
            
            $('#popupRecipeTable tbody tr').each(function() {
                var $row = $(this);
                var text = $row.text().toLowerCase();
                
                if (text.indexOf(searchTerm) > -1) {
                    $row.show();
                } else {
                    $row.hide();
                }
            });
        });
        
        // Sort functionality
        $('#popupRecipeTable thead th').on('click', function() {
            var $th = $(this);
            var sortKey = $th.data('sort');
            var $tbody = $('#popupRecipeTable tbody');
            var rows = $tbody.find('tr').toArray();
            
            // Determine sort direction
            var ascending = !$th.hasClass('sort-desc');
            
            // Remove sort classes from all headers
            $('#popupRecipeTable thead th').removeClass('sort-asc sort-desc');
            
            // Add appropriate class to clicked header
            $th.addClass(ascending ? 'sort-asc' : 'sort-desc');
            
            // Sort rows
            rows.sort(function(a, b) {
                var aText = $(a).find('td').eq($th.index()).text().trim();
                var bText = $(b).find('td').eq($th.index()).text().trim();
                
                // Try to parse as number
                var aNum = parseFloat(aText);
                var bNum = parseFloat(bText);
                
                if (!isNaN(aNum) && !isNaN(bNum)) {
                    return ascending ? aNum - bNum : bNum - aNum;
                }
                
                // Sort as text
                return ascending 
                    ? aText.localeCompare(bText)
                    : bText.localeCompare(aText);
            });
            
            // Re-append sorted rows
            $tbody.empty();
            rows.forEach(function(row) {
                $tbody.append(row);
            });
        });
        
        // Focus search input
        $('#popupSearchInput').focus();
    }
    
    // Close recipe popup
    function closeRecipePopup() {
        $('.recipe-popup-overlay').removeClass('active');
        
        setTimeout(function() {
            $('.recipe-popup-overlay').remove();
        }, 300);
    }
    
    // Initialize when DOM is ready
    $(function() {
        init();
    });
    
})(jQuery, mediaWiki);