diff --git a/.h5pignore b/.h5pignore new file mode 100644 index 0000000..909043a --- /dev/null +++ b/.h5pignore @@ -0,0 +1,2 @@ +crowdin.yml +.gitignore \ No newline at end of file diff --git a/js/blanks.js b/js/blanks.js index ae731d6..3cea7d3 100644 --- a/js/blanks.js +++ b/js/blanks.js @@ -9,6 +9,10 @@ H5P.Blanks = (function ($, Question) { var STATE_SHOWING_SOLUTION = 'showing-solution'; var STATE_FINISHED = 'finished'; + const XAPI_ALTERNATIVE_EXTENSION = 'https://h5p.org/x-api/alternatives'; + const XAPI_CASE_SENSITIVITY = 'https://h5p.org/x-api/case-sensitivity'; + const XAPI_REPORTING_VERSION_EXTENSION = 'https://h5p.org/x-api/h5p-reporting-version'; + /** * @typedef {Object} Params * Parameters/configuration object for Blanks @@ -624,39 +628,48 @@ H5P.Blanks = (function ($, Question) { }; definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction'; definition.interactionType = 'fill-in'; - definition.correctResponsesPattern = ['{case_matters=' + this.params.behaviour.caseSensitive + '}']; - var firstCorrectResponse = true; + const crpPrefix = '{case_matters=' + this.params.behaviour.caseSensitive + '}'; + const clozeSolutions = []; // xAPI forces us to create solution patterns for all possible solution combinations for (var i = 0; i < this.params.questions.length; i++) { var question = this.handleBlanks(this.params.questions[i], function (solution) { - // Store new patterns for each extra alternative answer - var newPatterns = []; - for (var j = 0; j < definition.correctResponsesPattern.length; j++) { - if (!firstCorrectResponse) { - definition.correctResponsesPattern[j] += '[,]'; - } - var prefix = definition.correctResponsesPattern[j]; - for (var k = 0; k < solution.solutions.length; k++) { - if (k === 0) { - // This is the first possible answr, just add it to the pattern - definition.correctResponsesPattern[j] += solution.solutions[k]; - } - else { - // This is an alternative possible answer, we need to create a new permutation - newPatterns.push(prefix + solution.solutions[k]); - } - } - } - // Add any new permutations to the list of response patterns - definition.correctResponsesPattern = definition.correctResponsesPattern.concat(newPatterns); - - firstCorrectResponse = false; + // Collect solutions + clozeSolutions.push(solution.solutions); // We replace the solutions in the question with a "blank" return '__________'; }); definition.description['en-US'] += question; } + + const hasAlternatives = clozeSolutions.some(function (cloze) { + return cloze.length > 1; + }); + + /** + * Note: + * If alternatives are used we don't provide a correct responses pattern + * since the solution space grows exponentially. We instead provide an + * extension for allowing reports to be generated. + * This is the recommended approach ref: + * @see(https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#correct-responses-pattern) + */ + if (hasAlternatives) { + // Use extension to avoid exponentially growing solution space + definition.extensions = definition.extensions || {}; + definition.extensions[XAPI_CASE_SENSITIVITY] = this.params.behaviour.caseSensitive; + definition.extensions[XAPI_ALTERNATIVE_EXTENSION] = clozeSolutions; + } + else { + // Use correct responses pattern + const singleAnswer = clozeSolutions.join('[,]'); + + definition.correctResponsesPattern = [ + crpPrefix + singleAnswer, + ]; + } + + return definition; }; @@ -665,7 +678,14 @@ H5P.Blanks = (function ($, Question) { */ Blanks.prototype.addQuestionToXAPI = function (xAPIEvent) { var definition = xAPIEvent.getVerifiedStatementValue(['object', 'definition']); - $.extend(definition, this.getxAPIDefinition()); + $.extend(true, definition, this.getxAPIDefinition()); + + // Set reporting module version if alternative extension is used + if (definition.extensions && definition.extensions[XAPI_ALTERNATIVE_EXTENSION]) { + const context = xAPIEvent.getVerifiedStatementValue(['context']); + context.extensions = context.extensions || {}; + context.extensions[XAPI_REPORTING_VERSION_EXTENSION] = '1.1.0'; + } }; /**