<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://bmcwiki.mit.edu/index.php?action=history&amp;feed=atom&amp;title=User%3AChallee%2Fcommon.js</id>
	<title>User:Challee/common.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://bmcwiki.mit.edu/index.php?action=history&amp;feed=atom&amp;title=User%3AChallee%2Fcommon.js"/>
	<link rel="alternate" type="text/html" href="http://bmcwiki.mit.edu/index.php?title=User:Challee/common.js&amp;action=history"/>
	<updated>2026-07-21T18:27:45Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>http://bmcwiki.mit.edu/index.php?title=User:Challee/common.js&amp;diff=131749&amp;oldid=prev</id>
		<title>Asoberan: Created page with &quot;/**  * BioMicro Center Wiki Wrapper — Full DOM Replacement  *  * Strategy:  * 1. Extract wiki article content from MediaWiki&#039;s DOM  * 2. Inject the complete shell structure  * 3. Place article content inside the shell  * MediaWiki still runs underneath for editing, search, login, etc.  */  ( function () {   &#039;use strict&#039;;    /* ── Extract nav from MediaWiki&#039;s sidebar DOM ─────────────── */   function extractNavFromDom() {     var SKIP_I...&quot;</title>
		<link rel="alternate" type="text/html" href="http://bmcwiki.mit.edu/index.php?title=User:Challee/common.js&amp;diff=131749&amp;oldid=prev"/>
		<updated>2026-07-14T14:32:40Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;&lt;span class=&quot;autocomment&quot;&gt;*  * BioMicro Center Wiki Wrapper — Full DOM Replacement  *  * Strategy:  * 1. Extract wiki article content from MediaWiki&amp;#039;s DOM  * 2. Inject the complete shell structure  * 3. Place article content inside the shell  * MediaWiki still runs underneath for editing, search, login, etc.: &lt;/span&gt;  ( function () {   &amp;#039;use strict&amp;#039;;    &lt;span class=&quot;autocomment&quot;&gt;── Extract nav from MediaWiki&amp;#039;s sidebar DOM ───────────────: &lt;/span&gt;   function extractNavFromDom() {     var SKIP_I...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;/**&lt;br /&gt;
 * BioMicro Center Wiki Wrapper — Full DOM Replacement&lt;br /&gt;
 *&lt;br /&gt;
 * Strategy:&lt;br /&gt;
 * 1. Extract wiki article content from MediaWiki&amp;#039;s DOM&lt;br /&gt;
 * 2. Inject the complete shell structure&lt;br /&gt;
 * 3. Place article content inside the shell&lt;br /&gt;
 * MediaWiki still runs underneath for editing, search, login, etc.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
( function () {&lt;br /&gt;
  &amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
  /* ── Extract nav from MediaWiki&amp;#039;s sidebar DOM ─────────────── */&lt;br /&gt;
  function extractNavFromDom() {&lt;br /&gt;
    var SKIP_IDS = {&lt;br /&gt;
      &amp;#039;p-tb&amp;#039;: 1, &amp;#039;p-personal&amp;#039;: 1, &amp;#039;p-search&amp;#039;: 1,&lt;br /&gt;
      &amp;#039;p-logo&amp;#039;: 1, &amp;#039;p-cactions&amp;#039;: 1, &amp;#039;p-views&amp;#039;: 1,&lt;br /&gt;
      &amp;#039;p-lang&amp;#039;: 1, &amp;#039;p-namespaces&amp;#039;: 1&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    /* Scope to the sidebar container to exclude Vector 2022 header user-menu portlets&lt;br /&gt;
       (p-vector-user-menu-userpage, p-user-menu-logout, p-personal-sticky-header, etc.) */&lt;br /&gt;
    var navContainer = document.getElementById( &amp;#039;vector-main-menu&amp;#039; ) ||&lt;br /&gt;
                       document.getElementById( &amp;#039;mw-panel&amp;#039; )         ||&lt;br /&gt;
                       document.getElementById( &amp;#039;mw-navigation&amp;#039; )    ||&lt;br /&gt;
                       document.body;&lt;br /&gt;
&lt;br /&gt;
    var portlets = Array.prototype.slice.call(&lt;br /&gt;
      navContainer.querySelectorAll( &amp;#039;[id^=&amp;quot;p-&amp;quot;]&amp;#039; )&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    var nav = [];&lt;br /&gt;
    var currentUrl = window.location.href.split( &amp;#039;?&amp;#039; )[0];&lt;br /&gt;
&lt;br /&gt;
    portlets.forEach( function ( portlet ) {&lt;br /&gt;
      if ( SKIP_IDS[ portlet.id ] ) return;&lt;br /&gt;
&lt;br /&gt;
      var links = Array.prototype.slice.call(&lt;br /&gt;
        portlet.querySelectorAll( &amp;#039;li a&amp;#039; )&lt;br /&gt;
      ).map( function ( a ) {&lt;br /&gt;
        return { label: a.textContent.trim(), href: a.href };&lt;br /&gt;
      } ).filter( function ( item ) { return item.label &amp;amp;&amp;amp; item.href; } );&lt;br /&gt;
&lt;br /&gt;
      if ( !links.length ) return;&lt;br /&gt;
&lt;br /&gt;
      var headingEl = portlet.querySelector( &amp;#039;[id$=&amp;quot;-label&amp;quot;], h3, h2&amp;#039; );&lt;br /&gt;
      var sectionLabel = headingEl ? headingEl.textContent.trim() : &amp;#039;&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
      var isActive = links.some( function ( link ) {&lt;br /&gt;
        return link.href.split( &amp;#039;?&amp;#039; )[0] === currentUrl;&lt;br /&gt;
      } );&lt;br /&gt;
&lt;br /&gt;
      if ( links.length === 1 ) {&lt;br /&gt;
        nav.push( {&lt;br /&gt;
          label:    links[0].label,&lt;br /&gt;
          href:     links[0].href,&lt;br /&gt;
          isActive: links[0].href.split( &amp;#039;?&amp;#039; )[0] === currentUrl&lt;br /&gt;
        } );&lt;br /&gt;
      } else {&lt;br /&gt;
        nav.push( {&lt;br /&gt;
          label:    sectionLabel || links[0].label,&lt;br /&gt;
          href:     links[0].href,&lt;br /&gt;
          items:    links,&lt;br /&gt;
          isActive: isActive&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    return nav;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Build topbar HTML ─────────────────────────────────────── */&lt;br /&gt;
  function buildTopbar() {&lt;br /&gt;
    var d = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    d.className = &amp;#039;bmc-topbar&amp;#039;;&lt;br /&gt;
    d.innerHTML =&lt;br /&gt;
      &amp;#039;&amp;lt;div class=&amp;quot;inner&amp;quot;&amp;gt;&amp;#039; +&lt;br /&gt;
        &amp;#039;&amp;lt;a href=&amp;quot;mailto:biomicro@mit.edu&amp;quot;&amp;gt;biomicro@mit.edu&amp;lt;/a&amp;gt;&amp;#039; +&lt;br /&gt;
        &amp;#039;&amp;lt;span&amp;gt;|&amp;lt;/span&amp;gt;&amp;#039; +&lt;br /&gt;
        &amp;#039;617-715-4533&amp;#039; +&lt;br /&gt;
        &amp;#039;&amp;lt;span&amp;gt;|&amp;lt;/span&amp;gt;&amp;#039; +&lt;br /&gt;
        &amp;#039;Building 68-322&amp;#039; +&lt;br /&gt;
      &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;;&lt;br /&gt;
    return d;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Build nav dropdown item ───────────────────────────────── */&lt;br /&gt;
  function buildDropdownMenu( items ) {&lt;br /&gt;
    var menu = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    menu.className = &amp;#039;bmc-dropdown-menu&amp;#039;;&lt;br /&gt;
    items.forEach( function ( item ) {&lt;br /&gt;
      if ( item.divider ) {&lt;br /&gt;
        var div = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
        div.className = &amp;#039;bmc-divider&amp;#039;;&lt;br /&gt;
        menu.appendChild( div );&lt;br /&gt;
      } else if ( item.groupLabel ) {&lt;br /&gt;
        var gl = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
        gl.className = &amp;#039;bmc-group-label&amp;#039;;&lt;br /&gt;
        gl.textContent = item.groupLabel;&lt;br /&gt;
        menu.appendChild( gl );&lt;br /&gt;
      } else {&lt;br /&gt;
        var a = document.createElement( &amp;#039;a&amp;#039; );&lt;br /&gt;
        a.href = item.page ? mw.util.getUrl( item.page ) : item.href;&lt;br /&gt;
        a.textContent = item.label;&lt;br /&gt;
        if ( item.ext ) a.target = &amp;#039;_blank&amp;#039;;&lt;br /&gt;
        menu.appendChild( a );&lt;br /&gt;
      }&lt;br /&gt;
    } );&lt;br /&gt;
    return menu;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Build full header ─────────────────────────────────────── */&lt;br /&gt;
  function buildHeader( currentPage, nav ) {&lt;br /&gt;
    /* Logo */&lt;br /&gt;
    var logo = document.createElement( &amp;#039;a&amp;#039; );&lt;br /&gt;
    logo.href = mw.util.getUrl( &amp;#039;BioMicroCenter&amp;#039; );&lt;br /&gt;
    logo.className = &amp;#039;bmc-logo&amp;#039;;&lt;br /&gt;
    logo.innerHTML =&lt;br /&gt;
      &amp;#039;&amp;lt;img class=&amp;quot;bmc-logo-img&amp;quot; src=&amp;quot;https://bmcwiki.mit.edu/images/c/c9/Logo.png&amp;quot; alt=&amp;quot;MIT BioMicro Center&amp;quot; /&amp;gt;&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    /* Nav */&lt;br /&gt;
    var ul = document.createElement( &amp;#039;ul&amp;#039; );&lt;br /&gt;
    nav.forEach( function ( item ) {&lt;br /&gt;
      var li = document.createElement( &amp;#039;li&amp;#039; );&lt;br /&gt;
      if ( item.isActive ) li.classList.add( &amp;#039;bmc-active&amp;#039; );&lt;br /&gt;
      if ( item.items ) li.classList.add( &amp;#039;bmc-dropdown&amp;#039; );&lt;br /&gt;
&lt;br /&gt;
      var a = document.createElement( &amp;#039;a&amp;#039; );&lt;br /&gt;
      a.href = item.page ? mw.util.getUrl( item.page ) : ( item.href || &amp;#039;#&amp;#039; );&lt;br /&gt;
      a.textContent = item.label;&lt;br /&gt;
      li.appendChild( a );&lt;br /&gt;
&lt;br /&gt;
      if ( item.items ) {&lt;br /&gt;
        li.appendChild( buildDropdownMenu( item.items ) );&lt;br /&gt;
      }&lt;br /&gt;
      ul.appendChild( li );&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    var navEl = document.createElement( &amp;#039;nav&amp;#039; );&lt;br /&gt;
    navEl.className = &amp;#039;bmc-nav&amp;#039;;&lt;br /&gt;
    navEl.appendChild( ul );&lt;br /&gt;
&lt;br /&gt;
    /* Search */&lt;br /&gt;
    var searchForm = document.createElement( &amp;#039;form&amp;#039; );&lt;br /&gt;
    searchForm.className = &amp;#039;bmc-search-form&amp;#039;;&lt;br /&gt;
    searchForm.method = &amp;#039;get&amp;#039;;&lt;br /&gt;
    searchForm.action = &amp;#039;/index.php&amp;#039;;&lt;br /&gt;
    searchForm.innerHTML =&lt;br /&gt;
      &amp;#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:Search&amp;quot;&amp;gt;&amp;#039; +&lt;br /&gt;
      &amp;#039;&amp;lt;input type=&amp;quot;search&amp;quot; name=&amp;quot;search&amp;quot; placeholder=&amp;quot;Search wiki…&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&amp;#039; +&lt;br /&gt;
      &amp;#039;&amp;lt;button type=&amp;quot;submit&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&amp;lt;svg xmlns=&amp;quot;http://www.w3.org/2000/svg&amp;quot; width=&amp;quot;14&amp;quot; height=&amp;quot;14&amp;quot; viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;none&amp;quot; stroke=&amp;quot;currentColor&amp;quot; stroke-width=&amp;quot;2.5&amp;quot; stroke-linecap=&amp;quot;round&amp;quot; stroke-linejoin=&amp;quot;round&amp;quot;&amp;gt;&amp;lt;circle cx=&amp;quot;11&amp;quot; cy=&amp;quot;11&amp;quot; r=&amp;quot;8&amp;quot;/&amp;gt;&amp;lt;line x1=&amp;quot;21&amp;quot; y1=&amp;quot;21&amp;quot; x2=&amp;quot;16.65&amp;quot; y2=&amp;quot;16.65&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/button&amp;gt;&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    /* User info — shown only when logged in */&lt;br /&gt;
    var userName = mw.config.get( &amp;#039;wgUserName&amp;#039; );&lt;br /&gt;
    var userEl = null;&lt;br /&gt;
    if ( userName ) {&lt;br /&gt;
      userEl = document.createElement( &amp;#039;span&amp;#039; );&lt;br /&gt;
      userEl.className = &amp;#039;bmc-user-info&amp;#039;;&lt;br /&gt;
      userEl.textContent = userName;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var right = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    right.className = &amp;#039;bmc-header-right&amp;#039;;&lt;br /&gt;
    right.appendChild( navEl );&lt;br /&gt;
    right.appendChild( searchForm );&lt;br /&gt;
    var editBar = buildEditBar( currentPage );&lt;br /&gt;
    if ( editBar ) { right.appendChild( editBar ); }&lt;br /&gt;
    if ( userEl ) { right.appendChild( userEl ); }&lt;br /&gt;
&lt;br /&gt;
    /* Inner wrapper */&lt;br /&gt;
    var inner = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    inner.className = &amp;#039;bmc-header-inner&amp;#039;;&lt;br /&gt;
    inner.appendChild( logo );&lt;br /&gt;
    inner.appendChild( right );&lt;br /&gt;
&lt;br /&gt;
    var header = document.createElement( &amp;#039;header&amp;#039; );&lt;br /&gt;
    header.className = &amp;#039;bmc-header&amp;#039;;&lt;br /&gt;
    header.appendChild( inner );&lt;br /&gt;
    return header;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Build page hero ───────────────────────────────────────── */&lt;br /&gt;
  function buildHero( titleText ) {&lt;br /&gt;
    var newTitle = titleText.replace(/^BioMicroCenter:/, &amp;quot;&amp;quot;);&lt;br /&gt;
    var crumb = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    crumb.className = &amp;#039;bmc-breadcrumb&amp;#039;;&lt;br /&gt;
    crumb.innerHTML =&lt;br /&gt;
      &amp;#039;&amp;lt;a href=&amp;quot;&amp;#039; + mw.util.getUrl( &amp;#039;BioMicroCenter&amp;#039; ) + &amp;#039;&amp;quot;&amp;gt;Home&amp;lt;/a&amp;gt;&amp;#039; +&lt;br /&gt;
      &amp;#039;&amp;lt;span class=&amp;quot;sep&amp;quot;&amp;gt;›&amp;lt;/span&amp;gt;&amp;#039; +&lt;br /&gt;
      document.createTextNode( newTitle ).textContent; // plain text, no XSS&lt;br /&gt;
&lt;br /&gt;
    var h1 = document.createElement( &amp;#039;h1&amp;#039; );&lt;br /&gt;
    h1.textContent = newTitle;&lt;br /&gt;
&lt;br /&gt;
    var inner = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    inner.className = &amp;#039;inner&amp;#039;;&lt;br /&gt;
    inner.appendChild( crumb );&lt;br /&gt;
    inner.appendChild( h1 );&lt;br /&gt;
&lt;br /&gt;
    var hero = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    hero.className = &amp;#039;bmc-page-hero&amp;#039;;&lt;br /&gt;
    hero.appendChild( inner );&lt;br /&gt;
    return hero;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Build sidebar TOC from MediaWiki #toc or heading scan ─── */&lt;br /&gt;
  function buildSidebarToc( mwToc, contentEl ) {&lt;br /&gt;
    var toc = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    toc.className = &amp;#039;bmc-toc&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    var h3 = document.createElement( &amp;#039;h3&amp;#039; );&lt;br /&gt;
    h3.textContent = &amp;#039;On this page&amp;#039;;&lt;br /&gt;
    toc.appendChild( h3 );&lt;br /&gt;
&lt;br /&gt;
    var list;&lt;br /&gt;
&lt;br /&gt;
    if ( mwToc ) {&lt;br /&gt;
      /* Clone only the list from the MediaWiki TOC */&lt;br /&gt;
      var mwList = mwToc.querySelector( &amp;#039;ul&amp;#039; );&lt;br /&gt;
      if ( mwList ) {&lt;br /&gt;
        list = mwList.cloneNode( true );&lt;br /&gt;
        /* Mark sub-items */&lt;br /&gt;
        list.querySelectorAll( &amp;#039;li li&amp;#039; ).forEach( function ( li ) {&lt;br /&gt;
          li.classList.add( &amp;#039;bmc-sub&amp;#039; );&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
    } else if ( contentEl ) {&lt;br /&gt;
      /* Fall back to scanning h2/h3 headings directly */&lt;br /&gt;
      var headings = Array.prototype.slice.call(&lt;br /&gt;
        contentEl.querySelectorAll( &amp;#039;h2, h3&amp;#039; )&lt;br /&gt;
      );&lt;br /&gt;
      if ( headings.length &amp;gt; 0 ) {&lt;br /&gt;
        list = document.createElement( &amp;#039;ul&amp;#039; );&lt;br /&gt;
        headings.forEach( function ( heading ) {&lt;br /&gt;
          var anchor = heading.querySelector( &amp;#039;.mw-headline&amp;#039; );&lt;br /&gt;
          if ( !anchor || !anchor.id ) return;&lt;br /&gt;
          var li = document.createElement( &amp;#039;li&amp;#039; );&lt;br /&gt;
          if ( heading.tagName === &amp;#039;H3&amp;#039; ) li.classList.add( &amp;#039;bmc-sub&amp;#039; );&lt;br /&gt;
          var a = document.createElement( &amp;#039;a&amp;#039; );&lt;br /&gt;
          a.href = &amp;#039;#&amp;#039; + anchor.id;&lt;br /&gt;
          a.textContent = anchor.textContent.trim();&lt;br /&gt;
          li.appendChild( a );&lt;br /&gt;
          list.appendChild( li );&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !list || list.childNodes.length === 0 ) return null;&lt;br /&gt;
&lt;br /&gt;
    toc.appendChild( list );&lt;br /&gt;
&lt;br /&gt;
    var aside = document.createElement( &amp;#039;aside&amp;#039; );&lt;br /&gt;
    aside.className = &amp;#039;bmc-sidebar&amp;#039;;&lt;br /&gt;
    aside.appendChild( toc );&lt;br /&gt;
    return aside;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* ── Smooth scroll for anchor links ───────────────────────── */&lt;br /&gt;
  function initSmoothScroll( container ) {&lt;br /&gt;
    container.addEventListener( &amp;#039;click&amp;#039;, function ( e ) {&lt;br /&gt;
      var a = e.target.closest( &amp;#039;a[href^=&amp;quot;#&amp;quot;]&amp;#039; );&lt;br /&gt;
      if ( !a ) return;&lt;br /&gt;
      var id = decodeURIComponent( a.getAttribute( &amp;#039;href&amp;#039; ).slice( 1 ) );&lt;br /&gt;
      var target = document.getElementById( id );&lt;br /&gt;
      if ( target ) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        target.scrollIntoView( { behavior: &amp;#039;smooth&amp;#039;, block: &amp;#039;start&amp;#039; } );&lt;br /&gt;
        history.pushState( null, &amp;#039;&amp;#039;, &amp;#039;#&amp;#039; + id );&lt;br /&gt;
      }&lt;br /&gt;
    } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Add a separator line at the END of each rowspan group, on every&lt;br /&gt;
       cell whose own span also ends at that row. This marks the boundary&lt;br /&gt;
       between e.g. &amp;quot;5M&amp;quot; and &amp;quot;25M&amp;quot; without ever drawing a line through&lt;br /&gt;
       the inside of a single rowspan cell.                              */&lt;br /&gt;
  function fixRowspanSeparators( container ) {&lt;br /&gt;
    container.querySelectorAll( &amp;#039;.wikitable&amp;#039; ).forEach( function ( table ) {&lt;br /&gt;
      var tbody = table.querySelector( &amp;#039;tbody&amp;#039; ) || table;&lt;br /&gt;
      var rows = Array.from( tbody.querySelectorAll( &amp;#039;tr&amp;#039; ) );&lt;br /&gt;
      if ( !rows.length ) return;&lt;br /&gt;
&lt;br /&gt;
      /* Build a grid: for each row index, find which row index each&lt;br /&gt;
         rowspan group ENDS at, per starting cell. Then for every row&lt;br /&gt;
         that is the last row of at least one rowspan group, draw a&lt;br /&gt;
         border-bottom on every td/th that is present in that row&lt;br /&gt;
         (cells whose own rowspan continues past this row are skipped,&lt;br /&gt;
         since the line would cut through them — instead they get their&lt;br /&gt;
         own end-of-group border drawn at their own last row). */&lt;br /&gt;
      rows.forEach( function ( row, rowIdx ) {&lt;br /&gt;
        row.querySelectorAll( &amp;#039;td[rowspan], th[rowspan]&amp;#039; ).forEach( function ( cell ) {&lt;br /&gt;
          var span = parseInt( cell.getAttribute( &amp;#039;rowspan&amp;#039; ), 10 ) || 1;&lt;br /&gt;
          var endRowIdx = rowIdx + span - 1;&lt;br /&gt;
          var endRow = rows[ endRowIdx ];&lt;br /&gt;
          if ( !endRow ) return;&lt;br /&gt;
&lt;br /&gt;
          /* Draw the boundary on every cell present in the END row,&lt;br /&gt;
             except cells whose OWN rowspan extends beyond this row&lt;br /&gt;
             (those get their boundary drawn at their own end row). */&lt;br /&gt;
          endRow.querySelectorAll( &amp;#039;td, th&amp;#039; ).forEach( function ( endCell ) {&lt;br /&gt;
            var ownSpan = parseInt( endCell.getAttribute( &amp;#039;rowspan&amp;#039; ), 10 ) || 1;&lt;br /&gt;
            if ( ownSpan &amp;gt; 1 ) return; /* this cell&amp;#039;s own group hasn&amp;#039;t ended yet */&lt;br /&gt;
            endCell.classList.add( &amp;#039;bmc-rowspan-boundary&amp;#039; );&lt;br /&gt;
          } );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
    } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* ── Split &amp;lt;br&amp;gt;-separated items inside a &amp;lt;ul&amp;gt; into separate &amp;lt;li&amp;gt;s ── */&lt;br /&gt;
  function splitBrBullets( ul ) {&lt;br /&gt;
    Array.prototype.slice.call( ul.querySelectorAll( &amp;#039;li&amp;#039; ) ).forEach( function ( li ) {&lt;br /&gt;
      if ( !li.querySelector( &amp;#039;br&amp;#039; ) ) return;&lt;br /&gt;
      var nodes = Array.prototype.slice.call( li.childNodes );&lt;br /&gt;
      var segments = [[]];&lt;br /&gt;
      nodes.forEach( function ( node ) {&lt;br /&gt;
        if ( node.nodeName === &amp;#039;BR&amp;#039; ) {&lt;br /&gt;
          segments.push( [] );&lt;br /&gt;
        } else {&lt;br /&gt;
          segments[ segments.length - 1 ].push( node );&lt;br /&gt;
        }&lt;br /&gt;
      } );&lt;br /&gt;
      /* Drop empty segments (only whitespace text nodes) */&lt;br /&gt;
      segments = segments.filter( function ( seg ) {&lt;br /&gt;
        return seg.some( function ( n ) {&lt;br /&gt;
          return n.nodeType === 1 ||&lt;br /&gt;
            ( n.nodeType === 3 &amp;amp;&amp;amp; n.textContent.trim() !== &amp;#039;&amp;#039; );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
      if ( segments.length &amp;lt;= 1 ) return;&lt;br /&gt;
      var parent = li.parentNode;&lt;br /&gt;
      var ref    = li.nextSibling;&lt;br /&gt;
      segments.forEach( function ( seg ) {&lt;br /&gt;
        var newLi = document.createElement( &amp;#039;li&amp;#039; );&lt;br /&gt;
        seg.forEach( function ( n ) { newLi.appendChild( n ); } );&lt;br /&gt;
        parent.insertBefore( newLi, ref );&lt;br /&gt;
      } );&lt;br /&gt;
      parent.removeChild( li );&lt;br /&gt;
    } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Edit bar for logged-in users ─────────────────────────── */&lt;br /&gt;
  function buildEditBar( currentPage ) {&lt;br /&gt;
    if ( !mw.config.get( &amp;#039;wgUserId&amp;#039; ) ) return null;&lt;br /&gt;
    var bar = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    bar.className = &amp;#039;bmc-edit-bar&amp;#039;;&lt;br /&gt;
    var editHref = mw.util.getUrl( currentPage, { action: &amp;#039;edit&amp;#039; } );&lt;br /&gt;
    var histHref = mw.util.getUrl( currentPage, { action: &amp;#039;history&amp;#039; } );&lt;br /&gt;
    var talkHref = mw.util.getUrl( &amp;#039;Talk:&amp;#039; + currentPage );&lt;br /&gt;
    bar.innerHTML =&lt;br /&gt;
      &amp;#039;&amp;lt;a href=&amp;quot;&amp;#039; + editHref + &amp;#039;&amp;quot;&amp;gt;Edit&amp;lt;/a&amp;gt;&amp;#039; +&lt;br /&gt;
      &amp;#039;&amp;lt;a href=&amp;quot;&amp;#039; + histHref + &amp;#039;&amp;quot;&amp;gt;History&amp;lt;/a&amp;gt;&amp;#039; +&lt;br /&gt;
      &amp;#039;&amp;lt;a href=&amp;quot;&amp;#039; + talkHref + &amp;#039;&amp;quot;&amp;gt;Talk&amp;lt;/a&amp;gt;&amp;#039;;&lt;br /&gt;
    return bar;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Main: build and inject the shell ─────────────────────── */&lt;br /&gt;
  function buildShell( nav ) {&lt;br /&gt;
    var currentPage = mw.config.get( &amp;#039;wgPageName&amp;#039; ) || &amp;#039;&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    /* 1. Extract wiki content BEFORE any DOM manipulation */&lt;br /&gt;
    var mwContentText = document.getElementById( &amp;#039;mw-content-text&amp;#039; );&lt;br /&gt;
    if ( !mwContentText ) return; /* not a content page, bail */&lt;br /&gt;
&lt;br /&gt;
    var contentClone = mwContentText.cloneNode( true );&lt;br /&gt;
&lt;br /&gt;
    /* 2. Extract page title */&lt;br /&gt;
    var titleEl = document.getElementById( &amp;#039;firstHeading&amp;#039; ) ||&lt;br /&gt;
      document.querySelector( &amp;#039;.mw-first-heading&amp;#039; );&lt;br /&gt;
    var titleText = titleEl&lt;br /&gt;
      ? titleEl.textContent.trim()&lt;br /&gt;
      : ( mw.config.get( &amp;#039;wgTitle&amp;#039; ) || &amp;#039;BioMicro Center&amp;#039; );&lt;br /&gt;
    var newTitle = titleText.replace(/^BioMicroCenter:/, &amp;quot;&amp;quot;);&lt;br /&gt;
    document.getElementsByTagName(&amp;quot;title&amp;quot;)[0].textContent = newTitle + &amp;quot; - BioMicro Center&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    /* 3. Extract MediaWiki TOC from the cloned content */&lt;br /&gt;
    var mwToc = contentClone.querySelector( &amp;#039;#toc, .toc&amp;#039; );&lt;br /&gt;
    if ( mwToc ) mwToc.parentNode.removeChild( mwToc ); /* remove from content body */&lt;br /&gt;
&lt;br /&gt;
    /* 3b. Hide the BMC logo/header sticker by exact image filename.&lt;br /&gt;
           The sticker is BMC_Header_2020_3.png (from the {{BioMicroCenter}} template)&lt;br /&gt;
           and bmc_logo_square.png. Target the nearest meaningful container so the&lt;br /&gt;
           surrounding content (nav links, table structure) is preserved. */&lt;br /&gt;
    contentClone.querySelectorAll( &amp;#039;img&amp;#039; ).forEach( function ( img ) {&lt;br /&gt;
      var src = img.getAttribute( &amp;#039;src&amp;#039; ) || &amp;#039;&amp;#039;;&lt;br /&gt;
      if ( /BMC_Header|bmc_logo_square|Bmc_logo|\/Logo\.png/i.test( src ) ) {&lt;br /&gt;
        var container = img.closest( &amp;#039;.thumb&amp;#039; ) || img.closest( &amp;#039;figure&amp;#039; ) || img.closest( &amp;#039;td&amp;#039; );&lt;br /&gt;
        if ( container ) {&lt;br /&gt;
          container.style.display = &amp;#039;none&amp;#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
          img.style.display = &amp;#039;none&amp;#039;;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    /* 4. Build shell elements */&lt;br /&gt;
    var topbar = buildTopbar();&lt;br /&gt;
    var header = buildHeader( currentPage, nav );&lt;br /&gt;
    var hero   = buildHero( titleText );&lt;br /&gt;
&lt;br /&gt;
    /* Page layout grid */&lt;br /&gt;
    var layout = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    layout.className = &amp;#039;bmc-page-layout&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    /* Sidebar — skip on the home page */&lt;br /&gt;
    var isHome = ( currentPage === &amp;#039;BioMicroCenter&amp;#039; ||&lt;br /&gt;
                   currentPage === &amp;#039;Main_Page&amp;#039; );&lt;br /&gt;
    var sidebar = isHome ? null : buildSidebarToc( mwToc, contentClone );&lt;br /&gt;
    if ( sidebar ) {&lt;br /&gt;
      layout.appendChild( sidebar );&lt;br /&gt;
    } else {&lt;br /&gt;
      /* No TOC — collapse to single-column via inline style */&lt;br /&gt;
      layout.style.gridTemplateColumns = &amp;#039;1fr&amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Main content area */&lt;br /&gt;
    var main = document.createElement( &amp;#039;main&amp;#039; );&lt;br /&gt;
    main.className = &amp;#039;bmc-content&amp;#039;;&lt;br /&gt;
    main.appendChild( contentClone );&lt;br /&gt;
    layout.appendChild( main );&lt;br /&gt;
&lt;br /&gt;
    /* 5. Build wrapper */&lt;br /&gt;
    var wrapper = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    wrapper.id = &amp;#039;bmc-wrapper&amp;#039;;&lt;br /&gt;
    wrapper.appendChild( topbar );&lt;br /&gt;
    wrapper.appendChild( header );&lt;br /&gt;
    wrapper.appendChild( hero );&lt;br /&gt;
    wrapper.appendChild( layout );&lt;br /&gt;
&lt;br /&gt;
    var templateFooter = contentClone.querySelector( &amp;#039;#footer&amp;#039; );&lt;br /&gt;
    if ( templateFooter ) {&lt;br /&gt;
      templateFooter.remove();&lt;br /&gt;
      wrapper.appendChild( templateFooter );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* 6. Inject into body (prepend so it appears first) */&lt;br /&gt;
    document.body.insertBefore( wrapper, document.body.firstChild );&lt;br /&gt;
&lt;br /&gt;
    /* 7. Add class immediately so the CSS rule kicks in and hides&lt;br /&gt;
          all original MW elements (works for any skin version) */&lt;br /&gt;
    document.body.classList.add( &amp;#039;bmc-active&amp;#039; );&lt;br /&gt;
    if ( isHome ) {&lt;br /&gt;
      document.body.classList.add( &amp;#039;bmc-home&amp;#039; );&lt;br /&gt;
&lt;br /&gt;
      /* Remove page title from hero — logo already identifies the site */&lt;br /&gt;
      var heroH1 = hero.querySelector( &amp;#039;h1&amp;#039; );&lt;br /&gt;
      if ( heroH1 ) { heroH1.remove(); }&lt;br /&gt;
      var heroCrumb = hero.querySelector( &amp;#039;.bmc-breadcrumb&amp;#039; );&lt;br /&gt;
      if ( heroCrumb ) { heroCrumb.remove(); }&lt;br /&gt;
&lt;br /&gt;
      var parserOutput = contentClone.querySelector( &amp;#039;.mw-parser-output&amp;#039; ) || contentClone;&lt;br /&gt;
&lt;br /&gt;
      /* 7a. Remove the &amp;quot;Welcome to the MIT BioMicro Center!&amp;quot; tagline */&lt;br /&gt;
      var welcomeEl = null;&lt;br /&gt;
      Array.prototype.forEach.call( parserOutput.children, function ( el ) {&lt;br /&gt;
        if ( !welcomeEl &amp;amp;&amp;amp; /Welcome to the MIT BioMicro Center/i.test( el.textContent ) ) {&lt;br /&gt;
          welcomeEl = el;&lt;br /&gt;
        }&lt;br /&gt;
      } );&lt;br /&gt;
      if ( welcomeEl ) { welcomeEl.parentNode.removeChild( welcomeEl ); }&lt;br /&gt;
&lt;br /&gt;
      /* 7b. The first table on the page is the quick-links list (News,&lt;br /&gt;
             Pricing, Sample Submission, etc.) — pull its links up into&lt;br /&gt;
             the hero bar as pills so they don&amp;#039;t eat vertical space, and&lt;br /&gt;
             remove the now-empty table. */&lt;br /&gt;
      var navLinksTable = parserOutput.querySelector( &amp;#039;table&amp;#039; );&lt;br /&gt;
      if ( navLinksTable ) {&lt;br /&gt;
        var heroLinksBar = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
        heroLinksBar.className = &amp;#039;bmc-hero-links&amp;#039;;&lt;br /&gt;
        var seenHref = {};&lt;br /&gt;
        navLinksTable.querySelectorAll( &amp;#039;a&amp;#039; ).forEach( function ( a ) {&lt;br /&gt;
          var label = a.textContent.trim();&lt;br /&gt;
          var href  = a.getAttribute( &amp;#039;href&amp;#039; );&lt;br /&gt;
          if ( !label || !href || seenHref[ href ] ) return;&lt;br /&gt;
          seenHref[ href ] = true;&lt;br /&gt;
          var pill = document.createElement( &amp;#039;a&amp;#039; );&lt;br /&gt;
          pill.className = &amp;#039;bmc-hero-link&amp;#039;;&lt;br /&gt;
          pill.setAttribute( &amp;#039;href&amp;#039;, href );&lt;br /&gt;
          pill.textContent = label;&lt;br /&gt;
          heroLinksBar.appendChild( pill );&lt;br /&gt;
        } );&lt;br /&gt;
        if ( heroLinksBar.children.length ) {&lt;br /&gt;
          hero.querySelector( &amp;#039;.inner&amp;#039; ).appendChild( heroLinksBar );&lt;br /&gt;
        }&lt;br /&gt;
        navLinksTable.parentNode.removeChild( navLinksTable );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      /* 7c. The remaining table is one single &amp;quot;services&amp;quot; mega-table (5 rows,&lt;br /&gt;
             3 columns) where every cell — including the &amp;quot;Assisted Services&amp;quot; /&lt;br /&gt;
             &amp;quot;Walkup Services&amp;quot; / &amp;quot;Informatics Services&amp;quot; headers — wraps its&lt;br /&gt;
             content in its own nested one-cell table, purely for old-school&lt;br /&gt;
             formatting. Left as-is, our .wikitable styling was being applied&lt;br /&gt;
             to those nested tables too, double-boxing every cell. Unwrap them&lt;br /&gt;
             first, then style the table as a whole. */&lt;br /&gt;
      var servicesTable = parserOutput.querySelector( &amp;#039;table&amp;#039; );&lt;br /&gt;
      if ( servicesTable ) {&lt;br /&gt;
        Array.prototype.slice.call( servicesTable.querySelectorAll( &amp;#039;table&amp;#039; ) ).forEach(&lt;br /&gt;
          function ( inner ) {&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            Array.prototype.forEach.call( inner.querySelectorAll( &amp;#039;td&amp;#039; ), function ( td ) {&lt;br /&gt;
              while ( td.firstChild ) { frag.appendChild( td.firstChild ); }&lt;br /&gt;
            } );&lt;br /&gt;
            inner.parentNode.replaceChild( frag, inner );&lt;br /&gt;
          }&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        servicesTable.classList.add( &amp;#039;wikitable&amp;#039;, &amp;#039;bmc-home-table&amp;#039; );&lt;br /&gt;
&lt;br /&gt;
        /* 7d. A row whose cells are ALL just a heading with no &amp;lt;ul&amp;gt; of&lt;br /&gt;
               items is a group-header row (&amp;quot;Assisted Services&amp;quot; / &amp;quot;Walkup&lt;br /&gt;
               Services&amp;quot;, &amp;quot;Informatics/Bioinformatics Services&amp;quot;) — style it&lt;br /&gt;
               as a colored bar. Real content rows (heading + &amp;lt;ul&amp;gt;) get&lt;br /&gt;
               normal styling. Because this uses the table&amp;#039;s own&lt;br /&gt;
               &amp;lt;tr&amp;gt;/&amp;lt;td&amp;gt;/colspan, the bar aligns exactly with the columns&lt;br /&gt;
               below it — no guesswork.&lt;br /&gt;
&lt;br /&gt;
               Color is chosen by matching the cell&amp;#039;s own label text rather&lt;br /&gt;
               than its position, so &amp;quot;Walkup&amp;quot; / &amp;quot;Assisted&amp;quot; / &amp;quot;Bioinformatics&amp;quot;&lt;br /&gt;
               each get their own distinct color regardless of column order.&lt;br /&gt;
               Any header text that doesn&amp;#039;t match one of these falls back to&lt;br /&gt;
               the neutral wide style, so new/renamed categories don&amp;#039;t break. */&lt;br /&gt;
        function headerColorClass( label ) {&lt;br /&gt;
          var t = label.toLowerCase();&lt;br /&gt;
          if ( t.indexOf( &amp;#039;walkup&amp;#039; ) !== -1 || t.indexOf( &amp;#039;walk-up&amp;#039; ) !== -1 ) {&lt;br /&gt;
            return &amp;#039;bmc-home-header-walkup&amp;#039;;&lt;br /&gt;
          }&lt;br /&gt;
          if ( t.indexOf( &amp;#039;bioinformatics&amp;#039; ) !== -1 || t.indexOf( &amp;#039;informatics&amp;#039; ) !== -1 ) {&lt;br /&gt;
            return &amp;#039;bmc-home-header-bioinformatics&amp;#039;;&lt;br /&gt;
          }&lt;br /&gt;
          if ( t.indexOf( &amp;#039;assisted&amp;#039; ) !== -1 ) {&lt;br /&gt;
            return &amp;#039;bmc-home-header-assisted&amp;#039;;&lt;br /&gt;
          }&lt;br /&gt;
          return &amp;#039;bmc-home-header-wide&amp;#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        Array.prototype.forEach.call( servicesTable.querySelectorAll( &amp;#039;tr&amp;#039; ), function ( tr ) {&lt;br /&gt;
          var cells = Array.prototype.slice.call( tr.children ).filter( function ( c ) {&lt;br /&gt;
            return c.tagName === &amp;#039;TD&amp;#039; || c.tagName === &amp;#039;TH&amp;#039;;&lt;br /&gt;
          } );&lt;br /&gt;
          if ( !cells.length ) return;&lt;br /&gt;
          var isHeaderRow = cells.every( function ( td ) { return !td.querySelector( &amp;#039;ul&amp;#039; ); } );&lt;br /&gt;
          if ( isHeaderRow ) {&lt;br /&gt;
            tr.classList.add( &amp;#039;bmc-home-header-row&amp;#039; );&lt;br /&gt;
            cells.forEach( function ( td ) {&lt;br /&gt;
              td.classList.add( &amp;#039;bmc-home-header-cell&amp;#039; );&lt;br /&gt;
              td.classList.add( headerColorClass( td.textContent || &amp;#039;&amp;#039; ) );&lt;br /&gt;
            } );&lt;br /&gt;
          }&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* 8. Smooth scroll */&lt;br /&gt;
    initSmoothScroll( main );&lt;br /&gt;
    initSmoothScroll( sidebar || main );&lt;br /&gt;
&lt;br /&gt;
    /* 9. Auto-style only genuine data tables — not layout/service-grid tables.&lt;br /&gt;
          Real data tables have &amp;lt;th&amp;gt; header cells; layout tables use &amp;lt;td&amp;gt; only. */&lt;br /&gt;
    main.querySelectorAll(&lt;br /&gt;
      &amp;#039;.mw-parser-output table:not(.wikitable):not(.infobox):not(.navbox)&amp;#039;&lt;br /&gt;
    ).forEach( function ( t ) {&lt;br /&gt;
      var firstRow = t.querySelector( &amp;#039;tr&amp;#039; );&lt;br /&gt;
      if ( firstRow &amp;amp;&amp;amp; firstRow.querySelector( &amp;#039;th&amp;#039; ) ) {&lt;br /&gt;
        t.classList.add( &amp;#039;wikitable&amp;#039; );&lt;br /&gt;
      }&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    /* 9b. Old-school &amp;quot;card grid&amp;quot; tables (e.g. Alumni/Staff bio grids) lay out&lt;br /&gt;
           N profile cells per row using a bare width=&amp;quot;350&amp;quot; HTML attribute on&lt;br /&gt;
           each &amp;lt;td&amp;gt;, with narrow width=&amp;quot;25&amp;quot; gap cells in between. Browsers&lt;br /&gt;
           only treat that width as a hint under automatic table layout, so&lt;br /&gt;
           columns end up sized by content instead of rendering equally.&lt;br /&gt;
           Force fixed layout via our own stylesheet (not user-editable&lt;br /&gt;
           wikitext, so nothing here is at risk of sanitizer stripping).&lt;br /&gt;
&lt;br /&gt;
           Only tag tables with this exact, unambiguous shape: every&lt;br /&gt;
           width-bearing cell in the table uses one of exactly two values —&lt;br /&gt;
           a &amp;quot;content&amp;quot; width and a much narrower &amp;quot;gap&amp;quot; width — with no other&lt;br /&gt;
           width values mixed in. Rows are allowed to have a trailing/short&lt;br /&gt;
           row with fewer cells (e.g. 2 people instead of 3), so this checks&lt;br /&gt;
           values used, not a fixed count per row. This keeps the rule from&lt;br /&gt;
           ever reaching colspan header-row tables, rowspan/pricing tables,&lt;br /&gt;
           or nested single-cell formatting tables — none of those share&lt;br /&gt;
           this bare width=&amp;quot;n&amp;quot; alternating content/gap pattern. */&lt;br /&gt;
    main.querySelectorAll( &amp;#039;table:not(.wikitable):not(.infobox):not(.navbox)&amp;#039; ).forEach(&lt;br /&gt;
      function ( t ) {&lt;br /&gt;
        var widthCells = t.querySelectorAll( &amp;#039;td[width]&amp;#039; );&lt;br /&gt;
        if ( widthCells.length &amp;lt; 3 ) { return; }&lt;br /&gt;
        var values = {};&lt;br /&gt;
        Array.prototype.forEach.call( widthCells, function ( td ) {&lt;br /&gt;
          var v = parseInt( td.getAttribute( &amp;#039;width&amp;#039; ), 10 );&lt;br /&gt;
          if ( !isNaN( v ) ) { values[ v ] = ( values[ v ] || 0 ) + 1; }&lt;br /&gt;
        } );&lt;br /&gt;
        var distinct = Object.keys( values ).map( Number ).sort( function ( a, b ) { return a - b; } );&lt;br /&gt;
        if ( distinct.length === 2 &amp;amp;&amp;amp; distinct[ 0 ] * 3 &amp;lt;= distinct[ 1 ] ) {&lt;br /&gt;
          t.classList.add( &amp;#039;bmc-card-grid-table&amp;#039; );&lt;br /&gt;
          var contentWidth = distinct[ 1 ];&lt;br /&gt;
          Array.prototype.forEach.call( widthCells, function ( td ) {&lt;br /&gt;
            if ( parseInt( td.getAttribute( &amp;#039;width&amp;#039; ), 10 ) === contentWidth ) {&lt;br /&gt;
              td.classList.add( &amp;#039;bmc-card-grid-content-cell&amp;#039; );&lt;br /&gt;
            }&lt;br /&gt;
          } );&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    /* 10. Fix row separators on rowspan tables */&lt;br /&gt;
    requestAnimationFrame( function () {&lt;br /&gt;
      fixRowspanSeparators( main );&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    /* 11. Pricing page — convert wikitables to flex layout so column&lt;br /&gt;
           borders and content stay perfectly aligned even with colspan rows.&lt;br /&gt;
           colspan cells get flex-grow = span so they span proportionally. */&lt;br /&gt;
    if ( currentPage.indexOf( &amp;#039;Pricing&amp;#039; ) !== -1 ) {&lt;br /&gt;
      document.body.classList.add( &amp;#039;bmc-pricing&amp;#039; );&lt;br /&gt;
      main.querySelectorAll( &amp;#039;.wikitable&amp;#039; ).forEach( function ( table ) {&lt;br /&gt;
        table.querySelectorAll( &amp;#039;tr&amp;#039; ).forEach( function ( row ) {&lt;br /&gt;
          row.querySelectorAll( &amp;#039;th, td&amp;#039; ).forEach( function ( cell ) {&lt;br /&gt;
            var span = parseInt( cell.getAttribute( &amp;#039;colspan&amp;#039; ) || &amp;#039;1&amp;#039;, 10 );&lt;br /&gt;
            cell.style.setProperty( &amp;#039;flex&amp;#039;, span + &amp;#039; 1 0%&amp;#039;, &amp;#039;important&amp;#039; );&lt;br /&gt;
            cell.style.setProperty( &amp;#039;min-width&amp;#039;, &amp;#039;0&amp;#039;, &amp;#039;important&amp;#039; );&lt;br /&gt;
          } );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ── Entry point ───────────────────────────────────────────── */&lt;br /&gt;
  mw.hook( &amp;#039;wikipage.content&amp;#039; ).add( function () {&lt;br /&gt;
    /* Skip special pages (edit forms, history, etc.) to not break them */&lt;br /&gt;
    var ns     = mw.config.get( &amp;#039;wgNamespaceNumber&amp;#039; );&lt;br /&gt;
    var action = mw.config.get( &amp;#039;wgAction&amp;#039; );&lt;br /&gt;
    if ( action !== &amp;#039;view&amp;#039; ) return;&lt;br /&gt;
    if ( ns &amp;lt; 0 ) return; /* Special: pages */&lt;br /&gt;
&lt;br /&gt;
    var nav = extractNavFromDom();&lt;br /&gt;
    buildShell( nav );&lt;br /&gt;
  } );&lt;br /&gt;
&lt;br /&gt;
} )();&lt;/div&gt;</summary>
		<author><name>Asoberan</name></author>
	</entry>
</feed>