|
Tags: Manual revert Reverted |
| Line 1,242: |
Line 1,242: |
| /* ── Expose storeQueryForLink for readerToolbar to call ── */ | | /* ── Expose storeQueryForLink for readerToolbar to call ── */ |
| window.grStoreSearchHL = storeQueryForLink; | | window.grStoreSearchHL = storeQueryForLink; |
|
| |
| }() );
| |
| /* ═══════════════════════════════════════════════════════════════
| |
| Mobile addon — paste at bottom of MediaWiki:Common.js
| |
| Only runs on Minerva (mobile) skin
| |
| ═══════════════════════════════════════════════════════════════ */
| |
| ( function () {
| |
| if ( !document.body.classList.contains( 'skin-minerva' ) ) return;
| |
|
| |
| /* ── 1. CSS injected into <head> — beats all other stylesheets ── */
| |
| function injectCSS() {
| |
| if ( document.getElementById( 'gr-mob-css' ) ) return;
| |
| var s = document.createElement( 'style' );
| |
| s.id = 'gr-mob-css';
| |
| s.textContent =
| |
| /* Body padding — readerToolbar sets this, we clear it */
| |
| 'body,#mw-mf-viewport,#mw-mf-page-center{padding-top:0!important;margin-top:0!important;}' +
| |
| 'html,body,#mw-mf-viewport,#mw-mf-page-center{overflow-x:hidden!important;max-width:100vw!important;}' +
| |
|
| |
| /* Header orange + sticky */
| |
| 'header.header-container{background:#b5451b!important;position:sticky!important;top:0!important;z-index:300!important;}' +
| |
| '.minerva-header{background:#b5451b!important;min-height:54px!important;}' +
| |
|
| |
| /* Header: keep only hamburger + branding, hide search */
| |
| '.minerva-header .search-toggle,.minerva-header .minerva-user-notifications{display:none!important;}' +
| |
|
| |
| /* Logo row: icon + title wrap cleanly */
| |
| '.branding-box a{display:flex!important;align-items:center!important;' +
| |
| 'text-decoration:none!important;max-width:calc(100vw - 80px)!important;}' +
| |
| '.branding-box a::before{content:"";display:block;width:30px;height:30px;flex-shrink:0;' +
| |
| 'background:url("/favicon.png") center/contain no-repeat;' +
| |
| 'margin-right:8px;}' +
| |
| '.branding-box a span{color:#fff!important;font-size:16px!important;font-weight:700!important;' +
| |
| 'font-family:system-ui,sans-serif!important;line-height:1.2!important;' +
| |
| 'flex:1 1 auto!important;min-width:0!important;}' +
| |
|
| |
| /* Header icons white */
| |
| '.minerva-header svg path,.minerva-header svg rect,.minerva-header svg circle{fill:#fff!important;}' +
| |
| '.minerva-header label{color:#fff!important;}' +
| |
|
| |
| /* Hide Page/Discussion tabs + icon toolbar */
| |
| '.minerva-tabs,.mw-portlet-associated-pages,.page-actions-menu,' +
| |
| '#page-secondary-actions,.last-modified-bar,.minerva-anon-talk-link{display:none!important;}' +
| |
|
| |
| /* Drawer: hide default items, show ours */
| |
| /* #mw-mf-page-left is kept — we replace its content in JS */ '' +
| |
| '#gr-mob-menu-items{display:block!important;}' +
| |
|
| |
| /* Drawer footer: hide About/Disclaimers */
| |
| '.mw-footer.minerva-footer,.footer-places,.footer-info,' +
| |
| '.minerva-footer-logo,#footer-places-about,' +
| |
| '#footer-places-disclaimers,#footer-places-privacy{display:none!important;}' +
| |
|
| |
| /* ReaderToolbar below header */
| |
| '#gr-static-bar{position:sticky!important;top:54px!important;z-index:200!important;}' +
| |
|
| |
| /* Sections expanded */
| |
| '.mf-section-0,.mf-section-1,.mf-section-2,.mf-section-3,.mf-section-4,' +
| |
| '.mf-section-5,.mf-section-6,.mf-section-7,.mf-section-8,.mf-section-9,' +
| |
| '.mf-section-10{display:block!important;visibility:visible!important;}' +
| |
| '.collapsible-block{display:block!important;}' +
| |
| '.section-heading .indicator,.collapsible-heading .indicator{display:none!important;}' +
| |
| '.section-heading,.collapsible-heading{pointer-events:none!important;}' +
| |
|
| |
| /* Home grid single column */
| |
| '.gr-home-grid{flex-direction:column!important;flex-wrap:nowrap!important;' +
| |
| 'gap:12px!important;width:100%!important;}' +
| |
| '.gr-home-card{width:100%!important;max-width:100%!important;' +
| |
| 'min-width:unset!important;box-sizing:border-box!important;flex:none!important;}' +
| |
| '.gr-home-toggle{flex-wrap:wrap!important;}' +
| |
|
| |
| /* Content */
| |
| '.mw-parser-output{font-size:18px!important;line-height:1.8!important;}' +
| |
| '.mw-parser-output h2,.mw-parser-output h3{width:100%!important;}' +
| |
| '.bhashyam-block{margin-left:8px!important;}' +
| |
| '#footer,.mw-footer,.catlinks,#catlinks{display:none!important;}' +
| |
|
| |
| /* TOC overlay */
| |
| '.gr-mob-toc-panel{position:fixed!important;top:0!important;left:0!important;' +
| |
| 'bottom:0!important;width:82vw!important;max-width:340px!important;' +
| |
| 'background:#fff!important;z-index:10400!important;' +
| |
| 'box-shadow:4px 0 28px rgba(0,0,0,0.22)!important;overflow-y:auto!important;' +
| |
| 'padding:0 0 40px!important;transform:translateX(-110%)!important;' +
| |
| 'transition:transform 0.26s cubic-bezier(0.4,0,0.2,1)!important;display:block!important;}' +
| |
| '.gr-mob-toc-panel.open{transform:translateX(0)!important;}' +
| |
| '.gr-mob-toc-backdrop{display:none!important;position:fixed!important;inset:0!important;' +
| |
| 'background:rgba(0,0,0,0.4)!important;z-index:10399!important;}' +
| |
| '.gr-mob-toc-backdrop.open{display:block!important;}' +
| |
| '.gr-mob-toc-header{position:sticky!important;top:0!important;background:#fff!important;' +
| |
| 'display:flex!important;align-items:center!important;justify-content:space-between!important;' +
| |
| 'padding:16px 16px 12px!important;border-bottom:1px solid #f0ebe6!important;z-index:1!important;}' +
| |
| '.gr-mob-toc-title{font-size:13px!important;font-weight:700!important;text-transform:uppercase!important;' +
| |
| 'letter-spacing:0.08em!important;color:#b5451b!important;font-family:system-ui,sans-serif!important;}' +
| |
| '.gr-mob-toc-close{background:none!important;border:none!important;' +
| |
| 'font-size:22px!important;color:#999!important;cursor:pointer!important;padding:4px 8px!important;}' +
| |
| '.gr-mob-toc-body{padding:12px 16px!important;}' +
| |
| '.gr-mob-toc-body a{display:block!important;font-size:16px!important;line-height:1.6!important;' +
| |
| 'color:#2c1810!important;text-decoration:none!important;padding:8px 0!important;' +
| |
| 'border-bottom:1px solid #f5f0ed!important;}';
| |
| document.head.appendChild( s );
| |
| }
| |
|
| |
| /* ── 2. Expand hidden sections ── */
| |
| function expandSections() {
| |
| document.querySelectorAll( '[class*="mf-section-"], .collapsible-block' )
| |
| .forEach( function ( el ) {
| |
| el.removeAttribute( 'hidden' );
| |
| el.style.setProperty( 'display', 'block', 'important' );
| |
| el.style.setProperty( 'visibility', 'visible', 'important' );
| |
| el.removeAttribute( 'aria-hidden' );
| |
| } );
| |
| document.querySelectorAll( '.section-heading, .collapsible-heading' )
| |
| .forEach( function ( el ) {
| |
| el.setAttribute( 'aria-expanded', 'true' );
| |
| el.style.setProperty( 'pointer-events', 'none', 'important' );
| |
| } );
| |
| }
| |
|
| |
| /* Persistent observer — fight MF re-collapsing */
| |
| function watchSections() {
| |
| var t = null;
| |
| var obs = new MutationObserver( function ( ms ) {
| |
| if ( ms.some( function(m){ return m.attributeName === 'hidden'; } ) ) {
| |
| clearTimeout(t); t = setTimeout( expandSections, 30 );
| |
| }
| |
| } );
| |
| var root = document.querySelector( '#mw-content-text' ) || document.body;
| |
| obs.observe( root, { subtree:true, attributes:true, attributeFilter:['hidden','aria-hidden'] } );
| |
| }
| |
|
| |
| /* ── 3. Body padding observer ── */
| |
| function watchBodyPadding() {
| |
| new MutationObserver( function () {
| |
| if ( document.body.style.paddingTop && document.body.style.paddingTop !== '0px' ) {
| |
| document.body.style.paddingTop = '';
| |
| }
| |
| } ).observe( document.body, { attributes:true, attributeFilter:['style'] } );
| |
| }
| |
|
| |
| /* ── 4. Inject custom links into drawer ── */
| |
| function injectMenuLinks() {
| |
| /* Already injected */
| |
| if ( document.getElementById( 'gr-mob-menu-items' ) ) return;
| |
|
| |
| /* The drawer container is .navigation-drawer (the <nav> element)
| |
| Our links go AFTER the #mw-mf-page-left div (which we hide via CSS) */
| |
| var navDrawer = document.querySelector( '.navigation-drawer' );
| |
| if ( !navDrawer ) return;
| |
|
| |
| var wrap = document.createElement( 'div' );
| |
| wrap.id = 'gr-mob-menu-items';
| |
| wrap.style.cssText = 'width:100%;background:#fff;margin-top:8px;';
| |
|
| |
| var itemStyle = 'display:flex;align-items:center;gap:14px;padding:15px 20px;' +
| |
| 'font-size:16px;color:#2c1810;text-decoration:none;' +
| |
| 'font-family:system-ui,sans-serif;border-bottom:1px solid #f0ebe6;background:#fff;';
| |
|
| |
| function makeItem( href, emoji, label ) {
| |
| var a = document.createElement( 'a' );
| |
| a.href = href;
| |
| a.style.cssText = itemStyle;
| |
| a.innerHTML =
| |
| '<span>' + label + '</span>';
| |
| return a;
| |
| }
| |
|
| |
| wrap.appendChild( makeItem( '/Main_Page', '', 'Home' ) );
| |
| wrap.appendChild( makeItem( '/My_wiki:Help', '', 'Help' ) );
| |
| wrap.appendChild( makeItem( '/My_wiki:About', '', 'About' ) );
| |
|
| |
| var userName = window.mw ? mw.config.get( 'wgUserName' ) : null;
| |
| if ( userName ) {
| |
| var la = document.querySelector( 'a[href*="action=logout"]' );
| |
| wrap.appendChild( makeItem(
| |
| la ? la.href : '/index.php?title=Special:UserLogout', '', 'Log out'
| |
| ) );
| |
| } else {
| |
| wrap.appendChild( makeItem( '/index.php?title=Special:UserLogin', '', 'Log in' ) );
| |
| }
| |
|
| |
| /* Replace contents of #mw-mf-page-left — it's inside the
| |
| toggle-list mechanism so it only shows when drawer is open */
| |
| var pageLeft = document.getElementById( 'mw-mf-page-left' );
| |
| if ( pageLeft ) {
| |
| /* Clear default items and insert ours */
| |
| while ( pageLeft.firstChild ) pageLeft.removeChild( pageLeft.firstChild );
| |
| pageLeft.style.removeProperty( 'display' ); /* un-hide it */
| |
| pageLeft.appendChild( wrap );
| |
| } else {
| |
| /* Fallback */
| |
| navDrawer.appendChild( wrap );
| |
| }
| |
|
| |
| /* Hide footer portlets */
| |
| document.querySelectorAll(
| |
| '.mw-footer.minerva-footer,.footer-places,.footer-info,' +
| |
| '.minerva-footer-logo,#footer-places-about,' +
| |
| '#footer-places-disclaimers,#footer-places-privacy'
| |
| ).forEach( function(el) { el.style.setProperty('display','none','important'); } );
| |
| }
| |
|
| |
| /* ── 5. Mobile TOC overlay ── */
| |
| var _tocDone = false;
| |
| function initToc() {
| |
| if ( _tocDone ) return;
| |
| var tocList = document.querySelector( '.vector-toc-contents, .vector-toc .vector-toc-list' );
| |
| if ( !tocList ) return;
| |
| if ( !tocList.querySelector( 'li' ) ) return;
| |
| _tocDone = true;
| |
|
| |
| var bd = document.createElement( 'div' );
| |
| bd.className = 'gr-mob-toc-backdrop';
| |
| document.body.appendChild( bd );
| |
|
| |
| var panel = document.createElement( 'div' );
| |
| panel.className = 'gr-mob-toc-panel';
| |
|
| |
| var hdr = document.createElement( 'div' );
| |
| hdr.className = 'gr-mob-toc-header';
| |
| var ttl = document.createElement( 'div' );
| |
| ttl.className = 'gr-mob-toc-title';
| |
| ttl.textContent = 'विषयसूची';
| |
| var cls = document.createElement( 'button' );
| |
| cls.className = 'gr-mob-toc-close';
| |
| cls.textContent = '✕';
| |
| hdr.appendChild( ttl ); hdr.appendChild( cls );
| |
| panel.appendChild( hdr );
| |
|
| |
| var body = document.createElement( 'div' );
| |
| body.className = 'gr-mob-toc-body';
| |
| body.appendChild( tocList.cloneNode( true ) );
| |
| panel.appendChild( body );
| |
| document.body.appendChild( panel );
| |
|
| |
| var btn = document.createElement( 'button' );
| |
| btn.id = 'gr-mob-toc-btn';
| |
| btn.innerHTML = '☰ Contents';
| |
| btn.style.cssText = 'position:fixed;bottom:148px;left:16px;z-index:9100;' +
| |
| 'background:#fff;border:1.5px solid #b5451b;border-radius:24px;' +
| |
| 'padding:10px 16px;font-size:15px;font-family:system-ui,sans-serif;' +
| |
| 'color:#b5451b;font-weight:600;box-shadow:0 3px 14px rgba(0,0,0,0.15);cursor:pointer;';
| |
| document.body.appendChild( btn );
| |
|
| |
| function open() { panel.classList.add('open'); bd.classList.add('open'); document.body.style.overflow='hidden'; }
| |
| function close() { panel.classList.remove('open'); bd.classList.remove('open'); document.body.style.overflow=''; }
| |
|
| |
| btn.onclick = open; cls.onclick = close; bd.onclick = close;
| |
| body.querySelectorAll('a').forEach(function(a){ a.onclick = close; });
| |
| }
| |
|
| |
| /* ── Boot ── */
| |
| injectCSS();
| |
| watchBodyPadding();
| |
|
| |
| function boot() {
| |
| expandSections();
| |
| watchSections();
| |
| injectMenuLinks();
| |
| [100, 400, 900, 1800].forEach(function(ms){ setTimeout(expandSections, ms); });
| |
| setTimeout(initToc, 700);
| |
| }
| |
|
| |
| if ( document.readyState === 'loading' ) {
| |
| document.addEventListener( 'DOMContentLoaded', boot );
| |
| } else {
| |
| boot();
| |
| }
| |
|
| |
| if ( window.mw ) {
| |
| mw.hook( 'wikipage.content' ).add(function() {
| |
| setTimeout(function(){ expandSections(); injectMenuLinks(); initToc(); }, 300);
| |
| });
| |
| }
| |
|
| |
| }() );
| |
| /**
| |
| * 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' ); }
| |
| } );
| |
|
| |
|
| }() ); | | }() ); |