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.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +162 -0
- package/docs/_sidebar.md +23 -0
- package/docs/_topbar.md +5 -0
- package/docs/cover.md +12 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/index.html +39 -0
- package/docs/retold-catalog.json +224 -0
- package/docs/retold-keyword-index.json +46846 -0
- package/example_applications/form_editor/.quackage.json +10 -0
- package/example_applications/form_editor/FormEditor-Example-Application.js +226 -0
- package/example_applications/form_editor/html/icon-chooser.html +375 -0
- package/example_applications/form_editor/html/index.html +54 -0
- package/example_applications/form_editor/package.json +50 -0
- package/package.json +55 -0
- package/sample_manifests/Complex-Table.json +974 -0
- package/sample_manifests/Distill-Example.json +200 -0
- package/sample_manifests/Gradebook-Assignment.json +38 -0
- package/sample_manifests/Gradebook-Student.json +40 -0
- package/sample_manifests/Manyfest-Editor.json +347 -0
- package/sample_manifests/Simple-Form.json +232 -0
- package/sample_manifests/Simple-Table.json +79 -0
- package/source/Pict-Section-FormEditor-DefaultConfiguration.js +3321 -0
- package/source/Pict-Section-FormEditor.js +35 -0
- package/source/providers/Pict-Provider-ChildPictManager-Application.js +40 -0
- package/source/providers/Pict-Provider-ChildPictManager.js +238 -0
- package/source/providers/Pict-Provider-FormEditorDocumentation.js +356 -0
- package/source/providers/Pict-Provider-FormEditorDragDrop.js +535 -0
- package/source/providers/Pict-Provider-FormEditorIconography.js +1002 -0
- package/source/providers/Pict-Provider-FormEditorManifestOps.js +1443 -0
- package/source/providers/Pict-Provider-FormEditorRendering.js +730 -0
- package/source/providers/Pict-Provider-FormEditorUtilities.js +862 -0
- package/source/providers/Pict-Provider-PreviewCSS.js +42 -0
- package/source/views/PictView-FormEditor-InlineEditing.js +309 -0
- package/source/views/PictView-FormEditor-InputTypePicker.js +532 -0
- package/source/views/PictView-FormEditor-PropertiesPanel.js +7730 -0
- package/source/views/PictView-FormEditor.js +681 -0
- package/test/Pict-Section-FormEditor_tests.js +4102 -0
- package/user-documentation/.pict_documentation_topics.json +695 -0
- package/user-documentation/Getting-Started.md +32 -0
- package/user-documentation/Groups.md +52 -0
- package/user-documentation/Inputs.md +98 -0
- package/user-documentation/Sections.md +36 -0
- package/user-documentation/Shortcuts.md +44 -0
- package/user-documentation/Solver-Expression-Walkthrough.md +176 -0
- package/user-documentation/Solver-Expressions-Advanced.md +344 -0
- package/user-documentation/Solver-Functions.md +213 -0
- package/user-documentation/Solvers.md +81 -0
- package/user-documentation/ToC.md +18 -0
- package/user-documentation/solverfunctions/abs.md +84 -0
- package/user-documentation/solverfunctions/aggregationhistogram.md +83 -0
- package/user-documentation/solverfunctions/aggregationhistogrambyobject.md +64 -0
- package/user-documentation/solverfunctions/arrayconcat.md +64 -0
- package/user-documentation/solverfunctions/avg.md +81 -0
- package/user-documentation/solverfunctions/bucketset.md +69 -0
- package/user-documentation/solverfunctions/ceil.md +70 -0
- package/user-documentation/solverfunctions/cleanvaluearray.md +66 -0
- package/user-documentation/solverfunctions/cleanvalueobject.md +68 -0
- package/user-documentation/solverfunctions/colorgroupbackground.md +60 -0
- package/user-documentation/solverfunctions/colorinputbackground.md +62 -0
- package/user-documentation/solverfunctions/colorinputbackgroundtabular.md +64 -0
- package/user-documentation/solverfunctions/colorsectionbackground.md +59 -0
- package/user-documentation/solverfunctions/compare.md +72 -0
- package/user-documentation/solverfunctions/concat.md +73 -0
- package/user-documentation/solverfunctions/concatraw.md +73 -0
- package/user-documentation/solverfunctions/cos.md +75 -0
- package/user-documentation/solverfunctions/count.md +73 -0
- package/user-documentation/solverfunctions/countset.md +65 -0
- package/user-documentation/solverfunctions/countsetelements.md +63 -0
- package/user-documentation/solverfunctions/createarrayfromabsolutevalues.md +63 -0
- package/user-documentation/solverfunctions/createvalueobjectbyhashes.md +69 -0
- package/user-documentation/solverfunctions/cumulativesummation.md +96 -0
- package/user-documentation/solverfunctions/dateadddays.md +79 -0
- package/user-documentation/solverfunctions/dateaddhours.md +74 -0
- package/user-documentation/solverfunctions/dateaddmilliseconds.md +65 -0
- package/user-documentation/solverfunctions/dateaddminutes.md +72 -0
- package/user-documentation/solverfunctions/dateaddmonths.md +74 -0
- package/user-documentation/solverfunctions/dateaddseconds.md +66 -0
- package/user-documentation/solverfunctions/dateaddweeks.md +73 -0
- package/user-documentation/solverfunctions/dateaddyears.md +74 -0
- package/user-documentation/solverfunctions/datedaydifference.md +84 -0
- package/user-documentation/solverfunctions/datefromparts.md +81 -0
- package/user-documentation/solverfunctions/datehourdifference.md +64 -0
- package/user-documentation/solverfunctions/datemathadd.md +72 -0
- package/user-documentation/solverfunctions/datemilliseconddifference.md +64 -0
- package/user-documentation/solverfunctions/dateminutedifference.md +64 -0
- package/user-documentation/solverfunctions/datemonthdifference.md +66 -0
- package/user-documentation/solverfunctions/dateseconddifference.md +64 -0
- package/user-documentation/solverfunctions/dateweekdifference.md +65 -0
- package/user-documentation/solverfunctions/dateyeardifference.md +64 -0
- package/user-documentation/solverfunctions/differencearrays.md +59 -0
- package/user-documentation/solverfunctions/disablesolverordinal.md +58 -0
- package/user-documentation/solverfunctions/distributionhistogram.md +96 -0
- package/user-documentation/solverfunctions/distributionhistogrambyobject.md +64 -0
- package/user-documentation/solverfunctions/enablesolverordinal.md +57 -0
- package/user-documentation/solverfunctions/entryinset.md +72 -0
- package/user-documentation/solverfunctions/euler.md +77 -0
- package/user-documentation/solverfunctions/exp.md +74 -0
- package/user-documentation/solverfunctions/findfirstvaluebyexactmatch.md +67 -0
- package/user-documentation/solverfunctions/findfirstvaluebystringincludes.md +67 -0
- package/user-documentation/solverfunctions/flatten.md +76 -0
- package/user-documentation/solverfunctions/floor.md +70 -0
- package/user-documentation/solverfunctions/gaussianelimination.md +75 -0
- package/user-documentation/solverfunctions/generatearrayofobjectsfromsets.md +70 -0
- package/user-documentation/solverfunctions/generatehtmlhexcolor.md +67 -0
- package/user-documentation/solverfunctions/getvalue.md +90 -0
- package/user-documentation/solverfunctions/getvaluearray.md +64 -0
- package/user-documentation/solverfunctions/getvalueobject.md +67 -0
- package/user-documentation/solverfunctions/hidesections.md +58 -0
- package/user-documentation/solverfunctions/if.md +109 -0
- package/user-documentation/solverfunctions/iterativeseries.md +107 -0
- package/user-documentation/solverfunctions/join.md +75 -0
- package/user-documentation/solverfunctions/joinraw.md +64 -0
- package/user-documentation/solverfunctions/largestinset.md +63 -0
- package/user-documentation/solverfunctions/leastsquares.md +66 -0
- package/user-documentation/solverfunctions/linest.md +58 -0
- package/user-documentation/solverfunctions/log.md +74 -0
- package/user-documentation/solverfunctions/logvalues.md +65 -0
- package/user-documentation/solverfunctions/match.md +71 -0
- package/user-documentation/solverfunctions/matrixinverse.md +67 -0
- package/user-documentation/solverfunctions/matrixmultiply.md +71 -0
- package/user-documentation/solverfunctions/matrixtranspose.md +72 -0
- package/user-documentation/solverfunctions/matrixvectormultiply.md +69 -0
- package/user-documentation/solverfunctions/max.md +73 -0
- package/user-documentation/solverfunctions/mean.md +63 -0
- package/user-documentation/solverfunctions/median.md +79 -0
- package/user-documentation/solverfunctions/min.md +73 -0
- package/user-documentation/solverfunctions/mode.md +66 -0
- package/user-documentation/solverfunctions/objectkeystoarray.md +66 -0
- package/user-documentation/solverfunctions/objectvaluessortbyexternalobjectarray.md +65 -0
- package/user-documentation/solverfunctions/objectvaluestoarray.md +67 -0
- package/user-documentation/solverfunctions/percent.md +75 -0
- package/user-documentation/solverfunctions/pi.md +77 -0
- package/user-documentation/solverfunctions/polynomialregression.md +69 -0
- package/user-documentation/solverfunctions/predict.md +71 -0
- package/user-documentation/solverfunctions/rad.md +85 -0
- package/user-documentation/solverfunctions/randomfloat.md +63 -0
- package/user-documentation/solverfunctions/randomfloatbetween.md +72 -0
- package/user-documentation/solverfunctions/randomfloatupto.md +65 -0
- package/user-documentation/solverfunctions/randominteger.md +56 -0
- package/user-documentation/solverfunctions/randomintegerbetween.md +72 -0
- package/user-documentation/solverfunctions/randomintegerupto.md +64 -0
- package/user-documentation/solverfunctions/refreshtabularsection.md +57 -0
- package/user-documentation/solverfunctions/resolvehtmlentities.md +64 -0
- package/user-documentation/solverfunctions/round.md +111 -0
- package/user-documentation/solverfunctions/runsolvers.md +49 -0
- package/user-documentation/solverfunctions/setconcatenate.md +64 -0
- package/user-documentation/solverfunctions/setgroupvisibility.md +60 -0
- package/user-documentation/solverfunctions/setsectionvisibility.md +59 -0
- package/user-documentation/solverfunctions/setsolverordinalenabled.md +59 -0
- package/user-documentation/solverfunctions/settabularrowlength.md +57 -0
- package/user-documentation/solverfunctions/setvalue.md +65 -0
- package/user-documentation/solverfunctions/showsections.md +58 -0
- package/user-documentation/solverfunctions/sin.md +83 -0
- package/user-documentation/solverfunctions/slice.md +80 -0
- package/user-documentation/solverfunctions/smallestinset.md +63 -0
- package/user-documentation/solverfunctions/sortarray.md +58 -0
- package/user-documentation/solverfunctions/sorthistogram.md +70 -0
- package/user-documentation/solverfunctions/sorthistogrambykeys.md +69 -0
- package/user-documentation/solverfunctions/sortset.md +75 -0
- package/user-documentation/solverfunctions/sqrt.md +85 -0
- package/user-documentation/solverfunctions/stdev.md +81 -0
- package/user-documentation/solverfunctions/stdeva.md +58 -0
- package/user-documentation/solverfunctions/stdevp.md +83 -0
- package/user-documentation/solverfunctions/stringcountsegments.md +66 -0
- package/user-documentation/solverfunctions/stringgetsegments.md +74 -0
- package/user-documentation/solverfunctions/subtractingsummation.md +66 -0
- package/user-documentation/solverfunctions/sum.md +78 -0
- package/user-documentation/solverfunctions/tan.md +78 -0
- package/user-documentation/solverfunctions/tofixed.md +75 -0
- package/user-documentation/solverfunctions/unionarrays.md +59 -0
- package/user-documentation/solverfunctions/uniquearray.md +58 -0
- package/user-documentation/solverfunctions/var.md +67 -0
- package/user-documentation/solverfunctions/vara.md +58 -0
- package/user-documentation/solverfunctions/varp.md +66 -0
- package/user-documentation/solverfunctions/when.md +98 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Pict Section: Form Editor
|
|
2
|
+
// A visual editor for pict-section-form configuration manifests.
|
|
3
|
+
|
|
4
|
+
// The main form editor view class
|
|
5
|
+
module.exports = require('./views/PictView-FormEditor.js');
|
|
6
|
+
|
|
7
|
+
// Default configuration
|
|
8
|
+
module.exports.default_configuration = require('./Pict-Section-FormEditor-DefaultConfiguration.js');
|
|
9
|
+
|
|
10
|
+
// Iconography provider for SVG icons
|
|
11
|
+
module.exports.IconographyProvider = require('./providers/Pict-Provider-FormEditorIconography.js');
|
|
12
|
+
|
|
13
|
+
// Rendering provider for visual editor HTML generation
|
|
14
|
+
module.exports.RenderingProvider = require('./providers/Pict-Provider-FormEditorRendering.js');
|
|
15
|
+
|
|
16
|
+
// Manifest operations provider for CRUD operations
|
|
17
|
+
module.exports.ManifestOpsProvider = require('./providers/Pict-Provider-FormEditorManifestOps.js');
|
|
18
|
+
|
|
19
|
+
// Drag-and-drop provider
|
|
20
|
+
module.exports.DragDropProvider = require('./providers/Pict-Provider-FormEditorDragDrop.js');
|
|
21
|
+
|
|
22
|
+
// Utilities provider for string helpers, InputType definitions, selection, and stats
|
|
23
|
+
module.exports.UtilitiesProvider = require('./providers/Pict-Provider-FormEditorUtilities.js');
|
|
24
|
+
|
|
25
|
+
// Properties panel view
|
|
26
|
+
module.exports.PropertiesPanel = require('./views/PictView-FormEditor-PropertiesPanel.js');
|
|
27
|
+
|
|
28
|
+
// Inline editing view
|
|
29
|
+
module.exports.InlineEditing = require('./views/PictView-FormEditor-InlineEditing.js');
|
|
30
|
+
|
|
31
|
+
// Input type picker view
|
|
32
|
+
module.exports.InputTypePicker = require('./views/PictView-FormEditor-InputTypePicker.js');
|
|
33
|
+
|
|
34
|
+
// Documentation provider for embedded help system
|
|
35
|
+
module.exports.DocumentationProvider = require('./providers/Pict-Provider-FormEditorDocumentation.js');
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const libPictSectionForm = require('pict-section-form');
|
|
2
|
+
|
|
3
|
+
class ChildPictApplication extends libPictSectionForm.PictFormApplication
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, pOptions, pServiceHash);
|
|
8
|
+
|
|
9
|
+
// Trying this pattern -- it seems to make the most sense.
|
|
10
|
+
// MainViewportViewIdentifier: 'Default-View',
|
|
11
|
+
// MainViewportRenderableHash: false,
|
|
12
|
+
// MainViewportDestinationAddress: false,
|
|
13
|
+
// MainViewportDefaultDataAddress: false,
|
|
14
|
+
|
|
15
|
+
this.options.AutoSolveAfterInitialize = false;
|
|
16
|
+
this.options.AutoRenderMainViewportViewAfterInitialize = false;
|
|
17
|
+
this.options.AutoRenderViewsAfterInitialize = false;
|
|
18
|
+
this.options.AutoLoginAfterInitialize = false;
|
|
19
|
+
this.options.AutoLoadDataAfterLogin = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onBeforeInitialize()
|
|
23
|
+
{
|
|
24
|
+
this.log.trace(`Initializing embedded application.`);
|
|
25
|
+
return super.onBeforeInitialize();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
onAfterInitialize()
|
|
29
|
+
{
|
|
30
|
+
this.log.trace(`Finished initializing embedded application.`);
|
|
31
|
+
return super.onAfterInitialize();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
onAfterRender()
|
|
35
|
+
{
|
|
36
|
+
return super.onAfterRender();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = ChildPictApplication;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
const libPictProvider = require('pict-provider');
|
|
2
|
+
|
|
3
|
+
const libPict = require('pict');
|
|
4
|
+
const libPictCustomApplication = require('./Pict-Provider-ChildPictManager-Application.js');
|
|
5
|
+
|
|
6
|
+
class ChildPictManager extends libPictProvider
|
|
7
|
+
{
|
|
8
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
9
|
+
{
|
|
10
|
+
super(pFable, pOptions, pServiceHash);
|
|
11
|
+
|
|
12
|
+
this.serviceType = 'PictProvider';
|
|
13
|
+
|
|
14
|
+
// The cache location for other instances of pict
|
|
15
|
+
this._PictCache = {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Check if a pict instance exists for this cache
|
|
19
|
+
childApplicationExists(pFormHash)
|
|
20
|
+
{
|
|
21
|
+
const tmpFormHash = this.fable.DataFormat.sanitizeObjectKey(pFormHash);
|
|
22
|
+
|
|
23
|
+
if (this._PictCache[tmpFormHash])
|
|
24
|
+
{
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
childApplication(pFormHash)
|
|
32
|
+
{
|
|
33
|
+
const tmpFormHash = this.fable.DataFormat.sanitizeObjectKey(pFormHash);
|
|
34
|
+
|
|
35
|
+
if (this.childApplicationExists(tmpFormHash))
|
|
36
|
+
{
|
|
37
|
+
return this._PictCache[tmpFormHash];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Destroy and remove a cached child application.
|
|
45
|
+
* Cleans up the child pict's views, providers, and application so
|
|
46
|
+
* nothing leaks between successive preview loads.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} pFormHash - The form hash key for the cached application
|
|
49
|
+
*/
|
|
50
|
+
destroyChildApplication(pFormHash)
|
|
51
|
+
{
|
|
52
|
+
const tmpFormHash = this.fable.DataFormat.sanitizeObjectKey(pFormHash);
|
|
53
|
+
|
|
54
|
+
let tmpChildPict = this._PictCache[tmpFormHash];
|
|
55
|
+
if (!tmpChildPict)
|
|
56
|
+
{
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Tear down all views registered on the child pict
|
|
61
|
+
let tmpViewKeys = Object.keys(tmpChildPict.views);
|
|
62
|
+
for (let i = 0; i < tmpViewKeys.length; i++)
|
|
63
|
+
{
|
|
64
|
+
try
|
|
65
|
+
{
|
|
66
|
+
let tmpView = tmpChildPict.views[tmpViewKeys[i]];
|
|
67
|
+
if (tmpView && typeof tmpView.destroy === 'function')
|
|
68
|
+
{
|
|
69
|
+
tmpView.destroy();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (pError)
|
|
73
|
+
{
|
|
74
|
+
this.log.warn('Error destroying child pict view [' + tmpViewKeys[i] + ']: ' + pError);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Remove the application reference
|
|
79
|
+
if (tmpChildPict.PictApplication)
|
|
80
|
+
{
|
|
81
|
+
try
|
|
82
|
+
{
|
|
83
|
+
if (typeof tmpChildPict.PictApplication.destroy === 'function')
|
|
84
|
+
{
|
|
85
|
+
tmpChildPict.PictApplication.destroy();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (pError)
|
|
89
|
+
{
|
|
90
|
+
this.log.warn('Error destroying child pict application: ' + pError);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
delete this._PictCache[tmpFormHash];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Initialize a new child application
|
|
98
|
+
initializeChildApplication(pFormHash, pPictSectionFormManifest)
|
|
99
|
+
{
|
|
100
|
+
const tmpFormHash = this.fable.DataFormat.sanitizeObjectKey(pFormHash);
|
|
101
|
+
|
|
102
|
+
try
|
|
103
|
+
{
|
|
104
|
+
// Construct a new pict instance
|
|
105
|
+
const tmpChildPictSettings =
|
|
106
|
+
{
|
|
107
|
+
Product: `Form-${tmpFormHash}`,
|
|
108
|
+
ProductVersion: '1.0.0',
|
|
109
|
+
DefaultFormManifest: pPictSectionFormManifest
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
let tmpChildPict = new libPict(tmpChildPictSettings);
|
|
113
|
+
|
|
114
|
+
this._PictCache[tmpFormHash] = tmpChildPict;
|
|
115
|
+
|
|
116
|
+
tmpChildPict.addApplication(tmpFormHash, {}, libPictCustomApplication);
|
|
117
|
+
|
|
118
|
+
tmpChildPict.PictApplication.initializeAsync(
|
|
119
|
+
function (pError)
|
|
120
|
+
{
|
|
121
|
+
if (pError)
|
|
122
|
+
{
|
|
123
|
+
console.log('Error initializing the pict application: ' + pError);
|
|
124
|
+
}
|
|
125
|
+
tmpChildPict.log.info('Loading the Application and associated views.');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// NOTICE: This application is initializing async
|
|
129
|
+
return tmpChildPict;
|
|
130
|
+
}
|
|
131
|
+
catch (pError)
|
|
132
|
+
{
|
|
133
|
+
this.log.error(`Error initializing child pict application for form ${pFormHash}: ${pError}`);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Initialize a child application configured for browser rendering.
|
|
140
|
+
* Sets BrowserAddress so the child pict is available on the window
|
|
141
|
+
* and {~P~} template expressions resolve correctly.
|
|
142
|
+
*
|
|
143
|
+
* @param {string} pFormHash - Cache key for the child application
|
|
144
|
+
* @param {Object} pPictSectionFormManifest - The form manifest (DefaultFormManifest)
|
|
145
|
+
* @param {string} pBrowserAddress - The global window address (e.g. 'window._ChildPict')
|
|
146
|
+
* @param {string} pDestinationSelector - CSS selector for the render target (e.g. '#Preview-Container')
|
|
147
|
+
* @param {Function} fCallback - Called with (pError, pChildPict) when initialization completes
|
|
148
|
+
*/
|
|
149
|
+
initializeRenderableChildApplication(pFormHash, pPictSectionFormManifest, pBrowserAddress, pDestinationSelector, fCallback)
|
|
150
|
+
{
|
|
151
|
+
const tmpFormHash = this.fable.DataFormat.sanitizeObjectKey(pFormHash);
|
|
152
|
+
|
|
153
|
+
try
|
|
154
|
+
{
|
|
155
|
+
// Destroy any previous instance with this hash
|
|
156
|
+
this.destroyChildApplication(tmpFormHash);
|
|
157
|
+
|
|
158
|
+
// Clean up previous global reference if it exists
|
|
159
|
+
if (typeof window !== 'undefined' && pBrowserAddress)
|
|
160
|
+
{
|
|
161
|
+
let tmpAddressParts = pBrowserAddress.split('.');
|
|
162
|
+
if (tmpAddressParts.length === 2 && tmpAddressParts[0] === 'window')
|
|
163
|
+
{
|
|
164
|
+
delete window[tmpAddressParts[1]];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Construct a new pict instance with BrowserAddress
|
|
169
|
+
const tmpChildPictSettings =
|
|
170
|
+
{
|
|
171
|
+
Product: `FormPreview-${tmpFormHash}`,
|
|
172
|
+
ProductVersion: '1.0.0',
|
|
173
|
+
BrowserAddress: pBrowserAddress || 'window._ChildPict',
|
|
174
|
+
DefaultFormManifest: pPictSectionFormManifest
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
let tmpChildPict = new libPict(tmpChildPictSettings);
|
|
178
|
+
|
|
179
|
+
// Disable CSS injection on the child pict so it does not
|
|
180
|
+
// overwrite the parent's #PICT-CSS style element.
|
|
181
|
+
// The parent already has all pict-section-form CSS loaded.
|
|
182
|
+
if (tmpChildPict.CSSMap)
|
|
183
|
+
{
|
|
184
|
+
tmpChildPict.CSSMap.injectCSS = function() {};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this._PictCache[tmpFormHash] = tmpChildPict;
|
|
188
|
+
|
|
189
|
+
// Set the global reference so {~P~} expressions resolve
|
|
190
|
+
if (typeof window !== 'undefined' && pBrowserAddress)
|
|
191
|
+
{
|
|
192
|
+
let tmpAddressParts = pBrowserAddress.split('.');
|
|
193
|
+
if (tmpAddressParts.length === 2 && tmpAddressParts[0] === 'window')
|
|
194
|
+
{
|
|
195
|
+
window[tmpAddressParts[1]] = tmpChildPict;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Configure the application to render into the target container
|
|
200
|
+
let tmpAppOptions =
|
|
201
|
+
{
|
|
202
|
+
MainViewportDestinationAddress: pDestinationSelector,
|
|
203
|
+
AutoPopulateAfterRender: true
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
tmpChildPict.addApplication(tmpFormHash, tmpAppOptions, libPictCustomApplication);
|
|
207
|
+
|
|
208
|
+
let tmpSelf = this;
|
|
209
|
+
tmpChildPict.PictApplication.initializeAsync(
|
|
210
|
+
function (pError)
|
|
211
|
+
{
|
|
212
|
+
if (pError)
|
|
213
|
+
{
|
|
214
|
+
tmpSelf.log.error('Error initializing renderable child pict application: ' + pError);
|
|
215
|
+
}
|
|
216
|
+
if (typeof fCallback === 'function')
|
|
217
|
+
{
|
|
218
|
+
fCallback(pError, tmpChildPict);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return tmpChildPict;
|
|
223
|
+
}
|
|
224
|
+
catch (pError)
|
|
225
|
+
{
|
|
226
|
+
this.log.error(`Error initializing renderable child pict application for form ${pFormHash}: ${pError}`);
|
|
227
|
+
if (typeof fCallback === 'function')
|
|
228
|
+
{
|
|
229
|
+
fCallback(pError, null);
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
module.exports = ChildPictManager;
|
|
238
|
+
module.exports.default_configuration = {};
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
const libPictProvider = require('pict-provider');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Documentation Provider for the Form Editor.
|
|
5
|
+
*
|
|
6
|
+
* Manages fetching, caching, and navigation of markdown help articles.
|
|
7
|
+
* Delegates markdown parsing to PictContentProvider and HTML rendering
|
|
8
|
+
* to PictContentView (both from pict-section-content).
|
|
9
|
+
*
|
|
10
|
+
* By default loads articles from relative file paths (e.g. docs/ToC.md).
|
|
11
|
+
* Can be extended to use custom REST endpoints by overriding loadArticle().
|
|
12
|
+
*/
|
|
13
|
+
class FormEditorDocumentation extends libPictProvider
|
|
14
|
+
{
|
|
15
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
16
|
+
{
|
|
17
|
+
super(pFable, pOptions, pServiceHash);
|
|
18
|
+
|
|
19
|
+
this.serviceType = 'PictProvider';
|
|
20
|
+
|
|
21
|
+
// Back-reference to the parent FormEditor view (set after construction)
|
|
22
|
+
this._ParentFormEditor = null;
|
|
23
|
+
|
|
24
|
+
// Navigation state
|
|
25
|
+
this._NavigationStack = [];
|
|
26
|
+
this._CurrentPath = null;
|
|
27
|
+
this._CurrentTitle = 'Help';
|
|
28
|
+
|
|
29
|
+
// Base path for documentation files (relative to the served app)
|
|
30
|
+
this._BasePath = 'docs/';
|
|
31
|
+
|
|
32
|
+
// Cache of parsed HTML keyed by path
|
|
33
|
+
this._Cache = {};
|
|
34
|
+
|
|
35
|
+
// Whether the click handler has been wired on the help body container
|
|
36
|
+
this._ClickHandlerAttached = false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load and display a documentation article.
|
|
41
|
+
*
|
|
42
|
+
* Fetches the markdown file, parses it via PictContentProvider, and
|
|
43
|
+
* renders the HTML via PictContentView.displayContent().
|
|
44
|
+
*
|
|
45
|
+
* @param {string} pPath - Path to the markdown file (e.g. 'docs/ToC.md')
|
|
46
|
+
* @param {boolean} pPushToStack - If true, push the current article onto the navigation stack before navigating
|
|
47
|
+
*/
|
|
48
|
+
loadArticle(pPath, pPushToStack)
|
|
49
|
+
{
|
|
50
|
+
if (!pPath)
|
|
51
|
+
{
|
|
52
|
+
pPath = this._BasePath + 'ToC.md';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!this._ParentFormEditor)
|
|
56
|
+
{
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Push current location onto the stack before navigating
|
|
61
|
+
if (pPushToStack && this._CurrentPath)
|
|
62
|
+
{
|
|
63
|
+
this._NavigationStack.push(
|
|
64
|
+
{
|
|
65
|
+
path: this._CurrentPath,
|
|
66
|
+
title: this._CurrentTitle
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this._CurrentPath = pPath;
|
|
71
|
+
|
|
72
|
+
// If cached, render immediately
|
|
73
|
+
if (this._Cache[pPath])
|
|
74
|
+
{
|
|
75
|
+
this._renderArticle(this._Cache[pPath].html, this._Cache[pPath].title);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Show loading state
|
|
80
|
+
let tmpContentView = this._ParentFormEditor._HelpContentView;
|
|
81
|
+
if (tmpContentView && typeof tmpContentView.showLoading === 'function')
|
|
82
|
+
{
|
|
83
|
+
let tmpBodyId = 'Pict-Content-Body';
|
|
84
|
+
tmpContentView.showLoading('Loading article...', tmpBodyId);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Fetch the markdown file
|
|
88
|
+
let tmpXHR = new XMLHttpRequest();
|
|
89
|
+
tmpXHR.open('GET', pPath, true);
|
|
90
|
+
tmpXHR.onreadystatechange = () =>
|
|
91
|
+
{
|
|
92
|
+
if (tmpXHR.readyState === 4)
|
|
93
|
+
{
|
|
94
|
+
if (tmpXHR.status === 200)
|
|
95
|
+
{
|
|
96
|
+
let tmpMarkdown = tmpXHR.responseText;
|
|
97
|
+
let tmpTitle = this._extractTitle(tmpMarkdown);
|
|
98
|
+
let tmpContentProvider = this._ParentFormEditor._HelpContentProvider;
|
|
99
|
+
|
|
100
|
+
if (tmpContentProvider)
|
|
101
|
+
{
|
|
102
|
+
let tmpLinkResolver = (pHref, pLinkText) =>
|
|
103
|
+
{
|
|
104
|
+
return this._linkResolver(pHref, pLinkText);
|
|
105
|
+
};
|
|
106
|
+
let tmpHTML = tmpContentProvider.parseMarkdown(tmpMarkdown, tmpLinkResolver);
|
|
107
|
+
|
|
108
|
+
// Cache the result
|
|
109
|
+
this._Cache[pPath] = { html: tmpHTML, title: tmpTitle };
|
|
110
|
+
|
|
111
|
+
this._renderArticle(tmpHTML, tmpTitle);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else
|
|
115
|
+
{
|
|
116
|
+
this._renderError(pPath, tmpXHR.status);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
tmpXHR.send();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Navigate back to the previous article.
|
|
125
|
+
*/
|
|
126
|
+
navigateBack()
|
|
127
|
+
{
|
|
128
|
+
if (this._NavigationStack.length < 1)
|
|
129
|
+
{
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let tmpPrevious = this._NavigationStack.pop();
|
|
134
|
+
this.loadArticle(tmpPrevious.path, false);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Navigate to the table of contents.
|
|
139
|
+
*/
|
|
140
|
+
navigateHome()
|
|
141
|
+
{
|
|
142
|
+
// Clear the stack and go home
|
|
143
|
+
this._NavigationStack = [];
|
|
144
|
+
this.loadArticle(this._BasePath + 'ToC.md', false);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Custom link resolver for parseMarkdown().
|
|
149
|
+
*
|
|
150
|
+
* Intercepts links to .md files and converts them to in-panel navigation.
|
|
151
|
+
* External links open in a new tab.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} pHref - The href from the markdown link
|
|
154
|
+
* @param {string} pLinkText - The display text of the link
|
|
155
|
+
* @returns {object|null} Link attributes object or null for default behavior
|
|
156
|
+
*/
|
|
157
|
+
_linkResolver(pHref, pLinkText)
|
|
158
|
+
{
|
|
159
|
+
if (!pHref)
|
|
160
|
+
{
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// If it's a .md file, intercept for in-panel navigation.
|
|
165
|
+
// Use a #help: scheme rather than javascript: to avoid the href being
|
|
166
|
+
// corrupted by the markdown parser's italic regex (underscores in
|
|
167
|
+
// variable names like _DocumentationProvider get wrapped in <em> tags).
|
|
168
|
+
// A click handler on the help body container intercepts these links.
|
|
169
|
+
if (pHref.endsWith('.md'))
|
|
170
|
+
{
|
|
171
|
+
let tmpFullPath = this._BasePath + pHref;
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
href: '#help:' + tmpFullPath
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// External links open in new tab
|
|
179
|
+
if (pHref.startsWith('http://') || pHref.startsWith('https://'))
|
|
180
|
+
{
|
|
181
|
+
return {
|
|
182
|
+
href: pHref,
|
|
183
|
+
target: '_blank',
|
|
184
|
+
rel: 'noopener noreferrer'
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Render the parsed HTML article into the help content area.
|
|
193
|
+
*
|
|
194
|
+
* @param {string} pHTML - The parsed HTML content
|
|
195
|
+
* @param {string} pTitle - The article title (from first heading)
|
|
196
|
+
*/
|
|
197
|
+
_renderArticle(pHTML, pTitle)
|
|
198
|
+
{
|
|
199
|
+
this._CurrentTitle = pTitle || 'Help';
|
|
200
|
+
|
|
201
|
+
let tmpContentView = this._ParentFormEditor._HelpContentView;
|
|
202
|
+
if (tmpContentView)
|
|
203
|
+
{
|
|
204
|
+
let tmpBodyId = 'Pict-Content-Body';
|
|
205
|
+
tmpContentView.displayContent(pHTML, tmpBodyId);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this._attachClickHandler();
|
|
209
|
+
this._renderBreadcrumbs();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Attach a delegated click handler on the help body container to
|
|
214
|
+
* intercept clicks on #help: links. This avoids putting JavaScript
|
|
215
|
+
* in href attributes (which gets corrupted by the markdown parser's
|
|
216
|
+
* italic regex treating underscores in variable names as emphasis).
|
|
217
|
+
*/
|
|
218
|
+
_attachClickHandler()
|
|
219
|
+
{
|
|
220
|
+
if (this._ClickHandlerAttached)
|
|
221
|
+
{
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (typeof document === 'undefined')
|
|
226
|
+
{
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
let tmpHash = this._ParentFormEditor.Hash;
|
|
231
|
+
let tmpBody = document.getElementById(`FormEditor-Help-Body-${tmpHash}`);
|
|
232
|
+
|
|
233
|
+
if (!tmpBody)
|
|
234
|
+
{
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let tmpSelf = this;
|
|
239
|
+
tmpBody.addEventListener('click', (pEvent) =>
|
|
240
|
+
{
|
|
241
|
+
// Walk up from the click target to find an anchor with #help: href
|
|
242
|
+
let tmpEl = pEvent.target;
|
|
243
|
+
while (tmpEl && tmpEl !== tmpBody)
|
|
244
|
+
{
|
|
245
|
+
if (tmpEl.tagName === 'A' && tmpEl.getAttribute('href') && tmpEl.getAttribute('href').indexOf('#help:') === 0)
|
|
246
|
+
{
|
|
247
|
+
pEvent.preventDefault();
|
|
248
|
+
let tmpPath = tmpEl.getAttribute('href').substring(6); // strip '#help:'
|
|
249
|
+
tmpSelf.loadArticle(tmpPath, true);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
tmpEl = tmpEl.parentElement;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
this._ClickHandlerAttached = true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Render an error message when an article fails to load.
|
|
261
|
+
*
|
|
262
|
+
* @param {string} pPath - The path that failed to load
|
|
263
|
+
* @param {number} pStatus - The HTTP status code
|
|
264
|
+
*/
|
|
265
|
+
_renderError(pPath, pStatus)
|
|
266
|
+
{
|
|
267
|
+
let tmpHTML = '<div style="padding: 20px; color: #8A7F72; text-align: center;">';
|
|
268
|
+
tmpHTML += '<p>Could not load article.</p>';
|
|
269
|
+
tmpHTML += `<p style="font-size: 12px; color: #B0A89E;">${pPath} (HTTP ${pStatus})</p>`;
|
|
270
|
+
tmpHTML += '</div>';
|
|
271
|
+
|
|
272
|
+
let tmpContentView = this._ParentFormEditor._HelpContentView;
|
|
273
|
+
if (tmpContentView)
|
|
274
|
+
{
|
|
275
|
+
let tmpBodyId = 'Pict-Content-Body';
|
|
276
|
+
tmpContentView.displayContent(tmpHTML, tmpBodyId);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
this._renderBreadcrumbs();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Render the breadcrumb navigation bar.
|
|
284
|
+
*/
|
|
285
|
+
_renderBreadcrumbs()
|
|
286
|
+
{
|
|
287
|
+
let tmpHash = this._ParentFormEditor.Hash;
|
|
288
|
+
let tmpNavEl = `#FormEditor-Help-Nav-${tmpHash}`;
|
|
289
|
+
let tmpViewRef = this._ParentFormEditor._browserViewRef();
|
|
290
|
+
|
|
291
|
+
let tmpHTML = '';
|
|
292
|
+
|
|
293
|
+
// Home link
|
|
294
|
+
tmpHTML += `<a href="javascript:void(0)" onclick="${tmpViewRef}._DocumentationProvider.navigateHome()">Help</a>`;
|
|
295
|
+
|
|
296
|
+
// Stack entries
|
|
297
|
+
for (let i = 0; i < this._NavigationStack.length; i++)
|
|
298
|
+
{
|
|
299
|
+
let tmpEntry = this._NavigationStack[i];
|
|
300
|
+
tmpHTML += '<span class="pict-fe-help-nav-sep">\u203A</span>';
|
|
301
|
+
tmpHTML += `<a href="javascript:void(0)" onclick="${tmpViewRef}._DocumentationProvider.loadArticle('${tmpEntry.path}', false); ${tmpViewRef}._DocumentationProvider._NavigationStack.splice(${i});">${this._escapeHTML(tmpEntry.title)}</a>`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Current article
|
|
305
|
+
if (this._CurrentTitle && this._CurrentTitle !== 'Help')
|
|
306
|
+
{
|
|
307
|
+
tmpHTML += '<span class="pict-fe-help-nav-sep">\u203A</span>';
|
|
308
|
+
tmpHTML += `<span>${this._escapeHTML(this._CurrentTitle)}</span>`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
this.pict.ContentAssignment.assignContent(tmpNavEl, tmpHTML);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Extract the title from the first heading in a markdown string.
|
|
316
|
+
*
|
|
317
|
+
* @param {string} pMarkdown - The raw markdown text
|
|
318
|
+
* @returns {string} The title text, or 'Help' if no heading found
|
|
319
|
+
*/
|
|
320
|
+
_extractTitle(pMarkdown)
|
|
321
|
+
{
|
|
322
|
+
if (!pMarkdown)
|
|
323
|
+
{
|
|
324
|
+
return 'Help';
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
let tmpMatch = pMarkdown.match(/^#\s+(.+)$/m);
|
|
328
|
+
if (tmpMatch)
|
|
329
|
+
{
|
|
330
|
+
return tmpMatch[1].trim();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return 'Help';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Escape HTML special characters in a string.
|
|
338
|
+
*
|
|
339
|
+
* @param {string} pString - The string to escape
|
|
340
|
+
* @returns {string} The escaped string
|
|
341
|
+
*/
|
|
342
|
+
_escapeHTML(pString)
|
|
343
|
+
{
|
|
344
|
+
if (typeof pString !== 'string')
|
|
345
|
+
{
|
|
346
|
+
return '';
|
|
347
|
+
}
|
|
348
|
+
return pString
|
|
349
|
+
.replace(/&/g, '&')
|
|
350
|
+
.replace(/</g, '<')
|
|
351
|
+
.replace(/>/g, '>')
|
|
352
|
+
.replace(/"/g, '"');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
module.exports = FormEditorDocumentation;
|