@getodk/xforms-engine 0.12.0 → 0.14.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/client/BaseItem.d.ts +6 -0
- package/dist/client/GroupNode.d.ts +4 -4
- package/dist/client/MarkdownNode.d.ts +33 -0
- package/dist/client/RankNode.d.ts +2 -4
- package/dist/client/SelectNode.d.ts +2 -5
- package/dist/client/TextRange.d.ts +2 -11
- package/dist/client/hierarchy.d.ts +1 -2
- package/dist/client/index.d.ts +1 -1
- package/dist/client/node-types.d.ts +1 -1
- package/dist/index.js +10543 -332
- package/dist/index.js.map +1 -1
- package/dist/instance/RankControl.d.ts +3 -2
- package/dist/instance/SelectControl.d.ts +3 -2
- package/dist/instance/hierarchy.d.ts +5 -6
- package/dist/instance/markdown/MarkdownNode.d.ts +75 -0
- package/dist/instance/text/TextChunk.d.ts +0 -1
- package/dist/instance/text/TextRange.d.ts +2 -1
- package/dist/instance/text/markdownFormat.d.ts +3 -0
- package/dist/lib/reactivity/createItemCollection.d.ts +5 -7
- package/dist/parse/body/BodyDefinition.d.ts +2 -8
- package/dist/parse/body/GroupElementDefinition.d.ts +22 -0
- package/dist/parse/body/control/ItemsetDefinition.d.ts +4 -1
- package/dist/parse/expression/BindComputationExpression.d.ts +1 -1
- package/dist/parse/expression/ItemPropertyExpression.d.ts +6 -0
- package/dist/parse/expression/ItemsetNodesetExpression.d.ts +1 -2
- package/dist/parse/expression/TextChunkExpression.d.ts +9 -7
- package/dist/parse/expression/abstract/DependentExpression.d.ts +1 -15
- package/dist/parse/model/{SubtreeDefinition.d.ts → GroupDefinition.d.ts} +4 -3
- package/dist/parse/model/ModelDefinition.d.ts +6 -1
- package/dist/parse/model/NodeDefinition.d.ts +7 -8
- package/dist/parse/model/RepeatDefinition.d.ts +1 -1
- package/dist/parse/model/RootDefinition.d.ts +1 -1
- package/dist/parse/model/generateItextChunks.d.ts +5 -0
- package/dist/parse/text/LabelDefinition.d.ts +4 -5
- package/dist/parse/xpath/semantic-analysis.d.ts +1 -0
- package/dist/solid.js +10543 -332
- package/dist/solid.js.map +1 -1
- package/package.json +3 -2
- package/src/client/BaseItem.ts +7 -0
- package/src/client/GroupNode.ts +4 -10
- package/src/client/MarkdownNode.ts +53 -0
- package/src/client/RankNode.ts +2 -5
- package/src/client/SelectNode.ts +2 -6
- package/src/client/TextRange.ts +2 -11
- package/src/client/hierarchy.ts +0 -2
- package/src/client/index.ts +1 -1
- package/src/client/node-types.ts +0 -1
- package/src/instance/Group.ts +1 -1
- package/src/instance/RankControl.ts +8 -2
- package/src/instance/SelectControl.ts +8 -2
- package/src/instance/children/buildChildren.ts +1 -17
- package/src/instance/children/normalizeChildInitOptions.ts +44 -59
- package/src/instance/hierarchy.ts +0 -6
- package/src/instance/markdown/MarkdownNode.ts +115 -0
- package/src/instance/text/TextChunk.ts +0 -5
- package/src/instance/text/TextRange.ts +5 -3
- package/src/instance/text/markdownFormat.ts +214 -0
- package/src/integration/xpath/adapter/names.ts +0 -1
- package/src/lib/reactivity/createItemCollection.ts +25 -9
- package/src/lib/reactivity/text/createTextRange.ts +30 -30
- package/src/parse/body/BodyDefinition.ts +7 -34
- package/src/parse/body/GroupElementDefinition.ts +47 -0
- package/src/parse/body/control/ItemsetDefinition.ts +9 -2
- package/src/parse/expression/BindComputationExpression.ts +2 -7
- package/src/parse/expression/ItemPropertyExpression.ts +12 -0
- package/src/parse/expression/ItemsetNodesetExpression.ts +2 -3
- package/src/parse/expression/ItemsetValueExpression.ts +1 -1
- package/src/parse/expression/RepeatCountControlExpression.ts +4 -4
- package/src/parse/expression/TextChunkExpression.ts +25 -35
- package/src/parse/expression/abstract/DependentExpression.ts +2 -38
- package/src/parse/model/{SubtreeDefinition.ts → GroupDefinition.ts} +6 -8
- package/src/parse/model/ModelDefinition.ts +13 -0
- package/src/parse/model/NodeDefinition.ts +8 -9
- package/src/parse/model/RepeatDefinition.ts +2 -2
- package/src/parse/model/RootDefinition.ts +2 -2
- package/src/parse/model/generateItextChunks.ts +61 -0
- package/src/parse/text/ItemsetLabelDefinition.ts +4 -4
- package/src/parse/text/LabelDefinition.ts +4 -9
- package/src/parse/text/MessageDefinition.ts +4 -4
- package/src/parse/text/abstract/TextElementDefinition.ts +6 -7
- package/src/parse/xpath/semantic-analysis.ts +37 -8
- package/dist/client/SubtreeNode.d.ts +0 -56
- package/dist/instance/Subtree.d.ts +0 -38
- package/dist/instance/text/FormattedTextStub.d.ts +0 -1
- package/dist/parse/body/group/BaseGroupDefinition.d.ts +0 -40
- package/dist/parse/body/group/LogicalGroupDefinition.d.ts +0 -6
- package/dist/parse/body/group/PresentationGroupDefinition.d.ts +0 -11
- package/dist/parse/body/group/StructuralGroupDefinition.d.ts +0 -6
- package/src/client/SubtreeNode.ts +0 -61
- package/src/instance/Subtree.ts +0 -102
- package/src/instance/text/FormattedTextStub.ts +0 -8
- package/src/parse/body/group/BaseGroupDefinition.ts +0 -89
- package/src/parse/body/group/LogicalGroupDefinition.ts +0 -11
- package/src/parse/body/group/PresentationGroupDefinition.ts +0 -28
- package/src/parse/body/group/StructuralGroupDefinition.ts +0 -11
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { LabelDefinition } from '../text/LabelDefinition.ts';
|
|
2
|
+
import type { XFormDefinition } from '../XFormDefinition.ts';
|
|
3
|
+
import { parseNodesetReference } from '../xpath/reference-parsing.ts';
|
|
4
|
+
import type { StructureElementAppearanceDefinition } from './appearance/structureElementAppearanceParser.ts';
|
|
5
|
+
import { structureElementAppearanceParser } from './appearance/structureElementAppearanceParser.ts';
|
|
6
|
+
import {
|
|
7
|
+
type BodyElementDefinitionArray,
|
|
8
|
+
type BodyElementParentContext,
|
|
9
|
+
} from './BodyDefinition.ts';
|
|
10
|
+
import { BodyElementDefinition } from './BodyElementDefinition.ts';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* As per the spec: https://getodk.github.io/xforms-spec/#groups
|
|
14
|
+
*
|
|
15
|
+
* A group combines elements together.
|
|
16
|
+
* The group can have a label, and if so is referred to as a "presentation group".
|
|
17
|
+
* The group can have a ref, and if so is referred to as a "logical group".
|
|
18
|
+
*/
|
|
19
|
+
export class GroupElementDefinition extends BodyElementDefinition<'group'> {
|
|
20
|
+
override readonly category = 'structure';
|
|
21
|
+
override readonly type = 'group';
|
|
22
|
+
|
|
23
|
+
readonly children: BodyElementDefinitionArray;
|
|
24
|
+
|
|
25
|
+
override readonly reference: string | null;
|
|
26
|
+
readonly appearances: StructureElementAppearanceDefinition;
|
|
27
|
+
override readonly label: LabelDefinition | null;
|
|
28
|
+
|
|
29
|
+
static override isCompatible(localName: string): boolean {
|
|
30
|
+
return localName === 'group';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
constructor(form: XFormDefinition, parent: BodyElementParentContext, element: Element) {
|
|
34
|
+
super(form, parent, element);
|
|
35
|
+
|
|
36
|
+
const childElements = Array.from(element.children).filter((child) => {
|
|
37
|
+
const childName = child.localName;
|
|
38
|
+
|
|
39
|
+
return childName !== 'label';
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
this.children = this.body.getChildElementDefinitions(form, this, element, childElements);
|
|
43
|
+
this.reference = parseNodesetReference(parent, element, 'ref');
|
|
44
|
+
this.appearances = structureElementAppearanceParser.parseFrom(element, 'appearance');
|
|
45
|
+
this.label = LabelDefinition.forGroup(form, this);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import type { StaticElement } from '../../../integration/xpath/static-dom/StaticElement.ts';
|
|
1
2
|
import type { ItemsetElement } from '../../../lib/dom/query.ts';
|
|
2
3
|
import { getValueElement } from '../../../lib/dom/query.ts';
|
|
4
|
+
import { DependentExpression } from '../../expression/abstract/DependentExpression.ts';
|
|
5
|
+
import { ItemPropertyExpression } from '../../expression/ItemPropertyExpression.ts';
|
|
3
6
|
import { ItemsetNodesetExpression } from '../../expression/ItemsetNodesetExpression.ts';
|
|
4
7
|
import { ItemsetValueExpression } from '../../expression/ItemsetValueExpression.ts';
|
|
5
8
|
import { ItemsetLabelDefinition } from '../../text/ItemsetLabelDefinition.ts';
|
|
6
9
|
import type { XFormDefinition } from '../../XFormDefinition.ts';
|
|
7
10
|
import { parseNodesetReference } from '../../xpath/reference-parsing.ts';
|
|
8
11
|
import { BodyElementDefinition } from '../BodyElementDefinition.ts';
|
|
9
|
-
import type { AnySelectControlDefinition } from './SelectControlDefinition.ts';
|
|
10
12
|
import { RankControlDefinition } from './RankControlDefinition.ts';
|
|
13
|
+
import type { AnySelectControlDefinition } from './SelectControlDefinition.ts';
|
|
11
14
|
|
|
12
15
|
export class ItemsetDefinition extends BodyElementDefinition<'itemset'> {
|
|
13
16
|
override readonly category = 'support';
|
|
@@ -28,7 +31,7 @@ export class ItemsetDefinition extends BodyElementDefinition<'itemset'> {
|
|
|
28
31
|
|
|
29
32
|
const nodesetExpression = parseNodesetReference(parent, element, 'nodeset');
|
|
30
33
|
|
|
31
|
-
this.nodes = new ItemsetNodesetExpression(
|
|
34
|
+
this.nodes = new ItemsetNodesetExpression(nodesetExpression);
|
|
32
35
|
this.reference = nodesetExpression;
|
|
33
36
|
|
|
34
37
|
const valueElement = getValueElement(element);
|
|
@@ -52,4 +55,8 @@ export class ItemsetDefinition extends BodyElementDefinition<'itemset'> {
|
|
|
52
55
|
this.value = new ItemsetValueExpression(this, valueExpression);
|
|
53
56
|
this.label = ItemsetLabelDefinition.from(form, this);
|
|
54
57
|
}
|
|
58
|
+
|
|
59
|
+
getPropertiesExpressions(propertiesNodes: StaticElement[]): Array<DependentExpression<'string'>> {
|
|
60
|
+
return ItemPropertyExpression.from(propertiesNodes);
|
|
61
|
+
}
|
|
55
62
|
}
|
|
@@ -47,18 +47,15 @@ export class BindComputationExpression<
|
|
|
47
47
|
return null as BindComputationFactoryResult<Type>;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
return new this(
|
|
50
|
+
return new this(computation, expression);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
readonly isDefaultExpression: boolean;
|
|
54
54
|
|
|
55
55
|
protected constructor(
|
|
56
|
-
bind: BindDefinition,
|
|
57
56
|
readonly computation: Computation,
|
|
58
57
|
expression: string | null
|
|
59
58
|
) {
|
|
60
|
-
const ignoreContextReference = computation === 'constraint';
|
|
61
|
-
|
|
62
59
|
let isDefaultExpression: boolean;
|
|
63
60
|
let resolvedExpression: string;
|
|
64
61
|
|
|
@@ -75,9 +72,7 @@ export class BindComputationExpression<
|
|
|
75
72
|
resolvedExpression = expression;
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
super(
|
|
79
|
-
ignoreContextReference,
|
|
80
|
-
});
|
|
75
|
+
super(bindComputationResultTypes[computation], resolvedExpression);
|
|
81
76
|
|
|
82
77
|
this.isDefaultExpression = isDefaultExpression;
|
|
83
78
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StaticElement } from '../../integration/xpath/static-dom/StaticElement.ts';
|
|
2
|
+
import { DependentExpression } from './abstract/DependentExpression.ts';
|
|
3
|
+
|
|
4
|
+
export class ItemPropertyExpression extends DependentExpression<'string'> {
|
|
5
|
+
static from(propertiesNodes: StaticElement[]) {
|
|
6
|
+
return propertiesNodes.map((node: StaticElement) => new this(node.qualifiedName.localName));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
constructor(propertyName: string) {
|
|
10
|
+
super('string', propertyName);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type { ItemsetDefinition } from '../body/control/ItemsetDefinition.ts';
|
|
2
1
|
import { DependentExpression } from './abstract/DependentExpression.ts';
|
|
3
2
|
|
|
4
3
|
export class ItemsetNodesetExpression extends DependentExpression<'nodes'> {
|
|
5
|
-
constructor(
|
|
6
|
-
super(
|
|
4
|
+
constructor(nodesetExpression: string) {
|
|
5
|
+
super('nodes', nodesetExpression);
|
|
7
6
|
}
|
|
8
7
|
}
|
|
@@ -23,7 +23,7 @@ export class RepeatCountControlExpression extends DependentExpression<'number'>
|
|
|
23
23
|
const { countExpression, noAddRemoveExpression } = bodyElement;
|
|
24
24
|
|
|
25
25
|
if (countExpression != null) {
|
|
26
|
-
return new this(
|
|
26
|
+
return new this(countExpression);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
if (noAddRemoveExpression != null && isConstantTruthyExpression(noAddRemoveExpression)) {
|
|
@@ -32,13 +32,13 @@ export class RepeatCountControlExpression extends DependentExpression<'number'>
|
|
|
32
32
|
// repeat's template.
|
|
33
33
|
const fixedCountExpression = String(Math.max(initialCount, 1));
|
|
34
34
|
|
|
35
|
-
return new this(
|
|
35
|
+
return new this(fixedCountExpression);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
private constructor(
|
|
42
|
-
super(
|
|
41
|
+
private constructor(expression: string) {
|
|
42
|
+
super('number', expression);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
JRResourceURLString,
|
|
3
|
+
ResourceType,
|
|
4
|
+
} from '@getodk/common/jr-resources/JRResourceURL.ts';
|
|
1
5
|
import type { KnownAttributeLocalNamedElement } from '@getodk/common/types/dom.ts';
|
|
2
6
|
import type { TextChunkSource } from '../../client/TextRange.ts';
|
|
3
|
-
import
|
|
4
|
-
import { isTranslationExpression } from '../xpath/semantic-analysis.ts';
|
|
7
|
+
import { getTranslationExpression } from '../xpath/semantic-analysis.ts';
|
|
5
8
|
import { DependentExpression } from './abstract/DependentExpression.ts';
|
|
6
9
|
|
|
7
10
|
interface TextChunkExpressionOptions {
|
|
8
|
-
readonly
|
|
11
|
+
readonly type?: ResourceType;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
interface OutputElement extends KnownAttributeLocalNamedElement<'output', 'value'> {}
|
|
@@ -18,59 +21,46 @@ export class TextChunkExpression<T extends 'nodes' | 'string'> extends Dependent
|
|
|
18
21
|
readonly source: TextChunkSource;
|
|
19
22
|
// Set for the literal source, blank otherwise
|
|
20
23
|
readonly stringValue: string;
|
|
24
|
+
readonly resourceType: ResourceType | null;
|
|
21
25
|
|
|
22
26
|
constructor(
|
|
23
|
-
context: AnyTextRangeDefinition,
|
|
24
27
|
resultType: T,
|
|
25
28
|
expression: string,
|
|
26
29
|
source: TextChunkSource,
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
literalValue = '',
|
|
31
|
+
options: TextChunkExpressionOptions = {}
|
|
29
32
|
) {
|
|
30
|
-
super(
|
|
31
|
-
semanticDependencies: {
|
|
32
|
-
translations: options.isTranslated,
|
|
33
|
-
},
|
|
34
|
-
ignoreContextReference: true,
|
|
35
|
-
});
|
|
33
|
+
super(resultType, expression);
|
|
36
34
|
|
|
35
|
+
this.resourceType = options.type ?? null;
|
|
37
36
|
this.source = source;
|
|
38
37
|
this.stringValue = literalValue;
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
static fromLiteral(
|
|
42
|
-
|
|
43
|
-
stringValue: string
|
|
44
|
-
): TextChunkExpression<'string'> {
|
|
45
|
-
return new TextChunkExpression(context, 'string', 'null', 'literal', {}, stringValue);
|
|
40
|
+
static fromLiteral(stringValue: string): TextChunkExpression<'string'> {
|
|
41
|
+
return new TextChunkExpression('string', 'null', 'literal', stringValue);
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
static fromReference(
|
|
49
|
-
|
|
50
|
-
ref: string
|
|
51
|
-
): TextChunkExpression<'string'> {
|
|
52
|
-
return new TextChunkExpression(context, 'string', ref, 'reference');
|
|
44
|
+
static fromReference(ref: string): TextChunkExpression<'string'> {
|
|
45
|
+
return new TextChunkExpression('string', ref, 'reference');
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
static fromOutput(
|
|
56
|
-
context: AnyTextRangeDefinition,
|
|
57
|
-
element: Element
|
|
58
|
-
): TextChunkExpression<'string'> | null {
|
|
48
|
+
static fromOutput(element: Element): TextChunkExpression<'string'> | null {
|
|
59
49
|
if (!isOutputElement(element)) {
|
|
60
50
|
return null;
|
|
61
51
|
}
|
|
62
52
|
|
|
63
|
-
return new TextChunkExpression(
|
|
53
|
+
return new TextChunkExpression('string', element.getAttribute('value'), 'output');
|
|
64
54
|
}
|
|
65
55
|
|
|
66
|
-
static
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
56
|
+
static fromResource(url: JRResourceURLString, type: ResourceType): TextChunkExpression<'string'> {
|
|
57
|
+
return new TextChunkExpression('string', 'null', 'literal', url, { type });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static fromTranslation(maybeExpression: string): TextChunkExpression<'nodes'> | null {
|
|
61
|
+
const translationExpression = getTranslationExpression(maybeExpression);
|
|
62
|
+
if (translationExpression) {
|
|
63
|
+
return new TextChunkExpression('nodes', translationExpression, 'translation');
|
|
74
64
|
}
|
|
75
65
|
|
|
76
66
|
return null;
|
|
@@ -3,12 +3,7 @@ import type {
|
|
|
3
3
|
ConstantExpression,
|
|
4
4
|
ConstantTruthyExpression,
|
|
5
5
|
} from '../../xpath/semantic-analysis.ts';
|
|
6
|
-
import {
|
|
7
|
-
isConstantExpression,
|
|
8
|
-
isConstantTruthyExpression,
|
|
9
|
-
isTranslationExpression,
|
|
10
|
-
} from '../../xpath/semantic-analysis.ts';
|
|
11
|
-
import type { DependencyContext } from './DependencyContext.ts';
|
|
6
|
+
import { isConstantExpression, isConstantTruthyExpression } from '../../xpath/semantic-analysis.ts';
|
|
12
7
|
|
|
13
8
|
const evaluatorMethodsByResultType = {
|
|
14
9
|
boolean: 'evaluateBoolean',
|
|
@@ -28,22 +23,6 @@ export type DependentExpressionResult<Type extends DependentExpressionResultType
|
|
|
28
23
|
EngineXPathEvaluator[DependentExpressionEvaluatorMethod<Type>]
|
|
29
24
|
>;
|
|
30
25
|
|
|
31
|
-
interface SemanticDependencyOptions {
|
|
32
|
-
/**
|
|
33
|
-
* @default false
|
|
34
|
-
*/
|
|
35
|
-
readonly translations?: boolean | undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface DependentExpressionOptions {
|
|
39
|
-
/**
|
|
40
|
-
* @default false
|
|
41
|
-
*/
|
|
42
|
-
readonly ignoreContextReference?: boolean;
|
|
43
|
-
|
|
44
|
-
readonly semanticDependencies?: SemanticDependencyOptions;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
26
|
export interface ConstantDependentExpression<Type extends DependentExpressionResultType>
|
|
48
27
|
extends DependentExpression<Type> {
|
|
49
28
|
readonly expression: ConstantExpression;
|
|
@@ -60,10 +39,8 @@ export abstract class DependentExpression<Type extends DependentExpressionResult
|
|
|
60
39
|
readonly constantTruthyExpression: ConstantTruthyExpression | null;
|
|
61
40
|
|
|
62
41
|
constructor(
|
|
63
|
-
context: DependencyContext,
|
|
64
42
|
readonly resultType: Type,
|
|
65
|
-
readonly expression: string
|
|
66
|
-
options: DependentExpressionOptions = {}
|
|
43
|
+
readonly expression: string
|
|
67
44
|
) {
|
|
68
45
|
if (resultType === 'boolean' && isConstantTruthyExpression(expression)) {
|
|
69
46
|
this.constantTruthyExpression = expression;
|
|
@@ -77,19 +54,6 @@ export abstract class DependentExpression<Type extends DependentExpressionResult
|
|
|
77
54
|
}
|
|
78
55
|
|
|
79
56
|
this.evaluatorMethod = evaluatorMethodsByResultType[resultType];
|
|
80
|
-
|
|
81
|
-
const {
|
|
82
|
-
semanticDependencies = {
|
|
83
|
-
translations: false,
|
|
84
|
-
},
|
|
85
|
-
} = options;
|
|
86
|
-
|
|
87
|
-
const isTranslated = semanticDependencies.translations && isTranslationExpression(expression);
|
|
88
|
-
|
|
89
|
-
if (isTranslated) {
|
|
90
|
-
this.isTranslated = true;
|
|
91
|
-
context.isTranslated = true;
|
|
92
|
-
}
|
|
93
57
|
}
|
|
94
58
|
|
|
95
59
|
isConstantExpression(): this is ConstantDependentExpression<Type> {
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import type { StaticElement } from '../../integration/xpath/static-dom/StaticElement.ts';
|
|
2
2
|
import { NamespaceDeclarationMap } from '../../lib/names/NamespaceDeclarationMap.ts';
|
|
3
3
|
import { QualifiedName } from '../../lib/names/QualifiedName.ts';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
AnyGroupElementDefinition,
|
|
7
|
-
} from '../body/BodyDefinition.ts';
|
|
4
|
+
import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
|
|
5
|
+
import type { GroupElementDefinition } from '../body/GroupElementDefinition.ts';
|
|
8
6
|
import type { BindDefinition } from './BindDefinition.ts';
|
|
9
7
|
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
10
8
|
import type { ChildNodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
|
|
11
9
|
|
|
12
|
-
export class
|
|
13
|
-
'
|
|
14
|
-
|
|
10
|
+
export class GroupDefinition extends DescendentNodeDefinition<
|
|
11
|
+
'group',
|
|
12
|
+
GroupElementDefinition | null
|
|
15
13
|
> {
|
|
16
|
-
readonly type = '
|
|
14
|
+
readonly type = 'group';
|
|
17
15
|
|
|
18
16
|
readonly namespaceDeclarations: NamespaceDeclarationMap;
|
|
19
17
|
readonly qualifiedName: QualifiedName;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import type { ActiveLanguage } from '../../client/FormLanguage.ts';
|
|
1
2
|
import { ErrorProductionDesignPendingError } from '../../error/ErrorProductionDesignPendingError.ts';
|
|
2
3
|
import type { StaticDocument } from '../../integration/xpath/static-dom/StaticDocument.ts';
|
|
4
|
+
import { TextChunkExpression } from '../expression/TextChunkExpression.ts';
|
|
3
5
|
import { parseStaticDocumentFromDOMSubtree } from '../shared/parseStaticDocumentFromDOMSubtree.ts';
|
|
4
6
|
import type { XFormDefinition } from '../XFormDefinition.ts';
|
|
7
|
+
import { generateItextChunks, type ChunkExpressionsByItextId } from './generateItextChunks.ts';
|
|
5
8
|
import { ItextTranslationsDefinition } from './ItextTranslationsDefinition.ts';
|
|
6
9
|
import { ModelBindMap } from './ModelBindMap.ts';
|
|
7
10
|
import type { AnyNodeDefinition } from './NodeDefinition.ts';
|
|
@@ -16,6 +19,7 @@ export class ModelDefinition {
|
|
|
16
19
|
readonly nodes: NodeDefinitionMap;
|
|
17
20
|
readonly instance: StaticDocument;
|
|
18
21
|
readonly itextTranslations: ItextTranslationsDefinition;
|
|
22
|
+
readonly itextChunks: Map<string, ChunkExpressionsByItextId>;
|
|
19
23
|
|
|
20
24
|
constructor(readonly form: XFormDefinition) {
|
|
21
25
|
const submission = new SubmissionDefinition(form.xformDOM);
|
|
@@ -27,6 +31,7 @@ export class ModelDefinition {
|
|
|
27
31
|
this.root = new RootDefinition(form, this, submission, form.body.classes);
|
|
28
32
|
this.nodes = nodeDefinitionMap(this.root);
|
|
29
33
|
this.itextTranslations = ItextTranslationsDefinition.from(form.xformDOM);
|
|
34
|
+
this.itextChunks = generateItextChunks(form.xformDOM.itextTranslationElements);
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
getNodeDefinition(nodeset: string): AnyNodeDefinition {
|
|
@@ -54,4 +59,12 @@ export class ModelDefinition {
|
|
|
54
59
|
|
|
55
60
|
return rest;
|
|
56
61
|
}
|
|
62
|
+
|
|
63
|
+
getTranslationChunks(
|
|
64
|
+
itextId: string,
|
|
65
|
+
activeLanguage: ActiveLanguage
|
|
66
|
+
): ReadonlyArray<TextChunkExpression<'string'>> {
|
|
67
|
+
const languageMap = this.itextChunks.get(activeLanguage.language);
|
|
68
|
+
return languageMap?.get(itextId) ?? [];
|
|
69
|
+
}
|
|
57
70
|
}
|
|
@@ -7,10 +7,10 @@ import type { QualifiedName } from '../../lib/names/QualifiedName.ts';
|
|
|
7
7
|
import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
|
|
8
8
|
import type { RepeatElementDefinition } from '../body/RepeatElementDefinition.ts';
|
|
9
9
|
import type { BindDefinition } from './BindDefinition.ts';
|
|
10
|
+
import type { GroupDefinition } from './GroupDefinition.ts';
|
|
10
11
|
import type { LeafNodeDefinition } from './LeafNodeDefinition.ts';
|
|
11
12
|
import type { AnyRepeatDefinition } from './RepeatDefinition.ts';
|
|
12
13
|
import type { RootDefinition } from './RootDefinition.ts';
|
|
13
|
-
import type { SubtreeDefinition } from './SubtreeDefinition.ts';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Corresponds to a model instance root node, i.e.:
|
|
@@ -33,10 +33,9 @@ export type RepeatType = 'repeat';
|
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Corresponds to a model instance subtree which **does not** correspond to a
|
|
36
|
-
* <repeat> in the form definition.
|
|
37
|
-
* but this is not strictly necessary per spec (hence the distinct name).
|
|
36
|
+
* <repeat> in the form definition.
|
|
38
37
|
*/
|
|
39
|
-
export type
|
|
38
|
+
export type GroupNodeType = 'group';
|
|
40
39
|
|
|
41
40
|
/**
|
|
42
41
|
* Corresponds to a model instance leaf node, i.e. one of:
|
|
@@ -51,7 +50,7 @@ export type NodeDefinitionType =
|
|
|
51
50
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
52
51
|
| RootNodeType
|
|
53
52
|
| RepeatType
|
|
54
|
-
|
|
|
53
|
+
| GroupNodeType
|
|
55
54
|
| LeafNodeType;
|
|
56
55
|
|
|
57
56
|
// prettier-ignore
|
|
@@ -59,13 +58,13 @@ export type ParentNodeDefinition =
|
|
|
59
58
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
60
59
|
| RootDefinition
|
|
61
60
|
| AnyRepeatDefinition
|
|
62
|
-
|
|
|
61
|
+
| GroupDefinition;
|
|
63
62
|
|
|
64
63
|
// prettier-ignore
|
|
65
64
|
export type ChildNodeDefinition =
|
|
66
65
|
| AnyRepeatDefinition
|
|
67
|
-
|
|
|
68
|
-
|
|
|
66
|
+
| GroupDefinition
|
|
67
|
+
| LeafNodeDefinition;
|
|
69
68
|
|
|
70
69
|
export abstract class NodeDefinition<Type extends NodeDefinitionType>
|
|
71
70
|
implements NamedSubtreeDefinition
|
|
@@ -92,5 +91,5 @@ export type AnyNodeDefinition =
|
|
|
92
91
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
93
92
|
| RootDefinition
|
|
94
93
|
| AnyRepeatDefinition
|
|
95
|
-
|
|
|
94
|
+
| GroupDefinition
|
|
96
95
|
| LeafNodeDefinition;
|
|
@@ -21,9 +21,9 @@ import type { RepeatElementDefinition } from '../body/RepeatElementDefinition.ts
|
|
|
21
21
|
import { RepeatCountControlExpression } from '../expression/RepeatCountControlExpression.ts';
|
|
22
22
|
import type { BindDefinition } from './BindDefinition.ts';
|
|
23
23
|
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
24
|
+
import type { GroupDefinition } from './GroupDefinition.ts';
|
|
24
25
|
import type { ChildNodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
|
|
25
26
|
import type { RootDefinition } from './RootDefinition.ts';
|
|
26
|
-
import type { SubtreeDefinition } from './SubtreeDefinition.ts';
|
|
27
27
|
|
|
28
28
|
interface JavaRosaNamespaceURI extends NamespaceURL {
|
|
29
29
|
readonly href: JAVAROSA_NAMESPACE_URI;
|
|
@@ -284,7 +284,7 @@ export interface UncontrolledRepeatDefinition extends RepeatDefinition {
|
|
|
284
284
|
* "repeat", as defined by a form, where those concepts include:
|
|
285
285
|
*
|
|
286
286
|
* - A {@link RepeatElementDefinition}—corresponding to a `<repeat>` {@link https://getodk.github.io/xforms-spec/#body-elements | body element}—which is associated with the nodeset referencing the "repeat template" and
|
|
287
|
-
* all "repeat instances" (see below points describing both concepts in more detail). The presence of such a body element determines whether to produce a repeat definition (rather than e.g. a {@link
|
|
287
|
+
* all "repeat instances" (see below points describing both concepts in more detail). The presence of such a body element determines whether to produce a repeat definition (rather than e.g. a {@link GroupDefinition}).
|
|
288
288
|
*
|
|
289
289
|
* - A "repeat template", defined by a form either
|
|
290
290
|
* explicitly,
|
|
@@ -3,6 +3,7 @@ import { NamespaceDeclarationMap } from '../../lib/names/NamespaceDeclarationMap
|
|
|
3
3
|
import { QualifiedName } from '../../lib/names/QualifiedName.ts';
|
|
4
4
|
import type { BodyClassList } from '../body/BodyDefinition.ts';
|
|
5
5
|
import type { XFormDefinition } from '../XFormDefinition.ts';
|
|
6
|
+
import { GroupDefinition } from './GroupDefinition.ts';
|
|
6
7
|
import { LeafNodeDefinition } from './LeafNodeDefinition.ts';
|
|
7
8
|
import type { ModelDefinition } from './ModelDefinition.ts';
|
|
8
9
|
import type { ChildNodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
|
|
@@ -12,7 +13,6 @@ import { RangeNodeDefinition } from './RangeNodeDefinition.ts';
|
|
|
12
13
|
import { RepeatDefinition } from './RepeatDefinition.ts';
|
|
13
14
|
import { RootAttributeMap } from './RootAttributeMap.ts';
|
|
14
15
|
import type { SubmissionDefinition } from './SubmissionDefinition.ts';
|
|
15
|
-
import { SubtreeDefinition } from './SubtreeDefinition.ts';
|
|
16
16
|
|
|
17
17
|
export class RootDefinition extends NodeDefinition<'root'> {
|
|
18
18
|
readonly type = 'root';
|
|
@@ -107,7 +107,7 @@ export class RootDefinition extends NodeDefinition<'root'> {
|
|
|
107
107
|
);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
return new
|
|
110
|
+
return new GroupDefinition(parent, bind, bodyElement, element);
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isResourceType,
|
|
3
|
+
type JRResourceURLString,
|
|
4
|
+
type ResourceType,
|
|
5
|
+
} from '@getodk/common/jr-resources/JRResourceURL.ts';
|
|
6
|
+
import { isElementNode, isTextNode } from '@getodk/common/lib/dom/predicates.ts';
|
|
7
|
+
import { TextChunkExpression } from '../expression/TextChunkExpression.ts';
|
|
8
|
+
import type { DOMItextTranslationElement } from '../XFormDOM.ts';
|
|
9
|
+
|
|
10
|
+
const generateChunk = (node: Node): TextChunkExpression<'string'> | null => {
|
|
11
|
+
if (isElementNode(node)) {
|
|
12
|
+
return TextChunkExpression.fromOutput(node);
|
|
13
|
+
}
|
|
14
|
+
if (isTextNode(node)) {
|
|
15
|
+
const formAttribute = node.parentElement!.getAttribute('form') as ResourceType;
|
|
16
|
+
if (isResourceType(formAttribute)) {
|
|
17
|
+
return TextChunkExpression.fromResource(node.data as JRResourceURLString, formAttribute);
|
|
18
|
+
}
|
|
19
|
+
return TextChunkExpression.fromLiteral(node.data);
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const generateChunksForValues = (valueElement: ChildNode): Array<TextChunkExpression<'string'>> => {
|
|
25
|
+
return Array.from(valueElement.childNodes)
|
|
26
|
+
.map((node) => generateChunk(node))
|
|
27
|
+
.filter((chunk) => chunk !== null);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const generateChunksForTranslation = (
|
|
31
|
+
textElement: Element
|
|
32
|
+
): Array<TextChunkExpression<'string'>> => {
|
|
33
|
+
return Array.from(textElement.childNodes).flatMap((valueElement) =>
|
|
34
|
+
generateChunksForValues(valueElement)
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const generateChunksForLanguage = (
|
|
39
|
+
translationElement: DOMItextTranslationElement
|
|
40
|
+
): Map<string, ReadonlyArray<TextChunkExpression<'string'>>> => {
|
|
41
|
+
return new Map(
|
|
42
|
+
Array.from(translationElement.children).map((textElement) => {
|
|
43
|
+
const itextId = textElement.getAttribute('id');
|
|
44
|
+
return [itextId!, generateChunksForTranslation(textElement)] as const;
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export interface ChunkExpressionsByItextId
|
|
50
|
+
extends Map<string, ReadonlyArray<TextChunkExpression<'string'>>> {}
|
|
51
|
+
|
|
52
|
+
export const generateItextChunks = (
|
|
53
|
+
translationElements: readonly DOMItextTranslationElement[]
|
|
54
|
+
): Map<string, ChunkExpressionsByItextId> => {
|
|
55
|
+
return new Map(
|
|
56
|
+
translationElements.map((translationElement) => {
|
|
57
|
+
const lang = translationElement.getAttribute('lang');
|
|
58
|
+
return [lang, generateChunksForLanguage(translationElement)] as const;
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -30,11 +30,11 @@ export class ItemsetLabelDefinition extends TextRangeDefinition<'item-label'> {
|
|
|
30
30
|
throw new Error('<itemset><label> missing ref attribute');
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
this.chunks = [
|
|
33
|
+
const translationChunk = TextChunkExpression.fromTranslation(refExpression);
|
|
34
|
+
if (translationChunk) {
|
|
35
|
+
this.chunks = [translationChunk];
|
|
36
36
|
} else {
|
|
37
|
-
this.chunks = [TextChunkExpression.fromReference(
|
|
37
|
+
this.chunks = [TextChunkExpression.fromReference(refExpression)];
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import type { LocalNamedElement } from '@getodk/common/types/dom.ts';
|
|
2
2
|
import { getLabelElement, getRepeatGroupLabelElement } from '../../lib/dom/query.ts';
|
|
3
3
|
import type { XFormDefinition } from '../../parse/XFormDefinition.ts';
|
|
4
|
-
import type { AnyGroupElementDefinition } from '../../parse/body/BodyDefinition.ts';
|
|
5
|
-
import type { RepeatElementDefinition } from '../body/RepeatElementDefinition.ts';
|
|
6
4
|
import type { AnyControlDefinition } from '../body/control/ControlDefinition.ts';
|
|
7
|
-
import type {
|
|
5
|
+
import type { GroupElementDefinition } from '../body/GroupElementDefinition.ts';
|
|
6
|
+
import type { RepeatElementDefinition } from '../body/RepeatElementDefinition.ts';
|
|
8
7
|
import { TextElementDefinition } from './abstract/TextElementDefinition.ts';
|
|
9
8
|
|
|
10
9
|
// prettier-ignore
|
|
11
10
|
export type LabelOwner =
|
|
12
11
|
| AnyControlDefinition
|
|
13
|
-
|
|
|
12
|
+
| GroupElementDefinition
|
|
14
13
|
| RepeatElementDefinition;
|
|
15
14
|
|
|
16
15
|
interface LabelElement extends LocalNamedElement<'label'> {}
|
|
@@ -39,11 +38,7 @@ export class LabelDefinition extends TextElementDefinition<'label'> {
|
|
|
39
38
|
return new this(form, repeat, repeatGroupLabel);
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
static forGroup(
|
|
43
|
-
form: XFormDefinition,
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
-
group: BaseGroupDefinition<any>
|
|
46
|
-
): LabelDefinition | null {
|
|
41
|
+
static forGroup(form: XFormDefinition, group: GroupElementDefinition): LabelDefinition | null {
|
|
47
42
|
const labelElement = getLabelElement(group.element);
|
|
48
43
|
|
|
49
44
|
if (labelElement == null) {
|
|
@@ -29,11 +29,11 @@ export class MessageDefinition<
|
|
|
29
29
|
) {
|
|
30
30
|
super(bind.form, bind, null);
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
this.chunks = [
|
|
32
|
+
const translationChunk = TextChunkExpression.fromTranslation(message);
|
|
33
|
+
if (translationChunk) {
|
|
34
|
+
this.chunks = [translationChunk];
|
|
35
35
|
} else {
|
|
36
|
-
this.chunks = [TextChunkExpression.fromLiteral(
|
|
36
|
+
this.chunks = [TextChunkExpression.fromLiteral(message)];
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|