diff --git a/grade/report/grader/amd/build/stickycolspan.min.js b/grade/report/grader/amd/build/stickycolspan.min.js
new file mode 100644
index 00000000000..3de06ec6408
--- /dev/null
+++ b/grade/report/grader/amd/build/stickycolspan.min.js
@@ -0,0 +1,2 @@
+define ("gradereport_grader/stickycolspan",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;var b={GRADEPARENT:".gradeparent",STUDENTHEADER:"#studentheader",TABLEHEADER:"th.header",BEHAT:"body.behat-site"};a.init=function init(){if(document.querySelector(b.BEHAT)){return}var a=document.querySelector(b.GRADEPARENT),c=a.querySelector(b.STUDENTHEADER),d=getComputedStyle(c).getPropertyValue("left"),e=getComputedStyle(c).getPropertyValue("right");a.querySelectorAll(b.TABLEHEADER).forEach(function(a){if(1.\n\n/**\n * Javascript module for fixing the position of sticky headers with multiple colspans\n *\n * @module gradereport_grader/stickycolspan\n * @copyright 2022 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst SELECTORS = {\n GRADEPARENT: '.gradeparent',\n STUDENTHEADER: '#studentheader',\n TABLEHEADER: 'th.header',\n BEHAT: 'body.behat-site'\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n if (document.querySelector(SELECTORS.BEHAT)) {\n return;\n }\n const grader = document.querySelector(SELECTORS.GRADEPARENT);\n const studentHeader = grader.querySelector(SELECTORS.STUDENTHEADER);\n const leftOffset = getComputedStyle(studentHeader).getPropertyValue('left');\n const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right');\n\n grader.querySelectorAll(SELECTORS.TABLEHEADER).forEach((tableHeader) => {\n if (tableHeader.colSpan > 1) {\n const addOffset = (tableHeader.offsetWidth - studentHeader.offsetWidth);\n if (window.right_to_left()) {\n tableHeader.style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )';\n } else {\n tableHeader.style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )';\n }\n }\n });\n};\n"],"file":"stickycolspan.min.js"}
\ No newline at end of file
diff --git a/grade/report/grader/amd/src/stickycolspan.js b/grade/report/grader/amd/src/stickycolspan.js
new file mode 100644
index 00000000000..61a251025a1
--- /dev/null
+++ b/grade/report/grader/amd/src/stickycolspan.js
@@ -0,0 +1,53 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
+
+/**
+ * Javascript module for fixing the position of sticky headers with multiple colspans
+ *
+ * @module gradereport_grader/stickycolspan
+ * @copyright 2022 Bas Brands
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+const SELECTORS = {
+ GRADEPARENT: '.gradeparent',
+ STUDENTHEADER: '#studentheader',
+ TABLEHEADER: 'th.header',
+ BEHAT: 'body.behat-site'
+};
+
+/**
+ * Initialize module
+ */
+export const init = () => {
+ if (document.querySelector(SELECTORS.BEHAT)) {
+ return;
+ }
+ const grader = document.querySelector(SELECTORS.GRADEPARENT);
+ const studentHeader = grader.querySelector(SELECTORS.STUDENTHEADER);
+ const leftOffset = getComputedStyle(studentHeader).getPropertyValue('left');
+ const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right');
+
+ grader.querySelectorAll(SELECTORS.TABLEHEADER).forEach((tableHeader) => {
+ if (tableHeader.colSpan > 1) {
+ const addOffset = (tableHeader.offsetWidth - studentHeader.offsetWidth);
+ if (window.right_to_left()) {
+ tableHeader.style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )';
+ } else {
+ tableHeader.style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )';
+ }
+ }
+ });
+};
diff --git a/grade/report/grader/index.php b/grade/report/grader/index.php
index 526b2e40334..9068c79e7b9 100644
--- a/grade/report/grader/index.php
+++ b/grade/report/grader/index.php
@@ -44,8 +44,8 @@ $graderreportsifirst = optional_param('sifirst', null, PARAM_NOTAGS);
$graderreportsilast = optional_param('silast', null, PARAM_NOTAGS);
$PAGE->set_url(new moodle_url('/grade/report/grader/index.php', array('id'=>$courseid)));
-$PAGE->requires->yui_module('moodle-gradereport_grader-gradereporttable', 'Y.M.gradereport_grader.init', null, null, true);
$PAGE->set_pagelayout('report');
+$PAGE->requires->js_call_amd('gradereport_grader/stickycolspan', 'init');
// basic access checks
if (!$course = $DB->get_record('course', array('id' => $courseid))) {
diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php
index a05fe1fffce..0ea931b342a 100644
--- a/grade/report/grader/lib.php
+++ b/grade/report/grader/lib.php
@@ -682,16 +682,17 @@ class grade_report_grader extends grade_report {
$studentheader->scope = 'col';
$studentheader->header = true;
$studentheader->id = 'studentheader';
- if ($hasuserreportcell) {
- $studentheader->colspan = 2;
- }
$studentheader->text = $arrows['studentname'];
-
$headerrow->cells[] = $studentheader;
+ if ($hasuserreportcell) {
+ $emptyheader = new html_table_cell();
+ $headerrow->cells[] = $emptyheader;
+ }
+
foreach ($extrafields as $field) {
$fieldheader = new html_table_cell();
- $fieldheader->attributes['class'] = 'header userfield user' . $field;
+ $fieldheader->attributes['class'] = 'userfield user' . $field;
$fieldheader->scope = 'col';
$fieldheader->header = true;
$fieldheader->text = $arrows[$field];
@@ -1305,10 +1306,11 @@ class grade_report_grader extends grade_report {
$controlsrow->attributes['class'] = 'controls';
$controlscell = new html_table_cell();
$controlscell->attributes['class'] = 'header controls';
+ $controlscell->header = true;
$controlscell->colspan = $colspan;
$controlscell->text = $this->get_lang_string('controls', 'grades');
-
$controlsrow->cells[] = $controlscell;
+
$rows[] = $controlsrow;
}
return $rows;
diff --git a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js
deleted file mode 100644
index 568a51819dc..00000000000
--- a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js
+++ /dev/null
@@ -1,1174 +0,0 @@
-YUI.add('moodle-gradereport_grader-gradereporttable', function (Y, NAME) {
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see .
-/* eslint-disable no-unused-vars */
-
-/**
- * Grader Report Functionality.
- *
- * @module moodle-gradereport_grader-gradereporttable
- * @package gradereport_grader
- * @copyright 2014 UC Regents
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @author Alfonso Roman
- */
-
-/**
- * @module moodle-gradereport_grader-gradereporttable
- */
-
-var SELECTORS = {
- FOOTERTITLE: '.avg .header',
- FOOTERCELLS: '#user-grades .avg .cell',
- FOOTERROW: '#user-grades .avg',
- GRADECELL: 'td.grade',
- GRADERTABLE: '.gradeparent table',
- GRADEPARENT: '.gradeparent',
- HEADERCELLS: '#user-grades .heading .cell',
- HEADERCELL: '.gradebook-header-cell',
- HEADERROW: '#user-grades tr.heading',
- STUDENTHEADER: '#studentheader',
- USERCELL: '#user-grades .user.cell',
- USERMAIL: '#user-grades .useremail'
- },
- CSS = {
- OVERRIDDEN: 'overridden',
- TOOLTIPACTIVE: 'tooltipactive'
- };
-
-/**
- * The Grader Report Table.
- *
- * @namespace M.gradereport_grader
- * @class ReportTable
- * @constructor
- */
-function ReportTable() {
- ReportTable.superclass.constructor.apply(this, arguments);
-}
-
-Y.extend(ReportTable, Y.Base, {
- /**
- * Array of EventHandles.
- *
- * @type EventHandle[]
- * @property _eventHandles
- * @protected
- */
- _eventHandles: [],
-
- /**
- * A Node reference to the grader table.
- *
- * @property graderTable
- * @type Node
- */
- graderTable: null,
-
- /**
- * Setup the grader report table.
- *
- * @method initializer
- */
- initializer: function() {
- // Some useful references within our target area.
- this.graderRegion = Y.one(SELECTORS.GRADEPARENT);
- this.graderTable = Y.one(SELECTORS.GRADERTABLE);
-
- // Setup the floating headers.
- this.setupFloatingHeaders();
- },
-
- /**
- * Get the text content of the username for the specified grade item.
- *
- * @method getGradeUserName
- * @param {Node} cell The grade item cell to obtain the username for
- * @return {String} The string content of the username cell.
- */
- getGradeUserName: function(cell) {
- var userrow = cell.ancestor('tr'),
- usercell = userrow.one("th.user .username");
-
- if (usercell) {
- return usercell.get('text');
- } else {
- return '';
- }
- },
-
- /**
- * Get the text content of the item name for the specified grade item.
- *
- * @method getGradeItemName
- * @param {Node} cell The grade item cell to obtain the item name for
- * @return {String} The string content of the item name cell.
- */
- getGradeItemName: function(cell) {
- var itemcell = Y.one("th.item[data-itemid='" + cell.getData('itemid') + "']");
- if (itemcell) {
- return itemcell.get('text');
- } else {
- return '';
- }
- },
-
- /**
- * Get the text content of any feedback associated with the grade item.
- *
- * @method getGradeFeedback
- * @param {Node} cell The grade item cell to obtain the item name for
- * @return {String} The string content of the feedback.
- */
- getGradeFeedback: function(cell) {
- return cell.getData('feedback');
- }
-});
-
-Y.namespace('M.gradereport_grader').ReportTable = ReportTable;
-Y.namespace('M.gradereport_grader').init = function(config) {
- return new Y.M.gradereport_grader.ReportTable(config);
-};
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see .
-
-/**
- * @module moodle-gradereport_grader-gradereporttable
- * @submodule floatingheaders
- */
-
-/**
- * Provides floating headers to the grader report.
- *
- * See {{#crossLink "M.gradereport_grader.ReportTable"}}{{/crossLink}} for details.
- *
- * @namespace M.gradereport_grader
- * @class FloatingHeaders
- */
-
-var HEIGHT = 'height',
- WIDTH = 'width',
- OFFSETWIDTH = 'offsetWidth',
- OFFSETHEIGHT = 'offsetHeight',
- LOGNS = 'moodle-core-grade-report-grader';
-
-CSS.FLOATING = 'floating';
-
-function FloatingHeaders() {}
-
-FloatingHeaders.ATTRS = {
-};
-
-FloatingHeaders.prototype = {
- /**
- * The height of the page header if a fixed position, floating header
- * was found.
- *
- * @property pageHeaderHeight
- * @type Number
- * @default 0
- * @protected
- */
- pageHeaderHeight: 0,
-
- /**
- * A Node representing the container div.
- *
- * Positioning will be based on this element, which must have
- * the CSS rule 'position: relative'.
- *
- * @property container
- * @type Node
- * @protected
- */
- container: null,
-
- /**
- * A Node representing the header cell.
- *
- * @property headerCell
- * @type Node
- * @protected
- */
- headerCell: null,
-
- /**
- * A Node representing the header row.
- *
- * @property headerRow
- * @type Node
- * @protected
- */
- headerRow: null,
-
- /**
- * A Node representing the first cell which contains user name information.
- *
- * @property firstUserCell
- * @type Node
- * @protected
- */
- firstUserCell: null,
-
- /**
- * A Node representing the first cell which does not contain a user header.
- *
- * @property firstNonUserCell
- * @type Node
- * @protected
- */
- firstNonUserCell: null,
-
- /**
- * The position of the left of the first non-header cell in a row - the one after the email address.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstNonUserCellLeft
- * @type Number
- * @protected
- */
- firstNonUserCellLeft: 0,
-
- /**
- * The width of the first non-header cell in a row - the one after the email address.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- * This is only used for RTL calculations.
- *
- * @property firstNonUserCellWidth
- * @type Number
- * @protected
- */
- firstNonUserCellWidth: 0,
-
- /**
- * A Node representing the original table footer row.
- *
- * @property tableFooterRow
- * @type Node
- * @protected
- */
- tableFooterRow: null,
-
- /**
- * A Node representing the floating footer row in the grading table.
- *
- * @property footerRow
- * @type Node
- * @protected
- */
- footerRow: null,
-
- /**
- * A Node representing the floating grade item header.
- *
- * @property gradeItemHeadingContainer
- * @type Node
- * @protected
- */
- gradeItemHeadingContainer: null,
-
- /**
- * A Node representing the floating user header. This is the header with the Surname/First name
- * sorting.
- *
- * @property userColumnHeader
- * @type Node
- * @protected
- */
- userColumnHeader: null,
-
- /**
- * A Node representing the floating user column. This is the column containing all of the user
- * names.
- *
- * @property userColumn
- * @type Node
- * @protected
- */
- userColumn: null,
-
- /**
- * The position of the bottom of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstUserCellBottom
- * @type Number
- * @protected
- */
- firstUserCellBottom: 0,
-
- /**
- * The position of the left of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstUserCellLeft
- * @type Number
- * @protected
- */
- firstUserCellLeft: 0,
-
- /**
- * The width of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- * This is only used for RTL calculations.
- *
- * @property firstUserCellWidth
- * @type Number
- * @protected
- */
- firstUserCellWidth: 0,
-
- /**
- * The width of the dock if it is visible.
- *
- * @property dockWidth
- * @type Number
- * @protected
- */
- dockWidth: 0,
-
- /**
- * The position of the top of the final user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property lastUserCellTop
- * @type Number
- * @protected
- */
- lastUserCellTop: 0,
-
- /**
- * A list of Nodes representing the generic floating rows.
- *
- * @property floatingHeaderRow
- * @type Node{}
- * @protected
- */
- floatingHeaderRow: null,
-
- /**
- * Array of EventHandles.
- *
- * @type EventHandle[]
- * @property _eventHandles
- * @protected
- */
- _eventHandles: [],
-
- /**
- * The last value of the bodyMargin style. We need to recompute positions if it is changed.
- *
- * @property lastBodyMargin
- * @type Number
- * @protected
- */
- lastBodyMargin: 0,
-
- /**
- * Setup the grader report table.
- *
- * @method setupFloatingHeaders
- * @chainable
- */
- setupFloatingHeaders: function() {
- // Grab references to commonly used Nodes.
- this.firstUserCell = Y.one(SELECTORS.USERCELL);
- this.container = Y.one(SELECTORS.GRADEPARENT);
- this.firstNonUserCell = Y.one(SELECTORS.GRADECELL);
-
- if (!this.firstUserCell) {
- // No need for floating elements, there are no users.
- return this;
- }
-
- if (M.cfg.behatsiterunning) {
- // If the behat site is running we don't want floating elements.
- return;
- }
-
- // Generate floating elements.
- this._setupFloatingUserColumn();
- this._setupFloatingUserHeader();
- this._setupFloatingAssignmentHeaders();
- this._setupFloatingAssignmentFooter();
-
- // Setup generic floating left-aligned headers.
- this.floatingHeaderRow = {};
-
- // The 'Controls' row (shown in editing mode when certain options are set).
- this._setupFloatingLeftHeaders('.controls .controls');
-
- // The 'Range' row (shown in editing mode when certain options are set).
- this._setupFloatingLeftHeaders('.range .range');
-
- // The 'Overall Average' field.
- this._setupFloatingLeftHeaders(SELECTORS.FOOTERTITLE);
-
- // Additional setup for the footertitle.
- this._setupFloatingAssignmentFooterTitle();
-
- // Calculate the positions of edge cells. These are used for positioning of the floating headers.
- // This must be called after the floating headers are setup, but before the scroll event handler is invoked.
- this._calculateCellPositions();
-
- // Setup the floating element initial positions by simulating scroll.
- this._handleScrollEvent();
-
- // Setup the event handlers.
- this._setupEventHandlers();
-
- // Listen for a resize event globally - other parts of the code not in this YUI wrapper may make changes to the
- // fields which result in size changes.
- Y.Global.on('moodle-gradereport_grader:resized', this._handleResizeEvent, this);
-
- return this;
- },
-
- /**
- * Calculate the positions of some cells. These values are used heavily
- * in scroll event handling.
- *
- * @method _calculateCellPositions
- * @protected
- */
- _calculateCellPositions: function() {
- // The header row shows the grade item headers and is floated to the top of the window.
- this.headerRowTop = this.headerRow.getY();
-
- // The footer row shows the grade averages and will be floated to the page bottom.
- if (this.tableFooterRow) {
- this.footerRowPosition = this.tableFooterRow.getY();
- }
-
- // Add the width of the dock if it is visible.
- this.dockWidth = 0;
- var dock = Y.one('.has_dock #dock');
- if (dock) {
- this.dockWidth = dock.get(OFFSETWIDTH);
- }
-
- var userCellList = Y.all(SELECTORS.USERCELL);
-
- // The left of the user cells matches the left of the headerRow.
- this.firstUserCellLeft = this.firstUserCell.getX();
- this.firstUserCellWidth = this.firstUserCell.get(OFFSETWIDTH);
-
- // The left of the user cells matches the left of the footer title.
- this.firstNonUserCellLeft = this.firstNonUserCell.getX();
- this.firstNonUserCellWidth = this.firstNonUserCell.get(OFFSETWIDTH);
-
- if (userCellList.size() > 1) {
- // Use the top of the second cell for the bottom of the first cell.
- // This is used when scrolling to fix the footer to the top edge of the window.
- var firstUserCell = userCellList.item(1);
- this.firstUserCellBottom = firstUserCell.getY() + parseInt(firstUserCell.getComputedStyle(HEIGHT), 10);
-
- // Use the top of the penultimate cell when scrolling the header.
- // The header is the same size as the cells.
- this.lastUserCellTop = userCellList.item(userCellList.size() - 2).getY();
- } else {
- var firstItem = userCellList.item(0);
- // We can't use the top of the second row as there is only one row.
- this.lastUserCellTop = firstItem.getY();
-
- if (this.tableFooterRow) {
- // The footer is present so we can use that.
- this.firstUserCellBottom = this.footerRowPosition + parseInt(this.tableFooterRow.getComputedStyle(HEIGHT), 10);
- } else {
- // No other clues - calculate the top instead.
- this.firstUserCellBottom = firstItem.getY() + firstItem.get('offsetHeight');
- }
- }
-
- // Check whether a header is present and whether it is floating.
- var header = Y.one('header');
- this.pageHeaderHeight = 0;
- if (header) {
- if (header.getComputedStyle('position') === 'fixed') {
- this.pageHeaderHeight = header.get(OFFSETHEIGHT);
- } else {
- var navbar = Y.one('.navbar');
-
- if (navbar && navbar.getComputedStyle('position') === 'fixed') {
- // If the navbar exists and isn't fixed, we need to offset the page header to accommodate for it.
- this.pageHeaderHeight = navbar.get(OFFSETHEIGHT);
- }
- }
- }
- },
-
- /**
- * Get the relative XY of the node.
- *
- * @method _getRelativeXY
- * @protected
- * @param {Node} node The node to get the position of.
- * @return {Array} Containing X and Y.
- */
- _getRelativeXY: function(node) {
- return this._getRelativeXYFromXY(node.getX(), node.getY());
- },
-
- /**
- * Get the relative positioning from coordinates.
- *
- * This gives the position according to the parent of the table, which must
- * be set as position: relative.
- *
- * @method _getRelativeXYFromXY
- * @protected
- * @param {Number} x X position.
- * @param {Number} y Y position.
- * @return {Array} Containing X and Y.
- */
- _getRelativeXYFromXY: function(x, y) {
- var parentXY = this.container.getXY();
- return [x - parentXY[0], y - parentXY[1]];
- },
-
- /**
- * Get the relative positioning of an elements from coordinates.
- *
- * @method _getRelativeXFromX
- * @protected
- * @param {Number} pos X position.
- * @return {Number} relative X position.
- */
- _getRelativeXFromX: function(pos) {
- return this._getRelativeXYFromXY(pos, 0)[0];
- },
-
- /**
- * Get the relative positioning of an elements from coordinates.
- *
- * @method _getRelativeYFromY
- * @protected
- * @param {Number} pos Y position.
- * @return {Number} relative Y position.
- */
- _getRelativeYFromY: function(pos) {
- return this._getRelativeXYFromXY(0, pos)[1];
- },
-
- /**
- * Return the size of the horizontal scrollbar.
- *
- * @method _getScrollBarHeight
- * @protected
- * @return {Number} Height of the scrollbar.
- */
- _getScrollBarHeight: function() {
- if (Y.UA.ie && Y.UA.ie >= 10) {
- // IE has transparent scrollbars, which sometimes disappear... it's better to ignore them.
- return 0;
- } else if (Y.config.doc.body.scrollWidth > Y.config.doc.body.clientWidth) {
- // The document can be horizontally scrolled.
- return Y.DOM.getScrollbarWidth();
- }
- return 0;
- },
-
- /**
- * Setup the main event listeners.
- * These deal with things like window events.
- *
- * @method _setupEventHandlers
- * @protected
- */
- _setupEventHandlers: function() {
- this._eventHandles.push(
- // Listen for window scrolls, resizes, and rotation events.
- Y.one(Y.config.win).on('scroll', this._handleScrollEvent, this),
- Y.one(Y.config.win).on('resize', this._handleResizeEvent, this),
- Y.one(Y.config.win).on('orientationchange', this._handleResizeEvent, this),
- Y.Global.on('dock:shown', this._handleResizeEvent, this),
- Y.Global.on('dock:hidden', this._handleResizeEvent, this)
- );
- },
-
- /**
- * Create and setup the floating column of user names.
- *
- * @method _setupFloatingUserColumn
- * @protected
- */
- _setupFloatingUserColumn: function() {
- // Grab all cells in the user names column.
- var userColumn = Y.all(SELECTORS.USERCELL),
-
- // Create a floating table.
- floatingUserColumn = Y.Node.create(''),
-
- // Get the XY for the floating element.
- coordinates = this._getRelativeXY(this.firstUserCell);
-
- // Generate the new fields.
- userColumn.each(function(node) {
- var height = node.getComputedStyle(HEIGHT);
- // Nasty hack to account for Internet Explorer
- if (Y.UA.ie !== 0) {
- var allHeight = node.get('offsetHeight');
- var marginHeight = parseInt(node.getComputedStyle('marginTop'), 10) +
- parseInt(node.getComputedStyle('marginBottom'), 10);
- var paddingHeight = parseInt(node.getComputedStyle('paddingTop'), 10) +
- parseInt(node.getComputedStyle('paddingBottom'), 10);
- var borderHeight = parseInt(node.getComputedStyle('borderTopWidth'), 10) +
- parseInt(node.getComputedStyle('borderBottomWidth'), 10);
- height = allHeight - marginHeight - paddingHeight - borderHeight;
- }
- // Create and configure the new container.
- var containerNode = Y.Node.create('');
- containerNode.set('innerHTML', node.get('innerHTML'))
- .setAttribute('class', node.getAttribute('class'))
- .setAttribute('data-uid', node.ancestor('tr').getData('uid'))
- .setStyles({
- height: height,
- width: node.getComputedStyle(WIDTH)
- });
-
- // Add the new nodes to our floating table.
- floatingUserColumn.appendChild(containerNode);
- }, this);
-
- // Style the floating user container.
- floatingUserColumn.setStyles({
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px'
- });
-
- // Append to the grader region.
- this.graderRegion.append(floatingUserColumn);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.userColumn = floatingUserColumn;
- },
-
- /**
- * Create and setup the floating username header cell.
- *
- * @method _setupFloatingUserHeader
- * @protected
- */
- _setupFloatingUserHeader: function() {
- // We make various references to the header cells. Store it for later.
- this.headerRow = Y.one(SELECTORS.HEADERROW);
- this.headerCell = Y.one(SELECTORS.STUDENTHEADER);
-
- // Create the floating row and cell.
- var floatingUserHeaderRow = Y.Node.create(''),
- floatingUserHeaderCell = Y.Node.create(''),
- nodepos = this._getRelativeXY(this.headerCell)[0],
- coordinates = this._getRelativeXY(this.headerRow),
- gradeHeadersOffset = coordinates[0];
-
- // Append the content and style to the floating cell.
- floatingUserHeaderCell
- .set('innerHTML', this.headerCell.getHTML())
- .setAttribute('class', this.headerCell.getAttribute('class'))
- .setStyles({
- // The header is larger than the user cells, so we take the user cell.
- width: this.firstUserCell.getComputedStyle(WIDTH),
- left: (nodepos - gradeHeadersOffset) + 'px'
- });
-
- // Style the floating row.
- floatingUserHeaderRow
- .setStyles({
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px'
- });
-
- // Append the cell to the row, and finally to the region.
- floatingUserHeaderRow.append(floatingUserHeaderCell);
- this.graderRegion.append(floatingUserHeaderRow);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.userColumnHeader = floatingUserHeaderRow;
- },
-
- /**
- * Create and setup the floating grade item header row.
- *
- * @method _setupFloatingAssignmentHeaders
- * @protected
- */
- _setupFloatingAssignmentHeaders: function() {
- this.headerRow = Y.one('#user-grades tr.heading');
-
- var gradeHeaders = Y.all('#user-grades tr.heading .cell');
-
- // Generate a floating headers
- var floatingGradeHeaders = Y.Node.create('');
-
- var coordinates = this._getRelativeXY(this.headerRow);
-
- var floatingGradeHeadersWidth = 0;
- var floatingGradeHeadersHeight = 0;
- var gradeHeadersOffset = coordinates[0];
-
- gradeHeaders.each(function(node) {
- var nodepos = this._getRelativeXY(node)[0];
-
- var newnode = Y.Node.create('');
- newnode.append(node.getHTML())
- .setAttribute('class', node.getAttribute('class'))
- .setData('itemid', node.getData('itemid'))
- .setStyles({
- height: node.getComputedStyle(HEIGHT),
- left: (nodepos - gradeHeadersOffset) + 'px',
- position: 'absolute',
- width: node.getComputedStyle(WIDTH)
- });
-
- // Sum up total widths - these are used in the container styles.
- // Use the offsetHeight and Width here as this contains the
- // padding, margin, and borders.
- floatingGradeHeadersWidth += parseInt(node.get(OFFSETWIDTH), 10);
- floatingGradeHeadersHeight = node.get(OFFSETHEIGHT);
-
- // Append to our floating table.
- floatingGradeHeaders.appendChild(newnode);
- }, this);
-
- // Position header table.
- floatingGradeHeaders.setStyles({
- height: floatingGradeHeadersHeight + 'px',
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px',
- width: floatingGradeHeadersWidth + 'px'
- });
-
- // Insert in place before the grader headers.
- this.userColumnHeader.insert(floatingGradeHeaders, 'before');
-
- // Store a reference to this for later - we use it in the event handlers.
- this.gradeItemHeadingContainer = floatingGradeHeaders;
- },
-
- /**
- * Create and setup the floating header row of grade item titles.
- *
- * @method _setupFloatingAssignmentFooter
- * @protected
- */
- _setupFloatingAssignmentFooter: function() {
- this.tableFooterRow = Y.one('#user-grades .avg');
- if (!this.tableFooterRow) {
- Y.log('Averages footer not found - unable to float it.', 'warn', LOGNS);
- return;
- }
-
- // Generate the sticky footer row.
- var footerCells = this.tableFooterRow.all('.cell');
-
- // Create a container.
- var floatingGraderFooter = Y.Node.create('');
- var footerWidth = 0;
- var coordinates = this._getRelativeXY(this.tableFooterRow);
- var footerRowOffset = coordinates[0];
- var floatingGraderFooterHeight = 0;
-
- // Copy cell content.
- footerCells.each(function(node) {
- var newnode = Y.Node.create('');
- var nodepos = this._getRelativeXY(node)[0];
- newnode.set('innerHTML', node.getHTML())
- .setAttribute('class', node.getAttribute('class'))
- .setStyles({
- height: node.getComputedStyle(HEIGHT),
- left: (nodepos - footerRowOffset) + 'px',
- position: 'absolute',
- width: node.getComputedStyle(WIDTH)
- });
-
- floatingGraderFooter.append(newnode);
- floatingGraderFooterHeight = node.get(OFFSETHEIGHT);
- footerWidth += parseInt(node.get(OFFSETWIDTH), 10);
- }, this);
-
- // Position the row.
- floatingGraderFooter.setStyles({
- position: 'absolute',
- left: coordinates[0] + 'px',
- bottom: '1px',
- height: floatingGraderFooterHeight + 'px',
- width: footerWidth + 'px'
- });
-
- // Append to the grader region.
- this.graderRegion.append(floatingGraderFooter);
-
- this.footerRow = floatingGraderFooter;
- },
-
- /**
- * Create and setup the floating footer title cell.
- *
- * @method _setupFloatingAssignmentFooterTitle
- * @protected
- */
- _setupFloatingAssignmentFooterTitle: function() {
- var floatingFooterRow = this.floatingHeaderRow[SELECTORS.FOOTERTITLE];
- if (floatingFooterRow) {
- // Style the floating row.
- floatingFooterRow
- .setStyles({
- bottom: '1px'
- });
- }
- },
-
- /**
- * Create and setup the floating left headers.
- *
- * @method _setupFloatingLeftHeaders
- * @protected
- */
- _setupFloatingLeftHeaders: function(headerSelector) {
- // We make various references to the origin cell. Store it for later.
- var origin = Y.one(headerSelector);
-
- if (!origin) {
- return;
- }
-
- // Create the floating row and cell.
- var floatingRow = Y.Node.create(''),
- floatingCell = Y.Node.create(''),
- coordinates = this._getRelativeXY(origin),
- width = this.firstUserCell.getComputedStyle(WIDTH),
- height = origin.get(OFFSETHEIGHT);
-
- // Append the content and style to the floating cell.
- floatingCell
- .set('innerHTML', origin.getHTML())
- .setAttribute('class', origin.getAttribute('class'))
- .setStyles({
- // The header is larger than the user cells, so we take the user cell.
- width: width
- });
-
- // Style the floating row.
- floatingRow
- .setStyles({
- position: 'absolute',
- top: coordinates[1] + 'px',
- left: coordinates[0] + 'px',
- height: height + 'px'
- })
- // Add all classes from the parent to the row
- .addClass(origin.get('parentNode').get('className'));
-
- // Append the cell to the row, and finally to the region.
- floatingRow.append(floatingCell);
- this.graderRegion.append(floatingRow);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.floatingHeaderRow[headerSelector] = floatingRow;
- },
-
- /**
- * Process a Scroll Event on the window.
- *
- * @method _handleScrollEvent
- * @protected
- */
- _handleScrollEvent: function() {
- // Performance is important in this function as it is called frequently and in quick succesion.
- // To prevent layout thrashing when the DOM is repeatedly updated and queried, updated and queried,
- // updates must be batched.
-
- // Next do all the calculations.
- var gradeItemHeadingContainerStyles = {},
- userColumnHeaderStyles = {},
- userColumnStyles = {},
- footerStyles = {},
- coord = 0,
- floatingUserTriggerPoint = 0, // The X position at which the floating should start.
- floatingUserRelativePoint = 0, // The point to use when calculating the new position.
- headerFloats = false,
- userFloats = false,
- footerFloats = false,
- leftTitleFloats = false,
- floatingHeaderStyles = {},
- floatingFooterTitleStyles = {},
- floatingFooterTitleRow = false,
- bodyMargin = 0;
-
- if (window.right_to_left()) {
- bodyMargin = parseInt(Y.one(Y.config.doc.body).getComputedStyle('marginRight'), 10);
- } else {
- bodyMargin = parseInt(Y.one(Y.config.doc.body).getComputedStyle('marginLeft'), 10);
- }
-
- if (bodyMargin != this.lastBodyMargin) {
- // Recalculate the position of the edge cells for scroll positioning.
- this._calculateCellPositions();
- }
-
- // Header position.
- gradeItemHeadingContainerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
- if (Y.config.win.pageYOffset + this.pageHeaderHeight > this.headerRowTop) {
- headerFloats = true;
- if (Y.config.win.pageYOffset + this.pageHeaderHeight < this.lastUserCellTop) {
- coord = this._getRelativeYFromY(Y.config.win.pageYOffset + this.pageHeaderHeight);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- } else {
- coord = this._getRelativeYFromY(this.lastUserCellTop);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- }
- } else {
- headerFloats = false;
- coord = this._getRelativeYFromY(this.headerRowTop);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- }
-
- // User column position.
-
- if (window.right_to_left()) {
- floatingUserTriggerPoint = Y.config.win.innerWidth + Y.config.win.pageXOffset - this.dockWidth;
- floatingUserRelativePoint = floatingUserTriggerPoint - this.firstUserCellWidth - bodyMargin;
- userFloats = floatingUserTriggerPoint < (this.firstUserCellLeft + this.firstUserCellWidth + bodyMargin);
- leftTitleFloats = (floatingUserTriggerPoint - this.firstNonUserCellWidth) <
- (this.firstNonUserCellLeft + this.firstUserCellWidth);
- } else {
- floatingUserRelativePoint = Y.config.win.pageXOffset + bodyMargin;
- floatingUserTriggerPoint = floatingUserRelativePoint + this.dockWidth + bodyMargin;
- userFloats = floatingUserTriggerPoint > this.firstUserCellLeft + bodyMargin;
- leftTitleFloats = floatingUserTriggerPoint > (this.firstNonUserCellLeft - this.firstUserCellWidth);
- }
-
- if (userFloats) {
- coord = this._getRelativeXFromX(floatingUserRelativePoint);
- userColumnStyles.left = coord + 'px';
- userColumnHeaderStyles.left = coord + 'px';
- } else {
- coord = this._getRelativeXFromX(this.firstUserCellLeft);
- userColumnStyles.left = coord + 'px';
- userColumnHeaderStyles.left = coord + 'px';
- }
-
- // Update the miscellaneous left-only floats.
- Y.Object.each(this.floatingHeaderRow, function(origin, key) {
- floatingHeaderStyles[key] = {
- left: userColumnStyles.left
- };
- }, this);
-
- // Update footer.
- if (this.footerRow) {
- footerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
-
- // Determine whether the footer should now be shown as sticky.
- var pageHeight = Y.config.win.innerHeight,
- pageOffset = Y.config.win.pageYOffset,
- bottomScrollPosition = pageHeight - this._getScrollBarHeight() + pageOffset,
- footerRowHeight = parseInt(this.footerRow.getComputedStyle(HEIGHT), 10),
- footerBottomPosition = footerRowHeight + this.footerRowPosition;
-
- floatingFooterTitleStyles = floatingHeaderStyles[SELECTORS.FOOTERTITLE];
- floatingFooterTitleRow = this.floatingHeaderRow[SELECTORS.FOOTERTITLE];
- if (bottomScrollPosition < footerBottomPosition && bottomScrollPosition > this.firstUserCellBottom) {
- // We have not scrolled below the footer, nor above the first row.
- footerStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
- footerFloats = true;
- } else {
- // The footer should not float any more.
- footerStyles.bottom = '1px';
- footerFloats = false;
- }
- if (floatingFooterTitleStyles) {
- floatingFooterTitleStyles.bottom = footerStyles.bottom;
- floatingFooterTitleStyles.top = null;
- }
- floatingHeaderStyles[SELECTORS.FOOTERTITLE] = floatingFooterTitleStyles;
- }
-
- // Apply the styles and mark elements as floating, or not.
- if (this.gradeItemHeadingContainer) {
- this.gradeItemHeadingContainer.setStyles(gradeItemHeadingContainerStyles);
- if (headerFloats) {
- this.gradeItemHeadingContainer.addClass(CSS.FLOATING);
- } else {
- this.gradeItemHeadingContainer.removeClass(CSS.FLOATING);
- }
- }
- if (this.userColumnHeader) {
- this.userColumnHeader.setStyles(userColumnHeaderStyles);
- if (userFloats) {
- this.userColumnHeader.addClass(CSS.FLOATING);
- } else {
- this.userColumnHeader.removeClass(CSS.FLOATING);
- }
- }
- if (this.userColumn) {
- this.userColumn.setStyles(userColumnStyles);
- if (userFloats) {
- this.userColumn.addClass(CSS.FLOATING);
- } else {
- this.userColumn.removeClass(CSS.FLOATING);
- }
- }
- if (this.footerRow) {
- this.footerRow.setStyles(footerStyles);
- if (footerFloats) {
- this.footerRow.addClass(CSS.FLOATING);
- } else {
- this.footerRow.removeClass(CSS.FLOATING);
- }
- }
-
- // And apply the styles to the generic left headers.
- Y.Object.each(floatingHeaderStyles, function(styles, key) {
- if (this.floatingHeaderRow[key]) {
- this.floatingHeaderRow[key].setStyles(styles);
- }
- }, this);
-
-
- Y.Object.each(this.floatingHeaderRow, function(value, key) {
- if (this.floatingHeaderRow[key]) {
- if (leftTitleFloats) {
- this.floatingHeaderRow[key].addClass(CSS.FLOATING);
- } else {
- this.floatingHeaderRow[key].removeClass(CSS.FLOATING);
- }
- }
- }, this);
-
- // The footer title has a more specific float setting.
- if (floatingFooterTitleRow) {
- if (leftTitleFloats) {
- floatingFooterTitleRow.addClass(CSS.FLOATING);
- } else {
- floatingFooterTitleRow.removeClass(CSS.FLOATING);
- }
- }
-
- },
-
- /**
- * Process a size change Event on the window.
- *
- * @method _handleResizeEvent
- * @protected
- */
- _handleResizeEvent: function() {
- // Recalculate the position of the edge cells for scroll positioning.
- this._calculateCellPositions();
-
- // Simulate a scroll.
- this._handleScrollEvent();
-
- // Resize user cells.
- var userWidth = this.firstUserCell.getComputedStyle(WIDTH);
- var userCells = Y.all(SELECTORS.USERCELL);
- this.userColumnHeader.one('.cell').setStyle('width', userWidth);
- this.userColumn.all('.cell').each(function(cell, idx) {
- var height = userCells.item(idx).getComputedStyle(HEIGHT);
- // Nasty hack to account for Internet Explorer
- if (Y.UA.ie !== 0) {
- var node = userCells.item(idx);
- var allHeight = node.getDOMNode ?
- node.getDOMNode().getBoundingClientRect().height :
- node.get('offsetHeight');
- var marginHeight = parseInt(node.getComputedStyle('marginTop'), 10) +
- parseInt(node.getComputedStyle('marginBottom'), 10);
- var paddingHeight = parseInt(node.getComputedStyle('paddingTop'), 10) +
- parseInt(node.getComputedStyle('paddingBottom'), 10);
- var borderHeight = parseInt(node.getComputedStyle('borderTopWidth'), 10) +
- parseInt(node.getComputedStyle('borderBottomWidth'), 10);
- height = allHeight - marginHeight - paddingHeight - borderHeight;
- }
- cell.setStyles({
- width: userWidth,
- height: height
- });
- }, this);
-
- // Resize headers & footers.
- // This is an expensive operation, not expected to happen often.
- var headers = this.gradeItemHeadingContainer.all('.cell');
- var resizedcells = Y.all(SELECTORS.HEADERCELLS);
-
- var headeroffsetleft = this.headerRow.getX();
- var newcontainerwidth = 0;
- resizedcells.each(function(cell, idx) {
- var headercell = headers.item(idx);
-
- newcontainerwidth += cell.get(OFFSETWIDTH);
- var styles = {
- width: cell.getComputedStyle(WIDTH),
- left: cell.getX() - headeroffsetleft + 'px'
- };
- headercell.setStyles(styles);
- });
-
- if (this.footerRow) {
- var footers = this.footerRow.all('.cell');
- if (footers.size() !== 0) {
- var resizedavgcells = Y.all(SELECTORS.FOOTERCELLS);
-
- resizedavgcells.each(function(cell, idx) {
- var footercell = footers.item(idx);
- var styles = {
- width: cell.getComputedStyle(WIDTH),
- left: cell.getX() - headeroffsetleft + 'px'
- };
- footercell.setStyles(styles);
- });
- }
- }
-
- // Resize the title areas too.
- Y.Object.each(this.floatingHeaderRow, function(row) {
- row.one('div').setStyle('width', userWidth);
- }, this);
-
- this.gradeItemHeadingContainer.setStyle('width', newcontainerwidth);
- }
-
-};
-
-Y.Base.mix(Y.M.gradereport_grader.ReportTable, [FloatingHeaders]);
-
-
-}, '@VERSION@', {"requires": ["base", "node", "event", "handlebars", "overlay", "event-hover"]});
diff --git a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js
deleted file mode 100644
index ff31a3ac1f7..00000000000
--- a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-YUI.add("moodle-gradereport_grader-gradereporttable",function(R,e){var w,h,g,l,H=".avg .header",t="#user-grades .avg .cell",i="td.grade",s=".gradeparent table",o=".gradeparent",p="#user-grades .heading .cell",r="#user-grades tr.heading",a="#studentheader",u="#user-grades .user.cell",_={OVERRIDDEN:"overridden",TOOLTIPACTIVE:"tooltipactive"};function n(){n.superclass.constructor.apply(this,arguments)}function d(){}R.extend(n,R.Base,{_eventHandles:[],graderTable:null,initializer:function(){this.graderRegion=R.one(o),this.graderTable=R.one(s),this.setupFloatingHeaders()},getGradeUserName:function(e){var t=e.ancestor("tr").one("th.user .username");return t?t.get("text"):""},getGradeItemName:function(e){var t=R.one("th.item[data-itemid='"+e.getData("itemid")+"']");return t?t.get("text"):""},getGradeFeedback:function(e){return e.getData("feedback")}}),R.namespace("M.gradereport_grader").ReportTable=n,R.namespace("M.gradereport_grader").init=function(e){return new R.M.gradereport_grader.ReportTable(e)},w="height",h="width",g="offsetWidth",l="offsetHeight",_.FLOATING="floating",d.ATTRS={},d.prototype={pageHeaderHeight:0,container:null,headerCell:null,headerRow:null,firstUserCell:null,firstNonUserCell:null,firstNonUserCellLeft:0,firstNonUserCellWidth:0,tableFooterRow:null,footerRow:null,gradeItemHeadingContainer:null,userColumnHeader:null,userColumn:null,firstUserCellBottom:0,firstUserCellLeft:0,firstUserCellWidth:0,dockWidth:0,lastUserCellTop:0,floatingHeaderRow:null,_eventHandles:[],lastBodyMargin:0,setupFloatingHeaders:function(){return this.firstUserCell=R.one(u),this.container=R.one(o),this.firstNonUserCell=R.one(i),this.firstUserCell?M.cfg.behatsiterunning?void 0:(this._setupFloatingUserColumn(),this._setupFloatingUserHeader(),this._setupFloatingAssignmentHeaders(),this._setupFloatingAssignmentFooter(),this.floatingHeaderRow={},this._setupFloatingLeftHeaders(".controls .controls"),this._setupFloatingLeftHeaders(".range .range"),this._setupFloatingLeftHeaders(H),this._setupFloatingAssignmentFooterTitle(),this._calculateCellPositions(),this._handleScrollEvent(),this._setupEventHandlers(),R.Global.on("moodle-gradereport_grader:resized",this._handleResizeEvent,this),this):this},_calculateCellPositions:function(){var e,t,i,s,o,r;this.headerRowTop=this.headerRow.getY(),this.tableFooterRow&&(this.footerRowPosition=this.tableFooterRow.getY()),this.dockWidth=0,(e=R.one(".has_dock #dock"))&&(this.dockWidth=e.get(g)),t=R.all(u),this.firstUserCellLeft=this.firstUserCell.getX(),this.firstUserCellWidth=this.firstUserCell.get(g),this.firstNonUserCellLeft=this.firstNonUserCell.getX(),this.firstNonUserCellWidth=this.firstNonUserCell.get(g),1R.config.doc.body.clientWidth?R.DOM.getScrollbarWidth():0},_setupEventHandlers:function(){this._eventHandles.push(R.one(R.config.win).on("scroll",this._handleScrollEvent,this),R.one(R.config.win).on("resize",this._handleResizeEvent,this),R.one(R.config.win).on("orientationchange",this._handleResizeEvent,this),R.Global.on("dock:shown",this._handleResizeEvent,this),R.Global.on("dock:hidden",this._handleResizeEvent,this))},_setupFloatingUserColumn:function(){var e=R.all(u),s=R.Node.create(''),t=this._getRelativeXY(this.firstUserCell);e.each(function(e){var t,i=e.getComputedStyle(w);0!==R.UA.ie&&(i=e.get("offsetHeight")-(parseInt(e.getComputedStyle("marginTop"),10)+parseInt(e.getComputedStyle("marginBottom"),10))-(parseInt(e.getComputedStyle("paddingTop"),10)+parseInt(e.getComputedStyle("paddingBottom"),10))-(parseInt(e.getComputedStyle("borderTopWidth"),10)+parseInt(e.getComputedStyle("borderBottomWidth"),10))),(t=R.Node.create("")).set("innerHTML",e.get("innerHTML")).setAttribute("class",e.getAttribute("class")).setAttribute("data-uid",e.ancestor("tr").getData("uid")).setStyles({height:i,width:e.getComputedStyle(h)}),s.appendChild(t)},this),s.setStyles({left:t[0]+"px",position:"absolute",top:t[1]+"px"}),this.graderRegion.append(s),this.userColumn=s},_setupFloatingUserHeader:function(){this.headerRow=R.one(r),this.headerCell=R.one(a);var e=R.Node.create(''),t=R.Node.create(""),i=this._getRelativeXY(this.headerCell)[0],s=this._getRelativeXY(this.headerRow),o=s[0];t.set("innerHTML",this.headerCell.getHTML()).setAttribute("class",this.headerCell.getAttribute("class")).setStyles({width:this.firstUserCell.getComputedStyle(h),left:i-o+"px"}),e.setStyles({left:s[0]+"px",position:"absolute",top:s[1]+"px"}),e.append(t),this.graderRegion.append(e),this.userColumnHeader=e},_setupFloatingAssignmentHeaders:function(){var e,s,t,o,r,a;this.headerRow=R.one("#user-grades tr.heading"),e=R.all("#user-grades tr.heading .cell"),s=R.Node.create(''),t=this._getRelativeXY(this.headerRow),a=t[r=o=0],e.each(function(e){var t=this._getRelativeXY(e)[0],i=R.Node.create("")
-;i.append(e.getHTML()).setAttribute("class",e.getAttribute("class")).setData("itemid",e.getData("itemid")).setStyles({height:e.getComputedStyle(w),left:t-a+"px",position:"absolute",width:e.getComputedStyle(h)}),o+=parseInt(e.get(g),10),r=e.get(l),s.appendChild(i)},this),s.setStyles({height:r+"px",left:t[0]+"px",position:"absolute",top:t[1]+"px",width:o+"px"}),this.userColumnHeader.insert(s,"before"),this.gradeItemHeadingContainer=s},_setupFloatingAssignmentFooter:function(){var e,s,o,t,r,a;this.tableFooterRow=R.one("#user-grades .avg"),this.tableFooterRow&&(e=this.tableFooterRow.all(".cell"),s=R.Node.create(''),o=0,t=this._getRelativeXY(this.tableFooterRow),r=t[0],a=0,e.each(function(e){var t=R.Node.create(""),i=this._getRelativeXY(e)[0];t.set("innerHTML",e.getHTML()).setAttribute("class",e.getAttribute("class")).setStyles({height:e.getComputedStyle(w),left:i-r+"px",position:"absolute",width:e.getComputedStyle(h)}),s.append(t),a=e.get(l),o+=parseInt(e.get(g),10)},this),s.setStyles({position:"absolute",left:t[0]+"px",bottom:"1px",height:a+"px",width:o+"px"}),this.graderRegion.append(s),this.footerRow=s)},_setupFloatingAssignmentFooterTitle:function(){var e=this.floatingHeaderRow[H];e&&e.setStyles({bottom:"1px"})},_setupFloatingLeftHeaders:function(e){var t,i,s,o,r,a=R.one(e);a&&(t=R.Node.create(''),i=R.Node.create(""),s=this._getRelativeXY(a),o=this.firstUserCell.getComputedStyle(h),r=a.get(l),i.set("innerHTML",a.getHTML()).setAttribute("class",a.getAttribute("class")).setStyles({width:o}),t.setStyles({position:"absolute",top:s[1]+"px",left:s[0]+"px",height:r+"px"}).addClass(a.get("parentNode").get("className")),t.append(i),this.graderRegion.append(t),this.floatingHeaderRow[e]=t)},_handleScrollEvent:function(){var e,t,i,s,o={},r={},a={},l={},n=0,d=0,h=0,g=!1,p=!1,u=!1,f=!1,c={},m={},C=!1,v=0;(v=window.right_to_left()?parseInt(R.one(R.config.doc.body).getComputedStyle("marginRight"),10):parseInt(R.one(R.config.doc.body).getComputedStyle("marginLeft"),10))!=this.lastBodyMargin&&this._calculateCellPositions(),o.left=this._getRelativeXFromX(this.headerRow.getX()),n=R.config.win.pageYOffset+this.pageHeaderHeight>this.headerRowTop?(g=!0,R.config.win.pageYOffset+this.pageHeaderHeightthis.firstUserCellLeft+v,d>this.firstNonUserCellLeft-this.firstUserCellWidth),n=p?this._getRelativeXFromX(h):this._getRelativeXFromX(this.firstUserCellLeft),a.left=n+"px",r.left=n+"px",R.Object.each(this.floatingHeaderRow,function(e,t){c[t]={left:a.left}},this),this.footerRow&&(l.left=this._getRelativeXFromX(this.headerRow.getX()),e=R.config.win.innerHeight,t=R.config.win.pageYOffset,i=e-this._getScrollBarHeight()+t,s=parseInt(this.footerRow.getComputedStyle(w),10)+this.footerRowPosition,m=c[H],C=this.floatingHeaderRow[H],u=ithis.firstUserCellBottom?(l.bottom=Math.ceil(s-i)+"px",!0):!(l.bottom="1px"),m&&(m.bottom=l.bottom,m.top=null),c[H]=m),this.gradeItemHeadingContainer&&(this.gradeItemHeadingContainer.setStyles(o),g?this.gradeItemHeadingContainer.addClass(_.FLOATING):this.gradeItemHeadingContainer.removeClass(_.FLOATING)),this.userColumnHeader&&(this.userColumnHeader.setStyles(r),p?this.userColumnHeader.addClass(_.FLOATING):this.userColumnHeader.removeClass(_.FLOATING)),this.userColumn&&(this.userColumn.setStyles(a),p?this.userColumn.addClass(_.FLOATING):this.userColumn.removeClass(_.FLOATING)),this.footerRow&&(this.footerRow.setStyles(l),u?this.footerRow.addClass(_.FLOATING):this.footerRow.removeClass(_.FLOATING)),R.Object.each(c,function(e,t){this.floatingHeaderRow[t]&&this.floatingHeaderRow[t].setStyles(e)},this),R.Object.each(this.floatingHeaderRow,function(e,t){this.floatingHeaderRow[t]&&(f?this.floatingHeaderRow[t].addClass(_.FLOATING):this.floatingHeaderRow[t].removeClass(_.FLOATING))},this),C&&(f?C.addClass(_.FLOATING):C.removeClass(_.FLOATING))},_handleResizeEvent:function(){var o,r,a,e,l,n,d;this._calculateCellPositions(),this._handleScrollEvent(),o=this.firstUserCell.getComputedStyle(h),r=R.all(u),this.userColumnHeader.one(".cell").setStyle("width",o),this.userColumn.all(".cell").each(function(e,t){var i,s=r.item(t).getComputedStyle(w);0!==R.UA.ie&&(s=((i=r.item(t)).getDOMNode?i.getDOMNode().getBoundingClientRect().height:i.get("offsetHeight"))-(parseInt(i.getComputedStyle("marginTop"),10)+parseInt(i.getComputedStyle("marginBottom"),10))-(parseInt(i.getComputedStyle("paddingTop"),10)+parseInt(i.getComputedStyle("paddingBottom"),10))-(parseInt(i.getComputedStyle("borderTopWidth"),10)+parseInt(i.getComputedStyle("borderBottomWidth"),10))),e.setStyles({width:o,height:s})},this),a=this.gradeItemHeadingContainer.all(".cell"),e=R.all(p),l=this.headerRow.getX(),n=0,e.each(function(e,t){var i,s=a.item(t);n+=e.get(g),i={width:e.getComputedStyle(h),left:e.getX()-l+"px"},s.setStyles(i)}),this.footerRow&&0!==(d=this.footerRow.all(".cell")).size()&&R.all(t).each(function(e,t){var i=d.item(t),s={width:e.getComputedStyle(h),left:e.getX()-l+"px"};i.setStyles(s)}),R.Object.each(this.floatingHeaderRow,function(e){e.one("div").setStyle("width",o)},this),this.gradeItemHeadingContainer.setStyle("width",n)}},R.Base.mix(R.M.gradereport_grader.ReportTable,[d])},"@VERSION@",{requires:["base","node","event","handlebars","overlay","event-hover"]});
\ No newline at end of file
diff --git a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js
deleted file mode 100644
index d2ef47ac688..00000000000
--- a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js
+++ /dev/null
@@ -1,1173 +0,0 @@
-YUI.add('moodle-gradereport_grader-gradereporttable', function (Y, NAME) {
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see .
-/* eslint-disable no-unused-vars */
-
-/**
- * Grader Report Functionality.
- *
- * @module moodle-gradereport_grader-gradereporttable
- * @package gradereport_grader
- * @copyright 2014 UC Regents
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @author Alfonso Roman
- */
-
-/**
- * @module moodle-gradereport_grader-gradereporttable
- */
-
-var SELECTORS = {
- FOOTERTITLE: '.avg .header',
- FOOTERCELLS: '#user-grades .avg .cell',
- FOOTERROW: '#user-grades .avg',
- GRADECELL: 'td.grade',
- GRADERTABLE: '.gradeparent table',
- GRADEPARENT: '.gradeparent',
- HEADERCELLS: '#user-grades .heading .cell',
- HEADERCELL: '.gradebook-header-cell',
- HEADERROW: '#user-grades tr.heading',
- STUDENTHEADER: '#studentheader',
- USERCELL: '#user-grades .user.cell',
- USERMAIL: '#user-grades .useremail'
- },
- CSS = {
- OVERRIDDEN: 'overridden',
- TOOLTIPACTIVE: 'tooltipactive'
- };
-
-/**
- * The Grader Report Table.
- *
- * @namespace M.gradereport_grader
- * @class ReportTable
- * @constructor
- */
-function ReportTable() {
- ReportTable.superclass.constructor.apply(this, arguments);
-}
-
-Y.extend(ReportTable, Y.Base, {
- /**
- * Array of EventHandles.
- *
- * @type EventHandle[]
- * @property _eventHandles
- * @protected
- */
- _eventHandles: [],
-
- /**
- * A Node reference to the grader table.
- *
- * @property graderTable
- * @type Node
- */
- graderTable: null,
-
- /**
- * Setup the grader report table.
- *
- * @method initializer
- */
- initializer: function() {
- // Some useful references within our target area.
- this.graderRegion = Y.one(SELECTORS.GRADEPARENT);
- this.graderTable = Y.one(SELECTORS.GRADERTABLE);
-
- // Setup the floating headers.
- this.setupFloatingHeaders();
- },
-
- /**
- * Get the text content of the username for the specified grade item.
- *
- * @method getGradeUserName
- * @param {Node} cell The grade item cell to obtain the username for
- * @return {String} The string content of the username cell.
- */
- getGradeUserName: function(cell) {
- var userrow = cell.ancestor('tr'),
- usercell = userrow.one("th.user .username");
-
- if (usercell) {
- return usercell.get('text');
- } else {
- return '';
- }
- },
-
- /**
- * Get the text content of the item name for the specified grade item.
- *
- * @method getGradeItemName
- * @param {Node} cell The grade item cell to obtain the item name for
- * @return {String} The string content of the item name cell.
- */
- getGradeItemName: function(cell) {
- var itemcell = Y.one("th.item[data-itemid='" + cell.getData('itemid') + "']");
- if (itemcell) {
- return itemcell.get('text');
- } else {
- return '';
- }
- },
-
- /**
- * Get the text content of any feedback associated with the grade item.
- *
- * @method getGradeFeedback
- * @param {Node} cell The grade item cell to obtain the item name for
- * @return {String} The string content of the feedback.
- */
- getGradeFeedback: function(cell) {
- return cell.getData('feedback');
- }
-});
-
-Y.namespace('M.gradereport_grader').ReportTable = ReportTable;
-Y.namespace('M.gradereport_grader').init = function(config) {
- return new Y.M.gradereport_grader.ReportTable(config);
-};
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see .
-
-/**
- * @module moodle-gradereport_grader-gradereporttable
- * @submodule floatingheaders
- */
-
-/**
- * Provides floating headers to the grader report.
- *
- * See {{#crossLink "M.gradereport_grader.ReportTable"}}{{/crossLink}} for details.
- *
- * @namespace M.gradereport_grader
- * @class FloatingHeaders
- */
-
-var HEIGHT = 'height',
- WIDTH = 'width',
- OFFSETWIDTH = 'offsetWidth',
- OFFSETHEIGHT = 'offsetHeight',
- LOGNS = 'moodle-core-grade-report-grader';
-
-CSS.FLOATING = 'floating';
-
-function FloatingHeaders() {}
-
-FloatingHeaders.ATTRS = {
-};
-
-FloatingHeaders.prototype = {
- /**
- * The height of the page header if a fixed position, floating header
- * was found.
- *
- * @property pageHeaderHeight
- * @type Number
- * @default 0
- * @protected
- */
- pageHeaderHeight: 0,
-
- /**
- * A Node representing the container div.
- *
- * Positioning will be based on this element, which must have
- * the CSS rule 'position: relative'.
- *
- * @property container
- * @type Node
- * @protected
- */
- container: null,
-
- /**
- * A Node representing the header cell.
- *
- * @property headerCell
- * @type Node
- * @protected
- */
- headerCell: null,
-
- /**
- * A Node representing the header row.
- *
- * @property headerRow
- * @type Node
- * @protected
- */
- headerRow: null,
-
- /**
- * A Node representing the first cell which contains user name information.
- *
- * @property firstUserCell
- * @type Node
- * @protected
- */
- firstUserCell: null,
-
- /**
- * A Node representing the first cell which does not contain a user header.
- *
- * @property firstNonUserCell
- * @type Node
- * @protected
- */
- firstNonUserCell: null,
-
- /**
- * The position of the left of the first non-header cell in a row - the one after the email address.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstNonUserCellLeft
- * @type Number
- * @protected
- */
- firstNonUserCellLeft: 0,
-
- /**
- * The width of the first non-header cell in a row - the one after the email address.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- * This is only used for RTL calculations.
- *
- * @property firstNonUserCellWidth
- * @type Number
- * @protected
- */
- firstNonUserCellWidth: 0,
-
- /**
- * A Node representing the original table footer row.
- *
- * @property tableFooterRow
- * @type Node
- * @protected
- */
- tableFooterRow: null,
-
- /**
- * A Node representing the floating footer row in the grading table.
- *
- * @property footerRow
- * @type Node
- * @protected
- */
- footerRow: null,
-
- /**
- * A Node representing the floating grade item header.
- *
- * @property gradeItemHeadingContainer
- * @type Node
- * @protected
- */
- gradeItemHeadingContainer: null,
-
- /**
- * A Node representing the floating user header. This is the header with the Surname/First name
- * sorting.
- *
- * @property userColumnHeader
- * @type Node
- * @protected
- */
- userColumnHeader: null,
-
- /**
- * A Node representing the floating user column. This is the column containing all of the user
- * names.
- *
- * @property userColumn
- * @type Node
- * @protected
- */
- userColumn: null,
-
- /**
- * The position of the bottom of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstUserCellBottom
- * @type Number
- * @protected
- */
- firstUserCellBottom: 0,
-
- /**
- * The position of the left of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstUserCellLeft
- * @type Number
- * @protected
- */
- firstUserCellLeft: 0,
-
- /**
- * The width of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- * This is only used for RTL calculations.
- *
- * @property firstUserCellWidth
- * @type Number
- * @protected
- */
- firstUserCellWidth: 0,
-
- /**
- * The width of the dock if it is visible.
- *
- * @property dockWidth
- * @type Number
- * @protected
- */
- dockWidth: 0,
-
- /**
- * The position of the top of the final user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property lastUserCellTop
- * @type Number
- * @protected
- */
- lastUserCellTop: 0,
-
- /**
- * A list of Nodes representing the generic floating rows.
- *
- * @property floatingHeaderRow
- * @type Node{}
- * @protected
- */
- floatingHeaderRow: null,
-
- /**
- * Array of EventHandles.
- *
- * @type EventHandle[]
- * @property _eventHandles
- * @protected
- */
- _eventHandles: [],
-
- /**
- * The last value of the bodyMargin style. We need to recompute positions if it is changed.
- *
- * @property lastBodyMargin
- * @type Number
- * @protected
- */
- lastBodyMargin: 0,
-
- /**
- * Setup the grader report table.
- *
- * @method setupFloatingHeaders
- * @chainable
- */
- setupFloatingHeaders: function() {
- // Grab references to commonly used Nodes.
- this.firstUserCell = Y.one(SELECTORS.USERCELL);
- this.container = Y.one(SELECTORS.GRADEPARENT);
- this.firstNonUserCell = Y.one(SELECTORS.GRADECELL);
-
- if (!this.firstUserCell) {
- // No need for floating elements, there are no users.
- return this;
- }
-
- if (M.cfg.behatsiterunning) {
- // If the behat site is running we don't want floating elements.
- return;
- }
-
- // Generate floating elements.
- this._setupFloatingUserColumn();
- this._setupFloatingUserHeader();
- this._setupFloatingAssignmentHeaders();
- this._setupFloatingAssignmentFooter();
-
- // Setup generic floating left-aligned headers.
- this.floatingHeaderRow = {};
-
- // The 'Controls' row (shown in editing mode when certain options are set).
- this._setupFloatingLeftHeaders('.controls .controls');
-
- // The 'Range' row (shown in editing mode when certain options are set).
- this._setupFloatingLeftHeaders('.range .range');
-
- // The 'Overall Average' field.
- this._setupFloatingLeftHeaders(SELECTORS.FOOTERTITLE);
-
- // Additional setup for the footertitle.
- this._setupFloatingAssignmentFooterTitle();
-
- // Calculate the positions of edge cells. These are used for positioning of the floating headers.
- // This must be called after the floating headers are setup, but before the scroll event handler is invoked.
- this._calculateCellPositions();
-
- // Setup the floating element initial positions by simulating scroll.
- this._handleScrollEvent();
-
- // Setup the event handlers.
- this._setupEventHandlers();
-
- // Listen for a resize event globally - other parts of the code not in this YUI wrapper may make changes to the
- // fields which result in size changes.
- Y.Global.on('moodle-gradereport_grader:resized', this._handleResizeEvent, this);
-
- return this;
- },
-
- /**
- * Calculate the positions of some cells. These values are used heavily
- * in scroll event handling.
- *
- * @method _calculateCellPositions
- * @protected
- */
- _calculateCellPositions: function() {
- // The header row shows the grade item headers and is floated to the top of the window.
- this.headerRowTop = this.headerRow.getY();
-
- // The footer row shows the grade averages and will be floated to the page bottom.
- if (this.tableFooterRow) {
- this.footerRowPosition = this.tableFooterRow.getY();
- }
-
- // Add the width of the dock if it is visible.
- this.dockWidth = 0;
- var dock = Y.one('.has_dock #dock');
- if (dock) {
- this.dockWidth = dock.get(OFFSETWIDTH);
- }
-
- var userCellList = Y.all(SELECTORS.USERCELL);
-
- // The left of the user cells matches the left of the headerRow.
- this.firstUserCellLeft = this.firstUserCell.getX();
- this.firstUserCellWidth = this.firstUserCell.get(OFFSETWIDTH);
-
- // The left of the user cells matches the left of the footer title.
- this.firstNonUserCellLeft = this.firstNonUserCell.getX();
- this.firstNonUserCellWidth = this.firstNonUserCell.get(OFFSETWIDTH);
-
- if (userCellList.size() > 1) {
- // Use the top of the second cell for the bottom of the first cell.
- // This is used when scrolling to fix the footer to the top edge of the window.
- var firstUserCell = userCellList.item(1);
- this.firstUserCellBottom = firstUserCell.getY() + parseInt(firstUserCell.getComputedStyle(HEIGHT), 10);
-
- // Use the top of the penultimate cell when scrolling the header.
- // The header is the same size as the cells.
- this.lastUserCellTop = userCellList.item(userCellList.size() - 2).getY();
- } else {
- var firstItem = userCellList.item(0);
- // We can't use the top of the second row as there is only one row.
- this.lastUserCellTop = firstItem.getY();
-
- if (this.tableFooterRow) {
- // The footer is present so we can use that.
- this.firstUserCellBottom = this.footerRowPosition + parseInt(this.tableFooterRow.getComputedStyle(HEIGHT), 10);
- } else {
- // No other clues - calculate the top instead.
- this.firstUserCellBottom = firstItem.getY() + firstItem.get('offsetHeight');
- }
- }
-
- // Check whether a header is present and whether it is floating.
- var header = Y.one('header');
- this.pageHeaderHeight = 0;
- if (header) {
- if (header.getComputedStyle('position') === 'fixed') {
- this.pageHeaderHeight = header.get(OFFSETHEIGHT);
- } else {
- var navbar = Y.one('.navbar');
-
- if (navbar && navbar.getComputedStyle('position') === 'fixed') {
- // If the navbar exists and isn't fixed, we need to offset the page header to accommodate for it.
- this.pageHeaderHeight = navbar.get(OFFSETHEIGHT);
- }
- }
- }
- },
-
- /**
- * Get the relative XY of the node.
- *
- * @method _getRelativeXY
- * @protected
- * @param {Node} node The node to get the position of.
- * @return {Array} Containing X and Y.
- */
- _getRelativeXY: function(node) {
- return this._getRelativeXYFromXY(node.getX(), node.getY());
- },
-
- /**
- * Get the relative positioning from coordinates.
- *
- * This gives the position according to the parent of the table, which must
- * be set as position: relative.
- *
- * @method _getRelativeXYFromXY
- * @protected
- * @param {Number} x X position.
- * @param {Number} y Y position.
- * @return {Array} Containing X and Y.
- */
- _getRelativeXYFromXY: function(x, y) {
- var parentXY = this.container.getXY();
- return [x - parentXY[0], y - parentXY[1]];
- },
-
- /**
- * Get the relative positioning of an elements from coordinates.
- *
- * @method _getRelativeXFromX
- * @protected
- * @param {Number} pos X position.
- * @return {Number} relative X position.
- */
- _getRelativeXFromX: function(pos) {
- return this._getRelativeXYFromXY(pos, 0)[0];
- },
-
- /**
- * Get the relative positioning of an elements from coordinates.
- *
- * @method _getRelativeYFromY
- * @protected
- * @param {Number} pos Y position.
- * @return {Number} relative Y position.
- */
- _getRelativeYFromY: function(pos) {
- return this._getRelativeXYFromXY(0, pos)[1];
- },
-
- /**
- * Return the size of the horizontal scrollbar.
- *
- * @method _getScrollBarHeight
- * @protected
- * @return {Number} Height of the scrollbar.
- */
- _getScrollBarHeight: function() {
- if (Y.UA.ie && Y.UA.ie >= 10) {
- // IE has transparent scrollbars, which sometimes disappear... it's better to ignore them.
- return 0;
- } else if (Y.config.doc.body.scrollWidth > Y.config.doc.body.clientWidth) {
- // The document can be horizontally scrolled.
- return Y.DOM.getScrollbarWidth();
- }
- return 0;
- },
-
- /**
- * Setup the main event listeners.
- * These deal with things like window events.
- *
- * @method _setupEventHandlers
- * @protected
- */
- _setupEventHandlers: function() {
- this._eventHandles.push(
- // Listen for window scrolls, resizes, and rotation events.
- Y.one(Y.config.win).on('scroll', this._handleScrollEvent, this),
- Y.one(Y.config.win).on('resize', this._handleResizeEvent, this),
- Y.one(Y.config.win).on('orientationchange', this._handleResizeEvent, this),
- Y.Global.on('dock:shown', this._handleResizeEvent, this),
- Y.Global.on('dock:hidden', this._handleResizeEvent, this)
- );
- },
-
- /**
- * Create and setup the floating column of user names.
- *
- * @method _setupFloatingUserColumn
- * @protected
- */
- _setupFloatingUserColumn: function() {
- // Grab all cells in the user names column.
- var userColumn = Y.all(SELECTORS.USERCELL),
-
- // Create a floating table.
- floatingUserColumn = Y.Node.create(''),
-
- // Get the XY for the floating element.
- coordinates = this._getRelativeXY(this.firstUserCell);
-
- // Generate the new fields.
- userColumn.each(function(node) {
- var height = node.getComputedStyle(HEIGHT);
- // Nasty hack to account for Internet Explorer
- if (Y.UA.ie !== 0) {
- var allHeight = node.get('offsetHeight');
- var marginHeight = parseInt(node.getComputedStyle('marginTop'), 10) +
- parseInt(node.getComputedStyle('marginBottom'), 10);
- var paddingHeight = parseInt(node.getComputedStyle('paddingTop'), 10) +
- parseInt(node.getComputedStyle('paddingBottom'), 10);
- var borderHeight = parseInt(node.getComputedStyle('borderTopWidth'), 10) +
- parseInt(node.getComputedStyle('borderBottomWidth'), 10);
- height = allHeight - marginHeight - paddingHeight - borderHeight;
- }
- // Create and configure the new container.
- var containerNode = Y.Node.create('');
- containerNode.set('innerHTML', node.get('innerHTML'))
- .setAttribute('class', node.getAttribute('class'))
- .setAttribute('data-uid', node.ancestor('tr').getData('uid'))
- .setStyles({
- height: height,
- width: node.getComputedStyle(WIDTH)
- });
-
- // Add the new nodes to our floating table.
- floatingUserColumn.appendChild(containerNode);
- }, this);
-
- // Style the floating user container.
- floatingUserColumn.setStyles({
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px'
- });
-
- // Append to the grader region.
- this.graderRegion.append(floatingUserColumn);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.userColumn = floatingUserColumn;
- },
-
- /**
- * Create and setup the floating username header cell.
- *
- * @method _setupFloatingUserHeader
- * @protected
- */
- _setupFloatingUserHeader: function() {
- // We make various references to the header cells. Store it for later.
- this.headerRow = Y.one(SELECTORS.HEADERROW);
- this.headerCell = Y.one(SELECTORS.STUDENTHEADER);
-
- // Create the floating row and cell.
- var floatingUserHeaderRow = Y.Node.create(''),
- floatingUserHeaderCell = Y.Node.create(''),
- nodepos = this._getRelativeXY(this.headerCell)[0],
- coordinates = this._getRelativeXY(this.headerRow),
- gradeHeadersOffset = coordinates[0];
-
- // Append the content and style to the floating cell.
- floatingUserHeaderCell
- .set('innerHTML', this.headerCell.getHTML())
- .setAttribute('class', this.headerCell.getAttribute('class'))
- .setStyles({
- // The header is larger than the user cells, so we take the user cell.
- width: this.firstUserCell.getComputedStyle(WIDTH),
- left: (nodepos - gradeHeadersOffset) + 'px'
- });
-
- // Style the floating row.
- floatingUserHeaderRow
- .setStyles({
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px'
- });
-
- // Append the cell to the row, and finally to the region.
- floatingUserHeaderRow.append(floatingUserHeaderCell);
- this.graderRegion.append(floatingUserHeaderRow);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.userColumnHeader = floatingUserHeaderRow;
- },
-
- /**
- * Create and setup the floating grade item header row.
- *
- * @method _setupFloatingAssignmentHeaders
- * @protected
- */
- _setupFloatingAssignmentHeaders: function() {
- this.headerRow = Y.one('#user-grades tr.heading');
-
- var gradeHeaders = Y.all('#user-grades tr.heading .cell');
-
- // Generate a floating headers
- var floatingGradeHeaders = Y.Node.create('');
-
- var coordinates = this._getRelativeXY(this.headerRow);
-
- var floatingGradeHeadersWidth = 0;
- var floatingGradeHeadersHeight = 0;
- var gradeHeadersOffset = coordinates[0];
-
- gradeHeaders.each(function(node) {
- var nodepos = this._getRelativeXY(node)[0];
-
- var newnode = Y.Node.create('');
- newnode.append(node.getHTML())
- .setAttribute('class', node.getAttribute('class'))
- .setData('itemid', node.getData('itemid'))
- .setStyles({
- height: node.getComputedStyle(HEIGHT),
- left: (nodepos - gradeHeadersOffset) + 'px',
- position: 'absolute',
- width: node.getComputedStyle(WIDTH)
- });
-
- // Sum up total widths - these are used in the container styles.
- // Use the offsetHeight and Width here as this contains the
- // padding, margin, and borders.
- floatingGradeHeadersWidth += parseInt(node.get(OFFSETWIDTH), 10);
- floatingGradeHeadersHeight = node.get(OFFSETHEIGHT);
-
- // Append to our floating table.
- floatingGradeHeaders.appendChild(newnode);
- }, this);
-
- // Position header table.
- floatingGradeHeaders.setStyles({
- height: floatingGradeHeadersHeight + 'px',
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px',
- width: floatingGradeHeadersWidth + 'px'
- });
-
- // Insert in place before the grader headers.
- this.userColumnHeader.insert(floatingGradeHeaders, 'before');
-
- // Store a reference to this for later - we use it in the event handlers.
- this.gradeItemHeadingContainer = floatingGradeHeaders;
- },
-
- /**
- * Create and setup the floating header row of grade item titles.
- *
- * @method _setupFloatingAssignmentFooter
- * @protected
- */
- _setupFloatingAssignmentFooter: function() {
- this.tableFooterRow = Y.one('#user-grades .avg');
- if (!this.tableFooterRow) {
- return;
- }
-
- // Generate the sticky footer row.
- var footerCells = this.tableFooterRow.all('.cell');
-
- // Create a container.
- var floatingGraderFooter = Y.Node.create('');
- var footerWidth = 0;
- var coordinates = this._getRelativeXY(this.tableFooterRow);
- var footerRowOffset = coordinates[0];
- var floatingGraderFooterHeight = 0;
-
- // Copy cell content.
- footerCells.each(function(node) {
- var newnode = Y.Node.create('');
- var nodepos = this._getRelativeXY(node)[0];
- newnode.set('innerHTML', node.getHTML())
- .setAttribute('class', node.getAttribute('class'))
- .setStyles({
- height: node.getComputedStyle(HEIGHT),
- left: (nodepos - footerRowOffset) + 'px',
- position: 'absolute',
- width: node.getComputedStyle(WIDTH)
- });
-
- floatingGraderFooter.append(newnode);
- floatingGraderFooterHeight = node.get(OFFSETHEIGHT);
- footerWidth += parseInt(node.get(OFFSETWIDTH), 10);
- }, this);
-
- // Position the row.
- floatingGraderFooter.setStyles({
- position: 'absolute',
- left: coordinates[0] + 'px',
- bottom: '1px',
- height: floatingGraderFooterHeight + 'px',
- width: footerWidth + 'px'
- });
-
- // Append to the grader region.
- this.graderRegion.append(floatingGraderFooter);
-
- this.footerRow = floatingGraderFooter;
- },
-
- /**
- * Create and setup the floating footer title cell.
- *
- * @method _setupFloatingAssignmentFooterTitle
- * @protected
- */
- _setupFloatingAssignmentFooterTitle: function() {
- var floatingFooterRow = this.floatingHeaderRow[SELECTORS.FOOTERTITLE];
- if (floatingFooterRow) {
- // Style the floating row.
- floatingFooterRow
- .setStyles({
- bottom: '1px'
- });
- }
- },
-
- /**
- * Create and setup the floating left headers.
- *
- * @method _setupFloatingLeftHeaders
- * @protected
- */
- _setupFloatingLeftHeaders: function(headerSelector) {
- // We make various references to the origin cell. Store it for later.
- var origin = Y.one(headerSelector);
-
- if (!origin) {
- return;
- }
-
- // Create the floating row and cell.
- var floatingRow = Y.Node.create(''),
- floatingCell = Y.Node.create(''),
- coordinates = this._getRelativeXY(origin),
- width = this.firstUserCell.getComputedStyle(WIDTH),
- height = origin.get(OFFSETHEIGHT);
-
- // Append the content and style to the floating cell.
- floatingCell
- .set('innerHTML', origin.getHTML())
- .setAttribute('class', origin.getAttribute('class'))
- .setStyles({
- // The header is larger than the user cells, so we take the user cell.
- width: width
- });
-
- // Style the floating row.
- floatingRow
- .setStyles({
- position: 'absolute',
- top: coordinates[1] + 'px',
- left: coordinates[0] + 'px',
- height: height + 'px'
- })
- // Add all classes from the parent to the row
- .addClass(origin.get('parentNode').get('className'));
-
- // Append the cell to the row, and finally to the region.
- floatingRow.append(floatingCell);
- this.graderRegion.append(floatingRow);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.floatingHeaderRow[headerSelector] = floatingRow;
- },
-
- /**
- * Process a Scroll Event on the window.
- *
- * @method _handleScrollEvent
- * @protected
- */
- _handleScrollEvent: function() {
- // Performance is important in this function as it is called frequently and in quick succesion.
- // To prevent layout thrashing when the DOM is repeatedly updated and queried, updated and queried,
- // updates must be batched.
-
- // Next do all the calculations.
- var gradeItemHeadingContainerStyles = {},
- userColumnHeaderStyles = {},
- userColumnStyles = {},
- footerStyles = {},
- coord = 0,
- floatingUserTriggerPoint = 0, // The X position at which the floating should start.
- floatingUserRelativePoint = 0, // The point to use when calculating the new position.
- headerFloats = false,
- userFloats = false,
- footerFloats = false,
- leftTitleFloats = false,
- floatingHeaderStyles = {},
- floatingFooterTitleStyles = {},
- floatingFooterTitleRow = false,
- bodyMargin = 0;
-
- if (window.right_to_left()) {
- bodyMargin = parseInt(Y.one(Y.config.doc.body).getComputedStyle('marginRight'), 10);
- } else {
- bodyMargin = parseInt(Y.one(Y.config.doc.body).getComputedStyle('marginLeft'), 10);
- }
-
- if (bodyMargin != this.lastBodyMargin) {
- // Recalculate the position of the edge cells for scroll positioning.
- this._calculateCellPositions();
- }
-
- // Header position.
- gradeItemHeadingContainerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
- if (Y.config.win.pageYOffset + this.pageHeaderHeight > this.headerRowTop) {
- headerFloats = true;
- if (Y.config.win.pageYOffset + this.pageHeaderHeight < this.lastUserCellTop) {
- coord = this._getRelativeYFromY(Y.config.win.pageYOffset + this.pageHeaderHeight);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- } else {
- coord = this._getRelativeYFromY(this.lastUserCellTop);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- }
- } else {
- headerFloats = false;
- coord = this._getRelativeYFromY(this.headerRowTop);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- }
-
- // User column position.
-
- if (window.right_to_left()) {
- floatingUserTriggerPoint = Y.config.win.innerWidth + Y.config.win.pageXOffset - this.dockWidth;
- floatingUserRelativePoint = floatingUserTriggerPoint - this.firstUserCellWidth - bodyMargin;
- userFloats = floatingUserTriggerPoint < (this.firstUserCellLeft + this.firstUserCellWidth + bodyMargin);
- leftTitleFloats = (floatingUserTriggerPoint - this.firstNonUserCellWidth) <
- (this.firstNonUserCellLeft + this.firstUserCellWidth);
- } else {
- floatingUserRelativePoint = Y.config.win.pageXOffset + bodyMargin;
- floatingUserTriggerPoint = floatingUserRelativePoint + this.dockWidth + bodyMargin;
- userFloats = floatingUserTriggerPoint > this.firstUserCellLeft + bodyMargin;
- leftTitleFloats = floatingUserTriggerPoint > (this.firstNonUserCellLeft - this.firstUserCellWidth);
- }
-
- if (userFloats) {
- coord = this._getRelativeXFromX(floatingUserRelativePoint);
- userColumnStyles.left = coord + 'px';
- userColumnHeaderStyles.left = coord + 'px';
- } else {
- coord = this._getRelativeXFromX(this.firstUserCellLeft);
- userColumnStyles.left = coord + 'px';
- userColumnHeaderStyles.left = coord + 'px';
- }
-
- // Update the miscellaneous left-only floats.
- Y.Object.each(this.floatingHeaderRow, function(origin, key) {
- floatingHeaderStyles[key] = {
- left: userColumnStyles.left
- };
- }, this);
-
- // Update footer.
- if (this.footerRow) {
- footerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
-
- // Determine whether the footer should now be shown as sticky.
- var pageHeight = Y.config.win.innerHeight,
- pageOffset = Y.config.win.pageYOffset,
- bottomScrollPosition = pageHeight - this._getScrollBarHeight() + pageOffset,
- footerRowHeight = parseInt(this.footerRow.getComputedStyle(HEIGHT), 10),
- footerBottomPosition = footerRowHeight + this.footerRowPosition;
-
- floatingFooterTitleStyles = floatingHeaderStyles[SELECTORS.FOOTERTITLE];
- floatingFooterTitleRow = this.floatingHeaderRow[SELECTORS.FOOTERTITLE];
- if (bottomScrollPosition < footerBottomPosition && bottomScrollPosition > this.firstUserCellBottom) {
- // We have not scrolled below the footer, nor above the first row.
- footerStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
- footerFloats = true;
- } else {
- // The footer should not float any more.
- footerStyles.bottom = '1px';
- footerFloats = false;
- }
- if (floatingFooterTitleStyles) {
- floatingFooterTitleStyles.bottom = footerStyles.bottom;
- floatingFooterTitleStyles.top = null;
- }
- floatingHeaderStyles[SELECTORS.FOOTERTITLE] = floatingFooterTitleStyles;
- }
-
- // Apply the styles and mark elements as floating, or not.
- if (this.gradeItemHeadingContainer) {
- this.gradeItemHeadingContainer.setStyles(gradeItemHeadingContainerStyles);
- if (headerFloats) {
- this.gradeItemHeadingContainer.addClass(CSS.FLOATING);
- } else {
- this.gradeItemHeadingContainer.removeClass(CSS.FLOATING);
- }
- }
- if (this.userColumnHeader) {
- this.userColumnHeader.setStyles(userColumnHeaderStyles);
- if (userFloats) {
- this.userColumnHeader.addClass(CSS.FLOATING);
- } else {
- this.userColumnHeader.removeClass(CSS.FLOATING);
- }
- }
- if (this.userColumn) {
- this.userColumn.setStyles(userColumnStyles);
- if (userFloats) {
- this.userColumn.addClass(CSS.FLOATING);
- } else {
- this.userColumn.removeClass(CSS.FLOATING);
- }
- }
- if (this.footerRow) {
- this.footerRow.setStyles(footerStyles);
- if (footerFloats) {
- this.footerRow.addClass(CSS.FLOATING);
- } else {
- this.footerRow.removeClass(CSS.FLOATING);
- }
- }
-
- // And apply the styles to the generic left headers.
- Y.Object.each(floatingHeaderStyles, function(styles, key) {
- if (this.floatingHeaderRow[key]) {
- this.floatingHeaderRow[key].setStyles(styles);
- }
- }, this);
-
-
- Y.Object.each(this.floatingHeaderRow, function(value, key) {
- if (this.floatingHeaderRow[key]) {
- if (leftTitleFloats) {
- this.floatingHeaderRow[key].addClass(CSS.FLOATING);
- } else {
- this.floatingHeaderRow[key].removeClass(CSS.FLOATING);
- }
- }
- }, this);
-
- // The footer title has a more specific float setting.
- if (floatingFooterTitleRow) {
- if (leftTitleFloats) {
- floatingFooterTitleRow.addClass(CSS.FLOATING);
- } else {
- floatingFooterTitleRow.removeClass(CSS.FLOATING);
- }
- }
-
- },
-
- /**
- * Process a size change Event on the window.
- *
- * @method _handleResizeEvent
- * @protected
- */
- _handleResizeEvent: function() {
- // Recalculate the position of the edge cells for scroll positioning.
- this._calculateCellPositions();
-
- // Simulate a scroll.
- this._handleScrollEvent();
-
- // Resize user cells.
- var userWidth = this.firstUserCell.getComputedStyle(WIDTH);
- var userCells = Y.all(SELECTORS.USERCELL);
- this.userColumnHeader.one('.cell').setStyle('width', userWidth);
- this.userColumn.all('.cell').each(function(cell, idx) {
- var height = userCells.item(idx).getComputedStyle(HEIGHT);
- // Nasty hack to account for Internet Explorer
- if (Y.UA.ie !== 0) {
- var node = userCells.item(idx);
- var allHeight = node.getDOMNode ?
- node.getDOMNode().getBoundingClientRect().height :
- node.get('offsetHeight');
- var marginHeight = parseInt(node.getComputedStyle('marginTop'), 10) +
- parseInt(node.getComputedStyle('marginBottom'), 10);
- var paddingHeight = parseInt(node.getComputedStyle('paddingTop'), 10) +
- parseInt(node.getComputedStyle('paddingBottom'), 10);
- var borderHeight = parseInt(node.getComputedStyle('borderTopWidth'), 10) +
- parseInt(node.getComputedStyle('borderBottomWidth'), 10);
- height = allHeight - marginHeight - paddingHeight - borderHeight;
- }
- cell.setStyles({
- width: userWidth,
- height: height
- });
- }, this);
-
- // Resize headers & footers.
- // This is an expensive operation, not expected to happen often.
- var headers = this.gradeItemHeadingContainer.all('.cell');
- var resizedcells = Y.all(SELECTORS.HEADERCELLS);
-
- var headeroffsetleft = this.headerRow.getX();
- var newcontainerwidth = 0;
- resizedcells.each(function(cell, idx) {
- var headercell = headers.item(idx);
-
- newcontainerwidth += cell.get(OFFSETWIDTH);
- var styles = {
- width: cell.getComputedStyle(WIDTH),
- left: cell.getX() - headeroffsetleft + 'px'
- };
- headercell.setStyles(styles);
- });
-
- if (this.footerRow) {
- var footers = this.footerRow.all('.cell');
- if (footers.size() !== 0) {
- var resizedavgcells = Y.all(SELECTORS.FOOTERCELLS);
-
- resizedavgcells.each(function(cell, idx) {
- var footercell = footers.item(idx);
- var styles = {
- width: cell.getComputedStyle(WIDTH),
- left: cell.getX() - headeroffsetleft + 'px'
- };
- footercell.setStyles(styles);
- });
- }
- }
-
- // Resize the title areas too.
- Y.Object.each(this.floatingHeaderRow, function(row) {
- row.one('div').setStyle('width', userWidth);
- }, this);
-
- this.gradeItemHeadingContainer.setStyle('width', newcontainerwidth);
- }
-
-};
-
-Y.Base.mix(Y.M.gradereport_grader.ReportTable, [FloatingHeaders]);
-
-
-}, '@VERSION@', {"requires": ["base", "node", "event", "handlebars", "overlay", "event-hover"]});
diff --git a/grade/report/grader/yui/src/gradereporttable/build.json b/grade/report/grader/yui/src/gradereporttable/build.json
deleted file mode 100644
index 1c838e87e26..00000000000
--- a/grade/report/grader/yui/src/gradereporttable/build.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "name": "moodle-gradereport_grader-gradereporttable",
- "builds": {
- "moodle-gradereport_grader-gradereporttable": {
- "jsfiles": [
- "gradereporttable.js",
- "floatingheaders.js"
- ]
- }
- }
-}
diff --git a/grade/report/grader/yui/src/gradereporttable/js/floatingheaders.js b/grade/report/grader/yui/src/gradereporttable/js/floatingheaders.js
deleted file mode 100644
index cdd3d810bb1..00000000000
--- a/grade/report/grader/yui/src/gradereporttable/js/floatingheaders.js
+++ /dev/null
@@ -1,1027 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see .
-
-/**
- * @module moodle-gradereport_grader-gradereporttable
- * @submodule floatingheaders
- */
-
-/**
- * Provides floating headers to the grader report.
- *
- * See {{#crossLink "M.gradereport_grader.ReportTable"}}{{/crossLink}} for details.
- *
- * @namespace M.gradereport_grader
- * @class FloatingHeaders
- */
-
-var HEIGHT = 'height',
- WIDTH = 'width',
- OFFSETWIDTH = 'offsetWidth',
- OFFSETHEIGHT = 'offsetHeight',
- LOGNS = 'moodle-core-grade-report-grader';
-
-CSS.FLOATING = 'floating';
-
-function FloatingHeaders() {}
-
-FloatingHeaders.ATTRS = {
-};
-
-FloatingHeaders.prototype = {
- /**
- * The height of the page header if a fixed position, floating header
- * was found.
- *
- * @property pageHeaderHeight
- * @type Number
- * @default 0
- * @protected
- */
- pageHeaderHeight: 0,
-
- /**
- * A Node representing the container div.
- *
- * Positioning will be based on this element, which must have
- * the CSS rule 'position: relative'.
- *
- * @property container
- * @type Node
- * @protected
- */
- container: null,
-
- /**
- * A Node representing the header cell.
- *
- * @property headerCell
- * @type Node
- * @protected
- */
- headerCell: null,
-
- /**
- * A Node representing the header row.
- *
- * @property headerRow
- * @type Node
- * @protected
- */
- headerRow: null,
-
- /**
- * A Node representing the first cell which contains user name information.
- *
- * @property firstUserCell
- * @type Node
- * @protected
- */
- firstUserCell: null,
-
- /**
- * A Node representing the first cell which does not contain a user header.
- *
- * @property firstNonUserCell
- * @type Node
- * @protected
- */
- firstNonUserCell: null,
-
- /**
- * The position of the left of the first non-header cell in a row - the one after the email address.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstNonUserCellLeft
- * @type Number
- * @protected
- */
- firstNonUserCellLeft: 0,
-
- /**
- * The width of the first non-header cell in a row - the one after the email address.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- * This is only used for RTL calculations.
- *
- * @property firstNonUserCellWidth
- * @type Number
- * @protected
- */
- firstNonUserCellWidth: 0,
-
- /**
- * A Node representing the original table footer row.
- *
- * @property tableFooterRow
- * @type Node
- * @protected
- */
- tableFooterRow: null,
-
- /**
- * A Node representing the floating footer row in the grading table.
- *
- * @property footerRow
- * @type Node
- * @protected
- */
- footerRow: null,
-
- /**
- * A Node representing the floating grade item header.
- *
- * @property gradeItemHeadingContainer
- * @type Node
- * @protected
- */
- gradeItemHeadingContainer: null,
-
- /**
- * A Node representing the floating user header. This is the header with the Surname/First name
- * sorting.
- *
- * @property userColumnHeader
- * @type Node
- * @protected
- */
- userColumnHeader: null,
-
- /**
- * A Node representing the floating user column. This is the column containing all of the user
- * names.
- *
- * @property userColumn
- * @type Node
- * @protected
- */
- userColumn: null,
-
- /**
- * The position of the bottom of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstUserCellBottom
- * @type Number
- * @protected
- */
- firstUserCellBottom: 0,
-
- /**
- * The position of the left of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property firstUserCellLeft
- * @type Number
- * @protected
- */
- firstUserCellLeft: 0,
-
- /**
- * The width of the first user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- * This is only used for RTL calculations.
- *
- * @property firstUserCellWidth
- * @type Number
- * @protected
- */
- firstUserCellWidth: 0,
-
- /**
- * The width of the dock if it is visible.
- *
- * @property dockWidth
- * @type Number
- * @protected
- */
- dockWidth: 0,
-
- /**
- * The position of the top of the final user cell.
- * This is used when processing the scroll event as an optimisation. It must be updated when
- * additional rows are loaded, or the window changes in some fashion.
- *
- * @property lastUserCellTop
- * @type Number
- * @protected
- */
- lastUserCellTop: 0,
-
- /**
- * A list of Nodes representing the generic floating rows.
- *
- * @property floatingHeaderRow
- * @type Node{}
- * @protected
- */
- floatingHeaderRow: null,
-
- /**
- * Array of EventHandles.
- *
- * @type EventHandle[]
- * @property _eventHandles
- * @protected
- */
- _eventHandles: [],
-
- /**
- * The last value of the bodyMargin style. We need to recompute positions if it is changed.
- *
- * @property lastBodyMargin
- * @type Number
- * @protected
- */
- lastBodyMargin: 0,
-
- /**
- * Setup the grader report table.
- *
- * @method setupFloatingHeaders
- * @chainable
- */
- setupFloatingHeaders: function() {
- // Grab references to commonly used Nodes.
- this.firstUserCell = Y.one(SELECTORS.USERCELL);
- this.container = Y.one(SELECTORS.GRADEPARENT);
- this.firstNonUserCell = Y.one(SELECTORS.GRADECELL);
-
- if (!this.firstUserCell) {
- // No need for floating elements, there are no users.
- return this;
- }
-
- if (M.cfg.behatsiterunning) {
- // If the behat site is running we don't want floating elements.
- return;
- }
-
- // Generate floating elements.
- this._setupFloatingUserColumn();
- this._setupFloatingUserHeader();
- this._setupFloatingAssignmentHeaders();
- this._setupFloatingAssignmentFooter();
-
- // Setup generic floating left-aligned headers.
- this.floatingHeaderRow = {};
-
- // The 'Controls' row (shown in editing mode when certain options are set).
- this._setupFloatingLeftHeaders('.controls .controls');
-
- // The 'Range' row (shown in editing mode when certain options are set).
- this._setupFloatingLeftHeaders('.range .range');
-
- // The 'Overall Average' field.
- this._setupFloatingLeftHeaders(SELECTORS.FOOTERTITLE);
-
- // Additional setup for the footertitle.
- this._setupFloatingAssignmentFooterTitle();
-
- // Calculate the positions of edge cells. These are used for positioning of the floating headers.
- // This must be called after the floating headers are setup, but before the scroll event handler is invoked.
- this._calculateCellPositions();
-
- // Setup the floating element initial positions by simulating scroll.
- this._handleScrollEvent();
-
- // Setup the event handlers.
- this._setupEventHandlers();
-
- // Listen for a resize event globally - other parts of the code not in this YUI wrapper may make changes to the
- // fields which result in size changes.
- Y.Global.on('moodle-gradereport_grader:resized', this._handleResizeEvent, this);
-
- return this;
- },
-
- /**
- * Calculate the positions of some cells. These values are used heavily
- * in scroll event handling.
- *
- * @method _calculateCellPositions
- * @protected
- */
- _calculateCellPositions: function() {
- // The header row shows the grade item headers and is floated to the top of the window.
- this.headerRowTop = this.headerRow.getY();
-
- // The footer row shows the grade averages and will be floated to the page bottom.
- if (this.tableFooterRow) {
- this.footerRowPosition = this.tableFooterRow.getY();
- }
-
- // Add the width of the dock if it is visible.
- this.dockWidth = 0;
- var dock = Y.one('.has_dock #dock');
- if (dock) {
- this.dockWidth = dock.get(OFFSETWIDTH);
- }
-
- var userCellList = Y.all(SELECTORS.USERCELL);
-
- // The left of the user cells matches the left of the headerRow.
- this.firstUserCellLeft = this.firstUserCell.getX();
- this.firstUserCellWidth = this.firstUserCell.get(OFFSETWIDTH);
-
- // The left of the user cells matches the left of the footer title.
- this.firstNonUserCellLeft = this.firstNonUserCell.getX();
- this.firstNonUserCellWidth = this.firstNonUserCell.get(OFFSETWIDTH);
-
- if (userCellList.size() > 1) {
- // Use the top of the second cell for the bottom of the first cell.
- // This is used when scrolling to fix the footer to the top edge of the window.
- var firstUserCell = userCellList.item(1);
- this.firstUserCellBottom = firstUserCell.getY() + parseInt(firstUserCell.getComputedStyle(HEIGHT), 10);
-
- // Use the top of the penultimate cell when scrolling the header.
- // The header is the same size as the cells.
- this.lastUserCellTop = userCellList.item(userCellList.size() - 2).getY();
- } else {
- var firstItem = userCellList.item(0);
- // We can't use the top of the second row as there is only one row.
- this.lastUserCellTop = firstItem.getY();
-
- if (this.tableFooterRow) {
- // The footer is present so we can use that.
- this.firstUserCellBottom = this.footerRowPosition + parseInt(this.tableFooterRow.getComputedStyle(HEIGHT), 10);
- } else {
- // No other clues - calculate the top instead.
- this.firstUserCellBottom = firstItem.getY() + firstItem.get('offsetHeight');
- }
- }
-
- // Check whether a header is present and whether it is floating.
- var header = Y.one('header');
- this.pageHeaderHeight = 0;
- if (header) {
- if (header.getComputedStyle('position') === 'fixed') {
- this.pageHeaderHeight = header.get(OFFSETHEIGHT);
- } else {
- var navbar = Y.one('.navbar');
-
- if (navbar && navbar.getComputedStyle('position') === 'fixed') {
- // If the navbar exists and isn't fixed, we need to offset the page header to accommodate for it.
- this.pageHeaderHeight = navbar.get(OFFSETHEIGHT);
- }
- }
- }
- },
-
- /**
- * Get the relative XY of the node.
- *
- * @method _getRelativeXY
- * @protected
- * @param {Node} node The node to get the position of.
- * @return {Array} Containing X and Y.
- */
- _getRelativeXY: function(node) {
- return this._getRelativeXYFromXY(node.getX(), node.getY());
- },
-
- /**
- * Get the relative positioning from coordinates.
- *
- * This gives the position according to the parent of the table, which must
- * be set as position: relative.
- *
- * @method _getRelativeXYFromXY
- * @protected
- * @param {Number} x X position.
- * @param {Number} y Y position.
- * @return {Array} Containing X and Y.
- */
- _getRelativeXYFromXY: function(x, y) {
- var parentXY = this.container.getXY();
- return [x - parentXY[0], y - parentXY[1]];
- },
-
- /**
- * Get the relative positioning of an elements from coordinates.
- *
- * @method _getRelativeXFromX
- * @protected
- * @param {Number} pos X position.
- * @return {Number} relative X position.
- */
- _getRelativeXFromX: function(pos) {
- return this._getRelativeXYFromXY(pos, 0)[0];
- },
-
- /**
- * Get the relative positioning of an elements from coordinates.
- *
- * @method _getRelativeYFromY
- * @protected
- * @param {Number} pos Y position.
- * @return {Number} relative Y position.
- */
- _getRelativeYFromY: function(pos) {
- return this._getRelativeXYFromXY(0, pos)[1];
- },
-
- /**
- * Return the size of the horizontal scrollbar.
- *
- * @method _getScrollBarHeight
- * @protected
- * @return {Number} Height of the scrollbar.
- */
- _getScrollBarHeight: function() {
- if (Y.UA.ie && Y.UA.ie >= 10) {
- // IE has transparent scrollbars, which sometimes disappear... it's better to ignore them.
- return 0;
- } else if (Y.config.doc.body.scrollWidth > Y.config.doc.body.clientWidth) {
- // The document can be horizontally scrolled.
- return Y.DOM.getScrollbarWidth();
- }
- return 0;
- },
-
- /**
- * Setup the main event listeners.
- * These deal with things like window events.
- *
- * @method _setupEventHandlers
- * @protected
- */
- _setupEventHandlers: function() {
- this._eventHandles.push(
- // Listen for window scrolls, resizes, and rotation events.
- Y.one(Y.config.win).on('scroll', this._handleScrollEvent, this),
- Y.one(Y.config.win).on('resize', this._handleResizeEvent, this),
- Y.one(Y.config.win).on('orientationchange', this._handleResizeEvent, this),
- Y.Global.on('dock:shown', this._handleResizeEvent, this),
- Y.Global.on('dock:hidden', this._handleResizeEvent, this)
- );
- },
-
- /**
- * Create and setup the floating column of user names.
- *
- * @method _setupFloatingUserColumn
- * @protected
- */
- _setupFloatingUserColumn: function() {
- // Grab all cells in the user names column.
- var userColumn = Y.all(SELECTORS.USERCELL),
-
- // Create a floating table.
- floatingUserColumn = Y.Node.create(''),
-
- // Get the XY for the floating element.
- coordinates = this._getRelativeXY(this.firstUserCell);
-
- // Generate the new fields.
- userColumn.each(function(node) {
- var height = node.getComputedStyle(HEIGHT);
- // Nasty hack to account for Internet Explorer
- if (Y.UA.ie !== 0) {
- var allHeight = node.get('offsetHeight');
- var marginHeight = parseInt(node.getComputedStyle('marginTop'), 10) +
- parseInt(node.getComputedStyle('marginBottom'), 10);
- var paddingHeight = parseInt(node.getComputedStyle('paddingTop'), 10) +
- parseInt(node.getComputedStyle('paddingBottom'), 10);
- var borderHeight = parseInt(node.getComputedStyle('borderTopWidth'), 10) +
- parseInt(node.getComputedStyle('borderBottomWidth'), 10);
- height = allHeight - marginHeight - paddingHeight - borderHeight;
- }
- // Create and configure the new container.
- var containerNode = Y.Node.create('');
- containerNode.set('innerHTML', node.get('innerHTML'))
- .setAttribute('class', node.getAttribute('class'))
- .setAttribute('data-uid', node.ancestor('tr').getData('uid'))
- .setStyles({
- height: height,
- width: node.getComputedStyle(WIDTH)
- });
-
- // Add the new nodes to our floating table.
- floatingUserColumn.appendChild(containerNode);
- }, this);
-
- // Style the floating user container.
- floatingUserColumn.setStyles({
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px'
- });
-
- // Append to the grader region.
- this.graderRegion.append(floatingUserColumn);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.userColumn = floatingUserColumn;
- },
-
- /**
- * Create and setup the floating username header cell.
- *
- * @method _setupFloatingUserHeader
- * @protected
- */
- _setupFloatingUserHeader: function() {
- // We make various references to the header cells. Store it for later.
- this.headerRow = Y.one(SELECTORS.HEADERROW);
- this.headerCell = Y.one(SELECTORS.STUDENTHEADER);
-
- // Create the floating row and cell.
- var floatingUserHeaderRow = Y.Node.create(''),
- floatingUserHeaderCell = Y.Node.create(''),
- nodepos = this._getRelativeXY(this.headerCell)[0],
- coordinates = this._getRelativeXY(this.headerRow),
- gradeHeadersOffset = coordinates[0];
-
- // Append the content and style to the floating cell.
- floatingUserHeaderCell
- .set('innerHTML', this.headerCell.getHTML())
- .setAttribute('class', this.headerCell.getAttribute('class'))
- .setStyles({
- // The header is larger than the user cells, so we take the user cell.
- width: this.firstUserCell.getComputedStyle(WIDTH),
- left: (nodepos - gradeHeadersOffset) + 'px'
- });
-
- // Style the floating row.
- floatingUserHeaderRow
- .setStyles({
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px'
- });
-
- // Append the cell to the row, and finally to the region.
- floatingUserHeaderRow.append(floatingUserHeaderCell);
- this.graderRegion.append(floatingUserHeaderRow);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.userColumnHeader = floatingUserHeaderRow;
- },
-
- /**
- * Create and setup the floating grade item header row.
- *
- * @method _setupFloatingAssignmentHeaders
- * @protected
- */
- _setupFloatingAssignmentHeaders: function() {
- this.headerRow = Y.one('#user-grades tr.heading');
-
- var gradeHeaders = Y.all('#user-grades tr.heading .cell');
-
- // Generate a floating headers
- var floatingGradeHeaders = Y.Node.create('');
-
- var coordinates = this._getRelativeXY(this.headerRow);
-
- var floatingGradeHeadersWidth = 0;
- var floatingGradeHeadersHeight = 0;
- var gradeHeadersOffset = coordinates[0];
-
- gradeHeaders.each(function(node) {
- var nodepos = this._getRelativeXY(node)[0];
-
- var newnode = Y.Node.create('');
- newnode.append(node.getHTML())
- .setAttribute('class', node.getAttribute('class'))
- .setData('itemid', node.getData('itemid'))
- .setStyles({
- height: node.getComputedStyle(HEIGHT),
- left: (nodepos - gradeHeadersOffset) + 'px',
- position: 'absolute',
- width: node.getComputedStyle(WIDTH)
- });
-
- // Sum up total widths - these are used in the container styles.
- // Use the offsetHeight and Width here as this contains the
- // padding, margin, and borders.
- floatingGradeHeadersWidth += parseInt(node.get(OFFSETWIDTH), 10);
- floatingGradeHeadersHeight = node.get(OFFSETHEIGHT);
-
- // Append to our floating table.
- floatingGradeHeaders.appendChild(newnode);
- }, this);
-
- // Position header table.
- floatingGradeHeaders.setStyles({
- height: floatingGradeHeadersHeight + 'px',
- left: coordinates[0] + 'px',
- position: 'absolute',
- top: coordinates[1] + 'px',
- width: floatingGradeHeadersWidth + 'px'
- });
-
- // Insert in place before the grader headers.
- this.userColumnHeader.insert(floatingGradeHeaders, 'before');
-
- // Store a reference to this for later - we use it in the event handlers.
- this.gradeItemHeadingContainer = floatingGradeHeaders;
- },
-
- /**
- * Create and setup the floating header row of grade item titles.
- *
- * @method _setupFloatingAssignmentFooter
- * @protected
- */
- _setupFloatingAssignmentFooter: function() {
- this.tableFooterRow = Y.one('#user-grades .avg');
- if (!this.tableFooterRow) {
- Y.log('Averages footer not found - unable to float it.', 'warn', LOGNS);
- return;
- }
-
- // Generate the sticky footer row.
- var footerCells = this.tableFooterRow.all('.cell');
-
- // Create a container.
- var floatingGraderFooter = Y.Node.create('');
- var footerWidth = 0;
- var coordinates = this._getRelativeXY(this.tableFooterRow);
- var footerRowOffset = coordinates[0];
- var floatingGraderFooterHeight = 0;
-
- // Copy cell content.
- footerCells.each(function(node) {
- var newnode = Y.Node.create('');
- var nodepos = this._getRelativeXY(node)[0];
- newnode.set('innerHTML', node.getHTML())
- .setAttribute('class', node.getAttribute('class'))
- .setStyles({
- height: node.getComputedStyle(HEIGHT),
- left: (nodepos - footerRowOffset) + 'px',
- position: 'absolute',
- width: node.getComputedStyle(WIDTH)
- });
-
- floatingGraderFooter.append(newnode);
- floatingGraderFooterHeight = node.get(OFFSETHEIGHT);
- footerWidth += parseInt(node.get(OFFSETWIDTH), 10);
- }, this);
-
- // Position the row.
- floatingGraderFooter.setStyles({
- position: 'absolute',
- left: coordinates[0] + 'px',
- bottom: '1px',
- height: floatingGraderFooterHeight + 'px',
- width: footerWidth + 'px'
- });
-
- // Append to the grader region.
- this.graderRegion.append(floatingGraderFooter);
-
- this.footerRow = floatingGraderFooter;
- },
-
- /**
- * Create and setup the floating footer title cell.
- *
- * @method _setupFloatingAssignmentFooterTitle
- * @protected
- */
- _setupFloatingAssignmentFooterTitle: function() {
- var floatingFooterRow = this.floatingHeaderRow[SELECTORS.FOOTERTITLE];
- if (floatingFooterRow) {
- // Style the floating row.
- floatingFooterRow
- .setStyles({
- bottom: '1px'
- });
- }
- },
-
- /**
- * Create and setup the floating left headers.
- *
- * @method _setupFloatingLeftHeaders
- * @protected
- */
- _setupFloatingLeftHeaders: function(headerSelector) {
- // We make various references to the origin cell. Store it for later.
- var origin = Y.one(headerSelector);
-
- if (!origin) {
- return;
- }
-
- // Create the floating row and cell.
- var floatingRow = Y.Node.create(''),
- floatingCell = Y.Node.create(''),
- coordinates = this._getRelativeXY(origin),
- width = this.firstUserCell.getComputedStyle(WIDTH),
- height = origin.get(OFFSETHEIGHT);
-
- // Append the content and style to the floating cell.
- floatingCell
- .set('innerHTML', origin.getHTML())
- .setAttribute('class', origin.getAttribute('class'))
- .setStyles({
- // The header is larger than the user cells, so we take the user cell.
- width: width
- });
-
- // Style the floating row.
- floatingRow
- .setStyles({
- position: 'absolute',
- top: coordinates[1] + 'px',
- left: coordinates[0] + 'px',
- height: height + 'px'
- })
- // Add all classes from the parent to the row
- .addClass(origin.get('parentNode').get('className'));
-
- // Append the cell to the row, and finally to the region.
- floatingRow.append(floatingCell);
- this.graderRegion.append(floatingRow);
-
- // Store a reference to this for later - we use it in the event handlers.
- this.floatingHeaderRow[headerSelector] = floatingRow;
- },
-
- /**
- * Process a Scroll Event on the window.
- *
- * @method _handleScrollEvent
- * @protected
- */
- _handleScrollEvent: function() {
- // Performance is important in this function as it is called frequently and in quick succesion.
- // To prevent layout thrashing when the DOM is repeatedly updated and queried, updated and queried,
- // updates must be batched.
-
- // Next do all the calculations.
- var gradeItemHeadingContainerStyles = {},
- userColumnHeaderStyles = {},
- userColumnStyles = {},
- footerStyles = {},
- coord = 0,
- floatingUserTriggerPoint = 0, // The X position at which the floating should start.
- floatingUserRelativePoint = 0, // The point to use when calculating the new position.
- headerFloats = false,
- userFloats = false,
- footerFloats = false,
- leftTitleFloats = false,
- floatingHeaderStyles = {},
- floatingFooterTitleStyles = {},
- floatingFooterTitleRow = false,
- bodyMargin = 0;
-
- if (window.right_to_left()) {
- bodyMargin = parseInt(Y.one(Y.config.doc.body).getComputedStyle('marginRight'), 10);
- } else {
- bodyMargin = parseInt(Y.one(Y.config.doc.body).getComputedStyle('marginLeft'), 10);
- }
-
- if (bodyMargin != this.lastBodyMargin) {
- // Recalculate the position of the edge cells for scroll positioning.
- this._calculateCellPositions();
- }
-
- // Header position.
- gradeItemHeadingContainerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
- if (Y.config.win.pageYOffset + this.pageHeaderHeight > this.headerRowTop) {
- headerFloats = true;
- if (Y.config.win.pageYOffset + this.pageHeaderHeight < this.lastUserCellTop) {
- coord = this._getRelativeYFromY(Y.config.win.pageYOffset + this.pageHeaderHeight);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- } else {
- coord = this._getRelativeYFromY(this.lastUserCellTop);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- }
- } else {
- headerFloats = false;
- coord = this._getRelativeYFromY(this.headerRowTop);
- gradeItemHeadingContainerStyles.top = coord + 'px';
- userColumnHeaderStyles.top = coord + 'px';
- }
-
- // User column position.
-
- if (window.right_to_left()) {
- floatingUserTriggerPoint = Y.config.win.innerWidth + Y.config.win.pageXOffset - this.dockWidth;
- floatingUserRelativePoint = floatingUserTriggerPoint - this.firstUserCellWidth - bodyMargin;
- userFloats = floatingUserTriggerPoint < (this.firstUserCellLeft + this.firstUserCellWidth + bodyMargin);
- leftTitleFloats = (floatingUserTriggerPoint - this.firstNonUserCellWidth) <
- (this.firstNonUserCellLeft + this.firstUserCellWidth);
- } else {
- floatingUserRelativePoint = Y.config.win.pageXOffset + bodyMargin;
- floatingUserTriggerPoint = floatingUserRelativePoint + this.dockWidth + bodyMargin;
- userFloats = floatingUserTriggerPoint > this.firstUserCellLeft + bodyMargin;
- leftTitleFloats = floatingUserTriggerPoint > (this.firstNonUserCellLeft - this.firstUserCellWidth);
- }
-
- if (userFloats) {
- coord = this._getRelativeXFromX(floatingUserRelativePoint);
- userColumnStyles.left = coord + 'px';
- userColumnHeaderStyles.left = coord + 'px';
- } else {
- coord = this._getRelativeXFromX(this.firstUserCellLeft);
- userColumnStyles.left = coord + 'px';
- userColumnHeaderStyles.left = coord + 'px';
- }
-
- // Update the miscellaneous left-only floats.
- Y.Object.each(this.floatingHeaderRow, function(origin, key) {
- floatingHeaderStyles[key] = {
- left: userColumnStyles.left
- };
- }, this);
-
- // Update footer.
- if (this.footerRow) {
- footerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
-
- // Determine whether the footer should now be shown as sticky.
- var pageHeight = Y.config.win.innerHeight,
- pageOffset = Y.config.win.pageYOffset,
- bottomScrollPosition = pageHeight - this._getScrollBarHeight() + pageOffset,
- footerRowHeight = parseInt(this.footerRow.getComputedStyle(HEIGHT), 10),
- footerBottomPosition = footerRowHeight + this.footerRowPosition;
-
- floatingFooterTitleStyles = floatingHeaderStyles[SELECTORS.FOOTERTITLE];
- floatingFooterTitleRow = this.floatingHeaderRow[SELECTORS.FOOTERTITLE];
- if (bottomScrollPosition < footerBottomPosition && bottomScrollPosition > this.firstUserCellBottom) {
- // We have not scrolled below the footer, nor above the first row.
- footerStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
- footerFloats = true;
- } else {
- // The footer should not float any more.
- footerStyles.bottom = '1px';
- footerFloats = false;
- }
- if (floatingFooterTitleStyles) {
- floatingFooterTitleStyles.bottom = footerStyles.bottom;
- floatingFooterTitleStyles.top = null;
- }
- floatingHeaderStyles[SELECTORS.FOOTERTITLE] = floatingFooterTitleStyles;
- }
-
- // Apply the styles and mark elements as floating, or not.
- if (this.gradeItemHeadingContainer) {
- this.gradeItemHeadingContainer.setStyles(gradeItemHeadingContainerStyles);
- if (headerFloats) {
- this.gradeItemHeadingContainer.addClass(CSS.FLOATING);
- } else {
- this.gradeItemHeadingContainer.removeClass(CSS.FLOATING);
- }
- }
- if (this.userColumnHeader) {
- this.userColumnHeader.setStyles(userColumnHeaderStyles);
- if (userFloats) {
- this.userColumnHeader.addClass(CSS.FLOATING);
- } else {
- this.userColumnHeader.removeClass(CSS.FLOATING);
- }
- }
- if (this.userColumn) {
- this.userColumn.setStyles(userColumnStyles);
- if (userFloats) {
- this.userColumn.addClass(CSS.FLOATING);
- } else {
- this.userColumn.removeClass(CSS.FLOATING);
- }
- }
- if (this.footerRow) {
- this.footerRow.setStyles(footerStyles);
- if (footerFloats) {
- this.footerRow.addClass(CSS.FLOATING);
- } else {
- this.footerRow.removeClass(CSS.FLOATING);
- }
- }
-
- // And apply the styles to the generic left headers.
- Y.Object.each(floatingHeaderStyles, function(styles, key) {
- if (this.floatingHeaderRow[key]) {
- this.floatingHeaderRow[key].setStyles(styles);
- }
- }, this);
-
-
- Y.Object.each(this.floatingHeaderRow, function(value, key) {
- if (this.floatingHeaderRow[key]) {
- if (leftTitleFloats) {
- this.floatingHeaderRow[key].addClass(CSS.FLOATING);
- } else {
- this.floatingHeaderRow[key].removeClass(CSS.FLOATING);
- }
- }
- }, this);
-
- // The footer title has a more specific float setting.
- if (floatingFooterTitleRow) {
- if (leftTitleFloats) {
- floatingFooterTitleRow.addClass(CSS.FLOATING);
- } else {
- floatingFooterTitleRow.removeClass(CSS.FLOATING);
- }
- }
-
- },
-
- /**
- * Process a size change Event on the window.
- *
- * @method _handleResizeEvent
- * @protected
- */
- _handleResizeEvent: function() {
- // Recalculate the position of the edge cells for scroll positioning.
- this._calculateCellPositions();
-
- // Simulate a scroll.
- this._handleScrollEvent();
-
- // Resize user cells.
- var userWidth = this.firstUserCell.getComputedStyle(WIDTH);
- var userCells = Y.all(SELECTORS.USERCELL);
- this.userColumnHeader.one('.cell').setStyle('width', userWidth);
- this.userColumn.all('.cell').each(function(cell, idx) {
- var height = userCells.item(idx).getComputedStyle(HEIGHT);
- // Nasty hack to account for Internet Explorer
- if (Y.UA.ie !== 0) {
- var node = userCells.item(idx);
- var allHeight = node.getDOMNode ?
- node.getDOMNode().getBoundingClientRect().height :
- node.get('offsetHeight');
- var marginHeight = parseInt(node.getComputedStyle('marginTop'), 10) +
- parseInt(node.getComputedStyle('marginBottom'), 10);
- var paddingHeight = parseInt(node.getComputedStyle('paddingTop'), 10) +
- parseInt(node.getComputedStyle('paddingBottom'), 10);
- var borderHeight = parseInt(node.getComputedStyle('borderTopWidth'), 10) +
- parseInt(node.getComputedStyle('borderBottomWidth'), 10);
- height = allHeight - marginHeight - paddingHeight - borderHeight;
- }
- cell.setStyles({
- width: userWidth,
- height: height
- });
- }, this);
-
- // Resize headers & footers.
- // This is an expensive operation, not expected to happen often.
- var headers = this.gradeItemHeadingContainer.all('.cell');
- var resizedcells = Y.all(SELECTORS.HEADERCELLS);
-
- var headeroffsetleft = this.headerRow.getX();
- var newcontainerwidth = 0;
- resizedcells.each(function(cell, idx) {
- var headercell = headers.item(idx);
-
- newcontainerwidth += cell.get(OFFSETWIDTH);
- var styles = {
- width: cell.getComputedStyle(WIDTH),
- left: cell.getX() - headeroffsetleft + 'px'
- };
- headercell.setStyles(styles);
- });
-
- if (this.footerRow) {
- var footers = this.footerRow.all('.cell');
- if (footers.size() !== 0) {
- var resizedavgcells = Y.all(SELECTORS.FOOTERCELLS);
-
- resizedavgcells.each(function(cell, idx) {
- var footercell = footers.item(idx);
- var styles = {
- width: cell.getComputedStyle(WIDTH),
- left: cell.getX() - headeroffsetleft + 'px'
- };
- footercell.setStyles(styles);
- });
- }
- }
-
- // Resize the title areas too.
- Y.Object.each(this.floatingHeaderRow, function(row) {
- row.one('div').setStyle('width', userWidth);
- }, this);
-
- this.gradeItemHeadingContainer.setStyle('width', newcontainerwidth);
- }
-
-};
-
-Y.Base.mix(Y.M.gradereport_grader.ReportTable, [FloatingHeaders]);
diff --git a/grade/report/grader/yui/src/gradereporttable/js/gradereporttable.js b/grade/report/grader/yui/src/gradereporttable/js/gradereporttable.js
deleted file mode 100644
index 8cc3e1088ad..00000000000
--- a/grade/report/grader/yui/src/gradereporttable/js/gradereporttable.js
+++ /dev/null
@@ -1,142 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see .
-/* eslint-disable no-unused-vars */
-
-/**
- * Grader Report Functionality.
- *
- * @module moodle-gradereport_grader-gradereporttable
- * @package gradereport_grader
- * @copyright 2014 UC Regents
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @author Alfonso Roman
- */
-
-/**
- * @module moodle-gradereport_grader-gradereporttable
- */
-
-var SELECTORS = {
- FOOTERTITLE: '.avg .header',
- FOOTERCELLS: '#user-grades .avg .cell',
- FOOTERROW: '#user-grades .avg',
- GRADECELL: 'td.grade',
- GRADERTABLE: '.gradeparent table',
- GRADEPARENT: '.gradeparent',
- HEADERCELLS: '#user-grades .heading .cell',
- HEADERCELL: '.gradebook-header-cell',
- HEADERROW: '#user-grades tr.heading',
- STUDENTHEADER: '#studentheader',
- USERCELL: '#user-grades .user.cell',
- USERMAIL: '#user-grades .useremail'
- },
- CSS = {
- OVERRIDDEN: 'overridden',
- TOOLTIPACTIVE: 'tooltipactive'
- };
-
-/**
- * The Grader Report Table.
- *
- * @namespace M.gradereport_grader
- * @class ReportTable
- * @constructor
- */
-function ReportTable() {
- ReportTable.superclass.constructor.apply(this, arguments);
-}
-
-Y.extend(ReportTable, Y.Base, {
- /**
- * Array of EventHandles.
- *
- * @type EventHandle[]
- * @property _eventHandles
- * @protected
- */
- _eventHandles: [],
-
- /**
- * A Node reference to the grader table.
- *
- * @property graderTable
- * @type Node
- */
- graderTable: null,
-
- /**
- * Setup the grader report table.
- *
- * @method initializer
- */
- initializer: function() {
- // Some useful references within our target area.
- this.graderRegion = Y.one(SELECTORS.GRADEPARENT);
- this.graderTable = Y.one(SELECTORS.GRADERTABLE);
-
- // Setup the floating headers.
- this.setupFloatingHeaders();
- },
-
- /**
- * Get the text content of the username for the specified grade item.
- *
- * @method getGradeUserName
- * @param {Node} cell The grade item cell to obtain the username for
- * @return {String} The string content of the username cell.
- */
- getGradeUserName: function(cell) {
- var userrow = cell.ancestor('tr'),
- usercell = userrow.one("th.user .username");
-
- if (usercell) {
- return usercell.get('text');
- } else {
- return '';
- }
- },
-
- /**
- * Get the text content of the item name for the specified grade item.
- *
- * @method getGradeItemName
- * @param {Node} cell The grade item cell to obtain the item name for
- * @return {String} The string content of the item name cell.
- */
- getGradeItemName: function(cell) {
- var itemcell = Y.one("th.item[data-itemid='" + cell.getData('itemid') + "']");
- if (itemcell) {
- return itemcell.get('text');
- } else {
- return '';
- }
- },
-
- /**
- * Get the text content of any feedback associated with the grade item.
- *
- * @method getGradeFeedback
- * @param {Node} cell The grade item cell to obtain the item name for
- * @return {String} The string content of the feedback.
- */
- getGradeFeedback: function(cell) {
- return cell.getData('feedback');
- }
-});
-
-Y.namespace('M.gradereport_grader').ReportTable = ReportTable;
-Y.namespace('M.gradereport_grader').init = function(config) {
- return new Y.M.gradereport_grader.ReportTable(config);
-};
diff --git a/grade/report/grader/yui/src/gradereporttable/meta/gradereporttable.json b/grade/report/grader/yui/src/gradereporttable/meta/gradereporttable.json
deleted file mode 100644
index 2fa70f2e0e5..00000000000
--- a/grade/report/grader/yui/src/gradereporttable/meta/gradereporttable.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "moodle-gradereport_grader-gradereporttable": {
- "requires": [
- "base",
- "node",
- "event",
- "handlebars",
- "overlay",
- "event-hover"
- ]
- }
-}
diff --git a/theme/boost/scss/moodle/debug.scss b/theme/boost/scss/moodle/debug.scss
index 6f09d467c83..0a00837a731 100644
--- a/theme/boost/scss/moodle/debug.scss
+++ b/theme/boost/scss/moodle/debug.scss
@@ -51,6 +51,18 @@ body.behat-site {
&.jsenabled #page-footer .footer-content-popover {
display: block;
}
+
+ &.path-grade-report-grader .gradeparent {
+ tr.heading,
+ tr.lastrow,
+ th.header {
+ position: relative;
+ left: auto;
+ }
+ tr.heading {
+ top: auto;
+ }
+ }
}
.phpinfo table,
diff --git a/theme/boost/scss/moodle/grade.scss b/theme/boost/scss/moodle/grade.scss
index 82d2946f72f..5702b3b9dca 100644
--- a/theme/boost/scss/moodle/grade.scss
+++ b/theme/boost/scss/moodle/grade.scss
@@ -424,6 +424,32 @@
table .clickable {
cursor: pointer;
}
+
+ tr.heading {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ tr.lastrow {
+ position: sticky;
+ bottom: 0;
+ z-index: 1;
+
+ td,
+ th {
+ border-top: 1px solid $table-border-color;
+ }
+ }
+
+ th.header {
+ position: sticky;
+ left: -3rem;
+ }
+
+ td.noborder {
+ border-right: transparent;
+ }
}
}
diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css
index 07a01806c0c..5175bf2766c 100644
--- a/theme/boost/style/moodle.css
+++ b/theme/boost/style/moodle.css
@@ -19522,6 +19522,26 @@ p.arrow_button {
.path-grade-report-grader .gradeparent table .clickable {
cursor: pointer; }
+.path-grade-report-grader .gradeparent tr.heading {
+ position: sticky;
+ top: 0;
+ z-index: 1; }
+
+.path-grade-report-grader .gradeparent tr.lastrow {
+ position: sticky;
+ bottom: 0;
+ z-index: 1; }
+ .path-grade-report-grader .gradeparent tr.lastrow td,
+ .path-grade-report-grader .gradeparent tr.lastrow th {
+ border-top: 1px solid #dee2e6; }
+
+.path-grade-report-grader .gradeparent th.header {
+ position: sticky;
+ left: -3rem; }
+
+.path-grade-report-grader .gradeparent td.noborder {
+ border-right: transparent; }
+
/**
* User report.
*/
@@ -19803,6 +19823,15 @@ body.behat-site [data-region="message-drawer"] {
body.behat-site.jsenabled #page-footer .footer-content-popover {
display: block; }
+body.behat-site.path-grade-report-grader .gradeparent tr.heading,
+body.behat-site.path-grade-report-grader .gradeparent tr.lastrow,
+body.behat-site.path-grade-report-grader .gradeparent th.header {
+ position: relative;
+ left: auto; }
+
+body.behat-site.path-grade-report-grader .gradeparent tr.heading {
+ top: auto; }
+
.phpinfo table,
.phpinfo th,
.phpinfo h2 {
diff --git a/theme/classic/scss/classic/post.scss b/theme/classic/scss/classic/post.scss
index 4ac7e52b325..573225d612b 100644
--- a/theme/classic/scss/classic/post.scss
+++ b/theme/classic/scss/classic/post.scss
@@ -116,6 +116,16 @@
}
}
+.path-grade-report-grader .gradeparent {
+ tr.heading {
+ top: $navbar-height;
+ }
+
+ th.header {
+ left: 0;
+ }
+}
+
// The block column needs some padding on small devices.
@include media-breakpoint-down(sm) {
.blockcolumn,
diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css
index 6e7eee23b33..4e88f2c7a38 100644
--- a/theme/classic/style/moodle.css
+++ b/theme/classic/style/moodle.css
@@ -19522,6 +19522,26 @@ p.arrow_button {
.path-grade-report-grader .gradeparent table .clickable {
cursor: pointer; }
+.path-grade-report-grader .gradeparent tr.heading {
+ position: sticky;
+ top: 0;
+ z-index: 1; }
+
+.path-grade-report-grader .gradeparent tr.lastrow {
+ position: sticky;
+ bottom: 0;
+ z-index: 1; }
+ .path-grade-report-grader .gradeparent tr.lastrow td,
+ .path-grade-report-grader .gradeparent tr.lastrow th {
+ border-top: 1px solid #dee2e6; }
+
+.path-grade-report-grader .gradeparent th.header {
+ position: sticky;
+ left: -3rem; }
+
+.path-grade-report-grader .gradeparent td.noborder {
+ border-right: transparent; }
+
/**
* User report.
*/
@@ -19749,6 +19769,15 @@ body.behat-site [data-region="message-drawer"] {
body.behat-site.jsenabled #page-footer .footer-content-popover {
display: block; }
+body.behat-site.path-grade-report-grader .gradeparent tr.heading,
+body.behat-site.path-grade-report-grader .gradeparent tr.lastrow,
+body.behat-site.path-grade-report-grader .gradeparent th.header {
+ position: relative;
+ left: auto; }
+
+body.behat-site.path-grade-report-grader .gradeparent tr.heading {
+ top: auto; }
+
.phpinfo table,
.phpinfo th,
.phpinfo h2 {
@@ -21544,6 +21573,12 @@ body {
.show > .btn-outline-warning.dropdown-toggle:focus {
box-shadow: 0 0 0 0.2rem rgba(166, 103, 14, 0.5); }
+.path-grade-report-grader .gradeparent tr.heading {
+ top: 50px; }
+
+.path-grade-report-grader .gradeparent th.header {
+ left: 0; }
+
@media (max-width: 767.98px) {
.blockcolumn,
.region-main {