pict-section-form 1.0.10 → 1.0.13

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 (37) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/example_applications/Build-Examples.sh +41 -0
  3. package/example_applications/Clean-Examples.sh +10 -0
  4. package/example_applications/Open-Examples.sh +8 -0
  5. package/example_applications/README.md +57 -0
  6. package/example_applications/complex_table/Complex-Tabular-Application.js +219 -0
  7. package/example_applications/complex_table/FruitData.json +695 -0
  8. package/example_applications/complex_table/README-ComplexTable.md +31 -0
  9. package/example_applications/complex_table/html/index.html +13 -0
  10. package/example_applications/complex_table/package.json +26 -0
  11. package/example_applications/gradebook/source/Gradebook-Application.js +2 -1
  12. package/example_applications/gradebook/source/manifests/Assignment-Manifest.json +36 -1
  13. package/example_applications/gradebook/source/manifests/Gradebook-Manifest.js +20 -3
  14. package/example_applications/gradebook/source/manifests/Student-Manifest.json +34 -23
  15. package/example_applications/gradebook/source/views/BasicContent-View-Templates.json +42 -0
  16. package/example_applications/postcard_example/Pict-Application-Postcard.js +15 -0
  17. package/example_applications/postcard_example/providers/PictProvider-BestPostcardTheme.js +19 -46
  18. package/example_applications/postcard_example/providers/PictProvider-Dynamic-Sections-MockServerResponse.json +8 -4
  19. package/example_applications/simple_form/html/index.html +1 -1
  20. package/example_applications/simple_table/FruitData.json +693 -692
  21. package/example_applications/simple_table/Simple-Tabular-Application.js +6 -18
  22. package/example_applications/simple_table_from_object/FruitDataInAnObject.json +692 -2
  23. package/example_applications/simple_table_from_object/MigrateThatFruit.js +2 -2
  24. package/example_applications/simple_table_from_object/Simple-Tabular-Application-FromObject.js +5 -18
  25. package/package.json +3 -2
  26. package/source/Pict-Section-Form.js +6 -10
  27. package/source/{Pict-Section-Form-Application.js → application/Pict-Application-Form.js} +1 -1
  28. package/source/providers/Pict-Provider-DynamicSolver.js +264 -0
  29. package/source/{Pict-Section-Form-Provider-Templates-DefaultFormTemplates.js → providers/Pict-Provider-DynamicTemplates-DefaultFormTemplates.js} +88 -77
  30. package/source/{Pict-Section-Form-Provider-Templates.js → providers/Pict-Provider-DynamicTemplates.js} +4 -1
  31. package/source/{Pict-Service-Informary.js → providers/Pict-Provider-Informary.js} +13 -4
  32. package/source/templates/Pict-Template-Base.js +87 -0
  33. package/source/{Pict-Template-MetacontrollerValueSetWithGroup.js → templates/Pict-Template-Metacontroller-ValueSetWithGroup.js} +1 -1
  34. package/source/{Pict-Section-Form-View-DefaultConfiguration.json → views/Pict-View-DynamicForm-DefaultConfiguration.json} +1 -0
  35. package/source/{Pict-Section-Form-View.js → views/Pict-View-DynamicForm.js} +149 -38
  36. package/source/{Pict-Form-Metacontroller.js → views/Pict-View-Form-Metacontroller.js} +45 -15
  37. /package/source/{Pict-Section-Form-MetatemplateGenerator.js → providers/Pict-Provider-MetatemplateGenerator.js} +0 -0
@@ -4,9 +4,9 @@ let tmpFruityData = require('../simple_table/FruitData.json');
4
4
 
5
5
  let tmpFruityViceObjectMap = {};
6
6
 
7
- for (let i = 0; i < tmpFruityData.FruityVice.length; i++)
7
+ for (let i = 0; i < tmpFruityData.FruitData.FruityVice.length; i++)
8
8
  {
9
- let tmpFruit = tmpFruityData.FruityVice[i];
9
+ let tmpFruit = tmpFruityData.FruitData.FruityVice[i];
10
10
  tmpFruityKey = `${tmpFruit.name}${tmpFruit.family}${tmpFruit.order}${tmpFruit.genus}`;
11
11
  tmpFruityViceObjectMap[tmpFruityKey] = tmpFruit;
12
12
  }
@@ -1,27 +1,14 @@
1
1
  const libPictSectionForm = require('../../source/Pict-Section-Form.js');
2
2
 
3
- class FruityGrid extends libPictSectionForm.PictFormApplication
4
- {
5
- constructor(pFable, pOptions, pServiceHash)
6
- {
7
- super(pFable, pOptions, pServiceHash);
8
- }
9
-
10
- onBeforeInitialize()
11
- {
12
- this.log.trace(`Loading the fruitiest data ever!`);
13
- this.AppData.FruitData = require('./FruitDataInAnObject.json');
14
- this.log.trace(`... LOADED THE FRUIT!`);
15
- return super.onBeforeInitialize();
16
- }
17
- }
18
-
19
- module.exports = FruityGrid;
3
+ module.exports = libPictSectionForm.PictFormApplication;
20
4
 
21
5
  module.exports.default_configuration = libPictSectionForm.PictFormApplication.default_configuration;
22
6
  module.exports.default_configuration.pict_configuration = (
23
7
  {
24
8
  "Product": "SimpleTable",
9
+
10
+ "DefaultAppData": require('./FruitDataInAnObject.json'),
11
+
25
12
  "DefaultFormManifest":
26
13
  {
27
14
  "Scope": "SuperSimpleTabularForm",
@@ -36,7 +23,7 @@ module.exports.default_configuration.pict_configuration = (
36
23
  "Name": "FruitGrid",
37
24
 
38
25
  "Layout": "Tabular",
39
- "RecordSetAddress": "FruitData.FruityViceObject",
26
+ "RecordSetAddress": "FruityViceObject",
40
27
  "RecordManifest": "FruitEditor"
41
28
  }
42
29
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pict-section-form",
3
- "version": "1.0.10",
3
+ "version": "1.0.13",
4
4
  "description": "Pict dynamic form sections",
5
5
  "main": "source/Pict-Section-Form.js",
6
6
  "directories": {
@@ -26,11 +26,12 @@
26
26
  "devDependencies": {
27
27
  "browser-env": "^3.3.0",
28
28
  "jquery": "^3.7.1",
29
- "pict": "^1.0.195",
29
+ "pict": "^1.0.199",
30
30
  "pict-application": "^1.0.18",
31
31
  "quackage": "^1.0.30"
32
32
  },
33
33
  "dependencies": {
34
+ "fable-serviceproviderbase": "^3.0.13",
34
35
  "pict-provider": "^1.0.2",
35
36
  "pict-view": "^1.0.47"
36
37
  },
@@ -1,18 +1,14 @@
1
1
  // The container for all the Pict-Section-Form related code.
2
2
 
3
- // The main pict-section-form view class
4
- module.exports = require('./Pict-Section-Form-View.js');
5
- module.exports.default_configuration = require('./Pict-Section-Form-View-DefaultConfiguration.json');
3
+ // The main dynamic view class
4
+ module.exports = require('./views/Pict-View-DynamicForm.js');
5
+ module.exports.default_configuration = require('./views/Pict-View-DynamicForm-DefaultConfiguration.json');
6
6
 
7
7
  // The base provider class for form section templates; meant to be subclassed
8
- module.exports.PictFormTemplateProvider = require('./Pict-Section-Form-Provider-Templates.js');
9
-
10
- // The metatemplate generator provider
11
- // (if we decide to abstract this out from the view class)
12
- // module.exports.PictFormMetatemplateGenerator = require('./Pict-Section-Form-MetatemplateGenerator.js');
8
+ module.exports.PictFormTemplateProvider = require('./providers/Pict-Provider-DynamicTemplates.js');
13
9
 
14
10
  // The metacontroller view
15
- module.exports.PictFormMetacontroller = require('./Pict-Form-Metacontroller.js');
11
+ module.exports.PictFormMetacontroller = require('./views/Pict-View-Form-Metacontroller.js');
16
12
 
17
13
  // The application container
18
- module.exports.PictFormApplication = require('./Pict-Section-Form-Application.js');
14
+ module.exports.PictFormApplication = require('./application/Pict-Application-Form.js');
@@ -1,6 +1,6 @@
1
1
  const libPictApplication = require('pict-application');
2
2
 
3
- const libPictSectionForm = require('./Pict-Section-Form.js');
3
+ const libPictSectionForm = require('../Pict-Section-Form.js');
4
4
 
5
5
  class PictSectionFormApplication extends libPictApplication
6
6
  {
@@ -0,0 +1,264 @@
1
+ const libPictProvider = require('pict-provider');
2
+
3
+ const _DefaultProviderConfiguration = (
4
+ {
5
+ "ProviderIdentifier": "Pict-DynamicForm-Solve",
6
+
7
+ "AutoInitialize": true,
8
+ "AutoInitializeOrdinal": 0,
9
+
10
+ "AutoSolveWithApp": false
11
+ });
12
+
13
+ class PictSectionFormTemplateProvider extends libPictProvider
14
+ {
15
+ constructor(pFable, pOptions, pServiceHash)
16
+ {
17
+ let tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultProviderConfiguration)), pOptions);
18
+ super(pFable, tmpOptions, pServiceHash);
19
+ }
20
+
21
+ /**
22
+ * Checks the solver and returns the solver object if it passes the checks.
23
+ *
24
+ * Automatically converts string solvers to have an Ordinal of 1.
25
+ *
26
+ * @param {string|object} pSolver - The solver to be checked. It can be either a string or an object.
27
+ * @param {boolean} pFiltered - Indicates whether the solvers should be filtered.
28
+ * @param {number} pOrdinal - The ordinal value to compare with the solver's ordinal value when filtered.
29
+ * @returns {object|undefined} - The solver object if it passes the checks, otherwise undefined.
30
+ */
31
+ checkSolver(pSolver, pFiltered, pOrdinal)
32
+ {
33
+ let tmpSolver = pSolver;
34
+ if (tmpSolver === undefined)
35
+ {
36
+ console.log('wut');
37
+ return;
38
+ }
39
+ if (typeof(tmpSolver) === 'string')
40
+ {
41
+ tmpSolver = {Expression:tmpSolver, Ordinal:1};
42
+ }
43
+ if (!('Expression' in tmpSolver))
44
+ {
45
+ pView.log.error(`Dynamic View [${pView.UUID}]::[${pView.Hash}] group ${tmpGroup.Hash} solver ${k} is missing the Expression property.`);
46
+ return;
47
+ }
48
+ if (!(`Ordinal` in tmpSolver))
49
+ {
50
+ tmpSolver.Ordinal = 1;
51
+ }
52
+
53
+ // This filters the solvers
54
+ if (pFiltered && (tmpSolver.Ordinal != pOrdinal))
55
+ {
56
+ return;
57
+ }
58
+
59
+ return tmpSolver;
60
+ }
61
+
62
+ /**
63
+ * Runs each recordset solver formulae for a dynamic view group at a given ordinal.
64
+ * Or for all ordinals if no ordinal is passed.
65
+ *
66
+ * @param {array} pGroupSolverArray - An array of Solvers from the groups to solve.
67
+ * @param {number} pOrdinal - The ordinal value to filter to. Optional.
68
+ */
69
+ executeGroupSolvers(pGroupSolverArray, pOrdinal)
70
+ {
71
+ // This is purely for readability of the code below ... uglify optimizes it out.
72
+ let tmpFiltered = (typeof(pOrdinal) === 'undefined') ? false : true;
73
+
74
+ // Solve the group RecordSet solvers first
75
+ for (let j = 0; j < pGroupSolverArray.length; j++)
76
+ {
77
+ let tmpView = this.pict.views[pGroupSolverArray[j].ViewHash];
78
+ let tmpGroup = tmpView.getGroup(pGroupSolverArray[j].GroupIndex);
79
+ let tmpSolver = this.checkSolver(pGroupSolverArray[j].Solver, tmpFiltered, pOrdinal);
80
+ if (typeof(tmpSolver) === 'undefined')
81
+ {
82
+ continue;
83
+ }
84
+
85
+ tmpView.log.trace(`Dynamic View [${tmpView.UUID}]::[${tmpView.Hash}] solving RecordSet ordinal ${tmpSolver.Ordinal} [${tmpSolver.Expression}]`);
86
+
87
+ let tmpRecordSet = tmpView.getTabularRecordSet(j);
88
+
89
+ if (typeof(tmpRecordSet) == 'object')
90
+ {
91
+ let tmpRecordSetKeys = Object.keys(tmpRecordSet);
92
+ for (let l = 0; l < tmpRecordSetKeys.length; l++)
93
+ {
94
+ let tmpRecord = tmpRecordSet[tmpRecordSetKeys[l]];
95
+ let tmpResultsObject = {};
96
+ let tmpSolutionValue = tmpView.fable.ExpressionParser.solve(tmpSolver.Expression, tmpRecord, tmpResultsObject, tmpGroup.supportingManifest, tmpRecord);
97
+ tmpView.log.trace(`Group ${tmpGroup.Hash} [${tmpSolver.Expression}] record ${l} result was ${tmpSolutionValue}`);
98
+ }
99
+ }
100
+ if (typeof(tmpRecordSet) == 'array')
101
+ {
102
+ for (let l = 0; l < tmpRecordSet.length; l++)
103
+ {
104
+ let tmpRecord = tmpRecordSet[l];
105
+ let tmpResultsObject = {};
106
+ let tmpSolutionValue = tmpView.fable.ExpressionParser.solve(tmpSolver.Expression, tmpRecord, tmpResultsObject, tmpGroup.supportingManifest, tmpRecord);
107
+ tmpView.log.trace(`Group ${tmpGroup.Hash} [${tmpSolver.Expression}] record ${l} result was ${tmpSolutionValue}`);
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Executes the section solvers.
115
+ *
116
+ * @param {Array} pViewSectionSolverArray - The array of view section solvers.
117
+ * @param {number} pOrdinal - The ordinal value.
118
+ */
119
+ executeSectionSolvers(pViewSectionSolverArray, pOrdinal)
120
+ {
121
+ let tmpFiltered = (typeof(pOrdinal) === 'undefined') ? false : true;
122
+
123
+ for (let i = 0; i < pViewSectionSolverArray.length; i++)
124
+ {
125
+ let tmpView = this.pict.views[pViewSectionSolverArray[i].ViewHash];
126
+ let tmpSolver = this.checkSolver(pViewSectionSolverArray[i].Solver, tmpFiltered, pOrdinal);
127
+ if (typeof(tmpSolver) === 'undefined')
128
+ {
129
+ continue;
130
+ }
131
+
132
+ // TODO: Precompile the solvers (it's super easy)
133
+ tmpView.log.trace(`Dynamic View [${tmpView.UUID}]::[${tmpView.Hash}] solving equation ${i} ordinal ${tmpSolver.Ordinal} [${tmpView.options.Solvers[i]}]`);
134
+ let tmpResultsObject = {};
135
+ let tmpSolutionValue = tmpView.fable.ExpressionParser.solve(tmpSolver.Expression, tmpView.getMarshalDestinationObject(), tmpResultsObject, tmpView.sectionManifest, tmpView.getMarshalDestinationObject());
136
+ tmpView.log.trace(`[${tmpSolver.Expression}] result was ${tmpSolutionValue}`);
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Executes the view solvers for the given array of views.
142
+ *
143
+ * @param {Array} pViewSolverArray - The array of view solvers to execute.
144
+ * @param {number} pOrdinal - The ordinal value.
145
+ */
146
+ executeViewSolvers(pViewSolverArray, pOrdinal)
147
+ {
148
+ let tmpFiltered = (typeof(pOrdinal) === 'undefined') ? false : true;
149
+
150
+ for (let i = 0; i < pViewSolverArray.length; i++)
151
+ {
152
+ let tmpSolver = this.checkSolver(pViewSolverArray[i].Solver, tmpFiltered, pOrdinal);
153
+ if (typeof(tmpSolver) === 'undefined')
154
+ {
155
+ continue;
156
+ }
157
+ // Solve a normal view
158
+ tmpView.log.trace(`Dynamic View [${tmpView.UUID}]::[${tmpView.Hash}] running solve() on view [${pViewSolverArray[i].ViewHash}`);
159
+ let tmpView = this.pict.views[pViewSolverArray[i].ViewHash];
160
+ tmpView.solve();
161
+ }
162
+ }
163
+
164
+ checkAutoSolveOrdinal (pOrdinal, pOrdinalSet)
165
+ {
166
+ if (!(pOrdinal.toString() in pOrdinalSet))
167
+ {
168
+ pOrdinalSet[pOrdinal.toString()] = { ViewSolvers:[], SectionSolvers:[], GroupSolvers:[] };
169
+ }
170
+ return pOrdinalSet[pOrdinal];
171
+ }
172
+
173
+ /**
174
+ * Solves the views based on the provided view hashes or all views in pict.
175
+ *
176
+ * If non-dynamic views are also passed in, they are solved as well.
177
+ *
178
+ * This algorithm is particularly complex because it solves views in
179
+ * order across two dimensions:
180
+ *
181
+ * 1. The order of the views in the view hash array.
182
+ * 2. Precedence order
183
+ *
184
+ * The way it manages the precedence order solving is by enumerating the
185
+ * view hash array multiple times until it exhausts the solution set.
186
+ *
187
+ * In dynamic views, when there are collisions in precedence order between
188
+ * Section Solvers and Group RecordSet Solvers, it prefers the RecordSet
189
+ * solvers first. The thinking behind this is that a RecordSet solver is
190
+ * a "tier down" from the core Section it resides within. These are
191
+ * leaves on the tree.
192
+
193
+ * @param {Array|string[]} [pViewHashes] - An optional array of view hashes to solve. If not provided, all views in the fable will be solved.
194
+ */
195
+ solveViews(pViewHashes)
196
+ {
197
+ this.log.trace(`Dynamic View Provider [${this.UUID}]::[${this.Hash}] solving views.`);
198
+ let tmpViewHashes = Array.isArray(pViewHashes) ? pViewHashes : Object.keys(this.fable.views);
199
+
200
+ let tmpOrdinalsToSolve = {};
201
+
202
+ for (let i = 0; i < tmpViewHashes.length; i++)
203
+ {
204
+ let tmpView = this.fable.views[tmpViewHashes[i]];
205
+ if (!tmpView.isPictSectionForm && !tmpView.isPictMetacontroller)
206
+ {
207
+ // This is just a normal view. We will solve it at the appropriate ordinal.
208
+ let tmpOrdinalContainer = this.checkAutoSolveOrdinal(tmpView.options.AutoSolveOrdinal, tmpOrdinalsToSolve);
209
+ tmpOrdinalContainer.ViewSolvers.push({ViewHash:tmpViewHashes[i]});
210
+ }
211
+ else if (tmpView.isPictSectionForm)
212
+ {
213
+ // These guards are here because the metacontroller view masquerades as a section form view but isn't one.
214
+ for (let j = 0; j < tmpView.sectionDefinition.Groups.length; j++)
215
+ {
216
+ let tmpGroup = tmpView.getGroup(j);
217
+ if (`RecordSetSolvers` in tmpGroup)
218
+ {
219
+ for (let k = 0; k < tmpGroup.RecordSetSolvers.length; k++)
220
+ {
221
+ let tmpSolver = this.checkSolver(tmpGroup.RecordSetSolvers[k]);
222
+ if (tmpSolver)
223
+ {
224
+ let tmpOrdinalContainer = this.checkAutoSolveOrdinal(tmpSolver.Ordinal, tmpOrdinalsToSolve);
225
+ tmpOrdinalContainer.GroupSolvers.push({ViewHash:tmpViewHashes[i], GroupIndex:j, Solver:tmpSolver});
226
+ }
227
+ }
228
+ }
229
+ }
230
+ if (Array.isArray(tmpView.options.Solvers))
231
+ {
232
+ // Add thje section solver(s)
233
+ for (let j = 0; j < tmpView.options.Solvers.length; j++)
234
+ {
235
+ let tmpSolver = this.checkSolver(tmpView.options.Solvers[j]);
236
+ if (tmpSolver)
237
+ {
238
+ let tmpOrdinalContainer = this.checkAutoSolveOrdinal(tmpSolver.Ordinal, tmpOrdinalsToSolve);
239
+ tmpOrdinalContainer.SectionSolvers.push({ViewHash:tmpViewHashes[i], Solver:tmpSolver});
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ // Now sort the ordinal container keys
247
+ let tmpOrdinalKeys = Object.keys(tmpOrdinalsToSolve);
248
+ tmpOrdinalKeys.sort();
249
+
250
+ // Now enumerate the keys and solve each layer of the solution set
251
+ for (let i = 0; i < tmpOrdinalKeys.length; i++)
252
+ {
253
+ let tmpOrdinalContainer = tmpOrdinalsToSolve[tmpOrdinalKeys[i]];
254
+ this.executeGroupSolvers(tmpOrdinalContainer.GroupSolvers, tmpOrdinalKeys[i]);
255
+ this.executeSectionSolvers(tmpOrdinalContainer.SectionSolvers, tmpOrdinalKeys[i]);
256
+ this.executeViewSolvers(tmpOrdinalContainer.ViewSolvers, tmpOrdinalKeys[i]);
257
+ }
258
+
259
+ console.log(tmpOrdinalsToSolve);
260
+ }
261
+ }
262
+
263
+ module.exports = PictSectionFormTemplateProvider;
264
+ module.exports.default_configuration = _DefaultProviderConfiguration;