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"
|
"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": {
|
"@types/navigo": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/navigo/-/navigo-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/navigo/-/navigo-7.0.1.tgz",
|
||||||
@ -2732,6 +2738,11 @@
|
|||||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"elasticlunr": {
|
||||||
|
"version": "0.9.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/elasticlunr/-/elasticlunr-0.9.5.tgz",
|
||||||
|
"integrity": "sha1-ZVQbswnd3Qz5Ty0ciGGyvmUbsNU="
|
||||||
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.3.314",
|
"version": "1.3.314",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.314.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.314.tgz",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hibas123/theme": "^1.2.14",
|
"@hibas123/theme": "^1.2.14",
|
||||||
"@hibas123/utils": "^2.2.3",
|
"@hibas123/utils": "^2.2.3",
|
||||||
|
"elasticlunr": "^0.9.5",
|
||||||
"feather-icons": "^4.24.1",
|
"feather-icons": "^4.24.1",
|
||||||
"navigo": "^7.1.2",
|
"navigo": "^7.1.2",
|
||||||
"svelte": "^3.16.0",
|
"svelte": "^3.16.0",
|
||||||
@ -19,6 +20,7 @@
|
|||||||
"uuid": "^3.3.3"
|
"uuid": "^3.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/elasticlunr": "^0.9.0",
|
||||||
"@types/navigo": "^7.0.1",
|
"@types/navigo": "^7.0.1",
|
||||||
"@types/node": "^12.12.14",
|
"@types/node": "^12.12.14",
|
||||||
"@types/uuid": "^3.4.6",
|
"@types/uuid": "^3.4.6",
|
||||||
|
@ -1,28 +1,40 @@
|
|||||||
<script>
|
<script>
|
||||||
// import Trash from "feather-icons/dist/icons/trash.svg"
|
// import Trash from "feather-icons/dist/icons/trash.svg"
|
||||||
// import Vaults from "./views/Vaults.svelte";
|
// import Vaults from "./views/Vaults.svelte";
|
||||||
|
|
||||||
import {
|
import { pageStore } from "./router";
|
||||||
pageStore
|
|
||||||
} from "./router";
|
|
||||||
|
|
||||||
import {
|
import { DeviceType, DeviceTypes, ModalStore } from "./stores";
|
||||||
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>
|
</script>
|
||||||
|
|
||||||
<div>
|
<style>
|
||||||
{#if $DeviceType === DeviceTypes.MOBILE}
|
.version {
|
||||||
<svelte:component this={$pageStore.mobile} params={$pageStore.params} />
|
position: fixed;
|
||||||
{:else}
|
bottom: 1.5rem;
|
||||||
<svelte:component this={$pageStore.desktop} params={$pageStore.params} />
|
left: 0.5rem;
|
||||||
{/if}
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
{#if $ModalStore}
|
<div>
|
||||||
<svelte:component this={$ModalStore.component} modal={$ModalStore}/>
|
<div class="header" style="padding: .25rem">
|
||||||
{/if}
|
<span on:click={() => (window.location.hash = '#/')}>CCNA Trainer:</span>
|
||||||
</div>
|
<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>
|
||||||
|
@ -43,4 +43,8 @@ const app = new App({
|
|||||||
target: document.getElementById("content")
|
target: document.getElementById("content")
|
||||||
});
|
});
|
||||||
|
|
||||||
export default 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();
|
router.resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
import Home from "./views/Home.svelte"
|
||||||
import Overview from "./views/Overview.svelte";
|
import Overview from "./views/Overview.svelte";
|
||||||
|
import Browse from "./views/Browse.svelte";
|
||||||
|
|
||||||
router.on("/", (params) => {
|
router.on("/", (params) => {
|
||||||
|
setComponent({
|
||||||
|
desktop: Home as typeof SvelteComponent,
|
||||||
|
mobile: Home as typeof SvelteComponent,
|
||||||
|
params: {}
|
||||||
|
})
|
||||||
|
}).on("/test", (params) => {
|
||||||
setComponent({
|
setComponent({
|
||||||
desktop: Overview as typeof SvelteComponent,
|
desktop: Overview as typeof SvelteComponent,
|
||||||
mobile: Overview as typeof SvelteComponent,
|
mobile: Overview as typeof SvelteComponent,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
}).on("/browse", (params) => {
|
||||||
|
setComponent({
|
||||||
|
desktop: Browse as typeof SvelteComponent,
|
||||||
|
mobile: Browse as typeof SvelteComponent,
|
||||||
|
params: {}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// .on("/:vaultid", (params) => {
|
// .on("/:vaultid", (params) => {
|
||||||
|
@ -1,28 +1,35 @@
|
|||||||
import { writable, readable } from "svelte/store";
|
import { writable, readable } from "svelte/store";
|
||||||
import { Question, Exam } from "./data";
|
import { Question, Exam, QuestionTypes } from "./data";
|
||||||
|
|
||||||
import CCNA2 from "../ccna2.json";
|
import CCNA2 from "../ccna2.json";
|
||||||
import CCNA3 from "../ccna3.json";
|
import CCNA3 from "../ccna3.json";
|
||||||
import CCNA4 from "../ccna4.json";
|
import CCNA4 from "../ccna4.json";
|
||||||
import TestData from "../data-test.json";
|
import TestData from "../data-test.json";
|
||||||
|
|
||||||
const tests = new Map<string, Exam>();
|
const Tests = new Map<string, Exam>();
|
||||||
tests.set("ccna4", CCNA4);
|
Tests.set("ccna4", CCNA4);
|
||||||
tests.set("ccna3", CCNA3);
|
Tests.set("ccna3", CCNA3);
|
||||||
tests.set("ccna2", CCNA2);
|
Tests.set("ccna2", CCNA2);
|
||||||
tests.set("test", TestData)
|
Tests.set("test", TestData)
|
||||||
|
|
||||||
const DEFAULT = "ccna4";
|
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 dataVersion = Tests.has(test) ? test : DEFAULT;
|
||||||
const Data = tests.get(dataVersion);
|
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);
|
console.log("Running exam:", dataVersion);
|
||||||
|
|
||||||
const runsShould = 3;
|
const runsShould = 3;
|
||||||
|
|
||||||
|
import elasticlunr from "elasticlunr";
|
||||||
|
|
||||||
|
console.log(elasticlunr);
|
||||||
|
|
||||||
class QuestionManager {
|
class QuestionManager {
|
||||||
version: string = dataVersion;
|
version: string = dataVersion;
|
||||||
|
|
||||||
@ -40,6 +47,22 @@ class QuestionManager {
|
|||||||
level: number
|
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() {
|
constructor() {
|
||||||
this.availableQuestions = Data.questions.map(question => {
|
this.availableQuestions = Data.questions.map(question => {
|
||||||
return {
|
return {
|
||||||
@ -49,6 +72,32 @@ class QuestionManager {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.getNewActive();
|
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() {
|
getNewActive() {
|
||||||
@ -108,4 +157,6 @@ export const IsDark = readable(Theme.isDark(), set => {
|
|||||||
return () => Theme.isDarkObservable.unsubscribe(onChange);
|
return () => Theme.isDarkObservable.unsubscribe(onChange);
|
||||||
})
|
})
|
||||||
|
|
||||||
export { ModalStore } from "./modals";
|
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>
|
<script>
|
||||||
import QuestionManager from "../stores";
|
import QuestionManager from "../stores";
|
||||||
const {
|
const {
|
||||||
progress,
|
progress
|
||||||
version
|
|
||||||
} = QuestionManager;
|
} = QuestionManager;
|
||||||
|
|
||||||
import Question from "./Question.svelte"
|
import Question from "./Question.svelte"
|
||||||
@ -20,19 +19,11 @@
|
|||||||
footer>div {
|
footer>div {
|
||||||
background-color: var(--primary);
|
background-color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.version {
|
|
||||||
position: fixed;
|
|
||||||
bottom :1.5rem;
|
|
||||||
left: .5rem;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<div class="container" style="overflow-x: hidden">
|
<div class="container" style="overflow-x: hidden">
|
||||||
<Question />
|
<Question />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="version">{version}</div>
|
|
||||||
|
|
||||||
<footer class="elv-24">
|
<footer class="elv-24">
|
||||||
<div style={"width:" + $progress + "%;" }>{($progress).toFixed(2)}%</div>
|
<div style={"width:" + $progress + "%;" }>{($progress).toFixed(2)}%</div>
|
||||||
</footer>
|
</footer>
|
Loading…
Reference in New Issue
Block a user