4296 lines
176 KiB
JavaScript
4296 lines
176 KiB
JavaScript
/*
|
|
* Copyright (C) 2011 - present Instructure, Inc.
|
|
*
|
|
* This file is part of Canvas.
|
|
*
|
|
* Canvas is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
* Software Foundation, version 3 of the License.
|
|
*
|
|
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// xsslint jqueryObject.function makeFormAnswer makeDisplayAnswer
|
|
// xsslint jqueryObject.property sortable placeholder
|
|
// xsslint safeString.property question_text
|
|
import regradeTemplate from 'jst/quiz/regrade'
|
|
import I18n from 'i18n!quizzes'
|
|
import _ from 'underscore'
|
|
import $ from 'jquery'
|
|
import calcCmd from './calcCmd'
|
|
import htmlEscape from './str/htmlEscape'
|
|
import pluralize from './str/pluralize'
|
|
import Handlebars from 'compiled/handlebars_helpers'
|
|
import DueDateOverrideView from 'compiled/views/assignments/DueDateOverride'
|
|
import Quiz from 'compiled/models/Quiz'
|
|
import DueDateList from 'compiled/models/DueDateList'
|
|
import QuizRegradeView from 'compiled/views/quizzes/QuizRegradeView'
|
|
import SectionList from 'compiled/collections/SectionCollection'
|
|
import MissingDateDialog from 'compiled/views/calendar/MissingDateDialogView'
|
|
import MultipleChoiceToggle from 'compiled/editor/MultipleChoiceToggle'
|
|
import EditorToggle from 'compiled/editor/EditorToggle'
|
|
import TextHelper from 'compiled/str/TextHelper'
|
|
import RCEKeyboardShortcuts from 'compiled/views/editor/KeyboardShortcuts'
|
|
import INST from './INST' // safari sniffing for VO workarounds
|
|
import QuizFormulaSolution from './quiz_formula_solution'
|
|
import addAriaDescription from './quiz_labels'
|
|
import RichContentEditor from 'jsx/shared/rce/RichContentEditor'
|
|
import ConditionalRelease from 'jsx/shared/conditional_release/ConditionalRelease'
|
|
import deparam from 'compiled/util/deparam'
|
|
import SisValidationHelper from 'compiled/util/SisValidationHelper'
|
|
import LockManager from 'jsx/blueprint_courses/apps/LockManager'
|
|
import './jquery.ajaxJSON'
|
|
import './jquery.instructure_date_and_time' /* time_field, datetime_field */
|
|
import './jquery.instructure_forms' /* formSubmit, fillFormData, getFormData, formErrors, errorBox */
|
|
import 'jqueryui/dialog'
|
|
import './jquery.instructure_misc_helpers' /* replaceTags, /\$\.underscore/ */
|
|
import './jquery.instructure_misc_plugins' /* .dim, confirmDelete, showIf */
|
|
import './jquery.keycodes'
|
|
import './jquery.loadingImg'
|
|
import 'compiled/jquery.rails_flash_notifications'
|
|
import './jquery.templateData'
|
|
import './supercalc'
|
|
import './vendor/jquery.scrollTo'
|
|
import 'jqueryui/sortable'
|
|
import 'jqueryui/tabs'
|
|
|
|
var dueDateList, overrideView, quizModel, sectionList, correctAnswerVisibility, scoreValidation;
|
|
|
|
var lockManager = new LockManager()
|
|
lockManager.init({ itemType: 'quiz', page: 'edit' })
|
|
const lockedItems = lockManager.isChildContent() ? lockManager.getItemLocks() : {}
|
|
|
|
|
|
RichContentEditor.preloadRemoteModule();
|
|
|
|
function adjustOverridesForFormParams(overrides){
|
|
var idx = 0;
|
|
var overridesLength = overrides.length;
|
|
var _override = null;
|
|
var dates = [ 'due_at', 'lock_at', 'unlock_at' ];
|
|
// make sure we don't send the literal string "null" to the server.
|
|
for (idx;idx< overridesLength;idx++){
|
|
_override = overrides[idx]
|
|
for (var date in dates) {
|
|
var _date = dates[date];
|
|
if (!dates.hasOwnProperty(date)) continue;
|
|
if (_override[_date]) {
|
|
if(_override[_date].toUTCString){
|
|
_override[_date] = _override[_date].toUTCString();
|
|
}
|
|
} else {
|
|
_override[_date] = "";
|
|
}
|
|
}
|
|
// TODO: let the quiz API handle this.
|
|
// The AssignmentOverride model can take care of these values.
|
|
// See the automatically defined methods via self.override
|
|
// in app/models/assignment_override.rb
|
|
delete _override.unlock_at_overridden;
|
|
delete _override.lock_at_overridden;
|
|
delete _override.all_day_date;
|
|
delete _override.due_at_overridden;
|
|
delete _override.all_day;
|
|
}
|
|
}
|
|
|
|
var isShowingResults = function() {
|
|
return $('#never_hide_results').prop('checked');
|
|
};
|
|
|
|
var isShowingResultsJustOnce = function() {
|
|
return $('#quiz_one_time_results').prop('checked');
|
|
};
|
|
|
|
var renderDueDates = function(){
|
|
if (ENV.QUIZ && ENV.ASSIGNMENT_OVERRIDES != null) {
|
|
|
|
ENV.QUIZ.assignment_overrides = ENV.ASSIGNMENT_OVERRIDES;
|
|
quizModel = new Quiz(ENV.QUIZ);
|
|
|
|
sectionList = new SectionList(ENV.SECTION_LIST);
|
|
|
|
dueDateList = new DueDateList(quizModel.get('assignment_overrides'),
|
|
sectionList, quizModel);
|
|
|
|
overrideView = window.overrideView = new DueDateOverrideView({
|
|
el: '.js-assignment-overrides',
|
|
model: dueDateList,
|
|
views: {},
|
|
dueDatesReadonly: lockedItems.due_dates,
|
|
availabilityDatesReadonly: lockedItems.availability_dates
|
|
});
|
|
overrideView.render()
|
|
}
|
|
}.bind(this);
|
|
|
|
var clickSetCorrect = I18n.t('titles.click_to_set_as_correct', "Click to set this answer as correct"),
|
|
isSetCorrect = I18n.t('titles.set_as_correct', "This answer is set as correct"),
|
|
clickUnsetCorrect = I18n.t('titles.click_to_unset_as_correct', "Click to unset this answer as correct"),
|
|
correctAnswerLabel = I18n.t('labels.correct_answer', "Correct Answer"),
|
|
possibleAnswerLabel = I18n.t('labels.possible_answer', "Possible Answer");
|
|
|
|
function togglePossibleCorrectAnswerLabel($answers) {
|
|
if(!$("#questions").hasClass('survey_quiz')){
|
|
$answers.find('.select_answer label').text(possibleAnswerLabel);
|
|
$answers.find('.select_answer input[name=answer_text]').attr("aria-label", possibleAnswerLabel);
|
|
$answers.filter('.correct_answer').find(".select_answer label").text(correctAnswerLabel);
|
|
$answers.filter('.correct_answer').find(".select_answer input[name=answer_text]").attr("aria-label", correctAnswerLabel);
|
|
}
|
|
}
|
|
|
|
function toggleSelectAnswerAltText($answers,type) {
|
|
if (type != "multiple_answer") {
|
|
$answers.find('.select_answer_link')
|
|
.attr('title', clickSetCorrect)
|
|
.find('img').attr('alt', clickSetCorrect);
|
|
$answers.filter('.correct_answer').find('.select_answer_link')
|
|
.attr('title', isSetCorrect)
|
|
.find('img').attr('alt', isSetCorrect);
|
|
} else {
|
|
$answers.filter(".correct_answer").find('.select_answer_link')
|
|
.attr('title', clickUnsetCorrect)
|
|
.find('img').attr('alt', clickUnsetCorrect);
|
|
$answers.filter(":not(.correct_answer)").find('.select_answer_link')
|
|
.attr('title', clickSetCorrect)
|
|
.find('img').attr('alt', clickSetCorrect);
|
|
}
|
|
}
|
|
|
|
export function isChangeMultiFuncBound ($questionContent) {
|
|
var ret = false;
|
|
var events = $._data($questionContent[0], 'events');
|
|
if (events && events.change) {
|
|
events.change.forEach(function (event) {
|
|
if (event.handler.origFuncNm === 'changeMultiFunc') {
|
|
ret = true;
|
|
}
|
|
});
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function getChangeMultiFunc ($questionContent, questionType, $select) {
|
|
var ret = function changeMultiFunc () {
|
|
if (questionType !== 'multiple_dropdowns_question' && questionType !== 'fill_in_multiple_blanks_question') {
|
|
return;
|
|
}
|
|
var text = RichContentEditor.callOnRCE($questionContent, 'get_code');
|
|
var matches = text.match(/\[[A-Za-z0-9_\-.]+\]/g);
|
|
$select.find('option.shown_when_no_other_options_available').remove();
|
|
$select.find('option').addClass('to_be_removed');
|
|
var matchHash = {};
|
|
if (matches) {
|
|
for (var idx = 0; idx < matches.length; idx++) {
|
|
if (matches[idx]) {
|
|
var variable = matches[idx].substring(1, matches[idx].length - 1);
|
|
if (!matchHash[variable]) {
|
|
var $option = $select.find('option').eq(idx);
|
|
if (!$option.length) {
|
|
$option = $('<option/>').appendTo($select);
|
|
}
|
|
$option
|
|
.removeClass('to_be_removed')
|
|
.val(variable)
|
|
.text(variable);
|
|
matchHash[variable] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// if there are not any options besides the default:
|
|
// "<option class='shown_when_no_other_options_available' value='0'>[ Enter Answer Variables Above ]</option>"
|
|
if (!$select.find('option:not(.shown_when_no_other_options_available)').length) {
|
|
$select.append("<option class='shown_when_no_other_options_available' value='0'>" + htmlEscape(I18n.t('enter_answer_variable_above',
|
|
'[ Enter Answer Variables Above ]')) + '</option>');
|
|
}
|
|
$select.find('option.to_be_removed').remove();
|
|
$select.change();
|
|
};
|
|
// doing the following because minifying will change the function name
|
|
// which we reference in isChangeMultiFuncBound()
|
|
ret.origFuncNm = 'changeMultiFunc';
|
|
return ret;
|
|
}
|
|
|
|
// TODO: refactor this... it's not going to be horrible, but it will
|
|
// take a little bit of work. I just wrapped it in a closure for now
|
|
// to not pollute the global namespace, but it could use more.
|
|
export const quiz = window.quiz = {
|
|
uniqueLocalIDStore: {},
|
|
|
|
// Should cache any elements used throughout the object here
|
|
init: function () {
|
|
this.$questions = $('#questions');
|
|
this.$showDetailsWrap = $('#show_question_details_wrap').hide();
|
|
|
|
return this;
|
|
},
|
|
|
|
// Determines whether or not to show the "show question details" link.
|
|
checkShowDetails: function() {
|
|
var hasQuestions = this.$questions.find('div.display_question:not(.essay_question, .file_upload_question, .text_only_question)').length;
|
|
this.$showDetailsWrap[hasQuestions ? 'show' : 'hide'](200);
|
|
},
|
|
|
|
generateUniqueLocalID: function($obj) {
|
|
var className = "object";
|
|
if ($obj.attr('class')) {
|
|
if ($obj.attr('class').indexOf(' ') != -1) {
|
|
className = $obj.attr('class').split(' ')[0];
|
|
} else {
|
|
className = $obj.attr('class');
|
|
}
|
|
}
|
|
var number = Math.floor(Math.random() * 99999);
|
|
var id = className + "_" + number;
|
|
while(quiz.uniqueLocalIDStore[id]) {
|
|
number = Math.floor(Math.random() * 99999);
|
|
id = className + "_" + number;
|
|
}
|
|
quiz.uniqueLocalIDStore[id] = true;
|
|
return id;
|
|
},
|
|
|
|
// Updates answer when the question's type changes
|
|
updateFormAnswer: function($answer, data, ignoreCurrent) {
|
|
var question_type = data.question_type;
|
|
var currentData = ignoreCurrent ? {} : $answer.getFormData();
|
|
var answer = $.extend({}, quiz.defaultAnswerData, currentData, data);
|
|
|
|
$answer.find(".answer_type").hide().filter("." + answer.answer_type).show();
|
|
answer.answer_weight = parseFloat(answer.answer_weight);
|
|
|
|
if (isNaN(answer.answer_weight)) {
|
|
answer.answer_weight = 0;
|
|
}
|
|
|
|
$answer.fillFormData(answer, {call_change: false});
|
|
$answer.find('.select_answer input').showIf(!answer.answer_html);
|
|
$answer.find('.matching_answer .answer_match_left').showIf(!answer.answer_match_left_html);
|
|
$answer.find('.matching_answer .answer_match_left_html').showIf(answer.answer_match_left_html);
|
|
|
|
if (answer.answer_comment || answer.answer_comment_html) {
|
|
$answer.find(".answer_comments").removeClass('empty')
|
|
}
|
|
answer.answer_selection_type = answer.answer_selection_type || quiz.answerSelectionType(answer.question_type);
|
|
|
|
var $answers = $answer.parent().find(".answer");
|
|
if (answer.answer_selection_type == "any_answer") {
|
|
$answer.addClass('correct_answer');
|
|
} else if (answer.answer_selection_type == "matching") {
|
|
$answer.removeClass('correct_answer');
|
|
} else if (answer.answer_selection_type != "multiple_answer") {
|
|
$answers.find(".answer").filter(".correct_answer").not(":first").removeClass('correct_answer');
|
|
if ($answers.filter(".correct_answer").length === 0) {
|
|
$answers.filter(":first").addClass('correct_answer');
|
|
}
|
|
toggleSelectAnswerAltText($answers,answer.answer_selection_type);
|
|
togglePossibleCorrectAnswerLabel($answers);
|
|
} else {
|
|
toggleSelectAnswerAltText($answers,answer.answer_selection_type);
|
|
togglePossibleCorrectAnswerLabel($answers);
|
|
}
|
|
|
|
numericalAnswerTypeChange($answer.find(".numerical_answer_type"))
|
|
|
|
var templateData = {
|
|
answer_text: answer.answer_text,
|
|
id: answer.id,
|
|
match_id: answer.match_id
|
|
};
|
|
templateData.comments_header = I18n.beforeLabel(I18n.t('labels.comments_on_answer', "Comments, if the user chooses this answer"));
|
|
templateData.short_answer_header = I18n.beforeLabel(I18n.t('labels.possible_answer', "Possible Answer"));
|
|
|
|
$answer.find(".comment_focus").attr('title', I18n.t('titles.click_to_enter_comments_on_answer', 'Click to enter comments for the student if they choose this answer'));
|
|
|
|
if (question_type == "essay_question" || question_type == "file_upload_question") {
|
|
templateData.comments_header = I18n.beforeLabel(I18n.t('labels.comments_on_question', "Comments for this question"));
|
|
} else if (question_type == "matching_question") {
|
|
templateData.answer_match_left_html = answer.answer_match_left_html;
|
|
templateData.comments_header = I18n.beforeLabel(I18n.t('labels.comments_on_wrong_match', "Comments if the user gets this match wrong"));
|
|
$answer.find(".comment_focus").attr('title', I18n.t('titles.click_to_enter_comments_on_wrong_match', 'Click to enter comments for the student if they miss this match'));
|
|
} else if (question_type == "missing_word_question") {
|
|
templateData.short_answer_header = I18n.beforeLabel(I18n.t('labels.answer_text', "Answer text"));
|
|
} else if (question_type == "multiple_choice_question") {
|
|
templateData.answer_html = answer.answer_html;
|
|
} else if (question_type == "multiple_answers_question") {
|
|
templateData.answer_html = answer.answer_html;
|
|
templateData.short_answer_header = I18n.beforeLabel(I18n.t('labels.answer_text', "Answer text"));
|
|
} else if (question_type == "fill_in_multiple_blanks_question") {
|
|
templateData.blank_id = answer.blank_id;
|
|
} else if (question_type == "multiple_dropdowns_question") {
|
|
templateData.short_answer_header = I18n.t('answer_text', "Answer text");
|
|
templateData.blank_id = answer.blank_id;
|
|
}
|
|
|
|
if (answer.blank_id && answer.blank_id != '0') {
|
|
$answer.addClass('answer_for_' + answer.blank_id);
|
|
}
|
|
|
|
if (answer.blank_index >= 0) {
|
|
$answer.addClass('answer_idx_' + answer.blank_index);
|
|
}
|
|
|
|
$answer.fillTemplateData({
|
|
data: templateData,
|
|
htmlValues: ['answer_html', 'answer_match_left_html']
|
|
});
|
|
|
|
addHTMLFeedback($answer.find('.answer_comments'), answer, 'answer_comment');
|
|
|
|
if (answer.answer_weight > 0) {
|
|
$answer.addClass('correct_answer');
|
|
if (answer.answer_selection_type == "multiple_answer") {
|
|
$answer.find('.select_answer_link')
|
|
.attr('title', clickUnsetCorrect)
|
|
.find('img').attr('alt', clickUnsetCorrect);
|
|
}
|
|
} else if (answer.answer_weight < 0) {
|
|
$answer.addClass('negative_answer');
|
|
}
|
|
|
|
if (question_type == "matching_question") {
|
|
$answer.removeClass('correct_answer');
|
|
}
|
|
|
|
// won't exist if they've never clicked the edit button
|
|
var htmlToggle = $answer.find('.edit_html').data('editorToggle')
|
|
|
|
var supportsHTMLAnswers = question_type === 'multiple_choice_question' || question_type === 'multiple_answers_question'
|
|
if (htmlToggle && supportsHTMLAnswers) {
|
|
// some answer types share the same text fields, so we show it
|
|
htmlToggle.showAnswerText();
|
|
} else if (htmlToggle) {
|
|
// call display so the editor gets closed and we display the HTML next
|
|
// time we're editing an answer type that supports HTML answers
|
|
htmlToggle.display();
|
|
}
|
|
},
|
|
|
|
questionContentCounter: 0,
|
|
|
|
loadJQueryElemById: function (id) {
|
|
return $('#' + id);
|
|
},
|
|
|
|
rebindMultiChange: function (questionType, questionContentId, $select) {
|
|
var $questionContent = quiz.loadJQueryElemById(questionContentId);
|
|
if (questionType === 'multiple_dropdowns_question' || questionType === 'fill_in_multiple_blanks_question') {
|
|
if (!isChangeMultiFuncBound($questionContent)) {
|
|
$questionContent.bind('change', getChangeMultiFunc($questionContent, questionType, $select)).change();
|
|
}
|
|
}
|
|
},
|
|
|
|
showFormQuestion: function ($form) {
|
|
var $question = $form.find('.question');
|
|
var $questionContent = $question.find('.question_content');
|
|
var $select = $question.find('.blank_id_select');
|
|
var questionType = $question.find('.question_type').val();
|
|
if (!$form.attr('id')) {
|
|
// we show and then hide the form so that the layout for the editor is computed correctly
|
|
$form.show();
|
|
$form.find('.question_content').attr('id', 'question_content_' + quiz.questionContentCounter++);
|
|
RichContentEditor.loadNewEditor($questionContent, {
|
|
tinyOptions: {
|
|
aria_label: I18n.t('label.question.instructions', 'Question instructions, rich text area')
|
|
}
|
|
},
|
|
()=>{
|
|
quiz.rebindMultiChange(questionType, $questionContent[0].id, $select);
|
|
}
|
|
);
|
|
$form.find('.text_after_answers').attr('id', 'text_after_answers_' + quiz.questionContentCounter++);
|
|
RichContentEditor.loadNewEditor($form.find('.text_after_answers'));
|
|
$form.hide();
|
|
}
|
|
return $form.show();
|
|
},
|
|
|
|
answerTypeDetails: function(qt) {
|
|
var answer_type, question_type, n_correct = "one";
|
|
if (qt == 'multiple_choice_question') {
|
|
answer_type = "select_answer";
|
|
question_type = "multiple_choice_question";
|
|
} else if (qt == 'true_false_question') {
|
|
answer_type = "select_answer";
|
|
question_type = "true_false_question";
|
|
} else if (qt == 'short_answer_question') {
|
|
answer_type = "short_answer";
|
|
question_type = "short_answer_question";
|
|
n_correct = "all";
|
|
} else if (qt == 'fill_in_multiple_blanks_question') {
|
|
answer_type = "short_answer";
|
|
question_type = "fill_in_multiple_blanks_question";
|
|
n_correct = "all";
|
|
} else if (qt == 'essay_question') {
|
|
answer_type = "comment";
|
|
question_type = "essay_question";
|
|
n_correct = "none";
|
|
} else if (qt == 'file_upload_question') {
|
|
answer_type = "comment";
|
|
question_type = "file_upload_question";
|
|
n_correct = "none";
|
|
} else if (qt == 'matching_question') {
|
|
answer_type = "matching_answer";
|
|
question_type = "matching_question";
|
|
n_correct = "all";
|
|
} else if (qt == 'missing_word_question') {
|
|
answer_type = "select_answer";
|
|
question_type = "missing_word_question";
|
|
} else if (qt == 'multiple_dropdowns_question') {
|
|
answer_type = "select_answer";
|
|
question_type = "multiple_dropdowns_question";
|
|
n_correct = "multiple";
|
|
} else if (qt == 'numerical_question') {
|
|
answer_type = "numerical_answer";
|
|
question_type = "numerical_question";
|
|
n_correct = "all";
|
|
} else if (qt == 'multiple_answers_question') {
|
|
answer_type = "select_answer";
|
|
question_type = "multiple_answers_question";
|
|
n_correct = "multiple";
|
|
} else if (qt == 'calculated_question') {
|
|
answer_type = "numerical_answer";
|
|
question_type = "question_question";
|
|
}
|
|
return {
|
|
question_type: question_type,
|
|
answer_type: answer_type,
|
|
n_correct: n_correct
|
|
};
|
|
},
|
|
|
|
answerSelectionType: function(question_type) {
|
|
var result = "single_answer";
|
|
if (question_type == 'multiple_choice_question') {
|
|
} else if (question_type == 'true_false_question') {
|
|
} else if (question_type == 'short_answer_question') {
|
|
result = "any_answer";
|
|
} else if (question_type == 'essay_question') {
|
|
result = "none";
|
|
} else if (question_type == 'file_upload_question') {
|
|
result = "none";
|
|
} else if (question_type == 'matching_question') {
|
|
result = "matching";
|
|
} else if (question_type == 'missing_word_question') {
|
|
} else if (question_type == 'numerical_question') {
|
|
result = "any_answer";
|
|
} else if (question_type == 'calculated_question') {
|
|
result = "any_answer";
|
|
} else if (question_type == 'multiple_dropdowns_question') {
|
|
} else if (question_type == 'fill_in_multiple_blanks_question') {
|
|
result = "any_answer";
|
|
} else if (question_type == 'multiple_answers_question') {
|
|
result = "multiple_answer";
|
|
} else if (question_type == "text_only_question") {
|
|
result = "none";
|
|
}
|
|
return result;
|
|
},
|
|
|
|
addExistingQuestion: function(question) {
|
|
var $group = $("#group_top_" + question.quiz_group_id);
|
|
var $bottom = null;
|
|
if ($group.length > 0) {
|
|
$bottom = $group.next();
|
|
while($bottom.length> 0 && !$bottom.hasClass('group_bottom')) {
|
|
$bottom = $bottom.next();
|
|
}
|
|
if ($bottom.length == 0) { $bottom = null; }
|
|
}
|
|
$.extend(question, question.question_data);
|
|
var $question = makeQuestion(question);
|
|
$("#unpublished_changes_message").slideDown();
|
|
if ($bottom) {
|
|
$bottom.before($question);
|
|
} else {
|
|
$("#questions").append($question);
|
|
}
|
|
quiz.updateDisplayQuestion($question.find(".question:first"), question, true);
|
|
},
|
|
|
|
updateDisplayQuestion: function($question, question, escaped) {
|
|
|
|
var fillArgs = {
|
|
data: question,
|
|
except: ['answers'],
|
|
htmlValues: ['correct_comments_html', 'incorrect_comments_html', 'neutral_comments_html']
|
|
};
|
|
|
|
if (escaped) {
|
|
fillArgs['htmlValues'].push('question_text');
|
|
} else {
|
|
fillArgs['except'].push('question_text');
|
|
}
|
|
|
|
$question.fillTemplateData(fillArgs);
|
|
$question.find(".original_question_text").fillFormData(question);
|
|
$question.find(".answers").empty();
|
|
$question.find(".equation_combinations").empty();
|
|
$question.find(".equation_combinations_holder_holder").css('display', 'none');
|
|
$question.find(".multiple_answer_sets_holder").css('display', 'none');
|
|
$question.find(".variable_definitions_holder").css('display', 'none').find("tbody").empty();
|
|
$question.find(".formulas_holder").css('display', 'none').find(".formulas_list").empty();
|
|
$question.find('.question_points').text(question.points_possible);
|
|
var details = quiz.answerTypeDetails(question.question_type);
|
|
var answer_type = details.answer_type,
|
|
question_type = details.question_type,
|
|
n_correct = details.n_correct;
|
|
|
|
$question.attr('class', 'question display_question').addClass(question_type || 'text_only_question');
|
|
|
|
if (question.question_type == 'fill_in_multiple_blanks_question') {
|
|
$question.find(".multiple_answer_sets_holder").css('display', '');
|
|
} else if (question.question_type == 'multiple_dropdowns_question') {
|
|
$question.find(".multiple_answer_sets_holder").css('display', '');
|
|
}
|
|
var $select = $(document.createElement("select")).addClass('answer_select');
|
|
var hadOne = false;
|
|
if (question.question_type == 'calculated_question') {
|
|
$.each(question.variables, function(i, variable) {
|
|
var $tr = $("<tr/>");
|
|
var $td = $("<td class='name'/>");
|
|
$td.text(variable.name);
|
|
$tr.append($td);
|
|
$td = $("<td class='min'/>");
|
|
$td.text(variable.min);
|
|
$tr.append($td);
|
|
$td = $("<td class='max'/>");
|
|
$td.text(variable.max);
|
|
$tr.append($td);
|
|
$td = $("<td class='scale'/>");
|
|
$td.text(variable.scale);
|
|
$tr.append($td);
|
|
$question.find(".variable_definitions_holder").css('display', '');
|
|
$question.find(".variable_definitions tbody").append($tr);
|
|
});
|
|
$.each(question.formulas, function(i, formula) {
|
|
var $div = $("<div/>");
|
|
$div.text(formula.formula);
|
|
$question.find(".formulas_holder").css('display', '').find(".formulas_list").append($div);
|
|
});
|
|
$question.find(".formula_decimal_places").text(question.formula_decimal_places);
|
|
if (question.answers.length > 0) {
|
|
$question.find(".equation_combinations").append($("<thead/>"));
|
|
$question.find(".equation_combinations").append($("<tbody/>"));
|
|
var $tr = $("<tr/>");
|
|
for(var idx in question.answers[0].variables) {
|
|
var $th = $("<th/>");
|
|
$th.text(question.answers[0].variables[idx].name);
|
|
$tr.append($th);
|
|
}
|
|
var $th = $("<th/>");
|
|
$th.text(I18n.t('final_answer', "Final Answer"));
|
|
$tr.append($th);
|
|
$question.find(".equation_combinations_holder_holder").css('display', '');
|
|
$question.find(".equation_combinations thead").append($tr).show();
|
|
$.each(question.answers, function(i, data) {
|
|
var $tr = $("<tr/>");
|
|
for(var idx in data.variables) {
|
|
var $td = $("<td/>");
|
|
$td.text(data.variables[idx].value);
|
|
$tr.append($td);
|
|
}
|
|
var $td = $("<td class='final_answer'/>");
|
|
var answerHtml = data.answer;
|
|
if (question.answerDecimalPoints || question.answer_tolerance) {
|
|
var tolerance = parseFloatOrPercentage(question.answer_tolerance);
|
|
tolerance = tolerance || Math.pow(0.1, question.answerDecimalPoints);
|
|
answerHtml = answerHtml + " <span style='font-size: 0.8em;'>+/-</span> " + htmlEscape(tolerance);
|
|
$question.find(".answer_tolerance").text(tolerance);
|
|
}
|
|
$td.html(answerHtml);
|
|
$tr.append($td);
|
|
$question.find(".equation_combinations tbody").append($tr);
|
|
});
|
|
}
|
|
} else {
|
|
var $option = $(document.createElement('option'));
|
|
$option.val("").text(I18n.t('choose_option', "[ Choose ]"));
|
|
$select.append($option);
|
|
$.each(question.answers, function(i, data) {
|
|
data.answer_type = answer_type;
|
|
if (n_correct == "all") {
|
|
data.answer_weight = 100;
|
|
} else if (n_correct == "one" && hadOne) {
|
|
data.answer_weight = 0;
|
|
} else if (n_correct == "none") {
|
|
data.answer_weight = 0;
|
|
}
|
|
if (data.answer_weight > 0) { hadOne = true; }
|
|
var $displayAnswer = makeDisplayAnswer(data, escaped);
|
|
// must use > in selector
|
|
$question.find(".text > .answers").append($displayAnswer);
|
|
var $option = $(document.createElement("option"));
|
|
$option.val("option_" + i).text(data.answer_text);
|
|
$select.append($option);
|
|
});
|
|
}
|
|
|
|
$question.find(".blank_id_select").empty();
|
|
if (question.question_type == 'missing_word_question') {
|
|
var $text = $question.find(".question_text");
|
|
$text.html("<span class='text_before_answers'>" + $.raw(question.question_text) + "</span> ");
|
|
$text.append($select);
|
|
$text.append(" <span class='text_after_answers'>" + $.raw(question.text_after_answers) + "</span>");
|
|
} else if (question.question_type == 'multiple_dropdowns_question' || question.question_type == 'fill_in_multiple_blanks_question') {
|
|
var variables = {}
|
|
$.each(question.answers, function(i, data) {
|
|
variables[data.blank_id] = true;
|
|
});
|
|
$question.find(".blank_id_select").empty();
|
|
for(var idx in variables) {
|
|
var variable = idx;
|
|
if (variable && variables[idx]) {
|
|
var $option = $("<option/>");
|
|
$option.val(variable).text(variable);
|
|
$question.find(".blank_id_select").append($option);
|
|
}
|
|
}
|
|
}
|
|
$question.find(".after_answers").empty();
|
|
if (question.question_type == 'matching_question') {
|
|
var $text = $question.find(".after_answers");
|
|
var split = [];
|
|
if (question.matches && question.answers) {
|
|
var correct_ids = {};
|
|
for(var idx in question.answers) {
|
|
correct_ids[question.answers[idx].match_id] = true;
|
|
}
|
|
for(var idx in question.matches) {
|
|
if (!correct_ids[question.matches[idx].match_id]) {
|
|
split.push(question.matches[idx].text);
|
|
}
|
|
}
|
|
} else {
|
|
var split = (question.matching_answer_incorrect_matches || "");
|
|
if (typeof(split) == 'string') {
|
|
split = split.split("\n");
|
|
}
|
|
}
|
|
var codeHtml = "";
|
|
for(var cdx in split) {
|
|
if(split[cdx]) {
|
|
codeHtml = codeHtml + "<li>" + htmlEscape(split[cdx]) + "</li>";
|
|
}
|
|
}
|
|
|
|
if (codeHtml) {
|
|
$text.append(htmlEscape(I18n.beforeLabel(I18n.t('labels.other_incorrect_matches', "Other Incorrect Match Options"))) + "<ul class='matching_answer_incorrect_matches_list'>" + codeHtml + "</ul>");
|
|
}
|
|
}
|
|
$question.find(".blank_id_select").change();
|
|
$question.fillTemplateData({
|
|
question_type: question_type,
|
|
answer_selection_type: answer_type
|
|
});
|
|
|
|
$question.show();
|
|
var isNew = $question.attr('id') == "question_new";
|
|
if (isNew) {
|
|
if (question_type != "text_only_question") {
|
|
quiz.defaultQuestionData.question_type = question_type;
|
|
quiz.defaultQuestionData.answer_count = Math.min($question.find(".answers .answer").length, 4);
|
|
}
|
|
}
|
|
$("html,body").scrollTo({top: $question.offset().top - 10, left: 0});
|
|
$question.find(".question_points_holder").showIf(!$question.closest(".question_holder").hasClass('group') && question.question_type != 'text_only_question');
|
|
$question.find(".unsupported_question_type_message").remove();
|
|
quiz.updateDisplayComments();
|
|
if (question.id) {
|
|
var answers = question.answers;
|
|
|
|
$question.fillTemplateData({
|
|
data: {id: question.id},
|
|
id: 'question_' + question.id,
|
|
hrefValues: ['id']
|
|
});
|
|
|
|
$question.find(".original_question_text").fillFormData(question)
|
|
quiz.updateDisplayComments();
|
|
|
|
// We have to do the operations below to solve the problem of answer ids being overwritten by fillTemplateData
|
|
if (answers) {
|
|
$question.find('.answers .answer .hidden.id').each(function(index, answerEl){
|
|
$(answerEl).text(answers[index].id);
|
|
});
|
|
|
|
$question.find('.answers .answer .id:not(.hidden)').each(function(index, answerEl){
|
|
$(answerEl).text(answers[index].id);
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
// Updates the question's form when the type changes
|
|
updateFormQuestion: function($form) {
|
|
var $formQuestion = $form.find(".question");
|
|
var question_type = $formQuestion.find(":input[name='question_type']").val();
|
|
var result = {};
|
|
result.answer_type = "select_answer";
|
|
result.answer_selection_type = quiz.answerSelectionType(question_type);
|
|
result.textValues = ['answer_weight', 'answer_text', 'answer_comment', 'blank_id', 'id', 'match_id'];
|
|
result.htmlValues = ['answer_html', 'answer_match_left_html', 'answer_comment_html'];
|
|
result.question_type = question_type;
|
|
|
|
var is_survey_quiz = $("#questions").hasClass('survey_quiz');
|
|
if (is_survey_quiz && $formQuestion.find("." + question_type + "_survey_quiz_explanation").length > 0) {
|
|
$formQuestion.find(".explanation").hide().filter("." + question_type + "_survey_quiz_explanation").show();
|
|
} else {
|
|
$formQuestion.find(".explanation").hide().filter("." + question_type + "_explanation").show();
|
|
}
|
|
|
|
$formQuestion.attr('class', 'question').addClass('selectable');
|
|
$formQuestion.find(".missing_word_after_answer").hide().end()
|
|
.find(".matching_answer_incorrect_matches_holder").hide().end()
|
|
.find(".question_comment").css('display', '').end();
|
|
if (is_survey_quiz) {
|
|
$formQuestion.find(".question_comment").css('display', 'none').end()
|
|
.find(".question_neutral_comment").css('display', '');
|
|
}
|
|
$formQuestion.find(".question_header").text(I18n.t('question_colon', "Question:"));
|
|
$formQuestion.addClass(question_type);
|
|
$formQuestion.find(".question_points_holder").showIf(!$formQuestion.closest(".question_holder").hasClass('group') && question_type != 'text_only_question');
|
|
|
|
var options = {
|
|
addable: true
|
|
};
|
|
|
|
limitTextInputFor($formQuestion, question_type);
|
|
|
|
if (question_type == 'multiple_choice_question') {
|
|
} else if (question_type == 'true_false_question') {
|
|
options.addable = false;
|
|
var $answers = $formQuestion.find(".form_answers .answer");
|
|
if ($answers.length < 2) {
|
|
for(var i = 0; i < 2 - $answers.length; i++) {
|
|
var $answer = makeFormAnswer({ answer_type: "fixed_answer", question_type: "true_false_question" });
|
|
$formQuestion.find(".form_answers").append($answer);
|
|
}
|
|
} else if ($answers.length > 2) {
|
|
for(var i = 2; i < $answers.length; i++) {
|
|
$answers.eq(i).remove();
|
|
}
|
|
}
|
|
var answerOptions = {
|
|
question_type: "true_false_question",
|
|
answer_type: "fixed_answer",
|
|
answer_text: I18n.t('true', "True")
|
|
};
|
|
quiz.updateFormAnswer($formQuestion.find(".answer:first"), answerOptions);
|
|
answerOptions.answer_text = I18n.t('false', "False");
|
|
quiz.updateFormAnswer($formQuestion.find(".answer:last"), answerOptions);
|
|
result.answer_type = "fixed_answer";
|
|
} else if (question_type == 'short_answer_question') {
|
|
$formQuestion.removeClass('selectable');
|
|
result.answer_type = "short_answer";
|
|
} else if (question_type == 'essay_question' || question_type == 'file_upload_question') {
|
|
$formQuestion.find(".answer").remove();
|
|
$formQuestion.removeClass('selectable');
|
|
$formQuestion.find(".answers_header").hide().end()
|
|
.find(".question_comment").css('display', 'none').end()
|
|
.find(".question_neutral_comment").css('display', '').end();
|
|
options.addable = false;
|
|
result.answer_type = "none";
|
|
result.textValues = [];
|
|
result.htmlValues = [];
|
|
} else if (question_type == 'matching_question') {
|
|
$formQuestion.removeClass('selectable');
|
|
$form.find(".matching_answer_incorrect_matches_holder").show();
|
|
result.answer_type = "matching_answer";
|
|
result.textValues = ['answer_match_left', 'answer_match_right', 'answer_comment'];
|
|
} else if (question_type == 'missing_word_question') {
|
|
$form.find(".missing_word_after_answer").show();
|
|
$form.find(".question_header").text("Text to go before answers:");
|
|
result.answer_type = "select_answer";
|
|
} else if (question_type == 'numerical_question') {
|
|
$formQuestion.removeClass('selectable');
|
|
result.answer_type = "numerical_answer";
|
|
result.textValues = ['numerical_answer_type', 'answer_exact', 'answer_error_margin', 'answer_range_start', 'answer_range_end', 'answer_approximate', 'answer_precision'];
|
|
result.html_values = [];
|
|
} else if (question_type == 'calculated_question') {
|
|
$formQuestion.removeClass('selectable');
|
|
result.answer_type = "numerical_answer";
|
|
result.textValues = ['answer_combinations'];
|
|
result.html_values = [];
|
|
$formQuestion.formulaQuestion();
|
|
} else if (question_type == 'multiple_dropdowns_question') {
|
|
result.answer_type = "select_answer";
|
|
$formQuestion.multipleAnswerSetsQuestion();
|
|
} else if (question_type == 'fill_in_multiple_blanks_question') {
|
|
result.answer_type = "short_answer";
|
|
$formQuestion.multipleAnswerSetsQuestion();
|
|
} else if (question_type == 'multiple_answers_question') {
|
|
} else if (question_type == "text_only_question") {
|
|
options.addable = false;
|
|
$formQuestion.find(".answer").remove();
|
|
$formQuestion.removeClass('selectable');
|
|
$formQuestion.find(".answers_header").hide().end()
|
|
.find(".question_comment").css('display', 'none');
|
|
$formQuestion.find(".question_header").text(I18n.beforeLabel(I18n.t('labels.message_text', "Message Text")));
|
|
$form.find(":input[name='question_points']").val(0);
|
|
result.answer_type = "none";
|
|
result.textValues = [];
|
|
result.htmlValues = [];
|
|
}
|
|
$formQuestion.find(".answer.hidden").remove();
|
|
$form.find("input[name='answer_selection_type']").val(result.answer_selection_type).change();
|
|
$form.find(".add_answer_link").showIf(options.addable);
|
|
var $answers = $formQuestion.find(".form_answers .answer");
|
|
if ($answers.length === 0 && result.answer_type != "none") {
|
|
$formQuestion.find(".form_answers").append(makeFormAnswer({answer_type: result.answer_type, question_type: question_type}));
|
|
$formQuestion.find(".form_answers").append(makeFormAnswer({answer_type: result.answer_type, question_type: question_type}));
|
|
$answers = $formQuestion.find(".form_answers .answer");
|
|
}
|
|
if (result.answer_selection_type == "any_answer") {
|
|
if (question_type == "short_answer_question") {
|
|
$answers.addClass('fill_in_blank_answer');
|
|
}
|
|
$answers.addClass('correct_answer');
|
|
} else if (result.answer_selection_type == "matching") {
|
|
$answers.removeClass('correct_answer');
|
|
} else if (result.answer_selection_type != "multiple_answer") {
|
|
$answers.filter(".correct_answer").not(":first").removeClass('correct_answer');
|
|
if ($answers.filter(".correct_answer").length === 0) {
|
|
$answers.filter(":first").addClass('correct_answer');
|
|
}
|
|
}
|
|
$form.find(".answer").each(function() {
|
|
var weight = 0;
|
|
if ($(this).hasClass('correct_answer')) {
|
|
weight = 100;
|
|
}
|
|
$(this).find(".answer_weight").text(weight);
|
|
quiz.updateFormAnswer($(this), result);
|
|
});
|
|
$form.find(".answer_type").hide().filter("." + result.answer_type).show();
|
|
return result;
|
|
},
|
|
|
|
calculatePointsPossible: function() {
|
|
var tally = 0;
|
|
$("#questions .question_holder:not(.group) .question:not(#question_new)").each(function() {
|
|
var val = parseFloat($(this).find(".question_points,.question_points.hidden").text());
|
|
if (isNaN(val) || val < 0) { val = 0; }
|
|
tally += val;
|
|
});
|
|
$("#questions .group_top:not(#group_top_new)").each(function(){
|
|
var val = parseFloat($(this).find(".question_points").text());
|
|
if (isNaN(val) || val < 0) { val = 0; }
|
|
var pickCount = $(this).find(".pick_count").text() || 0;
|
|
if (isNaN(pickCount)) { pickCount = 0; }
|
|
var groupId = this.id.replace("group_top_","")
|
|
var questionCount = $(this.parentElement).find("[data-group-id='" + groupId + "']").length
|
|
|
|
// unless it is a question bank, make sure
|
|
// enough questions are in the group
|
|
if(!$(this).hasClass("question_bank_top")){
|
|
pickCount = Math.min(pickCount, questionCount)
|
|
}
|
|
|
|
tally += val * pickCount;
|
|
});
|
|
tally = Math.round(tally * 100.0) / 100.0;
|
|
return tally;
|
|
},
|
|
|
|
updateDisplayComments: function() {
|
|
this.checkShowDetails();
|
|
$(".question_holder > .question > .question_comment").each(function() {
|
|
var val = $.trim($(this).find(".question_comment_text").html());
|
|
$(this).css('display', '').toggleClass('empty', !val);
|
|
});
|
|
$(".question_holder .answer_comment_holder").each(function() {
|
|
var val = $.trim($(this).find(".answer_comment").html());
|
|
$(this).css('display', '').toggleClass('empty', !val);
|
|
});
|
|
$("#questions .group_top:not(#group_top_new)").each(function(){
|
|
var pickCount = $(this).find(".pick_count").text() || 0;
|
|
if (isNaN(pickCount)) { pickCount = 0; }
|
|
var groupId = this.id.replace("group_top_","")
|
|
var questionCount = $(this.parentElement).find("[data-group-id='" + groupId + "']").length
|
|
|
|
var $warning = $('#insufficient_count_warning_' + groupId);
|
|
if (pickCount > ($(this).data('bank_question_count') || questionCount)) {
|
|
$warning.show();
|
|
} else {
|
|
$warning.hide();
|
|
}
|
|
});
|
|
$(".points_possible").text(this.calculatePointsPossible());
|
|
},
|
|
|
|
findContainerGroup: function($obj) {
|
|
$obj = $obj.prev();
|
|
while($obj.length > 0) {
|
|
if ($obj.hasClass('group_top')) {
|
|
return $obj;
|
|
} else if ($obj.hasClass('group_bottom')) {
|
|
return null;
|
|
}
|
|
$obj = $obj.prev();
|
|
}
|
|
return null;
|
|
},
|
|
|
|
parseInput: function($input, type) {
|
|
if ($input.val() == "") { return; }
|
|
|
|
var val = $input.val().replace(/,/g, '');
|
|
|
|
if (type == "int") {
|
|
val = parseInt(val, 10);
|
|
if (isNaN(val)) { val = 0; }
|
|
} else if (type == "float") {
|
|
val = Math.round(parseFloat(val) * 100.0) / 100.0;
|
|
if (isNaN(val)) { val = 0.0; }
|
|
} else if (type == "float_long") {
|
|
val = Math.round(parseFloat(val) * 10000.0) / 10000.0;
|
|
if (isNaN(val)) { val = 0.0; }
|
|
} else if (type == "precision") {
|
|
// Parse value and force NaN to 0
|
|
val = parseFloat(val)
|
|
if (isNaN(val)) { val = 0.0; }
|
|
|
|
// Round to precision 16 to handle floating point error
|
|
val = val.toPrecision(16);
|
|
|
|
// Truncate to specified precision
|
|
var precision = arguments[2] || 10;
|
|
if (precision < 16) {
|
|
var precision_shift = Math.pow(10, precision - Math.floor(Math.log(val) / Math.LN10) - 1);
|
|
val = Math.floor(val * precision_shift) / precision_shift;
|
|
|
|
// Format
|
|
val = val.toPrecision(precision)
|
|
}
|
|
}
|
|
|
|
$input.val(val);
|
|
},
|
|
|
|
/*****
|
|
Wrap parseInput with inforced range.
|
|
|
|
Value of $input must be between min and max inclusive, if value falls out
|
|
of the range it will be replaced with min or max respectively
|
|
*****/
|
|
parseInputRange: function($input, type, min, max) {
|
|
this.parseInput($input, type);
|
|
var val = $input.val();
|
|
if (val < min) {
|
|
$input.val(min);
|
|
}
|
|
if (val > max) {
|
|
$input.val(max);
|
|
}
|
|
},
|
|
|
|
validateAnswerTolerance: function($input) {
|
|
var val = $input.val();
|
|
if (val == "") { return; }
|
|
$input.val( parseFloatOrPercentage(val) );
|
|
},
|
|
|
|
defaultQuestionData: {
|
|
question_type: "multiple_choice_question",
|
|
question_text: "",
|
|
question_points: 1,
|
|
question_name: I18n.t('default_question_name', "Question"),
|
|
answer_count: 4
|
|
},
|
|
|
|
defaultAnswerData: {
|
|
answer_type: "select_answer",
|
|
answer_comment: "",
|
|
answer_weight: 0,
|
|
numerical_answer_type: "exact_answer",
|
|
answer_exact: "",
|
|
answer_error_margin: "",
|
|
answer_range_start: "",
|
|
answer_range_end: "",
|
|
answer_approximate: "",
|
|
answer_precision: "10"
|
|
}
|
|
};
|
|
|
|
scoreValidation = {
|
|
init: function() {
|
|
this.initValidators.apply(this);
|
|
|
|
$('#quiz_options_form').on('xhrError', this.onFormError);
|
|
},
|
|
|
|
initValidators: function() {
|
|
$('input#quiz_points_possible')
|
|
.on('invalid:not_a_number', function(e) {
|
|
$(this).errorBox(I18n.t('errors.quiz_score_not_a_number', 'Score must be a number between 0 and 2,000,000,000.'));
|
|
})
|
|
.on('invalid:greater_than', function(e) {
|
|
$(this).errorBox(I18n.t('errors.quiz_score_too_short', 'Score must be greater than 0.'));
|
|
})
|
|
.on('invalid:less_than', function(e) {
|
|
$(this).errorBox(I18n.t('errors.quiz_score_too_long', 'Score must be less than 2,000,000,000.'));
|
|
});
|
|
$("input#quiz_points_possible").change(this.validatePoints);
|
|
},
|
|
|
|
validatePoints: function() {
|
|
var value = $("input#quiz_points_possible").val();
|
|
var numVal = parseInt(value);
|
|
|
|
if (value && isNaN(numVal)) {
|
|
$("input#quiz_points_possible").trigger("invalid:not_a_number");
|
|
// valid = false;
|
|
} else if (numVal > 2000000000) {
|
|
$("input#quiz_points_possible").trigger("invalid:less_than");
|
|
// valid = false;
|
|
} else if (numVal < 0) {
|
|
$("input#quiz_points_possible").trigger("invalid:greater_than");
|
|
// valid = false;
|
|
}
|
|
},
|
|
|
|
// Delegate the handling of "points_possible" errors
|
|
onFormError: function(e, resp) {
|
|
if (resp && resp.points_possible) {
|
|
$("input#quiz_points_possible").triggerHandler("invalid:not_a_number");
|
|
|
|
// Prevent $.fn.formErrors from giving error box with cryptic message.
|
|
delete resp.points_possible;
|
|
}
|
|
}
|
|
}
|
|
|
|
var ipFilterValidation = {
|
|
init: function() {
|
|
this.initValidators.apply(this);
|
|
$('#quiz_options_form').on('xhrError', this.onFormError);
|
|
},
|
|
|
|
initValidators: function() {
|
|
$('#quiz_ip_filter').on('invalid:ip_filter', function(e) {
|
|
$(this).errorBox(I18n.t('IP filter is not valid.'));
|
|
});
|
|
},
|
|
|
|
onFormError: function(e, resp) {
|
|
if (resp && resp.invalid_ip_filter) {
|
|
var event = 'invalid:ip_filter'
|
|
$("#quiz_ip_filter").triggerHandler(event);
|
|
}
|
|
// Prevent $.fn.formErrors from giving error box with cryptic message.
|
|
delete resp.invalid_ip_filter;
|
|
}
|
|
}
|
|
|
|
|
|
correctAnswerVisibility = {
|
|
$toggler: $(),
|
|
$options: $(),
|
|
$pickers: $(),
|
|
|
|
/**
|
|
* Build date pickers, and install handlers for show_correct_answers stuff when:
|
|
* - form is being serialized
|
|
* - form errors are thrown
|
|
* - related date fields change
|
|
*/
|
|
init: function() {
|
|
var that = correctAnswerVisibility;
|
|
var $toggler = that.$toggler = $('#quiz_show_correct_answers');
|
|
var $options = that.$options = $('#quiz_show_correct_answers_options');
|
|
var $pickers = that.$pickers = $options.find('.date_field');
|
|
|
|
$pickers.each(function() {
|
|
var $field = $(this);
|
|
var formattedDate = Handlebars.helpers.datetimeFormatted($field.val() || '');
|
|
|
|
$field.val(formattedDate);
|
|
$field.datetime_field();
|
|
});
|
|
|
|
$('#quiz_options_form')
|
|
.on('xhrError', that.onFormError)
|
|
.on('serializing', that.serialize);
|
|
|
|
that.installValidators();
|
|
},
|
|
|
|
isOn: function() {
|
|
return correctAnswerVisibility.$toggler.prop('checked');
|
|
},
|
|
|
|
/**
|
|
* Install handlers for validating show_correct_answers (and related fields)
|
|
* values, as well as error handlers for bad values that show friendly
|
|
* error boxes.
|
|
*/
|
|
installValidators: function() {
|
|
var that = correctAnswerVisibility;
|
|
|
|
that.$toggler.on('invalid:bad_range', function() {
|
|
$('#quiz_hide_correct_answers_at').errorBox(
|
|
I18n.t('errors.invalid_show_correct_answers_range',
|
|
'Hide date cannot be before show date.'));
|
|
|
|
return true;
|
|
});
|
|
|
|
that.$pickers.on('change', that.validateRange);
|
|
},
|
|
|
|
/**
|
|
* Ensure that if both show_at and hide_at dates are specified, they form
|
|
* a valid time range.
|
|
*
|
|
* @fires invalid:bad_range
|
|
* @delegate #quiz_show_correct_answers
|
|
*/
|
|
validateRange: function() {
|
|
var that = correctAnswerVisibility;
|
|
|
|
var $hide_at = that.$options.find('#quiz_hide_correct_answers_at');
|
|
var $show_at = that.$options.find('#quiz_show_correct_answers_at');
|
|
|
|
// Clear any existing error boxes
|
|
that.$pickers.each(function() {
|
|
var $errorBox = $(this).data('associated_error_box');
|
|
|
|
if ($errorBox) {
|
|
$errorBox.remove();
|
|
}
|
|
});
|
|
|
|
if ($show_at.val().length && $hide_at.val().length) {
|
|
if ($show_at.data().date >= $hide_at.data().date) {
|
|
that.$toggler.triggerHandler('invalid:bad_range');
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handle remote show_correct_answers errors by triggering the corresponding
|
|
* events so the error handlers can pick them up.
|
|
*
|
|
* Side-effects:
|
|
*
|
|
* - `show_correct_answers` key will be deleted from the XHR response to prevent
|
|
* $.fn.formErrors from handling it.
|
|
*
|
|
* @param {jQuery} e XHR event.
|
|
* @param {Object} resp XHR response.
|
|
*
|
|
* @fires invalid:bad_range
|
|
* @delegate #quiz_show_correct_answers
|
|
*/
|
|
onFormError: function(e, resp) {
|
|
var that = correctAnswerVisibility;
|
|
var i, errorEntry, event;
|
|
|
|
// Delegate the handling of "show_correct_answers" errors to the handlers bound
|
|
// to #quiz_show_correct_answers.
|
|
if (resp && resp.show_correct_answers) {
|
|
for (i = 0; i < resp.show_correct_answers.length; ++i) {
|
|
errorEntry = resp.show_correct_answers[i];
|
|
event = 'invalid:' + errorEntry.type.toLowerCase().replace(/\s/, '_');
|
|
|
|
that.$toggler.triggerHandler(event);
|
|
}
|
|
|
|
// Prevent $.fn.formErrors from generating an error box with the API's
|
|
// cryptic error message.
|
|
delete resp.show_correct_answers;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Serialize the dates set for the visibility duration, if specified and
|
|
* `hide_results` is enabled. If that's not enabled, nullify the values.
|
|
*
|
|
* @param {jQuery} e A jQuery event
|
|
* @param {Object} data The form/XHR data.
|
|
*/
|
|
serialize: function(e, data) {
|
|
var showResults, showCorrectAnswers, showResultsOnce;
|
|
var resetField = function(key, value) {
|
|
data['quiz[' + key + ']'] = value || '';
|
|
};
|
|
var serializeField = function(key) {
|
|
var $field = $('#quiz_' + key);
|
|
var date;
|
|
|
|
if ($field.val().length) {
|
|
date = $field.data().date;
|
|
data['quiz[' + key + ']'] = $.unfudgeDateForProfileTimezone(date).toISOString();
|
|
} else {
|
|
resetField(key);
|
|
}
|
|
};
|
|
|
|
showResults = data['quiz[hide_results][never]'] != '0';
|
|
showCorrectAnswers = data['quiz[show_correct_answers]'] == '1';
|
|
showResultsOnce = data['quiz[one_time_results]'] == '1';
|
|
|
|
if (showResults && showCorrectAnswers) {
|
|
serializeField('show_correct_answers_at');
|
|
serializeField('hide_correct_answers_at');
|
|
} else {
|
|
resetField('show_correct_answers', '0');
|
|
}
|
|
|
|
// Discard the showAt / hideAt dates if we're showing results only once,
|
|
// or not at all:
|
|
if (!showResults || !showCorrectAnswers || showResultsOnce) {
|
|
resetField('show_correct_answers_at');
|
|
resetField('hide_correct_answers_at');
|
|
}
|
|
|
|
// One time results doesn't apply if results are hidden in the first
|
|
// place:
|
|
if (!showResults) {
|
|
resetField('one_time_results', '0');
|
|
}
|
|
},
|
|
|
|
disable: function() {
|
|
var that = correctAnswerVisibility;
|
|
|
|
that.$toggler.prop('checked', false);
|
|
that.showDatePickers(false);
|
|
},
|
|
|
|
// Totally show or hide the showAt/hideAt date pickers
|
|
showDatePickers: function(isVisible) {
|
|
correctAnswerVisibility.$options.toggle(isVisible);
|
|
},
|
|
|
|
// Keep the pickers visible, but enable or disable them
|
|
enableDatePickers: function(isEnabled) {
|
|
correctAnswerVisibility.$options
|
|
.find('input.date_field, button.date_field')
|
|
.prop('disabled', !isEnabled);
|
|
}
|
|
};
|
|
|
|
function makeQuestion(data) {
|
|
var idx = $(".question_holder:visible").length + 1;
|
|
var question = $.extend({}, quiz.defaultQuestionData, {question_name: I18n.t('default_quesiton_name', "Question")}, data);
|
|
var $question = $("#question_template").clone(true);
|
|
$question.attr('id', '').find('.question').attr('id', 'question_new');
|
|
$question.fillTemplateData({ data: question, except: ['answers'] });
|
|
$question.find(".original_question_text").fillFormData(question);
|
|
if (question.answers) {
|
|
question.answer_count = question.answers.length;
|
|
data.answer_type = question.answer_type;
|
|
question.anwer_type = quiz.answerTypeDetails(question.question_type);
|
|
}
|
|
for(var i = 0; i < question.answer_count; i++) {
|
|
var weight = i == 0 ? 100 : 0;
|
|
var answer = { answer_weight: weight };
|
|
if (question.answers && question.answers[i]) {
|
|
$.extend(answer, question.answers[i]);
|
|
}
|
|
$question.find(".answers").append(makeDisplayAnswer(answer));
|
|
}
|
|
$question.toggleClass('group', !!(data && data.quiz_group_id));
|
|
$question.show();
|
|
return $question;
|
|
}
|
|
|
|
function makeDisplayAnswer(data, escaped) {
|
|
data.answer_weight = data.weight || data.answer_weight;
|
|
data.answer_comment = data.comments || data.answer_comment;
|
|
data.answer_text = data.text || data.answer_text;
|
|
data.answer_html = data.html || data.answer_html;
|
|
data.answer_comment_html = data.comments_html || data.answer_comment_html;
|
|
data.answer_match_left = data.left || data.answer_match_left;
|
|
data.answer_match_left_html = data.left_html || data.answer_match_left_html;
|
|
data.answer_match_right = data.right || data.answer_match_right;
|
|
data.answer_exact = data.exact === undefined ? data.answer_exact : data.exact;
|
|
data.answer_error_margin = data.answer_error_margin || data.margin;
|
|
data.answer_range_start = data.start || data.answer_range_start;
|
|
data.answer_range_end = data.end || data.answer_range_end;
|
|
data.answer_approximate = data.approximate || data.answer_approximate;
|
|
data.answer_precision = data.precision || data.answer_precision;
|
|
|
|
var answer = $.extend({}, quiz.defaultAnswerData, data);
|
|
var $answer = $("#answer_template").clone(true).attr('id', '');
|
|
var answer_class = answer.answer_type;
|
|
if (answer_class == "numerical_answer") {
|
|
answer_class = "numerical_" + answer.numerical_answer_type;
|
|
}
|
|
$answer.addClass('answer_for_' + data.blank_id);
|
|
$answer.find(".answer_type").hide().filter("." + answer_class).show();
|
|
$answer.find('div.answer_text').showIf(!data.answer_html);
|
|
$answer.find('div.answer_match_left').showIf(!data.answer_match_left_html);
|
|
$answer.find('div.answer_match_left_html').showIf(data.answer_match_left_html);
|
|
delete answer['answer_type'];
|
|
answer.answer_weight = parseFloat(answer.answer_weight);
|
|
if (isNaN(answer.answer_weight)) { answer.answer_weight = 0; }
|
|
|
|
$answer.fillFormData({answer_text: answer.answer_text});
|
|
$answer.fillTemplateData({data: answer, htmlValues: ['answer_html', 'answer_match_left_html', 'answer_comment_html']});
|
|
|
|
if (!answer.answer_comment || answer.answer_comment == "" || answer.answer_comment == I18n.t('answer_comments', "Answer comments")) {
|
|
$answer.find(".answer_comment_holder").hide();
|
|
}
|
|
|
|
if (answer.answer_weight == 100) {
|
|
$answer.addClass('correct_answer');
|
|
} else if (answer.answer_weight > 0) {
|
|
$answer.addClass('correct_answer');
|
|
} else if (answer.answer_weight < 0) {
|
|
$answer.addClass('negative_answer');
|
|
}
|
|
|
|
$answer.show();
|
|
return $answer;
|
|
}
|
|
|
|
function makeFormAnswer(data) {
|
|
var answer = $.extend({}, quiz.defaultAnswerData, data);
|
|
var $answer = $("#form_answer_template").clone(true).attr('id', '');
|
|
$answer.find(".answer_type").hide().filter("." + answer.answer_type).show();
|
|
answer.answer_weight = parseFloat(answer.answer_weight);
|
|
|
|
if (isNaN(answer.answer_weight)) { answer.answer_weight = 0; }
|
|
quiz.updateFormAnswer($answer, answer, true);
|
|
$answer.show();
|
|
return $answer;
|
|
}
|
|
|
|
var REGRADE_DATA = {};
|
|
var REGRADE_OPTIONS = ENV.REGRADE_OPTIONS || {};
|
|
|
|
function quizData($question) {
|
|
var $quiz = $("#questions");
|
|
var quiz = {
|
|
questions: [],
|
|
points_possible: 0
|
|
};
|
|
var $list = $quiz.find(".question");
|
|
if ($question) { $list = $question; }
|
|
$list.each(function(i) {
|
|
var $question = $(this);
|
|
var questionData = $question.getTemplateData({
|
|
textValues: ['question_name', 'question_points', 'question_type', 'answer_selection_type', 'assessment_question_id', 'correct_comments', 'incorrect_comments', 'neutral_comments', 'matching_answer_incorrect_matches', 'equation_combinations', 'equation_formulas', 'regrade_option'],
|
|
htmlValues: ['question_text', 'text_before_answers', 'text_after_answers', 'correct_comments_html', 'incorrect_comments_html', 'neutral_comments_html']
|
|
});
|
|
questionData = $.extend(questionData, $question.find(".original_question_text").getFormData());
|
|
questionData.assessment_question_bank_id = $(".question_bank_id").text() || "";
|
|
if (questionData.text_before_answers) {
|
|
questionData.question_text = questionData.text_before_answers;
|
|
}
|
|
var matches = [];
|
|
$question.find(".matching_answer_incorrect_matches_list li").each(function() {
|
|
matches.push($(this).text());
|
|
});
|
|
questionData.matching_answer_incorrect_matches = matches.join("\n");
|
|
var question = $.extend({}, quiz.defaultQuestionData, questionData);
|
|
question.answers = [];
|
|
var blank_ids_hash = {};
|
|
var only_add_for_blank_ids = false;
|
|
if (question.question_type == "multiple_dropdowns_question" || question.question_type == "fill_in_multiple_blanks_question") {
|
|
only_add_for_blank_ids = true;
|
|
$question.find(".blank_id_select option").each(function() {
|
|
blank_ids_hash[$(this).text()] = true;
|
|
});
|
|
}
|
|
if (question.question_type != 'calculated_question') {
|
|
// must use > in selector
|
|
$question.find(".text > .answers .answer").each(function() {
|
|
var $answer = $(this);
|
|
var answerData = $answer.getTemplateData({
|
|
textValues: ['answer_exact', 'answer_error_margin', 'answer_range_start', 'answer_range_end', 'answer_approximate', 'answer_precision', 'answer_weight', 'numerical_answer_type', 'blank_id', 'id', 'match_id', 'answer_text', 'answer_match_left', 'answer_match_right', 'answer_comment'],
|
|
htmlValues: ['answer_html', 'answer_match_left_html', 'answer_comment_html']
|
|
});
|
|
var answer = $.extend({}, quiz.defaultAnswerData, answerData);
|
|
if (only_add_for_blank_ids && answer.blank_id && !blank_ids_hash[answer.blank_id]) {
|
|
return;
|
|
}
|
|
question.answers.push(answer);
|
|
});
|
|
} else {
|
|
question.formulas = [];
|
|
$question.find(".formulas_holder .formulas_list > div").each(function() {
|
|
question.formulas.push($.trim($(this).text()));
|
|
});
|
|
question.variables = [];
|
|
$question.find(".variable_definitions_holder .variable_definitions tbody tr").each(function() {
|
|
var data = $(this).getTemplateData({textValues: ['name', 'min', 'max', 'scale']});
|
|
question.variables.push(data);
|
|
});
|
|
question.answers = [];
|
|
$question.find(".equation_combinations tbody tr").each(function() {
|
|
var data = {};
|
|
data.variables = [];
|
|
$(this).find("td:not(.final_answer)").each(function(i) {
|
|
var variable = {};
|
|
variable.name = question.variables[i].name;
|
|
variable.value = parseFloat($(this).text(), 10) || 0;
|
|
data.variables.push(variable);
|
|
});
|
|
data.answer_text = parseFloat($(this).find(".final_answer").text(), 10) || 0;
|
|
question.answers.push(data);
|
|
});
|
|
question.formula_decimal_places = parseInt($question.find(".formula_decimal_places").text(), 10) || 0;
|
|
question.answer_tolerance = parseFloatOrPercentage($question.find(".answer_tolerance").text(), 10) || 0;
|
|
}
|
|
question.position = i;
|
|
question.question_points = parseFloat(question.question_points);
|
|
if (isNaN(question.question_points) || question.question_points < 0) {
|
|
question.question_points = 0;
|
|
}
|
|
quiz.points_possible += question.question_points;
|
|
quiz.questions.push(question);
|
|
});
|
|
return quiz;
|
|
}
|
|
|
|
function generateFormQuizQuestion(formQuiz) {
|
|
var data = {};
|
|
var quiz = formQuiz;
|
|
if(quiz.questions.length > 0) {
|
|
data["question"] = quiz.questions[0];
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
function generateFormQuiz(quiz) {
|
|
var data = {
|
|
quiz: {},
|
|
questions: []
|
|
};
|
|
|
|
if (ENV.ASSIGNMENT_ID) {
|
|
data.quiz.assignment_id = ENV.ASSIGNMENT_ID;
|
|
}
|
|
|
|
data.quiz.title = quiz.quiz_name;
|
|
quiz.questions.forEach(function(question) {
|
|
var q = {};
|
|
q.question_name = question.question_name;
|
|
q.assessment_question_id = question.assessment_question_id;
|
|
q.question_type = question.question_type;
|
|
q.points_possible = question.question_points;
|
|
q.correct_comments_html = question.correct_comments_html;
|
|
q.incorrect_comments_html = question.incorrect_comments_html;
|
|
q.neutral_comments_html = question.neutral_comments_html;
|
|
q.question_text = question.question_text;
|
|
q.regrade_option = question.regrade_option;
|
|
q.position = question.position;
|
|
q.text_after_answers = question.text_after_answers;
|
|
q.matching_answer_incorrect_matches = question.matching_answer_incorrect_matches;
|
|
q.formulas = question.formulas;
|
|
q.variables = question.variables;
|
|
q.answer_tolerance = question.answer_tolerance;
|
|
q.formula_decimal_places = question.formula_decimal_places;
|
|
|
|
q.answers = question.answers;
|
|
data.questions.push(q);
|
|
});
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function addHTMLFeedback($container, question_data, name) {
|
|
var html = question_data[name+'_html'];
|
|
if (!html || html.length == 0) {
|
|
html = htmlEscape(question_data[name]);
|
|
question_data[name+'_html'] = html;
|
|
}
|
|
if (html && html.length > 0) {
|
|
$container.find('.'+name+'_html').html(html);
|
|
$container.find('input[type="hidden"]').val(html);
|
|
$container.removeClass('empty');
|
|
}
|
|
}
|
|
|
|
var QUESTION_LIMIT = 1000;
|
|
function questionLimitReached(inclusiveLimit) {
|
|
if (!ENV.QUIZZES || !ENV.QUIZZES.QUIZ) return false; // quizzes have a limit, question banks do not
|
|
|
|
var numQuestions = $('#questions .question_holder').not('.group').length;
|
|
$('#questions .group_top').find('input[name="quiz_group[pick_count]"]').each(function() {
|
|
numQuestions += parseInt(this.value);
|
|
});
|
|
if (inclusiveLimit ? (numQuestions > QUESTION_LIMIT) : (numQuestions >= QUESTION_LIMIT)) {
|
|
setTimeout(function() {
|
|
alert(I18n.t('question_limit_reached', 'You have reached the maximum number of questions allowed for a quiz (%{count}/%{limit}).\n\nAs a workaround, consider spreading the material across multiple quizzes.', {count: numQuestions, limit: QUESTION_LIMIT}))
|
|
});
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function limitTextInputFor(form, question_type) {
|
|
// Add character limit class
|
|
if (question_type == 'missing_word_question' ||
|
|
question_type == 'fill_in_multiple_blanks_question' ||
|
|
question_type == 'short_answer_question') {
|
|
form.find("input[name='answer_text']").addClass("limit_text");
|
|
} else {
|
|
form.find("input[name='answer_text']").removeClass("limit_text");
|
|
}
|
|
}
|
|
|
|
function parseFloatOrPercentage(val) {
|
|
if (val == "") { return val; }
|
|
var result;
|
|
|
|
// percentage value
|
|
if ((val + "").indexOf('%') === val.length - 1) {
|
|
var number = val.replace("%", "");
|
|
result = (Math.round(parseFloat(number) * 10000.0) / 10000.0) + "%";
|
|
|
|
// point value
|
|
} else if (!isNaN(val)) {
|
|
result = Math.round(parseFloat(val) * 10000.0) / 10000.0;
|
|
if (isNaN(result)) { result = 0.0; }
|
|
|
|
} else {
|
|
result = 0.0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function toggleConditionalReleaseTab(quizType) {
|
|
if (ENV['CONDITIONAL_RELEASE_SERVICE_ENABLED']) {
|
|
if (quizType == "assignment") {
|
|
$('#quiz_tabs').tabs("option", "disabled", false);
|
|
} else {
|
|
$('#quiz_tabs').tabs("option", "disabled", [2]);
|
|
$('#quiz_tabs').tabs("option", "active", 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
quiz.init().updateDisplayComments();
|
|
correctAnswerVisibility.init();
|
|
scoreValidation.init();
|
|
ipFilterValidation.init();
|
|
renderDueDates();
|
|
|
|
$('#quiz_tabs').tabs();
|
|
$('#editor_tabs').show();
|
|
|
|
var $quiz_options_form = $("#quiz_options_form");
|
|
var $quiz_edit_wrapper = $("#quiz_edit_wrapper");
|
|
$(".datetime_field").datetime_field();
|
|
$("#questions").delegate('.group_top,.question,.answer_select,.comment', 'mouseover', function(event) {
|
|
$(this).addClass('hover');
|
|
}).delegate('.group_top,.question,.answer_select,.comment', 'mouseout', function(event) {
|
|
$(this).removeClass('hover');
|
|
});
|
|
|
|
$("#questions").delegate('.answer', 'mouseover', function(event) {
|
|
$("#questions .answer.hover").removeClass('hover');
|
|
$(this).addClass('hover');
|
|
});
|
|
|
|
$quiz_options_form.find("#extend_due_at").change(function() {
|
|
$("#quiz_lock_after").showIf($(this).attr('checked'));
|
|
}).change();
|
|
|
|
$quiz_options_form.find("#multiple_attempts_option").change(function(event) {
|
|
$("#multiple_attempts_suboptions").showIf($(this).attr('checked'));
|
|
var $text = $("#multiple_attempts_suboptions #quiz_allowed_attempts");
|
|
|
|
if ($text.val() == '-1') {
|
|
$text.val('1');
|
|
}
|
|
}).triggerHandler('change');
|
|
|
|
$quiz_options_form.find("#time_limit_option").change(function(event, noFocus) {
|
|
if (!$(this).attr('checked')) {
|
|
$("#quiz_time_limit").val("");
|
|
} else if (!noFocus) {
|
|
$("#quiz_time_limit").focus();
|
|
}
|
|
}).triggerHandler('change', [true]);
|
|
|
|
$("#limit_attempts_option").change(function(event, noFocus) {
|
|
var $item = $("#quiz_allowed_attempts");
|
|
if ($(this).attr('checked')) {
|
|
var val = parseInt($item.data('saved_value') || $item.val() || "2", 10);
|
|
if (val == -1 || isNaN(val)) {
|
|
val = 1;
|
|
}
|
|
$item.val(val);
|
|
if (!noFocus) { $item.focus(); }
|
|
} else {
|
|
$item.data('saved_value', $(this).val());
|
|
$item.val('--');
|
|
}
|
|
}).triggerHandler('change', [true]);
|
|
|
|
$('#enable_quiz_ip_filter, #enable_quiz_access_code').on('change', function() {
|
|
var $checkbox = $(this);
|
|
var $optionGroup = $checkbox.closest('.option-group');
|
|
var checked = $checkbox.prop('checked');
|
|
|
|
// All of this magic here is so that VoiceOver will properly navigate to
|
|
// the inputs after they are shown.
|
|
var $foundOptions = $optionGroup.find('> .options');
|
|
if (checked) {
|
|
$foundOptions.removeClass('screenreader-only');
|
|
// We have to do this bit so keyboard only users aren't confused
|
|
// when their focus goes to something shown offscreen.
|
|
$foundOptions.find('[tabindex="-1"]').each(function (k,v) {
|
|
$(v).attr('tabindex', 0);
|
|
});
|
|
} else {
|
|
$foundOptions.addClass('screenreader-only');
|
|
// Same as above
|
|
$foundOptions.find('[tabindex="0"]').each(function (k,v) {
|
|
$(v).attr('tabindex', -1);
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!checked) {
|
|
$optionGroup
|
|
.find('[type="text"]')
|
|
.val('');
|
|
}
|
|
}).each(function() {
|
|
$(this).triggerHandler('change');
|
|
});
|
|
|
|
$quiz_options_form.on('serializing', function(e, data) {
|
|
var erratic = false;
|
|
|
|
if ($('#enable_quiz_ip_filter').is(':checked')) {
|
|
if (!data['quiz[ip_filter]']) {
|
|
erratic = true;
|
|
$('#quiz_ip_filter').errorBox(
|
|
I18n.t('errors.missing_ip_filter', 'You must enter a valid IP Address')
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($('#enable_quiz_access_code').is(':checked')) {
|
|
if (!data['quiz[access_code]']) {
|
|
erratic = true;
|
|
$('#quiz_access_code').errorBox(
|
|
I18n.t('errors.missing_access_code', 'You must enter an access code')
|
|
);
|
|
}
|
|
}
|
|
|
|
if (erratic) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
|
|
$("#quiz_require_lockdown_browser").change(function() {
|
|
$("#lockdown_browser_suboptions").showIf($(this).attr('checked'));
|
|
$("#quiz_require_lockdown_browser_for_results").attr('checked', true).change();
|
|
});
|
|
|
|
$("#lockdown_browser_suboptions").showIf($("#quiz_require_lockdown_browser").attr('checked'));
|
|
|
|
$("#ip_filters_dialog").delegate('.ip_filter', 'click', function(event) {
|
|
event.preventDefault();
|
|
var filter = $(this).getTemplateData({textValues: ['filter']}).filter;
|
|
$("#protect_quiz").attr('checked', true).triggerHandler('change');
|
|
$("#ip_filter").attr('checked', true).triggerHandler('change');
|
|
$("#quiz_ip_filter").val(filter);
|
|
$("#ip_filters_dialog").dialog('close');
|
|
});
|
|
|
|
$(".ip_filtering_link").click(function(event) {
|
|
event.preventDefault();
|
|
var $dialog = $("#ip_filters_dialog");
|
|
$dialog.dialog({
|
|
width: 400,
|
|
title: I18n.t('titles.ip_address_filtering', "IP Address Filtering")
|
|
});
|
|
if (!$dialog.hasClass('loaded')) {
|
|
$dialog.find(".searching_message").text(I18n.t('retrieving_filters', "Retrieving Filters..."));
|
|
var url = ENV.QUIZ_IP_FILTERS_URL;
|
|
$.ajaxJSON(url, 'GET', {}, function(data) {
|
|
var ip_filters = data.quiz_ip_filters;
|
|
var idx, filter, $filter;
|
|
|
|
$dialog.addClass('loaded');
|
|
|
|
if (ip_filters.length) {
|
|
for (idx = 0; idx < ip_filters.length; ++idx) {
|
|
filter = ip_filters[idx];
|
|
$filter = $dialog.find(".ip_filter.blank:first").clone(true).removeClass('blank');
|
|
$filter.fillTemplateData({data: filter});
|
|
$dialog.find(".filters tbody").append($filter.show());
|
|
}
|
|
$dialog.find(".searching_message").hide().end()
|
|
.find(".filters").show();
|
|
} else {
|
|
$dialog.find(".searching_message").text(I18n.t('no_filters_found', "No filters found"));
|
|
}
|
|
}, function(data) {
|
|
$dialog.find(".searching_message").text(I18n.t('errors.retrieving_filters_failed', "Retrieving Filters Failed"));
|
|
});
|
|
}
|
|
});
|
|
|
|
$("#quiz_one_question_at_a_time").change(function() {
|
|
var $this = $(this);
|
|
$("#one_question_at_a_time_options").showIf($this.attr('checked'));
|
|
if (!$this.attr('checked')) {
|
|
$("#quiz_cant_go_back").attr('checked', false);
|
|
}
|
|
}).triggerHandler('change');
|
|
|
|
$(".question").on("change", ".limit_text", function() {
|
|
var answerValue = $(this).val();
|
|
var textLength = answerValue.length;
|
|
if(textLength > 80) {
|
|
alert(I18n.t('quiz_short_answer_length_error', 'Answers for fill in the blank questions must be under 80 characters long'));
|
|
$(this).val(answerValue.substring(0, 80));
|
|
}
|
|
});
|
|
|
|
$("#multiple_attempts_option,#limit_attempts_option,#quiz_allowed_attempts").change(function() {
|
|
var checked = $("#multiple_attempts_option").prop('checked') && $("#limit_attempts_option").prop('checked');
|
|
if (checked) {
|
|
$("#hide_results_only_after_last_holder").show();
|
|
var $attempts = $('#quiz_allowed_attempts');
|
|
var $attemptsVal = $attempts.val();
|
|
if(isNaN($attemptsVal)) {
|
|
alert(I18n.t('quiz_attempts_nan_error', 'Quiz attempts can only be specified in numbers'));
|
|
$attempts.val("");
|
|
} else if($attemptsVal.length > 3) {
|
|
alert(I18n.t('quiz_attempts_length_error', 'Quiz attempts are limited to 3 digits, if you would like to give your students unlimited attempts, do not check Allow Multiple Attempts box to the left'));
|
|
$attempts.val("");
|
|
}
|
|
} else {
|
|
$("#hide_results_only_after_last").attr('checked', false);
|
|
$("#hide_results_only_after_last_holder").hide();
|
|
}
|
|
}).triggerHandler('change');
|
|
|
|
var hasCheckedOverrides = false;
|
|
|
|
$quiz_options_form.formSubmit({
|
|
object_name: "quiz",
|
|
|
|
processData: function(data) {
|
|
$(this).attr('method', 'PUT');
|
|
var quiz_title = $("input[name='quiz[title]']").val();
|
|
let postToSIS = data['quiz[post_to_sis]'] === '1'
|
|
var vaildQuizType = data['quiz[quiz_type]'] != 'survey' && data['quiz[quiz_type]'] != 'practice_quiz'
|
|
let maxNameLength = 256;
|
|
|
|
if (postToSIS && ENV.MAX_NAME_LENGTH_REQUIRED_FOR_ACCOUNT && vaildQuizType){
|
|
maxNameLength = ENV.MAX_NAME_LENGTH
|
|
}
|
|
|
|
let valid = true
|
|
const validationData = {
|
|
assignment_overrides: overrideView.getAllDates(),
|
|
postToSIS: data['quiz[post_to_sis]'] == '1'
|
|
}
|
|
|
|
const errs = overrideView.validateBeforeSave(validationData,{})
|
|
|
|
let validationHelper = new SisValidationHelper({
|
|
postToSIS: validationData.postToSIS,
|
|
maxNameLengthRequired: ENV.MAX_NAME_LENGTH_REQUIRED_FOR_ACCOUNT,
|
|
maxNameLength: maxNameLength,
|
|
name: quiz_title
|
|
})
|
|
|
|
if (_.keys(errs).length > 0) {
|
|
valid = false
|
|
}
|
|
if (validationHelper.nameTooLong()) {
|
|
valid = false
|
|
let headerOffset = $('#quiz_title').errorBox(I18n.t('The Quiz name must be under %{length} characters', {length: maxNameLength + 1})).offset();
|
|
$('html,body').scrollTo({top: headerOffset.top, left: 0});
|
|
}
|
|
if (!valid) {
|
|
return false
|
|
}
|
|
|
|
if (quiz_title.length == 0) {
|
|
var offset = $("#quiz_title").errorBox(I18n.t('errors.field_is_required', "This field is required")).offset();
|
|
$('html,body').scrollTo({top: offset.top, left:0});
|
|
return false;
|
|
}
|
|
|
|
data['quiz[title]'] = quiz_title;
|
|
if (!lockedItems.content) {
|
|
data['quiz[description]'] = RichContentEditor.callOnRCE($('#quiz_description'), 'get_code');
|
|
}
|
|
if ($("#quiz_notify_of_update").is(':checked')) {
|
|
data['quiz[notify_of_update]'] = $("#quiz_notify_of_update").val();
|
|
}
|
|
var attempts = 1;
|
|
if (data.multiple_attempts) {
|
|
attempts = parseInt(data.allowed_attempts, 10);
|
|
if (isNaN(attempts) || !data.limit_attempts) { attempts = -1; }
|
|
}
|
|
data.allowed_attempts = attempts;
|
|
data['quiz[allowed_attempts]'] = attempts;
|
|
var overrides = overrideView.getOverrides();
|
|
data['quiz[only_visible_to_overrides]'] = overrideView.containsSectionsWithoutOverrides();
|
|
if (overrideView.containsSectionsWithoutOverrides() && !hasCheckedOverrides) {
|
|
var sections = overrideView.sectionsWithoutOverrides();
|
|
var missingDateView = new MissingDateDialog({
|
|
validationFn: function(){ return sections },
|
|
labelFn: function( section ) { return section.get('name')},
|
|
success: function(){
|
|
missingDateView.$dialog.dialog('close').remove();
|
|
missingDateView.remove();
|
|
hasCheckedOverrides = true;
|
|
$quiz_options_form.trigger('submit');
|
|
}
|
|
});
|
|
missingDateView.cancel = function() {
|
|
missingDateView.$dialog.dialog('close').remove();
|
|
};
|
|
missingDateView.render();
|
|
return false;
|
|
} else {
|
|
var finalQuiz = overrideView.getDefaultDueDate();
|
|
if (finalQuiz) {
|
|
finalQuiz = finalQuiz.toJSON().assignment_override;
|
|
adjustOverridesForFormParams([finalQuiz]);
|
|
data['quiz[due_at]'] = finalQuiz.due_at || ""
|
|
data['quiz[unlock_at]'] = finalQuiz.unlock_at || "";
|
|
data['quiz[lock_at]'] = finalQuiz.lock_at || "";
|
|
}
|
|
else {
|
|
data['quiz[due_at]'] = "";
|
|
data['quiz[unlock_at]'] = "";
|
|
data['quiz[lock_at]'] = "";
|
|
}
|
|
adjustOverridesForFormParams(overrides);
|
|
if (overrides.length === 0) { overrides = false; }
|
|
data['quiz[assignment_overrides]'] = overrides;
|
|
}
|
|
if (ENV['CONDITIONAL_RELEASE_SERVICE_ENABLED']) {
|
|
var crError = conditionalRelease.editor.validateBeforeSave();
|
|
if (crError) {
|
|
$('#quiz_tabs').tabs("option", "active", 2);
|
|
conditionalRelease.editor.focusOnError();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var serializingEvent = $.Event('serializing');
|
|
|
|
$(this).trigger(serializingEvent, data);
|
|
|
|
if (serializingEvent.isDefaultPrevented()) {
|
|
return false;
|
|
}
|
|
|
|
return data;
|
|
},
|
|
|
|
beforeSubmit: function(data) {
|
|
$quiz_edit_wrapper.find(".btn.save_quiz_button").attr('disabled', true);
|
|
$quiz_edit_wrapper.find(".btn.save_and_publish").attr('disabled', true);
|
|
},
|
|
|
|
onSubmit: function(promise, data) {
|
|
var form = this;
|
|
|
|
if (ENV['CONDITIONAL_RELEASE_SERVICE_ENABLED']) {
|
|
promise = promise.pipe(function(promisedData) {
|
|
if (promisedData && promisedData.quiz) {
|
|
conditionalRelease.editor.updateAssignment({
|
|
id: promisedData.quiz.assignment_id,
|
|
grading_type: 'points',
|
|
points_possible: promisedData.quiz.points_possible
|
|
});
|
|
}
|
|
return conditionalRelease.editor.save().pipe(function() {
|
|
return promisedData;
|
|
});
|
|
});
|
|
}
|
|
promise.then(success.bind(this), error.bind(this));
|
|
|
|
function success(data) {
|
|
$quiz_edit_wrapper
|
|
.find(".btn.saving")
|
|
.text(I18n.t('buttons.saved', "Saved!"));
|
|
if (data.quiz.assignment) {
|
|
var assignment = data.quiz.assignment;
|
|
if ($("#assignment_option_" + assignment.id).length === 0) {
|
|
if (assignment.assignment_group && $("#assignment_group_optgroup_" + assignment.assignment_group_id).length === 0) {
|
|
var assignment_group = assignment.assignment_group;
|
|
var $optgroup = $(document.createElement('optgroup'));
|
|
$optgroup.attr('label', assignment_group.name).attr('id', 'assignment_group_optgroup_' + assignment_group.id);
|
|
}
|
|
var $group = $("#assignment_group_optgroup_" + assignment.assignment_group_id);
|
|
var $option = $(document.createElement('option'));
|
|
$option.attr('id', 'assignment_option_' + assignment.id).val(assignment.id).text(assignment.title);
|
|
$group.append($option);
|
|
}
|
|
}
|
|
$(".show_rubric_link").showIf(data.quiz.assignment);
|
|
$("#quiz_assignment_id").val(data.quiz.quiz_type || "practice_quiz").change();
|
|
if (deparam()['return_to']) {
|
|
location.href = deparam()['return_to']
|
|
} else {
|
|
location.href = $(this).attr('action');
|
|
}
|
|
quiz.updateDisplayComments();
|
|
}
|
|
|
|
function error(data) {
|
|
$("#quiz_edit_wrapper")
|
|
.find(".btn.save_quiz_button")
|
|
.attr('disabled', false)
|
|
.removeClass("saving")
|
|
.text(I18n.t('buttons.save', "Save"));
|
|
$("#quiz_edit_wrapper")
|
|
.find(".btn.save_and_publish")
|
|
.attr('disabled', false)
|
|
.removeClass("saving")
|
|
.text(I18n.t('buttons.save_and_publish', "Save & Publish"));
|
|
$("#quiz_edit_wrapper")
|
|
.find('input[name="publish"]')
|
|
.remove();
|
|
|
|
$(this).trigger('xhrError', data);
|
|
$(this).formErrors(data);
|
|
}
|
|
}
|
|
});
|
|
|
|
$quiz_edit_wrapper.find(".save_quiz_button").click(function(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
$quiz_edit_wrapper
|
|
.find(".btn.save_quiz_button")
|
|
.addClass("saving")
|
|
.text(I18n.t('buttons.saving', "Saving..."));
|
|
$quiz_options_form.data('submit_type', 'save_only').submit();
|
|
}).end();
|
|
|
|
$quiz_edit_wrapper.find(".save_and_publish").click(function(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
var $publish_input = $(document.createElement('input'));
|
|
$publish_input.attr('type', 'hidden').attr('name', 'publish').attr('value', 'true');
|
|
$quiz_options_form.append($publish_input);
|
|
|
|
$quiz_edit_wrapper
|
|
.find(".btn.save_and_publish")
|
|
.addClass("saving")
|
|
.text(I18n.t('buttons.saving', "Saving..."));
|
|
$quiz_options_form.data('submit_type', 'save_only').submit();
|
|
}).end();
|
|
|
|
$("#show_question_details").change(function(event) {
|
|
$("#questions").toggleClass('brief', !$(this).attr('checked'));
|
|
}).triggerHandler('change');
|
|
|
|
$(".start_over_link").click(function(event) {
|
|
event.preventDefault();
|
|
var result = confirm(I18n.t('confirms.scrap_and_restart', "Scrap this quiz and start from scratch?"));
|
|
if (result) {
|
|
location.href = location.href + "?fresh=1";
|
|
}
|
|
});
|
|
|
|
$("#quiz_assignment_id").change(function(event) {
|
|
var previousData = $("#quiz_options").getTemplateData({textValues: ['assignment_id', 'title']});
|
|
var assignment_id = $("#quiz_assignment_id").val();
|
|
var quiz_title = $("#quiz_title_input").val();
|
|
if (assignment_id) {
|
|
var select = $("#quiz_assignment_id")[0];
|
|
quiz_title = $(select.options[select.selectedIndex]).text();
|
|
} else if (previousData.assignment_id) {
|
|
quiz_title = I18n.t('default_quiz_title', "Quiz");
|
|
}
|
|
var data = {
|
|
'quiz[assignment_id]': assignment_id,
|
|
'quiz[title]': quiz_title
|
|
};
|
|
$("#quiz_title").showIf(true);
|
|
var gradedQuiz = event.target.value === "assignment" ||
|
|
event.target.value === "graded_survey";
|
|
$("#post_to_sis_option").showIf(gradedQuiz);
|
|
$("#quiz_options_form .quiz_survey_setting").showIf(assignment_id && assignment_id.match(/survey/));
|
|
$("#quiz_points_possible").showIf(assignment_id == 'graded_survey');
|
|
$("#survey_instructions").showIf(assignment_id == 'survey' || assignment_id == 'graded_survey');
|
|
$("#quiz_assignment_group_id").closest('.control-group').showIf(assignment_id == 'assignment' || assignment_id == 'graded_survey');
|
|
$("#questions").toggleClass('survey_quiz', assignment_id == 'survey' || assignment_id == 'graded_survey');
|
|
$("#quiz_display_points_possible").showIf(assignment_id != 'survey' && assignment_id != 'graded_survey');
|
|
$("#quiz_options_holder").toggleClass('survey_quiz', assignment_id == 'survey' || assignment_id == 'graded_survey');
|
|
var url = $("#quiz_urls .update_quiz_url").attr('href');
|
|
$("#quiz_title_input").val(quiz_title);
|
|
$("#quiz_title_text").text(quiz_title);
|
|
|
|
if (ENV['CONDITIONAL_RELEASE_SERVICE_ENABLED']) {
|
|
toggleConditionalReleaseTab(event.target.value);
|
|
}
|
|
}).change();
|
|
|
|
$(".question_form :input").keycodes("esc", function(event) {
|
|
$(this).parents("form").find("input[value='" + I18n.t('#buttons.cancel', "Cancel") + "']").click();
|
|
});
|
|
|
|
$(document).delegate('.blank_id_select', 'change', function() {
|
|
var variable = $(this).val();
|
|
var idx = $(this)[0].selectedIndex;
|
|
$(this).closest(".question").find(".answer").css('display', 'none');
|
|
if (variable) {
|
|
if (variable != '0') {
|
|
$(this).closest(".question").find(".answer.answer_idx_" + idx).filter(":not(.answer_for_" + variable + ")").each(function() {
|
|
$(this).attr('class', $(this).attr('class').replace(/answer_for_[A-Za-z0-9]+/g, ""));
|
|
$(this).addClass('answer_for_' + variable);
|
|
});
|
|
}
|
|
$(this).closest(".question").find(".answer.answer_for_" + variable).css('display', '');
|
|
} else {
|
|
$(this).closest(".question").find(".answer").css('display', '');
|
|
$(this).closest(".question").find(".answer.answer_idx_" + idx).css('display', '');
|
|
}
|
|
});
|
|
|
|
$(".blank_id_select").change();
|
|
|
|
$(document).delegate(".delete_question_link", 'click', function(event) {
|
|
event.preventDefault();
|
|
$(this).parents(".question_holder").confirmDelete({
|
|
url: $(this).parents(".question_holder").find(".update_question_url").attr('href'),
|
|
message: I18n.t('confirms.delete_question', "Are you sure you want to delete this question?"),
|
|
success: function(data) {
|
|
$(this).remove();
|
|
quiz.updateDisplayComments();
|
|
}
|
|
});
|
|
});
|
|
|
|
$(document).delegate(".edit_question_link", 'click', function(event) {
|
|
event.preventDefault();
|
|
var $question = $(this).parents(".question");
|
|
setQuestionID($question);
|
|
var questionID = $question.data("questionID")
|
|
var question = $question.getTemplateData({
|
|
textValues: ['question_type', 'correct_comments', 'incorrect_comments', 'neutral_comments',
|
|
'question_name', 'question_points', 'answer_selection_type', 'blank_id',
|
|
'matching_answer_incorrect_matches', 'regrade_option', 'regrade_disabled'],
|
|
htmlValues: ['question_text', 'correct_comments_html', 'incorrect_comments_html', 'neutral_comments_html']
|
|
});
|
|
question.question_text = $question.find("textarea[name='question_text']").val();
|
|
var matches = [];
|
|
$question.find(".matching_answer_incorrect_matches_list li").each(function() {
|
|
matches.push($(this).text());
|
|
});
|
|
|
|
question.matching_answer_incorrect_matches = matches.join("\n");
|
|
question.question_points = parseFloat(question.question_points, 10);
|
|
if (isNaN(question.question_points) || question.question_points < 0) { question.question_points = 0; }
|
|
var $form = $("#question_form_template").clone(true).attr('id', '');
|
|
var $formQuestion = $form.find(".question");
|
|
$formQuestion.addClass('initialLoad');
|
|
$form.fillFormData(question);
|
|
$formQuestion.removeClass('initialLoad');
|
|
addHTMLFeedback($form.find(".question_correct_comment"), question, 'correct_comments');
|
|
addHTMLFeedback($form.find(".question_incorrect_comment"), question, 'incorrect_comments');
|
|
addHTMLFeedback($form.find(".question_neutral_comment"), question, 'neutral_comments');
|
|
|
|
$formQuestion.addClass('selectable');
|
|
$form.find(".answer_selection_type").change().show();
|
|
if (question.question_type != 'missing_word_question') { $form.find("option.missing_word").remove(); }
|
|
|
|
if ($question.hasClass('missing_word_question') || question.question_type == 'missing_word_question') {
|
|
question = $question.getTemplateData({textValues: ['text_before_answers', 'text_after_answers']});
|
|
var answer_data = $question.find(".original_question_text").getFormData();
|
|
question.text_before_answers = answer_data.question_text;
|
|
question.text_after_answers = answer_data.text_after_answers;
|
|
question.question_text = question.text_before_answers;
|
|
$form.fillFormData(question);
|
|
}
|
|
|
|
var data = quiz.updateFormQuestion($form);
|
|
$form.find(".form_answers").empty();
|
|
if (data.question_type == 'calculated_question') {
|
|
var question = quizData($question).questions[0];
|
|
$form.find(".combinations_holder .combinations thead tr").empty();
|
|
for(var idx in question.variables) {
|
|
var $var = $($form.find(".variables .variable." + question.variables[idx].name));
|
|
if (question.variables[idx].name == 'variable') {
|
|
$var = $($form.find(".variables .variable").get(idx));
|
|
}
|
|
if ($var && $var.length > 0) {
|
|
$var.find(".min").val(question.variables[idx].min);
|
|
$var.find(".max").val(question.variables[idx].max);
|
|
$var.find(".round").val(question.variables[idx].scale);
|
|
}
|
|
var $th = $("<th/>");
|
|
$th.text(question.variables[idx].name);
|
|
$th.attr('id', 'possible_solution_' + question.variables[idx].name);
|
|
$form.find(".combinations_holder .combinations thead tr").append($th);
|
|
}
|
|
var $th = $("<th class='final_answer'/>");
|
|
$th.text(I18n.t('final_answer', "Final Answer"));
|
|
$th.attr('id', 'possible_solution_final');
|
|
$form.find(".combinations_holder .combinations thead tr").append($th);
|
|
for(var idx in question.formulas) {
|
|
$form.find(".supercalc").val(question.formulas[idx]);
|
|
$form.find(".decimal_places .round").val(question.formula_decimal_places);
|
|
$form.find(".save_formula_button").click();
|
|
}
|
|
if (question.answer_tolerance) {
|
|
$form.find(".combination_answer_tolerance").val(question.answer_tolerance);
|
|
}
|
|
$form.find(".combination_count").val(question.answers.length);
|
|
for(var idx in question.answers) {
|
|
var $tr = $("<tr/>");
|
|
for(var jdx in question.answers[idx].variables) {
|
|
var $td = $("<td/>");
|
|
$td.text(question.answers[idx].variables[jdx].value);
|
|
$td.attr('aria-labelledby', 'possible_solution_' + question.answers[idx].variables[jdx].name);
|
|
$tr.append($td);
|
|
}
|
|
var html = question.answers[idx].answer_text;
|
|
if (question.answer_tolerance) {
|
|
html = html + " <span style='font-size: 0.8em;'>+/-</span> " + htmlEscape(question.answer_tolerance);
|
|
}
|
|
var $td = $("<td class='final_answer'/>");
|
|
$td.html(html);
|
|
$td.attr('aria-labelledby', 'possible_solution_final');
|
|
$tr.append($td);
|
|
$form.find(".combinations tbody").append($tr);
|
|
$form.find(".combinations_holder").show();
|
|
}
|
|
$form.triggerHandler('settings_change', false);
|
|
$formQuestion.triggerHandler('recompute_variables', true);
|
|
} else {
|
|
// must use > in selector
|
|
$question.find(".text > .answers .answer").each(function(index) {
|
|
var answer = $(this).getTemplateData({
|
|
textValues: data.textValues,
|
|
htmlValues: data.htmlValues
|
|
});
|
|
answer.answer_type = data.answer_type;
|
|
answer.question_type = data.question_type;
|
|
var $answer = makeFormAnswer(answer);
|
|
addAriaDescription($answer, index+1);
|
|
$form.find(".form_answers").append($answer);
|
|
});
|
|
}
|
|
if ($question.hasClass('essay_question') || $question.hasClass('file_upload')) {
|
|
$formQuestion.find(".comments_header").text(I18n.beforeLabel(I18n.t('labels.comments_on_question', "Comments for this question")));
|
|
}
|
|
|
|
limitTextInputFor($form, question.question_type);
|
|
|
|
$question.hide().after($form);
|
|
quiz.showFormQuestion($form);
|
|
$form.attr('action', $question.find(".update_question_url").attr('href'))
|
|
.attr('method', 'POST')
|
|
.find('.submit_button').text(I18n.t('buttons.update_question', 'Update Question'));
|
|
$form.find(":input:visible:first").focus().select();
|
|
$("html,body").scrollTo({top: $form.offset().top - 10, left: 0});
|
|
setTimeout(function() {
|
|
$formQuestion.find(".question_content").triggerHandler('change');
|
|
$formQuestion.addClass('ready');
|
|
}, 100);
|
|
|
|
// show regrade options if question was changed but quiz not saved
|
|
var $question = $form.find(".question");
|
|
setQuestionID($question)
|
|
var questionID = $question.data("questionID")
|
|
|
|
if (REGRADE_OPTIONS[questionID]) {
|
|
var regradeOption = $(QuizRegradeView.prototype.template()).find('input[value='+REGRADE_OPTIONS[questionID]+']')
|
|
var newAnswer = $form.find('.correct_answer')
|
|
toggleAnswer($question, {regradeOption: regradeOption, newAnswer: newAnswer})
|
|
}
|
|
toggleSelectAnswerAltText($('.form_answers .answer'), quiz.answerSelectionType(question.question_type))
|
|
togglePossibleCorrectAnswerLabel($('.form_answers .answer'));
|
|
});
|
|
|
|
$(".question_form :input[name='question_type']").change(function () {
|
|
// is this the initial loado of the question type
|
|
var loading = $(this).parents('.question.initialLoad').length > 0;
|
|
var holder = $(this).parents('.question_holder');
|
|
var isNew = $(holder).find('#question_new').length > 0;
|
|
|
|
quiz.updateFormQuestion($(this).parents('.question_form'));
|
|
|
|
if ($('#student_submissions_warning').length > 0 && !loading && !isNew) {
|
|
disableRegrade(holder);
|
|
}
|
|
});
|
|
|
|
$('#question_form_template .cancel_link').click(function (event) {
|
|
var $displayQuestion = $(this).parents('form').prev();
|
|
var isNew = $displayQuestion.attr('id') == 'question_new';
|
|
|
|
event.preventDefault();
|
|
|
|
if (!isNew) {
|
|
$(this).parents('form').remove();
|
|
}
|
|
$displayQuestion.show();
|
|
$("html,body").scrollTo({top: $displayQuestion.offset().top - 10, left: 0});
|
|
if (isNew) {
|
|
$displayQuestion.parent().remove();
|
|
quiz.updateDisplayComments();
|
|
}
|
|
quiz.updateDisplayComments();
|
|
});
|
|
|
|
$(document).delegate("a.comment_focus", 'click', function(event) {
|
|
event.preventDefault();
|
|
var $link = $(this);
|
|
var $comment = $link.closest('.question_comment, .answer_comments');
|
|
var $comment_html = $comment.find('.comment_html');
|
|
|
|
var toggler = $comment.data('editorToggle');
|
|
|
|
// create toggler instance on the first click
|
|
if (!toggler) {
|
|
var tinyOptions = {}
|
|
|
|
var inputColumn = $comment.parents().find('.answer_type:visible')[0]
|
|
|
|
if (inputColumn) {
|
|
var rightMargin = parseInt($comment.css('marginRight')) || 0
|
|
var leftMargin = parseInt($comment.css('marginLeft')) || 0
|
|
var commentMargin = rightMargin + leftMargin
|
|
|
|
var rceWidth = inputColumn.offsetWidth - commentMargin
|
|
tinyOptions.width = rceWidth
|
|
}
|
|
|
|
toggler = new EditorToggle($comment_html, {
|
|
editorBoxLabel: $link.title,
|
|
tinyOptions: tinyOptions
|
|
});
|
|
|
|
toggler.editButton = $link;
|
|
toggler.on('display', function() {
|
|
|
|
$comment.removeClass('editing');
|
|
|
|
var html = $comment_html.html();
|
|
$comment.find('input[type="hidden"]').val(html);
|
|
if (html == '') {
|
|
$comment.addClass('empty');
|
|
}
|
|
});
|
|
$comment.data('editorToggle', toggler);
|
|
}
|
|
|
|
if (!toggler.editing) {
|
|
$comment.removeClass('empty');
|
|
$comment.addClass('editing');
|
|
toggler.edit();
|
|
}
|
|
|
|
});
|
|
|
|
$(document).delegate(".numerical_answer_type", 'change', function(){
|
|
numericalAnswerTypeChange($(this))
|
|
}).change();
|
|
|
|
$(document).delegate(".select_answer_link", 'click', function(event) {
|
|
event.preventDefault();
|
|
|
|
var $question = $(this).parents(".question");
|
|
if (!$question.hasClass('selectable')) { return; }
|
|
setQuestionID($question)
|
|
var questionID = $question.data("questionID")
|
|
|
|
if (!REGRADE_DATA[questionID]){
|
|
REGRADE_DATA[questionID] = correctAnswerIDs($question);
|
|
}
|
|
|
|
updateAnswers($question, $(this).parents('.answer'));
|
|
});
|
|
|
|
function updateAnswers($question, newAnswer) {
|
|
var holder = $question.parents('.question_holder');
|
|
var isNew = holder.find("#question_new").length > 0;
|
|
if(isNew ||
|
|
!canRegradeQuestion($question) ||
|
|
!$("#student_submissions_warning").length > 0) {
|
|
toggleAnswer($question, {newAnswer: newAnswer, regradeOption: null});
|
|
}
|
|
else {
|
|
var isDisabled = holder.find('input[name="regrade_disabled"]').val() == '1';
|
|
var questionType = $question.find(".question_type").val();
|
|
var regradeOptions = new QuizRegradeView({
|
|
question: $question,
|
|
regradeDisabled: isDisabled,
|
|
regradeOption: REGRADE_OPTIONS[$question.data("questionID")],
|
|
multipleAnswer: questionType === "multiple_answers_question"
|
|
});
|
|
regradeOptions.on('update', function(regradeOption){
|
|
var newAnswerData = {regradeOption: regradeOption, newAnswer: newAnswer}
|
|
toggleAnswer($question, newAnswerData)
|
|
})
|
|
}
|
|
}
|
|
|
|
function toggleAnswer($question, newAnswerData) {
|
|
var $answer = $(newAnswerData.newAnswer);
|
|
var $answers = $answer.parent().find(".answer");
|
|
|
|
if ($question.find(":input[name='question_type']").val() != "multiple_answers_question") {
|
|
$question.find(".answer:visible").removeClass('correct_answer')
|
|
.find('.select_answer_link').attr('title', clickSetCorrect)
|
|
.find('img').attr('alt', clickSetCorrect)
|
|
setAnswerText(newAnswerData.newAnswer, isSetCorrect);
|
|
$answer.addClass('correct_answer');
|
|
togglePossibleCorrectAnswerLabel($answers);
|
|
}
|
|
else {
|
|
$answer.toggleClass('correct_answer');
|
|
var answerText = $answer.hasClass('correct_answer') ? clickUnsetCorrect : clickSetCorrect;
|
|
setAnswerText(newAnswerData.newAnswer, answerText);
|
|
togglePossibleCorrectAnswerLabel($answers);
|
|
}
|
|
if(!newAnswerData.regradeOption) { return; }
|
|
updateRegradeOption($question, newAnswerData)
|
|
}
|
|
|
|
function updateRegradeOption($question, newAnswerData) {
|
|
var option = newAnswerData.regradeOption
|
|
var optionText = option.next('span');
|
|
REGRADE_OPTIONS[$question.data("questionID")] = option.val();
|
|
$question.find("."+optionText.attr('class')).remove();
|
|
$(newAnswerData.newAnswer).append(htmlEscape(option.next('span')));
|
|
$(newAnswerData.newAnswer).parents('.answer').append(htmlEscape(optionText));
|
|
option.hide();
|
|
$question.append(htmlEscape(option));
|
|
}
|
|
|
|
function setAnswerText(answer, text) {
|
|
$(answer)
|
|
.attr('title', text)
|
|
.find('.answer_image').attr('alt', text)
|
|
}
|
|
|
|
function setQuestionID(question){
|
|
var questionID = $(question).closest('.question_holder')
|
|
.find('.display_question').attr('id')
|
|
.replace("question_", "");
|
|
question.data({"questionID": questionID})
|
|
}
|
|
|
|
function canRegradeQuestion($el) {
|
|
var regradeTypes = ["multiple_choice_question",
|
|
"true_false_question",
|
|
"multiple_answers_question"];
|
|
return _.find(regradeTypes, function(className) {
|
|
return $el.hasClass(className);
|
|
})
|
|
}
|
|
|
|
function disableRegrade(holder) {
|
|
holder.find('.regrade_enabled').hide();
|
|
holder.find('.regrade_disabled').show();
|
|
holder.find('input[name="regrade_option"]').attr('disabled', true);
|
|
holder.find('input[name="regrade_option"]').attr('checked', false);
|
|
holder.find('input[name="regrade_disabled"]').val('1');
|
|
}
|
|
|
|
function disableQuestionForm() {
|
|
$('.question_form').find(".submit_button")
|
|
.attr('disabled', true)
|
|
.addClass('disabled')
|
|
.removeClass('button_primary btn-primary');
|
|
}
|
|
|
|
function enableQuestionForm() {
|
|
$('.question_form').find(".submit_button")
|
|
.removeClass('disabled')
|
|
.removeAttr('disabled')
|
|
.addClass('button_primary btn-primary');
|
|
}
|
|
|
|
function correctAnswerIDs($el){
|
|
var answers = [];
|
|
$el.find('.answer').each(function(index) {
|
|
if ($(this).hasClass('correct_answer')) answers.push(index);
|
|
});
|
|
return answers;
|
|
}
|
|
|
|
function answersAreTheSameAsBefore($el) {
|
|
setQuestionID($el);
|
|
var questionID = $el.data("questionID");
|
|
|
|
// we don't know 'old answers' if they've updated and returned
|
|
if (REGRADE_OPTIONS[questionID]) {
|
|
return false;
|
|
|
|
} else {
|
|
var oldAnswers = REGRADE_DATA[questionID];
|
|
var newAnswers = correctAnswerIDs($el);
|
|
|
|
return oldAnswers.length == newAnswers.length &&
|
|
!_.difference(oldAnswers, newAnswers).length;
|
|
}
|
|
}
|
|
|
|
$(".question_form :input").change(function() {
|
|
if ($(this).parents(".answer").length > 0) {
|
|
var $answer = $(this).parents(".answer");
|
|
$answer.find(":input[name='" + $(this).attr('name') + "']").val($(this).val());
|
|
}
|
|
});
|
|
|
|
$(".question_form select.answer_selection_type").change(function() {
|
|
if ($(this).val() == "single_answer") {
|
|
$(this).parents(".question").removeClass('multiple_answers');
|
|
} else {
|
|
$(this).parents(".question").addClass('multiple_answers');
|
|
}
|
|
}).change();
|
|
|
|
$(".delete_answer_link").click(function(event) {
|
|
event.preventDefault();
|
|
|
|
var holder = $(this).parents('.question_holder');
|
|
var regradeOpt = holder.find('span.regrade_option');
|
|
|
|
// warn they can't regrade if there are submissions
|
|
var disabled = regradeOpt.text() == 'disabled';
|
|
var isNew = holder.find("#question_new").length > 0;
|
|
if ($("#student_submissions_warning").length > 0 && !disabled && !isNew) {
|
|
var msg = I18n.t('confirms.delete_answer',
|
|
"Are you sure? Deleting answers from a question with submissions " +
|
|
"disables the option to regrade this question.")
|
|
if (!confirm(msg)) { return; }
|
|
|
|
// disabled regrade if they've chosen already
|
|
disableRegrade(holder);
|
|
enableQuestionForm();
|
|
}
|
|
|
|
var $ans = $(this).parents(".answer");
|
|
var $ansHeader = $ans.closest('.question').find('.answers_header');
|
|
$(".errorBox").not("#error_box_template").remove();
|
|
$ans.remove();
|
|
$ansHeader.focus();
|
|
});
|
|
|
|
$(".add_question_group_link").click(function(event) {
|
|
event.preventDefault();
|
|
if (questionLimitReached()) return;
|
|
$(".question_form .submit_button:visible,.quiz_group_form .submit_button:visible").each(function() {
|
|
$(this).parents("form").submit();
|
|
});
|
|
var $group_top = $("#group_top_template").clone(true).attr('id', 'group_top_new');
|
|
var $group_bottom = $("#group_bottom_template").clone(true).attr('id', 'group_bottom_new');
|
|
$("#questions").append($group_top.show()).append($group_bottom.show());
|
|
$group_top.find(".edit_group_link").click();
|
|
$group_top.find(".quiz_group_form").attr('action', $("#quiz_urls .add_group_url").attr('href'))
|
|
.attr('method', 'POST');
|
|
$group_top.find(".submit_button").text(I18n.t('buttons.create_group', "Create Group"));
|
|
});
|
|
|
|
$(".add_question_link").click(function(event) {
|
|
event.preventDefault();
|
|
if (questionLimitReached()) return;
|
|
$(".question_form:visible,.group_top.editing .quiz_group_form:visible").submit();
|
|
var $question = makeQuestion();
|
|
if ($(this).parents(".group_top").length > 0) {
|
|
|
|
var groupID = $(this).parents(".group_top")[0].id.replace("group_top_","")
|
|
$($question[0]).attr("data-group-id", groupID)
|
|
|
|
var $bottom = $(this).parents(".group_top").next();
|
|
while($bottom.length > 0 && !$bottom.hasClass('group_bottom')) {
|
|
$bottom = $bottom.next();
|
|
}
|
|
$bottom.before($question.addClass('group'));
|
|
} else {
|
|
$("#questions").append($question);
|
|
}
|
|
$question.find(".edit_question_link:first").click();
|
|
var $form = $question.parents(".question_holder").children("form");
|
|
$form.attr('action', $("#quiz_urls .add_question_url,#bank_urls .add_question_url").attr('href'))
|
|
.attr('method', 'POST')
|
|
.find(".submit_button").text(I18n.t('buttons.create_question', "Create Question"));
|
|
$form.find("option.missing_word").remove();
|
|
$question.find(".question_type").change();
|
|
$("html,body").scrollTo({top: $question.offset().top - 10, left: 0});
|
|
$question.find(":input:first").focus().select();
|
|
});
|
|
|
|
quiz.$questions.delegate('.group_top input[name="quiz_group[pick_count]"]', {
|
|
focus: function() {
|
|
$(this).data('prev-value', this.value);
|
|
},
|
|
change: function() {
|
|
if (questionLimitReached(true)) {
|
|
this.value = $(this).data('prev-value');
|
|
}
|
|
}
|
|
});
|
|
|
|
var $findBankDialog = $("#find_bank_dialog");
|
|
|
|
$(".find_bank_link").click(function(event) {
|
|
event.preventDefault();
|
|
var $dialog = $findBankDialog;
|
|
$dialog.data('form', $(this).closest(".quiz_group_form"));
|
|
if (!$dialog.hasClass('loaded')) {
|
|
$dialog.data('banks', {});
|
|
$dialog.find(".find_banks").hide();
|
|
$dialog.find(".message").show().text(I18n.t('loading_question_banks', "Loading Question Banks..."));
|
|
var url = $dialog.find(".find_question_banks_url").attr('href');
|
|
$.ajaxJSON(url, 'GET', {}, function(banks) {
|
|
$dialog.find(".message").hide();
|
|
$dialog.find(".find_banks").show();
|
|
$dialog.addClass('loaded');
|
|
for(var idx in banks) {
|
|
var bank = banks[idx].assessment_question_bank;
|
|
var $bank = $dialog.find(".bank.blank:first").clone(true).removeClass('blank');
|
|
$bank.fillTemplateData({data: bank, dataValues: ['id', 'context_type', 'context_id']});
|
|
$dialog.find(".bank_list").append($bank);
|
|
$bank.data('bank_data', bank);
|
|
$bank.show();
|
|
}
|
|
}, function(data) {
|
|
$dialog.find(".message").text(I18n.t('errors.loading_banks_failed', "Question Banks failed to load, please try again"));
|
|
});
|
|
}
|
|
$dialog.find(".bank.selected").removeClass('selected');
|
|
$dialog.find(".submit_button").attr('disabled', true);
|
|
$dialog.dialog({
|
|
title: I18n.t('titles.find_question_bank', "Find Question Bank"),
|
|
width: 600,
|
|
height: 400
|
|
});
|
|
});
|
|
|
|
$findBankDialog.delegate('.bank', 'click', function() {
|
|
$findBankDialog.find(".bank.selected").removeClass('selected');
|
|
$(this).addClass('selected');
|
|
$findBankDialog.find(".submit_button").attr('disabled', false);
|
|
}).delegate('.submit_button', 'click', function() {
|
|
var $bank = $findBankDialog.find(".bank.selected:first");
|
|
var bank = $bank.data('bank_data');
|
|
var $form = $findBankDialog.data('form');
|
|
$form.find(".bank_id").val(bank.id);
|
|
bank.bank_name = bank.title;
|
|
var $formBank = $form.closest('.group_top').next(".assessment_question_bank")
|
|
if ($formBank.length == 0) {
|
|
$formBank = $("#group_top_template").next(".assessment_question_bank").clone(true);
|
|
$form.closest('.group_top').after($formBank);
|
|
}
|
|
$formBank.show()
|
|
.fillTemplateData({data: bank}).data('bank_data', bank);
|
|
$findBankDialog.dialog('close');
|
|
}).delegate('.cancel_button', 'click', function() {
|
|
$findBankDialog.dialog('close');
|
|
});
|
|
|
|
var $findQuestionDialog = $("#find_question_dialog");
|
|
|
|
$(".find_question_link").click(function(event) {
|
|
event.preventDefault();
|
|
var $dialog = $findQuestionDialog;
|
|
if (!$dialog.hasClass('loaded')) {
|
|
$dialog.data('banks', {});
|
|
$dialog.find(".side_tabs_table").hide();
|
|
$dialog.find(".message").show().text(I18n.t('loading_question_banks', "Loading Question Banks..."));
|
|
var url = $dialog.find(".find_question_banks_url").attr('href');
|
|
$.ajaxJSON(url, 'GET', {}, function(banks) {
|
|
$dialog.find(".message").hide();
|
|
$dialog.find(".side_tabs_table").show();
|
|
$dialog.addClass('loaded');
|
|
for(var idx in banks) {
|
|
var bank = banks[idx].assessment_question_bank;
|
|
bank.title = TextHelper.truncateText(bank.title)
|
|
var $bank = $dialog.find(".bank.blank:first").clone(true).removeClass('blank');
|
|
$bank.fillTemplateData({data: bank});
|
|
$dialog.find(".bank_list").append($bank);
|
|
$bank.data('bank_data', bank);
|
|
$bank.show();
|
|
}
|
|
$dialog.find(".bank:not(.blank):first").click();
|
|
}, function(data) {
|
|
$dialog.find(".message").text(I18n.t('errors.loading_banks_failed', "Question Banks failed to load, please try again"));
|
|
});
|
|
}
|
|
$dialog.data('add_source', '');
|
|
$dialog.dialog({
|
|
title: I18n.t('titles.find_quiz_question', "Find Quiz Question"),
|
|
open: function() {
|
|
if ($dialog.find(".selected_side_tab").length == 0) {
|
|
$dialog.find(".bank:not(.blank):first").click();
|
|
}
|
|
},
|
|
width: 600,
|
|
height: 400
|
|
});
|
|
});
|
|
|
|
var updateFindQuestionDialogQuizGroups = function(id) {
|
|
var groups = [];
|
|
$findQuestionDialog.find(".quiz_group_select").find("option.group").remove();
|
|
$findQuestionDialog.find(".quiz_group_select_holder").show();
|
|
$("#questions .group_top:visible").each(function() {
|
|
var group = {};
|
|
group.id = $(this).attr('id').substring(10);
|
|
group.name = $(this).getTemplateData({textValues: ['name']}).name;
|
|
var $option = $("<option/>");
|
|
$option.text(TextHelper.truncateText(group.name));
|
|
$option.val(group.id);
|
|
$option.addClass('group');
|
|
$findQuestionDialog.find(".quiz_group_select option.bottom").before($option);
|
|
});
|
|
if (id) {
|
|
$("#quiz_group_select").val(id);
|
|
}
|
|
if ($("#quiz_group_select").val() == "new") {
|
|
$("#quiz_group_select").val("none");
|
|
}
|
|
$("#quiz_group_select").change();
|
|
}
|
|
|
|
$("#quiz_group_select").change(function() {
|
|
if ($(this).val() == "new") {
|
|
var $dialog = $("#add_question_group_dialog");
|
|
var question_ids = [];
|
|
$findQuestionDialog.find(".question_list :checkbox:checked").each(function() {
|
|
question_ids.push($(this).parents(".found_question").data('question_data').id);
|
|
});
|
|
$dialog.find(".questions_count").text(question_ids.length);
|
|
$dialog.find("button").attr('disabled', false).filter(".submit_button").text(I18n.t('buttons.create_group', "Create Group"));
|
|
$dialog.dialog({
|
|
width: 400
|
|
});
|
|
}
|
|
});
|
|
|
|
$("#add_question_group_dialog .submit_button").click(function(event) {
|
|
var $dialog = $("#add_question_group_dialog");
|
|
$dialog.find("button").attr('disabled', true).filter(".submit_button").text(I18n.t('buttons.creating_group', "Creating Group..."));
|
|
|
|
var params = $dialog.getFormData();
|
|
var newParams = {};
|
|
_.each(params, function(val, key) {
|
|
newParams[key.replace('quiz_group[', 'quiz_groups[][')] = val;
|
|
});
|
|
|
|
var url = $dialog.find(".add_question_group_url").attr('href');
|
|
$.ajaxJSON(url, 'POST', newParams, function(data) {
|
|
$dialog.find("button").attr('disabled', false).filter(".submit_button").text(I18n.t('buttons.create_group', "Create Group"));
|
|
|
|
var $group_top = $("#group_top_template").clone(true).attr('id', 'group_top_new');
|
|
var $group_bottom = $("#group_bottom_template").clone(true).attr('id', 'group_bottom_new');
|
|
$("#questions").append($group_top.show()).append($group_bottom.show());
|
|
var groups = data.quiz_groups;
|
|
var group = groups[0];
|
|
$group_top.fillTemplateData({
|
|
data: group,
|
|
id: 'group_top_' + group.id,
|
|
hrefValues: ['id']
|
|
});
|
|
$group_top.fillFormData(data, {object_name: 'quiz_group'});
|
|
$("#unpublished_changes_message").slideDown();
|
|
$group_bottom.attr('id', 'group_bottom_' + group.id);
|
|
quiz.updateDisplayComments();
|
|
|
|
updateFindQuestionDialogQuizGroups(group.id);
|
|
$dialog.dialog('close');
|
|
}, function(data) {
|
|
$dialog.find("button").attr('disabled', false).filter(".submit_button").text(I18n.t('errors.creating_group_failed', "Create Group Failed, Please Try Again"));
|
|
});
|
|
});
|
|
|
|
$("#add_question_group_dialog .cancel_button").click(function(event) {
|
|
$("#add_question_group_dialog").dialog('close');
|
|
$("#quiz_group_select").val("none");
|
|
});
|
|
|
|
var showQuestions = function(questionData) {
|
|
var questionList = questionData.questions;
|
|
var $bank = $findQuestionDialog.find(".bank.selected_side_tab");
|
|
var bank = $bank.data('bank_data');
|
|
var bank_data = $findQuestionDialog.data('banks')[bank.id];
|
|
if (!$bank.hasClass('selected_side_tab')) { return; }
|
|
var existingIDs = {};
|
|
$(".display_question:visible").each(function() {
|
|
var id = $(this).getTemplateData({textValues: ['assessment_question_id']}).assessment_question_id;
|
|
if (id) {
|
|
existingIDs[id] = true;
|
|
}
|
|
});
|
|
$findQuestionDialog.find(".page_link").showIf(bank_data.pages && bank_data.last_page && bank_data.pages > bank_data.last_page);
|
|
updateFindQuestionDialogQuizGroups();
|
|
var $div = $("<div/>");
|
|
for(var idx in questionList) {
|
|
var question = questionList[idx].assessment_question;
|
|
if (!existingIDs[question.id] || true) {
|
|
$div.html(question.question_data.question_text);
|
|
question.question_text = TextHelper.truncateText($div.text(), {max: 75});
|
|
question.question_name = question.question_data.question_name;
|
|
var $question = $findQuestionDialog.find(".found_question.blank").clone(true).removeClass('blank');
|
|
$question.toggleClass('already_added', !!existingIDs[question.id]);
|
|
$question.fillTemplateData({data: question});
|
|
$question.find(":checkbox").attr('id', 'find_bank_question_' + question.id);
|
|
$question.find("label").attr('for', 'find_bank_question_' + question.id);
|
|
$question.data('question_data', question);
|
|
$findQuestionDialog.find(".question_list").append($question);
|
|
$question.show();
|
|
}
|
|
}
|
|
};
|
|
|
|
$("#find_question_dialog").delegate('.bank', 'click', function(event) {
|
|
event.preventDefault();
|
|
var id = $(this).getTemplateData({textValues: ['id']}).id;
|
|
var data = $findQuestionDialog.data('banks')[id];
|
|
$findQuestionDialog.find(".bank").removeClass('selected');
|
|
$findQuestionDialog.find(".selected_side_tab").removeClass('selected_side_tab');
|
|
$(this).addClass('selected_side_tab');
|
|
$findQuestionDialog.find(".page_link").data('page', 0);
|
|
if (data && data.last_page) {
|
|
$findQuestionDialog.find(".page_link").data('page', data.last_page);
|
|
}
|
|
if (!data) {
|
|
$findQuestionDialog.find(".found_question:visible").remove();
|
|
$findQuestionDialog.find(".page_link").click();
|
|
$findQuestionDialog.find(".question_list_holder").hide();
|
|
$findQuestionDialog.find(".question_message").show().text(I18n.t('loading_questions', "Loading Questions..."));
|
|
} else {
|
|
$findQuestionDialog.find(".found_question:visible").remove();
|
|
showQuestions(data);
|
|
}
|
|
}).delegate('.page_link', 'click', function(event) {
|
|
event.preventDefault();
|
|
var $link = $(this);
|
|
if ($link.hasClass('loading')) { return; }
|
|
$link.addClass('loading');
|
|
$findQuestionDialog.find(".page_link").text(I18n.t('loading_more_questions', "loading more questions..."));
|
|
var $bank = $findQuestionDialog.find(".bank.selected_side_tab");
|
|
var bank = $bank.data('bank_data');
|
|
var url = $findQuestionDialog.find(".question_bank_questions_url").attr('href');
|
|
url = $.replaceTags(url, 'question_bank_id', bank.id);
|
|
var page = ($findQuestionDialog.find(".page_link").data('page') || 0) + 1;
|
|
url += "&page=" + page;
|
|
$.ajaxJSON(url, 'GET', {}, function(data) {
|
|
$link.removeClass('loading');
|
|
$findQuestionDialog.find(".page_link").data('page', page);
|
|
$findQuestionDialog.find(".page_link").text(I18n.t('more_questions', "more questions"));
|
|
var questions = data.questions;
|
|
var banks = $findQuestionDialog.data('banks') || {};
|
|
var bank_data = banks[bank.id] || {};
|
|
bank_data.pages = data.pages;
|
|
bank_data.questions = (bank_data.questions || []).concat(data.questions);
|
|
bank_data.last_page = page;
|
|
banks[bank.id] = bank_data;
|
|
$findQuestionDialog.data('banks', banks);
|
|
$findQuestionDialog.find(".question_message").hide();
|
|
$findQuestionDialog.find(".question_list_holder").show();
|
|
showQuestions(data);
|
|
}, function(data) {
|
|
$link.removeClass('loading');
|
|
$findQuestionDialog.find(".question_message").text(I18n.t('errors.loading_questions_failed', "Questions failed to load, please try again"));
|
|
$findQuestionDialog.find(".page_link").text(I18n.t('errors.loading_more_questions_failed', "loading more questions failed"));
|
|
});
|
|
}).delegate('.select_all_link', 'click', function(event) {
|
|
event.preventDefault();
|
|
$findQuestionDialog.find(".question_list .found_question:not(.blank) :checkbox").attr('checked', true);
|
|
}).delegate('.clear_all_link', 'click', function(event) {
|
|
event.preventDefault();
|
|
$findQuestionDialog.find(".question_list .found_question:not(.blank) :checkbox").attr('checked', false);
|
|
}).delegate('.cancel_button', 'click', function(event) {
|
|
$findQuestionDialog.dialog('close');
|
|
}).delegate('.group_button', 'click', function(event) {
|
|
var $dialog = $("#add_found_questions_as_group_dialog");
|
|
var question_ids = [];
|
|
$findQuestionDialog.find(".question_list :checkbox:checked").each(function() {
|
|
question_ids.push($(this).parents(".found_question").data('question_data').id);
|
|
});
|
|
$dialog.find(".questions_count").text(question_ids.length);
|
|
$dialog.dialog({
|
|
autoOpen: false,
|
|
title: I18n.t('titles.add_questions_as_group', "Add Questions as a Group")
|
|
});
|
|
}).delegate('.submit_button', 'click', function(event) {
|
|
var question_ids = [];
|
|
$findQuestionDialog.find(".question_list :checkbox:checked").each(function() {
|
|
question_ids.push($(this).parents(".found_question").data('question_data').id);
|
|
});
|
|
var params = {};
|
|
params.quiz_group_id = $findQuestionDialog.find(".quiz_group_select").val();
|
|
params.assessment_question_bank_id = $findQuestionDialog.find(".bank.selected_side_tab:first").data('bank_data').id;
|
|
params.assessment_questions_ids = question_ids.join(',');
|
|
params.existing_questions = '1';
|
|
var url = $findQuestionDialog.find(".add_questions_url").attr('href');
|
|
$findQuestionDialog.find("button").attr('disabled', true).filter(".submit_button").text(I18n.t('buttons.adding_questions', "Adding Questions..."));
|
|
$.ajaxJSON(url, 'POST', params, function(question_results) {
|
|
$findQuestionDialog.find("button").attr('disabled', false).filter(".submit_button").text(I18n.t('buttons.add_selected_questions', "Add Selected Questions"));
|
|
$findQuestionDialog.find(".selected_side_tab").removeClass('selected_side_tab');
|
|
var counter = 0;
|
|
function nextQuestion() {
|
|
counter++;
|
|
var question = question_results.shift();
|
|
if (question) {
|
|
quiz.addExistingQuestion(question);
|
|
if (counter > 5) {
|
|
setTimeout(nextQuestion, 50);
|
|
} else {
|
|
nextQuestion();
|
|
}
|
|
}
|
|
}
|
|
setTimeout(nextQuestion, 10);
|
|
$findQuestionDialog.dialog('close');
|
|
}, function(data) {
|
|
$findQuestionDialog.find("button").attr('disabled', false).filter(".submit_button").text(I18n.t('errors.adding_questions_failed', "Adding Questions Failed, please try again"));
|
|
});
|
|
});
|
|
|
|
$(".add_answer_link").bind('click', function(event, skipFocus) {
|
|
event.preventDefault();
|
|
var $question = $(this).parents(".question");
|
|
var answers = [];
|
|
var answer_type = null, question_type = null, answer_selection_type = "single_answer";
|
|
if ($question.hasClass('multiple_choice_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_answer_comments', "Response if the student chooses this answer")
|
|
}];
|
|
answer_type = "select_answer";
|
|
question_type = "multiple_choice_question";
|
|
} else if ($question.hasClass('true_false_question')) {
|
|
return;
|
|
} else if ($question.hasClass('short_answer_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_answer_comments', "Response if the student chooses this answer")
|
|
}];
|
|
answer_type = "short_answer";
|
|
question_type = "short_answer_question";
|
|
answer_selection_type = "blanks";
|
|
} else if ($question.hasClass('essay_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_response_to_essay', "Response to show student after they submit an answer")
|
|
}];
|
|
answer_type = "comment";
|
|
question_type = "essay_question";
|
|
} else if ($question.hasClass('file_upload_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_response_to_file_upload', "Response to show student after they submit an answer")
|
|
}];
|
|
answer_type = "comment";
|
|
question_type = "file_upload_question";
|
|
} else if ($question.hasClass('matching_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_comments_on_wrong_match', "Response if the user misses this match")
|
|
}];
|
|
answer_type = "matching_answer";
|
|
question_type = "matching_question";
|
|
answer_selection_type = "matching";
|
|
} else if ($question.hasClass('missing_word_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_answer_comments', "Response if the student chooses this answer")
|
|
}];
|
|
answer_type = "short_answer";
|
|
question_type = "missing_word_question";
|
|
} else if ($question.hasClass('numerical_question')) {
|
|
var answers = [{
|
|
numerical_answer_type: "exact_answer",
|
|
answer_exact: "#",
|
|
answer_error_margin: "#",
|
|
answer_precision: "10",
|
|
comments: I18n.t('default_answer_comments_on_match', "Response if the student matches this answer")
|
|
}];
|
|
answer_type = "numerical_answer";
|
|
question_type = "numerical_question";
|
|
answer_selection_type = "any_answer";
|
|
} else if ($question.hasClass('multiple_answers_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_answer_comments', "Response if the student chooses this answer")
|
|
}];
|
|
answer_type = "select_answer";
|
|
question_type = "multiple_answers_question";
|
|
answer_selection_type = "multiple_answers";
|
|
} else if ($question.hasClass('multiple_dropdowns_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_answer_comments', "Response if the student chooses this answer")
|
|
}];
|
|
answer_type = "select_answer";
|
|
question_type = "multiple_dropdowns_question";
|
|
} else if ($question.hasClass('fill_in_multiple_blanks_question')) {
|
|
var answers = [{
|
|
comments: I18n.t('default_answer_comments', "Response if the student chooses this answer")
|
|
}];
|
|
answer_type = "short_answer";
|
|
question_type = "fill_in_multiple_blanks_question";
|
|
answer_selection_type = "blanks";
|
|
}
|
|
for(var i = 0; i < answers.length; i++) {
|
|
var answer = answers[i];
|
|
answer.answer_type = answer_type;
|
|
answer.question_type = question_type;
|
|
answer.blank_id = $question.find(".blank_id_select").val();
|
|
answer.blank_index = $question.find(".blank_id_select")[0].selectedIndex;
|
|
var $answer = makeFormAnswer(answer);
|
|
if (answer_selection_type == "any_answer") {
|
|
$answer.addClass('correct_answer');
|
|
} else if (answer_selection_type == "blanks") {
|
|
$answer.addClass("correct_answer");
|
|
$answer.addClass("fill_in_blank_answer");
|
|
} else if (answer_selection_type == "matching") {
|
|
$answer.removeClass('correct_answer');
|
|
}
|
|
$question.find(".form_answers").append($answer.show());
|
|
if (!skipFocus) {
|
|
$("html,body").scrollTo($answer);
|
|
$answer.find(":text:visible:first").focus().select();
|
|
}
|
|
}
|
|
});
|
|
|
|
$(document).delegate(".answer_comment_holder", 'click', function(event) {
|
|
$(this).find(".answer_comment").slideToggle();
|
|
});
|
|
|
|
$("#question_form_template").submit(function(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
var $displayQuestion = $(this).prev();
|
|
var $form = $(this);
|
|
$(".errorBox").not("#error_box_template").remove();
|
|
var $answers = $form.find(".answer");
|
|
var $question = $(this).find(".question");
|
|
var answers = [];
|
|
|
|
// save any open html answers or comments
|
|
$form.find('.edit_html_done').trigger('click');
|
|
var questionData = $question.getFormData({
|
|
textValues: ['question_type', 'question_name', 'question_points', 'correct_comments', 'incorrect_comments', 'neutral_comments',
|
|
'question_text', 'answer_selection_type', 'text_after_answers', 'matching_answer_incorrect_matches',
|
|
'regrade_option', 'regrade_disabled'],
|
|
htmlValues: ['correct_comments_html', 'incorrect_comments_html', 'neutral_comments_html']
|
|
});
|
|
|
|
if (questionData.question_points && questionData.question_points < 0) {
|
|
$form.find("input[name='question_points']").errorBox(I18n.t('question.positive_points', "Must be zero or greater"));
|
|
return;
|
|
}
|
|
|
|
questionData.assessment_question_bank_id = $(".question_bank_id").text() || ""
|
|
var error_text = null;
|
|
if (questionData.question_type == 'calculated_question') {
|
|
if ($form.find(".combinations_holder .combinations tbody tr").length === 0) {
|
|
error_text = I18n.t('errors.no_possible_solution', "Please generate at least one possible solution");
|
|
}
|
|
} else if ($answers.length === 0 || $answers.filter(".correct_answer").length === 0) {
|
|
if ($answers.length === 0 && !_.contains(["essay_question", "file_upload_question", "text_only_question"], questionData.question_type)) {
|
|
error_text = I18n.t('errors.no_answer', "Please add at least one answer");
|
|
} else if ($answers.filter(".correct_answer").length === 0 && (questionData.question_type == "multiple_choice_question" || questionData.question_type == "true_false_question" || questionData.question_tyep == "missing_word_question")) {
|
|
error_text = I18n.t('errors.no_correct_answer', "Please choose a correct answer");
|
|
}
|
|
} else if (questionData.question_type == "fill_in_multiple_blanks_question" || questionData.question_type == "short_answer_question") {
|
|
var checkForNotBlanks = function (elements) {
|
|
return elements.filter(function(i,element) {
|
|
return !!element.value;
|
|
}).length;
|
|
}
|
|
if (questionData.question_type == "fill_in_multiple_blanks_question") {
|
|
var $variables = $form.find(".blank_id_select > option");
|
|
$variables.each(function(i, answer_blank) {
|
|
var blankCount = 0;
|
|
$answers.filter(".answer_idx_" + i).each(function(i, element) {
|
|
var $validInputs = $(element).find( $("input[name='answer_text']")).not(".disabled_answer");
|
|
if (checkForNotBlanks($validInputs) > 0) {blankCount += 1;}
|
|
});
|
|
if (blankCount == 0) {error_text = I18n.t("Please add at least one non-blank answer for each variable.");}
|
|
});
|
|
} else {
|
|
var $validAnswers = $answers.filter("div[class*='answer_for_none'], div[class*='answer_idx_'], div.fill_in_blank_answer");
|
|
var $validInputs = $validAnswers.find( $("input[name='answer_text']")).not(".disabled_answer");
|
|
if (checkForNotBlanks($validInputs) == 0) {
|
|
error_text = I18n.t("Please add at least one non-blank answer.");
|
|
}
|
|
}
|
|
}
|
|
|
|
var isNotSurvey = $('#quiz_assignment_id').length == 0 || !$('#quiz_assignment_id').val().match(/survey/i);
|
|
if (isNotSurvey && error_text) {
|
|
$form.find(".answers_header").errorBox(error_text, true);
|
|
return;
|
|
}
|
|
var question = $.extend({}, questionData);
|
|
question.points_possible = parseFloat(question.question_points);
|
|
question.answers = [];
|
|
|
|
$displayQuestion.find(".blank_id_select").empty();
|
|
var blank_ids_hash = {};
|
|
var only_add_for_blank_ids = false;
|
|
if (question.question_type == "multiple_dropdowns_question" || question.question_type == "fill_in_multiple_blanks_question") {
|
|
only_add_for_blank_ids = true;
|
|
$question.find(".blank_id_select option").each(function() {
|
|
blank_ids_hash[$(this).text()] = true;
|
|
});
|
|
}
|
|
|
|
$question.find(".blank_id_select option").each(function() {
|
|
$displayQuestion.find(".blank_id_select").append($(this).clone());
|
|
});
|
|
|
|
var $answers = $question.find(".answer").each(function(i) {
|
|
var $answer = $(this);
|
|
$answer.show();
|
|
var data = $answer.getFormData();
|
|
data.id = $answer.find('.id').text();
|
|
data.blank_id = $answer.find(".blank_id").text();
|
|
data.answer_text = $answer.find("input[name='answer_text']:visible").val();
|
|
data.answer_html = $answer.find(".answer_html").html();
|
|
if (questionData.question_type == "true_false_question") {
|
|
data.answer_text = $answer.find(".fixed_answer .answer_text").text();
|
|
if (data.answer_text.length == 0) {
|
|
data.answer_text = (i == 0) ? I18n.t('true', "True") : I18n.t('false', "False");
|
|
}
|
|
}
|
|
if ($answer.hasClass('correct_answer')) {
|
|
data.answer_weight = 100;
|
|
} else {
|
|
data.answer_weight = 0;
|
|
}
|
|
if (only_add_for_blank_ids && data.blank_id && !blank_ids_hash[data.blank_id]) {
|
|
return;
|
|
}
|
|
question.answers.push(data);
|
|
});
|
|
if ($question.hasClass('calculated_question')) {
|
|
question.answers = [];
|
|
question.variables = [];
|
|
var sorts = {};
|
|
$question.find(".variables .variable").each(function(i) {
|
|
var data = {};
|
|
data.scale = "0";
|
|
data.name = $(this).find(".name").text();
|
|
data.scale = parseFloat($(this).find(".round").val(), 10) || 0;
|
|
data.min = parseFloat($(this).find(".min").val(), 10) || 0;
|
|
data.max = parseFloat($(this).find(".max").val(), 10) || 0;
|
|
sorts[data.name] = i;
|
|
question.variables.push(data);
|
|
});
|
|
question.formulas = [];
|
|
$question.find(".formulas .formula").each(function() {
|
|
var data = {}
|
|
data.formula = $.trim($(this).text());
|
|
question.formulas.push(data);
|
|
});
|
|
question.formula_decimal_places = parseInt($question.find(".decimal_places .round").val(), 10) || 0;
|
|
question.answer_tolerance = parseFloatOrPercentage($question.find(".combination_answer_tolerance").val());
|
|
question.answerDecimalPoints = parseFloat($question.find(".combination_error_margin").val(), 10) || 0;
|
|
var $ths = $question.find(".combinations thead th");
|
|
$question.find(".combinations tbody tr").each(function() {
|
|
var data = {};
|
|
data.variables = [];
|
|
data.answer = parseFloat($(this).find("td.final_answer").text(), 10) || 0;
|
|
$(this).find("td:not(.final_answer)").each(function(i) {
|
|
var variable = {};
|
|
variable.name = $.trim($ths.eq(i).text());
|
|
variable.value = parseFloat($(this).text(), 10) || 0;
|
|
data.variables.push(variable);
|
|
});
|
|
data.variables = data.variables.sort(function(a, b) {
|
|
return sorts[a.name] - sorts[b.name];
|
|
});
|
|
question.answers.push(data);
|
|
});
|
|
}
|
|
|
|
quiz.updateDisplayQuestion($displayQuestion, question);
|
|
|
|
var details = quiz.answerTypeDetails(question.question_type);
|
|
var answer_type = details.answer_type, question_type = details.question_type, n_correct = details.n_correct;
|
|
|
|
$form.remove();
|
|
$("html,body").scrollTo({top: $displayQuestion.offset().top - 10, left: 0});
|
|
var url = $("#quiz_urls .add_question_url,#bank_urls .add_question_url").attr('href');
|
|
var method = 'POST';
|
|
var isNew = $displayQuestion.attr('id') == "question_new";
|
|
if (!isNew) {
|
|
url = $displayQuestion.find(".update_question_url").attr('href');
|
|
method = 'PUT';
|
|
}
|
|
var oldQuestionData = questionData;
|
|
var questionData = quizData($displayQuestion);
|
|
var formData = generateFormQuiz(questionData);
|
|
var questionData = generateFormQuizQuestion(formData);
|
|
|
|
var disabled = oldQuestionData.regrade_disabled == '1';
|
|
var regradeOpt = disabled ? 'disabled' : oldQuestionData.regrade_option;
|
|
questionData['question[regrade_option]'] = regradeOpt;
|
|
|
|
if ($displayQuestion.parent(".question_holder").hasClass('group')) {
|
|
var $group = quiz.findContainerGroup($displayQuestion.parent(".question_holder"));
|
|
if ($group) {
|
|
questionData['question[quiz_group_id]'] = $group.attr('id').substring(10);
|
|
}
|
|
}
|
|
if ($("#assessment_question_bank_id").length > 0) {
|
|
questionData['assessment_question[assessment_question_bank_id]'] = $("#assessment_question_bank_id").text();
|
|
}
|
|
$displayQuestion.loadingImage();
|
|
quiz.updateDisplayComments();
|
|
$.ajaxJSON(url, method, questionData, function(data) {
|
|
$displayQuestion.loadingImage('remove');
|
|
$displayQuestion.find('.question_name').focus();
|
|
|
|
var questionData = data;
|
|
|
|
// questionData.assessment_question_id might be null now because
|
|
// question.question_data.assessment_quesiton_id might be null but
|
|
// question.assessment_question_id is the right value. because $.extend
|
|
// overwrites all keys that exist even if they have null values. this
|
|
// is hacky, the better thing to do is just get the right thing back
|
|
// from the server. it matters because the form when you click "find
|
|
// questions" uses it to see if the question already exists in this
|
|
// quiz, and is vital to properly move/copy it to another question bank
|
|
questionData.assessment_question_id = questionData.assessment_question_id || question.assessment_question_id || question.id || questionData.id;
|
|
quiz.updateDisplayQuestion($displayQuestion, questionData, true);
|
|
// Trigger a custom 'saved' event for catching and responding to change
|
|
// after save process completed. Used in quizzes_bundle.coffee
|
|
$displayQuestion.trigger('saved');
|
|
$("#unpublished_changes_message").slideDown();
|
|
if (question && questionData && questionData.id) {
|
|
REGRADE_OPTIONS[questionData.id] = question.regrade_option;
|
|
delete REGRADE_DATA['question_' + questionData.id];
|
|
}
|
|
}, function(data) {
|
|
$displayQuestion.formErrors(data);
|
|
});
|
|
});
|
|
|
|
$("#sort_questions").sortable({
|
|
revert: false,
|
|
update: function(event, ui) {
|
|
var ids = $(this).sortable('toArray');
|
|
for(var idx in ids) {
|
|
var id = ids[idx];
|
|
if (id && id != "sort_question_blank") {
|
|
$("#" + id.substring(5)).appendTo($("#questions"));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
$(document).delegate("input.float_value", 'keydown', function(event) {
|
|
if (event.keyCode > 57 && event.keyCode < 91 && event.keyCode != 69) {
|
|
event.preventDefault();
|
|
}
|
|
}).delegate('input.float_value', 'change blur focus', function(event) {
|
|
var $el = $(this)
|
|
|
|
if ($el.hasClass('long')) {
|
|
quiz.parseInput($el, 'float_long');
|
|
} else if ($el.hasClass('precision')) {
|
|
quiz.parseInput($el, 'precision', $el.siblings('.precision_value').val());
|
|
} else {
|
|
quiz.parseInput($el, 'float');
|
|
}
|
|
});
|
|
|
|
$(document).delegate("input.precision_value", 'keydown', function(event) {
|
|
// unless movement key || '0' through '9' || '-' || '+'
|
|
if (event.keyCode > 57 && event.keyCode != 189 && event.keyCode != 187) {
|
|
event.preventDefault();
|
|
}
|
|
}).delegate('input.precision_value', 'change blur focus', function(event) {
|
|
var $el = $(this);
|
|
quiz.parseInputRange($el, 'int', 1, 16);
|
|
$el.siblings('.float_value.precision').change();
|
|
});
|
|
|
|
$(document).delegate("input.combination_answer_tolerance", 'keydown', function(event) {
|
|
if (!event.metaKey && event.keyCode > 57 && event.keyCode < 91) {
|
|
event.preventDefault();
|
|
}
|
|
}).delegate('input.combination_answer_tolerance', 'change blur focus', function(event) {
|
|
quiz.validateAnswerTolerance($(this));
|
|
});
|
|
|
|
$("#questions").delegate('.question_teaser_link', 'click', function(event) {
|
|
event.preventDefault();
|
|
var $teaser = $(this).parents(".question_teaser");
|
|
var question_data = $teaser.data('question');
|
|
if (!question_data) {
|
|
$teaser.find(".teaser.question_text").text(I18n.t('loading_question', "Loading Question..."));
|
|
$.ajaxJSON($teaser.find(".update_question_url").attr('href'), 'GET', {}, function(question) {
|
|
showQuestion(question);
|
|
}, function() {
|
|
$teaser.find(".teaser.question_text").text(I18n.t('errors.loading_question_failed', "Loading Question Failed..."));
|
|
});
|
|
} else {
|
|
showQuestion(question_data);
|
|
}
|
|
function showQuestion(question_data) {
|
|
var $question = $("#question_template").clone().removeAttr('id');
|
|
var question = question_data;
|
|
var questionData = $.extend({}, question, question.question_data);
|
|
var $questionHeader = $question.find(".display_question .question_name")
|
|
$teaser.after($question);
|
|
$teaser.remove();
|
|
$question.show();
|
|
$question.find(".question_points").text(questionData.points_possible);
|
|
quiz.updateDisplayQuestion($question.find(".display_question"), questionData, true);
|
|
$questionHeader.attr('tabindex', '0');
|
|
$questionHeader.focus();
|
|
if ($teaser.hasClass('to_edit')) {
|
|
// we need to explicity set our quiz variables in the dom
|
|
// or this appears to be adding a new question instead of editing
|
|
var ques = $question.find(".question");
|
|
var link = $question.find("a.update_question_url");
|
|
var qId = $question.find(".assessment_question_id")
|
|
var href = link.attr('href');
|
|
|
|
ques.attr("id", "question_" + question_data.id);
|
|
link.attr("href", href.replace(/ions\/.*/, "ions/" + question_data.id));
|
|
qId.html(question_data.id);
|
|
|
|
$question.find(".edit_question_link").click();
|
|
}
|
|
}
|
|
}).delegate('.teaser.question_text', 'click', function(event) {
|
|
event.preventDefault();
|
|
$(this).parents(".question_teaser").find(".question_teaser_link").click();
|
|
}).delegate('.edit_teaser_link', 'click', function(event) {
|
|
event.preventDefault();
|
|
$(this).parents(".question_teaser").addClass('to_edit');
|
|
$(this).parents(".question_teaser").find(".question_teaser_link").click();
|
|
});
|
|
|
|
$(".keep_editing_link").click(function(event) {
|
|
event.preventDefault();
|
|
$(".question_generated,.question_preview").hide()
|
|
$(".question_editing").show();
|
|
$("html,body").scrollTo($("#questions"));
|
|
});
|
|
|
|
$(".quiz_group_form").formSubmit({
|
|
object_name: 'quiz_group',
|
|
|
|
// rewrite the data so that it fits the jsonapi format
|
|
processData: function(data) {
|
|
var quizGroupQuestionPoints = data['quiz_group[question_points]'];
|
|
if (quizGroupQuestionPoints && quizGroupQuestionPoints < 0) {
|
|
$(this).find("input[name='quiz_group[question_points]']").errorBox(I18n.t('question.positive_points', "Must be zero or greater"));
|
|
return false;
|
|
}
|
|
var newData = {};
|
|
_.each(data, function(val, key) {
|
|
newData[key.replace('quiz_group[', 'quiz_groups[][')] = val;
|
|
});
|
|
return newData;
|
|
},
|
|
beforeSubmit: function(formData) {
|
|
var $form = $(this);
|
|
var $group = $form.parents(".group_top");
|
|
$group.fillTemplateData({
|
|
data: formData
|
|
}).removeClass('editing');
|
|
$form.loadingImage();
|
|
},
|
|
success: function(data) {
|
|
var $form = $(this);
|
|
var $group = $form.parents(".group_top");
|
|
var groups = data.quiz_groups;
|
|
var group = groups[0];
|
|
$form.loadingImage('remove');
|
|
$group.removeClass('editing');
|
|
$group.fillTemplateData({
|
|
data: group,
|
|
id: 'group_top_' + group.id,
|
|
hrefValues: ['id']
|
|
});
|
|
$group.toggleClass('question_bank_top', !!group.assessment_question_bank_id);
|
|
|
|
var $bank = $group.next('.assessment_question_bank');
|
|
if (!group.assessment_question_bank_id) {
|
|
$bank.remove();
|
|
} else if ($bank.data('bank_data')) {
|
|
var bank = $bank.data('bank_data');
|
|
bank.bank_id = bank.id;
|
|
bank.context_type_string = pluralize($.underscore(bank.context_type));
|
|
$group.data('bank_question_count', bank.assessment_question_count);
|
|
$group.next(".assessment_question_bank").fillTemplateData({data: bank, hrefValues: ['bank_id', 'context_type_string', 'context_id']})
|
|
.find(".bank_name").hide().filter(".bank_name_link").show();
|
|
}
|
|
|
|
$group.find(".find_bank_link").hide();
|
|
$group.fillFormData(data, {object_name: 'quiz_group'});
|
|
var $bottom = $group.next();
|
|
while($bottom.length > 0 && !$bottom.hasClass('group_bottom')) {
|
|
$bottom = $bottom.next();
|
|
}
|
|
|
|
if ($('#insufficient_count_warning_' + group.id).length == 0) {
|
|
var $warning = $("#insufficient_count_warning_template").clone(true).attr('id', ('insufficient_count_warning_' + group.id));
|
|
$bottom.before($warning);
|
|
}
|
|
|
|
$("#unpublished_changes_message").slideDown();
|
|
$bottom.attr('id', 'group_bottom_' + group.id);
|
|
quiz.updateDisplayComments();
|
|
},
|
|
error: function(data) {
|
|
var $form = $(this);
|
|
var $group = $form.parents(".group_top");
|
|
$group.addClass('editing');
|
|
$form.loadingImage('remove');
|
|
$form.formErrors(data);
|
|
}
|
|
});
|
|
|
|
// accessible sortables
|
|
var accessibleSortables = {
|
|
|
|
init: function(target, questions, form) {
|
|
this.$questions = questions;
|
|
this.$form = form;
|
|
|
|
this.selected = this.selectedItem(target);
|
|
this.items = this.sortableItems();
|
|
this.intoGroups = this.findGroups();
|
|
|
|
this.buildHeader();
|
|
this.buildGroupMenu();
|
|
this.showDialog();
|
|
},
|
|
|
|
selectedItem: function(target) {
|
|
var target = $(target);
|
|
var group = target.closest(".group_top");
|
|
var holder = target.closest(".question");
|
|
var parent = group.length > 0 ? group : holder;
|
|
|
|
return {
|
|
id: parent.attr('id').replace(/group_top_|question_/, ""),
|
|
type: group.length > 0 ? 'group' : 'question',
|
|
name: parent.find(".name").text(),
|
|
sortable: group.length > 0 ? group : holder.parent()
|
|
}
|
|
},
|
|
|
|
sortableItems: function() {
|
|
return this.$questions.children('.quiz_sortable').map(function(i, item) {
|
|
var item = $(item);
|
|
var sortable = item;
|
|
|
|
// the group each question belongs to
|
|
var groupId;
|
|
if (item.hasClass('group')) {
|
|
groupId = item.prevAll('.group_top').attr('id').replace(/group_top_/, "")
|
|
}
|
|
|
|
// scope down to question to get id
|
|
if (item.hasClass('question_holder')) {
|
|
item = item.find('.question');
|
|
}
|
|
|
|
return {
|
|
id: item.attr('id').replace(/group_top_|question_/, ""),
|
|
type: item.hasClass('group_top') ? 'group' : 'question',
|
|
name: item.find('.name').text(),
|
|
group: groupId,
|
|
top: !item.parent().hasClass('group'),
|
|
sortable: sortable
|
|
}
|
|
});
|
|
},
|
|
|
|
// we can move item to a group if
|
|
// 1. the selected item is a question
|
|
// 2. there are groups on the quiz
|
|
findGroups: function() {
|
|
var intoGroups = [];
|
|
if (this.selected.type == 'question') {
|
|
intoGroups = $.grep(this.items, function(item) {
|
|
return item.type == 'group';
|
|
});
|
|
}
|
|
return intoGroups;
|
|
},
|
|
|
|
buildHeader: function() {
|
|
this.$form.find('.quiz_item_name').text(this.selected.name);
|
|
},
|
|
|
|
buildGroupMenu: function() {
|
|
var options = [];
|
|
var moveWrapper = this.$form.find(".move_select_group");
|
|
var moveSelect = this.$form.find("#move_select_group");
|
|
|
|
if (this.intoGroups.length > 0) {
|
|
options = $.map(this.intoGroups, function(g) {
|
|
return '<option value="' + g.id + '">' + htmlEscape(g.name) + '</option>';
|
|
});
|
|
options.unshift('<option value="top">' +
|
|
htmlEscape(I18n.t('top_level', '-- Top level --')) +
|
|
'</option>');
|
|
|
|
moveWrapper.show();
|
|
} else {
|
|
moveWrapper.hide();
|
|
}
|
|
moveSelect.html($.raw(options.join('')));
|
|
|
|
// trigger building 'place' menu
|
|
moveSelect.change(this.buildPlaceMenu.bind(this));
|
|
moveSelect.trigger('change');
|
|
},
|
|
|
|
buildPlaceMenu: function(event) {
|
|
var option = $(event.target).find('option:selected');
|
|
var value = option.length > 0 ? option.attr('value') : 'top';
|
|
|
|
// filter by selected
|
|
var filtered = this.itemsInGroup(value);
|
|
|
|
// build options
|
|
var options = $.map(filtered, function(item) {
|
|
return '<option value="' + htmlEscape(item.type) + '_' + item.id + '">' +
|
|
htmlEscape(I18n.t('before_quiz_item', "before %{name}", {name: item.name})) +
|
|
'</option>';
|
|
});
|
|
options.push('<option value="last">' +
|
|
htmlEscape(I18n.t('at_the_bottom', "-- at the bottom --")) +
|
|
'</option>');
|
|
this.$form.find('#move_select_question').html($.raw(options.join('')));
|
|
},
|
|
|
|
itemsInGroup: function(group) {
|
|
return $.grep(this.items, function(item) {
|
|
return item.id != this.selected.id &&
|
|
(group == 'top' ? item.top : item.group == group);
|
|
}.bind(this));
|
|
},
|
|
|
|
showDialog: function() {
|
|
var displayGroupSelector = this.intoGroups.length > 0;
|
|
|
|
var dialog = this.$form.dialog({
|
|
autoOpen: false,
|
|
modal: true,
|
|
width: 400,
|
|
height: displayGroupSelector ? 345: 265,
|
|
close: this.removeEventListeners.bind(this),
|
|
open: this.focusDialog.bind(this)
|
|
}).dialog('open');
|
|
|
|
this.$form.find("h2").focus();
|
|
|
|
this.$form.find('#move_quiz_item_cancel_btn').on('click keyclick', function() {
|
|
this.$form.filter(":visible").dialog('close');
|
|
}.bind(this));
|
|
|
|
this.$form.find('#move_quiz_item_submit_btn').on('click keyclick', this.saveMove.bind(this));
|
|
},
|
|
|
|
focusDialog: function() {
|
|
this.$form.find("h2").focus();
|
|
},
|
|
|
|
removeEventListeners: function() {
|
|
this.$form.find('#move_quiz_item_cancel_btn').off();
|
|
this.$form.find('#move_quiz_item_submit_btn').off();
|
|
this.$form.find('#move_select_group').off();
|
|
},
|
|
|
|
saveMove: function(event) {
|
|
event.preventDefault();
|
|
|
|
// get selected values
|
|
var option = this.$form.find("#move_select_group option:selected");
|
|
var group = option.length > 0 ? option.attr('value') : 'top';
|
|
|
|
this.reorderDom(group);
|
|
this.ajaxPostReorder(group);
|
|
|
|
// close form
|
|
this.$form.filter(":visible").dialog('close');
|
|
},
|
|
|
|
reorderDom: function(group) {
|
|
var option = this.$form.find("#move_select_question option:selected");
|
|
var place = option.attr('value');
|
|
var bottom = this.selected.sortable.nextAll('.group_bottom').first();
|
|
|
|
// move to bottom of the group
|
|
if (place == 'last') {
|
|
var inGroup = this.itemsInGroup(group);
|
|
|
|
// there is at least 1 item already in the group
|
|
if (inGroup.length > 0) {
|
|
var lastItem = inGroup[inGroup.length - 1].sortable;
|
|
if (lastItem.hasClass('group_top')) {
|
|
lastItem = lastItem.nextAll('.group_bottom');
|
|
}
|
|
lastItem.after(this.selected.sortable);
|
|
|
|
// adding as the first item in the group
|
|
} else {
|
|
var groupElt = this.$questions.find("#group_top_"+group);
|
|
groupElt.after(this.selected.sortable);
|
|
}
|
|
|
|
// move to before the item chosen
|
|
} else {
|
|
var beforeItem = $.grep(this.items, function(item) {
|
|
return item.type + "_" + item.id == place;
|
|
})[0].sortable;
|
|
beforeItem.before(this.selected.sortable);
|
|
}
|
|
|
|
this.reorderDomGroupContent(bottom);
|
|
this.setQuestionGroupClass(group);
|
|
},
|
|
reorderDomGroupContent: function($bottom) {
|
|
if (this.selected.type != 'group') { return; }
|
|
|
|
var prev = this.selected.sortable;
|
|
var items = this.itemsInGroup(this.selected.id);
|
|
if (items.length) {
|
|
$.each(items, function(i, item) {
|
|
prev.after(item.sortable);
|
|
prev = item.sortable;
|
|
});
|
|
}
|
|
prev.after($bottom);
|
|
},
|
|
setQuestionGroupClass: function(group) {
|
|
if (this.selected.type != 'question') { return; }
|
|
|
|
if (group == 'top') {
|
|
this.selected.sortable.removeClass('group');
|
|
} else {
|
|
this.selected.sortable.addClass('group');
|
|
}
|
|
},
|
|
|
|
ajaxPostReorder: function(group) {
|
|
var params = this.buildGroupParams(group);
|
|
|
|
var item;
|
|
if (group == 'top') {
|
|
item = $("#quiz_urls .reorder_questions_url, #bank_urls .reorder_questions_url");
|
|
} else {
|
|
item = $("#group_top_" + group).find(".reorder_group_questions_url");
|
|
}
|
|
var url = item.attr('href');
|
|
this.$questions.loadingImage();
|
|
|
|
$.ajaxJSON(url, 'POST', JSON.stringify({order: params}), function(data) {
|
|
this.$questions.loadingImage('remove');
|
|
}.bind(this), {}, {contentType: "application/json"});
|
|
},
|
|
|
|
buildGroupParams: function(group) {
|
|
var option = this.$form.find("#move_select_question option:selected");
|
|
var place = option.attr('value');
|
|
|
|
// rebuild the group list adding in our selection
|
|
var selected = this.selected;
|
|
var params = [];
|
|
$.each(this.itemsInGroup(group), function(i, item) {
|
|
if (item.type + '_' + item.id == place) {
|
|
params.push({type: selected.type, id: selected.id});
|
|
}
|
|
params.push({type: item.type, id: item.id});
|
|
}.bind(this));
|
|
|
|
if (place == 'last') {
|
|
params.push({type: selected.type, id: selected.id});
|
|
}
|
|
return params;
|
|
}
|
|
}
|
|
$(document).on('click keydown', ".draggable-handle", function(event) {
|
|
if (event.type == "keydown" && event.keyCode != 13 && event.keyCode != 32) { return; }
|
|
event.preventDefault();
|
|
|
|
accessibleSortables.init(
|
|
$(event.target), $("#questions"), $("#move_quiz_item_form")
|
|
);
|
|
});
|
|
$(document).on('focus blur', ".draggable-handle", function(e) {
|
|
var warning = $(e.target).find('.accessibility-warning');
|
|
warning[e.type == 'focusin' ? 'removeClass' : 'addClass']('screenreader-only');
|
|
});
|
|
|
|
$("#questions").sortable({
|
|
handle: '.draggable-handle',
|
|
helper: function(event, ui) {
|
|
return ui.clone().removeClass('group');
|
|
},
|
|
items: '.group_top,.group_bottom,.question_holder',
|
|
tolerance: 'pointer',
|
|
start: function(event, ui) {
|
|
ui.placeholder.css('visibility', 'visible');
|
|
if (ui.item.hasClass('group_top')) {
|
|
ui.helper.addClass('dragging');
|
|
var $obj = ui.item;
|
|
var take_with = []
|
|
while($obj.length > 0 && !$obj.hasClass('group_bottom')) {
|
|
$obj = $obj.next();
|
|
if (!$obj.hasClass('ui-sortable-placeholder')) {
|
|
take_with.push({item: $obj, visible: $obj.is(':visible')});
|
|
$obj.hide();
|
|
}
|
|
}
|
|
ui.item.data('take_with', take_with);
|
|
ui.placeholder.show();
|
|
} else {
|
|
if (quiz.findContainerGroup(ui.placeholder)) {
|
|
ui.placeholder.addClass('group');
|
|
} else {
|
|
ui.placeholder.removeClass('group');
|
|
}
|
|
ui.placeholder.append("<div class='question_placeholder' style='height: " + $.raw(ui.helper.height() - 10) + "px;'> </div>");
|
|
}
|
|
},
|
|
change: function(event, ui) {
|
|
var $group = quiz.findContainerGroup(ui.placeholder);
|
|
if (ui.item.hasClass('group_top')) {
|
|
if ($group) {
|
|
$group.before(ui.placeholder);
|
|
$("html,body").scrollTo(ui.placeholder);
|
|
} else {
|
|
}
|
|
} else {
|
|
if ($group) {
|
|
if ($group.attr('id') == 'group_top_new') {
|
|
$group.before(ui.placeholder);
|
|
$("html,body").scrollTo(ui.placeholder);
|
|
} else {
|
|
if ($group.hasClass('question_bank_top')){
|
|
// Groups that point to question banks aren't allowed to have questions
|
|
$group.before(ui.placeholder).addClass('group');
|
|
}else{
|
|
ui.placeholder.addClass('group');
|
|
}
|
|
}
|
|
} else {
|
|
ui.placeholder.removeClass('group');
|
|
}
|
|
ui.placeholder.height(ui.helper.height()).find(".question_placeholder").height(ui.helper.height() - 10);
|
|
}
|
|
},
|
|
stop: function(event, ui) {
|
|
if (ui.item.hasClass('group_top')) {
|
|
var take_with = ui.item.data('take_with');
|
|
if (take_with) {
|
|
var $obj = ui.item;
|
|
for(var idx in take_with) {
|
|
var data = take_with[idx];
|
|
var $item = data.item;
|
|
if (data.visible) $item.show();
|
|
$obj.after($item);
|
|
$obj = $item;
|
|
}
|
|
}
|
|
} else {
|
|
if (quiz.findContainerGroup(ui.item)) {
|
|
ui.item.addClass('group');
|
|
var $obj = ui.item.prev();
|
|
while($obj.length > 0 && !$obj.hasClass('group_top')) {
|
|
$obj = $obj.prev();
|
|
}
|
|
$obj.find(".expand_link").click();
|
|
} else {
|
|
ui.item.removeClass('group');
|
|
}
|
|
}
|
|
|
|
var url = $("#quiz_urls .reorder_questions_url, #bank_urls .reorder_questions_url").attr('href');
|
|
var data = {};
|
|
var $container = $("#questions");
|
|
var items = [];
|
|
if (quiz.findContainerGroup(ui.item)) {
|
|
$container = quiz.findContainerGroup(ui.item);
|
|
var $list = [];
|
|
url = $container.find(".reorder_group_questions_url").attr('href');
|
|
var $obj = $container.next();
|
|
while($obj.length > 0 && !$obj.hasClass('group_bottom')) {
|
|
items.push($obj);
|
|
$obj = $obj.next();
|
|
}
|
|
} else {
|
|
$container.children(".question_holder:not(.group),.group_top").each(function() {
|
|
items.push($(this));
|
|
});
|
|
}
|
|
$container.loadingImage();
|
|
var list = [];
|
|
|
|
$.each(items, function(i, $obj) {
|
|
var object;
|
|
if ($obj.hasClass('question_holder')) {
|
|
var $question = $obj.find('.question');
|
|
var attrID = $question.attr('id');
|
|
var id = attrID ? attrID.substring(9) : $question.find(".id").text();
|
|
object = {'type': 'question', 'id': id};
|
|
|
|
} else {
|
|
var id = $obj.attr('id').substring(10);
|
|
object = {'type': 'group', 'id': id};
|
|
}
|
|
list.push(object)
|
|
});
|
|
|
|
$.ajaxJSON(url, 'POST', JSON.stringify({order: list}), function(data) {
|
|
$container.loadingImage('remove');
|
|
}, {}, {contentType: "application/json"});
|
|
}
|
|
});
|
|
|
|
$(document).delegate(".edit_group_link", 'click', function(event) {
|
|
if ($(this).closest('.group_top').length == 0) { return; }
|
|
event.preventDefault();
|
|
var $top = $(this).parents(".group_top");
|
|
var data = $top.getTemplateData({textValues: ['name', 'pick_count', 'question_points']});
|
|
$top.fillFormData(data, {object_name: 'quiz_group'});
|
|
$top.addClass('editing');
|
|
$top.find(":text:visible:first").focus().select();
|
|
$top.find(".quiz_group_form").attr('action', $top.find(".update_group_url").attr('href'))
|
|
.attr('method', 'PUT');
|
|
$top.find(".submit_button").text(I18n.t('buttons.update_group', "Update Group"));
|
|
}).delegate(".delete_group_link", 'click', function(event) {
|
|
if ($(this).closest('.group_top').length == 0) { return; }
|
|
event.preventDefault();
|
|
var $top = $(this).parents(".group_top");
|
|
var $list = $("nothing").add($top);
|
|
var $next = $top.next();
|
|
while($next.length > 0 && !$next.hasClass('group_bottom')) {
|
|
$list = $list.add($next);
|
|
$next = $next.next();
|
|
}
|
|
$list = $list.add($next);
|
|
$top.confirmDelete({
|
|
url: $top.find(".update_group_url").attr('href'),
|
|
confirmed: function() {
|
|
$list.dim();
|
|
},
|
|
success: function() {
|
|
$list.fadeOut(function() {
|
|
$(this).remove();
|
|
quiz.updateDisplayComments();
|
|
});
|
|
}
|
|
});
|
|
}).delegate(".group_edit.cancel_button", 'click', function(event) {
|
|
if ($(this).closest('.group_top').length == 0) { return; }
|
|
var $top = $(this).parents(".group_top");
|
|
$top.removeClass('editing');
|
|
if ($top.attr('id') == 'group_top_new') {
|
|
var $next = $top.next();
|
|
while($next.length > 0 && !$next.hasClass('group_bottom')) {
|
|
var $current = $next
|
|
$next.removeClass('group');
|
|
$next = $next.next();
|
|
if ($current.hasClass('assessment_question_bank')) {
|
|
$current.remove();
|
|
}
|
|
}
|
|
$next.remove();
|
|
$top.remove();
|
|
}
|
|
quiz.updateDisplayComments();
|
|
}).delegate(".collapse_link", 'click', function(event) {
|
|
if ($(this).closest('.group_top').length == 0) { return; }
|
|
event.preventDefault();
|
|
$(this).parents(".group_top").find(".collapse_link").addClass('hidden').end()
|
|
.find(".expand_link").removeClass('hidden').focus();
|
|
var $obj = $(this).parents(".group_top").next();
|
|
while($obj.length > 0 && $obj.hasClass('question_holder')) {
|
|
$obj.hide();
|
|
$obj = $obj.next();
|
|
}
|
|
}).delegate(".expand_link", 'click', function(event) {
|
|
if ($(this).closest('.group_top').length == 0) { return; }
|
|
event.preventDefault();
|
|
$(this).parents(".group_top").find(".collapse_link").removeClass('hidden').focus().end()
|
|
.find(".expand_link").addClass('hidden');
|
|
var $obj = $(this).parents(".group_top").next();
|
|
while($obj.length > 0 && $obj.hasClass('question_holder')) {
|
|
$obj.show();
|
|
$obj = $obj.next();
|
|
}
|
|
});
|
|
|
|
RichContentEditor.initSidebar();
|
|
|
|
var keyboardShortcutsView = new RCEKeyboardShortcuts();
|
|
|
|
if (!lockedItems.content) {
|
|
RichContentEditor.loadNewEditor($('#quiz_description'), {
|
|
focus: true,
|
|
manageParent: true,
|
|
tinyOptions: {
|
|
aria_label: I18n.t('label.quiz.instructions', 'Quiz instructions, rich text area')
|
|
}
|
|
})
|
|
}
|
|
keyboardShortcutsView.render().$el.insertBefore($(".toggle_description_views_link:first"));
|
|
|
|
$(".toggle_description_views_link").click(function(event) {
|
|
event.preventDefault();
|
|
RichContentEditor.callOnRCE($("#quiz_description"), 'toggle');
|
|
// todo: replace .andSelf with .addBack when JQuery is upgraded.
|
|
$(this).siblings(".toggle_description_views_link").andSelf().toggle();
|
|
});
|
|
|
|
$(".toggle_question_content_views_link").click(function(event) {
|
|
event.preventDefault();
|
|
RichContentEditor.callOnRCE($(this).parents(".question_form").find(".question_content"), 'toggle');
|
|
// todo: replace .andSelf with .addBack when JQuery is upgraded.
|
|
$(this).siblings(".toggle_question_content_views_link").andSelf().toggle();
|
|
});
|
|
|
|
$(".toggle_text_after_answers_link").click(function(event) {
|
|
event.preventDefault();
|
|
RichContentEditor.callOnRCE($(this).parents(".question_form").find(".text_after_answers"), 'toggle');
|
|
// todo: replace .andSelf with .addBack when JQuery is upgraded.
|
|
$(this).siblings(".toggle_text_after_answers_link").andSelf().toggle();
|
|
});
|
|
|
|
$("#calc_helper_methods").change(function() {
|
|
var method = $(this).val();
|
|
$("#calc_helper_method_description").text(calcCmd.functionDescription(method));
|
|
var html = "<pre>" + $.raw($.map(calcCmd.functionExamples(method), htmlEscape).join("</pre><pre>")) + "</pre>";
|
|
$("#calc_helper_method_examples").html(html);
|
|
});
|
|
|
|
$("#equations_dialog_tabs").tabs();
|
|
|
|
$(".delete_quiz_link").click(function(event) {
|
|
event.preventDefault();
|
|
$(this).parents(".quiz").confirmDelete({
|
|
message: I18n.t('confirms.delete_quiz', "Are you sure you want to delete this quiz?"),
|
|
url: $(this).attr('href'),
|
|
success: function() { window.location.replace(ENV.QUIZZES_URL); }
|
|
});
|
|
});
|
|
|
|
if (ENV['CONDITIONAL_RELEASE_SERVICE_ENABLED']) {
|
|
var conditionalRelease = window.conditionalRelease = window.conditionalRelease || {};
|
|
conditionalRelease.editor = ConditionalRelease.attach(
|
|
$('#conditional_release_target').get(0),
|
|
I18n.t('quiz'),
|
|
ENV['CONDITIONAL_RELEASE_ENV']);
|
|
|
|
$('#questions').on("change DOMNodeRemoved DOMNodeInserted", function() {
|
|
conditionalRelease.assignmentUpToDate = false;
|
|
});
|
|
$("#quiz_tabs").on("tabsbeforeactivate", function(event) {
|
|
if (!conditionalRelease.assignmentUpToDate) {
|
|
var id = null;
|
|
if (quizModel) {
|
|
id = quizModel.attributes.assignment_id;
|
|
}
|
|
conditionalRelease.editor.updateAssignment({
|
|
id: id,
|
|
grading_type: "points",
|
|
points_possible: quiz.calculatePointsPossible()
|
|
});
|
|
conditionalRelease.assignmentUpToDate = true;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$.fn.multipleAnswerSetsQuestion = function () {
|
|
var $question = $(this);
|
|
var $questionContent = $question.find('.question_content');
|
|
var $select = $question.find('.blank_id_select');
|
|
var questionType = $question.find('.question_type').val();
|
|
|
|
if ($question.data('multiple_sets_question_bindings')) { return; }
|
|
$question.data('multiple_sets_question_bindings', true);
|
|
|
|
$questionContent.bind('keypress', function (event) {
|
|
setTimeout(function () { $(event.target).triggerHandler('change'); }, 50);
|
|
});
|
|
|
|
if (!isChangeMultiFuncBound($questionContent)) {
|
|
$questionContent.bind('change', getChangeMultiFunc($questionContent, questionType, $select)).change();
|
|
}
|
|
|
|
$select.change(function () {
|
|
if (questionType !== 'multiple_dropdowns_question' && questionType !== 'fill_in_multiple_blanks_question') {
|
|
return;
|
|
}
|
|
$question.find('.form_answers .answer').hide().addClass('hidden');
|
|
$select.find('option').each(function (i) {
|
|
$question.find('.form_answers .answer_for_' + $(this).val()).each(function () {
|
|
$(this).attr('class', $(this).attr('class').replace(/answer_idx_\d+/g, ''));
|
|
}).addClass('answer_idx_' + i);
|
|
});
|
|
if ($select.val() !== '0') {
|
|
var variable = $select.val();
|
|
var variableIdx = $select[0].selectedIndex;
|
|
if (variableIdx >= 0) {
|
|
$question.find('.form_answers .answer').each(function () {
|
|
var $this = $(this);
|
|
if (!$this.attr('class').match(/answer_idx_/)) {
|
|
if ($this.attr('class').match(/answer_for_/)) {
|
|
var idx = null;
|
|
var blankId = $this.attr('class').match(/answer_for_[^\s]+/);
|
|
if (blankId && blankId[0]) { blankId = blankId[0].substring(11); }
|
|
$select.find('option').each(function (i) {
|
|
if ($(this).text() == blankId) {
|
|
idx = i;
|
|
}
|
|
});
|
|
if (idx === null) {
|
|
idx = variableIdx;
|
|
}
|
|
$this.addClass('answer_idx_' + idx);
|
|
} else {
|
|
$this.addClass('answer_idx_' + variableIdx);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
$select.find("option").each(function(i) {
|
|
var text = $(this).text();
|
|
$question.find(".form_answers .answer.answer_idx_" + i).find(".blank_id").each(function() {
|
|
$(this).text(text);
|
|
});
|
|
});
|
|
var $valid_answers = $question.find(".form_answers .answer.answer_idx_" + variableIdx).show().removeClass('hidden');
|
|
if (!$valid_answers.length && variable && variable !== '0') {
|
|
for(var idx = 0; idx < 2; idx++) {
|
|
$question.find(".add_answer_link").triggerHandler('click', true);
|
|
}
|
|
$valid_answers = $question.find(".form_answers .answer.answer_idx_" + variableIdx).show().removeClass('hidden');
|
|
}
|
|
if (!$valid_answers.filter(".correct_answer").length) {
|
|
$valid_answers.filter(":first").addClass('correct_answer');
|
|
}
|
|
$valid_answers.each(function() {
|
|
$(this).find(".blank_id").text(variable);
|
|
});
|
|
}
|
|
}).change();
|
|
}
|
|
|
|
$.fn.formulaQuestion = function() {
|
|
var $question = $(this);
|
|
if ($question.data('formula_question_bindings')) { return; }
|
|
$question.data('formula_question_bindings', true);
|
|
$question.find(".supercalc").superCalc({
|
|
pre_process: function() {
|
|
var result = [];
|
|
$question.find(".variables .variable").each(function() {
|
|
var data = {
|
|
name: $(this).attr('data-name'),
|
|
value: $(this).attr('data-value')
|
|
};
|
|
result.push(data.name + " = " + data.value);
|
|
});
|
|
return result;
|
|
},
|
|
formula_added: function() {
|
|
$question.triggerHandler('settings_change', true);
|
|
}
|
|
});
|
|
$question.find(".compute_combinations").click(function() {
|
|
var $button = $(this);
|
|
$button.text(I18n.t('buttons.generating', "Generating...")).attr('disabled', true);
|
|
var question_type = $question.find(".question_type").val();
|
|
if (question_type != 'calculated_question') {
|
|
return;
|
|
}
|
|
var $table = $question.find(".combinations");
|
|
$table.find("thead tr").empty();
|
|
$question.find(".variables .variable").each(function() {
|
|
var $th = $("<th/>");
|
|
$th.text($(this).find(".name").text());
|
|
$table.find("thead tr").append($th);
|
|
});
|
|
var $th = $("<th/>");
|
|
$th.text(I18n.t('final_answer', "Final Answer"));
|
|
$th.addClass('final_answer');
|
|
$table.find("thead tr").append($th);
|
|
$table.find("tbody").empty();
|
|
var cnt = parseInt($question.find(".combination_count").val(), 10) || 10;
|
|
if (cnt < 0) {
|
|
cnt = 10;
|
|
} else if (cnt > ENV.quiz_max_combination_count) {
|
|
cnt = ENV.quiz_max_combination_count;
|
|
}
|
|
$question.find(".combination_count").val(cnt);
|
|
var succeeded = 0;
|
|
var existingCombinations = {};
|
|
var mod = 0;
|
|
var finished = function() {
|
|
$question.find(".supercalc").superCalc('clear_cached_finds');
|
|
$button.text("Generate").attr('disabled', false);
|
|
if (succeeded == 0) {
|
|
alert(I18n.t('alerts.no_valid_combinations', "The system could not generate any valid combinations for the parameters given"));
|
|
} else if (succeeded < cnt) {
|
|
alert(I18n.t('alerts.only_n_valid_combinations', {'one': "The system could only generate 1 valid combination for the parameters given", 'other': "The system could only generate %{count} valid combinations for the parameters given"}, {'count': succeeded}));
|
|
}
|
|
$question.triggerHandler('settings_change', false);
|
|
};
|
|
var combinationIndex = 0;
|
|
var failedCount = 0;
|
|
var $status = $question.find(".formulas .formula_row:last .status"),
|
|
$variable_values = $question.find(".variables .variable"),
|
|
$tbody = $table.find("tbody");
|
|
$question.find(".supercalc").superCalc('cache_finds');
|
|
var answer_tolerance = parseFloatOrPercentage($question.find(".combination_answer_tolerance").val());
|
|
var next = function() {
|
|
$button.text(I18n.t('buttons.generating_combinations_progress', "Generating... (%{done}/%{total})", {'done': succeeded, 'total': cnt}));
|
|
var fragment = document.createDocumentFragment();
|
|
for(var idx = 0; idx < 5 && succeeded < cnt && failedCount < 25; idx++) {
|
|
$variable_values.each(function() {
|
|
$(this).find(".variable_setting:first").trigger('change', {cache: true});
|
|
});
|
|
$question.find(".supercalc").superCalc('recalculate', true);
|
|
var result = $status.attr('data-res');
|
|
var solution = new QuizFormulaSolution(result);
|
|
var combination = [];
|
|
$variable_values.each(function() {
|
|
combination.push($(this).attr('data-value'));
|
|
});
|
|
if (!existingCombinations[combination] || true) {
|
|
if (solution.isValid()) {
|
|
var $result = $("<tr/>");
|
|
$variable_values.each(function() {
|
|
var $td = $("<td/>");
|
|
$td.html($(this).attr('data-value'));
|
|
$result.append($td);
|
|
});
|
|
var $td = $("<td/>");
|
|
$td.addClass('final_answer');
|
|
var html = htmlEscape(solution.rawText());
|
|
var tolerance = answer_tolerance;
|
|
if (tolerance) {
|
|
html += " <span style='font-size: 0.8em;'>+/-</span> " + htmlEscape(tolerance);
|
|
}
|
|
$td.html(html);
|
|
$result.append($td);
|
|
succeeded++;
|
|
failedCount = 0;
|
|
fragment.appendChild($result[0]);
|
|
} else {
|
|
failedCount++;
|
|
}
|
|
existingCombinations[combination] = true;
|
|
} else {
|
|
failedCount++;
|
|
}
|
|
}
|
|
$tbody[0].appendChild(fragment);
|
|
|
|
$button.text(I18n.t('buttons.generating_combinations_progress', "Generating... (%{done}/%{total})", {'done': succeeded, 'total': cnt}));
|
|
if (combinationIndex >= cnt || succeeded >= cnt || failedCount >= 25) {
|
|
finished();
|
|
return;
|
|
} else {
|
|
combinationIndex++;
|
|
setTimeout(function() {
|
|
next();
|
|
}, 500);
|
|
}
|
|
};
|
|
setTimeout(next, 100);
|
|
});
|
|
$question.find(".recompute_variables").click(function() {
|
|
var question_type = $question.find(".question_type").val();
|
|
if (question_type != 'calculated_question') {
|
|
return;
|
|
}
|
|
$question.triggerHandler('recompute_variables', true);
|
|
});
|
|
$question.bind('recompute_variables', function(event, in_dom) {
|
|
$question.find(".variables .variable").each(function() {
|
|
$(this).find(".variable_setting:first").trigger('change', in_dom ? { recompute: true, cache: true } : { cache: true });
|
|
});
|
|
});
|
|
$question.bind('settings_change', function(event, remove) {
|
|
var question_type = $question.find(".question_type").val();
|
|
if (question_type != 'calculated_question') {
|
|
return;
|
|
}
|
|
var variables = $question.find(".variables tbody tr.variable").length > 0;
|
|
var formulas = $question.find(".formulas .formula").length > 0;
|
|
|
|
$question.find(".combinations_option").attr('disabled', !variables || !formulas);
|
|
$question.find(".variables_specified").showIf(variables);
|
|
$question.find(".formulas_specified").showIf(formulas);
|
|
if ($question.hasClass('ready') && remove) {
|
|
$question.find(".combinations_holder .combinations tbody tr").remove();
|
|
}
|
|
$question.find(".combinations_holder").showIf($question.find(".combinations tbody tr").length > 0);
|
|
});
|
|
|
|
$question.find(".variables").delegate('.variable_setting', 'change', function(event, options) {
|
|
var question_type = $question.find(".question_type").val();
|
|
if (question_type != 'calculated_question') {
|
|
return;
|
|
}
|
|
var $variable = $(event.target).parents(".variable")
|
|
var data = $variable.data('cached_data');
|
|
if (!data || !options || !options.cache || (options.recompute && options.cache)) {
|
|
data = $variable.getFormData();
|
|
data.min = parseFloat(data.min) || 0;
|
|
data.max = Math.max(data.min, parseFloat(data.max) || 0);
|
|
data.round = parseInt(data.round, 10) || 0;
|
|
data.range = data.max - data.min;
|
|
data.rounder = Math.pow(10, data.round) || 1;
|
|
}
|
|
if (options && options.cache) {
|
|
$variable.data('cached_data', data);
|
|
}
|
|
var val = (Math.random() * data.range) + data.min;
|
|
val = Math.round(val * data.rounder) / (data.rounder);
|
|
$variable.attr('data-value', val);
|
|
if (!options || options.template || options.recompute) {
|
|
$variable.find(".value").text(val);
|
|
}
|
|
if (!options || options.recompute) {
|
|
$question.find(".supercalc").superCalc('recalculate');
|
|
$question.triggerHandler('settings_change', true);
|
|
}
|
|
});
|
|
$question.find(".help_with_equations_link").click(function(event) {
|
|
event.preventDefault();
|
|
$("#calc_helper_methods").empty();
|
|
var functions = calcCmd.functionList();
|
|
for(var idx in functions) {
|
|
var func = functions[idx][0];
|
|
var $option = $("<option/>");
|
|
$option.val(func).text(func);
|
|
$("#calc_helper_methods").append($option);
|
|
}
|
|
$("#calc_helper_methods").change();
|
|
$("#help_with_equations_dialog").dialog({
|
|
title: I18n.t('titles.help_with_formulas', "Help with Quiz Question Formulas"),
|
|
width: 500
|
|
});
|
|
});
|
|
$question.find(".combinations_option").attr('disabled', true);
|
|
$question.find(".question_content").bind('keypress', function(event) {
|
|
setTimeout(function() {$(event.target).triggerHandler('change')}, 50);
|
|
});
|
|
$question.find(".question_content").bind('change', function(event) {
|
|
var text = RichContentEditor.callOnRCE($(this), 'get_code');
|
|
var matches = text.match(/\[[A-Za-z][A-Za-z0-9]*\]/g);
|
|
$question.find(".variables").find("tr.variable").addClass('to_be_removed');
|
|
$question.find(".variables").showIf(matches && matches.length > 0);
|
|
var matchHash = {};
|
|
if (matches) {
|
|
for(var idx = 0; idx < matches.length; idx++) {
|
|
if (matches[idx]) {
|
|
var variable = matches[idx].substring(1, matches[idx].length - 1);
|
|
if (!matchHash[variable]) {
|
|
var $variable = $question.find('.variables tr.variable[data-name="' + variable + '"]');
|
|
if ($variable.length === 0) {
|
|
var label_id = htmlEscape("label_for_var_" + variable);
|
|
// xsslint safeString.identifier label_id
|
|
|
|
$variable = $("<tr class='variable'>"
|
|
+ "<th id='" + label_id + "' class='name'></th>"
|
|
+ "<td><div><input aria-labelledby='" + label_id + " equation_var_minimum' type='text' name='min' class='min variable_setting' style='width: 70%;' value='1'/></div></td>"
|
|
+ "<td><div><input aria-labelledby='" + label_id + " equation_var_maximum' type='text' name='max' class='max variable_setting' style='width: 70%;' value='10'/></div></td>"
|
|
+ "<td><div style='width: 70%;'><select aria-labelledby='" + label_id + " equation_var_precision' name='round' class='round variable_setting'><option>0</option><option>1</option><option>2</option><option>3</option></div></td>"
|
|
+ "<td aria-labelledby='equation_var_example' class='value'></td></tr>");
|
|
|
|
$question.find(".variables tbody").append($variable);
|
|
$variable.find(".variable_setting:first").triggerHandler('change');
|
|
}
|
|
$variable.removeClass('to_be_removed');
|
|
$variable.addClass(variable);
|
|
$variable.attr('data-name', variable);
|
|
$variable.find("th.name").text(variable);
|
|
matchHash[variable] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$question.find(".variables").find("tr.to_be_removed").remove();
|
|
$question.find(".supercalc").superCalc('recalculate', true);
|
|
$question.triggerHandler('settings_change', false);
|
|
}).change();
|
|
}
|
|
|
|
function numericalAnswerTypeChange($el) {
|
|
var val = $el.val();
|
|
var $answer = $el.parents(".numerical_answer");
|
|
$answer.find(".numerical_answer_text").hide();
|
|
$answer.find("." + val).show();
|
|
}
|
|
|
|
// attach HTML answers but only when they click the button
|
|
$('#questions').delegate('.edit_html', 'click', function(event) {
|
|
event.preventDefault();
|
|
var $this = $(this);
|
|
var toggler = $this.data('editorToggle');
|
|
var inputColumn = $this.parents().find('.answer_type:visible')[0]
|
|
var rceWidth = inputColumn.offsetWidth
|
|
|
|
// create toggler instance on the first click
|
|
if (!toggler) {
|
|
toggler = new MultipleChoiceToggle($this, {
|
|
editorBoxLabel: I18n.t('label.answer.text', 'Answer text, rich text area'),
|
|
tinyOptions: {width: rceWidth}
|
|
});
|
|
$this.data('editorToggle', toggler);
|
|
}
|
|
|
|
toggler.toggle();
|
|
});
|
|
|
|
$(function() {
|
|
$(document).on('change', function controlResultVisibilityFields() {
|
|
// "Let Students See Their Quiz Responses" related fields visibility:
|
|
if (isShowingResults()) {
|
|
$('.show_quiz_results_options').show();
|
|
// CA options:
|
|
//
|
|
// What we'd like to do is show/hide the date-pickers based on the
|
|
// "Let Students See The Correct Answers" option, and disable them
|
|
// if we're showing results just once ("Only Once After Each Attempt"):
|
|
correctAnswerVisibility.showDatePickers(correctAnswerVisibility.isOn());
|
|
correctAnswerVisibility.enableDatePickers(!isShowingResultsJustOnce());
|
|
} else {
|
|
correctAnswerVisibility.disable();
|
|
$('#quiz_one_time_results').prop('checked', false)
|
|
$('#hide_results_only_after_last').prop('checked', false);
|
|
$('#quiz_show_correct_answers_last_attempt').prop('checked', false);
|
|
$('.show_quiz_results_options').hide();
|
|
}
|
|
|
|
// Only allow students to see answers on last attempt if the quiz has more than one attempt
|
|
var showCorrectAnswersLastAttempt = parseInt($('#quiz_allowed_attempts').val()) > 0;
|
|
$('#quiz_show_correct_answers_last_attempt_container').toggle(showCorrectAnswersLastAttempt);
|
|
if(!showCorrectAnswersLastAttempt) {
|
|
$('#quiz_show_correct_answers_last_attempt').prop('checked', false);
|
|
}
|
|
}).triggerHandler('change');
|
|
});
|
|
|