Browse Source

Adding some new features

master
Fabian Stamm 2 years ago
parent
commit
94b27f9ee4
  1. 8
      ccna4-missing
  2. 11
      package-lock.json
  3. 2
      package.json
  4. 54
      src/App.svelte
  5. 6
      src/main.ts
  6. 14
      src/router.ts
  7. 71
      src/stores.ts
  8. 37
      src/sw.ts
  9. 78
      src/views/Browse.svelte
  10. 20
      src/views/Home.svelte
  11. 11
      src/views/Overview.svelte

8
ccna4-missing

@ -0,0 +1,8 @@
'21 | Next is not UL but: undefined',
'52 | No correct answers found!',
'98 | Next is not UL but: undefined',
'99 | Next is not UL but: undefined',
'100 | Next is not UL but: undefined',
'143 | Next is not UL but: undefined',
'144 | Next is not UL but: undefined',
'145 | No correct answers found!'

11
package-lock.json

@ -1038,6 +1038,12 @@
"physical-cpu-count": "^2.0.0"
}
},
"@types/elasticlunr": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@types/elasticlunr/-/elasticlunr-0.9.0.tgz",
"integrity": "sha512-7xUGaa0HqDmfawyFd8ANaoTHt5KF/BEdvKfqz4NxGEhbloIXGGtH065MOvC+YYQRZPAA/b5Vt/Xe5AmXoKTmhQ==",
"dev": true
},
"@types/navigo": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@types/navigo/-/navigo-7.0.1.tgz",
@ -2732,6 +2738,11 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
"dev": true
},
"elasticlunr": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/elasticlunr/-/elasticlunr-0.9.5.tgz",
"integrity": "sha1-ZVQbswnd3Qz5Ty0ciGGyvmUbsNU="
},
"electron-to-chromium": {
"version": "1.3.314",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.314.tgz",

2
package.json

@ -11,6 +11,7 @@
"dependencies": {
"@hibas123/theme": "^1.2.14",
"@hibas123/utils": "^2.2.3",
"elasticlunr": "^0.9.5",
"feather-icons": "^4.24.1",
"navigo": "^7.1.2",
"svelte": "^3.16.0",
@ -19,6 +20,7 @@
"uuid": "^3.3.3"
},
"devDependencies": {
"@types/elasticlunr": "^0.9.0",
"@types/navigo": "^7.0.1",
"@types/node": "^12.12.14",
"@types/uuid": "^3.4.6",

54
src/App.svelte

@ -1,28 +1,40 @@
<script>
// import Trash from "feather-icons/dist/icons/trash.svg"
// import Vaults from "./views/Vaults.svelte";
// import Trash from "feather-icons/dist/icons/trash.svg"
// import Vaults from "./views/Vaults.svelte";
import {
pageStore
} from "./router";
import { pageStore } from "./router";
import {
DeviceType,
DeviceTypes,
ModalStore
} from "./stores";
import { DeviceType, DeviceTypes, ModalStore } from "./stores";
$: console.log("Current DeviceType:", DeviceTypes[$DeviceType]);
import QuestionManager from "./stores";
const { version } = QuestionManager;
$: console.log("Current DeviceType:", DeviceTypes[$DeviceType]);
</script>
<style>
.version {
position: fixed;
bottom: 1.5rem;
left: 0.5rem;
}
</style>
<div>
{#if $DeviceType === DeviceTypes.MOBILE}
<svelte:component this={$pageStore.mobile} params={$pageStore.params} />
{:else}
<svelte:component this={$pageStore.desktop} params={$pageStore.params} />
{/if}
{#if $ModalStore}
<svelte:component this={$ModalStore.component} modal={$ModalStore}/>
{/if}
</div>
<div class="header" style="padding: .25rem">
<span on:click={() => (window.location.hash = '#/')}>CCNA Trainer:</span>
<span>{version}</span>
</div>
{#if $DeviceType === DeviceTypes.MOBILE}
<svelte:component this={$pageStore.mobile} params={$pageStore.params} />
{:else}
<svelte:component this={$pageStore.desktop} params={$pageStore.params} />
{/if}
{#if $ModalStore}
<svelte:component this={$ModalStore.component} modal={$ModalStore} />
{/if}
</div>
<div class="version">{version}</div>

6
src/main.ts

@ -43,4 +43,8 @@ const app = new App({
target: document.getElementById("content")
});
export default app;
export default app;
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register("sw.ts");
}

14
src/router.ts

@ -19,14 +19,28 @@ export const pageStore = readable<ActiveRoute>(undefined, (set) => {
router.resolve();
});
import Home from "./views/Home.svelte"
import Overview from "./views/Overview.svelte";
import Browse from "./views/Browse.svelte";
router.on("/", (params) => {
setComponent({
desktop: Home as typeof SvelteComponent,
mobile: Home as typeof SvelteComponent,
params: {}
})
}).on("/test", (params) => {
setComponent({
desktop: Overview as typeof SvelteComponent,
mobile: Overview as typeof SvelteComponent,
params
})
}).on("/browse", (params) => {
setComponent({
desktop: Browse as typeof SvelteComponent,
mobile: Browse as typeof SvelteComponent,
params: {}
})
})
// .on("/:vaultid", (params) => {

71
src/stores.ts

@ -1,28 +1,35 @@
import { writable, readable } from "svelte/store";
import { Question, Exam } from "./data";
import { Question, Exam, QuestionTypes } from "./data";
import CCNA2 from "../ccna2.json";
import CCNA3 from "../ccna3.json";
import CCNA4 from "../ccna4.json";
import TestData from "../data-test.json";
const tests = new Map<string, Exam>();
tests.set("ccna4", CCNA4);
tests.set("ccna3", CCNA3);
tests.set("ccna2", CCNA2);
tests.set("test", TestData)
const Tests = new Map<string, Exam>();
Tests.set("ccna4", CCNA4);
Tests.set("ccna3", CCNA3);
Tests.set("ccna2", CCNA2);
Tests.set("test", TestData)
const DEFAULT = "ccna4";
let test = new URL(window.location.href).searchParams.get("exam") || DEFAULT;
export let test = new URL(window.location.href).searchParams.get("exam") || DEFAULT;
const dataVersion = tests.has(test) ? test : DEFAULT;
const Data = tests.get(dataVersion);
const dataVersion = Tests.has(test) ? test : DEFAULT;
const Data = Tests.get(dataVersion);
const QuestionMap = new Map<string, Question>();
Data.questions.forEach(q => QuestionMap.set(q.id, q));
console.log("Running exam:", dataVersion);
const runsShould = 3;
import elasticlunr from "elasticlunr";
console.log(elasticlunr);
class QuestionManager {
version: string = dataVersion;
@ -40,6 +47,22 @@ class QuestionManager {
level: number
}[];
index: elasticlunr.Index<{ id: string, title: string, answers: string }>;
private getAnswerString(question: Question) {
switch (question.type) {
case QuestionTypes.SelectOne:
case QuestionTypes.SelectMultiple:
return Object.values(question.options).join(" ; ");
case QuestionTypes.TextInput:
return question.correct;
case QuestionTypes.AssignValues:
return [...Object.values(question.fields), ...Object.values(question.values)].join(" ; ");
default:
return "";
}
}
constructor() {
this.availableQuestions = Data.questions.map(question => {
return {
@ -49,6 +72,32 @@ class QuestionManager {
})
this.getNewActive();
this.index = elasticlunr(function () {
this.addField("title")
this.addField("answers");
this.setRef("id")
})
Data.questions
.map(e => ({
id: e.id,
title: e.title,
answers: this.getAnswerString(e)
}))
.forEach(e => this.index.addDoc(e));
}
search(term: string) {
if (term === "")
return Data.questions;
const match = this.index.search(term, {
fields: {
title: { boost: 2 },
answers: { boost: 1 }
}
}).map(e => QuestionMap.get(e.ref));
return match;
}
getNewActive() {
@ -108,4 +157,6 @@ export const IsDark = readable(Theme.isDark(), set => {
return () => Theme.isDarkObservable.unsubscribe(onChange);
})
export { ModalStore } from "./modals";
export { ModalStore } from "./modals";
export const AvailableTests = Array.from(Tests.keys());

37
src/sw.ts

@ -0,0 +1,37 @@
var CACHE = 'cache-and-update';
self.addEventListener('install', function (evt) {
console.log('The service worker is being installed.');
evt.waitUntil(precache());
});
self.addEventListener('fetch', function (evt: FetchEvent) {
console.log('The service worker is serving the asset.');
evt.respondWith(fromCache(evt.request, update(evt.request)));
evt.waitUntil(update(evt.request));
});
function precache() {
return caches.open(CACHE).then(function (cache) {
return cache.addAll([
"./",
"./index.html"
]);
});
}
function fromCache(request, update) {
return caches.open(CACHE).then(function (cache) {
return cache.match(request).then(function (matching) {
return matching || update;
});
});
}
function update(request) {
return caches.open(CACHE).then(function (cache) {
return fetch(request).then(function (response) {
return cache.put(request, response);
});
});
}

78
src/views/Browse.svelte

@ -0,0 +1,78 @@
<script>
import QuestionManager from "../stores";
import { QuestionTypes } from "../data";
let term = "";
$: questions = QuestionManager.search(term);
let opened = undefined;
// $:opened = openedId === undefined ? undefined : questions.find(e=>e.id === openedId);
$: console.log("Opened:", opened)
</script>
<div class="container" style="overflow-x: hidden">
<br />
<input
class="inp"
style="width: 100%"
type="text"
bind:value={term}
placeholder="Search..." />
<ul class="list list-divider list-clickable">
{#each questions as question}
<li on:click={()=>opened = opened === question.id ? undefined : question.id}>
<p>
{@html question.title}
</p>
{#if opened === question.id}
{#if question.type === QuestionTypes.SelectOne}
<ul>
{#each Object.entries(question.options) as [key, value]}
<li class={question.correct === key ? "correct" : ""}>
{value}
</li>
{/each}
</ul>
{:else if question.type === QuestionTypes.SelectMultiple}
<ul>
{#each Object.entries(question.options) as [key, value]}
<li class={question.correct.indexOf(key) >= 0 ? "correct" : ""}>
{value}
</li>
{/each}
</ul>
{:else if question.type === QuestionTypes.TextInput}
<p>{question.correct}</p>
{:else if question.type === QuestionTypes.AssignValues}
<ul>
{#each Object.entries(question.fields) as [key, value]}
<li>
{value} ➞ {question.values[question.correct[key]]}
</li>
{/each}
</ul>
{:else}
Unknown type!
{/if}
{/if}
</li>
{/each}
</ul>
</div>
<style>
.correct {
color: green
}
</style>
<!-- {#if opened}
<div class="modal">
{@html opened.title};
<div class="modal-action">
<button class="btn" on:click={()=>openedId = undefined}>Close</button>
</div>
</div>
{/if} -->

20
src/views/Home.svelte

@ -0,0 +1,20 @@
<script>
import {AvailableTests, test} from "../stores";
let selected = test;
</script>
<div class="container" style="overflow-x: hidden">
<p>Test your knowlegde</p>
<button class="btn btn-primary" on:click={()=>window.location.hash = "#/test"}>Test</button>
<p>Browse and search specific questions</p>
<button class="btn btn-primary" on:click={()=>window.location.hash = "#/browse"}>Search questions</button>
<br>
<select class="inp" bind:value={selected}>
{#each AvailableTests as test}
<option>{test}</option>
{/each}
</select>
<button class="btn" on:click={()=>window.location.href = "./?exam=" + selected}>switch</button>
</div>

11
src/views/Overview.svelte

@ -1,8 +1,7 @@
<script>
import QuestionManager from "../stores";
const {
progress,
version
progress
} = QuestionManager;
import Question from "./Question.svelte"
@ -20,19 +19,11 @@
footer>div {
background-color: var(--primary);
}
.version {
position: fixed;
bottom :1.5rem;
left: .5rem;
}
</style>
<div class="container" style="overflow-x: hidden">
<Question />
</div>
<div class="version">{version}</div>
<footer class="elv-24">
<div style={"width:" + $progress + "%;" }>{($progress).toFixed(2)}%</div>
</footer>
Loading…
Cancel
Save