Embedding an xAPI-enabled (Tin Can) HTML5 Activity
The OpenLearning authoring system includes an "HTML5" widget which supports xAPI (Tin Can).
This guide demonstrates how you can build your own learning interaction, which can be embedded into OpenLearning courses, and trigger a learner's completion to be marked off.
Step 1: Building an HTML package
Using a code editing piece of software (such as Visual Studio Code) create a new project and edit a file called "index.html". The contents of this page can be any valid HTML code, such as:
<html> <head> <title>HTML5 Example</title> </head> <body> <p>This is my custom learning content!</p> </body> </html>
To demonstrate the ability to include multiple files in one package, we'll include an image. This means that the code will now include an image tag, and the image will reside in the same directory as "index.html". The files we have are:
- index.html
- image.png
"index.html" now contains:
<html> <head> <title>HTML5 Example</title> </head> <body> <p>This is my custom learning content!</p> <img src="image.png"> </body> </html>
We can select both of the files in a file viewer (e.g. File Explorer in Windows, or Finder on Mac) and add them to a zip archive.
- On Windows, right-click on the selected files, select "Send to" and select "Compressed (zipped) folder".
- On Mac, right (option) click on the selected files, select "Compress 2 Items".
This will produce a .zip file containing the two files which can be uploaded to OpenLearning as an HTML package (ZIP Archive).
Step 2: Uploading the ZIP Archive to OpenLearning
While editing an OpenLearning page, drag in the HTML5 (xAPI) widget. In this widget's setup, click "Select a Zip File" and select the .zip archive created in the previous step.
The "index.html" file will then automatically be selected as the HTML document to open, and you can click "Start Upload (replace ZIP Archive)" to upload this package to OpenLearning. Once "Upload Complete" is displayed, click Done and return to the view mode of the page.
Your HTML5 page should now be displayed embedded within the OpenLearning page.
Step 3: Enabling Completion to be Triggered
OpenLearning supports the use of "xAPI" to receive messages from your HTML package.
In the HTML5 (xAPI) widget setup, select "Enable xAPI" and "Use xAPI statements for completion tracking" and save these settings.
By default, OpenLearning will be expecting to receive any xAPI statement which contains the data:
{ "result": { "completion": true } }
N.B. it may contain more fields in the statement, but OpenLearning expects at least this data to be present.
When OpenLearning receives a statement which includes this data, this section of the page will be marked off as completed for the current user.
Also ensure that under this Widget's "Completion Settings", the option "Completed when a specific xAPI statement is matched" is selected, otherwise the xAPI setup will be ignored.
Now we need to modify our HTML code so that it can:
- Receive an xAPI configuration for OpenLearning
- Send statements back to OpenLearning
Step 4: Adding JavaScript to the HTML package to Configure xAPI
Our HTML package will need to be updated to receive xAPI configuration information from OpenLearning. OpenLearning sends this information in the URL it uses to launch the package. (Technical documentation on this process can be found here, or if you are authoring the content in Articulate, this is the same process.)
We need to add JavaScript to our HTML to pick up these configuration parameters. OpenLearning provides these to the package as URL parameters:
- actor, which encodes which user is currently using the package
- activity_id, which specifies which OpenLearning activity is currently being used
- registration, which tells OpenLearning which class/group context the package is currently being used within
- endpoint, which is a URL generated by OpenLearning to which xAPI statements are sent (for this launch)
- auth, which is a token generated (for each user) by OpenLearning every time a user launches the package (to authenticate this user's connection to OpenLearning for this launch)
When the package is launched for a user, we can extract these parameters from the URL using JavaScript, and store the values in variables. e.g. our HTML code will now be:
<html> <head> <title>HTML5 Example</title> </head> <body> <p>This is my custom learning content!</p> <img src="image.png"> <script> // xAPI configuration is sent in the URL query string parameters var urlParams = new URLSearchParams(window.location.search); var endpoint = urlParams.get('endpoint'); var auth = urlParams.get('auth'); var actor = JSON.parse(urlParams.get('actor')); // this needs to be JSON decoded var activity_id = urlParams.get('activity_id'); var registration = urlParams.get('registration'); </script> </body> </html>
We will then need to configure the xAPI connection to OpenLearning using these parameters.
Step 5: Sending xAPI Statements from the HTML package
Now that the xAPI configuration information has been captured, we can use this to send a statement to OpenLearning. These statements can be any kind of xAPI statement (which will be passed on to any configured LRS), but in order to trigger completion, we need to send a statement which matches the setup in step 3.
There are many xAPI JavaScript libraries which have already been written to send xAPI statements. e.g. this example will be using TinCanJS.
We will be adding another file to our HTML5 package, called "tincan-min.js". This file can be downloaded here.
Our package will now contain three files:
- index.html
- image.png
- tincan-min.js
We can now update the code of "index.html" to use this library and send xAPI statements:
<html> <head> <title>HTML5 Example</title> <script src="tincan-min.js"></script> </head> <body> <p>This is my custom learning content!</p> <button type="button" id="complete-button">Send Completion Statement</button> <img src="image.png"> <script> // xAPI configuration is sent in the URL query string parameters var urlParams = new URLSearchParams(window.location.search); var endpoint = urlParams.get('endpoint'); var auth = urlParams.get('auth'); var actor = JSON.parse(urlParams.get('actor')); // this needs to be JSON decoded var activity_id = urlParams.get('activity_id'); var registration = urlParams.get('registration'); // Configure OpenLearning as the LRS var lrs = new TinCan.LRS({ endpoint: endpoint, auth: auth }); // The xAPI statement to send when the "complete-button" is pressed: var completionStatement = new TinCan.Statement({ actor: actor, // the actor data sent by OpenLearning object: { id: activity_id, // the activity_id sent by OpenLearning objectType: "Activity" }, context: { registration: registration // the registration sent by OpenLearning }, verb: { id: "http://adlnet.gov/expapi/verbs/completed", display: { "de-DE" : "beendete", "en-US" : "completed", "fr-FR" : "a terminé", "es-ES" : "completó" }, }, // the data OpenLearning is expecting to see for a completion trigger: result: { completion: true } }); // Listen for a click on the button var completeButton = document.getElementById("complete-button"); completeButton.addEventListener("click", function(event) { // When the button is clicked, save the statement lrs.saveStatement(completionStatement, { callback: function(error, xhr) { // error will be null if this succeeded } }) }); </script> </body> </html>
There are three main changes to the file:
- adding "tincan-min.js" as a script (under the title tag)
- adding a button to the screen, called "complete-button" with the text "Send Completion Statement"
- adding the JavaScript code to send the xAPI completion statement when the button is pressed
Save this file, and repeat Steps 1 and 2 with the updated files (to create and upload a new, updated package containing all three files).
Upon using this updated widget on OpenLearning, a user will be able to click the button to trigger being marked off for completion on OpenLearning, for this section of the page.
Customising for Other Use Cases
This method can be customised for other use cases. Depending on the interaction you are developing for the learner, you can control which set of actions indicates that the activity has been completed and sends the required statement.
It may also be the case that you have built the xAPI package using another piece of software (e.g. Articulate or H5P), and it already sends a range of its own xAPI statements. You can choose which statement triggers OpenLearning completion by changing the widget's xAPI completion tracking setup to match the statement of your choice.
A guide for generating xAPI statements using H5P is available here. These statements can then be sent using the above method.