commit 859e3cddd69a95da56e49e4117a3eead5dffef9b
parent 12e29627985d85f38b831686c20ec45244618204
Author: AsherMorgan <59518073+AsherMorgan@users.noreply.github.com>
Date: Fri, 4 Sep 2020 19:34:34 -0700
Merge pull request #17 from AsherMorgan/vue
Refactor Spanish-Quizzer to use Vue.js.
Diffstat:
| M | Offline/index.html | | | 84 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
| M | Scripts/Home.js | | | 290 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
| A | Scripts/Offline.js | | | 49 | +++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | Scripts/Quizzer.js | | | 123 | ++++++++++++++++++++++++++++++++----------------------------------------------- |
| M | Scripts/Reference.js | | | 131 | +++++++++++++++++++++++++++++++------------------------------------------------ |
| M | Scripts/Settings.js | | | 346 | ++++++++++++++++++------------------------------------------------------------- |
| M | Styles/Global.css | | | 5 | +++++ |
| M | Styles/Reference.css | | | 2 | +- |
| M | index.html | | | 326 | ++++++++++++++++++++++++++++++++++++++----------------------------------------- |
| M | reference.html | | | 120 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
10 files changed, 695 insertions(+), 781 deletions(-)
diff --git a/Offline/index.html b/Offline/index.html
@@ -9,48 +9,52 @@
<link rel="apple-touch-icon" sizes="180x180" href="../Images/apple-touch-icon.png">
<link rel="stylesheet" href="../Styles/Global.css">
<link rel="stylesheet" href="../Styles/Offline.css">
+ <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
+ <script src="../Scripts/Offline.js"></script>
</head>
- <body>
- <header onclick="window.location='../'">Spanish-Quizzer</header>
+ <body onload="Load();">
+ <div id="app">
+ <header onclick="window.location='../'">Spanish-Quizzer</header>
- <main>
- <h1>How to Study Offline</h1>
-
- <div id="instructions">
- <h2>Safari (iOS)</h2>
- <ol>
- <li>Go to the <a href="../">Spanish-Quizzer homepage</a>.</li>
- <li>Tap on either Conjugations, Vocab, or the Reference Tables.</li>
- <li>Tap the share icon and select "Add to Reading List".</li>
- <li>Open the Settings app and select "Safari".</li>
- <li>Scroll down to the "Reading List" section and enable "Automatically Save Offline".</li>
- <li>When you are offline, open Safari, go to your Reading List, and tap on Spanish-Quizzer.</li>
- </ol>
- <h2>Firefox</h2>
- <ol>
- <li>Go to the <a href="../">Spanish-Quizzer homepage</a>.</li>
- <li>Click on either Conjugations, Vocab, or the Reference Tables.</li>
- <li>When you are offline, click on the hamburger menu in the top right corner, click on "Web Developer", and enable "Work Offline".</li>
- <li>Return to the page you clicked on to study offline.</li>
- <li>When you get back online, you must disable "Work Offline" in order to browse the internet again.</li>
- </ol>
- <h2>Chrome / Edge</h2>
- <p>Offline studying is not supported on Google Chrome or Microsoft Edge. Spanish-Quizzer will not work if it is saved as an HTML document.</p>
- </div>
- </main>
-
- <footer>
- <div id="share" hidden>
- <a href="mailto:?Subject=Spanish-Quizzer&Body=https://ashermorgan.github.io/Spanish-Quizzer">Email</a>
- <a href="http://www.facebook.com/sharer.php?u=https://ashermorgan.github.io/Spanish-Quizzer" target="_blank">Facebook</a>
- <a href="https://twitter.com/share?url=https://ashermorgan.github.io/Spanish-Quizzer&text=Spanish-Quizzer" target="_blank">Twitter</a>
- <a href="http://reddit.com/submit?url=https://ashermorgan.github.io/Spanish-Quizzer&title=Spanish-Quizzer" target="_blank">Reddit</a>
- <a id="shareSMS" href="sms:&body=https://ashermorgan.github.io/Spanish-Quizzer" hidden>SMS</a>
- </div>
- <a href="../">Home</a>
- <a href="../reference.html">Reference Tables</a>
- <a href="javascript:document.getElementById('share').hidden = false;">Share</a>
- </footer>
+ <main>
+ <h1>How to Study Offline</h1>
+
+ <div id="instructions">
+ <h2>Safari (iOS)</h2>
+ <ol>
+ <li>Go to the <a href="../">Spanish-Quizzer homepage</a>.</li>
+ <li>Tap on either Conjugations, Vocab, or the Reference Tables.</li>
+ <li>Tap the share icon and select "Add to Reading List".</li>
+ <li>Open the Settings app and select "Safari".</li>
+ <li>Scroll down to the "Reading List" section and enable "Automatically Save Offline".</li>
+ <li>When you are offline, open Safari, go to your Reading List, and tap on Spanish-Quizzer.</li>
+ </ol>
+ <h2>Firefox</h2>
+ <ol>
+ <li>Go to the <a href="../">Spanish-Quizzer homepage</a>.</li>
+ <li>Click on either Conjugations, Vocab, or the Reference Tables.</li>
+ <li>When you are offline, click on the hamburger menu in the top right corner, click on "Web Developer", and enable "Work Offline".</li>
+ <li>Return to the page you clicked on to study offline.</li>
+ <li>When you get back online, you must disable "Work Offline" in order to browse the internet again.</li>
+ </ol>
+ <h2>Chrome / Edge</h2>
+ <p>Offline studying is not supported on Google Chrome or Microsoft Edge. Spanish-Quizzer will not work if it is saved as an HTML document.</p>
+ </div>
+ </main>
+
+ <footer>
+ <div id="share" hidden>
+ <a href="mailto:?Subject=Spanish-Quizzer&Body=https://ashermorgan.github.io/Spanish-Quizzer">Email</a>
+ <a href="http://www.facebook.com/sharer.php?u=https://ashermorgan.github.io/Spanish-Quizzer" target="_blank">Facebook</a>
+ <a href="https://twitter.com/share?url=https://ashermorgan.github.io/Spanish-Quizzer&text=Spanish-Quizzer" target="_blank">Twitter</a>
+ <a href="http://reddit.com/submit?url=https://ashermorgan.github.io/Spanish-Quizzer&title=Spanish-Quizzer" target="_blank">Reddit</a>
+ <a id="shareSMS" href="sms:&body=https://ashermorgan.github.io/Spanish-Quizzer" hidden>SMS</a>
+ </div>
+ <a href="../">Home</a>
+ <a href="../reference.html">Reference Tables</a>
+ <a href="javascript:document.getElementById('share').hidden = false; void 0;">Share</a>
+ </footer>
+ </div>
</body>
</html>
diff --git a/Scripts/Home.js b/Scripts/Home.js
@@ -1,54 +1,206 @@
// Declare global variables
let Sets; // List of parsed sets
let quizzerType = null; // Type of quizzer
+let app;
// Load the document
function Load() {
+ // Initialize Vue
+ app = new Vue({
+ el: "#app", // Mount to app div
+
+ data: {
+ state: "home",
+ darkTheme: false,
+ verbFilters: [],
+ vocabFilters: [],
+ promptType: localStorage.getItem("promptType") || "Text",
+ inputType: localStorage.getItem("inputType") || "Text",
+ repeatPrompts: localStorage.getItem("repeatPrompts") || "Never",
+
+ prompts: [],
+ promptIndex: 0,
+ responce: "",
+ responceActive: true,
+ },
+
+ methods: {
+ Back: function() {
+ switch (app.state) {
+ case "verbQuizzer":
+ app.state = "verbSettings";
+ break;
+ case "vocabQuizzer":
+ app.state = "vocabSettings";
+ break;
+ case "verbSettings":
+ case "vocabSettings":
+ case "home":
+ default:
+ app.state = "home";
+ break;
+ }
+ },
+ AddVerbFilter: function() {
+ this.verbFilters.push({"tense":"All Tenses", "type":"All Types"});
+ },
+ RemoveVerbFilter: function(index) {
+ // Remove filter
+ this.verbFilters.splice(index, 1);
+ },
+ AddVocabFilter: function() {
+ this.vocabFilters.push({"set":"Verbs", "type":"All Definitions"});
+ },
+ RemoveVocabFilter: function(index) {
+ // Remove filter
+ this.vocabFilters.splice(index, 1);
+ },
+ getTenseTypes: function(index) {
+ // Get filter options
+ let filters = {"All Types":true, "Reflexive":true, "Regular":true, "Nonregular":true, "Stem Changing":true, "Orthographic":true, "Irregular":true}
+ switch(this.verbFilters[index].tense)
+ {
+ case "All Tenses":
+ break;
+ case "Present Participles":
+ filters["Reflexive"] = false; // Reflexive
+ filters["Orthographic"] = false; // Orthographic
+ break;
+ case "Present Tense":
+ filters["Orthographic"] = false; // Orthographic
+ break;
+ case "Preterite Tense":
+ break;
+ case "Imperfect Tense":
+ filters["Stem Changing"] = false; // Stem Changing
+ filters["Orthographic"] = false; // Orthographic
+ break;
+ }
+
+ // Reset type if needed
+ if (!filters[this.verbFilters[index].type]) {
+ this.verbFilters[index].type = "All Types";
+ }
+
+ // Return filters
+ return filters;
+ },
+ getSetFilters: function(index) {
+ // Get filter options
+ var filters = [];
+ switch(this.vocabFilters[index].set)
+ {
+ case "Verbs":
+ filters = ["All Definitions", "Spanish Infinitives", "English Infinitives", "Reverse Conjugations"];
+ break;
+
+ case "Adjectives":
+ case "Adverbs":
+ case "Prepositions":
+ case "Transitions":
+ case "Colors":
+ case "Days":
+ case "Months":
+ case "Questions":
+ filters = ["All Definitions", "English to Spanish", "Spanish to English"];
+ break;
+
+ case "Weather":
+ case "Professions":
+ filters = ["All Definitions", "English to Spanish", "Spanish to English",
+ "Nouns", "Verbs"];
+ break;
+
+ case "Family":
+ case "Clothes":
+ filters = ["All Definitions", "English to Spanish", "Spanish to English",
+ "Nouns", "Adjectives"];
+ break;
+
+ case "Nature":
+ case "House":
+ case "Vacation":
+ case "Childhood":
+ case "Health":
+ filters = ["All Definitions", "English to Spanish", "Spanish to English",
+ "Nouns", "Verbs", "Adjectives"];
+ break;
+ }
+
+ // Reset type if needed
+ if (!filters.includes(this.vocabFilters[index].type)) {
+ this.vocabFilters[index].type = filters[0];
+ }
+
+ // Return filters
+ return filters;
+ }
+ },
+
+ watch: {
+ darkTheme: function() {
+ // Get theme from localStorage if null
+ if (this.darkTheme === null) {
+ this.darkTheme = JSON.parse(localStorage.getItem("darkTheme"));
+ }
+
+ // Detect preferred color scheme if null
+ if (this.darkTheme === null) {
+ this.darkTheme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
+ }
+
+ // Apply theme
+ if (this.darkTheme) {
+ document.body.classList.add("dark");
+ }
+ else {
+ document.body.classList.remove("dark");
+ }
+
+ // Save theme
+ localStorage.setItem("darkTheme", this.darkTheme);
+ },
+ promptType: function(value) {
+ localStorage.setItem("promptType", value);
+ },
+ inputType: function(value) {
+ localStorage.setItem("inputType", value);
+ },
+ repeatPrompts: function(value) {
+ localStorage.setItem("repeatPrompts", value);
+ }
+ },
+
+ computed: {
+ prompt: function() {
+ if (this.promptIndex < this.prompts.length) {
+ return this.prompts[this.promptIndex];
+ }
+ else {
+ return ["", "", "", ""];
+ }
+ }
+ }
+ });
+
+ // Unhide hidden divs
+ // Divs were hidden to improve interface for users with JS blocked
+ document.getElementById("home").hidden = false;
+ document.getElementById("settings").hidden = false;
+ document.getElementById("quizzer").hidden = false;
+ document.querySelector("footer").hidden = false;
+
+
// Load settings
- if (localStorage.getItem("darkMode") == "true") {
- document.body.classList.toggle("dark");
- document.getElementById("settingsDarkMode").checked = true;
- }
- if (localStorage.getItem("PromptType")) {
- document.getElementById("settingsPromptType").value = localStorage.getItem("PromptType");
- }
- if (localStorage.getItem("InputType")) {
- document.getElementById("settingsInputType").value = localStorage.getItem("InputType");
- }
- if (localStorage.getItem("repeatPrompt")) {
- document.getElementById("settingsRepeatPrompts").value = localStorage.getItem("repeatPrompt");
- }
+ app.darkTheme = null; // Force theme to update
// Add event Listeners
document.addEventListener("click", function (e) {
document.getElementById('share').hidden = true;
});
document.addEventListener("keydown", KeyDown);
- document.getElementById("quizzerInput").addEventListener("keydown", function (e) {
- if (e.ctrlKey && e.keyCode === 13) {
- // Key was Ctrl+Enter
- Reset(); // Skip prompt
- }
- else if (e.keyCode === 13) {
- // Key was Enter
- if (document.getElementById("quizzerInput").readOnly) {
- Continue();
- }
- else {
- Submit();
- }
- }
- });
- document.getElementById("quizzerEnter").addEventListener("click", function (e) {
- if (document.getElementById("quizzerInput").readOnly) {
- Continue();
- }
- else {
- Submit();
- }
- });
// Load CSVs
Sets = [];
@@ -68,73 +220,19 @@ function Load() {
-// Shows specific groups of elements
-function Show(div) {
- // Hide all elements
- document.getElementById("home").hidden = true;
- document.getElementById("settings").hidden = true;
- document.getElementById("verbSettings").hidden = true;
- document.getElementById("vocabSettings").hidden = true;
- document.getElementById("quizzerSettings").hidden = true;
- document.getElementById("quizzer").hidden = true;
-
- // Reset settings error message
- document.getElementById("settingsError").textContent = "";
-
- // Show elements
- switch (div) {
- default:
- case "home":
- document.getElementById("home").hidden = false;
- quizzerType = null;
- break;
- case "vocab":
- document.getElementById("settings").hidden = false;
- document.getElementById("vocabSettings").hidden = false;
- document.getElementById("quizzerSettings").hidden = false;
- quizzerType = "vocab";
- break;
- case "verbs":
- document.getElementById("settings").hidden = false;
- document.getElementById("verbSettings").hidden = false;
- document.getElementById("quizzerSettings").hidden = false;
- quizzerType = "verbs";
- break;
- case "quizzer":
- document.getElementById("quizzer").hidden = false;
- break;
- }
-}
-
-
-
-// Controls navigation when user clicks on title
-function TitleClicked() {
- if (!document.getElementById("quizzer").hidden) {
- // Go to settings screen
- Show(quizzerType);
- }
- else {
- // Go to home screen
- Show("home");
- }
-}
-
-
-
-// Handles keyDown events (implements keyboard shortcuts)
+// Handles keyDown events (implements some keyboard shortcuts)
function KeyDown(e) {
if (e.key === "Escape") {
- TitleClicked();
+ app.Back();
}
// Home shortcuts
- if (document.getElementById("home").hidden == false) {
+ if (app.state === "home") {
if (e.key === "c") {
- Show("verbs");
+ app.state = "verbSettings";
}
if (e.key === "v") {
- Show("vocab");
+ app.state = "vocabSettings";
}
if (e.key === "r") {
window.location = "/reference.html";
@@ -142,7 +240,7 @@ function KeyDown(e) {
}
// Settings shortcuts
- if (document.getElementById("settings").hidden == false) {
+ if (app.state === "verbSettings" || app.state === "vocabSettings") {
if (e.key === "s") {
CreateSession();
}
diff --git a/Scripts/Offline.js b/Scripts/Offline.js
@@ -0,0 +1,49 @@
+// Declare global variables
+let app;
+
+
+
+// Load the document
+function Load() {
+ // Initialize Vue
+ app = new Vue({
+ el: "#app", // Mount to app div
+
+ data: {
+ darkTheme: false
+ },
+
+ watch: {
+ darkTheme: function() {
+ // Get theme from localStorage if null
+ if (this.darkTheme === null) {
+ this.darkTheme = JSON.parse(localStorage.getItem("darkTheme"));
+ }
+
+ // Detect preferred color scheme if null
+ if (this.darkTheme === null) {
+ this.darkTheme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
+ }
+
+ // Apply theme
+ if (this.darkTheme) {
+ document.body.classList.add("dark");
+ }
+ else {
+ document.body.classList.remove("dark");
+ }
+
+ // Save theme
+ localStorage.setItem("darkTheme", this.darkTheme);
+ }
+ }
+ });
+
+ // Load settings
+ app.darkTheme = null; // Force theme to update
+
+ // Add event Listeners
+ document.addEventListener("click", function (e) {
+ document.getElementById('share').hidden = true;
+ });
+}
diff --git a/Scripts/Quizzer.js b/Scripts/Quizzer.js
@@ -1,7 +1,5 @@
// Declare global variables
-let Terms; // List of prompts
-let Term; // Index of current prompt
-let Settings = {}; // Dictionary of quizzer settings
+let Prefix; // Dictionary of quizzer settings
@@ -46,25 +44,21 @@ function Shuffle(items) {
// Starts the quizzer
-function StartQuizzer(terms, term, prefix, inputType, promptType, repeatPrompts) {
+function StartQuizzer(prefix) {
// Set variables and settings
- Terms = terms;
- Term = term - 1;
- Settings["Prefix"] = prefix;
- Settings["InputType"] = inputType;
- Settings["PromptType"] = promptType;
- Settings["RepeatPrompts"] = repeatPrompts;
+ app.promptIndex--;
+ Prefix = prefix;
// Validate Terms
- if (!Terms || isNaN(Term) || Term < -1 || Term > Terms.length) {
+ if (!app.prompts || isNaN(app.promptIndex) || app.promptIndex < -1 || app.promptIndex > app.prompts.length) {
throw "Bad arguments.";
}
- else if (Terms.length == 0) {
+ else if (app.prompts.length == 0) {
throw "Terms is empty.";
}
// Validate browser for voice input
- if (Settings["InputType"] != "Text") {
+ if (app.inputType != "Text") {
if (typeof InstallTrigger !== "undefined") {
// Browser is Firefox
alert("You must enable speech recognition in about:config.");
@@ -77,10 +71,10 @@ function StartQuizzer(terms, term, prefix, inputType, promptType, repeatPrompts)
}
// Save terms to local storage
- localStorage.setItem(Settings["Prefix"] + "terms", JSON.stringify(Terms));
+ localStorage.setItem(Prefix + "prompts", JSON.stringify(app.prompts));
// Give iOS devices ringer warning for prompt audio
- if (Settings["PromptType"] != "Text") {
+ if (app.promptType != "Text") {
if (!!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)) {
alert("Please make sure your ringer is on in order to hear audio prompts.");
}
@@ -95,65 +89,40 @@ function StartQuizzer(terms, term, prefix, inputType, promptType, repeatPrompts)
// Give the user a new prompt
function Reset() {
// Show and hide elements
- document.getElementById("quizzerEnter").textContent = "Submit";
- document.getElementById("quizzerEnter").disabled = false;
- document.getElementById("quizzerFeedback").hidden = true;
document.getElementById("quizzerCongrats").hidden = true;
- document.getElementById("quizzerInput").readOnly = false;
document.getElementById("quizzerInput").focus();
+ app.responceActive = true;
- // Get prompt
- Term++;
- if (Term == Terms.length) {
+ // Get new prompt
+ app.promptIndex++;
+ if (app.promptIndex == app.prompts.length) {
// The user just finished
- Terms = Shuffle(Terms);
- Term = 0;
-
- // Congradulate user
- document.getElementById("quizzerCongrats").textContent = "Congratulations! You made it back to the beginning!";
+ app.prompts = Shuffle(app.prompts);
+ app.promptIndex = 0;
document.getElementById("quizzerCongrats").hidden = false;
}
// Save progress to local storage
- localStorage.setItem(Settings["Prefix"] + "term", Term);
-
- // Update progress
- document.getElementById("quizzerProgress").textContent = `${Term} / ${Terms.length}`;
-
- // Set prompt
- document.getElementById("quizzerPromptType").textContent = `${Terms[Term][0]}: `;
- if (Settings["PromptType"] != "Audio") {
- document.getElementById("quizzerPrompt").textContent = Terms[Term][1];
- }
- else {
- document.getElementById("quizzerPrompt").textContent = "Click to hear again";
- }
- document.getElementById("quizzerInputType").textContent = `${Terms[Term][2]}: `;
+ localStorage.setItem(Prefix + "prompt", app.promptIndex);
// Reset responce
- document.getElementById("quizzerInput").value = "";
+ app.responce = "";
// Read prompt
- if (Settings["PromptType"] != "Text") {
- Read(Terms[Term][1], Terms[Term][0]);
- }
-
- // Disable textbox and submit button
- if (Settings["InputType"] == "Voice") {
- document.getElementById("quizzerInput").readOnly = true;
- document.getElementById("quizzerEnter").disabled = true;
+ if (app.promptType != "Text") {
+ Read(app.prompt[1], app.prompt[0]);
}
// Get voice input
- if (Settings["InputType"] != "Text") {
+ if (app.inputType != "Text") {
// Create recognition object
var recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.msSpeechRecognition)();
// Set language
- if (Terms[Term][2].toLowerCase().includes("english")) {
+ if (app.prompt[2].toLowerCase().includes("english")) {
recognition.lang = 'en-US';
}
- else if (Terms[Term][2].toLowerCase().includes("spanish")) {
+ else if (app.prompt[2].toLowerCase().includes("spanish")) {
recognition.lang = 'es-mx';
}
@@ -170,7 +139,7 @@ function Reset() {
responce += `${result.transcript}, `;
responce += `${result.transcript.split(" or ").join(", ")}, `;
}
- document.getElementById("quizzerInput").value = responce;
+ app.responce = responce;
Submit()
};
}
@@ -181,7 +150,7 @@ function Reset() {
// Processes a user's submitted responce
function Submit() {
// Parse responce
- var responce = document.getElementById("quizzerInput").value.toLowerCase(); // Make responce lowercase
+ var responce = app.responce.toLowerCase(); // Make responce lowercase
responce = responce.replace(/a`/g, "á"); // Apply accented a shortcut
responce = responce.replace(/e`/g, "é"); // Apply accented e shortcut
responce = responce.replace(/i`/g, "í"); // Apply accented i shortcut
@@ -196,7 +165,7 @@ function Submit() {
}
// Parse answer
- answers = Terms[Term][3].toLowerCase().split(","); // Split string by commas
+ answers = app.prompt[3].toLowerCase().split(","); // Split string by commas
for (var i = 0; i < answers.length; i++) {
answers[i] = answers[i].trim(); // Trim whitespace
}
@@ -211,17 +180,11 @@ function Submit() {
// Give user feedback
if (!correct) {
- // Responce was incorrect
- document.getElementById("quizzerFeedbackTerm").textContent = Terms[Term][3].toLowerCase();
-
// Show and hide elements
- document.getElementById("quizzerInput").readOnly = true;
- document.getElementById("quizzerEnter").textContent = "Continue";
- document.getElementById("quizzerEnter").disabled = false;
- document.getElementById("quizzerFeedback").hidden = false;
document.getElementById("quizzerCongrats").hidden = true;
document.getElementById("quizzerFeedback").scrollIntoView(false);
document.getElementById("quizzerInput").focus();
+ app.responceActive = false;
}
else {
// Responce was correct
@@ -234,34 +197,46 @@ function Submit() {
// Processes an incorrect responce and then resets the quizzer
function Continue() {
// Repeat prompt
- switch (Settings["RepeatPrompts"])
+ switch (app.repeatPrompts)
{
case "Never":
// Don't repeat
break;
case "Immediately":
// Repeat imitiately
- Term--;
+ app.promptIndex--;
break;
case "5 prompts later":
// Repeat 5 prompts later
- var temp = Terms[Term];
- Terms.splice(Term, 1);
- Terms.splice(Term + 5, 0, temp);
- Term--;
+ var temp = app.prompt;
+ app.prompts.splice(app.promptIndex, 1);
+ app.prompts.splice(app.promptIndex + 5, 0, temp);
+ app.promptIndex--;
break;
case "At the end":
// Repeat at end of Terms
- var temp = Terms[Term];
- Terms.splice(Term, 1);
- Terms.push(temp);
- Term--;
+ var temp = app.prompt;
+ app.prompts.splice(app.promptIndex, 1);
+ app.prompts.push(temp);
+ app.promptIndex--;
break;
}
// Save terms to local storage
- localStorage.setItem(Settings["Prefix"] + "terms", JSON.stringify(Terms));
+ localStorage.setItem(Prefix + "terms", JSON.stringify(app.prompts));
// Reset quizzer
Reset();
}
+
+
+
+// Called when the user hits enter or presses the enter button
+function Enter() {
+ if (app.responceActive) {
+ Continue();
+ }
+ else {
+ Submit();
+ }
+}
diff --git a/Scripts/Reference.js b/Scripts/Reference.js
@@ -1,14 +1,57 @@
// Declare global variables
-var Sets; // List of parsed sets
+let app;
// Load the document
function Load() {
- // Apply dark mode
- if (localStorage.getItem("darkMode") == "true") {
- document.body.classList.toggle("dark");
- }
+ // Initialize Vue
+ app = new Vue({
+ el: "#app", // Mount to app div
+
+ data: {
+ darkTheme: false,
+ set: "Choose a vocab set",
+ sets: {"Choose a vocab set":[]},
+ query: ""
+ },
+
+ watch: {
+ darkTheme: function() {
+ // Get theme from localStorage if null
+ if (this.darkTheme === null) {
+ this.darkTheme = JSON.parse(localStorage.getItem("darkTheme"));
+ }
+
+ // Detect preferred color scheme if null
+ if (this.darkTheme === null) {
+ this.darkTheme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
+ }
+
+ // Apply theme
+ if (this.darkTheme) {
+ document.body.classList.add("dark");
+ }
+ else {
+ document.body.classList.remove("dark");
+ }
+
+ // Save theme
+ localStorage.setItem("darkTheme", this.darkTheme);
+ }
+ }
+ });
+
+ // Unhide hidden divs
+ // Divs were hidden to improve interface for users with JS blocked
+ document.querySelector("h1").hidden = false;
+ document.getElementById("controls").hidden = false;
+ document.getElementById("referenceTable").hidden = false;
+ document.querySelector("footer").hidden = false;
+
+
+ // Load settings
+ app.darkTheme = null; // Force theme to update
// Set table height
setTableHeight();
@@ -19,7 +62,6 @@ function Load() {
});
// Load CSVs
- Sets = [];
let setNames = ["Verbs", "Adjectives", "Adverbs", "Prepositions", "Transitions",
"Colors", "Days", "Months", "Questions", "Weather", "Family", "Clothes",
"Nature", "House", "Vacation", "Childhood", "Professions", "Health"];
@@ -28,7 +70,7 @@ function Load() {
download: true,
complete: function(results) {
// Set verbs
- Sets[setName] = results.data;
+ app.sets[setName] = results.data;
}
});
}
@@ -44,83 +86,15 @@ function setTableHeight() {
-// Change the vocab set
-function referenceSetChanged() {
- // Clear table
- if (document.getElementById("referenceSet").value == "Choose a vocab set") {
- document.getElementById("referenceTableInner").innerHTML = "";
- return;
- }
-
- // Get headers
- var head = '<tr>';
- for (column of Sets[document.getElementById("referenceSet").value][0]) {
- head += `<th>${column}</th>`;
- }
- head += "</tr>";
-
- // Get body
- var body = "";
- rows = Sets[document.getElementById("referenceSet").value].slice(1);
- for (var row = 0; row < rows.length; row++) {
- body += '<tr>';
- columns = rows[row];
- for (var column = 0; column < columns.length; column++) {
- body += `<td onclick="Read(${row + 1}, ${column})">${columns[column]}</td>`;
- }
- body += "</tr>";
- }
-
- // Add html
- document.getElementById("referenceTableInner").innerHTML = head + body;
-}
-
-
-
// Reads a vocab word
function Read(row, column)
{
- var msg = new SpeechSynthesisUtterance(Sets[document.getElementById("referenceSet").value][row][column]);
- if (Sets[document.getElementById("referenceSet").value][0][column].toLowerCase().includes("english")) {
+ var msg = new SpeechSynthesisUtterance(app.sets[app.set][row][column]);
+ if (app.sets[app.set][0][column].toLowerCase().includes("english")) {
msg.lang = 'en';
}
- else if (Sets[document.getElementById("referenceSet").value][0][column].toLowerCase().includes("spanish")){
+ else if (app.sets[app.set][0][column].toLowerCase().includes("spanish")){
msg.lang = 'es';
}
window.speechSynthesis.speak(msg);
}
-
-
-
-// Filter the table
-function referenceFilterChanged() {
- // Declare variables
- var match, txtValue
- var filter = document.getElementById("referenceFilter").value.toLowerCase();
- var rows = document.getElementById("referenceTableInner").getElementsByTagName("tr");
-
- // Loop through rows
- for (row of rows)
- {
- // Loop through columns
- match = false;
- row.style.display = "none";
- for (column of row.children)
- {
- // If a match hasn't already been found
- if (!match) {
- // Get text
- txtValue = column.textContent || column.innerText;
-
- // Look for match
- if (txtValue.toLowerCase().indexOf(filter) != -1) {
- row.style.display = "";
- match = true;
- }
- }
- }
- }
-
- // Make first row visible
- rows[0].style.display = "";
-}
-\ No newline at end of file
diff --git a/Scripts/Settings.js b/Scripts/Settings.js
@@ -1,232 +1,45 @@
-// Declare global variables
-let setId = 0; // Next valid vocab set id number
-
-
-
-// Update local storage
-function UpdateLocalStorage() {
- localStorage.setItem("darkMode", document.getElementById("settingsDarkMode").checked);
- localStorage.setItem("PromptType", document.getElementById("settingsPromptType").value);
- localStorage.setItem("InputType", document.getElementById("settingsInputType").value);
- localStorage.setItem("repeatPrompt", document.getElementById("settingsRepeatPrompts").value);
-}
-
-
-
-// Add a filtered set
-function AddVocabSet() {
- // Create row
- var clone = document.getElementById("vocabSetTemplate").content.cloneNode(true);
-
- // Set row ids
- clone.children[0].setAttribute("id", `vocabSet-${setId}`);
- clone.getElementById("vocabSetName").setAttribute("id", `vocabSetName-${setId}`);
- clone.getElementById("vocabSetFilter").setAttribute("id", `vocabSetFilter-${setId}`);
-
- // Add remove button onclick attribute
- clone.getElementById("vocabSetRemove").setAttribute("onclick", `var element = document.getElementById('vocabSet-${setId}'); element.parentNode.removeChild(element);`);
-
- // Add row
- document.getElementById("vocabSetsInner").appendChild(clone);
-
- // Add filters
- VocabSetChanged(document.getElementById(`vocabSetName-${setId}`));
-
- // Increment setId
- setId++; // increment fileId to get a unique ID for the new element
-}
-
-
-
-// Add a verb filter
-function AddVerbFilter() {
- // Create row
- var clone = document.getElementById("verbFilterTemplate").content.cloneNode(true);
-
- // Set row ids
- clone.children[0].setAttribute("id", `verbFilter-${setId}`);
- clone.getElementById("verbFilterTense").setAttribute("id", `verbFilterTense-${setId}`);
- clone.getElementById("verbFilterType").setAttribute("id", `verbFilterType-${setId}`);
-
- // Add remove button onclick attribute
- clone.getElementById("verbFilterRemove").setAttribute("onclick", `var element = document.getElementById('verbFilter-${setId}'); element.parentNode.removeChild(element);`);
-
- // Add row
- document.getElementById("verbFiltersInner").appendChild(clone);
-
- // Increment setId
- setId++; // increment fileId to get a unique ID for the new element
-}
-
-
-
-// Update the filter option
-function VocabSetChanged(setName) {
- // Get filter options
- var items = [];
- switch(setName.value)
- {
- case "Verbs":
- items = ["All Definitions", "Spanish Infinitives", "English Infinitives", "Reverse Conjugations"];
- break;
-
- case "Adjectives":
- case "Adverbs":
- case "Prepositions":
- case "Transitions":
- case "Colors":
- case "Days":
- case "Months":
- case "Questions":
- items = ["All Definitions", "English to Spanish", "Spanish to English"];
- break;
-
- case "Weather":
- case "Professions":
- items = ["All Definitions", "English to Spanish", "Spanish to English",
- "Nouns", "Verbs"];
- break;
-
- case "Family":
- case "Clothes":
- items = ["All Definitions", "English to Spanish", "Spanish to English",
- "Nouns", "Adjectives"];
- break;
-
- case "Nature":
- case "House":
- case "Vacation":
- case "Childhood":
- case "Health":
- items = ["All Definitions", "English to Spanish", "Spanish to English",
- "Nouns", "Verbs", "Adjectives"];
- break;
- }
-
- // Create html
- var html = ""
- for (var item of items) {
- html += "<option>" + item + "</option>"
- }
-
- // Set html
- filterId = setName.id.replace("vocabSetName", "vocabSetFilter");
- document.getElementById(filterId).innerHTML = html;
-}
-
-
-
-// Update the type filter options
-function VerbTenseChanged(filter) {
- // Get type filter element
- let types = document.getElementById(filter.id.replace("verbFilterTense", "verbFilterType"));
-
- // Enable all types
- types[0].disabled = false;
- types[1].disabled = false;
- types[2].disabled = false;
- types[3].disabled = false;
- types[4].disabled = false;
- types[5].disabled = false;
- types[6].disabled = false;
-
- // Disable unavailable types
- switch(filter.value)
- {
- case "All Tenses":
- break;
- case "Present Participles":
- types[1].disabled = true; // Reflexive
- types[5].disabled = true; // Orthographic
- if (types.selectedIndex === 1 || types.selectedIndex === 5) {
- // Deselect unavailable types
- types.selectedIndex = 0
- }
- break;
- case "Present Tense":
- types[5].disabled = true; // Orthographic
- if (types.selectedIndex === 5) {
- // Deselect unavailable types
- types.selectedIndex = 0
- }
- break;
- case "Preterite Tense":
- break;
- case "Imperfect Tense":
- types[4].disabled = true; // Stem Changing
- types[5].disabled = true; // Orthographic
- if (types.selectedIndex === 4 || types.selectedIndex === 5) {
- // Deselect unavailable types
- types.selectedIndex = 0
- }
- break;
- }
-}
-
-
-
// Start a new session
function CreateSession() {
- // Get terms and localStorage prefix
- let terms;
+ // Get prompts and localStorage prefix
let prefix;
- if (!document.getElementById("vocabSettings").hidden) {
- // Filter and load Sets into Terms
- terms = [];
- for (var i = 0; i < setId; i++)
+ if (app.state == "vocabSettings") {
+ // Filter and load Sets into prompts
+ app.prompts = [];
+ for (let filter of app.vocabFilters)
{
- if (document.getElementById(`vocabSet-${i}`))
- {
- // Get filter information
- var set = document.getElementById(`vocabSetName-${i}`).value;
- var filter = document.getElementById(`vocabSetFilter-${i}`).value;
-
- // Add filtered set
- terms.push(...ApplyVocabFilter(Sets[set], filter));
- }
+ // Add filtered set
+ app.prompts.push(...ApplyVocabFilter(Sets[filter.set], filter.type));
}
- // Shuffle terms
- terms = Shuffle(terms);
+ // Shuffle prompts
+ app.prompts = Shuffle(app.prompts);
// Set prefix
prefix = "vocab-"
}
- else if (!document.getElementById("verbSettings").hidden) {
- // Get filters
- let filters = [];
- for (let i = 0; i < setId; i++)
- {
- if (document.getElementById(`verbFilter-${i}`))
- {
- // Get filter information
- let tense = document.getElementById(`verbFilterTense-${i}`).value;
- let type = document.getElementById(`verbFilterType-${i}`).value;
-
- // Add filter
- filters.push({tense: tense, regularity: type});
- }
- }
-
- // Get terms
- terms = Shuffle(ApplyVerbFilter(Sets["Verbs"], filters));
+ else if (app.state == "verbSettings") {
+ // Get prompts
+ app.prompts = Shuffle(ApplyVerbFilter(Sets["Verbs"], app.verbFilters));
// Set prefix
prefix = "verb-"
}
-
- // Get quizzer settings
- inputType = document.getElementById("settingsInputType").value;
- promptType = document.getElementById("settingsPromptType").value;
- repeatPrompts = document.getElementById("settingsRepeatPrompts").value;
+
+ // Set progress
+ app.promptIndex = 0;
// Start quizzer
try {
// Start quizzer
- StartQuizzer(terms, 0, prefix, inputType, promptType, repeatPrompts);
+ StartQuizzer(prefix);
// Show and hide elements
- Show("quizzer");
+ if (app.state == "verbSettings") {
+ app.state = "verbQuizzer";
+ }
+ if (app.state == "vocabSettings") {
+ app.state = "vocabQuizzer";
+ }
}
catch (e) {
switch (e) {
@@ -248,29 +61,28 @@ function CreateSession() {
function ResumeSession() {
// Get localStorage prefix
let prefix;
- if (!document.getElementById("vocabSettings").hidden) {
+ if (app.state == "vocabSettings") {
prefix = "vocab-"
}
- else if (!document.getElementById("verbSettings").hidden) {
+ else if (app.state == "verbSettings") {
prefix = "verb-"
}
- // Load terms and progress
- let terms = JSON.parse(localStorage.getItem(prefix + "terms"));
- let term = parseInt(localStorage.getItem(prefix + "term"));
-
- // Get quizzer settings
- inputType = document.getElementById("settingsInputType").value;
- promptType = document.getElementById("settingsPromptType").value;
- repeatPrompts = document.getElementById("settingsRepeatPrompts").value;
+ // Load prompts and progress
+ app.prompts = JSON.parse(localStorage.getItem(prefix + "prompts"));
+ app.promptIndex = parseInt(localStorage.getItem(prefix + "prompt"));
// Start quizzer
try {
- StartQuizzer(terms, term, prefix, inputType, promptType, repeatPrompts);
+ StartQuizzer(prefix);
// Show and hide elements
- document.getElementById("settings").hidden = true;
- document.getElementById("quizzer").hidden = false;
+ if (app.state == "verbSettings") {
+ app.state = "verbQuizzer";
+ }
+ if (app.state == "vocabSettings") {
+ app.state = "vocabQuizzer";
+ }
}
catch (e) {
switch (e) {
@@ -360,89 +172,89 @@ function ApplyVocabFilter(vocabSet, name) {
// Filters verbs set given the filter information
function ApplyVerbFilter(terms, filterInfo) {
- // Change regularity strings into regex
+ // Create filters
+ let filters = []; // Format: [{outputIndex:0, inputIndex:0, filterIndex:0, filterValue:"regex"}]
for (config of filterInfo) {
- switch (config.regularity.toLowerCase()) {
+ // Get regularity
+ let regularity;
+ switch (config.type.toLowerCase()) {
case "regular":
- config.regularity = "Regular";
+ regularity = "Regular";
break;
case "reflexive":
- config.regularity = "Reflexive";
+ regularity = "Reflexive";
break;
case "irregular":
- config.regularity = "Irregular";
+ regularity = "Irregular";
break;
case "stem-changing":
case "stem changing":
- config.regularity = "Stem.?Changing";
+ regularity = "Stem.?Changing";
break;
case "orthographic":
- config.regularity = "Orthographic";
+ regularity = "Orthographic";
break;
case "non-regular":
case "non regular":
case "nonregular":
- config.regularity = "Irregular|Stem.?Changing|Orthographic";
+ regularity = "Irregular|Stem.?Changing|Orthographic";
break;
default:
case "all":
- config.regularity = ".*";
+ regularity = ".*";
}
- }
- // Create filters
- let filters = []; // Format: [{outputIndex:0, inputIndex:0, filterIndex:0, filterValue:"regex"}]
- for (config of filterInfo) {
+ // Create filter
switch (config.tense.toLowerCase()) {
case "present participle":
case "present participles":
- filters.push({outputIndex:0, inputIndex:3, filterIndex:2, filterValue:config.regularity});
+ filters.push({outputIndex:0, inputIndex:3, filterIndex:2, filterValue:regularity});
break;
case "present":
case "present tense":
- filters.push({outputIndex:0, inputIndex:5, filterIndex:4, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:6, filterIndex:4, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:7, filterIndex:4, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:8, filterIndex:4, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:9, filterIndex:4, filterValue:config.regularity});
+ filters.push({outputIndex:0, inputIndex:5, filterIndex:4, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:6, filterIndex:4, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:7, filterIndex:4, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:8, filterIndex:4, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:9, filterIndex:4, filterValue:regularity});
break;
case "preterite":
case "preterite tense":
- filters.push({outputIndex:0, inputIndex:11, filterIndex:10, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:12, filterIndex:10, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:13, filterIndex:10, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:14, filterIndex:10, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:15, filterIndex:10, filterValue:config.regularity});
+ filters.push({outputIndex:0, inputIndex:11, filterIndex:10, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:12, filterIndex:10, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:13, filterIndex:10, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:14, filterIndex:10, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:15, filterIndex:10, filterValue:regularity});
break;
case "imperfect":
case "imperfect tense":
- filters.push({outputIndex:0, inputIndex:17, filterIndex:16, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:18, filterIndex:16, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:19, filterIndex:16, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:20, filterIndex:16, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:21, filterIndex:16, filterValue:config.regularity});
+ filters.push({outputIndex:0, inputIndex:17, filterIndex:16, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:18, filterIndex:16, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:19, filterIndex:16, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:20, filterIndex:16, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:21, filterIndex:16, filterValue:regularity});
break;
default:
case "all":
- filters.push({outputIndex:0, inputIndex:3, filterIndex:2, filterValue:config.regularity});
+ filters.push({outputIndex:0, inputIndex:3, filterIndex:2, filterValue:regularity});
- filters.push({outputIndex:0, inputIndex:5, filterIndex:4, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:6, filterIndex:4, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:7, filterIndex:4, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:8, filterIndex:4, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:9, filterIndex:4, filterValue:config.regularity});
+ filters.push({outputIndex:0, inputIndex:5, filterIndex:4, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:6, filterIndex:4, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:7, filterIndex:4, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:8, filterIndex:4, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:9, filterIndex:4, filterValue:regularity});
- filters.push({outputIndex:0, inputIndex:11, filterIndex:10, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:12, filterIndex:10, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:13, filterIndex:10, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:14, filterIndex:10, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:15, filterIndex:10, filterValue:config.regularity});
+ filters.push({outputIndex:0, inputIndex:11, filterIndex:10, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:12, filterIndex:10, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:13, filterIndex:10, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:14, filterIndex:10, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:15, filterIndex:10, filterValue:regularity});
- filters.push({outputIndex:0, inputIndex:17, filterIndex:16, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:18, filterIndex:16, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:19, filterIndex:16, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:20, filterIndex:16, filterValue:config.regularity});
- filters.push({outputIndex:0, inputIndex:21, filterIndex:16, filterValue:config.regularity});
+ filters.push({outputIndex:0, inputIndex:17, filterIndex:16, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:18, filterIndex:16, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:19, filterIndex:16, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:20, filterIndex:16, filterValue:regularity});
+ filters.push({outputIndex:0, inputIndex:21, filterIndex:16, filterValue:regularity});
break;
}
}
diff --git a/Styles/Global.css b/Styles/Global.css
@@ -27,6 +27,11 @@ body {
margin: 0px;
touch-action: manipulation;
+}
+
+#app {
+ width: 100%;
+ height: 100%;
display: grid;
grid-template-rows: auto auto 1fr auto;
diff --git a/Styles/Reference.css b/Styles/Reference.css
@@ -50,7 +50,7 @@ h1 {
--hover-color: #FFFFFF;
}
- header, h1, #referenceFilter, #referenceSet, footer, label, br {
+ header, h1, #controls, footer, label, br {
display: none;
}
diff --git a/index.html b/index.html
@@ -11,6 +11,7 @@
<link rel="stylesheet" href="Styles/Home.css">
<link rel="stylesheet" href="Styles/Settings.css">
<link rel="stylesheet" href="Styles/Quizzer.css">
+ <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script src="https://unpkg.com/papaparse@5.1.1/papaparse.min.js"></script>
<script src="Scripts/Home.js"></script>
<script src="Scripts/Settings.js"></script>
@@ -18,195 +19,184 @@
</head>
<body onload="Load();">
- <header onclick="TitleClicked();">Spanish-Quizzer</header>
+ <div id="app">
+ <header @click="Back();">Spanish-Quizzer</header>
- <main>
- <noscript>
- <h1 id="jsError">You must have JavaScript enabled to run Spanish-Quizzer.</h1>
- </noscript>
-
- <div id="home">
- <h1>What do you want to study?</h1>
+ <main>
+ <noscript>
+ <h1 id="jsError">You must have JavaScript enabled to run Spanish-Quizzer.</h1>
+ </noscript>
- <div>
- <button onclick="Show('verbs');">Conjugations</button>
- <button onclick="Show('vocab');">Vocab</button>
- </div>
+ <div id="home" v-show="state == 'home'" hidden>
+ <h1>What do you want to study?</h1>
- <div>
- <a href="reference.html">Or look something up in the Reference Tables</a>
+ <div>
+ <button @click="state = 'verbSettings';">Conjugations</button>
+ <button @click="state = 'vocabSettings';">Vocab</button>
+ </div>
+
+ <div>
+ <a href="reference.html">Or look something up in the Reference Tables</a>
+ </div>
</div>
- </div>
-
-
- <div id="settings" hidden>
- <div id="verbSettings" hidden>
- <template id="verbFilterTemplate">
- <div class="verbFilter">
- <select id="verbFilterTense" onchange="VerbTenseChanged(this);">
- <option>All Tenses</option>
- <option>Present Participles</option>
- <option>Present Tense</option>
- <option>Preterite Tense</option>
- <option>Imperfect Tense</option>
- </select>
- <select id="verbFilterType">
- <option>All Types</option>
- <option>Reflexive</option>
- <option>Regular</option>
- <option>Nonregular</option>
- <option>Stem Changing</option>
- <option>Orthographic</option>
- <option>Irregular</option>
- </select>
- <button id="verbFilterRemove" class="itemRemove">╳</button>
+
+
+ <div id="settings" v-show="state == 'verbSettings' || state == 'vocabSettings'" hidden>
+ <div id="verbSettings" v-show="state == 'verbSettings'">
+ <h1>Choose your settings and then click start.</h1>
+
+ <h2>
+ Verb Filters
+ <button @click="AddVerbFilter();">Add Filter</button>
+ </h2>
+
+ <div id="verbFilters">
+ <div v-for="(filter, index) in verbFilters" class="verbFilter">
+ <select v-model="filter.tense">
+ <option>All Tenses</option>
+ <option>Present Participles</option>
+ <option>Present Tense</option>
+ <option>Preterite Tense</option>
+ <option>Imperfect Tense</option>
+ </select>
+ <select v-model="filter.type">
+ <option v-for="(available, type) in getTenseTypes(index)" :disabled="!available">{{ type }}</option>
+ </select>
+ <button class="itemRemove" @click="RemoveVerbFilter(index);">╳</button>
+ </div>
</div>
- </template>
-
- <h1>Choose your settings and then click start.</h1>
+ </div>
- <h2>
- Verb Filters
- <button id="verbAddFilter" onclick="AddVerbFilter();">Add Filter</button>
- </h2>
- <div id="verbFilters">
- <div id="verbFiltersInner">
+ <div id="vocabSettings" v-show="state == 'vocabSettings'">
+ <h1>Choose your settings and then click start.</h1>
+
+ <h2>
+ Vocabulary Sets
+ <button @click="AddVocabFilter();">Add set</button>
+ </h2>
+
+ <div id="vocabSets">
+ <div v-for="(filter, index) in vocabFilters" class="vocabSet">
+ <select class="vocabSetName" v-model="filter.set">
+ <optgroup label="Common Words">
+ <option>Verbs</option>
+ <option>Adjectives</option>
+ <option>Adverbs</option>
+ <option>Prepositions</option>
+ <option>Transitions</option>
+ </optgroup>
+ <optgroup label="Spanish 1">
+ <option>Colors</option>
+ <option>Days</option>
+ <option>Months</option>
+ <option>Questions</option>
+ <option>Weather</option>
+ <option>Family</option>
+ <option>Clothes</option>
+ </optgroup>
+ <optgroup label="Spanish 2">
+ <option>Nature</option>
+ <option>House</option>
+ <option>Vacation</option>
+ <option>Childhood</option>
+ <option>Professions</option>
+ <option>Health</option>
+ </optgroup>
+ </select>
+ <select class="vocabSetFilter" v-model="filter.type">
+ <option v-for="type in getSetFilters(index)" >{{ type }}</option>
+ </select>
+ <button class="itemRemove" @click="RemoveVocabFilter(index);">╳</button>
+ </div>
</div>
</div>
- </div>
-
-
- <div id="vocabSettings" hidden>
- <template id="vocabSetTemplate">
- <div class="vocabSet">
- <select id="vocabSetName" class="vocabSetName" onchange="VocabSetChanged(this);">
- <optgroup label="Common Words">
- <option>Verbs</option>
- <option>Adjectives</option>
- <option>Adverbs</option>
- <option>Prepositions</option>
- <option>Transitions</option>
- </optgroup>
- <optgroup label="Spanish 1">
- <option>Colors</option>
- <option>Days</option>
- <option>Months</option>
- <option>Questions</option>
- <option>Weather</option>
- <option>Family</option>
- <option>Clothes</option>
- </optgroup>
- <optgroup label="Spanish 2">
- <option>Nature</option>
- <option>House</option>
- <option>Vacation</option>
- <option>Childhood</option>
- <option>Professions</option>
- <option>Health</option>
- </optgroup>
+
+
+ <div id="quizzerSettings">
+ <h2>Quizzer Settings</h2>
+
+ <div>
+ <input type="checkbox" id="settingsDarkTheme" v-model="darkTheme">
+ <label for="settingsDarkTheme">Dark Mode</label>
+ </div>
+ <div>
+ <label for="settingsPromptType">Prompt type</label>
+ <select id="settingsPromptType" v-model="promptType">
+ <option>Text</option>
+ <option>Audio</option>
+ <option>Both</option>
</select>
- <select id="vocabSetFilter" class="vocabSetFilter">
- <!-- Generated by VocabSetChanged() -->
+ </div>
+ <div>
+ <label for="settingsInputType">Input type</label>
+ <select id="settingsInputType" v-model="inputType">
+ <option>Text</option>
+ <option>Voice</option>
+ <option>Either</option>
+ </select>
+ </div>
+ <div>
+ <label for="settingsRepeatPrompts">Repeat missed prompts</label>
+ <select id="settingsRepeatPrompts" v-model="repeatPrompts">
+ <option>Never</option>
+ <option>Immediately</option>
+ <option>5 prompts later</option>
+ <option>At the end</option>
</select>
- <button id="vocabSetRemove" class="itemRemove">╳</button>
</div>
- </template>
-
- <h1>Choose your settings and then click start.</h1>
-
- <h2>
- Vocabulary Sets
- <button id="vocabAddSet" onclick="AddVocabSet();">Add set</button>
- </h2>
- <div id="vocabSets">
- <div id="vocabSetsInner">
+ <div id="settingButtons">
+ <button id="settingsStart" onclick="CreateSession();">Start</button>
+ <button id="settingsResume" onclick="ResumeSession();">Resume</button>
</div>
+
+ <div id="settingsError" class="centered bad"></div>
</div>
</div>
-
-
- <div id="quizzerSettings">
- <h2>Quizzer Settings</h2>
-
- <div>
- <input type="checkbox" id="settingsDarkMode" onchange="document.body.classList.toggle('dark'); UpdateLocalStorage();">
- <label for="settingsDarkMode">Dark Mode</label>
- </div>
- <div>
- <label for="settingsPromptType">Prompt type</label>
- <select id="settingsPromptType" onchange="UpdateLocalStorage();">
- <option>Text</option>
- <option>Audio</option>
- <option>Both</option>
- </select>
- </div>
- <div>
- <label for="settingsInputType">Input type</label>
- <select id="settingsInputType" onchange="UpdateLocalStorage();">
- <option>Text</option>
- <option>Voice</option>
- <option>Either</option>
- </select>
- </div>
- <div>
- <label for="settingsRepeatPrompts">Repeat missed prompts</label>
- <select id="settingsRepeatPrompts" onchange="UpdateLocalStorage();">
- <option>Never</option>
- <option>Immediately</option>
- <option>5 prompts later</option>
- <option>At the end</option>
- </select>
- </div>
-
- <div id="settingButtons">
- <button id="settingsStart" onclick="CreateSession();">Start</button>
- <button id="settingsResume" onclick="ResumeSession();">Resume</button>
+
+
+ <div id="quizzer" v-show="state == 'verbQuizzer' || state == 'vocabQuizzer'" hidden>
+ <p id="quizzerProgress">{{ promptIndex }} / {{ prompts.length}}</p>
+
+ <section>
+ <label id="quizzerPromptType" for="quizzerPrompt">{{ prompt[0] }}</label>
+ <span id="quizzerPrompt" @click="Read(prompt[1], prompt[0]);">{{ prompt[1] }}</span>
+ </section>
+
+ <section>
+ <label id="quizzerInputType" for="quizzerInput">{{ prompt[2] }}</label>
+ <input id="quizzerInput" type="text" v-model="responce" :readonly="!responceActive || inputType == 'Voice'"
+ @keyup.ctrl.enter.exact="Reset();" @keyup.enter.exact="Enter();"
+ autocomplete="off" spellcheck="false" autocorrect="off" placeholder="Type the answer">
+ </section>
+
+ <div id="quizzerButtons">
+ <button v-if="responceActive" :disabled="inputType == 'Voice'" @click="Submit();">Submit</button>
+ <button v-else @click="Continue();">Continue</button>
+ <button @click="Reset();">Skip</button>
</div>
- <div id="settingsError" class="centered bad"></div>
+ <div id="quizzerFeedback" v-show="!responceActive" class="bad">
+ The correct answer is
+ <span id="quizzerFeedbackTerm" @click="Read(prompt[3], prompt[2]);">{{ prompt[3].toLowerCase() }}</span>.
+ </div>
+ <div id="quizzerCongrats" class="good">Congratulations! You made it back to the beginning!</div>
</div>
- </div>
-
-
- <div id="quizzer" hidden>
- <p id="quizzerProgress"></p>
+ </main>
- <section>
- <label id="quizzerPromptType" for="quizzerPrompt"></label>
- <span id="quizzerPrompt" onclick="Read(Terms[Term][1], Terms[Term][0]);"></span>
- </section>
-
- <section>
- <label id="quizzerInputType" for="quizzerInput"></label>
- <input id="quizzerInput" type="text" autocomplete="off" spellcheck="false" autocorrect="off" placeholder="Type the answer">
- </section>
- <div id="quizzerButtons">
- <button id="quizzerEnter">Submit</button>
- <button id="quizzerSkip" onclick="Reset();">Skip</button>
+ <footer hidden>
+ <div id="share" hidden>
+ <a href="mailto:?Subject=Spanish-Quizzer&Body=https://ashermorgan.github.io/Spanish-Quizzer">Email</a>
+ <a rel="noopener" href="http://www.facebook.com/sharer.php?u=https://ashermorgan.github.io/Spanish-Quizzer" target="_blank">Facebook</a>
+ <a rel="noopener" href="https://twitter.com/share?url=https://ashermorgan.github.io/Spanish-Quizzer&text=Spanish-Quizzer" target="_blank">Twitter</a>
+ <a rel="noopener" href="http://reddit.com/submit?url=https://ashermorgan.github.io/Spanish-Quizzer&title=Spanish-Quizzer" target="_blank">Reddit</a>
+ <a id="shareSMS" href="sms:&body=https://ashermorgan.github.io/Spanish-Quizzer" hidden>SMS</a>
</div>
-
- <div id="quizzerFeedback" class="bad">
- The correct answer is
- <span id="quizzerFeedbackTerm" onclick="Read(Terms[Term][3], Terms[Term][2]);"></span>.
- </div>
- <div id="quizzerCongrats" class="good"></div>
- </div>
- </main>
-
-
- <footer>
- <div id="share" hidden>
- <a href="mailto:?Subject=Spanish-Quizzer&Body=https://ashermorgan.github.io/Spanish-Quizzer">Email</a>
- <a rel="noopener" href="http://www.facebook.com/sharer.php?u=https://ashermorgan.github.io/Spanish-Quizzer" target="_blank">Facebook</a>
- <a rel="noopener" href="https://twitter.com/share?url=https://ashermorgan.github.io/Spanish-Quizzer&text=Spanish-Quizzer" target="_blank">Twitter</a>
- <a rel="noopener" href="http://reddit.com/submit?url=https://ashermorgan.github.io/Spanish-Quizzer&title=Spanish-Quizzer" target="_blank">Reddit</a>
- <a id="shareSMS" href="sms:&body=https://ashermorgan.github.io/Spanish-Quizzer" hidden>SMS</a>
- </div>
- <a href="Offline">Study Offline</a>
- <a href="javascript:document.getElementById('share').hidden = false; void 0;">Share</a>
- </footer>
+ <a href="Offline">Study Offline</a>
+ <a href="javascript:document.getElementById('share').hidden = false; void 0;">Share</a>
+ </footer>
+ </div>
</body>
</html>
\ No newline at end of file
diff --git a/reference.html b/reference.html
@@ -9,67 +9,75 @@
<link rel="apple-touch-icon" sizes="180x180" href="Images/apple-touch-icon.png">
<link rel="stylesheet" href="Styles/Global.css">
<link rel="stylesheet" href="Styles/Reference.css">
+ <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script src="https://unpkg.com/papaparse@5.1.1/papaparse.min.js"></script>
<script src="Scripts/Reference.js"></script>
</head>
<body onload="Load()" onresize="setTableHeight()">
- <header onclick="window.location='./'">Spanish-Quizzer</header>
+ <div id="app">
+ <header onclick="window.location='./'">Spanish-Quizzer</header>
- <main>
- <noscript>
- <h1 id="jsError">You must have JavaScript enabled to run Spanish-Quizzer.</h1>
- </noscript>
-
- <h1>Reference Tables</h1>
-
- <div id="controls">
- <select id="referenceSet" aria-label="Vocab Set" onchange="referenceSetChanged()">
- <option>Choose a vocab set</option>
- <optgroup label="Common Words">
- <option>Verbs</option>
- <option>Adjectives</option>
- <option>Adverbs</option>
- <option>Prepositions</option>
- <option>Transitions</option>
- </optgroup>
- <optgroup label="Spanish 1">
- <option>Colors</option>
- <option>Days</option>
- <option>Months</option>
- <option>Questions</option>
- <option>Weather</option>
- <option>Family</option>
- <option>Clothes</option>
- </optgroup>
- <optgroup label="Spanish 2">
- <option>Nature</option>
- <option>House</option>
- <option>Vacation</option>
- <option>Childhood</option>
- <option>Professions</option>
- <option>Health</option>
- </optgroup>
- </select>
- <input type="text" aria-label="Search" id="referenceFilter" onkeyup="referenceFilterChanged()" placeholder="Search">
- </div>
-
- <div id="referenceTable">
- <table id="referenceTableInner"></table>
- </div>
- </main>
-
- <footer>
- <div id="share" hidden>
- <a href="mailto:?Subject=Spanish-Quizzer&Body=https://ashermorgan.github.io/Spanish-Quizzer/Reference">Email</a>
- <a rel="noopener" href="http://www.facebook.com/sharer.php?u=https://ashermorgan.github.io/Spanish-Quizzer/Reference" target="_blank">Facebook</a>
- <a rel="noopener" href="https://twitter.com/share?url=https://ashermorgan.github.io/Spanish-Quizzer/Reference&text=Spanish-Quizzer" target="_blank">Twitter</a>
- <a rel="noopener" href="http://reddit.com/submit?url=https://ashermorgan.github.io/Spanish-Quizzer/Reference&title=Spanish-Quizzer" target="_blank">Reddit</a>
- <a id="shareSMS" href="sms:&body=https://ashermorgan.github.io/Spanish-Quizzer/Reference" hidden>SMS</a>
- </div>
- <a href="./">Home</a>
- <a href="Offline">Study Offline</a>
- <a href="javascript:document.getElementById('share').hidden = false; void 0;">Share</a>
- </footer>
+ <main>
+ <noscript>
+ <h1 id="jsError">You must have JavaScript enabled to run Spanish-Quizzer.</h1>
+ </noscript>
+
+ <h1 hidden>Reference Tables</h1>
+
+ <div id="controls" hidden>
+ <select aria-label="Vocab Set" v-model="set">
+ <option>Choose a vocab set</option>
+ <optgroup label="Common Words">
+ <option>Verbs</option>
+ <option>Adjectives</option>
+ <option>Adverbs</option>
+ <option>Prepositions</option>
+ <option>Transitions</option>
+ </optgroup>
+ <optgroup label="Spanish 1">
+ <option>Colors</option>
+ <option>Days</option>
+ <option>Months</option>
+ <option>Questions</option>
+ <option>Weather</option>
+ <option>Family</option>
+ <option>Clothes</option>
+ </optgroup>
+ <optgroup label="Spanish 2">
+ <option>Nature</option>
+ <option>House</option>
+ <option>Vacation</option>
+ <option>Childhood</option>
+ <option>Professions</option>
+ <option>Health</option>
+ </optgroup>
+ </select>
+ <input type="text" aria-label="Search" v-model="query" placeholder="Search">
+ </div>
+
+ <div id="referenceTable" hidden>
+ <table>
+ <tr v-for="(row, rowIndex) in sets[set]" v-show="rowIndex == 0 || row.join(',').toLowerCase().includes(query.toLowerCase())">
+ <th v-if="rowIndex == 0" v-for="column in row">{{ column }}</th>
+ <td v-if="rowIndex != 0" v-for="(column, columnIndex) in row" @click="Read(rowIndex, columnIndex)">{{ column }}</td>
+ </tr>
+ </table>
+ </div>
+ </main>
+
+ <footer hidden>
+ <div id="share" hidden>
+ <a href="mailto:?Subject=Spanish-Quizzer&Body=https://ashermorgan.github.io/Spanish-Quizzer/Reference">Email</a>
+ <a rel="noopener" href="http://www.facebook.com/sharer.php?u=https://ashermorgan.github.io/Spanish-Quizzer/Reference" target="_blank">Facebook</a>
+ <a rel="noopener" href="https://twitter.com/share?url=https://ashermorgan.github.io/Spanish-Quizzer/Reference&text=Spanish-Quizzer" target="_blank">Twitter</a>
+ <a rel="noopener" href="http://reddit.com/submit?url=https://ashermorgan.github.io/Spanish-Quizzer/Reference&title=Spanish-Quizzer" target="_blank">Reddit</a>
+ <a id="shareSMS" href="sms:&body=https://ashermorgan.github.io/Spanish-Quizzer/Reference" hidden>SMS</a>
+ </div>
+ <a href="./">Home</a>
+ <a href="Offline">Study Offline</a>
+ <a href="javascript:document.getElementById('share').hidden = false; void 0;">Share</a>
+ </footer>
+ </div>
</body>
</html>
\ No newline at end of file