Chamilo is a learning management system focused on ease of use and accessibility
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chamilo-lms/main/newscorm/js/api_wrapper.js

513 lines
12 KiB

/**
* Wrapper to the SCORM API provided by Dokeos
* The complete set of functions and variables are in this file to avoid unnecessary file
* accesses.
* Only event triggers and answer data are inserted into the final document.
* @author Yannick Warnier - inspired by the ADLNet documentation on SCORM content-side API
* @package scorm.js
*/
/**
* Initialisation of the SCORM API section.
* Find the SCO functions (startTimer, computeTime, etc in the second section)
* Find the Dokeos-proper functions (checkAnswers, etc in the third section)
*/
var _debug = false;
var findAPITries = 0;
var _apiHandle = null; //private variable
var errMsgLocate = "Unable to locate the LMS's API implementation";
/**
* Gets the API handle right into the local API object and ensure there is only one.
* Using the singleton pattern to ensure there's only one API object.
* @return object The API object as given by the LMS
*/
var API = new function ()
{
if (_apiHandle == null)
{
_apiHandle = getAPI();
}
return _apiHandle;
}
/**
* Finds the API on the LMS side or gives up giving an error message
* @param object The window/frame object in which we are searching for the SCORM API
* @return object The API object recovered from the LMS's implementation of the SCORM API
*/
function findAPI(win)
{
while((win.API == null) && (win.parent != null) && (win.parent != win))
{
findAPITries++;
if(findAPITries>10)
{
alert("Error finding API - too deeply nested");
return null;
}
win = win.parent
}
return win.API;
}
/**
* Gets the API from the current window/frame or from parent objects if not found
* @return object The API object recovered from the LMS's implementation of the SCORM API
*/
function getAPI()
{
//window is the global/root object of the current window/frame
var MyAPI = findAPI(window);
//look through parents if any
if((MyAPI == null) && (window.opener != null) && (typeof(window.opener) != "undefined"))
{
MyAPI = findAPI(window.opener);
}
//still not found? error message
if(MyAPI == null)
{
alert("Unable to find SCORM API adapter.\nPlease check your LMS is considering this page as SCORM and providing the right JavaScript interface.")
}
return MyAPI;
}
/**
* Handles error codes (prints the error if it has a description)
* @return int Error code from LMS's API
*/
function ErrorHandler()
{
if(API == null)
{
alert("Unable to locate the LMS's API. Cannot determine LMS error code");
return;
}
var errCode = API.LMSGetLastError().toString();
if(errCode != _NoError)
{
var errDescription = API.LMSGetErrorString(errCode);
if(_debug == true)
{
errDescription += "\n";
errDescription += api.LMSGetDiagnostic(null);
}
alert (errDescription);
}
return errCode;
}
/**
* Calls the LMSInitialize method of the LMS's API object
* @return string The string value of the LMS returned value or false if error (should be "true" otherwise)
*/
function doLMSInitialize()
{
if(API == null)
{
alert(errMsgLocate + "\nLMSInitialize failed");
return false;
}
var result = API.LMSInitialize("");
if(result.toString() != "true")
{
var err = ErrorHandler();
}
return result.toString();
}
/**
* Calls the LMSFinish method of the LMS's API object
* @return string The string value of the LMS return value, or false if error (should be "true" otherwise)
*/
function doLMSFinish()
{
if(API == null)
{
alert(errMsgLocate + "\nLMSFinish failed");
return false;
}
else
{
var result = API.LMSFinish("");
if(result.toString() != "true")
{
var err = ErrorHandler();
}
}
return result.toString();
}
/**
* Calls the LMSGetValue method
* @param string The name of the SCORM parameter to get
* @return string The value returned by the LMS
*/
function doLMSGetValue(name)
{
if (API == null)
{
alert(errMsgLocate + "\nLMSGetValue was not successful.");
return "";
}
else
{
var value = API.LMSGetValue(name);
var errCode = API.LMSGetLastError().toString();
if (errCode != _NoError)
{
// an error was encountered so display the error description
var errDescription = API.LMSGetErrorString(errCode);
alert("LMSGetValue("+name+") failed. \n"+ errDescription);
return "";
}
else
{
return value.toString();
}
}
}
/**
* Calls the LMSSetValue method of the API object
* @param string The name of the SCORM parameter to set
* @param string The value to set the parameter to
* @return void
*/
function doLMSSetValue(name, value)
{
if (API == null)
{
alert("Unable to locate the LMS's API Implementation.\nLMSSetValue was not successful.");
return;
}
else
{
var result = API.LMSSetValue(name, value);
if (result.toString() != "true")
{
var err = ErrorHandler();
}
}
return;
}
/**
* Calls the LMSCommit method
*/
function doLMSCommit()
{
if(API == null)
{
alert(errMsgLocate +"\nLMSCommit was not successful.");
return "false";
}
else
{
var result = API.LMSCommit("");
if (result != "true")
{
var err = ErrorHandler();
}
}
return result.toString();
}
/**
* Calls GetLastError()
*/
function doLMSGetLastError()
{
if (API == null)
{
alert(errMsgLocate + "\nLMSGetLastError was not successful."); //since we can't get the error code from the LMS, return a general error
return _GeneralError;
}
return API.LMSGetLastError().toString();
}
/**
* Calls LMSGetErrorString()
*/
function doLMSGetErrorString(errorCode)
{
if (API == null)
{
alert(errMsgLocate + "\nLMSGetErrorString was not successful.");
}
return API.LMSGetErrorString(errorCode).toString();
}
/**
* Calls LMSGetDiagnostic()
*/
function doLMSGetDiagnostic(errorCode)
{
if (API == null)
{
alert(errMsgLocate + "\nLMSGetDiagnostic was not successful.");
}
return API.LMSGetDiagnostic(errorCode).toString();
}
/**
* Second section. The SCO functions are located here (handle time and score messaging to SCORM API)
* Initialisation
*/
var startTime;
var exitPageStatus;
/**
* Initialise page values
*/
function loadPage()
{
var result = doLMSInitialize();
if(result != false)
{
var status = doLMSGetValue("cmi.core.lesson_status");
if(status == "not attempted")
{
doLMSSetValue("cmi.core.lesson_status","incomplete");
}
exitPageStatus = false;
startTimer();
}
}
/**
* Starts the local timer
*/
function startTimer()
{
startTime = new Date().getTime();
}
/**
* Calculates the total time and sends the result to the LMS
*/
function computeTime()
{
if ( startTime != 0 )
{
var currentDate = new Date().getTime();
var elapsedSeconds = ( (currentDate - startTime) / 1000 );
var formattedTime = convertTotalSeconds( elapsedSeconds );
}
else
{
formattedTime = "00:00:00.0";
}
doLMSSetValue( "cmi.core.session_time", formattedTime );
}
/**
* Formats the time in a SCORM time format
*/
function convertTotalSeconds(ts)
{
var sec = (ts % 60);
ts -= sec;
var tmp = (ts % 3600); //# of seconds in the total # of minutes
ts -= tmp; //# of seconds in the total # of hours
// convert seconds to conform to CMITimespan type (e.g. SS.00)
sec = Math.round(sec*100)/100;
var strSec = new String(sec);
var strWholeSec = strSec;
var strFractionSec = "";
if (strSec.indexOf(".") != -1)
{
strWholeSec = strSec.substring(0, strSec.indexOf("."));
strFractionSec = strSec.substring(strSec.indexOf(".")+1, strSec.length);
}
if (strWholeSec.length < 2)
{
strWholeSec = "0" + strWholeSec;
}
strSec = strWholeSec;
if (strFractionSec.length)
{
strSec = strSec+ "." + strFractionSec;
}
if ((ts % 3600) != 0 )
var hour = 0;
else var hour = (ts / 3600);
if ( (tmp % 60) != 0 )
var min = 0;
else var min = (tmp / 60);
if ((new String(hour)).length < 2)
hour = "0"+hour;
if ((new String(min)).length < 2)
min = "0"+min;
var rtnVal = hour+":"+min+":"+strSec;
return rtnVal
}
/**
* Handles the use of the back button (saves data and closes SCO)
*/
function doBack()
{
checkAnswers(true);
doLMSSetValue( "cmi.core.exit", "suspend" );
computeTime();
exitPageStatus = true;
var result;
result = doLMSCommit();
result = doLMSFinish();
}
/**
* Handles the closure of the current SCO before an interruption. This is only useful if the LMS
* deals with the cmi.core.exit, cmi.core.lesson_status and cmi.core.lesson_mode *and* the SCO
* sends some kind of value for cmi.core.exit, which is not the case here (yet).
*/
function doContinue(status)
{
// Reinitialize Exit to blank
doLMSSetValue( "cmi.core.exit", "" );
var mode = doLMSGetValue( "cmi.core.lesson_mode" );
if ( mode != "review" && mode != "browse" )
{
doLMSSetValue( "cmi.core.lesson_status", status );
}
computeTime();
exitPageStatus = true;
var result;
result = doLMSCommit();
result = doLMSFinish();
}
/**
* handles the recording of everything on a normal shutdown
*/
function doQuit(status)
{
checkAnswers();
computeTime();
exitPageStatus = true;
var result;
result = doLMSCommit();
result = doLMSSetValue("cmi.core.lesson_status", status);
result = doLMSFinish();
}
/**
* Called upon unload event from body element
*/
function unloadPage(status)
{
if (exitPageStatus != true)
{
doQuit( status );
}
}
/**
* Third section - depending on Dokeos - check answers and set score
*/
var questions = new Array();
var questions_answers = new Array();
var questions_answers_correct = new Array();
var questions_types = new Array();
/**
* Checks the answers on the test formular page
*/
function checkAnswers(interrupted)
{
alert('Test');
var tmpScore = 0;
for(var i=0; i<questions_types.length;i++)
{
var idQuestion = questions[i];
alert('Question'+idQuestion);
var type = questions_types[idQuestion];
var interactionScore = 0;
var interactionAnswer = '';
if (type == 'mcma')
{
var myScore = 0;
for(var j=0; j<questions_answers[idQuestion];j++)
{
var idAnswer = questions_answers[idQuestion][j];
var answer = document.getElementById('question_'+(idQuestion)+'_multiple_'+(idAnswer));
if(answer.checked.value == 'checked')
{
alert(idQuestion+'_'+idAnswer+' was selected');
myScore += questions_answers_correct[idQuestion][idAnswer];
}
}
interactionScore = myScore;
tmpScore = myScore;
}
else if(type == 'mcua')
{
var myScore = 0;
for(var j=0; j<questions_answers[idQuestion];j++)
{
var idAnswer = questions_answers[idQuestion][j];
var answer = document.getElementById('question_'+(idQuestion)+'_unique_'+(idAnswer));
if(answer.selected.value == 'selected')
{
myScore += questions_answers_correct[idQuestion][idAnswer];
}
}
interactionScore = myScore;
tmpScore = myScore;
}
else if(type == 'tf')
{
var myScore = 0;
for(var j=0; j<questions_answers[idQuestion];j++)
{
var idAnswer = questions_answers[idQuestion][j];
var answer = document.getElementById('question_'+(idQuestion)+'_tf_'+(idAnswer));
if(answer.selected == 'selected')
{
myScore += questions_answers_correct[idQuestion][idAnswer];
}
}
interactionScore = myScore;
tmpScore = myScore;
}
else if(type == 'fib')
{
var myScore = 0;
for(var j=0; j<questions_answers[idQuestion];j++)
{
var idAnswer = questions_answers[idQuestion][j];
var answer = document.getElementById('question_'+(idQuestion)+'_fib_'+(idAnswer));
if(answer.value == questions_answers_correct[idQuestion][idAnswer])
{
myScore += 1;
}
interactionAnswers += answer.value+',';
}
interactionScore = myScore;
tmpScore = myScore;
}
else if(type == 'matching')
{
//
}
else if(type == 'free')
{
interactionAnswers += document.getElementById('question_'+idQuestion+'_free').value;
}
else if(type == 'hotspot')
{
//
}
else
{
//
}
var interactionCorrectResponses = '';
for(var i=0; i<questions_answers_correct.length();i++)
{
interactionCorrectResponses += questions_answers_correct[i];
}
doLMSSetValue('cmi.core.interactions.'+idQuestion+'.id','Q'+idQuestion);
doLMSSetValue('cmi.core.interactions.'+idQuestion+'.result',interactionScore);
doLMSSetValue('cmi.core.interactions.'+idQuestion+'.type',type);
doLMSSetValue('cmi.core.interactions.'+idQuestion+'.student_response',interactionAnswers);
doLMSSetValue('cmi.core.interactions.'+idQuestion+'.correct_responses',interactionCorrectRespnoses);
}
LMSSetValue('cmi.core.score_raw',tmpScore);
//get status
LMSSetValue('cmi.core.lesson_status','completed');
if((interrupted==true) && (status != 'completed') && (status != 'passed'))
{
doLMSSetValue('cmi.core.exit','suspended');
}
else
{
}
return false; //do not submit the form
}