@getodk/xforms-engine 0.5.0 → 0.6.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.
Files changed (232) hide show
  1. package/README.md +1 -1
  2. package/dist/client/BaseNode.d.ts +4 -0
  3. package/dist/client/BaseValueNode.d.ts +1 -1
  4. package/dist/client/GroupNode.d.ts +1 -0
  5. package/dist/client/InputNode.d.ts +32 -3
  6. package/dist/client/ModelValueNode.d.ts +1 -0
  7. package/dist/client/NoteNode.d.ts +24 -7
  8. package/dist/client/RangeNode.d.ts +36 -0
  9. package/dist/client/RankNode.d.ts +46 -0
  10. package/dist/client/RootNode.d.ts +6 -3
  11. package/dist/client/SelectNode.d.ts +47 -25
  12. package/dist/client/SubtreeNode.d.ts +1 -0
  13. package/dist/client/TriggerNode.d.ts +10 -6
  14. package/dist/client/hierarchy.d.ts +5 -5
  15. package/dist/client/node-types.d.ts +2 -2
  16. package/dist/client/unsupported/UnsupportedControlNode.d.ts +1 -3
  17. package/dist/error/RankMissingValueError.d.ts +3 -0
  18. package/dist/error/RankValueTypeError.d.ts +6 -0
  19. package/dist/error/SelectValueTypeError.d.ts +15 -0
  20. package/dist/error/XFormsSpecViolationError.d.ts +2 -0
  21. package/dist/index.d.ts +2 -3
  22. package/dist/index.js +5604 -4666
  23. package/dist/index.js.map +1 -1
  24. package/dist/instance/Group.d.ts +1 -0
  25. package/dist/instance/InputControl.d.ts +5 -3
  26. package/dist/instance/ModelValue.d.ts +2 -0
  27. package/dist/instance/Note.d.ts +13 -25
  28. package/dist/instance/PrimaryInstance.d.ts +1 -0
  29. package/dist/instance/RangeControl.d.ts +34 -0
  30. package/dist/instance/RankControl.d.ts +40 -0
  31. package/dist/instance/Root.d.ts +1 -0
  32. package/dist/instance/SelectControl.d.ts +66 -0
  33. package/dist/instance/Subtree.d.ts +1 -0
  34. package/dist/instance/TriggerControl.d.ts +9 -22
  35. package/dist/instance/abstract/DescendantNode.d.ts +1 -2
  36. package/dist/instance/abstract/InstanceNode.d.ts +3 -1
  37. package/dist/instance/abstract/UnsupportedControl.d.ts +1 -0
  38. package/dist/instance/abstract/ValueNode.d.ts +0 -1
  39. package/dist/instance/hierarchy.d.ts +9 -9
  40. package/dist/instance/internal-api/InstanceValueContext.d.ts +2 -0
  41. package/dist/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.d.ts +2 -1
  42. package/dist/instance/internal-api/submission/ClientReactiveSubmittableParentNode.d.ts +2 -1
  43. package/dist/instance/internal-api/submission/ClientReactiveSubmittableValueNode.d.ts +2 -1
  44. package/dist/instance/repeat/BaseRepeatRange.d.ts +1 -0
  45. package/dist/instance/repeat/RepeatInstance.d.ts +1 -0
  46. package/dist/integration/xpath/adapter/names.d.ts +1 -11
  47. package/dist/integration/xpath/adapter/traversal.d.ts +10 -9
  48. package/dist/integration/xpath/static-dom/StaticAttribute.d.ts +10 -3
  49. package/dist/integration/xpath/static-dom/StaticDocument.d.ts +0 -1
  50. package/dist/integration/xpath/static-dom/StaticElement.d.ts +12 -4
  51. package/dist/lib/client-reactivity/submission/createRootSubmissionState.d.ts +3 -0
  52. package/dist/lib/codecs/Geopoint/Geopoint.d.ts +48 -0
  53. package/dist/lib/codecs/Geopoint/GeopointValueCodec.d.ts +5 -0
  54. package/dist/lib/codecs/NoteCodec.d.ts +8 -0
  55. package/dist/lib/codecs/RangeCodec.d.ts +8 -0
  56. package/dist/lib/codecs/TriggerCodec.d.ts +7 -0
  57. package/dist/lib/codecs/ValueArrayCodec.d.ts +11 -0
  58. package/dist/lib/codecs/ValueCodec.d.ts +2 -2
  59. package/dist/lib/codecs/getNoteCodec.d.ts +3 -0
  60. package/dist/lib/codecs/getSelectCodec.d.ts +5 -0
  61. package/dist/lib/codecs/getSharedValueCodec.d.ts +3 -2
  62. package/dist/lib/codecs/items/BaseItemCodec.d.ts +9 -0
  63. package/dist/lib/codecs/items/MultipleValueItemCodec.d.ts +14 -0
  64. package/dist/lib/codecs/items/SingleValueItemCodec.d.ts +24 -0
  65. package/dist/lib/dom/query.d.ts +1 -2
  66. package/dist/lib/names/NamespaceDeclaration.d.ts +45 -0
  67. package/dist/lib/names/NamespaceDeclarationMap.d.ts +137 -0
  68. package/dist/lib/names/NamespaceURL.d.ts +30 -0
  69. package/dist/lib/names/QualifiedName.d.ts +113 -0
  70. package/dist/lib/number-parsers.d.ts +2 -0
  71. package/dist/lib/reactivity/createItemCollection.d.ts +21 -0
  72. package/dist/lib/xml-serialization.d.ts +11 -2
  73. package/dist/parse/XFormDOM.d.ts +1 -1
  74. package/dist/parse/body/BodyDefinition.d.ts +2 -2
  75. package/dist/parse/body/appearance/rangeAppearanceParser.d.ts +3 -0
  76. package/dist/parse/body/control/InputControlDefinition.d.ts +3 -0
  77. package/dist/parse/body/control/ItemDefinition.d.ts +14 -0
  78. package/dist/parse/body/control/ItemsetDefinition.d.ts +18 -0
  79. package/dist/parse/body/control/RangeControlDefinition.d.ts +31 -2
  80. package/dist/parse/body/control/RankControlDefinition.d.ts +7 -3
  81. package/dist/parse/body/control/{select/SelectDefinition.d.ts → SelectControlDefinition.d.ts} +9 -9
  82. package/dist/parse/expression/ItemsetNodesetExpression.d.ts +1 -1
  83. package/dist/parse/expression/ItemsetValueExpression.d.ts +1 -1
  84. package/dist/parse/model/BindDefinition.d.ts +3 -1
  85. package/dist/parse/model/BindPreloadDefinition.d.ts +42 -0
  86. package/dist/parse/model/DescendentNodeDefinition.d.ts +4 -13
  87. package/dist/parse/model/ItextTranslation/ItextTranslationRootDefinition.d.ts +2 -1
  88. package/dist/parse/model/LeafNodeDefinition.d.ts +7 -4
  89. package/dist/parse/model/ModelBindMap.d.ts +1 -1
  90. package/dist/parse/model/NodeDefinition.d.ts +16 -19
  91. package/dist/parse/model/NoteNodeDefinition.d.ts +6 -5
  92. package/dist/parse/model/RangeNodeDefinition.d.ts +41 -0
  93. package/dist/parse/model/RepeatInstanceDefinition.d.ts +7 -4
  94. package/dist/parse/model/RepeatRangeDefinition.d.ts +7 -4
  95. package/dist/parse/model/RepeatTemplateDefinition.d.ts +7 -4
  96. package/dist/parse/model/RootAttributeDefinition.d.ts +24 -0
  97. package/dist/parse/model/RootAttributeMap.d.ts +23 -0
  98. package/dist/parse/model/RootDefinition.d.ts +9 -7
  99. package/dist/parse/model/SubtreeDefinition.d.ts +7 -4
  100. package/dist/parse/shared/parseStaticDocumentFromDOMSubtree.d.ts +2 -3
  101. package/dist/parse/text/ItemLabelDefinition.d.ts +1 -1
  102. package/dist/parse/text/ItemsetLabelDefinition.d.ts +2 -2
  103. package/dist/parse/text/abstract/TextElementDefinition.d.ts +1 -1
  104. package/dist/parse/xpath/semantic-analysis.d.ts +1 -3
  105. package/dist/solid.js +5603 -4665
  106. package/dist/solid.js.map +1 -1
  107. package/package.json +15 -12
  108. package/src/client/BaseNode.ts +5 -0
  109. package/src/client/BaseValueNode.ts +1 -1
  110. package/src/client/GroupNode.ts +1 -0
  111. package/src/client/InputNode.ts +38 -2
  112. package/src/client/ModelValueNode.ts +1 -0
  113. package/src/client/NoteNode.ts +43 -7
  114. package/src/client/RangeNode.ts +51 -0
  115. package/src/client/RankNode.ts +54 -0
  116. package/src/client/RootNode.ts +11 -5
  117. package/src/client/SelectNode.ts +53 -26
  118. package/src/client/SubtreeNode.ts +1 -0
  119. package/src/client/TriggerNode.ts +12 -6
  120. package/src/client/hierarchy.ts +7 -8
  121. package/src/client/node-types.ts +1 -1
  122. package/src/client/unsupported/UnsupportedControlNode.ts +2 -6
  123. package/src/error/RankMissingValueError.ts +5 -0
  124. package/src/error/RankValueTypeError.ts +13 -0
  125. package/src/error/SelectValueTypeError.ts +22 -0
  126. package/src/error/XFormsSpecViolationError.ts +1 -0
  127. package/src/index.ts +2 -12
  128. package/src/instance/Group.ts +1 -0
  129. package/src/instance/InputControl.ts +42 -2
  130. package/src/instance/ModelValue.ts +2 -0
  131. package/src/instance/Note.ts +34 -59
  132. package/src/instance/PrimaryInstance.ts +1 -0
  133. package/src/instance/RangeControl.ts +113 -0
  134. package/src/instance/RankControl.ts +199 -0
  135. package/src/instance/Root.ts +3 -2
  136. package/src/instance/SelectControl.ts +219 -0
  137. package/src/instance/Subtree.ts +1 -0
  138. package/src/instance/TriggerControl.ts +36 -75
  139. package/src/instance/abstract/DescendantNode.ts +1 -6
  140. package/src/instance/abstract/InstanceNode.ts +10 -2
  141. package/src/instance/abstract/UnsupportedControl.ts +1 -0
  142. package/src/instance/abstract/ValueNode.ts +3 -2
  143. package/src/instance/children.ts +71 -30
  144. package/src/instance/hierarchy.ts +21 -16
  145. package/src/instance/internal-api/InstanceValueContext.ts +2 -0
  146. package/src/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.ts +2 -1
  147. package/src/instance/internal-api/submission/ClientReactiveSubmittableParentNode.ts +2 -1
  148. package/src/instance/internal-api/submission/ClientReactiveSubmittableValueNode.ts +2 -1
  149. package/src/instance/repeat/BaseRepeatRange.ts +2 -0
  150. package/src/instance/repeat/RepeatInstance.ts +1 -0
  151. package/src/instance/resource.ts +4 -1
  152. package/src/integration/xpath/adapter/names.ts +66 -17
  153. package/src/integration/xpath/adapter/traversal.ts +10 -9
  154. package/src/integration/xpath/static-dom/StaticAttribute.ts +15 -7
  155. package/src/integration/xpath/static-dom/StaticDocument.ts +0 -2
  156. package/src/integration/xpath/static-dom/StaticElement.ts +21 -8
  157. package/src/lib/client-reactivity/submission/createLeafNodeSubmissionState.ts +1 -1
  158. package/src/lib/client-reactivity/submission/createParentNodeSubmissionState.ts +1 -1
  159. package/src/lib/client-reactivity/submission/createRootSubmissionState.ts +19 -0
  160. package/src/lib/client-reactivity/submission/createValueNodeSubmissionState.ts +2 -2
  161. package/src/lib/codecs/Geopoint/Geopoint.ts +150 -0
  162. package/src/lib/codecs/Geopoint/GeopointValueCodec.ts +20 -0
  163. package/src/lib/codecs/NoteCodec.ts +32 -0
  164. package/src/lib/codecs/RangeCodec.ts +65 -0
  165. package/src/lib/codecs/TriggerCodec.ts +64 -0
  166. package/src/lib/codecs/ValueArrayCodec.ts +42 -0
  167. package/src/lib/codecs/ValueCodec.ts +2 -2
  168. package/src/lib/codecs/getNoteCodec.ts +27 -0
  169. package/src/lib/codecs/getSelectCodec.ts +27 -0
  170. package/src/lib/codecs/getSharedValueCodec.ts +5 -3
  171. package/src/lib/codecs/items/BaseItemCodec.ts +20 -0
  172. package/src/lib/codecs/items/MultipleValueItemCodec.ts +28 -0
  173. package/src/lib/codecs/items/SingleValueItemCodec.ts +67 -0
  174. package/src/lib/dom/query.ts +1 -2
  175. package/src/lib/names/NamespaceDeclaration.ts +106 -0
  176. package/src/lib/names/NamespaceDeclarationMap.ts +228 -0
  177. package/src/lib/names/NamespaceURL.ts +44 -0
  178. package/src/lib/names/QualifiedName.ts +170 -0
  179. package/src/lib/number-parsers.ts +25 -0
  180. package/src/lib/reactivity/createInstanceValueState.ts +50 -0
  181. package/src/lib/reactivity/{createSelectItems.ts → createItemCollection.ts} +41 -36
  182. package/src/lib/xml-serialization.ts +76 -9
  183. package/src/parse/XFormDOM.ts +141 -21
  184. package/src/parse/XFormDefinition.ts +1 -4
  185. package/src/parse/body/BodyDefinition.ts +4 -4
  186. package/src/parse/body/appearance/rangeAppearanceParser.ts +11 -0
  187. package/src/parse/body/control/InputControlDefinition.ts +9 -0
  188. package/src/parse/body/control/{select/ItemDefinition.ts → ItemDefinition.ts} +8 -6
  189. package/src/parse/body/control/{select/ItemsetDefinition.ts → ItemsetDefinition.ts} +11 -9
  190. package/src/parse/body/control/RangeControlDefinition.ts +91 -6
  191. package/src/parse/body/control/RankControlDefinition.ts +25 -7
  192. package/src/parse/body/control/{select/SelectDefinition.ts → SelectControlDefinition.ts} +9 -9
  193. package/src/parse/expression/ItemsetNodesetExpression.ts +1 -1
  194. package/src/parse/expression/ItemsetValueExpression.ts +1 -1
  195. package/src/parse/model/BindDefinition.ts +4 -0
  196. package/src/parse/model/BindPreloadDefinition.ts +100 -0
  197. package/src/parse/model/DescendentNodeDefinition.ts +7 -25
  198. package/src/parse/model/ItextTranslation/ItextTranslationRootDefinition.ts +2 -1
  199. package/src/parse/model/LeafNodeDefinition.ts +11 -4
  200. package/src/parse/model/NodeDefinition.ts +24 -45
  201. package/src/parse/model/NoteNodeDefinition.ts +8 -7
  202. package/src/parse/model/RangeNodeDefinition.ts +118 -0
  203. package/src/parse/model/RepeatInstanceDefinition.ts +11 -7
  204. package/src/parse/model/RepeatRangeDefinition.ts +11 -7
  205. package/src/parse/model/RepeatTemplateDefinition.ts +11 -7
  206. package/src/parse/model/RootAttributeDefinition.ts +45 -0
  207. package/src/parse/model/RootAttributeMap.ts +44 -0
  208. package/src/parse/model/RootDefinition.ts +29 -28
  209. package/src/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.ts +1 -0
  210. package/src/parse/model/SubtreeDefinition.ts +12 -12
  211. package/src/parse/shared/parseStaticDocumentFromDOMSubtree.ts +3 -3
  212. package/src/parse/text/ItemLabelDefinition.ts +1 -1
  213. package/src/parse/text/ItemsetLabelDefinition.ts +2 -2
  214. package/src/parse/text/abstract/TextElementDefinition.ts +1 -1
  215. package/src/parse/xpath/semantic-analysis.ts +4 -3
  216. package/dist/client/unsupported/RangeNode.d.ts +0 -9
  217. package/dist/client/unsupported/RankNode.d.ts +0 -9
  218. package/dist/instance/SelectField.d.ts +0 -58
  219. package/dist/instance/unsupported/RangeControl.d.ts +0 -6
  220. package/dist/instance/unsupported/RankControl.d.ts +0 -6
  221. package/dist/integration/xpath/static-dom/StaticNamedNode.d.ts +0 -17
  222. package/dist/lib/reactivity/createSelectItems.d.ts +0 -16
  223. package/dist/parse/body/control/select/ItemDefinition.d.ts +0 -13
  224. package/dist/parse/body/control/select/ItemsetDefinition.d.ts +0 -17
  225. package/dist/parse/body/control/select/ItemsetNodesetContext.d.ts +0 -9
  226. package/src/client/unsupported/RangeNode.ts +0 -14
  227. package/src/client/unsupported/RankNode.ts +0 -14
  228. package/src/instance/SelectField.ts +0 -263
  229. package/src/instance/unsupported/RangeControl.ts +0 -9
  230. package/src/instance/unsupported/RankControl.ts +0 -9
  231. package/src/integration/xpath/static-dom/StaticNamedNode.ts +0 -45
  232. package/src/parse/body/control/select/ItemsetNodesetContext.ts +0 -21
@@ -1,21 +1,26 @@
1
1
  import { UpsertableMap } from '@getodk/common/lib/collections/UpsertableMap.ts';
2
2
  import type { Accessor } from 'solid-js';
3
3
  import { createMemo } from 'solid-js';
4
+ import type { ActiveLanguage } from '../../client/FormLanguage.ts';
5
+ import type { SelectItem } from '../../client/SelectNode.ts';
6
+ import type { RankItem } from '../../client/RankNode.ts';
4
7
  import type { TextRange as ClientTextRange } from '../../client/TextRange.ts';
5
- import type { ActiveLanguage, SelectItem } from '../../index.ts';
6
8
  import type { EvaluationContext } from '../../instance/internal-api/EvaluationContext.ts';
7
9
  import type { TranslationContext } from '../../instance/internal-api/TranslationContext.ts';
8
- import type { SelectField } from '../../instance/SelectField.ts';
10
+ import type { SelectControl } from '../../instance/SelectControl.ts';
11
+ import type { RankControl } from '../../instance/RankControl.ts';
9
12
  import { TextChunk } from '../../instance/text/TextChunk.ts';
10
13
  import { TextRange } from '../../instance/text/TextRange.ts';
11
14
  import type { EngineXPathNode } from '../../integration/xpath/adapter/kind.ts';
12
15
  import type { EngineXPathEvaluator } from '../../integration/xpath/EngineXPathEvaluator.ts';
13
- import type { ItemDefinition } from '../../parse/body/control/select/ItemDefinition.ts';
14
- import type { ItemsetDefinition } from '../../parse/body/control/select/ItemsetDefinition.ts';
16
+ import type { ItemDefinition } from '../../parse/body/control/ItemDefinition.ts';
17
+ import type { ItemsetDefinition } from '../../parse/body/control/ItemsetDefinition.ts';
15
18
  import { createComputedExpression } from './createComputedExpression.ts';
16
19
  import type { ReactiveScope } from './scope.ts';
17
20
  import { createTextRange } from './text/createTextRange.ts';
18
21
 
22
+ export type ItemCollectionControl = RankControl | SelectControl;
23
+ type Item = RankItem | SelectItem;
19
24
  type DerivedItemLabel = ClientTextRange<'item-label', 'form-derived'>;
20
25
 
21
26
  const derivedItemLabel = (context: TranslationContext, value: string): DerivedItemLabel => {
@@ -24,7 +29,7 @@ const derivedItemLabel = (context: TranslationContext, value: string): DerivedIt
24
29
  return new TextRange('form-derived', 'item-label', [chunk]);
25
30
  };
26
31
 
27
- const createSelectItemLabel = (
32
+ const createItemLabel = (
28
33
  context: EvaluationContext,
29
34
  definition: ItemDefinition
30
35
  ): Accessor<ClientTextRange<'item-label'>> => {
@@ -37,14 +42,14 @@ const createSelectItemLabel = (
37
42
  return createTextRange(context, 'item-label', label);
38
43
  };
39
44
 
40
- const createTranslatedStaticSelectItems = (
41
- selectField: SelectField,
45
+ const createTranslatedStaticItems = (
46
+ control: ItemCollectionControl,
42
47
  items: readonly ItemDefinition[]
43
- ): Accessor<readonly SelectItem[]> => {
44
- return selectField.scope.runTask(() => {
48
+ ): Accessor<readonly Item[]> => {
49
+ return control.scope.runTask(() => {
45
50
  const labeledItems = items.map((item) => {
46
51
  const { value } = item;
47
- const label = createSelectItemLabel(selectField, item);
52
+ const label = createItemLabel(control, item);
48
53
 
49
54
  return () => ({
50
55
  value,
@@ -66,18 +71,18 @@ class ItemsetItemEvaluationContext implements EvaluationContext {
66
71
  readonly getActiveLanguage: Accessor<ActiveLanguage>;
67
72
 
68
73
  constructor(
69
- selectField: SelectField,
74
+ control: ItemCollectionControl,
70
75
  readonly contextNode: EngineXPathNode
71
76
  ) {
72
- this.isAttached = selectField.isAttached;
73
- this.scope = selectField.scope;
74
- this.evaluator = selectField.evaluator;
75
- this.contextReference = selectField.contextReference;
76
- this.getActiveLanguage = selectField.getActiveLanguage;
77
+ this.isAttached = control.isAttached;
78
+ this.scope = control.scope;
79
+ this.evaluator = control.evaluator;
80
+ this.contextReference = control.contextReference;
81
+ this.getActiveLanguage = control.getActiveLanguage;
77
82
  }
78
83
  }
79
84
 
80
- const createSelectItemsetItemLabel = (
85
+ const createItemsetItemLabel = (
81
86
  context: EvaluationContext,
82
87
  definition: ItemsetDefinition,
83
88
  itemValue: Accessor<string>
@@ -99,11 +104,11 @@ interface ItemsetItem {
99
104
  }
100
105
 
101
106
  const createItemsetItems = (
102
- selectField: SelectField,
107
+ control: ItemCollectionControl,
103
108
  itemset: ItemsetDefinition
104
109
  ): Accessor<readonly ItemsetItem[]> => {
105
- return selectField.scope.runTask(() => {
106
- const itemNodes = createComputedExpression(selectField, itemset.nodes, {
110
+ return control.scope.runTask(() => {
111
+ const itemNodes = createComputedExpression(control, itemset.nodes, {
107
112
  defaultValue: [],
108
113
  });
109
114
  const itemsCache = new UpsertableMap<EngineXPathNode, ItemsetItem>();
@@ -111,11 +116,11 @@ const createItemsetItems = (
111
116
  return createMemo(() => {
112
117
  return itemNodes().map((itemNode) => {
113
118
  return itemsCache.upsert(itemNode, () => {
114
- const context = new ItemsetItemEvaluationContext(selectField, itemNode);
119
+ const context = new ItemsetItemEvaluationContext(control, itemNode);
115
120
  const value = createComputedExpression(context, itemset.value, {
116
121
  defaultValue: '',
117
122
  });
118
- const label = createSelectItemsetItemLabel(context, itemset, value);
123
+ const label = createItemsetItemLabel(context, itemset, value);
119
124
 
120
125
  return {
121
126
  label,
@@ -128,11 +133,11 @@ const createItemsetItems = (
128
133
  };
129
134
 
130
135
  const createItemset = (
131
- selectField: SelectField,
136
+ control: ItemCollectionControl,
132
137
  itemset: ItemsetDefinition
133
- ): Accessor<readonly SelectItem[]> => {
134
- return selectField.scope.runTask(() => {
135
- const itemsetItems = createItemsetItems(selectField, itemset);
138
+ ): Accessor<readonly Item[]> => {
139
+ return control.scope.runTask(() => {
140
+ const itemsetItems = createItemsetItems(control, itemset);
136
141
 
137
142
  return createMemo(() => {
138
143
  return itemsetItems().map((item) => {
@@ -146,23 +151,23 @@ const createItemset = (
146
151
  };
147
152
 
148
153
  /**
149
- * Creates a reactive computation of a {@link SelectField}'s
150
- * {@link SelectItem}s, in support of the field's `valueOptions`.
154
+ * Creates a reactive computation of a {@link ItemCollectionControl}'s
155
+ * {@link Item}s, in support of the field's `valueOptions`.
151
156
  *
152
- * - Selects defined with static `<item>`s will compute to an corresponding
157
+ * - The control defined with static `<item>`s will compute to an corresponding
153
158
  * static list of items.
154
- * - Selects defined with a computed `<itemset>` will compute to a reactive list
159
+ * - The control defined with a computed `<itemset>` will compute to a reactive list
155
160
  * of items.
156
- * - Items of both will produce {@link SelectItem.label | labels} reactive to
161
+ * - Items of both will produce {@link ItemType.label | labels} reactive to
157
162
  * their appropriate dependencies (whether relative to the itemset item node,
158
163
  * referencing a form's `itext` translations, etc).
159
164
  */
160
- export const createSelectItems = (selectField: SelectField): Accessor<readonly SelectItem[]> => {
161
- const { items, itemset } = selectField.definition.bodyElement;
165
+ export const createItemCollection = (control: ItemCollectionControl): Accessor<readonly Item[]> => {
166
+ const { items, itemset } = control.definition.bodyElement;
162
167
 
163
- if (itemset == null) {
164
- return createTranslatedStaticSelectItems(selectField, items);
168
+ if (itemset != null) {
169
+ return createItemset(control, itemset);
165
170
  }
166
171
 
167
- return createItemset(selectField, itemset);
172
+ return createTranslatedStaticItems(control, items);
168
173
  };
@@ -1,3 +1,6 @@
1
+ import type { NamespaceDeclarationMap } from './names/NamespaceDeclarationMap.ts';
2
+ import type { QualifiedName } from './names/QualifiedName.ts';
3
+
1
4
  declare const ESCAPED_XML_TEXT_BRAND: unique symbol;
2
5
 
3
6
  export type EscapedXMLText = string & { readonly [ESCAPED_XML_TEXT_BRAND]: true };
@@ -75,22 +78,86 @@ export const escapeXMLText = <Text extends string>(
75
78
  : (out as EscapedXMLText);
76
79
  };
77
80
 
78
- const serializeElementXML = (nodeName: string, children: string): string => {
81
+ interface SerializableElementAttribute {
82
+ serializeAttributeXML(): string;
83
+ }
84
+
85
+ interface ElementXMLSerializationOptions {
86
+ readonly namespaceDeclarations?: NamespaceDeclarationMap;
87
+ readonly attributes?: readonly SerializableElementAttribute[];
88
+ }
89
+
90
+ const serializeElementNamespaceDeclarationXML = (
91
+ namespaceDeclarations?: NamespaceDeclarationMap
92
+ ): string => {
93
+ if (namespaceDeclarations == null) {
94
+ return '';
95
+ }
96
+
97
+ return Array.from(namespaceDeclarations.values())
98
+ .map((namespaceDeclaration) => {
99
+ return namespaceDeclaration.serializeNamespaceDeclarationXML({
100
+ omitDefaultNamespace: true,
101
+ });
102
+ })
103
+ .join('');
104
+ };
105
+
106
+ const serializeElementAttributeXML = (
107
+ attributes?: readonly SerializableElementAttribute[]
108
+ ): string => {
109
+ if (attributes == null) {
110
+ return '';
111
+ }
112
+
113
+ return attributes
114
+ .map((attribute) => {
115
+ return attribute.serializeAttributeXML();
116
+ })
117
+ .join('');
118
+ };
119
+
120
+ const serializeElementXML = (
121
+ qualifiedName: QualifiedName,
122
+ children: string,
123
+ options: ElementXMLSerializationOptions = {}
124
+ ): string => {
125
+ // See JSDoc for the `getPrefixedName` method. If we find we do actually need
126
+ // custom element (subtree) prefix resolution, we'd uncomment the argument
127
+ // below. (Either way, at time of writing the affected tests pass where
128
+ // expected when the option is passed. It's omitted on the presumption that it
129
+ // would be redundant, since the nodes being serialized are already resolved
130
+ // with the same set of namespace declarations which would affect them.)
131
+ //
132
+ // prettier-ignore
133
+ const nodeName = qualifiedName.getPrefixedName(
134
+ // options.namespaceDeclarations
135
+ );
136
+ const namespaceDeclarations = serializeElementNamespaceDeclarationXML(
137
+ options.namespaceDeclarations
138
+ );
139
+ const attributes = serializeElementAttributeXML(options.attributes);
140
+ const prefix = `<${nodeName}${namespaceDeclarations}${attributes}`;
141
+
79
142
  if (children === '') {
80
- return `<${nodeName}/>`;
143
+ return `${prefix}/>`;
81
144
  }
82
145
 
83
- // TODO: attributes
84
- return `<${nodeName}>${children}</${nodeName}>`;
146
+ return `${prefix}>${children}</${nodeName}>`;
85
147
  };
86
148
 
87
149
  export const serializeParentElementXML = (
88
- nodeName: string,
89
- serializedChildren: readonly string[]
150
+ qualifiedName: QualifiedName,
151
+ serializedChildren: readonly string[],
152
+ options?: ElementXMLSerializationOptions
90
153
  ): string => {
91
- return serializeElementXML(nodeName, serializedChildren.join(''));
154
+ return serializeElementXML(qualifiedName, serializedChildren.join(''), options);
92
155
  };
93
156
 
94
- export const serializeLeafElementXML = (nodeName: string, xmlValue: EscapedXMLText): string => {
95
- return serializeElementXML(nodeName, xmlValue.normalize());
157
+ export const serializeLeafElementXML = (
158
+ qualifiedName: QualifiedName,
159
+ xmlValue: EscapedXMLText,
160
+ options?: ElementXMLSerializationOptions
161
+ ): string => {
162
+ return serializeElementXML(qualifiedName, xmlValue.normalize(), options);
96
163
  };
@@ -1,4 +1,9 @@
1
- import { XFORMS_NAMESPACE_URI, XMLNS_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
1
+ import {
2
+ JAVAROSA_NAMESPACE_URI,
3
+ OPENROSA_XFORMS_NAMESPACE_URI,
4
+ XFORMS_NAMESPACE_URI,
5
+ XMLNS_NAMESPACE_URI,
6
+ } from '@getodk/common/constants/xmlns.ts';
2
7
  import type {
3
8
  KnownAttributeLocalNamedElement,
4
9
  LocalNamedElement,
@@ -7,6 +12,118 @@ import { DefaultEvaluator } from '@getodk/xpath';
7
12
 
8
13
  interface DOMBindElement extends KnownAttributeLocalNamedElement<'bind', 'nodeset'> {}
9
14
 
15
+ const getMetaElement = (primaryInstanceRoot: Element): Element | null => {
16
+ for (const child of primaryInstanceRoot.children) {
17
+ const { localName, namespaceURI } = child;
18
+
19
+ if (
20
+ (namespaceURI === OPENROSA_XFORMS_NAMESPACE_URI || namespaceURI === XFORMS_NAMESPACE_URI) &&
21
+ localName === 'meta'
22
+ ) {
23
+ return child;
24
+ }
25
+ }
26
+
27
+ return null;
28
+ };
29
+
30
+ const getMetaChildElement = (meta: Element | null, localName: string): Element | null => {
31
+ if (meta == null) {
32
+ return null;
33
+ }
34
+
35
+ const { namespaceURI } = meta;
36
+
37
+ for (const child of meta.children) {
38
+ if (child.localName === localName && child.namespaceURI === namespaceURI) {
39
+ return child;
40
+ }
41
+ }
42
+
43
+ return null;
44
+ };
45
+
46
+ const getQualifiedName = (
47
+ contextNode: Node,
48
+ namespaceURI: string | null,
49
+ localName: string
50
+ ): string => {
51
+ const prefix = contextNode.lookupPrefix(namespaceURI);
52
+
53
+ if (prefix == null) {
54
+ return localName;
55
+ }
56
+
57
+ return `${prefix}:${localName}`;
58
+ };
59
+
60
+ const createNamespacedChildElement = (
61
+ parent: Element,
62
+ namespaceURI: string | null,
63
+ localName: string
64
+ ): Element => {
65
+ const qualifiedName = getQualifiedName(parent, namespaceURI, localName);
66
+ const child = parent.ownerDocument.createElementNS(namespaceURI, qualifiedName);
67
+
68
+ parent.append(child);
69
+
70
+ return child;
71
+ };
72
+
73
+ const setNamespacedAttributeValue = (
74
+ element: Element,
75
+ namespaceURI: string | null,
76
+ localName: string,
77
+ value: string
78
+ ) => {
79
+ const qualifiedName = getQualifiedName(element, namespaceURI, localName);
80
+
81
+ element.setAttributeNS(namespaceURI, qualifiedName, value);
82
+ };
83
+
84
+ const createDefaultInstanceIDBinding = (
85
+ model: Element,
86
+ primaryInstanceRoot: Element,
87
+ meta: Element,
88
+ instanceID: Element
89
+ ): DOMBindElement => {
90
+ const bind = createNamespacedChildElement(model, model.namespaceURI, 'bind');
91
+ const nodeset = `/${primaryInstanceRoot.nodeName}/${meta.nodeName}/${instanceID.nodeName}`;
92
+
93
+ bind.setAttribute('nodeset', nodeset);
94
+ setNamespacedAttributeValue(bind, JAVAROSA_NAMESPACE_URI, 'preload', 'uid');
95
+
96
+ return bind as DOMBindElement;
97
+ };
98
+
99
+ const normalizeDefaultMetaBindings = (
100
+ model: Element,
101
+ primaryInstanceRoot: Element,
102
+ binds: readonly DOMBindElement[]
103
+ ): readonly DOMBindElement[] => {
104
+ let meta = getMetaElement(primaryInstanceRoot);
105
+ let instanceID = getMetaChildElement(meta, 'instanceID');
106
+
107
+ if (meta == null) {
108
+ meta = createNamespacedChildElement(primaryInstanceRoot, OPENROSA_XFORMS_NAMESPACE_URI, 'meta');
109
+ }
110
+
111
+ if (instanceID == null) {
112
+ instanceID = createNamespacedChildElement(meta, meta.namespaceURI, 'instanceID');
113
+
114
+ const instanceIDBinding = createDefaultInstanceIDBinding(
115
+ model,
116
+ primaryInstanceRoot,
117
+ meta,
118
+ instanceID
119
+ );
120
+
121
+ return [...binds, instanceIDBinding];
122
+ }
123
+
124
+ return binds;
125
+ };
126
+
10
127
  export interface DOMItextTranslationElement
11
128
  extends KnownAttributeLocalNamedElement<'translation', 'lang'> {}
12
129
 
@@ -238,23 +355,6 @@ export class XFormDOM {
238
355
  const html = evaluator.evaluateNonNullElement('/h:html', {
239
356
  contextNode: xformDocument,
240
357
  });
241
-
242
- let body = evaluator.evaluateNonNullElement('./h:body', {
243
- contextNode: html,
244
- });
245
- let normalizedXML: string;
246
-
247
- if (options.isNormalized) {
248
- normalizedXML = sourceXML;
249
- } else {
250
- body = normalizeXFormBody(body);
251
-
252
- // TODO: if we keep doing normalization this way long term (or using the DOM
253
- // for parsing long term, for that matter!), we should measure this. And we
254
- // should measure against XMLSerializer while we're at it!
255
- normalizedXML = xformDocument.documentElement.outerHTML;
256
- }
257
-
258
358
  const head = evaluator.evaluateNonNullElement('./h:head', {
259
359
  contextNode: html,
260
360
  });
@@ -264,9 +364,12 @@ export class XFormDOM {
264
364
  const model = evaluator.evaluateNonNullElement('./xf:model', {
265
365
  contextNode: head,
266
366
  });
267
- const binds = evaluator.evaluateNodes<DOMBindElement>('./xf:bind[@nodeset]', {
268
- contextNode: model,
269
- });
367
+ let binds: readonly DOMBindElement[] = evaluator.evaluateNodes<DOMBindElement>(
368
+ './xf:bind[@nodeset]',
369
+ {
370
+ contextNode: model,
371
+ }
372
+ );
270
373
 
271
374
  const instances = evaluator.evaluateNodes<DOMInstanceElement>('./xf:instance', {
272
375
  contextNode: model,
@@ -292,6 +395,23 @@ export class XFormDOM {
292
395
  }
293
396
  );
294
397
 
398
+ let body = evaluator.evaluateNonNullElement('./h:body', {
399
+ contextNode: html,
400
+ });
401
+ let normalizedXML: string;
402
+
403
+ if (options.isNormalized) {
404
+ normalizedXML = sourceXML;
405
+ } else {
406
+ body = normalizeXFormBody(body);
407
+ binds = normalizeDefaultMetaBindings(model, primaryInstanceRoot, binds);
408
+
409
+ // TODO: if we keep doing normalization this way long term (or using the DOM
410
+ // for parsing long term, for that matter!), we should measure this. And we
411
+ // should measure against XMLSerializer while we're at it!
412
+ normalizedXML = xformDocument.documentElement.outerHTML;
413
+ }
414
+
295
415
  this.normalizedXML = normalizedXML;
296
416
  this.xformDocument = xformDocument;
297
417
  this.html = html;
@@ -24,10 +24,7 @@ export class XFormDefinition {
24
24
  this.xformDocument = xformDocument;
25
25
  this.id = id;
26
26
  this.title = title.textContent ?? '';
27
-
28
- // TODO: highly unlikely primary instance root will need a namespace prefix
29
- // but noting it just in case there is such weird usage...
30
- this.rootReference = `/${primaryInstanceRoot.localName}`;
27
+ this.rootReference = `/${primaryInstanceRoot.nodeName}`;
31
28
 
32
29
  this.body = new BodyDefinition(this);
33
30
  this.model = new ModelDefinition(this);
@@ -6,8 +6,8 @@ import { ControlDefinition } from './control/ControlDefinition.ts';
6
6
  import { InputControlDefinition } from './control/InputControlDefinition.ts';
7
7
  import { RangeControlDefinition } from './control/RangeControlDefinition.ts';
8
8
  import { RankControlDefinition } from './control/RankControlDefinition.ts';
9
- import type { AnySelectDefinition } from './control/select/SelectDefinition.ts';
10
- import { SelectDefinition } from './control/select/SelectDefinition.ts';
9
+ import type { AnySelectControlDefinition } from './control/SelectControlDefinition.ts';
10
+ import { SelectControlDefinition } from './control/SelectControlDefinition.ts';
11
11
  import { TriggerControlDefinition } from './control/TriggerControlDefinition.ts';
12
12
  import { UploadControlDefinition } from './control/UploadControlDefinition.ts';
13
13
  import { LogicalGroupDefinition } from './group/LogicalGroupDefinition.ts';
@@ -24,7 +24,7 @@ export interface BodyElementParentContext {
24
24
 
25
25
  // prettier-ignore
26
26
  export type ControlElementDefinition =
27
- | AnySelectDefinition
27
+ | AnySelectControlDefinition
28
28
  | InputControlDefinition
29
29
  | RangeControlDefinition
30
30
  | RankControlDefinition
@@ -48,7 +48,7 @@ const BodyElementDefinitionConstructors = [
48
48
  PresentationGroupDefinition,
49
49
  StructuralGroupDefinition,
50
50
  InputControlDefinition,
51
- SelectDefinition,
51
+ SelectControlDefinition,
52
52
  RangeControlDefinition,
53
53
  RankControlDefinition,
54
54
  TriggerControlDefinition,
@@ -0,0 +1,11 @@
1
+ import type { ParsedTokenList } from '../../../lib/TokenListParser.ts';
2
+ import { TokenListParser } from '../../../lib/TokenListParser.ts';
3
+
4
+ export const rangeAppearanceParser = new TokenListParser([
5
+ 'no-ticks',
6
+ 'picker',
7
+ 'rating',
8
+ 'vertical',
9
+ ]);
10
+
11
+ export type RangeAppearanceDefinition = ParsedTokenList<typeof rangeAppearanceParser>;
@@ -2,6 +2,7 @@ import type { XFormDefinition } from '../../XFormDefinition.ts';
2
2
  import type { InputAppearanceDefinition } from '../appearance/inputAppearanceParser.ts';
3
3
  import { inputAppearanceParser } from '../appearance/inputAppearanceParser.ts';
4
4
  import type { BodyElementParentContext } from '../BodyDefinition.ts';
5
+ import { parseToFloat, parseToInteger } from '../../../lib/number-parsers.ts';
5
6
  import { ControlDefinition } from './ControlDefinition.ts';
6
7
 
7
8
  export class InputControlDefinition extends ControlDefinition<'input'> {
@@ -11,10 +12,18 @@ export class InputControlDefinition extends ControlDefinition<'input'> {
11
12
 
12
13
  readonly type = 'input';
13
14
  readonly appearances: InputAppearanceDefinition;
15
+ readonly rows: number | null;
16
+ readonly accuracyThreshold: number | null;
17
+ readonly unacceptableAccuracyThreshold: number | null;
14
18
 
15
19
  constructor(form: XFormDefinition, parent: BodyElementParentContext, element: Element) {
16
20
  super(form, parent, element);
17
21
 
18
22
  this.appearances = inputAppearanceParser.parseFrom(element, 'appearance');
23
+ this.rows = parseToInteger(element.getAttribute('rows'));
24
+ this.accuracyThreshold = parseToFloat(element.getAttribute('accuracyThreshold'));
25
+ this.unacceptableAccuracyThreshold = parseToFloat(
26
+ element.getAttribute('unacceptableAccuracyThreshold')
27
+ );
19
28
  }
20
29
  }
@@ -1,8 +1,10 @@
1
- import { getValueElement, type ItemElement } from '../../../../lib/dom/query.ts';
2
- import { ItemLabelDefinition } from '../../../text/ItemLabelDefinition.ts';
3
- import type { XFormDefinition } from '../../../XFormDefinition.ts';
4
- import { BodyElementDefinition } from '../../BodyElementDefinition.ts';
5
- import type { AnySelectDefinition } from './SelectDefinition.ts';
1
+ import type { ItemElement } from '../../../lib/dom/query.ts';
2
+ import { getValueElement } from '../../../lib/dom/query.ts';
3
+ import { ItemLabelDefinition } from '../../text/ItemLabelDefinition.ts';
4
+ import type { XFormDefinition } from '../../XFormDefinition.ts';
5
+ import { BodyElementDefinition } from '../BodyElementDefinition.ts';
6
+ import type { AnySelectControlDefinition } from './SelectControlDefinition.ts';
7
+ import { RankControlDefinition } from './RankControlDefinition.ts';
6
8
 
7
9
  export class ItemDefinition extends BodyElementDefinition<'item'> {
8
10
  override readonly category = 'support';
@@ -13,7 +15,7 @@ export class ItemDefinition extends BodyElementDefinition<'item'> {
13
15
 
14
16
  constructor(
15
17
  form: XFormDefinition,
16
- override readonly parent: AnySelectDefinition,
18
+ override readonly parent: AnySelectControlDefinition | RankControlDefinition,
17
19
  element: ItemElement
18
20
  ) {
19
21
  const valueElement = getValueElement(element);
@@ -1,11 +1,13 @@
1
- import { getValueElement, type ItemsetElement } from '../../../../lib/dom/query.ts';
2
- import { ItemsetNodesetExpression } from '../../../expression/ItemsetNodesetExpression.ts';
3
- import { ItemsetValueExpression } from '../../../expression/ItemsetValueExpression.ts';
4
- import { ItemsetLabelDefinition } from '../../../text/ItemsetLabelDefinition.ts';
5
- import type { XFormDefinition } from '../../../XFormDefinition.ts';
6
- import { parseNodesetReference } from '../../../xpath/reference-parsing.ts';
7
- import { BodyElementDefinition } from '../../BodyElementDefinition.ts';
8
- import type { AnySelectDefinition } from './SelectDefinition.ts';
1
+ import type { ItemsetElement } from '../../../lib/dom/query.ts';
2
+ import { getValueElement } from '../../../lib/dom/query.ts';
3
+ import { ItemsetNodesetExpression } from '../../expression/ItemsetNodesetExpression.ts';
4
+ import { ItemsetValueExpression } from '../../expression/ItemsetValueExpression.ts';
5
+ import { ItemsetLabelDefinition } from '../../text/ItemsetLabelDefinition.ts';
6
+ import type { XFormDefinition } from '../../XFormDefinition.ts';
7
+ import { parseNodesetReference } from '../../xpath/reference-parsing.ts';
8
+ import { BodyElementDefinition } from '../BodyElementDefinition.ts';
9
+ import type { AnySelectControlDefinition } from './SelectControlDefinition.ts';
10
+ import { RankControlDefinition } from './RankControlDefinition.ts';
9
11
 
10
12
  export class ItemsetDefinition extends BodyElementDefinition<'itemset'> {
11
13
  override readonly category = 'support';
@@ -19,7 +21,7 @@ export class ItemsetDefinition extends BodyElementDefinition<'itemset'> {
19
21
 
20
22
  constructor(
21
23
  form: XFormDefinition,
22
- override readonly parent: AnySelectDefinition,
24
+ override readonly parent: AnySelectControlDefinition | RankControlDefinition,
23
25
  element: ItemsetElement
24
26
  ) {
25
27
  super(form, parent, element);