Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Common.js: Difference between revisions

MediaWiki interface page
No edit summary
No edit summary
Line 664: Line 664:
});
});


/* ==============================================================
  BRACKET SYSTEM v2.0 JS — Add to MediaWiki:Common.js
  ============================================================== */
$(function () {
    /* ── Open modal on match card click ─────────────────────── */
    $(document).on('click', '.bk-match-clickable', function (e) {
        e.stopPropagation();
        var $m      = $(this);
        var team1  = $m.attr('data-team1')  || 'TBD';
        var team2  = $m.attr('data-team2')  || 'TBD';
        var score1  = $m.attr('data-score1')  || '';
        var score2  = $m.attr('data-score2')  || '';
        var bo      = $m.attr('data-bo')      || '';
        var date    = $m.attr('data-date')    || '';
        var casters = $m.attr('data-casters') || '';
        var vod    = $m.attr('data-vod')    || '';
        var notes  = $m.attr('data-notes')  || '';
        var $overlay = $m.closest('.bk-root').find('.bk-modal-overlay');
        if (!$overlay.length) { return; }
        $overlay.find('.bk-modal-t1').text(team1);
        $overlay.find('.bk-modal-t2').text(team2);
        var hasScore = (score1 !== '' || score2 !== '');
        $overlay.find('.bk-modal-s1').text(hasScore ? (score1 || '0') : '\u2014');
        $overlay.find('.bk-modal-s2').text(hasScore ? (score2 || '0') : '\u2014');
        $overlay.find('.bk-modal-dash').toggle(hasScore);
        var $boRow = $overlay.find('.bk-modal-bo').empty();
        if (bo) {
            $boRow.append($('<b>').text('Format')).append(' Best of ' + bo);
        }
        var $dateRow = $overlay.find('.bk-modal-date').empty();
        if (date) {
            var dateText = date;
            try {
                var clean = date.indexOf('T') !== -1 ? date : date.replace(' ', 'T');
                var d = new Date(clean);
                if (!isNaN(d.getTime())) {
                    dateText = new Intl.DateTimeFormat('en-IN', {
                        year: 'numeric', month: 'short', day: 'numeric',
                        hour: 'numeric', minute: '2-digit',
                        timeZoneName: 'short'
                    }).format(d);
                }
            } catch (ex) {}
            $dateRow.append($('<b>').text('Date')).append(' ' + dateText);
        }
        var $casterRow = $overlay.find('.bk-modal-casters').empty();
        if (casters) {
            $casterRow.append($('<b>').text('Casters')).append(' ' + casters);
        }
        var $notesRow = $overlay.find('.bk-modal-notes').empty();
        if (notes) {
            $notesRow.append($('<b>').text('Notes')).append(' ' + notes);
        }
        var $vodRow = $overlay.find('.bk-modal-vod').empty();
        if (vod) {
            var $link = $('<a>').attr('href', vod).attr('target', '_blank')
                .attr('rel', 'noopener noreferrer').text('Watch VOD');
            $vodRow.append($('<b>').text('VOD')).append(' ').append($link);
        }
        var win = $m.attr('data-winner') || '';
        $overlay.find('.bk-modal-s1').removeClass('bk-modal-win bk-modal-lose');
        $overlay.find('.bk-modal-s2').removeClass('bk-modal-win bk-modal-lose');
        if (win === '1' || win === team1) {
            $overlay.find('.bk-modal-s1').addClass('bk-modal-win');
            $overlay.find('.bk-modal-s2').addClass('bk-modal-lose');
        } else if (win === '2' || win === team2) {
            $overlay.find('.bk-modal-s2').addClass('bk-modal-win');
            $overlay.find('.bk-modal-s1').addClass('bk-modal-lose');
        }
        $overlay.fadeIn(150);
    });
    $(document).on('click', '.bk-modal-close', function (e) {
        e.stopPropagation();
        $('.bk-modal-overlay').fadeOut(150);
    });
    $(document).on('click', '.bk-modal-overlay', function (e) {
        if ($(e.target).hasClass('bk-modal-overlay')) {
            $('.bk-modal-overlay').fadeOut(150);
        }
    });
    $(document).on('keydown', function (e) {
        if (e.key === 'Escape' || e.keyCode === 27) {
            $('.bk-modal-overlay').fadeOut(150);
        }
    });
});


/* ================================================================
/* ================================================================
   BRACKET CONNECTOR LINES SVG overlay drawn after layout
   BRACKET CONNECTOR LINES
  Measures actual card positions so lines always hit card centres
  SVG drawn after layout — measures real card positions
   ================================================================ */
   ================================================================ */


$(function () {
$(function () {


     function drawBracketConnectors() {
     var LINE_COLOR = '#94a3b8';
    var LINE_WIDTH = '2';
    var svgNS = 'http://www.w3.org/2000/svg';


        /* ── GSL connectors ──────────────────────────────────── */
    /* Helper: create an SVG line element */
         $('.bk-gsl').each(function () {
    function makeLine(x1, y1, x2, y2) {
            var $root    = $(this);
         var l = document.createElementNS(svgNS, 'line');
            var rootOff  = $root.offset();
        l.setAttribute('x1', Math.round(x1));
            var rootW    = $root.outerWidth();
        l.setAttribute('y1', Math.round(y1));
            var rootH    = $root.outerHeight();
        l.setAttribute('x2', Math.round(x2));
        l.setAttribute('y2', Math.round(y2));
        l.setAttribute('stroke', LINE_COLOR);
        l.setAttribute('stroke-width', LINE_WIDTH);
        return l;
    }


            /* Remove old SVG if redrawing */
    /* Helper: get centre-right point of an element relative to a parent */
             $root.find('.bk-connector-svg').remove();
    function rightMid($el, $parent) {
        var elOff  = $el.offset();
        var parOff = $parent.offset();
        return {
             x: (elOff.left - parOff.left) + $el.outerWidth(),
            y: (elOff.top  - parOff.top) + $el.outerHeight() / 2
        };
    }


            var svgNS = 'http://www.w3.org/2000/svg';
    /* Helper: get centre-left point of an element relative to a parent */
            var svg  = document.createElementNS(svgNS, 'svg');
    function leftMid($el, $parent) {
            svg.setAttribute('class', 'bk-connector-svg');
        var elOff  = $el.offset();
            svg.setAttribute('width',  rootW);
        var parOff = $parent.offset();
            svg.setAttribute('height', rootH);
        return {
             svg.style.position = 'absolute';
             x: elOff.left - parOff.left,
             svg.style.top = '0';
             y: (elOff.top - parOff.top) + $el.outerHeight() / 2
            svg.style.left = '0';
        };
    }


            function midY($el) {
    /* Helper: inject a fresh SVG into a container */
                if (!$el.length) return 0;
    function makeSVG($container) {
                var off = $el.offset();
        $container.find('.bk-connector-svg').remove();
                return (off.top - rootOff.top) + $el.outerHeight() / 2;
        var svg = document.createElementNS(svgNS, 'svg');
            }
        svg.setAttribute('class', 'bk-connector-svg');
            function midX($el, side) {
        svg.setAttribute('width',  $container.outerWidth());
                if (!$el.length) return 0;
        svg.setAttribute('height', $container.outerHeight());
                var off = $el.offset();
        svg.style.position = 'absolute';
                if (side === 'right') return (off.left - rootOff.left) + $el.outerWidth();
        svg.style.top      = '0';
                return off.left - rootOff.left;
        svg.style.left    = '0';
            }
        svg.style.overflow = 'visible';
        svg.style.pointerEvents = 'none';
        svg.style.zIndex  = '0';
        $container.prepend(svg);
        return svg;
    }


            function line(x1, y1, x2, y2) {
    /* ── GSL ─────────────────────────────────────────────────── */
                var l = document.createElementNS(svgNS, 'line');
    function drawGSL($root) {
                l.setAttribute('x1', Math.round(x1));
        var $q1  = $root.find('.bk-gsl-top .bk-match').first();
                l.setAttribute('y1', Math.round(y1));
        var $elim = $root.find('.bk-gsl-bot .bk-match').first();
                l.setAttribute('x2', Math.round(x2));
        var $q2  = $root.find('.bk-gsl-mid .bk-match').first();
                l.setAttribute('y2', Math.round(y2));
        var $fin  = $root.find('.bk-gsl-final .bk-match').first();
                svg.appendChild(l);
            }


            var $col1 = $root.find('.bk-gsl-col1');
        if (!$q1.length || !$elim.length || !$q2.length || !$fin.length) { return; }
            var $col2 = $root.find('.bk-gsl-col2');
            var $col3 = $root.find('.bk-gsl-col3');


            var $q1  = $col1.find('.bk-gsl-top .bk-match');
        var svg = makeSVG($root);
            var $elim = $col1.find('.bk-gsl-bot .bk-match');
            var $q2  = $col2.find('.bk-gsl-mid .bk-match');
            var $fin  = $col3.find('.bk-gsl-final .bk-match');


            if ($q1.length && $q2.length) {
        /* Points */
                var q1Right  = midX($q1,  'right');
        var q1R  = rightMid($q1,  $root);
                var q1Mid    = midY($q1);
        var elimR = rightMid($elim, $root);
                var elimRight= midX($elim, 'right');
        var q2L  = leftMid($q2,    $root);
                var elimMid  = midY($elim);
        var q2R   = rightMid($q2,  $root);
                var q2Left   = midX($q2,  'left');
        var finL  = leftMid($fin,  $root);
                var q2Mid    = midY($q2);
                var midJoinX = q2Left - 10; /* vertical join point */


                /* Q1 horizontal arm → join */
        /* Midpoint X where the vertical join sits between col1 and col2 */
                line(q1Right,  q1Mid,  midJoinX, q1Mid);
        var joinX = q2L.x - 10;
                /* Elim horizontal arm → join */
                line(elimRight, elimMid, midJoinX, elimMid);
                /* Vertical join between Q1 and Elim arms */
                line(midJoinX, q1Mid, midJoinX, elimMid);
                /* Horizontal from join midpoint → Q2 left */
                var joinMidY = (q1Mid + elimMid) / 2;
                line(midJoinX, joinMidY, q2Left, joinMidY);
            }


            if ($q2.length && $fin.length) {
        /* Q1 horizontal → join */
                var q2Right = midX($q2, 'right');
        svg.appendChild(makeLine(q1R.x, q1R.y, joinX, q1R.y));
                var q2MidY  = midY($q2);
        /* Elim horizontal → join */
                var finLeft = midX($fin, 'left');
        svg.appendChild(makeLine(elimR.x, elimR.y, joinX, elimR.y));
                var finMidY = midY($fin);
        /* Vertical connecting Q1 and Elim arms */
                var midX2  = q2Right + (finLeft - q2Right) / 2;
        svg.appendChild(makeLine(joinX, q1R.y, joinX, elimR.y));
        /* Horizontal from vertical midpoint → Q2 left */
        var vMidY = (q1R.y + elimR.y) / 2;
        svg.appendChild(makeLine(joinX, vMidY, q2L.x, vMidY));


                /* Q2 → midpoint horizontal */
        /* Q2 → Grand Final (step connector if heights differ) */
                line(q2Right, q2MidY, midX2, q2MidY);
        var midX = q2R.x + (finL.x - q2R.x) / 2;
                /* Vertical drop/rise to Final midY */
        svg.appendChild(makeLine(q2R.x, q2R.y, midX,   q2R.y));
                line(midX2, q2MidY, midX2, finMidY);
        svg.appendChild(makeLine(midX, q2R.y, midX,   finL.y));
                /* midpoint → Final left */
        svg.appendChild(makeLine(midX, finL.y, finL.x, finL.y));
                line(midX2, finMidY, finLeft, finMidY);
    }
            }


             $root.prepend(svg);
    /* ── Single Elim ─────────────────────────────────────────── */
    function drawSingleElim($root) {
        /* Group cards by column */
        var cols = [];
        $root.find('.bk-col').each(function () {
             var cards = [];
            $(this).find('.bk-match').each(function () {
                cards.push($(this));
            });
            cols.push(cards);
         });
         });


         /* ── Single Elim connectors ──────────────────────────── */
         if (cols.length < 2) { return; }
        $('.bk-single-elim').each(function () {
 
            var $root  = $(this);
        var svg = makeSVG($root);
            var rootOff = $root.offset();
            var rootW  = $root.outerWidth();
            var rootH  = $root.outerHeight();


            $root.find('.bk-connector-svg').remove();
        for (var c = 0; c < cols.length - 1; c++) {
            var thisCol = cols[c];
            var nextCol = cols[c + 1];
            /* Each pair of cards in thisCol feeds into one card in nextCol */
            for (var i = 0; i < nextCol.length; i++) {
                var $top    = thisCol[i * 2];
                var $bot    = thisCol[i * 2 + 1];
                var $target = nextCol[i];
                if (!$top || !$bot || !$target) { continue; }


            var svgNS = 'http://www.w3.org/2000/svg';
                var topR  = rightMid($top,    $root);
            var svg  = document.createElementNS(svgNS, 'svg');
                var botR  = rightMid($bot,   $root);
            svg.setAttribute('class', 'bk-connector-svg');
                var tarL  = leftMid($target, $root);
            svg.setAttribute('width',  rootW);
                var joinX = topR.x + ($root.find('.bk-col').eq(c + 1).offset().left
            svg.setAttribute('height', rootH);
                            - $root.find('.bk-col').eq(c).offset().left) / 2;
            svg.style.position = 'absolute';
            svg.style.top  = '0';
            svg.style.left = '0';


            $root.find('.has-connector .bk-match').each(function () {
                /* Horizontal arms from each card */
                var $card = $(this);
                 svg.appendChild(makeLine(topR.x, topR.y, joinX, topR.y));
                 var off  = $card.offset();
                 svg.appendChild(makeLine(botR.x, botR.y, joinX, botR.y));
                var x1    = (off.left - rootOff.left) + $card.outerWidth();
                 /* Vertical join */
                 var y1    = (off.top  - rootOff.top) + $card.outerHeight() / 2;
                 svg.appendChild(makeLine(joinX, topR.y, joinX, botR.y));
                 var x2    = x1 + 44;
                 /* Horizontal to next card */
                 var l    = document.createElementNS(svgNS, 'line');
                 var midY = (topR.y + botR.y) / 2;
                l.setAttribute('x1', Math.round(x1));
                 svg.appendChild(makeLine(joinX, midY, tarL.x, midY));
                 l.setAttribute('y1', Math.round(y1));
            }
                 l.setAttribute('x2', Math.round(x2));
        }
                 l.setAttribute('y2', Math.round(y2 || y1));
    }
                svg.appendChild(l);
            });


            $root.prepend(svg);
    /* ── Main draw function ──────────────────────────────────── */
         });
    function drawAll() {
        $('.bk-gsl').each(function () { drawGSL($(this)); });
         $('.bk-single-elim').each(function () { drawSingleElim($(this)); });
     }
     }


     /* Draw on load and on window resize */
     /* Draw after fonts/images settle, redraw on resize */
     setTimeout(drawBracketConnectors, 200);
     setTimeout(drawAll, 300);
     $(window).on('resize', function () {
     $(window).on('resize', function () {
         clearTimeout(window._bkResizeTimer);
         clearTimeout(window._bkResizeTimer);
         window._bkResizeTimer = setTimeout(drawBracketConnectors, 150);
         window._bkResizeTimer = setTimeout(drawAll, 200);
     });
     });
});
});

Revision as of 02:01, 20 March 2026

/* Any JavaScript here will be loaded for all users on every page load. */

/* ==========================================================
   REMOVE DECIMALS (.0) FROM CARGO TABLES
   ========================================================== */
$(document).ready(function() {
    // Target only cells inside the auto-rank table
    $('.auto-rank td').each(function() {
        var text = $(this).text();
        
        // If the text looks like a number ending in .0 (e.g., "93.0")
        if (/^\d+\.0$/.test(text)) {
            // Replace it with just the integer part
            $(this).text(parseInt(text));
        }
    });
});


/* ==========================================================
   INDIAN CURRENCY FORMATTER (₹ Symbol Version)
   ========================================================== */
$(document).ready(function() {
    $('.indian-currency').each(function() {
        // 1. Get text and clean it (Remove commas AND existing ₹ symbol)
        var rawNum = $(this).text().trim().replace(/,/g, '').replace(/₹/g, '');
        
        // 2. Convert to a real number
        var number = parseFloat(rawNum);
        
        // 3. Check if it's a valid number
        if (!isNaN(number)) {
            // 4. Format to Indian Style (en-IN) - Automatically adds ₹
            var formatted = number.toLocaleString('en-IN', {
                style: 'currency', 
                currency: 'INR', 
                maximumFractionDigits: 0 
            });
            
            // 5. Apply directly (This keeps the ₹ symbol)
            $(this).text(formatted); 
        }
    });
});

/* ========================================================
   UNIVERSAL FILTER SYSTEM (Centralized Logic)
   ======================================================== */
$(function() {
    
    // MASTER FUNCTION: updates the entire wrapper based on current button states
    function refreshWrapperState($wrapper) {
        // 1. Get the current settings
        var activeView = $wrapper.find('.filter-btn[data-filter-type^="view_mode"].active').attr('data-value') || 'overall';
        var activeGroup = ($wrapper.find('.filter-btn[data-filter-type^="group_"].active').attr('data-value') || 'all').toString().trim();
        var filterType = $wrapper.find('.filter-btn[data-filter-type^="view_mode"].active').attr('data-filter-type'); // e.g., view_mode_The Grind

        // 2. Hide all Views (Overall & Matches), then Show the Active One
        $wrapper.find('.filterable-content[data-type^="view_mode"]').hide();
        
        // Find the specific view (e.g., Match 2 table)
        // We use the filterType to ensure we don't accidentally grab a view from a different stage
        var $activeTable = $wrapper.find('.filterable-content[data-type="' + filterType + '"][data-value="' + activeView + '"]');
        $activeTable.fadeIn(100);

        // 3. Apply Group Filter to the rows INSIDE the active table
        // We target ANY row (tr) that has a data-value attribute
        var $rows = $activeTable.find('tr[data-value]');
        
        if (activeGroup === 'all') {
            $rows.show();
        } else {
            $rows.each(function() {
                var $row = $(this);
                var rowGroup = ($row.attr('data-value') || "").toString().trim();
                
                if (rowGroup === activeGroup) {
                    $row.show();
                } else {
                    $row.hide();
                }
            });
        }
    }

    // CLICK HANDLER
    $(document).on('click', '.filter-btn', function() {
        var $btn = $(this);
        var filterType = $btn.attr('data-filter-type'); 
        var value = $btn.attr('data-value');

        // Visual Update (Active State)
        $btn.siblings('.filter-btn').removeClass('active');
        $btn.addClass('active');
        
        // LOGIC A: MASTER STAGE (Big Tabs like "Round 1")
        if (filterType.indexOf('master_stage') !== -1) {
            $('.filterable-content[data-type="' + filterType + '"]').hide();
            $('.filterable-content[data-type="' + filterType + '"][data-value="' + value + '"]').fadeIn(200);
            
            // Optional: When switching stages, trigger a refresh on that stage's wrapper
            var $newStage = $('.filterable-content[data-type="' + filterType + '"][data-value="' + value + '"]');
            var $innerWrapper = $newStage.find('.standings-wrapper');
            if ($innerWrapper.length) refreshWrapperState($innerWrapper);

        // LOGIC B: STANDINGS FILTERS (View or Group)
        } else {
            // Just run the master refresh on the parent wrapper
            var $wrapper = $btn.closest('.standings-wrapper');
            refreshWrapperState($wrapper);
        }
    });

    // AUTO-INIT
    setTimeout(function() {
        if (!$('.filter-btn[data-filter-type="master_stage_selector"].active').length) {
            $('.filter-btn[data-filter-type="master_stage_selector"]').first().click();
        }
    }, 500);
});

/* ==========================================================
   SMART HORIZONTAL SCROLL (Mouse Wheel)
   ========================================================== */
mw.loader.using(['jquery'], function () {
    $(function() {
        $('.standings-wrapper').on('wheel', function(e) {
            var container = this;
            var delta = e.originalEvent.deltaY;

            // Only act if the table is wider than the screen
            if (container.scrollWidth > container.clientWidth) {
                
                // Calculate current scroll position vs max scroll
                var scrollLeft = container.scrollLeft;
                var scrollWidth = container.scrollWidth;
                var clientWidth = container.clientWidth;
                var maxScroll = scrollWidth - clientWidth;
                
                // Check if we are at the edges (allow 1px buffer for browsers)
                var atRightEdge = (scrollLeft >= maxScroll - 1);
                var atLeftEdge = (scrollLeft <= 1);

                // LOGIC:
                // 1. If scrolling RIGHT (delta > 0) and NOT at end -> Scroll Table
                // 2. If scrolling LEFT (delta < 0) and NOT at start -> Scroll Table
                // 3. Otherwise -> Do nothing (Let page scroll naturally)
                
                if ((delta > 0 && !atRightEdge) || (delta < 0 && !atLeftEdge)) {
                    container.scrollLeft += delta;
                    e.preventDefault(); // Stop page scroll ONLY when table is moving
                }
            }
        });
    });
});

/* Active Tier Filter Highlight */
$(function() {
    var urlParams = new URLSearchParams(window.location.search);
    var tier = urlParams.get('tier');
    var $buttons = $('#tier-filters .filter-btn');
    $buttons.removeClass('active');
    if (tier) {
        $('#tier-filters .filter-btn[data-tier="' + tier + '"]').addClass('active');
    } else {
        $buttons.first().addClass('active');
    }
});


/* ==========================================================
   LIVE UPDATE NOTIFIER (Secure Version)
   Checks every 60 seconds if the page has a newer revision.
   ========================================================== */
$(document).ready(function() {
    // Only run on View mode
    if (mw.config.get('wgAction') !== 'view') return;

    var currentRevId = mw.config.get('wgCurRevisionId');
    var pageTitle = mw.config.get('wgPageName');

    function checkForUpdates() {
        new mw.Api().get({
            action: 'query',
            prop: 'info',
            titles: pageTitle,
            indexpageids: 1
        }).done(function(data) {
            var page = data.query.pages[data.query.pageids[0]];
            // If server has a newer ID
            if (page.lastrevid > currentRevId) {
                showUpdateToast();
            }
        });
    }

    function showUpdateToast() {
        // Prevent duplicate buttons
        if ($('#update-notification').length > 0) return;

        // 1. Create the Button Safely (No raw HTML strings)
        var $icon = $('<i>').addClass('fa-solid fa-rotate').css('margin-right', '8px');
        
        var $btn = $('<div>')
            .attr('id', 'update-notification')
            .text(' New updates available. Click to refresh.')
            .prepend($icon)
            .css({
                'position': 'fixed',
                'bottom': '30px',
                'left': '50%',
                'transform': 'translateX(-50%)',
                'background-color': '#00509d',
                'color': 'white',
                'padding': '12px 25px',
                'border-radius': '50px',
                'cursor': 'pointer',
                'box-shadow': '0 4px 15px rgba(0,0,0,0.3)',
                'z-index': '9999',
                'font-family': 'sans-serif',
                'font-weight': 'bold',
                'display': 'flex',
                'align-items': 'center'
            })
            .on('click', function() {
                location.reload();
            });

        // 2. Add to page with animation
        $('body').append($btn);
        $btn.hide().fadeIn(500).css('bottom', '30px'); 
    }

    // Run check every 60 seconds
    setInterval(checkForUpdates, 60000);
});

/* ==========================================================
   CLEAN PAGE TITLES (Fixes H1, Sticky Header, AND Browser Tab)
   Turns "BGMI/Teams/Orangutan" -> "Orangutan"
   ========================================================== */
$(function() {
    var pageTitle = mw.config.get('wgTitle');

    // Only run if we are deep in a structure (contains slash)
    if (pageTitle.indexOf('/') !== -1) {
        // Get the text after the last slash and replace underscores
        var cleanTitle = pageTitle.split('/').pop().replace(/_/g, ' '); 

        // 1. Fix the On-Page Header (H1)
        $('.firstHeading').text(cleanTitle);

        // 2. Fix the Citizen Skin Sticky Header
        $('.citizen-header__title').text(cleanTitle);

        // 3. Fix the Browser Tab Title
        var siteName = mw.config.get('wgSiteName') || 'eSportsAmaze';
        document.title = cleanTitle + ' - ' + siteName;
    }
});


/* ==========================================================
MANUAL AD INJECTION (Safe Mode / Firewall Bypass)
========================================================== */
$(document).ready(function() {
// 1. YOUR AD DETAILS
var clientID = "ca-pub-9380457493130804";
var bottomSlotID = "9418872470";
var topSlotID = "1308895278";

// 2. Build the Bottom/Sidebar Ad Unit (Standard Size)
var $bottomAdContainer = $('<div>')
.addClass('wiki-ad-bottom')
.css({
'margin-top': '20px',
'text-align': 'center',
'min-height': '250px'
});
var $bottomLabel = $('<div>')
.css({ 'font-size': '10px', 'color': '#999', 'margin-bottom': '5px' })
.text('ADVERTISEMENT');
var $bottomIns = $('<ins>')
.addClass('adsbygoogle')
.css('display', 'block')
.attr('data-ad-client', clientID)
.attr('data-ad-slot', bottomSlotID)
.attr('data-ad-format', 'auto')
.attr('data-full-width-responsive', 'true');
$bottomAdContainer.append($bottomLabel, $bottomIns);

// 3. Build the Top Mobile Ad Unit (Horizontal Only)
var $topAdContainer = $('<div>')
  .addClass('wiki-ad-top')
  .css({
    'margin-bottom': '15px',
    'text-align': 'center',
    'height': '70px',
    'max-height': '70px',
    'overflow': 'hidden'
  });

var $topLabel = $('<div>')
  .css({ 'font-size': '10px', 'color': '#999', 'margin-bottom': '3px', 'line-height': '10px' })
  .text('ADVERTISEMENT');

var $topIns = $('<ins>')
  .addClass('adsbygoogle')
  .css({ 'display': 'inline-block', 'width': '320px', 'height': '50px' })
  .attr('data-ad-client', clientID)
  .attr('data-ad-slot', topSlotID);

$topAdContainer.append($topLabel, $topIns);

// 4. Inject into the Page based strictly on device width
var $toc = $('#citizen-toc');
var $footer = $('#catlinks');
var $content = $('#mw-content-text');

if ($(window).width() > 1000) {
// === DESKTOP LOGIC (1 Ad Only) ===
if ($toc.length > 0) {
// If TOC exists, put it after the TOC
$toc.after($bottomAdContainer);
} else {
// If no TOC on desktop, put it at the bottom
if ($footer.length > 0) $footer.before($bottomAdContainer);
else $content.after($bottomAdContainer);
}
// Push AdSense once
try { (adsbygoogle = window.adsbygoogle || []).push({}); } catch (e) { console.log(e); }

} else {
// === MOBILE LOGIC (2 Ads) ===
// Inject Top Ad above content
$content.before($topAdContainer);

// Inject Bottom Ad at the end
if ($footer.length > 0) $footer.before($bottomAdContainer);
else $content.after($bottomAdContainer);

// Push AdSense twice for two ads
try { (adsbygoogle = window.adsbygoogle || []).push({}); } catch (e) { console.log(e); }
try { (adsbygoogle = window.adsbygoogle || []).push({}); } catch (e) { console.log(e); }
}
});


/* ==========================================================
   AUTO TIMEZONE CONVERTER (For Calendar & Schedules)
   Converts elements with <span class="local-time" data-utc="...">
   ========================================================== */
$(function() {
    $('.local-time').each(function() {
        var utcString = $(this).attr('data-utc');
        if (!utcString) return;

        var dateObj = new Date(utcString);
        
        // Check if date is valid
        if (isNaN(dateObj)) return;

        // Format to User's Local Time (e.g., "2:30 PM")
        var localTime = new Intl.DateTimeFormat('default', {
            hour: 'numeric',
            minute: '2-digit',
            timeZoneName: 'short'
        }).format(dateObj);

        // Update the text
        $(this).text(localTime);
    });
});

/* ==========================================================
   INTERACTIVE ESPORTS CALENDAR ENGINE (Stream Button)
   ========================================================== */
$(function() {
    $('.esports-calendar-container').each(function() {
        var $container = $(this);
        var $dataNode = $container.find('.calendar-data');
        if (!$dataNode.length) return;

        var scheduleData = {};
        try { scheduleData = JSON.parse($dataNode.text()); } 
        catch(e) { console.error("Calendar Parse Error", e); return; }

        var allDates = Object.keys(scheduleData).sort();
        if (allDates.length === 0) return;

        var today = new Date();
        var todayStr = today.getFullYear() + "-" + String(today.getMonth()+1).padStart(2,'0') + "-" + String(today.getDate()).padStart(2,'0');
        
        var targetDate = null;
        if (scheduleData[todayStr]) {
            targetDate = todayStr; 
        } else {
            var futureDates = allDates.filter(function(d) { return d > todayStr; });
            if (futureDates.length > 0) {
                targetDate = futureDates[0]; 
            } else {
                targetDate = allDates[allDates.length - 1]; 
            }
        }

        var currentMonth = new Date(targetDate).getMonth();
        var currentYear = new Date(targetDate).getFullYear();

        var $datesGrid = $container.find('.calendar-grid-dates');
        var $monthYearLabel = $container.find('.cal-month-year');
        var $detailsContent = $container.find('.cal-details-content');

        var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];

        function formatLocalTime(timeStr) {
            var d;
            if (timeStr.indexOf("T") !== -1) { d = new Date(timeStr); } 
            else { d = new Date("2000-01-01T" + timeStr + ":00Z"); } 
            
            if (isNaN(d)) return timeStr;
            return new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }).format(d);
        }

        function renderCalendar() {
            $datesGrid.empty();
            $monthYearLabel.text(monthNames[currentMonth] + " " + currentYear);

            var firstDayRaw = new Date(currentYear, currentMonth, 1).getDay();
            var firstDay = (firstDayRaw === 0) ? 6 : firstDayRaw - 1;

            var daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();

            for (var i = 0; i < firstDay; i++) { $datesGrid.append('<div class="cal-day empty"></div>'); }

            for (var day = 1; day <= daysInMonth; day++) {
                var dateStr = currentYear + "-" + String(currentMonth + 1).padStart(2, '0') + "-" + String(day).padStart(2, '0');
                var $dayDiv = $('<div class="cal-day"></div>').text(day).attr('data-date', dateStr);

                if (scheduleData[dateStr]) {
                    $dayDiv.addClass('has-events');
                    
                    var dayStages = [...new Set(scheduleData[dateStr].map(function(e){ return e.stage || "Matches"; }))];
                    var stageText = dayStages.length === 1 ? dayStages[0] : dayStages.length + " Stages";
                    $dayDiv.append('<div class="cal-day-stage">' + stageText + '</div>');

                    $dayDiv.on('click', function() {
                        $container.find('.cal-day').removeClass('active');
                        $(this).addClass('active');
                        showDetails($(this).attr('data-date'));
                    });
                }
                $datesGrid.append($dayDiv);
            }
            
            var $targetElem = $datesGrid.find('.cal-day[data-date="'+targetDate+'"]');
            if ($targetElem.length) { $targetElem.addClass('active'); showDetails(targetDate); }
        }

        function showDetails(dateStr) {
            $detailsContent.empty().show();
            var dayEvents = scheduleData[dateStr];
            if (!dayEvents) return;

            var stages = {};
            dayEvents.forEach(function(row) {
                var sName = row.stage || "Matches";
                if (!stages[sName]) stages[sName] = [];
                stages[sName].push(row);
            });

            for (var stageName in stages) {
                var stageRows = stages[stageName];
                
                // MULTI-STREAM LOGIC
                var streamData = stageRows[0].stream || "";
                var streams = {};
                try {
                    if (streamData.startsWith("{")) {
                        streams = JSON.parse(streamData);
                    } else if (streamData) {
                        streams["Watch"] = streamData; // Fallback for old data
                    }
                } catch(e) {}

                var streamBtns = "";
                for (var label in streams) {
                    var url = streams[label];
                    if (!url.match(/^https?:\/\//)) url = "https://" + url; 
                    
                    // Auto-Detect Icon (Twitch vs YouTube)
                    var icon = '<i class="fa-brands fa-youtube"></i>';
                    if (url.indexOf("twitch.tv") !== -1) {
                        icon = '<i class="fa-brands fa-twitch"></i>';
                    }
                    
                    streamBtns += '<a href="' + url + '" target="_blank" class="cal-stream-btn">' + icon + ' ' + label + '</a>';
                }
                
                var streamHtml = streamBtns ? '<div class="cal-stream-group">' + streamBtns + '</div>' : '';

                $detailsContent.append('<div class="cal-stage-header-wrap"><div class="cal-stage-header">' + stageName + '</div>' + streamHtml + '</div>');
                
                stageRows.forEach(function(row) {
                    row.data.forEach(function(match) {
                        var localTime = formatLocalTime(match.time);
                        
                        var html = '<div class="cal-match-row"><div class="cal-m-time">' + localTime + '</div>';

                        if (row.match_type === "BR") {
                            var map = match.map || "TBD";
                            var grp = match.group ? '<span class="cal-m-group">' + match.group + '</span>' : '';
                            html += '<div class="cal-m-info"><span class="cal-m-map">' + map + '</span>' + grp + '</div>';
                        } else {
                            var t1 = match.t1 || "TBD", t2 = match.t2 || "TBD";
                            var grp = match.group ? '<span class="cal-m-group">' + match.group + '</span>' : '';
                            html += '<div class="cal-m-info"><span class="cal-m-teams">' + t1 + ' vs ' + t2 + '</span>' + grp + '</div>';
                        }
                        html += '</div>';
                        $detailsContent.append(html);
                    });
                });
            }
        }

        $container.find('.cal-prev').on('click', function() {
            currentMonth--; if (currentMonth < 0) { currentMonth = 11; currentYear--; }
            renderCalendar();
        });
        $container.find('.cal-next').on('click', function() {
            currentMonth++; if (currentMonth > 11) { currentMonth = 0; currentYear++; }
            renderCalendar();
        });

        renderCalendar();
    });
});


/* ── COMMON.JS: dropdown toggle ── */
$(document).on('click', '.espa-dropdown-trigger', function(e) {
    e.stopPropagation();
    var $t = $(this);
    var $menu = $t.siblings('.espa-dropdown-menu');
    $t.toggleClass('open');
    $menu.toggleClass('open');
});
$(document).on('click', function() {
    $('.espa-dropdown-trigger').removeClass('open');
    $('.espa-dropdown-menu').removeClass('open');
});

/* ==============================================================
   BRACKET SYSTEM v2.0 JS — Add to MediaWiki:Common.js
   ============================================================== */

$(function () {

    /* ── Open modal on match card click ─────────────────────── */
    $(document).on('click', '.bk-match-clickable', function (e) {
        e.stopPropagation();

        var $m      = $(this);
        var team1   = $m.attr('data-team1')   || 'TBD';
        var team2   = $m.attr('data-team2')   || 'TBD';
        var score1  = $m.attr('data-score1')  || '';
        var score2  = $m.attr('data-score2')  || '';
        var bo      = $m.attr('data-bo')      || '';
        var date    = $m.attr('data-date')    || '';
        var casters = $m.attr('data-casters') || '';
        var vod     = $m.attr('data-vod')     || '';
        var notes   = $m.attr('data-notes')   || '';

        var $overlay = $m.closest('.bk-root').find('.bk-modal-overlay');
        if (!$overlay.length) { return; }

        $overlay.find('.bk-modal-t1').text(team1);
        $overlay.find('.bk-modal-t2').text(team2);

        var hasScore = (score1 !== '' || score2 !== '');
        $overlay.find('.bk-modal-s1').text(hasScore ? (score1 || '0') : '\u2014');
        $overlay.find('.bk-modal-s2').text(hasScore ? (score2 || '0') : '\u2014');
        $overlay.find('.bk-modal-dash').toggle(hasScore);

        /* Best-of */
        var $boRow = $overlay.find('.bk-modal-bo').empty();
        if (bo) {
            $boRow.append($('<b>').text('Format')).append(' Best of ' + bo);
        }

        /* Date — local timezone */
        var $dateRow = $overlay.find('.bk-modal-date').empty();
        if (date) {
            var dateText = date;
            try {
                var clean = date.indexOf('T') !== -1 ? date : date.replace(' ', 'T');
                var d = new Date(clean);
                if (!isNaN(d.getTime())) {
                    dateText = new Intl.DateTimeFormat('en-IN', {
                        year: 'numeric', month: 'short', day: 'numeric',
                        hour: 'numeric', minute: '2-digit',
                        timeZoneName: 'short'
                    }).format(d);
                }
            } catch (ex) { /* keep raw string */ }
            $dateRow.append($('<b>').text('Date')).append(' ' + dateText);
        }

        /* Casters */
        var $casterRow = $overlay.find('.bk-modal-casters').empty();
        if (casters) {
            $casterRow.append($('<b>').text('Casters')).append(' ' + casters);
        }

        /* Notes */
        var $notesRow = $overlay.find('.bk-modal-notes').empty();
        if (notes) {
            $notesRow.append($('<b>').text('Notes')).append(' ' + notes);
        }

        /* VOD */
        var $vodRow = $overlay.find('.bk-modal-vod').empty();
        if (vod) {
            var $link = $('<a>').attr('href', vod).attr('target', '_blank')
                .attr('rel', 'noopener noreferrer').text('Watch VOD');
            $vodRow.append($('<b>').text('VOD')).append(' ').append($link);
        }

        /* Score win/lose highlight */
        var win = $m.attr('data-winner') || '';
        $overlay.find('.bk-modal-s1').removeClass('bk-modal-win bk-modal-lose');
        $overlay.find('.bk-modal-s2').removeClass('bk-modal-win bk-modal-lose');
        if (win === '1' || win === team1) {
            $overlay.find('.bk-modal-s1').addClass('bk-modal-win');
            $overlay.find('.bk-modal-s2').addClass('bk-modal-lose');
        } else if (win === '2' || win === team2) {
            $overlay.find('.bk-modal-s2').addClass('bk-modal-win');
            $overlay.find('.bk-modal-s1').addClass('bk-modal-lose');
        }

        $overlay.fadeIn(150);
    });

    /* ── Close: X button ────────────────────────────────────── */
    $(document).on('click', '.bk-modal-close', function (e) {
        e.stopPropagation();
        $('.bk-modal-overlay').fadeOut(150);
    });

    /* ── Close: click outside modal box ─────────────────────── */
    $(document).on('click', '.bk-modal-overlay', function (e) {
        if ($(e.target).hasClass('bk-modal-overlay')) {
            $('.bk-modal-overlay').fadeOut(150);
        }
    });

    /* ── Close: Escape key ───────────────────────────────────── */
    $(document).on('keydown', function (e) {
        if (e.key === 'Escape' || e.keyCode === 27) {
            $('.bk-modal-overlay').fadeOut(150);
        }
    });

});


/* ==============================================================
   BRACKET SYSTEM v2.0 JS — Add to MediaWiki:Common.js
   ============================================================== */

$(function () {

    /* ── Open modal on match card click ─────────────────────── */
    $(document).on('click', '.bk-match-clickable', function (e) {
        e.stopPropagation();

        var $m      = $(this);
        var team1   = $m.attr('data-team1')   || 'TBD';
        var team2   = $m.attr('data-team2')   || 'TBD';
        var score1  = $m.attr('data-score1')  || '';
        var score2  = $m.attr('data-score2')  || '';
        var bo      = $m.attr('data-bo')      || '';
        var date    = $m.attr('data-date')    || '';
        var casters = $m.attr('data-casters') || '';
        var vod     = $m.attr('data-vod')     || '';
        var notes   = $m.attr('data-notes')   || '';

        var $overlay = $m.closest('.bk-root').find('.bk-modal-overlay');
        if (!$overlay.length) { return; }

        $overlay.find('.bk-modal-t1').text(team1);
        $overlay.find('.bk-modal-t2').text(team2);

        var hasScore = (score1 !== '' || score2 !== '');
        $overlay.find('.bk-modal-s1').text(hasScore ? (score1 || '0') : '\u2014');
        $overlay.find('.bk-modal-s2').text(hasScore ? (score2 || '0') : '\u2014');
        $overlay.find('.bk-modal-dash').toggle(hasScore);

        var $boRow = $overlay.find('.bk-modal-bo').empty();
        if (bo) {
            $boRow.append($('<b>').text('Format')).append(' Best of ' + bo);
        }

        var $dateRow = $overlay.find('.bk-modal-date').empty();
        if (date) {
            var dateText = date;
            try {
                var clean = date.indexOf('T') !== -1 ? date : date.replace(' ', 'T');
                var d = new Date(clean);
                if (!isNaN(d.getTime())) {
                    dateText = new Intl.DateTimeFormat('en-IN', {
                        year: 'numeric', month: 'short', day: 'numeric',
                        hour: 'numeric', minute: '2-digit',
                        timeZoneName: 'short'
                    }).format(d);
                }
            } catch (ex) {}
            $dateRow.append($('<b>').text('Date')).append(' ' + dateText);
        }

        var $casterRow = $overlay.find('.bk-modal-casters').empty();
        if (casters) {
            $casterRow.append($('<b>').text('Casters')).append(' ' + casters);
        }

        var $notesRow = $overlay.find('.bk-modal-notes').empty();
        if (notes) {
            $notesRow.append($('<b>').text('Notes')).append(' ' + notes);
        }

        var $vodRow = $overlay.find('.bk-modal-vod').empty();
        if (vod) {
            var $link = $('<a>').attr('href', vod).attr('target', '_blank')
                .attr('rel', 'noopener noreferrer').text('Watch VOD');
            $vodRow.append($('<b>').text('VOD')).append(' ').append($link);
        }

        var win = $m.attr('data-winner') || '';
        $overlay.find('.bk-modal-s1').removeClass('bk-modal-win bk-modal-lose');
        $overlay.find('.bk-modal-s2').removeClass('bk-modal-win bk-modal-lose');
        if (win === '1' || win === team1) {
            $overlay.find('.bk-modal-s1').addClass('bk-modal-win');
            $overlay.find('.bk-modal-s2').addClass('bk-modal-lose');
        } else if (win === '2' || win === team2) {
            $overlay.find('.bk-modal-s2').addClass('bk-modal-win');
            $overlay.find('.bk-modal-s1').addClass('bk-modal-lose');
        }

        $overlay.fadeIn(150);
    });

    $(document).on('click', '.bk-modal-close', function (e) {
        e.stopPropagation();
        $('.bk-modal-overlay').fadeOut(150);
    });

    $(document).on('click', '.bk-modal-overlay', function (e) {
        if ($(e.target).hasClass('bk-modal-overlay')) {
            $('.bk-modal-overlay').fadeOut(150);
        }
    });

    $(document).on('keydown', function (e) {
        if (e.key === 'Escape' || e.keyCode === 27) {
            $('.bk-modal-overlay').fadeOut(150);
        }
    });

});

/* ================================================================
   BRACKET CONNECTOR LINES
   SVG drawn after layout — measures real card positions
   ================================================================ */

$(function () {

    var LINE_COLOR = '#94a3b8';
    var LINE_WIDTH = '2';
    var svgNS = 'http://www.w3.org/2000/svg';

    /* Helper: create an SVG line element */
    function makeLine(x1, y1, x2, y2) {
        var l = document.createElementNS(svgNS, 'line');
        l.setAttribute('x1', Math.round(x1));
        l.setAttribute('y1', Math.round(y1));
        l.setAttribute('x2', Math.round(x2));
        l.setAttribute('y2', Math.round(y2));
        l.setAttribute('stroke', LINE_COLOR);
        l.setAttribute('stroke-width', LINE_WIDTH);
        return l;
    }

    /* Helper: get centre-right point of an element relative to a parent */
    function rightMid($el, $parent) {
        var elOff  = $el.offset();
        var parOff = $parent.offset();
        return {
            x: (elOff.left - parOff.left) + $el.outerWidth(),
            y: (elOff.top  - parOff.top)  + $el.outerHeight() / 2
        };
    }

    /* Helper: get centre-left point of an element relative to a parent */
    function leftMid($el, $parent) {
        var elOff  = $el.offset();
        var parOff = $parent.offset();
        return {
            x: elOff.left - parOff.left,
            y: (elOff.top - parOff.top) + $el.outerHeight() / 2
        };
    }

    /* Helper: inject a fresh SVG into a container */
    function makeSVG($container) {
        $container.find('.bk-connector-svg').remove();
        var svg = document.createElementNS(svgNS, 'svg');
        svg.setAttribute('class', 'bk-connector-svg');
        svg.setAttribute('width',  $container.outerWidth());
        svg.setAttribute('height', $container.outerHeight());
        svg.style.position = 'absolute';
        svg.style.top      = '0';
        svg.style.left     = '0';
        svg.style.overflow = 'visible';
        svg.style.pointerEvents = 'none';
        svg.style.zIndex   = '0';
        $container.prepend(svg);
        return svg;
    }

    /* ── GSL ─────────────────────────────────────────────────── */
    function drawGSL($root) {
        var $q1   = $root.find('.bk-gsl-top .bk-match').first();
        var $elim = $root.find('.bk-gsl-bot .bk-match').first();
        var $q2   = $root.find('.bk-gsl-mid .bk-match').first();
        var $fin  = $root.find('.bk-gsl-final .bk-match').first();

        if (!$q1.length || !$elim.length || !$q2.length || !$fin.length) { return; }

        var svg = makeSVG($root);

        /* Points */
        var q1R   = rightMid($q1,   $root);
        var elimR = rightMid($elim, $root);
        var q2L   = leftMid($q2,    $root);
        var q2R   = rightMid($q2,   $root);
        var finL  = leftMid($fin,   $root);

        /* Midpoint X where the vertical join sits between col1 and col2 */
        var joinX = q2L.x - 10;

        /* Q1 horizontal → join */
        svg.appendChild(makeLine(q1R.x, q1R.y, joinX, q1R.y));
        /* Elim horizontal → join */
        svg.appendChild(makeLine(elimR.x, elimR.y, joinX, elimR.y));
        /* Vertical connecting Q1 and Elim arms */
        svg.appendChild(makeLine(joinX, q1R.y, joinX, elimR.y));
        /* Horizontal from vertical midpoint → Q2 left */
        var vMidY = (q1R.y + elimR.y) / 2;
        svg.appendChild(makeLine(joinX, vMidY, q2L.x, vMidY));

        /* Q2 → Grand Final (step connector if heights differ) */
        var midX = q2R.x + (finL.x - q2R.x) / 2;
        svg.appendChild(makeLine(q2R.x, q2R.y, midX,   q2R.y));
        svg.appendChild(makeLine(midX,  q2R.y, midX,   finL.y));
        svg.appendChild(makeLine(midX,  finL.y, finL.x, finL.y));
    }

    /* ── Single Elim ─────────────────────────────────────────── */
    function drawSingleElim($root) {
        /* Group cards by column */
        var cols = [];
        $root.find('.bk-col').each(function () {
            var cards = [];
            $(this).find('.bk-match').each(function () {
                cards.push($(this));
            });
            cols.push(cards);
        });

        if (cols.length < 2) { return; }

        var svg = makeSVG($root);

        for (var c = 0; c < cols.length - 1; c++) {
            var thisCol = cols[c];
            var nextCol = cols[c + 1];
            /* Each pair of cards in thisCol feeds into one card in nextCol */
            for (var i = 0; i < nextCol.length; i++) {
                var $top    = thisCol[i * 2];
                var $bot    = thisCol[i * 2 + 1];
                var $target = nextCol[i];
                if (!$top || !$bot || !$target) { continue; }

                var topR  = rightMid($top,    $root);
                var botR  = rightMid($bot,    $root);
                var tarL  = leftMid($target,  $root);
                var joinX = topR.x + ($root.find('.bk-col').eq(c + 1).offset().left
                            - $root.find('.bk-col').eq(c).offset().left) / 2;

                /* Horizontal arms from each card */
                svg.appendChild(makeLine(topR.x, topR.y, joinX, topR.y));
                svg.appendChild(makeLine(botR.x, botR.y, joinX, botR.y));
                /* Vertical join */
                svg.appendChild(makeLine(joinX, topR.y, joinX, botR.y));
                /* Horizontal to next card */
                var midY = (topR.y + botR.y) / 2;
                svg.appendChild(makeLine(joinX, midY, tarL.x, midY));
            }
        }
    }

    /* ── Main draw function ──────────────────────────────────── */
    function drawAll() {
        $('.bk-gsl').each(function () { drawGSL($(this)); });
        $('.bk-single-elim').each(function () { drawSingleElim($(this)); });
    }

    /* Draw after fonts/images settle, redraw on resize */
    setTimeout(drawAll, 300);
    $(window).on('resize', function () {
        clearTimeout(window._bkResizeTimer);
        window._bkResizeTimer = setTimeout(drawAll, 200);
    });

});