Compare commits

...

11 Commits

Author SHA1 Message Date
Robbie Antenesse bfeea0da55 Make the default theme use Lexiconga colors! 2019-05-30 17:06:57 -06:00
Robbie Antenesse 602a5a136f Enable changing themes 2019-05-30 17:05:59 -06:00
Robbie Antenesse ca43c05b60 Split all colors into a _defaultTheme.scss 2019-05-30 15:47:50 -06:00
Robbie Antenesse ca1bb63295 Update footer styling 2019-05-30 14:59:38 -06:00
Robbie Antenesse f545f5c786 Show/remove share link if turning off Make Public 2019-05-30 14:21:09 -06:00
Robbie Antenesse 47964bcd53 Fix IPA field help rendering; Use arrows in usage.html 2019-05-30 14:11:06 -06:00
Robbie Antenesse 7221a094bc Add prominent share links if logged in or viewing 2019-05-30 14:01:34 -06:00
Robbie Antenesse b4687cd992 Parse references on backend for view 2019-05-30 13:59:53 -06:00
Robbie Antenesse cbe1099a5c Correctly link public word references 2019-05-30 12:18:59 -06:00
Robbie Antenesse d820aa87cc Add public word view 2019-05-30 11:20:02 -06:00
Robbie Antenesse afea58de6a Make parseReferences() parse its own references 2019-05-30 11:18:16 -06:00
23 changed files with 618 additions and 197 deletions

View File

@ -7,7 +7,7 @@
<title>Lexiconga</title>
<script src="src/index.js"></script>
</head>
<body>
<body id="defaultTheme">
<header id="top">
<h1 id="title">Lexiconga</h1>
@ -136,7 +136,7 @@
<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>
|
<span class="separator">|</span>
<a class="button" id="helpInfoButton">Help</a>
<a class="button" id="termsInfoButton">Terms</a>
<a class="button" id="privacyInfoButton">Privacy</a>
@ -162,8 +162,8 @@
</label>
<label>Theme
<select disabled>
<option selected value="default">Default</option>
<select id="settingsTheme">
<option value="default">Default</option>
<option value="dark">Dark</option>
<option value="light">Light</option>
<option value="blue">Blue</option>

View File

@ -57,6 +57,7 @@ export const DEFAULT_DICTIONARY = {
export const DEFAULT_SETTINGS = {
useIPAPronunciationField: true,
useHotkeys: true,
theme: 'default',
};
export const DEFAULT_PAGE_SIZE = 50;

View File

@ -9,17 +9,17 @@ that allow you to type any IPA symbol with only a standard keyboard.</p>
<li>
<p>Many symbols that look similar to letters of the English alphabet (such as
small caps) can be produced by doubling the capital letter:</p>
<pre><code> GG -&gt; ɢ ?? -&gt; ʔ NN -&gt; ɴ BB -&gt; ʙ RR -&gt; ʀ XX -&gt; χ
LL -&gt; ʟ II -&gt; ɪ YY -&gt; ʏ UU -&gt; ʊ EE -&gt; ɛ OO -&gt; ɞ
AA -&gt; ɑ '' -&gt; ˈ ,, -&gt; ˌ :: -&gt; ː
<pre><code> GG &rarr; ɢ ?? &rarr; ʔ NN &rarr; ɴ BB &rarr; ʙ RR &rarr; ʀ XX &rarr; χ
LL &rarr; ʟ II &rarr; ɪ YY &rarr; ʏ UU &rarr; ʊ EE &rarr; ɛ OO &rarr; ɞ
AA &rarr; ɑ '' &rarr; ˈ ,, &rarr; ˌ :: &rarr; ː
</code></pre>
</li>
<li>
<p>The slash is used to &quot;reflect,&quot; &quot;rotate,&quot; or otherwise flip around a given
symbol:</p>
<pre><code> ʀ/ -&gt; ʁ ʔ/ -&gt; ʕ ?/ -&gt; ʕ r/ -&gt; ɹ y/ -&gt; ʎ m/ -&gt; ɯ
o/ -&gt; ø e/ -&gt; ə ɛ/ -&gt; ɜ c/ -&gt; ɔ a/ -&gt; ɐ ɑ/ -&gt; ɒ
w/ -&gt; ʍ h/ -&gt; ɥ k/ -&gt; ʞ !/ -&gt; ¡ v/ -&gt; ʌ
<pre><code> ʀ/ &rarr; ʁ ʔ/ &rarr; ʕ ?/ &rarr; ʕ r/ &rarr; ɹ y/ &rarr; ʎ m/ &rarr; ɯ
o/ &rarr; ø e/ &rarr; ə ɛ/ &rarr; ɜ c/ &rarr; ɔ a/ &rarr; ɐ ɑ/ &rarr; ɒ
w/ &rarr; ʍ h/ &rarr; ɥ k/ &rarr; ʞ !/ &rarr; ¡ v/ &rarr; ʌ
</code></pre>
<p>Also note that for any digraph, if either of the two characters that
compose it are not &quot;standard&quot; letters you can find on your keyboard, the
@ -35,26 +35,26 @@ visual <code>o|</code> and <code>|o</code>.</p>
characters, or two characters next to each other. This includes &quot;hooked&quot;
letters (such as ŋ), produced with the original letter and a comma, and
&quot;stroked&quot; letters (such as ɟ), produced with the original letter and a dash:</p>
<pre><code> m, -&gt; ɱ n, -&gt; ŋ ŋ, -&gt; ɲ v, -&gt; ⱱ c, -&gt; ç j, -&gt; ʝ
x, -&gt; ɣ ɣ, -&gt; χ h, -&gt; ɦ w, -&gt; ɰ
<pre><code> m, &rarr; ɱ n, &rarr; ŋ ŋ, &rarr; ɲ v, &rarr; ⱱ c, &rarr; ç j, &rarr; ʝ
x, &rarr; ɣ ɣ, &rarr; χ h, &rarr; ɦ w, &rarr; ɰ
j- -&gt; ɟ h- -&gt; ħ l- -&gt; ɬ i- -&gt; ɨ u- -&gt; ʉ e- -&gt; ɘ
o- -&gt; ɵ ʕ- -&gt; ʢ ?- -&gt; ʡ ʔ- -&gt; ʡ
j- &rarr; ɟ h- &rarr; ħ l- &rarr; ɬ i- &rarr; ɨ u- &rarr; ʉ e- &rarr; ɘ
o- &rarr; ɵ ʕ- &rarr; ʢ ?- &rarr; ʡ ʔ- &rarr; ʡ
LZ -&gt; ɮ OX -&gt; ɤ XO -&gt; ɤ OE -&gt; œ EB -&gt; ɞ AE -&gt; æ
CE -&gt; ɶ RL -&gt; ɺ LR -&gt; ɺ ɾl -&gt; ɺ lɾ -&gt; ɺ
LZ &rarr; ɮ OX &rarr; ɤ XO &rarr; ɤ OE &rarr; œ EB &rarr; ɞ AE &rarr; æ
CE &rarr; ɶ RL &rarr; ɺ LR &rarr; ɺ ɾl &rarr; ɺ lɾ &rarr; ɺ
w| -&gt; ɰ o/ -&gt; ø ɜ( -&gt; ɞ /\ -&gt; ʌ o| -&gt; ɑ a| -&gt; ɑ
|o -&gt; ɒ |a -&gt; ɒ
w| &rarr; ɰ o/ &rarr; ø ɜ( &rarr; ɞ /\ &rarr; ʌ o| &rarr; ɑ a| &rarr; ɑ
|o &rarr; ɒ |a &rarr; ɒ
o. -&gt; ʘ |= -&gt; ǂ || -&gt; ‖ /^ -&gt; ↗ /&gt; -&gt; ↗ \v -&gt; ↘
\&gt; -&gt; ↘
o. &rarr; ʘ |= &rarr; ǂ || &rarr; ‖ /^ &rarr; ↗ /&gt; &rarr; ↗ \v &rarr; ↘
\&gt; &rarr; ↘
</code></pre>
</li>
<li>
<p>Others are based on pronunciation:</p>
<pre><code> PH -&gt; ɸ BH -&gt; β TH -&gt; θ DH -&gt; ð SH -&gt; ʃ ZH -&gt; ʒ
SJ -&gt; ɕ ZJ -&gt; ʑ ʃx -&gt; ɧ xʃ -&gt; ɧ
<pre><code> PH &rarr; ɸ BH &rarr; β TH &rarr; θ DH &rarr; ð SH &rarr; ʃ ZH &rarr; ʒ
SJ &rarr; ɕ ZJ &rarr; ʑ ʃx &rarr; ɧ xʃ &rarr; ɧ
</code></pre>
<p>It may be worth noting at this point that digraphs of two lowercase letters
were intentionally avoided to prevent interference with regular typing. If
@ -66,7 +66,7 @@ to the second character.</p>
</li>
<li>
<p>A few digraphs are based on shape:</p>
<pre><code> rO -&gt; ɾ r0 -&gt; ɾ vO -&gt; ʋ v0 -&gt; ʋ
<pre><code> rO &rarr; ɾ r0 &rarr; ɾ vO &rarr; ʋ v0 &rarr; ʋ
</code></pre>
<p>Another related point: digraphs that contain a lowercase letter can also be
typed with that letter as uppercase. So, if <code>RO</code> is easier to type than
@ -75,35 +75,35 @@ typed with that letter as uppercase. So, if <code>RO</code> is easier to type th
<li>
<p>Retroflex and nonpulmonic symbols have their own categories:</p>
<pre><code> retroflex: ) looks like the shape of the tongue
t) -&gt; ʈ d) -&gt; ɖ n) -&gt; ɳ r) -&gt; ɽ ɾ) -&gt; ɽ s) -&gt; ʂ
z) -&gt; ʐ ɹ) -&gt; ɻ l) -&gt; ɭ ɗ) -&gt; ᶑ
t) &rarr; ʈ d) &rarr; ɖ n) &rarr; ɳ r) &rarr; ɽ ɾ) &rarr; ɽ s) &rarr; ʂ
z) &rarr; ʐ ɹ) &rarr; ɻ l) &rarr; ɭ ɗ) &rarr; ᶑ
clicks: clicking noise reminiscent of a *
o* -&gt; ʘ |* -&gt; ǀ !* -&gt; ǃ =* -&gt; ǁ
o* &rarr; ʘ |* &rarr; ǀ !* &rarr; ǃ =* &rarr; ǁ
implosives and ejective marker: direction of airflow
b( -&gt; ɓ d( -&gt; ɗ j( -&gt; ʄ ɟ( -&gt; ʄ g( -&gt; ɠ ɢ( -&gt; ʛ
ɖ( -&gt; ᶑ ') -&gt; ʼ
b( &rarr; ɓ d( &rarr; ɗ j( &rarr; ʄ ɟ( &rarr; ʄ g( &rarr; ɠ ɢ( &rarr; ʛ
ɖ( &rarr; ᶑ ') &rarr; ʼ
</code></pre>
</li>
<li>
<p>Superscripts and diacritics that go above the letter use <code>^</code>, diacritics that
go below use <code>_</code>, and miscellaneous &quot;moved&quot; symbols
use <code>&lt;</code> or <code>&gt;</code>:</p>
<pre><code> ^h -&gt; ʰ ^n -&gt; ⁿ ^m -&gt; ᵐ ^ŋ -&gt; ᵑ ^l -&gt; ˡ ^w -&gt; ʷ
^j -&gt; ʲ ^ɥ -&gt; ᶣ ^ʋ -&gt; ᶹ ^ɣ -&gt; ˠ ^ʕ -&gt; ˤ
<pre><code> ^h &rarr; ʰ ^n &rarr; ⁿ ^m &rarr; ᵐ ^ŋ &rarr; ᵑ ^l &rarr; ˡ ^w &rarr; ʷ
^j &rarr; ʲ ^ɥ &rarr; ᶣ ^ʋ &rarr; ᶹ ^ɣ &rarr; ˠ ^ʕ &rarr; ˤ
_| -&gt; ◌̩ ^| -&gt; ◌̍ _o -&gt; ◌̥ ^o -&gt; ◌̊ _v -&gt; ◌̬ ^v -&gt; ◌̌
_| &rarr; ◌̩ ^| &rarr; ◌̍ _o &rarr; ◌̥ ^o &rarr; ◌̊ _v &rarr; ◌̬ ^v &rarr; ◌̌
_^ -&gt; ◌̯ _: -&gt; ◌̤ _~ -&gt; ◌̰ _[ -&gt; ◌̪ _] -&gt; ◌̺ _{ -&gt; ◌̼
_+ -&gt; ◌̟ __ -&gt; ◌̠ _) -&gt; ◌̹ _( -&gt; ◌̜ _# -&gt; ◌̻ [] -&gt; ◌̻
_^ &rarr; ◌̯ _: &rarr; ◌̤ _~ &rarr; ◌̰ _[ &rarr; ◌̪ _] &rarr; ◌̺ _{ &rarr; ◌̼
_+ &rarr; ◌̟ __ &rarr; ◌̠ _) &rarr; ◌̹ _( &rarr; ◌̜ _# &rarr; ◌̻ [] &rarr; ◌̻
^&gt; -&gt; ◌̚ ^: -&gt; ◌̈ ^x -&gt; ◌̽ ^~ -&gt; ◌̃
^&gt; &rarr; ◌̚ ^: &rarr; ◌̈ ^x &rarr; ◌̽ ^~ &rarr; ◌̃
-' -&gt; ˔ _˔ -&gt; ◌̝ -, -&gt; ˕ _˕ -&gt; ◌̞ &lt;| -&gt; ⊣ _⊣ -&gt; ◌̘
&gt;| -&gt; ⊢ _⊢ -&gt; ◌̙
-' &rarr; ˔ _˔ &rarr; ◌̝ -, &rarr; ˕ _˕ &rarr; ◌̞ &lt;| &rarr; ⊣ _⊣ &rarr; ◌̘
&gt;| &rarr; ⊢ _⊢ &rarr; ◌̙
~~ -&gt; ◌̴ &gt;r -&gt; ˞
~~ &rarr; ◌̴ &gt;r &rarr; ˞
</code></pre>
<p>Note that all of these sequences can be flipped in order—that is, <code>^h</code>
produces the same thing as <code>h^</code>. This allows usage such as <code>|&lt;_</code> to produce
@ -111,16 +111,16 @@ produces the same thing as <code>h^</code>. This allows usage such as <code>|&lt
</li>
<li>
<p>Tonal countours use numbers plus <code>|</code>:</p>
<pre><code> 5| -&gt; ˥ 4| -&gt; ˦ 3| -&gt; ˧ 2| -&gt; ˨ 1| -&gt; ˩
+| -&gt; ꜛ -| -&gt; ꜜ
<pre><code> 5| &rarr; ˥ 4| &rarr; ˦ 3| &rarr; ˧ 2| &rarr; ˨ 1| &rarr; ˩
+| &rarr; ꜛ -| &rarr; ꜜ
</code></pre>
</li>
<li>
<p>Finally, some digraphs simply have unique mnemonics:</p>
<pre><code> ː- -&gt; ˑ &quot;chop off&quot; the bottom triangle
(( -&gt; ◌͡◌ two parens for a tie that connects 2 chars
)) -&gt; ◌͜◌
◌͜◌) -&gt; ‿ one more paren to make it a little bit longer
<pre><code> ː- &rarr; ˑ &quot;chop off&quot; the bottom triangle
(( &rarr; ◌͡◌ two parens for a tie that connects 2 chars
)) &rarr; ◌͜◌
◌͜◌) &rarr; ‿ one more paren to make it a little bit longer
</code></pre>
</li>
</ul>

View File

@ -1,4 +1,4 @@
import { renderDictionaryDetails, renderPartsOfSpeech, renderAll } from "./render";
import { renderDictionaryDetails, renderPartsOfSpeech, renderAll, renderWords } from "./render";
import { removeTags, cloneObject, getTimestampInSeconds, download, slugify } from "../helpers";
import { LOCAL_STORAGE_KEY, DEFAULT_DICTIONARY, MIGRATE_VERSION } from "../constants";
import { addMessage, getNextId, hasToken } from "./utilities";
@ -63,7 +63,10 @@ export function saveEditModal() {
window.currentDictionary.settings.caseSensitive = document.getElementById('editCaseSensitive').checked;
const needsReSort = window.currentDictionary.settings.sortByDefinition !== document.getElementById('editSortByDefinition').checked;
window.currentDictionary.settings.sortByDefinition = document.getElementById('editSortByDefinition').checked;
let needsWordRender = false;
if (hasToken()) {
needsWordRender = window.currentDictionary.settings.isPublic !== document.getElementById('editIsPublic').checked;
window.currentDictionary.settings.isPublic = document.getElementById('editIsPublic').checked;
} else {
window.currentDictionary.settings.isPublic = false;
@ -73,8 +76,8 @@ export function saveEditModal() {
saveDictionary();
renderDictionaryDetails();
renderPartsOfSpeech();
if (needsReSort) {
if (needsReSort || needsWordRender) {
sortWords(true);
}

View File

@ -36,7 +36,25 @@ export function renderDictionaryDetails() {
export function renderName() {
const dictionaryName = removeTags(window.currentDictionary.name) + ' ' + removeTags(window.currentDictionary.specification);
document.getElementById('dictionaryName').innerHTML = dictionaryName;
const name = document.getElementById('dictionaryName');
name.innerHTML = dictionaryName;
const isPublic = hasToken() && window.currentDictionary.settings.isPublic;
const shareLinkElement = document.getElementById('dictionaryShare');
if (isPublic && !shareLinkElement) {
const shareLink = document.createElement('a');
shareLink.id = 'dictionaryShare';
shareLink.classList.add('button');
shareLink.style.float = 'right';
shareLink.href = window.location.pathname.match(new RegExp(window.currentDictionary.externalID + '$')) ? window.location.pathname
: window.location.pathname.substring(0, window.location.pathname.indexOf(window.currentDictionary.externalID)) + window.currentDictionary.externalID;
shareLink.target = '_blank';
shareLink.title = 'Public Link to Dictionary';
shareLink.innerHTML = '&#10150;';
name.parentElement.insertBefore(shareLink, name);
} else if (!isPublic && shareLinkElement) {
shareLinkElement.parentElement.removeChild(shareLinkElement);
}
}
export function renderDescription() {
@ -179,10 +197,10 @@ export function renderWords() {
<h4 class="word">${word.name}${homonymnNumber > 0 ? ' <sub>' + homonymnNumber.toString() + '</sub>' : ''}</h4>
<span class="pronunciation">${word.pronunciation}</span>
<span class="part-of-speech">${word.partOfSpeech}</span>
${isPublic ? `<a class="small button share-link" href="${shareLink}" target="_blank" title="Public Link to Word">&#10150;</a>` : ''}
<span class="small button word-option-button">Options</span>
<div class="word-option-list" style="display:none;">
<div class="word-option" id="edit_${word.wordId}">Edit</div>
${isPublic ? `<a class="word-option" href="${shareLink}" target="_blank">Share</a>` : ''}
<div class="word-option" id="delete_${word.wordId}">Delete</div>
</div>
</header>
@ -278,6 +296,12 @@ export function renderEditForm(wordId = false) {
}
}
export function renderIPAHelp() {
import('./KeyboardFire/phondue/usage.html').then(html => {
renderInfoModal(html);
});
}
export function renderIPATable(ipaTableButton) {
ipaTableButton = typeof ipaTableButton.target === 'undefined' || ipaTableButton.target === '' ? ipaTableButton : ipaTableButton.target;
const label = ipaTableButton.parentElement.innerText.replace(/(Field Help|IPA Chart)/g, '').trim();

View File

@ -8,6 +8,7 @@ import { enableHotKeys, disableHotKeys } from "./hotkeys";
export function loadSettings() {
const storedSettings = window.localStorage.getItem(SETTINGS_KEY);
window.settings = storedSettings ? JSON.parse(storedSettings) : cloneObject(DEFAULT_SETTINGS);
updateTheme();
toggleIPAPronunciationFields();
}
@ -16,11 +17,17 @@ export function saveSettings() {
addMessage('Settings Saved!');
}
export function updateTheme() {
const { theme } = window.settings;
document.body.id = theme + 'Theme';
}
export function openSettingsModal() {
const { useIPAPronunciationField, useHotkeys } = window.settings;
const { useIPAPronunciationField, useHotkeys, theme } = window.settings;
document.getElementById('settingsUseIPA').checked = useIPAPronunciationField;
document.getElementById('settingsUseHotkeys').checked = useHotkeys;
document.getElementById('settingsTheme').value = theme;
document.getElementById('settingsModal').style.display = '';
}
@ -28,6 +35,7 @@ export function openSettingsModal() {
export function saveSettingsModal() {
window.settings.useIPAPronunciationField = document.getElementById('settingsUseIPA').checked;
window.settings.useHotkeys = document.getElementById('settingsUseHotkeys').checked;
window.settings.theme = document.getElementById('settingsTheme').value;
if (hasToken()) {
import('./account/index.js').then(account => {
@ -49,6 +57,7 @@ export function saveSettingsModal() {
}
saveSettings();
updateTheme();
toggleHotkeysEnabled();
toggleIPAPronunciationFields();
}

View File

@ -1,5 +1,5 @@
import {showSection, hideDetailsPanel} from './displayToggles';
import { renderWords, renderEditForm, renderMaximizedTextbox, renderInfoModal, renderIPATable } from './render';
import { renderWords, renderEditForm, renderMaximizedTextbox, renderInfoModal, renderIPATable, renderIPAHelp } from './render';
import { confirmEditWord, cancelEditWord, confirmDeleteWord, submitWordForm } from './wordManagement';
import { openEditModal, saveEditModal, saveAndCloseEditModal, exportDictionary, exportWords, importDictionary, importWords, confirmDeleteDictionary } from './dictionaryManagement';
import { goToNextPage, goToPreviousPage, goToPage } from './pagination';
@ -287,11 +287,6 @@ export function setupIPAButtons() {
button.addEventListener('click', renderIPATable);
});
const renderIPAHelp = () => {
import('./KeyboardFire/phondue/usage.html').then(html => {
renderInfoModal(html);
});
}
Array.from(ipaFieldHelpButtons).forEach(button => {
button.removeEventListener('click', renderIPAHelp);
button.addEventListener('click', renderIPAHelp);

View File

@ -4,6 +4,7 @@ import { getWordsStats, wordExists } from '../utilities';
import { getMatchingSearchWords, highlightSearchTerm, getSearchFilters, getSearchTerm } from '../search';
import { showSection } from '../displayToggles';
import { setupSearchFilters, setupInfoModal } from './setupListeners';
import { parseReferences } from '../wordManagement';
export function renderAll() {
renderDictionaryDetails();
@ -25,6 +26,9 @@ export function renderDictionaryDetails() {
export function renderName() {
const dictionaryName = removeTags(window.currentDictionary.name) + ' ' + removeTags(window.currentDictionary.specification);
document.getElementById('dictionaryName').innerHTML = dictionaryName;
const shareLink = window.location.pathname.match(new RegExp(window.currentDictionary.externalID + '$')) ? window.location.pathname
: window.location.pathname.substring(0, window.location.pathname.indexOf(window.currentDictionary.externalID)) + window.currentDictionary.externalID;
document.getElementById('dictionaryShare').href = shareLink;
}
export function renderDescription() {
@ -134,18 +138,8 @@ export function renderWords() {
}
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);
}
});
}
let detailsMarkdown = originalWord.details;
detailsMarkdown = parseReferences(detailsMarkdown);
const word = highlightSearchTerm({
name: removeTags(originalWord.name),
pronunciation: removeTags(originalWord.pronunciation),
@ -154,11 +148,13 @@ export function renderWords() {
details: detailsMarkdown,
wordId: originalWord.wordId,
});
const shareLink = window.location.pathname + (window.location.pathname.match(new RegExp(word.wordId + '$')) ? '' : '/' + word.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>
<a href="${shareLink}" target="_blank" class="small button word-option-button" title="Link to Word">&#10150;</a>
</header>
<dl>
<dt class="definition">${word.definition}</dt>

View File

@ -46,43 +46,46 @@ export function sortWords(render) {
}
}
export function parseReferences(detailsMarkdown, references) {
new Set(references).forEach(reference => {
let wordToFind = reference.replace(/\{\{|\}\}/g, '');
let homonymn = 0;
if (wordToFind.includes(':')) {
const separator = wordToFind.indexOf(':');
homonymn = wordToFind.substr(separator + 1);
wordToFind = wordToFind.substring(0, separator);
if (homonymn && homonymn.trim()
&& !isNaN(parseInt(homonymn.trim())) && parseInt(homonymn.trim()) > 0) {
homonymn = parseInt(homonymn.trim());
} else {
homonymn = false;
export function parseReferences(detailsMarkdown) {
const references = detailsMarkdown.match(/\{\{.+?\}\}/g);
if (references && Array.isArray(references)) {
new Set(references).forEach(reference => {
let wordToFind = reference.replace(/\{\{|\}\}/g, '');
let homonymn = 0;
if (wordToFind.includes(':')) {
const separator = wordToFind.indexOf(':');
homonymn = wordToFind.substr(separator + 1);
wordToFind = wordToFind.substring(0, separator);
if (homonymn && homonymn.trim()
&& !isNaN(parseInt(homonymn.trim())) && parseInt(homonymn.trim()) > 0) {
homonymn = parseInt(homonymn.trim());
} else {
homonymn = false;
}
}
}
let existingWordId = false;
const homonymnIndexes = getHomonymnIndexes({ name: wordToFind, wordId: -1 });
let existingWordId = false;
const homonymnIndexes = getHomonymnIndexes({ name: wordToFind, wordId: -1 });
if (homonymn !== false && homonymn > 0) {
if (typeof homonymnIndexes[homonymn - 1] !== 'undefined') {
existingWordId = window.currentDictionary.words[homonymnIndexes[homonymn - 1]].wordId;
if (homonymn !== false && homonymn > 0) {
if (typeof homonymnIndexes[homonymn - 1] !== 'undefined') {
existingWordId = window.currentDictionary.words[homonymnIndexes[homonymn - 1]].wordId;
}
} else if (homonymn !== false) {
existingWordId = wordExists(wordToFind, true);
}
} else if (homonymn !== false) {
existingWordId = wordExists(wordToFind, true);
}
if (existingWordId !== false) {
if (homonymn < 1 && homonymnIndexes.length > 0) {
homonymn = 1;
if (existingWordId !== false) {
if (homonymn < 1 && homonymnIndexes.length > 0) {
homonymn = 1;
}
const homonymnSubHTML = homonymn > 0 ? '<sub>' + homonymn.toString() + '</sub>' : '';
const wordMarkdownLink = `[${wordToFind}${homonymnSubHTML}](#${existingWordId})`;
detailsMarkdown = detailsMarkdown.replace(new RegExp(reference, 'g'), wordMarkdownLink);
}
const homonymnSubHTML = homonymn > 0 ? '<sub>' + homonymn.toString() + '</sub>' : '';
const wordMarkdownLink = `[${wordToFind}${homonymnSubHTML}](#${existingWordId})`;
detailsMarkdown = detailsMarkdown.replace(new RegExp(reference, 'g'), wordMarkdownLink);
}
});
});
}
return detailsMarkdown;
}

View File

@ -9,6 +9,8 @@
@import 'scss/mobile/structure';
@import 'scss/mobile/elements';
@import 'scss/themes/default';
html, body {
font-family: $font;

View File

@ -1,12 +1,12 @@
RewriteEngine On # Turn on the rewriting engine
RewriteRule ^view/([0-9]+)/([0-9]+)/?$ api/router.php?view=publicview&dict=$1&word=$2 [NC,L] # Handle word ids.
RewriteRule ^view/([0-9]+)/([0-9]+)/?$ api/router.php?view=word&dict=$1&word=$2 [NC,L] # Handle word ids.
RewriteRule ^([0-9]+)/([0-9]+)/?$ api/router.php?view=publicview&dict=$1&word=$2 [NC,L] # Handle word ids.
RewriteRule ^([0-9]+)/([0-9]+)/?$ api/router.php?view=word&dict=$1&word=$2 [NC,L] # Handle word ids.
RewriteRule ^view/([0-9]+)/?$ api/router.php?view=publicview&dict=$1 [NC,L] # Handle dictionary ids.
RewriteRule ^view/([0-9]+)/?$ api/router.php?view=dictionary&dict=$1 [NC,L] # Handle dictionary ids.
RewriteRule ^([0-9]+)/?$ api/router.php?view=publicview&dict=$1 [NC,L] # Handle dictionary ids.
RewriteRule ^([0-9]+)/?$ api/router.php?view=dictionary&dict=$1 [NC,L] # Handle dictionary ids.
#RewriteRule ^issues/?$ https://github.com/Alamantus/Lexiconga/issues [R=301,L] # Shorten issues url.

View File

@ -143,13 +143,13 @@ VALUES ($new_id, ?, ?, ?, ?)";
$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_map(function ($row) use ($dictionary) {
return array(
'name' => $row['name'],
'pronunciation' => $row['pronunciation'],
'partOfSpeech' => $row['part_of_speech'],
'definition' => $row['definition'],
'details' => $row['details'],
'details' => $this->parseReferences($row['details'], $dictionary),
'lastUpdated' => is_null($row['last_updated']) ? intval($row['created_on']) : intval($row['last_updated']),
'createdOn' => intval($row['created_on']),
'wordId' => intval($row['word_id']),
@ -160,6 +160,87 @@ VALUES ($new_id, ?, ?, ?, ?)";
return array();
}
public function getSpecificPublicDictionaryWord ($dictionary, $word) {
if (is_numeric($dictionary) && is_numeric($word)) {
$query = "SELECT words.* FROM words JOIN dictionaries ON id = dictionary WHERE dictionary=? AND word_id=? AND is_public=1";
$result = $this->db->query($query, array($dictionary, $word))->fetch();
if ($result) {
return array(
'name' => $result['name'],
'pronunciation' => $result['pronunciation'],
'partOfSpeech' => $result['part_of_speech'],
'definition' => $result['definition'],
'details' => $this->parseReferences($result['details'], $dictionary),
'lastUpdated' => is_null($result['last_updated']) ? intval($result['created_on']) : intval($result['last_updated']),
'createdOn' => intval($result['created_on']),
'wordId' => intval($result['word_id']),
);
}
}
return false;
}
private function parseReferences($details, $dictionary_id) {
$details = strip_tags($details);
if (preg_match_all('/\{\{.+?\}\}/', $details, $references) !== false) {
$references = array_unique($references[0]);
foreach($references as $reference) {
$word_to_find = preg_replace('/\{\{|\}\}/', '', $reference);
$homonymn = 0;
if (strpos($word_to_find, ':') !== false) {
$separator = strpos($word_to_find, ':');
$homonymn = substr($word_to_find, $separator + 1);
$word_to_find = substr($word_to_find, 0, $separator);
if ($homonymn && trim($homonymn) && intval(trim($homonymn)) > 0) {
$homonymn = intval(trim($homonymn));
} else {
$homonymn = false;
}
}
$target_id = false;
$reference_ids = $this->getWordIdsWithName($dictionary_id, $word_to_find);
if (count($reference_ids) > 0) {
if ($homonymn !== false && $homonymn > 0) {
if (isset($reference_ids[$homonymn - 1])) {
$target_id = $reference_ids[$homonymn - 1];
}
} else if ($homonymn !== false) {
$target_id = $reference_ids[0];
}
if ($target_id !== false) {
if ($homonymn < 1) {
$homonymn = 1;
}
$homonymn_sub_html = $homonymn > 0 ? '<sub>' . $homonymn . '</sub>' : '';
$site_root = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], $dictionary_id));
$markdown_link = '<a href="' . $site_root . $dictionary_id . '/' . $target_id .'" target="_blank" title="Link to Reference">'
. $word_to_find . $homonymn_sub_html . '</a>';
$details = str_replace($reference, $markdown_link, $details);
}
}
}
}
return $details;
}
private function getWordIdsWithName($dictionary, $word_name) {
if (is_numeric($dictionary)) {
$query = "SELECT word_id FROM words WHERE dictionary=? AND name=?";
$results = $this->db->query($query, array($dictionary, $word_name))->fetchAll();
if ($results) {
return array_map(function ($row) {
return 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();

View File

@ -2,7 +2,7 @@
$view = isset($_GET['view']) ? $_GET['view'] : false;
switch ($view) {
case 'publicview': {
case 'dictionary': {
$html = file_get_contents('../view.html');
$dict = isset($_GET['dict']) ? $_GET['dict'] : false;
if ($dict !== false) {
@ -26,4 +26,43 @@ switch ($view) {
}
break;
}
case 'word': {
$html = file_get_contents('../view.html');
$dict = isset($_GET['dict']) ? $_GET['dict'] : false;
$word = isset($_GET['word']) ? $_GET['word'] : false;
if ($dict !== false && $word !== false) {
require_once('./Dictionary.php');
$dictionary = new Dictionary();
$dictionary_data = $dictionary->getPublicDictionaryDetails($dict);
if ($dictionary_data !== false) {
$dictionary_name = $dictionary_data['name'] . ' ' . $dictionary_data['specification'];
$word_data = $dictionary->getSpecificPublicDictionaryWord($dict, $word);
if ($word_data === false) {
$word_data = array(
'name' => 'Error: Word Not Found',
'pronunciation' => '',
'partOfSpeech' => '',
'definition' => 'No word with the id ' . $word . ' was found in the ' . $dictionary_name,
'details' => '',
'lastUpdated' => null,
'createdOn' => null,
'wordId' => null,
);
}
$dictionary_data['words'] = array($word_data);
$html = str_replace('{{dict}}', $dict, $html);
$html = str_replace('{{dict_name}}', $word_data['name'] . ' in the ' . $dictionary_name, $html);
$html = str_replace('{{public_name}}', $dictionary_data['createdBy'], $html);
$dictionary_json = json_encode($dictionary_data);
$html = str_replace('{{dict_json}}', addslashes($dictionary_json), $html);
} else {
$html = str_replace('{{dict}}', 'error', $html);
$html = str_replace('{{dict_name}}', 'Error: Dictionary Not Found', $html);
$html = str_replace('{{public_name}}', 'Error', $html);
$html = str_replace('{{dict_json}}', '{"name": "Error:", "specification": "Dictionary Not Found", "words": []}', $html);
}
echo $html;
}
break;
}
}

View File

@ -13,4 +13,10 @@
}
}
}
}
.share-link {
margin-left: $general-padding !important;
line-height: 16px !important;
padding: 1px 3px 3px !important;
}

View File

@ -1,7 +1,6 @@
header {
display: block;
padding: 5px $general-padding;
border-bottom: 1px solid $mid;
margin: 0 0 5px;
&#top {
@ -14,7 +13,7 @@ main {
width: 90%;
max-width: 1000px;
min-height: 400px;
margin: 0 auto;
margin: 0 auto ($header-height * 1.25);
#sideColumn,
#mainColumn {
@ -29,13 +28,12 @@ main {
#mainColumn {
width: 64%;
margin: 0;
margin: $general-padding 0 0;
}
article {
width: 95%;
margin: 10px auto;
border: $border;
dl {
padding: 0 $general-padding;
@ -46,6 +44,5 @@ main {
footer {
display: block;
padding: $general-padding;
border-top: 1px solid $mid;
margin: 5px 0 0;
}

View File

@ -1,8 +1,8 @@
p, span {
&.red {
color: $red;
}
a {
text-decoration: underline;
}
p, span {
&.bold {
font-weight: bold;
}
@ -34,24 +34,42 @@ label {
}
.label-button {
@extend .button;
display: inline-block;
padding: 3px 9px;
border-radius: 3px;
line-height: 30px;
cursor: pointer;
user-select: none;
font-size: 80%;
font-weight: normal;
float: right;
cursor: pointer;
line-height: 80% !important;
padding: 3px 3px 5px;
&.small {
font-size: 80%;
line-height: 25px;
}
}
.label-help-button {
@extend .button;
display: inline-block;
padding: 3px 9px;
border-radius: 3px;
line-height: 30px;
cursor: pointer;
user-select: none;
font-size: 70%;
font-weight: normal;
cursor: pointer;
line-height: 70% !important;
padding: 2px 2px 4px;
&.small {
font-size: 80%;
line-height: 25px;
}
}
}
@ -62,38 +80,46 @@ ul {
.tag {
display: inline-block;
padding: 3px 9px;
border: $border;
border-radius: 3px;
background-color: $light;
line-height: 30px;
&.small {
font-size: 80%;
line-height: 25px;
}
&.red {
background-color: $red;
color: $white;
}
}
span .tag {
@extend .tag;
border-radius: 3px 0 0 3px;
display: inline-block;
padding: 3px 9px;
border-radius: 3px;
line-height: 30px;
&.small {
font-size: 80%;
line-height: 25px;
}
&+.tag {
border-left: none;
border-radius: 0 3px 3px 0;
background-color: $white;
}
}
.button {
@extend .tag;
display: inline-block;
padding: 3px 9px;
border-radius: 3px;
line-height: 30px;
cursor: pointer;
user-select: none;
&.small {
font-size: 80%;
line-height: 25px;
}
}
.modal {
@ -109,7 +135,6 @@ span .tag {
left: 0;
bottom: 0;
right: 0;
background-color: #000000;
opacity: 0.5;
}
@ -122,8 +147,6 @@ span .tag {
max-width: 100%;
height: 600px;
max-height: 100%;
background-color: $white;
border: $border;
border-radius: 5px;
.close-button {
@ -139,25 +162,13 @@ span .tag {
}
.tabs {
ul {
border-bottom: $border;
}
li {
display: inline-block;
list-style: none;
margin: 0 2px -1px;
padding: 10px $general-padding;
border-top: $border;
border-left: $border;
border-right: $border;
border-radius: 5px 5px 0 0;
background-color: $mid;
cursor: pointer;
&.active {
background-color: $white;
border-bottom: 1px solid $white;
}
}
}

View File

@ -42,7 +42,6 @@
left: unset;
bottom: unset;
right: unset;
background-color: $white;
padding: ($general-padding / 2) $general-padding ($general-padding * 0.25);
font-size: 90%;
@ -100,16 +99,13 @@
#wordForm {
position: fixed;
top: auto;
top: $header-height + $general-padding;
width: 31%;
max-width: 320px;
padding: 10px;
background-color: $light;
border: $border;
border-radius: 5px;
max-height: 80%;
overflow-y: auto;
box-shadow: 4px 4px 5px 0px rgba(50, 50, 50, 0.75);
}
.edit-form {
@ -117,11 +113,12 @@
max-width: 500px;
}
#detailsSection {
padding: $general-padding;
background-color: $white;
border: $border;
#mainColumn {
border-radius: 5px;
}
#detailsSection {
padding: $general-padding $general-padding 0;
#dictionaryName {
margin-top: 0;
@ -135,8 +132,6 @@
list-style: none;
margin: 0;
padding: 10px $general-padding;
border: $border;
background-color: $light;
cursor: pointer;
&:first-child {
@ -148,15 +143,10 @@
&:not(:first-child) {
border-left: none;
}
&.active {
background-color: #bababa;
}
}
}
#detailsPanel {
background-color: $white;
padding: $general-padding;
max-height: 400px;
overflow-y: auto;
@ -170,8 +160,6 @@
.entry {
background-color: $light;
.word {
display: inline-block;
margin: 3px 0;
@ -202,15 +190,12 @@
position: absolute;
top: 3px;
right: 3px;
background-color: $white;
border: $border;
border-radius: 5px;
.word-option {
padding: 10px 25px;;
&:hover {
background-color: $light;
cursor: pointer;
}
}
@ -339,13 +324,12 @@ $nav-font-height: 16px;
bottom: $general-padding;
right: $general-padding;
max-width: 300px;
z-index: 10;
.message {
position: relative;
display: block;
padding: $general-padding ($general-padding * 2) $general-padding $general-padding;
background-color: $light;
border: $border;
border-radius: 5px;
margin-bottom: 5px;
@ -353,10 +337,6 @@ $nav-font-height: 16px;
margin-bottom: 0;
}
&.error {
background-color: lighten($red, 0.75);
}
.close-button {
position: absolute;
top: 5px;
@ -372,7 +352,6 @@ $nav-font-height: 16px;
right: -2px;
width: 20px;
height: 20px;
background-color: #455455;
opacity: 0.5;
transform-origin: center left;
transform: scaleX(0);
@ -391,10 +370,19 @@ $nav-font-height: 16px;
}
#bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 5;
text-align: center;
padding: $general-padding / 2;
a {
color: #000000;
text-decoration: none;
}
.separator {
display: inline;
}
}

View File

@ -2,12 +2,12 @@ $font: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubunt
$header-height: 60px;
$dark: #bababa;
$mid: #dedede;
$light: #efefef;
$white: #ffffff;
// $dark: #bababa;
// $mid: #dedede;
// $light: #efefef;
// $white: #ffffff;
$red: #d42932;
// $red: #d42932;
$border: 1px solid $dark;
// $border: 1px solid $dark;
$general-padding: 20px;

View File

@ -12,6 +12,8 @@ header {
}
main {
margin-bottom: $general-padding;
#sideColumn {
display: block;
width: 0;

View File

@ -1,21 +1,21 @@
@media (max-width: 750px) {
.tag {
padding: 2px 6px;
font-size: 90%;
line-height: 120%;
// .tag {
// padding: 2px 6px;
// font-size: 90%;
// line-height: 120%;
&.small {
font-size: 70%;
line-height: 100%;
}
}
// &.small {
// font-size: 70%;
// line-height: 100%;
// }
// }
.button {
@extend .tag;
cursor: pointer;
user-select: none;
}
// .button {
// @extend .tag;
// cursor: pointer;
// user-select: none;
// }
.tabs {
li {

View File

@ -34,10 +34,7 @@
width: 32px;
height: 32px;
display: block;
background-color: #00de00;
border: $border;
border-radius: 0 3px 3px 0;
color: $white;
font-size: 30px;
line-height: 24px;
font-weight: bold;
@ -88,4 +85,14 @@
}
}
#bottom {
position: relative;
bottom: unset;
.separator {
display: block;
visibility: hidden;
}
}
}

View File

@ -0,0 +1,255 @@
#defaultTheme {
$dark: #bababa;
$mid: #dedede;
$light: #efefef;
$white: #ffffff;
$red: #b42032;
$header-color: #eacc9d;
$background-color: #e6cfaa;
$footer-color: #cb6318;
$link-color: #a01000;
$button-color: #dcb078;
$message-color: #c0c088;
$word-form-color: #ba5536;
$dictionary-color: #bd7251;
$entry-color: #d7ad7d;
$input-color: #efdfc0;
$details-color: #f2d5b2;
$modal-color: #f2d5b2;
$border: 1px solid $dark;
background-color: $background-color;
a {
color: $link-color;
}
p, span {
&.red {
color: $red;
}
}
label {
.label-button {
border: 1px solid darken($button-color, 2);
background-color: $button-color;
color: #000000;
text-decoration: none;
}
.label-help-button {
border: 1px solid darken($button-color, 2);
background-color: $button-color;
color: #000000;
text-decoration: none;
}
}
input, textarea, select, option {
background-color: $input-color;
}
.tag {
border: $border;
background-color: $button-color;
&.red {
background-color: $red;
color: $white;
}
}
span .tag {
border: $border;
background-color: $button-color;
&+.tag {
background-color: lighten($button-color, 25);
border-left: none;
}
}
.button {
border: 1px solid darken($button-color, 2);
background-color: $button-color;
color: #000000;
text-decoration: none;
font-weight: bold;
&.red {
background-color: $red;
color: $white;
}
}
.modal {
.modal-background {
background-color: #000000;
}
.modal-content {
background-color: $modal-color;
border: 1px solid darken($modal-color, 10);
}
}
.tabs {
ul {
border-bottom: $border;
}
li {
border-top: $border;
border-left: $border;
border-right: $border;
background-color: $button-color;
&.active {
background-color: $modal-color;
border-bottom: 1px solid $modal-color;
}
}
}
header {
border-bottom: 1px solid $mid;
}
main {
article {
border: $border;
}
}
footer {
border-top: 1px solid $mid;
}
#top {
background-color: $header-color;
border-bottom: 1px solid darken($header-color, 2);
box-shadow: 0px 4px 5px 0px rgba(50, 50, 50, 0.75);
#title {
display: inline-block;
margin: 3px $general-padding 3px 0;
}
#openSearchModal {
cursor: pointer;
}
#searchModal {
.modal-content {
section+footer {
background-color: $modal-color;
}
}
}
}
#wordForm {
background-color: $word-form-color;
border: 1px solid darken($word-form-color, 2);
box-shadow: 4px 4px 5px 0px rgba(50, 50, 50, 0.75);
}
#mainColumn {
background-color: $dictionary-color;
border: 1px solid darken($dictionary-color, 2);
box-shadow: 4px 4px 5px 0px rgba(50, 50, 50, 0.75);
}
#detailsSection {
nav ul {
li {
border: 1px solid darken($button-color, 20);
background-color: $button-color;
&.active {
background-color: lighten($button-color, 15);
}
}
}
#detailsPanel {
background-color: $details-color;
}
}
.entry {
background-color: $entry-color;
border: 1px solid darken($entry-color, 20);
header {
border-bottom: 1px solid darken($entry-color, 20);
.word-option-button {
background-color: darken($entry-color, 10);
border: 1px solid darken($entry-color, 20);
}
.word-option-list {
background-color: darken($entry-color, 5);
border: 1px solid darken($entry-color, 10);
.word-option {
&:hover {
background-color: lighten($entry-color, 5);
}
}
}
}
.edit-form {
.button {
background-color: darken($button-color, 10);
}
}
}
#messagingSection {
.message {
background-color: $message-color;
border: $border;
&.error {
background-color: lighten($red, 0.75);
}
.close-button {
&:before {
background-color: #455455;
}
}
}
}
#bottom {
background-color: $footer-color;
border-top: 1px solid darken($footer-color, 2);
box-shadow: 0px -4px 5px 0px rgba(50, 50, 50, 0.75);
a {
color: #000000;
}
}
}
@media (max-width: 750px) {
#defaultTheme {
$dark: #bababa;
$mid: #dedede;
$light: #efefef;
$white: #ffffff;
$red: #b42032;
$border: 1px solid $dark;
#mobileWordFormShow {
background-color: #00de00;
border: $border;
color: $white;
}
}
}

View File

@ -109,7 +109,9 @@
<section id="mainColumn">
<section id="detailsSection">
<h2 id="dictionaryName">Dictionary Name</h2>
<a id="dictionaryShare" href="./" class="button" title="Link to Dictionary" style="float:right;">&#10150;</a>
<h2 id="dictionaryName">{{dict_name}}</h2>
<h4>Created by {{public_name}}</h4>
<nav>
<ul>
<li>Description</li><li>Details</li><li>Stats</li><!-- li id="editDictionaryButton">Edit</li -->