@pie-element/categorize 11.0.1 → 11.0.2-esm.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/configure/package.json +5 -5
- package/esm/configure.js +3189 -27002
- package/esm/configure.js.map +1 -1
- package/esm/controller.js +172 -225
- package/esm/controller.js.map +1 -1
- package/esm/element.js +1268 -8701
- package/esm/element.js.map +1 -1
- package/package.json +6 -6
package/esm/controller.js
CHANGED
|
@@ -5,83 +5,50 @@ import { getFeedbackForCorrectness } from '@pie-lib/feedback';
|
|
|
5
5
|
import { partialScoring, lockChoices, getShuffledChoices } from '@pie-lib/controller-utils';
|
|
6
6
|
import Translator from '@pie-lib/translator';
|
|
7
7
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (info.done) {
|
|
18
|
-
resolve(value);
|
|
19
|
-
} else {
|
|
20
|
-
Promise.resolve(value).then(_next, _throw);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function _asyncToGenerator(fn) {
|
|
25
|
-
return function () {
|
|
26
|
-
var self = this,
|
|
27
|
-
args = arguments;
|
|
28
|
-
return new Promise(function (resolve, reject) {
|
|
29
|
-
var gen = fn.apply(self, args);
|
|
30
|
-
|
|
31
|
-
function _next(value) {
|
|
32
|
-
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function _throw(err) {
|
|
36
|
-
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
8
|
+
function _extends() {
|
|
9
|
+
_extends = Object.assign || function (target) {
|
|
10
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
11
|
+
var source = arguments[i];
|
|
12
|
+
|
|
13
|
+
for (var key in source) {
|
|
14
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
15
|
+
target[key] = source[key];
|
|
16
|
+
}
|
|
37
17
|
}
|
|
18
|
+
}
|
|
38
19
|
|
|
39
|
-
|
|
40
|
-
});
|
|
20
|
+
return target;
|
|
41
21
|
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function _defineProperty(obj, key, value) {
|
|
45
|
-
if (key in obj) {
|
|
46
|
-
Object.defineProperty(obj, key, {
|
|
47
|
-
value: value,
|
|
48
|
-
enumerable: true,
|
|
49
|
-
configurable: true,
|
|
50
|
-
writable: true
|
|
51
|
-
});
|
|
52
|
-
} else {
|
|
53
|
-
obj[key] = value;
|
|
54
|
-
}
|
|
55
22
|
|
|
56
|
-
return
|
|
23
|
+
return _extends.apply(this, arguments);
|
|
57
24
|
}
|
|
58
25
|
|
|
59
26
|
// used in configure too, for consistency modify it there too
|
|
60
|
-
|
|
27
|
+
const multiplePlacements = {
|
|
61
28
|
enabled: 'Yes',
|
|
62
29
|
disabled: 'No',
|
|
63
30
|
perChoice: 'Set Per Choice'
|
|
64
31
|
}; // used to validate the config
|
|
65
32
|
|
|
66
|
-
|
|
33
|
+
const isCorrectResponseDuplicated = (choices, alternate) => {
|
|
67
34
|
if (choices.length < 1 || alternate.length < 1) {
|
|
68
35
|
return -1;
|
|
69
36
|
}
|
|
70
37
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
38
|
+
const stringChoices = JSON.stringify(choices.sort());
|
|
39
|
+
const stringAlternate = alternate.reduce((total, current) => current.length > 0 ? [...total, JSON.stringify(current.sort())] : total, []);
|
|
40
|
+
const foundIndexDuplicate = stringAlternate.findIndex(alternate => alternate.length && alternate === stringChoices);
|
|
74
41
|
return foundIndexDuplicate;
|
|
75
42
|
};
|
|
76
|
-
|
|
43
|
+
const isAlternateDuplicated = alternate => {
|
|
77
44
|
if (alternate.length <= 1) {
|
|
78
45
|
return -1;
|
|
79
46
|
}
|
|
80
47
|
|
|
81
|
-
|
|
82
|
-
|
|
48
|
+
const elementSet = new Set();
|
|
49
|
+
const stringAlternate = alternate.reduce((total, current) => current.length > 0 ? [...total, JSON.stringify(current.sort())] : total, []);
|
|
83
50
|
|
|
84
|
-
for (
|
|
51
|
+
for (let i = 0; i < stringAlternate.length; i++) {
|
|
85
52
|
if (elementSet.has(stringAlternate[i]) && stringAlternate[i]) {
|
|
86
53
|
return i;
|
|
87
54
|
}
|
|
@@ -118,52 +85,43 @@ var defaults = {
|
|
|
118
85
|
minRowHeight: '80px'
|
|
119
86
|
};
|
|
120
87
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
124
|
-
var {
|
|
88
|
+
const {
|
|
125
89
|
translator
|
|
126
90
|
} = Translator;
|
|
127
|
-
|
|
91
|
+
const getPartialScore = (correctResponse, builtCategories) => {
|
|
128
92
|
// in the resulted best scenario we make a sum with all the correct responses
|
|
129
93
|
// and all the placements
|
|
130
|
-
|
|
94
|
+
const {
|
|
131
95
|
placements,
|
|
132
96
|
score
|
|
133
|
-
} = builtCategories.reduce((acc,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
score: acc.score + choices.filter(ch => ch.correct).length
|
|
140
|
-
};
|
|
141
|
-
}, {
|
|
97
|
+
} = builtCategories.reduce((acc, {
|
|
98
|
+
choices: _choices = []
|
|
99
|
+
}) => ({
|
|
100
|
+
placements: acc.placements + _choices.length,
|
|
101
|
+
score: acc.score + _choices.filter(ch => ch.correct).length
|
|
102
|
+
}), {
|
|
142
103
|
placements: 0,
|
|
143
104
|
score: 0
|
|
144
105
|
}); // in the correct response, we make a sum of the max possible score
|
|
145
106
|
|
|
146
|
-
|
|
107
|
+
const {
|
|
147
108
|
maxScore
|
|
148
|
-
} = correctResponse.reduce((acc,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
maxScore: acc.maxScore + choices.length
|
|
154
|
-
};
|
|
155
|
-
}, {
|
|
109
|
+
} = correctResponse.reduce((acc, {
|
|
110
|
+
choices
|
|
111
|
+
}) => ({
|
|
112
|
+
maxScore: acc.maxScore + choices.length
|
|
113
|
+
}), {
|
|
156
114
|
maxScore: 0
|
|
157
115
|
}); // if there are any extra placements, we subtract from the obtained score
|
|
158
116
|
|
|
159
|
-
|
|
160
|
-
|
|
117
|
+
const extraPlacements = placements > maxScore ? placements - maxScore : 0;
|
|
118
|
+
const totalScore = (score - extraPlacements) / maxScore;
|
|
161
119
|
return totalScore < 0 ? 0 : parseFloat(totalScore.toFixed(2));
|
|
162
120
|
};
|
|
163
121
|
|
|
164
|
-
|
|
122
|
+
const getAlternates = correctResponse => correctResponse.map(c => c.alternateResponses).filter(alternate => alternate);
|
|
165
123
|
|
|
166
|
-
|
|
124
|
+
const getTotalScore = (question, session, env) => {
|
|
167
125
|
if (!session) {
|
|
168
126
|
return 0;
|
|
169
127
|
}
|
|
@@ -172,26 +130,26 @@ var getTotalScore = (question, session, env) => {
|
|
|
172
130
|
return 0;
|
|
173
131
|
}
|
|
174
132
|
|
|
175
|
-
|
|
133
|
+
const {
|
|
176
134
|
categories,
|
|
177
135
|
choices
|
|
178
136
|
} = question || {};
|
|
179
|
-
|
|
137
|
+
let {
|
|
180
138
|
correctResponse
|
|
181
139
|
} = question || {};
|
|
182
|
-
|
|
140
|
+
let {
|
|
183
141
|
answers
|
|
184
142
|
} = session || {};
|
|
185
143
|
answers = answers || [];
|
|
186
144
|
correctResponse = correctResponse || []; // this function is used in pie-ui/categorize as well, in order to get the best scenario
|
|
187
145
|
// so we get the best scenario and calculate the score
|
|
188
146
|
|
|
189
|
-
|
|
147
|
+
const {
|
|
190
148
|
categories: builtCategories,
|
|
191
149
|
correct
|
|
192
150
|
} = buildState(categories, choices, answers, correctResponse);
|
|
193
|
-
|
|
194
|
-
|
|
151
|
+
const alternates = getAlternates(correctResponse);
|
|
152
|
+
const enabled = partialScoring.enabled(question, env); // if there are any alternates, there will be no partial scoring!
|
|
195
153
|
|
|
196
154
|
if (enabled && !alternates.length) {
|
|
197
155
|
// we apply partial scoring
|
|
@@ -201,14 +159,14 @@ var getTotalScore = (question, session, env) => {
|
|
|
201
159
|
|
|
202
160
|
return correct ? 1 : 0;
|
|
203
161
|
};
|
|
204
|
-
|
|
162
|
+
const getCorrectness = (question, session, env) => {
|
|
205
163
|
return new Promise(resolve => {
|
|
206
164
|
if (env.mode === 'evaluate') {
|
|
207
|
-
|
|
165
|
+
const score = getTotalScore(question, session, env);
|
|
208
166
|
|
|
209
|
-
if (
|
|
167
|
+
if (score === 1) {
|
|
210
168
|
resolve('correct');
|
|
211
|
-
} else if (
|
|
169
|
+
} else if (score === 0) {
|
|
212
170
|
resolve('incorrect');
|
|
213
171
|
} else {
|
|
214
172
|
resolve('partially-correct');
|
|
@@ -218,13 +176,10 @@ var getCorrectness = (question, session, env) => {
|
|
|
218
176
|
}
|
|
219
177
|
});
|
|
220
178
|
};
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
});
|
|
226
|
-
};
|
|
227
|
-
var normalize = question => _objectSpread(_objectSpread({}, defaults), question);
|
|
179
|
+
const createDefaultModel = (model = {}) => new Promise(resolve => {
|
|
180
|
+
resolve(_extends({}, defaults, model));
|
|
181
|
+
});
|
|
182
|
+
const normalize = question => _extends({}, defaults, question);
|
|
228
183
|
/**
|
|
229
184
|
*
|
|
230
185
|
* @param {*} question
|
|
@@ -233,108 +188,102 @@ var normalize = question => _objectSpread(_objectSpread({}, defaults), question)
|
|
|
233
188
|
* @param {*} updateSession - optional - a function that will set the properties passed into it on the session.
|
|
234
189
|
*/
|
|
235
190
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
choices: filteredChoices
|
|
278
|
-
});
|
|
191
|
+
const model = (question, session, env, updateSession) => new Promise(async resolve => {
|
|
192
|
+
const normalizedQuestion = normalize(question);
|
|
193
|
+
const answerCorrectness = await getCorrectness(normalizedQuestion, session, env);
|
|
194
|
+
const {
|
|
195
|
+
mode,
|
|
196
|
+
role
|
|
197
|
+
} = env || {};
|
|
198
|
+
const {
|
|
199
|
+
categories,
|
|
200
|
+
categoriesPerRow,
|
|
201
|
+
choicesLabel,
|
|
202
|
+
choicesPosition,
|
|
203
|
+
correctResponse,
|
|
204
|
+
feedback,
|
|
205
|
+
feedbackEnabled,
|
|
206
|
+
promptEnabled,
|
|
207
|
+
prompt,
|
|
208
|
+
rowLabels,
|
|
209
|
+
rationaleEnabled,
|
|
210
|
+
rationale,
|
|
211
|
+
teacherInstructionsEnabled,
|
|
212
|
+
teacherInstructions,
|
|
213
|
+
language,
|
|
214
|
+
maxChoicesPerCategory,
|
|
215
|
+
extraCSSRules,
|
|
216
|
+
minRowHeight,
|
|
217
|
+
fontSizeFactor,
|
|
218
|
+
autoplayAudioEnabled,
|
|
219
|
+
completeAudioEnabled,
|
|
220
|
+
customAudioButton
|
|
221
|
+
} = normalizedQuestion;
|
|
222
|
+
let {
|
|
223
|
+
choices,
|
|
224
|
+
note
|
|
225
|
+
} = normalizedQuestion;
|
|
226
|
+
let fb;
|
|
227
|
+
const lockChoiceOrder = lockChoices(normalizedQuestion, session, env);
|
|
228
|
+
const filteredCorrectResponse = correctResponse.map(response => {
|
|
229
|
+
const filteredChoices = (response.choices || []).filter(choice => choice !== 'null');
|
|
230
|
+
return _extends({}, response, {
|
|
231
|
+
choices: filteredChoices
|
|
279
232
|
});
|
|
233
|
+
});
|
|
280
234
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
235
|
+
if (mode === 'evaluate' && feedbackEnabled) {
|
|
236
|
+
fb = await getFeedbackForCorrectness(answerCorrectness, feedback);
|
|
237
|
+
}
|
|
284
238
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
239
|
+
if (!lockChoiceOrder) {
|
|
240
|
+
choices = await getShuffledChoices(choices, session, updateSession, 'id');
|
|
241
|
+
}
|
|
288
242
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
243
|
+
if (!note) {
|
|
244
|
+
note = translator.t('common:commonCorrectAnswerWithAlternates', {
|
|
245
|
+
lng: language
|
|
246
|
+
});
|
|
247
|
+
}
|
|
294
248
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (role === 'instructor' && (mode === 'view' || mode === 'evaluate')) {
|
|
323
|
-
out.rationale = rationaleEnabled ? rationale : null;
|
|
324
|
-
out.teacherInstructions = teacherInstructionsEnabled ? teacherInstructions : null;
|
|
325
|
-
} else {
|
|
326
|
-
out.rationale = null;
|
|
327
|
-
out.teacherInstructions = null;
|
|
328
|
-
}
|
|
249
|
+
const alternates = getAlternates(filteredCorrectResponse);
|
|
250
|
+
const out = {
|
|
251
|
+
categories: categories || [],
|
|
252
|
+
categoriesPerRow: categoriesPerRow || 2,
|
|
253
|
+
maxChoicesPerCategory,
|
|
254
|
+
correctness: answerCorrectness,
|
|
255
|
+
choices: choices || [],
|
|
256
|
+
choicesLabel: choicesLabel || '',
|
|
257
|
+
choicesPosition,
|
|
258
|
+
disabled: mode !== 'gather',
|
|
259
|
+
feedback: fb,
|
|
260
|
+
lockChoiceOrder,
|
|
261
|
+
prompt: promptEnabled ? prompt : null,
|
|
262
|
+
rowLabels,
|
|
263
|
+
note,
|
|
264
|
+
env,
|
|
265
|
+
showNote: alternates && alternates.length > 0,
|
|
266
|
+
correctResponse: mode === 'evaluate' ? filteredCorrectResponse : undefined,
|
|
267
|
+
language,
|
|
268
|
+
extraCSSRules,
|
|
269
|
+
fontSizeFactor,
|
|
270
|
+
minRowHeight: minRowHeight,
|
|
271
|
+
autoplayAudioEnabled,
|
|
272
|
+
completeAudioEnabled,
|
|
273
|
+
customAudioButton
|
|
274
|
+
};
|
|
329
275
|
|
|
330
|
-
|
|
331
|
-
|
|
276
|
+
if (role === 'instructor' && (mode === 'view' || mode === 'evaluate')) {
|
|
277
|
+
out.rationale = rationaleEnabled ? rationale : null;
|
|
278
|
+
out.teacherInstructions = teacherInstructionsEnabled ? teacherInstructions : null;
|
|
279
|
+
} else {
|
|
280
|
+
out.rationale = null;
|
|
281
|
+
out.teacherInstructions = null;
|
|
282
|
+
}
|
|
332
283
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}());
|
|
337
|
-
var outcome = (question, session, env) => {
|
|
284
|
+
resolve(out);
|
|
285
|
+
});
|
|
286
|
+
const outcome = (question, session, env) => {
|
|
338
287
|
if (env.mode !== 'evaluate') {
|
|
339
288
|
return Promise.reject(new Error('Can not call outcome when mode is not evaluate'));
|
|
340
289
|
} else {
|
|
@@ -346,15 +295,15 @@ var outcome = (question, session, env) => {
|
|
|
346
295
|
});
|
|
347
296
|
}
|
|
348
297
|
};
|
|
349
|
-
|
|
298
|
+
const createCorrectResponseSession = (question, env) => {
|
|
350
299
|
return new Promise(resolve => {
|
|
351
|
-
|
|
300
|
+
const {
|
|
352
301
|
mode,
|
|
353
302
|
role
|
|
354
303
|
} = env || {};
|
|
355
304
|
|
|
356
305
|
if (mode !== 'evaluate' && role === 'instructor') {
|
|
357
|
-
|
|
306
|
+
const {
|
|
358
307
|
correctResponse
|
|
359
308
|
} = question;
|
|
360
309
|
resolve({
|
|
@@ -367,87 +316,85 @@ var createCorrectResponseSession = (question, env) => {
|
|
|
367
316
|
});
|
|
368
317
|
}; // remove all html tags
|
|
369
318
|
|
|
370
|
-
|
|
319
|
+
const getInnerText = html => (html || '').replaceAll(/<[^>]*>/g, ''); // remove all html tags except img, iframe and source tag for audio
|
|
371
320
|
|
|
372
321
|
|
|
373
|
-
|
|
322
|
+
const getContent = html => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');
|
|
374
323
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
378
|
-
var {
|
|
324
|
+
const validate = (model = {}, config = {}) => {
|
|
325
|
+
const {
|
|
379
326
|
categories,
|
|
380
327
|
choices,
|
|
381
328
|
correctResponse,
|
|
382
329
|
maxAnswerChoices
|
|
383
330
|
} = model;
|
|
384
|
-
|
|
331
|
+
const {
|
|
385
332
|
minChoices = 1,
|
|
386
333
|
minCategories = 1,
|
|
387
334
|
maxCategories = 12,
|
|
388
335
|
maxLengthPerChoice = 300,
|
|
389
336
|
maxLengthPerCategory = 150
|
|
390
337
|
} = config;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
338
|
+
const reversedChoices = [...(choices || [])].reverse();
|
|
339
|
+
const errors = {};
|
|
340
|
+
const choicesErrors = {};
|
|
341
|
+
const categoriesErrors = {};
|
|
395
342
|
['teacherInstructions', 'prompt', 'rationale'].forEach(field => {
|
|
396
343
|
var _config$field;
|
|
397
344
|
|
|
398
|
-
if ((_config$field = config[field])
|
|
345
|
+
if ((_config$field = config[field]) != null && _config$field.required && !getContent(model[field])) {
|
|
399
346
|
errors[field] = 'This field is required.';
|
|
400
347
|
}
|
|
401
348
|
});
|
|
402
349
|
(categories || []).forEach(category => {
|
|
403
|
-
|
|
350
|
+
const {
|
|
404
351
|
id,
|
|
405
352
|
label
|
|
406
353
|
} = category;
|
|
407
354
|
|
|
408
355
|
if (getInnerText(label).length > maxLengthPerCategory) {
|
|
409
|
-
categoriesErrors[id] =
|
|
356
|
+
categoriesErrors[id] = `Category labels should be no more than ${maxLengthPerCategory} characters long.`;
|
|
410
357
|
}
|
|
411
358
|
});
|
|
412
359
|
(reversedChoices || []).forEach((choice, index) => {
|
|
413
|
-
|
|
360
|
+
const {
|
|
414
361
|
id,
|
|
415
362
|
content
|
|
416
363
|
} = choice;
|
|
417
364
|
|
|
418
365
|
if (getInnerText(content).length > maxLengthPerChoice) {
|
|
419
|
-
choicesErrors[id] =
|
|
366
|
+
choicesErrors[id] = `Tokens should be no more than ${maxLengthPerChoice} characters long.`;
|
|
420
367
|
}
|
|
421
368
|
|
|
422
369
|
if (!getContent(content)) {
|
|
423
370
|
choicesErrors[id] = 'Tokens should not be empty.';
|
|
424
371
|
} else {
|
|
425
|
-
|
|
372
|
+
const identicalAnswer = reversedChoices.slice(index + 1).some(c => c.content === content);
|
|
426
373
|
|
|
427
374
|
if (identicalAnswer) {
|
|
428
375
|
choicesErrors[id] = 'Tokens content should be unique.';
|
|
429
376
|
}
|
|
430
377
|
}
|
|
431
378
|
});
|
|
432
|
-
|
|
433
|
-
|
|
379
|
+
const nbOfCategories = (categories || []).length;
|
|
380
|
+
const nbOfChoices = (choices || []).length;
|
|
434
381
|
|
|
435
382
|
if (nbOfCategories > maxCategories) {
|
|
436
|
-
errors.categoriesError =
|
|
383
|
+
errors.categoriesError = `No more than ${maxCategories} categories should be defined.`;
|
|
437
384
|
} else if (nbOfCategories < minCategories) {
|
|
438
|
-
errors.categoriesError =
|
|
385
|
+
errors.categoriesError = `There should be at least ${minCategories} category defined.`;
|
|
439
386
|
}
|
|
440
387
|
|
|
441
388
|
if (nbOfChoices < minChoices) {
|
|
442
|
-
errors.choicesError =
|
|
389
|
+
errors.choicesError = `There should be at least ${minChoices} choices defined.`;
|
|
443
390
|
} else if (nbOfChoices > maxAnswerChoices) {
|
|
444
|
-
errors.choicesError =
|
|
391
|
+
errors.choicesError = `No more than ${maxAnswerChoices} choices should be defined.`;
|
|
445
392
|
}
|
|
446
393
|
|
|
447
394
|
if (nbOfChoices && nbOfCategories) {
|
|
448
|
-
|
|
395
|
+
let hasAssociations = false;
|
|
449
396
|
(correctResponse || []).forEach(response => {
|
|
450
|
-
|
|
397
|
+
const {
|
|
451
398
|
choices = [],
|
|
452
399
|
alternateResponses = []
|
|
453
400
|
} = response;
|
|
@@ -462,10 +409,10 @@ var validate = function validate() {
|
|
|
462
409
|
});
|
|
463
410
|
}
|
|
464
411
|
});
|
|
465
|
-
|
|
466
|
-
|
|
412
|
+
let duplicateAlternateIndex = -1;
|
|
413
|
+
let duplicateCategory = '';
|
|
467
414
|
(correctResponse || []).forEach(response => {
|
|
468
|
-
|
|
415
|
+
const {
|
|
469
416
|
choices = [],
|
|
470
417
|
alternateResponses = [],
|
|
471
418
|
category
|