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