@pie-element/image-cloze-association 10.2.0-next.19 → 10.2.0-next.21
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/browser/{Check-DHSwIAJ-.js → Check-D1Xgr9Ov.js} +2433 -2400
- package/dist/browser/Check-D1Xgr9Ov.js.map +1 -0
- package/dist/browser/author/index.js +17 -17
- package/dist/browser/author/index.js.map +1 -1
- package/dist/browser/controller/index.js +26 -26
- package/dist/browser/controller/index.js.map +1 -1
- package/dist/browser/delivery/index.js +48 -50
- package/dist/browser/delivery/index.js.map +1 -1
- package/dist/browser/editor-runtime/{Check-DrKXQGXL.js → Check-BzY2qRmU.js} +2433 -2400
- package/dist/browser/editor-runtime/Check-BzY2qRmU.js.map +1 -0
- package/dist/browser/editor-runtime/author/index.js +12 -12
- package/dist/browser/editor-runtime/author/index.js.map +1 -1
- package/dist/browser/editor-runtime/controller/index.js +26 -26
- package/dist/browser/editor-runtime/controller/index.js.map +1 -1
- package/dist/browser/editor-runtime/delivery/index.js +48 -50
- package/dist/browser/editor-runtime/delivery/index.js.map +1 -1
- package/dist/controller/index.js +30 -30
- package/dist/delivery/utils-correctness.js +21 -21
- package/dist/index.iife.js +35 -35
- package/package.json +12 -9
- package/dist/browser/Check-DHSwIAJ-.js.map +0 -1
- package/dist/browser/editor-runtime/Check-DrKXQGXL.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { b as e, g as t, i as n, l as r, s as i } from "../dist-DZyIyIXw.js";
|
|
2
2
|
import { t as a } from "../humps-C6P4rNef.js";
|
|
3
3
|
//#region ../../shared/controller-utils/dist/index.js
|
|
4
|
-
var o = /* @__PURE__ */ e(t(), 1), s = a(), c = Object.defineProperty, l = /* @__PURE__ */ ((e, t) => {
|
|
4
|
+
var o = /* @__PURE__ */ e(t(), 1), s = /* @__PURE__ */ e(a(), 1), c = Object.defineProperty, l = /* @__PURE__ */ ((e, t) => {
|
|
5
5
|
let n = {};
|
|
6
6
|
for (var r in e) c(n, r, {
|
|
7
7
|
get: e[r],
|
|
@@ -52,17 +52,17 @@ var d = {
|
|
|
52
52
|
possibleResponses: s,
|
|
53
53
|
hasUnplacedChoices: l
|
|
54
54
|
};
|
|
55
|
-
}, g = (0, o.default)("pie-elements:image-cloze-association:controller"),
|
|
55
|
+
}, { camelizeKeys: g } = s.default, _ = (0, o.default)("pie-elements:image-cloze-association:controller"), v = (e) => ({
|
|
56
56
|
...d,
|
|
57
57
|
...e
|
|
58
|
-
}),
|
|
59
|
-
let r =
|
|
58
|
+
}), y = (e, t, n) => {
|
|
59
|
+
let r = v(e), a = g(r);
|
|
60
60
|
return new Promise((e) => {
|
|
61
61
|
let o = n.mode === "evaluate", { responseAreasToBeFilled: s, possibleResponses: c, hasUnplacedChoices: l } = h(a.validation, a.possibleResponses), u = {
|
|
62
62
|
disabled: n.mode !== "gather",
|
|
63
63
|
mode: n.mode,
|
|
64
64
|
...a,
|
|
65
|
-
responseCorrect: o ?
|
|
65
|
+
responseCorrect: o ? T(a, t) === 1 : void 0,
|
|
66
66
|
validation: o ? a.validation : void 0,
|
|
67
67
|
responseAreasToBeFilled: s,
|
|
68
68
|
completeResponses: c,
|
|
@@ -70,32 +70,32 @@ var d = {
|
|
|
70
70
|
};
|
|
71
71
|
r.shuffle && (u.possibleResponses = i(r.possible_responses)), n.role === "instructor" && (n.mode === "view" || n.mode === "evaluate") ? (u.teacherInstructions = a.teacherInstructionsEnabled ? a.teacherInstructions : null, u.rationale = a.rationale ? a.rationale : null) : (u.teacherInstructions = null, u.rationale = null), e(u);
|
|
72
72
|
});
|
|
73
|
-
},
|
|
73
|
+
}, b = (e, t) => {
|
|
74
74
|
let i = r(e), a = !0, o = 0;
|
|
75
75
|
return !t || n(t) ? !1 : (i.forEach((e) => o += (e.images || []).length), t.answers && o === t.answers.length ? t.answers.forEach((e) => {
|
|
76
76
|
let t = (i[e.containerIndex]?.images || []).indexOf(e.value);
|
|
77
77
|
t >= 0 ? i[e.containerIndex].images.splice(t, 1) : a = !1;
|
|
78
78
|
}) : a = !1, a);
|
|
79
|
-
},
|
|
80
|
-
let { validation: { altResponses: n } } = e, { validation: { validResponse: { value: r } } } = e, i =
|
|
79
|
+
}, x = (e) => r(e.filter((e) => e.images && e.images.length)), S = (e, t) => {
|
|
80
|
+
let { validation: { altResponses: n } } = e, { validation: { validResponse: { value: r } } } = e, i = b(r, t);
|
|
81
81
|
return !i && n && n.length && n.forEach((e) => {
|
|
82
|
-
|
|
82
|
+
b(e.value, t) && (i = !0);
|
|
83
83
|
}), i;
|
|
84
|
-
},
|
|
84
|
+
}, C = (e, t, n) => {
|
|
85
85
|
let r = t.filter((t) => t.containerIndex === e), i = r.filter((e) => !e.isCorrect), a = (n.value[e].images || []).length;
|
|
86
86
|
if (r.length > a) {
|
|
87
87
|
let e = r.length - a;
|
|
88
88
|
return i.slice(-e);
|
|
89
89
|
}
|
|
90
90
|
return [];
|
|
91
|
-
},
|
|
91
|
+
}, w = (e, t) => {
|
|
92
92
|
let { validation: { validResponse: r }, maxResponsePerZone: i } = e, a = 0, o = 0, s = 0;
|
|
93
93
|
if (!t || n(t)) return 0;
|
|
94
94
|
if (r.value.forEach((e) => s += (e.images || []).length), t.answers && t.answers.length) {
|
|
95
95
|
let e = m(t.answers, r.value);
|
|
96
96
|
a = e.filter((e) => e.isCorrect).length, o = e.filter((e) => !e.isCorrect).length, t.answers.forEach((t) => {
|
|
97
97
|
if (i > 1) {
|
|
98
|
-
let n =
|
|
98
|
+
let n = C(t.containerIndex, e, r);
|
|
99
99
|
n.length && n.forEach((e) => {
|
|
100
100
|
e.id === t.id && --a;
|
|
101
101
|
});
|
|
@@ -103,12 +103,12 @@ var d = {
|
|
|
103
103
|
}), (!i || i <= 1) && (a -= o);
|
|
104
104
|
} else a = 0;
|
|
105
105
|
a = a < 0 ? 0 : a;
|
|
106
|
-
let c =
|
|
106
|
+
let c = x(r.value), l = i > 1 ? s : (c || []).length, u = (a / l).toFixed(2);
|
|
107
107
|
return parseFloat(u);
|
|
108
|
-
},
|
|
109
|
-
let r = l.enabled(e, n), i =
|
|
110
|
-
return r ?
|
|
111
|
-
},
|
|
108
|
+
}, T = (e, t, n = {}) => {
|
|
109
|
+
let r = l.enabled(e, n), i = S(e, t);
|
|
110
|
+
return r ? w(e, t) : +!!i;
|
|
111
|
+
}, E = (e, t, n) => {
|
|
112
112
|
let r = [], { answers: i } = t || {}, a = e.validation?.validResponse?.value || [], o = a.length;
|
|
113
113
|
if (r.push(`${o} placement container(s) defined in this question.`), i && i.length > 0) {
|
|
114
114
|
r.push(`Student placed ${i.length} image(s) into placement containers.`);
|
|
@@ -128,23 +128,23 @@ var d = {
|
|
|
128
128
|
s.length > 0 && r.push(`${s.length} alternate response combination(s) are accepted for this question.`);
|
|
129
129
|
let c = l.enabled(e, n) ? "partial scoring" : "all-or-nothing scoring";
|
|
130
130
|
r.push(`Score calculated using ${c}.`);
|
|
131
|
-
let u =
|
|
131
|
+
let u = T(e, t, n);
|
|
132
132
|
return r.push(`Final score: ${u}.`), r;
|
|
133
|
-
},
|
|
134
|
-
if (
|
|
133
|
+
}, D = (e, t, r = {}) => new Promise((i) => {
|
|
134
|
+
if (_("outcome..."), !t || n(t)) i({
|
|
135
135
|
score: 0,
|
|
136
136
|
empty: !0,
|
|
137
137
|
traceLog: ["Student did not place any images into placement containers. Score is 0."]
|
|
138
138
|
});
|
|
139
139
|
else {
|
|
140
|
-
let n = (
|
|
140
|
+
let n = g(e), a = E(n, t, r);
|
|
141
141
|
i({
|
|
142
|
-
score:
|
|
142
|
+
score: T(n, t, r),
|
|
143
143
|
empty: !1,
|
|
144
144
|
traceLog: a
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
|
-
}),
|
|
147
|
+
}), O = (e, t) => new Promise((n) => {
|
|
148
148
|
if (t.mode !== "evaluate" && t.role === "instructor") {
|
|
149
149
|
let { validation: { valid_response: { value: t } } } = e, r = [];
|
|
150
150
|
t && t.forEach((e, t) => {
|
|
@@ -159,13 +159,13 @@ var d = {
|
|
|
159
159
|
id: "1"
|
|
160
160
|
});
|
|
161
161
|
} else n(null);
|
|
162
|
-
}),
|
|
162
|
+
}), k = (e) => (e || "").replace(/(<(?!img|iframe|source)([^>]+)>)/gi, ""), A = (e = {}, t = {}) => {
|
|
163
163
|
let n = {};
|
|
164
164
|
return ["teacherInstructions"].forEach((r) => {
|
|
165
|
-
t[r]?.required && !
|
|
165
|
+
t[r]?.required && !k(e[r]) && (n[r] = "This field is required.");
|
|
166
166
|
}), n;
|
|
167
167
|
};
|
|
168
168
|
//#endregion
|
|
169
|
-
export {
|
|
169
|
+
export { O as createCorrectResponseSession, E as getLogTrace, w as getPartialScore, b as isResponseCorrect, y as model, v as normalize, D as outcome, A as validate };
|
|
170
170
|
|
|
171
171
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../../shared/controller-utils/dist/index.js","../../../src/controller/defaults.ts","../../../src/controller/utils.ts","../../../src/controller/index.ts"],"sourcesContent":["//#region \\0rolldown/runtime.js\nvar e = Object.defineProperty, t = (t, n) => {\n\tlet r = {};\n\tfor (var i in t) e(r, i, {\n\t\tget: t[i],\n\t\tenumerable: !0\n\t});\n\treturn n || e(r, Symbol.toStringTag, { value: \"Module\" }), r;\n};\n//#endregion\n//#region src/persistence.ts\nfunction n(e) {\n\treturn Array.isArray(e) ? e.filter((e) => e != null) : [];\n}\nfunction r(e) {\n\tlet t = [...e];\n\tfor (let e = t.length - 1; e > 0; e--) {\n\t\tlet n = Math.floor(Math.random() * (e + 1));\n\t\t[t[e], t[n]] = [t[n], t[e]];\n\t}\n\treturn t;\n}\nfunction i(e) {\n\treturn e == null ? !0 : Array.isArray(e) ? e.length === 0 : typeof e == \"object\" && Object.keys(e).length === 0;\n}\nasync function a(e, t, a, o = \"value\") {\n\tlet s = n(t?.data?.shuffledValues ?? t?.shuffledValues ?? []);\n\tif (!t) {\n\t\tconsole.warn(\"Unable to save shuffled choices because there's no session.\");\n\t\treturn;\n\t}\n\tif (!i(s)) return n(s.map((t) => e.find((e) => e[o] === t)));\n\tlet c = r(e);\n\tif (a && typeof a == \"function\") try {\n\t\tlet e = n(c.map((e) => e[o]));\n\t\ti(e) ? console.error(`shuffledValues is an empty array - refusing to call updateSession. shuffledChoices: ${JSON.stringify(c)}, key: ${o}`) : t.id && t.element && await a(t.id, t.element, { shuffledValues: e });\n\t} catch (e) {\n\t\tconsole.warn(\"Unable to save shuffled order for choices\"), console.error(e);\n\t}\n\telse console.warn(\"Unable to save shuffled choices, shuffle will happen every time.\");\n\treturn c;\n}\nfunction o(e, t, n) {\n\treturn !!(e.lockChoiceOrder || n[\"@pie-element\"]?.lockChoiceOrder || (n.role ?? \"student\") === \"instructor\");\n}\n//#endregion\n//#region src/partial-scoring.ts\nvar s = /* @__PURE__ */ t({ enabled: () => c });\nfunction c(e, t, n) {\n\treturn e?.partialScoring === !1 || t?.partialScoring === !1 ? !1 : typeof n != \"boolean\" || n;\n}\n//#endregion\nexport { a as getShuffledChoices, o as lockChoices, s as partialScoring };\n","// @ts-nocheck\n/**\n * @synced-from pie-elements/packages/image-cloze-association/controller/src/defaults.js\n * @auto-generated\n *\n * This file is automatically synced from pie-elements and converted to TypeScript.\n * Manual edits will be overwritten on next sync.\n * To make changes, edit the upstream JavaScript file and run sync again.\n */\n\nexport default {\n rationale: '',\n rationaleEnabled: true,\n studentInstructionsEnabled: true,\n teacherInstructions: '',\n teacherInstructionsEnabled: true,\n};\n","// @ts-nocheck\n/**\n * @synced-from pie-elements/packages/image-cloze-association/controller/src/utils.js\n * @auto-generated\n *\n * This file is automatically synced from pie-elements and converted to TypeScript.\n * Manual edits will be overwritten on next sync.\n * To make changes, edit the upstream JavaScript file and run sync again.\n */\n\n// functions also used in src/utils-correctness.js\n\nconst getAllCorrectness = (answers, responses) =>\n (answers || []).map((answer) => ({\n ...answer,\n isCorrect: ((responses[answer.containerIndex] && responses[answer.containerIndex].images) || []).includes(\n answer.value,\n ),\n }));\n\nconst getValidAnswer = (answer, response) =>\n ((response[answer.containerIndex] && response[answer.containerIndex].images) || []).filter(\n (res) => res === answer.value,\n );\n\nexport const getAllUniqueCorrectness = (answers, validResponses) => {\n let allCorrectness = getAllCorrectness(answers, validResponses);\n\n answers.forEach((answer1) => {\n const valuesToParse = answers.filter(\n (answer2) => answer2.value === answer1.value && answer2.containerIndex === answer1.containerIndex,\n );\n\n if (valuesToParse.length > 1) {\n // point only to duplicates but first\n valuesToParse.shift();\n // mark duplicates as incorrect\n valuesToParse.forEach((value, index) => {\n allCorrectness = (allCorrectness || []).map((finalAnswer) => {\n if (finalAnswer.id === value.id) {\n let valid = getValidAnswer(finalAnswer, validResponses);\n return {\n ...finalAnswer,\n isCorrect: valid.length > index + 1,\n };\n }\n return finalAnswer;\n });\n });\n }\n });\n return allCorrectness;\n};\n\n// calculate the minimum number of populated response areas (categories) in the correct answer or alternates\n// and create an array with the possible responses ids\nexport const getCompleteResponseDetails = (validation, allChoices) => {\n const extractImages = (response) => (response?.value || []).map((container) => container.images);\n const countFilledResponseAreas = (container) => (container || []).filter((images) => images.length).length;\n\n const { validResponse, altResponses } = validation || {};\n const imagesPerContainer = extractImages(validResponse);\n const possibleResponses = [imagesPerContainer.flat()];\n let responseAreasToBeFilled = countFilledResponseAreas(imagesPerContainer);\n\n (altResponses || []).forEach((altResponse) => {\n const altImagesPerContainer = extractImages(altResponse);\n const filledResponseAreas = countFilledResponseAreas(altImagesPerContainer);\n possibleResponses.push(altImagesPerContainer.flat());\n\n if (filledResponseAreas < responseAreasToBeFilled) {\n responseAreasToBeFilled = filledResponseAreas;\n }\n });\n\n // check if any correct answer have any unplaced answer choices\n const hasUnplacedChoices = possibleResponses.some(\n (response) => !allChoices.every((val) => response.includes(val)),\n );\n\n return { responseAreasToBeFilled, possibleResponses, hasUnplacedChoices };\n};\n","// @ts-nocheck\n/**\n * @synced-from pie-elements/packages/image-cloze-association/controller/src/index.js\n * @auto-generated\n *\n * This file is automatically synced from pie-elements and converted to TypeScript.\n * Manual edits will be overwritten on next sync.\n * To make changes, edit the upstream JavaScript file and run sync again.\n */\n\nimport debug from 'debug';\nimport { camelizeKeys } from 'humps';\nimport { partialScoring } from '@pie-element/shared-controller-utils';\nimport { cloneDeep, isEmpty, shuffle } from '@pie-element/shared-lodash';\n\nimport defaults from './defaults.js';\nimport { getAllUniqueCorrectness, getCompleteResponseDetails } from './utils.js';\n\nconst log = debug('pie-elements:image-cloze-association:controller');\n\nexport const normalize = (question) => ({ ...defaults, ...question });\n\nexport const model = (question, session, env) => {\n const questionNormalized = normalize(question);\n const questionCamelized = camelizeKeys(questionNormalized);\n\n return new Promise((resolve) => {\n const shouldIncludeCorrectResponse = env.mode === 'evaluate';\n\n const {\n responseAreasToBeFilled,\n possibleResponses: completeResponses,\n hasUnplacedChoices,\n } = getCompleteResponseDetails(questionCamelized.validation, questionCamelized.possibleResponses);\n\n const out = {\n disabled: env.mode !== 'gather',\n mode: env.mode,\n ...questionCamelized,\n responseCorrect: shouldIncludeCorrectResponse ? getScore(questionCamelized, session) === 1 : undefined,\n validation: shouldIncludeCorrectResponse ? questionCamelized.validation : undefined,\n responseAreasToBeFilled,\n completeResponses,\n hasUnplacedChoices,\n };\n\n if (questionNormalized.shuffle) {\n out.possibleResponses = shuffle(questionNormalized.possible_responses);\n }\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n out.teacherInstructions = questionCamelized.teacherInstructionsEnabled\n ? questionCamelized.teacherInstructions\n : null;\n out.rationale = questionCamelized.rationale ? questionCamelized.rationale : null;\n } else {\n out.teacherInstructions = null;\n out.rationale = null;\n }\n\n resolve(out);\n });\n};\n\nexport const isResponseCorrect = (correctResponses, session) => {\n const responses = cloneDeep(correctResponses);\n let isCorrect = true;\n let totalValidResponses = 0;\n\n if (!session || isEmpty(session)) {\n return false;\n }\n\n responses.forEach((value) => (totalValidResponses += (value.images || []).length));\n\n if (session.answers && totalValidResponses === session.answers.length) {\n session.answers.forEach((answer) => {\n const index = (responses[answer.containerIndex]?.images || []).indexOf(answer.value);\n\n if (index >= 0) {\n // remove response from correct responses array to ensure that duplicates are evaluated correctly\n responses[answer.containerIndex].images.splice(index, 1);\n } else {\n isCorrect = false;\n }\n });\n } else {\n isCorrect = false;\n }\n\n return isCorrect;\n};\n\n// This applies for correct responses that have empty values\nconst keepNonEmptyResponses = (responses) => {\n const filtered = responses.filter((response) => response.images && response.images.length);\n return cloneDeep(filtered);\n};\n\n// This applies for items that don't support partial scoring.\nconst isDefaultOrAltResponseCorrect = (question, session) => {\n const {\n validation: { altResponses },\n } = question;\n let {\n validation: {\n validResponse: { value },\n },\n } = question;\n\n let isCorrect = isResponseCorrect(value, session);\n\n // Look for correct answers in alternate responses.\n if (!isCorrect && altResponses && altResponses.length) {\n altResponses.forEach((altResponse) => {\n if (isResponseCorrect(altResponse.value, session)) {\n isCorrect = true;\n }\n });\n }\n return isCorrect;\n};\n\n// Deduct only the items that exceeded the maximum valid response per container.\nconst getDeductionPerContainer = (containerIndex, answers, valid) => {\n const totalStack = answers.filter((item) => item.containerIndex === containerIndex);\n const incorrectStack = totalStack.filter((item) => !item.isCorrect);\n const maxValid = (valid.value[containerIndex].images || []).length;\n\n if (totalStack.length > maxValid) {\n const ignored = totalStack.length - maxValid;\n return incorrectStack.slice(-ignored);\n }\n return [];\n};\n\nexport const getPartialScore = (question, session) => {\n const {\n validation: { validResponse },\n maxResponsePerZone,\n } = question;\n let correctAnswers = 0;\n let incorrectAnswers = 0;\n let possibleResponses = 0;\n\n if (!session || isEmpty(session)) {\n return 0;\n }\n\n validResponse.value.forEach((value) => (possibleResponses += (value.images || []).length));\n\n if (session.answers && session.answers.length) {\n const all = getAllUniqueCorrectness(session.answers, validResponse.value);\n correctAnswers = all.filter((item) => item.isCorrect).length;\n incorrectAnswers = all.filter((item) => !item.isCorrect).length;\n\n // deduction rules: https://docs.google.com/document/d/1Oprm8Qs5fg_Dwoj2pNpsfu4D63QgCZgvcqTgeaVel7I/edit\n session.answers.forEach((answer) => {\n if (maxResponsePerZone > 1) {\n const deductionList = getDeductionPerContainer(answer.containerIndex, all, validResponse);\n\n if (deductionList.length) {\n deductionList.forEach((item) => {\n if (item.id === answer.id) {\n correctAnswers -= 1;\n }\n });\n }\n }\n });\n\n if (!maxResponsePerZone || maxResponsePerZone <= 1) {\n correctAnswers -= incorrectAnswers;\n }\n } else {\n correctAnswers = 0;\n }\n // negative values will implicitly make the score equal to zero\n correctAnswers = correctAnswers < 0 ? 0 : correctAnswers;\n\n // use length of validResponse since some containers can be left empty\n const nonEmptyResponses = keepNonEmptyResponses(validResponse.value);\n const denominator = maxResponsePerZone > 1 ? possibleResponses : (nonEmptyResponses || []).length;\n const str = (correctAnswers / denominator).toFixed(2);\n\n return parseFloat(str);\n};\n\nconst getScore = (config, session, env = {}) => {\n const isPartialScoring = partialScoring.enabled(config, env);\n const correct = isDefaultOrAltResponseCorrect(config, session);\n\n return isPartialScoring ? getPartialScore(config, session) : correct ? 1 : 0;\n};\n\n/**\n * Generates detailed trace log for scoring evaluation\n * @param {Object} model - the question model\n * @param {Object} session - the student session\n * @param {Object} env - the environment\n * @returns {Array} traceLog - array of trace messages\n */\nexport const getLogTrace = (model, session, env) => {\n const traceLog = [];\n const { answers } = session || {};\n \n const validResponse = model.validation?.validResponse?.value || [];\n const totalContainers = validResponse.length;\n traceLog.push(`${totalContainers} placement container(s) defined in this question.`);\n\n if (answers && answers.length > 0) {\n traceLog.push(`Student placed ${answers.length} image(s) into placement containers.`);\n \n const answersByContainer = {};\n answers.forEach((answer) => {\n if (!answersByContainer[answer.containerIndex]) {\n answersByContainer[answer.containerIndex] = [];\n }\n answersByContainer[answer.containerIndex].push(answer.value);\n });\n \n validResponse.forEach((container, containerIndex) => {\n const correctImages = container.images || [];\n const studentImages = answersByContainer[containerIndex] || [];\n \n if (correctImages.length > 0) {\n if (studentImages.length === 0) {\n traceLog.push(`Container ${containerIndex + 1}: student left empty (should contain ${correctImages.length} image(s)).`);\n } else {\n const correctCount = studentImages.filter(img => correctImages.includes(img)).length;\n const incorrectCount = studentImages.length - correctCount;\n \n if (correctCount > 0 && incorrectCount === 0) {\n traceLog.push(`Container ${containerIndex + 1}: student placed ${correctCount} correct image(s).`);\n } else if (correctCount === 0 && incorrectCount > 0) {\n traceLog.push(`Container ${containerIndex + 1}: student placed ${incorrectCount} incorrect image(s).`);\n } else {\n traceLog.push(`Container ${containerIndex + 1}: student placed ${correctCount} correct and ${incorrectCount} incorrect image(s).`);\n }\n }\n }\n });\n } else {\n traceLog.push('Student did not place any images into placement containers.');\n }\n\n const altResponses = model.validation?.altResponses || [];\n if (altResponses.length > 0) {\n traceLog.push(`${altResponses.length} alternate response combination(s) are accepted for this question.`);\n }\n\n const partialScoringEnabled = partialScoring.enabled(model, env);\n const scoringMethod = partialScoringEnabled ? 'partial scoring' : 'all-or-nothing scoring';\n traceLog.push(`Score calculated using ${scoringMethod}.`);\n\n const score = getScore(model, session, env);\n traceLog.push(`Final score: ${score}.`);\n\n return traceLog;\n}\n\nexport const outcome = (config, session, env = {}) => {\n return new Promise((resolve) => {\n log('outcome...');\n if (!session || isEmpty(session)) {\n resolve({ \n score: 0, \n empty: true, \n traceLog: ['Student did not place any images into placement containers. Score is 0.'] \n });\n } else {\n const configCamelized = camelizeKeys(config);\n const traceLog = getLogTrace(configCamelized, session, env);\n const score = getScore(configCamelized, session, env);\n \n resolve({ \n score, \n empty: false,\n traceLog \n });\n }\n });\n};\n\nexport const createCorrectResponseSession = (question, env) => {\n return new Promise((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n const {\n validation: {\n valid_response: { value },\n },\n } = question;\n const answers = [];\n\n if (value) {\n value.forEach((container, i) => {\n (container.images || []).forEach((v) => {\n answers.push({\n value: v,\n containerIndex: i,\n });\n });\n });\n }\n\n resolve({\n answers,\n id: '1',\n });\n } else {\n resolve(null);\n }\n });\n};\n\n// remove all html tags\nconst getInnerText = (html) => (html || '').replaceAll(/<[^>]*>/g, '');\n\n// remove all html tags except img, iframe and source tag for audio\nconst getContent = (html) => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');\n\nexport const validate = (model = {}, config = {}) => {\n const errors = {};\n\n ['teacherInstructions'].forEach((field) => {\n if (config[field]?.required && !getContent(model[field])) {\n errors[field] = 'This field is required.';\n }\n });\n\n return errors;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../../shared/controller-utils/dist/index.js","../../../src/controller/defaults.ts","../../../src/controller/utils.ts","../../../src/controller/index.ts"],"sourcesContent":["//#region \\0rolldown/runtime.js\nvar e = Object.defineProperty, t = (t, n) => {\n\tlet r = {};\n\tfor (var i in t) e(r, i, {\n\t\tget: t[i],\n\t\tenumerable: !0\n\t});\n\treturn n || e(r, Symbol.toStringTag, { value: \"Module\" }), r;\n};\n//#endregion\n//#region src/persistence.ts\nfunction n(e) {\n\treturn Array.isArray(e) ? e.filter((e) => e != null) : [];\n}\nfunction r(e) {\n\tlet t = [...e];\n\tfor (let e = t.length - 1; e > 0; e--) {\n\t\tlet n = Math.floor(Math.random() * (e + 1));\n\t\t[t[e], t[n]] = [t[n], t[e]];\n\t}\n\treturn t;\n}\nfunction i(e) {\n\treturn e == null ? !0 : Array.isArray(e) ? e.length === 0 : typeof e == \"object\" && Object.keys(e).length === 0;\n}\nasync function a(e, t, a, o = \"value\") {\n\tlet s = n(t?.data?.shuffledValues ?? t?.shuffledValues ?? []);\n\tif (!t) {\n\t\tconsole.warn(\"Unable to save shuffled choices because there's no session.\");\n\t\treturn;\n\t}\n\tif (!i(s)) return n(s.map((t) => e.find((e) => e[o] === t)));\n\tlet c = r(e);\n\tif (a && typeof a == \"function\") try {\n\t\tlet e = n(c.map((e) => e[o]));\n\t\ti(e) ? console.error(`shuffledValues is an empty array - refusing to call updateSession. shuffledChoices: ${JSON.stringify(c)}, key: ${o}`) : await a(t.id, t.element, { shuffledValues: e });\n\t} catch (e) {\n\t\tconsole.warn(\"Unable to save shuffled order for choices\"), console.error(e);\n\t}\n\telse console.warn(\"Unable to save shuffled choices, shuffle will happen every time.\");\n\treturn c;\n}\nfunction o(e, t, n) {\n\treturn !!(e.lockChoiceOrder || n?.[\"@pie-element\"]?.lockChoiceOrder);\n}\n//#endregion\n//#region src/partial-scoring.ts\nvar s = /* @__PURE__ */ t({ enabled: () => c });\nfunction c(e, t, n) {\n\treturn e?.partialScoring === !1 || t?.partialScoring === !1 ? !1 : typeof n != \"boolean\" || n;\n}\n//#endregion\nexport { a as getShuffledChoices, o as lockChoices, s as partialScoring };\n","// @ts-nocheck\n/**\n * @synced-from pie-elements/packages/image-cloze-association/controller/src/defaults.js\n * @auto-generated\n *\n * This file is automatically synced from pie-elements and converted to TypeScript.\n * Manual edits will be overwritten on next sync.\n * To make changes, edit the upstream JavaScript file and run sync again.\n */\n\nexport default {\n rationale: '',\n rationaleEnabled: true,\n studentInstructionsEnabled: true,\n teacherInstructions: '',\n teacherInstructionsEnabled: true,\n};\n","// @ts-nocheck\n/**\n * @synced-from pie-elements/packages/image-cloze-association/controller/src/utils.js\n * @auto-generated\n *\n * This file is automatically synced from pie-elements and converted to TypeScript.\n * Manual edits will be overwritten on next sync.\n * To make changes, edit the upstream JavaScript file and run sync again.\n */\n\n// functions also used in src/utils-correctness.js\n\nconst getAllCorrectness = (answers, responses) =>\n (answers || []).map((answer) => ({\n ...answer,\n isCorrect: ((responses[answer.containerIndex] && responses[answer.containerIndex].images) || []).includes(\n answer.value,\n ),\n }));\n\nconst getValidAnswer = (answer, response) =>\n ((response[answer.containerIndex] && response[answer.containerIndex].images) || []).filter(\n (res) => res === answer.value,\n );\n\nexport const getAllUniqueCorrectness = (answers, validResponses) => {\n let allCorrectness = getAllCorrectness(answers, validResponses);\n\n answers.forEach((answer1) => {\n const valuesToParse = answers.filter(\n (answer2) => answer2.value === answer1.value && answer2.containerIndex === answer1.containerIndex,\n );\n\n if (valuesToParse.length > 1) {\n // point only to duplicates but first\n valuesToParse.shift();\n // mark duplicates as incorrect\n valuesToParse.forEach((value, index) => {\n allCorrectness = (allCorrectness || []).map((finalAnswer) => {\n if (finalAnswer.id === value.id) {\n let valid = getValidAnswer(finalAnswer, validResponses);\n return {\n ...finalAnswer,\n isCorrect: valid.length > index + 1,\n };\n }\n return finalAnswer;\n });\n });\n }\n });\n return allCorrectness;\n};\n\n// calculate the minimum number of populated response areas (categories) in the correct answer or alternates\n// and create an array with the possible responses ids\nexport const getCompleteResponseDetails = (validation, allChoices) => {\n const extractImages = (response) => (response?.value || []).map((container) => container.images);\n const countFilledResponseAreas = (container) => (container || []).filter((images) => images.length).length;\n\n const { validResponse, altResponses } = validation || {};\n const imagesPerContainer = extractImages(validResponse);\n const possibleResponses = [imagesPerContainer.flat()];\n let responseAreasToBeFilled = countFilledResponseAreas(imagesPerContainer);\n\n (altResponses || []).forEach((altResponse) => {\n const altImagesPerContainer = extractImages(altResponse);\n const filledResponseAreas = countFilledResponseAreas(altImagesPerContainer);\n possibleResponses.push(altImagesPerContainer.flat());\n\n if (filledResponseAreas < responseAreasToBeFilled) {\n responseAreasToBeFilled = filledResponseAreas;\n }\n });\n\n // check if any correct answer have any unplaced answer choices\n const hasUnplacedChoices = possibleResponses.some(\n (response) => !allChoices.every((val) => response.includes(val)),\n );\n\n return { responseAreasToBeFilled, possibleResponses, hasUnplacedChoices };\n};\n","// @ts-nocheck\n/**\n * @synced-from pie-elements/packages/image-cloze-association/controller/src/index.js\n * @auto-generated\n *\n * This file is automatically synced from pie-elements and converted to TypeScript.\n * Manual edits will be overwritten on next sync.\n * To make changes, edit the upstream JavaScript file and run sync again.\n */\n\nimport debug from 'debug';\nimport humps from 'humps';\nconst { camelizeKeys } = humps;\nimport { partialScoring } from '@pie-element/shared-controller-utils';\nimport { cloneDeep, isEmpty, shuffle } from '@pie-element/shared-lodash';\n\nimport defaults from './defaults.js';\nimport { getAllUniqueCorrectness, getCompleteResponseDetails } from './utils.js';\n\nconst log = debug('pie-elements:image-cloze-association:controller');\n\nexport const normalize = (question) => ({ ...defaults, ...question });\n\nexport const model = (question, session, env) => {\n const questionNormalized = normalize(question);\n const questionCamelized = camelizeKeys(questionNormalized);\n\n return new Promise((resolve) => {\n const shouldIncludeCorrectResponse = env.mode === 'evaluate';\n\n const {\n responseAreasToBeFilled,\n possibleResponses: completeResponses,\n hasUnplacedChoices,\n } = getCompleteResponseDetails(questionCamelized.validation, questionCamelized.possibleResponses);\n\n const out = {\n disabled: env.mode !== 'gather',\n mode: env.mode,\n ...questionCamelized,\n responseCorrect: shouldIncludeCorrectResponse ? getScore(questionCamelized, session) === 1 : undefined,\n validation: shouldIncludeCorrectResponse ? questionCamelized.validation : undefined,\n responseAreasToBeFilled,\n completeResponses,\n hasUnplacedChoices,\n };\n\n if (questionNormalized.shuffle) {\n out.possibleResponses = shuffle(questionNormalized.possible_responses);\n }\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n out.teacherInstructions = questionCamelized.teacherInstructionsEnabled\n ? questionCamelized.teacherInstructions\n : null;\n out.rationale = questionCamelized.rationale ? questionCamelized.rationale : null;\n } else {\n out.teacherInstructions = null;\n out.rationale = null;\n }\n\n resolve(out);\n });\n};\n\nexport const isResponseCorrect = (correctResponses, session) => {\n const responses = cloneDeep(correctResponses);\n let isCorrect = true;\n let totalValidResponses = 0;\n\n if (!session || isEmpty(session)) {\n return false;\n }\n\n responses.forEach((value) => (totalValidResponses += (value.images || []).length));\n\n if (session.answers && totalValidResponses === session.answers.length) {\n session.answers.forEach((answer) => {\n const index = (responses[answer.containerIndex]?.images || []).indexOf(answer.value);\n\n if (index >= 0) {\n // remove response from correct responses array to ensure that duplicates are evaluated correctly\n responses[answer.containerIndex].images.splice(index, 1);\n } else {\n isCorrect = false;\n }\n });\n } else {\n isCorrect = false;\n }\n\n return isCorrect;\n};\n\n// This applies for correct responses that have empty values\nconst keepNonEmptyResponses = (responses) => {\n const filtered = responses.filter((response) => response.images && response.images.length);\n return cloneDeep(filtered);\n};\n\n// This applies for items that don't support partial scoring.\nconst isDefaultOrAltResponseCorrect = (question, session) => {\n const {\n validation: { altResponses },\n } = question;\n let {\n validation: {\n validResponse: { value },\n },\n } = question;\n\n let isCorrect = isResponseCorrect(value, session);\n\n // Look for correct answers in alternate responses.\n if (!isCorrect && altResponses && altResponses.length) {\n altResponses.forEach((altResponse) => {\n if (isResponseCorrect(altResponse.value, session)) {\n isCorrect = true;\n }\n });\n }\n return isCorrect;\n};\n\n// Deduct only the items that exceeded the maximum valid response per container.\nconst getDeductionPerContainer = (containerIndex, answers, valid) => {\n const totalStack = answers.filter((item) => item.containerIndex === containerIndex);\n const incorrectStack = totalStack.filter((item) => !item.isCorrect);\n const maxValid = (valid.value[containerIndex].images || []).length;\n\n if (totalStack.length > maxValid) {\n const ignored = totalStack.length - maxValid;\n return incorrectStack.slice(-ignored);\n }\n return [];\n};\n\nexport const getPartialScore = (question, session) => {\n const {\n validation: { validResponse },\n maxResponsePerZone,\n } = question;\n let correctAnswers = 0;\n let incorrectAnswers = 0;\n let possibleResponses = 0;\n\n if (!session || isEmpty(session)) {\n return 0;\n }\n\n validResponse.value.forEach((value) => (possibleResponses += (value.images || []).length));\n\n if (session.answers && session.answers.length) {\n const all = getAllUniqueCorrectness(session.answers, validResponse.value);\n correctAnswers = all.filter((item) => item.isCorrect).length;\n incorrectAnswers = all.filter((item) => !item.isCorrect).length;\n\n // deduction rules: https://docs.google.com/document/d/1Oprm8Qs5fg_Dwoj2pNpsfu4D63QgCZgvcqTgeaVel7I/edit\n session.answers.forEach((answer) => {\n if (maxResponsePerZone > 1) {\n const deductionList = getDeductionPerContainer(answer.containerIndex, all, validResponse);\n\n if (deductionList.length) {\n deductionList.forEach((item) => {\n if (item.id === answer.id) {\n correctAnswers -= 1;\n }\n });\n }\n }\n });\n\n if (!maxResponsePerZone || maxResponsePerZone <= 1) {\n correctAnswers -= incorrectAnswers;\n }\n } else {\n correctAnswers = 0;\n }\n // negative values will implicitly make the score equal to zero\n correctAnswers = correctAnswers < 0 ? 0 : correctAnswers;\n\n // use length of validResponse since some containers can be left empty\n const nonEmptyResponses = keepNonEmptyResponses(validResponse.value);\n const denominator = maxResponsePerZone > 1 ? possibleResponses : (nonEmptyResponses || []).length;\n const str = (correctAnswers / denominator).toFixed(2);\n\n return parseFloat(str);\n};\n\nconst getScore = (config, session, env = {}) => {\n const isPartialScoring = partialScoring.enabled(config, env);\n const correct = isDefaultOrAltResponseCorrect(config, session);\n\n return isPartialScoring ? getPartialScore(config, session) : correct ? 1 : 0;\n};\n\n/**\n * Generates detailed trace log for scoring evaluation\n * @param {Object} model - the question model\n * @param {Object} session - the student session\n * @param {Object} env - the environment\n * @returns {Array} traceLog - array of trace messages\n */\nexport const getLogTrace = (model, session, env) => {\n const traceLog = [];\n const { answers } = session || {};\n \n const validResponse = model.validation?.validResponse?.value || [];\n const totalContainers = validResponse.length;\n traceLog.push(`${totalContainers} placement container(s) defined in this question.`);\n\n if (answers && answers.length > 0) {\n traceLog.push(`Student placed ${answers.length} image(s) into placement containers.`);\n \n const answersByContainer = {};\n answers.forEach((answer) => {\n if (!answersByContainer[answer.containerIndex]) {\n answersByContainer[answer.containerIndex] = [];\n }\n answersByContainer[answer.containerIndex].push(answer.value);\n });\n \n validResponse.forEach((container, containerIndex) => {\n const correctImages = container.images || [];\n const studentImages = answersByContainer[containerIndex] || [];\n \n if (correctImages.length > 0) {\n if (studentImages.length === 0) {\n traceLog.push(`Container ${containerIndex + 1}: student left empty (should contain ${correctImages.length} image(s)).`);\n } else {\n const correctCount = studentImages.filter(img => correctImages.includes(img)).length;\n const incorrectCount = studentImages.length - correctCount;\n \n if (correctCount > 0 && incorrectCount === 0) {\n traceLog.push(`Container ${containerIndex + 1}: student placed ${correctCount} correct image(s).`);\n } else if (correctCount === 0 && incorrectCount > 0) {\n traceLog.push(`Container ${containerIndex + 1}: student placed ${incorrectCount} incorrect image(s).`);\n } else {\n traceLog.push(`Container ${containerIndex + 1}: student placed ${correctCount} correct and ${incorrectCount} incorrect image(s).`);\n }\n }\n }\n });\n } else {\n traceLog.push('Student did not place any images into placement containers.');\n }\n\n const altResponses = model.validation?.altResponses || [];\n if (altResponses.length > 0) {\n traceLog.push(`${altResponses.length} alternate response combination(s) are accepted for this question.`);\n }\n\n const partialScoringEnabled = partialScoring.enabled(model, env);\n const scoringMethod = partialScoringEnabled ? 'partial scoring' : 'all-or-nothing scoring';\n traceLog.push(`Score calculated using ${scoringMethod}.`);\n\n const score = getScore(model, session, env);\n traceLog.push(`Final score: ${score}.`);\n\n return traceLog;\n}\n\nexport const outcome = (config, session, env = {}) => {\n return new Promise((resolve) => {\n log('outcome...');\n if (!session || isEmpty(session)) {\n resolve({ \n score: 0, \n empty: true, \n traceLog: ['Student did not place any images into placement containers. Score is 0.'] \n });\n } else {\n const configCamelized = camelizeKeys(config);\n const traceLog = getLogTrace(configCamelized, session, env);\n const score = getScore(configCamelized, session, env);\n \n resolve({ \n score, \n empty: false,\n traceLog \n });\n }\n });\n};\n\nexport const createCorrectResponseSession = (question, env) => {\n return new Promise((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n const {\n validation: {\n valid_response: { value },\n },\n } = question;\n const answers = [];\n\n if (value) {\n value.forEach((container, i) => {\n (container.images || []).forEach((v) => {\n answers.push({\n value: v,\n containerIndex: i,\n });\n });\n });\n }\n\n resolve({\n answers,\n id: '1',\n });\n } else {\n resolve(null);\n }\n });\n};\n\n// remove all html tags\nconst getInnerText = (html) => (html || '').replaceAll(/<[^>]*>/g, '');\n\n// remove all html tags except img, iframe and source tag for audio\nconst getContent = (html) => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');\n\nexport const validate = (model = {}, config = {}) => {\n const errors = {};\n\n ['teacherInstructions'].forEach((field) => {\n if (config[field]?.required && !getContent(model[field])) {\n errors[field] = 'This field is required.';\n }\n });\n\n return errors;\n};\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { a as e, b as t, f as n } from "../dist-DZyIyIXw.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Dt as r, F as i, H as a, Lt as o, Rt as s, Vt as c, a as l, b as u, c as d, d as f, f as p, ft as m, ht as h, i as g, k as _, l as v, n as y, o as b, pt as x, r as S, s as C, t as w, u as T, v as E, x as D, y as O } from "../Check-D1Xgr9Ov.js";
|
|
3
3
|
import { t as ee } from "../humps-C6P4rNef.js";
|
|
4
4
|
import k, { Component as A, useEffect as te, useRef as j, useState as M } from "react";
|
|
5
5
|
import { createRoot as N } from "react-dom/client";
|
|
6
6
|
import { jsx as P, jsxs as F } from "react/jsx-runtime";
|
|
7
7
|
//#region ../../lib-react/icons/dist/correct-response-icon.js
|
|
8
|
-
var I = /* @__PURE__ */ t(
|
|
8
|
+
var I = /* @__PURE__ */ t(o(), 1), L = ({ bgFill: e, fgFill: t }) => /* @__PURE__ */ F("svg", {
|
|
9
9
|
preserveAspectRatio: "xMinYMin meet",
|
|
10
10
|
version: "1.1",
|
|
11
11
|
viewBox: "-283 359 34 35",
|
|
@@ -163,22 +163,22 @@ var le = h("div")(({ theme: e }) => ({
|
|
|
163
163
|
userSelect: "none",
|
|
164
164
|
width: "100%",
|
|
165
165
|
height: "100%",
|
|
166
|
-
background:
|
|
166
|
+
background: s.white(),
|
|
167
167
|
transition: "background-color 200ms linear, border-color 200ms linear",
|
|
168
168
|
boxSizing: "border-box",
|
|
169
169
|
display: "grid",
|
|
170
170
|
gridRowGap: e.spacing(1),
|
|
171
171
|
gridColumnGap: e.spacing(1),
|
|
172
172
|
padding: e.spacing(1),
|
|
173
|
-
border: `2px dashed ${
|
|
173
|
+
border: `2px dashed ${s.black()}`
|
|
174
174
|
},
|
|
175
175
|
"&.placeholderDisabled": {
|
|
176
176
|
boxShadow: "none",
|
|
177
|
-
background:
|
|
177
|
+
background: s.white()
|
|
178
178
|
},
|
|
179
179
|
"&.over": {
|
|
180
|
-
border: `1px solid ${
|
|
181
|
-
backgroundColor:
|
|
180
|
+
border: `1px solid ${s.border()}`,
|
|
181
|
+
backgroundColor: s.backgroundDark()
|
|
182
182
|
},
|
|
183
183
|
"&.board": {
|
|
184
184
|
padding: e.spacing(1),
|
|
@@ -189,7 +189,7 @@ var le = h("div")(({ theme: e }) => ({
|
|
|
189
189
|
justifyContent: "center",
|
|
190
190
|
overflow: "hidden",
|
|
191
191
|
touchAction: "none",
|
|
192
|
-
backgroundColor:
|
|
192
|
+
backgroundColor: s.surface()
|
|
193
193
|
},
|
|
194
194
|
"&.categorizeBoard": {
|
|
195
195
|
padding: e.spacing(.5),
|
|
@@ -200,19 +200,19 @@ var le = h("div")(({ theme: e }) => ({
|
|
|
200
200
|
justifyContent: "center",
|
|
201
201
|
overflow: "hidden",
|
|
202
202
|
touchAction: "none",
|
|
203
|
-
backgroundColor:
|
|
203
|
+
backgroundColor: s.surface()
|
|
204
204
|
},
|
|
205
205
|
"&.verticalPool": {
|
|
206
206
|
display: "flex",
|
|
207
207
|
flexFlow: "column wrap"
|
|
208
208
|
}
|
|
209
209
|
})), ue = (e) => {
|
|
210
|
-
let { children: t, className: n, isOver: r, type: i, grid: a, disabled: o, choiceBoard:
|
|
210
|
+
let { children: t, className: n, isOver: r, type: i, grid: a, disabled: o, choiceBoard: c, isCategorize: l, isVerticalPool: u, minHeight: d, extraStyles: f } = e, p = ce("placeholder", o && "placeholderDisabled", r && "over", i, n), m = {};
|
|
211
211
|
if (a && a.columns && (m.gridTemplateColumns = `repeat(${a.columns}, 1fr)`), a && a.rows) {
|
|
212
212
|
let e = a.rowsRepeatValue || "1fr";
|
|
213
213
|
m.gridTemplateRows = `repeat(${a.rows}, ${e})`;
|
|
214
214
|
}
|
|
215
|
-
i === "choice" && (m.border = `1px solid ${
|
|
215
|
+
i === "choice" && (m.border = `1px solid ${s.borderLight()}`, m.background = s.surface());
|
|
216
216
|
let h = l ? "categorizeBoard" : "board";
|
|
217
217
|
return /* @__PURE__ */ P(le, {
|
|
218
218
|
style: {
|
|
@@ -220,7 +220,7 @@ var le = h("div")(({ theme: e }) => ({
|
|
|
220
220
|
minHeight: d,
|
|
221
221
|
...f
|
|
222
222
|
},
|
|
223
|
-
className: ce(
|
|
223
|
+
className: ce(c ? h : p, u && "verticalPool"),
|
|
224
224
|
children: t
|
|
225
225
|
});
|
|
226
226
|
};
|
|
@@ -329,7 +329,7 @@ me.propTypes = {
|
|
|
329
329
|
};
|
|
330
330
|
//#endregion
|
|
331
331
|
//#region ../../../node_modules/.bun/@mui+icons-material@7.3.11+44ccb211c00abd9d/node_modules/@mui/icons-material/esm/NotInterested.js
|
|
332
|
-
var he =
|
|
332
|
+
var he = a(/*#__PURE__*/ P("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20m6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9" }), "NotInterested"), ge = "height ease-in 300ms, opacity ease-in 300ms", _e = h("div")(() => ({
|
|
333
333
|
position: "relative",
|
|
334
334
|
height: 0,
|
|
335
335
|
overflow: "hidden",
|
|
@@ -1941,7 +1941,7 @@ var mt = pt(u, "Readable") || pt(_t.Readable, "Readable"), ht = p, gt = ht.defau
|
|
|
1941
1941
|
minWidth: "140px",
|
|
1942
1942
|
alignSelf: "center",
|
|
1943
1943
|
verticalAlign: "middle",
|
|
1944
|
-
color: `var(--correct-answer-toggle-label-color, ${
|
|
1944
|
+
color: `var(--correct-answer-toggle-label-color, ${s.text()})`,
|
|
1945
1945
|
fontWeight: "normal",
|
|
1946
1946
|
...yt
|
|
1947
1947
|
})), Ct = h("div")(() => ({
|
|
@@ -2048,16 +2048,16 @@ var mt = pt(u, "Readable") || pt(_t.Readable, "Readable"), ht = p, gt = ht.defau
|
|
|
2048
2048
|
})
|
|
2049
2049
|
});
|
|
2050
2050
|
}
|
|
2051
|
-
}, Et =
|
|
2052
|
-
"&.correctEmpty": { color:
|
|
2051
|
+
}, Et = a(/*#__PURE__*/ P("path", { d: "M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }), "Close"), Dt = h("svg")(() => ({
|
|
2052
|
+
"&.correctEmpty": { color: s.correct() },
|
|
2053
2053
|
"&.correctFilled": {
|
|
2054
|
-
color:
|
|
2055
|
-
backgroundColor:
|
|
2054
|
+
color: s.background(),
|
|
2055
|
+
backgroundColor: s.correct()
|
|
2056
2056
|
},
|
|
2057
|
-
"&.incorrectEmpty": { color:
|
|
2057
|
+
"&.incorrectEmpty": { color: s.incorrect() },
|
|
2058
2058
|
"&.incorrectFilled": {
|
|
2059
|
-
color:
|
|
2060
|
-
backgroundColor:
|
|
2059
|
+
color: s.background(),
|
|
2060
|
+
backgroundColor: s.incorrect()
|
|
2061
2061
|
}
|
|
2062
2062
|
})), Ot = (e, t) => `${e ? "correct" : "incorrect"}${t ? "Filled" : "Empty"}`, kt = ({ containerStyle: e, isCorrect: t, filled: n }) => {
|
|
2063
2063
|
let r = t ? w : Et, i = t !== void 0, a = i ? Ot(t, n) : "";
|
|
@@ -2098,8 +2098,8 @@ At.propTypes = {
|
|
|
2098
2098
|
//#region src/delivery/possible-response.tsx
|
|
2099
2099
|
var jt = h("div")(() => ({
|
|
2100
2100
|
position: "relative",
|
|
2101
|
-
backgroundColor:
|
|
2102
|
-
border: `1px solid ${
|
|
2101
|
+
backgroundColor: s.white(),
|
|
2102
|
+
border: `1px solid ${s.borderDark()}`,
|
|
2103
2103
|
display: "flex",
|
|
2104
2104
|
alignItems: "center",
|
|
2105
2105
|
justifyContent: "center",
|
|
@@ -2112,20 +2112,20 @@ var jt = h("div")(() => ({
|
|
|
2112
2112
|
},
|
|
2113
2113
|
"&.answerChoiceTransparency": {
|
|
2114
2114
|
border: "none",
|
|
2115
|
-
backgroundColor: `${
|
|
2116
|
-
"&:hover": { border: `1px solid ${
|
|
2115
|
+
backgroundColor: `${s.transparent()}`,
|
|
2116
|
+
"&:hover": { border: `1px solid ${s.borderDark()}` }
|
|
2117
2117
|
},
|
|
2118
|
-
"&.baseCorrect": { border: `2px solid ${
|
|
2119
|
-
"&.baseIncorrect": { border: `2px solid ${
|
|
2118
|
+
"&.baseCorrect": { border: `2px solid ${s.correct()} !important` },
|
|
2119
|
+
"&.baseIncorrect": { border: `2px solid ${s.incorrect()} !important` },
|
|
2120
2120
|
"&.selected": {
|
|
2121
|
-
border: `2px solid ${
|
|
2121
|
+
border: `2px solid ${s.buttonFocusOutline()}`,
|
|
2122
2122
|
"& > *": { opacity: .7 }
|
|
2123
2123
|
}
|
|
2124
2124
|
})), Mt = h(At)(() => ({
|
|
2125
2125
|
cursor: "grab",
|
|
2126
|
-
backgroundColor:
|
|
2126
|
+
backgroundColor: s.background(),
|
|
2127
2127
|
"&.hiddenSpan": { visibility: "hidden" }
|
|
2128
|
-
})), Z = ({ canDrag: e, containerStyle: t, data: n, onDragBegin:
|
|
2128
|
+
})), Z = ({ canDrag: e, containerStyle: t, data: n, onDragBegin: i, answerChoiceTransparency: a, isOverlay: o, selectedResponse: s, onSelectClick: c, onPlacementClick: l }) => {
|
|
2129
2129
|
let u = j(null), f = j(null), { setNodeRef: p, attributes: m, listeners: h, isDragging: g } = d({
|
|
2130
2130
|
id: `possible-response-${n.id}`,
|
|
2131
2131
|
data: {
|
|
@@ -2145,7 +2145,7 @@ var jt = h("div")(() => ({
|
|
|
2145
2145
|
clearTimeout(f.current);
|
|
2146
2146
|
}, x = (t) => {
|
|
2147
2147
|
t.preventDefault(), f.current = setTimeout(() => {
|
|
2148
|
-
e && u.current &&
|
|
2148
|
+
e && u.current && i(n);
|
|
2149
2149
|
}, 500);
|
|
2150
2150
|
};
|
|
2151
2151
|
te(() => {
|
|
@@ -2159,12 +2159,12 @@ var jt = h("div")(() => ({
|
|
|
2159
2159
|
position: "absolute",
|
|
2160
2160
|
bottom: "3px",
|
|
2161
2161
|
right: "3px"
|
|
2162
|
-
}, w = S === !0 ? "baseCorrect" : S === !1 ? "baseIncorrect" : void 0, T = /<img[^>]+src="([^">]+)"/.test(n.value), E =
|
|
2162
|
+
}, w = S === !0 ? "baseCorrect" : S === !1 ? "baseIncorrect" : void 0, T = /<img[^>]+src="([^">]+)"/.test(n.value), E = r({
|
|
2163
2163
|
answerChoiceTransparency: a && !g,
|
|
2164
2164
|
[w]: !!w,
|
|
2165
2165
|
textAnswerChoiceStyle: !T && !o,
|
|
2166
2166
|
selected: _ && !g
|
|
2167
|
-
}), D =
|
|
2167
|
+
}), D = r({ hiddenSpan: n.hidden });
|
|
2168
2168
|
return /* @__PURE__ */ F(jt, {
|
|
2169
2169
|
className: E,
|
|
2170
2170
|
style: t,
|
|
@@ -2208,15 +2208,15 @@ var Nt = h("div")(() => ({
|
|
|
2208
2208
|
position: "absolute",
|
|
2209
2209
|
boxSizing: "border-box",
|
|
2210
2210
|
"&.active": {
|
|
2211
|
-
border: `2px solid ${
|
|
2211
|
+
border: `2px solid ${s.text()}`,
|
|
2212
2212
|
backgroundColor: "rgba(230, 242, 252, .8)"
|
|
2213
2213
|
},
|
|
2214
|
-
"&.dashed": { border: `2px dashed ${
|
|
2214
|
+
"&.dashed": { border: `2px dashed ${s.text()}` },
|
|
2215
2215
|
"&.is-over": {
|
|
2216
2216
|
border: "1px solid rgb(158, 158, 158)",
|
|
2217
2217
|
backgroundColor: "rgb(224, 224, 224)"
|
|
2218
2218
|
}
|
|
2219
|
-
})), Ft = ({ answers: e, canDrag: t, containerStyle: n, draggingElement:
|
|
2219
|
+
})), Ft = ({ answers: e, canDrag: t, containerStyle: n, draggingElement: i, onDragAnswerBegin: a, onDragAnswerEnd: o, showDashedBorder: s, responseAreaFill: c, responseContainerPadding: l = "10px", imageDropTargetPadding: u, answerChoiceTransparency: d, maxResponsePerZone: f, onDrop: p, index: m, selectedResponse: h, onSelectClick: g, onPlacementClick: _ }) => {
|
|
2220
2220
|
let [y, b] = M(!1), [x, S] = M(!1), C = j(null), w = j(null), { setNodeRef: T, isOver: E } = v({
|
|
2221
2221
|
id: `response-container-${m}`,
|
|
2222
2222
|
data: {
|
|
@@ -2234,7 +2234,7 @@ var Nt = h("div")(() => ({
|
|
|
2234
2234
|
e.removeEventListener("touchstart", t);
|
|
2235
2235
|
};
|
|
2236
2236
|
}, []);
|
|
2237
|
-
let D = !!
|
|
2237
|
+
let D = !!i.id, O = !!h, ee = r({
|
|
2238
2238
|
"is-over": E || O && y && t,
|
|
2239
2239
|
dashed: s && !D,
|
|
2240
2240
|
active: D
|
|
@@ -2372,9 +2372,9 @@ var Rt = h("div")(({ theme: e }) => ({
|
|
|
2372
2372
|
display: "flex",
|
|
2373
2373
|
width: "fit-content",
|
|
2374
2374
|
maxWidth: "100%",
|
|
2375
|
-
"&.default": { border: `1px solid ${
|
|
2376
|
-
"&.correct": { border: `2px solid ${
|
|
2377
|
-
"&.incorrect": { border: `2px solid ${
|
|
2375
|
+
"&.default": { border: `1px solid ${s.disabled()}` },
|
|
2376
|
+
"&.correct": { border: `2px solid ${s.correct()}` },
|
|
2377
|
+
"&.incorrect": { border: `2px solid ${s.incorrect()}` }
|
|
2378
2378
|
})), zt = class extends k.Component {
|
|
2379
2379
|
getClassname() {
|
|
2380
2380
|
let { responseCorrect: e } = this.props, t;
|
|
@@ -2434,7 +2434,7 @@ zt.propTypes = {
|
|
|
2434
2434
|
//#endregion
|
|
2435
2435
|
//#region src/delivery/possible-responses.tsx
|
|
2436
2436
|
var Bt = h("div")(({ theme: e }) => ({
|
|
2437
|
-
backgroundColor:
|
|
2437
|
+
backgroundColor: s.background(),
|
|
2438
2438
|
margin: e.spacing(2),
|
|
2439
2439
|
display: "flex",
|
|
2440
2440
|
alignItems: "center",
|
|
@@ -2473,9 +2473,7 @@ Vt.propTypes = {
|
|
|
2473
2473
|
onSelectClick: I.default.func,
|
|
2474
2474
|
onPlacementClick: I.default.func
|
|
2475
2475
|
};
|
|
2476
|
-
|
|
2477
|
-
//#region src/delivery/utils-correctness.ts
|
|
2478
|
-
var Ht = ee(), Ut = (e, t) => (e || []).map((e) => ({
|
|
2476
|
+
var { camelizeKeys: Ht } = (/* @__PURE__ */ t(ee(), 1)).default, Ut = (e, t) => (e || []).map((e) => ({
|
|
2479
2477
|
...e,
|
|
2480
2478
|
isCorrect: (t[e.containerIndex] && t[e.containerIndex].images || []).includes(e.value)
|
|
2481
2479
|
})), Wt = (e, t) => (t[e.containerIndex].images || []).filter((t) => t === e.value), Gt = (e, t) => {
|
|
@@ -2496,7 +2494,7 @@ var Ht = ee(), Ut = (e, t) => (e || []).map((e) => ({
|
|
|
2496
2494
|
}));
|
|
2497
2495
|
}), n;
|
|
2498
2496
|
}, Kt = (e, t) => {
|
|
2499
|
-
let { validResponse: { value: n } = {} } =
|
|
2497
|
+
let { validResponse: { value: n } = {} } = Ht(t);
|
|
2500
2498
|
return (n || []).reduce((t, n, r) => {
|
|
2501
2499
|
let i = !!e.find((e) => e.containerIndex === r);
|
|
2502
2500
|
return n.images = n.images || [], i ? t : [...t, {
|
|
@@ -2508,7 +2506,7 @@ var Ht = ee(), Ut = (e, t) => (e || []).map((e) => ({
|
|
|
2508
2506
|
}];
|
|
2509
2507
|
}, []);
|
|
2510
2508
|
}, qt = (e, t) => {
|
|
2511
|
-
let { validResponse: { value: n }, altResponses: r } =
|
|
2509
|
+
let { validResponse: { value: n }, altResponses: r } = Ht(t), i = Gt(Ut(e, n), n);
|
|
2512
2510
|
return i.filter((e) => e.isCorrect).length < i.length && r && r.length ? (r || []).map((t) => {
|
|
2513
2511
|
let n = t.value;
|
|
2514
2512
|
return Gt(Ut(e, n), n);
|
|
@@ -2566,9 +2564,9 @@ function Q(e) {
|
|
|
2566
2564
|
function $(e, t) {
|
|
2567
2565
|
return !e || Q(e) ? e : Q(e.default) ? e.default : t && Q(e[t]) ? e[t] : t && Q(e[t]?.default) ? e[t].default : e;
|
|
2568
2566
|
}
|
|
2569
|
-
var Xt = $(_, "UiLayout") || $(tn.UiLayout, "UiLayout"), Zt = $(D, "PreviewPrompt") || $(tn.PreviewPrompt, "PreviewPrompt"), Qt = $(
|
|
2570
|
-
color:
|
|
2571
|
-
backgroundColor:
|
|
2567
|
+
var Xt = $(_, "UiLayout") || $(tn.UiLayout, "UiLayout"), Zt = $(D, "PreviewPrompt") || $(tn.PreviewPrompt, "PreviewPrompt"), Qt = $(i, "Collapsible") || $(tn.Collapsible, "Collapsible"), $t = p, en = $t.default, tn = en && typeof en == "object" ? en : $t, { translator: nn } = dt, rn = () => Math.random().toString(36).substring(2) + (/* @__PURE__ */ new Date()).getTime().toString(36), an = 250, on = h(Xt)({
|
|
2568
|
+
color: s.text(),
|
|
2569
|
+
backgroundColor: s.background(),
|
|
2572
2570
|
position: "relative",
|
|
2573
2571
|
"& img": {
|
|
2574
2572
|
maxWidth: "100%",
|
|
@@ -2963,7 +2961,7 @@ var hn = mn("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMC
|
|
|
2963
2961
|
updateAnswer: this.updateAnswer.bind(this)
|
|
2964
2962
|
});
|
|
2965
2963
|
this._root ||= N(this), this._root.render(e), queueMicrotask(() => {
|
|
2966
|
-
|
|
2964
|
+
c(this);
|
|
2967
2965
|
});
|
|
2968
2966
|
}
|
|
2969
2967
|
}
|