commit
04642e6d5f
|
@ -1,4 +1,4 @@
|
|||
var H5P = H5P || {};
|
||||
H5P = H5P || {};
|
||||
|
||||
/**
|
||||
* Will render a Question with multiple choices for answers.
|
||||
|
@ -126,6 +126,7 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
var endTemplate = new EJS({text: resulttemplate});
|
||||
var params = $.extend(true, {}, defaults, options);
|
||||
|
||||
var initialParams = $.extend(true, {}, defaults, options);
|
||||
var poolOrder; // Order of questions in a pool
|
||||
var currentQuestion = 0;
|
||||
var questionInstances = [];
|
||||
|
@ -145,14 +146,12 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
questionOrder = contentData.previousState.order;
|
||||
}
|
||||
|
||||
// Create a pool of questions if necessary
|
||||
/**
|
||||
* Randomizes questions in an array and updates an array containing their order
|
||||
* @param {array} questions
|
||||
* @param {array} questionOrder
|
||||
* @return {Object.<array, array>} questionOrdering
|
||||
*/
|
||||
var randomizeQuestionOrdering = function (questions, questionOrder) {
|
||||
var randomizeQuestionOrdering = function (questions) {
|
||||
|
||||
// Save the original order of the questions in a multidimensional array [[question0,0],[question1,1]...
|
||||
var questionOrdering = questions.map(function(questionInstance, index) { return [questionInstance, index] });
|
||||
|
@ -171,7 +170,7 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
for (var i = 0; i< questionOrdering.length; i++) {
|
||||
|
||||
// Use a previous order if it exists
|
||||
if(questionOrder) {
|
||||
if(contentData.previousState && contentData.previousState.questionOrder) {
|
||||
newOrder[i] = questionOrder[questionOrdering[i][1]];
|
||||
}
|
||||
else {
|
||||
|
@ -179,15 +178,15 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
}
|
||||
}
|
||||
|
||||
// Return the questions in their new order *with* their new order
|
||||
// Return the questions in their new order *with* their new indexes
|
||||
return {
|
||||
questions: questions,
|
||||
questionOrder: newOrder
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Create a pool (a subset) of questions if necessary
|
||||
if (params.poolSize && params.poolSize < params.questions.length) {
|
||||
if (params.poolSize > 0) {
|
||||
|
||||
// If a previous pool exists, recreate it
|
||||
if(contentData.previousState && contentData.previousState.poolOrder) {
|
||||
|
@ -204,7 +203,7 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
}
|
||||
else { // Otherwise create a new pool
|
||||
// Randomize and get the results
|
||||
var poolResult = randomizeQuestionOrdering(params.questions, poolOrder);
|
||||
var poolResult = randomizeQuestionOrdering(params.questions);
|
||||
var poolQuestions = poolResult.questions;
|
||||
poolOrder = poolResult.questionOrder;
|
||||
|
||||
|
@ -238,44 +237,59 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
}
|
||||
}
|
||||
|
||||
// Instantiate question instances
|
||||
for (var i = 0; i < params.questions.length; i++) {
|
||||
/**
|
||||
* Generates question instances from H5P objects
|
||||
*
|
||||
* @param {object} questions H5P content types to be created as instances
|
||||
* @return {array} Array of questions instances
|
||||
*/
|
||||
var createQuestionInstancesFromQuestions = function(questions) {
|
||||
var result = [];
|
||||
// Create question instances from questions
|
||||
// Instantiate question instances
|
||||
for (var i = 0; i < questions.length; i++) {
|
||||
|
||||
var question;
|
||||
// If a previous order exists, use it
|
||||
if (questionOrder !== undefined) {
|
||||
question = params.questions[questionOrder[i]];
|
||||
}
|
||||
else {
|
||||
// Use a generic order when initialzing for the first time
|
||||
question = params.questions[i];
|
||||
}
|
||||
var question;
|
||||
// If a previous order exists, use it
|
||||
if (questionOrder !== undefined) {
|
||||
question = questions[questionOrder[i]];
|
||||
}
|
||||
else {
|
||||
// Use a generic order when initialzing for the first time
|
||||
question = questions[i];
|
||||
}
|
||||
|
||||
if (override) {
|
||||
// Extend subcontent with the overrided settings.
|
||||
$.extend(question.params.behaviour, override);
|
||||
}
|
||||
if (override) {
|
||||
// Extend subcontent with the overrided settings.
|
||||
$.extend(question.params.behaviour, override);
|
||||
}
|
||||
|
||||
question.params = question.params || {};
|
||||
question.params.overrideSettings = question.params.overrideSettings || {};
|
||||
question.params.overrideSettings.$confirmationDialogParent = $template.last();
|
||||
question.params.overrideSettings.instance = this;
|
||||
var hasAnswers = contentData.previousState && contentData.previousState.answers;
|
||||
var questionInstance = H5P.newRunnable(question, contentId, undefined, undefined,
|
||||
{
|
||||
previousState: hasAnswers ? contentData.previousState.answers[i] : undefined,
|
||||
parent: self
|
||||
question.params = question.params || {};
|
||||
question.params.overrideSettings = question.params.overrideSettings || {};
|
||||
question.params.overrideSettings.$confirmationDialogParent = $template.last();
|
||||
question.params.overrideSettings.instance = this;
|
||||
var hasAnswers = contentData.previousState && contentData.previousState.answers;
|
||||
var questionInstance = H5P.newRunnable(question, contentId, undefined, undefined,
|
||||
{
|
||||
previousState: hasAnswers ? contentData.previousState.answers[i] : undefined,
|
||||
parent: self
|
||||
});
|
||||
questionInstance.on('resize', function () {
|
||||
up = true;
|
||||
self.trigger('resize');
|
||||
});
|
||||
questionInstance.on('resize', function () {
|
||||
up = true;
|
||||
self.trigger('resize');
|
||||
});
|
||||
questionInstances.push(questionInstance);
|
||||
result.push(questionInstance);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create question instances from questions given by params
|
||||
questionInstances = createQuestionInstancesFromQuestions(params.questions);
|
||||
|
||||
// Randomize questions only on instantiation
|
||||
if (params.randomQuestions && contentData.previousState === undefined) {
|
||||
var result = randomizeQuestionOrdering(questionInstances,questionOrder);
|
||||
var result = randomizeQuestionOrdering(questionInstances);
|
||||
questionInstances = result.questions;
|
||||
questionOrder = result.questionOrder;
|
||||
}
|
||||
|
@ -440,7 +454,12 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
* @public
|
||||
*/
|
||||
var resetTask = function () {
|
||||
|
||||
// Clear previous state to ensure questions are created cleanly
|
||||
contentData.previousState = [];
|
||||
|
||||
showingSolutions = false;
|
||||
|
||||
for (var i = 0; i < questionInstances.length; i++) {
|
||||
try {
|
||||
questionInstances[i].resetTask();
|
||||
|
@ -477,7 +496,28 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
//Force the last page to be reRendered
|
||||
rendered = false;
|
||||
|
||||
if (params.randomQuestions) {
|
||||
if(params.poolSize > 0){
|
||||
|
||||
// Make new pool from params.questions
|
||||
// Randomize and get the results
|
||||
var poolResult = randomizeQuestionOrdering(initialParams.questions);
|
||||
var poolQuestions = poolResult.questions;
|
||||
poolOrder = poolResult.questionOrder;
|
||||
|
||||
// Discard extra questions
|
||||
poolQuestions = poolQuestions.slice(0, params.poolSize);
|
||||
poolOrder = poolOrder.slice(0, params.poolSize);
|
||||
|
||||
// Replace original questions with just the ones in the pool
|
||||
params.questions = poolQuestions;
|
||||
|
||||
// Recreate the question instances
|
||||
questionInstances = createQuestionInstancesFromQuestions(params.questions);
|
||||
|
||||
// Update buttons
|
||||
initializeQuestion();
|
||||
|
||||
} else if (params.randomQuestions) {
|
||||
randomizeQuestions();
|
||||
}
|
||||
|
||||
|
@ -494,10 +534,21 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
*/
|
||||
var randomizeQuestions = function () {
|
||||
|
||||
var result = randomizeQuestionOrdering(questionInstances,questionOrder);
|
||||
var result = randomizeQuestionOrdering(questionInstances);
|
||||
questionInstances = result.questions;
|
||||
questionOrder = result.questionOrder;
|
||||
|
||||
replaceQuestionsInDOM(questionInstances);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Empty the DOM of all questions, attach new questions and update buttons
|
||||
*
|
||||
* @param {type} questionInstances Array of questions to be attached to the DOM
|
||||
*/
|
||||
var replaceQuestionsInDOM = function (questionInstances) {
|
||||
|
||||
// Find all question containers and detach questions from them
|
||||
$('.question-container', $myDom).each(function (){
|
||||
$(this).children().detach();
|
||||
|
@ -516,18 +567,18 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
//Show buttons if necessary
|
||||
if(questionInstances[questionInstances.length -1] === question
|
||||
&& question.hasButton('finish')) {
|
||||
question.showButton('finish');
|
||||
question.showButton('finish');
|
||||
}
|
||||
|
||||
if(questionInstances[questionInstances.length -1] !== question
|
||||
&& question.hasButton('next')) {
|
||||
question.showButton('next');
|
||||
question.showButton('next');
|
||||
}
|
||||
|
||||
if(questionInstances[0] !== question
|
||||
&& question.hasButton('prev')
|
||||
&& !params.disableBackwardsNavigation) {
|
||||
question.showButton('prev');
|
||||
question.showButton('prev');
|
||||
}
|
||||
|
||||
// Hide relevant buttons since the order has changed
|
||||
|
@ -542,10 +593,8 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
if (questionInstances[questionInstances.length-1] !== question) {
|
||||
question.hideButton('finish');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
var moveQuestion = function (direction) {
|
||||
if (params.disableBackwardsNavigation && !questionInstances[currentQuestion].getAnswerGiven()) {
|
||||
|
@ -762,7 +811,77 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
self.trigger('resize');
|
||||
};
|
||||
|
||||
// Function for attaching the multichoice to a DOM element.
|
||||
var registerImageLoadedListener = function (question) {
|
||||
H5P.on(question, 'imageLoaded', function () {
|
||||
self.trigger('resize');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a question and attach it to the DOM
|
||||
*
|
||||
*/
|
||||
function initializeQuestion() {
|
||||
// Attach questions
|
||||
for (var i = 0; i < questionInstances.length; i++) {
|
||||
var question = questionInstances[i];
|
||||
|
||||
// Make sure styles are not being added twice
|
||||
$('.question-container:eq(' + i + ')', $myDom).attr('class', 'question-container');
|
||||
|
||||
question.attach($('.question-container:eq(' + i + ')', $myDom));
|
||||
|
||||
// Listen for image resize
|
||||
registerImageLoadedListener(question);
|
||||
|
||||
// Add finish button
|
||||
question.addButton('finish', params.texts.finishButton,
|
||||
moveQuestion.bind(this, 1), false);
|
||||
|
||||
// Add next button
|
||||
question.addButton('next', '', moveQuestion.bind(this, 1),
|
||||
!params.disableBackwardsNavigation || !!question.getAnswerGiven(), {
|
||||
href: '#', // Use href since this is a navigation button
|
||||
'aria-label': params.texts.nextButton
|
||||
});
|
||||
|
||||
// Add previous button
|
||||
question.addButton('prev', '', moveQuestion.bind(this, -1),
|
||||
!(questionInstances[0] === question || params.disableBackwardsNavigation), {
|
||||
href: '#', // Use href since this is a navigation button
|
||||
'aria-label': params.texts.prevButton
|
||||
});
|
||||
|
||||
// Hide next button if it is the last question
|
||||
if(questionInstances[questionInstances.length -1] === question) {
|
||||
question.hideButton('next');
|
||||
}
|
||||
|
||||
question.on('xAPI', function (event) {
|
||||
var shortVerb = event.getVerb();
|
||||
if (shortVerb === 'interacted' ||
|
||||
shortVerb === 'answered' ||
|
||||
shortVerb === 'attempted') {
|
||||
toggleAnsweredDot(currentQuestion,
|
||||
questionInstances[currentQuestion].getAnswerGiven());
|
||||
_updateButtons();
|
||||
}
|
||||
if (shortVerb === 'completed') {
|
||||
// An activity within this activity is not allowed to send completed events
|
||||
event.setVerb('answered');
|
||||
}
|
||||
if (event.data.statement.context.extensions === undefined) {
|
||||
event.data.statement.context.extensions = {};
|
||||
}
|
||||
event.data.statement.context.extensions['http://id.tincanapi.com/extension/ending-point'] = currentQuestion + 1;
|
||||
});
|
||||
|
||||
// Mark question if answered
|
||||
toggleAnsweredDot(i, question.getAnswerGiven());
|
||||
}
|
||||
}
|
||||
|
||||
this.attach = function (target) {
|
||||
if (this.isRoot()) {
|
||||
this.setActivityStarted();
|
||||
|
@ -797,66 +916,8 @@ H5P.QuestionSet = function (options, contentId, contentData) {
|
|||
});
|
||||
}
|
||||
}
|
||||
var registerImageLoadedListener = function (question) {
|
||||
H5P.on(question, 'imageLoaded', function () {
|
||||
self.trigger('resize');
|
||||
});
|
||||
};
|
||||
|
||||
// Attach questions
|
||||
for (var i = 0; i < questionInstances.length; i++) {
|
||||
var question = questionInstances[i];
|
||||
|
||||
question.attach($('.question-container:eq(' + i + ')', $myDom));
|
||||
|
||||
// Listen for image resize
|
||||
registerImageLoadedListener(question);
|
||||
|
||||
// Add finish button
|
||||
question.addButton('finish', params.texts.finishButton,
|
||||
moveQuestion.bind(this, 1), false);
|
||||
|
||||
// Add next button
|
||||
question.addButton('next', '', moveQuestion.bind(this, 1),
|
||||
!params.disableBackwardsNavigation || !!question.getAnswerGiven(), {
|
||||
href: '#', // Use href since this is a navigation button
|
||||
'aria-label': params.texts.nextButton
|
||||
});
|
||||
|
||||
// Add previous button
|
||||
question.addButton('prev', '', moveQuestion.bind(this, -1),
|
||||
!(questionInstances[0] === question || params.disableBackwardsNavigation), {
|
||||
href: '#', // Use href since this is a navigation button
|
||||
'aria-label': params.texts.prevButton
|
||||
});
|
||||
|
||||
// Hide next button if it is the last question
|
||||
if(questionInstances[questionInstances.length -1] === question) {
|
||||
question.hideButton('next');
|
||||
}
|
||||
|
||||
question.on('xAPI', function (event) {
|
||||
var shortVerb = event.getVerb();
|
||||
if (shortVerb === 'interacted' ||
|
||||
shortVerb === 'answered' ||
|
||||
shortVerb === 'attempted') {
|
||||
toggleAnsweredDot(currentQuestion,
|
||||
questionInstances[currentQuestion].getAnswerGiven());
|
||||
_updateButtons();
|
||||
}
|
||||
if (shortVerb === 'completed') {
|
||||
// An activity within this activity is not allowed to send completed events
|
||||
event.setVerb('answered');
|
||||
}
|
||||
if (event.data.statement.context.extensions === undefined) {
|
||||
event.data.statement.context.extensions = {};
|
||||
}
|
||||
event.data.statement.context.extensions['http://id.tincanapi.com/extension/ending-point'] = currentQuestion + 1;
|
||||
});
|
||||
|
||||
// Mark question if answered
|
||||
toggleAnsweredDot(i, question.getAnswerGiven());
|
||||
}
|
||||
initializeQuestion();
|
||||
|
||||
// Allow other libraries to add transitions after the questions have been inited
|
||||
$('.questionset', $myDom).addClass('started');
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"contentType": "question",
|
||||
"majorVersion": 1,
|
||||
"minorVersion": 10,
|
||||
"patchVersion": 0,
|
||||
"patchVersion": 1,
|
||||
"embedTypes": [
|
||||
"iframe"
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue