MC / static /js /main.js
smodusermc's picture
Create main.js
d0484c3 verified
Raw History Blame Contribute Delete
6.74 kB
// ============================================
// Minecraft Server Panel - Main JavaScript
// ============================================
// Toast Notification System
function showToast(message, type = 'info') {
const container = document.getElementById('toast-container');
if (!container) return;
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.textContent = message;
container.appendChild(toast);
// Auto remove after 5 seconds
setTimeout(() => {
toast.style.animation = 'slideOut 0.3s ease forwards';
setTimeout(() => toast.remove(), 300);
}, 5000);
}
// Add slideOut animation
const style = document.createElement('style');
style.textContent = `
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// API Helper Functions
async function apiCall(endpoint, method = 'GET', data = null) {
const options = {
method,
headers: {
'Content-Type': 'application/json'
}
};
if (data && method !== 'GET') {
options.body = JSON.stringify(data);
}
try {
const response = await fetch(endpoint, options);
const result = await response.json();
return result;
} catch (error) {
console.error('API Error:', error);
throw error;
}
}
// Server Control Functions
async function serverControl(action) {
try {
const result = await apiCall(`/api/server/${action}`, 'POST');
showToast(result.message, result.success ? 'success' : 'error');
return result;
} catch (error) {
showToast(`Failed to ${action} server`, 'error');
}
}
// Utility Functions
function formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function formatDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
if (hours > 0) {
return `${hours}h ${minutes}m ${secs}s`;
} else if (minutes > 0) {
return `${minutes}m ${secs}s`;
}
return `${secs}s`;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Debounce function for search inputs
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Update server status indicator in sidebar
function updateSidebarStatus(running, starting = false) {
const indicator = document.getElementById('server-status-indicator');
if (!indicator) return;
const dot = indicator.querySelector('.status-dot');
const text = indicator.querySelector('.status-text');
if (running) {
dot.className = 'status-dot online';
text.textContent = 'Online';
} else if (starting) {
dot.className = 'status-dot starting';
text.textContent = 'Starting';
} else {
dot.className = 'status-dot offline';
text.textContent = 'Offline';
}
}
// Global keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl/Cmd + K: Focus search if exists
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
const searchInput = document.querySelector('input[type="text"][placeholder*="Search"]');
if (searchInput) {
e.preventDefault();
searchInput.focus();
}
}
// Escape: Close modals
if (e.key === 'Escape') {
const activeModal = document.querySelector('.modal.active');
if (activeModal) {
activeModal.classList.remove('active');
}
}
});
// Initialize tooltips (if any elements have data-tooltip)
document.querySelectorAll('[data-tooltip]').forEach(el => {
el.addEventListener('mouseenter', (e) => {
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.textContent = el.dataset.tooltip;
document.body.appendChild(tooltip);
const rect = el.getBoundingClientRect();
tooltip.style.top = `${rect.bottom + 8}px`;
tooltip.style.left = `${rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2)}px`;
});
el.addEventListener('mouseleave', () => {
document.querySelectorAll('.tooltip').forEach(t => t.remove());
});
});
// Auto-refresh status periodically
if (typeof io !== 'undefined') {
// Socket.IO is available, status updates will come via websocket
console.log('WebSocket mode enabled');
} else {
// Fallback to polling
setInterval(async () => {
try {
const status = await apiCall('/api/status');
updateSidebarStatus(status.running, status.starting);
} catch (e) {
// Ignore errors during polling
}
}, 10000);
}
// Confirm before leaving page if there are unsaved changes
let hasUnsavedChanges = false;
window.addEventListener('beforeunload', (e) => {
if (hasUnsavedChanges) {
e.preventDefault();
e.returnValue = '';
}
});
// Mark page as having unsaved changes when editing
document.querySelectorAll('textarea, input[type="text"]').forEach(el => {
el.addEventListener('input', () => {
if (el.classList.contains('code-editor') || el.closest('.settings-grid')) {
hasUnsavedChanges = true;
}
});
});
// Reset unsaved changes flag after successful save
const originalShowToast = showToast;
showToast = function(message, type) {
if (type === 'success' && (message.includes('saved') || message.includes('updated'))) {
hasUnsavedChanges = false;
}
originalShowToast(message, type);
};
// File drag and drop prevention (except on upload zones)
document.addEventListener('dragover', (e) => {
if (!e.target.closest('.upload-zone')) {
e.preventDefault();
}
});
document.addEventListener('drop', (e) => {
if (!e.target.closest('.upload-zone')) {
e.preventDefault();
}
});
// Console log for debugging
console.log('MC Server Panel loaded');
console.log('Version: 1.0.0');
console.log('Minecraft: 1.21.1');
console.log('Fabric: 0.17.1');