@getodk/xforms-engine 0.1.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/README.md +44 -0
- package/dist/.vite/manifest.json +7 -0
- package/dist/XFormDOM.d.ts +31 -0
- package/dist/XFormDataType.d.ts +26 -0
- package/dist/XFormDefinition.d.ts +14 -0
- package/dist/body/BodyDefinition.d.ts +52 -0
- package/dist/body/BodyElementDefinition.d.ts +32 -0
- package/dist/body/RepeatDefinition.d.ts +15 -0
- package/dist/body/UnsupportedBodyElementDefinition.d.ts +10 -0
- package/dist/body/control/ControlDefinition.d.ts +16 -0
- package/dist/body/control/InputDefinition.d.ts +5 -0
- package/dist/body/control/select/ItemDefinition.d.ts +13 -0
- package/dist/body/control/select/ItemsetDefinition.d.ts +16 -0
- package/dist/body/control/select/ItemsetNodesetContext.d.ts +11 -0
- package/dist/body/control/select/ItemsetNodesetExpression.d.ts +5 -0
- package/dist/body/control/select/ItemsetValueExpression.d.ts +6 -0
- package/dist/body/control/select/SelectDefinition.d.ts +23 -0
- package/dist/body/group/BaseGroupDefinition.d.ts +46 -0
- package/dist/body/group/LogicalGroupDefinition.d.ts +6 -0
- package/dist/body/group/PresentationGroupDefinition.d.ts +11 -0
- package/dist/body/group/RepeatGroupDefinition.d.ts +12 -0
- package/dist/body/group/StructuralGroupDefinition.d.ts +6 -0
- package/dist/body/text/HintDefinition.d.ts +11 -0
- package/dist/body/text/LabelDefinition.d.ts +20 -0
- package/dist/body/text/TextElementDefinition.d.ts +32 -0
- package/dist/body/text/TextElementOutputPart.d.ts +12 -0
- package/dist/body/text/TextElementPart.d.ts +12 -0
- package/dist/body/text/TextElementReferencePart.d.ts +6 -0
- package/dist/body/text/TextElementStaticPart.d.ts +6 -0
- package/dist/client/BaseNode.d.ts +138 -0
- package/dist/client/EngineConfig.d.ts +78 -0
- package/dist/client/FormLanguage.d.ts +63 -0
- package/dist/client/GroupNode.d.ts +24 -0
- package/dist/client/OpaqueReactiveObjectFactory.d.ts +70 -0
- package/dist/client/RepeatInstanceNode.d.ts +28 -0
- package/dist/client/RepeatRangeNode.d.ts +94 -0
- package/dist/client/RootNode.d.ts +31 -0
- package/dist/client/SelectNode.d.ts +60 -0
- package/dist/client/StringNode.d.ts +41 -0
- package/dist/client/SubtreeNode.d.ts +52 -0
- package/dist/client/TextRange.d.ts +55 -0
- package/dist/client/hierarchy.d.ts +48 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/node-types.d.ts +1 -0
- package/dist/expression/DependencyContext.d.ts +12 -0
- package/dist/expression/DependentExpression.d.ts +43 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +37622 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/Group.d.ts +31 -0
- package/dist/instance/RepeatInstance.d.ts +60 -0
- package/dist/instance/RepeatRange.d.ts +81 -0
- package/dist/instance/Root.d.ts +70 -0
- package/dist/instance/SelectField.d.ts +45 -0
- package/dist/instance/StringField.d.ts +39 -0
- package/dist/instance/Subtree.d.ts +30 -0
- package/dist/instance/abstract/DescendantNode.d.ts +76 -0
- package/dist/instance/abstract/InstanceNode.d.ts +107 -0
- package/dist/instance/children.d.ts +2 -0
- package/dist/instance/hierarchy.d.ts +12 -0
- package/dist/instance/identity.d.ts +7 -0
- package/dist/instance/index.d.ts +8 -0
- package/dist/instance/internal-api/EvaluationContext.d.ts +34 -0
- package/dist/instance/internal-api/InstanceConfig.d.ts +8 -0
- package/dist/instance/internal-api/SubscribableDependency.d.ts +59 -0
- package/dist/instance/internal-api/TranslationContext.d.ts +4 -0
- package/dist/instance/internal-api/ValueContext.d.ts +22 -0
- package/dist/instance/resource.d.ts +10 -0
- package/dist/instance/text/FormattedTextStub.d.ts +1 -0
- package/dist/instance/text/TextChunk.d.ts +11 -0
- package/dist/instance/text/TextRange.d.ts +10 -0
- package/dist/lib/dom/query.d.ts +20 -0
- package/dist/lib/reactivity/createChildrenState.d.ts +36 -0
- package/dist/lib/reactivity/createComputedExpression.d.ts +12 -0
- package/dist/lib/reactivity/createSelectItems.d.ts +16 -0
- package/dist/lib/reactivity/createValueState.d.ts +44 -0
- package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +18 -0
- package/dist/lib/reactivity/node-state/createClientState.d.ts +9 -0
- package/dist/lib/reactivity/node-state/createCurrentState.d.ts +6 -0
- package/dist/lib/reactivity/node-state/createEngineState.d.ts +5 -0
- package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +22 -0
- package/dist/lib/reactivity/node-state/createSpecifiedPropertyDescriptor.d.ts +6 -0
- package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +139 -0
- package/dist/lib/reactivity/node-state/representations.d.ts +25 -0
- package/dist/lib/reactivity/scope.d.ts +23 -0
- package/dist/lib/reactivity/text/createFieldHint.d.ts +5 -0
- package/dist/lib/reactivity/text/createNodeLabel.d.ts +5 -0
- package/dist/lib/reactivity/text/createTextRange.d.ts +19 -0
- package/dist/lib/reactivity/types.d.ts +21 -0
- package/dist/lib/unique-id.d.ts +27 -0
- package/dist/lib/xpath/analysis.d.ts +22 -0
- package/dist/model/BindComputation.d.ts +30 -0
- package/dist/model/BindDefinition.d.ts +31 -0
- package/dist/model/BindElement.d.ts +6 -0
- package/dist/model/DescendentNodeDefinition.d.ts +25 -0
- package/dist/model/ModelBindMap.d.ts +15 -0
- package/dist/model/ModelDefinition.d.ts +10 -0
- package/dist/model/NodeDefinition.d.ts +74 -0
- package/dist/model/RepeatInstanceDefinition.d.ts +15 -0
- package/dist/model/RepeatSequenceDefinition.d.ts +19 -0
- package/dist/model/RepeatTemplateDefinition.d.ts +29 -0
- package/dist/model/RootDefinition.d.ts +24 -0
- package/dist/model/SubtreeDefinition.d.ts +14 -0
- package/dist/model/ValueNodeDefinition.d.ts +15 -0
- package/dist/solid.js +37273 -0
- package/dist/solid.js.map +1 -0
- package/package.json +87 -0
- package/src/XFormDOM.ts +224 -0
- package/src/XFormDataType.ts +64 -0
- package/src/XFormDefinition.ts +40 -0
- package/src/body/BodyDefinition.ts +202 -0
- package/src/body/BodyElementDefinition.ts +62 -0
- package/src/body/RepeatDefinition.ts +54 -0
- package/src/body/UnsupportedBodyElementDefinition.ts +17 -0
- package/src/body/control/ControlDefinition.ts +42 -0
- package/src/body/control/InputDefinition.ts +9 -0
- package/src/body/control/select/ItemDefinition.ts +31 -0
- package/src/body/control/select/ItemsetDefinition.ts +36 -0
- package/src/body/control/select/ItemsetNodesetContext.ts +26 -0
- package/src/body/control/select/ItemsetNodesetExpression.ts +8 -0
- package/src/body/control/select/ItemsetValueExpression.ts +11 -0
- package/src/body/control/select/SelectDefinition.ts +74 -0
- package/src/body/group/BaseGroupDefinition.ts +137 -0
- package/src/body/group/LogicalGroupDefinition.ts +11 -0
- package/src/body/group/PresentationGroupDefinition.ts +28 -0
- package/src/body/group/RepeatGroupDefinition.ts +91 -0
- package/src/body/group/StructuralGroupDefinition.ts +11 -0
- package/src/body/text/HintDefinition.ts +26 -0
- package/src/body/text/LabelDefinition.ts +54 -0
- package/src/body/text/TextElementDefinition.ts +97 -0
- package/src/body/text/TextElementOutputPart.ts +27 -0
- package/src/body/text/TextElementPart.ts +31 -0
- package/src/body/text/TextElementReferencePart.ts +21 -0
- package/src/body/text/TextElementStaticPart.ts +26 -0
- package/src/client/BaseNode.ts +180 -0
- package/src/client/EngineConfig.ts +83 -0
- package/src/client/FormLanguage.ts +77 -0
- package/src/client/GroupNode.ts +33 -0
- package/src/client/OpaqueReactiveObjectFactory.ts +100 -0
- package/src/client/README.md +39 -0
- package/src/client/RepeatInstanceNode.ts +41 -0
- package/src/client/RepeatRangeNode.ts +100 -0
- package/src/client/RootNode.ts +36 -0
- package/src/client/SelectNode.ts +69 -0
- package/src/client/StringNode.ts +46 -0
- package/src/client/SubtreeNode.ts +57 -0
- package/src/client/TextRange.ts +63 -0
- package/src/client/hierarchy.ts +63 -0
- package/src/client/index.ts +29 -0
- package/src/client/node-types.ts +10 -0
- package/src/expression/DependencyContext.ts +53 -0
- package/src/expression/DependentExpression.ts +102 -0
- package/src/index.ts +35 -0
- package/src/instance/Group.ts +82 -0
- package/src/instance/RepeatInstance.ts +164 -0
- package/src/instance/RepeatRange.ts +214 -0
- package/src/instance/Root.ts +264 -0
- package/src/instance/SelectField.ts +204 -0
- package/src/instance/StringField.ts +93 -0
- package/src/instance/Subtree.ts +79 -0
- package/src/instance/abstract/DescendantNode.ts +182 -0
- package/src/instance/abstract/InstanceNode.ts +257 -0
- package/src/instance/children.ts +52 -0
- package/src/instance/hierarchy.ts +54 -0
- package/src/instance/identity.ts +11 -0
- package/src/instance/index.ts +37 -0
- package/src/instance/internal-api/EvaluationContext.ts +41 -0
- package/src/instance/internal-api/InstanceConfig.ts +9 -0
- package/src/instance/internal-api/SubscribableDependency.ts +61 -0
- package/src/instance/internal-api/TranslationContext.ts +5 -0
- package/src/instance/internal-api/ValueContext.ts +27 -0
- package/src/instance/resource.ts +75 -0
- package/src/instance/text/FormattedTextStub.ts +8 -0
- package/src/instance/text/TextChunk.ts +20 -0
- package/src/instance/text/TextRange.ts +23 -0
- package/src/lib/dom/query.ts +49 -0
- package/src/lib/reactivity/createChildrenState.ts +60 -0
- package/src/lib/reactivity/createComputedExpression.ts +114 -0
- package/src/lib/reactivity/createSelectItems.ts +163 -0
- package/src/lib/reactivity/createValueState.ts +258 -0
- package/src/lib/reactivity/materializeCurrentStateChildren.ts +121 -0
- package/src/lib/reactivity/node-state/createClientState.ts +51 -0
- package/src/lib/reactivity/node-state/createCurrentState.ts +27 -0
- package/src/lib/reactivity/node-state/createEngineState.ts +18 -0
- package/src/lib/reactivity/node-state/createSharedNodeState.ts +79 -0
- package/src/lib/reactivity/node-state/createSpecifiedPropertyDescriptor.ts +85 -0
- package/src/lib/reactivity/node-state/createSpecifiedState.ts +229 -0
- package/src/lib/reactivity/node-state/representations.ts +64 -0
- package/src/lib/reactivity/scope.ts +106 -0
- package/src/lib/reactivity/text/createFieldHint.ts +16 -0
- package/src/lib/reactivity/text/createNodeLabel.ts +16 -0
- package/src/lib/reactivity/text/createTextRange.ts +155 -0
- package/src/lib/reactivity/types.ts +27 -0
- package/src/lib/unique-id.ts +34 -0
- package/src/lib/xpath/analysis.ts +241 -0
- package/src/model/BindComputation.ts +88 -0
- package/src/model/BindDefinition.ts +104 -0
- package/src/model/BindElement.ts +8 -0
- package/src/model/DescendentNodeDefinition.ts +56 -0
- package/src/model/ModelBindMap.ts +71 -0
- package/src/model/ModelDefinition.ts +19 -0
- package/src/model/NodeDefinition.ts +146 -0
- package/src/model/RepeatInstanceDefinition.ts +39 -0
- package/src/model/RepeatSequenceDefinition.ts +53 -0
- package/src/model/RepeatTemplateDefinition.ts +150 -0
- package/src/model/RootDefinition.ts +121 -0
- package/src/model/SubtreeDefinition.ts +50 -0
- package/src/model/ValueNodeDefinition.ts +39 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
|
|
2
|
+
import type { RepeatDefinition } from '../body/RepeatDefinition.ts';
|
|
3
|
+
import type { BindDefinition } from './BindDefinition.ts';
|
|
4
|
+
import type { RepeatInstanceDefinition } from './RepeatInstanceDefinition.ts';
|
|
5
|
+
import type { RepeatSequenceDefinition } from './RepeatSequenceDefinition.ts';
|
|
6
|
+
import type { RepeatTemplateDefinition } from './RepeatTemplateDefinition.ts';
|
|
7
|
+
import type { RootDefinition } from './RootDefinition.ts';
|
|
8
|
+
import type { SubtreeDefinition } from './SubtreeDefinition.ts';
|
|
9
|
+
import type { ValueNodeDefinition } from './ValueNodeDefinition.ts';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Corresponds to the model/entry DOM root node, i.e.:
|
|
13
|
+
*
|
|
14
|
+
* - the element matching `/*` in primary instance expressions, a.k.a.
|
|
15
|
+
* - `/h:html/h:head/xf:model/xf:instance[1]/*`
|
|
16
|
+
*/
|
|
17
|
+
export type RootNodeType = 'root';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Corresponds to a sequence of model/entry DOM subtrees which in turn
|
|
21
|
+
* corresponds to a <repeat> in the form body definition.
|
|
22
|
+
*/
|
|
23
|
+
export type RepeatSequenceType = 'repeat-sequence';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Corresponds to a template definition for a repeat sequence, which either has
|
|
27
|
+
* an explicit `jr:template=""` attribute in the form definition or is inferred
|
|
28
|
+
* as a template from the form's first element matched by a <repeat nodeset>.
|
|
29
|
+
*/
|
|
30
|
+
export type RepeatTemplateType = 'repeat-template';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Corresponds to a single instance of a model/entry DOM subtree which
|
|
34
|
+
* in turn corresponds to a <repeat> in the form body definition, and a
|
|
35
|
+
* 'repeat-sequence' definition.
|
|
36
|
+
*/
|
|
37
|
+
export type RepeatInstanceType = 'repeat-instance';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Corresponds to a model/entry DOM subtree which **does not** correspond to a
|
|
41
|
+
* <repeat> in the form definition. This will typically correspond to a <group>,
|
|
42
|
+
* but this is not strictly necessary per spec (hence the distinct name).
|
|
43
|
+
*/
|
|
44
|
+
export type SubtreeNodeType = 'subtree';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Corresponds to a model/entry DOM leaf node, i.e. one of:
|
|
48
|
+
*
|
|
49
|
+
* - An element with no child elements
|
|
50
|
+
* - Any attribute corresponding to a bind's `nodeset` expression
|
|
51
|
+
*/
|
|
52
|
+
export type ValueNodeType = 'value-node';
|
|
53
|
+
|
|
54
|
+
// prettier-ignore
|
|
55
|
+
export type NodeDefinitionType =
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
57
|
+
| RootNodeType
|
|
58
|
+
| RepeatSequenceType
|
|
59
|
+
| RepeatTemplateType
|
|
60
|
+
| RepeatInstanceType
|
|
61
|
+
| SubtreeNodeType
|
|
62
|
+
| ValueNodeType;
|
|
63
|
+
|
|
64
|
+
// prettier-ignore
|
|
65
|
+
export type ParentNodeDefinition =
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
67
|
+
| RootDefinition
|
|
68
|
+
| RepeatTemplateDefinition
|
|
69
|
+
| RepeatInstanceDefinition
|
|
70
|
+
| SubtreeDefinition;
|
|
71
|
+
|
|
72
|
+
// prettier-ignore
|
|
73
|
+
export type ChildNodeDefinition =
|
|
74
|
+
| RepeatSequenceDefinition
|
|
75
|
+
| SubtreeDefinition
|
|
76
|
+
| ValueNodeDefinition;
|
|
77
|
+
|
|
78
|
+
// prettier-ignore
|
|
79
|
+
export type ChildNodeInstanceDefinition =
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
81
|
+
| RepeatTemplateDefinition
|
|
82
|
+
| RepeatInstanceDefinition
|
|
83
|
+
| SubtreeDefinition
|
|
84
|
+
| ValueNodeDefinition;
|
|
85
|
+
|
|
86
|
+
// prettier-ignore
|
|
87
|
+
export type NodeChildren<Type extends NodeDefinitionType> =
|
|
88
|
+
Type extends ParentNodeDefinition['type']
|
|
89
|
+
? readonly ChildNodeDefinition[]
|
|
90
|
+
: null;
|
|
91
|
+
|
|
92
|
+
// prettier-ignore
|
|
93
|
+
export type NodeInstances<Type extends NodeDefinitionType> =
|
|
94
|
+
Type extends 'repeat-sequence'
|
|
95
|
+
? readonly RepeatInstanceDefinition[]
|
|
96
|
+
: null;
|
|
97
|
+
|
|
98
|
+
// prettier-ignore
|
|
99
|
+
export type NodeParent<Type extends NodeDefinitionType> =
|
|
100
|
+
Type extends ChildNodeDefinition['type'] | ChildNodeInstanceDefinition['type']
|
|
101
|
+
? ParentNodeDefinition
|
|
102
|
+
: null;
|
|
103
|
+
|
|
104
|
+
// TODO: value-node may be Attr
|
|
105
|
+
// prettier-ignore
|
|
106
|
+
export type ModelNode<Type extends NodeDefinitionType> =
|
|
107
|
+
Type extends 'repeat-sequence'
|
|
108
|
+
? null
|
|
109
|
+
: Element;
|
|
110
|
+
|
|
111
|
+
// prettier-ignore
|
|
112
|
+
export type NodeDefaultValue<Type extends NodeDefinitionType> =
|
|
113
|
+
Type extends 'value-node'
|
|
114
|
+
? string
|
|
115
|
+
: null;
|
|
116
|
+
|
|
117
|
+
export interface NodeDefinition<Type extends NodeDefinitionType> {
|
|
118
|
+
readonly type: Type;
|
|
119
|
+
|
|
120
|
+
readonly bind: BindDefinition;
|
|
121
|
+
readonly nodeset: string;
|
|
122
|
+
readonly nodeName: string;
|
|
123
|
+
readonly bodyElement: AnyBodyElementDefinition | RepeatDefinition | null;
|
|
124
|
+
|
|
125
|
+
readonly isTranslated: boolean;
|
|
126
|
+
readonly dependencyExpressions: ReadonlySet<string>;
|
|
127
|
+
|
|
128
|
+
readonly root: RootDefinition;
|
|
129
|
+
readonly parent: NodeParent<Type>;
|
|
130
|
+
readonly children: NodeChildren<Type>;
|
|
131
|
+
readonly instances: NodeInstances<Type>;
|
|
132
|
+
|
|
133
|
+
readonly node: ModelNode<Type>;
|
|
134
|
+
readonly defaultValue: NodeDefaultValue<Type>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type AnyNodeDefinition =
|
|
138
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
139
|
+
| RootDefinition
|
|
140
|
+
| RepeatSequenceDefinition
|
|
141
|
+
| RepeatTemplateDefinition
|
|
142
|
+
| RepeatInstanceDefinition
|
|
143
|
+
| SubtreeDefinition
|
|
144
|
+
| ValueNodeDefinition;
|
|
145
|
+
|
|
146
|
+
export type TypedNodeDefinition<Type> = Extract<AnyNodeDefinition, { readonly type: Type }>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { RepeatDefinition } from '../body/RepeatDefinition.ts';
|
|
2
|
+
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
3
|
+
import type { ChildNodeDefinition, NodeDefinition } from './NodeDefinition.ts';
|
|
4
|
+
import type { RepeatSequenceDefinition } from './RepeatSequenceDefinition.ts';
|
|
5
|
+
|
|
6
|
+
export class RepeatInstanceDefinition
|
|
7
|
+
extends DescendentNodeDefinition<'repeat-instance', RepeatDefinition>
|
|
8
|
+
implements NodeDefinition<'repeat-instance'>
|
|
9
|
+
{
|
|
10
|
+
readonly type = 'repeat-instance';
|
|
11
|
+
|
|
12
|
+
readonly nodeName: string;
|
|
13
|
+
readonly children: readonly ChildNodeDefinition[];
|
|
14
|
+
readonly instances = null;
|
|
15
|
+
readonly defaultValue = null;
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
protected readonly sequence: RepeatSequenceDefinition,
|
|
19
|
+
readonly node: Element
|
|
20
|
+
) {
|
|
21
|
+
const {
|
|
22
|
+
bind,
|
|
23
|
+
bodyElement: repeatGroupBodyElement,
|
|
24
|
+
parent: repeatSequenceParent,
|
|
25
|
+
root,
|
|
26
|
+
} = sequence;
|
|
27
|
+
|
|
28
|
+
super(repeatSequenceParent, bind, repeatGroupBodyElement.repeat);
|
|
29
|
+
|
|
30
|
+
this.nodeName = sequence.nodeName;
|
|
31
|
+
this.children = root.buildSubtree(this);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
toJSON() {
|
|
35
|
+
const { bind, bodyElement, parent, root, sequence, ...rest } = this;
|
|
36
|
+
|
|
37
|
+
return rest;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { RepeatGroupDefinition } from '../body/group/RepeatGroupDefinition.ts';
|
|
2
|
+
import type { BindDefinition } from './BindDefinition.ts';
|
|
3
|
+
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
4
|
+
import type { NodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
|
|
5
|
+
import { RepeatInstanceDefinition } from './RepeatInstanceDefinition.ts';
|
|
6
|
+
import { RepeatTemplateDefinition } from './RepeatTemplateDefinition.ts';
|
|
7
|
+
|
|
8
|
+
export class RepeatSequenceDefinition
|
|
9
|
+
extends DescendentNodeDefinition<'repeat-sequence', RepeatGroupDefinition>
|
|
10
|
+
implements NodeDefinition<'repeat-sequence'>
|
|
11
|
+
{
|
|
12
|
+
// TODO: if an implicit template is derived from an instance in a form
|
|
13
|
+
// definition, should its default values (if any) be cleared? Probably!
|
|
14
|
+
static createTemplateElement(instanceElement: Element): Element {
|
|
15
|
+
return instanceElement.cloneNode(true) as Element;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static createInstanceElement(templateElement: Element): Element {
|
|
19
|
+
return templateElement.cloneNode(true) as Element;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
readonly type = 'repeat-sequence';
|
|
23
|
+
|
|
24
|
+
readonly template: RepeatTemplateDefinition;
|
|
25
|
+
readonly children = null;
|
|
26
|
+
readonly instances: RepeatInstanceDefinition[];
|
|
27
|
+
|
|
28
|
+
readonly node = null;
|
|
29
|
+
readonly nodeName: string;
|
|
30
|
+
readonly defaultValue = null;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
parent: ParentNodeDefinition,
|
|
34
|
+
bind: BindDefinition,
|
|
35
|
+
bodyElement: RepeatGroupDefinition,
|
|
36
|
+
modelNodes: readonly [Element, ...Element[]]
|
|
37
|
+
) {
|
|
38
|
+
super(parent, bind, bodyElement);
|
|
39
|
+
const { template, instanceNodes } = RepeatTemplateDefinition.parseModelNodes(this, modelNodes);
|
|
40
|
+
|
|
41
|
+
this.template = template;
|
|
42
|
+
this.nodeName = template.nodeName;
|
|
43
|
+
this.instances = instanceNodes.map((element) => {
|
|
44
|
+
return new RepeatInstanceDefinition(this, element);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
toJSON() {
|
|
49
|
+
const { bind, bodyElement: groupDefinition, parent, root, ...rest } = this;
|
|
50
|
+
|
|
51
|
+
return rest;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { JAVAROSA_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
|
|
2
|
+
import type { RepeatDefinition } from '../body/RepeatDefinition.ts';
|
|
3
|
+
import { BindDefinition } from './BindDefinition.ts';
|
|
4
|
+
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
5
|
+
import type { ChildNodeDefinition, NodeDefinition } from './NodeDefinition.ts';
|
|
6
|
+
import type { RepeatSequenceDefinition } from './RepeatSequenceDefinition.ts';
|
|
7
|
+
|
|
8
|
+
const repeatTemplates = new WeakMap<BindDefinition, RepeatTemplateDefinition>();
|
|
9
|
+
|
|
10
|
+
interface ExplicitRepeatTemplateElement extends Element {
|
|
11
|
+
getAttributeNS(namespaceURI: typeof JAVAROSA_NAMESPACE_URI, name: 'template'): string;
|
|
12
|
+
getAttributeNS(namespaceURI: string | null, name: string): string | null;
|
|
13
|
+
|
|
14
|
+
hasAttributeNS(namespaceURI: typeof JAVAROSA_NAMESPACE_URI, name: 'template'): true;
|
|
15
|
+
(namespaceURI: string | null, name: string): boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const isExplicitRepeatTemplateElement = (
|
|
19
|
+
element: Element
|
|
20
|
+
): element is ExplicitRepeatTemplateElement => {
|
|
21
|
+
return element.hasAttributeNS(JAVAROSA_NAMESPACE_URI, 'template');
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type InstanceNodes = readonly [template: ExplicitRepeatTemplateElement, ...rest: Element[]];
|
|
25
|
+
|
|
26
|
+
interface LeafNode extends Element {
|
|
27
|
+
readonly childElementCount: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const isLeafNode = (element: Element): element is LeafNode => {
|
|
31
|
+
return element.childElementCount === 0;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const clearLeafNodes = <T extends Element>(element: T): T => {
|
|
35
|
+
if (isLeafNode(element)) {
|
|
36
|
+
element.textContent = '';
|
|
37
|
+
} else {
|
|
38
|
+
for (const child of element.children) {
|
|
39
|
+
clearLeafNodes(child);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return element;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getOrCreateTemplateElement = (element: Element): ExplicitRepeatTemplateElement => {
|
|
47
|
+
if (isExplicitRepeatTemplateElement(element)) {
|
|
48
|
+
return element;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const clone = element.cloneNode(true) as Element;
|
|
52
|
+
clone.setAttributeNS(JAVAROSA_NAMESPACE_URI, 'template', '');
|
|
53
|
+
|
|
54
|
+
return clearLeafNodes(clone as ExplicitRepeatTemplateElement);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// TODO: under what circumstances should a default instance be created, and is
|
|
58
|
+
// this the appropriate place for that?
|
|
59
|
+
const splitInstanceNodes = (modelNodes: readonly [Element, ...Element[]]): InstanceNodes => {
|
|
60
|
+
const [first, ...rest] = modelNodes;
|
|
61
|
+
const template = getOrCreateTemplateElement(first);
|
|
62
|
+
|
|
63
|
+
if (template === first) {
|
|
64
|
+
return [template, ...rest];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return [template, ...modelNodes];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
interface ParsedRepeatNodes {
|
|
71
|
+
readonly template: RepeatTemplateDefinition;
|
|
72
|
+
readonly instanceNodes: readonly Element[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class RepeatTemplateDefinition
|
|
76
|
+
extends DescendentNodeDefinition<'repeat-template', RepeatDefinition>
|
|
77
|
+
implements NodeDefinition<'repeat-template'>
|
|
78
|
+
{
|
|
79
|
+
static parseModelNodes(
|
|
80
|
+
sequence: RepeatSequenceDefinition,
|
|
81
|
+
modelNodes: readonly [Element, ...Element[]]
|
|
82
|
+
): ParsedRepeatNodes {
|
|
83
|
+
const { bind } = sequence;
|
|
84
|
+
|
|
85
|
+
let template = repeatTemplates.get(bind);
|
|
86
|
+
let instanceNodes: readonly Element[];
|
|
87
|
+
|
|
88
|
+
if (template == null) {
|
|
89
|
+
const [templateNode, ...rest] = splitInstanceNodes(modelNodes);
|
|
90
|
+
|
|
91
|
+
instanceNodes = rest;
|
|
92
|
+
template = new this(sequence, templateNode);
|
|
93
|
+
} else {
|
|
94
|
+
// TODO: this is under the assumption that for any depth > 1, if a
|
|
95
|
+
// template has already been defined for the given form definition, any
|
|
96
|
+
// subsequent nodes matching the repeat's nodeset are implicitly default
|
|
97
|
+
// instances. Is this right?
|
|
98
|
+
const duplicateTemplate = modelNodes.find((node) =>
|
|
99
|
+
node.hasAttributeNS(JAVAROSA_NAMESPACE_URI, 'template')
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
if (duplicateTemplate != null) {
|
|
103
|
+
throw new Error(`Multiple explicit templates defined for ${bind.nodeset}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
instanceNodes = modelNodes;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
template,
|
|
111
|
+
instanceNodes,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
readonly type = 'repeat-template';
|
|
116
|
+
|
|
117
|
+
readonly node: Element;
|
|
118
|
+
readonly nodeName: string;
|
|
119
|
+
readonly children: readonly ChildNodeDefinition[];
|
|
120
|
+
readonly instances = null;
|
|
121
|
+
readonly defaultValue = null;
|
|
122
|
+
|
|
123
|
+
protected constructor(
|
|
124
|
+
protected readonly sequence: RepeatSequenceDefinition,
|
|
125
|
+
protected readonly templateNode: ExplicitRepeatTemplateElement
|
|
126
|
+
) {
|
|
127
|
+
const {
|
|
128
|
+
bind,
|
|
129
|
+
bodyElement: repeatGroupBodyElement,
|
|
130
|
+
parent: repeatSequenceParent,
|
|
131
|
+
root,
|
|
132
|
+
} = sequence;
|
|
133
|
+
|
|
134
|
+
super(repeatSequenceParent, bind, repeatGroupBodyElement.repeat);
|
|
135
|
+
|
|
136
|
+
const node = templateNode.cloneNode(true) as Element;
|
|
137
|
+
|
|
138
|
+
node.removeAttributeNS(JAVAROSA_NAMESPACE_URI, 'template');
|
|
139
|
+
|
|
140
|
+
this.node = node;
|
|
141
|
+
this.nodeName = node.localName;
|
|
142
|
+
this.children = root.buildSubtree(this);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
toJSON() {
|
|
146
|
+
const { bind, bodyElement, parent, root, sequence, ...rest } = this;
|
|
147
|
+
|
|
148
|
+
return rest;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { XFormDefinition } from '../XFormDefinition.ts';
|
|
2
|
+
import type { RepeatGroupDefinition } from '../body/group/RepeatGroupDefinition.ts';
|
|
3
|
+
import type { BindDefinition } from './BindDefinition.ts';
|
|
4
|
+
import type { ModelDefinition } from './ModelDefinition.ts';
|
|
5
|
+
import type {
|
|
6
|
+
ChildNodeDefinition,
|
|
7
|
+
NodeDefinition,
|
|
8
|
+
ParentNodeDefinition,
|
|
9
|
+
} from './NodeDefinition.ts';
|
|
10
|
+
import { RepeatSequenceDefinition } from './RepeatSequenceDefinition.ts';
|
|
11
|
+
import { SubtreeDefinition } from './SubtreeDefinition.ts';
|
|
12
|
+
import { ValueNodeDefinition } from './ValueNodeDefinition.ts';
|
|
13
|
+
|
|
14
|
+
export class RootDefinition implements NodeDefinition<'root'> {
|
|
15
|
+
readonly type = 'root';
|
|
16
|
+
readonly bind: BindDefinition;
|
|
17
|
+
readonly nodeset: string;
|
|
18
|
+
readonly nodeName: string;
|
|
19
|
+
readonly bodyElement = null;
|
|
20
|
+
readonly root = this;
|
|
21
|
+
readonly parent = null;
|
|
22
|
+
readonly children: readonly ChildNodeDefinition[];
|
|
23
|
+
readonly instances = null;
|
|
24
|
+
readonly node: Element;
|
|
25
|
+
readonly defaultValue = null;
|
|
26
|
+
|
|
27
|
+
readonly isTranslated = false;
|
|
28
|
+
readonly dependencyExpressions: ReadonlySet<string> = new Set<string>();
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
protected readonly form: XFormDefinition,
|
|
32
|
+
protected readonly model: ModelDefinition
|
|
33
|
+
) {
|
|
34
|
+
// TODO: theoretically the pertinent step in the bind's `nodeset` *could* be
|
|
35
|
+
// namespaced. It also may make more sense to determine the root nodeset
|
|
36
|
+
// earlier (i.e. in the appropriate definition class).
|
|
37
|
+
//
|
|
38
|
+
// TODO: while it's unlikely a form actually defines a <bind> for the root,
|
|
39
|
+
// if it did, bind nodesets are not yet normalized, so `/root` may currently
|
|
40
|
+
// be defined as `/ root` (or even `/ *` or any other valid expression
|
|
41
|
+
// resolving to the root).
|
|
42
|
+
const { primaryInstanceRoot } = form.xformDOM;
|
|
43
|
+
const { localName: rootNodeName } = primaryInstanceRoot;
|
|
44
|
+
|
|
45
|
+
this.nodeName = rootNodeName;
|
|
46
|
+
|
|
47
|
+
const nodeset = `/${rootNodeName}`;
|
|
48
|
+
const bind = model.binds.get(nodeset);
|
|
49
|
+
|
|
50
|
+
if (bind == null) {
|
|
51
|
+
throw new Error('Missing root node bind definition');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.bind = bind;
|
|
55
|
+
this.nodeset = nodeset;
|
|
56
|
+
this.node = primaryInstanceRoot;
|
|
57
|
+
this.children = this.buildSubtree(this);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
buildSubtree(parent: ParentNodeDefinition): readonly ChildNodeDefinition[] {
|
|
61
|
+
const { form, model } = this;
|
|
62
|
+
const { body } = form;
|
|
63
|
+
const { binds } = model;
|
|
64
|
+
const { bind: parentBind, node } = parent;
|
|
65
|
+
const { nodeset: parentNodeset } = parentBind;
|
|
66
|
+
|
|
67
|
+
const childrenByName = new Map<string, [Element, ...Element[]]>();
|
|
68
|
+
|
|
69
|
+
for (const child of node.children) {
|
|
70
|
+
const { localName } = child;
|
|
71
|
+
|
|
72
|
+
let elements = childrenByName.get(localName);
|
|
73
|
+
|
|
74
|
+
if (elements == null) {
|
|
75
|
+
elements = [child];
|
|
76
|
+
childrenByName.set(localName, elements);
|
|
77
|
+
} else {
|
|
78
|
+
// TODO: check if previous element exists, was it previous element
|
|
79
|
+
// sibling. Highly likely this should otherwise fail!
|
|
80
|
+
elements.push(child);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return Array.from(childrenByName).map(([localName, children]) => {
|
|
85
|
+
const nodeset = `${parentNodeset}/${localName}`;
|
|
86
|
+
const bind = binds.getOrCreateBindDefinition(nodeset);
|
|
87
|
+
const bodyElement = body.getBodyElement(nodeset);
|
|
88
|
+
const [firstChild, ...restChildren] = children;
|
|
89
|
+
const repeatGroup = body.getRepeatGroup(nodeset);
|
|
90
|
+
|
|
91
|
+
if (repeatGroup != null) {
|
|
92
|
+
const repeatDefinition = (bodyElement as RepeatGroupDefinition).repeat;
|
|
93
|
+
|
|
94
|
+
if (repeatDefinition == null) {
|
|
95
|
+
throw 'TODO: this is why I have hesitated to pick an "is repeat" predicate direction';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return new RepeatSequenceDefinition(parent, bind, repeatGroup, children);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (restChildren.length) {
|
|
102
|
+
throw new Error(`Unexpected: multiple elements for non-repeat nodeset: ${nodeset}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const element = firstChild;
|
|
106
|
+
const isLeafNode = element.childElementCount === 0;
|
|
107
|
+
|
|
108
|
+
if (isLeafNode) {
|
|
109
|
+
return new ValueNodeDefinition(parent, bind, bodyElement, element);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return new SubtreeDefinition(parent, bind, bodyElement, element);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
toJSON() {
|
|
117
|
+
const { bind, bodyElement, form, model, root, ...rest } = this;
|
|
118
|
+
|
|
119
|
+
return rest;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnyBodyElementDefinition,
|
|
3
|
+
NonRepeatGroupElementDefinition,
|
|
4
|
+
} from '../body/BodyDefinition.ts';
|
|
5
|
+
import type { BindDefinition } from './BindDefinition.ts';
|
|
6
|
+
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
7
|
+
import type {
|
|
8
|
+
ChildNodeDefinition,
|
|
9
|
+
NodeDefinition,
|
|
10
|
+
ParentNodeDefinition,
|
|
11
|
+
} from './NodeDefinition.ts';
|
|
12
|
+
|
|
13
|
+
export class SubtreeDefinition
|
|
14
|
+
extends DescendentNodeDefinition<'subtree', NonRepeatGroupElementDefinition | null>
|
|
15
|
+
implements NodeDefinition<'subtree'>
|
|
16
|
+
{
|
|
17
|
+
readonly type = 'subtree';
|
|
18
|
+
|
|
19
|
+
readonly nodeName: string;
|
|
20
|
+
readonly children: readonly ChildNodeDefinition[];
|
|
21
|
+
readonly instances = null;
|
|
22
|
+
readonly defaultValue = null;
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
parent: ParentNodeDefinition,
|
|
26
|
+
bind: BindDefinition,
|
|
27
|
+
bodyElement: AnyBodyElementDefinition | null,
|
|
28
|
+
readonly node: Element
|
|
29
|
+
) {
|
|
30
|
+
if (
|
|
31
|
+
bodyElement != null &&
|
|
32
|
+
(bodyElement.category !== 'structure' || bodyElement.type === 'repeat-group')
|
|
33
|
+
) {
|
|
34
|
+
throw new Error(`Unexpected body element for nodeset ${bind.nodeset}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
super(parent, bind, bodyElement);
|
|
38
|
+
|
|
39
|
+
const { root } = parent;
|
|
40
|
+
|
|
41
|
+
this.nodeName = node.localName;
|
|
42
|
+
this.children = root.buildSubtree(this);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
toJSON() {
|
|
46
|
+
const { parent, bodyElement, bind, root, ...rest } = this;
|
|
47
|
+
|
|
48
|
+
return rest;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
|
|
2
|
+
import type { AnyControlDefinition } from '../body/control/ControlDefinition.ts';
|
|
3
|
+
import type { BindDefinition } from './BindDefinition.ts';
|
|
4
|
+
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
5
|
+
import type { NodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
|
|
6
|
+
|
|
7
|
+
export class ValueNodeDefinition
|
|
8
|
+
extends DescendentNodeDefinition<'value-node', AnyControlDefinition | null>
|
|
9
|
+
implements NodeDefinition<'value-node'>
|
|
10
|
+
{
|
|
11
|
+
readonly type = 'value-node';
|
|
12
|
+
|
|
13
|
+
readonly nodeName: string;
|
|
14
|
+
readonly children = null;
|
|
15
|
+
readonly instances = null;
|
|
16
|
+
readonly defaultValue: string;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
parent: ParentNodeDefinition,
|
|
20
|
+
bind: BindDefinition,
|
|
21
|
+
bodyElement: AnyBodyElementDefinition | null,
|
|
22
|
+
readonly node: Element
|
|
23
|
+
) {
|
|
24
|
+
if (bodyElement != null && bodyElement.category !== 'control') {
|
|
25
|
+
throw new Error(`Unexpected body element for nodeset ${bind.nodeset}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
super(parent, bind, bodyElement);
|
|
29
|
+
|
|
30
|
+
this.nodeName = node.localName;
|
|
31
|
+
this.defaultValue = node.textContent ?? '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
toJSON() {
|
|
35
|
+
const { bind, bodyElement, parent, root, ...rest } = this;
|
|
36
|
+
|
|
37
|
+
return rest;
|
|
38
|
+
}
|
|
39
|
+
}
|