MediaWiki:Common.js: Difference between revisions

From Toon Wiki
(Add interactive features)
 
(Interactive features)
 
Line 3: Line 3:
   ============================================ */
   ============================================ */


(function() {
$(document).ready(function() {
    'use strict';
      
      
     // Wait for DOM ready
     /* ===== Infobox Gallery Tabs ===== */
    document.addEventListener('DOMContentLoaded', function() {
    function initInfoboxTabs() {
       
         $('.infobox-image .gallery').each(function() {
        // ===== Smooth Scroll for TOC links =====
             var $gallery = $(this);
         document.querySelectorAll('.toc a, a[href^="#"]').forEach(function(link) {
            var $items = $gallery.find('.gallerybox');
             link.addEventListener('click', function(e) {
           
                 var targetId = this.getAttribute('href');
            if ($items.length < 2) return;
                 if (targetId && targetId.startsWith('#')) {
           
                    var target = document.querySelector(targetId);
            // Create tab navigation
                    if (target) {
            var $tabs = $('<div class="infobox-tabs"></div>');
                         e.preventDefault();
           
                         target.scrollIntoView({ behavior: 'smooth', block: 'start' });
            $items.each(function(index) {
                        history.pushState(null, null, targetId);
                 var $item = $(this);
                     }
                var label = $item.find('.gallerytext p').text().trim() || ('Image ' + (index + 1));
                  
                var $btn = $('<button class="tab-btn"></button>')
                    .text(label)
                    .attr('data-index', index)
                    .on('click', function() {
                        var idx = $(this).data('index');
                        $items.removeClass('active').hide();
                         $items.eq(idx).addClass('active').show();
                         $tabs.find('.tab-btn').removeClass('active');
                        $(this).addClass('active');
                    });
               
                if (index === 0) {
                    $btn.addClass('active');
                     $item.addClass('active').show();
                } else {
                    $item.hide();
                 }
                 }
               
                $tabs.append($btn);
             });
             });
           
            $gallery.parent().append($tabs);
         });
         });
    }
   
    /* ===== Smooth Scroll for TOC Links ===== */
    function initSmoothScroll() {
        $('a[href^="#"]').on('click', function(e) {
            var target = $(this.hash);
            if (target.length) {
                e.preventDefault();
                $('html, body').animate({
                    scrollTop: target.offset().top - 60
                }, 400);
            }
        });
    }
   
    /* ===== Back to Top Button ===== */
    function initBackToTop() {
        var $btn = $('<button id="back-to-top" title="Back to top">↑</button>')
            .css({
                position: 'fixed',
                bottom: '20px',
                right: '20px',
                width: '44px',
                height: '44px',
                background: '#611e03',
                color: 'white',
                border: 'none',
                borderRadius: '50%',
                fontSize: '20px',
                cursor: 'pointer',
                display: 'none',
                zIndex: 9999,
                boxShadow: '0 2px 10px rgba(0,0,0,0.2)',
                transition: 'all 0.2s ease'
            })
            .on('click', function() {
                $('html, body').animate({ scrollTop: 0 }, 400);
            })
            .on('mouseenter', function() {
                $(this).css({ transform: 'scale(1.1)', background: '#8b4513' });
            })
            .on('mouseleave', function() {
                $(this).css({ transform: 'scale(1)', background: '#611e03' });
            });
       
        $('body').append($btn);
          
          
         // ===== Lazy load images =====
         $(window).on('scroll', function() {
        if ('IntersectionObserver' in window) {
             if ($(this).scrollTop() > 300) {
             var imageObserver = new IntersectionObserver(function(entries) {
                 $btn.fadeIn(200);
                 entries.forEach(function(entry) {
            } else {
                    if (entry.isIntersecting) {
                $btn.fadeOut(200);
                        var img = entry.target;
            }
                        if (img.dataset.src) {
        });
                            img.src = img.dataset.src;
    }
                            img.removeAttribute('data-src');
   
                        }
    /* ===== Image Lightbox ===== */
                        imageObserver.unobserve(img);
    function initLightbox() {
                    }
        var $overlay = $('<div id="lightbox-overlay"></div>').css({
                });
            position: 'fixed',
             }, { rootMargin: '50px' });
            top: 0,
              
            left: 0,
             document.querySelectorAll('img[data-src]').forEach(function(img) {
            right: 0,
                imageObserver.observe(img);
             bottom: 0,
             });
            background: 'rgba(0,0,0,0.9)',
        }
            zIndex: 99999,
             display: 'none',
             cursor: 'zoom-out',
            justifyContent: 'center',
             alignItems: 'center'
        });
          
          
         // ===== Image lightbox effect =====
         var $img = $('<img id="lightbox-img">').css({
        document.querySelectorAll('.mw-parser-output figure img').forEach(function(img) {
             maxWidth: '90%',
             img.style.cursor = 'zoom-in';
             maxHeight: '90%',
             img.addEventListener('click', function(e) {
            boxShadow: '0 0 30px rgba(0,0,0,0.5)',
                e.preventDefault();
            borderRadius: '4px'
                var overlay = document.createElement('div');
                overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.9);z-index:10000;display:flex;align-items:center;justify-content:center;cursor:zoom-out;opacity:0;transition:opacity 0.3s';
               
                var largeImg = document.createElement('img');
                largeImg.src = this.closest('a') ? this.closest('a').href : this.src;
                largeImg.style.cssText = 'max-width:90vw;max-height:90vh;border-radius:8px;box-shadow:0 4px 32px rgba(0,0,0,0.5)';
               
                overlay.appendChild(largeImg);
                document.body.appendChild(overlay);
                document.body.style.overflow = 'hidden';
               
                requestAnimationFrame(function() { overlay.style.opacity = '1'; });
               
                overlay.addEventListener('click', function() {
                    overlay.style.opacity = '0';
                    setTimeout(function() {
                        overlay.remove();
                        document.body.style.overflow = '';
                    }, 300);
                });
            });
         });
         });
          
          
         // ===== Collapsible sections =====
         var $close = $('<button>&times;</button>').css({
        document.querySelectorAll('.mw-parser-output h2, .mw-parser-output h3').forEach(function(header) {
             position: 'absolute',
             header.style.cursor = 'pointer';
             top: '20px',
             header.title = 'Click to collapse/expand';
             right: '30px',
              
             background: 'none',
            var collapseBtn = document.createElement('span');
             border: 'none',
             collapseBtn.textContent = ' ';
             color: 'white',
             collapseBtn.style.cssText = 'font-size:0.7em;opacity:0.5;transition:transform 0.2s';
            fontSize: '40px',
             header.appendChild(collapseBtn);
            cursor: 'pointer',
           
             lineHeight: 1
            header.addEventListener('click', function() {
                var content = [];
                var next = this.nextElementSibling;
                var nextHeaderLevel = this.tagName;
               
                while (next && !next.matches(nextHeaderLevel + ', h2')) {
                    content.push(next);
                    next = next.nextElementSibling;
                }
               
                var isCollapsed = content[0] && content[0].style.display === 'none';
                content.forEach(function(el) {
                    el.style.display = isCollapsed ? '' : 'none';
                });
                collapseBtn.style.transform = isCollapsed ? '' : 'rotate(-90deg)';
             });
         });
         });
          
          
         // ===== Reading time estimate =====
        $overlay.append($img, $close);
         var content = document.querySelector('.mw-parser-output');
        $('body').append($overlay);
        if (content) {
       
             var text = content.textContent;
         // Click handler for images
             var words = text.trim().split(/\s+/).length;
         $('.mw-parser-output img').not('.mw-file-element[width="20"]').on('click', function(e) {
            var minutes = Math.ceil(words / 200);
             var fullSrc = $(this).closest('a').attr('href');
           
             if (fullSrc && fullSrc.match(/\.(png|jpg|jpeg|gif|webp)/i)) {
            var readingTime = document.createElement('div');
                e.preventDefault();
            readingTime.style.cssText = 'background:linear-gradient(135deg,#f8f9fa,#eaecf0);padding:8px 16px;border-radius:8px;margin-bottom:1em;font-size:0.9em;color:#555;display:flex;align-items:center;gap:8px';
                $img.attr('src', fullSrc);
            readingTime.innerHTML = '<span style="font-size:1.2em">📖</span> <strong>' + minutes + ' min read</strong> · ' + words.toLocaleString() + ' words';
                $overlay.css('display', 'flex');
           
                $('body').css('overflow', 'hidden');
            var firstElement = content.firstElementChild;
            if (firstElement) {
                content.insertBefore(readingTime, firstElement);
             }
             }
         }
         });
          
          
         // ===== Highlight current section while scrolling =====
         // Close handlers
        var headings = document.querySelectorAll('.mw-parser-output h2[id], .mw-parser-output h3[id]');
        $overlay.on('click', function(e) {
         var tocLinks = document.querySelectorAll('.toc a');
            if (e.target === this || e.target === $close[0]) {
                $overlay.hide();
                $('body').css('overflow', '');
            }
         });
          
          
         if (headings.length > 0 && tocLinks.length > 0) {
         $(document).on('keydown', function(e) {
             var highlightCurrentSection = function() {
             if (e.key === 'Escape') {
                 var scrollPos = window.scrollY + 100;
                 $overlay.hide();
                var current = null;
                 $('body').css('overflow', '');
                  
            }
                headings.forEach(function(heading) {
        });
                    if (heading.offsetTop <= scrollPos) {
    }
                        current = heading.id;
   
                    }
    /* ===== Reading Progress Bar ===== */
                });
    function initProgressBar() {
               
        var $bar = $('<div id="reading-progress"></div>').css({
                tocLinks.forEach(function(link) {
            position: 'fixed',
                    link.style.fontWeight = '';
            top: 0,
                    link.style.color = '';
            left: 0,
                    if (link.getAttribute('href') === '#' + current) {
            height: '3px',
                        link.style.fontWeight = 'bold';
            background: 'linear-gradient(90deg, #ff9900, #611e03)',
                        link.style.color = '#1a4d7a';
             width: '0%',
                    }
             zIndex: 99998,
                });
             transition: 'width 0.1s ease'
             };
        });
              
             window.addEventListener('scroll', highlightCurrentSection);
            highlightCurrentSection();
        }
          
          
         // ===== Back to top button =====
         $('body').append($bar);
        var backToTop = document.createElement('button');
        backToTop.textContent = '↑';
        backToTop.style.cssText = 'position:fixed;bottom:20px;right:20px;width:50px;height:50px;border-radius:50%;background:linear-gradient(135deg,#1a4d7a,#2c5aa0);color:white;border:none;font-size:24px;cursor:pointer;opacity:0;transition:all 0.3s;z-index:1000;box-shadow:0 4px 12px rgba(0,0,0,0.3)';
        backToTop.title = 'Back to top';
        document.body.appendChild(backToTop);
          
          
         backToTop.addEventListener('click', function() {
         $(window).on('scroll', function() {
             window.scrollTo({ top: 0, behavior: 'smooth' });
             var winHeight = $(window).height();
            var docHeight = $(document).height();
            var scrollTop = $(window).scrollTop();
            var progress = (scrollTop / (docHeight - winHeight)) * 100;
            $bar.css('width', Math.min(progress, 100) + '%');
        });
    }
   
    /* ===== Current Section Highlight ===== */
    function initSectionHighlight() {
        var $sections = $('h2, h3').filter(function() {
            return $(this).attr('id');
         });
         });
          
          
         window.addEventListener('scroll', function() {
         var $tocLinks = $('.toc a, .mw-toc a');
             backToTop.style.opacity = window.scrollY > 300 ? '1' : '0';
       
            backToTop.style.pointerEvents = window.scrollY > 300 ? 'auto' : 'none';
        $(window).on('scroll', function() {
             var scrollPos = $(window).scrollTop() + 100;
           
            $sections.each(function() {
                var $section = $(this);
                var sectionTop = $section.offset().top;
                var sectionId = $section.attr('id');
               
                if (scrollPos >= sectionTop) {
                    $tocLinks.removeClass('toc-active');
                    $tocLinks.filter('[href="#' + sectionId + '"]').addClass('toc-active');
                }
            });
         });
         });
          
          
     });
        // Add CSS for active TOC link
})();
        $('<style>.toc-active { font-weight: bold; color: #611e03 !important; }</style>').appendTo('head');
     }
   
    /* ===== Initialize All Features ===== */
    initInfoboxTabs();
    initSmoothScroll();
    initBackToTop();
    initLightbox();
    initProgressBar();
    initSectionHighlight();
   
    console.log('Toon Wiki enhancements loaded');
});

Latest revision as of 23:17, 22 December 2025

/* ============================================
   TOON WIKI - INTERACTIVE FEATURES
   ============================================ */

$(document).ready(function() {
    
    /* ===== Infobox Gallery Tabs ===== */
    function initInfoboxTabs() {
        $('.infobox-image .gallery').each(function() {
            var $gallery = $(this);
            var $items = $gallery.find('.gallerybox');
            
            if ($items.length < 2) return;
            
            // Create tab navigation
            var $tabs = $('<div class="infobox-tabs"></div>');
            
            $items.each(function(index) {
                var $item = $(this);
                var label = $item.find('.gallerytext p').text().trim() || ('Image ' + (index + 1));
                
                var $btn = $('<button class="tab-btn"></button>')
                    .text(label)
                    .attr('data-index', index)
                    .on('click', function() {
                        var idx = $(this).data('index');
                        $items.removeClass('active').hide();
                        $items.eq(idx).addClass('active').show();
                        $tabs.find('.tab-btn').removeClass('active');
                        $(this).addClass('active');
                    });
                
                if (index === 0) {
                    $btn.addClass('active');
                    $item.addClass('active').show();
                } else {
                    $item.hide();
                }
                
                $tabs.append($btn);
            });
            
            $gallery.parent().append($tabs);
        });
    }
    
    /* ===== Smooth Scroll for TOC Links ===== */
    function initSmoothScroll() {
        $('a[href^="#"]').on('click', function(e) {
            var target = $(this.hash);
            if (target.length) {
                e.preventDefault();
                $('html, body').animate({
                    scrollTop: target.offset().top - 60
                }, 400);
            }
        });
    }
    
    /* ===== Back to Top Button ===== */
    function initBackToTop() {
        var $btn = $('<button id="back-to-top" title="Back to top">↑</button>')
            .css({
                position: 'fixed',
                bottom: '20px',
                right: '20px',
                width: '44px',
                height: '44px',
                background: '#611e03',
                color: 'white',
                border: 'none',
                borderRadius: '50%',
                fontSize: '20px',
                cursor: 'pointer',
                display: 'none',
                zIndex: 9999,
                boxShadow: '0 2px 10px rgba(0,0,0,0.2)',
                transition: 'all 0.2s ease'
            })
            .on('click', function() {
                $('html, body').animate({ scrollTop: 0 }, 400);
            })
            .on('mouseenter', function() {
                $(this).css({ transform: 'scale(1.1)', background: '#8b4513' });
            })
            .on('mouseleave', function() {
                $(this).css({ transform: 'scale(1)', background: '#611e03' });
            });
        
        $('body').append($btn);
        
        $(window).on('scroll', function() {
            if ($(this).scrollTop() > 300) {
                $btn.fadeIn(200);
            } else {
                $btn.fadeOut(200);
            }
        });
    }
    
    /* ===== Image Lightbox ===== */
    function initLightbox() {
        var $overlay = $('<div id="lightbox-overlay"></div>').css({
            position: 'fixed',
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            background: 'rgba(0,0,0,0.9)',
            zIndex: 99999,
            display: 'none',
            cursor: 'zoom-out',
            justifyContent: 'center',
            alignItems: 'center'
        });
        
        var $img = $('<img id="lightbox-img">').css({
            maxWidth: '90%',
            maxHeight: '90%',
            boxShadow: '0 0 30px rgba(0,0,0,0.5)',
            borderRadius: '4px'
        });
        
        var $close = $('<button>&times;</button>').css({
            position: 'absolute',
            top: '20px',
            right: '30px',
            background: 'none',
            border: 'none',
            color: 'white',
            fontSize: '40px',
            cursor: 'pointer',
            lineHeight: 1
        });
        
        $overlay.append($img, $close);
        $('body').append($overlay);
        
        // Click handler for images
        $('.mw-parser-output img').not('.mw-file-element[width="20"]').on('click', function(e) {
            var fullSrc = $(this).closest('a').attr('href');
            if (fullSrc && fullSrc.match(/\.(png|jpg|jpeg|gif|webp)/i)) {
                e.preventDefault();
                $img.attr('src', fullSrc);
                $overlay.css('display', 'flex');
                $('body').css('overflow', 'hidden');
            }
        });
        
        // Close handlers
        $overlay.on('click', function(e) {
            if (e.target === this || e.target === $close[0]) {
                $overlay.hide();
                $('body').css('overflow', '');
            }
        });
        
        $(document).on('keydown', function(e) {
            if (e.key === 'Escape') {
                $overlay.hide();
                $('body').css('overflow', '');
            }
        });
    }
    
    /* ===== Reading Progress Bar ===== */
    function initProgressBar() {
        var $bar = $('<div id="reading-progress"></div>').css({
            position: 'fixed',
            top: 0,
            left: 0,
            height: '3px',
            background: 'linear-gradient(90deg, #ff9900, #611e03)',
            width: '0%',
            zIndex: 99998,
            transition: 'width 0.1s ease'
        });
        
        $('body').append($bar);
        
        $(window).on('scroll', function() {
            var winHeight = $(window).height();
            var docHeight = $(document).height();
            var scrollTop = $(window).scrollTop();
            var progress = (scrollTop / (docHeight - winHeight)) * 100;
            $bar.css('width', Math.min(progress, 100) + '%');
        });
    }
    
    /* ===== Current Section Highlight ===== */
    function initSectionHighlight() {
        var $sections = $('h2, h3').filter(function() {
            return $(this).attr('id');
        });
        
        var $tocLinks = $('.toc a, .mw-toc a');
        
        $(window).on('scroll', function() {
            var scrollPos = $(window).scrollTop() + 100;
            
            $sections.each(function() {
                var $section = $(this);
                var sectionTop = $section.offset().top;
                var sectionId = $section.attr('id');
                
                if (scrollPos >= sectionTop) {
                    $tocLinks.removeClass('toc-active');
                    $tocLinks.filter('[href="#' + sectionId + '"]').addClass('toc-active');
                }
            });
        });
        
        // Add CSS for active TOC link
        $('<style>.toc-active { font-weight: bold; color: #611e03 !important; }</style>').appendTo('head');
    }
    
    /* ===== Initialize All Features ===== */
    initInfoboxTabs();
    initSmoothScroll();
    initBackToTop();
    initLightbox();
    initProgressBar();
    initSectionHighlight();
    
    console.log('Toon Wiki enhancements loaded');
});