MediaWiki:Gadget-StickyToC.js

From JoJo's Bizarre Encyclopedia - JoJo Wiki
Jump to navigation Jump to search

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)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
$(function(){
	if (!$('#toc').length) {
		return;
	}
	var i18n = {
		header: 'Contents',
		top: '(Top)'
	};
	
	new ResizeObserver(function(entries) {
		$('#mw-panel').css('height', document.body.scrollHeight - 10);
	}).observe($('#content')[0]);
	var tocCollapsed = (localStorage.getItem("sidebar_vector_c_p-toc") === "y");
	$('<nav id="p-toc" class="vector-menu mw-portlet mw-portlet-toc vector-menu-portal portal" aria-labelledby="p-toc-label" role="navigation">').append(
	    $('<h3 id="p-toc-label" class="vector-menu-heading" tabindex="0">').append(
	        $('<span class="vector-menu-heading-label">').text(i18n.header)
	    ).on("keypress", function(event) {
			if (event.which == 13) {
				$(this).trigger("click");
				event.stopImmediatePropagation()
			}
		}).on("click", function(event) {
	        var $el = $(this).parent();
			event.stopPropagation();
	        $el.toggleClass("collapsed");
	        if($el.hasClass("collapsed")){
				localStorage.setItem("sidebar_vector_c_p-toc", "y");
	            $el.find(".vector-menu-content").slideUp("fast");
	            $el.css({'margin-right': '', 'margin-left': ''});
	        }
	        else{
				localStorage.setItem("sidebar_vector_c_p-toc", "n");
	            $el.find(".vector-menu-content").slideDown("fast");
	            var $toc = $el.find(".vector-menu-content-list")[0];
	            if ($toc.clientWidth < $toc.scrollWidth) {
	        		$el.css({'margin-right': '0', 'margin-left': '0'});
	            }
				if (window.innerWidth >= 982 && $toc.clientWidth < $toc.scrollWidth) {
	        		$el.css({'margin-right': '-0.5em', 'margin-left': '-0.5em'});
				}
	        }
		}),
	    $('<div class="vector-menu-content">').append(
	        $('<ul class="vector-menu-content-list">').append(
	        	$('<li class="toclevel-1 tocsection-0">').append($('<a href="#">').append(
	        		$('<span class="toctext">').text(i18n.top)
	        	)),
	            $('#toc > ul').children().clone()
	        )
	    ).addClass($('#toc').parent().prop('class').split(/\s+/).filter(function(classname) {
	    	return classname.startsWith('toc-');
	    }).join(' ')).css('display', tocCollapsed ? 'none' : '')
	).addClass(tocCollapsed ? 'collapsed' : '').appendTo('#mw-panel');
	
	mw.loader.using( ['ext.gadget.sectionObserver'], function(require) {
		var initSectionObserver = require('ext.gadget.sectionObserver');
		var allSections = document.querySelectorAll('#firstHeading, .mw-headline');
		var sectionObserver = initSectionObserver({topMargin: 20, onIntersection: onIntersection})
		sectionObserver.setElements(allSections);
		sectionObserver.calcIntersection();
		
		allSections = Array.from(allSections);
		var tocList = $('#p-toc .vector-menu-content-list')[0];
		var tocSections = $('#p-toc .vector-menu-content-list li');
		var baseOffset = $('#p-toc .tocsection-0 .toctext')[0].offsetTop;
		
	function onIntersection(section) {
	    var index = allSections.indexOf(section);
	    $('#p-toc .tocsection-current').removeClass('tocsection-current');
	    var tocSection = tocSections.eq(index).addClass('tocsection-current');
	
	    // Calculate the position of the ToC item within the ToC container
	    var tocItemOffsetTop = tocSection[0].offsetTop - tocList.offsetTop;
	    var tocItemHeight = tocSection.outerHeight();
	
	    // Adjust the ToC container's scrollTop to ensure the item is visible
	    if (tocItemOffsetTop < tocList.scrollTop) {
	        // If the item is above the visible area, scroll up to it
	        tocList.scrollTop = tocItemOffsetTop - tocItemHeight;
	    } else if (tocItemOffsetTop + tocItemHeight > tocList.scrollTop + tocList.clientHeight) {
	        // If the item is below the visible area, scroll down to it
	        tocList.scrollTop = tocItemOffsetTop + tocItemHeight - tocList.clientHeight;
	    }
	}

		
		// Helper function to check if an element is in the viewport within its container
		function isElementInViewport(el) {
		    var rect = el.getBoundingClientRect();
		    return (
		        rect.top >= 0 &&
		        rect.left >= 0 &&
		        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
		        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
		    );
		}
	} );
});