MediaWiki:Gadget-ResourcePage.js
From Dune Awakening DB
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
* 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;
}
// Cache for storing recipe data
var recipeCache = {
craftedWith: null,
craftedFrom: null
};
// Initialize module
function init() {
// Add table search functionality
initTableSearch();
// Add popup handlers
initPopupHandlers();
// Format tables
formatTables();
// Add animation styles once
addAnimationStyles();
}
// 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();
}
});
}
// Initialize popup handlers
function initPopupHandlers() {
// View All Crafted With button
$(document).on('click', '.view-all-crafted-with', function(e) {
e.preventDefault();
var resourceName = $(this).data('resource');
showRecipePopup('craftedWith', resourceName);
});
// View All Crafted From button
$(document).on('click', '.view-all-crafted-from', function(e) {
e.preventDefault();
var resourceName = $(this).data('resource');
showRecipePopup('craftedFrom', resourceName);
});
// Close popup handlers
$(document).on('click', '.recipe-popup-overlay, .popup-close', function(e) {
if (e.target === this || $(e.target).hasClass('popup-close')) {
closeRecipePopup();
}
});
// Escape key to close popup
$(document).on('keydown', function(e) {
if (e.key === 'Escape' && $('.recipe-popup-overlay').hasClass('active')) {
closeRecipePopup();
}
});
}
// Show recipe popup
function showRecipePopup(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 $fullDataElement = $('.' + type.replace('craftedWith', 'crafted-with').replace('craftedFrom', 'crafted-from') + '-full-data');
if ($fullDataElement.length && $fullDataElement.text().trim()) {
try {
// Parse JSON data from hidden element
var jsonData = JSON.parse($fullDataElement.text());
var rows = jsonData.results || [];
// Transform to expected format
var formattedRows = rows.map(function(row) {
return {
item: row.OutputItem || row.output_item || '',
resources: row.Resources || row.resources || '',
stations: row.Stations || row.station || '',
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.warn('Failed to parse full recipe data:', e);
}
}
// Fallback to visible data
var $table = $(type === 'craftedWith' ? '#craftedWithTable' : '#craftedFromTable');
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">Note: Showing only visible recipes. Full data requires server-side implementation.</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>' +
'<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);
});
});
}
// Close recipe popup
function closeRecipePopup() {
$('.recipe-popup-overlay').removeClass('active');
setTimeout(function() {
$('.recipe-popup-overlay').remove();
}, 300);
}
// Add required CSS for animations
function addAnimationStyles() {
if ($('#resourcePageGadgetStyles').length === 0) {
var styles = '<style id="resourcePageGadgetStyles">' +
'.loading-spinner { font-size: 48px; animation: spin 1s linear infinite; }' +
'@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }' +
'.recipe-popup-table th.sort-asc::after { content: " ↑"; color: #fce7c8; }' +
'.recipe-popup-table th.sort-desc::after { content: " ↓"; color: #fce7c8; }' +
'.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; }' +
'</style>';
$('head').append(styles);
}
}
// Initialize when DOM is ready
$(init);
})(jQuery, mediaWiki);