Paquetes incrustados y simulaciones

Visión general

El bloque Código incrustado puede mostrar una carpeta que subes: un index.html con sus recursos, ejecutándose dentro de la lección. Por defecto ese paquete es una caja cerrada. Si además lleva un pequeño manifiesto y habla con la lección mediante unos pocos mensajes, puede:

  • avisar a la lección de que el alumno ha terminado (para que la lección lo espere);
  • comunicar su alto, para que el marco se ajuste a su contenido;
  • entregar su progreso, para que el alumno lo encuentre donde lo dejó;
  • enviar una nota y cada decisión, lo que lo convierte en una simulación que cuenta en la calificación, en SCORM y en xAPI;
  • exponer variables que el contenido condicional puede leer.

Esta página es para quien construye el paquete. Se puede seguir sin leer ningún código de Creator. Para el lado del autor (el panel del bloque, la nota y los avisos) consulta Bloques de contenido.

Todo es opcional. Un paquete sin manifiesto funciona igualmente: se muestra, se da por completado al verse y nunca frena al alumno.

Cómo se ejecuta el paquete

El paquete corre en un marco aislado: puede ejecutar scripts, pero no tiene acceso a la aplicación (ni cookies, ni almacenamiento de la aplicación, ni origen compartido). La lección solo escucha los mensajes que vienen del marco de ese mismo bloque, así que nada más en la página puede hacerse pasar por tu paquete. Un mensaje que no es válido se ignora en silencio.

Un código incrustado que apunta a una URL externa en lugar de a una carpeta subida no tiene paquete: sus mensajes se ignoran siempre y nunca declara nada.

El manifiesto: slxd-embed.json

Pon un fichero llamado slxd-embed.json en la raíz de la carpeta, junto a index.html:

{
  "protocol": "slxd-embed",
  "version": 1,
  "capabilities": ["complete", "resize", "state", "result", "interaction"],
  "variables": { "trust": "number", "reused": "boolean" }
}
CampoSignificado
protocolSiempre "slxd-embed".
versionSiempre 1; es la única versión que se entiende.
capabilitiesLo que el paquete va a enviar. Cada comportamiento solo funciona si su capacidad está declarada. Lo desconocido se ignora. Hasta 32 entradas.
variablesOpcional. Las variables de state.vars que envía el paquete, con su tipo (number o boolean). Hasta 64. El contenido condicional solo puede nombrar estas.

Sin el fichero, con JSON inválido, con otra versión o con más de 16 KB, el paquete simplemente no declara nada.

El manifiesto se lee al subir la carpeta al bloque. Sube slxd-embed.json junto con el resto de la carpeta. Si cambias el manifiesto, vuelve a subir la carpeta: un paquete subido antes conserva lo que declaraba entonces.

CapacidadQué permite
completeEl paquete puede decir «he terminado». Hace falta para que el bloqueo del bloque espere al paquete.
resizeEl paquete puede fijar el alto de su marco.
stateLa lección guarda el progreso del paquete y se lo devuelve cuando el alumno vuelve.
resultEl paquete puede enviar una nota.
interactionEl paquete puede enviar cada decisión (interacciones SCORM y xAPI).

Nombres de variable reservados

grade, score, maxScore, ending y kind leen siempre el último result del paquete (ver abajo), así que no pueden ser además variables de tu estado. Un paquete cuyo variables declare alguno de ellos no se acepta al subirlo: el editor enseña el error y la carpeta no se usa hasta que cambies esos nombres. Dentro de state.vars, una clave con un nombre reservado simplemente se ignora.

Los nombres de variable empiezan por una letra o _, siguen con letras, cifras o _, y miden como máximo 64 caracteres. Una entrada con otro nombre o tipo se ignora sin rechazar el resto.

Mensajes

Cada mensaje es un objeto plano enviado con postMessage, con el mismo sobre:

const send = (type, data = {}) =>
  window.parent.postMessage({ channel: "slxd-embed", version: 1, type, ...data }, "*");

El origen de destino es "*" porque el marco no puede conocer el origen de la lección; en estos mensajes no viaja nada sensible.

Del paquete a la lección

typeCamposRequiereQué hace Creator
ready——Envíalo en cuanto el paquete haya cargado. Si el paquete declara state, la lección responde con restore.
complete—completeMarca el bloque como completado y libera su bloqueo. En una simulación además la cierra (ver abajo).
resizeheight: número ≥ 0, en píxeles CSSresizeEl marco toma ese alto, acotado entre 100 y 4000 px y redondeado hacia arriba. Hasta el primer mensaje rige el alto configurado en el bloque.
statestate: objeto JSON; done: booleanostateGuarda state tal cual. done: true indica que es un final: cierra la simulación y completa el bloque. Envíalo tras cada decisión y al llegar a un final.
resultscore, maxScore, grade, ending, kindresultSolo actualiza la nota. Puede llegar parcial y gana el último. No completa el bloque ni cierra la simulación.
interactionattempt, n, response, description, result, weightinginteractionRegistra una decisión. Envíalo tras cada decisión.

Un mensaje que no encaja en estas formas se ignora entero, sin ningún aviso. Revisa cada campo:

  • state.state: un objeto JSON plano (no una lista ni un valor suelto) de como máximo 1024 caracteres una vez serializado. state.done es obligatorio. Creator trata el estado como opaco, salvo un miembro opcional, vars: un objeto de números y valores verdadero/falso que lee el contenido condicional.
  • result.score: cualquier número finito (puede ser negativo). maxScore: número finito ≥ 0. grade: un número de 0 a 100 que calcula tu paquete; es lo único que cuenta en la calificación. ending: un identificador. kind: good, mixed o bad.
  • interaction.attempt y n (el número de la decisión dentro del intento): enteros de 1 a 9999. response: el identificador de la opción elegida. description: texto de hasta 250 caracteres, obligatorio (vale una cadena vacía). result: correct, incorrect o neutral. weighting: un número mayor que 0 (envía 1 si la decisión valía 0).
  • Identificadores (ending, response): minúsculas, cifras y _, empezando por una letra, hasta 64 caracteres.

De la lección al paquete

typeCamposCuándo
restorestate: el último estado guardado, o nullEn respuesta a ready, si el paquete declara state. Es null si no había nada guardado.
reset—Cuando el examen abre un intento nuevo. Empieza de cero: el estado y la nota guardados ya se han borrado.

Un paquete mínimo

const send = (type, data = {}) =>
  window.parent.postMessage({ channel: "slxd-embed", version: 1, type, ...data }, "*");

let scene = "s1";
let vars = { trust: 0, reused: false };

window.addEventListener("message", (event) => {
  const msg = event.data;
  if (!msg || msg.channel !== "slxd-embed") return;
  if (msg.type === "restore" && msg.state) {
    scene = msg.state.scene;
    vars = msg.state.vars;
    render();
  }
  if (msg.type === "reset") {
    scene = "s1";
    vars = { trust: 0, reused: false };
    render();
  }
});

function choose(option) {
  vars.trust += option.points;
  scene = option.next;
  send("interaction", {
    attempt: 1,
    n: 1,
    response: option.id,
    description: option.label,
    result: option.points > 0 ? "correct" : "incorrect",
    weighting: Math.abs(option.points) || 1,
  });
  send("state", { state: { scene, vars }, done: false });
  render();
}

send("ready");
send("resize", { height: document.body.scrollHeight });

Terminar: result antes de complete

Una simulación se cierra con complete o con un state con done: true. Un result nunca la cierra. La nota que cuenta es la del último result recibido antes del cierre; una vez cerrada, los resultados posteriores se ignoran. Si el paquete se cierra sin haber enviado ningún result, la simulación vale 0.

El orden correcto en un final es, por tanto:

send("result", { score: 8, maxScore: 10, grade: 80, ending: "agreement", kind: "good" });
send("state", { state: { scene: "end", vars }, done: true }); // o: send("complete")

Un state sin done después de un final es una partida nueva: la nota anterior se olvida.

Un paquete que se vaya a marcar como Evaluable debe declarar por tanto complete o state; si no, su nota no podría cerrarse nunca.

Qué hace Creator con cada cosa

Bloqueo del avance. El bloque de código incrustado no frena al alumno por defecto. Si el autor activa el bloqueo (en el bloque o en los ajustes del tema para el código incrustado), solo funciona con un paquete que declare complete: el alumno no puede continuar hasta que llegue el mensaje, sin tiempo de espera ni botón para desbloquear. state y result no lo liberan. Un paquete sin complete, y cualquier URL externa, se da por completado al verse. El editor avisa cuando el bloqueo está activado pero el paquete no puede cumplirlo.

Altura. El marco sigue el último resize (entre 100 y 4000 px). Hasta el primero, usa el alto configurado en el bloque.

Estado al volver. El estado de la simulación se guarda bajo su identificador de simulación, por contenido. Cuando el paquete carga y dice ready, recibe restore con el último estado. En una exportación SCORM el estado sobrevive entre sesiones (viaja en los datos de suspensión del LMS); en la vista previa, los enlaces compartidos y la revisión dura la sesión. Si el estado no cabe en el espacio del LMS, Creator recorta lo imprescindible en un orden fijo: primero las simulaciones que ni cuentan ni son leídas por el contenido condicional, después el estado de las demás, conservando sus variables y notas el mayor tiempo posible.

Nota. Para que la simulación cuente, el autor da al bloque un identificador de simulación (único en el contenido), la marca como Evaluable y le pone un peso; el paquete debe declarar result, además de complete o state. Dentro de un examen, la simulación cuenta junto a las preguntas: cada simulación vale su peso multiplicado por grade / 100, y el examen no se cierra hasta que todas las simulaciones que cuentan están cerradas. En un contenido sin exámenes, la nota del contenido es la media ponderada de sus simulaciones evaluables cuando todas han terminado, con el umbral de aprobado del contenido (que se enseña en la página de exportar). Las simulaciones evaluables que acompañan a exámenes puntuables no cuentan; el editor avisa.

Interacciones en SCORM. En una exportación SCORM, cada interaction se convierte en una entrada de cmi.interactions de tipo choice, con el id <identificador de simulación>-<intento>-<n>, la opción elegida, el resultado (correct, wrong/incorrect o neutral) y la ponderación; SCORM 2004 guarda además la descripción. En la vista previa no hay LMS, así que no se registra nada.

xAPI. Donde xAPI está activo, cada interaction se envía como «respondió» sobre una actividad de decisión (con la descripción, la opción elegida y success para correct e incorrect; neutral no lo lleva), y cuando la simulación se cierra una única sentencia de «puntuó» lleva la nota que cuenta, con el final y el tipo como extensiones. Un result parcial no emite nada. Empezar una partida nueva permite volver a emitir la sentencia. El autor puede desactivar xAPI en el bloque.

Límites de un vistazo

QuéLímite
slxd-embed.json16 KB, solo versión 1
Capacidadeshasta 32 entradas
Variableshasta 64, number o boolean
stateobjeto JSON, ≤ 1024 caracteres
Alto del marco100–4000 px
Descripción de una decisión≤ 250 caracteres
attempt, nenteros 1–9999
Identificadores[a-z][a-z0-9_]*, ≤ 64 caracteres
Progreso guardado (SCORM)presupuesto compartido de 4096 caracteres para todo el contenido

Siguientes pasos

Paquetes incrustados y simulaciones