MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
Add The World and Skills dropdowns in Compendium sidebar |
Position scroll to top button next to main container and above navbar |
||
| (13 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
/* UO Renaissance Wiki Common.js */ | /* UO Renaissance Wiki Common.js */ | ||
(function() { | (function() { | ||
var compendiumPortal = document.getElementById('p-Compendium'); | |||
if (compendiumPortal) { | |||
if (! | function attachSidebarSubmenu(liSelector, titleMatch, items) { | ||
var li = compendiumPortal.querySelector(liSelector) || | |||
compendiumPortal.querySelector('a[href*="' + titleMatch + '"]'); | |||
if (li && li.tagName.toLowerCase() === 'a') { | |||
li = li.closest('li'); | |||
} | |||
if (!li || li.querySelector('.uor-sidebar-submenu')) return; | |||
li.classList.add('uor-sidebar-has-submenu'); | |||
var link = li.querySelector(':scope > a'); | |||
if (link && !link.querySelector('.uor-submenu-arrow')) { | |||
var arrow = document.createElement('span'); | |||
arrow.className = 'uor-submenu-arrow'; | |||
arrow.innerHTML = '▸'; | |||
arrow.title = 'View Sections'; | |||
arrow.addEventListener('click', function(e) { | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
li.classList.toggle('is-open'); | |||
}); | |||
link.appendChild(arrow); | |||
} | |||
var submenu = document.createElement('ul'); | |||
submenu.className = 'uor-sidebar-submenu'; | |||
var html = ''; | |||
for (var i = 0; i < items.length; i++) { | |||
var | html += '<li><a href="' + items[i].href + '">' + items[i].label + '</a></li>'; | ||
} | |||
submenu.innerHTML = html; | |||
li.appendChild(submenu); | |||
'< | } | ||
// 1. The World Submenu | |||
attachSidebarSubmenu('li#n-The-World', '/wiki/index.php/The_World', [ | |||
{ label: 'Towns', href: '/wiki/index.php/The_World#Towns' }, | |||
if ( | { label: 'Dungeons', href: '/wiki/index.php/The_World#Dungeons' }, | ||
{ label: 'Points of Interest', href: '/wiki/index.php/The_World#Points_of_Interest' } | |||
]); | |||
// 2. Skills Submenu | |||
attachSidebarSubmenu('li#n-Skills', '/wiki/index.php/Skills', [ | |||
{ label: 'Combat', href: '/wiki/index.php/Skills#Combat' }, | |||
{ label: 'Crafting', href: '/wiki/index.php/Skills#Crafting' }, | |||
{ label: 'Creatures', href: '/wiki/index.php/Skills#Creatures' }, | |||
{ label: 'Lore & Knowledge', href: '/wiki/index.php/Skills#Lore_&_Knowledge' }, | |||
{ label: 'Miscellaneous', href: '/wiki/index.php/Skills#Miscellaneous' }, | |||
{ label: 'Rogue', href: '/wiki/index.php/Skills#Rogue' }, | |||
{ label: 'Spells & Magic', href: '/wiki/index.php/Spells' } | |||
]); | |||
// 3. Factions & Guilds Submenu | |||
attachSidebarSubmenu('li[id*="Factions"], li#n-Factions', '/wiki/index.php/Factions', [ | |||
{ label: 'Factions', href: '/wiki/index.php/Factions_%26_Guilds#Factions' }, | |||
{ label: 'Order & Chaos', href: '/wiki/index.php/Factions_%26_Guilds#Order_&_Chaos' }, | |||
{ label: 'Player run Guilds', href: '/wiki/index.php/Factions_%26_Guilds#Player_run_Guilds' } | |||
]); | |||
// 4. Champion Spawns Submenu | |||
attachSidebarSubmenu('li#n-Champion-Spawns', '/wiki/index.php/Champion_Spawns', [ | |||
{ label: 'Overview & Altars', href: '/wiki/index.php/Champion_Spawns#Altars' }, | |||
{ label: 'The Six Champions', href: '/wiki/index.php/Champion_Spawns#Champions' }, | |||
{ label: 'The Harrower', href: '/wiki/index.php/Champion_Spawns#The_Harrower' }, | |||
{ label: 'Rewards & Titles', href: '/wiki/index.php/Champion_Spawns#Rewards' } | |||
]); | |||
// 5. Bestiary Submenu | |||
attachSidebarSubmenu('li#n-Bestiary', '/wiki/index.php/Bestiary', [ | |||
{ label: 'Bosses & Champions', href: '/wiki/index.php/Bestiary#Bosses_and_Champions' }, | |||
{ label: 'Daemons & Elementals', href: '/wiki/index.php/Bestiary#Daemons_and_Elementals' }, | |||
{ label: 'Reptiles & Monstrosities', href: '/wiki/index.php/Bestiary#Reptiles_and_Monstrosities' }, | |||
{ label: 'Undead & Tomb Horrors', href: '/wiki/index.php/Bestiary#Undead_and_Horrors' }, | |||
{ label: 'Humanoids, Tribes & Beasts', href: '/wiki/index.php/Bestiary#Humanoids_and_Beasts' }, | |||
{ label: 'Holiday & Event Specials', href: '/wiki/index.php/Bestiary#Holiday_and_Events' } | |||
]); | |||
// 6. Housing & Vendors Submenu | |||
attachSidebarSubmenu('li[id*="Housing"], li#n-Housing-Vendors', 'Housing', [ | |||
{ label: 'Ownership Rules', href: '/wiki/index.php/Housing_System#Home_Ownership_Fundamentals' }, | |||
{ label: 'House Catalog', href: '/wiki/index.php/Housing_System#Complete_House_Catalog' }, | |||
{ label: 'Security & Tactics', href: '/wiki/index.php/Housing_System#House_Security_&_Fortification' }, | |||
{ label: 'Classic Vendors', href: '/wiki/index.php/Housing_System#Classic_Player_Vendors' }, | |||
{ label: 'Enhanced Vendors', href: '/wiki/index.php/Housing_System#Enhanced_Vendor_System' }, | |||
{ label: 'Quick Reference', href: '/wiki/index.php/Housing_System#Housing_&_Vendor_Quick_Reference' } | |||
]); | |||
// 7. Murder & Bounties Submenu | |||
attachSidebarSubmenu('li[id*="Murder"], li#n-Murder-Bounties', 'Murder', [ | |||
{ label: 'Murder Counts', href: '/wiki/index.php/The_Murder_System#Understanding_Murder_Counts' }, | |||
{ label: 'Stat Loss Formula', href: '/wiki/index.php/The_Murder_System#Stat_Loss_Formula_&_Penalties' }, | |||
{ label: 'Reporting & Bounties', href: '/wiki/index.php/The_Murder_System#Reporting_a_Murder_&_Placing_Bounties' }, | |||
{ label: 'The Bounty Board', href: '/wiki/index.php/The_Murder_System#The_Bounty_Board_System' }, | |||
{ label: 'Claiming Heads', href: '/wiki/index.php/The_Murder_System#Claiming_Bounties_&_Taking_Heads' }, | |||
{ label: 'Quick Reference', href: '/wiki/index.php/The_Murder_System#Murder_&_Bounty_Quick_Reference' } | |||
]); | |||
// 8. Patch Notes Archive Submenu | |||
attachSidebarSubmenu('li#n-Patch-Notes-Archive', '/wiki/index.php/Patch_Notes_Archive', [ | |||
{ label: 'Era 1: Alpha & Beta (2012)', href: '/wiki/index.php/Patch_Notes_Archive#Era_1:_Alpha_.26_Beta_Foundations_.28May_2012_-_Sept_2012.29' }, | |||
{ label: 'Era 2: Launch & Maturation (2012-2014)', href: '/wiki/index.php/Patch_Notes_Archive#Era_2:_Shard_Launch_.26_Server_Maturation_.28Sept_2012_-_Sept_2014.29' }, | |||
{ label: 'Era 3: Golden Age (2014-2016)', href: '/wiki/index.php/Patch_Notes_Archive#Era_3:_The_Golden_Age_.26_Expansions_.28Oct_2014_-_Oct_2016.29' }, | |||
{ label: 'Era 4: Veteran & Modern (2016-2020)', href: '/wiki/index.php/Patch_Notes_Archive#Era_4:_Veteran_Shard_.26_Modernization_.28Nov_2016_-_June_2020.29' } | |||
]); | |||
// 9. Unique Shard Systems Submenu | |||
var uniqueSystemsItems = [ | |||
{ label: "The Zookeeper's Quest", href: "/wiki/index.php/The_Zookeeper's_Quest" }, | |||
{ label: "Ancient MIB's", href: "/wiki/index.php/Ancient_MIB's" } | |||
]; | |||
function normalizeSystemUrl(url) { | |||
if (!url) return ''; | |||
try { | |||
return decodeURIComponent(url).replace(/\/index\.php\//, '/index.php/').replace(/\/+$/, '').toLowerCase(); | |||
} catch(e) { | |||
return url.toLowerCase(); | |||
} | |||
} | |||
function isExcludedOrDuplicate(item, list) { | |||
if (!item || !item.href) return true; | |||
var nHref = normalizeSystemUrl(item.href); | |||
var nLbl = (item.label || '').trim().toLowerCase(); | |||
// Exclude the parent page itself | |||
if (nHref.indexOf('unique_systems') !== -1 || nLbl === 'unique shard systems' || nLbl === 'unique systems') { | |||
return true; | |||
} | } | ||
return list.some(function(existing) { | |||
return normalizeSystemUrl(existing.href) === nHref || | |||
(existing.label && existing.label.trim().toLowerCase() === nLbl); | |||
}); | |||
} | |||
// Automatically incorporate any additional unique systems stored in localStorage | |||
if ( | try { | ||
var storedUnique = localStorage.getItem('uor_unique_shard_systems'); | |||
if (storedUnique) { | |||
var parsedUnique = JSON.parse(storedUnique); | |||
if (Array.isArray(parsedUnique)) { | |||
var filteredStorage = []; | |||
parsedUnique.forEach(function(item) { | |||
if (!isExcludedOrDuplicate(item, uniqueSystemsItems)) { | |||
uniqueSystemsItems.push(item); | |||
filteredStorage.push(item); | |||
} | |||
}); | |||
// Update localStorage to strip any invalid/duplicate entries | |||
localStorage.setItem('uor_unique_shard_systems', JSON.stringify(filteredStorage)); | |||
} | } | ||
}) | } | ||
} catch (e) {} | |||
// Check if the DOM already has sub-items (e.g. from MediaWiki:Sidebar) | |||
var uniqueLi = compendiumPortal.querySelector('li[id*="Unique"], li#n-Unique-Shard-Systems') || | |||
compendiumPortal.querySelector('a[href*="/wiki/index.php/Unique_Systems"]'); | |||
if (uniqueLi) { | |||
if (uniqueLi.tagName.toLowerCase() === 'a') uniqueLi = uniqueLi.closest('li'); | |||
if (uniqueLi) { | |||
var domSubLinks = uniqueLi.querySelectorAll('ul li a'); | |||
domSubLinks.forEach(function(sl) { | |||
var lbl = sl.textContent.trim(); | |||
var hrf = sl.getAttribute('href'); | |||
var candidate = { label: lbl, href: hrf }; | |||
if (!isExcludedOrDuplicate(candidate, uniqueSystemsItems)) { | |||
uniqueSystemsItems.push(candidate); | |||
} | |||
}); | |||
} | |||
} | } | ||
attachSidebarSubmenu('li[id*="Unique"], li#n-Unique-Shard-Systems', '/wiki/index.php/Unique_Systems', uniqueSystemsItems); | |||
// Sync any custom unique system links dynamically from the Unique Systems hub page | |||
if (window.location.pathname.indexOf('/Unique_Systems') !== -1) { | |||
if ( | try { | ||
var | var pageLinks = document.querySelectorAll('.uor-unique-system-link, [data-shard-system]'); | ||
if (pageLinks.length > 0) { | |||
var collected = []; | |||
pageLinks.forEach(function(pl) { | |||
var plLabel = pl.getAttribute('data-shard-system') || pl.textContent.trim(); | |||
var plHref = pl.getAttribute('href'); | |||
var candidate = { label: plLabel, href: plHref }; | |||
if (!isExcludedOrDuplicate(candidate, collected) && !isExcludedOrDuplicate(candidate, uniqueSystemsItems)) { | |||
collected.push(candidate); | |||
} | |||
}); | |||
if (collected.length > 0) { | |||
localStorage.setItem('uor_unique_shard_systems', JSON.stringify(collected)); | |||
} | |||
} | |||
} catch (e) {} | |||
} | |||
document.addEventListener('click', function(e) { | |||
if (!e.target.closest('.uor-sidebar-has-submenu')) { | |||
var openMenus = document.querySelectorAll('.uor-sidebar-has-submenu.is-open'); | |||
for (var i = 0; i < openMenus.length; i++) { | |||
openMenus[i].classList.remove('is-open'); | |||
} | } | ||
} | } | ||
}); | |||
} | |||
})(); | |||
/* Floating Scroll to Top Arrow Button */ | |||
(function() { | |||
function initScrollToTop() { | |||
var existing = document.getElementById('uor-scroll-top'); | |||
if (existing) existing.remove(); | |||
var btn = document.createElement('a'); | |||
btn.id = 'uor-scroll-top'; | |||
btn.className = 'uor-scroll-top-btn'; | |||
btn.href = '#top'; | |||
btn.title = 'Scroll to Top'; | |||
btn.setAttribute('aria-label', 'Scroll to top of page'); | |||
btn.innerHTML = '<svg viewBox="0 0 24 24"><path d="M12 4l-8 8h5v8h6v-8h5z"/></svg>'; | |||
btn.addEventListener('click', function(e) { | |||
e.preventDefault(); | |||
window.scrollTo({ | |||
top: 0, | |||
behavior: 'smooth' | |||
}); | |||
}); | |||
document.body.appendChild(btn); | |||
if ( | |||
function repositionButton() { | |||
var mainBox = document.getElementById('content') || document.querySelector('.mw-body'); | |||
if (mainBox) { | |||
var rect = mainBox.getBoundingClientRect(); | |||
var spaceRight = window.innerWidth - rect.right; | |||
// If there's at least 54px room to the right of the container, stick right beside it | |||
if (spaceRight >= 54) { | |||
btn.style.left = Math.round(rect.right + 10) + 'px'; | |||
btn.style.right = 'auto'; | |||
} else { | |||
// Narrow viewport / mobile: dock to right screen edge | |||
btn.style.left = 'auto'; | |||
btn.style.right = '16px'; | |||
} | } | ||
} | } else { | ||
btn.style.left = 'auto'; | |||
btn.style.right = '16px'; | |||
} | |||
} | |||
var ticking = false; | |||
function updateState() { | |||
var scrollY = window.pageYOffset || document.documentElement.scrollTop || 0; | |||
if (scrollY > 220) { | |||
btn.classList.add('is-visible'); | |||
} else { | |||
btn.classList.remove('is-visible'); | |||
} | |||
repositionButton(); | |||
ticking = false; | |||
} | } | ||
window.addEventListener('scroll', function() { | |||
if (!ticking) { | |||
window.requestAnimationFrame(updateState); | |||
ticking = true; | |||
} | |||
}, { passive: true }); | |||
window.addEventListener('resize', function() { | |||
repositionButton(); | |||
}, { passive: true }); | |||
// Initial check | |||
updateState(); | |||
setTimeout(repositionButton, 200); | |||
setTimeout(repositionButton, 1000); | |||
} | } | ||
if (document.readyState === 'loading') { | if (document.readyState === 'loading') { | ||
document.addEventListener('DOMContentLoaded', | document.addEventListener('DOMContentLoaded', initScrollToTop); | ||
} else { | } else { | ||
initScrollToTop(); | |||
} | } | ||
})(); | })(); | ||
Latest revision as of 18:28, 9 September 2026
/* UO Renaissance Wiki Common.js */
(function() {
var compendiumPortal = document.getElementById('p-Compendium');
if (compendiumPortal) {
function attachSidebarSubmenu(liSelector, titleMatch, items) {
var li = compendiumPortal.querySelector(liSelector) ||
compendiumPortal.querySelector('a[href*="' + titleMatch + '"]');
if (li && li.tagName.toLowerCase() === 'a') {
li = li.closest('li');
}
if (!li || li.querySelector('.uor-sidebar-submenu')) return;
li.classList.add('uor-sidebar-has-submenu');
var link = li.querySelector(':scope > a');
if (link && !link.querySelector('.uor-submenu-arrow')) {
var arrow = document.createElement('span');
arrow.className = 'uor-submenu-arrow';
arrow.innerHTML = '▸';
arrow.title = 'View Sections';
arrow.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
li.classList.toggle('is-open');
});
link.appendChild(arrow);
}
var submenu = document.createElement('ul');
submenu.className = 'uor-sidebar-submenu';
var html = '';
for (var i = 0; i < items.length; i++) {
html += '<li><a href="' + items[i].href + '">' + items[i].label + '</a></li>';
}
submenu.innerHTML = html;
li.appendChild(submenu);
}
// 1. The World Submenu
attachSidebarSubmenu('li#n-The-World', '/wiki/index.php/The_World', [
{ label: 'Towns', href: '/wiki/index.php/The_World#Towns' },
{ label: 'Dungeons', href: '/wiki/index.php/The_World#Dungeons' },
{ label: 'Points of Interest', href: '/wiki/index.php/The_World#Points_of_Interest' }
]);
// 2. Skills Submenu
attachSidebarSubmenu('li#n-Skills', '/wiki/index.php/Skills', [
{ label: 'Combat', href: '/wiki/index.php/Skills#Combat' },
{ label: 'Crafting', href: '/wiki/index.php/Skills#Crafting' },
{ label: 'Creatures', href: '/wiki/index.php/Skills#Creatures' },
{ label: 'Lore & Knowledge', href: '/wiki/index.php/Skills#Lore_&_Knowledge' },
{ label: 'Miscellaneous', href: '/wiki/index.php/Skills#Miscellaneous' },
{ label: 'Rogue', href: '/wiki/index.php/Skills#Rogue' },
{ label: 'Spells & Magic', href: '/wiki/index.php/Spells' }
]);
// 3. Factions & Guilds Submenu
attachSidebarSubmenu('li[id*="Factions"], li#n-Factions', '/wiki/index.php/Factions', [
{ label: 'Factions', href: '/wiki/index.php/Factions_%26_Guilds#Factions' },
{ label: 'Order & Chaos', href: '/wiki/index.php/Factions_%26_Guilds#Order_&_Chaos' },
{ label: 'Player run Guilds', href: '/wiki/index.php/Factions_%26_Guilds#Player_run_Guilds' }
]);
// 4. Champion Spawns Submenu
attachSidebarSubmenu('li#n-Champion-Spawns', '/wiki/index.php/Champion_Spawns', [
{ label: 'Overview & Altars', href: '/wiki/index.php/Champion_Spawns#Altars' },
{ label: 'The Six Champions', href: '/wiki/index.php/Champion_Spawns#Champions' },
{ label: 'The Harrower', href: '/wiki/index.php/Champion_Spawns#The_Harrower' },
{ label: 'Rewards & Titles', href: '/wiki/index.php/Champion_Spawns#Rewards' }
]);
// 5. Bestiary Submenu
attachSidebarSubmenu('li#n-Bestiary', '/wiki/index.php/Bestiary', [
{ label: 'Bosses & Champions', href: '/wiki/index.php/Bestiary#Bosses_and_Champions' },
{ label: 'Daemons & Elementals', href: '/wiki/index.php/Bestiary#Daemons_and_Elementals' },
{ label: 'Reptiles & Monstrosities', href: '/wiki/index.php/Bestiary#Reptiles_and_Monstrosities' },
{ label: 'Undead & Tomb Horrors', href: '/wiki/index.php/Bestiary#Undead_and_Horrors' },
{ label: 'Humanoids, Tribes & Beasts', href: '/wiki/index.php/Bestiary#Humanoids_and_Beasts' },
{ label: 'Holiday & Event Specials', href: '/wiki/index.php/Bestiary#Holiday_and_Events' }
]);
// 6. Housing & Vendors Submenu
attachSidebarSubmenu('li[id*="Housing"], li#n-Housing-Vendors', 'Housing', [
{ label: 'Ownership Rules', href: '/wiki/index.php/Housing_System#Home_Ownership_Fundamentals' },
{ label: 'House Catalog', href: '/wiki/index.php/Housing_System#Complete_House_Catalog' },
{ label: 'Security & Tactics', href: '/wiki/index.php/Housing_System#House_Security_&_Fortification' },
{ label: 'Classic Vendors', href: '/wiki/index.php/Housing_System#Classic_Player_Vendors' },
{ label: 'Enhanced Vendors', href: '/wiki/index.php/Housing_System#Enhanced_Vendor_System' },
{ label: 'Quick Reference', href: '/wiki/index.php/Housing_System#Housing_&_Vendor_Quick_Reference' }
]);
// 7. Murder & Bounties Submenu
attachSidebarSubmenu('li[id*="Murder"], li#n-Murder-Bounties', 'Murder', [
{ label: 'Murder Counts', href: '/wiki/index.php/The_Murder_System#Understanding_Murder_Counts' },
{ label: 'Stat Loss Formula', href: '/wiki/index.php/The_Murder_System#Stat_Loss_Formula_&_Penalties' },
{ label: 'Reporting & Bounties', href: '/wiki/index.php/The_Murder_System#Reporting_a_Murder_&_Placing_Bounties' },
{ label: 'The Bounty Board', href: '/wiki/index.php/The_Murder_System#The_Bounty_Board_System' },
{ label: 'Claiming Heads', href: '/wiki/index.php/The_Murder_System#Claiming_Bounties_&_Taking_Heads' },
{ label: 'Quick Reference', href: '/wiki/index.php/The_Murder_System#Murder_&_Bounty_Quick_Reference' }
]);
// 8. Patch Notes Archive Submenu
attachSidebarSubmenu('li#n-Patch-Notes-Archive', '/wiki/index.php/Patch_Notes_Archive', [
{ label: 'Era 1: Alpha & Beta (2012)', href: '/wiki/index.php/Patch_Notes_Archive#Era_1:_Alpha_.26_Beta_Foundations_.28May_2012_-_Sept_2012.29' },
{ label: 'Era 2: Launch & Maturation (2012-2014)', href: '/wiki/index.php/Patch_Notes_Archive#Era_2:_Shard_Launch_.26_Server_Maturation_.28Sept_2012_-_Sept_2014.29' },
{ label: 'Era 3: Golden Age (2014-2016)', href: '/wiki/index.php/Patch_Notes_Archive#Era_3:_The_Golden_Age_.26_Expansions_.28Oct_2014_-_Oct_2016.29' },
{ label: 'Era 4: Veteran & Modern (2016-2020)', href: '/wiki/index.php/Patch_Notes_Archive#Era_4:_Veteran_Shard_.26_Modernization_.28Nov_2016_-_June_2020.29' }
]);
// 9. Unique Shard Systems Submenu
var uniqueSystemsItems = [
{ label: "The Zookeeper's Quest", href: "/wiki/index.php/The_Zookeeper's_Quest" },
{ label: "Ancient MIB's", href: "/wiki/index.php/Ancient_MIB's" }
];
function normalizeSystemUrl(url) {
if (!url) return '';
try {
return decodeURIComponent(url).replace(/\/index\.php\//, '/index.php/').replace(/\/+$/, '').toLowerCase();
} catch(e) {
return url.toLowerCase();
}
}
function isExcludedOrDuplicate(item, list) {
if (!item || !item.href) return true;
var nHref = normalizeSystemUrl(item.href);
var nLbl = (item.label || '').trim().toLowerCase();
// Exclude the parent page itself
if (nHref.indexOf('unique_systems') !== -1 || nLbl === 'unique shard systems' || nLbl === 'unique systems') {
return true;
}
return list.some(function(existing) {
return normalizeSystemUrl(existing.href) === nHref ||
(existing.label && existing.label.trim().toLowerCase() === nLbl);
});
}
// Automatically incorporate any additional unique systems stored in localStorage
try {
var storedUnique = localStorage.getItem('uor_unique_shard_systems');
if (storedUnique) {
var parsedUnique = JSON.parse(storedUnique);
if (Array.isArray(parsedUnique)) {
var filteredStorage = [];
parsedUnique.forEach(function(item) {
if (!isExcludedOrDuplicate(item, uniqueSystemsItems)) {
uniqueSystemsItems.push(item);
filteredStorage.push(item);
}
});
// Update localStorage to strip any invalid/duplicate entries
localStorage.setItem('uor_unique_shard_systems', JSON.stringify(filteredStorage));
}
}
} catch (e) {}
// Check if the DOM already has sub-items (e.g. from MediaWiki:Sidebar)
var uniqueLi = compendiumPortal.querySelector('li[id*="Unique"], li#n-Unique-Shard-Systems') ||
compendiumPortal.querySelector('a[href*="/wiki/index.php/Unique_Systems"]');
if (uniqueLi) {
if (uniqueLi.tagName.toLowerCase() === 'a') uniqueLi = uniqueLi.closest('li');
if (uniqueLi) {
var domSubLinks = uniqueLi.querySelectorAll('ul li a');
domSubLinks.forEach(function(sl) {
var lbl = sl.textContent.trim();
var hrf = sl.getAttribute('href');
var candidate = { label: lbl, href: hrf };
if (!isExcludedOrDuplicate(candidate, uniqueSystemsItems)) {
uniqueSystemsItems.push(candidate);
}
});
}
}
attachSidebarSubmenu('li[id*="Unique"], li#n-Unique-Shard-Systems', '/wiki/index.php/Unique_Systems', uniqueSystemsItems);
// Sync any custom unique system links dynamically from the Unique Systems hub page
if (window.location.pathname.indexOf('/Unique_Systems') !== -1) {
try {
var pageLinks = document.querySelectorAll('.uor-unique-system-link, [data-shard-system]');
if (pageLinks.length > 0) {
var collected = [];
pageLinks.forEach(function(pl) {
var plLabel = pl.getAttribute('data-shard-system') || pl.textContent.trim();
var plHref = pl.getAttribute('href');
var candidate = { label: plLabel, href: plHref };
if (!isExcludedOrDuplicate(candidate, collected) && !isExcludedOrDuplicate(candidate, uniqueSystemsItems)) {
collected.push(candidate);
}
});
if (collected.length > 0) {
localStorage.setItem('uor_unique_shard_systems', JSON.stringify(collected));
}
}
} catch (e) {}
}
document.addEventListener('click', function(e) {
if (!e.target.closest('.uor-sidebar-has-submenu')) {
var openMenus = document.querySelectorAll('.uor-sidebar-has-submenu.is-open');
for (var i = 0; i < openMenus.length; i++) {
openMenus[i].classList.remove('is-open');
}
}
});
}
})();
/* Floating Scroll to Top Arrow Button */
(function() {
function initScrollToTop() {
var existing = document.getElementById('uor-scroll-top');
if (existing) existing.remove();
var btn = document.createElement('a');
btn.id = 'uor-scroll-top';
btn.className = 'uor-scroll-top-btn';
btn.href = '#top';
btn.title = 'Scroll to Top';
btn.setAttribute('aria-label', 'Scroll to top of page');
btn.innerHTML = '<svg viewBox="0 0 24 24"><path d="M12 4l-8 8h5v8h6v-8h5z"/></svg>';
btn.addEventListener('click', function(e) {
e.preventDefault();
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
document.body.appendChild(btn);
function repositionButton() {
var mainBox = document.getElementById('content') || document.querySelector('.mw-body');
if (mainBox) {
var rect = mainBox.getBoundingClientRect();
var spaceRight = window.innerWidth - rect.right;
// If there's at least 54px room to the right of the container, stick right beside it
if (spaceRight >= 54) {
btn.style.left = Math.round(rect.right + 10) + 'px';
btn.style.right = 'auto';
} else {
// Narrow viewport / mobile: dock to right screen edge
btn.style.left = 'auto';
btn.style.right = '16px';
}
} else {
btn.style.left = 'auto';
btn.style.right = '16px';
}
}
var ticking = false;
function updateState() {
var scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
if (scrollY > 220) {
btn.classList.add('is-visible');
} else {
btn.classList.remove('is-visible');
}
repositionButton();
ticking = false;
}
window.addEventListener('scroll', function() {
if (!ticking) {
window.requestAnimationFrame(updateState);
ticking = true;
}
}, { passive: true });
window.addEventListener('resize', function() {
repositionButton();
}, { passive: true });
// Initial check
updateState();
setTimeout(repositionButton, 200);
setTimeout(repositionButton, 1000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initScrollToTop);
} else {
initScrollToTop();
}
})();