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.

Files changed (149) hide show
  1. munchboka_edutools/__init__.py +184 -0
  2. munchboka_edutools/_plotmath_shim.py +126 -0
  3. munchboka_edutools/_version.py +2 -0
  4. munchboka_edutools/directives/__init__.py +1 -0
  5. munchboka_edutools/directives/admonitions.py +389 -0
  6. munchboka_edutools/directives/cas_popup.py +272 -0
  7. munchboka_edutools/directives/clear.py +103 -0
  8. munchboka_edutools/directives/dialogue.py +137 -0
  9. munchboka_edutools/directives/escape_room.py +296 -0
  10. munchboka_edutools/directives/factor_tree.py +549 -0
  11. munchboka_edutools/directives/ggb.py +209 -0
  12. munchboka_edutools/directives/ggb_icon.py +105 -0
  13. munchboka_edutools/directives/ggb_popup.py +165 -0
  14. munchboka_edutools/directives/horner.py +324 -0
  15. munchboka_edutools/directives/interactive_code.py +75 -0
  16. munchboka_edutools/directives/jeopardy.py +252 -0
  17. munchboka_edutools/directives/multi_plot.py +1126 -0
  18. munchboka_edutools/directives/pair_puzzle.py +191 -0
  19. munchboka_edutools/directives/parsons.py +109 -0
  20. munchboka_edutools/directives/plot.py +3105 -0
  21. munchboka_edutools/directives/poly_icon.py +111 -0
  22. munchboka_edutools/directives/polydiv.py +344 -0
  23. munchboka_edutools/directives/popup.py +245 -0
  24. munchboka_edutools/directives/quiz.py +291 -0
  25. munchboka_edutools/directives/signchart.py +516 -0
  26. munchboka_edutools/directives/timed_quiz.py +436 -0
  27. munchboka_edutools/directives/turtle.py +157 -0
  28. munchboka_edutools/static/css/admonitions.css +2012 -0
  29. munchboka_edutools/static/css/cas_popup.css +242 -0
  30. munchboka_edutools/static/css/code_mirror_themes/github_dark_cm.css +112 -0
  31. munchboka_edutools/static/css/code_mirror_themes/github_dark_default_cm.css +40 -0
  32. munchboka_edutools/static/css/code_mirror_themes/github_dark_high_contrast_cm.css +141 -0
  33. munchboka_edutools/static/css/code_mirror_themes/github_light_cm.css +120 -0
  34. munchboka_edutools/static/css/code_mirror_themes/github_light_default_cm.css +108 -0
  35. munchboka_edutools/static/css/code_mirror_themes/one_dark_cm.css +121 -0
  36. munchboka_edutools/static/css/dialogue.css +92 -0
  37. munchboka_edutools/static/css/escapeRoom/escape-room.css +223 -0
  38. munchboka_edutools/static/css/figures.css +274 -0
  39. munchboka_edutools/static/css/general_style.css +74 -0
  40. munchboka_edutools/static/css/github-dark-high-contrast.css +141 -0
  41. munchboka_edutools/static/css/github-dark.css +112 -0
  42. munchboka_edutools/static/css/github-light.css +120 -0
  43. munchboka_edutools/static/css/interactive_code/style.css +575 -0
  44. munchboka_edutools/static/css/interactive_code.css +582 -0
  45. munchboka_edutools/static/css/jeopardy.css +529 -0
  46. munchboka_edutools/static/css/pairPuzzle/style.css +177 -0
  47. munchboka_edutools/static/css/parsons/parsonsPuzzle.css +331 -0
  48. munchboka_edutools/static/css/popup.css +115 -0
  49. munchboka_edutools/static/css/quiz.css +312 -0
  50. munchboka_edutools/static/css/timedQuiz.css +375 -0
  51. munchboka_edutools/static/icons/ggb/mode_evaluate.svg +1 -0
  52. munchboka_edutools/static/icons/ggb/mode_extremum.svg +1 -0
  53. munchboka_edutools/static/icons/ggb/mode_intersect.svg +1 -0
  54. munchboka_edutools/static/icons/ggb/mode_nsolve.svg +1 -0
  55. munchboka_edutools/static/icons/ggb/mode_numeric.svg +1 -0
  56. munchboka_edutools/static/icons/ggb/mode_point.svg +1 -0
  57. munchboka_edutools/static/icons/ggb/mode_solve.svg +1 -0
  58. munchboka_edutools/static/icons/misc/windows-logo.svg +1 -0
  59. munchboka_edutools/static/icons/outline/dark_mode/academic.svg +3 -0
  60. munchboka_edutools/static/icons/outline/dark_mode/backward.svg +3 -0
  61. munchboka_edutools/static/icons/outline/dark_mode/book.svg +3 -0
  62. munchboka_edutools/static/icons/outline/dark_mode/chat_bubble.svg +3 -0
  63. munchboka_edutools/static/icons/outline/dark_mode/check.svg +3 -0
  64. munchboka_edutools/static/icons/outline/dark_mode/cmd_line.svg +3 -0
  65. munchboka_edutools/static/icons/outline/dark_mode/file.svg +1 -0
  66. munchboka_edutools/static/icons/outline/dark_mode/fire.svg +4 -0
  67. munchboka_edutools/static/icons/outline/dark_mode/key.svg +3 -0
  68. munchboka_edutools/static/icons/outline/dark_mode/magnifying.svg +3 -0
  69. munchboka_edutools/static/icons/outline/dark_mode/pencil_square.svg +3 -0
  70. munchboka_edutools/static/icons/outline/dark_mode/play.svg +3 -0
  71. munchboka_edutools/static/icons/outline/dark_mode/question.svg +3 -0
  72. munchboka_edutools/static/icons/outline/dark_mode/square_check.svg +1 -0
  73. munchboka_edutools/static/icons/outline/dark_mode/stop.svg +3 -0
  74. munchboka_edutools/static/icons/outline/dark_mode/summary.svg +3 -0
  75. munchboka_edutools/static/icons/outline/dark_mode/undo.svg +3 -0
  76. munchboka_edutools/static/icons/outline/dark_mode/unlock.svg +3 -0
  77. munchboka_edutools/static/icons/outline/light_mode/academic.svg +3 -0
  78. munchboka_edutools/static/icons/outline/light_mode/backward.svg +3 -0
  79. munchboka_edutools/static/icons/outline/light_mode/book.svg +3 -0
  80. munchboka_edutools/static/icons/outline/light_mode/chat_bubble.svg +3 -0
  81. munchboka_edutools/static/icons/outline/light_mode/check.svg +3 -0
  82. munchboka_edutools/static/icons/outline/light_mode/cmd_line.svg +3 -0
  83. munchboka_edutools/static/icons/outline/light_mode/file.svg +1 -0
  84. munchboka_edutools/static/icons/outline/light_mode/fire.svg +4 -0
  85. munchboka_edutools/static/icons/outline/light_mode/key.svg +3 -0
  86. munchboka_edutools/static/icons/outline/light_mode/magnifying.svg +3 -0
  87. munchboka_edutools/static/icons/outline/light_mode/pencil_square.svg +3 -0
  88. munchboka_edutools/static/icons/outline/light_mode/play.svg +3 -0
  89. munchboka_edutools/static/icons/outline/light_mode/question.svg +3 -0
  90. munchboka_edutools/static/icons/outline/light_mode/square_check.svg +1 -0
  91. munchboka_edutools/static/icons/outline/light_mode/stop.svg +3 -0
  92. munchboka_edutools/static/icons/outline/light_mode/summary.svg +3 -0
  93. munchboka_edutools/static/icons/outline/light_mode/undo.svg +3 -0
  94. munchboka_edutools/static/icons/outline/light_mode/unlock.svg +3 -0
  95. munchboka_edutools/static/icons/polyicons/cubicdown.svg +3 -0
  96. munchboka_edutools/static/icons/polyicons/cubicup.svg +3 -0
  97. munchboka_edutools/static/icons/polyicons/frown.svg +3 -0
  98. munchboka_edutools/static/icons/polyicons/smile.svg +3 -0
  99. munchboka_edutools/static/icons/solid/dark_mode/academic.svg +5 -0
  100. munchboka_edutools/static/icons/solid/dark_mode/backward.svg +3 -0
  101. munchboka_edutools/static/icons/solid/dark_mode/book.svg +3 -0
  102. munchboka_edutools/static/icons/solid/dark_mode/brain.svg +1 -0
  103. munchboka_edutools/static/icons/solid/dark_mode/file.svg +1 -0
  104. munchboka_edutools/static/icons/solid/dark_mode/fire.svg +3 -0
  105. munchboka_edutools/static/icons/solid/dark_mode/key.svg +3 -0
  106. munchboka_edutools/static/icons/solid/dark_mode/pencil_square.svg +4 -0
  107. munchboka_edutools/static/icons/solid/dark_mode/play.svg +3 -0
  108. munchboka_edutools/static/icons/solid/dark_mode/python.svg +1 -0
  109. munchboka_edutools/static/icons/solid/dark_mode/scroll.svg +1 -0
  110. munchboka_edutools/static/icons/solid/dark_mode/stop.svg +3 -0
  111. munchboka_edutools/static/icons/solid/light_mode/academic.svg +5 -0
  112. munchboka_edutools/static/icons/solid/light_mode/backward.svg +3 -0
  113. munchboka_edutools/static/icons/solid/light_mode/book.svg +3 -0
  114. munchboka_edutools/static/icons/solid/light_mode/brain.svg +1 -0
  115. munchboka_edutools/static/icons/solid/light_mode/file.svg +1 -0
  116. munchboka_edutools/static/icons/solid/light_mode/fire.svg +3 -0
  117. munchboka_edutools/static/icons/solid/light_mode/key.svg +3 -0
  118. munchboka_edutools/static/icons/solid/light_mode/pencil_square.svg +4 -0
  119. munchboka_edutools/static/icons/solid/light_mode/play.svg +3 -0
  120. munchboka_edutools/static/icons/solid/light_mode/python.svg +1 -0
  121. munchboka_edutools/static/icons/solid/light_mode/scroll.svg +1 -0
  122. munchboka_edutools/static/icons/solid/light_mode/stop.svg +3 -0
  123. munchboka_edutools/static/icons/stickers/edit.svg +1 -0
  124. munchboka_edutools/static/icons/stickers/pencil_square.svg +3 -0
  125. munchboka_edutools/static/js/casThemeManager.js +99 -0
  126. munchboka_edutools/static/js/escapeRoom/escape-room.js +219 -0
  127. munchboka_edutools/static/js/geogebra-setup.js +6 -0
  128. munchboka_edutools/static/js/highlight-init.js +6 -0
  129. munchboka_edutools/static/js/interactiveCode/codeEditor.js +632 -0
  130. munchboka_edutools/static/js/interactiveCode/interactiveCodeSetup.js +348 -0
  131. munchboka_edutools/static/js/interactiveCode/pythonRunner.js +336 -0
  132. munchboka_edutools/static/js/interactiveCode/turtleCode.js +203 -0
  133. munchboka_edutools/static/js/interactiveCode/workerManager.js +353 -0
  134. munchboka_edutools/static/js/jeopardy.js +523 -0
  135. munchboka_edutools/static/js/pairPuzzle/draggableItem.js +64 -0
  136. munchboka_edutools/static/js/pairPuzzle/dropZone.js +119 -0
  137. munchboka_edutools/static/js/pairPuzzle/game.js +160 -0
  138. munchboka_edutools/static/js/parsons/parsonsPuzzle.js +641 -0
  139. munchboka_edutools/static/js/popup.js +85 -0
  140. munchboka_edutools/static/js/quiz.js +422 -0
  141. munchboka_edutools/static/js/skulpt/skulpt.js +35721 -0
  142. munchboka_edutools/static/js/timedQuiz/multipleChoiceQuestion.js +184 -0
  143. munchboka_edutools/static/js/timedQuiz/timedMultipleChoiceQuiz.js +244 -0
  144. munchboka_edutools/static/js/timedQuiz/utils.js +6 -0
  145. munchboka_edutools/static/js/utils.js +3 -0
  146. munchboka_edutools-0.1.13.dist-info/METADATA +108 -0
  147. munchboka_edutools-0.1.13.dist-info/RECORD +149 -0
  148. munchboka_edutools-0.1.13.dist-info/WHEEL +4 -0
  149. 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
+ }