LinguaVerify / static /script.js
CHRISDANIEL145
Initial commit
3e08449
Raw
History Blame Contribute Delete
27.2 kB
/**
* ════════════════════════════════════════════════════════════════════════
* LinguaVerify AI v5.1 - COMPLETE PRODUCTION JAVASCRIPT
* Frontend Logic β€’ Text Visibility FIXED β€’ All Features Working
* Author: Your Name | Date: Oct 2025 | Version: 5.1
* ════════════════════════════════════════════════════════════════════════
*/
// ════════════════════════════════════════════════════════════════════════
// GLOBAL STATE
// ════════════════════════════════════════════════════════════════════════
let currentResult = null;
// Language names mapping
const LANGUAGE_NAMES = {
'en': 'English', 'es': 'Spanish', 'fr': 'French', 'de': 'German',
'hi': 'Hindi', 'ar': 'Arabic', 'zh': 'Chinese', 'ja': 'Japanese',
'ko': 'Korean', 'ru': 'Russian', 'pt': 'Portuguese', 'it': 'Italian',
'nl': 'Dutch', 'tr': 'Turkish', 'pl': 'Polish', 'th': 'Thai',
'vi': 'Vietnamese', 'id': 'Indonesian', 'ta': 'Tamil', 'te': 'Telugu',
'bn': 'Bengali', 'ur': 'Urdu', 'fa': 'Persian', 'he': 'Hebrew',
'mr': 'Marathi', 'gu': 'Gujarati', 'kn': 'Kannada', 'ml': 'Malayalam',
'pa': 'Punjabi', 'si': 'Sinhala', 'ne': 'Nepali', 'my': 'Burmese',
'km': 'Khmer', 'lo': 'Lao', 'am': 'Amharic', 'sw': 'Swahili',
'unknown': 'Unknown'
};
// Example datasets
const examples = {
1: {
title_a: "Deep Learning for Medical Diagnosis",
title_b: "ΰ€šΰ€Ώΰ€•ΰ€Ώΰ€€ΰ₯ΰ€Έΰ€Ύ ΰ€¨ΰ€Ώΰ€¦ΰ€Ύΰ€¨ ΰ€•ΰ₯‡ ΰ€²ΰ€Ώΰ€ ΰ€‘ΰ₯€ΰ€ͺ ΰ€²ΰ€°ΰ₯ΰ€¨ΰ€Ώΰ€‚ΰ€—",
domain: "medicine"
},
2: {
title_a: "Climate Change Impact on Agriculture",
title_b: "Impacto del Cambio ClimΓ‘tico en la Agricultura",
domain: "climate_change"
},
3: {
title_a: "Artificial Intelligence Research and Development",
title_b: "Ψ¨Ψ­Ψ« وΨͺطوير Ψ§Ω„Ψ°ΩƒΨ§Ψ‘ Ψ§Ω„Ψ§Ψ΅Ψ·Ω†Ψ§ΨΉΩŠ",
domain: "artificial_intelligence"
}
};
// ════════════════════════════════════════════════════════════════════════
// INITIALIZATION
// ════════════════════════════════════════════════════════════════════════
document.addEventListener('DOMContentLoaded', () => {
console.log('%c🧠 LinguaVerify AI v5.1 - PRODUCTION READY',
'color: #a78bfa; font-size: 16px; font-weight: bold; background: #1a1f3a; padding: 10px; border-radius: 5px;');
console.log('%c✨ All Features Active', 'color: #10b981; font-size: 12px;');
initializeApp();
setupEventListeners();
checkSystemHealth();
fixInputVisibility();
console.log('%c⌨️ Keyboard Shortcuts:', 'color: #a78bfa; font-weight: bold;');
console.log(' Ctrl/Cmd + Enter: Verify titles');
console.log(' Esc: Clear results');
});
// ════════════════════════════════════════════════════════════════════════
// TEXT VISIBILITY FIX (CRITICAL)
// ════════════════════════════════════════════════════════════════════════
function fixInputVisibility() {
const inputs = document.querySelectorAll('.luxury-input, .luxury-select');
inputs.forEach(input => {
// Set text color
input.style.color = '#ffffff';
input.style.webkitTextFillColor = '#ffffff';
// Fix on input event
input.addEventListener('input', function() {
this.style.color = '#ffffff';
this.style.webkitTextFillColor = '#ffffff';
});
// Fix on focus
input.addEventListener('focus', function() {
this.style.color = '#ffffff';
this.style.webkitTextFillColor = '#ffffff';
});
// Fix on blur
input.addEventListener('blur', function() {
this.style.color = '#ffffff';
this.style.webkitTextFillColor = '#ffffff';
});
});
console.log('βœ… Input visibility enforced');
}
// ════════════════════════════════════════════════════════════════════════
// APP INITIALIZATION
// ════════════════════════════════════════════════════════════════════════
function initializeApp() {
const titleA = document.getElementById('title_a');
const titleB = document.getElementById('title_b');
if (titleA) {
updateCharCount('title_a', 'char_count_a');
detectLanguageRealtime('title_a', 'detected_lang_a', 'lang_badge_a');
titleA.addEventListener('input', () => {
updateCharCount('title_a', 'char_count_a');
detectLanguageRealtime('title_a', 'detected_lang_a', 'lang_badge_a');
});
}
if (titleB) {
updateCharCount('title_b', 'char_count_b');
detectLanguageRealtime('title_b', 'detected_lang_b', 'lang_badge_b');
titleB.addEventListener('input', () => {
updateCharCount('title_b', 'char_count_b');
detectLanguageRealtime('title_b', 'detected_lang_b', 'lang_badge_b');
});
}
}
// ════════════════════════════════════════════════════════════════════════
// EVENT LISTENERS
// ════════════════════════════════════════════════════════════════════════
function setupEventListeners() {
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl/Cmd + Enter to verify
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
verifyTitles();
}
// Escape to clear results
if (e.key === 'Escape') {
const results = document.getElementById('results');
if (results && results.style.display !== 'none') {
hideResults();
showNotification('Results cleared', 'info');
}
}
});
}
// ════════════════════════════════════════════════════════════════════════
// CHARACTER COUNTING
// ════════════════════════════════════════════════════════════════════════
function updateCharCount(textareaId, countId) {
const textarea = document.getElementById(textareaId);
const counter = document.getElementById(countId);
if (textarea && counter) {
const length = textarea.value.length;
counter.textContent = `${length} char${length !== 1 ? 's' : ''}`;
// Change color if too long
if (length > 500) {
counter.style.color = '#f59e0b';
} else {
counter.style.color = 'rgba(255, 255, 255, 0.4)';
}
}
}
// ════════════════════════════════════════════════════════════════════════
// REAL-TIME LANGUAGE DETECTION
// ════════════════════════════════════════════════════════════════════════
let languageDetectionTimeouts = {};
function detectLanguageRealtime(textareaId, langSpanId, badgeId) {
const textarea = document.getElementById(textareaId);
const langSpan = document.getElementById(langSpanId);
const badge = document.getElementById(badgeId);
if (!textarea || !langSpan || !badge) return;
const text = textarea.value.trim();
// Handle empty input
if (!text) {
langSpan.textContent = 'Waiting...';
badge.style.background = 'rgba(139, 92, 246, 0.15)';
badge.style.borderColor = 'rgba(139, 92, 246, 0.25)';
badge.style.color = '#a78bfa';
return;
}
// Show detecting state
langSpan.textContent = 'Detecting...';
badge.style.background = 'rgba(59, 130, 246, 0.15)';
badge.style.borderColor = 'rgba(59, 130, 246, 0.25)';
badge.style.color = '#60a5fa';
// Clear previous timeout
if (languageDetectionTimeouts[textareaId]) {
clearTimeout(languageDetectionTimeouts[textareaId]);
}
// Debounce API call
languageDetectionTimeouts[textareaId] = setTimeout(() => {
fetch('/detect_language', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: text })
})
.then(response => response.json())
.then(data => {
const langName = LANGUAGE_NAMES[data.language] || data.language.toUpperCase();
const confidence = Math.round(data.confidence * 100);
langSpan.textContent = `${langName} (${confidence}%)`;
badge.style.background = 'rgba(16, 185, 129, 0.15)';
badge.style.borderColor = 'rgba(16, 185, 129, 0.25)';
badge.style.color = '#34d399';
})
.catch(error => {
console.error('Language detection error:', error);
langSpan.textContent = 'Auto-detect';
badge.style.background = 'rgba(239, 68, 68, 0.15)';
badge.style.borderColor = 'rgba(239, 68, 68, 0.25)';
badge.style.color = '#f87171';
});
}, 500); // 500ms debounce
}
// ════════════════════════════════════════════════════════════════════════
// EXAMPLE LOADING
// ════════════════════════════════════════════════════════════════════════
function loadExample(id) {
const example = examples[id];
if (!example) {
showNotification('Example not found', 'error');
return;
}
const titleA = document.getElementById('title_a');
const titleB = document.getElementById('title_b');
const domain = document.getElementById('domain');
if (titleA && titleB && domain) {
// Set values
titleA.value = example.title_a;
titleB.value = example.title_b;
domain.value = example.domain;
// Force text visibility
titleA.style.color = '#ffffff';
titleB.style.color = '#ffffff';
titleA.style.webkitTextFillColor = '#ffffff';
titleB.style.webkitTextFillColor = '#ffffff';
// Update UI
updateCharCount('title_a', 'char_count_a');
updateCharCount('title_b', 'char_count_b');
detectLanguageRealtime('title_a', 'detected_lang_a', 'lang_badge_a');
detectLanguageRealtime('title_b', 'detected_lang_b', 'lang_badge_b');
// Auto-verify after 1 second
setTimeout(() => verifyTitles(), 1000);
showNotification('βœ… Example loaded successfully', 'success');
}
}
// ════════════════════════════════════════════════════════════════════════
// MAIN VERIFICATION FUNCTION
// ════════════════════════════════════════════════════════════════════════
async function verifyTitles() {
const titleA = document.getElementById('title_a')?.value.trim();
const titleB = document.getElementById('title_b')?.value.trim();
const domain = document.getElementById('domain')?.value;
const enableTranslation = document.getElementById('enable_translation')?.checked;
// Validation
if (!titleA || !titleB) {
showNotification('⚠️ Please enter both titles', 'warning');
return;
}
// Set loading state
setLoadingState(true);
hideResults();
try {
const response = await fetch('/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title_a: titleA,
title_b: titleB,
domain: domain,
enable_translation: enableTranslation
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `HTTP ${response.status}`);
}
const result = await response.json();
currentResult = result;
// Display results with animation delay
setTimeout(() => {
displayResults(result, domain, enableTranslation);
}, 300);
showNotification('βœ… Analysis complete', 'success');
} catch (error) {
console.error('Verification error:', error);
showNotification('❌ Error: ' + error.message, 'error');
} finally {
setLoadingState(false);
}
}
// ════════════════════════════════════════════════════════════════════════
// LOADING STATE MANAGEMENT
// ════════════════════════════════════════════════════════════════════════
function setLoadingState(isLoading) {
const btnContent = document.getElementById('btn-content');
const btnLoader = document.getElementById('btn-loader');
const button = document.querySelector('.btn-primary-gradient');
if (btnContent && btnLoader && button) {
if (isLoading) {
btnContent.style.display = 'none';
btnLoader.style.display = 'flex';
button.disabled = true;
button.style.opacity = '0.7';
button.style.cursor = 'not-allowed';
} else {
btnContent.style.display = 'flex';
btnLoader.style.display = 'none';
button.disabled = false;
button.style.opacity = '1';
button.style.cursor = 'pointer';
}
}
}
// ════════════════════════════════════════════════════════════════════════
// DISPLAY RESULTS
// ════════════════════════════════════════════════════════════════════════
function displayResults(result, domain, translationEnabled) {
const resultsPanel = document.getElementById('results');
if (!resultsPanel) return;
// Show results panel
resultsPanel.style.display = 'block';
// Smooth scroll to results
setTimeout(() => {
resultsPanel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 100);
// Update decision badge
const badge = document.getElementById('decision-badge');
if (badge) {
badge.textContent = result.label.replace('_', ' ');
badge.className = 'decision-badge-luxury ' +
(result.label === 'EQUIVALENT' ? 'equivalent' : 'not-equivalent');
}
// Update translation panel
const translationPanel = document.getElementById('translation-panel');
if (translationEnabled && result.translations && translationPanel) {
translationPanel.style.display = 'block';
// Translation A
const transA = result.translations.title_a;
if (transA) {
document.getElementById('trans_lang_a').textContent =
(transA.src_lang || 'auto').toUpperCase();
document.getElementById('trans_original_a').textContent = transA.original_text;
document.getElementById('trans_result_a').textContent = transA.translated_text;
}
// Translation B
const transB = result.translations.title_b;
if (transB) {
document.getElementById('trans_lang_b').textContent =
(transB.src_lang || 'auto').toUpperCase();
document.getElementById('trans_original_b').textContent = transB.original_text;
document.getElementById('trans_result_b').textContent = transB.translated_text;
}
} else if (translationPanel) {
translationPanel.style.display = 'none';
}
// Animate metrics
animateMetric('final-score', result.final_score, 3);
animateMetric('embedding-score', result.embedding_score, 3);
animateProgressBar('progress-final', result.final_score * 100);
animateProgressBar('progress-embed', result.embedding_score * 100);
// Translation score (if available)
const translationScoreCard = document.getElementById('translation-score-card');
const translationScoreValue = document.getElementById('translation-score');
if (result.translation_score !== null && translationScoreCard && translationScoreValue) {
translationScoreCard.style.display = 'block';
animateMetric('translation-score', result.translation_score, 3);
animateProgressBar('progress-trans', result.translation_score * 100);
} else if (translationScoreCard) {
translationScoreCard.style.display = 'none';
}
// Update other metrics
document.getElementById('confidence').textContent = result.confidence;
document.getElementById('processing-time').textContent = result.traces.total_time_ms + 'ms';
document.getElementById('method').textContent =
(result.method || 'labse_only').replace(/_/g, ' ').toUpperCase();
// Language detection
const langs = result.detected_languages;
document.getElementById('lang_detect_a').textContent =
LANGUAGE_NAMES[langs.title_a] || langs.title_a.toUpperCase();
document.getElementById('lang_detect_b').textContent =
LANGUAGE_NAMES[langs.title_b] || langs.title_b.toUpperCase();
document.getElementById('lang_conf_a').textContent =
Math.round(langs.confidence_a * 100) + '%';
document.getElementById('lang_conf_b').textContent =
Math.round(langs.confidence_b * 100) + '%';
// Metadata
const domainFormatted = domain.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
document.getElementById('domain-display').textContent = domainFormatted;
document.getElementById('threshold').textContent = result.adjusted_threshold.toFixed(3);
}
// ════════════════════════════════════════════════════════════════════════
// ANIMATED COUNTERS
// ════════════════════════════════════════════════════════════════════════
function animateMetric(elementId, targetValue, decimals = 0) {
const element = document.getElementById(elementId);
if (!element) return;
const duration = 1000; // 1 second
const startValue = 0;
const increment = (targetValue - startValue) / (duration / 16);
let currentValue = startValue;
const counter = setInterval(() => {
currentValue += increment;
if (currentValue >= targetValue) {
currentValue = targetValue;
clearInterval(counter);
}
element.textContent = currentValue.toFixed(decimals);
}, 16); // ~60fps
}
function animateProgressBar(elementId, targetWidth) {
const element = document.getElementById(elementId);
if (!element) return;
// Start from 0
element.style.width = '0%';
// Animate to target
setTimeout(() => {
element.style.width = Math.min(targetWidth, 100) + '%';
}, 100);
}
// ════════════════════════════════════════════════════════════════════════
// UTILITY FUNCTIONS
// ════════════════════════════════════════════════════════════════════════
function hideResults() {
const resultsPanel = document.getElementById('results');
if (resultsPanel) {
resultsPanel.style.display = 'none';
}
}
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
// Styling
Object.assign(notification.style, {
position: 'fixed',
top: '100px',
right: '20px',
padding: '1rem 1.5rem',
borderRadius: '12px',
color: 'white',
fontSize: '0.9rem',
fontWeight: '500',
zIndex: '10000',
opacity: '0',
transform: 'translateX(400px)',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
backdropFilter: 'blur(10px)'
});
// Type-specific colors
if (type === 'success') {
notification.style.background = 'linear-gradient(135deg, rgba(16, 185, 129, 0.9), rgba(5, 150, 105, 0.9))';
} else if (type === 'error') {
notification.style.background = 'linear-gradient(135deg, rgba(239, 68, 68, 0.9), rgba(220, 38, 38, 0.9))';
} else if (type === 'warning') {
notification.style.background = 'linear-gradient(135deg, rgba(245, 158, 11, 0.9), rgba(217, 119, 6, 0.9))';
} else {
notification.style.background = 'linear-gradient(135deg, rgba(59, 130, 246, 0.9), rgba(37, 99, 235, 0.9))';
}
document.body.appendChild(notification);
// Slide in
setTimeout(() => {
notification.style.opacity = '1';
notification.style.transform = 'translateX(0)';
}, 10);
// Slide out and remove
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateX(400px)';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// ════════════════════════════════════════════════════════════════════════
// SYSTEM HEALTH CHECK
// ════════════════════════════════════════════════════════════════════════
async function checkSystemHealth() {
try {
const response = await fetch('/health');
const data = await response.json();
console.log('%c🟒 System Health:', 'color: #10b981; font-weight: bold;');
console.log(' Status:', data.status);
console.log(' Version:', data.version);
console.log(' Models:', data.models);
console.log(' Cache:', data.cache_size);
console.log(' Device:', data.device);
} catch (error) {
console.warn('%c⚠️ Could not fetch health status:', 'color: #f59e0b;', error);
}
}
// ════════════════════════════════════════════════════════════════════════
// GLOBAL EXPORTS
// ════════════════════════════════════════════════════════════════════════
window.verifyTitles = verifyTitles;
window.loadExample = loadExample;
window.hideResults = hideResults;
// ════════════════════════════════════════════════════════════════════════
// CONSOLE BRANDING
// ════════════════════════════════════════════════════════════════════════
console.log('%c━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'color: #a78bfa;');
console.log('%c🧠 LinguaVerify AI v5.1 - Production Ready', 'color: #10b981; font-size: 14px; font-weight: bold;');
console.log('%c200+ Languages β€’ Dual-Path AI β€’ Text Visibility Fixed βœ…', 'color: #10b981; font-size: 11px;');
console.log('%c━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'color: #a78bfa;');
console.log('%cDeveloped with ❀️ by Your Name', 'color: #60a5fa; font-size: 10px;');
console.log('%c━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'color: #a78bfa;');