Usage
Convert a problem with a static (non-interactable, no toolbars or panning) Geogebra app to a problem embedding an image of that applet.
This dramatically speeds up page loads after the image is generated and cached.
To use, import the library:
<import>/res/purdue/purdue_math/math15300/lib/staticgeogebra.library</import>
Then replace the injection
applet.inject(<container>,'preferHTML5');
with
embedStaticGgbApp(applet, container);
Motivation
Geogebra is a fantastic tool for education, and a good choice to have students interact with or draw graphs. To present the same axes and image when they are reading graphs, we would like to use Geogebra for graph reading problems as well; however, loading the full applet on each reload (and, with the way LonCapa unfortunately works, that can be frequent) is time-consuming and can frustrate students.
Under the Hood
The solution for static-image projects is to cache the image so that it only is constructed once per session, and reused on subsequent visits. To do that, we use the sessionStorage html5 feature available in most modern browsers. It has a simple interface:
// To store
sessionStorage.setItem(key,value);
// To retrieve
value = sessionStorage.getItem(key);
Though nobody should still use ancient browsers, we can check for compatiblility directly using a simple if statement:
if (typeof(Storage) !== "undefined"){
...
}
Combining, we can now store and retrieve an image:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | var applet = new GGBApplet({});
var screenshot_key = "some_database_key";
function embedApplet(){
// Check for compatibility before using function.
if (typeof(Storage) !== "undefined"){
var screenshot = sessionStorage.getItem(screenshot_key);
if (screenshot) {
// Embed the image in the container directly
$('#applet_container')[0].innerHTML = '<img src="data:image/png;base64,'+screenshot+'"/>';
} else {
applet.embed('applet_container');
}
} else {
// For other browsers
applet.embed('applet_container'):
}
}
$(embedApplet) // Jquery runs the function when the document is "ready".
function ggbOnInit(){
// Initialize app
...
// Cache image
var screenshot = document.ggbApplet.getPNGBase64(1,false,72);
sessionStorage.setItem(screenshot_key, screenshot);
// For consistency between reloads, swap in the screenshot.
$('#applet_container')[0].innerHTML = '<img src="data:image/png;base64,'+screenshot+'"/>';
}
|
Note that the student only sees the Geogebra applet while it is loading; to be more consistent, we swap in the screenshot.
The last difficult decision is the screenshot key. The sessionStorage store is domain-wide, so we need to prevent collisions with other problems, and possibly with other variations of the same problem.
For this, I currently use a two-part key:
- The current path
window.location.pathname
, which prevents collisions on different pages; - The seed, assigned in perl to
$seed = $external::seed
;
var screenshot_key = window.location.pathname + " " + "$seed";
Recall that LonCapa replaces $seed with the text of the variable before the page even loads, so the quotation marks are necessary to move a variable.
Source
We can clean this up and package it togther:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | var screenshot_key = window.location.pathname + " " + "$seed";
function base64ToImg(source){
var img = new Image();
img.src = 'data:image/png;base64,'+source;
return img;
}
function embedStaticGgbApp(applet, container){
var screenshot;
// Update ggbOnInit to cache screenshots.
var old_ggbOnInit = window.ggbOnInit;
window.ggbOnInit = function ggbOnInit(){
old_ggbOnInit.apply(this, arguments);
var screenshot = document.ggbApplet.getPNGBase64(1,false,72);
sessionStorage.setItem(screenshot_key, screenshot);
$('#'+container).html(base64ToImg(screenshot));
}
if ((typeof(Storage) !== "undefined") &&
(screenshot = sessionStorage.getItem(screenshot_key))){
$('#'+container).html(base64ToImg(screenshot));
} else {
applet.inject(container,'preferHTML5');
}
}
|
Which (with previously setting $seed
as above) allows us to simply replace applet.inject(container)
with embedStaticGgbApp(applet,container)
to achieve the desired functionality.
Links
At the time of publication, these are packaged in a library file on Purdue's server at
https://loncapa.purdue.edu/res/purdue/purdue_math/math15300/lib/staticgeogebra.library
With an example problem
https://loncapa.purdue.edu/priv/purdue/purdue_math/Training/sandbox/staticgeogebra.problem