npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

scorm-again

v2.5.0

Published

A modern SCORM JavaScript run-time library for AICC, SCORM 1.2, and SCORM 2004

Downloads

22,878

Readme

Github Actions Code Climate maintainability Code Climate coverage Code Climate technical debt jsDelivr hits (npm) NPM Downloads Libraries.io dependency status for GitHub repo npm bundle size npm GitHub License donate

SCORM Again

This project was created to modernize the SCORM JavaScript runtime, and to provide a stable, tested platform for running AICC, SCORM 1.2, and SCORM 2004 modules. This module is designed to be LMS agnostic, and is written to be able to be run without a backing LMS, logging all function calls and data instead of committing, if an LMS endpoint is not configured.

Potential Breaking Change!

Version 2.0.0 of scorm-again switches to using fetch, as well as async-only for reporting to the LMS. Since fetch is not supported by IE11, you will need to provide your own polyfill for this functionality if you need to support it.

What is this not and what doesn't it do?

  1. This is not an LMS
  2. This does not handle the uploading and verification of SCORM/AICC modules
  3. This project does not currently support TinCan/xAPI/CMI5, and I'm not sure if I will ever get around to it. However, I would welcome merge requests to add support for any additional specifications.
  4. This library does not setup communication between an external AICC module and an LMS.
  5. This project is not complete! I'm still working on AICC testing, and continuing to write proper test cases for all APIs

Setup

To begin with, you include either the scorm-again.js or scorm-again.min.js file on your launching page:


<script type="text/javascript" src="/dist/scorm-again.js"></script>

Or, if you would like to only pull in one API, you include either the aicc.js, scorm12.js or scorm2004.js files or their minified versions on your launching page:


<script type="text/javascript" src="/dist/scorm2004.js"></script>

Or, if you would like to install the library using your package manager, you can do the following:

npm install scorm-again

or

yarn add scorm-again

You would then initialize the APIs using the following JS statements:

import {AICC, Scorm12API, Scorm2004API} from 'scorm-again'; // you only do this if you're using the package manager

var settings = {};

// AICC
window.API = new AICC(settings);

// SCORM 1.2
window.API = new Scorm12API(settings);

// SCORM 2004
window.API_1484_11 = new Scorm2004API(settings);

A Note About API Discovery

Before creating a ticket about your module not being able to communicate with the LMS, please make sure you've looked over my examples in the gh-pages branch, as well as reading the SCORM API Discovery Algorithms page. I get that some of this stuff can be hard to implement at first, but I can't give an example for every possible way this library can be loaded into your application. The main thing to remember is that it should always be attached to the window object, because that's where modules are supposed to look.

Available Settings

The APIs include several settings to customize the functionality of each API:

| Setting | Default | Values | Description | |----------------------------|:--------------------------------:|:-----------------------------------------------------------------------------------------------------------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | autocommit | false | true/false | Determines whether the API schedules an autocommit to the LMS after setting a value. | | autocommitSeconds | 60 | int | Number of seconds to wait before autocommiting. Timer is restarted if another value is set. | | asyncCommit | false | true/false | Determines whether the API should send the request to the lmsCommitUrl asynchronously. | | sendFullCommit | true | true/false | Determines whether the API sends the full CMI object as part of the commit, or of it only sends the fields that actually contains values. | | lmsCommitUrl | false | url | The URL endpoint of the LMS where data should be sent upon commit. If no value is provided, modules will run as usual, but all method calls will be logged to the console. | | dataCommitFormat | json | json, flattened, params | json will send a JSON object to the lmsCommitUrl in the format of {'cmi': {'core': {...}} flattened will send the data in the format {'cmi.core.exit': 'suspend', 'cmi.core.mode': 'normal'...} params will send the data as ?cmi.core.exit=suspend&cmi.core.mode=normal... | | commitRequestDataType | 'application/json;charset=UTF-8' | string | This setting is provided in case your LMS expects a different content type or character set. | | renderCommonCommitFields | false | true/false | Determines whether the API should render the common fields in the commit object. Common fields are successStatus, completionStatus, totalTimeSeconds, score, and runtimeData. The runtimeData field contains the render CMI object. This allows for easier processing on the LMS. | | autoProgress | false | true/false | In case Sequencing is being used, you can tell the API to automatically throw the SequenceNext event. | | logLevel | 4 | number | string | LogLevelEnum1 => DEBUG2 => INFO3 => WARN4 => ERROR5 => NONE | By default, the APIs only log error messages. | | mastery_override | false | true/false | (SCORM 1.2) Used to override a module's cmi.core.lesson_status so that a pass/fail is determined based on a mastery score and the user's raw score, rather than using whatever status is provided by the module. An example of this would be if a module is published using a Complete/Incomplete final status, but the LMS always wants to receive a Passed/Failed for quizzes, then we can use this setting to override the given final status. | | selfReportSessionTime | false | true/false | Should the API override the default session_time reported by the module? Useful when modules don't properly report time. | | alwaysSendTotalTime | false | true/false | Should the API always send total_time when committing to the LMS | | fetchMode | 'cors' | 'cors', 'no-cors', 'same-origin', 'navigate' | The fetch mode to use when sending requests to the LMS. | | xhrWithCredentials | false | true/false | Sets the withCredentials flag on the request to the LMS | | xhrHeaders | {} | Object | This allows setting of additional headers on the request to the LMS where the key should be the header name and the value is the value of the header you want to send | | responseHandler | function | | A function to properly transform the response from the LMS to the correct format. The APIs expect the result from the LMS to be in the following format (errorCode is optional): { "result": true, "errorCode": 0 } | | requestHandler | function | | A function to transform the commit object before sending it to lmsCommitUrl. By default it's the identity function (no transformation). | | onLogMessage | function | | A function to be called whenever a message is logged. Defaults to console.{error,warn,info,debug,log} | | scoItemIds | [] | string[] | A list of valid SCO IDs to be used for choice/jump sequence validation. | | scoItemIdValidator | false | false / function | A function to be called during choice/jump sequence checks to determine if a SCO ID is valid. Could be used to call an API to check validity. |

Settings Function Examples

responseHandler

The responseHandler function is used to transform the response from the LMS to the correct format. The APIs expect the result from the LMS to be in the following format (errorCode is optional): { "result": true, "errorCode": 0 }

var settings = {
    responseHandler: function (response: Response): ResultObject {
        const responseObj = JSON.parse(response.text());
        return {
            result: responseObj.success,
            errorCode: responseObj.error
        };
    }
};

requestHandler

The requestHandler function is used to transform the commit object before sending it to lmsCommitUrl. By default, it's the identity function (no transformation).

var settings = {
    requestHandler: function (commitObject: CommitObject): CommitObject {
        commitObject.cmi.core.lesson_status = 'completed';
        return commitObject;
    }
};

onLogMessage

The onLogMessage function is used to log messages. By default, it logs messages to the console.

var settings = {
    onLogMessage: function (level: LogLevel, message: string): void {
        console.log(`[${level}] ${message}`);
    }
};

Initial Values

If you want to initially load data from your backend API, you must do it before launching your SCORM/AICC player. After the player has initialized, you will not be able to change any read-only values.

You can initialize your variables on the CMI object individually:

window.API_1484_11.cmi.learner_id = "123";

You can also initialize the CMI object in bulk by supplying a JSON object. Note that it can be a partial CMI JSON object:

window.API_1484_11.loadFromJSON(json);
var json = {
  "learner_id": "123",
  "learner_name": "Bob The Builder",
  "suspend_data": "viewed=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,29,30,31|lastviewedslide=31|7#1##,3,3,3,7,3,3,7,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,11#0#b5e89fbb-7cfb-46f0-a7cb-758165d3fe7e=236~262~2542812732762722742772682802752822882852892872832862962931000~3579~32590001001010101010101010101001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001010010010010010010010010011010010010010010010010010010010010112101021000171000~236a71d398e-4023-4967-88fe-1af18721422d06passed6failed000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105wrong110000000000000000000000000000000000~3185000000000000000000000000000000000000000000000000000000000000000000000000000000000~283~2191w11~21113101w41689~256~2100723031840~21007230314509062302670~2110723031061120000000000000000000~240~234531618~21601011000100000002814169400,#-1",
  "interactions": {
      "0": {
          "id": "Question14_1",
          "type": "choice",
          "timestamp": "2018-08-26T11:05:21",
          "weighting": "1",
          "learner_response": "HTH",
          "result": "wrong",
          "latency": "PT2M30S",
          "objectives": {
              "0": {
                  "id": "Question14_1"
              }
          },
          "correct_responses": {
              "0": {
                  "pattern": "CPR"
              }
          }
      }
  }
};

Another option for initializing the CMI object in bulk is by supplying a "flattened" JSON object. Note that it can be a partial CMI JSON object:

window.API_1484_11.loadFromFlattenedJSON(json);
var json = {
  "cmi.learner_id": "123",
  "cmi.learner_name": "Bob The Builder",
  "cmi.suspend_data": "viewed=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,29,30,31|lastviewedslide=31|7#1##,3,3,3,7,3,3,7,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,11#0#b5e89fbb-7cfb-46f0-a7cb-758165d3fe7e=236~262~2542812732762722742772682802752822882852892872832862962931000~3579~32590001001010101010101010101001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001001010010010010010010010010011010010010010010010010010010010010112101021000171000~236a71d398e-4023-4967-88fe-1af18721422d06passed6failed000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105wrong110000000000000000000000000000000000~3185000000000000000000000000000000000000000000000000000000000000000000000000000000000~283~2191w11~21113101w41689~256~2100723031840~21007230314509062302670~2110723031061120000000000000000000~240~234531618~21601011000100000002814169400,#-1",
  "cmi.interactions.0.id": "Question14_1",
  "cmi.interactions.0.type": "choice",
  "cmi.interactions.0.timestamp": "2018-08-26T11:05:21",
  "cmi.interactions.0.weighting": "1",
  "cmi.interactions.0.learner_response": "HTH",
  "cmi.interactions.0.result": "wrong",
  "cmi.interactions.0.latency": "PT2M30S",
  "cmi.interactions.0.objectives.0.id": "Question14_1"
  "cmi.interactions.0.objectives.0.correct_responses.0.pattern": "CPR"
};

Accessing CMI Data

The CMI data stored by the API can be accessed directly through the API.cmi object or API_1484_11.cmi object. For example, to get the Student Name in SCORM 1.2, you would do the following:

var studentName = window.API.cmi.core.student_name;

SCORM 1.2 and AICC Listeners

For convenience, hooks are available for all the SCORM 1.2/AICC API Signature functions: LMSInitialize, LMSFinish, LMSGetValue, LMSSetValue, LMSCommit, LMSGetLastError, LMSGetErrorString, LMSGetDiagnostic, SequenceNext, SequencePrevious

You can add your hook into these by adding a listener to the window.API object:

window.API.on("LMSInitialize", function () {
    [...]
});

You can also listen for events on specific SCORM CMI elements:

window.API.on("LMSSetValue.cmi.core.student_id", function (CMIElement, value) {
    [...]
});

Finally, you can listen for events using a wildcard:

window.API.on("LMSSetValue.cmi.*", function (CMIElement, value) {
    [...]
});

You also have to ability to remove specific callback listeners:

window.API.off("LMSInitialize", callback);

Or, you can clear all callbacks for a particular event:

window.API.clear("LMSInitialize");

SCORM 2004 Listeners

For convenience, hooks are available for all the SCORM API Signature functions: Initialize, Terminate, GetValue, SetValue, Commit, GetLastError, GetErrorString, GetDiagnostic, SequenceNext, SequencePrevious, SequenceChoice, SequenceExit, SequenceExitAll, SequenceAbandon, SequenceAbandonAll

You can add your hook into these by adding a listener to the window.API_1484_11 object:

window.API_1484_11.on("Initialize", function () {
    [...]
});

You can also listen for events on specific SCORM CMI elements:

window.API_1484_11.on("SetValue.cmi.learner_id ", function (CMIElement, value) {
    [...]
});

Finally, you can listen for events using a wildcard:

window.API_1484_11.on("SetValue.cmi.* ", function (CMIElement, value) {
    [...]
});

You also have to ability to remove specific callback listeners:

window.API_1484_11.off("Initialize", callback);

Or, you can clear all callbacks for a particular event:

window.API_1484_11.clear("Initialize");

Total Time Calculation

The APIs provide a convenience method getCurrentTotalTime() that can be used for calculating the current total_time value, based on the current session_time and the total_time supplied when the module was launched. This works for both ISO 8601 duration time formats in SCORM 2004 as well as the HH:MM:SS format in SCORM 1.2 and AICC, and outputs the correct format based on the version used.

Completion Status

The APIs will calculate the proper completion status to send back to an LMS. This status is usually based on completion threshold, progress measure, and lesson mode, but please see the mastery_override setting for how statuses can be changed based on scores, as well.

Sequencing

The APIs provide some hooks for the sequencing of modules, but this is primarily handled by the LMS, so no functionality beyond event listeners is provided. More work can be done in this area, but I'm primarily focused on the stability of the rest of the APIs at this point.

Credits and Thanks!

This project was heavily influenced by the simplify-scorm project by @gabrieldoty, but ended up being pretty much a ground-up rewrite. The big influence from this project was the inclusion of event listeners.

I also drew from the Moodle SCORM module, but avoided directly copying their code because it is...not very clean.

Contributing

I welcome any and all feedback and contributions to this project! I'm sure it would do with some cleanup and refactoring, and could definitely use some more test cases.

Setup and Development

You will need node installed on your local machine, and you'll have to run npm install in the repo directory before starting development.

To run a build, you need to just run the yarn run compile command in the root of the project.

Similarly, to run the tests, you just run the yarn test command.

Before submitting pull requests, please also run eslint ./src --fix against your code first, otherwise your pull request build could fail.