User:Udays108/common.js
Appearance
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.
/* ============================================================
MIT BioMicro Center — MediaWiki Custom Scripts
Skin: Vector-2022
Scope: User:USERNAME/common.js (personal preview only)
Promote to MediaWiki:Common.js for site-wide deployment
============================================================ */
mw.loader.using( [ 'mediawiki.util' ] ).then( function () {
'use strict';
/* ── Helpers ─────────────────────────────────────────────── */
function el( tag, props, children ) {
var node = document.createElement( tag );
Object.keys( props || {} ).forEach( function ( k ) {
if ( k === 'style' && typeof props[ k ] === 'object' ) {
Object.assign( node.style, props[ k ] );
} else if ( k === 'className' ) {
node.className = props[ k ];
} else {
node.setAttribute( k, props[ k ] );
}
} );
( children || [] ).forEach( function ( c ) {
if ( typeof c === 'string' ) {
node.appendChild( document.createTextNode( c ) );
} else {
node.appendChild( c );
}
} );
return node;
}
/* ── 1. Top Contact Bar ──────────────────────────────────── */
/* Injects a crimson bar above the header with email/phone/location */
( function injectTopbar() {
var header = document.querySelector( '.vector-header' );
if ( !header || document.querySelector( '.bmc-topbar' ) ) return;
var topbar = document.createElement( 'div' );
topbar.className = 'bmc-topbar';
topbar.innerHTML =
'<div class="bmc-topbar-inner">' +
'<a href="mailto:biomicro@mit.edu">biomicro@mit.edu</a>' +
'<span class="bmc-sep">|</span>' +
'617-715-4533' +
'<span class="bmc-sep">|</span>' +
'Building 68-322' +
'</div>';
header.parentNode.insertBefore( topbar, header );
/* Inject inline styles — common.css won't reach injected elements
before the DOM settles on first load */
mw.util.addCSS(
'.bmc-topbar{background:#a31f34;color:#fff;font-size:.75rem;' +
'padding:6px 0;text-align:right;font-family:\'Helvetica Neue\',Arial,sans-serif}' +
'.bmc-topbar-inner{max-width:1140px;margin:0 auto;padding:0 24px}' +
'.bmc-topbar a{color:#fff;opacity:.85;text-decoration:none}' +
'.bmc-topbar a:hover{opacity:1}' +
'.bmc-sep{margin:0 6px;opacity:.5}'
);
}() );
/* ── 2. Active Navigation Highlight ─────────────────────── */
/* Marks the sidebar nav link that matches the current wiki page */
( function markActiveNav() {
var pageName = mw.config.get( 'wgPageName' );
if ( !pageName ) return;
/* Normalise: spaces → underscores, lower-case for comparison */
var norm = pageName.replace( / /g, '_' ).toLowerCase();
var links = document.querySelectorAll(
'#p-navigation .mw-list-item a, ' +
'.vector-main-menu .mw-list-item a'
);
links.forEach( function ( link ) {
var href = ( link.getAttribute( 'href' ) || '' ).toLowerCase();
/* Match on the last path segment (/wiki/PageName) */
if ( href.indexOf( norm ) !== -1 ) {
var li = link.closest( 'li' );
if ( li ) li.classList.add( 'bmc-active' );
}
} );
}() );
/* ── 3. Scroll Offset Fix (sticky header) ────────────────── */
/* Vector-2022's sticky header is ~50–68px tall. Without this fix
clicking a TOC anchor scrolls the heading behind the header. */
( function fixScrollOffset() {
var OFFSET = 88; /* header + topbar combined */
document.addEventListener( 'click', function ( e ) {
var anchor = e.target.closest( 'a[href^="#"]' );
if ( !anchor ) return;
var targetId = anchor.getAttribute( 'href' ).slice( 1 );
if ( !targetId ) return;
var target = document.getElementById( targetId ) ||
document.querySelector( '[name="' + CSS.escape( targetId ) + '"]' );
if ( !target ) return;
e.preventDefault();
var top = target.getBoundingClientRect().top + window.pageYOffset - OFFSET;
window.scrollTo( { top: top, behavior: 'smooth' } );
/* Update URL hash without triggering browser jump */
if ( history.pushState ) {
history.pushState( null, null, '#' + targetId );
}
} );
}() );
/* ── 4. Tab Panel System ─────────────────────────────────── */
/* Activates the .tab-btn / .tab-panel system used on the
pricing page. Does nothing if no .tab-btn elements exist. */
( function initTabs() {
var tabBtns = document.querySelectorAll( '.tab-btn' );
if ( !tabBtns.length ) return;
var tabPanels = document.querySelectorAll( '.tab-panel' );
/* Activate first tab by default if none is active */
if ( !document.querySelector( '.tab-btn.active' ) ) {
tabBtns[ 0 ].classList.add( 'active' );
}
if ( !document.querySelector( '.tab-panel.active' ) ) {
tabPanels[ 0 ] && tabPanels[ 0 ].classList.add( 'active' );
}
tabBtns.forEach( function ( btn ) {
btn.addEventListener( 'click', function () {
var target = btn.dataset.tab;
tabBtns.forEach( function ( t ) { t.classList.remove( 'active' ); } );
tabPanels.forEach( function ( p ) { p.classList.remove( 'active' ); } );
btn.classList.add( 'active' );
var panel = document.getElementById( 'tab-' + target );
if ( panel ) panel.classList.add( 'active' );
/* Scroll to tab bar, accounting for sticky header */
var tabBarWrap = document.querySelector( '.tab-bar-wrap' );
if ( tabBarWrap ) {
var top = tabBarWrap.getBoundingClientRect().top + window.pageYOffset - 68;
window.scrollTo( { top: top, behavior: 'smooth' } );
}
} );
} );
}() );
/* ── 5. BMC Logo Mark ────────────────────────────────────── */
/* Injects the red square "BMC" mark next to the wiki wordmark
if the logo element is a plain text/image logo. */
( function injectLogoMark() {
var logoLink = document.querySelector( '.mw-logo' );
if ( !logoLink || document.querySelector( '.bmc-logo-mark' ) ) return;
var mark = document.createElement( 'div' );
mark.className = 'bmc-logo-mark';
mark.textContent = 'BMC';
mw.util.addCSS(
'.bmc-logo-mark{' +
'width:36px;height:36px;background:#a31f34;border-radius:4px;' +
'display:flex;align-items:center;justify-content:center;' +
'color:#fff;font-weight:700;font-size:.9rem;letter-spacing:-.5px;' +
'flex-shrink:0;font-family:\'Helvetica Neue\',Arial,sans-serif' +
'}' +
'.mw-logo{display:flex!important;align-items:center!important;gap:10px!important}'
);
/* Prepend mark before the existing logo image/text */
logoLink.insertBefore( mark, logoLink.firstChild );
}() );
/* ── 6. Wrap Long Tables for Mobile Scroll ───────────────── */
/* Wraps .wikitable in a scrollable div so wide tables don't
break the layout on small screens. */
( function wrapTables() {
document.querySelectorAll( '.mw-parser-output .wikitable' ).forEach( function ( table ) {
if ( table.closest( '.wikitable-wrapper' ) ) return;
var wrapper = document.createElement( 'div' );
wrapper.className = 'wikitable-wrapper';
table.parentNode.insertBefore( wrapper, table );
wrapper.appendChild( table );
} );
mw.util.addCSS(
'.wikitable-wrapper{overflow-x:auto;border-radius:8px;' +
'border:1px solid #e4e4e4;margin-bottom:26px}'
);
}() );
} );