Javascript Tracker Library

Main git repository with source code:

Scope of this repository

The purpose of this project is to provide an alternative to google analytics using javascript to store information about pages and user interactions in the form of xAPI statements. Those xAPI statements can be stored in a xAPI compatible LRS like, for example, Learning Locker.

Required files

  • JsTracking.js is the main file from this project , which exports all the functions needed to make the tracking work.
  • verbs.js is just a collection of verbs used in the xAPI statements to describe user actions.
  • xapiwrapper.min.js is a script injected automatically in the page head by JsTracking.js. This is needed to create the window.ADL object, which provide all the functions needed to store xAPI data in the LRS.


You will need to set several configuration values in order to communicate with the LRS where the tracking data will be stored:

    endpoint: BASE_URL + "edu/api/v1/xAPIProxy/",
    user: userId,
    password: userToken,
    lmsHomePage: ""

We have implemented (in the same backend repository as the Module Occupation Matching) a proxy to forward xAPI requests from our backend to the LRS in order to keep credentials safe.

  • userId: logged-in user ID.
  • userToken: logged-in user access token

How does it work

When you add this project as a package into your application you can access 2 modules:

  • jsTrackingFunctions which contains all the functions you will need to call to track in your platform. Those functions are:
    • initXapiTrack – This function initializes the LRS and other configurations needed to start tracking. It is an asynchronous function, and it is needed to be called before any other tracking function. It receives one parameter, an object like the one in the section above with the lrs configuration.
    • sendXapiStatement – This one triggers a tracking event, which is stored in the LRS as a statement. It receives a parameter with the name of the event, for example: viewed, progressed, responded, log in
  • verbsList – This is a group of constants for the available verbs. For example if you want to trigger a viewed event you can use the constant verbsList.viewed.key to access the name of the event.

Some verbs have a callback function to process some extra data, for example when tracking a search event you can include in the xAPI data what the user was looking for and even filters applied to the search. This callback function is optional but each verb will expect different objects in this function.

Before sending any tracking event the identifier of the logged-in user must be stored in window.JsTrackingUserLogged.

Every statement saved in the LRS includes data from the current page. Also, if the page has structured data with format in a application/ld+json script all this data is stored inside the statement.


In this example we are initializing the tracking and sending a viewed event. This is storing an xAPI statement in the LRS for the user tracking-test-user with the data from the current page.

window.JsTrackingUserLogged = 'tracking-test-user'
await jsTrackingFunctions.initXapiTrack({
    endpoint: "",
    user: "64b9634d139ecf5d77ec8bf335c582c8ee374f5",
    password: "password1234",
    lmsHomePage: ""

Also, an example of an event with custom options like the search mentioned before:

const options = {
jsTrackingFunctions.sendXapiStatement(verbsList.viewed.key, options)

Where searchQuery is a string and filters is an object.

Example statement

  "authority": {
    "objectType": "Agent",
    "name": "EduPLEx Client",
    "mbox": ""
  "stored": "2022-09-26T12:06:08.158Z",
  "context": {
    "extensions": {
      "": "929f703a-9e42-4c44-897b-735bd1679aa8",
      "": {
        "hasCourseInstance": {
          "endDate": "2023-01-16T14:00:00+01:00",
          "name": "A test course",
          "startDate": "2022-08-15T01:05:00+02:00",
          "location": {
            "@type": "Place",
            "address": {
              "@type": "PostalAddress",
              "addressCountry": {
                "@type": "Country"
          "offers": {
            "@type": "Offer",
            "url": "",
            "name": "A test course",
            "availability": "InStock",
            "price": 10,
            "priceCurrency": "EUR",
            "validFrom": "2022-06-19T00:00:00+02:00"
          "@type": "CourseInstance",
          "image": "",
          "description": "Learn the basics...",
          "instructor": [],
          "courseMode": "online"
        "name": "A test course",
        "timeRequired": "PT0H30M",
        "url": "",
        "@context": "",
        "provider": {
          "@id": "",
          "@type": "Organization",
          "name": "Nordev",
          "url": ""
        "isAccessibleForFree": "",
        "@type": "Course",
        "@id": "",
        "aggregateRating": {
          "@type": "AggregateRating",
          "ratingValue": 4.67,
          "bestRating": 5,
          "reviewCount": 3
        "description": "Learn the basics...",
        "inLanguage": "en"
  "actor": {
    "account": {
      "homePage": "",
      "name": "dani-test-tracking"
    "objectType": "Agent"
  "timestamp": "2022-09-26T12:06:08.158Z",
  "version": "1.0.0",
  "id": "3a2f0cb5-2ac5-4d17-a2ad-7704785c3d63",
  "verb": {
    "id": "",
    "display": {
      "en-US": "viewed"
  "object": {
    "id": "",
    "definition": {
      "type": ""
    "objectType": "Activity"

Other considerations

In projects with webpack and babel we found a recurring error with optional chaining operator (?.), it shows something like this in build time: Module parse failed: Unexpected token You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.

It can be fixed by adding this plugin to your webpack/babel config:


Also make sure webpack transpile our code, in a nuxt project this is as simple as adding the next line inside the build property in nuxt.config.js.

transpile: ['user-behavior-tracker']


The source code for the site is licensed under the MIT license, which you can find in the LICENSE file.

xapiwrapper.min.js file has been taken from another open source repository ( which is under Apache License