var Activity_tutorial = Activity_subtype.extend({
restore_saved_answers: function() {
this._super();
// for each singlet question, set display to block if the question
// has been answered
$('[data-type="question"][data-block_type="single"]').each(function() {
var $this = $(this);
var q_idx = $this.attr('data-question_index');
var question = player.activity.questions[q_idx];
//safe_log("question " + q_idx);
//safe_log(question);
var q = question.queries[0];
//safe_log(q);
if ((q.grade == 0 || q.grade == 100) && q.user_answer_index !== null) {
$this.css('display','block');
$this.addClass("answered");
$this.find('.select_correct').remove();
// hack for Q1
if (q_idx == 1) {
var $question_action_div = $this.find('.question_action_div');
$question_action_div.text("");
var feedbackDiv = $('
').addClass('question_feedback');
if (q.grade == 0) {
$question_action_div .append('
Incorrect.
Points awarded: 0 out of 1
');
}
else {
$question_action_div .append('
Correct.
Points awarded: 1 out of 1
');
}
}
//safe_log("restoring question " + q_idx + ", grade = " + q.grade);
//safe_log(question);
// handle MC questions with no correct answer
if (q.md.no_correct_answer == "true") {
var answer_index = q.user_answer_index;
$this.find('.query_mc_choice_table:eq(' + answer_index + ')')
.removeClass('disabled').addClass('correct');
if (question.md.feedback_correct !== undefined) {
$this.find('.question_feedback_part').html(question.md.feedback_correct);
}
}
// remove submit button for essay questions
if (/^ES:/.test(q.query_source)) {
$this.find('.question_submit_button').remove();
}
}
});
},
submit_question: function(question_index) {
var q_idx = question_index;
if (typeof q_idx == "object") {
// extract the question_index from the object
q_idx = question_index.data.question_index;
}
this._super(question_index);
$('[data-question_index="' + q_idx + '"]').find('.select_correct').remove();
//safe_log("submit_question: q_idx = " + q_idx);
// if there is no correct answer then set the answer the user chose
// as correct
var question = player.activity.questions[q_idx].queries[0];
if (question.md.no_correct_answer == "true") {
var answer_index = question.user_answer_index;
$('[data-question_index="' + q_idx + '"]')
.find('.query_mc_choice_table:eq(' + answer_index + ')')
.removeClass('disabled').addClass('correct');
}
// update progress indicator for single questions
var q_block_type = $('[data-question_index="' + q_idx + '"]').attr('data-block_type');
if (q_block_type == "single") {
$('.progress-wrapper .cell').not('.complete').eq(0).addClass('complete');
}
},
add_question_sequence: function(index, element) {
//safe_log("entering add_question_sequence");
var a = player.activity; // this fn gets called within the scope of a jquery each, so we can't refer to "this"
var index = a.question_sequences.length;
var qs = a.question_sequences[index] = new Object();
var jq = $(element);
jq.attr("question_sequence_index", index);
qs.index = index;
qs.jq = jq;
qs.inner_jq = jq.find("[data-type=box_inner]").first();
qs.questions = new Array();
qs.sequence_complete = false;
//safe_log(jq);
var $first_q = jq.find('[data-type="question"]').first();
var first_q_num = parseInt($first_q.find('[data-type="number"]').text());
// note whether this is a "one-at-a-time" sequence
qs.one_at_a_time = (jq.attr("data-block_type").search("one_at_a_time") > -1);
// if it *is* a one-at-a-time sequence, then ...
if (qs.one_at_a_time === true) {
// anything below the questions is considered "closing material". Wrap this stuff in a div and hide it
var question_found = false;
qs.inner_jq.children().each(function(index, element) {
var cjq = $(element);
if (cjq.attr("data-type") == "question") {
question_found = true;
} else if (question_found == true) {
if (jq.closing_material == null) {
var html = "";
qs.inner_jq.append(html);
qs.closing_material = jq.find(".question_sequence_closing_material").first();
}
qs.closing_material.append(cjq.detach());
}
});
// also show a navigator for the question sequence
var html = "
"
;
qs.inner_jq.prepend(html);
}
},
add_question: function(index, element) {
//safe_log("add_question: " + index);
var a = player.activity; // this fn gets called within the scope of a jquery each, so we can't refer to "this"
var jq = $(element);
// see if we're in a question_pool or question_sequence block
var qs_parent = jq.parents("[data-block_type=question_sequence], [data-block_type=question_sequence_one_at_a_time]");
var q = a.questions[a.questions.length] = new Question({
"index": a.questions.length
, "number": jQuery.trim($("[data-type='number']", jq).html())
, "title": jQuery.trim($("[data-type='title']", jq).html())
, "jq": jq
, "points_possible": 0
});
//safe_log(q);
// store the question index in the DOM too
jq.attr("data-question_index", q.index);
// get question queries
a.question_being_initialized = q;
$("[data-type='query']", jq).each(a.add_query);
// if the question has queries, add bling to the bottom
if (q.queries.length > 0) {
var html = "
";
var submit_handler;
// add a submit button if we're supposed to have one
// (image map and matching questions, which should contain one and only one query, don't have submit buttons)
if (q.queries[0].hasSubmitButton === true) {
// "answer_one" pool mode (yet to be fully implemented...)
if (q.pool_mode == 'answer_one') {
html += "";
submit_handler = player.activity.Show_Submit;
} else {
html += a.question_submit_button(q);
submit_handler = player.activity.submit_question;
}
}
// add a "give up" button if we're supposed to have one
// (image map questions, which should contain one and only one query, should have this button)
if (q.queries[0].hasGiveUpButton === true) {
html += "";
submit_handler = player.activity.give_up_question;
}
html += "
";
jq.append(html);
// bind the submit_handler function to the button, passing in the question_index
$("#question_submit_button_" + q.index).click({question_index:q.index}, submit_handler);
// float instructor controls to right of action button
jq.prepend(a.instructor_question_controls(q));
// also, determine question points and weight at this time
for (var i = 0; i < q.queries.length; ++i) {
q.points_possible += q.queries[i].getPointsPossible();
}
// round to two decimal places, in case there are decimals
q.points_possible = Math.round(q.points_possible * 100) / 100;
}
// now that the question has been created, deal with sequences and pools
if (qs_parent.length > 0) {
// get sequence index and item
var qs_index = qs_parent.attr("question_sequence_index") * 1;
var qs = a.question_sequences[qs_index];
// place in sequence
var sequence_num = qs.questions.length;
qs.questions[sequence_num] = q;
// place reference to sequence in question
q.question_sequence = qs;
q.question_sequence_index = sequence_num;
// if this isn't #1, hide the question
if (sequence_num > 0) {
jq.hide();
}
// update the sequence navigator if it's there
$("#question_sequence_max_index_" + qs.index).html(sequence_num+1);
}
// brb: hack to get the total number of questions correct for the
// sequence navigator
$("#question_sequence_max_index_0").html("7");
},
show_next_question: function(question) {
//safe_log("show_next_question");
//safe_log(question);
// if the item is in a sequence, show the next question
if (question.question_sequence != null) {
var qs = question.question_sequence;
var next_qs_index = question.question_sequence_index + 1;
var next_question = qs.questions[next_qs_index];
// what we do next is a little different
// depending on whether this is a one-at-a-time sequence
if (qs.one_at_a_time === false) {
// for *not* one at a time, just slide down the next item
if (next_question != null) {
next_question.jq.slideDown("fast", function() {
// when we're done showing the new question, scroll down to it
$(window).scrollTo($(this));
});
// if there is no next_question, sequence is complete.
} else {
qs.sequence_complete = "complete";
}
// for one-at-a-time, ...
} else {
// if we have a next question, change the question submit button
// to a button for showing the next item
if (next_question != null) {
$("#question_submit_button_" + question.index).val("Next Question")
.unbind('click')
.click({"question":question, "next_question":next_question}, function(e) {
// on click of this button, hide the current question and fade in the next one
e.data.question.jq.hide();
// use the below animation instead of fadeIn because it doesn't completely hide the item first
// and thus doesn't cause the window to scroll to the top.
e.data.next_question.jq.show()
.css({opacity:0.25})
.animate({opacity:1.0}, 500);
$.scroll_item_onto_screen(e.data.next_question.jq, {duration:10, additional_padding:50});
// update the sequence navigator
var index = e.data.next_question.question_sequence_index + 1;
var qs = e.data.question.question_sequence.index;
// brb: update the question number in the
// sequence navigator
var next_q_num = e.data.next_question.jq.attr('data-question_index');
//$("#question_sequence_index_" + qs).html(index);
$("[question_sequence_index=0]").find("#question_sequence_index_" + qs).html(next_q_num);
})
.show();
// make sure the button is visible
$.scroll_item_onto_screen($("#question_submit_button_" + question.index), {duration:10, additional_padding:50} );
// else we're done with the sequence, so if sequence_complete is false...
} else if (qs.sequence_complete === false) {
var points_possible = 0;
var points_awarded = 0;
for (var i = 0; i < qs.questions.length; ++i) {
for (var j = 0; j < qs.questions[i].queries.length; ++j) {
var query = qs.questions[i].queries[j];
points_awarded += query.getPointsAwarded();
points_possible += query.getPointsPossible();
}
}
// round to at most two decimal places
points_awarded = Math.round(points_awarded * 100) / 100;
points_possible = Math.round(points_possible * 100) / 100;
var html = "
"
+ "Total points awarded for this question sequence: " + points_awarded + " out of " + points_possible + " "
+ "
"
;
if (qs.closing_material != null) {
// if student is reviewing (i.e. came back to the activity after completing in an earlier
// session), the show_closing_material function will be called by restore_saved_answers
var show_summary_text = "Show Summary"
html += ""
// if there's closing material we're *kind of* done with the sequence now;
// set to "partial" so we won't add this html again
qs.sequence_complete = "partial";
// if there's no closing material, we're completely through with the sequence now.
} else {
qs.sequence_complete = "complete";
// in this case we need to show the question_sequence_review right away
html = html.replace(/class='question_sequence_review'/, "class='question_sequence_review' style='display:none'");
}
qs.inner_jq.append(html);
$("#closing_material_button_" + qs.index).button();
}
}
}
// brb: display all questions if we just answered the last one
var block_type = question.jq.attr('data-block_type');
if (block_type == "last_sequence_question") {
$('[data-block_type="question_sequence_one_at_a_time"][question_sequence_index="0"]').find('[data-type="question"]').each(function() {
var $this = $(this);
$this.css('display', 'block');
$this.find('.question_submit_button').hide();
$('.question_sequence_navigator').hide();
$this.find('.select_correct').remove();
});
}
}
});
var Player_tutorial = Player_devtk.extend({
initialize: function() {
this._super();
this.activity = new Activity_tutorial();
},
initialize2: function() {
this._super();
$('body').addClass('tutorial');
$('[data-block_type="instructions"]').each(function() {
var $this = $(this);
$this.attr('data-type', '');
$this.addClass('instructions');
});
// set up handler for MC choice option
$('[data-type="question"][data-block_type="single"] .query_mc_choice_table').each(function() {
$(this).click(function() {
var $this = $(this);
$this.closest('[data-type="question"]').find('.question_submit_button').show();
var radioBtn = $(this).find('input[type="radio"]');
radioBtn.attr('checked', true);
// remove currently selected answer
var $parent = $this.parent();
$parent.find('.query_mc_choice_table').removeClass('selected');
// select this answer
$this.addClass('selected');
});
});
$('[data-type="question"][data-block_type="single"] h3 [data-type="number"]').each(function() {
var $this = $(this);
var number = parseInt($this.text());
$this.text("Question " + number);
});
// Add some markup for instruction text on each question
$('[data-type="question"][data-block_type="single"]').not('.answered').find('.question_action_div').each(function() {
var $this = $(this);
var feedbackDiv = $('').addClass('select_correct');
var instr_text = "Select the correct answer.";
// check if we have customized instructions in the XML
var instructions = $this.prev().find('[key="instructions"]').text();
if (instructions.length > 0) {
instr_text = instructions;
}
feedbackDiv.append($('').addClass('question_feedback_part').text(instr_text));
$this.prepend(feedbackDiv);
});
// add question sequence navigator for each singlet
$('[data-type="question"][data-block_type="single"]').each(function() {
var $this = $(this);
var q_num = $this.attr('data-question_index');
var str = "
" + q_num + " of 7
";
$this.before(str);
});
// display all sequence questions if last question in sequence
// is answered
$('[data-block_type="last_sequence_question"]').each(function() {
var $this = $(this);
var q_idx = $this.attr('data-question_index');
var question = player.activity.questions[q_idx];
var q = question.queries[0];
if ((q.grade == 0 || q.grade == 100) && q.user_answer_index !== null) {
$this.parents('[data-block_type="question_sequence_one_at_a_time"]').find('[data-type="question"]').each(function() {
$(this).addClass('answered');
$(this).show();
});
}
});
} // end initialize2
});
player = new Player_tutorial();