Advertisement
jdalbey

Vocabulary practice not persisting

Dec 28th, 2024
45
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>Vocabulary Practice</title>
  5.     <style>
  6.         .hidden { display: none; }
  7.         .word { font-size: 24px; margin: 20px 0; }
  8.         .error { color: red; }
  9.         .success { color: green; margin: 10px 0; }
  10.         .container {
  11.             max-width: 800px;
  12.             margin: 20px auto;
  13.             padding: 20px;
  14.             font-family: Arial, sans-serif;
  15.         }
  16.         .definition-choice {
  17.             padding: 5px;
  18.             margin: 5px 0;
  19.             cursor: pointer;
  20.             border: 1px solid #ddd;
  21.         }
  22.         .definition-choice:hover {
  23.             background-color: #f0f0f0;
  24.         }
  25.         .selected {
  26.             background-color: #e0e0e0;
  27.         }
  28.         .section {
  29.             border: 1px solid #ccc;
  30.             padding: 15px;
  31.             margin: 15px 0;
  32.         }
  33.         .stats {
  34.             margin: 10px 0;
  35.             padding: 10px;
  36.             background-color: #f5f5f5;
  37.         }
  38.         .mode-switch {
  39.             margin: 10px 0;
  40.         }
  41.         .word-link {
  42.             color: #0066cc;
  43.             cursor: pointer;
  44.             text-decoration: underline;
  45.         }
  46.         .word-link:hover {
  47.             color: #003366;
  48.         }
  49.         .word-table {
  50.             width: 100%;
  51.             border-collapse: collapse;
  52.         }
  53.         .word-table td {
  54.             padding: 8px;
  55.             vertical-align: top;
  56.             width: 33.33%;
  57.             border: 1px solid #ddd;
  58.         }
  59.         .definition {
  60.             color: #666;
  61.             font-size: 0.9em;
  62.             margin-top: 5px;
  63.         }
  64.         button:disabled {
  65.             background-color: #cccccc;
  66.             cursor: not-allowed;
  67.         }
  68.     </style>
  69. </head>
  70. <body>
  71.     <div class="container">
  72.         <h1>Vocabulary Practice</h1>
  73.         <div class="section">
  74.             <h2>Add New Word</h2>
  75.             <div>
  76.                 <input type="text" id="newWord" placeholder="Enter word">
  77.                 <button onclick="lookupWord()">Look up</button>
  78.             </div>
  79.             <div id="definitionChoices"></div>
  80.             <div id="error" class="error"></div>
  81.             <div id="addSuccess" class="success hidden">Word added successfully!</div>
  82.         </div>
  83.  
  84.         <div class="stats">
  85.             Words in your list: <span id="wordCount">0</span>
  86.         </div>
  87.  
  88.         <div class="section">
  89.             <h2>Practice</h2>
  90.             <div class="mode-switch">
  91.                 <button onclick="toggleMode('practice')" id="practiceButton" disabled>Practice Mode</button>
  92.                 <button onclick="toggleMode('browse')" id="browseButton">Browse All Words</button>
  93.             </div>
  94.            
  95.             <div id="practiceContent">
  96.                 <div id="currentWord" class="word">Enter a word above to begin practicing</div>
  97.                 <button id="showAnswer" onclick="toggleAnswer()" class="hidden">Show Definition</button>
  98.                 <div id="answer" class="hidden"></div>
  99.                 <div id="responseButtons" class="hidden">
  100.                     <button onclick="handleResponse(false)">Incorrect</button>
  101.                     <button onclick="handleResponse(true)">Correct</button>
  102.                 </div>
  103.             </div>
  104.  
  105.             <div id="browseContent" class="hidden">
  106.                 <table class="word-table" id="wordList"></table>
  107.             </div>
  108.         </div>
  109.     </div>
  110.  
  111.     <script>
  112.         // IndexedDB helper class
  113.         class VocabStore {
  114.             constructor() {
  115.                 this.DB_NAME = 'VocabularyDB';
  116.                 this.STORE_NAME = 'words';
  117.                 this.VERSION = 1;
  118.                 this.db = null;
  119.             }
  120.  
  121.             async init() {
  122.                 return new Promise((resolve, reject) => {
  123.                     const request = indexedDB.open(this.DB_NAME, this.VERSION);
  124.  
  125.                     request.onerror = () => reject(request.error);
  126.                     request.onsuccess = () => {
  127.                         this.db = request.result;
  128.                         resolve();
  129.                     };
  130.  
  131.                     request.onupgradeneeded = (event) => {
  132.                         const db = event.target.result;
  133.                         if (!db.objectStoreNames.contains(this.STORE_NAME)) {
  134.                             const store = db.createObjectStore(this.STORE_NAME, { keyPath: 'id' });
  135.                             store.createIndex('word', 'word', { unique: true });
  136.                             store.createIndex('nextReview', 'nextReview');
  137.                         }
  138.                     };
  139.                 });
  140.             }
  141.  
  142.             async saveWords(words) {
  143.                 return new Promise((resolve, reject) => {
  144.                     const transaction = this.db.transaction(this.STORE_NAME, 'readwrite');
  145.                     const store = transaction.objectStore(this.STORE_NAME);
  146.  
  147.                     // Clear existing words
  148.                     store.clear();
  149.  
  150.                     // Add all words
  151.                     words.forEach(word => {
  152.                         store.add(word);
  153.                     });
  154.  
  155.                     transaction.oncomplete = () => resolve();
  156.                     transaction.onerror = () => reject(transaction.error);
  157.                 });
  158.             }
  159.  
  160.             async getAllWords() {
  161.                 return new Promise((resolve, reject) => {
  162.                     const transaction = this.db.transaction(this.STORE_NAME, 'readonly');
  163.                     const store = transaction.objectStore(this.STORE_NAME);
  164.                     const request = store.getAll();
  165.  
  166.                     request.onsuccess = () => {
  167.                         const words = request.result.map(word => ({
  168.                             ...word,
  169.                             nextReview: new Date(word.nextReview)
  170.                         }));
  171.                         resolve(words);
  172.                     };
  173.                     request.onerror = () => reject(request.error);
  174.                 });
  175.             }
  176.  
  177.             async addWord(word) {
  178.                 return new Promise((resolve, reject) => {
  179.                     const transaction = this.db.transaction(this.STORE_NAME, 'readwrite');
  180.                     const store = transaction.objectStore(this.STORE_NAME);
  181.                     const request = store.add(word);
  182.  
  183.                     request.onsuccess = () => resolve();
  184.                     request.onerror = () => reject(request.error);
  185.                 });
  186.             }
  187.  
  188.             async updateWord(word) {
  189.                 return new Promise((resolve, reject) => {
  190.                     const transaction = this.db.transaction(this.STORE_NAME, 'readwrite');
  191.                     const store = transaction.objectStore(this.STORE_NAME);
  192.                     const request = store.put(word);
  193.  
  194.                     request.onsuccess = () => resolve();
  195.                     request.onerror = () => reject(request.error);
  196.                 });
  197.             }
  198.  
  199.             async deleteWord(wordId) {
  200.                 return new Promise((resolve, reject) => {
  201.                     const transaction = this.db.transaction(this.STORE_NAME, 'readwrite');
  202.                     const store = transaction.objectStore(this.STORE_NAME);
  203.                     const request = store.delete(wordId);
  204.  
  205.                     request.onsuccess = () => resolve();
  206.                     request.onerror = () => reject(request.error);
  207.                 });
  208.             }
  209.         }
  210.  
  211.         let words = [];
  212.         let currentWord = null;
  213.         let definitions = [];
  214.         let currentMode = 'practice';
  215.         const vocabStore = new VocabStore();
  216.  
  217.         function toggleMode(mode) {
  218.             currentMode = mode;
  219.             document.getElementById('practiceContent').classList.toggle('hidden', mode !== 'practice');
  220.             document.getElementById('browseContent').classList.toggle('hidden', mode !== 'browse');
  221.             document.getElementById('practiceButton').disabled = mode === 'practice';
  222.             document.getElementById('browseButton').disabled = mode === 'browse';
  223.            
  224.             if (mode === 'browse') {
  225.                 showWordList();
  226.             } else {
  227.                 showNextWord();
  228.             }
  229.         }
  230.  
  231.         function showWordList() {
  232.             const wordList = document.getElementById('wordList');
  233.             wordList.innerHTML = '';
  234.            
  235.             if (words.length === 0) {
  236.                 wordList.innerHTML = '<tr><td>No words in your list yet. Add some words above!</td></tr>';
  237.                 return;
  238.             }
  239.  
  240.             const sortedWords = [...words].sort((a, b) => a.word.localeCompare(b.word));
  241.            
  242.             let tableHTML = '';
  243.             for (let i = 0; i < sortedWords.length; i += 3) {
  244.                 tableHTML += '<tr>';
  245.                 for (let j = 0; j < 3; j++) {
  246.                     const wordObj = sortedWords[i + j];
  247.                     if (wordObj) {
  248.                         tableHTML += `
  249.                             <td>
  250.                                 <span class="word-link" onclick="toggleDefinition('${wordObj.id}')">${wordObj.word}</span>
  251.                                 <div id="def-${wordObj.id}" class="definition hidden">${wordObj.definition}</div>
  252.                                 <button onclick="deleteWord('${wordObj.id}')" style="float: right; color: red;">X</button>
  253.                             </td>`;
  254.                     } else {
  255.                         tableHTML += '<td></td>';
  256.                     }
  257.                 }
  258.                 tableHTML += '</tr>';
  259.             }
  260.             wordList.innerHTML = tableHTML;
  261.         }
  262.  
  263.  
  264.         function toggleDefinition(wordId) {
  265.             const defElement = document.getElementById(`def-${wordId}`);
  266.             defElement.classList.toggle('hidden');
  267.         }
  268.  
  269.         function updateStats() {
  270.             document.getElementById('wordCount').textContent = words.length;
  271.             document.getElementById('practiceButton').disabled = words.length === 0 && currentMode === 'practice';
  272.         }
  273.  
  274.         async function lookupWord() {
  275.             const word = document.getElementById('newWord').value.trim();
  276.             if (!word) return;
  277.  
  278.             const error = document.getElementById('error');
  279.             const choices = document.getElementById('definitionChoices');
  280.             const success = document.getElementById('addSuccess');
  281.             choices.innerHTML = '';
  282.             error.textContent = '';
  283.             success.classList.add('hidden');
  284.  
  285.             try {
  286.                 const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${word}`);
  287.                 if (!response.ok) throw new Error('Word not found');
  288.                
  289.                 const data = await response.json();
  290.                 definitions = data.flatMap(entry =>
  291.                     entry.meanings.flatMap(meaning =>
  292.                         meaning.definitions.map(def => ({
  293.                             definition: def.definition,
  294.                             partOfSpeech: meaning.partOfSpeech
  295.                         }))
  296.                     )
  297.                 );
  298.  
  299.                 choices.innerHTML = '<p>Click a definition below to add this word:</p>';
  300.                 definitions.forEach((def, index) => {
  301.                     const div = document.createElement('div');
  302.                     div.className = 'definition-choice';
  303.                     div.innerHTML = `<strong>${def.partOfSpeech}</strong>: ${def.definition}`;
  304.                     div.onclick = () => selectDefinition(index);
  305.                     choices.appendChild(div);
  306.                 });
  307.             } catch (err) {
  308.                 error.textContent = err.message;
  309.             }
  310.         }
  311.  
  312.         function selectDefinition(index) {
  313.             const choices = document.querySelectorAll('.definition-choice');
  314.             choices.forEach(choice => choice.classList.remove('selected'));
  315.             choices[index].classList.add('selected');
  316.  
  317.             const word = document.getElementById('newWord').value.trim();
  318.             addWord(word, definitions[index].definition);
  319.            
  320.             document.getElementById('addSuccess').classList.remove('hidden');
  321.             document.getElementById('newWord').value = '';
  322.             setTimeout(() => {
  323.                 document.getElementById('addSuccess').classList.add('hidden');
  324.                 document.getElementById('definitionChoices').innerHTML = '';
  325.             }, 2000);
  326.            
  327.             updateStats();
  328.             if (!currentWord && currentMode === 'practice') {
  329.                 showNextWord();
  330.             } else if (currentMode === 'browse') {
  331.                 showWordList();
  332.             }
  333.         }
  334.  
  335.         async function addWord(word, definition) {
  336.             const newWord = {
  337.                 id: Date.now().toString(),
  338.                 word: word,
  339.                 definition: definition,
  340.                 level: 0,
  341.                 nextReview: new Date()
  342.             };
  343.            
  344.             try {
  345.                 await vocabStore.addWord(newWord);
  346.                 words.push(newWord);
  347.                 updateStats();
  348.             } catch (error) {
  349.                 console.error('Failed to add word:', error);
  350.             }
  351.         }
  352.  
  353.         function calculateNextReview(level) {
  354.             const now = new Date();
  355.             const hours = Math.pow(2, level) * 24;
  356.             return new Date(now.getTime() + hours * 60 * 60 * 1000);
  357.         }
  358.  
  359.         async function handleResponse(correct) {
  360.             const wordIndex = words.findIndex(w => w.id === currentWord.id);
  361.             if (wordIndex !== -1) {
  362.                 const newLevel = correct ? (words[wordIndex].level + 1) : 0;
  363.                 words[wordIndex].level = newLevel;
  364.                 words[wordIndex].nextReview = calculateNextReview(newLevel);
  365.                 try {
  366.                     await vocabStore.updateWord(words[wordIndex]);
  367.                 } catch (error) {
  368.                     console.error('Failed to update word:', error);
  369.                 }
  370.             }
  371.            
  372.             showNextWord();
  373.         }
  374.  
  375.         function toggleAnswer() {
  376.             const answer = document.getElementById('answer');
  377.             const showButton = document.getElementById('showAnswer');
  378.             const responseButtons = document.getElementById('responseButtons');
  379.            
  380.             answer.classList.toggle('hidden');
  381.             showButton.classList.toggle('hidden');
  382.             responseButtons.classList.toggle('hidden');
  383.         }
  384.  
  385.         function showNextWord() {
  386.             const now = new Date();
  387.             const dueWords = words.filter(word => new Date(word.nextReview) <= now);
  388.            
  389.             if (dueWords.length === 0) {
  390.                 currentWord = null;
  391.                 document.getElementById('currentWord').textContent = words.length === 0 ?
  392.                     'Enter a word above to begin practicing' :
  393.                     'No words due for review! Try browsing all words instead.';
  394.                 document.getElementById('answer').classList.add('hidden');
  395.                 document.getElementById('showAnswer').classList.add('hidden');
  396.                 document.getElementById('responseButtons').classList.add('hidden');
  397.                 return;
  398.             }
  399.            
  400.             currentWord = dueWords[Math.floor(Math.random() * dueWords.length)];
  401.            
  402.             document.getElementById('currentWord').textContent = currentWord.word;
  403.             document.getElementById('answer').textContent = currentWord.definition;
  404.             document.getElementById('answer').classList.add('hidden');
  405.             document.getElementById('showAnswer').classList.remove('hidden');
  406.             document.getElementById('responseButtons').classList.add('hidden');
  407.         }
  408.  
  409.         async function deleteWord(wordId) {
  410.             if (confirm('Are you sure you want to delete this word?')) {
  411.                 try {
  412.                     await vocabStore.deleteWord(wordId);
  413.                     words = words.filter(w => w.id !== wordId);
  414.                     updateStats();
  415.                     if (currentMode === 'browse') {
  416.                         showWordList();
  417.                     } else {
  418.                         showNextWord();
  419.                     }
  420.                 } catch (error) {
  421.                     console.error('Failed to delete word:', error);
  422.                 }
  423.             }
  424.         }
  425.  
  426.         async function loadFromStorage() {
  427.             try {
  428.                 await vocabStore.init();
  429.                 console.log("Init okay");
  430.  
  431.                 words = await vocabStore.getAllWords();
  432.                 updateStats();
  433.                 if (currentMode === 'practice') {
  434.                     showNextWord();
  435.                 } else {
  436.                     showWordList();
  437.                 }
  438.             } catch (error) {
  439.                 console.error('Failed to load words:', error);
  440.                 words = [];
  441.             }
  442.         }
  443.  
  444.         // Initialize
  445.         loadFromStorage();
  446.         toggleMode('practice');
  447.     </script>
  448. </body>
  449. </html>
  450.  
  451.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement