| |
| window.onload = function() { |
| console.log('Window fully loaded, initializing Tarjama app'); |
| |
| |
| const textTabLink = document.querySelector('nav ul li a[href="#text-translation"]'); |
| const docTabLink = document.querySelector('nav ul li a[href="#document-translation"]'); |
| const textSection = document.getElementById('text-translation'); |
| const docSection = document.getElementById('document-translation'); |
| |
| |
| const textTranslationForm = document.getElementById('text-translation-form'); |
| const docTranslationForm = document.getElementById('doc-translation-form'); |
| |
| |
| const textInput = document.getElementById('text-input'); |
| const textResult = document.getElementById('text-result'); |
| const docResult = document.getElementById('doc-result'); |
| const textOutput = document.getElementById('text-output'); |
| const docOutput = document.getElementById('doc-output'); |
| const docInputText = document.getElementById('doc-input-text'); |
| const textLoadingIndicator = document.getElementById('text-loading'); |
| const docLoadingIndicator = document.getElementById('doc-loading'); |
| const errorMessageElement = document.getElementById('error-message'); |
| const notificationElement = document.getElementById('notification'); |
| const charCountElement = document.getElementById('char-count'); |
| const fileNameDisplay = document.getElementById('file-name-display'); |
| const docFilename = document.getElementById('doc-filename'); |
| const docSourceLang = document.getElementById('doc-source-lang'); |
| |
| |
| const sourceLangText = document.getElementById('source-lang-text'); |
| const targetLangText = document.getElementById('target-lang-text'); |
| const sourceLangDoc = document.getElementById('source-lang-doc'); |
| const targetLangDoc = document.getElementById('target-lang-doc'); |
| |
| |
| const swapLanguages = document.getElementById('swap-languages'); |
| const swapLanguagesDoc = document.getElementById('swap-languages-doc'); |
| const copyTranslation = document.getElementById('copy-translation'); |
| const copyDocTranslation = document.getElementById('copy-doc-translation'); |
| const clearSource = document.getElementById('clear-source'); |
| |
| |
| const phraseButtons = document.querySelectorAll('.phrase-btn'); |
| |
| |
| const rtlLanguages = ['ar', 'he']; |
| |
| |
| if (textTabLink && docTabLink && textSection && docSection) { |
| textTabLink.addEventListener('click', function(e) { |
| e.preventDefault(); |
| docSection.classList.add('hidden'); |
| textSection.classList.remove('hidden'); |
| textTabLink.parentElement.classList.add('active'); |
| docTabLink.parentElement.classList.remove('active'); |
| }); |
| |
| docTabLink.addEventListener('click', function(e) { |
| e.preventDefault(); |
| textSection.classList.add('hidden'); |
| docSection.classList.remove('hidden'); |
| docTabLink.parentElement.classList.add('active'); |
| textTabLink.parentElement.classList.remove('active'); |
| }); |
| } |
| |
| |
| if (textInput && charCountElement) { |
| textInput.addEventListener('input', function() { |
| const charCount = textInput.value.length; |
| charCountElement.textContent = charCount; |
| |
| |
| if (charCount > 3000) { |
| charCountElement.className = 'char-count-warning'; |
| } else if (charCount > 2000) { |
| charCountElement.className = 'char-count-approaching'; |
| } else { |
| charCountElement.className = ''; |
| } |
| }); |
| } |
| |
| |
| if (phraseButtons && phraseButtons.length > 0) { |
| console.log('Found phrase buttons:', phraseButtons.length); |
| phraseButtons.forEach(button => { |
| button.addEventListener('click', function(e) { |
| e.preventDefault(); |
| console.log('Phrase button clicked'); |
| const phrase = this.getAttribute('data-text') || this.getAttribute('data-phrase'); |
| |
| if (phrase && textInput) { |
| console.log('Using phrase:', phrase); |
| |
| if (textSection.classList.contains('hidden')) { |
| console.log('Switching to text tab'); |
| docSection.classList.add('hidden'); |
| textSection.classList.remove('hidden'); |
| textTabLink.parentElement.classList.add('active'); |
| docTabLink.parentElement.classList.remove('active'); |
| } |
| |
| |
| if (typeof textInput.selectionStart === 'number') { |
| const startPos = textInput.selectionStart; |
| const endPos = textInput.selectionEnd; |
| const currentValue = textInput.value; |
| const spaceChar = currentValue && currentValue[startPos - 1] !== ' ' && startPos > 0 ? ' ' : ''; |
| |
| |
| textInput.value = currentValue.substring(0, startPos) + |
| spaceChar + phrase + |
| currentValue.substring(endPos); |
| |
| |
| textInput.selectionStart = startPos + phrase.length + spaceChar.length; |
| textInput.selectionEnd = textInput.selectionStart; |
| } else { |
| |
| const currentValue = textInput.value; |
| const spaceChar = currentValue && currentValue.length > 0 && currentValue[currentValue.length - 1] !== ' ' ? ' ' : ''; |
| textInput.value += spaceChar + phrase; |
| } |
| |
| |
| const inputEvent = new Event('input', { bubbles: true }); |
| textInput.dispatchEvent(inputEvent); |
| |
| |
| textInput.focus(); |
| |
| |
| const autoTranslate = this.getAttribute('data-auto-translate') === 'true'; |
| if (autoTranslate && textTranslationForm) { |
| textTranslationForm.dispatchEvent(new Event('submit')); |
| } |
| } |
| }); |
| }); |
| } |
| |
| |
| if (swapLanguages && sourceLangText && targetLangText) { |
| swapLanguages.addEventListener('click', function(e) { |
| e.preventDefault(); |
| |
| |
| if (sourceLangText.value === 'auto') { |
| showNotification('Cannot swap when source language is set to auto-detect.'); |
| return; |
| } |
| |
| |
| const sourceValue = sourceLangText.value; |
| const targetValue = targetLangText.value; |
| |
| |
| sourceLangText.value = targetValue; |
| targetLangText.value = sourceValue; |
| |
| |
| if (textOutput.textContent.trim() !== '') { |
| |
| textInput.value = textOutput.textContent; |
| |
| |
| const inputEvent = new Event('input', { bubbles: true }); |
| textInput.dispatchEvent(inputEvent); |
| |
| |
| if (textTranslationForm) { |
| textTranslationForm.dispatchEvent(new Event('submit')); |
| } |
| } |
| |
| |
| applyRtlStyling(sourceLangText.value, textInput); |
| applyRtlStyling(targetLangText.value, textOutput); |
| }); |
| } |
| |
| |
| if (swapLanguagesDoc && sourceLangDoc && targetLangDoc) { |
| swapLanguagesDoc.addEventListener('click', function(e) { |
| e.preventDefault(); |
| |
| |
| if (sourceLangDoc.value === 'auto') { |
| showNotification('Cannot swap when source language is set to auto-detect.'); |
| return; |
| } |
| |
| |
| const sourceValue = sourceLangDoc.value; |
| const targetValue = targetLangDoc.value; |
| |
| |
| sourceLangDoc.value = targetValue; |
| targetLangDoc.value = sourceValue; |
| }); |
| } |
| |
| |
| function applyRtlStyling(langCode, element) { |
| if (element) { |
| if (rtlLanguages.includes(langCode)) { |
| element.style.direction = 'rtl'; |
| element.style.textAlign = 'right'; |
| } else { |
| element.style.direction = 'ltr'; |
| element.style.textAlign = 'left'; |
| } |
| } |
| } |
| |
| |
| function handleLanguageChange() { |
| |
| if (sourceLangText && textInput) { |
| applyRtlStyling(sourceLangText.value, textInput); |
| } |
| |
| |
| if (targetLangText && textOutput) { |
| applyRtlStyling(targetLangText.value, textOutput); |
| } |
| |
| if (sourceLangDoc && docInputText) { |
| applyRtlStyling(sourceLangDoc.value, docInputText); |
| } |
| |
| if (targetLangDoc && docOutput) { |
| applyRtlStyling(targetLangDoc.value, docOutput); |
| } |
| } |
| |
| |
| if (sourceLangText) sourceLangText.addEventListener('change', handleLanguageChange); |
| if (targetLangText) targetLangText.addEventListener('change', handleLanguageChange); |
| if (sourceLangDoc) sourceLangDoc.addEventListener('change', handleLanguageChange); |
| if (targetLangDoc) targetLangDoc.addEventListener('change', handleLanguageChange); |
| |
| |
| if (copyTranslation) { |
| copyTranslation.addEventListener('click', function() { |
| if (textOutput && textOutput.textContent.trim() !== '') { |
| navigator.clipboard.writeText(textOutput.textContent) |
| .then(() => { |
| showNotification('Translation copied to clipboard!'); |
| }) |
| .catch(err => { |
| console.error('Error copying text: ', err); |
| showNotification('Failed to copy text. Please try again.'); |
| }); |
| } |
| }); |
| } |
| |
| |
| if (copyDocTranslation) { |
| copyDocTranslation.addEventListener('click', function() { |
| if (docOutput && docOutput.textContent.trim() !== '') { |
| navigator.clipboard.writeText(docOutput.textContent) |
| .then(() => { |
| showNotification('Document translation copied to clipboard!'); |
| }) |
| .catch(err => { |
| console.error('Error copying document: ', err); |
| showNotification('Failed to copy document. Please try again.'); |
| }); |
| } |
| }); |
| } |
| |
| |
| if (clearSource) { |
| clearSource.addEventListener('click', function() { |
| if (textInput) { |
| textInput.value = ''; |
| textOutput.textContent = ''; |
| |
| |
| const inputEvent = new Event('input', { bubbles: true }); |
| textInput.dispatchEvent(inputEvent); |
| |
| |
| textInput.focus(); |
| } |
| }); |
| } |
| |
| |
| const fileInput = document.getElementById('doc-input'); |
| const translateDocumentBtn = document.querySelector('#doc-translation-form .translate-button'); |
| |
| if (fileInput && fileNameDisplay && translateDocumentBtn) { |
| fileInput.addEventListener('change', function() { |
| if (this.files && this.files.length > 0) { |
| const fileName = this.files[0].name; |
| fileNameDisplay.textContent = `File selected: ${fileName}`; |
| fileNameDisplay.style.display = 'block'; |
| |
| |
| translateDocumentBtn.classList.add('active-button'); |
| |
| showNotification('Document uploaded successfully!'); |
| } else { |
| fileNameDisplay.textContent = ''; |
| fileNameDisplay.style.display = 'none'; |
| |
| |
| translateDocumentBtn.classList.remove('active-button'); |
| } |
| }); |
| } |
| |
| |
| if (textTranslationForm) { |
| textTranslationForm.addEventListener('submit', function(e) { |
| e.preventDefault(); |
| |
| const text = textInput.value.trim(); |
| if (!text) { |
| showNotification('Please enter text to translate.'); |
| return; |
| } |
| |
| const sourceLang = sourceLangText.value; |
| const targetLang = targetLangText.value; |
| |
| translateText(text, sourceLang, targetLang); |
| }); |
| } |
| |
| |
| if (docTranslationForm) { |
| docTranslationForm.addEventListener('submit', function(e) { |
| e.preventDefault(); |
| |
| const fileInput = document.getElementById('doc-input'); |
| if (!fileInput.files || fileInput.files.length === 0) { |
| showError('Please select a document to translate.'); |
| return; |
| } |
| |
| const file = fileInput.files[0]; |
| const sourceLang = sourceLangDoc.value; |
| const targetLang = targetLangDoc.value; |
| |
| translateDocument(file, sourceLang, targetLang); |
| }); |
| } |
| |
| |
| const fileUploadArea = document.querySelector('.file-upload-area'); |
| |
| if (fileUploadArea && fileInput) { |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
| fileUploadArea.addEventListener(eventName, preventDefaults, false); |
| }); |
| |
| function preventDefaults(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| |
| ['dragenter', 'dragover'].forEach(eventName => { |
| fileUploadArea.addEventListener(eventName, highlight, false); |
| }); |
| |
| ['dragleave', 'drop'].forEach(eventName => { |
| fileUploadArea.addEventListener(eventName, unhighlight, false); |
| }); |
| |
| function highlight() { |
| fileUploadArea.classList.add('highlight'); |
| } |
| |
| function unhighlight() { |
| fileUploadArea.classList.remove('highlight'); |
| } |
| |
| fileUploadArea.addEventListener('drop', handleDrop, false); |
| |
| function handleDrop(e) { |
| const dt = e.dataTransfer; |
| const files = dt.files; |
| |
| if (files && files.length > 0) { |
| fileInput.files = files; |
| const fileName = files[0].name; |
| fileNameDisplay.textContent = `File selected: ${fileName}`; |
| fileNameDisplay.style.display = 'block'; |
| |
| |
| if (translateDocumentBtn) { |
| translateDocumentBtn.classList.add('active-button'); |
| } |
| |
| showNotification('Document uploaded successfully!'); |
| } |
| } |
| } |
| |
| |
| function translateText(text, sourceLang, targetLang) { |
| if (textLoadingIndicator) textLoadingIndicator.style.display = 'block'; |
| if (errorMessageElement) errorMessageElement.style.display = 'none'; |
| |
| |
| fetch('/translate/text', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ |
| text: text, |
| source_lang: sourceLang, |
| target_lang: targetLang |
| }), |
| }) |
| .then(response => { |
| if (!response.ok) { |
| throw new Error(`HTTP error! Status: ${response.status}`); |
| } |
| return response.json(); |
| }) |
| .then(data => { |
| if (textLoadingIndicator) textLoadingIndicator.style.display = 'none'; |
| |
| if (data.success === false) { |
| throw new Error(data.error || 'Translation failed with an unknown error'); |
| } |
| |
| |
| if (data.detected_source_lang && sourceLang === 'auto') { |
| showNotification(`Detected language: ${getLanguageName(data.detected_source_lang)}`); |
| |
| |
| if (sourceLangText && data.detected_source_lang) { |
| |
| |
| const detectedOption = Array.from(sourceLangText.options).find( |
| option => option.value === data.detected_source_lang |
| ); |
| |
| if (detectedOption) { |
| |
| sourceLangText.parentElement.setAttribute('data-detected', |
| `Detected: ${detectedOption.text}`); |
| } |
| } |
| } |
| |
| |
| if (textOutput) { |
| textOutput.textContent = data.translated_text; |
| |
| |
| applyRtlStyling(targetLang, textOutput); |
| } |
| |
| |
| if (textResult) textResult.style.display = 'block'; |
| }) |
| .catch(error => { |
| console.error('Error during translation:', error); |
| |
| if (textLoadingIndicator) textLoadingIndicator.style.display = 'none'; |
| |
| |
| showError(`Translation error: ${error.message}`); |
| }); |
| } |
| |
| |
| function translateDocument(file, sourceLang, targetLang) { |
| if (docLoadingIndicator) docLoadingIndicator.style.display = 'block'; |
| if (errorMessageElement) errorMessageElement.style.display = 'none'; |
| |
| const formData = new FormData(); |
| formData.append('file', file); |
| formData.append('source_lang', sourceLang); |
| formData.append('target_lang', targetLang); |
| |
| fetch('/translate/document', { |
| method: 'POST', |
| body: formData, |
| }) |
| .then(response => { |
| if (!response.ok) { |
| throw new Error(`HTTP error! Status: ${response.status}`); |
| } |
| return response.json(); |
| }) |
| .then(data => { |
| if (docLoadingIndicator) docLoadingIndicator.style.display = 'none'; |
| |
| if (data.success === false) { |
| throw new Error(data.error || 'Document translation failed'); |
| } |
| |
| |
| if (data.detected_source_lang && sourceLang === 'auto') { |
| showNotification(`Detected document language: ${getLanguageName(data.detected_source_lang)}`); |
| } |
| |
| |
| if (docInputText) { |
| docInputText.textContent = data.original_text; |
| |
| if (data.detected_source_lang && sourceLang === 'auto') { |
| applyRtlStyling(data.detected_source_lang, docInputText); |
| } else { |
| applyRtlStyling(sourceLang, docInputText); |
| } |
| } |
|
|
| |
| if (docOutput) { |
| docOutput.textContent = data.translated_text; |
| |
| applyRtlStyling(targetLang, docOutput); |
| } |
|
|
| |
| if (docResult) { |
| docResult.classList.remove('hidden'); |
| docResult.style.display = 'flex'; |
| } |
| |
| |
| if (docFilename) docFilename.textContent = data.original_filename || file.name; |
| if (docSourceLang) docSourceLang.textContent = (data.detected_source_lang ? getLanguageName(data.detected_source_lang) : getLanguageName(sourceLang)); |
| |
| |
| const resultArea = document.querySelector('.document-result-area'); |
| if (resultArea) { |
| |
| const existingDownloadBtn = document.getElementById('download-translated-doc'); |
| if (existingDownloadBtn) { |
| existingDownloadBtn.remove(); |
| } |
| |
| |
| const downloadBtn = document.createElement('button'); |
| downloadBtn.id = 'download-translated-doc'; |
| downloadBtn.className = 'download-button'; |
| downloadBtn.innerHTML = '<i class="fas fa-download"></i> Download Translation'; |
| |
| |
| downloadBtn.addEventListener('click', function() { |
| downloadTranslatedDocument(data.translated_text, file.name, file.type); |
| }); |
| |
| |
| resultArea.appendChild(downloadBtn); |
| } |
| }) |
| .catch(error => { |
| console.error('Error during document translation:', error); |
| |
| if (docLoadingIndicator) docLoadingIndicator.style.display = 'none'; |
| |
| |
| showError(`Document translation error: ${error.message}`); |
| }); |
| } |
| |
| |
| function downloadTranslatedDocument(content, fileName, fileType) { |
| console.log('Downloading translated document:', fileName, fileType); |
| |
| let extension = ''; |
| if (fileName.toLowerCase().endsWith('.pdf')) { |
| extension = '.pdf'; |
| } else if (fileName.toLowerCase().endsWith('.docx')) { |
| extension = '.docx'; |
| } else if (fileName.toLowerCase().endsWith('.txt')) { |
| extension = '.txt'; |
| } else { |
| extension = '.txt'; |
| } |
| |
| |
| const baseName = fileName.substring(0, fileName.lastIndexOf('.')); |
| const translatedFileName = `${baseName}_translated${extension}`; |
| |
| |
| showNotification('Preparing document for download...'); |
| |
| |
| if (extension === '.txt') { |
| const blob = new Blob([content], { type: 'text/plain' }); |
| const url = URL.createObjectURL(blob); |
| |
| const a = document.createElement('a'); |
| a.href = url; |
| a.download = translatedFileName; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| URL.revokeObjectURL(url); |
| |
| showNotification('Document downloaded successfully!'); |
| } else { |
| |
| fetch('/download/translated-document', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ |
| content: content, |
| filename: translatedFileName, |
| original_type: fileType || 'text/plain' |
| }), |
| }) |
| .then(response => { |
| if (!response.ok) { |
| throw new Error(`Server error: ${response.status} ${response.statusText}`); |
| } |
| return response.blob(); |
| }) |
| .then(blob => { |
| |
| let mimeType; |
| if (extension === '.pdf') { |
| mimeType = 'application/pdf'; |
| } else if (extension === '.docx') { |
| mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; |
| } else { |
| mimeType = 'text/plain'; |
| } |
| |
| |
| const fileBlob = new Blob([blob], { type: mimeType }); |
| const url = URL.createObjectURL(fileBlob); |
| |
| |
| const a = document.createElement('a'); |
| a.href = url; |
| a.download = translatedFileName; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| URL.revokeObjectURL(url); |
| |
| showNotification('Document downloaded successfully!'); |
| }) |
| .catch(error => { |
| console.error('Error downloading document:', error); |
| showError(`Download error: ${error.message}`); |
| }); |
| } |
| } |
| |
| |
| function getLanguageName(code) { |
| |
| const langMap = { |
| 'ar': 'Arabic', |
| 'en': 'English', |
| 'fr': 'French', |
| 'es': 'Spanish', |
| 'de': 'German', |
| 'zh': 'Chinese', |
| 'ru': 'Russian', |
| 'ja': 'Japanese', |
| 'hi': 'Hindi', |
| 'auto': 'Auto-detect' |
| }; |
| |
| |
| if (langMap[code]) { |
| return langMap[code]; |
| } |
| |
| |
| if (sourceLangText) { |
| const option = Array.from(sourceLangText.options).find(opt => opt.value === code); |
| if (option) { |
| return option.text; |
| } |
| } |
| |
| |
| return code; |
| } |
| |
| |
| function showNotification(message) { |
| if (notificationElement) { |
| notificationElement.textContent = message; |
| notificationElement.style.display = 'block'; |
| notificationElement.classList.add('show'); |
| |
| |
| setTimeout(() => { |
| notificationElement.classList.remove('show'); |
| setTimeout(() => { |
| notificationElement.style.display = 'none'; |
| }, 300); |
| }, 3000); |
| } |
| } |
| |
| |
| function showError(message) { |
| if (errorMessageElement) { |
| errorMessageElement.textContent = message; |
| errorMessageElement.style.display = 'block'; |
| |
| |
| setTimeout(() => { |
| errorMessageElement.style.display = 'none'; |
| }, 5000); |
| } |
| } |
| |
| |
| handleLanguageChange(); |
| }; |
|
|