hyperbook 0.37.0 → 0.38.1
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.
|
@@ -0,0 +1,1015 @@
|
|
|
1
|
+
function capitalise([first, ...rest]) {
|
|
2
|
+
return first.toUpperCase() + rest.join("");
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function clean_attribute(v) {
|
|
6
|
+
if (v && !isNaN(v)) {
|
|
7
|
+
v = parseFloat(v);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const constants = {
|
|
11
|
+
true: true,
|
|
12
|
+
on: true,
|
|
13
|
+
false: false,
|
|
14
|
+
off: false,
|
|
15
|
+
"": true,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return constants[v] === undefined ? v : constants[v];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function splitlist(v) {
|
|
22
|
+
return v.split(/\s*,\s*/g);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function clean_material_id(material_id) {
|
|
26
|
+
if (!material_id) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let m;
|
|
30
|
+
if (
|
|
31
|
+
(m = material_id.match(
|
|
32
|
+
/(?:(?:beta.)?geogebra.org\/(?:[a-zA-Z0-9]+)|ggbm.at)\/([a-zA-Z0-9]+)$/,
|
|
33
|
+
))
|
|
34
|
+
) {
|
|
35
|
+
material_id = m[1];
|
|
36
|
+
}
|
|
37
|
+
return material_id;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Resolve an enum: either return the index of the value in the list, or the original value if it's not in the list.
|
|
41
|
+
*/
|
|
42
|
+
function enum_attribute(v, values) {
|
|
43
|
+
const i = values.indexOf(v);
|
|
44
|
+
return i >= 0 ? i : v;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Resolve a map of attribute values: either return the value corresponding to the given key, or the original value if it's not in the map.
|
|
48
|
+
*/
|
|
49
|
+
function map_attribute(raw_value, values) {
|
|
50
|
+
const mapped_value = values[raw_value];
|
|
51
|
+
const value = mapped_value === undefined ? raw_value : mapped_value;
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const mode_map = {
|
|
56
|
+
move: 0,
|
|
57
|
+
point: 1,
|
|
58
|
+
join: 2,
|
|
59
|
+
parallel: 3,
|
|
60
|
+
orthogonal: 4,
|
|
61
|
+
intersect: 5,
|
|
62
|
+
delete: 6,
|
|
63
|
+
vector: 7,
|
|
64
|
+
"line bisector": 8,
|
|
65
|
+
"angular bisector": 9,
|
|
66
|
+
"circle two points": 10,
|
|
67
|
+
"circle three points": 11,
|
|
68
|
+
"conic five points": 12,
|
|
69
|
+
tangents: 13,
|
|
70
|
+
relation: 14,
|
|
71
|
+
segment: 15,
|
|
72
|
+
polygon: 16,
|
|
73
|
+
text: 17,
|
|
74
|
+
ray: 18,
|
|
75
|
+
midpoint: 19,
|
|
76
|
+
"circle arc three points": 20,
|
|
77
|
+
"circle sector three points": 21,
|
|
78
|
+
"circumcircle arc three points": 22,
|
|
79
|
+
"circumcircle sector three points": 23,
|
|
80
|
+
semicircle: 24,
|
|
81
|
+
slider: 25,
|
|
82
|
+
image: 26,
|
|
83
|
+
"show hide object": 27,
|
|
84
|
+
"show hide label": 28,
|
|
85
|
+
"mirror at point": 29,
|
|
86
|
+
"mirror at line": 30,
|
|
87
|
+
"translate by vector": 31,
|
|
88
|
+
"rotate by angle": 32,
|
|
89
|
+
"dilate from point": 33,
|
|
90
|
+
"circle point radius": 34,
|
|
91
|
+
"copy visual style": 35,
|
|
92
|
+
angle: 36,
|
|
93
|
+
"vector from point": 37,
|
|
94
|
+
distance: 38,
|
|
95
|
+
"move rotate": 39,
|
|
96
|
+
translateview: 40,
|
|
97
|
+
"zoom in": 41,
|
|
98
|
+
"zoom out": 42,
|
|
99
|
+
"selection listener": 43,
|
|
100
|
+
"polar diameter": 44,
|
|
101
|
+
"segment fixed": 45,
|
|
102
|
+
"angle fixed": 46,
|
|
103
|
+
locus: 47,
|
|
104
|
+
macro: 48,
|
|
105
|
+
area: 49,
|
|
106
|
+
slope: 50,
|
|
107
|
+
"regular polygon": 51,
|
|
108
|
+
"show hide checkbox": 52,
|
|
109
|
+
compasses: 53,
|
|
110
|
+
"mirror at circle": 54,
|
|
111
|
+
"ellipse three points": 55,
|
|
112
|
+
"hyperbola three points": 56,
|
|
113
|
+
parabola: 57,
|
|
114
|
+
fitline: 58,
|
|
115
|
+
"record to spreadsheet": 59,
|
|
116
|
+
"button action": 60,
|
|
117
|
+
"textfield action": 61,
|
|
118
|
+
pen: 62,
|
|
119
|
+
"rigid polygon": 64,
|
|
120
|
+
polyline: 65,
|
|
121
|
+
"probability calculator": 66,
|
|
122
|
+
"attach / detach": 67,
|
|
123
|
+
"function inspector": 68,
|
|
124
|
+
"intersect two surfaces": 69,
|
|
125
|
+
"vector polygon": 70,
|
|
126
|
+
"create list": 71,
|
|
127
|
+
"complex number": 72,
|
|
128
|
+
"point on object": 501,
|
|
129
|
+
"mode spreadsheet create list": 2001,
|
|
130
|
+
"mode spreadsheet create matrix": 2002,
|
|
131
|
+
"mode spreadsheet create listofpoints": 2003,
|
|
132
|
+
"mode spreadsheet create tabletext": 2004,
|
|
133
|
+
"mode spreadsheet create polyline": 2005,
|
|
134
|
+
"mode spreadsheet onevarstats": 2020,
|
|
135
|
+
"mode spreadsheet twovarstats": 2021,
|
|
136
|
+
"mode spreadsheet multivarstats": 2022,
|
|
137
|
+
"mode spreadsheet sort": 2030,
|
|
138
|
+
"mode spreadsheet sort az": 2031,
|
|
139
|
+
"mode spreadsheet sort za": 2032,
|
|
140
|
+
"mode spreadsheet sum": 2040,
|
|
141
|
+
"mode spreadsheet average": 2041,
|
|
142
|
+
"mode spreadsheet count": 2042,
|
|
143
|
+
"mode spreadsheet min": 2043,
|
|
144
|
+
"mode spreadsheet max": 2044,
|
|
145
|
+
"freehand mode": 73,
|
|
146
|
+
"view in front of": 502,
|
|
147
|
+
"plane three points": 510,
|
|
148
|
+
"plane point line": 511,
|
|
149
|
+
"orthogonal plane": 512,
|
|
150
|
+
"parallel plane": 513,
|
|
151
|
+
"perpendicular line (3d)": 514,
|
|
152
|
+
"sphere point radius": 520,
|
|
153
|
+
"sphere two points": 521,
|
|
154
|
+
"cone given by two points and radius": 522,
|
|
155
|
+
"cylinder given by two points and radius": 523,
|
|
156
|
+
prism: 531,
|
|
157
|
+
"extrude to prism or cylinder": 532,
|
|
158
|
+
pyramid: 533,
|
|
159
|
+
"extrude to pyramid or cone": 534,
|
|
160
|
+
net: 535,
|
|
161
|
+
cube: 536,
|
|
162
|
+
tetrahedron: 537,
|
|
163
|
+
"rotate view": 540,
|
|
164
|
+
"circle point radius direction": 550,
|
|
165
|
+
"circle axis point": 551,
|
|
166
|
+
volume: 560,
|
|
167
|
+
"rotate around line": 570,
|
|
168
|
+
"mirror at plane": 571,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/** From https://wiki.geogebra.org/en/Reference:GeoGebra_Apps_API
|
|
172
|
+
*/
|
|
173
|
+
const method_names = [
|
|
174
|
+
"debug",
|
|
175
|
+
"deleteObject",
|
|
176
|
+
"enable3D",
|
|
177
|
+
"enableCAS",
|
|
178
|
+
"enableLabelDrags",
|
|
179
|
+
"enableRightClick",
|
|
180
|
+
"enableShiftDragZoom",
|
|
181
|
+
"evalCommand",
|
|
182
|
+
"evalCommandCAS",
|
|
183
|
+
"evalCommandGetLabels",
|
|
184
|
+
"evalXML",
|
|
185
|
+
"exists",
|
|
186
|
+
"exportPDF",
|
|
187
|
+
"exportSVG",
|
|
188
|
+
"getAlgorithmXML",
|
|
189
|
+
"getAllObjectNames",
|
|
190
|
+
"getBase64",
|
|
191
|
+
"getCASObjectNumber",
|
|
192
|
+
"getCaption",
|
|
193
|
+
"getColor",
|
|
194
|
+
"getCommandString",
|
|
195
|
+
"getDefinitionString",
|
|
196
|
+
"getEditorState",
|
|
197
|
+
"getFileJSON",
|
|
198
|
+
"getFilling",
|
|
199
|
+
"getGraphicsOptions",
|
|
200
|
+
"getGridVisible",
|
|
201
|
+
"getLaTeXBase64",
|
|
202
|
+
"getLaTeXString",
|
|
203
|
+
"getLabelStyle",
|
|
204
|
+
"getLabelVisible",
|
|
205
|
+
"getLayer",
|
|
206
|
+
"getLineStyle",
|
|
207
|
+
"getLineThickness",
|
|
208
|
+
"getListValue",
|
|
209
|
+
"getMode",
|
|
210
|
+
"getObjectName",
|
|
211
|
+
"getObjectNumber",
|
|
212
|
+
"getObjectType",
|
|
213
|
+
"getPNGBase64",
|
|
214
|
+
"getPerspectiveXML",
|
|
215
|
+
"getPointSize",
|
|
216
|
+
"getPointStyle",
|
|
217
|
+
"getScreenshotBase64",
|
|
218
|
+
"getValue",
|
|
219
|
+
"getValueString",
|
|
220
|
+
"getVersion",
|
|
221
|
+
"getVisible",
|
|
222
|
+
"getXML",
|
|
223
|
+
"getXcoord",
|
|
224
|
+
"getYcoord",
|
|
225
|
+
"getZcoord",
|
|
226
|
+
"hideCursorWhenDragging",
|
|
227
|
+
"isAnimationRunning",
|
|
228
|
+
"isDefined",
|
|
229
|
+
"isIndependent",
|
|
230
|
+
"isMoveable",
|
|
231
|
+
"newConstruction",
|
|
232
|
+
"openFile",
|
|
233
|
+
"recalculateEnvironments",
|
|
234
|
+
"redo",
|
|
235
|
+
"refreshViews",
|
|
236
|
+
"registerAddListener",
|
|
237
|
+
"registerClearListener",
|
|
238
|
+
"registerClickListener",
|
|
239
|
+
"registerClientListener",
|
|
240
|
+
"registerObjectClickListener",
|
|
241
|
+
"registerObjectUpdateListener",
|
|
242
|
+
"registerRemoveListener",
|
|
243
|
+
"registerRenameListener",
|
|
244
|
+
"registerStoreUndoListener\n",
|
|
245
|
+
"registerUpdateListener",
|
|
246
|
+
"remove",
|
|
247
|
+
"renameObject",
|
|
248
|
+
"reset",
|
|
249
|
+
"setAlgebraOptions",
|
|
250
|
+
"setAnimating",
|
|
251
|
+
"setAnimationSpeed",
|
|
252
|
+
"setAuxiliary",
|
|
253
|
+
"setAxesVisible",
|
|
254
|
+
"setAxisLabels",
|
|
255
|
+
"setAxisSteps",
|
|
256
|
+
"setAxisUnits",
|
|
257
|
+
"setBase64",
|
|
258
|
+
"setCaption",
|
|
259
|
+
"setColor",
|
|
260
|
+
"setCoordSystem",
|
|
261
|
+
"setCoords",
|
|
262
|
+
"setCustomToolBar",
|
|
263
|
+
"setDisplayStyle",
|
|
264
|
+
"setEditorState",
|
|
265
|
+
"setErrorDialogsActive",
|
|
266
|
+
"setFileJSON",
|
|
267
|
+
"setFilling",
|
|
268
|
+
"setFixed",
|
|
269
|
+
"setGraphicsOptions",
|
|
270
|
+
"setGridVisible",
|
|
271
|
+
"setHeight",
|
|
272
|
+
"setLabelStyle",
|
|
273
|
+
"setLabelVisible",
|
|
274
|
+
"setLayer",
|
|
275
|
+
"setLayerVisible",
|
|
276
|
+
"setLineStyle",
|
|
277
|
+
"setLineThickness",
|
|
278
|
+
"setListValue",
|
|
279
|
+
"setMode",
|
|
280
|
+
"setOnTheFlyPointCreationActive",
|
|
281
|
+
"setPerspective",
|
|
282
|
+
"setPointCapture",
|
|
283
|
+
"setPointSize",
|
|
284
|
+
"setPointStyle",
|
|
285
|
+
"setRepaintingActive",
|
|
286
|
+
"setRounding",
|
|
287
|
+
"setSize",
|
|
288
|
+
"setTextValue",
|
|
289
|
+
"setTrace",
|
|
290
|
+
"setUndoPoint",
|
|
291
|
+
"setValue",
|
|
292
|
+
"setVisible",
|
|
293
|
+
"setWidth",
|
|
294
|
+
"setXML",
|
|
295
|
+
"showAlgebraInput",
|
|
296
|
+
"showMenuBar",
|
|
297
|
+
"showResetIcon",
|
|
298
|
+
"showToolBar",
|
|
299
|
+
"startAnimation",
|
|
300
|
+
"stopAnimation",
|
|
301
|
+
"undo",
|
|
302
|
+
"unregisterAddListener",
|
|
303
|
+
"unregisterClearListener",
|
|
304
|
+
"unregisterClickListener",
|
|
305
|
+
"unregisterClientListener",
|
|
306
|
+
"unregisterObjectClickListener",
|
|
307
|
+
"unregisterObjectUpdateListener",
|
|
308
|
+
"unregisterRemoveListener",
|
|
309
|
+
"unregisterRenameListener",
|
|
310
|
+
"unregisterStoreUndoListener",
|
|
311
|
+
"unregisterUpdateListener",
|
|
312
|
+
"writePNGtoFile",
|
|
313
|
+
];
|
|
314
|
+
|
|
315
|
+
const props = {
|
|
316
|
+
appName: {
|
|
317
|
+
set: (app, v, e) => e.setAttribute("appname", v),
|
|
318
|
+
get: (app, e) => e.get_data("param-appname"),
|
|
319
|
+
},
|
|
320
|
+
animating: {
|
|
321
|
+
set: (app, v) => {
|
|
322
|
+
if (v) {
|
|
323
|
+
app.startAnimation();
|
|
324
|
+
} else {
|
|
325
|
+
app.stopAnimation();
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
get: (app) => app.isAnimationRunning(),
|
|
329
|
+
},
|
|
330
|
+
CASObjectNumber: {
|
|
331
|
+
get: (app) => app.getCASObjectNumber(),
|
|
332
|
+
},
|
|
333
|
+
editorState: {
|
|
334
|
+
set: (app, v) => {
|
|
335
|
+
app.setEditorState(v);
|
|
336
|
+
},
|
|
337
|
+
get: (app) => {
|
|
338
|
+
app.getEditorState();
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
graphicsOptions: {
|
|
342
|
+
set: (app, v) => {
|
|
343
|
+
app.setGraphicsOptions(v);
|
|
344
|
+
},
|
|
345
|
+
get: (app) => {
|
|
346
|
+
app.getGraphicsOptions();
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
height: {
|
|
350
|
+
set: (app, v) => {
|
|
351
|
+
app.setHeight(v);
|
|
352
|
+
},
|
|
353
|
+
get: (app, e) => parseFloat(e.get_data("param-height")),
|
|
354
|
+
},
|
|
355
|
+
mode: {
|
|
356
|
+
set: (app, v) => {
|
|
357
|
+
app.setMode(map_attribute(v, mode_map));
|
|
358
|
+
},
|
|
359
|
+
get: (app) => {
|
|
360
|
+
const mode = app.getMode();
|
|
361
|
+
return Object.keys(mode_map).find((n) => mode_map[n] == mode) || mode;
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
objectNames: {
|
|
365
|
+
get: (app) => app.getAllObjectNames(),
|
|
366
|
+
},
|
|
367
|
+
objectNumber: {
|
|
368
|
+
get: (app) => app.getObjectNumber(),
|
|
369
|
+
},
|
|
370
|
+
perspective: {
|
|
371
|
+
set: (app, v) => app.setPerspective(v),
|
|
372
|
+
},
|
|
373
|
+
scaleX: {
|
|
374
|
+
get: (app, e) => parseFloat(e.get_data("scalex")),
|
|
375
|
+
},
|
|
376
|
+
scaleY: {
|
|
377
|
+
get: (app, e) => parseFloat(e.get_data("scaley")),
|
|
378
|
+
},
|
|
379
|
+
width: {
|
|
380
|
+
set: (app, v) => {
|
|
381
|
+
app.setWidth(v);
|
|
382
|
+
},
|
|
383
|
+
get: (app, e) => parseFloat(e.get_data("param-width")),
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
/** From https://wiki.geogebra.org/en/Reference:GeoGebra_App_Parameters
|
|
388
|
+
*/
|
|
389
|
+
const init_param_names = [
|
|
390
|
+
"algebraInputPosition",
|
|
391
|
+
"allowStyleBar",
|
|
392
|
+
"allowUpscale",
|
|
393
|
+
"appName",
|
|
394
|
+
"autoHeight",
|
|
395
|
+
"borderColor",
|
|
396
|
+
"buttonBorderColor",
|
|
397
|
+
"buttonRounding",
|
|
398
|
+
"buttonShadows",
|
|
399
|
+
"capturingThreshold",
|
|
400
|
+
"country",
|
|
401
|
+
"customToolBar",
|
|
402
|
+
"editorBackgroundColor",
|
|
403
|
+
"editorForegroundColor",
|
|
404
|
+
"enable3d",
|
|
405
|
+
"enableCAS",
|
|
406
|
+
"enableFileFeatures",
|
|
407
|
+
"enableLabelDrags",
|
|
408
|
+
"enableRightClick",
|
|
409
|
+
"enableShiftDragZoom",
|
|
410
|
+
"enableUndoRedo",
|
|
411
|
+
"errorDialogsActive",
|
|
412
|
+
"filename",
|
|
413
|
+
"ggbBase64",
|
|
414
|
+
"height",
|
|
415
|
+
"keyboardType",
|
|
416
|
+
"language",
|
|
417
|
+
"playButton",
|
|
418
|
+
"preventFocus",
|
|
419
|
+
"randomSeed",
|
|
420
|
+
"rounding",
|
|
421
|
+
"scale",
|
|
422
|
+
"scaleContainerClass",
|
|
423
|
+
"showAlgebraInput",
|
|
424
|
+
"showAnimationButton",
|
|
425
|
+
"showFullscreenButton",
|
|
426
|
+
"showLogging",
|
|
427
|
+
"showMenuBar",
|
|
428
|
+
"showResetIcon",
|
|
429
|
+
"showStartTooltip",
|
|
430
|
+
"showSuggestionButtons",
|
|
431
|
+
"showToolBar",
|
|
432
|
+
"showToolBarHelp",
|
|
433
|
+
"showZoomButtons",
|
|
434
|
+
"textmode",
|
|
435
|
+
"transparentGraphics",
|
|
436
|
+
"width",
|
|
437
|
+
];
|
|
438
|
+
|
|
439
|
+
// extra attributes mapping to methods
|
|
440
|
+
const methodAttributes = {
|
|
441
|
+
perspective: (app, v) => app.setPerspective(v),
|
|
442
|
+
axes: (app, v) => app.setAxesVisible(...splitlist(v).map((x) => x == "true")),
|
|
443
|
+
xaxis: (app, v) =>
|
|
444
|
+
app.setAxesVisible(
|
|
445
|
+
v,
|
|
446
|
+
app.getGraphicsOptions(1).axes.y.visible,
|
|
447
|
+
app.getGraphicsOptions(1).axes.z.visible,
|
|
448
|
+
),
|
|
449
|
+
yaxis: (app, v) =>
|
|
450
|
+
app.setAxesVisible(
|
|
451
|
+
app.getGraphicsOptions(1).axes.x.visible,
|
|
452
|
+
v,
|
|
453
|
+
app.getGraphicsOptions(1).axes.z.visible,
|
|
454
|
+
),
|
|
455
|
+
zaxis: (app, v) =>
|
|
456
|
+
app.setAxesVisible(
|
|
457
|
+
app.getGraphicsOptions(1).axes.x.visible,
|
|
458
|
+
app.getGraphicsOptions(1).axes.y.visible,
|
|
459
|
+
v,
|
|
460
|
+
),
|
|
461
|
+
rounding: (app, v) => app.setRounding(v),
|
|
462
|
+
hidecursorwhendragging: (app, v) => app.hideCursorWhenDragging(v),
|
|
463
|
+
repaintingactive: (app, v) => app.setRepaintingActive(v),
|
|
464
|
+
errordialogsactive: (app, v) => app.setErrorDialogsActive(v),
|
|
465
|
+
coordsystem: (app, v) =>
|
|
466
|
+
app.setCoordSystem(...splitlist(v).map((x) => parseFloat(x))),
|
|
467
|
+
axislabels: (app, v) => app.setAxisLabels(1, ...splitlist(v)),
|
|
468
|
+
xaxislabel: (app, v) =>
|
|
469
|
+
app.setAxisLabels(
|
|
470
|
+
1,
|
|
471
|
+
v,
|
|
472
|
+
app.getGraphicsOptions(1).axes.y.label || "",
|
|
473
|
+
app.getGraphicsOptions(1).axes.z.label || "",
|
|
474
|
+
),
|
|
475
|
+
yaxislabel: (app, v) =>
|
|
476
|
+
app.setAxisLabels(
|
|
477
|
+
1,
|
|
478
|
+
app.getGraphicsOptions(1).axes.x.label || "",
|
|
479
|
+
v,
|
|
480
|
+
app.getGraphicsOptions(1).axes.z.label || "",
|
|
481
|
+
),
|
|
482
|
+
zaxislabel: (app, v) =>
|
|
483
|
+
app.setAxisLabels(
|
|
484
|
+
1,
|
|
485
|
+
app.getGraphicsOptions(1).axes.x.label || "",
|
|
486
|
+
app.getGraphicsOptions(1).axes.y.label || "",
|
|
487
|
+
v,
|
|
488
|
+
),
|
|
489
|
+
axissteps: (app, v) =>
|
|
490
|
+
app.setAxisSteps(...splitlist(v).map((x) => parseFloat(x))),
|
|
491
|
+
axisunits: (app, v) => app.setAxisUnits(...splitlist(v)),
|
|
492
|
+
grid: (app, v) => app.setGridVisible(1, v),
|
|
493
|
+
grid1: (app, v) => app.setGridVisible(1, v),
|
|
494
|
+
grid2: (app, v) => app.setGridVisible(2, v),
|
|
495
|
+
grid3: (app, v) => app.setGridVisible(3, v),
|
|
496
|
+
editorstate: (app, content) => app.setEditorState({ content }),
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
const graphics_option_names = [
|
|
500
|
+
"pointCapturing",
|
|
501
|
+
"gridIsBold",
|
|
502
|
+
"gridType",
|
|
503
|
+
"bgColor",
|
|
504
|
+
"gridColor",
|
|
505
|
+
"axesColor",
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
class GeogebraElement extends HTMLElement {
|
|
509
|
+
static get observedAttributes() {
|
|
510
|
+
let names = init_param_names.slice();
|
|
511
|
+
names = names.concat(Object.keys(methodAttributes));
|
|
512
|
+
names = names.concat(graphics_option_names);
|
|
513
|
+
names = names.concat([
|
|
514
|
+
"material",
|
|
515
|
+
"gridDistance",
|
|
516
|
+
"sortAlgebra",
|
|
517
|
+
"pointcapture",
|
|
518
|
+
"rightanglestyle",
|
|
519
|
+
"mode",
|
|
520
|
+
]);
|
|
521
|
+
return names.map((n) => n.toLowerCase());
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
reset() {
|
|
525
|
+
this._app_created = new Promise((resolve, reject) => {
|
|
526
|
+
this._resolve_app_created = resolve;
|
|
527
|
+
});
|
|
528
|
+
this._app = null;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
constructor() {
|
|
532
|
+
super();
|
|
533
|
+
this.reset();
|
|
534
|
+
|
|
535
|
+
this.init_commands = this.textContent.trim().replace(/^\s*$\n?/gm, "");
|
|
536
|
+
|
|
537
|
+
this.wrap_methods();
|
|
538
|
+
|
|
539
|
+
this.setup_attribute_changers();
|
|
540
|
+
|
|
541
|
+
this.setup_props();
|
|
542
|
+
|
|
543
|
+
this.dispatchEvent(new CustomEvent("appletconstructed"));
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
547
|
+
const { group, fn } = this.attribute_changers[name];
|
|
548
|
+
|
|
549
|
+
const value = clean_attribute(newValue);
|
|
550
|
+
|
|
551
|
+
const group_handlers = {
|
|
552
|
+
init: () => {
|
|
553
|
+
if (this._app) {
|
|
554
|
+
const xml = this._app.getXML();
|
|
555
|
+
this.reset();
|
|
556
|
+
this.try_create(xml);
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
immediate: async () => {
|
|
560
|
+
const app = await this.applet;
|
|
561
|
+
fn(app, value);
|
|
562
|
+
},
|
|
563
|
+
"graphics options": () => {
|
|
564
|
+
this.set_graphics_options();
|
|
565
|
+
},
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
if (!(group in group_handlers)) {
|
|
569
|
+
throw new Error(`Invalid attribute changer group: ${group}`);
|
|
570
|
+
}
|
|
571
|
+
group_handlers[group]();
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
connectedCallback() {
|
|
575
|
+
if (!this._app) {
|
|
576
|
+
this.try_create();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/** Add some methods to this object which wrap methods in the GeoGebra API
|
|
581
|
+
*/
|
|
582
|
+
wrap_methods() {
|
|
583
|
+
method_names.forEach((name) => {
|
|
584
|
+
this[name] = async (...args) => {
|
|
585
|
+
const app = await this.applet;
|
|
586
|
+
return app[name](...args);
|
|
587
|
+
};
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** Setup functions which should be called when an attribute's value changes
|
|
592
|
+
*/
|
|
593
|
+
setup_attribute_changers() {
|
|
594
|
+
this.attribute_changers = {
|
|
595
|
+
material: { group: "init" },
|
|
596
|
+
};
|
|
597
|
+
for (let name of init_param_names) {
|
|
598
|
+
let changer;
|
|
599
|
+
const set_method = "set" + capitalise(name);
|
|
600
|
+
if (method_names.indexOf(set_method) >= 0) {
|
|
601
|
+
changer = {
|
|
602
|
+
group: "immediate",
|
|
603
|
+
fn: (app, v) => app[set_method](v),
|
|
604
|
+
};
|
|
605
|
+
} else if (method_names.indexOf(name) >= 0) {
|
|
606
|
+
changer = {
|
|
607
|
+
group: "immediate",
|
|
608
|
+
fn: (app, v) => app[name](v),
|
|
609
|
+
};
|
|
610
|
+
} else {
|
|
611
|
+
changer = {
|
|
612
|
+
group: "init",
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
this.attribute_changers[name.toLowerCase()] = changer;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
Object.entries(methodAttributes).map(([name, fn]) => {
|
|
619
|
+
this.attribute_changers[name.toLowerCase()] = {
|
|
620
|
+
group: "immediate",
|
|
621
|
+
fn: (app, v) => {
|
|
622
|
+
if (v === null) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
fn(app, v);
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
for (let name of graphics_option_names.concat(["gridDistance"])) {
|
|
631
|
+
this.attribute_changers[name.toLowerCase()] = {
|
|
632
|
+
group: "graphics options",
|
|
633
|
+
fn: (app, v) => this.set_graphics_options(),
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
this.attribute_changers["pointcapture"] = {
|
|
638
|
+
group: "immediate",
|
|
639
|
+
fn: (app, v) =>
|
|
640
|
+
app.setPointCapture(
|
|
641
|
+
1,
|
|
642
|
+
enum_attribute(v, ["none", "snap", "fixed", "automatic"]),
|
|
643
|
+
),
|
|
644
|
+
};
|
|
645
|
+
this.attribute_changers["sortalgebra"] = {
|
|
646
|
+
group: "immediate",
|
|
647
|
+
fn: (app, v) =>
|
|
648
|
+
app.setAlgebraOptions({
|
|
649
|
+
sortBy: enum_attribute(v, ["dependency", "type", "layer", "order"]),
|
|
650
|
+
}),
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
this.attribute_changers["mode"] = {
|
|
654
|
+
group: "immediate",
|
|
655
|
+
fn: (app, v) => app.setMode(map_attribute(v, mode_map)),
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
get_data(name) {
|
|
660
|
+
const container = this.querySelector(".appletParameters");
|
|
661
|
+
if (!container) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
return container.getAttribute(`data-${name.toLowerCase()}`);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/** Add properties on this object which set or get properties from the applet.
|
|
668
|
+
*/
|
|
669
|
+
setup_props() {
|
|
670
|
+
Object.entries(props).forEach(([name, { get, set }]) => {
|
|
671
|
+
const d = {};
|
|
672
|
+
if (get) {
|
|
673
|
+
d.get = () => {
|
|
674
|
+
if (this._app) {
|
|
675
|
+
return get(this._app, this);
|
|
676
|
+
}
|
|
677
|
+
throw new Error("The GeoGebra app has not loaded yet.");
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
if (set) {
|
|
681
|
+
d.set = async (v) => {
|
|
682
|
+
const app = await this.applet;
|
|
683
|
+
set(app, v, this);
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
Object.defineProperty(this, name, d);
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/** Get the value of an attribute, and convert it to a number or boolean when appropriate.
|
|
691
|
+
*/
|
|
692
|
+
cleanGetAttribute(name) {
|
|
693
|
+
if (!this.hasAttribute(name)) {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
let v = this.getAttribute(name);
|
|
698
|
+
|
|
699
|
+
return clean_attribute(v);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
async set_graphics_options() {
|
|
703
|
+
const app = await this.applet;
|
|
704
|
+
const graphics_options = Object.fromEntries(
|
|
705
|
+
graphics_option_names
|
|
706
|
+
.filter((name) => this.hasAttribute(name))
|
|
707
|
+
.map((name) => [name, this.cleanGetAttribute(name)]),
|
|
708
|
+
);
|
|
709
|
+
graphics_options["rightAngleStyle"] = enum_attribute(
|
|
710
|
+
this.getAttribute("rightanglestyle"),
|
|
711
|
+
["none", "square", "dot", "l"],
|
|
712
|
+
);
|
|
713
|
+
if (this.hasAttribute("gridDistance")) {
|
|
714
|
+
const v = this.cleanGetAttribute("gridDistance");
|
|
715
|
+
const [x, y] = v.split(",").map((x) => parseFloat(x));
|
|
716
|
+
graphics_options["gridDistance"] = { x, y };
|
|
717
|
+
}
|
|
718
|
+
app.setGraphicsOptions(1, graphics_options);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
try_create(xml) {
|
|
722
|
+
let applet;
|
|
723
|
+
// Parameters not loaded from attributes:
|
|
724
|
+
// appletOnLoad, useBrowserForJS
|
|
725
|
+
|
|
726
|
+
const options = Object.fromEntries(
|
|
727
|
+
init_param_names
|
|
728
|
+
.filter((name) => this.hasAttribute(name))
|
|
729
|
+
.map((name) => [name, this.cleanGetAttribute(name)]),
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
if (!xml) {
|
|
733
|
+
if (this.hasAttribute("material")) {
|
|
734
|
+
options.material_id = clean_material_id(
|
|
735
|
+
this.cleanGetAttribute("material"),
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
this.innerHTML = "";
|
|
741
|
+
|
|
742
|
+
const div = document.createElement("div");
|
|
743
|
+
this.appendChild(div);
|
|
744
|
+
options.appletOnLoad = (app) => {
|
|
745
|
+
app.registerClientListener((detail) => {
|
|
746
|
+
this.dispatchEvent(new CustomEvent(detail.type, { detail: detail }));
|
|
747
|
+
});
|
|
748
|
+
app.registerClearListener(() =>
|
|
749
|
+
this.dispatchEvent(new CustomEvent("clear")),
|
|
750
|
+
);
|
|
751
|
+
const other_events = ["Add", "Click", "Remove", "Rename", "Update"];
|
|
752
|
+
other_events.forEach((event_name) => {
|
|
753
|
+
app[`register${event_name}Listener`]((name) => {
|
|
754
|
+
this.dispatchEvent(
|
|
755
|
+
new CustomEvent(event_name.toLowerCase(), {
|
|
756
|
+
detail: { object: name },
|
|
757
|
+
}),
|
|
758
|
+
);
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
this.set_graphics_options();
|
|
763
|
+
|
|
764
|
+
if (xml !== undefined) {
|
|
765
|
+
app.setXML(xml);
|
|
766
|
+
} else if (this.init_commands) {
|
|
767
|
+
app.evalCommand(this.init_commands);
|
|
768
|
+
}
|
|
769
|
+
this._app = app;
|
|
770
|
+
this._resolve_app_created(app);
|
|
771
|
+
this.dispatchEvent(new CustomEvent("load", { detail: app }));
|
|
772
|
+
};
|
|
773
|
+
applet = new window.GGBApplet(options, true);
|
|
774
|
+
applet.inject(div, "preferHTML5");
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/** Returns a Promise which resolves to the GGBApplet object for this element.
|
|
778
|
+
*/
|
|
779
|
+
get applet() {
|
|
780
|
+
return this._app_created;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
async set_point(name, coords) {
|
|
784
|
+
const app = await this.applet;
|
|
785
|
+
return app.evalCommand(`${name} = (${coords.join(",")})`);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
async create_object(name, command) {
|
|
789
|
+
const app = await this.applet;
|
|
790
|
+
app.evalCommand(`${name} = ${command}`);
|
|
791
|
+
return new GeoGebraObjectProxy(app, name);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async get_object(name) {
|
|
795
|
+
const app = await this.applet;
|
|
796
|
+
return new GeoGebraObjectProxy(app, name);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
objects() {
|
|
800
|
+
const app = this._app;
|
|
801
|
+
if (!app) {
|
|
802
|
+
return [];
|
|
803
|
+
}
|
|
804
|
+
const names = app.getAllObjectNames();
|
|
805
|
+
return names.map((name) => new GeoGebraObjectProxy(app, name));
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
class GeoGebraObjectProxy {
|
|
810
|
+
constructor(applet, name) {
|
|
811
|
+
this.applet = applet;
|
|
812
|
+
this._name = name;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
get name() {
|
|
816
|
+
return this._name;
|
|
817
|
+
}
|
|
818
|
+
set name(name) {
|
|
819
|
+
this.applet.renameObject(this._name, name);
|
|
820
|
+
this._name = name;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
get coords() {
|
|
824
|
+
return [
|
|
825
|
+
this.applet.getXcoord(this.name),
|
|
826
|
+
this.applet.getYcoord(this.name),
|
|
827
|
+
this.applet.getZcoord(this.name),
|
|
828
|
+
];
|
|
829
|
+
}
|
|
830
|
+
set coords(coords) {
|
|
831
|
+
this.applet.setCoords(this.name, ...coords);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
listValue(i) {
|
|
835
|
+
return this.applet.getListValue(this.name, i);
|
|
836
|
+
}
|
|
837
|
+
get valueStringLocalized() {
|
|
838
|
+
return this.applet.getValueString(this.name, true);
|
|
839
|
+
}
|
|
840
|
+
get commandLocalized() {
|
|
841
|
+
return this.applet.getCommandString(this.name, true);
|
|
842
|
+
}
|
|
843
|
+
get LaTeXBase64Value() {
|
|
844
|
+
return this.applet.getLaTeXBase64(this.name, true);
|
|
845
|
+
}
|
|
846
|
+
get labelStyleSubstituted() {
|
|
847
|
+
return this.applet.getLabelStyle(this.name, true);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
delete() {
|
|
851
|
+
this.applet.deleteObject(this.name);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
hide() {
|
|
855
|
+
this.applet.setVisible(this.name, false);
|
|
856
|
+
}
|
|
857
|
+
show() {
|
|
858
|
+
this.applet.setVisible(this.name, true);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
on(name, fn) {
|
|
862
|
+
const method = {
|
|
863
|
+
add: "registerAddListener",
|
|
864
|
+
click: "registerClickListener",
|
|
865
|
+
remove: "registerRemoveListener",
|
|
866
|
+
rename: "registerRenameListener",
|
|
867
|
+
update: "registerUpdateListener",
|
|
868
|
+
}[name];
|
|
869
|
+
|
|
870
|
+
if (method) {
|
|
871
|
+
this.applet[method]((object_name) => {
|
|
872
|
+
if (object_name != this.name) {
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
fn();
|
|
876
|
+
});
|
|
877
|
+
} else {
|
|
878
|
+
this.applet.registerClientListener((detail) => {
|
|
879
|
+
if (!(detail.target == this.name && detail.type == name)) {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
fn(detail);
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function parse_css_color(color) {
|
|
889
|
+
if (typeof color != "string") {
|
|
890
|
+
return color;
|
|
891
|
+
}
|
|
892
|
+
const d = document.createElement("div");
|
|
893
|
+
d.style.color = color;
|
|
894
|
+
if (!d.style.color) {
|
|
895
|
+
return [0, 0, 0];
|
|
896
|
+
}
|
|
897
|
+
const [_, r, g, b] = d.style.color.match(/^rgb\((.*?),(.*?),(.*)\)/);
|
|
898
|
+
return [r, g, b].map((v) => parseFloat(v));
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const object_proxy_properties = {
|
|
902
|
+
x: {
|
|
903
|
+
get: "getXcoord",
|
|
904
|
+
set(x) {
|
|
905
|
+
this.applet.setCoords(this.name, x, this.y, this.z);
|
|
906
|
+
},
|
|
907
|
+
},
|
|
908
|
+
y: {
|
|
909
|
+
get: "getYcoord",
|
|
910
|
+
set(y) {
|
|
911
|
+
this.applet.setCoords(this.name, this.x, y, this.z);
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
z: {
|
|
915
|
+
get: "getZcoord",
|
|
916
|
+
set(z) {
|
|
917
|
+
this.applet.setCoords(this.name, this.x, this.y, z);
|
|
918
|
+
},
|
|
919
|
+
},
|
|
920
|
+
value: { get: "getValue", set: "setValue" },
|
|
921
|
+
color: {
|
|
922
|
+
get: "getColor",
|
|
923
|
+
set(color) {
|
|
924
|
+
this.applet.setColor(this.name, ...parse_css_color(color));
|
|
925
|
+
},
|
|
926
|
+
},
|
|
927
|
+
visible: { get: "getVisible", set: "setVisible" },
|
|
928
|
+
valueString: { get: "getValueString" },
|
|
929
|
+
definition: { get: "getDefinitionString" },
|
|
930
|
+
command: {
|
|
931
|
+
get: "getCommandString",
|
|
932
|
+
set(cmd) {
|
|
933
|
+
this.applet.evalCommand(`${this.name} = ${cmd}`);
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
LaTeXString: { get: "getLaTeXString" },
|
|
937
|
+
LaTeXBase64: { get: "getLaTeXBase64" },
|
|
938
|
+
objectType: { get: "getObjectType" },
|
|
939
|
+
exists: { get: "exists" },
|
|
940
|
+
isDefined: { get: "isDefined" },
|
|
941
|
+
layer: { get: "getLayer", set: "setLayer" },
|
|
942
|
+
lineStyle: {
|
|
943
|
+
get: "getLineStyle",
|
|
944
|
+
set(v) {
|
|
945
|
+
this.applet.setLineStyle(
|
|
946
|
+
this.name,
|
|
947
|
+
enum_attribute(v, [
|
|
948
|
+
"full",
|
|
949
|
+
"dashed long",
|
|
950
|
+
"dashed short",
|
|
951
|
+
"dotted",
|
|
952
|
+
"dash-dot",
|
|
953
|
+
]),
|
|
954
|
+
);
|
|
955
|
+
},
|
|
956
|
+
},
|
|
957
|
+
lineThickness: { get: "getLineThickness", set: "setLineThickness" },
|
|
958
|
+
pointStyle: {
|
|
959
|
+
get: "getPointStyle",
|
|
960
|
+
set(v) {
|
|
961
|
+
this.applet.setPointStyle(
|
|
962
|
+
this.name,
|
|
963
|
+
enum_attribute(v, [
|
|
964
|
+
"filled circle",
|
|
965
|
+
"cross",
|
|
966
|
+
"circle",
|
|
967
|
+
"plus",
|
|
968
|
+
"filled diamond",
|
|
969
|
+
"unfilled diamond",
|
|
970
|
+
"triangle north",
|
|
971
|
+
"triangle south",
|
|
972
|
+
"triangle east",
|
|
973
|
+
"triangle west",
|
|
974
|
+
]),
|
|
975
|
+
);
|
|
976
|
+
},
|
|
977
|
+
},
|
|
978
|
+
pointSize: { get: "getPointSize", set: "setPointSize" },
|
|
979
|
+
filling: { get: "getFilling", set: "setFilling" },
|
|
980
|
+
caption: { get: "getCaption", set: "setCaption" },
|
|
981
|
+
labelStyle: {
|
|
982
|
+
get: "getLabelStyle",
|
|
983
|
+
set(v) {
|
|
984
|
+
this.applet.setLabelStyle(
|
|
985
|
+
this.name,
|
|
986
|
+
enum_attribute(v, ["name", "name value", "value", "caption"]),
|
|
987
|
+
);
|
|
988
|
+
},
|
|
989
|
+
},
|
|
990
|
+
labelVisible: { get: "getLabelVisible", set: "setLabelVisible" },
|
|
991
|
+
auxiliary: { set: "setAuxiliary" },
|
|
992
|
+
fixed: { set: "setFixed" },
|
|
993
|
+
trace: { set: "setTrace" },
|
|
994
|
+
isIndependent: { get: "isIndependent" },
|
|
995
|
+
isMoveable: { get: "isMoveable" },
|
|
996
|
+
animating: { set: "setAnimating" },
|
|
997
|
+
animationSpeed: { set: "setAnimationSpeed" },
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
Object.entries(object_proxy_properties).forEach(([prop, { get, set }]) => {
|
|
1001
|
+
Object.defineProperty(GeoGebraObjectProxy.prototype, prop, {
|
|
1002
|
+
get() {
|
|
1003
|
+
return this.applet[get](this.name);
|
|
1004
|
+
},
|
|
1005
|
+
set(v) {
|
|
1006
|
+
if (typeof set == "string") {
|
|
1007
|
+
this.applet[set](this.name, v);
|
|
1008
|
+
} else {
|
|
1009
|
+
set.apply(this, [v]);
|
|
1010
|
+
}
|
|
1011
|
+
},
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
customElements.define("hyperbook-geogebra", GeogebraElement);
|