@citolab/qti-components 8.0.2 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/cdn/index.global.js +1 -1
  2. package/cdn/index.js +751 -813
  3. package/dist/base.d.ts +63 -6
  4. package/dist/base.js +7 -1
  5. package/dist/{chunk-CK7AV2UJ.js → chunk-43XBJ47H.js} +3 -3
  6. package/dist/{chunk-7V4HJFHW.js → chunk-6ITD55QE.js} +2 -2
  7. package/dist/{chunk-HQESINXH.js → chunk-7D3NKAGZ.js} +137 -3
  8. package/dist/chunk-7D3NKAGZ.js.map +1 -0
  9. package/dist/{chunk-HBSWJC3B.js → chunk-CRLXQZFA.js} +595 -260
  10. package/dist/chunk-CRLXQZFA.js.map +1 -0
  11. package/dist/{chunk-IJD53WY6.js → chunk-HDEG6BAZ.js} +15 -15
  12. package/dist/{chunk-IJD53WY6.js.map → chunk-HDEG6BAZ.js.map} +1 -1
  13. package/dist/{chunk-RSVRS5AK.js → chunk-LMGJOBJA.js} +4 -4
  14. package/dist/{chunk-ZII6M3SN.js → chunk-M7K5C522.js} +2 -2
  15. package/dist/{chunk-J5SQLKNP.js → chunk-OIUK5BFU.js} +22 -6
  16. package/dist/{chunk-J5SQLKNP.js.map → chunk-OIUK5BFU.js.map} +1 -1
  17. package/dist/{chunk-VAE6HBKR.js → chunk-OVBG223G.js} +88 -62
  18. package/dist/chunk-OVBG223G.js.map +1 -0
  19. package/dist/{chunk-TWPXES3S.js → chunk-Q6ZK4LRH.js} +30 -6
  20. package/dist/chunk-Q6ZK4LRH.js.map +1 -0
  21. package/dist/chunk-TLTYXWW7.js +7 -0
  22. package/dist/chunk-TLTYXWW7.js.map +1 -0
  23. package/dist/{chunk-NJQHUUTN.js → chunk-U7YCXDYC.js} +2 -2
  24. package/dist/{chunk-BQFWCZFH.js → chunk-UC3T3NI3.js} +2 -2
  25. package/dist/{chunk-7AVWRQIS.js → chunk-YLRMOX6C.js} +63 -77
  26. package/dist/chunk-YLRMOX6C.js.map +1 -0
  27. package/dist/{computed.types.d-DpKIYO6e.d.ts → computed.types.d-CJarqC6s.d.ts} +12 -0
  28. package/dist/corrections.d.ts +14 -4
  29. package/dist/corrections.js +8 -8
  30. package/dist/elements.d.ts +1 -2
  31. package/dist/elements.js +4 -4
  32. package/dist/index.d.ts +7 -7
  33. package/dist/index.js +30 -14
  34. package/dist/interactions.js +3 -3
  35. package/dist/item.css +451 -532
  36. package/dist/item.d.ts +1 -1
  37. package/dist/item.js +5 -5
  38. package/dist/loader.d.ts +1 -1
  39. package/dist/loader.js +2 -2
  40. package/dist/processing.d.ts +64 -14
  41. package/dist/processing.js +10 -2
  42. package/dist/qti-components-jsx.d.ts +8139 -7358
  43. package/dist/{qti-condition-expression.d-aMvBvpc6.d.ts → qti-condition-expression.d-CYwXOUb1.d.ts} +1 -1
  44. package/dist/{qti-expression.d-BietT_8b.d.ts → qti-expression.d-B_-m9emA.d.ts} +11 -1
  45. package/dist/{qti-feedback.d-DZbad0RN.d.ts → qti-feedback.d-B1EIF-fU.d.ts} +1 -0
  46. package/dist/{qti-test.d-_YAlhDbt.d.ts → qti-test.d-C6lCEMRB.d.ts} +38 -3
  47. package/dist/test.d.ts +5 -5
  48. package/dist/test.js +8 -6
  49. package/dist/transformers.d.ts +1 -1
  50. package/dist/transformers.js +1 -1
  51. package/package.json +9 -9
  52. package/dist/chunk-7AVWRQIS.js.map +0 -1
  53. package/dist/chunk-G6GFQDVN.js +0 -7
  54. package/dist/chunk-G6GFQDVN.js.map +0 -1
  55. package/dist/chunk-HBSWJC3B.js.map +0 -1
  56. package/dist/chunk-HQESINXH.js.map +0 -1
  57. package/dist/chunk-TWPXES3S.js.map +0 -1
  58. package/dist/chunk-VAE6HBKR.js.map +0 -1
  59. /package/dist/{chunk-CK7AV2UJ.js.map → chunk-43XBJ47H.js.map} +0 -0
  60. /package/dist/{chunk-7V4HJFHW.js.map → chunk-6ITD55QE.js.map} +0 -0
  61. /package/dist/{chunk-RSVRS5AK.js.map → chunk-LMGJOBJA.js.map} +0 -0
  62. /package/dist/{chunk-ZII6M3SN.js.map → chunk-M7K5C522.js.map} +0 -0
  63. /package/dist/{chunk-NJQHUUTN.js.map → chunk-U7YCXDYC.js.map} +0 -0
  64. /package/dist/{chunk-BQFWCZFH.js.map → chunk-UC3T3NI3.js.map} +0 -0
@@ -521,13 +521,13 @@ var qtiTransformItem = () => {
521
521
  var qtiTransformManifest = () => {
522
522
  let xmlFragment;
523
523
  const api = {
524
+ // Awaits loadXML so a failed fetch (network, CORS, non-2xx) rejects this
525
+ // promise. Callers own the failure: they can render it, retry it or let it
526
+ // bubble — rather than it escaping as an unhandled rejection while their
527
+ // own `await` never settles.
524
528
  async load(uri, signal) {
525
- return new Promise((resolve) => {
526
- loadXML(uri, signal).then((xml) => {
527
- xmlFragment = xml;
528
- return resolve(api);
529
- });
530
- });
529
+ xmlFragment = await loadXML(uri, signal);
530
+ return api;
531
531
  },
532
532
  parse(xmlString) {
533
533
  xmlFragment = parseXML(xmlString);
@@ -655,15 +655,15 @@ var qtiTransformTest = () => {
655
655
  let xmlFragment;
656
656
  let xmlUri = "";
657
657
  const api = {
658
+ // Awaits loadXML so a failed fetch (network, CORS, non-2xx) rejects this
659
+ // promise. Callers own the failure: they can render it, retry it or let it
660
+ // bubble — rather than it escaping as an unhandled rejection while their
661
+ // own `await` never settles.
658
662
  async load(uri, signal) {
659
- return new Promise((resolve, _) => {
660
- loadXML(uri, signal).then((xml) => {
661
- xmlFragment = xml;
662
- xmlUri = uri;
663
- api.path(uri.substring(0, uri.lastIndexOf("/")));
664
- return resolve(api);
665
- });
666
- });
663
+ xmlFragment = await loadXML(uri, signal);
664
+ xmlUri = uri;
665
+ api.path(uri.substring(0, uri.lastIndexOf("/")));
666
+ return api;
667
667
  },
668
668
  parse(xmlString) {
669
669
  xmlFragment = parseXML(xmlString);
@@ -716,4 +716,4 @@ export {
716
716
  qtiTransformManifest,
717
717
  qtiTransformTest
718
718
  };
719
- //# sourceMappingURL=chunk-IJD53WY6.js.map
719
+ //# sourceMappingURL=chunk-HDEG6BAZ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../qti-transformers/src/item/cdata.ts","../../qti-transformers/src/item/configure-pci.ts","../../qti-transformers/src/item/extend.ts","../../qti-transformers/src/item/pci-hooks.ts","../../qti-transformers/src/shared/prng.ts","../../qti-transformers/src/shared/shuffle.ts","../../qti-transformers/src/item/shuffle-interactions.ts","../../qti-transformers/src/item/stylesheets.ts","../../qti-transformers/src/shared/missing-seed-warning.ts","../../qti-transformers/src/shared/xml.ts","../../qti-transformers/src/qti-transform-item.ts","../../qti-transformers/src/qti-transform-manifest.ts","../../qti-transformers/src/test/items.ts","../../qti-transformers/src/test/shuffle-sections.ts","../../qti-transformers/src/qti-transform-test.ts"],"sourcesContent":["export function convertCDATAtoComment(xmlFragment: DocumentFragment) {\n const cdataElements = xmlFragment.querySelectorAll('qti-custom-operator[class=\"js.org\"] > qti-base-value');\n cdataElements.forEach(element => {\n const commentText = document.createComment(element.textContent);\n element.replaceChild(commentText, element.firstChild);\n });\n}\n","// Type definition for module resolution config\nexport interface ModuleResolutionConfig {\n waitSeconds?: number;\n context?: string;\n catchError?: boolean;\n urlArgs?: string;\n paths: {\n [key: string]: string | string[];\n };\n shim?: {\n [key: string]: {\n deps?: string[]; // Array of dependencies\n exports?: string; // The global variable to use as the module's value\n };\n };\n}\n\nexport async function configurePci(\n xmlFragment: XMLDocument,\n baseUrl: string,\n getModuleResolutionConfig: (baseUrl: string, fileUrl: string) => Promise<ModuleResolutionConfig>,\n selector = 'qti-portable-custom-interaction'\n): Promise<void> {\n const customInteractionTypeIdentifiers: string[] = [];\n const portableCustomInteractions = xmlFragment.querySelectorAll(selector);\n\n // Avoid fetching module resolution configs for items that do not contain any PCIs.\n // This prevents unnecessary network requests when configurePci() is called for every item.\n if (portableCustomInteractions.length === 0) {\n return;\n }\n\n // Lazily load (and cache) the default module resolution configs.\n let moduleResolutionConfig: ModuleResolutionConfig | null = null;\n let moduleResolutionFallbackConfig: ModuleResolutionConfig | null = null;\n const ensureDefaultModuleResolutionConfigs = async () => {\n if (moduleResolutionConfig === null) {\n try {\n moduleResolutionConfig = await getModuleResolutionConfig(baseUrl, '/modules/module_resolution.js');\n } catch {\n moduleResolutionConfig = null;\n }\n }\n if (moduleResolutionFallbackConfig === null) {\n try {\n moduleResolutionFallbackConfig = await getModuleResolutionConfig(\n baseUrl,\n '/modules/fallback_module_resolution.js'\n );\n } catch {\n moduleResolutionFallbackConfig = null;\n }\n }\n };\n\n for (const interaction of Array.from(portableCustomInteractions)) {\n // set data-base-url\n interaction.setAttribute('data-base-url', baseUrl);\n\n let customInteractionTypeIdentifier = interaction.getAttribute('custom-interaction-type-identifier');\n if (customInteractionTypeIdentifier && customInteractionTypeIdentifiers.includes(customInteractionTypeIdentifier)) {\n customInteractionTypeIdentifier = customInteractionTypeIdentifier + customInteractionTypeIdentifiers.length;\n interaction.setAttribute('custom-interaction-type-identifier', customInteractionTypeIdentifier);\n customInteractionTypeIdentifiers.push(customInteractionTypeIdentifier);\n }\n if (customInteractionTypeIdentifier) {\n customInteractionTypeIdentifiers.push(customInteractionTypeIdentifier);\n }\n\n // Check if qti-interaction-modules already exists\n let modulesElement = interaction.querySelector('qti-interaction-modules');\n\n // If it exists and has primary-configuration, handle that format\n if (modulesElement && modulesElement.getAttribute('primary-configuration')) {\n await ensureDefaultModuleResolutionConfigs();\n const primaryConfigPath = modulesElement.getAttribute('primary-configuration');\n if (primaryConfigPath) {\n try {\n // Load the primary configuration\n const primaryConfig = await getModuleResolutionConfig(baseUrl, `/${primaryConfigPath}`);\n\n // Get existing module elements that only have id attributes\n const existingModules = Array.from(modulesElement.querySelectorAll('qti-interaction-module'));\n\n // Update existing modules with paths from config\n for (const moduleEl of existingModules) {\n const moduleId = moduleEl.getAttribute('id');\n if (moduleId && primaryConfig.paths && primaryConfig.paths[moduleId]) {\n const primaryPath = primaryConfig.paths[moduleId];\n const primaryPathString = Array.isArray(primaryPath) ? primaryPath[0] : primaryPath;\n moduleEl.setAttribute('primary-path', primaryPathString);\n\n // Check for fallback path\n if (\n moduleResolutionFallbackConfig &&\n moduleResolutionFallbackConfig.paths &&\n moduleResolutionFallbackConfig.paths[moduleId]\n ) {\n const fallbackPath = moduleResolutionFallbackConfig.paths[moduleId];\n if (Array.isArray(fallbackPath)) {\n moduleEl.setAttribute('fallback-path', fallbackPath[0]);\n } else {\n moduleEl.setAttribute('fallback-path', fallbackPath);\n }\n }\n }\n }\n\n // Add any additional modules from primary config that aren't already present\n if (primaryConfig.paths) {\n for (const moduleId in primaryConfig.paths) {\n const existingModule = modulesElement.querySelector(`qti-interaction-module[id=\"${moduleId}\"]`);\n if (!existingModule) {\n const newModuleElement = xmlFragment.createElement('qti-interaction-module');\n newModuleElement.setAttribute('id', moduleId);\n const primaryPathString = Array.isArray(primaryConfig.paths[moduleId])\n ? primaryConfig.paths[moduleId][0]\n : primaryConfig.paths[moduleId];\n newModuleElement.setAttribute('primary-path', primaryPathString);\n\n // Check for fallback path\n if (\n moduleResolutionFallbackConfig &&\n moduleResolutionFallbackConfig.paths &&\n moduleResolutionFallbackConfig.paths[moduleId]\n ) {\n const fallbackPath = moduleResolutionFallbackConfig.paths[moduleId];\n if (Array.isArray(fallbackPath)) {\n newModuleElement.setAttribute('fallback-path', fallbackPath[0]);\n } else {\n newModuleElement.setAttribute('fallback-path', fallbackPath);\n }\n }\n\n modulesElement.appendChild(newModuleElement);\n }\n }\n }\n\n // Apply urlArgs if present in config\n if (primaryConfig.urlArgs) {\n modulesElement.setAttribute('url-args', primaryConfig.urlArgs);\n }\n } catch (error) {\n console.warn(`Failed to load primary configuration: ${primaryConfigPath}`, error);\n }\n }\n } else {\n // Original logic for when there's no existing qti-interaction-modules or no primary-configuration\n await ensureDefaultModuleResolutionConfigs();\n if (moduleResolutionConfig) {\n // Create qti-interaction-modules if it doesn't exist\n if (interaction.querySelector('qti-interaction-modules') === null) {\n modulesElement = xmlFragment.createElement('qti-interaction-modules');\n interaction.appendChild(modulesElement);\n } else {\n modulesElement = interaction.querySelector('qti-interaction-modules');\n }\n\n for (const module in moduleResolutionConfig.paths) {\n const path = moduleResolutionConfig.paths[module];\n let fallbackPath: string | string[] = '';\n\n if (\n moduleResolutionFallbackConfig &&\n moduleResolutionFallbackConfig.paths &&\n moduleResolutionFallbackConfig.paths[module]\n ) {\n fallbackPath = moduleResolutionFallbackConfig.paths[module];\n }\n\n const primaryArray = Array.isArray(path) ? path : [path];\n const fallbackPathArray = Array.isArray(fallbackPath) ? fallbackPath : [fallbackPath];\n\n // create an array with primary and fallback paths.\n const paths = primaryArray.map((primaryPath, i) => {\n const fallbackPath = fallbackPathArray.length > i ? fallbackPathArray[i] : '';\n return {\n primaryPath,\n fallbackPath\n };\n });\n\n // check if all fallbackPath elements are in the array: paths, otherwise add\n for (const fallbackPath of fallbackPathArray) {\n if (!paths.some(p => p.fallbackPath === fallbackPath)) {\n paths.push({\n primaryPath: primaryArray.length > 0 ? primaryArray[0] : fallbackPath,\n fallbackPath\n });\n }\n }\n\n // add the paths to the qti-interaction-modules\n for (const path of paths) {\n const moduleElement = xmlFragment.createElement('qti-interaction-module');\n if (path.fallbackPath) {\n moduleElement.setAttribute('fallback-path', path.fallbackPath);\n }\n moduleElement.setAttribute('id', module);\n moduleElement.setAttribute('primary-path', path.primaryPath);\n\n if (modulesElement) {\n modulesElement.appendChild(moduleElement);\n }\n }\n }\n }\n }\n }\n}\n","// Function to extend elements with a specific tag name by adding an extension suffix\n// Uses localName matching to handle namespaced XML documents where querySelectorAll may not work\nexport function extendElementName(xmlFragment: XMLDocument, tagName: string, extension: string) {\n // Use getElementsByTagName with '*' to find all elements, then filter by localName\n // This works correctly for both namespaced and non-namespaced XML documents\n const allElements = Array.from(xmlFragment.getElementsByTagName('*'));\n const matchingElements = allElements.filter(\n element => element.localName === tagName || element.localName?.toLowerCase() === tagName.toLowerCase()\n );\n\n matchingElements.forEach(element => {\n const newTagName = `${tagName}-${extension}`;\n const newElement = createElementWithNewTagName(element, newTagName);\n element.replaceWith(newElement);\n });\n}\n\n// Function to extend any element with a specific class pattern (e.g., \"extend:suffix\")\nexport function extendElementsWithClass(xmlFragment: XMLDocument, classNamePattern: string) {\n xmlFragment.querySelectorAll('*').forEach(element => {\n const classList = element.classList;\n if (classList) {\n classList.forEach(className => {\n if (className.startsWith(`${classNamePattern}:`)) {\n const suffix = className.slice(`${classNamePattern}:`.length);\n const newTagName = `${element.nodeName}-${suffix}`;\n const newElement = createElementWithNewTagName(element, newTagName);\n element.replaceWith(newElement);\n }\n });\n }\n });\n}\n\n// Helper function to create a new element with a new tag name and copy attributes and children\nfunction createElementWithNewTagName(element: Element, newTagName: string) {\n // create Elements on the ownerDocument which is usually the XMLDocument\n const newElement = element.ownerDocument.createElement(newTagName);\n // Copy attributes\n for (const attr of element.attributes) {\n newElement.setAttribute(attr.name, attr.value);\n }\n // Copy child nodes\n while (element.firstChild) {\n newElement.appendChild(element.firstChild);\n }\n return newElement;\n}\n","export function pciHooks(xmlFragment: XMLDocument, uri: string): void {\n const attributes = ['hook', 'module'];\n const documentPath = uri.substring(0, uri.lastIndexOf('/'));\n for (const attribute of attributes) {\n const srcAttributes = xmlFragment.querySelectorAll('[' + attribute + ']');\n srcAttributes.forEach(node => {\n const srcValue = node.getAttribute(attribute)!;\n if (!srcValue.startsWith('data:') && !srcValue.startsWith('http')) {\n // Just paste the relative path of the src location after the documentrootPath\n // old pcis can have a .js, new pci's don't\n node.setAttribute('base-url', uri);\n node.setAttribute('module', documentPath + '/' + encodeURI(srcValue + (srcValue.endsWith('.js') ? '' : '.js')));\n }\n });\n }\n}\n","export interface SeededRandomSource {\n random(): number;\n}\n\n/**\n * Small, dependency-free seeded PRNG used for deterministic QTI shuffling\n * (sections and interactions). It intentionally exposes a `random()` method,\n * matching the TC39 seeded-random proposal shape so this can be swapped when a\n * standard implementation is available.\n *\n * xmur3-style seed hashing feeds mulberry32 (fast 32-bit generator).\n */\nexport class SeededRandom implements SeededRandomSource {\n #state: number;\n\n constructor(seed: Uint8Array) {\n if (!(seed instanceof Uint8Array)) {\n throw new TypeError('SeededRandom seed must be a Uint8Array.');\n }\n if (seed.length > 32) {\n throw new RangeError('SeededRandom seed must be 32 bytes or less.');\n }\n\n const paddedSeed = new Uint8Array(32);\n paddedSeed.set(seed, 32 - seed.length);\n this.#state = hashBytes(paddedSeed);\n }\n\n static fromSeed(seed: Uint8Array): SeededRandom {\n if (seed.length !== 32) {\n throw new RangeError('SeededRandom.fromSeed requires a 32-byte seed.');\n }\n return new SeededRandom(seed);\n }\n\n static fromFixed(byte: number): SeededRandom {\n if (typeof byte !== 'number') {\n throw new TypeError('SeededRandom.fromFixed requires a number.');\n }\n if (!Number.isInteger(byte) || byte < 0 || byte > 255) {\n throw new RangeError('SeededRandom.fromFixed requires an integer from 0 to 255.');\n }\n\n const seed = new Uint8Array(32);\n seed[31] = byte;\n return new SeededRandom(seed);\n }\n\n static fromSeedKey(seedKey: string | number): SeededRandom {\n const random = new SeededRandom(new Uint8Array());\n random.#state = hashString(`${seedKey}`);\n return random;\n }\n\n random(): number {\n this.#state |= 0;\n this.#state = (this.#state + 0x6d2b79f5) | 0;\n let t = Math.imul(this.#state ^ (this.#state >>> 15), 1 | this.#state);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n }\n}\n\nexport function createSeededRandom(seedKey: string | number): SeededRandomSource {\n return SeededRandom.fromSeedKey(seedKey);\n}\n\nfunction hashString(seedKey: string): number {\n let h = 1779033703 ^ seedKey.length;\n for (let i = 0; i < seedKey.length; i++) {\n h = Math.imul(h ^ seedKey.charCodeAt(i), 3432918353);\n h = (h << 13) | (h >>> 19);\n }\n return finalizeHash(h);\n}\n\nfunction hashBytes(seed: Uint8Array): number {\n let h = 1779033703 ^ seed.length;\n for (let i = 0; i < seed.length; i++) {\n h = Math.imul(h ^ seed[i], 3432918353);\n h = (h << 13) | (h >>> 19);\n }\n return finalizeHash(h);\n}\n\nfunction finalizeHash(hash: number): number {\n let h = Math.imul(hash ^ (hash >>> 16), 2246822507);\n h = Math.imul(h ^ (h >>> 13), 3266489909);\n return (h ^ (h >>> 16)) >>> 0;\n}\n","import type { SeededRandomSource } from './prng';\n\nexport type RandomSource = SeededRandomSource | (() => number);\n\nconst randomValue = (source: RandomSource): number => (typeof source === 'function' ? source() : source.random());\n\n/**\n * Fisher-Yates over non-fixed units only. Fixed units keep their authored slot\n * index while movable units are shuffled into the remaining slots.\n */\nexport function shuffleKeepingFixed<T extends { fixed: boolean }>(units: T[], random: RandomSource): T[] {\n const movable = units.filter(unit => !unit.fixed);\n\n for (let i = movable.length - 1; i > 0; i--) {\n const j = Math.floor(randomValue(random) * (i + 1));\n [movable[i], movable[j]] = [movable[j], movable[i]];\n }\n\n let movableIndex = 0;\n return units.map(unit => (unit.fixed ? unit : movable[movableIndex++]));\n}\n","import { createSeededRandom } from '../shared/prng';\nimport { shuffleKeepingFixed } from '../shared/shuffle';\n\nfunction getShuffleQuerySelectorByTagName(tagName: string): string | string[] | null {\n switch (tagName) {\n case 'qti-choice-interaction':\n case 'qti-order-interaction':\n return 'qti-simple-choice';\n case 'qti-inline-choice-interaction':\n return 'qti-inline-choice';\n case 'qti-match-interaction':\n return [\n 'qti-simple-match-set:first-of-type qti-simple-associable-choice',\n 'qti-simple-match-set:last-of-type > qti-simple-associable-choice'\n ];\n case 'qti-gap-match-interaction':\n return 'qti-gap-text';\n case 'qti-associate-interaction':\n return 'qti-simple-associable-choice';\n default:\n return null;\n }\n}\n\n/**\n * Shuffles the choices inside every interaction marked `shuffle=\"true\"`. When\n * `seed` is supplied, shuffling is deterministic and reproducible across\n * reloads; each interaction/group draws its own stream so they are independent\n * and stable even if siblings change. When `seed` is null/undefined, falls back\n * to Math.random.\n */\nexport function shuffleInteractions(xmlFragment: XMLDocument, seed?: string | number | null): void {\n const shuffleElements = xmlFragment.querySelectorAll(`[shuffle=\"true\"]`);\n const shuffleInteractions = Array.from(shuffleElements).filter(e =>\n e.tagName?.toLowerCase().endsWith('-interaction')\n );\n\n const seeded = seed != null && `${seed}`.length > 0;\n const itemId = xmlFragment.querySelector?.('qti-assessment-item')?.getAttribute('identifier') ?? '';\n\n let interactionIndex = 0;\n for (const shuffleInteraction of shuffleInteractions) {\n const responseId = shuffleInteraction.getAttribute('response-identifier') ?? `${interactionIndex}`;\n interactionIndex++;\n const query = getShuffleQuerySelectorByTagName(shuffleInteraction.tagName.toLowerCase());\n const queries = Array.isArray(query) ? query : [query];\n let shuffledAny = false;\n\n let queryIndex = 0;\n for (const q of queries) {\n if (!q) continue;\n const random = seeded ? createSeededRandom(`${seed}:${itemId}:${responseId}:${queryIndex}`) : Math.random;\n queryIndex++;\n const choices = (Array.from(shuffleInteraction.querySelectorAll(q)) as HTMLElement[]).filter(\n choice => choice.parentElement !== null\n );\n\n if (choices.filter(choice => choice.getAttribute('fixed') !== 'true').length <= 1) {\n console.warn('Shuffling is not possible with fewer than 2 non-fixed elements.');\n continue;\n }\n\n const parent = choices[0].parentElement;\n if (!parent || choices.some(choice => choice.parentElement !== parent)) {\n console.warn('Shuffling is not possible when choices do not share the same parent element.');\n continue;\n }\n\n const anchor = choices[choices.length - 1].nextSibling;\n const orderedChoices = shuffleKeepingFixed(\n choices.map(choice => ({\n element: choice,\n fixed: choice.getAttribute('fixed') === 'true'\n })),\n random\n );\n\n for (const choice of choices) {\n choice.remove();\n }\n for (const { element } of orderedChoices) {\n parent.insertBefore(element, anchor);\n }\n\n shuffledAny = true;\n }\n\n if (shuffledAny) {\n shuffleInteraction.removeAttribute('shuffle');\n }\n }\n}\n","export function stripStyleSheets(xmlFragment: DocumentFragment) {\n // remove qti-stylesheet tag\n xmlFragment.querySelectorAll('qti-stylesheet').forEach(stylesheet => stylesheet.remove());\n}\n","/**\n * The missing-seed warning is advice about configuration, not about the document being\n * transformed — say it once per session, shared by the item and test transformers since\n * both are pointing at the same missing `QTI_CONTEXT.seed`.\n *\n * Deliberately not re-exported from the package entry: `resetMissingSeedWarning` exists so\n * tests can restore the once-per-session state, not as public API.\n */\nlet missingSeedWarned = false;\n\nexport function warnMissingSeed(scope: string, fallbackSeed: string): void {\n if (missingSeedWarned) return;\n missingSeedWarned = true;\n console.warn(`[${scope}] No QTI_CONTEXT.seed provided; using \"${fallbackSeed}\" as deterministic fallback seed.`);\n}\n\nexport function resetMissingSeedWarning(): void {\n missingSeedWarned = false;\n}\n","export function loadXML(url: string, signal?: AbortSignal): Promise<XMLDocument | null> {\n return fetch(url, { signal })\n .then(response => {\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n return response.text();\n })\n .then(text => {\n const parser = new DOMParser();\n return parser.parseFromString(text, 'text/xml');\n })\n .catch(error => {\n if (error.name === 'AbortError') {\n throw error;\n }\n throw new Error(`Failed to load XML: ${error.message}`);\n });\n}\n\nexport function parseXML(xmlDocument: string) {\n const parser = new DOMParser();\n const xmlFragment = parser.parseFromString(xmlDocument, 'text/xml');\n return xmlFragment;\n}\n\n// Function to strip unsupported namespaces (qti) from the nodes sent to the browser\nfunction stripNamespaces(node: Node, doc: Document, registry?: CustomElementRegistry): Node {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n let newEl: Element;\n\n if (\n !el.namespaceURI ||\n el.namespaceURI.startsWith('http://www.imsglobal.org/xsd/qti/') ||\n el.namespaceURI.startsWith('http://www.imsglobal.org/xsd/imsqti')\n ) {\n newEl = registry\n ? doc.createElement(el.localName, { customElementRegistry: registry })\n : doc.createElement(el.localName);\n } else {\n newEl = doc.createElementNS(el.namespaceURI, el.tagName);\n }\n\n for (let i = 0; i < el.attributes.length; i++) {\n const attr = el.attributes[i];\n newEl.setAttribute(attr.localName, attr.value);\n }\n for (let i = 0; i < el.childNodes.length; i++) {\n newEl.appendChild(stripNamespaces(el.childNodes[i], doc, registry));\n }\n return newEl;\n }\n return node.cloneNode(false);\n}\n\nexport function toHTML(xmlFragment: Document, registry?: CustomElementRegistry): DocumentFragment {\n const fragment = document.createDocumentFragment();\n for (let i = 0; i < xmlFragment.childNodes.length; i++) {\n fragment.appendChild(stripNamespaces(xmlFragment.childNodes[i], document, registry));\n }\n return fragment;\n}\n\n// Updates src and href attributes with the base location\n// Uses querySelectorAll('*') to find all elements, then checks attributes manually\n// This handles namespaced XML documents where attribute selectors like [href] may not work\n// Note: primary-path is NOT processed here because it is resolved separately\n// by the PCI component using data-base-url in the iframe's module resolution\nexport function setLocation(xmlFragment: DocumentFragment, location: string) {\n if (!location.endsWith('/')) {\n location += '/';\n }\n\n // querySelectorAll('*') finds all descendant elements regardless of namespace\n const allElements = xmlFragment.querySelectorAll('*');\n\n allElements.forEach(el => {\n // Check each attribute we care about\n // src, srcset and poster are URLDType or xs:anyURI everywhere they're used, so we care about them\n // Note: primary-path is excluded - PCI handles module path resolution via data-base-url\n for (const attr of ['src', 'href', 'srcset', 'poster'] as const) {\n const attrValue = el.getAttribute(attr)?.trim();\n\n if (attrValue && !/^(data:|https?:|blob:)/.test(attrValue)) {\n const newValue = location + encodeURI(attrValue);\n el.setAttribute(attr, newValue);\n }\n }\n });\n}\n","/**\n * Browser based QTI-XML to HTML transformer.\n * Returns an object with methods to load, parse, transform and serialize QTI XML items.\n * @returns An object with methods to load, parse, transform and serialize QTI XML items.\n * @example\n * const qtiTransformer = qtiTransformItem();\n * await qtiTransformer.load('path/to/xml/file.xml');\n * qtiTransformer.path('/assessmentItem/itemBody');\n * const html = qtiTransformer.html();\n * const xml = qtiTransformer.xml();\n * const htmldoc = qtiTransformer.htmldoc();\n * const xmldoc = qtiTransformer.xmldoc();\n *\n * qtiTransformItem().parse(storyXML).html()\n */\nimport { convertCDATAtoComment } from './item/cdata';\nimport { configurePci, type ModuleResolutionConfig } from './item/configure-pci';\nimport { extendElementName, extendElementsWithClass } from './item/extend';\nimport { pciHooks } from './item/pci-hooks';\nimport { shuffleInteractions } from './item/shuffle-interactions';\nimport { stripStyleSheets } from './item/stylesheets';\nimport { warnMissingSeed } from './shared/missing-seed-warning';\nimport { loadXML, parseXML, setLocation, toHTML } from './shared/xml';\n\nexport type { ModuleResolutionConfig };\n\nexport type transformItemApi = {\n load: (uri: string, signal?: AbortSignal) => Promise<transformItemApi>;\n parse: (xmlString: string) => transformItemApi;\n path: (location: string) => transformItemApi;\n fn: (fn: (xmlFragment: XMLDocument) => void) => transformItemApi;\n pciHooks: (uri: string) => transformItemApi;\n configurePci: (\n baseUrl: string,\n getModuleResolutionConfig: (baseUrl: string, fileUrl: string) => Promise<ModuleResolutionConfig>,\n selector?: string\n ) => Promise<transformItemApi>;\n extendElementName: (elementName: string, extend: string) => transformItemApi;\n extendElementsWithClass: (param?: string) => transformItemApi;\n customInteraction: (baseRef: string, baseItem: string) => transformItemApi;\n convertCDATAtoComment: () => transformItemApi;\n shuffleInteractions: (seed?: string | number | null) => transformItemApi;\n stripStyleSheets: () => transformItemApi;\n html: () => string;\n xml: () => string;\n htmlDoc: (registry?: CustomElementRegistry) => DocumentFragment;\n xmlDoc: () => XMLDocument;\n};\n\nexport const qtiTransformItem = () => {\n let xmlFragment: XMLDocument;\n let xmlUri = '';\n\n const api: transformItemApi = {\n load(uri: string, signal?: AbortSignal) {\n xmlUri = uri;\n return loadXML(uri, signal).then(xml => {\n xmlFragment = xml;\n return api;\n });\n },\n parse(xmlString: string): typeof api {\n xmlFragment = parseXML(xmlString);\n xmlUri = '';\n return api;\n },\n path: (location: string): typeof api => {\n setLocation(xmlFragment, location);\n xmlUri = null;\n return api;\n },\n fn(fn: (xmlFragment: XMLDocument) => void): typeof api {\n fn(xmlFragment);\n return api;\n },\n pciHooks(uri: string): typeof api {\n pciHooks(xmlFragment, uri);\n return api;\n },\n async configurePci(\n baseUrl: string,\n getModuleResolutionConfig: (baseUrl: string, fileUrl: string) => Promise<ModuleResolutionConfig>,\n selector = 'qti-portable-custom-interaction'\n ): Promise<typeof api> {\n await configurePci(xmlFragment, baseUrl, getModuleResolutionConfig, selector);\n return api;\n },\n shuffleInteractions(seed?: string | number | null): typeof api {\n const normalizedSeed = typeof seed === 'string' ? seed.trim() : seed;\n\n if (normalizedSeed === null || normalizedSeed === undefined || normalizedSeed === '') {\n const fallbackSeed = xmlUri || 'default-item-seed';\n warnMissingSeed('qtiTransformItem', fallbackSeed);\n shuffleInteractions(xmlFragment, fallbackSeed);\n return api;\n }\n\n shuffleInteractions(xmlFragment, normalizedSeed);\n return api;\n },\n extendElementName: (tagName: string, extension: string): typeof api => {\n extendElementName(xmlFragment, tagName, extension);\n return api;\n },\n extendElementsWithClass: (param: string = 'extend'): typeof api => {\n extendElementsWithClass(xmlFragment, param);\n return api;\n },\n customInteraction(baseRef: string, baseItem: string): typeof api {\n const qtiCustomInteraction = xmlFragment.querySelector('qti-custom-interaction');\n const qtiCustomInteractionObject = qtiCustomInteraction.querySelector('object');\n\n qtiCustomInteraction.setAttribute('data-base-ref', baseRef);\n qtiCustomInteraction.setAttribute('data-base-item', baseRef + baseItem);\n qtiCustomInteraction.setAttribute('data', qtiCustomInteractionObject.getAttribute('data'));\n qtiCustomInteraction.setAttribute('width', qtiCustomInteractionObject.getAttribute('width'));\n qtiCustomInteraction.setAttribute('height', qtiCustomInteractionObject.getAttribute('height'));\n\n qtiCustomInteraction.removeChild(qtiCustomInteractionObject);\n return api;\n },\n convertCDATAtoComment(): typeof api {\n convertCDATAtoComment(xmlFragment);\n return api;\n },\n stripStyleSheets(): typeof api {\n stripStyleSheets(xmlFragment);\n return api;\n },\n html() {\n if (xmlUri !== null) {\n setLocation(xmlFragment, xmlUri.substring(0, xmlUri.lastIndexOf('/')));\n }\n return new XMLSerializer().serializeToString(toHTML(xmlFragment));\n },\n xml(): string {\n return new XMLSerializer().serializeToString(xmlFragment);\n },\n htmlDoc(registry?: CustomElementRegistry) {\n if (xmlUri !== null) {\n setLocation(xmlFragment, xmlUri.substring(0, xmlUri.lastIndexOf('/')));\n }\n return toHTML(xmlFragment, registry);\n },\n xmlDoc(): XMLDocument {\n return xmlFragment; // new XMLSerializer().serializeToString(xmlFragment);\n }\n };\n return api;\n};\n","import { loadXML, parseXML } from './shared/xml';\n\nexport const qtiTransformManifest = (): {\n load: (uri: string, signal?: AbortSignal) => Promise<typeof api>;\n assessmentTest: () => { href: string; identifier: string };\n} => {\n let xmlFragment: XMLDocument;\n\n const api = {\n async load(uri: string, signal: AbortSignal) {\n return new Promise<typeof api>(resolve => {\n loadXML(uri, signal).then(xml => {\n xmlFragment = xml;\n return resolve(api);\n });\n });\n },\n parse(xmlString: string) {\n xmlFragment = parseXML(xmlString);\n },\n assessmentTest() {\n const el = xmlFragment.querySelector('resource[type=\"imsqti_test_xmlv3p0\"]');\n return { href: el.getAttribute('href'), identifier: el.getAttribute('identifier') };\n }\n };\n return api;\n};\n","export function itemsFromTest(xmlFragment: DocumentFragment) {\n const items: { identifier: string; href: string; category: string }[] = [];\n xmlFragment.querySelectorAll('qti-assessment-item-ref').forEach(el => {\n const identifier = el.getAttribute('identifier');\n const href = el.getAttribute('href');\n const category = el.getAttribute('category');\n items.push({ identifier, href, category });\n });\n return items;\n}\n","import { createSeededRandom } from '../shared/prng';\nimport { shuffleKeepingFixed } from '../shared/shuffle';\n\nconst isLocal = (el: Element, ...names: string[]): boolean =>\n names.includes(el.localName) || names.includes(el.localName?.toLowerCase?.());\n\nconst isItemRef = (el: Element): boolean => isLocal(el, 'qti-assessment-item-ref', 'assessmentitemref');\n\nconst isSection = (el: Element): boolean => isLocal(el, 'qti-assessment-section', 'assessmentsection');\n\nconst attrIsTrue = (el: Element, ...names: string[]): boolean =>\n names.some(n => {\n const v = (el.getAttribute(n) ?? '').trim().toLowerCase();\n return v === 'true' || v === '1';\n });\n\nconst attrIsFalse = (el: Element, ...names: string[]): boolean =>\n names.some(n => {\n const v = (el.getAttribute(n) ?? '').trim().toLowerCase();\n return v === 'false' || v === '0';\n });\n\nconst isOrdering = (el: Element): boolean => isLocal(el, 'qti-ordering', 'ordering');\n\nconst isSelection = (el: Element): boolean => isLocal(el, 'qti-selection', 'selection');\n\nconst shuffleOrderingElements = (section: Element): Element[] =>\n Array.from(section.children).filter(child => isOrdering(child) && attrIsTrue(child, 'shuffle'));\n\nconst rngFor = (seed: string | number, key: string) => createSeededRandom(`${seed}:${key}`);\n\n/** A movable unit in a section: one or more sibling nodes that move together. */\ntype Unit = { nodes: Element[]; fixed: boolean };\n\n/**\n * The item refs of a subsection, in document order, plus the element children\n * that consuming the wrapper would discard.\n *\n * Descendant subsections are collected transitively: `orderSection` returns\n * early for a section without <qti-ordering>, so a wrapper nested inside a\n * non-shuffling subsection is never consumed there and has to be gathered here.\n */\nfunction collectItemRefsDeep(section: Element): { itemRefs: Element[]; dropped: string[] } {\n const itemRefs: Element[] = [];\n const dropped: string[] = [];\n\n const walk = (el: Element): void => {\n for (const child of Array.from(el.children)) {\n if (isItemRef(child)) {\n itemRefs.push(child);\n } else if (isSection(child)) {\n walk(child);\n } else if (!isOrdering(child) && !isSelection(child)) {\n // Ordering/selection directives are transform config; anything else\n // (a rubric block, say) is authored content that the lift loses.\n dropped.push(child.localName);\n }\n }\n };\n walk(section);\n\n return { itemRefs, dropped };\n}\n\nfunction warnDroppedChildren(section: Element, dropped: string[]): void {\n if (dropped.length === 0) return;\n const identifier = section.getAttribute('identifier') ?? '(no identifier)';\n console.warn(\n `[qtiTransformTest] Subsection \"${identifier}\" is consumed by section ordering; ` +\n `discarding its ${[...new Set(dropped)].join(', ')}.`\n );\n}\n\n/**\n * Seed-deterministic QTI section shuffling.\n *\n * Reorders the children of every <qti-assessment-section> that carries a\n * <qti-ordering shuffle=\"true\">, following QTI 3.0 ordering rules:\n *\n * - Depth-first: a subsection orders its own children before the parent\n * reorders it.\n * - Item refs with fixed=\"true\" stay in their authored position; the other\n * units shuffle around them.\n * - A subsection is a grouping device, not a delivered section: its item refs\n * are lifted into the parent and the wrapper is consumed, just as the\n * <qti-ordering> directive itself is. Nothing nested survives into the DOM,\n * so the player only ever sees the authored top-level sections.\n * - By default (QTI 3.0 defaults keep-together to true) a subsection travels as\n * one unit: its items stay contiguous and keep their authored order, while\n * the parent's remaining items shuffle around the block. Add fixed=\"true\" to\n * pin the block itself to its authored slot.\n * - Only an explicit keep-together=\"false\" dissolves a subsection: its\n * (already internally ordered) item refs are poured into the parent pool and\n * interleaved with the parent's own units.\n *\n * The same `seed` always produces the same order, so a restarted session keeps\n * its sequence. Each section draws from its own PRNG stream (seed + section id)\n * so sibling sections are independent and stable.\n */\nfunction orderSection(section: Element, seed: string | number): void {\n // Depth-first: let every child section order itself first.\n for (const child of Array.from(section.children)) {\n if (isSection(child)) {\n orderSection(child, seed);\n }\n }\n\n const orderingEls = shuffleOrderingElements(section);\n if (orderingEls.length === 0) {\n return;\n }\n\n // Build the pool of movable units in document order. Config/structural\n // children (qti-ordering, qti-selection, rubric-block, …) are left in place.\n const units: Unit[] = [];\n const toDetach: Element[] = [];\n\n for (const child of Array.from(section.children)) {\n if (isItemRef(child)) {\n units.push({ nodes: [child], fixed: attrIsTrue(child, 'fixed') });\n toDetach.push(child);\n } else if (isSection(child)) {\n // Internal order is already settled: the depth-first pass above shuffled\n // this subsection if it carried its own <qti-ordering shuffle=\"true\">, and\n // left the authored order alone otherwise.\n const { itemRefs, dropped } = collectItemRefsDeep(child);\n warnDroppedChildren(child, dropped);\n\n if (attrIsFalse(child, 'keep-together', 'keeptogether')) {\n // Explicitly dissolved: the items join the parent pool individually and\n // interleave with the parent's own items.\n for (const itemRef of itemRefs) {\n units.push({ nodes: [itemRef], fixed: attrIsTrue(itemRef, 'fixed') });\n }\n } else if (itemRefs.length > 0) {\n // One multi-node unit, so the items stay contiguous and keep their order\n // wherever the block lands.\n units.push({ nodes: itemRefs, fixed: attrIsTrue(child, 'fixed') });\n }\n\n toDetach.push(child); // the grouping wrapper is consumed\n }\n }\n\n // Where the reordered units go back in: after the last node we took out, so\n // any trailing structural children (a rubric block, say) keep their position.\n // Captured before detaching, and never itself a detached node.\n const anchor = toDetach.length > 0 ? toDetach[toDetach.length - 1].nextSibling : null;\n\n const sectionKey = section.getAttribute('identifier') ?? '';\n // A no-op for 0 or 1 movable units, but the re-insertion below still has to\n // run so consumed wrappers are lifted rather than left in the DOM.\n const ordered = shuffleKeepingFixed(units, rngFor(seed, sectionKey));\n\n for (const el of toDetach) {\n el.remove();\n }\n for (const unit of ordered) {\n for (const node of unit.nodes) {\n section.insertBefore(node, anchor);\n }\n }\n\n // Ordering directives are consumed by this transform step.\n for (const orderingEl of orderingEls) {\n orderingEl.remove();\n }\n}\n\n/** Whether anything in `doc` asks to be shuffled at all. */\nexport function hasShuffleOrdering(doc: XMLDocument): boolean {\n return Array.from(doc.getElementsByTagName('*')).some(el => isOrdering(el) && attrIsTrue(el, 'shuffle'));\n}\n\n/**\n * Mutates `doc` in place: shuffles every section flagged with\n * <qti-ordering shuffle=\"true\">, deterministically from `seed`.\n */\nexport function shuffleSectionsOrdering(doc: XMLDocument, seed: string | number): void {\n const sections = Array.from(doc.getElementsByTagName('*')).filter(el => isSection(el));\n // Kick off recursion only for top-level sections; orderSection handles nesting\n // depth-first, so descendants are not double-processed.\n for (const section of sections) {\n // The snapshot above can contain wrappers that an ancestor has since\n // consumed. A detached element has no parentNode; a document element still\n // has one (the document), so this does not skip a section-rooted document.\n if (!section.parentNode) continue;\n\n const parent = section.parentElement;\n if (parent && isSection(parent)) continue;\n\n orderSection(section, seed);\n }\n}\n","/**\n * Returns an object with methods to load, parse and transform QTI tests.\n * @returns An object with methods to load, parse and transform QTI tests.\n * @example\n * const qtiTransformer = qtiTransformTest();\n * await qtiTransformer.load('https://example.com/test.xml');\n * const items = qtiTransformer.items();\n * const html = qtiTransformer.html();\n * const xml = qtiTransformer.xml();\n */\n\nimport { warnMissingSeed } from './shared/missing-seed-warning';\nimport { loadXML, parseXML, setLocation, toHTML } from './shared/xml';\nimport { itemsFromTest } from './test/items';\nimport { hasShuffleOrdering, shuffleSectionsOrdering } from './test/shuffle-sections';\n\nexport type transformTestApi = {\n load: (uri: string, signal?: AbortSignal) => Promise<transformTestApi>;\n parse: (xmlString: string) => transformTestApi;\n path: (location: string) => transformTestApi;\n fn: (fn: (xmlFragment: XMLDocument) => void) => transformTestApi;\n /**\n * Deterministically shuffle the children of every section that carries a\n * <qti-ordering shuffle=\"true\">, honoring fixed items and nested\n * keep-together / visible rules. The same seed reproduces the same order.\n */\n shuffleOrdering: (seed?: string | number | null) => transformTestApi;\n items: () => { identifier: string; href: string; category: string }[];\n html: () => string;\n xml: () => string;\n htmlDoc: (registry?: CustomElementRegistry) => DocumentFragment;\n xmlDoc: () => XMLDocument;\n};\n\nexport const qtiTransformTest = (): transformTestApi => {\n let xmlFragment: XMLDocument;\n let xmlUri = '';\n\n const api: transformTestApi = {\n async load(uri, signal) {\n return new Promise<transformTestApi>((resolve, _) => {\n loadXML(uri, signal).then(xml => {\n xmlFragment = xml;\n xmlUri = uri;\n\n api.path(uri.substring(0, uri.lastIndexOf('/')));\n return resolve(api);\n });\n });\n },\n parse(xmlString: string) {\n xmlFragment = parseXML(xmlString);\n xmlUri = '';\n return api;\n },\n path: (location: string): typeof api => {\n setLocation(xmlFragment, location);\n return api;\n },\n fn(fn: (xmlFragment: XMLDocument) => void) {\n fn(xmlFragment);\n return api;\n },\n shuffleOrdering(seed?: string | number | null) {\n // No section asks to be shuffled: leave the test alone, and stay quiet\n // about a missing seed that would never have been used.\n if (!hasShuffleOrdering(xmlFragment)) {\n return api;\n }\n\n const normalizedSeed = typeof seed === 'string' ? seed.trim() : seed;\n\n if (normalizedSeed === null || normalizedSeed === undefined || normalizedSeed === '') {\n const fallbackSeed = xmlUri || 'default-test-seed';\n warnMissingSeed('qtiTransformTest', fallbackSeed);\n shuffleSectionsOrdering(xmlFragment, fallbackSeed);\n return api;\n }\n\n shuffleSectionsOrdering(xmlFragment, normalizedSeed);\n return api;\n },\n items() {\n return itemsFromTest(xmlFragment);\n },\n html() {\n return new XMLSerializer().serializeToString(toHTML(xmlFragment));\n },\n xml(): string {\n return new XMLSerializer().serializeToString(xmlFragment);\n },\n htmlDoc(registry?: CustomElementRegistry) {\n return toHTML(xmlFragment, registry);\n },\n xmlDoc(): XMLDocument {\n return xmlFragment;\n }\n };\n return api;\n};\n"],"mappings":"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","names":["fallbackPath","path","shuffleInteractions"]}
1
+ {"version":3,"sources":["../../qti-transformers/src/item/cdata.ts","../../qti-transformers/src/item/configure-pci.ts","../../qti-transformers/src/item/extend.ts","../../qti-transformers/src/item/pci-hooks.ts","../../qti-transformers/src/shared/prng.ts","../../qti-transformers/src/shared/shuffle.ts","../../qti-transformers/src/item/shuffle-interactions.ts","../../qti-transformers/src/item/stylesheets.ts","../../qti-transformers/src/shared/missing-seed-warning.ts","../../qti-transformers/src/shared/xml.ts","../../qti-transformers/src/qti-transform-item.ts","../../qti-transformers/src/qti-transform-manifest.ts","../../qti-transformers/src/test/items.ts","../../qti-transformers/src/test/shuffle-sections.ts","../../qti-transformers/src/qti-transform-test.ts"],"sourcesContent":["export function convertCDATAtoComment(xmlFragment: DocumentFragment) {\n const cdataElements = xmlFragment.querySelectorAll('qti-custom-operator[class=\"js.org\"] > qti-base-value');\n cdataElements.forEach(element => {\n const commentText = document.createComment(element.textContent);\n element.replaceChild(commentText, element.firstChild);\n });\n}\n","// Type definition for module resolution config\nexport interface ModuleResolutionConfig {\n waitSeconds?: number;\n context?: string;\n catchError?: boolean;\n urlArgs?: string;\n paths: {\n [key: string]: string | string[];\n };\n shim?: {\n [key: string]: {\n deps?: string[]; // Array of dependencies\n exports?: string; // The global variable to use as the module's value\n };\n };\n}\n\nexport async function configurePci(\n xmlFragment: XMLDocument,\n baseUrl: string,\n getModuleResolutionConfig: (baseUrl: string, fileUrl: string) => Promise<ModuleResolutionConfig>,\n selector = 'qti-portable-custom-interaction'\n): Promise<void> {\n const customInteractionTypeIdentifiers: string[] = [];\n const portableCustomInteractions = xmlFragment.querySelectorAll(selector);\n\n // Avoid fetching module resolution configs for items that do not contain any PCIs.\n // This prevents unnecessary network requests when configurePci() is called for every item.\n if (portableCustomInteractions.length === 0) {\n return;\n }\n\n // Lazily load (and cache) the default module resolution configs.\n let moduleResolutionConfig: ModuleResolutionConfig | null = null;\n let moduleResolutionFallbackConfig: ModuleResolutionConfig | null = null;\n const ensureDefaultModuleResolutionConfigs = async () => {\n if (moduleResolutionConfig === null) {\n try {\n moduleResolutionConfig = await getModuleResolutionConfig(baseUrl, '/modules/module_resolution.js');\n } catch {\n moduleResolutionConfig = null;\n }\n }\n if (moduleResolutionFallbackConfig === null) {\n try {\n moduleResolutionFallbackConfig = await getModuleResolutionConfig(\n baseUrl,\n '/modules/fallback_module_resolution.js'\n );\n } catch {\n moduleResolutionFallbackConfig = null;\n }\n }\n };\n\n for (const interaction of Array.from(portableCustomInteractions)) {\n // set data-base-url\n interaction.setAttribute('data-base-url', baseUrl);\n\n let customInteractionTypeIdentifier = interaction.getAttribute('custom-interaction-type-identifier');\n if (customInteractionTypeIdentifier && customInteractionTypeIdentifiers.includes(customInteractionTypeIdentifier)) {\n customInteractionTypeIdentifier = customInteractionTypeIdentifier + customInteractionTypeIdentifiers.length;\n interaction.setAttribute('custom-interaction-type-identifier', customInteractionTypeIdentifier);\n customInteractionTypeIdentifiers.push(customInteractionTypeIdentifier);\n }\n if (customInteractionTypeIdentifier) {\n customInteractionTypeIdentifiers.push(customInteractionTypeIdentifier);\n }\n\n // Check if qti-interaction-modules already exists\n let modulesElement = interaction.querySelector('qti-interaction-modules');\n\n // If it exists and has primary-configuration, handle that format\n if (modulesElement && modulesElement.getAttribute('primary-configuration')) {\n await ensureDefaultModuleResolutionConfigs();\n const primaryConfigPath = modulesElement.getAttribute('primary-configuration');\n if (primaryConfigPath) {\n try {\n // Load the primary configuration\n const primaryConfig = await getModuleResolutionConfig(baseUrl, `/${primaryConfigPath}`);\n\n // Get existing module elements that only have id attributes\n const existingModules = Array.from(modulesElement.querySelectorAll('qti-interaction-module'));\n\n // Update existing modules with paths from config\n for (const moduleEl of existingModules) {\n const moduleId = moduleEl.getAttribute('id');\n if (moduleId && primaryConfig.paths && primaryConfig.paths[moduleId]) {\n const primaryPath = primaryConfig.paths[moduleId];\n const primaryPathString = Array.isArray(primaryPath) ? primaryPath[0] : primaryPath;\n moduleEl.setAttribute('primary-path', primaryPathString);\n\n // Check for fallback path\n if (\n moduleResolutionFallbackConfig &&\n moduleResolutionFallbackConfig.paths &&\n moduleResolutionFallbackConfig.paths[moduleId]\n ) {\n const fallbackPath = moduleResolutionFallbackConfig.paths[moduleId];\n if (Array.isArray(fallbackPath)) {\n moduleEl.setAttribute('fallback-path', fallbackPath[0]);\n } else {\n moduleEl.setAttribute('fallback-path', fallbackPath);\n }\n }\n }\n }\n\n // Add any additional modules from primary config that aren't already present\n if (primaryConfig.paths) {\n for (const moduleId in primaryConfig.paths) {\n const existingModule = modulesElement.querySelector(`qti-interaction-module[id=\"${moduleId}\"]`);\n if (!existingModule) {\n const newModuleElement = xmlFragment.createElement('qti-interaction-module');\n newModuleElement.setAttribute('id', moduleId);\n const primaryPathString = Array.isArray(primaryConfig.paths[moduleId])\n ? primaryConfig.paths[moduleId][0]\n : primaryConfig.paths[moduleId];\n newModuleElement.setAttribute('primary-path', primaryPathString);\n\n // Check for fallback path\n if (\n moduleResolutionFallbackConfig &&\n moduleResolutionFallbackConfig.paths &&\n moduleResolutionFallbackConfig.paths[moduleId]\n ) {\n const fallbackPath = moduleResolutionFallbackConfig.paths[moduleId];\n if (Array.isArray(fallbackPath)) {\n newModuleElement.setAttribute('fallback-path', fallbackPath[0]);\n } else {\n newModuleElement.setAttribute('fallback-path', fallbackPath);\n }\n }\n\n modulesElement.appendChild(newModuleElement);\n }\n }\n }\n\n // Apply urlArgs if present in config\n if (primaryConfig.urlArgs) {\n modulesElement.setAttribute('url-args', primaryConfig.urlArgs);\n }\n } catch (error) {\n console.warn(`Failed to load primary configuration: ${primaryConfigPath}`, error);\n }\n }\n } else {\n // Original logic for when there's no existing qti-interaction-modules or no primary-configuration\n await ensureDefaultModuleResolutionConfigs();\n if (moduleResolutionConfig) {\n // Create qti-interaction-modules if it doesn't exist\n if (interaction.querySelector('qti-interaction-modules') === null) {\n modulesElement = xmlFragment.createElement('qti-interaction-modules');\n interaction.appendChild(modulesElement);\n } else {\n modulesElement = interaction.querySelector('qti-interaction-modules');\n }\n\n for (const module in moduleResolutionConfig.paths) {\n const path = moduleResolutionConfig.paths[module];\n let fallbackPath: string | string[] = '';\n\n if (\n moduleResolutionFallbackConfig &&\n moduleResolutionFallbackConfig.paths &&\n moduleResolutionFallbackConfig.paths[module]\n ) {\n fallbackPath = moduleResolutionFallbackConfig.paths[module];\n }\n\n const primaryArray = Array.isArray(path) ? path : [path];\n const fallbackPathArray = Array.isArray(fallbackPath) ? fallbackPath : [fallbackPath];\n\n // create an array with primary and fallback paths.\n const paths = primaryArray.map((primaryPath, i) => {\n const fallbackPath = fallbackPathArray.length > i ? fallbackPathArray[i] : '';\n return {\n primaryPath,\n fallbackPath\n };\n });\n\n // check if all fallbackPath elements are in the array: paths, otherwise add\n for (const fallbackPath of fallbackPathArray) {\n if (!paths.some(p => p.fallbackPath === fallbackPath)) {\n paths.push({\n primaryPath: primaryArray.length > 0 ? primaryArray[0] : fallbackPath,\n fallbackPath\n });\n }\n }\n\n // add the paths to the qti-interaction-modules\n for (const path of paths) {\n const moduleElement = xmlFragment.createElement('qti-interaction-module');\n if (path.fallbackPath) {\n moduleElement.setAttribute('fallback-path', path.fallbackPath);\n }\n moduleElement.setAttribute('id', module);\n moduleElement.setAttribute('primary-path', path.primaryPath);\n\n if (modulesElement) {\n modulesElement.appendChild(moduleElement);\n }\n }\n }\n }\n }\n }\n}\n","// Function to extend elements with a specific tag name by adding an extension suffix\n// Uses localName matching to handle namespaced XML documents where querySelectorAll may not work\nexport function extendElementName(xmlFragment: XMLDocument, tagName: string, extension: string) {\n // Use getElementsByTagName with '*' to find all elements, then filter by localName\n // This works correctly for both namespaced and non-namespaced XML documents\n const allElements = Array.from(xmlFragment.getElementsByTagName('*'));\n const matchingElements = allElements.filter(\n element => element.localName === tagName || element.localName?.toLowerCase() === tagName.toLowerCase()\n );\n\n matchingElements.forEach(element => {\n const newTagName = `${tagName}-${extension}`;\n const newElement = createElementWithNewTagName(element, newTagName);\n element.replaceWith(newElement);\n });\n}\n\n// Function to extend any element with a specific class pattern (e.g., \"extend:suffix\")\nexport function extendElementsWithClass(xmlFragment: XMLDocument, classNamePattern: string) {\n xmlFragment.querySelectorAll('*').forEach(element => {\n const classList = element.classList;\n if (classList) {\n classList.forEach(className => {\n if (className.startsWith(`${classNamePattern}:`)) {\n const suffix = className.slice(`${classNamePattern}:`.length);\n const newTagName = `${element.nodeName}-${suffix}`;\n const newElement = createElementWithNewTagName(element, newTagName);\n element.replaceWith(newElement);\n }\n });\n }\n });\n}\n\n// Helper function to create a new element with a new tag name and copy attributes and children\nfunction createElementWithNewTagName(element: Element, newTagName: string) {\n // create Elements on the ownerDocument which is usually the XMLDocument\n const newElement = element.ownerDocument.createElement(newTagName);\n // Copy attributes\n for (const attr of element.attributes) {\n newElement.setAttribute(attr.name, attr.value);\n }\n // Copy child nodes\n while (element.firstChild) {\n newElement.appendChild(element.firstChild);\n }\n return newElement;\n}\n","export function pciHooks(xmlFragment: XMLDocument, uri: string): void {\n const attributes = ['hook', 'module'];\n const documentPath = uri.substring(0, uri.lastIndexOf('/'));\n for (const attribute of attributes) {\n const srcAttributes = xmlFragment.querySelectorAll('[' + attribute + ']');\n srcAttributes.forEach(node => {\n const srcValue = node.getAttribute(attribute)!;\n if (!srcValue.startsWith('data:') && !srcValue.startsWith('http')) {\n // Just paste the relative path of the src location after the documentrootPath\n // old pcis can have a .js, new pci's don't\n node.setAttribute('base-url', uri);\n node.setAttribute('module', documentPath + '/' + encodeURI(srcValue + (srcValue.endsWith('.js') ? '' : '.js')));\n }\n });\n }\n}\n","export interface SeededRandomSource {\n random(): number;\n}\n\n/**\n * Small, dependency-free seeded PRNG used for deterministic QTI shuffling\n * (sections and interactions). It intentionally exposes a `random()` method,\n * matching the TC39 seeded-random proposal shape so this can be swapped when a\n * standard implementation is available.\n *\n * xmur3-style seed hashing feeds mulberry32 (fast 32-bit generator).\n */\nexport class SeededRandom implements SeededRandomSource {\n #state: number;\n\n constructor(seed: Uint8Array) {\n if (!(seed instanceof Uint8Array)) {\n throw new TypeError('SeededRandom seed must be a Uint8Array.');\n }\n if (seed.length > 32) {\n throw new RangeError('SeededRandom seed must be 32 bytes or less.');\n }\n\n const paddedSeed = new Uint8Array(32);\n paddedSeed.set(seed, 32 - seed.length);\n this.#state = hashBytes(paddedSeed);\n }\n\n static fromSeed(seed: Uint8Array): SeededRandom {\n if (seed.length !== 32) {\n throw new RangeError('SeededRandom.fromSeed requires a 32-byte seed.');\n }\n return new SeededRandom(seed);\n }\n\n static fromFixed(byte: number): SeededRandom {\n if (typeof byte !== 'number') {\n throw new TypeError('SeededRandom.fromFixed requires a number.');\n }\n if (!Number.isInteger(byte) || byte < 0 || byte > 255) {\n throw new RangeError('SeededRandom.fromFixed requires an integer from 0 to 255.');\n }\n\n const seed = new Uint8Array(32);\n seed[31] = byte;\n return new SeededRandom(seed);\n }\n\n static fromSeedKey(seedKey: string | number): SeededRandom {\n const random = new SeededRandom(new Uint8Array());\n random.#state = hashString(`${seedKey}`);\n return random;\n }\n\n random(): number {\n this.#state |= 0;\n this.#state = (this.#state + 0x6d2b79f5) | 0;\n let t = Math.imul(this.#state ^ (this.#state >>> 15), 1 | this.#state);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n }\n}\n\nexport function createSeededRandom(seedKey: string | number): SeededRandomSource {\n return SeededRandom.fromSeedKey(seedKey);\n}\n\nfunction hashString(seedKey: string): number {\n let h = 1779033703 ^ seedKey.length;\n for (let i = 0; i < seedKey.length; i++) {\n h = Math.imul(h ^ seedKey.charCodeAt(i), 3432918353);\n h = (h << 13) | (h >>> 19);\n }\n return finalizeHash(h);\n}\n\nfunction hashBytes(seed: Uint8Array): number {\n let h = 1779033703 ^ seed.length;\n for (let i = 0; i < seed.length; i++) {\n h = Math.imul(h ^ seed[i], 3432918353);\n h = (h << 13) | (h >>> 19);\n }\n return finalizeHash(h);\n}\n\nfunction finalizeHash(hash: number): number {\n let h = Math.imul(hash ^ (hash >>> 16), 2246822507);\n h = Math.imul(h ^ (h >>> 13), 3266489909);\n return (h ^ (h >>> 16)) >>> 0;\n}\n","import type { SeededRandomSource } from './prng';\n\nexport type RandomSource = SeededRandomSource | (() => number);\n\nconst randomValue = (source: RandomSource): number => (typeof source === 'function' ? source() : source.random());\n\n/**\n * Fisher-Yates over non-fixed units only. Fixed units keep their authored slot\n * index while movable units are shuffled into the remaining slots.\n */\nexport function shuffleKeepingFixed<T extends { fixed: boolean }>(units: T[], random: RandomSource): T[] {\n const movable = units.filter(unit => !unit.fixed);\n\n for (let i = movable.length - 1; i > 0; i--) {\n const j = Math.floor(randomValue(random) * (i + 1));\n [movable[i], movable[j]] = [movable[j], movable[i]];\n }\n\n let movableIndex = 0;\n return units.map(unit => (unit.fixed ? unit : movable[movableIndex++]));\n}\n","import { createSeededRandom } from '../shared/prng';\nimport { shuffleKeepingFixed } from '../shared/shuffle';\n\nfunction getShuffleQuerySelectorByTagName(tagName: string): string | string[] | null {\n switch (tagName) {\n case 'qti-choice-interaction':\n case 'qti-order-interaction':\n return 'qti-simple-choice';\n case 'qti-inline-choice-interaction':\n return 'qti-inline-choice';\n case 'qti-match-interaction':\n return [\n 'qti-simple-match-set:first-of-type qti-simple-associable-choice',\n 'qti-simple-match-set:last-of-type > qti-simple-associable-choice'\n ];\n case 'qti-gap-match-interaction':\n return 'qti-gap-text';\n case 'qti-associate-interaction':\n return 'qti-simple-associable-choice';\n default:\n return null;\n }\n}\n\n/**\n * Shuffles the choices inside every interaction marked `shuffle=\"true\"`. When\n * `seed` is supplied, shuffling is deterministic and reproducible across\n * reloads; each interaction/group draws its own stream so they are independent\n * and stable even if siblings change. When `seed` is null/undefined, falls back\n * to Math.random.\n */\nexport function shuffleInteractions(xmlFragment: XMLDocument, seed?: string | number | null): void {\n const shuffleElements = xmlFragment.querySelectorAll(`[shuffle=\"true\"]`);\n const shuffleInteractions = Array.from(shuffleElements).filter(e =>\n e.tagName?.toLowerCase().endsWith('-interaction')\n );\n\n const seeded = seed != null && `${seed}`.length > 0;\n const itemId = xmlFragment.querySelector?.('qti-assessment-item')?.getAttribute('identifier') ?? '';\n\n let interactionIndex = 0;\n for (const shuffleInteraction of shuffleInteractions) {\n const responseId = shuffleInteraction.getAttribute('response-identifier') ?? `${interactionIndex}`;\n interactionIndex++;\n const query = getShuffleQuerySelectorByTagName(shuffleInteraction.tagName.toLowerCase());\n const queries = Array.isArray(query) ? query : [query];\n let shuffledAny = false;\n\n let queryIndex = 0;\n for (const q of queries) {\n if (!q) continue;\n const random = seeded ? createSeededRandom(`${seed}:${itemId}:${responseId}:${queryIndex}`) : Math.random;\n queryIndex++;\n const choices = (Array.from(shuffleInteraction.querySelectorAll(q)) as HTMLElement[]).filter(\n choice => choice.parentElement !== null\n );\n\n if (choices.filter(choice => choice.getAttribute('fixed') !== 'true').length <= 1) {\n console.warn('Shuffling is not possible with fewer than 2 non-fixed elements.');\n continue;\n }\n\n const parent = choices[0].parentElement;\n if (!parent || choices.some(choice => choice.parentElement !== parent)) {\n console.warn('Shuffling is not possible when choices do not share the same parent element.');\n continue;\n }\n\n const anchor = choices[choices.length - 1].nextSibling;\n const orderedChoices = shuffleKeepingFixed(\n choices.map(choice => ({\n element: choice,\n fixed: choice.getAttribute('fixed') === 'true'\n })),\n random\n );\n\n for (const choice of choices) {\n choice.remove();\n }\n for (const { element } of orderedChoices) {\n parent.insertBefore(element, anchor);\n }\n\n shuffledAny = true;\n }\n\n if (shuffledAny) {\n shuffleInteraction.removeAttribute('shuffle');\n }\n }\n}\n","export function stripStyleSheets(xmlFragment: DocumentFragment) {\n // remove qti-stylesheet tag\n xmlFragment.querySelectorAll('qti-stylesheet').forEach(stylesheet => stylesheet.remove());\n}\n","/**\n * The missing-seed warning is advice about configuration, not about the document being\n * transformed — say it once per session, shared by the item and test transformers since\n * both are pointing at the same missing `QTI_CONTEXT.seed`.\n *\n * Deliberately not re-exported from the package entry: `resetMissingSeedWarning` exists so\n * tests can restore the once-per-session state, not as public API.\n */\nlet missingSeedWarned = false;\n\nexport function warnMissingSeed(scope: string, fallbackSeed: string): void {\n if (missingSeedWarned) return;\n missingSeedWarned = true;\n console.warn(`[${scope}] No QTI_CONTEXT.seed provided; using \"${fallbackSeed}\" as deterministic fallback seed.`);\n}\n\nexport function resetMissingSeedWarning(): void {\n missingSeedWarned = false;\n}\n","export function loadXML(url: string, signal?: AbortSignal): Promise<XMLDocument | null> {\n return fetch(url, { signal })\n .then(response => {\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n return response.text();\n })\n .then(text => {\n const parser = new DOMParser();\n return parser.parseFromString(text, 'text/xml');\n })\n .catch(error => {\n if (error.name === 'AbortError') {\n throw error;\n }\n throw new Error(`Failed to load XML: ${error.message}`);\n });\n}\n\nexport function parseXML(xmlDocument: string) {\n const parser = new DOMParser();\n const xmlFragment = parser.parseFromString(xmlDocument, 'text/xml');\n return xmlFragment;\n}\n\n// Function to strip unsupported namespaces (qti) from the nodes sent to the browser\nfunction stripNamespaces(node: Node, doc: Document, registry?: CustomElementRegistry): Node {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n let newEl: Element;\n\n if (\n !el.namespaceURI ||\n el.namespaceURI.startsWith('http://www.imsglobal.org/xsd/qti/') ||\n el.namespaceURI.startsWith('http://www.imsglobal.org/xsd/imsqti')\n ) {\n newEl = registry\n ? doc.createElement(el.localName, { customElementRegistry: registry })\n : doc.createElement(el.localName);\n } else {\n newEl = doc.createElementNS(el.namespaceURI, el.tagName);\n }\n\n for (let i = 0; i < el.attributes.length; i++) {\n const attr = el.attributes[i];\n newEl.setAttribute(attr.localName, attr.value);\n }\n for (let i = 0; i < el.childNodes.length; i++) {\n newEl.appendChild(stripNamespaces(el.childNodes[i], doc, registry));\n }\n return newEl;\n }\n return node.cloneNode(false);\n}\n\nexport function toHTML(xmlFragment: Document, registry?: CustomElementRegistry): DocumentFragment {\n const fragment = document.createDocumentFragment();\n for (let i = 0; i < xmlFragment.childNodes.length; i++) {\n fragment.appendChild(stripNamespaces(xmlFragment.childNodes[i], document, registry));\n }\n return fragment;\n}\n\n// Updates src and href attributes with the base location\n// Uses querySelectorAll('*') to find all elements, then checks attributes manually\n// This handles namespaced XML documents where attribute selectors like [href] may not work\n// Note: primary-path is NOT processed here because it is resolved separately\n// by the PCI component using data-base-url in the iframe's module resolution\nexport function setLocation(xmlFragment: DocumentFragment, location: string) {\n if (!location.endsWith('/')) {\n location += '/';\n }\n\n // querySelectorAll('*') finds all descendant elements regardless of namespace\n const allElements = xmlFragment.querySelectorAll('*');\n\n allElements.forEach(el => {\n // Check each attribute we care about\n // src, srcset and poster are URLDType or xs:anyURI everywhere they're used, so we care about them\n // Note: primary-path is excluded - PCI handles module path resolution via data-base-url\n for (const attr of ['src', 'href', 'srcset', 'poster'] as const) {\n const attrValue = el.getAttribute(attr)?.trim();\n\n if (attrValue && !/^(data:|https?:|blob:)/.test(attrValue)) {\n const newValue = location + encodeURI(attrValue);\n el.setAttribute(attr, newValue);\n }\n }\n });\n}\n","/**\n * Browser based QTI-XML to HTML transformer.\n * Returns an object with methods to load, parse, transform and serialize QTI XML items.\n * @returns An object with methods to load, parse, transform and serialize QTI XML items.\n * @example\n * const qtiTransformer = qtiTransformItem();\n * await qtiTransformer.load('path/to/xml/file.xml');\n * qtiTransformer.path('/assessmentItem/itemBody');\n * const html = qtiTransformer.html();\n * const xml = qtiTransformer.xml();\n * const htmldoc = qtiTransformer.htmldoc();\n * const xmldoc = qtiTransformer.xmldoc();\n *\n * qtiTransformItem().parse(storyXML).html()\n */\nimport { convertCDATAtoComment } from './item/cdata';\nimport { configurePci, type ModuleResolutionConfig } from './item/configure-pci';\nimport { extendElementName, extendElementsWithClass } from './item/extend';\nimport { pciHooks } from './item/pci-hooks';\nimport { shuffleInteractions } from './item/shuffle-interactions';\nimport { stripStyleSheets } from './item/stylesheets';\nimport { warnMissingSeed } from './shared/missing-seed-warning';\nimport { loadXML, parseXML, setLocation, toHTML } from './shared/xml';\n\nexport type { ModuleResolutionConfig };\n\nexport type transformItemApi = {\n load: (uri: string, signal?: AbortSignal) => Promise<transformItemApi>;\n parse: (xmlString: string) => transformItemApi;\n path: (location: string) => transformItemApi;\n fn: (fn: (xmlFragment: XMLDocument) => void) => transformItemApi;\n pciHooks: (uri: string) => transformItemApi;\n configurePci: (\n baseUrl: string,\n getModuleResolutionConfig: (baseUrl: string, fileUrl: string) => Promise<ModuleResolutionConfig>,\n selector?: string\n ) => Promise<transformItemApi>;\n extendElementName: (elementName: string, extend: string) => transformItemApi;\n extendElementsWithClass: (param?: string) => transformItemApi;\n customInteraction: (baseRef: string, baseItem: string) => transformItemApi;\n convertCDATAtoComment: () => transformItemApi;\n shuffleInteractions: (seed?: string | number | null) => transformItemApi;\n stripStyleSheets: () => transformItemApi;\n html: () => string;\n xml: () => string;\n htmlDoc: (registry?: CustomElementRegistry) => DocumentFragment;\n xmlDoc: () => XMLDocument;\n};\n\nexport const qtiTransformItem = () => {\n let xmlFragment: XMLDocument;\n let xmlUri = '';\n\n const api: transformItemApi = {\n load(uri: string, signal?: AbortSignal) {\n xmlUri = uri;\n return loadXML(uri, signal).then(xml => {\n xmlFragment = xml;\n return api;\n });\n },\n parse(xmlString: string): typeof api {\n xmlFragment = parseXML(xmlString);\n xmlUri = '';\n return api;\n },\n path: (location: string): typeof api => {\n setLocation(xmlFragment, location);\n xmlUri = null;\n return api;\n },\n fn(fn: (xmlFragment: XMLDocument) => void): typeof api {\n fn(xmlFragment);\n return api;\n },\n pciHooks(uri: string): typeof api {\n pciHooks(xmlFragment, uri);\n return api;\n },\n async configurePci(\n baseUrl: string,\n getModuleResolutionConfig: (baseUrl: string, fileUrl: string) => Promise<ModuleResolutionConfig>,\n selector = 'qti-portable-custom-interaction'\n ): Promise<typeof api> {\n await configurePci(xmlFragment, baseUrl, getModuleResolutionConfig, selector);\n return api;\n },\n shuffleInteractions(seed?: string | number | null): typeof api {\n const normalizedSeed = typeof seed === 'string' ? seed.trim() : seed;\n\n if (normalizedSeed === null || normalizedSeed === undefined || normalizedSeed === '') {\n const fallbackSeed = xmlUri || 'default-item-seed';\n warnMissingSeed('qtiTransformItem', fallbackSeed);\n shuffleInteractions(xmlFragment, fallbackSeed);\n return api;\n }\n\n shuffleInteractions(xmlFragment, normalizedSeed);\n return api;\n },\n extendElementName: (tagName: string, extension: string): typeof api => {\n extendElementName(xmlFragment, tagName, extension);\n return api;\n },\n extendElementsWithClass: (param: string = 'extend'): typeof api => {\n extendElementsWithClass(xmlFragment, param);\n return api;\n },\n customInteraction(baseRef: string, baseItem: string): typeof api {\n const qtiCustomInteraction = xmlFragment.querySelector('qti-custom-interaction');\n const qtiCustomInteractionObject = qtiCustomInteraction.querySelector('object');\n\n qtiCustomInteraction.setAttribute('data-base-ref', baseRef);\n qtiCustomInteraction.setAttribute('data-base-item', baseRef + baseItem);\n qtiCustomInteraction.setAttribute('data', qtiCustomInteractionObject.getAttribute('data'));\n qtiCustomInteraction.setAttribute('width', qtiCustomInteractionObject.getAttribute('width'));\n qtiCustomInteraction.setAttribute('height', qtiCustomInteractionObject.getAttribute('height'));\n\n qtiCustomInteraction.removeChild(qtiCustomInteractionObject);\n return api;\n },\n convertCDATAtoComment(): typeof api {\n convertCDATAtoComment(xmlFragment);\n return api;\n },\n stripStyleSheets(): typeof api {\n stripStyleSheets(xmlFragment);\n return api;\n },\n html() {\n if (xmlUri !== null) {\n setLocation(xmlFragment, xmlUri.substring(0, xmlUri.lastIndexOf('/')));\n }\n return new XMLSerializer().serializeToString(toHTML(xmlFragment));\n },\n xml(): string {\n return new XMLSerializer().serializeToString(xmlFragment);\n },\n htmlDoc(registry?: CustomElementRegistry) {\n if (xmlUri !== null) {\n setLocation(xmlFragment, xmlUri.substring(0, xmlUri.lastIndexOf('/')));\n }\n return toHTML(xmlFragment, registry);\n },\n xmlDoc(): XMLDocument {\n return xmlFragment; // new XMLSerializer().serializeToString(xmlFragment);\n }\n };\n return api;\n};\n","import { loadXML, parseXML } from './shared/xml';\n\nexport const qtiTransformManifest = (): {\n load: (uri: string, signal?: AbortSignal) => Promise<typeof api>;\n assessmentTest: () => { href: string; identifier: string };\n} => {\n let xmlFragment: XMLDocument;\n\n const api = {\n // Awaits loadXML so a failed fetch (network, CORS, non-2xx) rejects this\n // promise. Callers own the failure: they can render it, retry it or let it\n // bubble — rather than it escaping as an unhandled rejection while their\n // own `await` never settles.\n async load(uri: string, signal?: AbortSignal) {\n xmlFragment = await loadXML(uri, signal);\n return api;\n },\n parse(xmlString: string) {\n xmlFragment = parseXML(xmlString);\n },\n assessmentTest() {\n const el = xmlFragment.querySelector('resource[type=\"imsqti_test_xmlv3p0\"]');\n return { href: el.getAttribute('href'), identifier: el.getAttribute('identifier') };\n }\n };\n return api;\n};\n","export function itemsFromTest(xmlFragment: DocumentFragment) {\n const items: { identifier: string; href: string; category: string }[] = [];\n xmlFragment.querySelectorAll('qti-assessment-item-ref').forEach(el => {\n const identifier = el.getAttribute('identifier');\n const href = el.getAttribute('href');\n const category = el.getAttribute('category');\n items.push({ identifier, href, category });\n });\n return items;\n}\n","import { createSeededRandom } from '../shared/prng';\nimport { shuffleKeepingFixed } from '../shared/shuffle';\n\nconst isLocal = (el: Element, ...names: string[]): boolean =>\n names.includes(el.localName) || names.includes(el.localName?.toLowerCase?.());\n\nconst isItemRef = (el: Element): boolean => isLocal(el, 'qti-assessment-item-ref', 'assessmentitemref');\n\nconst isSection = (el: Element): boolean => isLocal(el, 'qti-assessment-section', 'assessmentsection');\n\nconst attrIsTrue = (el: Element, ...names: string[]): boolean =>\n names.some(n => {\n const v = (el.getAttribute(n) ?? '').trim().toLowerCase();\n return v === 'true' || v === '1';\n });\n\nconst attrIsFalse = (el: Element, ...names: string[]): boolean =>\n names.some(n => {\n const v = (el.getAttribute(n) ?? '').trim().toLowerCase();\n return v === 'false' || v === '0';\n });\n\nconst isOrdering = (el: Element): boolean => isLocal(el, 'qti-ordering', 'ordering');\n\nconst isSelection = (el: Element): boolean => isLocal(el, 'qti-selection', 'selection');\n\nconst shuffleOrderingElements = (section: Element): Element[] =>\n Array.from(section.children).filter(child => isOrdering(child) && attrIsTrue(child, 'shuffle'));\n\nconst rngFor = (seed: string | number, key: string) => createSeededRandom(`${seed}:${key}`);\n\n/** A movable unit in a section: one or more sibling nodes that move together. */\ntype Unit = { nodes: Element[]; fixed: boolean };\n\n/**\n * The item refs of a subsection, in document order, plus the element children\n * that consuming the wrapper would discard.\n *\n * Descendant subsections are collected transitively: `orderSection` returns\n * early for a section without <qti-ordering>, so a wrapper nested inside a\n * non-shuffling subsection is never consumed there and has to be gathered here.\n */\nfunction collectItemRefsDeep(section: Element): { itemRefs: Element[]; dropped: string[] } {\n const itemRefs: Element[] = [];\n const dropped: string[] = [];\n\n const walk = (el: Element): void => {\n for (const child of Array.from(el.children)) {\n if (isItemRef(child)) {\n itemRefs.push(child);\n } else if (isSection(child)) {\n walk(child);\n } else if (!isOrdering(child) && !isSelection(child)) {\n // Ordering/selection directives are transform config; anything else\n // (a rubric block, say) is authored content that the lift loses.\n dropped.push(child.localName);\n }\n }\n };\n walk(section);\n\n return { itemRefs, dropped };\n}\n\nfunction warnDroppedChildren(section: Element, dropped: string[]): void {\n if (dropped.length === 0) return;\n const identifier = section.getAttribute('identifier') ?? '(no identifier)';\n console.warn(\n `[qtiTransformTest] Subsection \"${identifier}\" is consumed by section ordering; ` +\n `discarding its ${[...new Set(dropped)].join(', ')}.`\n );\n}\n\n/**\n * Seed-deterministic QTI section shuffling.\n *\n * Reorders the children of every <qti-assessment-section> that carries a\n * <qti-ordering shuffle=\"true\">, following QTI 3.0 ordering rules:\n *\n * - Depth-first: a subsection orders its own children before the parent\n * reorders it.\n * - Item refs with fixed=\"true\" stay in their authored position; the other\n * units shuffle around them.\n * - A subsection is a grouping device, not a delivered section: its item refs\n * are lifted into the parent and the wrapper is consumed, just as the\n * <qti-ordering> directive itself is. Nothing nested survives into the DOM,\n * so the player only ever sees the authored top-level sections.\n * - By default (QTI 3.0 defaults keep-together to true) a subsection travels as\n * one unit: its items stay contiguous and keep their authored order, while\n * the parent's remaining items shuffle around the block. Add fixed=\"true\" to\n * pin the block itself to its authored slot.\n * - Only an explicit keep-together=\"false\" dissolves a subsection: its\n * (already internally ordered) item refs are poured into the parent pool and\n * interleaved with the parent's own units.\n *\n * The same `seed` always produces the same order, so a restarted session keeps\n * its sequence. Each section draws from its own PRNG stream (seed + section id)\n * so sibling sections are independent and stable.\n */\nfunction orderSection(section: Element, seed: string | number): void {\n // Depth-first: let every child section order itself first.\n for (const child of Array.from(section.children)) {\n if (isSection(child)) {\n orderSection(child, seed);\n }\n }\n\n const orderingEls = shuffleOrderingElements(section);\n if (orderingEls.length === 0) {\n return;\n }\n\n // Build the pool of movable units in document order. Config/structural\n // children (qti-ordering, qti-selection, rubric-block, …) are left in place.\n const units: Unit[] = [];\n const toDetach: Element[] = [];\n\n for (const child of Array.from(section.children)) {\n if (isItemRef(child)) {\n units.push({ nodes: [child], fixed: attrIsTrue(child, 'fixed') });\n toDetach.push(child);\n } else if (isSection(child)) {\n // Internal order is already settled: the depth-first pass above shuffled\n // this subsection if it carried its own <qti-ordering shuffle=\"true\">, and\n // left the authored order alone otherwise.\n const { itemRefs, dropped } = collectItemRefsDeep(child);\n warnDroppedChildren(child, dropped);\n\n if (attrIsFalse(child, 'keep-together', 'keeptogether')) {\n // Explicitly dissolved: the items join the parent pool individually and\n // interleave with the parent's own items.\n for (const itemRef of itemRefs) {\n units.push({ nodes: [itemRef], fixed: attrIsTrue(itemRef, 'fixed') });\n }\n } else if (itemRefs.length > 0) {\n // One multi-node unit, so the items stay contiguous and keep their order\n // wherever the block lands.\n units.push({ nodes: itemRefs, fixed: attrIsTrue(child, 'fixed') });\n }\n\n toDetach.push(child); // the grouping wrapper is consumed\n }\n }\n\n // Where the reordered units go back in: after the last node we took out, so\n // any trailing structural children (a rubric block, say) keep their position.\n // Captured before detaching, and never itself a detached node.\n const anchor = toDetach.length > 0 ? toDetach[toDetach.length - 1].nextSibling : null;\n\n const sectionKey = section.getAttribute('identifier') ?? '';\n // A no-op for 0 or 1 movable units, but the re-insertion below still has to\n // run so consumed wrappers are lifted rather than left in the DOM.\n const ordered = shuffleKeepingFixed(units, rngFor(seed, sectionKey));\n\n for (const el of toDetach) {\n el.remove();\n }\n for (const unit of ordered) {\n for (const node of unit.nodes) {\n section.insertBefore(node, anchor);\n }\n }\n\n // Ordering directives are consumed by this transform step.\n for (const orderingEl of orderingEls) {\n orderingEl.remove();\n }\n}\n\n/** Whether anything in `doc` asks to be shuffled at all. */\nexport function hasShuffleOrdering(doc: XMLDocument): boolean {\n return Array.from(doc.getElementsByTagName('*')).some(el => isOrdering(el) && attrIsTrue(el, 'shuffle'));\n}\n\n/**\n * Mutates `doc` in place: shuffles every section flagged with\n * <qti-ordering shuffle=\"true\">, deterministically from `seed`.\n */\nexport function shuffleSectionsOrdering(doc: XMLDocument, seed: string | number): void {\n const sections = Array.from(doc.getElementsByTagName('*')).filter(el => isSection(el));\n // Kick off recursion only for top-level sections; orderSection handles nesting\n // depth-first, so descendants are not double-processed.\n for (const section of sections) {\n // The snapshot above can contain wrappers that an ancestor has since\n // consumed. A detached element has no parentNode; a document element still\n // has one (the document), so this does not skip a section-rooted document.\n if (!section.parentNode) continue;\n\n const parent = section.parentElement;\n if (parent && isSection(parent)) continue;\n\n orderSection(section, seed);\n }\n}\n","/**\n * Returns an object with methods to load, parse and transform QTI tests.\n * @returns An object with methods to load, parse and transform QTI tests.\n * @example\n * const qtiTransformer = qtiTransformTest();\n * await qtiTransformer.load('https://example.com/test.xml');\n * const items = qtiTransformer.items();\n * const html = qtiTransformer.html();\n * const xml = qtiTransformer.xml();\n */\n\nimport { warnMissingSeed } from './shared/missing-seed-warning';\nimport { loadXML, parseXML, setLocation, toHTML } from './shared/xml';\nimport { itemsFromTest } from './test/items';\nimport { hasShuffleOrdering, shuffleSectionsOrdering } from './test/shuffle-sections';\n\nexport type transformTestApi = {\n load: (uri: string, signal?: AbortSignal) => Promise<transformTestApi>;\n parse: (xmlString: string) => transformTestApi;\n path: (location: string) => transformTestApi;\n fn: (fn: (xmlFragment: XMLDocument) => void) => transformTestApi;\n /**\n * Deterministically shuffle the children of every section that carries a\n * <qti-ordering shuffle=\"true\">, honoring fixed items and nested\n * keep-together / visible rules. The same seed reproduces the same order.\n */\n shuffleOrdering: (seed?: string | number | null) => transformTestApi;\n items: () => { identifier: string; href: string; category: string }[];\n html: () => string;\n xml: () => string;\n htmlDoc: (registry?: CustomElementRegistry) => DocumentFragment;\n xmlDoc: () => XMLDocument;\n};\n\nexport const qtiTransformTest = (): transformTestApi => {\n let xmlFragment: XMLDocument;\n let xmlUri = '';\n\n const api: transformTestApi = {\n // Awaits loadXML so a failed fetch (network, CORS, non-2xx) rejects this\n // promise. Callers own the failure: they can render it, retry it or let it\n // bubble — rather than it escaping as an unhandled rejection while their\n // own `await` never settles.\n async load(uri, signal) {\n xmlFragment = await loadXML(uri, signal);\n xmlUri = uri;\n\n api.path(uri.substring(0, uri.lastIndexOf('/')));\n return api;\n },\n parse(xmlString: string) {\n xmlFragment = parseXML(xmlString);\n xmlUri = '';\n return api;\n },\n path: (location: string): typeof api => {\n setLocation(xmlFragment, location);\n return api;\n },\n fn(fn: (xmlFragment: XMLDocument) => void) {\n fn(xmlFragment);\n return api;\n },\n shuffleOrdering(seed?: string | number | null) {\n // No section asks to be shuffled: leave the test alone, and stay quiet\n // about a missing seed that would never have been used.\n if (!hasShuffleOrdering(xmlFragment)) {\n return api;\n }\n\n const normalizedSeed = typeof seed === 'string' ? seed.trim() : seed;\n\n if (normalizedSeed === null || normalizedSeed === undefined || normalizedSeed === '') {\n const fallbackSeed = xmlUri || 'default-test-seed';\n warnMissingSeed('qtiTransformTest', fallbackSeed);\n shuffleSectionsOrdering(xmlFragment, fallbackSeed);\n return api;\n }\n\n shuffleSectionsOrdering(xmlFragment, normalizedSeed);\n return api;\n },\n items() {\n return itemsFromTest(xmlFragment);\n },\n html() {\n return new XMLSerializer().serializeToString(toHTML(xmlFragment));\n },\n xml(): string {\n return new XMLSerializer().serializeToString(xmlFragment);\n },\n htmlDoc(registry?: CustomElementRegistry) {\n return toHTML(xmlFragment, registry);\n },\n xmlDoc(): XMLDocument {\n return xmlFragment;\n }\n };\n return api;\n};\n"],"mappings":"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","names":["fallbackPath","path","shuffleInteractions"]}
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  item_default
3
- } from "./chunk-G6GFQDVN.js";
3
+ } from "./chunk-TLTYXWW7.js";
4
4
  import {
5
5
  watch
6
6
  } from "./chunk-4DB4SC5K.js";
7
7
  import {
8
8
  qtiTransformItem
9
- } from "./chunk-IJD53WY6.js";
9
+ } from "./chunk-HDEG6BAZ.js";
10
10
  import {
11
11
  computedItemContext,
12
12
  configContext,
13
13
  qtiContext
14
- } from "./chunk-HQESINXH.js";
14
+ } from "./chunk-7D3NKAGZ.js";
15
15
 
16
16
  // ../qti-item/dist/components/item-container/item-container.js
17
17
  import { consume } from "@lit/context";
@@ -292,4 +292,4 @@ export {
292
292
  QtiItem,
293
293
  qtiItemElements
294
294
  };
295
- //# sourceMappingURL=chunk-RSVRS5AK.js.map
295
+ //# sourceMappingURL=chunk-LMGJOBJA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  qtiItemElements
3
- } from "./chunk-RSVRS5AK.js";
3
+ } from "./chunk-LMGJOBJA.js";
4
4
 
5
5
  // ../qti-item/dist/register.js
6
6
  for (const { tag, ctor } of qtiItemElements) {
@@ -8,4 +8,4 @@ for (const { tag, ctor } of qtiItemElements) {
8
8
  customElements.define(tag, ctor);
9
9
  }
10
10
  }
11
- //# sourceMappingURL=chunk-ZII6M3SN.js.map
11
+ //# sourceMappingURL=chunk-M7K5C522.js.map
@@ -2,10 +2,10 @@ import {
2
2
  QtiTest,
3
3
  TestCheckItem,
4
4
  TestNavigation
5
- } from "./chunk-HBSWJC3B.js";
5
+ } from "./chunk-CRLXQZFA.js";
6
6
  import {
7
7
  QtiAssessmentItem
8
- } from "./chunk-7AVWRQIS.js";
8
+ } from "./chunk-YLRMOX6C.js";
9
9
  import {
10
10
  QtiAssociateInteraction,
11
11
  QtiChoiceInteraction,
@@ -28,10 +28,10 @@ import {
28
28
  QtiSimpleChoice,
29
29
  QtiSliderInteraction,
30
30
  QtiTextEntryInteraction
31
- } from "./chunk-TWPXES3S.js";
31
+ } from "./chunk-Q6ZK4LRH.js";
32
32
  import {
33
33
  QtiItem
34
- } from "./chunk-RSVRS5AK.js";
34
+ } from "./chunk-LMGJOBJA.js";
35
35
  import {
36
36
  watch
37
37
  } from "./chunk-4DB4SC5K.js";
@@ -41,7 +41,7 @@ import {
41
41
  computedItemContext,
42
42
  configContext,
43
43
  responseAttributeConverter
44
- } from "./chunk-HQESINXH.js";
44
+ } from "./chunk-7D3NKAGZ.js";
45
45
 
46
46
  // ../qti-corrections/dist/mixins/correct-response.mixin.js
47
47
  import { property, state } from "lit/decorators.js";
@@ -592,6 +592,22 @@ var TestNavigationCorrection = class extends TestNavigation {
592
592
  this.removeEventListener("test-show-candidate-correction", this.#onShowCandidateCorrection);
593
593
  super.disconnectedCallback();
594
594
  }
595
+ /**
596
+ * Reflect correctness back to the candidate after an ended attempt, for items
597
+ * the assessment opted in via the standard `qti-item-session-control
598
+ * show-solution` (QTI 3.0 ItemSessionControl.show-solution) — the player takes
599
+ * no opinion of its own:
600
+ * - candidate correction (a mark on the learner's selection) after every attempt;
601
+ * - the correct response (a mark on the right answer) once the item is done —
602
+ * the candidate reached the optimal outcome, or ran out of attempts.
603
+ */
604
+ afterAttemptEnded(assessmentItem, computedItem, done) {
605
+ if (!assessmentItem || !computedItem?.showSolution)
606
+ return;
607
+ const correctable = assessmentItem;
608
+ correctable.showCandidateCorrection?.(true);
609
+ correctable.showCorrectResponse?.(done);
610
+ }
595
611
  };
596
612
  var QtiTestCorrection = class extends QtiTest {
597
613
  updateAssessmentItemView(assessmentItem, view) {
@@ -1631,4 +1647,4 @@ export {
1631
1647
  QtiSliderInteractionCorrection,
1632
1648
  QtiTextEntryInteractionCorrection
1633
1649
  };
1634
- //# sourceMappingURL=chunk-J5SQLKNP.js.map
1650
+ //# sourceMappingURL=chunk-OIUK5BFU.js.map