Disable word entry when confirming edit.

Added separate "announcement" apart from notifications.
Added explanation for "Allow Emails".
Updated ReadMe.
This commit is contained in:
Robbie Antenesse 2015-12-16 18:13:45 -07:00
parent 6b7eebe9f3
commit 14f9942647
8 changed files with 116 additions and 15 deletions

View File

@ -28,9 +28,9 @@
<label><span>Public Name <span class="clickable" onclick="ExplainPublicName()" style="font-size:11px;vertical-align:top;background:#e0c19c;padding:4px 7px;">?</span></span> <label><span>Public Name <span class="clickable" onclick="ExplainPublicName()" style="font-size:11px;vertical-align:top;background:#e0c19c;padding:4px 7px;">?</span></span>
<input type="text" id="createAccountPublicNameField" name="publicname" /> <input type="text" id="createAccountPublicNameField" name="publicname" />
</label> </label>
<label><b>Allow Emails</b> <label style="display:inline;"><b>Allow Emails</b>
<input type="checkbox" id="createAccountAllowEmailsField" name="allowemails" checked="checked" /> <input type="checkbox" id="createAccountAllowEmailsField" name="allowemails" checked="checked" />
</label> </label> <span class="clickable" onclick="ExplainAllowEmails()" style="font-size:11px;vertical-align:top;background:#e0c19c;padding:4px 7px;">?</span>
<div id="createAccountError" style="font-weight:bold;color:red;"></div> <div id="createAccountError" style="font-weight:bold;color:red;"></div>
<button type="submit" id="createAccountSubmitButton" onclick="ValidateCreateAccount(); return false;">Create Account</button> <button type="submit" id="createAccountSubmitButton" onclick="ValidateCreateAccount(); return false;">Create Account</button>
</form></div> </form></div>

View File

@ -7,6 +7,26 @@ It accepts Unicode characters so you can utilize whatever typable characters you
If you would like an added layer of accessibility and security (in case you clear your browser cache frequently), you can create an account, where you can store and switch between as many dictionaries as you need. Having an account will also allow you to access your dictionaries from any browser by logging in. (Just be careful you don't overwrite dictionaries by logging in and saving from separate locations!) If you would like an added layer of accessibility and security (in case you clear your browser cache frequently), you can create an account, where you can store and switch between as many dictionaries as you need. Having an account will also allow you to access your dictionaries from any browser by logging in. (Just be careful you don't overwrite dictionaries by logging in and saving from separate locations!)
## Table of Contents
* [How do I use Lexiconga?](#how-do-i-use-lexiconga)
* [Getting Started](#getting-started)
* [Viewing your Dictionary's Description/Rules](#viewing-your-dictionary-s-description-rules)
* [Entry Management](#entry-management)
* [The Settings Menu](#the-settings-menu)
* [Search/Filter](#search-filter)
* [Accounts](#accounts)
* [Creating An Account](#creating-an-account)
* [Logging In](#logging-in)
* [Differences](#differences)
* [Account Settings](#account-settings)
* [Dictionary Settings](#dictionary-settings)
* [Forgot Your Password?](#forgot-your-password-)
* [Lockout](#lockout)
* [Problems or Requests](#problems-or-requests)
* [Future Plans](#future-plans)
* [Thanks](#thanks-)
* [Libraries Used](#libraries-used)
## How do I use Lexiconga? ## How do I use Lexiconga?
### Getting Started ### Getting Started
@ -51,6 +71,21 @@ The **Import Dictionary** form allows you to upload and view any previously-expo
The **Empty Current Dictionary** should only be used if you want to completely start over from scratch. It will ask you to confirm that you want to delete, and if you confirm, your dictionary will be gone forever. If you have not exported your dictionary before emptying it, there will be absolutely no way to get it back. Please be careful with this! The **Empty Current Dictionary** should only be used if you want to completely start over from scratch. It will ask you to confirm that you want to delete, and if you confirm, your dictionary will be gone forever. If you have not exported your dictionary before emptying it, there will be absolutely no way to get it back. Please be careful with this!
### Search/Filter
You can search entries or filter by part of speech by clicking the "Search/Filter Options" button to expand the search panel.
From there, you can enter any text you want in the search box and either press Enter or click anywhere outside the search box, and Lexiconga will display any and every entry including your entry. To display the entire dictionary again, you must clear the search box.
You can refine your search by using the checkboxes below the search box:
* **Word**: When checked, Lexiconga searches your dictionary's "Word" entries for the entered text. When unchecked, it ignores it.
* **Equivalent**: When checked, Lexiconga searches your dictionary's "Equivalent Word(s)" entries for the entered text. When unchecked, it ignores it.
* **Explanation**: When checked, Lexiconga searches your dictionary's "Explanation/Long Definition" entries for the entered text. When unchecked, it ignores it.
* **Search Case-Sensitive**: When checked, Lexiconga finds entries matching the letter case in the entered text. When unchecked, it will find any case as long as the letters match.
* **Ignore Diacritics/Accents**: When checked, Lexiconga will ignore accented letters and diacritics and identify them as their equivalent unaccented letter and vice-versa, in case you want to find a word with a diacritic without entering the diacritic in the search box. When unchecked, it will only find diacritics and accented letters if they are specifically entered in the search box.
The "Filter Words" drop-down box allows you to filter your dictionary by part of speech. To display the whole dictionary again after setting a filter, reset the filter option to "All".
## Accounts ## Accounts
If you are using an account with Lexiconga, your experience should remain essentially the same, but you will see some additional options in the Settings menu and you might notice some slight changes in performance as it saves to and loads from the database. If you are using an account with Lexiconga, your experience should remain essentially the same, but you will see some additional options in the Settings menu and you might notice some slight changes in performance as it saves to and loads from the database.
@ -63,6 +98,20 @@ To log in after creating an account, just click the "Log In/Create Account" butt
### Differences ### Differences
Every time you save a change to your dictionary's settings or add, edit, or delete a word, the changes are automatically saved to both your browser's localStorage in addition to being sent to your account. If you're paranoid that your changes are not being saved, you can check your browser's console log to see the little save and update notifications. Every time you save a change to your dictionary's settings or add, edit, or delete a word, the changes are automatically saved to both your browser's localStorage in addition to being sent to your account. If you're paranoid that your changes are not being saved, you can check your browser's console log to see the little save and update notifications.
#### Account Settings
After logging in, you'll see an "Account Settings" button in the top, right side of the Lexiconga window. Clicking this will allow you to change a few settings about your account:
The **Email** field allows you to specify a different login and contact email address. Make sure that you do not forget what you chose, because there is no way to retrieve your email address if you change it to something you forget!
The **Public Name** field allows you to change your public name.
The **Allow Emails** checkbox allows you to choose if you would like to receive emails about important Lexiconga updates. Make sure that you allow emails from addresses at lexicon.ga or check your spam folder just in case. Note that this checkbox does not affect password reset requests—if you forget your password, Lexiconga will send you a password reset email regardless of your choice here.
If you change any of the three options above, be sure you click the "Save Settings" button.
The "Reset Password" button in the "Reset Your Password" section will allow you to reset your login password. Don't forget it!
#### Dictionary Settings
Under the Settings menu, you'll see some additional options: Under the Settings menu, you'll see some additional options:
The **Change Dicitonaries** dropdown box contains the names of all of your created dictionaries. If you have more than one, selecting a different dictionary from the list will immediately download and display that dictionary. The **Change Dicitonaries** dropdown box contains the names of all of your created dictionaries. If you have more than one, selecting a different dictionary from the list will immediately download and display that dictionary.
@ -73,6 +122,9 @@ The **Import Dictionary** button acts the same as before, but instead of overwri
The **Delete Current Dictionary** button will permanently and irretrievably delete the currently loaded dictionary from your account! Be careful with that one. After deleting, you will then be prompted to either select another dictionary to load or create a new one, _or_ if you have no other dictionaries, immediately create a new one for you. The **Delete Current Dictionary** button will permanently and irretrievably delete the currently loaded dictionary from your account! Be careful with that one. After deleting, you will then be prompted to either select another dictionary to load or create a new one, _or_ if you have no other dictionaries, immediately create a new one for you.
### Forgot Your Password?
If you forget your password, you can request a password reset email by clicking the "Forgot Password" button on the "Log In/Create Account" button entering the email address associated with your account and clicking "Email Password Reset Key". This will send an email (_check your spam_) with a link that will allow you to reset your password. When you go to the link provided, you'll be able to enter a new password that you can log in with.
### Lockout ### Lockout
If you manage to enter your password wrong 10 times, you'll be locked out from logging in for an hour. Use this time to try to remember your password or something. You can get an idea of how long you've waited by refreshing the page and clicking the unfortunate "Can't Login" button. After an hour has passed, refresh the page again and you'll get another 10 tries. If you manage to enter your password wrong 10 times, you'll be locked out from logging in for an hour. Use this time to try to remember your password or something. You can get an idea of how long you've waited by refreshing the page and clicking the unfortunate "Can't Login" button. After an hour has passed, refresh the page again and you'll get another 10 tries.
@ -82,8 +134,6 @@ Please report any problems you come across to the [Dictionary Builder Issues pag
## Future Plans ## Future Plans
In the future, I'm planning to: In the future, I'm planning to:
* add the ability to easily share dictionaries by your Public Name, but all dictionaries will be private by default * add the ability to easily share dictionaries by your Public Name, but all dictionaries will be private by default
* enable editing your account settings such as your password and Public Name
* enable password reset if you forgot it
* enable account deletion if you lose trust or hope in Lexiconga's services * enable account deletion if you lose trust or hope in Lexiconga's services
* ad removal option? * ad removal option?
@ -92,7 +142,7 @@ I hope you enjoy Lexiconga and that it helps you build some awesome languages.
Robbie Antenesse Robbie Antenesse
### Libraries Used ## Libraries Used
* [Marked.js](https://github.com/chjj/marked) by Christopher Jeffrey (JJ) (a.k.a. chjj) * [Marked.js](https://github.com/chjj/marked) by Christopher Jeffrey (JJ) (a.k.a. chjj)
* [Defiant.js](http://defiantjs.com) by Hakan Bilgin (a.k.a. hbi99) * [Defiant.js](http://defiantjs.com) by Hakan Bilgin (a.k.a. hbi99)
* [removeDiacritics.js](http://stackoverflow.com/a/18391901/3508346) by [rdllopes](http://meta.stackoverflow.com/users/1879686/rdllopes) * [removeDiacritics.js](http://stackoverflow.com/a/18391901/3508346) by [rdllopes](http://meta.stackoverflow.com/users/1879686/rdllopes)

3
announcement.php Normal file
View File

@ -0,0 +1,3 @@
If you look in the top right corner of the page, you'll now see a new button.<br>
That's right, you can now create an account to store as many dictionaries as you could possibly need and access them all from wherever you want!<br>
Check the <span id="aboutButton" class="clickable" onclick="ShowInfo('aboutText')">About Lexiconga</span> button and look under "Accounts" for all the details!

View File

@ -140,6 +140,10 @@ input[type=checkbox] {
text-decoration: underline; text-decoration: underline;
} }
#createAccountSubmitButton, #accountSettingsSubmitButton {
margin-top: 10px;
}
#aboutButton { #aboutButton {
display: inline; display: inline;
margin: 0 10px 0 0; margin: 0 10px 0 0;

View File

@ -4,6 +4,7 @@ require_once('required.php');
session_start(); session_start();
$current_user = isset($_SESSION['user']) ? $_SESSION['user'] : 0; $current_user = isset($_SESSION['user']) ? $_SESSION['user'] : 0;
$announcement = get_include_contents(SITE_LOCATION . '/announcement.php');
$notificationMessage = ""; $notificationMessage = "";
if ($current_user > 0 || !isset($_SESSION['loginfailures']) || (isset($_SESSION['loginlockouttime']) && time() - $_SESSION['loginlockouttime'] >= 3600)) { if ($current_user > 0 || !isset($_SESSION['loginfailures']) || (isset($_SESSION['loginlockouttime']) && time() - $_SESSION['loginlockouttime'] >= 3600)) {
@ -47,9 +48,10 @@ require_once(SITE_LOCATION . '/php/notificationconditiontree.php');
</div> </div>
</header> </header>
<contents> <contents>
<div id="notificationArea" style="display:<?php echo (($notificationMessage) ? "block" : "none"); ?>;"> <div id="notificationArea" style="display:<?php echo (($announcement || $notificationMessage) ? "block" : "none"); ?>;">
<span id="notificationCloseButton" class="clickable" onclick="document.getElementById('notificationArea').style.display='none';">Close</span> <span id="notificationCloseButton" class="clickable" onclick="document.getElementById('notificationArea').style.display='none';">Close</span>
<div id="notificationMessage"><?php echo $notificationMessage; ?></div> <div id="notificationMessage"><?php echo $notificationMessage; ?></div>
<div id="announcement" style="margin-top:<?php echo (($announcement && $notificationMessage) ? "15px" : "0"); ?>;"><?php echo $announcement; ?></div>
</div> </div>
<div id="leftColumn"> <div id="leftColumn">
<form id="wordEntryForm"> <form id="wordEntryForm">
@ -216,9 +218,9 @@ require_once(SITE_LOCATION . '/php/notificationconditiontree.php');
<label><span>Public Name <span class="clickable" onclick="ExplainPublicName()" style="font-size:11px;vertical-align:top;background:#e0c19c;padding:4px 7px;">?</span></span> <label><span>Public Name <span class="clickable" onclick="ExplainPublicName()" style="font-size:11px;vertical-align:top;background:#e0c19c;padding:4px 7px;">?</span></span>
<input type="text" id="accountSettingsPublicNameField" name="publicname" value="<?php echo Get_Public_Name_By_Id($current_user); ?>" /> <input type="text" id="accountSettingsPublicNameField" name="publicname" value="<?php echo Get_Public_Name_By_Id($current_user); ?>" />
</label> </label>
<label><b>Allow Emails</b> <label style="display:inline;"><b>Allow Emails</b>
<input type="checkbox" id="accountSettingsAllowEmailsField" name="allowemails" <?php if (Get_Allow_Email_By_Id($current_user) == 1) echo 'checked="checked"'; ?> /> <input type="checkbox" id="createAccountAllowEmailsField" name="allowemails" checked="checked" />
</label> </label> <span class="clickable" onclick="ExplainAllowEmails()" style="font-size:11px;vertical-align:top;background:#e0c19c;padding:4px 7px;">?</span>
<div id="accountSettingsError" style="font-weight:bold;color:red;"></div> <div id="accountSettingsError" style="font-weight:bold;color:red;"></div>
<button type="submit" id="accountSettingsSubmitButton" onclick="ValidateAccountSettings(); return false;">Save Settings</button> <button type="submit" id="accountSettingsSubmitButton" onclick="ValidateAccountSettings(); return false;">Save Settings</button>
<br><br> <br><br>

View File

@ -43,12 +43,14 @@ function AddWord() {
if (editIndex != "") { if (editIndex != "") {
if (WordAtIndexWasChanged(editIndex, word, pronunciation, partOfSpeech, simpleDefinition, longDefinition)) { if (WordAtIndexWasChanged(editIndex, word, pronunciation, partOfSpeech, simpleDefinition, longDefinition)) {
document.getElementById("editWordButtonArea").style.display = "none";
DisableForm();
updateConflictArea.style.display = "block"; updateConflictArea.style.display = "block";
updateConflictArea.innerHTML = "<span id='updateConflictMessage'>Do you really want to change the word \"" + currentDictionary.words[parseInt(editIndex)].name + "\" to what you have set above?</span>"; updateConflictArea.innerHTML = "<span id='updateConflictMessage'>Do you really want to change the word \"" + currentDictionary.words[parseInt(editIndex)].name + "\" to what you have set above?</span>";
updateConflictArea.innerHTML += '<button type="button" id="updateConfirmButton" \ updateConflictArea.innerHTML += '<button type="button" id="updateConfirmButton" \
onclick="UpdateWord(' + editIndex + ', \'' + htmlEntities(word) + '\', \'' + htmlEntities(pronunciation) + '\', \'' + htmlEntities(partOfSpeech) + '\', \'' + htmlEntities(simpleDefinition) + '\', \'' + htmlEntities(longDefinition) + '\'); \ onclick="UpdateWord(' + editIndex + ', \'' + htmlEntities(word) + '\', \'' + htmlEntities(pronunciation) + '\', \'' + htmlEntities(partOfSpeech) + '\', \'' + htmlEntities(simpleDefinition) + '\', \'' + htmlEntities(longDefinition) + '\'); \
return false;">Yes, Update it</button>'; return false;">Yes, Update it</button>';
updateConflictArea.innerHTML += '<button type="button" id="updateCancelButton" onclick="CloseUpdateConflictArea(); return false;">No, Leave it</button>'; updateConflictArea.innerHTML += '<button type="button" id="updateCancelButton" onclick="CloseUpdateConflictArea(\'editWordButtonArea\'); return false;">No, Leave it</button>';
} else { } else {
errorMessage = "No change has been made to \"" + word + "\""; errorMessage = "No change has been made to \"" + word + "\"";
if (currentDictionary.words[parseInt(editIndex)].name != word) { if (currentDictionary.words[parseInt(editIndex)].name != word) {
@ -57,6 +59,8 @@ function AddWord() {
} }
} else if (wordIndex >= 0) { } else if (wordIndex >= 0) {
if (WordAtIndexWasChanged(wordIndex, word, pronunciation, partOfSpeech, simpleDefinition, longDefinition)) { if (WordAtIndexWasChanged(wordIndex, word, pronunciation, partOfSpeech, simpleDefinition, longDefinition)) {
document.getElementById("newWordButtonArea").style.display = "none";
DisableForm();
updateConflictArea.style.display = "block"; updateConflictArea.style.display = "block";
var updateConflictText = "<span id='updateConflictMessage'>\"" + word + "\" is already in the dictionary"; var updateConflictText = "<span id='updateConflictMessage'>\"" + word + "\" is already in the dictionary";
@ -69,7 +73,7 @@ function AddWord() {
updateConflictText += '<button type="button" id="updateConfirmButton" \ updateConflictText += '<button type="button" id="updateConfirmButton" \
onclick="UpdateWord(' + wordIndex + ', \'' + htmlEntities(word) + '\', \'' + htmlEntities(pronunciation) + '\', \'' + htmlEntities(partOfSpeech) + '\', \'' + htmlEntities(simpleDefinition) + '\', \'' + htmlEntities(longDefinition) + '\'); \ onclick="UpdateWord(' + wordIndex + ', \'' + htmlEntities(word) + '\', \'' + htmlEntities(pronunciation) + '\', \'' + htmlEntities(partOfSpeech) + '\', \'' + htmlEntities(simpleDefinition) + '\', \'' + htmlEntities(longDefinition) + '\'); \
return false;">Yes, Update it</button>'; return false;">Yes, Update it</button>';
updateConflictText += ' <button type="button" id="updateCancelButton" onclick="CloseUpdateConflictArea(); return false;">No, Leave it</button>'; updateConflictText += ' <button type="button" id="updateCancelButton" onclick="CloseUpdateConflictArea(\'newWordButtonArea\'); return false;">No, Leave it</button>';
updateConflictArea.innerHTML = updateConflictText; updateConflictArea.innerHTML = updateConflictText;
} else { } else {
@ -148,7 +152,7 @@ function SaveAndUpdateDictionary(keepFormContents) {
if (!keepFormContents) { if (!keepFormContents) {
ClearForm(); ClearForm();
} }
CloseUpdateConflictArea(); CloseUpdateConflictArea('newWordButtonArea');
} }
function UpdateWord(wordIndex, word, pronunciation, partOfSpeech, simpleDefinition, longDefinition) { function UpdateWord(wordIndex, word, pronunciation, partOfSpeech, simpleDefinition, longDefinition) {
@ -560,7 +564,7 @@ function ProcessLoad() {
HideSettingsWhenComplete(); HideSettingsWhenComplete();
ShowDictionary(); ShowDictionary();
SetPartsOfSpeech(); SetPartsOfSpeech();
if (currentDictionary.settings.isComplete) { if (currentDictionary.settings.isComplete) {

View File

@ -209,8 +209,35 @@ function ExplainPublicName() {
alert("This is the name we greet you with. It's also the name displayed if you ever decide to share any of your dictionaries.\n\nNote: this is not a username, and as such is not guaranteed to be unique. Use something people will recognize you as to differentiate from other people who might use the same name!"); alert("This is the name we greet you with. It's also the name displayed if you ever decide to share any of your dictionaries.\n\nNote: this is not a username, and as such is not guaranteed to be unique. Use something people will recognize you as to differentiate from other people who might use the same name!");
} }
function CloseUpdateConflictArea() { function ExplainAllowEmails() {
alert("We'll make sure that you're the first to hear about any new features that get added or if any of our policies change for any reason. We'll never spam you or sell your information, but you may need to mark emails from lexicon.ga as not spam to receive them.\nNOTE: Password reset emails will be sent regardless of your choice.");
}
function CloseUpdateConflictArea(displayId) {
displayId = (typeof displayId !== 'undefined' && displayId != null) ? displayId : false;
if (displayId != false) {
document.getElementById(displayId).style.display = "block";
}
document.getElementById("updateConflict").style.display = "none"; document.getElementById("updateConflict").style.display = "none";
EnableForm();
}
function DisableForm() {
document.getElementById("word").disabled = true;
document.getElementById("pronunciation").disabled = true;
document.getElementById("partOfSpeech").disabled = true;
document.getElementById("simpleDefinition").disabled = true;
document.getElementById("longDefinition").disabled = true;
document.getElementById("editIndex").disabled = true;
}
function EnableForm() {
document.getElementById("word").disabled = false;
document.getElementById("pronunciation").disabled = false;
document.getElementById("partOfSpeech").disabled = false;
document.getElementById("simpleDefinition").disabled = false;
document.getElementById("longDefinition").disabled = false;
document.getElementById("editIndex").disabled = false;
} }
function ClearForm() { function ClearForm() {
@ -225,6 +252,7 @@ function ClearForm() {
document.getElementById("editWordButtonArea").style.display = "none"; document.getElementById("editWordButtonArea").style.display = "none";
document.getElementById("errorMessage").innerHTML = ""; document.getElementById("errorMessage").innerHTML = "";
document.getElementById("updateConflict").style.display = "none"; document.getElementById("updateConflict").style.display = "none";
EnableForm();
} }
function ToggleDescription() { function ToggleDescription() {
@ -276,7 +304,7 @@ function ShowAccountSettings(variableName) {
} }
function HideAccountSettings() { function HideAccountSettings() {
If (document.getElementById("accountSettingsScreen")) if (document.getElementById("accountSettingsScreen"))
document.getElementById("accountSettingsScreen").style.display = "none"; document.getElementById("accountSettingsScreen").style.display = "none";
} }
@ -318,6 +346,8 @@ function HideSettingsWhenComplete() {
function SetPartsOfSpeech () { function SetPartsOfSpeech () {
var partsOfSpeechSelect = document.getElementById("partOfSpeech"); var partsOfSpeechSelect = document.getElementById("partOfSpeech");
var wordFilterSelect = document.getElementById("wordFilter"); var wordFilterSelect = document.getElementById("wordFilter");
var selectedWordFilter = wordFilterSelect.value;
var selectedWordStillExists = false;
if (partsOfSpeechSelect.options.length > 0) { if (partsOfSpeechSelect.options.length > 0) {
for (var i = partsOfSpeechSelect.options.length - 1; i >= 0; i--) { for (var i = partsOfSpeechSelect.options.length - 1; i >= 0; i--) {
partsOfSpeechSelect.removeChild(partsOfSpeechSelect.options[i]); partsOfSpeechSelect.removeChild(partsOfSpeechSelect.options[i]);
@ -335,6 +365,14 @@ function SetPartsOfSpeech () {
wordFilterOption.appendChild(document.createTextNode(newPartsOfSpeech[j].trim())); wordFilterOption.appendChild(document.createTextNode(newPartsOfSpeech[j].trim()));
wordFilterOption.value = newPartsOfSpeech[j].trim(); wordFilterOption.value = newPartsOfSpeech[j].trim();
wordFilterSelect.appendChild(wordFilterOption); wordFilterSelect.appendChild(wordFilterOption);
if (!selectedWordStillExists && newPartsOfSpeech[j].trim() == selectedWordFilter) {
selectedWordStillExists = true;
}
}
if (selectedWordStillExists) {
wordFilterSelect.value = selectedWordFilter;
} }
} }

View File