123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- /**
- * Wrapper to the SCORM API provided by Chamilo
- * 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 Chamilo-proper functions (checkAnswers, etc in the third section)
- */
- var _debug = true;
- var findAPITries = 0;
- var _apiHandle = null; //private variable
- var errMsgLocate = "Unable to locate the LMS's API implementation";
- var _NoError = 0;
- var _GeneralException = 101;
- var _ServerBusy = 102;
- var _InvalidArgumentError = 201;
- var _ElementCannotHaveChildren = 202;
- var _ElementIsNotAnArray = 203;
- var _NotInitialized = 301;
- var _NotImplementedError = 401;
- var _InvalidSetValue = 402;
- var _ElementIsReadOnly = 403;
- var _ElementIsWriteOnly = 404;
- var _IncorrectDataType = 405;
- var startTime;
- var exitPageStatus;
- /**
- * 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) {
- if (errCode == _NotImplementedError) {
- var errDescription = "The LMS doesn't support this feature";
- if (_debug) {
- errDescription += "\n";
- errDescription += api.LMSGetDiagnostic(null);
- }
- addDebug(errDescription);
- } else {
- var errDescription = API.LMSGetErrorString(errCode);
- if (_debug) {
- errDescription += "\n";
- errDescription += api.LMSGetDiagnostic(null);
- }
- addDebug(errDescription);
- }
- }
- return errCode;
- }
- function addDebug(message) {
- if (_debug && window.console) {
- console.log(message);
- }
- }
- function addDebugTable(message) {
- if (_debug && window.console) {
- console.table(message);
- }
- }
- /**
- * 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);
- addDebug("LMSGetValue(" + name + ") failed. \n" + errDescription)
- return '';
- }
- 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();
- }
- /**
- * Initialise page values
- */
- function loadPage()
- {
- var result = doLMSInitialize();
- if (result) {
- 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()
- {
- checkAnswers();
- computeTime();
- exitPageStatus = true;
- var result;
- result = doLMSCommit();
- result = doLMSFinish();
- }
- /**
- * Called upon unload event from body element
- */
- function unloadPage(status)
- {
- if (!exitPageStatus)
- {
- // doQuit( status );
- }
- }
- /**
- * Checks the answers on the test formular page
- */
- function checkAnswers(interrupted)
- {
- var tmpScore = 0;
- var status = 'not attempted';
- var scoreMax = 0;
- addDebug('Number of questions: '+ questions.length);
- for (var i=0; i < questions.length; i++) {
- if (questions[i] != undefined && questions[i] != null) {
- var idQuestion = questions[i];
- var type = questions_types[idQuestion];
- var interactionScore = 0;
- var interactionAnswers = '';
- var interactionCorrectResponses = '';
- var interactionType = '';
- addDebug('idQuestion: ' +idQuestion + ', Type: ' +type);
- addDebug('questions_answers: ');
- addDebugTable(questions_answers[idQuestion]);
- addDebug('questions_answers_ponderation: ');
- addDebugTable(questions_answers_ponderation[idQuestion]);
- addDebug('questions_answers_correct: ');
- addDebugTable(questions_answers_correct[idQuestion]);
- switch (type) {
- case 'mcma':
- interactionType = 'choice';
- var myScore = 0;
- for(var j = 0; j< questions_answers[idQuestion].length;j++) {
- var idAnswer = questions_answers[idQuestion][j];
- var answer = document.getElementById('question_'+(idQuestion)+'_multiple_'+(idAnswer));
- if (answer.checked) {
- interactionAnswers += idAnswer+'__|';// changed by isaac flores
- myScore += questions_answers_ponderation[idQuestion][idAnswer];
- }
- }
- interactionScore = myScore;
- scoreMax += questions_score_max[idQuestion];
- addDebug("Score: "+myScore);
- break;
- case 'mcua':
- interactionType = 'choice';
- var myScore = 0;
- for (var j = 0; j<questions_answers[idQuestion].length; j++) {
- var idAnswer = questions_answers[idQuestion][j];
- var elementId = 'question_'+(idQuestion)+'_unique_'+(idAnswer);
- var answer = document.getElementById(elementId);
- if (answer.checked) {
- addDebug('Element id # "'+ elementId +'" was checked');
- interactionAnswers += idAnswer;
- addDebug("List of correct answers: "+questions_answers_correct[idQuestion]);
- addDebug('Score for this answer: ' + questions_answers_ponderation[idQuestion][idAnswer]);
- addDebug("idAnswer: "+idAnswer);
- addDebug("Option selected: "+questions_answers_correct[idQuestion][idAnswer]);
- if (questions_answers_correct[idQuestion][idAnswer] == 1) {
- if (questions_answers_ponderation[idQuestion][idAnswer]) {
- myScore += questions_answers_ponderation[idQuestion][idAnswer];
- } else {
- myScore++;
- }
- }
- }
- }
- addDebug("Score: "+myScore);
- interactionScore = myScore;
- scoreMax += questions_score_max[idQuestion];
- break;
- case 'tf':
- interactionType = 'true-false';
- var myScore = 0;
- for (var j = 0; j < questions_answers[idQuestion].length; j++) {
- var idAnswer = questions_answers[idQuestion][j];
- var answer = document.getElementById('question_' + idQuestion + '_tf_' + (idAnswer));
- if (answer.checked.value) {
- interactionAnswers += idAnswer;
- for (k = 0; k < questions_answers_correct[idQuestion].length; k++) {
- if (questions_answers_correct[idQuestion][k] == idAnswer) {
- if (questions_answers_ponderation[idQuestion][idAnswer]) {
- myScore += questions_answers_ponderation[idQuestion][idAnswer];
- } else {
- myScore++;
- }
- }
- }
- }
- }
- addDebug("Score: "+ myScore);
- interactionScore = myScore;
- scoreMax += questions_score_max[idQuestion];
- break;
- case 'fib':
- interactionType = 'fill-in';
- var myScore = 0;
- for (var j = 0; j < questions_answers[idQuestion].length; j++) {
- var idAnswer = questions_answers[idQuestion][j];
- var answer = document.getElementById('question_'+(idQuestion)+'_fib_'+(idAnswer));
- if (answer.value) {
- interactionAnswers += answer.value + '__|';//changed by isaac flores
- for (k = 0; k < questions_answers_correct[idQuestion].length; k++) {
- if (questions_answers_correct[idQuestion][k] == answer.value) {
- if (questions_answers_ponderation[idQuestion][idAnswer]) {
- myScore += questions_answers_ponderation[idQuestion][idAnswer];
- } else {
- myScore++;
- }
- }
- }
- }
- }
- addDebug("Score: "+myScore);
- interactionScore = myScore;
- scoreMax += questions_score_max[idQuestion];
- break;
- case 'matching':
- interactionType = 'matching';
- var myScore = 0;
- addDebug("List of correct answers: ");
- console.log(questions_answers_correct[idQuestion]);
- for (var j = 0; j < questions_answers[idQuestion].length; j++) {
- var idAnswer = questions_answers[idQuestion][j];
- var elementId = 'question_' + (idQuestion) + '_matching_' + (idAnswer);
- addDebug("---------idAnswer #"+idAnswer+'------------------');
- addDebug("Checking element #"+elementId);
- var answer = document.getElementById(elementId);
- if (answer && answer.value) {
- interactionAnswers += answer.value + '__|';//changed by isaac flores
- for (k = 0; k < questions_answers_correct[idQuestion].length; k++) {
- var left = questions_answers_correct[idQuestion][k][0];
- var right = questions_answers_correct[idQuestion][k][1];
- addDebug('Left ' + left);
- addDebug('Right ' + right);
- addDebug('answer.value ' + answer.value);
- if (right == idAnswer && left == answer.value) {
- addDebug('Score for this answer: ' + questions_answers_ponderation[idQuestion][idAnswer]);
- if (questions_answers_ponderation[idQuestion][idAnswer]) {
- myScore += questions_answers_ponderation[idQuestion][idAnswer];
- } else {
- // myScore++;
- }
- }
- }
- }
- addDebug("Partial score: "+myScore);
- addDebug("--------- end --- idAnswer #"+idAnswer+'------------------');
- }
- addDebug("Score: "+myScore);
- interactionScore = myScore;
- scoreMax += questions_score_max[idQuestion];
- break;
- case 'free':
- //ignore for now as a score cannot be given
- interactionType = 'free';
- var answer = document.getElementById('question_'+(idQuestion)+'_free');
- if (answer && answer.value) {
- interactionAnswers += answer.value
- }
- //interactionScore = questions_score_max[idQuestion];
- interactionScore = 0;
- scoreMax += questions_score_max[idQuestion];
- //interactionAnswers = document.getElementById('question_'+(idQuestion)+'_free').value;
- //correct responses work by pattern, see SCORM Runtime Env Doc
- //interactionCorrectResponses += questions_answers_correct[idQuestion].toString();
- break;
- case 'hotspot':
- interactionType = 'sequencing';
- interactionScore = 0;
- //if(question_score && question_score[idQuestion]){
- // interactionScore = question_score[idQuestion];
- //} //else, 0
- //interactionAnswers = document.getElementById('question_'+(idQuestion)+'_free').innerHTML;
- //correct responses work by pattern, see SCORM Runtime Env Doc
- //for(k=0;k<questions_answers_correct[idQuestion].length;k++)
- //{
- // interactionCorrectResponses += questions_answers_correct[idQuestion][k].toString()+',';
- //}
- break;
- case 'exact':
- interactionType = 'exact';
- interactionScore = 0;
- var real_answers = new Array();
- for (var j = 0; j < questions_answers[idQuestion].length; j++) {
- var idAnswer = questions_answers[idQuestion][j];
- var answer = document.getElementById('question_' + (idQuestion) + '_exact_' + (idAnswer));
- if (answer.checked == true) {
- interactionAnswers += idAnswer+', ';
- if (questions_answers_correct[idQuestion][idAnswer] != 0) {
- real_answers[j] = true;
- } else {
- real_answers[j] = false;
- }
- } else {
- if (questions_answers_correct[idQuestion][idAnswer] != 0) {
- real_answers[j] = false;
- } else {
- real_answers[j] = true;
- }
- }
- }
- var final_answer = true;
- for (var z = 0; z < real_answers.length; z++) {
- if (real_answers[z] == false) {
- final_answer = false;
- }
- }
- interactionScore = 0;
- addDebug(real_answers);
- if (final_answer) {
- //getting only the first score where we save the weight of all the question
- interactionScore = questions_answers_ponderation[idQuestion][1];
- }
- addDebug("Score: "+interactionScore);
- scoreMax += questions_score_max[idQuestion];
- break;
- }
- tmpScore += interactionScore;
- doLMSSetValue('cmi.interactions.'+idQuestion+'.id', 'Q'+idQuestion);
- doLMSSetValue('cmi.interactions.'+idQuestion+'.type', interactionType);
- doLMSSetValue('cmi.interactions.'+idQuestion+'.student_response', interactionAnswers);
- doLMSSetValue('cmi.interactions.'+idQuestion+'.result', interactionScore);
- }
- }
- doLMSSetValue('cmi.core.score.min', 0);
- doLMSSetValue('cmi.core.score.max', scoreMax);
- doLMSSetValue('cmi.core.score.raw', tmpScore);
- // Get status
- var mastery_score = doLMSGetValue('cmi.student_data.mastery_score');
- if (mastery_score <= 0) {
- mastery_score = (scoreMax * 0.80);
- }
- if (tmpScore >= mastery_score) {
- status = 'passed';
- } else {
- status = 'failed';
- }
- addDebug('student_score: ' + tmpScore);
- addDebug('mastery_score: ' + mastery_score);
- addDebug('cmi.core.score.max: ' + scoreMax);
- addDebug('cmi.core.lesson_status: ' + status);
- doLMSSetValue('cmi.core.lesson_status', status);
- if (interrupted && (status != 'completed') && (status != 'passed')) {
- doLMSSetValue('cmi.core.exit', 'suspended');
- }
- return false; //do not submit the form
- }
- (function($){
- //Shuffle all rows, while keeping the first column
- //Requires: Shuffle
- $.fn.shuffleRows = function(){
- return this.each(function(){
- var main = $(/table/i.test(this.tagName) ? this.tBodies[0] : this);
- var firstElem = [], counter=0;
- main.children().each(function(){
- firstElem.push(this.firstChild);
- });
- main.shuffle();
- main.children().each(function(){
- this.insertBefore(firstElem[counter++], this.firstChild);
- });
- });
- }
- /* Shuffle is required */
- $.fn.shuffle = function() {
- return this.each(function(){
- var items = $(this).children();
- return (items.length)
- ? $(this).html($.shuffle(items))
- : this;
- });
- }
- $.shuffle = function(arr) {
- for(
- var j, x, i = arr.length; i;
- j = parseInt(Math.random() * i),
- x = arr[--i], arr[i] = arr[j], arr[j] = x
- );
- return arr;
- }
- })(jQuery);
- /*
- * Assigns any event handler to any element
- * @param object Element on which the event is added
- * @param string Name of event
- * @param string Function to trigger on event
- * @param boolean Capture the event and prevent
- */
- function addEvent(elm, evType, fn, useCapture)
- {
- if (elm.addEventListener) {
- elm.addEventListener(evType, fn, useCapture);
- return true;
- } else if(elm.attachEvent) {
- var r = elm.attachEvent('on' + evType, fn);
- return r;
- } else {
- elm['on' + evType] = fn;
- }
- }
|