Spaces:
Runtime error
Runtime error
File size: 5,234 Bytes
6a5b8d8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
// 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);
});
});
|