// TODO: Global config? // Use relative URL to avoid cross-domain issues: var GP_AICC_URL = "/relaystatus.pl"; function standardExit() { if ( top.exit ) { top.exit(false); } else { self.location.replace("/c/goodbye.html"); } } var courseData = { "cmi.suspend_data": "", "cmi.core.score.min": "0", "cmi.core.score.max": "0", "cmi.core.score.raw": "", "cmi.core.session_time": "", "cmi.core.lesson_status": "incomplete", "cmi.core.lesson_location": "" } var ADAPTER = function() { var aiccForm; var AICC_URL = ""; var AICC_SID = ""; var startTime = new Date(); // TODO: consider a combined and/or two-way hash var SCORM_TO_AICC = { "not started": "N", "incomplete": "I", "completed": "P", "passed": "P", "failed": "F" }; var AICC_TO_SCORM = { "N": "not started", "I": "incomplete", "C": "passed", "P": "passed", "F": "failed" }; function startClock() { startTime = new Date(); } function elapsedTime() { var endTime = new Date(); var ms = endTime - startTime; // ms return ms; } function msToAiccTime(ms) { var x = ms / 1000; seconds = x % 60; x /= 60; minutes = x % 60; x /= 60; hours = x % 24; x /= 24; days = x; return sprintf("%04d:%02d:%02d", hours, minutes, seconds); } function stopClock() { courseData["cmi.core.session_time"] = msToAiccTime(elapsedTime()); } function isDataSection(secName) { return jQuery.inArray(secName, [ "core", "student_preferences", "result" ]) > -1; } function getParam() { $.ajax( { type: 'POST', url: GP_AICC_URL, data: { relayto: AICC_URL, session_id: AICC_SID, version: "3.5", command: "GETPARAM", aicc_data: "" }, success: loadData, error: onFail }); } function test() { alert('test'); } function ajaxPutParam(fnWhenFinished) { $.ajax( { type: 'POST', url: GP_AICC_URL, data: { relayto: AICC_URL, session_id: AICC_SID, version: "3.5", command: "PUTPARAM", aicc_data: makePutParamData() }, success: fnWhenFinished, error: onFail }); } function onFail(XMLHttpRequest, textStatus, errorThrown) { alert("Failure: " + textStatus + ":" + errorThrown); } function getAiccStatus(scormStatus) { if ( SCORM_TO_AICC[scormStatus] ) { return (SCORM_TO_AICC[scormStatus]); } else { return "I"; } } function getScormStatus(aiccStatus) { if ( AICC_TO_SCORM[aiccStatus] ) { return AICC_TO_SCORM[aiccStatus]; } else { return "incomplete"; } } function isDataSection(secName) { return jQuery.inArray(secName, [ "core", "student_preferences", "result" ]) > -1; } function loadData(data) { var res = hacpToJSON(data); var myStatus = AICC_TO_SCORM[res.core.lesson_status]; // HACK: Refactor into function: if ( myStatus == "not started" ) { myStatus = "incomplete"; } courseData = { "cmi.suspend_data": res.coreLesson, "cmi.core.score.min": "0", "cmi.core.score.max": "100", "cmi.core.score.raw": res.core.score, "cmi.core.session_time": "0000:00:00.00", "cmi.core.lesson_status": myStatus, "cmi.core.lesson_location": res.core.lesson_location } startClock(); $("#main").attr("src", LAUNCH_URL); } function setupAiccForm() { aiccForm = $("#hacp").contents().find("form"); // GP relay: $("#aiccMessage").attr("action", GP_AICC_URL); } function hacpToJSON(hacpMessage) { var aiccJson = { coreLesson: "" // always included, may be empty }; var aiccLines = hacpMessage.replace(/AICC_DATA=/gi, "AICC_DATA=\r\n").split(/\n|\r\n|\n/); var bCoreLesson = false; var currSection = "result"; // TODO: refactor jQuery.each(aiccLines, function(key, val) { var lin = jQuery.trim(val); var keyval = val.split("="); if ( lin.match(/^\[.*\]$/i) ) { var findSecName = /^\[(.*)\]$/i; currSection = findSecName.exec(lin)[1].toLowerCase(); } else if ( isDataSection(currSection) ) { if ( lin ) { var fields = lin.split("="); if ( !aiccJson[currSection] ) { aiccJson[currSection] = { }; } var sec = aiccJson[currSection]; sec[fields[0].toLowerCase()] = fields[1]; } } else if ( currSection == "core_lesson" ) { aiccJson.coreLesson += lin; } }); // Could be comma-delimited - status,launchmode aiccJson.core.lesson_status = aiccJson.core.lesson_status.split(",")[0]; // If we're here, it's incomplete: if ( aiccJson.core.lesson_status == "N" ) { aiccJson.core.lesson_status == "I" } return aiccJson; } function makePutParamData() { var result = "[Core]\r\n" + "Lesson_Location=" + courseData["cmi.core.lesson_location"] + "\r\n" + "Lesson_Status=" + getAiccStatus(courseData["cmi.core.lesson_status"]) + "\r\n" + "Score=" + courseData["cmi.core.score.raw"] + "\r\n" + "Time=" + courseData["cmi.core.session_time"] + "\r\n" + "[Comments]\r\n" + "\r\n" + "[Objectives_Status]\r\n" + "\r\n" + "[Student_Preferences]\r\n" + "Audio=0\r\n" + "Language=\r\n" + "Speed=100\r\n" + "Text=0\r\n" + "[Core_Lesson]\r\n" + courseData["cmi.suspend_data"] + "\r\n"; return result; } var my = {}; my.init = function() { AICC_URL = window.location.querystring["aicc_url"]; AICC_SID = window.location.querystring["aicc_sid"]; if ( AICC_URL && AICC_SID ) { AICC_URL = decodeURIComponent(AICC_URL); AICC_SID = decodeURIComponent(AICC_SID); STARTS_WITH_PROTOCOL = /^http/; if ( !AICC_URL.match(STARTS_WITH_PROTOCOL) ) { // Prepend protocol if none provided: AICC_URL = "http://" + AICC_URL; } // cleanup for internally-hosted courses: if ( AICC_URL.match(/^http:\/\/staging\.nalcoedu\.com.*$/)) { AICC_URL = AICC_URL.replace(/staging\.nalcoedu\.com/gi, "172.27.8.20"); } if ( AICC_URL.match(/^http:\/\/plateau\.gpehosting\.com.*$/)) { AICC_URL = AICC_URL.replace(/plateau\.gpehosting\.com/gi, "172.27.48.36"); } if ( AICC_URL.match(/^http:\/\/saba\.gpehosting\.com.*$/)) { AICC_URL = AICC_URL.replace(/saba\.gpehosting\.com/gi, "172.27.48.41"); } if ( AICC_URL.match(/^http:\/\/fplpreprod\.learnflex\.net.*$/)) { AICC_URL = AICC_URL.replace(/fplpreprod\.learnflex\.net:/gi, "64.201.33.200"); } getParam(); } else { // TODO: Localize, more advanced handling e.g. email: alert("AICC parameters missing. Please contact your administrator."); } } my.doPutParam = function() { stopClock(); ajaxPutParam($.noop); } my.doExitAu = function() { $.ajax( { type: 'POST', url: GP_AICC_URL, data: { relayto: AICC_URL, session_id: AICC_SID, version: "3.5", command: "EXITAU" }, success: $.noop, error: onFail }); } return my; }(); var API = function() { var hasFinished = false; return { LMSInitialize: function(estring) { if ( window.console && console.log ) { console.log("LMSInitialize", estring) } return "true"; }, LMSFinish: function(estring) { if ( !hasFinished ) { if ( window.console && console.log ) { console.log("LMSFinish", estring) } hasFinished = true; standardExit(); return "true"; } }, LMSGetValue: function(key) { if ( window.console && console.log ) { console.log("LMSGetValue", key) } if ( courseData[key] ) { return courseData[key]; } else { return ""; } }, LMSSetValue: function(key, val) { if ( window.console && console.log ) { console.log("LMSSetValue", key, val) } courseData[key] = val; return "true"; }, LMSCommit: function(estring) { if ( window.console && console.log ) { console.log("LMSCommit", estring) } // NOOP return "true"; }, LMSGetLastError: function() { // TODO: Translate AICC errors? // For legacy SCORM wrapper purposes, content can't do anything useful with errors anyway. if ( window.console && console.log ) { console.log("LMSGetLastError") } return 0; }, LMSGetErrorString: function(errorCode) { if ( window.console && console.log ) { console.log("LMSGetErrorString", errorCode) } return ""; }, LMSGetDiagnostic: function(errorCode) { if ( window.console && console.log ) { console.log("LMSGetDiagnostic", errorCode) } return ""; } }; }(); $(document).ready(function() { ADAPTER.init(); }); window.onbeforeunload = function() { $("body").html("
Saving your progress..."); $.ajaxSetup( { async: false }); ADAPTER.doPutParam(); ADAPTER.doExitAu(); }