diff --git a/src/scripting_api/aform.js b/src/scripting_api/aform.js index 3975c5a7c..9ec325328 100644 --- a/src/scripting_api/aform.js +++ b/src/scripting_api/aform.js @@ -500,14 +500,18 @@ class AForm { const event = globalThis.event; const values = []; + + cFields = this.AFMakeArrayFromList(cFields); for (const cField of cFields) { const field = this._document.getField(cField); if (!field) { continue; } - const number = this.AFMakeNumber(field.value); - if (number !== null) { - values.push(number); + for (const child of field.getArray()) { + const number = this.AFMakeNumber(child.value); + if (number !== null) { + values.push(number); + } } } diff --git a/src/scripting_api/doc.js b/src/scripting_api/doc.js index 2f1d05e47..adbc71100 100644 --- a/src/scripting_api/doc.js +++ b/src/scripting_api/doc.js @@ -889,6 +889,24 @@ class Doc extends PDFObject { return children; } + _getTerminalChildren(fieldName) { + // Get all the descendants which have a value. + const children = []; + const len = fieldName.length; + for (const [name, field] of this._fields.entries()) { + if (name.startsWith(fieldName)) { + const finalPart = name.slice(len); + if ( + field.obj._hasValue && + (finalPart === "" || finalPart.startsWith(".")) + ) { + children.push(field.wrapped); + } + } + } + return children; + } + getIcon() { /* Not implemented */ } diff --git a/src/scripting_api/field.js b/src/scripting_api/field.js index 49dc39450..bdb2add5f 100644 --- a/src/scripting_api/field.js +++ b/src/scripting_api/field.js @@ -76,6 +76,7 @@ class Field extends PDFObject { this._fillColor = data.fillColor || ["T"]; this._isChoice = Array.isArray(data.items); this._items = data.items || []; + this._hasValue = data.hasOwnProperty("value"); this._page = data.page || 0; this._strokeColor = data.strokeColor || ["G", 0]; this._textColor = data.textColor || ["G", 0]; @@ -393,15 +394,32 @@ class Field extends PDFObject { } getArray() { + // Gets the array of terminal child fields (that is, fields that can have + // a value for this Field object, the parent field). if (this._kidIds) { - return this._kidIds.map(id => this._appObjects[id].wrapped); + const array = []; + const fillArrayWithKids = kidIds => { + for (const id of kidIds) { + const obj = this._appObjects[id]; + if (!obj) { + continue; + } + if (obj.obj._hasValue) { + array.push(obj.wrapped); + } + if (obj.obj._kidIds) { + fillArrayWithKids(obj.obj._kidIds); + } + } + }; + fillArrayWithKids(this._kidIds); + return array; } if (this._children === null) { - this._children = this._document.obj - ._getChildren(this._fieldPath) - .map(child => child.wrapped); + this._children = this._document.obj._getTerminalChildren(this._fieldPath); } + return this._children; } diff --git a/test/unit/scripting_spec.js b/test/unit/scripting_spec.js index 08e0ce496..9ae19cf3d 100644 --- a/test/unit/scripting_spec.js +++ b/test/unit/scripting_spec.js @@ -200,7 +200,7 @@ describe("Scripting", function () { value = await myeval( `this.getField("A.B.C.D").getArray().map((x) => x.value)` ); - expect(value).toEqual([5, 7]); + expect(value).toEqual([4, 5, 6, 7]); }); }); @@ -1037,7 +1037,7 @@ describe("Scripting", function () { describe("AFSimple_Calculate", function () { it("should compute the sum of several fields", async () => { - const refIds = [0, 1, 2, 3].map(_ => getId()); + const refIds = [0, 1, 2, 3, 4].map(_ => getId()); const data = { objects: { field1: [ @@ -1076,9 +1076,21 @@ describe("Scripting", function () { type: "text", }, ], + field5: [ + { + id: refIds[4], + value: "", + actions: { + Calculate: [ + `AFSimple_Calculate("SUM", "field1, field2, field3, unknown");`, + ], + }, + type: "text", + }, + ], }, appInfo: { language: "en-US", platform: "Linux x86_64" }, - calculationOrder: [refIds[3]], + calculationOrder: [refIds[3], refIds[4]], dispatchEventName: "_dispatchMe", }; @@ -1124,6 +1136,98 @@ describe("Scripting", function () { value: 6, formattedValue: null, }); + + expect(send_queue.has(refIds[4])).toEqual(true); + expect(send_queue.get(refIds[4])).toEqual({ + id: refIds[4], + siblings: null, + value: 6, + formattedValue: null, + }); + }); + + it("should compute the sum of several fields in fields tree", async () => { + const refIds = [0, 1, 2, 3, 4, 5].map(_ => getId()); + const data = { + objects: { + field1: [ + { + id: refIds[0], + kidIds: [refIds[1], refIds[2]], + }, + ], + "field1.field2": [ + { + id: refIds[1], + kidIds: [refIds[3]], + }, + ], + "field1.field3": [ + { + id: refIds[2], + value: "", + actions: {}, + type: "text", + }, + ], + "field1.field2.field4": [ + { + id: refIds[3], + kidIds: [refIds[4]], + }, + ], + "field1.field2.field4.field5": [ + { + id: refIds[4], + value: "", + actions: {}, + type: "text", + }, + ], + field6: [ + { + id: refIds[5], + value: "", + actions: { + Calculate: [`AFSimple_Calculate("SUM", "field1");`], + }, + type: "text", + }, + ], + }, + appInfo: { language: "en-US", platform: "Linux x86_64" }, + calculationOrder: [refIds[5]], + dispatchEventName: "_dispatchMe", + }; + + sandbox.createSandbox(data); + await sandbox.dispatchEventInSandbox({ + id: refIds[2], + value: "123", + name: "Keystroke", + willCommit: true, + }); + expect(send_queue.has(refIds[5])).toEqual(true); + expect(send_queue.get(refIds[5])).toEqual({ + id: refIds[5], + siblings: null, + value: 123, + formattedValue: null, + }); + + await sandbox.dispatchEventInSandbox({ + id: refIds[4], + value: "456", + name: "Keystroke", + willCommit: true, + }); + expect(send_queue.has(refIds[5])).toEqual(true); + expect(send_queue.get(refIds[5])).toEqual({ + id: refIds[5], + siblings: null, + value: 579, + formattedValue: null, + }); }); });