@qti-components/processing 1.4.0 → 1.5.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/components/qti-printed-variable/printed-variable-format.d.ts +19 -0
- package/dist/components/qti-printed-variable/printed-variable-format.js +113 -0
- package/dist/components/qti-printed-variable/printed-variable-format.js.map +1 -0
- package/dist/components/qti-printed-variable/qti-printed-variable.d.ts +26 -0
- package/dist/components/qti-printed-variable/qti-printed-variable.js +96 -2
- package/dist/components/qti-printed-variable/qti-printed-variable.js.map +1 -1
- package/dist/components/qti-variable/qti-variable.d.ts +1 -1
- package/dist/components/qti-variable/qti-variable.js +5 -3
- package/dist/components/qti-variable/qti-variable.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number formatting for `qti-printed-variable`.
|
|
3
|
+
*
|
|
4
|
+
* QTI's `format` attribute carries a C `printf`-style conversion string — QTI 3.0 calls this
|
|
5
|
+
* "the QTI number formatting rules" and points at printf for the grammar. A printed variable has
|
|
6
|
+
* exactly one value to print, so every conversion specifier in the string receives that same
|
|
7
|
+
* value; `%%` is a literal percent and any other text is passed through.
|
|
8
|
+
*
|
|
9
|
+
* Supported: flags `-`, `+`, ` `, `0`, `#`; a width; a precision; and the conversions
|
|
10
|
+
* `d i u o x X f F e E g G s c`. Anything the grammar does not cover is left in place, so a
|
|
11
|
+
* malformed format string degrades to visible text rather than throwing inside `render()`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function toExponential(value: number, precision?: number): string;
|
|
14
|
+
/**
|
|
15
|
+
* Renders `value` through a QTI `format` string. The value is passed as the raw string held in
|
|
16
|
+
* the item context, so integers and floats arrive as `"5"` / `"3.14"` and are coerced per
|
|
17
|
+
* conversion.
|
|
18
|
+
*/
|
|
19
|
+
export declare function applyFormat(format: string, value: string): string;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number formatting for `qti-printed-variable`.
|
|
3
|
+
*
|
|
4
|
+
* QTI's `format` attribute carries a C `printf`-style conversion string — QTI 3.0 calls this
|
|
5
|
+
* "the QTI number formatting rules" and points at printf for the grammar. A printed variable has
|
|
6
|
+
* exactly one value to print, so every conversion specifier in the string receives that same
|
|
7
|
+
* value; `%%` is a literal percent and any other text is passed through.
|
|
8
|
+
*
|
|
9
|
+
* Supported: flags `-`, `+`, ` `, `0`, `#`; a width; a precision; and the conversions
|
|
10
|
+
* `d i u o x X f F e E g G s c`. Anything the grammar does not cover is left in place, so a
|
|
11
|
+
* malformed format string degrades to visible text rather than throwing inside `render()`.
|
|
12
|
+
*/
|
|
13
|
+
const CONVERSION = /%(?:(%)|([-+ 0#]*)(\d+)?(?:\.(\d+))?([diuoxXfFeEgGsc]))/g;
|
|
14
|
+
/** JS writes `1e+3`; C (and therefore QTI) writes at least two exponent digits. */
|
|
15
|
+
function padExponent(exponential) {
|
|
16
|
+
return exponential.replace(/e([+-])(\d)$/, 'e$10$2');
|
|
17
|
+
}
|
|
18
|
+
export function toExponential(value, precision) {
|
|
19
|
+
return padExponent(precision === undefined ? value.toExponential() : value.toExponential(precision));
|
|
20
|
+
}
|
|
21
|
+
function stripTrailingZeros(formatted) {
|
|
22
|
+
const [mantissa, exponent] = formatted.split(/e/i);
|
|
23
|
+
const trimmed = mantissa.includes('.') ? mantissa.replace(/\.?0+$/, '') : mantissa;
|
|
24
|
+
const suffix = exponent === undefined ? '' : `e${exponent}`;
|
|
25
|
+
return `${trimmed}${suffix}`;
|
|
26
|
+
}
|
|
27
|
+
/** C's `%g`: exponential when the exponent falls outside `[-4, precision)`, else fixed, zeros trimmed. */
|
|
28
|
+
function toGeneral(value, precision) {
|
|
29
|
+
const significant = precision === 0 ? 1 : precision;
|
|
30
|
+
const exponent = value === 0 ? 0 : Math.floor(Math.log10(value));
|
|
31
|
+
if (exponent < -4 || exponent >= significant) {
|
|
32
|
+
return stripTrailingZeros(toExponential(value, significant - 1));
|
|
33
|
+
}
|
|
34
|
+
return stripTrailingZeros(value.toFixed(Math.max(0, significant - 1 - exponent)));
|
|
35
|
+
}
|
|
36
|
+
function convert(conversion, flags, precision, raw) {
|
|
37
|
+
if (conversion === 's' || conversion === 'c') {
|
|
38
|
+
const text = conversion === 'c' ? raw.slice(0, 1) : raw;
|
|
39
|
+
return precision === undefined ? text : text.slice(0, precision);
|
|
40
|
+
}
|
|
41
|
+
const numeric = Number(raw);
|
|
42
|
+
// Not a number after all — print what the author gave us instead of `NaN`.
|
|
43
|
+
if (raw.trim() === '' || !Number.isFinite(numeric))
|
|
44
|
+
return raw;
|
|
45
|
+
const magnitude = Math.abs(numeric);
|
|
46
|
+
let body;
|
|
47
|
+
let negative = numeric < 0;
|
|
48
|
+
switch (conversion) {
|
|
49
|
+
case 'd':
|
|
50
|
+
case 'i':
|
|
51
|
+
body = String(Math.trunc(magnitude));
|
|
52
|
+
break;
|
|
53
|
+
case 'u':
|
|
54
|
+
body = String(Math.trunc(magnitude));
|
|
55
|
+
negative = false;
|
|
56
|
+
break;
|
|
57
|
+
case 'o':
|
|
58
|
+
body = `${flags.includes('#') ? '0' : ''}${Math.trunc(magnitude).toString(8)}`;
|
|
59
|
+
break;
|
|
60
|
+
case 'x':
|
|
61
|
+
body = `${flags.includes('#') ? '0x' : ''}${Math.trunc(magnitude).toString(16)}`;
|
|
62
|
+
break;
|
|
63
|
+
case 'X':
|
|
64
|
+
body = `${flags.includes('#') ? '0X' : ''}${Math.trunc(magnitude).toString(16).toUpperCase()}`;
|
|
65
|
+
break;
|
|
66
|
+
case 'f':
|
|
67
|
+
case 'F':
|
|
68
|
+
body = magnitude.toFixed(precision ?? 6);
|
|
69
|
+
break;
|
|
70
|
+
case 'e':
|
|
71
|
+
body = toExponential(magnitude, precision ?? 6);
|
|
72
|
+
break;
|
|
73
|
+
case 'E':
|
|
74
|
+
body = toExponential(magnitude, precision ?? 6).toUpperCase();
|
|
75
|
+
break;
|
|
76
|
+
case 'g':
|
|
77
|
+
body = toGeneral(magnitude, precision ?? 6);
|
|
78
|
+
break;
|
|
79
|
+
case 'G':
|
|
80
|
+
body = toGeneral(magnitude, precision ?? 6).toUpperCase();
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
return raw;
|
|
84
|
+
}
|
|
85
|
+
const sign = negative ? '-' : flags.includes('+') ? '+' : flags.includes(' ') ? ' ' : '';
|
|
86
|
+
return `${sign}${body}`;
|
|
87
|
+
}
|
|
88
|
+
function pad(text, flags, width) {
|
|
89
|
+
if (text.length >= width)
|
|
90
|
+
return text;
|
|
91
|
+
const fill = width - text.length;
|
|
92
|
+
if (flags.includes('-'))
|
|
93
|
+
return `${text}${' '.repeat(fill)}`;
|
|
94
|
+
if (flags.includes('0')) {
|
|
95
|
+
// Zero padding goes between the sign and the digits, never before the sign.
|
|
96
|
+
const [, sign, digits] = /^([-+ ]?)(.*)$/.exec(text);
|
|
97
|
+
return `${sign}${'0'.repeat(fill)}${digits}`;
|
|
98
|
+
}
|
|
99
|
+
return `${' '.repeat(fill)}${text}`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Renders `value` through a QTI `format` string. The value is passed as the raw string held in
|
|
103
|
+
* the item context, so integers and floats arrive as `"5"` / `"3.14"` and are coerced per
|
|
104
|
+
* conversion.
|
|
105
|
+
*/
|
|
106
|
+
export function applyFormat(format, value) {
|
|
107
|
+
return format.replace(CONVERSION, (_match, percent, flags, width, precision, conversion) => {
|
|
108
|
+
if (percent)
|
|
109
|
+
return '%';
|
|
110
|
+
return pad(convert(conversion, flags ?? '', precision === undefined ? undefined : Number(precision), value), flags ?? '', width === undefined ? 0 : Number(width));
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=printed-variable-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"printed-variable-format.js","sourceRoot":"","sources":["../../../src/components/qti-printed-variable/printed-variable-format.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Number formatting for `qti-printed-variable`.\n *\n * QTI's `format` attribute carries a C `printf`-style conversion string — QTI 3.0 calls this\n * \"the QTI number formatting rules\" and points at printf for the grammar. A printed variable has\n * exactly one value to print, so every conversion specifier in the string receives that same\n * value; `%%` is a literal percent and any other text is passed through.\n *\n * Supported: flags `-`, `+`, ` `, `0`, `#`; a width; a precision; and the conversions\n * `d i u o x X f F e E g G s c`. Anything the grammar does not cover is left in place, so a\n * malformed format string degrades to visible text rather than throwing inside `render()`.\n */\n\nconst CONVERSION = /%(?:(%)|([-+ 0#]*)(\\d+)?(?:\\.(\\d+))?([diuoxXfFeEgGsc]))/g;\n\n/** JS writes `1e+3`; C (and therefore QTI) writes at least two exponent digits. */\nfunction padExponent(exponential: string): string {\n return exponential.replace(/e([+-])(\\d)$/, 'e$10$2');\n}\n\nexport function toExponential(value: number, precision?: number): string {\n return padExponent(precision === undefined ? value.toExponential() : value.toExponential(precision));\n}\n\nfunction stripTrailingZeros(formatted: string): string {\n const [mantissa, exponent] = formatted.split(/e/i);\n const trimmed = mantissa.includes('.') ? mantissa.replace(/\\.?0+$/, '') : mantissa;\n const suffix = exponent === undefined ? '' : `e${exponent}`;\n return `${trimmed}${suffix}`;\n}\n\n/** C's `%g`: exponential when the exponent falls outside `[-4, precision)`, else fixed, zeros trimmed. */\nfunction toGeneral(value: number, precision: number): string {\n const significant = precision === 0 ? 1 : precision;\n const exponent = value === 0 ? 0 : Math.floor(Math.log10(value));\n\n if (exponent < -4 || exponent >= significant) {\n return stripTrailingZeros(toExponential(value, significant - 1));\n }\n return stripTrailingZeros(value.toFixed(Math.max(0, significant - 1 - exponent)));\n}\n\nfunction convert(conversion: string, flags: string, precision: number | undefined, raw: string): string {\n if (conversion === 's' || conversion === 'c') {\n const text = conversion === 'c' ? raw.slice(0, 1) : raw;\n return precision === undefined ? text : text.slice(0, precision);\n }\n\n const numeric = Number(raw);\n // Not a number after all — print what the author gave us instead of `NaN`.\n if (raw.trim() === '' || !Number.isFinite(numeric)) return raw;\n\n const magnitude = Math.abs(numeric);\n let body: string;\n let negative = numeric < 0;\n\n switch (conversion) {\n case 'd':\n case 'i':\n body = String(Math.trunc(magnitude));\n break;\n case 'u':\n body = String(Math.trunc(magnitude));\n negative = false;\n break;\n case 'o':\n body = `${flags.includes('#') ? '0' : ''}${Math.trunc(magnitude).toString(8)}`;\n break;\n case 'x':\n body = `${flags.includes('#') ? '0x' : ''}${Math.trunc(magnitude).toString(16)}`;\n break;\n case 'X':\n body = `${flags.includes('#') ? '0X' : ''}${Math.trunc(magnitude).toString(16).toUpperCase()}`;\n break;\n case 'f':\n case 'F':\n body = magnitude.toFixed(precision ?? 6);\n break;\n case 'e':\n body = toExponential(magnitude, precision ?? 6);\n break;\n case 'E':\n body = toExponential(magnitude, precision ?? 6).toUpperCase();\n break;\n case 'g':\n body = toGeneral(magnitude, precision ?? 6);\n break;\n case 'G':\n body = toGeneral(magnitude, precision ?? 6).toUpperCase();\n break;\n default:\n return raw;\n }\n\n const sign = negative ? '-' : flags.includes('+') ? '+' : flags.includes(' ') ? ' ' : '';\n return `${sign}${body}`;\n}\n\nfunction pad(text: string, flags: string, width: number): string {\n if (text.length >= width) return text;\n const fill = width - text.length;\n\n if (flags.includes('-')) return `${text}${' '.repeat(fill)}`;\n if (flags.includes('0')) {\n // Zero padding goes between the sign and the digits, never before the sign.\n const [, sign, digits] = /^([-+ ]?)(.*)$/.exec(text)!;\n return `${sign}${'0'.repeat(fill)}${digits}`;\n }\n return `${' '.repeat(fill)}${text}`;\n}\n\n/**\n * Renders `value` through a QTI `format` string. The value is passed as the raw string held in\n * the item context, so integers and floats arrive as `\"5\"` / `\"3.14\"` and are coerced per\n * conversion.\n */\nexport function applyFormat(format: string, value: string): string {\n return format.replace(CONVERSION, (_match, percent, flags, width, precision, conversion) => {\n if (percent) return '%';\n return pad(\n convert(conversion, flags ?? '', precision === undefined ? undefined : Number(precision), value),\n flags ?? '',\n width === undefined ? 0 : Number(width)\n );\n });\n}\n"]}
|
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
import { LitElement, nothing } from 'lit';
|
|
2
2
|
import type { VariableDeclaration } from '@qti-components/base';
|
|
3
3
|
import type { ItemContext } from '@qti-components/base';
|
|
4
|
+
/**
|
|
5
|
+
* @summary Prints the value of an item variable into the item body.
|
|
6
|
+
* @documentation https://www.imsglobal.org/spec/qti/v3p0/info/index.html#printedVariable
|
|
7
|
+
*
|
|
8
|
+
* A container variable is printed as its values joined by `delimiter`; a record as
|
|
9
|
+
* `name<mapping-indicator>value` pairs joined by the same delimiter. A NULL variable — absent,
|
|
10
|
+
* null, or an empty container — prints nothing at all.
|
|
11
|
+
*
|
|
12
|
+
* @attr {string} identifier - Required. Identifier of the variable to print.
|
|
13
|
+
* @attr {string} [format] - printf-style conversion string, e.g. `%.2f` or `Score: %d`. Applies
|
|
14
|
+
* to each value printed. Takes precedence over `base` and `power-form`.
|
|
15
|
+
* @attr {number} [base=10] - Number base used when printing an integer variable.
|
|
16
|
+
* @attr {number} [index] - 1-based index selecting a single value of an ordered variable.
|
|
17
|
+
* @attr {boolean} [power-form=false] - Print numeric values in exponential form.
|
|
18
|
+
* @attr {string} [field] - Field of a record variable to print, instead of the whole record.
|
|
19
|
+
* @attr {string} [delimiter=;] - Separator between the values of an ordered/multiple/record variable.
|
|
20
|
+
* @attr {string} [mapping-indicator==] - Separator between a record field's name and its value.
|
|
21
|
+
*/
|
|
4
22
|
export declare class QtiPrintedVariable extends LitElement {
|
|
23
|
+
#private;
|
|
5
24
|
identifier: string;
|
|
25
|
+
format?: string;
|
|
26
|
+
base: number;
|
|
27
|
+
index?: number;
|
|
28
|
+
powerForm: boolean;
|
|
29
|
+
field?: string;
|
|
30
|
+
delimiter: string;
|
|
31
|
+
mappingIndicator: string;
|
|
6
32
|
protected context?: ItemContext;
|
|
7
33
|
render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
8
34
|
calculate(): VariableDeclaration<string | string[]>;
|
|
@@ -8,10 +8,83 @@ import { consume } from '@lit/context';
|
|
|
8
8
|
import { LitElement, html, nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import { itemContext } from '@qti-components/base';
|
|
11
|
+
import { applyFormat, toExponential } from './printed-variable-format';
|
|
12
|
+
/**
|
|
13
|
+
* @summary Prints the value of an item variable into the item body.
|
|
14
|
+
* @documentation https://www.imsglobal.org/spec/qti/v3p0/info/index.html#printedVariable
|
|
15
|
+
*
|
|
16
|
+
* A container variable is printed as its values joined by `delimiter`; a record as
|
|
17
|
+
* `name<mapping-indicator>value` pairs joined by the same delimiter. A NULL variable — absent,
|
|
18
|
+
* null, or an empty container — prints nothing at all.
|
|
19
|
+
*
|
|
20
|
+
* @attr {string} identifier - Required. Identifier of the variable to print.
|
|
21
|
+
* @attr {string} [format] - printf-style conversion string, e.g. `%.2f` or `Score: %d`. Applies
|
|
22
|
+
* to each value printed. Takes precedence over `base` and `power-form`.
|
|
23
|
+
* @attr {number} [base=10] - Number base used when printing an integer variable.
|
|
24
|
+
* @attr {number} [index] - 1-based index selecting a single value of an ordered variable.
|
|
25
|
+
* @attr {boolean} [power-form=false] - Print numeric values in exponential form.
|
|
26
|
+
* @attr {string} [field] - Field of a record variable to print, instead of the whole record.
|
|
27
|
+
* @attr {string} [delimiter=;] - Separator between the values of an ordered/multiple/record variable.
|
|
28
|
+
* @attr {string} [mapping-indicator==] - Separator between a record field's name and its value.
|
|
29
|
+
*/
|
|
11
30
|
export class QtiPrintedVariable extends LitElement {
|
|
31
|
+
constructor() {
|
|
32
|
+
super(...arguments);
|
|
33
|
+
this.base = 10;
|
|
34
|
+
this.powerForm = false;
|
|
35
|
+
this.delimiter = ';';
|
|
36
|
+
this.mappingIndicator = '=';
|
|
37
|
+
}
|
|
12
38
|
render() {
|
|
13
|
-
const
|
|
14
|
-
|
|
39
|
+
const variable = this.context?.variables.find(v => v.identifier === this.identifier);
|
|
40
|
+
const printed = variable ? this.#print(variable) : null;
|
|
41
|
+
return printed === null ? nothing : html `${printed}`;
|
|
42
|
+
}
|
|
43
|
+
/** Returns `null` for anything QTI treats as NULL, which prints as nothing. */
|
|
44
|
+
#print(variable) {
|
|
45
|
+
const value = variable.value;
|
|
46
|
+
if (value === null || value === undefined)
|
|
47
|
+
return null;
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
if (value.length === 0)
|
|
50
|
+
return null;
|
|
51
|
+
if (this.index !== undefined) {
|
|
52
|
+
const selected = value[this.index - 1];
|
|
53
|
+
return selected === null || selected === undefined ? null : this.#printValue(selected, variable.baseType);
|
|
54
|
+
}
|
|
55
|
+
return value.map(entry => this.#printValue(entry, variable.baseType)).join(this.delimiter);
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === 'object') {
|
|
58
|
+
const record = value;
|
|
59
|
+
if (this.field !== undefined) {
|
|
60
|
+
const selected = record[this.field];
|
|
61
|
+
return selected === null || selected === undefined ? null : this.#printValue(selected, variable.baseType);
|
|
62
|
+
}
|
|
63
|
+
const fields = Object.entries(record).filter(([, entry]) => entry !== null && entry !== undefined);
|
|
64
|
+
if (fields.length === 0)
|
|
65
|
+
return null;
|
|
66
|
+
return fields
|
|
67
|
+
.map(([name, entry]) => `${name}${this.mappingIndicator}${this.#printValue(entry, variable.baseType)}`)
|
|
68
|
+
.join(this.delimiter);
|
|
69
|
+
}
|
|
70
|
+
return this.#printValue(value, variable.baseType);
|
|
71
|
+
}
|
|
72
|
+
#printValue(value, baseType) {
|
|
73
|
+
// A pair/directedPair arrives as its two identifiers; QTI writes them space separated.
|
|
74
|
+
const raw = Array.isArray(value) ? value.join(' ') : String(value);
|
|
75
|
+
if (this.format)
|
|
76
|
+
return applyFormat(this.format, raw);
|
|
77
|
+
if (baseType === 'integer' && this.base !== 10) {
|
|
78
|
+
const integer = Number.parseInt(raw, 10);
|
|
79
|
+
if (Number.isFinite(integer))
|
|
80
|
+
return integer.toString(this.base);
|
|
81
|
+
}
|
|
82
|
+
if (this.powerForm) {
|
|
83
|
+
const numeric = Number(raw);
|
|
84
|
+
if (raw.trim() !== '' && Number.isFinite(numeric))
|
|
85
|
+
return toExponential(numeric);
|
|
86
|
+
}
|
|
87
|
+
return raw;
|
|
15
88
|
}
|
|
16
89
|
calculate() {
|
|
17
90
|
const result = this.context.variables.find(v => v.identifier === this.identifier) || null;
|
|
@@ -21,6 +94,27 @@ export class QtiPrintedVariable extends LitElement {
|
|
|
21
94
|
__decorate([
|
|
22
95
|
property({ type: String })
|
|
23
96
|
], QtiPrintedVariable.prototype, "identifier", void 0);
|
|
97
|
+
__decorate([
|
|
98
|
+
property({ type: String })
|
|
99
|
+
], QtiPrintedVariable.prototype, "format", void 0);
|
|
100
|
+
__decorate([
|
|
101
|
+
property({ type: Number })
|
|
102
|
+
], QtiPrintedVariable.prototype, "base", void 0);
|
|
103
|
+
__decorate([
|
|
104
|
+
property({ type: Number })
|
|
105
|
+
], QtiPrintedVariable.prototype, "index", void 0);
|
|
106
|
+
__decorate([
|
|
107
|
+
property({ type: Boolean, attribute: 'power-form' })
|
|
108
|
+
], QtiPrintedVariable.prototype, "powerForm", void 0);
|
|
109
|
+
__decorate([
|
|
110
|
+
property({ type: String })
|
|
111
|
+
], QtiPrintedVariable.prototype, "field", void 0);
|
|
112
|
+
__decorate([
|
|
113
|
+
property({ type: String })
|
|
114
|
+
], QtiPrintedVariable.prototype, "delimiter", void 0);
|
|
115
|
+
__decorate([
|
|
116
|
+
property({ type: String, attribute: 'mapping-indicator' })
|
|
117
|
+
], QtiPrintedVariable.prototype, "mappingIndicator", void 0);
|
|
24
118
|
__decorate([
|
|
25
119
|
consume({ context: itemContext, subscribe: true }),
|
|
26
120
|
state()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qti-printed-variable.js","sourceRoot":"","sources":["../../../src/components/qti-printed-variable/qti-printed-variable.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"qti-printed-variable.js","sourceRoot":"","sources":["../../../src/components/qti-printed-variable/qti-printed-variable.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { LitElement, html, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { itemContext } from '@qti-components/base';\n\nimport { applyFormat, toExponential } from './printed-variable-format';\n\nimport type { BaseType } from '@qti-components/base';\nimport type { VariableDeclaration } from '@qti-components/base';\nimport type { ItemContext } from '@qti-components/base';\n\n/**\n * @summary Prints the value of an item variable into the item body.\n * @documentation https://www.imsglobal.org/spec/qti/v3p0/info/index.html#printedVariable\n *\n * A container variable is printed as its values joined by `delimiter`; a record as\n * `name<mapping-indicator>value` pairs joined by the same delimiter. A NULL variable — absent,\n * null, or an empty container — prints nothing at all.\n *\n * @attr {string} identifier - Required. Identifier of the variable to print.\n * @attr {string} [format] - printf-style conversion string, e.g. `%.2f` or `Score: %d`. Applies\n * to each value printed. Takes precedence over `base` and `power-form`.\n * @attr {number} [base=10] - Number base used when printing an integer variable.\n * @attr {number} [index] - 1-based index selecting a single value of an ordered variable.\n * @attr {boolean} [power-form=false] - Print numeric values in exponential form.\n * @attr {string} [field] - Field of a record variable to print, instead of the whole record.\n * @attr {string} [delimiter=;] - Separator between the values of an ordered/multiple/record variable.\n * @attr {string} [mapping-indicator==] - Separator between a record field's name and its value.\n */\nexport class QtiPrintedVariable extends LitElement {\n @property({ type: String })\n identifier: string;\n\n @property({ type: String })\n format?: string;\n\n @property({ type: Number })\n base = 10;\n\n @property({ type: Number })\n index?: number;\n\n @property({ type: Boolean, attribute: 'power-form' })\n powerForm = false;\n\n @property({ type: String })\n field?: string;\n\n @property({ type: String })\n delimiter = ';';\n\n @property({ type: String, attribute: 'mapping-indicator' })\n mappingIndicator = '=';\n\n @consume({ context: itemContext, subscribe: true })\n @state()\n protected context?: ItemContext;\n\n override render() {\n const variable = this.context?.variables.find(v => v.identifier === this.identifier);\n const printed = variable ? this.#print(variable) : null;\n return printed === null ? nothing : html`${printed}`;\n }\n\n /** Returns `null` for anything QTI treats as NULL, which prints as nothing. */\n #print(variable: VariableDeclaration<unknown>): string | null {\n const value = variable.value;\n if (value === null || value === undefined) return null;\n\n if (Array.isArray(value)) {\n if (value.length === 0) return null;\n if (this.index !== undefined) {\n const selected = value[this.index - 1];\n return selected === null || selected === undefined ? null : this.#printValue(selected, variable.baseType);\n }\n return value.map(entry => this.#printValue(entry, variable.baseType)).join(this.delimiter);\n }\n\n if (typeof value === 'object') {\n const record = value as Record<string, unknown>;\n if (this.field !== undefined) {\n const selected = record[this.field];\n return selected === null || selected === undefined ? null : this.#printValue(selected, variable.baseType);\n }\n const fields = Object.entries(record).filter(([, entry]) => entry !== null && entry !== undefined);\n if (fields.length === 0) return null;\n return fields\n .map(([name, entry]) => `${name}${this.mappingIndicator}${this.#printValue(entry, variable.baseType)}`)\n .join(this.delimiter);\n }\n\n return this.#printValue(value, variable.baseType);\n }\n\n #printValue(value: unknown, baseType?: BaseType): string {\n // A pair/directedPair arrives as its two identifiers; QTI writes them space separated.\n const raw = Array.isArray(value) ? value.join(' ') : String(value);\n\n if (this.format) return applyFormat(this.format, raw);\n\n if (baseType === 'integer' && this.base !== 10) {\n const integer = Number.parseInt(raw, 10);\n if (Number.isFinite(integer)) return integer.toString(this.base);\n }\n\n if (this.powerForm) {\n const numeric = Number(raw);\n if (raw.trim() !== '' && Number.isFinite(numeric)) return toExponential(numeric);\n }\n\n return raw;\n }\n\n public calculate(): VariableDeclaration<string | string[]> {\n const result = this.context.variables.find(v => v.identifier === this.identifier) || null;\n return result;\n }\n}\n"]}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { QtiExpression } from '@qti-components/base';
|
|
2
2
|
export class QtiVariable extends QtiExpression {
|
|
3
3
|
getResult() {
|
|
4
|
-
const identifier = this.getAttribute('identifier');
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const identifier = this.getAttribute('identifier') || '';
|
|
5
|
+
// Resolve item-scope first, then test-level outcomes (e.g. when used in an
|
|
6
|
+
// assessmentTest's outcome processing), and tolerate an unknown identifier
|
|
7
|
+
// instead of throwing.
|
|
8
|
+
return (this.resolveVariable(identifier)?.value ?? null);
|
|
7
9
|
}
|
|
8
10
|
}
|
|
9
11
|
//# sourceMappingURL=qti-variable.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qti-variable.js","sourceRoot":"","sources":["../../../src/components/qti-variable/qti-variable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,WAAY,SAAQ,aAAgC;IAC/C,SAAS;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"qti-variable.js","sourceRoot":"","sources":["../../../src/components/qti-variable/qti-variable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,WAAY,SAAQ,aAAgC;IAC/C,SAAS;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACzD,2EAA2E;QAC3E,2EAA2E;QAC3E,uBAAuB;QACvB,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,KAAK,IAAI,IAAI,CAAsB,CAAC;IAChF,CAAC;CACF","sourcesContent":["import { QtiExpression } from '@qti-components/base';\n\nexport class QtiVariable extends QtiExpression<string | string[]> {\n public override getResult() {\n const identifier = this.getAttribute('identifier') || '';\n // Resolve item-scope first, then test-level outcomes (e.g. when used in an\n // assessmentTest's outcome processing), and tolerate an unknown identifier\n // instead of throwing.\n return (this.resolveVariable(identifier)?.value ?? null) as string | string[];\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qti-components/processing",
|
|
3
3
|
"description": "QTI processing utilities",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"author": "",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@qti-components/base": "^2.
|
|
7
|
+
"@qti-components/base": "^2.2.0",
|
|
8
8
|
"@qti-components/utilities": "^1.3.3"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@lit/context": "^1.1.6",
|
|
12
|
-
"lit": "^3.3.
|
|
12
|
+
"lit": "^3.3.3"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
15
15
|
"processing",
|