Start non-invasive work on public viewing
This commit is contained in:
parent
2d77fa667c
commit
d1b123317f
|
@ -8,7 +8,7 @@
|
|||
"license": "UNLICENCED",
|
||||
"scripts": {
|
||||
"start": "concurrently \"npm run watch-js\" \"npm run watch-php\"",
|
||||
"watch-js": "parcel watch index.html --public-url ./",
|
||||
"watch-js": "parcel watch index.html view.html --public-url ./",
|
||||
"watch-php": "cpx \"src/php/**/*\" dist -v -w",
|
||||
"bundle": "parcel build index.html && cpx src/php/**/* dist",
|
||||
"serve-frontend-only": "parcel index.html",
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
export function getDictionary() {
|
||||
const url = window.location.href.replace(/\#.*$/gi, '');
|
||||
console.log(url);
|
||||
let dict = url.substr(url.lastIndexOf('?'));
|
||||
console.log(dict);
|
||||
if (dict === url) {
|
||||
dict = dict.substr(dict.lastIndexOf('/'));
|
||||
console.log(dict);
|
||||
}
|
||||
dict = dict.replace(/[\?\/]/g, '');
|
||||
console.log(dict);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import '../../main.scss';
|
||||
import { getDictionary } from './dictionaryManagement';
|
||||
|
||||
// import setupListeners, { setupSearchFilters } from './js/setupListeners';
|
||||
// import { renderAll } from './js/render';
|
||||
// import { hasToken } from './js/utilities';
|
||||
// import { loadDictionary } from './js/dictionaryManagement';
|
||||
// import { loadSettings } from './js/settings';
|
||||
|
||||
function initialize() {
|
||||
getDictionary();
|
||||
// setupSearchFilters();
|
||||
}
|
||||
|
||||
window.onload = (function (oldLoad) {
|
||||
return function () {
|
||||
oldLoad && oldLoad();
|
||||
initialize();
|
||||
}
|
||||
})(window.onload);
|
|
@ -0,0 +1,199 @@
|
|||
import md from 'marked';
|
||||
import { removeTags, slugify } from '../../helpers';
|
||||
import { getWordsStats, wordExists } from '../utilities';
|
||||
import { getMatchingSearchWords, highlightSearchTerm, getSearchFilters, getSearchTerm } from '../search';
|
||||
import { showSection } from '../displayToggles';
|
||||
import { setupSearchFilters, setupInfoModal } from './setupListeners';
|
||||
|
||||
export function renderAll() {
|
||||
renderDictionaryDetails();
|
||||
renderPartsOfSpeech();
|
||||
renderWords();
|
||||
}
|
||||
|
||||
export function renderDictionaryDetails() {
|
||||
renderName();
|
||||
|
||||
const tabs = document.querySelectorAll('#detailsSection nav li');
|
||||
const shownTab = Array.from(tabs).find(tab => tab.classList.contains('active'));
|
||||
if (shownTab) {
|
||||
const tabName = shownTab.innerText.toLowerCase();
|
||||
showSection(tabName);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderName() {
|
||||
const dictionaryName = removeTags(window.currentDictionary.name) + ' ' + removeTags(window.currentDictionary.specification);
|
||||
document.getElementById('dictionaryName').innerHTML = dictionaryName;
|
||||
}
|
||||
|
||||
export function renderDescription() {
|
||||
const descriptionHTML = md(removeTags(window.currentDictionary.description));
|
||||
|
||||
document.getElementById('detailsPanel').innerHTML = '<div class="content">' + descriptionHTML + '</div>';
|
||||
}
|
||||
|
||||
export function renderDetails() {
|
||||
const { partsOfSpeech, alphabeticalOrder } = window.currentDictionary;
|
||||
const { phonology, orthography, grammar } = window.currentDictionary.details;
|
||||
const partsOfSpeechHTML = `<p><strong>Parts of Speech:</strong> ${partsOfSpeech.map(partOfSpeech => '<span class="tag">' + partOfSpeech + '</span>').join(' ')}</p>`;
|
||||
const alphabeticalOrderHTML = `<p><strong>Alphabetical Order:</strong> ${
|
||||
(alphabeticalOrder.length > 0 ? alphabeticalOrder : ['English Alphabet']).map(letter => `<span class="tag">${letter}</span>`).join(' ')
|
||||
}</p>`;
|
||||
const generalHTML = `<h3>General</h3>${partsOfSpeechHTML}${alphabeticalOrderHTML}`;
|
||||
|
||||
const { consonants, vowels, blends, phonotactics } = phonology
|
||||
const consonantHTML = `<p><strong>Consonants:</strong> ${consonants.map(letter => `<span class="tag">${letter}</span>`).join(' ')}</p>`;
|
||||
const vowelHTML = `<p><strong>Vowels:</strong> ${vowels.map(letter => `<span class="tag">${letter}</span>`).join(' ')}</p>`;
|
||||
const blendHTML = blends.length > 0 ? `<p><strong>Polyphthongs / Blends:</strong> ${blends.map(letter => `<span class="tag">${letter}</span>`).join(' ')}</p>` : '';
|
||||
const phonologyHTML = `<h3>Phonology</h3>
|
||||
<div class="split two">
|
||||
<div>${consonantHTML}</div>
|
||||
<div>${vowelHTML}</div>
|
||||
</div>
|
||||
${blendHTML}`;
|
||||
|
||||
const { onset, nucleus, coda, exceptions } = phonotactics;
|
||||
const onsetHTML = `<p><strong>Onset:</strong> ${onset.map(letter => `<span class="tag">${letter}</span>`).join(' ')}</p>`;
|
||||
const nucleusHTML = `<p><strong>Nucleus:</strong> ${nucleus.map(letter => `<span class="tag">${letter}</span>`).join(' ')}</p>`;
|
||||
const codaHTML = `<p><strong>Coda:</strong> ${coda.map(letter => `<span class="tag">${letter}</span>`).join(' ')}</p>`;
|
||||
const exceptionsHTML = exceptions.trim().length > 0 ? '<p><strong>Exceptions:</strong></p><div>' + md(removeTags(exceptions)) + '</div>' : '';
|
||||
const phonotacticsHTML = `<h3>Phonotactics</h3>
|
||||
<div class="split three">
|
||||
<div>${onsetHTML}</div>
|
||||
<div>${nucleusHTML}</div>
|
||||
<div>${codaHTML}</div>
|
||||
</div>
|
||||
${exceptionsHTML}`;
|
||||
|
||||
const orthographyHTML = '<h3>Orthography</h3><p><strong>Notes:</strong></p><div>' + md(removeTags(orthography.notes)) + '</div>';
|
||||
const grammarHTML = '<h3>Grammar</h3><p><strong>Notes:</strong></p><div>' + md(removeTags(grammar.notes)) + '</div>';
|
||||
|
||||
detailsPanel.innerHTML = generalHTML + phonologyHTML + phonotacticsHTML + orthographyHTML + grammarHTML;
|
||||
}
|
||||
|
||||
export function renderStats() {
|
||||
const wordStats = getWordsStats();
|
||||
const numberOfWordsHTML = `<p><strong>Number of Words</strong><br>${wordStats.numberOfWords.map(stat => `<span><span class="tag">${stat.name}</span><span class="tag">${stat.value}</span></span>`).join(' ')}</p>`;
|
||||
const wordLengthHTML = `<p><strong>Word Length</strong><br><span><span class="tag">Shortest</span><span class="tag">${wordStats.wordLength.shortest}</span></span>
|
||||
<span><span class="tag">Longest</span><span class="tag">${wordStats.wordLength.longest}</span></span>
|
||||
<span><span class="tag">Average</span><span class="tag">${wordStats.wordLength.average}</span></span></p>`;
|
||||
const letterDistributionHTML = `<p><strong>Letter Distribution</strong><br>${wordStats.letterDistribution.map(stat => `<span title="${stat.number} ${stat.letter}'s total"><span class="tag">${stat.letter}</span><span class="tag">${stat.percentage.toFixed(2)}</span></span>`).join(' ')}</p>`;
|
||||
const totalLettersHTML = `<p><strong>${wordStats.totalLetters} Total Letters</strong></p>`;
|
||||
|
||||
detailsPanel.innerHTML = numberOfWordsHTML + wordLengthHTML + letterDistributionHTML + totalLettersHTML;
|
||||
}
|
||||
|
||||
export function renderPartsOfSpeech(onlyOptions = false) {
|
||||
let optionsHTML = '<option value=""></option>',
|
||||
searchHTML = '<label>Unclassified <input type="checkbox" checked id="searchPartOfSpeech__None"></label>';
|
||||
window.currentDictionary.partsOfSpeech.forEach(partOfSpeech => {
|
||||
partOfSpeech = removeTags(partOfSpeech);
|
||||
optionsHTML += `<option value="${partOfSpeech}">${partOfSpeech}</option>`;
|
||||
searchHTML += `<label>${partOfSpeech} <input type="checkbox" checked id="searchPartOfSpeech_${slugify(partOfSpeech)}"></label>`;
|
||||
});
|
||||
searchHTML += `<a class="small button" id="checkAllFilters">Check All</a> <a class="small button" id="uncheckAllFilters">Uncheck All</a>`;
|
||||
|
||||
Array.from(document.getElementsByClassName('part-of-speech-select')).forEach(select => {
|
||||
const selectedValue = select.value;
|
||||
select.innerHTML = optionsHTML;
|
||||
select.value = selectedValue;
|
||||
});
|
||||
if (!onlyOptions) {
|
||||
document.getElementById('searchPartsOfSpeech').innerHTML = searchHTML;
|
||||
}
|
||||
|
||||
setupSearchFilters();
|
||||
}
|
||||
|
||||
export function renderWords() {
|
||||
let wordsHTML = '';
|
||||
let words = false;
|
||||
|
||||
if (window.currentDictionary.words.length === 0) {
|
||||
wordsHTML = `<article class="entry">
|
||||
<header>
|
||||
<h4 class="word">No Words Found</h4>
|
||||
</header>
|
||||
<dl>
|
||||
<dt class="definition">Either this dictionary has not yet been started, or something prevented words from downloading.</dt>
|
||||
</dl>
|
||||
</article>`;
|
||||
} else {
|
||||
words = getMatchingSearchWords();
|
||||
|
||||
if (words.length === 0) {
|
||||
wordsHTML = `<article class="entry">
|
||||
<header>
|
||||
<h4 class="word">No Search Results</h4>
|
||||
</header>
|
||||
<dl>
|
||||
<dt class="definition">Edit your search or filter to show words.</dt>
|
||||
</dl>
|
||||
</article>`;
|
||||
}
|
||||
|
||||
words.forEach(originalWord => {
|
||||
let detailsMarkdown = removeTags(originalWord.details);
|
||||
const references = detailsMarkdown.match(/\{\{.+?\}\}/g);
|
||||
if (references && Array.isArray(references)) {
|
||||
new Set(references).forEach(reference => {
|
||||
const wordToFind = reference.replace(/\{\{|\}\}/g, '');
|
||||
const existingWordId = wordExists(wordToFind, true);
|
||||
if (existingWordId !== false) {
|
||||
const wordMarkdownLink = `[${wordToFind}](#${existingWordId})`;
|
||||
detailsMarkdown = detailsMarkdown.replace(new RegExp(reference, 'g'), wordMarkdownLink);
|
||||
}
|
||||
});
|
||||
}
|
||||
const word = highlightSearchTerm({
|
||||
name: removeTags(originalWord.name),
|
||||
pronunciation: removeTags(originalWord.pronunciation),
|
||||
partOfSpeech: removeTags(originalWord.partOfSpeech),
|
||||
definition: removeTags(originalWord.definition),
|
||||
details: detailsMarkdown,
|
||||
wordId: originalWord.wordId,
|
||||
});
|
||||
wordsHTML += `<article class="entry" id="${word.wordId}">
|
||||
<header>
|
||||
<h4 class="word">${word.name}</h4>
|
||||
<span class="pronunciation">${word.pronunciation}</span>
|
||||
<span class="part-of-speech">${word.partOfSpeech}</span>
|
||||
</header>
|
||||
<dl>
|
||||
<dt class="definition">${word.definition}</dt>
|
||||
<dd class="details">
|
||||
${md(word.details)}
|
||||
</dd>
|
||||
</dl>
|
||||
</article>`;
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('entries').innerHTML = wordsHTML;
|
||||
|
||||
// Show Search Results
|
||||
const searchTerm = getSearchTerm();
|
||||
const filters = getSearchFilters();
|
||||
let resultsText = searchTerm !== '' || !filters.allPartsOfSpeechChecked ? (words ? words.length : 0).toString() + ' Results' : '';
|
||||
resultsText += !filters.allPartsOfSpeechChecked ? ' (Filtered)' : '';
|
||||
document.getElementById('searchResults').innerHTML = resultsText;
|
||||
}
|
||||
|
||||
export function renderInfoModal(content) {
|
||||
const modalElement = document.createElement('section');
|
||||
modalElement.classList.add('modal', 'info-modal');
|
||||
modalElement.innerHTML = `<div class="modal-background"></div>
|
||||
<div class="modal-content">
|
||||
<a class="close-button">×︎</a>
|
||||
<section class="info-modal">
|
||||
<div class="content">
|
||||
${content}
|
||||
</div>
|
||||
</section>
|
||||
</div>`;
|
||||
|
||||
document.body.appendChild(modalElement);
|
||||
|
||||
setupInfoModal(modalElement);
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
import {showSection, hideDetailsPanel} from '../displayToggles';
|
||||
import { showSearchModal, clearSearchText, checkAllPartsOfSpeechFilters, uncheckAllPartsOfSpeechFilters } from '../search';
|
||||
import { renderWords, renderInfoModal } from './render';
|
||||
|
||||
export default function setupListeners() {
|
||||
setupDetailsTabs();
|
||||
setupSearchBar();
|
||||
setupInfoButtons();
|
||||
}
|
||||
|
||||
function setupDetailsTabs() {
|
||||
const tabs = document.querySelectorAll('#detailsSection nav li');
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const section = tab.innerText.toLowerCase();
|
||||
const isActive = tab.classList.contains('active');
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
if (isActive) {
|
||||
hideDetailsPanel();
|
||||
} else {
|
||||
tab.classList.add('active');
|
||||
showSection(section);
|
||||
}
|
||||
});
|
||||
});
|
||||
setupEditFormTabs();
|
||||
setupEditFormInteractions();
|
||||
setupEditFormButtons();
|
||||
}
|
||||
|
||||
function setupSearchBar() {
|
||||
const searchBox = document.getElementById('searchBox'),
|
||||
clearSearchButton = document.getElementById('clearSearchButton'),
|
||||
openSearchModal = document.getElementById('openSearchModal'),
|
||||
searchIgnoreDiacritics = document.getElementById('searchIgnoreDiacritics'),
|
||||
searchExactWords = document.getElementById('searchExactWords'),
|
||||
searchIncludeDetails = document.getElementById('searchIncludeDetails');
|
||||
searchBox.addEventListener('change', () => {
|
||||
renderWords();
|
||||
});
|
||||
searchBox.addEventListener('input', event => {
|
||||
openSearchModal.value = event.target.value;
|
||||
});
|
||||
clearSearchButton.addEventListener('click', clearSearchText);
|
||||
openSearchModal.addEventListener('click', showSearchModal);
|
||||
|
||||
const toggleDetailsCheck = function() {
|
||||
if (searchExactWords.checked) {
|
||||
searchIncludeDetails.checked = false;
|
||||
searchIncludeDetails.disabled = true;
|
||||
} else {
|
||||
searchIncludeDetails.disabled = false;
|
||||
searchIncludeDetails.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
searchIgnoreDiacritics.addEventListener('change', () => {
|
||||
if (searchIgnoreDiacritics.checked) {
|
||||
searchExactWords.checked = false;
|
||||
searchExactWords.disabled = true;
|
||||
} else {
|
||||
searchExactWords.disabled = false;
|
||||
}
|
||||
toggleDetailsCheck();
|
||||
});
|
||||
|
||||
searchExactWords.addEventListener('change', () => toggleDetailsCheck());
|
||||
}
|
||||
|
||||
export function setupSearchFilters() {
|
||||
const searchFilters = document.querySelectorAll('#searchOptions input[type="checkbox"]'),
|
||||
searchBox = document.getElementById('searchBox');
|
||||
Array.from(searchFilters).concat([searchBox]).forEach(filter => {
|
||||
filter.removeEventListener('change', renderWords);
|
||||
filter.addEventListener('change', renderWords);
|
||||
});
|
||||
document.getElementById('checkAllFilters').removeEventListener('click', checkAllPartsOfSpeechFilters);
|
||||
document.getElementById('checkAllFilters').addEventListener('click', checkAllPartsOfSpeechFilters);
|
||||
document.getElementById('uncheckAllFilters').removeEventListener('click', uncheckAllPartsOfSpeechFilters);
|
||||
document.getElementById('uncheckAllFilters').addEventListener('click', uncheckAllPartsOfSpeechFilters);
|
||||
}
|
||||
|
||||
export function setupInfoButtons() {
|
||||
document.getElementById('helpInfoButton').addEventListener('click', () => {
|
||||
import('../markdown/help.md').then(html => {
|
||||
renderInfoModal(html);
|
||||
});
|
||||
});
|
||||
document.getElementById('termsInfoButton').addEventListener('click', () => {
|
||||
import('../markdown/terms.md').then(html => {
|
||||
renderInfoModal(html);
|
||||
});
|
||||
});
|
||||
document.getElementById('privacyInfoButton').addEventListener('click', () => {
|
||||
import('../markdown/privacy.md').then(html => {
|
||||
renderInfoModal(html);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function setupInfoModal(modal) {
|
||||
const closeElements = modal.querySelectorAll('.modal-background, .close-button');
|
||||
Array.from(closeElements).forEach(close => {
|
||||
close.addEventListener('click', () => {
|
||||
modal.parentElement.removeChild(modal);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -88,6 +88,78 @@ VALUES ($new_id, ?, ?, ?, ?)";
|
|||
return array();
|
||||
}
|
||||
|
||||
public function getPublicDictionaryDetails ($dictionary_hash) {
|
||||
$dictionary = $this->token->unhash($dictionary_hash);
|
||||
if ($dictionary !== false) {
|
||||
$query = "SELECT * FROM dictionaries JOIN dictionary_linguistics ON dictionary = id WHERE id=? AND is_public=1";
|
||||
$result = $this->db->query($query, array($dictionary))->fetch();
|
||||
if ($result) {
|
||||
// Default json values in case they are somehow not created by front end first
|
||||
$partsOfSpeech = $result['parts_of_speech'] !== '' ? $result['parts_of_speech'] : $this->defaults['partsOfSpeech'];
|
||||
|
||||
return array(
|
||||
'externalID' => $this->token->hash($result['id']),
|
||||
'name' => $result['name'],
|
||||
'specification' => $result['specification'],
|
||||
'description' => $result['description'],
|
||||
'partsOfSpeech' => explode(',', $partsOfSpeech),
|
||||
'details' => array(
|
||||
'phonology' => array(
|
||||
'consonants' => $result['consonants'] !== '' ? explode(' ', $result['consonants']) : array(),
|
||||
'vowels' => $result['vowels'] !== '' ? explode(' ', $result['vowels']) : array(),
|
||||
'blends' => $result['blends'] !== '' ? explode(' ', $result['blends']) : array(),
|
||||
'phonotactics' => array(
|
||||
'onset' => $result['onset'] !== '' ? explode(',', $result['onset']) : array(),
|
||||
'nucleus' => $result['nucleus'] !== '' ? explode(',', $result['nucleus']) : array(),
|
||||
'coda' => $result['coda'] !== '' ? explode(',', $result['coda']) : array(),
|
||||
'exceptions' => $result['exceptions'],
|
||||
),
|
||||
),
|
||||
'orthography' => array(
|
||||
'notes' => $result['orthography_notes'],
|
||||
),
|
||||
'grammar' => array(
|
||||
'notes' => $result['grammar_notes'],
|
||||
),
|
||||
),
|
||||
'settings' => array(
|
||||
'allowDuplicates' => $result['allow_duplicates'] === '1' ? true : false,
|
||||
'caseSensitive' => $result['case_sensitive'] === '1' ? true : false,
|
||||
'sortByDefinition' => $result['sort_by_definition'] === '1' ? true : false,
|
||||
'isComplete' => false,
|
||||
'isPublic' => $result['is_public'] === '1' ? true : false,
|
||||
),
|
||||
'lastUpdated' => is_null($result['last_updated']) ? $result['created_on'] : $result['last_updated'],
|
||||
'createdOn' => $result['created_on'],
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPublicDictionaryWords ($dictionary_hash) {
|
||||
$dictionary = $this->token->unhash($dictionary_hash);
|
||||
if ($dictionary !== false) {
|
||||
$query = "SELECT words.* FROM words JOIN dictionaries ON id = dictionary WHERE dictionary=? AND is_public=1";
|
||||
$results = $this->db->query($query, array($dictionary))->fetchAll();
|
||||
if ($results) {
|
||||
return array_map(function ($row) {
|
||||
return array(
|
||||
'name' => $row['name'],
|
||||
'pronunciation' => $row['pronunciation'],
|
||||
'partOfSpeech' => $row['part_of_speech'],
|
||||
'definition' => $row['definition'],
|
||||
'details' => $row['details'],
|
||||
'lastUpdated' => is_null($row['last_updated']) ? intval($row['created_on']) : intval($row['last_updated']),
|
||||
'createdOn' => intval($row['created_on']),
|
||||
'wordId' => intval($row['word_id']),
|
||||
);
|
||||
}, $results);
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getDetails ($user, $dictionary) {
|
||||
$query = "SELECT * FROM dictionaries JOIN dictionary_linguistics ON dictionary = id WHERE user=$user AND id=$dictionary";
|
||||
$result = $this->db->query($query)->fetch();
|
||||
|
@ -211,7 +283,7 @@ WHERE dictionary=$dictionary";
|
|||
'partOfSpeech' => $row['part_of_speech'],
|
||||
'definition' => $row['definition'],
|
||||
'details' => $row['details'],
|
||||
'lastUpdated' => is_null($row['last_updated']) ? null : intval($row['last_updated']),
|
||||
'lastUpdated' => is_null($row['last_updated']) ? intval($row['created_on']) : intval($row['last_updated']),
|
||||
'createdOn' => intval($row['created_on']),
|
||||
'wordId' => intval($row['word_id']),
|
||||
);
|
||||
|
|
|
@ -218,6 +218,27 @@ switch ($action) {
|
|||
'error' => true,
|
||||
), 400);
|
||||
}
|
||||
case 'get-public-dictionary': {
|
||||
if (isset($request['dictionary'])) {
|
||||
$dictionary = new Dictionary();
|
||||
$dictionary_data = $dictionary->getPublicDictionaryDetails($request['dictionary']);
|
||||
if ($dictionary_data !== false) {
|
||||
$dictionary_data['words'] = $dictionary->getPublicDictionaryWords($request['dictionary']);
|
||||
return Response::json(array(
|
||||
'data' => $dictionary_data,
|
||||
'error' => false,
|
||||
), 200);
|
||||
}
|
||||
return Response::json(array(
|
||||
'data' => 'Could not get dictionary: invalid id',
|
||||
'error' => true,
|
||||
), 401);
|
||||
}
|
||||
return Response::json(array(
|
||||
'data' => 'Could not get dictionary: no id provided',
|
||||
'error' => true,
|
||||
), 400);
|
||||
}
|
||||
case 'set-whole-current-dictionary': {
|
||||
if ($token !== false && isset($request['dictionary'])) {
|
||||
$user = new User();
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Lexiconga</title>
|
||||
<script src="src/js/view/index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header id="top">
|
||||
<h1 id="title">Lexiconga</h1>
|
||||
|
||||
<input id="openSearchModal" placeholder="🔍︎ Search"> <span id="searchResults"></span>
|
||||
<section id="searchModal" class="modal" style="display:none;">
|
||||
<div class="modal-background" onclick="this.parentElement.style.display='none';"></div>
|
||||
<div class="modal-content">
|
||||
<a class="close-button" onclick="this.parentElement.parentElement.style.display='none';">×︎</a>
|
||||
<section>
|
||||
<label>Search Term
|
||||
<input id="searchBox" placeholder="Search term">
|
||||
</label>
|
||||
<a id="searchButton" class="small button">Search</a>
|
||||
<a id="clearSearchButton" class="small red button">Clear</a>
|
||||
<a class="small button" onclick="var options=document.getElementById('searchOptions').style;options.display=options.display=='block'?'none':'block';">
|
||||
Toggle Options
|
||||
</a>
|
||||
</section>
|
||||
<footer id="searchOptions" style="display:none;">
|
||||
<div class="split">
|
||||
<div class="quarter category">
|
||||
<h3>Search For</h3>
|
||||
</div>
|
||||
<div class="three-quarter options">
|
||||
<label>Case-Sensitive
|
||||
<input type="checkbox" id="searchCaseSensitive">
|
||||
</label>
|
||||
<label>Ignore Diacritics/Accents
|
||||
<input type="checkbox" id="searchIgnoreDiacritics">
|
||||
</label>
|
||||
<label>Exact Words
|
||||
<input type="checkbox" id="searchExactWords">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="split">
|
||||
<div class="quarter category">
|
||||
<h3>Include in Search</h3>
|
||||
</div>
|
||||
<div class="three-quarter options">
|
||||
<label>Word Name
|
||||
<input type="checkbox" checked id="searchIncludeName">
|
||||
</label>
|
||||
<label>Definition
|
||||
<input type="checkbox" checked id="searchIncludeDefinition">
|
||||
</label>
|
||||
<label>Details
|
||||
<input type="checkbox" checked id="searchIncludeDetails">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="split">
|
||||
<div class="quarter category">
|
||||
<h3>Include Only</h3>
|
||||
</div>
|
||||
<div class="three-quarter options" id="searchPartsOfSpeech"></div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- div id="headerMenu">
|
||||
<a id="settingsButton" class="button">Settings</a>
|
||||
<a id="loginCreateAccountButton" class="button">Log In / Create Account</a>
|
||||
</div -->
|
||||
<div style="clear:both;"></div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!--aside id="sideColumn">
|
||||
<div id="mobileWordFormShow">+</div>
|
||||
<form id="wordForm">
|
||||
<label>Word<span class="red">*</span><br>
|
||||
<input id="wordName" maxlength="200">
|
||||
</label>
|
||||
<label>Pronunciation<a class="label-button ipa-table-button">IPA Chart</a><br>
|
||||
<input id="wordPronunciation" class="ipa-field" maxlength="200"><br>
|
||||
<a class="label-help-button ipa-field-help-button">Field Help</a>
|
||||
</label>
|
||||
<label>Part of Speech<br>
|
||||
<select id="wordPartOfSpeech" class="part-of-speech-select"></select>
|
||||
</label>
|
||||
<label>Definition<span class="red">*</span><br>
|
||||
<input id="wordDefinition" maxlength="2500" placeholder="Equivalent words">
|
||||
</label>
|
||||
<label>Details<span class="red">*</span><a class="label-button maximize-button">Maximize</a><br>
|
||||
<textarea id="wordDetails" placeholder="Markdown formatting allowed"></textarea>
|
||||
</label>
|
||||
<div id="wordErrorMessage"></div>
|
||||
<a class="button" id="addWordButton">Add Word</a>
|
||||
</form>
|
||||
</aside -->
|
||||
|
||||
<section id="mainColumn">
|
||||
<section id="detailsSection">
|
||||
<h2 id="dictionaryName">Dictionary Name</h2>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>Description</li><li>Details</li><li>Stats</li><!-- li id="editDictionaryButton">Edit</li -->
|
||||
</ul>
|
||||
</nav>
|
||||
<article id="detailsPanel" style="display:none;">
|
||||
<p>The dictionary details</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="pagination"></section>
|
||||
|
||||
<section id="entries">
|
||||
<article class="entry">
|
||||
<header>
|
||||
<h4 class="word">Loading Words</h4>
|
||||
</header>
|
||||
<dl>
|
||||
<dt class="definition">Please Wait...</dt>
|
||||
</dl>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="pagination"></section>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer id="bottom">
|
||||
<a href="https://buymeacoff.ee/robbieantenesse" target="_blank" class="small button">Support Lexiconga</a>
|
||||
<a href="https://blog.lexicon.ga" target="_blank" class="small button">Blog</a>
|
||||
<a href="https://github.com/Alamantus/Lexiconga/issues" target="_blank" class="small button">Issues</a>
|
||||
<a href="https://github.com/Alamantus/Lexiconga/releases" target="_blank" class="small button">Updates</a>
|
||||
|
|
||||
<a class="button" id="helpInfoButton">Help</a>
|
||||
<a class="button" id="termsInfoButton">Terms</a>
|
||||
<a class="button" id="privacyInfoButton">Privacy</a>
|
||||
</footer>
|
||||
|
||||
<!-- section id="settingsModal" class="modal" style="display:none;">
|
||||
<div class="modal-background" onclick="this.parentElement.style.display='none';"></div>
|
||||
<div class="modal-content">
|
||||
<a class="close-button" onclick="this.parentElement.parentElement.style.display='none';">×︎</a>
|
||||
<section>
|
||||
<form class="split two">
|
||||
<div>
|
||||
<h3>General Settings</h3>
|
||||
<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 id="accountSettings"></div>
|
||||
</div>
|
||||
<div id="accountActions"></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>
|
||||
</footer>
|
||||
</div>
|
||||
</section -->
|
||||
|
||||
<!-- section id="editModal" class="modal" style="display:none;">
|
||||
<div class="modal-background" onclick="this.parentElement.style.display='none';"></div>
|
||||
<div class="modal-content">
|
||||
<a class="close-button" onclick="this.parentElement.parentElement.style.display='none';">×︎</a>
|
||||
<nav class="tabs">
|
||||
<ul>
|
||||
<li class="active">Description</li><li>Details</li><li>Settings</li><li>Actions</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<section id="editDescriptionTab">
|
||||
<label>Name<br>
|
||||
<input id="editName" maxlength="50">
|
||||
</label>
|
||||
<label>Specification<br>
|
||||
<input id="editSpecification" maxlength="50">
|
||||
</label>
|
||||
<label>Description<a class="label-button maximize-button">Maximize</a><br>
|
||||
<textarea id="editDescription"></textarea>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section id="editDetailsTab" style="display:none;">
|
||||
<label>Parts of Speech <small>(Comma Separated List)</small><br>
|
||||
<input id="editPartsOfSpeech" maxlength="2500" placeholder="Noun,Adjective,Verb">
|
||||
</label>
|
||||
<label>Alphabetical Order <small>(Comma Separated List. Include every letter!)</small><br>
|
||||
<input id="editAlphabeticalOrder" disabled value="English Alphabet">
|
||||
</label>
|
||||
<h3>Phonology</h3>
|
||||
<div class="split three">
|
||||
<div>
|
||||
<label>Consonants<br>
|
||||
<small>(Space separated list)</small><br>
|
||||
<input id="editConsonants" class="ipa-field" maxlength="100" 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>
|
||||
<small>(Space separated list)</small><br>
|
||||
<input id="editVowels" class="ipa-field" maxlength="100" 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>
|
||||
<small>(Space separated list)</small><br>
|
||||
<input id="editBlends" class="ipa-field" maxlength="100" 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>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Phonotactics</h3>
|
||||
<div class="split three">
|
||||
<div>
|
||||
<label>Onset<br>
|
||||
<small>(Comma separated list)</small><br>
|
||||
<input id="editOnset" maxlength="100" placeholder="Consonants,Vowels">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Nucleus<br>
|
||||
<small>(Comma separated list)</small><br>
|
||||
<input id="editNucleus" maxlength="100" placeholder="Vowels,Blends">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Coda<br>
|
||||
<small>(Comma separated list)</small><br>
|
||||
<input id="editCoda" maxlength="100" placeholder="Any">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label>Exceptions <small>(Markdown-enabled)</small><br>
|
||||
<textarea id="editExceptions"></textarea>
|
||||
</label>
|
||||
<h3>Orthography</h3>
|
||||
<label>Notes <small>(Markdown-enabled)</small><a class="label-button maximize-button">Maximize</a><br>
|
||||
<textarea id="editOrthography"></textarea>
|
||||
</label>
|
||||
<h3>Grammar</h3>
|
||||
<label>Notes <small>(Markdown-enabled)</small><a class="label-button maximize-button">Maximize</a><br>
|
||||
<textarea id="editGrammar"></textarea>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section id="editSettingsTab" style="display:none;">
|
||||
<label>Prevent Duplicate Words
|
||||
<input type="checkbox" id="editPreventDuplicates"><br>
|
||||
<small>Checking this box will prevent the creation of words with the exact same spelling.</small>
|
||||
</label>
|
||||
<label>Words are Case-Sensitive
|
||||
<input type="checkbox" id="editCaseSensitive"><br>
|
||||
<small>Checking this box will allow the creation of words with the exact same spelling if their capitalization is different.</small>
|
||||
</label>
|
||||
<label>Sort by Definition
|
||||
<input type="checkbox" id="editSortByDefinition"><br>
|
||||
<small>Checking this box will sort the words in alphabetical order based on the Definition instead of the Word.</small>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section id="editActionsTab" style="display:none;">
|
||||
<h3>Import / Export</h3>
|
||||
<div class="split two">
|
||||
<div>
|
||||
<p>
|
||||
<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>
|
||||
<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" 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" id="exportWordsButton">Export Words</a><br>
|
||||
<small>Export a CSV file of your words.</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<a class="red button" id="deleteDictionaryButton">Delete Dictionary</a><br>
|
||||
<small>This will permanently delete your current dictionary, and it will not be possible to return it if you have not backed it up!</small>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<a class="button" id="editSave">Save</a>
|
||||
<a class="button" id="editSaveAndClose">Save & Close</a>
|
||||
<a class="red button" onclick="this.parentElement.parentElement.parentElement.style.display='none';">Close Without Saving</a>
|
||||
</footer>
|
||||
</div>
|
||||
</section -->
|
||||
|
||||
<div id="messagingSection"></div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue