User:Monster Domosed/common.js: Difference between revisions

Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
mw.loader.load('https://deadlock.wiki/index.php?title=User:Monster_Domosed/Wiki_Redesign.js&action=raw&ctype=text/javascript');
mw.loader.load('https://deadlock.wiki/index.php?title=User:Monster_Domosed/Wiki_Redesign.js&action=raw&ctype=text/javascript');
/**
* Custom Tooltips — replaces Extension:Popups for .item-link-wrapper and .ability-link-wrapper links
*/
( function () {
    'use strict';
    var tooltipCache = {};
    var stylesLoaded = {}; // Track which stylesheets have been loaded
    var $tooltip = null;
    var currentTarget = null;
    var showTimer = null;
    var hideTimer = null;
    var SHOW_DELAY = 250;
    var HIDE_DELAY = 300;
    var EDGE_MARGIN = 10; // margin from viewport edges
    /* ─── Load and inject CSS from a template page ─── */
    function loadTemplateStyles( pageName, callback ) {
        // Already loaded, just callback
        if ( stylesLoaded[ pageName ] ) {
            if ( callback ) callback();
            return;
        }
        // Fetch the CSS content
        new mw.Api().get( {
            action: 'query',
            prop: 'revisions',
            titles: pageName,
            rvprop: 'content',
            rvslots: 'main',
            formatversion: 2
        } ).then( function ( data ) {
            if ( data.query && data.query.pages && data.query.pages[0] ) {
                var page = data.query.pages[0];
                if ( page.revisions && page.revisions[0] ) {
                    var css = page.revisions[0].slots.main.content;
                   
                    // Create a style element and inject the CSS
                    var $style = $( '<style>' )
                        .attr( 'data-template-styles', pageName )
                        .text( css )
                        .appendTo( 'head' );
                   
                    stylesLoaded[ pageName ] = true;
                    if ( callback ) callback();
                }
            }
        } ).catch( function ( error ) {
            console.error( 'Failed to load template styles:', pageName, error );
            if ( callback ) callback(); // Continue anyway
        } );
    }
    /* ─── Create tooltip DOM element ─── */
    function getTooltip() {
        if ( !$tooltip ) {
            $tooltip = $( '<div>' )
                .addClass( 'custom-item-tooltip' )
                .hide()
                .appendTo( document.body )
                .on( 'mouseenter', function () {
                    clearTimeout( hideTimer );
                } )
                .on( 'mouseleave', function () {
                    scheduleHide();
                } );
        }
        return $tooltip;
    }
    /* ─── Positioning ─── */
    function positionTooltip( anchor ) {
        var tt = getTooltip();
        var rect = anchor.getBoundingClientRect();
        var scrollTop = window.scrollY || document.documentElement.scrollTop;
        var scrollLeft = window.scrollX || document.documentElement.scrollLeft;
        var vpW = window.innerWidth;
        var vpH = window.innerHeight;
        // Render offscreen to measure actual dimensions
        tt.css( { top: -9999, left: -9999, visibility: 'hidden' } ).show();
        var ttW = tt.outerWidth();
        var ttH = tt.outerHeight();
        tt.css( 'visibility', '' );
        // ─── Vertical positioning ───
        var top;
        var spaceBelow = vpH - rect.bottom - 8;
        var spaceAbove = rect.top - 8;
        if ( spaceBelow >= ttH + EDGE_MARGIN ) {
            // Fits below the anchor
            top = rect.bottom + scrollTop + 8;
        } else if ( spaceAbove >= ttH + EDGE_MARGIN ) {
            // Fits above the anchor
            top = rect.top + scrollTop - ttH - 8;
        } else {
            // Doesn't fit above or below — pin to the edge with more space
            if ( spaceBelow >= spaceAbove ) {
                top = scrollTop + vpH - ttH - EDGE_MARGIN;
            } else {
                top = scrollTop + EDGE_MARGIN;
            }
        }
        // ─── Horizontal positioning ───
        var left = rect.left + scrollLeft;
        // Overflows right edge
        if ( left + ttW > scrollLeft + vpW - EDGE_MARGIN ) {
            left = scrollLeft + vpW - ttW - EDGE_MARGIN;
        }
        // Overflows left edge
        if ( left < scrollLeft + EDGE_MARGIN ) {
            left = scrollLeft + EDGE_MARGIN;
        }
        tt.css( { top: top, left: left } );
    }
    /* ability upgrade gadget CURRENTLY DOESN'T WORK WITH TOOLTIP*/
    /* ─── Initialize ability upgrade gadget for tooltip content ─── */
    function initAbilityUpgrades( $container ) {
        // Check if the gadget exists
        if ( !window.abilityUpgradeGadget ) {
            return;
        }
        var abilityCards = $container.find( '.ability-card' );
        if ( !abilityCards.length ) {
            return;
        }
        // Re-run the gadget's initialization for new ability cards
        abilityCards.each( function () {
            var container = this;
           
            // Add hover listeners
            $( container ).find( '.ability-upgrade' ).each( function () {
                var el = this;
                $( el ).off( 'mouseenter.abilityUpgrade mouseleave.abilityUpgrade' );
                $( el ).on( 'mouseenter.abilityUpgrade', function () {
                    window.abilityUpgradeGadget.onHover( container, el );
                } );
                $( el ).on( 'mouseleave.abilityUpgrade', function () {
                    window.abilityUpgradeGadget.onUnhover( container );
                } );
            } );
        } );
    }
    /* ─── Capitalize first letter of each word ─── */
    function toTitleCase( str ) {
        return str.replace( /\b\w/g, function ( char ) {
            return char.toUpperCase();
        } );
    }
    /* ─── Show tooltip ─── */
    function showTooltip( anchor, name, type ) {
        clearTimeout( hideTimer );
        clearTimeout( showTimer );
        showTimer = setTimeout( function () {
            var tt = getTooltip();
            currentTarget = anchor;
            // Normalize the name to title case for abilities
            var normalizedName = type === 'ability' ? toTitleCase( name ) : name;
            var cacheKey = type + ':' + normalizedName;
            // Update tooltip classes based on type
            tt.removeClass( 'ability-tooltip item-tooltip' );
            if ( type === 'ability' ) {
                tt.addClass( 'ability-tooltip' );
            } else if ( type === 'item' ) {
                tt.addClass( 'item-tooltip' );
            }
            // Already cached — show immediately
            if ( tooltipCache[ cacheKey ] ) {
                tt.html( tooltipCache[ cacheKey ] ).show();
                positionTooltip( anchor );
               
                // Re-initialize ability upgrades for cached content
                if ( type === 'ability' ) {
                    initAbilityUpgrades( tt );
                }
                return;
            }
            // Loading indicator — move offscreen before showing to prevent flash at old position
            tt.html( '<div class="custom-item-tooltip__loading">Loading…</div>' ).css( { top: -9999, left: -9999 } ).show();
            positionTooltip( anchor );
            // Build template text based on type
            var templateText;
            if ( type === 'item' ) {
                templateText = '{{Infobox item/auto|default=true|' + normalizedName + '}}';
            } else if ( type === 'ability' ) {
                templateText = '{{Ability card v2' +
                    '|hero_name={{#invoke:Abilities/utils|find_hero_by_ability|' + normalizedName + '}}' +
                    '|ability_num={{#invoke:Abilities|get_ability_number|{{#invoke:Abilities/utils|find_hero_by_ability|' + normalizedName + '}}|' + normalizedName + '}}' +
                    '}}';
            }
            // Function to fetch and display the template
            function fetchAndShowTemplate() {
                new mw.Api().post( {
                    action: 'parse',
                    text: templateText,
                    title: mw.config.get( 'wgPageName' ),
                    prop: 'text',
                    disablelimitreport: true,
                    disableeditsection: true,
                    wrapoutputclass: '',
                    format: 'json'
                } ).then( function ( data ) {
                    var html = data.parse.text[ '*' ];
                    tooltipCache[ cacheKey ] = html;
                    // Only show if the mouse is still on this element
                    if ( currentTarget === anchor ) {
                        tt.html( html ).show();
                        positionTooltip( anchor );
                       
                        // Initialize ability upgrades for new content
                        if ( type === 'ability' ) {
                            // Trigger MediaWiki's content hook so other gadgets can initialize
                            mw.hook( 'wikipage.content' ).fire( tt );
                            initAbilityUpgrades( tt );
                        }
                    }
                } ).catch( function () {
                    if ( currentTarget === anchor ) {
                        tt.hide();
                    }
                } );
            }
            // Load template styles only for items (abilities use inline styles)
            if ( type === 'item' ) {
                loadTemplateStyles( 'Template:Infobox item/styles.css', fetchAndShowTemplate );
            } else {
                // For abilities, just fetch and show directly
                fetchAndShowTemplate();
            }
        }, SHOW_DELAY );
    }
    /* ─── Hide tooltip ─── */
    function scheduleHide() {
        clearTimeout( showTimer );
        hideTimer = setTimeout( function () {
            if ( $tooltip ) {
                $tooltip.hide();
            }
            currentTarget = null;
        }, HIDE_DELAY );
    }
    /* ─── Attach handlers to wrapper ─── */
    function attachTooltipHandlers( $wrapper, name, type ) {
        // All links inside the wrapper, not just the first one
        var $links = $wrapper.find( 'a[href]' );
       
        // If no links found, attach to the wrapper itself (for icon-only mode)
        if ( !$links.length ) {
            $links = $wrapper;
        }
        $links.each( function () {
            var $link = $( this );
            // Remove native title from each link
            $link.removeAttr( 'title' );
            // Remove any existing handlers before attaching to prevent duplicates
            // if wikipage.content fires multiple times for the same elements
            $link.off( '.customTooltip' ).on( 'mouseenter.customTooltip', function () {
                showTooltip( this, name, type );
            } ).on( 'mouseleave.customTooltip', function () {
                scheduleHide();
            } );
        } );
    }
    /* ─── Hide tooltip on scroll to prevent it detaching from its anchor ─── */
    $( window ).on( 'scroll.customTooltip', function () {
        if ( $tooltip && $tooltip.is( ':visible' ) ) {
            $tooltip.hide();
            currentTarget = null;
        }
    } );
    /* ─── Initialization ─── */
    mw.hook( 'wikipage.content' ).add( function ( $content ) {
        // Don't attach handlers inside an existing tooltip — prevents tooltip-in-tooltip replacement bug
        if ( $content.closest( '.custom-item-tooltip' ).length ) return;
        // Item tooltips
        $content.find( '.item-link-wrapper' ).each( function () {
            var $wrapper = $( this );
            var itemName = $wrapper.attr( 'data-item-name' );
            if ( !itemName ) return;
            attachTooltipHandlers( $wrapper, itemName, 'item' );
        } );
        // Ability tooltips
        $content.find( '.ability-link-wrapper' ).each( function () {
            var $wrapper = $( this );
            var abilityName = $wrapper.attr( 'data-ability-name' );
            if ( !abilityName ) return;
            attachTooltipHandlers( $wrapper, abilityName, 'ability' );
        } );
    } );
} )();


/**
/**