MediaWiki:Gadget-StickyToC.js

From JoJo's Bizarre Encyclopedia - JoJo Wiki
Revision as of 22:49, 8 April 2024 by Vish (talk | contribs)
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") !== "n");
    
    // Create the table of contents (TOC) element
    $('<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');
    
    // Initialize section observer for scrolling behavior
    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');
            if ( tocSection.is(':hidden') && index > 0 ) return onIntersection(allSections[index - 1]);
            var tocSectionPos = tocSection[0].offsetTop - baseOffset;
            if ( tocSectionPos < tocList.scrollTop + (tocList.clientHeight * 0.15) ) {
                tocList.scrollTo({top: tocSectionPos - (tocList.clientHeight * 0.3)});
            }
            else if ( tocSectionPos > tocList.scrollTop + (tocList.clientHeight * 0.85) ) {
                tocList.scrollTo({top: tocSectionPos - (tocList.clientHeight * 0.7)});
            }
        }
    });
    
    // Update the TOC position based on the sticky ad height
    function updateTocPosition() {
        var stickyAdHeight = $('#stickySidePic').outerHeight(true) || 0;
        $('#p-toc').css('top', stickyAdHeight + 0.5 + 'em');
    }

    // Initialize and adjust TOC position
    updateTocPosition();
    $(window).resize(updateTocPosition);
});