'update'
[qomet.git] / public / javascripts / components / statements.js
CommitLineData
43828378
BA
1/*
2 * questions group by index prefix 1.2.3 1.1 ...etc --> '1'
3
4NOTE: questions can contain parameterized exercises (how ?
5--> describe variables (syntax ?)
6--> write javascript script (OK, users trusted ? ==> safe mode possible if public website)
7Imaginary example: (using math.js)
8 <params> (avant l'exo)
fdf62750
BA
9 const x = math.random();
10 const y = math.random();
43828378 11 </params>
92a6f830
BA
12 <result>
13 let M = math.matrix([[7, x], [y, -3]]);
14 return math.det(M);
15 </result>
43828378 16 <div>Calculer le déterminant de
92a6f830 17 $$\begin{matrix}7 & £x£\\£y£ & -3\end{matrix}$$</div>
43828378 18 * ...
73609d3b 19
a3080c33
BA
20+ fixed + question time (syntax ?)
21
73609d3b
BA
22--> input of type text (number, or vector, or matrix e.g. in R syntax)
23--> parameter stored in question.param (TODO)
24
43828378
BA
25*/
26
435371c7 27Vue.component("statements", {
73609d3b 28 // 'inputs': object with key = question index and value = text or boolean array
a80c6a3b
BA
29 // display: 'all', 'one', 'solution'
30 // iidx: current level-0 integer index (can match a group of questions / inputs)
14c9c66a 31 props: ['questions','inputs','answers','display','iidx'],
a80c6a3b
BA
32 data: function() {
33 return {
34 displayStyle: "compact", //or "all": all on same page
14c9c66a 35 parameters: 0, //TODO: DO NOT re-draw parameters for already answered questions
a80c6a3b 36 };
cb39647a 37 },
435371c7
BA
38 // Full questions tree is rendered, but some parts hidden depending on display settings
39 render(h) {
73609d3b
BA
40 // Prepare questions groups, ordered
41 let questions = this.questions || [ ]
42 let questionGroups = _.groupBy(questions, q => {
43 const dotPos = q.index.indexOf(".");
44 return dotPos === -1 ? q.index : q.index.substring(0,dotPos);
45 });
c8f68697
BA
46 let domTree = Object.keys(questionGroups).map( idx => {
47 let qg = questionGroups[idx];
48 const i = parseInt(idx);
73609d3b
BA
49 // Re-order questions 1.1.1 then 1.1.2 then...
50 const orderedQg = qg.sort( (a,b) => {
51 let aParts = a.split('.').map(Number);
52 let bParts = b.split('.').map(Number);
53 const La = aParts.length, Lb = bParts.length;
54 for (let i=0; i<Math.min(La,Lb); i++)
55 {
56 if (aParts[i] != bParts[i])
57 return aParts[i] - bParts[i];
58 }
59 return La - Lb; //the longer should appear after
60 });
61 let qgDom = orderedQg.map( q => {
62 let questionContent = [ ];
63 questionContent.push(
64 h(
65 "h4",
66 {
67 "class": {
68 "questionIndex": true,
69 }
435371c7 70 },
73609d3b
BA
71 q.index
72 )
73 );
74 questionContent.push(
75 h(
76 "div",
77 {
78 "class": {
79 wording: true,
435371c7 80 },
73609d3b
BA
81 domProps: {
82 innerHTML: q.wording,
83 },
84 }
85 )
86 );
87 if (!!q.options)
88 {
89 // quiz-like question
90 let optionsOrder = _.range(q.options.length);
91 if (!q.fixed)
92 optionsOrder = _.shuffle(optionsOrder);
93 let optionList = [ ];
94 optionsOrder.forEach( idx => {
95 let option = [ ];
96 option.push(
97 h(
98 "input",
99 {
100 domProps: {
a3080c33
BA
101 checked: !!this.inputs && this.inputs[q.index][idx],
102 disabled: monitoring || this.display == "solution",
73609d3b
BA
103 },
104 attrs: {
105 id: this.inputId(q.index,idx),
106 type: "checkbox",
107 },
108 on: {
109 change: e => { this.inputs[q.index][idx] = e.target.checked; },
110 },
a80c6a3b 111 },
73609d3b
BA
112 [ '' ] //to work in Firefox 45.9 ESR @ ENSTA...
113 )
114 );
115 option.push(
116 h(
117 "label",
118 {
119 domProps: {
120 innerHTML: q.options[idx],
121 },
122 attrs: {
123 "for": this.inputId(q.index,idx),
124 },
125 }
126 )
127 );
14c9c66a 128 const aIdx = (this.answers || [ ]).findIndex( item => { return item.index == q.index; });
73609d3b
BA
129 optionList.push(
130 h(
131 "div",
132 {
133 "class": {
134 option: true,
14c9c66a
BA
135 choiceCorrect: this.display == "solution" && this.answers[aIdx].includes(idx),
136 choiceWrong: this.display == "solution" && !!this.inputs && this.inputs[q.index][idx] && !this.answers[aIdx].includes(idx),
73609d3b 137 },
a80c6a3b 138 },
73609d3b
BA
139 option
140 )
141 );
142 });
143 questionContent.push(
a80c6a3b
BA
144 h(
145 "div",
146 {
147 "class": {
73609d3b 148 optionList: true,
a80c6a3b 149 },
435371c7 150 },
73609d3b 151 optionList
a80c6a3b
BA
152 )
153 );
73609d3b 154 }
14c9c66a
BA
155 else
156 {
157 // Open question, or parameterized: TODO
158 }
73609d3b
BA
159 const depth = (q.index.match(/\./g) || []).length;
160 return h(
161 "div",
162 {
163 "class": {
164 "question": true,
cb39647a 165 ["depth" + depth]: true,
435371c7 166 },
73609d3b
BA
167 },
168 questionContent
435371c7 169 );
73609d3b 170 });
435371c7
BA
171 return h(
172 "div",
173 {
174 "class": {
73609d3b 175 "questionGroup": true,
a80c6a3b 176 "hide": this.display == "one" && this.iidx != i,
435371c7 177 },
cb39647a 178 },
73609d3b 179 qgDom
435371c7
BA
180 );
181 });
a80c6a3b
BA
182 const navigator = h(
183 "div",
184 {
185 "class": {
186 "hide": this.displayStyle == "all"
187 },
188 },
189 [
190 h(
191 "button",
192 {
193 "class": {
194 "btn": true,
195 },
196 on: {
197 click: () => {
198 this.index = Math.max(0, this.index - 1);
199 },
200 },
201 },
202 [ h("span", { "class": { "material-icon": true } }, "fast_rewind") ]
73609d3b 203 ),
a80c6a3b
BA
204 h("span",{ },(this.iidx+1).toString()),
205 h(
206 "button",
207 {
208 "class": {
209 "btn": true,
210 },
211 on: {
212 click: () => {
213 this.index = Math.min(this.index+1, this.questions.length-1)
214 },
215 },
216 },
217 [ h("span", { "class": { "material-icon": true } }, "fast_forward") ]
218 )
219 ]
220 );
221 domTree.push(navigator);
222 domTree.push(
223 h(
224 "button",
225 {
226 on: {
227 click: () => {
228 this.displayStyle = displayStyle == "compact" ? "all" : "compact";
229 },
230 },
231 },
232 this.displayStyle == "compact" ? "Show all" : "Navigator"
233 )
234 );
435371c7
BA
235 return h(
236 "div",
237 {
238 attrs: {
239 id: "statements",
240 },
241 },
71d1ca9c 242 domTree
435371c7
BA
243 );
244 },
3b8117c5
BA
245 mounted: function() {
246 statementsLibsRefresh();
247 },
435371c7 248 updated: function() {
435371c7
BA
249 statementsLibsRefresh();
250 },
251 methods: {
252 inputId: function(i,j) {
253 return "q" + i + "_" + "input" + j;
254 },
255 },
256});