imported>Dawning
(by SublimeText.Mediawiker)
m sound maybe
 
(46 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-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');


  $(".druid-collapsible").off("click");
        if ($container.hasClass('is-worn-active')) {
  $(".druid-collapsible").click(function () {
            $container.removeClass('is-worn-active');
    var kind = $(this).attr("data-druid-section");
            $worn.hide();
    $(this).toggleClass("druid-collapsible-collapsed");
            $base.show();
    $(this)
            $btn.text('Show Worn');
      .closest(".druid-container")
        } else {
      .find("[data-druid-section-row=" + kind + "]")
            $container.addClass('is-worn-active');
      .toggleClass("druid-collapsed");
            $base.hide();
            $worn.show();
            $btn.text('Show Normal');
        }
       
        console.log("Toggle state: " + ($container.hasClass('is-worn-active') ? "Worn" : "Normal"));
    });
   });
   });
});
  /* End DRUID */
/* End DRUID */
 
 
  /* Sound */
/* [[Template:Spoiler]] */
  (function () {
$(function () {
    let currentAudio = null;
$('.spoiler-content')
    let currentBtn = null;
.off('click') // in case this code is loaded twice
.on('click', function(e){
$(this).toggleClass('show');
}).find('a').on('click', function(e){
e.stopPropagation();
});


});
    $(document).on('click', '.sound-button', function () {
/* End Template:Spoiler */
        const $btn = $(this);
        const soundUrl = $btn.data('sound-file');


        if (!soundUrl) return;


/* Link to imported modules from Lua code */
         if (currentAudio && currentBtn && currentBtn[0] === $btn[0]) {
$(function() {
             if (!currentAudio.paused) {
    var config = mw.config.get([
                 currentAudio.pause();
        'wgCanonicalNamespace',
                 currentAudio.currentTime = 0;
         'wgFormattedNamespaces'
                 $btn.removeClass('playing');
    ]);
                 return; // Stop here
    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 */
        if (currentAudio) {
            currentAudio.pause();
            currentAudio.currentTime = 0;
            $('.sound-button').removeClass('playing');
        }


(function() {
         const audio = new Audio(soundUrl);
    'use strict';
         currentAudio = audio;
   
         currentBtn = $btn;
    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 => {
         $btn.addClass('playing');
             console.log('Dynamic GIF Displayer: Setting up container', container.id);
        audio.play().catch(function(error) {
             setupContainer(container);
             console.error("Playback failed:", error);
            attachTooltipListeners(container);
             $btn.removeClass('playing');
         });
         });
     }
 
   
        audio.onended = function() {
    function setupContainer(container) {
            $btn.removeClass('playing');
        const displayArea = container.querySelector('.' + CONFIG.displayClass);
            currentAudio = null;
        if (!displayArea) {
            currentBtn = null;
            console.error('Dynamic GIF Displayer: No display area found');
        };
            return;
     });
        }
})();
       
  /* End Sound */
        displayArea.addEventListener('mouseenter', () => {
 
            clearTimeout(collapseTimer);
  /* [[Template:Spoiler]] */
        });
  $(function () {
       
      $('.spoiler-content')
        displayArea.addEventListener('mouseleave', () => {
      .off('click') // in case this code is loaded twice
            scheduleCollapse(container);
      .on('click', function(e){
        });
          $(this).toggleClass('show');
    }
      }).find('a').on('click', function(e){
   
          e.stopPropagation();
    function attachTooltipListeners(container) {
      });
        console.log('Dynamic GIF Displayer: Attaching tooltip listeners...');
 
       
  });
        const gifMap = buildGifMap(container);
  /* End Template:Spoiler */
        console.log('Dynamic GIF Displayer: GIF map', gifMap);
 
       
  /* Link to imported modules from Lua code */
        const tooltips = document.querySelectorAll('.advanced-tooltip');
  $(function() {
        console.log('Dynamic GIF Displayer: Found', tooltips.length, 'tooltips');
      var config = mw.config.get([
       
          'wgCanonicalNamespace',
        tooltips.forEach((tooltip) => {
          'wgFormattedNamespaces'
            const skillTitle = tooltip.querySelector('.skill-title');
      ]);
            if (!skillTitle) return;
      if (config.wgCanonicalNamespace !== 'Module') {
           
          return;
            const skillName = skillTitle.textContent.trim();
      }
            const gifUrl = gifMap[skillName];
      var localizedNamespace = config.wgFormattedNamespaces[828];
           
      $('.s1, .s2, .s').each(function() {
            console.log('Dynamic GIF Displayer: Processing tooltip:', skillName, 'GIF:', gifUrl);
          var $this = $(this);
           
          var html = $this.html();
            if (gifUrl) {
          var quote = html[0];
                tooltip.removeEventListener('mouseenter', tooltip._gifHoverHandler);
          var isLongStringQuote = quote === '[';
                tooltip.removeEventListener('mouseleave', tooltip._gifLeaveHandler);
          var quoteRE = new RegExp('^\\' + quote + '|\\' + quote + '$', 'g');
               
          if (isLongStringQuote) {
                tooltip._gifHoverHandler = () => handleTooltipHover(container, gifUrl, skillName);
              quoteRE = /^\[\[|\]\]$/g;
                tooltip._gifLeaveHandler = () => scheduleCollapse(container);
          }
               
          var name = html.replace(quoteRE, '');
                tooltip.addEventListener('mouseenter', tooltip._gifHoverHandler);
          var isEnglishPrefix = name.startsWith('Module:');
                tooltip.addEventListener('mouseleave', tooltip._gifLeaveHandler);
          var isLocalizedPrefix = name.startsWith(localizedNamespace + ':');
               
          var isDevPrefix = name.startsWith('Dev:');
                console.log('Dynamic GIF Displayer: Attached listeners to', skillName);
          if (isEnglishPrefix || isLocalizedPrefix || isDevPrefix) {
            }
              var attrs = {
        });
                  href: mw.util.getUrl(name)
    }
              };
   
              if (isDevPrefix) {
function buildGifMap(container) {
                  attrs.href = 'https://commons.wiki.gg/wiki/Module:' + mw.util.wikiUrlencode(name.replace('Dev:', ''));
    const gifMap = {};
                  attrs.target = '_blank';
    const listData = container.getAttribute('data-gif-list');
                  attrs.rel = 'noopener';
   
              }
    if (!listData) {
              var link = mw.html.element('a', attrs, name);
        console.error('Dynamic GIF Displayer: No data-gif-list attribute found');
              var str = quote + link + quote;
        return gifMap;
              if (isLongStringQuote) {
    }
                  str = '[[' + link + ']]';
   
              }
    console.log('Dynamic GIF Displayer: Raw list data:', listData);
              $this.html(str);
   
          }
    const entries = listData.split(';').filter(e => e.trim());
      });
    console.log('Dynamic GIF Displayer: Found', entries.length, 'entries');
  });
   
 
    entries.forEach(entry => {
  /* dynamic gif displayer ONLY TOUCH IF YOU KNOW WHAT YOU ARE DOING */
        const parts = entry.split(':');
 
        if (parts.length >= 2) {
  (function() {
            const skillName = parts[0].trim();
      'use strict';
            const gifFile = parts.slice(1).join(':').trim();
     
           
      console.log('Dynamic GIF Displayer: Script loading...');
            if (skillName && gifFile) {
     
                let gifUrl; // FIXED: Declare the variable
      // Configuration
                if (typeof mw !== 'undefined' && mw.config) {
      const CONFIG = {
                    // FIXED: Removed the useless mw.util.getUrl line
          containerClass: 'dynamic-gif-container',
                    const scriptPath = mw.config.get('wgScriptPath') || '';
          displayClass: 'gif-display-area',
                    gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
          autoExpand: true,
                } else {
          expandDelay: 200,
                    gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
          collapseDelay: 1000
                }
      };
               
     
                gifMap[skillName] = gifUrl;
      let expandTimer = null;
                console.log('Dynamic GIF Displayer: Mapped "' + skillName + '" to', gifUrl);
      let collapseTimer = null;
            }
      let currentGif = null;
        }
     
    });
      function safeCreateError(message) {
   
          const div = document.createElement('div');
    return gifMap;
          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 219: 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);
     }
     }
 
 
     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 229: Line 302:
         const displayArea = container.querySelector('.' + CONFIG.displayClass);
         const displayArea = container.querySelector('.' + CONFIG.displayClass);
         if (!displayArea) return;
         if (!displayArea) return;
       
        const gifSize = container.getAttribute('data-gif-size') || '400';
          
          
         if (!container.classList.contains('expanded')) {
         if (!container.classList.contains('expanded')) {
Line 237: Line 308:
         }
         }
          
          
         if (currentGif !== gifUrl) {
         const imgContainer = displayArea.querySelector('.gif-image-container');
            const imgContainer = displayArea.querySelector('.gif-image-container');
        const caption = displayArea.querySelector('.gif-caption');
            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();
            }
              
              
             if (imgContainer) {
            // Don't set dynamic height - let CSS handle fixed sizing
                 const placeholder = imgContainer.querySelector('.gif-placeholder');
           
                 if (placeholder) {
            const gifFilename = gifUrl.split('/').pop();
                     placeholder.remove();
            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.style.height = gifSize + 'px';
                 imgContainer.classList.add('loading');
                imgContainer.style.minHeight = gifSize + 'px';
                  
                  
                 const gifFilename = gifUrl.split('/').pop();
                 const img = document.createElement('img');
                if (gifFilename.toLowerCase() === 'blank' || gifFilename.toLowerCase() === 'blank.gif') {
                img.src = gifUrl;
                    imgContainer.innerHTML = '';
                img.alt = skillName;
                    imgContainer.appendChild(safeCreatePlaceholder('This node does not require a GIF due to its simplicity.'));
                // CSS handles sizing via .gif-image-container img rules
                    imgContainer.classList.remove('loading');
               
                } else {
                // Store reference for validation
                    imgContainer.classList.add('loading');
                const targetGif = gifUrl;
                   
               
                    const img = document.createElement('img');
                img.onload = () => {
                    img.src = gifUrl;
                    // Only update if this is still the current target GIF
                    img.alt = skillName;
                    if (currentGif === targetGif) {
                    img.style.maxHeight = gifSize + 'px';
                   
                    img.onload = () => {
                         imgContainer.innerHTML = '';
                         imgContainer.innerHTML = '';
                         imgContainer.appendChild(img);
                         imgContainer.appendChild(img);
                         imgContainer.classList.remove('loading');
                         imgContainer.classList.remove('loading');
                         console.log('Dynamic GIF Displayer: GIF loaded successfully');
                         console.log('Dynamic GIF Displayer: GIF loaded successfully');
                     };
                     } else {
                      
                        console.log('Dynamic GIF Displayer: Discarded outdated load for', skillName);
                    img.onerror = () => {
                     }
                };
               
                img.onerror = () => {
                    // Only update if this is still the current target GIF
                    if (currentGif === targetGif) {
                         imgContainer.innerHTML = '';
                         imgContainer.innerHTML = '';
                         imgContainer.appendChild(safeCreateError('GIF not found: ' + gifUrl));
                         imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename));
                         imgContainer.classList.remove('loading');
                         imgContainer.classList.remove('loading');
                         console.error('Dynamic GIF Displayer: Failed to load GIF', gifUrl);
                         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 scheduleCollapse(container) {
     function initMpLinksClick() {
         clearTimeout(collapseTimer);
         const mpLinkItems = document.querySelectorAll('.mp-links > ul > li');
          
          
         collapseTimer = setTimeout(() => {
         mpLinkItems.forEach(item => {
             container.classList.remove('expanded');
             // Remove any existing click handler
            if (item._clickHandlerAttached) return;
              
              
             const displayArea = container.querySelector('.' + CONFIG.displayClass);
             item.addEventListener('click', function(e) {
            if (displayArea) {
                 // Find the first anchor tag in this li
                 const imgContainer = displayArea.querySelector('.gif-image-container');
                 const link = this.querySelector('a');
                 const caption = displayArea.querySelector('.gif-caption');
                 if (link && e.target !== link) {
               
                     // Trigger a click on the link
                 if (imgContainer) {
                     link.click();
                     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';
                 }
                 }
             }
             });
              
              
             currentGif = null;
             item._clickHandlerAttached = true;
            console.log('Dynamic GIF Displayer: Collapsed container and cleared content');
         });
         }, CONFIG.collapseDelay);
     }
     }
      
      
     window.toggleGifDisplay = function(containerId) {
     // Initialize on page load
        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';
        }
       
        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') {
     if (document.readyState === 'loading') {
         document.addEventListener('DOMContentLoaded', init);
         document.addEventListener('DOMContentLoaded', initMpLinksClick);
     } else {
     } else {
         init();
         initMpLinksClick();
     }
     }
      
      
     window.addEventListener('load', init);
    // Reinitialize when content changes
     window.addEventListener('load', initMpLinksClick);
      
      
     if (typeof mw !== 'undefined' && mw.hook) {
     if (typeof mw !== 'undefined' && mw.hook) {
         mw.hook('wikipage.content').add(init);
         mw.hook('wikipage.content').add(initMpLinksClick);
     }
     }
      
      
    // Watch for DOM changes
     const observer = new MutationObserver(function(mutations) {
     const observer = new MutationObserver(function(mutations) {
         let shouldReinit = false;
         let shouldReinit = false;
Line 375: 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 384: Line 526:
         });
         });
         if (shouldReinit) {
         if (shouldReinit) {
            console.log('Dynamic GIF Displayer: Content changed, reinitializing...');
             setTimeout(initMpLinksClick, 100);
             setTimeout(init, 100);
         }
         }
     });
     });
Line 393: Line 534:
         subtree: true
         subtree: true
     });
     });
   
    console.log('Dynamic GIF Displayer: Script loaded');
})();
})();


        class WikiLinkParser {
(function() {
            constructor(options = {}) {
      'use strict';
                this.options = {
     
                    baseUrl: options.baseUrl || '/wiki/',
      console.log('Tab GIF Display: Script loading...');
                    openInNewWindow: options.openInNewWindow !== false,
     
                    checkExistence: options.checkExistence !== false,
      // Configuration
                    slugify: options.slugify !== false,
      const CONFIG = {
                    existingPages: options.existingPages || ['HomePage', 'Documentation', 'Getting Started', 'API Reference', 'JavaScript', 'Regular Expressions']
          containerClass: 'tab-gif-container',
                };
          displayClass: 'tab-gif-display-area',
            }
          tabsClass: 'tab-gif-tabs',
 
          tabClass: 'tab-gif-tab',
            // Main parsing function
          activeTabClass: 'tab-gif-active'
            parse(text) {
      };
                // Pattern matches [[Link]] or [[Link|Display]]
     
                const linkPattern = /\[\[([^\[\]|]+)(?:\|([^\[\]]+))?\]\]/g;
      let currentContainers = new Map();
               
      let initAttempts = 0;
                let linkCount = 0;
      const MAX_INIT_ATTEMPTS = 5;
                const uniqueLinks = new Set();
     
               
      function safeCreateError(message) {
                const parsed = text.replace(linkPattern, (match, link, display) => {
          const div = document.createElement('div');
                    linkCount++;
          div.className = 'gif-error';
                    uniqueLinks.add(link.trim());
          div.textContent = message;
                    return this.createLink(link.trim(), display ? display.trim() : null);
          return div;
                });
      }
 
     
                return {
      function safeCreatePlaceholder(message) {
                    html: parsed,
          const div = document.createElement('div');
                    stats: {
          div.className = 'gif-placeholder';
                        linkCount,
          div.textContent = message;
                        uniqueLinks: uniqueLinks.size
          return div;
                    }
      }
                };
     
            }
      function init() {
 
          initAttempts++;
            // Create HTML link from wiki syntax
          console.log('Tab GIF Display: Initializing... (attempt ' + initAttempts + ')');
            createLink(pageName, displayText = null) {
         
                const text = displayText || pageName;
          const containers = document.querySelectorAll('.' + CONFIG.containerClass);
                const url = this.generateUrl(pageName);
          console.log('Tab GIF Display: Found', containers.length, 'containers');
                const exists = this.pageExists(pageName);
         
                const className = exists ? 'wiki-link' : 'wiki-link missing';
          if (containers.length === 0 && initAttempts < MAX_INIT_ATTEMPTS) {
                const target = this.options.openInNewWindow ? ' target="_blank"' : '';
              console.log('Tab GIF Display: No containers found yet, will retry...');
                const title = exists ? `Go to ${pageName}` : `${pageName} (page does not exist)`;
              setTimeout(init, 500);
               
              return;
                return `<a href="${url}" class="${className}" title="${title}"${target}>${text}</a>`;
          }
            }
         
 
          let setupCount = 0;
            // Generate URL from page name
          containers.forEach(container => {
            generateUrl(pageName) {
              const containerId = container.id;
                let url = pageName;
              console.log('Tab GIF Display: Checking container', containerId);
               
             
                // Handle special namespaces
              if (!currentContainers.has(containerId)) {
                if (pageName.includes(':')) {
                  console.log('Tab GIF Display: Setting up NEW container', containerId);
                    const [namespace, name] = pageName.split(':', 2);
                  const success = setupContainer(container);
                    url = `${namespace.toLowerCase()}/${name}`;
                  if (success) {
                }
                      currentContainers.set(containerId, container);
               
                      setupCount++;
                // Slugify if option is enabled
                  }
                if (this.options.slugify) {
              } else {
                    url = url
                  console.log('Tab GIF Display: Container already initialized', containerId);
                        .toLowerCase()
              }
                        .replace(/\s+/g, '-')
          });
                        .replace(/[^\w\-\/]/g, '');
         
                }
          console.log('Tab GIF Display: Successfully set up', setupCount, 'containers');
               
      }
                return this.options.baseUrl + url;
     
            }
      function setupContainer(container) {
 
          console.log('Tab GIF Display: setupContainer called for', container.id);
            // Check if page exists (mock implementation)
         
            pageExists(pageName) {
          const gifMap = buildGifMap(container);
                if (!this.options.checkExistence) return true;
          console.log('Tab GIF Display: GIF map built', gifMap);
               
         
                // Remove namespace for checking
          if (Object.keys(gifMap).length === 0) {
                const cleanName = pageName.includes(':')  
              console.error('Tab GIF Display: No GIFs found for container', container.id);
                    ? pageName.split(':', 2)[1]
              const displayArea = container.querySelector('.' + CONFIG.displayClass);
                    : pageName;
              if (displayArea) {
               
                  const imgContainer = displayArea.querySelector('.gif-image-container');
                return this.options.existingPages.includes(cleanName);
                  if (imgContainer) {
            }
                      imgContainer.innerHTML = '';
 
                      imgContainer.appendChild(safeCreateError('No GIFs configured. Check your list parameter.'));
            // Parse and convert to HTML with line breaks
                  }
            parseToHtml(text) {
              }
                const result = this.parse(text);
              return false;
                // Convert line breaks to <br> tags for display
          }
                result.html = result.html.replace(/\n/g, '<br>');
         
                return result;
          createTabs(container, gifMap);
            }
         
        }
          // Display the first GIF by default
 
          const firstTabName = Object.keys(gifMap)[0];
        // Initialize the demo
          console.log('Tab GIF Display: Showing first tab:', firstTabName);
        let parser = new WikiLinkParser();
          showGif(container, gifMap[firstTabName], firstTabName);
       
         
        function updatePreview() {
          return true;
            const input = document.getElementById('input').value;
      }
            const preview = document.getElementById('preview');
     
            const stats = document.getElementById('stats');
      function buildGifMap(container) {
           
          const gifMap = {};
            // Update parser options based on checkboxes
          const listData = container.getAttribute('data-gif-list');
            parser.options.openInNewWindow = document.getElementById('newWindow').checked;
         
            parser.options.checkExistence = document.getElementById('checkExistence').checked;
          console.log('Tab GIF Display: Building map for container', container.id);
            parser.options.slugify = document.getElementById('slugify').checked;
          console.log('Tab GIF Display: data-gif-list attribute:', listData);
           
         
            const result = parser.parseToHtml(input);
          if (!listData) {
            preview.innerHTML = result.html;
              console.error('Tab GIF Display: No data-gif-list attribute found on', container.id);
            stats.innerHTML = `Links found: ${result.stats.linkCount} | Unique pages: ${result.stats.uniqueLinks}`;
              return gifMap;
        }
          }
 
         
        // Set up event listeners
          if (listData.trim() === '') {
        document.getElementById('input').addEventListener('input', updatePreview);
              console.error('Tab GIF Display: data-gif-list is empty on', container.id);
        document.getElementById('newWindow').addEventListener('change', updatePreview);
              return gifMap;
        document.getElementById('checkExistence').addEventListener('change', updatePreview);
          }
        document.getElementById('slugify').addEventListener('change', updatePreview);
         
 
          const entries = listData.split(';').filter(e => e.trim());
        // Initial preview
          console.log('Tab GIF Display: Found', entries.length, 'entries in list');
        updatePreview();
         
 
          entries.forEach((entry, index) => {
        // Make parser available globally for console experimentation
              console.log('Tab GIF Display: Processing entry', index, ':', entry);
        window.wikiParser = parser;
              const parts = entry.split(':');
        console.log('WikiLinkParser is available as window.wikiParser for testing');
             
 
              if (parts.length < 2) {
$(document).ready(function() {
                  console.warn('Tab GIF Display: Invalid entry format (no colon):', entry);
    // Hide default footer
                  return;
    $('#p-lang, .minerva-footer-logo, #footer-info, #footer-places').hide();
              }
   
             
    // Add custom footer sections
              const tabName = parts[0].trim();
    addFooterSection('About', [
              const gifFile = parts.slice(1).join(':').trim();
        { url: mw.util.getUrl('Main_Page'), text: 'Home' },
             
        { url: mw.util.getUrl('Telos_Realms_Wiki:About'), text: 'About' },
              console.log('Tab GIF Display: Parsed - Tab:', tabName, 'File:', gifFile);
        { url: mw.util.getUrl('Help:Contents'), text: 'Help' }
             
    ]);
              if (!tabName) {
   
                  console.warn('Tab GIF Display: Empty tab name in entry:', entry);
    addFooterSection('Legal', [
                  return;
        { url: mw.util.getUrl('Telos_Realms_Wiki:Privacy'), text: 'Privacy Policy' },
              }
        { url: mw.util.getUrl('Telos_Realms_Wiki:Terms'), text: 'Terms of Service' }
             
    ]);
              if (!gifFile) {
   
                  console.warn('Tab GIF Display: Empty gif file in entry:', entry);
    addFooterSection('Community', [
                  return;
        { url: 'https://discord.gg/yourserver', text: 'Discord' },
              }
        { url: 'https://twitter.com/yourhandle', text: 'Twitter' }
             
    ]);
              let gifUrl;
});
              if (typeof mw !== 'undefined' && mw.config) {
 
                  const scriptPath = mw.config.get('wgScriptPath') || '';
function addFooterSection(title, links) {
                  gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
    var portletId = 'footer-' + title.toLowerCase();
              } else {
   
                  gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
    var section = $('<div>').attr('id', portletId).addClass('footer-section');
              }
    var heading = $('<h4>').text(title);
             
    section.append(heading);
              gifMap[tabName] = gifUrl;
   
              console.log('Tab GIF Display: Mapped "' + tabName + '" to', gifUrl);
    $('.post-content.footer-content').append(section);
          });
   
         
    links.forEach(function(link) {
          console.log('Tab GIF Display: Final map has', Object.keys(gifMap).length, 'entries');
        mw.util.addPortletLink(portletId, link.url, link.text);
          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];
    }
})();