|
|
|
|
|
|
| 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];
|
| }
|
|
|
|
|
| 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`;
|
| }
|
|
|
|
|
| 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();
|
| });
|
| }
|
|
|
|
|
| function copyToClipboard(text, successMessage = 'Copied to clipboard!') {
|
| navigator.clipboard.writeText(text)
|
| .then(() => showToast(successMessage, 'success'))
|
| .catch(() => showToast('Failed to copy text', 'danger'));
|
| }
|
|
|
|
|
| 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);
|
| }
|
|
|
|
|
| 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;
|
| }
|
|
|
|
|
| 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'
|
| };
|
| }
|
|
|
|
|
| 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>
|
| `;
|
| });
|
| }
|
| });
|
|
|
|
|
| document.addEventListener('DOMContentLoaded', () => {
|
| const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
| tooltipTriggerList.map(function (tooltipTriggerEl) {
|
| return new bootstrap.Tooltip(tooltipTriggerEl);
|
| });
|
| });
|
|
|