# This patch file was generated by NetBeans IDE
# This patch can be applied using context Tools: Apply Diff Patch action on respective folder.
# It uses platform neutral UTF-8 encoding.
# Above lines and this line are ignored by the patching process.
Index: moodle/grade/report/grader/ajax.php
--- moodle/grade/report/grader/ajax.php Base (1.2)
+++ moodle/grade/report/grader/ajax.php Locally Deleted
@@ -1,582 +0,0 @@
-<script type="text/javascript">
-//<![CDATA[
-
-// If the mouse is clicked outside this element, the edit is CANCELLED (even if the mouse clicks another grade/feedback cell)
-// If ctrl-arrow is used, or if [tab] or [enter] are pressed, the edit is RECORDED and the row is updated. The previous element returns to normal
-
-YAHOO.namespace("grader_report");
-YAHOO.grader_report.el_being_edited = null;
-YAHOO.grader_report.courseid = <?php echo $COURSE->id; ?>;
-YAHOO.grader_report.wwwroot = '<?php echo $CFG->wwwroot; ?>';
-YAHOO.grader_report.straddfeedback = '<?php echo get_string("addfeedback", "grades"); ?>';
-YAHOO.grader_report.strfeedback = '<?php echo get_string("feedback", "grades"); ?>';
-YAHOO.grader_report.feedback_trunc_length = <?php echo $report->feedback_trunc_length ?>;
-YAHOO.grader_report.decimalpoints = <?php echo $report->getItemsDecimalPoints() ?>;
-YAHOO.grader_report.studentsperpage = <?php echo $report->get_pref('studentsperpage'); ?>;
-YAHOO.grader_report.showquickfeedback = <?php echo $report->get_pref('showquickfeedback'); ?>;
-
-// Feedback data is cached in a JS array: we don't want to show complete feedback strings in the report, but
-// neither do we want to fetch the feedback from PHP each time we click one to edit it
-YAHOO.grader_report.feedbacks = <?php echo $report->getFeedbackJsArray(); ?>
-
-
-/**
- * Given an elementId formatted as grade[cell|value|feedback]_u[$userId]-i[$itemId], returns an object with key-value pairs
- * @param string elId
- * @return object
- */
-YAHOO.grader_report.getIdData = function(elId) {
-    var re = /grade(value|feedback|cell|scale)_([0-9]*)-i([0-9]*)/;
-    var matches = re.exec(elId);
-    if (undefined != matches && matches.length > 0) {
-        return {type: matches[1], userId: matches[2], itemId: matches[3]};
-    } else {
-        YAHOO.log("getIdData: Invalid elementId: " + elId, "warn");
-        return false;
-    }
-};
-
-/**
- * Reverse-engineering of getIdData: returns a string based on an object
- * @param object idData
- * @return string
- */
-YAHOO.grader_report.getElementId = function(idData) {
-    if (undefined != idData.userId && undefined != idData.type && undefined != idData.itemId) {
-        return "grade" + idData.type + "_" + idData.userId + "-i" + idData.itemId;
-    } else {
-        YAHOO.log("getElementId: Invalid elementId: " + idData, "warn");
-        return false;
-    }
-};
-
-/**
- * Interface to the overlib js library. DEPENDENCY ALERT!
- */
-YAHOO.grader_report.tooltip = function(e, dataObj) {
-    var gr = YAHOO.grader_report;
-    if (undefined != dataObj.text) {
-        return overlib(dataObj.text, BORDER, 0, FGCLASS, 'feedback', CAPTIONFONTCLASS, 'caption', CAPTION, gr.strfeedback);
-    } else {
-        return null;
-    }
-};
-
-/**
- * Sends the record request and un-edits the element
- * @param object editedEl The DOM element being edited, whose value we are trying to save in DB
- * @return array An array of values used to update the row
- */
-YAHOO.grader_report.saveField = function(editedEl) {
-    var gr = YAHOO.grader_report;
-    var idData = gr.getIdData(editedEl.id);
-
-    if (idData.type == 'value') { // Text input
-        var newVal = editedEl.firstChild.value;
-    } else if (idData.type == 'feedback') { // Textarea
-        var newVal = editedEl.firstChild.innerHTML;
-    } else if (idData.type == 'scale') { // Select
-        var newVal = editedEl.options[editedEl.selectedIndex].value;
-    }
-
-    // Don't save if the new value is the same as the old
-    if (gr.el_being_edited.value == newVal || (gr.el_being_edited.value == gr.straddfeedback && newVal == '')) {
-        YAHOO.log("saveField: Field unchanged, not saving. (" + newVal + ")", "info");
-        return false;
-    }
-
-    YAHOO.log("saveField: Old value: " + gr.el_being_edited.value + ", new value: " + newVal + ". Saving field...", "info");
-
-    var postData = "id=" + gr.courseid + "&userid=" + idData.userId + "&itemid=" + idData.itemId +
-                   "&action=update&newvalue=" + newVal + "&type=" + idData.type;
-
-    var handleSuccess = function(o) {
-        try {
-            var queryResult = YAHOO.lang.JSON.parse(o.responseText);
-        } catch (e) {
-            YAHOO.log("saveField: JSON syntax error! " + o.responseText, "error");
-        }
-
-        if (queryResult.result == "success") {
-            // For a textarea, truncate the feedback to 40 chars and provide a tooltip with the full text
-            if (queryResult.gradevalue == null) {
-                if (idData.type == 'scale') {
-                    editedEl.selectedIndex = 0;
-                } else {
-                    editedEl.innerHTML = '';
-                }
-            } else if (idData.type == 'feedback') {
-                editedEl.innerHTML = gr.truncateText(queryResult.gradevalue);
-
-                YAHOO.util.Event.addListener(editedEl.id, 'mouseover', gr.tooltip, {text: queryResult.gradevalue}, true);
-                YAHOO.util.Event.addListener(editedEl.id, 'mouseout', nd); // See overlib doc for reference
-                gr.feedbacks[idData.userId][idData.itemId] = queryResult.gradevalue;
-            } else if (idData.type == 'value') {
-                editedEl.innerHTML = gr.roundValue(queryResult.gradevalue, idData.itemId);
-            }
-
-            YAHOO.util.Dom.addClass(editedEl, "editable");
-
-            // TODO "highlight" the updated element using animation of color (yellow fade-out)
-
-            // Update the row's final grade values
-            gr.updateRow(gr.getIdData(editedEl.id).userId, queryResult.row);
-        } else {
-
-        }
-
-        // Display message
-        gr.displayMessage(queryResult.result, queryResult.message, editedEl);
-    }
-
-    var handleFailure = function(o) {
-        YAHOO.log("saveField: Failure to call the ajax callbacks page!", "error");
-    }
-
-    var uri = gr.wwwroot + '/grade/report/grader/ajax_callbacks.php';
-    var callback = {success: handleSuccess, failure: handleFailure};
-    var conn = YAHOO.util.Connect.asyncRequest("post", uri, callback, postData);
-}; // End of saveField function
-
-/**
- * Displays a message in the message bar above the report, Google-style
- * @param string result "success", "notice" or "error"
- * @param string message
- * @param object An element to highlight
- */
-YAHOO.grader_report.displayMessage = function(result, message, elToHighlight) {
-    var messageDiv = document.getElementById('grader_report_message');
-    // Remove previous message
-    // TODO log messages in DB?
-    messageDiv.innerHTML = '';
-
-    if (message.length < 1 || !message) {
-        return false;
-    }
-
-    // Remove all state classes first
-    YAHOO.util.Dom.removeClass(messageDiv, 'success');
-    YAHOO.util.Dom.removeClass(messageDiv, 'error');
-    YAHOO.util.Dom.removeClass(messageDiv, 'notice');
-
-    var attributes = {backgroundColor: { to: '#00F0F0'} };
-
-    // Add result class
-    YAHOO.util.Dom.addClass(messageDiv, result);
-
-    messageDiv.innerHTML = message;
-
-    // Highlight given element
-    if (result == 'error' && elToHighlight != null) {
-        YAHOO.util.Dom.addClass(elToHighlight, 'error');
-    }
-};
-
-/**
- * Given a userId and an array of string values, updates the innerHTML of each grade cell in the row
- * @param string userId Identifies the correct row
- * @param array An array of values
- *
- */
-YAHOO.grader_report.updateRow = function(userId, row) {
-    var gr = YAHOO.grader_report;
-
-    // Send update request and update the row
-    YAHOO.log("updateRow: Updating row..." + row, "info");
-
-    for (var i in row) {
-        if (row[i].finalgrade != null) {
-            // Build id string
-            var gradevalue = gr.roundValue(row[i].finalgrade, row[i].itemid);
-
-            if (row[i].scale) {
-                var idString = "gradescale_";
-            } else {
-                var idString = "gradevalue_";
-            }
-
-            idString += row[i].userid + "-i" + row[i].itemid;
-
-            var elementToUpdate = document.getElementById(idString);
-
-            if (undefined == elementToUpdate) {
-                YAHOO.log("updateRow: Element with id " + idString + " does not exist!", "error");
-            } else {
-                if (row[i].scale) {
-                    elementToUpdate.selectedIndex = gradevalue;
-                } else {
-                    elementToUpdate.innerHTML = gradevalue;
-                }
-
-                // Add overridden class where it applies, and remove it where it does not
-                // TODO fix the code below, it doesn't currently work. See ajax_callbacks.php for the code building the JSON data
-                if (row[i].overridden > 0) {
-                    YAHOO.util.Dom.addClass(elementToUpdate.parentNode, "overridden");
-                } else if (YAHOO.util.Dom.hasClass(elementToUpdate.parentNode, "overridden")) {
-                    YAHOO.util.Dom.removeClass(elementToUpdate.parentNode, "overridden");
-                }
-
-                YAHOO.log("updateRow: Updated finalgrade (" + gradevalue + ") of user " + row[i].userid + " for grade item " + row[i].itemid, "info");
-            }
-        }
-    }
-};
-
-/**
- * Given a gradevalue or gradefeedback <a> element,
- * @param object element A DOM element
- */
-YAHOO.grader_report.openField = function(element) {
-    YAHOO.log("openField: Moving to next item: " + element.id, "info");
-    var gr = YAHOO.grader_report;
-    var idData = gr.getIdData(element.id);
-    element.inputId = element.id + "_input";
-
-    // If field is in error, empty it before editing
-    if (YAHOO.util.Dom.hasClass(element, 'error')) {
-        if (idData.type == 'feedback') {
-            element.innerHTML = '';
-        } else if (idData.type == 'value') {
-            element.value = '';
-        }
-
-        YAHOO.util.Dom.removeClass(element, 'error');
-    }
-
-    // Show a textarea for feedback, input for grade and leave scale as it is
-    if (undefined == idData.type) {
-        YAHOO.log("openField: Could not get info from elementId: " + element.id, "warn");
-    } else if (idData.type == 'feedback') {
-        var original = gr.feedbacks[idData.userId][idData.itemId].toString();
-        var tabIndex = element.tabIndex.toString();
-        var displayValue = null;
-
-        // If empty feedback, show empty textarea
-        if (original == gr.straddfeedback) {
-            displayValue = '';
-        } else {
-            displayValue = original;
-        }
-
-        element.innerHTML = '<textarea id="' + element.inputId + '" name="' + idData.type + '">' + displayValue + '</textarea>';
-        setTimeout(function() {element.firstChild.focus(); }, 0);
-    } else if (idData.type == 'value') {
-        var original = element.innerHTML.toString(); // Removes reference to original
-        element.innerHTML = '<input onfocus="this.select()" id="' + element.inputId + '" type="text" name="' +
-                            idData.type + '" value="' + gr.roundValue(original, idData.itemId) + '" />';
-        setTimeout(function() {element.firstChild.focus(); }, 0);
-    } else if (idData.type == 'scale') {
-        var original = element.options[element.selectedIndex].value;
-        setTimeout(function() {element.focus(); }, 0);
-    }
-
-    YAHOO.util.Dom.removeClass(element, "editable");
-
-    // Save the element and its original value
-    gr.el_being_edited = {elementId: element.id, value: original, tabIndex: tabIndex};
-    YAHOO.log("openField: el_being_edited saved as: " + gr.el_being_edited.value, "info");
-}
-
-/**
- * Replaces the input, textarea or select inside a gradecell with the value currently held in that
- * input, textarea or select. If the second argument (cancel) is true, replaces the innerHTML with
- * the value held in YAHOO.grader_report.el_being_edited
- *
- * @param object  element DOM element being closed
- * @param boolean forcecancel If true, current value held in input element is dropped in favour of original value in memory
- */
-YAHOO.grader_report.closeField = function(element, forcecancel) {
-    YAHOO.log("closeField: Closing field: " + element.id, "info");
-
-    var gr = YAHOO.grader_report;
-    var idData = gr.getIdData(element.id);
-
-    if (idData.type == 'feedback') {
-        var newValue = element.firstChild.value;
-        var originalValue = gr.feedbacks[idData.userId][idData.itemId];
-
-        if (!forcecancel && originalValue == newValue) {
-            YAHOO.log("closeField: originalValue == newvalue, forcing a cancel", "info");
-            forcecancel = true;
-            originalValue = gr.truncateText(originalValue);
-        }
-    } else if (idData.type == 'value') {
-        var originalValue = gr.el_being_edited.value;
-        var newValue = element.firstChild.value;
-    }
-
-    YAHOO.util.Dom.addClass(element, "editable");
-
-    // No need to change HTML for select element
-    if (idData.type == 'scale') {
-        gr.el_being_edited = null;
-        return;
-    }
-
-    if (forcecancel || ((newValue == '' && idData.type == 'feedback') && idData.type != 'scale')) {
-        // Replace the input by the original value as plain text
-        element.innerHTML = gr.truncateText(originalValue);
-        YAHOO.log("closeField: Cancelling : Replacing field by original value: " + originalValue, "info");
-    } else {
-        // For numeric grades, round off to item decimal points
-        if (idData.type == 'value') {
-            element.innerHTML = gr.roundValue(newValue, idData.itemId);
-        } else if (idData.type == 'feedback') {
-            element.innerHTML = newValue;
-        }
-    }
-
-    // Erase the element in memory
-    gr.el_being_edited = null;
-};
-
-/**
- * Given a string, truncates it if over the limit defined in the report's preferences, adding an ellipsis at the end.
- * TODO improve the regex so that it doesn't truncate halfway through a word
- * @param string text
- * @return string
- */
-YAHOO.grader_report.truncateText = function(text) {
-    var gr = YAHOO.grader_report;
-    var returnString = '';
-
-    returnString = text.substring(0, gr.feedback_trunc_length);
-
-    // Add ... if the string was truncated
-    if (returnString.length < text.length) {
-        returnString += '...';
-    }
-
-    return returnString;
-}
-
-/**
- * Given a float value and an itemId, uses that grade_item's decimalpoints preference to round off the value and return it.
- * @param float value
- * @param int itemId
- * @return string
- */
-YAHOO.grader_report.roundValue = function(value, itemId) {
-    return value;
-
-    // I am ignoring the rest for now, because I am not sure we should round values while in editing mode
-    if (value.length == 0) {
-        return '';
-    }
-
-    var gr = YAHOO.grader_report;
-    var decimalpoints = gr.decimalpoints[itemId];
-    var gradevalue = Math.round(Number(value) * Math.pow(10, decimalpoints)) / Math.pow(10, decimalpoints);
-
-    // If the value is an integer, add appropriate zeros after the decimal point
-    if (gradevalue % 1 == 0 && decimalpoints > 0) {
-        gradevalue += '.';
-        for (var i = 0; i < decimalpoints; i++) {
-            gradevalue += '0';
-        }
-    }
-
-    return gradevalue;
-};
-
-/**
- * Given an element of origin, returns the field to be edited in the given direction.
- * @param object origin_element
- * @param string direction previous|next|left|right|up|down
- * @return object element
- */
-YAHOO.grader_report.getField = function(origin_element, direction) {
-    var gr = YAHOO.grader_report;
-    // get all 'editable' elements
-    var haystack = YAHOO.util.Dom.getElementsByClassName('editable');
-    var wrapElement = null;
-
-    var feedbackModifier = 1;
-    var upDownOffset = 1;
-    var idData = gr.getIdData(origin_element.id);
-
-    if (gr.showquickfeedback) {
-        feedbackModifier = 2;
-
-        if (idData.type == 'value' || idData.type == 'scale') {
-            upDownOffset = gr.studentsperpage;
-        }
-
-    }
-
-    if (direction == 'next') {
-        var wrapValue = 1;
-        var needle = origin_element.tabIndex + 1;
-
-    } else if (direction == 'previous') {
-        var wrapValue = haystack.length;
-        var needle = origin_element.tabIndex - 1;
-
-    } else if (direction == 'right') {
-        var needle = origin_element.tabIndex + gr.studentsperpage * feedbackModifier;
-        var wrapValue = null; // TODO implement wrapping when moving right
-
-    } else if (direction == 'left') {
-        var needle = origin_element.tabIndex - gr.studentsperpage * feedbackModifier;
-        var wrapValue = null; // TODO implement wrapping when moving left
-
-    } else if (direction == 'up') {
-
-        // Jump up from value to feedback: origin + (studentsperpage - 1)
-        if (idData.type == 'value' || idData.type == 'scale') {
-            var upDownOffset = gr.studentsperpage - 1;
-
-        // Jump up from feedback to value: origin - studentsperpage
-        } else if (idData.type == 'feedback') {
-            var upDownOffset = -(gr.studentsperpage);
-        }
-
-        var needle = origin_element.tabIndex + upDownOffset;
-        var wrapValue = haystack.length;
-
-    } else if (direction == 'down') {
-        // Jump down from value to feedback: origin + studentsperpage
-        if (idData.type == 'value' || idData.type == 'scale') {
-            var upDownOffset = gr.studentsperpage;
-
-        // Jump down from feedback to value: origin - (studentsperpage - 1)
-        } else if (idData.type == 'feedback') {
-            var upDownOffset = -(gr.studentsperpage - 1);
-        }
-        var needle = origin_element.tabIndex + upDownOffset;
-        var wrapValue = 1;
-    }
-
-    for (var i = 0; i < haystack.length; i++) {
-        if (haystack[i].tabIndex == wrapValue) {
-            wrapElement = haystack[i];
-        }
-
-        if (haystack[i].tabIndex == needle) {
-            return haystack[i];
-        }
-    }
-
-    // If we haven't returned yet, it means we have reached the end of tabindices: return the wrap element
-    if (wrapElement != null) {
-        return wrapElement;
-    } else { // If no wrap element, just return the element of origin: we are stuck!
-        return origin_element;
-    }
-};
-
-
-YAHOO.grader_report.init = function() {
-    var gr = YAHOO.grader_report;
-
-    // Handle Key presses: Tab and Enter
-    this.keyHandler = function(e) {
-        var charCode = YAHOO.util.Event.getCharCode(e);
-
-        YAHOO.log("init: Key pressed (" + charCode + "). el_being_edited = " + gr.el_being_edited, "info");
-        // Handle keys if editing
-        if (gr.el_being_edited !== null) {
-            var editedEl = document.getElementById(gr.el_being_edited.elementId);
-            var idData = gr.getIdData(editedEl.id);
-
-            // Handle Tab and Shift-Tab for navigation forward/backward
-            if (charCode == 9) {
-                gr.saveField(editedEl);
-                gr.closeField(editedEl, false);
-
-                if (e.shiftKey) {
-                    var fieldToOpen = gr.getField(editedEl, 'previous');
-                } else {
-                    var fieldToOpen = gr.getField(editedEl, 'next');
-                }
-
-                gr.openField(fieldToOpen);
-            }
-
-            // Handle Enter key press
-            if (charCode == 13 && idData.type != 'feedback') { // textareas need [enter]
-                // Locate element being edited
-                var editedEl = document.getElementById(gr.el_being_edited.elementId);
-                gr.saveField(editedEl);
-                gr.closeField(editedEl, false);
-            }
-
-            // Handle ctrl-arrows
-            var arrows = { 37: "left", 38: "up", 39: "right", 40: "down" };
-
-            if (e.ctrlKey && (charCode == 37 || charCode == 38 || charCode == 39 || charCode == 40)) {
-                gr.saveField(editedEl);
-                gr.closeField(editedEl, false);
-
-                var fieldToOpen = gr.getField(editedEl, arrows[charCode]);
-                gr.openField(fieldToOpen);
-            }
-        }
-    }
-
-    // Handle mouse clicks
-    this.clickHandler = function(e) {
-        var clickTargetElement = YAHOO.util.Event.getTarget(e);
-
-
-        // Handle a click while a grade value or feedback is being edited
-        if (gr.el_being_edited !== null) {
-            // idData represents the element being edited, not the clicked element
-            var idData = gr.getIdData(gr.el_being_edited.elementId);
-
-            if (idData.type != 'scale') {
-                // If clicking in a cell being edited, no action
-                // parentNode is the original a to which the element was added as a Child node
-                if (gr.el_being_edited.elementId == clickTargetElement.parentNode.id) {
-                    YAHOO.log("init: Clicked within the edited element, no action.", "info");
-                    return false;
-                }
-
-                // Otherwise, we CANCEL the current edit
-                var originalTarget = document.getElementById(gr.el_being_edited.elementId);
-                YAHOO.log("init: Clicked out of the edited element, cancelling edit.", "info");
-                gr.closeField(originalTarget, true);
-
-            } else if (idData.type == 'scale' && gr.el_being_edited.elementId == clickTargetElement.id) {
-
-                // An option has been selected, update the element
-                gr.saveField(clickTargetElement);
-                // Then open the element to save the new value in el_being_edited
-                gr.openField(clickTargetElement);
-                return;
-            }
-        }
-
-        while (clickTargetElement.id != 'grade-report-grader-index') {
-            anchor_re = /(gradevalue|gradefeedback|gradescale)(.*) editable/;
-            anchor_matches = anchor_re.exec(clickTargetElement.className);
-
-            // YAHOO.log("className = " + clickTargetElement.className, "info");
-            var nodeName = clickTargetElement.nodeName.toLowerCase();
-
-            if ((nodeName == 'a' || nodeName == 'select') && anchor_matches) {
-                gr.openField(clickTargetElement);
-                break;
-
-            // If clicked anywhere else in the cell, default to editing the grade, not the feedback
-            } else if (clickTargetElement.nodeName.toLowerCase() == "td" && clickTargetElement.id.match(/gradecell/)) {
-                anchors = YAHOO.util.Dom.getElementsByClassName("editable", "a", clickTargetElement);
-                if (anchors.length > 0) {
-                    clickTargetElement = anchors[0];
-                } else {
-                    break;
-                }
-            } else {
-                clickTargetElement = clickTargetElement.parentNode;
-            }
-        }
-    }
-
-    YAHOO.util.Event.on("grade-report-grader-index", "click", this.clickHandler);
-    YAHOO.util.Event.on(document, "keydown", this.keyHandler, this, true);
-};
-
-YAHOO.util.Event.onDOMReady(YAHOO.grader_report.init);
-
-// ]]>
-</script>
Index: moodle/grade/report/grader/ajaxlib.php
--- moodle/grade/report/grader/ajaxlib.php Base (1.11)
+++ moodle/grade/report/grader/ajaxlib.php Locally Modified (Based On 1.11)
@@ -25,6 +25,7 @@
  * Class providing an API for the grader report building and displaying.
  * @uses grade_report
  * @package gradebook
+ * @todo MDL-21562 Look at this class + its methods and try to work out what is still required
  */
 class grade_report_grader_ajax extends grade_report_grader {
 
@@ -131,6 +132,7 @@
 
     /**
      * Builds and return the HTML rows of the table (grades headed by student).
+     * @todo MDL-21562 Is this still used anywhere
      * @return string HTML
      */
     function get_studentshtml() {
@@ -154,6 +156,9 @@
     /**
      * Given a userid, and provided the gtree is correctly loaded, returns a complete HTML row for this user.
      *
+     * @todo MDL-21562 Apparently not used anywhere please check
+     * @todo MDL-21562 Calls to JavaScript function `set_row` will no longer work
+     *          and need to be replaced
      * @param object $user
      * @return string
      */
Index: moodle/grade/report/grader/functions.js
--- moodle/grade/report/grader/functions.js Base (1.5)
+++ moodle/grade/report/grader/functions.js Locally Deleted
@@ -1,32 +0,0 @@
-/** highlight/unset the row of a table **/
-function set_row(idx) {
-    var table = document.getElementById('user-grades');
-    var rowsize = table.rows[idx].cells.length;
-    for (var i = 0; i < rowsize; i++) {
-        if (table.rows[idx].cells[i]) {
-            if (table.rows[idx].cells[i].className.search(/hmarked/) != -1) {
-                table.rows[idx].cells[i].className = table.rows[idx].cells[i].className.replace(' hmarked', '');
-            } else {
-                table.rows[idx].cells[i].className += ' hmarked';
-            }
-        }
-    }
-}
-
-function yui_set_row(e) {
-    set_row(this.parentNode.rowIndex);
-}
-
-/** highlight/unset the column of a table **/
-function set_col(idx) {
-    var table = document.getElementById('user-grades');
-    for (var i = 1; i < table.rows.length; i++) {
-        if (table.rows[i].cells[idx]) {
-            if (table.rows[i].cells[idx].className.search(/vmarked/) != -1) {
-                table.rows[i].cells[idx].className = table.rows[i].cells[idx].className.replace(' vmarked', '');
-            } else {
-                table.rows[i].cells[idx].className += ' vmarked';
-            }
-        }
-    }
-}
Index: moodle/grade/report/grader/grader.js
--- moodle/grade/report/grader/grader.js Base (1.5)
+++ moodle/grade/report/grader/grader.js Locally Deleted
@@ -1,141 +0,0 @@
-YAHOO.namespace("graderreport");
-
-YAHOO.graderreport.init = function() {
-    // attach event listener to the table for mouseover and mouseout
-    var table = document.getElementById('user-grades');
-    YAHOO.util.Event.on(table, 'mouseover', YAHOO.graderreport.mouseoverHandler);
-    YAHOO.util.Event.on(table, 'mouseout', YAHOO.graderreport.mouseoutHandler);
-
-    // Make single panel that can be dynamically re-rendered wit the right data.
-    YAHOO.graderreport.panelEl = new YAHOO.widget.Panel("tooltipPanel", {
-
-        draggable: false,
-        visible: false,
-        close: false,
-        preventcontextoverlap: true,
-        underlay: 'none'
-    });
-
-    YAHOO.graderreport.panelEl.render(table);
-
-};
-
-YAHOO.graderreport.mouseoverHandler = function (e) {
-
-    var tempNode = '';
-    var searchString = '';
-    var tooltipNode = '';
-
-    // get the element that we just moved the mouse over
-    var elTarget = YAHOO.util.Event.getTarget(e);
-
-    // if it was part of the yui panel, we don't want to redraw yet
-    searchString = /fullname|itemname|feedback/;
-    if (elTarget.className.search(searchString) > -1) {
-        return false;
-    }
-
-    // move up until we are in the actual cell, not any other child div or span
-    while (elTarget.id != 'user-grades') {
-        if(elTarget.nodeName.toUpperCase() == "TD") {
-            break;
-        } else {
-            elTarget = elTarget.parentNode;
-        }
-    }
-
-    // only make a tooltip for cells with grades
-    if (elTarget.className.search('grade cell') > -1) {
-
-        // each time we go over a new cell, we need to put it's tooltip into a div to stop it from
-        // popping up on top of the panel.
-
-        // don't do anything if we have already made the tooltip div
-        var makeTooltip = true;
-        for (var k=0; k < elTarget.childNodes.length; k++) {
-            if (typeof(elTarget.childNodes[k].className) != 'undefined') {
-                if (elTarget.childNodes[k].className.search('tooltipDiv') > -1) {
-                    makeTooltip =  false;
-                }
-            }
-        }
-
-        // if need to, make the tooltip div and append it to the cell
-        if (makeTooltip) {
-
-            tempNode = document.createElement("div");
-            tempNode.className = "tooltipDiv";
-            tempNode.innerHTML = elTarget.title;
-            elTarget.appendChild(tempNode);
-            elTarget.title = null;
-        }
-
-        // Get the tooltip div
-        elChildren = elTarget.childNodes;
-        for (var m=0; m < elChildren.length; m++) {
-            if (typeof(elChildren[m].className) != 'undefined') {
-                if (elChildren[m].className.search('tooltipDiv') > -1) {
-                    tooltipNode = elChildren[m];
-                    break;
-                }
-            }
-        }
-        //build and show the tooltip (if not empty)
-        if(tooltipNode.innerHTML)
-        {
-            YAHOO.graderreport.panelEl.setBody(tooltipNode.innerHTML);
-            YAHOO.graderreport.panelEl.render(elTarget);
-            YAHOO.graderreport.panelEl.show();
-        }
-    }
-};
-
-// only hide the overlay if the mouse has not moved over it
-YAHOO.graderreport.mouseoutHandler = function (e) {
-
-    var classVar = '';
-    var searchString = '';
-    var newTargetClass = '';
-    var newTarget = YAHOO.util.Event.getRelatedTarget(e);
-
-    // deals with an error if the mouseout event is over the lower scrollbar
-    try {
-        classVar = newTarget.className;
-    } catch (err) {
-        YAHOO.graderreport.panelEl.hide()
-        return false;
-    }
-
-    // if we are over any part of the panel, do not hide
-    // do this by walking up the DOM till we reach table level, looking for panel tag
-    while ((typeof(newTarget.id) == 'undefined') || (newTarget.id != 'user-grades')) {
-
-        try {
-            newTargetClass = newTarget.className;
-        } catch (err) {
-            // we've gone over the scrollbar again
-            YAHOO.graderreport.panelEl.hide()
-            return false;
-        }
-        searchString = /yui-panel|grade cell/;
-        if (newTargetClass.search(searchString) > -1) {
-            // we're in the panel so don't hide it
-            return false;
-        }
-
-        if (newTarget.nodeName.toUpperCase() == "HTML") {
-            // we missed the user-grades table altogether by moving down off screen to read a long one
-            YAHOO.graderreport.panelEl.hide()
-            break;
-        }
-
-        newTarget = newTarget.parentNode;
-    }
-
-    // no panel so far and we went up to the
-    YAHOO.graderreport.panelEl.hide()
-
-};
-
-
-YAHOO.util.Event.onDOMReady(YAHOO.graderreport.init);
Index: moodle/grade/report/grader/index.php
--- moodle/grade/report/grader/index.php Base (1.116)
+++ moodle/grade/report/grader/index.php Locally Modified (Based On 1.116)
@@ -110,17 +110,6 @@
 // Initialise the grader report object
 $report = new grade_report_grader($courseid, $gpr, $context, $page, $sortitemid);
 
-$PAGE->requires->yui2_lib('event');
-$PAGE->requires->yui2_lib('json');
-$PAGE->requires->yui2_lib('connection');
-$PAGE->requires->yui2_lib('dragdrop');
-$PAGE->requires->yui2_lib('element');
-$PAGE->requires->yui2_lib('container');
-$PAGE->requires->js('/grade/report/grader/functions.js');
-$PAGE->requires->js('/grade/report/grader/grader.js');
-$PAGE->requires->js('/lib/overlib/overlib.js', true);
-$PAGE->requires->js('/lib/overlib/overlib_cssstyle.js', true);
-
 if ($report->get_pref('enableajax')) {
     $report = new grade_report_grader_ajax($courseid, $gpr, $context, $page, $sortitemid);
 }
@@ -172,7 +161,7 @@
 $reporthtml = $report->get_grade_table();
 
 // print submit button
-if ($USER->gradeediting[$course->id] && ($report->get_pref('showquickfeedback') || $report->get_pref('quickgrading')) && !$report->get_pref('enableajax')) {
+if ($USER->gradeediting[$course->id] && ($report->get_pref('showquickfeedback') || $report->get_pref('quickgrading'))) {
     echo '<form action="index.php" method="post">';
     echo '<div>';
     echo '<input type="hidden" value="'.s($courseid).'" name="id" />';
@@ -189,10 +178,4 @@
 if (!empty($studentsperpage) && $studentsperpage >= 20) {
     echo $OUTPUT->paging_bar(moodle_paging_bar::make($numusers, $report->page, $studentsperpage, $report->pbarurl));
 }
-
-echo $OUTPUT->container('tooltip panel', '', 'hiddentooltiproot');
-// Print AJAX code
-if ($report->get_pref('enableajax')) {
-    require_once 'ajax.php';
-}
 echo $OUTPUT->footer();
Index: moodle/grade/report/grader/lib.php
--- moodle/grade/report/grader/lib.php Base (1.190)
+++ moodle/grade/report/grader/lib.php Locally Modified (Based On 1.190)
@@ -545,7 +545,7 @@
 
         for ($i = 0; $i < $levels; $i++) {
             $fillercell = new html_table_cell();
-            $fillercell->add_classes(array('fixedcolumn', 'cell', 'c0', 'topleft'));
+            $fillercell->add_classes(array('fixedcolumn', 'cell', 'topleft'));
             $fillercell->text = ' ';
             $fillercell->colspan = $colspan;
             $row = html_table_row::make(array($fillercell));
@@ -556,7 +556,7 @@
         $headerrow->add_class('heading');
 
         $studentheader = new html_table_cell();
-        $studentheader->add_classes(array('header', 'c0'));
+        $studentheader->add_classes(array('header'));
         $studentheader->scope = 'col';
         $studentheader->header = true;
         $studentheader->id = 'studentheader';
@@ -572,7 +572,7 @@
             $sortidnumberlink = html_writer::link(new moodle_url($this->baseurl, array('sortitemid'=>'idnumber')), get_string('idnumber'));
 
             $idnumberheader = new html_table_cell();
-            $idnumberheader->add_classes(array('header', 'c0', 'useridnumber'));
+            $idnumberheader->add_classes(array('header', 'useridnumber'));
             $idnumberheader->scope = 'col';
             $idnumberheader->header = true;
             $idnumberheader->text = $arrows['idnumber'];
@@ -588,13 +588,13 @@
 
         foreach ($this->users as $userid => $user) {
             $userrow = new html_table_row();
+            $userrow->id = 'fixed_user_'.$userid;
             $userrow->add_classes(array('r'.$this->rowcount++, $rowclasses[$this->rowcount % 2]));
 
             $usercell = new html_table_cell();
-            $usercell->add_classes(array('c0', 'user'));
+            $usercell->add_classes(array('user'));
             $usercell->header = true;
             $usercell->scope = 'row';
-            $usercell->add_action('click', 'yui_set_row');
 
             if ($showuserimage) {
                 $usercell->text = $OUTPUT->container($OUTPUT->user_picture($user), 'userpic');
@@ -617,10 +617,9 @@
 
             if ($showuseridnumber) {
                 $idnumbercell = new html_table_cell();
-                $idnumbercell->add_classes(array('header', 'c0', 'useridnumber'));
+                $idnumbercell->add_classes(array('header', 'useridnumber'));
                 $idnumbercell->header = true;
                 $idnumbercell->scope = 'row';
-                $idnumbercell->add_action('click', 'yui_set_row');
                 $userrow->cells[] = $idnumbercell;
             }
 
@@ -639,7 +638,7 @@
      * @return array Array of html_table_row objects
      */
     public function get_right_rows() {
-        global $CFG, $USER, $OUTPUT, $DB;
+        global $CFG, $USER, $OUTPUT, $DB, $PAGE;
 
         $rows = array();
         $this->rowcount = 0;
@@ -648,10 +647,18 @@
         $gradetabindex = 1;
         $columnstounset = array();
         $strgrade = $this->get_lang_string('grade');
+        $strfeedback  = $this->get_lang_string("feedback");
         $arrows = $this->get_sort_arrows();
 
+        $jsarguments = array(
+            'cfg'   => array('ajaxenabled'=>false),
+            'items'     => array(),
+            'users'     => array(),
+            'feedback'  => array()
+        );
+        $jsscales = array();
+
         foreach ($this->gtree->get_levels() as $key=>$row) {
-            $columncount = 0;
             if ($key == 0) {
                 // do not display course grade category
                 // continue;
@@ -673,10 +680,8 @@
                 $itemmodule = null;
                 $iteminstance = null;
 
-                $columnclass = 'c' . $columncount++;
                 if (!empty($element['colspan'])) {
                     $colspan = $element['colspan'];
-                    $columnclass = '';
                 } else {
                     $colspan = 1;
                 }
@@ -690,7 +695,7 @@
 // Element is a filler
                 if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') {
                     $fillercell = new html_table_cell();
-                    $fillercell->add_classes(array($columnclass, $type, $catlevel));
+                    $fillercell->add_classes(array($type, $catlevel));
                     $fillercell->colspan = $colspan;
                     $fillercell->text = '&nbsp;';
                     $fillercell->header = true;
@@ -700,7 +705,7 @@
 // Element is a category
                 else if ($type == 'category') {
                     $categorycell = new html_table_cell();
-                    $categorycell->add_classes(array($columnclass, 'category', $catlevel));
+                    $categorycell->add_classes(array('category', $catlevel));
                     $categorycell->colspan = $colspan;
                     $categorycell->text = shorten_text($element['object']->get_name());
                     $categorycell->text .= $this->get_collapsing_icon($element);
@@ -732,7 +737,7 @@
                     $headerlink = $this->gtree->get_element_header($element, true, $this->get_pref('showactivityicons'), false);
 
                     $itemcell = new html_table_cell();
-                    $itemcell->add_classes(array($columnclass, $type, $catlevel));
+                    $itemcell->add_classes(array($type, $catlevel, 'highlightable'));
 
                     if ($element['object']->is_hidden()) {
                         $itemcell->add_class('hidden');
@@ -742,7 +747,6 @@
                     $itemcell->text = shorten_text($headerlink) . $arrow;
                     $itemcell->header = true;
                     $itemcell->scope = 'col';
-                    $itemcell->onclick = 'set_col(this.cellIndex);';
 
                     $headingrow->cells[] = $itemcell;
                 }
@@ -757,11 +761,14 @@
         $scaleslist = array();
         $tabindices = array();
 
-        foreach ($this->gtree->get_items() as $item) {
+        foreach ($this->gtree->get_items() as $itemid=>$item) {
+            $scale = null;
             if (!empty($item->scaleid)) {
                 $scaleslist[] = $item->scaleid;
+                $jsarguments['items'][$itemid] = array('id'=>$itemid, 'name'=>$item->get_name(true), 'type'=>'scale', 'scale'=>$item->scaleid, 'decimals'=>$item->get_decimals());
+            } else {
+                $jsarguments['items'][$itemid] = array('id'=>$itemid, 'name'=>$item->get_name(true), 'type'=>'value', 'scale'=>false, 'decimals'=>$item->get_decimals());
             }
-
             $tabindices[$item->id]['grade'] = $gradetabindex;
             $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers;
             $gradetabindex += $numusers * 2;
@@ -771,6 +778,7 @@
         if (!empty($scaleslist)) {
             $scalesarray = $DB->get_records_list('scale', 'id', $scaleslist);
         }
+        $jsscales = $scalesarray;
 
         $rowclasses = array('even', 'odd');
 
@@ -786,17 +794,21 @@
                 unset($hidingaffected);
             }
 
-            $columncount = 0;
 
             $itemrow = new html_table_row();
+            $itemrow->id = 'user_'.$userid;
             $itemrow->add_class($rowclasses[$this->rowcount % 2]);
 
+            $jsarguments['users'][$userid] = fullname($user);
+
             foreach ($this->gtree->items as $itemid=>$unused) {
                 $item =& $this->gtree->items[$itemid];
                 $grade = $this->grades[$userid][$item->id];
 
                 $itemcell = new html_table_cell();
 
+                $itemcell->id = 'u'.$userid.'i'.$itemid;
+
                 // Get the decimal points preference for this item
                 $decimalpoints = $item->get_decimals();
 
@@ -840,15 +852,10 @@
                     // $itemcell->add_class('excluded');
                 }
 
-                $gradetitle = $OUTPUT->container(fullname($user), 'fullname');
-                $gradetitle .= $OUTPUT->container($item->get_name(true), 'itemname');
-
-                if (!empty($grade->feedback) && !$USER->gradeediting[$this->courseid]) {
-                    $gradetitle .= $OUTPUT->container(wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br/ >'), 'feedback');
+                if (!empty($grade->feedback)) {
+                    $jsarguments['feedback'][] = array('user'=>$userid, 'item'=>$itemid, 'content'=>wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br/ >'));
                 }
 
-                $itemcell->title = s($gradetitle);
-
                 if ($grade->is_excluded()) {
                     $itemcell->text .= $OUTPUT->span(get_string('excluded', 'grades'), 'excludedfloater');
                 }
@@ -900,9 +907,8 @@
                             } else {
                                 $nogradestr = $this->get_lang_string('nooutcome', 'grades');
                             }
-                            $itemcell->text .= '<input type="hidden" name="oldgrade_'.$userid.'_'
-                                          .$item->id.'" value="'.$oldval.'"/>';
-                            $attributes = array('tabindex' => $tabindices[$item->id]['grade']);
+                            $itemcell->text .= '<input type="hidden" id="oldgrade_'.$userid.'_'.$item->id.'" name="oldgrade_'.$userid.'_'.$item->id.'" value="'.$oldval.'"/>';
+                            $attributes = array('tabindex' => $tabindices[$item->id]['grade'], 'id'=>'grade_'.$userid.'_'.$item->id);
                             $itemcell->text .= html_writer::select($scaleopt, 'grade_'.$userid.'_'.$item->id, $gradeval, array(-1=>$nogradestr), $attributes);;
                         } elseif(!empty($scale)) {
                             $scales = explode(",", $scale->scale);
@@ -921,10 +927,10 @@
                     } else if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type
                         if ($this->get_pref('quickgrading') and $grade->is_editable()) {
                             $value = format_float($gradeval, $decimalpoints);
-                            $itemcell->text .= '<input type="hidden" name="oldgrade_'.$userid.'_'.$item->id.'" value="'.$value.'" />';
+                            $itemcell->text .= '<input type="hidden" id="oldgrade_'.$userid.'_'.$item->id.'" name="oldgrade_'.$userid.'_'.$item->id.'" value="'.$value.'" />';
                             $itemcell->text .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade']
                                           . '" type="text" class="text" title="'. $strgrade .'" name="grade_'
-                                          .$userid.'_' .$item->id.'" value="'.$value.'" />';
+                                          .$userid.'_' .$item->id.'" id="grade_'.$userid.'_'.$item->id.'" value="'.$value.'" />';
                         } else {
                             $itemcell->text .= $OUTPUT->span(format_float($gradeval, $decimalpoints), "gradevalue$hidden$gradepass");
                         }
@@ -934,17 +940,19 @@
                     // If quickfeedback is on, print an input element
                     if ($this->get_pref('showquickfeedback') and $grade->is_editable()) {
 
-                        $itemcell->text .= '<input type="hidden" name="oldfeedback_'
-                                      .$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />';
-                        $itemcell->text .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback']
-                                      . '" size="6" title="' . $strfeedback . '" type="text" name="feedback_'
-                                      .$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />';
+                        $itemcell->text .= '<input type="hidden" id="oldfeedback_'.$userid.'_'.$item->id.'" name="oldfeedback_'.$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />';
+                        $itemcell->text .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback'].'" id="feedback_'.$userid.'_'.$item->id
+                                      . '" size="6" title="' . $strfeedback . '" type="text" name="feedback_'.$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />';
                     }
 
                 } else { // Not editing
                     $gradedisplaytype = $item->get_displaytype();
 
-                    // If feedback present, surround grade with feedback tooltip: Open span here
+                    if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
+                        $itemcell->add_class('grade_type_scale');
+                    } else if ($item->gradetype != GRADE_TYPE_TEXT) {
+                        $itemcell->add_class('grade_type_text');
+                    }
 
                     if ($item->needsupdate) {
                         $itemcell->text .= $OUTPUT->span(get_string('error'), "gradingerror$hidden$gradepass");
@@ -962,6 +970,29 @@
             $rows[] = $itemrow;
         }
 
+        if ($this->get_pref('enableajax')) {
+            $jsarguments['cfg']['ajaxenabled'] = true;
+            $jsarguments['cfg']['scales'] = array();
+            foreach ($jsscales as $scale) {
+                $jsarguments['cfg']['scales'][$scale->id] = explode(',',$scale->scale);
+            }
+            $jsarguments['cfg']['feedbacktrunclength'] =  $this->feedback_trunc_length;
+            $jsarguments['cfg']['feedback'] =  $this->feedbacks;
+        }
+        $jsarguments['cfg']['isediting'] = (bool)$USER->gradeediting[$this->courseid];
+        $jsarguments['cfg']['courseid'] =  $this->courseid;
+        $jsarguments['cfg']['studentsperpage'] =  $this->get_pref('studentsperpage');
+        $jsarguments['cfg']['showquickfeedback'] =  (bool)$this->get_pref('showquickfeedback');
+
+        $module = array(
+            'name'      => 'gradereport_grader',
+            'fullpath'  => '/grade/report/grader/module.js',
+            'requires'  => array('base', 'dom', 'event', 'event-mouseenter', 'event-key', 'io', 'json-parse', 'overlay')
+        );
+        $PAGE->requires->js_init_call('M.gradereport_grader.init', $jsarguments, false, $module);
+        $PAGE->requires->strings_for_js(array('addfeedback','feedback', 'grade'), 'grades');
+        $PAGE->requires->strings_for_js(array('ajaxchoosescale','ajaxclicktoclose','ajaxerror','ajaxfailedupdate', 'ajaxfieldchanged'), 'gradereport_grader');
+
         $rows = $this->get_right_range_row($rows);
         $rows = $this->get_right_avg_row($rows, true);
         $rows = $this->get_right_avg_row($rows);
@@ -984,6 +1015,7 @@
 
         $html = '';
 
+
         if ($fixedstudents) {
             $fixedcolumntable = new html_table();
             $fixedcolumntable->id = 'fixed_column';
@@ -1027,7 +1059,7 @@
             $controlsrow = new html_table_row();
             $controlsrow->add_class('controls');
             $controlscell = new html_table_cell();
-            $controlscell->add_classes(array('header', 'c0', 'controls'));
+            $controlscell->add_classes(array('header', 'controls'));
             $controlscell->colspan = $colspan;
             $controlscell->text = $this->get_lang_string('controls','grades');
 
@@ -1050,7 +1082,7 @@
             $rangerow = new html_table_row();
             $rangerow->add_classes(array('range', 'r'.$this->rowcount++));
             $rangecell = new html_table_cell();
-            $rangecell->add_classes(array('header', 'c0', 'range'));
+            $rangecell->add_classes(array('header', 'range'));
             $rangecell->colspan = $colspan;
             $rangecell->header = true;
             $rangecell->scope = 'row';
@@ -1085,7 +1117,7 @@
                 $groupavgrow = new html_table_row();
                 $groupavgrow->add_classes(array('groupavg', 'r'.$this->rowcount++));
                 $groupavgcell = new html_table_cell();
-                $groupavgcell->add_classes(array('header', 'c0', 'range'));
+                $groupavgcell->add_classes(array('header', 'range'));
                 $groupavgcell->colspan = $colspan;
                 $groupavgcell->header = true;
                 $groupavgcell->scope = 'row';
@@ -1100,7 +1132,7 @@
                 $avgrow = new html_table_row();
                 $avgrow->add_classes(array('avg', 'r'.$this->rowcount++));
                 $avgcell = new html_table_cell();
-                $avgcell->add_classes(array('header', 'c0', 'range'));
+                $avgcell->add_classes(array('header', 'range'));
                 $avgcell->colspan = $colspan;
                 $avgcell->header = true;
                 $avgcell->scope = 'row';
@@ -1126,7 +1158,6 @@
 
             $showuseridnumber = $this->get_pref('showuseridnumber');
 
-            $columncount = 0;
             foreach ($this->gtree->items as $itemid=>$unused) {
                 // emulate grade element
                 $item =& $this->gtree->get_item($itemid);
Index: moodle/grade/report/grader/module.js
--- moodle/grade/report/grader/module.js No Base Revision
+++ moodle/grade/report/grader/module.js Locally New
@@ -0,0 +1,1136 @@
+/**
+ * Grader Report Module
+ */
+YUI.add('gradereport_grader', function(Y) {
+    /**
+     * The core namespace for the grader report JavaScript
+     * @namespace M.gradereport_grader
+     */
+    M.gradereport_grader = {
+        table : null,           // YUI Node for the reports main table
+        items : [],             // Array containing grade items
+        users : [],             // Array containing user information
+        feedback : [],          // Array containing feedback items
+        ajaxenabled : false,    // True is AJAX is enabled for the report
+        /**
+         * Initialises the JavaScript for the gradebook grader report
+         * 
+         * @function M.gradereport_grader.init
+         * @this {M.gradereport_grader}
+         * @param {YUI} Y A YUI3 instance
+         * @param {Object} cfg Configuration variables
+         * @param {Array} items An array containing grade items
+         * @param {Array} users An array containing user infomation
+         * @param {Array} feedback An array containing feedback information
+         */
+        init : function(Y, cfg, items, users, feedback) {
+            this.isediting = (cfg.isediting);
+            this.ajaxenabled = (cfg.ajaxenabled);
+            this.items = items;
+            this.users = users;
+            this.feedback = feedback;
+            this.table = Y.one('#user-grades');
+
+            // Initalise tooltips
+            this.tooltip.init();
+            
+            // Alias this so that we can use the correct scope in the coming
+            // node iteration
+            this.table.all('tr').each(function(tr){
+                // Check it is a user row
+                if (tr.getAttribute('id').match(/^fixed_user_(\d+)$/)) {
+                    // Highlight rows
+                    tr.all('th.cell').on('click', this.table_highlight_row, this, tr);
+                    // Display tooltips
+                    tr.all('td.cell').each(function(cell){
+                        this.tooltip.attach(cell);
+                    }, this);
+                }
+            }, this);
+
+            // If the fixed table exists then map those rows to highlight the
+            // grades table rows
+            var fixed = Y.one('#fixed_column');
+            if (fixed) {
+                fixed.all('tr').each(function(tr) {
+                    if (tr.getAttribute('id').match(/^fixed_user_(\d+)$/)) {
+                        tr.all('th.cell').on('click', this.table_highlight_row, this, Y.one(tr.getAttribute('id').replace(/^fixed_/, '#')));
+                    }
+                }, this)
+            }
+
+            // Highlight columns
+            this.table.all('.highlightable').each(function(cell){
+                cell.on('click', this.table_highlight_column, this, cell);
+                cell.removeClass('highlightable');
+            }, this);
+
+            // If ajax is enabled then initialise the ajax component
+            if (this.ajaxenabled) {
+                this.ajax.init(cfg);
+            }
+        },
+        /**
+         * Highlights a row in the report
+         *
+         * @function M.gradereport_grader.table_highlight_row
+         * @param {Event} e
+         * @param {Y.Node} tr The table row to hightlight
+         */
+        table_highlight_row : function (e, tr) {
+            tr.all('.cell').toggleClass('hmarked');
+        },
+        /**
+         * Highlights a cell in the table
+         *
+         * @function M.gradereport_grader.table_highlight_column
+         * @param {Event} e
+         * @param {Y.Node} cell
+         */
+        table_highlight_column : function(e, cell) {
+            var column = 0;
+            while (cell = cell.previous('.cell')) {
+                column += parseFloat(cell.getAttribute('colspan')) || 1;
+            }
+            this.table.all('.c'+column).toggleClass('vmarked');
+        },
+        /**
+         * 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 M.gradereport_grader.get_cell_info
+         * @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}
+         */
+        get_cell_info : function(arg) {
+
+            var userid= null;
+            var itemid = null;
+            var feedback = null;
+            var cell = null;
+            var i = null;
+
+            if (arg instanceof 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 = Y.one('#u'+userid+'i'+itemid);
+            }
+
+            if (!cell) {
+                return null;
+            }
+
+            for (i in this.feedback) {
+                var f = this.feedback[i];
+                if (f.user == userid && f.item == itemid) {
+                    feedback = f.content;
+                    break;
+                }
+            }
+
+            return {
+                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 M.gradereport_grader.update_feedback
+         * @this {M.gradereport_grader}
+         * @param {Int} userid
+         * @param {Int} itemid
+         * @param {String} newfeedback
+         * @return {Bool}
+         */
+        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;
+        }
+    };
+    /**
+     * Namespace that contains the functions required to deal with the tooltips.
+     * This is a namespace rather than an object so that we can reuse the overlay
+     * and manage the tooltips though a single easy to understand interface.
+     * @namespace
+     */
+    M.gradereport_grader.tooltip = {
+        overlay : null, // Y.Overlay instance
+        /**
+         * Initialises the tooltip by creating the overlay
+         * 
+         * @function M.gradereport_grader.tooltip.init
+         * @this {M.gradereport_grader.tooltip}
+         */
+        init : function() {
+            this.overlay = new Y.Overlay({
+                bodyContent : 'Loading',
+                visible : false,
+                zIndex : 2
+            });
+            this.overlay.render(M.gradereport_grader.table.ancestor('div'));
+        },
+        /**
+         * Attaches the tooltip event to the provided cell
+         *
+         * @function M.gradereport_grader.tooltip.attach
+         * @this M.gradereport_grader
+         * @param {Y.Node} td The cell to attach the tooltip event to
+         */
+        attach : function(td) {
+            td.on('mouseenter', this.show, this);
+        },
+        /**
+         * Shows the tooltip: Callback from @see M.gradereport_grader.tooltip#attach
+         *
+         * @function M.gradereport_grader.tooltip.show
+         * @this {M.gradereport_grader.tooltip}
+         * @param {Event} e
+         */
+        show : function(e) {
+            e.halt();
+
+            var properties = M.gradereport_grader.get_cell_info(e.target);
+            if (!properties) {
+                return;
+            }
+
+            var content  = '<div class="fullname">'+properties.username+'</div>';
+            content += '<div class="itemname">'+properties.itemname+'</div>';
+            if (properties.feedback) {
+                content += '<div class="feedback">'+properties.feedback+'</div>';
+            }
+
+            properties.cell.on('mouseleave', this.hide, this, properties.cell);
+            properties.cell.addClass('tooltipactive');
+            this.overlay.set('xy', [e.target.getX()+(e.target.get('offsetWidth')/2),e.target.getY()+e.target.get('offsetHeight')-5]);
+            this.overlay.set("bodyContent", content);
+            this.overlay.show();
+            this.overlay.get('boundingBox').setStyle('visibility', 'visible');
+        },
+        /**
+         * Hides the tooltip
+         *
+         * @function M.gradereport_grader.tooltip.hide
+         * @this {M.gradereport_grader.tooltip}
+         * @param {Event} e
+         * @param {Y.Node} cell
+         */
+        hide : function(e, cell) {
+            cell.removeClass('tooltipactive');
+            this.overlay.hide();
+            this.overlay.get('boundingBox').setStyle('visibility', 'hidden');
+        }
+    }
+    /**
+     * Namespace that contains all of the AJAX functionality for the grader report
+     * @namespace M.gradereport_grader.ajax
+     */
+    M.gradereport_grader.ajax = {
+        courseid : null,                // The id for the course being viewed
+        feedbacktrunclength : null,     // The length to truncate feedback to
+        studentsperpage : null,         // The number of students shown per page
+        showquickfeedback : null,       // True if feedback editing should be shown
+        current : null,                 // The field being currently editing
+        pendingsubmissions : [],        // Array containing pending IO transactions
+        scales : [],                    // An array of scales used in this report
+        /**
+         * Initalises the AJAX component of this report
+         * @function M.gradereport_grader.ajax.init
+         * @this {M.gradereport_grader.ajax}
+         * @param {Object} cfg
+         */
+        init : function(cfg) {
+            this.courseid = cfg.courseid || null;
+            this.feedbacktrunclength = cfg.courseid || null;
+            this.studentsperpage = cfg.studentsperpage || null;
+            this.showquickfeedback = cfg.showquickfeedback || false;
+            this.scales = cfg.scales || null;
+            this.existingfields = [];
+            var table = M.gradereport_grader.table;
+            if (!M.gradereport_grader.isediting) {
+                table.all('.cell.grade').on('makeditable|click', this.make_editable, this);
+            } else {
+                var g = M.gradereport_grader;
+                for (var userid in g.users) {
+                    if (!this.existingfields[userid]) {
+                        this.existingfields[userid] = [];
+                    }
+                    for (var itemid in g.items) {
+                        this.existingfields[userid][itemid] = new this.existingfield(userid, itemid);
+                    }
+                }
+            }
+        },
+        /**
+         * Makes a cell editable
+         * @function M.gradereport_grader.ajax.make_editable
+         * @this {M.gradereport_grader.ajax}
+         */
+        make_editable : function(e) {
+            if (e.halt) {
+                e.halt();
+                var target = e.target;
+            } else {
+                var target = e;
+            }
+            if (target.get('nodeName').toUpperCase() !== 'TD') {
+                target = target.ancestor('td');
+            }
+            Y.detach('click', this.make_editable, target);
+
+            if (this.current) {
+                // Current is already set!
+                this.process_editable_field(target);
+                return;
+            }
+
+            // Sort out the field type
+            var fieldtype = 'text';
+            if (target.hasClass('grade_type_scale')) {
+                fieldtype = 'scale';
+            }
+            // Create the appropriate field widget
+            switch (fieldtype) {
+                case 'scale':
+                    this.current = new this.scalefield(target);
+                    break;
+                case 'text':
+                default:
+                    this.current = new this.textfield(target);
+                    break;
+            }
+            this.current.replace().attach_key_events();
+        },
+        /**
+         * Callback function for the user pressing the enter key on an editable field
+         *
+         * @function M.gradereport_grader.ajax.keypress_enter
+         * @this {M.gradereport_grader.ajax}
+         * @param {Event} e
+         */
+        keypress_enter : function(e) {
+            this.process_editable_field(null);
+        },
+        /**
+         * Callback function for the user pressing Tab or Shift+Tab
+         *
+         * @function M.gradereport_grader.ajax.keypress_tab
+         * @this {M.gradereport_grader.ajax}
+         * @param {Event} e
+         * @param {Bool} ignoreshift If true and shift is pressed then don't exec
+         */
+        keypress_tab : function(e, ignoreshift) {
+            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 M.gradereport_grader.ajax.keypress_arrows
+         * @this {M.gradereport_grader.ajax}
+         */
+        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 M.gradereport_grader.ajax.process_editable_field
+         * @this {M.gradereport_grader.ajax}
+         * @param {Y.Node|null} next The next node to make editable (chaining)
+         */
+        process_editable_field : function(next) {
+            if (this.current.has_changed()) {
+                var properties = M.gradereport_grader.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);
+            }
+        },
+        /**
+         * Gets the next cell that is editable (right)
+         * @function M.gradereport_grader.ajax.get_next_cell
+         * @this {M.gradereport_grader.ajax}
+         * @return {Y.Node}
+         */
+        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) {
+                next = this.current.node;
+            }
+            return next;
+        },
+        /**
+         * Gets the previous cell that is editable (left)
+         * @function M.gradereport_grader.ajax.get_prev_cell
+         * @this {M.gradereport_grader.ajax}
+         * @return {Y.Node}
+         */
+        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) {
+                next = this.current.node;
+            }
+            return next;
+        },
+        /**
+         * Gets the cell above if it is editable (up)
+         * @function M.gradereport_grader.ajax.get_above_cell
+         * @this {M.gradereport_grader.ajax}
+         * @return {Y.Node}
+         */
+        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) {
+                next = this.current.node;
+            }
+            return next;
+        },
+        /**
+         * Gets the cell below if it is editable (down)
+         * @function M.gradereport_grader.ajax.get_below_cell
+         * @this {M.gradereport_grader.ajax}
+         */
+        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) {
+                next = this.current.node;
+            }
+            return next;
+        },
+        /**
+         * Submits changes for update
+         *
+         * @function M.gradereport_grader.ajax.submit
+         * @this {M.gradereport_grader.ajax}
+         * @param {Object} properties Properties of the cell being editied
+         * @param {Object} values Object containing old + new values
+         */
+        submit : function(properties, values) {
+
+            // Stop the IO queue so we can add to it
+            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: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: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
+            Y.io.queue.start();
+        },
+        /**
+         * Callback function for IO transaction completions
+         *
+         * Uses a synchronous queue to ensure we maintain some sort of order
+         * 
+         * @function M.gradereport_grader.ajax.submission_outcome
+         * @this {M.gradereport_grader.ajax}
+         */
+        submission_outcome : function(tid, outcome, args) {
+            // Parse the response as JSON
+            try {
+                outcome = Y.JSON.parse(outcome.responseText);
+            } catch(e) {
+                var message = M.str.gradereport_grader.ajaxfailedupdate;
+                message.replace(/\[1\]/, args.type);
+                message.replace(/\[2\]/, M.gradereport_grader.users[args.properties.userid]);
+                this.display_submission_error(message, p.cell);
+                return;
+            }
+
+            // Quick reference for the grader report
+            var g = M.gradereport_grader;
+            // Check the outcome
+            if (outcome.result == 'success') {
+                // Iterate through each row in the result object
+                for (i in outcome.row) {
+                    // alias it, we use it quite a bit
+                    var r = outcome.row[i];
+                    // Get the cell refered to by this result object
+                    var info = g.get_cell_info([r.userid, r.itemid]);
+                    if (!info) {
+                        continue;
+                    }
+                    // Calculate the final grade for the cell
+                    var finalgrade = '';
+                    if (r.scale) {
+                        finalgrade = (r.finalgrade)?this.scales[r.scale][parseFloat(r.finalgrade)-1]:'-';
+                    } else {
+                        finalgrade = (r.finalgrade)?parseFloat(r.finalgrade).toFixed(info.itemdp):'-';
+                    }
+                    if (g.isediting) {
+                        if (args.properties.itemtype == 'scale') {
+                            info.cell.one('#grade_'+r.userid+'_'+r.itemid).all('options').each(function(option){
+                                if (option.get('value') == finalgrade) {
+                                    option.setAttribute('selected', 'selected');
+                                } else {
+                                    option.removeAttribute('selected');
+                                }
+                            });
+                        } else {
+                            info.cell.one('#grade_'+r.userid+'_'+r.itemid).set('value', 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
+                            info.cell.one('.gradevalue').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.str.gradereport_grader.ajaxfieldchanged)) {
+                                this.current.set_grade(finalgrade);
+                                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') {
+                    g.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 (var i in this.pendingsubmissions) {
+                if (this.pendingsubmissions[i].transaction.id == tid) {
+                    this.pendingsubmissions[i].complete = true;
+                    this.pendingsubmissions[i].outcome = outcome;
+                    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 M.gradereport_grader.ajax.display_submission_error
+         * @this {M.gradereport_grader.ajax}
+         * @param {String} message
+         * @param {Y.Node} cell
+         */
+        display_submission_error : function(message, cell) {
+            var erroroverlay = new Y.Overlay({
+                headerContent : '<div><strong class="error">'+M.str.gradereport_grader.ajaxerror+'</strong>  <em>'+M.str.gradereport_grader.ajaxclicktoclose+'</em></div>',
+                bodyContent : message,
+                visible : false,
+                zIndex : 3
+            });
+            erroroverlay.set('xy', [cell.getX()+10,cell.getY()+10]);
+            erroroverlay.render(M.gradereport_grader.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');
+            // Transparency is COOL!!
+            var makeopace = function(e) {
+                if (this.get) {
+                    if (e.type == 'mouseover') {
+                        this.get('boundingBox').setStyle('opacity', 0.5);
+                    } else {
+                        this.get('boundingBox').setStyle('opacity', 1);
+                    }
+                }
+            }
+            erroroverlay.get('boundingBox').on('mouseenter', makeopace, erroroverlay);
+            erroroverlay.get('boundingBox').on('mouseleave', makeopace, erroroverlay);
+        }
+    };
+    /**
+     * A class for existing fields
+     * This class is used only when the user is in editing mode
+     *
+     * @class
+     * @constructor
+     * @param {Int} userid
+     * @param {Int} itemid
+     */
+    M.gradereport_grader.ajax.existingfield = function(userid, itemid) {
+        this.userid = userid;
+        this.itemid = itemid;
+        this.editfeedback = M.gradereport_grader.ajax.showquickfeedback;
+        this.grade = Y.one('#grade_'+userid+'_'+itemid);
+        this.oldgrade = Y.one('#oldgrade_'+userid+'_'+itemid);
+
+        // 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 = Y.one('#feedback_'+userid+'_'+itemid);
+            this.oldfeedback = Y.one('#oldfeedback_'+userid+'_'+itemid);
+            // 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
+            this.keyevents.push(Y.on('key', this.keypress_tab, this.grade, 'press:9+shift', this));                // Handle Shift+Tab
+            this.keyevents.push(Y.on('key', this.keypress_tab, this.feedback, 'press:9', this, true));                   // Handle Tab
+            this.keyevents.push(Y.on('key', this.keypress_enter, this.feedback, 'press:13', this));                // Handle the Enter key being pressed
+            this.keyevents.push(Y.on('key', this.keypress_arrows, this.feedback, 'press:37,38,39,40+ctrl', this)); // Handle CTRL + arrow keys
+
+            // Override the default tab movements for fields in the same cell
+            this.keyevents.push(Y.on('key', function(e){e.preventDefault();this.grade.focus()}, this.feedback, 'press:9+shift', this));
+            this.keyevents.push(Y.on('key', function(e){if (e.shiftKey) return;e.preventDefault();this.feedback.focus()}, this.grade, 'press:9', this));
+        } else {
+            this.keyevents.push(Y.on('key', this.keypress_tab, this.grade, 'press:9', this));                      // Handle Tab and Shift+Tab
+        }
+        this.keyevents.push(Y.on('key', this.keypress_enter, this.grade, 'press:13', this));                   // Handle the Enter key being pressed
+        this.keyevents.push(Y.on('key', this.keypress_arrows, this.grade, 'press:37,38,39,40+ctrl', this));    // Handle CTRL + arrow keys
+    };
+    M.gradereport_grader.ajax.existingfield.prototype.userid = null;
+    M.gradereport_grader.ajax.existingfield.prototype.itemid = null;
+    M.gradereport_grader.ajax.existingfield.prototype.editfeedback = false;
+    M.gradereport_grader.ajax.existingfield.prototype.grade = null;
+    M.gradereport_grader.ajax.existingfield.prototype.oldgrade = null;
+    M.gradereport_grader.ajax.existingfield.prototype.keyevents = [];
+    /**
+     * Handles saving of changed on keypress
+     * 
+     * @function M.gradereport_grader.ajax.existingfield.keypress_enter
+     * @this {M.gradereport_grader.ajax.existingfield}
+     * @param {Event} e
+     */
+    M.gradereport_grader.ajax.existingfield.prototype.keypress_enter = function(e) {
+        this.submit();
+    }
+    /**
+     * Handles setting the correct focus if the user presses tab
+     * 
+     * @function M.gradereport_grader.ajax.existingfield.keypress_tab
+     * @this {M.gradereport_grader.ajax.existingfield}
+     * @param {Event} e
+     * @param {Bool} ignoreshift
+     */
+    M.gradereport_grader.ajax.existingfield.prototype.keypress_tab = function(e, ignoreshift) {
+        e.preventDefault();
+        var next = null;
+        if (e.shiftKey) {
+            if (ignoreshift) {
+                return;
+            }
+            next = M.gradereport_grader.ajax.get_above_cell(this.grade.ancestor('td'));
+        } else {
+            next = M.gradereport_grader.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 M.gradereport_grader.ajax.existingfield.keypress_arrows
+     * @this {M.gradereport_grader.ajax.existingfield}
+     * @param {Event} e
+     */
+    M.gradereport_grader.ajax.existingfield.prototype.keypress_arrows = function(e) {
+        var next = null;
+        switch (e.keyCode) {
+            case 37:    // Left
+                next = M.gradereport_grader.ajax.get_prev_cell(this.grade.ancestor('td'));
+                break;
+            case 38:    // Up
+                next = M.gradereport_grader.ajax.get_above_cell(this.grade.ancestor('td'));
+                break;
+            case 39:    // Right
+                next = M.gradereport_grader.ajax.get_next_cell(this.grade.ancestor('td'));
+                break;
+            case 40:    // Down
+                next = M.gradereport_grader.ajax.get_below_cell(this.grade.ancestor('td'));
+                break;
+        }
+        this.move_focus(next);
+    }
+    /**
+     * Move the focus to the node
+     * @function M.gradereport_grader.ajax.existingfield.move_focus
+     * @this {M.gradereport_grader.ajax.existingfield}
+     * @param {Y.Node} node
+     */
+    M.gradereport_grader.ajax.existingfield.prototype.move_focus = function(node) {
+        if (node) {
+            var properties = M.gradereport_grader.get_cell_info(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 M.gradereport_grader.ajax.existingfield.has_changed
+     * @this {M.gradereport_grader.ajax.existingfield}
+     */
+    M.gradereport_grader.ajax.existingfield.prototype.has_changed = function() {
+        if (this.editfeedback) {
+            return (this.grade.get('value') !== this.oldgrade.get('value') || this.feedback.get('value') !== this.oldfeedback.get('value'));
+        }
+        return (this.grade.get('value') !== this.oldgrade.get('value'));
+    }
+    /**
+     * Submits any changes and then updates the fields accordingly
+     *
+     * @function M.gradereport_grader.ajax.existingfield.submit
+     * @this {M.gradereport_grader.ajax.existingfield}
+     */
+    M.gradereport_grader.ajax.existingfield.prototype.submit = function() {
+        if (!this.has_changed()) {
+            return;
+        }
+        var properties = M.gradereport_grader.get_cell_info([this.userid,this.itemid]);
+        var values = (function(f){
+            var feedback, oldfeedback = null;
+            if (f.editfeedback) {
+                feedback = f.feedback.get('value');
+                oldfeedback = f.oldfeedback.get('value');
+            }
+            return {
+                editablefeedback : f.editfeedback,
+                grade : f.grade.get('value'),
+                oldgrade : f.oldgrade.get('value'),
+                feedback : feedback,
+                oldfeedback : oldfeedback
+            }
+        })(this);
+        if (values.editablefeedback && values.feedback != values.oldfeedback) {
+            M.gradereport_grader.update_feedback(this.userid, this.itemid, values.feedback);
+        }
+        M.gradereport_grader.ajax.submit(properties, values);
+    }
+    /**
+     * Class used to make an editable text field for grade values + feedback
+     *
+     * @class textfield
+     * @constructor
+     * @this {M.gradereport_grader.ajax.textfield}
+     * @param {Y.Node}
+     */
+    M.gradereport_grader.ajax.textfield = function(node) {
+        this.node = node;
+        this.gradespan = node.one('.gradevalue'),
+        this.inputdiv = Y.Node.create('<div></div>');
+        this.editfeedback = M.gradereport_grader.ajax.showquickfeedback;
+        this.grade = Y.Node.create('<input type="text" class="text" value="" />');
+        this.gradetype = 'value';
+        this.inputdiv.append(this.grade);
+        if (M.gradereport_grader.ajax.showquickfeedback) {
+            this.feedback = Y.Node.create('<input type="text" class="quickfeedback" value="" />');
+            this.inputdiv.append(this.feedback);
+        }
+    };
+    /**
+     * @property {Array} keyevents
+     */
+    M.gradereport_grader.ajax.textfield.prototype.keyevents = [];
+    /**
+     * @property {Bool} editable
+     */
+    M.gradereport_grader.ajax.textfield.prototype.editable = false;
+    /**
+     * Replaces the cell contents with the controls to enable editing
+     * 
+     * @function M.gradereport_grader.ajax.textfield.replace
+     * @this {M.gradereport_grader.ajax.textfield}
+     */
+    M.gradereport_grader.ajax.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);
+        this.grade.focus();
+        this.editable = true;
+        return this;
+    }
+    /**
+     * Commits the changes within a cell and returns a result object of new + old values
+     * @function M.gradereport_grader.ajax.textfield.commit
+     * @this {M.gradereport_grader.ajax.textfield}
+     * @return {Object}
+     */
+    M.gradereport_grader.ajax.textfield.prototype.commit = function() {
+        // Produce an anonymous result object contianing all values
+        var result = (function(field){
+            field.editable = false;
+            var oldgrade = field.get_grade();
+            if (oldgrade == '-') {
+                oldgrade = '';
+            }
+            var feedback, oldfeedback = null
+            if (field.editfeedback) {
+                oldfeedback = field.get_feedback();
+            }
+            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 M.gradereport_grader.ajax.textfield.revert
+     * @this {M.gradereport_grader.ajax.textfield}
+     */
+    M.gradereport_grader.ajax.textfield.prototype.revert = function() {
+        this.node.replaceChild(this.gradespan, this.inputdiv);
+        for (var i in this.keyevents) {
+            this.keyevents[i].detach();
+        }
+        this.keyevents = [];
+        this.node.on('makeditable|click', M.gradereport_grader.ajax.make_editable, M.gradereport_grader.ajax);
+    },
+    /**
+     * Gets the grade for current cell
+     * 
+     * @function M.gradereport_grader.ajax.textfield.get_grade
+     * @this {M.gradereport_grader.ajax.textfield}
+     * @return {Mixed}
+     */
+    M.gradereport_grader.ajax.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 M.gradereport_grader.ajax.textfield.set_grade
+     * @this {M.gradereport_grader.ajax.textfield}
+     * @param {Mixed} value
+     */
+    M.gradereport_grader.ajax.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 M.gradereport_grader.ajax.textfield.get_feedback
+     * @this {M.gradereport_grader.ajax.textfield}
+     * @return {String}
+     */
+    M.gradereport_grader.ajax.textfield.prototype.get_feedback = function() {
+        if (this.editable) {
+            return this.feedback.get('value');
+        }
+        var properties = M.gradereport_grader.get_cell_info(this.node);
+        if (properties) {
+            return properties.feedback || '';
+        }
+        return '';
+    }
+    /**
+     * Sets the feedback for the current cell
+     * @function M.gradereport_grader.ajax.textfield.set_feedback
+     * @this {M.gradereport_grader.ajax.textfield}
+     * @param {Mixed} value
+     */
+    M.gradereport_grader.ajax.textfield.prototype.set_feedback = function(value) {
+        if (!this.editable) {
+            this.feedback.set('value', value);
+        } else {
+            var properties = M.gradereport_grader.get_cell_info(this.node);
+            M.gradereport_grader.update_feedback(properties.userid, properties.itemid, value);
+        }
+    }
+    /**
+     * Checks if the current cell has changed at all
+     * @function M.gradereport_grader.ajax.textfield.has_changed
+     * @this {M.gradereport_grader.ajax.textfield}
+     * @return {Bool}
+     */
+    M.gradereport_grader.ajax.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 = M.gradereport_grader.get_cell_info(this.node);
+            return (this.get_grade() != this.gradespan.get('innerHTML') || this.get_feedback() != properties.feedback)
+        }
+        return (this.get_grade() != this.gradespan.get('innerHTML'));
+    }
+    /**
+     * Attaches the key listeners for the editable fields and stored the event references
+     * against the textfield
+     * 
+     * @function M.gradereport_grader.ajax.textfield.attach_key_events
+     * @this {M.gradereport_grader.ajax.textfield}
+     */
+    M.gradereport_grader.ajax.textfield.prototype.attach_key_events = function() {
+        var a = M.gradereport_grader.ajax;
+        // Setup the default key events for tab and enter
+        if (this.editfeedback) {
+            this.keyevents.push(Y.on('key', a.keypress_tab, this.grade, 'press:9+shift', a));               // Handle Shift+Tab
+            this.keyevents.push(Y.on('key', a.keypress_tab, this.feedback, 'press:9', a, true));            // Handle Tab
+            this.keyevents.push(Y.on('key', a.keypress_enter, this.feedback, 'press:13', a));               // Handle the Enter key being pressed
+        } else {
+            this.keyevents.push(Y.on('key', a.keypress_tab, this.grade, 'press:9', a));                     // Handle Tab and Shift+Tab
+        }
+        this.keyevents.push(Y.on('key', a.keypress_enter, this.grade, 'press:13', a));                      // Handle the Enter key being pressed
+        // Setup the arrow key events
+        this.keyevents.push(Y.on('key', a.keypress_arrows, this.grade.ancestor('td'), 'down:37,38,39,40+ctrl', a));       // Handle CTRL + arrow keys
+        // 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(Y.on('key', function(e){e.preventDefault();}, this.grade, 'down:37,38,39,40+ctrl'));
+        this.keyevents.push(Y.on('key', function(e){e.preventDefault();}, this.grade, 'press:37,38,39,40+ctrl'));
+        this.keyevents.push(Y.on('key', function(e){e.preventDefault();}, this.grade, 'up:37,38,39,40+ctrl'));
+    }
+    /**
+     * An editable scale field
+     * @class scalefield
+     * @constructor
+     * @inherits M.gradereport_grader.ajax.textfield
+     * @base M.gradereport_grader.ajax.textfield
+     * @this {M.gradereport_grader.ajax.scalefield}
+     */
+    M.gradereport_grader.ajax.scalefield = function(node) {
+        this.node = node;
+        this.gradespan = node.one('.gradevalue'),
+        this.inputdiv = Y.Node.create('<div></div>');
+        this.editfeedback = M.gradereport_grader.ajax.showquickfeedback;
+        this.grade = Y.Node.create('<select type="text" class="text" /><option value="-1">'+M.str.gradereport_grader.ajaxchoosescale+'</option></select>');
+        this.gradetype = 'scale';
+        this.inputdiv.append(this.grade);
+        if (this.editfeedback) {
+            this.feedback = Y.Node.create('<input type="text" class="quickfeedback" value="" />');
+            this.inputdiv.append(this.feedback);
+        }
+        var properties = M.gradereport_grader.get_cell_info(node);
+        this.scale = M.gradereport_grader.ajax.scales[properties.itemscale];
+        for (var i in this.scale) {
+            this.grade.append(Y.Node.create('<option value="'+(parseFloat(i)+1)+'">'+this.scale[i]+'</option>'));
+        }
+    };
+    /**
+     * @property {Array} scale
+     */
+    M.gradereport_grader.ajax.scalefield.prototype.scale = [];
+    /**
+     * Extend the scalefield with the functions from the textfield
+     */
+    Y.extend(M.gradereport_grader.ajax.scalefield, M.gradereport_grader.ajax.textfield);
+    /**
+     * Overrides the get_grade function so that it can pick up the value from the
+     * scales select box
+     * 
+     * @function M.gradereport_grader.ajax.scalefield.get_grade
+     * @this {M.gradereport_grader.ajax.scalefield}
+     * return {Int} the scale id
+     */
+    M.gradereport_grader.ajax.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 M.gradereport_grader.ajax.scalefield.set_grade
+     * @this {M.gradereport_grader.ajax.scalefield}
+     */
+    M.gradereport_grader.ajax.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 M.gradereport_grader.ajax.scalefield.has_changed
+     * @this {M.gradereport_grader.ajax.scalefield}
+     * @return {Bool}
+     */
+    M.gradereport_grader.ajax.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 = M.gradereport_grader.get_cell_info(this.node);
+            var feedback = properties.feedback || '';
+            return (gradef != gradec || this.get_feedback() != feedback)
+        }
+        return (gradef != gradec);
+    }
+
+}, '2.0.0', {requires:['base', 'dom', 'event', 'event-mouseenter', 'event-key', 'io', 'json-parse', 'overlay']});
Index: moodle/grade/report/grader/styles.css
--- moodle/grade/report/grader/styles.css No Base Revision
+++ moodle/grade/report/grader/styles.css Locally New
@@ -0,0 +1,624 @@
+.flexible th {
+white-space:normal;
+}
+
+.gradestable th.user img {
+width:20px;
+height:20px;
+}
+
+.grade-report-grader table#user-grades .catlevel2 {
+background-color:#f9f9f9;
+}
+
+.grade-report-grader table#user-grades tr.avg td.cell {
+background-color:#efefff;
+font-weight:700;
+color:#00008B;
+}
+
+.grade-report-grader table#user-grades tr.odd td.cell {
+background-color:#efefef;
+white-space:nowrap;
+}
+
+.grade-report-grader table#user-grades tr td.overridden {background-color:#F3E4C0;}
+.grade-report-grader table#user-grades tr.odd td.overridden {background-color:#EFD9A4;}
+
+.grade-report-grader table#user-grades tr td.ajaxoverridden {background-color:#FFE3A0;}
+.grade-report-grader table#user-grades tr.odd td.ajaxoverridden {background-color:#FFDA83;}
+
+.grade-report-grader table#user-grades tr.even td.excluded {
+background-color:#EABFFF;
+}
+
+.grade-report-grader table#user-grades tr.odd td.excluded {
+background-color:#E5AFFF;
+}
+
+.grade-report-grader table#user-grades tr.odd th.header {
+background-color:#efefef;
+background-image:none;
+}
+
+.grade-report-grader table#user-grades tr.groupavg td.cell {
+background-color:#efffef;
+font-weight:700;
+color:#006400;
+}
+
+.grade-report-grader table#user-grades td.cat,
+.grade-report-grader table#user-grades td.course {
+font-weight:700;
+}
+
+.grade-report-grader table#user-grades {
+font-size:10px;
+width:auto;
+background-color:transparent;
+border-style:solid;
+border-width:1px;
+margin:20px 0 0;
+}
+
+.grade-report-grader #overDiv table {
+margin:0;
+}
+
+.grade-report-grader #overDiv table td.feedback {
+border:0;
+}
+
+.grade-report-grader #overDiv .feedback {
+font-size:70%;
+background-color:#ABF;
+color:#000;
+font-family:Verdana;
+font-weight:400;
+}
+
+.grade-report-grader #overDiv .caption {
+font-size:70%;
+background-color:#56C;
+color:#CCF;
+font-family:Arial;
+font-weight:700;
+}
+
+.grade-report-grader #overDiv .intersection {
+font-size:70%;
+background-color:#ABF;
+color:#000;
+font-family:Verdana;
+font-weight:400;
+}
+
+.grade-report-grader #overDiv .intersectioncaption {
+background-color:#56C;
+color:#CCF;
+font-family:Arial;
+font-weight:700;
+}
+
+.grade-report-grader div.submit {
+margin-top:20px;
+text-align:center;
+}
+
+.grade-report-grader table#user-grades td {
+text-align:right;
+border-style:solid;
+border-width:0 1px 1px 0;
+}
+
+.grade-report-grader table#user-grades th.category {
+vertical-align:top;
+border-style:solid;
+border-width:1px 1px 0;
+}
+
+.grade-report-grader table#user-grades th.user {
+text-align:left;
+border-style:solid;
+border-width:0 0 1px;
+}
+
+.grade-report-grader table#user-grades th.useridnumber {
+border-style:solid;
+border-width:0 0 1px 1px;
+}
+
+.grade-report-grader table#user-grades th.categoryitem,
+.grade-report-grader table#user-grades th.courseitem,
+.grade-report-grader table#user-grades td.topleft {
+vertical-align:top;
+border-style:solid;
+border-width:0 1px;
+}
+
+.grade-report-grader td,.grade-report-grader th {
+border-color:#CECECE;
+}
+
+.grade-report-grader table#participants th {
+vertical-align:top;
+width:auto;
+}
+
+.grade-report-grader table#user-grades td.fillerfirst {
+border-style:solid;
+border-width:0 0 0 1px;
+}
+
+.grade-report-grader table#user-grades td.fillerlast {
+border-style:solid;
+border-width:0 1px 0 0;
+}
+
+.grade-report-grader table#user-grades th.item ,
+.grade-report-grader table#user-grades th.categoryitem ,
+.grade-report-grader table#user-grades th.courseitem {
+border-bottom-color:#000;
+vertical-align:top;
+border-style:solid;
+border-width:1px;
+}
+
+.grade-report-grader div.gradertoggle {
+display:inline;
+margin-left:20px;
+}
+
+.grade-report-grader table#user-grades th.range {
+text-align:right;
+border-style:solid;
+border-width:1px;
+}
+
+.grade-report-grader table#user-grades .userpic {
+display:inline;
+margin-right:10px;
+}
+
+.grade-report-grader table#user-grades .quickfeedback {
+border:#000 1px dashed;
+}
+
+.grade-report-grader #siteconfiglink {
+text-align:right;
+}
+
+.grade-report-grader table#user-grades .hidden,
+.grade-report-grader table#user-grades .hidden a {
+color:#aaa;
+}
+
+.grade-report-grader table#user-grades .datesubmitted {
+font-size:.7em;
+}
+
+.grade-report-grader table#user-grades td.cell {
+padding-left:5px;
+padding-right:5px;
+vertical-align:middle;
+}
+
+.grade-report-grader table {
+border-collapse:collapse;
+background-color:#fff;
+border-color:#cecece;
+}
+
+.grade-report-grader th {
+padding:2px 10px 0;
+}
+
+.grade-report-grader span.inclusion-links {
+margin:0 5px 0 10px;
+}
+
+.grade-report-grader table#user-grades .item {
+background-color:#e9e9e9;
+}
+
+.grade-report-grader table tr.odd th.header {
+background-color:#efefef;
+background-image:none;
+border-width:0 0 1px;
+}
+
+.grade-report-grader table tr.heading th.header {
+border-top:1px solid #cecece;
+}
+
+.grade-report-grader table#user-grades tr.heading th.categoryitem,
+.grade-report-grader table#user-grades tr.heading th.courseitem {
+border-width:0 0 0 1px;
+}
+
+.grade-report-grader table#user-grades th.category.header.catlevel1 {
+vertical-align:top;
+border-style:solid;
+border-width:1px 1px 0 0;
+}
+
+.grade-report-grader div.left_scroller th.user a {
+vertical-align:middle;
+margin:0;
+padding:0;
+}
+
+.grade-report-grader table#user-grades th.categoryitem,
+.grade-report-grader table#user-grades th.courseitem,
+.grade-report-grader table td.topleft {
+vertical-align:top;
+border-color:#cecece #cecece #000;
+border-style:solid;
+border-width:0 1px 1px;
+}
+
+.grade-report-grader table td.topleft {
+border-bottom:none;
+}
+
+.grade-report-grader table#user-grades td.topleft {
+background-color:#fff;
+}
+
+.grade-report-grader div.userpic {
+margin-right:10px;
+float:left;
+}
+
+.grade-report-grader div.userpic img {
+border:3px double #cecece;
+vertical-align:middle;
+width:2.7em;
+height:2.7em;
+}
+
+.grade-report-grader a.quickedit {
+line-height:1em;
+display:block;
+float:right;
+clear:none;
+font-size:9px;
+background-color:transparent;
+margin:.1em 0 0;
+}
+
+.grade-report-grader a.quickedit2 {
+display:block;
+float:right;
+clear:none;
+background-color:transparent;
+margin:1.3em 0 0;
+}
+
+.grade-report-grader table#quick_edit {
+border:1px solid #cecece;
+margin:0 auto;
+}
+
+.grade-report-grader table#quick_edit td {
+vertical-align:middle;
+border:1px solid #cecece;
+text-align:left;
+margin:0;
+padding:5px;
+}
+
+.grade-report-grader table#quick_edit td img {
+border:3px double #cecece;
+vertical-align:middle;
+padding:0;
+}
+
+.grade-report-grader td input.text {
+border:1px solid #666;
+margin-left:10px;
+margin-right:10px;
+}
+
+.grade-report-grader td input.submit {
+margin: 10px 10px 0px 10px;
+}
+
+.grade-report-grader table#quick_edit td.fullname {
+border-left:none;
+padding-left:5px;
+}
+
+.grade-report-grader table#quick_edit td.picture {
+border-right:none;
+}
+
+.grade-report-grader table#quick_edit td.finalgrade input {
+width:5em;
+}
+
+.grade-report-grader h1 {
+text-align:center;
+clear:both;
+}
+
+.grade-report-grader form {
+text-align:center;
+}
+
+.grade-report-grader input.center {
+margin:10px auto 0;
+}
+
+.grade-report-grader .lefttbody {
+width:auto;
+vertical-align:middle;
+}
+
+.grade-report-grader table#user-grades th.fixedcolumn {
+border:1px solid #cecece;
+vertical-align:middle;
+}
+
+.grade-report-grader table#fixed_column th {
+border:1px solid #cecece;
+vertical-align:middle;
+border-right-color:#000;
+}
+
+.grade-report-grader table#fixed_column th.user{
+border-right-color:#cecece;
+}
+
+.grade-report-grader table#fixed_column {
+padding-top:20px;
+border-top:1px solid #cecece;
+background-color:#fff;
+}
+
+.grade-report-grader .left_scroller {
+float:left;
+clear:none;
+padding-top:20px;
+}
+
+.grade-report-grader .right_scroller {
+width:auto;
+clear:none;
+overflow-x:scroll;
+}
+
+.grade-report-grader table tr.avg,
+.grade-report-grader table tr.groupavg td,
+.grade-report-grader table tr.avg td,
+.grade-report-grader table tr.groupavg th,
+.grade-report-grader table tr.avg th,
+.grade-report-grader table tr.controls_row,
+.grade-report-grader table tr.controls_row th,
+.grade-report-grader table tr.range_row,
+.grade-report-grader table tr.range_row th,
+div.right_scroller tr {
+height:2em;
+}
+
+.grade-report-grader table#user-grades tr.groupavg td.cell,
+.grade-report-grader tr.groupavg th.header {
+background-color:#efffef;
+}
+
+.grade-report-grader form td.excluded {
+color:red;
+}
+
+.grade-report-grader .excludedfloater {
+font-weight:700;
+color:red;
+font-size:9px;
+float:left;
+}
+
+.grade-report-grader span.gradepass {
+color:#298721;
+}
+
+.grade-report-grader span.gradefail {
+color:#890d0d;
+}
+
+.gradeweight {
+color:#461d7c;
+font-weight:700;
+}
+
+.grade-report-grader td select {
+font-size:100%;
+padding:0;
+}
+
+.grade-report-grader .righttest td select {
+font-size:86%;
+padding:0;
+}
+
+.grade-report-grader tr.avg,
+.grade-report-grader tr.controls,
+.grade-report-grader td.controls,
+.grade-report-grader th.controls,
+.grade-report-grader tr.groupavg,
+.grade-report-grader tr.range,
+.grade-report-grader th.range,
+.grade-report-grader td.range,
+.grade-report-grader tr.heading th.range {
+height:2em!important;
+white-space:nowrap;
+}
+
+.heading_name_row th {
+white-space:nowrap;
+width:2000px;
+}
+
+/*MDL-21088 - IE 7 ignores nowraps on tds or ths so we put a span within it with a nowrap on it*/
+heading_name_row th span {
+    white-space:nowrap;
+}
+
+.grade_icons img.ajax {
+float:right;
+}
+
+.gradestable th.user,
+.gradestable th.range,
+.grade-report-grader .flexible th,
+.grade-report-grader .flexible td,
+.grade-report-grader .flexible th a,
+.grade-report-grader .flexible td a,
+.grade-report-grader .gradestable th.range,
+.grade-report-grader td {
+white-space:nowrap;
+}
+
+.grade-report-grader table#user-grades .catlevel1,
+.grade-report-grader .r1,
+.grade-report-grader table tr.even td.cell,
+.grade-report-grader table tr.even th {
+background-color:#fff;
+}
+
+.grade-report-grader table#user-grades .catlevel3,
+.grade-report-grader table tr.odd td.cell {
+background-color:#efefef;
+}
+
+.grade-report-grader table#fixed_column tr.odd th ,
+.grade-report-grader table#user-grades tr.odd th {
+background-color:#efefef;
+}
+
+.grade-report-grader table#user-grades td.vmarked,
+.grade-report-grader table#user-grades tr.odd td.vmarked,
+.grade-report-grader table#user-grades td.vmarked,
+.grade-report-grader table#user-grades tr.odd td.vmarked,
+.grade-report-grader table#user-grades tr.even td.vmarked {
+background-color:#fc3;
+}
+
+.grade-report-grader table#user-grades td.hmarked,
+.grade-report-grader table#user-grades tr.odd td.hmarked,
+.grade-report-grader table#user-grades td.hmarked,
+.grade-report-grader table#user-grades tr.odd td.hmarked,
+.grade-report-grader table#user-grades tr.even td.hmarked {
+background-color:#ff9;
+}
+
+.grade-report-grader table#user-grades td.hmarked.vmarked,
+.grade-report-grader table#user-grades tr.odd td.hmarked.vmarked,
+.grade-report-grader table#user-grades td.hmarked.vmarked,
+.grade-report-grader table#user-grades tr.even td.hmarked.vmarked,
+.grade-report-grader table#user-grades tr.odd td.hmarked.vmarked {
+background-color:#fc9;
+}
+
+.grade-report-grader table#user-grades tr.heading,
+.grade-report-grader table#user-grades .heading td {
+border-style:solid;
+border-width:0;
+}
+
+.grade-report-grader table#user-grades td.useridnumber,
+.grade-report-grader table#user-grades th,
+.grade-report-grader div.gradeparent,
+.ie6 form,.grade-report-grader table#user-grades td.ajax {
+text-align:left;
+}
+
+.grade-report-grader table tr.avg td.cell,
+.grade-report-grader table#user-grades td.controls,
+.grade-report-grader table tr.avg,
+.grade-report-grader table tr.avg td,
+.grade-report-grader table tr.avg th {
+background-color:#f3ead8;
+}
+
+.grade-report-grader div.left_scroller tr,
+.grade-report-grader div.right_scroller tr,
+.grade-report-grader div.left_scroller td,
+.grade-report-grader div.right_scroller td,
+.grade-report-grader div.left_scroller th,
+.grade-report-grader div.right_scroller th {
+height:4.5em;
+font-size:10px;
+}
+
+.grade-report-grader table th.user,
+.grade-report-grader table td.useridnumber {
+text-align:left;
+vertical-align:middle;
+}
+
+.grade-report-grader .yui-overlay {
+    background-color: #FFEE69;
+    border-color: #D4C237 #A6982B #A6982B;
+    border-style: solid;
+    border-width: 1px;
+    left: 0;
+    padding: 2px 5px;
+    font-size: 0.7em;
+}
+
+.grade-report-grader .yui-overlay .fullname {
+    color: #5F3E00;
+    font-weight: bold;
+}
+.grade-report-grader .yui-overlay .itemname {
+    color: #194F3E;
+    font-weight: bold;
+}
+.grade-report-grader .yui-overlay .feedback {
+    color: #5F595E;
+}
+/* .grade-report-grader table#user-grades td */
+/* .grader-report-grader table#user-grades td .yui-panel div.hd { */
+#tooltipPanel {
+  text-align: left;
+}
+
+.grade-report-grader .yui-overlay a.container-close {
+  margin-top: -3px;
+}
+
+#hiddentooltiproot, .tooltipDiv {
+  display: none;
+}
+
+.ie .right_scroller {
+padding-bottom:15px;
+overflow-y:hidden;
+}
+
+.ie table#fixed_column th {
+height:4.5em;
+}
+
+.ie table#fixed_column tr.avg th {
+height:2.1em;
+}
+
+.ie div.left_scroller td {
+height:4.5em;
+}
+
+.ie div.left_scroller {
+margin-top:-0.6em;
+margin-right: -0.6em;
+}
+
+.ie6 div.right_scroller {
+margin-top:4em;
+width:auto;
+position:absolute;
+}
+
+.ie6 .excludedfloater {
+font-size:7px;
+}
Index: moodle/grade/report/grader/styles.php
--- moodle/grade/report/grader/styles.php Base (1.25)
+++ moodle/grade/report/grader/styles.php Locally Deleted
@@ -1,580 +0,0 @@
-.flexible th {
-white-space:normal;
-}
-
-.gradestable th.user img {
-width:20px;
-height:20px;
-}
-
-.grade-report-grader table#user-grades .catlevel2 {
-background-color:#f9f9f9;
-}
-
-.grade-report-grader table#user-grades td.overridden {
-background-color:#ddd;
-}
-
-.grade-report-grader table#user-grades tr.avg td.cell {
-background-color:#efefff;
-font-weight:700;
-color:#00008B;
-}
-
-.grade-report-grader table#user-grades tr.odd td.cell {
-background-color:#efefef;
-white-space:nowrap;
-}
-
-.grade-report-grader table#user-grades tr.even td.overridden {
-background-color:#F3E4C0;
-}
-
-.grade-report-grader table#user-grades tr.odd td.overridden {
-background-color:#EFD9A4;
-}
-
-.grade-report-grader table#user-grades tr.even td.excluded {
-background-color:#EABFFF;
-}
-
-.grade-report-grader table#user-grades tr.odd td.excluded {
-background-color:#E5AFFF;
-}
-
-.grade-report-grader table#user-grades tr.odd th.header {
-background-color:#efefef;
-background-image:none;
-}
-
-.grade-report-grader table#user-grades tr.groupavg td.cell {
-background-color:#efffef;
-font-weight:700;
-color:#006400;
-}
-
-.grade-report-grader table#user-grades td.cat,.grade-report-grader table#user-grades td.course {
-font-weight:700;
-}
-
-.grade-report-grader table#user-grades {
-font-size:10px;
-width:auto;
-background-color:transparent;
-border-style:solid;
-border-width:1px;
-margin:20px 0 0;
-}
-
-.grade-report-grader #overDiv table {
-margin:0;
-}
-
-.grade-report-grader #overDiv table td.feedback {
-border:0;
-}
-
-.grade-report-grader #overDiv .feedback {
-font-size:70%;
-background-color:#ABF;
-color:#000;
-font-family:Verdana;
-font-weight:400;
-}
-
-.grade-report-grader #overDiv .caption {
-font-size:70%;
-background-color:#56C;
-color:#CCF;
-font-family:Arial;
-font-weight:700;
-}
-
-.grade-report-grader #overDiv .intersection {
-font-size:70%;
-background-color:#ABF;
-color:#000;
-font-family:Verdana;
-font-weight:400;
-}
-
-.grade-report-grader #overDiv .intersectioncaption {
-background-color:#56C;
-color:#CCF;
-font-family:Arial;
-font-weight:700;
-}
-
-.grade-report-grader div.submit {
-margin-top:20px;
-text-align:center;
-}
-
-.grade-report-grader table#user-grades td {
-text-align:right;
-border-style:solid;
-border-width:0 1px 1px 0;
-}
-
-.grade-report-grader table#user-grades th.category {
-vertical-align:top;
-border-style:solid;
-border-width:1px 1px 0;
-}
-
-.grade-report-grader table#user-grades th.user {
-text-align:left;
-border-style:solid;
-border-width:0 0 1px;
-}
-
-.grade-report-grader table#user-grades th.useridnumber {
-border-style:solid;
-border-width:0 0 1px 1px;
-}
-
-.grade-report-grader table#user-grades th.categoryitem,.grade-report-grader table#user-grades th.courseitem,.grade-report-grader table#user-grades td.topleft {
-vertical-align:top;
-border-style:solid;
-border-width:0 1px;
-}
-
-/* we don't want 25px height - at all
- * This causes the columns to fall out of line if Static Students Column is enabled
-.grade-report-grader table#user-grades th.category,
-.grade-report-grader table#user-grades th.item,
-.grade-report-grader table#user-grades th.categoryitem,
-.grade-report-grader table#user-grades th.courseitem {
-height: 25px;
-}
-*/
-
-.grade-report-grader td,.grade-report-grader th {
-border-color:#CECECE;
-}
-
-.grade-report-grader table#participants th {
-vertical-align:top;
-width:auto;
-}
-
-.grade-report-grader table#user-grades td.fillerfirst {
-border-style:solid;
-border-width:0 0 0 1px;
-}
-
-.grade-report-grader table#user-grades td.fillerlast {
-border-style:solid;
-border-width:0 1px 0 0;
-}
-
-.grade-report-grader table#user-grades th.item ,
-.grade-report-grader table#user-grades th.categoryitem ,
-.grade-report-grader table#user-grades th.courseitem {
-border-bottom-color:#000;
-vertical-align:top;
-border-style:solid;
-border-width:1px;
-}
-
-.grade-report-grader div.gradertoggle {
-display:inline;
-margin-left:20px;
-}
-
-.grade-report-grader table#user-grades th.range {
-text-align:right;
-border-style:solid;
-border-width:1px;
-}
-
-.grade-report-grader table#user-grades .userpic {
-display:inline;
-margin-right:10px;
-}
-
-.grade-report-grader table#user-grades .quickfeedback {
-border:#000 1px dashed;
-}
-
-.grade-report-grader #siteconfiglink {
-text-align:right;
-}
-
-.grade-report-grader table#user-grades .hidden,.grade-report-grader table#user-grades .hidden a {
-color:#aaa;
-}
-
-.grade-report-grader table#user-grades .datesubmitted {
-font-size:.7em;
-}
-
-.grade-report-grader table#user-grades td.cell {
-padding-left:5px;
-padding-right:5px;
-vertical-align:middle;
-}
-
-.grade-report-grader table {
-border-collapse:collapse;
-background-color:#fff;
-border-color:#cecece;
-}
-
-.grade-report-grader th {
-padding:2px 10px 0;
-}
-
-.grade-report-grader span.inclusion-links {
-margin:0 5px 0 10px;
-}
-
-.grade-report-grader table#user-grades .item {
-background-color:#e9e9e9;
-}
-
-.grade-report-grader table tr.odd th.header {
-background-color:#efefef;
-background-image:none;
-border-width:0 0 1px;
-}
-
-.grade-report-grader table tr.heading th.header {
-border-top:1px solid #cecece;
-}
-
-.grade-report-grader table#user-grades tr.heading th.categoryitem,.grade-report-grader table#user-grades tr.heading th.courseitem {
-border-width:0 0 0 1px;
-}
-
-.grade-report-grader table#user-grades th.category.header.catlevel1 {
-vertical-align:top;
-border-style:solid;
-border-width:1px 1px 0 0;
-}
-
-.grade-report-grader div.left_scroller th.user a {
-vertical-align:middle;
-margin:0;
-padding:0;
-}
-
-.grade-report-grader table#user-grades th.categoryitem,.grade-report-grader table#user-grades th.courseitem,.grade-report-grader table td.topleft {
-vertical-align:top;
-border-color:#cecece #cecece #000;
-border-style:solid;
-border-width:0 1px 1px;
-}
-
-.grade-report-grader table td.topleft {
-border-bottom:none;
-}
-
-.grade-report-grader table#user-grades td.topleft {
-background-color:#fff;
-}
-
-.grade-report-grader div.userpic {
-margin-right:10px;
-float:left;
-}
-
-.grade-report-grader div.userpic img {
-border:3px double #cecece;
-vertical-align:middle;
-width:2.7em;
-height:2.7em;
-}
-
-.grade-report-grader a.quickedit {
-line-height:1em;
-display:block;
-float:right;
-clear:none;
-font-size:9px;
-background-color:transparent;
-margin:.1em 0 0;
-}
-
-.grade-report-grader a.quickedit2 {
-display:block;
-float:right;
-clear:none;
-background-color:transparent;
-margin:1.3em 0 0;
-}
-
-.grade-report-grader table#quick_edit {
-border:1px solid #cecece;
-margin:0 auto;
-}
-
-.grade-report-grader table#quick_edit td {
-vertical-align:middle;
-border:1px solid #cecece;
-text-align:left;
-margin:0;
-padding:5px;
-}
-
-.grade-report-grader table#quick_edit td img {
-border:3px double #cecece;
-vertical-align:middle;
-padding:0;
-}
-
-.grade-report-grader td input.text {
-border:1px solid #666;
-margin-left:10px;
-margin-right:10px;
-}
-
-.grade-report-grader td input.submit {
-margin: 10px 10px 0px 10px;
-}
-
-.grade-report-grader table#quick_edit td.fullname {
-border-left:none;
-padding-left:5px;
-}
-
-.grade-report-grader table#quick_edit td.picture {
-border-right:none;
-}
-
-.grade-report-grader table#quick_edit td.finalgrade input {
-width:5em;
-}
-
-.grade-report-grader h1 {
-text-align:center;
-clear:both;
-}
-
-.grade-report-grader form {
-text-align:center;
-}
-
-.grade-report-grader input.center {
-margin:10px auto 0;
-}
-
-.grade-report-grader .lefttbody {
-width:auto;
-vertical-align:middle;
-}
-
-.grade-report-grader table#user-grades th.fixedcolumn {
-border:1px solid #cecece;
-vertical-align:middle;
-}
-
-.grade-report-grader table#fixed_column th {
-border:1px solid #cecece;
-vertical-align:middle;
-border-right-color:#000;
-}
-
-.grade-report-grader table#fixed_column th.user{
-border-right-color:#cecece;
-}
-
-.grade-report-grader table#fixed_column {
-padding-top:20px;
-border-top:1px solid #cecece;
-background-color:#fff;
-}
-
-.grade-report-grader .left_scroller {
-float:left;
-clear:none;
-padding-top:20px;
-}
-
-.grade-report-grader .right_scroller {
-width:auto;
-clear:none;
-overflow-x:scroll;
-}
-
-.grade-report-grader table tr.avg,.grade-report-grader table tr.groupavg td,.grade-report-grader table tr.avg td,.grade-report-grader table tr.groupavg th,.grade-report-grader table tr.avg th,.grade-report-grader table tr.controls_row,.grade-report-grader table tr.controls_row th,.grade-report-grader table tr.range_row,.grade-report-grader table tr.range_row th,div.right_scroller tr {
-height:2em;
-}
-
-.grade-report-grader table#user-grades tr.groupavg td.cell,.grade-report-grader tr.groupavg th.header {
-background-color:#efffef;
-}
-
-.grade-report-grader form td.excluded {
-color:red;
-}
-
-.grade-report-grader .excludedfloater {
-font-weight:700;
-color:red;
-font-size:9px;
-float:left;
-}
-
-.grade-report-grader span.gradepass {
-color:#298721;
-}
-
-.grade-report-grader span.gradefail {
-color:#890d0d;
-}
-
-.gradeweight {
-color:#461d7c;
-font-weight:700;
-}
-
-.grade-report-grader td select {
-font-size:100%;
-padding:0;
-}
-
-.grade-report-grader .righttest td select {
-font-size:86%;
-padding:0;
-}
-
-.grade-report-grader tr.avg,tr.controls,td.controls,th.controls,.grade-report-grader tr.groupavg,tr.range,th.range,td.range,tr.heading th.range {
-height:2em!important;
-white-space:nowrap;
-}
-
-.heading_name_row th {
-white-space:nowrap;
-width:2000px;
-}
-
-/*MDL-21088 - IE 7 ignores nowraps on tds or ths so we put a span within it with a nowrap on it*/
-heading_name_row th span {
-    white-space:nowrap;
-}
-
-.grade_icons img.ajax {
-float:right;
-}
-
-.gradestable th.user,.gradestable th.range,.grade-report-grader .flexible th,.grade-report-grader .flexible td,.grade-report-grader .flexible th a,.grade-report-grader .flexible td a,.grade-report-grader .gradestable th.range,.grade-report-grader td {
-white-space:nowrap;
-}
-
-.grade-report-grader table#user-grades .catlevel1,.grade-report-grader .r1,.grade-report-grader table tr.even td.cell,.grade-report-grader table tr.even th {
-background-color:#fff;
-}
-
-.grade-report-grader table#user-grades .catlevel3,.grade-report-grader table tr.odd td.cell {
-background-color:#efefef;
-}
-
-.grade-report-grader table#fixed_column tr.odd th ,
-.grade-report-grader table#user-grades tr.odd th {
-background-color:#efefef;
-}
-
-.grade-report-grader table#user-grades td.vmarked,.grade-report-grader table#user-grades tr.odd td.vmarked,.grade-report-grader table#user-grades td.vmarked,.grade-report-grader table#user-grades tr.odd td.vmarked,.grade-report-grader table#user-grades tr.even td.vmarked {
-background-color:#fc3;
-}
-
-.grade-report-grader table#user-grades td.hmarked,.grade-report-grader table#user-grades tr.odd td.hmarked,.grade-report-grader table#user-grades td.hmarked,.grade-report-grader table#user-grades tr.odd td.hmarked,.grade-report-grader table#user-grades tr.even td.hmarked {
-background-color:#ff9;
-}
-
-.grade-report-grader table#user-grades td.hmarked.vmarked,.grade-report-grader table#user-grades tr.odd td.hmarked.vmarked,.grade-report-grader table#user-grades td.hmarked.vmarked,.grade-report-grader table#user-grades tr.even td.hmarked.vmarked,.grade-report-grader table#user-grades tr.odd td.hmarked.vmarked {
-background-color:#fc9;
-}
-
-.grade-report-grader table#user-grades tr.heading,.grade-report-grader table#user-grades .heading td {
-border-style:solid;
-border-width:0;
-}
-
-.grade-report-grader table#user-grades td.useridnumber,.grade-report-grader table#user-grades th,.grade-report-grader div.gradeparent,.ie6 form,.grade-report-grader table#user-grades td.ajax {
-text-align:left;
-}
-
-.grade-report-grader table tr.avg td.cell,.grade-report-grader table#user-grades td.controls,.grade-report-grader table tr.avg,.grade-report-grader table tr.avg td,.grade-report-grader table tr.avg th {
-background-color:#f3ead8;
-}
-
-.grade-report-grader div.left_scroller tr,.grade-report-grader div.right_scroller tr,.grade-report-grader div.left_scroller td,.grade-report-grader div.right_scroller td,.grade-report-grader div.left_scroller th,.grade-report-grader div.right_scroller th {
-height:4.5em;
-font-size:10px;
-}
-
-.grade-report-grader table th.user,.grade-report-grader table td.useridnumber {
-text-align:left;
-vertical-align:middle;
-}
-
-/*
-.grade-report-grader .yui-overlay {
-    background-color: #FFEE69;
-    border-color: #D4C237 #A6982B #A6982B;
-    border-style: solid;
-    border-width: 1px;
-    left: 0;
-    padding: 2px 5px;
-    font-size: 0.7em;
-}
-*/
-
-.grade-report-grader .yui-overlay .fullname {
-    color: #5F3E00;
-    font-weight: bold;
-}
-.grade-report-grader .yui-overlay .itemname {
-    color: #194F3E;
-    font-weight: bold;
-}
-.grade-report-grader .yui-overlay .feedback {
-    color: #5F595E;
-}
-/* .grade-report-grader table#user-grades td */
-/* .grader-report-grader table#user-grades td .yui-panel div.hd { */
-#tooltipPanel {
-  text-align: left;
-}
-
-.grade-report-grader .yui-overlay a.container-close {
-  margin-top: -3px;
-}
-
-#hiddentooltiproot, .tooltipDiv {
-  display: none;
-}
-
-.ie .right_scroller {
-padding-bottom:15px;
-overflow-y:hidden;
-}
-
-.ie table#fixed_column th {
-height:4.5em;
-}
-
-.ie table#fixed_column tr.avg th {
-height:2.1em;
-}
-
-.ie div.left_scroller td {
-height:4.5em;
-}
-
-.ie div.left_scroller {
-margin-top:-0.6em;
-margin-right: -0.6em;
-}
-
-.ie6 div.right_scroller {
-margin-top:4em;
-width:auto;
-position:absolute;
-}
-
-.ie6 .excludedfloater {
-font-size:7px;
-}
Index: moodle/lang/en_utf8/gradereport_grader.php
--- moodle/lang/en_utf8/gradereport_grader.php Base (1.3)
+++ moodle/lang/en_utf8/gradereport_grader.php Locally Modified (Based On 1.3)
@@ -1,5 +1,10 @@
-<?PHP // $Id: gradereport_grader.php,v 1.3 2009/02/09 10:49:42 nicolasconnault Exp $
+<?php
 
+$string['ajaxchoosescale'] = 'Choose';
+$string['ajaxclicktoclose'] = 'Click this box to remove it';
+$string['ajaxerror'] = 'Error';
+$string['ajaxfailedupdate'] = 'Unable to update [1] for [2]';
+$string['ajaxfieldchanged'] = 'The field you are currently editing has changed, would you like to use the updated value?';
 $string['modulename'] = 'Grader report';
 $string['grader:manage'] = 'Manage the grader report';
 $string['grader:view'] = 'View the grader report';
Index: moodle/lib/javascript-static.js
--- moodle/lib/javascript-static.js Base (1.138)
+++ moodle/lib/javascript-static.js Locally Modified (Based On 1.138)
@@ -46,6 +46,16 @@
     UFO.create(FO, eid);
 }
 
+
+M.util.in_array = function(item, array){
+    for( var i = 0; i<array.length; i++){
+        if(item==array[i]){
+            return true;
+        }
+    }
+    return false;
+}
+
 /**
  * Init a collapsible region, see print_collapsible_region in weblib.php
  * @param {YUI} Y YUI3 instance with all libraries loaded
Index: moodle/lib/outputrenderers.php
--- moodle/lib/outputrenderers.php Base (1.145)
+++ moodle/lib/outputrenderers.php Locally Modified (Based On 1.145)
@@ -2008,10 +2008,12 @@
                         if (!($cell instanceof html_table_cell)) {
                             $mycell = new html_table_cell();
                             $mycell->text = $cell;
-                            $this->prepare_event_handlers($mycell);
                             $cell = $mycell;
                         }
 
+                        // Prepare all events handlers for this cell
+                        $this->prepare_event_handlers($cell);
+
                         if (isset($table->colclasses[$key])) {
                             $cell->add_classes(array_unique(html_component::clean_classes($table->colclasses[$key])));
                         }
@@ -2060,7 +2062,7 @@
      * @param string $id The target name from the corresponding $PAGE->requires->skip_link_to($target) call.
      * @return string the HTML to output.
      */
-    public function skip_link_target($id = '') {
+    public function skip_link_target($id = null) {
         return html_writer::tag('span', array('id' => $id), '');
     }
 
@@ -2072,7 +2074,7 @@
      * @param string $id An optional ID
      * @return string the HTML to output.
      */
-    public function heading($text, $level = 2, $classes = 'main', $id = '') {
+    public function heading($text, $level = 2, $classes = 'main', $id = null) {
         $level = (integer) $level;
         if ($level < 1 or $level > 6) {
             throw new coding_exception('Heading level must be an integer between 1 and 6.');
@@ -2088,7 +2090,7 @@
      * @param string $id An optional ID
      * @return string the HTML to output.
      */
-    public function box($contents, $classes = 'generalbox', $id = '') {
+    public function box($contents, $classes = 'generalbox', $id = null) {
         return $this->box_start($classes, $id) . $contents . $this->box_end();
     }
 
@@ -2098,7 +2100,7 @@
      * @param string $id An optional ID
      * @return string the HTML to output.
      */
-    public function box_start($classes = 'generalbox', $id = '') {
+    public function box_start($classes = 'generalbox', $id = null) {
         $this->opencontainers->push('box', html_writer::end_tag('div'));
         return html_writer::start_tag('div', array('id' => $id,
                 'class' => 'box ' . renderer_base::prepare_classes($classes)));
@@ -2119,7 +2121,7 @@
      * @param string $id An optional ID
      * @return string the HTML to output.
      */
-    public function container($contents, $classes = '', $id = '') {
+    public function container($contents, $classes = null, $id = null) {
         return $this->container_start($classes, $id) . $contents . $this->container_end();
     }
 
@@ -2129,7 +2131,7 @@
      * @param string $id An optional ID
      * @return string the HTML to output.
      */
-    public function container_start($classes = '', $id = '') {
+    public function container_start($classes = null, $id = null) {
         $this->opencontainers->push('container', html_writer::end_tag('div'));
         return html_writer::start_tag('div', array('id' => $id,
                 'class' => renderer_base::prepare_classes($classes)));
@@ -2286,7 +2288,7 @@
      * @param string $id An optional ID
      * @return string A template fragment for a heading
      */
-    public function heading($text, $level, $classes = 'main', $id = '') {
+    public function heading($text, $level, $classes = 'main', $id = null) {
         $text .= "\n";
         switch ($level) {
             case 1:
