|
|
| 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' );
| |
| } );
| |
| } );
| |
|
| |
| } )();
| |
|
| |
|
| /** | | /** |