diff --git a/src/core/xfa/bind.js b/src/core/xfa/bind.js index 576118f25..8af671948 100644 --- a/src/core/xfa/bind.js +++ b/src/core/xfa/bind.js @@ -55,12 +55,12 @@ class Binder { this.root = root; this.datasets = root.datasets; if (root.datasets && root.datasets.data) { - this.emptyMerge = false; this.data = root.datasets.data; } else { - this.emptyMerge = true; this.data = new XmlObject(NamespaceIds.datasets.id, "data"); } + this.emptyMerge = this.data[$getChildren]().length === 0; + this.root.form = this.form = root.template[$clone](); } @@ -477,6 +477,23 @@ class Binder { if (this._mergeMode === undefined && child[$nodeName] === "subform") { this._mergeMode = child.mergeMode === "consumeData"; + + // XFA specs p. 182: + // The highest-level subform and the data node representing + // the current record are special; they are always + // bound even if their names don't match. + const dataChildren = dataNode[$getChildren](); + if (dataChildren.length > 0) { + this._bindOccurrences(child, [dataChildren[0]], null); + } else if (this.emptyMerge) { + const dataChild = new XmlObject( + dataNode[$namespaceId], + child.name || "root" + ); + dataNode[$appendChild](dataChild); + this._bindElement(child, dataChild); + } + continue; } let global = false; @@ -570,21 +587,29 @@ class Binder { } match = matches.length > 0 ? matches : null; } else { + // If we've an empty merge, there are no reason + // to make multiple bind so skip consumed nodes. match = dataNode[$getRealChildrenByNameIt]( child.name, /* allTransparent = */ false, - /* skipConsumed = */ false + /* skipConsumed = */ this.emptyMerge ).next().value; if (!match) { // We're in matchTemplate mode so create a node in data to reflect // what we've in template. match = new XmlObject(dataNode[$namespaceId], child.name); + if (this.emptyMerge) { + match[$consumed] = true; + } dataNode[$appendChild](match); // Don't bind the value in newly created node because it's empty. this._bindElement(child, match); continue; } + if (this.emptyMerge) { + match[$consumed] = true; + } match = [match]; } } diff --git a/test/unit/xfa_tohtml_spec.js b/test/unit/xfa_tohtml_spec.js index 42767e362..0461f3075 100644 --- a/test/unit/xfa_tohtml_spec.js +++ b/test/unit/xfa_tohtml_spec.js @@ -219,4 +219,55 @@ describe("XFAFactory", function () { expect(field2).not.toEqual(null); }); }); + + it("should have an input or textarea", function () { + const xml = ` + + + + + + + + 123 + + + + + + + `; + const factory = new XFAFactory({ "xdp:xdp": xml }); + + expect(factory.numberPages).toEqual(1); + + const pages = factory.getPages(); + const field1 = searchHtmlNode(pages, "name", "input"); + expect(field1).not.toEqual(null); + expect(field1.attributes.value).toEqual("123"); + }); });