Add import/export functions
This commit is contained in:
		
							parent
							
								
									3f17a19c50
								
							
						
					
					
						commit
						335b3dbb3e
					
				
					 6 changed files with 158 additions and 14 deletions
				
			
		
							
								
								
									
										13
									
								
								index.html
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								index.html
									
										
									
									
									
								
							|  | @ -289,21 +289,24 @@ | |||
|         <div class="split two"> | ||||
|           <div> | ||||
|             <p> | ||||
|               <a class="button">Import JSON</a><br> | ||||
|               <label class="button">Import JSON <input type="file" id="importDictionaryFile" accept="application/json, .dict"><br> | ||||
|                 <small>Import a previously-exported <code>JSON</code> file.</small> | ||||
|               </label> | ||||
|             </p> | ||||
|             <p> | ||||
|               <a class="button">Import Words</a><br> | ||||
|               <small>Import a CSV file of words. (Download an <a>example file with the correct formatting</a>.)</small> | ||||
|               <label class="button">Import Words <input type="file" id="importWordsCSV" accept="text/csv, .csv"><br> | ||||
|                 <small>Import a CSV file of words.</small> | ||||
|               </label> | ||||
|               <a class="small button" download="Lexiconga_import-template.csv" href="data:text/csv;charset=utf-8,%22word%22,%22pronunciation%22,%22part of speech%22,%22definition%22,%22explanation%22%0A">Download an example file with the correct formatting</a> | ||||
|             </p> | ||||
|           </div> | ||||
|           <div> | ||||
|             <p> | ||||
|               <a class="button">Export JSON</a><br> | ||||
|               <a class="button" id="exportDictionaryButton">Export JSON</a><br> | ||||
|               <small>Export your work as a <code>JSON</code> file to re-import later.</small> | ||||
|             </p> | ||||
|             <p> | ||||
|               <a class="button">Export Words</a><br> | ||||
|               <a class="button" id="exportWordsButton">Export Words</a><br> | ||||
|               <small>Export a CSV file of your words.</small> | ||||
|             </p> | ||||
|           </div> | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
|   }, | ||||
|   "dependencies": { | ||||
|     "marked": "^0.6.2", | ||||
|     "normalize.css": "^8.0.1" | ||||
|     "normalize.css": "^8.0.1", | ||||
|     "papaparse": "^4.6.3" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -4,6 +4,24 @@ export function cloneObject(object) { | |||
|   return JSON.parse(JSON.stringify(object)); | ||||
| } | ||||
| 
 | ||||
| export function download(data, filename, type) { | ||||
|   var file = new Blob([data], { type }); | ||||
|   if (window.navigator.msSaveOrOpenBlob) // IE10+
 | ||||
|     window.navigator.msSaveOrOpenBlob(file, filename); | ||||
|   else { // Others
 | ||||
|     var a = document.createElement("a"), | ||||
|       url = URL.createObjectURL(file); | ||||
|     a.href = url; | ||||
|     a.download = filename; | ||||
|     document.body.appendChild(a); | ||||
|     a.click(); | ||||
|     setTimeout(function () { | ||||
|       document.body.removeChild(a); | ||||
|       window.URL.revokeObjectURL(url); | ||||
|     }, 0); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function getIndicesOf(searchStr, findIn, caseSensitive) { | ||||
|   // https://stackoverflow.com/a/3410557
 | ||||
|   const searchStrLen = searchStr.length; | ||||
|  | @ -52,5 +70,5 @@ export function removeTags(html) { | |||
| } | ||||
| 
 | ||||
| export function slugify(string) { | ||||
|   return removeDiacritics(string).replace(/[!a-zA-Z0-9-_]/g, '-'); | ||||
|   return removeDiacritics(string).replace(/[^a-zA-Z0-9-_]/g, '-'); | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| import { renderDictionaryDetails, renderPartsOfSpeech } from "./render"; | ||||
| import { removeTags, cloneObject, getTimestampInSeconds } from "../helpers"; | ||||
| import { renderDictionaryDetails, renderPartsOfSpeech, renderAll } from "./render"; | ||||
| import { removeTags, cloneObject, getTimestampInSeconds, download, slugify } from "../helpers"; | ||||
| import { LOCAL_STORAGE_KEY, DEFAULT_DICTIONARY, MIGRATE_VERSION } from "../constants"; | ||||
| import { addMessage } from "./utilities"; | ||||
| import { addMessage, getNextId } from "./utilities"; | ||||
| import { addWord } from "./wordManagement"; | ||||
| 
 | ||||
| export function updateDictionary () { | ||||
| 
 | ||||
|  | @ -93,6 +94,118 @@ export function clearDictionary() { | |||
|   window.currentDictionary = cloneObject(DEFAULT_DICTIONARY); | ||||
| } | ||||
| 
 | ||||
| export function importDictionary() { | ||||
|   const importDictionaryField = document.getElementById('importDictionaryFile'); | ||||
|    | ||||
|   if (importDictionaryField.files.length === 1) { | ||||
|     if (confirm('Importing a dicitonary file will overwrite and replace your current dictionary!\nDo you want to continue?')) { | ||||
|       addMessage('Importing Dictionary...'); | ||||
|       const fileReader = new FileReader(); | ||||
|       fileReader.onload = function (fileLoadedEvent) { | ||||
|         const textFromFileLoaded = fileLoadedEvent.target.result; | ||||
|         const importedDictionary = JSON.parse(textFromFileLoaded); | ||||
|         if (importedDictionary && importedDictionary.hasOwnProperty('words')) { | ||||
|           window.currentDictionary = importedDictionary; | ||||
|           saveDictionary(); | ||||
|           renderAll(); | ||||
|           importDictionaryField.value = ''; | ||||
|           document.getElementById('editModal').style.display = 'none'; | ||||
|           addMessage('Dictionary Imported Successfully'); | ||||
|         } else { | ||||
|           addMessage('Dictionary could not be imported', 10000); | ||||
|         } | ||||
|       }; | ||||
| 
 | ||||
|       fileReader.readAsText(importDictionaryField.files[0], "UTF-8"); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function importWords() { | ||||
|   const importWordsField = document.getElementById('importWordsCSV'); | ||||
|    | ||||
|   if (importWordsField.files.length === 1) { | ||||
|     if (confirm('Importing a CSV file with words will add all of the words in the file to your dictionary regardless of duplication!\nDo you want to continue?')) { | ||||
|       addMessage('Importing words...'); | ||||
|       import('papaparse').then(papa => { | ||||
|         let wordsImported = 0; | ||||
|         papa.parse(importWordsField.files[0], { | ||||
|           header: true, | ||||
|           encoding: "utf-8", | ||||
|           step: results => { | ||||
|             if (results.errors.length > 0) { | ||||
|               results.errors.forEach(err => { | ||||
|                 addMessage('Error Importing Word: ' + err); | ||||
|                 console.error('Error Importing Word: ', err) | ||||
|               }); | ||||
|             } else { | ||||
|               const row = results.data[0]; | ||||
|               addWord({ | ||||
|                 name: removeTags(row.word).trim(), | ||||
|                 pronunciation: removeTags(row.pronunciation).trim(), | ||||
|                 partOfSpeech: removeTags(row['part of speech']).trim(), | ||||
|                 definition: removeTags(row.definition).trim(), | ||||
|                 details: removeTags(row.explanation).trim(), | ||||
|                 wordId: getNextId(), | ||||
|               }, false, false); | ||||
|               wordsImported++; | ||||
|             } | ||||
|           }, | ||||
|           complete: () => { | ||||
|             saveDictionary(); | ||||
|             renderAll(); | ||||
|             importWordsField.value = ''; | ||||
|             document.getElementById('editModal').style.display = 'none'; | ||||
|             addMessage(`Done Importing ${wordsImported} Words`); | ||||
|           }, | ||||
|           error: err => { | ||||
|             addMessage('Error Importing Words: ' + err); | ||||
|             console.error('Error Importing Words: ', err); | ||||
|           }, | ||||
|           skipEmptyLines: true, | ||||
|         }); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function exportDictionary() { | ||||
|   addMessage('Exporting JSON...'); | ||||
| 
 | ||||
|   setTimeout(() => { | ||||
|     const file = JSON.stringify(window.currentDictionary), | ||||
|       { name, specification } = window.currentDictionary; | ||||
| 
 | ||||
|     const fileName = slugify(name + '_' + specification) + '.json'; | ||||
| 
 | ||||
|     download(file, fileName, 'application/json;charset=utf-8'); | ||||
|   }, 1); | ||||
| } | ||||
| 
 | ||||
| export function exportWords() { | ||||
|   addMessage('Exporting Words...'); | ||||
| 
 | ||||
|   setTimeout(() => { | ||||
|     import('papaparse').then(papa => { | ||||
|       const { name, specification } = window.currentDictionary; | ||||
|        | ||||
|       const fileName = slugify(name + '_' + specification) + '_words.csv'; | ||||
|        | ||||
|       const words = window.currentDictionary.words.map(word => { | ||||
|         return { | ||||
|           word: word.name, | ||||
|           pronunciation: word.pronunciation, | ||||
|           'part of speech': word.partOfSpeech, | ||||
|           definition: word.definition, | ||||
|           explanation: word.details, | ||||
|         } | ||||
|       }); | ||||
|       const csv = papa.unparse(words, { quotes: true }); | ||||
|       download(csv, fileName, 'text/csv;charset=utf-8'); | ||||
|     }); | ||||
|   }, 1); | ||||
| } | ||||
| 
 | ||||
| export function migrateDictionary() { | ||||
|   let migrated = false; | ||||
|   if (!window.currentDictionary.hasOwnProperty('version')) { | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import {showSection, hideDetailsPanel} from './displayToggles'; | ||||
| import { renderWords, renderEditForm, renderMaximizedTextbox, renderInfoModal, renderIPATable } from './render'; | ||||
| import { confirmEditWord, cancelEditWord, confirmDeleteWord, submitWordForm } from './wordManagement'; | ||||
| import { openEditModal, saveEditModal, saveAndCloseEditModal } from './dictionaryManagement'; | ||||
| import { openEditModal, saveEditModal, saveAndCloseEditModal, exportDictionary, exportWords, importDictionary, importWords } from './dictionaryManagement'; | ||||
| import { goToNextPage, goToPreviousPage, goToPage } from './pagination'; | ||||
| import { insertAtCursor, getInputSelection, setSelectionRange } from './StackOverflow/inputCursorManagement'; | ||||
| import { usePhondueDigraphs } from './KeyboardFire/phondue/ipaField'; | ||||
|  | @ -82,6 +82,10 @@ function setupEditFormInteractions() { | |||
| function setupEditFormButtons() { | ||||
|   document.getElementById('editSave').addEventListener('click', () => saveEditModal()); | ||||
|   document.getElementById('editSaveAndClose').addEventListener('click', () => saveAndCloseEditModal()); | ||||
|   document.getElementById('importDictionaryFile').addEventListener('change', importDictionary); | ||||
|   document.getElementById('importWordsCSV').addEventListener('change', importWords); | ||||
|   document.getElementById('exportDictionaryButton').addEventListener('click', exportDictionary); | ||||
|   document.getElementById('exportWordsButton').addEventListener('click', exportWords); | ||||
| 
 | ||||
|   setupMaximizeButtons(); | ||||
| } | ||||
|  | @ -196,8 +200,8 @@ export function setupSettingsModal() { | |||
| } | ||||
| 
 | ||||
| export function setupWordEditFormButtons() { | ||||
|   const saveChangesButtons = document.getElementsByClassName('edit-save-changes'); | ||||
|   const cancelChangesButtons = document.getElementsByClassName('edit-cancel'); | ||||
|   const saveChangesButtons = document.getElementsByClassName('edit-save-changes'), | ||||
|     cancelChangesButtons = document.getElementsByClassName('edit-cancel'); | ||||
|   Array.from(saveChangesButtons).forEach(button => { | ||||
|     button.removeEventListener('click', confirmEditWord); | ||||
|     button.addEventListener('click', confirmEditWord); | ||||
|  |  | |||
|  | @ -3524,6 +3524,11 @@ pako@~1.0.5: | |||
|   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" | ||||
|   integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== | ||||
| 
 | ||||
| papaparse@^4.6.3: | ||||
|   version "4.6.3" | ||||
|   resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-4.6.3.tgz#742e5eaaa97fa6c7e1358d2934d8f18f44aee781" | ||||
|   integrity sha512-LRq7BrHC2kHPBYSD50aKuw/B/dGcg29omyJbKWY3KsYUZU69RKwaBHu13jGmCYBtOc4odsLCrFyk6imfyNubJQ== | ||||
| 
 | ||||
| parcel-bundler@^1.12.3: | ||||
|   version "1.12.3" | ||||
|   resolved "https://registry.yarnpkg.com/parcel-bundler/-/parcel-bundler-1.12.3.tgz#2bbf70bfa2d06097f071653285040bd125684d09" | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue