Jump to content

MediaWiki:Common.js: Difference between revisions

From Once Human Guide
No edit summary
No edit summary
 
(39 intermediate revisions by the same user not shown)
Line 1: Line 1:
mw.loader.using('mediawiki.util').then(function () {
/**
* MediaWiki:Common.js
* Once Human Guide
*/
 
mw.loader.using(['mediawiki.util']).then(function () {
   $(function () {
   $(function () {


     const data = {
     /* ============================================================
    * TAB SYSTEM
    * Wires up .mw-tab-buttons groups so each .mw-tab-btn shows its
    * matching .mw-tab-content sibling.
    * ============================================================ */
    function initTabs() {
      document.querySelectorAll('.mw-tab-buttons').forEach(group => {
        const buttons = group.querySelectorAll('.mw-tab-btn');
        buttons.forEach(btn => {
          btn.addEventListener('click', function () {
            const tabId = this.getAttribute('data-tab');
 
            // Deactivate buttons in this group only
            buttons.forEach(b => b.classList.remove('active'));
            this.classList.add('active');
 
            // Hide only the direct-child tab panels
            const container = group.parentElement;
            container.querySelectorAll(':scope > .mw-tab-content').forEach(c => {
              c.classList.remove('active');
            });
 
            // Show the target panel and init any dropdowns inside it
            const target = container.querySelector('#' + tabId);
            if (target) {
              target.classList.add('active');
              initDropdowns(target);
            }
          });
        });
      });
 
      // Auto-click the first tab in each group on load
      document.querySelectorAll('.mw-tab-buttons').forEach(group => {
        const first = group.querySelector('.mw-tab-btn');
        if (first) first.click();
      });
    }
 
    /* ============================================================
    * JSON-DRIVEN DROPDOWN UI
    * Renders .mw-dropdown-ui[data-options] containers as a
    * label button, sorted list, and detail output panel.
    * Supports both the new "lines" array format and the old
    * semicolon-separated "content" string format.
    * ============================================================ */
    function initDropdowns(scope) {
      scope.querySelectorAll('.mw-dropdown-ui[data-options]').forEach(container => {
        if (container.dataset.rendered === 'true') return;
 
        let data;
        try {
          data = JSON.parse(container.dataset.options);
        } catch (e) {
          console.error('Bad dropdown JSON:', e);
          return;
        }
 
        container.innerHTML = '';
 
        const btn = document.createElement('div');
        btn.className = 'mod-dropdown-btn';
        btn.textContent = container.dataset.label || 'Select One';
 
        const list = document.createElement('div');
        list.className = 'mod-dropdown-list';
 
        const output = document.createElement('div');
        output.className = 'mw-dropdown-output';
        output.innerHTML = '<div class="mw-ui-placeholder">Select something to see details</div>';
 
        Object.values(data)
          .sort((a, b) => a.label.localeCompare(b.label))
          .forEach(item => {
            const option = document.createElement('div');
            option.className = 'mod-dropdown-item';
            option.textContent = item.label;
            option.onclick = () => {
              btn.textContent = item.label;
              list.style.display = 'none';
 
              let lines = [];
              if (item.lines && Array.isArray(item.lines)) {
                lines = item.lines;
              } else if (item.content) {
                lines = item.content.split(';').map(s => s.trim()).filter(Boolean);
              }
 
              output.innerHTML =
                `<div class="mw-ui-title">${item.label}</div>` +
                lines.map(line => `<div class="mw-ui-line">${line}</div>`).join('');
            };
            list.appendChild(option);
          });
 
        // Toggle list open/closed
        btn.onclick = (e) => {
          e.stopPropagation();
          list.style.display = list.style.display === 'block' ? 'none' : 'block';
        };
 
        // Close when clicking outside
        document.addEventListener('click', () => {
          list.style.display = 'none';
        });


      slot1: `<table class="wikitable">
        container.appendChild(btn);
      <tr><th>Trait</th><th>Effect</th></tr>
        container.appendChild(list);
      <tr><td>Cheer Up</td><td>Max Mood +30–50%; Power recovery -5%</td></tr>
        container.appendChild(output);
      <tr><td>Covert Energy</td><td>Max Power +10–35%</td></tr>
        container.dataset.rendered = 'true';
      <tr><td>Feeling Blue</td><td>Max Mood -5–10%</td></tr>
       });
       <tr><td>Growing Pains</td><td>Max Power +30–50%; Mood recovery -5%</td></tr>
    }
      <tr><td>Optimist</td><td>Max Mood +15–35%</td></tr>
      <tr><td>Power Rewind</td><td>Power recovery +5–10%</td></tr>
      <tr><td>Rise and Shine</td><td>Mood recovery +5–25%</td></tr>
      <tr><td>Stable Energy</td><td>Max Power +30</td></tr>
      <tr><td>Stable Vitality</td><td>Max Mood +30</td></tr>
      <tr><td>Upper Hand</td><td>Convert Mood → Power at 0</td></tr>
      <tr><td>Worn-out</td><td>Max Power -5–10%</td></tr>
      </table>`,


      slot1_dev: `<table class="wikitable">
    /* ============================================================
      <tr><th>Trait</th><th>Effect</th><th>Deviant</th></tr>
    * EXCLUSIVE COLLAPSIBLE DROPDOWNS (Loot Crate pages)
      <tr><td>Pumpkin Lantern</td><td>+10% Power</td><td>Buzzy Bee</td></tr>
    * When one item's image is opened, all others in the group
      <tr><td>Gold Lightning</td><td>+15% Power Recovery</td><td>Electric Eel</td></tr>
    * are forced to collapse so only one is visible at a time.
       <tr><td>Optimism – Good Fortune</td><td>+40% Mood</td><td>Extradimensional Cat</td></tr>
    * ============================================================ */
       <tr><td>Aquarius</td><td>+30% Mood Recovery</td><td>Fetch-A-Lot</td></tr>
    function initExclusiveCollapsibles() {
      <tr><td>Golden Toad</td><td>+20% Mood Recovery</td><td>Flame Essence</td></tr>
       const outerKey = 'lootcrate';
      <tr><td>Shadow Shroom</td><td>+40% Energy</td><td>Growshroom</td></tr>
       const outerCollapsibleId = 'mw-customcollapsible-' + outerKey;
      <tr><td>Prototype</td><td>+10% Power Recovery</td><td>H37</td></tr>
       let suppress = false;
      <tr><td>Evergreen</td><td>2% chance to bring Bait</td><td>Hydronaut Fish</td></tr>
      <tr><td>Feisty Vitality</td><td>+30% Mood Recovery</td><td>Nutcracker</td></tr>
      <tr><td>Witch's Beloved</td><td>+20% Mood Recovery</td><td>Paper Doll</td></tr>
       <tr><td>Childlike Whimsy</td><td>+30% Power Recovery</td><td>Rain Man</td></tr>
      <tr><td>Radiance</td><td>+40% Power</td><td>Tar Pudding</td></tr>
      <tr><td>Emerald</td><td>+10% Power Recovery</td><td>The Digby Boy</td></tr>
      </table>`,


       slot2: `<table class="wikitable">
       // Listen on document and check the click target. Use NATIVE click()
      <tr><th>Trait</th><th>Effect</th></tr>
       // to retrigger MediaWiki's handler — jQuery .trigger() doesn't always
       <tr><td>A World of Charm</td><td>10% give +20 Mood</td></tr>
       // reach handlers that MW attached with native addEventListener.
       <tr><td>Anti-Burnout</td><td>20% give +5 Power</td></tr>
       document.addEventListener('click', function (e) {
       <tr><td>Devoted Laborer</td><td>+20% duration</td></tr>
        if (suppress) return;
      <tr><td>Dream Wild</td><td>10% give +10 Power</td></tr>
      <tr><td>Hydrophilic</td><td>+30% recovery near water</td></tr>
      <tr><td>Lazy Bones</td><td>-5% speed</td></tr>
      <tr><td>Living Map</td><td>-5% interval</td></tr>
      <tr><td>OnePlus</td><td>Rare crystals chance</td></tr>
      <tr><td>Panovision</td><td>-10% speed</td></tr>
      <tr><td>Slacking Off</td><td>+5% consumption</td></tr>
      <tr><td>Stardust Affinity</td><td>20% no Mood cost</td></tr>
      <tr><td>Sweet Talk</td><td>20% give Mood</td></tr>
      <tr><td>Voluntary Overtime</td><td>10% no Mood cost</td></tr>
      <tr><td>Workaholic</td><td>+10% duration</td></tr>
      </table>`,


      slot2_dev: `<table class="wikitable">
        // Find an ancestor with a mw-customtoggle-* class
      <tr><th>Trait</th><th>Effect</th><th>Deviant</th></tr>
        let toggle = e.target;
      <tr><td>Lunar Oracle</td><td>+20% duration</td><td>Electric Eel</td></tr>
        while (toggle && toggle !== document) {
      <tr><td>Daydreaming</td><td>Restore Power</td><td>Extradimensional Cat</td></tr>
          if (toggle.className && /mw-customtoggle-(\S+)/.test(toggle.className)) break;
      <tr><td>Toxicologist – Green Touch</td><td>25% no Power cost</td><td>Growshroom</td></tr>
          toggle = toggle.parentElement;
      <tr><td>Admiral</td><td>3% no Power cost</td><td>Nutcracker</td></tr>
        }
      <tr><td>Diamond Duke</td><td>5% no cost</td><td>Nutcracker</td></tr>
        if (!toggle || toggle === document) return;
      <tr><td>Workaholic – Lights</td><td>+20% duration</td><td>Nutcracker</td></tr>
      <tr><td>Gold Speaker</td><td>Extra ores</td><td>The Digby Boy</td></tr>
      </table>`,


      slot3: `<table class="wikitable">
        // Must be inside the outer loot-crate dropdown
      <tr><th>Trait</th><th>Effect</th></tr>
        const outerCol = document.getElementById(outerCollapsibleId);
      <tr><td>Brute Force Rules</td><td>10% double output</td></tr>
        if (!outerCol || !outerCol.contains(toggle)) return;
      <tr><td>Clean and Hygienic</td><td>Sanity floor 20%</td></tr>
      <tr><td>Extra Load</td><td>+15% storage</td></tr>
      <tr><td>Feline Creatures</td><td>Extra item chance</td></tr>
      <tr><td>Moonlight Assault</td><td>+15% recovery at night</td></tr>
      <tr><td>Top Grunt</td><td>Mood → Energy conversion</td></tr>
      </table>`
    };


    $(document).on("change", "#traitDropdown", function () {
        const match = toggle.className.match(/mw-customtoggle-(\S+)/);
      const value = $(this).val();
        if (!match) return;
      $("#traitContent").html(data[value] || "<b>Provide Trait information here</b>");
        const clickedKey = match[1];
    });
        const pickedName = toggle.textContent.trim();


  });
        setTimeout(function () {
});
          suppress = true;
mw.loader.using('mediawiki.util').then(function () {
 
  $(function () {
          // Close any other items that are currently open
          const inners = outerCol.querySelectorAll('[class*="mw-customtoggle-"]');
          inners.forEach(function (t) {
            const m = t.className.match(/mw-customtoggle-(\S+)/);
            if (!m || m[1] === clickedKey) return;
            const img = document.getElementById('mw-customcollapsible-' + m[1]);
            if (img && !img.classList.contains('mw-collapsed')) {
              t.click();
            }
          });
 
          // Update outer dropdown label
          const outerToggle = document.querySelector('.mw-customtoggle-' + outerKey);
          if (outerToggle) {
            const labelSpan = outerToggle.querySelector('span');
            if (labelSpan) labelSpan.textContent = pickedName;
          }
 
          // Close the outer dropdown
          if (outerCol && !outerCol.classList.contains('mw-collapsed') && outerToggle) {
            outerToggle.click();
          }


    console.log("JS LOADED");
          suppress = false;
        }, 50);
      }, true);
    }


     $(document).on("change", "#traitDropdown", function () {
     /* ============================================================
       $("#traitContent").html("WORKING");
    * DISCORD TAB OVERRIDE
     });
    * Repurposes the page's "Discussion" tab as a Discord invite.
    * ============================================================ */
    function initDiscordTab() {
       const discussionTab = document.querySelector('#ca-talk a');
      if (discussionTab) {
        discussionTab.href = 'https://discord.com/invite/FZtkXeGeUA';
        discussionTab.target = '_blank';
        discussionTab.textContent = 'Discord';
      }
     }


    /* ============================================================
    * BOOT
    * ============================================================ */
    initTabs();
    initExclusiveCollapsibles();
    initDiscordTab();
   });
   });
});
});

Latest revision as of 13:38, 27 May 2026

/**
 * MediaWiki:Common.js
 * Once Human Guide
 */

mw.loader.using(['mediawiki.util']).then(function () {
  $(function () {

    /* ============================================================
     * TAB SYSTEM
     * Wires up .mw-tab-buttons groups so each .mw-tab-btn shows its
     * matching .mw-tab-content sibling.
     * ============================================================ */
    function initTabs() {
      document.querySelectorAll('.mw-tab-buttons').forEach(group => {
        const buttons = group.querySelectorAll('.mw-tab-btn');
        buttons.forEach(btn => {
          btn.addEventListener('click', function () {
            const tabId = this.getAttribute('data-tab');

            // Deactivate buttons in this group only
            buttons.forEach(b => b.classList.remove('active'));
            this.classList.add('active');

            // Hide only the direct-child tab panels
            const container = group.parentElement;
            container.querySelectorAll(':scope > .mw-tab-content').forEach(c => {
              c.classList.remove('active');
            });

            // Show the target panel and init any dropdowns inside it
            const target = container.querySelector('#' + tabId);
            if (target) {
              target.classList.add('active');
              initDropdowns(target);
            }
          });
        });
      });

      // Auto-click the first tab in each group on load
      document.querySelectorAll('.mw-tab-buttons').forEach(group => {
        const first = group.querySelector('.mw-tab-btn');
        if (first) first.click();
      });
    }

    /* ============================================================
     * JSON-DRIVEN DROPDOWN UI
     * Renders .mw-dropdown-ui[data-options] containers as a
     * label button, sorted list, and detail output panel.
     * Supports both the new "lines" array format and the old
     * semicolon-separated "content" string format.
     * ============================================================ */
    function initDropdowns(scope) {
      scope.querySelectorAll('.mw-dropdown-ui[data-options]').forEach(container => {
        if (container.dataset.rendered === 'true') return;

        let data;
        try {
          data = JSON.parse(container.dataset.options);
        } catch (e) {
          console.error('Bad dropdown JSON:', e);
          return;
        }

        container.innerHTML = '';

        const btn = document.createElement('div');
        btn.className = 'mod-dropdown-btn';
        btn.textContent = container.dataset.label || 'Select One';

        const list = document.createElement('div');
        list.className = 'mod-dropdown-list';

        const output = document.createElement('div');
        output.className = 'mw-dropdown-output';
        output.innerHTML = '<div class="mw-ui-placeholder">Select something to see details</div>';

        Object.values(data)
          .sort((a, b) => a.label.localeCompare(b.label))
          .forEach(item => {
            const option = document.createElement('div');
            option.className = 'mod-dropdown-item';
            option.textContent = item.label;
            option.onclick = () => {
              btn.textContent = item.label;
              list.style.display = 'none';

              let lines = [];
              if (item.lines && Array.isArray(item.lines)) {
                lines = item.lines;
              } else if (item.content) {
                lines = item.content.split(';').map(s => s.trim()).filter(Boolean);
              }

              output.innerHTML =
                `<div class="mw-ui-title">${item.label}</div>` +
                lines.map(line => `<div class="mw-ui-line">${line}</div>`).join('');
            };
            list.appendChild(option);
          });

        // Toggle list open/closed
        btn.onclick = (e) => {
          e.stopPropagation();
          list.style.display = list.style.display === 'block' ? 'none' : 'block';
        };

        // Close when clicking outside
        document.addEventListener('click', () => {
          list.style.display = 'none';
        });

        container.appendChild(btn);
        container.appendChild(list);
        container.appendChild(output);
        container.dataset.rendered = 'true';
      });
    }

    /* ============================================================
     * EXCLUSIVE COLLAPSIBLE DROPDOWNS (Loot Crate pages)
     * When one item's image is opened, all others in the group
     * are forced to collapse so only one is visible at a time.
     * ============================================================ */
    function initExclusiveCollapsibles() {
      const outerKey = 'lootcrate';
      const outerCollapsibleId = 'mw-customcollapsible-' + outerKey;
      let suppress = false;

      // Listen on document and check the click target. Use NATIVE click()
      // to retrigger MediaWiki's handler — jQuery .trigger() doesn't always
      // reach handlers that MW attached with native addEventListener.
      document.addEventListener('click', function (e) {
        if (suppress) return;

        // Find an ancestor with a mw-customtoggle-* class
        let toggle = e.target;
        while (toggle && toggle !== document) {
          if (toggle.className && /mw-customtoggle-(\S+)/.test(toggle.className)) break;
          toggle = toggle.parentElement;
        }
        if (!toggle || toggle === document) return;

        // Must be inside the outer loot-crate dropdown
        const outerCol = document.getElementById(outerCollapsibleId);
        if (!outerCol || !outerCol.contains(toggle)) return;

        const match = toggle.className.match(/mw-customtoggle-(\S+)/);
        if (!match) return;
        const clickedKey = match[1];
        const pickedName = toggle.textContent.trim();

        setTimeout(function () {
          suppress = true;

          // Close any other items that are currently open
          const inners = outerCol.querySelectorAll('[class*="mw-customtoggle-"]');
          inners.forEach(function (t) {
            const m = t.className.match(/mw-customtoggle-(\S+)/);
            if (!m || m[1] === clickedKey) return;
            const img = document.getElementById('mw-customcollapsible-' + m[1]);
            if (img && !img.classList.contains('mw-collapsed')) {
              t.click();
            }
          });

          // Update outer dropdown label
          const outerToggle = document.querySelector('.mw-customtoggle-' + outerKey);
          if (outerToggle) {
            const labelSpan = outerToggle.querySelector('span');
            if (labelSpan) labelSpan.textContent = pickedName;
          }

          // Close the outer dropdown
          if (outerCol && !outerCol.classList.contains('mw-collapsed') && outerToggle) {
            outerToggle.click();
          }

          suppress = false;
        }, 50);
      }, true);
    }

    /* ============================================================
     * DISCORD TAB OVERRIDE
     * Repurposes the page's "Discussion" tab as a Discord invite.
     * ============================================================ */
    function initDiscordTab() {
      const discussionTab = document.querySelector('#ca-talk a');
      if (discussionTab) {
        discussionTab.href = 'https://discord.com/invite/FZtkXeGeUA';
        discussionTab.target = '_blank';
        discussionTab.textContent = 'Discord';
      }
    }

    /* ============================================================
     * BOOT
     * ============================================================ */
    initTabs();
    initExclusiveCollapsibles();
    initDiscordTab();
  });
});