mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
245f45f4d6
Keyboard navigation (ctrl+up/down/left/right) with AJAX enabled ceased functioning with editing mode enabled after MDL-36606, as it was looking for cells with a "clickable" class - but was only applying that class when editing mode is NOT enabled. This patch uses a new "gbnavigable" class to control keyboard navigation, whether editing mode is on or not. It also addresses some browser compatibility and minor behavioural issues.
1294 lines
45 KiB
JavaScript
1294 lines
45 KiB
JavaScript
/**
|
|
* Grader report namespace
|
|
*/
|
|
M.gradereport_grader = {
|
|
/**
|
|
* @namespace M.gradereport_grader
|
|
* @param {Object} reports A collection of classes used by the grader report module
|
|
*/
|
|
classes : {},
|
|
/**
|
|
* Instantiates a new grader report
|
|
*
|
|
* @function
|
|
* @param {YUI} Y
|
|
* @param {Object} cfg A configuration object
|
|
* @param {Array} An array of items in the report
|
|
* @param {Array} An array of users on the report
|
|
* @param {Array} An array of feedback objects
|
|
* @param {Array} An array of student grades
|
|
*/
|
|
init_report : function(Y, cfg, items, users, feedback, grades) {
|
|
// Create the actual report
|
|
new this.classes.report(Y, cfg, items, users, feedback, grades);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initialises the JavaScript for the gradebook grader report
|
|
*
|
|
* The functions fall into 3 groups:
|
|
* M.gradereport_grader.classes.ajax Used when editing is off and fields are dynamically added and removed
|
|
* M.gradereport_grader.classes.existingfield Used when editing is on meaning all fields are already displayed
|
|
* M.gradereport_grader.classes.report Common to both of the above
|
|
*
|
|
* @class report
|
|
* @constructor
|
|
* @this {M.gradereport_grader}
|
|
* @param {YUI} Y
|
|
* @param {Object} cfg Configuration variables
|
|
* @param {Array} items An array containing grade items
|
|
* @param {Array} users An array containing user information
|
|
* @param {Array} feedback An array containing feedback information
|
|
*/
|
|
M.gradereport_grader.classes.report = function(Y, cfg, items, users, feedback, grades) {
|
|
this.Y = Y;
|
|
this.isediting = (cfg.isediting);
|
|
this.ajaxenabled = (cfg.ajaxenabled);
|
|
this.items = items;
|
|
this.users = users;
|
|
this.feedback = feedback;
|
|
this.table = Y.one('#user-grades');
|
|
this.grades = grades;
|
|
|
|
// If ajax is enabled then initialise the ajax component
|
|
if (this.ajaxenabled) {
|
|
this.ajax = new M.gradereport_grader.classes.ajax(this, cfg);
|
|
}
|
|
};
|
|
/**
|
|
* Extend the report class with the following methods and properties
|
|
*/
|
|
M.gradereport_grader.classes.report.prototype.table = null; // YUI Node for the reports main table
|
|
M.gradereport_grader.classes.report.prototype.items = []; // Array containing grade items
|
|
M.gradereport_grader.classes.report.prototype.users = []; // Array containing user information
|
|
M.gradereport_grader.classes.report.prototype.feedback = []; // Array containing feedback items
|
|
M.gradereport_grader.classes.report.prototype.ajaxenabled = false; // True is AJAX is enabled for the report
|
|
M.gradereport_grader.classes.report.prototype.ajax = null; // An instance of the ajax class or null
|
|
/**
|
|
* Builds an object containing information at the relevant cell given either
|
|
* the cell to get information for or an array containing userid and itemid
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader}
|
|
* @param {Y.Node|Array} arg Either a YUI Node instance or an array containing
|
|
* the userid and itemid to reference
|
|
* @return {Object}
|
|
*/
|
|
M.gradereport_grader.classes.report.prototype.get_cell_info = function(arg) {
|
|
|
|
var userid= null;
|
|
var itemid = null;
|
|
var feedback = ''; // Don't default feedback to null or string comparisons become error prone
|
|
var cell = null;
|
|
var i = null;
|
|
|
|
if (arg instanceof this.Y.Node) {
|
|
if (arg.get('nodeName').toUpperCase() !== 'TD') {
|
|
arg = arg.ancestor('td.cell');
|
|
}
|
|
var regexp = /^u(\d+)i(\d+)$/;
|
|
var parts = regexp.exec(arg.getAttribute('id'));
|
|
userid = parts[1];
|
|
itemid = parts[2];
|
|
cell = arg;
|
|
} else {
|
|
userid = arg[0];
|
|
itemid = arg[1];
|
|
cell = this.Y.one('#u'+userid+'i'+itemid);
|
|
}
|
|
|
|
if (!cell) {
|
|
return null;
|
|
}
|
|
|
|
for (i in this.feedback) {
|
|
if (this.feedback[i] && this.feedback[i].user == userid && this.feedback[i].item == itemid) {
|
|
feedback = this.feedback[i].content;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
id : cell.getAttribute('id'),
|
|
userid : userid,
|
|
username : this.users[userid],
|
|
itemid : itemid,
|
|
itemname : this.items[itemid].name,
|
|
itemtype : this.items[itemid].type,
|
|
itemscale : this.items[itemid].scale,
|
|
itemdp : this.items[itemid].decimals,
|
|
feedback : feedback,
|
|
cell : cell
|
|
};
|
|
};
|
|
/**
|
|
* Updates or creates the feedback JS structure for the given user/item
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader}
|
|
* @param {Int} userid
|
|
* @param {Int} itemid
|
|
* @param {String} newfeedback
|
|
* @return {Bool}
|
|
*/
|
|
M.gradereport_grader.classes.report.prototype.update_feedback = function(userid, itemid, newfeedback) {
|
|
for (var i in this.feedback) {
|
|
if (this.feedback[i].user == userid && this.feedback[i].item == itemid) {
|
|
this.feedback[i].content = newfeedback;
|
|
return true;
|
|
}
|
|
}
|
|
this.feedback.push({user:userid,item:itemid,content:newfeedback});
|
|
return true;
|
|
};
|
|
/**
|
|
* Initialises the AJAX component of this report
|
|
* @class ajax
|
|
* @constructor
|
|
* @this {M.gradereport_grader.ajax}
|
|
* @param {M.gradereport_grader.classes.report} report
|
|
* @param {Object} cfg
|
|
*/
|
|
M.gradereport_grader.classes.ajax = function(report, cfg) {
|
|
this.report = report;
|
|
this.courseid = cfg.courseid || null;
|
|
this.feedbacktrunclength = cfg.feedbacktrunclength || null;
|
|
this.studentsperpage = cfg.studentsperpage || null;
|
|
this.showquickfeedback = cfg.showquickfeedback || false;
|
|
this.scales = cfg.scales || null;
|
|
this.existingfields = [];
|
|
|
|
if (!report.isediting) {
|
|
report.table.all('.clickable').on('click', this.make_editable, this);
|
|
} else {
|
|
for (var userid in report.users) {
|
|
if (!this.existingfields[userid]) {
|
|
this.existingfields[userid] = [];
|
|
}
|
|
for (var itemid in report.items) {
|
|
this.existingfields[userid][itemid] = new M.gradereport_grader.classes.existingfield(this, userid, itemid);
|
|
}
|
|
}
|
|
// Disable the Update button as we're saving using ajax.
|
|
submitbutton = this.report.Y.one('#gradersubmit');
|
|
submitbutton.set('disabled', true);
|
|
}
|
|
};
|
|
/**
|
|
* Extend the ajax class with the following methods and properties
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.report = null; // A reference to the report class this object will use
|
|
M.gradereport_grader.classes.ajax.prototype.courseid = null; // The id for the course being viewed
|
|
M.gradereport_grader.classes.ajax.prototype.feedbacktrunclength = null; // The length to truncate feedback to
|
|
M.gradereport_grader.classes.ajax.prototype.studentsperpage = null; // The number of students shown per page
|
|
M.gradereport_grader.classes.ajax.prototype.showquickfeedback = null; // True if feedback editing should be shown
|
|
M.gradereport_grader.classes.ajax.prototype.current = null; // The field being currently editing
|
|
M.gradereport_grader.classes.ajax.prototype.pendingsubmissions = []; // Array containing pending IO transactions
|
|
M.gradereport_grader.classes.ajax.prototype.scales = []; // An array of scales used in this report
|
|
/**
|
|
* Makes a cell editable
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.make_editable = function(e) {
|
|
var node = e;
|
|
if (e.halt) {
|
|
e.halt();
|
|
node = e.target;
|
|
}
|
|
if (node.get('nodeName').toUpperCase() !== 'TD') {
|
|
node = node.ancestor('td');
|
|
}
|
|
this.report.Y.detach('click', this.make_editable, node);
|
|
|
|
if (this.current) {
|
|
// Current is already set!
|
|
this.process_editable_field(node);
|
|
return;
|
|
}
|
|
|
|
// Sort out the field type
|
|
var fieldtype = 'value';
|
|
if (node.hasClass('grade_type_scale')) {
|
|
fieldtype = 'scale';
|
|
} else if (node.hasClass('grade_type_text')) {
|
|
fieldtype = 'text';
|
|
}
|
|
// Create the appropriate field widget
|
|
switch (fieldtype) {
|
|
case 'scale':
|
|
this.current = new M.gradereport_grader.classes.scalefield(this.report, node);
|
|
break;
|
|
case 'text':
|
|
this.current = new M.gradereport_grader.classes.feedbackfield(this.report, node);
|
|
break;
|
|
default:
|
|
this.current = new M.gradereport_grader.classes.textfield(this.report, node);
|
|
break;
|
|
}
|
|
this.current.replace().attach_key_events();
|
|
|
|
// Fire the global resized event for the gradereport_grader to update the table row/column sizes.
|
|
Y.Global.fire('moodle-gradereport_grader:resized');
|
|
};
|
|
/**
|
|
* Callback function for the user pressing the enter key on an editable field
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Event} e
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.keypress_enter = function(e) {
|
|
this.process_editable_field(null);
|
|
};
|
|
/**
|
|
* Callback function for the user pressing Tab or Shift+Tab
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Event} e
|
|
* @param {Bool} ignoreshift If true and shift is pressed then don't exec
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.keypress_tab = function(e, ignoreshift) {
|
|
e.preventDefault();
|
|
var next = null;
|
|
if (e.shiftKey) {
|
|
if (ignoreshift) {
|
|
return;
|
|
}
|
|
next = this.get_above_cell();
|
|
} else {
|
|
next = this.get_below_cell();
|
|
}
|
|
this.process_editable_field(next);
|
|
};
|
|
/**
|
|
* Callback function for the user pressing an CTRL + an arrow key
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.keypress_arrows = function(e) {
|
|
e.preventDefault();
|
|
var next = null;
|
|
switch (e.keyCode) {
|
|
case 37: // Left
|
|
next = this.get_prev_cell();
|
|
break;
|
|
case 38: // Up
|
|
next = this.get_above_cell();
|
|
break;
|
|
case 39: // Right
|
|
next = this.get_next_cell();
|
|
break;
|
|
case 40: // Down
|
|
next = this.get_below_cell();
|
|
break;
|
|
}
|
|
this.process_editable_field(next);
|
|
};
|
|
/**
|
|
* Processes an editable field an does what ever is required to update it
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Y.Node|null} next The next node to make editable (chaining)
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.process_editable_field = function(next) {
|
|
if (this.current.has_changed()) {
|
|
var properties = this.report.get_cell_info(this.current.node);
|
|
var values = this.current.commit();
|
|
this.current.revert();
|
|
this.submit(properties, values);
|
|
} else {
|
|
this.current.revert();
|
|
}
|
|
this.current = null;
|
|
if (next) {
|
|
this.make_editable(next, null);
|
|
}
|
|
|
|
// Fire the global resized event for the gradereport_grader to update the table row/column sizes.
|
|
Y.Global.fire('moodle-gradereport_grader:resized');
|
|
};
|
|
/**
|
|
* Gets the next cell that is editable (right)
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Y.Node} cell
|
|
* @return {Y.Node}
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.get_next_cell = function(cell) {
|
|
var n = cell || this.current.node;
|
|
var next = n.next('td');
|
|
var tr = null;
|
|
if (!next && (tr = n.ancestor('tr').next('tr'))) {
|
|
next = tr.all('.grade').item(0);
|
|
}
|
|
if (!next) {
|
|
return this.current.node;
|
|
}
|
|
// Continue on until we find a navigable cell
|
|
if (!next.hasClass('gbnavigable')) {
|
|
return this.get_next_cell(next);
|
|
}
|
|
return next;
|
|
};
|
|
/**
|
|
* Gets the previous cell that is editable (left)
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Y.Node} cell
|
|
* @return {Y.Node}
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.get_prev_cell = function(cell) {
|
|
var n = cell || this.current.node;
|
|
var next = n.previous('.grade');
|
|
var tr = null;
|
|
if (!next && (tr = n.ancestor('tr').previous('tr'))) {
|
|
var cells = tr.all('.grade');
|
|
next = cells.item(cells.size()-1);
|
|
}
|
|
if (!next) {
|
|
return this.current.node;
|
|
}
|
|
// Continue on until we find a navigable cell
|
|
if (!next.hasClass('gbnavigable')) {
|
|
return this.get_prev_cell(next);
|
|
}
|
|
return next;
|
|
};
|
|
/**
|
|
* Gets the cell above if it is editable (up)
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Y.Node} cell
|
|
* @return {Y.Node}
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.get_above_cell = function(cell) {
|
|
var n = cell || this.current.node;
|
|
var tr = n.ancestor('tr').previous('tr');
|
|
var next = null;
|
|
if (tr) {
|
|
var column = 0;
|
|
var ntemp = n;
|
|
while (ntemp = ntemp.previous('td.cell')) {
|
|
column++;
|
|
}
|
|
next = tr.all('td.cell').item(column);
|
|
}
|
|
if (!next) {
|
|
return this.current.node;
|
|
}
|
|
// Continue on until we find a navigable cell
|
|
if (!next.hasClass('gbnavigable')) {
|
|
return this.get_above_cell(next);
|
|
}
|
|
return next;
|
|
};
|
|
/**
|
|
* Gets the cell below if it is editable (down)
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Y.Node} cell
|
|
* @return {Y.Node}
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.get_below_cell = function(cell) {
|
|
var n = cell || this.current.node;
|
|
var tr = n.ancestor('tr').next('tr');
|
|
var next = null;
|
|
if (tr && !tr.hasClass('avg')) {
|
|
var column = 0;
|
|
var ntemp = n;
|
|
while (ntemp = ntemp.previous('td.cell')) {
|
|
column++;
|
|
}
|
|
next = tr.all('td.cell').item(column);
|
|
}
|
|
if (!next) {
|
|
return this.current.node;
|
|
}
|
|
// Continue on until we find a navigable cell
|
|
if (!next.hasClass('gbnavigable')) {
|
|
return this.get_below_cell(next);
|
|
}
|
|
return next;
|
|
};
|
|
/**
|
|
* Submits changes for update
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Object} properties Properties of the cell being edited
|
|
* @param {Object} values Object containing old + new values
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.submit = function(properties, values) {
|
|
// Stop the IO queue so we can add to it
|
|
this.report.Y.io.queue.stop();
|
|
// If the grade has changed add an IO transaction to update it to the queue
|
|
if (values.grade !== values.oldgrade) {
|
|
this.pendingsubmissions.push({transaction:this.report.Y.io.queue(M.cfg.wwwroot+'/grade/report/grader/ajax_callbacks.php', {
|
|
method : 'POST',
|
|
data : 'id='+this.courseid+'&userid='+properties.userid+'&itemid='+properties.itemid+'&action=update&newvalue='+values.grade+'&type='+properties.itemtype+'&sesskey='+M.cfg.sesskey,
|
|
on : {
|
|
complete : this.submission_outcome
|
|
},
|
|
context : this,
|
|
arguments : {
|
|
properties : properties,
|
|
values : values,
|
|
type : 'grade'
|
|
}
|
|
}),complete:false,outcome:null});
|
|
}
|
|
// If feedback is editable and has changed add to the IO queue for it
|
|
if (values.editablefeedback && values.feedback !== values.oldfeedback) {
|
|
this.pendingsubmissions.push({transaction:this.report.Y.io.queue(M.cfg.wwwroot+'/grade/report/grader/ajax_callbacks.php', {
|
|
method : 'POST',
|
|
data : 'id='+this.courseid+'&userid='+properties.userid+'&itemid='+properties.itemid+'&action=update&newvalue='+values.feedback+'&type=feedback&sesskey='+M.cfg.sesskey,
|
|
on : {
|
|
complete : this.submission_outcome
|
|
},
|
|
context : this,
|
|
arguments : {
|
|
properties : properties,
|
|
values : values,
|
|
type : 'feedback'
|
|
}
|
|
}),complete:false,outcome:null});
|
|
}
|
|
// Process the IO queue
|
|
this.report.Y.io.queue.start();
|
|
};
|
|
/**
|
|
* Callback function for IO transaction completions
|
|
*
|
|
* Uses a synchronous queue to ensure we maintain some sort of order
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {Int} tid Transaction ID
|
|
* @param {Object} outcome
|
|
* @param {Mixed} args
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.submission_outcome = function(tid, outcome, args) {
|
|
// Parse the response as JSON
|
|
try {
|
|
outcome = this.report.Y.JSON.parse(outcome.responseText);
|
|
} catch(e) {
|
|
var message = M.util.get_string('ajaxfailedupdate', 'gradereport_grader');
|
|
message = message.replace(/\[1\]/, args.type);
|
|
message = message.replace(/\[2\]/, this.report.users[args.properties.userid]);
|
|
|
|
this.display_submission_error(message, args.properties.cell);
|
|
return;
|
|
}
|
|
|
|
// Quick reference for the grader report
|
|
var i = null;
|
|
// Check the outcome
|
|
if (outcome.result == 'success') {
|
|
// Iterate through each row in the result object
|
|
for (i in outcome.row) {
|
|
if (outcome.row[i] && outcome.row[i].userid && outcome.row[i].itemid) {
|
|
// alias it, we use it quite a bit
|
|
var r = outcome.row[i];
|
|
// Get the cell referred to by this result object
|
|
var info = this.report.get_cell_info([r.userid, r.itemid]);
|
|
if (!info) {
|
|
continue;
|
|
}
|
|
// Calculate the final grade for the cell
|
|
var finalgrade = '';
|
|
var scalegrade = -1;
|
|
if (!r.finalgrade) {
|
|
if (this.report.isediting) {
|
|
// In edit mode don't put hyphens in the grade text boxes
|
|
finalgrade = '';
|
|
} else {
|
|
// In non-edit mode put a hyphen in the grade cell
|
|
finalgrade = '-';
|
|
}
|
|
} else {
|
|
if (r.scale) {
|
|
scalegrade = parseFloat(r.finalgrade);
|
|
finalgrade = this.scales[r.scale][scalegrade-1];
|
|
} else {
|
|
finalgrade = parseFloat(r.finalgrade).toFixed(info.itemdp);
|
|
}
|
|
}
|
|
if (this.report.isediting) {
|
|
var grade = info.cell.one('#grade_'+r.userid+'_'+r.itemid);
|
|
if (grade) {
|
|
// This means the item has a input element to update.
|
|
var parent = grade.ancestor('td');
|
|
if (parent.hasClass('grade_type_scale')) {
|
|
grade.all('option').each(function(option) {
|
|
if (option.get('value') == scalegrade) {
|
|
option.setAttribute('selected', 'selected');
|
|
} else {
|
|
option.removeAttribute('selected');
|
|
}
|
|
});
|
|
} else {
|
|
grade.set('value', finalgrade);
|
|
}
|
|
} else if (info.cell.one('.gradevalue')) {
|
|
// This means we are updating a value for something without editing boxed (locked, etc).
|
|
info.cell.one('.gradevalue').set('innerHTML', finalgrade);
|
|
}
|
|
} else {
|
|
// If there is no currently editing field or if this cell is not being currently edited
|
|
if (!this.current || info.cell.get('id') != this.current.node.get('id')) {
|
|
// Update the value
|
|
var node = info.cell.one('.gradevalue');
|
|
var td = node.ancestor('td');
|
|
// Only scale and value type grades should have their content updated in this way.
|
|
if (td.hasClass('grade_type_value') || td.hasClass('grade_type_scale')) {
|
|
node.set('innerHTML', finalgrade);
|
|
}
|
|
} else if (this.current && info.cell.get('id') == this.current.node.get('id')) {
|
|
// If we are here the grade value of the cell currently being edited has changed !!!!!!!!!
|
|
// If the user has not actually changed the old value yet we will automatically correct it
|
|
// otherwise we will prompt the user to choose to use their value or the new value!
|
|
if (!this.current.has_changed() || confirm(M.util.get_string('ajaxfieldchanged', 'gradereport_grader'))) {
|
|
this.current.set_grade(finalgrade);
|
|
if (this.current.grade) {
|
|
this.current.grade.set('value', finalgrade);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Flag the changed cell as overridden by ajax
|
|
args.properties.cell.addClass('ajaxoverridden');
|
|
} else {
|
|
var p = args.properties;
|
|
if (args.type == 'grade') {
|
|
var oldgrade = args.values.oldgrade;
|
|
p.cell.one('.gradevalue').set('innerHTML',oldgrade);
|
|
} else if (args.type == 'feedback') {
|
|
this.report.update_feedback(p.userid, p.itemid, args.values.oldfeedback);
|
|
}
|
|
this.display_submission_error(outcome.message, p.cell);
|
|
}
|
|
// Check if all IO transactions in the queue are complete yet
|
|
var allcomplete = true;
|
|
for (i in this.pendingsubmissions) {
|
|
if (this.pendingsubmissions[i]) {
|
|
if (this.pendingsubmissions[i].transaction.id == tid) {
|
|
this.pendingsubmissions[i].complete = true;
|
|
this.pendingsubmissions[i].outcome = outcome;
|
|
this.report.Y.io.queue.remove(this.pendingsubmissions[i].transaction);
|
|
}
|
|
if (!this.pendingsubmissions[i].complete) {
|
|
allcomplete = false;
|
|
}
|
|
}
|
|
}
|
|
if (allcomplete) {
|
|
this.pendingsubmissions = [];
|
|
}
|
|
};
|
|
/**
|
|
* Displays a submission error within a overlay on the cell that failed update
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.ajax}
|
|
* @param {String} message
|
|
* @param {Y.Node} cell
|
|
*/
|
|
M.gradereport_grader.classes.ajax.prototype.display_submission_error = function(message, cell) {
|
|
var erroroverlay = new this.report.Y.Overlay({
|
|
headerContent : '<div><strong class="error">'+M.util.get_string('ajaxerror', 'gradereport_grader')+'</strong> <em>'+M.util.get_string('ajaxclicktoclose', 'gradereport_grader')+'</em></div>',
|
|
bodyContent : message,
|
|
visible : false,
|
|
zIndex : 3
|
|
});
|
|
erroroverlay.set('xy', [cell.getX()+10,cell.getY()+10]);
|
|
erroroverlay.render(this.report.table.ancestor('div'));
|
|
erroroverlay.show();
|
|
erroroverlay.get('boundingBox').on('click', function(){
|
|
this.get('boundingBox').setStyle('visibility', 'hidden');
|
|
this.hide();
|
|
this.destroy();
|
|
}, erroroverlay);
|
|
erroroverlay.get('boundingBox').setStyle('visibility', 'visible');
|
|
};
|
|
/**
|
|
* A class for existing fields
|
|
* This class is used only when the user is in editing mode
|
|
*
|
|
* @class existingfield
|
|
* @constructor
|
|
* @param {M.gradereport_grader.classes.report} report
|
|
* @param {Int} userid
|
|
* @param {Int} itemid
|
|
*/
|
|
M.gradereport_grader.classes.existingfield = function(ajax, userid, itemid) {
|
|
this.report = ajax.report;
|
|
this.userid = userid;
|
|
this.itemid = itemid;
|
|
this.editfeedback = ajax.showquickfeedback;
|
|
this.grade = this.report.Y.one('#grade_'+userid+'_'+itemid);
|
|
|
|
var i = 0;
|
|
if (this.grade) {
|
|
for (i = 0; i < this.report.grades.length; i++) {
|
|
if (this.report.grades[i]['user'] == this.userid && this.report.grades[i]['item'] == this.itemid) {
|
|
this.oldgrade = this.report.grades[i]['grade'];
|
|
}
|
|
}
|
|
|
|
if (!this.oldgrade) {
|
|
// Assigning an empty string makes determining whether the grade has been changed easier
|
|
// This value is never sent to the server
|
|
this.oldgrade = '';
|
|
}
|
|
|
|
// On blur save any changes in the grade field
|
|
this.grade.on('blur', this.submit, this);
|
|
}
|
|
|
|
// Check if feedback is enabled
|
|
if (this.editfeedback) {
|
|
// Get the feedback fields
|
|
this.feedback = this.report.Y.one('#feedback_'+userid+'_'+itemid);
|
|
|
|
if (this.feedback) {
|
|
for(i = 0; i < this.report.feedback.length; i++) {
|
|
if (this.report.feedback[i]['user'] == this.userid && this.report.feedback[i]['item'] == this.itemid) {
|
|
this.oldfeedback = this.report.feedback[i]['content'];
|
|
}
|
|
}
|
|
|
|
if(!this.oldfeedback) {
|
|
// Assigning an empty string makes determining whether the feedback has been changed easier
|
|
// This value is never sent to the server
|
|
this.oldfeedback = '';
|
|
}
|
|
|
|
// On blur save any changes in the feedback field
|
|
this.feedback.on('blur', this.submit, this);
|
|
|
|
// Override the default tab movements when moving between cells
|
|
// Handle Tab.
|
|
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.feedback, 'press:9', this, true));
|
|
// Handle the Enter key being pressed.
|
|
this.keyevents.push(this.report.Y.on('key', this.keypress_enter, this.feedback, 'press:13', this));
|
|
// Handle CTRL + arrow keys.
|
|
this.keyevents.push(this.report.Y.on('key', this.keypress_arrows, this.feedback, 'press:37,38,39,40+ctrl', this));
|
|
|
|
if (this.grade) {
|
|
// Override the default tab movements when moving between cells
|
|
// Handle Shift+Tab.
|
|
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.grade, 'press:9+shift', this));
|
|
|
|
// Override the default tab movements for fields in the same cell
|
|
this.keyevents.push(this.report.Y.on('key',
|
|
function(e){e.preventDefault();this.grade.focus();},
|
|
this.feedback,
|
|
'press:9+shift',
|
|
this));
|
|
this.keyevents.push(this.report.Y.on('key',
|
|
function(e){if (e.shiftKey) {return;}e.preventDefault();this.feedback.focus();},
|
|
this.grade,
|
|
'press:9',
|
|
this));
|
|
}
|
|
}
|
|
} else if (this.grade) {
|
|
// Handle Tab and Shift+Tab.
|
|
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.grade, 'down:9', this));
|
|
}
|
|
if (this.grade) {
|
|
// Handle the Enter key being pressed.
|
|
this.keyevents.push(this.report.Y.on('key', this.keypress_enter, this.grade, 'up:13', this));
|
|
// Handle CTRL + arrow keys.
|
|
this.keyevents.push(this.report.Y.on('key', this.keypress_arrows, this.grade, 'down:37,38,39,40+ctrl', this));
|
|
}
|
|
};
|
|
/**
|
|
* Attach the required properties and methods to the existing field class
|
|
* via prototyping
|
|
*/
|
|
M.gradereport_grader.classes.existingfield.prototype.userid = null;
|
|
M.gradereport_grader.classes.existingfield.prototype.itemid = null;
|
|
M.gradereport_grader.classes.existingfield.prototype.editfeedback = false;
|
|
M.gradereport_grader.classes.existingfield.prototype.grade = null;
|
|
M.gradereport_grader.classes.existingfield.prototype.oldgrade = null;
|
|
M.gradereport_grader.classes.existingfield.prototype.keyevents = [];
|
|
/**
|
|
* Handles saving of changed on keypress
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.existingfield}
|
|
* @param {Event} e
|
|
*/
|
|
M.gradereport_grader.classes.existingfield.prototype.keypress_enter = function(e) {
|
|
e.preventDefault();
|
|
this.submit();
|
|
};
|
|
/**
|
|
* Handles setting the correct focus if the user presses tab
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.existingfield}
|
|
* @param {Event} e
|
|
* @param {Bool} ignoreshift
|
|
*/
|
|
M.gradereport_grader.classes.existingfield.prototype.keypress_tab = function(e, ignoreshift) {
|
|
e.preventDefault();
|
|
var next = null;
|
|
if (e.shiftKey) {
|
|
if (ignoreshift) {
|
|
return;
|
|
}
|
|
next = this.report.ajax.get_above_cell(this.grade.ancestor('td'));
|
|
} else {
|
|
next = this.report.ajax.get_below_cell(this.grade.ancestor('td'));
|
|
}
|
|
this.move_focus(next);
|
|
};
|
|
/**
|
|
* Handles setting the correct focus when the user presses CTRL+arrow keys
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.existingfield}
|
|
* @param {Event} e
|
|
*/
|
|
M.gradereport_grader.classes.existingfield.prototype.keypress_arrows = function(e) {
|
|
e.preventDefault();
|
|
var next = null;
|
|
switch (e.keyCode) {
|
|
case 37: // Left
|
|
next = this.report.ajax.get_prev_cell(this.grade.ancestor('td'));
|
|
break;
|
|
case 38: // Up
|
|
next = this.report.ajax.get_above_cell(this.grade.ancestor('td'));
|
|
break;
|
|
case 39: // Right
|
|
next = this.report.ajax.get_next_cell(this.grade.ancestor('td'));
|
|
break;
|
|
case 40: // Down
|
|
next = this.report.ajax.get_below_cell(this.grade.ancestor('td'));
|
|
break;
|
|
}
|
|
this.move_focus(next);
|
|
};
|
|
/**
|
|
* Move the focus to the node
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.existingfield}
|
|
* @param {Y.Node} node
|
|
*/
|
|
M.gradereport_grader.classes.existingfield.prototype.move_focus = function(node) {
|
|
if (node) {
|
|
var properties = this.report.get_cell_info(node);
|
|
this.report.ajax.current = node;
|
|
switch(properties.itemtype) {
|
|
case 'scale':
|
|
properties.cell.one('select.select').focus();
|
|
break;
|
|
case 'value':
|
|
default:
|
|
properties.cell.one('input.text').focus();
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Checks if the values for the field have changed
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.existingfield}
|
|
* @return {Bool}
|
|
*/
|
|
M.gradereport_grader.classes.existingfield.prototype.has_changed = function() {
|
|
if (this.grade) {
|
|
if (this.grade.get('value') !== this.oldgrade) {
|
|
return true;
|
|
}
|
|
}
|
|
if (this.editfeedback && this.feedback) {
|
|
if (this.feedback.get('value') !== this.oldfeedback) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
/**
|
|
* Submits any changes and then updates the fields accordingly
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.existingfield}
|
|
*/
|
|
M.gradereport_grader.classes.existingfield.prototype.submit = function() {
|
|
if (!this.has_changed()) {
|
|
return;
|
|
}
|
|
|
|
var properties = this.report.get_cell_info([this.userid,this.itemid]);
|
|
var values = (function(f){
|
|
var feedback, oldfeedback, grade, oldgrade = null;
|
|
if (f.editfeedback && f.feedback) {
|
|
feedback = f.feedback.get('value');
|
|
oldfeedback = f.oldfeedback;
|
|
}
|
|
if (f.grade) {
|
|
grade = f.grade.get('value');
|
|
oldgrade = f.oldgrade;
|
|
}
|
|
return {
|
|
editablefeedback : f.editfeedback,
|
|
grade : grade,
|
|
oldgrade : oldgrade,
|
|
feedback : feedback,
|
|
oldfeedback : oldfeedback
|
|
};
|
|
})(this);
|
|
|
|
this.oldgrade = values.grade;
|
|
if (values.editablefeedback && values.feedback != values.oldfeedback) {
|
|
this.report.update_feedback(this.userid, this.itemid, values.feedback);
|
|
this.oldfeedback = values.feedback;
|
|
}
|
|
|
|
this.report.ajax.submit(properties, values);
|
|
};
|
|
|
|
/**
|
|
* Textfield class
|
|
* This classes gets used in conjunction with the report running with AJAX enabled
|
|
* and is used to manage a cell that has a grade requiring a textfield for input
|
|
*
|
|
* @class textfield
|
|
* @constructor
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
* @param {M.gradereport_grader.classes.report} report
|
|
* @param {Y.Node} node
|
|
*/
|
|
M.gradereport_grader.classes.textfield = function(report, node) {
|
|
this.report = report;
|
|
this.node = node;
|
|
this.gradespan = node.one('.gradevalue');
|
|
this.inputdiv = this.report.Y.Node.create('<div></div>');
|
|
this.editfeedback = this.report.ajax.showquickfeedback;
|
|
this.grade = this.report.Y.Node.create('<input type="text" class="text" value="" name="ajaxgrade" />');
|
|
this.gradetype = 'value';
|
|
this.inputdiv.append(this.grade);
|
|
if (this.report.ajax.showquickfeedback) {
|
|
this.feedback = this.report.Y.Node.create('<input type="text" class="quickfeedback" value="" name="ajaxfeedback" />');
|
|
this.inputdiv.append(this.feedback);
|
|
}
|
|
};
|
|
/**
|
|
* Extend the textfield class with the following methods and properties
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.keyevents = [];
|
|
M.gradereport_grader.classes.textfield.prototype.editable = false;
|
|
M.gradereport_grader.classes.textfield.prototype.gradetype = null;
|
|
M.gradereport_grader.classes.textfield.prototype.grade = null;
|
|
M.gradereport_grader.classes.textfield.prototype.report = null;
|
|
M.gradereport_grader.classes.textfield.prototype.node = null;
|
|
M.gradereport_grader.classes.textfield.prototype.gradespam = null;
|
|
M.gradereport_grader.classes.textfield.prototype.inputdiv = null;
|
|
M.gradereport_grader.classes.textfield.prototype.editfeedback = false;
|
|
/**
|
|
* Replaces the cell contents with the controls to enable editing
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
* @return {M.gradereport_grader.classes.textfield}
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.replace = function() {
|
|
this.set_grade(this.get_grade());
|
|
if (this.editfeedback) {
|
|
this.set_feedback(this.get_feedback());
|
|
}
|
|
this.node.replaceChild(this.inputdiv, this.gradespan);
|
|
if (this.grade) {
|
|
this.grade.focus();
|
|
} else if (this.feedback) {
|
|
this.feedback.focus();
|
|
}
|
|
this.editable = true;
|
|
return this;
|
|
};
|
|
/**
|
|
* Commits the changes within a cell and returns a result object of new + old values
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
* @return {Object}
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.commit = function() {
|
|
// Produce an anonymous result object contianing all values
|
|
var result = (function(field){
|
|
// Editable false lets us get the pre-update values.
|
|
field.editable = false;
|
|
var oldgrade = field.get_grade();
|
|
if (oldgrade == '-') {
|
|
oldgrade = '';
|
|
}
|
|
var feedback = null;
|
|
var oldfeedback = null;
|
|
if (field.editfeedback) {
|
|
oldfeedback = field.get_feedback();
|
|
}
|
|
|
|
// Now back to editable gives us the values in the edit areas.
|
|
field.editable = true;
|
|
if (field.editfeedback) {
|
|
feedback = field.get_feedback();
|
|
}
|
|
return {
|
|
gradetype : field.gradetype,
|
|
editablefeedback : field.editfeedback,
|
|
grade : field.get_grade(),
|
|
oldgrade : oldgrade,
|
|
feedback : feedback,
|
|
oldfeedback : oldfeedback
|
|
};
|
|
})(this);
|
|
// Set the changes in stone
|
|
this.set_grade(result.grade);
|
|
if (this.editfeedback) {
|
|
this.set_feedback(result.feedback);
|
|
}
|
|
// Return the result object
|
|
return result;
|
|
};
|
|
/**
|
|
* Reverts a cell back to its static contents
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.revert = function() {
|
|
this.node.replaceChild(this.gradespan, this.inputdiv);
|
|
for (var i in this.keyevents) {
|
|
if (this.keyevents[i]) {
|
|
this.keyevents[i].detach();
|
|
}
|
|
}
|
|
this.keyevents = [];
|
|
this.node.on('click', this.report.ajax.make_editable, this.report.ajax);
|
|
};
|
|
/**
|
|
* Gets the grade for current cell
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
* @return {Mixed}
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.get_grade = function() {
|
|
if (this.editable) {
|
|
return this.grade.get('value');
|
|
}
|
|
return this.gradespan.get('innerHTML');
|
|
};
|
|
/**
|
|
* Sets the grade for the current cell
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
* @param {Mixed} value
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.set_grade = function(value) {
|
|
if (!this.editable) {
|
|
if (value == '-') {
|
|
value = '';
|
|
}
|
|
this.grade.set('value', value);
|
|
} else {
|
|
if (value == '') {
|
|
value = '-';
|
|
}
|
|
this.gradespan.set('innerHTML', value);
|
|
}
|
|
};
|
|
/**
|
|
* Gets the feedback for the current cell
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
* @return {String}
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.get_feedback = function() {
|
|
if (this.editable) {
|
|
if (this.feedback) {
|
|
return this.feedback.get('value');
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
var properties = this.report.get_cell_info(this.node);
|
|
if (properties) {
|
|
return properties.feedback;
|
|
}
|
|
return '';
|
|
};
|
|
/**
|
|
* Sets the feedback for the current cell
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
* @param {Mixed} value
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.set_feedback = function(value) {
|
|
if (!this.editable) {
|
|
if (this.feedback) {
|
|
this.feedback.set('value', value);
|
|
}
|
|
} else {
|
|
var properties = this.report.get_cell_info(this.node);
|
|
this.report.update_feedback(properties.userid, properties.itemid, value);
|
|
}
|
|
};
|
|
/**
|
|
* Checks if the current cell has changed at all
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
* @return {Bool}
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.has_changed = function() {
|
|
// If its not editable it has not changed
|
|
if (!this.editable) {
|
|
return false;
|
|
}
|
|
// If feedback is being edited then it has changed if either grade or feedback have changed
|
|
if (this.editfeedback) {
|
|
var properties = this.report.get_cell_info(this.node);
|
|
if (this.get_feedback() != properties.feedback) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (this.grade) {
|
|
return (this.get_grade() != this.gradespan.get('innerHTML'));
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
/**
|
|
* Attaches the key listeners for the editable fields and stored the event references
|
|
* against the textfield
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.textfield}
|
|
*/
|
|
M.gradereport_grader.classes.textfield.prototype.attach_key_events = function() {
|
|
var a = this.report.ajax;
|
|
// Setup the default key events for tab and enter
|
|
if (this.editfeedback) {
|
|
if (this.grade) {
|
|
// Handle Shift+Tab.
|
|
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.grade, 'down:9+shift', a));
|
|
}
|
|
// Handle Tab.
|
|
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.feedback, 'down:9', a, true));
|
|
// Handle the Enter key being pressed.
|
|
this.keyevents.push(this.report.Y.on('key', a.keypress_enter, this.feedback, 'up:13', a));
|
|
} else {
|
|
if (this.grade) {
|
|
// Handle Tab and Shift+Tab.
|
|
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.grade, 'down:9', a));
|
|
}
|
|
}
|
|
|
|
// Setup the arrow key events.
|
|
// Handle CTRL + arrow keys.
|
|
this.keyevents.push(this.report.Y.on('key', a.keypress_arrows, this.inputdiv.ancestor('td'), 'down:37,38,39,40+ctrl', a));
|
|
|
|
if (this.grade) {
|
|
// Handle the Enter key being pressed.
|
|
this.keyevents.push(this.report.Y.on('key', a.keypress_enter, this.grade, 'up:13', a));
|
|
// Prevent the default key action on all fields for arrow keys on all key events!
|
|
// Note: this still does not work in FF!!!!!
|
|
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'down:37,38,39,40+ctrl'));
|
|
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'press:37,38,39,40+ctrl'));
|
|
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'up:37,38,39,40+ctrl'));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Feedback field class
|
|
* This classes gets used in conjunction with the report running with AJAX enabled
|
|
* and is used to manage a cell that no editable grade, only possibly feedback
|
|
*
|
|
* @class feedbackfield
|
|
* @constructor
|
|
* @this {M.gradereport_grader.classes.feedbackfield}
|
|
* @param {M.gradereport_grader.classes.report} report
|
|
* @param {Y.Node} node
|
|
*/
|
|
M.gradereport_grader.classes.feedbackfield = function(report, node) {
|
|
this.report = report;
|
|
this.node = node;
|
|
this.gradespan = node.one('.gradevalue');
|
|
this.inputdiv = this.report.Y.Node.create('<div></div>');
|
|
this.editfeedback = this.report.ajax.showquickfeedback;
|
|
this.gradetype = 'text';
|
|
if (this.report.ajax.showquickfeedback) {
|
|
this.feedback = this.report.Y.Node.create('<input type="text" class="quickfeedback" value="" name="ajaxfeedback" />');
|
|
this.inputdiv.append(this.feedback);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Gets the grade for current cell (which will always be null)
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.feedbackfield}
|
|
* @return {Mixed}
|
|
*/
|
|
M.gradereport_grader.classes.feedbackfield.prototype.get_grade = function() {
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Overrides the set_grade function of textfield so that it can ignore the set-grade
|
|
* for grade cells without grades
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.feedbackfield}
|
|
* @param {String} value
|
|
*/
|
|
M.gradereport_grader.classes.feedbackfield.prototype.set_grade = function() {
|
|
return;
|
|
};
|
|
|
|
/**
|
|
* Manually extend the feedbackfield class with the properties and methods of the
|
|
* textfield class that have not been defined
|
|
*/
|
|
for (var i in M.gradereport_grader.classes.textfield.prototype) {
|
|
if (!M.gradereport_grader.classes.feedbackfield.prototype[i]) {
|
|
M.gradereport_grader.classes.feedbackfield.prototype[i] = M.gradereport_grader.classes.textfield.prototype[i];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An editable scale field
|
|
*
|
|
* @class scalefield
|
|
* @constructor
|
|
* @inherits M.gradereport_grader.classes.textfield
|
|
* @base M.gradereport_grader.classes.textfield
|
|
* @this {M.gradereport_grader.classes.scalefield}
|
|
* @param {M.gradereport_grader.classes.report} report
|
|
* @param {Y.Node} node
|
|
*/
|
|
M.gradereport_grader.classes.scalefield = function(report, node) {
|
|
this.report = report;
|
|
this.node = node;
|
|
this.gradespan = node.one('.gradevalue');
|
|
this.inputdiv = this.report.Y.Node.create('<div></div>');
|
|
this.editfeedback = this.report.ajax.showquickfeedback;
|
|
this.grade = this.report.Y.Node.create('<select type="text" class="text" name="ajaxgrade" /><option value="-1">'+
|
|
M.util.get_string('ajaxchoosescale', 'gradereport_grader')+'</option></select>');
|
|
this.gradetype = 'scale';
|
|
this.inputdiv.append(this.grade);
|
|
if (this.editfeedback) {
|
|
this.feedback = this.report.Y.Node.create('<input type="text" class="quickfeedback" value="" name="ajaxfeedback"/>');
|
|
this.inputdiv.append(this.feedback);
|
|
}
|
|
var properties = this.report.get_cell_info(node);
|
|
this.scale = this.report.ajax.scales[properties.itemscale];
|
|
for (var i in this.scale) {
|
|
if (this.scale[i]) {
|
|
this.grade.append(this.report.Y.Node.create('<option value="'+(parseFloat(i)+1)+'">'+this.scale[i]+'</option>'));
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Override + extend the scalefield class with the following properties
|
|
* and methods
|
|
*/
|
|
/**
|
|
* @property {Array} scale
|
|
*/
|
|
M.gradereport_grader.classes.scalefield.prototype.scale = [];
|
|
/**
|
|
* Extend the scalefield with the functions from the textfield
|
|
*/
|
|
/**
|
|
* Overrides the get_grade function so that it can pick up the value from the
|
|
* scales select box
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.scalefield}
|
|
* @return {Int} the scale id
|
|
*/
|
|
M.gradereport_grader.classes.scalefield.prototype.get_grade = function(){
|
|
if (this.editable) {
|
|
// Return the scale value
|
|
return this.grade.all('option').item(this.grade.get('selectedIndex')).get('value');
|
|
} else {
|
|
// Return the scale values id
|
|
var value = this.gradespan.get('innerHTML');
|
|
for (var i in this.scale) {
|
|
if (this.scale[i] == value) {
|
|
return parseFloat(i)+1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
};
|
|
/**
|
|
* Overrides the set_grade function of textfield so that it can set the scale
|
|
* within the scale select box
|
|
*
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.scalefield}
|
|
* @param {String} value
|
|
*/
|
|
M.gradereport_grader.classes.scalefield.prototype.set_grade = function(value) {
|
|
if (!this.editable) {
|
|
if (value == '-') {
|
|
value = '-1';
|
|
}
|
|
this.grade.all('option').each(function(node){
|
|
if (node.get('value') == value) {
|
|
node.set('selected', true);
|
|
}
|
|
});
|
|
} else {
|
|
if (value == '' || value == '-1') {
|
|
value = '-';
|
|
} else {
|
|
value = this.scale[parseFloat(value)-1];
|
|
}
|
|
this.gradespan.set('innerHTML', value);
|
|
}
|
|
};
|
|
/**
|
|
* Checks if the current cell has changed at all
|
|
* @function
|
|
* @this {M.gradereport_grader.classes.scalefield}
|
|
* @return {Bool}
|
|
*/
|
|
M.gradereport_grader.classes.scalefield.prototype.has_changed = function() {
|
|
if (!this.editable) {
|
|
return false;
|
|
}
|
|
var gradef = this.get_grade();
|
|
this.editable = false;
|
|
var gradec = this.get_grade();
|
|
this.editable = true;
|
|
if (this.editfeedback) {
|
|
var properties = this.report.get_cell_info(this.node);
|
|
var feedback = properties.feedback;
|
|
return (gradef != gradec || this.get_feedback() != feedback);
|
|
}
|
|
return (gradef != gradec);
|
|
};
|
|
|
|
/**
|
|
* Manually extend the scalefield class with the properties and methods of the
|
|
* textfield class that have not been defined
|
|
*/
|
|
for (var i in M.gradereport_grader.classes.textfield.prototype) {
|
|
if (!M.gradereport_grader.classes.scalefield.prototype[i]) {
|
|
M.gradereport_grader.classes.scalefield.prototype[i] = M.gradereport_grader.classes.textfield.prototype[i];
|
|
}
|
|
}
|