Compare commits
10 Commits
b8702a6716
...
a321ae1582
Author | SHA1 | Date |
---|---|---|
Robbie Antenesse | a321ae1582 | |
Robbie Antenesse | c644593994 | |
Robbie Antenesse | 335b3dbb3e | |
Robbie Antenesse | 3f17a19c50 | |
Robbie Antenesse | 048c964405 | |
Robbie Antenesse | 5d38aa4e17 | |
Robbie Antenesse | 15bba03e1d | |
Robbie Antenesse | ad3141ef6e | |
Robbie Antenesse | d682b0060e | |
Robbie Antenesse | ac8d88f082 |
69
index.html
69
index.html
|
@ -143,30 +143,37 @@
|
|||
<div class="modal-content">
|
||||
<a class="close-button" onclick="this.parentElement.parentElement.style.display='none';">×︎</a>
|
||||
<section>
|
||||
<form>
|
||||
<label>Use IPA Auto-Fill
|
||||
<input id="settingsUseIPA" type="checkbox" checked><br />
|
||||
<small>Check this to use character combinations to input International Phonetic Alphabet characters into
|
||||
Pronunciation fields.</small>
|
||||
</label>
|
||||
|
||||
<label>Theme
|
||||
<select disabled>
|
||||
<option selected value="default">Default</option>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="royal">Royal</option>
|
||||
</select>
|
||||
</label>
|
||||
<form class="split two">
|
||||
<div>
|
||||
<label>Use IPA Auto-Fill
|
||||
<input id="settingsUseIPA" type="checkbox" checked><br />
|
||||
<small>Check this to use character combinations to input International Phonetic Alphabet characters into
|
||||
Pronunciation fields.</small>
|
||||
</label>
|
||||
|
||||
<label>Use Hotkeys
|
||||
<input id="settingsUseHotkeys" type="checkbox" checked><br />
|
||||
<small>Check this to enable keyboard combinations to perform different helpful actions.</small>
|
||||
</label>
|
||||
|
||||
<label>Theme
|
||||
<select disabled>
|
||||
<option selected value="default">Default</option>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="royal">Royal</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div id="accountSettings"></div>
|
||||
</form>
|
||||
</section>
|
||||
<footer>
|
||||
<a class="button" id="settingsSave">Save</a>
|
||||
<a class="button" id="settingsSaveAndClose">Save & Close</a>
|
||||
<a class="red button" onclick="this.parentElement.parentElement.parentElement.style.display='none';">Close Without
|
||||
Saving</a>
|
||||
<a class="red button" onclick="this.parentElement.parentElement.parentElement.style.display='none';">Close Without Saving</a>
|
||||
</footer>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -203,21 +210,24 @@
|
|||
<div class="split three">
|
||||
<div>
|
||||
<label>Consonants<br>
|
||||
<input id="editConsonants" class="ipa-field"><br>
|
||||
<small>(Space separated list)</small><br>
|
||||
<input id="editConsonants" class="ipa-field" placeholder="p b m n t ..."><br>
|
||||
<a class="label-help-button ipa-field-help-button">Field Help</a>
|
||||
<a class="label-button ipa-table-button">IPA Chart</a>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Vowels<br>
|
||||
<input id="editVowels" class="ipa-field"><br>
|
||||
<small>(Space separated list)</small><br>
|
||||
<input id="editVowels" class="ipa-field" placeholder="æ u e ɪ ..."><br>
|
||||
<a class="label-help-button ipa-field-help-button">Field Help</a>
|
||||
<a class="label-button ipa-table-button">IPA Chart</a>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Polyphthongs / Blends<br>
|
||||
<input id="editBlends" class="ipa-field"><br>
|
||||
<small>(Space separated list)</small><br>
|
||||
<input id="editBlends" class="ipa-field" placeholder="ai ou ue ..."><br>
|
||||
<a class="label-help-button ipa-field-help-button">Field Help</a>
|
||||
<a class="label-button ipa-table-button">IPA Chart</a>
|
||||
</label>
|
||||
|
@ -282,21 +292,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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ export const DEFAULT_DICTIONARY = {
|
|||
name: '',
|
||||
pronunciation: '',
|
||||
partOfSpeech: '',
|
||||
simpleDefinition: '',
|
||||
longDefinition: '',
|
||||
definition: '',
|
||||
details: '',
|
||||
wordId: 0
|
||||
}, */
|
||||
],
|
||||
|
@ -56,6 +56,7 @@ export const DEFAULT_DICTIONARY = {
|
|||
|
||||
export const DEFAULT_SETTINGS = {
|
||||
useIPAPronunciationField: true,
|
||||
useHotkeys: true,
|
||||
};
|
||||
|
||||
export const DEFAULT_PAGE_SIZE = 50;
|
||||
|
|
|
@ -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 () {
|
||||
|
||||
|
@ -19,9 +20,9 @@ export function openEditModal() {
|
|||
document.getElementById('editDescription').value = description;
|
||||
document.getElementById('editPartsOfSpeech').value = partsOfSpeech.join(',');
|
||||
|
||||
document.getElementById('editConsonants').value = consonants.join(',');
|
||||
document.getElementById('editVowels').value = vowels.join(',');
|
||||
document.getElementById('editBlends').value = blends.join(',');
|
||||
document.getElementById('editConsonants').value = consonants.join(' ');
|
||||
document.getElementById('editVowels').value = vowels.join(' ');
|
||||
document.getElementById('editBlends').value = blends.join(' ');
|
||||
document.getElementById('editOnset').value = phonotactics.onset.join(',');
|
||||
document.getElementById('editNucleus').value = phonotactics.nucleus.join(',');
|
||||
document.getElementById('editCoda').value = phonotactics.coda.join(',');
|
||||
|
@ -46,9 +47,9 @@ export function saveEditModal() {
|
|||
window.currentDictionary.description = removeTags(document.getElementById('editDescription').value.trim());
|
||||
window.currentDictionary.partsOfSpeech = document.getElementById('editPartsOfSpeech').value.split(',').map(val => val.trim()).filter(val => val !== '');
|
||||
|
||||
window.currentDictionary.details.phonology.consonants = document.getElementById('editConsonants').value.split(',').map(val => val.trim()).filter(val => val !== '');
|
||||
window.currentDictionary.details.phonology.vowels = document.getElementById('editVowels').value.split(',').map(val => val.trim()).filter(val => val !== '');
|
||||
window.currentDictionary.details.phonology.blends = document.getElementById('editBlends').value.split(',').map(val => val.trim()).filter(val => val !== '');
|
||||
window.currentDictionary.details.phonology.consonants = document.getElementById('editConsonants').value.split(' ').map(val => val.trim()).filter(val => val !== '');
|
||||
window.currentDictionary.details.phonology.vowels = document.getElementById('editVowels').value.split(' ').map(val => val.trim()).filter(val => val !== '');
|
||||
window.currentDictionary.details.phonology.blends = document.getElementById('editBlends').value.split(' ').map(val => val.trim()).filter(val => val !== '');
|
||||
window.currentDictionary.details.phonology.phonotactics.onset = document.getElementById('editOnset').value.split(',').map(val => val.trim()).filter(val => val !== '');
|
||||
window.currentDictionary.details.phonology.phonotactics.nucleus = document.getElementById('editNucleus').value.split(',').map(val => val.trim()).filter(val => val !== '');
|
||||
window.currentDictionary.details.phonology.phonotactics.coda = document.getElementById('editCoda').value.split(',').map(val => val.trim()).filter(val => val !== '');
|
||||
|
@ -93,13 +94,128 @@ 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')) {
|
||||
const fixStupidOldNonsense = string => string.replace(/"/g, '"').replace(/'/g, "'").replace(/\/g, '\\').replace(/<br>/g, '\n');
|
||||
window.currentDictionary.description = fixStupidOldNonsense(window.currentDictionary.description);
|
||||
window.currentDictionary.words = window.currentDictionary.words.map(word => {
|
||||
word.longDefinition = fixStupidOldNonsense(word.longDefinition);
|
||||
word.definition = word.simpleDefinition;
|
||||
delete word.simpleDefinition;
|
||||
word.details = fixStupidOldNonsense(word.longDefinition);
|
||||
delete word.longDefinition;
|
||||
return word;
|
||||
});
|
||||
window.currentDictionary = Object.assign({}, DEFAULT_DICTIONARY, window.currentDictionary);
|
||||
|
|
|
@ -8,6 +8,14 @@ export function showSection(sectionName) {
|
|||
}
|
||||
}
|
||||
|
||||
export function hideDetailsPanel() {
|
||||
document.getElementById('detailsPanel').style.display = 'none';
|
||||
}
|
||||
|
||||
export function getIsDetailsPanelDisplayed() {
|
||||
return document.getElementById('detailsPanel').style.display !== 'none';
|
||||
}
|
||||
|
||||
function showDescription() {
|
||||
const detailsPanel = document.getElementById('detailsPanel');
|
||||
detailsPanel.style.display = 'block';
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
import { confirmEditWord, submitWordForm } from "./wordManagement";
|
||||
import { showSection, getIsDetailsPanelDisplayed, hideDetailsPanel } from "./displayToggles";
|
||||
import { renderInfoModal, renderMaximizedTextbox } from "./render";
|
||||
import { showSearchModal, clearSearchText } from "./search";
|
||||
import { saveAndCloseSettingsModal, openSettingsModal, saveSettings } from "./settings";
|
||||
import { saveAndCloseEditModal, openEditModal } from "./dictionaryManagement";
|
||||
import { addMessage } from "./utilities";
|
||||
|
||||
export function enableHotKeys() {
|
||||
document.addEventListener('keydown', hotKeyActions);
|
||||
}
|
||||
|
||||
export function disableHotKeys() {
|
||||
document.removeEventListener('keydown', hotKeyActions);
|
||||
}
|
||||
|
||||
export function hotKeyActions(event) {
|
||||
if (typeof event.key === 'undefined' || typeof event.ctrlKey === 'undefined' || typeof event.altKey === 'undefined') {
|
||||
addMessage('Hotkeys disabled');
|
||||
console.warn('Browser does not have required event properties for hotkeys.');
|
||||
window.settings.useHotkeys = false;
|
||||
saveSettings();
|
||||
disableHotKeys();
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case 'Escape': hideAllModals(); break;
|
||||
case 'Return':
|
||||
case 'Enter': {
|
||||
if (event.ctrlKey) {
|
||||
if (document.getElementById('settingsModal').style.display !== 'none') {
|
||||
saveAndCloseSettingsModal();
|
||||
} else if (document.getElementById('editModal').style.display !== 'none') {
|
||||
saveAndCloseEditModal();
|
||||
} else {
|
||||
submitWord();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
case 'd': if (event.ctrlKey) {event.preventDefault(); toggleDetailsDisplay();} break;
|
||||
case 'e': if (event.ctrlKey) {event.preventDefault(); hideAllModals(); openEditModal();} break;
|
||||
case 'h': if (event.ctrlKey) {event.preventDefault(); hideAllModals(); showHelpModal();} break;
|
||||
case 'm': if (event.ctrlKey) {event.preventDefault(); maximizeTextarea();} break;
|
||||
case 's': {
|
||||
if (event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
hideAllModals();
|
||||
if (event.shiftKey) { // This is a failsafe in case the 'S' case below doesn't work for certain browsers
|
||||
openSettingsModal();
|
||||
} else {
|
||||
showSearchModal();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'S': if (event.ctrlKey) {event.preventDefault(); hideAllModals(); openSettingsModal();} break;
|
||||
case 'x': if (event.ctrlKey) {event.preventDefault(); clearSearchText();} break;
|
||||
}
|
||||
}
|
||||
|
||||
function hideAllModals() {
|
||||
const permanentModals = ['#searchModal', '#settingsModal', '#editModal'];
|
||||
const hideModals = document.querySelectorAll(permanentModals.join(',')),
|
||||
removeModals = document.querySelectorAll('.modal:not(' + permanentModals.join('):not(') + ')');
|
||||
Array.from(hideModals).forEach(modal => modal.style.display = 'none');
|
||||
Array.from(removeModals).forEach(modal => modal.parentElement.removeChild(modal));
|
||||
}
|
||||
|
||||
function toggleDetailsDisplay() {
|
||||
const activeTab = document.querySelector('#detailsSection nav li.active');
|
||||
console.log(activeTab);
|
||||
Array.from(document.querySelectorAll('#detailsSection nav li')).forEach(li => li.classList.remove('active'));
|
||||
if (activeTab) {
|
||||
switch(activeTab.innerText.trim().toLowerCase()) {
|
||||
case 'description': {
|
||||
document.querySelector('#detailsSection nav li:nth-child(2)').classList.add('active');
|
||||
showSection('details');
|
||||
break;
|
||||
}
|
||||
case 'details': {
|
||||
document.querySelector('#detailsSection nav li:nth-child(3)').classList.add('active');
|
||||
showSection('stats');
|
||||
break;
|
||||
}
|
||||
case 'stats': {
|
||||
hideDetailsPanel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
document.querySelector('#detailsSection nav li:nth-child(1)').classList.add('active');
|
||||
showSection('description');
|
||||
}
|
||||
}
|
||||
|
||||
function submitWord() {
|
||||
const focused = document.activeElement;
|
||||
if (focused && focused.id) {
|
||||
const isSubmittableField = focused.id.includes('wordName') || focused.id.includes('wordDefinition') || focused.id.includes('wordDetails');
|
||||
if (isSubmittableField) {
|
||||
if (focused.parentElement.parentElement.classList.contains('edit-form')) {
|
||||
const wordId = parseInt(focused.parentElement.parentElement.id.replace('editForm_', ''));
|
||||
confirmEditWord(wordId);
|
||||
} else {
|
||||
submitWordForm();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showHelpModal() {
|
||||
import('../markdown/help.md').then(html => {
|
||||
renderInfoModal(html);
|
||||
});
|
||||
}
|
||||
|
||||
function maximizeTextarea() {
|
||||
const focused = document.activeElement;
|
||||
if (focused) {
|
||||
const maximizeButton = focused.parentElement.querySelector('.maximize-button');
|
||||
console.log(maximizeButton);
|
||||
console.log(maximizeButton.parentElement);
|
||||
if (maximizeButton) {
|
||||
renderMaximizedTextbox(maximizeButton);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -154,7 +154,7 @@ export function renderWords() {
|
|||
|
||||
// words.slice(pageStart, pageEnd).forEach(originalWord => {
|
||||
words.forEach(originalWord => {
|
||||
let detailsMarkdown = removeTags(originalWord.longDefinition);
|
||||
let detailsMarkdown = removeTags(originalWord.details);
|
||||
const references = detailsMarkdown.match(/\{\{.+?\}\}/g);
|
||||
if (references && Array.isArray(references)) {
|
||||
new Set(references).forEach(reference => {
|
||||
|
@ -170,8 +170,8 @@ export function renderWords() {
|
|||
name: removeTags(originalWord.name),
|
||||
pronunciation: removeTags(originalWord.pronunciation),
|
||||
partOfSpeech: removeTags(originalWord.partOfSpeech),
|
||||
simpleDefinition: removeTags(originalWord.simpleDefinition),
|
||||
longDefinition: detailsMarkdown,
|
||||
definition: removeTags(originalWord.definition),
|
||||
details: detailsMarkdown,
|
||||
wordId: originalWord.wordId,
|
||||
});
|
||||
wordsHTML += `<article class="entry" id="${word.wordId}">
|
||||
|
@ -186,9 +186,9 @@ export function renderWords() {
|
|||
</div>
|
||||
</header>
|
||||
<dl>
|
||||
<dt class="definition">${word.simpleDefinition}</dt>
|
||||
<dt class="definition">${word.definition}</dt>
|
||||
<dd class="details">
|
||||
${md(word.longDefinition)}
|
||||
${md(word.details)}
|
||||
</dd>
|
||||
</dl>
|
||||
</article>`;
|
||||
|
@ -261,10 +261,10 @@ export function renderEditForm(wordId = false) {
|
|||
</select>
|
||||
</label>
|
||||
<label>Definition<span class="red">*</span><br>
|
||||
<input id="wordDefinition_${wordId}" value="${word.simpleDefinition}" placeholder="Equivalent words">
|
||||
<input id="wordDefinition_${wordId}" value="${word.definition}" placeholder="Equivalent words">
|
||||
</label>
|
||||
<label>Details<span class="red">*</span><a class="label-button maximize-button">Maximize</a><br>
|
||||
<textarea id="wordDetails_${wordId}" placeholder="Markdown formatting allowed">${word.longDefinition}</textarea>
|
||||
<textarea id="wordDetails_${wordId}" placeholder="Markdown formatting allowed">${word.details}</textarea>
|
||||
</label>
|
||||
<div id="wordErrorMessage_${wordId}"></div>
|
||||
<a class="button edit-save-changes" id="editWordButton_${wordId}">Save Changes</a>
|
||||
|
@ -277,8 +277,8 @@ export function renderEditForm(wordId = false) {
|
|||
}
|
||||
|
||||
export function renderIPATable(ipaTableButton) {
|
||||
ipaTableButton = typeof ipaTableButton.target === 'undefined' ? ipaTableButton : ipaTableButton.target;
|
||||
// const label = ipaTableButton.parentElement.innerText.replace(/(\*|Maximize)/g, '').trim();
|
||||
ipaTableButton = typeof ipaTableButton.target === 'undefined' || ipaTableButton.target === '' ? ipaTableButton : ipaTableButton.target;
|
||||
const label = ipaTableButton.parentElement.innerText.replace(/(Field Help|IPA Chart)/g, '').trim();
|
||||
const textBox = ipaTableButton.parentElement.querySelector('input');
|
||||
import('./KeyboardFire/phondue/ipa-table.html').then(html => {
|
||||
const modalElement = document.createElement('section');
|
||||
|
@ -286,7 +286,7 @@ export function renderIPATable(ipaTableButton) {
|
|||
modalElement.innerHTML = `<div class="modal-background"></div>
|
||||
<div class="modal-content">
|
||||
<a class="close-button">×︎</a>
|
||||
<header><label>Pronunciation <input value="${textBox.value}" class="ipa-field"></label></header>
|
||||
<header><label>${label} <input value="${textBox.value}" class="ipa-field"></label></header>
|
||||
<section>
|
||||
${html}
|
||||
</section>
|
||||
|
@ -301,7 +301,8 @@ export function renderIPATable(ipaTableButton) {
|
|||
}
|
||||
|
||||
export function renderMaximizedTextbox(maximizeButton) {
|
||||
maximizeButton = typeof maximizeButton.target === 'undefined' ? maximizeButton : maximizeButton.target;
|
||||
maximizeButton = typeof maximizeButton.target === 'undefined' || maximizeButton.target === '' ? maximizeButton : maximizeButton.target;
|
||||
console.log(maximizeButton.parentElement);
|
||||
const label = maximizeButton.parentElement.innerText.replace(/(\*|Maximize)/g, '').trim();
|
||||
const textBox = maximizeButton.parentElement.querySelector('textarea');
|
||||
const modalElement = document.createElement('section');
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
import { cloneObject, getIndicesOf } from "../helpers";
|
||||
import removeDiacritics from "./StackOverflow/removeDiacritics";
|
||||
import { renderWords } from "./render";
|
||||
|
||||
export function showSearchModal() {
|
||||
document.getElementById('searchModal').style.display = 'block';
|
||||
document.getElementById('searchBox').focus();
|
||||
}
|
||||
|
||||
export function clearSearchText() {
|
||||
document.getElementById('searchBox').value = '';
|
||||
document.getElementById('openSearchModal').value = '';
|
||||
renderWords();
|
||||
}
|
||||
|
||||
export function getSearchTerm() {
|
||||
return document.getElementById('searchBox').value;
|
||||
|
@ -44,18 +56,18 @@ export function getMatchingSearchWords() {
|
|||
searchTerm = filters.caseSensitive ? searchTerm : searchTerm.toLowerCase();
|
||||
let name = filters.ignoreDiacritics ? removeDiacritics(word.name) : word.name;
|
||||
name = filters.caseSensitive ? name : name.toLowerCase();
|
||||
let simpleDefinition = filters.ignoreDiacritics ? removeDiacritics(word.simpleDefinition) : word.simpleDefinition;
|
||||
simpleDefinition = filters.caseSensitive ? simpleDefinition : simpleDefinition.toLowerCase();
|
||||
let longDefinition = filters.ignoreDiacritics ? removeDiacritics(word.longDefinition) : word.longDefinition;
|
||||
longDefinition = filters.caseSensitive ? longDefinition : longDefinition.toLowerCase();
|
||||
let definition = filters.ignoreDiacritics ? removeDiacritics(word.definition) : word.definition;
|
||||
definition = filters.caseSensitive ? definition : definition.toLowerCase();
|
||||
let details = filters.ignoreDiacritics ? removeDiacritics(word.details) : word.details;
|
||||
details = filters.caseSensitive ? details : details.toLowerCase();
|
||||
|
||||
const isInName = filters.name && (filters.exact
|
||||
? searchTerm == name
|
||||
: new RegExp(searchTerm, 'g').test(name));
|
||||
const isInDefinition = filters.definition && (filters.exact
|
||||
? searchTerm == simpleDefinition
|
||||
: new RegExp(searchTerm, 'g').test(simpleDefinition));
|
||||
const isInDetails = filters.details && new RegExp(searchTerm, 'g').test(longDefinition);
|
||||
? searchTerm == definition
|
||||
: new RegExp(searchTerm, 'g').test(definition));
|
||||
const isInDetails = filters.details && new RegExp(searchTerm, 'g').test(details);
|
||||
return searchTerm === '' || isInName || isInDefinition || isInDetails;
|
||||
});
|
||||
return matchingWords;
|
||||
|
@ -82,21 +94,21 @@ export function highlightSearchTerm(word) {
|
|||
});
|
||||
}
|
||||
if (filters.definition) {
|
||||
const simpleDefinitionMatches = getIndicesOf(searchTerm, removeDiacritics(markedUpWord.simpleDefinition), filters.caseSensitive);
|
||||
simpleDefinitionMatches.forEach((wordIndex, i) => {
|
||||
const definitionMatches = getIndicesOf(searchTerm, removeDiacritics(markedUpWord.definition), filters.caseSensitive);
|
||||
definitionMatches.forEach((wordIndex, i) => {
|
||||
wordIndex += '<mark></mark>'.length * i;
|
||||
markedUpWord.simpleDefinition = markedUpWord.simpleDefinition.substring(0, wordIndex)
|
||||
+ '<mark>' + markedUpWord.simpleDefinition.substr(wordIndex, searchTermLength) + '</mark>'
|
||||
+ markedUpWord.simpleDefinition.substr(wordIndex + searchTermLength);
|
||||
markedUpWord.definition = markedUpWord.definition.substring(0, wordIndex)
|
||||
+ '<mark>' + markedUpWord.definition.substr(wordIndex, searchTermLength) + '</mark>'
|
||||
+ markedUpWord.definition.substr(wordIndex + searchTermLength);
|
||||
});
|
||||
}
|
||||
if (filters.details) {
|
||||
const longDefinitionMatches = getIndicesOf(searchTerm, removeDiacritics(markedUpWord.longDefinition), filters.caseSensitive);
|
||||
longDefinitionMatches.forEach((wordIndex, i) => {
|
||||
const detailsMatches = getIndicesOf(searchTerm, removeDiacritics(markedUpWord.details), filters.caseSensitive);
|
||||
detailsMatches.forEach((wordIndex, i) => {
|
||||
wordIndex += '<mark></mark>'.length * i;
|
||||
markedUpWord.longDefinition = markedUpWord.longDefinition.substring(0, wordIndex)
|
||||
+ '<mark>' + markedUpWord.longDefinition.substr(wordIndex, searchTermLength) + '</mark>'
|
||||
+ markedUpWord.longDefinition.substr(wordIndex + searchTermLength);
|
||||
markedUpWord.details = markedUpWord.details.substring(0, wordIndex)
|
||||
+ '<mark>' + markedUpWord.details.substr(wordIndex, searchTermLength) + '</mark>'
|
||||
+ markedUpWord.details.substr(wordIndex + searchTermLength);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -105,10 +117,10 @@ export function highlightSearchTerm(word) {
|
|||
markedUpWord.name = markedUpWord.name.replace(new RegExp(`(${searchTerm})`, regexMethod), `<mark>$1</mark>`);
|
||||
}
|
||||
if (filters.definition) {
|
||||
markedUpWord.simpleDefinition = markedUpWord.simpleDefinition.replace(new RegExp(`(${searchTerm})`, regexMethod), `<mark>$1</mark>`);
|
||||
markedUpWord.definition = markedUpWord.definition.replace(new RegExp(`(${searchTerm})`, regexMethod), `<mark>$1</mark>`);
|
||||
}
|
||||
if (filters.details) {
|
||||
markedUpWord.longDefinition = markedUpWord.longDefinition.replace(new RegExp(`(${searchTerm})`, regexMethod), `<mark>$1</mark>`);
|
||||
markedUpWord.details = markedUpWord.details.replace(new RegExp(`(${searchTerm})`, regexMethod), `<mark>$1</mark>`);
|
||||
}
|
||||
}
|
||||
return markedUpWord;
|
||||
|
|
|
@ -3,6 +3,7 @@ import { cloneObject } from "../helpers";
|
|||
import { usePhondueDigraphs } from "./KeyboardFire/phondue/ipaField";
|
||||
import { renderWords } from "./render";
|
||||
import { addMessage } from "./utilities";
|
||||
import { enableHotKeys, disableHotKeys } from "./hotkeys";
|
||||
|
||||
export function loadSettings() {
|
||||
const storedSettings = window.localStorage.getItem(SETTINGS_KEY);
|
||||
|
@ -16,17 +17,20 @@ export function saveSettings() {
|
|||
}
|
||||
|
||||
export function openSettingsModal() {
|
||||
const { useIPAPronunciationField } = window.settings;
|
||||
const { useIPAPronunciationField, useHotkeys } = window.settings;
|
||||
|
||||
document.getElementById('settingsUseIPA').checked = useIPAPronunciationField;
|
||||
document.getElementById('settingsUseHotkeys').checked = useHotkeys;
|
||||
|
||||
document.getElementById('settingsModal').style.display = '';
|
||||
}
|
||||
|
||||
export function saveSettingsModal() {
|
||||
window.settings.useIPAPronunciationField = document.getElementById('settingsUseIPA').checked;
|
||||
window.settings.useHotkeys = document.getElementById('settingsUseHotkeys').checked;
|
||||
|
||||
saveSettings();
|
||||
toggleHotkeysEnabled();
|
||||
toggleIPAPronunciationFields();
|
||||
}
|
||||
|
||||
|
@ -35,6 +39,13 @@ export function saveAndCloseSettingsModal() {
|
|||
document.getElementById('settingsModal').style.display = 'none';
|
||||
}
|
||||
|
||||
export function toggleHotkeysEnabled() {
|
||||
disableHotKeys();
|
||||
if (window.settings.useHotkeys) {
|
||||
enableHotKeys();
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleIPAPronunciationFields() {
|
||||
const ipaButtons = document.querySelectorAll('.ipa-table-button, .ipa-field-help-button'),
|
||||
ipaFields = document.querySelectorAll('.ipa-field');
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import {showSection} from './displayToggles';
|
||||
import {showSection, hideDetailsPanel} from './displayToggles';
|
||||
import { renderWords, renderEditForm, renderMaximizedTextbox, renderInfoModal, renderIPATable } from './render';
|
||||
import { validateWord, addWord, confirmEditWord, cancelEditWord, confirmDeleteWord } from './wordManagement';
|
||||
import { removeTags } from '../helpers';
|
||||
import { getNextId } from './utilities';
|
||||
import { openEditModal, saveEditModal, saveAndCloseEditModal } from './dictionaryManagement';
|
||||
import { confirmEditWord, cancelEditWord, confirmDeleteWord, submitWordForm } from './wordManagement';
|
||||
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';
|
||||
import { openSettingsModal, saveSettingsModal, saveAndCloseSettingsModal } from './settings';
|
||||
import { enableHotKeys } from './hotkeys';
|
||||
import { showSearchModal, clearSearchText } from './search';
|
||||
|
||||
export default function setupListeners() {
|
||||
setupDetailsTabs();
|
||||
|
@ -16,6 +16,9 @@ export default function setupListeners() {
|
|||
setupWordForm();
|
||||
setupMobileWordFormButton();
|
||||
setupInfoButtons();
|
||||
if (window.settings.useHotkeys) {
|
||||
enableHotKeys();
|
||||
}
|
||||
}
|
||||
|
||||
function setupDetailsTabs() {
|
||||
|
@ -33,7 +36,7 @@ function setupDetailsTabs() {
|
|||
const isActive = tab.classList.contains('active');
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
if (isActive) {
|
||||
document.getElementById('detailsPanel').style.display = 'none';
|
||||
hideDetailsPanel();
|
||||
} else {
|
||||
tab.classList.add('active');
|
||||
showSection(section);
|
||||
|
@ -79,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();
|
||||
}
|
||||
|
@ -96,15 +103,8 @@ function setupSearchBar() {
|
|||
searchBox.addEventListener('input', event => {
|
||||
openSearchModal.value = event.target.value;
|
||||
});
|
||||
clearSearchButton.addEventListener('click', event => {
|
||||
searchBox.value = '';
|
||||
openSearchModal.value = '';
|
||||
renderWords();
|
||||
});
|
||||
openSearchModal.addEventListener('click', () => {
|
||||
document.getElementById('searchModal').style.display = 'block';
|
||||
searchBox.focus();
|
||||
});
|
||||
clearSearchButton.addEventListener('click', clearSearchText);
|
||||
openSearchModal.addEventListener('click', showSearchModal);
|
||||
|
||||
const toggleDetailsCheck = function() {
|
||||
if (searchExactWords.checked) {
|
||||
|
@ -145,26 +145,7 @@ function setupWordForm() {
|
|||
event.preventDefault();
|
||||
return false;
|
||||
});
|
||||
addWordButton.addEventListener('click', () => {
|
||||
const name = document.getElementById('wordName').value,
|
||||
pronunciation = document.getElementById('wordPronunciation').value,
|
||||
partOfSpeech = document.getElementById('wordPartOfSpeech').value,
|
||||
definition = document.getElementById('wordDefinition').value,
|
||||
details = document.getElementById('wordDetails').value;
|
||||
|
||||
const word = {
|
||||
name: removeTags(name).trim(),
|
||||
pronunciation: removeTags(pronunciation).trim(),
|
||||
partOfSpeech: removeTags(partOfSpeech).trim(),
|
||||
simpleDefinition: removeTags(definition).trim(),
|
||||
longDefinition: removeTags(details).trim(),
|
||||
wordId: getNextId(),
|
||||
};
|
||||
|
||||
if (validateWord(word)) {
|
||||
addWord(word);
|
||||
}
|
||||
});
|
||||
addWordButton.addEventListener('click', submitWordForm);
|
||||
|
||||
setupIPAFields();
|
||||
setupMaximizeButtons();
|
||||
|
@ -219,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);
|
||||
|
|
|
@ -123,8 +123,8 @@ export function generateRandomWords(numberOfWords) {
|
|||
name: word,
|
||||
pronunciation: '/' + word + '/',
|
||||
partOfSpeech: Math.random() > 0.5 ? 'Noun' : 'Verb',
|
||||
simpleDefinition: word,
|
||||
longDefinition: word + (index > 0 ? '\n\nRef: {{' + words[index - 1] + '}}' : ''),
|
||||
definition: word,
|
||||
details: word + (index > 0 ? '\n\nRef: {{' + words[index - 1] + '}}' : ''),
|
||||
wordId: getNextId(),
|
||||
}, false);
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { renderWords } from "./render";
|
||||
import { wordExists, addMessage } from "./utilities";
|
||||
import { wordExists, addMessage, getNextId } from "./utilities";
|
||||
import removeDiacritics from "./StackOverflow/removeDiacritics";
|
||||
import { removeTags } from "../helpers";
|
||||
import { saveDictionary } from "./dictionaryManagement";
|
||||
|
@ -12,7 +12,7 @@ export function validateWord(word, wordId = false) {
|
|||
if (word.name === '') {
|
||||
errorMessage += '<p class="bold red">Word field must not be blank.</p>';
|
||||
}
|
||||
if (word.simpleDefinition === '' && word.longDefinition === '') {
|
||||
if (word.definition === '' && word.details === '') {
|
||||
errorMessage += '<p class="bold red">You must enter Definition or Details.</p>';
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ export function validateWord(word, wordId = false) {
|
|||
|
||||
export function sortWords(render) {
|
||||
const { sortByDefinition } = window.currentDictionary.settings;
|
||||
const sortBy = sortByDefinition ? 'simpleDefinition' : 'name';
|
||||
const sortBy = sortByDefinition ? 'definition' : 'name';
|
||||
|
||||
window.currentDictionary.words.sort((wordA, wordB) => {
|
||||
if (removeDiacritics(wordA[sortBy]).toLowerCase() === removeDiacritics(wordB[sortBy]).toLowerCase()) return 0;
|
||||
|
@ -46,9 +46,43 @@ export function sortWords(render) {
|
|||
}
|
||||
}
|
||||
|
||||
export function addWord(word, render = true) {
|
||||
export function submitWordForm() {
|
||||
const name = document.getElementById('wordName').value,
|
||||
pronunciation = document.getElementById('wordPronunciation').value,
|
||||
partOfSpeech = document.getElementById('wordPartOfSpeech').value,
|
||||
definition = document.getElementById('wordDefinition').value,
|
||||
details = document.getElementById('wordDetails').value;
|
||||
|
||||
const word = {
|
||||
name: removeTags(name).trim(),
|
||||
pronunciation: removeTags(pronunciation).trim(),
|
||||
partOfSpeech: removeTags(partOfSpeech).trim(),
|
||||
definition: removeTags(definition).trim(),
|
||||
details: removeTags(details).trim(),
|
||||
wordId: getNextId(),
|
||||
};
|
||||
|
||||
if (validateWord(word)) {
|
||||
addWord(word);
|
||||
clearWordForm();
|
||||
}
|
||||
}
|
||||
|
||||
export function clearWordForm() {
|
||||
document.getElementById('wordName').value = '';
|
||||
document.getElementById('wordPronunciation').value = '';
|
||||
document.getElementById('wordPartOfSpeech').value = '';
|
||||
document.getElementById('wordDefinition').value = '';
|
||||
document.getElementById('wordDetails').value = '';
|
||||
|
||||
document.getElementById('wordName').focus();
|
||||
}
|
||||
|
||||
export function addWord(word, render = true, message = true) {
|
||||
window.currentDictionary.words.push(word);
|
||||
addMessage('Word Created Successfully');
|
||||
if (message) {
|
||||
addMessage(`<a href="#${word.wordId}">${word.name}</a> Created Successfully`, 10000);
|
||||
}
|
||||
sortWords(render);
|
||||
}
|
||||
|
||||
|
@ -75,8 +109,9 @@ export function updateWord(word, wordId) {
|
|||
}
|
||||
}
|
||||
|
||||
export function confirmEditWord() {
|
||||
const wordId = parseInt(this.id.replace('editWordButton_', ''));
|
||||
export function confirmEditWord(id) {
|
||||
const wordId = typeof id.target !== 'undefined' ? parseInt(this.id.replace('editWordButton_', '')) : id;
|
||||
console.log(wordId);
|
||||
const name = document.getElementById('wordName_' + wordId).value,
|
||||
pronunciation = document.getElementById('wordPronunciation_' + wordId).value,
|
||||
partOfSpeech = document.getElementById('wordPartOfSpeech_' + wordId).value,
|
||||
|
@ -87,8 +122,8 @@ export function confirmEditWord() {
|
|||
name: removeTags(name).trim(),
|
||||
pronunciation: removeTags(pronunciation).trim(),
|
||||
partOfSpeech: removeTags(partOfSpeech).trim(),
|
||||
simpleDefinition: removeTags(definition).trim(),
|
||||
longDefinition: removeTags(details).trim(),
|
||||
definition: removeTags(definition).trim(),
|
||||
details: removeTags(details).trim(),
|
||||
wordId,
|
||||
};
|
||||
|
||||
|
|
|
@ -82,6 +82,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
#settingsModal {
|
||||
.modal-content section {
|
||||
padding: ($general-padding * 2) $general-padding $general-padding;
|
||||
}
|
||||
}
|
||||
|
||||
#mobileWordFormShow {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -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