Merge branch 'release' into stable

pull/9/head release-2016-nov-16
Paal Joergensen 2016-11-16 15:10:46 +01:00
commit 04642e6d5f
2 changed files with 169 additions and 108 deletions

View File

@ -1,4 +1,4 @@
var H5P = H5P || {}; H5P = H5P || {};
/** /**
* Will render a Question with multiple choices for answers. * 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 endTemplate = new EJS({text: resulttemplate});
var params = $.extend(true, {}, defaults, options); var params = $.extend(true, {}, defaults, options);
var initialParams = $.extend(true, {}, defaults, options);
var poolOrder; // Order of questions in a pool var poolOrder; // Order of questions in a pool
var currentQuestion = 0; var currentQuestion = 0;
var questionInstances = []; var questionInstances = [];
@ -145,14 +146,12 @@ H5P.QuestionSet = function (options, contentId, contentData) {
questionOrder = contentData.previousState.order; questionOrder = contentData.previousState.order;
} }
// Create a pool of questions if necessary
/** /**
* Randomizes questions in an array and updates an array containing their order * Randomizes questions in an array and updates an array containing their order
* @param {array} questions * @param {array} questions
* @param {array} questionOrder
* @return {Object.<array, array>} questionOrdering * @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]... // 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] }); 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++) { for (var i = 0; i< questionOrdering.length; i++) {
// Use a previous order if it exists // Use a previous order if it exists
if(questionOrder) { if(contentData.previousState && contentData.previousState.questionOrder) {
newOrder[i] = questionOrder[questionOrdering[i][1]]; newOrder[i] = questionOrder[questionOrdering[i][1]];
} }
else { 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 { return {
questions: questions, questions: questions,
questionOrder: newOrder questionOrder: newOrder
}; };
} };
// Create a pool (a subset) of questions if necessary // 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 a previous pool exists, recreate it
if(contentData.previousState && contentData.previousState.poolOrder) { if(contentData.previousState && contentData.previousState.poolOrder) {
@ -204,7 +203,7 @@ H5P.QuestionSet = function (options, contentId, contentData) {
} }
else { // Otherwise create a new pool else { // Otherwise create a new pool
// Randomize and get the results // Randomize and get the results
var poolResult = randomizeQuestionOrdering(params.questions, poolOrder); var poolResult = randomizeQuestionOrdering(params.questions);
var poolQuestions = poolResult.questions; var poolQuestions = poolResult.questions;
poolOrder = poolResult.questionOrder; 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; var question;
// If a previous order exists, use it // If a previous order exists, use it
if (questionOrder !== undefined) { if (questionOrder !== undefined) {
question = params.questions[questionOrder[i]]; question = questions[questionOrder[i]];
} }
else { else {
// Use a generic order when initialzing for the first time // Use a generic order when initialzing for the first time
question = params.questions[i]; question = questions[i];
} }
if (override) { if (override) {
// Extend subcontent with the overrided settings. // Extend subcontent with the overrided settings.
$.extend(question.params.behaviour, override); $.extend(question.params.behaviour, override);
} }
question.params = question.params || {}; question.params = question.params || {};
question.params.overrideSettings = question.params.overrideSettings || {}; question.params.overrideSettings = question.params.overrideSettings || {};
question.params.overrideSettings.$confirmationDialogParent = $template.last(); question.params.overrideSettings.$confirmationDialogParent = $template.last();
question.params.overrideSettings.instance = this; question.params.overrideSettings.instance = this;
var hasAnswers = contentData.previousState && contentData.previousState.answers; var hasAnswers = contentData.previousState && contentData.previousState.answers;
var questionInstance = H5P.newRunnable(question, contentId, undefined, undefined, var questionInstance = H5P.newRunnable(question, contentId, undefined, undefined,
{ {
previousState: hasAnswers ? contentData.previousState.answers[i] : undefined, previousState: hasAnswers ? contentData.previousState.answers[i] : undefined,
parent: self parent: self
});
questionInstance.on('resize', function () {
up = true;
self.trigger('resize');
}); });
questionInstance.on('resize', function () { result.push(questionInstance);
up = true; }
self.trigger('resize');
}); return result;
questionInstances.push(questionInstance);
} }
// Create question instances from questions given by params
questionInstances = createQuestionInstancesFromQuestions(params.questions);
// Randomize questions only on instantiation // Randomize questions only on instantiation
if (params.randomQuestions && contentData.previousState === undefined) { if (params.randomQuestions && contentData.previousState === undefined) {
var result = randomizeQuestionOrdering(questionInstances,questionOrder); var result = randomizeQuestionOrdering(questionInstances);
questionInstances = result.questions; questionInstances = result.questions;
questionOrder = result.questionOrder; questionOrder = result.questionOrder;
} }
@ -440,7 +454,12 @@ H5P.QuestionSet = function (options, contentId, contentData) {
* @public * @public
*/ */
var resetTask = function () { var resetTask = function () {
// Clear previous state to ensure questions are created cleanly
contentData.previousState = [];
showingSolutions = false; showingSolutions = false;
for (var i = 0; i < questionInstances.length; i++) { for (var i = 0; i < questionInstances.length; i++) {
try { try {
questionInstances[i].resetTask(); questionInstances[i].resetTask();
@ -477,7 +496,28 @@ H5P.QuestionSet = function (options, contentId, contentData) {
//Force the last page to be reRendered //Force the last page to be reRendered
rendered = false; 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(); randomizeQuestions();
} }
@ -494,10 +534,21 @@ H5P.QuestionSet = function (options, contentId, contentData) {
*/ */
var randomizeQuestions = function () { var randomizeQuestions = function () {
var result = randomizeQuestionOrdering(questionInstances,questionOrder); var result = randomizeQuestionOrdering(questionInstances);
questionInstances = result.questions; questionInstances = result.questions;
questionOrder = result.questionOrder; 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 // Find all question containers and detach questions from them
$('.question-container', $myDom).each(function (){ $('.question-container', $myDom).each(function (){
$(this).children().detach(); $(this).children().detach();
@ -516,18 +567,18 @@ H5P.QuestionSet = function (options, contentId, contentData) {
//Show buttons if necessary //Show buttons if necessary
if(questionInstances[questionInstances.length -1] === question if(questionInstances[questionInstances.length -1] === question
&& question.hasButton('finish')) { && question.hasButton('finish')) {
question.showButton('finish'); question.showButton('finish');
} }
if(questionInstances[questionInstances.length -1] !== question if(questionInstances[questionInstances.length -1] !== question
&& question.hasButton('next')) { && question.hasButton('next')) {
question.showButton('next'); question.showButton('next');
} }
if(questionInstances[0] !== question if(questionInstances[0] !== question
&& question.hasButton('prev') && question.hasButton('prev')
&& !params.disableBackwardsNavigation) { && !params.disableBackwardsNavigation) {
question.showButton('prev'); question.showButton('prev');
} }
// Hide relevant buttons since the order has changed // Hide relevant buttons since the order has changed
@ -542,10 +593,8 @@ H5P.QuestionSet = function (options, contentId, contentData) {
if (questionInstances[questionInstances.length-1] !== question) { if (questionInstances[questionInstances.length-1] !== question) {
question.hideButton('finish'); question.hideButton('finish');
} }
} }
};
}
var moveQuestion = function (direction) { var moveQuestion = function (direction) {
if (params.disableBackwardsNavigation && !questionInstances[currentQuestion].getAnswerGiven()) { if (params.disableBackwardsNavigation && !questionInstances[currentQuestion].getAnswerGiven()) {
@ -762,7 +811,77 @@ H5P.QuestionSet = function (options, contentId, contentData) {
self.trigger('resize'); 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) { this.attach = function (target) {
if (this.isRoot()) { if (this.isRoot()) {
this.setActivityStarted(); 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 initializeQuestion();
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());
}
// Allow other libraries to add transitions after the questions have been inited // Allow other libraries to add transitions after the questions have been inited
$('.questionset', $myDom).addClass('started'); $('.questionset', $myDom).addClass('started');

View File

@ -4,7 +4,7 @@
"contentType": "question", "contentType": "question",
"majorVersion": 1, "majorVersion": 1,
"minorVersion": 10, "minorVersion": 10,
"patchVersion": 0, "patchVersion": 1,
"embedTypes": [ "embedTypes": [
"iframe" "iframe"
], ],