commit 5b7e779179bb81e6f88fe8289dbd198358cf51a4
parent 0463eb13b405375a5b666fd2dd12ffaf549e069e
Author: AsherMorgan <59518073+AsherMorgan@users.noreply.github.com>
Date: Wed, 21 Oct 2020 12:22:57 -0700
Group all quizzer settings into one object.
Diffstat:
7 files changed, 170 insertions(+), 168 deletions(-)
diff --git a/index.html b/index.html
@@ -51,8 +51,7 @@
</settings>
<quizzer id="quizzer" v-show="state === 'quizzer'" :active="state === 'quizzer'" hidden
- :starting-index="promptIndex" :starting-prompts="prompts" @new-prompt="updateProgress"
- :prompt-type="promptType" :input-type="inputType" :on-missed-prompt="onMissedPrompt" :repeat-prompts="repeatPrompts">
+ :starting-index="promptIndex" :starting-prompts="prompts" :settings="settings" @new-prompt="updateProgress">
</quizzer>
</main>
diff --git a/js/home.js b/js/home.js
@@ -13,12 +13,14 @@ function loadVue() {
data: {
state: "home", // Can be either "home", "settings", or "quizzer"
- category: "home", // Can be either "verbs" or "vocab"
+ category: "verbs", // Can be either "verbs" or "vocab"
- promptType: "Text",
- inputType: "Text",
- onMissedPrompt: "Correct me",
- repeatPrompts: "Never",
+ settings: {
+ promptType: "Text",
+ inputType: "Text",
+ onMissedPrompt: "Correct me",
+ repeatPrompts: "Never",
+ },
prompts: [],
promptIndex: 0,
@@ -59,14 +61,11 @@ function loadVue() {
* Perform validation checks and then start the quizzer.
* @param {Array} prompts - The list of prompts.
* @param {Number} promptIndex - The index of the current prompt.
- * @param {String} promptType - The prompt type.
- * @param {String} inputType - The input type.
- * @param {String} onMissedPrompt - The onMissedPrompt setting value.
- * @param {String} repeatPrompts - The repeat prompts setting value.
+ * @param {Object} settings - The user's settings.
*/
- StartSession: function(prompts, promptIndex, promptType, inputType, onMissedPrompt, repeatPrompts) {
+ StartSession: function(prompts, promptIndex, settings) {
// Validate browser for voice input
- if (this.inputType !== "Text") {
+ if (this.settings.inputType !== "Text") {
if (typeof InstallTrigger !== "undefined") {
// Browser is Firefox
alert("You must enable speech recognition in about:config.");
@@ -79,21 +78,16 @@ function loadVue() {
}
// Give iOS devices ringer warning for prompt audio
- if (this.promptType !== "Text") {
+ if (this.settings.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.");
}
}
- // Copy over prompts and promptIndex
+ // Copy over parameters
this.prompts = prompts;
this.promptIndex = promptIndex;
-
- // Copy over settings
- this.promptType = promptType
- this.inputType = inputType
- this.onMissedPrompt = onMissedPrompt
- this.repeatPrompts = repeatPrompts
+ this.settings = settings;
// Show and hide elements (also enables the quizzer)
this.state = "quizzer";
diff --git a/js/quizzer.js b/js/quizzer.js
@@ -15,22 +15,16 @@ let quizzer = Vue.component("quizzer", {
type: Number,
default: 0,
},
-
- promptType: {
- type: String,
- default: "Text",
- },
- inputType: {
- type: String,
- default: "Text",
- },
- onMissedPrompt: {
- type: String,
- default: "Correct me",
- },
- repeatPrompts: {
- type: String,
- default: "Never",
+ settings: {
+ type: Object,
+ default: function() {
+ return {
+ promptType: "Text",
+ inputType: "Text",
+ onMissedPrompt: "Correct me",
+ repeatPrompts: "Never",
+ }
+ },
},
},
@@ -91,12 +85,12 @@ let quizzer = Vue.component("quizzer", {
this.responce = "";
// Read prompt
- if (this.promptType !== "Text") {
+ if (this.settings.promptType !== "Text") {
Read(this.prompt[1], this.prompt[0]);
}
// Get voice input
- if (this.inputType !== "Text") {
+ if (this.settings.inputType !== "Text") {
// Create recognition object
var recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.msSpeechRecognition)();
@@ -166,7 +160,7 @@ let quizzer = Vue.component("quizzer", {
}
// Give user feedback
- if (!correct && (this.onMissedPrompt === "Correct me" || this.onMissedPrompt === "Tell me")) {
+ if (!correct && (this.settings.onMissedPrompt === "Correct me" || this.settings.onMissedPrompt === "Tell me")) {
// Show and hide elements
this.congratsActive = false;
this.responceActive = false;
@@ -177,7 +171,7 @@ let quizzer = Vue.component("quizzer", {
}
catch { }
}
- else if (!correct && this.onMissedPrompt === "Ignore it") {
+ else if (!correct && this.settings.onMissedPrompt === "Ignore it") {
this.Continue();
}
else {
@@ -196,7 +190,7 @@ let quizzer = Vue.component("quizzer", {
}
// Repeat prompt
- switch (this.repeatPrompts)
+ switch (this.settings.repeatPrompts)
{
case "Never":
// Don't repeat
@@ -264,28 +258,28 @@ let quizzer = Vue.component("quizzer", {
<section>
<label id="quizzerPromptType" for="quizzerPrompt">{{ prompt[0] }}</label>
- <span id="quizzerPrompt" :lang="getLang(prompt[0])" @click="Read(prompt[1], prompt[0]);">{{ promptType === "Audio" ? "Click to hear again" : prompt[1] }}</span>
+ <span id="quizzerPrompt" :lang="getLang(prompt[0])" @click="Read(prompt[1], prompt[0]);">{{ settings.promptType === "Audio" ? "Click to hear again" : prompt[1] }}</span>
</section>
<section>
<label id="quizzerInputType" for="quizzerInput">{{ prompt[2] }}</label>
- <input id="quizzerInput" ref="input" type="text" v-model="responce" :readonly="!responceActive || inputType === 'Voice'"
+ <input id="quizzerInput" ref="input" type="text" v-model="responce" :readonly="!responceActive || settings.inputType === 'Voice'"
@keyup.ctrl.enter.exact="Reset();" @keyup.enter.exact="Enter();" :lang="getLang(prompt[2])"
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-if="responceActive" :disabled="settings.inputType === 'Voice'" @click="Submit();">Submit</button>
<button v-else @click="Continue();">Continue</button>
<button @click="Reset();">Skip</button>
</div>
<div id="quizzerFeedback" ref="feedback" v-show="!responceActive" class="bad">
- <span v-if="onMissedPrompt === 'Correct me'">
+ <span v-if="settings.onMissedPrompt === 'Correct me'">
The correct answer is
<span id="quizzerFeedbackTerm" @click="Read(prompt[3], prompt[2]);">{{ prompt[3].toLowerCase() }}</span>.
</span>
- <span v-if="onMissedPrompt === 'Tell me'">
+ <span v-if="settings.onMissedPrompt === 'Tell me'">
Incorrect.
</span>
</div>
diff --git a/js/settings.js b/js/settings.js
@@ -1,7 +1,7 @@
let settings = Vue.component("settings", {
props: {
category: {
- type: Boolean,
+ type: String,
default: "verbs",
},
},
@@ -11,10 +11,12 @@ let settings = Vue.component("settings", {
darkTheme: document.body.classList.contains("dark"),
verbFilters: [],
vocabFilters: [],
- promptType: localStorage.getItem("promptType") || "Text",
- inputType: localStorage.getItem("inputType") || "Text",
- onMissedPrompt: localStorage.getItem("onMissedPrompt") || "Correct me",
- repeatPrompts: localStorage.getItem("repeatPrompts") || "Never",
+ settings: {
+ promptType: "Text",
+ inputType: "Text",
+ onMissedPrompt: "Correct me",
+ repeatPrompts: "Never",
+ },
};
},
@@ -211,7 +213,7 @@ let settings = Vue.component("settings", {
}
// Start quizzer
- this.$emit("start-session", prompts, promptIndex, this.promptType, this.inputType, this.onMissedPrompt, this.repeatPrompts);
+ this.$emit("start-session", prompts, promptIndex, this.settings);
},
/**
@@ -246,7 +248,7 @@ let settings = Vue.component("settings", {
}
// Start quizzer
- this.$emit("start-session", prompts, promptIndex, this.promptType, this.inputType, this.onMissedPrompt, this.repeatPrompts);
+ this.$emit("start-session", prompts, promptIndex, this.settings);
},
/**
@@ -281,41 +283,42 @@ let settings = Vue.component("settings", {
},
/**
- * Update the promptType setting in localStorage.
- * @param {String} value - The prompt type.
+ * Update setting in localStorage.
+ * @param {String} value - The settings object.
*/
- promptType: function(value) {
- localStorage.setItem("promptType", value);
- },
-
- /**
- * Update the inputType setting in localStorage.
- * @param {String} value - The input type.
- */
- inputType: function(value) {
- localStorage.setItem("inputType", value);
- },
-
- /**
- * Update the onMissedPrompt setting in localStorage.
- * @param {String} value - The onMissedPrompt setting value.
- */
- onMissedPrompt: function(value) {
- localStorage.setItem("onMissedPrompt", value);
- },
-
- /**
- * Update the repeatPrompts setting in localStorage.
- * @param {String} value - The repeat prompts setting value.
- */
- repeatPrompts: function(value) {
- localStorage.setItem("repeatPrompts", value);
+ settings: {
+ handler: function(value) {
+ localStorage.setItem("settings", JSON.stringify(value));
+ },
+ deep: true,
},
},
created: function() {
// Add keyup handler
window.addEventListener("keyup", this.keyup);
+
+ // Parse settings
+ let parsedSettings
+ try {
+ parsedSettings = JSON.parse(localStorage.getItem("settings"));
+ }
+ catch { return; }
+ if (!parsedSettings) { return; }
+
+ // Load settings
+ if (parsedSettings.promptType && ["Text", "Audio", "Both"].includes(parsedSettings.promptType)) {
+ this.settings.promptType = parsedSettings.promptType;
+ }
+ if (parsedSettings.inputType && ["Text", "Voice", "Either"].includes(parsedSettings.inputType)) {
+ this.settings.inputType = parsedSettings.inputType;
+ }
+ if (parsedSettings.onMissedPrompt && ["Correct me", "Tell me", "Ignore it"].includes(parsedSettings.onMissedPrompt)) {
+ this.settings.onMissedPrompt = parsedSettings.onMissedPrompt;
+ }
+ if (parsedSettings.repeatPrompts && ["Never", "Immediately", "5 prompts later", "At the end"].includes(parsedSettings.repeatPrompts)) {
+ this.settings.repeatPrompts = parsedSettings.repeatPrompts;
+ }
},
destroyed: function() {
@@ -415,7 +418,7 @@ let settings = Vue.component("settings", {
</div>
<div>
<label for="settingsPromptType">Prompt type</label>
- <select id="settingsPromptType" v-model="promptType">
+ <select id="settingsPromptType" v-model="settings.promptType">
<option>Text</option>
<option>Audio</option>
<option>Both</option>
@@ -423,7 +426,7 @@ let settings = Vue.component("settings", {
</div>
<div>
<label for="settingsInputType">Input type</label>
- <select id="settingsInputType" v-model="inputType">
+ <select id="settingsInputType" v-model="settings.inputType">
<option>Text</option>
<option>Voice</option>
<option>Either</option>
@@ -431,7 +434,7 @@ let settings = Vue.component("settings", {
</div>
<div>
<label for="settingsRepeatPrompts">When I miss a prompt</label>
- <select id="settingsRepeatPrompts" v-model="onMissedPrompt">
+ <select id="settingsRepeatPrompts" v-model="settings.onMissedPrompt">
<option>Correct me</option>
<option>Tell me</option>
<option>Ignore it</option>
@@ -439,7 +442,7 @@ let settings = Vue.component("settings", {
</div>
<div>
<label for="settingsRepeatPrompts">Repeat missed prompts</label>
- <select id="settingsRepeatPrompts" v-model="repeatPrompts">
+ <select id="settingsRepeatPrompts" v-model="settings.repeatPrompts">
<option>Never</option>
<option>Immediately</option>
<option>5 prompts later</option>
diff --git a/tests/test.app.js b/tests/test.app.js
@@ -9,20 +9,15 @@ describe("App", function() {
expect(app.state).to.equal("home");
});
- it("PromptType should be 'Text'", function() {
- expect(app.promptType).to.equal("Text");
+ it("Category should be 'verbs'", function() {
+ expect(app.category).to.equal("verbs");
});
-
- it("InputType should be 'Text'", function() {
- expect(app.inputType).to.equal("Text");
- });
-
- it("OnMissedPrompt should be 'Correct me'", function() {
- expect(app.onMissedPrompt).to.equal("Correct me");
- });
-
- it("RepeatPrompts should be 'Never'", function() {
- expect(app.repeatPrompts).to.equal("Never");
+
+ it("Settings should be correct", function() {
+ expect(app.settings.promptType).to.equal("Text");
+ expect(app.settings.inputType).to.equal("Text");
+ expect(app.settings.onMissedPrompt).to.equal("Correct me");
+ expect(app.settings.repeatPrompts).to.equal("Never");
});
it("Prompts should be empty", function() {
@@ -70,15 +65,12 @@ describe("App", function() {
describe("StartSession method", function() {
it("Should import parameter values", function() {
// Call StartSession
- app.StartSession([1, 2, 3], 4, "a", "b", "c", "d");
+ app.StartSession([1, 2, 3], 4, "test settings");
// Assert parameters imported
expect(app.prompts).to.have.members([1, 2, 3]);
expect(app.promptIndex).to.equal(4);
- expect(app.promptType).to.equal("a");
- expect(app.inputType).to.equal("b");
- expect(app.onMissedPrompt).to.equal("c");
- expect(app.repeatPrompts).to.equal("d");
+ expect(app.settings).to.equal("test settings");
});
it("Should set state to 'quizzer'", function() {
diff --git a/tests/test.quizzer.js b/tests/test.quizzer.js
@@ -18,20 +18,11 @@ describe("Quizzer", function() {
expect(Quizzer.startingIndex).to.equal(0);
});
- it("PromptType should be 'Text'", function() {
- expect(Quizzer.promptType).to.equal("Text");
- });
-
- it("InputType should be 'Text'", function() {
- expect(Quizzer.inputType).to.equal("Text");
- });
-
- it("OnMissedPrompt should be 'Correct me'", function() {
- expect(Quizzer.onMissedPrompt).to.equal("Correct me");
- });
-
- it("RepeatPrompts should be 'Never'", function() {
- expect(Quizzer.repeatPrompts).to.equal("Never");
+ it("Settings should be correct", function() {
+ expect(Quizzer.settings.promptType).to.equal("Text");
+ expect(Quizzer.settings.inputType).to.equal("Text");
+ expect(Quizzer.settings.onMissedPrompt).to.equal("Correct me");
+ expect(Quizzer.settings.repeatPrompts).to.equal("Never");
});
it("Prompts should be empty", function() {
@@ -153,7 +144,7 @@ describe("Quizzer", function() {
it("Should call Continue if onMissedPrompt is set to 'Ignore it'", function() {
// Initialize variables
Quizzer.active = true;
- Quizzer.repeatPrompts = "At the end";
+ Quizzer.settings.repeatPrompts = "At the end";
Quizzer.onMissedPrompt = "Ignore it";
Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]]
Quizzer.responce = "A5";
@@ -277,7 +268,7 @@ describe("Quizzer", function() {
// Initialize variables
Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
Quizzer.index = 0;
- Quizzer.repeatPrompts = "At the end";
+ Quizzer.settings.repeatPrompts = "At the end";
Quizzer.responceActive = false; // Will be changed if Reset is called
// Run Continue
@@ -295,7 +286,7 @@ describe("Quizzer", function() {
Quizzer.active = true;
Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
Quizzer.index = 0;
- Quizzer.repeatPrompts = "Never";
+ Quizzer.settings.repeatPrompts = "Never";
// Run Continue
Quizzer.Continue();
@@ -311,7 +302,7 @@ describe("Quizzer", function() {
Quizzer.active = true;
Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
Quizzer.index = 0;
- Quizzer.repeatPrompts = "test";
+ Quizzer.settings.repeatPrompts = "test";
// Run Continue
Quizzer.Continue();
@@ -327,7 +318,7 @@ describe("Quizzer", function() {
Quizzer.active = true;
Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
Quizzer.index = 0;
- Quizzer.repeatPrompts = "Immediately";
+ Quizzer.settings.repeatPrompts = "Immediately";
// Run Continue
Quizzer.Continue();
@@ -351,7 +342,7 @@ describe("Quizzer", function() {
["G1", "G2", "G3", "G4"],
];
Quizzer.index = 0;
- Quizzer.repeatPrompts = "5 prompts later";
+ Quizzer.settings.repeatPrompts = "5 prompts later";
// Run Continue
Quizzer.Continue();
@@ -380,7 +371,7 @@ describe("Quizzer", function() {
["G1", "G2", "G3", "G4"],
];
Quizzer.index = 0;
- Quizzer.repeatPrompts = "At the end";
+ Quizzer.settings.repeatPrompts = "At the end";
// Run Continue
Quizzer.Continue();
@@ -402,7 +393,7 @@ describe("Quizzer", function() {
// Initialize variables
Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]]; // Will change if Continue is called
Quizzer.index = 0; // Will be changed if Reset is called
- Quizzer.repeatPrompts = "At the end";
+ Quizzer.settings.repeatPrompts = "At the end";
// Run Enter
Quizzer.Enter();
@@ -419,7 +410,7 @@ describe("Quizzer", function() {
Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]]; // Will change if Continue is called
Quizzer.index = 0; // Will be changed if Reset is called
Quizzer.responce = "A4";
- Quizzer.repeatPrompts = "At the end";
+ Quizzer.settings.repeatPrompts = "At the end";
Quizzer.responceActive = true;
// Run Enter
@@ -436,7 +427,7 @@ describe("Quizzer", function() {
Quizzer.active = true;
Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]]; // Will change if Continue is called
Quizzer.index = 0; // Will be changed if Reset is called
- Quizzer.repeatPrompts = "At the end";
+ Quizzer.settings.repeatPrompts = "At the end";
Quizzer.responceActive = false;
// Run Enter
diff --git a/tests/test.settings.js b/tests/test.settings.js
@@ -17,6 +17,66 @@ describe("Settings", function() {
it("VocabFilters should be empty", function() {
expect(Settings.vocabFilters.length).to.equal(0);
});
+
+ it("Settings should be loaded", function() {
+ // Save original setting from localStorage
+ let originalValue = localStorage.getItem("settings");
+
+ // Set localStorage settings
+ localStorage.setItem("settings", "{\"promptType\":\"Audio\",\"inputType\":\"Voice\",\"onMissedPrompt\":\"Tell me\",\"repeatPrompts\":\"5 prompts later\"}")
+
+ // (re)Create settings component
+ Settings = new settings();
+
+ // Assert settings loaded
+ expect(Settings.settings.promptType).to.equal("Audio");
+ expect(Settings.settings.inputType).to.equal("Voice");
+ expect(Settings.settings.onMissedPrompt).to.equal("Tell me");
+ expect(Settings.settings.repeatPrompts).to.equal("5 prompts later");
+
+ // Restore original setting to localStorage
+ localStorage.setItem("settings", originalValue);
+ });
+
+ it("Invalid individual settings should not be loaded", function() {
+ // Save original setting from localStorage
+ let originalValue = localStorage.getItem("settings");
+
+ // Set localStorage settings
+ localStorage.setItem("settings", "{\"promptType\":\"Audio\",\"inputType\":\"test\",\"onMissedPrompt\":null}")
+
+ // (re)Create settings component
+ Settings = new settings();
+
+ // Assert default settings loaded
+ expect(Settings.settings.promptType).to.equal("Audio"); // promptType wasn't invalid, so it should still be loaded
+ expect(Settings.settings.inputType).to.equal("Text");
+ expect(Settings.settings.onMissedPrompt).to.equal("Correct me");
+ expect(Settings.settings.repeatPrompts).to.equal("Never");
+
+ // Restore original setting to localStorage
+ localStorage.setItem("settings", originalValue);
+ });
+
+ it("Invalid JSON settings should not be loaded", function() {
+ // Save original setting from localStorage
+ let originalValue = localStorage.getItem("settings");
+
+ // Set localStorage settings
+ localStorage.setItem("settings", "asdf")
+
+ // (re)Create settings component
+ Settings = new settings();
+
+ // Assert default settings loaded
+ expect(Settings.settings.promptType).to.equal("Text");
+ expect(Settings.settings.inputType).to.equal("Text");
+ expect(Settings.settings.onMissedPrompt).to.equal("Correct me");
+ expect(Settings.settings.repeatPrompts).to.equal("Never");
+
+ // Restore original setting to localStorage
+ localStorage.setItem("settings", originalValue);
+ });
});
describe("AddFilter method", function() {
@@ -338,54 +398,23 @@ describe("Settings", function() {
});
});
- describe("PromptType watch", function() {
- it("Should update setting in localStorage", async function() {
- // Save original setting from localStorage
- let originalValue = localStorage.getItem("promptType");
-
- // Set promptType
- Settings.promptType = "test";
- await Settings.$nextTick();
-
- // Assert localStorage setting updated
- expect(localStorage.getItem("promptType")).to.equal("test");
-
- // Restore original setting to localStorage
- localStorage.setItem("promptType", originalValue);
- });
- });
-
- describe("InputType watch", function() {
- it("Should update setting in localStorage", async function() {
- // Save original setting from localStorage
- let originalValue = localStorage.getItem("inputType");
-
- // Set inputType
- Settings.inputType = "test";
- await Settings.$nextTick();
-
- // Assert localStorage setting updated
- expect(localStorage.getItem("inputType")).to.equal("test");
-
- // Restore original setting to localStorage
- localStorage.setItem("inputType", originalValue);
- });
- });
-
- describe("RepeatPrompts watch", function() {
+ describe("Settings watch", function() {
it("Should update setting in localStorage", async function() {
// Save original setting from localStorage
- let originalValue = localStorage.getItem("repeatPrompts");
+ let originalValue = localStorage.getItem("settings");
- // Set repeatPrompts
- Settings.repeatPrompts = "test";
+ // Set settings
+ Settings.settings.promptType = "A";
+ Settings.settings.inputType = "B";
+ Settings.settings.onMissedPrompt = "C";
+ Settings.settings.repeatPrompts = "D";
await Settings.$nextTick();
// Assert localStorage setting updated
- expect(localStorage.getItem("repeatPrompts")).to.equal("test");
+ expect(localStorage.getItem("settings")).to.equal("{\"promptType\":\"A\",\"inputType\":\"B\",\"onMissedPrompt\":\"C\",\"repeatPrompts\":\"D\"}");
// Restore original setting to localStorage
- localStorage.setItem("repeatPrompts", originalValue);
+ localStorage.setItem("settings", originalValue);
});
});