diff --git a/mod/assign/amd/build/grading_panel.min.js b/mod/assign/amd/build/grading_panel.min.js
index 59b397625a6..180796928be 100644
--- a/mod/assign/amd/build/grading_panel.min.js
+++ b/mod/assign/amd/build/grading_panel.min.js
@@ -1,2 +1,2 @@
-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/event","core/toast"],function(a,b,c,d,e,f,g,h,i,j,k){var l=function(b){this._regionSelector=b;this._region=a(b);this._userCache=[];this.registerEventListeners()};l.prototype._regionSelector=null;l.prototype._lastUserId=0;l.prototype._lastAttemptNumber=-1;l.prototype._region=null;l.prototype.nextUserId=null;l.prototype.nextUser=!1;l.prototype._niceReplaceNodeContents=function(b,c,e){var f=a.Deferred();b.fadeOut("fast",function(){d.replaceNodeContents(b,c,e);b.fadeIn("fast",function(){f.resolve()})});return f.promise()};l.prototype._saveFormState=function(){var b=a("[data-region=\"grading-actions-form\"] [name=\"sendstudentnotifications\"]").prop("checked");a(".gradeform [name=\"sendstudentnotifications\"]").val(b)};l.prototype._submitForm=function(b,d,e){var g=a(this._region.find("form.gradeform"));a("[data-region=\"overlay\"]").show();g.trigger("save-form-state");j.notifyFormSubmitAjax(g[0]);var h=g.serialize(),i=this._region.attr("data-assignmentid");f.call([{methodname:"mod_assign_submit_grading_form",args:{assignmentid:i,userid:this._lastUserId,jsonformdata:JSON.stringify(h)},done:this._handleFormSubmissionResponse.bind(this,h,d,e),fail:c.exception}])};l.prototype._handleFormSubmissionResponse=function(d,e,f,h){if("undefined"==typeof e){e=this._lastUserId}if(h.length){a(document).trigger("reset",[this._lastUserId,d])}else{g.get_strings([{key:"gradechangessaveddetail",component:"mod_assign"}]).done(function(a){k.add(a[0])}).fail(c.exception);b.use("moodle-core-formchangechecker",function(){M.core_formchangechecker.reset_form_dirty_state()});if(e==this._lastUserId){a(document).trigger("reset",e)}else if(f){a(document).trigger("done-saving-show-next",!0)}else{a(document).trigger("user-changed",e)}}a("[data-region=\"overlay\"]").hide()};l.prototype._resetForm=function(b,c,d){var e=a.Event("custom");if("undefined"==typeof c){c=this._lastUserId}this._lastUserId=0;this._refreshGradingPanel(e,c,d)};l.prototype._chooseAttempt=function(b){var d=a(b.target),e=d.data("submissions"),f=a(document.getElementById(e)),h=f.clone(),i=h.wrap(a("
")).html();g.get_strings([{key:"viewadifferentattempt",component:"mod_assign"},{key:"view",component:"core"},{key:"cancel",component:"core"}]).done(function(b){c.confirm(b[0],i,b[1],b[2],function(){var b=a("input:radio[name='select-attemptnumber']:checked").val();this._refreshGradingPanel(null,this._lastUserId,"",b)}.bind(this))}.bind(this)).fail(c.exception)};l.prototype._addPopoutButtons=function(b){var e=a(b);d.render("mod_assign/popout_button",{}).done(function(a){var b=e.find("[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]").closest(".fitem");b.addClass("has-popout").find("label").parent().append(a);e.on("click","[data-region=\"popout-button\"]",this._togglePopout.bind(this))}.bind(this)).fail(c.exception)};l.prototype._togglePopout=function(b){b.preventDefault();var c=a(b.target).closest(".fitem");if(c.hasClass("popout")){a(".popout").removeClass("popout")}else{a(".popout").removeClass("popout");c.addClass("popout");c.addClass("moodle-has-zindex")}};l.prototype._refreshGradingPanel=function(b,f,g,i){var j=this._region.attr("data-contextid");if("undefined"==typeof g){g=""}if("undefined"==typeof i){i=-1}if(this._lastUserId==f&&this._lastAttemptNumber==i&&""===g){return}this._lastUserId=f;this._lastAttemptNumber=i;a(document).trigger("start-loading-user");window.M.util.js_pending("mod-assign-loading-user");d.render("mod_assign/loading",{}).done(function(b,d){this._niceReplaceNodeContents(this._region,b,d).done(function(){if(0 textarea");if(""!==h.value){var i=g.querySelector(".fd a[id^=\"comment-action-post-\"]");i.click()}}var k=a(this._region.find("form.gradeform"));a("[data-region=\"overlay\"]").show();k.trigger("save-form-state");j.notifyFormSubmitAjax(k[0]);var l=k.serialize(),m=this._region.attr("data-assignmentid");f.call([{methodname:"mod_assign_submit_grading_form",args:{assignmentid:m,userid:this._lastUserId,jsonformdata:JSON.stringify(l)},done:this._handleFormSubmissionResponse.bind(this,l,d,e),fail:c.exception}])};l.prototype._handleFormSubmissionResponse=function(d,e,f,h){if("undefined"==typeof e){e=this._lastUserId}if(h.length){a(document).trigger("reset",[this._lastUserId,d])}else{g.get_strings([{key:"gradechangessaveddetail",component:"mod_assign"}]).done(function(a){k.add(a[0])}).fail(c.exception);b.use("moodle-core-formchangechecker",function(){M.core_formchangechecker.reset_form_dirty_state()});if(e==this._lastUserId){a(document).trigger("reset",e)}else if(f){a(document).trigger("done-saving-show-next",!0)}else{a(document).trigger("user-changed",e)}}a("[data-region=\"overlay\"]").hide()};l.prototype._resetForm=function(b,c,d){var e=a.Event("custom");if("undefined"==typeof c){c=this._lastUserId}this._lastUserId=0;this._refreshGradingPanel(e,c,d)};l.prototype._chooseAttempt=function(b){var d=a(b.target),e=d.data("submissions"),f=a(document.getElementById(e)),h=f.clone(),i=h.wrap(a("")).html();g.get_strings([{key:"viewadifferentattempt",component:"mod_assign"},{key:"view",component:"core"},{key:"cancel",component:"core"}]).done(function(b){c.confirm(b[0],i,b[1],b[2],function(){var b=a("input:radio[name='select-attemptnumber']:checked").val();this._refreshGradingPanel(null,this._lastUserId,"",b)}.bind(this))}.bind(this)).fail(c.exception)};l.prototype._addPopoutButtons=function(b){var e=a(b);d.render("mod_assign/popout_button",{}).done(function(a){var b=e.find("[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]").closest(".fitem");b.addClass("has-popout").find("label").parent().append(a);e.on("click","[data-region=\"popout-button\"]",this._togglePopout.bind(this))}.bind(this)).fail(c.exception)};l.prototype._togglePopout=function(b){b.preventDefault();var c=a(b.target).closest(".fitem");if(c.hasClass("popout")){a(".popout").removeClass("popout")}else{a(".popout").removeClass("popout");c.addClass("popout");c.addClass("moodle-has-zindex")}};l.prototype._refreshGradingPanel=function(b,f,g,i){var j=this._region.attr("data-contextid");if("undefined"==typeof g){g=""}if("undefined"==typeof i){i=-1}if(this._lastUserId==f&&this._lastAttemptNumber==i&&""===g){return}this._lastUserId=f;this._lastAttemptNumber=i;a(document).trigger("start-loading-user");window.M.util.js_pending("mod-assign-loading-user");d.render("mod_assign/loading",{}).done(function(b,d){this._niceReplaceNodeContents(this._region,b,d).done(function(){if(0.\n\n/**\n * Javascript controller for the \"Grading\" panel at the right of the page.\n *\n * @module mod_assign/grading_panel\n * @package mod_assign\n * @class GradingPanel\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', 'core/yui', 'core/notification', 'core/templates', 'core/fragment',\n 'core/ajax', 'core/str', 'mod_assign/grading_form_change_checker',\n 'mod_assign/grading_events', 'core/event', 'core/toast'],\n function($, Y, notification, templates, fragment, ajax, str, checker, GradingEvents, Event, Toast) {\n\n /**\n * GradingPanel class.\n *\n * @class GradingPanel\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 /** @type {String} Selector for the page region containing the user navigation. */\n GradingPanel.prototype._regionSelector = null;\n\n /** @type {Integer} Remember the last user id to prevent unnessecary reloads. */\n GradingPanel.prototype._lastUserId = 0;\n\n /** @type {Integer} Remember the last attempt number to prevent unnessecary reloads. */\n GradingPanel.prototype._lastAttemptNumber = -1;\n\n /** @type {JQuery} JQuery node for the page region containing the user navigation. */\n GradingPanel.prototype._region = null;\n\n /** @type {Integer} The id of the next user in the grading list */\n GradingPanel.prototype.nextUserId = null;\n\n /** @type {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 */\n GradingPanel.prototype._submitForm = function(event, nextUserId, nextUser) {\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 // 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 Event.notifyFormSubmitAjax(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 {Integer} nextUserId - optional. The id of the user to load after the form is saved.\n * @param {Array} response List of errors.\n * @param {Boolean} nextUser - optional. If true, switch to next user in the grading list.\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_strings([\n {key: 'gradechangessaveddetail', component: 'mod_assign'},\n ]).done(function(strs) {\n Toast.add(strs[0]);\n }).fail(notification.exception);\n Y.use('moodle-core-formchangechecker', function() {\n M.core_formchangechecker.reset_form_dirty_state();\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"],"file":"grading_panel.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/grading_panel.js"],"names":["define","$","Y","notification","templates","fragment","ajax","str","checker","GradingEvents","Event","Toast","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","commentTextAreaElement","value","commentActionPostElement","click","form","find","show","trigger","notifyFormSubmitAjax","data","serialize","assignmentid","attr","call","methodname","args","userid","jsonformdata","JSON","stringify","done","_handleFormSubmissionResponse","bind","fail","exception","formdata","response","length","get_strings","key","component","strs","add","use","M","core_formchangechecker","reset_form_dirty_state","hide","_resetForm","e","_refreshGradingPanel","_chooseAttempt","link","target","submissionsId","submissionsform","getElementById","formcopy","clone","formhtml","wrap","confirm","attemptnumber","_addPopoutButtons","region","render","parents","closest","addClass","parent","append","on","_togglePopout","preventDefault","container","hasClass","removeClass","submissiondata","contextid","window","util","js_pending","params","loadFragment","saveFormState","js_complete","_getNextUser","_handleSaveAndShowNext","getPanelElement","collapsePanel","expandPanel","docElement","COLLAPSE_GRADE_PANEL","COLLAPSE_REVIEW_PANEL","EXPAND_GRADE_PANEL"],"mappings":"AAyBAA,OAAM,4BAAC,CAAC,QAAD,CAAW,UAAX,CAAuB,mBAAvB,CAA4C,gBAA5C,CAA8D,eAA9D,CACC,WADD,CACc,UADd,CAC0B,wCAD1B,CAEC,2BAFD,CAE8B,YAF9B,CAE4C,YAF5C,CAAD,CAGC,SAASC,CAAT,CAAYC,CAAZ,CAAeC,CAAf,CAA6BC,CAA7B,CAAwCC,CAAxC,CAAkDC,CAAlD,CAAwDC,CAAxD,CAA6DC,CAA7D,CAAsEC,CAAtE,CAAqFC,CAArF,CAA4FC,CAA5F,CAAmG,CAQtG,GAAIC,CAAAA,CAAY,CAAG,SAASC,CAAT,CAAmB,CAClC,KAAKC,eAAL,CAAuBD,CAAvB,CACA,KAAKE,OAAL,CAAed,CAAC,CAACY,CAAD,CAAhB,CACA,KAAKG,UAAL,CAAkB,EAAlB,CAEA,KAAKC,sBAAL,EACH,CAND,CASAL,CAAY,CAACM,SAAb,CAAuBJ,eAAvB,CAAyC,IAAzC,CAGAF,CAAY,CAACM,SAAb,CAAuBC,WAAvB,CAAqC,CAArC,CAGAP,CAAY,CAACM,SAAb,CAAuBE,kBAAvB,CAA4C,CAAC,CAA7C,CAGAR,CAAY,CAACM,SAAb,CAAuBH,OAAvB,CAAiC,IAAjC,CAGAH,CAAY,CAACM,SAAb,CAAuBG,UAAvB,CAAoC,IAApC,CAGAT,CAAY,CAACM,SAAb,CAAuBI,QAAvB,IAYAV,CAAY,CAACM,SAAb,CAAuBK,wBAAvB,CAAkD,SAASC,CAAT,CAAeC,CAAf,CAAqBC,CAArB,CAAyB,CACvE,GAAIC,CAAAA,CAAO,CAAG1B,CAAC,CAAC2B,QAAF,EAAd,CAEAJ,CAAI,CAACK,OAAL,CAAa,MAAb,CAAqB,UAAW,CAC5BzB,CAAS,CAAC0B,mBAAV,CAA8BN,CAA9B,CAAoCC,CAApC,CAA0CC,CAA1C,EACAF,CAAI,CAACO,MAAL,CAAY,MAAZ,CAAoB,UAAW,CAC3BJ,CAAO,CAACK,OAAR,EACH,CAFD,CAGH,CALD,EAOA,MAAOL,CAAAA,CAAO,CAACA,OAAR,EACV,CAXD,CAkBAf,CAAY,CAACM,SAAb,CAAuBe,cAAvB,CAAwC,UAAW,CAE/C,GAAIC,CAAAA,CAAO,CAAGjC,CAAC,CAAC,4EAAD,CAAD,CAA4EkC,IAA5E,CAAiF,SAAjF,CAAd,CACAlC,CAAC,CAAC,gDAAD,CAAD,CAAkDmC,GAAlD,CAAsDF,CAAtD,CACH,CAJD,CAeAtB,CAAY,CAACM,SAAb,CAAuBmB,WAAvB,CAAqC,SAASC,CAAT,CAAgBjB,CAAhB,CAA4BC,CAA5B,CAAsC,CAEvE,GAAIiB,CAAAA,CAAkB,CAAGC,QAAQ,CAACC,aAAT,CAAuB,eAAvB,CAAzB,CACA,GAAIF,CAAJ,CAAwB,CACpB,GAAIG,CAAAA,CAAsB,CAAGH,CAAkB,CAACE,aAAnB,CAAiC,gBAAjC,CAA7B,CACA,GAAqC,EAAjC,GAAAC,CAAsB,CAACC,KAA3B,CAAyC,CACrC,GAAIC,CAAAA,CAAwB,CAAGL,CAAkB,CAACE,aAAnB,CAAiC,qCAAjC,CAA/B,CACAG,CAAwB,CAACC,KAAzB,EACH,CACJ,CAGD,GAAIC,CAAAA,CAAI,CAAG7C,CAAC,CAAC,KAAKc,OAAL,CAAagC,IAAb,CAAkB,gBAAlB,CAAD,CAAZ,CAEA9C,CAAC,CAAC,2BAAD,CAAD,CAA6B+C,IAA7B,GAGAF,CAAI,CAACG,OAAL,CAAa,iBAAb,EAGAvC,CAAK,CAACwC,oBAAN,CAA2BJ,CAAI,CAAC,CAAD,CAA/B,EApBuE,GAuBnEK,CAAAA,CAAI,CAAGL,CAAI,CAACM,SAAL,EAvB4D,CAwBnEC,CAAY,CAAG,KAAKtC,OAAL,CAAauC,IAAb,CAAkB,mBAAlB,CAxBoD,CA2BvEhD,CAAI,CAACiD,IAAL,CAAU,CAAC,CACPC,UAAU,CAAE,gCADL,CAEPC,IAAI,CAAE,CAACJ,YAAY,CAAEA,CAAf,CAA6BK,MAAM,CAAE,KAAKvC,WAA1C,CAAuDwC,YAAY,CAAEC,IAAI,CAACC,SAAL,CAAeV,CAAf,CAArE,CAFC,CAGPW,IAAI,CAAE,KAAKC,6BAAL,CAAmCC,IAAnC,CAAwC,IAAxC,CAA8Cb,CAA9C,CAAoD9B,CAApD,CAAgEC,CAAhE,CAHC,CAIP2C,IAAI,CAAE9D,CAAY,CAAC+D,SAJZ,CAAD,CAAV,CAMH,CAjCD,CA6CAtD,CAAY,CAACM,SAAb,CAAuB6C,6BAAvB,CAAuD,SAASI,CAAT,CAAmB9C,CAAnB,CAA+BC,CAA/B,CAAyC8C,CAAzC,CAAmD,CACtG,GAA0B,WAAtB,QAAO/C,CAAAA,CAAX,CAAuC,CACnCA,CAAU,CAAG,KAAKF,WACrB,CACD,GAAIiD,CAAQ,CAACC,MAAb,CAAqB,CAGjBpE,CAAC,CAACuC,QAAD,CAAD,CAAYS,OAAZ,CAAoB,OAApB,CAA6B,CAAC,KAAK9B,WAAN,CAAmBgD,CAAnB,CAA7B,CACH,CAJD,IAIO,CACH5D,CAAG,CAAC+D,WAAJ,CAAgB,CACZ,CAACC,GAAG,CAAE,yBAAN,CAAiCC,SAAS,CAAE,YAA5C,CADY,CAAhB,EAEGV,IAFH,CAEQ,SAASW,CAAT,CAAe,CACnB9D,CAAK,CAAC+D,GAAN,CAAUD,CAAI,CAAC,CAAD,CAAd,CACH,CAJD,EAIGR,IAJH,CAIQ9D,CAAY,CAAC+D,SAJrB,EAKAhE,CAAC,CAACyE,GAAF,CAAM,+BAAN,CAAuC,UAAW,CAC9CC,CAAC,CAACC,sBAAF,CAAyBC,sBAAzB,EACH,CAFD,EAGA,GAAIzD,CAAU,EAAI,KAAKF,WAAvB,CAAoC,CAChClB,CAAC,CAACuC,QAAD,CAAD,CAAYS,OAAZ,CAAoB,OAApB,CAA6B5B,CAA7B,CACH,CAFD,IAEO,IAAIC,CAAJ,CAAc,CACjBrB,CAAC,CAACuC,QAAD,CAAD,CAAYS,OAAZ,CAAoB,uBAApB,IACH,CAFM,IAEA,CACHhD,CAAC,CAACuC,QAAD,CAAD,CAAYS,OAAZ,CAAoB,cAApB,CAAoC5B,CAApC,CACH,CACJ,CACDpB,CAAC,CAAC,2BAAD,CAAD,CAA6B8E,IAA7B,EACH,CA1BD,CAqCAnE,CAAY,CAACM,SAAb,CAAuB8D,UAAvB,CAAoC,SAASC,CAAT,CAAYvB,CAAZ,CAAoBS,CAApB,CAA8B,CAE9D,GAAI7B,CAAAA,CAAK,CAAGrC,CAAC,CAACS,KAAF,CAAQ,QAAR,CAAZ,CACA,GAAqB,WAAjB,QAAOgD,CAAAA,CAAX,CAAkC,CAC9BA,CAAM,CAAG,KAAKvC,WACjB,CACD,KAAKA,WAAL,CAAmB,CAAnB,CACA,KAAK+D,oBAAL,CAA0B5C,CAA1B,CAAiCoB,CAAjC,CAAyCS,CAAzC,CACH,CARD,CAiBAvD,CAAY,CAACM,SAAb,CAAuBiE,cAAvB,CAAwC,SAASF,CAAT,CAAY,IAI5CG,CAAAA,CAAI,CAAGnF,CAAC,CAACgF,CAAC,CAACI,MAAH,CAJoC,CAK5CC,CAAa,CAAGF,CAAI,CAACjC,IAAL,CAAU,aAAV,CAL4B,CAM5CoC,CAAe,CAAGtF,CAAC,CAACuC,QAAQ,CAACgD,cAAT,CAAwBF,CAAxB,CAAD,CANyB,CAO5CG,CAAQ,CAAGF,CAAe,CAACG,KAAhB,EAPiC,CAQ5CC,CAAQ,CAAGF,CAAQ,CAACG,IAAT,CAAc3F,CAAC,CAAC,SAAD,CAAf,EAA4BwB,IAA5B,EARiC,CAUhDlB,CAAG,CAAC+D,WAAJ,CAAgB,CACZ,CAACC,GAAG,CAAE,uBAAN,CAA+BC,SAAS,CAAE,YAA1C,CADY,CAEZ,CAACD,GAAG,CAAE,MAAN,CAAcC,SAAS,CAAE,MAAzB,CAFY,CAGZ,CAACD,GAAG,CAAE,QAAN,CAAgBC,SAAS,CAAE,MAA3B,CAHY,CAAhB,EAIGV,IAJH,CAIQ,SAASW,CAAT,CAAe,CACnBtE,CAAY,CAAC0F,OAAb,CAAqBpB,CAAI,CAAC,CAAD,CAAzB,CAA8BkB,CAA9B,CAAwClB,CAAI,CAAC,CAAD,CAA5C,CAAiDA,CAAI,CAAC,CAAD,CAArD,CAA0D,UAAW,CACjE,GAAIqB,CAAAA,CAAa,CAAG7F,CAAC,CAAC,kDAAD,CAAD,CAAsDmC,GAAtD,EAApB,CAEA,KAAK8C,oBAAL,CAA0B,IAA1B,CAAgC,KAAK/D,WAArC,CAAkD,EAAlD,CAAsD2E,CAAtD,CACH,CAJyD,CAIxD9B,IAJwD,CAInD,IAJmD,CAA1D,CAKH,CANO,CAMNA,IANM,CAMD,IANC,CAJR,EAUcC,IAVd,CAUmB9D,CAAY,CAAC+D,SAVhC,CAWH,CArBD,CA8BAtD,CAAY,CAACM,SAAb,CAAuB6E,iBAAvB,CAA2C,SAASlF,CAAT,CAAmB,CAC1D,GAAImF,CAAAA,CAAM,CAAG/F,CAAC,CAACY,CAAD,CAAd,CAEAT,CAAS,CAAC6F,MAAV,CAAiB,0BAAjB,CAA6C,EAA7C,EAAiDnC,IAAjD,CAAsD,SAASrC,CAAT,CAAe,CACjE,GAAIyE,CAAAA,CAAO,CAAGF,CAAM,CAACjD,IAAP,CAAY,2FAAZ,EACLoD,OADK,CACG,QADH,CAAd,CAEAD,CAAO,CAACE,QAAR,CAAiB,YAAjB,EAA+BrD,IAA/B,CAAoC,OAApC,EAA6CsD,MAA7C,GAAsDC,MAAtD,CAA6D7E,CAA7D,EAEAuE,CAAM,CAACO,EAAP,CAAU,OAAV,CAAmB,iCAAnB,CAAoD,KAAKC,aAAL,CAAmBxC,IAAnB,CAAwB,IAAxB,CAApD,CACH,CANqD,CAMpDA,IANoD,CAM/C,IAN+C,CAAtD,EAMcC,IANd,CAMmB9D,CAAY,CAAC+D,SANhC,CAOH,CAVD,CAmBAtD,CAAY,CAACM,SAAb,CAAuBsF,aAAvB,CAAuC,SAASlE,CAAT,CAAgB,CACnDA,CAAK,CAACmE,cAAN,GACA,GAAIC,CAAAA,CAAS,CAAGzG,CAAC,CAACqC,CAAK,CAAC+C,MAAP,CAAD,CAAgBc,OAAhB,CAAwB,QAAxB,CAAhB,CACA,GAAIO,CAAS,CAACC,QAAV,CAAmB,QAAnB,CAAJ,CAAkC,CAC9B1G,CAAC,CAAC,SAAD,CAAD,CAAa2G,WAAb,CAAyB,QAAzB,CACH,CAFD,IAEO,CACH3G,CAAC,CAAC,SAAD,CAAD,CAAa2G,WAAb,CAAyB,QAAzB,EACAF,CAAS,CAACN,QAAV,CAAmB,QAAnB,EACAM,CAAS,CAACN,QAAV,CAAmB,mBAAnB,CACH,CACJ,CAVD,CAsBAxF,CAAY,CAACM,SAAb,CAAuBgE,oBAAvB,CAA8C,SAAS5C,CAAT,CAAgBoB,CAAhB,CAAwBmD,CAAxB,CAAwCf,CAAxC,CAAuD,CACjG,GAAIgB,CAAAA,CAAS,CAAG,KAAK/F,OAAL,CAAauC,IAAb,CAAkB,gBAAlB,CAAhB,CACA,GAA8B,WAA1B,QAAOuD,CAAAA,CAAX,CAA2C,CACvCA,CAAc,CAAG,EACpB,CACD,GAA6B,WAAzB,QAAOf,CAAAA,CAAX,CAA0C,CACtCA,CAAa,CAAG,CAAC,CACpB,CAED,GAAI,KAAK3E,WAAL,EAAoBuC,CAApB,EAA8B,KAAKtC,kBAAL,EAA2B0E,CAAzD,EAA6F,EAAnB,GAAAe,CAA9E,CAAqG,CACjG,MACH,CACD,KAAK1F,WAAL,CAAmBuC,CAAnB,CACA,KAAKtC,kBAAL,CAA0B0E,CAA1B,CACA7F,CAAC,CAACuC,QAAD,CAAD,CAAYS,OAAZ,CAAoB,oBAApB,EAEA8D,MAAM,CAACnC,CAAP,CAASoC,IAAT,CAAcC,UAAd,CAAyB,yBAAzB,EAEA7G,CAAS,CAAC6F,MAAV,CAAiB,oBAAjB,CAAuC,EAAvC,EAA2CnC,IAA3C,CAAgD,SAASrC,CAAT,CAAeC,CAAf,CAAmB,CAE/D,KAAKH,wBAAL,CAA8B,KAAKR,OAAnC,CAA4CU,CAA5C,CAAkDC,CAAlD,EAAsDoC,IAAtD,CAA2D,UAAW,CAClE,GAAa,CAAT,CAAAJ,CAAJ,CAAgB,CACZ,KAAK3C,OAAL,CAAaiC,IAAb,GAEA,GAAIkE,CAAAA,CAAM,CAAG,CAACxD,MAAM,CAAEA,CAAT,CAAiBoC,aAAa,CAAEA,CAAhC,CAA+CnC,YAAY,CAAEC,IAAI,CAACC,SAAL,CAAegD,CAAf,CAA7D,CAAb,CACAxG,CAAQ,CAAC8G,YAAT,CAAsB,YAAtB,CAAoC,cAApC,CAAoDL,CAApD,CAA+DI,CAA/D,EAAuEpD,IAAvE,CAA4E,SAASrC,CAAT,CAAeC,CAAf,CAAmB,CAC3F,KAAKH,wBAAL,CAA8B,KAAKR,OAAnC,CAA4CU,CAA5C,CAAkDC,CAAlD,EACCoC,IADD,CACM,UAAW,CACbtD,CAAO,CAAC4G,aAAR,CAAsB,0CAAtB,EACAnH,CAAC,CAACuC,QAAD,CAAD,CAAY+D,EAAZ,CAAe,yBAAf,CAA0C,UAAW,CAGjD/F,CAAO,CAAC4G,aAAR,CAAsB,0CAAtB,CACH,CAJD,EAKAnH,CAAC,CAAC,mCAAD,CAAD,CAAqCsG,EAArC,CAAwC,OAAxC,CAAiD,KAAKpB,cAAL,CAAoBnB,IAApB,CAAyB,IAAzB,CAAjD,EACA,KAAK+B,iBAAL,CAAuB,0CAAvB,EACA9F,CAAC,CAACuC,QAAD,CAAD,CAAYS,OAAZ,CAAoB,qBAApB,EAEA8D,MAAM,CAACnC,CAAP,CAASoC,IAAT,CAAcK,WAAd,CAA0B,yBAA1B,CACH,CAZK,CAYJrD,IAZI,CAYC,IAZD,CADN,EAcCC,IAdD,CAcM9D,CAAY,CAAC+D,SAdnB,CAeH,CAhB2E,CAgB1EF,IAhB0E,CAgBrE,IAhBqE,CAA5E,EAgBcC,IAhBd,CAgBmB9D,CAAY,CAAC+D,SAhBhC,EAiBAjE,CAAC,CAAC,gCAAD,CAAD,CAAkC+C,IAAlC,EACH,CAtBD,IAsBO,CACH,KAAKjC,OAAL,CAAagE,IAAb,GACA9E,CAAC,CAAC,gCAAD,CAAD,CAAkC8E,IAAlC,GACA9E,CAAC,CAACuC,QAAD,CAAD,CAAYS,OAAZ,CAAoB,qBAApB,EAEA8D,MAAM,CAACnC,CAAP,CAASoC,IAAT,CAAcK,WAAd,CAA0B,yBAA1B,CACH,CACJ,CA9B0D,CA8BzDrD,IA9ByD,CA8BpD,IA9BoD,CAA3D,CA+BH,CAjC+C,CAiC9CA,IAjC8C,CAiCzC,IAjCyC,CAAhD,EAiCcC,IAjCd,CAiCmB9D,CAAY,CAAC+D,SAjChC,CAkCH,CApDD,CA8DAtD,CAAY,CAACM,SAAb,CAAuBoG,YAAvB,CAAsC,SAAShF,CAAT,CAAgBa,CAAhB,CAAsB,CACxD,KAAK9B,UAAL,CAAkB8B,CAAI,CAAC9B,UAAvB,CACA,KAAKC,QAAL,CAAgB6B,CAAI,CAAC7B,QACxB,CAHD,CAWAV,CAAY,CAACM,SAAb,CAAuBqG,sBAAvB,CAAgD,UAAW,CACvD,KAAKlF,WAAL,CAAiB,IAAjB,CAAuB,KAAKhB,UAA5B,CAAwC,KAAKC,QAA7C,CACH,CAFD,CAUAV,CAAY,CAACM,SAAb,CAAuBsG,eAAvB,CAAyC,UAAW,CAChD,MAAOvH,CAAAA,CAAC,CAAC,+BAAD,CACX,CAFD,CASAW,CAAY,CAACM,SAAb,CAAuBuG,aAAvB,CAAuC,UAAW,CAC9C,KAAKD,eAAL,GAAuBpB,QAAvB,CAAgC,WAAhC,CACH,CAFD,CASAxF,CAAY,CAACM,SAAb,CAAuBwG,WAAvB,CAAqC,UAAW,CAC5C,KAAKF,eAAL,GAAuBZ,WAAvB,CAAmC,WAAnC,CACH,CAFD,CASAhG,CAAY,CAACM,SAAb,CAAuBD,sBAAvB,CAAgD,UAAW,IACnD0G,CAAAA,CAAU,CAAG1H,CAAC,CAACuC,QAAD,CADqC,CAEnDwD,CAAM,CAAG/F,CAAC,CAAC,KAAKc,OAAN,CAFyC,CAIvDiF,CAAM,CAACO,EAAP,CAAU,QAAV,CAAoB,MAApB,CAA4B,SAAStB,CAAT,CAAY,CACpCA,CAAC,CAACwB,cAAF,EACH,CAFD,EAIAkB,CAAU,CAACpB,EAAX,CAAc,WAAd,CAA2B,KAAKe,YAAL,CAAkBtD,IAAlB,CAAuB,IAAvB,CAA3B,EACA2D,CAAU,CAACpB,EAAX,CAAc,cAAd,CAA8B,KAAKrB,oBAAL,CAA0BlB,IAA1B,CAA+B,IAA/B,CAA9B,EACA2D,CAAU,CAACpB,EAAX,CAAc,cAAd,CAA8B,KAAKlE,WAAL,CAAiB2B,IAAjB,CAAsB,IAAtB,CAA9B,EACA2D,CAAU,CAACpB,EAAX,CAAc,oBAAd,CAAoC,KAAKgB,sBAAL,CAA4BvD,IAA5B,CAAiC,IAAjC,CAApC,EACA2D,CAAU,CAACpB,EAAX,CAAc,OAAd,CAAuB,KAAKvB,UAAL,CAAgBhB,IAAhB,CAAqB,IAArB,CAAvB,EAEA2D,CAAU,CAACpB,EAAX,CAAc,iBAAd,CAAiC,KAAKtE,cAAL,CAAoB+B,IAApB,CAAyB,IAAzB,CAAjC,EAEA2D,CAAU,CAACpB,EAAX,CAAc9F,CAAa,CAACmH,oBAA5B,CAAkD,UAAW,CACzD,KAAKH,aAAL,EACH,CAFiD,CAEhDzD,IAFgD,CAE3C,IAF2C,CAAlD,EAKA2D,CAAU,CAACpB,EAAX,CAAc9F,CAAa,CAACoH,qBAA5B,CAAmD,UAAW,CAC1D,KAAKH,WAAL,EACH,CAFkD,CAEjD1D,IAFiD,CAE5C,IAF4C,CAAnD,EAIA2D,CAAU,CAACpB,EAAX,CAAc9F,CAAa,CAACqH,kBAA5B,CAAgD,UAAW,CACvD,KAAKJ,WAAL,EACH,CAF+C,CAE9C1D,IAF8C,CAEzC,IAFyC,CAAhD,CAGH,CA5BD,CA8BA,MAAOpD,CAAAA,CACV,CAvYK,CAAN","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 * @package mod_assign\n * @class GradingPanel\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', 'core/yui', 'core/notification', 'core/templates', 'core/fragment',\n 'core/ajax', 'core/str', 'mod_assign/grading_form_change_checker',\n 'mod_assign/grading_events', 'core/event', 'core/toast'],\n function($, Y, notification, templates, fragment, ajax, str, checker, GradingEvents, Event, Toast) {\n\n /**\n * GradingPanel class.\n *\n * @class GradingPanel\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 /** @type {String} Selector for the page region containing the user navigation. */\n GradingPanel.prototype._regionSelector = null;\n\n /** @type {Integer} Remember the last user id to prevent unnessecary reloads. */\n GradingPanel.prototype._lastUserId = 0;\n\n /** @type {Integer} Remember the last attempt number to prevent unnessecary reloads. */\n GradingPanel.prototype._lastAttemptNumber = -1;\n\n /** @type {JQuery} JQuery node for the page region containing the user navigation. */\n GradingPanel.prototype._region = null;\n\n /** @type {Integer} The id of the next user in the grading list */\n GradingPanel.prototype.nextUserId = null;\n\n /** @type {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 */\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 // 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 Event.notifyFormSubmitAjax(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 {Integer} nextUserId - optional. The id of the user to load after the form is saved.\n * @param {Array} response List of errors.\n * @param {Boolean} nextUser - optional. If true, switch to next user in the grading list.\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_strings([\n {key: 'gradechangessaveddetail', component: 'mod_assign'},\n ]).done(function(strs) {\n Toast.add(strs[0]);\n }).fail(notification.exception);\n Y.use('moodle-core-formchangechecker', function() {\n M.core_formchangechecker.reset_form_dirty_state();\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"],"file":"grading_panel.min.js"}
\ No newline at end of file
diff --git a/mod/assign/amd/src/grading_panel.js b/mod/assign/amd/src/grading_panel.js
index dbd75ff0087..fca1a1e018e 100644
--- a/mod/assign/amd/src/grading_panel.js
+++ b/mod/assign/amd/src/grading_panel.js
@@ -104,6 +104,16 @@ define(['jquery', 'core/yui', 'core/notification', 'core/templates', 'core/fragm
* @method _submitForm
*/
GradingPanel.prototype._submitForm = function(event, nextUserId, nextUser) {
+ // If the form has data in comment-area, then we need to save that comment
+ var commentAreaElement = document.querySelector('.comment-area');
+ if (commentAreaElement) {
+ var commentTextAreaElement = commentAreaElement.querySelector('.db > textarea');
+ if (commentTextAreaElement.value !== '') {
+ var commentActionPostElement = commentAreaElement.querySelector('.fd a[id^="comment-action-post-"]');
+ commentActionPostElement.click();
+ }
+ }
+
// The form was submitted - send it via ajax instead.
var form = $(this._region.find('form.gradeform'));
diff --git a/mod/assign/tests/behat/submission_comments.feature b/mod/assign/tests/behat/submission_comments.feature
index ec77765c5c2..7e4ddd5511a 100644
--- a/mod/assign/tests/behat/submission_comments.feature
+++ b/mod/assign/tests/behat/submission_comments.feature
@@ -12,10 +12,12 @@ Feature: In an assignment, students can comment in their submissions
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
+ | student2 | Student | 1 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
+ | student2 | C1 | student |
Scenario: Student comments an assignment submission
Given the following "activities" exist:
@@ -45,6 +47,57 @@ Feature: In an assignment, students can comment in their submissions
And I should see "Second student comment"
And I should not see "First student comment"
+ Scenario: Teacher updated the comment box and clicked the save changes to reflect the comment
+ Given the following "activities" exist:
+ | activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled |
+ | assign | C1 | assign1 | Test assignment name | Test assignment description | 1 |
+ And I log in as "student1"
+ And I am on "Course 1" course homepage
+ And I follow "Test assignment name"
+ And I press "Add submission"
+ And I set the following fields to these values:
+ | Online text | I'm the student submission |
+ And I press "Save changes"
+ And I log out
+ And I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ And I follow "Test assignment name"
+ And I navigate to "View all submissions" in current page administration
+ And I click on "Grade" "link" in the "Student 1" "table_row"
+ And I click on ".comment-link" "css_element"
+ When I set the field "content" to "Teacher feedback first comment"
+ And I press "Save changes"
+ And I should see "Comments (1)" in the ".comment-link" "css_element"
+ And I click on ".comment-link" "css_element"
+ Then I should see "Teacher feedback first comment" in the ".comment-list" "css_element"
+
+ Scenario: Teacher updated the comment box and clicked on save and show next to reflect the comment
+ Given the following "activities" exist:
+ | activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled |
+ | assign | C1 | assign1 | Test assignment name | Test assignment description | 1 |
+ And I log in as "student1"
+ And I am on "Course 1" course homepage
+ And I follow "Test assignment name"
+ And I press "Add submission"
+ And I set the following fields to these values:
+ | Online text | I'm the student submission |
+ And I press "Save changes"
+ And I log out
+ And I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ And I follow "Test assignment name"
+ And I navigate to "View all submissions" in current page administration
+ And I click on "Grade" "link" in the "Student 1" "table_row"
+ And I click on ".comment-link" "css_element"
+ When I set the field "content" to "Teacher feedback first comment"
+ # click the save and show next twice as we have only 2 students
+ # so the second time you click we reach the same student who made
+ # the change
+ And I press "Save and show next"
+ And I press "Save and show next"
+ And I click on ".comment-link" "css_element"
+ Then I should see "Teacher feedback first comment" in the ".comment-list" "css_element"
+
Scenario: Teacher can comment on an offline assignment
Given the following "activities" exist:
| activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled | assignmentsubmission_file_enabled | assignfeedback_comments_enabled |