|
Мітки: Очищення Скасовано |
| Рядок 1: |
Рядок 1: |
| <html>
| |
| <div>
| |
| <style>
| |
| .hidden { display: none !important; }
| |
| .fade-in {
| |
| animation: fadeIn 0.4s ease-in-out;
| |
| }
| |
| @keyframes fadeIn {
| |
| from { opacity: 0; }
| |
| to { opacity: 1; }
| |
| }
| |
| </style>
| |
| <style>
| |
| html, body {
| |
| height: 100%;
| |
| margin: 0;
| |
| background-color: #121212;
| |
| color: #ffffff;
| |
| }
| |
|
| |
|
| .page-wrapper {
| |
| display: flex;
| |
| flex-direction: column;
| |
| min-height: 100vh;
| |
| }
| |
|
| |
| h5 {
| |
| font-size: 1.64rem;
| |
| line-height: 110%;
| |
| margin: 1.093rem 0 0.656rem 0;
| |
| position: relative;
| |
| padding-bottom: 12px;
| |
| text-align: center;
| |
| }
| |
|
| |
| h5::after {
| |
| content: '';
| |
| position: absolute;
| |
| left: 5%;
| |
| bottom: 0;
| |
| height: 2px;
| |
| width: 90%;
| |
| background-color: #388e3c;
| |
| border-radius: 1px;
| |
| }
| |
|
| |
| main {
| |
| flex: 1 0 auto;
| |
| }
| |
|
| |
| nav {
| |
| background-color: #1b1b1b;
| |
| }
| |
|
| |
| .btn-green {
| |
| background-color: #388e3c !important;
| |
| transition: transform 0.2s ease, box-shadow 0.2s ease;
| |
| }
| |
|
| |
| .btn-green:hover {
| |
| transform: translateY(-2px);
| |
| box-shadow: 0 4px 8px rgba(56, 142, 60, 0.6);
| |
| }
| |
|
| |
| .btn-green:active {
| |
| transform: translateY(1px);
| |
| box-shadow: 0 2px 4px rgba(56, 142, 60, 0.4);
| |
| }
| |
|
| |
| .content {
| |
| display: flex;
| |
| margin-top: 20px;
| |
| }
| |
|
| |
| .main-content {
| |
| width: 70%;
| |
| padding: 20px;
| |
| }
| |
|
| |
| .side-menu {
| |
| width: 30%;
| |
| background-color: #2c2c2c;
| |
| padding: 20px;
| |
| border-left: 2px solid #388e3c;
| |
| margin-bottom: 20px;
| |
| }
| |
|
| |
| footer {
| |
| background-color: #1b1b1b;
| |
| padding: 20px;
| |
| color: #9e9e9e;
| |
| text-align: center;
| |
| }
| |
|
| |
| #player-list {
| |
| font-size: 13px;
| |
| padding-left: 0;
| |
| list-style: none;
| |
| max-height: 150px;
| |
| overflow-y: auto;
| |
| }
| |
|
| |
| #player-list li {
| |
| display: flex;
| |
| align-items: center;
| |
| gap: 8px;
| |
| margin-bottom: 4px;
| |
| white-space: nowrap;
| |
| padding-bottom: 5px;
| |
| box-shadow: 0 1px 1px rgba(56, 142, 60, 0.6);
| |
| }
| |
|
| |
| table.placeholders-table {
| |
| width: 100%;
| |
| border-collapse: collapse;
| |
| margin-top: 20px;
| |
| }
| |
|
| |
| table.placeholders-table th,
| |
| table.placeholders-table td {
| |
| border: 1px solid #388e3c;
| |
| padding: 8px;
| |
| text-align: left;
| |
| }
| |
|
| |
| table.placeholders-table th {
| |
| background-color: #388e3c;
| |
| color: #fff;
| |
| }
| |
| </style>
| |
| <script>
| |
| document.addEventListener('DOMContentLoaded', function () {
| |
| const serverAddress = @json($serverAddress);
| |
| const playerName = @json($playerName);
| |
|
| |
| const onlineBlock = document.getElementById('server-online');
| |
| const offlineBlock = document.getElementById('server-offline');
| |
| const versionEl = document.getElementById('server-version');
| |
| const iconEl = document.getElementById('icon-img');
| |
| const motdEl = document.getElementById('motd');
| |
| const playersEl = document.getElementById('players-online');
| |
| const playerList = document.getElementById('player-list');
| |
| const uptimeEl = document.getElementById('server-uptime');
| |
|
| |
| function applyFadeInOnce(block) {
| |
| if (block.classList.contains('hidden')) {
| |
| block.classList.remove('hidden');
| |
| block.classList.add('fade-in');
| |
| block.addEventListener('animationend', () => {
| |
| block.classList.remove('fade-in');
| |
| }, { once: true });
| |
| }
| |
| }
| |
|
| |
| function parseMinecraftColors(text) {
| |
| const colors = {
| |
| '0': '#000000', '1': '#0000AA', '2': '#00AA00', '3': '#00AAAA',
| |
| '4': '#AA0000', '5': '#AA00AA', '6': '#FFAA00', '7': '#AAAAAA',
| |
| '8': '#555555', '9': '#5555FF', 'a': '#55FF55', 'b': '#55FFFF',
| |
| 'c': '#FF5555', 'd': '#FF55FF', 'e': '#FFFF55', 'f': '#FFFFFF',
| |
| };
| |
| const formats = { 'l': 'b', 'n': 'u', 'm': 's', 'o': 'i' };
| |
|
| |
| let result = '', openTags = [];
| |
| text = (text || '').replace(/&/g, '§');
| |
|
| |
| for (let i = 0; i < text.length; i++) {
| |
| if (text[i] === '§') {
| |
| const code = text[i + 1]?.toLowerCase();
| |
|
| |
| if (code === 'x' && i + 13 < text.length) {
| |
| const hex = `#${text[i + 2]}${text[i + 3]}${text[i + 4]}${text[i + 5]}${text[i + 6]}${text[i + 7]}`;
| |
| while (openTags.length && openTags[openTags.length - 1] === 'span') {
| |
| result += `</${openTags.pop()}>`;
| |
| }
| |
| result += `<span style="color:${hex}">`;
| |
| openTags.push('span');
| |
| i += 7;
| |
| continue;
| |
| }
| |
|
| |
| i++;
| |
| if (code === 'r') {
| |
| while (openTags.length) result += `</${openTags.pop()}>`;
| |
| } else if (colors[code]) {
| |
| while (openTags.length && openTags[openTags.length - 1] === 'span') {
| |
| result += `</${openTags.pop()}>`;
| |
| }
| |
| result += `<span style="color:${colors[code]}">`;
| |
| openTags.push('span');
| |
| } else if (formats[code]) {
| |
| const tag = formats[code];
| |
| result += `<${tag}>`;
| |
| openTags.push(tag);
| |
| }
| |
| } else {
| |
| result += text[i];
| |
| }
| |
| }
| |
|
| |
| while (openTags.length) result += `</${openTags.pop()}>`;
| |
| return result;
| |
| }
| |
|
| |
| async function updateStatus() {
| |
| try {
| |
| const response = await fetch(`http://192.168.10.113:8888/placeholders?player=${encodeURIComponent(playerName)}`);
| |
| const data = await response.json();
| |
|
| |
| const isOnline = data["%pinger_online_bastion16.co.ua:35211%"]?.toLowerCase().includes("online");
| |
| if (!isOnline) return showOfflineUI();
| |
|
| |
| offlineBlock.classList.add('hidden');
| |
| applyFadeInOnce(onlineBlock);
| |
|
| |
| if (iconEl) {
| |
| fetch(`https://api.mcstatus.io/v2/status/java/${serverAddress}`)
| |
| .then(res => res.json())
| |
| .then(msData => {
| |
| if (iconEl.src !== msData.icon) {
| |
| iconEl.src = msData.icon;
| |
| }
| |
| });
| |
| }
| |
|
| |
| const version = data["%pinger_version_bastion16.co.ua:35211%"] || '';
| |
| if (versionEl && versionEl.textContent !== version) versionEl.textContent = version;
| |
|
| |
| const motd = parseMinecraftColors(data["%pinger_motd_bastion16.co.ua:35211%"]);
| |
| if (motdEl && motdEl.innerHTML !== motd) motdEl.innerHTML = motd;
| |
|
| |
| const uptime = data["%server_uptime%"] || '—';
| |
| if (uptimeEl && uptimeEl.textContent !== uptime) uptimeEl.textContent = uptime;
| |
|
| |
| const online = data["%pinger_players_bastion16.co.ua:35211%"] || '0';
| |
| const max = data["%pinger_max_bastion16.co.ua:35211%"] || '0';
| |
| const playersText = `${online} / ${max}`;
| |
| if (playersEl && playersEl.textContent !== playersText) playersEl.textContent = playersText;
| |
|
| |
| if (playerList) {
| |
| const playersRaw = data["%playerlist_players_list%"];
| |
| const currentPlayers = Array.from(playerList.querySelectorAll('li')).map(li => li.textContent.trim());
| |
| const players = playersRaw ? playersRaw.split(',').map(p => p.trim()) : [];
| |
|
| |
| if (players.map(p => p.toLowerCase()).join(',') !== currentPlayers.join(',').toLowerCase()) {
| |
| playerList.innerHTML = '';
| |
| if (players.length > 0) {
| |
| players.forEach(name => {
| |
| const li = document.createElement('li');
| |
| li.style.display = 'flex';
| |
| li.style.alignItems = 'center';
| |
| li.style.gap = '8px';
| |
|
| |
| const img = document.createElement('img');
| |
| img.src = `https://mc-heads.net/avatar/${name}/24`;
| |
| img.alt = name;
| |
| img.style.width = '24px';
| |
| img.style.height = '24px';
| |
| img.style.borderRadius = '4px';
| |
|
| |
| const span = document.createElement('span');
| |
|
| |
| fetch(`http://192.168.10.113:8888/placeholders?player=${name}`)
| |
| .then(r => r.json())
| |
| .then(pData => {
| |
| const displayName = parseMinecraftColors(pData["%player_displayname%"] || name);
| |
| const afk = pData["%essentials_afk%"] === 'yes' ? '💤' : '';
| |
| span.innerHTML = `${displayName} ${afk}`;
| |
| });
| |
|
| |
| li.appendChild(img);
| |
| li.appendChild(span);
| |
| playerList.appendChild(li);
| |
| });
| |
| } else {
| |
| const li = document.createElement('li');
| |
| li.textContent = '— немає гравців —';
| |
| li.style.color = '#777';
| |
| playerList.appendChild(li);
| |
| }
| |
| }
| |
| }
| |
| } catch (e) {
| |
| console.error('Помилка отримання плейсхолдерів:', e);
| |
| showOfflineUI();
| |
| }
| |
| }
| |
|
| |
| function showOfflineUI() {
| |
| onlineBlock.classList.add('hidden');
| |
| applyFadeInOnce(offlineBlock);
| |
| }
| |
|
| |
| updateStatus();
| |
| setInterval(updateStatus, 10000);
| |
| });
| |
| </script>
| |
|
| |
| <h5>Статус Сервера</h5>
| |
|
| |
| <div id="server-status" class="card grey darken-3 white-text" style="margin-top: 20px; padding: 12px;">
| |
| <div id="server-online" class="card-content hidden">
| |
| <div style="display: flex; align-items: center; justify-content: space-between;">
| |
| <span style="font-size: 16px; font-weight: bold;">
| |
| <i class="material-icons green-text" style="vertical-align: middle;">cloud_done</i>
| |
| Сервер Online
| |
| </span>
| |
| <span id="server-version" class="green-text text-lighten-2" style="font-size: 14px;"></span>
| |
| </div>
| |
|
| |
| <div style="margin-top: 10px; text-align: center;">
| |
| <img id="icon-img" src="" alt="Server Icon" style="width: 48px; height: 48px;" />
| |
| </div>
| |
|
| |
| <div style="margin-top: 8px; font-size: 14px;">
| |
| <strong class="green-text">MOTD:</strong>
| |
| <div id="motd" style="color: #a5d6a7; font-size: 13px;"></div>
| |
| </div>
| |
|
| |
| <div style="margin-top: 6px;">
| |
| <strong class="green-text">Аптайм:</strong> <span id="server-uptime" style="color: #a5d6a7; font-size: 13px;"></span>
| |
| </div>
| |
|
| |
| <div style="margin-top: 8px; font-size: 14px;">
| |
| <strong class="green-text">Гравці онлайн:</strong>
| |
| <div id="players-online" class="white-text">—</div>
| |
| <ul id="player-list" class="white-text" style="list-style: none; padding-left: 0;"></ul>
| |
| </div>
| |
| </div>
| |
|
| |
| <div id="server-offline" class="card-content hidden" style="text-align: center;">
| |
| <i class="material-icons red-text" style="vertical-align: middle;">cloud_off</i>
| |
| <p style="margin-top: 10px; color: #ef9a9a;">Сервер недоступний</p>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| </html>
| |