@pie-element/mc-populated-blank 0.3.5-next.20261012213240 → 0.3.5

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.
@@ -22,8 +22,11 @@ a(u, {
22
22
  }, [], []);
23
23
  //#endregion
24
24
  //#region src/author/index.ts
25
- var d = u.element;
25
+ var d = u.element, f = Object.freeze({
26
+ name: "@pie-element/mc-populated-blank",
27
+ version: "0.3.5"
28
+ });
26
29
  //#endregion
27
- export { d as default };
30
+ export { d as default, f as piePackage };
28
31
 
29
32
  //# sourceMappingURL=index.js.map
@@ -1,2 +1,2 @@
1
- import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "../controller-Dv_mGlnz.js";
2
- export { c as countBlankTokens, i as createCorrectResponseSession, o as createDefaultModel, n as getCorrectness, e as getPartialScore, a as model, s as normalize, t as normalizeSession, r as outcome, l as validate };
1
+ import { a as e, c as t, d as n, i as r, l as i, n as a, o, r as s, s as c, t as l, u } from "../controller-DBLwJyvm.js";
2
+ export { l as countBlankTokens, a as createCorrectResponseSession, s as createDefaultModel, r as getCorrectness, e as getPartialScore, o as model, c as normalize, t as normalizeSession, i as outcome, u as piePackage, n as validate };
@@ -214,8 +214,11 @@ var f = (e, n) => {
214
214
  }
215
215
  }
216
216
  return n;
217
- };
217
+ }, w = Object.freeze({
218
+ name: "@pie-element/mc-populated-blank",
219
+ version: "0.3.5"
220
+ });
218
221
  //#endregion
219
- export { p as a, v as c, f as i, m as l, x as n, b as o, g as r, _ as s, d as t, C as u };
222
+ export { p as a, v as c, C as d, f as i, m as l, x as n, b as o, g as r, _ as s, d as t, w as u };
220
223
 
221
- //# sourceMappingURL=controller-Dv_mGlnz.js.map
224
+ //# sourceMappingURL=controller-DBLwJyvm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"controller-Dv_mGlnz.js","names":[],"sources":["../../../../shared/controller-utils/dist/index.js","../../src/controller/defaults.ts","../../src/author/defaults.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","import { DEFAULT_LAYOUT_LIMITS } from '../shared/layoutLimits.js';\nimport type { McpbChoice, McPopulatedBlankModel } from '../types.js';\n\nexport { DEFAULT_LAYOUT_LIMITS };\n\n/** Token authors must include exactly once in `template` (HTML). */\nexport const BLANK_TOKEN = '{{blank}}';\n\n/**\n * The values `normalize` fills in for fields an item leaves out. They carry no\n * content and no `id` or `element`: those come from the item config, and a\n * default here would stand in for a field the author never set.\n */\nexport default {\n model: {\n prompt: '',\n promptEnabled: true,\n interactionMode: 'populate_blank' as const,\n layoutProfile: '',\n choiceLayout: '',\n layoutProfilePresets: {},\n layoutLimits: { ...DEFAULT_LAYOUT_LIMITS },\n audioButtonSkin: null,\n audioButtonSkinsByLocale: {},\n sentenceHtml: '',\n template: '',\n choiceMode: 'text' as const,\n choices: [] as McpbChoice[],\n choiceGroupLabel: '',\n correctChoiceId: '',\n hasAudio: false,\n autoplayAudioEnabled: false,\n completeAudioEnabled: false,\n audioUrl: '',\n audioTranscript: '',\n locale: '',\n teacherInstructions: '',\n teacherInstructionsEnabled: true,\n },\n} satisfies { model: Omit<McPopulatedBlankModel, 'id' | 'element'> };\n","import controllerDefaults, { BLANK_TOKEN } from '../controller/defaults.js';\nimport type { McPopulatedBlankConfiguration, McPopulatedBlankModel } from '../types.js';\n\n/** The model a new item starts from: a valid question an author edits in place. */\nexport default {\n model: {\n ...controllerDefaults.model,\n prompt: '<p><strong>Demo prompt</strong></p>',\n template: `<p>The answer is ${BLANK_TOKEN}.</p>`,\n choices: [\n { id: 'c1', labelHtml: '<p>Option A</p>' },\n { id: 'c2', labelHtml: '<p>Option B</p>' },\n { id: 'c3', labelHtml: '<p>Option C</p>' },\n ],\n correctChoiceId: 'c2',\n },\n configuration: {\n prompt: {\n label: 'Prompt',\n settings: true,\n },\n },\n} satisfies {\n model: Omit<McPopulatedBlankModel, 'id' | 'element'>;\n configuration: McPopulatedBlankConfiguration;\n};\n","import { getShuffledChoices, lockChoices } from '@pie-element/shared-controller-utils';\nimport type { PieUpdateSession } from '@pie-element/shared-types';\nimport authorDefaults from '../author/defaults.js';\nimport defaults, { BLANK_TOKEN, DEFAULT_LAYOUT_LIMITS } from './defaults.js';\nimport type {\n McpbChoice,\n McpbCorrectness,\n McpbEnv,\n McPopulatedBlankConfiguration,\n McPopulatedBlankModel,\n McPopulatedBlankSession,\n} from '../types.js';\nimport { selectedChoiceId } from '../shared/session.js';\n\nconst isEmptyObject = (value: unknown): boolean =>\n !!value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.keys(value as Record<string, unknown>).length === 0;\n\nexport function countBlankTokens(template: string): number {\n if (!template) return 0;\n const re = /\\{\\{blank\\}\\}/g;\n return (template.match(re) || []).length;\n}\n\nexport const getCorrectness = (\n question: Partial<McPopulatedBlankModel>,\n session: McPopulatedBlankSession\n): McpbCorrectness => {\n const picked = selectedChoiceId(session);\n if (!picked) {\n return 'unanswered';\n }\n const correct = question?.correctChoiceId || '';\n if (picked === correct) {\n return 'correct';\n }\n return 'incorrect';\n};\n\n/** One blank, one key: an answered session scores 1 only when it picked the key. */\nexport const getPartialScore = (\n question: Partial<McPopulatedBlankModel>,\n session: McPopulatedBlankSession\n) => (getCorrectness(question, session) === 'correct' ? 1 : 0);\n\nexport const outcome = (\n question: McPopulatedBlankModel,\n session: McPopulatedBlankSession,\n env: McpbEnv\n) =>\n new Promise<{ score: number; empty: boolean; traceLog: string[] }>((resolve) => {\n if (!session || isEmptyObject(session)) {\n resolve({\n score: 0,\n empty: true,\n traceLog: ['Student did not select any answers. Score is 0.'],\n });\n return;\n }\n\n session = normalizeSession(session);\n const normalizedQuestion = normalize(question);\n const correctness = getCorrectness(normalizedQuestion, session);\n\n if (correctness === 'unanswered') {\n resolve({\n score: 0,\n empty: true,\n traceLog: ['Student did not select any answers. Score is 0.'],\n });\n return;\n }\n\n const score = getPartialScore(normalizedQuestion, session);\n const traceLog = [\n `Mode: ${env?.mode || 'unknown'}.`,\n `Student selected choice: ${selectedChoiceId(session)}.`,\n `Correct choice: ${normalizedQuestion.correctChoiceId || 'none'}.`,\n `Final score: ${score}.`,\n ];\n resolve({ score, empty: false, traceLog });\n });\n\nconst withDefaults = (\n base: Partial<McPopulatedBlankModel>,\n model: Partial<McPopulatedBlankModel> = {}\n) => ({\n ...base,\n ...model,\n layoutLimits: {\n ...DEFAULT_LAYOUT_LIMITS,\n ...(model?.layoutLimits || {}),\n },\n});\n\n/** The authoring starting point: a question that passes `validate()` as it stands. */\nexport const createDefaultModel = (model: Partial<McPopulatedBlankModel> = {}) =>\n withDefaults(authorDefaults.model, model);\n\n/**\n * What `model()` and `outcome()` read: the item's own fields, with neutral\n * values for the rest. Starter content here would reach a learner as the\n * item's prompt or choices.\n */\nexport const normalize = (question: Partial<McPopulatedBlankModel> = {}) => {\n const { shuffle, ...rest } = question || {};\n return {\n ...withDefaults(defaults.model, rest),\n // `lockChoiceOrder` alone decides the order, as in multiple-choice. Older items\n // spell an unlocked order `shuffle: true`, which counts only when it is unset.\n lockChoiceOrder:\n typeof rest.lockChoiceOrder === 'boolean' ? rest.lockChoiceOrder : shuffle !== true,\n };\n};\n\nexport const normalizeSession = (s: McPopulatedBlankSession): McPopulatedBlankSession => ({\n ...s,\n});\n\n/**\n * The fields delivery renders. An imported choice can carry more, such as a\n * correctness flag or feedback, which must not reach a learner's browser.\n */\nconst toDeliveryChoice = ({ id, labelHtml, imageUrl, imageAlt }: McpbChoice): McpbChoice => ({\n id,\n ...(labelHtml !== undefined ? { labelHtml } : {}),\n ...(imageUrl !== undefined ? { imageUrl } : {}),\n ...(imageAlt !== undefined ? { imageAlt } : {}),\n});\n\nexport const model = async (\n question: McPopulatedBlankModel,\n session: McPopulatedBlankSession | null,\n env: McpbEnv | null,\n updateSession?: PieUpdateSession\n) => {\n const safeSession: McPopulatedBlankSession = session || {};\n const safeEnv: McpbEnv = env || {};\n const normalizedQuestion = normalize(question);\n let orderedChoices = Array.isArray(normalizedQuestion.choices)\n ? [...normalizedQuestion.choices]\n : [];\n if (orderedChoices.length && !lockChoices(normalizedQuestion, safeSession, safeEnv)) {\n orderedChoices =\n (await getShuffledChoices(orderedChoices, safeSession, updateSession, 'id')) ??\n orderedChoices;\n }\n const choices = orderedChoices\n .filter((c): c is McpbChoice => !!c && typeof c === 'object')\n .map(toDeliveryChoice);\n\n const out: Record<string, unknown> = {\n prompt: normalizedQuestion.promptEnabled ? normalizedQuestion.prompt : null,\n interactionMode: normalizedQuestion.interactionMode || 'populate_blank',\n layoutProfile: normalizedQuestion.layoutProfile || '',\n choiceLayout: normalizedQuestion.choiceLayout || '',\n layoutProfilePresets:\n normalizedQuestion.layoutProfilePresets &&\n typeof normalizedQuestion.layoutProfilePresets === 'object'\n ? normalizedQuestion.layoutProfilePresets\n : {},\n layoutLimits: normalizedQuestion.layoutLimits || { ...DEFAULT_LAYOUT_LIMITS },\n audioButtonSkin:\n normalizedQuestion.audioButtonSkin && typeof normalizedQuestion.audioButtonSkin === 'object'\n ? normalizedQuestion.audioButtonSkin\n : null,\n audioButtonSkinsByLocale:\n normalizedQuestion.audioButtonSkinsByLocale &&\n typeof normalizedQuestion.audioButtonSkinsByLocale === 'object'\n ? normalizedQuestion.audioButtonSkinsByLocale\n : {},\n sentenceHtml: normalizedQuestion.sentenceHtml || null,\n template: normalizedQuestion.template,\n choiceMode: normalizedQuestion.choiceMode,\n choices,\n choiceGroupLabel: normalizedQuestion.choiceGroupLabel || '',\n hasAudio: normalizedQuestion.hasAudio,\n autoplayAudioEnabled: !!normalizedQuestion.autoplayAudioEnabled,\n completeAudioEnabled: !!normalizedQuestion.completeAudioEnabled,\n audioUrl: normalizedQuestion.hasAudio ? normalizedQuestion.audioUrl : null,\n audioTranscript: normalizedQuestion.hasAudio ? normalizedQuestion.audioTranscript : null,\n customType: normalizedQuestion.customType || '',\n useFeatureButtonAudio:\n typeof normalizedQuestion.useFeatureButtonAudio === 'boolean'\n ? normalizedQuestion.useFeatureButtonAudio\n : undefined,\n locale: normalizedQuestion.locale || '',\n language: normalizedQuestion.language || normalizedQuestion.locale || undefined,\n disabled: safeEnv.mode !== 'gather',\n mode: safeEnv.mode,\n };\n\n if (safeEnv.mode === 'evaluate') {\n const correctness = getCorrectness(normalizedQuestion, safeSession);\n out.correctness = correctness;\n out.responseCorrect = correctness === 'correct';\n out.correctChoiceId = normalizedQuestion.correctChoiceId;\n }\n\n if (safeEnv.role === 'instructor' && (safeEnv.mode === 'view' || safeEnv.mode === 'evaluate')) {\n out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled\n ? normalizedQuestion.teacherInstructions\n : null;\n } else {\n out.teacherInstructions = null;\n }\n\n return out;\n};\n\nexport const createCorrectResponseSession = (question: McPopulatedBlankModel, env: McpbEnv) => {\n return new Promise<McPopulatedBlankSession | null>((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n // The player sets `id` and `element` on the entry from the item config.\n resolve({\n id: '1',\n value: question?.correctChoiceId ? [question.correctChoiceId] : [],\n });\n } else {\n resolve(null);\n }\n });\n};\n\n/** Markup with its tags removed, keeping media an author can answer from. */\nconst getContent = (html?: string) =>\n (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '').trim();\n\n/**\n * Authoring errors in multiple-choice's shape: a message per field, and under\n * `choices` a message per choice keyed by its id.\n */\nexport const validate = (\n question: Partial<McPopulatedBlankModel> = {},\n config: McPopulatedBlankConfiguration = {}\n) => {\n const errors: Record<string, string | Record<string, string>> = {};\n const { minAnswerChoices = 2, maxAnswerChoices } = config;\n\n if ((question.promptEnabled || config.prompt?.required) && !getContent(question.prompt)) {\n errors.prompt = 'This field is required.';\n }\n\n if (config.teacherInstructions?.required && !getContent(question.teacherInstructions)) {\n errors.teacherInstructions = 'This field is required.';\n }\n\n const interactionMode = question.interactionMode || 'populate_blank';\n const template = question.template || '';\n const n = countBlankTokens(template);\n if (interactionMode === 'audio_mc_only') {\n if (n > 0) {\n errors.template = `Template cannot contain ${BLANK_TOKEN} in audio-only mode`;\n }\n } else if (interactionMode === 'populate_blank') {\n if (n === 0) {\n errors.template = `Template must contain exactly one ${BLANK_TOKEN} placeholder`;\n } else if (n > 1) {\n errors.template = `Template must contain only one ${BLANK_TOKEN} placeholder`;\n }\n } else {\n errors.interactionMode = 'Unknown interaction mode';\n }\n\n const choices = Array.isArray(question.choices) ? question.choices : [];\n if (choices.length < minAnswerChoices) {\n errors.answerChoices = `There should be at least ${minAnswerChoices} choices defined.`;\n } else if (maxAnswerChoices != null && choices.length > maxAnswerChoices) {\n errors.answerChoices = `No more than ${maxAnswerChoices} choices should be defined.`;\n }\n\n const mode = question.choiceMode || 'text';\n const choicesErrors: Record<string, string> = {};\n const seenIds = new Set<string>();\n const seenLabels = new Set<string>();\n choices.forEach((c, i) => {\n const key = c?.id || String(i);\n if (!c?.id) {\n choicesErrors[key] = 'Choice needs an id.';\n return;\n }\n // The session stores the picked id, so two choices sharing one cannot be told apart.\n if (seenIds.has(c.id)) {\n choicesErrors[key] = 'Choice id should be unique.';\n return;\n }\n seenIds.add(c.id);\n if (mode === 'text') {\n const label = getContent(c.labelHtml);\n if (!label) {\n choicesErrors[key] = 'Content should not be empty.';\n } else if (seenLabels.has(label)) {\n choicesErrors[key] = 'Content should be unique.';\n }\n seenLabels.add(label);\n } else if (!c.imageUrl?.trim()) {\n choicesErrors[key] = 'An image is required.';\n } else if (!c.imageAlt?.trim()) {\n choicesErrors[key] = 'Image alt text is required.';\n }\n });\n if (Object.keys(choicesErrors).length) {\n errors.choices = choicesErrors;\n }\n\n const correct = question.correctChoiceId;\n if (!correct) {\n errors.correctResponse = 'No correct response defined.';\n } else if (!choices.some((c) => c?.id === correct)) {\n errors.correctResponse = 'The correct response must be one of the choices.';\n }\n\n if (question.hasAudio) {\n const hasAudioUrl = !!question.audioUrl?.trim();\n if (!hasAudioUrl) {\n errors.audioUrl = 'A playable audio URL is required when audio is enabled';\n }\n }\n\n const limits = question?.layoutLimits;\n if (limits && typeof limits === 'object') {\n const numericLimitKeys = [\n 'blankStandaloneWidthRem',\n 'blankWideWidthRem',\n 'blankUnderlineWidthPx',\n 'blankUnderlineWideWidthPx',\n 'horizontalChoiceWidthPx',\n 'horizontalChoiceWidthVw',\n 'horizontalChoiceTileMinHeightRem',\n 'horizontalChoiceContentMinHeightRem',\n 'choiceImageMaxHeightRem',\n 'choiceImageMaxWidthRem',\n 'listenButtonSizePx',\n 'stimulusMinColumnPx',\n 'textMinColumnPx',\n 'legendMaxChars',\n 'choiceGroupGapRem',\n 'choiceRowGapRem',\n 'toggleButtonGapRem',\n 'horizontalChoiceRadioTopMarginRem',\n 'audioBlankTemplateMarginTopRem',\n 'audioBlankTemplateMarginBottomRem',\n 'audioInstructionsMaxWidthPx',\n 'stimulusGridColumnGapRem',\n 'stimulusGridRowGapRem',\n 'stimulusSentenceMarginTopRem',\n 'stimulusChoicesMarginTopRem',\n 'tokenGridColumnGapRem',\n 'tokenGridRowGapRem',\n 'tokenTemplateMarginTopRem',\n 'tokenInlineTokenGapRem',\n 'tokenChoicesMarginTopRem',\n 'inlineGridColumnGapRem',\n 'inlineGridRowGapRem',\n 'inlineTemplateMarginTopRem',\n 'inlineChoicesMarginTopRem',\n ] as const;\n for (const key of numericLimitKeys) {\n if (limits[key] === undefined || limits[key] === null) continue;\n const value = Number(limits[key]);\n if (!Number.isFinite(value) || value <= 0) {\n errors.layoutLimits = `${key} must be a positive number when provided`;\n break;\n }\n }\n }\n\n return errors;\n};\n"],"mappings":";;AAWA,SAAS,EAAE,GAAG;CACb,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,QAAQ,MAAM,KAAK,IAAI,IAAI,CAAC;AACzD;AACA,SAAS,EAAE,GAAG;CACb,IAAI,IAAI,CAAC,GAAG,CAAC;CACb,KAAK,IAAI,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,KAAK;EACtC,IAAI,IAAI,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,EAAE;EAC1C,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE;CAC3B;CACA,OAAO;AACR;AACA,SAAS,EAAE,GAAG;CACb,OAAO,KAAK,OAAO,CAAC,IAAI,MAAM,QAAQ,CAAC,IAAI,EAAE,WAAW,IAAI,OAAO,KAAK,YAAY,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW;AAC/G;AACA,eAAe,EAAE,GAAG,GAAG,GAAG,IAAI,SAAS;CACtC,IAAI,IAAI,EAAE,GAAG,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,CAAC;CAC5D,IAAI,CAAC,GAAG;EACP,QAAQ,KAAK,6DAA6D;EAC1E;CACD;CACA,IAAI,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3D,IAAI,IAAI,EAAE,CAAC;CACX,IAAI,KAAK,OAAO,KAAK,YAAY,IAAI;EACpC,IAAI,IAAI,EAAE,EAAE,KAAK,MAAM,EAAE,EAAE,CAAC;EAC5B,EAAE,CAAC,IAAI,QAAQ,MAAM,uFAAuF,KAAK,UAAU,CAAC,EAAE,SAAS,GAAG,IAAI,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;CAC7L,SAAS,GAAG;EACX,QAAQ,KAAK,2CAA2C,GAAG,QAAQ,MAAM,CAAC;CAC3E;MACK,QAAQ,KAAK,kEAAkE;CACpF,OAAO;AACR;AACA,SAAS,EAAE,GAAG,GAAG,GAAG;CACnB,OAAO,CAAC,EAAE,EAAE,mBAAmB,IAAI,eAAe,EAAE;AACrD;;;ACtCA,IAAa,IAAc,aAO3B,IAAe,EACb,OAAO;CACL,QAAQ;CACR,eAAe;CACf,iBAAiB;CACjB,eAAe;CACf,cAAc;CACd,sBAAsB,CAAC;CACvB,cAAc,EAAE,GAAG,EAAsB;CACzC,iBAAiB;CACjB,0BAA0B,CAAC;CAC3B,cAAc;CACd,UAAU;CACV,YAAY;CACZ,SAAS,CAAC;CACV,kBAAkB;CAClB,iBAAiB;CACjB,UAAU;CACV,sBAAsB;CACtB,sBAAsB;CACtB,UAAU;CACV,iBAAiB;CACjB,QAAQ;CACR,qBAAqB;CACrB,4BAA4B;AAC9B,EACF,GCnCA,IAAe;CACb,OAAO;EACL,GAAG,EAAmB;EACtB,QAAQ;EACR,UAAU,oBAAoB,EAAY;EAC1C,SAAS;GACP;IAAE,IAAI;IAAM,WAAW;GAAkB;GACzC;IAAE,IAAI;IAAM,WAAW;GAAkB;GACzC;IAAE,IAAI;IAAM,WAAW;GAAkB;EAC3C;EACA,iBAAiB;CACnB;CACA,eAAe,EACb,QAAQ;EACN,OAAO;EACP,UAAU;CACZ,EACF;AACF,GCRM,KAAiB,MACrB,CAAC,CAAC,KACF,OAAO,KAAU,YACjB,CAAC,MAAM,QAAQ,CAAK,KACpB,OAAO,KAAK,CAAgC,CAAC,CAAC,WAAW;AAE3D,SAAgB,EAAiB,GAA0B;CAGzD,OAFK,KAEG,EAAS,MAAM,gBAAE,KAAK,CAAC,EAAA,CAAG,SAFZ;AAGxB;AAEA,IAAa,KACX,GACA,MACoB;CACpB,IAAM,IAAS,EAAiB,CAAO;CAQvC,OAPK,IAID,OADY,GAAU,mBAAmB,MAEpC,YAEF,cANE;AAOX,GAGa,KACX,GACA,MACI,IAAe,GAAU,CAAO,MAAM,YAE/B,KACX,GACA,GACA,MAEA,IAAI,SAAgE,MAAY;CAC9E,IAAI,CAAC,KAAW,EAAc,CAAO,GAAG;EACtC,EAAQ;GACN,OAAO;GACP,OAAO;GACP,UAAU,CAAC,iDAAiD;EAC9D,CAAC;EACD;CACF;CAEA,IAAU,EAAiB,CAAO;CAClC,IAAM,IAAqB,EAAU,CAAQ;CAG7C,IAFoB,EAAe,GAAoB,CAEnD,MAAgB,cAAc;EAChC,EAAQ;GACN,OAAO;GACP,OAAO;GACP,UAAU,CAAC,iDAAiD;EAC9D,CAAC;EACD;CACF;CAEA,IAAM,IAAQ,EAAgB,GAAoB,CAAO;CAOzD,EAAQ;EAAE;EAAO,OAAO;EAAO,UAAA;GAL7B,SAAS,GAAK,QAAQ,UAAU;GAChC,4BAA4B,EAAiB,CAAO,EAAE;GACtD,mBAAmB,EAAmB,mBAAmB,OAAO;GAChE,gBAAgB,EAAM;EAEO;CAAS,CAAC;AAC3C,CAAC,GAEG,KACJ,GACA,IAAwC,CAAC,OACrC;CACJ,GAAG;CACH,GAAG;CACH,cAAc;EACZ,GAAG;EACH,GAAI,GAAO,gBAAgB,CAAC;CAC9B;AACF,IAGa,KAAsB,IAAwC,CAAC,MAC1E,EAAa,EAAe,OAAO,CAAK,GAO7B,KAAa,IAA2C,CAAC,MAAM;CAC1E,IAAM,EAAE,YAAS,GAAG,MAAS,KAAY,CAAC;CAC1C,OAAO;EACL,GAAG,EAAa,EAAS,OAAO,CAAI;EAGpC,iBACE,OAAO,EAAK,mBAAoB,YAAY,EAAK,kBAAkB,MAAY;CACnF;AACF,GAEa,KAAoB,OAAyD,EACxF,GAAG,EACL,IAMM,KAAoB,EAAE,OAAI,cAAW,aAAU,mBAAwC;CAC3F;CACA,GAAI,MAAc,KAAA,IAA4B,CAAC,IAAjB,EAAE,aAAU;CAC1C,GAAI,MAAa,KAAA,IAA2B,CAAC,IAAhB,EAAE,YAAS;CACxC,GAAI,MAAa,KAAA,IAA2B,CAAC,IAAhB,EAAE,YAAS;AAC1C,IAEa,IAAQ,OACnB,GACA,GACA,GACA,MACG;CACH,IAAM,IAAuC,KAAW,CAAC,GACnD,IAAmB,KAAO,CAAC,GAC3B,IAAqB,EAAU,CAAQ,GACzC,IAAiB,MAAM,QAAQ,EAAmB,OAAO,IACzD,CAAC,GAAG,EAAmB,OAAO,IAC9B,CAAC;CACL,AAAI,EAAe,UAAU,CAAC,EAAY,GAAoB,GAAa,CAAO,MAChF,IACG,MAAM,EAAmB,GAAgB,GAAa,GAAe,IAAI,KAC1E;CAEJ,IAAM,IAAU,EACb,QAAQ,MAAuB,CAAC,CAAC,KAAK,OAAO,KAAM,QAAQ,CAAC,CAC5D,IAAI,CAAgB,GAEjB,IAA+B;EACnC,QAAQ,EAAmB,gBAAgB,EAAmB,SAAS;EACvE,iBAAiB,EAAmB,mBAAmB;EACvD,eAAe,EAAmB,iBAAiB;EACnD,cAAc,EAAmB,gBAAgB;EACjD,sBACE,EAAmB,wBACnB,OAAO,EAAmB,wBAAyB,WAC/C,EAAmB,uBACnB,CAAC;EACP,cAAc,EAAmB,gBAAgB,EAAE,GAAG,EAAsB;EAC5E,iBACE,EAAmB,mBAAmB,OAAO,EAAmB,mBAAoB,WAChF,EAAmB,kBACnB;EACN,0BACE,EAAmB,4BACnB,OAAO,EAAmB,4BAA6B,WACnD,EAAmB,2BACnB,CAAC;EACP,cAAc,EAAmB,gBAAgB;EACjD,UAAU,EAAmB;EAC7B,YAAY,EAAmB;EAC/B;EACA,kBAAkB,EAAmB,oBAAoB;EACzD,UAAU,EAAmB;EAC7B,sBAAsB,CAAC,CAAC,EAAmB;EAC3C,sBAAsB,CAAC,CAAC,EAAmB;EAC3C,UAAU,EAAmB,WAAW,EAAmB,WAAW;EACtE,iBAAiB,EAAmB,WAAW,EAAmB,kBAAkB;EACpF,YAAY,EAAmB,cAAc;EAC7C,uBACE,OAAO,EAAmB,yBAA0B,YAChD,EAAmB,wBACnB,KAAA;EACN,QAAQ,EAAmB,UAAU;EACrC,UAAU,EAAmB,YAAY,EAAmB,UAAU,KAAA;EACtE,UAAU,EAAQ,SAAS;EAC3B,MAAM,EAAQ;CAChB;CAEA,IAAI,EAAQ,SAAS,YAAY;EAC/B,IAAM,IAAc,EAAe,GAAoB,CAAW;EAGlE,AAFA,EAAI,cAAc,GAClB,EAAI,kBAAkB,MAAgB,WACtC,EAAI,kBAAkB,EAAmB;CAC3C;CAUA,OARI,EAAQ,SAAS,iBAAiB,EAAQ,SAAS,UAAU,EAAQ,SAAS,cAChF,EAAI,sBAAsB,EAAmB,6BACzC,EAAmB,sBACnB,OAEJ,EAAI,sBAAsB,MAGrB;AACT,GAEa,KAAgC,GAAiC,MACrE,IAAI,SAAyC,MAAY;CAC9D,AAAI,EAAI,SAAS,cAAc,EAAI,SAAS,eAE1C,EAAQ;EACN,IAAI;EACJ,OAAO,GAAU,kBAAkB,CAAC,EAAS,eAAe,IAAI,CAAC;CACnE,CAAC,IAED,EAAQ,IAAI;AAEhB,CAAC,GAIG,KAAc,OACjB,KAAQ,GAAA,CAAI,QAAQ,sCAAsC,EAAE,CAAC,CAAC,KAAK,GAMzD,KACX,IAA2C,CAAC,GAC5C,IAAwC,CAAC,MACtC;CACH,IAAM,IAA0D,CAAC,GAC3D,EAAE,sBAAmB,GAAG,wBAAqB;CAMnD,CAJK,EAAS,iBAAiB,EAAO,QAAQ,aAAa,CAAC,EAAW,EAAS,MAAM,MACpF,EAAO,SAAS,4BAGd,EAAO,qBAAqB,YAAY,CAAC,EAAW,EAAS,mBAAmB,MAClF,EAAO,sBAAsB;CAG/B,IAAM,IAAkB,EAAS,mBAAmB,kBAE9C,IAAI,EADO,EAAS,YAAY,EACH;CACnC,AAAI,MAAoB,kBAClB,IAAI,MACN,EAAO,WAAW,2BAA2B,EAAY,wBAElD,MAAoB,mBACzB,MAAM,IACR,EAAO,WAAW,qCAAqC,EAAY,gBAC1D,IAAI,MACb,EAAO,WAAW,kCAAkC,EAAY,iBAGlE,EAAO,kBAAkB;CAG3B,IAAM,IAAU,MAAM,QAAQ,EAAS,OAAO,IAAI,EAAS,UAAU,CAAC;CACtE,AAAI,EAAQ,SAAS,IACnB,EAAO,gBAAgB,4BAA4B,EAAiB,qBAC3D,KAAoB,QAAQ,EAAQ,SAAS,MACtD,EAAO,gBAAgB,gBAAgB,EAAiB;CAG1D,IAAM,IAAO,EAAS,cAAc,QAC9B,IAAwC,CAAC,GACzC,oBAAU,IAAI,IAAY,GAC1B,oBAAa,IAAI,IAAY;CA2BnC,AA1BA,EAAQ,SAAS,GAAG,MAAM;EACxB,IAAM,IAAM,GAAG,MAAM,OAAO,CAAC;EAC7B,IAAI,CAAC,GAAG,IAAI;GACV,EAAc,KAAO;GACrB;EACF;EAEA,IAAI,EAAQ,IAAI,EAAE,EAAE,GAAG;GACrB,EAAc,KAAO;GACrB;EACF;EAEA,IADA,EAAQ,IAAI,EAAE,EAAE,GACZ,MAAS,QAAQ;GACnB,IAAM,IAAQ,EAAW,EAAE,SAAS;GAMpC,AALK,IAEM,EAAW,IAAI,CAAK,MAC7B,EAAc,KAAO,+BAFrB,EAAc,KAAO,gCAIvB,EAAW,IAAI,CAAK;EACtB,OAAO,AAAK,EAAE,UAAU,KAAK,IAEjB,EAAE,UAAU,KAAK,MAC3B,EAAc,KAAO,iCAFrB,EAAc,KAAO;CAIzB,CAAC,GACG,OAAO,KAAK,CAAa,CAAC,CAAC,WAC7B,EAAO,UAAU;CAGnB,IAAM,IAAU,EAAS;CAOzB,AANK,IAEO,EAAQ,MAAM,MAAM,GAAG,OAAO,CAAO,MAC/C,EAAO,kBAAkB,sDAFzB,EAAO,kBAAkB,gCAKvB,EAAS,aACW,EAAS,UAAU,KAAK,MAE5C,EAAO,WAAW;CAItB,IAAM,IAAS,GAAU;CACzB,IAAI,KAAU,OAAO,KAAW,UAqC9B,KAAK,IAAM,KAAO,80BAAA,GAAkB;EAClC,IAAI,EAAO,OAAS,KAAA,KAAa,EAAO,OAAS,MAAM;EACvD,IAAM,IAAQ,OAAO,EAAO,EAAI;EAChC,IAAI,CAAC,OAAO,SAAS,CAAK,KAAK,KAAS,GAAG;GACzC,EAAO,eAAe,GAAG,EAAI;GAC7B;EACF;CACF;CAGF,OAAO;AACT"}
1
+ {"version":3,"file":"controller-DBLwJyvm.js","names":[],"sources":["../../../../shared/controller-utils/dist/index.js","../../src/controller/defaults.ts","../../src/author/defaults.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","import { DEFAULT_LAYOUT_LIMITS } from '../shared/layoutLimits.js';\nimport type { McpbChoice, McPopulatedBlankModel } from '../types.js';\n\nexport { DEFAULT_LAYOUT_LIMITS };\n\n/** Token authors must include exactly once in `template` (HTML). */\nexport const BLANK_TOKEN = '{{blank}}';\n\n/**\n * The values `normalize` fills in for fields an item leaves out. They carry no\n * content and no `id` or `element`: those come from the item config, and a\n * default here would stand in for a field the author never set.\n */\nexport default {\n model: {\n prompt: '',\n promptEnabled: true,\n interactionMode: 'populate_blank' as const,\n layoutProfile: '',\n choiceLayout: '',\n layoutProfilePresets: {},\n layoutLimits: { ...DEFAULT_LAYOUT_LIMITS },\n audioButtonSkin: null,\n audioButtonSkinsByLocale: {},\n sentenceHtml: '',\n template: '',\n choiceMode: 'text' as const,\n choices: [] as McpbChoice[],\n choiceGroupLabel: '',\n correctChoiceId: '',\n hasAudio: false,\n autoplayAudioEnabled: false,\n completeAudioEnabled: false,\n audioUrl: '',\n audioTranscript: '',\n locale: '',\n teacherInstructions: '',\n teacherInstructionsEnabled: true,\n },\n} satisfies { model: Omit<McPopulatedBlankModel, 'id' | 'element'> };\n","import controllerDefaults, { BLANK_TOKEN } from '../controller/defaults.js';\nimport type { McPopulatedBlankConfiguration, McPopulatedBlankModel } from '../types.js';\n\n/** The model a new item starts from: a valid question an author edits in place. */\nexport default {\n model: {\n ...controllerDefaults.model,\n prompt: '<p><strong>Demo prompt</strong></p>',\n template: `<p>The answer is ${BLANK_TOKEN}.</p>`,\n choices: [\n { id: 'c1', labelHtml: '<p>Option A</p>' },\n { id: 'c2', labelHtml: '<p>Option B</p>' },\n { id: 'c3', labelHtml: '<p>Option C</p>' },\n ],\n correctChoiceId: 'c2',\n },\n configuration: {\n prompt: {\n label: 'Prompt',\n settings: true,\n },\n },\n} satisfies {\n model: Omit<McPopulatedBlankModel, 'id' | 'element'>;\n configuration: McPopulatedBlankConfiguration;\n};\n","import { getShuffledChoices, lockChoices } from '@pie-element/shared-controller-utils';\nimport type { PieUpdateSession } from '@pie-element/shared-types';\nimport authorDefaults from '../author/defaults.js';\nimport defaults, { BLANK_TOKEN, DEFAULT_LAYOUT_LIMITS } from './defaults.js';\nimport type {\n McpbChoice,\n McpbCorrectness,\n McpbEnv,\n McPopulatedBlankConfiguration,\n McPopulatedBlankModel,\n McPopulatedBlankSession,\n} from '../types.js';\nimport { selectedChoiceId } from '../shared/session.js';\n\nconst isEmptyObject = (value: unknown): boolean =>\n !!value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.keys(value as Record<string, unknown>).length === 0;\n\nexport function countBlankTokens(template: string): number {\n if (!template) return 0;\n const re = /\\{\\{blank\\}\\}/g;\n return (template.match(re) || []).length;\n}\n\nexport const getCorrectness = (\n question: Partial<McPopulatedBlankModel>,\n session: McPopulatedBlankSession\n): McpbCorrectness => {\n const picked = selectedChoiceId(session);\n if (!picked) {\n return 'unanswered';\n }\n const correct = question?.correctChoiceId || '';\n if (picked === correct) {\n return 'correct';\n }\n return 'incorrect';\n};\n\n/** One blank, one key: an answered session scores 1 only when it picked the key. */\nexport const getPartialScore = (\n question: Partial<McPopulatedBlankModel>,\n session: McPopulatedBlankSession\n) => (getCorrectness(question, session) === 'correct' ? 1 : 0);\n\nexport const outcome = (\n question: McPopulatedBlankModel,\n session: McPopulatedBlankSession,\n env: McpbEnv\n) =>\n new Promise<{ score: number; empty: boolean; traceLog: string[] }>((resolve) => {\n if (!session || isEmptyObject(session)) {\n resolve({\n score: 0,\n empty: true,\n traceLog: ['Student did not select any answers. Score is 0.'],\n });\n return;\n }\n\n session = normalizeSession(session);\n const normalizedQuestion = normalize(question);\n const correctness = getCorrectness(normalizedQuestion, session);\n\n if (correctness === 'unanswered') {\n resolve({\n score: 0,\n empty: true,\n traceLog: ['Student did not select any answers. Score is 0.'],\n });\n return;\n }\n\n const score = getPartialScore(normalizedQuestion, session);\n const traceLog = [\n `Mode: ${env?.mode || 'unknown'}.`,\n `Student selected choice: ${selectedChoiceId(session)}.`,\n `Correct choice: ${normalizedQuestion.correctChoiceId || 'none'}.`,\n `Final score: ${score}.`,\n ];\n resolve({ score, empty: false, traceLog });\n });\n\nconst withDefaults = (\n base: Partial<McPopulatedBlankModel>,\n model: Partial<McPopulatedBlankModel> = {}\n) => ({\n ...base,\n ...model,\n layoutLimits: {\n ...DEFAULT_LAYOUT_LIMITS,\n ...(model?.layoutLimits || {}),\n },\n});\n\n/** The authoring starting point: a question that passes `validate()` as it stands. */\nexport const createDefaultModel = (model: Partial<McPopulatedBlankModel> = {}) =>\n withDefaults(authorDefaults.model, model);\n\n/**\n * What `model()` and `outcome()` read: the item's own fields, with neutral\n * values for the rest. Starter content here would reach a learner as the\n * item's prompt or choices.\n */\nexport const normalize = (question: Partial<McPopulatedBlankModel> = {}) => {\n const { shuffle, ...rest } = question || {};\n return {\n ...withDefaults(defaults.model, rest),\n // `lockChoiceOrder` alone decides the order, as in multiple-choice. Older items\n // spell an unlocked order `shuffle: true`, which counts only when it is unset.\n lockChoiceOrder:\n typeof rest.lockChoiceOrder === 'boolean' ? rest.lockChoiceOrder : shuffle !== true,\n };\n};\n\nexport const normalizeSession = (s: McPopulatedBlankSession): McPopulatedBlankSession => ({\n ...s,\n});\n\n/**\n * The fields delivery renders. An imported choice can carry more, such as a\n * correctness flag or feedback, which must not reach a learner's browser.\n */\nconst toDeliveryChoice = ({ id, labelHtml, imageUrl, imageAlt }: McpbChoice): McpbChoice => ({\n id,\n ...(labelHtml !== undefined ? { labelHtml } : {}),\n ...(imageUrl !== undefined ? { imageUrl } : {}),\n ...(imageAlt !== undefined ? { imageAlt } : {}),\n});\n\nexport const model = async (\n question: McPopulatedBlankModel,\n session: McPopulatedBlankSession | null,\n env: McpbEnv | null,\n updateSession?: PieUpdateSession\n) => {\n const safeSession: McPopulatedBlankSession = session || {};\n const safeEnv: McpbEnv = env || {};\n const normalizedQuestion = normalize(question);\n let orderedChoices = Array.isArray(normalizedQuestion.choices)\n ? [...normalizedQuestion.choices]\n : [];\n if (orderedChoices.length && !lockChoices(normalizedQuestion, safeSession, safeEnv)) {\n orderedChoices =\n (await getShuffledChoices(orderedChoices, safeSession, updateSession, 'id')) ??\n orderedChoices;\n }\n const choices = orderedChoices\n .filter((c): c is McpbChoice => !!c && typeof c === 'object')\n .map(toDeliveryChoice);\n\n const out: Record<string, unknown> = {\n prompt: normalizedQuestion.promptEnabled ? normalizedQuestion.prompt : null,\n interactionMode: normalizedQuestion.interactionMode || 'populate_blank',\n layoutProfile: normalizedQuestion.layoutProfile || '',\n choiceLayout: normalizedQuestion.choiceLayout || '',\n layoutProfilePresets:\n normalizedQuestion.layoutProfilePresets &&\n typeof normalizedQuestion.layoutProfilePresets === 'object'\n ? normalizedQuestion.layoutProfilePresets\n : {},\n layoutLimits: normalizedQuestion.layoutLimits || { ...DEFAULT_LAYOUT_LIMITS },\n audioButtonSkin:\n normalizedQuestion.audioButtonSkin && typeof normalizedQuestion.audioButtonSkin === 'object'\n ? normalizedQuestion.audioButtonSkin\n : null,\n audioButtonSkinsByLocale:\n normalizedQuestion.audioButtonSkinsByLocale &&\n typeof normalizedQuestion.audioButtonSkinsByLocale === 'object'\n ? normalizedQuestion.audioButtonSkinsByLocale\n : {},\n sentenceHtml: normalizedQuestion.sentenceHtml || null,\n template: normalizedQuestion.template,\n choiceMode: normalizedQuestion.choiceMode,\n choices,\n choiceGroupLabel: normalizedQuestion.choiceGroupLabel || '',\n hasAudio: normalizedQuestion.hasAudio,\n autoplayAudioEnabled: !!normalizedQuestion.autoplayAudioEnabled,\n completeAudioEnabled: !!normalizedQuestion.completeAudioEnabled,\n audioUrl: normalizedQuestion.hasAudio ? normalizedQuestion.audioUrl : null,\n audioTranscript: normalizedQuestion.hasAudio ? normalizedQuestion.audioTranscript : null,\n customType: normalizedQuestion.customType || '',\n useFeatureButtonAudio:\n typeof normalizedQuestion.useFeatureButtonAudio === 'boolean'\n ? normalizedQuestion.useFeatureButtonAudio\n : undefined,\n locale: normalizedQuestion.locale || '',\n language: normalizedQuestion.language || normalizedQuestion.locale || undefined,\n disabled: safeEnv.mode !== 'gather',\n mode: safeEnv.mode,\n };\n\n if (safeEnv.mode === 'evaluate') {\n const correctness = getCorrectness(normalizedQuestion, safeSession);\n out.correctness = correctness;\n out.responseCorrect = correctness === 'correct';\n out.correctChoiceId = normalizedQuestion.correctChoiceId;\n }\n\n if (safeEnv.role === 'instructor' && (safeEnv.mode === 'view' || safeEnv.mode === 'evaluate')) {\n out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled\n ? normalizedQuestion.teacherInstructions\n : null;\n } else {\n out.teacherInstructions = null;\n }\n\n return out;\n};\n\nexport const createCorrectResponseSession = (question: McPopulatedBlankModel, env: McpbEnv) => {\n return new Promise<McPopulatedBlankSession | null>((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n // The player sets `id` and `element` on the entry from the item config.\n resolve({\n id: '1',\n value: question?.correctChoiceId ? [question.correctChoiceId] : [],\n });\n } else {\n resolve(null);\n }\n });\n};\n\n/** Markup with its tags removed, keeping media an author can answer from. */\nconst getContent = (html?: string) =>\n (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '').trim();\n\n/**\n * Authoring errors in multiple-choice's shape: a message per field, and under\n * `choices` a message per choice keyed by its id.\n */\nexport const validate = (\n question: Partial<McPopulatedBlankModel> = {},\n config: McPopulatedBlankConfiguration = {}\n) => {\n const errors: Record<string, string | Record<string, string>> = {};\n const { minAnswerChoices = 2, maxAnswerChoices } = config;\n\n if ((question.promptEnabled || config.prompt?.required) && !getContent(question.prompt)) {\n errors.prompt = 'This field is required.';\n }\n\n if (config.teacherInstructions?.required && !getContent(question.teacherInstructions)) {\n errors.teacherInstructions = 'This field is required.';\n }\n\n const interactionMode = question.interactionMode || 'populate_blank';\n const template = question.template || '';\n const n = countBlankTokens(template);\n if (interactionMode === 'audio_mc_only') {\n if (n > 0) {\n errors.template = `Template cannot contain ${BLANK_TOKEN} in audio-only mode`;\n }\n } else if (interactionMode === 'populate_blank') {\n if (n === 0) {\n errors.template = `Template must contain exactly one ${BLANK_TOKEN} placeholder`;\n } else if (n > 1) {\n errors.template = `Template must contain only one ${BLANK_TOKEN} placeholder`;\n }\n } else {\n errors.interactionMode = 'Unknown interaction mode';\n }\n\n const choices = Array.isArray(question.choices) ? question.choices : [];\n if (choices.length < minAnswerChoices) {\n errors.answerChoices = `There should be at least ${minAnswerChoices} choices defined.`;\n } else if (maxAnswerChoices != null && choices.length > maxAnswerChoices) {\n errors.answerChoices = `No more than ${maxAnswerChoices} choices should be defined.`;\n }\n\n const mode = question.choiceMode || 'text';\n const choicesErrors: Record<string, string> = {};\n const seenIds = new Set<string>();\n const seenLabels = new Set<string>();\n choices.forEach((c, i) => {\n const key = c?.id || String(i);\n if (!c?.id) {\n choicesErrors[key] = 'Choice needs an id.';\n return;\n }\n // The session stores the picked id, so two choices sharing one cannot be told apart.\n if (seenIds.has(c.id)) {\n choicesErrors[key] = 'Choice id should be unique.';\n return;\n }\n seenIds.add(c.id);\n if (mode === 'text') {\n const label = getContent(c.labelHtml);\n if (!label) {\n choicesErrors[key] = 'Content should not be empty.';\n } else if (seenLabels.has(label)) {\n choicesErrors[key] = 'Content should be unique.';\n }\n seenLabels.add(label);\n } else if (!c.imageUrl?.trim()) {\n choicesErrors[key] = 'An image is required.';\n } else if (!c.imageAlt?.trim()) {\n choicesErrors[key] = 'Image alt text is required.';\n }\n });\n if (Object.keys(choicesErrors).length) {\n errors.choices = choicesErrors;\n }\n\n const correct = question.correctChoiceId;\n if (!correct) {\n errors.correctResponse = 'No correct response defined.';\n } else if (!choices.some((c) => c?.id === correct)) {\n errors.correctResponse = 'The correct response must be one of the choices.';\n }\n\n if (question.hasAudio) {\n const hasAudioUrl = !!question.audioUrl?.trim();\n if (!hasAudioUrl) {\n errors.audioUrl = 'A playable audio URL is required when audio is enabled';\n }\n }\n\n const limits = question?.layoutLimits;\n if (limits && typeof limits === 'object') {\n const numericLimitKeys = [\n 'blankStandaloneWidthRem',\n 'blankWideWidthRem',\n 'blankUnderlineWidthPx',\n 'blankUnderlineWideWidthPx',\n 'horizontalChoiceWidthPx',\n 'horizontalChoiceWidthVw',\n 'horizontalChoiceTileMinHeightRem',\n 'horizontalChoiceContentMinHeightRem',\n 'choiceImageMaxHeightRem',\n 'choiceImageMaxWidthRem',\n 'listenButtonSizePx',\n 'stimulusMinColumnPx',\n 'textMinColumnPx',\n 'legendMaxChars',\n 'choiceGroupGapRem',\n 'choiceRowGapRem',\n 'toggleButtonGapRem',\n 'horizontalChoiceRadioTopMarginRem',\n 'audioBlankTemplateMarginTopRem',\n 'audioBlankTemplateMarginBottomRem',\n 'audioInstructionsMaxWidthPx',\n 'stimulusGridColumnGapRem',\n 'stimulusGridRowGapRem',\n 'stimulusSentenceMarginTopRem',\n 'stimulusChoicesMarginTopRem',\n 'tokenGridColumnGapRem',\n 'tokenGridRowGapRem',\n 'tokenTemplateMarginTopRem',\n 'tokenInlineTokenGapRem',\n 'tokenChoicesMarginTopRem',\n 'inlineGridColumnGapRem',\n 'inlineGridRowGapRem',\n 'inlineTemplateMarginTopRem',\n 'inlineChoicesMarginTopRem',\n ] as const;\n for (const key of numericLimitKeys) {\n if (limits[key] === undefined || limits[key] === null) continue;\n const value = Number(limits[key]);\n if (!Number.isFinite(value) || value <= 0) {\n errors.layoutLimits = `${key} must be a positive number when provided`;\n break;\n }\n }\n }\n\n return errors;\n};\n"],"mappings":"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"}
@@ -118,8 +118,11 @@ var l = class extends s {
118
118
  };
119
119
  !i.waitTime && n && r && (i.waitTime = r - n), this.writeSession(i);
120
120
  }
121
- };
121
+ }, u = Object.freeze({
122
+ name: "@pie-element/mc-populated-blank",
123
+ version: "0.3.5"
124
+ });
122
125
  //#endregion
123
- export { l as default };
126
+ export { l as default, u as piePackage };
124
127
 
125
128
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#e","t","#t","e","n","#writeAudioTiming"],"sources":["../../../../../lib-svelte/delivery-events/dist/index2.js","../../../../../shared/player-events/dist/index.js","../../../../../lib-svelte/delivery-events/dist/index3.js","../../../src/delivery/index.ts"],"sourcesContent":["//#region src/write-session-in-place.ts\nfunction e(e) {\n\treturn !!e && typeof e == \"object\" && !Array.isArray(e);\n}\nfunction t(t, n) {\n\tif (t === n) return t;\n\tif (!e(t) || !e(n) || Object.isFrozen(t) || !Object.isExtensible(t)) return n;\n\ttry {\n\t\tfor (let e of Object.keys(t)) e in n || delete t[e];\n\t\tObject.assign(t, n);\n\t} catch {\n\t\treturn n;\n\t}\n\treturn t;\n}\n//#endregion\nexport { t as writeSessionInPlace };\n","//#region src/session-notifier.ts\nvar e = \"commitPendingSession\", t = /* @__PURE__ */ new WeakMap();\nfunction n(e, n) {\n\tlet r = t.get(e);\n\tr || (r = /* @__PURE__ */ new Set(), t.set(e, r)), r.add(n);\n}\nfunction r(t) {\n\treturn Object.getOwnPropertyDescriptor(t, e) !== void 0;\n}\nfunction i(t) {\n\tif (r(t)) return;\n\tlet n = t[e];\n\tObject.defineProperty(t, e, {\n\t\tvalue: () => {\n\t\t\tif (typeof n == \"function\") try {\n\t\t\t\tn.call(t);\n\t\t\t} catch {}\n\t\t\to(t);\n\t\t},\n\t\tconfigurable: !0,\n\t\tenumerable: !1,\n\t\twritable: !0\n\t});\n}\nfunction a(e, t, r = {}) {\n\tlet a = r.delayMs, o = typeof a == \"function\" ? a : () => a ?? 0, s = r.maxWaitMs, c = null, l = null, u = !1, d = () => {\n\t\tc !== null && (clearTimeout(c), c = null), l !== null && (clearTimeout(l), l = null);\n\t}, f = () => {\n\t\td(), u && (u = !1, t());\n\t}, p = {\n\t\tnotify() {\n\t\t\tu = !0, c !== null && clearTimeout(c), c = setTimeout(f, Math.max(0, o())), s !== void 0 && l === null && (l = setTimeout(f, Math.max(0, s)));\n\t\t},\n\t\tflush() {\n\t\t\tif (!u) {\n\t\t\t\td();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tf();\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(\"[session-notifier] a committed session-changed dispatch threw\", e);\n\t\t\t}\n\t\t}\n\t};\n\treturn n(e, p), i(e), p;\n}\nfunction o(e) {\n\tlet n = t.get(e);\n\tif (n) for (let e of [...n]) e.flush();\n}\n//#endregion\n//#region src/index.ts\nvar s = class e extends CustomEvent {\n\tstatic {\n\t\tthis.TYPE = \"model-set\";\n\t}\n\tconstructor(t, n, r) {\n\t\tsuper(e.TYPE, {\n\t\t\tbubbles: !0,\n\t\t\tcomposed: !0,\n\t\t\tdetail: {\n\t\t\t\tcomplete: n,\n\t\t\t\tcomponent: t,\n\t\t\t\thasModel: r\n\t\t\t}\n\t\t}), this.component = t, this.complete = n;\n\t}\n}, c = class e extends CustomEvent {\n\tstatic {\n\t\tthis.TYPE = \"session-changed\";\n\t}\n\tconstructor(t, n) {\n\t\tsuper(e.TYPE, {\n\t\t\tbubbles: !0,\n\t\t\tcomposed: !0,\n\t\t\tdetail: {\n\t\t\t\tcomplete: n,\n\t\t\t\tcomponent: t\n\t\t\t}\n\t\t}), this.component = t, this.complete = n;\n\t}\n};\n//#endregion\nexport { s as ModelSetEvent, e as SESSION_COMMIT_METHOD, c as SessionChangedEvent, a as createSessionNotifier, o as flushSessionNotifiers };\n","import { writeSessionInPlace as e } from \"./index2.js\";\nimport { ModelSetEvent as t, SessionChangedEvent as n } from \"@pie-element/shared-player-events\";\n//#region src/delivery-element.ts\nfunction r(r, i) {\n\tlet a = r.element;\n\tif (!a) throw TypeError(\"defineDeliveryElement: the component was not compiled with `customElement`\");\n\tclass o extends a {\n\t\t#e;\n\t\t#t;\n\t\tconstructor() {\n\t\t\tsuper(), this.onSessionChange = (e) => {\n\t\t\t\tthis.writeSession(e), this.dispatchSessionChanged();\n\t\t\t};\n\t\t}\n\t\tset model(e) {\n\t\t\tthis.#e = e, super.model = e, queueMicrotask(() => {\n\t\t\t\tthis.dispatchEvent(new t(this.tagName.toLowerCase(), this.isComplete(), this.#e !== void 0));\n\t\t\t});\n\t\t}\n\t\tget model() {\n\t\t\treturn this.#e;\n\t\t}\n\t\tset session(e) {\n\t\t\tthis.#t = e, super.session = e, this.dispatchSessionChanged();\n\t\t}\n\t\tget session() {\n\t\t\treturn this.#t;\n\t\t}\n\t\tisComplete() {\n\t\t\treturn i.isComplete(this.#e, this.#t);\n\t\t}\n\t\twriteSession(t) {\n\t\t\tthis.#t = e(this.#t, t), super.session = t;\n\t\t}\n\t\tdispatchSessionChanged() {\n\t\t\tthis.dispatchEvent(new n(this.tagName.toLowerCase(), this.isComplete()));\n\t\t}\n\t}\n\treturn o;\n}\n//#endregion\nexport { r as defineDeliveryElement };\n","import McPopulatedBlankComponent from './McPopulatedBlank.svelte';\nimport { defineDeliveryElement } from '@pie-lib/delivery-events-svelte';\nimport type { McPopulatedBlankModel, McPopulatedBlankSession } from '../types.js';\nimport { selectedChoiceId } from '../shared/session.js';\n\nconst DeliveryElement = defineDeliveryElement<McPopulatedBlankModel, McPopulatedBlankSession>(\n McPopulatedBlankComponent,\n { isComplete: (_model, session) => !!selectedChoiceId(session) }\n);\n\n/**\n * Audio the learner must hear to the end before a pick counts as complete,\n * whether autoplay or the learner started it.\n */\nfunction requiresAudioCompletion(model: McPopulatedBlankModel | undefined): boolean {\n return !!model?.completeAudioEnabled && !!model?.hasAudio;\n}\n\nclass McPopulatedBlankElement extends DeliveryElement {\n audioComplete = false;\n declare onAudioStarted: () => void;\n declare onAudioEnded: () => void;\n\n constructor() {\n super();\n this.onAudioStarted = () => {\n this.#writeAudioTiming({ audioStartTime: Date.now() });\n };\n this.onAudioEnded = () => {\n this.audioComplete = true;\n this.#writeAudioTiming({ audioEndTime: Date.now() });\n this.dispatchSessionChanged();\n };\n }\n\n set model(m: McPopulatedBlankModel | undefined) {\n // Autoplay re-fires only for a new `audioUrl`, so a re-set of the same\n // model must keep a finished playback counted.\n if (m?.audioUrl !== this.model?.audioUrl) this.audioComplete = false;\n super.model = m;\n }\n\n get model(): McPopulatedBlankModel | undefined {\n return super.model;\n }\n\n isComplete(): boolean {\n return super.isComplete() && (this.audioComplete || !requiresAudioCompletion(this.model));\n }\n\n /**\n * The first playback's timing, as multiple-choice's `updateSessionMetadata`\n * records it: a replay does not move either timestamp, and `waitTime` is set\n * once both exist. It reaches the component too, which builds the learner's\n * next pick from the session it renders.\n */\n #writeAudioTiming(timing: { audioStartTime?: number; audioEndTime?: number }) {\n const session: McPopulatedBlankSession = this.session || {};\n const audioStartTime = session.audioStartTime || timing.audioStartTime;\n const audioEndTime = session.audioEndTime || timing.audioEndTime;\n const next: McPopulatedBlankSession = {\n ...session,\n ...(audioStartTime ? { audioStartTime } : {}),\n ...(audioEndTime ? { audioEndTime } : {}),\n };\n if (!next.waitTime && audioStartTime && audioEndTime) {\n next.waitTime = audioEndTime - audioStartTime;\n }\n this.writeSession(next);\n }\n}\n\nexport default McPopulatedBlankElement;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#e","t","#t","e","n","#writeAudioTiming"],"sources":["../../../../../lib-svelte/delivery-events/dist/index2.js","../../../../../shared/player-events/dist/index.js","../../../../../lib-svelte/delivery-events/dist/index3.js","../../../src/delivery/index.ts"],"sourcesContent":["//#region src/write-session-in-place.ts\nfunction e(e) {\n\treturn !!e && typeof e == \"object\" && !Array.isArray(e);\n}\nfunction t(t, n) {\n\tif (t === n) return t;\n\tif (!e(t) || !e(n) || Object.isFrozen(t) || !Object.isExtensible(t)) return n;\n\ttry {\n\t\tfor (let e of Object.keys(t)) e in n || delete t[e];\n\t\tObject.assign(t, n);\n\t} catch {\n\t\treturn n;\n\t}\n\treturn t;\n}\n//#endregion\nexport { t as writeSessionInPlace };\n","//#region src/session-notifier.ts\nvar e = \"commitPendingSession\", t = /* @__PURE__ */ new WeakMap();\nfunction n(e, n) {\n\tlet r = t.get(e);\n\tr || (r = /* @__PURE__ */ new Set(), t.set(e, r)), r.add(n);\n}\nfunction r(t) {\n\treturn Object.getOwnPropertyDescriptor(t, e) !== void 0;\n}\nfunction i(t) {\n\tif (r(t)) return;\n\tlet n = t[e];\n\tObject.defineProperty(t, e, {\n\t\tvalue: () => {\n\t\t\tif (typeof n == \"function\") try {\n\t\t\t\tn.call(t);\n\t\t\t} catch {}\n\t\t\to(t);\n\t\t},\n\t\tconfigurable: !0,\n\t\tenumerable: !1,\n\t\twritable: !0\n\t});\n}\nfunction a(e, t, r = {}) {\n\tlet a = r.delayMs, o = typeof a == \"function\" ? a : () => a ?? 0, s = r.maxWaitMs, c = null, l = null, u = !1, d = () => {\n\t\tc !== null && (clearTimeout(c), c = null), l !== null && (clearTimeout(l), l = null);\n\t}, f = () => {\n\t\td(), u && (u = !1, t());\n\t}, p = {\n\t\tnotify() {\n\t\t\tu = !0, c !== null && clearTimeout(c), c = setTimeout(f, Math.max(0, o())), s !== void 0 && l === null && (l = setTimeout(f, Math.max(0, s)));\n\t\t},\n\t\tflush() {\n\t\t\tif (!u) {\n\t\t\t\td();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tf();\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(\"[session-notifier] a committed session-changed dispatch threw\", e);\n\t\t\t}\n\t\t}\n\t};\n\treturn n(e, p), i(e), p;\n}\nfunction o(e) {\n\tlet n = t.get(e);\n\tif (n) for (let e of [...n]) e.flush();\n}\n//#endregion\n//#region ../../../node_modules/.bun/@pie-players+pie-context@1.0.0-beta.3/node_modules/@pie-players/pie-context/dist/events.js\nfunction s(e, t, n) {\n\tlet r = t.ownerDocument?.defaultView?.Event ?? Event;\n\treturn Object.assign(new r(e, {\n\t\tbubbles: !0,\n\t\tcomposed: !0\n\t}), n);\n}\nvar c = /* @__PURE__ */ new WeakSet(), l = (e, t) => typeof t == \"object\" && !!t && e.has(t), u = class {\n\tconstructor(e, t, n, r) {\n\t\tlet i = s(\"context-request\", t, {\n\t\t\tcontext: e,\n\t\t\tcontextTarget: t,\n\t\t\tcallback: n,\n\t\t\tsubscribe: r ?? !1\n\t\t});\n\t\treturn c.add(i), i;\n\t}\n\tstatic [Symbol.hasInstance](e) {\n\t\treturn l(c, e);\n\t}\n}, d = (e) => {\n\tlet t = [];\n\tfor (let n of e) n.requestorRef.deref() && n.callbackRef.deref() && t.push(n);\n\treturn t;\n}, f = class {\n\thost;\n\tpendingByContext = /* @__PURE__ */ new Map();\n\tattached = !1;\n\tconstructor(e) {\n\t\tthis.host = e;\n\t}\n\tattach() {\n\t\tthis.attached || (this.attached = !0, this.host.addEventListener(\"context-request\", this.onContextRequest), this.host.addEventListener(\"context-provider\", this.onContextProvider));\n\t}\n\tdetach() {\n\t\tthis.attached && (this.attached = !1, this.host.removeEventListener(\"context-request\", this.onContextRequest), this.host.removeEventListener(\"context-provider\", this.onContextProvider), this.pendingByContext.clear());\n\t}\n\tonContextRequest = (e) => {\n\t\tif (e.cancelBubble) return;\n\t\tlet t = e;\n\t\tif (t.subscribe !== !0 || typeof t.callback != \"function\" || t.context === void 0) return;\n\t\tlet n = t.contextTarget ?? e.composedPath?.()[0];\n\t\tif (!n) return;\n\t\tlet r = this.pendingByContext.get(t.context);\n\t\tr || (r = {\n\t\t\tcallbacks: /* @__PURE__ */ new WeakMap(),\n\t\t\trequests: []\n\t\t}, this.pendingByContext.set(t.context, r));\n\t\tlet i = t.callback, a = r.callbacks.get(n);\n\t\ta || (a = /* @__PURE__ */ new WeakSet(), r.callbacks.set(n, a)), !a.has(i) && (a.add(i), r.requests.push({\n\t\t\trequestorRef: new WeakRef(n),\n\t\t\tcallbackRef: new WeakRef(i)\n\t\t}), r.requests = d(r.requests));\n\t};\n\tonContextProvider = (e) => {\n\t\tlet t = e.context;\n\t\tif (t === void 0) return;\n\t\tlet n = this.pendingByContext.get(t);\n\t\tif (!(!n || n.requests.length === 0)) {\n\t\t\tthis.pendingByContext.delete(t);\n\t\t\tfor (let e of n.requests) {\n\t\t\t\tlet n = e.requestorRef.deref(), r = e.callbackRef.deref();\n\t\t\t\t!n || !r || n.dispatchEvent(new u(t, n, r, !0));\n\t\t\t}\n\t\t}\n\t};\n}, p = Symbol.for(\"pie.context.documentRoot\");\nfunction m(e) {\n\tif (!e) return;\n\tlet t = e;\n\tif (t[p]) return;\n\tlet n = e.documentElement;\n\tif (!n) return;\n\tlet r = new f(n);\n\tr.attach(), t[p] = r;\n}\n//#endregion\n//#region ../../../node_modules/.bun/@pie-players+pie-context@1.0.0-beta.3/node_modules/@pie-players/pie-context/dist/consumer.js\nvar h = class {\n\thost;\n\tcontext;\n\tsubscribe;\n\tonValue;\n\tisConnected = !1;\n\tisDisconnected = !1;\n\tunsubscribe;\n\tcurrentValue;\n\tconstructor(e, t) {\n\t\tthis.host = e, this.context = t.context, this.subscribe = t.subscribe ?? !0, this.onValue = t.onValue;\n\t}\n\tconnect() {\n\t\tthis.isConnected || (this.isConnected = !0, this.isDisconnected = !1, this.subscribe && m(this.host.ownerDocument), this.requestValue());\n\t}\n\tdisconnect() {\n\t\tthis.isConnected && (this.isConnected = !1, this.isDisconnected = !0, this.unsubscribe?.(), this.unsubscribe = void 0);\n\t}\n\tget value() {\n\t\treturn this.currentValue;\n\t}\n\thandleValue = (e, t) => {\n\t\tif (this.isDisconnected) {\n\t\t\tt?.();\n\t\t\treturn;\n\t\t}\n\t\tt !== this.unsubscribe && (this.unsubscribe?.(), this.unsubscribe = t), !this.subscribe && this.unsubscribe && (this.unsubscribe(), this.unsubscribe = void 0), this.currentValue = e, this.onValue?.(e);\n\t};\n\trequestValue() {\n\t\tthis.host.dispatchEvent(new u(this.context, this.host, this.handleValue, this.subscribe));\n\t}\n}, g = (e, t, n) => {\n\tlet r = new h(e, {\n\t\tcontext: t,\n\t\tsubscribe: !0,\n\t\tonValue: n\n\t});\n\tr.connect();\n\tlet i = (e) => {\n\t\te.context === t && r.requestValue();\n\t};\n\treturn e.addEventListener(\"context-provider\", i), () => {\n\t\te.removeEventListener(\"context-provider\", i), r.disconnect();\n\t};\n}, _ = ((e) => e)(Symbol.for(\"pie.sessionCommit\")), v = 1500;\nfunction y(e) {\n\treturn typeof e == \"number\" && Number.isFinite(e) && e >= 0 ? e : v;\n}\nfunction b(e, t) {\n\treturn g(e, _, (e) => {\n\t\tt(y(e?.idleSaveDelayMs));\n\t});\n}\n//#endregion\n//#region src/index.ts\nvar x = class e extends CustomEvent {\n\tstatic {\n\t\tthis.TYPE = \"model-set\";\n\t}\n\tconstructor(t, n, r) {\n\t\tsuper(e.TYPE, {\n\t\t\tbubbles: !0,\n\t\t\tcomposed: !0,\n\t\t\tdetail: {\n\t\t\t\tcomplete: n,\n\t\t\t\tcomponent: t,\n\t\t\t\thasModel: r\n\t\t\t}\n\t\t}), this.component = t, this.complete = n;\n\t}\n}, S = class e extends CustomEvent {\n\tstatic {\n\t\tthis.TYPE = \"session-changed\";\n\t}\n\tconstructor(t, n) {\n\t\tsuper(e.TYPE, {\n\t\t\tbubbles: !0,\n\t\t\tcomposed: !0,\n\t\t\tdetail: {\n\t\t\t\tcomplete: n,\n\t\t\t\tcomponent: t\n\t\t\t}\n\t\t}), this.component = t, this.complete = n;\n\t}\n};\n//#endregion\nexport { v as DEFAULT_IDLE_SAVE_DELAY_MS, x as ModelSetEvent, e as SESSION_COMMIT_METHOD, S as SessionChangedEvent, a as createSessionNotifier, o as flushSessionNotifiers, y as resolveIdleSaveDelay, b as subscribeIdleSaveDelay };\n","import { writeSessionInPlace as e } from \"./index2.js\";\nimport { ModelSetEvent as t, SessionChangedEvent as n } from \"@pie-element/shared-player-events\";\n//#region src/delivery-element.ts\nfunction r(r, i) {\n\tlet a = r.element;\n\tif (!a) throw TypeError(\"defineDeliveryElement: the component was not compiled with `customElement`\");\n\tclass o extends a {\n\t\t#e;\n\t\t#t;\n\t\tconstructor() {\n\t\t\tsuper(), this.onSessionChange = (e) => {\n\t\t\t\tthis.writeSession(e), this.dispatchSessionChanged();\n\t\t\t};\n\t\t}\n\t\tset model(e) {\n\t\t\tthis.#e = e, super.model = e, queueMicrotask(() => {\n\t\t\t\tthis.dispatchEvent(new t(this.tagName.toLowerCase(), this.isComplete(), this.#e !== void 0));\n\t\t\t});\n\t\t}\n\t\tget model() {\n\t\t\treturn this.#e;\n\t\t}\n\t\tset session(e) {\n\t\t\tthis.#t = e, super.session = e, this.dispatchSessionChanged();\n\t\t}\n\t\tget session() {\n\t\t\treturn this.#t;\n\t\t}\n\t\tisComplete() {\n\t\t\treturn i.isComplete(this.#e, this.#t);\n\t\t}\n\t\twriteSession(t) {\n\t\t\tthis.#t = e(this.#t, t), super.session = t;\n\t\t}\n\t\tdispatchSessionChanged() {\n\t\t\tthis.dispatchEvent(new n(this.tagName.toLowerCase(), this.isComplete()));\n\t\t}\n\t}\n\treturn o;\n}\n//#endregion\nexport { r as defineDeliveryElement };\n","import McPopulatedBlankComponent from './McPopulatedBlank.svelte';\nimport { defineDeliveryElement } from '@pie-lib/delivery-events-svelte';\nimport type { McPopulatedBlankModel, McPopulatedBlankSession } from '../types.js';\nimport { selectedChoiceId } from '../shared/session.js';\n\nconst DeliveryElement = defineDeliveryElement<McPopulatedBlankModel, McPopulatedBlankSession>(\n McPopulatedBlankComponent,\n { isComplete: (_model, session) => !!selectedChoiceId(session) }\n);\n\n/**\n * Audio the learner must hear to the end before a pick counts as complete,\n * whether autoplay or the learner started it.\n */\nfunction requiresAudioCompletion(model: McPopulatedBlankModel | undefined): boolean {\n return !!model?.completeAudioEnabled && !!model?.hasAudio;\n}\n\nclass McPopulatedBlankElement extends DeliveryElement {\n audioComplete = false;\n declare onAudioStarted: () => void;\n declare onAudioEnded: () => void;\n\n constructor() {\n super();\n this.onAudioStarted = () => {\n this.#writeAudioTiming({ audioStartTime: Date.now() });\n };\n this.onAudioEnded = () => {\n this.audioComplete = true;\n this.#writeAudioTiming({ audioEndTime: Date.now() });\n this.dispatchSessionChanged();\n };\n }\n\n set model(m: McPopulatedBlankModel | undefined) {\n // Autoplay re-fires only for a new `audioUrl`, so a re-set of the same\n // model must keep a finished playback counted.\n if (m?.audioUrl !== this.model?.audioUrl) this.audioComplete = false;\n super.model = m;\n }\n\n get model(): McPopulatedBlankModel | undefined {\n return super.model;\n }\n\n isComplete(): boolean {\n return super.isComplete() && (this.audioComplete || !requiresAudioCompletion(this.model));\n }\n\n /**\n * The first playback's timing, as multiple-choice's `updateSessionMetadata`\n * records it: a replay does not move either timestamp, and `waitTime` is set\n * once both exist. It reaches the component too, which builds the learner's\n * next pick from the session it renders.\n */\n #writeAudioTiming(timing: { audioStartTime?: number; audioEndTime?: number }) {\n const session: McPopulatedBlankSession = this.session || {};\n const audioStartTime = session.audioStartTime || timing.audioStartTime;\n const audioEndTime = session.audioEndTime || timing.audioEndTime;\n const next: McPopulatedBlankSession = {\n ...session,\n ...(audioStartTime ? { audioStartTime } : {}),\n ...(audioEndTime ? { audioEndTime } : {}),\n };\n if (!next.waitTime && audioStartTime && audioEndTime) {\n next.waitTime = audioEndTime - audioStartTime;\n }\n this.writeSession(next);\n }\n}\n\nexport default McPopulatedBlankElement;\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { A as e, C as t, D as n, E as r, F as i, I as a, M as o, O as s, P as c, S as l, T as u, _ as d, d as f, g as p, h as m, j as h, m as g, n as _, t as v, u as y, v as b, w as x } from "../client-BOmFEbpb.js";
2
2
  import { n as S, r as C, t as w } from "../McPopulatedBlank-CJcV9lfT.js";
3
- import { n as T, o as E } from "../controller-Dv_mGlnz.js";
3
+ import { n as T, o as E } from "../controller-DBLwJyvm.js";
4
4
  //#region src/print/preparePrint.ts
5
5
  async function D(e, t) {
6
6
  let n = {
@@ -107,8 +107,11 @@ v(N, {
107
107
  }, [], []);
108
108
  //#endregion
109
109
  //#region src/print/index.ts
110
- var P = N.element;
110
+ var P = N.element, F = Object.freeze({
111
+ name: "@pie-element/mc-populated-blank",
112
+ version: "0.3.5"
113
+ });
111
114
  //#endregion
112
- export { P as default };
115
+ export { P as default, F as piePackage };
113
116
 
114
117
  //# sourceMappingURL=index.js.map
@@ -7823,7 +7823,7 @@ function b(e) {
7823
7823
  b: 0,
7824
7824
  a: 0
7825
7825
  };
7826
- let n = S().get(t);
7826
+ let n = S$1().get(t);
7827
7827
  if (n) return C(n);
7828
7828
  if (t.startsWith("#")) return C(t.slice(1));
7829
7829
  let r = /^([a-z]+)\(([^()]*)\)$/.exec(t);
@@ -7870,9 +7870,9 @@ function b(e) {
7870
7870
  a: l
7871
7871
  };
7872
7872
  }
7873
- var x;
7874
- function S() {
7875
- return x ?? (x = new Map("aliceblue:f0f8ff antiquewhite:faebd7 aqua:00ffff aquamarine:7fffd4 azure:f0ffff beige:f5f5dc bisque:ffe4c4 black:000000 blanchedalmond:ffebcd blue:0000ff blueviolet:8a2be2 brown:a52a2a burlywood:deb887 cadetblue:5f9ea0 chartreuse:7fff00 chocolate:d2691e coral:ff7f50 cornflowerblue:6495ed cornsilk:fff8dc crimson:dc143c cyan:00ffff darkblue:00008b darkcyan:008b8b darkgoldenrod:b8860b darkgray:a9a9a9 darkgreen:006400 darkgrey:a9a9a9 darkkhaki:bdb76b darkmagenta:8b008b darkolivegreen:556b2f darkorange:ff8c00 darkorchid:9932cc darkred:8b0000 darksalmon:e9967a darkseagreen:8fbc8f darkslateblue:483d8b darkslategray:2f4f4f darkslategrey:2f4f4f darkturquoise:00ced1 darkviolet:9400d3 deeppink:ff1493 deepskyblue:00bfff dimgray:696969 dimgrey:696969 dodgerblue:1e90ff firebrick:b22222 floralwhite:fffaf0 forestgreen:228b22 fuchsia:ff00ff gainsboro:dcdcdc ghostwhite:f8f8ff gold:ffd700 goldenrod:daa520 gray:808080 green:008000 greenyellow:adff2f grey:808080 honeydew:f0fff0 hotpink:ff69b4 indianred:cd5c5c indigo:4b0082 ivory:fffff0 khaki:f0e68c lavender:e6e6fa lavenderblush:fff0f5 lawngreen:7cfc00 lemonchiffon:fffacd lightblue:add8e6 lightcoral:f08080 lightcyan:e0ffff lightgoldenrodyellow:fafad2 lightgray:d3d3d3 lightgreen:90ee90 lightgrey:d3d3d3 lightpink:ffb6c1 lightsalmon:ffa07a lightseagreen:20b2aa lightskyblue:87cefa lightslategray:778899 lightslategrey:778899 lightsteelblue:b0c4de lightyellow:ffffe0 lime:00ff00 limegreen:32cd32 linen:faf0e6 magenta:ff00ff maroon:800000 mediumaquamarine:66cdaa mediumblue:0000cd mediumorchid:ba55d3 mediumpurple:9370db mediumseagreen:3cb371 mediumslateblue:7b68ee mediumspringgreen:00fa9a mediumturquoise:48d1cc mediumvioletred:c71585 midnightblue:191970 mintcream:f5fffa mistyrose:ffe4e1 moccasin:ffe4b5 navajowhite:ffdead navy:000080 oldlace:fdf5e6 olive:808000 olivedrab:6b8e23 orange:ffa500 orangered:ff4500 orchid:da70d6 palegoldenrod:eee8aa palegreen:98fb98 paleturquoise:afeeee palevioletred:db7093 papayawhip:ffefd5 peachpuff:ffdab9 peru:cd853f pink:ffc0cb plum:dda0dd powderblue:b0e0e6 purple:800080 rebeccapurple:663399 red:ff0000 rosybrown:bc8f8f royalblue:4169e1 saddlebrown:8b4513 salmon:fa8072 sandybrown:f4a460 seagreen:2e8b57 seashell:fff5ee sienna:a0522d silver:c0c0c0 skyblue:87ceeb slateblue:6a5acd slategray:708090 slategrey:708090 snow:fffafa springgreen:00ff7f steelblue:4682b4 tan:d2b48c teal:008080 thistle:d8bfd8 tomato:ff6347 turquoise:40e0d0 violet:ee82ee wheat:f5deb3 white:ffffff whitesmoke:f5f5f5 yellow:ffff00 yellowgreen:9acd32".split(" ").map((e) => e.split(":")))), x;
7873
+ var x$1;
7874
+ function S$1() {
7875
+ return x$1 ?? (x$1 = new Map("aliceblue:f0f8ff antiquewhite:faebd7 aqua:00ffff aquamarine:7fffd4 azure:f0ffff beige:f5f5dc bisque:ffe4c4 black:000000 blanchedalmond:ffebcd blue:0000ff blueviolet:8a2be2 brown:a52a2a burlywood:deb887 cadetblue:5f9ea0 chartreuse:7fff00 chocolate:d2691e coral:ff7f50 cornflowerblue:6495ed cornsilk:fff8dc crimson:dc143c cyan:00ffff darkblue:00008b darkcyan:008b8b darkgoldenrod:b8860b darkgray:a9a9a9 darkgreen:006400 darkgrey:a9a9a9 darkkhaki:bdb76b darkmagenta:8b008b darkolivegreen:556b2f darkorange:ff8c00 darkorchid:9932cc darkred:8b0000 darksalmon:e9967a darkseagreen:8fbc8f darkslateblue:483d8b darkslategray:2f4f4f darkslategrey:2f4f4f darkturquoise:00ced1 darkviolet:9400d3 deeppink:ff1493 deepskyblue:00bfff dimgray:696969 dimgrey:696969 dodgerblue:1e90ff firebrick:b22222 floralwhite:fffaf0 forestgreen:228b22 fuchsia:ff00ff gainsboro:dcdcdc ghostwhite:f8f8ff gold:ffd700 goldenrod:daa520 gray:808080 green:008000 greenyellow:adff2f grey:808080 honeydew:f0fff0 hotpink:ff69b4 indianred:cd5c5c indigo:4b0082 ivory:fffff0 khaki:f0e68c lavender:e6e6fa lavenderblush:fff0f5 lawngreen:7cfc00 lemonchiffon:fffacd lightblue:add8e6 lightcoral:f08080 lightcyan:e0ffff lightgoldenrodyellow:fafad2 lightgray:d3d3d3 lightgreen:90ee90 lightgrey:d3d3d3 lightpink:ffb6c1 lightsalmon:ffa07a lightseagreen:20b2aa lightskyblue:87cefa lightslategray:778899 lightslategrey:778899 lightsteelblue:b0c4de lightyellow:ffffe0 lime:00ff00 limegreen:32cd32 linen:faf0e6 magenta:ff00ff maroon:800000 mediumaquamarine:66cdaa mediumblue:0000cd mediumorchid:ba55d3 mediumpurple:9370db mediumseagreen:3cb371 mediumslateblue:7b68ee mediumspringgreen:00fa9a mediumturquoise:48d1cc mediumvioletred:c71585 midnightblue:191970 mintcream:f5fffa mistyrose:ffe4e1 moccasin:ffe4b5 navajowhite:ffdead navy:000080 oldlace:fdf5e6 olive:808000 olivedrab:6b8e23 orange:ffa500 orangered:ff4500 orchid:da70d6 palegoldenrod:eee8aa palegreen:98fb98 paleturquoise:afeeee palevioletred:db7093 papayawhip:ffefd5 peachpuff:ffdab9 peru:cd853f pink:ffc0cb plum:dda0dd powderblue:b0e0e6 purple:800080 rebeccapurple:663399 red:ff0000 rosybrown:bc8f8f royalblue:4169e1 saddlebrown:8b4513 salmon:fa8072 sandybrown:f4a460 seagreen:2e8b57 seashell:fff5ee sienna:a0522d silver:c0c0c0 skyblue:87ceeb slateblue:6a5acd slategray:708090 slategrey:708090 snow:fffafa springgreen:00ff7f steelblue:4682b4 tan:d2b48c teal:008080 thistle:d8bfd8 tomato:ff6347 turquoise:40e0d0 violet:ee82ee wheat:f5deb3 white:ffffff whitesmoke:f5f5f5 yellow:ffff00 yellowgreen:9acd32".split(" ").map((e) => e.split(":")))), x$1;
7876
7876
  }
7877
7877
  function C(e) {
7878
7878
  if (!/^(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/.test(e)) return null;
@@ -12370,7 +12370,7 @@ function t(t, n) {
12370
12370
  //#region ../../shared/player-events/dist/index.js
12371
12371
  var _e;
12372
12372
  var _e2;
12373
- var s = (_e = class e extends CustomEvent {
12373
+ var x = (_e = class e extends CustomEvent {
12374
12374
  constructor(t, n, r) {
12375
12375
  super(e.TYPE, {
12376
12376
  bubbles: !0,
@@ -12383,7 +12383,7 @@ var s = (_e = class e extends CustomEvent {
12383
12383
  }), this.component = t, this.complete = n;
12384
12384
  }
12385
12385
  }, _e.TYPE = "model-set", _e);
12386
- var c = (_e2 = class e extends CustomEvent {
12386
+ var S = (_e2 = class e extends CustomEvent {
12387
12387
  constructor(t, n) {
12388
12388
  super(e.TYPE, {
12389
12389
  bubbles: !0,
@@ -12411,7 +12411,7 @@ function r(r, i) {
12411
12411
  }
12412
12412
  set model(e) {
12413
12413
  _classPrivateFieldSet2(_e, this, e), super.model = e, queueMicrotask(() => {
12414
- this.dispatchEvent(new s(this.tagName.toLowerCase(), this.isComplete(), _classPrivateFieldGet2(_e, this) !== void 0));
12414
+ this.dispatchEvent(new x(this.tagName.toLowerCase(), this.isComplete(), _classPrivateFieldGet2(_e, this) !== void 0));
12415
12415
  });
12416
12416
  }
12417
12417
  get model() {
@@ -12430,7 +12430,7 @@ function r(r, i) {
12430
12430
  _classPrivateFieldSet2(_t, this, t(_classPrivateFieldGet2(_t, this), t$3)), super.session = t$3;
12431
12431
  }
12432
12432
  dispatchSessionChanged() {
12433
- this.dispatchEvent(new c(this.tagName.toLowerCase(), this.isComplete()));
12433
+ this.dispatchEvent(new S(this.tagName.toLowerCase(), this.isComplete()));
12434
12434
  }
12435
12435
  }
12436
12436
  return o;