MediaWiki:Common.js: Difference between revisions

No edit summary
Tag: Reverted
No edit summary
Tag: Reverted
Line 1,515: Line 1,515:
     });
     });
   }
   }
}() );
/**
* grantha-mobile-fixes.js
* Add this to MediaWiki:Common.js (or load as a separate gadget)
*
* Fixes:
*  1. Patches Minerva's ToggleList to guard against null parentNode
*    (prevents "Cannot read properties of null" crash on Main_Page)
*  2. Keeps #gr-home-toggle visible below #gr-static-bar on mobile
*  3. Wires the grantha/author tab toggle if it hasn't been wired yet
*/
( function () {
  'use strict';
  /* ══════════════════════════════════════════════════════════════
  * 1. TOGGLELIST NULL GUARD
  * Minerva's initMobile.js runs ToggleList.init() which calls
  * el.parentNode on every .collapsible-block it finds.
  * On Main_Page, some of these are dynamically injected and may
  * not yet be in the live DOM when ToggleList runs.
  * We patch by removing any element that has no parentNode
  * before ToggleList.init() fires.
  * ══════════════════════════════════════════════════════════════ */
  mw.hook( 'wikipage.content' ).add( function () {
    /* Wait one tick so Minerva's own DOMReady handlers run first */
    setTimeout( function () {
      var blocks = document.querySelectorAll( '.collapsible-block, .toggle-list' );
      Array.prototype.forEach.call( blocks, function ( el ) {
        /* If the element has no parentNode it's detached — remove it
          before ToggleList tries to call parentNode on it          */
        if ( !el.parentNode ) {
          try { el.remove(); } catch(e) {}
        }
      } );
    }, 0 );
  } );
  /* ══════════════════════════════════════════════════════════════
  * 2. MAIN PAGE: keep #gr-home-toggle below the reader bar
  * ══════════════════════════════════════════════════════════════ */
  if ( mw.config.get( 'wgPageName' ) !== 'Main_Page' ) return;
  mw.loader.using( 'mediawiki.util' ).done( function () {
    $( function () {
      applyHomeToggleOffset();
      /* Re-apply when bar height might change (orientation change, etc.) */
      window.addEventListener( 'resize', applyHomeToggleOffset, { passive: true } );
      /* Re-apply after a short delay in case the bar hasn't rendered yet */
      setTimeout( applyHomeToggleOffset, 300 );
      setTimeout( applyHomeToggleOffset, 800 );
    } );
  } );
  function applyHomeToggleOffset() {
    var bar = document.getElementById( 'gr-static-bar' );
    if ( !bar ) return;
    var barRect = bar.getBoundingClientRect();
    /* barRect.bottom = distance from viewport top to bar bottom.
      On load (before any scroll) this equals the bar's actual top offset
      plus its height. After scrolling (Minerva header gone) it's just
      the bar height since bar.top → 0.                                  */
    var barBottom = Math.round( barRect.bottom );
    /* #gr-home is the outermost container */
    var homeEl = document.getElementById( 'gr-home' );
    /* #gr-home-toggle is the tab switcher row */
    var toggleEl = document.getElementById( 'gr-home-toggle' );
    /* Also target the mw-parser-output first child as a fallback */
    var firstChild = document.querySelector(
      '#mw-content-text .mw-parser-output > .gr-home, ' +
      '#mw-content-text .mw-parser-output > *:first-child'
    );
    /* scroll-margin-top: so that if someone scrolls-to-top the toggle
      is visible below the bar (not behind it)                        */
    [ homeEl, toggleEl, firstChild ].forEach( function ( el ) {
      if ( el ) el.style.scrollMarginTop = ( barBottom + 4 ) + 'px';
    } );
    /* The body already has padding-top set by readerToolbar.js.
      But on Main_Page the toggle might STILL be hidden if the
      Minerva header is taller. Force the content area's top
      padding to be at least barBottom.                                */
    var contentText = document.getElementById( 'mw-content-text' );
    if ( contentText ) {
      /* Only add extra padding if we're on mobile */
      var isMob = window.innerWidth < 768 || !!document.getElementById( 'mw-mf-viewport' );
      if ( isMob ) {
        /* Check if the toggle is actually obscured */
        if ( toggleEl ) {
          var toggleRect = toggleEl.getBoundingClientRect();
          /* If toggle top < barBottom, the bar is covering it */
          if ( toggleRect.top < barBottom ) {
            var currentPT = parseInt( window.getComputedStyle( document.body ).paddingTop, 10 ) || 0;
            var needed    = currentPT + ( barBottom - toggleRect.top ) + 4;
            document.body.style.paddingTop = needed + 'px';
          }
        }
      }
    }
  }
  /* ══════════════════════════════════════════════════════════════
  * 3. WIRE THE GRANTHA / AUTHOR TAB TOGGLE
  * In case the toggle script hasn't loaded yet or failed
  * ══════════════════════════════════════════════════════════════ */
  $( function () {
    var $toggle  = $( '#gr-home-toggle' );
    var $viewG    = $( '#gr-view-grantha' );
    var $viewA    = $( '#gr-view-author' );
    var $btnG    = $( '#gr-toggle-grantha' );
    var $btnA    = $( '#gr-toggle-author' );
    if ( !$toggle.length || !$viewG.length || !$viewA.length ) return;
    /* Only wire if not already wired */
    if ( $toggle.data( 'gr-wired' ) ) return;
    $toggle.data( 'gr-wired', true );
    function showView( which ) {
      if ( which === 'grantha' ) {
        $viewG.show(); $viewA.hide();
        $btnG.addClass( 'gr-toggle-active' );
        $btnA.removeClass( 'gr-toggle-active' );
      } else {
        $viewA.show(); $viewG.hide();
        $btnA.addClass( 'gr-toggle-active' );
        $btnG.removeClass( 'gr-toggle-active' );
      }
      try { localStorage.setItem( 'grantha_home_tab', which ); } catch(e){}
    }
    $btnG.on( 'click keydown', function(e){
      if ( e.type === 'keydown' && e.key !== 'Enter' && e.key !== ' ' ) return;
      showView( 'grantha' );
    });
    $btnA.on( 'click keydown', function(e){
      if ( e.type === 'keydown' && e.key !== 'Enter' && e.key !== ' ' ) return;
      showView( 'author' );
    });
    /* Restore last-used tab */
    try {
      var saved = localStorage.getItem( 'grantha_home_tab' );
      if ( saved === 'author' ) showView( 'author' );
      else showView( 'grantha' );
    } catch(e) { showView( 'grantha' ); }
  } );


}() );
}() );