CREACIÓN DE TESTS INTERACTIVOS...
Transcript of CREACIÓN DE TESTS INTERACTIVOS...
Escu
ela
Polit
écni
ca S
uper
ior d
e Li
nare
s
UNIVERSIDAD DE JAÉN Escuela Politécnica Superior de Linares
Trabajo Fin de Grado
______
CREACIÓN DE TESTS INTERACTIVOS SCORM
Alumno: Sergio Díaz Fuentes Tutor: Prof. D. Ildefonso Ruano Ruano Depto.: Ingeniería de Telecomunicación
Septiembre, 2016
Junio, 2016
1
UNIVERSIDAD DE JAÉN
ESCUELA POLITÉCNICA SUPERIOR DE LINARES
Departamento de Ingeniería de Telecomunicación
CURSO ACADÉMICO
2015-2016
TRABAJO FIN DE GRADO
TÍTULO DEL TRABAJO
CREACIÓN DE TESTS INTERACTIVOS SCORM
Grado en Ingeniería Telemática
AUTOR: Sergio Díaz Fuentes
TUTOR: D. Ildefonso Ruano Ruano
2
1
1 ÍNDICE .......................................................................................................................2
2 RESUMEN ..................................................................................................................4 3 INTRODUCCIÓN ........................................................................................................6
3.1 JavaScript ............................................................................................................7 3.2 HTML ...................................................................................................................9
3.2.1 La estructura del documento HTML ............................................................10 3.3 CSS ...................................................................................................................11 3.4 DOM ..................................................................................................................12 3.5 XML ...................................................................................................................14 3.6 QTI ....................................................................................................................15 3.7 SCORM .............................................................................................................18
3.7.1 SCORM 1.1 ................................................................................................19 3.7.2 SCORM 1.2 ................................................................................................19 3.7.3 La versión actual: SCORM 2004 .................................................................20 3.7.4 Compatibilidad entre versiones ...................................................................20 3.7.5 ¿Qué significa SCORM?.............................................................................20 3.7.6 Contenido del paquete ................................................................................22 3.7.7 Run Time ....................................................................................................22 3.7.8 Secuenciación ............................................................................................22
4 OBJETIVOS .............................................................................................................24 5 MATERIALES Y MÉTODOS .....................................................................................26
5.1 JavaScript ..........................................................................................................26 5.2 Editores de texto ................................................................................................26 5.3 SCORM RTE .....................................................................................................27
5.3.1 Run-Time Environment (RTE) Management ...............................................28 5.3.2 Application Programming Interface (API) ....................................................29
5.4 SCO ...................................................................................................................33 5.5 Navegadores Web .............................................................................................35
5.5.1 Google Chrome ..........................................................................................35 5.5.2 Mozilla Firefox ............................................................................................35 5.5.3 Internet Explorer .........................................................................................36
5.6 Funcionamiento de las librerías JavaScript para creación de tests online ..........36
5.6.1 Análisis del banco de preguntas .................................................................38 5.6.2 Evaluación de las preguntas .......................................................................44 5.6.3 Interacción de las librerías con el LMS .......................................................46
3
5.7 Interacciones .....................................................................................................50 5.8 Módulos SCORM desarrollados .........................................................................53
5.8.1 Módulo SCORM 2004 .................................................................................53 5.8.2 Módulo SCORM 1.2 ....................................................................................57
5.9 Manual de usuario y mantenimiento ..................................................................58 6 RESULTADOS Y DISCUSIÓN .................................................................................63 7 CONCLUSIONES .....................................................................................................71 8 ANEXOS...................................................................................................................74
8.1 Índice de figuras ................................................................................................74 8.2 Siglas .................................................................................................................76 8.3 Librería JavaScript creaPreguntas2004.js..........................................................76 8.4 Librería JavaScript creaPreguntasSinCom.js ................................................... 114 8.5 Librería JavaScript creaPreguntas12.js ........................................................... 146 8.6 Librería JavaScript principal.js ......................................................................... 188 8.7 Hoja de estilo estiloIndex.css ........................................................................... 189 8.8 Hoja de estilo estilo.css ................................................................................... 193
9 REFERENCIAS BIBLIOGRÁFICAS ........................................................................ 199
4
El e-learning o docencia electrónica consiste en el uso de Tecnologías de la
Información y Comunicaciones (TIC), principalmente Internet, aplicadas a la enseñanza. Puede presentarse como apoyo a clases presenciales en su modalidad mixta o b-learning
(de blended learning) o totalmente a distancia (e-learning puro). En cualquiera de estos
casos el elemento TIC más utilizado en la docencia universitaria son los LMS (Learning
Management System, Sistema de Gestión de Aprendizaje), prácticamente todas las
universidades españolas cuentan con un LMS institucional que sirve de apoyo a la
docencia en cualquiera de sus modalidades (ESPAÑOLAS, 2015).
En este Trabajo Fin de Grado (TFG) se han desarrollado una serie de herramientas que facilitan la creación de tests online y unos documentos web en forma
de contenidos SCORM (Sharable Content Object Reference Model) que muestran su uso
(Ostyn, 2007). SCORM es el estándar de facto de contenido de e-learning ya que es
soportado por la mayoría de los LMS del mercado. Los tests creados con estas
herramientas pueden integrarse en un LMS para interactuar con el mismo o presentarse de forma independiente en páginas Web HTML (Hypertext Markup Language) (“HTML
W3C,” 02/08/2016.).
Las herramientas desarrolladas se han implementado como cuatro librerías JavaScript (Melorose, Perroy, & Careas, 2015) que permiten crear tests interactivos, una
de ellas se utiliza mediante la asignación de sus argumentos para gestionar el uso de las
otras tres, las cuales se han diseñado para ser empleadas en distintos entornos:
Interaccionando con un LMS estableciendo comunicaciones mediante la versión
1.2 del estándar SCORM.
Interaccionando con un LMS estableciendo comunicaciones mediante la versión
2004 edición 4 del estándar SCORM.
Sin interaccionar con un LMS.
Las librerías desarrolladas han sido diseñadas para poder utilizar ficheros XML
(eXtensible Markup Language) (W3C, 02/08/2016.) que cumplan el estándar QTI (Question & Test Interoperability) (Tobergte & Curtis, 2013). QTI constituye una
especificación de interoperabilidad de preguntas y tests, es decir, permite definir tests y
preguntas de test en e-learning. De hecho constituyen el estándar de facto en evaluación
online, es utilizado por múltiples aplicaciones y, por supuesto, por los LMS. De este modo se pueden usar los ficheros QTI de bancos de preguntas o test que se hayan creado y
exportado en LMS para crear tests online mediante las herramientas desarrolladas en el
ámbito de este TFG. Las cuatro librerías, aparecen en el apartado Anexos.
5
De este modo, estas herramientas poseen una serie de características que las
hacen muy adecuadas tanto para su utilización en entornos educativos como para
entornos de evaluación más personales, que no necesiten interactuar con ningún LMS.
Entre ellas, podemos destacar las siguientes:
Cómoda portabilidad entre distintos Sistemas de Gestión de Aprendizaje al
trabajar con estándares.
Permite su reusabilidad entre distintos LMSs por el mismo motivo anterior.
Posibilidad de ser incluidas en módulos de aprendizaje SCORM, así como de
interactuar con el LMS, de tal forma, que quede en la plataforma reflejada toda
la actuación del alumno.
Posibilidad de ser incluidas en plataformas que utilicen la versión 1.2 de
SCORM, la 2004 edición 4 o ambas.
Capacidad de personalizar los tests mediante distintos parámetros, como el
número de preguntas o la aleatoriedad de las mismas entre otras.
Las dos librerías que se comunican con el LMS permiten la personalización de los tests y de la experiencia a través del módulo, mediante la identidad de los
alumnos y las puntuaciones obtenidas a lo largo del módulo, interactuando con
el LMS.
Además de las librerías JavaScript, los dos módulos SCORM constituyen
ejemplos de uso de las librerías en LMS mostrando algunas de las distintas posibilidades
de uso que se les puede dar:
Test sin seguimiento en el LMS.
Test de evaluación con seguimiento en el LMS.
Test de autoevaluación flexible con posibilidad de elegir sus parámetros.
6
Vivimos en una época de información y conocimientos llena de oportunidades,
donde las TIC juegan un papel importante y que han evolucionado de tal manera que han
cambiado la forma en que se realizan muchas actividades cotidianas de la sociedad
moderna. Esto ha originado lo que se conoce como sociedad de la información.
En ella, el acceso a la información es cada vez más libre y amplio, elimina
barreras geográficas, de tiempo y espacio, y favorece así, una comunicación ubicua y
asíncrona. Y es de este modo, como surge otra sociedad, la sociedad del conocimiento.
Esta sociedad está ligada a las actividades económicas, sociales y culturales, y propone
cambios especialmente en instituciones educativas en la forma de utilizar las tecnologías
en la enseñanza y en el aprendizaje de sus alumnos, creando así un nuevo conocimiento. En palabras de Fco. Javier Quiroz Waldez, “el siglo XXI se presenta asomando el
rostro de un nuevo paradigma de sociedad, un modelo donde la información entendida
como conocimiento acumulado de forma comunicable aparece como el cimiento del
desarrollo económico, político y social. El proceso de transformación hacia este modelo –
se afirma– es irreversible. El avance tecnológico faculta al ser humano para hacer
provecho de datos, información y conocimiento en formas, modos o maneras sin
precedentes, propiciando un intercambio científico, cultural y técnico a escala mundial,
pasando sobre las barreras geográficas, las divisiones políticas y las de tiempo”. (Quiroz,
2003). Es en este escenario, en el que se enmarca este TFG. Un escenario de
conocimiento libre, de interoperabilidad entre sistemas, de presentación de la información
en múltiples dispositivos y formatos y en el que los métodos de enseñanza han
evolucionado. Los alumnos ya no solo aprenden en el aula o a través de los libros, ahora
además tienen a su disposición una gran herramienta como es Internet para afianzar sus
conocimientos y solventar las posibles dudas que les surjan, aumentando así su
motivación.
Y es aquí, donde hay que mencionar la función realizada por el aprendizaje
electrónico o e-learning, un término que hace referencia a la educación a distancia
totalmente virtual en la que los estudiantes pueden interactuar con el profesor, así como
con el objeto de estudio en cuestión, utilizando un computador y un software cualquiera
como soporte de los procesos de enseñanza-aprendizaje.
Entre las ventajas brindadas por la formación online nos encontramos con:
Eliminación de barreras espaciales y temporales.
Actualización constante de los contenidos.
Reducción de costos.
7
El instrumento usado para compartir contenidos de aprendizaje electrónico es
Internet o intranets. Con la ayuda de un laboratorio web, se pueden mostrar textos,
imágenes y gráficos en un navegador web.
Pero para que los usuarios puedan hacer uso de estos contenidos, el laboratorio
ha de estar en un entorno software y hardware adecuado. Este entorno, creado para
automatizar y gestionar el desarrollo de actividades didácticas se conoce como Sistema
de Gestión de Aprendizaje (SGA), en inglés LMS.
Existe un gran número de plataformas virtuales, tanto propietarias como de código
abierto. La plataforma de licencia libre Moodle sería un ejemplo de ellas, así como ILIAS,
utilizada por la Universidad de Jaén.
Una vez que se añade el laboratorio al LMS, se permite el acceso solo a los
usuarios autorizados. Esto facilita el seguimiento en el aprendizaje de los alumnos por
parte del profesorado, así como evaluar sus conocimientos y competencias.
La posibilidad de ofrecer a la Universidad una serie de herramientas para la
creación de test interactivos de forma dinámica y contribuir de esta manera con el
aprendizaje electrónico junto con el trabajar con JavaScript, con el lenguaje de marcas de
hipertexto (HTML) y con los estándares SCORM y QTI han sido las principales
motivaciones para la realización de este TFG.
Lo que se ha logrado con este trabajo ha sido la obtención de unas librerías
capaces de mostrar hasta cuatro tipos diferentes de preguntas, que serían las de opción
múltiple respuesta única, opción múltiple respuesta múltiple, de tipo numérica y de
rellenar huecos, a partir de un fichero XML en el que se encuentra el banco de preguntas
relativas al tema a evaluar. Además, este fichero XML estaría estructurado siguiendo el
estándar QTI, ampliamente utilizado por los SGAs, como ILIAS por ejemplo.
Además, para la creación de los tests, se le da la posibilidad al administrador o
profesor, de configurar ciertos parámetros de los mismos, como lo serían el banco de preguntas a utilizar, el número de preguntas del que va a constar el test, la aleatoriedad
de las mismas, la aleatoriedad de sus opciones en caso de tener, un tiempo para la realización del test y si se va a comunicar o no con el Sistema de Gestión de Aprendizaje.
3.1 JavaScript
JavaScript es el lenguaje de programación de la Web. La inmensa mayoría de
sitios web actuales utilizan JavaScript, y todos los navegadores web modernos -en
ordenadores de sobremesa, videoconsolas, tabletas y teléfonos inteligentes- incluyen
8
intérpretes de JavaScript haciéndolo el lenguaje de programación más omnipresente en
la historia.
JavaScript es parte del trío de tecnologías que todos los desarrolladores web deben aprender: HTML para especificar el contenido de las páginas web, Cascading
Style Sheets (CSS) para especificar la presentación de las páginas web y JavaScript para
especificar el comportamiento de las páginas web. (Melorose et al., 2015).
Surgió de la necesidad de crear un lenguaje de programación que se ejecutara en
el navegador del usuario ante la creciente inclusión de formularios en las primeras
aplicaciones web allá por principios de los años 90 y la lentitud de las conexiones
existentes por entonces.
Así, si se producían errores al rellenar el formulario, no había que esperar hasta
que el servidor respondiera y mostrara de nuevo el formulario indicándole los errores que
había cometido.
De este modo, y tras las implementaciones de varias empresas, para evitar una
guerra de tecnologías, se decidió que lo más adecuado era estandarizar el lenguaje
JavaScript. De esta forma, en 1997 se envió la especificación por parte de Netscape al organismo European Computer Manufacturers Association (ECMA).
El primer estándar se denominó ECMA-262, y allí se definió el lenguaje
ECMAScript. JavaScript de esta forma, no es más que la implementación que hizo
Netscape del estándar ECMAScript.
Se trata de un lenguaje de propósito general robusto y eficiente. Un lenguaje de
programación interpretado (no requiere compilación) y utilizado principalmente en su
forma del lado cliente para crear aplicaciones cliente. Su sintaxis es similar a Java y al
lenguaje C. Se puede insertar en páginas HTML o bien ser agregado como una
referencia a un archivo externo.
Mejora la apariencia con la inclusión de gráficos y permitiendo la interacción con el
usuario. Además, se pueden implementar cálculo de funciones y permite la validación de
formularios como se ha comentado anteriormente.
JavaScript se puede incluir en el contenido HTML o puede ser usado para modificar la presentación (CSS) o el comportamiento (event handling) de las páginas
webs. Un manejador de eventos (o event handler) es una función JavaScript que
registramos con el navegador y que el navegador invoca cuando ocurren algunos tipos
de eventos. El evento en cuestión puede ser un clic de ratón o la pulsación de una tecla.
Un manejador de eventos también puede ser invocado cuando el navegador termina de
cargar un documento, cuando el usuario cambia el tamaño de la ventana del navegador o
cuando el usuario introduce datos en un elemento de un formulario HTML. (Melorose et
al., 2015).
9
La manera más sencilla de definir manejadores de eventos es mediante los
atributos HTML que comienzan con “on”. El manejador “onclick” es particularmente útil
cuando estás desarrollando programas de prueba. Este haría referencia a hacer una
pulsación o clic al botón del ratón sobre un elemento de la página.
Por otro lado, las interfaces de programación de interfaces (APIs) usadas
normalmente pueden ser algo complicadas y están llenas de incompatibilidades con los
navegadores. Por estas razones, muchos o la mayoría de los programadores de
JavaScript del lado cliente eligen utilizar una librería del lado cliente o framework para
simplificar el trabajo de la programación.
La librería más popular en este sentido sería jQuery. Esta define un API inteligente
y fácil de usar para acceder al contenido HTML, modificar la presentación y el
comportamiento. Esta librería ha sido probada y trabaja en la mayoría de navegadores,
incluidos los antiguos como Internet Explorer 6.
En resumen, se pueden manejar distintos objetos como ventanas, documentos,
botones, tablas, etcétera que tienen asociados distintos parámetros o atributos cuyo valor
puede ser modificado a través de las sentencias.
3.2 HTML
El lenguaje de marcas de hipertexto comúnmente abreviado como HTML, se basa en el metalenguaje SGML (Standard Generalized Markup Language) y es el formato de
los documentos de la World Wide Web (WWW). El World Wide Web Consortium (W3C)
es la organización que desarrolla los estándares para normalizar el desarrollo y la
expansión de la web y la que publica las especificaciones relativas al lenguaje HTML.
HTML fue concebido como un lenguaje para el intercambio de documentos
científicos y técnicos adaptado para su uso por no especialistas en tratamiento de
documentos. HTML resolvió el problema de la complejidad de SGML sirviéndose de un
reducido conjunto de etiquetas estructurales y semánticas apropiadas para la realización
de documentos relativamente simples. Pero, además de simplificar la estructura de los
documentos, HTML soportaba el hipertexto. El lenguaje HTML nace en 1991 de manos de Tim Bernes-Lee del CERN (Conseil
Européen pour la Recherche Nucléaire) como un sistema hipertexto con el único objetivo
de servir como medio de transmisión de información entre los científicos que se
ocupaban de la Física de alta energía, como parte de la iniciativa WWW. Así pues, HTML
tuvo lugar a la par que el origen de la Web, ya que se trata del lenguaje que sirve para
crear páginas web.
10
HTML es el lenguaje de marcado estándar utilizado para crear páginas web. Junto
con CSS y JavaScript, HTML es una tecnología fundamental utilizada para crear páginas
web así como para crear interfaces de usuario para aplicaciones móviles y web.
Los navegadores web pueden leer los archivos HTML y traducirlos a páginas web
visibles o audibles. HTML describe la estructura de un sitio web semánticamente y antes
del advenimiento de CSS, incluía señales para la presentación o apariencia del
documento, por lo que es un lenguaje de marcas, en lugar de un lenguaje de
programación.
Los elementos HTML forman los pilares de las páginas HTML. HTML permite que
tanto las imágenes como otros objetos sean incrustados y puedan ser usados para crear
formularios interactivos. Proporciona un medio para crear documentos estructurados al
indicar la semántica estructural del texto con encabezados, párrafos, listas, enlaces, citas
y otros ítems.
Los elementos HTML son delineados por las etiquetas, escritas usando paréntesis
angulares (<>). Los navegadores no muestran las etiquetas HTML, pero las utilizan para
interpretar el contenido de la página.
HTML puede incrustar scripts escritos en lenguajes tales como JavaScript que
afecten al comportamiento de las páginas web HTML. Las marcas HTML pueden además
referirse al navegador como hojas de estilo en cascada (CSS) para definir el aspecto y el
diseño del texto y otros ítems. El W3C, encargado de mantener los estándares de HTML
y de CSS, ha fomentado el uso de CSS para la presentación de los documentos desde
1997.
3.2.1 La estructura del documento HTML
Un documento HTML comienza con la etiqueta <html> y termina con </html>.
Dentro del documento hay dos zonas principales: el encabezamiento, delimitado
por las marcas <head> y </head> que sirve para definir algunos valores válidos para todo
el documento, y el cuerpo, delimitado por las etiquetas <body>, donde reside la
información del documento. Existen muchos otros elementos que se engloban dentro del
encabezamiento pero para la estructura básica del lenguaje HTML en su nivel básico no
son necesarios.
El cuerpo de un documento HTML contiene el texto, imágenes, etc. que con la
presentación y los efectos que se decidan, se presentarán ante el usuario. Dentro del
cuerpo se puede aplicar una serie de efectos a través de diferentes etiquetas.(“Hipertexto
HTML,” 29/07/2016.).
Así pues, la figura 1 muestra la estructura de un documento HTML:
11
Figura 1: Estructura básica documento HTML. Fuente:http://www.hipertexto.info/documentos/html.htm
3.3 CSS
Las hojas de estilo en cascada son un mecanismo simple para añadir estilo (por
ejemplo, fuentes, colores, espacios) a los documentos web. (“CSS W3C,” 30/07/2016.).
Esta separación entre la estructura y la presentación es muy importante, ya que
permite que sólo modificando las hojas de estilo el aspecto de una página web cambie
completamente. Esto hace posible que los usuarios puedan utilizar sus propias hojas de
estilo personalizadas.
De nuevo, es el W3C el encargado de definir las especificaciones del estándar
CSS. Más tarde, son los navegadores los que intentan implementar todas esas
especificaciones, para que las páginas web se vean igual en todos ellos. En este sentido,
y dado que cada navegador puede implementar el estándar en un porcentaje distinto, se
recomienda el realizar las páginas web siguiendo el estándar CSS y no las
especificaciones de un navegador determinado. Además, se ha de intentar mantener
nuestro navegador lo más actualizado posible, ya que a medida que el navegador va
cumpliendo con el estándar, va añadiendo nuevas funcionalidades.
De este estándar, se han realizado varias especificaciones, generalmente
añadiendo funciones al anterior. Así, la primera especificación oficial de CSS fue CSS1 y
se publicó en 1995 y se abandonó en abril de 2008. Entre las funcionalidades que ofrecía
nos encontramos con las siguientes:
Propiedades de la fuente, como tipo, énfasis, tamaño…
Color del texto, bordes o fondos.
Atributos del texto, como letras, líneas, espaciado entre las palabras…
La especificación CSS2 fue publicada como recomendación en mayo de 1998
también por la W3C y se abandonó en abril de 2008. Amplió las funcionalidades de CSS1
ofreciendo entre otras las siguientes:
Las funcionalidades propias de las capas div como posicionamiento
relativo/absoluto/fijo, niveles, etcétera.
Soporte para las hojas de estilo auditivas.
12
Sombras, texto bidireccional, etcétera.
La primera revisión de CSS2, comúnmente conocida como CSS 2.1, corrige
algunos errores encontrados en CSS2, elimina algunas funcionalidades poco soportadas
en los navegadores y añade alguna nueva. Después de ser propuesta como candidata y
ser rechazada varias veces, finalmente fue publicada como recomendación oficial en
junio de 2011.
La última especificación hasta la fecha, sería CSS3 y está dividida en varios
documentos separados, llamados módulos. Cada módulo añade nuevas funcionalidades
a las definidas en CSS2, de forma que se conservan las anteriores para preservar la
compatibilidad. Debido a su modularización, diferentes módulos pueden encontrarse en
distintos estados de desarrollo. A fecha de noviembre de 2011, existen alrededor de
cincuenta módulos publicados, tres de los mismos se convirtieron en recomendaciones
oficiales de la W3C en 2011.
Una vez repasadas las distintas especificaciones de este estándar, pasamos a
comentar las ventajas que presenta:
Control centralizado de la presentación de un sitio web completo con lo
que se agiliza de forma considerable la actualización del mismo.
Optimización del ancho de banda de la conexión, pues un sólo archivo
CSS puede servir para una multitud de documentos.
Mejora en la accesibilidad del documento, debido a que con el uso de
hojas de estilo se evitan antiguas prácticas necesarias para el control del
diseño y que iban en perjuicio de ciertos usos de los documentos, por
parte de navegadores orientados a personas con algunas limitaciones
sensoriales. (“CSS Wiki,” 30/07/2016.).
3.4 DOM
El Modelo de Objeto del Documento (DOM) es un estándar del W3C. El DOM
define un estándar para el acceso a documentos.
“El DOM es una interfaz de plataforma y leguaje neutro que permite a los
programas y scripts acceder y actualizar el contenido, la estructura y el estilo de un
documento de forma dinámica”. (“https://www.w3.org/DOM/#what,” 29/07/16.).
El estándar W3C DOM está separado en tres partes diferentes:
Núcleo DOM – modelo estándar para todos los tipos de documentos.
XML DOM – modelo estándar para documentos XML.
HTML DOM – modelo estándar para los documentos HTML.
13
El HTML DOM es el modelo de objetos y la interfaz de programación estándar
para los documentos HTML. Este define:
Los elementos HTML como objetos.
Las propiedades de todos los elementos HTML.
Los métodos para acceder a todos los elementos HTML.
Los eventos para todos los elementos HTML.
Cuando una página web se carga, el navegador crea un modelo de objetos del
documento de la página. La figura 2 muestra el árbol de objetos del HTML DOM:
Figura 2: Árbol de objetos HTML DOM. Fuente: http://www.w3schools.com/js/js_htmldom.asp
Con el modelo de objetos, JavaScript consigue todo el poder que necesita para
crear HTML de forma dinámica:
JavaScript puede cambiar todos los elementos HTML de la página.
JavaScript puede cambiar todos los atributos HTML de la página.
JavaScript puede cambiar todos los estilos CSS de la página.
JavaScript puede eliminar elementos y atributos HTML ya existentes.
JavaScript puede añadir nuevos elementos y atributos HTML.
JavaScript puede reaccionar a todos los eventos HTML existentes en la
página.
JavaScript puede crear nuevos eventos HTML en la página.
En otras palabras, el HTML DOM es un estándar para obtener, cambiar, añadir, o
eliminar elementos HTML. (“http://www.w3schools.com/js/js_htmldom.aspitle,”
29/07/2016.).
14
3.5 XML
El lenguaje extensible de marcas XML, describe una clase de objetos de datos
llamados documentos XML y parcialmente describe el comportamiento de los programas
informáticos que los procesan. XML es un perfil de aplicación o una forma restringida de SGML, el estándar del lenguaje de marcado generalizado (Standard Generalized Markup
Language) [ISO 8879]. Por construcción, los documentos XML son conformes a los
documentos SGML. (“XML W3C,” 31/07/2016.).
En la figura 3 podemos ver cómo SGML engloba tanto a XML como a HTML.
Figura 3: Gráfico estándares. Fuente: http://www.cyta.com.ar/elearn/editor_digital/curso_archivos/m4_a2_lectura/xml_0.htm
Los documentos XML están compuestos de unidades de almacenamiento
llamadas entidades, que contienen datos, ya sean analizados o no. Los datos analizados
se componen de caracteres, algunos de los cuales forman datos de caracteres y algunos
otros componen las marcas.
Las marcas codifican la descripción del documento y la estructura lógica. XML
proporciona un mecanismo para imponer limitaciones al diseño del almacenamiento y a la
estructura lógica. Un ejemplo de fichero XML puede verse en la figura 4.
15
Figura 4: Ejemplo fichero XML. Fuente: http://social.technet.microsoft.com/wiki/contents/articles/27147.windows-phone-how-to-
create-a-data-class-from-an-xml-document.aspx
Los objetivos que se cubren con el diseño de XML son los siguientes:
XML será directamente utilizable a través de Internet.
XML debe soportar una amplia variedad de aplicaciones.
XML debe ser compatible con SGML.
Debe ser fácil escribir programas que procesen documentos XML.
El número de características opcionales en XML debe mantenerse al
mínimo absoluto, idealmente cero.
Los documentos XML deben ser legibles por humanos y ser
razonablemente claros.
El diseño de XML debe ser formal y conciso.
Los documentos XML deben ser fáciles de crear.
Así pues, tenemos un estándar con las siguientes características: (“Manual de
XML,” 31/07/16.).
Extensibilidad.
Estructura.
Validación.
Basado en texto.
Orientado a los contenidos no a la presentación.
Las etiquetas se definen para crear los documentos, no tienen un
significado preestablecido.
3.6 QTI
La Especificación de Interoperabilidad de Preguntas y Pruebas de IMS (IMS/QTI
por sus siglas en inglés) define un formato estándar para la representación de contenidos
16
y resultados de evaluaciones educativas, soportando el intercambio de este material
entre sistemas de creación y de visualización, repositorios y otros sistemas de gestión del
aprendizaje (LMS por sus siglas en inglés). Permite la creación y entrega de materiales
de pruebas en múltiples sistemas de forma intercambiable. Es diseñado para facilitar
la interoperabilidad entre sistemas. (Joint Information Systems Committee (JISC), 2007).
Las especificaciones de las preguntas y exámenes QTI describen la estructura
básica para la representación de las preguntas (item) y los exámenes (assessment). Esta
especificación capacita el intercambio de preguntas y exámenes entre sistemas para la
gestión del aprendizaje (Learning Management Systems), al igual que los estándares
para los contenidos desarrollados por este mismo consorcio. La especificación del QTI
está definida en lenguaje XML para que pueda ser adoptado lo más ampliamente posible.
La especificación se puede extender y personalizar para ser implantado en sistemas
propietarios. (Tobergte & Curtis, 2013).
La primera versión de la especificación V0.5 apareció en marzo de 1999, y la
primera versión no beta V 1.0 es de febrero del 2000. Actualmente se dispone de la
versión 1.2 de febrero del 2002.
Los objetivos principales del grupo de trabajo del IMS para las QTI son:
Capacidad de proporcionar exámenes o bancos de preguntas a los
usuarios de los entornos virtuales de enseñanza.
La capacidad de usar exámenes y bancos de preguntas procedentes de
distintas fuentes no vinculadas a un solo vendedor de sistemas virtuales
de enseñanza (VLE).
Soporte para las herramientas que permitan crear nuevos exámenes y
preguntas.
Capacidad de generar los informes de los resultados de las pruebas de
una forma consistente.
Se presenta a continuación en la figura 5 el núcleo de la estructura adoptada de
forma esquemática:
17
Figura 5: Esquema de la estructura básica adoptada por el QTI IMS. Fuente: (Tobergte &
Curtis, 2013).
Los ítems son las unidades mínimas que pueden ser intercambiadas usando QTI.
Los ítems no pueden estar compuestos por otros ítems.
Además, un archivo QTI-XML puede contener varias secciones. Las secciones
pueden contener más secciones o ítems y realizan la función de agrupar las preguntas. Un archivo QTI-XML también puede incluir más de un assessment o evaluación.
Cada evaluación debe contener al menos una sección. No puede anexarse ítems
directamente a la evaluación.
La figura 6 muestra de forma gráfica el diagrama de casos de uso, en el que
aparecen los posibles actores del sistema, junto con los procesos y sistemas.
Figura 6: Casos de uso del sistema de exámenes. Fuente: (Tobergte & Curtis, 2013).
Los principales componentes del sistema de exámenes son:
Authoring system. Es el proceso que soporta la creación y edición de
exámenes, secciones y preguntas (ASIs).
18
Assessment engine: Es el proceso que soporta la evaluación o
calificación de las respuestas de forma que genere calificaciones y
consejos o refuerzos de estudio.
Learning management system: Sistema o proceso responsable de la
gestión completa de la arquitectura de enseñanza.
Candidate and Repository. Se trata de una base de datos con las
especificaciones para el candidato (alumno).
ASI repository. Se trata de una base de datos local de exámenes
secciones y preguntas.
External ASI repository. Se trata de una base de datos externa que debe
ser importada haciendo uso de las especificaciones del QTI.
En cuanto a las preguntas y las respuestas, en primer lugar hay que aclarar que
cada ítem se interpreta como un bloque fundamental que contiene una o más preguntas y
respuestas.
Básicamente, se distinguen tres tipos de respuestas: simples, compuestas y
definidas por el fabricante. Además, dentro de las simples y compuestas, se pueden
diferenciar dos tipos en función del control que se haga del tiempo.
3.7 SCORM
Desde hace mucho tiempo, uno de los mayores problemas relacionados con el e-
learning ha sido la creación y el desarrollo de contenidos e-learning de calidad. Los
contenidos de e-learning son en realidad software, por lo que su desarrollo ha estado
ligado a los mismos problemas que otros proyectos software.
Cada sistema de gestión de aprendizaje tenía distintos entornos de envío y
seguimiento del contenido de aprendizaje, por lo que si una empresa quería renovar su
sistema o cambiar de distribuidor, esto a menudo significaba abandonar un contenido
caro y empezar de nuevo. Del mismo modo, si un distribuidor de contenido quería
distribuir ampliamente su contenido, esto era bastante caro, ya que había que
acomodarlo a una versión distinta para cada sistema de gestión de aprendizaje.
A medida que los requerimientos cambiaron y coincidieron en empresas y en
agencias de gobierno, se hizo evidente la importancia de realizar módulos de contenido
reutilizable.
Fue entonces cuando hubo una fuerte motivación por parte del mercado para
crear contenido que fuese duradero, portable y reutilizable entre distintos sistemas, en
otras palabras, que el contenido fuera interoperable.
19
Con contenido interoperable, los desarrolladores de contenido ganan porque el
mismo contenido puede funcionar en diferentes sistemas sin modificación. Los
vendedores de sistemas de gestión ganan porque pueden centrarse en los aspectos de
gestión del aprendizaje, sin tener que estar adaptando constantemente el entorno de
entrega a varias librerías de contenido.
Empresas y agencias que utilizan los sistemas de gestión de aprendizaje y el
contenido ganan porque en lugar de perder dinero y tiempo en la integración de las
diferentes bibliotecas del contenido, estas se pueden mezclar fácilmente con su propio
contenido personalizado usando el mismo entorno de entrega. (Ostyn, 2007).
De este modo fue como, hace ya varios años, la oficina de tecnología de la casa blanca, el departamento de defensa y el departamento de trabajo lanzaron la Advanced
Distributed Learning Initiative (ADL) o iniciativa de aprendizaje distribuido avanzada en
los Estados Unidos. Uno de los primeros proyectos de la ADL fue el de crear ciertas
especificaciones o normas para el contenido electrónico de aprendizaje. El resultado fue
un conjunto de libros, cada uno de los cuales, describía un aspecto diferente de la solución. Al resultado se le llamó Sharable Content Object Reference Model, o SCORM.
Una posible traducción al castellano sería “modelo de referencia para un objeto de
contenido compartible”.
El SCORM nació para tomar lo mejor de los primeros esfuerzos, las
especificaciones y normas, y alcanzar los objetivos de durabilidad, portabilidad,
reutilización, interoperabilidad y accesibilidad al contenido.
Muchas personas de la industria del e-learning estaban implicados en la génesis
de SCORM, junto con el equipo técnico financiado por la ADL. Cada versión de SCORM ha sido probada en eventos “Plugfest”.
3.7.1 SCORM 1.1
La primera versión de SCORM fue un globo de ensayo, destinado a descubrir
problemas no resueltos. Implementaciones de prueba revelaron que SCORM 1.1 era
menos funcional de lo esperado, y la interoperabilidad fue en su mayor parte fruto del
azar. Las lecciones de SCORM 1.1 fueron utilizadas para las versiones posteriores.
3.7.2 SCORM 1.2
La primera versión “real” de la era SCORM fue SCORM 1.2. Esta fue la primera
versión para la cual había disponible un conjunto de pruebas, y por lo tanto, fue la
primera versión para la que la conformidad puedo ser verificada.
SCORM 1.2 demostró que el contenido podía ser portátil e interoperable. Alguna
cuestión pendiente fue el resultado de que no se ajustara a SCORM, o el resultado de
20
una mala interpretación de una característica SCORM, hecho que aún no se había
contemplado.
3.7.3 La versión actual: SCORM 2004
SCORM 2004 mejora significativamente a SCORM 1.2, eliminando aún más
ambigüedades en la especificación, y haciendo SCORM operable con robustos
estándares del Instituto de Ingenieros eléctricos y electrónicos (IEEE). El API ahora es
compatible con la gran variedad de lenguajes apoyados por el ECMAScript.
Además de mejorar la versión 1.2, SCORM 2004 incluye las funciones opcionales
de secuenciación y navegación. La adición de la secuenciación es un hito importante de
funcionamiento. SCORM 1.2 trataba sobre la portabilidad del contenido, pero dejó en
manos del alumno elegir qué parte del contenido se ejecutara.
SCORM 2004 agrega la capacidad de presentar paquetes de contenido al alumno
mediante la secuenciación guiada o adaptativa.
3.7.4 Compatibilidad entre versiones
Muchos proveedores de sistemas de gestión de aprendizaje continuarán
apoyando el contenido SCORM 1.2 durante mucho tiempo, junto con SCORM 2004. Hay
herramientas disponibles o se pueden construir relativamente con facilidad para convertir
los paquetes de contenido SCORM 1.2 a SCORM 2004.
Es posible lanzar objetos de contenido SCORM 1.2 sin modificaciones en un
entorno SCORM 2004 mediante el uso de un “contenedor” (wrapper) proporcionado por
la ADL.
También es posible poner en marcha objetos de contenido SCORM 2004 en un
entorno SCORM 1.2 a través de otro contenedor. Sin embargo, en ese caso, y
dependiendo del contenido, algunos datos de seguimiento o de sesión se pueden perder
debido a que SCORM 1.2 no soporta el modelo de datos completo del IEEE utilizado por
SCORM 2004. Obviamente, los entornos SCORM 1.2 no son compatibles con la
secuenciación de SCORM 2004.
Otro enfoque sería el de crear objetos de contenido que puedan trabajar tanto en
SCORM 1.2 como en SCORM 2004, con una degradación importante si el entorno es
SCORM 1.2. Esta aproximación es por supuesto más cara. (Ostyn, 2007).
3.7.5 ¿Qué significa SCORM?
SCORM es un conjunto de normas técnicas para los productos software de e-
learning. SCORM explica a los programadores cómo escribir el código para que pueda
encajar bien con otro software de e-learning. Es el estándar de facto para la
21
interoperabilidad en e-learning. En concreto, SCORM gobierna cómo el contenido de
aprendizaje online y los LMSs se comunican entre sí.
Un contenido de aprendizaje que cumpla con las normas SCORM deberá cumplir
los siguientes requisitos:
Durabilidad: el contenido debe durar el tiempo suficiente para amortizar
su coste, y ser utilizado siempre y cuando sea relevante.
Portabilidad: Debe ser posible trasladar el contenido fácilmente desde un
entorno de entrega a otro. El mismo contenido debe trabajar sin
modificaciones en diferentes entornos de entrega, siempre que el entorno
incluya un navegador web.
Reusabilidad: Debe ser posible construir el contenido en pequeños
módulos reutilizables que puedan ser recombinados de diferentes
maneras. Los diferentes LMSs deben ser capaces de compartir contenido
reutilizable.
Interoperabilidad: El mismo contenido debe trabajar de la misma manera
cuando se hace uso de él en diferentes entornos.
Accesibilidad: Debe ser posible encontrar el contenido en un repositorio.
Esto requiere que algunos datos de catalogación estándar sean
asociados con el contenido.
El SCORM se compone de varios libros, cada uno de ellos especifica algunos
aspectos técnicos de contenido compartible. Se proporciona algún tipo de software junto
con los libros para verificar la conformidad y permitir las demostraciones de algunas de
las funcionalidades SCORM.
SCORM especifica que el contenido debería:
Ser empaquetado en un fichero ZIP.
Ser descrito en un fichero XML.
Comunicarse vía JavaScript.
Secuenciar usando reglas en XML.
SCORM se compone de tres sub-especificaciones: (“SCORM explained,”
31/07/16).
La sección del contenido del paquete especifica cómo debe ser
empaquetado y descrito el contenido. Se basa principalmente en XML.
La sección de tiempo de ejecución (Run Time) detalla cómo el contenido
debería lanzarse y cómo se comunica este con el LMS. Se basa
principalmente en ECMAScript (JavaScript).
22
La sección se secuenciación establece cómo el alumno puede navegar entre las diferentes partes de un curso o SCO (Sharable Content Object).
Se define mediante un conjunto de reglas y atributos escritos en XML.
3.7.6 Contenido del paquete
SCORM especifica que el contenido debe ser empaquetado en un directorio auto
contenido o en un fichero ZIP. Esta forma de importación se denomina fichero de
intercambio de paquete (PIF). El PIF debe contener siempre un fichero XML llamado
imsmanifest.xml en el directorio raíz. El archivo de manifiesto contiene toda la información
que el LMS necesita para importar el contenido.
El manifiesto divide el curso en una o más partes llamadas SCOs que traducido
serían objetos de contenido compartible. Estos se pueden combinar en una estructura de
árbol que representa el curso. El manifiesto contiene una representación en XML del
árbol de actividades, información sobre cómo poner en marcha cada SCO y
(opcionalmente) metadatos que describen el curso y sus partes.
3.7.7 Run Time
La determinación del tiempo de ejecución detalla que el LMS debería lanzar el
contenido en un navegador web, ya sea en una nueva ventana o en un conjunto de
marcos. El LMS sólo puede presentar un SCO a la vez.
Todo el contenido debe ser entregado y presentado en un navegador web. Una
vez que el contenido es presentado, utiliza un algoritmo bien definido para localizar una
API de ECMAScript (JavaScript) que es proporcionado por el LMS. Esta API tiene
funciones que permiten el intercambio de datos con el LMS. El modelo de datos CMI
proporciona una lista de elementos de datos (un vocabulario) que pueden ser escritos y
leídos desde el LMS. Algunos elementos del modelo de datos incluyen por ejemplo el
estado del SCO (completado, aprobado, suspenso, etcétera), la puntuación que el
alumno logra, la ubicación del alumno, o la cantidad total de tiempo que el alumno pasa
en el SCO.
3.7.8 Secuenciación
La definición de la secuenciación permite al autor del contenido administrar la
forma en la que el alumno navega entre los distintos SCOs y cómo los datos del progreso
se van acumulando.
Las reglas de la secuenciación se representan mediante XML dentro del
manifiesto del curso. La secuenciación actúa en un modelo de seguimiento que se
asemeja mucho a los datos del modelo CMI producidos por los SCOs durante el tiempo
23
de ejecución. Las reglas de secuenciación permiten al autor del contenido hacer cosas
como:
Determinar qué controles de navegación debería el LMS proporcionar al
usuario (botones de siguiente/anterior, una tabla de navegación por los
contenidos, etcétera).
Precisar que ciertas actividades se han de completar antes de comenzar
otras (requisitos previos).
Hacer que unas partes del curso cuenten más que otras para el estado
final del curso o para la puntuación final (creando secciones opcionales o
mediante pesos en las preguntas).
Seleccionar de forma aleatoria un subconjunto diferente de SCOs
disponibles para ser entregados en cada nuevo intento.
Llevar al usuario hacia la lección que no fue superada.
24
El objetivo principal de este TFG es la obtención de herramientas y ejemplos de
desarrollo de tests en formato SCORM. Para ello, existen una serie de objetivos
secundarios previos, que serían los siguientes:
Estudio de estándares SCORM.
Estudio de sus normas y la evolución de sus distintas versiones.
Comprender los modelos existentes de WebLab basados en SCORM que
se han facilitado.
Estudio del estándar QTI. Obtención de tests.
Desarrollo de librerías JavaScript que ayuden a la creación y ejecución de los tests.
Edición de páginas web mediante programación HTML y JavaScript.
Edición de páginas web haciendo uso de hojas de estilo.
Personalización de la interacción entre el LMS y el alumno a través del
uso de las comunicaciones SCORM-LMS y de las herramientas
desarrolladas.
Revisión y mejora de los modelos WebLab basados en SCORM con el fin
de obtener mejores desarrollos.
Adaptación de WebLab basado en SCORM 2004 a SCORM 1.2.
Creación de ejemplos de WebLab demostrativos del trabajo realizado.
Presentación de los WebLab en un LMS.
Además, para el desarrollo de las librerías JavaScript para crear y ejecutar los tests, se han debido cubrir estos otros objetivos:
Parsear mediante JavaScript los ficheros XML en formato QTI de los bancos de preguntas para los tests.
Proporcionar librerías para la realización de test sin comunicación
SCORM-LMS.
Elegir e importar la librería JavaScript a utilizar en el documento
dinámicamente dependiendo de si se va a comunicar con el LMS o no.
Dar soporte al mayor número de tipos de preguntas posible. Poder modificar el número de preguntas que aparecen en el test creado.
Dar la opción al administrador de barajar o no las preguntas contenidas
en el fichero XML.
25
Dar la opción al administrador de aleatorizar las opciones de las
preguntas (en caso de existir, dependerá del tipo de pregunta).
Proporcionar la posibilidad de ponerle tiempo de realización al test.
26
En este capítulo se va a abordar el lenguaje de programación con el que se han
desarrollado las librerías, así como las herramientas usadas para programarlas y
depurarlas. También se explican conceptos necesarios para la comprensión de la
interacción entre las librerías JavaScript y el LMS.
5.1 JavaScript
El lenguaje de programación utilizado para el desarrollo de las librerías ha sido
JavaScript. Este lenguaje se imparte en varias asignaturas del Grado en Ingeniería
Telemática y cuenta con un nutrido número de ventajas, hecho que hace atractivo su uso.
Entre ellas vamos a destacar las siguientes (“Ventajas JavaScript,” 01/08/2016.):
Es un lenguaje de alto nivel que se compila en tiempo de ejecución.
Es un lenguaje sencillo y ligero.
Casi todos los navegadores puede ejecutar JavaScript.
Tiene una baja barrera de entrada ya que todo lo que necesitas para
programar en JavaScript es un editor de texto y un navegador web.
Es un lenguaje dinámico, lo que significa que los elementos de un
programa pueden variar mientras está en ejecución.
Es flexible, lo que permite por ejemplo la reutilización del código, y la
adición de funciones o la modificación de ellas si fuera necesario.
El código JavaScript se ejecuta en el cliente, por lo que no es necesario
la realización excesiva de peticiones al servidor.
El lenguaje de scripting es fiable y seguro porque va en claro y hay que
interpretarlo, por lo que es posible realizarle un filtrado.
Utiliza poca memoria.
Útil para el desarrollo de páginas web dinámicas.
Fácil de integrar.
Puede agregar interactividad a elementos web.
5.2 Editores de texto
Para desarrollar las librerías, se ha elegido el editor de texto libre Notepad++,
cuyo logo se aprecia en la figura 7 y que en Windows funciona muy bien. Otros editores
interesantes también para la programación de JavaScript serían E Text Editor, UltraEdit,
Atom o Sublime Text.
27
Figura 7: Icono de Notepad++. Fuente: http://www.transitionblog.com/alternatives-to-notepad/
También se podría haber considerado el uso de un entorno de programación
integrado (IDE) como Eclipse, Coda o Netbeans. Por último, otra opción interesante sería
la de Brackets, que además de ser gratis y multiplataforma, está escrita en JavaScript.
Además de para el desarrollo de las librerías, comentar que este editor ha sido el
que se ha usado también para la visión y modificación de los ficheros XML, edición de las
páginas web y creación de hojas de estilo utilizadas en ellas.
5.3 SCORM RTE
El modelo del entorno de tiempo de ejecución (Run-Time Enviroment) detalla los
requisitos para el lanzamiento de objetos de contenido, para el establecimiento de la
comunicación entre los LMSs y los objetos de contenido compartible o SCO (Sharable
Content Object) y para la gestión de la información de seguimiento que puede ser
comunicada entre el SCO y el LMS. En el contexto de SCORM, los objetos de contenido
pueden ser: (Advanced Distributed Learning, 2009)
- SCOs, que se comunican en tiempo de ejecución, o
- Assets (recursos), que no se comunican en tiempo de ejecución.
SCORM se desarrolló para permitir el desarrollo de objetos de contenido que
fueran reutilizables e interoperables sobre múltiples sistemas LMS. Para hacer esto
posible, debe haber una forma común de gestionar objetos de contenido, un mecanismo
común para que los objetos de contenido se comuniquen con un LMS y un lenguaje
predefinido o vocabulario que formen la base de la comunicación.
El proceso de lanzamiento define una forma común para que los sistemas LMS
inicien los objetos de contenido basados en la web. El término “objeto de contenido” se
usa genéricamente para describir cualquier pieza de contenido que pueda ser ejecutada
por un alumno. En SCORM, hay dos tipos de objetos de contenido: los SCOs y los
Assets. El proceso de puesta en marcha establece los procedimientos y
responsabilidades para el establecimiento de la comunicación entre el objeto de
contenido lanzado y el LMS. El mecanismo para la comunicación ha sido estandarizado
con un API común.
28
El API es el mecanismo de comunicación para informar al LMS del estado de la
comunicación entre un objeto de contenido y el LMS (por ejemplo, inicializada, terminada
y/o en una condición de error) y se utiliza para recuperar y almacenar datos entre el LMS
y el SCO.
Un modelo de datos es un conjunto estándar de los elementos del modelo de
datos utilizado para definir la información que se registra para un SCO, tales como el estado de finalización del SCO o la puntuación de una evaluación como la de un test o un
examen. En su forma más simple, el modelo de datos define los elementos del modelo de
datos que tanto el LMS como el SCO esperan conocer. El LMS debe mantener el estado
de los elementos del modelo de datos del SCO a través de las sesiones del alumno, y el
SCO debe utilizar solamente esos elementos predefinidos del modelo de datos para
asegurar la reutilización a través de múltiples sistemas.
5.3.1 Run-Time Environment (RTE) Management
Mientras que el alumno interactúa con objetos de contenido (la experiencia de
aprendizaje), el LMS evalúa el desempeño del alumno y las solicitudes de navegación.
Cuando el LMS identifique una actividad que debe ser presentada al alumno, la
actividad tiene un objeto de contenido asociada a ella. El LMS pondrá en marcha el
objeto de contenido y se lo presentará al alumno. La figura 8 muestra cómo la estructura
del contenido (la sección de organización de un manifiesto) se puede interpretar como un
árbol. La representación en árbol es solo una manera distinta de presentar la estructura
de contenido que se encuentra en el manifiesto.
29
Figura 8: Estructura conceptual del contenido. Fuente: (Advanced Distributed Learning, 2009).
5.3.2 Application Programming Interface (API)
Todas las comunicaciones entre un SCO y el LMS utilizan un API de ECMAScript
(JavaScript) (“Run-Time SCORM,” 03/08/2016). Esta es la única forma posible para que
la comunicación se lleve a cabo. No existen otros canales de comunicación disponibles.
El contenido no puede comunicarse a través de servicios web, formularios de correo,
escritura en base de datos o cualquier otro mecanismo, sólo a través del API de
JavaScript proporcionada por el LMS.
El LMS es el responsable de proporcionar un objeto JavaScript con un nombre
específico en una ubicación específica dentro del DOM del navegador. Por tanto, el
contenido siempre localiza este API utilizando un algoritmo común.
En SCORM 1.1 y SCORM 1.2, el objeto del API siempre se denomina “API”. En
SCORM 2004 el objeto se denomina “API_1484_11”.
Una vez que un SCO encuentra el API, puede usarlo para comunicarse con el
LMS. Hay que tener en cuenta que únicamente los SCOs pueden iniciar la comunicación.
El LMS es una entidad pasiva que simplemente responde a las llamadas API realizadas
por el contenido. El LMS no puede iniciar cualquier comunicación, simplemente lanza el
contenido y responde a las solicitudes.
30
El API de SCORM contiene ocho métodos que difieren ligeramente en su nombre
en función de la versión del estándar. En la figura 9 vemos los de la versión 2004
mientras que en la 10 aparecen los de la versión 1.2 de SCORM:
Figura 9: Métodos del API SCORM 2004. Fuente:(“Run-Time SCORM,” 03/08/2016).
Figura 10: Métodos del API SCORM 1.1/SCORM 1.2. Fuente: (“Run-Time SCORM,” 03/08/2016).
Los nombres de los métodos varían ligeramente entre las versiones SCORM, pero
conceptualmente los métodos son idénticos. Initialize / LMSInitialize El método de inicialización indica al LMS que el contenido desea comenzar una
sesión de comunicación. Todos los SCOs deben llamar a Initizalize antes de realizar
cualquier tipo de comunicación. El LMS devuelve un valor booleano que indica el éxito o
fracaso de la inicialización. Por lo general, siempre devuelve verdadero.
Terminate / LMSFinish
El método Terminate le indica al LMS que la comunicación se ha realizado. Todos
los SCOs deben llamar a Terminate, por lo que no importa cómo el alumno salga del
SCO. Sin embargo, es aconsejable llamar a Terminate en el evento onunload del SCO. El
valor devuelto por el LMS normalmente indica si los datos fueron guardados en el
servidor de forma satisfactoria o no.
GetValue / LMSGetValue
El método GetValue permite al SCO recuperar datos del LMS. Los datos que se
obtienen son siempre elementos definidos en el modelo de datos SCORM. Cada uno de
31
estos elementos del modelo de datos contiene información diferente. Algunos de los
elementos del modelo de datos tienen valores inicializados por el LMS que hacen
referencia a las circunstancias bajo las que el SCO se ha lanzado. Otros valores son
inicializados por el SCO a través de llamadas al método SetValue.
SetValue / LMSSetValue
El método SetValue posibilita que el SCO pueda almacenar datos en el LMS. Los
datos siempre se almacenan en uno de los elementos del modelo de datos definidos por
SCORM. La llamada a este método, devuelve un valor booleano indicando el éxito o
fracaso de la misma.
Commit / LMSCommit
Este método señala al LMS que un chuck de datos considerable se ha guardado y
que debería asegurar que persiste correctamente. No hay requisitos para la puesta en
práctica de este método por parte del LMS.
GetLastError / LMSGetLastError
El método GetLastError comprueba si la última llamada al API SCORM lanzó un
error. Si es así, este método devuelve un código de error que corresponde con un
conjunto definido de posibles errores. La lista completa de códigos de error se encuentra
en la referencia RTE de SCORM.
GetErrorString / LMSGetErrorString
Dado un código numérico (por lo general el número del error devuelto por
GetLastError), el método GetErrorString devolverá una descripción textual de lo que
significa el código del error.
GetDiagnostic / LMSGetDiagnostic
Este método permite al LMS devolver información detallada sobre el error anterior
que puede ser útil en el diagnóstico del problema.
A continuación, la figura 11 muestra el API, una instancia de ella y su
implementación.
32
Figura 11: Ilustración del API, instancia del API y la implementación del API. Fuente: (Advanced Distributed Learning, 2009)
Los estados de la instancia del API especifican las transiciones de la instancia API
para eventos específicos. Cada uno de los estados de la instancia del API establece qué
funciones puede invocar un SCO. Los estados de la instancia del API se definen como
“no inicializado”, “en proceso” o “terminado”. El modelo de estado es un modelo
conceptual utilizado para ayudar a ilustrar el comportamiento previsto de las funciones del
API durante una sesión de comunicación típica. La figura 12 presenta las transiciones de
estados de la instancia del API.
Figura 12: Transiciones de estados de la instancia del API. Fuente: (Advanced Distributed Learning, 2009)
33
5.4 SCO
Un SCO (“SCO SCORM,” 02/08/2016.) u objeto de contenido compartible es un
objeto de aprendizaje que puede ser lanzado (recurso) que se comunica con el entorno
de tiempo de ejecución. Un SCO debe estar diseñado de manera que pueda ser
ejecutado en una ventana web independiente o en un marco dentro de una página web.
Un SCO es especial, ya que cuando se lanza para un alumno en el navegador de
este, el SCO comunicará información de vuelta al LMS que lo lanzó, normalmente un
servidor remoto. Esta comunicación permite al LMS rastrear la información relativa a la
experiencia del alumno.
Un SCO representa el nivel más bajo de granularidad de un recurso de
aprendizaje al que un LMS debe hacer un seguimiento. SCORM no impone ninguna
limitación particular sobre el tamaño de un SCO. Un SCO puede ser una sola página web
o una colección de ellas (siempre y cuando el conjunto de páginas puedan ser
consideradas una sola unidad auto-contenida).
Cada SCO debe ser reutilizable e independiente de su contexto de aprendizaje.
Para lograr dicha reutilización, un SCO debe ser autónomo y no hacer referencia a otros
SCOs.
Cualquier recurso pasivo se puede convertir en un SCO declarándolo como SCO
en el fichero del manifiesto y asegurándonos de que muestra los comportamientos
requeridos por un SCO. Los comportamientos requeridos por un SCO en tiempo de
ejecución serían:
Encontrar la instancia del API RTE proporcionada por el LMS.
Usar la instancia del API para inicializar la comunicación con el LMS.
Utilizar la instancia del API para finalizar la comunicación con el LMS.
Por su lado, los comportamientos que se recomiendan que tenga un SCO serían
los siguientes:
Un SCO debe ser reutilizable en diferentes contextos educativos.
Un SCO debería ser independiente de las limitaciones visuales, tales
como el tamaño de la ventana.
Un SCO debería transmitir de forma fiable los datos del alumno de forma
que no se pierdan en caso de cerrarse inesperadamente.
Un SCO debería comunicar su estado de finalización.
Un SCO no debería lanzar nuevas ventanas en el navegador si no las
cierra una vez se realicen.
34
Un SCO no debería tener vínculos a otros ficheros en el paquete de
contenido que no aparezcan listados como SCO en el manifiesto.
Como comportamientos restringidos de un SCO tenemos:
Un SCO no puede interactuar con el entorno de tiempo de ejecución de
otra forma que no sea la prevista por la API.
Un SCO no puede intentar cambiar el tamaño o la apariencia del RTE en
el que se ha ejecutado.
Un SCO no puede cerrar la ventana principal del navegador que fue
lanzada a menos que sea la única cosa en la ventana.
Por último vamos a comentar el ciclo de vida típico de un SCO:
1. El SCO es lanzado por un RTE SCORM (a menudo un LMS).
2. El SCO encuentra el API aportada por el RTE.
3. El SCO comienza la comunicación con la API RTE (a través de la
llamada al método Initialize()).
4. El alumno comienza a interactuar con el SCO.
5. El SCO envía y recibe datos a través de la API RTE (a través de la
llamada a los métodos Get/SetValue()).
6. El alumno termina de interaccionar con el SCO.
7. El SCO finaliza la comunicación con la API RTE (a través de la llamada al
método Terminate()).
El esquema de presentación de SCOs a los alumnos aparece en la figura 13.
Figura 13: Esquema presentación de SCOs a los alumnos. Fuente: http://eduworks.com/LOTT/Tutorial/scormconcepts.html
35
5.5 Navegadores Web
En el desarrollo y depuración de las librerías y páginas realizadas, ha sido de vital
importancia el uso de los navegadores web en general, y de su herramienta de
depuración en particular. Se ha trabajado principalmente con tres navegadores,
asegurando el correcto funcionamiento en cada uno de ellos. Estos navegadores han
sido Google Chrome, Mozilla Firefox e Internet Explorer.
5.5.1 Google Chrome
Ha sido el principal navegador con el que se ha trabajado, eso sí, asegurándonos
después de la operatividad en el resto. La versión utilizada ha sido la 51.0.2704.103 m.
La herramienta de depuración puede ser accedida a través de la tecla F12. A
continuación la figura 14 presenta una captura de la misma:
Figura 14: Herramienta para desarrolladores en Google Chrome. Fuente: Propia.
5.5.2 Mozilla Firefox
Este navegador también presenta buenas prestaciones a la hora de desarrollar
contenido HTML y usar JavaScript. La versión utilizada en este caso ha sido la 47 y en la
figura 15 podemos apreciar una imagen de su herramienta para desarrolladores:
Figura 15: Herramienta para desarrolladores en Mozilla Firefox. Fuente: Propia.
36
5.5.3 Internet Explorer
El navegador menos usado en el desarrollo y a la vez el que más trabas ha
presentado. La versión del navegador ha sido la 11.0.9600.18376 y el aspecto de su
herramienta para desarrolladores se puede ver en la figura 16.
Figura 16: Herramienta de desarrolladores en Internet Explorer. Fuente: Propia.
5.6 Funcionamiento de las librerías JavaScript para creación de tests online
Para darle a una página o SCO la posibilidad de crear tests online tenemos que
importar la librería denominada principal.js en la cabecera de la página web, es decir,
dentro de las etiquetas head del fichero HTML. Para importarla añadimos la línea que se
aprecia en la figura 17 dentro de la cabecera (anotando en el atributo src la ruta en la
que se encuentre la misma):
Figura 17: Línea para importar la librería principal.js.
En cada página web desarrollada, se han añadido seis variables JavaScript, para posibilitar modificaciones rápidas y de forma sencilla a la hora de la creación de los tests.
Las variables aparecen gráficamente en la figura 18. Estas seis variables son:
banco. Esta variable hace referencia al banco de preguntas que alberga
las cuestiones de las que se compondrá el test. En ella hay que
especificar la ruta (path) del mismo en forma de cadena de caracteres.
numeroPr. Sirve para establecer el número de preguntas de las que va a
constar el test. Habría que indicar un entero.
time. Si queremos que el test cuente con tiempo de realización debemos
darle un valor entero diferente de cero. Este entero indicaría los minutos de los que dispone el alumno para la realización del test.
37
Estableceríamos esta variable a cero si, por el contrario, no se deseara que el test disponga de un tiempo máximo para su realización.
prAleat. Variable utilizada para aleatorizar o no las cuestiones contenidas
en el banco de preguntas. Hay que establecerla bien al string si o bien al
string no.
opAleat. Variable utilizada para aleatorizar o no las opciones de las
preguntas en el caso de las preguntas de opción múltiple y respuesta
única y de las preguntas de opción múltiple y respuesta múltiple. Al igual que para la variable anterior, se debe establecer a si o no.
comunicaciones. Esta variable le permitiría al administrador elegir entre la
posibilidad de que el SCO se comunique con el LMS o por el contrario
que no lo hiciese. Del mismo modo que las variables previas, se fijaría a si o no.
Figura 18: Ejemplo de inicialización de variables JavaScript en páginas web. Fuente:
Propia.
Una vez definidas las variables anteriores, estamos en disposición de llamar a la
función definida en la librería principal.js y denominada loadJsLib. Debemos llamarla
dentro de etiquetas <script> en el documento HTML y pasarle como parámetros todas las
variables. La figura 19 muestra cómo sería esta llamada a la función.
Figura 19: Ejemplo de llamada a la función loadJsLib. Fuente: Propia.
Una vez hecho esto, la función loadJsLib se encarga, dependiendo del valor de la
variable comunicaciones, de importar una librería u otra. Además, una vez la importa,
llama a la función loadXMLDoc definida en la librería recién importada (está en ambas) y
analiza el banco de preguntas en caso de existir. La función implementada para la importación de las librerías sería loadScript,
definida en principal.js. A ella habría que pasarle el localizador de recursos uniforme
(URL, del inglés Uniform Resource Locator) y la función loadXMLDoc con sus
parámetros. En la figura 20 vemos cómo sería:
38
Figura 20: Uso de la función loadScript. Fuente: Propia.
A continuación se muestra un esquema resumiendo todo lo anterior:
SÍ NO
Se consigue de esta forma, una programación modular y escalable. Asimismo, se
produce una encapsulación haciendo posible la modificación de las otras librerías sin que
ello afecte al funcionamiento básico creado. Otra ventaja de hacerlo de este modo, sería
que sólo se le solicitaría al servidor una librería bastante liviana a la hora de cargar la
página web, y más tarde se produciría la importación de la otra más pesada. Además,
proporciona la facilidad de ofrecer la librería adecuada a partir de una variable, sin tener
que preocuparnos del directorio de la librería y su importación.
5.6.1 Análisis del banco de preguntas
Los bancos de preguntas como ya se ha dilucidado en capítulos anteriores que se
han utilizado en este TFG, están estructurados siguiendo el estándar QTI. Estos se
pueden crear en el LMS y después importarlos en XML. Algunos de los bancos aquí utilizados han sido importados de la asignatura Sistemas de Telefonía, impartida en el
Grado en Ingeniería Telemática. Otros en cambio, han sido de elaboración propia. En el
Establecer las variables
en el documento HTML
Llamar a la función
loadJsLib
¿Se va a comunicar
con el LMS?
Importar librería de
comunicaciones y llamar
a loadXMLDoc
Importar librería sin
comunicaciones y llamar
a loadXMLDoc
39
apartado donde se explican los módulos realizados, se detallará qué banco de preguntas
se ha empleado para cada SCO.
Un fichero XML conforme al estándar QTI tiene una estructura simplificada que se
puede apreciar en la figura 21:
Figura 21: Contenido minimizado de un banco de preguntas acorde con QTI. Fuente:
Propia.
Como podemos ver en la figura anterior, las etiquetas padre serían
<questestinterop> y dentro de ellas cada pregunta estaría representada por la etiqueta
<item>.
Además, cada etiqueta ítem presenta tres atributos, dos de ellos muy interesantes
a la hora de la creación de los tests online. Los dos primeros serían un identificador de la
pregunta y un título. El tercero haría referencia al número máximo de intentos, pero para la creación de los tests no se ha tenido en cuenta.
La función encargada de analizar los bancos de preguntas ha sido loadXMLDoc.
Dicha función realiza la petición del fichero XML, y si existe lo analiza. El análisis y creación de tests se realiza del siguiente modo:
1. Obtenemos el contenedor preguntas que debe existir en el fichero HTML.
Ahí será donde vayamos añadiendo las preguntas. La figura 22 muestra
este punto.
Figura 22: Obtención del div preguntas del documento HTML. Fuente: Propia.
2. En la figura 23 obtenemos las preguntas existentes en el banco.
40
Figura 23: Obtención de las preguntas del fichero XML. Fuente: Propia.
3. Después, mientras no se alcance el número de preguntas deseado se
ejecuta un bucle while que va incluyendo en el fichero las distintas
preguntas.
4. Para cada pregunta, se obtiene el título, la descripción, el tipo y el
identificador. Las figuras 24 y 25 muestran la obtención de cada uno.
Figura 24: Obtención del título y la descripción de la pregunta. Fuente: Propia.
Figura 25: Obtención del tipo y del identificador de la pregunta. Fuente: Propia.
5. A continuación se comprueba el tipo de pregunta que es, y dependiendo
de su tipo, se llama a unas funciones u otras. Los cuatro tipos de preguntas soportados en formato QTI se denominan SINGLE CHOICE
QUESTION, MULTIPLE CHOICE QUESTION, NUMERIC QUESTION y
CLOZE QUESTION.
6. Para el tipo de pregunta de opción múltiple y respuesta única (SINGLE
CHOICE QUESTION) primero se obtienen las opciones de la misma, la
puntuación de cada una de ellas y finalmente se llama a la función única,
pasándole como atributos las opciones, el identificador, los puntos y el número de pregunta. Esta vuelca su contenido la variable opciones que
un poco antes de finalizar el bucle while se añade al fichero HTML. En la
figura 26 se aprecia la obtención de las variables para este tipo de
pregunta.
41
Figura 26: Obtención de las opciones, los puntos y llamada a la función única. Fuente:
Propia.
7. La pregunta de opción múltiple y respuesta múltiple (MULTIPLE CHOICE
QUESTION) sigue el mismo método anterior para su creación. Difiere en
que para su creación en lugar de llamar a la función única llamamos a la
función múltiple. Lo comprobamos en la figura 27.
Figura 27: Obtención de las opciones, los puntos y la llamada a la función múltiple.
Fuente: Propia.
8. Para la creación de una pregunta numérica (NUMERIC QUESTION)
obtenemos los límites superior e inferior, el número máximo de
caracteres permitidos y los puntos en caso de acierto. Tras esto, llamamos a la función numerica que volcará su resultado en la variable
opciones del mismo modo que para las preguntas anteriores. Esto se
muestra en la figura 28.
Figura 28: Acciones a realizar en caso de pregunta de tipo numérica. Fuente: Propia.
9. Por último, si el tipo de pregunta es de rellenar huecos (CLOZE
QUESTION) hay que crear más variables y obtener más parámetros,
debido a la complejidad que presentan este tipo de preguntas, ya que sus
huecos pueden ser numéricos, de texto o listas desplegables. En este
42
sentido, tenemos que obtener las opciones, el número total de huecos,
las opciones en caso de listas desplegables y los puntos de cada opción.
Después de esto, se ejecuta un bucle do while para formar la pregunta.
Todo lo descrito, se presenta en la figura 29.
Figura 29: Obtención de las distintas variables para un pregunta de rellenar huecos.
Fuente: Propia.
10. Una vez se le ha dado valor a cada una de las variables dependiendo del
tipo de pregunta, se añaden al div preguntas obtenido en el paso 1. Para
añadirlas se usa el método innerHTML:
Figura 30: Método innerHTML para agregar contenido al documento HTML. Fuente:
Propia.
Un posible diagrama que representara lo explicado previamente podría ser este:
SÍ NO
Obtención de las preguntas del fichero XML
¿Se desea barajar las preguntas?
Obtención del div preguntas
Obtención de las preguntas del fichero XML
43
SÍ NO
Para la aleatorización de las preguntas, así como de las opciones, se ha utilizado el algoritmo de Fisher-Yates que podemos observar en la figura 31. A esta función, le
pasamos un array de valores, y nos devuelve el mismo array, pero con sus valores
desordenados.
¿Se ha alcanzado el número de preguntas
deseado?
Fin de la función loadXMLDoc
Obtener título, descripción, tipo e identificador de la
pregunta
Aleatorizar preguntas No aleatorizar preguntas
¿Tipo de la pregunta?
SINGLE CHOICE QUESTION
MULTIPLE CHOICE
QUESTION
CLOZE QUESTION
NUMERIC QUESTION
Obtención de variables y llamada a la función
única.
Obtención de variables y llamada a la función
multiple.
Obtención de variables y llamada a la función
numerica.
Obtención de variables y ejecución de bucle do
while.
Añadir al div preguntas
44
Figura 31: Función fisher_yates para aleatorizar un array. Fuente: Propia.
5.6.2 Evaluación de las preguntas
Una vez añadidas las preguntas, nos encontramos tres botones debajo de cada una de ellas. El primero de ellos sería el de validar y se usaría para puntuar la pregunta.
El segundo es el de borrar y lo pulsaríamos si queremos resetear los campos de la
pregunta o eliminar nuestra elección y el último sería el de posponer. Este botón tiene
sentido ya que se ha decidido mostrar las preguntas una a una, pensando en las
comunicaciones con el LMS y con el fin de evitar demoras a la hora de interaccionar con
el LMS, enviando la información de la pregunta cuando es mostrada al usuario, y no
antes. El aspecto de estos tres botones los podemos ver en la figura 32.
Figura 32: Botones bajo cada pregunta. Fuente: Propia.
Para evaluar una pregunta de opción múltiple y respuesta única, la función que se ha desarrollado se ha denominado puntuarUni a la cual debemos pasarle como
parámetros el identificador de la pregunta y el número de pregunta que es.
La función obtiene todos los elementos con ese identificador y comprueba el
elemento que se ha seleccionado. Adquiere la puntuación de la selección y la respuesta
dada para después deshabilitar las opciones así como los botones de la pregunta,
actualizar la puntuación y mostrar un mensaje al alumno y la siguiente pregunta. La actualización de la puntuación consiste en la modificación de un div pequeño
que se añade en la parte superior derecha de la página cuando se crea el test. Este div
muestra nuestra puntuación actual y el total de puntos que podemos obtener separados
por una barra. El aspecto de este div aparece en la figura 33.
45
Figura 33: Div con la puntuación del alumno. Fuente: Propia.
El mensaje que se muestra al alumno no deja de ser un alert para el que se ha
usado la librería smoke.js (“Smoke JS,” 01/07/2016). Este mensaje difiere dependiendo
de si se acierta o no la cuestión. La alerta que aparece en los tests sin comunicación se
muestra en la figura 34.
Figura 34: Mensaje que aparece al acertar una pregunta. Fuente: Propia.
Por lo que respecta a mostrar la siguiente pregunta, se ha creado una función denominada muestra a la que le pasamos el número de pregunta desde la que la
llamamos. En ella, se obtiene el div que contiene a la siguiente pregunta y lo modifica
para que sea visible.
La corrección del resto de preguntas se realizaría de manera análoga a la de las
preguntas de opción múltiple y respuesta única, con la única salvedad de la función usada para la corrección, que en cada caso difiere. Para las preguntas de opción múltiple
y respuesta múltiple se denomina puntuarMul, para las preguntas numéricas puntuarNum
y para las preguntas de rellenar huecos puntuaHuecos.
Validar pregunta de opción múltiple y
respuesta única
Llamada a la función
puntuarUni
Deshabilitar opciones y botones, obtener
puntuación, actualizarla,
mostrar mensaje y
mostrar siguiente pregunta.
46
La función puntuarNum necesita para su correcto funcionamiento además del
identificador y del número de pregunta, los valores inferior y superior obtenidos para esa
pregunta. La corrección de la pregunta dependerá de que el valor introducido por el
usuario se encuentre dentro de esos valores. La función putuaHuecos sería algo más compleja que el resto, debido a que tiene
que discernir si el hueco es de tipo decimal, string o es una lista desplegable. Un ejemplo
de pregunta de rellenar huecos se puede ver en la figura 35.
Figura 35: Ejemplo de pregunta de rellenar huecos. Fuente: Propia.
Además, si el hueco es de tipo string es necesario obtener la sensibilidad para
saber si a la hora de evaluar su corrección debemos tener en cuenta las letras mayúsculas o una cierta distancia de Levenshtein. Si la opción escrita por el usuario no
fue contemplada en la creación de la pregunta y por tanto no aparece en el banco de
preguntas, no sumará ni restará puntos. La corrección de un hueco de tipo decimal se llevaría a cabo del mismo modo que
el de una pregunta de tipo numérica y la de tipo lista desplegable sería similar a la de las
respuestas múltiples, obteniendo los puntos de la opción seleccionada.
5.6.3 Interacción de las librerías con el LMS
Todo lo comentado previamente, serviría tanto para las librerías que interaccionan
con el LMS como las que no. En este sentido, se han desarrollado dos librerías, denominadas creaPreguntas2004.js y creaPreguntas12.js que, aparte de las funciones
anteriores, realizan otras tareas relacionadas con la interacción entre alumno y LMS. A
continuación comentaremos estas otras funciones.
Pero antes de explicar esas funciones, hay que aclarar que para la interactividad añadida a los tests se ha utilizado la librería JavaScript APIWrapper.js de la ADL y la
librería RTE.js cuyo autor es Ildefonso Ruano Ruano, ambas de necesaria importación en
los documentos HTML. Un fragmento del inicio de la librería RTE.js se puede visualizar
en la figura 36.
47
Figura 36: Fragmento objeto RTE de la librería RTE.js. Fuente: RTE.js.
creaPreguntas2004.js
Después de haber obtenido las variables necesarias para poder modificar el div de
la página web con la pregunta adecuada, en caso de ser la primera pregunta que se genera, se llama a las funciones setInteractionsAddNewI y setInteractionsDescription de
la instancia del RTE que previamente ha debido ser inicializada a la versión 2004. Si no fuera la primera pregunta, estas funciones son llamadas en la función muestra.
A la función setInteractionsAddNewI se le pasan cinco parámetros para su
correcto funcionamiento. Estos parámetros serían el número de interacción que es, el identificador de la misma, el tipo de cuestión (en nuestro caso podrá ser choice, fill-in o
numeric), el peso de la pregunta (que por defecto se ha establecido a uno) y la respuesta
correcta de la pregunta.
Para la obtención de la respuesta correcta, se ha realizado una función denominada getCorrectaInv a la cual debemos pasarle el identificador de la pregunta y el
tipo de pregunta que es como parámetros. A partir del tipo y de los puntos de las
opciones, se realizan las acciones necesarias para obtener la respuesta correcta.
La respuesta correcta debemos obtenerla en el mismo formato aceptado por el
LMS para que no se produzca ningún tipo de error a la hora de almacenarla en él. Del
mismo modo, a la hora de obtener la respuesta dada por el alumno, también debemos
formatearla de la misma manera para que se pueda guardar en el LMS y se pueda
comparar con la respuesta correcta. La función setInteractionsDescription por su parte, le añade una descripción a la
interacción. En nuestro caso, como descripción le hemos pasado el título de la cuestión,
por lo que se recomienda que no haya preguntas con el mismo título en el banco de
preguntas. A esta función hay que pasarle el número de interacción y la descripción que
queremos que añada a esa interacción.
48
En la figura 37 tenemos una captura de ambas funciones en la librería creaPreguntas2004.js.
Figura 37: Uso de estas funciones en la librería creaPreguntas2004.js. Fuente: Propia.
Por lo que respecta a la corrección de las preguntas, en todas las funciones
realizadas para tal fin, se ha seguido el mismo procedimiento:
1. Primero se obtiene la respuesta correcta a través de la función getInteractionsCorrectRespPattern.
2. Después se compara la respuesta dada por el alumno con la respuesta
correcta obtenida en la consulta al LMS. A partir de esta comparación, se
determina la corrección de la interacción. Esta consulta y la posterior
comparación, aparecen plasmadas en la figura 38.
3. Una vez se determina la corrección de la interacción, se llama a la función setInteractionsResponseAll pasándole el número de interacción,
la respuesta dada por el alumno y la corrección de la interacción como
parámetros, quedando así almacenada en el LMS la información de la
interacción realizada por el alumno.
Figura 38: Uso de funciones para interacciones con el LMS en creaPreguntas2004.js.
Fuente: Propia.
4. Además, a la hora de presentarle el mensaje al alumno con su
puntuación para esa pregunta/interacción, este se modifica para que aparezca el nombre de pila del alumno, personalizando el tests aún más.
Esto se consigue gracias a la función getLearnerName y a la función split
para sólo quedarnos con la primera cadena del nombre. El uso de esta
función aparece en la figura 39.
Figura 39: Uso de la función getLearnerName en creaPreguntas2004.js. Fuente: Propia.
49
creaPreguntas12.js
Igualmente que con la librería creaPreguntas2004.js una vez que se han obtenido
las variables necesarias para poder modificar el div de la página web con la pregunta
adecuada, en caso de ser la primera pregunta que se genera, se llama a las funciones setInteractionsId, setInteractionsType, setInteractionsTs, setInteractionsWeight y
setInteractionsCorrectRespPattern de la instancia del RTE que previamente ha debido
ser inicializada a la versión 1.2. Si no fuera la primera pregunta, estas funciones son llamadas en la función muestra.
Estas funciones tienen el mismo cometido que la función setInteractionsAddNewI
utilizada en la versión 2004. Sin embargo, hay que utilizarlas por separado, dado que esta función no es soportada por la versión 1.2 de SCORM, como se aprecia en la figura
40. En este sentido, los parámetros necesarios para el uso de cada función son los
siguientes:
Para la función setInteractionsId es necesario indicarle el número de
interacción de la que se trata y el identificador de la misma.
La función setInteractionsType se ocuparía de señalar el tipo de
interacción de la que se trata. Así, tendríamos que pasarle el número de interacción y el tipo al que corresponde (choice, fill-in o numeric).
setInteractionsTs nos ayudaría a almacenar el timestamp de la
interacción. Tendríamos que pasarle como parámetros el número de
interacción y el tiempo de ella, para lo que usamos la función de la
librería RTE.js llamada timeStamp a la que basta con indicarle la versión
en la que queremos el timestamp puesto que de una versión a otra
varían.
La función setInteractionsWeight necesitaría el número de interacción y
el peso de la interacción, que por defecto hemos establecido a uno.
Por último, la función setInteractionsCorrectRespPattern nos facilitaría el
almacenamiento de la respuesta correcta en el LMS. Habría que pasarle
el número de interacción, el valor cero, y la respuesta correcta.
Figura 40: Uso de funciones para añadir una interacción al LMS en creaPreguntas12.js.
Fuente: Propia.
50
Para obtener la respuesta correcta, se ha obrado de la misma manera que en la versión de 2004, es decir, se ha utilizado la función denominada getCorrectaInv y de la
misma manera, estas respuestas deben ir bien formateadas para que el LMS no nos
rechace la operación de almacenarlas.
En cuanto a la corrección de las preguntas, la versión 1.2 no proporciona ninguna
función para recuperar del LMS la respuesta correcta de la interacción, por lo que hemos tenido que usar de nuevo la función getCorrectaInv para tal fin.
De este modo, una vez se comprueba la corrección de la pregunta, se utiliza la función setInteractionsResponseAll que tendría los mismos parámetros que su versión de
2004 para almacenar la experiencia del alumno con esa interacción. Esta función se
muestra en la figura 41.
Figura 41: Uso de la función setInteractionsResponseAll en la creaPreguntas12.js.
Fuente: Propia.
Finalmente, para mostrar el nombre de pila del alumno, se procede de una forma similar pero no igual, dado que la función getLearnerName de la versión 1.2 nos devuelve
el nombre del usuario de manera diferente, en concreto, esta nos devuelve primero los
apellidos del usuario y tras una coma, su nombre de pila. La obtención del nombre de pila
se muestra en la figura 42.
Figura 42: Uso de la función getLearnerName en la librería creaPreguntas12.js. Fuente:
Propia.
5.7 Interacciones
El modelo de datos del RTE de SCORM contiene, entre otros, una serie de datos
a los que llama interacciones. Estas interacciones definen un conjunto elementos que
sirven para representar preguntas que se presentan a un alumno en el entorno de un
SCO y las respuestas del alumno. Estos elementos pueden enviarse desde el SCO al
LMS para que guarde un registro de estas interacciones realizadas entre el alumno y el
SCO. Las interacciones pretenden ser respuestas a cuestiones o tareas individuales que
el desarrollador desea almacenar. No existe ningún comportamiento implícito que el LMS
deba tener cuando se le solicita introducir una interacción en el sistema a parte de la del
almacenamiento de los datos.
Las interacciones sirven para registrar en un LMS el trabajo que realiza un alumno
referente a las respuestas que introduce en un SCO ante cuestiones que se le han
51
planteado. La 43 muestra los datos de una interacción en la versión 2004 edición 4 de
SCORM, en la que puede haber 10 tipos distintos de interacciones: “true-false”, “choice”,
“fill-in”, “long-fill-in”, “matching”, “performance”, “sequencing”, “likert”, “numeric” y “other”.
Las interacciones pueden ser consideradas como una colección de información
(datos de la interacción) (Advanced Distributed Learning, 2009). Según se define en el estándar IEEE 1484.11.1, un LMS tiene la obligación de dar soporte (por ejemplo
almacenar) al menos a 250 conjuntos de datos de interacción. Un LMS puede elegir dar
soporte a más de 250, sin embargo, el requisito es el de dar soporte al menos a 250
conjuntos de datos de interacción.
Figura 43: Interacciones y datos de interacción. Fuente: (Advanced Distributed Learning, 2009).
Existen dos elementos de datos que deben aparecer en los datos de interacción, un identificador (cmi.interactions.n.id) y un tipo de interacción (cmi.interactions.n.type). El
elemento cmi.interactions.n.type del modelo de datos es necesario si la interacción
incluye datos relativos a la respuesta correcta o a la respuesta del alumno. Estos dos
datos son los que distinguen los datos de interacción de otros datos de interacción
encontrados en el conjunto de interacciones (considerados como los dependientes de los
datos de interacción). El identificador identifica de manera única una interacción dentro del alcance de un SCO. El tipo identifica de forma única el tipo de la interacción (true-
false, matching, etcétera).
El elemento del modelo de datos de las interacciones se puede usar por el SCO
en dos modalidades: de diario y de estado.
Un esquema de diario requiere que el SCO registre datos de interacción cada vez
que el alumno realiza alguna acción en el SCO, es decir, cada vez que datos nuevos de interacción se añaden al array de interacciones. Aplicando este esquema para el
52
almacenamiento de interacciones, la información puede reunirse para estudiar la experiencia del alumno a partir de las interacciones encontradas en el SCO.
Por ejemplo, en los informes generados se puede ver el número de veces que un
alumno ha respondido a una interacción, cuál fue la latencia de cada interacción, cuál fue
la respuesta del alumno o el resultado de su respuesta. Estos datos se pueden reunir y
utilizar para la actualización de la interacción para su uso futuro. Los LMSs pueden
proporcionar este tipo de información para la investigación y análisis independiente, sin embargo, la disponibilidad de estos datos está fuera del alcance de SCORM.
Un esquema de estados necesita que el SCO almacene datos de interacción y mantenga la interacción actualizada basándose en la experiencia del alumno con el SCO.
Por ejemplo, si el alumno responde a una interacción, la respuesta se añade. Si el
alumno después corrige su respuesta, los mismos datos de interacción deben ser modificados para reflejar el cambio (en contra de añadir una nueva entrada en el array de
interacciones). En este escenario, los datos de interacción contienen el último estado
almacenado de la interacción y por lo tanto, no existe la posibilidad de conocer cuántas
veces ha cambiado la respuesta el alumno en la interacción. Este modelo sería el
seguido por el LMS ILIAS usado por la Universidad de Jaén.
Tenemos pros y contras dependiendo de un esquema u otro. Los desarrolladores
de los SCO deben ser conscientes de ambos esquemas y usar el que deseen. Desde la perspectiva del LMS, el esquema de diario requiere más carga en cuanto a requisitos de
almacenamiento. Pero de nuevo recordamos que, el único requisito en cuanto a
almacenamiento de datos de interacción es el de almacenar al menos 250. Como con cualquier elemento del modelo de datos almacenado en arrays, la
posición del índice (n) no es lo que distingue la singularidad de los datos que se
almacenan. Los requisitos de implementación definidos en la norma, indican que los arrays deben ser implementados como una bolsa.
Esta estructura de datos permite que el mismo objeto (datos de interacción) se repita en el array (a diferencia de un conjunto que requiere que los elementos en el
conjunto sean únicos). Dependiendo del alumno y la sesión de aprendizaje, los mismos datos de interacción no se pueden almacenar en la misma posición en el array.
Todos los elementos del modelo de datos, así como sus requisitos de implementación, los requisitos en el comportamiento del LMS, los requisitos en el
comportamiento del SCO y los requisitos de implementación del API para la versión 2004
de SCORM que es más completa, se pueden consultar en (Advanced Distributed
Learning, 2009).
53
5.8 Módulos SCORM desarrollados
En este apartado, vamos a explicar los dos módulos SCORM desarrollados. En
primer lugar se desarrolló el módulo para la versión 2004 edición 4 y posteriormente,
eliminando las funcionalidades no soportadas por la versión 1.2 del estándar, se
desarrolló el segundo. Dada la naturaleza de estos módulos, que necesitan comprimirse
en formato ZIP para su uso y cuya entrega debía ser en este formato según la propuesta
de este TFG, se han entregado como anexos a esta memoria. Durante el desarrollo, se
han ido probando en la web de la Universidad de Jaén de espacios virtuales https://ev.ujaen.es/ en un espacio reservado para dicho fin. La versión de ILIAS utilizada
ha sido la v5.1.7. En la figura 44 se presenta el aspecto del acceso a la plataforma.
Figura 44: Acceso a espacios virtuales de la Universidad de Jaén. Fuente: ev.ujaen.es.
5.8.1 Módulo SCORM 2004
Este módulo consta de cinco páginas o SCOs y muestra los distintos usos que se
le pueden dar a las librerías realizadas.
En este sentido, al iniciar la primera página, se llevan a cabo varias acciones. Si
es la primera vez que accedemos al módulo, este nos da la bienvenida a través de una
alerta personalizada mientras que si no lo es, nos avisa del número de veces que hemos
accedido, nos muestra nuestra calificación para ese SCO tras consultarlo con el LMS y
nos pregunta si queremos continuar en él o por el contrario continuar hacia el siguiente SCO. La página en sí, nos explica brevemente cómo usar la librería
creaPreguntas2004.js y cómo sería su incorporación en el fichero HTML. Además, en su
parte final se da la posibilidad de realizar un pequeño test sobre el contenido de la
página. Este test tiene las siguientes características:
Presenta dos preguntas.
No existe tiempo de realización.
54
No se comunica con el LMS.
Preguntas aleatorizadas.
Opciones en caso de tener aleatorizadas también.
Al final de la página, también se muestra un botón para pasar a la página 2 del
módulo. Una vez se pulsa, se llevan a cabo las siguientes acciones:
Si la puntuación obtenida en el test es la mitad o superior a la máxima
posible se da como aprobado, y como tal, se envía al LMS la calificación de aprobado (passed). En caso contrario, se daría la evaluación como
suspensa (failed).
Se establecen los objetivos de la página, mediante un identificador, la
puntuación obtenida, el mínimo y el máximo posible, y su estado, bien
completo o bien incompleto. Además, se incluye una descripción de la
página.
Se envía al LMS la puntuación obtenida por el alumno, así como la
mínima y la máxima posible.
Por último, cierra la sesión y nos presenta la página dos.
En la segunda página del módulo, se ha incluido información relacionada con este TFG como por ejemplo los objetivos principales así como algunos secundarios, los cuales
se pueden ver en la figura 45. También se ha incluido una breve introducción a las
tecnologías utilizadas en él y de nuevo, se ha incluido la posibilidad de realizar antes de pasar a la página tres un pequeño test con las mismas características que el presentado
en la página uno. Una vez se pulsa el botón de ir a la página tres, dependiendo de la puntuación obtenida en el test se da por superado o no el SCO, se cierra la sesión y se
pasa a la página tres.
Figura 45: Objetivos secundarios que aparecen en la página dos del módulo. Fuente:
Propia.
En la tercera página, se le muestra al alumno un test de diez preguntas tomadas
de un banco que contiene un total de doce, muchas de ellas utilizadas en los tests de
evaluación de la asignatura Sistemas de Telefonía. En este test, como en los anteriores,
55
las preguntas aparecen de forma aleatoria al igual que sus opciones, tampoco dispone de
tiempo de ejecución pero sí que interacciona con el LMS.
Cada vez que aparece una nueva pregunta, esta queda registrada como una nueva interacción en el LMS. Las respuestas del alumno así como su evaluación,
también son enviadas al sistema de aprendizaje. En el momento en el que decidamos dar por terminado el test, es decir, pulsemos el botón de Terminar Test, aparecerá un cuadro
resumen bajo las preguntas, con toda la información de nuestra actuación en el SCO. El
aspecto de este cuadro resumen se muestra en la figura 46. Bajo este cuadro, aparecerá
un botón para avanzar hacia la página cuatro.
Figura 46: Cuadro resumen de la página 3. Fuente: Propia.
Es en la página cuatro en la que se ha incluido un vídeo explicativo de cómo
descargar un banco de preguntas previamente hecho en el LMS y de cómo realizando pequeños cambios en el fichero HTML, se da la posibilidad de la creación de un test
SCORM. El aspecto inicial del vídeo aparece en la figura 47. Tras el vídeo, tenemos un
botón para ir a la página cinco, a la que hemos titulado como de autoevaluación. Una vez
lo pulsamos, se da por completado este SCO, finaliza la sesión y nos presenta la página
cinco. Comentar también, que, al igual que en el resto de páginas, si no es la primera vez
que accedemos a la página, nos presenta un mensaje avisándonoslo y mostrándonos
nuestros valores obtenidos en la evaluación de esta página.
56
Figura 47: Vídeo incluido en la página 4 del módulo. Fuente: Propia.
Finalmente, la página cinco sería una página de autoevaluación. Y es que en ella, se ha hecho un formulario con el que podemos crear un test a nuestra medida. Dicho
formulario aparece en la figura 48. En él, se solicitan los seis parámetros necesarios para su creación. Una vez rellenados, pulsamos el botón de crear test para que bajo este
formulario aparezca nuestro test con las características deseadas. Tras su creación, bajo
el formulario aparece el test y a su conclusión un nuevo botón para ver nuestro
desempeño en el test. Tras todo esto, ya estaríamos en disposición de terminar el módulo
SCORM.
Al terminar se da por completado el SCO y dependiendo de la puntuación
obtenida, habremos aprobado o suspendido el mismo. Después de esto, saldríamos de la
sesión y finalizaríamos el módulo.
57
Figura 48: Formulario de la página 5 del módulo. Fuente: Propia.
5.8.2 Módulo SCORM 1.2
Al igual que el módulo para la versión 2004, este módulo consta de cinco páginas
o SCOs y muestra los distintos usos que se le pueden dar a las librerías realizadas.
A diferencia del módulo anterior, en esta versión no se pueden establecer caminos
de aprendizaje, por lo que la navegación es necesario realizarla a través del árbol de
navegación que proporciona el LMS para tal fin. La figura 49 muestra el aspecto del árbol
de navegación en el LMS para la versión 1.2 de SCORM.
Figura 49: Árbol de navegación en el LMS para la versión 1.2 de SCORM. Fuente: Ilias.
Del mismo modo, los elementos para la evaluación son más limitados en esta
versión, por lo que los SCOs de este módulo van a ser lo más similares posible a los
anteriores con la salvedad de la ausencia de los elementos y funciones de los que carece
esta versión. También el debugger, que se aprecia en la figura 50, es más simple.
Figura 50: Debugger del LMS para la versión 1.2 de SCORM. Fuente: Ilias.
58
5.9 Manual de usuario y mantenimiento
Seguidamente vamos a pasar a explicar cómo utilizar estas herramientas para
aprovechar todas sus posibilidades.
En primer lugar, es necesario obtener el banco de preguntas sobre el tema que
queremos evaluar/nos en un fichero XML que siga la norma QTI. Para tal fin, lo más
sencillo sería crear un banco de preguntas en el LMS y posteriormente exportarlo en formato QTI. La creación del fichero de exportación se puede ver en la figura 51.
Figura 51: Creación de archivo de exportación QTI. Fuente: Ilias.
Una vez descargado el fichero, lo descomprimimos y nos aparecerá una carpeta llamada objects normalmente vacía y dos ficheros XML, como bien muestra la figura 52.
La única diferencia en el nombre entre ambos es que en uno aparecen las siglas qpl y en
otro qti. El fichero en cuyo nombre aparecen las siglas qti contiene toda la información
referente a las preguntas del test, mientras que en el fichero con las siglas qpl aparece
información relacionada con el banco de preguntas y la plataforma virtual. El que
usaremos nosotros para pasárselo como parámetro a nuestra función es el que contiene las siglas qti en su nombre.
Figura 52: Ficheros que aparecen al descomprimir el archivo descargado. Fuente: Propia.
En cuanto a la integración con nuestro fichero HTML, lo primero que tendríamos
que hacer sería incluir en la cabecera (etiquetas head) la librería principal.js con la ayuda
de las etiquetas script. Además, si deseamos interaccionar con el LMS tampoco deben
faltar las librerías denominadas RTE.js y APIWrapper.js. En nuestro caso también hemos
añadido la librería smoke.js que se puede descargar de http://smoke-js.com/ para darle
estilo a las alertas, pero esa sería opcional, eso sí, si no se incluye habría que modificar
los ficheros para no utilizar las funciones de esta librería.
59
También habría que añadir las hojas de estilo que fueran necesarias, en nuestro caso han sido dos, una propia y la otra para ser usada por la librería smoke.js. La figura
53 muestra todas las librerías importadas así como hojas de estilo para la página 1 del módulo SCORM además de un favicon para la página.
Figura 53: Cabecera del documento HTML. Fuente: Propia.
Después tendríamos que darle valores a las variables que utiliza la función de creación de tests en el fichero HTML. En este sentido, tendríamos que indicar la ruta del
banco de preguntas, el número de preguntas del test, su tiempo de realización, si
deseamos aleatorizar las preguntas así como sus opciones en caso de tenerlas y si se va
a comunicar con el LMS o no. Esto se ve de forma gráfica en la figura 54.
Figura 54: Inicialización de variables para creación de tests. Fuente: Propia.
Antes de llamar a la función loadJsLib hay que comentar que esta busca una
tabla con el identificador tabla en el documento HTML en la que ir añadiendo las distintas
preguntas del test, por lo que para su correcto funcionamiento esta debe aparecer en el
fichero. Llegados a este punto, estamos en disposición de llamar a la función loadJsLib la
cual importará la librería adecuada en función del valor de la variable comunicaciones y
añadirá las preguntas a la tabla. La llamada a la función se muestra en la figura 55.
Figura 55: Llamada a la función LoadJsLib. Fuente: Propia.
Si se desea añadir algún botón de finalización del test para realizar alguna acción
o mostrar cierta información, este debe añadirse al fichero HTML puesto que la función
no añade ningún tipo de botón final por sí sola. Se da así la posibilidad de personalizar
60
aún más el test generado. En este caso, la figura 56 muestra el botón de terminación del
test de la página 3 del módulo SCORM.
Figura 56: Botón de terminación de test en página 3 del módulo SCORM. Fuente: Propia.
Cuando la función nos presente las preguntas, bajo cada una de ellas, podemos
observar tres botones cuyo aspecto se puede ver en la figura 57, uno para validar la
pregunta y mostrar la siguiente, otro para borrar nuestra elección y un último para pasar a
la siguiente pregunta si no deseamos responder o no conocemos la respuesta a la
pregunta actual.
Figura 57: Botones bajo cada pregunta. Fuente: Propia.
Además, durante la realización del mismo, podremos ver en la esquina superior
derecha nuestra puntuación total junto con la máxima posible. Esta puntuación sería la utilizada para dar como aprobado o suspenso un test y por extensión el SCO.
Por lo que respecta al estilo y presentación de los tests, esta puede verse y/o
modificarse en las dos hojas de estilo desarrolladas. Para modificar el estilo, no
tendríamos más que localizar el elemento que queremos modificar en el fichero CSS y
cambiar el atributo deseado. Por ejemplo, la figura 58 muestra el estilo aplicado a la etiqueta h3 en estilo.css. A las hojas de estilo creadas se las ha denominado
estiloIndex.css y estilo.css. Ambas se han añadido en el apartado Anexos.
Figura 58: Estilo para el elemento h3 en estilo.css. Fuente: Propia.
Cada función contenida en las librerías incluye en su parte superior una
descripción de la misma, por lo que si queremos modificarla o deseamos tener una
funcionalidad que no está incluida en ninguna de ellas no tenemos más que añadirla en la
librería.
61
Si vamos a utilizar la librería de creación de tests para autoevaluación es
necesario acceder a la página web a través de un servidor local dado que los
navegadores por temas de seguridad sólo abren páginas que sigan los esquemas de los estándares conocidos. En nuestro caso, para probar los test de autoevaluación hemos
usado el servidor Apache contenido en el paquete XAMPP, que aparece en la figura 59.
Figura 59: Panel de control de XAMPP. Fuente: XAMPP.
Para el mantenimiento de las mismas, se recomienda utilizar las herramientas
para desarrolladores que proporciona cada navegador. Ante cualquier error, lo mejor
sería utilizar estas herramientas y con la ayuda de puntos de ruptura seguir el código e ir
viendo el valor que van tomando las distintas variables y ver cómo responde y si realiza lo
que deseamos o no. La figura 60 muestra una captura de la herramienta para
desarrolladores de Google Chrome en la que se ha añadido un punto de ruptura para
analizar el código. La función aquí analizada es la de puntuaHuecos. Justo al lado de su definición,
aparecen los valores que toman los parámetros recibidos por la función. En la parte derecha de la captura se observan tres pestañas, una llamada Watch, otra Call Stack y la
tercera Scope. Es en la pestaña Call Stack donde se aprecia que el punto de ruptura
está en la línea 955 de la librería creaPreguntas.js.
Por último, en la pestaña Scope es en la que aparecen todas las variables de la
función y sus valores. Como está pausado al inicio de la función, todas las variables
excepto las pasadas como parámetros están indefinidas.
62
Figura 60: Ejemplo de punto de ruptura en la herramienta para desarrolladores de
Google Chrome. Fuente: Google Chrome.
63
El resultado del TFG actual ha sido, por un lado, la obtención de cuatro librerías
JavaScript, una para interaccionar con la versión 1.2 de SCORM, otra para interaccionar
con la versión 2004 edición 4 del estándar, una tercera para autoevaluación y una última
de apoyo que ayuda a importar la librería adecuada (bien la de comunicaciones o bien la
de sin comunicaciones con el LMS) que se ha realizado con vistas a ser escalable y
hacer posible la inclusión de más estándares haciendo las mínimas modificaciones
posibles. Las cuatro librerías, se pueden ver en el apartado Anexos.
Por otro lado, se han desarrollado dos módulos SCORM, uno para la versión 1.2
del estándar y otro para la versión 2004 edición 4, en los que se han incluido estas
librerías con la finalidad de demostrar su uso.
A continuación, se va a proceder a la realización del módulo SCORM 2004 y a la
visualización de la información que se puede obtener por parte del administrador o
profesor desde el LMS, una vez terminado. En este sentido, la figura 61 presenta una
captura parcial de la página 1 del módulo.
Figura 61: Captura parcial de la página 1 del módulo SCORM 2004. Fuente: Propia.
En esta página se da la posibilidad de realizar un test que evalúa la comprensión
de la explicación dada en ella sobre cómo usar la librería creaPreguntas2004.js. Además,
se añade como objetivo de la página en el LMS la correcta comprensión de la página
inicial, el cual puede verse en la figura 62 y que se evalúa dependiendo de la puntuación obtenida en la realización del test. También se evalúa a partir de esta puntuación el
elemento successStatus del modelo de datos, que por defecto se inicializa a suspenso.
64
Eso sí, una vez se pulsa el botón para pasar a la página 2, se establece a completado el elemento CompletionStatus del modelo de datos que por defecto se inicializa a
incompleto y se fija la puntuación de la página.
Figura 62: Informe de objetivos para la página 1 del módulo. Fuente: Ilias.
La página 2, que aparece en parte en la figura 63, sigue el mismo guion de la
página 1, en este caso explica brevemente los objetivos y las tecnologías involucradas en este TFG así como el test de la página 3 y por último presenta otro pequeño test con
cuestiones relativas a lo comentado anteriormente.
Figura 63: Captura parcial de la página 2 del módulo SCORM 2004. Fuente: Propia.
Los elementos SuccessStatus y CompletionStatus se evalúan del mismo modo
que en la página 1, es decir, dependiendo de la puntuación obtenida en la realización del test. Al pulsar el botón de ir a página 3, se establece la puntuación del SCO, que va
acorde a la obtenida en el test también.
Al iniciar la página 3, por defecto los elementos SuccessStatus y
CompletionStatus se inicializan a suspenso e incompleto respectivamente. Después
presenta un total de 10 preguntas, que aparecen de una en una. Conforme van
apareciendo, se va añadiendo la interacción en el LMS. En ella, se especifica el número
65
de interacción, su identificador, su tipo, el peso, la respuesta correcta y una descripción
de la misma.
Cuando el alumno responde, se añade la respuesta dada y si esta es correcta o no. Además, al finalizar el test, aparece una tabla al pulsar el botón de Terminar Test con
el nombre del alumno, el tiempo empleado en la realización del test y su desempeño en
el mismo. De la misma manera que en las páginas anteriores, el valor de SuccessStatus
dependerá de si la puntuación obtenida por el alumno es igual o mayor a la máxima
posible, así como la puntuación del SCO.
La página 4 muestra un vídeo sobre cómo exportar un banco de preguntas desde el LMS e incluirlo en un fichero HTML para la creación de test con la ayuda de las
variables JavaScript que en él aparecen. También se ve en el vídeo el test que se crea.
Un fragmento de esta página aparece en la figura 64. Al pulsar el botón de Ir a
autoevaluación y antes de pasar a la página 5, se establece el elemento
CompletionStatus a completado, ya que se entiende que el alumno ha visto el vídeo y
SuccessStatus a aprobado, sin necesidad de ninguna puntuación en este caso.
Figura 64: Captura parcial de la página 4 del módulo SCORM 2004. Fuente: Propia.
Por último, la página 5 es una página de autoevaluación. En ella aparece un
cuadro, el cual se muestra en la figura 65 y en el que se puede elegir todos los parámetros necesarios para la creación de un test. Este test según la opción elegida, se
comunicará o no con el LMS.
Si se elige la librería creaPreguntas2004.js el funcionamiento sería el mismo que
el comentado para la página 3, es decir, añadiendo cada interacción en el sistema con la
aparición de cada pregunta y más tarde incluyendo las respuestas dadas por el alumno.
66
Si por el contrario se elige la librería creaPreguntasSinCom.js las respuestas
dadas por el alumno así como las latencias de cada pregunta, serían administradas
localmente por la librería, ya que esta, no se comunica con el LMS.
Figura 65: Captura parcial de la página 5 del módulo SCORM 2004. Fuente: Propia.
La puntuación que se obtenga en este test será la puntuación del SCO y se
utilizará para dar por aprobado o suspenso el mismo. Cuando el alumno termine el test
puede ver su desempeño en un cuadro resumen que aparece bajo el test y tras ello
pulsar el botón de Finalizar módulo para establecer a completado CompletionStatus y
salir del módulo.
Al finalizar un alumno el módulo, es posible ver información sobre su desempeño en Ilias, en el apartado Datos de Seguimiento de la configuración del módulo. Así por
ejemplo, si aplicamos el filtro de Informe básico de éxito nos aparece la información
contenida en la figura 66:
Figura 66: Informe básico de éxito. Fuente: Ilias.
En este informe, podemos ver el título del módulo, el nombre del usuario que lo ha
realizado, el estado de superación, el número de intentos que ha hecho, el número de
SCOs completados y el número de SCOs superados. Además, de esta información, se
67
podría ver también el identificador del módulo, la versión del módulo, el tiempo total en
segundos o el último acceso realizado entre otros.
Si ahora aplicamos el filtro de informe básico de capítulos (SCO) nos sale el
cuadro que aparece en la figura 67:
Figura 67: Informe básico de capítulos (SCO). Fuente: Ilias.
Este informe nos muestra el título del capítulo, el nombre del usuario, la
puntuación obtenida en cada uno, el tiempo empleado en la última sesión en segundos,
el estado de éxito del SCO y la fecha del último acceso. Aparte de estas columnas, al
igual que en el informe anterior, se podrían mostrar algunas más sobre la actuación del
alumno en el módulo, pero que en este caso, no se han seleccionado. Otro filtro aplicable dentro del apartado de Datos de seguimiento sería el de
informe básico de interacciones, el cual muestra todas las interacciones que se han
almacenado en el LMS durante el módulo. En este caso, todas se produjeron durante la
realización de la página 3.
En la figura 68 podemos ver 10 interacciones, cada una con su título, un
identificador de usuario, su identificador, una descripción que se corresponde con el título
de la pregunta, el tipo de interacción de la que se trata, el resultado de la respuesta dada
por el alumno y la respuesta que dio el alumno. Otras columnas como el título del módulo
de aprendizaje, su identificador, el email del alumno o su departamento se han obviado
por considerarse poco interesantes para la captura.
68
Figura 68: Informe básico de interacciones. Fuente: Propia.
El último informe que se puede consultar sería el de Objetivos globales del
sistema (usado en la secuenciación). La figura 69 muestra este informe, en el que
podemos observar de nuevo el título del módulo de aprendizaje, el nombre del usuario, el
estado total del módulo y el estado de éxito.
Figura 69: Informe de objetivos globales del sistema. Fuente: Ilias.
Todos los informes anteriores, formarían parte de los informes por usuario, pero
también podemos obtener los informes por capítulo. En este sentido, para un mismo
capítulo, podemos obtener los siguientes informes:
Informe básico de capítulos (SCO).
Informe básico de interacciones.
Informe de objetivos.
Evaluación por interacción.
Evaluación por usuario.
Respuestas de los usuarios.
A continuación, la figura 70 muestra una captura del informe de evaluación por
usuario para la página 3 del módulo:
69
Figura 70: Informe de evaluación por usuario para la página 3 del módulo. Fuente: Ilias.
En esta captura, se aprecia el título del SCO, el nombre del usuario, las
interacciones contestadas correctamente así como el porcentaje que representan dentro
del SCO, las interacciones contestadas incorrectamente y su porcentaje y la puntuación
obtenida.
El informe de evaluación por interacción también es bastante interesante. La
figura 71 muestra un posible aspecto del mismo:
Figura 71: Informe de evaluación por interacción para la página 5. Fuente: Ilias.
Igualmente, en la pestaña Progreso de aprendizaje de la configuración del
módulo, también es posible ver un cuadro con información sobre el alumno, sus accesos
al módulo o su estado entre otros. La figura 72 presenta esta información.
Figura 72: Progreso de aprendizaje del módulo. Fuente: Ilias.
Echando un simple vistazo rápido al cuadro, podemos ver por ejemplo si el alumno ha accedido o aún no al módulo, como sería el caso de Fernando. También
70
puede verse si lo ha superado o no, el tiempo que ha empleado y las fechas del primer y
del último acceso. Estas y otras columnas son configurables por parte del administrador
del espacio, por lo que se pueden quitar o añadir al gusto.
71
Como se ha explicado en el comienzo del capítulo anterior, el resultado del
presente TFG ha sido la creación de cuatro librerías JavaScript, una para cada versión
del estándar SCORM, una para autoevaluación y una última de apoyo para la creación de tests. Asimismo, también se han desarrollado dos módulos basados en la estructura de
contenidos SCORM para hacer una demostración de sus posibles usos y se han
integrado de manera exitosa en el LMS de la Universidad de Jaén.
De este modo, se han obtenido una serie de herramientas útiles para su uso en el
ámbito de la enseñanza online y aptas para cualquier tema de evaluación de cualquier
asignatura. Además, se han desarrollado de acuerdo con estándares, como lo son
SCORM y QTI, lo que asegura su interoperabilidad y portabilidad a través de los distintos
LMSs del mercado.
Este trabajo, además, permite realizar una enseñanza personalizada en el alumno
y ofrecer la posibilidad de modificar hasta seis parámetros del mismo. Finalmente, se ha
dado soporte hasta a cuatro tipos de preguntas diferentes que han sido las siguientes:
Opción múltiple respuesta única.
Opción múltiple respuesta múltiple.
Numérica.
De rellenar huecos.
Ha sido así, como se han cumplido todos los objetivos marcados al inicio del TFG
y además, se han proporcionado algunas funcionalidades extra que no estaban
contempladas en la propuesta del TFG, como la posibilidad de utilizar librerías para
autoevaluación y la obtención de las preguntas a partir de un fichero XML que cumpla el
estándar QTI.
Asimismo, gracias al desarrollo realizado durante la elaboración de este TFG, el
autor del mismo ha desarrollado una serie de competencias y habilidades:
- Evolución de los conocimientos sobre la programación en JavaScript.
- Evolución de los conocimientos sobre la programación en HTML.
- Progreso en el uso de las hojas de estilo para dar estilo a las páginas
web.
- Aprendizaje y uso de módulos con contenidos basados en SCORM.
- Estudio y uso de ficheros XML acordes a la norma QTI.
- Aprendizaje y uso de las herramientas para desarrolladores en tres
navegadores distintos para resolver errores de programación.
- Aprendizaje de términos como LMS o SCO.
72
- Aprendizaje y uso de funciones para comunicarse con el LMS.
- Desarrollo de la capacidad de transformar indicaciones y sugerencias en
acciones.
- Mejora de la capacidad de solucionar problemas de distinto grado de
dificultad.
- Mejora de la capacidad de trabajo.
- Mejora de la capacidad de organización.
- Mejora de la capacidad de sacrificio.
- Realización personal.
En cuanto a las líneas de futuro se podrían añadir elementos gráficos a los tipos
de preguntas ya soportados o por otro lado, dar soporte a más tipos de preguntas (de
ordenación, de unir parejas, de mapa de imagen…).
Además, se podría usar la secuenciación de SCORM en conjunción con los
resultados que obtiene el alumno para personalizar su experiencia a lo largo del módulo
así como los SCOs que se le ofrecen.
Para finalizar, se debería tener en cuenta la posibilidad de ampliar este trabajo
haciéndolo compatible con otros estándares de e-learning. Al hacer este trabajo
escalable, sería cuestión de estudiar los nuevos estándares y realizar una librería para
cada norma. Una vez hecho esto, simplemente habría que modificar la librería de apoyo principal.js para añadirlas y darles soporte.
Entre los nuevos estándares de e-learning vamos a destacar los siguientes: LTI (Learning Tools Interoperability), Experience API o xAPI y cmi5 (“Versiones SCORM,”
12/08/2016.).
LTI se utiliza principalmente en sistemas LMSs de centros educativos. Se trata de una arquitectura plug-in que permite a los LMSs trabajar con herramientas de aprendizaje
remotas.
Con fecha de lanzamiento el 26 de abril de 2013, Experience API es la nueva
versión de SCORM y resuelve muchos de los problemas que eran inherentes a versiones
anteriores de SCORM. El aprendizaje móvil, aprendizaje en equipo, la eliminación de la
necesidad de un navegador web o simulaciones de juegos son solo algunos aspectos
que ahora son relativamente fáciles de conseguir.
cmi5 sería el último en aparecer, ya que lo hizo el 1 de junio de 2016. Es una
especificación complementaria a xAPI. Proporciona un conjunto de normas destinadas a
lograr la interoperabilidad en un entorno tradicional de LMS, y utiliza a xAPI como
protocolo de comunicación y formato de datos. cmi5 incluye el concepto de una sesión de
73
aprendizaje y tiene normas específicas para la captura de un conjunto básico de datos
para las experiencias de aprendizaje.
cmi5 está listo para su adopción, pero aún está muy limitado. Eso sí, si se tiene un
sistema de aprendizaje LMS y se desea flexibilidad y la conveniencia de datos a largo
plazo de xAPI, se debe considerar su adopción.
74
Es en este capítulo en el que se han incluido el índice de figuras y la relación de
siglas utilizadas a lo largo de este TFG. Además, también se han incorporado las cuatro
librerías JavaScript implementadas así como las dos hojas de estilo utilizadas.
8.1 Índice de figuras Figura 1: Estructura básica documento HTML.
Fuente:http://www.hipertexto.info/documentos/html.htm ........................................................ 11
Figura 2: Árbol de objetos HTML DOM. Fuente: http://www.w3schools.com/js/js_htmldom.asp . 13
Figura 3: Gráfico estándares. Fuente:
http://www.cyta.com.ar/elearn/editor_digital/curso_archivos/m4_a2_lectura/xml_0.htm ......... 14
Figura 4: Ejemplo fichero XML. Fuente:
http://social.technet.microsoft.com/wiki/contents/articles/27147.windows-phone-how-to-
create-a-data-class-from-an-xml-document.aspx ......................................................................... 15
Figura 5: Esquema de la estructura básica adoptada por el QTI IMS. Fuente: (Tobergte & Curtis,
2013). .......................................................................................................................................... 17
Figura 6: Casos de uso del sistema de exámenes. Fuente: (Tobergte & Curtis, 2013). ................... 17
Figura 7: Icono de Notepad++. Fuente: http://www.transitionblog.com/alternatives-to-notepad/
.................................................................................................................................................... 27
Figura 8: Estructura conceptual del contenido. Fuente: (Advanced Distributed Learning, 2009). .. 29
Figura 9: Métodos del API SCORM 2004. Fuente:(“Run-Time SCORM,” 03/08/2016). ................... 30
Figura 10: Métodos del API SCORM 1.1/SCORM 1.2. Fuente: (“Run-Time SCORM,” 03/08/2016). 30
Figura 11: Ilustración del API, instancia del API y la implementación del API. Fuente: (Advanced
Distributed Learning, 2009) .......................................................................................................... 32
Figura 12: Transiciones de estados de la instancia del API. Fuente: (Advanced Distributed Learning,
2009) ........................................................................................................................................... 32
Figura 13: Esquema presentación de SCOs a los alumnos. Fuente:
http://eduworks.com/LOTT/Tutorial/scormconcepts.html ........................................................... 34
Figura 14: Herramienta para desarrolladores en Google Chrome. Fuente: Propia. ........................ 35
Figura 15: Herramienta para desarrolladores en Mozilla Firefox. Fuente: Propia. ......................... 35
Figura 16: Herramienta de desarrolladores en Internet Explorer. Fuente: Propia. ......................... 36
Figura 17: Línea para importar la librería principal.js. ................................................................... 36
Figura 18: Ejemplo de inicialización de variables JavaScript en páginas web. Fuente: Propia......... 37
Figura 19: Ejemplo de llamada a la función loadJsLib. Fuente: Propia. .......................................... 37
Figura 20: Uso de la función loadScript. Fuente: Propia. ............................................................... 38
Figura 21: Contenido minimizado de un banco de preguntas acorde con QTI. Fuente: Propia....... 39
Figura 22: Obtención del div preguntas del documento HTML. Fuente: Propia. ............................ 39
Figura 23: Obtención de las preguntas del fichero XML. Fuente: Propia........................................ 40
Figura 24: Obtención del título y la descripción de la pregunta. Fuente: Propia. ........................... 40
Figura 25: Obtención del tipo y del identificador de la pregunta. Fuente: Propia. ......................... 40
Figura 26: Obtención de las opciones, los puntos y llamada a la función única. Fuente: Propia. .... 41
Figura 27: Obtención de las opciones, los puntos y la llamada a la función múltiple. Fuente: Propia.
.................................................................................................................................................... 41
Figura 28: Acciones a realizar en caso de pregunta de tipo numérica. Fuente: Propia. .................. 41
Figura 29: Obtención de las distintas variables para un pregunta de rellenar huecos. Fuente:
Propia. ......................................................................................................................................... 42
75
Figura 30: Método innerHTML para agregar contenido al documento HTML. Fuente: Propia. ...... 42
Figura 31: Función fisher_yates para aleatorizar un array. Fuente: Propia. ................................... 44
Figura 32: Botones bajo cada pregunta. Fuente: Propia. ............................................................... 44
Figura 33: Div con la puntuación del alumno. Fuente: Propia. ...................................................... 45
Figura 34: Mensaje que aparece al acertar una pregunta. Fuente: Propia. .................................... 45
Figura 35: Ejemplo de pregunta de rellenar huecos. Fuente: Propia. ............................................ 46
Figura 36: Fragmento objeto RTE de la librería RTE.js. Fuente: RTE.js. .......................................... 47
Figura 37: Uso de estas funciones en la librería creaPreguntas2004.js. Fuente: Propia. ................ 48
Figura 38: Uso de funciones para interacciones con el LMS en creaPreguntas2004.js. Fuente:
Propia. ......................................................................................................................................... 48
Figura 39: Uso de la función getLearnerName en creaPreguntas2004.js. Fuente: Propia. ............. 48
Figura 40: Uso de funciones para añadir una interacción al LMS en creaPreguntas12.js. Fuente:
Propia. ......................................................................................................................................... 49
Figura 41: Uso de la función setInteractionsResponseAll en la creaPreguntas12.js. Fuente: Propia.
.................................................................................................................................................... 50
Figura 42: Uso de la función getLearnerName en la librería creaPreguntas12.js. Fuente: Propia. .. 50
Figura 43: Interacciones y datos de interacción. Fuente: (Advanced Distributed Learning, 2009). . 51
Figura 44: Acceso a espacios virtuales de la Universidad de Jaén. Fuente: ev.ujaen.es.................. 53
Figura 45: Objetivos secundarios que aparecen en la página dos del módulo. Fuente: Propia. ...... 54
Figura 46: Cuadro resumen de la página 3. Fuente: Propia. .......................................................... 55
Figura 47: Vídeo incluido en la página 4 del módulo. Fuente: Propia. ........................................... 56
Figura 48: Formulario de la página 5 del módulo. Fuente: Propia. ................................................ 57
Figura 49: Árbol de navegación en el LMS para la versión 1.2 de SCORM. Fuente: Ilias. ................ 57
Figura 50: Debugger del LMS para la versión 1.2 de SCORM. Fuente: Ilias. ................................... 57
Figura 51: Creación de archivo de exportación QTI. Fuente: Ilias. ................................................. 58
Figura 52: Ficheros que aparecen al descomprimir el archivo descargado. Fuente: Propia. ........... 58
Figura 53: Cabecera del documento HTML. Fuente: Propia. ......................................................... 59
Figura 54: Inicialización de variables para creación de tests. Fuente: Propia. ................................ 59
Figura 55: Llamada a la función LoadJsLib. Fuente: Propia. ........................................................... 59
Figura 56: Botón de terminación de test en página 3 del módulo SCORM. Fuente: Propia. ........... 60
Figura 57: Botones bajo cada pregunta. Fuente: Propia. ............................................................... 60
Figura 58: Estilo para el elemento h3 en estilo.css. Fuente: Propia. .............................................. 60
Figura 59: Panel de control de XAMPP. Fuente: XAMPP. .............................................................. 61
Figura 60: Ejemplo de punto de ruptura en la herramienta para desarrolladores de Google
Chrome. Fuente: Google Chrome. ................................................................................................ 62
Figura 61: Captura parcial de la página 1 del módulo SCORM 2004. Fuente: Propia. ..................... 63
Figura 62: Informe de objetivos para la página 1 del módulo. Fuente: Ilias. .................................. 64
Figura 63: Captura parcial de la página 2 del módulo SCORM 2004. Fuente: Propia. ..................... 64
Figura 64: Captura parcial de la página 4 del módulo SCORM 2004. Fuente: Propia. ..................... 65
Figura 65: Captura parcial de la página 5 del módulo SCORM 2004. Fuente: Propia. ..................... 66
Figura 66: Informe básico de éxito. Fuente: Ilias. .......................................................................... 66
Figura 67: Informe básico de capítulos (SCO). Fuente: Ilias. .......................................................... 67
Figura 68: Informe básico de interacciones. Fuente: Propia. ......................................................... 68
Figura 69: Informe de objetivos globales del sistema. Fuente: Ilias. .............................................. 68
Figura 70: Informe de evaluación por usuario para la página 3 del módulo. Fuente: Ilias. ............. 69
Figura 71: Informe de evaluación por interacción para la página 5. Fuente: Ilias. .......................... 69
Figura 72: Progreso de aprendizaje del módulo. Fuente: Ilias. ...................................................... 69
76
8.2 Siglas
ADL Advanced Distributed Learning
API Application Programming Interface
CERN Conseil Européen pour la Recherche Nucléaire
CSS Cascading Style Sheets
DOM Document Object Model
ECMA European Computer Manufacturers Association
HTML Hypertext Markup Language
IDE Integrated Development Environment
IEEE Institute of Electrical and Electronics Engineers
LMS Learning Management System
LTI Learning Tools Interoperability
PIF Package Interchange Format
QTI Question & Test Interoperability
RTE Run-Time Environment
SCO Sharable Content Object
SCORM Sharable Content Object Reference Model
SGA Sistema de Gestión de Aprendizaje
SGML Standard Generalized Markup Language
TFG Trabajo Fin de Grado
TIC Tecnologías de la Información y Comunicaciones
URL Uniform Resource Locator
W3C World Wide Web Consortium
WWW World Wide Web
XML eXtensible Markup Language
8.3 Librería JavaScript creaPreguntas2004.js
/* creaPreguntas2004.js JavaScript library by Sergio Díaz
Librería desarrollada por Sergio Díaz Fuentes. Se permite cualquier
explotación de la obra, incluyendo una finalidad comercial, así
como la creación de obras derivadas, la distribución de las cuales
también está permitida sin ninguna restricción.
*/
var puntosPreguntas = new Array ();
var puntuacion = 0;
var minimo = 0;
var total = 0;
77
var longitudPreg = 0;
var auxiliarCont = 0;
var contador = 0;
var auxiliarResp = ["a","b","c","d","e","f","g","h","i","j"];
var miTiempo="";
rellenaPuntos();
/*********************************************************************
**********
**
** Function: loadXMLDoc()
** Inputs: The name of the xml file in which the questions are, the
number of questions of the test, time for the test, random questions,
random options and type of comunication.
** Return: The number of requested test questions presented to the
user with the chosen features.
**
** Description:
** Requests the xml file and presents the questions.
**
**********************************************************************
*********/
function loadXMLDoc (xmlName,numPreguntas,time,aleaPr, aleaOp) {
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
// code for older browsers
xmlhttp = new ActiveXObject("Microsoft.XMLDOM");
}
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var xmlDoc = xmlhttp.responseXML;
if (xmlDoc != null){
ponDiv();
ponPuntuacion();
if (parseInt(time) > 0){
if (time.toString().length < 2)
var t = "0" + time + ":00";
else
var t = time + ":00";
ponTime(t);
}
if (time != 0)
miTiempo = setInterval( function(){cuentAtras()},
1000);
// En la variable div_preguntas obtenemos el contenedor
div con el id 'preguntas'
var div_preguntas =
document.getElementById('preguntas');
// Obtenemos la lista de preguntas
var preguntas_set =
xmlDoc.getElementsByTagName("questestinterop")[0].getElementsByTagName
("item");
longitudPreg = preguntas_set.length;
78
// Comprobamos que el número de preguntas sea igual o
inferior al número de preguntas disponibles
if (numPreguntas > preguntas_set.length){
numeroPr = preguntas_set.length;
}
//Mezclamos un array auxiliar que nos servirá para
obtener las preguntas de forma aleatoria
if (aleaPr.localeCompare("si") == 0)
var arrayaux = arrayAlea(preguntas_set);
else
var arrayaux = obtenArray(preguntas_set.length);
while (auxiliarCont < numeroPr){
// Obtenemos el título y la descripción de la pregunta
var titulo =
preguntas_set[arrayaux[auxiliarCont]].getAttribute("title");
var
descripcion=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName
("mattext")[0].childNodes[0].nodeValue;
if (descripcion.indexOf("<p>") == -1)
descripcion = "<p>"+descripcion+"</p>";
//Obtenemos el tipo de la pregunta
var
tipo=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("qtime
tadatafield")[1].getElementsByTagName("fieldentry")[0].childNodes[0].n
odeValue;
//Obtenemos el identificador de la pregunta
var
id=preguntas_set[arrayaux[auxiliarCont]].getAttribute("ident");
//Inicializamos un par de flags auxiliares a cero
var flag=0;
var flagE = 0;
var patt = "";
//Inicializamos las opciones de la pregunta a un string
vacío
var opciones="";
if(tipo=="SINGLE CHOICE QUESTION"){
// Obtenemos las opciones de la pregunta
var opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
//Obtenemos los puntos de cada opcion
var puntos =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("setvar");
opciones +=
unica(opciones_set,id,puntos,contador);
total += sumaPuntos(puntos, tipo, 0);
minimo += restaPuntos(puntos, tipo,
0);
flagE = 1;
} else
if(tipo=="NUMERIC QUESTION"){
79
//Obtenemos los límites
superior e inferior para la pregunta
var sup =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("varlte")[0
].childNodes[0].nodeValue;
var inf =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("vargte")[0
].childNodes[0].nodeValue;
//Obtenemos el numero máximo de
caracteres permitidos
var maxcar =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("render_fib
")[0].getAttribute("maxchars");
//Obtenemos los puntos en caso
de acierto
var puntos =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("setvar")[0
].childNodes[0].nodeValue;
opciones +=
numerica(id,sup,inf,puntos,maxcar,contador);
total += parseFloat(puntos);
flagE = 1;
} else
if(tipo=="MULTIPLE CHOICE
QUESTION"){
// Obtenemos las
opciones de la pregunta
var opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
//Obtenemos los
puntos de cada opcion
var puntos =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("setvar");
opciones +=
multiple(opciones_set,id,puntos,contador);
//patt =
getCorrectaMul (opciones_set,puntos);
total +=
sumaPuntos(puntos, tipo, 0);
minimo +=
restaPuntos(puntos, tipo, 0);
flagE = 1;
} else
if(tipo=="CLOZE
QUESTION"){
var
opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
var
huecosString =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("response_s
tr");
var huecosNum
=
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("response_n
um");
var oposibles
=
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("varequal")
; //Opciones posibles
80
var puntos =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("setvar");
//Puntos de las distintas opciones
var nHuecos =
huecosString.length + huecosNum.length; //Número total de huecos
var tagNot =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("not");
var arr =
[].slice.call(oposibles);
var arrMat =
[].slice.call(opciones_set);
if
(tagNot.length != 0){
var osobrantes
= tagNot[0].getElementsByTagName("varequal").length;
arr.length =
oposibles.length - osobrantes * 2;
arrMat.length
= opciones_set.length - osobrantes;
}
var gapsT = 0;
var gapsN = 0;
descripcion="";
total +=
sumaPuntos(puntos,tipo, oposibles);
minimo +=
restaPuntos(puntos, tipo, oposibles);
var w = 0;
do{
if
(arrMat[w].childNodes[0].nodeValue.indexOf("<p>") != -1)
descripcion += arrMat[w].childNodes[0].nodeValue; //La primera
nunca será un hueco
else{
if (w==0)
descripcion += "<p>" +
arrMat[w].childNodes[0].nodeValue.replace(/<br \/>/g,
'').replace(/<\/?span[^>]*>/g,"");
else if
(w == arrMat.length - nHuecos - 1)
descripcion += arrMat[w].childNodes[0].nodeValue.replace(/<br
\/>/g, '').replace(/<\/?span[^>]*>/g,"") +"</p>";
else
descripcion += arrMat[w].childNodes[0].nodeValue.replace(/<br
\/>/g, '').replace(/<\/?span[^>]*>/g,"");
}
if
(gapsT + gapsN < nHuecos){
if (huecosString.length != 0 && gapsT < huecosString.length){
81
if
(huecosString[gapsT].getAttribute("ident").indexOf(gapsT + gapsN) != -
1){
if
(huecosString[gapsT].getElementsByTagName("render_choice").length ==
1){
descripcion +=
creaDesplegable(id,arr,puntos,gapsT + gapsN);
w = w +
huecosString[gapsT].getElementsByTagName("render_choice")[0].getElemen
tsByTagName("mattext").length;
}
else{
descripcion+="<input type=\"text\"
name=\""+id+"\" class=\"huecoT\"
tipo=\""+huecosString[gapsT].getElementsByTagName("render_fib")[0].get
Attribute("fibtype")+"\"
sensibilidad=\""+preguntas_set[arrayaux[auxiliarCont]].getElementsByTa
gName("fieldentry")[5].childNodes[0].nodeValue+"\" puntos=\"\"
sol=\"\"
size=\""+huecosString[gapsT].getElementsByTagName("render_fib")[0].get
Attribute("columns")+"\"
maxlength=\""+parseInt(huecosString[gapsT].getElementsByTagName("rende
r_fib")[0].getAttribute("columns"))+"\"/>";
}
gapsT++;
w++;
} else if
(huecosNum[gapsN].getAttribute("ident").indexOf(gapsT + gapsN) != -1){
var min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
innumber");
if (min == null)
min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
axnumber");
descripcion += "<input type=\"text\"
name=\""+id+"\" class=\"huecoT\"
tipo=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAtt
ribute("fibtype")+"\" inf=\""+min+"\"
sup=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttr
ibute("maxnumber")+"\" puntos=\"\" size=\"3\"
maxlength=\""+parseInt(huecosNum[gapsN].getElementsByTagName("render_f
ib")[0].getAttribute("columns"))+"\"/>";
82
gapsN++;
w++;
}
}
else if (huecosNum[gapsN].getAttribute("ident").indexOf(gapsT +
gapsN) != -1){
var min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
innumber");
if (min == null)
min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
axnumber");
descripcion += "<input type=\"text\" name=\""+id+"\"
class=\"huecoT\"
tipo=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAtt
ribute("fibtype")+"\" inf=\""+min+"\"
sup=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttr
ibute("maxnumber")+"\" puntos=\"\" size=\"3\"
maxlength=\""+parseInt(huecosNum[gapsN].getElementsByTagName("render_f
ib")[0].getAttribute("columns"))+"\"/>";
gapsN++;
w++;
}
}
else
w++;
} while
(w < arrMat.length - nHuecos);
if (contador + 1 !=
numeroPr && auxiliarCont + 1 < preguntas_set.length)
descripcion+="<input
name=\"valida"+contador+"\" type=\"button\" value=\"Validar\"
onclick=\"puntuaHuecos("+id+","+contador+")\" class=\"boton\"/><input
name=\"borra"+contador+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/><input name=\"posponer"+contador+"\" type=\"button\"
value=\"Posponer\" class=\"boton\"
onclick=\"muestra("+contador+")\"/></form>";
else
descripcion+="<input
name=\"valida"+contador+"\" type=\"button\" value=\"Validar\"
onclick=\"puntuaHuecos("+id+","+contador+")\" class=\"boton\"/><input
name=\"borra"+contador+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/></form>";
83
// Modificamos el
contenido html del contenedor div
if (contador==0)
div_preguntas.innerHTML += "<div id=\"div"+contador+"\"
style=\"display:block;\"><form id=\"formulario"+contador+"\"
tipo=\""+tipo+"\"><h3>" + parseInt(contador+1) + ". " + titulo +
"</h3>" + descripcion +"</form></div>";
else
div_preguntas.innerHTML += "<div id=\"div"+contador+"\"
style=\"display:none;\"><form id=\"formulario"+contador+"\"
tipo=\""+tipo+"\"><h3>" + parseInt(contador+1) + ". " + titulo +
"</h3>" + descripcion + "</form></div>";
for (var q = 0; q <
nHuecos; q++){
for (var k =
0; k < oposibles.length; k++){
if
(oposibles[k].getAttribute("respident").indexOf(q) != -1 &&
document.getElementsByName(id)[q].getAttribute("tipo")){
if
(document.getElementsByName(id)[q].getAttribute("tipo").localeCompare(
"String") == 0){
var attAnterior =
document.getElementsByName(id)[q].getAttribute("sol");
var puntAnterior =
document.getElementsByName(id)[q].getAttribute("puntos");
document.getElementsByName(id)[q].setAttribute("sol",attAnterior+op
osibles[k].childNodes[0].nodeValue+",");
document.getElementsByName(id)[q].setAttribute("puntos",puntAnterio
r + puntos[k].childNodes[0].nodeValue+",");
}
else if
(document.getElementsByName(id)[q].getAttribute("tipo").localeCompare(
"Decimal") == 0)
document.getElementsByName(id)[q].setAttribute("puntos",
puntos[k].childNodes[0].nodeValue);
}
}
}
if (contador ==0){
patt =
getCorrectaInv(id,tipo);
miRTE.setInteractionsAddNewI(contador,id,"fill-
in",1,patt.toLowerCase());
miRTE.setInteractionsDescription(contador, titulo);
84
}
flag = 1;
//Este flag nos permite que no se repita la pregunta de nuevo en el
div_preguntas
flagE = 1;
}
// Modificamos el contenido html del contenedor div
if(flag == 0 && flagE == 1){
if (contador == 0){
div_preguntas.innerHTML += "<div
id=\"div"+contador+"\" style=\"display:block;\"><form
id=\"formulario"+contador+"\" tipo=\""+tipo+"\"><h3>" +
parseInt(contador+1) + ". " + titulo + "</h3>" + descripcion +
opciones+"</form></div>";
patt = getCorrectaInv(id,tipo);
switch (tipo){
case "SINGLE CHOICE QUESTION":
miRTE.setInteractionsAddNewI(contador,id,"choice",1, patt);
miRTE.setInteractionsDescription(contador,titulo);
break;
case "MULTIPLE CHOICE QUESTION":
miRTE.setInteractionsAddNewI(contador,id,"choice",1, patt);
miRTE.setInteractionsDescription(contador,titulo);
break;
case "NUMERIC QUESTION":
miRTE.setInteractionsAddNewI(contador,id,"numeric",1, patt);
miRTE.setInteractionsDescription(contador,titulo);
break;
default:
console.log("No es un tipo
válido de pregunta");
}
} else
div_preguntas.innerHTML += "<div
id=\"div"+contador+"\" style=\"display:none;\"><form
id=\"formulario"+contador+"\" tipo=\""+tipo+"\"><h3>" +
parseInt(contador+1) + ". " + titulo + "</h3>" + descripcion +
opciones+"</form></div>";
}
if(flagE == 1){
auxiliarCont++;
contador++;
} else{
auxiliarCont++;
}
if (auxiliarCont >= preguntas_set.length)
break;
}
85
actualiza(puntuacion,total);
}
}
};
xmlhttp.open("GET", xmlName, true);
xmlhttp.send();
}
/*********************************************************************
**********
**
** Function: ponDiv()
** Inputs: None.
** Return: None.
**
** Description:
** It adds a div tag to the html page.
**
**********************************************************************
*********/
function ponDiv(){
var midiv = document.createElement("div");
midiv.setAttribute("id","preguntas");
midiv.setAttribute("class","desplazado");
document.getElementById('tabla').appendChild(midiv);
}
/*********************************************************************
**********
**
** Function: ponPuntuacion()
** Inputs: None.
** Return: None.
**
** Description:
** It adds a puntuation div tag to the html page.
**
**********************************************************************
*********/
function ponPuntuacion(){
var midiv = document.createElement("div");
midiv.setAttribute("id","divPuntuacion");
midiv.setAttribute("class","cuadroFijo");
var mipar = document.createElement("p");
mipar.setAttribute("align","center");
var t = document.createTextNode("Puntuación");
mipar.appendChild(t);
var centr = document.createElement("center");
var cuadro = document.createElement("input");
cuadro.setAttribute("type","text");
cuadro.setAttribute("value","");
cuadro.setAttribute("id","tupuntuacion");
cuadro.setAttribute("size","3");
cuadro.setAttribute("maxlength","3");
cuadro.setAttribute("readonly","true");
cuadro.setAttribute("class","altura puntos");
centr.appendChild(cuadro);
midiv.appendChild(mipar);
midiv.appendChild(centr);
document.body.appendChild(midiv);
86
}
/*********************************************************************
**********
**
** Function: ponTime()
** Inputs: The time in minutes of test performance.
** Return: None.
**
** Description:
** It adds a time div tag to the html page.
**
**********************************************************************
*********/
function ponTime(time){
var midiv = document.createElement("div");
midiv.setAttribute("id","divTiempo");
midiv.setAttribute("class","cuadroFijoT");
var para = document.createElement("p");
para.setAttribute("align","center");
var t = document.createTextNode("Tiempo");
para.appendChild(t);
var centrado = document.createElement("center");
var cuadro = document.createElement("input");
cuadro.setAttribute("type","text");
cuadro.setAttribute("id","tutiempo");
cuadro.setAttribute("value",time); cuadro.setAttribute("size","3");
cuadro.setAttribute("class","altura puntos");
cuadro.setAttribute("readonly","true");
cuadro.setAttribute("maxlength","3");
centrado.appendChild(cuadro);
midiv.appendChild(para);
midiv.appendChild(centrado);
document.body.appendChild(midiv);
}
/*********************************************************************
**********
**
** Function: cuentAtras()
** Inputs: None.
** Return: None.
**
** Description:
** It decrements the clock.
**
**********************************************************************
*********/
function cuentAtras(){
var cad = document.getElementById("tutiempo").value;
var minutos = parseInt(cad.substring(0,2));
var segundos = parseInt(cad.substring(3,cad.length));
segundos--;
if (segundos < 0){
minutos--;
segundos = 60 + segundos;
}
if (minutos < 0){
clearInterval(miTiempo);
87
deshabilitar();
smoke.alert("Lo sentimos "+miRTE.getLearnerName().split("
")[0]+". Se ha acabado tu tiempo para responder");
} else{
if (minutos.toString().length < 2)
minutos = "0" + minutos;
if (segundos.toString().length < 2)
segundos = "0" + segundos;
document.getElementById("tutiempo").value = minutos
+":"+segundos;
}
}
/*********************************************************************
**********
**
** Function: arrayAlea()
** Inputs: An array of questions.
** Return: An array with unordered values depending of the length of
de questions array.
**
** Description:
** It mixes a vector of sorted values.
**
**********************************************************************
*********/
function arrayAlea(array){
var unArray=[];
for(var z=0;z<array.length;z++){
unArray[z]=z;
}
fisher_yates(unArray);
return unArray;
}
/*********************************************************************
**********
**
** Function: obtenArray()
** Inputs: An integer.
** Return: An array of ordered values with variable length depending
of the integer.
**
** Description:
** It creates an ordered array.
**
**********************************************************************
*********/
function obtenArray(entero){
var unArray=[];
for(var z=0;z<entero;z++){
unArray[z]=z;
}
return unArray;
}
/*********************************************************************
**********
**
** Function: arrayAleaEnt()
88
** Inputs: An integer.
** Return: An array with unordered values with variable length
depending of the integer.
**
** Description:
** It creates an unordered array.
**
**********************************************************************
*********/
function arrayAleaEnt(entero){
var unArray=[];
for(var z=0;z<entero;z++){
unArray[z]=z;
}
fisher_yates(unArray);
return unArray;
}
/*********************************************************************
**********
**
** Function: rellenaPuntos()
** Inputs: None.
** Return: None.
**
** Description:
** It initializes the points vector called puntosPreguntas.
**
**********************************************************************
*********/
function rellenaPuntos(){
for (var j=0; j < numeroPr; j++){
puntosPreguntas[j] = 0;
}
}
/*********************************************************************
**********
**
** Function: fisher_yates()
** Inputs: An array of sorted values.
** Return: The function returns the untidy vector.
**
** Description:
** It mixes a vector of sorted values according to Fisher-Yates
algorithm.
**
**********************************************************************
*********/
function fisher_yates(array){
var i=array.length;
while(i--){
var j=Math.floor( Math.random() * (i+1) );
var tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
}
/*********************************************************************
**********
89
**
** Function: sumaPuntos()
** Inputs: An array with the points of a question, the type of the
question and the number of gaps.
** Return: The maximum points for this question.
**
** Description:
** It calculates the maximum number of points for this question..
**
**********************************************************************
*********/
function sumaPuntos(pts,tipo,ngaps){
var suma= 0;
var cont = 0;
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
var arr = [];
for(var z=0;z<pts.length;z++){
arr[z] = parseFloat(pts[z].childNodes[0].nodeValue);
}
suma+=Math.max.apply(null, arr);
} else if (tipo.localeCompare("CLOZE QUESTION") == 0){
suma += getPuntos(pts,ngaps);
} else {
for(var z=0;z<pts.length;z++){
if (parseFloat(pts[z].childNodes[0].nodeValue) > 0)
suma+=parseFloat(pts[z].childNodes[0].nodeValue);
}
}
return suma;
}
/*********************************************************************
**********
**
** Function: restaPuntos()
** Inputs: An array with the points of a question, the type of the
question and the number of gaps.
** Return: The minimum points for this question.
**
** Description:
** It calculates the minimumm number of points for this question..
**
**********************************************************************
*********/
function restaPuntos(pts,tipo,ngaps){
var resta= 0;
var cont = 0;
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
var arr = [];
for(var z=0;z<pts.length;z++){
arr[z] = parseFloat(pts[z].childNodes[0].nodeValue);
}
resta+=Math.min.apply(null, arr);
} else if (tipo.localeCompare("CLOZE QUESTION") == 0){
resta += getPuntosResta(pts,ngaps);
} else {
for(var z=0;z<pts.length;z++){
if (parseFloat(pts[z].childNodes[0].nodeValue) < 0)
resta+=parseFloat(pts[z].childNodes[0].nodeValue);
}
}
90
return resta;
}
/*********************************************************************
**********
**
** Function: getPuntos()
** Inputs: An array with the points of a question and the number of
gaps.
** Return: The maximum points for this question.
**
** Description:
** It calculates the maximum number of points for this cloze question.
**
**********************************************************************
*********/
function getPuntos(pts,ngaps){
cont = 0;
cont2 = 0;
z=0;
suma = 0;
var arr = [];
do{
if (ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) > 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
else if (ngaps[z].getAttribute("respident").indexOf(cont) ==
-1){
suma+=Math.max.apply(null, arr);
arr = [];
cont++;
cont2 = 0;
if
(ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) > 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
}
z++;
if (z == pts.length)
suma+=Math.max.apply(null, arr);
} while (z < pts.length);
return suma;
}
/*********************************************************************
**********
**
** Function: getPuntosResta()
** Inputs: An array with the points of a question and the number of
gaps.
** Return: The minimum points for this question.
**
** Description:
** It calculates the minimum number of points for this cloze question.
**
91
**********************************************************************
*********/
function getPuntosResta(pts,ngaps){
cont = 0;
cont2 = 0;
z=0;
resta = 0;
var arr = [];
do{
if (ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) < 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
else if (ngaps[z].getAttribute("respident").indexOf(cont) ==
-1){
if (arr.length != 0)
resta+=Math.min.apply(null, arr);
arr = [];
cont++;
cont2 = 0;
if
(ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) < 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
}
z++;
if (z == pts.length){
if(arr.length != 0)
resta+=Math.min.apply(null, arr);
}
} while (z < pts.length);
return resta;
}
/*********************************************************************
**********
**
** Function: creaDesplegable()
** Inputs: The identifier, the options of the cloze question, the
points of the question and an integer that represents the order of the
question.
** Return: A drop-down list
**
** Description:
** It creates a drop-down list gap.
**
**********************************************************************
*********/
function creaDesplegable (ident, opcs, pts, ent){
if (opAleat.localeCompare("si") == 0)
var unArray = arrayAleaEnt(opcs.length);
else
var unArray = obtenArray (opcs.length);
var lista = "<select name=\""+ident+"\" class=\"desplegable\"
tipo=\"Lista\">";
for (j = 0; j < opcs.length; j++){
92
if (opcs[unArray[j]].getAttribute("respident").indexOf(ent)
!= -1)
lista += "<option
value=\""+opcs[unArray[j]].childNodes[0].nodeValue+"\"
puntos=\""+pts[unArray[j]].childNodes[0].nodeValue+"\"
>"+opcs[unArray[j]].childNodes[0].nodeValue +"</option>";
}
lista += "</select>";
return lista;
}
/*********************************************************************
**********
**
** Function: getCorrecta()
** Inputs: An array with the options of the question and another
array with the points of each option.
** Return: The correct answer of a multiple choice question and only
answer.
**
** Description:
** It obtains the correct answer of a multiple choice question and
only answer.
**
**********************************************************************
*********/
function getCorrecta(opciones_tag, pts){
var corr = "";
var arr = [];
for(var z=0;z<pts.length;z++){
arr[z] = parseFloat(pts[z].childNodes[0].nodeValue);
}
corr =
normalize(opciones_tag[arr.indexOf(Math.max.apply(null,
arr))+1].childNodes[0].nodeValue);
if (corr.replace(/\s/g,"_").length < 66)
corr = corr.replace(/\s/g,"_");
else
corr =
auxiliarResp[arr.indexOf(Math.max.apply(null, arr),0)];
return corr;
}
/*********************************************************************
**********
**
** Function: getCorrectaMul()
** Inputs: An array with the options of the question and another
array with the points of each option.
** Return: The correct answers of a multiple choice question and
multiple answer.
**
** Description:
** It obtains the correct answer of a multiple choice question and
multiple answer.
**
**********************************************************************
*********/
93
function getCorrectaMul(opciones_tag, pts){
var corr = "";
var corr2 = "";
var count = 0;
for(var z=0;z < pts.length; z = z + 2){
if (parseFloat(pts[z].childNodes[0].nodeValue) > 0){
if (count == 0){
corr =
normalize(opciones_tag[z/2+1].childNodes[0].nodeValue);
corr2 = auxiliarResp[z/2];
} else {
corr =
corr.concat("[,]",normalize(opciones_tag[z/2+1].childNodes[0].nodeValu
e));
corr2 = corr2.concat("[,]",auxiliarResp[z/2]);
}
count++;
}
}
if (corr.replace(/\s/g,"_").length < 66)
return corr.replace(/\s/g,"_");
else
return corr2;
}
/*********************************************************************
**********
**
** Function: unica()
** Inputs: An array of options, an identifier, an array wiht the
options points and an integer.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a multiple choice
question and only answer.
**
**********************************************************************
*********/
function unica(opciones_tag,ident,pts,ent){
if (opAleat.localeCompare("si") == 0)
var vector = arrayAleaEnt(pts.length);
else
var vector = obtenArray(pts.length);
var opciones="<form name=\"formulario\">";
for (var j = 0; j < pts.length; j++){
if
(opciones_tag[vector[j]+1].childNodes[0].nodeValue.indexOf("<p>"
) != -1)
opciones += "<p class=\opciones\><input type=\"radio\"
name=\""+ident+"\"
value=\""+opciones_tag[vector[j]+1].childNodes[0].nodeValue+"\"puntos=
\""+pts[vector[j]].childNodes[0].nodeValue+"\"/>"+opciones_tag[vector[
j]+1].childNodes[0].nodeValue+"</p>";
else{
var cadena =
opciones_tag[vector[j]+1].childNodes[0].nodeValue.replace("<p>",'');
cadena = cadena.replace("</p>",'');
opciones += "<p class=\opciones\><input type=\"radio\"
name=\""+ident+"\"
94
value=\""+cadena+"\"puntos=\""+pts[vector[j]].childNodes[0].nodeValue+
"\"/>"+cadena+"</p>";
}
}
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarUni("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/><input name=\"posponer"+ent+"\" type=\"button\"
value=\"Posponer\" class=\"boton\" onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarUni("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/>";
return opciones;
}
/*********************************************************************
**********
**
** Function: numerica()
** Inputs: An identifier, two floats, an array with the options
points and two integers.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a numeric question.
**
**********************************************************************
*********/
function numerica(iden,superior,inferior,pts,maxcar,ent){
var opciones="<form name=\"formulario\">";
opciones += "<p class=\"opciones\">Escribe el valor: <input
type=\"text\" name=\""+iden+"\" inf=\""+inferior+"\"
sup=\""+superior+"\" puntos=\""+pts+"\" size=\"3\"
maxlength=\""+parseInt(maxcar)+"\"/></p></br>";
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarNum("+iden+","+ent+","+inferior+","+superior+")\"
class=\"boton\"/><input name=\"borra"+ent+"\" type=\"reset\"
value=\"Borrar\" class=\"boton\"/><input name=\"posponer"+ent+"\"
type=\"button\" value=\"Posponer\" class=\"boton\"
onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarNum("+iden+","+ent+","+inferior+","+superior+")\"
class=\"boton\"/><input name=\"borra"+ent+"\" type=\"reset\"
value=\"Borrar\" class=\"boton\"/>";
return opciones;
}
/*********************************************************************
**********
**
** Function: multiple()
95
** Inputs: An array of options, an identifier, an array wiht the
options points and an integer.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a multiple choice
question and multiple answer.
**
**********************************************************************
*********/
function multiple(opciones_tag,ident,pts,ent){
//Vectores que contendran los puntos de las opciones
var respondidas=[];
var norespondidas=[];
var j=0;
for(var z = 0; z < pts.length; z=z+2){
respondidas[j]=pts[z].childNodes[0].nodeValue;
norespondidas[j]=pts[z+1].childNodes[0].nodeValue;
j++;
}
if (opAleat.localeCompare("si") == 0)
var vector=arrayAleaEnt(pts.length/2);
else
var vector=obtenArray(pts.length/2);
var opciones="<form name=\"formulario\">";
for (var j = 0; j < pts.length/2; j++){
if
(opciones_tag[vector[j]+1].childNodes[0].nodeValue.indexOf("<p>"
) != -1)
opciones += "<p class=\"opciones\"><input
type=\"checkbox\" name=\""+ident+"\"
value=\""+opciones_tag[vector[j]+1].childNodes[0].nodeValue+"\"
respondida=\""+respondidas[vector[j]]+"\"
norespondida=\""+norespondidas[vector[j]]+"\"/>"+opciones_tag[vector[j
]+1].childNodes[0].nodeValue+"</p>";
else{
var cadena =
opciones_tag[vector[j]+1].childNodes[0].nodeValue.replace("<p>",'');
cadena = cadena.replace("</p>",'');
opciones += "<p class=\"opciones\"><input
type=\"checkbox\" name=\""+ident+"\" value=\""+cadena+"\"
respondida=\""+respondidas[vector[j]]+"\"
norespondida=\""+norespondidas[vector[j]]+"\"/>"+cadena+"</p>";
}
}
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarMul("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/><input name=\"posponer"+ent+"\" type=\"button\"
value=\"Posponer\" class=\"boton\" onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarMul("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/>";
return opciones;
}
96
/*********************************************************************
**********
**
** Function: puntuarUni()
** Inputs: An identifier and an integer.
** Return: None.
**
** Description:
** It gets the score of a multiple choice question and only answer.
**
**********************************************************************
*********/
function puntuarUni(iden, ent){
var contestada = "";
var contestadaAux = "";
var res = "";
var puntos = 0;
var corr= miRTE.getInteractionsCorrectRespPattern(ent,0);
var opt = document.getElementsByName(iden[0].name);
for(var i=0; i<opt.length;i++){
if(opt[i].checked){
puntuacion+=
parseFloat(opt[i].getAttribute("puntos"));
puntos =
parseFloat(opt[i].getAttribute("puntos"));
contestada = normalize(opt[i].value);
contestadaAux = auxiliarResp[i];
}
opt[i].disabled = true;
}
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled
= true;
if (contestada.replace(/\s/g,"_").length <= 66){
if (corr.localeCompare(contestada.replace(/[^a-
zA-Z 0-9.]+/g,' ').replace(/\s/g,"_")) == 0)
res="correct";
else
res="incorrect";
miRTE.setInteractionsResponseAll(ent,contestada.replace(/[^a-zA-Z
0-9.]+/g,' ').replace(/\s/g,"_"),res);
} else{
if
(corr.localeCompare(contestadaAux.replace(/[^a-zA-Z 0-9.]+/g,'
').replace(/\s/g,"_")) == 0)
res="correct";
else
res="incorrect";
miRTE.setInteractionsResponseAll(ent,contestadaAux.replace(/[^a-zA-
Z 0-9.]+/g,' ').replace(/\s/g,"_"),res);
97
}
//Actualizamos la puntuación y mostramos la siguiente
pregunta
puntuacion = Math.round(puntuacion * 100) / 100;
actualiza(puntuacion, total);
if (puntos > 0)
smoke.alert("¡Enhorabuena
"+miRTE.getLearnerName().split(" ")[0]+"! Has conseguido "+puntos+ "
puntos en esta pregunta. Sigue así.");
else
smoke.alert("¡No te desanimes
"+miRTE.getLearnerName().split(" ")[0]+"! ¡A por las siguientes
preguntas!");
muestra(ent);
puntosPreguntas[ent] = puntos;
}
/*********************************************************************
**********
**
** Function: puntuarMul()
** Inputs: An identifier and an integer.
** Return: None.
**
** Description:
** It gets the score of a multiple choice question and multiple
answer.
**
**********************************************************************
*********/
function puntuarMul(iden, ent){
var opt=document.getElementsByName(iden[0].name);
var corr= miRTE.getInteractionsCorrectRespPattern(ent,0);
var contestada = "";
var contestadaAux = "";
var res = "";
var puntos = 0;
var count = 0;
for(var i=0; i<opt.length;i++){
if(opt[i].checked){
puntuacion +=
parseFloat(opt[i].getAttribute("respondida"));
puntos +=
parseFloat(opt[i].getAttribute("respondida"));
if (count == 0){
contestada =
normalize(opt[i].value).replace(/[^a-zA-Z 0-9.]+/g,' ');
contestadaAux = auxiliarResp[i];
} else {
contestada =
contestada.concat("[,]"+normalize(opt[i].value).replace(/[^a-zA-Z 0-
9.]+/g,' '));
contestadaAux =
contestadaAux.concat("[,]"+auxiliarResp[i]);
}
count++;
}
else {
98
puntuacion +=
parseFloat(opt[i].getAttribute("norespondida"));
puntos +=
parseFloat(opt[i].getAttribute("norespondida"));
}
opt[i].disabled = true;
}
if (contestada.replace(/\s/g,"_").length <= 66){
if
(corr.localeCompare(contestada.replace(/\s/g,"_")) == 0)
res="correct";
else
res="incorrect";
miRTE.setInteractionsResponseAll(ent,contestada.replace(/\s/g,"_"),
res);
} else{
if
(corr.localeCompare(contestadaAux.replace(/\s/g,"_")) == 0)
res="correct";
else
res="incorrect";
miRTE.setInteractionsResponseAll(ent,contestadaAux.replace(/\s/g,"_
"),res);
}
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled
= true;
//Actualizamos la puntuación y mostramos la siguiente
pregunta
actualiza(puntuacion, total);
puntuacion = Math.round(puntuacion * 100) / 100;
if (puntos > 0)
smoke.alert("¡Enhorabuena
"+miRTE.getLearnerName().split(" ")[0]+"! Has conseguido "+puntos+ "
puntos en esta pregunta. Sigue así.");
else
smoke.alert("¡No te desanimes
"+miRTE.getLearnerName().split(" ")[0]+"! ¡A por las siguientes
preguntas!");
muestra(ent);
puntosPreguntas[ent] = puntos;
}
/*********************************************************************
**********
**
** Function: puntuarNum()
** Inputs: An identifier, an integer and two floats.
** Return: None.
**
** Description:
** It gets the score of a numeric question.
99
**
**********************************************************************
*********/
function puntuarNum(iden, ent, inf, sup){
var valor = parseFloat(iden.value);
var res = "";
var puntos =
document.getElementsByName(iden.name)[0].getAttribute("puntos");
var points = 0;
if(valor<=sup && valor>=inf){
puntuacion+=parseFloat(puntos);
points = parseFloat(puntos);
res = "correct";
}
else
res = "incorrect";
//Deshabilitamos los botones
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled
= true;
document.getElementsByName(iden.name)[0].disabled = true;
miRTE.setInteractionsResponseAll(ent,valor,res);
//Actualizamos la puntuación y mostramos la siguiente
pregunta
puntuacion = Math.round(puntuacion * 100) / 100;
actualiza(puntuacion, total);
if (points > 0)
smoke.alert("¡Enhorabuena
"+miRTE.getLearnerName().split(" ")[0]+"! Has conseguido "+points+ "
puntos en esta pregunta. Sigue así.");
else
smoke.alert("¡No te desanimes
"+miRTE.getLearnerName().split(" ")[0]+"! ¡A por las siguientes
preguntas!");
muestra(ent);
puntosPreguntas[ent] = points;
}
/*********************************************************************
**********
**
** Function: puntuaHuecos()
** Inputs: An identifier and an integer.
** Return: None.
**
** Description:
** It gets the score of a cloze question.
**
**********************************************************************
*********/
function puntuaHuecos(iden, ent){
//Obtenemos la respuestas correctas
var corr= miRTE.getInteractionsCorrectRespPattern(ent,0);
100
var count = 0;
var contestada = "";
var contestada2 = "";
var res = "";
var points = 0;
if (typeof iden.name !== "undefined"){
//Obtenemos los huecos
var opcs = document.getElementsByName(iden.name);
for(var i = 0; i < opcs.length; i++){
//Obtenemos el tipo del hueco
var tipo = opcs[i].getAttribute("tipo");
if (tipo.localeCompare("Decimal") == 0){
var valor = parseFloat(opcs[i].value);
var puntos =
opcs[i].getAttribute("puntos");
var sup =
parseFloat(opcs[i].getAttribute("sup"));
var inf =
parseFloat(opcs[i].getAttribute("inf"));
if(valor<=sup && valor>=inf){
puntuacion += parseFloat(puntos);
points += parseFloat(puntos);
if (count==0){
contestada += inf+"[:]"+sup;
contestada2 += auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",inf+"[:]"+sup);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
} else {
if (count==0){
contestada += valor;
contestada2 += auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",valor);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
}
} else
if (tipo.localeCompare("Lista") == 0){
if (count==0){
contestada +=
opcs[i].options[opcs[i].options.selectedIndex].value;
contestada2 +=
auxiliarResp[opcs[i].options.selectedIndex];
} else{
101
contestada =
contestada.concat("[,]",opcs[i].options[opcs[i].options.selectedIndex]
.value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[opcs[i].options.selectedIndex]);
}
puntuacion +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
points +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
} else
if (tipo.localeCompare("String") == 0){
//Obtenemos la sensibilidad del
hueco
var sensibilidad =
opcs[i].getAttribute("sensibilidad");
//Obtenemos los puntos de las
opciones
var puntos =
opcs[i].getAttribute("puntos").split(",");
//Obtenemos los opciones de la
pregunta
var opciones =
opcs[i].getAttribute("sol").split(",");
//Si no es sensible a mayúsculas
if(sensibilidad == "ci"){
if (count==0){
contestada += iden.value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
for(var k = 0; k <
opciones.length - 1; k++){
if(opcs[i].value.toLowerCase() == opciones[k].toLowerCase()){
if (k==0){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
}
else if (opciones[k-
1].toLowerCase() != opciones[k].toLowerCase()){
puntuacion += parseFloat(puntos[k]);
points
+= parseFloat(puntos[k]);
}
}
}
} else
102
//Si es sensible a mayúsculas
if(sensibilidad == "cs"){
if (count==0){
contestada += iden.value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden.value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value ==
opciones[k]){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
break;
}
}
}
//Si tiene distancia de Levenshtein
else {
var dist =
parseInt(sensibilidad.substr(1,1)); //Valor de distancia de
Levenshtein para esta pregunta
var levens = [];
for(var k = 0; k < opciones.length -
1; k++){
levens [k] =
getEditDistance(opcs[i].value, opciones[k]); //Distancia Levenshtein
entre las dos palabras
}
var min = Math.min.apply(null,
levens); //Mínimo del vector
var indice = levens.indexOf(min,0);
//Índice del valor mínimo encontrado
if (count==0){
contestada +=
opcs[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",opcs[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
if(min <= dist){
puntuacion +=
parseFloat(puntos[indice]);
points +=
parseFloat(puntos[indice]);
}
}
103
}
//Deshabilitamos el hueco
opcs[i].disabled = true;
}
} else {
//Obtenemos los huecos
var opcs = document.getElementsByName(iden[0].name);
for(var i = 0; i < opcs.length; i++){
//Obtenemos el tipo de la pregunta
var tipo = opcs[i].getAttribute("tipo");
if (tipo.localeCompare("Decimal") == 0){
var valor = parseFloat(opcs[i].value);
var puntos = opcs[i].getAttribute("puntos");
var sup = parseFloat(opcs[i].getAttribute("sup"));
var inf = parseFloat(opcs[i].getAttribute("inf"));
if(valor<=sup && valor>=inf){
puntuacion += parseFloat(puntos);
points += parseFloat(puntos);
if (count==0){
contestada += inf+"[:]"+sup;
contestada2 += auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",inf+"[:]"+sup);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
} else {
if (count==0){
contestada += valor;
contestada2 += auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",valor);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
}
} else
if (tipo.localeCompare("String") == 0){
//Obtenemos la sensibilidad del hueco
var sensibilidad =
opcs[i].getAttribute("sensibilidad");
//Obtenemos los puntos de las
opciones
var puntos =
opcs[i].getAttribute("puntos").split(",");
//Obtenemos los opciones de la
pregunta
var opciones =
opcs[i].getAttribute("sol").split(",");
//Si no es sensible a mayúsculas
if(sensibilidad == "ci"){
104
if (count==0){
contestada +=
iden[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value.toLowerCase()
== opciones[k].toLowerCase()){
if (k==0){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
}
else if (opciones[k-
1].toLowerCase() != opciones[k].toLowerCase()){
puntuacion += parseFloat(puntos[k]);
points
+= parseFloat(puntos[k]);
}
}
}
} else
//Si es sensible a mayúsculas
if(sensibilidad == "cs"){
if (count==0){
contestada +=
iden[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value ==
opciones[k]){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
break;
}
}
}
105
//Si tiene distancia de Levenshtein
else {
var dist =
parseInt(sensibilidad.substr(1,1)); //Valor de distancia de
Levenshtein para esta pregunta
var levens = [];
for(var k = 0; k < opciones.length -
1; k++){
levens [k] =
getEditDistance(opcs[i].value, opciones[k]); //Distancia Levenshtein
entre las dos palabras
}
var min = Math.min.apply(null,
levens); //Mínimo del vector
var indice = levens.indexOf(min,0);
//Índice del valor mínimo encontrado
if (count==0){
contestada +=
opcs[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",opcs[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
if(min <= dist){
puntuacion +=
parseFloat(puntos[indice]);
points +=
parseFloat(puntos[indice]);
}
}
} else {
if (count==0){
contestada +=
opcs[i].options[opcs[i].options.selectedIndex].value;
contestada2 +=
auxiliarResp[opcs[i].options.selectedIndex];
} else{
contestada =
contestada.concat("[,]",opcs[i].options[opcs[i].options.selectedIndex]
.value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[opcs[i].options.selectedIndex]);
}
count++;
puntuacion +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
points +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
}
//Deshabilitamos el hueco
opcs[i].disabled = true;
106
}
}
if (normalize(contestada.replace(/\s/g,"_")).length > 66)
contestada = contestada2;
contestada =
normalize(contestada.toLowerCase().replace(/\s/g,"_"));
if
(corr.localeCompare(normalize(contestada.replace(/\s/g,"_"))) == 0)
res="correct";
else
res="incorrect";
miRTE.setInteractionsResponseAll(ent,contestada,res);
//Deshabilitamos los botones
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled =
true;
//Actualizamos la puntuación y mostramos la siguiente
pregunta
puntuacion = Math.round(puntuacion * 100) / 100;
actualiza(puntuacion, total);
if (points > 0)
smoke.alert("¡Enhorabuena
"+miRTE.getLearnerName().split(" ")[0]+"! Has conseguido "+points+ "
puntos en esta pregunta. Sigue así.");
else
smoke.alert("¡No te desanimes
"+miRTE.getLearnerName().split(" ")[0]+"! ¡A por las siguientes
preguntas!");
muestra(ent);
puntosPreguntas[ent] = points;
}
/*********************************************************************
**********
**
** Function: muestra()
** Inputs: The number of the question and the total number of
questions of the test.
** Return: None.
**
** Description:
** It shows the next question and introduces it in the LMS.
**
**********************************************************************
*********/
function muestra (num){
var next = num + 1;
var patt = "";
if (next < numeroPr){
var aux = "div"+next;
document.getElementById(aux).style.display = "block";
var id =
document.getElementById("formulario"+next).getElementsByTagName("input
")[0].getAttribute("name");
107
var tipo =
document.getElementById("formulario"+next).getAttribute("tipo");
var desc =
document.getElementById("formulario"+next).getElementsByTagName("h3")[
0].innerHTML;
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
patt = getCorrectaInv(id,tipo);
miRTE.setInteractionsAddNewI(next,id,"choice",1, patt);
miRTE.setInteractionsDescription(next, desc);
} else
if (tipo.localeCompare("MULTIPLE CHOICE QUESTION") ==
0){
patt = getCorrectaInv(id,tipo);
miRTE.setInteractionsAddNewI(next,id,"choice",1,
patt);
miRTE.setInteractionsDescription(next,desc);
} else
if (tipo.localeCompare("CLOZE QUESTION") == 0){
patt = getCorrectaInv(id,tipo);
miRTE.setInteractionsAddNewI(next,id,"fill-in",1,
patt.toLowerCase());
miRTE.setInteractionsDescription(next, desc);
} else
if (tipo.localeCompare("NUMERIC QUESTION") == 0){
patt = getCorrectaInv(id,tipo);
miRTE.setInteractionsAddNewI(next,id,"numeric",1,
patt);
miRTE.setInteractionsDescription(next, desc);
}
}
}
/*********************************************************************
**********
**
** Function: actualiza()
** Inputs: The score of the learner and the total number of available
points of the test.
** Return: None.
**
** Description:
** It updates the score and puts it into the square situated in the
top right of the page.
**
**********************************************************************
*********/
function actualiza (pts, sum){
document.getElementById("tupuntuacion").value = pts + "/" + sum;
}
/*********************************************************************
**********
**
** Function: getCorrectaInv()
** Inputs: The identifier of the question and its type
** Return: The correct answer without special signs neither spaces.
**
** Description:
** It obtains the correct answer/s of any question.
**
108
**********************************************************************
*********/
function getCorrectaInv(iden, tipo){
var opt=document.getElementsByName(iden);
var correcta = "";
var correcta2 = "";
var count = 0;
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
var arr = [];
for(var i=0; i<opt.length;i++){
arr[i] =
parseFloat(opt[i].getAttribute("puntos"));
}
if (opt[arr.indexOf(Math.max.apply(null,
arr))].value.length < 66)
correcta =
normalize(opt[arr.indexOf(Math.max.apply(null,
arr))].value).replace(/[^a-zA-Z 0-9.]+/g,' ');
else
correcta =
auxiliarResp[arr.indexOf(Math.max.apply(null, arr))].replace(/[^a-zA-Z
0-9.]+/g,' ');
} else
if (tipo.localeCompare("MULTIPLE CHOICE QUESTION") == 0){
for(var i=0; i<opt.length;i++){
if (parseFloat(opt[i].getAttribute("respondida"))
> 0){
if (count==0){
correcta =
normalize(opt[i].value).replace(/[^a-zA-Z 0-9.]+/g,' ');
correcta2 = auxiliarResp[i];
} else {
correcta =
correcta.concat("[,]",normalize(opt[i].value).replace(/[^a-zA-Z 0-
9.]+/g,' '));
correcta2 = correcta2.concat("[,]",
auxiliarResp[i]);
} count++;
}
}
if (correcta.replace(/\s/g,"_").length >=
66)
correcta = correcta2;
} else
if (tipo.localeCompare("CLOZE QUESTION") == 0){
for(var i=0; i<opt.length;i++){
//Obtenemos el tipo de hueco
var hueco = opt[i].getAttribute("tipo");
if (hueco.localeCompare("Decimal") == 0){
if (count==0){
correcta =
opt[i].getAttribute("inf")+"[:]"+opt[i].getAttribute("sup");
correcta2 =
opt[i].getAttribute("inf")+"[:]"+opt[i].getAttribute("sup");
} else{
correcta =
correcta.concat("[,]",opt[i].getAttribute("inf")+"[:]"+opt[i].getAttri
bute("sup"));
109
correcta2 =
correcta2.concat("[,]",opt[i].getAttribute("inf")+"[:]"+opt[i].getAttr
ibute("sup"));
}
count++;
} else
if (hueco.localeCompare("Lista") ==
0){
if (count==0){
correcta =
getCorrectaH(opt[i])[0];
correcta2 =
auxiliarResp[getCorrectaH(opt[i])[1]];
} else {
correcta =
correcta.concat("[,]",getCorrectaH(opt[i])[0]);
correcta2 =
correcta2.concat("[,]",getCorrectaH(opt[i])[1]);
}
count++;
} else
if (hueco.localeCompare("String") ==
0){
var opcs =
opt[i].getAttribute("sol").split(",");
var pts =
opt[i].getAttribute("puntos").split(",");
pts.length = pts.length - 1;
var numbers = pts.map(Number);
var index =
numbers.indexOf(Math.max.apply(null, numbers));
if (count==0)
correcta =
opcs[index];
else
correcta =
correcta.concat("[,]",opcs[index]);
count++;
}
}
if (correcta.replace(/\s/g,"_").length >=
66)
correcta = correcta2;
} else
if (tipo.localeCompare("NUMERIC QUESTION")
== 0){
correcta =
opt[0].getAttribute("inf")+"[:]"+opt[0].getAttribute("sup");
}
return normalize(correcta.replace(/\s/g,"_"));
}
/*********************************************************************
**********
**
** Function: getCorrectaH()
** Inputs: The options of a drop-down list of a cloze question.
** Return: The correct answer.
**
** Description:
** It obtains the correct answer of the list.
110
**
**********************************************************************
*********/
function getCorrectaH (opciones) {
var arr = [];
cont = 0;
for(var j = 0; j < opciones.options.length; j++){
arr[cont] =
parseFloat(opciones.options[j].getAttribute("puntos"));
cont++;
}
var index = arr.indexOf(Math.max.apply(null, arr));
return [opciones.options[index].value,index];
}
/*********************************************************************
**********
**
** Function: normalize()
** Inputs: A string with special signs.
** Return: The same string without any special sign.
**
** Description:
** It normalizes a string.
**
**********************************************************************
*********/
var normalize = (function() {
var from = "ÃÀÁÄÂÈÉËÊÌÍÏÎÒÓÖÔÙÚÜÛãàáäâèéëêìíïîòóöôùúüûÑñÇç",
to = "AAAAAEEEEIIIIOOOOUUUUaaaaaeeeeiiiioooouuuunncc",
mapping = {};
for(var i = 0, j = from.length; i < j; i++ )
mapping[ from.charAt( i ) ] = to.charAt( i );
return function( str ) {
var ret = [];
for( var i = 0, j = str.length; i < j; i++ ) {
var c = str.charAt( i );
if( mapping.hasOwnProperty( str.charAt( i ) ) )
ret.push( mapping[ c ] );
else
ret.push( c );
}
return ret.join( '' );
}
})();
/*********************************************************************
**********
**
** Function: getEditDistance()
** Inputs: Two strings.
** Return: An integer that represents the Levenshtein distance
between the two strings.
**
** Description:
** It gets the Levenshtein distance between the two strings given.
**
111
**********************************************************************
*********/
function getEditDistance (a, b){
if(a.length == 0) return b.length;
if(b.length == 0) return a.length;
var matrix = [];
// increment along the first column of each row
var i;
for(i = 0; i <= b.length; i++){
matrix[i] = [i];
}
// increment each column in the first row
var j;
for(j = 0; j <= a.length; j++){
matrix[0][j] = j;
}
// Fill in the rest of the matrix
for(i = 1; i <= b.length; i++){
for(j = 1; j <= a.length; j++){
if(b.charAt(i-1) == a.charAt(j-1)){
matrix[i][j] = matrix[i-1][j-1];
} else {
matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
Math.min(matrix[i][j-1] + 1, //
insertion
matrix[i-1][j] + 1)); //
deletion
}
}
}
return matrix[b.length][a.length];
};
/*********************************************************************
**********
**
** Function: obtieneTiempo()
** Inputs: Two strings of time.
** Return: The difference between both strings.
**
** Description:
** It calculates the difference between both strings.
**
**********************************************************************
*********/
function obtieneTiempo(cad1, cad2){
var annio = parseInt(cad2.substring(0,4)) -
parseInt(cad1.substring(0,4));
var mes = parseInt(cad2.substring(5,7)) -
parseInt(cad1.substring(5,7));
var dias = parseInt(cad2.substring(8,10)) -
parseInt(cad1.substring(8,10));
var horas = parseInt(cad2.substring(11,13)) -
parseInt(cad1.substring(11,13));
var minutos = parseInt(cad2.substring(14,16)) -
parseInt(cad1.substring(14,16));
112
var segundos = parseInt(cad2.substring(17,19)) -
parseInt(cad1.substring(17,19));
var tiempo = "";
if (segundos < 0) {
minutos--;
segundos = 60 + segundos;
}
if (minutos < 0) {
horas--;
minutos = 60 + minutos;
}
if (horas < 0) {
dias--;
horas = 24 + horas;
}
if (dias < 0) {
mes--;
dias = 30 + dias;
}
if (mes < 0) {
annio--;
mes = 12 + mes;
}
if (segundos.toString().length < 2){
segundos = "0"+segundos;
}
if (minutos.toString().length < 2){
minutos = "0"+minutos;
}
if (horas.toString().length < 2){
horas = "0"+horas;
}
if (annio != 0){
if (annio != 1 && mes != 1)
tiempo = annio +" años, "+ mes + " meses, " +
dias +" días y " + horas+":"+minutos+":"+segundos;
else if (annio != 1 && mes == 1)
tiempo = annio +" años, "+ mes + " mes, " + dias
+" días y " + horas+":"+minutos+":"+segundos;
else if (annio == 1 && mes != 1)
tiempo = annio +" año, "+ mes + " meses, " + dias
+" días y " + horas+":"+minutos+":"+segundos;
else if (annio == 1 && mes == 1)
tiempo = annio +" año, "+ mes + " mes, " + dias
+" días y " + horas+":"+minutos+":"+segundos;
} else if (mes != 0){
if (mes != 1 && dias != 1)
tiempo = mes + " meses, " + dias +" días y " +
horas+":"+minutos+":"+segundos;
else if (mes != 1 && dias == 1)
tiempo = mes + " meses, " + dias +" día y " +
horas+":"+minutos+":"+segundos;
else if (mes == 1 && dias != 1)
tiempo = mes + " mes, " + dias +" días y " +
horas+":"+minutos+":"+segundos;
else if (mes == 1 && dias == 1)
113
tiempo = mes + " mes, " + dias +" día y " +
horas+":"+minutos+":"+segundos;
} else if (dias != 0){
if (dias != 1)
tiempo = dias +" días y " +
horas+":"+minutos+":"+segundos;
else
tiempo = dias +" día y " +
horas+":"+minutos+":"+segundos;
}
else
tiempo = horas+":"+minutos+":"+segundos;
return tiempo;
}
/*********************************************************************
**********
**
** Function: ponRespuestas()
** Inputs: An integer depending on the rows of the table.
** Return: None.
**
** Description:
** It adds the answers given by the learner in a table interacting
with the LMS.
**
**********************************************************************
*********/
function ponRespuestas(entero){
for (var i = 0; i < numeroPr; i++){
var res = miRTE.getInteractionsResponse(i).split("[,]");
var lat = miRTE.getInteractionsLatency(i);
var type = miRTE.getInteractionsType(i);
if (res == "")
res = "No_contestada";
var aux = entero + i;
var preg = "PE"+ aux;
document.getElementById("PE"+aux).innerHTML = res;
if (lat.localeCompare("") == 0)
document.getElementById("PE"+aux).innerHTML += "
Sin_latencia";
else
document.getElementById("PE"+aux).innerHTML += " " +
miRTE.getInteractionsLatency(i);
if (puntosPreguntas[i] != "undefined")
document.getElementById("PE"+aux).innerHTML += " " +
puntosPreguntas[i] + " puntos";
else
document.getElementById("PE"+aux).innerHTML += " 0
puntos";
}
}
/*********************************************************************
**********
**
** Function: reiniciar()
** Inputs: None.
** Return: None.
**
114
** Description:
** It resets a couple of fields in the LMS.
**
**********************************************************************
*********/
function reiniciar (){
for (var i = 0; i < numeroPr; i++){
if (i == 0)
miRTE.setInteractionsResponse(i,"No_contestada");
else {
miRTE.setInteractionsCorrectRespPattern(i,0,"No_contestada");
miRTE.setInteractionsResponse(i,"No_contestada");
}
}
}
/*********************************************************************
**********
**
** Function: deshabilitar()
** Inputs: None.
** Return: None.
**
** Description:
** It disables the questions buttons.
**
**********************************************************************
*********/
function deshabilitar (){
for (var i = 0; i < numeroPr; i++){
document.getElementsByName("valida"+i)[0].disabled = true;
document.getElementsByName("borra"+i)[0].disabled = true;
if (i + 1 != numeroPr)
document.getElementsByName("posponer"+i)[0].disabled =
true;
}
}
8.4 Librería JavaScript creaPreguntasSinCom.js
/* creaPreguntasSinCom.js JavaScript library by Sergio Díaz
Librería desarrollada por Sergio Díaz Fuentes. Se permite cualquier
explotación de la obra, incluyendo una finalidad comercial, así como
la creación de obras derivadas, la distribución de las cuales también
está permitida sin ninguna restricción.
*/
var puntuacion = 0;
var total = 0;
var minimo = 0;
var longitudPreg = 0;
var auxiliarCont = 0;
var contador = 0;
var auxiliarResp = ["a","b","c","d","e","f","g","h","i","j"];
var respuestas = [];
var puntosPreguntas = new Array ();
var latencias = new Array ();
var f=new Date();
var hora = f.getHours();
115
var min = f.getMinutes();
var sec = f.getSeconds();
if (hora.length == 1)
hora = "0"+ hora;
if (min.length == 1)
min = "0"+ min;
if (sec.length == 1)
sec = "0"+ sec;
var horaInicio = hora+":"+min+":"+sec;
var miTiempo="";
rellenaPuntos();
/*********************************************************************
**********
**
** Function: loadXMLDoc()
** Inputs: The path of the XML file, the number of questions of the
test, time for the test, random questions and random options.
** Return: The number of requested test questions presented to the
user with the chosen features.
**
** Description:
** Requests the xml file and presents the questions.
**
**********************************************************************
*********/
function loadXMLDoc(xml,numPreg,time,aleaPr, aleaOp) {
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
// code for older browsers
xmlhttp = new ActiveXObject("Microsoft.XMLDOM");
}
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var xmlDoc = xmlhttp.responseXML;
if (xmlDoc != null) {
ponDiv();
ponPuntuacion();
if (parseInt(time) > 0){
if (time.toString().length < 2)
var t = "0" + time + ":00";
else
var t = time + ":00";
ponTime(t);
}
if (time != 0)
miTiempo = setInterval( function(){cuentAtras()},
1000);
// En la variable div_preguntas obtenemos el contenedor div
con el id 'preguntas'
var div_preguntas = document.getElementById('preguntas');
// Obtenemos la lista de preguntas
116
var preguntas_set =
xmlDoc.getElementsByTagName("questestinterop")[0].getElementsByTagName
("item");
longitudPreg = preguntas_set.length;
// Comprobamos que el número de preguntas sea igual o
inferior al número de preguntas disponibles
if (numPreg > preguntas_set.length){
numeroPr = preguntas_set.length;
}
//Mezclamos un array auxiliar que nos servirá para obtener
las preguntas de forma aleatoria
if (aleaPr.localeCompare("si") == 0)
var arrayaux = arrayAlea(preguntas_set);
else
var arrayaux = obtenArray(preguntas_set.length);
while (auxiliarCont < numeroPr)
{
// Obtenemos el título de la pregunta
var titulo =
preguntas_set[arrayaux[auxiliarCont]].getAttribute("title");
var
descripcion=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName
("mattext")[0].childNodes[0].nodeValue;
if (descripcion.indexOf("<p>") == -1)
descripcion = "<p>"+descripcion+"</p>";
//Obtenemos el tipo de la pregunta
var
tipo=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("qtime
tadatafield")[1].getElementsByTagName("fieldentry")[0].childNodes[0].n
odeValue;
//Obtenemos el identificador de la pregunta
var
id=preguntas_set[arrayaux[auxiliarCont]].getAttribute("ident");
var flag=0;
var flagE = 0;
var opciones="";
if(tipo=="SINGLE CHOICE QUESTION"){
// Obtenemos las opciones de la pregunta
var opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
//Obtenemos los puntos de cada opcion
var
puntos=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("set
var");
opciones+=unica(opciones_set,id,puntos,contador);
total+=sumaPuntos(puntos, tipo, 0);
minimo+=restaPuntos(puntos, tipo, 0);
flagE = 1;
} else
if(tipo=="NUMERIC QUESTION"){
117
//Obtenemos los límites superior e inferior
para la pregunta
var
sup=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("varlte
")[0].childNodes[0].nodeValue;
var
inf=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("vargte
")[0].childNodes[0].nodeValue;
//Obtenemos el numero máximo de caracteres
permitidos
var
maxcar=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("ren
der_fib")[0].getAttribute("maxchars");
//Obtenemos los puntos en caso de acierto
var
puntos=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("set
var")[0].childNodes[0].nodeValue;
opciones+=numerica(id,sup,inf,puntos,maxcar,contador);
total+=parseFloat(puntos);
flagE = 1;
}
else
if(tipo=="MULTIPLE CHOICE QUESTION"){
// Obtenemos las opciones de la
pregunta
var opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
//Obtenemos los puntos de cada opcion
var
puntos=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("set
var");
opciones+=multiple(opciones_set,id,puntos,contador);
total+=sumaPuntos(puntos, tipo, 0);
minimo+=restaPuntos(puntos, tipo, 0);
flagE = 1;
}
else
//https://social.msdn.microsoft.com/Forums/es-ES/119aa457-11f6-
408b-9c70-d368deabd597/javascript-saber-si-una-letra-esta-en-una-
cadena-de-caracteres?forum=netfxwebes
if(tipo=="CLOZE QUESTION"){
var opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
var huecosString =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("response_s
tr");
var huecosNum =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("response_n
um");
var oposibles =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("varequal")
; //Opciones posibles
var puntos =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("setvar");
//Puntos de las distintas opciones
var nHuecos = huecosString.length +
huecosNum.length; //Número total de huecos
118
var tagNot =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("not");
var arr = [].slice.call(oposibles);
var arrMat =
[].slice.call(opciones_set);
if (tagNot.length != 0){
var osobrantes =
tagNot[0].getElementsByTagName("varequal").length;
arr.length = oposibles.length -
osobrantes * 2;
arrMat.length = opciones_set.length -
osobrantes;
}
var gapsT = 0;
var gapsN = 0;
descripcion="";
total += sumaPuntos(puntos,tipo,
oposibles);
minimo += restaPuntos(puntos, tipo,
oposibles);
var w = 0;
do{
if
(arrMat[w].childNodes[0].nodeValue.indexOf("<p>") != -1)
descripcion +=
arrMat[w].childNodes[0].nodeValue; //La primera nunca será un hueco
else{
if (w==0)
descripcion += "<p>"
+arrMat[w].childNodes[0].nodeValue.replace(/<br \/>/g,
'').replace(/<\/?span[^>]*>/g,"");
else if (w ==
arrMat.length - nHuecos - 1)
descripcion += arrMat[w].childNodes[0].nodeValue.replace(/<br
\/>/g, '').replace(/<\/?span[^>]*>/g,"") +"</p>";
else
descripcion += arrMat[w].childNodes[0].nodeValue.replace(/<br
\/>/g, '').replace(/<\/?span[^>]*>/g,"");
}
if (gapsT + gapsN <
nHuecos){
if
(huecosString.length != 0 && gapsT < huecosString.length){
if
(huecosString[gapsT].getAttribute("ident").indexOf(gapsT + gapsN) != -
1){
if
(huecosString[gapsT].getElementsByTagName("render_choice").length ==
1){
descripcion += creaDesplegable(id,arr,puntos,gapsT + gapsN,
aleaOp);
w = w +
119
huecosString[gapsT].getElementsByTagName("render_choice")[0].getElemen
tsByTagName("mattext").length;
}
else{
descripcion+="<input type=\"text\" name=\""+id+"\"
class=\"huecoT\"
tipo=\""+huecosString[gapsT].getElementsByTagName("render_fib")[0].get
Attribute("fibtype")+"\"
sensibilidad=\""+preguntas_set[arrayaux[auxiliarCont]].getElementsByTa
gName("fieldentry")[5].childNodes[0].nodeValue+"\" puntos=\"\"
sol=\"\"
size=\""+huecosString[gapsT].getElementsByTagName("render_fib")[0].get
Attribute("columns")+"\"
maxlength=\""+parseInt(huecosString[gapsT].getElementsByTagName("rende
r_fib")[0].getAttribute("columns"))+"\"/>";
}
gapsT++;
w++;
}
else if (huecosNum[gapsN].getAttribute("ident").indexOf(gapsT + gapsN)
!= -1){
var min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
innumber");
if (min == null)
min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
axnumber");
descripcion += "<input type=\"text\" name=\""+id+"\"
class=\"huecoT\"
tipo=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAtt
ribute("fibtype")+"\" inf=\""+min+"\"
sup=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttr
ibute("maxnumber")+"\" puntos=\"\" size=\"3\"
maxlength=\""+parseInt(huecosNum[gapsN].getElementsByTagName("render_f
ib")[0].getAttribute("columns"))+"\"/>";
gapsN++;
w++;
}
}
else if
(huecosNum[gapsN].getAttribute("ident").indexOf(gapsT + gapsN) != -1){
var min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
innumber");
120
if
(min == null)
min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
axnumber");
descripcion += "<input type=\"text\" name=\""+id+"\"
class=\"huecoT\"
tipo=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAtt
ribute("fibtype")+"\" inf=\""+min+"\"
sup=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttr
ibute("maxnumber")+"\" puntos=\"\" size=\"3\"
maxlength=\""+parseInt(huecosNum[gapsN].getElementsByTagName("render_f
ib")[0].getAttribute("columns"))+"\"/>";
gapsN++;
w++;
}
}
else
w++;
} while (w < arrMat.length
- nHuecos);
if (contador + 1 != numeroPr &&
auxiliarCont + 1 < preguntas_set.length)
descripcion+="<input
name=\"valida"+contador+"\" type=\"button\" value=\"Validar\"
onclick=\"puntuaHuecos("+id+","+contador+")\" class=\"boton\"/><input
name=\"borra"+contador+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/><input name=\"posponer"+contador+"\" type=\"button\"
value=\"Posponer\" class=\"boton\"
onclick=\"muestra("+contador+")\"/></form>";
else
descripcion+="<input
name=\"valida"+contador+"\" type=\"button\" value=\"Validar\"
onclick=\"puntuaHuecos("+id+","+contador+")\" class=\"boton\"/><input
name=\"borra"+contador+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/></form>";
// Modificamos el contenido
html del contenedor div
if (contador==0)
div_preguntas.innerHTML +=
"<div id=\"div"+contador+"\" style=\"display:block;\"><form
id=\"formulario"+contador+"\" tipo=\""+tipo+"\"><h3>" +
parseInt(contador+1) + ". " + titulo + "</h3>" + descripcion
+"</form></div>";
else
div_preguntas.innerHTML +=
"<div id=\"div"+contador+"\" style=\"display:none;\"><form
id=\"formulario"+contador+"\" tipo=\""+tipo+"\"><h3>" +
parseInt(contador+1) + ". " + titulo + "</h3>" + descripcion +
"</form></div>";
for (var q = 0; q < nHuecos;
q++){
for (var k = 0; k <
oposibles.length; k++){
121
if
(oposibles[k].getAttribute("respident").indexOf(q) != -1 &&
document.getElementsByName(id)[q].getAttribute("tipo")){
if
(document.getElementsByName(id)[q].getAttribute("tipo").localeCompare(
"String") == 0){
var attAnterior =
document.getElementsByName(id)[q].getAttribute("sol");
var puntAnterior =
document.getElementsByName(id)[q].getAttribute("puntos");
document.getElementsByName(id)[q].setAttribute("sol",attAnterior+op
osibles[k].childNodes[0].nodeValue+",");
document.getElementsByName(id)[q].setAttribute("puntos",puntAnterio
r + puntos[k].childNodes[0].nodeValue+",");
}
else if
(document.getElementsByName(id)[q].getAttribute("tipo").localeCompare(
"Decimal") == 0)
document.getElementsByName(id)[q].setAttribute("puntos",
puntos[k].childNodes[0].nodeValue);
}
}
}
flag = 1; //Este flag nos
permite que no se repita la pregunta de nuevo en el div_preguntas
flagE = 1;
}
// Modificamos el contenido html del contenedor div
if(flag == 0 && flagE == 1){
if (contador == 0)
div_preguntas.innerHTML += "<div id=\"div"+contador+"\"
style=\"display:block;\"><form id=\"formulario"+contador+"\"
tipo=\""+tipo+"\"><h3>" + parseInt(contador+1) + ". " + titulo +
"</h3>" + descripcion + opciones+"</form></div>";
else
div_preguntas.innerHTML += "<div id=\"div"+contador+"\"
style=\"display:none;\"><form id=\"formulario"+contador+"\"
tipo=\""+tipo+"\"><h3>" + parseInt(contador+1) + ". " + titulo +
"</h3>" + descripcion + opciones+"</form></div>";
}
if(flagE == 1){
auxiliarCont++;
contador++;
} else{
auxiliarCont++;
}
if (auxiliarCont >= preguntas_set.length)
break;
}
actualiza(puntuacion,total);
}
}
122
};
xmlhttp.open("GET", xml, true);
xmlhttp.send();
}
/*********************************************************************
**********
** Function: ponDiv()
** Inputs: None.
** Return: None.
**
** Description:
** It adds a div tag to the html page.
**
**********************************************************************
*********/
function ponDiv(){
var midiv = document.createElement("div");
midiv.setAttribute("id","preguntas");
midiv.setAttribute("class","desplazado");
//document.body.appendChild(midiv); // Lo pones en "body", si
quieres ponerlo dentro de algún id en concreto usas
document.getElementById('donde lo quiero poner').appendChild(midiv);
document.getElementById('tabla').appendChild(midiv);
}
/*********************************************************************
**********
**
** Function: ponPuntuacion()
** Inputs: None.
** Return: None.
**
** Description:
** It adds a puntuation div tag to the html page.
**
**********************************************************************
*********/
function ponPuntuacion(){
var midiv = document.createElement("div");
midiv.setAttribute("class","cuadroFijo");
midiv.setAttribute("id","divPuntuacion");
if
(banco.localeCompare("../../_ejs_library/html/PreguntasPage2.xml") ==
0 || banco.localeCompare("../../_ejs_library/html/PreguntasPage1.xml")
== 0)
midiv.setAttribute("style","display:none;");
var centro = document.createElement("center");
var mipar = document.createElement("p");
mipar.setAttribute("align","center");
centro.appendChild(mipar);
var t = document.createTextNode("Puntuación");
mipar.appendChild(t);
var centr = document.createElement("center");
var cuadro = document.createElement("input");
cuadro.setAttribute("type","text");
cuadro.setAttribute("value","");
cuadro.setAttribute("id","tupuntuacion");
cuadro.setAttribute("size","3");
cuadro.setAttribute("maxlength","3");
cuadro.setAttribute("readonly","true");
123
cuadro.setAttribute("class","altura puntos");
centr.appendChild(cuadro);
midiv.appendChild(centro);
midiv.appendChild(centr);
document.body.appendChild(midiv);
}
/*********************************************************************
**********
**
** Function: ponTime()
** Inputs: The time in minutes of test performance.
** Return: None.
**
** Description:
** It adds a time div tag to the html page.
**
**********************************************************************
*********/
function ponTime(time){
var midiv = document.createElement("div");
midiv.setAttribute("id","divTiempo");
midiv.setAttribute("class","cuadroFijoT");
var para = document.createElement("p");
para.setAttribute("align","center");
var t = document.createTextNode("Tiempo");
para.appendChild(t);
var centrado = document.createElement("center");
var cuadro = document.createElement("input");
cuadro.setAttribute("type","text");
cuadro.setAttribute("id","tutiempo");
cuadro.setAttribute("value",time); cuadro.setAttribute("size","3");
cuadro.setAttribute("class","altura puntos");
cuadro.setAttribute("readonly","true");
cuadro.setAttribute("maxlength","3");
centrado.appendChild(cuadro);
midiv.appendChild(para);
midiv.appendChild(centrado);
document.body.appendChild(midiv);
}
/*********************************************************************
**********
**
** Function: cuentAtras()
** Inputs: None.
** Return: None.
**
** Description:
** It decrements the clock.
**
**********************************************************************
*********/
function cuentAtras(){
var cad = document.getElementById("tutiempo").value;
var minutos = parseInt(cad.substring(0,2));
var segundos = parseInt(cad.substring(3,cad.length));
segundos--;
if (segundos < 0){
124
minutos--;
segundos = 60 + segundos;
}
if (minutos < 0){
clearInterval(miTiempo);
deshabilitar();
smoke.alert("Lo sentimos. Se ha acabado tu tiempo para
responder");
} else{
if (minutos.toString().length < 2)
minutos = "0" + minutos;
if (segundos.toString().length < 2)
segundos = "0" + segundos;
document.getElementById("tutiempo").value = minutos
+":"+segundos;
}
}
/*********************************************************************
**********
**
** Function: arrayAlea()
** Inputs: An array of questions.
** Return: An array with unordered values depending of the length of
de questions array.
**
** Description:
** It mixes a vector of sorted values.
**
**********************************************************************
*********/
function arrayAlea(array){
var unArray=[];
for(var z=0;z<array.length;z++){
unArray[z]=z;
}
fisher_yates(unArray);
return unArray;
}
/*********************************************************************
**********
**
** Function: arrayAleaEnt()
** Inputs: An integer.
** Return: An array with unordered values with variable length
depending of the integer.
**
** Description:
** It creates an unordered array.
**
**********************************************************************
*********/
function arrayAleaEnt(entero){
var unArray=[];
for(var z=0;z<entero;z++){
unArray[z]=z;
}
fisher_yates(unArray);
return unArray;
}
125
/*********************************************************************
**********
**
** Function: obtenArray()
** Inputs: An integer.
** Return: An array of ordered values with variable length depending
of the integer.
**
** Description:
** It creates an ordered array.
**
**********************************************************************
*********/
function obtenArray(entero){
var unArray=[];
for(var z=0;z<entero;z++){
unArray[z]=z;
}
return unArray;
}
/*********************************************************************
**********
**
** Function: rellenaPuntos()
** Inputs: None.
** Return: None.
**
** Description:
** It initializes the points vector called puntosPreguntas.
**
**********************************************************************
*********/
function rellenaPuntos(){
for (var j=0; j < numeroPr; j++){
puntosPreguntas[j] = 0;
}
}
/*********************************************************************
**********
**
** Function: fisher_yates()
** Inputs: An array of sorted values.
** Return: The function returns the untidy vector.
**
** Description:
** It mixes a vector of sorted values according to Fisher-Yates
algorithm.
**
**********************************************************************
*********/
function fisher_yates(array){
var i=array.length;
while(i--){
var j=Math.floor( Math.random() * (i+1) );
var tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
126
}
/*********************************************************************
**********
**
** Function: creaDesplegable()
** Inputs: The identifier, the options of the cloze question, the
points of the question and an integer that represents the order of the
question.
** Return: A drop-down list
**
** Description:
** It creates a drop-down list gap.
**
**********************************************************************
*********/
function creaDesplegable (ident, opcs, pts, ent, alea){
if (alea.localeCompare("si") == 0)
var unArray = arrayAleaEnt(opcs.length);
else
var unArray = obtenArray (opcs.length);
var lista = "<select name=\""+ident+"\" class=\"desplegable\"
tipo=\"Lista\">";
for (j = 0; j < opcs.length; j++){
if (opcs[unArray[j]].getAttribute("respident").indexOf(ent)
!= -1)
lista += "<option
value=\""+opcs[unArray[j]].childNodes[0].nodeValue+"\"
puntos=\""+pts[unArray[j]].childNodes[0].nodeValue+"\"
>"+opcs[unArray[j]].childNodes[0].nodeValue +"</option>";
}
lista += "</select>";
return lista;
}
/*********************************************************************
**********
**
** Function: sumaPuntos()
** Inputs: An array with the points of a question, the type of the
question and the number of gaps.
** Return: The maximum points for this question.
**
** Description:
** It calculates the maximum number of points for this question..
**
**********************************************************************
*********/
function sumaPuntos(pts,tipo,ngaps){
var suma= 0;
var cont = 0;
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
var arr = [];
for(var z=0;z<pts.length;z++){
arr[z] = parseFloat(pts[z].childNodes[0].nodeValue);
}
suma+=Math.max.apply(null, arr);
} else if (tipo.localeCompare("CLOZE QUESTION") == 0){
suma += getPuntos(pts,ngaps);
} else {
for(var z=0;z<pts.length;z++){
127
if (parseFloat(pts[z].childNodes[0].nodeValue) > 0)
suma+=parseFloat(pts[z].childNodes[0].nodeValue);
}
}
return suma;
}
/*********************************************************************
**********
**
** Function: restaPuntos()
** Inputs: An array with the points of a question, the type of the
question and the number of gaps.
** Return: The minimum points for this question.
**
** Description:
** It calculates the minimum number of points for this question..
**
**********************************************************************
*********/
function restaPuntos(pts,tipo,ngaps){
var resta= 0;
var cont = 0;
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
var arr = [];
for(var z=0;z<pts.length;z++){
arr[z] = parseFloat(pts[z].childNodes[0].nodeValue);
}
resta+=Math.min.apply(null, arr);
} else if (tipo.localeCompare("CLOZE QUESTION") == 0){
resta += getPuntosResta(pts,ngaps);
} else {
for(var z=0;z<pts.length;z++){
if (parseFloat(pts[z].childNodes[0].nodeValue) < 0)
resta+=parseFloat(pts[z].childNodes[0].nodeValue);
}
}
return resta;
}
/*********************************************************************
**********
**
** Function: getPuntos()
** Inputs: An array with the points of a question and the number of
gaps.
** Return: The maximum points for this question.
**
** Description:
** It calculates the maximum number of points for this cloze question.
**
**********************************************************************
*********/
function getPuntos(pts,ngaps){
cont = 0;
cont2 = 0;
z=0;
suma = 0;
var arr = [];
do{
128
if (ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) > 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
else if (ngaps[z].getAttribute("respident").indexOf(cont) ==
-1){
suma+=Math.max.apply(null, arr);
arr = [];
cont++;
cont2 = 0;
if
(ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) > 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
}
z++;
if (z == pts.length)
suma+=Math.max.apply(null, arr);
} while (z < pts.length);
return suma;
}
/*********************************************************************
**********
**
** Function: getPuntosResta()
** Inputs: An array with the points of a question and the number of
gaps.
** Return: The minimum points for this question.
**
** Description:
** It calculates the minimum number of points for this cloze question.
**
**********************************************************************
*********/
function getPuntosResta(pts,ngaps){
cont = 0;
cont2 = 0;
z=0;
resta = 0;
var arr = [];
do{
if (ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) < 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
else if (ngaps[z].getAttribute("respident").indexOf(cont) ==
-1){
if (arr.length != 0)
resta+=Math.min.apply(null, arr);
arr = [];
cont++;
cont2 = 0;
129
if
(ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) < 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
}
z++;
if (z == pts.length){
if(arr.length != 0)
resta+=Math.min.apply(null, arr);
}
} while (z < pts.length);
return resta;
}
/*********************************************************************
**********
**
** Function: obtenLatencia()
** Inputs: Two strings.
** Return: The time between both strings.
**
** Description:
** It calculates the difference between two given strings.
**
**********************************************************************
*********/
function obtenLatencia(cad1, cad2){
var hora1 = cad1.split(":")[0];
var hora2 = cad2.split(":")[0];
var min1 = cad1.split(":")[1];
var min2 = cad2.split(":")[1];
var sec1 = cad1.split(":")[2];
var sec2 = cad2.split(":")[2];
var sec = parseInt(sec1) - parseInt(sec2);
var min = parseInt(min1) - parseInt(min2);
var hora = parseInt(hora1) - parseInt(hora2);
if (sec < 0){
min--;
sec = 60 + sec;
}
if (hora < 10) hora = "0" + hora;
if (min < 10) min = "0" + min;
if (sec < 10) sec = "0" + sec;
return hora+":"+min+":"+sec;
}
/*********************************************************************
**********
**
** Function: unica()
** Inputs: An array of options, an identifier, an array wiht the
options points and an integer.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a multiple choice
question and only answer.
**
130
**********************************************************************
*********/
function unica(opciones_tag,ident,pts,ent){
if (opAleat.localeCompare("si") == 0)
var vector = arrayAleaEnt(pts.length);
else
var vector = obtenArray(pts.length);
var opciones="";
for (var j = 0; j < pts.length; j++){
if
(opciones_tag[vector[j]+1].childNodes[0].nodeValue.indexOf("<p>"
) != -1)
opciones += "<p class=\opciones\><input type=\"radio\"
name=\""+ident+"\"
value=\""+opciones_tag[vector[j]+1].childNodes[0].nodeValue+"\"puntos=
\""+pts[vector[j]].childNodes[0].nodeValue+"\"/>"+opciones_tag[vector[
j]+1].childNodes[0].nodeValue+"</p>";
else{
var cadena =
opciones_tag[vector[j]+1].childNodes[0].nodeValue.replace("<p>",'');
cadena = cadena.replace("</p>",'');
opciones += "<p class=\opciones\><input type=\"radio\"
name=\""+ident+"\"
value=\""+cadena+"\"puntos=\""+pts[vector[j]].childNodes[0].nodeValue+
"\"/>"+cadena+"</p>";
}
}
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\" type=\"button\"
value=\"Validar\" onclick=\"puntuarUni("+ident+","+ent+")\"
class=\"boton\"/><input name=\"borra"+ent+"\" type=\"reset\"
value=\"Borrar\" class=\"boton\"/><input name=\"posponer"+ent+"\"
type=\"button\" value=\"Posponer\" class=\"boton\"
onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\" type=\"button\"
value=\"Validar\" onclick=\"puntuarUni("+ident+","+ent+")\"
class=\"boton\"/><input name=\"borra"+ent+"\" type=\"reset\"
value=\"Borrar\" class=\"boton\"/>";
return opciones;
}
/*********************************************************************
**********
**
** Function: numerica()
** Inputs: An identifier, two floats, an array with the options
points and two integers.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a numeric question.
**
**********************************************************************
*********/
function numerica(iden,superior,inferior,pts,maxcar,ent){
var opciones="";
opciones += "<p class=\"opciones\">Escribe el valor: <input
type=\"text\" name=\""+iden+"\" inf=\""+inferior+"\"
sup=\""+superior+"\" puntos=\""+pts+"\" size=\"3\"
maxlength=\""+parseInt(maxcar)+"\"/></p></br>";
131
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarNum("+iden+","+ent+","+inferior+","+superior+")\"
class=\"boton\"/><input name=\"borra"+ent+"\" type=\"reset\"
value=\"Borrar\" class=\"boton\"/><input name=\"posponer"+ent+"\"
type=\"button\" value=\"Posponer\" class=\"boton\"
onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarNum("+iden+","+ent+","+inferior+","+superior+")\"
class=\"boton\"/><input name=\"borra"+ent+"\" type=\"reset\"
value=\"Borrar\" class=\"boton\"/>";
return opciones;
}
/*********************************************************************
**********
**
** Function: multiple()
** Inputs: An array of options, an identifier, an array wiht the
options points and an integer.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a multiple choice
question and multiple answer.
**
**********************************************************************
*********/
function multiple(opciones_tag,ident,pts,ent){
//Vectores que contendran los puntos de las opciones
var respondidas=[];
var norespondidas=[];
var j=0;
for(var z = 0; z < pts.length; z=z+2){
respondidas[j]=pts[z].childNodes[0].nodeValue;
norespondidas[j]=pts[z+1].childNodes[0].nodeValue;
j++;
}
if (opAleat.localeCompare("si") == 0)
var vector=arrayAleaEnt(pts.length/2);
else
var vector=obtenArray(pts.length/2);
var opciones="";
for (var j = 0; j < pts.length/2; j++){
if
(opciones_tag[vector[j]+1].childNodes[0].nodeValue.indexOf("<p>"
) != -1)
opciones += "<p class=\"opciones\"><input
type=\"checkbox\" name=\""+ident+"\"
value=\""+opciones_tag[vector[j]+1].childNodes[0].nodeValue+"\"
respondida=\""+respondidas[vector[j]]+"\"
norespondida=\""+norespondidas[vector[j]]+"\"/>"+opciones_tag[vector[j
]+1].childNodes[0].nodeValue+"</p>";
else{
var cadena =
opciones_tag[vector[j]+1].childNodes[0].nodeValue.replace("<p>",'');
132
cadena = cadena.replace("</p>",'');
opciones += "<p class=\"opciones\"><input
type=\"checkbox\" name=\""+ident+"\" value=\""+cadena+"\"
respondida=\""+respondidas[vector[j]]+"\"
norespondida=\""+norespondidas[vector[j]]+"\"/>"+cadena+"</p>";
}
}
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarMul("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/><input name=\"posponer"+ent+"\" type=\"button\"
value=\"Posponer\" class=\"boton\" onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarMul("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/>";
return opciones;
}
/*********************************************************************
**********
**
** Function: puntuarUni()
** Inputs: An identifier and an integer.
** Return: None.
**
** Description:
** It gets the score of a multiple choice question and only answer.
**
**********************************************************************
*********/
function puntuarUni(iden, ent){
var opt = document.getElementsByName(iden[0].name);
var points = 0;
for(var i=0; i<opt.length;i++){
if(opt[i].checked){
puntuacion +=
parseFloat(opt[i].getAttribute("puntos"));
points +=
parseFloat(opt[i].getAttribute("puntos"));
respuestas[ent] = opt[i].value;
}
opt[i].disabled = true;
}
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled =
true;
puntuacion = Math.round(puntuacion * 100) / 100;
if (points > 0)
smoke.alert("¡Enhorabuena! Has conseguido "+points+ "
puntos en esta pregunta. Tu puntuación total es de "+puntuacion+ "
puntos.");
133
else
smoke.alert("¡No te desanimes! ¡A por las siguientes
preguntas!");
actualiza(puntuacion, total);
puntosPreguntas[ent] = Math.round(points * 100) / 100;
var d = new Date();
var hora1 = d.getHours();
var min1 = d.getMinutes();
var sec1 = d.getSeconds();
if (hora1.length == 1)
hora1 = "0"+ hora1;
if (min1.length == 1)
min1 = "0"+ min1;
if (sec1.length == 1)
sec1 = "0"+ sec1;
latencias[ent] =
obtenLatencia(hora1+":"+min1+":"+sec1,horaInicio);
muestra(ent);
}
/*********************************************************************
**********
**
** Function: puntuarMul()
** Inputs: An identifier and an integer.
** Return: None.
**
** Description:
** It gets the score of a multiple choice question and multiple
answer.
**
**********************************************************************
*********/
function puntuarMul(iden, ent){
var opt = document.getElementsByName(iden[0].name);
var contestada = "";
var points = 0;
var count = 0;
for(var i=0; i<opt.length;i++){
if(opt[i].checked){
puntuacion +=
parseFloat(opt[i].getAttribute("respondida"));
points +=
parseFloat(opt[i].getAttribute("respondida"));
if (count==0)
contestada = opt[i].value;
else
contestada =
contestada.concat("[,]"+opt[i].value);
count++;
}
else {
puntuacion +=
parseFloat(opt[i].getAttribute("norespondida"));
points +=
parseFloat(opt[i].getAttribute("norespondida"));
}
opt[i].disabled = true;
134
}
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled =
true;
puntuacion = Math.round(puntuacion * 100) / 100;
if (points > 0)
smoke.alert("¡Enhorabuena! Has conseguido "+points+ "
puntos en esta pregunta. Sigue así. Tu puntuación total es de
"+puntuacion+ " puntos.");
else
smoke.alert("¡No te desanimes! ¡A por las siguientes
preguntas!");
actualiza(puntuacion, total);
respuestas[ent] = contestada;
puntosPreguntas[ent] = Math.round(points * 100) / 100;
var d = new Date();
var hora1 = d.getHours();
var min1 = d.getMinutes();
var sec1 = d.getSeconds();
if (hora1.length == 1)
hora1 = "0"+ hora1;
if (min1.length == 1)
min1 = "0"+ min1;
if (sec1.length == 1)
sec1 = "0"+ sec1;
latencias[ent] =
obtenLatencia(hora1+":"+min1+":"+sec1,horaInicio);
muestra(ent);
}
/*********************************************************************
**********
**
** Function: puntuarNum()
** Inputs: An identifier, an integer and two floats.
** Return: None.
**
** Description:
** It gets the score of a numeric question.
**
**********************************************************************
*********/
function puntuarNum(iden, ent, inf, sup){
var valor = parseFloat(iden.value);
var puntos =
document.getElementsByName(iden.name)[0].getAttribute("puntos");
var points = 0;
if(valor <= sup && valor >= inf){
puntuacion += parseFloat(puntos);
points += parseFloat(puntos);
}
//Deshabilitamos los botones
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled =
true;
135
document.getElementsByName(iden.name)[0].disabled = true;
puntuacion = Math.round(puntuacion * 100) / 100;
if (points > 0)
smoke.alert("¡Enhorabuena! Has conseguido "+points+ "
puntos en esta pregunta. Tu puntuación total es de "+puntuacion+ "
puntos.");
else
smoke.alert("¡No te desanimes! ¡A por las siguientes
preguntas!");
respuestas[ent] = valor;
puntosPreguntas[ent] = Math.round(points * 100) / 100;
var d = new Date();
var hora1 = d.getHours();
var min1 = d.getMinutes();
var sec1 = d.getSeconds();
if (hora1.length == 1)
hora1 = "0"+ hora1;
if (min1.length == 1)
min1 = "0"+ min1;
if (sec1.length == 1)
sec1 = "0"+ sec1;
latencias[ent] =
obtenLatencia(hora1+":"+min1+":"+sec1,horaInicio);
actualiza(puntuacion, total);
muestra(ent);
}
/*********************************************************************
**********
**
** Function: puntuaHuecos()
** Inputs: An identifier and an integer.
** Return: None.
**
** Description:
** It gets the score of a cloze question.
**
**********************************************************************
*********/
function puntuaHuecos(iden, ent){
var count = 0;
var points = 0;
var contestada = "";
var contestada2 = "";
if (typeof iden.name !== "undefined"){
//Obtenemos los huecos
var opcs = document.getElementsByName(iden.name);
for(var i = 0; i < opcs.length; i++){
//Obtenemos el tipo del hueco
var tipo = opcs[i].getAttribute("tipo");
if (tipo.localeCompare("Decimal") == 0){
var valor = parseFloat(opcs[i].value);
var puntos = opcs[i].getAttribute("puntos");
136
var sup =
parseFloat(opcs[i].getAttribute("sup"));
var inf =
parseFloat(opcs[i].getAttribute("inf"));
if(valor<=sup && valor>=inf){
puntuacion += parseFloat(puntos);
points += parseFloat(puntos);
if (count == 0){
contestada += inf +"[:]"+ sup;
contestada2 += inf +"[:]"+ sup;
} else {
contestada = contestada.concat("[,]",
inf +"[:]"+ sup);
contestada2 =
contestada.concat("[,]", inf +"[:]"+ sup);
}
count++;
} else {
if (count == 0){
contestada += valor +"[:]"+ valor;
contestada2 += valor +"[:]"+ valor;
} else {
contestada = contestada.concat("[,]",
valor +"[:]"+ valor);
contestada2 =
contestada.concat("[,]", valor +"[:]"+ valor);
}
count++;
}
} else
if (tipo.localeCompare("Lista") == 0){
if (count==0){
contestada +=
opcs[i].options[opcs[i].options.selectedIndex].value;
contestada2 +=
auxiliarResp[opcs[i].options.selectedIndex];
} else{
contestada =
contestada.concat("[,]",opcs[i].options[opcs[i].options.selectedIndex]
.value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[opcs[i].options.selectedIndex]);
}
puntuacion +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
points +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
} else
if (tipo.localeCompare("String") == 0){
//Obtenemos la sensibilidad del
hueco
var sensibilidad =
opcs[i].getAttribute("sensibilidad");
//Obtenemos los puntos de las
opciones
var puntos =
opcs[i].getAttribute("puntos").split(",");
137
//Obtenemos los opciones de la
pregunta
var opciones =
opcs[i].getAttribute("sol").split(",");
//Si no es sensible a mayúsculas
if(sensibilidad == "ci"){
if (count==0){
contestada += iden.value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value.toLowerCase()
== opciones[k].toLowerCase()){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
break;
}
}
} else
//Si es sensible a mayúsculas
if(sensibilidad == "cs"){
if (count==0){
contestada += iden.value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden.value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value ==
opciones[k]){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
}
}
}
//Si tiene distancia de Levenshtein
else {
138
var dist =
parseInt(sensibilidad.substr(1,1)); //Valor de distancia de
Levenshtein para esta pregunta
var levens = [];
for(var k = 0; k < opciones.length -
1; k++){
levens [k] =
getEditDistance(opcs[i].value, opciones[k]); //Distancia Levenshtein
entre las dos palabras
}
var min = Math.min.apply(null,
levens); //Mínimo del vector
var indice = levens.indexOf(min,0);
//Índice del valor mínimo encontrado
if (count==0){
contestada +=
opcs[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",opcs[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
if(min == dist){
puntuacion +=
parseFloat(puntos[indice]);
points +=
parseFloat(puntos[indice]);
}
}
}
//Deshabilitamos el hueco
opcs[i].disabled = true;
}
} else {
//Obtenemos los huecos
var opcs = document.getElementsByName(iden[0].name);
for(var i = 0; i < opcs.length; i++){
//Obtenemos el tipo de la pregunta
var tipo = opcs[i].getAttribute("tipo");
if (tipo.localeCompare("Decimal") == 0){
var valor = parseFloat(opcs[i].value);
var puntos = opcs[i].getAttribute("puntos");
var sup = parseFloat(opcs[i].getAttribute("sup"));
var inf = parseFloat(opcs[i].getAttribute("inf"));
if(valor<=sup && valor>=inf){
puntuacion += parseFloat(puntos);
points += parseFloat(puntos);
if (count == 0){
contestada += inf +"[:]"+ sup;
contestada2 += inf +"[:]"+ sup;
} else {
139
contestada = contestada.concat("[,]",
inf +"[:]"+ sup);
contestada2 =
contestada.concat("[,]", inf +"[:]"+ sup);
}
count++;
} else {
if (count == 0){
contestada += valor +"[:]"+ valor;
contestada2 += valor +"[:]"+ valor;
} else {
contestada = contestada.concat("[,]",
valor +"[:]"+ valor);
contestada2 =
contestada2.concat("[,]", valor +"[:]"+ valor);
}
count++;
}
} else
if (tipo.localeCompare("String") == 0){
//Obtenemos la sensibilidad del hueco
var sensibilidad =
opcs[i].getAttribute("sensibilidad");
//Obtenemos los puntos de las
opciones
var puntos =
opcs[i].getAttribute("puntos").split(",");
//Obtenemos los opciones de la
pregunta
var opciones =
opcs[i].getAttribute("sol").split(",");
//Si no es sensible a mayúsculas
if(sensibilidad == "ci"){
if (count==0){
contestada +=
iden[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value.toLowerCase()
== opciones[k].toLowerCase()){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
break;
}
}
} else
140
//Si es sensible a mayúsculas
if(sensibilidad == "cs"){
if (count==0){
contestada +=
iden[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value ==
opciones[k]){
puntuacion+=parseFloat(puntos[k]);
points+=parseFloat(puntos[k]);
}
}
}
//Si tiene distancia de Levenshtein
else {
var dist =
parseInt(sensibilidad.substr(1,1)); //Valor de distancia de
Levenshtein para esta pregunta
var levens = [];
for(var k = 0; k < opciones.length -
1; k++){
levens [k] =
getEditDistance(opcs[i].value, opciones[k]); //Distancia Levenshtein
entre las dos palabras
}
var min = Math.min.apply(null,
levens); //Mínimo del vector
var indice = levens.indexOf(min,0);
//Índice del valor mínimo encontrado
if (count==0){
contestada +=
opcs[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",opcs[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
if(min <= dist){
puntuacion+=parseFloat(puntos[indice]);
points+=parseFloat(puntos[indice]);
}
}
141
} else {
if (count==0){
contestada +=
opcs[i].options[opcs[i].options.selectedIndex].value;
contestada2 +=
auxiliarResp[opcs[i].options.selectedIndex];
} else{
contestada =
contestada.concat("[,]",opcs[i].options[opcs[i].options.selectedIndex]
.value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[opcs[i].options.selectedIndex]);
}
count++;
puntuacion +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
points +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
}
//Deshabilitamos el hueco
opcs[i].disabled = true;
}
}
if (normalize(contestada.replace(/\s/g,"_")).length > 66)
contestada = contestada2;
//Deshabilitamos los botones
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled =
true;
puntuacion = Math.round(puntuacion * 100) / 100;
if (points > 0)
smoke.alert("¡Enhorabuena! Has conseguido "+points+ "
puntos en esta pregunta. Tu puntuación total es de "+puntuacion+ "
puntos.");
else
smoke.alert("¡No te desanimes! ¡A por las siguientes
preguntas!");
//Actualizamos la puntuación y mostramos la siguiente
pregunta
actualiza(puntuacion, total);
respuestas[ent] = contestada;
puntosPreguntas[ent] = Math.round(points * 100) / 100;
var d = new Date();
var hora1 = d.getHours();
var min1 = d.getMinutes();
var sec1 = d.getSeconds();
if (hora1.length == 1)
hora1 = "0"+ hora1;
if (min1.length == 1)
min1 = "0"+ min1;
if (sec1.length == 1)
142
sec1 = "0"+ sec1;
latencias[ent] =
obtenLatencia(hora1+":"+min1+":"+sec1,horaInicio);
muestra(ent);
}
/*********************************************************************
**********
**
** Function: actualiza()
** Inputs: The score of the learner and the total number of available
points of the test.
** Return: None.
**
** Description:
** It updates the score and puts it into the square situated in the
top right of the page.
**
**********************************************************************
*********/
function actualiza (pts, sum){
document.getElementById("tupuntuacion").value = pts+"/"+sum;
}
/*********************************************************************
**********
**
** Function: normalize()
** Inputs: A string with special signs.
** Return: The same string without any special sign.
**
** Description:
** It normalizes a string.
**
**********************************************************************
*********/
var normalize = (function() {
var from = "ÃÀÁÄÂÈÉËÊÌÍÏÎÒÓÖÔÙÚÜÛãàáäâèéëêìíïîòóöôùúüûÑñÇç",
to = "AAAAAEEEEIIIIOOOOUUUUaaaaaeeeeiiiioooouuuunncc",
mapping = {};
for(var i = 0, j = from.length; i < j; i++ )
mapping[ from.charAt( i ) ] = to.charAt( i );
return function( str ) {
var ret = [];
for( var i = 0, j = str.length; i < j; i++ ) {
var c = str.charAt( i );
if( mapping.hasOwnProperty( str.charAt( i ) ) )
ret.push( mapping[ c ] );
else
ret.push( c );
}
return ret.join( '' );
}
})();
/*********************************************************************
**********
**
143
** Function: muestra()
** Inputs: The number of the question and the total number of
questions of the test.
** Return: None.
**
** Description:
** It shows the next question and introduces it in the LMS.
**
**********************************************************************
*********/
function muestra (num){
var next = num + 1;
var patt = "";
if (next < numeroPr){
var aux = "div"+next;
document.getElementById(aux).style.display = "block";
}
var f2=new Date();
var hora2 = f2.getHours();
var min2 = f2.getMinutes();
var sec2 = f2.getSeconds();
if (hora2.length == 1)
hora2 = "0"+ hora2;
if (min2.length == 1)
min2 = "0"+ min2;
if (sec2.length == 1)
sec2 = "0"+ sec2;
horaInicio = hora2+":"+min2+":"+sec2;
}
/*********************************************************************
**********
**
** Function: getEditDistance()
** Inputs: Two strings.
** Return: An integer that represents the Levenshtein distance
between the two strings.
**
** Description:
** It gets the Levenshtein distance between the two strings given.
**
**********************************************************************
*********/
function getEditDistance (a, b){
if(a.length == 0) return b.length;
if(b.length == 0) return a.length;
var matrix = [];
// increment along the first column of each row
var i;
for(i = 0; i <= b.length; i++){
matrix[i] = [i];
}
// increment each column in the first row
var j;
for(j = 0; j <= a.length; j++){
matrix[0][j] = j;
}
144
// Fill in the rest of the matrix
for(i = 1; i <= b.length; i++){
for(j = 1; j <= a.length; j++){
if(b.charAt(i-1) == a.charAt(j-1)){
matrix[i][j] = matrix[i-1][j-1];
} else {
matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
Math.min(matrix[i][j-1] + 1, //
insertion
matrix[i-1][j] + 1)); //
deletion
}
}
}
return matrix[b.length][a.length];
};
/*********************************************************************
**********
**
** Function: obtieneTiempo()
** Inputs: Two strings of time.
** Return: The difference between both strings.
**
** Description:
** It calculates the difference between both strings.
**
**********************************************************************
*********/
function obtieneTiempo(cad1, cad2){
var annio = parseInt(cad2.substring(0,4)) -
parseInt(cad1.substring(0,4));
var mes = parseInt(cad2.substring(5,7)) -
parseInt(cad1.substring(5,7));
var dias = parseInt(cad2.substring(8,10)) -
parseInt(cad1.substring(8,10));
var horas = parseInt(cad2.substring(11,13)) -
parseInt(cad1.substring(11,13));
var minutos = parseInt(cad2.substring(14,16)) -
parseInt(cad1.substring(14,16));
var segundos = parseInt(cad2.substring(17,19)) -
parseInt(cad1.substring(17,19));
var tiempo = "";
if (segundos < 0) {
minutos--;
segundos = 60 + segundos;
}
if (minutos < 0) {
horas--;
minutos = 60 + minutos;
}
if (horas < 0) {
dias--;
horas = 24 + horas;
}
if (dias < 0) {
mes--;
dias = 30 + dias;
}
145
if (mes < 0) {
annio--;
mes = 12 + mes;
}
if (segundos.toString().length < 2){
segundos = "0"+segundos;
}
if (minutos.toString().length < 2){
minutos = "0"+minutos;
}
if (horas.toString().length < 2){
horas = "0"+horas;
}
if (annio != 0){
if (annio != 1 && mes != 1)
tiempo = annio +" años, "+ mes + " meses, " +
dias +" días y " + horas+":"+minutos+":"+segundos;
else if (annio != 1 && mes == 1)
tiempo = annio +" años, "+ mes + " mes, " + dias
+" días y " + horas+":"+minutos+":"+segundos;
else if (annio == 1 && mes != 1)
tiempo = annio +" año, "+ mes + " meses, " + dias
+" días y " + horas+":"+minutos+":"+segundos;
else if (annio == 1 && mes == 1)
tiempo = annio +" año, "+ mes + " mes, " + dias
+" días y " + horas+":"+minutos+":"+segundos;
} else if (mes != 0){
if (mes != 1 && dias != 1)
tiempo = mes + " meses, " + dias +" días y " +
horas+":"+minutos+":"+segundos;
else if (mes != 1 && dias == 1)
tiempo = mes + " meses, " + dias +" día y " +
horas+":"+minutos+":"+segundos;
else if (mes == 1 && dias != 1)
tiempo = mes + " mes, " + dias +" días y " +
horas+":"+minutos+":"+segundos;
else if (mes == 1 && dias == 1)
tiempo = mes + " mes, " + dias +" día y " +
horas+":"+minutos+":"+segundos;
} else if (dias != 0){
if (dias != 1)
tiempo = dias +" días y " +
horas+":"+minutos+":"+segundos;
else
tiempo = dias +" día y " +
horas+":"+minutos+":"+segundos;
} else
tiempo = horas+":"+minutos+":"+segundos;
return tiempo;
}
/*********************************************************************
**********
**
** Function: deshabilitar()
** Inputs: None.
146
** Return: None.
**
** Description:
** It disables the questions buttons.
**
**********************************************************************
*********/
function deshabilitar (){
for (var i = 0; i < contador; i++){
document.getElementsByName("valida"+i)[0].disabled =
true;
document.getElementsByName("borra"+i)[0].disabled =
true;
if (i + 1 != contador)
document.getElementsByName("posponer"+i)[0].disabled = true;
}
}
/*********************************************************************
**********
**
** Function: ponRespuestas()
** Inputs: An integer depending on the rows of the table.
** Return: None.
**
** Description:
** It adds the answers given by the learner in a summary table.
**
**********************************************************************
*********/
function ponRespuestas(entero){
for (var i = 0; i < contador; i++){
var res = respuestas[i];
var lat = latencias[i];
if (lat == "undefined")
lat = "Sin latencia";
var puntos = puntosPreguntas[i];
var aux = entero + i;
var preg = "PE"+ aux;
document.getElementById("PE"+aux).innerHTML = res;
if (puntosPreguntas[i] != "undefined")
document.getElementById("PE"+aux).innerHTML += " " +
puntosPreguntas[i] + " puntos";
else
document.getElementById("PE"+aux).innerHTML += " 0
puntos";
document.getElementById("PE"+aux).innerHTML += " "+lat;
}
}
8.5 Librería JavaScript creaPreguntas12.js
/* creaPreguntas.js JavaScript library by Sergio Díaz
Librería desarrollada por Sergio Díaz Fuentes. Se permite cualquier
explotación de la obra, incluyendo una finalidad comercial, así como
la creación de obras derivadas, la distribución de las cuales también
está permitida sin ninguna restricción.
*/
147
var puntosPreguntas = new Array ();
var puntuacion = 0;
var total = 0;
var longitudPreg = 0;
var auxiliarCont = 0;
var contador = 0;
var auxiliarResp = ["a","b","c","d","e","f","g","h","i","j"];
var miTiempo="";
var latencias = new Array ();
var f=new Date();
var hora = f.getHours();
var min = f.getMinutes();
var sec = f.getSeconds();
if (hora < 10)
hora = "0"+ hora;
if (min < 10)
min = "0"+ min;
if (sec < 10)
sec = "0"+ sec;
var horaInicio = hora+":"+min+":"+sec;
var respuestas = new Array ();
rellenaPuntos();
/*********************************************************************
**********
**
** Function: loadXMLDoc()
** Inputs: The name of the xml file in which the questions are, the
number of questions of the test, time for the test, random questions
and random options.
** Return: The number of requested test questions presented randomly.
**
** Description:
** Requests the xml file and presents the questions.
**
**********************************************************************
*********/
function loadXMLDoc (xmlName,numPreguntas,time,aleaPr, aleaOp) {
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
// code for older browsers
xmlhttp = new ActiveXObject("Microsoft.XMLDOM");
}
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var xmlDoc = xmlhttp.responseXML;
if (xmlDoc != null){
ponDiv();
ponPuntuacion();
if (parseInt(time) > 0){
if (time.toString().length < 2)
var t = "0" + time + ":00";
else
var t = time + ":00";
ponTime(t);
148
}
if (time != 0)
miTiempo = setInterval(
function(){cuentAtras()}, 1000);
// En la variable div_preguntas obtenemos el
contenedor div con el id 'preguntas'
var div_preguntas =
document.getElementById('preguntas');
// Obtenemos la lista de preguntas
var preguntas_set =
xmlDoc.getElementsByTagName("questestinterop")[0].getElementsByTagName
("item");
longitudPreg = preguntas_set.length;
// Comprobamos que el número de preguntas sea
igual o inferior al número de preguntas disponibles
if (numPreguntas > preguntas_set.length){
numeroPr = preguntas_set.length;
}
//Mezclamos un array auxiliar que nos servirá
para obtener las preguntas de forma aleatoria
if (aleaPr.localeCompare("si") == 0)
var arrayaux = arrayAlea(preguntas_set);
else
var arrayaux =
obtenArray(preguntas_set.length);
while (auxiliarCont < numeroPr){
// Obtenemos el título de la pregunta
var titulo =
preguntas_set[arrayaux[auxiliarCont]].getAttribute("title");
var
descripcion=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName
("mattext")[0].childNodes[0].nodeValue;
if (descripcion.indexOf("<p>") == -1)
descripcion = "<p>"+descripcion+"</p>";
//Obtenemos el tipo de la pregunta
var
tipo=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("qtime
tadatafield")[1].getElementsByTagName("fieldentry")[0].childNodes[0].n
odeValue;
//Obtenemos el identificador de la pregunta
var
id=preguntas_set[arrayaux[auxiliarCont]].getAttribute("ident");
//Inicializamos un par de flags auxiliares a
cero
var flag=0;
var flagE = 0;
var patt = "";
149
//Inicializamos las opciones de la pregunta a
un string vacío
var opciones="";
if(tipo=="SINGLE CHOICE QUESTION"){
// Obtenemos las opciones de la pregunta
var opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
//Obtenemos los puntos de cada opcion
var
puntos=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("set
var");
opciones+=unica(opciones_set,id,puntos,contador);
//patt = obtieneCorrecta
(opciones_set,puntos);
total+=sumaPuntos(puntos, tipo, 0);
flagE = 1;
} else
if(tipo=="NUMERIC QUESTION"){
//Obtenemos los límites superior e
inferior para la pregunta
var
sup=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("varlte
")[0].childNodes[0].nodeValue;
var
inf=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("vargte
")[0].childNodes[0].nodeValue;
//Obtenemos el numero máximo de
caracteres permitidos
var
maxcar=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("ren
der_fib")[0].getAttribute("maxchars");
//Obtenemos los puntos en caso de
acierto
var
puntos=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("set
var")[0].childNodes[0].nodeValue;
opciones+=numerica(id,sup,inf,puntos,maxcar,contador);
total+=parseFloat(puntos);
flagE = 1;
} else
if(tipo=="MULTIPLE CHOICE
QUESTION"){
// Obtenemos las opciones
de la pregunta
var opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
//Obtenemos los puntos de
cada opcion
var
puntos=preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("set
var");
opciones+=multiple(opciones_set,id,puntos,contador);
total+=sumaPuntos(puntos,
tipo, 0);
flagE = 1;
} else
150
//https://social.msdn.microsoft.com/Forums/es-ES/119aa457-11f6-
408b-9c70-d368deabd597/javascript-saber-si-una-letra-esta-en-una-
cadena-de-caracteres?forum=netfxwebes
if(tipo=="CLOZE
QUESTION"){
var opciones_set =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("mattext");
var huecosString =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("response_s
tr");
var huecosNum =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("response_n
um");
var oposibles =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("varequal")
; //Opciones posibles
var puntos =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("setvar");
//Puntos de las distintas opciones
var nHuecos =
huecosString.length + huecosNum.length; //Número total de huecos
var tagNot =
preguntas_set[arrayaux[auxiliarCont]].getElementsByTagName("not");
var arr =
[].slice.call(oposibles);
var arrMat =
[].slice.call(opciones_set);
if (tagNot.length !=
0){
var osobrantes =
tagNot[0].getElementsByTagName("varequal").length;
arr.length =
oposibles.length - osobrantes * 2;
arrMat.length =
opciones_set.length - osobrantes;
}
var gapsT = 0;
var gapsN = 0;
descripcion="";
total+=sumaPuntos(puntos,tipo, oposibles);
var w = 0;
do{
if
(arrMat[w].childNodes[0].nodeValue.indexOf("<p>") != -1)
descripcion += arrMat[w].childNodes[0].nodeValue; //La primera
nunca será un hueco
else{
if
(w==0)
descripcion += "<p>"
+arrMat[w].childNodes[0].nodeValue.replace(/<br \/>/g,
'').replace(/<\/?span[^>]*>/g,"");
else if (w == arrMat.length - nHuecos - 1)
151
descripcion += arrMat[w].childNodes[0].nodeValue.replace(/<br
\/>/g, '').replace(/<\/?span[^>]*>/g,"") +"</p>";
else
descripcion += arrMat[w].childNodes[0].nodeValue.replace(/<br
\/>/g, '').replace(/<\/?span[^>]*>/g,"");
}
if
(gapsT + gapsN < nHuecos){
if
(huecosString.length != 0 && gapsT < huecosString.length){
if (huecosString[gapsT].getAttribute("ident").indexOf(gapsT +
gapsN) != -1){
if
(huecosString[gapsT].getElementsByTagName("render_choice").length ==
1){
descripcion +=
creaDesplegable(id,arr,puntos,gapsT + gapsN);
w = w +
huecosString[gapsT].getElementsByTagName("render_choice")[0].getElemen
tsByTagName("mattext").length;
}
else{
descripcion+="<input type=\"text\"
name=\""+id+"\" class=\"huecoT\"
tipo=\""+huecosString[gapsT].getElementsByTagName("render_fib")[0].get
Attribute("fibtype")+"\"
sensibilidad=\""+preguntas_set[arrayaux[auxiliarCont]].getElementsByTa
gName("fieldentry")[5].childNodes[0].nodeValue+"\" puntos=\"\"
sol=\"\"
size=\""+huecosString[gapsT].getElementsByTagName("render_fib")[0].get
Attribute("columns")+"\"
maxlength=\""+parseInt(huecosString[gapsT].getElementsByTagName("rende
r_fib")[0].getAttribute("columns"))+"\"/>";
}
gapsT++;
w++;
} else if
(huecosNum[gapsN].getAttribute("ident").indexOf(gapsT + gapsN) != -1){
var min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
innumber");
if (min == null)
152
min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
axnumber");
descripcion += "<input type=\"text\"
name=\""+id+"\" class=\"huecoT\"
tipo=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAtt
ribute("fibtype")+"\" inf=\""+min+"\"
sup=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttr
ibute("maxnumber")+"\" puntos=\"\" size=\"3\"
maxlength=\""+parseInt(huecosNum[gapsN].getElementsByTagName("render_f
ib")[0].getAttribute("columns"))+"\"/>";
gapsN++;
w++;
}
}
else if (huecosNum[gapsN].getAttribute("ident").indexOf(gapsT +
gapsN) != -1){
var min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
innumber");
if (min == null)
min =
huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttribute("m
axnumber");
descripcion += "<input type=\"text\" name=\""+id+"\"
class=\"huecoT\"
tipo=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAtt
ribute("fibtype")+"\" inf=\""+min+"\"
sup=\""+huecosNum[gapsN].getElementsByTagName("render_fib")[0].getAttr
ibute("maxnumber")+"\" puntos=\"\" size=\"3\"
maxlength=\""+parseInt(huecosNum[gapsN].getElementsByTagName("render_f
ib")[0].getAttribute("columns"))+"\"/>";
gapsN++;
w++;
}
}
else
w++;
} while (w <
arrMat.length - nHuecos);
if (contador + 1 != numeroPr &&
auxiliarCont + 1 < preguntas_set.length)
descripcion+="<input
name=\"valida"+contador+"\" type=\"button\" value=\"Validar\"
onclick=\"puntuaHuecos("+id+","+contador+")\" class=\"boton\"/><input
name=\"borra"+contador+"\" type=\"reset\" value=\"Borrar\"
153
class=\"boton\"/><input name=\"posponer"+contador+"\" type=\"button\"
value=\"Posponer\" class=\"boton\"
onclick=\"muestra("+contador+")\"/></form>";
else
descripcion+="<input
name=\"valida"+contador+"\" type=\"button\" value=\"Validar\"
onclick=\"puntuaHuecos("+id+","+contador+")\" class=\"boton\"/><input
name=\"borra"+contador+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/></form>";
// Modificamos el contenido
html del contenedor div
if (contador==0)
div_preguntas.innerHTML +=
"<div id=\"div"+contador+"\" style=\"display:block;\"><form
id=\"formulario"+contador+"\" tipo=\""+tipo+"\"><h3>" +
parseInt(contador+1) + ". " + titulo + "</h3>" + descripcion
+"</form></div>";
else
div_preguntas.innerHTML +=
"<div id=\"div"+contador+"\" style=\"display:none;\"><form
id=\"formulario"+contador+"\" tipo=\""+tipo+"\"><h3>" +
parseInt(contador+1) + ". " + titulo + "</h3>" + descripcion +
"</form></div>";
for (var q = 0; q < nHuecos;
q++){
for (var k = 0; k <
oposibles.length; k++){
if
(oposibles[k].getAttribute("respident").indexOf(q) != -1 &&
document.getElementsByName(id)[q].getAttribute("tipo")){
if
(document.getElementsByName(id)[q].getAttribute("tipo").localeCompare(
"String") == 0){
var attAnterior =
document.getElementsByName(id)[q].getAttribute("sol");
var puntAnterior =
document.getElementsByName(id)[q].getAttribute("puntos");
document.getElementsByName(id)[q].setAttribute("sol",attAnterior+op
osibles[k].childNodes[0].nodeValue+",");
document.getElementsByName(id)[q].setAttribute("puntos",puntAnterio
r + puntos[k].childNodes[0].nodeValue+",");
}
else if
(document.getElementsByName(id)[q].getAttribute("tipo").localeCompare(
"Decimal") == 0)
document.getElementsByName(id)[q].setAttribute("puntos",
puntos[k].childNodes[0].nodeValue);
}
}
}
if (contador ==0){
patt =
getCorrectaInv(id,tipo);
154
miRTE.setInteractionsId(contador,id);
miRTE.setInteractionsType(contador,"fill-in");
miRTE.setInteractionsTs(contador, timeStamp("1.2"));
miRTE.setInteractionsWeight(contador, 1);
miRTE.setInteractionsCorrectRespPattern(contador, 0,
patt.toLowerCase());
}
flag = 1; //Este
flag nos permite que no se repita la pregunta de nuevo en el
div_preguntas
flagE = 1;
}
// Modificamos el contenido html del contenedor div
if(flag == 0 && flagE == 1){
if (contador == 0){
div_preguntas.innerHTML += "<div
id=\"div"+contador+"\" style=\"display:block;\"><form
id=\"formulario"+contador+"\" tipo=\""+tipo+"\"><h3>" +
parseInt(contador+1) + ". " + titulo + "</h3>" + descripcion +
opciones+"</form></div>";
patt = getCorrectaInv(id, tipo);
switch (tipo){
case "SINGLE CHOICE QUESTION":
miRTE.setInteractionsId(contador,id);
miRTE.setInteractionsType(contador,"choice");
miRTE.setInteractionsTs(contador,
timeStamp("1.2"));
miRTE.setInteractionsWeight(contador,
1);
miRTE.setInteractionsCorrectRespPattern(contador, 0, patt);
break;
case "MULTIPLE CHOICE QUESTION":
miRTE.setInteractionsId(contador,id);
miRTE.setInteractionsType(contador,"choice");
miRTE.setInteractionsTs(contador,
timeStamp("1.2"));
miRTE.setInteractionsWeight(contador,
1);
miRTE.setInteractionsCorrectRespPattern(contador, 0, patt);
break;
case "NUMERIC QUESTION":
miRTE.setInteractionsId(contador,id);
miRTE.setInteractionsType(contador,"numeric");
miRTE.setInteractionsTs(contador,
timeStamp("1.2"));
miRTE.setInteractionsWeight(contador,
1);
155
miRTE.setInteractionsCorrectRespPattern(contador, 0, patt);
break;
default:
console.log("No es un tipo
válido de pregunta");
}
} else
div_preguntas.innerHTML += "<div
id=\"div"+contador+"\" style=\"display:none;\"><form
id=\"formulario"+contador+"\" tipo=\""+tipo+"\"><h3>" +
parseInt(contador+1) + ". " + titulo + "</h3>" + descripcion +
opciones+"</form></div>";
}
if(flagE == 1){
auxiliarCont++;
contador++;
} else{
auxiliarCont++;
}
if (auxiliarCont >= preguntas_set.length)
break;
}
//ponBoton();
actualiza(puntuacion,total);
}
}
};
xmlhttp.open("GET", xmlName, true);
xmlhttp.send();
}
/*********************************************************************
**********
**
** Function: ponDiv()
** Inputs: None.
** Return: None.
**
** Description:
** It adds a div tag to the html page.
**
**********************************************************************
*********/
function ponDiv(){
var midiv = document.createElement("div");
midiv.setAttribute("id","preguntas");
midiv.setAttribute("class","desplazado");
//document.body.appendChild(midiv); // Lo pones en "body", si
quieres ponerlo dentro de algún id en concreto usas
document.getElementById('donde lo quiero poner').appendChild(midiv);
document.getElementById('tabla').appendChild(midiv);
}
/*********************************************************************
**********
**
** Function: ponPuntuacion()
156
** Inputs: None.
** Return: None.
**
** Description:
** It adds a puntuation div tag to the html page.
**
**********************************************************************
*********/
function ponPuntuacion(){
var midiv = document.createElement("div");
midiv.setAttribute("class","cuadroFijo");
midiv.setAttribute("id","divPuntuacion");
var centr = document.createElement("center");
var cuadro = document.createElement("input");
cuadro.setAttribute("type","text");
cuadro.setAttribute("value","");
cuadro.setAttribute("id","tupuntuacion");
cuadro.setAttribute("size","3");
cuadro.setAttribute("maxlength","3");
cuadro.setAttribute("readonly","true");
cuadro.setAttribute("class","altura puntos");
centr.appendChild(cuadro);
midiv.appendChild(centr);
document.body.appendChild(midiv);
}
/*********************************************************************
**********
**
** Function: ponTime()
** Inputs: The time in minutes of test performance.
** Return: None.
**
** Description:
** It adds a time div tag to the html page.
**
**********************************************************************
*********/
function ponTime(time){
var midiv = document.createElement("div");
midiv.setAttribute("id","divTiempo");
midiv.setAttribute("class","cuadroFijoT");
var para = document.createElement("p");
para.setAttribute("align","center");
var t = document.createTextNode("Tiempo");
para.appendChild(t);
var centrado = document.createElement("center");
var cuadro = document.createElement("input");
cuadro.setAttribute("type","text");
cuadro.setAttribute("id","tutiempo");
cuadro.setAttribute("value",time); cuadro.setAttribute("size","3");
cuadro.setAttribute("class","altura puntos");
cuadro.setAttribute("readonly","true");
cuadro.setAttribute("maxlength","3");
centrado.appendChild(cuadro);
midiv.appendChild(para);
midiv.appendChild(centrado);
document.body.appendChild(midiv);
}
157
/*********************************************************************
**********
**
** Function: cuentAtras()
** Inputs: None.
** Return: None.
**
** Description:
** It decrements the clock.
**
**********************************************************************
*********/
function cuentAtras(){
var cad = document.getElementById("tutiempo").value;
var minutos = parseInt(cad.substring(0,2));
var segundos = parseInt(cad.substring(3,cad.length));
segundos--;
if (segundos < 0){
minutos--;
segundos = 60 + segundos;
}
if (minutos < 0){
clearInterval(miTiempo);
deshabilitar();
smoke.alert("Lo sentimos. Se ha acabado tu tiempo para
responder");
} else{
if (minutos.toString().length < 2)
minutos = "0" + minutos;
if (segundos.toString().length < 2)
segundos = "0" + segundos;
document.getElementById("tutiempo").value = minutos
+":"+segundos;
}
}
/*********************************************************************
**********
**
** Function: ponBoton()
** Inputs: None.
** Return: None.
**
** Description:
** It adds a button to the html page to correct questions.
**
**********************************************************************
*********/
function ponBoton(){
// En la variable div_preguntas obtenemos el contenedor div
con el id 'preguntas'
var div_preguntas = document.getElementById('preguntas');
div_preguntas.innerHTML += "<input type=\"button\" name=\"test\"
value=\"Corregir todas\" onclick=\"corrige()\" class=\"corrige\"/>";
}
/*********************************************************************
**********
**
158
** Function: arrayAlea()
** Inputs: An array of questions.
** Return: An array with unordered values depending of the length of
de questions array.
**
** Description:
** It mixes a vector of sorted values.
**
**********************************************************************
*********/
function arrayAlea(array){
var unArray=[];
for(var z=0;z<array.length;z++){
unArray[z]=z;
}
fisher_yates(unArray);
return unArray;
}
/*********************************************************************
**********
**
** Function: obtenArray()
** Inputs: An integer.
** Return: An array of ordered values with variable length depending
of the integer.
**
** Description:
** It creates an ordered array.
**
**********************************************************************
*********/
function obtenArray(entero){
var unArray=[];
for(var z=0;z<entero;z++){
unArray[z]=z;
}
return unArray;
}
/*********************************************************************
**********
**
** Function: arrayAleaEnt()
** Inputs: An integer.
** Return: An array with unordered values depending of the integer.
**
** Description:
** It mixes a vector of sorted values.
**
**********************************************************************
*********/
function arrayAleaEnt(entero){
var unArray=[];
for(var z=0;z<entero;z++){
unArray[z]=z;
}
fisher_yates(unArray);
return unArray;
}
159
/*********************************************************************
**********
**
** Function: rellenaPuntos()
** Inputs: None.
** Return: None.
**
** Description:
** It initializes the points vector called puntosPreguntas.
**
**********************************************************************
*********/
function rellenaPuntos(){
for (var j=0; j < numeroPr; j++){
puntosPreguntas[j] = 0;
}
}
/*********************************************************************
**********
**
** Function: fisher_yates()
** Inputs: An array of sorted values.
** Return: The function returns the untidy vector.
**
** Description:
** It mixes a vector of sorted values according to Fisher-Yates
algorithm.
**
**********************************************************************
*********/
function fisher_yates(array){
var i=array.length;
while(i--){
var j=Math.floor( Math.random() * (i+1) );
var tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
}
/*********************************************************************
**********
**
** Function: sumaPuntos()
** Inputs: An array with the points of a question and the type of the
question.
** Return: The maximum points for this question.
**
** Description:
** It calculates the maximum number of points for this question..
**
**********************************************************************
*********/
function sumaPuntos(pts,tipo,ngaps){
var suma= 0;
var cont = 0;
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
var arr = [];
for(var z=0;z<pts.length;z++){
arr[z] = parseFloat(pts[z].childNodes[0].nodeValue);
}
160
suma+=Math.max.apply(null, arr);
} else if (tipo.localeCompare("CLOZE QUESTION") == 0){
suma += getPuntos(pts,ngaps);
} else {
for(var z=0;z<pts.length;z++){
if (parseFloat(pts[z].childNodes[0].nodeValue) > 0)
suma+=parseFloat(pts[z].childNodes[0].nodeValue);
}
}
return suma;
}
/*********************************************************************
**********
**
** Function: getPuntos()
** Inputs: An array with the points of a question and the number of
gaps.
** Return: The maximum points for this question.
**
** Description:
** It calculates the maximum number of points for this cloze question.
**
**********************************************************************
*********/
function getPuntos(pts,ngaps){
cont = 0;
cont2 = 0;
z=0;
suma = 0;
var arr = [];
do{
if (ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) > 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
else if (ngaps[z].getAttribute("respident").indexOf(cont) ==
-1){
suma+=Math.max.apply(null, arr);
arr = [];
cont++;
cont2 = 0;
if
(ngaps[z].getAttribute("respident").indexOf(cont) != -1 &&
parseFloat(pts[z].childNodes[0].nodeValue) > 0){
arr[cont2] =
parseFloat(pts[z].childNodes[0].nodeValue);
cont2++;
}
}
z++;
if (z == pts.length)
suma+=Math.max.apply(null, arr);
} while (z < pts.length);
return suma;
}
/*********************************************************************
**********
161
**
** Function: creaDesplegable()
** Inputs: The identifier, the options of the cloze question, the
points of the question and an integer that represents the order of the
question.
** Return: A drop-down list.
**
** Description:
** It creates a drop-down list gap.
**
**********************************************************************
*********/
function creaDesplegable (ident, opcs, pts, ent){
if (opAleat.localeCompare("si") == 0)
var unArray = arrayAleaEnt(opcs.length);
else
var unArray = obtenArray (opcs.length);
var lista = "<select name=\""+ident+"\" class=\"desplegable\"
tipo=\"Lista\">";
for (j = 0; j < opcs.length; j++){
if (opcs[unArray[j]].getAttribute("respident").indexOf(ent)
!= -1)
lista += "<option
value=\""+opcs[unArray[j]].childNodes[0].nodeValue+"\"
puntos=\""+pts[unArray[j]].childNodes[0].nodeValue+"\"
>"+opcs[unArray[j]].childNodes[0].nodeValue +"</option>";
}
lista += "</select>";
return lista;
}
/*********************************************************************
**********
**
** Function: obtieneCorrecta()
** Inputs: An array with the options of the question and another
array with the points of each option.
** Return: The correct answer of a multiple choice question and only
answer.
**
** Description:
** It obtains the correct answer of a multiple choice question and
only answer.
**
**********************************************************************
*********/
function obtieneCorrecta(opciones_tag, pts){
var corr = "";
var arr = [];
for(var z=0;z<pts.length;z++){
arr[z] = parseFloat(pts[z].childNodes[0].nodeValue);
}
corr =
normalize(opciones_tag[arr.indexOf(Math.max.apply(null,
arr))+1].childNodes[0].nodeValue);
if (corr.replace(/\s/g,"_").length < 66)
corr = corr.replace(/\s/g,"_");
else
162
corr =
auxiliarResp[arr.indexOf(Math.max.apply(null, arr),0)];
return corr;
}
/*********************************************************************
**********
**
** Function: obtieneCorrectaMul()
** Inputs: An array with the options of the question and another
array with the points of each option.
** Return: The correct answers of a multiple choice question and
multiple answer.
**
** Description:
** It obtains the correct answer of a multiple choice question and
multiple answer.
**
**********************************************************************
*********/
function obtieneCorrectaMul(opciones_tag, pts){
var corr = "";
var corr2 = "";
var count = 0;
for(var z=0;z < pts.length; z = z + 2){
if (parseFloat(pts[z].childNodes[0].nodeValue) > 0){
if (count == 0){
corr =
normalize(opciones_tag[z/2+1].childNodes[0].nodeValue);
corr2 = auxiliarResp[z/2];
} else {
corr =
corr.concat("[,]",normalize(opciones_tag[z/2+1].childNodes[0].nodeValu
e));
corr2 = corr2.concat("[,]",auxiliarResp[z/2]);
}
count++;
}
}
if (corr.replace(/\s/g,"_").length < 66)
return corr.replace(/\s/g,"_");
else
return corr2;
}
/*********************************************************************
**********
**
** Function: unica()
** Inputs: An array of options, an identifier, an array wiht the
options points and an integer.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a multiple choice
question and only answer.
**
**********************************************************************
*********/
163
function unica(opciones_tag,ident,pts,ent){
if (opAleat.localeCompare("si") == 0)
var vector = arrayAleaEnt(pts.length);
else
var vector = obtenArray(pts.length);
var opciones="<form name=\"formulario\">";
for (var j = 0; j < pts.length; j++){
if
(opciones_tag[vector[j]+1].childNodes[0].nodeValue.indexOf("<p>"
) != -1)
opciones += "<p class=\opciones\><input type=\"radio\"
name=\""+ident+"\"
value=\""+opciones_tag[vector[j]+1].childNodes[0].nodeValue+"\"puntos=
\""+pts[vector[j]].childNodes[0].nodeValue+"\"/>"+opciones_tag[vector[
j]+1].childNodes[0].nodeValue+"</p>";
else{
var cadena =
opciones_tag[vector[j]+1].childNodes[0].nodeValue.replace("<p>",'');
cadena = cadena.replace("</p>",'');
opciones += "<p class=\opciones\><input type=\"radio\"
name=\""+ident+"\"
value=\""+cadena+"\"puntos=\""+pts[vector[j]].childNodes[0].nodeValue+
"\"/>"+cadena+"</p>";
}
}
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarUni("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/><input name=\"posponer"+ent+"\" type=\"button\"
value=\"Posponer\" class=\"boton\" onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarUni("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/>";
return opciones;
}
/*********************************************************************
**********
**
** Function: numerica()
** Inputs: An identifier, two floats, an array with the options
points and two integers.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a numeric question.
**
**********************************************************************
*********/
function numerica(iden,superior,inferior,pts,maxcar,ent){
var opciones="<form name=\"formulario\">";
opciones += "<p class=\"opciones\">Escribe el valor: <input
type=\"text\" name=\""+iden+"\" inf=\""+inferior+"\"
sup=\""+superior+"\" puntos=\""+pts+"\" size=\"3\"
maxlength=\""+parseInt(maxcar)+"\"/></p></br>";
164
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarNum("+iden+","+ent+","+inferior+","+superior+")\"
class=\"boton\"/><input name=\"borra"+ent+"\" type=\"reset\"
value=\"Borrar\" class=\"boton\"/><input name=\"posponer"+ent+"\"
type=\"button\" value=\"Posponer\" class=\"boton\"
onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarNum("+iden+","+ent+","+inferior+","+superior+")\"
class=\"boton\"/><input name=\"borra"+ent+"\" type=\"reset\"
value=\"Borrar\" class=\"boton\"/>";
return opciones;
}
/*********************************************************************
**********
**
** Function: multiple()
** Inputs: An array of options, an identifier, an array wiht the
options points and an integer.
** Return: The options of a question in a html format.
**
** Description:
** It gets the options of a question to create a multiple choice
question and multiple answer.
**
**********************************************************************
*********/
function multiple(opciones_tag,ident,pts,ent){
//Vectores que contendran los puntos de las opciones
var respondidas=[];
var norespondidas=[];
var j=0;
for(var z = 0; z < pts.length; z=z+2){
respondidas[j]=pts[z].childNodes[0].nodeValue;
norespondidas[j]=pts[z+1].childNodes[0].nodeValue;
j++;
}
if (opAleat.localeCompare("si") == 0)
var vector=arrayAleaEnt(pts.length/2);
else
var vector=obtenArray(pts.length/2);
var opciones="<form name=\"formulario\">";
for (var j = 0; j < pts.length/2; j++){
if
(opciones_tag[vector[j]+1].childNodes[0].nodeValue.indexOf("<p>"
) != -1)
opciones += "<p class=\"opciones\"><input
type=\"checkbox\" name=\""+ident+"\"
value=\""+opciones_tag[vector[j]+1].childNodes[0].nodeValue+"\"
respondida=\""+respondidas[vector[j]]+"\"
norespondida=\""+norespondidas[vector[j]]+"\"/>"+opciones_tag[vector[j
]+1].childNodes[0].nodeValue+"</p>";
else{
var cadena =
opciones_tag[vector[j]+1].childNodes[0].nodeValue.replace("<p>",'');
cadena = cadena.replace("</p>",'');
165
opciones += "<p class=\"opciones\"><input
type=\"checkbox\" name=\""+ident+"\" value=\""+cadena+"\"
respondida=\""+respondidas[vector[j]]+"\"
norespondida=\""+norespondidas[vector[j]]+"\"/>"+cadena+"</p>";
}
}
if (ent + 1 != numeroPr && auxiliarCont < longitudPreg)
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarMul("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/><input name=\"posponer"+ent+"\" type=\"button\"
value=\"Posponer\" class=\"boton\" onclick=\"muestra("+ent+")\"/>";
else
opciones += "<input name=\"valida"+ent+"\"
type=\"button\" value=\"Validar\"
onclick=\"puntuarMul("+ident+","+ent+")\" class=\"boton\"/><input
name=\"borra"+ent+"\" type=\"reset\" value=\"Borrar\"
class=\"boton\"/>";
return opciones;
}
/*********************************************************************
**********
**
** Function: puntuarUni()
** Inputs: An identifier and an integer.
** Return: None.
**
** Description:
** It gets the score of a multiple choice question and only answer.
**
**********************************************************************
*********/
function puntuarUni(iden, ent){
var contestada = "";
var contestadaAux = "";
var res = "";
var puntos = 0;
var opt=document.getElementsByName(iden[0].name);
var corr = '';
for(var i=0; i<opt.length;i++){
if(opt[i].checked){
puntuacion+=
parseFloat(opt[i].getAttribute("puntos"));
puntos =
parseFloat(opt[i].getAttribute("puntos"));
contestada = normalize(opt[i].value);
contestadaAux = auxiliarResp[i];
}
opt[i].disabled = true;
}
corr = getCorrectaInv(iden[0].name, "SINGLE CHOICE
QUESTION");
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
166
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled
= true;
//Actualizamos la puntuación y mostramos la siguiente
pregunta
contestada = contestada.toLowerCase();
corr = corr.toLowerCase()
if (contestada.replace(/\s/g,"_").length <= 66){
if (corr.localeCompare(contestada.replace(/[^a-
zA-Z 0-9.]+/g,' ').replace(/\s/g,"_")) == 0)
res="correct";
else
res="wrong";
miRTE.setInteractionsResponseAll(ent,contestada.replace(/[^a-zA-Z
0-9.]+/g,' ').replace(/\s/g,"_"),res);
} else{
if
(corr.localeCompare(contestadaAux.replace(/\s/g,"_")) == 0)
res="correct";
else
res="wrong";
miRTE.setInteractionsResponseAll(ent,contestadaAux.replace(/\s/g,"_
"),res);
}
puntuacion = Math.round(puntuacion * 100) / 100;
actualiza(puntuacion, total);
respuestas[ent] = contestada;
if (puntos > 0)
smoke.alert("¡Enhorabuena
"+miRTE.getLearnerName().split(",")[1]+"! Has conseguido "+puntos+ "
puntos en esta pregunta. Sigue así.");
else
smoke.alert("¡No te desanimes
"+miRTE.getLearnerName().split(",")[1]+"! ¡A por las siguientes
preguntas!");
var d = new Date();
var hora1 = d.getHours();
var min1 = d.getMinutes();
var sec1 = d.getSeconds();
if (hora1 < 10)
hora1 = "0"+ hora1;
if (min1 < 10)
min1 = "0"+ min1;
if (sec1 < 10)
sec1 = "0"+ sec1;
latencias[ent] =
obtenLatencia(hora1+":"+min1+":"+sec1,horaInicio);
muestra(ent);
puntosPreguntas[ent] = Math.round(puntos * 100) / 100;
}
/*********************************************************************
**********
**
** Function: puntuarMul()
** Inputs: An identifier and an integer.
** Return: None.
167
**
** Description:
** It gets the score of a multiple choice question and multiple
answer.
**
**********************************************************************
*********/
function puntuarMul(iden, ent){
var opt = document.getElementsByName(iden[0].name);
var corr = '';
var corr2 = '';
var contestada = "";
var contestadaAux = "";
var res = "";
var puntos = 0;
var count = 0;
var count2 = 0;
for(var i=0; i<opt.length;i++){
if(opt[i].checked){
puntuacion+=parseFloat(opt[i].getAttribute("respondida"));
puntos +=
parseFloat(opt[i].getAttribute("respondida"));
if (count == 0){
contestada =
normalize(opt[i].value).replace(/[^a-zA-Z 0-9.]+/g,' ');
contestadaAux = auxiliarResp[i];
} else {
contestada =
contestada.concat("[,]"+normalize(opt[i].value).replace(/[^a-zA-Z 0-
9.]+/g,' '));
contestadaAux =
contestadaAux.concat("[,]"+auxiliarResp[i]);
}
count++;
}
else {
puntuacion +=
parseFloat(opt[i].getAttribute("norespondida"));
puntos +=
parseFloat(opt[i].getAttribute("norespondida"));
}
if (parseFloat(opt[i].getAttribute("respondida"))
> 0){
if (count2 == 0){
corr =
normalize(opt[i].value).replace(/[^a-zA-Z 0-9.]+/g,' ');
corr2 = auxiliarResp[i];
} else {
corr =
corr.concat("[,]"+normalize(opt[i].value).replace(/[^a-zA-Z 0-9.]+/g,'
'));
corr2 =
corr2.concat("[,]"+auxiliarResp[i]);
}
count2++;
}
opt[i].disabled = true;
}
168
if (contestada.replace(/\s/g,"_").length <= 66){
if
(corr.replace(/\s/g,"_").localeCompare(contestada.replace(/\s/g,"_"))
== 0)
res="correct";
else
res="wrong";
miRTE.setInteractionsResponseAll(ent,contestada.replace(/\s/g,"_"),
res);
} else{
if
(corr2.localeCompare(contestadaAux.replace(/\s/g,"_")) == 0)
res="correct";
else
res="wrong";
miRTE.setInteractionsResponseAll(ent,contestadaAux.replace(/\s/g,"_
"),res);
}
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled
= true;
//Actualizamos la puntuación y mostramos la siguiente
pregunta
puntuacion = Math.round(puntuacion * 100) / 100;
actualiza(puntuacion, total);
respuestas[ent] = contestada;
var d = new Date();
var hora1 = d.getHours();
var min1 = d.getMinutes();
var sec1 = d.getSeconds();
if (hora1 < 10)
hora1 = "0"+ hora1;
if (min1 < 10)
min1 = "0"+ min1;
if (sec1 < 10)
sec1 = "0"+ sec1;
latencias[ent] =
obtenLatencia(hora1+":"+min1+":"+sec1,horaInicio);
if (puntos > 0)
smoke.alert("¡Enhorabuena
"+miRTE.getLearnerName().split(",")[1]+"! Has conseguido "+puntos+ "
puntos en esta pregunta. Sigue así.");
else
smoke.alert("¡No te desanimes
"+miRTE.getLearnerName().split(",")[1]+"! ¡A por las siguientes
preguntas!");
muestra(ent);
puntosPreguntas[ent] = Math.round(puntos * 100) / 100;
}
/*********************************************************************
**********
169
**
** Function: puntuarNum()
** Inputs: An identifier, an integer and two floats.
** Return: None.
**
** Description:
** It gets the score of a numeric question.
**
**********************************************************************
*********/
function puntuarNum(iden, ent, inf, sup){
var valor = parseFloat(iden.value);
var res = "";
var puntos =
document.getElementsByName(iden.name)[0].getAttribute("puntos");
var points = 0;
if(valor <= sup && valor >= inf){
puntuacion += parseFloat(puntos);
points = parseFloat(puntos);
res = "correct";
}
else
res = "wrong";
//Deshabilitamos los botones
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled
= true;
document.getElementsByName(iden.name)[0].disabled = true;
puntuacion = Math.round(puntuacion * 100) / 100;
miRTE.setInteractionsResponseAll(ent,valor,res);
//Actualizamos la puntuación y mostramos la siguiente
pregunta
actualiza(puntuacion, total);
respuestas[ent] = valor;
if (points > 0)
smoke.alert("¡Enhorabuena
"+miRTE.getLearnerName().split(",")[1]+"! Has conseguido "+points+ "
puntos en esta pregunta. Sigue así.");
else
smoke.alert("¡No te desanimes
"+miRTE.getLearnerName().split(",")[1]+"! ¡A por las siguientes
preguntas!");
var d = new Date();
var hora1 = d.getHours();
var min1 = d.getMinutes();
var sec1 = d.getSeconds();
if (hora1 < 10)
hora1 = "0"+ hora1;
if (min1 < 10)
min1 = "0"+ min1;
if (sec1 < 10)
sec1 = "0"+ sec1;
latencias[ent] =
obtenLatencia(hora1+":"+min1+":"+sec1,horaInicio);
170
muestra(ent);
puntosPreguntas[ent] = Math.round(points * 100) / 100;
}
/*********************************************************************
**********
**
** Function: puntuaHuecos()
** Inputs: An identifier and an integer.
** Return: None.
**
** Description:
** It gets the score of a cloze question.
**
**********************************************************************
*********/
function puntuaHuecos(iden, ent){
var corr= "";
var corr2= "";
var count = 0;
var countCor = 0;
var contestada = "";
var contestada2 = "";
var res = "";
var points = 0;
if (typeof iden.name !== "undefined"){
//Obtenemos los huecos
var opcs = document.getElementsByName(iden.name);
for(var i = 0; i < opcs.length; i++){
//Obtenemos el tipo del hueco
var tipo = opcs[i].getAttribute("tipo");
if (tipo.localeCompare("Decimal") == 0){
var valor = parseFloat(opcs[i].value);
var puntos =
opcs[i].getAttribute("puntos");
var sup =
parseFloat(opcs[i].getAttribute("sup"));
var inf =
parseFloat(opcs[i].getAttribute("inf"));
if(valor<=sup && valor>=inf){
puntuacion += parseFloat(puntos);
points += parseFloat(puntos);
if (count == 0){
contestada += sup +"[:]"+ inf;
contestada2 += sup +"[:]"+ inf;
} else {
contestada =
contestada.concat("[,]", sup +"[:]"+ inf);
contestada2 =
contestada.concat("[,]", sup +"[:]"+ inf);
}
if (countCor == 0){
171
corr += sup +"[:]"+ inf;
corr2 += sup +"[:]"+ inf;
} else {
corr = corr.concat("[,]", sup
+"[:]"+ inf);
corr2 = corr2.concat("[,]", sup
+"[:]"+ inf);
}
countCor++;
count++;
} else {
if (count == 0){
contestada += valor +"[:]"+ valor;
contestada2 += valor +"[:]"+ valor;
} else {
contestada = contestada.concat("[,]",
valor +"[:]"+ valor);
contestada2 =
contestada.concat("[,]", valor +"[:]"+ valor);
}
if (countCor == 0){
corr += sup +"[:]"+ inf;
corr2 += sup +"[:]"+ inf;
} else {
corr = corr.concat("[,]", sup +"[:]"+
inf);
corr2 = corr2.concat("[,]", sup
+"[:]"+ inf);
}
countCor++;
count++;
}
} else
if (tipo.localeCompare("Lista") == 0){
if (count==0){
contestada +=
opcs[i].options[opcs[i].options.selectedIndex].value;
contestada2 +=
auxiliarResp[opcs[i].options.selectedIndex];
} else{
contestada =
contestada.concat("[,]",
opcs[i].options[opcs[i].options.selectedIndex].value);
contestada2 =
contestada2.concat("[,]",
auxiliarResp[opcs[i].options.selectedIndex]);
}
var arr = [];
for (var k = 0; k <
opcs[i].options.length; k++){
arr[k] =
parseFloat(opcs[i].options[k].getAttribute("puntos"));
}
if (countCor == 0){
172
corr +=
opcs[i].options[arr.indexOf(Math.max.apply(null, arr))].value;
corr2 +=
auxiliarResp[k];
} else{
corr =
corr.concat("[,]",opcs[i].options[arr.indexOf(Math.max.apply(null,
arr))].value);
corr2 =
corr2.concat("[,]", auxiliarResp[k]);
}
count++;
countCor++;
puntuacion +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
points +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
} else
if (tipo.localeCompare("String") == 0){
//Obtenemos la sensibilidad del
hueco
var sensibilidad =
opcs[i].getAttribute("sensibilidad");
//Obtenemos los puntos de las
opciones
var puntos =
opcs[i].getAttribute("puntos").split(",");
//Obtenemos los opciones de la
pregunta
var opciones =
opcs[i].getAttribute("sol").split(",");
//Si no es sensible a mayúsculas
if(sensibilidad == "ci"){
if (count==0){
contestada += iden.value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
var arrPunt = [];
for(var k = 0; k <
opciones.length - 1; k++){
if(opcs[i].value.toLowerCase() == opciones[k].toLowerCase()){
if (k==0){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
}
else if (opciones[k-
1].toLowerCase() != opciones[k].toLowerCase()){
173
puntuacion += parseFloat(puntos[k]);
points
+= parseFloat(puntos[k]);
}
}
arrPunt[k] =
parseFloat(puntos[k]);
}
if (countCor == 0){
corr +=
opciones[arrPunt.indexOf(Math.max.apply(null, arrPunt))];
corr2 +=
auxiliarResp[k];
} else{
corr =
corr.concat("[,]", opciones[arrPunt.indexOf(Math.max.apply(null,
arrPunt))]);
corr2 =
corr2.concat("[,]", auxiliarResp[k]);
}
countCor++;
} else
//Si es sensible a mayúsculas
if(sensibilidad == "cs"){
if (count==0){
contestada +=
iden.value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden.value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
var arrPunt = [];
for(var k = 0; k <
opciones.length - 1; k++){
if(opcs[i].value ==
opciones[k]){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
}
arrPunt[k] =
parseFloat(puntos[k]);
}
if (countCor
== 0){
corr += opciones[arrPunt.indexOf(Math.max.apply(null, arrPunt))];
corr2 += auxiliarResp[k];
} else{
corr = corr.concat("[,]",
opciones[arrPunt.indexOf(Math.max.apply(null, arrPunt))]);
174
corr2 = corr2.concat("[,]", auxiliarResp[k]);
}
countCor++;
}
//Si tiene distancia de Levenshtein
else {
var dist =
parseInt(sensibilidad.substr(1,1)); //Valor de distancia de
Levenshtein para esta pregunta
var levens = [];
for(var k = 0; k < opciones.length -
1; k++){
levens [k] =
getEditDistance(opcs[i].value, opciones[k]); //Distancia Levenshtein
entre las dos palabras
}
var min = Math.min.apply(null,
levens); //Mínimo del vector
var indice = levens.indexOf(min,0);
//Índice del valor mínimo encontrado
if (count==0){
contestada +=
opcs[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",opcs[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
if (countCor==0){
corr += opciones[0];
corr += auxiliarResp[0];
} else{
corr =
corr.concat("[,]",opciones[0]);
ccorr2 +=
corr2.concat("[,]", auxiliarResp[0]);
}
count++;
countCor++;
if(min <= dist){
puntuacion +=
parseFloat(puntos[indice]);
points +=
parseFloat(puntos[indice]);
}
}
}
//Deshabilitamos el hueco
opcs[i].disabled = true;
}
} else {
//Obtenemos los huecos
var opcs = document.getElementsByName(iden[0].name);
175
for(var i = 0; i < opcs.length; i++){
//Obtenemos el tipo de la pregunta
var tipo = opcs[i].getAttribute("tipo");
if (tipo.localeCompare("Decimal") == 0){
var valor = parseFloat(opcs[i].value);
var puntos = opcs[i].getAttribute("puntos");
var sup = parseFloat(opcs[i].getAttribute("sup"));
var inf = parseFloat(opcs[i].getAttribute("inf"));
if(valor<=sup && valor>=inf){
puntuacion += parseFloat(puntos);
points += parseFloat(puntos);
if (count == 0){
contestada += sup +"[:]"+ inf;
contestada2 += sup +"[:]"+ inf;
} else {
contestada = contestada.concat("[,]",
sup +"[:]"+ inf);
contestada2 =
contestada2.concat("[,]", sup +"[:]"+ inf);
}
if (countCor == 0){
corr += sup +"[:]"+ inf;
corr2 += sup +"[:]"+ inf;
} else {
corr = corr.concat("[,]", sup +"[:]"+
inf);
corr2 = corr2.concat("[,]", sup
+"[:]"+ inf);
}
countCor++;
count++;
} else {
if (count == 0){
contestada += valor +"[:]"+ valor;
contestada2 += valor +"[:]"+ valor;
} else {
contestada = contestada.concat("[,]",
valor +"[:]"+ valor);
contestada2 =
contestada.concat("[,]", valor +"[:]"+ valor);
}
if (countCor == 0){
corr += sup +"[:]"+ inf;
corr2 += sup +"[:]"+ inf;
} else {
corr = corr.concat("[,]", sup +"[:]"+
inf);
corr2 = corr2.concat("[,]", sup
+"[:]"+ inf);
}
countCor++;
count++;
}
} else
176
if (tipo.localeCompare("String") == 0){
//Obtenemos la sensibilidad del hueco
var sensibilidad =
opcs[i].getAttribute("sensibilidad");
//Obtenemos los puntos de las
opciones
var puntos =
opcs[i].getAttribute("puntos").split(",");
//Obtenemos los opciones de la
pregunta
var opciones =
opcs[i].getAttribute("sol").split(",");
//Si no es sensible a mayúsculas
if(sensibilidad == "ci"){
if (count==0){
contestada +=
iden[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
var arrPunt = [];
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value.toLowerCase()
== opciones[k].toLowerCase()){
if (k==0){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
}
else if (opciones[k-
1].toLowerCase() != opciones[k].toLowerCase()){
puntuacion += parseFloat(puntos[k]);
points
+= parseFloat(puntos[k]);
}
}
arrPunt[k] =
parseFloat(puntos[k]);
}
if (countCor == 0){
corr +=
opciones[arrPunt.indexOf(Math.max.apply(null, arrPunt))];
corr2 += auxiliarResp[k];
} else{
corr =
corr.concat("[,]", opciones[arrPunt.indexOf(Math.max.apply(null,
arrPunt))]);
corr2 =
corr2.concat("[,]", auxiliarResp[k]);
}
countCor++;
177
} else
//Si es sensible a mayúsculas
if(sensibilidad == "cs"){
if (count==0){
contestada +=
iden[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",iden[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
count++;
var arrPunt = [];
for(var k = 0; k < opciones.length -
1; k++){
if(opcs[i].value ==
opciones[k]){
puntuacion +=
parseFloat(puntos[k]);
points +=
parseFloat(puntos[k]);
}
arrPunt[k] =
parseFloat(puntos[k]);
}
if (countCor == 0){
corr +=
opciones[arrPunt.indexOf(Math.max.apply(null, arrPunt))];
corr2 +=
auxiliarResp[k];
} else{
corr =
corr.concat("[,]", opciones[arrPunt.indexOf(Math.max.apply(null,
arrPunt))]);
corr2 =
corr2.concat("[,]", auxiliarResp[k]);
}
countCor++;
}
//Si tiene distancia de Levenshtein
else {
var dist =
parseInt(sensibilidad.substr(1,1)); //Valor de distancia de
Levenshtein para esta pregunta
var levens = [];
for(var k = 0; k < opciones.length -
1; k++){
levens [k] =
getEditDistance(opcs[i].value, opciones[k]); //Distancia Levenshtein
entre las dos palabras
}
var min = Math.min.apply(null,
levens); //Mínimo del vector
var indice = levens.indexOf(min,0);
//Índice del valor mínimo encontrado
178
if (count==0){
contestada +=
opcs[i].value;
contestada2 +=
auxiliarResp[i];
} else{
contestada =
contestada.concat("[,]",opcs[i].value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[i]);
}
if (countCor==0){
corr += opciones[0];
corr2 += auxiliarResp[0];
} else{
corr = corr.concat("[,]",
opciones[0]);
corr2 =
corr2.concat("[,]", auxiliarResp[0]);
}
count++;
countCor++;
if(min <= dist){
puntuacion +=
parseFloat(puntos[indice]);
points +=
parseFloat(puntos[indice]);
}
}
} else {
if (count==0){
contestada +=
opcs[i].options[opcs[i].options.selectedIndex].value;
contestada2 +=
auxiliarResp[opcs[i].options.selectedIndex];
} else{
contestada =
contestada.concat("[,]",opcs[i].options[opcs[i].options.selectedIndex]
.value);
contestada2 =
contestada2.concat("[,]",auxiliarResp[opcs[i].options.selectedIndex]);
}
count++;
var arr = [];
for (var k = 0; k <
opcs[i].options.length; k++){
arr[k] =
parseFloat(opcs[i].options[k].getAttribute("puntos"));
}
if (countCor == 0){
corr +=
opcs[i].options[arr.indexOf(Math.max.apply(null, arr))].value;
corr2 +=
auxiliarResp[k];
} else{
corr =
corr.concat("[,]",opcs[i].options[arr.indexOf(Math.max.apply(null,
arr))].value);
179
corr2 =
corr2.concat("[,]", auxiliarResp[k]);
}
countCor++;
puntuacion +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
points +=
parseFloat(opcs[i].options[opcs[i].options.selectedIndex].getAttribute
("puntos"));
}
//Deshabilitamos el hueco
opcs[i].disabled = true;
}
}
if (normalize(contestada.replace(/\s/g,"_")).length > 66)
contestada = contestada2;
contestada = contestada.toLowerCase();
corr = corr.toLowerCase();
if
(normalize(corr.replace(/\s/g,"_")).localeCompare(normalize(contestada
.replace(/\s/g,"_"))) == 0)
res="correct";
else
res="wrong";
miRTE.setInteractionsResponseAll(ent,contestada,res);
//Deshabilitamos los botones
document.getElementsByName("valida"+ent)[0].disabled = true;
document.getElementsByName("borra"+ent)[0].disabled = true;
if (ent + 1 != contador)
document.getElementsByName("posponer"+ent)[0].disabled
= true;
//Actualizamos la puntuación y mostramos la siguiente
pregunta
puntuacion = Math.round(puntuacion * 100) / 100;
actualiza(puntuacion, total);
if (points > 0)
smoke.alert("¡Enhorabuena
"+miRTE.getLearnerName().split(",")[1]+"! Has conseguido "+points+ "
puntos en esta pregunta. Sigue así.");
else
smoke.alert("¡No te desanimes
"+miRTE.getLearnerName().split(",")[1]+"! ¡A por las siguientes
preguntas!");
respuestas[ent] = contestada;
var d = new Date();
var hora1 = d.getHours();
var min1 = d.getMinutes();
var sec1 = d.getSeconds();
if (hora1 < 10)
hora1 = "0"+ hora1;
if (min1 < 10)
min1 = "0"+ min1;
180
if (sec1 < 10)
sec1 = "0"+ sec1;
latencias[ent] =
obtenLatencia(hora1+":"+min1+":"+sec1,horaInicio);
muestra(ent);
puntosPreguntas[ent] = Math.round(points * 100) / 100;
}
/*********************************************************************
**********
**
** Function: muestra()
** Inputs: The number of the question and the total number of
questions of the test.
** Return: None.
**
** Description:
** It shows the next question and introduces it in the LMS.
**
**********************************************************************
*********/
function muestra (num){
var next = num + 1;
var patt = "";
if (next < numeroPr){
var aux = "div"+next;
document.getElementById(aux).style.display = "block";
var id =
document.getElementById("formulario"+next).getElementsByTagName("input
")[0].getAttribute("name");
var tipo =
document.getElementById("formulario"+next).getAttribute("tipo");
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
patt = getCorrectaInv(id,tipo);
miRTE.setInteractionsId(next,id);
miRTE.setInteractionsType(next,"choice");
miRTE.setInteractionsTs(next, timeStamp("1.2"));
miRTE.setInteractionsWeight(next, 1);
miRTE.setInteractionsCorrectRespPattern(next, 0, patt);
} else
if (tipo.localeCompare("MULTIPLE CHOICE QUESTION") ==
0){
patt = getCorrectaInv(id,tipo);
miRTE.setInteractionsId(next,id);
miRTE.setInteractionsType(next,"choice");
miRTE.setInteractionsTs(next, timeStamp("1.2"));
miRTE.setInteractionsWeight(next, 1);
miRTE.setInteractionsCorrectRespPattern(next, 0,
patt);
} else
if (tipo.localeCompare("CLOZE QUESTION") == 0){
patt = getCorrectaInv(id,tipo);
miRTE.setInteractionsId(next,id);
miRTE.setInteractionsType(next,"fill-in");
miRTE.setInteractionsTs(next, timeStamp("1.2"));
miRTE.setInteractionsWeight(next, 1);
miRTE.setInteractionsCorrectRespPattern(next, 0,
patt.toLowerCase());
181
} else
if (tipo.localeCompare("NUMERIC QUESTION") == 0){
patt = getCorrectaInv(id,tipo);
miRTE.setInteractionsId(next,id);
miRTE.setInteractionsType(next,"numeric");
miRTE.setInteractionsTs(next, timeStamp("1.2"));
miRTE.setInteractionsWeight(next, 1);
miRTE.setInteractionsCorrectRespPattern(next, 0,
patt);
}
}
var f2=new Date();
var hora2 = f2.getHours();
var min2 = f2.getMinutes();
var sec2 = f2.getSeconds();
if (hora2 < 10)
hora2 = "0"+ hora2;
if (min2 < 10)
min2 = "0"+ min2;
if (sec2 < 10)
sec2 = "0"+ sec2;
horaInicio = hora2+":"+min2+":"+sec2;
}
/*********************************************************************
**********
**
** Function: actualiza()
** Inputs: The score of the learner and the total number of available
points of the test.
** Return: None.
**
** Description:
** It updates the score and puts it into the square situated in the
top right of the page.
**
**********************************************************************
*********/
function actualiza (pts, sum){
document.getElementById("tupuntuacion").value = pts + "/" + sum;
}
/*********************************************************************
**********
**
** Function: getCorrectaInv()
** Inputs: The identifier of the question and its type
** Return: The correct answer without special signs neither spaces.
**
** Description:
** It obtains the correct answer/s of any question.
**
**********************************************************************
*********/
function getCorrectaInv(iden, tipo){
var opt=document.getElementsByName(iden);
var correcta = "";
var correcta2 = "";
var count = 0;
if (tipo.localeCompare("SINGLE CHOICE QUESTION") == 0){
182
var arr = [];
for(var i=0; i<opt.length;i++){
arr[i] =
parseFloat(opt[i].getAttribute("puntos"));
}
if (opt[arr.indexOf(Math.max.apply(null,
arr))].value.length < 66)
correcta =
normalize(opt[arr.indexOf(Math.max.apply(null,
arr))].value).replace(/[^a-zA-Z 0-9.]+/g,' ');
else
correcta =
auxiliarResp[arr.indexOf(Math.max.apply(null, arr))];
} else
if (tipo.localeCompare("MULTIPLE CHOICE QUESTION") == 0){
for(var i=0; i<opt.length;i++){
if (parseFloat(opt[i].getAttribute("respondida"))
> 0){
if (count==0){
correcta =
normalize(opt[i].value).replace(/[^a-zA-Z 0-9.]+/g,' ');
correcta2 = auxiliarResp[i];
} else {
correcta =
correcta.concat("[,]",normalize(opt[i].value).replace(/[^a-zA-Z 0-
9.]+/g,' '));
correcta2 = correcta2.concat("[,]",
auxiliarResp[i]);
} count++;
}
}
if (correcta.replace(/\s/g,"_").length >= 66)
correcta = correcta2;
} else
if (tipo.localeCompare("CLOZE QUESTION") == 0){
for(var i=0; i<opt.length;i++){
//Obtenemos el tipo de hueco
var hueco = opt[i].getAttribute("tipo");
if (hueco.localeCompare("Decimal") == 0){
if (count==0){
correcta =
opt[i].getAttribute("inf")+"[:]"+opt[i].getAttribute("sup");
correcta2 =
opt[i].getAttribute("inf")+"[:]"+opt[i].getAttribute("sup");
} else{
correcta =
correcta.concat("[,]",opt[i].getAttribute("inf")+"[:]"+opt[i].getAttri
bute("sup"));
correcta2 =
correcta2.concat("[,]",opt[i].getAttribute("inf")+"[:]"+opt[i].getAttr
ibute("sup"));
}
count++;
} else
if (hueco.localeCompare("Lista") ==
0){
if (count==0){
correcta =
getCorrecta(opt[i])[0];
183
correcta2 =
auxiliarResp[getCorrecta(opt[i])[1]];
} else {
correcta =
correcta.concat("[,]",getCorrecta(opt[i])[0]);
correcta2 =
correcta2.concat("[,]",getCorrecta(opt[i])[1]);
}
count++;
} else
if (hueco.localeCompare("String") ==
0){
var opcs =
opt[i].getAttribute("sol").split(",");
var pts =
opt[i].getAttribute("puntos").split(",");
pts.length = pts.length - 1;
var numbers = pts.map(Number);
var index =
numbers.indexOf(Math.max.apply(null, numbers));
if (count==0)
correcta =
opcs[index];
else
correcta =
correcta.concat("[,]",opcs[index]);
count++;
}
}
if (correcta.replace(/\s/g,"_").length >=
66)
correcta = correcta2;
} else
if (tipo.localeCompare("NUMERIC QUESTION")
== 0){
correcta =
opt[0].getAttribute("inf")+"[:]"+opt[0].getAttribute("sup");
}
return normalize(correcta.replace(/\s/g,"_"));
}
/*********************************************************************
**********
**
** Function: getCorrecta()
** Inputs: The options of a drop-down list of a cloze question.
** Return: The correct answer.
**
** Description:
** It obtains the correct answer of the list.
**
**********************************************************************
*********/
function getCorrecta (opciones) {
var arr = [];
cont = 0;
for(var j = 0; j < opciones.options.length; j++){
arr[cont] =
parseFloat(opciones.options[j].getAttribute("puntos"));
184
cont++;
}
var index = arr.indexOf(Math.max.apply(null, arr));
return [opciones.options[index].value,index];
}
/*********************************************************************
**********
**
** Function: normalize()
** Inputs: A string with special signs.
** Return: The same string without any special sign.
**
** Description:
** It normalizes a string.
**
**********************************************************************
*********/
var normalize = (function() {
var from = "ÃÀÁÄÂÈÉËÊÌÍÏÎÒÓÖÔÙÚÜÛãàáäâèéëêìíïîòóöôùúüûÑñÇç",
to = "AAAAAEEEEIIIIOOOOUUUUaaaaaeeeeiiiioooouuuunncc",
mapping = {};
for(var i = 0, j = from.length; i < j; i++ )
mapping[ from.charAt( i ) ] = to.charAt( i );
return function( str ) {
var ret = [];
for( var i = 0, j = str.length; i < j; i++ ) {
var c = str.charAt( i );
if( mapping.hasOwnProperty( str.charAt( i ) ) )
ret.push( mapping[ c ] );
else
ret.push( c );
}
return ret.join( '' );
}
})();
/*********************************************************************
**********
**
** Function: getEditDistance()
** Inputs: Two strings.
** Return: An integer that represents the Levenshtein distance
between the two strings.
**
** Description:
** It gets the Levenshtein distance between the two strings given.
**
**********************************************************************
*********/
function getEditDistance (a, b){
if(a.length == 0) return b.length;
if(b.length == 0) return a.length;
var matrix = [];
// increment along the first column of each row
var i;
185
for(i = 0; i <= b.length; i++){
matrix[i] = [i];
}
// increment each column in the first row
var j;
for(j = 0; j <= a.length; j++){
matrix[0][j] = j;
}
// Fill in the rest of the matrix
for(i = 1; i <= b.length; i++){
for(j = 1; j <= a.length; j++){
if(b.charAt(i-1) == a.charAt(j-1)){
matrix[i][j] = matrix[i-1][j-1];
} else {
matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
Math.min(matrix[i][j-1] + 1, //
insertion
matrix[i-1][j] + 1)); //
deletion
}
}
}
return matrix[b.length][a.length];
};
/*********************************************************************
**********
**
** Function: obtieneTiempo()
** Inputs: Two strings of time.
** Return: The difference between both strings.
**
** Description:
** It calculates the difference between both strings.
**
**********************************************************************
*********/
function obtieneTiempo(cad1, cad2){
var horas = parseInt(cad2.substring(0,2)) -
parseInt(cad1.substring(0,2));
var minutos = parseInt(cad2.substring(3,5)) -
parseInt(cad1.substring(3,5));
var segundos = parseInt(cad2.substring(6,8)) -
parseInt(cad1.substring(6,8));
var tiempo = "";
if (segundos < 0) {
minutos--;
segundos = 60 + segundos;
}
if (minutos < 0) {
horas--;
minutos = 60 + minutos;
}
if (horas < 0) {
dias--;
horas = 24 + horas;
}
186
if (segundos.toString().length < 2){
segundos = "0"+segundos;
}
if (minutos.toString().length < 2){
minutos = "0"+minutos;
}
if (horas.toString().length < 2){
horas = "0"+horas;
}
tiempo = horas+":"+minutos+":"+segundos;
return tiempo;
}
/*********************************************************************
**********
**
** Function: obtenLatencia()
** Inputs: Two strings.
** Return: The time between both strings.
**
** Description:
** It calculates the difference between two given strings.
**
**********************************************************************
*********/
function obtenLatencia(cad1, cad2){
var hora1 = cad1.split(":")[0];
var hora2 = cad2.split(":")[0];
var min1 = cad1.split(":")[1];
var min2 = cad2.split(":")[1];
var sec1 = cad1.split(":")[2];
var sec2 = cad2.split(":")[2];
var sec = parseInt(sec1) - parseInt(sec2);
var min = parseInt(min1) - parseInt(min2);
var hora = parseInt(hora1) - parseInt(hora2);
if (sec < 0){
min--;
sec = 60 + sec;
}
if (hora < 10) hora = "0" + hora;
if (min < 10) min = "0" + min;
if (sec < 10) sec = "0" + sec;
return hora+":"+min+":"+sec;
}
/*********************************************************************
**********
**
** Function: ponRespuestas()
** Inputs: An integer depending on the rows of the table.
** Return: None.
**
** Description:
** It adds the answers given by the learner in the table interacting
with the LMS.
**
187
**********************************************************************
*********/
function ponRespuestas(entero){
for (var i = 0; i < contador; i++){
var res = "";
if (respuestas[i])
res = respuestas[i].toString();
else
res = "No contestada";
res = res.split("[,]");
var lat = latencias[i];
var aux = entero + i;
document.getElementById("PE"+aux).innerHTML = res;
if (typeof lat == "undefined")
lat = "Sin latencia";
document.getElementById("PE"+aux).innerHTML += " " + lat;
if (typeof puntosPreguntas[i] !== "undefined" )
document.getElementById("PE"+aux).innerHTML += " " +
puntosPreguntas[i] + " puntos";
else
document.getElementById("PE"+aux).innerHTML += " 0
puntos";
}
}
/*********************************************************************
**********
**
** Function: reiniciar()
** Inputs: None.
** Return: None.
**
** Description:
** It resets a couple of fields in the LMS.
**
**********************************************************************
*********/
function reiniciar (){
for (var i = 0; i < contador; i++){
if (i == 0)
miRTE.setInteractionsResponse(i,"No_contestada");
else {
miRTE.setInteractionsCorrectRespPattern(i,0,"No_contestada");
miRTE.setInteractionsResponse(i,"No_contestada");
}
}
}
/*********************************************************************
**********
**
** Function: deshabilitar()
** Inputs: None.
** Return: None.
**
** Description:
** It disables the questions buttons.
**
188
**********************************************************************
*********/
function deshabilitar (){
for (var i = 0; i < contador; i++){
document.getElementsByName("valida"+i)[0].disabled = true;
document.getElementsByName("borra"+i)[0].disabled = true;
if (i + 1 != contador)
document.getElementsByName("posponer"+i)[0].disabled =
true;
}
}
8.6 Librería JavaScript principal.js
// JavaScript Document
// Nombre del archivo: principal.js
//Autor: Sergio Díaz Fuentes
/*********************************************************************
**********
** Function: loadJsLib()
** Inputs: The path of the XML file, the number of questions of the
test, time for the test, random questions, random options and type of
comunication.
** Return: None.
**
** Description:
** It selects the correct JavaScript library.
**
**********************************************************************
*********/
function loadJsLib(xmlName,numPreguntas,time, aleaPr, aleaOp, comunic)
{
if(comunic.localeCompare("no") == 0){
loadScript("_ejs_library/scripts/creaPreguntasSinCom.js",
function(){
loadXMLDoc(xmlName,numPreguntas,time, aleaPr, aleaOp);
});
} else if (comunic.localeCompare("si") == 0){
loadScript("_ejs_library/scripts/creaPreguntas12.js", function(){
loadXMLDoc(xmlName,numPreguntas,time,
aleaPr, aleaOp);
});
}
}
/*********************************************************************
**********
** Function: loadScript()
** Inputs: The url of the JavaScript library and the call to the
function loadXMLDoc.
** Return: None.
**
** Description:
** It adds the correct library to the html page.
**
189
**********************************************************************
*********/
function loadScript(url, callback) {
var script = document.createElement('script');
if (script.readyState) { // IE
script.onreadystatechange = function () {
if (script.readyState === 'loaded' || script.readyState ===
'complete') {
script.onreadystatechange = null;
callback();
}
};
} else { // Others
script.onload = function() {
callback();
};
}
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
8.7 Hoja de estilo estiloIndex.css
@font-face{
font-family: Dinlight;
src: url('../html/DIN-Light.woff') format('woff');
src: url('../html/dinlight.ttf') format('truetype');
src: url('../html/dinbeklight-webfont.woff?#iefix') format('woff');
}
body { margin-left: 3%;
margin-right: 3%;
margin-top: 2%;
margin-bottom: 2%;
text-align: justify;
overflow-x: hidden;
}
p{ font-family: Dinlight, cursive;
line-height: 1.5em;
margin: 0;
margin-bottom:1.5em;
text-align: justify;
orphans: 2;
widows: 2;
}
b{ font-family: Dinlight, cursive;
color: #00a65d;
}
ul{ list-style-type: disc;
font-family: Dinlight, cursive;
font-size: 11pt;
}
190
li{ font-family: Dinlight, cursive;
font-size: 11pt;
}
table{ width: 90%;
max-width: 1000px;
border-collapse: collapse;
background-color: #FAE38E;
}
h2{ text-align: center;
font-family: Dinlight, cursive;
color: #00a65d;
}
h3{ font-family: Dinlight, cursive;
color: #00a65d;
}
p span{ font-weight: bold;
}
video{ max-width: 87%;
height: 94%;
margin-right: 7%;
}
[type="checkbox"] { cursor: pointer;
}
[type="checkbox"]:disabled{
cursor: not-allowed;
}
[type="radio"] { cursor: pointer;
}
[type="radio"]:disabled{
cursor: not-allowed;
}
[type="text"] { cursor: text;
}
[type="text"]:disabled{
cursor: not-allowed;
}
.tablaIma{
background-color: #ffffff;
border-collapse: collapse;
}
.trIma{
border: 3px solid #ddb10a;
}
191
.thIma{
background-color: #00a65d;
}
.pEnca{
font-family: Dinlight, cursive;
color: white;
text-align: center;
height: 2px;
font-size: 14pt;
}
.boton{
font-size:16px;
font-family:Dinlight, cursive;
font-weight:bold;
color:white;
background:#00a65d;
border:0px;
width:160px;
height:22px;
cursor: pointer;
margin-left: 2%;
}
.boton:hover{
background: #00884D;
box-shadow: 0 6px 12px 0 rgba(0,0,0,0.24), 0 17px 50px 0
rgba(0,0,0,0.19);
}
.boton:disabled{
opacity: 0.6;
cursor: not-allowed;
}
.boton2{
font-size:16px;
font-family:Dinlight, cursive;
font-weight:bold;
color:white;
background:#00a65d;
border:0px;
width:10%;
height:3%;
cursor: pointer;
margin-left: 2%;
}
.boton2:hover{
background: #00884D;
box-shadow: 0 6px 12px 0 rgba(0,0,0,0.24), 0 17px 50px 0
rgba(0,0,0,0.19);
}
.boton2:disabled{
opacity: 0.6;
cursor: not-allowed;
}
.desplazado {
192
margin-top: 2em;
margin-left: 2em;
margin-right: 2em;
}
.desplaza2 { margin-right: 2em;
margin-left: 2em;
margin-top: 2em;
margin-bottom: 2em;
}
.desplegable{
font-family: Dinlight, cursive;
font-size:12pt;
font-weight: lighter;
}
.desplegable:disabled{
cursor: not-allowed;
}
.altura{
height:35%;
width:70%;
text-align: center;
bottom: 5%;
}
.cuadroFijo{
position: fixed;
opacity: 0.9;
border: 3px solid #00a65d;
background: #FAE38E;
width: 8%;
height: 14%;
top: 1%;
right: 0.5%;
}
.parrafoPuntos{
font-family: Dinlight, cursive;
font-size:12pt;
font-weight: bold;
}
.puntos{
font-family: Dinlight, cursive;
font-size: 12pt;
font-weight: bold;
cursor: not-allowed;
}
.huecoT{
font-family: Dinlight, cursive;
font-size:12pt;
font-weight: lighter;
cursor: text;
}
#integracion {
193
background:-webkit-linear-gradient(top, #FAE38E, #F7C91F);
width: 100%;
height: 100%;
position: relative;
text-align: center;
margin: 0 auto;
border: 3px solid #00a65d;
}
8.8 Hoja de estilo estilo.css
@font-face{
font-family: Dinlight;
src: url('../html/DIN-Light.woff') format('woff');
src: url('../html/dinlight.ttf') format('truetype');
src: url('../html/dinbeklight-webfont.woff?#iefix') format('woff');
}
b{ font-family: Dinlight, cursive;
color: #00a65d;
}
body { margin-left: 3%;
margin-right: 3%;
margin-top: 2%;
margin-bottom: 2%;
text-align: justify;
overflow-x: hidden;
}
p{ font-family: Dinlight, cursive;
font-size:14pt;
}
p span{ font-size:12pt;
font-weight: bold;
}
h2{ text-align: center;
font-family: Dinlight, cursive;
color: #00a65d;
}
h3{ font-family: Dinlight, cursive;
font-size: 20pt;
line-height: 150%;
text-align: center;
margin-left: 2em;
margin-right: 2em;
}
ul{ font-family: Dinlight, cursive;
font-size:12pt;
194
}
table{ width: 100%;
border-collapse: collapse;
}
th{ width: 100%;
}
[type="checkbox"] { cursor: pointer;
}
[type="checkbox"]:disabled{
cursor: not-allowed;
}
[type="radio"] { cursor: pointer;
}
[type="radio"]:disabled{
cursor: not-allowed;
}
[type="text"] { cursor: text;
}
[type="text"]:disabled{
cursor: not-allowed;
}
.epigrafe{
font-family: Dinlight, cursive;
font-weight: bold;
font-size: 16pt;
color: #00a65d;
}
.desplaza2 { width: 90%;
margin-right: 2em;
margin-left: 2em;
margin-top: 2em;
margin-bottom: 2em;
}
.desplazado { margin-top: 2em;
margin-left: 2em;
margin-right: 2em;
}
.desplegable{
font-family: Dinlight, cursive;
font-size:12pt;
font-weight: lighter;
cursor: pointer;
}
.desplegable:disabled{
cursor: not-allowed;
}
195
.huecoT{
font-family: Dinlight, cursive;
font-size:12pt;
font-weight: lighter;
cursor: text;
}
.opciones{
font-family: Dinlight, cursive;
font-size:14pt;
font-weight: lighter;
}
.boton{
font-size:16px;
font-family:Dinlight, cursive;
font-weight:bold;
color:white;
background:#00a65d;
border:0px;
width:13%;
height:3%;
cursor: pointer;
margin-left: 2%;
margin-bottom: 2%;
}
.boton:hover{
background: #00884D;
box-shadow: 0 6px 12px 0 rgba(0,0,0,0.24), 0 17px 50px 0
rgba(0,0,0,0.19);
}
.boton:disabled{
opacity: 0.6;
cursor: not-allowed;
}
.boton2{
font-size:16px;
font-family:Dinlight, cursive;
font-weight:bold;
color:white;
background:#00a65d;
border:0px;
width:30%;
cursor: pointer;
height:100%;
}
.boton2:hover{
background: #00884D;
box-shadow: 0 6px 12px 0 rgba(0,0,0,0.24), 0 17px 50px 0
rgba(0,0,0,0.19);
}
.boton2:disabled{
opacity: 0.6;
cursor: not-allowed;
}
196
.corrige{
font-size:16px;
font-family:Dinlight, cursive;
font-weight:bold;
color:white;
background:#00a65d;
border:0px;
width:12%;
height:4%;
margin-left: 26em;
}
.botonLMS{
font-size:16px;
font-family:Dinlight, cursive;
font-weight:bold;
color:white;
background:#00a65d;
border:0px;
width:21%;
height:5%;
cursor: pointer;
}
.botonLMS:hover{
background: #00884D;
box-shadow: 0 6px 12px 0 rgba(0,0,0,0.24), 0 17px 50px 0
rgba(0,0,0,0.19);
}
.botonLMS:disabled{
opacity: 0.6;
cursor: not-allowed;
}
.botonMostrar{
font-size:16px;
font-family:Dinlight, cursive;
font-weight:bold;
color:white;
background:#00a65d;
border:0px;
width:20%;
height:100%;
margin-left: 40%;
cursor: pointer;
}
.botonMostrar:hover{
background: #00884D;
box-shadow: 0 6px 12px 0 rgba(0,0,0,0.24), 0 17px 50px 0
rgba(0,0,0,0.19);
}
.botonMostrar:disabled{
opacity: 0.6;
cursor: not-allowed;
}
.boton3 {
197
background: #00a65d;
font-size:20px;
font-family:Dinlight, cursive;
font-weight:bold;
color:white;
outline: 0;
height: 200px; /* Alto del botón */
width: 200px; /* Ancho del botón */
display: table;
border-radius: 100%;
cursor: pointer;
box-shadow:
/* Sombras internas */
inset 0 10px 15px rgba(255,255,255,.35), inset 0 -10px 15px
rgba(0,0,0,.05), inset 10px 0 15px rgba(0,0,0,.05), inset -10px 0 15px
rgba(0,0,0,.05),
/* Sombra externa */
0 5px 20px rgba(0,0,0,0.4);
}
/* Al presionar */
.boton3:active { box-shadow: inset 0 5px 30px rgba(0,0,0,.2); /* Sombra interior */
background-size: 55%; /* Cambiamos el tamaño de la imagen */
}
.boton3:hover{
background: #00884D;
}
.boton3:disabled{
opacity: 0.6;
cursor: not-allowed;
}
.estiloTitulo {font-family: Dinlight, cursive; font-weight: bold;
font-size: 26px;
color: #FFFFFF;
}
.fila{
text-align: right;
}
.altura{
height:35%;
width:80%;
text-align: center;
margin-top: 7%;
}
.cuadroFijo{
position: fixed;
opacity: 0.9;
border: 3px solid #00a65d;
background: #FAE38E;
width: 8%;
height: 7%;
top: 1%;
right: 0.5%;
198
}
.cuadroFijoT{
position: fixed;
opacity: 0.9;
border: 3px solid #00a65d;
background: #FAE38E;
width: 10%;
height: 18%;
top: 1%;
left: 0.5%;
}
.parrafoPuntos{
font-family: Dinlight, cursive;
font-size:12pt;
font-weight: bold;
}
.puntos{
font-family: Dinlight, cursive;
font-size: 12pt;
font-weight: bold;
cursor: not-allowed;
}
#parametros{
background:linear-gradient(top, #FAE38E, #F7C91F);
background:-webkit-linear-gradient(top, #FAE38E, #F7C91F);
background:-moz-linear-gradient(top, #FAE38E, #F7C91F);
background:-o-linear-gradient(top, #FAE38E, #F7C91F);
background:-ms-linear-gradient(top, #FAE38E, #F7C91F);
width: 95%;
}
#questions{
background:linear-gradient(top, #FAE38E, #FAD54C);
background:-webkit-linear-gradient(top, #FAE38E, #FAD54C);
background:-moz-linear-gradient(top, #FAE38E, #FAD54C);
background:-o-linear-gradient(top, #FAE38E, #FAD54C);
background:-ms-linear-gradient(top, #FAE38E, #FAD54C);
width: 95%;
}
#resultados{
background:linear-gradient(top, #FAE38E, #F7C91F);
background:-webkit-linear-gradient(top, #FAE38E, #F7C91F);
background:-moz-linear-gradient(top, #FAE38E, #F7C91F);
background:-o-linear-gradient(top, #FAE38E, #F7C91F);
background:-ms-linear-gradient(top, #FAE38E, #F7C91F);
width: 95%;
}
199
Advanced Distributed Learning. (2009). SCORM 2004 Run-Time Environment [RTE].
CSS W3C. (n.d.). Retrieved July 30, 2016, from https://www.w3.org/Style/CSS/
CSS Wiki. (n.d.). Retrieved July 30, 2016, from https://es.wikipedia.org/wiki/Hoja_de_estilos_en_cascada#cite_note-CSS1-3
DOM W3C. (n.d.). Retrieved July 29, 2016, from https://www.w3.org/DOM/#what
DOM W3Schools. (n.d.). Retrieved July 29, 2016, from http://www.w3schools.com/js/js_htmldom.aspitle
ESPAÑOLAS, C. U. (2015). Analisis de las TIC en las Universidades Españolas. Universitic 2015. http://doi.org/10.1017/CBO9781107415324.004
Hipertexto HTML. (n.d.). Retrieved July 29, 2016, from http://www.hipertexto.info/documentos/html.htm
HTML W3C. (n.d.). Retrieved August 2, 2016, from https://www.w3.org/html/
Joint Information Systems Committee (JISC). (2007). Effective Practice with e-Assessment. Information Systems Journal. Retrieved from http://www.jisc.ac.uk/publications
Manual de XML. (n.d.). Retrieved July 31, 2016, from http://www.mundolinux.info/que-es-xml.htm
Melorose, J., Perroy, R., & Careas, S. (2015). JavaScript. The definitive Guide, 6th ed. Statewide Agricultural Land Use Baseline 2015 (Vol. 1). http://doi.org/10.1017/CBO9781107415324.004
Ostyn, C. (2007). In the Eye of the SCORM - An introduction to SCORM 2004 for Content Developers. Ostyn Consulting. Retrieved from https://www.google.ba/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=0CDgQFjAB&url=http://xa.yimg.com/kq/groups/18757856/1506523990/name/SCORM.pdf&ei=9AOKUtGYEsTCswaz7oCICw&usg=AFQjCNFVGGvo6uIPz_-EOaA4MfUxtMHFIQ&sig2=yrZEM_uSs0YmmT0u0YXrGw&bvm=bv.56643336,d
Quiroz, J. (2003). Sociedad de la información y del conocimiento. In Boletín de sistemas Nacionales Estadístico y de Información Geográfica (Vol. 1, pp. 81–92).
Run-Time SCORM. (n.d.). Retrieved August 2, 2016, from http://scorm.com/scorm-explained/technical-scorm/run-time/
SCO SCORM. (n.d.). Retrieved August 2, 2016, from http://www.scormsoft.com/scorm/cam/scos
SCORM explained. (n.d.). Retrieved July 31, 2016, from http://scorm.com/scorm-explained/technical-scorm/
Smoke JS. (n.d.). Retrieved August 4, 2016, from http://smoke-js.com/
Tobergte, D. R., & Curtis, S. (2013). Estándar QTI. Descripción. Journal of Chemical Information and Modeling. http://doi.org/10.1017/CBO9781107415324.004
Ventajas JavaScript. (n.d.). Retrieved August 1, 2016, from https://www.clubensayos.com/Temas-Variados/JAVA-SCRIPT-VENTAJAS-Y-DESVENTAJAS/222066.html
Versiones SCORM. (n.d.). Retrieved August 6, 2016, from http://scorm.com/scorm-