Use JavaScript to send data to LMS with SCORM 2004 4th edition

Jun 05, 2021

Hi all, 

There are many discussions on this topic but I haven't seen a full script using SCORM 2004 so I thought I could share what I've used to report data that includes detailed question text and user responses back to LMS with SCORM 2004. 

For this code, I used in Workday Learning LMS but I think it should work on other LMSs. The code below is just showing the basic to report data back to LMS. So you will most likely use variables to feed cmi.interactions. 

// For SCORM2004 only 
var player = GetPlayer(); //talk to Storyline

function findLMSAPI(win) {
if (win.hasOwnProperty("GetStudentID")) return win;
else if (win.parent == win) return null;
else return findLMSAPI(win.parent);
}
var lmsAPI = findLMSAPI(this);


//These lines will be used to set score value in SCORM2004
var finalRawScore = player.GetVar("zScore");
var passPercent = player.GetVar("zPassingPercentage");
var scoreScale = finalRawScore/100;

//Set score value here. These 4 lines are necessary for SCORM2004
SCORM2004_objAPI.SetValue('cmi.score.scaled', scoreScale);
SCORM2004_objAPI.SetValue('cmi.score.raw', finalRawScore);
SCORM2004_objAPI.SetValue('cmi.score.min', '0');
SCORM2004_objAPI.SetValue('cmi.score.max', '100');


//Here is the code to report question text and user responses back to LMS.
//These fields were tested in Workday Learning LMS and were necessary for reporting.
//You can also use variables to feed cmi.interactions. The 0 here means the first record.

SCORM2004_objAPI.SetValue('cmi.interactions.0.id', '001');
SCORM2004_objAPI.SetValue('cmi.interactions.0.description', 'question 1 text here');
SCORM2004_objAPI.SetValue('cmi.interactions.0.objectives.0.id', '001');
SCORM2004_objAPI.SetValue('cmi.interactions.0.type', 'fill-in'); //refer to note1 below
SCORM2004_objAPI.SetValue('cmi.interactions.0.timestamp', '2020-12-23T10:00:00');
SCORM2004_objAPI.SetValue('cmi.interactions.0.learner_response', 'This_is_a_TEST_response');
SCORM2004_objAPI.SetValue('cmi.interactions.0.result', 'neutral'); //refer to note2 below

//note1 (“true-false”, “choice”, “fill-in”, “long-fill-in”, “matching”, “performance”, “sequencing”, “likert”, “numeric” or “other”, RW) Which type of interaction is recorded
//note2 “correct”, “incorrect”, “unanticipated”, “neutral”


//determine if the user passed or failed. And if you would like the course to mark as completed or not if the user failed.
if (SCORM2004_objAPI.GetValue('cmi.score.raw') >= passPercent)
{
SCORM2004_objAPI.SetValue("cmi.success_status","passed");
SCORM2004_objAPI.SetValue("cmi.completion_status","completed");

}
else
{

SCORM2004_objAPI.SetValue("cmi.success_status","failed");
SCORM2004_objAPI.SetValue("cmi.completion_status","incomplete");
//can change to completed if you want the course to be marked as completed even if the user failed.

}

 

Here is an example of using the Storyline variables to feed cmi.interactions.

var Q1_ID = player.GetVar("Q1_ID");
var Q1_Description = player.GetVar("Q1_QuestionDescription");
var Q1_Response = player.GetVar("Q1_Response");
var Q1_Result = player.GetVar("Q1_Result");
var Q1_Timestamp = player.GetVar("Q1_Timestamp");


SCORM2004_objAPI.SetValue('cmi.interactions.0.id', Q1_ID);
SCORM2004_objAPI.SetValue('cmi.interactions.0.description', Q1_Description);
SCORM2004_objAPI.SetValue('cmi.interactions.0.objectives.0.id', Q1_ID);
SCORM2004_objAPI.SetValue('cmi.interactions.0.type', 'fill-in');
SCORM2004_objAPI.SetValue('cmi.interactions.0.timestamp', Q1_Timestamp);
SCORM2004_objAPI.SetValue('cmi.interactions.0.learner_response', Q1_Response);
SCORM2004_objAPI.SetValue('cmi.interactions.0.result', Q1_Result);

Note that the timestamp format would need to be like this: '2020-12-23T10:00:00'. So when you set up the Storyline variables to store timestamp, you would need to use JavaScript to convert current time. Here is the code to convert the time to this format. 

//convert local time to ISO format and remove millisecond with slice
var date = new Date();
var JStimestamp = new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0,-5);

player.SetVar("Q1_Timestamp", JStimestamp); //send value back to the Storyline variable.

 

For the complete list of SCORM 2004 runtime reference, visit: 

https://scorm.com/scorm-explained/technical-scorm/run-time/run-time-reference/

9 Replies
PJ Palomaki

@Carrie Thanks for posting this, really helpful!

Just checking, how would you implement this with something like Likert? I can see that there's a cmi.interactions.0.type for it but what would be the format for the actual cmi.interactions.0.learner_response?

Do you just have to combine the responses into one long string? I assume you can't send an array in cmi.interactions.0.learner_response?

Thanks again for the code!

Carrie Wang

@PJ, I am so sorry that it takes me so long to respond as I am not following the thread. 

I never used Likert type with this code before as I discovered the default Likert responses were not reader-friendly in Workday Learning. So in a situation like this, I built my own Likert and stored information and used fill-in type. 

You can use array in the code. When you use array, you will end up having multiple "rows" in your final report. See partial code below using for loop with array:

(forgive me for my sloppy coding. I didn't start var i with 0 because I had the question# setup starting with 1 in SL and didn't want to go back to change all the variables. lol)  

for (i=1; i <=totalQuestion; i++)
{
ID[i] = player.GetVar("Q"+i+"_ID");
Description[i] = player.GetVar("Q"+i+"_QuestionDescription");
Response[i] = player.GetVar("Q"+i+"_Response");
Result[i] = player.GetVar("Q"+i+"_Result");
Timestamp[i] = player.GetVar("Q"+i+"_Timestamp");


SCORM2004_objAPI.SetValue('cmi.interactions.'+(i-1)+'.id', ID[i]);
SCORM2004_objAPI.SetValue('cmi.interactions.'+(i-1)+'.description', Description[i]);
SCORM2004_objAPI.SetValue('cmi.interactions.'+(i-1)+'.objectives.'+(i-1)+'.id', ID[i]);
SCORM2004_objAPI.SetValue('cmi.interactions.'+(i-1)+'.type', 'fill-in');
SCORM2004_objAPI.SetValue('cmi.interactions.'+(i-1)+'.timestamp', Timestamp[i]);
SCORM2004_objAPI.SetValue('cmi.interactions.'+(i-1)+'.learner_response', Response[i]);
SCORM2004_objAPI.SetValue('cmi.interactions.'+(i-1)+'.result', Result[i]);
}

In this case, you will generate a new row for each question. 

Carrie Wang
PJ Palomaki
Carrie Wang

var lmsAPI = findLMSAPI(this);

Also, is there a reason the script discovers the LMS API if the reference is not then being used?

@PJ, I copied the first segment of the code from other discussions, so I probably cannot answer you on this one. From what I saw in other discussions, it seems like var lmsAPI = findLMSAPI(this) is used to communicate with LMS. But I could be wrong so if anyone can help out with this question, it will be much appreciated!