MDL-57660 mod_forum: Preserve forum ID selection after form submission
[moodle.git] / mod / scorm / datamodels / scorm_12.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 //
17 // SCORM 1.2 API Implementation
18 //
19 function SCORMapi1_2(def, cmiobj, cmiint, cmistring256, cmistring4096, scormdebugging, scormauto, scormid, cfgwwwroot, sesskey, scoid, attempt, viewmode, cmid, currentorg, autocommit, masteryoverride) {
21     var prerequrl = cfgwwwroot + "/mod/scorm/prereqs.php?a=" + scormid + "&scoid=" + scoid + "&attempt=" + attempt + "&mode=" + viewmode + "&currentorg=" + currentorg + "&sesskey=" + sesskey;
22     var datamodelurl = cfgwwwroot + "/mod/scorm/datamodel.php";
23     var datamodelurlparams = "id=" + cmid + "&a=" + scormid + "&sesskey=" + sesskey + "&attempt=" + attempt + "&scoid=" + scoid;
25     // Standard Data Type Definition
26     CMIString256 = cmistring256;
27     CMIString4096 = cmistring4096;
28     CMITime = '^([0-2]{1}[0-9]{1}):([0-5]{1}[0-9]{1}):([0-5]{1}[0-9]{1})(\.[0-9]{1,2})?$';
29     CMITimespan = '^([0-9]{2,4}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,2})?$';
30     CMIInteger = '^\\d+$';
31     CMISInteger = '^-?([0-9]+)$';
32     CMIDecimal = '^-?([0-9]{0,3})(\.[0-9]*)?$';
33     CMIIdentifier = '^[\\u0021-\\u007E]{0,255}$';
34     CMIFeedback = CMIString256; // This must be redefined
35     CMIIndex = '[._](\\d+).';
36     // Vocabulary Data Type Definition
37     CMIStatus = '^passed$|^completed$|^failed$|^incomplete$|^browsed$';
38     CMIStatus2 = '^passed$|^completed$|^failed$|^incomplete$|^browsed$|^not attempted$';
39     CMIExit = '^time-out$|^suspend$|^logout$|^$';
40     CMIType = '^true-false$|^choice$|^fill-in$|^matching$|^performance$|^sequencing$|^likert$|^numeric$';
41     CMIResult = '^correct$|^wrong$|^unanticipated$|^neutral$|^([0-9]{0,3})?(\.[0-9]*)?$';
42     NAVEvent = '^previous$|^continue$';
43     // Children lists
44     cmi_children = 'core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions';
45     core_children = 'student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,lesson_mode,exit,session_time';
46     score_children = 'raw,min,max';
47     comments_children = 'content,location,time';
48     objectives_children = 'id,score,status';
49     correct_responses_children = 'pattern';
50     student_data_children = 'mastery_score,max_time_allowed,time_limit_action';
51     student_preference_children = 'audio,language,speed,text';
52     interactions_children = 'id,objectives,time,type,correct_responses,weighting,student_response,result,latency';
53     // Data ranges
54     score_range = '0#100';
55     audio_range = '-1#100';
56     speed_range = '-100#100';
57     weighting_range = '-100#100';
58     text_range = '-1#1';
60     // The SCORM 1.2 data model
61     // Set up data model for each sco
62     var datamodel = {};
63     for(scoid in def){
64         datamodel[scoid] = {
65             'cmi._children':{'defaultvalue':cmi_children, 'mod':'r', 'writeerror':'402'},
66             'cmi._version':{'defaultvalue':'3.4', 'mod':'r', 'writeerror':'402'},
67             'cmi.core._children':{'defaultvalue':core_children, 'mod':'r', 'writeerror':'402'},
68             'cmi.core.student_id':{'defaultvalue':def[scoid]['cmi.core.student_id'], 'mod':'r', 'writeerror':'403'},
69             'cmi.core.student_name':{'defaultvalue':def[scoid]['cmi.core.student_name'], 'mod':'r', 'writeerror':'403'},
70             'cmi.core.lesson_location':{'defaultvalue':def[scoid]['cmi.core.lesson_location'], 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
71             'cmi.core.credit':{'defaultvalue':def[scoid]['cmi.core.credit'], 'mod':'r', 'writeerror':'403'},
72             'cmi.core.lesson_status':{'defaultvalue':def[scoid]['cmi.core.lesson_status'], 'format':CMIStatus, 'mod':'rw', 'writeerror':'405'},
73             'cmi.core.entry':{'defaultvalue':def[scoid]['cmi.core.entry'], 'mod':'r', 'writeerror':'403'},
74             'cmi.core.score._children':{'defaultvalue':score_children, 'mod':'r', 'writeerror':'402'},
75             'cmi.core.score.raw':{'defaultvalue':def[scoid]['cmi.core.score.raw'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
76             'cmi.core.score.max':{'defaultvalue':def[scoid]['cmi.core.score.max'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
77             'cmi.core.score.min':{'defaultvalue':def[scoid]['cmi.core.score.min'], 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
78             'cmi.core.total_time':{'defaultvalue':def[scoid]['cmi.core.total_time'], 'mod':'r', 'writeerror':'403'},
79             'cmi.core.lesson_mode':{'defaultvalue':def[scoid]['cmi.core.lesson_mode'], 'mod':'r', 'writeerror':'403'},
80             'cmi.core.exit':{'defaultvalue':def[scoid]['cmi.core.exit'], 'format':CMIExit, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
81             'cmi.core.session_time':{'format':CMITimespan, 'mod':'w', 'defaultvalue':'00:00:00', 'readerror':'404', 'writeerror':'405'},
82             'cmi.suspend_data':{'defaultvalue':def[scoid]['cmi.suspend_data'], 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'},
83             'cmi.launch_data':{'defaultvalue':def[scoid]['cmi.launch_data'], 'mod':'r', 'writeerror':'403'},
84             'cmi.comments':{'defaultvalue':def[scoid]['cmi.comments'], 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'},
85             // deprecated evaluation attributes
86             'cmi.evaluation.comments._count':{'defaultvalue':'0', 'mod':'r', 'writeerror':'402'},
87             'cmi.evaluation.comments._children':{'defaultvalue':comments_children, 'mod':'r', 'writeerror':'402'},
88             'cmi.evaluation.comments.n.content':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
89             'cmi.evaluation.comments.n.location':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
90             'cmi.evaluation.comments.n.time':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMITime, 'mod':'rw', 'writeerror':'405'},
91             'cmi.comments_from_lms':{'mod':'r', 'writeerror':'403'},
92             'cmi.objectives._children':{'defaultvalue':objectives_children, 'mod':'r', 'writeerror':'402'},
93             'cmi.objectives._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
94             'cmi.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'rw', 'writeerror':'405'},
95             'cmi.objectives.n.score._children':{'pattern':CMIIndex, 'mod':'r', 'writeerror':'402'},
96             'cmi.objectives.n.score.raw':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
97             'cmi.objectives.n.score.min':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
98             'cmi.objectives.n.score.max':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'},
99             'cmi.objectives.n.status':{'pattern':CMIIndex, 'format':CMIStatus2, 'mod':'rw', 'writeerror':'405'},
100             'cmi.student_data._children':{'defaultvalue':student_data_children, 'mod':'r', 'writeerror':'402'},
101             'cmi.student_data.mastery_score':{'defaultvalue':def[scoid]['cmi.student_data.mastery_score'], 'mod':'r', 'writeerror':'403'},
102             'cmi.student_data.max_time_allowed':{'defaultvalue':def[scoid]['cmi.student_data.max_time_allowed'], 'mod':'r', 'writeerror':'403'},
103             'cmi.student_data.time_limit_action':{'defaultvalue':def[scoid]['cmi.student_data.time_limit_action'], 'mod':'r', 'writeerror':'403'},
104             'cmi.student_preference._children':{'defaultvalue':student_preference_children, 'mod':'r', 'writeerror':'402'},
105             'cmi.student_preference.audio':{'defaultvalue':def[scoid]['cmi.student_preference.audio'], 'format':CMISInteger, 'range':audio_range, 'mod':'rw', 'writeerror':'405'},
106             'cmi.student_preference.language':{'defaultvalue':def[scoid]['cmi.student_preference.language'], 'format':CMIString256, 'mod':'rw', 'writeerror':'405'},
107             'cmi.student_preference.speed':{'defaultvalue':def[scoid]['cmi.student_preference.speed'], 'format':CMISInteger, 'range':speed_range, 'mod':'rw', 'writeerror':'405'},
108             'cmi.student_preference.text':{'defaultvalue':def[scoid]['cmi.student_preference.text'], 'format':CMISInteger, 'range':text_range, 'mod':'rw', 'writeerror':'405'},
109             'cmi.interactions._children':{'defaultvalue':interactions_children, 'mod':'r', 'writeerror':'402'},
110             'cmi.interactions._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
111             'cmi.interactions.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
112             'cmi.interactions.n.objectives._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
113             'cmi.interactions.n.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
114             'cmi.interactions.n.time':{'pattern':CMIIndex, 'format':CMITime, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
115             'cmi.interactions.n.type':{'pattern':CMIIndex, 'format':CMIType, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
116             'cmi.interactions.n.correct_responses._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'},
117             'cmi.interactions.n.correct_responses.n.pattern':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
118             'cmi.interactions.n.weighting':{'pattern':CMIIndex, 'format':CMIDecimal, 'range':weighting_range, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
119             'cmi.interactions.n.student_response':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
120             'cmi.interactions.n.result':{'pattern':CMIIndex, 'format':CMIResult, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
121             'cmi.interactions.n.latency':{'pattern':CMIIndex, 'format':CMITimespan, 'mod':'w', 'readerror':'404', 'writeerror':'405'},
122             'nav.event':{'defaultvalue':'', 'format':NAVEvent, 'mod':'w', 'readerror':'404', 'writeerror':'405'}
123         };
124     }
126     var cmi, nav;
127     function initdatamodel(scoid){
128         prerequrl = cfgwwwroot + "/mod/scorm/prereqs.php?a=" + scormid + "&scoid=" + scoid + "&attempt=" + attempt + "&mode=" + viewmode + "&currentorg=" + currentorg + "&sesskey=" + sesskey;
129         datamodelurlparams = "id=" + cmid + "&a=" + scormid + "&sesskey=" + sesskey + "&attempt=" + attempt + "&scoid=" + scoid;
131         //
132         // Datamodel inizialization
133         //
134         cmi = new Object();
135             cmi.core = new Object();
136             cmi.core.score = new Object();
137             cmi.objectives = new Object();
138             cmi.student_data = new Object();
139             cmi.student_preference = new Object();
140             cmi.interactions = new Object();
141             // deprecated evaluation attributes
142             cmi.evaluation = new Object();
143             cmi.evaluation.comments = new Object();
145         // Navigation Object
146         nav = new Object();
148         for (element in datamodel[scoid]) {
149             if (element.match(/\.n\./) == null) {
150                 if ((typeof eval('datamodel["' + scoid + '"]["' + element + '"].defaultvalue')) != 'undefined') {
151                     eval(element + ' = datamodel["' + scoid + '"]["' + element + '"].defaultvalue;');
152                 } else {
153                     eval(element + ' = "";');
154                 }
155             }
156         }
158         eval(cmiobj[scoid]);
159         eval(cmiint[scoid]);
161         if (cmi.core.lesson_status == '') {
162             cmi.core.lesson_status = 'not attempted';
163         }
164     }
166     //
167     // API Methods definition
168     //
169     var Initialized = false;
171     function LMSInitialize (param) {
172         scoid = (scorm_current_node && scorm_current_node.scoid) ?  scorm_current_node.scoid : scoid;
173         initdatamodel(scoid);
175         errorCode = "0";
176         if (param == "") {
177             if (!Initialized) {
178                 Initialized = true;
179                 errorCode = "0";
180                 if (scormdebugging) {
181                     LogAPICall("LMSInitialize", param, "", errorCode);
182                 }
183                 return "true";
184             } else {
185                 errorCode = "101";
186             }
187         } else {
188             errorCode = "201";
189         }
190         if (scormdebugging) {
191             LogAPICall("LMSInitialize", param, "", errorCode);
192         }
193         return "false";
194     }
196     function LMSFinish (param) {
197         errorCode = "0";
198         if (param == "") {
199             if (Initialized) {
200                 Initialized = false;
201                 result = StoreData(cmi,true);
202                 if (nav.event != '') {
203                     if (nav.event == 'continue') {
204                         setTimeout('mod_scorm_launch_next_sco();',500);
205                     } else {
206                         setTimeout('mod_scorm_launch_prev_sco();',500);
207                     }
208                 } else {
209                     if (scormauto == 1) {
210                         setTimeout('mod_scorm_launch_next_sco();',500);
211                     }
212                 }
213                 if (scormdebugging) {
214                     LogAPICall("LMSFinish", "AJAXResult", result, 0);
215                 }
216                 result = ('true' == result) ? 'true' : 'false';
217                 errorCode = (result == 'true') ? '0' : '101';
218                 if (scormdebugging) {
219                     LogAPICall("LMSFinish", "result", result, 0);
220                     LogAPICall("LMSFinish", param, "", 0);
221                 }
222                 // trigger TOC update
223                 var callback = M.mod_scorm.connectPrereqCallback;
224                 YUI().use('io-base', function(Y) {
225                     Y.on('io:complete', callback.success, Y);
226                     Y.io(prerequrl);
227                 });
228                 return result;
229             } else {
230                 errorCode = "301";
231             }
232         } else {
233             errorCode = "201";
234         }
235         if (scormdebugging) {
236             LogAPICall("LMSFinish", param, "", errorCode);
237         }
238         return "false";
239     }
241     function LMSGetValue (element) {
242         errorCode = "0";
243         if (Initialized) {
244             if (element != "") {
245                 expression = new RegExp(CMIIndex,'g');
246                 elementmodel = String(element).replace(expression,'.n.');
247                 if ((typeof eval('datamodel["' + scoid + '"]["' + elementmodel + '"]')) != "undefined") {
248                     if (eval('datamodel["' + scoid + '"]["' + elementmodel + '"].mod') != 'w') {
249                         element = String(element).replace(expression, "_$1.");
250                         elementIndexes = element.split('.');
251                         subelement = 'cmi';
252                         i = 1;
253                         while ((i < elementIndexes.length) && (typeof eval(subelement) != "undefined")) {
254                             subelement += '.' + elementIndexes[i++];
255                         }
256                             if (subelement == element) {
257                             errorCode = "0";
258                             if (scormdebugging) {
259                                 LogAPICall("LMSGetValue", element, eval(element), 0);
260                             }
261                             return eval(element);
262                         } else {
263                             errorCode = "0"; // Need to check if it is the right errorCode
264                         }
265                     } else {
266                         errorCode = eval('datamodel["' + scoid + '"]["' + elementmodel + '"].readerror');
267                     }
268                 } else {
269                     childrenstr = '._children';
270                     countstr = '._count';
271                     if (elementmodel.substr(elementmodel.length - childrenstr.length,elementmodel.length) == childrenstr) {
272                         parentmodel = elementmodel.substr(0,elementmodel.length - childrenstr.length);
273                         if ((typeof eval('datamodel["' + scoid + '"]["' + parentmodel + '"]')) != "undefined") {
274                             errorCode = "202";
275                         } else {
276                             errorCode = "201";
277                         }
278                     } else if (elementmodel.substr(elementmodel.length - countstr.length,elementmodel.length) == countstr) {
279                         parentmodel = elementmodel.substr(0,elementmodel.length - countstr.length);
280                         if ((typeof eval('datamodel["' + scoid + '"]["' + parentmodel + '"]')) != "undefined") {
281                             errorCode = "203";
282                         } else {
283                             errorCode = "201";
284                         }
285                     } else {
286                         errorCode = "201";
287                     }
288                 }
289             } else {
290                 errorCode = "201";
291             }
292         } else {
293             errorCode = "301";
294         }
295         if (scormdebugging) {
296             LogAPICall("LMSGetValue", element, "", errorCode);
297         }
298         return "";
299     }
301     function LMSSetValue (element,value) {
302         errorCode = "0";
303         if (Initialized) {
304             if (element != "") {
305                 expression = new RegExp(CMIIndex,'g');
306                 elementmodel = String(element).replace(expression,'.n.');
307                 if ((typeof eval('datamodel["' + scoid + '"]["' + elementmodel + '"]')) != "undefined") {
308                     if (eval('datamodel["' + scoid + '"]["' + elementmodel + '"].mod') != 'r') {
309                         expression = new RegExp(eval('datamodel["' + scoid + '"]["' + elementmodel + '"].format'));
310                         value = value + '';
311                         matches = value.match(expression);
312                         if (matches != null) {
313                             //Create dynamic data model element
314                             if (element != elementmodel) {
315                                 elementIndexes = element.split('.');
316                                 subelement = 'cmi';
317                                 for (i = 1; i < elementIndexes.length - 1; i++) {
318                                     elementIndex = elementIndexes[i];
319                                     if (elementIndexes[i + 1].match(/^\d+$/)) {
320                                         if ((typeof eval(subelement + '.' + elementIndex)) == "undefined") {
321                                             eval(subelement + '.' + elementIndex + ' = new Object();');
322                                             eval(subelement + '.' + elementIndex + '._count = 0;');
323                                         }
324                                         if (elementIndexes[i + 1] == eval(subelement + '.' + elementIndex + '._count')) {
325                                             eval(subelement + '.' + elementIndex + '._count++;');
326                                         }
327                                         if (elementIndexes[i + 1] > eval(subelement + '.' + elementIndex + '._count')) {
328                                             errorCode = "201";
329                                         }
330                                         subelement = subelement.concat('.' + elementIndex + '_' + elementIndexes[i + 1]);
331                                         i++;
332                                     } else {
333                                         subelement = subelement.concat('.' + elementIndex);
334                                     }
335                                     if ((typeof eval(subelement)) == "undefined") {
336                                         eval(subelement + ' = new Object();');
337                                         if (subelement.substr(0,14) == 'cmi.objectives') {
338                                             eval(subelement + '.score = new Object();');
339                                             eval(subelement + '.score._children = score_children;');
340                                             eval(subelement + '.score.raw = "";');
341                                             eval(subelement + '.score.min = "";');
342                                             eval(subelement + '.score.max = "";');
343                                         }
344                                         if (subelement.substr(0,16) == 'cmi.interactions') {
345                                             eval(subelement + '.objectives = new Object();');
346                                             eval(subelement + '.objectives._count = 0;');
347                                             eval(subelement + '.correct_responses = new Object();');
348                                             eval(subelement + '.correct_responses._count = 0;');
349                                         }
350                                     }
351                                 }
352                                 element = subelement.concat('.' + elementIndexes[elementIndexes.length - 1]);
353                             }
354                             //Store data
355                             if (errorCode == "0") {
356                                 if (autocommit && !(SCORMapi1_2.timeout)) {
357                                     SCORMapi1_2.timeout = Y.later(60000, API, 'LMSCommit', [""], false);
358                                 }
359                                 if ((typeof eval('datamodel["' + scoid + '"]["' + elementmodel + '"].range')) != "undefined") {
360                                     range = eval('datamodel["' + scoid + '"]["' + elementmodel + '"].range');
361                                     ranges = range.split('#');
362                                     value = value * 1.0;
363                                     if ((value >= ranges[0]) && (value <= ranges[1])) {
364                                         eval(element + '=value;');
365                                         errorCode = "0";
366                                         if (scormdebugging) {
367                                             LogAPICall("LMSSetValue", element, value, errorCode);
368                                         }
369                                         return "true";
370                                     } else {
371                                         errorCode = eval('datamodel["' + scoid + '"]["' + elementmodel + '"].writeerror');
372                                     }
373                                 } else {
374                                     if (element == 'cmi.comments') {
375                                         cmi.comments = cmi.comments + value;
376                                     } else {
377                                         eval(element + '=value;');
378                                     }
379                                     errorCode = "0";
380                                     if (scormdebugging) {
381                                         LogAPICall("LMSSetValue", element, value, errorCode);
382                                     }
383                                     return "true";
384                                 }
385                             }
386                         } else {
387                             errorCode = eval('datamodel["' + scoid + '"]["' + elementmodel + '"].writeerror');
388                         }
389                     } else {
390                         errorCode = eval('datamodel["' + scoid + '"]["' + elementmodel + '"].writeerror');
391                     }
392                 } else {
393                     errorCode = "201"
394                 }
395             } else {
396                 errorCode = "201";
397             }
398         } else {
399             errorCode = "301";
400         }
401         if (scormdebugging) {
402             LogAPICall("LMSSetValue", element, value, errorCode);
403         }
404         return "false";
405     }
407     function LMSCommit (param) {
408         if (SCORMapi1_2.timeout) {
409             SCORMapi1_2.timeout.cancel();
410             SCORMapi1_2.timeout = null;
411         }
412         errorCode = "0";
413         if (param == "") {
414             if (Initialized) {
415                 result = StoreData(cmi,false);
416                 // trigger TOC update
417                 var callback = M.mod_scorm.connectPrereqCallback;
418                 YUI().use('io-base', function(Y) {
419                     Y.on('io:complete', callback.success, Y);
420                     Y.io(prerequrl);
421                 });
422                 if (scormdebugging) {
423                     LogAPICall("Commit", param, "", 0);
424                 }
425                 if (scormdebugging) {
426                     LogAPICall("LMSCommit", "AJAXResult", result, 0);
427                 }
428                 result = ('true' == result) ? 'true' : 'false';
429                 errorCode = (result == 'true') ? '0' : '101';
430                 if (scormdebugging) {
431                     LogAPICall("LMSCommit", "result", result, 0);
432                     LogAPICall("LMSCommit", "errorCode", errorCode, 0);
433                 }
434                 return result;
435             } else {
436                 errorCode = "301";
437             }
438         } else {
439             errorCode = "201";
440         }
441         if (scormdebugging) {
442             LogAPICall("LMSCommit", param, "", 0);
443         }
444         return "false";
445     }
447     function LMSGetLastError () {
448         if (scormdebugging) {
449             LogAPICall("LMSGetLastError", "", "", errorCode);
450         }
451         return errorCode;
452     }
454     function LMSGetErrorString (param) {
455         if (param != "") {
456             var errorString = new Array();
457             errorString["0"] = "No error";
458             errorString["101"] = "General exception";
459             errorString["201"] = "Invalid argument error";
460             errorString["202"] = "Element cannot have children";
461             errorString["203"] = "Element not an array - cannot have count";
462             errorString["301"] = "Not initialized";
463             errorString["401"] = "Not implemented error";
464             errorString["402"] = "Invalid set value, element is a keyword";
465             errorString["403"] = "Element is read only";
466             errorString["404"] = "Element is write only";
467             errorString["405"] = "Incorrect data type";
468             if (scormdebugging) {
469                 LogAPICall("LMSGetErrorString", param,  errorString[param], 0);
470             }
471             return errorString[param];
472         } else {
473             if (scormdebugging) {
474                 LogAPICall("LMSGetErrorString", param,  "No error string found!", 0);
475             }
476            return "";
477         }
478     }
480     function LMSGetDiagnostic (param) {
481         if (param == "") {
482             param = errorCode;
483         }
484         if (scormdebugging) {
485             LogAPICall("LMSGetDiagnostic", param, param, 0);
486         }
487         return param;
488     }
490     function AddTime (first, second) {
491         var sFirst = first.split(":");
492         var sSecond = second.split(":");
493         var cFirst = sFirst[2].split(".");
494         var cSecond = sSecond[2].split(".");
495         var change = 0;
497         FirstCents = 0;  //Cents
498         if (cFirst.length > 1) {
499             FirstCents = parseInt(cFirst[1],10);
500         }
501         SecondCents = 0;
502         if (cSecond.length > 1) {
503             SecondCents = parseInt(cSecond[1],10);
504         }
505         var cents = FirstCents + SecondCents;
506         change = Math.floor(cents / 100);
507         cents = cents - (change * 100);
508         if (Math.floor(cents) < 10) {
509             cents = "0" + cents.toString();
510         }
512         var secs = parseInt(cFirst[0],10) + parseInt(cSecond[0],10) + change;  //Seconds
513         change = Math.floor(secs / 60);
514         secs = secs - (change * 60);
515         if (Math.floor(secs) < 10) {
516             secs = "0" + secs.toString();
517         }
519         mins = parseInt(sFirst[1],10) + parseInt(sSecond[1],10) + change;   //Minutes
520         change = Math.floor(mins / 60);
521         mins = mins - (change * 60);
522         if (mins < 10) {
523             mins = "0" + mins.toString();
524         }
526         hours = parseInt(sFirst[0],10) + parseInt(sSecond[0],10) + change;  //Hours
527         if (hours < 10) {
528             hours = "0" + hours.toString();
529         }
531         if (cents != '0') {
532             return hours + ":" + mins + ":" + secs + '.' + cents;
533         } else {
534             return hours + ":" + mins + ":" + secs;
535         }
536     }
538     function TotalTime() {
539         total_time = AddTime(cmi.core.total_time, cmi.core.session_time);
540         return '&' + underscore('cmi.core.total_time') + '=' + encodeURIComponent(total_time);
541     }
543     function CollectData(data,parent) {
544         var datastring = '';
545         for (property in data) {
546             if (typeof data[property] == 'object') {
547                 datastring += CollectData(data[property],parent + '.' + property);
548             } else {
549                 element = parent + '.' + property;
550                 expression = new RegExp(CMIIndex,'g');
552                 // get the generic name for this element (e.g. convert 'cmi.interactions.1.id' to 'cmi.interactions.n.id')
553                 elementmodel = String(element).replace(expression,'.n.');
555                 // ignore the session time element
556                 if (element != "cmi.core.session_time") {
558                     // check if this specific element is not defined in the datamodel,
559                     // but the generic element name is
560                     if ((eval('typeof datamodel["' + scoid + '"]["' + element + '"]')) == "undefined"
561                         && (eval('typeof datamodel["' + scoid + '"]["' + elementmodel + '"]')) != "undefined") {
563                         // add this specific element to the data model (by cloning
564                         // the generic element) so we can track changes to it
565                         eval('datamodel["' + scoid + '"]["' + element + '"]=CloneObj(datamodel["' + scoid + '"]["' + elementmodel + '"]);');
566                     }
568                     // check if the current element exists in the datamodel
569                     if ((typeof eval('datamodel["' + scoid + '"]["' + element + '"]')) != "undefined") {
571                         // make sure this is not a read only element
572                         if (eval('datamodel["' + scoid + '"]["' + element + '"].mod') != 'r') {
574                             elementstring = '&' + underscore(element) + '=' + encodeURIComponent(data[property]);
576                             // check if the element has a default value
577                             if ((typeof eval('datamodel["' + scoid + '"]["' + element + '"].defaultvalue')) != "undefined") {
579                                 // check if the default value is different from the current value
580                                 if (eval('datamodel["' + scoid + '"]["' + element + '"].defaultvalue') != data[property]
581                                     || eval('typeof(datamodel["' + scoid + '"]["' + element + '"].defaultvalue)') != typeof(data[property])) {
583                                     // append the URI fragment to the string we plan to commit
584                                     datastring += elementstring;
586                                     // update the element default to reflect the current committed value
587                                     eval('datamodel["' + scoid + '"]["' + element + '"].defaultvalue=data[property];');
588                                 }
589                             } else {
590                                 // append the URI fragment to the string we plan to commit
591                                 datastring += elementstring;
592                                 // no default value for the element, so set it now
593                                 eval('datamodel["' + scoid + '"]["' + element + '"].defaultvalue=data[property];');
594                             }
595                         }
596                     }
597                 }
598             }
599         }
600         return datastring;
601     }
603     function CloneObj(obj){
604         if(obj == null || typeof(obj) != 'object') {
605             return obj;
606         }
608         var temp = new obj.constructor(); // changed (twice)
609         for(var key in obj) {
610             temp[key] = CloneObj(obj[key]);
611         }
613         return temp;
614     }
616     function StoreData(data,storetotaltime) {
617         if (storetotaltime) {
618             if (cmi.core.lesson_status == 'not attempted') {
619                 cmi.core.lesson_status = 'completed';
620             }
621             if (cmi.core.lesson_mode == 'normal') {
622                 if (cmi.core.credit == 'credit') {
623                     if (masteryoverride && cmi.student_data.mastery_score !== '' && cmi.core.score.raw !== '') {
624                         if (parseFloat(cmi.core.score.raw) >= parseFloat(cmi.student_data.mastery_score)) {
625                             cmi.core.lesson_status = 'passed';
626                         } else {
627                             cmi.core.lesson_status = 'failed';
628                         }
629                     }
630                 }
631             }
632             if (cmi.core.lesson_mode == 'browse') {
633                 if (datamodel[scoid]['cmi.core.lesson_status'].defaultvalue == '' && cmi.core.lesson_status == 'not attempted') {
634                     cmi.core.lesson_status = 'browsed';
635                 }
636             }
637             datastring = CollectData(data,'cmi');
638             datastring += TotalTime();
639         } else {
640             datastring = CollectData(data,'cmi');
641         }
643         var myRequest = NewHttpReq();
644         //alert('going to:' + "<?php p($CFG->wwwroot) ?>/mod/scorm/datamodel.php" + "id=<?php p($id) ?>&a=<?php p($a) ?>&sesskey=<?php echo sesskey() ?>"+datastring);
645         result = DoRequest(myRequest,datamodelurl,datamodelurlparams + datastring);
646         results = String(result).split('\n');
647         errorCode = results[1];
648         return results[0];
649     }
651     this.LMSInitialize = LMSInitialize;
652     this.LMSFinish = LMSFinish;
653     this.LMSGetValue = LMSGetValue;
654     this.LMSSetValue = LMSSetValue;
655     this.LMSCommit = LMSCommit;
656     this.LMSGetLastError = LMSGetLastError;
657     this.LMSGetErrorString = LMSGetErrorString;
658     this.LMSGetDiagnostic = LMSGetDiagnostic;
661 M.scorm_api = {};
663 M.scorm_api.init = function(Y, def, cmiobj, cmiint, cmistring256, cmistring4096, scormdebugging, scormauto, scormid, cfgwwwroot, sesskey, scoid, attempt, viewmode, cmid, currentorg, autocommit, masteryoverride) {
664     window.API = new SCORMapi1_2(def, cmiobj, cmiint, cmistring256, cmistring4096, scormdebugging, scormauto, scormid, cfgwwwroot, sesskey, scoid, attempt, viewmode, cmid, currentorg, autocommit, masteryoverride);