-
Notifications
You must be signed in to change notification settings - Fork 34
The Anti Library
I often get asked...
Do I really need a library or framework for my content to communicate with SCORM?
This really depends on what your are doing. I often poke fun at 'powerpoint' SCO's that simply act as presentations, but normally go the extra extent of giving the student credit for visiting it. And you're right, there isn't much reason to lug around 30+ Kilobytes of infrastructure if you're only using <1% of it. That said, these type of presentations do have a valid place in learning. Also, there is a bit of history here. SCORM 1.2's Specification is almost completely optional. So really, the only base support you could rely on from 2001-2004 was a score, lesson status and a time value. This seemed to have a ripple effect on the remaining years up to today, due to the fact not every LMS adopted SCORM 2004, or adopted a partially implemented version of it.
- You may already have a proven/tested system of executing content. Re-writing something custom could open yourself up to issues, but you can duplicate that consistency.
- You may be benefiting from a up or down conversion from SCORM 1.2 to 2004 or vice versa. You may even have a library that also support AICC. All these swiss army knife type approaches are hitting your bandwidth though.
- Looking for richer feedback, logging and get/set management all rolled up is handy. As you'll see below there is next to no error control vs. what you'd get with a library.
- For the features you are using, you don't want SCORM logic buried all over your content objects. Meaning, you have nested SCORM features/calls in certain parts of your content. Dev's may end up re-writing this logic if they didn't know it was already present.
- Leverage years of building content, on mixed platforms.
If bandwidth is a concern for you, and all you are doing is loading, initializing, setting a score, completion, and success then committing and terminating, then no hauling around everything is a bit extreme. Specially with mobile devices and low bandwidth situations you'd want to maximize your approach. Now you may not of even known what the bare minimum even was, and actually its mostly just Initialize and Terminate. However, if you want to see a score, success and completion listed as well as possibly session time, you'll have to do just a little more work.
Couple solutions to lighten the load and take advantage of the users caching ability would be to point all your content to CDN's or a central media server. You gain a dependency on that domain/server being available, but it beats pushing the same file into all your SCO's. Sites like Cloudflare.com are great examples of free ways to host or work with already hosted scripts like http://cdnjs.com.
Please note the following example is void of error management, and mostly assumes a connection. That could be tacked on, but for now its going to try to connect to the API_1484_11 (LMS) and then initialize. On exit, it will set the exit type, completion, success, score scaled, commit and terminate.
var API {
connection: false,
path: false,
startTime: {}
};
// FindAPI Method
function findAPI(win) {
var attempts = 0, limit = 500;
while ((!win.API && !win.API_1484_11) && (win.parent) && (win.parent !== win) && (attempts <= limit)) {
attempts += 1;
win = win.parent;
}
if (win.API_1484_11) {//SCORM 2004-specific API.
API.version = "2004";
//Set version
API.path = win.API_1484_11;
/*} else if (win.API) {//SCORM 1.2-specific API
API.version = "1.2";
//Set version
API.path = win.API;*/
} else {
return false;
}
return true;
}
// LMS Connected Method
function lmsConnected() {
API.path.Initialize("");
API.startTime = new Date().getTime();
}
// centisecsToISODuration for SCORM 2004 (Needed for Session Time)
function centisecsToISODuration(n, bPrecise) {
var str = "P",
nCs = Math.max(n, 0),
nY = 0,
nM = 0,
nD = 0,
nH,
nMin;
// Next set of operations uses whole seconds
//with (Math) { //argumentatively considered harmful
nCs = Math.round(nCs);
if (bPrecise === true) {
nD = Math.floor(nCs / 8640000);
} else {
nY = Math.floor(nCs / 3155760000);
nCs -= nY * 3155760000;
nM = Math.floor(nCs / 262980000);
nCs -= nM * 262980000;
nD = Math.floor(nCs / 8640000);
}
nCs -= nD * 8640000;
nH = Math.floor(nCs / 360000);
nCs -= nH * 360000;
nMin = Math.floor(nCs / 6000);
nCs -= nMin * 6000;
//}
// Now we can construct string
if (nY > 0) {
str += nY + "Y";
}
if (nM > 0) {
str += nM + "M";
}
if (nD > 0) {
str += nD + "D";
}
if ((nH > 0) || (nMin > 0) || (nCs > 0)) {
str += "T";
if (nH > 0) {
str += nH + "H";
}
if (nMin > 0) {
str += nMin + "M";
}
if (nCs > 0) {
str += (nCs / 100) + "S";
}
}
if (str === "P") {
str = "PT0H0M0S";
}
// technically PT0S should do but SCORM test suite assumes longer form.
return str;
}
// initSCO Method
function initSCO() {
// Search for LMS API
var win;
try {
win = window.parent;
if (win && win !== window) {
findAPI(window.parent);
}
} catch (e) {/* Cross Domain issue */
}
if (!API.path) {
try {
win = window.top.opener;
findAPI(win);
} catch (ee) {/* Cross domain issue */}
}
if (API.path) {
API.connection = true;
lmsConnected();
return true;
}
// I was unable to locate an API for communication;
}
// Exit SCO Method
function exitSCO() {
var session_secs;
API.endTime = new Date().getTime();
API.path.SetValue('cmi.exit', 'normal');
API.path.SetValue('cmi.completion_status', "completed");
API.path.SetValue('cmi.success_status', 'passed');
API.path.SetValue('cmi.score.scaled', '1');
// Could save session time, but it would take a little more code.
session_secs = (API.endTime - API.startTime) / 1000;
API.path.SetValue('cmi.session_time', centisecsToISODuration(session_secs * 100, true));
API.path.Commit("");
API.path.Terminate("");
}
// Events for Load/Unload
$(window).bind('load', initSCO);
$(window).bind('unload', exitSCO);
Any issues, concerns or feedback - make contact