Найден корректный robots.txt
Файл управления сканированием сайта роботами
?
Robots.txt указывает поисковым роботам, какие страницы сканировать, а какие — нет. Ошибки в файле могут случайно закрыть важные разделы от индексации.
Robots.txt настроен корректно. Размер файла: 33838 байт. Загружен за: 0сек.
Проверяемая страница не запрещена в robots.txt.
Показать содержимое robots.txt
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<base href="https://globalhardcore.net/">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<!-- DYNAMIC SEO TITLES & DESC -->
<title>Global Hardcore - The Cycle</title>
<meta name="description" content="A Cycle-Based Roguelike Speedrun MMO.">
<meta name="keywords" content="minecraft, hardcore, smp, mmo, server">
<link rel="canonical" href="https://globalhardcore.net/">
<link rel="icon" href="favicon.png" type="image/png">
<!-- Configurable Browser Color -->
<meta name="theme-color" content="#386628">
<!-- OG Tags for Discord Embeds -->
<meta property="og:title" content="Global Hardcore - The Cycle">
<meta property="og:description" content="A Cycle-Based Roguelike Speedrun MMO.">
<meta property="og:url" content="https://globalhardcore.net/">
<meta property="og:type" content="website">
<meta property="og:image" content="https://globalhardcore.net/og-image.jpg"> <!-- Ensure you provide a generic og-image.jpg -->
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
</head>
<body>
<!-- BACKGROUNDS -->
<div class="bg-container">
<div class="vignette"></div>
<!-- Note to Buyer: Replace these images in /public_html/ -->
<img src="bg-top.jpg" class="bg-layer" id="bg-1" alt="Overworld">
<img src="bg-mid.jpg" class="bg-layer" id="bg-2" alt="Nether">
<img src="bg-bot.jpg" class="bg-layer" id="bg-3" alt="End">
</div>
<!-- NAVBAR (Dynamic based on Config) -->
<nav class="navbar" id="mainNav">
<div class="nav-links">
<a href="#" class="nav-item">Home</a>
<a href="/store" onclick="scrollToPanel('panel-store'); return false;" class="nav-item">Store</a>
<a href="/chat" onclick="scrollToPanel('panel-chat'); return false;" class="nav-item">Chat</a>
<a href="https://discord.gg/MdArGak8wE" class="nav-item" target="_blank">Discord</a>
</div>
</nav>
<main>
<!-- SECTION 1: HERO -->
<section class="panel" id="panel-hero">
<div class="hero-content">
<!-- LOGO: If logo.png exists, use it. Otherwise text. -->
<img src="logo.png" class="hero-logo" alt="Global Hardcore">
<h1>Global Hardcore</h1>
<!-- EDIT HERO TEXT HERE -->
<p class="subtitle">Beautiful World. Brutal Mechanics. Inevitable Collapse.</p>
<div class="ip-pill" onclick="copyIP()">
<!-- Uses SERVER_DISPLAY_IP so it shows/copies ports if needed -->
<span id="ip-text">IP: globalhardcore.net</span>
</div>
<div class="glass-widget" style="max-width: 800px; margin: 0 auto;">
<div class="status-header">
<div class="indicator-group">
<span id="status-dot" class="dot offline-dot"></span>
<span id="server-status">OFFLINE</span>
</div>
<span class="player-count"><span id="p-count">0</span> ONLINE</span>
</div>
<!-- Player Grid Container -->
<div class="player-grid" id="player-grid">
<span style="color:#888; font-size: 0.9rem;">Scanning timeline...</span>
</div>
</div>
</div>
</section>
<!-- SECTION 2: STORE -->
<section class="panel" id="panel-store">
<div class="content-wrapper" style="max-width: 1400px;">
<!-- EDIT TEXT HERE -->
<h2 class="glitch-text" style="color: var(--highlight); text-shadow: 0 0 20px var(--highlight);">STORE</h2>
<p class="subtitle">Support the server. Acquire assets. Secure your legacy.</p>
<div id="store-cats" class="store-tabs">
<span class="loading-text">Connecting to Tebex...</span>
</div>
<div id="store-grid" class="store-grid"></div>
</div>
</section>
<!-- SECTION 3: CHAT & NEWS -->
<section class="panel" id="panel-chat">
<div class="content-wrapper">
<h2 class="glitch-text">LIVE INTEL</h2>
<p class="subtitle">Real-time data from the network.</p>
<div class="split-grid">
<!-- CHAT -->
<div class="glass-widget chat-widget">
<div id="chat-feed" style="height: 330px; overflow-y: auto;">
<div style="padding: 20px; color: #666;">Decrypting Chat...</div>
</div>
</div>
<!-- NEWS -->
<div class="glass-widget news-widget">
<div id="news-hero"></div>
<div class="news-content">
<span class="news-date">LATEST TRANSMISSION</span>
<div id="news-text">Scanning for updates...</div>
</div>
</div>
</div>
</div>
</section>
<!-- SECTION 4: LEADERBOARD & SUPPORT -->
<section class="panel" id="panel-support">
<div class="content-wrapper">
<h2 class="void-text">DOMINATE</h2>
<p class="subtitle">Climb the ranks or submit a distress signal.</p>
<div class="split-grid" style="align-items: stretch;">
<!-- LEFT BOX: Leaderboard -->
<div class="glass-widget leaderboard-widget" style="display: flex; flex-direction: column;">
<h3 style="color: var(--electric); margin-bottom: 5px; text-transform:uppercase; letter-spacing:2px;">TOP PLAYERS</h3>
<p style="color:#666; font-size: 0.8rem; margin-bottom: 25px;">
Global Rankings
</p>
<div class="lb-header">
<div class="lb-col-rank">#</div>
<div class="lb-col-name">PLAYER</div>
<!-- Dynamic Columns handled by JS/PHP -->
<div id="lb-headers-dynamic" style="display:contents;"></div>
</div>
<div id="leaderboard-body" class="lb-body" style="flex-grow: 1;">
<div class="lb-row">
<div style="padding: 20px; color: #666; text-align:center; width:100%;">Accessing Database...</div>
</div>
</div>
<button onclick="openLbModal()" class="btn btn-sm" style="margin-top: 20px; background: rgba(255,255,255,0.05); color: #aaa; border: 1px solid rgba(255,255,255,0.1);">VIEW FULL STANDINGS</button>
</div>
<!-- RIGHT BOX: Distress Signal -->
<div class="glass-widget" style="display: flex; flex-direction: column;">
<h3 id="distress-title" style="color: #ff4d00; transition: 0.3s;">DISTRESS SIGNAL</h3>
<p style="font-size: 0.85rem; color:#aaa; margin-bottom: 20px;">
Submit bug reports, ban appeals, or server suggestions.
</p>
<form id="supportForm" class="support-form" onsubmit="sendAlert(event)" style="display: flex; flex-direction: column; flex-grow: 1;">
<input type="text" id="ticketName" class="form-input" placeholder="Your Minecraft Name" required>
<!-- HONEYPOT -->
<div style="opacity: 0; position: absolute; top: 0; left: 0; height: 0; width: 0; z-index: -1;">
<label for="website_check">Website</label>
<input type="text" id="website_check" name="website_check" tabindex="-1" autocomplete="off">
</div>
<select id="ticketType" class="form-select" onchange="updateTicketColors()">
<option value="Bug Report" data-color="#ff4d00">Bug Report</option>
<option value="Ban Appeal" data-color="#FF0040">Ban Appeal</option>
<option value="Suggestion" data-color="#00ff88">Suggestion</option>
<option value="Staff App" data-color="#00C3FF">Staff App</option>
</select>
<textarea id="ticketMsg" class="form-textarea" placeholder="Describe the anomaly or idea..." required style="flex-grow: 1;"></textarea>
<button type="submit" class="btn-submit">TRANSMIT SIGNAL</button>
</form>
</div>
</div>
<!-- BOTTOM: Discord Section -->
<div class="discord-banner" style="margin-top: 30px;">
<div class="glass-widget" style="text-align: center; padding: 25px;">
<p style="color:#ccc; margin-bottom: 15px; font-size: 0.9rem;">
Join the Network for wipe alerts, faction recruiting, and changelogs.
</p>
<a href="https://discord.gg/MdArGak8wE" class="btn btn-discord" target="_blank" style="max-width: 300px; margin: 0 auto;">JOIN DISCORD</a>
</div>
</div>
<footer style="margin-top: 80px; font-size: 0.8rem; opacity: 0.4;">
© 2026 Global Hardcore. All Rights Reserved. - <a href="https://selcreative.co.uk/" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">Website by SEL Creative</a>
</footer>
</div>
</section>
</main>
<!-- LEADERBOARD MODAL -->
<div id="lb-modal" class="modal-overlay">
<div class="modal-content glass-widget">
<span class="close-modal" onclick="closeLbModal()">×</span>
<h3 style="color: var(--electric);">GLOBAL RANKINGS</h3>
<div style="margin: 15px 0;">
<input type="text" id="lb-search" class="form-input" placeholder="Search player name..." onkeyup="searchLb(this.value)">
</div>
<div class="lb-header">
<div class="lb-col-rank">#</div>
<div class="lb-col-name">PLAYER</div>
<div id="lb-headers-dynamic-modal" style="display:contents;"></div>
</div>
<div id="lb-modal-body" class="lb-body" style="max-height: 50vh; overflow-y: auto;">
<div style="padding:20px; text-align:center; color:#666;">Loading...</div>
</div>
</div>
</div>
<script>
// --- PHP CONFIG TO JS INJECTION ---
const SERVER_IP = "globalhardcore.net";
const SERVER_PORT = "25565";
const DISPLAY_IP = "globalhardcore.net";
const STORE_BASE = "https://global-hardcore.tebex.io";
const CURRENCY = "£";
const THEME_COLOR = "#386628";
// Chat Configuration (Reads from Config)
const BOT_NAME = "Global Hardcore Bot";
const RANK_COLORS = {"ADMIN":"#FF5555","MVP":"#55FFFF","VIP":"#FFAA00","GRIEFER":"#ff009d","PLAYER":"#00ff88","DEFAULT":"#AAAAAA"};
// --- TICKET COLOR LOGIC ---
function updateTicketColors() {
const select = document.getElementById('ticketType');
if(!select) return;
const title = document.getElementById('distress-title');
const btn = document.querySelector('.btn-submit');
// Get color from the selected option's data attribute
const selectedOption = select.options[select.selectedIndex];
const color = selectedOption.getAttribute('data-color') || "#ff4d00";
title.style.color = color;
title.style.textShadow = `0 0 15px ${color}`;
btn.style.background = color;
btn.style.boxShadow = `0 0 20px ${color}`;
// Contrast check: If bright color (green/cyan), use black text
if(color === "#00ff88" || color === "#00C3FF" || color === "#55FFFF") btn.style.color = "black";
else btn.style.color = "white";
}
// --- BROWSER COLOR LOGIC ---
function setBrowserColor(color) {
const meta = document.querySelector('meta[name="theme-color"]');
if(meta) meta.setAttribute('content', color);
}
gsap.registerPlugin(ScrollTrigger);
// Background Parallax
let tl = gsap.timeline({ scrollTrigger: { trigger: "main", start: "top top", end: "bottom bottom", scrub: 0.5 }});
tl.to("#bg-2", { opacity: 1, duration: 0.5, ease: "none" }, 0.1);
tl.to("#bg-3", { opacity: 1, duration: 0.35, ease: "none" }, 0.65);
// Section Scroll Colors (Check if elements exist before attaching triggers)
const heroPanel = document.getElementById("panel-hero");
if(heroPanel) {
ScrollTrigger.create({ trigger: "#panel-hero", start: "top center", end: "bottom center", onEnter: () => setBrowserColor(THEME_COLOR), onEnterBack: () => setBrowserColor(THEME_COLOR) });
}
const storePanel = document.getElementById("panel-store");
if(storePanel) {
ScrollTrigger.create({ trigger: "#panel-store", start: "top center", end: "bottom center", onEnter: () => setBrowserColor("#520808"), onEnterBack: () => setBrowserColor("#520808") });
}
const supportPanel = document.getElementById("panel-support");
if(supportPanel) {
ScrollTrigger.create({ trigger: "#panel-support", start: "top center", onEnter: () => setBrowserColor("#13091c"), onLeaveBack: () => setBrowserColor("#520808") });
}
// --- LEADERBOARD LOGIC ---
const crownSVG = `<svg width="20" height="20" viewBox="0 0 135 130" fill="currentColor" xmlns="http://www.w3.org/2000/svg" style="margin-right:5px; vertical-align: text-bottom;"><path fill-rule="evenodd" clip-rule="evenodd" d="M25.537 1.40456C25.205 2.27156 25.196 5.20156 25.517 7.91556L26.102 12.8516H13.051H0L0.676998 19.1016C1.66 28.1626 2.949 32.5256 6.848 39.9796C11.32 48.5286 19.465 58.3606 27.958 65.4616C35.002 71.3516 51.718 82.7696 56.446 84.9216C59.103 86.1306 59.194 86.4296 59.14 93.7626C59.035 107.832 55.066 112.852 44.048 112.852H38.196V121.352V129.852H67.196H96.196V121.352V112.852L90.446 112.848C79.329 112.841 75.196 107.746 75.196 94.0445C75.196 86.4545 75.293 86.1286 77.908 84.9366C83.061 82.5896 97.723 72.5646 105.604 66.0016C121.022 53.1616 131.091 37.5646 133.147 23.3396C134.815 11.7976 136.008 12.8516 121.272 12.8516H108.348L108.522 6.60156L108.696 0.351557L67.419 0.0905556C30.617 -0.142444 26.076 0.000558496 25.537 1.40456ZM28.22 31.4226C29.937 41.6746 33.149 52.9266 35.848 58.1446C36.67 59.7356 37.156 61.2256 36.926 61.4546C35.977 62.4036 22.637 48.9816 18.08 42.4926C13.964 36.6316 9.196 25.4766 9.196 21.7076C9.196 21.1786 12.957 20.8986 17.864 21.0626L26.533 21.3516L28.22 31.4226ZM125.172 23.1016C125.102 29.7486 117.513 42.4976 107.257 53.1966C98.172 62.6746 96.643 63.3576 99.614 56.6096C102.716 49.5626 104.787 42.1596 106.649 31.4706C107.5 26.5856 108.196 22.1986 108.196 21.7206C108.196 21.2426 112.021 20.8516 116.696 20.8516C124.851 20.8516 125.195 20.9426 125.172 23.1016Z" fill="currentColor"/></svg>`;
function renderLbHeaders(headers) {
const html = headers.map(h => `<div class="lb-col-stat text-center">${h}</div>`).join('');
const main = document.getElementById('lb-headers-dynamic');
const modal = document.getElementById('lb-headers-dynamic-modal');
if(main) main.innerHTML = html;
if(modal) modal.innerHTML = html;
}
function renderLbRows(data, containerId, limit = 100) {
const container = document.getElementById(containerId);
if (!Array.isArray(data) || data.length === 0) {
container.innerHTML = `<div class="lb-row"><div style="width:100%; text-align:center; padding:15px; color:#666;">No records found.</div></div>`;
return;
}
let html = '';
data.forEach((row, idx) => {
if (idx >= limit) return;
let rank = row.rank_pos ? row.rank_pos : (idx + 1);
let rowClass = 'lb-row';
let iconHtml = "";
let rankDisplay = rank;
if (rank == 1) { rowClass += ' lb-rank-1'; iconHtml = `<span style="color:#FFD700;">${crownSVG}</span>`; rankDisplay = ""; }
else if (rank == 2) { rowClass += ' lb-rank-2'; }
else if (rank == 3) { rowClass += ' lb-rank-3'; }
let statsHtml = '';
let i = 0;
while(row['stat_' + i] !== undefined) {
statsHtml += `<div class="lb-col-stat text-center">${row['stat_' + i]}</div>`;
i++;
}
html += `
<div class="${rowClass}">
<div class="lb-col-rank">${iconHtml}${rankDisplay}</div>
<div class="lb-col-name">${row.name}</div>
${statsHtml}
</div>`;
});
container.innerHTML = html;
}
async function updateLeaderboard() {
if(!document.getElementById('leaderboard-body')) return;
try {
const response = await fetch('get_leaderboard.php');
const result = await response.json();
if(result.headers) renderLbHeaders(result.headers);
if(result.data) renderLbRows(result.data, 'leaderboard-body', 5);
} catch (e) {
console.error(e);
document.getElementById('leaderboard-body').innerHTML = `<div class="lb-row"><div style="width:100%; text-align:center; padding:15px; color:#ff4d00;">Connection Failed</div></div>`;
}
}
function openLbModal() {
document.getElementById('lb-modal').style.display = 'flex';
document.body.style.overflow = 'hidden';
fetch('get_leaderboard.php?full=true')
.then(res => res.json())
.then(result => {
if(result.headers) renderLbHeaders(result.headers);
if(result.data) renderLbRows(result.data, 'lb-modal-body', 100);
});
}
function closeLbModal() {
document.getElementById('lb-modal').style.display = 'none';
document.body.style.overflow = 'auto';
}
let searchTimeout;
function searchLb(val) {
clearTimeout(searchTimeout);
const container = document.getElementById('lb-modal-body');
container.innerHTML = '<div style="padding:20px; text-align:center; color:#888;">Searching...</div>';
searchTimeout = setTimeout(() => {
let url = 'get_leaderboard.php?full=true';
if(val.trim().length > 0) url = `get_leaderboard.php?search=${encodeURIComponent(val)}`;
fetch(url)
.then(res => res.json())
.then(result => renderLbRows(result.data, 'lb-modal-body', 100));
}, 500);
}
// --- STORE LOGIC ---
let storeData = [];
async function loadStore() {
if(typeof STORE_BASE === 'undefined' || STORE_BASE === '') return;
try {
const response = await fetch('get_store.php');
const json = await response.json();
if(!json.categories) return;
storeData = json.categories;
renderCategories();
if(storeData.length > 0) selectCategory(0);
} catch(e) {
console.error(e);
document.getElementById('store-cats').innerHTML = "Store Unavailable";
}
}
function renderCategories() {
const container = document.getElementById('store-cats');
if(!container) return;
let html = '';
storeData.forEach((cat, index) => {
if(cat.packages.length > 0) {
html += `<button class="cat-btn" onclick="selectCategory(${index})" id="cat-${index}">${cat.name}</button>`;
}
});
container.innerHTML = html;
}
function selectCategory(index) {
document.querySelectorAll('.cat-btn').forEach(b => b.classList.remove('active'));
const btn = document.getElementById(`cat-${index}`);
if(btn) btn.classList.add('active');
const grid = document.getElementById('store-grid');
if(!grid) return;
const packages = storeData[index].packages;
let html = '';
packages.forEach(pkg => {
let imgUrl = pkg.image || "logo.png"; // Fallback image
let displayPrice = !isNaN(parseFloat(pkg.price)) ? parseFloat(pkg.price).toFixed(2) : pkg.price;
html += `
<div class="store-card" style="opacity: 0;">
<div class="card-img" style="background-image: url('${imgUrl}')"></div>
<div class="card-body">
<h4>${pkg.name}</h4>
<div class="card-price">${CURRENCY}${displayPrice}</div>
<a href="${STORE_BASE}/checkout/packages/add/${pkg.id}/single" target="_blank" class="btn btn-primary btn-sm">BUY NOW</a>
</div>
</div>`;
});
grid.innerHTML = html;
gsap.fromTo(".store-card", { y: 20, scale: 0.95, filter: "blur(5px)" }, { opacity: 1, y: 0, scale: 1, filter: "blur(0px)", duration: 0.4, stagger: 0.05, ease: "power2.out", clearProps: "all" });
}
function scrollToPanel(id) {
const el = document.getElementById(id);
if(el) el.scrollIntoView({ behavior: 'smooth' });
}
window.addEventListener('DOMContentLoaded', () => {
setBrowserColor(THEME_COLOR);
// Init Ticket Colors
const ticketSelect = document.getElementById('ticketType');
if(ticketSelect) updateTicketColors();
const path = window.location.pathname.replace('/', '').toLowerCase();
if (path === 'store') setTimeout(() => scrollToPanel('panel-store'), 100);
else if (path === 'chat') setTimeout(() => scrollToPanel('panel-chat'), 100);
else if (path === 'help') setTimeout(() => scrollToPanel('panel-support'), 100);
// Feature Checks before loading
if(document.getElementById('store-grid')) loadStore();
if(document.getElementById('leaderboard-body')) updateLeaderboard();
});
window.addEventListener('scroll', () => {
const nav = document.getElementById('mainNav');
if(!nav) return;
if (window.scrollY > 50) nav.classList.add('scrolled');
else nav.classList.remove('scrolled');
});
// --- MINECRAFT SERVER STATUS (WITH PORT SUPPORT) ---
async function updateStatus() {
try {
// Build Query URL: Use IP only if port is standard, otherwise append port
// Note: SERVER_IP is for querying, DISPLAY_IP is for the clipboard
let queryUrl = SERVER_IP;
if(SERVER_PORT && SERVER_PORT != "25565") {
queryUrl += ":" + SERVER_PORT;
}
const response = await fetch(`https://api.mcsrvstat.us/2/${queryUrl}?t=${Date.now()}`);
const data = await response.json();
const dot = document.getElementById('status-dot');
const text = document.getElementById('server-status');
const count = document.getElementById('p-count');
const grid = document.getElementById('player-grid');
if (data.online) {
// SERVER IS ONLINE
text.innerText = "ONLINE";
text.style.color = "#00ff88";
dot.className = "dot online-dot";
const numOnline = data.players.online;
count.innerText = numOnline;
grid.innerHTML = '';
// Determine Player List
let players = [];
if (data.players.list) {
players = data.players.list;
} else if (data.players.uuid) {
players = Object.values(data.players.uuid);
}
if (players.length > 0) {
players.forEach(p => {
const name = p.replace(/§./g, '');
grid.innerHTML += `
<div class="player-slot">
<a href="https://namemc.com/profile/${name}" target="_blank">
<img src="https://minotar.net/helm/${name}/60.png" class="player-head" title="${name}" alt="${name}">
</a>
<span class="player-name">${name}</span>
</div>`;
});
} else {
if (numOnline > 0) {
grid.innerHTML = '<span style="color:#aaa; font-size:0.8rem; font-style:italic;">' + numOnline + ' Hidden Entities</span>';
} else {
grid.innerHTML = '<span style="color:#666; font-size:0.8rem;">No lifeforms detected.</span>';
}
}
} else {
// SERVER OFFLINE
text.innerText = "OFFLINE";
text.style.color = "var(--danger)";
dot.className = "dot offline-dot";
count.innerText = "0";
grid.innerHTML = '<span style="color:#666; font-size:0.8rem;">Signal lost.</span>';
}
} catch (e) {
console.error("Status check failed:", e);
}
}
// --- CHAT LOGIC (DISCORD + GAME PARSING) ---
async function updateChat() {
if(!document.getElementById('chat-feed')) return;
try {
const response = await fetch('get_chat.php');
const messages = await response.json();
const feed = document.getElementById('chat-feed');
if (messages.error || messages.length === 0) return;
let html = '';
// Reverse so newest is at bottom
const reversed = [...messages].reverse();
reversed.forEach(msg => {
let displayName = msg.author.global_name || msg.author.username;
let avatar = `https://cdn.discordapp.com/avatars/${msg.author.id}/${msg.author.avatar}.png`;
let content = msg.content;
let tagHtml = `<span class="chat-tag tag-discord">DISCORD</span>`;
let nameColor = "#7289da";
// --- BOT PARSING LOGIC ---
if (msg.author.username === BOT_NAME || msg.author.bot) {
const regex = /^(?:\[(.*?)\]\s*)?(.*?)(?:\s*[»:]\s*)(.*)/;
const match = content.match(regex);
if (match) {
const rank = match[1];
const name = match[2];
const text = match[3];
if(name && text) {
content = text;
displayName = name;
avatar = `https://minotar.net/helm/${name}/64`;
nameColor = "#ffffff";
if(rank) {
const cleanRank = rank.replace(/§./g, '').toUpperCase();
const rankColor = RANK_COLORS[cleanRank] || "#AAAAAA";
tagHtml = `<span class="chat-tag tag-game" style="color:${rankColor}; border-color:${rankColor}">${cleanRank}</span>`;
nameColor = rankColor;
} else {
tagHtml = `<span class="chat-tag tag-game">PLAYER</span>`;
}
}
}
}
html += `
<div class="chat-line">
<img src="${avatar}" class="chat-avatar-small" onerror="this.src='favicon.png'">
<div class="chat-content-wrapper">
${tagHtml}
<span class="chat-meta" style="color:${nameColor}">${displayName}:</span>
<span class="chat-text">${content}</span>
</div>
</div>`;
});
// JUST SET HTML. CSS handles the alignment.
feed.innerHTML = html;
} catch (e) { console.error(e); }
}
async function updateNews() {
if(!document.getElementById('news-text')) return;
try {
const response = await fetch('get_news.php');
const data = await response.json();
if (data.length > 0) {
const msg = data[0];
if (!msg.content || msg.content.trim() === "") {
document.getElementById('news-text').innerHTML = "<em>(Visual Transmission Received)</em>";
} else {
document.getElementById('news-text').innerHTML = msg.content.replace(/\n/g, "<br>");
}
if (msg.attachments && msg.attachments.length > 0) {
const hero = document.getElementById('news-hero');
hero.innerHTML = `<img src="${msg.attachments[0].url}" class="news-image">`;
hero.style.display = "block";
} else {
document.getElementById('news-hero').style.display = "none";
}
}
} catch (e) {}
}
async function sendAlert(e) {
e.preventDefault();
const btn = document.querySelector('.btn-submit');
const originalText = "TRANSMIT SIGNAL";
btn.innerText = "TRANSMITTING..."; btn.disabled = true;
try {
const response = await fetch('send_alert.php', {
method: 'POST',
body: JSON.stringify({
username: document.getElementById('ticketName').value,
type: document.getElementById('ticketType').value,
message: document.getElementById('ticketMsg').value,
website_check: document.getElementById('website_check').value
}),
headers: {'Content-Type': 'application/json'}
});
const data = await response.json();
if (data.status === "success") {
btn.innerText = "SIGNAL SENT"; btn.style.background = "#00ff88"; btn.style.color = "black";
document.getElementById('supportForm').reset();
} else {
btn.innerText = "PLEASE WAIT"; btn.style.background = "var(--danger)";
}
} catch (err) {
btn.innerText = "CONNECTION ERROR"; btn.style.background = "var(--danger)";
}
setTimeout(() => { btn.innerText = originalText; updateTicketColors(); btn.disabled = false; }, 3000);
}
function copyIP() {
// Copies the DISPLAY_IP (which might include a port or be different from query IP)
navigator.clipboard.writeText(DISPLAY_IP);
const btn = document.getElementById('ip-text');
const original = btn.innerText;
btn.innerText = "COPIED!";
setTimeout(() => { btn.innerText = original; }, 2000);
}
// Init Loops
updateStatus();
if(document.getElementById('chat-feed')) { updateChat(); setInterval(updateChat, 5000); }
if(document.getElementById('news-text')) { updateNews(); }
setInterval(updateStatus, 30000);
</script>
</body>
</html>