Added complete datamodel support for SCORM 1.2
authorbobopinna <bobopinna>
Tue, 22 Feb 2005 08:00:19 +0000 (08:00 +0000)
committerbobopinna <bobopinna>
Tue, 22 Feb 2005 08:00:19 +0000 (08:00 +0000)
mod/scorm/api.php
mod/scorm/datamodel.php
mod/scorm/datamodels/scorm1_2.js [new file with mode: 0644]
mod/scorm/playscorm.php
mod/scorm/version.php

index ef7a3f0..a7ab82e 100644 (file)
@@ -53,14 +53,13 @@ function closeMain() {
 //
 var errorCode = "0";
 
-function SCORM_Call (call,param,value) {
+function SCORM_Call (call,param) {
     if (arguments.length < 2) {
        alert ("Invalid SCORM_Call function call: too few arguments.\nYou need pass at least 2 parameters");
-    } else if (arguments.length == 3) {
-       param = param.concat("&value=",value);
     }
     var myRequest = NewHttpReq();
-    result = DoRequest(myRequest,"<?php p($CFG->wwwroot) ?>/mod/scorm/datamodel.php?id=<?php p($id) ?>&sesskey=<?php p($USER->sesskey) ?>&call="+call+"&param="+param);
+    result = DoRequest(myRequest,"<?php p($CFG->wwwroot) ?>/mod/scorm/datamodel.php?id=<?php p($id) ?>&sesskey=<?php p($USER->sesskey) ?>&call="+call+param);
+    //alert('Call: '+call+'\nParam: '+param+'\nResult: '+result);
     results = result.split('\n');
     
     errorCode = results[1];
@@ -71,25 +70,154 @@ function SCORM_Call (call,param,value) {
 // SCORM 1.2 API Implementation
 //
 function SCORMapi1_2() {
+    var Initialized = false;
+    <?php include_once ('datamodels/scorm1_2.js'); ?>
 
     function LMSInitialize (param) {
-       return SCORM_Call('LMSInitialize',param);
+        if (param == "") {
+            if (!Initialized) {
+               Initialized = true;
+               errorCode = "0";
+               return "true";
+            } else {
+               errorCode = "101";
+           }
+        } else {
+            errorCode = "201";
+        }
+        return "false";
     }
     
     function LMSFinish (param) {
-       return SCORM_Call('LMSGetValue',param);
+       if (param == "") {
+            if (Initialized) {
+               LMSCommit("");
+               Initialized = false;
+               errorCode = "0";
+               return "true";
+            } else {
+               errorCode = "301";
+            }
+        } else {
+            errorCode = "201";
+       }
     }
     
     function LMSGetValue (element) {
-       return SCORM_Call('LMSGetValue',element);
+       if (Initialized) {
+           if (element !="") {
+               expression = new RegExp(CMIIndex,'g');
+               element = element.replace(expression,'.n.');
+               if ((typeof eval('datamodel["'+element+'"]')) != "undefined") {
+                   if (eval('datamodel["'+element+'"].mod') != 'w') {
+                       errorCode = "0";
+                       return eval(element);
+                   } else {
+                       errorCode = eval('datamodel["'+element+'"].readerror');
+                   }
+               } else {
+                   errorCode = "401"
+               }
+           } else {
+               errorCode = "201";
+           }
+       } else {
+            errorCode = "301";
+        }
+       return "";
     }
     
     function LMSSetValue (element,value) {
-       return SCORM_Call('LMSGetValue',element,value);
+       if (Initialized) {
+           if (element != "") {
+               expression = new RegExp(CMIIndex,'g');
+               elementmodel = element.replace(expression,'.n.');
+               if ((typeof eval('datamodel["'+elementmodel+'"]')) != "undefined") {
+                   if (eval('datamodel["'+elementmodel+'"].mod') != 'r') {
+                       expression.compile(eval('datamodel["'+elementmodel+'"].format'));
+                       value = value+'';
+                       matches = value.match(expression);
+                       if (matches != null) {
+                           //Create dynamic data model element
+                           if (element != elementmodel) {
+                               elementIndexes = element.split('.');
+                               subelement = 'cmi';
+                               for (i=1;i<elementIndexes.length-1;i++) {
+                                   elementIndex = elementIndexes[i];
+                                   if (elementIndexes[i+1].match(/^\d+$/)) {
+                                       //alert(eval(subelement+'.'+elementIndex+'._count')+1.0); 
+                                       //if (elementIndexes[i+1] > eval(subelement+'.'+elementIndex+'._count')) {
+                                       //    if (elementIndexes[i+1] == eval(subelement+'.'+elementIndex+'._count')) {
+                                       //      eval(subelement+'.'+elementIndex+'._count')+1.0;
+                                       //    }
+                                           subelement = subelement.concat('.'+elementIndex+'_'+elementIndexes[i+1]);
+                                           i++;
+                                       //} 
+                                   } else {
+                                       subelement = subelement.concat('.'+elementIndex);
+                                   }
+                                   
+                                   if ((typeof eval(subelement)) == "undefined") {
+                                       eval(subelement+' = new Object();');
+                                       /*if (elementIndexes[i].match(/^\d+$/)) {
+                                           alert(subelement.substring(0,subelement.length-elementIndexes[i]-1));
+                                           if ((typeof eval(subelement.substring(0,subelement.length-elementIndexes[i]-1))) == "undefined") {
+                                               // create new count
+                                           }
+                                       } */
+                                   }
+                               }
+                               element = subelement.concat('.'+elementIndexes[elementIndexes.length-1]);
+                               //alert('LMSSetValue: '+element+'\nModel: '+elementmodel+'\nValue: '+value+'\nMatches: '+matches);
+                           }
+                           //Store data
+                           if ((typeof eval('datamodel["'+elementmodel+'"].range')) != "undefined") {
+                               range = eval('datamodel["'+elementmodel+'"].range');
+                               ranges = range.split('#');
+                               value = value+0.0;
+                               if ((value >= ranges[0]) && (value <= ranges[1])) {
+                                   eval(element+'="'+value+'";');
+                                   errorCode = "0";
+                                   return "true";
+                               } else {
+                                   errorCode = eval('datamodel["'+elementmodel+'"].writeerror');
+                               }
+                           } else {
+                               eval(element+'="'+value+'";');
+                               errorCode = "0";
+                               return "true";
+                           }
+                       } else {
+                           errorCode = eval('datamodel["'+elementmodel+'"].writeerror');
+                       }
+                   } else {
+                       errorCode = eval('datamodel["'+elementmodel+'"].writeerror');
+                   }
+               } else {
+                   errorCode = "401"
+               }
+           } else {
+               errorCode = "201";
+           }
+       } else {
+            errorCode = "301";
+        }
+       alert('LMSSetValue: '+element+'\nValue: '+value+'\nPattern: '+expression+'\nMatches: '+matches+'\nError Code: '+errorCode);
+       return "false";
     }
     
     function LMSCommit (param) {
-       return SCORM_Call('LMSGetValue',param);
+       if (param == "") {
+            if (Initialized) {
+               errorCode = "0";
+               return "true";
+            } else {
+               errorCode = "301";
+           }
+        } else {
+            errorCode = "201";
+        }
+        return "false";
     }
     
     function LMSGetLastError () {
@@ -97,22 +225,29 @@ function SCORMapi1_2() {
     }
     
     function LMSGetErrorString (param) {
-       var errorString = new Array();
-       errorString["0"] = "No error";
-       errorString["101"] = "General exception";
-       errorString["201"] = "Invalid argument error";
-       errorString["202"] = "Element cannot have children";
-       errorString["203"] = "Element not an array - cannot have count";
-       errorString["301"] = "Not initialized";
-       errorString["401"] = "Not implemented error";
-       errorString["402"] = "Invalid set value, element is a keyword";
-       errorString["403"] = "Element is read only";
-       errorString["404"] = "Element is write only";
-       errorString["405"] = "Incorrect data type";
-       return errorString[param];
+       if (param != "") {
+           var errorString = new Array();
+           errorString["0"] = "No error";
+           errorString["101"] = "General exception";
+           errorString["201"] = "Invalid argument error";
+           errorString["202"] = "Element cannot have children";
+           errorString["203"] = "Element not an array - cannot have count";
+           errorString["301"] = "Not initialized";
+           errorString["401"] = "Not implemented error";
+           errorString["402"] = "Invalid set value, element is a keyword";
+           errorString["403"] = "Element is read only";
+           errorString["404"] = "Element is write only";
+           errorString["405"] = "Incorrect data type";
+           return errorString[param];
+       } else {
+          return "";
+       }
     }
     
     function LMSGetDiagnostic (param) {
+       if (param == "") {
+           param = errorCode;
+       }
        return param;
     }
     
@@ -202,4 +337,4 @@ function SCORMapi2004() {
     this.GetDiagnostic = GetDiagnostic;
 }
 
-var API_1484_11 = new SCORMapi2004();
\ No newline at end of file
+var API_1484_11 = new SCORMapi2004();
index 0580508..910a4ea 100755 (executable)
 
     require_login($course->id, false, $cm);
     
-    if (isset($_POST['call']) && confirm_sesskey()) {
-       
+    
+    if (isset($_GET['call']) && confirm_sesskey()) {
+       if (strstr($_GET['call'],'LMS') !== false) {
+           // SCORM 1.2 Call
+           //require_once('datamodels/scorm1_2.php');
+       } else {
+           // SCORM 1.3 (aka SCORM 2004) Call
+           //require_once('datamodels/scorm1_3.php');
+       }
     }
 ?>
 
diff --git a/mod/scorm/datamodels/scorm1_2.js b/mod/scorm/datamodels/scorm1_2.js
new file mode 100644 (file)
index 0000000..76e6297
--- /dev/null
@@ -0,0 +1,103 @@
+    // Standard Data Type Definition
+    CMIString255 = '^.{0,255}$';
+    CMIString4096 = '^[.|\\n|\\r]{0,4096}$';
+    CMITime = '^([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,2})?$';
+    CMITimespan = '^([0-9]{2,4}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,2})?$';
+    CMIInteger = '^\\d+$';
+    CMISInteger = '^-?([0-9]+)$';
+    CMIDecimal = '^[0-9]?(\.[0-9]{1,2})?$';
+    CMIIdentifier = '^\\w{0,255}$';
+    CMIFeedback = CMIString255; // This must be redefined
+    CMIIndex = '.\\d+.';
+    // Vocabulary Data Type Definition
+    CMIStatus = '^passed|completed|failed|incomplete|browsed|not attempted$';
+    CMIExit = '^time-out|suspend|logout|$';
+    CMIType = '^true-false|choice|fill-in|matching|performance|sequencing|likert|numeric$';
+    CMIResult = '^correct|wrong|unanticipated|neutral|[0-9]?(\.[0-9]{1,2})?$';
+    // Children lists
+    cmi_children = 'core, suspend_data, launch_data, comments, objectives, student_data, student_preference, interactions';
+    core_children = 'student_id, student_name, lesson_location, credit, lesson_status, entry, score, total_time, lesson_mode, exit, session_time';
+    score_children = 'raw, min, max';
+    objectives_children = 'id, score, status';
+    student_data_children = 'mastery_score, max_time_allowed, time_limit_action';
+    student_preference_children = 'audio, language, speed, text';
+    interactions_children = 'id, objectives, time, type, correct_responses, weighting, student_response, result, latency';
+    // Data ranges
+    score_range = '0#100';
+    audio_range = '-1#100';
+    speed_range = '-100#100';
+    text_range = '-1#1';
+    // The SCORM 1.2 data model
+    var datamodel =  {
+       'cmi._children':{'defaultvalue':cmi_children, 'mod':'r', 'writeerror':'402'},
+       'cmi._version':{'defaultvalue':'3.4', 'mod':'r', 'writeerror':'402'},
+       'cmi.core._children':{'defaultvalue':core_children, 'mod':'r', 'writeerror':'402'},
+       'cmi.core.student_id':{'defaultvalue':'<?php echo $USER->username ?>', 'mod':'r', 'writeerror':'403'},
+       'cmi.core.student_name':{'defaultvalue':'<?php echo $USER->lastname.', '.$USER->firstname ?>', 'mod':'r', 'writeerror':'403'},
+       'cmi.core.lesson_location':{'format':CMIString255, 'mod':'rw', 'writeerror':'405'},
+       'cmi.core.credit':{'mod':'r', 'writeerror':'403'},
+       'cmi.core.lesson_status':{'format':CMIStatus, 'mod':'rw', 'writeerror':'405'},
+       'cmi.core.entry':{'mod':'r', 'writeerror':'403'},
+       'cmi.core.score._children':{'defaultvalue':score_children, 'mod':'r', 'writeerror':'402'},
+       'cmi.core.score.raw':{'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.core.score.max':{'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.core.score.min':{'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.core.total_time':{'mod':'r', 'writeerror':'403'},
+       'cmi.core.lesson_mode':{'mod':'r', 'writeerror':'405'},
+       'cmi.core.exit':{'format':CMIExit, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.core.session_time':{'format':CMITimespan, 'mod':'w', 'defaultvalue':'00:00:00', 'readerror':'404', 'writeerror':'405'},
+       'cmi.suspend_data':{'format':CMIString4096, 'mod':'rw', 'writeerror':'405'},
+       'cmi.launch_data':{'mod':'r', 'writeerror':'403'},
+       'cmi.comments':{'format':CMIString4096, 'mod':'rw', 'writeerror':'405'},
+       'cmi.comments_from_lms':{'mod':'r', 'writeerror':'403'},
+       'cmi.objectives._children':{'defaultvalue':objectives_children, 'mod':'r', 'writeerror':'403'},
+       'cmi.objectives._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
+       'cmi.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'rw', 'writeerror':'405'},
+       'cmi.objectives.n.score._children':{'pattern':CMIIndex, 'defaultvalue':score_children, 'mod':'r', 'writeerror':'402'},
+       'cmi.objectives.n.score.raw':{'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.objectives.n.score.min':{'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.objectives.n.score.max':{'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.objectives.n.status':{'pattern':CMIIndex, 'format':CMIStatus, 'mod':'rw', 'writeerror':'405'},
+       'cmi.student_data._children':{'defaultvalue':student_data_children, 'mod':'r', 'writeerror':'403'},
+       'cmi.student_data.mastery_score':{'mod':'r', 'writeerror':'403'},
+       'cmi.student_data.max_time_allowed':{'mod':'r', 'writeerror':'403'},
+       'cmi.student_data.time_limit_action':{'mod':'r', 'writeerror':'403'},
+       'cmi.student_preference._children':{'defaultvalue':student_preference_children, 'mod':'r', 'writeerror':'403'},
+       'cmi.student_preference.audio':{'format':CMISInteger, 'range':audio_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.student_preference.language':{'format':CMIString255, 'mod':'rw', 'writeerror':'405'},
+       'cmi.student_preference.speed':{'format':CMISInteger, 'range':speed_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.student_preference.text':{'format':CMISInteger, 'range':text_range, 'mod':'rw', 'writeerror':'405'},
+       'cmi.interactions._children':{'defaultvalue':interactions_children, 'mod':'r', 'writeerror':'403'},
+       'cmi.interactions._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
+       'cmi.interactions.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.interactions.n.objectives._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
+       'cmi.interactions.n.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.interactions.n.time':{'pattern':CMIIndex, 'format':CMITime, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.interactions.n.type':{'pattern':CMIIndex, 'format':CMIType, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.interactions.n.correct_responses._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
+       'cmi.interactions.n.correct_responses.n.pattern':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.interactions.n.weighting':{'pattern':CMIIndex, 'format':CMIDecimal, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.interactions.n.student_response':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.interactions.n.result':{'pattern':CMIIndex, 'format':CMIResult, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
+       'cmi.interactions.n.latency':{'pattern':CMIIndex, 'format':CMITimespan, 'mod':'w', 'readerror':'404', 'writeerror':'405'}
+    };
+    // Datamodel inizialization
+    var cmi = new Object();
+       cmi.core = new Object();
+       cmi.core.score = new Object();
+       cmi.objectives = new Object();
+       cmi.student_data = new Object();
+       cmi.student_preference = new Object();
+       cmi.interactions = new Object();
+    var nav = new Object();
+    for (element in datamodel) {
+       if (element.match(/\.n\./) == null) {
+           //alert (element+' = '+eval('datamodel["'+element+'"].defaultvalue'));
+           if ((typeof eval('datamodel["'+element+'"].defaultvalue')) != 'undefined') {
+               eval(element+' = datamodel["'+element+'"].defaultvalue;');
+           } else {
+               eval(element+' = "";');
+           }
+           //alert (element+' = '+eval(element));
+       }
+    } 
index 4e498f3..d16a8b4 100755 (executable)
                    $options[$sco->id] = $indenting."&gt; ".$sco->title;
                }
            }
-           choose_from_menu($options, "courseStructure", "", $scoid, "document.navform.scoid.value=document.navform.courseStructure.options[document.navform.courseStructure.selectedIndex].value;document.navform.submit();");
+           choose_from_menu($options, "courseStructure", $scoid, "", "document.navform.scoid.value=document.navform.courseStructure.options[document.navform.courseStructure.selectedIndex].value;document.navform.submit();");
        }
        echo "          &nbsp;<input name=\"next\" type=\"button\" value=\"".get_string("next","scorm")."\" onClick=\"top.changeSco('continue')\" />\n";
        echo "       </form>
index 8eb78aa..8e7b3e7 100755 (executable)
@@ -5,7 +5,7 @@
 ///  This fragment is called by moodle_needs_upgrading() and /admin/index.php
 /////////////////////////////////////////////////////////////////////////////////
 
-$module->version  = 2005021700;   // The (date) version of this module
+$module->version  = 2005022100;   // The (date) version of this module
 $module->requires = 2005021600;   // The version of Moodle that is required
 $module->cron     = 0;            // How often should cron check this module (seconds)?