diff --git a/mod/assign/amd/build/grading_form_change_checker.min.js b/mod/assign/amd/build/grading_form_change_checker.min.js
index 84120c9f6925b..f2b9f56127b36 100644
--- a/mod/assign/amd/build/grading_form_change_checker.min.js
+++ b/mod/assign/amd/build/grading_form_change_checker.min.js
@@ -6,6 +6,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.1
*/
-define("mod_assign/grading_form_change_checker",["jquery"],(function($){return{saveFormState:function(selector){$(selector).trigger("save-form-state");var data=$(selector).serialize();$(selector).data("saved-form-state",data)},checkFormForChanges:function(selector){$(selector).trigger("save-form-state");var data=$(selector).serialize(),previousdata=$(selector).data("saved-form-state");return void 0!==previousdata&&previousdata!=data}}}));
+define("mod_assign/grading_form_change_checker",["jquery"],(function($){return{saveFormState:function(selector){$(selector).trigger("save-form-state");var data=$(selector).serialize();$(selector).data("saved-form-state",data)},checkFormForChanges:function(selector){$(selector).trigger("save-form-state");var data=$(selector).serialize(),previousdata=$(selector).data("saved-form-state");return!!$(selector).data("unresolved-error")||void 0!==previousdata&&previousdata!=data}}}));
//# sourceMappingURL=grading_form_change_checker.min.js.map
\ No newline at end of file
diff --git a/mod/assign/amd/build/grading_form_change_checker.min.js.map b/mod/assign/amd/build/grading_form_change_checker.min.js.map
index be6b0287b4d36..c2466b12af8cf 100644
--- a/mod/assign/amd/build/grading_form_change_checker.min.js.map
+++ b/mod/assign/amd/build/grading_form_change_checker.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"grading_form_change_checker.min.js","sources":["../src/grading_form_change_checker.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Simple method to check for changes to a form between two points in time.\n *\n * @module mod_assign/grading_form_change_checker\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery'], function($) {\n\n return {\n /**\n * Save the values in the form to a data attribute so they can be compared later for changes.\n *\n * @method saveFormState\n * @param {String} selector The selector for the form element.\n */\n saveFormState: function(selector) {\n $(selector).trigger('save-form-state');\n var data = $(selector).serialize();\n $(selector).data('saved-form-state', data);\n },\n\n /**\n * Compare the current values in the form to the previously saved state.\n *\n * @method checkFormForChanges\n * @param {String} selector The selector for the form element.\n * @return {Boolean} True if there are changes to the form data.\n */\n checkFormForChanges: function(selector) {\n\n $(selector).trigger('save-form-state');\n\n var data = $(selector).serialize(),\n previousdata = $(selector).data('saved-form-state');\n\n if (typeof previousdata === 'undefined') {\n return false;\n }\n return (previousdata != data);\n }\n };\n});\n"],"names":["define","$","saveFormState","selector","trigger","data","serialize","checkFormForChanges","previousdata"],"mappings":";;;;;;;;AAuBAA,gDAAO,CAAC,WAAW,SAASC,SAEjB,CAOHC,cAAe,SAASC,UACpBF,EAAEE,UAAUC,QAAQ,uBAChBC,KAAOJ,EAAEE,UAAUG,YACvBL,EAAEE,UAAUE,KAAK,mBAAoBA,OAUzCE,oBAAqB,SAASJ,UAE1BF,EAAEE,UAAUC,QAAQ,uBAEhBC,KAAOJ,EAAEE,UAAUG,YACnBE,aAAeP,EAAEE,UAAUE,KAAK,gCAER,IAAjBG,cAGHA,cAAgBH"}
\ No newline at end of file
+{"version":3,"file":"grading_form_change_checker.min.js","sources":["../src/grading_form_change_checker.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Simple method to check for changes to a form between two points in time.\n *\n * @module mod_assign/grading_form_change_checker\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery'], function($) {\n\n return {\n /**\n * Save the values in the form to a data attribute so they can be compared later for changes.\n *\n * @method saveFormState\n * @param {String} selector The selector for the form element.\n */\n saveFormState: function(selector) {\n $(selector).trigger('save-form-state');\n var data = $(selector).serialize();\n $(selector).data('saved-form-state', data);\n },\n\n /**\n * Compare the current values in the form to the previously saved state.\n *\n * @method checkFormForChanges\n * @param {String} selector The selector for the form element.\n * @return {Boolean} True if there are changes to the form data.\n */\n checkFormForChanges: function(selector) {\n\n $(selector).trigger('save-form-state');\n\n var data = $(selector).serialize(),\n previousdata = $(selector).data('saved-form-state'),\n unresolvederror = $(selector).data('unresolved-error');\n\n if (unresolvederror) {\n return true;\n }\n if (typeof previousdata === 'undefined') {\n return false;\n }\n return (previousdata != data);\n }\n };\n});\n"],"names":["define","$","saveFormState","selector","trigger","data","serialize","checkFormForChanges","previousdata"],"mappings":";;;;;;;;AAuBAA,gDAAO,CAAC,WAAW,SAASC,SAEjB,CAOHC,cAAe,SAASC,UACpBF,EAAEE,UAAUC,QAAQ,uBAChBC,KAAOJ,EAAEE,UAAUG,YACvBL,EAAEE,UAAUE,KAAK,mBAAoBA,OAUzCE,oBAAqB,SAASJ,UAE1BF,EAAEE,UAAUC,QAAQ,uBAEhBC,KAAOJ,EAAEE,UAAUG,YACnBE,aAAeP,EAAEE,UAAUE,KAAK,4BACdJ,EAAEE,UAAUE,KAAK,0BAKX,IAAjBG,cAGHA,cAAgBH"}
\ No newline at end of file
diff --git a/mod/assign/amd/build/grading_panel.min.js b/mod/assign/amd/build/grading_panel.min.js
index bdd6256eb4653..a408b904d357c 100644
--- a/mod/assign/amd/build/grading_panel.min.js
+++ b/mod/assign/amd/build/grading_panel.min.js
@@ -6,6 +6,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.1
*/
-define("mod_assign/grading_panel",["jquery","core/yui","core/notification","core/templates","core/fragment","core/ajax","core/str","mod_assign/grading_form_change_checker","mod_assign/grading_events","core_form/events","core/toast","core_form/changechecker"],(function($,Y,notification,templates,fragment,ajax,str,checker,GradingEvents,FormEvents,Toast,FormChangeChecker){var GradingPanel=function(selector){this._regionSelector=selector,this._region=$(selector),this._userCache=[],this.registerEventListeners()};return GradingPanel.prototype._regionSelector=null,GradingPanel.prototype._lastUserId=0,GradingPanel.prototype._lastAttemptNumber=-1,GradingPanel.prototype._region=null,GradingPanel.prototype.nextUserId=null,GradingPanel.prototype.nextUser=!1,GradingPanel.prototype._niceReplaceNodeContents=function(node,html,js){var promise=$.Deferred();return node.fadeOut("fast",(function(){templates.replaceNodeContents(node,html,js),node.fadeIn("fast",(function(){promise.resolve()}))})),promise.promise()},GradingPanel.prototype._saveFormState=function(){var checked=$('[data-region="grading-actions-form"] [name="sendstudentnotifications"]').prop("checked");$('.gradeform [name="sendstudentnotifications"]').val(checked)},GradingPanel.prototype._submitForm=function(event,nextUserId,nextUser){var commentAreaElement=document.querySelector(".comment-area");commentAreaElement&&(""!==commentAreaElement.querySelector(".db > textarea").value&&commentAreaElement.querySelector('.fd a[id^="comment-action-post-"]').click());var form=$(this._region.find("form.gradeform"));$('[data-region="overlay"]').show(),FormChangeChecker.markFormSubmitted(form[0]),form.trigger("save-form-state"),FormEvents.notifyFormSubmittedByJavascript(form[0]);var data=form.serialize(),assignmentid=this._region.attr("data-assignmentid");ajax.call([{methodname:"mod_assign_submit_grading_form",args:{assignmentid:assignmentid,userid:this._lastUserId,jsonformdata:JSON.stringify(data)},done:this._handleFormSubmissionResponse.bind(this,data,nextUserId,nextUser),fail:notification.exception}])},GradingPanel.prototype._handleFormSubmissionResponse=function(formdata,nextUserId,nextUser,response){if(void 0===nextUserId&&(nextUserId=this._lastUserId),response.length)$(document).trigger("reset",[this._lastUserId,formdata]);else{str.get_string("gradechangessaveddetail","mod_assign").then((function(str){return Toast.add(str),str})).catch(notification.exception);var form=$(this._region.find("form.gradeform"));FormChangeChecker.resetFormDirtyState(form[0]),nextUserId==this._lastUserId?$(document).trigger("reset",nextUserId):nextUser?$(document).trigger("done-saving-show-next",!0):$(document).trigger("user-changed",nextUserId)}$('[data-region="overlay"]').hide()},GradingPanel.prototype._resetForm=function(e,userid,formdata){var event=$.Event("custom");void 0===userid&&(userid=this._lastUserId),this._lastUserId=0,this._refreshGradingPanel(event,userid,formdata)},GradingPanel.prototype._chooseAttempt=function(e){var submissionsId=$(e.target).data("submissions"),formhtml=$(document.getElementById(submissionsId)).clone().wrap($("")).html();str.get_strings([{key:"viewadifferentattempt",component:"mod_assign"},{key:"view",component:"core"},{key:"cancel",component:"core"}]).done(function(strs){notification.confirm(strs[0],formhtml,strs[1],strs[2],function(){var attemptnumber=$("input:radio[name='select-attemptnumber']:checked").val();this._refreshGradingPanel(null,this._lastUserId,"",attemptnumber)}.bind(this))}.bind(this)).fail(notification.exception)},GradingPanel.prototype._addPopoutButtons=function(selector){var region=$(selector);templates.render("mod_assign/popout_button",{}).done(function(html){region.find('[data-fieldtype="filemanager"],[data-fieldtype="editor"],[data-fieldtype="grading"]').closest(".fitem").addClass("has-popout").find("label").parent().append(html),region.on("click",'[data-region="popout-button"]',this._togglePopout.bind(this))}.bind(this)).fail(notification.exception)},GradingPanel.prototype._togglePopout=function(event){event.preventDefault();var container=$(event.target).closest(".fitem");container.hasClass("popout")?$(".popout").removeClass("popout"):($(".popout").removeClass("popout"),container.addClass("popout"),container.addClass("moodle-has-zindex"))},GradingPanel.prototype._refreshGradingPanel=function(event,userid,submissiondata,attemptnumber){var contextid=this._region.attr("data-contextid");void 0===submissiondata&&(submissiondata=""),void 0===attemptnumber&&(attemptnumber=-1),this._lastUserId==userid&&this._lastAttemptNumber==attemptnumber&&""===submissiondata||(this._lastUserId=userid,this._lastAttemptNumber=attemptnumber,$(document).trigger("start-loading-user"),window.M.util.js_pending("mod-assign-loading-user"),templates.render("mod_assign/loading",{}).done(function(html,js){this._niceReplaceNodeContents(this._region,html,js).done(function(){if(userid>0){this._region.show();var params={userid:userid,attemptnumber:attemptnumber,jsonformdata:JSON.stringify(submissiondata)};fragment.loadFragment("mod_assign","gradingpanel",contextid,params).done(function(html,js){this._niceReplaceNodeContents(this._region,html,js).done(function(){checker.saveFormState('[data-region="grade-panel"] .gradeform'),$(document).on("editor-content-restored",(function(){checker.saveFormState('[data-region="grade-panel"] .gradeform')})),$('[data-region="attempt-chooser"]').on("click",this._chooseAttempt.bind(this)),this._addPopoutButtons('[data-region="grade-panel"] .gradeform'),$(document).trigger("finish-loading-user"),window.M.util.js_complete("mod-assign-loading-user")}.bind(this)).fail(notification.exception)}.bind(this)).fail(notification.exception),$('[data-region="review-panel"]').show()}else this._region.hide(),$('[data-region="review-panel"]').hide(),$(document).trigger("finish-loading-user"),window.M.util.js_complete("mod-assign-loading-user")}.bind(this))}.bind(this)).fail(notification.exception))},GradingPanel.prototype._getNextUser=function(event,data){this.nextUserId=data.nextUserId,this.nextUser=data.nextUser},GradingPanel.prototype._handleSaveAndShowNext=function(){this._submitForm(null,this.nextUserId,this.nextUser)},GradingPanel.prototype.getPanelElement=function(){return $('[data-region="grade-panel"]')},GradingPanel.prototype.collapsePanel=function(){this.getPanelElement().addClass("collapsed")},GradingPanel.prototype.expandPanel=function(){this.getPanelElement().removeClass("collapsed")},GradingPanel.prototype.registerEventListeners=function(){var docElement=$(document);$(this._region).on("submit","form",(function(e){e.preventDefault()})),docElement.on("next-user",this._getNextUser.bind(this)),docElement.on("user-changed",this._refreshGradingPanel.bind(this)),docElement.on("save-changes",this._submitForm.bind(this)),docElement.on("save-and-show-next",this._handleSaveAndShowNext.bind(this)),docElement.on("reset",this._resetForm.bind(this)),docElement.on("save-form-state",this._saveFormState.bind(this)),docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL,function(){this.collapsePanel()}.bind(this)),docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL,function(){this.expandPanel()}.bind(this)),docElement.on(GradingEvents.EXPAND_GRADE_PANEL,function(){this.expandPanel()}.bind(this))},GradingPanel}));
+define("mod_assign/grading_panel",["jquery","core/yui","core/notification","core/templates","core/fragment","core/ajax","core/str","mod_assign/grading_form_change_checker","mod_assign/grading_events","core_form/events","core/toast","core_form/changechecker"],(function($,Y,notification,templates,fragment,ajax,str,checker,GradingEvents,FormEvents,Toast,FormChangeChecker){var GradingPanel=function(selector){this._regionSelector=selector,this._region=$(selector),this._userCache=[],this.registerEventListeners()};return GradingPanel.prototype._regionSelector=null,GradingPanel.prototype._lastUserId=0,GradingPanel.prototype._lastAttemptNumber=-1,GradingPanel.prototype._region=null,GradingPanel.prototype.nextUserId=null,GradingPanel.prototype.nextUser=!1,GradingPanel.prototype._niceReplaceNodeContents=function(node,html,js){var promise=$.Deferred();return node.fadeOut("fast",(function(){templates.replaceNodeContents(node,html,js),node.fadeIn("fast",(function(){promise.resolve()}))})),promise.promise()},GradingPanel.prototype._saveFormState=function(){var checked=$('[data-region="grading-actions-form"] [name="sendstudentnotifications"]').prop("checked");$('.gradeform [name="sendstudentnotifications"]').val(checked)},GradingPanel.prototype._submitForm=function(event,nextUserId,nextUser){var commentAreaElement=document.querySelector(".comment-area");commentAreaElement&&(""!==commentAreaElement.querySelector(".db > textarea").value&&commentAreaElement.querySelector('.fd a[id^="comment-action-post-"]').click());var form=$(this._region.find("form.gradeform"));$('[data-region="overlay"]').show(),FormChangeChecker.markFormSubmitted(form[0]),form.trigger("save-form-state"),FormEvents.notifyFormSubmittedByJavascript(form[0]);var data=form.serialize(),assignmentid=this._region.attr("data-assignmentid");ajax.call([{methodname:"mod_assign_submit_grading_form",args:{assignmentid:assignmentid,userid:this._lastUserId,jsonformdata:JSON.stringify(data)},done:this._handleFormSubmissionResponse.bind(this,data,nextUserId,nextUser),fail:notification.exception}])},GradingPanel.prototype._handleFormSubmissionResponse=function(formdata,nextUserId,nextUser,response){if(void 0===nextUserId&&(nextUserId=this._lastUserId),response.length)str.get_string("errorgradechangessaveddetail","mod_assign").then((function(str){return Toast.add(str,{type:"danger",delay:4e3}),str})).catch(notification.exception),$(document).trigger("reset",[this._lastUserId,formdata,!0]);else{str.get_string("gradechangessaveddetail","mod_assign").then((function(str){return Toast.add(str),str})).catch(notification.exception);var form=$(this._region.find("form.gradeform"));FormChangeChecker.resetFormDirtyState(form[0]),nextUserId==this._lastUserId?$(document).trigger("reset",nextUserId):nextUser?$(document).trigger("done-saving-show-next",!0):$(document).trigger("user-changed",nextUserId)}$('[data-region="overlay"]').hide()},GradingPanel.prototype._resetForm=function(e,userid,formdata,unresolvederror){var event=$.Event("custom");void 0===userid&&(userid=this._lastUserId),this._lastUserId=0,this._refreshGradingPanel(event,userid,formdata,-1,unresolvederror)},GradingPanel.prototype._chooseAttempt=function(e){var submissionsId=$(e.target).data("submissions"),formhtml=$(document.getElementById(submissionsId)).clone().wrap($("")).html();str.get_strings([{key:"viewadifferentattempt",component:"mod_assign"},{key:"view",component:"core"},{key:"cancel",component:"core"}]).done(function(strs){notification.confirm(strs[0],formhtml,strs[1],strs[2],function(){var attemptnumber=$("input:radio[name='select-attemptnumber']:checked").val();this._refreshGradingPanel(null,this._lastUserId,"",attemptnumber)}.bind(this))}.bind(this)).fail(notification.exception)},GradingPanel.prototype._addPopoutButtons=function(selector){var region=$(selector);templates.render("mod_assign/popout_button",{}).done(function(html){region.find('[data-fieldtype="filemanager"],[data-fieldtype="editor"],[data-fieldtype="grading"]').closest(".fitem").addClass("has-popout").find("label").parent().append(html),region.on("click",'[data-region="popout-button"]',this._togglePopout.bind(this))}.bind(this)).fail(notification.exception)},GradingPanel.prototype._togglePopout=function(event){event.preventDefault();var container=$(event.target).closest(".fitem");container.hasClass("popout")?$(".popout").removeClass("popout"):($(".popout").removeClass("popout"),container.addClass("popout"),container.addClass("moodle-has-zindex"))},GradingPanel.prototype._refreshGradingPanel=function(event,userid,submissiondata,attemptnumber,unresolvederror){var contextid=this._region.attr("data-contextid");void 0===submissiondata&&(submissiondata=""),void 0===attemptnumber&&(attemptnumber=-1),void 0===unresolvederror&&(unresolvederror=!1),this._lastUserId==userid&&this._lastAttemptNumber==attemptnumber&&""===submissiondata||(this._lastUserId=userid,this._lastAttemptNumber=attemptnumber,$(document).trigger("start-loading-user"),window.M.util.js_pending("mod-assign-loading-user"),templates.render("mod_assign/loading",{}).done(function(html,js){this._niceReplaceNodeContents(this._region,html,js).done(function(){if(userid>0){this._region.show();var params={userid:userid,attemptnumber:attemptnumber,jsonformdata:JSON.stringify(submissiondata)};fragment.loadFragment("mod_assign","gradingpanel",contextid,params).done(function(html,js){this._niceReplaceNodeContents(this._region,html,js).done(function(){checker.saveFormState('[data-region="grade-panel"] .gradeform'),$(document).on("editor-content-restored",(function(){checker.saveFormState('[data-region="grade-panel"] .gradeform')})),$('[data-region="attempt-chooser"]').on("click",this._chooseAttempt.bind(this)),this._addPopoutButtons('[data-region="grade-panel"] .gradeform'),unresolvederror&&$('[data-region="grade-panel"] .gradeform').data("unresolved-error",!0),$(document).trigger("finish-loading-user"),window.M.util.js_complete("mod-assign-loading-user")}.bind(this)).fail(notification.exception)}.bind(this)).fail(notification.exception),$('[data-region="review-panel"]').show()}else this._region.hide(),$('[data-region="review-panel"]').hide(),$(document).trigger("finish-loading-user"),window.M.util.js_complete("mod-assign-loading-user")}.bind(this))}.bind(this)).fail(notification.exception))},GradingPanel.prototype._getNextUser=function(event,data){this.nextUserId=data.nextUserId,this.nextUser=data.nextUser},GradingPanel.prototype._handleSaveAndShowNext=function(){this._submitForm(null,this.nextUserId,this.nextUser)},GradingPanel.prototype.getPanelElement=function(){return $('[data-region="grade-panel"]')},GradingPanel.prototype.collapsePanel=function(){this.getPanelElement().addClass("collapsed")},GradingPanel.prototype.expandPanel=function(){this.getPanelElement().removeClass("collapsed")},GradingPanel.prototype.registerEventListeners=function(){var docElement=$(document);$(this._region).on("submit","form",(function(e){e.preventDefault()})),docElement.on("next-user",this._getNextUser.bind(this)),docElement.on("user-changed",this._refreshGradingPanel.bind(this)),docElement.on("save-changes",this._submitForm.bind(this)),docElement.on("save-and-show-next",this._handleSaveAndShowNext.bind(this)),docElement.on("reset",this._resetForm.bind(this)),docElement.on("save-form-state",this._saveFormState.bind(this)),docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL,function(){this.collapsePanel()}.bind(this)),docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL,function(){this.expandPanel()}.bind(this)),docElement.on(GradingEvents.EXPAND_GRADE_PANEL,function(){this.expandPanel()}.bind(this))},GradingPanel}));
//# sourceMappingURL=grading_panel.min.js.map
\ No newline at end of file
diff --git a/mod/assign/amd/build/grading_panel.min.js.map b/mod/assign/amd/build/grading_panel.min.js.map
index 930e9b4c6d308..86989108b7e0c 100644
--- a/mod/assign/amd/build/grading_panel.min.js.map
+++ b/mod/assign/amd/build/grading_panel.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"grading_panel.min.js","sources":["../src/grading_panel.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript controller for the \"Grading\" panel at the right of the page.\n *\n * @module mod_assign/grading_panel\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine([\n 'jquery',\n 'core/yui',\n 'core/notification',\n 'core/templates',\n 'core/fragment',\n 'core/ajax',\n 'core/str',\n 'mod_assign/grading_form_change_checker',\n 'mod_assign/grading_events',\n 'core_form/events',\n 'core/toast',\n 'core_form/changechecker',\n], function(\n $,\n Y,\n notification,\n templates,\n fragment,\n ajax,\n str,\n checker,\n GradingEvents,\n FormEvents,\n Toast,\n FormChangeChecker\n) {\n\n /**\n * GradingPanel class.\n *\n * @class mod_assign/grading_panel\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var GradingPanel = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._userCache = [];\n\n this.registerEventListeners();\n };\n\n /** @property {String} Selector for the page region containing the user navigation. */\n GradingPanel.prototype._regionSelector = null;\n\n /** @property {Integer} Remember the last user id to prevent unnessecary reloads. */\n GradingPanel.prototype._lastUserId = 0;\n\n /** @property {Integer} Remember the last attempt number to prevent unnessecary reloads. */\n GradingPanel.prototype._lastAttemptNumber = -1;\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n GradingPanel.prototype._region = null;\n\n /** @property {Integer} The id of the next user in the grading list */\n GradingPanel.prototype.nextUserId = null;\n\n /** @property {Boolean} Next user exists in the grading list */\n GradingPanel.prototype.nextUser = false;\n\n /**\n * Fade the dom node out, update it, and fade it back.\n *\n * @private\n * @method _niceReplaceNodeContents\n * @param {JQuery} node\n * @param {String} html\n * @param {String} js\n * @return {Deferred} promise resolved when the animations are complete.\n */\n GradingPanel.prototype._niceReplaceNodeContents = function(node, html, js) {\n var promise = $.Deferred();\n\n node.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(node, html, js);\n node.fadeIn(\"fast\", function() {\n promise.resolve();\n });\n });\n\n return promise.promise();\n };\n\n /**\n * Make sure all form fields have the latest saved state.\n * @private\n * @method _saveFormState\n */\n GradingPanel.prototype._saveFormState = function() {\n // Copy data from notify students checkbox which was moved out of the form.\n var checked = $('[data-region=\"grading-actions-form\"] [name=\"sendstudentnotifications\"]').prop(\"checked\");\n $('.gradeform [name=\"sendstudentnotifications\"]').val(checked);\n };\n\n /**\n * Make form submit via ajax.\n *\n * @private\n * @param {Object} event\n * @param {Integer} nextUserId\n * @param {Boolean} nextUser optional. Load next user in the grading list.\n * @method _submitForm\n * @fires event:formSubmittedByJavascript\n */\n GradingPanel.prototype._submitForm = function(event, nextUserId, nextUser) {\n // If the form has data in comment-area, then we need to save that comment\n var commentAreaElement = document.querySelector('.comment-area');\n if (commentAreaElement) {\n var commentTextAreaElement = commentAreaElement.querySelector('.db > textarea');\n if (commentTextAreaElement.value !== '') {\n var commentActionPostElement = commentAreaElement.querySelector('.fd a[id^=\"comment-action-post-\"]');\n commentActionPostElement.click();\n }\n }\n\n // The form was submitted - send it via ajax instead.\n var form = $(this._region.find('form.gradeform'));\n\n $('[data-region=\"overlay\"]').show();\n\n // Mark the form as submitted in the change checker.\n FormChangeChecker.markFormSubmitted(form[0]);\n\n // We call this, so other modules can update the form with the latest state.\n form.trigger('save-form-state');\n\n // Tell all form fields we are about to submit the form.\n FormEvents.notifyFormSubmittedByJavascript(form[0]);\n\n // Now we get all the current values from the form.\n var data = form.serialize();\n var assignmentid = this._region.attr('data-assignmentid');\n\n // Now we can continue...\n ajax.call([{\n methodname: 'mod_assign_submit_grading_form',\n args: {assignmentid: assignmentid, userid: this._lastUserId, jsonformdata: JSON.stringify(data)},\n done: this._handleFormSubmissionResponse.bind(this, data, nextUserId, nextUser),\n fail: notification.exception\n }]);\n };\n\n /**\n * Handle form submission response.\n *\n * @private\n * @method _handleFormSubmissionResponse\n * @param {Array} formdata - submitted values\n * @param {Number} [nextUserId] The id of the user to load after the form is saved\n * @param {Boolean} [nextUser] - Whether to switch to next user in the grading list.\n * @param {Array} response List of errors.\n */\n GradingPanel.prototype._handleFormSubmissionResponse = function(formdata, nextUserId, nextUser, response) {\n if (typeof nextUserId === \"undefined\") {\n nextUserId = this._lastUserId;\n }\n if (response.length) {\n // There was an error saving the grade. Re-render the form using the submitted data so we can show\n // validation errors.\n $(document).trigger('reset', [this._lastUserId, formdata]);\n } else {\n str.get_string('gradechangessaveddetail', 'mod_assign')\n .then(function(str) {\n Toast.add(str);\n return str;\n })\n .catch(notification.exception);\n\n // Reset the form state.\n var form = $(this._region.find('form.gradeform'));\n FormChangeChecker.resetFormDirtyState(form[0]);\n\n if (nextUserId == this._lastUserId) {\n $(document).trigger('reset', nextUserId);\n } else if (nextUser) {\n $(document).trigger('done-saving-show-next', true);\n } else {\n $(document).trigger('user-changed', nextUserId);\n }\n }\n $('[data-region=\"overlay\"]').hide();\n };\n\n /**\n * Refresh form with default values.\n *\n * @private\n * @method _resetForm\n * @param {Event} e\n * @param {Integer} userid\n * @param {Array} formdata\n */\n GradingPanel.prototype._resetForm = function(e, userid, formdata) {\n // The form was cancelled - refresh with default values.\n var event = $.Event(\"custom\");\n if (typeof userid == \"undefined\") {\n userid = this._lastUserId;\n }\n this._lastUserId = 0;\n this._refreshGradingPanel(event, userid, formdata);\n };\n\n /**\n * Open a picker to choose an older attempt.\n *\n * @private\n * @param {Object} e\n * @method _chooseAttempt\n */\n GradingPanel.prototype._chooseAttempt = function(e) {\n // Show a dialog.\n\n // The form is in the element pointed to by data-submissions.\n var link = $(e.target);\n var submissionsId = link.data('submissions');\n var submissionsform = $(document.getElementById(submissionsId));\n var formcopy = submissionsform.clone();\n var formhtml = formcopy.wrap($('')).html();\n\n str.get_strings([\n {key: 'viewadifferentattempt', component: 'mod_assign'},\n {key: 'view', component: 'core'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], formhtml, strs[1], strs[2], function() {\n var attemptnumber = $(\"input:radio[name='select-attemptnumber']:checked\").val();\n\n this._refreshGradingPanel(null, this._lastUserId, '', attemptnumber);\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Add popout buttons\n *\n * @private\n * @method _addPopoutButtons\n * @param {JQuery} selector The region selector to add popout buttons to.\n */\n GradingPanel.prototype._addPopoutButtons = function(selector) {\n var region = $(selector);\n\n templates.render('mod_assign/popout_button', {}).done(function(html) {\n var parents = region.find('[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]')\n .closest('.fitem');\n parents.addClass('has-popout').find('label').parent().append(html);\n\n region.on('click', '[data-region=\"popout-button\"]', this._togglePopout.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Make a div \"popout\" or \"popback\".\n *\n * @private\n * @method _togglePopout\n * @param {Event} event\n */\n GradingPanel.prototype._togglePopout = function(event) {\n event.preventDefault();\n var container = $(event.target).closest('.fitem');\n if (container.hasClass('popout')) {\n $('.popout').removeClass('popout');\n } else {\n $('.popout').removeClass('popout');\n container.addClass('popout');\n container.addClass('moodle-has-zindex');\n }\n };\n\n /**\n * Get the user context - re-render the template in the page.\n *\n * @private\n * @method _refreshGradingPanel\n * @param {Event} event\n * @param {Number} userid\n * @param {String} submissiondata serialised submission data.\n * @param {Integer} attemptnumber\n */\n GradingPanel.prototype._refreshGradingPanel = function(event, userid, submissiondata, attemptnumber) {\n var contextid = this._region.attr('data-contextid');\n if (typeof submissiondata === 'undefined') {\n submissiondata = '';\n }\n if (typeof attemptnumber === 'undefined') {\n attemptnumber = -1;\n }\n // Skip reloading if it is the same user.\n if (this._lastUserId == userid && this._lastAttemptNumber == attemptnumber && submissiondata === '') {\n return;\n }\n this._lastUserId = userid;\n this._lastAttemptNumber = attemptnumber;\n $(document).trigger('start-loading-user');\n // Tell behat to back off too.\n window.M.util.js_pending('mod-assign-loading-user');\n // First insert the loading template.\n templates.render('mod_assign/loading', {}).done(function(html, js) {\n // Update the page.\n this._niceReplaceNodeContents(this._region, html, js).done(function() {\n if (userid > 0) {\n this._region.show();\n // Reload the grading form \"fragment\" for this user.\n var params = {userid: userid, attemptnumber: attemptnumber, jsonformdata: JSON.stringify(submissiondata)};\n fragment.loadFragment('mod_assign', 'gradingpanel', contextid, params).done(function(html, js) {\n this._niceReplaceNodeContents(this._region, html, js)\n .done(function() {\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n $(document).on('editor-content-restored', function() {\n // If the editor has some content that has been restored\n // then save the form state again for comparison.\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n });\n $('[data-region=\"attempt-chooser\"]').on('click', this._chooseAttempt.bind(this));\n this._addPopoutButtons('[data-region=\"grade-panel\"] .gradeform');\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }.bind(this))\n .fail(notification.exception);\n }.bind(this)).fail(notification.exception);\n $('[data-region=\"review-panel\"]').show();\n } else {\n this._region.hide();\n $('[data-region=\"review-panel\"]').hide();\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Get next user data and store it in global variables\n *\n * @private\n * @method _getNextUser\n * @param {Event} event\n * @param {Object} data Next user's data\n */\n GradingPanel.prototype._getNextUser = function(event, data) {\n this.nextUserId = data.nextUserId;\n this.nextUser = data.nextUser;\n };\n\n /**\n * Handle the save-and-show-next event\n *\n * @private\n * @method _handleSaveAndShowNext\n */\n GradingPanel.prototype._handleSaveAndShowNext = function() {\n this._submitForm(null, this.nextUserId, this.nextUser);\n };\n\n /**\n * Get the grade panel element.\n *\n * @method getPanelElement\n * @return {jQuery}\n */\n GradingPanel.prototype.getPanelElement = function() {\n return $('[data-region=\"grade-panel\"]');\n };\n\n /**\n * Hide the grade panel.\n *\n * @method collapsePanel\n */\n GradingPanel.prototype.collapsePanel = function() {\n this.getPanelElement().addClass('collapsed');\n };\n\n /**\n * Show the grade panel.\n *\n * @method expandPanel\n */\n GradingPanel.prototype.expandPanel = function() {\n this.getPanelElement().removeClass('collapsed');\n };\n\n /**\n * Register event listeners for the grade panel.\n *\n * @method registerEventListeners\n */\n GradingPanel.prototype.registerEventListeners = function() {\n var docElement = $(document);\n var region = $(this._region);\n // Add an event listener to prevent form submission when pressing enter key.\n region.on('submit', 'form', function(e) {\n e.preventDefault();\n });\n\n docElement.on('next-user', this._getNextUser.bind(this));\n docElement.on('user-changed', this._refreshGradingPanel.bind(this));\n docElement.on('save-changes', this._submitForm.bind(this));\n docElement.on('save-and-show-next', this._handleSaveAndShowNext.bind(this));\n docElement.on('reset', this._resetForm.bind(this));\n\n docElement.on('save-form-state', this._saveFormState.bind(this));\n\n docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL, function() {\n this.collapsePanel();\n }.bind(this));\n\n // We should expand if the review panel is collapsed.\n docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n\n docElement.on(GradingEvents.EXPAND_GRADE_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n };\n\n return GradingPanel;\n});\n"],"names":["define","$","Y","notification","templates","fragment","ajax","str","checker","GradingEvents","FormEvents","Toast","FormChangeChecker","GradingPanel","selector","_regionSelector","_region","_userCache","registerEventListeners","prototype","_lastUserId","_lastAttemptNumber","nextUserId","nextUser","_niceReplaceNodeContents","node","html","js","promise","Deferred","fadeOut","replaceNodeContents","fadeIn","resolve","_saveFormState","checked","prop","val","_submitForm","event","commentAreaElement","document","querySelector","value","click","form","this","find","show","markFormSubmitted","trigger","notifyFormSubmittedByJavascript","data","serialize","assignmentid","attr","call","methodname","args","userid","jsonformdata","JSON","stringify","done","_handleFormSubmissionResponse","bind","fail","exception","formdata","response","length","get_string","then","add","catch","resetFormDirtyState","hide","_resetForm","e","Event","_refreshGradingPanel","_chooseAttempt","submissionsId","target","formhtml","getElementById","clone","wrap","get_strings","key","component","strs","confirm","attemptnumber","_addPopoutButtons","region","render","closest","addClass","parent","append","on","_togglePopout","preventDefault","container","hasClass","removeClass","submissiondata","contextid","window","M","util","js_pending","params","loadFragment","saveFormState","js_complete","_getNextUser","_handleSaveAndShowNext","getPanelElement","collapsePanel","expandPanel","docElement","COLLAPSE_GRADE_PANEL","COLLAPSE_REVIEW_PANEL","EXPAND_GRADE_PANEL"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"grading_panel.min.js","sources":["../src/grading_panel.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript controller for the \"Grading\" panel at the right of the page.\n *\n * @module mod_assign/grading_panel\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine([\n 'jquery',\n 'core/yui',\n 'core/notification',\n 'core/templates',\n 'core/fragment',\n 'core/ajax',\n 'core/str',\n 'mod_assign/grading_form_change_checker',\n 'mod_assign/grading_events',\n 'core_form/events',\n 'core/toast',\n 'core_form/changechecker',\n], function(\n $,\n Y,\n notification,\n templates,\n fragment,\n ajax,\n str,\n checker,\n GradingEvents,\n FormEvents,\n Toast,\n FormChangeChecker\n) {\n\n /**\n * GradingPanel class.\n *\n * @class mod_assign/grading_panel\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var GradingPanel = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._userCache = [];\n\n this.registerEventListeners();\n };\n\n /** @property {String} Selector for the page region containing the user navigation. */\n GradingPanel.prototype._regionSelector = null;\n\n /** @property {Integer} Remember the last user id to prevent unnessecary reloads. */\n GradingPanel.prototype._lastUserId = 0;\n\n /** @property {Integer} Remember the last attempt number to prevent unnessecary reloads. */\n GradingPanel.prototype._lastAttemptNumber = -1;\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n GradingPanel.prototype._region = null;\n\n /** @property {Integer} The id of the next user in the grading list */\n GradingPanel.prototype.nextUserId = null;\n\n /** @property {Boolean} Next user exists in the grading list */\n GradingPanel.prototype.nextUser = false;\n\n /**\n * Fade the dom node out, update it, and fade it back.\n *\n * @private\n * @method _niceReplaceNodeContents\n * @param {JQuery} node\n * @param {String} html\n * @param {String} js\n * @return {Deferred} promise resolved when the animations are complete.\n */\n GradingPanel.prototype._niceReplaceNodeContents = function(node, html, js) {\n var promise = $.Deferred();\n\n node.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(node, html, js);\n node.fadeIn(\"fast\", function() {\n promise.resolve();\n });\n });\n\n return promise.promise();\n };\n\n /**\n * Make sure all form fields have the latest saved state.\n * @private\n * @method _saveFormState\n */\n GradingPanel.prototype._saveFormState = function() {\n // Copy data from notify students checkbox which was moved out of the form.\n var checked = $('[data-region=\"grading-actions-form\"] [name=\"sendstudentnotifications\"]').prop(\"checked\");\n $('.gradeform [name=\"sendstudentnotifications\"]').val(checked);\n };\n\n /**\n * Make form submit via ajax.\n *\n * @private\n * @param {Object} event\n * @param {Integer} nextUserId\n * @param {Boolean} nextUser optional. Load next user in the grading list.\n * @method _submitForm\n * @fires event:formSubmittedByJavascript\n */\n GradingPanel.prototype._submitForm = function(event, nextUserId, nextUser) {\n // If the form has data in comment-area, then we need to save that comment\n var commentAreaElement = document.querySelector('.comment-area');\n if (commentAreaElement) {\n var commentTextAreaElement = commentAreaElement.querySelector('.db > textarea');\n if (commentTextAreaElement.value !== '') {\n var commentActionPostElement = commentAreaElement.querySelector('.fd a[id^=\"comment-action-post-\"]');\n commentActionPostElement.click();\n }\n }\n\n // The form was submitted - send it via ajax instead.\n var form = $(this._region.find('form.gradeform'));\n\n $('[data-region=\"overlay\"]').show();\n\n // Mark the form as submitted in the change checker.\n FormChangeChecker.markFormSubmitted(form[0]);\n\n // We call this, so other modules can update the form with the latest state.\n form.trigger('save-form-state');\n\n // Tell all form fields we are about to submit the form.\n FormEvents.notifyFormSubmittedByJavascript(form[0]);\n\n // Now we get all the current values from the form.\n var data = form.serialize();\n var assignmentid = this._region.attr('data-assignmentid');\n\n // Now we can continue...\n ajax.call([{\n methodname: 'mod_assign_submit_grading_form',\n args: {assignmentid: assignmentid, userid: this._lastUserId, jsonformdata: JSON.stringify(data)},\n done: this._handleFormSubmissionResponse.bind(this, data, nextUserId, nextUser),\n fail: notification.exception\n }]);\n };\n\n /**\n * Handle form submission response.\n *\n * @private\n * @method _handleFormSubmissionResponse\n * @param {Array} formdata - submitted values\n * @param {Number} [nextUserId] The id of the user to load after the form is saved\n * @param {Boolean} [nextUser] - Whether to switch to next user in the grading list.\n * @param {Array} response List of errors.\n */\n GradingPanel.prototype._handleFormSubmissionResponse = function(formdata, nextUserId, nextUser, response) {\n if (typeof nextUserId === \"undefined\") {\n nextUserId = this._lastUserId;\n }\n if (response.length) {\n str.get_string('errorgradechangessaveddetail', 'mod_assign')\n .then(function(str) {\n Toast.add(str, {type: 'danger', delay: 4000});\n return str;\n })\n .catch(notification.exception);\n\n // There was an error saving the grade. Re-render the form using the submitted data so we can show\n // validation errors.\n $(document).trigger('reset', [this._lastUserId, formdata, true]);\n } else {\n str.get_string('gradechangessaveddetail', 'mod_assign')\n .then(function(str) {\n Toast.add(str);\n return str;\n })\n .catch(notification.exception);\n\n // Reset the form state.\n var form = $(this._region.find('form.gradeform'));\n FormChangeChecker.resetFormDirtyState(form[0]);\n\n if (nextUserId == this._lastUserId) {\n $(document).trigger('reset', nextUserId);\n } else if (nextUser) {\n $(document).trigger('done-saving-show-next', true);\n } else {\n $(document).trigger('user-changed', nextUserId);\n }\n }\n $('[data-region=\"overlay\"]').hide();\n };\n\n /**\n * Refresh form with default values.\n *\n * @private\n * @method _resetForm\n * @param {Event} e\n * @param {Integer} userid\n * @param {Array} formdata\n * @param {Boolean} unresolvederror\n */\n GradingPanel.prototype._resetForm = function(e, userid, formdata, unresolvederror) {\n // The form was cancelled - refresh with default values.\n var event = $.Event(\"custom\");\n if (typeof userid == \"undefined\") {\n userid = this._lastUserId;\n }\n this._lastUserId = 0;\n this._refreshGradingPanel(event, userid, formdata, -1, unresolvederror);\n };\n\n /**\n * Open a picker to choose an older attempt.\n *\n * @private\n * @param {Object} e\n * @method _chooseAttempt\n */\n GradingPanel.prototype._chooseAttempt = function(e) {\n // Show a dialog.\n\n // The form is in the element pointed to by data-submissions.\n var link = $(e.target);\n var submissionsId = link.data('submissions');\n var submissionsform = $(document.getElementById(submissionsId));\n var formcopy = submissionsform.clone();\n var formhtml = formcopy.wrap($('')).html();\n\n str.get_strings([\n {key: 'viewadifferentattempt', component: 'mod_assign'},\n {key: 'view', component: 'core'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], formhtml, strs[1], strs[2], function() {\n var attemptnumber = $(\"input:radio[name='select-attemptnumber']:checked\").val();\n\n this._refreshGradingPanel(null, this._lastUserId, '', attemptnumber);\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Add popout buttons\n *\n * @private\n * @method _addPopoutButtons\n * @param {JQuery} selector The region selector to add popout buttons to.\n */\n GradingPanel.prototype._addPopoutButtons = function(selector) {\n var region = $(selector);\n\n templates.render('mod_assign/popout_button', {}).done(function(html) {\n var parents = region.find('[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]')\n .closest('.fitem');\n parents.addClass('has-popout').find('label').parent().append(html);\n\n region.on('click', '[data-region=\"popout-button\"]', this._togglePopout.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Make a div \"popout\" or \"popback\".\n *\n * @private\n * @method _togglePopout\n * @param {Event} event\n */\n GradingPanel.prototype._togglePopout = function(event) {\n event.preventDefault();\n var container = $(event.target).closest('.fitem');\n if (container.hasClass('popout')) {\n $('.popout').removeClass('popout');\n } else {\n $('.popout').removeClass('popout');\n container.addClass('popout');\n container.addClass('moodle-has-zindex');\n }\n };\n\n /**\n * Get the user context - re-render the template in the page.\n *\n * @private\n * @method _refreshGradingPanel\n * @param {Event} event\n * @param {Number} userid\n * @param {String} submissiondata serialised submission data.\n * @param {Integer} attemptnumber\n * @param {Boolean} unresolvederror\n */\n GradingPanel.prototype._refreshGradingPanel = function(event, userid, submissiondata, attemptnumber, unresolvederror) {\n var contextid = this._region.attr('data-contextid');\n if (typeof submissiondata === 'undefined') {\n submissiondata = '';\n }\n if (typeof attemptnumber === 'undefined') {\n attemptnumber = -1;\n }\n if (typeof unresolvederror === 'undefined') {\n unresolvederror = false;\n }\n // Skip reloading if it is the same user.\n if (this._lastUserId == userid && this._lastAttemptNumber == attemptnumber && submissiondata === '') {\n return;\n }\n this._lastUserId = userid;\n this._lastAttemptNumber = attemptnumber;\n $(document).trigger('start-loading-user');\n // Tell behat to back off too.\n window.M.util.js_pending('mod-assign-loading-user');\n // First insert the loading template.\n templates.render('mod_assign/loading', {}).done(function(html, js) {\n // Update the page.\n this._niceReplaceNodeContents(this._region, html, js).done(function() {\n if (userid > 0) {\n this._region.show();\n // Reload the grading form \"fragment\" for this user.\n var params = {userid: userid, attemptnumber: attemptnumber, jsonformdata: JSON.stringify(submissiondata)};\n fragment.loadFragment('mod_assign', 'gradingpanel', contextid, params).done(function(html, js) {\n this._niceReplaceNodeContents(this._region, html, js)\n .done(function() {\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n $(document).on('editor-content-restored', function() {\n // If the editor has some content that has been restored\n // then save the form state again for comparison.\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n });\n $('[data-region=\"attempt-chooser\"]').on('click', this._chooseAttempt.bind(this));\n this._addPopoutButtons('[data-region=\"grade-panel\"] .gradeform');\n if (unresolvederror) {\n $('[data-region=\"grade-panel\"] .gradeform').data('unresolved-error', true);\n }\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }.bind(this))\n .fail(notification.exception);\n }.bind(this)).fail(notification.exception);\n $('[data-region=\"review-panel\"]').show();\n } else {\n this._region.hide();\n $('[data-region=\"review-panel\"]').hide();\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Get next user data and store it in global variables\n *\n * @private\n * @method _getNextUser\n * @param {Event} event\n * @param {Object} data Next user's data\n */\n GradingPanel.prototype._getNextUser = function(event, data) {\n this.nextUserId = data.nextUserId;\n this.nextUser = data.nextUser;\n };\n\n /**\n * Handle the save-and-show-next event\n *\n * @private\n * @method _handleSaveAndShowNext\n */\n GradingPanel.prototype._handleSaveAndShowNext = function() {\n this._submitForm(null, this.nextUserId, this.nextUser);\n };\n\n /**\n * Get the grade panel element.\n *\n * @method getPanelElement\n * @return {jQuery}\n */\n GradingPanel.prototype.getPanelElement = function() {\n return $('[data-region=\"grade-panel\"]');\n };\n\n /**\n * Hide the grade panel.\n *\n * @method collapsePanel\n */\n GradingPanel.prototype.collapsePanel = function() {\n this.getPanelElement().addClass('collapsed');\n };\n\n /**\n * Show the grade panel.\n *\n * @method expandPanel\n */\n GradingPanel.prototype.expandPanel = function() {\n this.getPanelElement().removeClass('collapsed');\n };\n\n /**\n * Register event listeners for the grade panel.\n *\n * @method registerEventListeners\n */\n GradingPanel.prototype.registerEventListeners = function() {\n var docElement = $(document);\n var region = $(this._region);\n // Add an event listener to prevent form submission when pressing enter key.\n region.on('submit', 'form', function(e) {\n e.preventDefault();\n });\n\n docElement.on('next-user', this._getNextUser.bind(this));\n docElement.on('user-changed', this._refreshGradingPanel.bind(this));\n docElement.on('save-changes', this._submitForm.bind(this));\n docElement.on('save-and-show-next', this._handleSaveAndShowNext.bind(this));\n docElement.on('reset', this._resetForm.bind(this));\n\n docElement.on('save-form-state', this._saveFormState.bind(this));\n\n docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL, function() {\n this.collapsePanel();\n }.bind(this));\n\n // We should expand if the review panel is collapsed.\n docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n\n docElement.on(GradingEvents.EXPAND_GRADE_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n };\n\n return GradingPanel;\n});\n"],"names":["define","$","Y","notification","templates","fragment","ajax","str","checker","GradingEvents","FormEvents","Toast","FormChangeChecker","GradingPanel","selector","_regionSelector","_region","_userCache","registerEventListeners","prototype","_lastUserId","_lastAttemptNumber","nextUserId","nextUser","_niceReplaceNodeContents","node","html","js","promise","Deferred","fadeOut","replaceNodeContents","fadeIn","resolve","_saveFormState","checked","prop","val","_submitForm","event","commentAreaElement","document","querySelector","value","click","form","this","find","show","markFormSubmitted","trigger","notifyFormSubmittedByJavascript","data","serialize","assignmentid","attr","call","methodname","args","userid","jsonformdata","JSON","stringify","done","_handleFormSubmissionResponse","bind","fail","exception","formdata","response","length","get_string","then","add","type","delay","catch","resetFormDirtyState","hide","_resetForm","e","unresolvederror","Event","_refreshGradingPanel","_chooseAttempt","submissionsId","target","formhtml","getElementById","clone","wrap","get_strings","key","component","strs","confirm","attemptnumber","_addPopoutButtons","region","render","closest","addClass","parent","append","on","_togglePopout","preventDefault","container","hasClass","removeClass","submissiondata","contextid","window","M","util","js_pending","params","loadFragment","saveFormState","js_complete","_getNextUser","_handleSaveAndShowNext","getPanelElement","collapsePanel","expandPanel","docElement","COLLAPSE_GRADE_PANEL","COLLAPSE_REVIEW_PANEL","EXPAND_GRADE_PANEL"],"mappings":"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"}
\ No newline at end of file
diff --git a/mod/assign/amd/src/grading_form_change_checker.js b/mod/assign/amd/src/grading_form_change_checker.js
index 3cbaa2bfd183e..2fc173040227c 100644
--- a/mod/assign/amd/src/grading_form_change_checker.js
+++ b/mod/assign/amd/src/grading_form_change_checker.js
@@ -48,8 +48,12 @@ define(['jquery'], function($) {
$(selector).trigger('save-form-state');
var data = $(selector).serialize(),
- previousdata = $(selector).data('saved-form-state');
+ previousdata = $(selector).data('saved-form-state'),
+ unresolvederror = $(selector).data('unresolved-error');
+ if (unresolvederror) {
+ return true;
+ }
if (typeof previousdata === 'undefined') {
return false;
}
diff --git a/mod/assign/amd/src/grading_panel.js b/mod/assign/amd/src/grading_panel.js
index d499071df3d9e..b1d713690e40c 100644
--- a/mod/assign/amd/src/grading_panel.js
+++ b/mod/assign/amd/src/grading_panel.js
@@ -178,9 +178,16 @@ define([
nextUserId = this._lastUserId;
}
if (response.length) {
+ str.get_string('errorgradechangessaveddetail', 'mod_assign')
+ .then(function(str) {
+ Toast.add(str, {type: 'danger', delay: 4000});
+ return str;
+ })
+ .catch(notification.exception);
+
// There was an error saving the grade. Re-render the form using the submitted data so we can show
// validation errors.
- $(document).trigger('reset', [this._lastUserId, formdata]);
+ $(document).trigger('reset', [this._lastUserId, formdata, true]);
} else {
str.get_string('gradechangessaveddetail', 'mod_assign')
.then(function(str) {
@@ -212,15 +219,16 @@ define([
* @param {Event} e
* @param {Integer} userid
* @param {Array} formdata
+ * @param {Boolean} unresolvederror
*/
- GradingPanel.prototype._resetForm = function(e, userid, formdata) {
+ GradingPanel.prototype._resetForm = function(e, userid, formdata, unresolvederror) {
// The form was cancelled - refresh with default values.
var event = $.Event("custom");
if (typeof userid == "undefined") {
userid = this._lastUserId;
}
this._lastUserId = 0;
- this._refreshGradingPanel(event, userid, formdata);
+ this._refreshGradingPanel(event, userid, formdata, -1, unresolvederror);
};
/**
@@ -300,8 +308,9 @@ define([
* @param {Number} userid
* @param {String} submissiondata serialised submission data.
* @param {Integer} attemptnumber
+ * @param {Boolean} unresolvederror
*/
- GradingPanel.prototype._refreshGradingPanel = function(event, userid, submissiondata, attemptnumber) {
+ GradingPanel.prototype._refreshGradingPanel = function(event, userid, submissiondata, attemptnumber, unresolvederror) {
var contextid = this._region.attr('data-contextid');
if (typeof submissiondata === 'undefined') {
submissiondata = '';
@@ -309,6 +318,9 @@ define([
if (typeof attemptnumber === 'undefined') {
attemptnumber = -1;
}
+ if (typeof unresolvederror === 'undefined') {
+ unresolvederror = false;
+ }
// Skip reloading if it is the same user.
if (this._lastUserId == userid && this._lastAttemptNumber == attemptnumber && submissiondata === '') {
return;
@@ -337,6 +349,9 @@ define([
});
$('[data-region="attempt-chooser"]').on('click', this._chooseAttempt.bind(this));
this._addPopoutButtons('[data-region="grade-panel"] .gradeform');
+ if (unresolvederror) {
+ $('[data-region="grade-panel"] .gradeform').data('unresolved-error', true);
+ }
$(document).trigger('finish-loading-user');
// Tell behat we are friends again.
window.M.util.js_complete('mod-assign-loading-user');
diff --git a/mod/assign/lang/en/assign.php b/mod/assign/lang/en/assign.php
index e5b0c553ffb80..c849e34e8e162 100644
--- a/mod/assign/lang/en/assign.php
+++ b/mod/assign/lang/en/assign.php
@@ -218,6 +218,7 @@
$string['enabled'] = 'Enabled';
$string['enabletimelimit'] = 'Enable timed assignments';
$string['enabletimelimit_help'] = 'If enabled, you can set a time limit on assignment settings page.';
+$string['errorgradechangessaveddetail'] = 'The changes to the grade and feedback failed to save';
$string['eventallsubmissionsdownloaded'] = 'All the submissions are being downloaded.';
$string['eventassessablesubmitted'] = 'A submission has been submitted.';
$string['eventbatchsetmarkerallocationviewed'] = 'Batch set marker allocation viewed';