/**
* MegicAI - Main JavaScript
* Common functionality used across the application
*/
// Helper function to format numbers with commas
function formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// Update credit display with animation
function updateCredits(newValue, animate = true) {
const creditDisplay = document.querySelector('.credit-value');
if (!creditDisplay) return;
const currentValue = parseInt(creditDisplay.textContent.replace(/,/g, ''), 10);
if (animate && !isNaN(currentValue)) {
const diff = newValue - currentValue;
const duration = 1000; // 1 second animation
const startTime = performance.now();
function updateCounter(timestamp) {
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / duration, 1);
const currentCount = Math.floor(currentValue + (diff * progress));
creditDisplay.textContent = formatNumber(currentCount);
if (progress < 1) {
requestAnimationFrame(updateCounter);
} else {
creditDisplay.textContent = formatNumber(newValue);
}
}
requestAnimationFrame(updateCounter);
} else {
creditDisplay.textContent = formatNumber(newValue);
}
}
// Copy text to clipboard
function copyToClipboard(text, successCallback = null) {
navigator.clipboard.writeText(text)
.then(() => {
if (successCallback) successCallback();
})
.catch(err => {
console.error('Failed to copy text: ', err);
});
}
// Toggle visibility of an element
function toggleVisibility(elementId) {
const element = document.getElementById(elementId);
if (element) {
element.style.display = element.style.display === 'none' ? 'block' : 'none';
}
}
// Add suggestions to a textarea
function addSuggestionToPrompt(suggestion, elementId) {
const textarea = document.getElementById(elementId);
if (textarea) {
const currentText = textarea.value;
textarea.value = currentText ? `${currentText}\n${suggestion}` : suggestion;
textarea.focus();
}
}
// Form validation
function validateForm(formId, errorElementId = null) {
const form = document.getElementById(formId);
if (!form) return true;
let isValid = true;
const requiredInputs = form.querySelectorAll('[required]');
// Clear previous error messages
form.querySelectorAll('.field-error').forEach(el => el.remove());
requiredInputs.forEach(input => {
if (!input.value.trim()) {
isValid = false;
// Create error message
const errorMsg = document.createElement('div');
errorMsg.className = 'field-error';
errorMsg.textContent = 'This field is required';
input.parentNode.appendChild(errorMsg);
// Add error style to input
input.classList.add('input-error');
} else {
input.classList.remove('input-error');
}
});
// If there's a global error element, update it
if (!isValid && errorElementId) {
const errorElement = document.getElementById(errorElementId);
if (errorElement) {
errorElement.textContent = 'Please fill in all required fields';
errorElement.style.display = 'block';
}
}
return isValid;
}
// Handle form submission with AJAX
function submitFormAsync(formId, successCallback, errorCallback) {
const form = document.getElementById(formId);
if (!form) return;
form.addEventListener('submit', function(event) {
event.preventDefault();
if (!validateForm(formId)) return;
const formData = new FormData(form);
const submitButton = form.querySelector('button[type="submit"]');
if (submitButton) {
const originalText = submitButton.innerHTML;
submitButton.disabled = true;
submitButton.innerHTML = ' Processing...';
}
fetch(form.action, {
method: form.method,
body: formData
})
.then(response => {
// Check if the response is JSON or HTML
const contentType = response.headers.get('content-type');
if (!response.ok) {
if (contentType && contentType.includes('application/json')) {
return response.json().then(data => {
throw new Error(data.message || 'An error occurred');
});
} else {
// For non-JSON errors, just show the status text instead of trying to parse JSON
throw new Error('Server error occurred: ' + response.statusText);
}
}
// If response is HTML, handle it as a redirect
if (contentType && contentType.includes('text/html')) {
// This is an HTML response, likely a result page - redirect to it
window.location.href = response.url;
return { success: true, redirected: true };
}
// Check if it's a redirect (e.g., 302, 303)
if (response.redirected) {
window.location.href = response.url;
return { success: true, redirected: true };
}
// Otherwise process as JSON
if (contentType && contentType.includes('application/json')) {
return response.json();
} else {
// If it's not JSON and not HTML with a redirect, handle it as a success
return { success: true, message: "Operation completed successfully" };
}
})
.then(data => {
if (successCallback) successCallback(data);
})
.catch(error => {
console.error("Error during form submission:", error);
if (errorCallback) errorCallback(error.message);
})
.finally(() => {
if (submitButton) {
submitButton.disabled = false;
submitButton.innerHTML = originalText;
}
});
});
}
// Document ready event
document.addEventListener('DOMContentLoaded', function() {
console.log('MegicAI application initialized');
// Initialize any forms with async submission
const asyncForms = document.querySelectorAll('[data-async-submit]');
asyncForms.forEach(form => {
const formId = form.id;
const successTarget = form.getAttribute('data-success-target');
const errorTarget = form.getAttribute('data-error-target');
// Check if the form is for HTML-based operations like process-request
const actionUrl = form.getAttribute('action');
if (actionUrl && (actionUrl.includes('/process-request') || actionUrl.includes('/watch-ad'))) {
// These endpoints return HTML, not JSON - use regular form submission
console.log('Form will use direct submission:', actionUrl);
form.removeAttribute('data-async-submit');
// Add regular submit handler with validation
form.addEventListener('submit', function(event) {
if (!validateForm(formId)) {
event.preventDefault();
return false;
}
const submitButton = form.querySelector('button[type="submit"]');
if (submitButton) {
// Add loading indicator
const originalText = submitButton.innerHTML;
submitButton.disabled = true;
submitButton.innerHTML = ' Processing...';
// Make sure the form submits normally for HTML responses
setTimeout(() => {
if (submitButton.disabled) {
// Re-enable after a timeout just in case
submitButton.disabled = false;
submitButton.innerHTML = originalText;
}
}, 10000); // 10 second timeout
}
return true;
});
} else {
// Use async submission for JSON-based API endpoints
submitFormAsync(
formId,
data => {
if (successTarget) {
const targetElement = document.getElementById(successTarget);
if (targetElement) {
targetElement.textContent = data.message || 'Success!';
targetElement.style.display = 'block';
}
}
// If a redirect URL is provided in the response
if (data.redirect) {
window.location.href = data.redirect;
}
},
error => {
if (errorTarget) {
const targetElement = document.getElementById(errorTarget);
if (targetElement) {
targetElement.textContent = error;
targetElement.style.display = 'block';
}
}
}
);
}
});
});