app.js (8175B)
1 // Declare global variables 2 let app; 3 4 5 6 // page-header component 7 const pageHeader = Vue.component("pageHeader", { 8 props: { 9 /** 10 * The 1st header icon 11 */ 12 icon1: { 13 type: String 14 }, 15 16 /** 17 * The 2nd header icon 18 */ 19 icon2: { 20 type: String 21 }, 22 23 /** 24 * The label for the 1st header icon 25 */ 26 label1: { 27 type: String 28 }, 29 30 /** 31 * The label for the 2nd header icon 32 */ 33 label2: { 34 type: String 35 }, 36 37 /** 38 * The header text 39 */ 40 title: { 41 type: String, 42 default: "Spanish-Quizzer", 43 } 44 }, 45 computed: { 46 /** 47 * The path to the 1st header icon 48 */ 49 path1: function() { 50 if (this.icon1) return `images/${this.icon1}.svg`; 51 else return null; 52 }, 53 54 /** 55 * The path to the 2nd header icon 56 */ 57 path2: function() { 58 if (this.icon2) return `images/${this.icon2}.svg`; 59 else return null; 60 } 61 }, 62 methods: { 63 /** 64 * Navigate to the home page 65 */ 66 goHome: function() { 67 if (this.$route.name !== 'home') this.$router.push('home'); 68 } 69 }, 70 template: ` 71 <header> 72 <button v-if="path1" :title="label1" class="icon"><img :src="path1" alt="" @click="$emit('click1')"/></button> 73 <span @click="goHome">{{ title }}</span> 74 <button v-if="path2" :title="label2" class="icon"><img :src="path2" alt="" @click="$emit('click2')"/></button> 75 </header> 76 ` 77 }); 78 79 80 81 // App pages 82 const homePage = Vue.component("homePage", { 83 data: function() { 84 return { 85 isResumable: false, 86 showResumeBanner: true, 87 }; 88 }, 89 methods: { 90 /** 91 * Handle a keyup event (implements keyboard shortcuts). 92 * @param {object} e - The event args. 93 */ 94 keyup: function(e) { 95 if (this._inactive || e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) return; 96 if (e.key === "c") this.$router.push("verbs"); 97 if (e.key === "v") this.$router.push("vocab"); 98 if (e.key === ",") this.$router.push("settings"); 99 if (e.key === "t") this.$router.push("reference"); 100 if (e.key === "r") this.$router.push("quizzer"); 101 }, 102 }, 103 activated: function() { 104 // Update isResumable property 105 try { 106 // Get last session 107 let { prompts, index } = JSON.parse(localStorage.getItem("last-session")); 108 109 // Validate prompts and promptIndex 110 if (prompts && !isNaN(index) && index >= 0 && index < prompts.length) { 111 this.isResumable = true; 112 } 113 } catch {} 114 }, 115 created: function() { 116 // Add keyup handler 117 window.addEventListener("keyup", this.keyup); 118 }, 119 destroyed: function() { 120 // Remove keyup handler 121 window.removeEventListener("keyup", this.keyup); 122 }, 123 template: ` 124 <div class="home"> 125 <page-header icon2="settings" label2="Settings" @click2="$router.push({name:'settings', params:{referer:$route.name}})"></page-header> 126 <div v-if="isResumable && showResumeBanner" id="resumeBanner"> 127 <router-link v-if="isResumable" to="/quizzer">Resume previous session</router-link> 128 <button @click="showResumeBanner=false" class="icon" title="Dismiss"><img alt="" src="images/x.svg"></button> 129 </div> 130 <main> 131 <h1>The best way to study Spanish vocabulary and verb conjugations</h1> 132 <div> 133 <router-link tag="button" to="/verbs">Study Conjugations</router-link> 134 <router-link tag="button" to="/vocab">Study Vocab</router-link> 135 <router-link tag="button" to="/reference">Reference Tables</router-link> 136 </div> 137 </main> 138 </div> 139 `, 140 }); 141 142 143 144 /** 145 * Initialize the Vue app 146 */ 147 function loadVue() { 148 app = new Vue({ 149 el: "#app", // Mount to app div 150 151 router: new VueRouter({ 152 routes: [ 153 { 154 path: "/", 155 redirect: "/home" 156 }, 157 { 158 path: "/home", 159 name: "home", 160 component: homePage 161 }, 162 { 163 path: "/verbs", 164 name: "verbs", 165 meta: { title: "Verb Filters" }, 166 component: filtersPage, 167 props: { category: "verbs" } 168 }, 169 { 170 path: "/vocab", 171 name: "vocab", 172 meta: { title: "Vocab Filters" }, 173 component: filtersPage, 174 props: { category: "vocab" } 175 }, 176 { 177 path: "/settings", 178 name: "settings", 179 meta: { title: "Settings" }, 180 component: settingsPage, 181 props: true 182 }, 183 { 184 path: "/quizzer", 185 name: "quizzer", 186 meta: { title: "Quizzer" }, 187 component: quizzerPage, 188 props: true 189 }, 190 { 191 path: "/reference", 192 name: "reference", 193 meta: { title: "Reference Tables" }, 194 component: referencePage 195 }, 196 { 197 path: "/data", 198 beforeEnter: function() { window.location.href = "data"; } 199 }, 200 { 201 path: "/tests", 202 beforeEnter: function() { window.location.href = "tests"; } 203 }, 204 { 205 path: "*", 206 redirect: "/home" 207 }, 208 ], 209 }), 210 211 methods: { 212 /** 213 * Go back to the previous page. 214 */ 215 Back: function() { 216 switch (this.$route.name) { 217 case "home": 218 break; 219 case "verbs": 220 case "vocab": 221 case "reference": 222 this.$router.push("home"); 223 break; 224 case "settings": 225 this.$router.push(this.$route.params.referer || "home"); 226 break; 227 case "quizzer": 228 this.$router.push(this.$route.params.referer || "home"); 229 break; 230 } 231 }, 232 233 /** 234 * Handle a keyup event (implements keyboard shortcuts). 235 * @param {object} e - The event args. 236 */ 237 keyup: function(e) { 238 if (e.key === "Escape") this.Back(); 239 }, 240 }, 241 242 data: { 243 data: {} 244 }, 245 246 watch: { 247 $route: function(to, from) { 248 if (to.meta.title) document.title = to.meta.title + " - Spanish-Quizzer"; 249 else document.title = "Spanish-Quizzer"; 250 } 251 }, 252 253 created: function() { 254 // Add keyup handler 255 window.addEventListener("keyup", this.keyup); 256 257 // Set page title 258 if (this.$route.meta.title) document.title = this.$route.meta.title + " - Spanish-Quizzer"; 259 else document.title = "Spanish-Quizzer"; 260 }, 261 262 destroyed: function() { 263 // Remove keyup handler 264 window.removeEventListener("keyup", this.keyup); 265 }, 266 }); 267 } 268 269 270 271 /** 272 * Load the document. 273 */ 274 async function Load() { 275 // Set theme 276 SetTheme(null); 277 278 // Initialize the Vue app 279 loadVue(); 280 281 // Load Spanish-Quizzer data 282 app.data = await loadData(); 283 }