Spaces:
Build error
Build error
/** | |
* 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 = '<i class="fas fa-spinner fa-spin"></i> 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 = '<i class="fas fa-spinner fa-spin"></i> 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'; | |
} | |
} | |
} | |
); | |
} | |
}); | |
}); |