This is the Vue 3 version of the original Amp-up.io QTI 3 Item Player Component ("QTI 3 Player") which was originally built with Vue 2.6.
The QTI 3 Player is a 100% JavaScript component that aims to encapsulate the best practices and behaviors of the 1EdTech QTI 3 Assessment Item specification.
A conforming QTI 3 authoring or exporting system can construct a QTI 3 Item XML solution that will "play" authentically and reliably in the QTI 3 Player - according to the Best Practices Implementation Guide which can be found here:
1EdTech QTI v3 Best Practices and Implementation Guide
QTI 3 Player has achieved both QTI 3 Basic and QTI 3 Advanced "Delivery" Conformance Certification from 1EdTech. Furthermore, QTI 3 Player supports many so-called "Elective" capabilities that go beyond Advanced delivery conformance.
For a complete list of the QTI3 XML elements supported by the QTI 3 Player component, please refer to the QTI 3 Player Item Reference.
The QTI 3 Player has API's, interfaces, and capabilities which are optimized for formative or classroom assessment settings. Such settings typically require sophisticated QTI features such as adaptive items, item templating (sometimes called item "cloning"), template processing, and full response processing; i.e., scoring. The QTI 3 Player implements the full expressive QTI 3 Item XML vocabulary according to best practices. Consequently, you don't have to know anything about QTI. Just install the component in your project, inject XML, and go! In the following demo, a "TestRunner" application embeds the QTI 3 Player component for Item Rendering.
git clone https://github.com/amp-up-io/qti3-item-player-vue3.git
QTI 3 Player for Vue 3 has been built and tested with Node v20.14.0.
QTI 3 Player for Vue 3 includes a dependency on Vue 3, and a dependency on Vite for bundling.
npm install
npm run dev
npm run build
cd <Project folder where your package.json is located>
npm install qti3-item-player-vue3
import { createApp } from 'vue'
import App from './App.vue'
// The Qti3Player component
import Qti3Player from 'qti3-item-player-vue3'
const app = createApp(App)
// "use" triggers the component installer, and makes Qti3Player
// available globally to all app components.
app
.use(Qti3Player)
.mount('#app')
<Qti3Player
ref="qti3player"
:container-class="containerClass"
:container-padding-class="containerPaddingClass"
:color-class="colorClass"
suppress-alert-messages
suppress-invalid-response-messages
@notifyQti3PlayerReady="handlePlayerReady"
@notifyQti3ItemReady="handleItemReady"
@notifyQti3SuspendAttemptCompleted="handleSuspendAttemptCompleted"
@notifyQti3EndAttemptCompleted="handleEndAttemptCompleted"
@notifyQti3ScoreAttemptCompleted="handleScoreAttemptCompleted"
@notifyQti3ItemAlertEvent="displayItemAlertEvent"
@notifyQti3ItemCatalogEvent="handleItemCatalogEvent"
/>
// The Qti3Player built-in CSS
import 'qti3-item-player-vue3/dist/qti3Player.css'
This event signifies that the QTI 3 Player component is loaded and ready for action. The following snippet is a sample handler for the notifyQti3PlayerReady
event. QTI 3 Player hands itself as an argument to the notifyQti3PlayerReady
event, thus simplifying further QTI 3 Player API calls.
/**
* @description Event handler for the QTI 3 Player component's 'notifyQti3PlayerReady'
* event. This event is fired upon mounting of QTI 3 Player.
*
* The Qti3Player is now ready for loading XML.
* @param {Component} qti3Player - the QTI 3 Player component itself
*/
handlePlayerReady (qti3Player) {
this.qti3Player = qti3Player
}
Once QTI 3 Player is loaded and ready (see #4 above), QTI 3 Item XML can be loaded directly into QTI 3 Player via the Player's loadItemFromXML
method which takes two arguments xml {String}
and configuration {Object}
.
// Load item XML with a configuration. Use the 'this.qti3Player' reference
// saved in the notifyQti3PlayerReady event handler.
this.qti3Player.loadItemFromXml(xml, configuration)
The configuration
object is used to specify runtime context to QTI 3 Player during the item session loaded in loadItemFromXml
. A configuration object has the following structure:
configuration: {
guid: <{String} identifier used to track item state>,
pnp: <{Object} used to define Personal Needs and Preferences>,
status: <{String} use to define Item Lifecycle Status>,
sessionControl: <{Object} used to define Item Session Control>,
state: <{Object} used to RESTORE prior state saved from a prior Item Session>
}
The following snippet is an example of how an application can construct a configuration
.
// Intialize
const configuration = {}
// Stamp an item's tracking guid (if any) onto the configuration
configuration.guid = myItemTrackingGuid
// QTI 3 Player includes a helper class called 'PnpFactory' which can be used
// to build a Personal Needs and Preferences definition.
// The Default pnp object in the PnpFactory is:
const pnp = {
textAppearance: {
colorStyle: 'qti3-player-color-default'
},
// Glossary is universal support turned on (true) by default
glossaryOnScreen: true,
// Keyword translation is off ('') by default
keywordTranslationLanguage: '',
// Custom SBAC Illustrated Glossary is off (false) by default
extSbacGlossaryIllustration: false,
layoutSingleColumn: false // unsupported - see Roadmap (Simplified Layout)
}
// Set the configuration's 'pnp' property
configuration.pnp = pnp
// OPTIONAL
// Use 'status' to reflect the current QTI Item Lifecycle Status, which is an
// enumerated vocabulary from the set of:
// { 'initial' | 'interacting' | 'closed' | 'review' | 'solution'}
configuration.status = 'interacting'
// Set 'status' to 'review' in order to disable interactions; i.e., interactions
// can no longer be altered.
// configuration.status = 'review'
// QTI 3 Player includes a helper class called 'SessionControlFactory' which can be
// used to build an Item Session Control definition.
// The Default sessionControl object in the SessionControlFactory is:
const defaultItemSessionControl = {
max_attempts: 0, // no limit
show_feedback: false,
validate_responses: false
}
// Set the configuration's 'sessionControl' property
configuration.sessionControl = defaultItemSessionControl
// OPTIONAL
// If a 'state' property is in a configuration then QTI 3 Player will
// use this to restore a prior item state - including all
// template, context, outcome, and response variables.
const state = testController.getTestStateItemState(myItemTrackingGuid)
if (typeof state !== 'undefined') configuration.state = state
In the absence of a pnp
property, QTI 3 Player will use defaults, or previous settings, for presentation and accessibility supports. In the absence of a sessionControl
property, QTI 3 Player will use defaults, or previous settings, for the Item Session Control definition.
QTI 3 Player triggers a notifyQti3ItemReady
event upon completion of the Player's loadItemFromXML
method. The following snippet is a sample handler for the notifyQti3ItemReady
event.
/**
* @description Event handler for the QTI3Player component's 'notifyQti3ItemReady'
* event. This event is fired upon completion of the qti-assessment-item
* component's loading of XML.
* The inner qti-assessment-item component is passed in the event.
* @param {Component} item - the qti-assessment-item component itself
*/
handleItemReady (item) {
console.log('QTI 3 Item XML is loaded and rendered! The latest "attempt" has officially begun.')
// The item argument is the qti-assessment-item component
// with all exposed properties and methods.
this.item = item
}
After item XML is loaded and an attempt has begun, a test controller may retrieve the item's current state via two methods:
-
endAttempt
This performs response validation (if validateResponses=true), ends the attempt, executes response processing, and produces the state of all item variables. Typical use is when
submissionMode: "individual"
, or when you want to generate a raw score from the responses and the response processing. Note that Feedback (inline, block, modal) is also displayed ifshowFeedback: true
.QTI 3 Player supports the full QTI 3 response processing expression vocabulary.
-
suspendAttempt
This performs response validation (if
validateResponses: true
) and produces the state of all item variables. No response processing is executed. Typical use is whensubmissionMode: "simultaneous"
.
The endAttempt
and suspendAttempt
methods may take a considerable amount of time to complete. QTI 3 Player triggers the notifyQti3EndAttemptCompleted
and notifyQti3SuspendAttemptCompleted
events, respectively, upon completion of an endAttempt
or a suspendAttempt
method call.
// Call the endAttempt method, passing a string/target action that will be
// echoed back in the notifyQti3EndAttemptCompleted event payload.
this.qti3Player.endAttempt('navigateNextItem')
/**
* @description Example event handler for the QTI3Player component's 'notifyQti3EndAttemptCompleted'
* event. This event is fired upon completion of the endAttempt method.
* @param {Object} data - the item's state, including outcomes from response processing
*/
handleEndAttemptCompleted (data) {
// 'data' contains the item state, including any validation messages,
// response variable values, outcome variable values, template variable values,
// and context variable values.
// ... do something ...
}
// Call the suspendAttempt method, passing a string/target action that will be
// echoed back in the notifyQti3SuspendAttemptCompleted event payload.
this.qti3Player.suspendAttempt('navigateNextItem')
/**
* @description Example event handler for the QTI3Player component's 'notifyQti3SuspendAttemptCompleted'
* event. This event is fired upon completion of the suspendAttempt method.
* @param {Object} data - the item's state
*/
handleSuspendAttemptCompleted (data) {
// 'data' contains the item state, including any validation messages,
// response variable values, outcome variable values, template variable values,
// and context variable values.
// ... do something ...
}
The endAttempt
, suspendAttempt
, and scoreAttempt
methods produce a state
object
with the following properties/structure:
"state": {
"identifier": "<qti-assessment-item identifier>",
"guid": "<tracking guid passed in the configuration>",
"contextVariables": [ <built-in and declared context variables> ],
"responseVariables": [ <built-in and declared response variables> ],
"outcomeVariables": [ <built-in and declared outcome variables> ],
"templateVariables": [ <declared template variables> ],
"validationMessages": [ <validation messages (if validateResponses: true, and response constraints not met)> ]
}
In this item, there are two qti-choice-interaction's, each with single cardinality. This item is not adaptive; i.e., adaptive="false"
, and the item's XML contains no response processing that changes the original value of completionStatus
.
{
"state": {
"identifier": "q2-choice-interaction-single-sv-4a",
"guid": "0000-0002-0001",
"contextVariables": [
{
"identifier": "QTI_CONTEXT",
"cardinality": "record",
"value": {}
}
],
"responseVariables": [
{
"identifier": "numAttempts",
"cardinality": "single",
"value": 1,
"state": null
},
{
"identifier": "duration",
"cardinality": "single",
"value": 0,
"state": null
},
{
"identifier": "RESPONSE1",
"cardinality": "single",
"value": "ChoiceA",
"state": {
"order": [
"ChoiceA",
"ChoiceB",
"ChoiceC"
]
},
"correctResponse": null
},
{
"identifier": "RESPONSE2",
"cardinality": "single",
"value": "ChoiceB",
"state": {
"order": [
"ChoiceA",
"ChoiceB",
"ChoiceC"
]
},
"correctResponse": null
}
],
"outcomeVariables": [
{
"identifier": "SCORE",
"cardinality": "single",
"value": 0
},
{
"identifier": "completionStatus",
"cardinality": "single",
"value": "not_attempted"
}
],
"templateVariables": [],
"validationMessages": []
},
"target": "navigateNextItem"
}
There are scenarios where an encapsulating application may load item XML - along with a provided Item State - into QTI 3 Player and then score the Item State by executing response processing. Such a scenario exists when a Test Part's submission-mode
is set to simultaneous
. Another use-case is when a scoring system is batch-scoring a collection of items and a candidate's Item States from a submitted Test. For these scenarios, use the scoreAttempt
method:
-
scoreAttempt
Given an Item State, executes response processing with the item's XML, and produces the state of all item variables upon completion of response processing. No response validation is performed. Typical use is when
submissionMode: "simultaneous"
, or when you want to generate a raw score from the responses and the response processing without invoking the endAttempt method (which has some side-effects - such as incrementing the numAttempts variable - that may be undesirable when simply trying to get a machine score from the given Item State).QTI 3 Player supports the full QTI 3 response processing expression vocabulary.
The scoreAttempt
method may take a considerable amount of time to complete. QTI 3 Player triggers the notifyQti3ScoreAttemptCompleted
event upon completion of a scoreAttempt
method invocation.
<Qti3Player
ref="qti3player"
@notifyQti3ScoreAttemptCompleted="handleScoreAttemptCompleted"
/>
// Call the scoreAttempt method, passing a string/target action that will be
// echoed back in the notifyQti3ScoreAttemptCompleted event payload.
this.qti3Player.scoreAttempt('itemScoreReady')
/**
* @description Example event handler for the QTI3Player component's 'notifyQti3ScoreAttemptCompleted'
* event. This event is fired upon completion of the scoreAttempt method.
* @param {Object} data - the item's state, including outcomes from response processing
*/
handleScoreAttemptCompleted (data) {
// 'data' contains the item state in a 'state' property, including response variable values,
// outcome variable values, template variable values, and context variable values.
// 'data' also has a 'target' property that echos the value (if any) of the target string
// parameter passed into the originaging scoreAttempt call.
// ... do something ...
console.log('SCORE ATTEMPT OUTCOMES', data.state)
const itemState = data.state
const target = data.target
// Echo all Outcome variables in the Item State
itemState.outcomeVariables.forEach((outcomeVariable) => {
console.log(`[Outcome][${outcomeVariable.identifier}][Value=${outcomeVariable.value}]`)
})
}
An item session 'alert' message is triggered by QTI 3 Player when a person exceeds an interaction's max-choices or max-associations threshold. QTI 3 Player uses a built-in messaging/toast component to display such alerts to the candidate.
An encapsulating application may instrument the QTI 3 Player to not display alert messages by specifying the boolean attribute suppress-alert-messages
. Example:
<Qti3Player
ref="qti3player"
suppress-alert-messages
@notifyQti3ItemAlertEvent="displayItemAlertEvent"
/>
An encapsulating application should implement a handler for the notifyQti3ItemAlertEvent
when instrumenting QTI 3 Player to suppress its internal alert message display. Example:
/**
* @description Handler for QTI item alert messages such as max selections messages.
* @param {Object} event - object containing an icon property and a message property
*/
displayItemAlertEvent (event) {
// This example uses the sweetalert component to display messages as toasts
Swal.fire({
toast: true,
position: 'top-end',
icon: event.icon,
html: event.message,
showConfirmButton: false,
showCloseButton: true,
timer: 3000,
timerProgressBar: true
})
}
An item session 'invalid response' message is triggered by QTI 3 Player when,
SessionControl.validateResponses=true
, and- A response validity requirement is not met on an interaction in the loaded item
As with item session 'alert' messages, QTI 3 Player uses a built-in messaging/toast component to display such 'invalid response' messages to the candidate.
An encapsulating application may instrument the QTI 3 Player to not display invalid response messages by specifying the boolean attribute suppress-invalid-response-messages
. Example:
<Qti3Player
ref="qti3player"
suppress-invalid-response-messages
/>
All violations of response validity are reported in the validationMessages
property of the state object returned to the notifyQti3EndAttemptCompleted and notifyQti3SuspendAttemptCompleted event handlers.
"state": {
"identifier": "<qti-assessment-item identifier>",
"guid": "<tracking guid passed in the configuration>",
"contextVariables": [ <built-in and declared context variables> ],
"responseVariables": [ <built-in and declared response variables> ],
"outcomeVariables": [ <built-in and declared outcome variables> ],
"templateVariables": [ <declared template variables> ],
"validationMessages": [
{
"identifier": "RESPONSE3",
"message": "Not enough selected! Please select at least two."
}
]
}
This permits an encapsulating application to handle and display validation messages using its own UX.
An item 'catalog' event is triggered by QTI 3 Player when a user selects a control (such as a highlighted term) within the item's presentation that is bound to an item's catalog. As of QTI 3 Player version 0.3.1, the only supported catalog event () is a 'glossary' event. QTI 3 Player will display its own Catalog Glossary Dialog component when a user selects a control within the item's presentation that is bound to a 'glossary' event.
An encapsulating application may instrument the QTI 3 Player to not display its internal Catalog Dialog component by specifying the boolean attribute suppress-catalog-messages
. When instrumenting QTI 3 Player to suppress its internal catalog message display, an application should implement a handler for the notifyQti3ItemCatalogEvent
. This permits an application to handle and display catalog event messages using its own UX. Example:
<Qti3Player
ref="qti3player"
suppress-catalog-messages
@notifyQti3ItemCatalogEvent="handleItemCatalogEvent"
/>
/**
* @description Handler for QTI item catalog events such as 'glossary' events.
* @param {Object} event - object containing a catalog event payload
* Sample event schema:
* {
* type: "glossary",
* term: "acronym",
* catalogIdRef: "glosscat",
* data: [
* {
* support: "glossary-on-screen",
* card: {
* content: ""<p>An abbreviation.</p>"",
* properties: {
* name: "qti-html-content"
* }
* }
* }
* ... additional Card supports in Catalog based on PNP ...
* ]
* }
*/
handleItemCatalogEvent (event) {
console.log('[ItemCatalogEvent][Type: ' + event.type + ']', event)
switch (event.type) {
case 'glossary':
// Do something!
break
default:
}
},
QTI 3 Player groups PNP 'glossary-on-screen', 'keyword-translation', and 'ext:sbac-glossary-illustration' supports into 'glossary' events that will trigger a Catalog event of type 'glossary'.
As of the 0.3.4 release, QTI 3 Player supports the following IS0 639 language codes for keyword translations:
{ ar | cmn | de | en | es | fr | hmn | it | ja | ko | my | nl | pa | ru | so | tl | uk | vi | yue | zh }
Under most use-cases, a PNP is passed into QTI 3 Player as part of the configuration (see 4b Constructing a Configuration) as an item's XML is loaded. However, after an item is loaded, an encapsulating application may update PNP settings and then force a catalog rebinding with the updated PNP settings. QTI 3 Player implements a bindCatalog
API method for this use-case.
// 1) Use the PnpFactory helper class to build an updated PNP.
let pnpFactory = new PnpFactory()
// Example: turn off glossary
pnpFactory.setGlossaryOnScreen(false)
// Example: turn on Spanish keyword translations
pnpFactory.setKeywordTranslationLanguage('es')
// Example: turn on ext:sbac-glossary-illustration
pnpFactory.setExtSbacGlossaryIllustration(true)
// 2) Set QTI 3 Player's current PNP to our new PNP constructed in 1) above.
this.qti3Player.setItemContextPnp(pnpFactory.getPnp())
// 3) Even with a new Item Context PNP (step 2) above, QTI 3 Player will not
// automatically rebind the PNP + Catalog.
// Force QTI3 Player to bind (rebind) the Catalog.
this.qti3Player.bindCatalog()
QTI 3 Player has several attributes to instrument presentation within an encapsulating application/web page. These attributes are container-class
, container-padding-class
, and color-class
Container classes are used to contain and pad content within them. QTI 3 Player comes with built-in support for two container classes: qti3-player-container-fluid
and qti3-player-container
.
-
qti3-player-container-fluid DEFAULT
This container is a width=100%, padding=0 container at all widths.
-
qti3-player-container
This container has responsive breakpoints at screen widths of 1200px, 980px, and 768px.
Experiment with the Container Class Note: Click the "Settings" menu in the top-right corner of the TestRunner application.
Container padding classes are for setting the padding between the QTI 3 Player container and the qti-assessment-item rendered content. QTI 3 Player comes with built-in support for six container padding classes.
- qti3-player-container-padding-0 { padding: 0; } DEFAULT
- qti3-player-container-padding-1 { padding: 0.25rem; }
- qti3-player-container-padding-2 { padding: 0.5rem; }
- qti3-player-container-padding-3 { padding: 1rem; }
- qti3-player-container-padding-4 { padding: 1.5rem; }
- qti3-player-container-padding-5 { padding: 3rem; }
Experiment with the Container Padding Class Note: Click the "Settings" menu in the top-right corner of the TestRunner application.
QTI 3 Player has built-in support for 14 foreground / background color combinations (a.k.a. color "themes") in accordance with best practices for many forms of color blindness or other visual impairments. In addition to setting a colorClass in a PNP, color settings may also be applied dynamically.
- qti3-player-color-default DEFAULT
- qti3-player-color-defaultreverse (Default - Reverse Polarity)
- qti3-player-color-blackwhite (High Contrast - foreground color: black, background color: white)
- qti3-player-color-whiteblack (High Contrast - foreground color: white, background color: black)
- qti3-player-color-blackrose (foreground color: black, background color: rose)
- qti3-player-color-roseblack (foreground color: rose, background color: black)
- qti3-player-color-dgraymgray (foreground color: dark gray, background color: medium gray)
- qti3-player-color-mgraydgray (foreground color: medium gray, background color: dark gray)
- qti3-player-color-yellowblue (foreground color: yellow, background color: blue)
- qti3-player-color-blueyellow (foreground color: blue, background color: yellow)
- qti3-player-color-blackcyan (foreground color: black, background color: lblue)
- qti3-player-color-cyanblack (foreground color: lblue, background color: black)
- qti3-player-color-blackcream (foreground color: black, background color: lemonchiffon)
- qti3-player-color-creamblack (foreground color: lemonchiffon, background color: black)
Experiment with the Color Class Note: Click the "Settings" menu in the top-right corner of the TestRunner application.
The QTI3 Item Player 2022-2024 development roadmap includes all features and capabilities included in QTI 3 Delivery System "Basic" and "Advanced" conformance + PCI support.
- Support for the 14 Standard Color Combinations/Themes
- Support for Template Processing and all Template Rules
- Support for Template Math Variables
- Support for Response Processing and all Response Processing Rules
- Support for Smarter Balanced-style Choice Interaction
- Support for Smarter Balanced-style Audio player
- Support for Adaptive Items and QtiEndAttemptInteraction
- Catalog Support for Glossary and Keyword Translation
- Catalog Support for Smarter Balanced Illustrated Glossary
- Rubric Block Support
- QtiStylesheet Support
- Shared Stimulus Support
- QtiMatch Interaction Support
- QtiGapMatch Interaction Support
- QtiGraphicGapMatch Interaction Support
- QtiHottext Interaction Support
- QtiHotspot Interaction Support
- QtiOrder Interaction Support
- QtiPortableCustom Interaction Support
- QtiMedia Interaction Support
- Scoring API Examples
- Support for Interaction "Review" Status
- Improved Audio Player
- Improved Video Player
- Update Quill.js to version 2.x
Distributed under the MIT License. See LICENSE
for more information.
The QTI3 Item Player is built with the Vue 3.4 (v3.4.34) framework and Vite.
Paul Grudnitski - [email protected]
Project Link: https://github.com/amp-up-io/qti3-item-player-vue3
This component would not be possible were it not for a fortuitous decision by the aQTI Task Force (the original name of the QTI 3 Working Group) - meeting at CITO headquarters in Arnhem, NE, January 2015 - to make the aQTI / QTI 3 specification "web component friendly".