MDL-47045 gradereport_grader: Improve Grader report user interface

This issue is a part of the MDL-46658 Task.
This issue is a part of the MDL-25544 Epic.
This commit is contained in:
Alfonso Roman
2014-10-03 15:12:36 +08:00
committed by Andrew Nicols
parent 6597413d41
commit 2c4a3f7525
11 changed files with 1585 additions and 207 deletions
+11 -1
View File
@@ -52,7 +52,8 @@ if (isset($graderreportsilast)) {
}
$PAGE->set_url(new moodle_url('/grade/report/grader/index.php', array('id'=>$courseid)));
$PAGE->requires->yui_module('moodle-gradereport_grader-scrollview', 'M.gradereport_grader.scrollview.init');
//$PAGE->requires->yui_module('moodle-gradereport_grader-scrollview', 'M.gradereport_grader.scrollview.init');
$PAGE->requires->yui_module('moodle-gradereport_grader-gradereporttable', 'M.gradereport_grader.gradereporttable.init', array());
// basic access checks
if (!$course = $DB->get_record('course', array('id' => $courseid))) {
@@ -193,6 +194,15 @@ if ($USER->gradeediting[$course->id] && ($report->get_pref('showquickfeedback')
echo $reporthtml;
}
// Render a loading screen.
$loading = html_writer::div(
html_writer::div(
'<i></i><i></i><i></i><i></i>', 'gradebook-loading'
), 'gradebook-loading-screen', array('aria-hidden' => 'true')
);
echo $loading;
// prints paging bar at bottom for large pages
if (!empty($studentsperpage) && $studentsperpage >= 20) {
echo $OUTPUT->paging_bar($numusers, $report->page, $studentsperpage, $report->pbarurl);
+1 -1
View File
@@ -1137,7 +1137,7 @@ class grade_report_grader extends grade_report {
$html .= $OUTPUT->container(html_writer::table($righttable), 'right_scroller');
} else {
$fulltable = new html_table();
$fulltable->attributes['class'] = 'gradestable flexible boxaligncenter generaltable';
$fulltable->attributes['class'] = 'table table-bordered table-striped table-condensed';
$fulltable->id = 'user-grades';
// Extract rows from each side (left and right) and collate them into one row each
+2 -1
View File
@@ -56,7 +56,8 @@ M.gradereport_grader = {
return;
}
var content = '<div class="graderreportoverlay" role="tooltip" aria-describedby="' + properties.id + '">';
var overriden = /.*overridden.*/g.test(properties.cell.getAttribute('class')) ? 'overriden' : '';
var content = '<div class="graderreportoverlay ' + overriden + '" role="tooltip" aria-describedby="' + properties.id + '">';
content += '<div class="fullname">'+properties.username+'</div><div class="itemname">'+properties.itemname+'</div>';
if (properties.feedback) {
content += '<div class="feedback">'+properties.feedback+'</div>';
Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

+370 -204
View File
@@ -1,75 +1,142 @@
.path-grade-report-grader .flexible th {
white-space: normal;
.path-grade-report-grader .yui3-overlay {
border: 0;
background: none;
background-color: 'initial';
min-width: 200px;
}
.gradestable {
margin-bottom: 0;
.path-grade-report-grader .graderreportoverlay {
background-color: white;
width: auto;
padding: 10px;
border-color: #ddd;
font-size: 12px;
-webkit-box-shadow: #999999 2px 2px 3px;
-moz-box-shadow: #999999 2px 2px 3px;
box-shadow: #999999 2px 2px 3px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-ms-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
}
.gradestable th.user img {
width: 20px;
height: 20px;
.path-grade-report-grader .graderreportoverlay.overriden {
border-top: 22px solid #f1c32a;
}
.gradestable th img {
vertical-align: text-bottom;
padding-bottom: 0;
.path-grade-report-grader .graderreportoverlay.overriden:before {
font-size: 12px;
content: "Overridden grade";
color: black;
height: 0px;
width: 100%;
display: inline-block;
text-align: center;
top: -32px;
float: left;
position: relative;
}
.gradestable th .grade_icons {
margin-top: .3em;
.path-grade-report-grader .graderreportoverlay .overriden {
background-color: #f1c32a;
width: 100%;
text-align: center;
}
.gradestable th img.sorticon {
margin-left: .3em;
.path-grade-report-grader .graderreportoverlay .fullname {
color: #444;
}
.dir-rtl .gradestable th img.sorticon {
margin-left: 0;
margin-right: .3em;
.path-grade-report-grader .graderreportoverlay .itemname {
padding-top: 4px;
color: #666;
}
table#user-grades .catlevel2 {
background-color: #f9f9f9;
.path-grade-report-grader .gradeparent {
overflow: inherit;
}
table#user-grades tr.range td.cell {
font-weight: 700;
table#user-grades {
font-size: 14px;
}
table#user-grades tr.avg td.cell {
background-color: #efefff;
font-weight: 700;
color: #00008B;
table#user-grades .avg th,
table#user-grades .avg td,
table#user-grades .heading th {
font-weight: bold;
background-color: #333;
color: #ddd;
border-color: #444;
}
table#user-grades tr.odd td.cell {
background-color: #efefef;
white-space: nowrap;
table#user-grades .avg th a,
table#user-grades .avg td a,
table#user-grades .heading th a {
color: white;
font-weight: normal;
}
table#user-grades tr td.overridden {
background-color: #F3E4C0;
table#user-grades .avg .cell {
font-weight: bold;
text-align: center;
}
table#user-grades tr.odd td.overridden {
background-color: #EFD9A4;
table#user-grades .avg .cell.header {
text-align: right;
}
table#user-grades tr td.ajaxoverridden {
background-color: #FFE3A0;
table#user-grades th {
font-weight: normal;
}
table#user-grades tr.odd td.ajaxoverridden {
background-color: #FFDA83;
table#user-grades th.cell {
height: 20px;
white-space: nowrap;
}
table#user-grades tr.even td.excluded {
background-color: #EABFFF;
.path-grade-report-history table#user-grades th.cell {
white-space: normal;
}
table#user-grades tr.odd td.excluded {
background-color: #E5AFFF;
table#user-grades .controls {
text-align: right;
background-color: #eee;
height: 20px;
}
table#user-grades tr.odd th.header {
background-color: #efefef;
background-image: none;
table#user-grades tr {
height: 40px;
}
table#user-grades tr.even th.header {
background-image: none;
table#user-grades tr .gradevalue {
width: 100%;
text-align: center;
display: inline-block;
}
table#user-grades tr.groupavg td.cell {
background-color: #efffef;
font-weight: 700;
color: #006400;
table#user-grades tr .user {
min-width: 200px;
max-width: 200px;
width: 200px;
background-color: #777777;
border-color: #666666;
}
table#user-grades td.cat,
table#user-grades td.course {
font-weight: 700;
table#user-grades tr .user a {
color: white;
}
table#user-grades tr .user .userpicture {
display: none;
}
table#user-grades tr.odd td.grade.vmarked {
background-color: #f39a00;
border-bottom-color: #f39a00;
}
table#user-grades tr.odd td.grade.vmarked.overridden {
background-color: #f39a00;
}
table#user-grades tr.odd td.grade.hmarked {
background-color: #f5cd00;
border-bottom-color: #f5cd00;
}
table#user-grades tr.odd td.grade.hmarked.overridden {
background-color: #f5cd00;
}
table#user-grades tr.odd td.grade.vmarked.hmarked {
background-color: #f1b900;
border-bottom-color: #f1b900;
}
table#user-grades tr.odd td.grade.vmarked.hmarked.overridden {
background-color: #f1b900;
}
table#user-grades tr.odd td.grade.overridden {
background-color: #fff;
background: url([[pix:gradereport_grader|corner-fold]]) no-repeat;
width: 16px;
height: 16px;
background-color: #efefef;
}
table#user-grades {
font-size: 10px;
@@ -411,110 +478,36 @@ tr.groupavg th.header {
.path-grade-report-grader .heading_name_row th {
white-space: nowrap;
width: 2000px;
table#user-grades tr.even td.grade.vmarked {
background-color: #f4a400;
border-bottom-color: #f4a400;
}
/*MDL-21088 - IE 7 ignores nowraps on tds or ths so we put a span within it with a nowrap on it*/
.path-grade-report-grader heading_name_row th span {
white-space: nowrap;
table#user-grades tr.even td.grade.vmarked.overridden {
background-color: #f4a400;
}
.path-grade-report-grader .grade_icons img.ajax {
float: right;
table#user-grades tr.even td.grade.hmarked {
background-color: #f5cf2c;
border-bottom-color: #f5cf2c;
}
.path-grade-report-grader .gradestable th.user,
.path-grade-report-grader .gradestable th.range,
.path-grade-report-grader .flexible th,
.path-grade-report-grader .flexible td,
.path-grade-report-grader .flexible th a,
.path-grade-report-grader .flexible td a,
.path-grade-report-grader .gradestable th.range,
.path-grade-report-grader td {
white-space: nowrap;
table#user-grades tr.even td.grade.hmarked.overridden {
background-color: #f5cf2c;
}
table#user-grades .catlevel1,
table#user-grades .r1,
.path-grade-report-grader table tr.even td.cell,
.path-grade-report-grader table tr.even th {
background-color: #fff;
table#user-grades tr.even td.grade.vmarked.hmarked {
background-color: #f1be00;
border-bottom-color: #f1be00;
}
table#user-grades .catlevel3,
.path-grade-report-grader table tr.odd td.cell {
background-color: #efefef;
table#user-grades tr.even td.grade.vmarked.hmarked.overridden {
background-color: #f1be00;
}
table#fixed_column tr.odd th ,
table#user-grades tr.odd th {
background-color: #efefef;
table#user-grades tr.even td.grade.overridden {
background-color: #efefef;
background: url([[pix:gradereport_grader|corner-fold]]) no-repeat;
width: 16px;
height: 16px;
background-color: #efefef;
}
table#user-grades td.vmarked,
table#user-grades tr.odd td.vmarked,
table#user-grades tr.avg td.vmarked,
table#user-grades tr.controls td.vmarked,
table#user-grades .catlevel1.vmarked,
table#user-grades .catlevel2.vmarked,
table#user-grades .catlevel3.vmarked,
table#user-grades tr.range td.vmarked,
table#user-grades tr.groupavg td.vmarked {
background-color: #fc3;
}
table#user-grades td.hmarked,
table#user-grades tr.odd td.hmarked,
table#user-grades tr.even td.hmarked,
table#user-grades tr.odd th.hmarked,
table#user-grades tr.even th.hmarked {
background-color: #ff9;
}
table#user-grades td.hmarked.vmarked,
table#user-grades tr.odd td.hmarked.vmarked,
table#user-grades tr.even td.hmarked.vmarked {
background-color: #fc9;
}
table#user-grades tr.heading,
table#user-grades .heading td {
border-style: solid;
border-width: 0;
}
table#user-grades td.userfield,
table#user-grades th,
.path-grade-report-grader div.gradeparent,
.path-grade-report-grader .ie6 form,
table#user-grades td.ajax {
text-align: left;
}
.dir-rtl table#user-grades td.userfield,
.dir-rtl table#user-grades th,
.path-grade-report-grader.dir-rtl div.gradeparent,
.path-grade-report-grader.dir-rtl .ie6 form,
.dir-rtl table#user-grades td.ajax {
text-align: right;
}
.path-grade-report-grader .gradeparent {
overflow: auto;
}
table#user-grades td.controls,
.path-grade-report-grader table tr.avg .cell,
.path-grade-report-grader table tr.range .cell {
background-color: #f3ead8;
}
.path-grade-report-grader div.left_scroller tr,
.path-grade-report-grader div.right_scroller tr,
.path-grade-report-grader div.left_scroller td,
.path-grade-report-grader div.right_scroller td,
.path-grade-report-grader div.left_scroller th,
.path-grade-report-grader div.right_scroller th {
height: 4.5em;
font-size: 10px;
}
.path-grade-report-grader table th.user,
.path-grade-report-grader table td.userfield {
text-align: left;
vertical-align: middle;
}
.path-grade-report-grader .usersuspended a:link,
.path-grade-report-grader .usersuspended a:visited {
color: #666;
}
.path-grade-report-grader table th.usersuspended img.usersuspendedicon {
vertical-align: text-bottom;
margin-left: .45em;
table#user-grades .overridden .grade_icons {
padding-left: 10px;
}
.path-grade-report-grader .yui3-overlay {
background-color: #FFEE69;
@@ -524,69 +517,234 @@ table#user-grades td.controls,
left: 0;
padding: 2px 5px;
font-size: 0.7em;
table#user-grades .catlevel1 .action-icon img {
opacity: 0.5;
}
.path-grade-report-grader .yui3-overlay .fullname {
color: #5F3E00;
font-weight: bold;
}
.path-grade-report-grader .yui3-overlay .itemname {
color: #194F3E;
font-weight: bold;
}
.path-grade-report-grader .yui3-overlay .feedback {
color: #5F595E;
}
/* table#user-grades td */
/* .grader-report-grader table#user-grades td .yui-panel div.hd { */
.path-grade-report-grader #tooltipPanel {
text-align: left;
}
.path-grade-report-grader .yui3-overlay a.container-close {
margin-top: -3px;
}
.path-grade-report-grader #hiddentooltiproot,
.tooltipDiv {
display: none;
}
.path-grade-report-grader.ie .right_scroller {
overflow-y: hidden;
}
.path-grade-report-grader.ie table#fixed_column th {
height: 4.5em;
}
.path-grade-report-grader.ie table#fixed_column tr.avg th {
height: 2.1em;
}
.path-grade-report-grader.ie div.left_scroller td {
height: 4.5em;
}
.path-grade-report-grader.ie6 div.right_scroller {
margin-top: 4em;
width: auto;
position: absolute;
}
.path-grade-report-grader.ie6 .excludedfloater {
font-size: 7px;
table#user-grades .catlevel1 .action-icon.selected img {
opacity: 1;
}
/** MDL-40071 **/
.path-grade-report-grader.dir-rtl table th.user,
.path-grade-report-grader.dir-rtl table td.userfield {
text-align: right;
#gradebook-header-container {
font-size: 14px;
background-color: #444;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#gradebook-header-container .gradebook-header-cell {
background-color: #444;
color: white;
padding: 4px;
white-space: nowrap;
border: 1px solid #444;
border-top: 0;
border-right: 0;
overflow: hidden;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#gradebook-header-container .gradebook-header-cell.catlevel1 {
background-color: #333;
}
#gradebook-header-container .gradebook-header-cell.catlevel2 {
background-color: #222;
}
#gradebook-header-container .gradebook-header-cell a {
color: white;
}
#gradebook-header-container .gradebook-header-cell:hover {
background-color: #111;
}
#gradebook-header-container .gradebook-header-cell .icon {
width: 24px;
height: 24px;
vertical-align: middle;
border: 0;
padding-right: 6px;
}
/** MDL-40180 **/
.dir-rtl table#user-grades th.category,
.dir-rtl table#user-grades th#studentheader,
.dir-rtl table#user-grades th.user {
text-align: right;
#gradebook-user-container {
background-color: #777;
}
.path-grade-report-grader.dir-rtl th.user img.userpicture {
margin-left: 0.5em;
#gradebook-user-container .gradebook-user-cell {
background-color: #666;
font-size: 14px;
border: 1px solid #666;
border-top: 0;
padding: 4px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#gradebook-user-container .gradebook-user-cell .userpicture {
display: none;
}
#gradebook-user-container .gradebook-user-cell a {
font-weight: normal;
color: #fff;
}
#gradebook-user-container .gradebook-user-cell:nth-child(2n+1) {
background-color: #777;
border-bottom: 1px solid #777;
}
#gradebook-user-container .gradebook-user-cell:nth-child(2n):hover {
background-color: #5c5c5c;
}
#gradebook-user-container .gradebook-user-cell:hover {
background-color: #6d6d6d;
}
#gradebook-user-container .gradebook-user-cell:first-child {
/*border-top: 1px solid #666;*/
}
.path-grade-report-grader .yui3-scrollview-scrollbar {
opacity: 1 !important;
#gradebook-footer-container {
display: none;
font-size: 14px;
background-color: #444;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#gradebook-footer-container.gradebook-footer-row-sticky {
display: block;
}
#gradebook-footer-container.gradebook-footer-row-sticky .cell {
border-top: 2px solid gray;
}
#gradebook-footer-container .gradebook-footer-cell {
padding: 8px;
font-size: 14px;
background-color: #333;
color: white;
font-weight: bold;
text-align: center;
border: 1px solid #444;
border-right: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#gradebook-footer-container .gradebook-footer-cell:nth-of-type(1) {
text-align: right;
}
#gradebook-footer-container .gradebook-footer-cell .btn {
float: left;
}
#gradebook-user-header-container {
background-color: #333;
font-size: 14px;
text-align: left;
padding: 8px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#gradebook-user-header-container a {
color: white;
}
.gradebook-loading-screen {
width: 100%;
height: 40px;
position: fixed;
left: 0;
bottom: 0;
background-color: black;
z-index: 99999;
opacity: 0.8;
padding: 10px;
text-align: center;
}
.gradebook-loading-screen .gradebook-loading {
padding-right: 40px;
text-align: center;
width: 100%;
}
.gradebook-loading i {
width: 20px;
height: 20px;
display: inline-block;
border-radius: 50%;
background: #00b3d5;
}
.gradebook-loading i:first-child {
-webkit-animation: gradebook-loading-ani2 0.5s linear infinite;
animation: gradebook-loading-ani2 0.5s linear infinite;
opacity: 0;
-webkit-transform: translate(-20px);
-moz-transform: translate(-20px);
-ms-transform: translate(-20px);
-o-transform: translate(-20px);
transform: translate(-20px);
}
.gradebook-loading i:nth-child(2),
.gradebook-loading i:nth-child(3) {
-webkit-animation: gradebook-loading-ani3 0.5s linear infinite;
animation: gradebook-loading-ani3 0.5s linear infinite;
}
.gradebook-loading i:last-child {
-webkit-animation: gradebook-loading-ani1 0.5s linear infinite;
animation: gradebook-loading-ani1 0.5s linear infinite;
}
@-webkit-keyframes gradebook-loading-ani1 {
100% {
-webkit-transform: translate(40px);
-moz-transform: translate(40px);
-ms-transform: translate(40px);
-o-transform: translate(40px);
transform: translate(40px);
opacity: 0;
}
}
@keyframes gradebook-loading-ani1 {
100% {
-webkit-transform: translate(40px);
-moz-transform: translate(40px);
-ms-transform: translate(40px);
-o-transform: translate(40px);
transform: translate(40px);
opacity: 0;
}
}
@-webkit-keyframes gradebook-loading-ani2 {
100% {
-webkit-transform: translate(20px);
-moz-transform: translate(20px);
-ms-transform: translate(20px);
-o-transform: translate(20px);
transform: translate(20px);
opacity: 1;
}
}
@keyframes gradebook-loading-ani2 {
100% {
-webkit-transform: translate(20px);
-moz-transform: translate(20px);
-ms-transform: translate(20px);
-o-transform: translate(20px);
transform: translate(20px);
opacity: 1;
}
}
@-webkit-keyframes gradebook-loading-ani3 {
100% {
-webkit-transform: translate(20px);
-moz-transform: translate(20px);
-ms-transform: translate(20px);
-o-transform: translate(20px);
transform: translate(20px);
}
}
.path-grade-report-grader .yui3-scrollview-scrollbar-horiz {
@@ -627,6 +785,14 @@ table#user-grades th.category {
}
.path-grade-report-grader td.grade.overridden {
line-height: 20px;
@keyframes gradebook-loading-ani3 {
100% {
-webkit-transform: translate(20px);
-moz-transform: translate(20px);
-ms-transform: translate(20px);
-o-transform: translate(20px);
transform: translate(20px);
}
}
/** MDL-46812 **/
@@ -0,0 +1,397 @@
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 <http://www.gnu.org/licenses/>.
/**
* Static header and student column for grader table.
*
* @package gradereport_grader
* @copyright 2014 UC Regents
* @author Alfonso Roman <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
var LOGNS = 'moodle-gradereport_grader-gradereporttable';
M.gradereport_grader = M.gradereport_grader || {};
// Create a gradebook module
M.gradereport_grader.gradereporttable = {
// Resuable nodes
node_student_header_cell: {},
node_student_cell: {},
node_footer_row: {},
scrollevent: function() {
console.log('scrolll event');
},
// Init module
init: function() {
Y.log('Loading grader report floating headers and columns.', 'debug', LOGNS);
// Set up some reusable nodes.
this.node_student_header_cell = Y.one('#studentheader');
// First student cell.
this.node_student_cell = Y.one('#user-grades .user.cell');
// Averages row.
this.node_footer_row = Y.one('#user-grades .avg');
// Check if there are any students -- otherwise no need to do anything.
if (this.node_student_cell) {
// Generate floating elements.
this.float_user_column();
this.float_assignment_header();
this.float_user_header();
// Onscroll event updates all the floating header/column positions.
var onscroll = function() {
// Get better performance by preventing layout thrashing. This occurs
// when the DOM is repeatedly updated and queried for updated values.
//
// To fix this, group reads and writes
//
// First get all the readable values needed.
// Header row
var headercontainer = document.getElementById('gradebook-header-container');
var userheadercell = document.getElementById('studentheader');
var usercolumnheader = document.getElementById('gradebook-user-header-container');
var headercelltop = userheadercell.offsetTop + userheadercell.offsetParent.offsetTop;
// User column
var pageleftcutoff = window.pageXOffset;
var firstusercell = document.querySelectorAll("#user-grades .user.cell")[0];
var firstusercellpos = firstusercell.offsetLeft + firstusercell.offsetParent.offsetLeft;
var usercolumn = document.getElementById('gradebook-user-container');
// Footer row
var lastrow = document.querySelectorAll('#user-grades .avg')[0];
var footer, lastrowpos;
// Check that Average footer is available.
if (lastrow !== undefined) {
footer = document.getElementById('gradebook-footer-container');
lastrowpos = lastrow.offsetTop + lastrow.offsetParent.offsetTop;
}
// Viewport values
var pageYOffset = window.pageYOffset;
var windowInnerHeight = window.innerHeight;
//
// Next do all the writing.
// Header position
headercontainer.style.left = userheadercell.offsetLeft + userheadercell.offsetParent.offsetLeft + 'px';
if (pageYOffset > headercelltop) {
headercontainer.style.top = pageYOffset + 40 + 'px';
usercolumnheader.style.top = pageYOffset + 40 + 'px';
} else {
headercontainer.style.top = headercelltop + 'px';
usercolumnheader.style.top = headercelltop + 'px';
}
// User column position
if (pageleftcutoff > firstusercellpos) {
usercolumn.style.left = pageleftcutoff + 'px';
usercolumnheader.style.left = pageleftcutoff + 'px';
} else {
usercolumn.style.left = firstusercellpos + 'px';
usercolumnheader.style.left = firstusercellpos + 'px';
}
// Update footer
if (lastrow !== undefined) {
footer.style.left = userheadercell.offsetLeft + userheadercell.offsetParent.offsetLeft + 'px';
if (pageYOffset + windowInnerHeight < lastrowpos) {
footer.style.top = (pageYOffset + windowInnerHeight - 50) + 'px';
footer.classList.add('gradebook-footer-row-sticky');
} else {
footer.style.top = lastrowpos + 'px';
footer.classList.remove('gradebook-footer-row-sticky');
}
}
};
// Add the floating 'average' footer if available.
if (this.node_footer_row) {
this.float_assignment_footer();
}
// Set floating element initial positions by simulating scroll.
onscroll();
// Generate events.
//
// Use native DOM scroll & resize events instead of YUI synthetic event.
window.onscroll = onscroll;
window.onresize = function() {
onscroll();
// Resize headers & footers.
// This is an expensive operation, not expected to happen often.
var headers = Y.all('#gradebook-header-container .gradebook-header-cell');
var resizedcells = Y.all('#user-grades .heading .cell');
var headeroffsetleft = Y.one('#studentheader').getX();
var newcontainerwidth = 0;
resizedcells.each(function(cell, idx) {
var headercell = headers.item(idx);
newcontainerwidth += cell.get('offsetWidth');
var styles = {
width: cell.get('offsetWidth'),
left: cell.getX() - headeroffsetleft + 'px'
};
headercell.setStyles(styles);
});
var footers = Y.all('#gradebook-footer-container .gradebook-footer-cell');
if (footers.size() !== 0) {
var resizedavgcells = Y.all('#user-grades .avg .cell');
resizedavgcells.each(function(cell, idx) {
var footercell = footers.item(idx);
var styles = {
width: cell.get('offsetWidth'),
left: cell.getX() - headeroffsetleft + 'px'
};
footercell.setStyles(styles);
});
Y.one('#gradebook-footer-container').setStyle('width', newcontainerwidth);
}
Y.one('#gradebook-header-container').setStyle('width', newcontainerwidth);
};
}
// Remove loading screen. Need to do YUI synthetic event to trigger
// on all browsers.
Y.on('domready', function() {
Y.one('.gradebook-loading-screen').remove(true);
Y.all('#user-grades .overridden').setAttribute('aria-label', 'Overriden grade');
});
},
float_user_column: function() {
Y.log('Floating the user column.', 'debug', LOGNS);
// Grab the user names column
var user_column = Y.all('#user-grades .user.cell');
// Generate a floating table
var floating_user_column = Y.Node.create('<div aria-hidden="true" id="gradebook-user-container"></div>');
var floating_user_column_height = 0;
var user_column_offset = this.node_student_cell.getY();
user_column.each(function(node) {
// Create cloned node and container.
// We'll absolutely position the container to each cell position,
// this will guarantee that student cells are always aligned.
var container_node = Y.Node.create('<div class="gradebook-user-cell"></div>');
// Grab the username
var usernamenode = node.cloneNode(true);
container_node.append(usernamenode.getHTML());
usernamenode = null;
container_node.setStyles({
'height': node.get('offsetHeight') + 'px',
'width': node.get('offsetWidth') + 'px',
'position': 'absolute',
'top': (node.getY() - user_column_offset) + 'px'
});
floating_user_column_height += node.get('offsetHeight');
// Retrieve the corresponding row
var classes = node.ancestor().getAttribute('class').split(' ').join('.');
// Attach highlight event
container_node.on('click', function() {
Y.one('.' + classes).all('.grade').toggleClass('hmarked');
});
// Add the cloned nodes to our floating table
floating_user_column.appendChild(container_node);
}, this);
// Style the table
floating_user_column.setStyles({
'position': 'absolute',
'left': this.node_student_cell.getX() + 'px',
'top': this.node_student_cell.getY() + 'px',
'width': this.node_student_cell.get('offsetWidth'),
'height': floating_user_column_height + 'px',
'background-color': '#f9f9f9'
});
Y.one('body').append(floating_user_column);
},
float_user_header: function() {
Y.log('Floating the user column header.', 'debug', LOGNS);
// Float the 'user name' header cell.
var floating_user_header_cell = Y.Node.create('<div aria-hidden="true" id="gradebook-user-header-container"></div>');
// Clone the node
var cellnode = this.node_student_header_cell.cloneNode(true);
// Append node contents
floating_user_header_cell.append(cellnode.getHTML());
floating_user_header_cell.setStyles({
'position': 'absolute',
'left': this.node_student_cell.getX() + 'px',
'top': this.node_student_header_cell.getY() + 'px',
'width': '200px',
'height': this.node_student_header_cell.get('offsetHeight') + 'px'
});
// Safe for collection
cellnode = null;
Y.one('body').append(floating_user_header_cell);
},
float_assignment_header: function() {
Y.log('Floating the assignment header.', 'debug', LOGNS);
var grade_headers = Y.all('#user-grades tr.heading .cell');
// Generate a floating headers
var floating_grade_headers = Y.Node.create('<div aria-hidden="true" id="gradebook-header-container"></div>');
var floating_grade_headers_width = 0;
var floating_grade_headers_height = 0;
var grade_headers_offset = this.node_student_header_cell.getX();
grade_headers.each(function(node) {
// Get the target column to highlight. This is embedded in
// the column cell #, but it's off by one, so need to adjust for that.
var col = node.getAttribute('class');
// Extract the column #
var search = /c[0-9]+/g;
var match = search.exec(col);
match = match[0].replace('c', '');
// Offset
var target_col = parseInt(match, 10);
++target_col;
var nodepos = node.getX();
// We need to clone the node, otherwise we mutate original obj
var nodeclone = node.cloneNode(true);
var newnode = Y.Node.create('<div class="gradebook-header-cell"></div>');
newnode.append(nodeclone.getHTML());
newnode.addClass(nodeclone.getAttribute('class'));
nodeclone = null;
newnode.setStyles({
'width': node.get('offsetWidth') + 'px',
'height': node.get('offsetHeight') + 'px',
'position': 'absolute',
'left': (nodepos - grade_headers_offset) + 'px'
});
// Sum up total width
floating_grade_headers_width += parseInt(node.get('offsetWidth'), 10);
floating_grade_headers_height = node.get('offsetHeight');
// Attach 'highlight column' event to new node
newnode.on('click', function() {
Y.all('.cell.c' + target_col).toggleClass('vmarked');
});
// Append to floating table.
floating_grade_headers.appendChild(newnode);
}, this);
// Position header table.
floating_grade_headers.setStyles({
'position': 'absolute',
'top': this.node_student_header_cell.getY() + 'px',
'left': this.node_student_header_cell.getX() + 'px',
'width': floating_grade_headers_width + 'px',
'height': floating_grade_headers_height + 'px'
});
Y.one('body').append(floating_grade_headers);
},
float_assignment_footer: function() {
Y.log('Floating the averages footer.', 'debug', LOGNS);
// Generate the sticky footer row.
// Grab the row.
var footer_row = Y.all('#user-grades .lastrow .cell');
// Create a container.
var floating_grade_footers = Y.Node.create('<div aria-hidden="true" id="gradebook-footer-container"></div>');
var floating_grade_footer_width = 0;
var footer_row_offset = this.node_footer_row.getX();
// Copy nodes
footer_row.each(function(node) {
var nodepos = node.getX();
var cellnodeclone = node.cloneNode(true);
var newnode = Y.Node.create('<div class="gradebook-footer-cell"></div>');
newnode.append(cellnodeclone.getHTML());
newnode.setStyles({
'width': node.get('offsetWidth') + 'px',
'height': '50px',
'position': 'absolute',
'left': (nodepos - footer_row_offset) + 'px'
});
floating_grade_footers.append(newnode);
floating_grade_footer_width += parseInt(node.get('offsetWidth'), 10);
}, this);
// Attach 'Update' button.
var update_button = Y.one('#gradersubmit');
if (update_button) {
var button = Y.Node.create('<button class="btn btn-sm btn-default">' + update_button.getAttribute('value') + '</button>');
button.on('click', function() {
YUI().use('node-event-simulate', function(Y) {
Y.one('#gradersubmit').simulate('click');
});
});
floating_grade_footers.one('.gradebook-footer-cell').append(button);
}
// Position the row
floating_grade_footers.setStyles({
'position': 'absolute',
'left': this.node_footer_row.getX() + 'px',
'bottom': '0',
'height': '50px',
'width': floating_grade_footer_width + 'px'
});
Y.one('body').append(floating_grade_footers);
}
};
}, '@VERSION@');
@@ -0,0 +1,392 @@
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 <http://www.gnu.org/licenses/>.
/**
* Static header and student column for grader table.
*
* @package gradereport_grader
* @copyright 2014 UC Regents
* @author Alfonso Roman <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
var LOGNS = 'moodle-gradereport_grader-gradereporttable';
M.gradereport_grader = M.gradereport_grader || {};
// Create a gradebook module
M.gradereport_grader.gradereporttable = {
// Resuable nodes
node_student_header_cell: {},
node_student_cell: {},
node_footer_row: {},
scrollevent: function() {
console.log('scrolll event');
},
// Init module
init: function() {
// Set up some reusable nodes.
this.node_student_header_cell = Y.one('#studentheader');
// First student cell.
this.node_student_cell = Y.one('#user-grades .user.cell');
// Averages row.
this.node_footer_row = Y.one('#user-grades .avg');
// Check if there are any students -- otherwise no need to do anything.
if (this.node_student_cell) {
// Generate floating elements.
this.float_user_column();
this.float_assignment_header();
this.float_user_header();
// Onscroll event updates all the floating header/column positions.
var onscroll = function() {
// Get better performance by preventing layout thrashing. This occurs
// when the DOM is repeatedly updated and queried for updated values.
//
// To fix this, group reads and writes
//
// First get all the readable values needed.
// Header row
var headercontainer = document.getElementById('gradebook-header-container');
var userheadercell = document.getElementById('studentheader');
var usercolumnheader = document.getElementById('gradebook-user-header-container');
var headercelltop = userheadercell.offsetTop + userheadercell.offsetParent.offsetTop;
// User column
var pageleftcutoff = window.pageXOffset;
var firstusercell = document.querySelectorAll("#user-grades .user.cell")[0];
var firstusercellpos = firstusercell.offsetLeft + firstusercell.offsetParent.offsetLeft;
var usercolumn = document.getElementById('gradebook-user-container');
// Footer row
var lastrow = document.querySelectorAll('#user-grades .avg')[0];
var footer, lastrowpos;
// Check that Average footer is available.
if (lastrow !== undefined) {
footer = document.getElementById('gradebook-footer-container');
lastrowpos = lastrow.offsetTop + lastrow.offsetParent.offsetTop;
}
// Viewport values
var pageYOffset = window.pageYOffset;
var windowInnerHeight = window.innerHeight;
//
// Next do all the writing.
// Header position
headercontainer.style.left = userheadercell.offsetLeft + userheadercell.offsetParent.offsetLeft + 'px';
if (pageYOffset > headercelltop) {
headercontainer.style.top = pageYOffset + 40 + 'px';
usercolumnheader.style.top = pageYOffset + 40 + 'px';
} else {
headercontainer.style.top = headercelltop + 'px';
usercolumnheader.style.top = headercelltop + 'px';
}
// User column position
if (pageleftcutoff > firstusercellpos) {
usercolumn.style.left = pageleftcutoff + 'px';
usercolumnheader.style.left = pageleftcutoff + 'px';
} else {
usercolumn.style.left = firstusercellpos + 'px';
usercolumnheader.style.left = firstusercellpos + 'px';
}
// Update footer
if (lastrow !== undefined) {
footer.style.left = userheadercell.offsetLeft + userheadercell.offsetParent.offsetLeft + 'px';
if (pageYOffset + windowInnerHeight < lastrowpos) {
footer.style.top = (pageYOffset + windowInnerHeight - 50) + 'px';
footer.classList.add('gradebook-footer-row-sticky');
} else {
footer.style.top = lastrowpos + 'px';
footer.classList.remove('gradebook-footer-row-sticky');
}
}
};
// Add the floating 'average' footer if available.
if (this.node_footer_row) {
this.float_assignment_footer();
}
// Set floating element initial positions by simulating scroll.
onscroll();
// Generate events.
//
// Use native DOM scroll & resize events instead of YUI synthetic event.
window.onscroll = onscroll;
window.onresize = function() {
onscroll();
// Resize headers & footers.
// This is an expensive operation, not expected to happen often.
var headers = Y.all('#gradebook-header-container .gradebook-header-cell');
var resizedcells = Y.all('#user-grades .heading .cell');
var headeroffsetleft = Y.one('#studentheader').getX();
var newcontainerwidth = 0;
resizedcells.each(function(cell, idx) {
var headercell = headers.item(idx);
newcontainerwidth += cell.get('offsetWidth');
var styles = {
width: cell.get('offsetWidth'),
left: cell.getX() - headeroffsetleft + 'px'
};
headercell.setStyles(styles);
});
var footers = Y.all('#gradebook-footer-container .gradebook-footer-cell');
if (footers.size() !== 0) {
var resizedavgcells = Y.all('#user-grades .avg .cell');
resizedavgcells.each(function(cell, idx) {
var footercell = footers.item(idx);
var styles = {
width: cell.get('offsetWidth'),
left: cell.getX() - headeroffsetleft + 'px'
};
footercell.setStyles(styles);
});
Y.one('#gradebook-footer-container').setStyle('width', newcontainerwidth);
}
Y.one('#gradebook-header-container').setStyle('width', newcontainerwidth);
};
}
// Remove loading screen. Need to do YUI synthetic event to trigger
// on all browsers.
Y.on('domready', function() {
Y.one('.gradebook-loading-screen').remove(true);
Y.all('#user-grades .overridden').setAttribute('aria-label', 'Overriden grade');
});
},
float_user_column: function() {
// Grab the user names column
var user_column = Y.all('#user-grades .user.cell');
// Generate a floating table
var floating_user_column = Y.Node.create('<div aria-hidden="true" id="gradebook-user-container"></div>');
var floating_user_column_height = 0;
var user_column_offset = this.node_student_cell.getY();
user_column.each(function(node) {
// Create cloned node and container.
// We'll absolutely position the container to each cell position,
// this will guarantee that student cells are always aligned.
var container_node = Y.Node.create('<div class="gradebook-user-cell"></div>');
// Grab the username
var usernamenode = node.cloneNode(true);
container_node.append(usernamenode.getHTML());
usernamenode = null;
container_node.setStyles({
'height': node.get('offsetHeight') + 'px',
'width': node.get('offsetWidth') + 'px',
'position': 'absolute',
'top': (node.getY() - user_column_offset) + 'px'
});
floating_user_column_height += node.get('offsetHeight');
// Retrieve the corresponding row
var classes = node.ancestor().getAttribute('class').split(' ').join('.');
// Attach highlight event
container_node.on('click', function() {
Y.one('.' + classes).all('.grade').toggleClass('hmarked');
});
// Add the cloned nodes to our floating table
floating_user_column.appendChild(container_node);
}, this);
// Style the table
floating_user_column.setStyles({
'position': 'absolute',
'left': this.node_student_cell.getX() + 'px',
'top': this.node_student_cell.getY() + 'px',
'width': this.node_student_cell.get('offsetWidth'),
'height': floating_user_column_height + 'px',
'background-color': '#f9f9f9'
});
Y.one('body').append(floating_user_column);
},
float_user_header: function() {
// Float the 'user name' header cell.
var floating_user_header_cell = Y.Node.create('<div aria-hidden="true" id="gradebook-user-header-container"></div>');
// Clone the node
var cellnode = this.node_student_header_cell.cloneNode(true);
// Append node contents
floating_user_header_cell.append(cellnode.getHTML());
floating_user_header_cell.setStyles({
'position': 'absolute',
'left': this.node_student_cell.getX() + 'px',
'top': this.node_student_header_cell.getY() + 'px',
'width': '200px',
'height': this.node_student_header_cell.get('offsetHeight') + 'px'
});
// Safe for collection
cellnode = null;
Y.one('body').append(floating_user_header_cell);
},
float_assignment_header: function() {
var grade_headers = Y.all('#user-grades tr.heading .cell');
// Generate a floating headers
var floating_grade_headers = Y.Node.create('<div aria-hidden="true" id="gradebook-header-container"></div>');
var floating_grade_headers_width = 0;
var floating_grade_headers_height = 0;
var grade_headers_offset = this.node_student_header_cell.getX();
grade_headers.each(function(node) {
// Get the target column to highlight. This is embedded in
// the column cell #, but it's off by one, so need to adjust for that.
var col = node.getAttribute('class');
// Extract the column #
var search = /c[0-9]+/g;
var match = search.exec(col);
match = match[0].replace('c', '');
// Offset
var target_col = parseInt(match, 10);
++target_col;
var nodepos = node.getX();
// We need to clone the node, otherwise we mutate original obj
var nodeclone = node.cloneNode(true);
var newnode = Y.Node.create('<div class="gradebook-header-cell"></div>');
newnode.append(nodeclone.getHTML());
newnode.addClass(nodeclone.getAttribute('class'));
nodeclone = null;
newnode.setStyles({
'width': node.get('offsetWidth') + 'px',
'height': node.get('offsetHeight') + 'px',
'position': 'absolute',
'left': (nodepos - grade_headers_offset) + 'px'
});
// Sum up total width
floating_grade_headers_width += parseInt(node.get('offsetWidth'), 10);
floating_grade_headers_height = node.get('offsetHeight');
// Attach 'highlight column' event to new node
newnode.on('click', function() {
Y.all('.cell.c' + target_col).toggleClass('vmarked');
});
// Append to floating table.
floating_grade_headers.appendChild(newnode);
}, this);
// Position header table.
floating_grade_headers.setStyles({
'position': 'absolute',
'top': this.node_student_header_cell.getY() + 'px',
'left': this.node_student_header_cell.getX() + 'px',
'width': floating_grade_headers_width + 'px',
'height': floating_grade_headers_height + 'px'
});
Y.one('body').append(floating_grade_headers);
},
float_assignment_footer: function() {
// Generate the sticky footer row.
// Grab the row.
var footer_row = Y.all('#user-grades .lastrow .cell');
// Create a container.
var floating_grade_footers = Y.Node.create('<div aria-hidden="true" id="gradebook-footer-container"></div>');
var floating_grade_footer_width = 0;
var footer_row_offset = this.node_footer_row.getX();
// Copy nodes
footer_row.each(function(node) {
var nodepos = node.getX();
var cellnodeclone = node.cloneNode(true);
var newnode = Y.Node.create('<div class="gradebook-footer-cell"></div>');
newnode.append(cellnodeclone.getHTML());
newnode.setStyles({
'width': node.get('offsetWidth') + 'px',
'height': '50px',
'position': 'absolute',
'left': (nodepos - footer_row_offset) + 'px'
});
floating_grade_footers.append(newnode);
floating_grade_footer_width += parseInt(node.get('offsetWidth'), 10);
}, this);
// Attach 'Update' button.
var update_button = Y.one('#gradersubmit');
if (update_button) {
var button = Y.Node.create('<button class="btn btn-sm btn-default">' + update_button.getAttribute('value') + '</button>');
button.on('click', function() {
YUI().use('node-event-simulate', function(Y) {
Y.one('#gradersubmit').simulate('click');
});
});
floating_grade_footers.one('.gradebook-footer-cell').append(button);
}
// Position the row
floating_grade_footers.setStyles({
'position': 'absolute',
'left': this.node_footer_row.getX() + 'px',
'bottom': '0',
'height': '50px',
'width': floating_grade_footer_width + 'px'
});
Y.one('body').append(floating_grade_footers);
}
};
}, '@VERSION@');
@@ -0,0 +1,10 @@
{
"name": "moodle-gradereport_grader-gradereporttable",
"builds": {
"moodle-gradereport_grader-gradereporttable": {
"jsfiles": [
"gradereporttable.js"
]
}
}
}
@@ -0,0 +1,392 @@
// 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 <http://www.gnu.org/licenses/>.
/**
* Static header and student column for grader table.
*
* @package gradereport_grader
* @copyright 2014 UC Regents
* @author Alfonso Roman <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
var LOGNS = 'moodle-gradereport_grader-gradereporttable';
M.gradereport_grader = M.gradereport_grader || {};
// Create a gradebook module
M.gradereport_grader.gradereporttable = {
// Resuable nodes
node_student_header_cell: {},
node_student_cell: {},
node_footer_row: {},
scrollevent: function() {
console.log('scrolll event');
},
// Init module
init: function() {
Y.log('Loading grader report floating headers and columns.', 'debug', LOGNS);
// Set up some reusable nodes.
this.node_student_header_cell = Y.one('#studentheader');
// First student cell.
this.node_student_cell = Y.one('#user-grades .user.cell');
// Averages row.
this.node_footer_row = Y.one('#user-grades .avg');
// Check if there are any students -- otherwise no need to do anything.
if (this.node_student_cell) {
// Generate floating elements.
this.float_user_column();
this.float_assignment_header();
this.float_user_header();
// Onscroll event updates all the floating header/column positions.
var onscroll = function() {
// Get better performance by preventing layout thrashing. This occurs
// when the DOM is repeatedly updated and queried for updated values.
//
// To fix this, group reads and writes
//
// First get all the readable values needed.
// Header row
var headercontainer = document.getElementById('gradebook-header-container');
var userheadercell = document.getElementById('studentheader');
var usercolumnheader = document.getElementById('gradebook-user-header-container');
var headercelltop = userheadercell.offsetTop + userheadercell.offsetParent.offsetTop;
// User column
var pageleftcutoff = window.pageXOffset;
var firstusercell = document.querySelectorAll("#user-grades .user.cell")[0];
var firstusercellpos = firstusercell.offsetLeft + firstusercell.offsetParent.offsetLeft;
var usercolumn = document.getElementById('gradebook-user-container');
// Footer row
var lastrow = document.querySelectorAll('#user-grades .avg')[0];
var footer, lastrowpos;
// Check that Average footer is available.
if (lastrow !== undefined) {
footer = document.getElementById('gradebook-footer-container');
lastrowpos = lastrow.offsetTop + lastrow.offsetParent.offsetTop;
}
// Viewport values
var pageYOffset = window.pageYOffset;
var windowInnerHeight = window.innerHeight;
//
// Next do all the writing.
// Header position
headercontainer.style.left = userheadercell.offsetLeft + userheadercell.offsetParent.offsetLeft + 'px';
if (pageYOffset > headercelltop) {
headercontainer.style.top = pageYOffset + 40 + 'px';
usercolumnheader.style.top = pageYOffset + 40 + 'px';
} else {
headercontainer.style.top = headercelltop + 'px';
usercolumnheader.style.top = headercelltop + 'px';
}
// User column position
if (pageleftcutoff > firstusercellpos) {
usercolumn.style.left = pageleftcutoff + 'px';
usercolumnheader.style.left = pageleftcutoff + 'px';
} else {
usercolumn.style.left = firstusercellpos + 'px';
usercolumnheader.style.left = firstusercellpos + 'px';
}
// Update footer
if (lastrow !== undefined) {
footer.style.left = userheadercell.offsetLeft + userheadercell.offsetParent.offsetLeft + 'px';
if (pageYOffset + windowInnerHeight < lastrowpos) {
footer.style.top = (pageYOffset + windowInnerHeight - 50) + 'px';
footer.classList.add('gradebook-footer-row-sticky');
} else {
footer.style.top = lastrowpos + 'px';
footer.classList.remove('gradebook-footer-row-sticky');
}
}
};
// Add the floating 'average' footer if available.
if (this.node_footer_row) {
this.float_assignment_footer();
}
// Set floating element initial positions by simulating scroll.
onscroll();
// Generate events.
//
// Use native DOM scroll & resize events instead of YUI synthetic event.
window.onscroll = onscroll;
window.onresize = function() {
onscroll();
// Resize headers & footers.
// This is an expensive operation, not expected to happen often.
var headers = Y.all('#gradebook-header-container .gradebook-header-cell');
var resizedcells = Y.all('#user-grades .heading .cell');
var headeroffsetleft = Y.one('#studentheader').getX();
var newcontainerwidth = 0;
resizedcells.each(function(cell, idx) {
var headercell = headers.item(idx);
newcontainerwidth += cell.get('offsetWidth');
var styles = {
width: cell.get('offsetWidth'),
left: cell.getX() - headeroffsetleft + 'px'
};
headercell.setStyles(styles);
});
var footers = Y.all('#gradebook-footer-container .gradebook-footer-cell');
if (footers.size() !== 0) {
var resizedavgcells = Y.all('#user-grades .avg .cell');
resizedavgcells.each(function(cell, idx) {
var footercell = footers.item(idx);
var styles = {
width: cell.get('offsetWidth'),
left: cell.getX() - headeroffsetleft + 'px'
};
footercell.setStyles(styles);
});
Y.one('#gradebook-footer-container').setStyle('width', newcontainerwidth);
}
Y.one('#gradebook-header-container').setStyle('width', newcontainerwidth);
};
}
// Remove loading screen. Need to do YUI synthetic event to trigger
// on all browsers.
Y.on('domready', function() {
Y.one('.gradebook-loading-screen').remove(true);
Y.all('#user-grades .overridden').setAttribute('aria-label', 'Overriden grade');
});
},
float_user_column: function() {
Y.log('Floating the user column.', 'debug', LOGNS);
// Grab the user names column
var user_column = Y.all('#user-grades .user.cell');
// Generate a floating table
var floating_user_column = Y.Node.create('<div aria-hidden="true" id="gradebook-user-container"></div>');
var floating_user_column_height = 0;
var user_column_offset = this.node_student_cell.getY();
user_column.each(function(node) {
// Create cloned node and container.
// We'll absolutely position the container to each cell position,
// this will guarantee that student cells are always aligned.
var container_node = Y.Node.create('<div class="gradebook-user-cell"></div>');
// Grab the username
var usernamenode = node.cloneNode(true);
container_node.append(usernamenode.getHTML());
usernamenode = null;
container_node.setStyles({
'height': node.get('offsetHeight') + 'px',
'width': node.get('offsetWidth') + 'px',
'position': 'absolute',
'top': (node.getY() - user_column_offset) + 'px'
});
floating_user_column_height += node.get('offsetHeight');
// Retrieve the corresponding row
var classes = node.ancestor().getAttribute('class').split(' ').join('.');
// Attach highlight event
container_node.on('click', function() {
Y.one('.' + classes).all('.grade').toggleClass('hmarked');
});
// Add the cloned nodes to our floating table
floating_user_column.appendChild(container_node);
}, this);
// Style the table
floating_user_column.setStyles({
'position': 'absolute',
'left': this.node_student_cell.getX() + 'px',
'top': this.node_student_cell.getY() + 'px',
'width': this.node_student_cell.get('offsetWidth'),
'height': floating_user_column_height + 'px',
'background-color': '#f9f9f9'
});
Y.one('body').append(floating_user_column);
},
float_user_header: function() {
Y.log('Floating the user column header.', 'debug', LOGNS);
// Float the 'user name' header cell.
var floating_user_header_cell = Y.Node.create('<div aria-hidden="true" id="gradebook-user-header-container"></div>');
// Clone the node
var cellnode = this.node_student_header_cell.cloneNode(true);
// Append node contents
floating_user_header_cell.append(cellnode.getHTML());
floating_user_header_cell.setStyles({
'position': 'absolute',
'left': this.node_student_cell.getX() + 'px',
'top': this.node_student_header_cell.getY() + 'px',
'width': '200px',
'height': this.node_student_header_cell.get('offsetHeight') + 'px'
});
// Safe for collection
cellnode = null;
Y.one('body').append(floating_user_header_cell);
},
float_assignment_header: function() {
Y.log('Floating the assignment header.', 'debug', LOGNS);
var grade_headers = Y.all('#user-grades tr.heading .cell');
// Generate a floating headers
var floating_grade_headers = Y.Node.create('<div aria-hidden="true" id="gradebook-header-container"></div>');
var floating_grade_headers_width = 0;
var floating_grade_headers_height = 0;
var grade_headers_offset = this.node_student_header_cell.getX();
grade_headers.each(function(node) {
// Get the target column to highlight. This is embedded in
// the column cell #, but it's off by one, so need to adjust for that.
var col = node.getAttribute('class');
// Extract the column #
var search = /c[0-9]+/g;
var match = search.exec(col);
match = match[0].replace('c', '');
// Offset
var target_col = parseInt(match, 10);
++target_col;
var nodepos = node.getX();
// We need to clone the node, otherwise we mutate original obj
var nodeclone = node.cloneNode(true);
var newnode = Y.Node.create('<div class="gradebook-header-cell"></div>');
newnode.append(nodeclone.getHTML());
newnode.addClass(nodeclone.getAttribute('class'));
nodeclone = null;
newnode.setStyles({
'width': node.get('offsetWidth') + 'px',
'height': node.get('offsetHeight') + 'px',
'position': 'absolute',
'left': (nodepos - grade_headers_offset) + 'px'
});
// Sum up total width
floating_grade_headers_width += parseInt(node.get('offsetWidth'), 10);
floating_grade_headers_height = node.get('offsetHeight');
// Attach 'highlight column' event to new node
newnode.on('click', function() {
Y.all('.cell.c' + target_col).toggleClass('vmarked');
});
// Append to floating table.
floating_grade_headers.appendChild(newnode);
}, this);
// Position header table.
floating_grade_headers.setStyles({
'position': 'absolute',
'top': this.node_student_header_cell.getY() + 'px',
'left': this.node_student_header_cell.getX() + 'px',
'width': floating_grade_headers_width + 'px',
'height': floating_grade_headers_height + 'px'
});
Y.one('body').append(floating_grade_headers);
},
float_assignment_footer: function() {
Y.log('Floating the averages footer.', 'debug', LOGNS);
// Generate the sticky footer row.
// Grab the row.
var footer_row = Y.all('#user-grades .lastrow .cell');
// Create a container.
var floating_grade_footers = Y.Node.create('<div aria-hidden="true" id="gradebook-footer-container"></div>');
var floating_grade_footer_width = 0;
var footer_row_offset = this.node_footer_row.getX();
// Copy nodes
footer_row.each(function(node) {
var nodepos = node.getX();
var cellnodeclone = node.cloneNode(true);
var newnode = Y.Node.create('<div class="gradebook-footer-cell"></div>');
newnode.append(cellnodeclone.getHTML());
newnode.setStyles({
'width': node.get('offsetWidth') + 'px',
'height': '50px',
'position': 'absolute',
'left': (nodepos - footer_row_offset) + 'px'
});
floating_grade_footers.append(newnode);
floating_grade_footer_width += parseInt(node.get('offsetWidth'), 10);
}, this);
// Attach 'Update' button.
var update_button = Y.one('#gradersubmit');
if (update_button) {
var button = Y.Node.create('<button class="btn btn-sm btn-default">' + update_button.getAttribute('value') + '</button>');
button.on('click', function() {
YUI().use('node-event-simulate', function(Y) {
Y.one('#gradersubmit').simulate('click');
});
});
floating_grade_footers.one('.gradebook-footer-cell').append(button);
}
// Position the row
floating_grade_footers.setStyles({
'position': 'absolute',
'left': this.node_footer_row.getX() + 'px',
'bottom': '0',
'height': '50px',
'width': floating_grade_footer_width + 'px'
});
Y.one('body').append(floating_grade_footers);
}
};
@@ -0,0 +1,9 @@
{
"moodle-local_ucla-gradebooktable": {
"requires": [
"node",
"event",
"node-event-simulate"
]
}
}