@teachinglab/omd 0.6.1 → 0.6.2
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/README.md +257 -251
- package/README.old.md +137 -137
- package/canvas/core/canvasConfig.js +202 -202
- package/canvas/drawing/segment.js +167 -167
- package/canvas/drawing/stroke.js +385 -385
- package/canvas/events/eventManager.js +444 -444
- package/canvas/events/pointerEventHandler.js +262 -262
- package/canvas/index.js +48 -48
- package/canvas/tools/PointerTool.js +71 -71
- package/canvas/tools/tool.js +222 -222
- package/canvas/utils/boundingBox.js +377 -377
- package/canvas/utils/mathUtils.js +258 -258
- package/docs/api/configuration-options.md +198 -198
- package/docs/api/eventManager.md +82 -82
- package/docs/api/focusFrameManager.md +144 -144
- package/docs/api/index.md +105 -105
- package/docs/api/main.md +62 -62
- package/docs/api/omdBinaryExpressionNode.md +86 -86
- package/docs/api/omdCanvas.md +83 -83
- package/docs/api/omdConfigManager.md +112 -112
- package/docs/api/omdConstantNode.md +52 -52
- package/docs/api/omdDisplay.md +87 -87
- package/docs/api/omdEquationNode.md +174 -174
- package/docs/api/omdEquationSequenceNode.md +258 -258
- package/docs/api/omdEquationStack.md +192 -192
- package/docs/api/omdFunctionNode.md +82 -82
- package/docs/api/omdGroupNode.md +78 -78
- package/docs/api/omdHelpers.md +87 -87
- package/docs/api/omdLeafNode.md +85 -85
- package/docs/api/omdNode.md +201 -201
- package/docs/api/omdOperationDisplayNode.md +117 -117
- package/docs/api/omdOperatorNode.md +91 -91
- package/docs/api/omdParenthesisNode.md +133 -133
- package/docs/api/omdPopup.md +191 -191
- package/docs/api/omdPowerNode.md +131 -131
- package/docs/api/omdRationalNode.md +144 -144
- package/docs/api/omdSequenceNode.md +128 -128
- package/docs/api/omdSimplification.md +78 -78
- package/docs/api/omdSqrtNode.md +144 -144
- package/docs/api/omdStepVisualizer.md +146 -146
- package/docs/api/omdStepVisualizerHighlighting.md +65 -65
- package/docs/api/omdStepVisualizerInteractiveSteps.md +108 -108
- package/docs/api/omdStepVisualizerLayout.md +70 -70
- package/docs/api/omdStepVisualizerNodeUtils.md +140 -140
- package/docs/api/omdStepVisualizerTextBoxes.md +76 -76
- package/docs/api/omdToolbar.md +130 -130
- package/docs/api/omdTranscriptionService.md +95 -95
- package/docs/api/omdTreeDiff.md +169 -169
- package/docs/api/omdUnaryExpressionNode.md +137 -137
- package/docs/api/omdUtilities.md +82 -82
- package/docs/api/omdVariableNode.md +123 -123
- package/docs/api/selectTool.md +74 -74
- package/docs/api/simplificationEngine.md +97 -97
- package/docs/api/simplificationRules.md +76 -76
- package/docs/api/simplificationUtils.md +64 -64
- package/docs/api/transcribe.md +43 -43
- package/docs/api-reference.md +85 -85
- package/docs/index.html +453 -453
- package/docs/index.md +38 -38
- package/docs/omd-objects.md +258 -258
- package/index.js +79 -79
- package/jsvg/index.js +3 -0
- package/jsvg/jsvg.js +898 -898
- package/jsvg/jsvgComponents.js +357 -358
- package/npm-docs/DOCUMENTATION_SUMMARY.md +220 -220
- package/npm-docs/README.md +251 -251
- package/npm-docs/api/api-reference.md +85 -85
- package/npm-docs/api/configuration-options.md +198 -198
- package/npm-docs/api/eventManager.md +82 -82
- package/npm-docs/api/expression-nodes.md +561 -561
- package/npm-docs/api/focusFrameManager.md +144 -144
- package/npm-docs/api/index.md +105 -105
- package/npm-docs/api/main.md +62 -62
- package/npm-docs/api/omdBinaryExpressionNode.md +86 -86
- package/npm-docs/api/omdCanvas.md +83 -83
- package/npm-docs/api/omdConfigManager.md +112 -112
- package/npm-docs/api/omdConstantNode.md +52 -52
- package/npm-docs/api/omdDisplay.md +87 -87
- package/npm-docs/api/omdEquationNode.md +174 -174
- package/npm-docs/api/omdEquationSequenceNode.md +258 -258
- package/npm-docs/api/omdEquationStack.md +192 -192
- package/npm-docs/api/omdFunctionNode.md +82 -82
- package/npm-docs/api/omdGroupNode.md +78 -78
- package/npm-docs/api/omdHelpers.md +87 -87
- package/npm-docs/api/omdLeafNode.md +85 -85
- package/npm-docs/api/omdNode.md +201 -201
- package/npm-docs/api/omdOperationDisplayNode.md +117 -117
- package/npm-docs/api/omdOperatorNode.md +91 -91
- package/npm-docs/api/omdParenthesisNode.md +133 -133
- package/npm-docs/api/omdPopup.md +191 -191
- package/npm-docs/api/omdPowerNode.md +131 -131
- package/npm-docs/api/omdRationalNode.md +144 -144
- package/npm-docs/api/omdSequenceNode.md +128 -128
- package/npm-docs/api/omdSimplification.md +78 -78
- package/npm-docs/api/omdSqrtNode.md +144 -144
- package/npm-docs/api/omdStepVisualizer.md +146 -146
- package/npm-docs/api/omdStepVisualizerHighlighting.md +65 -65
- package/npm-docs/api/omdStepVisualizerInteractiveSteps.md +108 -108
- package/npm-docs/api/omdStepVisualizerLayout.md +70 -70
- package/npm-docs/api/omdStepVisualizerNodeUtils.md +140 -140
- package/npm-docs/api/omdStepVisualizerTextBoxes.md +76 -76
- package/npm-docs/api/omdToolbar.md +130 -130
- package/npm-docs/api/omdTranscriptionService.md +95 -95
- package/npm-docs/api/omdTreeDiff.md +169 -169
- package/npm-docs/api/omdUnaryExpressionNode.md +137 -137
- package/npm-docs/api/omdUtilities.md +82 -82
- package/npm-docs/api/omdVariableNode.md +123 -123
- package/npm-docs/api/selectTool.md +74 -74
- package/npm-docs/api/simplificationEngine.md +97 -97
- package/npm-docs/api/simplificationRules.md +76 -76
- package/npm-docs/api/simplificationUtils.md +64 -64
- package/npm-docs/api/transcribe.md +43 -43
- package/npm-docs/guides/equations.md +854 -854
- package/npm-docs/guides/factory-functions.md +354 -354
- package/npm-docs/guides/getting-started.md +318 -318
- package/npm-docs/guides/quick-examples.md +525 -525
- package/npm-docs/guides/visualizations.md +682 -682
- package/npm-docs/index.html +12 -0
- package/npm-docs/json-schemas.md +826 -826
- package/omd/config/omdConfigManager.js +279 -267
- package/omd/core/index.js +158 -158
- package/omd/core/omdEquationStack.js +546 -546
- package/omd/core/omdUtilities.js +113 -113
- package/omd/display/omdDisplay.js +969 -962
- package/omd/display/omdToolbar.js +501 -501
- package/omd/nodes/omdBinaryExpressionNode.js +459 -459
- package/omd/nodes/omdConstantNode.js +141 -141
- package/omd/nodes/omdEquationNode.js +1327 -1327
- package/omd/nodes/omdFunctionNode.js +351 -351
- package/omd/nodes/omdGroupNode.js +67 -67
- package/omd/nodes/omdLeafNode.js +76 -76
- package/omd/nodes/omdNode.js +556 -556
- package/omd/nodes/omdOperationDisplayNode.js +321 -321
- package/omd/nodes/omdOperatorNode.js +108 -108
- package/omd/nodes/omdParenthesisNode.js +292 -292
- package/omd/nodes/omdPowerNode.js +235 -235
- package/omd/nodes/omdRationalNode.js +295 -295
- package/omd/nodes/omdSqrtNode.js +307 -307
- package/omd/nodes/omdUnaryExpressionNode.js +227 -227
- package/omd/nodes/omdVariableNode.js +122 -122
- package/omd/simplification/omdSimplification.js +140 -140
- package/omd/simplification/omdSimplificationEngine.js +887 -887
- package/omd/simplification/package.json +5 -5
- package/omd/simplification/rules/binaryRules.js +1037 -1037
- package/omd/simplification/rules/functionRules.js +111 -111
- package/omd/simplification/rules/index.js +48 -48
- package/omd/simplification/rules/parenthesisRules.js +19 -19
- package/omd/simplification/rules/powerRules.js +143 -143
- package/omd/simplification/rules/rationalRules.js +725 -725
- package/omd/simplification/rules/sqrtRules.js +48 -48
- package/omd/simplification/rules/unaryRules.js +37 -37
- package/omd/simplification/simplificationRules.js +31 -31
- package/omd/simplification/simplificationUtils.js +1055 -1055
- package/omd/step-visualizer/omdStepVisualizer.js +947 -947
- package/omd/step-visualizer/omdStepVisualizerHighlighting.js +246 -246
- package/omd/step-visualizer/omdStepVisualizerLayout.js +892 -892
- package/omd/step-visualizer/omdStepVisualizerTextBoxes.js +200 -200
- package/omd/utils/aiNextEquationStep.js +106 -106
- package/omd/utils/omdNodeOverlay.js +638 -638
- package/omd/utils/omdPopup.js +1203 -1203
- package/omd/utils/omdStepVisualizerInteractiveSteps.js +684 -684
- package/omd/utils/omdStepVisualizerNodeUtils.js +267 -267
- package/omd/utils/omdTranscriptionService.js +123 -123
- package/omd/utils/omdTreeDiff.js +733 -733
- package/package.json +59 -57
- package/readme.html +184 -120
- package/src/index.js +74 -74
- package/src/json-schemas.md +576 -576
- package/src/omd-json-samples.js +147 -147
- package/src/omdApp.js +391 -391
- package/src/omdAppCanvas.js +335 -335
- package/src/omdBalanceHanger.js +199 -199
- package/src/omdColor.js +13 -13
- package/src/omdCoordinatePlane.js +541 -541
- package/src/omdExpression.js +115 -115
- package/src/omdFactory.js +150 -150
- package/src/omdFunction.js +114 -114
- package/src/omdMetaExpression.js +290 -290
- package/src/omdNaturalExpression.js +563 -563
- package/src/omdNode.js +383 -383
- package/src/omdNumber.js +52 -52
- package/src/omdNumberLine.js +114 -112
- package/src/omdNumberTile.js +118 -118
- package/src/omdOperator.js +72 -72
- package/src/omdPowerExpression.js +91 -91
- package/src/omdProblem.js +259 -259
- package/src/omdRatioChart.js +251 -251
- package/src/omdRationalExpression.js +114 -114
- package/src/omdSampleData.js +215 -215
- package/src/omdShapes.js +512 -512
- package/src/omdSpinner.js +151 -151
- package/src/omdString.js +49 -49
- package/src/omdTable.js +498 -498
- package/src/omdTapeDiagram.js +244 -244
- package/src/omdTerm.js +91 -91
- package/src/omdTileEquation.js +349 -349
- package/src/omdUtils.js +84 -84
- package/src/omdVariable.js +51 -51
package/src/omdMetaExpression.js
CHANGED
|
@@ -1,291 +1,291 @@
|
|
|
1
|
-
import { omdColor } from "./omdColor.js";
|
|
2
|
-
import { jsvgGroup, jsvgRect } from "@teachinglab/jsvg";
|
|
3
|
-
|
|
4
|
-
export class omdMetaExpression extends jsvgGroup
|
|
5
|
-
{
|
|
6
|
-
constructor( V = 1 )
|
|
7
|
-
{
|
|
8
|
-
// initialization
|
|
9
|
-
super();
|
|
10
|
-
|
|
11
|
-
this.type = "";
|
|
12
|
-
this.value = V;
|
|
13
|
-
|
|
14
|
-
this.defaultOpaqueBack = true;
|
|
15
|
-
|
|
16
|
-
this.backRect = new jsvgRect();
|
|
17
|
-
this.backRect.setWidthAndHeight( 30,30 );
|
|
18
|
-
this.backRect.setCornerRadius( 5 );
|
|
19
|
-
this.backRect.setFillColor( this.getBackgroundColor() );
|
|
20
|
-
this.addChild( this.backRect );
|
|
21
|
-
|
|
22
|
-
// Old events for selection - we will replace these with provenance highlighting
|
|
23
|
-
// this.svgObject.onmouseenter = this.select.bind(this);
|
|
24
|
-
// this.svgObject.onmouseleave = this.deselect.bind(this);
|
|
25
|
-
|
|
26
|
-
// New events for provenance highlighting
|
|
27
|
-
this.svgObject.onmouseenter = this.highlightProvenance.bind(this);
|
|
28
|
-
this.svgObject.onmouseleave = this.clearProvenance.bind(this);
|
|
29
|
-
this.svgObject.style.cursor = "pointer";
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/* Gerard - ADDED */
|
|
33
|
-
setWidthAndHeight(w, h) {
|
|
34
|
-
super.setWidthAndHeight(w, h);
|
|
35
|
-
this.backRect.setWidthAndHeight(w, h);
|
|
36
|
-
}
|
|
37
|
-
setFontSize(fontSize) {
|
|
38
|
-
this.fontSize = fontSize;
|
|
39
|
-
|
|
40
|
-
// Propagate font size to all children that support it
|
|
41
|
-
if (this.childList) {
|
|
42
|
-
this.childList.forEach(child => {
|
|
43
|
-
if (child && typeof child.setFontSize === 'function') {
|
|
44
|
-
child.setFontSize(fontSize);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
getFontSize() {
|
|
50
|
-
if (this.fontSize) return this.fontSize;
|
|
51
|
-
|
|
52
|
-
if (this.parent && typeof this.parent.getFontSize === 'function') {
|
|
53
|
-
return this.parent.getFontSize();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return 16; // Default fallback
|
|
57
|
-
}
|
|
58
|
-
getRootFontSize() {
|
|
59
|
-
if (this.parent && (this.parent instanceof omdMetaExpression)) {
|
|
60
|
-
return this.parent.getRootFontSize();
|
|
61
|
-
}
|
|
62
|
-
// No parent, so this is the root. Return its font size or default.
|
|
63
|
-
return this.fontSize || 16;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
getBackgroundColor() {
|
|
67
|
-
return this._backgroundStyle?.backgroundColor ?? omdColor.lightGray;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ===== PROVENANCE HIGHLIGHTING =====
|
|
71
|
-
|
|
72
|
-
highlightProvenance(event, color = omdColor.hiliteColor, minStepNumber = -Infinity) {
|
|
73
|
-
// Prevent event from bubbling up to parent containers and causing flickering
|
|
74
|
-
event.stopPropagation();
|
|
75
|
-
|
|
76
|
-
const rootNode = this.getRootNode();
|
|
77
|
-
if (!rootNode || !rootNode.nodeMap) return;
|
|
78
|
-
|
|
79
|
-
// Clear any previous highlights from the entire sequence
|
|
80
|
-
rootNode.clearProvenanceHighlights();
|
|
81
|
-
|
|
82
|
-
// Highlight the node being hovered over
|
|
83
|
-
this.highlight(color);
|
|
84
|
-
|
|
85
|
-
// Use an iterative approach to traverse the provenance chain
|
|
86
|
-
let nodesToProcess = [...this.provenance];
|
|
87
|
-
const visited = new Set(nodesToProcess);
|
|
88
|
-
|
|
89
|
-
while (nodesToProcess.length > 0) {
|
|
90
|
-
const currentId = nodesToProcess.shift(); // Get the next node to process
|
|
91
|
-
const node = rootNode.nodeMap.get(currentId);
|
|
92
|
-
|
|
93
|
-
if (node) {
|
|
94
|
-
node.highlight(color);
|
|
95
|
-
if (node.provenance) {
|
|
96
|
-
node.provenance.forEach(pId => {
|
|
97
|
-
if (!visited.has(pId)) {
|
|
98
|
-
visited.add(pId);
|
|
99
|
-
nodesToProcess.push(pId);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
clearProvenance(event) {
|
|
108
|
-
event.stopPropagation();
|
|
109
|
-
const rootNode = this.getRootNode();
|
|
110
|
-
if (rootNode) {
|
|
111
|
-
rootNode.clearProvenanceHighlights();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
highlight(color) {
|
|
116
|
-
this.backRect.setFillColor(color);
|
|
117
|
-
this.backRect.setOpacity(1.0);
|
|
118
|
-
|
|
119
|
-
// also highlight children
|
|
120
|
-
this.childList.forEach((child) => {
|
|
121
|
-
if (child instanceof omdMetaExpression)
|
|
122
|
-
child.highlight(color);
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
clearProvenanceHighlights() {
|
|
127
|
-
// Preserve step visualizer highlights (explain highlights) if present
|
|
128
|
-
if (this.isExplainHighlighted) {
|
|
129
|
-
// Restore the explain highlight color and ensure full opacity
|
|
130
|
-
this.backRect.setFillColor(omdColor.explainColor);
|
|
131
|
-
this.backRect.setOpacity(1.0);
|
|
132
|
-
} else if (this.isProvenanceHighlighted) {
|
|
133
|
-
// Preserve step visualizer provenance highlights
|
|
134
|
-
this.backRect.setFillColor(omdColor.explainColor);
|
|
135
|
-
this.backRect.setOpacity(1.0);
|
|
136
|
-
} else {
|
|
137
|
-
// Reset to the default background state
|
|
138
|
-
this.backRect.setFillColor(this._backgroundStyle?.backgroundColor ?? omdColor.lightGray);
|
|
139
|
-
if (!this.defaultOpaqueBack) {
|
|
140
|
-
this.backRect.setOpacity(0.01);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Recursively clear highlights for all children in the tree
|
|
145
|
-
this.childList.forEach((child) => {
|
|
146
|
-
if (child instanceof omdMetaExpression && typeof child.clearProvenanceHighlights === 'function') {
|
|
147
|
-
child.clearProvenanceHighlights();
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Recursively applies or clears a persistent "explain" highlight.
|
|
154
|
-
* This is used by the step visualizer to mark nodes that have changed between steps.
|
|
155
|
-
* The highlight is preserved during hover events.
|
|
156
|
-
* @param {boolean} isOn - True to apply the highlight, false to clear it.
|
|
157
|
-
* @param {string} color - The color to use for the highlight.
|
|
158
|
-
*/
|
|
159
|
-
setExplainHighlight(isOn = true, color = omdColor.explainColor) {
|
|
160
|
-
this.isExplainHighlighted = isOn;
|
|
161
|
-
|
|
162
|
-
if (isOn) {
|
|
163
|
-
this.backRect.setFillColor(color);
|
|
164
|
-
this.backRect.setOpacity(1.0);
|
|
165
|
-
} else {
|
|
166
|
-
// Reset to default state
|
|
167
|
-
this.backRect.setFillColor(this.getBackgroundColor());
|
|
168
|
-
if (!this.defaultOpaqueBack) {
|
|
169
|
-
this.backRect.setOpacity(0.01);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Use the same traversal logic as hover highlighting to recurse
|
|
174
|
-
if (this.argumentNodeList) {
|
|
175
|
-
Object.values(this.argumentNodeList).forEach(child => {
|
|
176
|
-
if (child && typeof child.setExplainHighlight === 'function') {
|
|
177
|
-
child.setExplainHighlight(isOn, color);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
getRootNode() {
|
|
184
|
-
let current = this;
|
|
185
|
-
// Traverse upwards as long as the parent is also part of the omd expression system
|
|
186
|
-
while (current.parent && (current.parent instanceof omdMetaExpression)) {
|
|
187
|
-
current = current.parent;
|
|
188
|
-
}
|
|
189
|
-
return current;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
findAllNodes(nodes = []) {
|
|
193
|
-
nodes.push(this);
|
|
194
|
-
if (this.childList) {
|
|
195
|
-
this.childList.forEach(child => {
|
|
196
|
-
if (child.findAllNodes) { // Ensure child has the method
|
|
197
|
-
child.findAllNodes(nodes);
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
return nodes;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// ===== OLD SELECTION LOGIC (can be removed or kept for other purposes) =====
|
|
205
|
-
|
|
206
|
-
select(root) {
|
|
207
|
-
if (root === this) return;
|
|
208
|
-
|
|
209
|
-
if (!(root instanceof omdMetaExpression)) root = this;
|
|
210
|
-
|
|
211
|
-
this.backRect.setFillColor( omdColor.hiliteColor );
|
|
212
|
-
this.backRect.setOpacity( 1.0 );
|
|
213
|
-
|
|
214
|
-
this.childList.forEach((child) => {
|
|
215
|
-
if (child instanceof omdMetaExpression)
|
|
216
|
-
child.select(root);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
if (this === root && this.parent instanceof omdMetaExpression)
|
|
220
|
-
root.parent.deselect(root);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
deselect(root) {
|
|
224
|
-
if (!(root instanceof omdMetaExpression)) root = this;
|
|
225
|
-
|
|
226
|
-
if (this === root && this.parent instanceof omdMetaExpression)
|
|
227
|
-
root.parent.select(root);
|
|
228
|
-
|
|
229
|
-
if (root === this && this.parent instanceof omdMetaExpression) return;
|
|
230
|
-
|
|
231
|
-
this.backRect.setFillColor( this.getBackgroundColor() );
|
|
232
|
-
if ( this.defaultOpaqueBack == false )
|
|
233
|
-
this.backRect.setOpacity(0.01);
|
|
234
|
-
|
|
235
|
-
this.childList.forEach((child) => {
|
|
236
|
-
if (child !== root && child instanceof omdMetaExpression)
|
|
237
|
-
child.deselect(root);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// select(root)
|
|
243
|
-
// {
|
|
244
|
-
// this.backRect.setFillColor( omdColor.hiliteColor );
|
|
245
|
-
// this.backRect.setOpacity( 1.0 );
|
|
246
|
-
|
|
247
|
-
// if (!(root instanceof omdMetaExpression))
|
|
248
|
-
// root = this;
|
|
249
|
-
|
|
250
|
-
// this.childList.forEach((child) => {
|
|
251
|
-
// if (child instanceof omdMetaExpression)
|
|
252
|
-
// child.select(root);
|
|
253
|
-
// });
|
|
254
|
-
// if (this === root)
|
|
255
|
-
// this.unselectParents(root);
|
|
256
|
-
// }
|
|
257
|
-
|
|
258
|
-
// deselect(root)
|
|
259
|
-
// {
|
|
260
|
-
// this.backRect.setFillColor( omdColor.lightGray );
|
|
261
|
-
// if ( this.defaultOpaqueBack == false )
|
|
262
|
-
// this.backRect.setOpacity(0.01);
|
|
263
|
-
|
|
264
|
-
// this.childList.forEach((child) => {
|
|
265
|
-
// if (child !== root && child instanceof omdMetaExpression) {
|
|
266
|
-
// child.deselect(root);
|
|
267
|
-
// }
|
|
268
|
-
// });
|
|
269
|
-
|
|
270
|
-
// if (this.parent instanceof omdMetaExpression)
|
|
271
|
-
// this.parent.select(this.parent);
|
|
272
|
-
// }
|
|
273
|
-
|
|
274
|
-
// unselectParents(root)
|
|
275
|
-
// {
|
|
276
|
-
// if (this.parent instanceof omdMetaExpression) {
|
|
277
|
-
// this.parent.unselectParents(root);
|
|
278
|
-
// this.parent.deselect(root);
|
|
279
|
-
// }
|
|
280
|
-
// }
|
|
281
|
-
|
|
282
|
-
hideBackgroundByDefault()
|
|
283
|
-
{
|
|
284
|
-
this.defaultOpaqueBack = false;
|
|
285
|
-
this.backRect.setOpacity(0.01);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
makeBackgroundLight() { this.backRect.setFillColor( this.getBackgroundColor() ) }
|
|
289
|
-
makeBackgroundDark() { this.backRect.setFillColor( omdColor.mediumGray ) }
|
|
290
|
-
|
|
1
|
+
import { omdColor } from "./omdColor.js";
|
|
2
|
+
import { jsvgGroup, jsvgRect } from "@teachinglab/jsvg";
|
|
3
|
+
|
|
4
|
+
export class omdMetaExpression extends jsvgGroup
|
|
5
|
+
{
|
|
6
|
+
constructor( V = 1 )
|
|
7
|
+
{
|
|
8
|
+
// initialization
|
|
9
|
+
super();
|
|
10
|
+
|
|
11
|
+
this.type = "";
|
|
12
|
+
this.value = V;
|
|
13
|
+
|
|
14
|
+
this.defaultOpaqueBack = true;
|
|
15
|
+
|
|
16
|
+
this.backRect = new jsvgRect();
|
|
17
|
+
this.backRect.setWidthAndHeight( 30,30 );
|
|
18
|
+
this.backRect.setCornerRadius( 5 );
|
|
19
|
+
this.backRect.setFillColor( this.getBackgroundColor() );
|
|
20
|
+
this.addChild( this.backRect );
|
|
21
|
+
|
|
22
|
+
// Old events for selection - we will replace these with provenance highlighting
|
|
23
|
+
// this.svgObject.onmouseenter = this.select.bind(this);
|
|
24
|
+
// this.svgObject.onmouseleave = this.deselect.bind(this);
|
|
25
|
+
|
|
26
|
+
// New events for provenance highlighting
|
|
27
|
+
this.svgObject.onmouseenter = this.highlightProvenance.bind(this);
|
|
28
|
+
this.svgObject.onmouseleave = this.clearProvenance.bind(this);
|
|
29
|
+
this.svgObject.style.cursor = "pointer";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Gerard - ADDED */
|
|
33
|
+
setWidthAndHeight(w, h) {
|
|
34
|
+
super.setWidthAndHeight(w, h);
|
|
35
|
+
this.backRect.setWidthAndHeight(w, h);
|
|
36
|
+
}
|
|
37
|
+
setFontSize(fontSize) {
|
|
38
|
+
this.fontSize = fontSize;
|
|
39
|
+
|
|
40
|
+
// Propagate font size to all children that support it
|
|
41
|
+
if (this.childList) {
|
|
42
|
+
this.childList.forEach(child => {
|
|
43
|
+
if (child && typeof child.setFontSize === 'function') {
|
|
44
|
+
child.setFontSize(fontSize);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
getFontSize() {
|
|
50
|
+
if (this.fontSize) return this.fontSize;
|
|
51
|
+
|
|
52
|
+
if (this.parent && typeof this.parent.getFontSize === 'function') {
|
|
53
|
+
return this.parent.getFontSize();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return 16; // Default fallback
|
|
57
|
+
}
|
|
58
|
+
getRootFontSize() {
|
|
59
|
+
if (this.parent && (this.parent instanceof omdMetaExpression)) {
|
|
60
|
+
return this.parent.getRootFontSize();
|
|
61
|
+
}
|
|
62
|
+
// No parent, so this is the root. Return its font size or default.
|
|
63
|
+
return this.fontSize || 16;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getBackgroundColor() {
|
|
67
|
+
return this._backgroundStyle?.backgroundColor ?? omdColor.lightGray;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ===== PROVENANCE HIGHLIGHTING =====
|
|
71
|
+
|
|
72
|
+
highlightProvenance(event, color = omdColor.hiliteColor, minStepNumber = -Infinity) {
|
|
73
|
+
// Prevent event from bubbling up to parent containers and causing flickering
|
|
74
|
+
event.stopPropagation();
|
|
75
|
+
|
|
76
|
+
const rootNode = this.getRootNode();
|
|
77
|
+
if (!rootNode || !rootNode.nodeMap) return;
|
|
78
|
+
|
|
79
|
+
// Clear any previous highlights from the entire sequence
|
|
80
|
+
rootNode.clearProvenanceHighlights();
|
|
81
|
+
|
|
82
|
+
// Highlight the node being hovered over
|
|
83
|
+
this.highlight(color);
|
|
84
|
+
|
|
85
|
+
// Use an iterative approach to traverse the provenance chain
|
|
86
|
+
let nodesToProcess = [...this.provenance];
|
|
87
|
+
const visited = new Set(nodesToProcess);
|
|
88
|
+
|
|
89
|
+
while (nodesToProcess.length > 0) {
|
|
90
|
+
const currentId = nodesToProcess.shift(); // Get the next node to process
|
|
91
|
+
const node = rootNode.nodeMap.get(currentId);
|
|
92
|
+
|
|
93
|
+
if (node) {
|
|
94
|
+
node.highlight(color);
|
|
95
|
+
if (node.provenance) {
|
|
96
|
+
node.provenance.forEach(pId => {
|
|
97
|
+
if (!visited.has(pId)) {
|
|
98
|
+
visited.add(pId);
|
|
99
|
+
nodesToProcess.push(pId);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
clearProvenance(event) {
|
|
108
|
+
event.stopPropagation();
|
|
109
|
+
const rootNode = this.getRootNode();
|
|
110
|
+
if (rootNode) {
|
|
111
|
+
rootNode.clearProvenanceHighlights();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
highlight(color) {
|
|
116
|
+
this.backRect.setFillColor(color);
|
|
117
|
+
this.backRect.setOpacity(1.0);
|
|
118
|
+
|
|
119
|
+
// also highlight children
|
|
120
|
+
this.childList.forEach((child) => {
|
|
121
|
+
if (child instanceof omdMetaExpression)
|
|
122
|
+
child.highlight(color);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
clearProvenanceHighlights() {
|
|
127
|
+
// Preserve step visualizer highlights (explain highlights) if present
|
|
128
|
+
if (this.isExplainHighlighted) {
|
|
129
|
+
// Restore the explain highlight color and ensure full opacity
|
|
130
|
+
this.backRect.setFillColor(omdColor.explainColor);
|
|
131
|
+
this.backRect.setOpacity(1.0);
|
|
132
|
+
} else if (this.isProvenanceHighlighted) {
|
|
133
|
+
// Preserve step visualizer provenance highlights
|
|
134
|
+
this.backRect.setFillColor(omdColor.explainColor);
|
|
135
|
+
this.backRect.setOpacity(1.0);
|
|
136
|
+
} else {
|
|
137
|
+
// Reset to the default background state
|
|
138
|
+
this.backRect.setFillColor(this._backgroundStyle?.backgroundColor ?? omdColor.lightGray);
|
|
139
|
+
if (!this.defaultOpaqueBack) {
|
|
140
|
+
this.backRect.setOpacity(0.01);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Recursively clear highlights for all children in the tree
|
|
145
|
+
this.childList.forEach((child) => {
|
|
146
|
+
if (child instanceof omdMetaExpression && typeof child.clearProvenanceHighlights === 'function') {
|
|
147
|
+
child.clearProvenanceHighlights();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Recursively applies or clears a persistent "explain" highlight.
|
|
154
|
+
* This is used by the step visualizer to mark nodes that have changed between steps.
|
|
155
|
+
* The highlight is preserved during hover events.
|
|
156
|
+
* @param {boolean} isOn - True to apply the highlight, false to clear it.
|
|
157
|
+
* @param {string} color - The color to use for the highlight.
|
|
158
|
+
*/
|
|
159
|
+
setExplainHighlight(isOn = true, color = omdColor.explainColor) {
|
|
160
|
+
this.isExplainHighlighted = isOn;
|
|
161
|
+
|
|
162
|
+
if (isOn) {
|
|
163
|
+
this.backRect.setFillColor(color);
|
|
164
|
+
this.backRect.setOpacity(1.0);
|
|
165
|
+
} else {
|
|
166
|
+
// Reset to default state
|
|
167
|
+
this.backRect.setFillColor(this.getBackgroundColor());
|
|
168
|
+
if (!this.defaultOpaqueBack) {
|
|
169
|
+
this.backRect.setOpacity(0.01);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Use the same traversal logic as hover highlighting to recurse
|
|
174
|
+
if (this.argumentNodeList) {
|
|
175
|
+
Object.values(this.argumentNodeList).forEach(child => {
|
|
176
|
+
if (child && typeof child.setExplainHighlight === 'function') {
|
|
177
|
+
child.setExplainHighlight(isOn, color);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
getRootNode() {
|
|
184
|
+
let current = this;
|
|
185
|
+
// Traverse upwards as long as the parent is also part of the omd expression system
|
|
186
|
+
while (current.parent && (current.parent instanceof omdMetaExpression)) {
|
|
187
|
+
current = current.parent;
|
|
188
|
+
}
|
|
189
|
+
return current;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
findAllNodes(nodes = []) {
|
|
193
|
+
nodes.push(this);
|
|
194
|
+
if (this.childList) {
|
|
195
|
+
this.childList.forEach(child => {
|
|
196
|
+
if (child.findAllNodes) { // Ensure child has the method
|
|
197
|
+
child.findAllNodes(nodes);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return nodes;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ===== OLD SELECTION LOGIC (can be removed or kept for other purposes) =====
|
|
205
|
+
|
|
206
|
+
select(root) {
|
|
207
|
+
if (root === this) return;
|
|
208
|
+
|
|
209
|
+
if (!(root instanceof omdMetaExpression)) root = this;
|
|
210
|
+
|
|
211
|
+
this.backRect.setFillColor( omdColor.hiliteColor );
|
|
212
|
+
this.backRect.setOpacity( 1.0 );
|
|
213
|
+
|
|
214
|
+
this.childList.forEach((child) => {
|
|
215
|
+
if (child instanceof omdMetaExpression)
|
|
216
|
+
child.select(root);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (this === root && this.parent instanceof omdMetaExpression)
|
|
220
|
+
root.parent.deselect(root);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
deselect(root) {
|
|
224
|
+
if (!(root instanceof omdMetaExpression)) root = this;
|
|
225
|
+
|
|
226
|
+
if (this === root && this.parent instanceof omdMetaExpression)
|
|
227
|
+
root.parent.select(root);
|
|
228
|
+
|
|
229
|
+
if (root === this && this.parent instanceof omdMetaExpression) return;
|
|
230
|
+
|
|
231
|
+
this.backRect.setFillColor( this.getBackgroundColor() );
|
|
232
|
+
if ( this.defaultOpaqueBack == false )
|
|
233
|
+
this.backRect.setOpacity(0.01);
|
|
234
|
+
|
|
235
|
+
this.childList.forEach((child) => {
|
|
236
|
+
if (child !== root && child instanceof omdMetaExpression)
|
|
237
|
+
child.deselect(root);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// select(root)
|
|
243
|
+
// {
|
|
244
|
+
// this.backRect.setFillColor( omdColor.hiliteColor );
|
|
245
|
+
// this.backRect.setOpacity( 1.0 );
|
|
246
|
+
|
|
247
|
+
// if (!(root instanceof omdMetaExpression))
|
|
248
|
+
// root = this;
|
|
249
|
+
|
|
250
|
+
// this.childList.forEach((child) => {
|
|
251
|
+
// if (child instanceof omdMetaExpression)
|
|
252
|
+
// child.select(root);
|
|
253
|
+
// });
|
|
254
|
+
// if (this === root)
|
|
255
|
+
// this.unselectParents(root);
|
|
256
|
+
// }
|
|
257
|
+
|
|
258
|
+
// deselect(root)
|
|
259
|
+
// {
|
|
260
|
+
// this.backRect.setFillColor( omdColor.lightGray );
|
|
261
|
+
// if ( this.defaultOpaqueBack == false )
|
|
262
|
+
// this.backRect.setOpacity(0.01);
|
|
263
|
+
|
|
264
|
+
// this.childList.forEach((child) => {
|
|
265
|
+
// if (child !== root && child instanceof omdMetaExpression) {
|
|
266
|
+
// child.deselect(root);
|
|
267
|
+
// }
|
|
268
|
+
// });
|
|
269
|
+
|
|
270
|
+
// if (this.parent instanceof omdMetaExpression)
|
|
271
|
+
// this.parent.select(this.parent);
|
|
272
|
+
// }
|
|
273
|
+
|
|
274
|
+
// unselectParents(root)
|
|
275
|
+
// {
|
|
276
|
+
// if (this.parent instanceof omdMetaExpression) {
|
|
277
|
+
// this.parent.unselectParents(root);
|
|
278
|
+
// this.parent.deselect(root);
|
|
279
|
+
// }
|
|
280
|
+
// }
|
|
281
|
+
|
|
282
|
+
hideBackgroundByDefault()
|
|
283
|
+
{
|
|
284
|
+
this.defaultOpaqueBack = false;
|
|
285
|
+
this.backRect.setOpacity(0.01);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
makeBackgroundLight() { this.backRect.setFillColor( this.getBackgroundColor() ) }
|
|
289
|
+
makeBackgroundDark() { this.backRect.setFillColor( omdColor.mediumGray ) }
|
|
290
|
+
|
|
291
291
|
}
|