Actions

MediaWiki

MediaWiki:Common.js

From Dune Awakening DB

Revision as of 01:31, 31 May 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.
/* ========================================
   MediaWiki:Common.js - Fixed Header Spacing
   FIXED VERSION - Addresses all positioning issues
   ======================================== */
console.log('%c[JourneySystem] common.js loaded','color:teal;font-weight:bold');

$(document).ready(function() {
    // FIXED: Check if page has breadcrumb navigation
    var hasBreadcrumb = $('.dune-breadcrumb-nav').length > 0;
    if (hasBreadcrumb) {
        $('body').addClass('has-breadcrumb');
    }
    
    // HEADER FIX - Wait for page to load then force header
    setTimeout(function() {
        // Remove ALL existing headers and nav attempts
        $('.mw-header, #mw-header-container, nav.tab-bar, .tab-bar').remove();
        
        // Hide the page title and extra elements completely
        $('.firstHeading, h1.title, .mw-page-title-main, #firstHeading, #tagline, h3#tagline, #contentSub, .mw-content-subtitle').remove();
        
        // Remove any MediaWiki notices that might appear
        $('#siteNotice, .mw-indicators, .printfooter, .catlinks').remove();
        
        // Create the navigation bar HTML with inline styles
        var headerHTML = `
            <div id="dune-header" style="position: fixed; top: 0; left: 0; right: 0; height: 60px; background: linear-gradient(180deg, rgba(20, 18, 28, 0.95) 0%, rgba(10, 8, 16, 0.98) 100%); border-bottom: 3px solid rgba(252, 231, 200, 0.4); box-shadow: 0 2px 20px rgba(0,0,0,0.8); z-index: 99999; display: flex; align-items: center;">
                <div style="width: 100%; max-width: 1600px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; padding: 0 20px; height: 100%;">
                    <a href="/wiki/Main_Page" style="font-family: 'Orbitron', sans-serif; font-size: 22px; color: #fce7c8; text-transform: uppercase; letter-spacing: 3px; font-weight: 700; text-shadow: 0 0 15px rgba(252, 231, 200, 0.4); text-decoration: none; white-space: nowrap;">DUNE DB</a>
                    
                    <nav style="display: flex; gap: 0; margin: 0 20px; height: 100%;">
                        <a href="/wiki/Items" style="display: flex; align-items: center; padding: 0 18px; height: 100%; color: rgba(252, 231, 200, 0.8); font-family: 'Rajdhani', sans-serif; font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; text-decoration: none; border-left: 1px solid rgba(252, 231, 200, 0.1); border-right: 1px solid rgba(252, 231, 200, 0.1); transition: all 0.3s ease; background: transparent;" onmouseover="this.style.background='rgba(252, 231, 200, 0.1)'; this.style.color='#fce7c8';" onmouseout="this.style.background='transparent'; this.style.color='rgba(252, 231, 200, 0.8)';">◇ Items</a>
                        <a href="/wiki/Crafting" style="display: flex; align-items: center; padding: 0 18px; height: 100%; color: rgba(252, 231, 200, 0.8); font-family: 'Rajdhani', sans-serif; font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; text-decoration: none; border-right: 1px solid rgba(252, 231, 200, 0.1); transition: all 0.3s ease; background: transparent;" onmouseover="this.style.background='rgba(252, 231, 200, 0.1)'; this.style.color='#fce7c8';" onmouseout="this.style.background='transparent'; this.style.color='rgba(252, 231, 200, 0.8)';">⚒ Crafting</a>
                        <a href="/wiki/Building" style="display: flex; align-items: center; padding: 0 18px; height: 100%; color: rgba(252, 231, 200, 0.8); font-family: 'Rajdhani', sans-serif; font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; text-decoration: none; border-right: 1px solid rgba(252, 231, 200, 0.1); transition: all 0.3s ease; background: transparent;" onmouseover="this.style.background='rgba(252, 231, 200, 0.1)'; this.style.color='#fce7c8';" onmouseout="this.style.background='transparent'; this.style.color='rgba(252, 231, 200, 0.8)';">⌂ Building</a>
                        <a href="/wiki/Tech_Tree" style="display: flex; align-items: center; padding: 0 18px; height: 100%; color: rgba(252, 231, 200, 0.8); font-family: 'Rajdhani', sans-serif; font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; text-decoration: none; border-right: 1px solid rgba(252, 231, 200, 0.1); transition: all 0.3s ease; background: transparent;" onmouseover="this.style.background='rgba(252, 231, 200, 0.1)'; this.style.color='#fce7c8';" onmouseout="this.style.background='transparent'; this.style.color='rgba(252, 231, 200, 0.8)';">⬢ Tech Tree</a>
                        <a href="/wiki/Journeys" style="display: flex; align-items: center; padding: 0 18px; height: 100%; color: rgba(252, 231, 200, 0.8); font-family: 'Rajdhani', sans-serif; font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; text-decoration: none; border-right: 1px solid rgba(252, 231, 200, 0.1); transition: all 0.3s ease; background: transparent;" onmouseover="this.style.background='rgba(252, 231, 200, 0.1)'; this.style.color='#fce7c8';" onmouseout="this.style.background='transparent'; this.style.color='rgba(252, 231, 200, 0.8)';">➤ Journeys</a>
                        <a href="/wiki/Skills" style="display: flex; align-items: center; padding: 0 18px; height: 100%; color: rgba(252, 231, 200, 0.8); font-family: 'Rajdhani', sans-serif; font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; text-decoration: none; border-right: 1px solid rgba(252, 231, 200, 0.1); transition: all 0.3s ease; background: transparent;" onmouseover="this.style.background='rgba(252, 231, 200, 0.1)'; this.style.color='#fce7c8';" onmouseout="this.style.background='transparent'; this.style.color='rgba(252, 231, 200, 0.8)';">★ Skills</a>
                        <a href="/wiki/Map" style="display: flex; align-items: center; padding: 0 18px; height: 100%; color: rgba(252, 231, 200, 0.8); font-family: 'Rajdhani', sans-serif; font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; text-decoration: none; border-right: 1px solid rgba(252, 231, 200, 0.1); transition: all 0.3s ease; background: transparent;" onmouseover="this.style.background='rgba(252, 231, 200, 0.1)'; this.style.color='#fce7c8';" onmouseout="this.style.background='transparent'; this.style.color='rgba(252, 231, 200, 0.8)';">🗺 Map</a>
                    </nav>
                    
                    <div style="display: flex; align-items: center;">
                        <form action="/index.php" method="get" style="display: flex; align-items: center; gap: 0; height: 38px;">
                            <input type="search" name="search" placeholder="Search wiki..." style="background: rgba(0, 0, 2, 0.8); border: 2px solid rgba(252, 231, 200, 0.3); border-radius: 0; color: #fce7c8; padding: 0 12px; width: 200px; font-size: 13px; font-family: 'Rajdhani', sans-serif; transition: all 0.3s ease; outline: none; height: 38px; box-sizing: border-box; margin: 0; vertical-align: middle;" onfocus="this.style.borderColor='#fce7c8'; this.style.boxShadow='0 0 10px rgba(252, 231, 200, 0.3)'; this.style.width='250px';" onblur="this.style.borderColor='rgba(252, 231, 200, 0.3)'; this.style.boxShadow='none'; this.style.width='200px';">
                            <button type="submit" style="background: rgba(252, 231, 200, 0.1); border: 2px solid rgba(252, 231, 200, 0.3); border-left: none; color: #fce7c8; padding: 0 12px; cursor: pointer; font-size: 14px; transition: all 0.3s ease; height: 38px; box-sizing: border-box; margin: 0; vertical-align: middle; display: flex; align-items: center; justify-content: center;" onmouseover="this.style.background='rgba(252, 231, 200, 0.2)'; this.style.borderColor='#fce7c8';" onmouseout="this.style.background='rgba(252, 231, 200, 0.1)'; this.style.borderColor='rgba(252, 231, 200, 0.3)';"><span>🔍</span></button>
                        </form>
                    </div>
                </div>
            </div>
        `;
        
        // Check if header already exists to avoid duplicates
        if ($('#dune-header').length === 0) {
            $('body').prepend(headerHTML);
        }
        
        // CRITICAL: Force proper spacing
        $('body').css({
            'padding-top': '60px',
            'margin-top': '0',
            'overflow-y': 'auto',
            'height': 'auto'
        });
        
        // FIXED: Different spacing for pages with/without breadcrumb
        if (hasBreadcrumb) {
            // Pages with breadcrumb - content starts immediately after header
            $('#content, .mw-body, #mw-content-text, .mw-body-content, .mw-parser-output, #main-section, .main-section, #page-content').css({
                'padding-top': '0',
                'margin-top': '0'
            });
            
            // Breadcrumb should have proper spacing from header
            $('.dune-breadcrumb-nav').css({
                'margin-top': '20px',
                'margin-bottom': '20px'
            });
        } else {
            // Pages without breadcrumb - add padding to content
            $('#content, .mw-body, #mw-content-text, .mw-body-content, .mw-parser-output, #main-section, .main-section, #page-content').css({
                'padding-top': '20px',
                'margin-top': '0'
            });
        }
        
        // Force first element to start at top (unless it's breadcrumb)
        $('.mw-parser-output > *:first-child:not(.dune-breadcrumb-nav), .mw-body-content > *:first-child:not(.dune-breadcrumb-nav)').css({
            'margin-top': '0',
            'padding-top': '0'
        });
        
        // Remove sidebar space
        $('#sidebar, .columns.large-2.medium-3').css('display', 'none');
        $('#page-content, .columns.large-10.medium-9').css({
            'width': '100%',
            'max-width': '100%',
            'margin': '0',
            'padding': '0'
        });
        
        // Handle Actions button positioning
        $('#p-cactions').css({
            'position': 'fixed',
            'top': '11px',
            'right': '20px',
            'z-index': '100001',
            'margin': '0',
            'padding': '0',
            'height': '38px'
        });
        
        // Remove spacing from all column wrappers
        $('.row > .columns').css({
            'padding-top': '0',
            'margin-top': '0'
        });
        
        // FIXED: Ensure page content can scroll
        $('#content').css({
            'min-height': 'calc(100vh - 60px)',
            'position': 'relative',
            'z-index': '1'
        });
        
        // Remove any floating MediaWiki elements
        $('.mw-jump-link, .mw-redirectedfrom, #siteNotice').remove();
        
    }, 100);
});

/* ===== RADIAL MENU CODE ===== */
function waitForRadialData(callback, attempts = 0) {
  const dataTag = document.getElementById('radialMenuData');
  if (dataTag) {
    try {
      const menuItems = JSON.parse(dataTag.innerText || dataTag.textContent);
      console.log("✅ Loaded radialMenuData:", menuItems);
      callback(menuItems);
    } catch (e) {
      console.error("❌ Failed to parse radialMenuData", e);
    }
  } else if (attempts < 20) {
    setTimeout(() => waitForRadialData(callback, attempts + 1), 100);
  } else {
    console.warn("⚠️ radialMenuData tag not found after timeout.");
  }
}

$(document).ready(function () {
  waitForRadialData(function (menuItems) {
    const isMobile = () => window.innerWidth <= 768;

    let radialItemsHTML = '';
    menuItems.forEach((item, index) => {
      const id = item.category.toLowerCase().replace(/\s+/g, '-');
      item.id = id;

      radialItemsHTML += `
        <div class="dune-radial-item-container ${item.position}">
          <a data-id="${id}" class="dune-radial-item">
            <img src="${item.icon}" alt="${item.name}" class="dune-radial-icon">
            <span class="dune-radial-tooltip">${item.name}</span>
          </a>
        </div>
      `;
    });

    const centerButtonHTML = `
      <a href="https://dunedb.com/Main_Page" class="dune-radial-center">
        <img src="https://dunedb.com/images/9/99/HomeNavIcon.png" alt="Home" class="dune-radial-icon">
        <span class="dune-radial-tooltip">Main Page</span>
      </a>
    `;

    const radialMenuHTML = `
      <div id="duneRadialMenu" class="dune-radial-menu">
        <div class="dune-radial-background">
          <div class="dune-radial-circle outer"></div>
          <div class="dune-radial-circle middle"></div>
          <div class="dune-radial-circle inner"></div>
        </div>
        ${centerButtonHTML}
        ${radialItemsHTML}
      </div>
      <div id="duneRadialOverlay" class="dune-radial-overlay"></div>
    `;

    const subcategoryContainerHTML = `<div id="duneSubcategoryContainer" class="dune-subcategory-container"></div>`;
    $('body').append(radialMenuHTML);
    $('body').append(subcategoryContainerHTML);

    if (isMobile()) {
      $('#duneRadialMenu').addClass('mobile-grid');
    }

    const showSubcategories = (itemId, event) => {
      const item = menuItems.find(i => i.id === itemId);
      if (!item || !item.subcategories) return;

      $('.dune-radial-item').removeClass('selected');
      $(`.dune-radial-item[data-id="${itemId}"]`).addClass('selected');

      let subcategoryHTML = `
        <div class="dune-subcategory-header">
          <img src="${item.icon}" alt="${item.name}" class="dune-subcategory-icon">
          <span>${item.name}</span>
        </div>
        <div class="dune-subcategory-items">
      `;

      item.subcategories.forEach((sub, index) => {
        subcategoryHTML += `
          <a href="${sub.url}" class="dune-subcategory-item" style="--item-index: ${index}">
            <span class="dune-subcategory-name">${sub.name}</span>
          </a>
        `;
      });

      subcategoryHTML += `
        </div>
        <div class="dune-subcategory-footer">
          <a href="${item.url}" class="dune-subcategory-all">View All ${item.name}</a>
        </div>
      `;

      const $subcategoryContainer = $('#duneSubcategoryContainer');
      $subcategoryContainer.html(subcategoryHTML);

      $subcategoryContainer.removeClass((index, className) => {
        return (className.match(/from-\S+/g) || []).join(' ');
      });
      $subcategoryContainer.addClass(`from-${item.position}`);
      $subcategoryContainer.addClass('active');

      event.stopPropagation();
    };

    const hideSubcategories = () => {
      $('#duneSubcategoryContainer').removeClass('active');
      $('.dune-radial-item').removeClass('selected');
    };

    const toggleRadialMenu = () => {
      if ($('#duneRadialMenu').hasClass('active')) {
        hideSubcategories();
        $('#duneRadialMenu').removeClass('active');
        $('#duneRadialOverlay').removeClass('active');
      } else {
        $('#duneRadialMenu').addClass('active');
        $('#duneRadialOverlay').addClass('active');
        $('.dune-radial-item-container').each(function (index) {
          const $item = $(this);
          setTimeout(() => {
            $item.addClass('animated');
          }, index * 50);
        });
        setTimeout(() => {
          $('.dune-radial-center').addClass('animated');
        }, menuItems.length * 50);
      }
    };

    $(document).on('click', '#duneLogoBtn, #menuRadialTrigger img', function (e) {
      console.log("✅ Radial trigger clicked");
      e.preventDefault();
      e.stopPropagation();
      toggleRadialMenu();
    });

    $(document).on('click', '.dune-radial-item', function (e) {
      e.preventDefault();
      e.stopPropagation();
      const itemId = $(this).data('id');
      if ($(this).hasClass('selected')) {
        hideSubcategories();
      } else {
        showSubcategories(itemId, e);
      }
    });

    $(document).on('click', '#duneRadialOverlay', function () {
      hideSubcategories();
      $('#duneRadialMenu').removeClass('active');
      $('#duneRadialOverlay').removeClass('active');
    });

    $(document).on('click', 'li.name.logo a[href="/Main_Page"]', function (e) {
      e.preventDefault();
      toggleRadialMenu();
    });

    $(document).on('keydown', function (e) {
      if (e.key === 'Escape') {
        if ($('#duneSubcategoryContainer').hasClass('active')) {
          hideSubcategories();
        } else if ($('#duneRadialMenu').hasClass('active')) {
          $('#duneRadialMenu').removeClass('active');
          $('#duneRadialOverlay').removeClass('active');
        }
      }
    });

    $(window).on('resize', function () {
      if (isMobile()) {
        $('#duneRadialMenu').addClass('mobile-grid');
      } else {
        $('#duneRadialMenu').removeClass('mobile-grid');
      }
    });
  });
});

/* Used-In dynamic filters & search */
mw.hook( 'wikipage.content' ).add( function ( $content ) {

  const $holder = $content.find( '#usedInFilterHolder' );
  const $table  = $content.find( '#usedInTable' );

  if ( !$holder.length || !$table.length ) return;

  const rowsData = [];
  const stations = new Set();
  const types    = new Set();

  $table.find( 'tbody tr' ).each( function () {
    const $tds     = $( this ).children( 'td' );
    const output   = $tds.eq( 0 ).text().trim();
    const type     = $tds.eq( 1 ).text().trim();
    const station  = $tds.last().text().trim();

    rowsData.push( { tr: this, output, type, station } );
    stations.add( station );
    types.add( type );
  } );

  const makeSel = ( id, label, opts ) => {
    const $sel = $( '<select>', { id, class: 'game-button', css:{margin:'6px 4px 6px 0'} } )
                  .append( $( '<option>', { value:'', text:label } ) );
    opts.forEach( o => $sel.append( $( '<option>', { value:o, text:o } ) ) );
    return $sel;
  };

  const $stationSel = makeSel( 'filterStation', 'All Stations', [ ...stations ].sort() );
  const $typeSel    = makeSel( 'filterType',    'All Types',    [ ...types ].sort() );
  const $search     = $( '<input>', {
                          type:'text',
                          placeholder:'Search…',
                          css:{ margin:'6px 0', padding:'4px', width:'140px',
                               'background':'#111', color:'#fff', border:'1px solid #555' }
                        } );

  $holder.append( $stationSel, $typeSel, $search );

  function applyFilters() {
    const sVal = $stationSel.val();
    const tVal = $typeSel.val();
    const q    = $search.val().toLowerCase();

    rowsData.forEach( ({ tr, output, type, station }) => {
      const show =
        (!sVal || station === sVal) &&
        (!tVal || type    === tVal) &&
        (!q    || output.toLowerCase().includes( q ));
      tr.style.display = show ? '' : 'none';
    } );
  }

  $stationSel.on( 'change', applyFilters );
  $typeSel   .on( 'change', applyFilters );
  $search    .on( 'input',  applyFilters );
} );

/* Lazy-load background image Blur */
document.addEventListener('DOMContentLoaded', () => {
  const hero = document.querySelector('.dune-hero');
  if (hero && !hero.style.backgroundImage) {
    hero.style.backgroundImage =
      'url(/images/7/70/Arrakis_dunes.jpg?2025)';
  }

  const radial = document.querySelector('.radial-menu-wrapper');
  if (!radial) return;
  const y = radial.offsetTop;
  window.addEventListener('scroll', () => {
    radial.classList.toggle('is-fixed', window.scrollY > y);
  });
});

/**
 * Journey System JavaScript for MediaWiki
 * Enhanced with proper task counting and initialization
 */
(function($, mw) {
    'use strict';
     console.log('%c[JourneySystem] common.js loaded','color:teal;font-weight:bold');
    // Only run on pages with journey system
    if (!$('.journey-main-container').length) return;
    
    window.DuneJourneySystem = {
        // Store current active journey
        currentActiveJourney: null,
        journeyData: {},
        
        // Initialize the system
        init: function() {
            var self = this;
             console.log('[JourneySystem] init() called');

            // Parse journey data from the page
            self.parseJourneyData();
            
            // Set up event listeners
            self.setupEventListeners();
            
            // Check for URL hash
            self.checkUrlHash();

            
        },
        
        setInitialAccordionState: function() {
		    console.log('[JourneySystem] Setting initial accordion state');
		    
		    // Add collapsed class to all objectives first
		    $('.objective-item').addClass('collapsed');
		    
		    // Remove collapsed class from first objective only
		    $('.objective-item:first').removeClass('collapsed');
		},
		        
        // Parse journey data embedded in the page
        parseJourneyData: function() {
            var self = this;
            
            // Journey data is embedded as JSON in a script tag
            var dataElement = $('#journey-data-json');
            if (dataElement.length) {
                try {
                    self.journeyData = JSON.parse(dataElement.text());
                } catch (e) {
                    console.error('Failed to parse journey data:', e);
                }
            }
            
            // Alternative: Use data from HTML attributes if JSON parsing fails
            if (!self.journeyData || Object.keys(self.journeyData).length === 0) {
                if (window.journeyDataFromHTML) {
                    self.journeyData = window.journeyDataFromHTML;
                }
            }
        },
        
        // Set up all event listeners
        setupEventListeners: function() {
            var self = this;
            
            // Journey card clicks
            $(document).on('click', '.journey-card', function(e) {
                e.preventDefault();
                var journeyId = $(this).data('journey-id');
                self.loadJourneyDetails(journeyId);
            });
            
            // Objective accordion
				$(document).on('click', '.objective-header', function(e) {
				    e.preventDefault();
				    e.stopPropagation();
				    
				    console.log('[Toggle] Click detected');
				    
				    var $objectiveItem = $(this).closest('.objective-item');
				    
				    console.log('[Toggle] Current state:', $objectiveItem.hasClass('collapsed'));
				    
				    // Just toggle this item - don't touch others!
				    $objectiveItem.toggleClass('collapsed');
				    
				    console.log('[Toggle] New state:', $objectiveItem.hasClass('collapsed') ? 'closed' : 'open');
				});
							
			// Prevent clicks on progress from toggling accordion
			$(document).on('click', '.objective-progress', function(e) {
			    e.stopPropagation();
			});
            
            // Task checkboxes
            $(document).on('click', '.task-checkbox', function(e) {
                e.stopPropagation();
                $(this).toggleClass('completed');
                self.updateProgress();
            });
            
            // Material checkboxes
            $(document).on('click', '.material-item', function(e) {
                var checkbox = $(this).find('.material-checkbox');
                checkbox.toggleClass('completed');
                $(this).toggleClass('checked');
                self.updateMaterialProgress();
            });
            
            // Description toggle
            $(document).on('click', '.description-toggle', function(e) {
                e.preventDefault();
                var desc = $(this).closest('.journey-description');
                if (desc.hasClass('collapsed')) {
                    desc.removeClass('collapsed').addClass('expanded');
                    $(this).text('Less');
                } else {
                    desc.addClass('collapsed').removeClass('expanded');
                    $(this).text('More');
                }
            });
            
            // Action buttons  –– use delegation so it still works after re-render
			$(document).on('click', '.journey-actions .view-items', function (e) {
				    e.preventDefault();
				    e.stopPropagation();
				    console.log('[JourneySystem] View Items button clicked');
				    self.showItemPrepList();
				    return false;
				});
				
			$(document).on('click', '.journey-actions .view-guide', function (e) {
				    e.preventDefault();
				    e.stopPropagation();
				    console.log('[JourneySystem] View Guide button clicked');
				    self.showVideoGuide();
				    return false;
				});

            
            // Popup close
            $(document).on('click', '.popup-close, .journey-popup-overlay', function(e) {
                if (e.target === this || $(e.target).hasClass('popup-close')) {
                    $('.journey-popup-overlay').removeClass('active');
                    setTimeout(function() {
                        $('.journey-popup-overlay').remove();
                    }, 300);
                }
            });
        },
        
        // Journey objective subtotals
			fixTaskObjectiveIds: function() {
			    console.log('[JourneySystem] Fixing task objective IDs...');
			    
			    $('.objective-item').each(function() {
			        var $objective = $(this);
			        var objectiveId = $objective.find('.objective-header').attr('data-objective-id');
			        
			        // Set the objective ID on all tasks within this objective
			        $objective.find('.task-item').attr('data-objective-id', objectiveId);
			        
			        console.log('Fixed objective', objectiveId, 'with', $objective.find('.task-item').length, 'tasks');
			    });
			},
        
        // Initialize task counts when journey loads
        initializeTaskCounts: function() {
            var self = this;
            
            // Count tasks for each objective on initial load
            $('.objective-item').each(function() {
                var objective = $(this);
                var totalTasks = objective.find('.task-item').length;
                var progressEl = objective.find('.objective-progress');
                
                // Set initial count (0 completed out of total)
                progressEl.text('0/' + totalTasks);
            });
        },
        
        // Load journey details via AJAX
        loadJourneyDetails: function(journeyId) {
            var self = this;
            
            // Update active card styling
            $('.journey-card').removeClass('active');
            $('.journey-card[data-journey-id="' + journeyId + '"]').addClass('active');
            
            // Show loading state
            var panel = $('#journeyDetailsPanel');
            panel.addClass('active loading');
            
            // Make AJAX request to get journey details
            $.ajax({
                url: mw.util.wikiScript('api'),
                data: {
                    action: 'parse',
                    format: 'json',
                    text: '{{JourneyDetails|id=' + journeyId + '}}',
                    contentmodel: 'wikitext'
                },
                success: function(response) {
				    if (response.parse && response.parse.text) {
				        var content = response.parse.text['*'];
				        panel.html(content);
				        panel.removeClass('loading');
				        console.log('[JourneySystem] AJAX success – journey', journeyId, 'loaded');
				        
				        // Store current journey data
				        self.currentActiveJourney = self.findJourneyById(journeyId);
				        console.log('[JourneySystem] Current active journey set to:', self.currentActiveJourney);
				        
				        // Give DOM time to settle before counting
				        setTimeout(function() {
				        	$('[class*="objective-item"]').each(function() {
						        var currentClass = $(this).attr('class');
						        if (currentClass.includes('objective-itemcollapsed')) {
						            $(this).attr('class', 'objective-item collapsed');
						        }
						    });
				        	
				            // Initialize task counts
				            self.initializeTaskCounts();
				            self.updateProgress();
				            
				            // Check for long descriptions
				            self.checkDescriptionLength();
				            
				             // Set initial accordion state (first open, others closed)
    						self.setInitialAccordionState();

				        }, 150);
				        
				        // Update URL hash
				        window.location.hash = 'journey-' + journeyId;
				    }
				},
                error: function() {
                    panel.removeClass('loading');
                    panel.html('<div class="error">Failed to load journey details</div>');
                }
            });
        },
        
        // Find journey by ID in data
        findJourneyById: function(journeyId) {
            for (var group in this.journeyData) {
                var journeys = this.journeyData[group];
                for (var i = 0; i < journeys.length; i++) {
                    if (journeys[i].id == journeyId) {
                        return journeys[i];
                    }
                }
            }
            return null;
        },
        
        // Check and add toggle to long descriptions
        checkDescriptionLength: function() {
            $('.journey-description').each(function() {
                var desc = $(this);
                var text = desc.text();
                if (text.length > 100 && !desc.find('.description-toggle').length) {
                    desc.addClass('collapsed');
                    desc.append('<button class="description-toggle">More</button>');
                }
            });
        },
        
		// Update progress counters with proper task counting
		updateProgress: function() {
		    console.log('[JourneySystem] updateProgress() called');
		    
		    // Small delay to ensure DOM is ready
		    setTimeout(function() {
		        // Per-objective counts (using attr instead of data for fresh elements)
		        $('.objective-progress').each(function() {
		            var $prog = $(this);
		            var objId = $prog.attr('data-objective-id');
		            
		            // Find all tasks with this objective ID
		            var $tasks = $('.task-item[data-objective-id="' + objId + '"]');
		            var totalTasks = $tasks.length;
		            var completedTasks = $tasks.find('.task-checkbox.completed').length;
		            
		            console.log(
		                '[JourneySystem] Objective', objId,
		                '→', completedTasks + '/' + totalTasks,
		                'Found tasks:', $tasks.length
		            );
		            
		            // Update the UI
		            $prog.text(completedTasks + '/' + totalTasks);
		            $prog.toggleClass('completed', completedTasks === totalTasks && totalTasks > 0);
		        });
		        
		        // Overall completion
		        var total = $('.task-item').length;
		        var done = $('.task-checkbox.completed').length;
		        var percent = total ? Math.round((done / total) * 100) : 0;
		        console.log('[JourneySystem] Overall →', done + '/' + total, percent + '%');
		        $('.completion-status').text(percent + '% COMPLETE');
		        
		        // Save progress if user is logged in
		        if (mw.config.get('wgUserName')) {
		            this.saveProgress();
		        }
		    }.bind(this), 100); // 100ms delay to ensure DOM is ready
		},


        
        // Update the showItemPrepList function in your DuneJourneySystem
			showItemPrepList: function() {
			    console.log('[JourneySystem] showItemPrepList called');
			    var self = this;
			    
			    // Get active journey if not set
			    if (!self.currentActiveJourney) {
			        var activeCard = $('.journey-card.active');
			        if (activeCard.length) {
			            var journeyId = activeCard.data('journey-id');
			            self.currentActiveJourney = self.journeyData[journeyId] || {
			                id: journeyId, 
			                title: 'Journey ' + journeyId
			            };
			            console.log('[JourneySystem] Set active journey from card:', journeyId);
			        } else {
			            console.error('[JourneySystem] No active journey found!');
			            return;
			        }
			    }
			    
			    // Create popup
			    var popup = self.createPopup('items-popup', 'Item Prep List - ' + self.currentActiveJourney.title);
			    
			    // Show loading state
			    popup.find('.popup-content').html('<div class="loading">Loading materials...</div>');
			    self.showPopup(popup);
			    
			    // Add close handlers
			    popup.find('.popup-close').on('click', function() {
			        popup.removeClass('active');
			        setTimeout(function() { 
			            popup.remove(); 
			        }, 300);
			    });
			    
			    // Click overlay to close
			    popup.on('click', function(e) {
			        if ($(e.target).hasClass('journey-popup-overlay')) {
			            popup.removeClass('active');
			            setTimeout(function() { 
			                popup.remove(); 
			            }, 300);
			        }
			    });
			    
			    // Load materials from template
			    $.ajax({
			        url: mw.util.wikiScript('api'),
			        data: {
			            action: 'parse',
			            format: 'json',
			            text: '{{JourneyMaterials|id=' + self.currentActiveJourney.id + '}}',
			            contentmodel: 'wikitext'
			        },
			        success: function(response) {
			            if (response.parse && response.parse.text) {
			                var content = response.parse.text['*'];
			                
			                // Check if we got an error about missing view/table
			                if (content.includes('vw_recipe_inputs_by_level') && content.includes('doesn\'t exist')) {
			                    // The view doesn't exist yet, show example
			                    content = self.getExampleMaterialsDisplay();
			                }
			                
			                popup.find('.popup-content').html(content);
			            } else {
			                popup.find('.popup-content').html(
			                    '<div class="error-message">' +
			                    '<p><strong>Error:</strong> Unable to parse template response.</p>' +
			                    '</div>'
			                );
			            }
			        },
			        error: function(xhr, status, error) {
			            popup.find('.popup-content').html(
			                '<div class="error-message">' +
			                '<p><strong>Error loading materials</strong></p>' +
			                '<p>' + error + '</p>' +
			                '</div>'
			            );
			        }
			    });
			},
			
			// Add this helper function after showItemPrepList
			getExampleMaterialsDisplay: function() {
			    return `
			        <div class="materials-container">
			            <div class="materials-tier level-1">
			                <div class="tier-header">Direct Materials Required</div>
			                <div class="material-item">
			                    <span class="material-name">Duraluminum Ingot</span>
			                    <span class="material-qty">1</span>
			                </div>
			                <div class="material-item">
			                    <span class="material-name">Agave Seeds</span>
			                    <span class="material-qty">10</span>
			                </div>
			                <div class="material-item">
			                    <span class="material-name">Water</span>
			                    <span class="material-qty">170</span>
			                </div>
			            </div>
			            
			            <div class="materials-tier level-2">
			                <div class="tier-header">Components for Level 1 Materials</div>
			                <div class="material-item">
			                    <span class="material-name">Aluminum Ingot</span>
			                    <span class="material-qty">2</span>
			                </div>
			                <div class="material-item">
			                    <span class="material-name">Jasmium Crystal</span>
			                    <span class="material-qty">7</span>
			                </div>
			                <div class="material-item">
			                    <span class="material-name">Water</span>
			                    <span class="material-qty">1300</span>
			                </div>
			            </div>
			            
			            <div class="materials-tier level-3">
			                <div class="tier-header">Base Resources Needed</div>
			                <div class="material-item">
			                    <span class="material-name">Aluminum Ore</span>
			                    <span class="material-qty">22</span>
			                </div>
			                <div class="material-item">
			                    <span class="material-name">Water</span>
			                    <span class="material-qty">800</span>
			                </div>
			            </div>
			            
			            <div class="materials-summary">
			                <p>Note: This is example data. The database view 'vw_recipe_inputs_by_level' needs to be configured.</p>
			            </div>
			        </div>
			    `;
			},
        
        // Show video guide popup
        showVideoGuide: function() {
		    console.log('[JourneySystem] showVideoGuide called');
		    var self = this;
		    if (!self.currentActiveJourney) {
		        console.error('[JourneySystem] No active journey!');
		        return;
		    }
		    console.log('[JourneySystem] Current journey:', self.currentActiveJourney);
            
            var popup = self.createPopup('video-popup', self.currentActiveJourney.name + ' - Video Guide');
            
            // For now, show coming soon
            var content = '<div class="video-container">' +
                         '<div style="text-align: center; padding: 50px;">' +
                         '<h3>Video Guide Coming Soon</h3>' +
                         '</div></div>';
            
            popup.find('.popup-content').html(content);
            self.showPopup(popup);
        },
        
        // Create popup structure
        createPopup: function(className, title) {
            var popup = $('<div class="journey-popup-overlay">' +
                         '<div class="journey-popup ' + className + '">' +
                         '<div class="popup-header">' +
                         '<h3 class="popup-title">' + title + '</h3>' +
                         '<button class="popup-close">×</button>' +
                         '</div>' +
                         '<div class="popup-content"></div>' +
                         '</div></div>');
            return popup;
        },
        
        // Show popup with animation
        showPopup: function(popup) {
            $('body').append(popup);
            setTimeout(function() {
                popup.addClass('active');
            }, 10);
        },
        
        // Update material progress in popup
        updateMaterialProgress: function() {
            var totalItems = $('.material-item').length;
            var checkedItems = $('.material-item.checked').length;
            
            $('.materials-progress-count').text(checkedItems + '/' + totalItems);
            var progressEl = $('.materials-progress');
            var progressText = $('.materials-progress-text');
            
            if (checkedItems === totalItems && totalItems > 0) {
                progressEl.addClass('completed');
                progressText.text('All Materials Ready!');
            } else {
                progressEl.removeClass('completed');
                progressText.text('Materials Collected');
            }
        },
        
        // Check URL hash on load
        checkUrlHash: function() {
            var hash = window.location.hash;
            if (hash && hash.startsWith('#journey-')) {
                var journeyId = hash.replace('#journey-', '');
                this.loadJourneyDetails(journeyId);
            }
        }
    };
    
    // Initialize when DOM is ready
    $(function() {
    	console.log('[JourneySystem] DOM ready – calling init()');
        DuneJourneySystem.init();
    });
    
})(jQuery, mediaWiki);

/**
 * Journey Card Background Image Handler
 * Extracts MediaWiki file URLs and applies them as background images
 */
(function() {
    'use strict';
    
    /**
     * Apply background images to journey cards
     */
    function applyJourneyBackgrounds() {
        // Find all journey cards with data-icon attribute
        const journeyCards = document.querySelectorAll('.journey-card[data-icon]');
        
        journeyCards.forEach(function(card) {
            const iconFile = card.getAttribute('data-icon');
            
            // Skip if no icon file specified
            if (!iconFile || iconFile === '' || iconFile === 'NULL' || iconFile === 'null') {
                return;
            }
            
            // Check if it ends with .png or .jpg
            if (!iconFile.match(/\.(png|jpg|jpeg)$/i)) {
                return;
            }
            
            // Look for the hidden MediaWiki file element
            const bgSource = card.querySelector('.journey-bg-source img');
            
            if (bgSource) {
                // Get the src URL from the img element
                const imageUrl = bgSource.getAttribute('src');
                
                if (imageUrl) {
                    // Apply as background image
                    card.style.backgroundImage = 'url(' + imageUrl + ')';
                    card.classList.add('has-background');
                    
                    // Remove the has-icon class if it exists to avoid conflicts
                    card.classList.remove('has-icon');
                }
            } else {
                // Fallback: construct URL from filename
                // This handles cases where MediaWiki might not have rendered the file yet
                const encodedFilename = encodeURIComponent(iconFile.replace(/ /g, '_'));
                const fallbackUrl = '/images/' + encodedFilename;
                
                // Check if the file exists by creating a test image
                const testImg = new Image();
                testImg.onload = function() {
                    card.style.backgroundImage = 'url(' + fallbackUrl + ')';
                    card.classList.add('has-background');
                    card.classList.remove('has-icon');
                };
                testImg.onerror = function() {
                    // If direct path fails, try the thumb path
                    const thumbUrl = '/images/thumb/' + encodedFilename + '/300px-' + encodedFilename;
                    card.style.backgroundImage = 'url(' + thumbUrl + ')';
                    card.classList.add('has-background');
                    card.classList.remove('has-icon');
                };
                testImg.src = fallbackUrl;
            }
        });
    }
    
    /**
     * Initialize when DOM is ready
     */
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', applyJourneyBackgrounds);
    } else {
        // DOM is already loaded
        applyJourneyBackgrounds();
    }
    
    // Also run after a short delay to catch any lazy-loaded images
    setTimeout(applyJourneyBackgrounds, 1000);
    
    // Make function available globally for debugging
    window.applyJourneyBackgrounds = applyJourneyBackgrounds;
})();





// Resource Page Module Loader
$(function() {
    // Only on resource pages
    if ($('.crafting-section, .recipe-table, [class*="crafted-"]').length > 0) {
        // Load dependencies first
        mw.loader.using(['jquery', 'mediawiki.api', 'mediawiki.util'], function() {
            // Load resource page module
            $.getScript('/index.php?title=MediaWiki:ResourcePage.js&action=raw&ctype=text/javascript')
                .done(function() {
                    console.log('Resource page module loaded');
                })
                .fail(function() {
                    console.error('Failed to load resource page module');
                });
        });
    }
});






$(function () {
  const $drop = $('#drop');
  const $menu = $('#drop1');

  if ($drop.length && $menu.length) {
    // Move Actions button into the footer visually
    $('#footer').append($drop);
    $('#footer').append($menu);

    // Style the button like a footer element
    $drop.css({
      position: 'relative',
      display: 'inline-block',
      marginTop: '12px',
      background: 'rgba(252,231,200,0.08)',
      border: '1px solid rgba(252,231,200,0.2)',
      color: '#fce7c8',
      fontFamily: 'Rajdhani, sans-serif',
      padding: '6px 12px',
      boxShadow: '0 0 6px rgba(0,0,0,0.3)',
      cursor: 'pointer'
    });

    $drop.hover(
      () => $drop.css({ background: 'rgba(252,231,200,0.15)', color: '#fff' }),
      () => $drop.css({ background: 'rgba(252,231,200,0.08)', color: '#fce7c8' })
    );

    // Ensure dropdown menu opens below and is styled cleanly
    $menu.css({
      position: 'absolute',
      top: '100%',
      right: '0',
      zIndex: '9999',
      background: '#1a1a1a',
      boxShadow: '0 2px 6px rgba(0,0,0,0.5)',
      marginTop: '4px'
    });
  }

  // Footer message logic
  if (!$('#custom-footer-note').length) {
    const $customLine = $('<div>', {
      id: 'custom-footer-note',
      text: 'This is an experimental solo project exploring what’s possible in gaming UI and data experiences. Things may break—thank you for being part of the push forward.'
    });

    $('#footer').prepend($customLine);
    $('#footer-left').hide();
  }
});