User:Gibcus/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.
/**
* User:Gibcus/common.js
* MIT BioMicro Center — Modern Wiki Theme Engine
*
* Paste this entire file into:
* https://bmcwiki.mit.edu/index.php/User:Gibcus/common.js
*
* It applies to YOUR login session only.
* To deploy for all users, an admin with editinterface rights
* must paste it into MediaWiki:Common.js instead.
*/
window.BMC_DEBUG = true;
( function () {
'use strict';
/* ============================================================
1. DESIGN TOKENS
============================================================ */
var TOKENS = {
black: '#0d0d0d',
darkGray: '#1a1a1a',
midGray: '#2e2e2e',
border: '#e2ddd6',
bg: '#f5f3ef',
bgCard: '#ffffff',
gold: '#b8913a',
goldHov: '#d4a84b',
red: '#8b1a1a', // MIT red accent
text: '#1c1c1c',
textMid: '#555555',
textLight:'#888888',
fontBody: "'DM Sans', 'Helvetica Neue', Arial, sans-serif",
fontMono: "'DM Mono', 'Courier New', monospace",
};
/* ============================================================
2. GLOBAL CSS — injected once, applies to every BMC page
============================================================ */
var GLOBAL_CSS = [
"@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;1,400&family=DM+Mono:wght@400;500&display=swap');",
/* --- Reset MediaWiki chrome --- */
'#mw-navigation, #mw-head, #mw-panel, #mw-sidebar-button,',
'#mw-sidebar-checkbox, .mw-footer, #footer, #p-logo,',
'.vector-menu-tabs, .vector-header-start .vector-header-logo,',
'.mw-portlet-lang, #p-cactions, .vector-page-toolbar { display: none !important; }',
'#content, #mw-content-text, .mw-body, #mw-body-content,',
'.mw-body-content { margin: 0 !important; padding: 0 !important;',
'max-width: none !important; border: none !important; }',
'body { background: ' + TOKENS.bg + ' !important;',
'font-family: ' + TOKENS.fontBody + ' !important;',
'color: ' + TOKENS.text + ' !important; margin: 0 !important; padding: 0 !important; }',
/* hide edit pencil icons in headings */
'.mw-editsection { display: none !important; }',
/* --- BMC Top Nav Bar --- */
'#bmc-topnav {',
' position: sticky; top: 0; z-index: 1000;',
' background: ' + TOKENS.black + ';',
' display: flex; align-items: center;',
' padding: 0 32px; height: 56px;',
' box-shadow: 0 1px 0 rgba(255,255,255,0.06);',
' gap: 0;',
'}',
'#bmc-topnav .bmc-nav-logo {',
' font-family: ' + TOKENS.fontBody + ';',
' font-size: 0.95em; font-weight: 600;',
' color: #fff; text-decoration: none;',
' letter-spacing: -0.01em; white-space: nowrap;',
' padding-right: 32px;',
' border-right: 1px solid #333;',
' margin-right: 8px;',
' display: flex; align-items: center; gap: 10px;',
'}',
'#bmc-topnav .bmc-nav-logo span.accent { color: ' + TOKENS.gold + '; }',
'#bmc-topnav .bmc-nav-logo img { height: 28px; width: 28px; border-radius: 4px; }',
'#bmc-nav-links {',
' display: flex; align-items: center;',
' gap: 2px; flex: 1; overflow-x: auto;',
'}',
'#bmc-nav-links a {',
' color: #aaa; text-decoration: none;',
' font-size: 0.8em; font-weight: 500;',
' letter-spacing: 0.03em;',
' padding: 6px 13px; border-radius: 6px;',
' white-space: nowrap;',
' transition: color 0.15s, background 0.15s;',
'}',
'#bmc-nav-links a:hover { color: #fff; background: #2a2a2a; }',
'#bmc-nav-links a.bmc-nav-active { color: ' + TOKENS.gold + '; }',
'#bmc-nav-right {',
' display: flex; align-items: center; gap: 12px; margin-left: auto;',
'}',
'#bmc-nav-right a.bmc-ilabs-btn {',
' background: ' + TOKENS.gold + ';',
' color: ' + TOKENS.black + '; font-weight: 600;',
' font-size: 0.75em; letter-spacing: 0.05em;',
' padding: 6px 14px; border-radius: 6px;',
' text-decoration: none; white-space: nowrap;',
' transition: background 0.15s;',
'}',
'#bmc-nav-right a.bmc-ilabs-btn:hover { background: ' + TOKENS.goldHov + '; }',
'#bmc-nav-right a.bmc-nav-search {',
' color: #888; font-size: 1.1em;',
' text-decoration: none;',
' padding: 4px 8px; border-radius: 6px;',
' transition: color 0.15s;',
'}',
'#bmc-nav-right a.bmc-nav-search:hover { color: #fff; }',
/* --- Page wrapper --- */
'#bmc-page-wrap {',
' max-width: 1140px; margin: 0 auto;',
' padding: 40px 32px 80px;',
'}',
'@media (max-width: 768px) {',
' #bmc-page-wrap { padding: 24px 16px 60px; }',
' #bmc-topnav { padding: 0 16px; }',
'}',
/* --- Page title --- */
'#bmc-page-title {',
' margin-bottom: 32px;',
'}',
'#bmc-page-title h1 {',
' font-size: 2em; font-weight: 600;',
' color: ' + TOKENS.black + '; letter-spacing: -0.025em;',
' line-height: 1.1; margin: 0 0 6px;',
'}',
'#bmc-page-title .bmc-subtitle {',
' font-size: 0.92em; color: ' + TOKENS.textMid + ';',
' font-weight: 400;',
'}',
/* --- Section headings --- */
'#bmc-page-wrap h2 {',
' font-size: 1.1em; font-weight: 600;',
' color: ' + TOKENS.black + ';',
' letter-spacing: 0.06em; text-transform: uppercase;',
' margin: 40px 0 16px;',
' padding-bottom: 8px;',
' border-bottom: 1px solid ' + TOKENS.border + ';',
'}',
'#bmc-page-wrap h3 {',
' font-size: 0.95em; font-weight: 600;',
' color: ' + TOKENS.textMid + ';',
' letter-spacing: 0.04em; text-transform: uppercase;',
' margin: 28px 0 12px;',
'}',
/* --- Generic body content prose --- */
'#bmc-page-wrap p {',
' font-size: 0.95em; line-height: 1.7;',
' color: ' + TOKENS.textMid + '; margin-bottom: 12px;',
'}',
'#bmc-page-wrap a {',
' color: ' + TOKENS.gold + '; text-decoration: none;',
' border-bottom: 1px solid transparent;',
' transition: border-color 0.15s;',
'}',
'#bmc-page-wrap a:hover { border-bottom-color: ' + TOKENS.gold + '; }',
/* --- Generic list styling --- */
'#bmc-page-wrap ul, #bmc-page-wrap ol {',
' padding-left: 1.4em; margin-bottom: 12px;',
'}',
'#bmc-page-wrap li {',
' font-size: 0.92em; line-height: 1.7;',
' color: ' + TOKENS.textMid + '; margin-bottom: 3px;',
'}',
/* --- Generic wiki tables → clean style --- */
'#bmc-page-wrap table.wikitable, #bmc-page-wrap table {',
' border-collapse: collapse; width: 100%;',
' font-size: 0.87em; margin-bottom: 24px;',
' background: ' + TOKENS.bgCard + ';',
' border-radius: 10px; overflow: hidden;',
' border: 1px solid ' + TOKENS.border + ';',
' box-shadow: 0 1px 4px rgba(0,0,0,0.05);',
'}',
'#bmc-page-wrap table th {',
' background: ' + TOKENS.darkGray + ';',
' color: #ddd; font-weight: 500;',
' padding: 10px 14px; text-align: left;',
' font-size: 0.82em; letter-spacing: 0.04em;',
' text-transform: uppercase;',
'}',
'#bmc-page-wrap table td {',
' padding: 9px 14px;',
' border-bottom: 1px solid ' + TOKENS.border + ';',
' color: ' + TOKENS.text + '; vertical-align: top;',
'}',
'#bmc-page-wrap table tr:last-child td { border-bottom: none; }',
'#bmc-page-wrap table tr:hover td { background: #faf8f5; }',
/* --- Card component --- */
'.bmc-card {',
' background: ' + TOKENS.bgCard + ';',
' border: 1px solid ' + TOKENS.border + ';',
' border-radius: 12px;',
' padding: 24px;',
' box-shadow: 0 1px 4px rgba(0,0,0,0.05);',
'}',
'.bmc-card-grid {',
' display: grid;',
' grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));',
' gap: 16px; margin-bottom: 32px;',
'}',
/* --- Inline badge --- */
'.bmc-badge {',
' display: inline-block;',
' font-size: 0.7em; font-weight: 600;',
' letter-spacing: 0.06em; text-transform: uppercase;',
' padding: 2px 7px; border-radius: 4px;',
' background: ' + TOKENS.gold + '22;',
' color: ' + TOKENS.gold + '; margin-left: 6px;',
' vertical-align: middle;',
'}',
/* --- TOC hide (we build nav instead) --- */
'#toc, .toc { display: none !important; }',
/* --- Breadcrumb strip under nav --- */
'#bmc-breadcrumb {',
' background: #fff;',
' border-bottom: 1px solid ' + TOKENS.border + ';',
' padding: 10px 32px;',
' font-size: 0.78em;',
' color: ' + TOKENS.textLight + ';',
' display: flex; align-items: center; gap: 6px;',
'}',
'#bmc-breadcrumb a { color: ' + TOKENS.textMid + '; text-decoration: none; }',
'#bmc-breadcrumb a:hover { color: ' + TOKENS.gold + '; }',
'#bmc-breadcrumb .sep { color: #ccc; }',
/* --- Pricing-specific styles (reused from earlier) --- */
'#bmc-pricing-tier-toggle {',
' display: flex; gap: 4px;',
' background: ' + TOKENS.darkGray + ';',
' border-radius: 10px; padding: 4px;',
' margin-bottom: 24px; width: fit-content;',
'}',
'#bmc-pricing-tier-toggle button {',
' padding: 7px 18px; border: none;',
' border-radius: 7px; font-size: 0.8em;',
' font-weight: 600; cursor: pointer;',
' letter-spacing: 0.04em;',
' transition: all 0.15s;',
' font-family: ' + TOKENS.fontBody + ';',
' background: transparent; color: #777;',
'}',
'#bmc-pricing-tier-toggle button.active-core { background: #1a6b3c; color: #fff; }',
'#bmc-pricing-tier-toggle button.active-mit { background: #8b1a1a; color: #fff; }',
'#bmc-pricing-tier-toggle button.active-nonmit{ background: #1a3b8b; color: #fff; }',
'.bmc-price-section {',
' background: ' + TOKENS.bgCard + ';',
' border: 1px solid ' + TOKENS.border + ';',
' border-radius: 12px; overflow: hidden;',
' margin-bottom: 12px;',
' box-shadow: 0 1px 3px rgba(0,0,0,0.04);',
'}',
'.bmc-price-section-header {',
' background: #faf9f7;',
' border-bottom: 1px solid ' + TOKENS.border + ';',
' padding: 13px 20px;',
' font-size: 0.88em; font-weight: 600;',
' color: #333; cursor: pointer;',
' display: flex; justify-content: space-between; align-items: center;',
' user-select: none;',
'}',
'.bmc-price-section-header:hover { background: #f2f0ec; }',
'.bmc-price-section-header .chevron { color: #aaa; font-size: 0.8em; transition: transform 0.2s; }',
'.bmc-price-section-header.collapsed .chevron { transform: rotate(-90deg); }',
'.bmc-price-row {',
' display: flex; justify-content: space-between; align-items: flex-start;',
' padding: 11px 20px;',
' border-bottom: 1px solid #f5f2ee;',
' transition: background 0.1s; gap: 16px;',
'}',
'.bmc-price-row:last-child { border-bottom: none; }',
'.bmc-price-row:hover { background: #faf8f5; }',
'.bmc-price-row-name {',
' font-size: 0.87em; color: #222;',
'}',
'.bmc-price-row-unit {',
' font-size: 0.74em; color: #aaa;',
' font-family: ' + TOKENS.fontMono + '; margin-top: 2px;',
'}',
'.bmc-price-row-note {',
' font-size: 0.74em; color: #999;',
' font-style: italic; margin-top: 3px;',
'}',
'.bmc-price-amount {',
' font-size: 1em; font-weight: 600;',
' font-family: ' + TOKENS.fontMono + ';',
' white-space: nowrap; text-align: right;',
' min-width: 90px;',
'}',
'.bmc-price-amount.tier-core { color: #1a6b3c; }',
'.bmc-price-amount.tier-mit { color: #8b1a1a; }',
'.bmc-price-amount.tier-nonmit { color: #1a3b8b; }',
'.bmc-price-amount.tier-na { color: #ccc; font-style: italic; font-size: 0.82em; }',
/* search bar */
'#bmc-pricing-search-wrap {',
' position: relative; margin-bottom: 20px; width: 260px;',
'}',
'#bmc-pricing-search {',
' width: 100%; padding: 9px 14px 9px 36px;',
' border: 1.5px solid #ddd; border-radius: 8px;',
' font-size: 0.85em; font-family: ' + TOKENS.fontBody + ';',
' background: #fff; outline: none;',
'}',
'#bmc-pricing-search:focus { border-color: ' + TOKENS.gold + '; }',
'#bmc-pricing-search-icon {',
' position: absolute; left: 12px; top: 50%;',
' transform: translateY(-50%); color: #aaa; pointer-events: none;',
'}',
/* tier indicator pill */
'#bmc-tier-pill {',
' display: inline-flex; align-items: center; gap: 8px;',
' padding: 5px 12px; border-radius: 6px;',
' font-size: 0.78em; font-weight: 600;',
' letter-spacing: 0.04em; margin-bottom: 20px;',
'}',
/* homepage card grid */
'.bmc-home-grid {',
' display: grid;',
' grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));',
' gap: 16px; margin-top: 24px;',
'}',
'.bmc-home-card {',
' background: ' + TOKENS.bgCard + ';',
' border: 1px solid ' + TOKENS.border + ';',
' border-radius: 14px; padding: 22px 24px;',
' box-shadow: 0 1px 4px rgba(0,0,0,0.05);',
' transition: box-shadow 0.2s, transform 0.2s;',
'}',
'.bmc-home-card:hover {',
' box-shadow: 0 4px 16px rgba(0,0,0,0.1);',
' transform: translateY(-2px);',
'}',
'.bmc-home-card .card-icon {',
' font-size: 1.6em; margin-bottom: 12px;',
'}',
'.bmc-home-card .card-title {',
' font-size: 0.95em; font-weight: 600;',
' color: ' + TOKENS.black + '; margin-bottom: 8px;',
'}',
'.bmc-home-card .card-links {',
' list-style: none; padding: 0; margin: 0;',
'}',
'.bmc-home-card .card-links li {',
' font-size: 0.84em; line-height: 1.8;',
' color: ' + TOKENS.textMid + ';',
'}',
'.bmc-home-card .card-links li a {',
' color: ' + TOKENS.textMid + '; text-decoration: none;',
' border-bottom: none;',
' transition: color 0.15s;',
'}',
'.bmc-home-card .card-links li a:hover { color: ' + TOKENS.gold + '; }',
'.bmc-home-card .card-links li::before {',
' content: "→ "; color: ' + TOKENS.gold + '; font-size: 0.85em;',
'}',
/* contact strip */
'#bmc-contact-strip {',
' background: ' + TOKENS.darkGray + ';',
' color: #ccc; border-radius: 12px;',
' padding: 24px 28px; margin-top: 32px;',
' display: flex; gap: 32px; flex-wrap: wrap; align-items: center;',
'}',
'#bmc-contact-strip .contact-item {',
' font-size: 0.87em; display: flex; gap: 8px; align-items: center;',
'}',
'#bmc-contact-strip a { color: ' + TOKENS.gold + '; text-decoration: none; }',
'#bmc-contact-strip .contact-label { color: #666; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.08em; }',
].join('\n');
/* ============================================================
3. NAV STRUCTURE
============================================================ */
var NAV_LINKS = [
{ label: 'Home', href: '/index.php/BioMicroCenter', match: 'BioMicroCenter$' },
{ label: 'Sequencing', href: '/index.php/BioMicroCenter:Sequencing', match: 'Sequencing' },
{ label: 'Library Prep', href: '/index.php/BioMicroCenter:Illumina_Library_Preparation', match: 'Library' },
{ label: 'Single Cell', href: '/index.php/BioMicroCenter:SingleCell', match: 'SingleCell|SpTx|Visium' },
{ label: 'Long Read', href: '/index.php/BioMicroCenter:Oxford_Nanopore_Technologies', match: 'Nanopore|PacBio' },
{ label: 'Computing', href: '/index.php/BioMicroCenter:Computing', match: 'Computing' },
{ label: 'QC', href: '/index.php/BioMicroCenter:QC', match: ':QC' },
{ label: 'Pricing', href: '/index.php/BioMicroCenter:Pricing', match: 'Pricing' },
{ label: 'People', href: '/index.php/BioMicroCenter:People', match: 'People' },
{ label: 'FAQ', href: '/index.php/BioMicroCenter:FAQ', match: 'FAQ' },
];
/* ============================================================
4. MAIN BMC OBJECT
============================================================ */
var BMC = {
page: mw.config.get( 'wgPageName' ) || '',
/* ---- 4a. INIT ---- */
init: function () {
if ( document.getElementById( 'bmc-topnav' ) ) return; // already ran
this.injectCSS();
this.injectNav();
this.injectBreadcrumb();
this.wrapContent();
// Page-specific transforms
if ( /^BioMicroCenter$/.test( this.page ) ) {
this.transformHomepage();
} else if ( /Pricing/.test( this.page ) ) {
this.transformPricing();
} else {
this.transformGeneric();
}
},
/* ---- 4b. INJECT GLOBAL CSS ---- */
injectCSS: function () {
var style = document.createElement( 'style' );
style.id = 'bmc-theme-css';
style.textContent = GLOBAL_CSS;
document.head.appendChild( style );
},
/* ---- 4c. INJECT TOP NAV ---- */
injectNav: function () {
var page = this.page;
var logoImg = document.querySelector( '#p-logo img, .mw-wiki-logo' );
var logoSrc = logoImg ? logoImg.src : '';
var linksHtml = NAV_LINKS.map( function ( l ) {
var active = new RegExp( l.match ).test( page ) ? ' bmc-nav-active' : '';
return '<a href="' + l.href + '" class="' + active + '">' + l.label + '</a>';
} ).join( '' );
var nav = document.createElement( 'div' );
nav.id = 'bmc-topnav';
nav.innerHTML =
'<a href="/index.php/BioMicroCenter" class="bmc-nav-logo">' +
( logoSrc ? '<img src="' + logoSrc + '" alt="BMC">' : '' ) +
'BioMicro <span class="accent">Center</span>' +
'</a>' +
'<div id="bmc-nav-links">' + linksHtml + '</div>' +
'<div id="bmc-nav-right">' +
'<a href="/index.php/Special:Search" class="bmc-nav-search" title="Search">⌕</a>' +
'<a href="https://mit-ki.ilabsolutions.com/sc/3381/ki-genomics-core-mit-biomicro-center?tab=about"' +
' class="bmc-ilabs-btn" target="_blank">Submit via iLabs ↗</a>' +
'</div>';
// Insert before body content
var body = document.body;
body.insertBefore( nav, body.firstChild );
},
/* ---- 4d. BREADCRUMB ---- */
injectBreadcrumb: function () {
var page = this.page;
if ( /^BioMicroCenter$/.test( page ) ) return; // no breadcrumb on homepage
var parts = page.split( ':' );
var crumb = document.createElement( 'div' );
crumb.id = 'bmc-breadcrumb';
crumb.innerHTML =
'<a href="/index.php/BioMicroCenter">Home</a>' +
'<span class="sep">›</span>' +
'<span>' + ( parts[1] || parts[0] ).replace( /_/g, ' ' ) + '</span>';
var nav = document.getElementById( 'bmc-topnav' );
if ( nav ) nav.after( crumb );
},
/* ---- 4e. WRAP MAIN CONTENT ---- */
wrapContent: function () {
var content = document.getElementById( 'mw-content-text' )
|| document.getElementById( 'content' )
|| document.querySelector( '.mw-body-content' );
if ( !content ) return;
// Get the first heading
var h1 = document.querySelector( '#firstHeading, h1.firstHeading' );
var titleText = h1 ? h1.textContent.replace( /^BioMicroCenter:?/, '' ).trim() : '';
// Build page title block
var titleBlock = document.createElement( 'div' );
titleBlock.id = 'bmc-page-title';
if ( titleText && !/^BioMicroCenter$/.test( this.page ) ) {
titleBlock.innerHTML =
'<h1>' + titleText + '</h1>';
}
if ( h1 ) h1.style.display = 'none';
// Wrap in page container
var wrap = document.createElement( 'div' );
wrap.id = 'bmc-page-wrap';
content.parentNode.insertBefore( wrap, content );
wrap.appendChild( titleBlock );
wrap.appendChild( content );
},
/* ---- 4f. GENERIC PAGE TRANSFORM ---- */
transformGeneric: function () {
// Just let the global CSS handle it — tables, headings, prose all look better automatically.
// Remove the old banner image if present (it's usually a 500px wide header)
var banner = document.querySelector( '#mw-content-text img[src*="BMC_Header"]' );
if ( banner ) {
var bannerParent = banner.closest( 'p, div' );
if ( bannerParent ) bannerParent.style.display = 'none';
}
},
/* ---- 4g. HOMEPAGE TRANSFORM ---- */
transformHomepage: function () {
var wrap = document.getElementById( 'bmc-page-wrap' );
if ( !wrap ) return;
// Hide the old content and replace with hero + cards
var oldContent = document.getElementById( 'mw-content-text' );
// Extract links from existing wiki tables to preserve them
var allLinks = {};
if ( oldContent ) {
oldContent.querySelectorAll( 'a' ).forEach( function ( a ) {
allLinks[a.textContent.trim()] = a.href;
} );
oldContent.style.display = 'none';
}
// Hero
var hero = document.createElement( 'div' );
hero.style.cssText = [
'background: ' + TOKENS.black + ';',
'border-radius: 16px; padding: 48px 40px;',
'margin-bottom: 32px; position: relative; overflow: hidden;',
'color: #fff;',
].join( '' );
hero.innerHTML = [
'<div style="position:relative;z-index:1">',
'<div style="font-size:0.72em;letter-spacing:0.16em;color:#888;text-transform:uppercase;',
'margin-bottom:12px;font-family:' + TOKENS.fontMono + '">',
'Koch Institute · MIT Department of Biology · Biological Engineering',
'</div>',
'<h1 style="font-size:2.4em;font-weight:600;letter-spacing:-0.03em;',
'line-height:1;margin:0 0 16px;color:#fff">',
'MIT BioMicro<br><span style="color:' + TOKENS.gold + '">Center</span>',
'</h1>',
'<p style="max-width:560px;font-size:0.95em;line-height:1.7;color:#aaa;margin:0 0 24px">',
'An integrated genomics core facility providing expertise and equipment for ',
'systems biology — from sample prep through sequencing, single-cell, and bioinformatics.',
'</p>',
'<div style="display:flex;gap:12px;flex-wrap:wrap">',
'<a href="https://mit-ki.ilabsolutions.com/sc/3381/ki-genomics-core-mit-biomicro-center?tab=about"',
' target="_blank"',
' style="background:' + TOKENS.gold + ';color:' + TOKENS.black + ';',
'font-weight:600;font-size:0.85em;padding:10px 20px;border-radius:8px;',
'text-decoration:none;letter-spacing:0.02em">',
'Submit Samples via iLabs ↗',
'</a>',
'<a href="/index.php/BioMicroCenter:Pricing"',
' style="background:#222;color:#ccc;',
'font-weight:500;font-size:0.85em;padding:10px 20px;border-radius:8px;',
'text-decoration:none">',
'View Pricing',
'</a>',
'</div>',
'</div>',
// subtle grid pattern overlay
'<div style="position:absolute;inset:0;opacity:0.04;pointer-events:none;',
'background-image:repeating-linear-gradient(0deg,#fff 0,#fff 1px,transparent 1px,transparent 40px),',
'repeating-linear-gradient(90deg,#fff 0,#fff 1px,transparent 1px,transparent 40px)">',
'</div>',
].join( '' );
wrap.appendChild( hero );
// Service cards
var CARDS = [
{
icon: '🧬',
title: 'Sequencing',
links: [
{ label: 'NovaSeq X / NovaSeq 6000', href: '/index.php/BioMicroCenter:Illumina_Sequencing' },
{ label: 'Element AVITI24', href: '/index.php/BioMicroCenter:Element_Sequencing' },
{ label: 'Singular G4 / MiSeq', href: '/index.php/BioMicroCenter:Illumina_Sequencing' },
{ label: 'ONT PromethION', href: '/index.php/BioMicroCenter:Oxford_Nanopore_Technologies' },
{ label: 'PacBio Revio †', href: '/index.php/BioMicroCenter:PacBio' },
],
},
{
icon: '📚',
title: 'Library Preparation',
links: [
{ label: 'Short Read — DNA', href: '/index.php/BioMicroCenter:DNA_LIB' },
{ label: 'Short Read — RNA', href: '/index.php/BioMicroCenter:RNA_LIB' },
{ label: 'High Throughput DNA', href: '/index.php/BioMicroCenter:DNA_HTL' },
{ label: 'High Throughput RNA', href: '/index.php/BioMicroCenter:RNA_HTL' },
{ label: 'Nanopore Library Prep', href: '/index.php/BioMicroCenter:NanoPore_Library_Prep' },
{ label: 'PacBio Library Prep', href: '/index.php/BioMicroCenter:PacBio_Library_Preparation' },
],
},
{
icon: '⬡',
title: 'Single Cell & Spatial',
links: [
{ label: '10X Chromium (5′/3′ RNA, ATAC)', href: '/index.php/BioMicroCenter:SingleCell' },
{ label: '10X Visium (Standard + HD)', href: '/index.php/BioMicroCenter:SpTx' },
{ label: 'AVITI24 in situ', href: '/index.php/BioMicroCenter:Element_Sequencing' },
],
},
{
icon: '🔬',
title: 'Sample Services & QC',
links: [
{ label: 'Fragment Analyzer / FemtoPulse', href: '/index.php/BioMicroCenter:QC' },
{ label: 'BioAnalyzer', href: '/index.php/BioMicroCenter:QC' },
{ label: 'Chemagic360 DNA/RNA Extraction', href: '/index.php/BioMicroCenter:Tecan_Freedom_Evo' },
{ label: 'Covaris Sonicator', href: '/index.php/BioMicroCenter:Covaris' },
{ label: 'RT-PCR (Roche LC480)', href: '/index.php/BioMicroCenter:RTPCR' },
],
},
{
icon: '🤖',
title: 'Automation',
links: [
{ label: 'Tecan EVO 150 Liquid Handler', href: '/index.php/BioMicroCenter:Tecan_Freedom_Evo' },
{ label: 'SPT Mosquito HV', href: '/index.php/BioMicroCenter:Tecan_Freedom_Evo' },
{ label: 'Pippin Prep Electrophoresis', href: '/index.php/BioMicroCenter:PippinPrep' },
{ label: 'Oligo Synthesis', href: '/index.php/BioMicroCenter:Oligo_Synthesis' },
],
},
{
icon: '💻',
title: 'Bioinformatics & Computing',
links: [
{ label: 'Bioinformatics Consulting (IGB)', href: 'https://igb.mit.edu/' },
{ label: 'Luria Computing Cluster', href: 'https://igb.mit.edu/computing-resources/luria-cluster' },
{ label: 'Active Data Storage', href: 'https://igb.mit.edu/computing-resources/active-data-storage' },
{ label: 'Data Transfer (Globus)', href: 'https://igb.mit.edu/data-management/globus' },
{ label: 'Training Sessions', href: 'https://igb.mit.edu/mini-courses' },
],
},
{
icon: '📋',
title: 'Get Started',
links: [
{ label: 'Pricing', href: '/index.php/BioMicroCenter:Pricing' },
{ label: 'New User Signup', href: '/index.php/BioMicroCenter:Forms#NEW_USERS' },
{ label: 'External Submission Forms', href: '/index.php/BioMicroCenter:Forms' },
{ label: 'FAQs', href: '/index.php/BioMicroCenter:FAQ' },
{ label: 'Staff Directory', href: '/index.php/BioMicroCenter:People' },
{ label: 'News & Updates', href: '/index.php/BioMicroCenter:News' },
],
},
{
icon: '📅',
title: 'News & Resources',
links: [
{ label: 'News & General Information', href: '/index.php/BioMicroCenter:News' },
{ label: 'Technology Seminar Series', href: '/index.php/BioMicroCenter:Technology_Seminar_Series' },
{ label: 'Biostuff Mailing List', href: 'http://mailman.mit.edu:/mailman/listinfo/biostuff' },
{ label: 'FAIR Data Management', href: 'https://koch-institute-mit.gitbook.io/mit-data-management-analysis-core/' },
],
},
];
var grid = document.createElement( 'div' );
grid.className = 'bmc-home-grid';
CARDS.forEach( function ( card ) {
var li = card.links.map( function ( lnk ) {
return '<li><a href="' + lnk.href + '">' + lnk.label + '</a></li>';
} ).join( '' );
var el = document.createElement( 'div' );
el.className = 'bmc-home-card';
el.innerHTML =
'<div class="card-icon">' + card.icon + '</div>' +
'<div class="card-title">' + card.title + '</div>' +
'<ul class="card-links">' + li + '</ul>';
grid.appendChild( el );
} );
wrap.appendChild( grid );
// Contact strip
var contact = document.createElement( 'div' );
contact.id = 'bmc-contact-strip';
contact.innerHTML = [
'<div><div class="contact-label">Location</div>',
'<div class="contact-item">Room 68-322, MIT</div></div>',
'<div><div class="contact-label">Email</div>',
'<div class="contact-item"><a href="mailto:biomicro@mit.edu">biomicro@mit.edu</a></div></div>',
'<div><div class="contact-label">Phone</div>',
'<div class="contact-item">617-715-4533</div></div>',
'<div style="margin-left:auto">',
'<a href="https://mit-ki.ilabsolutions.com/sc/3381/ki-genomics-core-mit-biomicro-center?tab=about"',
' target="_blank"',
' style="background:' + TOKENS.gold + ';color:#000;font-weight:600;',
'font-size:0.82em;padding:9px 18px;border-radius:7px;text-decoration:none">',
'iLabs — Internal Submission ↗',
'</a></div>',
].join( '' );
wrap.appendChild( contact );
},
/* ---- 4h. PRICING PAGE TRANSFORM ---- */
transformPricing: function () {
var self = this;
var oldContent = document.getElementById( 'mw-content-text' );
if ( !oldContent ) return;
// Current tier state
var tier = 'mit';
var tierColors = { core: '#1a6b3c', mit: '#8b1a1a', nonmit: '#1a3b8b' };
var tierBadges = { core: 'CORE', mit: 'MIT', nonmit: 'EXT' };
var tierLabels = { core: 'Core Lab', mit: 'MIT', nonmit: 'Non-MIT' };
// Parse all wiki tables from the existing rendered HTML
// Each table = one pricing section
var sections = self.parsePricingTables( oldContent );
// Hide original content
oldContent.style.display = 'none';
var wrap = document.getElementById( 'bmc-page-wrap' );
if ( !wrap ) return;
// Controls row
var controls = document.createElement( 'div' );
controls.style.cssText = 'display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:16px;margin-bottom:8px;';
// Tier toggle
var toggle = document.createElement( 'div' );
toggle.id = 'bmc-pricing-tier-toggle';
[ 'core', 'mit', 'nonmit' ].forEach( function ( t ) {
var btn = document.createElement( 'button' );
btn.textContent = tierLabels[t];
btn.dataset.tier = t;
btn.className = t === tier ? 'active-' + t : '';
btn.addEventListener( 'click', function () {
tier = t;
toggle.querySelectorAll( 'button' ).forEach( function ( b ) { b.className = ''; } );
btn.className = 'active-' + t;
pill.style.background = tierColors[t] + '22';
pill.style.color = tierColors[t];
pill.style.borderColor = tierColors[t] + '44';
pillBadge.style.background = tierColors[t];
pillBadge.textContent = tierBadges[t];
pillLabel.textContent = 'Showing ' + tierLabels[t] + ' pricing';
self.updatePricingTier( t );
} );
toggle.appendChild( btn );
} );
// Search
var searchWrap = document.createElement( 'div' );
searchWrap.id = 'bmc-pricing-search-wrap';
searchWrap.innerHTML =
'<span id="bmc-pricing-search-icon">⌕</span>' +
'<input id="bmc-pricing-search" type="text" placeholder="Search services…">';
searchWrap.querySelector( 'input' ).addEventListener( 'input', function () {
self.filterPricingRows( this.value.toLowerCase() );
} );
controls.appendChild( toggle );
controls.appendChild( searchWrap );
wrap.appendChild( controls );
// Tier pill
var pill = document.createElement( 'div' );
pill.id = 'bmc-tier-pill';
pill.style.cssText = 'background:' + tierColors[tier] + '22;color:' + tierColors[tier] + ';border:1.5px solid ' + tierColors[tier] + '44;border-radius:6px;';
var pillBadge = document.createElement( 'span' );
pillBadge.style.cssText = 'background:' + tierColors[tier] + ';color:#fff;border-radius:3px;padding:1px 6px;font-size:0.82em;letter-spacing:0.08em;';
pillBadge.textContent = tierBadges[tier];
var pillLabel = document.createElement( 'span' );
pillLabel.textContent = 'Showing ' + tierLabels[tier] + ' pricing';
pill.appendChild( pillBadge );
pill.appendChild( pillLabel );
wrap.appendChild( pill );
// Render sections
var container = document.createElement( 'div' );
container.id = 'bmc-pricing-container';
wrap.appendChild( container );
sections.forEach( function ( section ) {
var card = document.createElement( 'div' );
card.className = 'bmc-price-section';
var header = document.createElement( 'div' );
header.className = 'bmc-price-section-header';
header.innerHTML = section.title + '<span class="chevron">▼</span>';
header.addEventListener( 'click', function () {
var body = card.querySelector( '.bmc-price-body' );
var collapsed = header.classList.toggle( 'collapsed' );
body.style.display = collapsed ? 'none' : '';
} );
var body = document.createElement( 'div' );
body.className = 'bmc-price-body';
section.rows.forEach( function ( row ) {
var rowEl = document.createElement( 'div' );
rowEl.className = 'bmc-price-row';
rowEl.dataset.rowName = row.name.toLowerCase();
var amt = row.prices[tier];
var amtClass = ( amt === null || amt === undefined || amt === '' )
? 'tier-na' : 'tier-' + tier;
var amtText = ( amt === null || amt === undefined || amt === '' )
? '—' : amt;
rowEl.innerHTML =
'<div>' +
'<div class="bmc-price-row-name">' + row.name + '</div>' +
( row.unit ? '<div class="bmc-price-row-unit">' + row.unit + '</div>' : '' ) +
( row.notes ? '<div class="bmc-price-row-note">' + row.notes + '</div>' : '' ) +
'</div>' +
'<div class="bmc-price-amount ' + amtClass + '" data-prices=\'' + JSON.stringify( row.prices ) + '\'>' +
amtText +
'</div>';
body.appendChild( rowEl );
} );
card.appendChild( header );
card.appendChild( body );
container.appendChild( card );
} );
// Footer note
var note = document.createElement( 'div' );
note.style.cssText = 'margin-top:24px;padding:14px 18px;background:#fff8e8;border-radius:8px;border:1px solid #e8d8a0;font-size:0.78em;color:#7a6a3a;line-height:1.6;';
note.innerHTML = '✦ = Minimal inventory maintained · †† = Through collaboration with nearby academic shared resources · Pricing as of January 2026. Questions? <a href="mailto:biomicro@mit.edu" style="color:#8b6914">biomicro@mit.edu</a>';
wrap.appendChild( note );
},
/* ---- 4i. PARSE PRICING TABLES FROM WIKI HTML ---- */
parsePricingTables: function ( container ) {
var sections = [];
// Walk h2/h3 + table pairs
var nodes = container.querySelectorAll( 'h2, h3, table' );
var currentTitle = 'Services';
var currentRows = [];
var flush = function () {
if ( currentRows.length > 0 ) {
sections.push( { title: currentTitle, rows: currentRows } );
currentRows = [];
}
};
nodes.forEach( function ( node ) {
if ( node.tagName === 'H2' || node.tagName === 'H3' ) {
flush();
// Clean up heading text (remove [edit] spans)
var clone = node.cloneNode( true );
clone.querySelectorAll( '.mw-editsection' ).forEach( function ( e ) { e.remove(); } );
currentTitle = clone.textContent.trim();
} else if ( node.tagName === 'TABLE' ) {
// Find header row to determine column order
var headers = [];
var headerRow = node.querySelector( 'tr' );
if ( headerRow ) {
headerRow.querySelectorAll( 'th, td' ).forEach( function ( cell ) {
headers.push( cell.textContent.trim().toLowerCase() );
} );
}
// Find price column indices
var coreIdx = -1, mitIdx = -1, nonmitIdx = -1;
var nameIdx = 0;
var unitIdx = -1, notesIdx = -1;
headers.forEach( function ( h, i ) {
if ( /core/.test( h ) ) coreIdx = i;
if ( /^mit$/.test( h ) ) mitIdx = i;
if ( /non.?mit/.test( h ) ) nonmitIdx = i;
if ( /unit/.test( h ) ) unitIdx = i;
if ( /note/.test( h ) ) notesIdx = i;
} );
// Parse data rows
var rows = node.querySelectorAll( 'tr' );
rows.forEach( function ( row, ri ) {
if ( ri === 0 ) return; // skip header
var cells = row.querySelectorAll( 'td, th' );
if ( cells.length < 2 ) return;
var getText = function ( idx ) {
if ( idx < 0 || idx >= cells.length ) return '';
return cells[idx].textContent.trim();
};
var name = getText( nameIdx );
if ( !name ) return;
var formatPrice = function ( idx ) {
var raw = getText( idx );
if ( !raw || raw === '' || /pricing in/i.test( raw ) ) return null;
// Already has $ sign in wiki? Return as-is, else prefix
return raw;
};
currentRows.push( {
name: name,
unit: unitIdx >= 0 ? getText( unitIdx ) : '',
notes: notesIdx >= 0 ? getText( notesIdx ) : '',
prices: {
core: formatPrice( coreIdx ),
mit: formatPrice( mitIdx ),
nonmit: formatPrice( nonmitIdx ),
}
} );
} );
}
} );
flush();
return sections;
},
/* ---- 4j. UPDATE PRICING TIER (live swap) ---- */
updatePricingTier: function ( tier ) {
document.querySelectorAll( '.bmc-price-amount' ).forEach( function ( el ) {
try {
var prices = JSON.parse( el.dataset.prices );
var amt = prices[tier];
var isNA = ( amt === null || amt === undefined || amt === '' );
el.textContent = isNA ? '—' : amt;
el.className = 'bmc-price-amount ' + ( isNA ? 'tier-na' : 'tier-' + tier );
} catch ( e ) {}
} );
},
/* ---- 4k. FILTER PRICING ROWS ---- */
filterPricingRows: function ( query ) {
document.querySelectorAll( '.bmc-price-row' ).forEach( function ( row ) {
var name = row.dataset.rowName || '';
row.style.display = name.includes( query ) ? '' : 'none';
} );
// Show/hide empty sections
document.querySelectorAll( '.bmc-price-section' ).forEach( function ( section ) {
var visible = section.querySelectorAll( '.bmc-price-row:not([style*="none"])' );
section.style.display = visible.length ? '' : 'none';
} );
},
}; // end BMC
window.BMC = BMC; // expose globally for debugging
/* ============================================================
5. KICK OFF — hook registered AFTER BMC is fully defined
============================================================ */
mw.hook( 'wikipage.content' ).add( function () {
BMC.init();
} );
// Fire immediately — multiple strategies
function tryInit() {
if ( document.getElementById( 'bmc-topnav' ) ) return;
if ( typeof mw !== 'undefined' && mw.config ) { BMC.init(); }
}
tryInit();
setTimeout( tryInit, 500 );
setTimeout( tryInit, 1500 );
if ( document.readyState !== 'loading' ) {
BMC.init();
}
}() );