From 85a18446fb863939d7930182eaec4ac6929df22f Mon Sep 17 00:00:00 2001 From: Frank Ronny Larsen Date: Wed, 16 Jan 2013 14:37:45 +0100 Subject: [PATCH 01/83] Added H5P libraries --- content.json | 0 js/questionset.js | 305 ++++++++++++++++++++++++++++++++++ library.json | 0 schema.json | 168 +++++++++++++++++++ views/questionset-intro.ejs | 5 + views/questionset-results.ejs | 6 + views/questionset.ejs | 60 +++++++ 7 files changed, 544 insertions(+) create mode 100644 content.json create mode 100644 js/questionset.js create mode 100644 library.json create mode 100644 schema.json create mode 100644 views/questionset-intro.ejs create mode 100644 views/questionset-results.ejs create mode 100644 views/questionset.ejs diff --git a/content.json b/content.json new file mode 100644 index 0000000..e69de29 diff --git a/js/questionset.js b/js/questionset.js new file mode 100644 index 0000000..ca85d98 --- /dev/null +++ b/js/questionset.js @@ -0,0 +1,305 @@ +// Will render a Question with multiple choices for answers. + +// Options format: +// { +// title: "Optional title for question box", +// question: "Question text", +// answers: [{text: "Answer text", correct: false}, ...], +// singleAnswer: true, // or false, will change rendered output slightly. +// } +// +// Events provided: +// - h5pQuestionSetFinished: Triggered when a question is finished. (User presses Finish-button) +window.H5P = window.H5P || {}; + +H5P.QuestionSet = function (options) { + if ( !(this instanceof H5P.QuestionSet) ) + return new H5P.QuestionSet(options); + + var $ = H5P.jQuery; + + var texttemplate = '' + +'' + +'
' + +' <% if (introPage.showIntroPage) { %>' + +'
' + +'
<%= introPage.title %>
' + +'
<%= introPage.introduction %>
' + +'
' + +'
' + +' <%} %>' + +'
<%= title %>
' + +' <% for (var i=0; i' + +'
' + +'
<%= questions[i].machineName %>
' + +'
' + +' <% } %>' + +' ' + +'
' + + ''; + + var resulttemplate = '' + +'
' + +'
<%= greeting %>
' + +'
<%= score %>
' + +'
<%= resulttext %>
' + +'
' + +'
' + +''; + + var that = this; + var defaults = { + title: "", + randomOrder: false, + initialQuestion: 0, + backgroundImage: "", + progressType: 'dots', + passPercentage: 50, + questions: [], + introPage: { + showIntroPage: true, + title: "Welcome", + introduction: "Click start to start.", + startButtonText: "Start" + }, + texts: { + prevButton: "Previous", + nextButton: "Next", + finishButton: "Finish", + textualProgress: "Question: @current of @total questions" + }, + endGame: { + showResultPage: true, + resultPage: { + succesGreeting: "Congratulations!", + successComment: "You have enough correct answers to pass the test.", + failGreeting: "Sorry!", + failComment: "You don't have enough correct answers to pass this test.", + scoreString: "@score/@total", + finishButtonText: "Finish" + }, + animations: { + showAnimations: false, + succesResultAnimation: { + machineName: "H5P.Image", + options: {image: ""} + }, + failedResultAnimation: { + machineName: "H5P.Image", + options: {image: ""} + } + } + } + }; + + var template = new EJS({text: texttemplate}); + var endTemplate = new EJS({text: resulttemplate}); + var params = $.extend({}, defaults, options); + + var currentQuestion = 0; + var questionInstances = new Array(); + var allQuestionsAnswered = false; + var $myDom; + + if (params.randomOrder) { + // TODO: Randomize order of questions + console.log("TODO: Randomize order of questions"); + } + + // Instantiate question instances + for (var i=0; i= 0; i--) { + answered = answered && (questionInstances[i]).getAnswerGiven(); + } + $('.finish.button', $myDom).attr({'disabled': !answered}); + }; + + var _showQuestion = function (questionNumber) { + // Sanitize input. + if (questionNumber < 0) { questionNumber = 0; } + if (questionNumber >= params.questions.length) { questionNumber = params.questions.length - 1; } + + $('.prev.button', $myDom).attr({'disabled': (questionNumber === 0)}); + $('.next.button', $myDom).attr({'disabled': (questionNumber == params.questions.length-1)}); + + + // Hide all questions + $('.question-container', $myDom).hide(); + + // Reshow the requested question + $('#q-' + questionNumber, $myDom).show(); + + // Update progress indicator + // Test if current has been answered. + if (params.progressType == 'textual') { + $('.progress-text', $myDom).text(params.texts.textualProgress.replace("@current", questionNumber+1).replace("@total", params.questions.length)); + } else { + // Set currentNess + $('.progress-dot.current', $myDom).removeClass('current'); + $('#qdot-' + questionNumber, $myDom).addClass('current'); + } + + // Remember where we are + currentQuestion = questionNumber; + return currentQuestion; + }; + + // Function for attaching the multichoice to a DOM element. + var attach = function (targetId) { + // Render own DOM into target. + template.update(targetId, params); + $myDom = $('#' + targetId); + $myDom.css({backgroundImage: 'url(' + params.backgroundImage + ')'}); + + // Attach questions + for (var i=0; i= params.passPercentage); + var eventData = { + score: scoreString, + passed: success + }; + // Display result page. + if (params.endGame.showResultPage) { + // Render result page into. + var eparams = { + greeting: (success ? params.endGame.resultPage.succesGreeting : params.endGame.resultPage.failGreeting), + score: scoreString, + scoreclass: (success ? 'success' : 'fail'), + resulttext: (success ? params.endGame.resultPage.successComment : params.endGame.resultPage.failComment), + finishButtonText: params.endGame.resultPage.finishButtonText + }; + endTemplate.update(targetId, eparams); + $('.qs-finishbutton').click(function (ev) { + // Display animation if present. + if (params.endGame.animations.showAnimations) { + // Init anims. + console.log("Now we should have started some anims..."); + // On animation finished: + $(returnObject).trigger('h5pQuestionSetFinished', eventData); + } else { + // Trigger finished event. + $(returnObject).trigger('h5pQuestionSetFinished', eventData); + } + }); + } else { + $(returnObject).trigger('h5pQuestionSetFinished', eventData); + } + }); + + // Hide all but initial Question. + _showQuestion(params.initialQuestion); + _updateFinishButton(); + return this; + }; + + // Get current score for questionset. + var getScore = function () { + var score = 0; + for (var i = questionInstances.length - 1; i >= 0; i--) { + score += questionInstances[i].getScore(); + } + return score; + }; + + // Get total score possible for questionset. + var totalScore = function () { + var score = 0; + for (var i = questionInstances.length - 1; i >= 0; i--) { + score += questionInstances[i].totalScore(); + } + return score; + }; + + // Masquerade the main object to hide inner properties and functions. + var returnObject = { + attach: attach, // Attach to DOM object + getQuestions: function () {return questionInstances;}, + getScore: getScore, + totalScore: totalScore, + defaults: defaults // Provide defaults for inspection + }; + return returnObject; +}; diff --git a/library.json b/library.json new file mode 100644 index 0000000..e69de29 diff --git a/schema.json b/schema.json new file mode 100644 index 0000000..790af6a --- /dev/null +++ b/schema.json @@ -0,0 +1,168 @@ +{ + "title": { + "label": "Title", + "description": "Question set title (optional)", + "type": "text", + "default": "" + }, + "randomOrder": { + "label": "Randomize order", + "description": "Whether questions should be shown in random order", + "type": "boolean", + "default": false + }, + "initialQuestion": { + "label": "Initial question", + "description": "Which question to start with. Count from 0", + "type": "integer", + "default": 0 + }, + "backgroundImage": { + "label": "Background image", + "description": "An optional background image for the Question set.", + "type": "image", + "default": "" + }, + "progressType": { + "label": "Progress indicator", + "description": "Question set progress indicator style", + "type": "select", + "values": [{"text": "Textual", "value": "textual"}, {"text": "Dots", "value": "dots"}], + "default": "textual" + }, + "passPercentage": { + "label": "Pass percentage", + "description": "Percentage of total score required for passing the quiz.", + "type": "integer", + "minValue": 0, + "maxValue": 100, + "default": 50 + }, + "questions": { + "label": "Questions", + "description": "List of questions in this set.", + "type": "h5p-library", + "validLibs": ["H5P.MultiChoice", "H5P.DragTextMatch", "H5PDragImageIntoPosition"], + "array": true, + "minEntries": 1, + "maxEntries": -1 + }, + "introPage": { + "label": "Intro page", + "description": "Data for the intro page", + "type": "combined", + "fields": { + "showIntroPage": { + "label": "Show intro page?", + "type": "boolean" + }, + "title": { + "label": "Title", + "type": "text" + }, + "introduction": { + "label": "Introduction text", + "type": "text" + }, + "startButtonText": { + "label": "Start button text", + "type": "text" + } + } + }, + "texts": { + "label": "Interface texts in quiz", + "type": "combined" + "fields": { + "prevButton": { + "label": "Back button", + "type": "text", + "default": "Previous" + }, + "nextButton": { + "label": "Next button", + "type": "text", + "default": "Next" + }, + "finishButton": { + "label": "Finish button", + "type": "text", + "default": "Finish" + }, + "textualProgress": { + "label": "Progress text", + "description": "Text used if textual progress is selected.", + "type": "text", + "default": "Question: @current of @total questions" + } + } + }, + "endGame": { + "label": "End game data", + "type": "combined", + "fields" { + "showResultPage": { + "label": "Show result page", + "type": "boolean", + "default": true + }, + "resultPage": { + "label": "Result page", + "description": "Data and texts for the result page", + "type": "combined", + "fields": { + "successGreeting": { + "label": "Success greeting", + "description": "Title in result page on success", + "type": "text" + }, + "successComment": { + "label": "Success comment", + "description": "Comment shown after the score", + "type": "text" + }, + "failGreeting": { + "label": "Failed greeting", + "description": "Title in result page on failed quiz", + "type": "text" + }, + "failComment": { + "label": "Failed comment", + "description": "Comment shown after the score on failed quiz", + "type": "text" + }, + "finishButtonText": { + "label": "Finish button text", + "description": "Text for the finish button", + "type": "text" + } + } + }, + "animations": { + "label": "Animations", + "type": "combined" + "fields": { + "showAnimations": { + "label": "Show animations", + "type": "boolean", + "default": false + } + "successResultAnimation": { + "label": "Result animation for success", + "description": "Add animation for successful completion of the quiz", + "optional": true, + "type": "h5p-library", + "validLibs": ["H5P.Animation", "H5P.Video", "H5P.Image"] + }, + "failedResultAnimation": { + "label": "Result animation for failed quiz", + "description": "Add animation for failed completion of the quiz", + "optional": true, + "type": "h5p-library", + "validLibs": ["H5P.Animation", "H5P.Video", "H5P.Image"] + } + } + } + } + } +} diff --git a/views/questionset-intro.ejs b/views/questionset-intro.ejs new file mode 100644 index 0000000..cc18bb6 --- /dev/null +++ b/views/questionset-intro.ejs @@ -0,0 +1,5 @@ +
+
<%= title %>
+
<%= introduction %>
+
+
\ No newline at end of file diff --git a/views/questionset-results.ejs b/views/questionset-results.ejs new file mode 100644 index 0000000..a107cb5 --- /dev/null +++ b/views/questionset-results.ejs @@ -0,0 +1,6 @@ +
+
<%= greeting %>
+
<%= score %>
+
<%= resulttext %>
+
+
diff --git a/views/questionset.ejs b/views/questionset.ejs new file mode 100644 index 0000000..574132b --- /dev/null +++ b/views/questionset.ejs @@ -0,0 +1,60 @@ + +
+ <% if (introPage.showIntroPage) { %> +
+
<%= introPage.title %>
+
<%= introPage.introduction %>
+
+
+ <%} %> +
<%= title %>
+ <% for (var i=0; i +
+
<%= questions[i].machineName %>
+
+ <% } %> + +
From f9e8360a7f594f5da0b448bd8f8042b77f5d176a Mon Sep 17 00:00:00 2001 From: Frank Ronny Larsen Date: Wed, 16 Jan 2013 19:33:59 +0100 Subject: [PATCH 02/83] Changes to example content. Example content is now a working board game sans Images. It can be uploaded using the H5P module too. It will work. (At least with the next commit) --- content.json | 0 css/questionset.css | 27 +++++++++++++++++++++++++++ js/questionset.js | 42 ++++++++++-------------------------------- library.json | 16 ++++++++++++++++ views/questionset.ejs | 29 ----------------------------- 5 files changed, 53 insertions(+), 61 deletions(-) delete mode 100644 content.json create mode 100644 css/questionset.css diff --git a/content.json b/content.json deleted file mode 100644 index e69de29..0000000 diff --git a/css/questionset.css b/css/questionset.css new file mode 100644 index 0000000..fdc21b3 --- /dev/null +++ b/css/questionset.css @@ -0,0 +1,27 @@ +.dots-container { + text-align: center; +} +.progress-dot { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; +} +.progress-dot.unanswered { + background: darkred; +} +.progress-dot.answered { + background: darkgreen; +} +.progress-dot.current { + box-shadow: 0px 0px 6px firebrick; +} +.intro-page { + background: white; + position: absolute; + z-index: 20; + height: 100%; + width: 100%; + left: 0px; + top: 0px; +} diff --git a/js/questionset.js b/js/questionset.js index ca85d98..a3dbbff 100644 --- a/js/questionset.js +++ b/js/questionset.js @@ -19,35 +19,6 @@ H5P.QuestionSet = function (options) { var $ = H5P.jQuery; var texttemplate = '' + -'' + '
' + ' <% if (introPage.showIntroPage) { %>' + '
' + @@ -197,10 +168,17 @@ H5P.QuestionSet = function (options) { // Function for attaching the multichoice to a DOM element. var attach = function (targetId) { + if (typeof(target) == "string") { + target = $("#" + target); + } else { + target = $(target); + } + // Render own DOM into target. - template.update(targetId, params); - $myDom = $('#' + targetId); - $myDom.css({backgroundImage: 'url(' + params.backgroundImage + ')'}); + $myDom = target; + $myDom.html(template.render(params)).css({ + backgroundImage: 'url(' + params.backgroundImage + ')' + }); // Attach questions for (var i=0; i -.dots-container { - text-align: center; -} -.progress-dot { - display: inline-block; - width: 10px; - height: 10px; - border-radius: 50%; -} -.progress-dot.unanswered { - background: darkred; -} -.progress-dot.answered { - background: darkgreen; -} -.progress-dot.current { - box-shadow: 0px 0px 6px firebrick; -} -.intro-page { - background: white; - position: absolute; - z-index: 20; - height: 100%; - width: 100%; - left: 0px; - top: 0px; -} -
<% if (introPage.showIntroPage) { %>
From b98ef0fc7a15a2fd2d55fa2f809f9378f38aa802 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Wed, 16 Jan 2013 22:01:39 +0100 Subject: [PATCH 03/83] Fix bug in questionsets.js --- js/questionset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/questionset.js b/js/questionset.js index a3dbbff..0f880e4 100644 --- a/js/questionset.js +++ b/js/questionset.js @@ -167,7 +167,7 @@ H5P.QuestionSet = function (options) { }; // Function for attaching the multichoice to a DOM element. - var attach = function (targetId) { + var attach = function (target) { if (typeof(target) == "string") { target = $("#" + target); } else { From 0dd48ee8429a3144cdfe0b3169a6ef95a5d987b7 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Thu, 17 Jan 2013 22:57:10 +0100 Subject: [PATCH 04/83] Stop using dropbox for images --- js/questionset.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/js/questionset.js b/js/questionset.js index 0f880e4..5cd512a 100644 --- a/js/questionset.js +++ b/js/questionset.js @@ -12,11 +12,12 @@ // - h5pQuestionSetFinished: Triggered when a question is finished. (User presses Finish-button) window.H5P = window.H5P || {}; -H5P.QuestionSet = function (options) { +H5P.QuestionSet = function (options, contentId) { if ( !(this instanceof H5P.QuestionSet) ) - return new H5P.QuestionSet(options); + return new H5P.QuestionSet(options, contentId); var $ = H5P.jQuery; + var cp = H5P.getContentPath(contentId); var texttemplate = '' + '
' + @@ -124,7 +125,7 @@ H5P.QuestionSet = function (options) { for (var i=0; i Date: Thu, 17 Jan 2013 23:21:10 +0100 Subject: [PATCH 05/83] Store whether or not a library is runnable --- library.json | 1 + 1 file changed, 1 insertion(+) diff --git a/library.json b/library.json index f957624..5e5df50 100644 --- a/library.json +++ b/library.json @@ -4,6 +4,7 @@ "majorVersion": 1, "minorVersion": 0, "patchVersion": 0, + "runnable": 1, "machineName": "H5P.QuestionSet", "author": "Amendor AS", "license": "cc-by-sa", From 6c8e2c57afaaf362eec7d4fba97096213614f97d Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Sun, 20 Jan 2013 22:25:34 +0100 Subject: [PATCH 06/83] Fix target bug in questionset --- js/questionset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/questionset.js b/js/questionset.js index 5cd512a..c9ebdc9 100644 --- a/js/questionset.js +++ b/js/questionset.js @@ -230,7 +230,7 @@ H5P.QuestionSet = function (options, contentId) { resulttext: (success ? params.endGame.resultPage.successComment : params.endGame.resultPage.failComment), finishButtonText: params.endGame.resultPage.finishButtonText }; - endTemplate.update(targetId, eparams); + endTemplate.update(target.attr('id'), eparams); $('.qs-finishbutton').click(function (ev) { // Display animation if present. if (params.endGame.animations.showAnimations) { From e8ef20a99517d866b4d6ccc271e60d613d0ec625 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Sun, 20 Jan 2013 22:57:53 +0100 Subject: [PATCH 07/83] Add temporary styling --- css/questionset.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/css/questionset.css b/css/questionset.css index fdc21b3..26c5a3a 100644 --- a/css/questionset.css +++ b/css/questionset.css @@ -17,7 +17,8 @@ box-shadow: 0px 0px 6px firebrick; } .intro-page { - background: white; + background-color: rgba(255,255,255,0.9); + padding: 10px; position: absolute; z-index: 20; height: 100%; @@ -25,3 +26,7 @@ left: 0px; top: 0px; } +.questionset-results { + background-color: rgba(255, 255, 255, 0.75); + padding: 10px; +} From e8849590815e789e610cbaa578f36e666ad87a10 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Tue, 22 Jan 2013 22:07:24 +0100 Subject: [PATCH 08/83] Remove questionset --- css/questionset.css | 32 ---- js/questionset.js | 284 ---------------------------------- library.json | 17 -- schema.json | 168 -------------------- views/questionset-intro.ejs | 5 - views/questionset-results.ejs | 6 - views/questionset.ejs | 31 ---- 7 files changed, 543 deletions(-) delete mode 100644 css/questionset.css delete mode 100644 js/questionset.js delete mode 100644 library.json delete mode 100644 schema.json delete mode 100644 views/questionset-intro.ejs delete mode 100644 views/questionset-results.ejs delete mode 100644 views/questionset.ejs diff --git a/css/questionset.css b/css/questionset.css deleted file mode 100644 index 26c5a3a..0000000 --- a/css/questionset.css +++ /dev/null @@ -1,32 +0,0 @@ -.dots-container { - text-align: center; -} -.progress-dot { - display: inline-block; - width: 10px; - height: 10px; - border-radius: 50%; -} -.progress-dot.unanswered { - background: darkred; -} -.progress-dot.answered { - background: darkgreen; -} -.progress-dot.current { - box-shadow: 0px 0px 6px firebrick; -} -.intro-page { - background-color: rgba(255,255,255,0.9); - padding: 10px; - position: absolute; - z-index: 20; - height: 100%; - width: 100%; - left: 0px; - top: 0px; -} -.questionset-results { - background-color: rgba(255, 255, 255, 0.75); - padding: 10px; -} diff --git a/js/questionset.js b/js/questionset.js deleted file mode 100644 index c9ebdc9..0000000 --- a/js/questionset.js +++ /dev/null @@ -1,284 +0,0 @@ -// Will render a Question with multiple choices for answers. - -// Options format: -// { -// title: "Optional title for question box", -// question: "Question text", -// answers: [{text: "Answer text", correct: false}, ...], -// singleAnswer: true, // or false, will change rendered output slightly. -// } -// -// Events provided: -// - h5pQuestionSetFinished: Triggered when a question is finished. (User presses Finish-button) -window.H5P = window.H5P || {}; - -H5P.QuestionSet = function (options, contentId) { - if ( !(this instanceof H5P.QuestionSet) ) - return new H5P.QuestionSet(options, contentId); - - var $ = H5P.jQuery; - var cp = H5P.getContentPath(contentId); - - var texttemplate = '' + -'
' + -' <% if (introPage.showIntroPage) { %>' + -'
' + -'
<%= introPage.title %>
' + -'
<%= introPage.introduction %>
' + -'
' + -'
' + -' <%} %>' + -'
<%= title %>
' + -' <% for (var i=0; i' + -'
' + -'
<%= questions[i].machineName %>
' + -'
' + -' <% } %>' + -' ' + -'
' + - ''; - - var resulttemplate = '' + -'
' + -'
<%= greeting %>
' + -'
<%= score %>
' + -'
<%= resulttext %>
' + -'
' + -'
' + -''; - - var that = this; - var defaults = { - title: "", - randomOrder: false, - initialQuestion: 0, - backgroundImage: "", - progressType: 'dots', - passPercentage: 50, - questions: [], - introPage: { - showIntroPage: true, - title: "Welcome", - introduction: "Click start to start.", - startButtonText: "Start" - }, - texts: { - prevButton: "Previous", - nextButton: "Next", - finishButton: "Finish", - textualProgress: "Question: @current of @total questions" - }, - endGame: { - showResultPage: true, - resultPage: { - succesGreeting: "Congratulations!", - successComment: "You have enough correct answers to pass the test.", - failGreeting: "Sorry!", - failComment: "You don't have enough correct answers to pass this test.", - scoreString: "@score/@total", - finishButtonText: "Finish" - }, - animations: { - showAnimations: false, - succesResultAnimation: { - machineName: "H5P.Image", - options: {image: ""} - }, - failedResultAnimation: { - machineName: "H5P.Image", - options: {image: ""} - } - } - } - }; - - var template = new EJS({text: texttemplate}); - var endTemplate = new EJS({text: resulttemplate}); - var params = $.extend({}, defaults, options); - - var currentQuestion = 0; - var questionInstances = new Array(); - var allQuestionsAnswered = false; - var $myDom; - - if (params.randomOrder) { - // TODO: Randomize order of questions - console.log("TODO: Randomize order of questions"); - } - - // Instantiate question instances - for (var i=0; i= 0; i--) { - answered = answered && (questionInstances[i]).getAnswerGiven(); - } - $('.finish.button', $myDom).attr({'disabled': !answered}); - }; - - var _showQuestion = function (questionNumber) { - // Sanitize input. - if (questionNumber < 0) { questionNumber = 0; } - if (questionNumber >= params.questions.length) { questionNumber = params.questions.length - 1; } - - $('.prev.button', $myDom).attr({'disabled': (questionNumber === 0)}); - $('.next.button', $myDom).attr({'disabled': (questionNumber == params.questions.length-1)}); - - - // Hide all questions - $('.question-container', $myDom).hide(); - - // Reshow the requested question - $('#q-' + questionNumber, $myDom).show(); - - // Update progress indicator - // Test if current has been answered. - if (params.progressType == 'textual') { - $('.progress-text', $myDom).text(params.texts.textualProgress.replace("@current", questionNumber+1).replace("@total", params.questions.length)); - } else { - // Set currentNess - $('.progress-dot.current', $myDom).removeClass('current'); - $('#qdot-' + questionNumber, $myDom).addClass('current'); - } - - // Remember where we are - currentQuestion = questionNumber; - return currentQuestion; - }; - - // Function for attaching the multichoice to a DOM element. - var attach = function (target) { - if (typeof(target) == "string") { - target = $("#" + target); - } else { - target = $(target); - } - - // Render own DOM into target. - $myDom = target; - $myDom.html(template.render(params)).css({ - backgroundImage: 'url(' + cp + params.backgroundImage + ')' - }); - - // Attach questions - for (var i=0; i= params.passPercentage); - var eventData = { - score: scoreString, - passed: success - }; - // Display result page. - if (params.endGame.showResultPage) { - // Render result page into. - var eparams = { - greeting: (success ? params.endGame.resultPage.succesGreeting : params.endGame.resultPage.failGreeting), - score: scoreString, - scoreclass: (success ? 'success' : 'fail'), - resulttext: (success ? params.endGame.resultPage.successComment : params.endGame.resultPage.failComment), - finishButtonText: params.endGame.resultPage.finishButtonText - }; - endTemplate.update(target.attr('id'), eparams); - $('.qs-finishbutton').click(function (ev) { - // Display animation if present. - if (params.endGame.animations.showAnimations) { - // Init anims. - console.log("Now we should have started some anims..."); - // On animation finished: - $(returnObject).trigger('h5pQuestionSetFinished', eventData); - } else { - // Trigger finished event. - $(returnObject).trigger('h5pQuestionSetFinished', eventData); - } - }); - } else { - $(returnObject).trigger('h5pQuestionSetFinished', eventData); - } - }); - - // Hide all but initial Question. - _showQuestion(params.initialQuestion); - _updateFinishButton(); - return this; - }; - - // Get current score for questionset. - var getScore = function () { - var score = 0; - for (var i = questionInstances.length - 1; i >= 0; i--) { - score += questionInstances[i].getScore(); - } - return score; - }; - - // Get total score possible for questionset. - var totalScore = function () { - var score = 0; - for (var i = questionInstances.length - 1; i >= 0; i--) { - score += questionInstances[i].totalScore(); - } - return score; - }; - - // Masquerade the main object to hide inner properties and functions. - var returnObject = { - attach: attach, // Attach to DOM object - getQuestions: function () {return questionInstances;}, - getScore: getScore, - totalScore: totalScore, - defaults: defaults // Provide defaults for inspection - }; - return returnObject; -}; diff --git a/library.json b/library.json deleted file mode 100644 index 5e5df50..0000000 --- a/library.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "title": "Question set", - "contentType": "question", - "majorVersion": 1, - "minorVersion": 0, - "patchVersion": 0, - "runnable": 1, - "machineName": "H5P.QuestionSet", - "author": "Amendor AS", - "license": "cc-by-sa", - "preloadedJs": [ - {"path": "js/questionset.js"} - ], - "preloadedCss": [ - {"path": "css/questionset.css"} - ] -} diff --git a/schema.json b/schema.json deleted file mode 100644 index 790af6a..0000000 --- a/schema.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "title": { - "label": "Title", - "description": "Question set title (optional)", - "type": "text", - "default": "" - }, - "randomOrder": { - "label": "Randomize order", - "description": "Whether questions should be shown in random order", - "type": "boolean", - "default": false - }, - "initialQuestion": { - "label": "Initial question", - "description": "Which question to start with. Count from 0", - "type": "integer", - "default": 0 - }, - "backgroundImage": { - "label": "Background image", - "description": "An optional background image for the Question set.", - "type": "image", - "default": "" - }, - "progressType": { - "label": "Progress indicator", - "description": "Question set progress indicator style", - "type": "select", - "values": [{"text": "Textual", "value": "textual"}, {"text": "Dots", "value": "dots"}], - "default": "textual" - }, - "passPercentage": { - "label": "Pass percentage", - "description": "Percentage of total score required for passing the quiz.", - "type": "integer", - "minValue": 0, - "maxValue": 100, - "default": 50 - }, - "questions": { - "label": "Questions", - "description": "List of questions in this set.", - "type": "h5p-library", - "validLibs": ["H5P.MultiChoice", "H5P.DragTextMatch", "H5PDragImageIntoPosition"], - "array": true, - "minEntries": 1, - "maxEntries": -1 - }, - "introPage": { - "label": "Intro page", - "description": "Data for the intro page", - "type": "combined", - "fields": { - "showIntroPage": { - "label": "Show intro page?", - "type": "boolean" - }, - "title": { - "label": "Title", - "type": "text" - }, - "introduction": { - "label": "Introduction text", - "type": "text" - }, - "startButtonText": { - "label": "Start button text", - "type": "text" - } - } - }, - "texts": { - "label": "Interface texts in quiz", - "type": "combined" - "fields": { - "prevButton": { - "label": "Back button", - "type": "text", - "default": "Previous" - }, - "nextButton": { - "label": "Next button", - "type": "text", - "default": "Next" - }, - "finishButton": { - "label": "Finish button", - "type": "text", - "default": "Finish" - }, - "textualProgress": { - "label": "Progress text", - "description": "Text used if textual progress is selected.", - "type": "text", - "default": "Question: @current of @total questions" - } - } - }, - "endGame": { - "label": "End game data", - "type": "combined", - "fields" { - "showResultPage": { - "label": "Show result page", - "type": "boolean", - "default": true - }, - "resultPage": { - "label": "Result page", - "description": "Data and texts for the result page", - "type": "combined", - "fields": { - "successGreeting": { - "label": "Success greeting", - "description": "Title in result page on success", - "type": "text" - }, - "successComment": { - "label": "Success comment", - "description": "Comment shown after the score", - "type": "text" - }, - "failGreeting": { - "label": "Failed greeting", - "description": "Title in result page on failed quiz", - "type": "text" - }, - "failComment": { - "label": "Failed comment", - "description": "Comment shown after the score on failed quiz", - "type": "text" - }, - "finishButtonText": { - "label": "Finish button text", - "description": "Text for the finish button", - "type": "text" - } - } - }, - "animations": { - "label": "Animations", - "type": "combined" - "fields": { - "showAnimations": { - "label": "Show animations", - "type": "boolean", - "default": false - } - "successResultAnimation": { - "label": "Result animation for success", - "description": "Add animation for successful completion of the quiz", - "optional": true, - "type": "h5p-library", - "validLibs": ["H5P.Animation", "H5P.Video", "H5P.Image"] - }, - "failedResultAnimation": { - "label": "Result animation for failed quiz", - "description": "Add animation for failed completion of the quiz", - "optional": true, - "type": "h5p-library", - "validLibs": ["H5P.Animation", "H5P.Video", "H5P.Image"] - } - } - } - } - } -} diff --git a/views/questionset-intro.ejs b/views/questionset-intro.ejs deleted file mode 100644 index cc18bb6..0000000 --- a/views/questionset-intro.ejs +++ /dev/null @@ -1,5 +0,0 @@ -
-
<%= title %>
-
<%= introduction %>
-
-
\ No newline at end of file diff --git a/views/questionset-results.ejs b/views/questionset-results.ejs deleted file mode 100644 index a107cb5..0000000 --- a/views/questionset-results.ejs +++ /dev/null @@ -1,6 +0,0 @@ -
-
<%= greeting %>
-
<%= score %>
-
<%= resulttext %>
-
-
diff --git a/views/questionset.ejs b/views/questionset.ejs deleted file mode 100644 index f097cd3..0000000 --- a/views/questionset.ejs +++ /dev/null @@ -1,31 +0,0 @@ -
- <% if (introPage.showIntroPage) { %> -
-
<%= introPage.title %>
-
<%= introPage.introduction %>
-
-
- <%} %> -
<%= title %>
- <% for (var i=0; i -
-
<%= questions[i].machineName %>
-
- <% } %> - -
From 5f8e1a9f54accf7539510ceea81e928155b741f8 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Thu, 4 Apr 2013 15:18:08 +0200 Subject: [PATCH 09/83] Adding back the modules that used to be submodules --- css/img/mark-fail.png | Bin 0 -> 3520 bytes css/img/mark-pass.png | Bin 0 -> 3220 bytes css/questionset.css | 111 +++++++++++++ js/questionset.js | 303 ++++++++++++++++++++++++++++++++++ library.json | 24 +++ semantics.json | 221 +++++++++++++++++++++++++ views/questionset-results.ejs | 6 + views/questionset.ejs | 31 ++++ 8 files changed, 696 insertions(+) create mode 100644 css/img/mark-fail.png create mode 100644 css/img/mark-pass.png create mode 100644 css/questionset.css create mode 100644 js/questionset.js create mode 100644 library.json create mode 100644 semantics.json create mode 100644 views/questionset-results.ejs create mode 100644 views/questionset.ejs diff --git a/css/img/mark-fail.png b/css/img/mark-fail.png new file mode 100644 index 0000000000000000000000000000000000000000..cbd3936b1928c0ed73326d13e5253ed980926409 GIT binary patch literal 3520 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' + +'
' + +'
<%= introPage.title %>
' + +'
<%= introPage.introduction %>
' + +' ' + +'
' + +'<%} %>' + +'' + + ''; + var resulttemplate = '' + +'
' + +'
<%= greeting %>
' + +'
<%= score %>
' + +'
<%= resulttext %>
' + +' ' + +'
' + + ''; + + var that = this; + var defaults = { + title: "", + randomOrder: false, + initialQuestion: 0, + backgroundImage: "", + progressType: 'dots', + passPercentage: 50, + questions: [], + introPage: { + showIntroPage: true, + title: "Welcome", + introduction: "Click start to start.", + startButtonText: "Start" + }, + texts: { + prevButton: "Previous", + nextButton: "Next", + finishButton: "Finish", + textualProgress: "Question: @current of @total questions" + }, + endGame: { + showResultPage: true, + resultPage: { + successGreeting: "Congratulations!", + successComment: "You have enough correct answers to pass the test.", + failGreeting: "Sorry!", + failComment: "You don't have enough correct answers to pass this test.", + scoreString: "@score/@total", + finishButtonText: "Finish" + }, + animations: { + showAnimations: false, + successVideo: undefined, + failVideo: undefined + } + } + }; + + var template = new EJS({text: texttemplate}); + var endTemplate = new EJS({text: resulttemplate}); + var params = $.extend({}, defaults, options); + + var currentQuestion = 0; + var questionInstances = new Array(); + var allQuestionsAnswered = false; + var $myDom; + + if (params.randomOrder) { + // TODO: Randomize order of questions + console.log("TODO: Randomize order of questions"); + } + + // Instantiate question instances + for (var i=0; i= 0; i--) { + answered = answered && (questionInstances[i]).getAnswerGiven(); + } + + if (currentQuestion === 0) { + $('.prev.button', $myDom).hide(); + } else { + $('.prev.button', $myDom).show(); + } + if (currentQuestion == (params.questions.length - 1)) { + $('.next.button', $myDom).hide(); + if (answered) { + $('.finish.button', $myDom).show(); + } + } else { + $('.next.button', $myDom).show(); + $('.finish.button', $myDom).hide(); + } + + }; + + var _showQuestion = function (questionNumber) { + // Sanitize input. + if (questionNumber < 0) { questionNumber = 0; } + if (questionNumber >= params.questions.length) { questionNumber = params.questions.length - 1; } + + // Hide all questions + $('.question-container', $myDom).hide(); + + // Reshow the requested question + $('#q-' + questionNumber, $myDom).show(); + + // Update progress indicator + // Test if current has been answered. + if (params.progressType == 'textual') { + $('.progress-text', $myDom).text(params.texts.textualProgress.replace("@current", questionNumber+1).replace("@total", params.questions.length)); + } else { + // Set currentNess + $('.progress-dot.current', $myDom).removeClass('current'); + $('#qdot-' + questionNumber, $myDom).addClass('current'); + } + + // Remember where we are + currentQuestion = questionNumber; + _updateButtons(); + return currentQuestion; + }; + + var _displayEndGame = function () { + // Get total score. + var finals = getScore(); + var totals = totalScore(); + var scoreString = params.endGame.resultPage.scoreString.replace("@score", finals).replace("@total", totals); + var success = ((100 * finals / totals) >= params.passPercentage); + var eventData = { + score: scoreString, + passed: success + }; + var displayResults = function () { + if (!params.endGame.showResultPage) { + $(returnObject).trigger('h5pQuestionSetFinished', eventData); + return; + } + + var eparams = { + greeting: (success ? params.endGame.resultPage.succesGreeting : params.endGame.resultPage.failGreeting), + score: scoreString, + scoreclass: (success ? 'success' : 'fail'), + resulttext: (success ? params.endGame.resultPage.successComment : params.endGame.resultPage.failComment), + finishButtonText: params.endGame.resultPage.finishButtonText + }; + + // Show result page. + $myDom.children().hide(); + $myDom.append(endTemplate.render(eparams)); + $('.qs-finishbutton').click(function (ev) { + $(returnObject).trigger('h5pQuestionSetFinished', eventData); + }); + }; + + if (params.endGame.animations.showAnimations) { + var videoData = success ? params.endGame.animations.successVideo : params.endGame.animations.failVideo; + if (videoData) { + H5P.playVideo($myDom, videoData, params.endGame.animations.skipButtonText, cp, function () { + displayResults(); + }); + return; + } + } + // Trigger finished event. + displayResults(); + }; + + // Function for attaching the multichoice to a DOM element. + var attach = function (target) { + if (typeof(target) == "string") { + $myDom = $("#" + target); + } else { + $myDom = $(target); + } + + // Render own DOM into target. + $myDom.html(template.render(params)).css({ + backgroundImage: 'url(' + cp + params.backgroundImage.path + ')' + }); + + // Attach questions + for (var i=0; i= 0; i--) { + score += questionInstances[i].getScore(); + } + return score; + }; + + // Get total score possible for questionset. + var totalScore = function () { + var score = 0; + for (var i = questionInstances.length - 1; i >= 0; i--) { + score += questionInstances[i].totalScore(); + } + return score; + }; + + // Masquerade the main object to hide inner properties and functions. + var returnObject = { + attach: attach, // Attach to DOM object + getQuestions: function () {return questionInstances;}, + getScore: getScore, + totalScore: totalScore, + defaults: defaults // Provide defaults for inspection + }; + return returnObject; +}; diff --git a/library.json b/library.json new file mode 100644 index 0000000..cd31dac --- /dev/null +++ b/library.json @@ -0,0 +1,24 @@ +{ + "title": "Question set", + "contentType": "question", + "majorVersion": 1, + "minorVersion": 0, + "patchVersion": 6, + "runnable": 1, + "machineName": "H5P.QuestionSet", + "author": "Amendor AS", + "license": "cc-by-sa", + "preloadedJs": [ + {"path": "js/questionset.js"} + ], + "preloadedCss": [ + {"path": "css/questionset.css"} + ], + "preloadedDependencies": [ + { + "machineName": "EmbeddedJS", + "majorVersion": 1, + "minorVersion": 0 + } + ] +} diff --git a/semantics.json b/semantics.json new file mode 100644 index 0000000..d1ef7e8 --- /dev/null +++ b/semantics.json @@ -0,0 +1,221 @@ +[ + { + "name": "title", + "type": "text", + "label": "Title", + "description": "Question set title (optional)." + }, + { + "name": "randomOrder", + "type": "boolean", + "label": "Randomize order", + "description": "Whether questions should be shown in random order." + }, + { + "name": "initialQuestion", + "type": "number", + "label": "Initial question", + "description": "Which question to start with. Count from 0." + }, + { + "name": "backgroundImage", + "type": "image", + "label": "Background image", + "description": "An optional background image for the Question set." + }, + { + "name": "progressType", + "type": "select", + "label": "Progress indicator", + "description": "Question set progress indicator style.", + "values": [ + { + "text": "Textual", + "value": "textual" + }, + { + "text": "Dots", + "value": "dots" + } + ], + "default": "textual" + }, + { + "name": "passPercentage", + "type": "number", + "label": "Pass percentage", + "description": "Percentage of total score required for passing the quiz.", + "min": 0, + "max": 100, + "step": 1, + "default": 50 + }, + { + "name": "questions", + "type": "list", + "entity": "question", + "field": { + "name": "question", + "type": "group", + "label": "Question", + "fields": [ + { + "name": "library", + "type": "library", + "label": "Question library", + "description": "Library for this question.", + "options": [ + "H5P.MultiChoice 1.0" + ] + } + ] + } + }, + { + "name": "introPage", + "type": "group", + "label": "Intro page", + "description": "Data for the intro page.", + "fields": [ + { + "name": "showIntroPage", + "type": "boolean", + "label": "Show intro page?" + }, + { + "name": "title", + "type": "text", + "label": "Title" + }, + { + "name": "introduction", + "type": "text", + "label": "Introduction text" + }, + { + "name": "startButtonText", + "type": "text", + "label": "Start button text" + } + ] + }, + { + "name": "texts", + "type": "group", + "label": "Interface texts in quiz", + "common": true, + "fields": [ + { + "name": "prevButton", + "type": "text", + "label": "Back button", + "default": "Previous" + }, + { + "name": "nextButton", + "type": "text", + "label": "Next button", + "default": "Next" + }, + { + "name": "finishButton", + "type": "text", + "label": "Finish button", + "default": "Finish" + }, + { + "name": "textualProgress", + "type": "text", + "label": "Progress text", + "description": "Text used if textual progress is selected.", + "default": "Question: @current of @total questions" + } + ] + }, + { + "name": "endGame", + "type": "group", + "label": "End game data", + "fields": [ + { + "name": "showResultPage", + "type": "boolean", + "label": "Show result page", + "default": true + }, + { + "name": "resultPage", + "type": "group", + "label": "Result page", + "description": "Data and texts for the result page.", + "fields": [ + { + "name": "successGreeting", + "type": "text", + "label": "Success greeting", + "description": "Title in result page on success." + }, + { + "name": "successComment", + "type": "text", + "label": "Success comment", + "description": "Comment shown after the score." + }, + { + "name": "failGreeting", + "type": "text", + "label": "Failed greeting", + "description": "Title in result page on failed quiz." + }, + { + "name": "failComment", + "type": "text", + "label": "Failed comment", + "description": "Comment shown after the score on failed quiz." + }, + { + "name": "finishButtonText", + "type": "text", + "label": "Finish button text", + "description": "Text for the finish button." + } + ] + }, + { + "name": "animations", + "type": "group", + "label": "Animations", + "fields": [ + { + "name": "showAnimations", + "type": "boolean", + "label": "Show animations" + }, + { + "name": "skippable", + "type": "boolean", + "label": "Skippable" + }, + { + "name": "skipButtonText", + "type": "text", + "label": "Skip button text", + "common": true + }, + { + "name": "successVideo", + "type": "video", + "label": "Success video", + "description": "Video displayed on successful quiz." + }, + { + "name": "failVideo", + "type": "video", + "label": "Fail video", + "description": "Video displayed on failed quiz." + } + ] + } + ] + } +] diff --git a/views/questionset-results.ejs b/views/questionset-results.ejs new file mode 100644 index 0000000..722776d --- /dev/null +++ b/views/questionset-results.ejs @@ -0,0 +1,6 @@ +
+
<%= greeting %>
+
<%= score %>
+
<%= resulttext %>
+ +
diff --git a/views/questionset.ejs b/views/questionset.ejs new file mode 100644 index 0000000..5219d42 --- /dev/null +++ b/views/questionset.ejs @@ -0,0 +1,31 @@ +<% if (introPage.showIntroPage) { %> +
+
<%= introPage.title %>
+
<%= introPage.introduction %>
+ +
+<%} %> + From 785f787913e2da3652e66d3fcb41be3cd52b6f94 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 4 Apr 2013 15:44:06 +0200 Subject: [PATCH 10/83] Removed submodules --- css/img/mark-fail.png | Bin 0 -> 3520 bytes css/img/mark-pass.png | Bin 0 -> 3220 bytes css/questionset.css | 111 +++++++++++++ js/questionset.js | 303 ++++++++++++++++++++++++++++++++++ library.json | 24 +++ semantics.json | 221 +++++++++++++++++++++++++ views/questionset-results.ejs | 6 + views/questionset.ejs | 31 ++++ 8 files changed, 696 insertions(+) create mode 100644 css/img/mark-fail.png create mode 100644 css/img/mark-pass.png create mode 100644 css/questionset.css create mode 100644 js/questionset.js create mode 100644 library.json create mode 100644 semantics.json create mode 100644 views/questionset-results.ejs create mode 100644 views/questionset.ejs diff --git a/css/img/mark-fail.png b/css/img/mark-fail.png new file mode 100644 index 0000000000000000000000000000000000000000..cbd3936b1928c0ed73326d13e5253ed980926409 GIT binary patch literal 3520 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' + +'
' + +'
<%= introPage.title %>
' + +'
<%= introPage.introduction %>
' + +' ' + +'
' + +'<%} %>' + +'' + + ''; + var resulttemplate = '' + +'
' + +'
<%= greeting %>
' + +'
<%= score %>
' + +'
<%= resulttext %>
' + +' ' + +'
' + + ''; + + var that = this; + var defaults = { + title: "", + randomOrder: false, + initialQuestion: 0, + backgroundImage: "", + progressType: 'dots', + passPercentage: 50, + questions: [], + introPage: { + showIntroPage: true, + title: "Welcome", + introduction: "Click start to start.", + startButtonText: "Start" + }, + texts: { + prevButton: "Previous", + nextButton: "Next", + finishButton: "Finish", + textualProgress: "Question: @current of @total questions" + }, + endGame: { + showResultPage: true, + resultPage: { + successGreeting: "Congratulations!", + successComment: "You have enough correct answers to pass the test.", + failGreeting: "Sorry!", + failComment: "You don't have enough correct answers to pass this test.", + scoreString: "@score/@total", + finishButtonText: "Finish" + }, + animations: { + showAnimations: false, + successVideo: undefined, + failVideo: undefined + } + } + }; + + var template = new EJS({text: texttemplate}); + var endTemplate = new EJS({text: resulttemplate}); + var params = $.extend({}, defaults, options); + + var currentQuestion = 0; + var questionInstances = new Array(); + var allQuestionsAnswered = false; + var $myDom; + + if (params.randomOrder) { + // TODO: Randomize order of questions + console.log("TODO: Randomize order of questions"); + } + + // Instantiate question instances + for (var i=0; i= 0; i--) { + answered = answered && (questionInstances[i]).getAnswerGiven(); + } + + if (currentQuestion === 0) { + $('.prev.button', $myDom).hide(); + } else { + $('.prev.button', $myDom).show(); + } + if (currentQuestion == (params.questions.length - 1)) { + $('.next.button', $myDom).hide(); + if (answered) { + $('.finish.button', $myDom).show(); + } + } else { + $('.next.button', $myDom).show(); + $('.finish.button', $myDom).hide(); + } + + }; + + var _showQuestion = function (questionNumber) { + // Sanitize input. + if (questionNumber < 0) { questionNumber = 0; } + if (questionNumber >= params.questions.length) { questionNumber = params.questions.length - 1; } + + // Hide all questions + $('.question-container', $myDom).hide(); + + // Reshow the requested question + $('#q-' + questionNumber, $myDom).show(); + + // Update progress indicator + // Test if current has been answered. + if (params.progressType == 'textual') { + $('.progress-text', $myDom).text(params.texts.textualProgress.replace("@current", questionNumber+1).replace("@total", params.questions.length)); + } else { + // Set currentNess + $('.progress-dot.current', $myDom).removeClass('current'); + $('#qdot-' + questionNumber, $myDom).addClass('current'); + } + + // Remember where we are + currentQuestion = questionNumber; + _updateButtons(); + return currentQuestion; + }; + + var _displayEndGame = function () { + // Get total score. + var finals = getScore(); + var totals = totalScore(); + var scoreString = params.endGame.resultPage.scoreString.replace("@score", finals).replace("@total", totals); + var success = ((100 * finals / totals) >= params.passPercentage); + var eventData = { + score: scoreString, + passed: success + }; + var displayResults = function () { + if (!params.endGame.showResultPage) { + $(returnObject).trigger('h5pQuestionSetFinished', eventData); + return; + } + + var eparams = { + greeting: (success ? params.endGame.resultPage.succesGreeting : params.endGame.resultPage.failGreeting), + score: scoreString, + scoreclass: (success ? 'success' : 'fail'), + resulttext: (success ? params.endGame.resultPage.successComment : params.endGame.resultPage.failComment), + finishButtonText: params.endGame.resultPage.finishButtonText + }; + + // Show result page. + $myDom.children().hide(); + $myDom.append(endTemplate.render(eparams)); + $('.qs-finishbutton').click(function (ev) { + $(returnObject).trigger('h5pQuestionSetFinished', eventData); + }); + }; + + if (params.endGame.animations.showAnimations) { + var videoData = success ? params.endGame.animations.successVideo : params.endGame.animations.failVideo; + if (videoData) { + H5P.playVideo($myDom, videoData, params.endGame.animations.skipButtonText, cp, function () { + displayResults(); + }); + return; + } + } + // Trigger finished event. + displayResults(); + }; + + // Function for attaching the multichoice to a DOM element. + var attach = function (target) { + if (typeof(target) == "string") { + $myDom = $("#" + target); + } else { + $myDom = $(target); + } + + // Render own DOM into target. + $myDom.html(template.render(params)).css({ + backgroundImage: 'url(' + cp + params.backgroundImage.path + ')' + }); + + // Attach questions + for (var i=0; i= 0; i--) { + score += questionInstances[i].getScore(); + } + return score; + }; + + // Get total score possible for questionset. + var totalScore = function () { + var score = 0; + for (var i = questionInstances.length - 1; i >= 0; i--) { + score += questionInstances[i].totalScore(); + } + return score; + }; + + // Masquerade the main object to hide inner properties and functions. + var returnObject = { + attach: attach, // Attach to DOM object + getQuestions: function () {return questionInstances;}, + getScore: getScore, + totalScore: totalScore, + defaults: defaults // Provide defaults for inspection + }; + return returnObject; +}; diff --git a/library.json b/library.json new file mode 100644 index 0000000..cd31dac --- /dev/null +++ b/library.json @@ -0,0 +1,24 @@ +{ + "title": "Question set", + "contentType": "question", + "majorVersion": 1, + "minorVersion": 0, + "patchVersion": 6, + "runnable": 1, + "machineName": "H5P.QuestionSet", + "author": "Amendor AS", + "license": "cc-by-sa", + "preloadedJs": [ + {"path": "js/questionset.js"} + ], + "preloadedCss": [ + {"path": "css/questionset.css"} + ], + "preloadedDependencies": [ + { + "machineName": "EmbeddedJS", + "majorVersion": 1, + "minorVersion": 0 + } + ] +} diff --git a/semantics.json b/semantics.json new file mode 100644 index 0000000..d1ef7e8 --- /dev/null +++ b/semantics.json @@ -0,0 +1,221 @@ +[ + { + "name": "title", + "type": "text", + "label": "Title", + "description": "Question set title (optional)." + }, + { + "name": "randomOrder", + "type": "boolean", + "label": "Randomize order", + "description": "Whether questions should be shown in random order." + }, + { + "name": "initialQuestion", + "type": "number", + "label": "Initial question", + "description": "Which question to start with. Count from 0." + }, + { + "name": "backgroundImage", + "type": "image", + "label": "Background image", + "description": "An optional background image for the Question set." + }, + { + "name": "progressType", + "type": "select", + "label": "Progress indicator", + "description": "Question set progress indicator style.", + "values": [ + { + "text": "Textual", + "value": "textual" + }, + { + "text": "Dots", + "value": "dots" + } + ], + "default": "textual" + }, + { + "name": "passPercentage", + "type": "number", + "label": "Pass percentage", + "description": "Percentage of total score required for passing the quiz.", + "min": 0, + "max": 100, + "step": 1, + "default": 50 + }, + { + "name": "questions", + "type": "list", + "entity": "question", + "field": { + "name": "question", + "type": "group", + "label": "Question", + "fields": [ + { + "name": "library", + "type": "library", + "label": "Question library", + "description": "Library for this question.", + "options": [ + "H5P.MultiChoice 1.0" + ] + } + ] + } + }, + { + "name": "introPage", + "type": "group", + "label": "Intro page", + "description": "Data for the intro page.", + "fields": [ + { + "name": "showIntroPage", + "type": "boolean", + "label": "Show intro page?" + }, + { + "name": "title", + "type": "text", + "label": "Title" + }, + { + "name": "introduction", + "type": "text", + "label": "Introduction text" + }, + { + "name": "startButtonText", + "type": "text", + "label": "Start button text" + } + ] + }, + { + "name": "texts", + "type": "group", + "label": "Interface texts in quiz", + "common": true, + "fields": [ + { + "name": "prevButton", + "type": "text", + "label": "Back button", + "default": "Previous" + }, + { + "name": "nextButton", + "type": "text", + "label": "Next button", + "default": "Next" + }, + { + "name": "finishButton", + "type": "text", + "label": "Finish button", + "default": "Finish" + }, + { + "name": "textualProgress", + "type": "text", + "label": "Progress text", + "description": "Text used if textual progress is selected.", + "default": "Question: @current of @total questions" + } + ] + }, + { + "name": "endGame", + "type": "group", + "label": "End game data", + "fields": [ + { + "name": "showResultPage", + "type": "boolean", + "label": "Show result page", + "default": true + }, + { + "name": "resultPage", + "type": "group", + "label": "Result page", + "description": "Data and texts for the result page.", + "fields": [ + { + "name": "successGreeting", + "type": "text", + "label": "Success greeting", + "description": "Title in result page on success." + }, + { + "name": "successComment", + "type": "text", + "label": "Success comment", + "description": "Comment shown after the score." + }, + { + "name": "failGreeting", + "type": "text", + "label": "Failed greeting", + "description": "Title in result page on failed quiz." + }, + { + "name": "failComment", + "type": "text", + "label": "Failed comment", + "description": "Comment shown after the score on failed quiz." + }, + { + "name": "finishButtonText", + "type": "text", + "label": "Finish button text", + "description": "Text for the finish button." + } + ] + }, + { + "name": "animations", + "type": "group", + "label": "Animations", + "fields": [ + { + "name": "showAnimations", + "type": "boolean", + "label": "Show animations" + }, + { + "name": "skippable", + "type": "boolean", + "label": "Skippable" + }, + { + "name": "skipButtonText", + "type": "text", + "label": "Skip button text", + "common": true + }, + { + "name": "successVideo", + "type": "video", + "label": "Success video", + "description": "Video displayed on successful quiz." + }, + { + "name": "failVideo", + "type": "video", + "label": "Fail video", + "description": "Video displayed on failed quiz." + } + ] + } + ] + } +] diff --git a/views/questionset-results.ejs b/views/questionset-results.ejs new file mode 100644 index 0000000..722776d --- /dev/null +++ b/views/questionset-results.ejs @@ -0,0 +1,6 @@ +
+
<%= greeting %>
+
<%= score %>
+
<%= resulttext %>
+ +
diff --git a/views/questionset.ejs b/views/questionset.ejs new file mode 100644 index 0000000..5219d42 --- /dev/null +++ b/views/questionset.ejs @@ -0,0 +1,31 @@ +<% if (introPage.showIntroPage) { %> +
+
<%= introPage.title %>
+
<%= introPage.introduction %>
+ +
+<%} %> + From c4c526b1e410a559e81f4ff68fd62f0313f585ee Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 26 Apr 2013 17:27:35 +0200 Subject: [PATCH 11/83] Improved boardgame flexibility. --- css/questionset.css | 40 +++----- js/questionset.js | 221 +++++++++++++++++++++++++------------------- 2 files changed, 138 insertions(+), 123 deletions(-) diff --git a/css/questionset.css b/css/questionset.css index 0fe886e..49ebe12 100644 --- a/css/questionset.css +++ b/css/questionset.css @@ -18,51 +18,38 @@ margin: -1px 4px 1px 2px; } .intro-page { - /*background-color: rgba(255,255,255,0.9);*/ - /*padding: 10px;*/ - position: absolute; - z-index: 20; - height: 100%; - width: 100%; - left: 0px; - top: 0px; + } .intro-page .title { - font-size: 200%; + font-size: 2em; font-weight: bold; text-align: center; - margin: 1em; + padding: 1em 1em 0.5em; } .intro-page .introduction { - font-size: 125%; - margin: 1em 10%; + font-size: 1.25em; + margin: 0 1em; } .intro-page .buttons { - position: absolute; - top: 200px; - left: 182px; - text-align: right; + margin: 3em auto; + text-align: center; } #qs-startbutton { - border: 4px solid #BABABA; - border-radius: 10px 10px 10px 10px; - font-size: 50px; - padding: 10px 20px; + font-size: 3em; + border: 0.083em solid #BABABA; + border-radius: 0.2083em; + padding: 0.2083em 0.4167em; cursor: pointer; } .qs-progress { - line-height: 40px; + line-height: 2em; padding: 0; + margin: 1em 1em 0; } .questionset-results { - background-image: url(img/Paper-clean.png); - background-color: rgba(255, 255, 255, 0.75); - - padding: 10px; - height: 480px; text-align: center; } @@ -76,6 +63,7 @@ } .questionset-results .resulttext { + text-align: left; font-size: 20px; line-height: 25px; margin: 40px 100px; diff --git a/js/questionset.js b/js/questionset.js index 078d1da..2a7c822 100644 --- a/js/questionset.js +++ b/js/questionset.js @@ -1,96 +1,101 @@ -// Will render a Question with multiple choices for answers. - -// Options format: -// { -// title: "Optional title for question box", -// question: "Question text", -// answers: [{text: "Answer text", correct: false}, ...], -// singleAnswer: true, // or false, will change rendered output slightly. -// } -// -// Events provided: -// - h5pQuestionSetFinished: Triggered when a question is finished. (User presses Finish-button) var H5P = H5P || {}; +/** + * Will render a Question with multiple choices for answers. + * + * Options format: + * { + * title: "Optional title for question box", + * question: "Question text", + * answers: [{text: "Answer text", correct: false}, ...], + * singleAnswer: true, // or false, will change rendered output slightly. + * } + * + * Events provided: + * - h5pQuestionSetFinished: Triggered when a question is finished. (User presses Finish-button) + * + * @param {Array} options + * @param {int} contentId + * @returns {H5P.QuestionSet} Instance + */ H5P.QuestionSet = function (options, contentId) { - if ( !(this instanceof H5P.QuestionSet) ) + if (!(this instanceof H5P.QuestionSet)) { return new H5P.QuestionSet(options, contentId); + } var $ = H5P.jQuery; var cp = H5P.getContentPath(contentId); - var texttemplate = '' + -'<% if (introPage.showIntroPage) { %>' + -'
' + -'
<%= introPage.title %>
' + -'
<%= introPage.introduction %>
' + -' ' + -'
' + -'<%} %>' + -'' + - ''; - var resulttemplate = '' + -'
' + -'
<%= greeting %>
' + -'
<%= score %>
' + -'
<%= resulttext %>
' + -' ' + -'
' + - ''; + var texttemplate = + '<% if (introPage.showIntroPage) { %>' + + '
' + + '
<%= introPage.title %>
' + + '
<%= introPage.introduction %>
' + + ' ' + + '
' + + '<%} %>' + + ''; + + var resulttemplate = + '
' + + '
<%= greeting %>
' + + '
<%= score %>
' + + '
<%= resulttext %>
' + + ' ' + + '
'; - var that = this; var defaults = { - title: "", + title: '', randomOrder: false, initialQuestion: 0, - backgroundImage: "", + backgroundImage: '', progressType: 'dots', passPercentage: 50, questions: [], introPage: { showIntroPage: true, - title: "Welcome", - introduction: "Click start to start.", - startButtonText: "Start" + title: 'Welcome', + introduction: 'Click start to start.', + startButtonText: 'Start' }, texts: { - prevButton: "Previous", - nextButton: "Next", - finishButton: "Finish", - textualProgress: "Question: @current of @total questions" + prevButton: 'Previous', + nextButton: 'Next', + finishButton: 'Finish', + textualProgress: 'Question: @current of @total questions' }, endGame: { showResultPage: true, resultPage: { - successGreeting: "Congratulations!", - successComment: "You have enough correct answers to pass the test.", - failGreeting: "Sorry!", + successGreeting: 'Congratulations!', + successComment: 'You have enough correct answers to pass the test.', + failGreeting: 'Sorry!', failComment: "You don't have enough correct answers to pass this test.", - scoreString: "@score/@total", - finishButtonText: "Finish" + scoreString: '@score/@total', + finishButtonText: 'Finish' }, animations: { showAnimations: false, @@ -102,20 +107,18 @@ H5P.QuestionSet = function (options, contentId) { var template = new EJS({text: texttemplate}); var endTemplate = new EJS({text: resulttemplate}); - var params = $.extend({}, defaults, options); + var params = $.extend(true, {}, defaults, options); var currentQuestion = 0; var questionInstances = new Array(); - var allQuestionsAnswered = false; var $myDom; - if (params.randomOrder) { - // TODO: Randomize order of questions - console.log("TODO: Randomize order of questions"); - } +// if (params.randomOrder) { +// // TODO: Randomize order of questions +// } // Instantiate question instances - for (var i=0; i= params.questions.length) { questionNumber = params.questions.length - 1; } + if (questionNumber < 0) { + questionNumber = 0; + } + if (questionNumber >= params.questions.length) { + questionNumber = params.questions.length - 1; + } // Hide all questions $('.question-container', $myDom).hide(); @@ -161,9 +167,10 @@ H5P.QuestionSet = function (options, contentId) { // Update progress indicator // Test if current has been answered. - if (params.progressType == 'textual') { + if (params.progressType === 'textual') { $('.progress-text', $myDom).text(params.texts.textualProgress.replace("@current", questionNumber+1).replace("@total", params.questions.length)); - } else { + } + else { // Set currentNess $('.progress-dot.current', $myDom).removeClass('current'); $('#qdot-' + questionNumber, $myDom).addClass('current'); @@ -202,7 +209,7 @@ H5P.QuestionSet = function (options, contentId) { // Show result page. $myDom.children().hide(); $myDom.append(endTemplate.render(eparams)); - $('.qs-finishbutton').click(function (ev) { + $('.qs-finishbutton').click(function () { $(returnObject).trigger('h5pQuestionSetFinished', eventData); }); }; @@ -210,9 +217,28 @@ H5P.QuestionSet = function (options, contentId) { if (params.endGame.animations.showAnimations) { var videoData = success ? params.endGame.animations.successVideo : params.endGame.animations.failVideo; if (videoData) { - H5P.playVideo($myDom, videoData, params.endGame.animations.skipButtonText, cp, function () { + var $videoContainer = $('
').appendTo($myDom); + + var video = new H5P.Video({ + files: videoData, + fitToWrapper: true, + controls: false, + autoplay: true + }, cp); + video.endedCallback = function () { displayResults(); - }); + $videoContainer.hide(); + }; + video.attach($videoContainer); + + if (params.endGame.animations.skipButtonText) { + $('').click(function () { + video.stop(); + $videoContainer.hide(); + displayResults(); + }).appendTo($videoContainer); + } + return; } } @@ -222,9 +248,10 @@ H5P.QuestionSet = function (options, contentId) { // Function for attaching the multichoice to a DOM element. var attach = function (target) { - if (typeof(target) == "string") { - $myDom = $("#" + target); - } else { + if (typeof(target) === "string") { + $myDom = $('#' + target); + } + else { $myDom = $(target); } @@ -234,11 +261,11 @@ H5P.QuestionSet = function (options, contentId) { }); // Attach questions - for (var i=0; i= 0; i--) { - score += questionInstances[i].totalScore(); + score += questionInstances[i].getMaxScore(); } return score; }; @@ -300,4 +327,4 @@ H5P.QuestionSet = function (options, contentId) { defaults: defaults // Provide defaults for inspection }; return returnObject; -}; +}; \ No newline at end of file From 7e1be0c0f169f9f1cb048c0dc1c6a55455b6396b Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 30 Apr 2013 11:45:25 +0200 Subject: [PATCH 12/83] Added select box. --- semantics.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/semantics.json b/semantics.json index d1ef7e8..db0c03d 100644 --- a/semantics.json +++ b/semantics.json @@ -28,14 +28,14 @@ "type": "select", "label": "Progress indicator", "description": "Question set progress indicator style.", - "values": [ + "options": [ { - "text": "Textual", - "value": "textual" + "value": "textual", + "label": "Textual" }, { - "text": "Dots", - "value": "dots" + "value": "dots", + "label": "Dots" } ], "default": "textual" From 0118a7dfe9baad6b7000623def6ff41bca552ae2 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 30 Apr 2013 13:01:14 +0200 Subject: [PATCH 13/83] Fixed Boardgame video. Fixed QuestionSet dependency. --- library.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library.json b/library.json index cd31dac..0ff733b 100644 --- a/library.json +++ b/library.json @@ -17,7 +17,12 @@ "preloadedDependencies": [ { "machineName": "EmbeddedJS", - "majorVersion": 1, + "majorVersion": 1, + "minorVersion": 0 + }, + { + "machineName": "H5P.Video", + "majorVersion": 1, "minorVersion": 0 } ] From 9b3130b463bd6ecdfd7461ab54d32342f5a1544c Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 2 May 2013 14:41:39 +0200 Subject: [PATCH 14/83] Numerus bug fixes and improved editor for boardgame. Fixed CKEditor readonly when created in invisible field. --- css/questionset.css | 2 +- js/questionset.js | 53 +++++++------- semantics.json | 166 +++++++++++++++++++++++--------------------- 3 files changed, 114 insertions(+), 107 deletions(-) diff --git a/css/questionset.css b/css/questionset.css index 49ebe12..152b284 100644 --- a/css/questionset.css +++ b/css/questionset.css @@ -54,7 +54,7 @@ } .questionset-results .greeting { - margin-top: 180px; + margin: 1em; } .questionset-results .score { diff --git a/js/questionset.js b/js/questionset.js index 2a7c822..27edb9c 100644 --- a/js/questionset.js +++ b/js/questionset.js @@ -71,14 +71,14 @@ H5P.QuestionSet = function (options, contentId) { title: '', randomOrder: false, initialQuestion: 0, - backgroundImage: '', + backgroundImage: undefined, progressType: 'dots', passPercentage: 50, questions: [], introPage: { showIntroPage: true, - title: 'Welcome', - introduction: 'Click start to start.', + title: '', + introduction: '', startButtonText: 'Start' }, texts: { @@ -89,19 +89,15 @@ H5P.QuestionSet = function (options, contentId) { }, endGame: { showResultPage: true, - resultPage: { - successGreeting: 'Congratulations!', - successComment: 'You have enough correct answers to pass the test.', - failGreeting: 'Sorry!', - failComment: "You don't have enough correct answers to pass this test.", - scoreString: '@score/@total', - finishButtonText: 'Finish' - }, - animations: { - showAnimations: false, - successVideo: undefined, - failVideo: undefined - } + successGreeting: 'Congratulations!', + successComment: 'You have enough correct answers to pass the test.', + failGreeting: 'Sorry!', + failComment: "You don't have enough correct answers to pass this test.", + scoreString: '@score/@total', + finishButtonText: 'Finish', + showAnimations: false, + successVideo: undefined, + failVideo: undefined } }; @@ -186,7 +182,7 @@ H5P.QuestionSet = function (options, contentId) { // Get total score. var finals = getScore(); var totals = totalScore(); - var scoreString = params.endGame.resultPage.scoreString.replace("@score", finals).replace("@total", totals); + var scoreString = params.endGame.scoreString.replace("@score", finals).replace("@total", totals); var success = ((100 * finals / totals) >= params.passPercentage); var eventData = { score: scoreString, @@ -199,11 +195,11 @@ H5P.QuestionSet = function (options, contentId) { } var eparams = { - greeting: (success ? params.endGame.resultPage.succesGreeting : params.endGame.resultPage.failGreeting), + greeting: (success ? params.endGame.successGreeting : params.endGame.failGreeting), score: scoreString, scoreclass: (success ? 'success' : 'fail'), - resulttext: (success ? params.endGame.resultPage.successComment : params.endGame.resultPage.failComment), - finishButtonText: params.endGame.resultPage.finishButtonText + resulttext: (success ? params.endGame.successComment : params.endGame.failComment), + finishButtonText: params.endGame.finishButtonText }; // Show result page. @@ -214,8 +210,8 @@ H5P.QuestionSet = function (options, contentId) { }); }; - if (params.endGame.animations.showAnimations) { - var videoData = success ? params.endGame.animations.successVideo : params.endGame.animations.failVideo; + if (params.endGame.showAnimations) { + var videoData = success ? params.endGame.successVideo : params.endGame.failVideo; if (videoData) { var $videoContainer = $('
').appendTo($myDom); @@ -231,8 +227,8 @@ H5P.QuestionSet = function (options, contentId) { }; video.attach($videoContainer); - if (params.endGame.animations.skipButtonText) { - $('').click(function () { + if (params.endGame.skipButtonText) { + $('').click(function () { video.stop(); $videoContainer.hide(); displayResults(); @@ -256,9 +252,12 @@ H5P.QuestionSet = function (options, contentId) { } // Render own DOM into target. - $myDom.html(template.render(params)).css({ - backgroundImage: 'url(' + cp + params.backgroundImage.path + ')' - }); + $myDom.html(template.render(params)); + if (params.backgroundImage !== undefined) { + $myDom.css({ + background: 'url(' + cp + params.backgroundImage.path + ')' + }); + } // Attach questions for (var i = 0; i < questionInstances.length; i++) { diff --git a/semantics.json b/semantics.json index db0c03d..acd3f6b 100644 --- a/semantics.json +++ b/semantics.json @@ -15,12 +15,14 @@ "name": "initialQuestion", "type": "number", "label": "Initial question", - "description": "Which question to start with. Count from 0." + "description": "Which question to start with. Count from 0.", + "default": 0 }, { "name": "backgroundImage", "type": "image", "label": "Background image", + "optional": true, "description": "An optional background image for the Question set." }, { @@ -52,6 +54,7 @@ }, { "name": "questions", + "label": "Questions", "type": "list", "entity": "question", "field": { @@ -74,28 +77,35 @@ { "name": "introPage", "type": "group", - "label": "Intro page", - "description": "Data for the intro page.", + "label": "Quiz introduction", "fields": [ { "name": "showIntroPage", "type": "boolean", - "label": "Show intro page?" + "label": "Display introduction" }, { "name": "title", "type": "text", - "label": "Title" + "label": "Title", + "optional": true, + "description": "This title will be displayed above the introduction text." }, { "name": "introduction", "type": "text", - "label": "Introduction text" + "widget": "textarea", + "label": "Introduction text", + "optional": true, + "description": "This text will be displayed before the quiz starts." }, { "name": "startButtonText", "type": "text", - "label": "Start button text" + "label": "Start button text", + "optional": true, + "placeholder": "Start Quiz", + "default": "Start Quiz" } ] }, @@ -135,87 +145,85 @@ { "name": "endGame", "type": "group", - "label": "End game data", + "label": "Quiz finished", "fields": [ { "name": "showResultPage", "type": "boolean", - "label": "Show result page", + "label": "Display results", "default": true }, { - "name": "resultPage", - "type": "group", - "label": "Result page", - "description": "Data and texts for the result page.", - "fields": [ - { - "name": "successGreeting", - "type": "text", - "label": "Success greeting", - "description": "Title in result page on success." - }, - { - "name": "successComment", - "type": "text", - "label": "Success comment", - "description": "Comment shown after the score." - }, - { - "name": "failGreeting", - "type": "text", - "label": "Failed greeting", - "description": "Title in result page on failed quiz." - }, - { - "name": "failComment", - "type": "text", - "label": "Failed comment", - "description": "Comment shown after the score on failed quiz." - }, - { - "name": "finishButtonText", - "type": "text", - "label": "Finish button text", - "description": "Text for the finish button." - } - ] + "name": "successGreeting", + "type": "text", + "label": "Quiz passed title", + "placeholder": "Congratulations!", + "default": "Congratulations!", + "description": "This title will be displayed above results if the user has successfully passed the quiz." }, { - "name": "animations", - "type": "group", - "label": "Animations", - "fields": [ - { - "name": "showAnimations", - "type": "boolean", - "label": "Show animations" - }, - { - "name": "skippable", - "type": "boolean", - "label": "Skippable" - }, - { - "name": "skipButtonText", - "type": "text", - "label": "Skip button text", - "common": true - }, - { - "name": "successVideo", - "type": "video", - "label": "Success video", - "description": "Video displayed on successful quiz." - }, - { - "name": "failVideo", - "type": "video", - "label": "Fail video", - "description": "Video displayed on failed quiz." - } - ] + "name": "successComment", + "type": "text", + "widget": "textarea", + "label": "Passed comment", + "default": "You did very well!", + "description": "This comment will be displayed after the score if the user has successfully passed the quiz." + }, + { + "name": "failGreeting", + "type": "text", + "label": "Quiz failed title", + "placeholder": "Oh, no!", + "default": "Oh, no!", + "description": "This title will be displayed above the results if the user has failed the quiz." + }, + { + "name": "failComment", + "type": "text", + "widget": "textarea", + "label": "Failed comment", + "default": "This didn't go so well.", + "description": "This comment will be displayed after the score if the user has failed the quiz." + }, + { + "name": "finishButtonText", + "type": "text", + "label": "Finish button text", + "default": "Finish", + "description": "Text for the finish button." + }, + { + "name": "showAnimations", + "type": "boolean", + "label": "Display video before quiz results" + }, + { + "name": "skippable", + "type": "boolean", + "label": "Enable skip video button" + }, + { + "name": "skipButtonText", + "type": "text", + "label": "Skip video button label", + "default": "Skip video", + "description": "This is the label for the button which allows users to skip videos after a quiz.", + "common": true + }, + { + "name": "successVideo", + "type": "video", + "label": "Passed video", + "optional": true, + "description": "This video will be played if the user successfully passed the quiz." + }, + { + "name": "failVideo", + "type": "video", + "label": "Fail video", + "optional": true, + "description": "This video will be played if the user failes the quiz." } ] } -] +] \ No newline at end of file From bde6f57f5ae022ffb34c200fa152f0e8e957b639 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 3 May 2013 10:05:49 +0200 Subject: [PATCH 15/83] Made questionset standalone possible. --- css/questionset.css | 82 +++++++++++++++++++++++++++++++-------------- js/questionset.js | 46 +++++++++++++++++++++---- 2 files changed, 97 insertions(+), 31 deletions(-) diff --git a/css/questionset.css b/css/questionset.css index 152b284..af821e1 100644 --- a/css/questionset.css +++ b/css/questionset.css @@ -1,3 +1,13 @@ +.questionset.hidden { + display: none; +} +.questionset .h5p-multichoice > .h5p-show-solution { + display: none; +} +.questionset > .title { + font-size: 2em; + margin: 0.5em 0.16em 0.16em; +} .dots-container { text-align: center; } @@ -22,9 +32,9 @@ } .intro-page .title { font-size: 2em; + line-height: 2em; font-weight: bold; text-align: center; - padding: 1em 1em 0.5em; } .intro-page .introduction { font-size: 1.25em; @@ -35,18 +45,49 @@ text-align: center; } -#qs-startbutton { - font-size: 3em; - border: 0.083em solid #BABABA; - border-radius: 0.2083em; - padding: 0.2083em 0.4167em; +.qs-footer { + overflow: hidden; +} + +.qs-footer .button, .qs-startbutton, .qs-finishbutton, .questionset-results .button { + display: block; + line-height: 1.25em; + padding: 0.25em 1em; + border-radius: 0.75em; cursor: pointer; + color: #fff; + box-shadow: 0 0 0.5em #aebdc9; + margin: 0.5em; + + background: #267ec9; + background-image: -webkit-linear-gradient(top, #3999ea, #267ec9); + background-image: -moz-linear-gradient(top, #3999ea, #267ec9); + background-image: -o-linear-gradient(top, #3999ea, #267ec9); + background-image: -ms-linear-gradient(top, #3999ea, #267ec9); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#3999ea', endColorstr='#267ec9',GradientType=0); +} +.qs-footer .button:hover, .qs-startbutton:hover, .questionset-results .button:hover { + text-decoration: none; + color: #fff; + box-shadow: 0 0 0.5em #267ec9; +} +.qs-startbutton { + display: inline-block; + font-size: 2.5em; +} +.qs-footer > .prev, .qs-solutionbutton { + float: left; +} +.qs-footer > .next, .qs-footer > .finish, .qs-finishbutton { + float: right; +} +.questionset-results .qs-finishbutton { + display: none; } .qs-progress { - line-height: 2em; - padding: 0; - margin: 1em 1em 0; + text-align: center; + line-height: 1.5em; } .questionset-results { @@ -58,17 +99,17 @@ } .questionset-results .score { - font-size: 25px; - margin-top: 40px; + font-size: 1.5em; + margin: 1em; } .questionset-results .resulttext { text-align: left; - font-size: 20px; - line-height: 25px; - margin: 40px 100px; - padding-left: 60px; - min-height: 60px; + font-size: 1.25em; + line-height: 1.25em; + margin: 1em 4em; + padding-left: 3em; + min-height: 3em; background-repeat: no-repeat; background-position: left center; } @@ -87,13 +128,4 @@ .questionset-results .resulttext.success em { font-style: normal; color: #6aa81b; -} -.questionset-results .buttons { - position: absolute; - bottom: 40px; - right: 25px; -} - -button, .button { - cursor: pointer; } \ No newline at end of file diff --git a/js/questionset.js b/js/questionset.js index 27edb9c..9d14a3c 100644 --- a/js/questionset.js +++ b/js/questionset.js @@ -31,10 +31,10 @@ H5P.QuestionSet = function (options, contentId) { '
' + '
<%= introPage.title %>
' + '
<%= introPage.introduction %>
' + - ' ' + + ' ' + '
' + - '<%} %>' + - '