refactoring, better README (breaking commit...)
[qomet.git] / public / javascripts / components / statements.js
1 /*
2 * questions group by index prefix 1.2.3 1.1 ...etc --> '1'
3
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)
8 <params> (avant l'exo)
9 x: math.random()
10 y: math.random()
11 M: math.matrix([[7, x], [y, -3]]);
12 res: math.det(M)
13 </params>
14 <div>Calculer le déterminant de
15 $$\begin{matrix}7 & x\\y & -3\end{matrix}$$</div>
16 * ...
17 */
18
19 Vue.component("statements", {
20 // 'answers' is an object containing
21 // 'inputs'(array),
22 // 'displayAll'(bool), //TODO: should be in questions!
23 // 'showSolution'(bool),
24 // 'indices': order of appearance
25 // 'index': current integer index (focused question)
26 props: ['questions','answers'],
27 // TODO: general render function for nested exercises
28 // There should be a questions navigator below, or next (visible if display=='all')
29 // Full questions tree is rendered, but some parts hidden depending on display settings
30 render(h) {
31 // TODO: render nothing if answers is empty
32 let domTree = (this.questions || [ ]).map( (q,i) => {
33 let questionContent = [ ];
34 questionContent.push(
35 h(
36 "div",
37 {
38 "class": {
39 wording: true,
40 },
41 domProps: {
42 innerHTML: q.wording,
43 },
44 }
45 )
46 );
47 let optionsOrder = _.range(q.options.length);
48 if (!q.fixed)
49 optionsOrder = _.shuffle(optionsOrder);
50 let optionList = [ ];
51 optionsOrder.forEach( idx => {
52 let option = [ ];
53 option.push(
54 h(
55 "input",
56 {
57 domProps: {
58 checked: this.answers.inputs.length > 0 && this.answers.inputs[i][idx],
59 disabled: monitoring,
60 },
61 attrs: {
62 id: this.inputId(i,idx),
63 type: "checkbox",
64 },
65 on: {
66 change: e => { this.answers.inputs[i][idx] = e.target.checked; },
67 },
68 },
69 [ '' ] //to work in Firefox 45.9 ESR @ ENSTA...
70 )
71 );
72 option.push(
73 h(
74 "label",
75 {
76 domProps: {
77 innerHTML: q.options[idx],
78 },
79 attrs: {
80 "for": this.inputId(i,idx),
81 },
82 }
83 )
84 );
85 optionList.push(
86 h(
87 "div",
88 {
89 "class": {
90 option: true,
91 choiceCorrect: this.answers.showSolution && this.questions[i].answer.includes(idx),
92 choiceWrong: this.answers.showSolution && this.answers.inputs[i][idx] && !q.answer.includes(idx),
93 },
94 },
95 option
96 )
97 );
98 });
99 questionContent.push(
100 h(
101 "div",
102 {
103 "class": {
104 optionList: true,
105 },
106 },
107 optionList
108 )
109 );
110 if (this.answers.displayAll && i < this.questions.length-1)
111 questionContent.push( h("hr") );
112 return h(
113 "div",
114 {
115 "class": {
116 "question": true,
117 "hide": !this.answers.displayAll && this.answers.indices[this.answers.index] != i,
118 },
119 },
120 questionContent
121 );
122 });
123 return h(
124 "div",
125 {
126 attrs: {
127 id: "statements",
128 },
129 },
130 domTree
131 );
132 },
133 mounted: function() {
134 statementsLibsRefresh();
135 },
136 updated: function() {
137 // TODO: next line shouldn't be required: questions wordings + answer + options
138 // are processed earlier; their content should be updated at this time.
139 statementsLibsRefresh();
140 },
141 methods: {
142 inputId: function(i,j) {
143 return "q" + i + "_" + "input" + j;
144 },
145 },
146 });