pict-section-formeditor 1.0.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 (178) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/docs/.nojekyll +0 -0
  4. package/docs/README.md +162 -0
  5. package/docs/_sidebar.md +23 -0
  6. package/docs/_topbar.md +5 -0
  7. package/docs/cover.md +12 -0
  8. package/docs/css/docuserve.css +73 -0
  9. package/docs/index.html +39 -0
  10. package/docs/retold-catalog.json +224 -0
  11. package/docs/retold-keyword-index.json +46846 -0
  12. package/example_applications/form_editor/.quackage.json +10 -0
  13. package/example_applications/form_editor/FormEditor-Example-Application.js +226 -0
  14. package/example_applications/form_editor/html/icon-chooser.html +375 -0
  15. package/example_applications/form_editor/html/index.html +54 -0
  16. package/example_applications/form_editor/package.json +50 -0
  17. package/package.json +55 -0
  18. package/sample_manifests/Complex-Table.json +974 -0
  19. package/sample_manifests/Distill-Example.json +200 -0
  20. package/sample_manifests/Gradebook-Assignment.json +38 -0
  21. package/sample_manifests/Gradebook-Student.json +40 -0
  22. package/sample_manifests/Manyfest-Editor.json +347 -0
  23. package/sample_manifests/Simple-Form.json +232 -0
  24. package/sample_manifests/Simple-Table.json +79 -0
  25. package/source/Pict-Section-FormEditor-DefaultConfiguration.js +3321 -0
  26. package/source/Pict-Section-FormEditor.js +35 -0
  27. package/source/providers/Pict-Provider-ChildPictManager-Application.js +40 -0
  28. package/source/providers/Pict-Provider-ChildPictManager.js +238 -0
  29. package/source/providers/Pict-Provider-FormEditorDocumentation.js +356 -0
  30. package/source/providers/Pict-Provider-FormEditorDragDrop.js +535 -0
  31. package/source/providers/Pict-Provider-FormEditorIconography.js +1002 -0
  32. package/source/providers/Pict-Provider-FormEditorManifestOps.js +1443 -0
  33. package/source/providers/Pict-Provider-FormEditorRendering.js +730 -0
  34. package/source/providers/Pict-Provider-FormEditorUtilities.js +862 -0
  35. package/source/providers/Pict-Provider-PreviewCSS.js +42 -0
  36. package/source/views/PictView-FormEditor-InlineEditing.js +309 -0
  37. package/source/views/PictView-FormEditor-InputTypePicker.js +532 -0
  38. package/source/views/PictView-FormEditor-PropertiesPanel.js +7730 -0
  39. package/source/views/PictView-FormEditor.js +681 -0
  40. package/test/Pict-Section-FormEditor_tests.js +4102 -0
  41. package/user-documentation/.pict_documentation_topics.json +695 -0
  42. package/user-documentation/Getting-Started.md +32 -0
  43. package/user-documentation/Groups.md +52 -0
  44. package/user-documentation/Inputs.md +98 -0
  45. package/user-documentation/Sections.md +36 -0
  46. package/user-documentation/Shortcuts.md +44 -0
  47. package/user-documentation/Solver-Expression-Walkthrough.md +176 -0
  48. package/user-documentation/Solver-Expressions-Advanced.md +344 -0
  49. package/user-documentation/Solver-Functions.md +213 -0
  50. package/user-documentation/Solvers.md +81 -0
  51. package/user-documentation/ToC.md +18 -0
  52. package/user-documentation/solverfunctions/abs.md +84 -0
  53. package/user-documentation/solverfunctions/aggregationhistogram.md +83 -0
  54. package/user-documentation/solverfunctions/aggregationhistogrambyobject.md +64 -0
  55. package/user-documentation/solverfunctions/arrayconcat.md +64 -0
  56. package/user-documentation/solverfunctions/avg.md +81 -0
  57. package/user-documentation/solverfunctions/bucketset.md +69 -0
  58. package/user-documentation/solverfunctions/ceil.md +70 -0
  59. package/user-documentation/solverfunctions/cleanvaluearray.md +66 -0
  60. package/user-documentation/solverfunctions/cleanvalueobject.md +68 -0
  61. package/user-documentation/solverfunctions/colorgroupbackground.md +60 -0
  62. package/user-documentation/solverfunctions/colorinputbackground.md +62 -0
  63. package/user-documentation/solverfunctions/colorinputbackgroundtabular.md +64 -0
  64. package/user-documentation/solverfunctions/colorsectionbackground.md +59 -0
  65. package/user-documentation/solverfunctions/compare.md +72 -0
  66. package/user-documentation/solverfunctions/concat.md +73 -0
  67. package/user-documentation/solverfunctions/concatraw.md +73 -0
  68. package/user-documentation/solverfunctions/cos.md +75 -0
  69. package/user-documentation/solverfunctions/count.md +73 -0
  70. package/user-documentation/solverfunctions/countset.md +65 -0
  71. package/user-documentation/solverfunctions/countsetelements.md +63 -0
  72. package/user-documentation/solverfunctions/createarrayfromabsolutevalues.md +63 -0
  73. package/user-documentation/solverfunctions/createvalueobjectbyhashes.md +69 -0
  74. package/user-documentation/solverfunctions/cumulativesummation.md +96 -0
  75. package/user-documentation/solverfunctions/dateadddays.md +79 -0
  76. package/user-documentation/solverfunctions/dateaddhours.md +74 -0
  77. package/user-documentation/solverfunctions/dateaddmilliseconds.md +65 -0
  78. package/user-documentation/solverfunctions/dateaddminutes.md +72 -0
  79. package/user-documentation/solverfunctions/dateaddmonths.md +74 -0
  80. package/user-documentation/solverfunctions/dateaddseconds.md +66 -0
  81. package/user-documentation/solverfunctions/dateaddweeks.md +73 -0
  82. package/user-documentation/solverfunctions/dateaddyears.md +74 -0
  83. package/user-documentation/solverfunctions/datedaydifference.md +84 -0
  84. package/user-documentation/solverfunctions/datefromparts.md +81 -0
  85. package/user-documentation/solverfunctions/datehourdifference.md +64 -0
  86. package/user-documentation/solverfunctions/datemathadd.md +72 -0
  87. package/user-documentation/solverfunctions/datemilliseconddifference.md +64 -0
  88. package/user-documentation/solverfunctions/dateminutedifference.md +64 -0
  89. package/user-documentation/solverfunctions/datemonthdifference.md +66 -0
  90. package/user-documentation/solverfunctions/dateseconddifference.md +64 -0
  91. package/user-documentation/solverfunctions/dateweekdifference.md +65 -0
  92. package/user-documentation/solverfunctions/dateyeardifference.md +64 -0
  93. package/user-documentation/solverfunctions/differencearrays.md +59 -0
  94. package/user-documentation/solverfunctions/disablesolverordinal.md +58 -0
  95. package/user-documentation/solverfunctions/distributionhistogram.md +96 -0
  96. package/user-documentation/solverfunctions/distributionhistogrambyobject.md +64 -0
  97. package/user-documentation/solverfunctions/enablesolverordinal.md +57 -0
  98. package/user-documentation/solverfunctions/entryinset.md +72 -0
  99. package/user-documentation/solverfunctions/euler.md +77 -0
  100. package/user-documentation/solverfunctions/exp.md +74 -0
  101. package/user-documentation/solverfunctions/findfirstvaluebyexactmatch.md +67 -0
  102. package/user-documentation/solverfunctions/findfirstvaluebystringincludes.md +67 -0
  103. package/user-documentation/solverfunctions/flatten.md +76 -0
  104. package/user-documentation/solverfunctions/floor.md +70 -0
  105. package/user-documentation/solverfunctions/gaussianelimination.md +75 -0
  106. package/user-documentation/solverfunctions/generatearrayofobjectsfromsets.md +70 -0
  107. package/user-documentation/solverfunctions/generatehtmlhexcolor.md +67 -0
  108. package/user-documentation/solverfunctions/getvalue.md +90 -0
  109. package/user-documentation/solverfunctions/getvaluearray.md +64 -0
  110. package/user-documentation/solverfunctions/getvalueobject.md +67 -0
  111. package/user-documentation/solverfunctions/hidesections.md +58 -0
  112. package/user-documentation/solverfunctions/if.md +109 -0
  113. package/user-documentation/solverfunctions/iterativeseries.md +107 -0
  114. package/user-documentation/solverfunctions/join.md +75 -0
  115. package/user-documentation/solverfunctions/joinraw.md +64 -0
  116. package/user-documentation/solverfunctions/largestinset.md +63 -0
  117. package/user-documentation/solverfunctions/leastsquares.md +66 -0
  118. package/user-documentation/solverfunctions/linest.md +58 -0
  119. package/user-documentation/solverfunctions/log.md +74 -0
  120. package/user-documentation/solverfunctions/logvalues.md +65 -0
  121. package/user-documentation/solverfunctions/match.md +71 -0
  122. package/user-documentation/solverfunctions/matrixinverse.md +67 -0
  123. package/user-documentation/solverfunctions/matrixmultiply.md +71 -0
  124. package/user-documentation/solverfunctions/matrixtranspose.md +72 -0
  125. package/user-documentation/solverfunctions/matrixvectormultiply.md +69 -0
  126. package/user-documentation/solverfunctions/max.md +73 -0
  127. package/user-documentation/solverfunctions/mean.md +63 -0
  128. package/user-documentation/solverfunctions/median.md +79 -0
  129. package/user-documentation/solverfunctions/min.md +73 -0
  130. package/user-documentation/solverfunctions/mode.md +66 -0
  131. package/user-documentation/solverfunctions/objectkeystoarray.md +66 -0
  132. package/user-documentation/solverfunctions/objectvaluessortbyexternalobjectarray.md +65 -0
  133. package/user-documentation/solverfunctions/objectvaluestoarray.md +67 -0
  134. package/user-documentation/solverfunctions/percent.md +75 -0
  135. package/user-documentation/solverfunctions/pi.md +77 -0
  136. package/user-documentation/solverfunctions/polynomialregression.md +69 -0
  137. package/user-documentation/solverfunctions/predict.md +71 -0
  138. package/user-documentation/solverfunctions/rad.md +85 -0
  139. package/user-documentation/solverfunctions/randomfloat.md +63 -0
  140. package/user-documentation/solverfunctions/randomfloatbetween.md +72 -0
  141. package/user-documentation/solverfunctions/randomfloatupto.md +65 -0
  142. package/user-documentation/solverfunctions/randominteger.md +56 -0
  143. package/user-documentation/solverfunctions/randomintegerbetween.md +72 -0
  144. package/user-documentation/solverfunctions/randomintegerupto.md +64 -0
  145. package/user-documentation/solverfunctions/refreshtabularsection.md +57 -0
  146. package/user-documentation/solverfunctions/resolvehtmlentities.md +64 -0
  147. package/user-documentation/solverfunctions/round.md +111 -0
  148. package/user-documentation/solverfunctions/runsolvers.md +49 -0
  149. package/user-documentation/solverfunctions/setconcatenate.md +64 -0
  150. package/user-documentation/solverfunctions/setgroupvisibility.md +60 -0
  151. package/user-documentation/solverfunctions/setsectionvisibility.md +59 -0
  152. package/user-documentation/solverfunctions/setsolverordinalenabled.md +59 -0
  153. package/user-documentation/solverfunctions/settabularrowlength.md +57 -0
  154. package/user-documentation/solverfunctions/setvalue.md +65 -0
  155. package/user-documentation/solverfunctions/showsections.md +58 -0
  156. package/user-documentation/solverfunctions/sin.md +83 -0
  157. package/user-documentation/solverfunctions/slice.md +80 -0
  158. package/user-documentation/solverfunctions/smallestinset.md +63 -0
  159. package/user-documentation/solverfunctions/sortarray.md +58 -0
  160. package/user-documentation/solverfunctions/sorthistogram.md +70 -0
  161. package/user-documentation/solverfunctions/sorthistogrambykeys.md +69 -0
  162. package/user-documentation/solverfunctions/sortset.md +75 -0
  163. package/user-documentation/solverfunctions/sqrt.md +85 -0
  164. package/user-documentation/solverfunctions/stdev.md +81 -0
  165. package/user-documentation/solverfunctions/stdeva.md +58 -0
  166. package/user-documentation/solverfunctions/stdevp.md +83 -0
  167. package/user-documentation/solverfunctions/stringcountsegments.md +66 -0
  168. package/user-documentation/solverfunctions/stringgetsegments.md +74 -0
  169. package/user-documentation/solverfunctions/subtractingsummation.md +66 -0
  170. package/user-documentation/solverfunctions/sum.md +78 -0
  171. package/user-documentation/solverfunctions/tan.md +78 -0
  172. package/user-documentation/solverfunctions/tofixed.md +75 -0
  173. package/user-documentation/solverfunctions/unionarrays.md +59 -0
  174. package/user-documentation/solverfunctions/uniquearray.md +58 -0
  175. package/user-documentation/solverfunctions/var.md +67 -0
  176. package/user-documentation/solverfunctions/vara.md +58 -0
  177. package/user-documentation/solverfunctions/varp.md +66 -0
  178. package/user-documentation/solverfunctions/when.md +98 -0
@@ -0,0 +1,1443 @@
1
+ const libPictProvider = require('pict-provider');
2
+
3
+ class FormEditorManifestOps extends libPictProvider
4
+ {
5
+ constructor(pFable, pOptions, pServiceHash)
6
+ {
7
+ super(pFable, pOptions, pServiceHash);
8
+
9
+ this.serviceType = 'PictProvider';
10
+
11
+ // Back-reference to the parent FormEditor view (set after construction)
12
+ this._ParentFormEditor = null;
13
+ }
14
+
15
+ /**
16
+ * Reconcile the manifest structure so that every Descriptor's PictForm
17
+ * reference is reflected in its Section's Group's Rows[].Inputs[] arrays.
18
+ *
19
+ * Existing manifests store layout information entirely in the Descriptors'
20
+ * PictForm objects (Section, Group, Row) and do not carry Rows/Inputs
21
+ * arrays on the Groups. This method builds those arrays from the
22
+ * Descriptors so the visual editor can render them.
23
+ *
24
+ * It is safe to call repeatedly — it only adds missing structure and never
25
+ * removes or duplicates entries.
26
+ */
27
+ _reconcileManifestStructure()
28
+ {
29
+ let tmpManifest = this._resolveManifestData();
30
+ if (!tmpManifest)
31
+ {
32
+ return;
33
+ }
34
+
35
+ if (!Array.isArray(tmpManifest.Sections))
36
+ {
37
+ return;
38
+ }
39
+
40
+ if (!tmpManifest.Descriptors || typeof tmpManifest.Descriptors !== 'object')
41
+ {
42
+ return;
43
+ }
44
+
45
+ // Build a quick lookup: SectionHash -> section index
46
+ let tmpSectionMap = {};
47
+ for (let i = 0; i < tmpManifest.Sections.length; i++)
48
+ {
49
+ let tmpSection = tmpManifest.Sections[i];
50
+ if (tmpSection.Hash)
51
+ {
52
+ tmpSectionMap[tmpSection.Hash] = i;
53
+ }
54
+
55
+ // Ensure every section has a Groups array
56
+ if (!Array.isArray(tmpSection.Groups))
57
+ {
58
+ tmpSection.Groups = [];
59
+ }
60
+ }
61
+
62
+ // Build a lookup: SectionHash -> GroupHash -> group index
63
+ let tmpGroupMap = {};
64
+ for (let i = 0; i < tmpManifest.Sections.length; i++)
65
+ {
66
+ let tmpSection = tmpManifest.Sections[i];
67
+ let tmpSectionHash = tmpSection.Hash || '';
68
+ tmpGroupMap[tmpSectionHash] = {};
69
+ for (let j = 0; j < tmpSection.Groups.length; j++)
70
+ {
71
+ let tmpGroup = tmpSection.Groups[j];
72
+ if (tmpGroup.Hash)
73
+ {
74
+ tmpGroupMap[tmpSectionHash][tmpGroup.Hash] = j;
75
+ }
76
+ }
77
+ }
78
+
79
+ // Build a set of all Addresses already present in any Rows[].Inputs[]
80
+ let tmpExistingAddresses = {};
81
+ for (let i = 0; i < tmpManifest.Sections.length; i++)
82
+ {
83
+ let tmpSection = tmpManifest.Sections[i];
84
+ for (let j = 0; j < tmpSection.Groups.length; j++)
85
+ {
86
+ let tmpGroup = tmpSection.Groups[j];
87
+ if (Array.isArray(tmpGroup.Rows))
88
+ {
89
+ for (let k = 0; k < tmpGroup.Rows.length; k++)
90
+ {
91
+ let tmpRow = tmpGroup.Rows[k];
92
+ if (tmpRow && Array.isArray(tmpRow.Inputs))
93
+ {
94
+ for (let m = 0; m < tmpRow.Inputs.length; m++)
95
+ {
96
+ tmpExistingAddresses[tmpRow.Inputs[m]] = true;
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ // Walk every Descriptor and place it into the correct Rows[].Inputs[]
105
+ let tmpDescriptorKeys = Object.keys(tmpManifest.Descriptors);
106
+ for (let d = 0; d < tmpDescriptorKeys.length; d++)
107
+ {
108
+ let tmpAddress = tmpDescriptorKeys[d];
109
+
110
+ // Skip if already placed
111
+ if (tmpExistingAddresses[tmpAddress])
112
+ {
113
+ continue;
114
+ }
115
+
116
+ let tmpDescriptor = tmpManifest.Descriptors[tmpAddress];
117
+ if (!tmpDescriptor || !tmpDescriptor.PictForm)
118
+ {
119
+ continue;
120
+ }
121
+
122
+ let tmpPictForm = tmpDescriptor.PictForm;
123
+ let tmpSectionHash = tmpPictForm.Section;
124
+ if (!tmpSectionHash || !tmpSectionMap.hasOwnProperty(tmpSectionHash))
125
+ {
126
+ continue;
127
+ }
128
+
129
+ let tmpSectionIndex = tmpSectionMap[tmpSectionHash];
130
+ let tmpSection = tmpManifest.Sections[tmpSectionIndex];
131
+
132
+ // Resolve the group — if PictForm.Group is missing, use the first
133
+ // group; create a default group if none exist.
134
+ let tmpGroupHash = tmpPictForm.Group || null;
135
+ let tmpGroupIndex = -1;
136
+
137
+ if (tmpGroupHash && tmpGroupMap[tmpSectionHash].hasOwnProperty(tmpGroupHash))
138
+ {
139
+ tmpGroupIndex = tmpGroupMap[tmpSectionHash][tmpGroupHash];
140
+ }
141
+ else if (!tmpGroupHash && tmpSection.Groups.length > 0)
142
+ {
143
+ // Default to first group when PictForm.Group is omitted
144
+ tmpGroupIndex = 0;
145
+ }
146
+
147
+ if (tmpGroupIndex < 0)
148
+ {
149
+ // Create a default group for this section
150
+ let tmpNewGroupHash = tmpGroupHash || (tmpSectionHash + 'Group_Default');
151
+ let tmpNewGroupName = tmpGroupHash || 'Default';
152
+ tmpSection.Groups.push(
153
+ {
154
+ Hash: tmpNewGroupHash,
155
+ Name: tmpNewGroupName,
156
+ Layout: 'Record'
157
+ });
158
+ tmpGroupIndex = tmpSection.Groups.length - 1;
159
+ tmpGroupMap[tmpSectionHash][tmpNewGroupHash] = tmpGroupIndex;
160
+ }
161
+
162
+ let tmpGroup = tmpSection.Groups[tmpGroupIndex];
163
+
164
+ // Ensure the group has a Rows array
165
+ if (!Array.isArray(tmpGroup.Rows))
166
+ {
167
+ tmpGroup.Rows = [];
168
+ }
169
+
170
+ // PictForm.Row is 1-based; pad with empty rows if necessary
171
+ let tmpRowNumber = (typeof tmpPictForm.Row === 'number' && tmpPictForm.Row > 0) ? tmpPictForm.Row : 1;
172
+ let tmpRowIndex = tmpRowNumber - 1;
173
+ while (tmpGroup.Rows.length <= tmpRowIndex)
174
+ {
175
+ tmpGroup.Rows.push({ Inputs: [] });
176
+ }
177
+
178
+ let tmpRow = tmpGroup.Rows[tmpRowIndex];
179
+ if (!Array.isArray(tmpRow.Inputs))
180
+ {
181
+ tmpRow.Inputs = [];
182
+ }
183
+
184
+ tmpRow.Inputs.push(tmpAddress);
185
+ }
186
+ }
187
+
188
+ addSection()
189
+ {
190
+ let tmpManifest = this._resolveManifestData();
191
+ if (!tmpManifest)
192
+ {
193
+ return;
194
+ }
195
+
196
+ if (!Array.isArray(tmpManifest.Sections))
197
+ {
198
+ tmpManifest.Sections = [];
199
+ }
200
+
201
+ let tmpIndex = tmpManifest.Sections.length;
202
+ let tmpSectionNum = tmpIndex + 1;
203
+ let tmpSectionName = `Section ${tmpSectionNum}`;
204
+ let tmpSectionHash = `S${tmpSectionNum}`;
205
+ let tmpGroupName = 'Group 1';
206
+ let tmpGroupHash = `${tmpSectionHash}_G1`;
207
+
208
+ tmpManifest.Sections.push(
209
+ {
210
+ Hash: tmpSectionHash,
211
+ Name: tmpSectionName,
212
+ Groups:
213
+ [
214
+ {
215
+ Hash: tmpGroupHash,
216
+ Name: tmpGroupName,
217
+ Layout: 'Record'
218
+ }
219
+ ]
220
+ });
221
+
222
+ this._ParentFormEditor.renderVisualEditor();
223
+ }
224
+
225
+ removeSection(pIndex)
226
+ {
227
+ let tmpManifest = this._resolveManifestData();
228
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
229
+ {
230
+ return;
231
+ }
232
+
233
+ if (pIndex >= 0 && pIndex < tmpManifest.Sections.length)
234
+ {
235
+ // Clean up Descriptor entries for all inputs in this section
236
+ let tmpSection = tmpManifest.Sections[pIndex];
237
+ this._removeDescriptorsForSection(tmpManifest, tmpSection);
238
+
239
+ tmpManifest.Sections.splice(pIndex, 1);
240
+ this._ParentFormEditor.renderVisualEditor();
241
+ }
242
+ }
243
+
244
+ moveSectionUp(pIndex)
245
+ {
246
+ let tmpManifest = this._resolveManifestData();
247
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections) || pIndex <= 0)
248
+ {
249
+ return;
250
+ }
251
+
252
+ let tmpSection = tmpManifest.Sections.splice(pIndex, 1)[0];
253
+ tmpManifest.Sections.splice(pIndex - 1, 0, tmpSection);
254
+ this._ParentFormEditor.renderVisualEditor();
255
+ }
256
+
257
+ moveSectionDown(pIndex)
258
+ {
259
+ let tmpManifest = this._resolveManifestData();
260
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections) || pIndex >= tmpManifest.Sections.length - 1)
261
+ {
262
+ return;
263
+ }
264
+
265
+ let tmpSection = tmpManifest.Sections.splice(pIndex, 1)[0];
266
+ tmpManifest.Sections.splice(pIndex + 1, 0, tmpSection);
267
+ this._ParentFormEditor.renderVisualEditor();
268
+ }
269
+
270
+ updateSectionProperty(pSectionIndex, pProperty, pValue)
271
+ {
272
+ let tmpManifest = this._resolveManifestData();
273
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
274
+ {
275
+ return;
276
+ }
277
+
278
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
279
+ if (tmpSection)
280
+ {
281
+ let tmpOldValue = tmpSection[pProperty];
282
+ tmpSection[pProperty] = pValue;
283
+
284
+ // When the section Hash changes, cascade to group hashes that
285
+ // are still auto-generated. Auto-generated group hashes follow
286
+ // the pattern {SectionHash}_G{...}. If the user has overridden
287
+ // a group hash the "_G" prefix after the old section hash will
288
+ // no longer be present, so it is skipped.
289
+ if (pProperty === 'Hash' && tmpOldValue !== pValue && Array.isArray(tmpSection.Groups))
290
+ {
291
+ let tmpOldPrefix = tmpOldValue + '_G';
292
+ let tmpNewPrefix = pValue + '_G';
293
+ for (let i = 0; i < tmpSection.Groups.length; i++)
294
+ {
295
+ let tmpGroup = tmpSection.Groups[i];
296
+ if (tmpGroup.Hash && tmpGroup.Hash.indexOf(tmpOldPrefix) === 0)
297
+ {
298
+ tmpGroup.Hash = tmpNewPrefix + tmpGroup.Hash.substring(tmpOldPrefix.length);
299
+ }
300
+ }
301
+ }
302
+ }
303
+ }
304
+
305
+ addGroup(pSectionIndex)
306
+ {
307
+ let tmpManifest = this._resolveManifestData();
308
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
309
+ {
310
+ return;
311
+ }
312
+
313
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
314
+ if (!tmpSection)
315
+ {
316
+ return;
317
+ }
318
+
319
+ if (!Array.isArray(tmpSection.Groups))
320
+ {
321
+ tmpSection.Groups = [];
322
+ }
323
+
324
+ let tmpIndex = tmpSection.Groups.length;
325
+ let tmpSectionHash = tmpSection.Hash || '';
326
+ let tmpGroupNum = tmpIndex + 1;
327
+ let tmpGroupName = `Group ${tmpGroupNum}`;
328
+ let tmpGroupHash = `${tmpSectionHash}_G${tmpGroupNum}`;
329
+
330
+ tmpSection.Groups.push(
331
+ {
332
+ Hash: tmpGroupHash,
333
+ Name: tmpGroupName,
334
+ Layout: 'Record'
335
+ });
336
+
337
+ this._ParentFormEditor.renderVisualEditor();
338
+ }
339
+
340
+ removeGroup(pSectionIndex, pGroupIndex)
341
+ {
342
+ let tmpManifest = this._resolveManifestData();
343
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
344
+ {
345
+ return;
346
+ }
347
+
348
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
349
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
350
+ {
351
+ return;
352
+ }
353
+
354
+ if (pGroupIndex >= 0 && pGroupIndex < tmpSection.Groups.length)
355
+ {
356
+ // Clean up Descriptor entries for all inputs in this group
357
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
358
+ this._removeDescriptorsForGroup(tmpManifest, tmpGroup);
359
+
360
+ tmpSection.Groups.splice(pGroupIndex, 1);
361
+ this._ParentFormEditor.renderVisualEditor();
362
+ }
363
+ }
364
+
365
+ moveGroupUp(pSectionIndex, pGroupIndex)
366
+ {
367
+ let tmpManifest = this._resolveManifestData();
368
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
369
+ {
370
+ return;
371
+ }
372
+
373
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
374
+ if (!tmpSection || !Array.isArray(tmpSection.Groups) || pGroupIndex <= 0)
375
+ {
376
+ return;
377
+ }
378
+
379
+ let tmpGroup = tmpSection.Groups.splice(pGroupIndex, 1)[0];
380
+ tmpSection.Groups.splice(pGroupIndex - 1, 0, tmpGroup);
381
+ this._ParentFormEditor.renderVisualEditor();
382
+ }
383
+
384
+ moveGroupDown(pSectionIndex, pGroupIndex)
385
+ {
386
+ let tmpManifest = this._resolveManifestData();
387
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
388
+ {
389
+ return;
390
+ }
391
+
392
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
393
+ if (!tmpSection || !Array.isArray(tmpSection.Groups) || pGroupIndex >= tmpSection.Groups.length - 1)
394
+ {
395
+ return;
396
+ }
397
+
398
+ let tmpGroup = tmpSection.Groups.splice(pGroupIndex, 1)[0];
399
+ tmpSection.Groups.splice(pGroupIndex + 1, 0, tmpGroup);
400
+ this._ParentFormEditor.renderVisualEditor();
401
+ }
402
+
403
+ updateGroupProperty(pSectionIndex, pGroupIndex, pProperty, pValue)
404
+ {
405
+ let tmpManifest = this._resolveManifestData();
406
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
407
+ {
408
+ return;
409
+ }
410
+
411
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
412
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
413
+ {
414
+ return;
415
+ }
416
+
417
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
418
+ if (tmpGroup)
419
+ {
420
+ tmpGroup[pProperty] = pValue;
421
+ }
422
+ }
423
+
424
+ addRow(pSectionIndex, pGroupIndex)
425
+ {
426
+ let tmpManifest = this._resolveManifestData();
427
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
428
+ {
429
+ return;
430
+ }
431
+
432
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
433
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
434
+ {
435
+ return;
436
+ }
437
+
438
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
439
+ if (!tmpGroup)
440
+ {
441
+ return;
442
+ }
443
+
444
+ if (!Array.isArray(tmpGroup.Rows))
445
+ {
446
+ tmpGroup.Rows = [];
447
+ }
448
+
449
+ tmpGroup.Rows.push({ Inputs: [] });
450
+
451
+ this._ParentFormEditor.renderVisualEditor();
452
+ }
453
+
454
+ removeRow(pSectionIndex, pGroupIndex, pRowIndex)
455
+ {
456
+ let tmpManifest = this._resolveManifestData();
457
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
458
+ {
459
+ return;
460
+ }
461
+
462
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
463
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
464
+ {
465
+ return;
466
+ }
467
+
468
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
469
+ if (!tmpGroup || !Array.isArray(tmpGroup.Rows))
470
+ {
471
+ return;
472
+ }
473
+
474
+ if (pRowIndex >= 0 && pRowIndex < tmpGroup.Rows.length)
475
+ {
476
+ // Clean up Descriptor entries for all inputs in this row
477
+ let tmpRow = tmpGroup.Rows[pRowIndex];
478
+ if (tmpRow && Array.isArray(tmpRow.Inputs) && tmpManifest.Descriptors)
479
+ {
480
+ for (let i = 0; i < tmpRow.Inputs.length; i++)
481
+ {
482
+ let tmpAddress = tmpRow.Inputs[i];
483
+ if (typeof tmpAddress === 'string' && tmpManifest.Descriptors.hasOwnProperty(tmpAddress))
484
+ {
485
+ delete tmpManifest.Descriptors[tmpAddress];
486
+ }
487
+ }
488
+ }
489
+
490
+ tmpGroup.Rows.splice(pRowIndex, 1);
491
+
492
+ // Update PictForm.Row for inputs in subsequent rows
493
+ this._syncRowIndices(tmpManifest, tmpGroup);
494
+
495
+ this._ParentFormEditor.renderVisualEditor();
496
+ }
497
+ }
498
+
499
+ moveRowUp(pSectionIndex, pGroupIndex, pRowIndex)
500
+ {
501
+ let tmpManifest = this._resolveManifestData();
502
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
503
+ {
504
+ return;
505
+ }
506
+
507
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
508
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
509
+ {
510
+ return;
511
+ }
512
+
513
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
514
+ if (!tmpGroup || !Array.isArray(tmpGroup.Rows) || pRowIndex <= 0)
515
+ {
516
+ return;
517
+ }
518
+
519
+ let tmpRow = tmpGroup.Rows.splice(pRowIndex, 1)[0];
520
+ tmpGroup.Rows.splice(pRowIndex - 1, 0, tmpRow);
521
+
522
+ this._syncRowIndices(tmpManifest, tmpGroup);
523
+ this._ParentFormEditor.renderVisualEditor();
524
+ }
525
+
526
+ moveRowDown(pSectionIndex, pGroupIndex, pRowIndex)
527
+ {
528
+ let tmpManifest = this._resolveManifestData();
529
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
530
+ {
531
+ return;
532
+ }
533
+
534
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
535
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
536
+ {
537
+ return;
538
+ }
539
+
540
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
541
+ if (!tmpGroup || !Array.isArray(tmpGroup.Rows) || pRowIndex >= tmpGroup.Rows.length - 1)
542
+ {
543
+ return;
544
+ }
545
+
546
+ let tmpRow = tmpGroup.Rows.splice(pRowIndex, 1)[0];
547
+ tmpGroup.Rows.splice(pRowIndex + 1, 0, tmpRow);
548
+
549
+ this._syncRowIndices(tmpManifest, tmpGroup);
550
+ this._ParentFormEditor.renderVisualEditor();
551
+ }
552
+
553
+ addInput(pSectionIndex, pGroupIndex, pRowIndex)
554
+ {
555
+ let tmpManifest = this._resolveManifestData();
556
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
557
+ {
558
+ return;
559
+ }
560
+
561
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
562
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
563
+ {
564
+ return;
565
+ }
566
+
567
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
568
+ if (!tmpGroup || !Array.isArray(tmpGroup.Rows))
569
+ {
570
+ return;
571
+ }
572
+
573
+ let tmpRow = tmpGroup.Rows[pRowIndex];
574
+ if (!tmpRow)
575
+ {
576
+ return;
577
+ }
578
+
579
+ if (!Array.isArray(tmpRow.Inputs))
580
+ {
581
+ tmpRow.Inputs = [];
582
+ }
583
+
584
+ if (!tmpManifest.Descriptors || typeof tmpManifest.Descriptors !== 'object')
585
+ {
586
+ tmpManifest.Descriptors = {};
587
+ }
588
+
589
+ // Generate a unique address for this input using short format
590
+ let tmpInputNum = tmpRow.Inputs.length + 1;
591
+ let tmpSectionHash = tmpSection.Hash || 'S';
592
+ let tmpGroupHash = tmpGroup.Hash || 'G';
593
+ let tmpRowNum = pRowIndex + 1;
594
+ let tmpInputHash = `${tmpGroupHash}_R${tmpRowNum}_Input${tmpInputNum}`;
595
+ let tmpAddress = tmpInputHash;
596
+
597
+ // Ensure the address is unique in the Descriptors
598
+ while (tmpManifest.Descriptors.hasOwnProperty(tmpAddress))
599
+ {
600
+ tmpInputNum++;
601
+ tmpInputHash = `${tmpGroupHash}_R${tmpRowNum}_Input${tmpInputNum}`;
602
+ tmpAddress = tmpInputHash;
603
+ }
604
+
605
+ let tmpInputName = `Input ${tmpInputNum}`;
606
+
607
+ // Create the Descriptor entry
608
+ tmpManifest.Descriptors[tmpAddress] =
609
+ {
610
+ Name: tmpInputName,
611
+ Hash: tmpInputHash,
612
+ DataType: 'String',
613
+ PictForm:
614
+ {
615
+ Section: tmpSectionHash,
616
+ Group: tmpGroupHash,
617
+ Row: tmpRowNum
618
+ }
619
+ };
620
+
621
+ // Store the Address reference in the row's Inputs array
622
+ tmpRow.Inputs.push(tmpAddress);
623
+
624
+ this._ParentFormEditor.renderVisualEditor();
625
+ }
626
+
627
+ removeInput(pSectionIndex, pGroupIndex, pRowIndex, pInputIndex)
628
+ {
629
+ let tmpManifest = this._resolveManifestData();
630
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
631
+ {
632
+ return;
633
+ }
634
+
635
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
636
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
637
+ {
638
+ return;
639
+ }
640
+
641
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
642
+ if (!tmpGroup || !Array.isArray(tmpGroup.Rows))
643
+ {
644
+ return;
645
+ }
646
+
647
+ let tmpRow = tmpGroup.Rows[pRowIndex];
648
+ if (!tmpRow || !Array.isArray(tmpRow.Inputs))
649
+ {
650
+ return;
651
+ }
652
+
653
+ if (pInputIndex >= 0 && pInputIndex < tmpRow.Inputs.length)
654
+ {
655
+ // Remove the corresponding Descriptor entry
656
+ let tmpAddress = tmpRow.Inputs[pInputIndex];
657
+ if (typeof tmpAddress === 'string' && tmpManifest.Descriptors && tmpManifest.Descriptors.hasOwnProperty(tmpAddress))
658
+ {
659
+ delete tmpManifest.Descriptors[tmpAddress];
660
+ }
661
+
662
+ tmpRow.Inputs.splice(pInputIndex, 1);
663
+ this._ParentFormEditor.renderVisualEditor();
664
+ }
665
+ }
666
+
667
+ /**
668
+ * Move an input left (earlier) within its row.
669
+ *
670
+ * @param {number} pSectionIndex
671
+ * @param {number} pGroupIndex
672
+ * @param {number} pRowIndex
673
+ * @param {number} pInputIndex
674
+ */
675
+ moveInputLeft(pSectionIndex, pGroupIndex, pRowIndex, pInputIndex)
676
+ {
677
+ let tmpManifest = this._resolveManifestData();
678
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
679
+ {
680
+ return;
681
+ }
682
+
683
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
684
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
685
+ {
686
+ return;
687
+ }
688
+
689
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
690
+ if (!tmpGroup || !Array.isArray(tmpGroup.Rows))
691
+ {
692
+ return;
693
+ }
694
+
695
+ let tmpRow = tmpGroup.Rows[pRowIndex];
696
+ if (!tmpRow || !Array.isArray(tmpRow.Inputs))
697
+ {
698
+ return;
699
+ }
700
+
701
+ if (pInputIndex <= 0 || pInputIndex >= tmpRow.Inputs.length)
702
+ {
703
+ return;
704
+ }
705
+
706
+ let tmpItem = tmpRow.Inputs.splice(pInputIndex, 1)[0];
707
+ tmpRow.Inputs.splice(pInputIndex - 1, 0, tmpItem);
708
+
709
+ // Update the selection to follow the moved input
710
+ this._ParentFormEditor._SelectedInputIndices = [pSectionIndex, pGroupIndex, pRowIndex, pInputIndex - 1];
711
+ if (this._ParentFormEditor._PropertiesPanelView && this._ParentFormEditor._PropertiesPanelView._SelectedInput)
712
+ {
713
+ this._ParentFormEditor._PropertiesPanelView._SelectedInput.InputIndex = pInputIndex - 1;
714
+ }
715
+
716
+ this._ParentFormEditor.renderVisualEditor();
717
+ }
718
+
719
+ /**
720
+ * Move an input right (later) within its row.
721
+ *
722
+ * @param {number} pSectionIndex
723
+ * @param {number} pGroupIndex
724
+ * @param {number} pRowIndex
725
+ * @param {number} pInputIndex
726
+ */
727
+ moveInputRight(pSectionIndex, pGroupIndex, pRowIndex, pInputIndex)
728
+ {
729
+ let tmpManifest = this._resolveManifestData();
730
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
731
+ {
732
+ return;
733
+ }
734
+
735
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
736
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
737
+ {
738
+ return;
739
+ }
740
+
741
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
742
+ if (!tmpGroup || !Array.isArray(tmpGroup.Rows))
743
+ {
744
+ return;
745
+ }
746
+
747
+ let tmpRow = tmpGroup.Rows[pRowIndex];
748
+ if (!tmpRow || !Array.isArray(tmpRow.Inputs))
749
+ {
750
+ return;
751
+ }
752
+
753
+ if (pInputIndex < 0 || pInputIndex >= tmpRow.Inputs.length - 1)
754
+ {
755
+ return;
756
+ }
757
+
758
+ let tmpItem = tmpRow.Inputs.splice(pInputIndex, 1)[0];
759
+ tmpRow.Inputs.splice(pInputIndex + 1, 0, tmpItem);
760
+
761
+ // Update the selection to follow the moved input
762
+ this._ParentFormEditor._SelectedInputIndices = [pSectionIndex, pGroupIndex, pRowIndex, pInputIndex + 1];
763
+ if (this._ParentFormEditor._PropertiesPanelView && this._ParentFormEditor._PropertiesPanelView._SelectedInput)
764
+ {
765
+ this._ParentFormEditor._PropertiesPanelView._SelectedInput.InputIndex = pInputIndex + 1;
766
+ }
767
+
768
+ this._ParentFormEditor.renderVisualEditor();
769
+ }
770
+
771
+ /**
772
+ * Return the list of ReferenceManifest keys in the manifest.
773
+ *
774
+ * @returns {Array<string>}
775
+ */
776
+ getReferenceManifestNames()
777
+ {
778
+ let tmpManifest = this._resolveManifestData();
779
+ if (!tmpManifest || !tmpManifest.ReferenceManifests || typeof tmpManifest.ReferenceManifests !== 'object')
780
+ {
781
+ return [];
782
+ }
783
+ return Object.keys(tmpManifest.ReferenceManifests);
784
+ }
785
+
786
+ /**
787
+ * Create a new ReferenceManifest entry.
788
+ *
789
+ * @param {string} pName - Desired name/key for the ReferenceManifest
790
+ * @returns {string} The actual key used (may differ if pName was taken)
791
+ */
792
+ createReferenceManifest(pName)
793
+ {
794
+ let tmpManifest = this._resolveManifestData();
795
+ if (!tmpManifest)
796
+ {
797
+ return '';
798
+ }
799
+
800
+ if (!tmpManifest.ReferenceManifests || typeof tmpManifest.ReferenceManifests !== 'object')
801
+ {
802
+ tmpManifest.ReferenceManifests = {};
803
+ }
804
+
805
+ let tmpKey = (typeof pName === 'string' && pName.length > 0) ? pName : 'SubManifest_1';
806
+
807
+ // Ensure uniqueness
808
+ let tmpBase = tmpKey;
809
+ let tmpCounter = 1;
810
+ while (tmpManifest.ReferenceManifests.hasOwnProperty(tmpKey))
811
+ {
812
+ tmpCounter++;
813
+ tmpKey = tmpBase + '_' + tmpCounter;
814
+ }
815
+
816
+ tmpManifest.ReferenceManifests[tmpKey] =
817
+ {
818
+ Scope: tmpKey,
819
+ Descriptors: {}
820
+ };
821
+
822
+ return tmpKey;
823
+ }
824
+
825
+ /**
826
+ * Resolve a ReferenceManifest by name.
827
+ *
828
+ * @param {string} pManifestName - Key in ReferenceManifests
829
+ * @returns {object|null}
830
+ */
831
+ _resolveReferenceManifest(pManifestName)
832
+ {
833
+ if (!pManifestName || typeof pManifestName !== 'string')
834
+ {
835
+ return null;
836
+ }
837
+
838
+ let tmpManifest = this._resolveManifestData();
839
+ if (!tmpManifest || !tmpManifest.ReferenceManifests)
840
+ {
841
+ return null;
842
+ }
843
+
844
+ return tmpManifest.ReferenceManifests[pManifestName] || null;
845
+ }
846
+
847
+ /**
848
+ * Bind an existing ReferenceManifest to a group.
849
+ *
850
+ * @param {number} pSectionIndex
851
+ * @param {number} pGroupIndex
852
+ * @param {string} pManifestName - Key in ReferenceManifests
853
+ */
854
+ bindReferenceManifest(pSectionIndex, pGroupIndex, pManifestName)
855
+ {
856
+ let tmpManifest = this._resolveManifestData();
857
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
858
+ {
859
+ return;
860
+ }
861
+
862
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
863
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
864
+ {
865
+ return;
866
+ }
867
+
868
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
869
+ if (!tmpGroup)
870
+ {
871
+ return;
872
+ }
873
+
874
+ tmpGroup.RecordManifest = pManifestName;
875
+ this._ParentFormEditor.renderVisualEditor();
876
+ }
877
+
878
+ /**
879
+ * Unbind a ReferenceManifest from a group.
880
+ *
881
+ * @param {number} pSectionIndex
882
+ * @param {number} pGroupIndex
883
+ */
884
+ unbindReferenceManifest(pSectionIndex, pGroupIndex)
885
+ {
886
+ let tmpManifest = this._resolveManifestData();
887
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
888
+ {
889
+ return;
890
+ }
891
+
892
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
893
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
894
+ {
895
+ return;
896
+ }
897
+
898
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
899
+ if (!tmpGroup)
900
+ {
901
+ return;
902
+ }
903
+
904
+ delete tmpGroup.RecordManifest;
905
+ this._ParentFormEditor.renderVisualEditor();
906
+ }
907
+
908
+ /**
909
+ * Create a new ReferenceManifest and bind it to a group in one step.
910
+ *
911
+ * @param {number} pSectionIndex
912
+ * @param {number} pGroupIndex
913
+ */
914
+ createAndBindReferenceManifest(pSectionIndex, pGroupIndex)
915
+ {
916
+ let tmpManifest = this._resolveManifestData();
917
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
918
+ {
919
+ return;
920
+ }
921
+
922
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
923
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
924
+ {
925
+ return;
926
+ }
927
+
928
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
929
+ if (!tmpGroup)
930
+ {
931
+ return;
932
+ }
933
+
934
+ // Generate name based on group hash
935
+ let tmpBaseName = tmpGroup.Hash || ('Manifest_' + pSectionIndex + '_' + pGroupIndex);
936
+ let tmpKey = this.createReferenceManifest(tmpBaseName);
937
+
938
+ if (tmpKey)
939
+ {
940
+ tmpGroup.RecordManifest = tmpKey;
941
+ }
942
+
943
+ this._ParentFormEditor.renderVisualEditor();
944
+ }
945
+
946
+ /**
947
+ * Add a column (Descriptor) to the bound ReferenceManifest.
948
+ *
949
+ * @param {number} pSectionIndex
950
+ * @param {number} pGroupIndex
951
+ * @param {number} [pRow] - PictForm.Row value (defaults to 1)
952
+ */
953
+ addSubmanifestColumn(pSectionIndex, pGroupIndex, pRow)
954
+ {
955
+ let tmpManifest = this._resolveManifestData();
956
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
957
+ {
958
+ return;
959
+ }
960
+
961
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
962
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
963
+ {
964
+ return;
965
+ }
966
+
967
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
968
+ if (!tmpGroup || !tmpGroup.RecordManifest)
969
+ {
970
+ return;
971
+ }
972
+
973
+ let tmpRefManifest = this._resolveReferenceManifest(tmpGroup.RecordManifest);
974
+ if (!tmpRefManifest)
975
+ {
976
+ return;
977
+ }
978
+
979
+ if (!tmpRefManifest.Descriptors || typeof tmpRefManifest.Descriptors !== 'object')
980
+ {
981
+ tmpRefManifest.Descriptors = {};
982
+ }
983
+
984
+ let tmpRow = (typeof pRow === 'number' && pRow > 0) ? pRow : 1;
985
+
986
+ // Generate a unique column address
987
+ let tmpColumnNum = Object.keys(tmpRefManifest.Descriptors).length + 1;
988
+ let tmpAddress = 'Column_' + tmpColumnNum;
989
+ while (tmpRefManifest.Descriptors.hasOwnProperty(tmpAddress))
990
+ {
991
+ tmpColumnNum++;
992
+ tmpAddress = 'Column_' + tmpColumnNum;
993
+ }
994
+
995
+ let tmpColumnName = 'Column ' + tmpColumnNum;
996
+
997
+ tmpRefManifest.Descriptors[tmpAddress] =
998
+ {
999
+ Name: tmpColumnName,
1000
+ Hash: tmpAddress,
1001
+ DataType: 'String',
1002
+ PictForm:
1003
+ {
1004
+ Section: tmpSection.Hash || '',
1005
+ Group: tmpGroup.Hash || '',
1006
+ Row: tmpRow
1007
+ }
1008
+ };
1009
+
1010
+ this._ParentFormEditor.renderVisualEditor();
1011
+ }
1012
+
1013
+ /**
1014
+ * Remove a column (Descriptor) from the bound ReferenceManifest.
1015
+ *
1016
+ * @param {number} pSectionIndex
1017
+ * @param {number} pGroupIndex
1018
+ * @param {string} pColumnAddress - The Descriptor address key
1019
+ */
1020
+ removeSubmanifestColumn(pSectionIndex, pGroupIndex, pColumnAddress)
1021
+ {
1022
+ let tmpManifest = this._resolveManifestData();
1023
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
1024
+ {
1025
+ return;
1026
+ }
1027
+
1028
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
1029
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
1030
+ {
1031
+ return;
1032
+ }
1033
+
1034
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
1035
+ if (!tmpGroup || !tmpGroup.RecordManifest)
1036
+ {
1037
+ return;
1038
+ }
1039
+
1040
+ let tmpRefManifest = this._resolveReferenceManifest(tmpGroup.RecordManifest);
1041
+ if (!tmpRefManifest || !tmpRefManifest.Descriptors)
1042
+ {
1043
+ return;
1044
+ }
1045
+
1046
+ if (tmpRefManifest.Descriptors.hasOwnProperty(pColumnAddress))
1047
+ {
1048
+ delete tmpRefManifest.Descriptors[pColumnAddress];
1049
+ }
1050
+
1051
+ this._ParentFormEditor.renderVisualEditor();
1052
+ }
1053
+
1054
+ /**
1055
+ * Move a submanifest column up (earlier in key order).
1056
+ */
1057
+ moveSubmanifestColumnUp(pSectionIndex, pGroupIndex, pColumnAddress)
1058
+ {
1059
+ this._reorderSubmanifestColumn(pSectionIndex, pGroupIndex, pColumnAddress, -1);
1060
+ }
1061
+
1062
+ /**
1063
+ * Move a submanifest column down (later in key order).
1064
+ */
1065
+ moveSubmanifestColumnDown(pSectionIndex, pGroupIndex, pColumnAddress)
1066
+ {
1067
+ this._reorderSubmanifestColumn(pSectionIndex, pGroupIndex, pColumnAddress, 1);
1068
+ }
1069
+
1070
+ /**
1071
+ * Reorder a column within the submanifest Descriptors by rebuilding the object.
1072
+ *
1073
+ * @param {number} pSectionIndex
1074
+ * @param {number} pGroupIndex
1075
+ * @param {string} pColumnAddress
1076
+ * @param {number} pDirection - -1 for up, +1 for down
1077
+ */
1078
+ _reorderSubmanifestColumn(pSectionIndex, pGroupIndex, pColumnAddress, pDirection)
1079
+ {
1080
+ let tmpManifest = this._resolveManifestData();
1081
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
1082
+ {
1083
+ return;
1084
+ }
1085
+
1086
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
1087
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
1088
+ {
1089
+ return;
1090
+ }
1091
+
1092
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
1093
+ if (!tmpGroup || !tmpGroup.RecordManifest)
1094
+ {
1095
+ return;
1096
+ }
1097
+
1098
+ let tmpRefManifest = this._resolveReferenceManifest(tmpGroup.RecordManifest);
1099
+ if (!tmpRefManifest || !tmpRefManifest.Descriptors)
1100
+ {
1101
+ return;
1102
+ }
1103
+
1104
+ let tmpKeys = Object.keys(tmpRefManifest.Descriptors);
1105
+ let tmpIndex = tmpKeys.indexOf(pColumnAddress);
1106
+ if (tmpIndex < 0)
1107
+ {
1108
+ return;
1109
+ }
1110
+
1111
+ let tmpNewIndex = tmpIndex + pDirection;
1112
+ if (tmpNewIndex < 0 || tmpNewIndex >= tmpKeys.length)
1113
+ {
1114
+ return;
1115
+ }
1116
+
1117
+ // Swap keys
1118
+ let tmpSwap = tmpKeys[tmpNewIndex];
1119
+ tmpKeys[tmpNewIndex] = tmpKeys[tmpIndex];
1120
+ tmpKeys[tmpIndex] = tmpSwap;
1121
+
1122
+ // Rebuild the Descriptors object in new order
1123
+ let tmpNewDescriptors = {};
1124
+ for (let i = 0; i < tmpKeys.length; i++)
1125
+ {
1126
+ tmpNewDescriptors[tmpKeys[i]] = tmpRefManifest.Descriptors[tmpKeys[i]];
1127
+ }
1128
+ tmpRefManifest.Descriptors = tmpNewDescriptors;
1129
+
1130
+ this._ParentFormEditor.renderVisualEditor();
1131
+ }
1132
+
1133
+ /**
1134
+ * Group submanifest Descriptors by PictForm.Row.
1135
+ *
1136
+ * @param {object} pRefManifest - A ReferenceManifest object
1137
+ * @returns {Array} Array of { Row: number, Columns: [{ Address, Descriptor }, ...] }
1138
+ */
1139
+ _getSubmanifestRows(pRefManifest)
1140
+ {
1141
+ let tmpResult = [];
1142
+
1143
+ if (!pRefManifest || !pRefManifest.Descriptors || typeof pRefManifest.Descriptors !== 'object')
1144
+ {
1145
+ return tmpResult;
1146
+ }
1147
+
1148
+ let tmpRowMap = {};
1149
+ let tmpKeys = Object.keys(pRefManifest.Descriptors);
1150
+
1151
+ for (let i = 0; i < tmpKeys.length; i++)
1152
+ {
1153
+ let tmpDescriptor = pRefManifest.Descriptors[tmpKeys[i]];
1154
+ let tmpRow = 1;
1155
+ if (tmpDescriptor && tmpDescriptor.PictForm && tmpDescriptor.PictForm.Row)
1156
+ {
1157
+ tmpRow = parseInt(tmpDescriptor.PictForm.Row, 10);
1158
+ if (isNaN(tmpRow) || tmpRow < 1)
1159
+ {
1160
+ tmpRow = 1;
1161
+ }
1162
+ }
1163
+
1164
+ if (!tmpRowMap[tmpRow])
1165
+ {
1166
+ tmpRowMap[tmpRow] = [];
1167
+ }
1168
+ tmpRowMap[tmpRow].push({ Address: tmpKeys[i], Descriptor: tmpDescriptor });
1169
+ }
1170
+
1171
+ // Sort by row number
1172
+ let tmpRowNumbers = Object.keys(tmpRowMap).map(Number).sort(function(a, b) { return a - b; });
1173
+ for (let i = 0; i < tmpRowNumbers.length; i++)
1174
+ {
1175
+ tmpResult.push({ Row: tmpRowNumbers[i], Columns: tmpRowMap[tmpRowNumbers[i]] });
1176
+ }
1177
+
1178
+ return tmpResult;
1179
+ }
1180
+
1181
+ /**
1182
+ * Get the highest PictForm.Row value across all Descriptors in a ReferenceManifest.
1183
+ *
1184
+ * @param {object} pRefManifest
1185
+ * @returns {number}
1186
+ */
1187
+ _getSubmanifestMaxRow(pRefManifest)
1188
+ {
1189
+ if (!pRefManifest || !pRefManifest.Descriptors || typeof pRefManifest.Descriptors !== 'object')
1190
+ {
1191
+ return 0;
1192
+ }
1193
+
1194
+ let tmpMaxRow = 0;
1195
+ let tmpKeys = Object.keys(pRefManifest.Descriptors);
1196
+
1197
+ for (let i = 0; i < tmpKeys.length; i++)
1198
+ {
1199
+ let tmpDescriptor = pRefManifest.Descriptors[tmpKeys[i]];
1200
+ if (tmpDescriptor && tmpDescriptor.PictForm && tmpDescriptor.PictForm.Row)
1201
+ {
1202
+ let tmpRow = parseInt(tmpDescriptor.PictForm.Row, 10);
1203
+ if (!isNaN(tmpRow) && tmpRow > tmpMaxRow)
1204
+ {
1205
+ tmpMaxRow = tmpRow;
1206
+ }
1207
+ }
1208
+ }
1209
+
1210
+ return tmpMaxRow;
1211
+ }
1212
+
1213
+ /**
1214
+ * Add a new row to a RecordSet submanifest by creating a column in the next row.
1215
+ *
1216
+ * @param {number} pSectionIndex
1217
+ * @param {number} pGroupIndex
1218
+ */
1219
+ addSubmanifestRow(pSectionIndex, pGroupIndex)
1220
+ {
1221
+ let tmpManifest = this._resolveManifestData();
1222
+ if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
1223
+ {
1224
+ return;
1225
+ }
1226
+
1227
+ let tmpSection = tmpManifest.Sections[pSectionIndex];
1228
+ if (!tmpSection || !Array.isArray(tmpSection.Groups))
1229
+ {
1230
+ return;
1231
+ }
1232
+
1233
+ let tmpGroup = tmpSection.Groups[pGroupIndex];
1234
+ if (!tmpGroup || !tmpGroup.RecordManifest)
1235
+ {
1236
+ return;
1237
+ }
1238
+
1239
+ let tmpRefManifest = this._resolveReferenceManifest(tmpGroup.RecordManifest);
1240
+ if (!tmpRefManifest)
1241
+ {
1242
+ return;
1243
+ }
1244
+
1245
+ let tmpNextRow = this._getSubmanifestMaxRow(tmpRefManifest) + 1;
1246
+ this.addSubmanifestColumn(pSectionIndex, pGroupIndex, tmpNextRow);
1247
+ }
1248
+
1249
+ /**
1250
+ * Select a submanifest column (Tabular/RecordSet) for the properties panel.
1251
+ *
1252
+ * @param {number} pSectionIndex
1253
+ * @param {number} pGroupIndex
1254
+ * @param {string} pColumnAddress - The Descriptor address key
1255
+ */
1256
+ selectSubmanifestColumn(pSectionIndex, pGroupIndex, pColumnAddress)
1257
+ {
1258
+ // Clear Record input selection
1259
+ this._ParentFormEditor._SelectedInputIndices = null;
1260
+ // Also select the containing section and group
1261
+ this._ParentFormEditor._SelectedSectionIndex = pSectionIndex;
1262
+ this._ParentFormEditor._SelectedGroupIndices = { SectionIndex: pSectionIndex, GroupIndex: pGroupIndex };
1263
+
1264
+ this._ParentFormEditor._SelectedTabularColumn =
1265
+ {
1266
+ SectionIndex: pSectionIndex,
1267
+ GroupIndex: pGroupIndex,
1268
+ ColumnAddress: pColumnAddress
1269
+ };
1270
+
1271
+ if (this._ParentFormEditor._PropertiesPanelView)
1272
+ {
1273
+ this._ParentFormEditor._PropertiesPanelView.selectTabularColumn(pSectionIndex, pGroupIndex, pColumnAddress);
1274
+ }
1275
+
1276
+ // Auto-switch to properties tab and expand panel
1277
+ this._ParentFormEditor._PanelActiveTab = 'properties';
1278
+ if (this._ParentFormEditor._PanelCollapsed)
1279
+ {
1280
+ this._ParentFormEditor._PanelCollapsed = false;
1281
+ }
1282
+
1283
+ this._ParentFormEditor.renderVisualEditor();
1284
+
1285
+ // Align the properties panel with the selected column after layout settles
1286
+ let tmpSelf = this._ParentFormEditor;
1287
+ setTimeout(function () { tmpSelf._UtilitiesProvider._alignPanelToSelection(); }, 0);
1288
+ }
1289
+
1290
+ _resolveManifestData()
1291
+ {
1292
+ let tmpAddress = this._ParentFormEditor.options.ManifestDataAddress;
1293
+ if (!tmpAddress)
1294
+ {
1295
+ return null;
1296
+ }
1297
+
1298
+ let tmpSegments = tmpAddress.split('.');
1299
+ let tmpCurrent = this.fable;
1300
+
1301
+ for (let i = 0; i < tmpSegments.length; i++)
1302
+ {
1303
+ if (tmpCurrent && typeof tmpCurrent === 'object' && tmpCurrent.hasOwnProperty(tmpSegments[i]))
1304
+ {
1305
+ tmpCurrent = tmpCurrent[tmpSegments[i]];
1306
+ }
1307
+ else
1308
+ {
1309
+ return null;
1310
+ }
1311
+ }
1312
+
1313
+ return tmpCurrent;
1314
+ }
1315
+
1316
+ _setManifestData(pData)
1317
+ {
1318
+ let tmpAddress = this._ParentFormEditor.options.ManifestDataAddress;
1319
+ if (!tmpAddress)
1320
+ {
1321
+ return;
1322
+ }
1323
+
1324
+ let tmpSegments = tmpAddress.split('.');
1325
+ let tmpCurrent = this.fable;
1326
+
1327
+ for (let i = 0; i < tmpSegments.length - 1; i++)
1328
+ {
1329
+ if (!tmpCurrent.hasOwnProperty(tmpSegments[i]) || typeof tmpCurrent[tmpSegments[i]] !== 'object')
1330
+ {
1331
+ tmpCurrent[tmpSegments[i]] = {};
1332
+ }
1333
+ tmpCurrent = tmpCurrent[tmpSegments[i]];
1334
+ }
1335
+
1336
+ tmpCurrent[tmpSegments[tmpSegments.length - 1]] = pData;
1337
+ }
1338
+
1339
+ _createEmptyManifest()
1340
+ {
1341
+ return (
1342
+ {
1343
+ Scope: 'NewForm',
1344
+ Sections: [],
1345
+ Descriptors: {},
1346
+ ReferenceManifests: {},
1347
+ StaticOptionLists: [],
1348
+ PickLists: []
1349
+ });
1350
+ }
1351
+
1352
+ /**
1353
+ * Synchronize PictForm.Row indices for all inputs in a group's rows.
1354
+ *
1355
+ * Called after row reorder or removal so each Descriptor's PictForm.Row
1356
+ * value matches the input's actual position (1-based).
1357
+ *
1358
+ * @param {object} pManifest - The manifest data object
1359
+ * @param {object} pGroup - The group whose rows need syncing
1360
+ */
1361
+ _syncRowIndices(pManifest, pGroup)
1362
+ {
1363
+ if (!pGroup || !Array.isArray(pGroup.Rows) || !pManifest || !pManifest.Descriptors)
1364
+ {
1365
+ return;
1366
+ }
1367
+
1368
+ for (let i = 0; i < pGroup.Rows.length; i++)
1369
+ {
1370
+ let tmpRow = pGroup.Rows[i];
1371
+ if (!tmpRow || !Array.isArray(tmpRow.Inputs))
1372
+ {
1373
+ continue;
1374
+ }
1375
+
1376
+ for (let j = 0; j < tmpRow.Inputs.length; j++)
1377
+ {
1378
+ let tmpAddress = tmpRow.Inputs[j];
1379
+ if (typeof tmpAddress === 'string' && pManifest.Descriptors.hasOwnProperty(tmpAddress))
1380
+ {
1381
+ let tmpDescriptor = pManifest.Descriptors[tmpAddress];
1382
+ if (tmpDescriptor && tmpDescriptor.PictForm)
1383
+ {
1384
+ tmpDescriptor.PictForm.Row = i + 1;
1385
+ }
1386
+ }
1387
+ }
1388
+ }
1389
+ }
1390
+
1391
+ /**
1392
+ * Remove all Descriptor entries for inputs within a group.
1393
+ *
1394
+ * @param {object} pManifest - The manifest data object
1395
+ * @param {object} pGroup - The group being removed
1396
+ */
1397
+ _removeDescriptorsForGroup(pManifest, pGroup)
1398
+ {
1399
+ if (!pGroup || !Array.isArray(pGroup.Rows) || !pManifest || !pManifest.Descriptors)
1400
+ {
1401
+ return;
1402
+ }
1403
+
1404
+ for (let i = 0; i < pGroup.Rows.length; i++)
1405
+ {
1406
+ let tmpRow = pGroup.Rows[i];
1407
+ if (!tmpRow || !Array.isArray(tmpRow.Inputs))
1408
+ {
1409
+ continue;
1410
+ }
1411
+
1412
+ for (let j = 0; j < tmpRow.Inputs.length; j++)
1413
+ {
1414
+ let tmpAddress = tmpRow.Inputs[j];
1415
+ if (typeof tmpAddress === 'string' && pManifest.Descriptors.hasOwnProperty(tmpAddress))
1416
+ {
1417
+ delete pManifest.Descriptors[tmpAddress];
1418
+ }
1419
+ }
1420
+ }
1421
+ }
1422
+
1423
+ /**
1424
+ * Remove all Descriptor entries for inputs within a section.
1425
+ *
1426
+ * @param {object} pManifest - The manifest data object
1427
+ * @param {object} pSection - The section being removed
1428
+ */
1429
+ _removeDescriptorsForSection(pManifest, pSection)
1430
+ {
1431
+ if (!pSection || !Array.isArray(pSection.Groups) || !pManifest)
1432
+ {
1433
+ return;
1434
+ }
1435
+
1436
+ for (let i = 0; i < pSection.Groups.length; i++)
1437
+ {
1438
+ this._removeDescriptorsForGroup(pManifest, pSection.Groups[i]);
1439
+ }
1440
+ }
1441
+ }
1442
+
1443
+ module.exports = FormEditorManifestOps;