diff --git a/index.html b/index.html
index 7f3633e..1ea5648 100644
--- a/index.html
+++ b/index.html
@@ -168,8 +168,7 @@
diff --git a/src/constants.js b/src/constants.js
index d1a7a58..50de91b 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -56,6 +56,7 @@ export const DEFAULT_DICTIONARY = {
export const DEFAULT_SETTINGS = {
useIPAPronunciationField: true,
+ useHotkeys: true,
};
export const DEFAULT_PAGE_SIZE = 50;
diff --git a/src/js/displayToggles.js b/src/js/displayToggles.js
index 13fe8d4..bf1d893 100644
--- a/src/js/displayToggles.js
+++ b/src/js/displayToggles.js
@@ -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';
diff --git a/src/js/hotkeys.js b/src/js/hotkeys.js
new file mode 100644
index 0000000..703ba5d
--- /dev/null
+++ b/src/js/hotkeys.js
@@ -0,0 +1,126 @@
+import { confirmEditWord, submitWordForm } from "./wordManagement";
+import { showSection, getIsDetailsPanelDisplayed, hideDetailsPanel } from "./displayToggles";
+import { renderInfoModal, renderMaximizedTextbox } from "./render";
+import { showSearchModal, clearSearchText } from "./search";
+import { saveAndCloseSettingsModal, openSettingsModal } from "./settings";
+import { saveAndCloseEditModal, openEditModal } from "./dictionaryManagement";
+
+export function enableHotKeys() {
+ document.addEventListener('keydown', hotKeyActions);
+}
+
+export function disableHotKeys() {
+ document.removeEventListener('keydown', hotKeyActions);
+}
+
+export function hotKeyActions(event) {
+ console.log(event);
+
+ if (typeof event.key === 'undefined' || typeof event.ctrlKey === 'undefined' || typeof event.altKey === 'undefined') {
+ console.warn('Browser does not have required event properties for hotkeys.');
+ 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(); openEditModal()} break;
+ case 'h': if (event.ctrlKey) {event.preventDefault(); 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);
+ }
+ }
+}
diff --git a/src/js/render.js b/src/js/render.js
index 7c129d8..cb85906 100644
--- a/src/js/render.js
+++ b/src/js/render.js
@@ -277,7 +277,7 @@ export function renderEditForm(wordId = false) {
}
export function renderIPATable(ipaTableButton) {
- ipaTableButton = typeof ipaTableButton.target === 'undefined' ? ipaTableButton : ipaTableButton.target;
+ 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 => {
@@ -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');
diff --git a/src/js/search.js b/src/js/search.js
index 68c9b02..ea08930 100644
--- a/src/js/search.js
+++ b/src/js/search.js
@@ -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;
diff --git a/src/js/setupListeners.js b/src/js/setupListeners.js
index 8c17c1e..b6ebe12 100644
--- a/src/js/setupListeners.js
+++ b/src/js/setupListeners.js
@@ -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 { confirmEditWord, cancelEditWord, confirmDeleteWord, submitWordForm } from './wordManagement';
import { openEditModal, saveEditModal, saveAndCloseEditModal } 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);
@@ -96,15 +99,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 +141,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(),
- definition: removeTags(definition).trim(),
- details: removeTags(details).trim(),
- wordId: getNextId(),
- };
-
- if (validateWord(word)) {
- addWord(word);
- }
- });
+ addWordButton.addEventListener('click', submitWordForm);
setupIPAFields();
setupMaximizeButtons();
diff --git a/src/js/wordManagement.js b/src/js/wordManagement.js
index 700744c..d288199 100644
--- a/src/js/wordManagement.js
+++ b/src/js/wordManagement.js
@@ -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";
@@ -46,6 +46,27 @@ export function sortWords(render) {
}
}
+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);
+ }
+}
+
export function addWord(word, render = true) {
window.currentDictionary.words.push(word);
addMessage('Word Created Successfully');
@@ -75,8 +96,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,