@qti-editor/core 1.1.1 → 1.3.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.
package/dist/composer/index.d.ts
CHANGED
|
@@ -34,8 +34,39 @@ export interface ResponseDeclaration {
|
|
|
34
34
|
}>;
|
|
35
35
|
};
|
|
36
36
|
sourceTag: string;
|
|
37
|
+
score?: number;
|
|
37
38
|
}
|
|
38
39
|
export declare function extractResponseDeclarations(itemBodyRoot?: Element | null): ResponseDeclaration[];
|
|
39
40
|
export declare function buildAssessmentItemXml(itemContext?: ComposerItemContext): string;
|
|
41
|
+
/**
|
|
42
|
+
* Build multiple QTI assessment items from a single editor document.
|
|
43
|
+
*
|
|
44
|
+
* Splits the item body at qti-item-divider elements and generates
|
|
45
|
+
* a separate <qti-assessment-item> for each segment.
|
|
46
|
+
*
|
|
47
|
+
* @returns XML string containing multiple assessment items wrapped in a container,
|
|
48
|
+
* or a single assessment item if no dividers are found.
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildMultipleAssessmentItemsXml(itemContext?: ComposerItemContext): string;
|
|
51
|
+
/**
|
|
52
|
+
* Build a single QTI assessment item, converting any dividers to <hr /> elements.
|
|
53
|
+
* Use this when you want to export the entire editor content as one item.
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildSingleAssessmentItemXml(itemContext?: ComposerItemContext): string;
|
|
56
|
+
/**
|
|
57
|
+
* Count how many items would be generated from an item body document.
|
|
58
|
+
* Returns 1 if no dividers, or dividers.length + 1 otherwise.
|
|
59
|
+
*/
|
|
60
|
+
export declare function countItemFragments(itemContext?: ComposerItemContext): number;
|
|
61
|
+
/**
|
|
62
|
+
* Get an array of individual assessment item XMLs.
|
|
63
|
+
* Each item is generated from a segment between dividers.
|
|
64
|
+
* Returns an array with identifier and XML for each item.
|
|
65
|
+
*/
|
|
66
|
+
export declare function getItemFragmentXmls(itemContext?: ComposerItemContext): Array<{
|
|
67
|
+
identifier: string;
|
|
68
|
+
title: string;
|
|
69
|
+
xml: string;
|
|
70
|
+
}>;
|
|
40
71
|
export declare function formatXml(xml: string): string;
|
|
41
72
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composer/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAErE,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC/C,QAAQ,EAAE,YAAY,GAAG,cAAc,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;YACpB,aAAa,EAAE,OAAO,CAAC;SACxB,CAAC,CAAC;KACJ,CAAC;IACF,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,WAAW,CAAC,EAAE;QACZ,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,CAAC;YACb,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC;YACzB,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACJ,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composer/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAErE,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC/C,QAAQ,EAAE,YAAY,GAAG,cAAc,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;YACpB,aAAa,EAAE,OAAO,CAAC;SACxB,CAAC,CAAC;KACJ,CAAC;IACF,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,WAAW,CAAC,EAAE;QACZ,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,CAAC;YACb,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC;YACzB,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACJ,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoCD,wBAAgB,2BAA2B,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,mBAAmB,EAAE,CAOhG;AAkFD,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAqHhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAkCzF;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAUtF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAK5E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAyChI;AAgMD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4B7C"}
|
package/dist/composer/index.js
CHANGED
|
@@ -40,6 +40,74 @@ export function extractResponseDeclarations(itemBodyRoot) {
|
|
|
40
40
|
const { declarations } = composeAndNormalizeItemBody(tempRoot, tempDoc);
|
|
41
41
|
return declarations;
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Convert qti-item-divider elements to <hr /> elements.
|
|
45
|
+
* Used when composing a single item that should preserve dividers as visual separators.
|
|
46
|
+
*/
|
|
47
|
+
function convertDividersToHr(itemBodyDoc) {
|
|
48
|
+
const itemBodyRoot = itemBodyDoc.querySelector('qti-item-body') ??
|
|
49
|
+
(itemBodyDoc.documentElement?.tagName.toLowerCase() === 'qti-item-body'
|
|
50
|
+
? itemBodyDoc.documentElement
|
|
51
|
+
: null);
|
|
52
|
+
if (!itemBodyRoot)
|
|
53
|
+
return itemBodyDoc;
|
|
54
|
+
// Clone the document to avoid mutating the original
|
|
55
|
+
const clonedDoc = document.implementation.createDocument(QTI_NS, 'qti-item-body', null);
|
|
56
|
+
const clonedRoot = clonedDoc.importNode(itemBodyRoot, true);
|
|
57
|
+
clonedDoc.replaceChild(clonedRoot, clonedDoc.documentElement);
|
|
58
|
+
// Find and replace all dividers with <hr />
|
|
59
|
+
const dividers = Array.from(clonedRoot.querySelectorAll('qti-item-divider'));
|
|
60
|
+
for (const divider of dividers) {
|
|
61
|
+
const hr = clonedDoc.createElementNS(QTI_NS, 'hr');
|
|
62
|
+
divider.parentNode?.replaceChild(hr, divider);
|
|
63
|
+
}
|
|
64
|
+
return clonedDoc;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Split an item body document at qti-item-divider elements.
|
|
68
|
+
* Returns an array of item body fragments, one for each item.
|
|
69
|
+
*/
|
|
70
|
+
function splitItemBodyAtDividers(itemBodyDoc) {
|
|
71
|
+
const itemBodyRoot = itemBodyDoc.querySelector('qti-item-body') ??
|
|
72
|
+
(itemBodyDoc.documentElement?.tagName.toLowerCase() === 'qti-item-body'
|
|
73
|
+
? itemBodyDoc.documentElement
|
|
74
|
+
: null);
|
|
75
|
+
if (!itemBodyRoot)
|
|
76
|
+
return [];
|
|
77
|
+
// Find all dividers
|
|
78
|
+
const dividers = Array.from(itemBodyRoot.querySelectorAll('qti-item-divider'));
|
|
79
|
+
// If no dividers, return the whole body as a single item
|
|
80
|
+
if (dividers.length === 0) {
|
|
81
|
+
return [itemBodyRoot];
|
|
82
|
+
}
|
|
83
|
+
const fragments = [];
|
|
84
|
+
const children = Array.from(itemBodyRoot.childNodes);
|
|
85
|
+
let currentFragment = [];
|
|
86
|
+
for (const child of children) {
|
|
87
|
+
// Check if this child is a divider
|
|
88
|
+
if (child.nodeType === Node.ELEMENT_NODE &&
|
|
89
|
+
child.tagName.toLowerCase() === 'qti-item-divider') {
|
|
90
|
+
// Save the current fragment if it has content
|
|
91
|
+
if (currentFragment.length > 0) {
|
|
92
|
+
const fragmentBody = itemBodyDoc.createElementNS(QTI_NS, 'qti-item-body');
|
|
93
|
+
currentFragment.forEach(node => fragmentBody.appendChild(node.cloneNode(true)));
|
|
94
|
+
fragments.push(fragmentBody);
|
|
95
|
+
currentFragment = [];
|
|
96
|
+
}
|
|
97
|
+
// Skip the divider itself - it doesn't go into any fragment
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
currentFragment.push(child);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Add the last fragment if it has content
|
|
104
|
+
if (currentFragment.length > 0) {
|
|
105
|
+
const fragmentBody = itemBodyDoc.createElementNS(QTI_NS, 'qti-item-body');
|
|
106
|
+
currentFragment.forEach(node => fragmentBody.appendChild(node.cloneNode(true)));
|
|
107
|
+
fragments.push(fragmentBody);
|
|
108
|
+
}
|
|
109
|
+
return fragments;
|
|
110
|
+
}
|
|
43
111
|
export function buildAssessmentItemXml(itemContext) {
|
|
44
112
|
if (!itemContext)
|
|
45
113
|
return '';
|
|
@@ -137,6 +205,109 @@ export function buildAssessmentItemXml(itemContext) {
|
|
|
137
205
|
}
|
|
138
206
|
return new XMLSerializer().serializeToString(xmlDoc);
|
|
139
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Build multiple QTI assessment items from a single editor document.
|
|
210
|
+
*
|
|
211
|
+
* Splits the item body at qti-item-divider elements and generates
|
|
212
|
+
* a separate <qti-assessment-item> for each segment.
|
|
213
|
+
*
|
|
214
|
+
* @returns XML string containing multiple assessment items wrapped in a container,
|
|
215
|
+
* or a single assessment item if no dividers are found.
|
|
216
|
+
*/
|
|
217
|
+
export function buildMultipleAssessmentItemsXml(itemContext) {
|
|
218
|
+
if (!itemContext?.itemBody)
|
|
219
|
+
return '';
|
|
220
|
+
const fragments = splitItemBodyAtDividers(itemContext.itemBody);
|
|
221
|
+
// If only one fragment (no dividers), use the regular single-item builder
|
|
222
|
+
if (fragments.length <= 1) {
|
|
223
|
+
return buildAssessmentItemXml(itemContext);
|
|
224
|
+
}
|
|
225
|
+
// Build multiple items
|
|
226
|
+
const baseIdentifier = itemContext.identifier?.trim() || 'item';
|
|
227
|
+
const baseTitle = itemContext.title?.trim() || 'Untitled Item';
|
|
228
|
+
const lang = itemContext.lang?.trim() || 'en';
|
|
229
|
+
const itemXmls = fragments.map((fragmentBody, index) => {
|
|
230
|
+
const itemNumber = index + 1;
|
|
231
|
+
const fragmentDoc = document.implementation.createDocument(QTI_NS, 'qti-item-body', null);
|
|
232
|
+
const importedFragment = fragmentDoc.importNode(fragmentBody, true);
|
|
233
|
+
fragmentDoc.replaceChild(importedFragment, fragmentDoc.documentElement);
|
|
234
|
+
const fragmentContext = {
|
|
235
|
+
identifier: `${baseIdentifier}-${itemNumber}`,
|
|
236
|
+
title: `${baseTitle} ${itemNumber}`,
|
|
237
|
+
lang,
|
|
238
|
+
itemBody: fragmentDoc,
|
|
239
|
+
};
|
|
240
|
+
return buildAssessmentItemXml(fragmentContext);
|
|
241
|
+
});
|
|
242
|
+
// Join multiple items with a comment separator for clarity
|
|
243
|
+
const separator = '\n\n<!-- Next Assessment Item -->\n\n';
|
|
244
|
+
return itemXmls.join(separator);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Build a single QTI assessment item, converting any dividers to <hr /> elements.
|
|
248
|
+
* Use this when you want to export the entire editor content as one item.
|
|
249
|
+
*/
|
|
250
|
+
export function buildSingleAssessmentItemXml(itemContext) {
|
|
251
|
+
if (!itemContext?.itemBody)
|
|
252
|
+
return '';
|
|
253
|
+
// Convert dividers to <hr /> elements
|
|
254
|
+
const convertedDoc = convertDividersToHr(itemContext.itemBody);
|
|
255
|
+
return buildAssessmentItemXml({
|
|
256
|
+
...itemContext,
|
|
257
|
+
itemBody: convertedDoc,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Count how many items would be generated from an item body document.
|
|
262
|
+
* Returns 1 if no dividers, or dividers.length + 1 otherwise.
|
|
263
|
+
*/
|
|
264
|
+
export function countItemFragments(itemContext) {
|
|
265
|
+
if (!itemContext?.itemBody)
|
|
266
|
+
return 0;
|
|
267
|
+
const fragments = splitItemBodyAtDividers(itemContext.itemBody);
|
|
268
|
+
return fragments.length;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Get an array of individual assessment item XMLs.
|
|
272
|
+
* Each item is generated from a segment between dividers.
|
|
273
|
+
* Returns an array with identifier and XML for each item.
|
|
274
|
+
*/
|
|
275
|
+
export function getItemFragmentXmls(itemContext) {
|
|
276
|
+
if (!itemContext?.itemBody)
|
|
277
|
+
return [];
|
|
278
|
+
const fragments = splitItemBodyAtDividers(itemContext.itemBody);
|
|
279
|
+
// If only one fragment (no dividers), return single item
|
|
280
|
+
if (fragments.length <= 1) {
|
|
281
|
+
const xml = buildAssessmentItemXml(itemContext);
|
|
282
|
+
return [{
|
|
283
|
+
identifier: itemContext.identifier?.trim() || 'item-1',
|
|
284
|
+
title: itemContext.title?.trim() || 'Untitled Item',
|
|
285
|
+
xml,
|
|
286
|
+
}];
|
|
287
|
+
}
|
|
288
|
+
const baseIdentifier = itemContext.identifier?.trim() || 'item';
|
|
289
|
+
const baseTitle = itemContext.title?.trim() || 'Untitled Item';
|
|
290
|
+
const lang = itemContext.lang?.trim() || 'en';
|
|
291
|
+
return fragments.map((fragmentBody, index) => {
|
|
292
|
+
const itemNumber = index + 1;
|
|
293
|
+
const fragmentDoc = document.implementation.createDocument(QTI_NS, 'qti-item-body', null);
|
|
294
|
+
const importedFragment = fragmentDoc.importNode(fragmentBody, true);
|
|
295
|
+
fragmentDoc.replaceChild(importedFragment, fragmentDoc.documentElement);
|
|
296
|
+
const identifier = `${baseIdentifier}-${itemNumber}`;
|
|
297
|
+
const title = `${baseTitle} ${itemNumber}`;
|
|
298
|
+
const fragmentContext = {
|
|
299
|
+
identifier,
|
|
300
|
+
title,
|
|
301
|
+
lang,
|
|
302
|
+
itemBody: fragmentDoc,
|
|
303
|
+
};
|
|
304
|
+
return {
|
|
305
|
+
identifier,
|
|
306
|
+
title,
|
|
307
|
+
xml: buildAssessmentItemXml(fragmentContext),
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}
|
|
140
311
|
function composeAndNormalizeItemBody(itemBody, xmlDoc) {
|
|
141
312
|
const declarations = [];
|
|
142
313
|
const seenIdentifiers = new Set();
|
|
@@ -155,9 +326,21 @@ function composeAndNormalizeItemBody(itemBody, xmlDoc) {
|
|
|
155
326
|
const parent = element.parentNode;
|
|
156
327
|
if (parent) {
|
|
157
328
|
parent.replaceChild(composeResult.normalizedElement, element);
|
|
329
|
+
// Insert any additional elements (like rubric blocks) after the interaction
|
|
330
|
+
if (composeResult.additionalElements?.length) {
|
|
331
|
+
const nextSibling = composeResult.normalizedElement.nextSibling;
|
|
332
|
+
for (const additionalElement of composeResult.additionalElements) {
|
|
333
|
+
if (nextSibling) {
|
|
334
|
+
parent.insertBefore(additionalElement, nextSibling);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
parent.appendChild(additionalElement);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
158
341
|
}
|
|
159
342
|
if (composeResult.responseDeclaration) {
|
|
160
|
-
maxScore += 1;
|
|
343
|
+
maxScore += composeResult.responseDeclaration.score ?? 1;
|
|
161
344
|
}
|
|
162
345
|
if (composeResult.responseDeclaration && !seenIdentifiers.has(composeResult.responseDeclaration.identifier)) {
|
|
163
346
|
const responseProcessingKind = composeResult
|
|
@@ -180,6 +363,9 @@ function composeAndNormalizeItemBody(itemBody, xmlDoc) {
|
|
|
180
363
|
itemBody.querySelectorAll('[correct-response]').forEach(interaction => {
|
|
181
364
|
interaction.removeAttribute('correct-response');
|
|
182
365
|
});
|
|
366
|
+
itemBody.querySelectorAll('[score]').forEach(element => {
|
|
367
|
+
element.removeAttribute('score');
|
|
368
|
+
});
|
|
183
369
|
normalizeResponseIdentifiers(itemBody, declarations);
|
|
184
370
|
if (declarations.length === 1 && templateCandidates.size === 1) {
|
|
185
371
|
return { declarations, responseTemplate: Array.from(templateCandidates)[0], maxScore };
|
|
@@ -191,7 +377,7 @@ function buildMultiInteractionResponseProcessing(xmlDoc, declarations) {
|
|
|
191
377
|
declarations.forEach(declaration => {
|
|
192
378
|
const kind = declaration.responseProcessingKind ?? 'match_correct';
|
|
193
379
|
if (kind === 'match_correct') {
|
|
194
|
-
responseProcessing.appendChild(createMatchCorrectContribution(xmlDoc, declaration.identifier));
|
|
380
|
+
responseProcessing.appendChild(createMatchCorrectContribution(xmlDoc, declaration.identifier, declaration.score ?? 1));
|
|
195
381
|
return;
|
|
196
382
|
}
|
|
197
383
|
if (kind === 'map_response') {
|
|
@@ -202,7 +388,7 @@ function buildMultiInteractionResponseProcessing(xmlDoc, declarations) {
|
|
|
202
388
|
});
|
|
203
389
|
return responseProcessing;
|
|
204
390
|
}
|
|
205
|
-
function createMatchCorrectContribution(xmlDoc, responseIdentifier) {
|
|
391
|
+
function createMatchCorrectContribution(xmlDoc, responseIdentifier, score = 1) {
|
|
206
392
|
const responseCondition = xmlDoc.createElementNS(QTI_NS, 'qti-response-condition');
|
|
207
393
|
const responseIf = xmlDoc.createElementNS(QTI_NS, 'qti-response-if');
|
|
208
394
|
const match = xmlDoc.createElementNS(QTI_NS, 'qti-match');
|
|
@@ -215,7 +401,7 @@ function createMatchCorrectContribution(xmlDoc, responseIdentifier) {
|
|
|
215
401
|
responseIf.appendChild(match);
|
|
216
402
|
const incrementValue = xmlDoc.createElementNS(QTI_NS, 'qti-base-value');
|
|
217
403
|
incrementValue.setAttribute('base-type', 'float');
|
|
218
|
-
incrementValue.textContent =
|
|
404
|
+
incrementValue.textContent = String(score);
|
|
219
405
|
responseIf.appendChild(createScoreIncrement(xmlDoc, incrementValue));
|
|
220
406
|
responseCondition.appendChild(responseIf);
|
|
221
407
|
return responseCondition;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/interactions/composer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/interactions/composer.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AAoChC,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS,CAEvG;AAED,wBAAgB,4CAA4C,CAC1D,YAAY,EAAE,MAAM,GACnB,2BAA2B,GAAG,SAAS,CAEzC;AAED,wBAAgB,2CAA2C,CACzD,YAAY,EAAE,MAAM,GACnB,0BAA0B,GAAG,SAAS,CAExC;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,GAAG,SAAS,CAErG;AAED,wBAAgB,+BAA+B,IAAI,aAAa,CAAC,0BAA0B,CAAC,CAE3F;AAED,wBAAgB,0BAA0B,IAAI,aAAa,CAAC,qBAAqB,CAAC,CAEjF;AAED,wBAAgB,8BAA8B,IAAI,aAAa,CAC7D,WAAW,CAAC,qBAAqB,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,CAC9D,CAEA"}
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { associateInteractionDescriptor } from '@qti-editor/interaction-associate';
|
|
2
2
|
import { choiceInteractionDescriptor } from '@qti-editor/interaction-choice';
|
|
3
3
|
import { extendedTextInteractionDescriptor } from '@qti-editor/interaction-extended-text';
|
|
4
|
+
import { gapMatchInteractionDescriptor } from '@qti-editor/interaction-gap-match';
|
|
4
5
|
import { hottextInteractionDescriptor } from '@qti-editor/interaction-hottext';
|
|
5
6
|
import { inlineChoiceInteractionDescriptor } from '@qti-editor/interaction-inline-choice';
|
|
6
7
|
import { matchInteractionDescriptor } from '@qti-editor/interaction-match';
|
|
7
8
|
import { orderInteractionDescriptor } from '@qti-editor/interaction-order';
|
|
8
9
|
import { selectPointInteractionDescriptor } from '@qti-editor/interaction-select-point';
|
|
9
10
|
import { textEntryInteractionDescriptor } from '@qti-editor/interaction-text-entry';
|
|
11
|
+
import { qtiItemDividerDescriptor } from '@qti-editor/qti-item-divider';
|
|
10
12
|
const registeredDescriptors = [
|
|
11
13
|
associateInteractionDescriptor,
|
|
12
14
|
choiceInteractionDescriptor,
|
|
13
15
|
extendedTextInteractionDescriptor,
|
|
16
|
+
gapMatchInteractionDescriptor,
|
|
14
17
|
hottextInteractionDescriptor,
|
|
15
18
|
inlineChoiceInteractionDescriptor,
|
|
16
19
|
matchInteractionDescriptor,
|
|
17
20
|
orderInteractionDescriptor,
|
|
18
21
|
selectPointInteractionDescriptor,
|
|
19
22
|
textEntryInteractionDescriptor,
|
|
23
|
+
qtiItemDividerDescriptor,
|
|
20
24
|
];
|
|
21
25
|
const metadataByTagName = new Map(registeredDescriptors.map(d => [d.tagName, d.composerMetadata]));
|
|
22
26
|
const metadataByNodeTypeName = new Map(registeredDescriptors.map(d => [d.nodeTypeName.toLowerCase(), d.composerMetadata]));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qti-editor/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "QTI semantics, composer registry, and XML export orchestration for QTI Editor",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,17 +24,19 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@qti-editor/interfaces": "1.
|
|
28
|
-
"@qti-editor/interaction-associate": "1.1
|
|
29
|
-
"@qti-editor/interaction-choice": "1.1
|
|
30
|
-
"@qti-editor/interaction-extended-text": "1.1
|
|
31
|
-
"@qti-editor/interaction-
|
|
32
|
-
"@qti-editor/interaction-
|
|
33
|
-
"@qti-editor/interaction-
|
|
34
|
-
"@qti-editor/interaction-
|
|
35
|
-
"@qti-editor/interaction-
|
|
36
|
-
"@qti-editor/interaction-
|
|
37
|
-
"@qti-editor/interaction-
|
|
27
|
+
"@qti-editor/interfaces": "1.2.0",
|
|
28
|
+
"@qti-editor/interaction-associate": "1.2.1",
|
|
29
|
+
"@qti-editor/interaction-choice": "1.2.1",
|
|
30
|
+
"@qti-editor/interaction-extended-text": "1.2.1",
|
|
31
|
+
"@qti-editor/interaction-gap-match": "1.1.0",
|
|
32
|
+
"@qti-editor/interaction-hottext": "1.2.1",
|
|
33
|
+
"@qti-editor/interaction-inline-choice": "1.2.1",
|
|
34
|
+
"@qti-editor/interaction-match": "1.2.0",
|
|
35
|
+
"@qti-editor/interaction-order": "0.6.0",
|
|
36
|
+
"@qti-editor/interaction-select-point": "1.2.0",
|
|
37
|
+
"@qti-editor/interaction-shared": "1.3.0",
|
|
38
|
+
"@qti-editor/interaction-text-entry": "1.2.0",
|
|
39
|
+
"@qti-editor/qti-item-divider": "1.1.0"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@types/node": "^20.0.0",
|