Commit | Line | Data |
---|---|---|
43828378 BA |
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 | ||
435371c7 | 19 | Vue.component("statements", { |
a80c6a3b BA |
20 | // 'inputs': array of index (as in questions) + input (text or array of ints) |
21 | // display: 'all', 'one', 'solution' | |
22 | // iidx: current level-0 integer index (can match a group of questions / inputs) | |
23 | props: ['questions','inputs','display','iidx'], | |
24 | data: function() { | |
25 | return { | |
26 | displayStyle: "compact", //or "all": all on same page | |
27 | }; | |
28 | } | |
435371c7 BA |
29 | // Full questions tree is rendered, but some parts hidden depending on display settings |
30 | render(h) { | |
71d1ca9c | 31 | let domTree = (this.questions || [ ]).map( (q,i) => { |
435371c7 | 32 | let questionContent = [ ]; |
a80c6a3b BA |
33 | questionContent.push( |
34 | h( | |
35 | "h4", | |
36 | { | |
37 | "class": { | |
38 | "questionIndex": true, | |
39 | } | |
40 | }, | |
41 | q.index | |
42 | ) | |
43 | ); | |
435371c7 BA |
44 | questionContent.push( |
45 | h( | |
46 | "div", | |
47 | { | |
48 | "class": { | |
49 | wording: true, | |
a80c6a3b | 50 | |
435371c7 BA |
51 | }, |
52 | domProps: { | |
53 | innerHTML: q.wording, | |
54 | }, | |
55 | } | |
56 | ) | |
57 | ); | |
a80c6a3b BA |
58 | if (!!q.options) |
59 | { | |
60 | // quiz-like question | |
61 | let optionsOrder = _.range(q.options.length); | |
62 | if (!q.fixed) | |
63 | optionsOrder = _.shuffle(optionsOrder); | |
64 | let optionList = [ ]; | |
65 | optionsOrder.forEach( idx => { | |
66 | let option = [ ]; | |
67 | option.push( | |
68 | h( | |
69 | "input", | |
70 | { | |
71 | domProps: { | |
72 | checked: this.answers.inputs.length > 0 && this.answers.inputs[i][idx], | |
73 | disabled: monitoring, | |
74 | }, | |
75 | attrs: { | |
76 | id: this.inputId(i,idx), | |
77 | type: "checkbox", | |
78 | }, | |
79 | on: { | |
80 | change: e => { this.answers.inputs[i][idx] = e.target.checked; }, | |
81 | }, | |
435371c7 | 82 | }, |
a80c6a3b BA |
83 | [ '' ] //to work in Firefox 45.9 ESR @ ENSTA... |
84 | ) | |
85 | ); | |
86 | option.push( | |
87 | h( | |
88 | "label", | |
89 | { | |
90 | domProps: { | |
91 | innerHTML: q.options[idx], | |
92 | }, | |
93 | attrs: { | |
94 | "for": this.inputId(i,idx), | |
95 | }, | |
96 | } | |
97 | ) | |
98 | ); | |
99 | optionList.push( | |
100 | h( | |
101 | "div", | |
102 | { | |
103 | "class": { | |
104 | option: true, | |
105 | choiceCorrect: this.answers.showSolution && this.questions[i].answer.includes(idx), | |
106 | choiceWrong: this.answers.showSolution && this.answers.inputs[i][idx] && !q.answer.includes(idx), | |
107 | }, | |
435371c7 | 108 | }, |
a80c6a3b BA |
109 | option |
110 | ) | |
111 | ); | |
112 | }); | |
113 | questionContent.push( | |
435371c7 BA |
114 | h( |
115 | "div", | |
116 | { | |
117 | "class": { | |
a80c6a3b | 118 | optionList: true, |
435371c7 BA |
119 | }, |
120 | }, | |
a80c6a3b | 121 | optionList |
435371c7 BA |
122 | ) |
123 | ); | |
a80c6a3b BA |
124 | } |
125 | if (this.display == "all" && !this.navigator && i < this.questions.length-1) | |
e49ec3e4 | 126 | questionContent.push( h("hr") ); |
a80c6a3b | 127 | const depth = (q.index.match(/\./g) || []).length; |
435371c7 BA |
128 | return h( |
129 | "div", | |
130 | { | |
131 | "class": { | |
132 | "question": true, | |
a80c6a3b BA |
133 | "hide": this.display == "one" && this.iidx != i, |
134 | "depth" + depth: true, | |
435371c7 BA |
135 | }, |
136 | }, | |
137 | questionContent | |
138 | ); | |
139 | }); | |
a80c6a3b BA |
140 | const navigator = h( |
141 | "div", | |
142 | { | |
143 | "class": { | |
144 | "hide": this.displayStyle == "all" | |
145 | }, | |
146 | }, | |
147 | [ | |
148 | h( | |
149 | "button", | |
150 | { | |
151 | "class": { | |
152 | "btn": true, | |
153 | }, | |
154 | on: { | |
155 | click: () => { | |
156 | this.index = Math.max(0, this.index - 1); | |
157 | }, | |
158 | }, | |
159 | }, | |
160 | [ h("span", { "class": { "material-icon": true } }, "fast_rewind") ] | |
161 | ), //onclick: index = max(0,index-1) | |
162 | h("span",{ },(this.iidx+1).toString()), | |
163 | h( | |
164 | "button", | |
165 | { | |
166 | "class": { | |
167 | "btn": true, | |
168 | }, | |
169 | on: { | |
170 | click: () => { | |
171 | this.index = Math.min(this.index+1, this.questions.length-1) | |
172 | }, | |
173 | }, | |
174 | }, | |
175 | [ h("span", { "class": { "material-icon": true } }, "fast_forward") ] | |
176 | ) | |
177 | ] | |
178 | ); | |
179 | domTree.push(navigator); | |
180 | domTree.push( | |
181 | h( | |
182 | "button", | |
183 | { | |
184 | on: { | |
185 | click: () => { | |
186 | this.displayStyle = displayStyle == "compact" ? "all" : "compact"; | |
187 | }, | |
188 | }, | |
189 | }, | |
190 | this.displayStyle == "compact" ? "Show all" : "Navigator" | |
191 | ) | |
192 | ); | |
435371c7 BA |
193 | return h( |
194 | "div", | |
195 | { | |
196 | attrs: { | |
197 | id: "statements", | |
198 | }, | |
199 | }, | |
71d1ca9c | 200 | domTree |
435371c7 BA |
201 | ); |
202 | }, | |
3b8117c5 BA |
203 | mounted: function() { |
204 | statementsLibsRefresh(); | |
205 | }, | |
435371c7 | 206 | updated: function() { |
435371c7 BA |
207 | statementsLibsRefresh(); |
208 | }, | |
209 | methods: { | |
210 | inputId: function(i,j) { | |
211 | return "q" + i + "_" + "input" + j; | |
212 | }, | |
213 | }, | |
214 | }); |