unserver-unify
Version:
264 lines (259 loc) • 8.58 kB
JavaScript
angular.module('bamboo.common').service('ScormService', function($timeout, ApiService, $localStorage) {
// default options
var defaultOpts = {
persistFor: 60,
model: null
},
// SCORM 1.2 error codes
errorCodes = {
0: 'No error.',
101: 'General Exception.',
201: 'Invalid argument error.',
202: 'Element cannot have children.',
203: 'Element not an array. Cannot have count.',
301: 'Not initialized.',
401: 'Not implemented error',
402: 'Invalid set value, element is a keyword',
403: 'Element is read only.',
404: 'Element is write only.',
405: 'Incorrect Data Type.'
},
// a default sample of a SCORM 1.2 data model
defaultModel = {
'cmi.core._children': ['student_id', 'student_name', 'lesson_location', 'credit', 'lesson_status', 'entry', 'score', 'total_time', 'lesson_mode', 'exit', 'session_time'],
'cmi.core.student_id': '770b7a652cee065cbb9759ff1a0d46a927c8ba98',
'cmi.core.student_name': 'Appleseed, John',
'cmi.core.lesson_location': '',
'cmi.core.credit': 'credit',
'cmi.core.lesson_status': 'not attempted',
'cmi.core.entry': 'ab-initio',
'cmi.core.score_children': ['raw', 'min', 'max'],
// TODO: limit score.raw to 100 and check its initial value
'cmi.core.score.raw': null,
'cmi.core.score.max': null,
'cmi.core.score.min': null,
// TODO: compute total_time, when setting session_time
'cmi.core.total_time': '0000:00:00.00',
'cmi.core.lesson_mode': 'normal',
'cmi.core.exit': '',
'cmi.core.session_time': '00:00:00',
// TODO: limit suspend_data to 4096 chars
'cmi.suspend_data': '',
'cmi.launch_data': '',
// TODO: limit comments to 4096 chars
'cmi.comments': '',
'cmi.comments_from_lms': '',
'cmi.objectives.0.status': '',
'cmi.objectives._children': ['id', 'score', 'status'],
'cmi.objectives._count': 0,
'cmi.student_data._children': ['mastery_score', 'max_time_allowed', 'time_limit_action'],
// TODO: check student_data initial values
'cmi.student_data.mastery_score': null,
'cmi.student_data.max_time_allowed': null,
'cmi.student_data.time_limit_action': 'continue,no message',
'cmi.student_preference._children': ['audio', 'language', 'speed', 'text'],
'cmi.student_preference.audio': null,
'cmi.student_preference.language': null,
'cmi.student_preference.speed': null,
'cmi.student_preference.text': null,
'cmi.interactions._children': ['id', 'objectives', 'time', 'type', 'correct_responses', 'weighting', 'student_response', 'result', 'latency'],
'cmi.interactions._count': 0
// TODO: correct count, after adding interactions
};
// returns current time with, optionally, seconds added.
function time(s) {
return (new Date()).getTime() + ((s || 0) * 1000);
}
// returns [a shallow copy of] a, including b, without overwriting.
function incl(a, b) {
var c = {};
a = a || {};
b = b || {};
Object.keys(b).forEach(function(k) {
c[k] = typeof a[k] == 'undefined' ? b[k] : a[k];
});
return c;
}
var courseId, userId;
var lmsId;
function mvApi(info, callback) {
ApiService.post('/mvsubjects', info).then(function(result) {
console.log(result);
if (result.data.success) {
if (!callback) {
console.log('success');
} else {
callback(result.data.data);
}
} else {
console.log("Error");
console.log(result.data);
}
});
}
function getlmsrecord(callback) {
var info = {
action: "getlmsrecord",
cid: courseId,
rid: lmsId,
mvId: mvId,
}
mvApi(info, callback)
}
// get/set non-default data in lms
var timer;
function updatelms() {
timer = null;
var updateFlag;
var info = {
action: "updatelms",
cid: courseId,
rid: lmsId,
mvId: mvId,
object: data,
}
console.log(info);
if(data['cmi.core.lesson_status'] == 'passed'||data['cmi.core.lesson_status'] == 'completed'){
if(!passed){
console.log('force to send completed!');
passed=true;
updateFlag=true;
}
}
mvApi(info,function(result){
if(updateFlag){
console.log('successCallBack');
successCallBack(true);
}else if(passed){
console.log('already completed out!');
}
})
}
function store() {
data._expireTime = (function() {
switch (true) {
case (opts.persistFor > 0):
return time(opts.persistFor) + 1;
case (opts.persistFor < 0):
return -1;
default:
return 0;
}
})();
if (timer) {
$timeout.cancel(timer);
}
timer = $timeout(updatelms, 2000);
}
var opts;
var initialized, lastError, model, data;
var mvId;
var passed;
var successCallBack;
// returns function to create API instances, for different SCOs
this.scormLocal = function(cid, mvid, rid, _opts,_successCallBack, callback) {
// console.log(user);
courseId = cid;
lmsId = rid;
mvId = mvid;
opts = _opts;
successCallBack=_successCallBack;
// defaultModel['cmi.core.student_id']=user._id;
// defaultModel['cmi.core.student_name']=user.fullname;
// console.log(defaultModel);
opts = incl(opts, defaultOpts);
initialized = false;
lastError = 0;
model = incl(opts.model, defaultModel);
getlmsrecord(function(r) {
console.log('load server',r);
if (!r) {
r = {};
}
if (!r || {}.toString.call(r) != '[object Object]' || r._flush || r._expireTime === 0) {
data = {}
} else if (r['cmi.core.lesson_status'] == 'passed'||r['cmi.core.lesson_status'] == 'completed') { // never expire for passed records
console.log('already completed in!');
passed=true;
data = r;
} else if (r._expireTime !== -1 && time() >= (r._expireTime || Number.POSITIVE_INIFINITY)) {
data = {};
} else {
data = r;
}
if (callback) {
callback();
}
})
// data = store();
}
this.API = {
// imports the API methods to another object
importTo: function(obj) {
for (var k in this)
if (/^LMS/.test(k)) obj[k] = this[k].bind(this);
},
// removes data from lms and mark it as flushed
// in case it's commited after flushing
flush: function() {
data = {
_flush: true
};
store();
},
// TODO: check if return booleans should be strings
LMSInitialize: function() {
if (initialized) return lastError = 101, false;
lastError = 0;
initialized = true;
return true;
},
// TODO: check if return booleans should be strings
LMSFinish: function() {
if (!initialized) return lastError = 301, false;
lastError = 0;
initialized = false;
return true;
},
// TODO: check if return booleans should be strings
// TODO: validate arguments
LMSGetValue: function(key) {
console.log("get value :" + key);
if (!initialized) return lastError = 301, false;
var v = data[key] || model[key];
if (typeof v == 'undefined') return lastError = 201, null;
lastError = 0;
return v;
},
// TODO: check if return booleans should be strings
// TODO: validate arguments
LMSSetValue: function(key, value) {
// console.log("set value :" +key+" : " +value);
if (!initialized) return lastError = 301, false;
// if (typeof model[key] == 'undefined') return lastError = 201, false;
data[key] = value;
lastError = 0;
store(data);
return 'true';
},
// TODO: check if return booleans should be strings
LMSCommit: function() {
console.log("commit :");
if (!initialized) return lastError = 301, false;
console.log(data);
store(data);
lastError = 0;
return true;
},
LMSGetLastError: function() {
return lastError;
},
LMSGetErrorString: function(code) {
return errorCodes[code];
},
// TODO: improve diagnostics
LMSGetDiagnostic: function(code) {
return errorCodes[code];
}
};
});