@getodk/xforms-engine 0.9.0 → 0.11.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 (40) hide show
  1. package/dist/client/SelectNode.d.ts +1 -0
  2. package/dist/client/TextRange.d.ts +4 -0
  3. package/dist/index.js +133 -160
  4. package/dist/index.js.map +1 -1
  5. package/dist/instance/SelectControl.d.ts +1 -0
  6. package/dist/instance/text/TextRange.d.ts +11 -1
  7. package/dist/lib/reactivity/text/createTextRange.d.ts +1 -2
  8. package/dist/parse/expression/TextChunkExpression.d.ts +16 -0
  9. package/dist/parse/shared/parseInstanceXML.d.ts +1 -1
  10. package/dist/parse/text/ItemsetLabelDefinition.d.ts +2 -4
  11. package/dist/parse/text/MessageDefinition.d.ts +3 -7
  12. package/dist/parse/text/abstract/TextElementDefinition.d.ts +2 -8
  13. package/dist/parse/text/abstract/TextRangeDefinition.d.ts +2 -2
  14. package/dist/solid.js +133 -160
  15. package/dist/solid.js.map +1 -1
  16. package/package.json +2 -2
  17. package/src/client/SelectNode.ts +2 -0
  18. package/src/client/TextRange.ts +5 -1
  19. package/src/error/LoadFormFailureError.ts +9 -35
  20. package/src/instance/SelectControl.ts +6 -0
  21. package/src/instance/text/TextRange.ts +21 -1
  22. package/src/lib/reactivity/text/createTextRange.ts +56 -43
  23. package/src/lib/reactivity/validation/createValidation.ts +1 -3
  24. package/src/parse/expression/TextChunkExpression.ts +78 -0
  25. package/src/parse/shared/parseInstanceXML.ts +4 -2
  26. package/src/parse/text/ItemsetLabelDefinition.ts +8 -12
  27. package/src/parse/text/MessageDefinition.ts +9 -16
  28. package/src/parse/text/abstract/TextElementDefinition.ts +10 -26
  29. package/src/parse/text/abstract/TextRangeDefinition.ts +2 -2
  30. package/src/parse/xpath/semantic-analysis.ts +7 -2
  31. package/dist/parse/expression/TextLiteralExpression.d.ts +0 -9
  32. package/dist/parse/expression/TextOutputExpression.d.ts +0 -7
  33. package/dist/parse/expression/TextReferenceExpression.d.ts +0 -7
  34. package/dist/parse/expression/TextTranslationExpression.d.ts +0 -8
  35. package/dist/parse/expression/abstract/TextChunkExpression.d.ts +0 -17
  36. package/src/parse/expression/TextLiteralExpression.ts +0 -19
  37. package/src/parse/expression/TextOutputExpression.ts +0 -25
  38. package/src/parse/expression/TextReferenceExpression.ts +0 -14
  39. package/src/parse/expression/TextTranslationExpression.ts +0 -38
  40. package/src/parse/expression/abstract/TextChunkExpression.ts +0 -38
@@ -22,6 +22,7 @@ interface SelectControlStateSpec extends ValueNodeStateSpec<readonly string[]> {
22
22
  readonly label: Accessor<TextRange<'label'> | null>;
23
23
  readonly hint: Accessor<TextRange<'hint'> | null>;
24
24
  readonly valueOptions: Accessor<SelectValueOptions>;
25
+ readonly isSelectWithImages: Accessor<boolean>;
25
26
  }
26
27
  export declare class SelectControl extends ValueNode<'string', SelectDefinition<'string'>, readonly string[], readonly string[]> implements SelectNode, XFormsXPathElement, EvaluationContext, ValidationContext, ClientReactiveSerializableValueNode {
27
28
  static from(parent: GeneralParentNode, instanceNode: StaticLeafElement | null, definition: SelectDefinition): SelectControl;
@@ -1,10 +1,20 @@
1
+ import { JRResourceURL } from '../../../../common/src/jr-resources/JRResourceURL.ts';
1
2
  import { TextRange as ClientTextRange, TextChunk, TextOrigin, TextRole } from '../../client/TextRange.ts';
3
+ export interface MediaSources {
4
+ image?: JRResourceURL;
5
+ video?: JRResourceURL;
6
+ audio?: JRResourceURL;
7
+ }
2
8
  export declare class TextRange<Role extends TextRole, Origin extends TextOrigin> implements ClientTextRange<Role, Origin> {
3
9
  readonly origin: Origin;
4
10
  readonly role: Role;
5
11
  protected readonly chunks: readonly TextChunk[];
12
+ protected readonly mediaSources?: MediaSources | undefined;
6
13
  [Symbol.iterator](): Generator<TextChunk, void, unknown>;
7
14
  get formatted(): Record<PropertyKey, unknown>;
8
15
  get asString(): string;
9
- constructor(origin: Origin, role: Role, chunks: readonly TextChunk[]);
16
+ get imageSource(): JRResourceURL | undefined;
17
+ get audioSource(): JRResourceURL | undefined;
18
+ get videoSource(): JRResourceURL | undefined;
19
+ constructor(origin: Origin, role: Role, chunks: readonly TextChunk[], mediaSources?: MediaSources | undefined);
10
20
  }
@@ -5,8 +5,7 @@ import { TextRange } from '../../../instance/text/TextRange.ts';
5
5
  import { TextRangeDefinition } from '../../../parse/text/abstract/TextRangeDefinition.ts';
6
6
  type ComputedFormTextRange<Role extends TextRole> = Accessor<TextRange<Role, 'form'>>;
7
7
  /**
8
- * Creates a text range (e.g. label or hint) from the provided definition,
9
- * reactive to:
8
+ * Creates a text range (e.g. label or hint) from the provided definition, reactive to:
10
9
  *
11
10
  * - The form's current language (e.g. `<label ref="jr:itext('text-id')" />`)
12
11
  * - Direct `<output>` references within the label's children
@@ -0,0 +1,16 @@
1
+ import { TextChunkSource } from '../../client/TextRange.ts';
2
+ import { AnyTextRangeDefinition } from '../text/abstract/TextRangeDefinition.ts';
3
+ import { DependentExpression } from './abstract/DependentExpression.ts';
4
+ interface TextChunkExpressionOptions {
5
+ readonly isTranslated?: true;
6
+ }
7
+ export declare class TextChunkExpression<T extends 'nodes' | 'string'> extends DependentExpression<T> {
8
+ readonly source: TextChunkSource;
9
+ readonly stringValue: string;
10
+ constructor(context: AnyTextRangeDefinition, resultType: T, expression: string, source: TextChunkSource, options?: TextChunkExpressionOptions, literalValue?: string);
11
+ static fromLiteral(context: AnyTextRangeDefinition, stringValue: string): TextChunkExpression<'string'>;
12
+ static fromReference(context: AnyTextRangeDefinition, ref: string): TextChunkExpression<'string'>;
13
+ static fromOutput(context: AnyTextRangeDefinition, element: Element): TextChunkExpression<'string'> | null;
14
+ static fromTranslation(context: AnyTextRangeDefinition, maybeExpression: string): TextChunkExpression<'nodes'> | null;
15
+ }
16
+ export {};
@@ -15,7 +15,7 @@ import { ModelDefinition } from '../model/ModelDefinition.ts';
15
15
  * @todo Aside from this being a hack, it's not very robust because it makes
16
16
  * assumptions which are _likely but definitely not guaranteed_!
17
17
  *
18
- * - Instance XML (probably) doeesn't declare a default namespace
18
+ * - Instance XML (probably) doesn't declare a default namespace
19
19
  * - Instance XML **definitely** declares non-default namespaces
20
20
  */
21
21
  export declare const parseInstanceXML: (model: ModelDefinition, instanceXML: string) => StaticDocument;
@@ -1,12 +1,10 @@
1
1
  import { XFormDefinition } from '../../parse/XFormDefinition.ts';
2
- import { ItemDefinition } from '../body/control/ItemDefinition.ts';
3
2
  import { ItemsetDefinition } from '../body/control/ItemsetDefinition.ts';
4
- import { RefAttributeChunk } from './abstract/TextElementDefinition.ts';
3
+ import { TextChunkExpression } from '../expression/TextChunkExpression.ts';
5
4
  import { TextRangeDefinition } from './abstract/TextRangeDefinition.ts';
6
- export type ItemLabelOwner = ItemDefinition | ItemsetDefinition;
7
5
  export declare class ItemsetLabelDefinition extends TextRangeDefinition<'item-label'> {
8
6
  static from(form: XFormDefinition, owner: ItemsetDefinition): ItemsetLabelDefinition | null;
9
7
  readonly role = "item-label";
10
- readonly chunks: readonly [RefAttributeChunk];
8
+ readonly chunks: ReadonlyArray<TextChunkExpression<'nodes' | 'string'>>;
11
9
  private constructor();
12
10
  }
@@ -1,14 +1,10 @@
1
- import { TextLiteralExpression } from '../expression/TextLiteralExpression.ts';
2
- import { TextTranslationExpression } from '../expression/TextTranslationExpression.ts';
1
+ import { TextChunkExpression } from '../expression/TextChunkExpression.ts';
3
2
  import { BindDefinition } from '../model/BindDefinition.ts';
4
- import { TextBindAttributeLocalName, TextSourceNode, TextRangeDefinition } from './abstract/TextRangeDefinition.ts';
5
- export type MessageSourceNode = TextSourceNode<TextBindAttributeLocalName>;
6
- type MessageChunk = TextLiteralExpression | TextTranslationExpression;
3
+ import { TextBindAttributeLocalName, TextRangeDefinition } from './abstract/TextRangeDefinition.ts';
7
4
  export declare class MessageDefinition<Type extends TextBindAttributeLocalName> extends TextRangeDefinition<Type> {
8
5
  readonly role: Type;
9
6
  static from<Type extends TextBindAttributeLocalName>(bind: BindDefinition, type: Type): MessageDefinition<Type> | null;
10
- readonly chunks: readonly [MessageChunk];
7
+ readonly chunks: ReadonlyArray<TextChunkExpression<'nodes' | 'string'>>;
11
8
  private constructor();
12
9
  }
13
10
  export type AnyMessageDefinition = MessageDefinition<TextBindAttributeLocalName>;
14
- export {};
@@ -1,21 +1,15 @@
1
1
  import { ElementTextRole } from '../../../client/TextRange.ts';
2
2
  import { XFormDefinition } from '../../../parse/XFormDefinition.ts';
3
3
  import { ItemDefinition } from '../../body/control/ItemDefinition.ts';
4
- import { TextLiteralExpression } from '../../expression/TextLiteralExpression.ts';
5
- import { TextOutputExpression } from '../../expression/TextOutputExpression.ts';
6
- import { TextReferenceExpression } from '../../expression/TextReferenceExpression.ts';
7
- import { TextTranslationExpression } from '../../expression/TextTranslationExpression.ts';
4
+ import { TextChunkExpression } from '../../expression/TextChunkExpression.ts';
8
5
  import { HintDefinition } from '../HintDefinition.ts';
9
6
  import { ItemLabelDefinition } from '../ItemLabelDefinition.ts';
10
7
  import { ItemsetLabelDefinition } from '../ItemsetLabelDefinition.ts';
11
8
  import { LabelDefinition, LabelOwner } from '../LabelDefinition.ts';
12
9
  import { TextSourceNode, TextRangeDefinition } from './TextRangeDefinition.ts';
13
- export type RefAttributeChunk = TextReferenceExpression | TextTranslationExpression;
14
- type TextElementChildChunk = TextLiteralExpression | TextOutputExpression;
15
- type TextElementChunks = readonly [RefAttributeChunk] | readonly TextElementChildChunk[];
16
10
  type TextElementOwner = ItemDefinition | LabelOwner;
17
11
  export declare abstract class TextElementDefinition<Role extends ElementTextRole> extends TextRangeDefinition<Role> {
18
- readonly chunks: TextElementChunks;
12
+ readonly chunks: ReadonlyArray<TextChunkExpression<'nodes' | 'string'>>;
19
13
  constructor(form: XFormDefinition, owner: TextElementOwner, sourceNode: TextSourceNode<Role>);
20
14
  }
21
15
  export type AnyTextElementDefinition = HintDefinition | ItemLabelDefinition | ItemsetLabelDefinition | LabelDefinition;
@@ -2,7 +2,7 @@ import { LocalNamedElement } from '../../../../../common/types/dom.ts';
2
2
  import { TextRole } from '../../../client/TextRange.ts';
3
3
  import { XFormDefinition } from '../../../parse/XFormDefinition.ts';
4
4
  import { DependencyContext } from '../../expression/abstract/DependencyContext.ts';
5
- import { AnyTextChunkExpression } from '../../expression/abstract/TextChunkExpression.ts';
5
+ import { TextChunkExpression } from '../../expression/TextChunkExpression.ts';
6
6
  import { AnyMessageDefinition } from '../MessageDefinition.ts';
7
7
  import { AnyTextElementDefinition } from './TextElementDefinition.ts';
8
8
  export type TextBindAttributeLocalName = 'constraintMsg' | 'requiredMsg';
@@ -22,7 +22,7 @@ export declare abstract class TextRangeDefinition<Role extends TextRole> extends
22
22
  abstract readonly role: Role;
23
23
  readonly parentReference: string | null;
24
24
  readonly reference: string | null;
25
- abstract readonly chunks: readonly AnyTextChunkExpression[];
25
+ abstract readonly chunks: ReadonlyArray<TextChunkExpression<'nodes' | 'string'>>;
26
26
  get isTranslated(): boolean;
27
27
  set isTranslated(value: true);
28
28
  protected constructor(form: XFormDefinition, ownerContext: DependencyContext, sourceNode: TextSourceNode<Role>);
package/dist/solid.js CHANGED
@@ -67,37 +67,17 @@ const formResourceMetadata = (resource) => {
67
67
  rawData: null
68
68
  };
69
69
  }
70
- return {
71
- description: "Raw string data",
72
- rawData: resource
73
- };
70
+ return;
74
71
  };
75
72
  class LoadFormFailureError extends AggregateError {
76
73
  constructor(resource, errors) {
77
- const { description, rawData } = formResourceMetadata(resource);
78
- const messageLines = [
79
- `Failed to load form resource: ${description}`,
80
- "\n",
81
- ...errors.map((error) => {
82
- const aggregatedMessage = error.message;
83
- if (aggregatedMessage == null) {
84
- return "- Unknown error";
85
- }
86
- return `- ${aggregatedMessage}`;
87
- })
88
- ];
89
- if (rawData != null) {
90
- messageLines.push("\n- - -\n", "Raw resource data:", rawData);
91
- }
92
- const message = messageLines.join("\n");
74
+ const metadata = formResourceMetadata(resource);
75
+ const errorMessages = errors.map((error) => error.message || "Unknown error").join("\n");
76
+ const message = metadata?.description ? `Form source: ${metadata.description}
77
+ ${errorMessages}` : errorMessages;
93
78
  super(errors, message);
94
79
  const [head, ...tail] = errors;
95
- if (head != null && tail.length === 0) {
96
- const { stack } = head;
97
- if (typeof stack === "string") {
98
- this.stack = stack;
99
- }
100
- }
80
+ this.stack = typeof head?.stack === "string" && !tail.length ? head.stack : "No error trace available.";
101
81
  }
102
82
  }
103
83
 
@@ -9402,7 +9382,7 @@ const substring = new StringFunction(
9402
9382
  return string2.substring(start, end);
9403
9383
  }
9404
9384
  );
9405
- const string$2 = new StringFunction(
9385
+ const string$1 = new StringFunction(
9406
9386
  "string",
9407
9387
  [{ arityType: "optional" }],
9408
9388
  (context, [expression]) => (expression?.evaluate(context) ?? context).toString()
@@ -9435,13 +9415,13 @@ const translate = new StringFunction(
9435
9415
  }
9436
9416
  );
9437
9417
 
9438
- const string$3 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
9418
+ const string$2 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
9439
9419
  __proto__: null,
9440
9420
  concat: concat$1,
9441
9421
  contains,
9442
9422
  normalizeSpace,
9443
9423
  startsWith,
9444
- string: string$2,
9424
+ string: string$1,
9445
9425
  stringLength,
9446
9426
  substring,
9447
9427
  substringAfter,
@@ -9453,7 +9433,7 @@ const fn$2 = new FunctionLibrary(FN_NAMESPACE_URI, [
9453
9433
  ...Object.values(boolean$2),
9454
9434
  ...Object.values(nodeset$1),
9455
9435
  ...Object.values(number$3),
9456
- ...Object.values(string$3)
9436
+ ...Object.values(string$2)
9457
9437
  ]);
9458
9438
 
9459
9439
  class BaseStep {
@@ -11233,21 +11213,21 @@ const enk = new FunctionLibrary(ENKETO_NAMESPACE_URI, [
11233
11213
  new FunctionAlias("format-date", formatDateTime)
11234
11214
  ]);
11235
11215
 
11236
- const itext = new StringFunction(
11216
+ const itext = new NodeSetFunction(
11237
11217
  "itext",
11238
11218
  [{ arityType: "required", typeHint: "string" }],
11239
11219
  (context, [itextIDExpression]) => {
11240
11220
  const itextID = itextIDExpression.evaluate(context).toString();
11241
- return XFormsXPathEvaluator.getDefaultTranslationText(context, itextID);
11221
+ return XFormsXPathEvaluator.getTranslationValues(context, itextID) ?? [];
11242
11222
  }
11243
11223
  );
11244
11224
 
11245
- const string$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
11225
+ const nodeSet = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
11246
11226
  __proto__: null,
11247
11227
  itext
11248
11228
  }, Symbol.toStringTag, { value: 'Module' }));
11249
11229
 
11250
- const jr$2 = new FunctionLibrary(JAVAROSA_NAMESPACE_URI, [...Object.values(string$1)]);
11230
+ const jr$2 = new FunctionLibrary(JAVAROSA_NAMESPACE_URI, [...Object.values(nodeSet)]);
11251
11231
 
11252
11232
  const booleanFromString = new BooleanFunction(
11253
11233
  "boolean-from-string",
@@ -18950,46 +18930,39 @@ class XFormsItextTranslations {
18950
18930
  return textMap.get(itextID) ?? null;
18951
18931
  }
18952
18932
  /**
18933
+ * Retrieves all translation value elements for a given Itext in the active language.
18934
+ *
18953
18935
  * @package
18954
18936
  *
18955
- * Here, "default" is meant as more precise language than "regular" as
18956
- * {@link https://getodk.github.io/xforms-spec/#languages | specified by ODK XForms}. In other words, this is equivalent to the following hypothetical XPath pseudo-code (whitespace added to improve structural clarity):
18937
+ * This method fetches the `text` element matching `itextID` and returns its child `value` elements,
18938
+ * which may have an optional `@form` attribute.
18939
+ *
18940
+ * The operation is conceptually similar to the following XPath query:
18957
18941
  *
18958
18942
  * ```xpath
18959
- * string(
18960
- * imaginary:itext-translation(
18961
- * xpath3-fn:environment-variable('activeLanguage')
18962
- * )
18963
- * /text[@id = $itextID]
18964
- * /value[not(@form)]
18943
+ * imaginary:itext-translation(
18944
+ * xpath3-fn:environment-variable('activeLanguage')
18965
18945
  * )
18946
+ * /text[@id = $itextID]
18947
+ * /value
18966
18948
  * ```
18967
18949
  *
18968
18950
  * Or alternately:
18969
18951
  *
18970
18952
  * ```xpath
18971
- * string(
18972
- * imaginary:itext-root()
18973
- * /translation[@lang = xpath3-fn:environment-variable('activeLanguage')]
18974
- * /text[@id = $itextID]
18975
- * /value[not(@form)]
18976
- * )
18953
+ * imaginary:itext-root()
18954
+ * /translation[@lang = xpath3-fn:environment-variable('activeLanguage')]
18955
+ * /text[@id = $itextID]
18956
+ * /value
18977
18957
  * ```
18978
- *
18979
- * @todo The above really feels like it adds some helpful clarity to how `jr:itext()` is designed to work, and the kinds of structures, state and input involved. Since there's already some discomfort around that API as specified, it's worth considering
18958
+ * Returns an array of `XFormsItextTranslationValueElement<T>`
18980
18959
  */
18981
- getDefaultTranslationText(itextID) {
18960
+ getTranslationValues(itextID) {
18982
18961
  const textElement = this.getTranslationTextElement(itextID);
18983
18962
  if (textElement == null) {
18984
- return "";
18985
- }
18986
- const { domProvider } = this;
18987
- for (const valueElement of domProvider.getChildrenByLocalName(textElement, "value")) {
18988
- if (!domProvider.hasLocalNamedAttribute(valueElement, "form")) {
18989
- return domProvider.getNodeValue(valueElement);
18990
- }
18963
+ return [];
18991
18964
  }
18992
- return "";
18965
+ return [...this.domProvider.getChildrenByLocalName(textElement, "value")];
18993
18966
  }
18994
18967
  getLanguages() {
18995
18968
  return this.languages;
@@ -19026,9 +18999,9 @@ class XFormsXPathEvaluator extends Evaluator {
19026
18999
  assertInternalXFormsXPathEvaluatorContext(context);
19027
19000
  return context.evaluator.secondaryInstancesById.get(id) ?? null;
19028
19001
  }
19029
- static getDefaultTranslationText(context, itextID) {
19002
+ static getTranslationValues(context, itextID) {
19030
19003
  assertInternalXFormsXPathEvaluatorContext(context);
19031
- return context.evaluator.itextTranslations.getDefaultTranslationText(itextID);
19004
+ return context.evaluator.itextTranslations.getTranslationValues(itextID);
19032
19005
  }
19033
19006
  rootNode;
19034
19007
  /**
@@ -19582,8 +19555,13 @@ const isTranslationFunctionCall = (syntaxNode) => {
19582
19555
  ]);
19583
19556
  };
19584
19557
  const isTranslationExpression = (expression) => {
19585
- const { rootNode } = expressionParser.parse(expression);
19586
- const functionCallNode = findTypedPrincipalExpressionNode(["function_call"], rootNode);
19558
+ let result;
19559
+ try {
19560
+ result = expressionParser.parse(expression);
19561
+ } catch {
19562
+ return false;
19563
+ }
19564
+ const functionCallNode = findTypedPrincipalExpressionNode(["function_call"], result.rootNode);
19587
19565
  if (functionCallNode == null) {
19588
19566
  return false;
19589
19567
  }
@@ -19747,77 +19725,43 @@ class DependentExpression {
19747
19725
  }
19748
19726
  }
19749
19727
 
19728
+ const isOutputElement = (element) => {
19729
+ return element.localName === "output" && element.hasAttribute("value");
19730
+ };
19750
19731
  class TextChunkExpression extends DependentExpression {
19732
+ source;
19733
+ // Set for the literal source, blank otherwise
19751
19734
  stringValue;
19752
- constructor(context, expression, options = {}) {
19753
- super(context, "string", expression, {
19735
+ constructor(context, resultType, expression, source, options = {}, literalValue = "") {
19736
+ super(context, resultType, expression, {
19754
19737
  semanticDependencies: {
19755
19738
  translations: options.isTranslated
19756
19739
  },
19757
19740
  ignoreContextReference: true
19758
19741
  });
19742
+ this.source = source;
19743
+ this.stringValue = literalValue;
19759
19744
  }
19760
- }
19761
-
19762
- class TextLiteralExpression extends TextChunkExpression {
19763
- constructor(context, stringValue) {
19764
- super(context, "null");
19765
- this.stringValue = stringValue;
19766
- }
19767
- static from(context, stringValue) {
19768
- return new this(context, stringValue);
19769
- }
19770
- source = "literal";
19771
- }
19772
-
19773
- const isOutputElement = (element) => {
19774
- return element.localName === "output" && element.hasAttribute("value");
19775
- };
19776
- class TextOutputExpression extends TextChunkExpression {
19777
- static from(context, element) {
19778
- if (isOutputElement(element)) {
19779
- return new this(context, element);
19780
- }
19781
- return null;
19782
- }
19783
- source = "output";
19784
- constructor(context, element) {
19785
- super(context, element.getAttribute("value"));
19745
+ static fromLiteral(context, stringValue) {
19746
+ return new TextChunkExpression(context, "string", "null", "literal", {}, stringValue);
19786
19747
  }
19787
- }
19788
-
19789
- class TextReferenceExpression extends TextChunkExpression {
19790
- static from(context, refExpression) {
19791
- return new this(context, refExpression);
19748
+ static fromReference(context, ref) {
19749
+ return new TextChunkExpression(context, "string", ref, "reference");
19792
19750
  }
19793
- source = "reference";
19794
- constructor(context, refExpression) {
19795
- super(context, refExpression);
19796
- }
19797
- }
19798
-
19799
- class TextTranslationExpression extends TextChunkExpression {
19800
- static fromMessage(context, maybeExpression) {
19801
- try {
19802
- expressionParser.parse(maybeExpression);
19803
- } catch {
19751
+ static fromOutput(context, element) {
19752
+ if (!isOutputElement(element)) {
19804
19753
  return null;
19805
19754
  }
19806
- if (isTranslationExpression(maybeExpression)) {
19807
- return new this(context, maybeExpression);
19808
- }
19809
- return null;
19755
+ return new TextChunkExpression(context, "string", element.getAttribute("value"), "output");
19810
19756
  }
19811
- static from(context, maybeExpression) {
19757
+ static fromTranslation(context, maybeExpression) {
19812
19758
  if (isTranslationExpression(maybeExpression)) {
19813
- return new this(context, maybeExpression);
19759
+ return new TextChunkExpression(context, "nodes", maybeExpression, "translation", {
19760
+ isTranslated: true
19761
+ });
19814
19762
  }
19815
19763
  return null;
19816
19764
  }
19817
- source = "translation";
19818
- constructor(context, expression) {
19819
- super(context, expression, { isTranslated: true });
19820
- }
19821
19765
  }
19822
19766
 
19823
19767
  const absolutePathNodeList = (pathNode) => {
@@ -20066,16 +20010,20 @@ class TextElementDefinition extends TextRangeDefinition {
20066
20010
  if (refExpression == null) {
20067
20011
  this.chunks = Array.from(sourceNode.childNodes).flatMap((childNode) => {
20068
20012
  if (isElementNode(childNode)) {
20069
- return TextOutputExpression.from(context, childNode) ?? [];
20013
+ return TextChunkExpression.fromOutput(context, childNode) ?? [];
20070
20014
  }
20071
20015
  if (isTextNode(childNode)) {
20072
- return TextLiteralExpression.from(context, childNode.data);
20016
+ return TextChunkExpression.fromLiteral(context, childNode.data);
20073
20017
  }
20074
20018
  return [];
20075
20019
  });
20076
20020
  } else {
20077
- const refChunk = TextTranslationExpression.from(context, refExpression) ?? TextReferenceExpression.from(context, refExpression);
20078
- this.chunks = [refChunk];
20021
+ const expression = TextChunkExpression.fromTranslation(context, refExpression);
20022
+ if (expression != null) {
20023
+ this.chunks = [expression];
20024
+ } else {
20025
+ this.chunks = [TextChunkExpression.fromReference(context, refExpression)];
20026
+ }
20079
20027
  }
20080
20028
  }
20081
20029
  }
@@ -20327,8 +20275,12 @@ class ItemsetLabelDefinition extends TextRangeDefinition {
20327
20275
  if (refExpression == null) {
20328
20276
  throw new Error("<itemset><label> missing ref attribute");
20329
20277
  }
20330
- const refChunk = TextTranslationExpression.from(this, refExpression) ?? TextReferenceExpression.from(this, refExpression);
20331
- this.chunks = [refChunk];
20278
+ const expression = TextChunkExpression.fromTranslation(this, refExpression);
20279
+ if (expression != null) {
20280
+ this.chunks = [expression];
20281
+ } else {
20282
+ this.chunks = [TextChunkExpression.fromReference(this, refExpression)];
20283
+ }
20332
20284
  }
20333
20285
  }
20334
20286
 
@@ -21236,8 +21188,12 @@ class MessageDefinition extends TextRangeDefinition {
21236
21188
  constructor(bind, role, message) {
21237
21189
  super(bind.form, bind, null);
21238
21190
  this.role = role;
21239
- const chunk = TextTranslationExpression.fromMessage(this, message) ?? TextLiteralExpression.from(this, message);
21240
- this.chunks = [chunk];
21191
+ const expression = TextChunkExpression.fromTranslation(this, message);
21192
+ if (expression != null) {
21193
+ this.chunks = [expression];
21194
+ } else {
21195
+ this.chunks = [TextChunkExpression.fromLiteral(this, message)];
21196
+ }
21241
21197
  }
21242
21198
  static from(bind, type) {
21243
21199
  const message = bind.bindElement.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, type);
@@ -27151,7 +27107,8 @@ const getWrappedInstanceRootElement = (xml) => {
27151
27107
  return root;
27152
27108
  };
27153
27109
  const parseInstanceXML = (model, instanceXML) => {
27154
- const wrappedXML = wrapInstanceXML(model, instanceXML);
27110
+ const cleanedXML = instanceXML.replace(/<\?xml\s+[^?]*\?>\s*/, "");
27111
+ const wrappedXML = wrapInstanceXML(model, cleanedXML);
27155
27112
  const root = getWrappedInstanceRootElement(wrappedXML);
27156
27113
  return parseStaticDocumentFromDOMSubtree(root);
27157
27114
  };
@@ -29122,10 +29079,11 @@ class TextChunk {
29122
29079
  }
29123
29080
 
29124
29081
  class TextRange {
29125
- constructor(origin, role, chunks) {
29082
+ constructor(origin, role, chunks, mediaSources) {
29126
29083
  this.origin = origin;
29127
29084
  this.role = role;
29128
29085
  this.chunks = chunks;
29086
+ this.mediaSources = mediaSources;
29129
29087
  }
29130
29088
  *[Symbol.iterator]() {
29131
29089
  yield* this.chunks;
@@ -29136,44 +29094,56 @@ class TextRange {
29136
29094
  get asString() {
29137
29095
  return this.chunks.map((chunk) => chunk.asString).join("");
29138
29096
  }
29097
+ get imageSource() {
29098
+ return this.mediaSources?.image;
29099
+ }
29100
+ get audioSource() {
29101
+ return this.mediaSources?.audio;
29102
+ }
29103
+ get videoSource() {
29104
+ return this.mediaSources?.video;
29105
+ }
29139
29106
  }
29140
29107
 
29141
- const createComputedTextChunk = (context, textSource) => {
29142
- const { source } = textSource;
29143
- if (source === "literal") {
29144
- const { stringValue } = textSource;
29145
- return {
29146
- source,
29147
- getText: () => stringValue
29148
- };
29149
- }
29150
- return context.scope.runTask(() => {
29151
- const getText = createComputedExpression(context, textSource, {
29152
- defaultValue: ""
29153
- });
29154
- return {
29155
- source,
29156
- getText
29157
- };
29158
- });
29159
- };
29160
- const createTextChunks = (context, textSources) => {
29161
- return context.scope.runTask(() => {
29162
- const chunkComputations = textSources.map((textSource) => {
29163
- return createComputedTextChunk(context, textSource);
29164
- });
29165
- return createMemo(() => {
29166
- return chunkComputations.map(({ source, getText }) => {
29167
- return new TextChunk(context, source, getText());
29168
- });
29108
+ const createTextChunks = (context, chunkExpressions) => {
29109
+ return createMemo(() => {
29110
+ const chunks = [];
29111
+ const mediaSources = {};
29112
+ chunkExpressions.forEach((chunkExpression) => {
29113
+ if (chunkExpression.source === "literal") {
29114
+ chunks.push(new TextChunk(context, chunkExpression.source, chunkExpression.stringValue));
29115
+ return;
29116
+ }
29117
+ const computed = createComputedExpression(context, chunkExpression)();
29118
+ if (typeof computed === "string") {
29119
+ chunks.push(new TextChunk(context, chunkExpression.source, computed));
29120
+ return;
29121
+ } else {
29122
+ computed.forEach((itextForm) => {
29123
+ if (isEngineXPathElement(itextForm) && itextForm instanceof StaticElement) {
29124
+ const formAttribute = itextForm.getAttributeValue("form");
29125
+ if (!formAttribute) {
29126
+ const defaultFormValue = itextForm.getXPathValue();
29127
+ chunks.push(new TextChunk(context, chunkExpression.source, defaultFormValue));
29128
+ } else if (["image", "video", "audio"].includes(formAttribute)) {
29129
+ const formValue = itextForm.getXPathValue();
29130
+ if (JRResourceURL.isJRResourceReference(formValue)) {
29131
+ mediaSources[formAttribute] = JRResourceURL.from(formValue);
29132
+ }
29133
+ }
29134
+ }
29135
+ });
29136
+ }
29169
29137
  });
29138
+ return { chunks, mediaSources };
29170
29139
  });
29171
29140
  };
29172
29141
  const createTextRange = (context, role, definition) => {
29173
29142
  return context.scope.runTask(() => {
29174
- const getTextChunks = createTextChunks(context, definition.chunks);
29143
+ const textChunks = createTextChunks(context, definition.chunks);
29175
29144
  return createMemo(() => {
29176
- return new TextRange("form", role, getTextChunks());
29145
+ const chunks = textChunks();
29146
+ return new TextRange("form", role, chunks.chunks, chunks.mediaSources);
29177
29147
  });
29178
29148
  });
29179
29149
  };
@@ -29340,8 +29310,7 @@ const createInstanceValueState = (context) => {
29340
29310
  const engineViolationMessage = (context, role) => {
29341
29311
  const messageText = VALIDATION_TEXT[role];
29342
29312
  const chunk = new TextChunk(context, "literal", messageText);
29343
- const message = new TextRange("engine", role, [chunk]);
29344
- return () => message;
29313
+ return () => new TextRange("engine", role, [chunk]);
29345
29314
  };
29346
29315
  const createViolationMessage = (context, role, definition) => {
29347
29316
  if (definition == null) {
@@ -30518,6 +30487,9 @@ class SelectControl extends ValueNode {
30518
30487
  this.appearances = definition.bodyElement.appearances;
30519
30488
  this.selectType = definition.bodyElement.type;
30520
30489
  const valueOptions = createItemCollection(this);
30490
+ const isSelectWithImages = this.scope.runTask(() => {
30491
+ return createMemo(() => valueOptions().some((item) => !!item.label.imageSource));
30492
+ });
30521
30493
  const mapOptionsByValue = this.scope.runTask(() => {
30522
30494
  return createMemo(() => {
30523
30495
  return new Map(valueOptions().map((item) => [item.value, item]));
@@ -30552,7 +30524,8 @@ class SelectControl extends ValueNode {
30552
30524
  children: null,
30553
30525
  valueOptions,
30554
30526
  value: valueState,
30555
- instanceValue: this.getInstanceValue
30527
+ instanceValue: this.getInstanceValue,
30528
+ isSelectWithImages
30556
30529
  },
30557
30530
  this.instanceConfig
30558
30531
  );