USER
let actionBubble = null;
let selectedTextName = "";
let selectedWords = [];
let wordCount = "";
let alertShown = false;
const textContainer = document.getElementById('text-with-blanks');
const wordContainer = document.getElementById('image-container');
let totalPages = 1;
let allTexts = [];
function showAlertOnceInInterval(message, type) {
if (!alertShown) {
showAlert(message, type);
alertShown = true;
// Resetowanie flagi po 5 sekundach
setTimeout(() => {
alertShown = false;
}, 5000); // 5000 milisekund = 5 sekund
}
}
/////////////////////////////////////////////////
// # LOGIKA WCZYTYWANIA TEKSTÓW #//
/////////////////////////////////////////////////
// Główna funkcja inicjalizująca aplikację
document.addEventListener('DOMContentLoaded', () => {
decideWhichTextToLoad();
});
// Pobranie tekstów z serwera z uwzględnieniem limitu i wyszukiwania
function decideWhichTextToLoad() {
const limit = document.getElementById('textLimit').value || 10;
const searchQuery = document.getElementById('searchInput').value || '';
const tagQuery = document.getElementById('tagInput').value || '';
fetch(`/user/get_texts_for_insert_words_to_text?limit=${limit}&page=${currentPage}&search=${searchQuery}&tag=${tagQuery}`)
.then(response => response.json())
.then(data => {
allTexts = data.text_names;
totalPages = data.total_pages; // Załóżmy, że serwer zwraca też liczbę stron
populateTextList(allTexts);
updatePaginationControls(); // Funkcja aktualizująca przyciski paginacji
showAlert('Lista tekstów została załadowana!', 'success');
})
.catch(error => {
console.error('Błąd przy pobieraniu listy tekstów:', error);
showAlert('Błąd przy pobieraniu listy tekstów.', 'danger');
});
}
// Funkcja generująca listę tekstów
function populateTextList(textNames) {
const textList = document.getElementById('textList');
textList.innerHTML = ''; // Czyszczenie listy
textNames.forEach(text => {
const listItem = createTextListItem(text);
textList.appendChild(listItem);
});
}
// Funkcja tworząca element listy tekstów
function createTextListItem(text) {
const listItem = document.createElement('li');
listItem.className = 'list-group-item';
listItem.dataset.name = text.name;
listItem.innerText = text.name;
listItem.addEventListener('click', () => fetchAndLoadText(text.name));
return listItem;
}
function updatePaginationControls() {
const nextPageBtn = document.getElementById('nextPage');
const prevPageBtn = document.getElementById('prevPage');
nextPageBtn.disabled = currentPage >= totalPages;
prevPageBtn.disabled = currentPage <= 1;
}
function nextPage() {
if (currentPage < totalPages) {
currentPage++;
decideWhichTextToLoad();
}
}
function prevPage() {
if (currentPage > 1) {
currentPage--;
decideWhichTextToLoad();
}
}
// Filtrowanie tekstów po nazwie
function filterTextsByName() {
currentPage = 1;
const query = document.getElementById('searchInput').value.toLowerCase();
decideWhichTextToLoad();
}
function filterTextsByTag() {
currentPage = 1;
const tag = document.getElementById('tagInput').value.toLowerCase();
decideWhichTextToLoad();
}
///////////////////////////////////////////////
// # LOGIKA WYŚWIETLANIA TEKSTU #//
///////////////////////////////////////////////
// Funkcja pobierająca i wyświetlająca wybrany tekst
function fetchAndLoadText(textName) {
selectedWords = [];
wordCount = {};
// Usunięcie podświetlenia z poprzednio aktywnego elementu
const previousActiveItem = document.querySelector('.list-group-item.active-text');
if (previousActiveItem) {
previousActiveItem.classList.remove('active-text');
}
// Znalezienie klikniętego elementu listy na podstawie nazwy tekstu i dodanie klasy podświetlenia
const activeItem = document.querySelector(`li[data-name="${textName}"]`);
if (activeItem) {
activeItem.classList.add('active-text');
}
if(actionBubble != null){document.body.removeChild(actionBubble);actionBubble = null;} // if exist actionBubble in html remove from html
fetch(`/user/get_texts_data_for_insert_fot_text?name=${encodeURIComponent(textName)}`)
.then(response => response.json())
.then(data => {
const textData = data.texts[0];
wordCount = calculateWordCount(textData);
const fullTextHtml = generateTextHtml(textData);
selectedTextName = textName;
updateTextContainer(fullTextHtml); // Aktualizacja kontenera z tekstem
initializeWordContainer(); // Inicjalizacja kontenera z rozsypanką słów
showAlert(`Tekst: "${textName}" został załadowany!`, 'success');
})
.catch(error => {
console.error('Błąd przy pobieraniu tekstu:', error);
showAlert('Błąd przy wczytywaniu tekstu.', 'danger');
});
}
// Funkcja obliczająca ilość brakujących słów
function calculateWordCount(textData) {
return textData.sentences.reduce((count, sentence) => {
sentence.missing_words.forEach(missingWord => {
const word = missingWord.word;
count[word] = (count[word] || 0) + 1;
});
return count;
}, {});
}
// Funkcja generująca HTML dla całego tekstu
function generateTextHtml(textData) {
let bufforMissingWord = null; // Używamy null do sprawdzenia, czy brakujące słowo zmieniło się
return textData.sentences.map((sentence, sentenceIndex) => {
return sentence.english.split(' ').map((word, positionIndex) => {
const missingWord = sentence.missing_words.find(mw => {
const isMultiplePositions = Array.isArray(mw.position);
return isMultiplePositions
? mw.position.includes(positionIndex + 1)
: mw.position === positionIndex + 1;
});
if (missingWord && (bufforMissingWord === null || bufforMissingWord.word !== missingWord.word)) {
// Jeżeli brakujące słowo się zmieniło
if (bufforMissingWord !== null) {
// Dodaj HTML dla poprzedniego brakującego słowa, jeżeli istnieje
const missingWordLength = bufforMissingWord.word.split(' ').length;
positionIndex += missingWordLength - 1;
}
// Zaktualizuj bufforMissingWord
bufforMissingWord = missingWord;
return `<span class="droppable word-button" data-word="${missingWord.word}" data-index="${sentenceIndex}-${positionIndex}">____</span>`;
}
const isPartOfMissingWord = sentence.missing_words.some(mw => {
return Array.isArray(mw.position) ? mw.position.includes(positionIndex + 1) : mw.position === positionIndex + 1;
});
if (!isPartOfMissingWord) {
return `<button class="word-button" data-word="${word}" data-index="${sentenceIndex}-${positionIndex}">${word}</button>`;
}
return ''; // Jeśli słowo jest częścią brakującego słowa, nie generujemy HTML dla niego
}).join(' ');
}).join(' ');
}
// Funkcja aktualizująca zawartość kontenera z tekstem
function updateTextContainer(html) {
textContainer.innerHTML = html;
}
// Funkcja inicjalizująca kontener z rozsypanką słów
function initializeWordContainer() {
updateWordContainer();
}
function updateWordContainer() {
wordContainer.innerHTML = ''; // Czyszczenie kontenera
const words = shuffleArray(getAvailableWords());
words.forEach(createDraggableWord); // Tworzenie słów do przeciągania
}
// Funkcja zwracająca dostępne słowa
function getAvailableWords() {
let words = [];
Object.keys(wordCount).forEach(word => {
if (wordCount[word] > 0) {
for (let i = 0; i < wordCount[word]; i++) {
words.push(word);
}
}
});
return words;
}
// Funkcja do losowego przetasowania tablicy
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// Funkcja tworząca elementy słów do przeciągania
function createDraggableWord(word) {
const wordElement = document.createElement('div');
wordElement.classList.add('image-option');
wordElement.innerText = word;
wordElement.draggable = true;
wordElement.ondragstart = drag;
wordContainer.appendChild(wordElement);
}
///////////////////////////////////////////////
// # LOGIKA WSTAWIANIA SŁÓWEK W PUSTE POLA #//
///////////////////////////////////////////////
// Obsługa zaznaczania i odznaczania słów
textContainer.addEventListener('mousedown', function(event) {
if (event.target.classList.contains('word-button')) {
isMouseDown = true;
toggleSelection(event.target);
updateBubble(event);
}
});
textContainer.addEventListener('mouseover', function(event) {
if (isMouseDown && event.target.classList.contains('word-button')) {
toggleSelection(event.target);
updateBubble(event);
}
});
textContainer.addEventListener('mouseup', function(event) {
isMouseDown = false;
if (selectedWords.length > 0) {
updateBubble(event, true);
}
});
document.addEventListener('mouseup', function() {
isMouseDown = false;
});
let lastSelectedIndex = -1; // Przechowuje ostatni zaznaczony indeks
function toggleSelection(element) {
const word = element.dataset.word; // Pobieramy słowo z atrybutu data
const wordIndex = element.dataset.index; // Pobieramy indeks słowa
const [sentenceIndex, positionIndex] = wordIndex.split('-').map(Number);
// Sprawdzamy, czy element jest już zaznaczony (usuwanie zaznaczenia)
if (element.classList.contains('selected')) {
element.classList.remove('selected');
selectedWords = selectedWords.filter(selectedWord => selectedWord !== word);
lastSelectedIndex = selectedWords.length > 0 ? wordIndex : -1;
return;
}
// Jeśli nie ma zaznaczonych słów, zaznacz pierwsze słowo
if (selectedWords.length === 0) {
element.classList.add('selected');
selectedWords.push(word);
lastSelectedIndex = wordIndex;
return;
}
const [lastSentenceIndex, lastPositionIndex] = lastSelectedIndex.split('-').map(Number);
// Sprawdzenie, czy bieżący indeks jest poprawną kontynuacją zaznaczenia
if (sentenceIndex === lastSentenceIndex && positionIndex >= lastPositionIndex) {
// Zaznaczenie wszystkich słów między ostatnim zaznaczonym a bieżącym
const buttons = [...textContainer.querySelectorAll('.word-button')];
const startIndex = buttons.findIndex(btn => btn.dataset.index === lastSelectedIndex);
const endIndex = buttons.findIndex(btn => btn.dataset.index === wordIndex);
for (let i = startIndex; i <= endIndex; i++) {
const btn = buttons[i];
if (!btn.classList.contains('selected')) {
btn.classList.add('selected');
selectedWords.push(btn.dataset.word);
}
}
lastSelectedIndex = wordIndex;
selectedWords = [selectedWords.join(' ')]; // Łączymy słowa w jedną frazę
} else {
showAlert("Słowa muszą być zaznaczane po kolei!", 'warning');
}
if (selectedWords.length === 0 && actionBubble) {
actionBubble.style.display = 'none';
}
}
// Obsługa przeciągania i upuszczania
function allowDrop(event) {
event.preventDefault();
}
function drag(event) {
event.dataTransfer.setData('text', event.target.innerText);
}
function drop(event) {
event.preventDefault();
const draggedWord = event.dataTransfer.getData('text');
const target = event.target;
if (target.classList.contains('droppable') && draggedWord === target.dataset.word) {
target.innerText = draggedWord;
target.classList.add('correct');
target.classList.remove('droppable');
target.classList.add('word-button');
wordCount[draggedWord]--;
updateWordContainer(); // Aktualizujemy rozsypankę słów
}
}
// Obsługa urządzeń dotykowych (mobile)
textContainer.addEventListener('touchstart', function(event) {
if (event.target.classList.contains('word-button')) {
toggleSelection(event.target);
updateBubble(event.touches[0]);
}
});
textContainer.addEventListener('touchmove', function(event) {
const touch = event.touches[0];
const element = document.elementFromPoint(touch.clientX, touch.clientY);
if (element && element.classList.contains('word-button')) {
toggleSelection(element);
updateBubble(touch);
}
});
textContainer.addEventListener('touchend', function(event) {
if (selectedWords.length > 0) {
updateBubble(event.changedTouches[0], true);
}
});
document.addEventListener('dragover', allowDrop);
document.addEventListener('drop', drop);
/////////////////////////////////////////////////
// # LOGIKA DYMKA ZAZNACZANIA SŁÓWEK #//
/////////////////////////////////////////////////
function updateBubble(event, finalizePosition = false) {
const firstSelectedWord = document.querySelector('.selected');
if (selectedWords.length > 0 && firstSelectedWord) {
const rect = firstSelectedWord.getBoundingClientRect();
const x = rect.left;
const y = rect.top - 40;
if (!actionBubble) {
createActionBubble();
}
updateBubblePosition(x, y);
}
}
function createActionBubble() {
actionBubble = document.createElement('div');
actionBubble.id = 'action-bubble';
actionBubble.style.position = 'absolute';
actionBubble.style.padding = '10px';
actionBubble.style.border = '1px solid #ccc';
actionBubble.style.borderRadius = '5px';
actionBubble.style.backgroundColor = '#fff';
actionBubble.style.boxShadow = '0px 2px 5px rgba(0,0,0,0.2)';
actionBubble.innerHTML = `
<button id="translateButton">Tłumacz</button>
<button id="selectSentenceButton">Zaznacz całe zdanie</button>
<button id="clearSelectionButton">Usuń zaznaczenie</button>
<button id="translateButtonGoogle">Google API</button>
`;
document.body.appendChild(actionBubble);
addEventListeners();
}
function addEventListeners() {
document.getElementById('translateButton').addEventListener('click', handleTranslate);
document.getElementById('selectSentenceButton').addEventListener('click', selectEntireSentence);
document.getElementById('clearSelectionButton').addEventListener('click', clearSelections);
document.getElementById('translateButtonGoogle').addEventListener('click', handleGoogleTranslate);
}
function handleTranslate() {
const textToTranslate = selectedWords.join(' ');
// Wykonywanie fetch do API
fetch('/user/model_fb_translate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: textToTranslate }),
})
.then(response => response.json())
.then(data => {
if (data.translatedText) {
updateTranslationHistory(textToTranslate, data.translatedText);
} else {
showAlert('Brak tłumaczenia', 'warning');
}
actionBubble.style.display = 'none';
})
.catch(error => {
console.error('Error:', error);
showAlert('Brak tłumaczenia', 'warning');
actionBubble.style.display = 'none';
});
}
async function handleGoogleTranslate() {
console.log("selectedWords", selectedWords);
const translatedText = await translateText(selectedWords, 'pl'); // Przetłumaczenie na polski
console.log("const translatedText", translatedText);
updateTranslationHistory(selectedWords, translatedText); // Aktualizacja historii tłumaczeń
}
function updateBubblePosition(x, y) {
actionBubble.style.left = `${x}px`;
actionBubble.style.top = `${y}px`;
actionBubble.style.display = 'block';
}
// Zbieranie zdania z zaznaczonych słów
function selectEntireSentence() {
const firstSelectedWord = document.querySelector('.selected');
if (firstSelectedWord) {
// ZnajdĹş najbliższy element .word-button, aby określić początek zdania
const sentenceElement = firstSelectedWord.closest('.card-body').querySelectorAll('.word-button');
if (sentenceElement) {
let startIndex = Array.from(sentenceElement).indexOf(firstSelectedWord);
let selectedSentence = [];
if (startIndex !== -1) {
// Zaznacz całe zdanie od początkowego do końcowego indeksu
for (let i = startIndex; i < sentenceElement.length; i++) {
const wordElement = sentenceElement[i];
if (wordElement.dataset.index.split('-')[0] !== firstSelectedWord.dataset.index.split('-')[0]) {
break;
}
wordElement.classList.add('selected');
selectedSentence.push(wordElement.dataset.word);
}
// Złóż zdanie z zaznaczonych słów
let sentenceString = selectedSentence.join(' ');
// Szukaj, czy nowe zdanie zawiera się w już istniejących, lub je rozszerza
let wasUpdated = false;
selectedWords = selectedWords.map(existingSentence => {
// Jeśli istnieje już fragment nowego zdania w istniejącym
if (sentenceString.startsWith(existingSentence)) {
// Dodajemy brakującą część do istniejącego zdania
wasUpdated = true;
return existingSentence.split(' ')[0] + sentenceString.slice(existingSentence.length);
} else if (existingSentence.startsWith(sentenceString)) {
// Jeśli całe nowe zdanie jest już częścią istniejącego, nic nie robimy
wasUpdated = true;
return existingSentence;
}
});
// Jeśli zdanie nie zostało dodane ani rozszerzone, dodaj nowe zdanie
if (!wasUpdated) {
selectedWords.push(sentenceString);
}
}
}
}
}
function clearSelections() {
// Funkcja do usuwania wszystkich zaznaczeń
document.querySelectorAll('.selected').forEach(word => {
word.classList.remove('selected');
});
selectedWords = [];
actionBubble.style.display = 'none';
}
/////////////////////////////////////////////////
// # CZĘŚĆ ZARZĄDZANIA HISTORIĄ #//
/////////////////////////////////////////////////
document.getElementById('save-history-to-file').addEventListener('click', function() {
const translations = [];
document.querySelectorAll('#translation-history li').forEach(function(item) {
const originalText = item.querySelector('.editable').textContent.split(' - ')[0].trim();
const translatedText = item.querySelector('.editable').textContent.split(' - ')[1].trim();
translations.push({
"originalText": originalText,
"translatedText": translatedText,
"metadata": {
"translationSource": "manual" // Wartość domyślna, można zmienić
}
});
});
fetch('/user/save_history_translations_for_insert_words_to_text?name=' + encodeURIComponent(selectedTextName), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ translations: translations }),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
showAlert('Historia tłumaczeń została pomyślnie zapisana.', 'success');
})
.catch((error) => {
console.error('Error:', error);
});
});
// Obsługa przycisku wczytywania historii z pliku
document.getElementById('load-history-from-file').addEventListener('click', function() {
console.log(selectedTextName)
fetch('/user/load_translation_history_for_insert_wors_to_text?name=' + encodeURIComponent(selectedTextName))
.then(response => response.json())
.then(data => {
const translationHistory = document.getElementById('translation-history');
translationHistory.innerHTML = ''; // Wyczyść obecne elementy listy
if (data.translations && data.translations.length > 0) {
data.translations.forEach(function(translation) {
updateTranslationHistory(translation.originalText, translation.translatedText);
});
showAlert('Historia tłumaczeń została pomyślnie wczytana.', 'success');
} else {
showAlert('Brak zapisanych tłumaczeń dla wybranego pliku.', 'warning');
}
})
.catch((error) => {
console.error('Error:', error);
showAlert('Wystąpił błąd podczas wczytywania historii tłumaczeń.', 'danger');
});
});
// Funkcja do wykonania zapytania do Google Translate API v1 bez klucza
async function translateText(text, targetLang = 'en') {
console.log("translateText", text)
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
try {
const response = await fetch(url);
const data = await response.json();
showAlert('Tłumaczenie wykonane pomyślnie');
return data[0][0][0]; // Zwracany przetłumaczony tekst
} catch (error) {
console.error('Błąd tłumaczenia:', error);
showAlert('Błąd tłumaczenia', 'danger');
return 'Błąd tłumaczenia';
}
}
// Funkcja tworząca nowy wpis historii tłumaczeń
function createNewHistoryEntry(originalText, translatedText) {
const newEntry = document.createElement('li');
newEntry.innerHTML = `
<span class="editable">${originalText} - ${translatedText}</span>
<button class="delete-btn btn btn-sm btn-danger ml-2">Usuń</button>
<button class="retranslate-btn btn btn-sm btn-primary ml-2">Tłumacz google</button>
`;
return newEntry;
}
// Funkcja dodająca wpis do historii
function addEntryToHistory(newEntry) {
const historyList = document.getElementById('translation-history');
historyList.appendChild(newEntry);
//showAlert('Dodano nowe tłumaczenie');
}
// Funkcja obsługująca edytowanie wpisu
function enableEditing(editableSpan) {
editableSpan.addEventListener('click', function () {
if (!this.isContentEditable) {
this.contentEditable = true;
this.focus();
}
});
editableSpan.addEventListener('blur', function () {
this.contentEditable = false;
showAlert('Zapisano zmiany');
});
editableSpan.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
this.blur();
}
});
}
// Funkcja obsługująca usuwanie wpisu
function enableDeletion(deleteButton, newEntry) {
const historyList = document.getElementById('translation-history');
deleteButton.addEventListener('click', function() {
historyList.removeChild(newEntry);
showAlert('Usunięto tłumaczenie', 'info');
});
}
// Funkcja obsługująca ponowne tłumaczenie
function enableRetranslation(retranslateButton, originalText, editableSpan) {
retranslateButton.addEventListener('click', async function() {
console.log("originalText", originalText)
const newTranslation = await translateText(originalText, 'pl'); // Tłumaczenie na polski
editableSpan.innerText = `${originalText} - ${newTranslation}`;
showAlert('Zaktualizowano tłumaczenie przez Google');
});
}
// Główna funkcja aktualizująca historię tłumaczeń
function updateTranslationHistory(originalText, translatedText) {
const newEntry = createNewHistoryEntry(originalText, translatedText);
addEntryToHistory(newEntry);
const editableSpan = newEntry.querySelector('.editable');
const deleteButton = newEntry.querySelector('.delete-btn');
const retranslateButton = newEntry.querySelector('.retranslate-btn');
enableEditing(editableSpan);
enableDeletion(deleteButton, newEntry);
enableRetranslation(retranslateButton, originalText, editableSpan);
// Wyświetl powiadomienie o dodaniu nowego wpisu do historii
showAlertOnceInInterval('Nowe wpisy do historii tłumaczeń zostały dodane.', 'success');
}
// Funkcja czyszczenia całej historii
document.getElementById('clear-history').addEventListener('click', function() {
const historyList = document.getElementById('translation-history');
historyList.innerHTML = ''; // Usuwa wszystkie elementy
showAlert('Wyczyszczono całą historię', 'warning');
});
! ## Teraz znając logikę wczytującą tekst utwóż logikę wyświetlania tego tekstu z podziałem na strony, tak jak wyświetla się książkę, każde słowo musi być klikalne. Te funkcje których nie modyfikujesz pisz "pass"