|
|
| Line 1: |
Line 1: |
| /* ============================================================ | | /** |
| MIT BioMicro Center — MediaWiki Custom Scripts
| | * BioMicro Center Wiki — Modern UI JavaScript |
| Skin: Vector-2022 | Scope: User:USERNAME/common.js
| | * Paste into: User:USERNAME/common.js |
| ============================================================ */
| | * Promoted to: MediaWiki:Common.js (by sysop) |
| | */ |
|
| |
|
| mw.loader.using( [ 'mediawiki.util' ] ).then( function () {
| | ( function () { |
| 'use strict';
| | 'use strict'; |
|
| |
|
| var WIKI = 'https://bmcwiki.mit.edu';
| | /* ── 1. Sidebar Toggle ──────────────────────────────────────── */ |
| | function initSidebarToggle() { |
| | var STORAGE_KEY = 'bmc-sidebar-collapsed'; |
| | var body = document.body; |
|
| |
|
| /* Navigation — matches the mockup exactly.
| | // Create toggle button |
| Update hrefs if your wiki uses different page titles. */
| | var btn = document.createElement( 'button' ); |
| var NAV = [
| | btn.id = 'bmc-sidebar-toggle'; |
| {
| | btn.setAttribute( 'aria-label', 'Toggle sidebar' ); |
| label: 'About',
| | btn.setAttribute( 'title', 'Toggle sidebar' ); |
| href: WIKI + '/index.php/BioMicroCenter:About'
| | btn.innerHTML = '☰'; // ☰ hamburger |
| },
| | document.body.appendChild( btn ); |
| {
| |
| label: 'News',
| |
| href: WIKI + '/index.php/BioMicroCenter:News',
| |
| children: [
| |
| { label: 'Latest News', href: WIKI + '/index.php/BioMicroCenter:News' },
| |
| { label: 'Seminars', href: WIKI + '/index.php/BioMicroCenter:Seminars' },
| |
| { label: 'Classes & Training', href: 'https://igb.mit.edu/mini-courses', external: true }
| |
| ]
| |
| },
| |
| {
| |
| label: 'Services',
| |
| href: WIKI + '/index.php/BioMicroCenter:Assisted_Services',
| |
| children: [
| |
| { label: 'Walkup', href: WIKI + '/index.php/BioMicroCenter:Walk-up_Services' },
| |
| { label: 'Assisted', href: WIKI + '/index.php/BioMicroCenter:Assisted_Services' },
| |
| { label: 'Consumables', href: WIKI + '/index.php/BioMicroCenter:Consumables' },
| |
| { label: 'Training', href: 'https://igb.mit.edu/mini-courses', external: true },
| |
| { label: 'Informatics', href: 'https://igb.mit.edu/', external: true }
| |
| ]
| |
| },
| |
| {
| |
| label: 'Submission',
| |
| href: WIKI + '/index.php/BioMicroCenter:Submission',
| |
| children: [
| |
| { group: 'MIT Users' },
| |
| { label: 'Submit a Sample', href: WIKI + '/index.php/BioMicroCenter:Submission' },
| |
| { label: 'MIT Pricing', href: WIKI + '/index.php/MIT:Pricing', external: true },
| |
| { divider: true },
| |
| { group: 'Non-MIT Users' },
| |
| { label: 'External Submission', href: WIKI + '/index.php/BioMicroCenter:Submission' },
| |
| { label: 'External Pricing', href: WIKI + '/index.php/BioMicroCenter:Grant_Support_%26_Pricing', highlight: true }
| |
| ]
| |
| },
| |
| {
| |
| label: 'Staff',
| |
| href: WIKI + '/index.php/BioMicroCenter:Staff'
| |
| },
| |
| {
| |
| label: 'Resources',
| |
| href: WIKI + '/index.php/BioMicroCenter:FAQ',
| |
| children: [
| |
| { label: 'FAQs', href: WIKI + '/index.php/BioMicroCenter:FAQ' },
| |
| { label: 'Forms', href: WIKI + '/index.php/BioMicroCenter:Forms' },
| |
| { label: 'Grant Support & Pricing', href: WIKI + '/index.php/BioMicroCenter:Grant_Support_%26_Pricing' },
| |
| { label: 'Acknowledgements', href: WIKI + '/index.php/BioMicroCenter:Acknowledgement', external: true }
| |
| ]
| |
| }
| |
| ];
| |
|
| |
|
| | // Restore persisted state |
| | if ( localStorage.getItem( STORAGE_KEY ) === '1' ) { |
| | body.classList.add( 'sidebar-collapsed' ); |
| | } |
|
| |
|
| /* ── Helpers ── */
| | btn.addEventListener( 'click', function () { |
| function make( tag, cls ) {
| | var collapsed = body.classList.toggle( 'sidebar-collapsed' ); |
| var n = document.createElement( tag );
| | localStorage.setItem( STORAGE_KEY, collapsed ? '1' : '0' ); |
| if ( cls ) n.className = cls;
| | } ); |
| return n;
| | } |
| }
| |
|
| |
|
| function isCurrentPage( href ) {
| | /* ── 2. Smooth Scroll for TOC links ─────────────────────────── */ |
| return window.location.href.indexOf( href ) !== -1;
| | function initSmoothScroll() { |
| }
| | var tocLinks = document.querySelectorAll( '#toc a, .toc a' ); |
| | tocLinks.forEach( function ( link ) { |
| | link.addEventListener( 'click', function ( e ) { |
| | var href = link.getAttribute( 'href' ); |
| | if ( href && href.charAt( 0 ) === '#' ) { |
| | var target = document.getElementById( decodeURIComponent( href.slice( 1 ) ) ); |
| | if ( target ) { |
| | e.preventDefault(); |
| | target.scrollIntoView( { behavior: 'smooth', block: 'start' } ); |
| | // Update URL without page jump |
| | history.pushState( null, '', href ); |
| | } |
| | } |
| | } ); |
| | } ); |
| | } |
|
| |
|
| | /* ── 3. Active Sidebar Nav Highlighting ─────────────────────── */ |
| | function highlightActiveSidebarLink() { |
| | var currentTitle = mw.config.get( 'wgPageName' ); |
| | var sidebarLinks = document.querySelectorAll( |
| | '#mw-panel a, .vector-column-start a' |
| | ); |
| | sidebarLinks.forEach( function ( link ) { |
| | var href = link.getAttribute( 'href' ) || ''; |
| | // Match by title query param or path segment |
| | if ( |
| | href.indexOf( encodeURIComponent( currentTitle ) ) !== -1 || |
| | href.indexOf( currentTitle.replace( / /g, '_' ) ) !== -1 |
| | ) { |
| | var li = link.closest( 'li' ); |
| | if ( li ) { |
| | li.classList.add( 'active' ); |
| | } |
| | } |
| | } ); |
| | } |
|
| |
|
| /* ── 1. Inject Topbar ── */
| | /* ── 4. Auto-style Plain Tables ─────────────────────────────── */ |
| function injectTopbar() {
| | function styleUnstyledTables() { |
| if ( document.querySelector( '.bmc-topbar' ) ) return;
| | var tables = document.querySelectorAll( |
| var bar = make( 'div', 'bmc-topbar' );
| | '.mw-parser-output table:not(.wikitable):not(.infobox):not(.navbox)' |
| bar.innerHTML =
| | ); |
| '<div class="bmc-inner">' +
| | tables.forEach( function ( table ) { |
| '<a href="mailto:biomicro@mit.edu">biomicro@mit.edu</a>' +
| | table.classList.add( 'wikitable' ); |
| '<span class="bmc-sep">|</span>617-715-4533' +
| | } ); |
| '<span class="bmc-sep">|</span>Building 68-322' +
| | } |
| '</div>';
| |
| document.body.insertBefore( bar, document.body.firstChild );
| |
| }
| |
|
| |
|
| | /* ── 5. Run on page content ready ───────────────────────────── */ |
| | mw.hook( 'wikipage.content' ).add( function () { |
| | initSidebarToggle(); |
| | initSmoothScroll(); |
| | highlightActiveSidebarLink(); |
| | styleUnstyledTables(); |
| | } ); |
|
| |
|
| /* ── 2. Build Nav HTML ── */
| | } )(); |
| function buildNavList() {
| |
| var ul = make( 'ul' );
| |
| NAV.forEach( function ( item ) {
| |
| var li = make( 'li' );
| |
| var active = isCurrentPage( item.href );
| |
| | |
| if ( item.children ) {
| |
| li.className = 'bmc-dropdown' + ( active ? ' bmc-active' : '' );
| |
| var topLink = make( 'a' );
| |
| topLink.href = item.href;
| |
| topLink.textContent = item.label;
| |
| li.appendChild( topLink );
| |
| | |
| var menu = make( 'div', 'bmc-dropdown-menu' );
| |
| item.children.forEach( function ( c ) {
| |
| if ( c.divider ) {
| |
| menu.appendChild( make( 'div', 'bmc-divider' ) );
| |
| } else if ( c.group ) {
| |
| var g = make( 'div', 'bmc-group-label' );
| |
| g.textContent = c.group;
| |
| menu.appendChild( g );
| |
| } else {
| |
| var a = make( 'a' );
| |
| a.href = c.href;
| |
| a.textContent = c.label;
| |
| if ( c.external ) a.setAttribute( 'target', '_blank' );
| |
| if ( c.highlight ) { a.style.fontWeight = '600'; a.style.color = '#a31f34'; }
| |
| menu.appendChild( a );
| |
| }
| |
| } );
| |
| li.appendChild( menu );
| |
| } else {
| |
| li.className = active ? 'bmc-active' : '';
| |
| var a = make( 'a' );
| |
| a.href = item.href;
| |
| a.textContent = item.label;
| |
| li.appendChild( a );
| |
| }
| |
| ul.appendChild( li );
| |
| } );
| |
| return ul;
| |
| }
| |
| | |
| | |
| /* ── 3. Inject Header ── */
| |
| function injectHeader() {
| |
| if ( document.querySelector( '.bmc-header' ) ) return;
| |
| | |
| var header = make( 'div', 'bmc-header' );
| |
| var inner = make( 'div', 'bmc-header-inner' );
| |
| | |
| /* Logo */
| |
| var logo = make( 'a', 'bmc-logo' );
| |
| logo.href = WIKI;
| |
| logo.innerHTML =
| |
| '<div class="bmc-logo-mark">BMC</div>' +
| |
| '<div class="bmc-logo-text">' +
| |
| '<span class="bmc-logo-name">MIT BioMicro Center</span>' +
| |
| '<span class="bmc-logo-sub">Integrated Genomics Core Facility</span>' +
| |
| '</div>';
| |
| | |
| /* Nav */
| |
| var nav = make( 'nav', 'bmc-nav' );
| |
| nav.appendChild( buildNavList() );
| |
| | |
| /* Right side: move Vector's search + user tools here */
| |
| var right = make( 'div', 'bmc-header-right' );
| |
| | |
| /* Grab Vector's search box and user links — move into our header */
| |
| var vectorSearch = document.querySelector( '.vector-search-box, #p-search, .mw-search-container' );
| |
| if ( vectorSearch ) right.appendChild( vectorSearch );
| |
| | |
| var vectorUser = document.querySelector( '.vector-user-links, #p-personal, .mw-portlet-personal' );
| |
| if ( vectorUser ) right.appendChild( vectorUser );
| |
| | |
| inner.appendChild( logo );
| |
| inner.appendChild( nav );
| |
| inner.appendChild( right );
| |
| header.appendChild( inner );
| |
| | |
| /* Insert after topbar */
| |
| var topbar = document.querySelector( '.bmc-topbar' );
| |
| if ( topbar ) {
| |
| topbar.insertAdjacentElement( 'afterend', header );
| |
| } else {
| |
| document.body.insertBefore( header, document.body.firstChild );
| |
| }
| |
| }
| |
| | |
| | |
| /* ── 4. Inject Hero Banner ── */
| |
| function injectHero() {
| |
| if ( document.querySelector( '.bmc-hero' ) ) return;
| |
| | |
| /* Skip Special: pages and edit forms */
| |
| var ns = mw.config.get( 'wgNamespaceNumber' );
| |
| if ( ns < 0 ) return;
| |
| if ( document.querySelector( '#editform' ) ) return;
| |
| | |
| /* Read title from the hidden Vector h1 */
| |
| var h1El = document.querySelector( 'h1.firstHeading, .mw-first-heading' );
| |
| var title = h1El ? h1El.textContent.trim() : mw.config.get( 'wgTitle' );
| |
| | |
| var hero = make( 'div', 'bmc-hero' );
| |
| hero.innerHTML =
| |
| '<div class="bmc-inner">' +
| |
| '<div class="bmc-breadcrumb">' +
| |
| '<a href="' + WIKI + '">Home</a>' +
| |
| '<span class="bmc-sep">›</span>' +
| |
| mw.html.escape( title ) +
| |
| '</div>' +
| |
| '<div class="bmc-hero-title">' + mw.html.escape( title ) + '</div>' +
| |
| '</div>';
| |
| | |
| /* Insert immediately before the page content container — safe, no element moved */
| |
| var target = document.querySelector( '.mw-content-container' );
| |
| if ( target ) {
| |
| target.parentNode.insertBefore( hero, target );
| |
| }
| |
| }
| |
| | |
| | |
| /* ── 5. Inject Footer ── */
| |
| function injectFooter() {
| |
| if ( document.querySelector( '.bmc-footer' ) ) return;
| |
| | |
| var footer = make( 'div', 'bmc-footer' );
| |
| footer.innerHTML =
| |
| '<div class="bmc-footer-inner">' +
| |
| '<div>' +
| |
| '<h4>MIT BioMicro Center</h4>' +
| |
| '<ul>' +
| |
| '<li>Building 68-322</li>' +
| |
| '<li>Cambridge, MA 02139</li>' +
| |
| '<li><a href="mailto:biomicro@mit.edu">biomicro@mit.edu</a></li>' +
| |
| '<li>617-715-4533</li>' +
| |
| '</ul>' +
| |
| '</div>' +
| |
| '<div>' +
| |
| '<h4>Services</h4>' +
| |
| '<ul>' +
| |
| '<li><a href="' + WIKI + '/index.php/BioMicroCenter:Walk-up_Services">Walk-up</a></li>' +
| |
| '<li><a href="' + WIKI + '/index.php/BioMicroCenter:Assisted_Services">Assisted</a></li>' +
| |
| '<li><a href="' + WIKI + '/index.php/BioMicroCenter:Consumables">Consumables</a></li>' +
| |
| '<li><a href="https://igb.mit.edu/" target="_blank">Informatics</a></li>' +
| |
| '</ul>' +
| |
| '</div>' +
| |
| '<div>' +
| |
| '<h4>Resources</h4>' +
| |
| '<ul>' +
| |
| '<li><a href="' + WIKI + '/index.php/BioMicroCenter:FAQ">FAQs</a></li>' +
| |
| '<li><a href="' + WIKI + '/index.php/BioMicroCenter:Forms">Forms</a></li>' +
| |
| '<li><a href="' + WIKI + '/index.php/BioMicroCenter:Grant_Support_%26_Pricing">Grant Support & Pricing</a></li>' +
| |
| '<li><a href="' + WIKI + '/index.php/BioMicroCenter:Acknowledgement">Acknowledgements</a></li>' +
| |
| '</ul>' +
| |
| '</div>' +
| |
| '</div>' +
| |
| '<div class="bmc-footer-bottom">' +
| |
| '<span>© ' + new Date().getFullYear() + ' MIT BioMicro Center</span>' +
| |
| '<span><a href="https://accessibility.mit.edu" target="_blank">Accessibility</a></span>' +
| |
| '</div>';
| |
| | |
| document.body.appendChild( footer );
| |
| }
| |
| | |
| | |
| /* ── 6. Scroll Offset Fix ── */
| |
| function fixScrollOffset() {
| |
| var OFFSET = 116; /* topbar 28px + header 68px + buffer 20px */
| |
| document.addEventListener( 'click', function ( e ) {
| |
| var anchor = e.target.closest( 'a[href^="#"]' );
| |
| if ( !anchor ) return;
| |
| var id = anchor.getAttribute( 'href' ).slice( 1 );
| |
| if ( !id ) return;
| |
| var target = document.getElementById( id ) ||
| |
| document.querySelector( '[name="' + CSS.escape( id ) + '"]' );
| |
| if ( !target ) return;
| |
| e.preventDefault();
| |
| window.scrollTo( {
| |
| top: target.getBoundingClientRect().top + window.pageYOffset - OFFSET,
| |
| behavior: 'smooth'
| |
| } );
| |
| if ( history.pushState ) history.pushState( null, null, '#' + id );
| |
| } );
| |
| }
| |
| | |
| | |
| /* ── 7. Tab Panel System ── */
| |
| function initTabs() {
| |
| var btns = document.querySelectorAll( '.tab-btn' );
| |
| var panels = document.querySelectorAll( '.tab-panel' );
| |
| if ( !btns.length ) return;
| |
| | |
| if ( !document.querySelector( '.tab-btn.active' ) ) btns[ 0 ].classList.add( 'active' );
| |
| if ( !document.querySelector( '.tab-panel.active' ) && panels[ 0 ] ) panels[ 0 ].classList.add( 'active' );
| |
| | |
| btns.forEach( function ( btn ) {
| |
| btn.addEventListener( 'click', function () {
| |
| btns.forEach( function ( t ) { t.classList.remove( 'active' ); } );
| |
| panels.forEach( function ( p ) { p.classList.remove( 'active' ); } );
| |
| btn.classList.add( 'active' );
| |
| var panel = document.getElementById( 'tab-' + btn.dataset.tab );
| |
| if ( panel ) panel.classList.add( 'active' );
| |
| var wrap = document.querySelector( '.tab-bar-wrap' );
| |
| if ( wrap ) window.scrollTo( { top: wrap.getBoundingClientRect().top + window.pageYOffset - 96, behavior: 'smooth' } );
| |
| } );
| |
| } );
| |
| }
| |
| | |
| | |
| /* ── Run ── */
| |
| injectTopbar();
| |
| injectHeader();
| |
| injectHero();
| |
| injectFooter();
| |
| fixScrollOffset();
| |
| initTabs();
| |
| | |
| } );
| |