Jump to content

MediaWiki:Common.js

From Utopia Game
Revision as of 10:58, 19 February 2026 by Sonja says (talk | contribs)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* Any JavaScript here will be loaded for all users on every page load. */

/* =========================================================
   Tabs (unchanged)
   ========================================================= */
(function () {
  const DISCORD_URL = 'https://discord.gg/t2Rp2dRvze';

  // Countdown target: Sat, 18 Apr 2026 00:00 UTC
  const COUNTDOWN_TARGET_UTC = Date.parse('2026-04-18T00:00:00Z');

  // Clock timezone (same for everyone)
  const CLOCK_TIMEZONE = 'Etc/UTC'; // change if desired

  // Game time anchor (real UTC -> game time)
  // At 2026-02-19 14:00:00 UTC, game time was Jan 21 YR2
  const GAME_ANCHOR_REAL_UTC = Date.parse('2026-02-19T14:00:00Z');
  const GAME_ANCHOR_MONTH_INDEX = 0; // Jan
  const GAME_ANCHOR_DAY = 21;        // 1..24
  const GAME_ANCHOR_YEAR = 2;        // YR2
  const GAME_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'];

  const pad2 = (n) => String(n).padStart(2, '0');

  function ensureDiscordLink(icons) {
    if (document.getElementById('custom-sticky-link')) return;

    const link = document.createElement('a');
    link.id = 'custom-sticky-link';
    link.href = DISCORD_URL;
    link.target = '_blank';
    link.className =
      'cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet';
    link.textContent = 'Join Us on Discord!';
    link.style.marginLeft = '10px';
    link.style.fontWeight = 'bold';

    icons.prepend(link);
  }

$(document).ready(function () {
  $('.wiki-tabs-container').each(function () {
    var $container = $(this);
    var $firstButton = $container.find('.wiki-tab-button').first();
    var firstTabId = $firstButton.data('tab');

    $firstButton.addClass('active');
    $container.find('#' + firstTabId).addClass('active').show();
  });

  $('.wiki-tab-button').on('click', function () {
    var $button = $(this);
    var tabId = $button.data('tab');
    var $container = $button.closest('.wiki-tabs-container');

    $container.find('.wiki-tab-button').removeClass('active');
    $container.find('.wiki-tab-pane').removeClass('active').hide();

    $button.addClass('active');
    $container.find('#' + tabId).addClass('active').show();
  });
});


/* =========================================================
   Sticky Header Widgets (Discord + Clock | Game Date | Countdown)
   CLEAN + FIXED:
   - No CSS inside JS (your old paste had CSS at the bottom of JS)
   - No   spacing hacks (spacing should be CSS)
   - One shared container for the 3 widgets
   ========================================================= */

  // Container that will hold ONLY the three widgets (not Discord, not default icons)
  function getStickyTimeContainer() {
    const icons = document.querySelector('.vector-sticky-header-icons');
    if (!icons) return null;

    let mid = document.getElementById('sticky-time-widgets');
    if (mid) return mid;

    // Make sure Discord is created first so we can insert after it
    ensureDiscordLink(icons);

    mid = document.createElement('span');
    mid.id = 'sticky-time-widgets';
    mid.className = 'sticky-time-widgets';

    const discord = document.getElementById('custom-sticky-link');
    if (discord && discord.parentNode === icons) {
      discord.insertAdjacentElement('afterend', mid);
    } else {
      icons.prepend(mid);
    }

    return mid;
  }

  function ensureClock(mid) {
    if (document.getElementById('sticky-clock')) return;

    const wrap = document.createElement('span');
    wrap.id = 'sticky-clock';
    wrap.className = 'sticky-clock header-widget';

    const label = document.createElement('span');
    label.className = 'header-widget__label';
    label.textContent = (CLOCK_TIMEZONE === 'Etc/UTC') ? 'UTC:' : 'Time:';

    const value = document.createElement('span');
    value.className = 'header-widget__value';
    value.dataset.role = 'clock';

    wrap.appendChild(label);
    wrap.appendChild(value);
    mid.appendChild(wrap);
  }

  function ensureGame(mid) {
    if (document.getElementById('sticky-game-time')) return;

    const wrap = document.createElement('span');
    wrap.id = 'sticky-game-time';
    wrap.className = 'sticky-game-time header-widget';

    const value = document.createElement('span');
    value.className = 'header-widget__value';
    value.dataset.role = 'game';

    wrap.appendChild(value);
    mid.appendChild(wrap);
  }

  function ensureCountdown(mid) {
    if (document.getElementById('sticky-countdown')) return;

    const wrap = document.createElement('span');
    wrap.id = 'sticky-countdown';
    wrap.className = 'sticky-countdown header-widget';

    const label = document.createElement('span');
    label.className = 'header-widget__label';
    label.textContent = 'Age ends in:';

    const value = document.createElement('span');
    value.className = 'header-widget__value';
    value.dataset.role = 'countdown';

    wrap.appendChild(label);
    wrap.appendChild(value);
    mid.appendChild(wrap);
  }

  function formatRemaining(ms) {
    if (ms <= 0) return '00:00:00';

    const totalSeconds = Math.floor(ms / 1000);
    const days = Math.floor(totalSeconds / 86400);
    const hours = Math.floor((totalSeconds % 86400) / 3600);
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const seconds = totalSeconds % 60;

    if (days > 0) return `${days}d ${pad2(hours)}:${pad2(minutes)}:${pad2(seconds)}`;
    return `${pad2(hours)}:${pad2(minutes)}:${pad2(seconds)}`;
  }

  function computeGameString(nowMs) {
    const hoursPassed = Math.floor((nowMs - GAME_ANCHOR_REAL_UTC) / 3600000);

    // Day increments every hour
    const dayIndex = (GAME_ANCHOR_DAY - 1) + hoursPassed;
    const dayNumber = ((dayIndex % 24) + 24) % 24 + 1;

    // Month increments every 24 hours
    const daysPassed = Math.floor(dayIndex / 24);

    // Month cycles (Jan..Jul)
    const totalMonthIndex = GAME_ANCHOR_MONTH_INDEX + daysPassed;
    const monthIndex = ((totalMonthIndex % 7) + 7) % 7;

    // Year increments every 7 real days
    const yearsPassed = Math.floor(totalMonthIndex / 7);
    const year = GAME_ANCHOR_YEAR + yearsPassed;

    return `Current Game Date: ${GAME_MONTHS[monthIndex]} ${dayNumber} YR${year}`;
  }

  const clockFmt = new Intl.DateTimeFormat('en-GB', {
    timeZone: CLOCK_TIMEZONE,
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false
  });

  function updateAll() {
    const now = Date.now();

    const clockVal = document.querySelector('#sticky-time-widgets [data-role="clock"]');
    if (clockVal) clockVal.textContent = clockFmt.format(new Date(now));

    const gameVal = document.querySelector('#sticky-time-widgets [data-role="game"]');
    if (gameVal) gameVal.textContent = computeGameString(now);

    const countdownVal = document.querySelector('#sticky-time-widgets [data-role="countdown"]');
    if (countdownVal) countdownVal.textContent = formatRemaining(COUNTDOWN_TARGET_UTC - now);
  }

  mw.hook('wikipage.content').add(function () {
    const icons = document.querySelector('.vector-sticky-header-icons');
    if (!icons) return;

    const mid = getStickyTimeContainer();
    if (!mid) return;

    ensureClock(mid);
    ensureGame(mid);
    ensureCountdown(mid);

    // Start update loop once
    if (!window.__stickyTimeWidgetsStarted) {
      window.__stickyTimeWidgetsStarted = true;
      updateAll();
      setInterval(updateAll, 1000);
    } else {
      // If navigating, ensure values refresh immediately
      updateAll();
    }
  });

})();