imported>Dawning
(by SublimeText.Mediawiker)
m sound maybe
 
(56 intermediate revisions by 2 users not shown)
Line 3: Line 3:
/* DRUID */
/* DRUID */
$(function () {
$(function () {
  $(".druid-main-images-label").off("click");
    $(".druid-main-images-label").off("click");
  $(".druid-main-images-label").click(function () {
    $(".druid-main-images-label").click(function () {
    var $parent = $(this).closest(".druid-container");
      var $parent = $(this).closest(".druid-container");
    $parent.find(".druid-toggleable").removeClass("focused");
      $parent.find(".druid-toggleable").removeClass("focused");
    var i = $(this).attr("data-druid");
      var i = $(this).attr("data-druid");
    $parent.find(".druid-toggleable[data-druid=" + i + "]").addClass("focused");
      $parent.find(".druid-toggleable[data-druid=" + i + "]").addClass("focused");
   });
    });
    
    $(".druid-collapsible").off("click");
    $(".druid-collapsible").click(function () {
      var kind = $(this).attr("data-druid-section");
      $(this).toggleClass("druid-collapsible-collapsed");
      $(this)
        .closest(".druid-container")
        .find("[data-druid-section-row=" + kind + "]")
        .toggleClass("druid-collapsed");
    });


  $(".druid-collapsible").off("click");
    $(".druid-container").on("click", ".worn-toggle-button", function (e) {
  $(".druid-collapsible").click(function () {
        e.preventDefault();
    var kind = $(this).attr("data-druid-section");
        e.stopPropagation();
    $(this).toggleClass("druid-collapsible-collapsed");
    $(this)
      .closest(".druid-container")
      .find("[data-druid-section-row=" + kind + "]")
      .toggleClass("druid-collapsed");
  });
});
/* End DRUID */


/* [[Template:Spoiler]] */
        var $btn = $(this);
$(function () {
        var $container = $btn.closest('.has-worn-variant');
$('.spoiler-content')
        var $base = $container.find('.image-base');
.off('click') // in case this code is loaded twice
        var $worn = $container.find('.image-worn');
.on('click', function(e){
$(this).toggleClass('show');
}).find('a').on('click', function(e){
e.stopPropagation();
});


});
         if ($container.hasClass('is-worn-active')) {
/* End Template:Spoiler */
            $container.removeClass('is-worn-active');
 
             $worn.hide();
 
            $base.show();
/* Link to imported modules from Lua code */
            $btn.text('Show Worn');
$(function() {
         } else {
    var config = mw.config.get([
             $container.addClass('is-worn-active');
        'wgCanonicalNamespace',
             $base.hide();
         'wgFormattedNamespaces'
             $worn.show();
    ]);
             $btn.text('Show Normal');
    if (config.wgCanonicalNamespace !== 'Module') {
        return;
    }
    var localizedNamespace = config.wgFormattedNamespaces[828];
    $('.s1, .s2, .s').each(function() {
        var $this = $(this);
        var html = $this.html();
        var quote = html[0];
        var isLongStringQuote = quote === '[';
        var quoteRE = new RegExp('^\\' + quote + '|\\' + quote + '$', 'g');
        if (isLongStringQuote) {
             quoteRE = /^\[\[|\]\]$/g;
        }
        var name = html.replace(quoteRE, '');
        var isEnglishPrefix = name.startsWith('Module:');
        var isLocalizedPrefix = name.startsWith(localizedNamespace + ':');
         var isDevPrefix = name.startsWith('Dev:');
        if (isEnglishPrefix || isLocalizedPrefix || isDevPrefix) {
             var attrs = {
                href: mw.util.getUrl(name)
            };
            if (isDevPrefix) {
                attrs.href = 'https://commons.wiki.gg/wiki/Module:' + mw.util.wikiUrlencode(name.replace('Dev:', ''));
                attrs.target = '_blank';
                attrs.rel = 'noopener';
             }
            var link = mw.html.element('a', attrs, name);
             var str = quote + link + quote;
            if (isLongStringQuote) {
                str = '[[' + link + ']]';
            }
             $this.html(str);
         }
         }
       
        console.log("Toggle state: " + ($container.hasClass('is-worn-active') ? "Worn" : "Normal"));
     });
     });
});
  });
  /* End DRUID */
 
  /* Sound */
  (function () {
    let currentAudio = null;
    let currentBtn = null;


/* dynamic gif displayer ONLY TOUCH IF YOU KNOW WHAT YOU ARE DOING */
    $(document).on('click', '.sound-button', function () {
        const $btn = $(this);
        const soundUrl = $btn.data('sound-file');


/**
        if (!soundUrl) return;
* Dynamic GIF Displayer for Telos Realms Wiki
* Works with Fandom Tooltips extension
*/


(function() {
        if (currentAudio && currentBtn && currentBtn[0] === $btn[0]) {
    'use strict';
            if (!currentAudio.paused) {
   
                currentAudio.pause();
    console.log('Dynamic GIF Displayer: Script loading...');
                currentAudio.currentTime = 0;
   
                $btn.removeClass('playing');
    // Configuration
                return; // Stop here
    const CONFIG = {
             }
        containerClass: 'dynamic-gif-container',
        displayClass: 'gif-display-area',
        autoExpand: true,
        expandDelay: 200,
        collapseDelay: 1000
    };
   
    let expandTimer = null;
    let collapseTimer = null;
    let currentGif = null;
   
    /**
    * Initialize the dynamic GIF displayer
    */
    function init() {
        console.log('Dynamic GIF Displayer: Initializing...');
       
        const containers = document.querySelectorAll('.' + CONFIG.containerClass);
        console.log('Dynamic GIF Displayer: Found', containers.length, 'containers');
       
        containers.forEach(container => {
            console.log('Dynamic GIF Displayer: Setting up container', container.id);
            setupContainer(container);
            attachTooltipListeners(container);
        });
    }
   
    /**
    * Setup the GIF display container
    */
    function setupContainer(container) {
        const displayArea = container.querySelector('.' + CONFIG.displayClass);
        if (!displayArea) {
             console.error('Dynamic GIF Displayer: No display area found');
            return;
         }
         }
       
 
         // Add hover listeners to prevent auto-collapse when hovering the display
         if (currentAudio) {
        displayArea.addEventListener('mouseenter', () => {
             currentAudio.pause();
             clearTimeout(collapseTimer);
             currentAudio.currentTime = 0;
        });
             $('.sound-button').removeClass('playing');
       
        displayArea.addEventListener('mouseleave', () => {
            scheduleCollapse(container);
        });
    }
   
    /**
    * Attach listeners to all tooltips within the skill tree area
    */
    function attachTooltipListeners(container) {
        console.log('Dynamic GIF Displayer: Attaching tooltip listeners...');
       
        // Build GIF map from the data store
        const gifMap = buildGifMap(container);
        console.log('Dynamic GIF Displayer: GIF map', gifMap);
       
        // Find all tooltips on the entire page
        const tooltips = document.querySelectorAll('.advanced-tooltip');
        console.log('Dynamic GIF Displayer: Found', tooltips.length, 'tooltips');
       
        tooltips.forEach((tooltip) => {
             const skillTitle = tooltip.querySelector('.skill-title');
            if (!skillTitle) return;
           
            const skillName = skillTitle.textContent.trim();
            const gifUrl = gifMap[skillName];
           
            console.log('Dynamic GIF Displayer: Processing tooltip:', skillName, 'GIF:', gifUrl);
              
            if (gifUrl) {
                // Remove existing listeners to prevent duplicates
                tooltip.removeEventListener('mouseenter', tooltip._gifHoverHandler);
                tooltip.removeEventListener('mouseleave', tooltip._gifLeaveHandler);
               
                // Create and store handlers
                tooltip._gifHoverHandler = () => handleTooltipHover(container, gifUrl, skillName);
                tooltip._gifLeaveHandler = () => scheduleCollapse(container);
               
                // Add hover listeners
                tooltip.addEventListener('mouseenter', tooltip._gifHoverHandler);
                tooltip.addEventListener('mouseleave', tooltip._gifLeaveHandler);
               
                console.log('Dynamic GIF Displayer: Attached listeners to', skillName);
            }
        });
    }
   
    /**
    * Build a map of skill names to GIF URLs from data-gif-list attribute
    */
    function buildGifMap(container) {
        const gifMap = {};
        const listData = container.getAttribute('data-gif-list');
       
        if (!listData) {
            console.error('Dynamic GIF Displayer: No data-gif-list attribute found');
            return gifMap;
         }
         }
        const audio = new Audio(soundUrl);
        currentAudio = audio;
        currentBtn = $btn;
          
          
         console.log('Dynamic GIF Displayer: Raw list data:', listData);
         $btn.addClass('playing');
          
         audio.play().catch(function(error) {
        // Parse the semicolon-separated list
            console.error("Playback failed:", error);
        const entries = listData.split(';').filter(e => e.trim());
             $btn.removeClass('playing');
        console.log('Dynamic GIF Displayer: Found', entries.length, 'entries');
       
        entries.forEach(entry => {
            const parts = entry.split(':');
             if (parts.length >= 2) {
                const skillName = parts[0].trim();
                const gifFile = parts.slice(1).join(':').trim(); // In case filename has colons
               
                if (skillName && gifFile) {
                    // Construct full URL - try multiple possible paths
                    let gifUrl;
                    if (typeof mw !== 'undefined' && mw.config) {
                        // Use MediaWiki's file path
                        gifUrl = mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/images/' + gifFile;
                    } else {
                        // Fallback to relative path
                        gifUrl = '/images/' + gifFile;
                    }
                   
                    gifMap[skillName] = gifUrl;
                    console.log('Dynamic GIF Displayer: Mapped "' + skillName + '" to', gifUrl);
                }
            }
         });
         });
          
 
         return gifMap;
         audio.onended = function() {
     }
            $btn.removeClass('playing');
   
            currentAudio = null;
    /**
            currentBtn = null;
    * Handle tooltip hover event
         };
    */
     });
})();
  /* End Sound */
 
  /* [[Template:Spoiler]] */
  $(function () {
      $('.spoiler-content')
      .off('click') // in case this code is loaded twice
      .on('click', function(e){
          $(this).toggleClass('show');
      }).find('a').on('click', function(e){
          e.stopPropagation();
      });
 
  });
  /* End Template:Spoiler */
 
  /* Link to imported modules from Lua code */
  $(function() {
      var config = mw.config.get([
          'wgCanonicalNamespace',
          'wgFormattedNamespaces'
      ]);
      if (config.wgCanonicalNamespace !== 'Module') {
          return;
      }
      var localizedNamespace = config.wgFormattedNamespaces[828];
      $('.s1, .s2, .s').each(function() {
          var $this = $(this);
          var html = $this.html();
          var quote = html[0];
          var isLongStringQuote = quote === '[';
          var quoteRE = new RegExp('^\\' + quote + '|\\' + quote + '$', 'g');
          if (isLongStringQuote) {
              quoteRE = /^\[\[|\]\]$/g;
          }
          var name = html.replace(quoteRE, '');
          var isEnglishPrefix = name.startsWith('Module:');
          var isLocalizedPrefix = name.startsWith(localizedNamespace + ':');
          var isDevPrefix = name.startsWith('Dev:');
          if (isEnglishPrefix || isLocalizedPrefix || isDevPrefix) {
              var attrs = {
                  href: mw.util.getUrl(name)
              };
              if (isDevPrefix) {
                  attrs.href = 'https://commons.wiki.gg/wiki/Module:' + mw.util.wikiUrlencode(name.replace('Dev:', ''));
                  attrs.target = '_blank';
                  attrs.rel = 'noopener';
              }
              var link = mw.html.element('a', attrs, name);
              var str = quote + link + quote;
              if (isLongStringQuote) {
                  str = '[[' + link + ']]';
              }
              $this.html(str);
          }
      });
  });
 
  /* dynamic gif displayer ONLY TOUCH IF YOU KNOW WHAT YOU ARE DOING */
 
  (function() {
      'use strict';
     
      console.log('Dynamic GIF Displayer: Script loading...');
     
      // Configuration
      const CONFIG = {
          containerClass: 'dynamic-gif-container',
          displayClass: 'gif-display-area',
          autoExpand: true,
          expandDelay: 200,
          collapseDelay: 1000
      };
     
      let expandTimer = null;
      let collapseTimer = null;
      let currentGif = null;
     
      function safeCreateError(message) {
          const div = document.createElement('div');
          div.className = 'gif-error';
          div.textContent = message;
          return div;
      }
     
      function safeCreatePlaceholder(message) {
          const div = document.createElement('div');
          div.className = 'gif-placeholder';
          div.textContent = message;
          return div;
      }
     
      function init() {
          console.log('Dynamic GIF Displayer: Initializing...');
         
          const containers = document.querySelectorAll('.' + CONFIG.containerClass);
          console.log('Dynamic GIF Displayer: Found', containers.length, 'containers');
         
          containers.forEach(container => {
              console.log('Dynamic GIF Displayer: Setting up container', container.id);
              setupContainer(container);
              attachTooltipListeners(container);
          });
      }
     
      function setupContainer(container) {
          const displayArea = container.querySelector('.' + CONFIG.displayClass);
          if (!displayArea) {
              console.error('Dynamic GIF Displayer: No display area found');
              return;
          }
         
          displayArea.addEventListener('mouseenter', () => {
              clearTimeout(collapseTimer);
          });
         
          displayArea.addEventListener('mouseleave', () => {
              scheduleCollapse(container);
          });
      }
     
      function attachTooltipListeners(container) {
          console.log('Dynamic GIF Displayer: Attaching tooltip listeners...');
         
          const gifMap = buildGifMap(container);
          console.log('Dynamic GIF Displayer: GIF map', gifMap);
         
          const tooltips = document.querySelectorAll('.advanced-tooltip');
          console.log('Dynamic GIF Displayer: Found', tooltips.length, 'tooltips');
         
          tooltips.forEach((tooltip) => {
              const skillTitle = tooltip.querySelector('.skill-title');
              if (!skillTitle) return;
             
              const skillName = skillTitle.textContent.trim();
              const gifUrl = gifMap[skillName];
             
              console.log('Dynamic GIF Displayer: Processing tooltip:', skillName, 'GIF:', gifUrl);
             
              if (gifUrl) {
                  // Remove old listeners if they exist
                  if (tooltip._gifHoverHandler) {
                      tooltip.removeEventListener('mouseenter', tooltip._gifHoverHandler);
                  }
                  if (tooltip._gifLeaveHandler) {
                      tooltip.removeEventListener('mouseleave', tooltip._gifLeaveHandler);
                  }
                 
                  tooltip._gifHoverHandler = () => handleTooltipHover(container, gifUrl, skillName);
                  tooltip._gifLeaveHandler = () => scheduleCollapse(container);
                 
                  tooltip.addEventListener('mouseenter', tooltip._gifHoverHandler);
                  tooltip.addEventListener('mouseleave', tooltip._gifLeaveHandler);
                 
                  console.log('Dynamic GIF Displayer: Attached listeners to', skillName);
              }
          });
      }
     
      function buildGifMap(container) {
          const gifMap = {};
          const listData = container.getAttribute('data-gif-list');
         
          if (!listData) {
              console.error('Dynamic GIF Displayer: No data-gif-list attribute found');
              return gifMap;
          }
         
          console.log('Dynamic GIF Displayer: Raw list data:', listData);
         
          const entries = listData.split(';').filter(e => e.trim());
          console.log('Dynamic GIF Displayer: Found', entries.length, 'entries');
         
          entries.forEach(entry => {
              const parts = entry.split(':');
              if (parts.length >= 2) {
                  const skillName = parts[0].trim();
                  const gifFile = parts.slice(1).join(':').trim();
                 
                  if (skillName && gifFile) {
                      let gifUrl;
                      if (typeof mw !== 'undefined' && mw.config) {
                          const scriptPath = mw.config.get('wgScriptPath') || '';
                          gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
                      } else {
                          gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
                      }
                     
                      gifMap[skillName] = gifUrl;
                      console.log('Dynamic GIF Displayer: Mapped "' + skillName + '" to', gifUrl);
                  }
              }
          });
         
          return gifMap;
      }
     
     function handleTooltipHover(container, gifUrl, skillName) {
     function handleTooltipHover(container, gifUrl, skillName) {
         console.log('Dynamic GIF Displayer: Hovering', skillName);
         console.log('Dynamic GIF Displayer: Hovering', skillName);
Line 233: Line 291:
         clearTimeout(expandTimer);
         clearTimeout(expandTimer);
          
          
        // Show GIF after delay (allows checking if different from current)
         expandTimer = setTimeout(() => {
         expandTimer = setTimeout(() => {
             showGif(container, gifUrl, skillName);
             showGif(container, gifUrl, skillName);
         }, CONFIG.expandDelay);
         }, CONFIG.expandDelay);
     }
     }
   
 
    /**
    * Show the GIF in the display area
    */
     function showGif(container, gifUrl, skillName) {
     function showGif(container, gifUrl, skillName) {
         console.log('Dynamic GIF Displayer: Showing GIF for', skillName, gifUrl);
         console.log('Dynamic GIF Displayer: Showing GIF for', skillName, gifUrl);
Line 247: Line 303:
         if (!displayArea) return;
         if (!displayArea) return;
          
          
        // Get the size setting
        const gifSize = container.getAttribute('data-gif-size') || '400';
       
        // Expand container if collapsed
         if (!container.classList.contains('expanded')) {
         if (!container.classList.contains('expanded')) {
             container.classList.add('expanded');
             container.classList.add('expanded');
Line 256: Line 308:
         }
         }
          
          
         // Update GIF if different from current
        const imgContainer = displayArea.querySelector('.gif-image-container');
         if (currentGif !== gifUrl) {
        const caption = displayArea.querySelector('.gif-caption');
             const imgContainer = displayArea.querySelector('.gif-image-container');
       
             const caption = displayArea.querySelector('.gif-caption');
         // Update caption immediately
        if (caption) {
            caption.textContent = skillName;
            caption.style.display = 'block';
        }
       
        // Only reload if different GIF
         if (currentGif === gifUrl) {
            console.log('Dynamic GIF Displayer: Already showing', skillName);
            return;
        }
       
        // Set currentGif IMMEDIATELY to prevent race conditions
        currentGif = gifUrl;
       
        if (imgContainer) {
             const placeholder = imgContainer.querySelector('.gif-placeholder');
            if (placeholder) {
                placeholder.remove();
            }
           
            // Don't set dynamic height - let CSS handle fixed sizing
           
             const gifFilename = gifUrl.split('/').pop();
            const decodedFilename = decodeURIComponent(gifFilename);
              
              
             if (imgContainer) {
             if (decodedFilename.toLowerCase() === 'blank' || decodedFilename.toLowerCase() === 'blank.gif') {
                 // Clear placeholder
                imgContainer.innerHTML = '';
                 const placeholder = imgContainer.querySelector('.gif-placeholder');
                imgContainer.appendChild(safeCreatePlaceholder('This node does not require a GIF due to its simplicity.'));
                 if (placeholder) {
                imgContainer.classList.remove('loading');
                     placeholder.remove();
            } else {
                 // Cancel any existing image loads
                 const existingImg = imgContainer.querySelector('img');
                 if (existingImg) {
                     existingImg.onload = null;
                    existingImg.onerror = null;
                 }
                 }
                  
                  
                // Set container height based on size parameter
                imgContainer.style.height = gifSize + 'px';
                imgContainer.style.minHeight = gifSize + 'px';
               
                // Add loading state
                 imgContainer.classList.add('loading');
                 imgContainer.classList.add('loading');
                  
                  
                // Create new image
                 const img = document.createElement('img');
                 const img = document.createElement('img');
                 img.src = gifUrl;
                 img.src = gifUrl;
                 img.alt = skillName;
                 img.alt = skillName;
                 img.style.maxHeight = gifSize + 'px';
                 // CSS handles sizing via .gif-image-container img rules
               
                // Store reference for validation
                const targetGif = gifUrl;
                  
                  
                 img.onload = () => {
                 img.onload = () => {
                     imgContainer.innerHTML = '';
                     // Only update if this is still the current target GIF
                    imgContainer.appendChild(img);
                    if (currentGif === targetGif) {
                    imgContainer.classList.remove('loading');
                        imgContainer.innerHTML = '';
                    console.log('Dynamic GIF Displayer: GIF loaded successfully');
                        imgContainer.appendChild(img);
                        imgContainer.classList.remove('loading');
                        console.log('Dynamic GIF Displayer: GIF loaded successfully');
                    } else {
                        console.log('Dynamic GIF Displayer: Discarded outdated load for', skillName);
                    }
                 };
                 };
                  
                  
                 img.onerror = () => {
                 img.onerror = () => {
                     imgContainer.innerHTML = '<div class="gif-error">GIF not found: ' + gifUrl + '</div>';
                     // Only update if this is still the current target GIF
                    imgContainer.classList.remove('loading');
                    if (currentGif === targetGif) {
                    console.error('Dynamic GIF Displayer: Failed to load GIF', gifUrl);
                        imgContainer.innerHTML = '';
                        imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename));
                        imgContainer.classList.remove('loading');
                        console.error('Dynamic GIF Displayer: Failed to load GIF', gifUrl);
                    }
                 };
                 };
             }
             }
           
            if (caption) {
                caption.textContent = skillName;
                caption.style.display = 'block';
            }
           
            currentGif = gifUrl;
         }
         }
     }
     }
     
      function scheduleCollapse(container) {
          // Keep the most recent GIF displayed instead of clearing it
          // GIF will only change when a different skill node is hovered
          clearTimeout(collapseTimer);
          console.log('Dynamic GIF Displayer: Keeping current GIF displayed');
      }
     
      window.toggleGifDisplay = function(containerId) {
          console.log('Dynamic GIF Displayer: Toggle clicked for', containerId);
          const container = document.getElementById(containerId);
          if (!container) {
              console.error('Dynamic GIF Displayer: Container not found', containerId);
              return;
          }
         
          clearTimeout(collapseTimer);
         
          if (container.style.display === 'none') {
              container.style.display = 'block';
          } else {
              container.style.display = 'none';
          }
         
          // Only clear the GIF when manually toggling the display off
          if (container.style.display === 'none') {
              const displayArea = container.querySelector('.' + CONFIG.displayClass);
              if (displayArea) {
                  const imgContainer = displayArea.querySelector('.gif-image-container');
                  const caption = displayArea.querySelector('.gif-caption');
                 
                  if (imgContainer) {
                      imgContainer.innerHTML = '';
                      imgContainer.appendChild(safeCreatePlaceholder('Hover over a skill node to see its demonstration'));
                      imgContainer.style.height = '';
                      imgContainer.style.minHeight = '';
                  }
                 
                  if (caption) {
                      caption.textContent = '';
                      caption.style.display = 'none';
                  }
              }
             
              container.classList.remove('expanded');
              currentGif = null;
          }
      };
     
      if (document.readyState === 'loading') {
          document.addEventListener('DOMContentLoaded', init);
      } else {
          init();
      }
     
      window.addEventListener('load', init);
     
      if (typeof mw !== 'undefined' && mw.hook) {
          mw.hook('wikipage.content').add(init);
      }
     
      const observer = new MutationObserver(function(mutations) {
          let shouldReinit = false;
          mutations.forEach(function(mutation) {
              if (mutation.addedNodes.length) {
                  mutation.addedNodes.forEach(function(node) {
                      if (node.nodeType === 1 && (
                          node.classList && node.classList.contains('advanced-tooltip') ||
                          node.querySelector && node.querySelector('.advanced-tooltip')
                      )) {
                          shouldReinit = true;
                      }
                  });
              }
          });
          if (shouldReinit) {
              console.log('Dynamic GIF Displayer: Content changed, reinitializing...');
              setTimeout(init, 100);
          }
      });
     
      observer.observe(document.body, {
          childList: true,
          subtree: true
      });
     
      console.log('Dynamic GIF Displayer: Script loaded');
})();
/* Make mp-links clickable anywhere in the box */
(function() {
    'use strict';
      
      
    /**
     function initMpLinksClick() {
    * Schedule container collapse
         const mpLinkItems = document.querySelectorAll('.mp-links > ul > li');
    */
     function scheduleCollapse(container) {
         clearTimeout(collapseTimer);
          
          
         collapseTimer = setTimeout(() => {
         mpLinkItems.forEach(item => {
             container.classList.remove('expanded');
             // Remove any existing click handler
            if (item._clickHandlerAttached) return;
              
              
             // Clear the GIF and caption when collapsing
             item.addEventListener('click', function(e) {
            const displayArea = container.querySelector('.' + CONFIG.displayClass);
                 // Find the first anchor tag in this li
            if (displayArea) {
                 const link = this.querySelector('a');
                 const imgContainer = displayArea.querySelector('.gif-image-container');
                 if (link && e.target !== link) {
                 const caption = displayArea.querySelector('.gif-caption');
                     // Trigger a click on the link
               
                     link.click();
                 if (imgContainer) {
                     imgContainer.innerHTML = '<div class="gif-placeholder">Hover over a skill node to see its demonstration</div>';
                    imgContainer.style.height = '';
                     imgContainer.style.minHeight = '';
                }
               
                if (caption) {
                    caption.textContent = '';
                    caption.style.display = 'none';
                 }
                 }
             }
             });
              
              
             currentGif = null;
             item._clickHandlerAttached = true;
            console.log('Dynamic GIF Displayer: Collapsed container and cleared content');
         });
         }, CONFIG.collapseDelay);
     }
     }
      
      
    /**
     // Initialize on page load
    * Manual toggle function for the collapse button
    */
    window.toggleGifDisplay = function(containerId) {
        const container = document.getElementById(containerId);
        if (!container) return;
       
        clearTimeout(collapseTimer);
        container.classList.toggle('expanded');
       
        if (!container.classList.contains('expanded')) {
            currentGif = null;
        }
    };
   
     // Initialize when DOM is ready
     if (document.readyState === 'loading') {
     if (document.readyState === 'loading') {
         document.addEventListener('DOMContentLoaded', init);
         document.addEventListener('DOMContentLoaded', initMpLinksClick);
     } else {
     } else {
         init();
         initMpLinksClick();
     }
     }
      
      
     // Also initialize on window load as fallback
     // Reinitialize when content changes
     window.addEventListener('load', init);
     window.addEventListener('load', initMpLinksClick);
      
      
    // Re-initialize when content changes (for tabber and dynamic content)
     if (typeof mw !== 'undefined' && mw.hook) {
     if (typeof mw !== 'undefined' && mw.hook) {
         mw.hook('wikipage.content').add(init);
         mw.hook('wikipage.content').add(initMpLinksClick);
     }
     }
      
      
     // MutationObserver for dynamic content
     // Watch for DOM changes
     const observer = new MutationObserver(function(mutations) {
     const observer = new MutationObserver(function(mutations) {
         let shouldReinit = false;
         let shouldReinit = false;
Line 373: Line 517:
                 mutation.addedNodes.forEach(function(node) {
                 mutation.addedNodes.forEach(function(node) {
                     if (node.nodeType === 1 && (
                     if (node.nodeType === 1 && (
                         node.classList && node.classList.contains('advanced-tooltip') ||
                         node.classList && node.classList.contains('mp-links') ||
                         node.querySelector && node.querySelector('.advanced-tooltip')
                         node.querySelector && node.querySelector('.mp-links')
                     )) {
                     )) {
                         shouldReinit = true;
                         shouldReinit = true;
Line 382: Line 526:
         });
         });
         if (shouldReinit) {
         if (shouldReinit) {
            console.log('Dynamic GIF Displayer: Content changed, reinitializing...');
             setTimeout(initMpLinksClick, 100);
             setTimeout(init, 100);
         }
         }
     });
     });
Line 391: Line 534:
         subtree: true
         subtree: true
     });
     });
   
})();
    console.log('Dynamic GIF Displayer: Script loaded');
 
(function() {
      'use strict';
     
      console.log('Tab GIF Display: Script loading...');
     
      // Configuration
      const CONFIG = {
          containerClass: 'tab-gif-container',
          displayClass: 'tab-gif-display-area',
          tabsClass: 'tab-gif-tabs',
          tabClass: 'tab-gif-tab',
          activeTabClass: 'tab-gif-active'
      };
     
      let currentContainers = new Map();
      let initAttempts = 0;
      const MAX_INIT_ATTEMPTS = 5;
     
      function safeCreateError(message) {
          const div = document.createElement('div');
          div.className = 'gif-error';
          div.textContent = message;
          return div;
      }
     
      function safeCreatePlaceholder(message) {
          const div = document.createElement('div');
          div.className = 'gif-placeholder';
          div.textContent = message;
          return div;
      }
     
      function init() {
          initAttempts++;
          console.log('Tab GIF Display: Initializing... (attempt ' + initAttempts + ')');
         
          const containers = document.querySelectorAll('.' + CONFIG.containerClass);
          console.log('Tab GIF Display: Found', containers.length, 'containers');
         
          if (containers.length === 0 && initAttempts < MAX_INIT_ATTEMPTS) {
              console.log('Tab GIF Display: No containers found yet, will retry...');
              setTimeout(init, 500);
              return;
          }
         
          let setupCount = 0;
          containers.forEach(container => {
              const containerId = container.id;
              console.log('Tab GIF Display: Checking container', containerId);
             
              if (!currentContainers.has(containerId)) {
                  console.log('Tab GIF Display: Setting up NEW container', containerId);
                  const success = setupContainer(container);
                  if (success) {
                      currentContainers.set(containerId, container);
                      setupCount++;
                  }
              } else {
                  console.log('Tab GIF Display: Container already initialized', containerId);
              }
          });
         
          console.log('Tab GIF Display: Successfully set up', setupCount, 'containers');
      }
     
      function setupContainer(container) {
          console.log('Tab GIF Display: setupContainer called for', container.id);
         
          const gifMap = buildGifMap(container);
          console.log('Tab GIF Display: GIF map built', gifMap);
         
          if (Object.keys(gifMap).length === 0) {
              console.error('Tab GIF Display: No GIFs found for container', container.id);
              const displayArea = container.querySelector('.' + CONFIG.displayClass);
              if (displayArea) {
                  const imgContainer = displayArea.querySelector('.gif-image-container');
                  if (imgContainer) {
                      imgContainer.innerHTML = '';
                      imgContainer.appendChild(safeCreateError('No GIFs configured. Check your list parameter.'));
                  }
              }
              return false;
          }
         
          createTabs(container, gifMap);
         
          // Display the first GIF by default
          const firstTabName = Object.keys(gifMap)[0];
          console.log('Tab GIF Display: Showing first tab:', firstTabName);
          showGif(container, gifMap[firstTabName], firstTabName);
         
          return true;
      }
     
      function buildGifMap(container) {
          const gifMap = {};
          const listData = container.getAttribute('data-gif-list');
         
          console.log('Tab GIF Display: Building map for container', container.id);
          console.log('Tab GIF Display: data-gif-list attribute:', listData);
         
          if (!listData) {
              console.error('Tab GIF Display: No data-gif-list attribute found on', container.id);
              return gifMap;
          }
         
          if (listData.trim() === '') {
              console.error('Tab GIF Display: data-gif-list is empty on', container.id);
              return gifMap;
          }
         
          const entries = listData.split(';').filter(e => e.trim());
          console.log('Tab GIF Display: Found', entries.length, 'entries in list');
         
          entries.forEach((entry, index) => {
              console.log('Tab GIF Display: Processing entry', index, ':', entry);
              const parts = entry.split(':');
             
              if (parts.length < 2) {
                  console.warn('Tab GIF Display: Invalid entry format (no colon):', entry);
                  return;
              }
             
              const tabName = parts[0].trim();
              const gifFile = parts.slice(1).join(':').trim();
             
              console.log('Tab GIF Display: Parsed - Tab:', tabName, 'File:', gifFile);
             
              if (!tabName) {
                  console.warn('Tab GIF Display: Empty tab name in entry:', entry);
                  return;
              }
             
              if (!gifFile) {
                  console.warn('Tab GIF Display: Empty gif file in entry:', entry);
                  return;
              }
             
              let gifUrl;
              if (typeof mw !== 'undefined' && mw.config) {
                  const scriptPath = mw.config.get('wgScriptPath') || '';
                  gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
              } else {
                  gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
              }
             
              gifMap[tabName] = gifUrl;
              console.log('Tab GIF Display: Mapped "' + tabName + '" to', gifUrl);
          });
         
          console.log('Tab GIF Display: Final map has', Object.keys(gifMap).length, 'entries');
          return gifMap;
      }
     
      function createTabs(container, gifMap) {
          const tabsContainer = container.querySelector('.' + CONFIG.tabsClass);
          if (!tabsContainer) {
              console.error('Tab GIF Display: No tabs container found in', container.id);
              return;
          }
         
          console.log('Tab GIF Display: Creating tabs in', container.id);
         
          tabsContainer.innerHTML = '';
         
          let isFirst = true;
          let tabCount = 0;
         
          Object.keys(gifMap).forEach(tabName => {
              const tab = document.createElement('button');
              tab.className = CONFIG.tabClass;
              tab.textContent = tabName;
              tab.setAttribute('data-tab-name', tabName);
              tab.type = 'button';
             
              if (isFirst) {
                  tab.classList.add(CONFIG.activeTabClass);
                  isFirst = false;
              }
             
              tab.addEventListener('click', () => {
                  console.log('Tab GIF Display: Tab clicked:', tabName);
                  handleTabClick(container, gifMap, tabName);
              });
             
              tabsContainer.appendChild(tab);
              tabCount++;
              console.log('Tab GIF Display: Created tab button for', tabName);
          });
         
          console.log('Tab GIF Display: Created', tabCount, 'tabs total');
      }
     
      function handleTabClick(container, gifMap, tabName) {
          console.log('Tab GIF Display: Handling tab click for', tabName);
         
          const tabs = container.querySelectorAll('.' + CONFIG.tabClass);
          tabs.forEach(tab => {
              if (tab.getAttribute('data-tab-name') === tabName) {
                  tab.classList.add(CONFIG.activeTabClass);
              } else {
                  tab.classList.remove(CONFIG.activeTabClass);
              }
          });
         
          const gifUrl = gifMap[tabName];
          showGif(container, gifUrl, tabName);
      }
     
      function showGif(container, gifUrl, tabName) {
          console.log('Tab GIF Display: Showing GIF for', tabName, '-', gifUrl);
         
          const displayArea = container.querySelector('.' + CONFIG.displayClass);
          if (!displayArea) {
              console.error('Tab GIF Display: No display area found');
              return;
          }
         
          const rawSize = container.getAttribute('data-gif-size') || '400';
          const gifSize = rawSize.replace(/[^0-9]/g, '') || '400';
         
          const imgContainer = displayArea.querySelector('.gif-image-container');
          const caption = displayArea.querySelector('.gif-caption');
         
          if (imgContainer) {
              const placeholder = imgContainer.querySelector('.gif-placeholder');
              if (placeholder) {
                  placeholder.remove();
              }
             
              imgContainer.style.height = gifSize + 'px';
              imgContainer.style.minHeight = gifSize + 'px';
             
              const gifFilename = gifUrl.split('/').pop();
              const decodedFilename = decodeURIComponent(gifFilename);
             
              if (decodedFilename.toLowerCase() === 'blank' || decodedFilename.toLowerCase() === 'blank.gif') {
                  imgContainer.innerHTML = '';
                  imgContainer.appendChild(safeCreatePlaceholder('No GIF available for this tab.'));
                  imgContainer.classList.remove('loading');
              } else {
                  const oldImg = imgContainer.querySelector('img');
                 
                  const img = document.createElement('img');
                  img.src = gifUrl;
                  img.alt = tabName;
                  img.style.maxHeight = gifSize + 'px';
                  img.style.opacity = '0';
                  img.style.position = 'absolute';
                  img.style.transition = 'opacity 0.2s ease';
                 
                  img.onload = () => {
                      if (oldImg) {
                          oldImg.style.transition = 'opacity 0.15s ease';
                          oldImg.style.opacity = '0';
                          setTimeout(() => {
                              imgContainer.innerHTML = '';
                              img.style.position = 'relative';
                              img.style.opacity = '1';
                              imgContainer.appendChild(img);
                              imgContainer.classList.remove('loading');
                          }, 150);
                      } else {
                          imgContainer.innerHTML = '';
                          img.style.position = 'relative';
                          img.style.opacity = '1';
                          imgContainer.appendChild(img);
                          imgContainer.classList.remove('loading');
                      }
                      console.log('Tab GIF Display: GIF loaded successfully');
                  };
                 
                  img.onerror = () => {
                      imgContainer.innerHTML = '';
                      imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename));
                      imgContainer.classList.remove('loading');
                      console.error('Tab GIF Display: Failed to load GIF', gifUrl);
                  };
                 
                  if (!oldImg) {
                      imgContainer.appendChild(img);
                      imgContainer.classList.add('loading');
                  }
              }
          }
         
          if (caption) {
              caption.textContent = tabName;
              caption.style.display = 'block';
          }
      }
     
      window.toggleTabGifDisplay = function(containerId) {
          console.log('Tab GIF Display: Toggle clicked for', containerId);
          const container = document.getElementById(containerId);
          if (!container) {
              console.error('Tab GIF Display: Container not found', containerId);
              return;
          }
         
          if (container.style.display === 'none') {
              container.style.display = 'block';
          } else {
              container.style.display = 'none';
          }
      };
     
      if (document.readyState === 'loading') {
          document.addEventListener('DOMContentLoaded', init);
      } else {
          init();
      }
     
      window.addEventListener('load', init);
     
      if (typeof mw !== 'undefined' && mw.hook) {
          mw.hook('wikipage.content').add(init);
      }
     
      const observer = new MutationObserver(function(mutations) {
          let shouldReinit = false;
          mutations.forEach(function(mutation) {
              if (mutation.addedNodes.length) {
                  mutation.addedNodes.forEach(function(node) {
                      if (node.nodeType === 1 && (
                          node.classList && node.classList.contains(CONFIG.containerClass) ||
                          node.querySelector && node.querySelector('.' + CONFIG.containerClass)
                      )) {
                          shouldReinit = true;
                      }
                  });
              }
          });
          if (shouldReinit) {
              console.log('Tab GIF Display: Content changed, reinitializing...');
              setTimeout(init, 100);
          }
      });
     
      observer.observe(document.body, {
          childList: true,
          subtree: true
      });
     
      console.log('Tab GIF Display: Script loaded and ready');
  })();
 
(function () {
    const images = [
        'url(/wiki/Special:Redirect/file/Site-background-image.jpg)',
        'url(/wiki/Special:Redirect/file/Site-background-image2.jpg)',
        'url(/wiki/Special:Redirect/file/Site-background-image3.jpg)',
        'url(/wiki/Special:Redirect/file/Site-background-image4.jpg)',
    ];
    function seededRandom(seed) {
        let x = Math.sin(seed * 9999) * 10000;
        return x - Math.floor(x);
    }
    const day = new Date().getDate();
    const pick1 = Math.floor(seededRandom(day) * images.length);
    let pick2 = Math.floor(seededRandom(day + 1) * images.length);
    if (pick2 === pick1) {
        pick2 = (pick1 + 1) % images.length;
    }
    document.body.style.backgroundImage = images[pick1];
    const footer = document.querySelector('.mw-footer');
    if (footer) {
        footer.style.backgroundImage = images[pick2];
    }
})();
})();

Latest revision as of 11:19, 31 December 2025

/* Any JavaScript here will be loaded for all users on every page load. */

/* DRUID */
$(function () {
    $(".druid-main-images-label").off("click");
    $(".druid-main-images-label").click(function () {
      var $parent = $(this).closest(".druid-container");
      $parent.find(".druid-toggleable").removeClass("focused");
      var i = $(this).attr("data-druid");
      $parent.find(".druid-toggleable[data-druid=" + i + "]").addClass("focused");
    });
  
    $(".druid-collapsible").off("click");
    $(".druid-collapsible").click(function () {
      var kind = $(this).attr("data-druid-section");
      $(this).toggleClass("druid-collapsible-collapsed");
      $(this)
        .closest(".druid-container")
        .find("[data-druid-section-row=" + kind + "]")
        .toggleClass("druid-collapsed");
    });

    $(".druid-container").on("click", ".worn-toggle-button", function (e) {
        e.preventDefault();
        e.stopPropagation();

        var $btn = $(this);
        var $container = $btn.closest('.has-worn-variant');
        var $base = $container.find('.image-base');
        var $worn = $container.find('.image-worn');

        if ($container.hasClass('is-worn-active')) {
            $container.removeClass('is-worn-active');
            $worn.hide();
            $base.show();
            $btn.text('Show Worn');
        } else {
            $container.addClass('is-worn-active');
            $base.hide();
            $worn.show();
            $btn.text('Show Normal');
        }
        
        console.log("Toggle state: " + ($container.hasClass('is-worn-active') ? "Worn" : "Normal"));
    });
  });
  /* End DRUID */
  
  /* Sound */
  (function () {
    let currentAudio = null;
    let currentBtn = null;

    $(document).on('click', '.sound-button', function () {
        const $btn = $(this);
        const soundUrl = $btn.data('sound-file');

        if (!soundUrl) return;

        if (currentAudio && currentBtn && currentBtn[0] === $btn[0]) {
            if (!currentAudio.paused) {
                currentAudio.pause();
                currentAudio.currentTime = 0;
                $btn.removeClass('playing');
                return; // Stop here
            }
        }

        if (currentAudio) {
            currentAudio.pause();
            currentAudio.currentTime = 0;
            $('.sound-button').removeClass('playing');
        }

        const audio = new Audio(soundUrl);
        currentAudio = audio;
        currentBtn = $btn;
        
        $btn.addClass('playing');
        audio.play().catch(function(error) {
            console.error("Playback failed:", error);
            $btn.removeClass('playing');
        });

        audio.onended = function() {
            $btn.removeClass('playing');
            currentAudio = null;
            currentBtn = null;
        };
    });
})();
  /* End Sound */
  
  /* [[Template:Spoiler]] */
  $(function () {
      $('.spoiler-content')
      .off('click') // in case this code is loaded twice
      .on('click', function(e){
          $(this).toggleClass('show');
      }).find('a').on('click', function(e){
          e.stopPropagation();
      });
  
  });
  /* End Template:Spoiler */

  /* Link to imported modules from Lua code */
  $(function() {
      var config = mw.config.get([
          'wgCanonicalNamespace',
          'wgFormattedNamespaces'
      ]);
      if (config.wgCanonicalNamespace !== 'Module') {
          return;
      }
      var localizedNamespace = config.wgFormattedNamespaces[828];
      $('.s1, .s2, .s').each(function() {
          var $this = $(this);
          var html = $this.html();
          var quote = html[0];
          var isLongStringQuote = quote === '[';
          var quoteRE = new RegExp('^\\' + quote + '|\\' + quote + '$', 'g');
          if (isLongStringQuote) {
              quoteRE = /^\[\[|\]\]$/g;
          }
          var name = html.replace(quoteRE, '');
          var isEnglishPrefix = name.startsWith('Module:');
          var isLocalizedPrefix = name.startsWith(localizedNamespace + ':');
          var isDevPrefix = name.startsWith('Dev:');
          if (isEnglishPrefix || isLocalizedPrefix || isDevPrefix) {
              var attrs = {
                  href: mw.util.getUrl(name)
              };
              if (isDevPrefix) {
                  attrs.href = 'https://commons.wiki.gg/wiki/Module:' + mw.util.wikiUrlencode(name.replace('Dev:', ''));
                  attrs.target = '_blank';
                  attrs.rel = 'noopener';
              }
              var link = mw.html.element('a', attrs, name);
              var str = quote + link + quote;
              if (isLongStringQuote) {
                  str = '[[' + link + ']]';
              }
              $this.html(str);
          }
      });
  });
  
  /* dynamic gif displayer ONLY TOUCH IF YOU KNOW WHAT YOU ARE DOING */
  
  (function() {
      'use strict';
      
      console.log('Dynamic GIF Displayer: Script loading...');
      
      // Configuration
      const CONFIG = {
          containerClass: 'dynamic-gif-container',
          displayClass: 'gif-display-area',
          autoExpand: true,
          expandDelay: 200,
          collapseDelay: 1000
      };
      
      let expandTimer = null;
      let collapseTimer = null;
      let currentGif = null;
      
      function safeCreateError(message) {
          const div = document.createElement('div');
          div.className = 'gif-error';
          div.textContent = message;
          return div;
      }
      
      function safeCreatePlaceholder(message) {
          const div = document.createElement('div');
          div.className = 'gif-placeholder';
          div.textContent = message;
          return div;
      }
      
      function init() {
          console.log('Dynamic GIF Displayer: Initializing...');
          
          const containers = document.querySelectorAll('.' + CONFIG.containerClass);
          console.log('Dynamic GIF Displayer: Found', containers.length, 'containers');
          
          containers.forEach(container => {
              console.log('Dynamic GIF Displayer: Setting up container', container.id);
              setupContainer(container);
              attachTooltipListeners(container);
          });
      }
      
      function setupContainer(container) {
          const displayArea = container.querySelector('.' + CONFIG.displayClass);
          if (!displayArea) {
              console.error('Dynamic GIF Displayer: No display area found');
              return;
          }
          
          displayArea.addEventListener('mouseenter', () => {
              clearTimeout(collapseTimer);
          });
          
          displayArea.addEventListener('mouseleave', () => {
              scheduleCollapse(container);
          });
      }
      
      function attachTooltipListeners(container) {
          console.log('Dynamic GIF Displayer: Attaching tooltip listeners...');
          
          const gifMap = buildGifMap(container);
          console.log('Dynamic GIF Displayer: GIF map', gifMap);
          
          const tooltips = document.querySelectorAll('.advanced-tooltip');
          console.log('Dynamic GIF Displayer: Found', tooltips.length, 'tooltips');
          
          tooltips.forEach((tooltip) => {
              const skillTitle = tooltip.querySelector('.skill-title');
              if (!skillTitle) return;
              
              const skillName = skillTitle.textContent.trim();
              const gifUrl = gifMap[skillName];
              
              console.log('Dynamic GIF Displayer: Processing tooltip:', skillName, 'GIF:', gifUrl);
              
              if (gifUrl) {
                  // Remove old listeners if they exist
                  if (tooltip._gifHoverHandler) {
                      tooltip.removeEventListener('mouseenter', tooltip._gifHoverHandler);
                  }
                  if (tooltip._gifLeaveHandler) {
                      tooltip.removeEventListener('mouseleave', tooltip._gifLeaveHandler);
                  }
                  
                  tooltip._gifHoverHandler = () => handleTooltipHover(container, gifUrl, skillName);
                  tooltip._gifLeaveHandler = () => scheduleCollapse(container);
                  
                  tooltip.addEventListener('mouseenter', tooltip._gifHoverHandler);
                  tooltip.addEventListener('mouseleave', tooltip._gifLeaveHandler);
                  
                  console.log('Dynamic GIF Displayer: Attached listeners to', skillName);
              }
          });
      }
      
      function buildGifMap(container) {
          const gifMap = {};
          const listData = container.getAttribute('data-gif-list');
          
          if (!listData) {
              console.error('Dynamic GIF Displayer: No data-gif-list attribute found');
              return gifMap;
          }
          
          console.log('Dynamic GIF Displayer: Raw list data:', listData);
          
          const entries = listData.split(';').filter(e => e.trim());
          console.log('Dynamic GIF Displayer: Found', entries.length, 'entries');
          
          entries.forEach(entry => {
              const parts = entry.split(':');
              if (parts.length >= 2) {
                  const skillName = parts[0].trim();
                  const gifFile = parts.slice(1).join(':').trim();
                  
                  if (skillName && gifFile) {
                      let gifUrl;
                      if (typeof mw !== 'undefined' && mw.config) {
                          const scriptPath = mw.config.get('wgScriptPath') || '';
                          gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
                      } else {
                          gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
                      }
                      
                      gifMap[skillName] = gifUrl;
                      console.log('Dynamic GIF Displayer: Mapped "' + skillName + '" to', gifUrl);
                  }
              }
          });
          
          return gifMap;
      }
      
    function handleTooltipHover(container, gifUrl, skillName) {
        console.log('Dynamic GIF Displayer: Hovering', skillName);
        clearTimeout(collapseTimer);
        clearTimeout(expandTimer);
        
        // Show GIF after delay (allows checking if different from current)
        expandTimer = setTimeout(() => {
            showGif(container, gifUrl, skillName);
        }, CONFIG.expandDelay);
    }
  
    function showGif(container, gifUrl, skillName) {
        console.log('Dynamic GIF Displayer: Showing GIF for', skillName, gifUrl);
        
        const displayArea = container.querySelector('.' + CONFIG.displayClass);
        if (!displayArea) return;
        
        if (!container.classList.contains('expanded')) {
            container.classList.add('expanded');
            console.log('Dynamic GIF Displayer: Expanded container');
        }
        
        const imgContainer = displayArea.querySelector('.gif-image-container');
        const caption = displayArea.querySelector('.gif-caption');
        
        // Update caption immediately
        if (caption) {
            caption.textContent = skillName;
            caption.style.display = 'block';
        }
        
        // Only reload if different GIF
        if (currentGif === gifUrl) {
            console.log('Dynamic GIF Displayer: Already showing', skillName);
            return;
        }
        
        // Set currentGif IMMEDIATELY to prevent race conditions
        currentGif = gifUrl;
        
        if (imgContainer) {
            const placeholder = imgContainer.querySelector('.gif-placeholder');
            if (placeholder) {
                placeholder.remove();
            }
            
            // Don't set dynamic height - let CSS handle fixed sizing
            
            const gifFilename = gifUrl.split('/').pop();
            const decodedFilename = decodeURIComponent(gifFilename);
            
            if (decodedFilename.toLowerCase() === 'blank' || decodedFilename.toLowerCase() === 'blank.gif') {
                imgContainer.innerHTML = '';
                imgContainer.appendChild(safeCreatePlaceholder('This node does not require a GIF due to its simplicity.'));
                imgContainer.classList.remove('loading');
            } else {
                // Cancel any existing image loads
                const existingImg = imgContainer.querySelector('img');
                if (existingImg) {
                    existingImg.onload = null;
                    existingImg.onerror = null;
                }
                
                imgContainer.classList.add('loading');
                
                const img = document.createElement('img');
                img.src = gifUrl;
                img.alt = skillName;
                // CSS handles sizing via .gif-image-container img rules
                
                // Store reference for validation
                const targetGif = gifUrl;
                
                img.onload = () => {
                    // Only update if this is still the current target GIF
                    if (currentGif === targetGif) {
                        imgContainer.innerHTML = '';
                        imgContainer.appendChild(img);
                        imgContainer.classList.remove('loading');
                        console.log('Dynamic GIF Displayer: GIF loaded successfully');
                    } else {
                        console.log('Dynamic GIF Displayer: Discarded outdated load for', skillName);
                    }
                };
                
                img.onerror = () => {
                    // Only update if this is still the current target GIF
                    if (currentGif === targetGif) {
                        imgContainer.innerHTML = '';
                        imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename));
                        imgContainer.classList.remove('loading');
                        console.error('Dynamic GIF Displayer: Failed to load GIF', gifUrl);
                    }
                };
            }
        }
    }
      
      function scheduleCollapse(container) {
          // Keep the most recent GIF displayed instead of clearing it
          // GIF will only change when a different skill node is hovered
          clearTimeout(collapseTimer);
          console.log('Dynamic GIF Displayer: Keeping current GIF displayed');
      }
      
      window.toggleGifDisplay = function(containerId) {
          console.log('Dynamic GIF Displayer: Toggle clicked for', containerId);
          const container = document.getElementById(containerId);
          if (!container) {
              console.error('Dynamic GIF Displayer: Container not found', containerId);
              return;
          }
          
          clearTimeout(collapseTimer);
          
          if (container.style.display === 'none') {
              container.style.display = 'block';
          } else {
              container.style.display = 'none';
          }
          
          // Only clear the GIF when manually toggling the display off
          if (container.style.display === 'none') {
              const displayArea = container.querySelector('.' + CONFIG.displayClass);
              if (displayArea) {
                  const imgContainer = displayArea.querySelector('.gif-image-container');
                  const caption = displayArea.querySelector('.gif-caption');
                  
                  if (imgContainer) {
                      imgContainer.innerHTML = '';
                      imgContainer.appendChild(safeCreatePlaceholder('Hover over a skill node to see its demonstration'));
                      imgContainer.style.height = '';
                      imgContainer.style.minHeight = '';
                  }
                  
                  if (caption) {
                      caption.textContent = '';
                      caption.style.display = 'none';
                  }
              }
              
              container.classList.remove('expanded');
              currentGif = null;
          }
      };
      
      if (document.readyState === 'loading') {
          document.addEventListener('DOMContentLoaded', init);
      } else {
          init();
      }
      
      window.addEventListener('load', init);
      
      if (typeof mw !== 'undefined' && mw.hook) {
          mw.hook('wikipage.content').add(init);
      }
      
      const observer = new MutationObserver(function(mutations) {
          let shouldReinit = false;
          mutations.forEach(function(mutation) {
              if (mutation.addedNodes.length) {
                  mutation.addedNodes.forEach(function(node) {
                      if (node.nodeType === 1 && (
                          node.classList && node.classList.contains('advanced-tooltip') ||
                          node.querySelector && node.querySelector('.advanced-tooltip')
                      )) {
                          shouldReinit = true;
                      }
                  });
              }
          });
          if (shouldReinit) {
              console.log('Dynamic GIF Displayer: Content changed, reinitializing...');
              setTimeout(init, 100);
          }
      });
      
      observer.observe(document.body, {
          childList: true,
          subtree: true
      });
      
      console.log('Dynamic GIF Displayer: Script loaded');
})();

/* Make mp-links clickable anywhere in the box */
(function() {
    'use strict';
    
    function initMpLinksClick() {
        const mpLinkItems = document.querySelectorAll('.mp-links > ul > li');
        
        mpLinkItems.forEach(item => {
            // Remove any existing click handler
            if (item._clickHandlerAttached) return;
            
            item.addEventListener('click', function(e) {
                // Find the first anchor tag in this li
                const link = this.querySelector('a');
                if (link && e.target !== link) {
                    // Trigger a click on the link
                    link.click();
                }
            });
            
            item._clickHandlerAttached = true;
        });
    }
    
    // Initialize on page load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initMpLinksClick);
    } else {
        initMpLinksClick();
    }
    
    // Reinitialize when content changes
    window.addEventListener('load', initMpLinksClick);
    
    if (typeof mw !== 'undefined' && mw.hook) {
        mw.hook('wikipage.content').add(initMpLinksClick);
    }
    
    // Watch for DOM changes
    const observer = new MutationObserver(function(mutations) {
        let shouldReinit = false;
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach(function(node) {
                    if (node.nodeType === 1 && (
                        node.classList && node.classList.contains('mp-links') ||
                        node.querySelector && node.querySelector('.mp-links')
                    )) {
                        shouldReinit = true;
                    }
                });
            }
        });
        if (shouldReinit) {
            setTimeout(initMpLinksClick, 100);
        }
    });
    
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();

(function() {
      'use strict';
      
      console.log('Tab GIF Display: Script loading...');
      
      // Configuration
      const CONFIG = {
          containerClass: 'tab-gif-container',
          displayClass: 'tab-gif-display-area',
          tabsClass: 'tab-gif-tabs',
          tabClass: 'tab-gif-tab',
          activeTabClass: 'tab-gif-active'
      };
      
      let currentContainers = new Map();
      let initAttempts = 0;
      const MAX_INIT_ATTEMPTS = 5;
      
      function safeCreateError(message) {
          const div = document.createElement('div');
          div.className = 'gif-error';
          div.textContent = message;
          return div;
      }
      
      function safeCreatePlaceholder(message) {
          const div = document.createElement('div');
          div.className = 'gif-placeholder';
          div.textContent = message;
          return div;
      }
      
      function init() {
          initAttempts++;
          console.log('Tab GIF Display: Initializing... (attempt ' + initAttempts + ')');
          
          const containers = document.querySelectorAll('.' + CONFIG.containerClass);
          console.log('Tab GIF Display: Found', containers.length, 'containers');
          
          if (containers.length === 0 && initAttempts < MAX_INIT_ATTEMPTS) {
              console.log('Tab GIF Display: No containers found yet, will retry...');
              setTimeout(init, 500);
              return;
          }
          
          let setupCount = 0;
          containers.forEach(container => {
              const containerId = container.id;
              console.log('Tab GIF Display: Checking container', containerId);
              
              if (!currentContainers.has(containerId)) {
                  console.log('Tab GIF Display: Setting up NEW container', containerId);
                  const success = setupContainer(container);
                  if (success) {
                      currentContainers.set(containerId, container);
                      setupCount++;
                  }
              } else {
                  console.log('Tab GIF Display: Container already initialized', containerId);
              }
          });
          
          console.log('Tab GIF Display: Successfully set up', setupCount, 'containers');
      }
      
      function setupContainer(container) {
          console.log('Tab GIF Display: setupContainer called for', container.id);
          
          const gifMap = buildGifMap(container);
          console.log('Tab GIF Display: GIF map built', gifMap);
          
          if (Object.keys(gifMap).length === 0) {
              console.error('Tab GIF Display: No GIFs found for container', container.id);
              const displayArea = container.querySelector('.' + CONFIG.displayClass);
              if (displayArea) {
                  const imgContainer = displayArea.querySelector('.gif-image-container');
                  if (imgContainer) {
                      imgContainer.innerHTML = '';
                      imgContainer.appendChild(safeCreateError('No GIFs configured. Check your list parameter.'));
                  }
              }
              return false;
          }
          
          createTabs(container, gifMap);
          
          // Display the first GIF by default
          const firstTabName = Object.keys(gifMap)[0];
          console.log('Tab GIF Display: Showing first tab:', firstTabName);
          showGif(container, gifMap[firstTabName], firstTabName);
          
          return true;
      }
      
      function buildGifMap(container) {
          const gifMap = {};
          const listData = container.getAttribute('data-gif-list');
          
          console.log('Tab GIF Display: Building map for container', container.id);
          console.log('Tab GIF Display: data-gif-list attribute:', listData);
          
          if (!listData) {
              console.error('Tab GIF Display: No data-gif-list attribute found on', container.id);
              return gifMap;
          }
          
          if (listData.trim() === '') {
              console.error('Tab GIF Display: data-gif-list is empty on', container.id);
              return gifMap;
          }
          
          const entries = listData.split(';').filter(e => e.trim());
          console.log('Tab GIF Display: Found', entries.length, 'entries in list');
          
          entries.forEach((entry, index) => {
              console.log('Tab GIF Display: Processing entry', index, ':', entry);
              const parts = entry.split(':');
              
              if (parts.length < 2) {
                  console.warn('Tab GIF Display: Invalid entry format (no colon):', entry);
                  return;
              }
              
              const tabName = parts[0].trim();
              const gifFile = parts.slice(1).join(':').trim();
              
              console.log('Tab GIF Display: Parsed - Tab:', tabName, 'File:', gifFile);
              
              if (!tabName) {
                  console.warn('Tab GIF Display: Empty tab name in entry:', entry);
                  return;
              }
              
              if (!gifFile) {
                  console.warn('Tab GIF Display: Empty gif file in entry:', entry);
                  return;
              }
              
              let gifUrl;
              if (typeof mw !== 'undefined' && mw.config) {
                  const scriptPath = mw.config.get('wgScriptPath') || '';
                  gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
              } else {
                  gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
              }
              
              gifMap[tabName] = gifUrl;
              console.log('Tab GIF Display: Mapped "' + tabName + '" to', gifUrl);
          });
          
          console.log('Tab GIF Display: Final map has', Object.keys(gifMap).length, 'entries');
          return gifMap;
      }
      
      function createTabs(container, gifMap) {
          const tabsContainer = container.querySelector('.' + CONFIG.tabsClass);
          if (!tabsContainer) {
              console.error('Tab GIF Display: No tabs container found in', container.id);
              return;
          }
          
          console.log('Tab GIF Display: Creating tabs in', container.id);
          
          tabsContainer.innerHTML = '';
          
          let isFirst = true;
          let tabCount = 0;
          
          Object.keys(gifMap).forEach(tabName => {
              const tab = document.createElement('button');
              tab.className = CONFIG.tabClass;
              tab.textContent = tabName;
              tab.setAttribute('data-tab-name', tabName);
              tab.type = 'button';
              
              if (isFirst) {
                  tab.classList.add(CONFIG.activeTabClass);
                  isFirst = false;
              }
              
              tab.addEventListener('click', () => {
                  console.log('Tab GIF Display: Tab clicked:', tabName);
                  handleTabClick(container, gifMap, tabName);
              });
              
              tabsContainer.appendChild(tab);
              tabCount++;
              console.log('Tab GIF Display: Created tab button for', tabName);
          });
          
          console.log('Tab GIF Display: Created', tabCount, 'tabs total');
      }
      
      function handleTabClick(container, gifMap, tabName) {
          console.log('Tab GIF Display: Handling tab click for', tabName);
          
          const tabs = container.querySelectorAll('.' + CONFIG.tabClass);
          tabs.forEach(tab => {
              if (tab.getAttribute('data-tab-name') === tabName) {
                  tab.classList.add(CONFIG.activeTabClass);
              } else {
                  tab.classList.remove(CONFIG.activeTabClass);
              }
          });
          
          const gifUrl = gifMap[tabName];
          showGif(container, gifUrl, tabName);
      }
      
      function showGif(container, gifUrl, tabName) {
          console.log('Tab GIF Display: Showing GIF for', tabName, '-', gifUrl);
          
          const displayArea = container.querySelector('.' + CONFIG.displayClass);
          if (!displayArea) {
              console.error('Tab GIF Display: No display area found');
              return;
          }
          
          const rawSize = container.getAttribute('data-gif-size') || '400';
          const gifSize = rawSize.replace(/[^0-9]/g, '') || '400';
          
          const imgContainer = displayArea.querySelector('.gif-image-container');
          const caption = displayArea.querySelector('.gif-caption');
          
          if (imgContainer) {
              const placeholder = imgContainer.querySelector('.gif-placeholder');
              if (placeholder) {
                  placeholder.remove();
              }
              
              imgContainer.style.height = gifSize + 'px';
              imgContainer.style.minHeight = gifSize + 'px';
              
              const gifFilename = gifUrl.split('/').pop();
              const decodedFilename = decodeURIComponent(gifFilename);
              
              if (decodedFilename.toLowerCase() === 'blank' || decodedFilename.toLowerCase() === 'blank.gif') {
                  imgContainer.innerHTML = '';
                  imgContainer.appendChild(safeCreatePlaceholder('No GIF available for this tab.'));
                  imgContainer.classList.remove('loading');
              } else {
                  const oldImg = imgContainer.querySelector('img');
                  
                  const img = document.createElement('img');
                  img.src = gifUrl;
                  img.alt = tabName;
                  img.style.maxHeight = gifSize + 'px';
                  img.style.opacity = '0';
                  img.style.position = 'absolute';
                  img.style.transition = 'opacity 0.2s ease';
                  
                  img.onload = () => {
                      if (oldImg) {
                          oldImg.style.transition = 'opacity 0.15s ease';
                          oldImg.style.opacity = '0';
                          setTimeout(() => {
                              imgContainer.innerHTML = '';
                              img.style.position = 'relative';
                              img.style.opacity = '1';
                              imgContainer.appendChild(img);
                              imgContainer.classList.remove('loading');
                          }, 150);
                      } else {
                          imgContainer.innerHTML = '';
                          img.style.position = 'relative';
                          img.style.opacity = '1';
                          imgContainer.appendChild(img);
                          imgContainer.classList.remove('loading');
                      }
                      console.log('Tab GIF Display: GIF loaded successfully');
                  };
                  
                  img.onerror = () => {
                      imgContainer.innerHTML = '';
                      imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename));
                      imgContainer.classList.remove('loading');
                      console.error('Tab GIF Display: Failed to load GIF', gifUrl);
                  };
                  
                  if (!oldImg) {
                      imgContainer.appendChild(img);
                      imgContainer.classList.add('loading');
                  }
              }
          }
          
          if (caption) {
              caption.textContent = tabName;
              caption.style.display = 'block';
          }
      }
      
      window.toggleTabGifDisplay = function(containerId) {
          console.log('Tab GIF Display: Toggle clicked for', containerId);
          const container = document.getElementById(containerId);
          if (!container) {
              console.error('Tab GIF Display: Container not found', containerId);
              return;
          }
          
          if (container.style.display === 'none') {
              container.style.display = 'block';
          } else {
              container.style.display = 'none';
          }
      };
      
      if (document.readyState === 'loading') {
          document.addEventListener('DOMContentLoaded', init);
      } else {
          init();
      }
      
      window.addEventListener('load', init);
      
      if (typeof mw !== 'undefined' && mw.hook) {
          mw.hook('wikipage.content').add(init);
      }
      
      const observer = new MutationObserver(function(mutations) {
          let shouldReinit = false;
          mutations.forEach(function(mutation) {
              if (mutation.addedNodes.length) {
                  mutation.addedNodes.forEach(function(node) {
                      if (node.nodeType === 1 && (
                          node.classList && node.classList.contains(CONFIG.containerClass) ||
                          node.querySelector && node.querySelector('.' + CONFIG.containerClass)
                      )) {
                          shouldReinit = true;
                      }
                  });
              }
          });
          if (shouldReinit) {
              console.log('Tab GIF Display: Content changed, reinitializing...');
              setTimeout(init, 100);
          }
      });
      
      observer.observe(document.body, {
          childList: true,
          subtree: true
      });
      
      console.log('Tab GIF Display: Script loaded and ready');
  })();
  
(function () {
    const images = [
        'url(/wiki/Special:Redirect/file/Site-background-image.jpg)',
        'url(/wiki/Special:Redirect/file/Site-background-image2.jpg)',
        'url(/wiki/Special:Redirect/file/Site-background-image3.jpg)',
        'url(/wiki/Special:Redirect/file/Site-background-image4.jpg)',
    ];
    function seededRandom(seed) {
        let x = Math.sin(seed * 9999) * 10000;
        return x - Math.floor(x);
    }
    const day = new Date().getDate();
    const pick1 = Math.floor(seededRandom(day) * images.length);
    let pick2 = Math.floor(seededRandom(day + 1) * images.length);
    if (pick2 === pick1) {
        pick2 = (pick1 + 1) % images.length;
    }
    document.body.style.backgroundImage = images[pick1];
    const footer = document.querySelector('.mw-footer');
    if (footer) {
        footer.style.backgroundImage = images[pick2];
    }
})();