MediaWiki:Common.js

From CoraTO Wiki - Official Wiki
Revision as of 22:08, 12 August 2025 by Noorisei (talk | contribs)
Jump to navigation Jump to search

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.
// MainPage2 interactions. Designed for MediaWiki site usage.
(function(){
  'use strict';

  // Simple tab controller (inspired by GuardianGuide.html)
  window.showTab = function(tabId, el){
    var contents = document.querySelectorAll('.tab-content');
    for (var i=0;i<contents.length;i++){ contents[i].classList.remove('active'); }
    var tabs = document.querySelectorAll('.nav-tab');
    for (var j=0;j<tabs.length;j++){ tabs[j].classList.remove('active'); }
    var target = document.getElementById(tabId);
    if (target) target.classList.add('active');
    if (el) el.classList.add('active');
  };

  // MediaWiki-safe tab click handler using event delegation
  function onTabClick(e) {
    var tab = e.target.closest('.nav-tab[data-tab]');
    if (!tab) return;
    e.preventDefault();
    var targetId = tab.getAttribute('data-tab');
    if (targetId) {
      showTab(targetId, tab);
    }
  }
  document.addEventListener('click', onTabClick, false);

  // Handle special navigation clicks for tabs (like in Hologramsie)
  function onSpecialTabNav(e) {
    var trigger = e.target.closest('[data-tab-trigger]');
    if (!trigger) return;
    e.preventDefault();
    var tabData = trigger.getAttribute('data-tab-trigger');
    var buttonId = trigger.getAttribute('data-tab-button');

    // Support anchors via href="#section" when data-scroll-to is not provided
    var href = trigger.getAttribute('href');
    var scrollToAttr = trigger.getAttribute('data-scroll-to');
    var scrollTargetSelector = null;
    if (scrollToAttr && scrollToAttr.trim()) {
      scrollTargetSelector = '#' + scrollToAttr.replace(/^#/, '');
    } else if (href && href.charAt(0) === '#') {
      scrollTargetSelector = href; // already a selector
    }
    
    if (tabData && buttonId) {
      var targetButton = document.getElementById(buttonId);
      if (targetButton) {
        showTab(tabData, targetButton);
        // Scroll to anchor if specified (from data-scroll-to or href)
        if (scrollTargetSelector) {
          var element = document.querySelector(scrollTargetSelector);
          if (element) {
            setTimeout(function() {
              element.scrollIntoView({behavior: 'smooth'});
            }, 100);
          }
        }
      }
    }
  }
  document.addEventListener('click', onSpecialTabNav, false);

  // Back to Top smooth scroll
  function onBackToTop(e){
    var trigger = e.target.closest('.back-to-top');
    if (!trigger) return;
    e.preventDefault();
    window.scrollTo({ top: 0, behavior: 'smooth' });
  }
  document.addEventListener('click', onBackToTop, false);

  // Card click-throughs using data-link to wiki pages or in-page anchors
  function onCardClick(e){
    var card = e.target.closest('.card[data-link], .destaque-card[data-link]');
    if (!card) return;
    var link = card.getAttribute('data-link');
    if (!link) return;

    // In-page anchor navigation: data-link="#section-id"
    if (link.charAt(0) === '#') {
      e.preventDefault();
      var anchorEl = document.querySelector(link);
      if (anchorEl) {
        anchorEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
      }
      return;
    }

    // Absolute external URL
    if (/^https?:\/\//i.test(link)) {
      window.location.href = link;
      return;
    }

    // Navigate to the MediaWiki page (prefer mw.util if available)
    var targetUrl = (window.mw && mw.util && typeof mw.util.getUrl === 'function')
      ? mw.util.getUrl(link)
      : ('index.php?title=' + encodeURIComponent(link));
    window.location.href = targetUrl;
  }
  document.addEventListener('click', onCardClick, false);

  // Helper: determine if a node is inside an excluded container
  function isInExcludedContext(node) {
    if (!node || !node.parentElement) return false;
    var p = node.parentElement.closest('script, style, textarea, input, select, option, .ve-ui-surface, .mw-editform, .CodeMirror, .cm-editor, .ace_editor');
    return !!p;
  }

  // Safely process MediaWiki-like internal link markup [[Title]] -> <a href="...">Title</a>
  function processWikiLinks() {
    // Skip if the page was already parsed by MediaWiki (no raw brackets found)
    if (document.body && document.body.textContent.indexOf('[[') === -1) return;

    var walker = document.createTreeWalker(
      document.body,
      NodeFilter.SHOW_TEXT,
      null,
      false
    );

    var textNodes = [];
    var node;
    while ((node = walker.nextNode())) {
      var value = node.nodeValue;
      if (!value) continue;
      if (value.indexOf('[[') === -1) continue;
      if (isInExcludedContext(node)) continue;
      textNodes.push(node);
    }

    var linkRe = /\[\[([^\|\]]+)(?:\|([^\]]+))?\]\]/g;

    textNodes.forEach(function(textNode) {
      var src = textNode.nodeValue;
      if (!linkRe.test(src)) return; // quick check
      linkRe.lastIndex = 0; // reset stateful regex

      var parent = textNode.parentNode;
      var frag = document.createDocumentFragment();
      var last = 0; var m;

      while ((m = linkRe.exec(src)) !== null) {
        // text before match
        if (m.index > last) frag.appendChild(document.createTextNode(src.slice(last, m.index)));

        var rawTitle = (m[1] || '').trim();
        var display = (m[2] != null ? m[2] : rawTitle).trim();

        // Skip File: or Image: links - keep original text intact
        if (/^(File:|Image:)/i.test(rawTitle)) {
          frag.appendChild(document.createTextNode(m[0]));
        } else {
          // Remove leading colon if present
          if (rawTitle.charAt(0) === ':') rawTitle = rawTitle.slice(1);
          var href = (window.mw && mw.util && typeof mw.util.getUrl === 'function')
            ? mw.util.getUrl(rawTitle)
            : ('index.php?title=' + encodeURIComponent(rawTitle));
          var a = document.createElement('a');
          a.className = 'mw-link-internal';
          a.setAttribute('href', href);
          a.appendChild(document.createTextNode(display)); // avoid HTML injection
          frag.appendChild(a);
        }

        last = linkRe.lastIndex;
      }

      // trailing text
      if (last < src.length) frag.appendChild(document.createTextNode(src.slice(last)));

      // Replace the original text node safely
      parent.insertBefore(frag, textNode);
      parent.removeChild(textNode);
    });
  }

  // Process wiki links when DOM is ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', processWikiLinks);
  } else {
    processWikiLinks();
  }
  // Mobile hamburger menu for sidebar
  function createHamburgerMenu() {
    // Only create if we're in MediaWiki Vector Legacy and on mobile
    if (!document.body.classList.contains('skin-vector-legacy')) return;
    
    var panel = document.getElementById('mw-panel');
    if (!panel) return;
    
    // Create hamburger button
    var hamburger = document.createElement('button');
    hamburger.className = 'mobile-hamburger-menu';
    hamburger.setAttribute('aria-label', 'Toggle navigation menu');
    hamburger.innerHTML = '<span></span><span></span><span></span>';
    
    // Insert hamburger button at the top of the page
    var content = document.getElementById('content') || document.querySelector('.mw-body');
    if (content) {
      content.parentNode.insertBefore(hamburger, content);
    }
    
    // Toggle sidebar function
    function toggleSidebar() {
      panel.classList.toggle('mobile-open');
      hamburger.classList.toggle('active');
      document.body.classList.toggle('sidebar-open');
    }
    
    // Click handler for hamburger
    hamburger.addEventListener('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      toggleSidebar();
    });
    
    // Close sidebar when clicking outside
    document.addEventListener('click', function(e) {
      if (panel.classList.contains('mobile-open') && 
          !panel.contains(e.target) && 
          !hamburger.contains(e.target)) {
        panel.classList.remove('mobile-open');
        hamburger.classList.remove('active');
        document.body.classList.remove('sidebar-open');
      }
    });
    
    // Close sidebar on escape key
    document.addEventListener('keydown', function(e) {
      if (e.key === 'Escape' && panel.classList.contains('mobile-open')) {
        panel.classList.remove('mobile-open');
        hamburger.classList.remove('active');
        document.body.classList.remove('sidebar-open');
      }
    });
  }
  
  // Collapsible sections functionality
  function initCollapsibleSections() {
    // Handle .collapsible-header elements (new style)
    var collapsibleHeaders = document.querySelectorAll('.collapsible-header');
    
    collapsibleHeaders.forEach(function(header) {
      header.addEventListener('click', function(e) {
        e.preventDefault();
        var section = header.parentElement;
        var content = section.querySelector('.collapsible-content');
        
        if (content) {
          var isExpanded = section.classList.contains('expanded');
          
          if (isExpanded) {
            section.classList.remove('expanded');
            content.style.maxHeight = '0';
          } else {
            section.classList.add('expanded');
            content.style.maxHeight = content.scrollHeight + 'px';
          }
        }
      });
    });
    
    // Handle .collapsible elements (original GuardianGuide style)
    var collapsibleButtons = document.querySelectorAll('.collapsible');
    
    collapsibleButtons.forEach(function(button) {
      button.addEventListener('click', function(e) {
        e.preventDefault();
        var content = button.nextElementSibling;
        
        if (content && content.classList.contains('collapsible-content')) {
          var isActive = button.classList.contains('active');
          
          if (isActive) {
            button.classList.remove('active');
            content.style.maxHeight = '0';
          } else {
            button.classList.add('active');
            content.style.maxHeight = content.scrollHeight + 'px';
          }
        }
      });
    });
  }

  // Guardian Type Decision Helper functionality
  function initGuardianDecisionHelper() {
    // Initialize accordion functionality for guardian types
    function initAccordion() {
      const accordions = document.getElementsByClassName("guardian-accordion");
      for (let i = 0; i < accordions.length; i++) {
        accordions[i].addEventListener("click", function() {
          this.classList.toggle("active");
          const panel = this.nextElementSibling;
          if (panel.style.maxHeight) {
            panel.style.maxHeight = null;
          } else {
            panel.style.maxHeight = panel.scrollHeight + "px";
          }
        });
      }
    }
    
    // Function to show guardian by difficulty level
    function showGuardianByDifficulty(level) {
      const accordions = document.getElementsByClassName("guardian-accordion");
      let targetAccordion;
      
      // Reset all accordions
      for (let i = 0; i < accordions.length; i++) {
        accordions[i].classList.remove("active");
        accordions[i].nextElementSibling.style.maxHeight = null;
      }
      
      // Select the appropriate accordion based on difficulty level
      switch(level) {
        case "1":
          targetAccordion = document.querySelector(".regular-accordion");
          break;
        case "2":
          targetAccordion = document.querySelector(".mighty-accordion");
          break;
        case "3":
          targetAccordion = document.querySelector(".legendary-accordion");
          break;
        case "4":
          targetAccordion = document.querySelector(".superior-accordion");
          break;
        case "5":
          targetAccordion = document.querySelector(".accomplished-accordion");
          break;
      }
      
      // Activate the target accordion
      if (targetAccordion) {
        targetAccordion.classList.add("active");
        const panel = targetAccordion.nextElementSibling;
        panel.style.maxHeight = panel.scrollHeight + "px";
        
        // Scroll to the accordion
        setTimeout(function() {
          targetAccordion.scrollIntoView({ behavior: 'smooth', block: 'center' });
        }, 300);
      }
    }

    // Initialize the decision helper
    function initDecisionHelper() {
      // Find all option elements
      const options = document.querySelectorAll('.decision-options .option');
      
      // Add click event listeners to each option
      options.forEach(option => {
        option.addEventListener('click', function() {
          // Remove selected class from siblings
          const siblings = this.parentElement.querySelectorAll('.option');
          siblings.forEach(sib => sib.classList.remove('selected'));
          
          // Add selected class to clicked option
          this.classList.add('selected');
          
          // Update recommendation
          updateRecommendation();
        });
      });
    }

    // Function to update recommendation based on selections
    function updateRecommendation() {
      const selectedOptions = document.querySelectorAll('.decision-options .option.selected');
      
      // If no options selected, show default message
      if (selectedOptions.length === 0) {
        document.getElementById('recommendedType').textContent = 'Select options above';
        return;
      }
      
      // Count votes for each guardian type
      const votes = {
        'regular': 0,
        'mighty': 0,
        'legendary': 0,
        'superior': 0,
        'accomplished': 0
      };
      
      // Tally votes from selected options
      selectedOptions.forEach(option => {
        const types = option.getAttribute('data-type').split(',');
        types.forEach(type => {
          votes[type]++;
        });
      });
      
      // Find the type with the most votes
      let maxVotes = 0;
      let recommendedType = '';
      let tiedTypes = [];
      
      for (const type in votes) {
        if (votes[type] > maxVotes) {
          maxVotes = votes[type];
          recommendedType = type;
          tiedTypes = [type];
        } else if (votes[type] === maxVotes && maxVotes > 0) {
          tiedTypes.push(type);
        }
      }
      
      // Handle ties by recommending the more challenging type
      if (tiedTypes.length > 1) {
        const difficultyOrder = ['regular', 'mighty', 'legendary', 'superior', 'accomplished'];
        let highestDifficultyIndex = -1;
        
        tiedTypes.forEach(type => {
          const typeIndex = difficultyOrder.indexOf(type);
          if (typeIndex > highestDifficultyIndex) {
            highestDifficultyIndex = typeIndex;
            recommendedType = type;
          }
        });
      }
      
      // Format the recommendation with additional context
      let formattedType = recommendedType.charAt(0).toUpperCase() + recommendedType.slice(1) + ' Guardian';
      let recommendationText = formattedType;
      
      // Add context based on the recommended type
      switch(recommendedType) {
        case 'regular':
          recommendationText += ' - Perfect for beginners with limited time';
          break;
        case 'mighty':
          recommendationText += ' - Good balance of effort and power';
          break;
        case 'legendary':
          recommendationText += ' - Excellent for endgame content';
          break;
        case 'superior':
          recommendationText += ' - For dedicated players seeking optimization';
          break;
        case 'accomplished':
          recommendationText += ' - For true perfectionists';
          break;
      }
      
      // Update the recommendation text
      document.getElementById('recommendedType').textContent = recommendationText;
      
      // Highlight the recommended guardian type in the accordion
      highlightRecommendedType(recommendedType);
    }
    
    // Function to highlight the recommended guardian type in the accordion
    function highlightRecommendedType(type) {
      // Remove highlight from all accordions
      const accordions = document.querySelectorAll('.guardian-accordion');
      accordions.forEach(accordion => {
        accordion.classList.remove('recommended');
      });
      
      // Add highlight to the recommended type
      const recommendedAccordion = document.querySelector('.' + type + '-accordion');
      if (recommendedAccordion) {
        recommendedAccordion.classList.add('recommended');
        
        // Scroll to the recommended type if not visible
        const isVisible = isElementInViewport(recommendedAccordion);
        if (!isVisible) {
          recommendedAccordion.scrollIntoView({ behavior: 'smooth', block: 'center' });
        }
      }
    }
    
    // Helper function to check if an element is in the viewport
    function isElementInViewport(el) {
      const rect = el.getBoundingClientRect();
      return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
      );
    }
    
    // Initialize components if they exist on the page
    if (document.querySelector('.guardian-accordion') || document.querySelector('.decision-options')) {
      initAccordion();
      initDecisionHelper();
      
      // Make functions available globally
      window.updateGuardianRecommendation = updateRecommendation;
      window.showGuardianByDifficulty = showGuardianByDifficulty;
    }
  }
  

  // Initialize collapsible sections when DOM is ready
  function initializeAll() {
    createHamburgerMenu();
    initCollapsibleSections();
    initGuardianDecisionHelper();
  }

  // Initialize all functionality when DOM is ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initializeAll);
  } else {
    initializeAll();
  }

})();