Actualización
This commit is contained in:
@@ -0,0 +1,483 @@
|
||||
H5P.TrueFalse = (function ($, Question) {
|
||||
'use strict';
|
||||
|
||||
// Maximum score for True False
|
||||
var MAX_SCORE = 1;
|
||||
|
||||
/**
|
||||
* Enum containing the different states this content type can exist in
|
||||
*
|
||||
* @enum
|
||||
*/
|
||||
var State = Object.freeze({
|
||||
ONGOING: 1,
|
||||
FINISHED_WRONG: 2,
|
||||
FINISHED_CORRECT: 3,
|
||||
INTERNAL_SOLUTION: 4,
|
||||
EXTERNAL_SOLUTION: 5
|
||||
});
|
||||
|
||||
/**
|
||||
* Button IDs
|
||||
*/
|
||||
var Button = Object.freeze({
|
||||
CHECK: 'check-answer',
|
||||
TRYAGAIN: 'try-again',
|
||||
SHOW_SOLUTION: 'show-solution'
|
||||
});
|
||||
|
||||
/**
|
||||
* Initialize module.
|
||||
*
|
||||
* @class H5P.TrueFalse
|
||||
* @extends H5P.Question
|
||||
* @param {Object} options
|
||||
* @param {number} id Content identification
|
||||
* @param {Object} contentData Task specific content data
|
||||
*/
|
||||
function TrueFalse(options, id, contentData) {
|
||||
var self = this;
|
||||
|
||||
// Inheritance
|
||||
Question.call(self, 'true-false');
|
||||
|
||||
var params = $.extend(true, {
|
||||
question: 'No question text provided',
|
||||
correct: 'true',
|
||||
l10n: {
|
||||
trueText: 'True',
|
||||
falseText: 'False',
|
||||
score: 'You got @score of @total points',
|
||||
checkAnswer: 'Check',
|
||||
showSolutionButton: 'Show solution',
|
||||
tryAgain: 'Retry',
|
||||
wrongAnswerMessage: 'Wrong answer',
|
||||
correctAnswerMessage: 'Correct answer',
|
||||
scoreBarLabel: 'You got :num out of :total points'
|
||||
},
|
||||
behaviour: {
|
||||
enableRetry: true,
|
||||
enableSolutionsButton: true,
|
||||
enableCheckButton: true,
|
||||
confirmCheckDialog: false,
|
||||
confirmRetryDialog: false,
|
||||
autoCheck: false
|
||||
}
|
||||
}, options);
|
||||
|
||||
// Counter used to create unique id for this question
|
||||
TrueFalse.counter = (TrueFalse.counter === undefined ? 0 : TrueFalse.counter + 1);
|
||||
|
||||
// A unique ID is needed for aria label
|
||||
var domId = 'h5p-tfq' + H5P.TrueFalse.counter;
|
||||
|
||||
// saves the content id
|
||||
this.contentId = id;
|
||||
this.contentData = contentData;
|
||||
|
||||
// The radio group
|
||||
var answerGroup = new H5P.TrueFalse.AnswerGroup(domId, params.correct, params.l10n);
|
||||
if (contentData.previousState !== undefined && contentData.previousState.answer !== undefined) {
|
||||
answerGroup.check(contentData.previousState.answer);
|
||||
}
|
||||
answerGroup.on('selected', function () {
|
||||
self.triggerXAPI('interacted');
|
||||
|
||||
if (params.behaviour.autoCheck) {
|
||||
checkAnswer();
|
||||
triggerXAPIAnswered();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Create the answers
|
||||
*
|
||||
* @method createAnswers
|
||||
* @private
|
||||
* @return {H5P.jQuery}
|
||||
*/
|
||||
var createAnswers = function () {
|
||||
return answerGroup.getDomElement();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register buttons
|
||||
*
|
||||
* @method registerButtons
|
||||
* @private
|
||||
*/
|
||||
var registerButtons = function () {
|
||||
var $content = $('[data-content-id="' + self.contentId + '"].h5p-content');
|
||||
var $containerParents = $content.parents('.h5p-container');
|
||||
|
||||
// select find container to attach dialogs to
|
||||
var $container;
|
||||
if($containerParents.length !== 0) {
|
||||
// use parent highest up if any
|
||||
$container = $containerParents.last();
|
||||
}
|
||||
else if($content.length !== 0){
|
||||
$container = $content;
|
||||
}
|
||||
else {
|
||||
$container = $(document.body);
|
||||
}
|
||||
|
||||
// Show solution button
|
||||
if (params.behaviour.enableSolutionsButton === true) {
|
||||
self.addButton(Button.SHOW_SOLUTION, params.l10n.showSolutionButton, function () {
|
||||
self.showSolutions(true);
|
||||
}, false);
|
||||
}
|
||||
|
||||
// Check button
|
||||
if (!params.behaviour.autoCheck && params.behaviour.enableCheckButton) {
|
||||
self.addButton(Button.CHECK, params.l10n.checkAnswer, function () {
|
||||
checkAnswer();
|
||||
triggerXAPIAnswered();
|
||||
}, true, {}, {
|
||||
confirmationDialog: {
|
||||
enable: params.behaviour.confirmCheckDialog,
|
||||
l10n: params.confirmCheck,
|
||||
instance: self,
|
||||
$parentElement: $container
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Try again button
|
||||
if (params.behaviour.enableRetry === true) {
|
||||
self.addButton(Button.TRYAGAIN, params.l10n.tryAgain, function () {
|
||||
self.resetTask();
|
||||
}, true, {}, {
|
||||
confirmationDialog: {
|
||||
enable: params.behaviour.confirmRetryDialog,
|
||||
l10n: params.confirmRetry,
|
||||
instance: self,
|
||||
$parentElement: $container
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleButtonState(State.ONGOING);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and triggers the xAPI answered event
|
||||
*
|
||||
* @method triggerXAPIAnswered
|
||||
* @private
|
||||
* @fires xAPIEvent
|
||||
*/
|
||||
var triggerXAPIAnswered = function () {
|
||||
var xAPIEvent = self.createXAPIEventTemplate('answered');
|
||||
addQuestionToXAPI(xAPIEvent);
|
||||
addResponseToXAPI(xAPIEvent);
|
||||
self.trigger(xAPIEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the question itself to the definition part of an xAPIEvent
|
||||
*
|
||||
* @method addQuestionToXAPI
|
||||
* @param {XAPIEvent} xAPIEvent
|
||||
* @private
|
||||
*/
|
||||
var addQuestionToXAPI = function(xAPIEvent) {
|
||||
var definition = xAPIEvent.getVerifiedStatementValue(['object', 'definition']);
|
||||
definition.description = {
|
||||
// Remove tags, must wrap in div tag because jQuery 1.9 will crash if the string isn't wrapped in a tag.
|
||||
'en-US': $('<div>' + params.question + '</div>').text()
|
||||
};
|
||||
definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction';
|
||||
definition.interactionType = 'true-false';
|
||||
definition.correctResponsesPattern = [getCorrectAnswer()];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the correct answer
|
||||
*
|
||||
* @method getCorrectAnswer
|
||||
* @private
|
||||
* @return {String}
|
||||
*/
|
||||
var getCorrectAnswer = function () {
|
||||
return (params.correct === 'true' ? 'true' : 'false');
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the wrong answer
|
||||
*
|
||||
* @method getWrongAnswer
|
||||
* @private
|
||||
* @return {String}
|
||||
*/
|
||||
var getWrongAnswer = function () {
|
||||
return (params.correct === 'false' ? 'true' : 'false');
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the response part to an xAPI event
|
||||
*
|
||||
* @method addResponseToXAPI
|
||||
* @private
|
||||
* @param {H5P.XAPIEvent} xAPIEvent
|
||||
* The xAPI event we will add a response to
|
||||
*/
|
||||
var addResponseToXAPI = function(xAPIEvent) {
|
||||
var isCorrect = answerGroup.isCorrect();
|
||||
xAPIEvent.setScoredResult(isCorrect ? MAX_SCORE : 0, MAX_SCORE, self, true, isCorrect);
|
||||
xAPIEvent.data.statement.result.response = (isCorrect ? getCorrectAnswer() : getWrongAnswer());
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles btton visibility dependent of current state
|
||||
*
|
||||
* @method toggleButtonVisibility
|
||||
* @private
|
||||
* @param {String} buttonId
|
||||
* @param {Boolean} visible
|
||||
*/
|
||||
var toggleButtonVisibility = function (buttonId, visible) {
|
||||
if (visible === true) {
|
||||
self.showButton(buttonId);
|
||||
}
|
||||
else {
|
||||
self.hideButton(buttonId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles buttons state
|
||||
*
|
||||
* @method toggleButtonState
|
||||
* @private
|
||||
* @param {String} state
|
||||
*/
|
||||
var toggleButtonState = function (state) {
|
||||
toggleButtonVisibility(Button.SHOW_SOLUTION, state === State.FINISHED_WRONG);
|
||||
toggleButtonVisibility(Button.CHECK, state === State.ONGOING);
|
||||
toggleButtonVisibility(Button.TRYAGAIN, state === State.FINISHED_WRONG || state === State.INTERNAL_SOLUTION);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if answer is correct or wrong, and update visuals accordingly
|
||||
*
|
||||
* @method checkAnswer
|
||||
* @private
|
||||
*/
|
||||
var checkAnswer = function () {
|
||||
// Create feedback widget
|
||||
var score = self.getScore();
|
||||
var scoreText;
|
||||
|
||||
toggleButtonState(score === MAX_SCORE ? State.FINISHED_CORRECT : State.FINISHED_WRONG);
|
||||
|
||||
if (score === MAX_SCORE && params.behaviour.feedbackOnCorrect) {
|
||||
scoreText = params.behaviour.feedbackOnCorrect;
|
||||
}
|
||||
else if (score === 0 && params.behaviour.feedbackOnWrong) {
|
||||
scoreText = params.behaviour.feedbackOnWrong;
|
||||
}
|
||||
else {
|
||||
scoreText = params.l10n.score;
|
||||
}
|
||||
// Replace relevant variables:
|
||||
scoreText = scoreText.replace('@score', score).replace('@total', MAX_SCORE);
|
||||
self.setFeedback(scoreText, score, MAX_SCORE, params.l10n.scoreBarLabel);
|
||||
answerGroup.reveal();
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers this question type's DOM elements before they are attached.
|
||||
* Called from H5P.Question.
|
||||
*
|
||||
* @method registerDomElements
|
||||
* @private
|
||||
*/
|
||||
self.registerDomElements = function () {
|
||||
var self = this;
|
||||
|
||||
// Check for task media
|
||||
var media = params.media;
|
||||
if (media && media.type && media.type.library) {
|
||||
media = media.type;
|
||||
var type = media.library.split(' ')[0];
|
||||
if (type === 'H5P.Image') {
|
||||
if (media.params.file) {
|
||||
// Register task image
|
||||
self.setImage(media.params.file.path, {
|
||||
disableImageZooming: params.media.disableImageZooming || false,
|
||||
alt: media.params.alt
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (type === 'H5P.Video') {
|
||||
if (media.params.sources) {
|
||||
// Register task video
|
||||
self.setVideo(media);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add task question text
|
||||
self.setIntroduction('<div id="' + domId + '">' + params.question + '</div>');
|
||||
|
||||
// Register task content area
|
||||
self.$content = createAnswers();
|
||||
self.setContent(self.$content);
|
||||
|
||||
// ... and buttons
|
||||
registerButtons();
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements resume (save content state)
|
||||
*
|
||||
* @method getCurrentState
|
||||
* @public
|
||||
* @returns {object} object containing answer
|
||||
*/
|
||||
self.getCurrentState = function () {
|
||||
return {answer: answerGroup.getAnswer()};
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for contracts.
|
||||
* Checks if the parent program can proceed. Always true.
|
||||
*
|
||||
* @method getAnswerGiven
|
||||
* @public
|
||||
* @returns {Boolean} true
|
||||
*/
|
||||
self.getAnswerGiven = function () {
|
||||
return answerGroup.hasAnswered();
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for contracts.
|
||||
* Checks the current score for this task.
|
||||
*
|
||||
* @method getScore
|
||||
* @public
|
||||
* @returns {Number} The current score.
|
||||
*/
|
||||
self.getScore = function () {
|
||||
return answerGroup.isCorrect() ? MAX_SCORE : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for contracts.
|
||||
* Checks the maximum score for this task.
|
||||
*
|
||||
* @method getMaxScore
|
||||
* @public
|
||||
* @returns {Number} The maximum score.
|
||||
*/
|
||||
self.getMaxScore = function () {
|
||||
return MAX_SCORE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get title of task
|
||||
*
|
||||
* @method getTitle
|
||||
* @public
|
||||
* @returns {string} title
|
||||
*/
|
||||
self.getTitle = function () {
|
||||
return H5P.createTitle((self.contentData && self.contentData.metadata && self.contentData.metadata.title) ? self.contentData.metadata.title : 'True-False');
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for contracts.
|
||||
* Show the solution.
|
||||
*
|
||||
* @method showSolutions
|
||||
* @public
|
||||
*/
|
||||
self.showSolutions = function (internal) {
|
||||
checkAnswer();
|
||||
answerGroup.showSolution();
|
||||
toggleButtonState(internal ? State.INTERNAL_SOLUTION : State.EXTERNAL_SOLUTION);
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for contracts.
|
||||
* Resets the complete task back to its' initial state.
|
||||
*
|
||||
* @method resetTask
|
||||
* @public
|
||||
*/
|
||||
self.resetTask = function () {
|
||||
answerGroup.reset();
|
||||
self.removeFeedback();
|
||||
toggleButtonState(State.ONGOING);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get xAPI data.
|
||||
* Contract used by report rendering engine.
|
||||
*
|
||||
* @see contract at {@link https://h5p.org/documentation/developers/contracts#guides-header-6}
|
||||
*/
|
||||
self.getXAPIData = function(){
|
||||
var xAPIEvent = this.createXAPIEventTemplate('answered');
|
||||
this.addQuestionToXAPI(xAPIEvent);
|
||||
this.addResponseToXAPI(xAPIEvent);
|
||||
return {
|
||||
statement: xAPIEvent.data.statement
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the question itself to the definition part of an xAPIEvent
|
||||
*/
|
||||
self.addQuestionToXAPI = function(xAPIEvent) {
|
||||
var definition = xAPIEvent.getVerifiedStatementValue(['object', 'definition']);
|
||||
$.extend(definition, this.getxAPIDefinition());
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate xAPI object definition used in xAPI statements.
|
||||
* @return {Object}
|
||||
*/
|
||||
self.getxAPIDefinition = function () {
|
||||
var definition = {};
|
||||
definition.interactionType = 'true-false';
|
||||
definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction';
|
||||
definition.description = {
|
||||
'en-US': $('<div>' + params.question + '</div>').text()
|
||||
};
|
||||
definition.correctResponsesPattern = [getCorrectAnswer()];
|
||||
|
||||
return definition;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the response part to an xAPI event
|
||||
*
|
||||
* @param {H5P.XAPIEvent} xAPIEvent
|
||||
* The xAPI event we will add a response to
|
||||
*/
|
||||
self.addResponseToXAPI = function (xAPIEvent) {
|
||||
var isCorrect = answerGroup.isCorrect();
|
||||
var rawUserScore = isCorrect ? MAX_SCORE : 0;
|
||||
var currentResponse = '';
|
||||
|
||||
xAPIEvent.setScoredResult(rawUserScore, MAX_SCORE, self, true, isCorrect);
|
||||
|
||||
if(self.getCurrentState().answer !== undefined) {
|
||||
currentResponse += answerGroup.isCorrect() ? getCorrectAnswer() : getWrongAnswer();
|
||||
}
|
||||
xAPIEvent.data.statement.result.response = currentResponse;
|
||||
};
|
||||
}
|
||||
|
||||
// Inheritance
|
||||
TrueFalse.prototype = Object.create(Question.prototype);
|
||||
TrueFalse.prototype.constructor = TrueFalse;
|
||||
|
||||
return TrueFalse;
|
||||
})(H5P.jQuery, H5P.Question);
|
||||
Reference in New Issue
Block a user