Add import/export functions
This commit is contained in:
parent
3f17a19c50
commit
335b3dbb3e
15
index.html
15
index.html
|
@ -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>
|
||||
|
|
|
@ -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…
Reference in New Issue