canvas-lms/public/javascripts/submissions.js

408 lines
17 KiB
JavaScript

/*
* Copyright (C) 2012 - 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/>.
*/
import round from 'compiled/util/round'
import I18n from 'i18n!submissions'
import $ from 'jquery'
import GradeFormatHelper from 'jsx/gradebook/shared/helpers/GradeFormatHelper'
import './jquery.ajaxJSON'
import './jquery.instructure_forms' /* ajaxJSONFiles */
import './jquery.instructure_date_and_time' /* datetimeString */
import './jquery.instructure_misc_plugins' /* fragmentChange, showIf */
import './jquery.loadingImg'
import './jquery.templateData'
import './media_comments'
import 'compiled/jquery/mediaCommentThumbnail'
import './vendor/jquery.scrollTo'
import './rubric_assessment' /*global rubricAssessment*/
var rubricAssessments = ENV.rubricAssessments;
$("#content").addClass('padless');
var fileIndex = 1;
function submissionLoaded (data) {
if(data.submission) {
var d = [];
d.push(data);
data = d;
}
for(var jdx in data) {
var submission = data[jdx].submission;
var comments = submission.visible_submission_comments || submission.submission_comments;
if(submission.user_id != ENV.SUBMISSION.user_id) { continue; }
for(var idx in comments) {
var comment = comments[idx].submission_comment;
if($("#submission_comment_" + comment.id).length > 0) { continue; }
var $comment = $("#comment_blank").clone(true).removeAttr('id');
comment.posted_at = $.datetimeString(comment.created_at);
$comment.fillTemplateData({
data: comment,
id: 'submission_comment_' + comment.id
});
if(comment.media_comment_id) {
var $media_comment_link = $("#comment_media_blank").clone(true).removeAttr('id');
$media_comment_link.fillTemplateData({
data: comment
});
$comment.find(".comment").empty().append($media_comment_link.show());
} else {
for(var jdx in comment.attachments) {
var attachment = comment.attachments[jdx].attachment;
var $attachment = $("#comment_attachment_blank").clone(true).removeAttr('id');
attachment.comment_id = comment.id;
$attachment.fillTemplateData({
data: attachment,
hrefValues: ['comment_id', 'id']
});
$comment.find(".comment_attachments").append($attachment.show());
}
}
$(".comments .comment_list").append($comment.show()).scrollTop(10000);
if ($(".grading_comment").val() === comment.comment) {
$(".grading_comment").val("");
}
}
$(".comments .comment_list .play_comment_link").mediaCommentThumbnail('small');
$(".save_comment_button").attr('disabled',null);
if(submission) {
showGrade(submission);
$(".submission_details").fillTemplateData({
data: submission
});
$("#add_comment_form .comment_attachments").empty();
}
}
$(".submission_header").loadingImage('remove');
}
function callIfSet (value, fn) {
return value == null ? '' : fn.call(this, value);
}
function roundAndFormat (value) {
return I18n.n(round(value, round.DEFAULT));
}
function showGrade (submission) {
if (['pass', 'fail', 'complete', 'incomplete'].indexOf(submission.entered_grade) > -1) {
$('.grading_box').val(submission.entered_grade);
} else {
$('.grading_box').val(callIfSet(submission.entered_grade, GradeFormatHelper.formatGrade));
}
$('.late_penalty').text(callIfSet(-submission.points_deducted, roundAndFormat));
$('.published_grade').text(callIfSet(submission.published_grade, GradeFormatHelper.formatGrade));
$('.grade').text(callIfSet(submission.grade, GradeFormatHelper.formatGrade));
if (submission.excused) {
$('.entered_grade').text(I18n.t('Excused'));
} else {
$('.entered_grade').text(callIfSet(submission.entered_grade, GradeFormatHelper.formatGrade));
}
if (!submission.excused && submission.points_deducted) {
$('.late-penalty-display').show();
} else {
$('.late-penalty-display').hide();
}
}
function makeRubricAccessible ($rubric) {
$rubric.show()
var $tabs = $rubric.find(":tabbable")
var tabBounds = [$tabs.first()[0], $tabs.last()[0]]
var keyCodes = {
9: "tab",
13: "enter",
27: "esc"
}
$(".hide_rubric_link").keydown(function(e) {
if (keyCodes[e.which] == "enter") {
e.preventDefault();
$(this).click();
};
});
$tabs.each(function(){
$(this).bind('keydown', function(e){
if(keyCodes[e.which] == "esc")
$(".hide_rubric_link").click()
});
});
$(tabBounds).each(function(e){
$(this).bind('keydown', function(e){
if (keyCodes[e.which] == "tab"){
var isLeavingHolder = $(this).is($(tabBounds).first()) ? e.shiftKey : !e.shiftKey;
if(isLeavingHolder) {
e.preventDefault();
var thisEl = this
var target = $.grep(tabBounds,function(el){return el != thisEl})
$(target).focus();
};
};
});
});
$rubric.siblings().attr('data-hide_from_rubric', true).end().
parentsUntil("#application").siblings().not("#aria_alerts").attr('data-hide_from_rubric', true)
$rubric.hide()
}
function closeRubric () {
$("#rubric_holder").fadeOut(function() {
toggleRubric($(this));
$(".assess_submission_link").focus();
});
}
function openRubric () {
$("#rubric_holder").fadeIn(function() {
toggleRubric($(this));
$(this).find('.hide_rubric_link').focus();
});
}
function toggleRubric ($rubric) {
var ariaSetting = $rubric.is(":visible");
$("#application").find("[data-hide_from_rubric]").attr("aria-hidden", ariaSetting)
}
function windowResize () {
var $frame = $("#preview_frame");
var top = $frame.offset().top;
var height = $(window).height() - top;
$frame.height(height);
$("#rubric_holder").css({'maxHeight': height - 50, 'overflow': 'auto', 'zIndex': 5});
$(".comments").height(height);
};
// This `setup` function allows us to control when the setup is triggered.
// submissions.coffee requires this file and then immediately triggers it,
// while submissionsSpec.jsx triggers it after setup is complete.
export function setup () {
$(document).ready(function() {
$(".comments .comment_list .play_comment_link").mediaCommentThumbnail('small');
$(window).bind('resize', windowResize).triggerHandler('resize');
$(".comments_link").click(function(event) {
event.preventDefault();
$(".comments").slideToggle(function() {
$(".comments .media_comment_content").empty();
$(".comments textarea:visible").focus().select();
});
});
$(".save_comment_button").click(function(event) {
$(document).triggerHandler('comment_change');
});
// post new comment but no grade
$(document).bind('comment_change', function(event) {
$(".save_comment_button").attr('disabled','disabled');
$(".submission_header").loadingImage();
var url = $(".update_submission_url").attr('href');
var method = $(".update_submission_url").attr('title');
var formData = {
'submission[assignment_id]': ENV.SUBMISSION.assignment_id,
'submission[user_id]': ENV.SUBMISSION.user_id,
'submission[group_comment]': ($("#submission_group_comment").attr('checked') ? "1" : "0")
};
if($("#media_media_recording:visible").length > 0) {
var comment_id = $("#media_media_recording").data('comment_id');
var comment_type = $("#media_media_recording").data('comment_type');
formData['submission[media_comment_type]'] = comment_type || 'video';
formData['submission[media_comment_id]'] = comment_id;
} else {
if($(".grading_comment").val() && $(".grading_comment").val != "") {
formData['submission[comment]'] = $(".grading_comment").val();
}
if(!formData['submission[comment]'] && $("#add_comment_form input[type='file']").length > 0) {
formData['submission[comment]'] = I18n.t("see_attached_files", "See attached files");
}
}
if(!formData['submission[comment]'] && !formData['submission[media_comment_id]']) {
$(".submission_header").loadingImage('remove');
$(".save_comment_button").attr('disabled',null);
return;
}
if($("#add_comment_form input[type='file']").length > 0) {
$.ajaxJSONFiles(url + ".text", method, formData, $("#add_comment_form input[type='file']"), submissionLoaded);
} else {
$.ajaxJSON(url, method, formData, submissionLoaded);
}
});
$(".cancel_comment_button").click(function(event) {
$(".grading_comment").val("");
$(".comments_link").click();
});
$(".grading_value").change(function(event) {
$(document).triggerHandler('grading_change');
});
// post new grade but no comments
$(document).bind('grading_change', function(event) {
$(".save_comment_button").attr('disabled','disabled');
$(".submission_header").loadingImage();
var url = $(".update_submission_url").attr('href');
var method = $(".update_submission_url").attr('title');
var formData = {
'submission[assignment_id]': ENV.SUBMISSION.assignment_id,
'submission[user_id]': ENV.SUBMISSION.user_id,
'submission[group_comment]': ($("#submission_group_comment").attr('checked') ? "1" : "0")
};
if($(".grading_value:visible").length > 0) {
formData['submission[grade]'] = GradeFormatHelper.delocalizeGrade($('.grading_value').val());
$.ajaxJSON(url, method, formData, submissionLoaded);
} else {
$(".submission_header").loadingImage('remove');
$(".save_comment_button").attr('disabled',null);
}
});
$(".attach_comment_file_link").click(function(event) {
event.preventDefault();
var $attachment = $("#comment_attachment_input_blank").clone(true).removeAttr('id');
$attachment.find("input").attr('name', 'attachments[' + (fileIndex++) + '][uploaded_data]');
$("#add_comment_form .comment_attachments").append($attachment.slideDown());
});
$(".delete_comment_attachment_link").click(function(event) {
event.preventDefault();
$(this).parents(".comment_attachment_input").slideUp(function() {
$(this).remove();
});
});
$(".save_rubric_button").click(function() {
var $rubric = $(this).parents("#rubric_holder").find(".rubric");
var data = rubricAssessment.assessmentData($rubric);
var url = $(".update_rubric_assessment_url").attr('href');
var method = "POST";
$rubric.loadingImage();
$.ajaxJSON(url, method, data, function(data) {
$rubric.loadingImage('remove');
var assessment = data;
var found = false;
if(assessment.rubric_association) {
rubricAssessment.updateRubricAssociation($rubric, data.rubric_association);
delete assessment.rubric_association;
}
for(var idx in rubricAssessments) {
var a = rubricAssessments[idx].rubric_assessment;
if(a && assessment && assessment.id == a.id) {
rubricAssessments[idx].rubric_assessment = assessment;
found = true;
}
}
if(!found) {
if (!data.rubric_assessment) {
data = { rubric_assessment: data };
}
rubricAssessments.push(data);
var $option = $(document.createElement('option'));
$option.val(assessment.id).text(assessment.assessor_name).attr('id', 'rubric_assessment_option_' + assessment.id);
$("#rubric_assessments_select").prepend($option).val(assessment.id);
}
$("#rubric_assessment_option_" + assessment.id).text(assessment.assessor_name);
$("#new_rubric_assessment_option").remove();
$("#rubric_assessments_list").show();
rubricAssessment.populateRubric($rubric, assessment);
var submission = assessment.artifact;
if (submission) {
showGrade(submission);
}
closeRubric();
});
});
$("#rubric_holder .rubric").css({'width': 'auto', 'marginTop': 0});
makeRubricAccessible($("#rubric_holder"));
$(".hide_rubric_link").click(function(event) {
event.preventDefault();
closeRubric();
});
$(".assess_submission_link").click(function(event) {
event.preventDefault();
$("#rubric_assessments_select").change();
openRubric();
});
$("#rubric_assessments_select").change(function() {
var id = $(this).val();
var found = null;
for(var idx in rubricAssessments) {
var assessment = rubricAssessments[idx].rubric_assessment;
if(assessment.id == id) {
found = assessment;
}
}
rubricAssessment.populateRubric($("#rubric_holder .rubric"), found);
var current_user = (!found || found.assessor_id == ENV.RUBRIC_ASSESSMENT.assessor_id);
$("#rubric_holder .save_rubric_button").showIf(current_user);
}).change();
$(".media_comment_link").click(function(event) {
event.preventDefault();
$("#add_comment_form").hide();
$("#media_media_recording").show();
var $recording = $("#media_media_recording").find(".media_recording");
$recording.mediaComment('create', 'any', function(id, type) {
$("#media_media_recording").data('comment_id', id).data('comment_type', type);
$(document).triggerHandler('comment_change');
$("#add_comment_form").show();
$("#media_media_recording").hide();
$recording.empty();
}, function() {
$("#add_comment_form").show();
$("#media_media_recording").hide();
});
});
$("#media_recorder_container a").live('click', function(event) {
$("#add_comment_form").show();
$("#media_media_recording").hide();
});
$(".comments .comment_list")
.delegate(".play_comment_link", 'click', function(event) {
event.preventDefault();
var comment_id = $(this).parents(".comment_media").getTemplateData({textValues: ['media_comment_id']}).media_comment_id;
if(comment_id) {
$(this).parents(".comment_media").find(".media_comment_content").mediaComment('show', comment_id, 'video');
}
})
// this is to prevent the default behavior of loading the video inline from happening
// the .delegate(".play_comment_link"... and the .delegate('a.instructure_inline_media_comment'...
// are actually selecting the same links I just wanted to use the different selectors because
// instructure.js uses 'a.instructure_inline_media_comment' as the selector for its .live handler
// to show things inline.
.delegate('a.instructure_inline_media_comment', 'click', function(e){
// dont let it bubble past this so it doesnt get to the .live handler to show the video inline
e.preventDefault();
e.stopPropagation();
});
showGrade(ENV.SUBMISSION.submission);
});
};
// necessary for tests
export function teardown () {
$(window).unbind('resize', windowResize);
$(document).unbind('comment_change');
$(document).unbind('grading_change');
};
$(document).fragmentChange(function(event, hash) {
if(hash == '#rubric') {
$(".assess_submission_link:visible:first").click();
} else if(hash.match(/^#comment/)) {
var params = null;
try {
params = JSON.parse(hash.substring(8));
} catch(e) { }
if(params && params.comment) {
$(".grading_comment").val(params.comment);
}
$(".grading_comment").focus().select();
}
});
INST.refreshGrades = function() {
var url = $(".submission_data_url").attr('href');
setTimeout(function() {
$.ajaxJSON(url, 'GET', {}, submissionLoaded);
}, 500);
};