@pie-element/graphing 8.0.1-next.0 → 8.0.1
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/controller/package.json +3 -3
- package/esm/configure.js +10338 -0
- package/esm/configure.js.map +1 -0
- package/esm/controller.js +846 -0
- package/esm/controller.js.map +1 -0
- package/esm/element.js +7623 -0
- package/esm/element.js.map +1 -0
- package/package.json +24 -7
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
3
|
+
import uniqWith from 'lodash/uniqWith';
|
|
4
|
+
import isEmpty from 'lodash/isEmpty';
|
|
5
|
+
import isEqual from 'lodash/isEqual';
|
|
6
|
+
import lodash from 'lodash';
|
|
7
|
+
import differenceWith from 'lodash/differenceWith';
|
|
8
|
+
import { pointsToABC, pointsToAForAbsolute, pointsToABForExponential, getAmplitudeAndFreq } from '@pie-lib/graphing-utils';
|
|
9
|
+
import { partialScoring } from '@pie-lib/controller-utils';
|
|
10
|
+
|
|
11
|
+
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
12
|
+
if (source == null) return {};
|
|
13
|
+
var target = {};
|
|
14
|
+
var sourceKeys = Object.keys(source);
|
|
15
|
+
var key, i;
|
|
16
|
+
|
|
17
|
+
for (i = 0; i < sourceKeys.length; i++) {
|
|
18
|
+
key = sourceKeys[i];
|
|
19
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
20
|
+
target[key] = source[key];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return target;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function _objectWithoutProperties(source, excluded) {
|
|
27
|
+
if (source == null) return {};
|
|
28
|
+
var target = _objectWithoutPropertiesLoose(source, excluded);
|
|
29
|
+
var key, i;
|
|
30
|
+
|
|
31
|
+
if (Object.getOwnPropertySymbols) {
|
|
32
|
+
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
|
|
33
|
+
|
|
34
|
+
for (i = 0; i < sourceSymbolKeys.length; i++) {
|
|
35
|
+
key = sourceSymbolKeys[i];
|
|
36
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
37
|
+
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
|
38
|
+
target[key] = source[key];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return target;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function _defineProperty(obj, key, value) {
|
|
46
|
+
if (key in obj) {
|
|
47
|
+
Object.defineProperty(obj, key, {
|
|
48
|
+
value: value,
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
writable: true
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
obj[key] = value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return obj;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// TODO: This is lifted from @pie-lib/graphing, however importing this will break a controller build because it has jsx source in that package.
|
|
61
|
+
var allTools = ['circle', 'line', 'label', 'parabola', 'point', 'polygon', 'ray', 'segment', 'sine', 'vector' // 'absolute', // - not available as default
|
|
62
|
+
// 'exponential', // - not available as default
|
|
63
|
+
];
|
|
64
|
+
/* model defaults */
|
|
65
|
+
|
|
66
|
+
var defaults = {
|
|
67
|
+
answers: {
|
|
68
|
+
correctAnswer: {
|
|
69
|
+
name: 'Correct Answer',
|
|
70
|
+
marks: []
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
arrows: {
|
|
74
|
+
left: true,
|
|
75
|
+
right: true,
|
|
76
|
+
up: true,
|
|
77
|
+
down: true
|
|
78
|
+
},
|
|
79
|
+
backgroundMarks: [],
|
|
80
|
+
coordinatesOnHover: false,
|
|
81
|
+
defaultGridConfiguration: 0,
|
|
82
|
+
domain: {
|
|
83
|
+
min: -5,
|
|
84
|
+
max: 5,
|
|
85
|
+
step: 1,
|
|
86
|
+
labelStep: 1,
|
|
87
|
+
axisLabel: 'x'
|
|
88
|
+
},
|
|
89
|
+
graph: {
|
|
90
|
+
width: 500,
|
|
91
|
+
height: 500
|
|
92
|
+
},
|
|
93
|
+
includeAxes: true,
|
|
94
|
+
labels: {},
|
|
95
|
+
labelsEnabled: true,
|
|
96
|
+
padding: true,
|
|
97
|
+
prompt: '',
|
|
98
|
+
promptEnabled: true,
|
|
99
|
+
range: {
|
|
100
|
+
min: -5,
|
|
101
|
+
max: 5,
|
|
102
|
+
step: 1,
|
|
103
|
+
labelStep: 1,
|
|
104
|
+
axisLabel: 'y'
|
|
105
|
+
},
|
|
106
|
+
rationale: '',
|
|
107
|
+
rationaleEnabled: true,
|
|
108
|
+
standardGrid: false,
|
|
109
|
+
studentInstructionsEnabled: true,
|
|
110
|
+
teacherInstructions: '',
|
|
111
|
+
teacherInstructionsEnabled: true,
|
|
112
|
+
title: '',
|
|
113
|
+
titleEnabled: true,
|
|
114
|
+
toolbarTools: allTools
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
function ownKeys$1(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
118
|
+
|
|
119
|
+
function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$1(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$1(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
120
|
+
var equalPoint = (A, B) => {
|
|
121
|
+
A = _objectSpread$1({}, A);
|
|
122
|
+
B = _objectSpread$1({}, B);
|
|
123
|
+
|
|
124
|
+
if (A.label || B.label) {
|
|
125
|
+
isEqual(A.label, B.label);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return isEqual(A.x, B.x) && isEqual(A.y, B.y);
|
|
129
|
+
};
|
|
130
|
+
var equalSegment = (segment1, segment2) => {
|
|
131
|
+
// A.from = B.from, A.to = B.to OR A.from = B.to, A.to = B.from
|
|
132
|
+
// x1 = x3 & y1 = y3 & x2 = x4 & y2 = y4
|
|
133
|
+
return isEqual(segment1.from, segment2.from) && isEqual(segment1.to, segment2.to) || isEqual(segment1.to, segment2.from) && isEqual(segment1.from, segment2.to);
|
|
134
|
+
};
|
|
135
|
+
var equalVector = (vector1, vector2) => {
|
|
136
|
+
// A.from = B.from, A.to = B.to;
|
|
137
|
+
// x1 = x3 & y1 = y3 & x2 = x4 & y2 = y4
|
|
138
|
+
return isEqual(vector1.from, vector2.from) && isEqual(vector1.to, vector2.to);
|
|
139
|
+
}; // this function is implemented in configure as well
|
|
140
|
+
|
|
141
|
+
var sortedAnswers = answers => Object.keys(answers || {}).sort().reduce((result, key) => {
|
|
142
|
+
if (key !== 'correctAnswer') {
|
|
143
|
+
result[key] = answers[key];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return result;
|
|
147
|
+
}, {});
|
|
148
|
+
|
|
149
|
+
var returnLineEquationCoefficients = line => {
|
|
150
|
+
line = _objectSpread$1(_objectSpread$1({}, line), {}, {
|
|
151
|
+
to: _objectSpread$1({}, line.to),
|
|
152
|
+
from: _objectSpread$1({}, line.from)
|
|
153
|
+
});
|
|
154
|
+
var xA = line.from.x;
|
|
155
|
+
var yA = line.from.y;
|
|
156
|
+
var xB = line.to.x;
|
|
157
|
+
var yB = line.to.y;
|
|
158
|
+
return {
|
|
159
|
+
a: yB - yA,
|
|
160
|
+
b: xA - xB,
|
|
161
|
+
c: xB * yA - xA * yB
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
var getSignificantDecimals = number => Math.round(number * 10000) / 10000;
|
|
166
|
+
|
|
167
|
+
var equalLine = (line1, line2) => {
|
|
168
|
+
// line equation: ax + by + c = 0
|
|
169
|
+
// 2 lines are equal if a1/a2 = b1/b2 = c1/c2, where a, b, c are the coefficients in line equation
|
|
170
|
+
// line equation knowing 2 points: (y - yA) / (yB - yA) = (x - xA) / (xB - xA)
|
|
171
|
+
// extending this equation, we get: x * (yB - yA) + y * (xA - xB) + (xB * yA - xA * yB) = 0
|
|
172
|
+
// where a = yB - yA; b = xA - xB; c = xB * yA - xA * yB
|
|
173
|
+
var {
|
|
174
|
+
a: a1,
|
|
175
|
+
b: b1,
|
|
176
|
+
c: c1
|
|
177
|
+
} = returnLineEquationCoefficients(line1);
|
|
178
|
+
var {
|
|
179
|
+
a: a2,
|
|
180
|
+
b: b2,
|
|
181
|
+
c: c2
|
|
182
|
+
} = returnLineEquationCoefficients(line2);
|
|
183
|
+
var proportions = [];
|
|
184
|
+
|
|
185
|
+
if (a2 !== 0) {
|
|
186
|
+
proportions.push(getSignificantDecimals(a1 / a2));
|
|
187
|
+
} else if (a1 !== a2) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (b2 !== 0) {
|
|
192
|
+
proportions.push(getSignificantDecimals(b1 / b2));
|
|
193
|
+
} else if (b1 !== b2) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (c2 !== 0) {
|
|
198
|
+
proportions.push(getSignificantDecimals(c1 / c2));
|
|
199
|
+
} else if (c1 !== c2) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return lodash.uniq(proportions).length === 1; // (y2 - y1)/(x2 - x1) = (y4 - y3)/(x4 - x3);
|
|
204
|
+
// return ((Math.abs((line1.to.y - line1.from.y) / (line1.to.x - line1.from.x))) === (Math.abs((line2.to.y - line2.from.y) / (line2.to.x - line2.from.x))));
|
|
205
|
+
};
|
|
206
|
+
var equalRay = (ray1, ray2) => {
|
|
207
|
+
ray1 = _objectSpread$1(_objectSpread$1({}, ray1), {}, {
|
|
208
|
+
to: _objectSpread$1({}, ray1.to),
|
|
209
|
+
from: _objectSpread$1({}, ray1.from)
|
|
210
|
+
});
|
|
211
|
+
ray2 = _objectSpread$1(_objectSpread$1({}, ray2), {}, {
|
|
212
|
+
to: _objectSpread$1({}, ray2.to),
|
|
213
|
+
from: _objectSpread$1({}, ray2.from)
|
|
214
|
+
}); // slope: m = (y2-y1)/(x2-x1)
|
|
215
|
+
// slope & x1 = x3 & y1 = y3 & angle between (x1, y1) (x2, y2) is same as angle between (x3, y3) (x4, y4)
|
|
216
|
+
|
|
217
|
+
var mRay1 = (ray1.to.y - ray1.from.y) / (ray1.to.x - ray1.from.x);
|
|
218
|
+
var mRay2 = (ray2.to.y - ray2.from.y) / (ray2.to.x - ray2.from.x);
|
|
219
|
+
var angleRay1 = Math.atan2(ray1.to.y - ray1.from.y, ray1.to.x - ray1.from.x) * 180 / Math.PI;
|
|
220
|
+
var angleRay2 = Math.atan2(ray2.to.y - ray2.from.y, ray2.to.x - ray2.from.x) * 180 / Math.PI;
|
|
221
|
+
return mRay1 === mRay2 && ray1.from.x === ray2.from.x && ray1.from.y === ray2.from.y && angleRay1 === angleRay2;
|
|
222
|
+
};
|
|
223
|
+
var constructSegmentsFromPoints = points => {
|
|
224
|
+
// takes the list of points that represent a polygon and transforms it into a list of segments; eg.:
|
|
225
|
+
// points: A, B, C, D => segments: AB, BC, CD, DA
|
|
226
|
+
return (points || []).map((point, index) => ({
|
|
227
|
+
from: point,
|
|
228
|
+
to: points[(index + 1) % points.length]
|
|
229
|
+
}));
|
|
230
|
+
};
|
|
231
|
+
var removeDuplicateSegments = segments => {
|
|
232
|
+
segments = segments || []; // removes segments that are duplicates; eg. These segments are the same, so one will be removed:
|
|
233
|
+
// segment1: from: { x: 1, y: 1 }, to: { x: 2, y: 1 }
|
|
234
|
+
// segment2: from: { x: 2, y: 1 }, to: { x: 1, y: 1 }
|
|
235
|
+
|
|
236
|
+
return uniqWith(segments, (s1, s2) => equalSegment(s1, s2));
|
|
237
|
+
};
|
|
238
|
+
var removeInvalidSegments = segments => {
|
|
239
|
+
segments = segments || []; // removes segments that start in a point and end in the same point (eg.: from: { x: 1, y: 1 }, to: { x: 1, y: 1 })
|
|
240
|
+
|
|
241
|
+
return segments.filter(segment => !isEqual(segment.from, segment.to));
|
|
242
|
+
};
|
|
243
|
+
var equalPolygon = (poly1, poly2) => {
|
|
244
|
+
var {
|
|
245
|
+
points: points1
|
|
246
|
+
} = poly1;
|
|
247
|
+
var {
|
|
248
|
+
points: points2
|
|
249
|
+
} = poly2; // generate segments
|
|
250
|
+
|
|
251
|
+
var segments1 = constructSegmentsFromPoints(points1);
|
|
252
|
+
var segments2 = constructSegmentsFromPoints(points2);
|
|
253
|
+
var segments1NoDuplicates = removeDuplicateSegments(removeInvalidSegments(segments1));
|
|
254
|
+
var segments2NoDuplicates = removeDuplicateSegments(removeInvalidSegments(segments2));
|
|
255
|
+
var differentSegments1 = differenceWith(segments1NoDuplicates, segments2NoDuplicates, equalSegment);
|
|
256
|
+
var differentSegments2 = differenceWith(segments2NoDuplicates, segments1NoDuplicates, equalSegment);
|
|
257
|
+
return (!differentSegments1 || !differentSegments1.length) && (!differentSegments2 || !differentSegments2.length);
|
|
258
|
+
};
|
|
259
|
+
var equalCircle = (c1, c2) => {
|
|
260
|
+
c1 = _objectSpread$1(_objectSpread$1({}, c1), {}, {
|
|
261
|
+
root: _objectSpread$1({}, c1.root),
|
|
262
|
+
edge: _objectSpread$1({}, c1.edge)
|
|
263
|
+
});
|
|
264
|
+
c2 = _objectSpread$1(_objectSpread$1({}, c2), {}, {
|
|
265
|
+
root: _objectSpread$1({}, c2.root),
|
|
266
|
+
edge: _objectSpread$1({}, c2.edge)
|
|
267
|
+
});
|
|
268
|
+
var equalRootAndEdge = isEqual(c2.edge, c1.edge) && isEqual(c2.root, c1.root); // if both edge and root are the same, it means the shapes are exactly the same
|
|
269
|
+
|
|
270
|
+
if (equalRootAndEdge) return true;
|
|
271
|
+
var rC1 = Math.sqrt((c1.edge.x - c1.root.x) ** 2 + (c1.edge.y - c1.root.y) ** 2);
|
|
272
|
+
var rC2 = Math.sqrt((c2.edge.x - c2.root.x) ** 2 + (c2.edge.y - c2.root.y) ** 2); // if both root and radius are the same, it means the shapes are equal
|
|
273
|
+
|
|
274
|
+
return isEqual(c2.root, c1.root) && isEqual(rC1, rC2);
|
|
275
|
+
};
|
|
276
|
+
var equalSine = (sine1, sine2) => {
|
|
277
|
+
var getPoints = _ref => {
|
|
278
|
+
var {
|
|
279
|
+
root,
|
|
280
|
+
edge
|
|
281
|
+
} = _ref;
|
|
282
|
+
root = _objectSpread$1({}, root);
|
|
283
|
+
edge = _objectSpread$1({}, edge);
|
|
284
|
+
var {
|
|
285
|
+
amplitude,
|
|
286
|
+
freq
|
|
287
|
+
} = getAmplitudeAndFreq(root, edge); // the height of the sine wave
|
|
288
|
+
|
|
289
|
+
var tY = Math.abs(root.y - edge.y) * 2; // the distance on x axis between edge and root
|
|
290
|
+
|
|
291
|
+
var tXRoot = Math.abs(root.x - edge.x); // the distance on x axis between 2 edges for sine wave (min & max)
|
|
292
|
+
|
|
293
|
+
var tX = tXRoot * 2; // the first edge placed east side of root
|
|
294
|
+
|
|
295
|
+
var edgeAboveZeroX = edge.x;
|
|
296
|
+
var edgeAboveZeroY = edge.y; // if edge less then 0, find out the appropriate edge placed east side of zero (0)
|
|
297
|
+
|
|
298
|
+
while (edgeAboveZeroX < 0 && tX !== 0) {
|
|
299
|
+
edgeAboveZeroX = edgeAboveZeroX + tX;
|
|
300
|
+
edgeAboveZeroY = edgeAboveZeroY < root.y ? edgeAboveZeroY + tY : edgeAboveZeroY - tY;
|
|
301
|
+
} // if edge more then 0, find out the appropriate edge placed east side of zero (0)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
while (edgeAboveZeroX - tX > 0 && tX !== 0) {
|
|
305
|
+
edgeAboveZeroX = edgeAboveZeroX - tX;
|
|
306
|
+
edgeAboveZeroY = edgeAboveZeroY < root.y ? edgeAboveZeroY + tY : edgeAboveZeroY - tY;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
amplitude: getSignificantDecimals(amplitude),
|
|
311
|
+
freq: getSignificantDecimals(freq),
|
|
312
|
+
min: getSignificantDecimals(edge.y < root.y ? edge.y : edge.y - tY),
|
|
313
|
+
max: getSignificantDecimals(edge.y < root.y ? edge.y + tY : edge.y),
|
|
314
|
+
edgeAboveZeroX: getSignificantDecimals(edgeAboveZeroX),
|
|
315
|
+
edgeAboveZeroY: getSignificantDecimals(edgeAboveZeroY)
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
var studentAnswerBpY = getPoints(sine1);
|
|
320
|
+
var correctAnswerBpY = getPoints(sine2);
|
|
321
|
+
var {
|
|
322
|
+
amplitude: amplitude1,
|
|
323
|
+
freq: freq1,
|
|
324
|
+
min: min1,
|
|
325
|
+
max: max1,
|
|
326
|
+
edgeAboveZeroX: edgeAboveZeroX1,
|
|
327
|
+
edgeAboveZeroY: edgeAboveZeroY1
|
|
328
|
+
} = studentAnswerBpY;
|
|
329
|
+
var {
|
|
330
|
+
amplitude: amplitude2,
|
|
331
|
+
freq: freq2,
|
|
332
|
+
min: min2,
|
|
333
|
+
max: max2,
|
|
334
|
+
edgeAboveZeroX: edgeAboveZeroX2,
|
|
335
|
+
edgeAboveZeroY: edgeAboveZeroY2
|
|
336
|
+
} = correctAnswerBpY;
|
|
337
|
+
return Math.abs(amplitude1) === Math.abs(amplitude2) && Math.abs(freq1) === Math.abs(freq2) && min1 === min2 && max1 === max2 && edgeAboveZeroX1 === edgeAboveZeroX2 && edgeAboveZeroY1 === edgeAboveZeroY2; // rootDiff1 === rootDiff2);
|
|
338
|
+
};
|
|
339
|
+
var equalParabola = (p1, p2) => {
|
|
340
|
+
var {
|
|
341
|
+
edge: edgeP1
|
|
342
|
+
} = p1;
|
|
343
|
+
var {
|
|
344
|
+
edge: edgeP2
|
|
345
|
+
} = p2;
|
|
346
|
+
var {
|
|
347
|
+
root: rootP1
|
|
348
|
+
} = p1;
|
|
349
|
+
var {
|
|
350
|
+
root: rootP2
|
|
351
|
+
} = p2;
|
|
352
|
+
rootP1 = _objectSpread$1({}, rootP1);
|
|
353
|
+
rootP2 = _objectSpread$1({}, rootP2);
|
|
354
|
+
|
|
355
|
+
var p1edge = edgeP1 || _objectSpread$1({}, rootP1);
|
|
356
|
+
|
|
357
|
+
var p2edge = edgeP2 || _objectSpread$1({}, rootP2);
|
|
358
|
+
|
|
359
|
+
var p1mirrorEdge = {
|
|
360
|
+
x: rootP1.x - (p1edge.x - rootP1.x),
|
|
361
|
+
y: p1edge.y
|
|
362
|
+
};
|
|
363
|
+
var p2mirrorEdge = {
|
|
364
|
+
x: rootP2.x - (p2edge.x - rootP2.x),
|
|
365
|
+
y: p2edge.y
|
|
366
|
+
};
|
|
367
|
+
if (!edgeP1 || !edgeP2) return false;
|
|
368
|
+
var {
|
|
369
|
+
a: a1,
|
|
370
|
+
b: b1,
|
|
371
|
+
c: c1
|
|
372
|
+
} = pointsToABC(rootP1, edgeP1, p1mirrorEdge);
|
|
373
|
+
var {
|
|
374
|
+
a: a2,
|
|
375
|
+
b: b2,
|
|
376
|
+
c: c2
|
|
377
|
+
} = pointsToABC(rootP2, edgeP2, p2mirrorEdge); // sometimes numbers have this form: 1.00000000002 because of calculations, we have to round them
|
|
378
|
+
|
|
379
|
+
var round = number => Math.round(number * 10000) / 10000;
|
|
380
|
+
|
|
381
|
+
return round(a1) === round(a2) && round(b1) === round(b2) && round(c1) === round(c2);
|
|
382
|
+
};
|
|
383
|
+
/*
|
|
384
|
+
* Function to check if given two points for absolute function
|
|
385
|
+
* for correct answer and student answer are equal or not.
|
|
386
|
+
* @param p1 - student answer
|
|
387
|
+
* @param p2 - correct answer
|
|
388
|
+
* */
|
|
389
|
+
|
|
390
|
+
var equalAbsolute = (p1, p2) => {
|
|
391
|
+
var {
|
|
392
|
+
edge: edgeP1
|
|
393
|
+
} = p1;
|
|
394
|
+
var {
|
|
395
|
+
edge: edgeP2
|
|
396
|
+
} = p2;
|
|
397
|
+
var {
|
|
398
|
+
root: rootP1
|
|
399
|
+
} = p1;
|
|
400
|
+
var {
|
|
401
|
+
root: rootP2
|
|
402
|
+
} = p2;
|
|
403
|
+
rootP1 = _objectSpread$1({}, rootP1);
|
|
404
|
+
rootP2 = _objectSpread$1({}, rootP2);
|
|
405
|
+
|
|
406
|
+
var p1edge = edgeP1 || _objectSpread$1({}, rootP1);
|
|
407
|
+
|
|
408
|
+
var p2edge = edgeP2 || _objectSpread$1({}, rootP2);
|
|
409
|
+
|
|
410
|
+
var p1a1 = pointsToAForAbsolute(rootP1, p1edge);
|
|
411
|
+
var p2a2 = pointsToAForAbsolute(rootP2, p2edge); // if both root and a value are equal
|
|
412
|
+
|
|
413
|
+
return isEqual(rootP2, rootP1) && isEqual(p2a2, p1a1);
|
|
414
|
+
};
|
|
415
|
+
/*
|
|
416
|
+
* Function to check if given two points for exponential function
|
|
417
|
+
* for correct answer and student answer are equal or not.
|
|
418
|
+
* @param p1 - student answer
|
|
419
|
+
* @param p2 - correct answer
|
|
420
|
+
* */
|
|
421
|
+
|
|
422
|
+
var equalExponential = (p1, p2) => {
|
|
423
|
+
var {
|
|
424
|
+
edge: edgeP1
|
|
425
|
+
} = p1;
|
|
426
|
+
var {
|
|
427
|
+
edge: edgeP2
|
|
428
|
+
} = p2;
|
|
429
|
+
var {
|
|
430
|
+
root: rootP1
|
|
431
|
+
} = p1;
|
|
432
|
+
var {
|
|
433
|
+
root: rootP2
|
|
434
|
+
} = p2;
|
|
435
|
+
rootP1 = _objectSpread$1({}, rootP1);
|
|
436
|
+
rootP2 = _objectSpread$1({}, rootP2);
|
|
437
|
+
|
|
438
|
+
var p1edge = edgeP1 || _objectSpread$1({}, rootP1);
|
|
439
|
+
|
|
440
|
+
var p2edge = edgeP2 || _objectSpread$1({}, rootP2);
|
|
441
|
+
|
|
442
|
+
var {
|
|
443
|
+
a1,
|
|
444
|
+
b1
|
|
445
|
+
} = pointsToABForExponential(rootP1, p1edge);
|
|
446
|
+
var {
|
|
447
|
+
a2,
|
|
448
|
+
b2
|
|
449
|
+
} = pointsToABForExponential(rootP2, p2edge); // if both a and b value are equal
|
|
450
|
+
|
|
451
|
+
return isEqual(a2, a1) && isEqual(b2, b1);
|
|
452
|
+
};
|
|
453
|
+
var equalMarks = {
|
|
454
|
+
circle: (sessAnswer, mark) => equalCircle(sessAnswer, mark),
|
|
455
|
+
line: (sessAnswer, mark) => equalLine(sessAnswer, mark),
|
|
456
|
+
parabola: (sessAnswer, mark) => equalParabola(sessAnswer, mark),
|
|
457
|
+
absolute: (sessAnswer, mark) => equalAbsolute(sessAnswer, mark),
|
|
458
|
+
exponential: (sessAnswer, mark) => equalExponential(sessAnswer, mark),
|
|
459
|
+
point: (sessAnswer, mark) => equalPoint(sessAnswer, mark),
|
|
460
|
+
polygon: (sessAnswer, poly) => equalPolygon(sessAnswer, poly),
|
|
461
|
+
ray: (sessAnswer, mark) => equalRay(sessAnswer, mark),
|
|
462
|
+
segment: (sessAnswer, mark) => equalSegment(sessAnswer, mark),
|
|
463
|
+
sine: (sessAnswer, mark) => equalSine(sessAnswer, mark),
|
|
464
|
+
vector: (sessAnswer, mark) => equalVector(sessAnswer, mark)
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
var _excluded = ["defaultTool", "extraCSSRules", "prompt", "promptEnabled", "graph", "answers", "toolbarTools"];
|
|
468
|
+
|
|
469
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
470
|
+
|
|
471
|
+
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; }
|
|
472
|
+
var log = debug('@pie-element:graphing:controller');
|
|
473
|
+
|
|
474
|
+
var initializeGraphMap = () => ({
|
|
475
|
+
point: [],
|
|
476
|
+
segment: [],
|
|
477
|
+
line: [],
|
|
478
|
+
ray: [],
|
|
479
|
+
vector: [],
|
|
480
|
+
polygon: [],
|
|
481
|
+
circle: [],
|
|
482
|
+
sine: [],
|
|
483
|
+
parabola: [],
|
|
484
|
+
absolute: [],
|
|
485
|
+
exponential: []
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
var graphObjectsOrder = {
|
|
489
|
+
incorrect: 0,
|
|
490
|
+
correct: 1,
|
|
491
|
+
missing: 2
|
|
492
|
+
};
|
|
493
|
+
var compareMarks = (mark1, mark2) => {
|
|
494
|
+
// marks can be compared with equalMarks[type] function only if they have the same type;
|
|
495
|
+
// if type is different, they are clearly not equal
|
|
496
|
+
return !!(mark1 && mark2 && mark1.type === mark2.type && equalMarks[mark1.type] && equalMarks[mark1.type](mark1, mark2));
|
|
497
|
+
};
|
|
498
|
+
var comparLabelMarks = (mark1, mark2) => {
|
|
499
|
+
return mark1.label === mark2.label ? 'correct' : 'incorrect';
|
|
500
|
+
};
|
|
501
|
+
var getAnswerCorrected = _ref => {
|
|
502
|
+
var {
|
|
503
|
+
sessionAnswers,
|
|
504
|
+
marks: correctAnswers
|
|
505
|
+
} = _ref;
|
|
506
|
+
sessionAnswers = sessionAnswers || [];
|
|
507
|
+
correctAnswers = correctAnswers || [];
|
|
508
|
+
var rez = cloneDeep(sessionAnswers).reduce((correctedAnswer, answer) => {
|
|
509
|
+
var answerIsCorrect = correctAnswers.find(mark => compareMarks(answer, mark));
|
|
510
|
+
answer.correctness = answerIsCorrect ? 'correct' : 'incorrect';
|
|
511
|
+
|
|
512
|
+
if (answerIsCorrect) {
|
|
513
|
+
answer.correctnesslabel = comparLabelMarks(answer, answerIsCorrect);
|
|
514
|
+
answer.correctlabel = answerIsCorrect.label ? answerIsCorrect.label : '';
|
|
515
|
+
answer.label = answer.label ? answer.label : '';
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return [...correctedAnswer, answer];
|
|
519
|
+
}, []); // add missing objects from correct answer
|
|
520
|
+
|
|
521
|
+
var missingAnswers = cloneDeep(correctAnswers).reduce((correctedAnswer, answer) => {
|
|
522
|
+
var answerIndex = sessionAnswers.find(mark => compareMarks(answer, mark));
|
|
523
|
+
|
|
524
|
+
if (!answerIndex) {
|
|
525
|
+
// means that corrected answer is missing from session, so we mark it as missing object
|
|
526
|
+
return [...correctedAnswer, _objectSpread(_objectSpread({}, answer), {}, {
|
|
527
|
+
correctness: 'missing'
|
|
528
|
+
})];
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return correctedAnswer;
|
|
532
|
+
}, []);
|
|
533
|
+
return [...rez, ...missingAnswers];
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
var getPartialScoring = _ref2 => {
|
|
537
|
+
var {
|
|
538
|
+
scoringType,
|
|
539
|
+
env
|
|
540
|
+
} = _ref2;
|
|
541
|
+
var pS = scoringType; // if scoringType is undefined, partialScoring should be considered undefined (not set)
|
|
542
|
+
// because partialScoring.enabled is using that information
|
|
543
|
+
// if it has a value, we check if it is partial scoring or dichotomous
|
|
544
|
+
|
|
545
|
+
if (scoringType) {
|
|
546
|
+
pS = scoringType === 'partial scoring';
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return partialScoring.enabled({
|
|
550
|
+
partialScoring: pS
|
|
551
|
+
}, env);
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
var getBestAnswer = function getBestAnswer(question, session) {
|
|
555
|
+
var env = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
556
|
+
// questionPossibleAnswers contains all possible answers (correct response and alternates);
|
|
557
|
+
var {
|
|
558
|
+
answers: questionPossibleAnswers = {},
|
|
559
|
+
scoringType
|
|
560
|
+
} = question || {};
|
|
561
|
+
var {
|
|
562
|
+
answer
|
|
563
|
+
} = session || {}; // filter the incomplete objects
|
|
564
|
+
|
|
565
|
+
Object.entries(questionPossibleAnswers || {}).forEach(_ref3 => {
|
|
566
|
+
var [key, value] = _ref3;
|
|
567
|
+
return questionPossibleAnswers[key] = _objectSpread(_objectSpread({}, value), {}, {
|
|
568
|
+
marks: value === null || value === void 0 ? void 0 : value.marks.filter(mark => !mark.building)
|
|
569
|
+
});
|
|
570
|
+
}); // initialize answer if no values
|
|
571
|
+
|
|
572
|
+
answer = answer || []; //filter the incomplete objects for student response - Fix for SC-33160
|
|
573
|
+
|
|
574
|
+
answer = answer.filter(mark => !mark.building); // initialize one possible answer if no values
|
|
575
|
+
|
|
576
|
+
if (isEmpty(questionPossibleAnswers)) {
|
|
577
|
+
questionPossibleAnswers = {
|
|
578
|
+
correctAnswer: initializeGraphMap()
|
|
579
|
+
};
|
|
580
|
+
} else {
|
|
581
|
+
questionPossibleAnswers = _objectSpread({
|
|
582
|
+
correctAnswer: questionPossibleAnswers.correctAnswer
|
|
583
|
+
}, sortedAnswers(questionPossibleAnswers));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
var partialScoringEnabled = getPartialScoring({
|
|
587
|
+
scoringType,
|
|
588
|
+
env
|
|
589
|
+
}); // student's answers without DUPLICATES
|
|
590
|
+
|
|
591
|
+
var sessionAnswers = uniqWith(answer, compareMarks); // array of possible answers entries
|
|
592
|
+
|
|
593
|
+
var possibleAnswers = Object.entries(questionPossibleAnswers);
|
|
594
|
+
return possibleAnswers.reduce((acc, entry) => {
|
|
595
|
+
// iterating each possible answer (main + alternates)
|
|
596
|
+
var possibleAnswerKey = entry[0];
|
|
597
|
+
var possibleAnswer = entry[1] || {};
|
|
598
|
+
var {
|
|
599
|
+
marks
|
|
600
|
+
} = possibleAnswer;
|
|
601
|
+
|
|
602
|
+
if (!marks || !marks.length) {
|
|
603
|
+
return acc;
|
|
604
|
+
} // returns array of marks, each having 'correctness' property
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
var correctedAnswer = getAnswerCorrected({
|
|
608
|
+
sessionAnswers,
|
|
609
|
+
marks
|
|
610
|
+
});
|
|
611
|
+
var correctMarks = correctedAnswer.filter(answer => answer.correctness === 'correct'); // filter out missing objects because they do not affect the calculation of the score
|
|
612
|
+
// only correct and incorrect are needed
|
|
613
|
+
|
|
614
|
+
var scoredCorrectedAnswer = correctedAnswer.filter(answer => answer.correctness !== 'missing');
|
|
615
|
+
var maxScore = marks.length;
|
|
616
|
+
var score = correctMarks.length; // if extra placements
|
|
617
|
+
|
|
618
|
+
if (scoredCorrectedAnswer.length > maxScore) {
|
|
619
|
+
score -= scoredCorrectedAnswer.length - maxScore;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (score < 0) {
|
|
623
|
+
score = 0;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (score / maxScore > acc.bestScore || !acc.foundOneSolution) {
|
|
627
|
+
if (partialScoringEnabled) {
|
|
628
|
+
acc.bestScore = parseFloat((score / maxScore).toFixed(2));
|
|
629
|
+
} else {
|
|
630
|
+
acc.bestScore = Math.floor(score / maxScore);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
acc.bestScoreAnswerKey = possibleAnswerKey;
|
|
634
|
+
acc.answersCorrected = correctedAnswer;
|
|
635
|
+
acc.foundOneSolution = true;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return acc;
|
|
639
|
+
}, {
|
|
640
|
+
bestScore: 0,
|
|
641
|
+
bestScoreAnswerKey: null,
|
|
642
|
+
// initially we just suppose all the answers are incorrect
|
|
643
|
+
answersCorrected: cloneDeep(sessionAnswers).map(answer => _objectSpread(_objectSpread({}, answer), {}, {
|
|
644
|
+
correctness: 'incorrect'
|
|
645
|
+
})),
|
|
646
|
+
foundOneSolution: false
|
|
647
|
+
});
|
|
648
|
+
};
|
|
649
|
+
var normalize = question => _objectSpread(_objectSpread({}, defaults), question);
|
|
650
|
+
function model(question, session, env) {
|
|
651
|
+
return new Promise(resolve => {
|
|
652
|
+
var normalizedQuestion = normalize(question); // added a sanity check for session for environments where it is not passed initially (ex. pie-website)
|
|
653
|
+
|
|
654
|
+
if (session === undefined || session === null) {
|
|
655
|
+
session = {};
|
|
656
|
+
} // console.log('normalizedQuestion', normalizedQuestion);
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
var _ref4 = normalizedQuestion || {},
|
|
660
|
+
{
|
|
661
|
+
defaultTool,
|
|
662
|
+
extraCSSRules,
|
|
663
|
+
prompt,
|
|
664
|
+
promptEnabled,
|
|
665
|
+
graph,
|
|
666
|
+
answers,
|
|
667
|
+
toolbarTools
|
|
668
|
+
} = _ref4,
|
|
669
|
+
questionProps = _objectWithoutProperties(_ref4, _excluded);
|
|
670
|
+
|
|
671
|
+
var {
|
|
672
|
+
arrows
|
|
673
|
+
} = normalizedQuestion;
|
|
674
|
+
var {
|
|
675
|
+
mode,
|
|
676
|
+
role
|
|
677
|
+
} = env || {}; // This is used for offering support for old models which have the property arrows: boolean
|
|
678
|
+
// Same thing is set in authoring : packages/graphing/configure/src/configure.jsx - componentDidMount
|
|
679
|
+
|
|
680
|
+
if (typeof arrows === 'boolean') {
|
|
681
|
+
if (arrows) {
|
|
682
|
+
arrows = {
|
|
683
|
+
left: true,
|
|
684
|
+
right: true,
|
|
685
|
+
up: true,
|
|
686
|
+
down: true
|
|
687
|
+
};
|
|
688
|
+
} else {
|
|
689
|
+
arrows = {
|
|
690
|
+
left: false,
|
|
691
|
+
right: false,
|
|
692
|
+
up: false,
|
|
693
|
+
down: false
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
} // added support for models without defaultTool defined; also used in packages/graphing/configure/src/index.js
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
var toolbarToolsNoLabel = (toolbarTools || []).filter(tool => tool !== 'label');
|
|
700
|
+
var normalizedDefaultTool = defaultTool || toolbarToolsNoLabel.length && toolbarToolsNoLabel[0] || '';
|
|
701
|
+
|
|
702
|
+
var base = _objectSpread(_objectSpread({}, questionProps), {}, {
|
|
703
|
+
answers,
|
|
704
|
+
arrows,
|
|
705
|
+
defaultTool: normalizedDefaultTool,
|
|
706
|
+
disabled: env.mode !== 'gather',
|
|
707
|
+
prompt: promptEnabled ? prompt : null,
|
|
708
|
+
rationale: null,
|
|
709
|
+
size: graph,
|
|
710
|
+
showKeyLegend: env.mode === 'evaluate',
|
|
711
|
+
showToggle: env.mode === 'evaluate' && !isEmpty(answers) && answers.correctAnswer && answers.correctAnswer.marks && !isEmpty(answers.correctAnswer.marks),
|
|
712
|
+
teacherInstructions: null,
|
|
713
|
+
toolbarTools,
|
|
714
|
+
extraCSSRules
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
if (role === 'instructor' && (mode === 'view' || mode === 'evaluate')) {
|
|
718
|
+
var {
|
|
719
|
+
rationale,
|
|
720
|
+
rationaleEnabled,
|
|
721
|
+
teacherInstructions,
|
|
722
|
+
teacherInstructionsEnabled
|
|
723
|
+
} = normalizedQuestion || {};
|
|
724
|
+
base.rationale = rationaleEnabled ? rationale : null;
|
|
725
|
+
base.teacherInstructions = teacherInstructionsEnabled ? teacherInstructions : null;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (mode === 'evaluate') {
|
|
729
|
+
if (!isEmpty(answers) && answers.correctAnswer && answers.correctAnswer.marks && !isEmpty(answers.correctAnswer.marks)) {
|
|
730
|
+
var {
|
|
731
|
+
answersCorrected,
|
|
732
|
+
bestScoreAnswerKey,
|
|
733
|
+
bestScore
|
|
734
|
+
} = getBestAnswer(normalizedQuestion, session, env); // array of marks from session with 'correctness' property set
|
|
735
|
+
|
|
736
|
+
base.answersCorrected = answersCorrected.sort((a, b) => graphObjectsOrder[a.correctness] - graphObjectsOrder[b.correctness]);
|
|
737
|
+
base.correctResponse = bestScoreAnswerKey ? (answers[bestScoreAnswerKey] || {}).marks : [];
|
|
738
|
+
base.showToggle = base.showToggle && bestScore !== 1;
|
|
739
|
+
} else {
|
|
740
|
+
base.answersCorrected = session && session.answer || [];
|
|
741
|
+
base.correctResponse = [];
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
log('base: ', base);
|
|
746
|
+
resolve(base);
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
function outcome(question, session) {
|
|
750
|
+
var env = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
751
|
+
return new Promise(resolve => {
|
|
752
|
+
if (!session || isEmpty(session)) {
|
|
753
|
+
resolve({
|
|
754
|
+
score: 0,
|
|
755
|
+
empty: true
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (env.mode !== 'evaluate' || isEmpty(question.answers) || question.answers && question.answers.correctAnswer && isEmpty(question.answers.correctAnswer.marks)) {
|
|
760
|
+
resolve({
|
|
761
|
+
score: 0
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
var {
|
|
766
|
+
bestScore
|
|
767
|
+
} = getBestAnswer(question, session, env);
|
|
768
|
+
resolve({
|
|
769
|
+
score: bestScore
|
|
770
|
+
});
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
var createCorrectResponseSession = (question, env) => {
|
|
774
|
+
return new Promise(resolve => {
|
|
775
|
+
if (env.mode !== 'evaluate' && env.role === 'instructor') {
|
|
776
|
+
var {
|
|
777
|
+
answers
|
|
778
|
+
} = question || {};
|
|
779
|
+
var marks = [];
|
|
780
|
+
|
|
781
|
+
if (answers && Object.values(answers)) {
|
|
782
|
+
var correctAnswer = answers.correctAnswer || Object.values(answers)[0] || {};
|
|
783
|
+
marks = correctAnswer.marks || [];
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
resolve({
|
|
787
|
+
answer: marks,
|
|
788
|
+
id: '1'
|
|
789
|
+
});
|
|
790
|
+
} else {
|
|
791
|
+
resolve(null);
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
}; // remove all html tags
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
var getContent = html => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');
|
|
798
|
+
|
|
799
|
+
var validate = function validate() {
|
|
800
|
+
var model = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
801
|
+
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
802
|
+
var {
|
|
803
|
+
answers,
|
|
804
|
+
toolbarTools
|
|
805
|
+
} = model;
|
|
806
|
+
var errors = {};
|
|
807
|
+
var correctAnswerErrors = {};
|
|
808
|
+
var toolbarToolsNoLabel = (toolbarTools || []).filter(tool => tool !== 'label');
|
|
809
|
+
|
|
810
|
+
if (!toolbarToolsNoLabel.length) {
|
|
811
|
+
errors.toolbarToolsError = 'There should be at least 1 tool defined.';
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
['teacherInstructions', 'prompt', 'rationale'].forEach(field => {
|
|
815
|
+
var _config$field;
|
|
816
|
+
|
|
817
|
+
if ((_config$field = config[field]) !== null && _config$field !== void 0 && _config$field.required && !getContent(model[field])) {
|
|
818
|
+
errors[field] = 'This field is required.';
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
Object.entries(answers || {}).forEach(_ref5 => {
|
|
822
|
+
var [key, value] = _ref5;
|
|
823
|
+
|
|
824
|
+
if (!value.marks.length) {
|
|
825
|
+
correctAnswerErrors[key] = 'At least 1 graph object should be defined.';
|
|
826
|
+
} // check if all graph objects are correctly defined with respect to root, edge and from, to
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
if (value.marks.length > 0) {
|
|
830
|
+
value.marks.forEach(mark => {
|
|
831
|
+
if (mark.building) {
|
|
832
|
+
correctAnswerErrors[key] = 'At least 1 graph object is not correctly defined.';
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
if (!isEmpty(correctAnswerErrors)) {
|
|
839
|
+
errors.correctAnswerErrors = correctAnswerErrors;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
return errors;
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
export { comparLabelMarks, compareMarks, createCorrectResponseSession, getAnswerCorrected, getBestAnswer, model, normalize, outcome, validate };
|
|
846
|
+
//# sourceMappingURL=controller.js.map
|