User:Udays108/common.js: Difference between revisions
Appearance
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
/* ============================================================ | /* ============================================================ | ||
MIT BioMicro Center — MediaWiki Custom Scripts | MIT BioMicro Center — MediaWiki Custom Scripts | ||
Skin: Vector-2022 | Skin: Vector-2022 | Scope: User:USERNAME/common.js | ||
============================================================ */ | ============================================================ */ | ||
| Line 10: | Line 9: | ||
var WIKI = 'https://bmcwiki.mit.edu'; | var WIKI = 'https://bmcwiki.mit.edu'; | ||
/* | /* Navigation — matches the mockup exactly. | ||
Update | Update hrefs if your wiki uses different page titles. */ | ||
var NAV = [ | var NAV = [ | ||
{ | { | ||
label: 'About', | label: 'About', | ||
href: | href: WIKI + '/index.php/BioMicroCenter:About' | ||
}, | }, | ||
{ | { | ||
label: 'News', | label: 'News', | ||
href: | href: WIKI + '/index.php/BioMicroCenter:News', | ||
children: [ | children: [ | ||
{ label: 'Latest News', | { label: 'Latest News', href: WIKI + '/index.php/BioMicroCenter:News' }, | ||
{ label: 'Seminars', | { label: 'Seminars', href: WIKI + '/index.php/BioMicroCenter:Seminars' }, | ||
{ label: 'Classes & Training',href: 'https://igb.mit.edu/mini-courses', external: true } | { label: 'Classes & Training', href: 'https://igb.mit.edu/mini-courses', external: true } | ||
] | ] | ||
}, | }, | ||
{ | { | ||
label: 'Services', | label: 'Services', | ||
href: | href: WIKI + '/index.php/BioMicroCenter:Assisted_Services', | ||
children: [ | children: [ | ||
{ label: 'Walkup', | { label: 'Walkup', href: WIKI + '/index.php/BioMicroCenter:Walk-up_Services' }, | ||
{ label: 'Assisted', | { label: 'Assisted', href: WIKI + '/index.php/BioMicroCenter:Assisted_Services' }, | ||
{ label: 'Consumables', | { label: 'Consumables', href: WIKI + '/index.php/BioMicroCenter:Consumables' }, | ||
{ label: 'Training', | { label: 'Training', href: 'https://igb.mit.edu/mini-courses', external: true }, | ||
{ label: 'Informatics', | { label: 'Informatics', href: 'https://igb.mit.edu/', external: true } | ||
] | ] | ||
}, | }, | ||
{ | { | ||
label: 'Submission', | label: 'Submission', | ||
href: | href: WIKI + '/index.php/BioMicroCenter:Submission', | ||
children: [ | children: [ | ||
{ | { group: 'MIT Users' }, | ||
{ label: 'Submit a Sample', | { label: 'Submit a Sample', href: WIKI + '/index.php/BioMicroCenter:Submission' }, | ||
{ label: 'MIT Pricing', | { label: 'MIT Pricing', href: WIKI + '/index.php/MIT:Pricing', external: true }, | ||
{ divider: true }, | { divider: true }, | ||
{ | { group: 'Non-MIT Users' }, | ||
{ label: 'External Submission', href: WIKI + '/index.php/BioMicroCenter:Submission' }, | { label: 'External Submission', href: WIKI + '/index.php/BioMicroCenter:Submission' }, | ||
{ label: 'External Pricing', href: WIKI + '/index.php/BioMicroCenter:Grant_Support_%26_Pricing', highlight: true } | { label: 'External Pricing', href: WIKI + '/index.php/BioMicroCenter:Grant_Support_%26_Pricing', highlight: true } | ||
| Line 52: | Line 51: | ||
{ | { | ||
label: 'Staff', | label: 'Staff', | ||
href: | href: WIKI + '/index.php/BioMicroCenter:Staff' | ||
}, | }, | ||
{ | { | ||
label: 'Resources', | label: 'Resources', | ||
href: | href: WIKI + '/index.php/BioMicroCenter:FAQ', | ||
children: [ | children: [ | ||
{ label: 'FAQs', | { label: 'FAQs', href: WIKI + '/index.php/BioMicroCenter:FAQ' }, | ||
{ label: 'Forms', | { label: 'Forms', href: WIKI + '/index.php/BioMicroCenter:Forms' }, | ||
{ label: 'Grant Support & Pricing',href: WIKI + '/index.php/BioMicroCenter:Grant_Support_%26_Pricing' }, | { label: 'Grant Support & Pricing', href: WIKI + '/index.php/BioMicroCenter:Grant_Support_%26_Pricing' }, | ||
{ label: 'Acknowledgements', | { label: 'Acknowledgements', href: WIKI + '/index.php/BioMicroCenter:Acknowledgement', external: true } | ||
] | ] | ||
} | } | ||
| Line 67: | Line 66: | ||
/* ── Helpers | /* ── Helpers ── */ | ||
function make( tag, cls ) { | |||
function make( tag, cls | |||
var n = document.createElement( tag ); | var n = document.createElement( tag ); | ||
if ( cls ) n.className = cls; | if ( cls ) n.className = cls; | ||
return n; | return n; | ||
} | } | ||
function isCurrentPage( href ) { | |||
function | |||
return window.location.href.indexOf( href ) !== -1; | return window.location.href.indexOf( href ) !== -1; | ||
} | } | ||
/* ── 1. | /* ── 1. Inject Topbar ── */ | ||
function injectTopbar() { | function injectTopbar() { | ||
if ( document.querySelector( '.bmc-topbar' ) ) return; | |||
var bar = make( 'div', 'bmc-topbar' ); | var bar = make( 'div', 'bmc-topbar' ); | ||
bar.innerHTML = | bar.innerHTML = | ||
| Line 95: | Line 92: | ||
/* ── 2. Build | /* ── 2. Build Nav HTML ── */ | ||
function | function buildNavList() { | ||
var ul = make( 'ul' ); | var ul = make( 'ul' ); | ||
NAV.forEach( function ( item ) { | NAV.forEach( function ( item ) { | ||
var li = make( 'li' ); | var li = make( 'li' ); | ||
var | var active = isCurrentPage( item.href ); | ||
if ( item.children ) { | if ( item.children ) { | ||
li.className = 'bmc-dropdown' + ( | 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' ); | var menu = make( 'div', 'bmc-dropdown-menu' ); | ||
| Line 113: | Line 112: | ||
} else if ( c.group ) { | } else if ( c.group ) { | ||
var g = make( 'div', 'bmc-group-label' ); | var g = make( 'div', 'bmc-group-label' ); | ||
g.textContent = c. | g.textContent = c.group; | ||
menu.appendChild( g ); | menu.appendChild( g ); | ||
} else { | } else { | ||
| Line 119: | Line 118: | ||
a.href = c.href; | a.href = c.href; | ||
a.textContent = c.label; | a.textContent = c.label; | ||
if ( c.external ) a.target | if ( c.external ) a.setAttribute( 'target', '_blank' ); | ||
if ( c.highlight ) { a.style.fontWeight = '600'; a.style.color = '#a31f34'; } | if ( c.highlight ) { a.style.fontWeight = '600'; a.style.color = '#a31f34'; } | ||
menu.appendChild( a ); | menu.appendChild( a ); | ||
| Line 126: | Line 125: | ||
li.appendChild( menu ); | li.appendChild( menu ); | ||
} else { | } else { | ||
li.className = | li.className = active ? 'bmc-active' : ''; | ||
var a = make( 'a' ); | |||
a.href = item.href; | |||
a.textContent = item.label; | |||
li.appendChild( a ); | |||
} | } | ||
ul.appendChild( li ); | ul.appendChild( li ); | ||
} ); | } ); | ||
return ul; | return ul; | ||
} | } | ||
/* ── 3. | /* ── 3. Inject Header ── */ | ||
function injectHeader() { | function injectHeader() { | ||
if ( document.querySelector( '.bmc-header' ) ) return; | |||
var header = make( 'div', 'bmc-header' ); | var header = make( 'div', 'bmc-header' ); | ||
var inner = make( 'div', 'bmc-header-inner' ); | var inner = make( 'div', 'bmc-header-inner' ); | ||
| Line 148: | Line 150: | ||
'<div class="bmc-logo-mark">BMC</div>' + | '<div class="bmc-logo-mark">BMC</div>' + | ||
'<div class="bmc-logo-text">' + | '<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>'; | '</div>'; | ||
/* Nav */ | /* Nav */ | ||
var nav = make( 'nav', 'bmc-nav' ); | var nav = make( 'nav', 'bmc-nav' ); | ||
nav.appendChild( | nav.appendChild( buildNavList() ); | ||
/* Right side: move Vector's search + user | /* Right side: move Vector's search + user tools here */ | ||
var right = make( 'div', 'bmc-header-right' ); | var right = make( 'div', 'bmc-header-right' ); | ||
var vectorSearch = document.querySelector( '.vector-search-box, #p-search' ); | /* Grab Vector's search box and user links — move into our header */ | ||
if ( vectorSearch ) | 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' ); | var vectorUser = document.querySelector( '.vector-user-links, #p-personal, .mw-portlet-personal' ); | ||
if ( vectorUser ) | if ( vectorUser ) right.appendChild( vectorUser ); | ||
inner.appendChild( logo ); | inner.appendChild( logo ); | ||
| Line 178: | Line 175: | ||
/* Insert after topbar */ | /* Insert after topbar */ | ||
var topbar = document.querySelector( '.bmc-topbar' ); | var topbar = document.querySelector( '.bmc-topbar' ); | ||
if ( topbar | if ( topbar ) { | ||
topbar.insertAdjacentElement( 'afterend', header ); | |||
} else { | } else { | ||
document.body.insertBefore( header, document.body.firstChild ); | document.body.insertBefore( header, document.body.firstChild ); | ||
| Line 186: | Line 183: | ||
/* ── 4. | /* ── 4. Inject Hero Banner ── */ | ||
function injectHero() { | function injectHero() { | ||
/* | if ( document.querySelector( '.bmc-hero' ) ) return; | ||
/* Skip Special: pages and edit forms */ | |||
var ns = mw.config.get( 'wgNamespaceNumber' ); | var ns = mw.config.get( 'wgNamespaceNumber' ); | ||
if ( ns < 0 ) return; | if ( ns < 0 ) return; | ||
if ( document.querySelector( '# | if ( document.querySelector( '#editform' ) ) return; | ||
/* | /* Read title from the hidden Vector h1 */ | ||
var | var h1El = document.querySelector( 'h1.firstHeading, .mw-first-heading' ); | ||
var title = | var title = h1El ? h1El.textContent.trim() : mw.config.get( 'wgTitle' ); | ||
var hero = make( 'div', 'bmc-hero' ); | var hero = make( 'div', 'bmc-hero' ); | ||
hero.innerHTML = | hero.innerHTML = | ||
| Line 206: | Line 204: | ||
mw.html.escape( title ) + | mw.html.escape( title ) + | ||
'</div>' + | '</div>' + | ||
'< | '<div class="bmc-hero-title">' + mw.html.escape( title ) + '</div>' + | ||
'</div>'; | '</div>'; | ||
/* Insert before the | /* Insert immediately before the page content container — safe, no element moved */ | ||
var | var target = document.querySelector( '.mw-content-container' ); | ||
if ( | if ( target ) { | ||
target.parentNode.insertBefore( hero, target ); | |||
} | } | ||
} | } | ||
/* ── 5. | /* ── 5. Inject Footer ── */ | ||
function | function injectFooter() { | ||
if ( document.querySelector( '.bmc-footer' ) ) return; | |||
var footer = make( 'div', 'bmc-footer' ); | var footer = make( 'div', 'bmc-footer' ); | ||
footer.innerHTML = | footer.innerHTML = | ||
| Line 290: | Line 259: | ||
/* ── | /* ── 6. Scroll Offset Fix ── */ | ||
function fixScrollOffset() { | function fixScrollOffset() { | ||
var OFFSET = | var OFFSET = 116; /* topbar 28px + header 68px + buffer 20px */ | ||
document.addEventListener( 'click', function ( e ) { | document.addEventListener( 'click', function ( e ) { | ||
var anchor = e.target.closest( 'a[href^="#"]' ); | var anchor = e.target.closest( 'a[href^="#"]' ); | ||
| Line 311: | Line 280: | ||
/* ── | /* ── 7. Tab Panel System ── */ | ||
function initTabs() { | function initTabs() { | ||
var btns = document.querySelectorAll( '.tab-btn' ); | var btns = document.querySelectorAll( '.tab-btn' ); | ||
| Line 322: | Line 291: | ||
btns.forEach( function ( btn ) { | btns.forEach( function ( btn ) { | ||
btn.addEventListener( 'click', function () { | btn.addEventListener( 'click', function () { | ||
btns.forEach( function ( t ) { t.classList.remove( 'active' ); } ); | btns.forEach( function ( t ) { t.classList.remove( 'active' ); } ); | ||
panels.forEach( function ( p ) { p.classList.remove( 'active' ); } ); | panels.forEach( function ( p ) { p.classList.remove( 'active' ); } ); | ||
btn.classList.add( 'active' ); | btn.classList.add( 'active' ); | ||
| Line 328: | Line 297: | ||
if ( panel ) panel.classList.add( 'active' ); | if ( panel ) panel.classList.add( 'active' ); | ||
var wrap = document.querySelector( '.tab-bar-wrap' ); | var wrap = document.querySelector( '.tab-bar-wrap' ); | ||
if ( wrap ) window.scrollTo( { top: wrap.getBoundingClientRect().top + window.pageYOffset - | if ( wrap ) window.scrollTo( { top: wrap.getBoundingClientRect().top + window.pageYOffset - 96, behavior: 'smooth' } ); | ||
} ); | } ); | ||
} ); | } ); | ||
| Line 334: | Line 303: | ||
/* ── Run | /* ── Run ── */ | ||
injectTopbar(); | injectTopbar(); | ||
injectHeader(); | injectHeader(); | ||
injectHero(); | injectHero(); | ||
injectFooter(); | injectFooter(); | ||
fixScrollOffset(); | fixScrollOffset(); | ||
Revision as of 20:17, 8 May 2026
/* ============================================================
MIT BioMicro Center — MediaWiki Custom Scripts
Skin: Vector-2022 | Scope: User:USERNAME/common.js
============================================================ */
mw.loader.using( [ 'mediawiki.util' ] ).then( function () {
'use strict';
var WIKI = 'https://bmcwiki.mit.edu';
/* Navigation — matches the mockup exactly.
Update hrefs if your wiki uses different page titles. */
var NAV = [
{
label: 'About',
href: WIKI + '/index.php/BioMicroCenter:About'
},
{
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 }
]
}
];
/* ── Helpers ── */
function make( tag, cls ) {
var n = document.createElement( tag );
if ( cls ) n.className = cls;
return n;
}
function isCurrentPage( href ) {
return window.location.href.indexOf( href ) !== -1;
}
/* ── 1. Inject Topbar ── */
function injectTopbar() {
if ( document.querySelector( '.bmc-topbar' ) ) return;
var bar = make( 'div', 'bmc-topbar' );
bar.innerHTML =
'<div class="bmc-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>';
document.body.insertBefore( bar, document.body.firstChild );
}
/* ── 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();
} );