MediaWiki:Common.js: Difference between revisions

From CoraTO Wiki - Official Wiki
Jump to navigation Jump to search
No edit summary
Tag: Reverted
No edit summary
Tag: Manual revert
Line 1: Line 1:
/**
// MainPage2 interactions. Designed for MediaWiki site usage.
* MediaWiki Common JavaScript Module
(function(){
* Enhanced version with improved organization, documentation, and error handling
* Designed for MediaWiki site usage with Vector Legacy skin support
*
* @author NewCora Wiki Team
* @version 2.0
* @license MIT
*/
 
(function() {
   'use strict';
   'use strict';


   // ============================================================================
   // Simple tab controller (inspired by GuardianGuide.html)
  // CONFIGURATION & CONSTANTS
  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
  * Configuration object for the module
   function onTabClick(e) {
  * @type {Object}
     var tab = e.target.closest('.nav-tab[data-tab]');
  */
     if (!tab) return;
   const CONFIG = {
     e.preventDefault();
     SELECTORS: {
     var targetId = tab.getAttribute('data-tab');
      TAB_CONTENT: '.tab-content',
     if (targetId) {
      NAV_TAB: '.nav-tab',
       showTab(targetId, tab);
      BACK_TO_TOP: '.back-to-top',
      CARD_LINKS: '.card[data-link], .destaque-card[data-link]',
      COLLAPSIBLE_HEADER: '.collapsible-header',
      COLLAPSIBLE_BUTTON: '.collapsible',
      GUARDIAN_ACCORDION: '.guardian-accordion'
     },
     CLASSES: {
      ACTIVE: 'active',
      EXPANDED: 'expanded',
      COLLAPSED: 'collapsed',
      MOBILE_OPEN: 'mobile-open',
      SIDEBAR_OPEN: 'sidebar-open',
      RECOMMENDED: 'recommended'
     },
    STORAGE_KEYS: {
      SIDEBAR_PREFIX: 'sidebar-',
      USER_MENU_STATE: 'user-menu-state'
    },
     DELAYS: {
       SCROLL_DELAY: 100,
      ACCORDION_SCROLL_DELAY: 300
     }
     }
   };
   }
  document.addEventListener('click', onTabClick, false);


   // ============================================================================
   // Handle special navigation clicks for tabs (like in Hologramsie)
   // UTILITY FUNCTIONS
   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
  * Safely gets an element by ID with error handling
    var href = trigger.getAttribute('href');
  * @param {string} id - The element ID
    var scrollToAttr = trigger.getAttribute('data-scroll-to');
  * @returns {Element|null} The element or null if not found
    var scrollTargetSelector = null;
  */
    if (scrollToAttr && scrollToAttr.trim()) {
  function safeGetElementById(id) {
      scrollTargetSelector = '#' + scrollToAttr.replace(/^#/, '');
     try {
    } else if (href && href.charAt(0) === '#') {
       return document.getElementById(id);
      scrollTargetSelector = href; // already a selector
    } catch (error) {
    }
      console.warn(`Element with ID '${id}' not found:`, error);
      
       return null;
    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
  * Safely queries for elements with error handling
   function onBackToTop(e){
  * @param {string} selector - CSS selector
     var trigger = e.target.closest('.back-to-top');
  * @param {Element} [context=document] - Context element
    if (!trigger) return;
  * @returns {NodeList} NodeList of matching elements
     e.preventDefault();
  */
    window.scrollTo({ top: 0, behavior: 'smooth' });
   function safeQuerySelectorAll(selector, context = document) {
     try {
      return context.querySelectorAll(selector);
     } catch (error) {
      console.warn(`Invalid selector '${selector}':`, error);
      return [];
    }
   }
   }
  document.addEventListener('click', onBackToTop, false);


   /**
   // Card click-throughs using data-link to wiki pages or in-page anchors
  * Safely queries for a single element with error handling
   function onCardClick(e){
  * @param {string} selector - CSS selector
     var card = e.target.closest('.card[data-link], .destaque-card[data-link]');
  * @param {Element} [context=document] - Context element
     if (!card) return;
  * @returns {Element|null} The first matching element or null
    var link = card.getAttribute('data-link');
  */
    if (!link) return;
   function safeQuerySelector(selector, context = document) {
     try {
      return context.querySelector(selector);
     } catch (error) {
      console.warn(`Invalid selector '${selector}':`, error);
      return null;
    }
  }


  /**
    // In-page anchor navigation: data-link="#section-id"
  * Safely accesses localStorage with error handling
    if (link.charAt(0) === '#') {
  * @param {string} key - Storage key
      e.preventDefault();
  * @param {string} [defaultValue=''] - Default value if key doesn't exist
       var anchorEl = document.querySelector(link);
  * @returns {string} The stored value or default
      if (anchorEl) {
  */
        anchorEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
  function safeGetLocalStorage(key, defaultValue = '') {
      }
    try {
       return;
       return localStorage.getItem(key) || defaultValue;
    } catch (error) {
      console.warn(`localStorage access failed for key '${key}':`, error);
       return defaultValue;
     }
     }
  }


  /**
    // Absolute external URL
  * Safely sets localStorage with error handling
    if (/^https?:\/\//i.test(link)) {
  * @param {string} key - Storage key
       window.location.href = link;
  * @param {string} value - Value to store
       return;
  * @returns {boolean} Success status
  */
  function safeSetLocalStorage(key, value) {
    try {
      localStorage.setItem(key, value);
      return true;
    } catch (error) {
       console.warn(`localStorage write failed for key '${key}':`, error);
       return false;
     }
     }
  }


  /**
    // Navigate to the MediaWiki page (prefer mw.util if available)
  * Checks if an element is in the viewport
     var targetUrl = (window.mw && mw.util && typeof mw.util.getUrl === 'function')
  * @param {Element} element - Element to check
       ? mw.util.getUrl(link)
  * @returns {boolean} True if element is visible in viewport
       : ('index.php?title=' + encodeURIComponent(link));
  */
    window.location.href = targetUrl;
  function isElementInViewport(element) {
    if (!element) return false;
   
     const rect = element.getBoundingClientRect();
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
       rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
       rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
   }
   }
  document.addEventListener('click', onCardClick, false);


   /**
   // Helper: determine if a node is inside an excluded container
  * Determines if a node is inside an excluded container
  * @param {Node} node - The node to check
  * @returns {boolean} True if node is in excluded context
  */
   function isInExcludedContext(node) {
   function isInExcludedContext(node) {
     if (!node || !node.parentElement) return false;
     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');
    const excludedSelectors = [
     return !!p;
      'script', 'style', 'textarea', 'input', 'select', 'option',
      '.ve-ui-surface', '.mw-editform', '.CodeMirror', '.cm-editor', '.ace_editor'
    ].join(', ');
   
     return !!node.parentElement.closest(excludedSelectors);
   }
   }


   // ============================================================================
   // Safely process MediaWiki-like internal link markup [[Title]] -> <a href="...">Title</a>
   // TAB NAVIGATION SYSTEM
   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(
  * Tab Navigation Module
      document.body,
  * Handles tab switching functionality with smooth scrolling support
      NodeFilter.SHOW_TEXT,
  */
      null,
  const TabNavigation = {
       false
    /**
    );
    * Shows a specific tab and hides others
    * @param {string} tabId - ID of the tab content to show
    * @param {Element} [tabElement] - The tab button element
    */
    showTab: function(tabId, tabElement) {
       if (!tabId) {
        console.warn('TabNavigation.showTab: tabId is required');
        return;
      }


       // Hide all tab contents
    var textNodes = [];
       const contents = safeQuerySelectorAll(CONFIG.SELECTORS.TAB_CONTENT);
    var node;
       contents.forEach(content => content.classList.remove(CONFIG.CLASSES.ACTIVE));
    while ((node = walker.nextNode())) {
       var value = node.nodeValue;
       if (!value) continue;
       if (value.indexOf('[[') === -1) continue;
      if (isInExcludedContext(node)) continue;
      textNodes.push(node);
    }


      // Deactivate all tab buttons
    var linkRe = /\[\[([^\|\]]+)(?:\|([^\]]+))?\]\]/g;
      const tabs = safeQuerySelectorAll(CONFIG.SELECTORS.NAV_TAB);
      tabs.forEach(tab => tab.classList.remove(CONFIG.CLASSES.ACTIVE));


      // Show target tab content
    textNodes.forEach(function(textNode) {
      const targetContent = safeGetElementById(tabId);
      var src = textNode.nodeValue;
      if (targetContent) {
      if (!linkRe.test(src)) return; // quick check
        targetContent.classList.add(CONFIG.CLASSES.ACTIVE);
       linkRe.lastIndex = 0; // reset stateful regex
       
        // If this is the Normal Daily tab, activate the first nested tab
        if (tabId === 'tab-normal-daily') {
          setTimeout(() => {
            const firstNestedTab = targetContent.querySelector('.nested-tab');
            if (firstNestedTab) {
              const firstNestedTabId = firstNestedTab.getAttribute('data-tab');
              if (firstNestedTabId) {
                TabNavigation.showNestedTab(firstNestedTabId, firstNestedTab);
              }
            }
          }, 50);
        }
       } else {
        console.warn(`Tab content with ID '${tabId}' not found`);
      }


       // Activate the clicked tab button
       var parent = textNode.parentNode;
       if (tabElement) {
       var frag = document.createDocumentFragment();
        tabElement.classList.add(CONFIG.CLASSES.ACTIVE);
       var last = 0; var m;
       }
    },


    /**
      while ((m = linkRe.exec(src)) !== null) {
    * Shows a specific nested tab and hides others
        // text before match
    * @param {string} tabId - ID of the nested tab content to show
        if (m.index > last) frag.appendChild(document.createTextNode(src.slice(last, m.index)));
    * @param {Element} [tabElement] - The nested tab button element
    */
    showNestedTab: function(tabId, tabElement) {
      if (!tabId) {
        console.warn('TabNavigation.showNestedTab: tabId is required');
        return;
      }


      // Find the parent tab content to scope the nested tab switching
        var rawTitle = (m[1] || '').trim();
      const parentTabContent = tabElement ? tabElement.closest('.tab-content') : null;
        var display = (m[2] != null ? m[2] : rawTitle).trim();
      const searchScope = parentTabContent || document;


      // Hide all nested tab contents within the scope
        // Skip File: or Image: links - keep original text intact
      const nestedContents = safeQuerySelectorAll('.nested-content', searchScope);
        if (/^(File:|Image:)/i.test(rawTitle)) {
      nestedContents.forEach(content => content.classList.remove(CONFIG.CLASSES.ACTIVE));
          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);
        }


      // Deactivate all nested tab buttons within the scope
        last = linkRe.lastIndex;
      const nestedTabs = safeQuerySelectorAll('.nested-tab', searchScope);
      nestedTabs.forEach(tab => tab.classList.remove(CONFIG.CLASSES.ACTIVE));
 
      // Show target nested tab content
      const targetContent = safeGetElementById(tabId);
      if (targetContent) {
        targetContent.classList.add(CONFIG.CLASSES.ACTIVE);
      } else {
        console.warn(`Nested tab content with ID '${tabId}' not found`);
       }
       }


       // Activate the clicked nested tab button
       // trailing text
       if (tabElement) {
       if (last < src.length) frag.appendChild(document.createTextNode(src.slice(last)));
        tabElement.classList.add(CONFIG.CLASSES.ACTIVE);
      }
    },


    /**
      // Replace the original text node safely
    * Handles tab click events using event delegation
      parent.insertBefore(frag, textNode);
    * @param {Event} event - Click event
       parent.removeChild(textNode);
    */
    });
    handleTabClick: function(event) {
  }
       const tab = event.target.closest('.nav-tab[data-tab]');
      if (!tab) return;


      event.preventDefault();
  // Process wiki links when DOM is ready
      const targetId = tab.getAttribute('data-tab');
  if (document.readyState === 'loading') {
     
    document.addEventListener('DOMContentLoaded', processWikiLinks);
      if (targetId) {
  } else {
        TabNavigation.showTab(targetId, tab);
    processWikiLinks();
      }
  }
    },
  // Mobile hamburger menu for sidebar
 
  function createHamburgerMenu() {
    /**
    // Only create if we're in MediaWiki Vector Legacy and on mobile
    * Handles nested tab click events using event delegation
    if (!document.body.classList.contains('skin-vector-legacy')) return;
    * @param {Event} event - Click event
      
    */
     var panel = document.getElementById('mw-panel');
    handleNestedTabClick: function(event) {
    if (!panel) return;
      const nestedTab = event.target.closest('.nested-tab[data-tab]');
   
      if (!nestedTab) return;
    // Create hamburger button
 
    var hamburger = document.createElement('button');
      event.preventDefault();
    hamburger.className = 'mobile-hamburger-menu';
      const targetId = nestedTab.getAttribute('data-tab');
    hamburger.setAttribute('aria-label', 'Toggle navigation menu');
     
    hamburger.innerHTML = '<span></span><span></span><span></span>';
      if (targetId) {
      
        TabNavigation.showNestedTab(targetId, nestedTab);
     // 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);
     /**
    * Handles special navigation clicks with scroll support
    * @param {Event} event - Click event
    */
     handleSpecialTabNavigation: function(event) {
      const trigger = event.target.closest('[data-tab-trigger]');
      if (!trigger) return;
 
      event.preventDefault();
     
      const tabData = trigger.getAttribute('data-tab-trigger');
      const buttonId = trigger.getAttribute('data-tab-button');
      const href = trigger.getAttribute('href');
      const scrollToAttr = trigger.getAttribute('data-scroll-to');
 
      // Determine scroll target
      let scrollTargetSelector = null;
      if (scrollToAttr && scrollToAttr.trim()) {
        scrollTargetSelector = '#' + scrollToAttr.replace(/^#/, '');
      } else if (href && href.charAt(0) === '#') {
        scrollTargetSelector = href;
      }
 
      // Switch tab if specified
      if (tabData && buttonId) {
        const targetButton = safeGetElementById(buttonId);
        if (targetButton) {
          TabNavigation.showTab(tabData, targetButton);
         
          // Scroll to anchor after tab switch
          if (scrollTargetSelector) {
            const scrollTarget = safeQuerySelector(scrollTargetSelector);
            if (scrollTarget) {
              setTimeout(() => {
                scrollTarget.scrollIntoView({ behavior: 'smooth' });
              }, CONFIG.DELAYS.SCROLL_DELAY);
            }
          }
        }
      }
     },
 
     /**
    * Initializes tab navigation event listeners
    */
     init: function() {
      document.addEventListener('click', this.handleTabClick, false);
      document.addEventListener('click', this.handleNestedTabClick, false);
      document.addEventListener('click', this.handleSpecialTabNavigation, false);
     
      // Make showTab globally available for backward compatibility
       window.showTab = this.showTab;
     }
     }
  };
   
 
     // Toggle sidebar function
  // ============================================================================
     function toggleSidebar() {
  // NAVIGATION & INTERACTION HANDLERS
       panel.classList.toggle('mobile-open');
  // ============================================================================
       hamburger.classList.toggle('active');
 
       document.body.classList.toggle('sidebar-open');
  /**
  * Navigation Handlers Module
  * Manages various navigation interactions
  */
  const NavigationHandlers = {
     /**
    * Handles back-to-top button clicks
    * @param {Event} event - Click event
    */
    handleBackToTop: function(event) {
      const trigger = event.target.closest(CONFIG.SELECTORS.BACK_TO_TOP);
      if (!trigger) return;
 
      event.preventDefault();
      window.scrollTo({ top: 0, behavior: 'smooth' });
    },
 
    /**
    * Handles card click-through navigation
    * @param {Event} event - Click event
    */
     handleCardClick: function(event) {
       const card = event.target.closest(CONFIG.SELECTORS.CARD_LINKS);
      if (!card) return;
 
      const link = card.getAttribute('data-link');
      if (!link) return;
 
      // Handle in-page anchor navigation
      if (link.charAt(0) === '#') {
        event.preventDefault();
        const anchorElement = safeQuerySelector(link);
        if (anchorElement) {
          anchorElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
        return;
      }
 
      // Handle absolute external URLs
      if (/^https?:\/\//i.test(link)) {
        window.location.href = link;
        return;
      }
 
      // Navigate to MediaWiki page
       const targetUrl = (window.mw && window.mw.util && typeof window.mw.util.getUrl === 'function')
        ? window.mw.util.getUrl(link)
        : ('index.php?title=' + encodeURIComponent(link));
     
      window.location.href = targetUrl;
    },
 
    /**
    * Initializes navigation event handlers
    */
    init: function() {
       document.addEventListener('click', this.handleBackToTop, false);
      document.addEventListener('click', this.handleCardClick, false);
     }
     }
  };
   
 
    // Click handler for hamburger
  // ============================================================================
    hamburger.addEventListener('click', function(e) {
  // WIKI LINK PROCESSOR
      e.preventDefault();
  // ============================================================================
      e.stopPropagation();
 
      toggleSidebar();
  /**
    });
  * Wiki Link Processor Module
   
  * Converts [[Title]] markup to proper MediaWiki links
     // Close sidebar when clicking outside
  */
     document.addEventListener('click', function(e) {
  const WikiLinkProcessor = {
       if (panel.classList.contains('mobile-open') &&
     /**
          !panel.contains(e.target) &&
    * Processes MediaWiki-style internal link markup
          !hamburger.contains(e.target)) {
    * Converts [[Title]] or [[Title|Display]] to proper anchor tags
        panel.classList.remove('mobile-open');
    */
        hamburger.classList.remove('active');
     processWikiLinks: function() {
         document.body.classList.remove('sidebar-open');
       // Skip if no raw brackets found (already processed by MediaWiki)
      if (!document.body || document.body.textContent.indexOf('[[') === -1) {
         return;
       }
       }
 
    });
      const walker = document.createTreeWalker(
   
        document.body,
    // Close sidebar on escape key
        NodeFilter.SHOW_TEXT,
    document.addEventListener('keydown', function(e) {
        null,
       if (e.key === 'Escape' && panel.classList.contains('mobile-open')) {
        false
         panel.classList.remove('mobile-open');
      );
         hamburger.classList.remove('active');
 
         document.body.classList.remove('sidebar-open');
       const textNodes = [];
      let node;
     
      // Collect text nodes containing wiki links
      while ((node = walker.nextNode())) {
         const value = node.nodeValue;
         if (!value || value.indexOf('[[') === -1 || isInExcludedContext(node)) {
          continue;
         }
        textNodes.push(node);
       }
       }
 
    });
      const linkRegex = /\[\[([^\|\]]+)(?:\|([^\]]+))?\]\]/g;
  }
 
 
      // Process each text node
  // Collapsible sections functionality
       textNodes.forEach(textNode => {
  function initCollapsibleSections() {
         const sourceText = textNode.nodeValue;
    // Handle .collapsible-header elements (new style)
         if (!linkRegex.test(sourceText)) return;
    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');
          
          
         linkRegex.lastIndex = 0; // Reset regex state
         if (content) {
 
          var isExpanded = section.classList.contains('expanded');
        const parent = textNode.parentNode;
            
        const fragment = document.createDocumentFragment();
           if (isExpanded) {
        let lastIndex = 0;
             section.classList.remove('expanded');
        let match;
             content.style.maxHeight = '0';
 
        while ((match = linkRegex.exec(sourceText)) !== null) {
           // Add text before the match
           if (match.index > lastIndex) {
             fragment.appendChild(
              document.createTextNode(sourceText.slice(lastIndex, match.index))
            );
          }
 
          const rawTitle = (match[1] || '').trim();
          const displayText = (match[2] != null ? match[2] : rawTitle).trim();
 
          // Skip File: or Image: links - keep original text
          if (/^(File:|Image:)/i.test(rawTitle)) {
             fragment.appendChild(document.createTextNode(match[0]));
           } else {
           } else {
             // Remove leading colon if present
             section.classList.add('expanded');
            const cleanTitle = rawTitle.charAt(0) === ':' ? rawTitle.slice(1) : rawTitle;
             content.style.maxHeight = content.scrollHeight + 'px';
              
            // Create proper MediaWiki link
            const href = (window.mw && window.mw.util && typeof window.mw.util.getUrl === 'function')
              ? window.mw.util.getUrl(cleanTitle)
              : ('index.php?title=' + encodeURIComponent(cleanTitle));
           
            const anchor = document.createElement('a');
            anchor.className = 'mw-link-internal';
            anchor.setAttribute('href', href);
            anchor.appendChild(document.createTextNode(displayText));
            fragment.appendChild(anchor);
           }
           }
          lastIndex = linkRegex.lastIndex;
         }
         }
        // Add remaining text
        if (lastIndex < sourceText.length) {
          fragment.appendChild(
            document.createTextNode(sourceText.slice(lastIndex))
          );
        }
        // Replace the original text node
        parent.insertBefore(fragment, textNode);
        parent.removeChild(textNode);
       });
       });
     },
     });
 
   
     /**
     // Handle .collapsible elements (original GuardianGuide style)
    * Initializes wiki link processing
    var collapsibleButtons = document.querySelectorAll('.collapsible');
    */
   
    init: function() {
    collapsibleButtons.forEach(function(button) {
      if (document.readyState === 'loading') {
       button.addEventListener('click', function(e) {
        document.addEventListener('DOMContentLoaded', this.processWikiLinks);
         e.preventDefault();
      } else {
        var content = button.nextElementSibling;
        this.processWikiLinks();
       
      }
        if (content && content.classList.contains('collapsible-content')) {
    }
          var isActive = button.classList.contains('active');
  };
         
 
          if (isActive) {
  // ============================================================================
            button.classList.remove('active');
  // MOBILE INTERFACE
            content.style.maxHeight = '0';
  // ============================================================================
          } else {
 
             button.classList.add('active');
  /**
            content.style.maxHeight = content.scrollHeight + 'px';
  * Mobile Interface Module
           }
  * Handles mobile-specific UI components
  */
  const MobileInterface = {
    /**
    * Creates hamburger menu for mobile sidebar navigation
    */
    createHamburgerMenu: function() {
       // Only create for Vector Legacy skin
      if (!document.body.classList.contains('skin-vector-legacy')) {
         return;
      }
 
      const panel = safeGetElementById('mw-panel');
      if (!panel) return;
 
      // Create hamburger button
      const 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 at top of page
      const content = safeGetElementById('content') || safeQuerySelector('.mw-body');
      if (content && content.parentNode) {
        content.parentNode.insertBefore(hamburger, content);
      }
 
      // Toggle functionality
      const toggleSidebar = () => {
        panel.classList.toggle(CONFIG.CLASSES.MOBILE_OPEN);
        hamburger.classList.toggle(CONFIG.CLASSES.ACTIVE);
        document.body.classList.toggle(CONFIG.CLASSES.SIDEBAR_OPEN);
      };
 
      // Event listeners
      hamburger.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        toggleSidebar();
      });
 
      // Close on outside click
      document.addEventListener('click', (event) => {
        if (panel.classList.contains(CONFIG.CLASSES.MOBILE_OPEN) &&
             !panel.contains(event.target) &&
            !hamburger.contains(event.target)) {
          panel.classList.remove(CONFIG.CLASSES.MOBILE_OPEN);
          hamburger.classList.remove(CONFIG.CLASSES.ACTIVE);
           document.body.classList.remove(CONFIG.CLASSES.SIDEBAR_OPEN);
         }
         }
       });
       });
    });
  }


      // Close on escape key
  // Vector Legacy Sidebar Collapsible functionality
      document.addEventListener('keydown', (event) => {
  function initSidebarCollapsible() {
        if (event.key === 'Escape' && panel.classList.contains(CONFIG.CLASSES.MOBILE_OPEN)) {
    // Only initialize if we're in Vector Legacy skin
          panel.classList.remove(CONFIG.CLASSES.MOBILE_OPEN);
    if (!document.body.classList.contains('skin-vector-legacy')) return;
          hamburger.classList.remove(CONFIG.CLASSES.ACTIVE);
   
           document.body.classList.remove(CONFIG.CLASSES.SIDEBAR_OPEN);
    var panel = document.getElementById('mw-panel');
    if (!panel) return;
   
    // Get all portals (both vector-menu-portal and portal classes)
    var portals = panel.querySelectorAll('.vector-menu-portal, .portal');
   
    // Auto-collapse all sections except the first one (Navigation) on page load
    portals.forEach(function(portal, index) {
      if (index > 0) { // Skip first portal (Navigation)
        portal.classList.add('collapsed');
        var content = portal.querySelector('.vector-menu-content, .body');
        if (content) {
           content.style.maxHeight = '0';
         }
         }
       });
       }
     },
    });
 
      
     /**
     // Add click handlers to portal headings
    * Initializes mobile interface components
    var headings = panel.querySelectorAll('.vector-menu-heading, .portal h3');
    */
   
     init: function() {
     headings.forEach(function(heading) {
       this.createHamburgerMenu();
       heading.addEventListener('click', function(e) {
    }
        e.preventDefault();
  };
        e.stopPropagation();
 
       
  // ============================================================================
        var portal = heading.closest('.vector-menu-portal, .portal');
  // COLLAPSIBLE SECTIONS
        if (!portal) return;
  // ============================================================================
       
 
        var content = portal.querySelector('.vector-menu-content, .body');
  /**
        if (!content) return;
  * Collapsible Sections Module
       
  * Manages expandable/collapsible content sections
        var isCollapsed = portal.classList.contains('collapsed');
  */
       
  const CollapsibleSections = {
        if (isCollapsed) {
    /**
          // Expand
    * Initializes collapsible header functionality
          portal.classList.remove('collapsed');
    */
           content.style.maxHeight = content.scrollHeight + 'px';
    initCollapsibleHeaders: function() {
      const headers = safeQuerySelectorAll(CONFIG.SELECTORS.COLLAPSIBLE_HEADER);
     
      headers.forEach(header => {
        header.addEventListener('click', (event) => {
           event.preventDefault();
            
            
           const section = header.parentElement;
           // Store the expanded state
           const content = section.querySelector('.collapsible-content');
          try {
            localStorage.setItem('sidebar-' + getPortalId(portal), 'expanded');
           } catch (e) {
            // Ignore localStorage errors
          }
        } else {
          // Collapse
          portal.classList.add('collapsed');
          content.style.maxHeight = '0';
            
            
           if (content) {
           // Store the collapsed state
             const isExpanded = section.classList.contains(CONFIG.CLASSES.EXPANDED);
          try {
           
             localStorage.setItem('sidebar-' + getPortalId(portal), 'collapsed');
            if (isExpanded) {
          } catch (e) {
              section.classList.remove(CONFIG.CLASSES.EXPANDED);
             // Ignore localStorage errors
              content.style.maxHeight = '0';
            } else {
              section.classList.add(CONFIG.CLASSES.EXPANDED);
              content.style.maxHeight = content.scrollHeight + 'px';
             }
           }
           }
         });
         }
       });
       });
     },
     });
 
   
     /**
     // Restore saved states from localStorage
    * Initializes collapsible button functionality
     portals.forEach(function(portal, index) {
    */
       if (index === 0) return; // Skip first portal (always expanded)
     initCollapsibleButtons: function() {
       const buttons = safeQuerySelectorAll(CONFIG.SELECTORS.COLLAPSIBLE_BUTTON);
        
        
       buttons.forEach(button => {
       try {
         button.addEventListener('click', (event) => {
         var savedState = localStorage.getItem('sidebar-' + getPortalId(portal));
          event.preventDefault();
        var content = portal.querySelector('.vector-menu-content, .body');
         
       
          const content = button.nextElementSibling;
        if (savedState === 'expanded' && content) {
         
          portal.classList.remove('collapsed');
          if (content && content.classList.contains('collapsible-content')) {
          content.style.maxHeight = content.scrollHeight + 'px';
            const isActive = button.classList.contains(CONFIG.CLASSES.ACTIVE);
         }
           
       } catch (e) {
            if (isActive) {
        // Ignore localStorage errors
              button.classList.remove(CONFIG.CLASSES.ACTIVE);
       }
              content.style.maxHeight = '0';
    });
            } else {
      
              button.classList.add(CONFIG.CLASSES.ACTIVE);
    // Helper function to get a unique ID for each portal
              content.style.maxHeight = content.scrollHeight + 'px';
     function getPortalId(portal) {
            }
       var heading = portal.querySelector('.vector-menu-heading, h3');
          }
         });
       });
    },
 
    /**
    * Initializes all collapsible section types
    */
    init: function() {
       this.initCollapsibleHeaders();
      this.initCollapsibleButtons();
     }
  };
 
  // ============================================================================
  // SIDEBAR MANAGEMENT
  // ============================================================================
 
  /**
  * Sidebar Management Module
  * Handles Vector Legacy sidebar collapsible functionality
  */
  const SidebarManager = {
    /**
    * Gets a unique portal ID for localStorage
    * @param {Element} portal - Portal element
    * @returns {string} Unique portal identifier
    */
     getPortalId: function(portal) {
       const heading = portal.querySelector('.vector-menu-heading, h3');
       if (heading) {
       if (heading) {
         return heading.textContent.trim().toLowerCase().replace(/\s+/g, '-');
         return heading.textContent.trim().toLowerCase().replace(/\s+/g, '-');
       }
       }
       return 'unknown-portal';
       return 'unknown-portal';
     },
     }
 
   
     /**
     // Recalculate heights when window is resized
    * Initializes sidebar collapsible functionality
     window.addEventListener('resize', function() {
    */
       portals.forEach(function(portal) {
     initSidebarCollapsible: function() {
         if (!portal.classList.contains('collapsed')) {
      // Only for Vector Legacy skin
           var content = portal.querySelector('.vector-menu-content, .body');
      if (!document.body.classList.contains('skin-vector-legacy')) {
        return;
      }
 
      const panel = safeGetElementById('mw-panel');
      if (!panel) return;
 
      const portals = safeQuerySelectorAll('.vector-menu-portal, .portal', panel);
     
      // Auto-collapse all sections except first (Navigation)
       portals.forEach((portal, index) => {
         if (index > 0) {
          portal.classList.add(CONFIG.CLASSES.COLLAPSED);
           const content = portal.querySelector('.vector-menu-content, .body');
           if (content) {
           if (content) {
             content.style.maxHeight = '0';
             content.style.maxHeight = content.scrollHeight + 'px';
           }
           }
         }
         }
       });
       });
    });
  }


      // Add click handlers to headings
  // Guardian Type Decision Helper functionality
      const headings = safeQuerySelectorAll('.vector-menu-heading, .portal h3', panel);
  function initGuardianDecisionHelper() {
     
    // Initialize accordion functionality for guardian types
      headings.forEach(heading => {
    function initAccordion() {
        heading.addEventListener('click', (event) => {
      const accordions = document.getElementsByClassName("guardian-accordion");
          event.preventDefault();
      for (let i = 0; i < accordions.length; i++) {
          event.stopPropagation();
        accordions[i].addEventListener("click", function() {
         
           this.classList.toggle("active");
          const portal = heading.closest('.vector-menu-portal, .portal');
           const panel = this.nextElementSibling;
          if (!portal) return;
           if (panel.style.maxHeight) {
 
             panel.style.maxHeight = null;
          const content = portal.querySelector('.vector-menu-content, .body');
          if (!content) return;
 
           const isCollapsed = portal.classList.contains(CONFIG.CLASSES.COLLAPSED);
           const portalId = this.getPortalId(portal);
 
           if (isCollapsed) {
            // Expand
            portal.classList.remove(CONFIG.CLASSES.COLLAPSED);
             content.style.maxHeight = content.scrollHeight + 'px';
            safeSetLocalStorage(CONFIG.STORAGE_KEYS.SIDEBAR_PREFIX + portalId, 'expanded');
           } else {
           } else {
             // Collapse
             panel.style.maxHeight = panel.scrollHeight + "px";
            portal.classList.add(CONFIG.CLASSES.COLLAPSED);
            content.style.maxHeight = '0';
            safeSetLocalStorage(CONFIG.STORAGE_KEYS.SIDEBAR_PREFIX + portalId, 'collapsed');
           }
           }
         });
         });
       });
       }
 
      // Restore saved states
      portals.forEach((portal, index) => {
        if (index === 0) return; // Skip first portal
       
        const savedState = safeGetLocalStorage(CONFIG.STORAGE_KEYS.SIDEBAR_PREFIX + this.getPortalId(portal));
        const content = portal.querySelector('.vector-menu-content, .body');
       
        if (savedState === 'expanded' && content) {
          portal.classList.remove(CONFIG.CLASSES.COLLAPSED);
          content.style.maxHeight = content.scrollHeight + 'px';
        }
      });
 
      // Recalculate heights on resize
      window.addEventListener('resize', () => {
        portals.forEach(portal => {
          if (!portal.classList.contains(CONFIG.CLASSES.COLLAPSED)) {
            const content = portal.querySelector('.vector-menu-content, .body');
            if (content) {
              content.style.maxHeight = content.scrollHeight + 'px';
            }
          }
        });
      });
    },
 
    /**
    * Initializes sidebar management
    */
    init: function() {
      this.initSidebarCollapsible();
     }
     }
  };
   
 
    // Function to show guardian by difficulty level
  // ============================================================================
     function showGuardianByDifficulty(level) {
  // GUARDIAN DECISION HELPER
       const accordions = document.getElementsByClassName("guardian-accordion");
  // ============================================================================
       let targetAccordion;
 
        
  /**
       // Reset all accordions
  * Guardian Decision Helper Module
       for (let i = 0; i < accordions.length; i++) {
  * Manages guardian type recommendation system
        accordions[i].classList.remove("active");
  */
        accordions[i].nextElementSibling.style.maxHeight = null;
  const GuardianDecisionHelper = {
      }
    /**
    * Guardian type difficulty mapping
    */
     DIFFICULTY_MAP: {
       '1': 'regular',
       '2': 'mighty',
      '3': 'legendary',
       '4': 'superior',
       '5': 'accomplished'
    },
 
    /**
    * Guardian type descriptions
    */
    TYPE_DESCRIPTIONS: {
      'regular': 'Perfect for beginners with limited time',
      'mighty': 'Good balance of effort and power',
       'legendary': 'Excellent for endgame content',
      'superior': 'For dedicated players seeking optimization',
      'accomplished': 'For true perfectionists'
    },
 
    /**
    * Initializes accordion functionality
    */
    initAccordion: function() {
      const accordions = safeQuerySelectorAll(CONFIG.SELECTORS.GUARDIAN_ACCORDION);
        
        
       accordions.forEach(accordion => {
       // Select the appropriate accordion based on difficulty level
        accordion.addEventListener('click', function() {
      switch(level) {
           this.classList.toggle(CONFIG.CLASSES.ACTIVE);
        case "1":
           const panel = this.nextElementSibling;
           targetAccordion = document.querySelector(".regular-accordion");
         
           break;
           if (panel) {
        case "2":
            if (panel.style.maxHeight) {
           targetAccordion = document.querySelector(".mighty-accordion");
              panel.style.maxHeight = null;
          break;
            } else {
        case "3":
              panel.style.maxHeight = panel.scrollHeight + 'px';
          targetAccordion = document.querySelector(".legendary-accordion");
            }
           break;
           }
         case "4":
         });
          targetAccordion = document.querySelector(".superior-accordion");
      });
          break;
    },
        case "5":
 
          targetAccordion = document.querySelector(".accomplished-accordion");
    /**
          break;
    * Shows guardian by difficulty level
      }
    * @param {string} level - Difficulty level (1-5)
    */
    showGuardianByDifficulty: function(level) {
      const accordions = safeQuerySelectorAll(CONFIG.SELECTORS.GUARDIAN_ACCORDION);
        
        
       // Reset all accordions
       // Activate the target accordion
      accordions.forEach(accordion => {
       if (targetAccordion) {
        accordion.classList.remove(CONFIG.CLASSES.ACTIVE);
         targetAccordion.classList.add("active");
        const panel = accordion.nextElementSibling;
        const panel = targetAccordion.nextElementSibling;
        if (panel) {
        panel.style.maxHeight = panel.scrollHeight + "px";
          panel.style.maxHeight = null;
       
        }
        // Scroll to the accordion
      });
        setTimeout(function() {
 
          targetAccordion.scrollIntoView({ behavior: 'smooth', block: 'center' });
      // Find and activate target accordion
        }, 300);
      const guardianType = this.DIFFICULTY_MAP[level];
       if (guardianType) {
         const targetAccordion = safeQuerySelector(`.${guardianType}-accordion`);
        if (targetAccordion) {
          targetAccordion.classList.add(CONFIG.CLASSES.ACTIVE);
          const panel = targetAccordion.nextElementSibling;
          if (panel) {
            panel.style.maxHeight = panel.scrollHeight + 'px';
          }
         
          // Scroll to accordion
          setTimeout(() => {
            targetAccordion.scrollIntoView({  
              behavior: 'smooth',  
              block: 'center'  
            });
          }, CONFIG.DELAYS.ACCORDION_SCROLL_DELAY);
        }
       }
       }
     },
     }


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


     /**
     // Function to update recommendation based on selections
    * Updates recommendation based on selected options
     function updateRecommendation() {
    */
       const selectedOptions = document.querySelectorAll('.decision-options .option.selected');
     updateRecommendation: function() {
       const selectedOptions = safeQuerySelectorAll('.decision-options .option.selected');
      const recommendedTypeElement = safeGetElementById('recommendedType');
        
        
      if (!recommendedTypeElement) return;
       // If no options selected, show default message
 
       // Show default message if no options selected
       if (selectedOptions.length === 0) {
       if (selectedOptions.length === 0) {
         recommendedTypeElement.textContent = 'Select options above';
         document.getElementById('recommendedType').textContent = 'Select options above';
         return;
         return;
       }
       }
 
     
       // Count votes for each guardian type
       // Count votes for each guardian type
       const votes = {
       const votes = {
Line 910: Line 489:
         'accomplished': 0
         'accomplished': 0
       };
       };
 
     
       // Tally votes from selected options
       // Tally votes from selected options
       selectedOptions.forEach(option => {
       selectedOptions.forEach(option => {
         const types = option.getAttribute('data-type');
         const types = option.getAttribute('data-type').split(',');
        if (types) {
        types.forEach(type => {
          types.split(',').forEach(type => {
          votes[type]++;
            if (votes.hasOwnProperty(type.trim())) {
        });
              votes[type.trim()]++;
            }
          });
        }
       });
       });
 
     
       // Find type with most votes
       // Find the type with the most votes
       let maxVotes = 0;
       let maxVotes = 0;
       let recommendedType = '';
       let recommendedType = '';
       let tiedTypes = [];
       let tiedTypes = [];
 
     
       for (const type in votes) {
       for (const type in votes) {
         if (votes[type] > maxVotes) {
         if (votes[type] > maxVotes) {
Line 937: Line 512:
         }
         }
       }
       }
 
     
       // Handle ties by choosing more challenging type
       // Handle ties by recommending the more challenging type
       if (tiedTypes.length > 1) {
       if (tiedTypes.length > 1) {
         const difficultyOrder = ['regular', 'mighty', 'legendary', 'superior', 'accomplished'];
         const difficultyOrder = ['regular', 'mighty', 'legendary', 'superior', 'accomplished'];
Line 951: Line 526:
         });
         });
       }
       }
 
     
       // Format recommendation text
       // Format the recommendation with additional context
       const formattedType = recommendedType.charAt(0).toUpperCase() + recommendedType.slice(1) + ' Guardian';
       let formattedType = recommendedType.charAt(0).toUpperCase() + recommendedType.slice(1) + ' Guardian';
       const description = this.TYPE_DESCRIPTIONS[recommendedType] || '';
       let recommendationText = formattedType;
      const recommendationText = `${formattedType} - ${description}`;
     
 
      // Add context based on the recommended type
       // Update display
      switch(recommendedType) {
       recommendedTypeElement.textContent = recommendationText;
        case 'regular':
       this.highlightRecommendedType(recommendedType);
          recommendationText += ' - Perfect for beginners with limited time';
     },
          break;
 
        case 'mighty':
     /**
          recommendationText += ' - Good balance of effort and power';
    * Highlights the recommended guardian type
          break;
    * @param {string} type - Guardian type to highlight
        case 'legendary':
    */
          recommendationText += ' - Excellent for endgame content';
     highlightRecommendedType: function(type) {
          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
       // Remove highlight from all accordions
       const accordions = safeQuerySelectorAll(CONFIG.SELECTORS.GUARDIAN_ACCORDION);
       const accordions = document.querySelectorAll('.guardian-accordion');
       accordions.forEach(accordion => {
       accordions.forEach(accordion => {
         accordion.classList.remove(CONFIG.CLASSES.RECOMMENDED);
         accordion.classList.remove('recommended');
       });
       });
 
     
       // Add highlight to recommended type
       // Add highlight to the recommended type
       const recommendedAccordion = safeQuerySelector(`.${type}-accordion`);
       const recommendedAccordion = document.querySelector('.' + type + '-accordion');
       if (recommendedAccordion) {
       if (recommendedAccordion) {
         recommendedAccordion.classList.add(CONFIG.CLASSES.RECOMMENDED);
         recommendedAccordion.classList.add('recommended');
          
          
         // Scroll to recommended type if not visible
         // Scroll to the recommended type if not visible
         if (!isElementInViewport(recommendedAccordion)) {
         const isVisible = isElementInViewport(recommendedAccordion);
           recommendedAccordion.scrollIntoView({  
        if (!isVisible) {
            behavior: 'smooth',  
           recommendedAccordion.scrollIntoView({ behavior: 'smooth', block: 'center' });
            block: 'center'  
          });
         }
         }
       }
       }
     },
     }
 
   
     /**
     // Helper function to check if an element is in the viewport
    * Initializes Guardian Decision Helper
     function isElementInViewport(el) {
    */
       const rect = el.getBoundingClientRect();
     init: function() {
       return (
       // Only initialize if guardian elements exist
        rect.top >= 0 &&
       const hasGuardianElements =  
        rect.left >= 0 &&
         safeQuerySelector(CONFIG.SELECTORS.GUARDIAN_ACCORDION) ||
         rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        safeQuerySelector('.decision-options');
        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();
        
        
       if (hasGuardianElements) {
       // Make functions available globally
        this.initAccordion();
      window.updateGuardianRecommendation = updateRecommendation;
        this.initDecisionHelper();
      window.showGuardianByDifficulty = showGuardianByDifficulty;
       
        // Make functions globally available
        window.updateGuardianRecommendation = this.updateRecommendation.bind(this);
        window.showGuardianByDifficulty = this.showGuardianByDifficulty.bind(this);
      }
     }
     }
   };
   }
 


   // ============================================================================
   // User menu collapsible functionality
  // USER MENU MANAGEMENT
   function initUserMenuCollapsible() {
  // ============================================================================
    // Only initialize if we're in Vector Legacy skin
 
    if (!document.body.classList.contains('skin-vector-legacy')) return;
  /**
   
  * User Menu Management Module
    // Get the personal tools container
  * Handles collapsible user menu functionality
    var personalTools = document.getElementById('p-personal');
  */
    if (!personalTools) return;
   const UserMenuManager = {
   
    /**
    // Get the list of user menu items
    * Initializes user menu collapsible functionality
    var userMenuItems = [
    */
      document.getElementById('pt-userpage'),
    initUserMenuCollapsible: function() {
      document.getElementById('pt-mytalk'),
      // Only for Vector Legacy skin
      document.getElementById('pt-preferences'),
      if (!document.body.classList.contains('skin-vector-legacy')) {
      document.getElementById('pt-watchlist'),
        return;
      document.getElementById('pt-mycontris'),
      }
      document.getElementById('pt-logout')
 
    ].filter(Boolean); // Filter out any null items
      const personalTools = safeGetElementById('p-personal');
   
      if (!personalTools) return;
    if (userMenuItems.length === 0) return;
 
   
      // Get user menu items
    // Create a container for our collapsible menu
      const userMenuItems = [
    var menuContainer = document.createElement('div');
        'pt-userpage', 'pt-mytalk', 'pt-preferences',
    menuContainer.id = 'user-menu-collapsible';
        'pt-watchlist', 'pt-mycontris', 'pt-logout'
    menuContainer.className = 'user-menu-container';
      ].map(id => safeGetElementById(id)).filter(Boolean);
   
 
    // Create the header/toggle button
      if (userMenuItems.length === 0) return;
    var menuHeader = document.createElement('div');
 
    menuHeader.className = 'user-menu-header';
      // Create menu container
    menuHeader.textContent = 'User';
      const menuContainer = document.createElement('div');
    menuHeader.addEventListener('click', function() {
      menuContainer.id = 'user-menu-collapsible';
      menuContainer.classList.toggle('expanded');
      menuContainer.className = 'user-menu-container';
 
      // Create header/toggle button
      const menuHeader = document.createElement('div');
      menuHeader.className = 'user-menu-header';
      menuHeader.textContent = 'User';
        
        
       menuHeader.addEventListener('click', () => {
       // Store the expanded state
         menuContainer.classList.toggle(CONFIG.CLASSES.EXPANDED);
      try {
       
         if (menuContainer.classList.contains('expanded')) {
        const state = menuContainer.classList.contains(CONFIG.CLASSES.EXPANDED)
          localStorage.setItem('user-menu-state', 'expanded');
          ? 'expanded' : 'collapsed';
        } else {
        safeSetLocalStorage(CONFIG.STORAGE_KEYS.USER_MENU_STATE, state);
          localStorage.setItem('user-menu-state', 'collapsed');
      });
 
      // Create content container
      const menuContent = document.createElement('div');
      menuContent.className = 'user-menu-content';
 
      const menuList = document.createElement('ul');
      menuList.className = 'user-menu-list';
 
      // Move user menu items to new container
      userMenuItems.forEach(item => {
        if (item && item.parentNode) {
          menuList.appendChild(item);
         }
         }
       });
       } catch (e) {
 
        // Ignore localStorage errors
      // Assemble menu
      }
       menuContent.appendChild(menuList);
    });
      menuContainer.appendChild(menuHeader);
   
      menuContainer.appendChild(menuContent);
    // Create the content container
      document.body.appendChild(menuContainer);
    var menuContent = document.createElement('div');
 
    menuContent.className = 'user-menu-content';
      // Restore saved state
   
       const savedState = safeGetLocalStorage(CONFIG.STORAGE_KEYS.USER_MENU_STATE);
    // Create a list to hold the menu items
    var menuList = document.createElement('ul');
    menuList.className = 'user-menu-list';
   
    // Move the user menu items to our new container
    userMenuItems.forEach(function(item) {
      if (item && item.parentNode) {
        menuList.appendChild(item);
       }
    });
   
    // Add the list to the content container
    menuContent.appendChild(menuList);
   
    // Assemble the menu
    menuContainer.appendChild(menuHeader);
    menuContainer.appendChild(menuContent);
   
    // Add the menu to the body for fixed positioning
    document.body.appendChild(menuContainer);
   
    // Check for saved state
    try {
       var savedState = localStorage.getItem('user-menu-state');
       if (savedState === 'expanded') {
       if (savedState === 'expanded') {
         menuContainer.classList.add(CONFIG.CLASSES.EXPANDED);
         menuContainer.classList.add('expanded');
       }
       }
 
    } catch (e) {
       // Close menu on outside click
       // Ignore localStorage errors
      document.addEventListener('click', (event) => {
    }
        if (menuContainer.classList.contains(CONFIG.CLASSES.EXPANDED) &&
   
            !menuContainer.contains(event.target)) {
    // Close menu when clicking outside
          menuContainer.classList.remove(CONFIG.CLASSES.EXPANDED);
    document.addEventListener('click', function(e) {
           safeSetLocalStorage(CONFIG.STORAGE_KEYS.USER_MENU_STATE, 'collapsed');
      if (menuContainer.classList.contains('expanded') &&  
          !menuContainer.contains(e.target)) {
        menuContainer.classList.remove('expanded');
       
        // Update stored state
        try {
           localStorage.setItem('user-menu-state', 'collapsed');
        } catch (e) {
          // Ignore localStorage errors
         }
         }
      });
      // Hover functionality
      menuContainer.addEventListener('mouseenter', () => {
        menuContainer.classList.add(CONFIG.CLASSES.EXPANDED);
      });
      menuContainer.addEventListener('mouseleave', () => {
        menuContainer.classList.remove(CONFIG.CLASSES.EXPANDED);
        safeSetLocalStorage(CONFIG.STORAGE_KEYS.USER_MENU_STATE, 'collapsed');
      });
    },
    /**
    * Initializes user menu management
    */
    init: function() {
      this.initUserMenuCollapsible();
    }
  };
  // ============================================================================
  // RESPONSIVE TABLE MANAGER
  // ============================================================================
  /**
  * Responsive Table Manager Module
  * Wraps tables in containers only for very small screens (< 480px)
  */
  const ResponsiveTableManager = {
    /**
    * Table selectors to make responsive
    */
    TABLE_SELECTORS: [
      'table.wikitable',
      'table.mw-datatable',
      'table.faq-table',
      'table.daily-table'
    ],
    /**
    * Checks if screen is very small (< 480px)
    * @returns {boolean} True if screen is very small
    */
    isVerySmallScreen: function() {
      return window.innerWidth < 550;
    },
    /**
    * Wraps a table in a container for small screens
    * @param {HTMLElement} table - Table element to wrap
    */
    wrapTable: function(table) {
      // Skip if already wrapped or not on small screen
      if (table.closest('.table-container') || !this.isVerySmallScreen()) {
        return;
       }
       }
 
    });
      // Create wrapper
   
       const wrapper = document.createElement('div');
    // Add hover functionality for better usability
       wrapper.className = 'table-container';
    menuContainer.addEventListener('mouseenter', function() {
       menuContainer.classList.add('expanded');
    });
   
    menuContainer.addEventListener('mouseleave', function() {
       menuContainer.classList.remove('expanded');
        
        
       // Insert wrapper before table
       // Update stored state
       table.parentNode.insertBefore(wrapper, table);
       try {
     
        localStorage.setItem('user-menu-state', 'collapsed');
      // Move table into wrapper
       } catch (e) {
      wrapper.appendChild(table);
         // Ignore localStorage errors
    },
 
    /**
    * Unwraps tables when screen becomes larger
    * @param {HTMLElement} table - Table element to unwrap
    */
    unwrapTable: function(table) {
      const wrapper = table.closest('.table-container');
       if (wrapper && !this.isVerySmallScreen()) {
         // Move table out of wrapper
        wrapper.parentNode.insertBefore(table, wrapper);
        // Remove wrapper
        wrapper.remove();
       }
       }
     },
     });
 
  }
    /**
    * Processes all tables based on screen size
    */
    processAllTables: function() {
      this.TABLE_SELECTORS.forEach(selector => {
        const tables = document.querySelectorAll(selector);
        tables.forEach(table => {
          if (this.isVerySmallScreen()) {
            this.wrapTable(table);
          } else {
            this.unwrapTable(table);
          }
        });
      });
    },


    /**
   // Initialize collapsible sections when DOM is ready
    * Handles window resize events
    */
    handleResize: function() {
      this.processAllTables();
    },
 
    /**
    * Observes for dynamically added tables
    */
    observeNewTables: function() {
      const observer = new MutationObserver((mutations) => {
        mutations.forEach(mutation => {
          mutation.addedNodes.forEach(node => {
            if (node.nodeType === Node.ELEMENT_NODE) {
              // Check if the added node is a table
              this.TABLE_SELECTORS.forEach(selector => {
                if (node.matches && node.matches(selector)) {
                  if (this.isVerySmallScreen()) {
                    this.wrapTable(node);
                  }
                }
                // Check for tables within the added node
                const tables = node.querySelectorAll ? node.querySelectorAll(selector) : [];
                tables.forEach(table => {
                  if (this.isVerySmallScreen()) {
                    this.wrapTable(table);
                  }
                });
              });
            }
          });
        });
      });
 
      observer.observe(document.body, {
        childList: true,
        subtree: true
      });
    },
 
    /**
    * Initializes responsive table management
    */
    init: function() {
      // Process existing tables
      this.processAllTables();
     
      // Listen for window resize
      window.addEventListener('resize', () => {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = setTimeout(() => {
          this.handleResize();
        }, 150);
      });
     
      // Observe for new tables
      this.observeNewTables();
     
      console.log('Responsive Table Manager initialized');
    }
  };
 
   // ============================================================================
  // MAIN INITIALIZATION
  // ============================================================================
 
  /**
  * Main initialization function
  * Initializes all modules when DOM is ready
  */
   function initializeAll() {
   function initializeAll() {
     try {
     createHamburgerMenu();
      // Initialize all modules
    initCollapsibleSections();
      TabNavigation.init();
    initSidebarCollapsible();
      NavigationHandlers.init();
    initGuardianDecisionHelper();
      WikiLinkProcessor.init();
     initUserMenuCollapsible();
      MobileInterface.init();
      CollapsibleSections.init();
      SidebarManager.init();
      GuardianDecisionHelper.init();
      UserMenuManager.init();
      ResponsiveTableManager.init();
     
      console.log('MediaWiki Common JavaScript initialized successfully');
     } catch (error) {
      console.error('Error initializing MediaWiki Common JavaScript:', error);
    }
   }
   }


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

Revision as of 06:05, 17 September 2025

// 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';
          }
        }
      });
    });
  }

  // Vector Legacy Sidebar Collapsible functionality
  function initSidebarCollapsible() {
    // Only initialize if we're in Vector Legacy skin
    if (!document.body.classList.contains('skin-vector-legacy')) return;
    
    var panel = document.getElementById('mw-panel');
    if (!panel) return;
    
    // Get all portals (both vector-menu-portal and portal classes)
    var portals = panel.querySelectorAll('.vector-menu-portal, .portal');
    
    // Auto-collapse all sections except the first one (Navigation) on page load
    portals.forEach(function(portal, index) {
      if (index > 0) { // Skip first portal (Navigation)
        portal.classList.add('collapsed');
        var content = portal.querySelector('.vector-menu-content, .body');
        if (content) {
          content.style.maxHeight = '0';
        }
      }
    });
    
    // Add click handlers to portal headings
    var headings = panel.querySelectorAll('.vector-menu-heading, .portal h3');
    
    headings.forEach(function(heading) {
      heading.addEventListener('click', function(e) {
        e.preventDefault();
        e.stopPropagation();
        
        var portal = heading.closest('.vector-menu-portal, .portal');
        if (!portal) return;
        
        var content = portal.querySelector('.vector-menu-content, .body');
        if (!content) return;
        
        var isCollapsed = portal.classList.contains('collapsed');
        
        if (isCollapsed) {
          // Expand
          portal.classList.remove('collapsed');
          content.style.maxHeight = content.scrollHeight + 'px';
          
          // Store the expanded state
          try {
            localStorage.setItem('sidebar-' + getPortalId(portal), 'expanded');
          } catch (e) {
            // Ignore localStorage errors
          }
        } else {
          // Collapse
          portal.classList.add('collapsed');
          content.style.maxHeight = '0';
          
          // Store the collapsed state
          try {
            localStorage.setItem('sidebar-' + getPortalId(portal), 'collapsed');
          } catch (e) {
            // Ignore localStorage errors
          }
        }
      });
    });
    
    // Restore saved states from localStorage
    portals.forEach(function(portal, index) {
      if (index === 0) return; // Skip first portal (always expanded)
      
      try {
        var savedState = localStorage.getItem('sidebar-' + getPortalId(portal));
        var content = portal.querySelector('.vector-menu-content, .body');
        
        if (savedState === 'expanded' && content) {
          portal.classList.remove('collapsed');
          content.style.maxHeight = content.scrollHeight + 'px';
        }
      } catch (e) {
        // Ignore localStorage errors
      }
    });
    
    // Helper function to get a unique ID for each portal
    function getPortalId(portal) {
      var heading = portal.querySelector('.vector-menu-heading, h3');
      if (heading) {
        return heading.textContent.trim().toLowerCase().replace(/\s+/g, '-');
      }
      return 'unknown-portal';
    }
    
    // Recalculate heights when window is resized
    window.addEventListener('resize', function() {
      portals.forEach(function(portal) {
        if (!portal.classList.contains('collapsed')) {
          var content = portal.querySelector('.vector-menu-content, .body');
          if (content) {
            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;
    }
  }
  

  // User menu collapsible functionality
  function initUserMenuCollapsible() {
    // Only initialize if we're in Vector Legacy skin
    if (!document.body.classList.contains('skin-vector-legacy')) return;
    
    // Get the personal tools container
    var personalTools = document.getElementById('p-personal');
    if (!personalTools) return;
    
    // Get the list of user menu items
    var userMenuItems = [
      document.getElementById('pt-userpage'),
      document.getElementById('pt-mytalk'),
      document.getElementById('pt-preferences'),
      document.getElementById('pt-watchlist'),
      document.getElementById('pt-mycontris'),
      document.getElementById('pt-logout')
    ].filter(Boolean); // Filter out any null items
    
    if (userMenuItems.length === 0) return;
    
    // Create a container for our collapsible menu
    var menuContainer = document.createElement('div');
    menuContainer.id = 'user-menu-collapsible';
    menuContainer.className = 'user-menu-container';
    
    // Create the header/toggle button
    var menuHeader = document.createElement('div');
    menuHeader.className = 'user-menu-header';
    menuHeader.textContent = 'User';
    menuHeader.addEventListener('click', function() {
      menuContainer.classList.toggle('expanded');
      
      // Store the expanded state
      try {
        if (menuContainer.classList.contains('expanded')) {
          localStorage.setItem('user-menu-state', 'expanded');
        } else {
          localStorage.setItem('user-menu-state', 'collapsed');
        }
      } catch (e) {
        // Ignore localStorage errors
      }
    });
    
    // Create the content container
    var menuContent = document.createElement('div');
    menuContent.className = 'user-menu-content';
    
    // Create a list to hold the menu items
    var menuList = document.createElement('ul');
    menuList.className = 'user-menu-list';
    
    // Move the user menu items to our new container
    userMenuItems.forEach(function(item) {
      if (item && item.parentNode) {
        menuList.appendChild(item);
      }
    });
    
    // Add the list to the content container
    menuContent.appendChild(menuList);
    
    // Assemble the menu
    menuContainer.appendChild(menuHeader);
    menuContainer.appendChild(menuContent);
    
    // Add the menu to the body for fixed positioning
    document.body.appendChild(menuContainer);
    
    // Check for saved state
    try {
      var savedState = localStorage.getItem('user-menu-state');
      if (savedState === 'expanded') {
        menuContainer.classList.add('expanded');
      }
    } catch (e) {
      // Ignore localStorage errors
    }
    
    // Close menu when clicking outside
    document.addEventListener('click', function(e) {
      if (menuContainer.classList.contains('expanded') && 
          !menuContainer.contains(e.target)) {
        menuContainer.classList.remove('expanded');
        
        // Update stored state
        try {
          localStorage.setItem('user-menu-state', 'collapsed');
        } catch (e) {
          // Ignore localStorage errors
        }
      }
    });
    
    // Add hover functionality for better usability
    menuContainer.addEventListener('mouseenter', function() {
      menuContainer.classList.add('expanded');
    });
    
    menuContainer.addEventListener('mouseleave', function() {
      menuContainer.classList.remove('expanded');
      
      // Update stored state
      try {
        localStorage.setItem('user-menu-state', 'collapsed');
      } catch (e) {
        // Ignore localStorage errors
      }
    });
  }

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

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

})();