Jump to content

MediaWiki:Common.js

From Transight Wiki
Revision as of 11:44, 27 October 2025 by Editor (talk | contribs)

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.

$(document).ready(function() {
  $('input#searchInput').attr('placeholder', 'Search for Transight Wiki');
});

// Open all external links in a new tab
$(document).ready(function () {
    $("a.external").attr("target", "_blank");
});

mw.loader.using('mediawiki.util', function () {
    // Force small text
    document.documentElement.classList.remove('vector-feature-custom-font-size-standard', 'vector-feature-custom-font-size-large');
    document.documentElement.classList.add('vector-feature-custom-font-size-small');

    // Force wide layout
    document.documentElement.classList.remove('vector-feature-limited-width');
    document.documentElement.classList.add('vector-feature-wide-width');
});



/* === Transight site-wide footer (robust body-append) === */
mw.loader.using(['mediawiki.util']).then(function () {

  function injectTsFooter() {
    if (document.getElementById('ts-footer')) return; // avoid duplicates

    var html = '\
<div id="ts-footer" class="ts-footer">\
  <div class="ts-footer__container">\
    <nav class="ts-footer__sitemap">\
      <ul class="ts-footer__col">\
        <li class="ts-footer__title">USE CASES</li>\
        <li><a href="https://transight.com/solutions/fleet-management/">Fleet Telematics</a></li>\
        <li><a href="https://transight.com/solutions/remote-management-system/">Remote Monitoring System</a></li>\
        <li><a href="https://transight.com/solutions/automation/">Automation</a></li>\
        <li><a href="https://transight.com/solutions/asset-tracking/">Asset Tracking</a></li>\
        <li><a href="https://transight.com/transforming-battery-management-for-ev/">Advanced EV Telematics</a></li>\
      </ul>\
      <ul class="ts-footer__col">\
        <li class="ts-footer__title">PRODUCTS</li>\
        <li><a href="https://telematics.transight.com/Trackers">Trackers</a></li>\
        <li><a href="https://telematics.transight.com/Accessories">Accessories</a></li>\
        <li><a href="https://transight.com/products/cloud-iot-platform/">Cloud IoT Solutions</a></li>\
      </ul>\
      <ul class="ts-footer__col">\
        <li class="ts-footer__title">SUPPORT</li>\
        <li><a href="https://transight.com/client-support/">Product Support</a></li>\
        <li><a href="/wiki/WIKI_KNOWLEDGE_BASE">Wiki Knowledge Base</a></li>\
        <li><a href="/wiki/RMA_Guidelines">Warranty & Repair</a></li>\
      </ul>\
      <ul class="ts-footer__col">\
        <li class="ts-footer__title">ABOUT US</li>\
        <li><a href="https://transight.com/about/">Mission, Vision</a></li>\
        <li><a href="/wiki/Career">Career</a></li>\
        <li><a href="https://transight.com/contact/">Contacts</a></li>\
      </ul>\
    </nav>\
    <div class="ts-footer__bottom">\
      <p class="ts-footer__legal">© 2024, Transight | <a href="/wiki/Privacy">Privacy</a> | <a href="/wiki/Cookies">Cookies</a> | <a href="/wiki/Policies">All Policies</a></p>\
      <p class="ts-footer__social"><a href="https://facebook.com">Facebook</a> <a href="https://www.linkedin.com/company/transight/">LinkedIn</a> <a href="https://x.com/transight_iot?s=21">X</a> <a href="https://youtube.com/@transightsystems">YouTube</a> <a href="https://www.instagram.com/transightsystems/">Instagram</a></p>\
    </div>\
  </div>\
</div>';

    // Always append at the very end of <body> so no page element can constrain width
    document.body.insertAdjacentHTML('beforeend', html);

    // Hide any built-in wiki footers
    document.querySelectorAll('footer.mw-footer, .mw-footer, #mw-footer, .mw-footer-container')
      .forEach(function (el) { el.style.display = 'none'; });
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', injectTsFooter);
  } else {
    injectTsFooter();
  }
});



/* === Inject floating buttons: "Go to our web" & "Go to Top" === */
mw.loader.using(['mediawiki.util']).then(function () {
    // Prevent duplicate injection
    if (!document.getElementById('ts-webfab')) {
        var webUrl = 'https://transight.com/';
        var htmlWeb = ''
            + '<div id="ts-webfab" class="ts-fab">'
            + '  <a class="ts-fab__btn" href="' + webUrl + '" target="_blank" rel="noopener" aria-label="Go to our website"></a>'
            + '  <span class="ts-fab__bubble">Go to our web</span>'
            + '</div>';
        document.body.insertAdjacentHTML('beforeend', htmlWeb);
    }

    if (!document.getElementById('ts-topfab')) {
        var htmlTop = ''
            + '<div id="ts-topfab" class="ts-fab">'
            + '  <a class="ts-fab__btn" href="javascript:void(0);" aria-label="Go to top"></a>'
            + '  <span class="ts-fab__bubble">Go to Top</span>'
            + '</div>';
        document.body.insertAdjacentHTML('beforeend', htmlTop);

        // Smooth scroll to top
        document.querySelector('#ts-topfab .ts-fab__btn').addEventListener('click', function() {
            window.scrollTo({ top: 0, behavior: 'smooth' });
        });
    }
});
/* === Transightee: Sticky header helper === */

( function () {
    if ( window.transighteeStickyHeaderInit ) return;
    window.transighteeStickyHeaderInit = true;

    function getTopFixedElementsHeight(excludeEl) {
        // find fixed-position elements at the very top (likely toolbars) and sum their heights
        var all = document.getElementsByTagName('*');
        var sum = 0;
        for (var i = 0; i < all.length; i++) {
            var el = all[i];
            if (el === excludeEl) continue;
            var cs = window.getComputedStyle(el);
            if (cs.position === 'fixed' && cs.display !== 'none' && el.offsetHeight > 0) {
                var rect = el.getBoundingClientRect();
                // consider elements whose top is near the top of viewport
                if (rect.top >= -2 && rect.top <= 10) {
                    sum += el.offsetHeight;
                }
            }
        }
        return sum;
    }

    function updateStickyHeader() {
        var header = document.querySelector('.vector-header-container');
        var pageContainer = document.querySelector('.mw-page-container');

        if (!header || !pageContainer) {
            // If header or container not present yet, keep trying later
            return;
        }

        // Ensure header has a stable background so it doesn't appear transparent after becoming fixed
        try {
            var comp = getComputedStyle(header);
            if (!header.style.background && comp.backgroundColor && comp.backgroundColor !== 'rgba(0, 0, 0, 0)') {
                header.style.backgroundColor = comp.backgroundColor;
            }
        } catch (e) {
            // ignore
        }

		 // Detect any other fixed elements at the top (toolbars) and position header below them
        var topOffset = getTopFixedElementsHeight(header);
        header.style.top = topOffset + 'px';

        // measure header height AFTER top is applied
        // force reflow to get accurate size
        var height = header.offsetHeight;

        // total vertical space header occupies (top offset + header height) = required padding
        var totalSpace = topOffset + height;
        document.documentElement.style.setProperty('--trans-header-height', totalSpace + 'px');

        // add body class to enable CSS that uses the variable
        document.body.classList.add('transightee-has-sticky-header');
    }

    // debounce helper for resize events
    var resizeTimer = null;
    function scheduleUpdate() {
        if (resizeTimer) clearTimeout(resizeTimer);
        resizeTimer = setTimeout(function () {
            updateStickyHeader();
            resizeTimer = null;
        }, 120);
    }

    document.addEventListener('DOMContentLoaded', function () {
        // initial attempt
        updateStickyHeader();
        // update when all resources loaded (images, fonts can change header height)
        window.addEventListener('load', updateStickyHeader);
        // keep updated on resize
        window.addEventListener('resize', scheduleUpdate);

        // Observe mutations inside header (e.g. buttons collapsing/expanding) and update padding
        var header = document.querySelector('.vector-header-container');
        if (header && window.MutationObserver) {
            var mo = new MutationObserver(function () {
                scheduleUpdate();
            });
            mo.observe(header, { attributes: true, childList: true, subtree: true, characterData: true });
            // keep reference to avoid GC in some environments
            window._transighteeStickyMO = mo;
        }
    });

    // Also try a periodic update a couple of times in case skin scripts alter header after load
    var tries = 0;
    var intId = setInterval(function () {
        updateStickyHeader();
        tries++;
        if (tries > 6) clearInterval(intId);
    }, 300);
}() );


/* Mobile Menu JS for MediaWiki (updated, matching your HTML keys)
   - Uses the exact element ids/classes present in your HTML:
     mw-head, mw-panel, p-views, p-cactions, p-namespaces
   - Avoids duplicate IDs (creates one #menu-toggle and a cloned button without an id)
*/

document.addEventListener('DOMContentLoaded', function () {
  // Helper: create toggle element (single id on the first one)
  function buildToggle(withId) {
    const wrapper = document.createElement('div');
    if (withId) wrapper.id = 'menu-toggle';
    wrapper.className = 'menu-toggle-button';
    wrapper.setAttribute('role', 'button');
    wrapper.setAttribute('aria-label', 'Toggle menu');

    const bar1 = document.createElement('div');
    bar1.className = 'bar1';
    const bar2 = document.createElement('div');
    bar2.className = 'bar2';
    const bar3 = document.createElement('div');
    bar3.className = 'bar3';

    wrapper.appendChild(bar1);
    wrapper.appendChild(bar2);
    wrapper.appendChild(bar3);

    return wrapper;
  }

  // Target nodes
  const head = document.getElementById('mw-head');
  const panel = document.getElementById('mw-panel');

  if (head) {
    // Create primary toggle with id
    const togglePrimary = buildToggle(true);
    head.appendChild(togglePrimary);
  }

  if (panel) {
    // Create a second toggle but avoid duplicate id (clone without id)
    const toggleClone = buildToggle(false);
    panel.appendChild(toggleClone);
  }

  // Toggle behavior - use event delegation on document to handle both toggle elements
  document.addEventListener('click', function (ev) {
    // Accept clicks on either element with id=menu-toggle or class menu-toggle-button
    let el = ev.target;
    // climb up to find the menu-toggle element if inner div was clicked
    while (el && el !== document) {
      if (el.id === 'menu-toggle' || el.classList.contains('menu-toggle-button')) {
        // toggle 'open-toggle' class on #mw-panel (if panel exists)
        if (panel) {
          panel.classList.toggle('open-toggle');
        }
        ev.preventDefault();
        return;
      }
      el = el.parentNode;
    }
  });

  // Close menu if window resized beyond breakpoint
  window.addEventListener('resize', function () {
    if (window.innerWidth > 800 && panel) {
      panel.classList.remove('open-toggle');
    }
  });

  /* -------------------------
     Copy links from #p-views and #p-namespaces into #p-cactions > ul[0]
     (preserves original behavior)
     ------------------------- */
  (function copyMenuLinks() {
    const pViews = document.getElementById('p-views');
    const pNamespaces = document.getElementById('p-namespaces');
    const pCactions = document.getElementById('p-cactions');

    // helper to append links into p-cactions ul[0]
    function appendLinks(sourceElem) {
      if (!sourceElem || !pCactions) return;
      const targetUl = pCactions.getElementsByTagName('ul')[0];
      if (!targetUl) return;

      const anchors = sourceElem.getElementsByTagName('a');
      for (let i = 0; i < anchors.length; i++) {
        const a = anchors[i];
        // create li > a
        const li = document.createElement('li');
        const newA = document.createElement('a');
        newA.textContent = (a.textContent || '').trim();
        newA.href = a.href;
        // preserve title if present
        if (a.title) newA.title = a.title;
        li.appendChild(newA);
        targetUl.appendChild(li);
      }
    }

    appendLinks(pViews);
    appendLinks(pNamespaces);
  })();

  /* -------------------------
     Build namespace short title link and append to the first span in p-namespaces
     Mirrors original logic: uses first <a> inside p-namespaces, trims last word
     ------------------------- */
  (function namespaceTitle() {
    const pNamespaces = document.getElementById('p-namespaces');
    if (!pNamespaces) return;

    const firstLink = pNamespaces.getElementsByTagName('a')[0];
    const span = pNamespaces.getElementsByTagName('span')[0];
    if (!firstLink || !span) return;

    let myString = (firstLink.textContent || '').trim();
    const lastSpace = myString.lastIndexOf(' ');
    if (lastSpace > 0) {
      myString = myString.substring(0, lastSpace);
    }
    const textToUse = myString.length > 0 ? myString : firstLink.textContent;

    const textnodeHref = document.createElement('a');
    textnodeHref.innerText = textToUse;
    textnodeHref.href = firstLink.href;
    textnodeHref.title = firstLink.title || '';
    span.appendChild(textnodeHref);
  })();

  /* -------------------------
     Show #p-cactions if there are any links in #p-views (original conditional)
     ------------------------- */
  (function revealCactionsIfViewsExist() {
    const pViewsAnchors = document.getElementById('p-views')?.getElementsByTagName('a') || [];
    const pCactions = document.getElementById('p-cactions');
    if (pViewsAnchors.length > 0 && pCactions) {
      pCactions.style.display = 'block';
    }
  })();

  // done — basic debug info (safe, will not throw)
  try {
    // eslint-disable-next-line no-console
    console.log('Transight mobile JS initialized.');
  } catch (e) { /* ignore */ }
});