MediaWiki:Common.js

From CoraTO Wiki - Official Wiki
Revision as of 05:58, 11 August 2025 by Noorisei (talk | contribs)
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// MainPage2 interactions. Designed for MediaWiki site usage.
(function(){
  'use strict';

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        last = linkRe.lastIndex;
      }

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

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

  // Process wiki links when DOM is ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', processWikiLinks);
  } else {
    processWikiLinks();
  }
  // Mobile hamburger menu for sidebar
  function createHamburgerMenu() {
    // Only create if we're in MediaWiki Vector Legacy and on mobile
    if (!document.body.classList.contains('skin-vector-legacy')) return;
    
    var panel = document.getElementById('mw-panel');
    if (!panel) return;
    
    // Create hamburger button
    var hamburger = document.createElement('button');
    hamburger.className = 'mobile-hamburger-menu';
    hamburger.setAttribute('aria-label', 'Toggle navigation menu');
    hamburger.innerHTML = '<span></span><span></span><span></span>';
    
    // Insert hamburger button at the top of the page
    var content = document.getElementById('content') || document.querySelector('.mw-body');
    if (content) {
      content.parentNode.insertBefore(hamburger, content);
    }
    
    // Toggle sidebar function
    function toggleSidebar() {
      panel.classList.toggle('mobile-open');
      hamburger.classList.toggle('active');
      document.body.classList.toggle('sidebar-open');
    }
    
    // Click handler for hamburger
    hamburger.addEventListener('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      toggleSidebar();
    });
    
    // Close sidebar when clicking outside
    document.addEventListener('click', function(e) {
      if (panel.classList.contains('mobile-open') && 
          !panel.contains(e.target) && 
          !hamburger.contains(e.target)) {
        panel.classList.remove('mobile-open');
        hamburger.classList.remove('active');
        document.body.classList.remove('sidebar-open');
      }
    });
    
    // Close sidebar on escape key
    document.addEventListener('keydown', function(e) {
      if (e.key === 'Escape' && panel.classList.contains('mobile-open')) {
        panel.classList.remove('mobile-open');
        hamburger.classList.remove('active');
        document.body.classList.remove('sidebar-open');
      }
    });
  }
  
  // Initialize hamburger menu when DOM is ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', createHamburgerMenu);
  } else {
    createHamburgerMenu();
  }

})();