Create a first draft of the quiz that lets users order the elements
This commit is contained in:
commit
1ac81b7541
51
index.html
Normal file
51
index.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ministranten-Quiz</title>
|
||||
<link rel="stylesheet" href="quiz.css">
|
||||
<script src="vue.js"></script>
|
||||
<script src="quiz.js" async></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="quiz">
|
||||
<div class="store">
|
||||
<div
|
||||
class="card"
|
||||
v-for="item_idx in store.items"
|
||||
v-bind:class="{selected: item_idx == store.selected}"
|
||||
v-bind:style="{visibility: store_card_visible(item_idx) ? 'visible' : 'hidden'}"
|
||||
v-on:click="store_select(item_idx)"
|
||||
>
|
||||
{{ store_text(item_idx) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="state">
|
||||
<div class="helptext" v-if="!all_inserted || order_revealed">{{ helptext }}</div>
|
||||
<button v-if="all_inserted && !order_revealed" v-on:click="reveal_order">Reihenfolge prüfen</button>
|
||||
</div>
|
||||
<div class="solution">
|
||||
<div class="arrow" v-bind:style="{visibility: arrows_visible ? 'visible' : 'hidden'}">
|
||||
<svg width="20" height="20" v-on:click="insert_front()">
|
||||
<path d="M20 0V20L0 10Z" fill="#2b8856" />
|
||||
</svg>
|
||||
</div>
|
||||
<template v-for="elem in solution">
|
||||
<div class="row">
|
||||
<div class="card" v-bind:class="{incorrect: incorrects[elem.main_idx]}">
|
||||
{{ solution_main_text(elem) }}
|
||||
<svg width=20 height=20 v-if="!order_correct" v-on:click="remove_solution(elem)">
|
||||
<path d="M2 2L18 18M18 2L2 18" stroke="#f00" stroke-width="2px" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow" v-bind:style="{visibility: arrows_visible ? 'visible' : 'hidden'}">
|
||||
<svg width="20" height="20" v-on:click="insert_after(elem)">
|
||||
<path d="M20 0V20L0 10Z" fill="#2b8856" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
88
quiz.css
Normal file
88
quiz.css
Normal file
@ -0,0 +1,88 @@
|
||||
body {
|
||||
background-color: #2b8856;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto Slab';
|
||||
src: url(https://fonts.jimstatic.com/s/robotoslab/v11/BngbUXZYTXPIvIBgJJSb6s3BzlRRfKOFbvjojISmb2Rj.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
#quiz {
|
||||
background-color: #fff;
|
||||
font-family: "Roboto Slab";
|
||||
font-size: 16px;
|
||||
margin: 0 auto;
|
||||
width: 960px;
|
||||
}
|
||||
#quiz .store {
|
||||
border-bottom: 2px solid #000;
|
||||
padding: 5px;
|
||||
}
|
||||
#quiz .card {
|
||||
background-color: #eee;
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
min-height: 50px;
|
||||
padding: 5px;
|
||||
vertical-align: top;
|
||||
width: 166px;
|
||||
}
|
||||
#quiz .store .card {
|
||||
cursor: pointer;
|
||||
}
|
||||
#quiz .store .card:hover {
|
||||
background-color: #c7dad0;
|
||||
}
|
||||
#quiz .card.selected {
|
||||
border: 3px solid #2b8856;
|
||||
padding: 2px;
|
||||
}
|
||||
#quiz .card.incorrect {
|
||||
background-color: #faa;
|
||||
}
|
||||
#quiz .state {
|
||||
border-bottom: 2px solid #000;
|
||||
padding: 5px;
|
||||
}
|
||||
#quiz .state .helptext {
|
||||
margin: 5px;
|
||||
}
|
||||
#quiz .state button {
|
||||
background-color: #e8912a;
|
||||
border-radius: 0;
|
||||
border-width: 0;
|
||||
cursor: pointer;
|
||||
font-family: "Roboto Slab";
|
||||
font-size: 16px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
#quiz .state button:hover {
|
||||
background-color: #facc99;
|
||||
}
|
||||
#quiz .solution {
|
||||
padding: 20px 5px;
|
||||
}
|
||||
#quiz .row .card {
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#quiz .row .card svg {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
visibility: hidden;
|
||||
}
|
||||
#quiz .row .card:hover svg {
|
||||
visibility: visible;
|
||||
}
|
||||
#quiz .arrow {
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
#quiz .arrow svg {
|
||||
cursor: pointer;
|
||||
left: 190px;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
}
|
197
quiz.js
Normal file
197
quiz.js
Normal file
@ -0,0 +1,197 @@
|
||||
let cards_content = [
|
||||
[
|
||||
"Einzug",
|
||||
"Kreuzzeichen und liturgischer Gruß",
|
||||
"Begrüßung und Einführung in die Feier",
|
||||
"Schuldbekenntnis und Vergebungsbitte (Bußakt)",
|
||||
"Kyrie",
|
||||
"Gloria",
|
||||
"Tagesgebet",
|
||||
"Lesung (Altes Testament)",
|
||||
"Zwischengesang / Antwortpsalm",
|
||||
"Lesung (Neues Testament)",
|
||||
"Hallelujaruf",
|
||||
"Evangelium",
|
||||
"Homilie / Predigt",
|
||||
"Credo / Glaubensbekenntnis",
|
||||
"Fürbitten",
|
||||
"Gabenbereitung mit Gabengebet",
|
||||
"Eucharistisches Hochgebet: Präfation",
|
||||
"Sanctus",
|
||||
"Einsetzungsbericht",
|
||||
"Vaterunser",
|
||||
"Friedensgruß",
|
||||
"Brechen des Brotes / Agnus Dei – Lamm Gottes",
|
||||
"Kommunion",
|
||||
"Dank- / Schlussgebet",
|
||||
"Segen",
|
||||
"Entlassung",
|
||||
],
|
||||
];
|
||||
|
||||
function shuffle(a) {
|
||||
for (let x = a.length-1; x>0; x--) {
|
||||
let y = Math.floor(Math.random() * (x + 1));
|
||||
let temp = a[x];
|
||||
a[x] = a[y];
|
||||
a[y] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
function shuffle_cards() {
|
||||
let a = [];
|
||||
for (let i=0; i<cards_content[0].length; i++) {
|
||||
a.push(i);
|
||||
}
|
||||
shuffle(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
function choose_incorrects(idx_vals) {
|
||||
let options = {
|
||||
0: [],
|
||||
};
|
||||
function find_longest_chain(maxkey) {
|
||||
let longest = null;
|
||||
for (let key in options) {
|
||||
if (
|
||||
(
|
||||
maxkey == null
|
||||
|| key <= maxkey
|
||||
)
|
||||
&& (
|
||||
longest == null
|
||||
|| options[key].length > options[longest].length
|
||||
|| (
|
||||
options[key].length == options[longest].length
|
||||
&& Number.parseInt(key) < Number.parseInt(longest)
|
||||
)
|
||||
)
|
||||
) {
|
||||
longest = key;
|
||||
}
|
||||
}
|
||||
return longest;
|
||||
}
|
||||
idx_vals.forEach((val) => {
|
||||
let longest = find_longest_chain(val);
|
||||
let mychain = options[longest].slice();
|
||||
mychain.push(val);
|
||||
options[val + 1] = mychain;
|
||||
});
|
||||
let result_chain = find_longest_chain(null);
|
||||
let a = [];
|
||||
for (let i=0; i<cards_content[0].length; i++) {
|
||||
a.push(true);
|
||||
}
|
||||
options[result_chain].forEach((elem) => {
|
||||
a[elem] = false;
|
||||
});
|
||||
return a;
|
||||
}
|
||||
|
||||
var app = new Vue({
|
||||
el: "#quiz",
|
||||
data: {
|
||||
cards_content,
|
||||
store: {
|
||||
section: 0,
|
||||
selected: null,
|
||||
items: shuffle_cards(),
|
||||
},
|
||||
solution: [],
|
||||
order_revealed: false,
|
||||
},
|
||||
computed: {
|
||||
helptext: function() {
|
||||
if (this.order_revealed) {
|
||||
if (this.order_correct) {
|
||||
return "Perfekt! Die Reihenfolge ist korrekt.";
|
||||
} else {
|
||||
return "Die Reihenfolge passt leider noch nicht ganz. Falsch eingeordnete Elemente sind rot markiert. Entferne diese mit einem Klick auf das rote X und füge sie neu ein, bis die Reihenfolge stimmt.";
|
||||
}
|
||||
} else {
|
||||
if (this.solution.length == 0) {
|
||||
return "Klicke oben auf ein Element des Gottesdienstablaufs, um es nach unten zu übernehmen.";
|
||||
} else if (this.solution.length == 1) {
|
||||
if (this.store.selected == null) {
|
||||
return "Wähle nun von oben das nächste Element aus.";
|
||||
} else {
|
||||
return "Klicke unten auf einen grünen Pfeil, um es an der richtigen Stelle in die Liste einzuordnen.";
|
||||
}
|
||||
} else {
|
||||
return "Bringe nun auch alle weiteren Elemente in die richtige Reihenfolge.";
|
||||
}
|
||||
}
|
||||
},
|
||||
all_inserted: function() {
|
||||
return this.solution.length == this.cards_content[0].length;
|
||||
},
|
||||
arrows_visible: function() {
|
||||
return this.store.selected != null;
|
||||
},
|
||||
incorrects: function() {
|
||||
if (this.order_revealed) {
|
||||
let idx_vals = this.solution.map(elem => elem.main_idx);
|
||||
return choose_incorrects(idx_vals);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
order_correct: function() {
|
||||
return this.order_revealed && this.all_inserted && !this.incorrects.includes(true);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
store_text: function(idx) {
|
||||
return this.cards_content[this.store.section][idx];
|
||||
},
|
||||
store_card_visible: function(idx) {
|
||||
for (let i=0; i<this.solution.length; i++) {
|
||||
if (this.solution[i].main_idx == idx) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
store_select: function(i) {
|
||||
if (this.solution.length == 0) {
|
||||
this.solution.push({
|
||||
main_idx: i,
|
||||
});
|
||||
} else if (i == this.store.selected) {
|
||||
this.store.selected = null;
|
||||
} else {
|
||||
this.store.selected = i;
|
||||
}
|
||||
},
|
||||
solution_main_text: function(elem) {
|
||||
return cards_content[0][elem.main_idx];
|
||||
},
|
||||
insert_front: function() {
|
||||
this.solution.splice(0, 0, {
|
||||
main_idx: this.store.selected,
|
||||
});
|
||||
this.store.selected = null;
|
||||
},
|
||||
find_solution: function(elem) {
|
||||
for (let i=0; i<this.solution.length; i++) {
|
||||
if (this.solution[i] == elem) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
},
|
||||
insert_after: function(elem) {
|
||||
this.solution.splice(this.find_solution(elem)+1, 0, {
|
||||
main_idx: this.store.selected,
|
||||
});
|
||||
this.store.selected = null;
|
||||
},
|
||||
remove_solution: function(elem) {
|
||||
this.solution.splice(this.find_solution(elem), 1);
|
||||
},
|
||||
reveal_order: function() {
|
||||
this.order_revealed = true;
|
||||
},
|
||||
},
|
||||
});
|
11965
vue-debug.js
Normal file
11965
vue-debug.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user