JRNET / static /js /main.js
Factor Studios
Upload 96 files
6a5b8d8 verified
// Main JavaScript file for Outline VPN
// Helper function to format bytes to human-readable format
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
// Helper function to format duration
function formatDuration(milliseconds) {
const seconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}d ${hours % 24}h`;
if (hours > 0) return `${hours}h ${minutes % 60}m`;
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
return `${seconds}s`;
}
// Show toast notification
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast align-items-center text-white bg-${type} border-0`;
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">${message}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
`;
const container = document.createElement('div');
container.className = 'toast-container position-fixed bottom-0 end-0 p-3';
container.appendChild(toast);
document.body.appendChild(container);
const bsToast = new bootstrap.Toast(toast);
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => {
container.remove();
});
}
// Copy text to clipboard
function copyToClipboard(text, successMessage = 'Copied to clipboard!') {
navigator.clipboard.writeText(text)
.then(() => showToast(successMessage, 'success'))
.catch(() => showToast('Failed to copy text', 'danger'));
}
// Download file helper
function downloadFile(content, filename, type = 'application/json') {
const blob = new Blob([content], { type });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
// Form validation
function validateForm(formElement) {
const requiredFields = formElement.querySelectorAll('[required]');
let isValid = true;
requiredFields.forEach(field => {
if (!field.value.trim()) {
isValid = false;
field.classList.add('is-invalid');
} else {
field.classList.remove('is-invalid');
}
});
return isValid;
}
// Password strength checker
function checkPasswordStrength(password) {
let strength = 0;
const messages = [];
if (password.length >= 8) strength++;
else messages.push('Password should be at least 8 characters long');
if (password.match(/[a-z]/)) strength++;
if (password.match(/[A-Z]/)) strength++;
else messages.push('Include at least one uppercase letter');
if (password.match(/[0-9]/)) strength++;
else messages.push('Include at least one number');
if (password.match(/[^a-zA-Z0-9]/)) strength++;
else messages.push('Include at least one special character');
return {
score: strength,
messages: messages,
label: ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong'][strength - 1] || 'Very Weak'
};
}
// Initialize password strength meter if it exists
document.addEventListener('DOMContentLoaded', () => {
const passwordInput = document.querySelector('input[type="password"]');
if (passwordInput) {
const feedbackDiv = document.createElement('div');
feedbackDiv.className = 'password-strength-meter mt-2';
passwordInput.parentNode.appendChild(feedbackDiv);
passwordInput.addEventListener('input', (e) => {
const strength = checkPasswordStrength(e.target.value);
const color = ['danger', 'warning', 'info', 'primary', 'success'][strength.score - 1] || 'danger';
feedbackDiv.innerHTML = `
<div class="progress" style="height: 5px;">
<div class="progress-bar bg-${color}"
style="width: ${(strength.score / 5) * 100}%">
</div>
</div>
<small class="text-${color} mt-1 d-block">${strength.label}</small>
`;
});
}
});
// Initialize tooltips
document.addEventListener('DOMContentLoaded', () => {
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
});