/** * eLms Objeto para control e interacción del contenido con el usuario * albergando informacion a la plataforma LMS bajo el estandar scorm 1.2 * Requiere el framework jquery * * @author Martin R. Mondragon - martin@aquainteractive.com * @version 1.0 * +Marzo 05/2013 implementacion de diplomas+ +Agosto 19/08/2013 * Evitar el bloquedo en modo estricto al iniciar +Febrero 11/02/2014 * Permitir evaluaciones parciales y finales en modo estricto */ var eLms = { /** * Se activa cuando cuando incuentra el api de la plataforma */ isApi : false, /** * Objeto que hace referencia al api sco de la plataforma */ api : null, /** * Objeto dom que hace referencia al area de contenido */ eBody : null, /** * Identificador unico */ serial : "", /** * Contador de intentos de busqueda de API sco de la plataforma */ tries : 0, /** * Codido de error bajo el estandar scorm 1.2 */ errCode : "0", /** * Cadena de error bajo el standar scorm 1.2 */ errString : "", /** * Registra el tiempo al inicializar la comunicacion */ initTime : 0, /** * Registra el tiempo al finalizar la comunicacion */ endTime : 0, /** * Ruta relativa o url de la carpeta contenedora del curso */ course : "", /** * Bandera que se activa cuando termina la comunicacion */ isClose : false, /***************************************************************************** * Tipo de retroalimentacion de evaluacion */ exTy : 0, /** * Identificador de la evaluacion final */ idExFinal : 0, /** * Identificador unico de la evaluacion actual */ idEva : 0, /** * Bandera que indica que por lo menos existe una evaluacion */ isExam : false, /** * Arreglo que contiene las preguntas de las evaluaciones */ exArr : new Array(), /** * Preguntas aleatorias de la evaluacion actual */ exApp : new Array(), /** * Opciones de respuestas aleatorias de las preguntas */ exApr : new Array(), /** * Opciones de respuestas del usuario */ exArs : new Array(), /** * Opciones de respuestas del usuario */ exArr : new Array(), /** * Modo estricto de aplicacion de evaluación, no permite avanzar si no se * acredita */ exMode : 0, /** * Numero de intentos permitidos para aplicar una misma evaluacion */ scoreTries : 0, /** * Arreglo de cadenas con las evaluaciones */ exData : new Array(), /** * Tiempo como plazo para realizacion de la evalucion */ exTime : 0, /** * Numero de preguntas por evaluacion */ exPreg : 10, /** * Calificacion de la evaluacion actual */ scoreRaw : 0, /** * Calificacion minima aprobatoria para la evaluacion */ scoreMin : 70, /** * Calificacion maxima para una evaluacion */ scoreMax : 100, /** * Modo para el despliege de lecciones, 1=Modo estricto */ lessonMode : 0, /** * Arreglo con todas las lecciones */ lessons : new Array(), /** * Arreglo con el tiempo en que un usuario le dedica a cada leccion */ lessonsTimes : new Array(), /** * Tiempo requerido para leccion actual */ lessonTime : 0, /** * Formato del contenido actual */ lessonFormat : 0, /** * Estatus actual de leccion actual */ lessonSatus : "", /** * Tipo de contenido de la leccion actual: C = Contenido, A = Autoevaluación P = * Evaluación parcial E = Evaluación final * Se registra Raw O = Objetos de * contenido */ lessonType : '', /** * Indice de la leccion actual */ idLesson : 0, /** * Inidice de la leccion anterior */ idLessonAnt : 0, /** * Cadena de identificacion para la leccion actual (lesson_location) */ lesson : '', /** * Visualiza/Oculta boton de inicio */ btInicio : 0, /** * Visualiza/Oculta menu principal de lecciones */ btMenu : 0, /** * Visualiza/Oculta boton de salir */ btExit : 0, /** * Visualiza/Oculta boton de actualizar contenido */ btRefresh : 0, /** * Visualiza/Oculta boton de ayuda */ btHelp : '', /** * Visualiza/Oculta boton de Glosario + ruta del recurso */ btGlossary : '', /** * Visualiza/Oculta boton de Biografia + ruta del recurso */ btBiography : '', /** * Visualiza/Oculta boton de Material + ruta del recurso */ btMaterial : '', /** * Visualiza/Oculta controles de medicio del tiempo por leccion */ ctTime : 0, /** * Visualiza/Oculta controles para contenido SWF */ ctSwf : 0, /** * Tipografia pordefault */ ctFont : 'Verdana,Geneva,sans-serif', /** * Mostrar el nombre del curso como titulo */ isTitle : false, /** * Elementos del suspend_data */ /** * Arreglo que contiene parametros de configuracion y resultados */ cfgData : new Array(), /** * Arreglo con los estatus de todas las lecciones */ arrData : new Array(), /** * Arreglo con los tiempos acumulados de todas las lecciones */ tmData : new Array(), /** * Arreglo con los resultados de las evaluaciones */ evData : new Array(), /** * Bandera que se activa cuando se carga el suspend_data */ isData : false, /** * Constantes */ isStyleDiploma : true, STATUS_INCOMPLETE : 1, STATUS_COMPLETED : 2, FORMAT_CONTENT : 0, FORMAT_OBJECT : 1, FORMAT_EVAL : 2, FORMAT_DIPLOMA : 3, /** * Devuelve el error bajo el estandar scorm 1.2 * * @param String name Nombre del parametro scorm 1.2 * @param String ee Tipo de tratamiento de error */ errHandle : function(name, ee) { ee = (ee == null || typeof (ee) == 'undefined') ? "fun" : ee; if (eLms.api == null) { desc = ""; switch (ee) { case "err": desc = "No se encuentra implementado esta accion/Not this action is implemented\n" + name; break; case "get": desc = "LMSGetValue(" + name + ") failed. \n" + eLms.api.LMSGetErrorString(eLms.errCode); break; default: eLms.errCode = api.LMSGetLastError().toString(); if (eLms.errCode != "0") { desc = eLms.api.LMSGetErrorString(eLms.errCode); } } if (desc != "") { eLms.errString = desc; alert(eLms.jsText(eLms.errString)); } return ""; } alert(eLms .jsText("Posiblemente existe un error de pérdida de session/There is probably a mistake session loss.")); return; }, /** * Busqueda del objeto API de la plataforma servidor LMS * * @param Object win Objeto window actual * @return Object API Objeto api */ findApi : function(win) { while ((win.API == null) && (win.parent != null) && (win.parent != win)) { if (eLms.tries++ > 7) { return null; } win = win.parent; } return win.API; }, /** * Devuel el objeto API, si no existe inicializa la busqueda para obtenerlo * * @return Object API */ getApi : function() { if (eLms.api == null) { eLms.api = eLms.findApi(window); if ((eLms.api == null) && (window.opener != null) && (typeof (window.opener) != 'undefined')) { eLms.api = eLms.findApi(window.opener); } if (eLms.api == null) { alert(eLms .jsText("No se encontro el adaptador API/Unable to find an API adapter")); } } return eLms.api; }, /** * Inicializa la comunicacion con el servidor LMS, devuelve una cadena "true" * si se tiene exito, de lo contrario "false" * * @return String Valor que representa un boolean */ initialize : function() { if (!eLms.isApi) return "true"; if (eLms.getApi()) { var result = eLms.api.LMSInitialize(""); if (result.toString() != "true") { eLms.errHandle(); } return result.toString(); } eLms.errHandle("LMSInitialize", "err"); return "false"; }, /** * Finaliza la comunicacion con el servidor LMS, devuelve una cadena "true" si * se tiene exito, de lo contrario "false" * * @return String Valor que representa un boolean */ finish : function() { if (!eLms.isApi) return "true"; if (eLms.getApi()) { var result = eLms.api.LMSFinish(""); if (result.toString() != "true") { eLms.errHandle(); } return result.toString(); } eLms.errHandle("LMSFinish", "err"); return "false"; }, /** * Devuelve el valor del elemento scorm solicitado al servidor LMS, Si no * tiene exito devuelve un "false" * * @param String Elemento scorm * @return String Valor del elemento solicitado */ getValue : function(name) { if (!eLms.isApi) return ""; if (eLms.getApi()) { var value = eLms.api.LMSGetValue(name); eLms.errCode = eLms.api.LMSGetLastError().toString(); if (eLms.errCode != "0") { return eLms.errHandle(name, "get"); } return value.toString(); } return eLms.errHandle("LMSGetValue", "err"); }, /** * Asigna el valor del elemento Scorm solicitado al servidor LMS, Si no tiene * exito devuelve un "false" * * @param String Elemento scorm * @return String Valor del elemento solicitado */ setValue : function(name, value) { if (!eLms.isApi) return "true"; if (eLms.getApi()) { var result = eLms.api.LMSSetValue(name, value); if (result.toString() != "true") { eLms.errHandle(); } return result.toString(); } return eLms.errHandle("LMSSetValue", "err"); }, /** * Envia una interaccion al servidor LMS */ setInteraction : function(ee, id, vl) { eLms.setValue('cmi.interactions.' + id + '.' + ee, vl); }, /** * Devuelve la cadena de error registrado * * @return String Cadena del error */ getErrorString : function() { if (!eLms.isApi) return ""; if (eLms.getApi()) { return eLms.api.doLMSGetErrorString().toString(); } return eLms.errHandle("LMSGetErrorString", "err"); }, /** * Devuelve una cadena del ultimo error registrado * * @return String Cadena del ultimo error */ getLastError : function() { if (!eLms.isApi) return ""; if (eLms.getApi()) { return eLms.api.LMSGetLastError().toString(); } return eLms.errHandle("LMSGetLastError", "err"); }, /** * Clase para Decodificacion de una cadena de UTF8 */ utf8 : { decode : function(utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } }, /** * Decodificacion de unaca cadena en base 64 */ bs64 : { _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", decode : function(input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = eLms.bs64._keyStr.indexOf(input.charAt(i++)); enc2 = eLms.bs64._keyStr.indexOf(input.charAt(i++)); enc3 = eLms.bs64._keyStr.indexOf(input.charAt(i++)); enc4 = eLms.bs64._keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) output = output + String.fromCharCode(chr2); if (enc4 != 64) output = output + String.fromCharCode(chr3); } return eLms.utf8.decode(output); } }, /** * Limpia la cadena de caracteres especiales y reemplaza los espacios por * guiones bajos, los caracteres ãàáäâèéëêìíïîòóöôùúüûÑñÇç se convierten a el * carecter ASCII equivalente * * @param String str Cadena de texto * @return String Cadena de texto limpia */ cvText : function(str) { var text = str.toLowerCase(); text = text.replace(/[ãàáäâ]/g, 'a'); text = text.replace(/[èéëê]/g, 'e'); text = text.replace(/[ìíïî]/g, 'i'); text = text.replace(/[òóöô]/g, 'o'); text = text.replace(/[ùúüû]/g, 'u'); text = text.replace(/[ýÿ]/g, 'y'); text = text.replace(/[ñ]/g, 'n'); text = text.replace(/[ç]/g, 'c'); text = text.replace(/['"]/g, ''); text = text.replace(/\s+/g, '_'); text = text.replace(/[^a-zA-Z0-9_]/g, ''); text = text.replace(/(_)$/g, ''); text = text.replace(/^(_)/g, ''); return text; }, jsText : function(str) { return str; text = str.replace('Á', '\u00C1'); text = text.replace('á', '\u00E1'); text = text.replace('É', '\u00C9'); text = text.replace('é', '\u00E9'); text = text.replace('Í', '\u00CD'); text = text.replace('í', '\u00ED'); text = text.replace('Ó', '\u00D3'); text = text.replace('ó', '\u00F3'); text = text.replace('Ú', '\u00DA'); text = text.replace('ú', '\u00FA'); text = text.replace('Ü', '\u00DC'); text = text.replace('ü', '\u00FC'); text = text.replace('Ñ', '\u00D1'); text = text.replace('ñ', '\u00F1'); return text; }, /** * Revuelve de forma aleatoria el contenido de un arreglo * * @param Array arr Arreglo de elementos * @return Arreglo con los elementos revueltos */ shuffle : function(arr) { for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(i * Math.random()); var tmp = arr[j]; arr[j] = arr[i]; arr[i] = tmp; } return arr; }, /** * Agregar ceros a la izquierdaa un numero * * @param Integer p1 Numero * @param Integer p2 Numero de ceros a colocar a la Izq. Max:4 * @return String Cadena con el numero + ceros a la izquierda */ addZeros : function(p1, p2) { return p2 == 4 ? p1 < 10 ? '000' + p1 : p1 < 100 ? '00' + p1 : p1 < 1000 ? '0' + p1 : p1 : p1 < 10 ? '0' + p1 : p1; }, /** * Formato en minutos y segundos 00:00 * * @param Integer time Numero de segundos * @return String Formato en minutos y segundo 00:00 */ formatTimeMin : function(time) { var min = Math.floor(time / 60); var seg = Math.floor(time - (min * 60)); return (eLms.addZeros(min, 2) + ":" + eLms.addZeros(seg, 2)); }, /** * Formato en Horas, minutos y segundos 00:00:00 * * @param Integer t1 Micro segundos * @param Integer t2 Micro segundos * @return String Cadena en formato 00:00:00 */ formatTime : function(t1, t2) { var time = Math.floor((t2 - t1) / 1000); var hrs = Math.floor(time / 3600); var min = Math.floor((time - (hrs * 3600)) / 60); var seg = Math.floor(time - (hrs * 3600) - (min * 60)); return (eLms.addZeros(hrs, 4) + ":" + eLms.addZeros(min, 2) + ":" + eLms .addZeros(seg, 2)); }, /** * Actualiza el tiempo al servidor LMS */ updTime : function() { var d = new Date(); eLms.setValue("cmi.core.session_time", eLms.formatTime(eLms.initTime, d .getTime())); eLms.initTime = d.getTime(); }, /** * Inicializa el proceso para establecer comunicacion con el servidor LMS * * @return String Valor Boolean "true" si se tiene exito */ init : function() { var d = new Date(); eLms.initTime = d.getTime(); if (eLms.initialize() == "true") { eLms.lessonSatus = eLms.getValue("cmi.core.lesson_status"); switch (eLms.lessonSatus) { case "passed": case "completed": case "failed": case "incomplete": case "browsed": /* se queda el mismo estatus */ break; case "not attempted": default: eLms.setValue("cmi.core.lesson_status", "incomplete"); } return "true"; } return "false"; }, /** * Obtiene la cadena del suspend_data * * @param String Cadena con el valor completo del suspend_data */ getSuspendData : function() { return $.trim(eLms.getValue("cmi.suspend_data")); }, /** * Envia la cadena suspend_data con los elementos que la integran */ setSuspendData : function() { // alert(eLms.tmData.toString()); eLms.setValue("cmi.suspend_data", eLms.arrData.join('-') + '|' + eLms.cfgData.join('-') + '|' + eLms.tmData.join('-') + '|' + eLms.evData.join('-')); }, /** * Asigna valores e Inicializa la cadena suspend_data * * @param Integer id Identificador de la leccion * @param Integer st Estatus de la lecicion */ suspendData : function(id, st) { if (!eLms.arrData.length) { str = eLms.getSuspendData(); if (eLms.getLastError() == "0") { eLms.isData = true; if (str != '') { ar1 = str.split('|'); if (typeof (ar1[2]) == undefined) { for (var i = 0; i < eLms.lessons.length; i++) { eLms.tmData[i] = 0; } ar1[2] = eLms.tmData.join('-'); } if (typeof (ar1[3]) == undefined) { ar1[3] = ''; } eLms.arrData = ar1[0].split('-'); eLms.cfgData = ar1[1].split('-'); eLms.tmData = ar1[2].split('-'); eLms.evData = ar1[3].split('-'); } else { for (var i = 0; i < eLms.lessons.length; i++) { eLms.arrData[i] = 0; eLms.tmData[i] = 0; } eLms.cfgData = new Array(0, 0, 0, 0, 0, 0); eLms.setSuspendData(); } } } }, /** * Obtiene la leccion desde el servidor LMS (lesson_location) * * @return String Cadena de la leccion */ getLesson : function() { return eLms.getValue("cmi.core.lesson_location"); }, /** * Asigna la leccion al servidor LMS (lesson_location) * * @param String pp Cadena de la leccion actual * @return String Valor boolean d ela operacion */ setLesson : function(pp) { return eLms.setValue("cmi.core.lesson_location", pp); }, /** * Identificador de la pregunta actual */ idPreg : 0, /** * Total de opciones de respuesta de la pregunta actual */ exTotRs : 0, /** * Registra la opcion de respuesta seleccionada por le usuario */ onResp : function(op) { if (typeof (eLms.exArs[eLms.idPreg]) == 'undefined') { eLms.exTotRs++; } var d = new Date(); eLms.exRspTime[eLms.idPreg][1] = d.getTime(); eLms.exArs[eLms.idPreg] = eLms.exApr[eLms.idPreg][op.value]; eLms.exActiveFin(); eLms.exRetro(op.value); }, exActiveFin : function() { if (eLms.exTotRs >= eLms.exApp.length) $('#idExBtFin', eLms.ifm).show(); else $('#idExBtFin', eLms.ifm).hide(); }, idPregAnt : 0, exToolBar : function() { if (eLms.idPreg < 1) $('#idExBtPrv', eLms.ifm).hide(); else $('#idExBtPrv', eLms.ifm).show(); if (eLms.idPreg >= (eLms.exApp.length - 1)) $('#idExBtNxt', eLms.ifm).hide(); else $('#idExBtNxt', eLms.ifm).show(); $('#idPr' + eLms.idPregAnt).removeClass(); for (var i = 0; i < eLms.exArs.length; i++) { if (typeof (eLms.exArs[i]) != 'undefined') { $('#idPr' + i).removeClass().addClass("ok"); } else { $('#idPr' + i).removeClass(); } } $('#idPr' + eLms.idPreg).removeClass().addClass("on"); eLms.exActiveFin(); }, exPrevious : function() { if (eLms.idPreg > 0) { eLms.loadPreg(eLms.idPreg - 1); } }, exNext : function() { if (eLms.idPreg < (eLms.exApp.length - 1)) { eLms.loadPreg(eLms.idPreg + 1); } }, exReset : function() { eLms.exApp = new Array(); eLms.exApr = new Array(); eLms.exArs = new Array(); eLms.exArr = new Array(); eLms.exRspTime = new Array(); eLms.isOkExam = false; eLms.exTotRs = 0; eLms.idPreg = 0; if (eLms.idEva == eLms.idExFinal) { eLms.scoreRaw = 0; } // eLms.cfgData[1]++; /* Numero de intentos */ // eLms.setSuspendData(); eLms.showControl(false); eLms.runExam(); }, /** * */ lessonTitle : '', exResult : function() { var retro = ''; acc = eLms.ex.aciert; var isDiploma = false; var txtDiloma = ''; if (eLms.ex.calif >= eLms.scoreMin) { if (typeof (eLms.lessons[eLms.idLesson + 1]) != undefined) { ar = eLms.lessons[eLms.idLesson + 1].split('~'); if (ar[0] == 'D') { txtDiloma = ar[3]; isDiploma = true; } } } eLms.eBody .html('
¿Estás seguro de iniciar en este momento?
' + '