@khanacademy/perseus-score 9.0.9 → 9.0.11

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/dist/index.js CHANGED
@@ -1,129 +1,1297 @@
1
- import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-utils';
2
- import * as KAS from '@khanacademy/kas';
3
- import { KhanMath, geometry, coefficients, angles, number } from '@khanacademy/kmath';
4
- import { ErrorCodes, PerseusError, Errors, getDivideSymbol, getDecimalSeparator, approximateDeepEqual, GrapherUtil, approximateEqual, deepClone, getMatrixSize, exceedsCardLimit, getWidgetIdsFromContent, Registry } from '@khanacademy/perseus-core';
5
- import _ from 'underscore';
6
- import invariant from 'tiny-invariant';
1
+ import { addLibraryVersionToPerseusDebug as e } from "@khanacademy/perseus-utils";
2
+ import * as t from "@khanacademy/kas";
3
+ import { KhanMath as n, angles as r, coefficients as i, geometry as a, number as o } from "@khanacademy/kmath";
4
+ import { ErrorCodes as s, Errors as c, GrapherUtil as l, PerseusError as u, Registry as d, approximateDeepEqual as f, approximateEqual as p, deepClone as m, exceedsCardLimit as h, getDecimalSeparator as g, getDivideSymbol as _, getMatrixSize as v, getWidgetIdsFromContent as y } from "@khanacademy/perseus-core";
5
+ import b from "underscore";
6
+ import x from "tiny-invariant";
7
+ //#region \0rolldown/runtime.js
8
+ var S = "@khanacademy/perseus-score", C = "9.0.11";
9
+ e(S, C);
10
+ //#endregion
11
+ //#region packages/perseus-score/src/util/answer-types.ts
12
+ var w = 2 ** -42, T = {
13
+ predicate: {
14
+ defaultForms: "integer, proper, improper, mixed, decimal",
15
+ createValidatorFunctional: function(e, t) {
16
+ let r = {
17
+ simplify: "required",
18
+ ratio: !1,
19
+ forms: T.predicate.defaultForms,
20
+ ...t
21
+ }, i;
22
+ i = Array.isArray(r.forms) ? r.forms : r.forms.split(/\s*,\s*/), r.inexact === void 0 && (r.maxError = 0);
23
+ let a = +(r.maxError ?? 0) + w;
24
+ i.includes("percent") && (i = b.without(i, "percent"), i.push("percent"));
25
+ let fractionTransformer = function(e) {
26
+ e = e.replace(/\u2212/, "-").replace(/([+-])\s+/g, "$1").replace(/(^\s*)|(\s*$)/gi, "");
27
+ let t = e.match(/^([+-]?\d+)\s*\/\s*([+-]?\d+)$/), i = e.match(/^([+-]?)\\frac\{([+-]?\d+)\}\{([+-]?\d+)\}$/), a = parseInt(e, 10);
28
+ if (t || i) {
29
+ let e, a, o = !0;
30
+ return t ? (e = parseFloat(t[1]), a = parseFloat(t[2])) : (e = parseFloat(i[2]), i[1] === "-" && (e < 0 && (o = !1), e = -e), a = parseFloat(i[3])), o = o && a > 0 && (r.ratio || a !== 1) && n.getGCD(e, a) === 1, [{
31
+ value: e / a,
32
+ exact: o
33
+ }];
34
+ }
35
+ return !isNaN(a) && "" + a === e ? [{
36
+ value: a,
37
+ exact: !0
38
+ }] : [];
39
+ }, o = {
40
+ integer: function(e) {
41
+ let t = o.decimal(e), n = o.decimal(e, 1);
42
+ return t[0]?.value != null && t[0].value === n[0]?.value || t[1]?.value != null && t[1].value === n[1]?.value ? t : [];
43
+ },
44
+ proper: function(e) {
45
+ return fractionTransformer(e).flatMap((e) => Math.abs(e.value) < 1 ? [e] : []);
46
+ },
47
+ improper: function(e) {
48
+ return e.includes("/") || e.match(/\\(d?frac)/) ? fractionTransformer(e).flatMap((e) => Math.abs(e.value) >= 1 ? [e] : []) : [];
49
+ },
50
+ pi: function(e) {
51
+ let t, r = [];
52
+ if (e = e.replace(/\u2212/, "-"), t = e.match(/^([+-]?)\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)) r = [{
53
+ value: parseFloat(t[1] + "1"),
54
+ exact: !0
55
+ }];
56
+ else if (t = e.match(/^([+-]?\s*\d+\s*(?:\/\s*[+-]?\s*\d+)?)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)) r = fractionTransformer(t[1]);
57
+ else if (t = e.match(/^([+-]?)\s*(\d+)\s*([+-]?\d+)\s*\/\s*([+-]?\d+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)) {
58
+ let e = parseFloat(t[1] + "1"), i = parseFloat(t[2]), a = parseFloat(t[3]), o = parseFloat(t[4]), s = a < o && n.getGCD(a, o) === 1;
59
+ r = [{
60
+ value: e * (i + a / o),
61
+ exact: s
62
+ }];
63
+ } else if (t = e.match(/^([+-]?\s*\d+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)\s*(?:\/\s*([+-]?\s*\d+))?$/i)) r = fractionTransformer(t[1] + "/" + t[3]);
64
+ else if (t = e.match(/^([+-]?)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)\s*(?:\/\s*([+-]?\d+))?$/i)) r = fractionTransformer(t[1] + "1/" + t[3]);
65
+ else if (e === "0") r = [{
66
+ value: 0,
67
+ exact: !0
68
+ }];
69
+ else if (t = e.match(/^(.+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)) r = o.decimal(t[1]);
70
+ else {
71
+ r = b.reduce(T.predicate.defaultForms.split(/\s*,\s*/), function(t, n) {
72
+ return t.concat(o[n](e));
73
+ }, []);
74
+ let t = !1, n = parseFloat(e);
75
+ if (!isNaN(n) && n !== parseInt(e)) {
76
+ let e = Math.PI / 12, r = e * Math.round(n / e);
77
+ Math.abs(n - r) < .01 && (t = !0);
78
+ } else e.match(/\/\s*7/) && (t = !0);
79
+ return t && b.each(r, function(e) {
80
+ e.piApprox = !0;
81
+ }), r;
82
+ }
83
+ let i = Math.PI;
84
+ return e.match(/\\?tau|t|\u03c4/) && (i = Math.PI * 2), e.match(/pau/) && (i = Math.PI * 1.5), r.forEach((e) => {
85
+ e.value *= i;
86
+ }), r;
87
+ },
88
+ coefficient: function(e) {
89
+ let t = [];
90
+ return e = e.replace(/\u2212/, "-"), e === "" ? t = [{
91
+ value: 1,
92
+ exact: !0
93
+ }] : e === "-" && (t = [{
94
+ value: -1,
95
+ exact: !0
96
+ }]), t;
97
+ },
98
+ log: function(e) {
99
+ let t, n = [];
100
+ return e = e.replace(/\u2212/, "-"), e = e.replace(/[ ()]/g, ""), (t = e.match(/^log\s*(\S+)\s*$/i)) ? n = o.decimal(t[1]) : e === "0" && (n = [{
101
+ value: 0,
102
+ exact: !0
103
+ }]), n;
104
+ },
105
+ percent: function(e) {
106
+ e = String(e).trim();
107
+ let t = !1;
108
+ e.indexOf("%") === e.length - 1 && (e = e.substring(0, e.length - 1).trim(), t = !0);
109
+ let n = o.decimal(e);
110
+ return n.forEach((e) => {
111
+ e.exact = t, e.value /= 100;
112
+ }), n;
113
+ },
114
+ mixed: function(e) {
115
+ let t = e.replace(/\u2212/, "-").replace(/([+-])\s+/g, "$1").match(/^([+-]?)(\d+)\s+(\d+)\s*\/\s*(\d+)$/);
116
+ if (t) {
117
+ let e = parseFloat(t[1] + "1"), r = parseFloat(t[2]), i = parseFloat(t[3]), a = parseFloat(t[4]), o = i < a && n.getGCD(i, a) === 1;
118
+ return [{
119
+ value: e * (r + i / a),
120
+ exact: o
121
+ }];
122
+ }
123
+ return [];
124
+ },
125
+ decimal: function(e, t = 1e10) {
126
+ let normal = function(e) {
127
+ e = String(e).trim();
128
+ let n = e.replace(/\u2212/, "-").replace(/([+-])\s+/g, "$1").match(/^([+-]?(?:\d{1,3}(?:[, ]?\d{3})*\.?|\d{0,3}(?:[, ]?\d{3})*\.(?:\d{3}[, ]?)*\d{1,3}))$/), i = e.match(/^0[0,]*,/);
129
+ if (n && !i) {
130
+ let e = parseFloat(n[1].replace(/[, ]/g, ""));
131
+ return r.inexact === void 0 && (e = Math.round(e * t) / t), e;
132
+ }
133
+ }, commas = function(e) {
134
+ return e = e.replace(/([.,])/g, function(e, t) {
135
+ return t === "." ? "," : ".";
136
+ }), normal(e);
137
+ }, n = [{
138
+ value: normal(e),
139
+ exact: !0
140
+ }];
141
+ return r.decimal_separator === "," && n.push({
142
+ value: commas(e),
143
+ exact: !0
144
+ }), n;
145
+ }
146
+ };
147
+ return function(t) {
148
+ let n = r.fallback == null ? "" : "" + r.fallback;
149
+ t = String(t).trim() || n;
150
+ let c = {
151
+ empty: t === "",
152
+ correct: !1,
153
+ message: null,
154
+ guess: t
155
+ }, findCorrectAnswer = () => {
156
+ for (let n of i) {
157
+ let i = o[n](t);
158
+ for (let t = 0, o = i.length; t < o; t++) {
159
+ let o = i[t].value, l = i[t].exact, u = i[t].piApprox;
160
+ if (e(o, a)) return l || r.simplify === "optional" ? (c.correct = !0, c.message = r.message || null, c.empty = !1) : n === "percent" ? (c.empty = !0, c.message = s.MISSING_PERCENT_ERROR) : (r.simplify !== "enforced" && (c.empty = !0), c.message = s.NEEDS_TO_BE_SIMPLIFIED_ERROR), !1;
161
+ u && e(o, Math.abs(o * .001)) && (c.empty = !0, c.message = s.APPROXIMATED_PI_ERROR);
162
+ }
163
+ }
164
+ };
165
+ if (findCorrectAnswer(), c.correct === !1) {
166
+ let e = !1;
167
+ if (b.each(o, function(n) {
168
+ b.any(n(t), function(e) {
169
+ return e.value != null && !Number.isNaN(e.value);
170
+ }) && (e = !0);
171
+ }), !e) return c.empty = !0, c.message = s.EXTRA_SYMBOLS_ERROR, c;
172
+ }
173
+ return c;
174
+ };
175
+ }
176
+ },
177
+ number: { createValidatorFunctional: function(e, t) {
178
+ let n = parseFloat(e);
179
+ return T.predicate.createValidatorFunctional((e, t) => Math.abs(e - n) < t, { ...t });
180
+ } },
181
+ expression: {
182
+ parseSolution: function(e, n) {
183
+ let r = t.parse(e, n);
184
+ if (!r.parsed) throw new u("The provided solution (" + e + ") didn't parse.", c.InvalidInput);
185
+ if (n.simplified && !r.expr.isSimplified()) throw new u("The provided solution (" + e + ") isn't fully expanded and simplified.", c.InvalidInput);
186
+ return r = r.expr, r;
187
+ },
188
+ createValidatorFunctional: function(e, n) {
189
+ return function(r) {
190
+ let i = {
191
+ empty: !1,
192
+ correct: !1,
193
+ message: null,
194
+ guess: r,
195
+ ungraded: !1
196
+ };
197
+ if (!r) return i.empty = !0, i;
198
+ let a = t.parse(r, n);
199
+ if (!a.parsed) return i.empty = !0, i;
200
+ typeof e == "string" && (e = T.expression.parseSolution(e, n));
201
+ let o = t.compare(a.expr, e, n);
202
+ if (o.equal) i.correct = !0;
203
+ else if (o.message) i.message = o.message;
204
+ else {
205
+ let a = t.parse(r.replace(/[xX]/g, "*"), n);
206
+ if (a.parsed) {
207
+ let r = t.compare(a.expr, e, n);
208
+ r.equal ? (i.ungraded = !0, i.message = s.MULTIPLICATION_SIGN_ERROR) : r.message && (i.message = r.message + " Also, I'm a computer. I only understand multiplication if you use an asterisk (*) as the multiplication sign.");
209
+ }
210
+ }
211
+ return i;
212
+ };
213
+ }
214
+ }
215
+ };
216
+ //#endregion
217
+ //#region packages/perseus-score/src/widgets/categorizer/score-categorizer.ts
218
+ function scoreCategorizer(e, t) {
219
+ if (e == null) return {
220
+ type: "invalid",
221
+ message: null
222
+ };
223
+ let n = !0;
224
+ return t.values.forEach((t, r) => {
225
+ e.values[r] !== t && (n = !1);
226
+ }), {
227
+ type: "points",
228
+ earned: +!!n,
229
+ total: 1,
230
+ message: null
231
+ };
232
+ }
233
+ //#endregion
234
+ //#region packages/perseus-score/src/widgets/categorizer/validate-categorizer.ts
235
+ function validateCategorizer(e, t) {
236
+ return e == null ? {
237
+ type: "invalid",
238
+ message: null
239
+ } : t.items.some((t, n) => e.values[n] == null) ? {
240
+ type: "invalid",
241
+ message: s.INVALID_SELECTION_ERROR
242
+ } : null;
243
+ }
244
+ //#endregion
245
+ //#region packages/perseus-score/src/widgets/cs-program/score-cs-program.ts
246
+ function scoreCSProgram(e) {
247
+ return e == null ? {
248
+ type: "invalid",
249
+ message: null
250
+ } : e.status === "correct" ? {
251
+ type: "points",
252
+ earned: 1,
253
+ total: 1,
254
+ message: e.message || null
255
+ } : e.status === "incorrect" ? {
256
+ type: "points",
257
+ earned: 0,
258
+ total: 1,
259
+ message: e.message || null
260
+ } : {
261
+ type: "invalid",
262
+ message: "Keep going, you're not there yet!"
263
+ };
264
+ }
265
+ //#endregion
266
+ //#region packages/perseus-score/src/widgets/dropdown/score-dropdown.ts
267
+ function scoreDropdown(e, t) {
268
+ return e == null ? {
269
+ type: "invalid",
270
+ message: null
271
+ } : {
272
+ type: "points",
273
+ earned: +!!t.choices[e.value - 1].correct,
274
+ total: 1,
275
+ message: null
276
+ };
277
+ }
278
+ //#endregion
279
+ //#region packages/perseus-score/src/widgets/dropdown/validate-dropdown.ts
280
+ function validateDropdown(e) {
281
+ return e == null || e.value === 0 ? {
282
+ type: "invalid",
283
+ message: null
284
+ } : null;
285
+ }
286
+ //#endregion
287
+ //#region packages/perseus-score/src/widgets/expression/score-expression.ts
288
+ function getVarError(e, t, n) {
289
+ if (e.every((e) => t.includes(e))) return;
290
+ let r = [...new Set(e.map((e) => e.toLowerCase()))], i = t.map((e) => e.toLowerCase());
291
+ if (r.every((e) => i.includes(e))) return s.WRONG_CASE_ERROR;
292
+ if (e.filter((e) => !t.includes(e)).some((e) => !n.includes(e))) return s.WRONG_LETTER_ERROR;
293
+ }
294
+ function scoreExpression(e, n, r) {
295
+ if (e == null) return {
296
+ type: "invalid",
297
+ message: null
298
+ };
299
+ let i = {
300
+ ...n,
301
+ decimal_separator: g(r),
302
+ divide_symbol: _(r)
303
+ }, a = t.parse(e, i);
304
+ if (!a.parsed) return {
305
+ type: "invalid",
306
+ message: s.EXTRA_SYMBOLS_ERROR
307
+ };
308
+ let o = a.expr.getVars(), createValidator = (e) => {
309
+ let r = t.parse(e.value, n);
310
+ if (!r.parsed)
311
+ /* c8 ignore next */
312
+ throw new u("Unable to parse solution answer for expression", c.InvalidInput, { metadata: { rubric: JSON.stringify(n) } });
313
+ let a = getVarError(o, r.expr.getVars(), n.extraKeys ?? []);
314
+ return a == null ? T.expression.createValidatorFunctional(r.expr, {
315
+ ...i,
316
+ simplify: e.simplify,
317
+ form: e.form
318
+ }) : (e) => ({
319
+ correct: !1,
320
+ empty: !1,
321
+ ungraded: !0,
322
+ suppressAlmostThere: !0,
323
+ message: a,
324
+ guess: e
325
+ });
326
+ }, l, d, f = !0, p;
327
+ for (let t of n.answerForms ?? []) {
328
+ let n = createValidator(t);
329
+ if (n == null) continue;
330
+ let r = n(e);
331
+ if (r.correct) {
332
+ l = t, d = r.message || "";
333
+ break;
334
+ }
335
+ f = f && r.empty, t.considered === "correct" && r.ungraded && !p && (p = r);
336
+ }
337
+ return l ? l.considered === "ungraded" ? {
338
+ type: "invalid",
339
+ message: d
340
+ } : {
341
+ type: "points",
342
+ earned: +(l.considered === "correct"),
343
+ total: 1,
344
+ message: d
345
+ } : p ? {
346
+ type: "invalid",
347
+ message: p.message,
348
+ suppressAlmostThere: p.suppressAlmostThere
349
+ } : f ? {
350
+ type: "invalid",
351
+ message: null
352
+ } : {
353
+ type: "points",
354
+ earned: 0,
355
+ total: 1
356
+ };
357
+ }
358
+ //#endregion
359
+ //#region packages/perseus-score/src/widgets/expression/validate-expression.ts
360
+ function validateExpression(e) {
361
+ return e === "" || e == null ? {
362
+ type: "invalid",
363
+ message: null
364
+ } : null;
365
+ }
366
+ //#endregion
367
+ //#region packages/perseus-score/src/widgets/grapher/score-grapher.ts
368
+ function getCoefficientsByType(e) {
369
+ if (e.coords != null) {
370
+ if (e.type === "exponential" || e.type === "logarithm") return l.functionForType(e.type).getCoefficients(e.coords, e.asymptote);
371
+ if (e.type === "tangent") {
372
+ let t = i.getTangentCoefficients(e.coords);
373
+ return t.some((e) => !isFinite(e)) ? void 0 : t;
374
+ }
375
+ if (e.type === "linear" || e.type === "quadratic" || e.type === "absolute_value" || e.type === "sinusoid") return l.functionForType(e.type).getCoefficients(e.coords);
376
+ throw new u("Invalid grapher type", c.InvalidInput);
377
+ }
378
+ }
379
+ function scoreGrapher(e, t) {
380
+ if (e == null) return {
381
+ type: "invalid",
382
+ message: null
383
+ };
384
+ if (x(t.correct != null, "correct is null or undefined in scoreGrapher"), e.type !== t.correct.type) return {
385
+ type: "points",
386
+ earned: 0,
387
+ total: 1,
388
+ message: null
389
+ };
390
+ if (e.coords == null) return {
391
+ type: "invalid",
392
+ message: null
393
+ };
394
+ let n = getCoefficientsByType(e), r = getCoefficientsByType(t.correct);
395
+ return n == null || r == null ? {
396
+ type: "invalid",
397
+ message: null
398
+ } : (e.type === "tangent" ? f(a.canonicalTangentCoefficients(n), a.canonicalTangentCoefficients(r)) : l.functionForType(e.type).areEqual(n, r)) ? {
399
+ type: "points",
400
+ earned: 1,
401
+ total: 1,
402
+ message: null
403
+ } : {
404
+ type: "points",
405
+ earned: 0,
406
+ total: 1,
407
+ message: null
408
+ };
409
+ }
410
+ //#endregion
411
+ //#region packages/perseus-score/src/widgets/iframe/score-iframe.ts
412
+ function scoreIframe(e) {
413
+ return e == null ? {
414
+ type: "invalid",
415
+ message: null
416
+ } : e.status === "correct" ? {
417
+ type: "points",
418
+ earned: 1,
419
+ total: 1,
420
+ message: e.message || null
421
+ } : e.status === "incorrect" ? {
422
+ type: "points",
423
+ earned: 0,
424
+ total: 1,
425
+ message: e.message || null
426
+ } : {
427
+ type: "invalid",
428
+ message: "Keep going, you're not there yet!"
429
+ };
430
+ }
431
+ //#endregion
432
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-absolute-value.ts
433
+ var { getAbsoluteValueCoefficients: E } = i;
434
+ function scoreAbsoluteValue(e, t) {
435
+ if (!e.coords || !t.coords) return {
436
+ type: "invalid",
437
+ message: null
438
+ };
439
+ let n = E(e.coords), r = E(t.coords);
440
+ return {
441
+ type: "points",
442
+ earned: +!!f(n, r),
443
+ total: 1,
444
+ message: null
445
+ };
446
+ }
447
+ //#endregion
448
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-angle.ts
449
+ var { collinear: D, clockwise: O } = a, { getClockwiseAngle: k } = r;
450
+ function scoreAngle(e, t) {
451
+ let n = e.coords, r = t.coords, i = t.allowReflexAngles;
452
+ if (!n) return {
453
+ type: "invalid",
454
+ message: null
455
+ };
456
+ let a = O([
457
+ n[0],
458
+ n[2],
459
+ n[1]
460
+ ]) && !i ? n.slice().reverse() : n, o;
461
+ if (t.match === "congruent") {
462
+ let e = k(a, i), t = r ? k(r, i) : null;
463
+ o = t !== null && p(e, t);
464
+ } else o = r != null && f(a[1], r[1]) && D(r[1], r[0], a[0]) && D(r[1], r[2], a[2]);
465
+ return {
466
+ type: "points",
467
+ earned: +!!o,
468
+ total: 1,
469
+ message: null
470
+ };
471
+ }
472
+ //#endregion
473
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-circle.ts
474
+ function scoreCircle(e, t) {
475
+ return e.center == null || e.radius == null || t.center == null || t.radius == null ? {
476
+ type: "invalid",
477
+ message: null
478
+ } : {
479
+ type: "points",
480
+ earned: f(e.center, t.center) && p(e.radius, t.radius) ? 1 : 0,
481
+ total: 1,
482
+ message: null
483
+ };
484
+ }
485
+ //#endregion
486
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-exponential.ts
487
+ var { getExponentialCoefficients: A } = i;
488
+ function scoreExponential(e, t) {
489
+ if (!e.coords || e.asymptote == null || !t.coords || t.asymptote == null) return {
490
+ type: "invalid",
491
+ message: null
492
+ };
493
+ let n = A(e.coords, e.asymptote), r = A(t.coords, t.asymptote);
494
+ return {
495
+ type: "points",
496
+ earned: n != null && r != null && f([
497
+ n.a,
498
+ n.b,
499
+ n.c
500
+ ], [
501
+ r.a,
502
+ r.b,
503
+ r.c
504
+ ]) ? 1 : 0,
505
+ total: 1,
506
+ message: null
507
+ };
508
+ }
509
+ //#endregion
510
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-linear.ts
511
+ var { collinear: j } = a;
512
+ function scoreLinear(e, t) {
513
+ if (!e.coords || !t.coords) return {
514
+ type: "invalid",
515
+ message: null
516
+ };
517
+ let n = e.coords, r = t.coords;
518
+ return {
519
+ type: "points",
520
+ earned: j(r[0], r[1], n[0]) && j(r[0], r[1], n[1]) ? 1 : 0,
521
+ total: 1,
522
+ message: null
523
+ };
524
+ }
525
+ //#endregion
526
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-linear-system.ts
527
+ var { collinear: M } = a;
528
+ function scoreLinearSystem(e, t) {
529
+ if (!e.coords || !t.coords) return {
530
+ type: "invalid",
531
+ message: null
532
+ };
533
+ let n = e.coords, r = t.coords;
534
+ return {
535
+ type: "points",
536
+ earned: M(r[0][0], r[0][1], n[0][0]) && M(r[0][0], r[0][1], n[0][1]) && M(r[1][0], r[1][1], n[1][0]) && M(r[1][0], r[1][1], n[1][1]) || M(r[0][0], r[0][1], n[1][0]) && M(r[0][0], r[0][1], n[1][1]) && M(r[1][0], r[1][1], n[0][0]) && M(r[1][0], r[1][1], n[0][1]) ? 1 : 0,
537
+ total: 1,
538
+ message: null
539
+ };
540
+ }
541
+ //#endregion
542
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-logarithm.ts
543
+ var { getLogarithmCoefficients: N } = i;
544
+ function scoreLogarithm(e, t) {
545
+ if (!e.coords || e.asymptote == null || !t.coords || t.asymptote == null) return {
546
+ type: "invalid",
547
+ message: null
548
+ };
549
+ let n = N(e.coords, e.asymptote), r = N(t.coords, t.asymptote);
550
+ return {
551
+ type: "points",
552
+ earned: n != null && r != null && f([
553
+ n.a,
554
+ n.b,
555
+ n.c
556
+ ], [
557
+ r.a,
558
+ r.b,
559
+ r.c
560
+ ]) ? 1 : 0,
561
+ total: 1,
562
+ message: null
563
+ };
564
+ }
565
+ //#endregion
566
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-point.ts
567
+ function scorePoint(e, t) {
568
+ if (!e.coords) return {
569
+ type: "invalid",
570
+ message: null
571
+ };
572
+ if (t.coords == null) throw Error("Point graph rubric has null coords");
573
+ let n = e.coords.slice(), r = t.coords.slice();
574
+ return n.sort(), r.sort(), {
575
+ type: "points",
576
+ earned: +!!f(n, r),
577
+ total: 1,
578
+ message: null
579
+ };
580
+ }
581
+ //#endregion
582
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-polygon.ts
583
+ var { similar: P } = a;
584
+ function scorePolygon(e, t) {
585
+ if (!e.coords || !t.coords) return {
586
+ type: "invalid",
587
+ message: null
588
+ };
589
+ let n = e.coords.slice(), r = t.coords.slice(), i;
590
+ return t.match === "similar" ? i = P(n, r, Infinity) : t.match === "congruent" ? i = P(n, r, o.DEFAULT_TOLERANCE) : t.match === "approx" ? i = P(n, r, .1) : (n.sort(), r.sort(), i = f(n, r)), {
591
+ type: "points",
592
+ earned: +!!i,
593
+ total: 1,
594
+ message: null
595
+ };
596
+ }
597
+ //#endregion
598
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-quadratic.ts
599
+ var { getQuadraticCoefficients: F } = i;
600
+ function scoreQuadratic(e, t) {
601
+ if (!e.coords || !t.coords) return {
602
+ type: "invalid",
603
+ message: null
604
+ };
605
+ let n = F(e.coords), r = F(t.coords);
606
+ return {
607
+ type: "points",
608
+ earned: +!!f(n, r),
609
+ total: 1,
610
+ message: null
611
+ };
612
+ }
613
+ //#endregion
614
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-ray.ts
615
+ var { collinear: I } = a;
616
+ function scoreRay(e, t) {
617
+ if (!e.coords || !t.coords) return {
618
+ type: "invalid",
619
+ message: null
620
+ };
621
+ let n = e.coords, r = t.coords;
622
+ return {
623
+ type: "points",
624
+ earned: f(n[0], r[0]) && I(r[0], r[1], n[1]) ? 1 : 0,
625
+ total: 1,
626
+ message: null
627
+ };
628
+ }
629
+ //#endregion
630
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-segment.ts
631
+ function scoreSegment(e, t) {
632
+ if (!e.coords || !t.coords) return {
633
+ type: "invalid",
634
+ message: null
635
+ };
636
+ let n = m(e.coords), r = m(t.coords);
637
+ return n = b.invoke(n, "sort").sort(), r = b.invoke(r, "sort").sort(), {
638
+ type: "points",
639
+ earned: +!!f(n, r),
640
+ total: 1,
641
+ message: null
642
+ };
643
+ }
644
+ //#endregion
645
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-sinusoid.ts
646
+ var { getSinusoidCoefficients: L } = i, { canonicalSineCoefficients: R } = a;
647
+ function scoreSinusoid(e, t) {
648
+ if (!e.coords || !t.coords) return {
649
+ type: "invalid",
650
+ message: null
651
+ };
652
+ let n = L(e.coords), r = L(t.coords), i = R(n), a = R(r);
653
+ return {
654
+ type: "points",
655
+ earned: +!!f(i, a),
656
+ total: 1,
657
+ message: null
658
+ };
659
+ }
660
+ //#endregion
661
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-tangent.ts
662
+ var { getTangentCoefficients: z } = i, { canonicalTangentCoefficients: B } = a;
663
+ function scoreTangent(e, t) {
664
+ if (!e.coords || !t.coords) return {
665
+ type: "invalid",
666
+ message: null
667
+ };
668
+ let n = z(e.coords), r = z(t.coords), i = B(n), a = B(r);
669
+ return {
670
+ type: "points",
671
+ earned: +!!f(i, a),
672
+ total: 1,
673
+ message: null
674
+ };
675
+ }
676
+ //#endregion
677
+ //#region packages/perseus-score/src/widgets/interactive-graph/sub-scorers/score-vector.ts
678
+ function scoreVector(e, t) {
679
+ if (!e.coords || !t.coords) return {
680
+ type: "invalid",
681
+ message: null
682
+ };
683
+ let n = e.coords, r = t.coords, i;
684
+ if (t.match === "congruent") {
685
+ let e = [n[1][0] - n[0][0], n[1][1] - n[0][1]], t = [r[1][0] - r[0][0], r[1][1] - r[0][1]];
686
+ i = f(e, t);
687
+ } else i = f(n[0], r[0]) && f(n[1], r[1]);
688
+ return {
689
+ type: "points",
690
+ earned: +!!i,
691
+ total: 1,
692
+ message: null
693
+ };
694
+ }
695
+ //#endregion
696
+ //#region packages/perseus-score/src/widgets/interactive-graph/score-interactive-graph.ts
697
+ function scoreInteractiveGraph(e, t) {
698
+ if (e == null) return {
699
+ type: "invalid",
700
+ message: null
701
+ };
702
+ if (e.type === "none" && t.correct.type === "none") return {
703
+ type: "points",
704
+ earned: 0,
705
+ total: 0,
706
+ message: null
707
+ };
708
+ let n = !!(e.coords || e.center && e.radius);
709
+ return e.type === "absolute-value" && t.correct.type === "absolute-value" ? scoreAbsoluteValue(e, t.correct) : e.type === "angle" && t.correct.type === "angle" ? scoreAngle(e, t.correct) : e.type === "circle" && t.correct.type === "circle" ? scoreCircle(e, t.correct) : e.type === "exponential" && t.correct.type === "exponential" && e.asymptote != null ? scoreExponential(e, t.correct) : e.type === "linear" && t.correct.type === "linear" ? scoreLinear(e, t.correct) : e.type === "linear-system" && t.correct.type === "linear-system" ? scoreLinearSystem(e, t.correct) : e.type === "logarithm" && t.correct.type === "logarithm" && e.asymptote != null ? scoreLogarithm(e, t.correct) : e.type === "point" && t.correct.type === "point" ? scorePoint(e, t.correct) : e.type === "polygon" && t.correct.type === "polygon" ? scorePolygon(e, t.correct) : e.type === "quadratic" && t.correct.type === "quadratic" ? scoreQuadratic(e, t.correct) : e.type === "ray" && t.correct.type === "ray" ? scoreRay(e, t.correct) : e.type === "segment" && t.correct.type === "segment" ? scoreSegment(e, t.correct) : e.type === "sinusoid" && t.correct.type === "sinusoid" ? scoreSinusoid(e, t.correct) : e.type === "tangent" && t.correct.type === "tangent" ? scoreTangent(e, t.correct) : e.type === "vector" && t.correct.type === "vector" ? scoreVector(e, t.correct) : !n || b.isEqual(e, t.graph) ? {
710
+ type: "invalid",
711
+ message: null
712
+ } : {
713
+ type: "points",
714
+ earned: 0,
715
+ total: 1,
716
+ message: null
717
+ };
718
+ }
719
+ //#endregion
720
+ //#region packages/perseus-score/src/widgets/label-image/score-label-image.ts
721
+ function scoreLabelImageMarker(e, t) {
722
+ let n = {
723
+ hasAnswers: !1,
724
+ isCorrect: !1
725
+ };
726
+ return e && e.length > 0 && (n.hasAnswers = !0), t.length > 0 ? e && e.length === t.length && (n.isCorrect = e.every((e) => t.includes(e))) : (!e || e.length === 0) && (n.isCorrect = !0), n;
727
+ }
728
+ function scoreLabelImage(e, t) {
729
+ if (e == null) return {
730
+ type: "invalid",
731
+ message: null
732
+ };
733
+ let n = 0;
734
+ for (let r = 0; r < e.markers.length; r++) scoreLabelImageMarker(e.markers[r].selected, t.markers[r].answers).isCorrect && n++;
735
+ return {
736
+ type: "points",
737
+ earned: +(n === e.markers.length),
738
+ total: 1,
739
+ message: null
740
+ };
741
+ }
742
+ //#endregion
743
+ //#region packages/perseus-score/src/widgets/matcher/score-matcher.ts
744
+ function scoreMatcher(e, t) {
745
+ return e == null ? {
746
+ type: "invalid",
747
+ message: null
748
+ } : {
749
+ type: "points",
750
+ earned: b.isEqual(e.left, t.left) && b.isEqual(e.right, t.right) ? 1 : 0,
751
+ total: 1,
752
+ message: null
753
+ };
754
+ }
755
+ //#endregion
756
+ //#region packages/perseus-score/src/widgets/matrix/score-matrix.ts
757
+ function scoreMatrix(e, t) {
758
+ if (e == null) return {
759
+ type: "invalid",
760
+ message: null
761
+ };
762
+ let n = t.answers, r = e.answers, i = v(n), a = v(r), o = i[0] !== a[0] || i[1] !== a[1], s = T.number.createValidatorFunctional, c = null, l = !1;
763
+ return b(a[0]).times((e) => {
764
+ b(a[1]).times((t) => {
765
+ if (!o) {
766
+ let i = s(n[e][t], { simplify: !0 })(r[e][t]);
767
+ i.message && (c = i.message), i.correct || (l = !0);
768
+ }
769
+ });
770
+ }), o ? {
771
+ type: "points",
772
+ earned: 0,
773
+ total: 1,
774
+ message: null
775
+ } : {
776
+ type: "points",
777
+ earned: +!l,
778
+ total: 1,
779
+ message: c
780
+ };
781
+ }
782
+ //#endregion
783
+ //#region packages/perseus-score/src/widgets/matrix/validate-matrix.ts
784
+ function validateMatrix(e) {
785
+ if (e == null) return {
786
+ type: "invalid",
787
+ message: null
788
+ };
789
+ let t = e.answers, n = v(t);
790
+ for (let e = 0; e < n[0]; e++) for (let r = 0; r < n[1]; r++) {
791
+ let n = t[e]?.[r];
792
+ if (n == null || n.toString().length === 0) return {
793
+ type: "invalid",
794
+ message: s.FILL_ALL_CELLS_ERROR
795
+ };
796
+ }
797
+ return null;
798
+ }
799
+ //#endregion
800
+ //#region packages/perseus-score/src/widgets/number-line/score-number-line.ts
801
+ function scoreNumberLine(e, t) {
802
+ if (e == null) return {
803
+ type: "invalid",
804
+ message: null
805
+ };
806
+ let n = t.divisionRange, r = e.numDivisions > n[1] || e.numDivisions < n[0];
807
+ if (t.isTickCtrl && r) return {
808
+ type: "invalid",
809
+ message: "Number of divisions is outside the allowed range."
810
+ };
811
+ let i = t.range, a = t.initialX == null ? i[0] : t.initialX, s = t.isInequality ? "ge" : "eq", c = t.correctRel || "eq";
812
+ return o.equal(e.numLinePosition, t.correctX || 0) && c === e.rel ? {
813
+ type: "points",
814
+ earned: 1,
815
+ total: 1,
816
+ message: null
817
+ } : e.numLinePosition === a && e.rel === s ? {
818
+ type: "invalid",
819
+ message: null
820
+ } : {
821
+ type: "points",
822
+ earned: 0,
823
+ total: 1,
824
+ message: null
825
+ };
826
+ }
827
+ //#endregion
828
+ //#region packages/perseus-score/src/util/tex-wrangler.ts
829
+ function findEndpoint(e, t) {
830
+ let n = 0;
831
+ for (let r = t, i = e.length; r < i; r++) {
832
+ let t = e[r];
833
+ if (t === "{" ? n++ : t === "}" && n--, n < 0) return r;
834
+ }
835
+ return e.length;
836
+ }
837
+ function parseNextExpression(e, t, n) {
838
+ let r = e.indexOf("{", t);
839
+ if (r === -1) return {
840
+ endpoint: e.length,
841
+ expression: ""
842
+ };
843
+ let i = r + 1, a = findEndpoint(e, i);
844
+ return {
845
+ endpoint: a,
846
+ expression: walkTex(e.substring(i, a), n)
847
+ };
848
+ }
849
+ function getNextFracIndex(e, t) {
850
+ let n = e.indexOf("\\frac", t), r = e.indexOf("\\dfrac", t);
851
+ return n > -1 && r > -1 ? Math.min(n, r) : n > -1 ? n : r > -1 ? r : -1;
852
+ }
853
+ function walkTex(e, t) {
854
+ if (!e) return "";
855
+ let n = "", r = 0, i = getNextFracIndex(e, r);
856
+ for (; i > -1;) {
857
+ n += e.substring(r, i), r = i;
858
+ let a = parseNextExpression(e, r, t);
859
+ r = a.endpoint + 1;
860
+ let o = parseNextExpression(e, r, t);
861
+ r = o.endpoint + 1, n.length && (n += " "), n += t(a.expression, o.expression), i = getNextFracIndex(e, r);
862
+ }
863
+ return n += e.slice(r), n;
864
+ }
865
+ function parseTex(e) {
866
+ let handler = function(e, t) {
867
+ return e + "/" + t;
868
+ };
869
+ return walkTex(e, handler).replace("\\%", "%");
870
+ }
871
+ //#endregion
872
+ //#region packages/perseus-score/src/widgets/numeric-input/score-numeric-input.ts
873
+ var V = [
874
+ {
875
+ title: "Integers",
876
+ value: "integer",
877
+ content: "6"
878
+ },
879
+ {
880
+ title: "Decimals",
881
+ value: "decimal",
882
+ content: "0.75"
883
+ },
884
+ {
885
+ title: "Proper fractions",
886
+ value: "proper",
887
+ content: "⅗"
888
+ },
889
+ {
890
+ title: "Improper fractions",
891
+ value: "improper",
892
+ content: "⁷⁄₄"
893
+ },
894
+ {
895
+ title: "Mixed numbers",
896
+ value: "mixed",
897
+ content: "1¾"
898
+ },
899
+ {
900
+ title: "Numbers with π",
901
+ value: "pi",
902
+ content: "π"
903
+ }
904
+ ];
905
+ function maybeParsePercentInput(e, t) {
906
+ if (!(typeof e == "string" && e.endsWith("%"))) return e;
907
+ let n = parseFloat(e.slice(0, -1));
908
+ return isNaN(n) ? e : t ? n / 100 : n;
909
+ }
910
+ function scoreNumericInput(e, t, n) {
911
+ if (e == null) return {
912
+ type: "invalid",
913
+ message: null
914
+ };
915
+ let r = V.map((e) => e.value).filter((e) => e !== "pi"), createValidator = (e) => {
916
+ let t = `${e.value}`, i = [...e.answerForms ?? []];
917
+ return (!e.strict || i.length === 0) && i.push(...r), T.number.createValidatorFunctional(t, {
918
+ message: e.message,
919
+ simplify: e.status === "correct" ? e.simplify : "optional",
920
+ inexact: !0,
921
+ maxError: e.maxError,
922
+ forms: i,
923
+ ...n && { decimal_separator: g(n) }
924
+ });
925
+ }, i = parseTex(e.currentValue), a = t.answers.filter((e) => e.status === "correct").every((e) => e.value != null && Math.abs(e.value) <= 1), o = i;
926
+ t.coefficient && (o ? o === "-" && (o = -1) : o = 1);
927
+ let s = t.answers.map((e) => {
928
+ let t = createValidator(e)(maybeParsePercentInput(o, a));
929
+ return {
930
+ ...e,
931
+ score: t
932
+ };
933
+ }).find((e) => e.score.correct || e.status === "correct" && e.score.empty), c = s?.status === "correct" ? s.score : {
934
+ empty: s?.status === "ungraded",
935
+ correct: s?.status === "correct",
936
+ message: s?.message ?? null,
937
+ guess: o
938
+ };
939
+ return c.empty ? {
940
+ type: "invalid",
941
+ message: c.message
942
+ } : {
943
+ type: "points",
944
+ earned: +!!c.correct,
945
+ total: 1,
946
+ message: c.message
947
+ };
948
+ }
949
+ //#endregion
950
+ //#region packages/perseus-score/src/widgets/orderer/score-orderer.ts
951
+ function scoreOrderer(e, t) {
952
+ return e == null ? {
953
+ type: "invalid",
954
+ message: null
955
+ } : {
956
+ type: "points",
957
+ earned: +!!b.isEqual(e.current, t.correctOptions.map((e) => e.content)),
958
+ total: 1,
959
+ message: null
960
+ };
961
+ }
962
+ //#endregion
963
+ //#region packages/perseus-score/src/widgets/orderer/validate-orderer.ts
964
+ function validateOrderer(e) {
965
+ return e == null || e.current.length === 0 ? {
966
+ type: "invalid",
967
+ message: null
968
+ } : null;
969
+ }
970
+ //#endregion
971
+ //#region packages/perseus-score/src/widgets/plotter/score-plotter.ts
972
+ function scorePlotter(e, t) {
973
+ return {
974
+ type: "points",
975
+ earned: +!!f(e, t.correct),
976
+ total: 1,
977
+ message: null
978
+ };
979
+ }
980
+ //#endregion
981
+ //#region packages/perseus-score/src/widgets/plotter/validate-plotter.ts
982
+ function validatePlotter(e, t) {
983
+ return e == null || f(e, t.starting) ? {
984
+ type: "invalid",
985
+ message: null
986
+ } : null;
987
+ }
988
+ //#endregion
989
+ //#region packages/perseus-score/src/widgets/radio/score-radio.ts
990
+ function scoreRadio(e, t) {
991
+ if (e == null) return {
992
+ type: "invalid",
993
+ message: null
994
+ };
995
+ if (e.selectedChoiceIds.filter((e) => !t.choices.some((t) => t.id === e)).length > 0) return {
996
+ type: "invalid",
997
+ message: s.INVALID_CHOICE_SELECTION
998
+ };
999
+ let n = e.selectedChoiceIds.length, r = t.choices.reduce((e, t) => t.correct ? e + 1 : e, 0);
1000
+ return r > 1 && n !== r && t.countChoices ? {
1001
+ type: "invalid",
1002
+ message: s.CHOOSE_CORRECT_NUM_ERROR
1003
+ } : t.choices.some((t) => t.isNoneOfTheAbove && e.selectedChoiceIds.includes(t.id)) && n > 1 ? {
1004
+ type: "invalid",
1005
+ message: s.NOT_NONE_ABOVE_ERROR
1006
+ } : {
1007
+ type: "points",
1008
+ earned: +!!t.choices.every((t) => {
1009
+ let n = e.selectedChoiceIds.includes(t.id);
1010
+ return !!t.correct === n;
1011
+ }),
1012
+ total: 1,
1013
+ message: null
1014
+ };
1015
+ }
1016
+ //#endregion
1017
+ //#region packages/perseus-score/src/widgets/radio/validate-radio.ts
1018
+ function validateRadio(e) {
1019
+ return e == null || e.selectedChoiceIds.length === 0 ? {
1020
+ type: "invalid",
1021
+ message: null
1022
+ } : null;
1023
+ }
1024
+ //#endregion
1025
+ //#region packages/perseus-score/src/widgets/sorter/score-sorter.ts
1026
+ function scoreSorter(e, t) {
1027
+ return h(t.correct) ? {
1028
+ type: "points",
1029
+ earned: 1,
1030
+ total: 1,
1031
+ message: null
1032
+ } : {
1033
+ type: "points",
1034
+ earned: +!!f(e.options, t.correct),
1035
+ total: 1,
1036
+ message: null
1037
+ };
1038
+ }
1039
+ //#endregion
1040
+ //#region packages/perseus-score/src/widgets/sorter/validate-sorter.ts
1041
+ function validateSorter(e, t) {
1042
+ return h(t.correct) || e?.changed ? null : {
1043
+ type: "invalid",
1044
+ message: null
1045
+ };
1046
+ }
1047
+ //#endregion
1048
+ //#region packages/perseus-score/src/widgets/table/utils.ts
1049
+ var filterNonEmpty = function(e) {
1050
+ return e.filter(function(e) {
1051
+ return e.some((e) => e);
1052
+ });
1053
+ };
1054
+ //#endregion
1055
+ //#region packages/perseus-score/src/widgets/table/validate-table.ts
1056
+ function validateTable(e) {
1057
+ let t = filterNonEmpty(e);
1058
+ return t.some(function(e) {
1059
+ return e.some(function(e) {
1060
+ return e === "";
1061
+ });
1062
+ }) || !t.length ? {
1063
+ type: "invalid",
1064
+ message: null
1065
+ } : null;
1066
+ }
1067
+ //#endregion
1068
+ //#region packages/perseus-score/src/widgets/table/score-table.ts
1069
+ function scoreTable(e, t) {
1070
+ let n = validateTable(e);
1071
+ if (n != null) return n;
1072
+ let r = filterNonEmpty(e), i = filterNonEmpty(t.answers);
1073
+ if (r.length !== i.length) return {
1074
+ type: "points",
1075
+ earned: 0,
1076
+ total: 1,
1077
+ message: null
1078
+ };
1079
+ let a = T.number.createValidatorFunctional, o = null;
1080
+ return {
1081
+ type: "points",
1082
+ earned: +!!i.every(function(e) {
1083
+ for (let t = 0; t < r.length; t++) if (r[t].every(function(t, n) {
1084
+ let r = e[n], i = a(r, { simplify: !0 })(t);
1085
+ return i.message && (o = i.message), i.correct;
1086
+ })) return r.splice(t, 1), !0;
1087
+ return !1;
1088
+ }),
1089
+ total: 1,
1090
+ message: o
1091
+ };
1092
+ }
1093
+ //#endregion
1094
+ //#region packages/perseus-score/src/widgets/input-number/score-input-number.ts
1095
+ var H = scoreNumericInput, U = {
1096
+ type: "points",
1097
+ earned: 0,
1098
+ total: 0,
1099
+ message: null
1100
+ };
1101
+ function combineScores(e, t) {
1102
+ let n;
1103
+ if (e.type === "points" && t.type === "points") return n = e.message && t.message && e.message !== t.message ? null : e.message || t.message, {
1104
+ type: "points",
1105
+ earned: e.earned + t.earned,
1106
+ total: e.total + t.total,
1107
+ message: n
1108
+ };
1109
+ if (e.type === "points" && t.type === "invalid") return t;
1110
+ if (e.type === "invalid" && t.type === "points") return e;
1111
+ if (e.type === "invalid" && t.type === "invalid") return n = e.message && t.message && e.message !== t.message ? null : e.message || t.message, {
1112
+ type: "invalid",
1113
+ message: n
1114
+ };
1115
+ throw new u("PerseusScore with unknown type encountered", c.InvalidInput, { metadata: {
1116
+ scoreA: JSON.stringify(e),
1117
+ scoreB: JSON.stringify(t)
1118
+ } });
1119
+ }
1120
+ function flattenScores(e) {
1121
+ return Object.values(e).reduce(combineScores, U);
1122
+ }
1123
+ //#endregion
1124
+ //#region packages/perseus-score/src/widgets/free-response/score-free-response.ts
1125
+ function scoreFreeResponse(e, t, n) {
1126
+ return {
1127
+ type: "points",
1128
+ earned: 0,
1129
+ total: 0,
1130
+ message: null
1131
+ };
1132
+ }
1133
+ //#endregion
1134
+ //#region packages/perseus-score/src/widgets/free-response/validate-free-response.ts
1135
+ function validateFreeResponse(e, t) {
1136
+ let n = e?.currentValue.trim().length ?? 0;
1137
+ return n === 0 ? {
1138
+ type: "invalid",
1139
+ message: s.USER_INPUT_EMPTY
1140
+ } : !t.allowUnlimitedCharacters && n > t.characterLimit ? {
1141
+ type: "invalid",
1142
+ message: s.USER_INPUT_TOO_LONG
1143
+ } : null;
1144
+ }
1145
+ //#endregion
1146
+ //#region packages/perseus-score/src/widgets/group/score-group.ts
1147
+ function scoreGroup(e, t, n) {
1148
+ return e == null ? {
1149
+ type: "invalid",
1150
+ message: null
1151
+ } : flattenScores(scoreWidgetsFunctional(t.widgets, Object.keys(t.widgets), e, n));
1152
+ }
1153
+ //#endregion
1154
+ //#region packages/perseus-score/src/util/score-is-empty.ts
1155
+ function scoreIsEmpty(e) {
1156
+ return e.type === "invalid" && (!e.message || e.message.length === 0);
1157
+ }
1158
+ //#endregion
1159
+ //#region packages/perseus-score/src/validate.ts
1160
+ function validateUserInput(e, t, n) {
1161
+ let r = getScoreableWidgets(e), i = {};
1162
+ return r.forEach((r) => {
1163
+ let a = e.widgets[r], o = t[r], s = getWidgetValidator(a.type)?.(o, a.options, n);
1164
+ s != null && (i[r] = s);
1165
+ }), Object.keys(i).length > 0 ? flattenScores(i) : null;
1166
+ }
1167
+ function emptyWidgetsFunctional(e, t, n, r) {
1168
+ return t.filter((t) => {
1169
+ let i = e[t];
1170
+ if (!isWidgetScoreable(i)) return !1;
1171
+ let a = getWidgetValidator(i.type), o = n[t], s = i.options, c = a?.(o, s, r);
1172
+ return c ? scoreIsEmpty(c) : !1;
1173
+ });
1174
+ }
1175
+ //#endregion
1176
+ //#region packages/perseus-score/src/widgets/group/validate-group.ts
1177
+ function validateGroup(e, t, n) {
1178
+ if (e == null) return {
1179
+ type: "invalid",
1180
+ message: null
1181
+ };
1182
+ let r = y(t.content);
1183
+ return emptyWidgetsFunctional(t.widgets, r, e, n).length === 0 ? null : {
1184
+ type: "invalid",
1185
+ message: null
1186
+ };
1187
+ }
1188
+ //#endregion
1189
+ //#region packages/perseus-score/src/widgets/label-image/validate-label-image.ts
1190
+ function validateLabelImage(e) {
1191
+ if (e == null) return {
1192
+ type: "invalid",
1193
+ message: null
1194
+ };
1195
+ let t = 0;
1196
+ for (let n = 0; n < e.markers.length; n++) {
1197
+ let r = e.markers[n].selected;
1198
+ r && r.length > 0 && t++;
1199
+ }
1200
+ return t === e.markers.length ? null : {
1201
+ type: "invalid",
1202
+ message: null
1203
+ };
1204
+ }
1205
+ //#endregion
1206
+ //#region packages/perseus-score/src/widgets/mock-widget/validate-mock-widget.ts
1207
+ function validateMockWidget(e) {
1208
+ return e?.currentValue == null || e.currentValue === "" ? {
1209
+ type: "invalid",
1210
+ message: ""
1211
+ } : null;
1212
+ }
1213
+ //#endregion
1214
+ //#region packages/perseus-score/src/widgets/mock-widget/score-mock-widget.ts
1215
+ function scoreMockWidget(e, t) {
1216
+ return validateMockWidget(e) ?? {
1217
+ type: "points",
1218
+ earned: +(e?.currentValue === t.value),
1219
+ total: 1,
1220
+ message: ""
1221
+ };
1222
+ }
1223
+ //#endregion
1224
+ //#region packages/perseus-score/src/widgets/widget-registry.ts
1225
+ var W = new d("Score widget registry");
1226
+ function registerWidget(e, t, n) {
1227
+ let r = {
1228
+ scorer: t,
1229
+ validator: n
1230
+ };
1231
+ W.set(e, r);
1232
+ }
1233
+ var getWidgetValidator = (e) => W.get(e)?.validator ?? null, getWidgetScorer = (e) => W.get(e)?.scorer ?? null;
1234
+ registerWidget("categorizer", scoreCategorizer, validateCategorizer), registerWidget("cs-program", scoreCSProgram), registerWidget("dropdown", scoreDropdown, validateDropdown), registerWidget("expression", scoreExpression, validateExpression), registerWidget("free-response", scoreFreeResponse, validateFreeResponse), registerWidget("grapher", scoreGrapher), registerWidget("group", scoreGroup, validateGroup), registerWidget("iframe", scoreIframe), registerWidget("input-number", H), registerWidget("interactive-graph", scoreInteractiveGraph), registerWidget("label-image", scoreLabelImage, validateLabelImage), registerWidget("matcher", scoreMatcher), registerWidget("matrix", scoreMatrix, validateMatrix), registerWidget("mock-widget", scoreMockWidget, scoreMockWidget), registerWidget("number-line", scoreNumberLine), registerWidget("numeric-input", scoreNumericInput), registerWidget("orderer", scoreOrderer, validateOrderer), registerWidget("plotter", scorePlotter, validatePlotter), registerWidget("radio", scoreRadio, validateRadio), registerWidget("sorter", scoreSorter, validateSorter), registerWidget("table", scoreTable, validateTable);
1235
+ //#endregion
1236
+ //#region packages/perseus-score/src/util/is-widget-scoreable.ts
1237
+ function isWidgetScoreable(e) {
1238
+ if (!e) return !1;
1239
+ let t = e.graded == null || e.graded, n = !!e.static, r = getWidgetScorer(e.type) !== null;
1240
+ return t && !n && r;
1241
+ }
1242
+ //#endregion
1243
+ //#region packages/perseus-score/src/util/get-scoreable-widgets.ts
1244
+ function getScoreableWidgets(e) {
1245
+ return y(e.content).filter((t) => isWidgetScoreable(e.widgets[t]));
1246
+ }
1247
+ //#endregion
1248
+ //#region packages/perseus-score/src/score.ts
1249
+ function onlyInvalidScores(e) {
1250
+ return Object.fromEntries(Object.entries(e).filter(([e, t]) => t.type === "invalid"));
1251
+ }
1252
+ function combineScoreWithWidgetScores(e, t) {
1253
+ return e.type === "invalid" ? {
1254
+ ...e,
1255
+ widgetScores: onlyInvalidScores(t)
1256
+ } : {
1257
+ ...e,
1258
+ widgetScores: t
1259
+ };
1260
+ }
1261
+ function scorePerseusItem(e, t, n) {
1262
+ let r = getScoreableWidgets(e), i = scoreWidgetsFunctional(e.widgets, r, t, n);
1263
+ return combineScoreWithWidgetScores(flattenScores(i), i);
1264
+ }
1265
+ function scoreWidgetsFunctional(e, t, n, r) {
1266
+ let i = t.filter((t) => isWidgetScoreable(e[t])), a = {};
1267
+ return i.forEach((t) => {
1268
+ let i = e[t], o = n[t], s = getWidgetValidator(i.type), c = getWidgetScorer(i.type), l = s?.(o, i.options, r) ?? c?.(o, i.options, r);
1269
+ l != null && (a[t] = l);
1270
+ }), a;
1271
+ }
1272
+ //#endregion
1273
+ //#region packages/perseus-score/src/has-empty-diner-widgets.ts
1274
+ function hasEmptyDINERWidgets(e, t) {
1275
+ let n = y(e.question.content), r = e.question.widgets;
1276
+ for (let e of n) {
1277
+ let n = r[e], i = t[e];
1278
+ switch (n.type) {
1279
+ case "dropdown":
1280
+ if (i.value === 0) return !0;
1281
+ break;
1282
+ case "interactive-graph": break;
1283
+ case "numeric-input":
1284
+ if (!i.currentValue && !n.options.coefficient) return !0;
1285
+ break;
1286
+ case "expression":
1287
+ if (!i) return !0;
1288
+ break;
1289
+ case "radio": if (i.selectedChoiceIds.length === 0) return !0;
1290
+ }
1291
+ }
1292
+ return !1;
1293
+ }
1294
+ //#endregion
1295
+ export { T as KhanAnswerTypes, emptyWidgetsFunctional, flattenScores, getWidgetScorer, getWidgetValidator, hasEmptyDINERWidgets, isWidgetScoreable, C as libVersion, registerWidget, scoreCSProgram, scoreCategorizer, scoreDropdown, scoreExpression, scoreGrapher, scoreIframe, H as scoreInputNumber, scoreInteractiveGraph, scoreLabelImage, scoreLabelImageMarker, scoreMatcher, scoreMatrix, scoreNumberLine, scoreNumericInput, scoreOrderer, scorePerseusItem, scorePlotter, scoreRadio, scoreSorter, scoreTable, scoreWidgetsFunctional, validateCategorizer, validateDropdown, validateExpression, validateMatrix, validateOrderer, validatePlotter, validateRadio, validateSorter, validateTable, validateUserInput };
7
1296
 
8
- const libName="@khanacademy/perseus-score";const libVersion="9.0.9";addLibraryVersionToPerseusDebug(libName,libVersion);
9
-
10
- const MAXERROR_EPSILON=Math.pow(2,-42);const KhanAnswerTypes={predicate:{defaultForms:"integer, proper, improper, mixed, decimal",createValidatorFunctional:function(predicate,rawOptions){const options={simplify:"required",ratio:false,forms:KhanAnswerTypes.predicate.defaultForms,...rawOptions};let acceptableForms;if(!Array.isArray(options.forms)){acceptableForms=options.forms.split(/\s*,\s*/);}else {acceptableForms=options.forms;}if(options.inexact===undefined){options.maxError=0;}const maxError=+(options.maxError??0)+MAXERROR_EPSILON;if(acceptableForms.includes("percent")){acceptableForms=_.without(acceptableForms,"percent");acceptableForms.push("percent");}const fractionTransformer=function(text){text=text.replace(/\u2212/,"-").replace(/([+-])\s+/g,"$1").replace(/(^\s*)|(\s*$)/gi,"");const match=text.match(/^([+-]?\d+)\s*\/\s*([+-]?\d+)$/);const mobileDeviceMatch=text.match(/^([+-]?)\\frac\{([+-]?\d+)\}\{([+-]?\d+)\}$/);const parsedInt=parseInt(text,10);if(match||mobileDeviceMatch){let num;let denom;let simplified=true;if(match){num=parseFloat(match[1]);denom=parseFloat(match[2]);}else {num=parseFloat(mobileDeviceMatch[2]);if(mobileDeviceMatch[1]==="-"){if(num<0){simplified=false;}num=-num;}denom=parseFloat(mobileDeviceMatch[3]);}simplified=simplified&&denom>0&&(options.ratio||denom!==1)&&KhanMath.getGCD(num,denom)===1;return [{value:num/denom,exact:simplified}]}if(!isNaN(parsedInt)&&""+parsedInt===text){return [{value:parsedInt,exact:true}]}return []};const forms={integer:function(text){const decimal=forms.decimal(text);const rounded=forms.decimal(text,1);if(decimal[0]?.value!=null&&decimal[0].value===rounded[0]?.value||decimal[1]?.value!=null&&decimal[1].value===rounded[1]?.value){return decimal}return []},proper:function(text){const transformed=fractionTransformer(text);return transformed.flatMap(o=>{if(Math.abs(o.value)<1){return [o]}return []})},improper:function(text){const fractionExists=text.includes("/")||text.match(/\\(d?frac)/);if(!fractionExists){return []}const transformed=fractionTransformer(text);return transformed.flatMap(o=>{if(Math.abs(o.value)>=1){return [o]}return []})},pi:function(text){let match;let possibilities=[];text=text.replace(/\u2212/,"-");if(match=text.match(/^([+-]?)\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)){possibilities=[{value:parseFloat(match[1]+"1"),exact:true}];}else if(match=text.match(/^([+-]?\s*\d+\s*(?:\/\s*[+-]?\s*\d+)?)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)){possibilities=fractionTransformer(match[1]);}else if(match=text.match(/^([+-]?)\s*(\d+)\s*([+-]?\d+)\s*\/\s*([+-]?\d+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)){const sign=parseFloat(match[1]+"1");const integ=parseFloat(match[2]);const num=parseFloat(match[3]);const denom=parseFloat(match[4]);const simplified=num<denom&&KhanMath.getGCD(num,denom)===1;possibilities=[{value:sign*(integ+num/denom),exact:simplified}];}else if(match=text.match(/^([+-]?\s*\d+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)\s*(?:\/\s*([+-]?\s*\d+))?$/i)){possibilities=fractionTransformer(match[1]+"/"+match[3]);}else if(match=text.match(/^([+-]?)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)\s*(?:\/\s*([+-]?\d+))?$/i)){possibilities=fractionTransformer(match[1]+"1/"+match[3]);}else if(text==="0"){possibilities=[{value:0,exact:true}];}else if(match=text.match(/^(.+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)){possibilities=forms.decimal(match[1]);}else {possibilities=_.reduce(KhanAnswerTypes.predicate.defaultForms.split(/\s*,\s*/),function(memo,form){return memo.concat(forms[form](text))},[]);let approximatesPi=false;const number=parseFloat(text);if(!isNaN(number)&&number!==parseInt(text)){const piMult=Math.PI/12;const roundedNumber=piMult*Math.round(number/piMult);if(Math.abs(number-roundedNumber)<.01){approximatesPi=true;}}else if(text.match(/\/\s*7/)){approximatesPi=true;}if(approximatesPi){_.each(possibilities,function(possibility){possibility.piApprox=true;});}return possibilities}let multiplier=Math.PI;if(text.match(/\\?tau|t|\u03c4/)){multiplier=Math.PI*2;}if(text.match(/pau/)){multiplier=Math.PI*1.5;}possibilities.forEach(possibility=>{possibility.value*=multiplier;});return possibilities},coefficient:function(text){let possibilities=[];text=text.replace(/\u2212/,"-");if(text===""){possibilities=[{value:1,exact:true}];}else if(text==="-"){possibilities=[{value:-1,exact:true}];}return possibilities},log:function(text){let match;let possibilities=[];text=text.replace(/\u2212/,"-");text=text.replace(/[ ()]/g,"");if(match=text.match(/^log\s*(\S+)\s*$/i)){possibilities=forms.decimal(match[1]);}else if(text==="0"){possibilities=[{value:0,exact:true}];}return possibilities},percent:function(text){text=String(text).trim();let hasPercentSign=false;if(text.indexOf("%")===text.length-1){text=text.substring(0,text.length-1).trim();hasPercentSign=true;}const transformed=forms.decimal(text);transformed.forEach(t=>{t.exact=hasPercentSign;t.value=t.value/100;});return transformed},mixed:function(text){const match=text.replace(/\u2212/,"-").replace(/([+-])\s+/g,"$1").match(/^([+-]?)(\d+)\s+(\d+)\s*\/\s*(\d+)$/);if(match){const sign=parseFloat(match[1]+"1");const integ=parseFloat(match[2]);const num=parseFloat(match[3]);const denom=parseFloat(match[4]);const simplified=num<denom&&KhanMath.getGCD(num,denom)===1;return [{value:sign*(integ+num/denom),exact:simplified}]}return []},decimal:function(text,precision=1e10){const normal=function(text){text=String(text).trim();const match=text.replace(/\u2212/,"-").replace(/([+-])\s+/g,"$1").match(/^([+-]?(?:\d{1,3}(?:[, ]?\d{3})*\.?|\d{0,3}(?:[, ]?\d{3})*\.(?:\d{3}[, ]?)*\d{1,3}))$/);const badLeadingZero=text.match(/^0[0,]*,/);if(match&&!badLeadingZero){let x=parseFloat(match[1].replace(/[, ]/g,""));if(options.inexact===undefined){x=Math.round(x*precision)/precision;}return x}};const commas=function(text){text=text.replace(/([.,])/g,function(_,c){return c==="."?",":"."});return normal(text)};const results=[{value:normal(text),exact:true}];if(options.decimal_separator===","){results.push({value:commas(text),exact:true});}return results}};return function(guess){const fallback=options.fallback!=null?""+options.fallback:"";guess=String(guess).trim()||fallback;const score={empty:guess==="",correct:false,message:null,guess:guess};const findCorrectAnswer=()=>{for(const form of acceptableForms){const transformed=forms[form](guess);for(let j=0,l=transformed.length;j<l;j++){const val=transformed[j].value;const exact=transformed[j].exact;const piApprox=transformed[j].piApprox;if(predicate(val,maxError)){if(exact||options.simplify==="optional"){score.correct=true;score.message=options.message||null;score.empty=false;}else if(form==="percent"){score.empty=true;score.message=ErrorCodes.MISSING_PERCENT_ERROR;}else {if(options.simplify!=="enforced"){score.empty=true;}score.message=ErrorCodes.NEEDS_TO_BE_SIMPLIFIED_ERROR;}return false}if(piApprox&&predicate(val,Math.abs(val*.001))){score.empty=true;score.message=ErrorCodes.APPROXIMATED_PI_ERROR;}}}};findCorrectAnswer();if(score.correct===false){let interpretedGuess=false;_.each(forms,function(form){const anyAreNaN=_.any(form(guess),function(t){return t.value!=null&&!Number.isNaN(t.value)});if(anyAreNaN){interpretedGuess=true;}});if(!interpretedGuess){score.empty=true;score.message=ErrorCodes.EXTRA_SYMBOLS_ERROR;return score}}return score}}},number:{createValidatorFunctional:function(correctAnswer,options){const correctFloat=parseFloat(correctAnswer);return KhanAnswerTypes.predicate.createValidatorFunctional((guess,maxError)=>Math.abs(guess-correctFloat)<maxError,{...options})}},expression:{parseSolution:function(solutionString,options){let solution=KAS.parse(solutionString,options);if(!solution.parsed){throw new PerseusError("The provided solution ("+solutionString+") didn't parse.",Errors.InvalidInput)}else if(options.simplified&&!solution.expr.isSimplified()){throw new PerseusError("The provided solution ("+solutionString+") isn't fully expanded and simplified.",Errors.InvalidInput)}else {solution=solution.expr;}return solution},createValidatorFunctional:function(solution,options){return function(guess){const score={empty:false,correct:false,message:null,guess:guess,ungraded:false};if(!guess){score.empty=true;return score}const answer=KAS.parse(guess,options);if(!answer.parsed){score.empty=true;return score}if(typeof solution==="string"){solution=KhanAnswerTypes.expression.parseSolution(solution,options);}const result=KAS.compare(answer.expr,solution,options);if(result.equal){score.correct=true;}else if(result.message){score.message=result.message;}else {const answerX=KAS.parse(guess.replace(/[xX]/g,"*"),options);if(answerX.parsed){const resultX=KAS.compare(answerX.expr,solution,options);if(resultX.equal){score.ungraded=true;score.message=ErrorCodes.MULTIPLICATION_SIGN_ERROR;}else if(resultX.message){score.message=resultX.message+" Also, I'm a computer. I only understand "+"multiplication if you use an "+"asterisk (*) as the multiplication "+"sign.";}}}return score}}}};
11
-
12
- function scoreCategorizer(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}let allCorrect=true;rubric.values.forEach((value,i)=>{if(userInput.values[i]!==value){allCorrect=false;}});return {type:"points",earned:allCorrect?1:0,total:1,message:null}}
13
-
14
- function validateCategorizer(userInput,validationData){if(userInput==null){return {type:"invalid",message:null}}const incomplete=validationData.items.some((_,i)=>userInput.values[i]==null);if(incomplete){return {type:"invalid",message:ErrorCodes.INVALID_SELECTION_ERROR}}return null}
15
-
16
- function scoreCSProgram(userInput){if(userInput==null){return {type:"invalid",message:null}}if(userInput.status==="correct"){return {type:"points",earned:1,total:1,message:userInput.message||null}}if(userInput.status==="incorrect"){return {type:"points",earned:0,total:1,message:userInput.message||null}}return {type:"invalid",message:"Keep going, you're not there yet!"}}
17
-
18
- function scoreDropdown(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}const correct=rubric.choices[userInput.value-1].correct;return {type:"points",earned:correct?1:0,total:1,message:null}}
19
-
20
- function validateDropdown(userInput){if(userInput==null){return {type:"invalid",message:null}}if(userInput.value===0){return {type:"invalid",message:null}}return null}
21
-
22
- function getVarError(studentVars,answerVars,extraKeys){if(studentVars.every(v=>answerVars.includes(v))){return}const studentVarsLower=[...new Set(studentVars.map(v=>v.toLowerCase()))];const answerVarsLower=answerVars.map(v=>v.toLowerCase());if(studentVarsLower.every(v=>answerVarsLower.includes(v))){return ErrorCodes.WRONG_CASE_ERROR}const extraVars=studentVars.filter(v=>!answerVars.includes(v));if(extraVars.some(v=>!extraKeys.includes(v))){return ErrorCodes.WRONG_LETTER_ERROR}return}function scoreExpression(userInput,rubric,locale){if(userInput==null){return {type:"invalid",message:null}}const options={...rubric,decimal_separator:getDecimalSeparator(locale),divide_symbol:getDivideSymbol(locale)};const parsedStudent=KAS.parse(userInput,options);if(!parsedStudent.parsed){return {type:"invalid",message:ErrorCodes.EXTRA_SYMBOLS_ERROR}}const studentVars=parsedStudent.expr.getVars();const createValidator=answer=>{const expression=KAS.parse(answer.value,rubric);if(!expression.parsed){throw new PerseusError("Unable to parse solution answer for expression",Errors.InvalidInput,{metadata:{rubric:JSON.stringify(rubric)}})}const varError=getVarError(studentVars,expression.expr.getVars(),rubric.extraKeys??[]);if(varError!=null){return input=>({correct:false,empty:false,ungraded:true,suppressAlmostThere:true,message:varError,guess:input})}return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr,{...options,simplify:answer.simplify,form:answer.form})};let matchingAnswerForm;let matchMessage;let allEmpty=true;let firstUngradedResult;for(const answerForm of rubric.answerForms??[]){const validator=createValidator(answerForm);if(validator==null){continue}const result=validator(userInput);if(result.correct){matchingAnswerForm=answerForm;matchMessage=result.message||"";break}allEmpty=allEmpty&&result.empty;if(answerForm.considered==="correct"&&result.ungraded&&!firstUngradedResult){firstUngradedResult=result;}}if(!matchingAnswerForm){if(firstUngradedResult){return {type:"invalid",message:firstUngradedResult.message,suppressAlmostThere:firstUngradedResult.suppressAlmostThere}}if(allEmpty){return {type:"invalid",message:null}}return {type:"points",earned:0,total:1}}if(matchingAnswerForm.considered==="ungraded"){return {type:"invalid",message:matchMessage}}return {type:"points",earned:matchingAnswerForm.considered==="correct"?1:0,total:1,message:matchMessage}}
23
-
24
- function validateExpression(userInput){if(userInput===""||userInput==null){return {type:"invalid",message:null}}return null}
25
-
26
- function getCoefficientsByType(data){if(data.coords==null){return undefined}if(data.type==="exponential"||data.type==="logarithm"){const grader=GrapherUtil.functionForType(data.type);return grader.getCoefficients(data.coords,data.asymptote)}else if(data.type==="tangent"){const coeffs=coefficients.getTangentCoefficients(data.coords);if(coeffs.some(c=>!isFinite(c))){return undefined}return coeffs}else if(data.type==="linear"||data.type==="quadratic"||data.type==="absolute_value"||data.type==="sinusoid"){const grader=GrapherUtil.functionForType(data.type);return grader.getCoefficients(data.coords)}else {throw new PerseusError("Invalid grapher type",Errors.InvalidInput)}}function scoreGrapher(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}invariant(rubric.correct!=null,"correct is null or undefined in scoreGrapher");if(userInput.type!==rubric.correct.type){return {type:"points",earned:0,total:1,message:null}}if(userInput.coords==null){return {type:"invalid",message:null}}const guessCoeffs=getCoefficientsByType(userInput);const correctCoeffs=getCoefficientsByType(rubric.correct);if(guessCoeffs==null||correctCoeffs==null){return {type:"invalid",message:null}}const isEqual=userInput.type==="tangent"?approximateDeepEqual(geometry.canonicalTangentCoefficients(guessCoeffs),geometry.canonicalTangentCoefficients(correctCoeffs)):GrapherUtil.functionForType(userInput.type).areEqual(guessCoeffs,correctCoeffs);if(isEqual){return {type:"points",earned:1,total:1,message:null}}return {type:"points",earned:0,total:1,message:null}}
27
-
28
- function scoreIframe(userInput){if(userInput==null){return {type:"invalid",message:null}}if(userInput.status==="correct"){return {type:"points",earned:1,total:1,message:userInput.message||null}}if(userInput.status==="incorrect"){return {type:"points",earned:0,total:1,message:userInput.message||null}}return {type:"invalid",message:"Keep going, you're not there yet!"}}
29
-
30
- const{getAbsoluteValueCoefficients}=coefficients;function scoreAbsoluteValue(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const userCoeffs=getAbsoluteValueCoefficients(userInput.coords);const rubricCoeffs=getAbsoluteValueCoefficients(rubric.coords);const isCorrect=approximateDeepEqual(userCoeffs,rubricCoeffs);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
31
-
32
- const{collinear: collinear$3,clockwise}=geometry;const{getClockwiseAngle}=angles;function scoreAngle(userInput,rubric){const coords=userInput.coords;const correct=rubric.coords;const allowReflexAngles=rubric.allowReflexAngles;if(!coords){return {type:"invalid",message:null}}const areClockwise=clockwise([coords[0],coords[2],coords[1]]);const shouldReverseCoords=areClockwise&&!allowReflexAngles;const guess=shouldReverseCoords?coords.slice().reverse():coords;let match;if(rubric.match==="congruent"){const guessAngle=getClockwiseAngle(guess,allowReflexAngles);const correctAngle=correct?getClockwiseAngle(correct,allowReflexAngles):null;match=correctAngle!==null&&approximateEqual(guessAngle,correctAngle);}else {match=correct!=null&&approximateDeepEqual(guess[1],correct[1])&&collinear$3(correct[1],correct[0],guess[0])&&collinear$3(correct[1],correct[2],guess[2]);}return {type:"points",earned:match?1:0,total:1,message:null}}
33
-
34
- function scoreCircle(userInput,rubric){if(userInput.center==null||userInput.radius==null||rubric.center==null||rubric.radius==null){return {type:"invalid",message:null}}const isCorrect=approximateDeepEqual(userInput.center,rubric.center)&&approximateEqual(userInput.radius,rubric.radius);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
35
-
36
- const{getExponentialCoefficients}=coefficients;function scoreExponential(userInput,rubric){if(!userInput.coords||userInput.asymptote==null||!rubric.coords||rubric.asymptote==null){return {type:"invalid",message:null}}const guessCoeffs=getExponentialCoefficients(userInput.coords,userInput.asymptote);const correctCoeffs=getExponentialCoefficients(rubric.coords,rubric.asymptote);const isCorrect=guessCoeffs!=null&&correctCoeffs!=null&&approximateDeepEqual([guessCoeffs.a,guessCoeffs.b,guessCoeffs.c],[correctCoeffs.a,correctCoeffs.b,correctCoeffs.c]);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
37
-
38
- const{collinear: collinear$2}=geometry;function scoreLinear(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const guess=userInput.coords;const correct=rubric.coords;const isCorrect=collinear$2(correct[0],correct[1],guess[0])&&collinear$2(correct[0],correct[1],guess[1]);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
39
-
40
- const{collinear: collinear$1}=geometry;function scoreLinearSystem(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const guess=userInput.coords;const correct=rubric.coords;const isCorrect=collinear$1(correct[0][0],correct[0][1],guess[0][0])&&collinear$1(correct[0][0],correct[0][1],guess[0][1])&&collinear$1(correct[1][0],correct[1][1],guess[1][0])&&collinear$1(correct[1][0],correct[1][1],guess[1][1])||collinear$1(correct[0][0],correct[0][1],guess[1][0])&&collinear$1(correct[0][0],correct[0][1],guess[1][1])&&collinear$1(correct[1][0],correct[1][1],guess[0][0])&&collinear$1(correct[1][0],correct[1][1],guess[0][1]);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
41
-
42
- const{getLogarithmCoefficients}=coefficients;function scoreLogarithm(userInput,rubric){if(!userInput.coords||userInput.asymptote==null||!rubric.coords||rubric.asymptote==null){return {type:"invalid",message:null}}const guessCoeffs=getLogarithmCoefficients(userInput.coords,userInput.asymptote);const correctCoeffs=getLogarithmCoefficients(rubric.coords,rubric.asymptote);const isCorrect=guessCoeffs!=null&&correctCoeffs!=null&&approximateDeepEqual([guessCoeffs.a,guessCoeffs.b,guessCoeffs.c],[correctCoeffs.a,correctCoeffs.b,correctCoeffs.c]);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
43
-
44
- function scorePoint(userInput,rubric){if(!userInput.coords){return {type:"invalid",message:null}}if(rubric.coords==null){throw new Error("Point graph rubric has null coords")}const guess=userInput.coords.slice();const correct=rubric.coords.slice();guess.sort();correct.sort();return {type:"points",earned:approximateDeepEqual(guess,correct)?1:0,total:1,message:null}}
45
-
46
- const{similar}=geometry;function scorePolygon(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const guess=userInput.coords.slice();const correct=rubric.coords.slice();let match;if(rubric.match==="similar"){match=similar(guess,correct,Number.POSITIVE_INFINITY);}else if(rubric.match==="congruent"){match=similar(guess,correct,number.DEFAULT_TOLERANCE);}else if(rubric.match==="approx"){match=similar(guess,correct,.1);}else {guess.sort();correct.sort();match=approximateDeepEqual(guess,correct);}return {type:"points",earned:match?1:0,total:1,message:null}}
47
-
48
- const{getQuadraticCoefficients}=coefficients;function scoreQuadratic(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const guessCoeffs=getQuadraticCoefficients(userInput.coords);const correctCoeffs=getQuadraticCoefficients(rubric.coords);const isCorrect=approximateDeepEqual(guessCoeffs,correctCoeffs);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
49
-
50
- const{collinear}=geometry;function scoreRay(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const guess=userInput.coords;const correct=rubric.coords;const isCorrect=approximateDeepEqual(guess[0],correct[0])&&collinear(correct[0],correct[1],guess[1]);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
51
-
52
- function scoreSegment(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}let guess=deepClone(userInput.coords);let correct=deepClone(rubric.coords);guess=_.invoke(guess,"sort").sort();correct=_.invoke(correct,"sort").sort();return {type:"points",earned:approximateDeepEqual(guess,correct)?1:0,total:1,message:null}}
53
-
54
- const{getSinusoidCoefficients}=coefficients;const{canonicalSineCoefficients}=geometry;function scoreSinusoid(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const guessCoeffs=getSinusoidCoefficients(userInput.coords);const correctCoeffs=getSinusoidCoefficients(rubric.coords);const canonicalGuessCoeffs=canonicalSineCoefficients(guessCoeffs);const canonicalCorrectCoeffs=canonicalSineCoefficients(correctCoeffs);const isCorrect=approximateDeepEqual(canonicalGuessCoeffs,canonicalCorrectCoeffs);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
55
-
56
- const{getTangentCoefficients}=coefficients;const{canonicalTangentCoefficients}=geometry;function scoreTangent(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const guessCoeffs=getTangentCoefficients(userInput.coords);const correctCoeffs=getTangentCoefficients(rubric.coords);const canonicalGuessCoeffs=canonicalTangentCoefficients(guessCoeffs);const canonicalCorrectCoeffs=canonicalTangentCoefficients(correctCoeffs);const isCorrect=approximateDeepEqual(canonicalGuessCoeffs,canonicalCorrectCoeffs);return {type:"points",earned:isCorrect?1:0,total:1,message:null}}
57
-
58
- function scoreVector(userInput,rubric){if(!userInput.coords||!rubric.coords){return {type:"invalid",message:null}}const guess=userInput.coords;const correct=rubric.coords;let match;if(rubric.match==="congruent"){const guessDelta=[guess[1][0]-guess[0][0],guess[1][1]-guess[0][1]];const correctDelta=[correct[1][0]-correct[0][0],correct[1][1]-correct[0][1]];match=approximateDeepEqual(guessDelta,correctDelta);}else {match=approximateDeepEqual(guess[0],correct[0])&&approximateDeepEqual(guess[1],correct[1]);}return {type:"points",earned:match?1:0,total:1,message:null}}
59
-
60
- function scoreInteractiveGraph(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}if(userInput.type==="none"&&rubric.correct.type==="none"){return {type:"points",earned:0,total:0,message:null}}const hasValue=Boolean(userInput.coords||userInput.center&&userInput.radius);if(userInput.type==="absolute-value"&&rubric.correct.type==="absolute-value"){return scoreAbsoluteValue(userInput,rubric.correct)}else if(userInput.type==="angle"&&rubric.correct.type==="angle"){return scoreAngle(userInput,rubric.correct)}else if(userInput.type==="circle"&&rubric.correct.type==="circle"){return scoreCircle(userInput,rubric.correct)}else if(userInput.type==="exponential"&&rubric.correct.type==="exponential"&&userInput.asymptote!=null){return scoreExponential(userInput,rubric.correct)}else if(userInput.type==="linear"&&rubric.correct.type==="linear"){return scoreLinear(userInput,rubric.correct)}else if(userInput.type==="linear-system"&&rubric.correct.type==="linear-system"){return scoreLinearSystem(userInput,rubric.correct)}else if(userInput.type==="logarithm"&&rubric.correct.type==="logarithm"&&userInput.asymptote!=null){return scoreLogarithm(userInput,rubric.correct)}else if(userInput.type==="point"&&rubric.correct.type==="point"){return scorePoint(userInput,rubric.correct)}else if(userInput.type==="polygon"&&rubric.correct.type==="polygon"){return scorePolygon(userInput,rubric.correct)}else if(userInput.type==="quadratic"&&rubric.correct.type==="quadratic"){return scoreQuadratic(userInput,rubric.correct)}else if(userInput.type==="ray"&&rubric.correct.type==="ray"){return scoreRay(userInput,rubric.correct)}else if(userInput.type==="segment"&&rubric.correct.type==="segment"){return scoreSegment(userInput,rubric.correct)}else if(userInput.type==="sinusoid"&&rubric.correct.type==="sinusoid"){return scoreSinusoid(userInput,rubric.correct)}else if(userInput.type==="tangent"&&rubric.correct.type==="tangent"){return scoreTangent(userInput,rubric.correct)}else if(userInput.type==="vector"&&rubric.correct.type==="vector"){return scoreVector(userInput,rubric.correct)}if(!hasValue||_.isEqual(userInput,rubric.graph)){return {type:"invalid",message:null}}return {type:"points",earned:0,total:1,message:null}}
61
-
62
- function scoreLabelImageMarker(userInput,rubric){const score={hasAnswers:false,isCorrect:false};if(userInput&&userInput.length>0){score.hasAnswers=true;}if(rubric.length>0){if(userInput&&userInput.length===rubric.length){score.isCorrect=userInput.every(choice=>rubric.includes(choice));}}else if(!userInput||userInput.length===0){score.isCorrect=true;}return score}function scoreLabelImage(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}let numCorrect=0;for(let i=0;i<userInput.markers.length;i++){const score=scoreLabelImageMarker(userInput.markers[i].selected,rubric.markers[i].answers);if(score.isCorrect){numCorrect++;}}return {type:"points",earned:numCorrect===userInput.markers.length?1:0,total:1,message:null}}
63
-
64
- function scoreMatcher(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}const correct=_.isEqual(userInput.left,rubric.left)&&_.isEqual(userInput.right,rubric.right);return {type:"points",earned:correct?1:0,total:1,message:null}}
65
-
66
- function scoreMatrix(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}const solution=rubric.answers;const supplied=userInput.answers;const solutionSize=getMatrixSize(solution);const suppliedSize=getMatrixSize(supplied);const incorrectSize=solutionSize[0]!==suppliedSize[0]||solutionSize[1]!==suppliedSize[1];const createValidator=KhanAnswerTypes.number.createValidatorFunctional;let message=null;let incorrect=false;_(suppliedSize[0]).times(row=>{_(suppliedSize[1]).times(col=>{if(!incorrectSize){const validator=createValidator(solution[row][col],{simplify:true});const result=validator(supplied[row][col]);if(result.message){message=result.message;}if(!result.correct){incorrect=true;}}});});if(incorrectSize){return {type:"points",earned:0,total:1,message:null}}return {type:"points",earned:incorrect?0:1,total:1,message:message}}
67
-
68
- function validateMatrix(userInput){if(userInput==null){return {type:"invalid",message:null}}const supplied=userInput.answers;const suppliedSize=getMatrixSize(supplied);for(let row=0;row<suppliedSize[0];row++){for(let col=0;col<suppliedSize[1];col++){const rowData=supplied[row];const cellValue=rowData?.[col];if(cellValue==null||cellValue.toString().length===0){return {type:"invalid",message:ErrorCodes.FILL_ALL_CELLS_ERROR}}}}return null}
69
-
70
- function scoreNumberLine(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}const divisionRange=rubric.divisionRange;const outsideAllowedRange=userInput.numDivisions>divisionRange[1]||userInput.numDivisions<divisionRange[0];if(rubric.isTickCtrl&&outsideAllowedRange){return {type:"invalid",message:"Number of divisions is outside the allowed range."}}const range=rubric.range;const start=rubric.initialX!=null?rubric.initialX:range[0];const startRel=rubric.isInequality?"ge":"eq";const correctRel=rubric.correctRel||"eq";const correctPos=number.equal(userInput.numLinePosition,rubric.correctX||0);if(correctPos&&correctRel===userInput.rel){return {type:"points",earned:1,total:1,message:null}}if(userInput.numLinePosition===start&&userInput.rel===startRel){return {type:"invalid",message:null}}return {type:"points",earned:0,total:1,message:null}}
71
-
72
- function findEndpoint(tex,currentIndex){let bracketDepth=0;for(let i=currentIndex,len=tex.length;i<len;i++){const c=tex[i];if(c==="{"){bracketDepth++;}else if(c==="}"){bracketDepth--;}if(bracketDepth<0){return i}}return tex.length}function parseNextExpression(tex,currentIndex,handler){const openBracketIndex=tex.indexOf("{",currentIndex);if(openBracketIndex===-1){return {endpoint:tex.length,expression:""}}const nextExpIndex=openBracketIndex+1;const endpoint=findEndpoint(tex,nextExpIndex);const expressionTeX=tex.substring(nextExpIndex,endpoint);const parsedExp=walkTex(expressionTeX,handler);return {endpoint:endpoint,expression:parsedExp}}function getNextFracIndex(tex,currentIndex){const dfrac="\\dfrac";const frac="\\frac";const nextFrac=tex.indexOf(frac,currentIndex);const nextDFrac=tex.indexOf(dfrac,currentIndex);if(nextFrac>-1&&nextDFrac>-1){return Math.min(nextFrac,nextDFrac)}if(nextFrac>-1){return nextFrac}if(nextDFrac>-1){return nextDFrac}return -1}function walkTex(tex,handler){if(!tex){return ""}let parsedString="";let currentIndex=0;let nextFrac=getNextFracIndex(tex,currentIndex);while(nextFrac>-1){parsedString+=tex.substring(currentIndex,nextFrac);currentIndex=nextFrac;const firstParsedExpression=parseNextExpression(tex,currentIndex,handler);currentIndex=firstParsedExpression.endpoint+1;const secondParsedExpression=parseNextExpression(tex,currentIndex,handler);currentIndex=secondParsedExpression.endpoint+1;if(parsedString.length){parsedString+=" ";}parsedString+=handler(firstParsedExpression.expression,secondParsedExpression.expression);nextFrac=getNextFracIndex(tex,currentIndex);}parsedString+=tex.slice(currentIndex);return parsedString}function parseTex(tex){const handler=function(exp1,exp2){return exp1+"/"+exp2};const texWithoutFracs=walkTex(tex,handler);return texWithoutFracs.replace("\\%","%")}
73
-
74
- const answerFormButtons=[{title:"Integers",value:"integer",content:"6"},{title:"Decimals",value:"decimal",content:"0.75"},{title:"Proper fractions",value:"proper",content:"⅗"},{title:"Improper fractions",value:"improper",content:"⁷⁄₄"},{title:"Mixed numbers",value:"mixed",content:"1¾"},{title:"Numbers with π",value:"pi",content:"π"}];function maybeParsePercentInput(inputValue,normalizedAnswerExpected){if(!(typeof inputValue==="string"&&inputValue.endsWith("%"))){return inputValue}const value=parseFloat(inputValue.slice(0,-1));if(isNaN(value)){return inputValue}if(normalizedAnswerExpected){return value/100}return value}function scoreNumericInput(userInput,rubric,locale){if(userInput==null){return {type:"invalid",message:null}}const defaultAnswerForms=answerFormButtons.map(e=>e["value"]).filter(e=>e!=="pi");const createValidator=answer=>{const stringAnswer=`${answer.value}`;const validatorForms=[...answer.answerForms??[]];if(!answer.strict||validatorForms.length===0){validatorForms.push(...defaultAnswerForms);}return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer,{message:answer.message,simplify:answer.status==="correct"?answer.simplify:"optional",inexact:true,maxError:answer.maxError,forms:validatorForms,...locale&&{decimal_separator:getDecimalSeparator(locale)}})};const currentValue=parseTex(userInput.currentValue);const normalizedAnswerExpected=rubric.answers.filter(answer=>answer.status==="correct").every(answer=>answer.value!=null&&Math.abs(answer.value)<=1);let localValue=currentValue;if(rubric.coefficient){if(!localValue){localValue=1;}else if(localValue==="-"){localValue=-1;}}const matchedAnswer=rubric.answers.map(answer=>{const validateFn=createValidator(answer);const score=validateFn(maybeParsePercentInput(localValue,normalizedAnswerExpected));return {...answer,score}}).find(answer=>{return answer.score.correct||answer.status==="correct"&&answer.score.empty});const result=matchedAnswer?.status==="correct"?matchedAnswer.score:{empty:matchedAnswer?.status==="ungraded",correct:matchedAnswer?.status==="correct",message:matchedAnswer?.message??null};if(result.empty){return {type:"invalid",message:result.message}}return {type:"points",earned:result.correct?1:0,total:1,message:result.message}}
75
-
76
- function scoreOrderer(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}const correct=_.isEqual(userInput.current,rubric.correctOptions.map(option=>option.content));return {type:"points",earned:correct?1:0,total:1,message:null}}
77
-
78
- function validateOrderer(userInput){if(userInput==null||userInput.current.length===0){return {type:"invalid",message:null}}return null}
79
-
80
- function scorePlotter(userInput,rubric){return {type:"points",earned:approximateDeepEqual(userInput,rubric.correct)?1:0,total:1,message:null}}
81
-
82
- function validatePlotter(userInput,validationData){if(userInput==null||approximateDeepEqual(userInput,validationData.starting)){return {type:"invalid",message:null}}return null}
83
-
84
- function scoreRadio(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}const invalidIds=userInput.selectedChoiceIds.filter(id=>!rubric.choices.some(choice=>choice.id===id));if(invalidIds.length>0){return {type:"invalid",message:ErrorCodes.INVALID_CHOICE_SELECTION}}const numSelected=userInput.selectedChoiceIds.length;const numCorrect=rubric.choices.reduce((sum,currentChoice)=>{return currentChoice.correct?sum+1:sum},0);if(numCorrect>1&&numSelected!==numCorrect&&rubric.countChoices){return {type:"invalid",message:ErrorCodes.CHOOSE_CORRECT_NUM_ERROR}}const noneOfTheAboveSelected=rubric.choices.some(choice=>choice.isNoneOfTheAbove&&userInput.selectedChoiceIds.includes(choice.id));if(noneOfTheAboveSelected&&numSelected>1){return {type:"invalid",message:ErrorCodes.NOT_NONE_ABOVE_ERROR}}const correct=rubric.choices.every(choice=>{const isSelected=userInput.selectedChoiceIds.includes(choice.id);const isCorrect=!!choice.correct;return isCorrect===isSelected});return {type:"points",earned:correct?1:0,total:1,message:null}}
85
-
86
- function validateRadio(userInput){if(userInput==null||userInput.selectedChoiceIds.length===0){return {type:"invalid",message:null}}return null}
87
-
88
- function scoreSorter(userInput,rubric){if(exceedsCardLimit(rubric.correct)){return {type:"points",earned:1,total:1,message:null}}const correct=approximateDeepEqual(userInput.options,rubric.correct);return {type:"points",earned:correct?1:0,total:1,message:null}}
89
-
90
- function validateSorter(userInput,rubric){if(exceedsCardLimit(rubric.correct)){return null}if(!userInput?.changed){return {type:"invalid",message:null}}return null}
91
-
92
- const filterNonEmpty=function(table){return table.filter(function(row){return row.some(cell=>cell)})};
93
-
94
- function validateTable(userInput){const supplied=filterNonEmpty(userInput);const hasEmptyCell=supplied.some(function(row){return row.some(function(cell){return cell===""})});if(hasEmptyCell||!supplied.length){return {type:"invalid",message:null}}return null}
95
-
96
- function scoreTable(userInput,rubric){const validationResult=validateTable(userInput);if(validationResult!=null){return validationResult}const supplied=filterNonEmpty(userInput);const solution=filterNonEmpty(rubric.answers);if(supplied.length!==solution.length){return {type:"points",earned:0,total:1,message:null}}const createValidator=KhanAnswerTypes.number.createValidatorFunctional;let message=null;const allCorrect=solution.every(function(rowSolution){for(let i=0;i<supplied.length;i++){const rowSupplied=supplied[i];const correct=rowSupplied.every(function(cellSupplied,i){const cellSolution=rowSolution[i];const validator=createValidator(cellSolution,{simplify:true});const result=validator(cellSupplied);if(result.message){message=result.message;}return result.correct});if(correct){supplied.splice(i,1);return true}}return false});return {type:"points",earned:allCorrect?1:0,total:1,message}}
97
-
98
- const noScore={type:"points",earned:0,total:0,message:null};function combineScores(scoreA,scoreB){let message;if(scoreA.type==="points"&&scoreB.type==="points"){if(scoreA.message&&scoreB.message&&scoreA.message!==scoreB.message){message=null;}else {message=scoreA.message||scoreB.message;}return {type:"points",earned:scoreA.earned+scoreB.earned,total:scoreA.total+scoreB.total,message:message}}if(scoreA.type==="points"&&scoreB.type==="invalid"){return scoreB}if(scoreA.type==="invalid"&&scoreB.type==="points"){return scoreA}if(scoreA.type==="invalid"&&scoreB.type==="invalid"){if(scoreA.message&&scoreB.message&&scoreA.message!==scoreB.message){message=null;}else {message=scoreA.message||scoreB.message;}return {type:"invalid",message:message}}throw new PerseusError("PerseusScore with unknown type encountered",Errors.InvalidInput,{metadata:{scoreA:JSON.stringify(scoreA),scoreB:JSON.stringify(scoreB)}})}function flattenScores(widgetScoreMap){return Object.values(widgetScoreMap).reduce(combineScores,noScore)}
99
-
100
- function scoreFreeResponse(userInput,rubric,locale){return {type:"points",earned:0,total:0,message:null}}
101
-
102
- function validateFreeResponse(userInput,widgetOptions){const userInputLength=userInput?.currentValue.trim().length??0;if(userInputLength===0){return {type:"invalid",message:ErrorCodes.USER_INPUT_EMPTY}}if(!widgetOptions.allowUnlimitedCharacters&&userInputLength>widgetOptions.characterLimit){return {type:"invalid",message:ErrorCodes.USER_INPUT_TOO_LONG}}return null}
103
-
104
- function scoreGroup(userInput,rubric,locale){if(userInput==null){return {type:"invalid",message:null}}const scores=scoreWidgetsFunctional(rubric.widgets,Object.keys(rubric.widgets),userInput,locale);return flattenScores(scores)}
105
-
106
- function scoreIsEmpty(score){return score.type==="invalid"&&(!score.message||score.message.length===0)}
107
-
108
- function validateUserInput(perseusRenderData,userInputMap,locale){const scoreableWidgetIds=getScoreableWidgets(perseusRenderData);const validationErrors={};scoreableWidgetIds.forEach(id=>{const widget=perseusRenderData.widgets[id];const userInput=userInputMap[id];const validator=getWidgetValidator(widget.type);const validationError=validator?.(userInput,widget.options,locale);if(validationError!=null){validationErrors[id]=validationError;}});return Object.keys(validationErrors).length>0?flattenScores(validationErrors):null}function emptyWidgetsFunctional(widgets,widgetIds,userInputMap,locale){return widgetIds.filter(id=>{const widget=widgets[id];if(!isWidgetScoreable(widget)){return false}const validator=getWidgetValidator(widget.type);const userInput=userInputMap[id];const validationData=widget.options;const score=validator?.(userInput,validationData,locale);if(score){return scoreIsEmpty(score)}return false})}
109
-
110
- function validateGroup(userInput,validationData,locale){if(userInput==null){return {type:"invalid",message:null}}const widgetIds=getWidgetIdsFromContent(validationData.content);const emptyWidgets=emptyWidgetsFunctional(validationData.widgets,widgetIds,userInput,locale);if(emptyWidgets.length===0){return null}return {type:"invalid",message:null}}
111
-
112
- function validateLabelImage(userInput){if(userInput==null){return {type:"invalid",message:null}}let numAnswered=0;for(let i=0;i<userInput.markers.length;i++){const userSelection=userInput.markers[i].selected;if(userSelection&&userSelection.length>0){numAnswered++;}}if(numAnswered!==userInput.markers.length){return {type:"invalid",message:null}}return null}
113
-
114
- function validateMockWidget(userInput){if(userInput?.currentValue==null||userInput.currentValue===""){return {type:"invalid",message:""}}return null}
115
-
116
- function scoreMockWidget(userInput,rubric){const validationResult=validateMockWidget(userInput);if(validationResult!=null){return validationResult}return {type:"points",earned:userInput?.currentValue===rubric.value?1:0,total:1,message:""}}
117
-
118
- const widgets=new Registry("Score widget registry");function registerWidget(type,scorer,validator){const logic={scorer,validator};widgets.set(type,logic);}const getWidgetValidator=type=>{return widgets.get(type)?.validator??null};const getWidgetScorer=type=>{return widgets.get(type)?.scorer??null};registerWidget("categorizer",scoreCategorizer,validateCategorizer);registerWidget("cs-program",scoreCSProgram);registerWidget("dropdown",scoreDropdown,validateDropdown);registerWidget("expression",scoreExpression,validateExpression);registerWidget("free-response",scoreFreeResponse,validateFreeResponse);registerWidget("grapher",scoreGrapher);registerWidget("group",scoreGroup,validateGroup);registerWidget("iframe",scoreIframe);registerWidget("input-number",scoreNumericInput);registerWidget("interactive-graph",scoreInteractiveGraph);registerWidget("label-image",scoreLabelImage,validateLabelImage);registerWidget("matcher",scoreMatcher);registerWidget("matrix",scoreMatrix,validateMatrix);registerWidget("mock-widget",scoreMockWidget,scoreMockWidget);registerWidget("number-line",scoreNumberLine);registerWidget("numeric-input",scoreNumericInput);registerWidget("orderer",scoreOrderer,validateOrderer);registerWidget("plotter",scorePlotter,validatePlotter);registerWidget("radio",scoreRadio,validateRadio);registerWidget("sorter",scoreSorter,validateSorter);registerWidget("table",scoreTable,validateTable);
119
-
120
- function isWidgetScoreable(widget){if(!widget){return false}const widgetIsGraded=widget.graded==null||widget.graded;const widgetIsStatic=!!widget.static;const widgetHasScorer=getWidgetScorer(widget.type)!==null;return widgetIsGraded&&!widgetIsStatic&&widgetHasScorer}
121
-
122
- function getScoreableWidgets(perseusRenderData){const usedWidgetIds=getWidgetIdsFromContent(perseusRenderData.content);return usedWidgetIds.filter(id=>isWidgetScoreable(perseusRenderData.widgets[id]))}
123
-
124
- function onlyInvalidScores(scores){return Object.fromEntries(Object.entries(scores).filter(([key,value])=>value.type==="invalid"))}function combineScoreWithWidgetScores(score,widgetScores){if(score.type==="invalid"){return {...score,widgetScores:onlyInvalidScores(widgetScores)}}else {return {...score,widgetScores}}}function scorePerseusItem(perseusRenderData,userInputMap,locale){const scoreableWidgetIds=getScoreableWidgets(perseusRenderData);const widgetScores=scoreWidgetsFunctional(perseusRenderData.widgets,scoreableWidgetIds,userInputMap,locale);return combineScoreWithWidgetScores(flattenScores(widgetScores),widgetScores)}function scoreWidgetsFunctional(widgets,widgetIds,userInputMap,locale){const gradedWidgetIds=widgetIds.filter(id=>isWidgetScoreable(widgets[id]));const widgetScores={};gradedWidgetIds.forEach(id=>{const widget=widgets[id];const userInput=userInputMap[id];const validator=getWidgetValidator(widget.type);const scorer=getWidgetScorer(widget.type);const score=validator?.(userInput,widget.options,locale)??scorer?.(userInput,widget.options,locale);if(score!=null){widgetScores[id]=score;}});return widgetScores}
125
-
126
- function hasEmptyDINERWidgets(itemData,userInputMap){const usedWidgetIds=getWidgetIdsFromContent(itemData.question.content);const widgets=itemData.question.widgets;for(const widgetId of usedWidgetIds){const widget=widgets[widgetId];const input=userInputMap[widgetId];switch(widget.type){case "dropdown":{if(input.value===0){return true}break}case "interactive-graph":{break}case "numeric-input":{if(!input.currentValue&&!widget.options.coefficient){return true}break}case "expression":{if(!input){return true}break}case "radio":{if(input.selectedChoiceIds.length===0){return true}break}}}return false}
127
-
128
- export { KhanAnswerTypes, emptyWidgetsFunctional, flattenScores, getWidgetScorer, getWidgetValidator, hasEmptyDINERWidgets, isWidgetScoreable, libVersion, registerWidget, scoreCSProgram, scoreCategorizer, scoreDropdown, scoreExpression, scoreGrapher, scoreIframe, scoreNumericInput as scoreInputNumber, scoreInteractiveGraph, scoreLabelImage, scoreLabelImageMarker, scoreMatcher, scoreMatrix, scoreNumberLine, scoreNumericInput, scoreOrderer, scorePerseusItem, scorePlotter, scoreRadio, scoreSorter, scoreTable, scoreWidgetsFunctional, validateCategorizer, validateDropdown, validateExpression, validateMatrix, validateOrderer, validatePlotter, validateRadio, validateSorter, validateTable, validateUserInput };
129
- //# sourceMappingURL=index.js.map
1297
+ //# sourceMappingURL=index.js.map