Add import/export functions

This commit is contained in:
Robbie Antenesse 2019-05-10 15:39:00 -06:00
parent 3f17a19c50
commit 335b3dbb3e
6 changed files with 158 additions and 14 deletions

View File

@ -289,21 +289,24 @@
<div class="split two">
<div>
<p>
<a class="button">Import JSON</a><br>
<small>Import a previously-exported <code>JSON</code> file.</small>
<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>

View File

@ -21,6 +21,7 @@
},
"dependencies": {
"marked": "^0.6.2",
"normalize.css": "^8.0.1"
"normalize.css": "^8.0.1",
"papaparse": "^4.6.3"
}
}

View File

@ -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, '-');
}

View File

@ -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')) {

View File

@ -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);

View File

@ -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"