munchboka-edutools 0.1.13__py3-none-any.whl
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.
Potentially problematic release.
This version of munchboka-edutools might be problematic. Click here for more details.
- munchboka_edutools/__init__.py +184 -0
- munchboka_edutools/_plotmath_shim.py +126 -0
- munchboka_edutools/_version.py +2 -0
- munchboka_edutools/directives/__init__.py +1 -0
- munchboka_edutools/directives/admonitions.py +389 -0
- munchboka_edutools/directives/cas_popup.py +272 -0
- munchboka_edutools/directives/clear.py +103 -0
- munchboka_edutools/directives/dialogue.py +137 -0
- munchboka_edutools/directives/escape_room.py +296 -0
- munchboka_edutools/directives/factor_tree.py +549 -0
- munchboka_edutools/directives/ggb.py +209 -0
- munchboka_edutools/directives/ggb_icon.py +105 -0
- munchboka_edutools/directives/ggb_popup.py +165 -0
- munchboka_edutools/directives/horner.py +324 -0
- munchboka_edutools/directives/interactive_code.py +75 -0
- munchboka_edutools/directives/jeopardy.py +252 -0
- munchboka_edutools/directives/multi_plot.py +1126 -0
- munchboka_edutools/directives/pair_puzzle.py +191 -0
- munchboka_edutools/directives/parsons.py +109 -0
- munchboka_edutools/directives/plot.py +3105 -0
- munchboka_edutools/directives/poly_icon.py +111 -0
- munchboka_edutools/directives/polydiv.py +344 -0
- munchboka_edutools/directives/popup.py +245 -0
- munchboka_edutools/directives/quiz.py +291 -0
- munchboka_edutools/directives/signchart.py +516 -0
- munchboka_edutools/directives/timed_quiz.py +436 -0
- munchboka_edutools/directives/turtle.py +157 -0
- munchboka_edutools/static/css/admonitions.css +2012 -0
- munchboka_edutools/static/css/cas_popup.css +242 -0
- munchboka_edutools/static/css/code_mirror_themes/github_dark_cm.css +112 -0
- munchboka_edutools/static/css/code_mirror_themes/github_dark_default_cm.css +40 -0
- munchboka_edutools/static/css/code_mirror_themes/github_dark_high_contrast_cm.css +141 -0
- munchboka_edutools/static/css/code_mirror_themes/github_light_cm.css +120 -0
- munchboka_edutools/static/css/code_mirror_themes/github_light_default_cm.css +108 -0
- munchboka_edutools/static/css/code_mirror_themes/one_dark_cm.css +121 -0
- munchboka_edutools/static/css/dialogue.css +92 -0
- munchboka_edutools/static/css/escapeRoom/escape-room.css +223 -0
- munchboka_edutools/static/css/figures.css +274 -0
- munchboka_edutools/static/css/general_style.css +74 -0
- munchboka_edutools/static/css/github-dark-high-contrast.css +141 -0
- munchboka_edutools/static/css/github-dark.css +112 -0
- munchboka_edutools/static/css/github-light.css +120 -0
- munchboka_edutools/static/css/interactive_code/style.css +575 -0
- munchboka_edutools/static/css/interactive_code.css +582 -0
- munchboka_edutools/static/css/jeopardy.css +529 -0
- munchboka_edutools/static/css/pairPuzzle/style.css +177 -0
- munchboka_edutools/static/css/parsons/parsonsPuzzle.css +331 -0
- munchboka_edutools/static/css/popup.css +115 -0
- munchboka_edutools/static/css/quiz.css +312 -0
- munchboka_edutools/static/css/timedQuiz.css +375 -0
- munchboka_edutools/static/icons/ggb/mode_evaluate.svg +1 -0
- munchboka_edutools/static/icons/ggb/mode_extremum.svg +1 -0
- munchboka_edutools/static/icons/ggb/mode_intersect.svg +1 -0
- munchboka_edutools/static/icons/ggb/mode_nsolve.svg +1 -0
- munchboka_edutools/static/icons/ggb/mode_numeric.svg +1 -0
- munchboka_edutools/static/icons/ggb/mode_point.svg +1 -0
- munchboka_edutools/static/icons/ggb/mode_solve.svg +1 -0
- munchboka_edutools/static/icons/misc/windows-logo.svg +1 -0
- munchboka_edutools/static/icons/outline/dark_mode/academic.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/backward.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/book.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/chat_bubble.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/check.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/cmd_line.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/file.svg +1 -0
- munchboka_edutools/static/icons/outline/dark_mode/fire.svg +4 -0
- munchboka_edutools/static/icons/outline/dark_mode/key.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/magnifying.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/pencil_square.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/play.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/question.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/square_check.svg +1 -0
- munchboka_edutools/static/icons/outline/dark_mode/stop.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/summary.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/undo.svg +3 -0
- munchboka_edutools/static/icons/outline/dark_mode/unlock.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/academic.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/backward.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/book.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/chat_bubble.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/check.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/cmd_line.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/file.svg +1 -0
- munchboka_edutools/static/icons/outline/light_mode/fire.svg +4 -0
- munchboka_edutools/static/icons/outline/light_mode/key.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/magnifying.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/pencil_square.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/play.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/question.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/square_check.svg +1 -0
- munchboka_edutools/static/icons/outline/light_mode/stop.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/summary.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/undo.svg +3 -0
- munchboka_edutools/static/icons/outline/light_mode/unlock.svg +3 -0
- munchboka_edutools/static/icons/polyicons/cubicdown.svg +3 -0
- munchboka_edutools/static/icons/polyicons/cubicup.svg +3 -0
- munchboka_edutools/static/icons/polyicons/frown.svg +3 -0
- munchboka_edutools/static/icons/polyicons/smile.svg +3 -0
- munchboka_edutools/static/icons/solid/dark_mode/academic.svg +5 -0
- munchboka_edutools/static/icons/solid/dark_mode/backward.svg +3 -0
- munchboka_edutools/static/icons/solid/dark_mode/book.svg +3 -0
- munchboka_edutools/static/icons/solid/dark_mode/brain.svg +1 -0
- munchboka_edutools/static/icons/solid/dark_mode/file.svg +1 -0
- munchboka_edutools/static/icons/solid/dark_mode/fire.svg +3 -0
- munchboka_edutools/static/icons/solid/dark_mode/key.svg +3 -0
- munchboka_edutools/static/icons/solid/dark_mode/pencil_square.svg +4 -0
- munchboka_edutools/static/icons/solid/dark_mode/play.svg +3 -0
- munchboka_edutools/static/icons/solid/dark_mode/python.svg +1 -0
- munchboka_edutools/static/icons/solid/dark_mode/scroll.svg +1 -0
- munchboka_edutools/static/icons/solid/dark_mode/stop.svg +3 -0
- munchboka_edutools/static/icons/solid/light_mode/academic.svg +5 -0
- munchboka_edutools/static/icons/solid/light_mode/backward.svg +3 -0
- munchboka_edutools/static/icons/solid/light_mode/book.svg +3 -0
- munchboka_edutools/static/icons/solid/light_mode/brain.svg +1 -0
- munchboka_edutools/static/icons/solid/light_mode/file.svg +1 -0
- munchboka_edutools/static/icons/solid/light_mode/fire.svg +3 -0
- munchboka_edutools/static/icons/solid/light_mode/key.svg +3 -0
- munchboka_edutools/static/icons/solid/light_mode/pencil_square.svg +4 -0
- munchboka_edutools/static/icons/solid/light_mode/play.svg +3 -0
- munchboka_edutools/static/icons/solid/light_mode/python.svg +1 -0
- munchboka_edutools/static/icons/solid/light_mode/scroll.svg +1 -0
- munchboka_edutools/static/icons/solid/light_mode/stop.svg +3 -0
- munchboka_edutools/static/icons/stickers/edit.svg +1 -0
- munchboka_edutools/static/icons/stickers/pencil_square.svg +3 -0
- munchboka_edutools/static/js/casThemeManager.js +99 -0
- munchboka_edutools/static/js/escapeRoom/escape-room.js +219 -0
- munchboka_edutools/static/js/geogebra-setup.js +6 -0
- munchboka_edutools/static/js/highlight-init.js +6 -0
- munchboka_edutools/static/js/interactiveCode/codeEditor.js +632 -0
- munchboka_edutools/static/js/interactiveCode/interactiveCodeSetup.js +348 -0
- munchboka_edutools/static/js/interactiveCode/pythonRunner.js +336 -0
- munchboka_edutools/static/js/interactiveCode/turtleCode.js +203 -0
- munchboka_edutools/static/js/interactiveCode/workerManager.js +353 -0
- munchboka_edutools/static/js/jeopardy.js +523 -0
- munchboka_edutools/static/js/pairPuzzle/draggableItem.js +64 -0
- munchboka_edutools/static/js/pairPuzzle/dropZone.js +119 -0
- munchboka_edutools/static/js/pairPuzzle/game.js +160 -0
- munchboka_edutools/static/js/parsons/parsonsPuzzle.js +641 -0
- munchboka_edutools/static/js/popup.js +85 -0
- munchboka_edutools/static/js/quiz.js +422 -0
- munchboka_edutools/static/js/skulpt/skulpt.js +35721 -0
- munchboka_edutools/static/js/timedQuiz/multipleChoiceQuestion.js +184 -0
- munchboka_edutools/static/js/timedQuiz/timedMultipleChoiceQuiz.js +244 -0
- munchboka_edutools/static/js/timedQuiz/utils.js +6 -0
- munchboka_edutools/static/js/utils.js +3 -0
- munchboka_edutools-0.1.13.dist-info/METADATA +108 -0
- munchboka_edutools-0.1.13.dist-info/RECORD +149 -0
- munchboka_edutools-0.1.13.dist-info/WHEEL +4 -0
- munchboka_edutools-0.1.13.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GeoGebra Directive for Munchboka Edutools
|
|
3
|
+
=========================================
|
|
4
|
+
|
|
5
|
+
This directive embeds interactive GeoGebra applets in the documentation.
|
|
6
|
+
It uses the GeoGebra API to create fully functional mathematics tools
|
|
7
|
+
directly in the browser.
|
|
8
|
+
|
|
9
|
+
Usage in MyST Markdown:
|
|
10
|
+
```{ggb} 720 600
|
|
11
|
+
:material_id: abcdef123
|
|
12
|
+
:toolbar: true
|
|
13
|
+
:menubar: true
|
|
14
|
+
:algebra: true
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or with defaults (empty applet):
|
|
18
|
+
```{ggb} 800 600
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or with perspective:
|
|
22
|
+
```{ggb} 720 600
|
|
23
|
+
:material_id: xyz789
|
|
24
|
+
:perspective: graphing
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Features:
|
|
28
|
+
- Embed existing GeoGebra materials by ID
|
|
29
|
+
- Configure toolbar, menubar, and algebra view visibility
|
|
30
|
+
- Set custom dimensions (width × height)
|
|
31
|
+
- Multiple perspective options (graphing, geometry, 3d, etc.)
|
|
32
|
+
- Automatic fullscreen and reset buttons
|
|
33
|
+
- Norwegian language interface
|
|
34
|
+
|
|
35
|
+
Arguments:
|
|
36
|
+
width (optional): Width in pixels (default: 720)
|
|
37
|
+
height (optional): Height in pixels (default: 600)
|
|
38
|
+
|
|
39
|
+
Options:
|
|
40
|
+
material_id (optional): GeoGebra material ID to load
|
|
41
|
+
toolbar (optional): Show toolbar ("true"/"false", default: "false")
|
|
42
|
+
menubar (optional): Show menubar ("true"/"false", default: "false")
|
|
43
|
+
algebra (optional): Show algebra view ("true"/"false", default: "false")
|
|
44
|
+
perspective (optional): Perspective to use (e.g., "graphing", "geometry", "3d")
|
|
45
|
+
|
|
46
|
+
Dependencies:
|
|
47
|
+
- GeoGebra API: Loaded via deployggb.js (already registered in __init__.py)
|
|
48
|
+
- geogebra-setup.js: Theme management and initialization
|
|
49
|
+
|
|
50
|
+
Author: René Aasen (ported from matematikk_r1)
|
|
51
|
+
Date: November 2025
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
from docutils import nodes
|
|
55
|
+
from sphinx.util.docutils import SphinxDirective
|
|
56
|
+
from docutils.parsers.rst import directives
|
|
57
|
+
import uuid
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class GGBDirective(SphinxDirective):
|
|
61
|
+
"""
|
|
62
|
+
Sphinx directive for embedding interactive GeoGebra applets.
|
|
63
|
+
|
|
64
|
+
This directive creates a container div and uses the GeoGebra API
|
|
65
|
+
(deployggb.js) to inject an interactive mathematics applet.
|
|
66
|
+
|
|
67
|
+
Arguments:
|
|
68
|
+
width (optional): Width in pixels (default: 720)
|
|
69
|
+
height (optional): Height in pixels (default: 600)
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
material_id: GeoGebra material ID (e.g., from geogebra.org/m/xyz)
|
|
73
|
+
toolbar: Show toolbar ("true"/"false")
|
|
74
|
+
menubar: Show menubar ("true"/"false")
|
|
75
|
+
algebra: Show algebra view ("true"/"false")
|
|
76
|
+
perspective: Perspective to use ("graphing", "geometry", "3d", etc.)
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
Embed a specific GeoGebra material:
|
|
80
|
+
```{ggb} 720 600
|
|
81
|
+
:material_id: abcdef123
|
|
82
|
+
:toolbar: true
|
|
83
|
+
:menubar: true
|
|
84
|
+
:algebra: true
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Create an empty applet:
|
|
88
|
+
```{ggb} 800 600
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use a specific perspective:
|
|
92
|
+
```{ggb} 720 600
|
|
93
|
+
:material_id: xyz789
|
|
94
|
+
:perspective: graphing
|
|
95
|
+
```
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
required_arguments = 0
|
|
99
|
+
optional_arguments = 2 # width and height
|
|
100
|
+
has_content = False
|
|
101
|
+
|
|
102
|
+
option_spec = {
|
|
103
|
+
"material_id": directives.unchanged,
|
|
104
|
+
"toolbar": directives.unchanged,
|
|
105
|
+
"menubar": directives.unchanged,
|
|
106
|
+
"algebra": directives.unchanged,
|
|
107
|
+
"perspective": directives.unchanged,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
def run(self):
|
|
111
|
+
"""
|
|
112
|
+
Generate the HTML for the GeoGebra applet.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
list: List of docutils nodes (raw HTML node)
|
|
116
|
+
"""
|
|
117
|
+
# Convert arguments to integers (with defaults if conversion fails)
|
|
118
|
+
try:
|
|
119
|
+
width = int(self.arguments[0])
|
|
120
|
+
except (IndexError, ValueError):
|
|
121
|
+
width = 720
|
|
122
|
+
try:
|
|
123
|
+
height = int(self.arguments[1])
|
|
124
|
+
except (IndexError, ValueError):
|
|
125
|
+
height = 600
|
|
126
|
+
|
|
127
|
+
# Get options
|
|
128
|
+
material_id = self.options.get("material_id", None)
|
|
129
|
+
toolbar = self.options.get("toolbar", "false")
|
|
130
|
+
menubar = self.options.get("menubar", "false")
|
|
131
|
+
algebra = self.options.get("algebra", "false")
|
|
132
|
+
perspective = self.options.get("perspective", None)
|
|
133
|
+
|
|
134
|
+
# Format perspective option
|
|
135
|
+
if perspective:
|
|
136
|
+
perspective_option = f"perspective: '{perspective}'"
|
|
137
|
+
else:
|
|
138
|
+
perspective_option = "'': ''"
|
|
139
|
+
|
|
140
|
+
# Format material_id option
|
|
141
|
+
if material_id:
|
|
142
|
+
material_id_option = f"material_id: '{material_id}'"
|
|
143
|
+
else:
|
|
144
|
+
# If no material_id is provided, enable all controls by default
|
|
145
|
+
# This creates an empty applet with full functionality
|
|
146
|
+
material_id_option = "'': ''"
|
|
147
|
+
toolbar = "true"
|
|
148
|
+
menubar = "true"
|
|
149
|
+
algebra = "true"
|
|
150
|
+
|
|
151
|
+
# Generate a unique container ID using a short UUID
|
|
152
|
+
container_id = f"ggb-cas-{uuid.uuid4().hex[:8]}"
|
|
153
|
+
|
|
154
|
+
# Create the raw HTML
|
|
155
|
+
# Note: GeoGebra API (deployggb.js) is already loaded via __init__.py
|
|
156
|
+
html = f"""
|
|
157
|
+
<div id="{container_id}" style="width: {width}px; height: {height}px;" class="ggb-window"></div>
|
|
158
|
+
<script>
|
|
159
|
+
document.addEventListener("DOMContentLoaded", function() {{
|
|
160
|
+
var options = {{
|
|
161
|
+
appName: 'classic',
|
|
162
|
+
width: {width},
|
|
163
|
+
height: {height},
|
|
164
|
+
showToolBar: {toolbar},
|
|
165
|
+
showAlgebraInput: {algebra},
|
|
166
|
+
showMenuBar: {menubar},
|
|
167
|
+
language: 'nb',
|
|
168
|
+
borderRadius: 8,
|
|
169
|
+
borderColor: '#000000',
|
|
170
|
+
showFullscreenButton: true,
|
|
171
|
+
showResetIcon: true,
|
|
172
|
+
scale: 1,
|
|
173
|
+
rounding: 2,
|
|
174
|
+
showKeyboardOnFocus: false,
|
|
175
|
+
preventFocus: true,
|
|
176
|
+
id: '{container_id}',
|
|
177
|
+
{material_id_option},
|
|
178
|
+
{perspective_option},
|
|
179
|
+
}};
|
|
180
|
+
|
|
181
|
+
var applet = new GGBApplet(options, true);
|
|
182
|
+
applet.inject('{container_id}');
|
|
183
|
+
}});
|
|
184
|
+
</script>
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
return [nodes.raw("", html, format="html")]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def setup(app):
|
|
191
|
+
"""
|
|
192
|
+
Setup function to register the directive with Sphinx.
|
|
193
|
+
|
|
194
|
+
This function is called automatically by Sphinx when the extension is loaded.
|
|
195
|
+
It registers the 'ggb' directive for use in documentation.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
app: The Sphinx application instance
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
dict: Extension metadata including version and parallel processing flags
|
|
202
|
+
"""
|
|
203
|
+
app.add_directive("ggb", GGBDirective)
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
"version": "0.1",
|
|
207
|
+
"parallel_read_safe": True,
|
|
208
|
+
"parallel_write_safe": True,
|
|
209
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GeoGebra icon role for inline SVG icons.
|
|
3
|
+
|
|
4
|
+
This role allows you to insert GeoGebra tool icons inline in text.
|
|
5
|
+
The icons are SVG files that represent various GeoGebra tools and modes.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
This is the {ggb-icon}`mode_intersect` tool in GeoGebra.
|
|
9
|
+
|
|
10
|
+
Click the {ggb-icon}`mode_solve` icon to solve equations.
|
|
11
|
+
|
|
12
|
+
Available icons:
|
|
13
|
+
- mode_evaluate: Evaluate/compute icon
|
|
14
|
+
- mode_extremum: Find extremum (min/max) icon
|
|
15
|
+
- mode_intersect: Find intersection point icon
|
|
16
|
+
- mode_nsolve: Numeric solve icon
|
|
17
|
+
- mode_numeric: Numeric computation icon
|
|
18
|
+
- mode_point: Point tool icon
|
|
19
|
+
- mode_solve: Symbolic solve icon
|
|
20
|
+
|
|
21
|
+
The icons are rendered as inline images with appropriate alt text.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from docutils import nodes
|
|
25
|
+
from docutils.parsers.rst import roles
|
|
26
|
+
from sphinx.util.osutil import relative_uri
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Custom node for GeoGebra icons
|
|
30
|
+
class ggb_icon_node(nodes.Inline, nodes.Element):
|
|
31
|
+
"""Custom node for GeoGebra icons."""
|
|
32
|
+
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def ggb_icon_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
|
37
|
+
"""
|
|
38
|
+
Custom role for GeoGebra icons.
|
|
39
|
+
|
|
40
|
+
Usage: {ggb-icon}`mode_intersect`
|
|
41
|
+
|
|
42
|
+
This generates a custom node that will be rendered with relative paths.
|
|
43
|
+
"""
|
|
44
|
+
# Clean up the icon name (remove any extra whitespace)
|
|
45
|
+
icon_name = text.strip()
|
|
46
|
+
|
|
47
|
+
# Create our custom node
|
|
48
|
+
node = ggb_icon_node()
|
|
49
|
+
node["icon_name"] = icon_name
|
|
50
|
+
node["classes"] = ["inline-image"]
|
|
51
|
+
|
|
52
|
+
return [node], []
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def visit_ggb_icon_html(self, node):
|
|
56
|
+
"""HTML visitor for ggb_icon_node - generates relative path."""
|
|
57
|
+
icon_name = node["icon_name"]
|
|
58
|
+
|
|
59
|
+
# Get the relative path from current document to _static directory
|
|
60
|
+
# self.builder.current_docname is like "examples/ggb_icon"
|
|
61
|
+
# We want path from there to ../static/munchboka/icons/ggb/
|
|
62
|
+
from os.path import relpath, dirname, join
|
|
63
|
+
|
|
64
|
+
# Current document directory depth
|
|
65
|
+
doc_dir = dirname(self.builder.current_docname) if "/" in self.builder.current_docname else ""
|
|
66
|
+
|
|
67
|
+
# Calculate relative path
|
|
68
|
+
if doc_dir:
|
|
69
|
+
# For documents in subdirectories like "examples/ggb_icon"
|
|
70
|
+
depth = doc_dir.count("/") + 1
|
|
71
|
+
rel_prefix = "../" * depth
|
|
72
|
+
else:
|
|
73
|
+
# For top-level documents
|
|
74
|
+
rel_prefix = ""
|
|
75
|
+
|
|
76
|
+
img_path = f"{rel_prefix}_static/munchboka/icons/ggb/{icon_name}.svg"
|
|
77
|
+
|
|
78
|
+
# Generate the HTML with relative path
|
|
79
|
+
html = f'<img src="{img_path}" alt="GeoGebra {icon_name} icon" class="inline-image" />'
|
|
80
|
+
self.body.append(html)
|
|
81
|
+
raise nodes.SkipNode
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def depart_ggb_icon_html(self, node):
|
|
85
|
+
"""Depart function (not needed as we raise SkipNode)."""
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def setup(app):
|
|
90
|
+
"""Setup function to register the role with Sphinx."""
|
|
91
|
+
# Register the custom node
|
|
92
|
+
app.add_node(
|
|
93
|
+
ggb_icon_node,
|
|
94
|
+
html=(visit_ggb_icon_html, depart_ggb_icon_html),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Register the role with both hyphenated and unhyphenated names
|
|
98
|
+
roles.register_local_role("ggb-icon", ggb_icon_role)
|
|
99
|
+
roles.register_local_role("ggbicon", ggb_icon_role)
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
"version": "0.1.0",
|
|
103
|
+
"parallel_read_safe": True,
|
|
104
|
+
"parallel_write_safe": True,
|
|
105
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GeoGebra popup directive for creating interactive GeoGebra applets in dialog windows.
|
|
3
|
+
|
|
4
|
+
This directive creates a button that opens a GeoGebra Classic applet in a
|
|
5
|
+
draggable, resizable dialog window using jQuery UI.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
Basic usage with defaults (700x500 window):
|
|
9
|
+
```{ggb-popup}
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Custom size and text:
|
|
13
|
+
```{ggb-popup} 800 600 "Open Calculator" "GeoGebra Calculator"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
With options:
|
|
17
|
+
```{ggb-popup} 900 700 "Open Geometry" "Geometry Window"
|
|
18
|
+
:perspective: G
|
|
19
|
+
:menubar: true
|
|
20
|
+
:layout: sidebar
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Arguments (all optional):
|
|
24
|
+
1. Width (default: 700) - Width of the GeoGebra applet in pixels
|
|
25
|
+
2. Height (default: 500) - Height of the GeoGebra applet in pixels
|
|
26
|
+
3. Button text (default: "Åpne Geogebra‑vindu") - Text shown on the button
|
|
27
|
+
4. Dialog title (default: "Geogebra‑vindu") - Title of the dialog window
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
- perspective: GeoGebra perspective/view (default: "AG")
|
|
31
|
+
Common values: "AG" (Algebra & Graphics), "G" (Geometry), "GS" (Graphing), "CAS"
|
|
32
|
+
- menubar: Show menu bar (default: "false") - "true" or "false"
|
|
33
|
+
- layout: Layout style (default: none) - Use "sidebar" to wrap in sidebar-cas div
|
|
34
|
+
|
|
35
|
+
Features:
|
|
36
|
+
- Draggable and resizable dialog window
|
|
37
|
+
- GeoGebra Classic applet with full features
|
|
38
|
+
- Responsive sizing when dialog is resized
|
|
39
|
+
- Centered on screen when opened
|
|
40
|
+
- jQuery UI styling
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
from docutils import nodes
|
|
44
|
+
from sphinx.util.docutils import SphinxDirective
|
|
45
|
+
import uuid
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class GGBPopUpDirective(SphinxDirective):
|
|
49
|
+
"""
|
|
50
|
+
Directive for creating GeoGebra popup windows.
|
|
51
|
+
|
|
52
|
+
Creates a button that opens a GeoGebra Classic applet in a jQuery UI dialog.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
required_arguments = 0
|
|
56
|
+
optional_arguments = 4 # width, height, button text, dialog title
|
|
57
|
+
final_argument_whitespace = True
|
|
58
|
+
has_content = False
|
|
59
|
+
option_spec = {
|
|
60
|
+
"layout": lambda arg: arg, # e.g., "sidebar"
|
|
61
|
+
"menubar": lambda arg: arg, # e.g., "true" or "false"
|
|
62
|
+
"perspective": lambda arg: arg, # e.g., "GS"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
def run(self):
|
|
66
|
+
"""Generate HTML for the GeoGebra popup."""
|
|
67
|
+
# 1 » Parse arguments
|
|
68
|
+
width = int(self.arguments[0]) if len(self.arguments) > 0 else 700
|
|
69
|
+
height = int(self.arguments[1]) if len(self.arguments) > 1 else 500
|
|
70
|
+
|
|
71
|
+
button_text = self.arguments[2] if len(self.arguments) > 2 else "Åpne Geogebra‑vindu"
|
|
72
|
+
dialog_title = self.arguments[3] if len(self.arguments) > 3 else "Geogebra‑vindu"
|
|
73
|
+
|
|
74
|
+
menubar = self.options.get("menubar", "false")
|
|
75
|
+
|
|
76
|
+
perspective = self.options.get("perspective", "AG").strip()
|
|
77
|
+
|
|
78
|
+
# 2 » Generate unique IDs
|
|
79
|
+
cid = f"ggb-geogebra-{uuid.uuid4().hex[:8]}"
|
|
80
|
+
dialog_id = f"dialog-{cid}"
|
|
81
|
+
button_id = f"button-{cid}"
|
|
82
|
+
|
|
83
|
+
# 3 » Handle layout option
|
|
84
|
+
layout = self.options.get("layout", "").strip().lower()
|
|
85
|
+
use_sidebar = layout == "sidebar"
|
|
86
|
+
|
|
87
|
+
wrapper_start = '<div class="sidebar-cas">' if use_sidebar else ""
|
|
88
|
+
wrapper_end = "</div>" if use_sidebar else ""
|
|
89
|
+
|
|
90
|
+
# 4 » Generate HTML content
|
|
91
|
+
html = f"""
|
|
92
|
+
{wrapper_start}
|
|
93
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
94
|
+
|
|
95
|
+
<button id="{button_id}" class="ggb-cas-button">{button_text}</button>
|
|
96
|
+
<div id="{dialog_id}" title="{dialog_title}" style="display:none;">
|
|
97
|
+
<div id="{cid}" class="ggb-window"></div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<style>
|
|
101
|
+
.ui-resizable-handle {{ min-width:16px;min-height:16px; }}
|
|
102
|
+
.ui-dialog-content{{padding:0!important;}}
|
|
103
|
+
.ggb-window {{width:100%!important;height:100%!important;box-sizing:border-box;}}
|
|
104
|
+
.ggb-cas-button {{margin-top: 1em; margin-bottom: 1em;}}
|
|
105
|
+
</style>
|
|
106
|
+
|
|
107
|
+
<script>
|
|
108
|
+
(function() {{
|
|
109
|
+
$(function() {{
|
|
110
|
+
let ggbReady = false;
|
|
111
|
+
|
|
112
|
+
function applySize() {{
|
|
113
|
+
if (!ggbReady) return;
|
|
114
|
+
const w = $("#{cid}").width(),
|
|
115
|
+
h = $("#{cid}").height();
|
|
116
|
+
window.ggbApplet.setSize(Math.round(w), Math.round(h));
|
|
117
|
+
}}
|
|
118
|
+
|
|
119
|
+
$("#{dialog_id}").dialog({{
|
|
120
|
+
autoOpen: false,
|
|
121
|
+
width: {width+40}, height: {height+80},
|
|
122
|
+
resizable: true, draggable: true,
|
|
123
|
+
position: {{ my: "center", at: "center", of: window }},
|
|
124
|
+
resize: () => window.requestAnimationFrame(applySize),
|
|
125
|
+
open: function() {{
|
|
126
|
+
if (!ggbReady) {{
|
|
127
|
+
new GGBApplet({{
|
|
128
|
+
appName: "classic", id: "{cid}",
|
|
129
|
+
width: {width}, height: {height},
|
|
130
|
+
perspective: "{perspective}", language: "nb",
|
|
131
|
+
showToolBar: true, showAlgebraInput: true,
|
|
132
|
+
borderRadius: 8, enableRightClick: true, showKeyboardOnFocus: false,
|
|
133
|
+
showMenuBar: {menubar},
|
|
134
|
+
appletOnLoad: () => {{ ggbReady = true; applySize(); }}
|
|
135
|
+
}}, true).inject("{cid}");
|
|
136
|
+
}} else {{
|
|
137
|
+
applySize();
|
|
138
|
+
}}
|
|
139
|
+
}}
|
|
140
|
+
}});
|
|
141
|
+
|
|
142
|
+
$("#{button_id}").button()
|
|
143
|
+
.on("click touchend pointerup", e => {{
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
$("#{dialog_id}").dialog("open");
|
|
146
|
+
}});
|
|
147
|
+
}});
|
|
148
|
+
}})();
|
|
149
|
+
</script>
|
|
150
|
+
{wrapper_end}
|
|
151
|
+
"""
|
|
152
|
+
return [nodes.raw("", html, format="html")]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def setup(app):
|
|
156
|
+
"""Register the ggb-popup directive with Sphinx."""
|
|
157
|
+
app.add_directive("ggb-popup", GGBPopUpDirective)
|
|
158
|
+
# Also register without hyphen for MyST compatibility
|
|
159
|
+
app.add_directive("ggbpopup", GGBPopUpDirective)
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
"version": "0.1.0",
|
|
163
|
+
"parallel_read_safe": True,
|
|
164
|
+
"parallel_write_safe": True,
|
|
165
|
+
}
|