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 {