diff -r 539c9bee5372 -r 7623f9af9272 src/widgets/Quiz.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/Quiz.js Fri Oct 02 11:27:17 2015 +0200 @@ -0,0 +1,388 @@ +IriSP.Widgets.Quiz = function(player, config) { + IriSP.Widgets.Widget.call(this, player, config); +} + +IriSP.Widgets.Quiz.prototype = new IriSP.Widgets.Widget(); + +IriSP.Widgets.Quiz.prototype.defaults = { + // annotation_type: "at_quiz", + quiz_activated: true, + api_serializer: "ldt_annotate", + analytics_api: "", + api_method: "POST", + user: "", + userid:"" +} + +IriSP.Widgets.Quiz.prototype.template = '
' + + '
' + + '
' + + '
' + + '
' + + '

{{question}}

' + + '
' + + '
' + + '
' + + '' + + '
'; + +IriSP.Widgets.Quiz.prototype.annotationTemplate = ''; + +IriSP.Widgets.Quiz.prototype.update = function(annotation) { + var _this = this; + + if (this.quiz_activated && + this.correct[annotation.id] != 1 && + this.correct[annotation.id] != 0) { + + _this.quiz_displayed = true; + + //Pause the current video + this.media.pause(); + + this.annotation = annotation; + + var question = annotation.content.data.question; + var answers = annotation.content.data.answers; + var resource = annotation.content.data.resource; + + $(".Ldt-Quiz-Votes").hide(); + $(".Ldt-Pause-Add-Question").hide(); + $(".Ldt-Quiz-Container .Ldt-Quiz-Title").html(question); + + var i = 0; + + var score = Mustache.to_html('{{ correctness.0 }} / {{ correctness.1 }}', { correctness: this.globalScore() }); + $(".Ldt-Quiz-Index").html(Mustache.to_html("Q{{index}}/{{total}}", { index: annotation.number + 1, + total: this.totalAmount })); + $(".Ldt-Quiz-Score").html(score); + this.question = new IriSP.Widgets.UniqueChoiceQuestion(annotation); + this.resource = new IriSP.Widgets.UniqueChoiceQuestion(resource); + + if (annotation.content.data.type == "multiple_choice") { + this.question = new IriSP.Widgets.MultipleChoiceQuestion(annotation); + this.resource = new IriSP.Widgets.MultipleChoiceQuestion(resource); + } + else if (annotation.content.data.type == "unique_choice") { + this.question = new IriSP.Widgets.UniqueChoiceQuestion(annotation); + this.resource = new IriSP.Widgets.UniqueChoiceQuestion(resource); + } + + var output = ""; + for (i = 0; i < answers.length; i++) { + output += '

' + this.question.renderQuizTemplate(answers[i], i) + ''+ answers[i].content + '

'; + } + + + var QR = ''; + //If there is an attached resource, display it on the resources overlay + if (resource != null) { + QR = '
' + resource + '
'; + }; + $(".Ldt-Quiz-Questions").html(QR + output); + $(".Ldt-Quiz-Overlay").fadeIn(); + + $(".Ldt-Quiz-Submit").fadeIn(); + + //Let's automatically check the checkbox/radio if we click on the label + $(".quiz-question-label").click(function() { + var input = $(this).siblings("input"); + if (input.prop('checked') && input.prop('type') == 'radio') { + // Already checked. Consider a double click on unique question as a validation. + _this.answer(); + } else { + input.prop('checked', !input.prop('checked')); + } + }); + + //In case we click on the first "Skip" link + $(".Ldt-Quiz-Submit-Skip-Link").click({media: this.media}, function(event) { + _this.hide(); + _this.player.trigger("QuizCreator.skip"); + event.data.media.play(); + }); + } +}; + +IriSP.Widgets.Quiz.prototype.hide = function() { + var _this = this; + + $(".Ldt-Quiz-Votes").hide(); + $(".Ldt-Quiz-Overlay").hide(); + $(".Ldt-Pause-Add-Question").hide(); + _this.quiz_displayed = false; +} + +IriSP.Widgets.Quiz.prototype.answer = function() { + var _this = this; + + function insert_timecode_links (s) { + return (s || "").replace(/\s(\d+:\d+)/, function (match, timecode) { + return ' ' + timecode + ''; + }); + }; + + var answers = _this.annotation.content.data.answers; + + // Augment answers with the correct feedback + var i = 0; + var wrong = 0; + // Signature is an array giving the answers signature: 1 for checked, 0 for unchecked + // We cannot simply store the right answer index, since there may be multiple-choice questions + var signature = []; + _this.$.find(".Ldt-Quiz-Question-Check").each( function (code) { + var checked = $(this).is(":checked"); + signature.push(checked ? 1 : 0); + var ans = answers[i]; + if ((ans.correct && !checked) + || (!ans.correct && checked)) { + wrong += 1; + IriSP.jQuery(this).parents(".quiz-question-block").append('
'+ insert_timecode_links(ans.feedback) +'
'); + } else { + IriSP.jQuery(this).parents(".quiz-question-block").append('
'+ insert_timecode_links(ans.feedback) +'
'); + } + i++; + }); + + if (wrong) { + $(".Ldt-Quiz-Result").html("Mauvaise réponse"); + $(".Ldt-Quiz-Result").css({"background-color" : "red"}); + this.correct[this.annotation.id] = 0; + } else { + $(".Ldt-Quiz-Result").html("Bonne réponse !"); + $(".Ldt-Quiz-Result").css({"background-color" : "green"}); + this.correct[this.annotation.id] = 1; + } + + $(".Ldt-Quiz-Result").animate({height:"100%"},500, "linear", function() { + $(".Ldt-Quiz-Result").delay(2000).animate({ height:"0%" }, 500); + }); + + var question_number = this.annotation.number + 1; + var correctness = this.globalScore(); + var score = ""; + score += '' + correctness[0] +' / ' + correctness[1] + ''; + $(".Ldt-Quiz-Index").html("Q"+ question_number + "/" + this.totalAmount); + $(".Ldt-Quiz-Score").html(score); + + this.submit(this.user, this.userid, this.annotation.id, wrong ? 'wrong_answer' : 'right_answer', signature.join("")); + + //Hide the "Validate" button and display the UI dedicated to votes + $(".Ldt-Quiz-Submit").fadeOut(400, function () { + $(".Ldt-Quiz-Votes").show(); + }); +}; + +IriSP.Widgets.Quiz.prototype.globalScore = function() { + // Return 2 variables to know how many right and wrong answers there are + var values = _.values(this.correct); + var ok = values.filter( function (s) { return s == 1; }).length; + var not_ok = values.filter( function (s) { return s == 0; }).length; + return [ok, not_ok]; +}; + +IriSP.Widgets.Quiz.prototype.refresh = function() { + var _annotations = this.getWidgetAnnotations().sortBy(function(_annotation) { + return _annotation.begin; + }); + + var _this = this; + + _this.totalAmount = _annotations.length; + _this.number = 0; + _this.correct = {}; + _this.keys = {}; + + _annotations.forEach(function(_a) { + //Fix each annotation as "non-answered yet" + _this.correct[_a.id] = -1; + _this.keys[_this.number] = _a.id; + _a.number = _this.number++; + }); + +} + +IriSP.Widgets.Quiz.prototype.draw = function() { + var _this = this; + _this.quiz_displayed = false; + this.onMediaEvent("enter-annotation", function (annotation) { + var an = _this.getWidgetAnnotations().filter( function (a) { return a === annotation; }); + if (an.number === undefined) { + _this.refresh(); + } + if (an.length) { + _this.update(an[0]); + }; + }); + this.onMdpEvent("Quiz.activate", function() { + _this.quiz_activated = true; + }); + + this.onMdpEvent("Quiz.deactivate", function() { + _this.quiz_activated = false; + _this.hide(); + }); + + this.onMdpEvent("Quiz.hide", function() { + _this.hide(); + }); + + this.onMdpEvent("Quiz.refresh", function() { + _this.refresh(); + }); + + this.onMediaEvent("pause", function() { + if (! _this.quiz_displayed) { + $(".Ldt-Pause-Add-Question").show(); + } + }); + + this.onMediaEvent("play", function() { + $(".Ldt-Pause-Add-Question").hide(); + }); + + // Add Ldt-Quiz-Overlay widget on top of video player + _this.overlay = $("
").appendTo($('#' + _this.container)); + _this.PauseAddQuestion = $("
") + .on("click", function() { _this.player.trigger("QuizCreator.create"); }) + .appendTo($('#' + _this.container)); + _this.overlay.html(this.template); + + $(".Ldt-Quiz-Overlay").hide(); + + $(".Ldt-Quiz-Submit input").click(function() { + _this.answer(); + }); + + //In case we click on the first "Skip" link + $(".Ldt-Quiz-Submit-Skip-Link").click({ media: this.media }, function(event) { + _this.submit(_this.user, _this.userid, _this.annotation.id, "skipped_answer", 0); + _this.hide(); + _this.player.trigger("QuizCreator.skip"); + event.data.media.play(); + }); + + $(".Ldt-Quiz-Votes-Buttons input[type=\"button\"], .Ldt-Quiz-Votes-Buttons a").click({media: this.media}, function(event) { + var vote_prop, vote_val; + + if ($(this).hasClass("Ldt-Quiz-Vote-Useful")) { + vote_prop = "useful"; + vote_val = 1; + } else if ($(this).hasClass("Ldt-Quiz-Vote-Useless")) { + vote_prop = "useless"; + vote_val = -1; + + $(".Ldt-Ctrl-Quiz-Create").addClass("button_highlight").delay(5000).queue(function() { + $(this).removeClass("button_highlight").dequeue(); + }); + }else{ + vote_prop = "skipped_vote"; + vote_val = 0; + } + + _this.submit(_this.user, _this.userid, _this.annotation.id, vote_prop, vote_val); + + //Resume the current video + event.data.media.play(); + + _this.hide(); + $(".Ldt-Pause-Add-Question").hide(); + + _this.player.trigger("QuizCreator.skip"); + }); + + _this.refresh(); +}; + +//Generates uid +//source : http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript +IriSP.Widgets.Widget.prototype.generateUid = function () { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); +} + +//UniqueChoice Question +IriSP.Widgets.UniqueChoiceQuestion = function(annotation) { + this.annotation = annotation; +} + +IriSP.Widgets.UniqueChoiceQuestion.prototype = new IriSP.Widgets.Widget(); + +IriSP.Widgets.UniqueChoiceQuestion.prototype.renderQuizTemplate = function(answer, identifier) { + return ''; +} + +IriSP.Widgets.UniqueChoiceQuestion.prototype.renderTemplate = function(answer, identifier) { + var id = this.generateUid(); + return ''; +} + +IriSP.Widgets.UniqueChoiceQuestion.prototype.renderFullTemplate = function(answer, identifier) { + var correct = (answer && answer.correct) ? "checked" : ""; + var id = this.generateUid(); + return ''; +} + + +//MultipleChoice Question +IriSP.Widgets.MultipleChoiceQuestion = function(annotation) { + this.annotation = annotation; +} + +IriSP.Widgets.MultipleChoiceQuestion.prototype = new IriSP.Widgets.Widget(); + +IriSP.Widgets.MultipleChoiceQuestion.prototype.renderQuizTemplate = function(answer, identifier) { + return ' '; +} + +IriSP.Widgets.MultipleChoiceQuestion.prototype.renderTemplate = function(answer, identifier) { + var id = this.generateUid(); + return ''; +} + +IriSP.Widgets.MultipleChoiceQuestion.prototype.renderFullTemplate = function(answer, identifier) { + var correct = (answer && answer.correct) ? "checked" : ""; + var id = this.generateUid(); + return ' '; +} + +IriSP.Widgets.Quiz.prototype.submit = function(user,user_id,question,prop,val) { + var _this = this; + var _url = Mustache.to_html(this.analytics_api, {id: this.source.projectId}), + donnees = { + "username": user, + "useruuid": user_id, + "subject": question, + "property": prop, + "value": val, + "session": _this.session_id + }; + + IriSP.jQuery.ajax({ + url: _url, + type: this.api_method, + contentType: 'application/json', + data: JSON.stringify(donnees), + success: function(_data) { + }, + error: function(_xhr, _error, _thrown) { + IriSP.log("Error when sending annotation", _thrown); + } + }); +}