MediaWiki:Common.js

Материал из Space Station 14 Вики

Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.

  • Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
  • Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
  • Internet Explorer / Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
  • Opera: Нажмите Ctrl+F5.
const IFRAME_CONFIG = {
    iframe1: {
        src: 'https://example.com',
        srcdoc: '',
        width: '600',
        height: '400',
        name: 'iframe1',
        allow: 'clipboard-write',
        referrerpolicy: 'no-referrer',
        sandbox: 'allow-same-origin',
        id: 'iframe1'
    },
    iframe2: {
        src: '',
        srcdoc: '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Bootstrap demo</title><link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script></head><body><div class="accordion" id="accordionExample"><div class="accordion-item"><h2 class="accordion-header"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> Accordion Item #1 </button></h2><div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionExample"><div class="accordion-body"><strong>This is the first item"s accordion body.</strong> It is shown by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It»s also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow. </div></div></div><div class="accordion-item"><h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"> Accordion Item #2 </button></h2><div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionExample"><div class="accordion-body"><strong>This is the second item»s accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It»s also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow. </div></div></div><div class="accordion-item"><h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree"> Accordion Item #3 </button></h2><div id="collapseThree" class="accordion-collapse collapse" data-bs-parent="#accordionExample"><div class="accordion-body"><strong>This is the third item»s accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It»s also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow. </div></div></div></div><a href="javascript:void(0);" onclick="(function(){ var name = prompt("Введите ваше имя"); if (name) { alert("Привет, " + name); } })();">Click me</a><button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#staticBackdrop"> Launch static backdrop modal </button><div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h1 class="modal-title fs-5" id="staticBackdropLabel">Modal title</h1><button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button></div><div class="modal-body"> ... </div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button><button type="button" class="btn btn-primary">Understood</button></div></div></div></div></body></html>',
        width: '600',
        height: '400',
        name: 'iframe2',
        allow: '',
        referrerpolicy: '',
        sandbox: '',
        id: 'iframe2'
    }
};


const currentURL = window.location.href;
const serversStatus = [{
  name: "server-main",
  connect: "https://game2.station14.ru/main/server/status",
  serverconnect: "game2.station14.ru/main/server"
}, {
  name: "server-nova",
  connect: "https://game2.station14.ru/nova/server/status",
  serverconnect: "game2.station14.ru/nova/server"
}, {
  name: "server-athara",
  connect: "https://game2.station14.ru/athara/server/status",
  serverconnect: "game2.station14.ru/athara/server"
}, {
  name: "server-solaris",
  connect: "https://game2.station14.ru/solaris/server/status",
  serverconnect: "game2.station14.ru/solaris/server"
}, {
  name: "server-echo",
  connect: "https://game2.station14.ru/echo/server/status",
  serverconnect: "game2.station14.ru/echo/server"
}, {
  name: "server-elysium",
  connect: "https://game2.station14.ru/elysium/server/status",
  serverconnect: "game2.station14.ru/elysium/server"
}, {
  name: "server-nebula",
  connect: "https://game2.station14.ru/nebula/server/status",
  serverconnect: "game2.station14.ru/nebula/server"
}, {
  name: "server-wl",
  connect: "https://game2.station14.ru/wl/server/status",
  serverconnect: "game2.station14.ru/wl/server"
}, {
  name: "server-frontier",
  connect: "https://api.codetabs.com/v1/proxy/?quest=https://arles.station14.ru/frontier-main/server/status",
  serverconnect: "arles.station14.ru/frontier-main/server"
}, {
  name: "server-fallout",
  connect: "https://api.codetabs.com/v1/proxy/?quest=http://188.92.78.98:1221/status",
  serverconnect: "188.92.78.98:1221"
}, {
  name: "server-marines",
  connect: "https://game1.station14.ru/marines-main/server/status",
  serverconnect: "game1.station14.ru/marines-main/server"
}, {
  name: "server-stalker",
  connect: "https://api.codetabs.com/v1/proxy/?quest=https://game.stalkers14.xyz/status",
  serverconnect: "game.stalkers14.xyz"
}];

function getXHRInfo(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject('Ошибка при выполнении запроса: ' + xhr.status);
      }
    };
    xhr.onerror = function() {
      reject('Ошибка при выполнении запроса');
    };
    xhr.send();
  });
}

function updateServerInfoByConnect(connect, name) {
  getServerTime().then(function(currentUnixTime) {
    return getXHRInfo(connect).then(function(serverData) {
      var unixStartRound = getUnixTime(serverData.round_start_time);
      var timeDisplay = unixStartRound ? secondsToDHM(currentUnixTime -
        unixStartRound) : "–";
      var currentPlayers = serverData.players;
      var maxPlayers = serverData.soft_max_players;
      // Вычисляем процент
      var percentage = maxPlayers > 0 ? Math.round((currentPlayers /
        maxPlayers) * 100) : 0;
      // Форматируем строку с количеством игроков
      var playersDisplay = currentPlayers && maxPlayers ? currentPlayers + '/' +
        maxPlayers : "–";
      // Функция для проверки на пустые данные
      function checkValue(value) {
        return value ? value : "–";
      }
      var serverInfo = {
        name: checkValue(serverData.name),
        round: checkValue(serverData.round_id),
        status: checkValue(serverData.run_level),
        map: checkValue(serverData.map),
        time: timeDisplay,
        players: playersDisplay,
        preset: checkValue(serverData.preset),
        percentage: percentage
      };
      updateServerInfo(serverInfo, name);
      //console.log("serverInfo", serverInfo, "currentUnixTime", currentUnixTime,"unixStartRound", unixStartRound);
    });
  }).catch(function(error) {
    console.error(error);
  });
}

function getUnixTime(dateString) {
  return Math.floor(new Date(dateString).getTime() / 1000); // Делим на 1000, чтобы получить секунды
}

function secondsToDHM(seconds) {
  var days = Math.floor(seconds / 86400);
  var hours = Math.floor((seconds % 86400) / 3600);
  var minutes = Math.floor((seconds % 3600) / 60);
  var result = "";
  if (days > 0) result += days + " дн, ";
  if (hours > 0) result += hours + " ч ";
  // Проверка на наличие минут
  if (minutes > 0) {
    result += minutes + " мин";
  } else {
    result += "–"; // Символ ожидания, если минут нет
  }
  return result.trim();
}

function getServerTime() {
  return fetch(
    'https://station14.ru/api.php?action=query&meta=siteinfo&siprop=general&format=json'
  ).then(function(response) {
    return response.json();
  }).then(function(data) {
    return getUnixTime(data.query.general.time);
  });
}

function updateServerInfo(serverObj, serverName) {
  var serverStatusFrame = document.getElementById(serverName);
  var statusText = {
    0: "Лобби",
    1: "Раунд идёт",
    2: "Завершение"
  };
  serverStatusFrame.querySelector(".serverInfoRoundSet").textContent = "#" +
    serverObj.round;
  serverStatusFrame.querySelector(".serverStatusSet").textContent = statusText[
    serverObj.status] || "Неизвестный статус";
  serverStatusFrame.querySelector(".serverMapSet").textContent = serverObj.map;
  serverStatusFrame.querySelector(".serverTimeSet").textContent = serverObj.time;
  serverStatusFrame.querySelector(".serverPlayersSet").textContent = serverObj.players;
  serverStatusFrame.querySelector(".serverPresetSet").textContent = serverObj.preset;
  // Получаем строку подключения из serversStatus
  const serverData = serversStatus.find(function(server) {
    return server.name === serverName;
  });
  var connectionStringElement = serverStatusFrame.querySelector(
    ".serverConnectSet");
  if (connectionStringElement) {
    connectionStringElement.textContent = serverData ? serverData.serverconnect :
      '-'; // Устанавливаем строку подключения
  }
  // Обновляем прогресс-бар
  var progressBar = serverStatusFrame.querySelector(".progressBar"); // Предполагаем, что у вас есть элемент для прогресс-бара
  if (progressBar) {
    progressBar.style.width = serverObj.percentage + '%'; // Устанавливаем ширину прогресс-бара
  }
  // Обновляем отображение процента
  var percentageDisplay = serverStatusFrame.querySelector(".percentageDisplay"); // Элемент для отображения процента
  if (percentageDisplay) {
    percentageDisplay.textContent = serverObj.percentage + '%'; // Устанавливаем текст с процентом
  }
}

function updateAllServersInfo() {
  serversStatus.forEach(function(server) {
    updateServerInfoByConnect(server.connect, server.name);
  });
}

function copyToClipboard(connectUrl) {
  const copyIcons = document.querySelectorAll('.copy-icon');
  copyIcons.forEach(function(icon) {
    icon.addEventListener('click', function() {
      // Находим предыдущий элемент
      const previousElement = this.previousElementSibling;
      if (previousElement) {
        // Получаем текстовое содержимое
        const textToCopy = previousElement.textContent;
        const tempInput = document.createElement('input');
        tempInput.value = textToCopy;
        document.body.appendChild(tempInput);
        tempInput.select();
        document.execCommand('copy');
        document.body.removeChild(tempInput);
        // Визуальный эффект
        this.style.opacity = '0.5'; // Сменяем прозрачность
        setTimeout(function() {
          this.style.opacity = '1'; // Возвращаем прозрачность
        }.bind(this), 200); // Через 1 секунду
      }
    });
  });
}

function customCSS(textCSS) {
  const styleSheet = document.createElement("style");
  styleSheet.textContent = textCSS;
  //console.log(textCSS);
  document.head.appendChild(styleSheet);
}

function createIframe(id) {
    const config = IFRAME_CONFIG[id];
    if (!config) return;

    const iframe = document.createElement('iframe');

    if (config.src && config.src !== "") {
        iframe.src = config.src;
    }

    if (config.srcdoc && config.srcdoc !== "") {
        iframe.srcdoc = config.srcdoc;
    }

    if (config.width && config.width !== "") {
        iframe.width = config.width;
    } else {
        iframe.width = '100%'; // Значение по умолчанию
    }

    if (config.height && config.height !== "") {
        iframe.height = config.height;
    } else {
        iframe.height = '400px'; // Значение по умолчанию
    }

    if (config.name && config.name !== "") {
        iframe.name = config.name;
    }

    if (config.allow && config.allow !== "") {
        iframe.allow = config.allow;
    }

    if (config.referrerpolicy && config.referrerpolicy !== "") {
        iframe.referrerPolicy = config.referrerpolicy;
    }

    if (config.sandbox && config.sandbox !== "") {
        iframe.sandbox = config.sandbox;
    }

    iframe.id = config.id || ''; // id может быть пустым, если не задано

    return iframe;
}



$(document).ready(function() {
  var serversStatus = document.querySelectorAll('.serversStatus');
  if (serversStatus.length > 0) {
    updateAllServersInfo();
    // Перезапускать каждую минуту (60000 мс)
    setInterval(updateAllServersInfo, 60000);
  }
  
  var divs = document.querySelectorAll('.customIFrame');
  for (var i = 0; i < divs.length; i++) {
    var div = divs[i];
    var id = div.id;
    var iframe = createIframe(id);
    if (iframe) {
        div.appendChild(iframe);
    }
  }

  var cssEls = document.querySelectorAll('.customCSS');
  if (cssEls.length > 0) {
    for (var j = 0; j < cssEls.length; j++) {
      const textCSS = cssEls[i].textContent;
      //console.log(textCSS);
      customCSS(textCSS);
    }
  }
  const copyServerConnectionIcon = document.querySelectorAll(
    '.copyServerConnectionIcon');
  copyServerConnectionIcon.forEach(function(icon) {
    icon.addEventListener('click', function() {
      const connectUrl = this.getAttribute('data-connect');
      copyToClipboard(connectUrl);
    });
  });
  copyToClipboard();
 
});
/*WikiEditor/Викификатор*/
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) !== -1) {
  mw.loader.load(
    '//ru.wikipedia.org/w/index.php?title=MediaWiki:Gadget-wikificator.js&action=raw&ctype=text/javascript'
  );
}
var customizeToolbar = function() {
  $('#wpTextbox1').wikiEditor('addToToolbar', {
    'section': 'advanced',
    'group': 'format',
    'tools': {
      'wikify': {
        label: 'Викификатор',
        type: 'button',
        icon: '//upload.wikimedia.org/wikipedia/commons/0/06/Wikify-toolbutton.png',
        action: {
          type: 'callback',
          execute: function(context) {
            Wikify();
          }
        }
      }
    }
  });
};
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) !== -1) {
  mw.loader.using('user.options', function() {
    if (mw.user.options.get('usebetatoolbar')) {
      mw.loader.using('ext.wikiEditor.toolbar', function() {
        $(document).ready(customizeToolbar);
      });
    }
  });
}
/**
 * Taken from https://wiki.ss220.space/index.php?title=MediaWiki:Common.js&oldid=35626
 */
/* Variables for interface text used throughout the script, for ease of translating */
var i18n = {
  // Collapsible elements and page loader
  hideText: 'скрыть',
  showText: 'показать',
  // Page loader
  loadErrorTitle: 'Возникла ошибка при загрузке содержимого'
};
var mcw = window.mcw = {};
/* Keep track of delegated events on dynamic content */
mcw.events = {};
/* Fired whenever wiki content is added. (#mw-content-text, live preview, load page, etc.) */
mw.hook('wikipage.content').add(function($wikipageContent) {
  /**
   * Page loader
   *
   * Allows a page to be downloaded and displayed on demand.
   * Use with Template:LoadPage and Template:LoadBox
   */
  (function() {
    var $loadPage = $wikipageContent.find('.load-page');
    if (!$loadPage.length) {
      return;
    }
    // We need the spinner to show loading is happening, but we don't want
    // to have a delay while the module downloads, so we'll load this now,
    // regardless of if something is clicked
    mw.loader.load('jquery.spinner');
    // Create button starting with hide text
    // Will be changed to the show text while calculating the maximum button size
    var $buttonTemplate = $('<span>').addClass(
      'mw-editsection-like load-page-button').append('[ ', $('<span>').addClass(
      'jslink').text(i18n.hideText), ' ]');
    var extractList = function($contentContainer, listClass) {
      var $content = $contentContainer.find('.mw-parser-output > ul > li > ul')
        .children(':not(.nbttree-inherited)');
      if (listClass) {
        $content.addClass(listClass);
      }
      return $content;
    };
    $loadPage.each(function() {
      var $body = $(this);
      var page = $body.data('page');
      if (!page) {
        return;
      }
      var template = $body.data('template');
      var treeview = $body.data('treeview');
      var treeviewClass = $body.data('treeviewclass');
      var $heading;
      var $contentContainer;
      var $content;
      var $button = $buttonTemplate.clone();
      var $buttonLink = $button.find('.jslink');
      if (treeview) {
        $heading = $body;
        $contentContainer = $('<div>');
      } else {
        $heading = $body.children().first();
        $contentContainer = $body.find('.load-page-content');
      }
      // Add the button
      $heading.append($button);
      // Move the edit button to the right spot
      $contentContainer.find('.mw-editsection, .mw-editsection-like').insertAfter(
        $button);
      // Find max button width, and set its min-width to it
      var hideWidth = $button.width();
      $buttonLink.text(i18n.showText);
      var showWidth = $button.width();
      if (hideWidth !== showWidth) {
        $button.css('min-width', hideWidth > showWidth ? hideWidth : showWidth);
      }
      $buttonLink.click(function() {
        if ($body.hasClass('pageloader-contentloaded')) {
          if ($buttonLink.text() === i18n.showText) {
            if (treeview) {
              $content.insertAfter($body);
            } else {
              $contentContainer.show();
            }
            $buttonLink.text(i18n.hideText);
          } else {
            if (treeview) {
              $content.detach();
            } else {
              $contentContainer.hide();
            }
            $buttonLink.text(i18n.showText);
          }
          return;
        }
        // See if this was loaded elsewhere before making a request
        var gotContent;
        $('.pageloader-contentloaded').each(function() {
          var $fLoader = $(this);
          if ($fLoader.data('page') === page && $fLoader.data(
              'pageloader-content')) {
            $contentContainer.html($fLoader.data('pageloader-content')).removeClass(
              'noscript');
            mw.hook('wikipage.content').fire($contentContainer);
            if (treeview) {
              $body.find('.noscript').remove();
              $content = extractList($contentContainer, treeviewClass);
              $content.insertAfter($body);
            }
            $buttonLink.text(i18n.hideText);
            $body.addClass('pageloader-contentloaded');
            gotContent = true;
            return false;
          }
        });
        if (gotContent) {
          return;
        }
        // Just in-case the spinner module is still not ready yet
        var $spinner = $();
        mw.loader.using('jquery.spinner', function() {
          // $spinner will be false if the content somehow loaded before the module did
          if ($spinner) {
            $spinner = $.createSpinner().addClass('mw-editsection-like').css(
              'min-width', $button.css('min-width'));
            $button.hide().after($spinner);
          }
        });
        var requestData = {
          action: 'parse',
          prop: 'text'
        };
        if (template) {
          requestData.page = page;
        } else {
          requestData.title = mw.config.get('wgPageName');
          requestData.text = '{' + '{:' + page + '}}';
        }
        new mw.Api().get(requestData).done(function(data) {
          var html = data.parse.text['*'];
          $contentContainer.html(html).removeClass('noscript');
          // Resolve self-links
          if (template) {
            var curPage = '/' + mw.config.get('wgPageName');
            $contentContainer.find('a').each(function() {
              var $link = $(this);
              if ($link.attr('href') === curPage) {
                $link.replaceWith($('<strong>').addClass('selflink').append(
                  $link.contents()));
              }
            });
            html = $contentContainer.html();
          }
          $body.data('pageloader-content', html);
          // Fire content hook on the new content, running all this stuff again and more :)
          mw.hook('wikipage.content').fire($contentContainer);
          if (treeview) {
            $body.find('.noscript').remove();
            $content = extractList($contentContainer, treeviewClass);
            $content.insertAfter($body);
          }
          $spinner.remove();
          $spinner = false;
          $buttonLink.text(i18n.hideText);
          $button.show();
          $body.addClass('pageloader-contentloaded');
        }).fail(function(_, error) {
          $spinner.remove();
          $spinner = false;
          $button.show();
          var errorText = '';
          if (error.textStatus) {
            errorText = error.textStatus;
          } else if (error.error) {
            errorText = error.error.info;
          }
          mw.notify(errorText, {
            title: i18n.loadErrorTitle,
            autoHide: false
          });
        });
      });
    });
  }());
  /**
   * Element animator
   *
   * Will cycle the active class on any child elements
   * within an element with the animated class.
   */
  (function() {
    if (!mcw.animate) {
      mcw.animate = setInterval(function() {
        $('.animated').each(function() {
          var $elem = $(this);
          var $current = $elem.children('.active');
          var $next = $current.nextAll(':not(.skip):first');
          // Loop back to the start
          if (!$next.length) {
            $next = $elem.children(':not(.skip):first');
          }
          $current.removeClass('active');
          $next.addClass('active');
        });
      }, 2000);
    }
  }());
});
// SS220 import end