@citolab/qti-components 7.21.1 → 7.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24753,6 +24753,28 @@
24753
24753
  "text": "iFrameClickOnElementByText(text: string) => void"
24754
24754
  }
24755
24755
  },
24756
+ {
24757
+ "kind": "method",
24758
+ "name": "iFrameGetBoundingClientRect",
24759
+ "return": {
24760
+ "type": {
24761
+ "text": ""
24762
+ }
24763
+ },
24764
+ "parameters": [
24765
+ {
24766
+ "name": "selector",
24767
+ "type": {
24768
+ "text": "string"
24769
+ },
24770
+ "description": "The CSS selector string"
24771
+ }
24772
+ ],
24773
+ "description": "Gets the bounding client rect for an element inside the iframe",
24774
+ "type": {
24775
+ "text": "iFrameGetBoundingClientRect(selector: string) => void"
24776
+ }
24777
+ },
24756
24778
  {
24757
24779
  "kind": "method",
24758
24780
  "name": "iFrameMouseClick",
@@ -1,6 +1,10 @@
1
1
  // ../qti-transformers/src/qti-transformers.ts
2
2
  function extendElementName(xmlFragment, tagName, extension) {
3
- xmlFragment.querySelectorAll(tagName).forEach((element) => {
3
+ const allElements = Array.from(xmlFragment.getElementsByTagName("*"));
4
+ const matchingElements = allElements.filter(
5
+ (element) => element.localName === tagName || element.localName?.toLowerCase() === tagName.toLowerCase()
6
+ );
7
+ matchingElements.forEach((element) => {
4
8
  const newTagName = `${tagName}-${extension}`;
5
9
  const newElement = createElementWithNewTagName(element, newTagName);
6
10
  element.replaceWith(newElement);
@@ -88,21 +92,14 @@ function setLocation(xmlFragment, location) {
88
92
  if (!location.endsWith("/")) {
89
93
  location += "/";
90
94
  }
91
- xmlFragment.querySelectorAll("[src],[href],[primary-path]").forEach((elWithSrc) => {
92
- let attr = "";
93
- if (elWithSrc.getAttribute("src")) {
94
- attr = "src";
95
- }
96
- if (elWithSrc.getAttribute("href")) {
97
- attr = "href";
98
- }
99
- if (elWithSrc.getAttribute("primary-path")) {
100
- attr = "primary-path";
101
- }
102
- const attrValue = elWithSrc.getAttribute(attr)?.trim();
103
- if (!/^(data:|https?:|blob:)/.test(attrValue)) {
104
- const newSrcValue = location + encodeURI(attrValue);
105
- elWithSrc.setAttribute(attr, newSrcValue);
95
+ const allElements = xmlFragment.querySelectorAll("*");
96
+ allElements.forEach((el) => {
97
+ for (const attr of ["src", "href"]) {
98
+ const attrValue = el.getAttribute(attr)?.trim();
99
+ if (attrValue && !/^(data:|https?:|blob:)/.test(attrValue)) {
100
+ const newValue = location + encodeURI(attrValue);
101
+ el.setAttribute(attr, newValue);
102
+ }
106
103
  }
107
104
  });
108
105
  }
@@ -500,4 +497,4 @@ export {
500
497
  qtiTransformManifest,
501
498
  qtiTransformTest
502
499
  };
503
- //# sourceMappingURL=chunk-EZL3MMGB.js.map
500
+ //# sourceMappingURL=chunk-K7HR6ZAY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../qti-transformers/src/qti-transformers.ts","../../qti-transformers/src/qti-transform-item.ts","../../qti-transformers/src/qti-transform-manifest.ts","../../qti-transformers/src/qti-transform-test.ts"],"sourcesContent":["// 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\nexport 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\n// let currentRequest: XMLHttpRequest | null = null;\n\nexport 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\nfunction stripNamespaces(node: Node, doc: Document): Node {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n const newEl = doc.createElement(el.localName);\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));\n }\n return newEl;\n }\n return node.cloneNode(false);\n}\n\nexport function toHTML(xmlFragment: Document): DocumentFragment {\n const fragment = document.createDocumentFragment();\n for (let i = 0; i < xmlFragment.childNodes.length; i++) {\n fragment.appendChild(stripNamespaces(xmlFragment.childNodes[i], document));\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 // Note: primary-path is excluded - PCI handles module path resolution via data-base-url\n for (const attr of ['src', 'href'] 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\nexport 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\nexport function stripStyleSheets(xmlFragment: DocumentFragment) {\n // remove qti-stylesheet tag\n xmlFragment.querySelectorAll('qti-stylesheet').forEach(stylesheet => stylesheet.remove());\n}\n\nexport function 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 * 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 {\n convertCDATAtoComment,\n extendElementName,\n extendElementsWithClass,\n getShuffleQuerySelectorByTagName,\n loadXML,\n parseXML,\n setLocation,\n stripStyleSheets,\n toHTML\n} from './qti-transformers';\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 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: () => transformItemApi;\n stripStyleSheets: () => transformItemApi;\n html: () => string;\n xml: () => string;\n htmlDoc: () => DocumentFragment;\n xmlDoc: () => XMLDocument;\n};\n\nexport const qtiTransformItem = (cache: boolean = false) => {\n let xmlFragment: XMLDocument;\n let xmlUri = '';\n\n const api: transformItemApi = {\n load(uri: string, signal?: AbortSignal) {\n xmlUri = uri;\n const fullKey = encodeURI(uri);\n if (cache) {\n if (sessionStorage.getItem(fullKey)) {\n return Promise.resolve(api.parse(sessionStorage.getItem(fullKey)!));\n }\n }\n return loadXML(uri, signal).then(xml => {\n xmlFragment = xml;\n api.shuffleInteractions();\n if (cache) sessionStorage.setItem(fullKey, new XMLSerializer().serializeToString(xmlFragment));\n return api;\n });\n },\n parse(xmlString: string): typeof api {\n xmlFragment = parseXML(xmlString);\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 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(\n 'module',\n documentPath + '/' + encodeURI(srcValue + (srcValue.endsWith('.js') ? '' : '.js'))\n );\n }\n });\n }\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 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 api;\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 (\n customInteractionTypeIdentifier &&\n customInteractionTypeIdentifiers.includes(customInteractionTypeIdentifier)\n ) {\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 return api;\n },\n shuffleInteractions(): typeof api {\n const shuffleElements = xmlFragment.querySelectorAll(`[shuffle=\"true\"]`);\n const shuffleInteractions = Array.from(shuffleElements).filter(e =>\n e.tagName?.toLowerCase().endsWith('-interaction')\n );\n\n for (const shuffleInteraction of shuffleInteractions) {\n const query = getShuffleQuerySelectorByTagName(shuffleInteraction.tagName.toLowerCase());\n const queries = Array.isArray(query) ? query : [query];\n\n for (const q of queries) {\n const choices = Array.from(shuffleInteraction.querySelectorAll(q)) as HTMLElement[];\n\n const fixedChoices = choices\n .map((choice, originalOrder) => ({\n element: choice,\n fixed: choice.hasAttribute('fixed') && choice.getAttribute('fixed') === 'true',\n originalOrder\n }))\n .filter(choice => choice.fixed);\n\n const nonFixedChoices = choices.filter(\n choice => !choice.hasAttribute('fixed') || choice.getAttribute('fixed') !== 'true'\n );\n\n if (nonFixedChoices.length <= 1) {\n console.warn('Shuffling is not possible with fewer than 2 non-fixed elements.');\n return api;\n }\n\n const originalOrder = [...nonFixedChoices];\n let shuffled = false;\n let attempts = 0;\n\n while (!shuffled && attempts < 20) {\n attempts++;\n for (let i = nonFixedChoices.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [nonFixedChoices[i], nonFixedChoices[j]] = [nonFixedChoices[j], nonFixedChoices[i]];\n }\n shuffled = !nonFixedChoices.every((choice, index) => choice === originalOrder[index]);\n }\n\n if (!shuffled) {\n console.warn('Failed to shuffle the choices after multiple attempts.');\n return null;\n }\n\n // Remove the shuffle attribute\n shuffleInteraction.removeAttribute('shuffle');\n\n // Reorder the elements in the DOM\n let nonFixedIndex = 0;\n for (const nonFixedChoice of nonFixedChoices) {\n nonFixedChoice.parentElement.insertBefore(nonFixedChoice, fixedChoices[nonFixedIndex]?.element);\n nonFixedIndex++;\n }\n for (const fixedChoice of fixedChoices) {\n fixedChoice.element.parentElement.insertBefore(\n fixedChoice.element,\n nonFixedChoices[fixedChoice.originalOrder]\n );\n }\n }\n }\n\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() {\n if (xmlUri !== null) {\n setLocation(xmlFragment, xmlUri.substring(0, xmlUri.lastIndexOf('/')));\n }\n return toHTML(xmlFragment);\n },\n xmlDoc(): XMLDocument {\n return xmlFragment; // new XMLSerializer().serializeToString(xmlFragment);\n }\n };\n return api;\n};\n","import { loadXML, parseXML } from './qti-transformers';\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","/**\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 { itemsFromTest, loadXML, parseXML, setLocation, toHTML } from './qti-transformers';\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 items: () => { identifier: string; href: string; category: string }[];\n html: () => string;\n xml: () => string;\n htmlDoc: () => DocumentFragment;\n xmlDoc: () => XMLDocument;\n};\n\nexport const qtiTransformTest = (): transformTestApi => {\n let xmlFragment: XMLDocument;\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\n api.path(uri.substring(0, uri.lastIndexOf('/')));\n return resolve(api);\n });\n });\n },\n parse(xmlString: string) {\n xmlFragment = parseXML(xmlString);\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 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() {\n return toHTML(xmlFragment);\n },\n xmlDoc(): XMLDocument {\n return xmlFragment;\n }\n };\n return api;\n};\n"],"mappings":"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","names":["fallbackPath","path","originalOrder"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  QtiModalFeedback
3
- } from "./chunk-Q67QWJ64.js";
3
+ } from "./chunk-ULXR5TWK.js";
4
4
  import {
5
5
  item_default,
6
6
  m
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  qtiTransformItem,
10
10
  qtiTransformTest
11
- } from "./chunk-EZL3MMGB.js";
11
+ } from "./chunk-K7HR6ZAY.js";
12
12
  import {
13
13
  E,
14
14
  INITIAL_SESSION_CONTEXT,
@@ -3409,4 +3409,4 @@ lit-html/node/private-ssr-support.js:
3409
3409
  * SPDX-License-Identifier: BSD-3-Clause
3410
3410
  *)
3411
3411
  */
3412
- //# sourceMappingURL=chunk-GXGMEWRF.js.map
3412
+ //# sourceMappingURL=chunk-MF25NAZ4.js.map
@@ -862,7 +862,9 @@ var DragDropInteractionMixin = (superClass, draggablesSelector, droppablesSelect
862
862
  element.dispatchEvent(event);
863
863
  }
864
864
  appendClone() {
865
- document.body.appendChild(this.dragClone);
865
+ const fullscreenRoot = document.fullscreenElement;
866
+ const target = fullscreenRoot ?? document.body;
867
+ target.appendChild(this.dragClone);
866
868
  }
867
869
  handleTouchStart(e5) {
868
870
  if (this.isMatchTabular()) return;
@@ -985,7 +987,7 @@ var qti_associate_interaction_styles_default = i`
985
987
  align-items: flex-start;
986
988
  flex: 1;
987
989
  border: 2px solid transparent;
988
- padding: 0.3rem;
990
+ margin: 1rem 0;
989
991
  border-radius: 0.3rem;
990
992
  gap: 0.5rem;
991
993
  }
@@ -995,18 +997,23 @@ var qti_associate_interaction_styles_default = i`
995
997
  background-color: var(--qti-bg-active) !important;
996
998
  }
997
999
 
1000
+ [part='drop-container'] {
1001
+ display: flex;
1002
+ flex-direction: column;
1003
+ gap: 0.5rem;
1004
+ }
1005
+
998
1006
  [part='drop-list'][enabled] {
999
1007
  background-color: var(--qti-bg-active) !important;
1000
1008
  }
1001
1009
 
1002
1010
  :host::part(associables-container) {
1003
1011
  display: flex;
1004
- padding: 0.5rem;
1005
1012
  justify-content: space-between;
1006
1013
  background: linear-gradient(
1007
1014
  180deg,
1008
1015
  rgb(0 0 0 / 0%) calc(50% - 1px),
1009
- var(--qti-border-color-gray) calc(50%),
1016
+ var(--qti-border-color) calc(50%),
1010
1017
  rgb(0 0 0 / 0%) calc(50% + 1px)
1011
1018
  );
1012
1019
  }
@@ -4110,6 +4117,17 @@ var QtiPortableCustomInteraction = class extends Interaction {
4110
4117
  this.iframe.style.width = `${data.width}px`;
4111
4118
  }
4112
4119
  break;
4120
+ case "console":
4121
+ this.dispatchEvent(
4122
+ new CustomEvent("qti-pci-console", {
4123
+ detail: {
4124
+ level: data.level,
4125
+ args: data.args
4126
+ },
4127
+ bubbles: true
4128
+ })
4129
+ );
4130
+ break;
4113
4131
  case "interactionChanged": {
4114
4132
  const raw = data?.params?.value;
4115
4133
  const converted = raw && typeof raw === "object" ? this.#convertQtiVariableJSON(raw) : null;
@@ -4828,6 +4846,8 @@ var QtiPortableCustomInteraction = class extends Interaction {
4828
4846
  const parentStyles = window.getComputedStyle(document.body);
4829
4847
  const requirePaths = JSON.stringify(this.#getFinalRequirePaths());
4830
4848
  const requireShim = JSON.stringify(this.#getFinalRequireShim());
4849
+ const iframeBaseUrl = this.dataset.baseUrl ? this.dataset.baseUrl.startsWith("/") ? `${window.location.origin}${this.dataset.baseUrl}` : this.dataset.baseUrl : "";
4850
+ const forwardConsole = this.dataset.forwardConsole === "true";
4831
4851
  const fontStyles = `
4832
4852
  font-family: ${parentStyles.getPropertyValue("font-family")};
4833
4853
  font-size: ${parentStyles.getPropertyValue("font-size")};
@@ -4866,6 +4886,46 @@ var QtiPortableCustomInteraction = class extends Interaction {
4866
4886
  </style>
4867
4887
  <script src="${this.requireJsUrl}"></script>
4868
4888
  <script>
4889
+ const forwardConsole = ${forwardConsole ? "true" : "false"};
4890
+ if (forwardConsole) {
4891
+ const originalLog = console.log.bind(console);
4892
+ const originalError = console.error.bind(console);
4893
+ const stringifyArgs = args =>
4894
+ args.map(arg => {
4895
+ if (typeof arg === 'string') return arg;
4896
+ try {
4897
+ return JSON.stringify(arg);
4898
+ } catch (e) {
4899
+ return String(arg);
4900
+ }
4901
+ });
4902
+ console.log = (...args) => {
4903
+ originalLog(...args);
4904
+ window.parent.postMessage(
4905
+ {
4906
+ source: 'qti-pci-iframe',
4907
+ responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
4908
+ method: 'console',
4909
+ level: 'log',
4910
+ args: stringifyArgs(args)
4911
+ },
4912
+ '*'
4913
+ );
4914
+ };
4915
+ console.error = (...args) => {
4916
+ originalError(...args);
4917
+ window.parent.postMessage(
4918
+ {
4919
+ source: 'qti-pci-iframe',
4920
+ responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
4921
+ method: 'console',
4922
+ level: 'error',
4923
+ args: stringifyArgs(args)
4924
+ },
4925
+ '*'
4926
+ );
4927
+ };
4928
+ }
4869
4929
  // Define standard paths and shims
4870
4930
  window.requirePaths = ${requirePaths};
4871
4931
 
@@ -4876,7 +4936,7 @@ var QtiPortableCustomInteraction = class extends Interaction {
4876
4936
  catchError: true,
4877
4937
  waitSeconds: 30,
4878
4938
  paths: window.requirePaths,
4879
- baseUrl: '${this.dataset.baseUrl}',
4939
+ baseUrl: '${iframeBaseUrl}',
4880
4940
  shim: window.requireShim,
4881
4941
  onNodeCreated: function(node, config, moduleName, url) {
4882
4942
  // Add error handler to script node
@@ -5426,7 +5486,18 @@ var QtiPortableCustomInteraction = class extends Interaction {
5426
5486
  const x = data.params && data.params.x;
5427
5487
  const y = data.params && data.params.y;
5428
5488
  const el = typeof x === 'number' && typeof y === 'number' ? document.elementFromPoint(x, y) : null;
5429
- if (el && typeof el.click === 'function') el.click();
5489
+ const target = (el && el.closest && el.closest('.hitbox')) || document.querySelector('.hitbox') || el;
5490
+ if (target) {
5491
+ const evt = new MouseEvent('click', {
5492
+ bubbles: true,
5493
+ clientX: x,
5494
+ clientY: y,
5495
+ screenX: x,
5496
+ screenY: y,
5497
+ view: window
5498
+ });
5499
+ target.dispatchEvent(evt);
5500
+ }
5430
5501
  window.parent.postMessage(
5431
5502
  {
5432
5503
  source: 'qti-pci-iframe',
@@ -5439,6 +5510,31 @@ var QtiPortableCustomInteraction = class extends Interaction {
5439
5510
  break;
5440
5511
  }
5441
5512
 
5513
+ case 'getBoundingRect': {
5514
+ const messageId = data.params && data.params.messageId;
5515
+ const selector = data.params && data.params.selector;
5516
+ const el = selector ? deepQuerySelector(document, selector) : null;
5517
+ const rect = el && typeof el.getBoundingClientRect === 'function' ? el.getBoundingClientRect() : null;
5518
+ window.parent.postMessage(
5519
+ {
5520
+ source: 'qti-pci-iframe',
5521
+ responseIdentifier: PCIManager.responseIdentifier,
5522
+ method: 'getBoundingRectResponse',
5523
+ messageId: messageId,
5524
+ rect: rect
5525
+ ? {
5526
+ left: rect.left,
5527
+ top: rect.top,
5528
+ width: rect.width,
5529
+ height: rect.height
5530
+ }
5531
+ : null
5532
+ },
5533
+ '*'
5534
+ );
5535
+ break;
5536
+ }
5537
+
5442
5538
  case 'clickOnSelector': {
5443
5539
  const messageId = data.params && data.params.messageId;
5444
5540
  const selector = data.params && data.params.selector;
@@ -5505,6 +5601,19 @@ var QtiPortableCustomInteraction = class extends Interaction {
5505
5601
  );
5506
5602
  break;
5507
5603
  }
5604
+
5605
+ case 'console':
5606
+ window.parent.postMessage(
5607
+ {
5608
+ source: 'qti-pci-iframe',
5609
+ responseIdentifier: PCIManager.responseIdentifier,
5610
+ method: 'console',
5611
+ level: data.level,
5612
+ args: data.args
5613
+ },
5614
+ '*'
5615
+ );
5616
+ break;
5508
5617
  }
5509
5618
  });
5510
5619
 
@@ -7364,6 +7473,29 @@ var QtiPortableCustomInteractionTest = class extends QtiPortableCustomInteractio
7364
7473
  }, 5e3);
7365
7474
  });
7366
7475
  }
7476
+ /**
7477
+ * Gets the bounding client rect for an element inside the iframe
7478
+ * @param selector The CSS selector string
7479
+ * @returns Promise that resolves with the rect or null
7480
+ */
7481
+ async iFrameGetBoundingClientRect(selector) {
7482
+ return new Promise((resolve) => {
7483
+ const messageId = `get-bounding-rect-${Date.now()}`;
7484
+ const messageHandler = (event) => {
7485
+ const { data } = event;
7486
+ if (data?.source === "qti-pci-iframe" && (!data?.responseIdentifier || data?.responseIdentifier === this.responseIdentifier) && data?.method === "getBoundingRectResponse" && data?.messageId === messageId) {
7487
+ window.removeEventListener("message", messageHandler);
7488
+ resolve(data.rect ?? null);
7489
+ }
7490
+ };
7491
+ window.addEventListener("message", messageHandler);
7492
+ this.sendMessageToIframe("getBoundingRect", { selector, messageId });
7493
+ setTimeout(() => {
7494
+ window.removeEventListener("message", messageHandler);
7495
+ resolve(null);
7496
+ }, 5e3);
7497
+ });
7498
+ }
7367
7499
  /**
7368
7500
  * Sets the value of an input element containing the specified text
7369
7501
  * @param text The text to search for within elements
@@ -7725,4 +7857,4 @@ lit-html/node/directives/ref.js:
7725
7857
  * SPDX-License-Identifier: BSD-3-Clause
7726
7858
  *)
7727
7859
  */
7728
- //# sourceMappingURL=chunk-WVQHQNRT.js.map
7860
+ //# sourceMappingURL=chunk-NUNAE73U.js.map