MediaWiki:Common.js: Difference between revisions
No edit summary Tag: Reverted |
No edit summary Tag: Reverted |
||
| Line 1,028: | Line 1,028: | ||
setTimeout( initMobileToc, 400 ); | setTimeout( initMobileToc, 400 ); | ||
} | } | ||
}() ); | }() );/* ═══════════════════════════════════════════════════════════════ | ||
/* | Mobile fixes — paste at bottom of MediaWiki:Common.js | ||
Injects a <style> tag AFTER readerToolbar's inline style | |||
so it wins the cascade without touching the extension. | |||
═══════════════════════════════════════════════════════════════ */ | |||
( function () { | ( function () { | ||
if ( window.innerWidth > 768 ) return; | if ( window.innerWidth > 768 ) return; | ||
function | /* ── 1. Inject mobile CSS after readerToolbar's <style> ─────── */ | ||
function injectMobileCSS() { | |||
if ( document.getElementById( 'gr-mobile-css' ) ) return; | if ( document.getElementById( 'gr-mobile-css' ) ) return; | ||
var s = document.createElement( 'style' ); | var s = document.createElement( 'style' ); | ||
s.id = 'gr-mobile-css'; | s.id = 'gr-mobile-css'; | ||
s.textContent = [ | s.textContent = [ | ||
'html{font-size:18px!important;}', | /* Font scale */ | ||
'html body #gr-static-bar{height:auto!important;min-height:52px!important;padding:6px 8px!important;flex-wrap:nowrap!important;overflow-x:auto!important;overflow-y:hidden!important;scrollbar-width:none!important;gap:4px!important;}', | 'html { font-size: 18px !important; }', | ||
'html body #gr-static-bar::-webkit-scrollbar{display:none!important;}', | |||
'html body .gr-controls .gr-btn{height:44px!important;min-width:44px!important;font-size:15px!important;padding:0 10px!important;flex-shrink:0!important;}', | /* Full-width layout — kill x-scroll everywhere */ | ||
'html body .gr-controls .gr-icon-btn{width:44px!important;padding:0!important;}', | 'html body, html body .mw-page-container, html body .vector-page-container,', | ||
'html body .gr-script-sel{height:40px!important;font-size:14px!important;min-width:90px!important;max-width:120px!important;}', | 'html body .mw-content-container, html body .vector-body-container,', | ||
'html body .gr-controls .gr-sep{display:none!important;}', | 'html body .vector-page-content, html body .mw-body,', | ||
'html body .gr-btn-staging{display:none!important;}', | 'html body .mw-body-content, html body #mw-content-text {', | ||
' | ' max-width: 100% !important; width: 100% !important;', | ||
' | ' overflow-x: hidden !important; box-sizing: border-box !important;', | ||
' | ' margin: 0 !important; padding: 0 !important; }', | ||
' | |||
' | /* Content padding */ | ||
' | 'html body #mw-content-text { padding: 8px 14px 48px !important; }', | ||
' | |||
' | /* Content font */ | ||
' | 'html body .mw-parser-output {', | ||
'html body #gra-toggle{bottom:80px!important;right:14px!important;width:54px!important;height:54px!important;}', | ' font-size: 18px !important; line-height: 1.8 !important;', | ||
].join( '' ); | ' max-width: 100% !important; overflow-x: hidden !important; }', | ||
/* Headings full width */ | |||
'html body .mw-parser-output h2, html body .mw-parser-output .mw-heading2 h2 {', | |||
' font-size: 1.1em !important; width: 100% !important; margin-top: 1.2em !important; }', | |||
'html body .mw-parser-output h3, html body .mw-parser-output .mw-heading3 h3 {', | |||
' font-size: 1em !important; width: 100% !important; }', | |||
/* Hide Vector sidebar TOC — replaced by overlay */ | |||
'html body .vector-column-start, html body #vector-toc,', | |||
'html body .vector-toc-landmark, html body .mw-table-of-contents-container,', | |||
'html body .vector-sticky-pinned-container,', | |||
'html body .vector-pinnable-element.vector-toc-pinnable-element {', | |||
' display: none !important; }', | |||
/* Home grid — single column, no x-scroll */ | |||
'html body .gr-home { overflow-x: hidden !important; max-width: 100% !important; }', | |||
'html body .gr-home-grid {', | |||
' flex-direction: column !important; flex-wrap: nowrap !important;', | |||
' gap: 14px !important; overflow-x: hidden !important; width: 100% !important; }', | |||
'html body .gr-home-card {', | |||
' flex: 0 0 auto !important; width: 100% !important;', | |||
' max-width: 100% !important; min-width: unset !important;', | |||
' box-sizing: border-box !important; }', | |||
/* Toolbar */ | |||
'html body #gr-static-bar {', | |||
' height: auto !important; min-height: 52px !important;', | |||
' padding: 6px 8px !important; flex-wrap: nowrap !important;', | |||
' overflow-x: auto !important; overflow-y: hidden !important;', | |||
' scrollbar-width: none !important; gap: 4px !important; }', | |||
'html body #gr-static-bar::-webkit-scrollbar { display: none !important; }', | |||
'html body .gr-controls .gr-btn {', | |||
' height: 44px !important; min-width: 44px !important;', | |||
' font-size: 15px !important; padding: 0 10px !important; flex-shrink: 0 !important; }', | |||
'html body .gr-controls .gr-icon-btn { width: 44px !important; padding: 0 !important; }', | |||
'html body .gr-script-sel {', | |||
' height: 40px !important; font-size: 14px !important;', | |||
' min-width: 90px !important; max-width: 120px !important; }', | |||
'html body .gr-controls .gr-sep { display: none !important; }', | |||
'html body .gr-btn-staging { display: none !important; }', | |||
/* TOC overlay styles */ | |||
'.gr-mob-toc-btn {', | |||
' display: flex !important; position: fixed !important;', | |||
' bottom: 148px !important; left: 16px !important; z-index: 9100 !important;', | |||
' background: #fff !important; border: 1.5px solid #b5451b !important;', | |||
' border-radius: 24px !important; padding: 10px 16px !important;', | |||
' font-size: 15px !important; font-family: system-ui, sans-serif !important;', | |||
' color: #b5451b !important; font-weight: 600 !important;', | |||
' box-shadow: 0 3px 14px rgba(0,0,0,0.15) !important;', | |||
' cursor: pointer !important; align-items: center !important;', | |||
' gap: 6px !important; white-space: nowrap !important; }', | |||
'.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; line-height: 1 !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;', | |||
" font-family: 'Adishila','Noto Serif Devanagari',system-ui,sans-serif !important; }", | |||
'.gr-mob-toc-body .vector-toc-level-2 a {', | |||
' padding-left: 16px !important; font-size: 15px !important; color: #555 !important; }', | |||
/* Annotation toggle button position */ | |||
'html body #gra-toggle {', | |||
' bottom: 80px !important; right: 14px !important;', | |||
' width: 54px !important; height: 54px !important; }', | |||
].join( '\n' ); | |||
document.head.appendChild( s ); | document.head.appendChild( s ); | ||
} | } | ||
/* | /* ── 2. Mobile TOC overlay ────────────────────────────────────── */ | ||
function initMobileToc() { | |||
if ( document.getElementById( 'gr-mob-toc-btn' ) ) return; | |||
var tocList = document.querySelector( | |||
'.vector-toc-contents, .vector-toc .vector-toc-list' | |||
); | |||
if ( !tocList ) { setTimeout( initMobileToc, 500 ); return; } | |||
/* Check if page actually has TOC items */ | |||
var hasItems = tocList.querySelector( '.vector-toc-list-item' ); | |||
/* Backdrop */ | |||
var bd = document.createElement( 'div' ); | |||
bd.className = 'gr-mob-toc-backdrop'; | |||
document.body.appendChild( bd ); | |||
/* Panel */ | |||
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 ); | |||
/* Toggle button — only show if page has TOC */ | |||
var btn = document.createElement( 'button' ); | |||
btn.className = 'gr-mob-toc-btn'; | |||
btn.id = 'gr-mob-toc-btn'; | |||
btn.innerHTML = '☰ Contents'; | |||
if ( !hasItems ) btn.style.display = 'none'; | |||
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.addEventListener( 'click', open ); | |||
cls.addEventListener( 'click', close ); | |||
bd.addEventListener( 'click', close ); | |||
body.querySelectorAll( 'a' ).forEach( function ( a ) { | |||
a.addEventListener( 'click', close ); | |||
} ); | |||
} | |||
/* ── 3. Mobile hamburger menu ─────────────────────────────────── */ | |||
function initMobileMenu() { | |||
if ( document.getElementById( 'gr-mob-menu-btn' ) ) return; | |||
var headerStart = document.querySelector( '.vector-header-start' ); | |||
if ( !headerStart ) return; | |||
/* Hamburger button — prepend to header start */ | |||
var btn = document.createElement( 'button' ); | |||
btn.id = 'gr-mob-menu-btn'; | |||
btn.className = 'gr-mob-menu-btn'; | |||
btn.setAttribute( 'aria-label', 'Open menu' ); | |||
btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>'; | |||
headerStart.insertBefore( btn, headerStart.firstChild ); | |||
/* Backdrop */ | |||
var bd = document.createElement( 'div' ); | |||
bd.className = 'gr-mob-menu-backdrop'; | |||
document.body.appendChild( bd ); | |||
/* Panel */ | |||
var panel = document.createElement( 'div' ); | |||
panel.className = 'gr-mob-menu-panel'; | |||
/* Header */ | |||
var hdr = document.createElement( 'div' ); | |||
hdr.className = 'gr-mob-menu-header'; | |||
var ttl = document.createElement( 'div' ); | |||
ttl.className = 'gr-mob-menu-header-title'; | |||
ttl.textContent = 'Anandamakaranda'; | |||
var cls = document.createElement( 'button' ); | |||
cls.className = 'gr-mob-menu-close'; | |||
cls.textContent = '✕'; | |||
hdr.appendChild( ttl ); hdr.appendChild( cls ); | |||
panel.appendChild( hdr ); | |||
/* Body — collect links from vector-header-end */ | |||
var body = document.createElement( 'div' ); | |||
body.className = 'gr-mob-menu-body'; | |||
/* Username / user page */ | |||
var userName = window.mw ? mw.config.get( 'wgUserName' ) : null; | |||
if ( userName ) { | |||
var userLink = document.createElement( 'a' ); | |||
userLink.className = 'gr-mob-menu-item gr-mob-menu-item-user'; | |||
userLink.href = '/User:' + encodeURIComponent( userName ); | |||
userLink.textContent = '👤 ' + userName; | |||
body.appendChild( userLink ); | |||
var div0 = document.createElement( 'div' ); | |||
div0.className = 'gr-mob-menu-divider'; | |||
body.appendChild( div0 ); | |||
} | |||
/* Collect links from header-end */ | |||
var headerEnd = document.querySelector( '.vector-header-end' ); | |||
if ( headerEnd ) { | |||
var links = headerEnd.querySelectorAll( 'a' ); | |||
links.forEach( function ( a ) { | |||
var item = document.createElement( 'a' ); | |||
item.className = 'gr-mob-menu-item'; | |||
item.href = a.href; | |||
item.textContent = a.textContent.trim(); | |||
if ( item.textContent ) body.appendChild( item ); | |||
} ); | |||
} | |||
/* Static links */ | |||
var statics = [ | |||
{ label: 'Help', href: '/Help:Contents' }, | |||
{ label: 'About', href: '/Anandamakaranda:About' }, | |||
]; | |||
var div1 = document.createElement( 'div' ); | |||
div1.className = 'gr-mob-menu-divider'; | |||
body.appendChild( div1 ); | |||
/* Login / logout */ | |||
var isLoggedIn = userName !== null; | |||
var authItem = document.createElement( 'a' ); | |||
authItem.className = 'gr-mob-menu-item'; | |||
if ( isLoggedIn ) { | |||
var logoutLink = document.querySelector( '#pt-logout a, a[href*="action=logout"]' ); | |||
authItem.href = logoutLink ? logoutLink.href : '/index.php?title=Special:UserLogout'; | |||
authItem.textContent = 'Log out'; | |||
} else { | |||
authItem.href = '/index.php?title=Special:UserLogin'; | |||
authItem.textContent = 'Log in'; | |||
} | |||
body.appendChild( authItem ); | |||
panel.appendChild( body ); | |||
document.body.appendChild( panel ); | |||
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.addEventListener( 'click', open ); | |||
cls.addEventListener( 'click', close ); | |||
bd.addEventListener( 'click', close ); | |||
body.querySelectorAll( 'a' ).forEach( function(a){ a.addEventListener( 'click', close ); } ); | |||
} | |||
/* ── Boot ─────────────────────────────────────────────────────── */ | |||
injectMobileCSS(); /* CSS first — synchronous */ | |||
initMobileMenu(); | |||
if ( document.readyState === 'loading' ) { | if ( document.readyState === 'loading' ) { | ||
document.addEventListener( 'DOMContentLoaded', | document.addEventListener( 'DOMContentLoaded', function () { | ||
setTimeout( initMobileToc, 600 ); | |||
} ); | |||
} else { | } else { | ||
setTimeout( initMobileToc, 600 ); | |||
} | } | ||
if ( window.mw ) { | |||
mw.hook( 'wikipage.content' ).add( function () { | |||
setTimeout( initMobileToc, 600 ); | |||
} ); | |||
} | |||
}() ); | }() ); | ||