2 * questions group by index prefix 1.2.3 1.1 ...etc --> '1'
4 NOTE: questions can contain parameterized exercises (how ?
5 --> describe variables (syntax ?)
6 --> write javascript script (OK, users trusted ? ==> safe mode possible if public website)
7 Imaginary example: (using math.js)
9 const x = math.random()
10 const y = math.random()
13 let M = math.matrix([[7, x], [y, -3]]);
16 <div>Calculer le déterminant de
17 $$\begin{matrix}7 & £x£\\£y£ & -3\end{matrix}$$</div>
20 + fixed + question time (syntax ?)
22 --> input of type text (number, or vector, or matrix e.g. in R syntax)
23 --> parameter stored in question.param (TODO)
27 Vue
.component("statements", {
28 // 'inputs': object with key = question index and value = text or boolean array
29 // display: 'all', 'one', 'solution'
30 // iidx: current level-0 integer index (can match a group of questions / inputs)
31 props: ['questions','inputs','answers','display','iidx'],
34 displayStyle: "compact", //or "all": all on same page
35 parameters: 0, //TODO: DO NOT re-draw parameters for already answered questions
38 // Full questions tree is rendered, but some parts hidden depending on display settings
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
);
46 let domTree
= Object
.keys(questionGroups
).map( idx
=> {
47 let qg
= questionGroups
[idx
];
48 const i
= parseInt(idx
);
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
++)
56 if (aParts
[i
] != bParts
[i
])
57 return aParts
[i
] - bParts
[i
];
59 return La
- Lb
; //the longer should appear after
61 let qgDom
= orderedQg
.map( q
=> {
62 let questionContent
= [ ];
68 "questionIndex": true,
90 let optionsOrder
= _
.range(q
.options
.length
);
92 optionsOrder
= _
.shuffle(optionsOrder
);
94 optionsOrder
.forEach( idx
=> {
101 checked: !!this.inputs
&& this.inputs
[q
.index
][idx
],
102 disabled: monitoring
|| this.display
== "solution",
105 id: this.inputId(q
.index
,idx
),
109 change: e
=> { this.inputs
[q
.index
][idx
] = e
.target
.checked
; },
112 [ '' ] //to work in Firefox 45.9 ESR @ ENSTA...
120 innerHTML: q
.options
[idx
],
123 "for": this.inputId(q
.index
,idx
),
128 const aIdx
= (this.answers
|| [ ]).findIndex( item
=> { return item
.index
== q
.index
; });
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
),
143 questionContent
.push(
157 // Open question, or parameterized: TODO
159 const depth
= (q
.index
.match(/\./g) || []).length
;
165 ["depth" + depth
]: true,
175 "questionGroup": true,
176 "hide": this.display
== "one" && this.iidx
!= i
,
186 "hide": this.displayStyle
== "all"
198 this.index
= Math
.max(0, this.index
- 1);
202 [ h("span", { "class": { "material-icon": true } }, "fast_rewind") ]
204 h("span",{ },(this.iidx
+1).toString()),
213 this.index
= Math
.min(this.index
+1, this.questions
.length
-1)
217 [ h("span", { "class": { "material-icon": true } }, "fast_forward") ]
221 domTree
.push(navigator
);
228 this.displayStyle
= displayStyle
== "compact" ? "all" : "compact";
232 this.displayStyle
== "compact" ? "Show all" : "Navigator"
245 mounted: function() {
246 statementsLibsRefresh();
248 updated: function() {
249 statementsLibsRefresh();
252 inputId: function(i
,j
) {
253 return "q" + i
+ "_" + "input" + j
;