Adding some new features
This commit is contained in:
parent
2accd04546
commit
94b27f9ee4
8
ccna4-missing
Normal file
8
ccna4-missing
Normal file
@ -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
generated
11
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -2,20 +2,30 @@
|
||||
// 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";
|
||||
|
||||
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>
|
||||
<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}
|
||||
@ -26,3 +36,5 @@
|
||||
<svelte:component this={$ModalStore.component} modal={$ModalStore} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="version">{version}</div>
|
||||
|
@ -44,3 +44,7 @@ const app = new App({
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register("sw.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) => {
|
||||
|
@ -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() {
|
||||
@ -109,3 +158,5 @@ export const IsDark = readable(Theme.isDark(), set => {
|
||||
})
|
||||
|
||||
export { ModalStore } from "./modals";
|
||||
|
||||
export const AvailableTests = Array.from(Tests.keys());
|
37
src/sw.ts
Normal file
37
src/sw.ts
Normal file
@ -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
Normal file
78
src/views/Browse.svelte
Normal file
@ -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
Normal file
20
src/views/Home.svelte
Normal file
@ -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>
|
@ -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…
Reference in New Issue
Block a user