MDL-47046 gradereport_grader: Update JS to meet coding style guidelines

This also moves the code into a set of separate files to allow for
additional components of the report to be migrated to the same YUI module,
adjusts some UI components, improves JS performance, and adds appropriate
documentation.

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:
Andrew Nicols 2014-08-07 10:55:08 +08:00
parent 2c4a3f7525
commit 6ad1af094e
12 changed files with 2225 additions and 1719 deletions

View File

@ -52,8 +52,7 @@ 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-gradereporttable', 'M.gradereport_grader.gradereporttable.init', array());
$PAGE->requires->yui_module('moodle-gradereport_grader-gradereporttable', 'Y.M.gradereport_grader.init');
// basic access checks
if (!$course = $DB->get_record('course', array('id' => $courseid))) {
@ -194,15 +193,6 @@ 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);

View File

@ -35,3 +35,4 @@ $string['pluginname'] = 'Grader report';
$string['preferences'] = 'Grader report preferences';
$string['useractivitygrade'] = '{$a} grade';
$string['useractivityfeedback'] = '{$a} feedback';
$string['overriddengrade'] = 'Overridden grade';

View File

@ -636,16 +636,14 @@ class grade_report_grader extends grade_report {
$rows = $this->get_left_icons_row($rows, $colspan);
$rowclasses = array('even', 'odd');
$suspendedstring = null;
foreach ($this->users as $userid => $user) {
$userrow = new html_table_row();
$userrow->id = 'fixed_user_'.$userid;
$userrow->attributes['class'] = 'r'.$this->rowcount++.' '.$rowclasses[$this->rowcount % 2];
$usercell = new html_table_cell();
$usercell->attributes['class'] = 'user';
$usercell->attributes['data-uid'] = $userid;
$usercell->header = true;
$usercell->scope = 'row';
@ -811,6 +809,7 @@ class grade_report_grader extends grade_report {
$itemcell = new html_table_cell();
$itemcell->attributes['class'] = $type . ' ' . $catlevel . ' highlightable'. ' i'. $element['object']->id;
$itemcell->attributes['data-itemid'] = $element['object']->id;
if ($element['object']->is_hidden()) {
$itemcell->attributes['class'] .= ' dimmed_text';
@ -852,8 +851,6 @@ class grade_report_grader extends grade_report {
}
$jsscales = $scalesarray;
$rowclasses = array('even', 'odd');
foreach ($this->users as $userid => $user) {
if ($this->canviewhidden) {
@ -868,7 +865,6 @@ class grade_report_grader extends grade_report {
$itemrow = new html_table_row();
$itemrow->id = 'user_'.$userid;
$itemrow->attributes['class'] = $rowclasses[$this->rowcount % 2];
$fullname = fullname($user);
$jsarguments['users'][$userid] = $fullname;
@ -880,6 +876,8 @@ class grade_report_grader extends grade_report {
$itemcell = new html_table_cell();
$itemcell->id = 'u'.$userid.'i'.$itemid;
$itemcell->attributes['data-itemid'] = $itemid;
$itemcell->attributes['data-uid'] = $userid;
// Get the decimal points preference for this item
$decimalpoints = $item->get_decimals();
@ -928,6 +926,7 @@ class grade_report_grader extends grade_report {
}
if ($grade->is_overridden()) {
$itemcell->attributes['class'] .= ' overridden';
$itemcell->attributes['aria-label'] = get_string('overriddengrade', 'gradereport_grader');
}
if (!empty($grade->feedback)) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 B

View File

@ -1,798 +1,155 @@
.path-grade-report-grader .yui3-overlay {
border: 0;
background: none;
background-color: 'initial';
min-width: 200px;
}
.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;
}
.path-grade-report-grader .graderreportoverlay.overriden {
border-top: 22px solid #f1c32a;
}
.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;
}
.path-grade-report-grader .graderreportoverlay .overriden {
background-color: #f1c32a;
width: 100%;
text-align: center;
}
.path-grade-report-grader .graderreportoverlay .fullname {
color: #444;
}
.path-grade-report-grader .graderreportoverlay .itemname {
padding-top: 4px;
color: #666;
}
.path-grade-report-grader .gradeparent {
overflow: inherit;
/* The grade information tooltip */
.gradeparent .grader-information-tooltip {
min-width: 200px;
}
table#user-grades {
font-size: 14px;
}
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 .avg th a,
table#user-grades .avg td a,
table#user-grades .heading th a {
color: white;
font-weight: normal;
}
table#user-grades .avg .cell {
font-weight: bold;
text-align: center;
}
table#user-grades .avg .cell.header {
text-align: right;
}
table#user-grades th {
font-weight: normal;
}
table#user-grades th.cell {
height: 20px;
white-space: nowrap;
}
.path-grade-report-history table#user-grades th.cell {
white-space: normal;
}
table#user-grades .controls {
text-align: right;
background-color: #eee;
height: 20px;
}
table#user-grades tr {
height: 40px;
}
table#user-grades tr .gradevalue {
width: 100%;
text-align: center;
display: inline-block;
}
table#user-grades tr .user {
min-width: 200px;
max-width: 200px;
width: 200px;
background-color: #777777;
border-color: #666666;
}
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;
.gradeparent .graderreportoverlay {
background-color: white;
width: auto;
background-color: transparent;
border-style: solid;
border-width: 1px;
margin: 0;
}
.path-grade-report-grader #overDiv table {
margin: 0;
}
.path-grade-report-grader #overDiv table td.feedback {
border: 0;
}
.path-grade-report-grader #overDiv .feedback {
font-size: 70%;
background-color: #ABF;
color: #000;
font-family: Verdana;
font-weight: 400;
}
.path-grade-report-grader #overDiv .caption {
font-size: 70%;
background-color: #56C;
color: #CCF;
font-family: Arial;
font-weight: 700;
}
.path-grade-report-grader #overDiv .intersection {
font-size: 70%;
background-color: #ABF;
color: #000;
font-family: Verdana;
font-weight: 400;
}
.path-grade-report-grader #overDiv .intersectioncaption {
background-color: #56C;
color: #CCF;
font-family: Arial;
font-weight: 700;
}
.path-grade-report-grader div.submit {
margin-top: 20px;
text-align: center;
}
table#user-grades td {
text-align: right;
border-style: solid;
border-width: 0 1px 1px 0;
}
table#user-grades th.category {
vertical-align: top;
border-style: solid;
border-width: 1px 1px 0;
}
table#user-grades th.user {
text-align: left;
border-style: solid;
border-width: 1px 0;
}
table#user-grades th.userfield {
border-style: solid;
border-width: 1px;
}
table#user-grades th.categoryitem,
table#user-grades td.topleft {
vertical-align: bottom;
border-style: solid;
border-width: 0 1px;
}
.path-grade-report-grader td,.path-grade-report-grader th {
border-color: #CECECE;
}
.path-grade-report-grader table#participants th {
vertical-align: top;
width: auto;
}
table#user-grades td.fillerfirst {
border-style: solid;
border-width: 0 0 0 1px;
}
table#user-grades td.fillerlast {
border-style: solid;
border-width: 0 1px 0 0;
}
table#user-grades th.item,
table#user-grades th.categoryitem,
table#user-grades th.courseitem {
border-bottom-color: #000;
vertical-align: bottom;
border-style: solid;
border-width: 1px;
}
div.gradertoggle {
display: inline;
margin-left: 20px;
}
table#user-grades th.range {
text-align: right;
border-style: solid;
border-width: 1px;
}
table#user-grades .userpic {
display: inline;
margin-right: 10px;
}
table#user-grades .quickfeedback {
border: 1px dashed #000;
width: auto;
margin: 0;
padding: 0;
margin-left: 10px;
}
.dir-rtl table#user-grades .quickfeedback {
margin-left: 0;
margin-right: 10px;
}
.path-grade-report-grader #siteconfiglink {
text-align: right;
}
table#user-grades .datesubmitted {
font-size: .7em;
}
table#user-grades td.cell {
padding-left: 5px;
padding-right: 5px;
vertical-align: middle;
}
.path-grade-report-grader table {
border-collapse: collapse;
background-color: #fff;
border-color: #cecece;
}
.path-grade-report-grader th {
padding: 1px 10px;
}
.path-grade-report-grader span.inclusion-links {
margin: 0 5px 0 10px;
}
table#user-grades .item {
background-color: #e9e9e9;
}
.path-grade-report-grader table tr.odd th.header {
background-color: #efefef;
background-image: none;
border-width: 0 0 1px;
}
.path-grade-report-grader table tr.heading th.header {
border-top: 1px solid #cecece;
}
table#user-grades tr.heading th.categoryitem,
table#user-grades tr.heading th.courseitem {
border-width: 0 0 0 1px;
}
table#user-grades th.category.header.catlevel1 {
vertical-align: top;
border-style: solid;
border-width: 1px 1px 0 0;
}
.path-grade-report-grader div.left_scroller th.user a {
vertical-align: middle;
margin: 0;
padding: 0;
}
table#user-grades th.categoryitem,
table#user-grades th.courseitem,
.path-grade-report-grader table td.topleft {
vertical-align: bottom;
border-color: #cecece #cecece #000;
border-style: solid;
border-width: 0 1px 1px;
}
.path-grade-report-grader .left_scroller table td.topleft {
background-color: #fff;
border-bottom-color: #cecece;
}
table#user-grades td.topleft {
background-color: #fff;
}
.path-grade-report-grader th.user img.userpicture {
border: 3px double #cecece;
vertical-align: top;
width: 2.7em;
height: 2.7em;
margin-right: 10px;
}
.path-grade-report-grader a.quickedit {
line-height: 1em;
display: block;
float: right;
clear: none;
font-size: 9px;
background-color: transparent;
margin: .1em 0 0;
}
.path-grade-report-grader a.quickedit2 {
display: block;
float: right;
clear: none;
background-color: transparent;
margin: 1.3em 0 0;
}
.path-grade-report-grader table#quick_edit {
border: 1px solid #cecece;
margin: 0 auto;
}
.path-grade-report-grader table#quick_edit td {
vertical-align: middle;
border: 1px solid #cecece;
text-align: left;
margin: 0;
padding: 5px;
}
.path-grade-report-grader table#quick_edit td img {
border: 3px double #cecece;
vertical-align: middle;
padding: 0;
}
.path-grade-report-grader td input.text {
border: 1px solid #666;
width: auto;
margin: 0;
padding: 0;
padding: 10px;
border-color: #ddd;
font-size: 12px;
height: 20px;
line-height: 20px;
box-shadow: #999999 2px 2px 3px;
border-radius: 4px;
}
.gradebook-header-cell,
.gradeparent table th {
font-size: 14px;
font-weight: normal;
padding: 4px 5px 4px 5px;
border: 0 solid #ddd;
border-left-width: 1px;
border-top-width: 1px;
}
/* General table properties */
.gradeparent table td,
.gradeparent table th,
.gradeparent .cell,
.gradebook-footer-cell,
.gradebook-user-cell {
padding: 4px 5px;
vertical-align: middle;
}
/* Grade Item titles */
.gradebook-header-cell,
.gradebook-header-cell,
#gradebook-user-header-container,
.gradeparent tr.heading th.item,
.gradeparent tr.heading th.courseitem,
.gradeparent tr.heading th.userfield,
#studentheader {
white-space: nowrap;
background-color: #333;
}
.gradeparent .gradebook-header-cell.item,
.gradeparent .gradebook-header-cell.item a,
.gradeparent .gradebook-header-cell.courseitem,
.gradeparent .gradebook-header-cell.courseitem a,
.gradeparent .gradebook-header-cell.userfield,
.gradeparent .gradebook-header-cell.userfield a,
.gradeparent #gradebook-user-header-container,
.gradeparent #gradebook-user-header-container a,
.gradeparent .gradebook-user-cell,
.gradeparent .gradebook-user-cell a,
.gradeparent tr.heading th.item,
.gradeparent tr.heading th.item a,
.gradeparent tr.heading th.courseitem,
.gradeparent tr.heading th.courseitem a,
.gradeparent tr.heading th.userfield,
.gradeparent tr.heading th.userfield a,
.gradeparent th a,
.gradeparent #studentheader a {
color: #fff;
}
/* The username cells */
.gradebook-user-cell,
.gradeparent th.user {
border-top: 0;
padding: 4px;
min-width: 200px;
max-width: 200px;
width: 200px;
border-color: #666666;
border-right-width: 1px;
border-left: none;
background-color: #333;
color: #ddd;
}
.gradebook-user-cell:nth-child(odd),
.gradeparent tr:nth-child(odd) th.user {
background-color: #777;
}
.gradebook-user-cell:nth-child(even),
.gradeparent tr:nth-child(even) th.user {
background-color: #5c5c5c;
}
.gradebook-user-cell .userpicture,
.gradeparent th.user .userpicture {
margin-right: 4px;
border-radius: 50px;
border: none;
vertical-align: top;
}
/* The top-left user header cell */
#gradebook-user-header-container,
#studentheader {
padding: 4px 5px;
border-top: 1px solid #ddd;
}
#studentheader {
border-left: none;
}
/* The footer row */
.gradebook-footer-cell,
.gradeparent .gradevalue,
.gradeparent .avg .cell {
text-align: center;
width: 100%;
}
.path-grade-report-grader td input.submit {
margin: 10px 10px 0px 10px;
.gradeparent .gradevalue {
display: inline-block;
}
.path-grade-report-grader table#quick_edit td.fullname {
border-left: 0;
padding-left: 5px;
}
.path-grade-report-grader table#quick_edit td.picture {
.gradeparent table#user-grades tr.avg th,
.gradeparent table#user-grades tr.avg td,
.gradebook-footer-cell {
padding: 8px;
background-color: #333;
color: white;
font-weight: bold;
text-align: center;
border: 1px solid #444;
border-right: 0;
}
.path-grade-report-grader table#quick_edit td.finalgrade input {
width: 5em;
}
.path-grade-report-grader h1 {
text-align: center;
clear: both;
}
.path-grade-report-grader input.center {
margin: 10px auto 0;
}
.path-grade-report-grader .lefttbody {
width: auto;
vertical-align: middle;
}
table#user-grades th.fixedcolumn {
border: 1px solid #cecece;
vertical-align: middle;
}
.path-grade-report-grader table#fixed_column th {
border: 1px solid #cecece;
vertical-align: middle;
border-right-color: #000;
}
.path-grade-report-grader table#fixed_column th.user{
border-right-color: #cecece;
}
.path-grade-report-grader table#fixed_column {
padding-top: 20px;
border-top: 1px solid #cecece;
background-color: #fff;
}
.path-grade-report-grader .left_scroller {
float: left;
clear: none;
}
.path-grade-report-grader.dir-rtl .left_scroller {
float: right;
}
.path-grade-report-grader .right_scroller {
width: auto;
clear: none;
overflow-x: scroll;
overflow-y: hidden;
}
.path-grade-report-grader table tr.avg,
.path-grade-report-grader table tr.groupavg td,
.path-grade-report-grader table tr.avg td,
.path-grade-report-grader table tr.groupavg th,
.path-grade-report-grader table tr.avg th,
.path-grade-report-grader table tr.controls_row,
.path-grade-report-grader table tr.controls_row th,
.path-grade-report-grader table tr.range_row,
.path-grade-report-grader table tr.range_row th,
div.right_scroller tr {
height: 2em;
}
table#user-grades tr.groupavg td.cell,
tr.groupavg th.header {
background-color: #efffef;
}
.path-grade-report-grader form td.excluded {
color: red;
}
.path-grade-report-grader .excludedfloater {
font-weight: 700;
color: red;
font-size: 9px;
float: left;
}
.path-grade-report-grader span.gradepass {
color: #298721;
}
.path-grade-report-grader span.gradefail {
color: #890d0d;
}
.path-grade-report-grader .gradeweight {
color: #461d7c;
font-weight: 700;
}
.path-grade-report-grader td select {
font-size: 100%;
padding: 0;
}
.path-grade-report-grader .right_scroller td select {
font-size: 86%;
padding: 0;
}
.path-grade-report-grader tr.avg,
.path-grade-report-grader tr.controls,
.path-grade-report-grader td.controls,
.path-grade-report-grader th.controls,
.path-grade-report-grader tr.groupavg,
.path-grade-report-grader tr.range,
.path-grade-report-grader th.range,
.path-grade-report-grader td.range,
.path-grade-report-grader tr.heading th.range {
height: 2em!important;
white-space: nowrap;
}
.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;
}
table#user-grades tr.even td.grade.vmarked.overridden {
background-color: #f4a400;
}
table#user-grades tr.even td.grade.hmarked {
background-color: #f5cf2c;
border-bottom-color: #f5cf2c;
}
table#user-grades tr.even td.grade.hmarked.overridden {
background-color: #f5cf2c;
}
table#user-grades tr.even td.grade.vmarked.hmarked {
background-color: #f1be00;
border-bottom-color: #f1be00;
}
table#user-grades tr.even td.grade.vmarked.hmarked.overridden {
background-color: #f1be00;
}
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 .overridden .grade_icons {
padding-left: 10px;
}
.path-grade-report-grader .yui3-overlay {
background-color: #FFEE69;
border-color: #D4C237 #A6982B #A6982B;
border-style: solid;
border-width: 1px;
left: 0;
padding: 2px 5px;
font-size: 0.7em;
table#user-grades .catlevel1 .action-icon img {
opacity: 0.5;
}
table#user-grades .catlevel1 .action-icon.selected img {
opacity: 1;
}
#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;
}
#gradebook-user-container {
background-color: #777;
}
#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;*/
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#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;
display: none;
}
#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;
background-color: #444;
display: block;
border-bottom: 0px solid #444;
}
#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 {
bottom: 0;
}
/** MDL-43824 **/
#page-grade-report-grader-index table#fixed_column td.topleft.cell,
#page-grade-report-grader-index.jsenabled table#fixed_column td.topleft.cell {
padding: 8px 5px;
}
#page-grade-report-grader-index table#fixed_column td.header.controls {
border-left: 1px solid #cecece;
}
.path-grade-report-grader .right_scroller td select {
line-height: 2;
margin: 0;
padding: 0;
height: 22px;
}
table#user-grades {
margin-bottom: 0;
}
table#user-grades td,
table#user-grades th.category {
padding: 8px 5px;
}
.path-grade-report-grader .gradeparent {
border-top: 1px solid #cecece;
}
.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 {
padding: 8px 5px;
}
.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);
}
.gradebook-footer-cell:first-child,
.gradeparent table#user-grades tr.avg th.range {
text-align: right;
}
/** MDL-46812 **/

View File

@ -3,8 +3,9 @@
"builds": {
"moodle-gradereport_grader-gradereporttable": {
"jsfiles": [
"gradereporttable.js"
"gradereporttable.js",
"floatingheaders.js"
]
}
}
}
}

View File

@ -0,0 +1,611 @@
// 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/>.
/**
* @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';
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 header cell.
*
* @property headerCell
* @type Node
* @protected
*/
headerCell: null,
/**
* A Node representing the first cell which contains user name information.
*
* @property firstUserCell
* @type Node
* @protected
*/
firstUserCell: null,
/**
* 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 Node
* @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 Node
* @protected
*/
firstUserCellLeft: 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 Node
* @protected
*/
lastUserCellTop: 0,
/**
* Array of EventHandles.
*
* @type EventHandle[]
* @property _eventHandles
* @protected
*/
_eventHandles: [],
/**
* Setup the grader report table.
*
* @method setupFloatingHeaders
* @chainable
*/
setupFloatingHeaders: function() {
// Grab references to commonly used Nodes.
this.firstUserCell = Y.one(SELECTORS.USERCELL);
if (!this.firstUserCell) {
// There was no first user cell - no need to do anything at this stage.
this._hideSpinner();
return;
}
// Generate floating elements.
this._setupFloatingUserColumn();
this._setupFloatingUserHeader();
this._setupFloatingAssignmentHeaders();
this._setupFloatingAssignmentFooter();
// 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();
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.headerCellTop = this.headerCell.getY();
// The footer row shows the grade averages and will be floated to the page bottom.
if (this.tableFooterRow) {
this.footerRowPosition = this.tableFooterRow.getY();
}
var userCellList = Y.all(SELECTORS.USERCELL);
// The left of the user cells matches the left of the headerCell.
this.firstUserCellLeft = this.headerCell.getX();
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.
this.firstUserCellBottom = userCellList.item(1).getY();
// 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;
} 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);
}
}
},
/**
* 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)
);
},
/**
* 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('<div aria-hidden="true" role="presentation" id="gradebook-user-container"></div>');
// Generate the new fields.
userColumn.each(function(node) {
// Create and configure the new container.
var containerNode = Y.Node.create('<div aria-hidden="true" class="gradebook-user-cell"></div>');
containerNode.set('innerHTML', node.get('innerHTML'))
.setAttribute('data-uid', node.ancestor('tr').getData('uid'))
.setStyles({
height: node.getComputedStyle(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: this.firstUserCell.getX() + 'px',
position: 'absolute',
top: this.firstUserCell.getY() + '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 this header cell. Store it for later.
this.headerCell = Y.one(SELECTORS.STUDENTHEADER);
// Float the 'user name' header cell.
var floatingUserCell = Y.Node.create('<div aria-hidden="true" role="presentation" id="gradebook-user-header-container"></div>');
// Append node contents
floatingUserCell.set('innerHTML', this.headerCell.getHTML());
floatingUserCell.setStyles({
height: this.headerCell.getComputedStyle(HEIGHT),
left: this.firstUserCell.getX() + 'px',
position: 'absolute',
top: this.headerCell.getY() + 'px',
width: this.firstUserCell.getComputedStyle(WIDTH)
});
// Append to the grader region.
this.graderRegion.append(floatingUserCell);
// Store a reference to this for later - we use it in the event handlers.
this.userColumnHeader = floatingUserCell;
},
/**
* Create and setup the floating grade item header row.
*
* @method _setupFloatingAssignmentHeaders
* @protected
*/
_setupFloatingAssignmentHeaders: function() {
var gradeHeaders = Y.all('#user-grades tr.heading .cell');
// Generate a floating headers
var floatingGradeHeaders = Y.Node.create('<div aria-hidden="true" role="presentation" id="gradebook-header-container"></div>');
var floatingGradeHeadersWidth = 0;
var floatingGradeHeadersHeight = 0;
var gradeHeadersOffset = this.headerCell.getX();
gradeHeaders.each(function(node) {
var nodepos = node.getX();
var newnode = Y.Node.create('<div class="gradebook-header-cell"></div>');
newnode.append(node.getHTML())
.addClass(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: this.headerCell.getX() + 'px',
position: 'absolute',
top: this.headerCell.getY() + '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('<div aria-hidden="true" role="presentation" id="gradebook-footer-container"></div>');
var footerWidth = 0;
var footerRowOffset = this.tableFooterRow.getX();
// Copy cell content.
footerCells.each(function(node) {
var newnode = Y.Node.create('<div class="gradebook-footer-cell"></div>');
newnode.set('innerHTML', node.getHTML());
newnode.setStyles({
height: this._getHeight(node),
left: (node.getX() - footerRowOffset) + 'px',
position: 'absolute',
width: this._getWidth(node)
});
floatingGraderFooter.append(newnode);
footerWidth += parseInt(node.get(OFFSETWIDTH), 10);
}, this);
// Attach 'Update' button.
var updateButton = Y.one('#gradersubmit');
if (updateButton) {
// TODO decide what to do with classes here to make them compatible with the base themes.
var button = Y.Node.create('<button class="btn btn-sm btn-default">' + updateButton.getAttribute('value') + '</button>');
button.on('click', function() {
updateButton.simulate('click');
});
floatingGraderFooter.one('.gradebook-footer-cell').append(button);
}
// Position the row
floatingGraderFooter.setStyles({
position: 'absolute',
left: this.tableFooterRow.getX() + 'px',
bottom: 0,
height: this._getHeight(this.tableFooterRow),
width: footerWidth + 'px',
borderBottomWidth: Y.DOM.getScrollbarWidth() + 'px'
});
// Append to the grader region.
this.graderRegion.append(floatingGraderFooter);
this.footerRow = floatingGraderFooter;
},
/**
* 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 = {};
// Header position.
gradeItemHeadingContainerStyles.left = this.headerCell.getX();
if (Y.config.win.pageYOffset + this.pageHeaderHeight > this.headerCellTop) {
if (Y.config.win.pageYOffset + this.pageHeaderHeight < this.lastUserCellTop) {
gradeItemHeadingContainerStyles.top = Y.config.win.pageYOffset + this.pageHeaderHeight + 'px';
userColumnHeaderStyles.top = Y.config.win.pageYOffset + this.pageHeaderHeight + 'px';
} else {
gradeItemHeadingContainerStyles.top = this.lastUserCellTop + 'px';
userColumnHeaderStyles.top = this.lastUserCellTop + 'px';
}
} else {
gradeItemHeadingContainerStyles.top = this.headerCellTop + 'px';
userColumnHeaderStyles.top = this.headerCellTop + 'px';
}
// User column position.
if (Y.config.win.pageXOffset > this.firstUserCellLeft) {
userColumnStyles.left = Y.config.win.pageXOffset + 'px';
userColumnHeaderStyles.left = Y.config.win.pageXOffset + 'px';
} else {
userColumnStyles.left = this.firstUserCellLeft + 'px';
userColumnHeaderStyles.left = this.firstUserCellLeft + 'px';
}
// Update footer.
if (this.footerRow) {
footerStyles.left = this.headerCell.getX();
// Determine whether the footer should now be shown as sticky.
var pageHeight = Y.config.win.pageYOffset + Y.config.win.innerHeight,
usablePageHeight = pageHeight - Y.DOM.getScrollbarWidth();
if (usablePageHeight - this.pageHeaderHeight < this.footerRowPosition) {
// The footer is off the bottom of the page.
this.footerRow.addClass(CSS.STICKYFOOTER);
if (usablePageHeight - this.pageHeaderHeight > this.firstUserCellBottom) {
// The footer is above the bottom of the first user.
footerStyles.top = (pageHeight - this.footerRow.get(OFFSETHEIGHT)) + 'px';
} else {
footerStyles.top = this.firstUserCellBottom;
}
} else {
footerStyles.top = this.footerRowPosition + 'px';
this.footerRow.removeClass(CSS.STICKYFOOTER);
}
}
// Finally, apply the styles.
this.gradeItemHeadingContainer.setStyles(gradeItemHeadingContainerStyles);
this.userColumnHeader.setStyles(userColumnHeaderStyles);
this.userColumn.setStyles(userColumnStyles);
this.footerRow.setStyles(footerStyles);
},
/**
* 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 headers & footers.
// This is an expensive operation, not expected to happen often.
var headers = this.gradeItemHeadingContainer.all(SELECTORS.HEADERCELL);
var resizedcells = Y.all('#user-grades .heading .cell');
var headeroffsetleft = this.headerCell.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);
});
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.getComputedStyle(WIDTH),
left: cell.getX() - headeroffsetleft + 'px'
};
footercell.setStyles(styles);
});
}
this.gradeItemHeadingContainer.setStyle('width', newcontainerwidth);
},
/**
* Determine the height of the specified Node.
*
* With IE, the height used when setting a height is the offsetHeight.
* All other browsers set this as this inner height.
*
* @method _getHeight
* @protected
* @param {Node} node
* @return String
*/
_getHeight: function(node) {
if (Y.UA.ie) {
return node.get(OFFSETHEIGHT) + 'px';
} else {
return node.getComputedStyle(HEIGHT);
}
},
/**
* Determine the width of the specified Node.
*
* With IE, the width used when setting a width is the offsetWidth.
* All other browsers set this as this inner width.
*
* @method _getWidth
* @protected
* @param {Node} node
* @return String
*/
_getWidth: function(node) {
if (Y.UA.ie) {
return node.get(OFFSETWIDTH) + 'px';
} else {
return node.getComputedStyle(WIDTH);
}
}
};
Y.Base.mix(Y.M.gradereport_grader.ReportTable, [FloatingHeaders]);

View File

@ -13,380 +13,153 @@
// 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.
* Grader Report Functionality.
*
* @module moodle-gradereport_grader-gradereporttable
* @package gradereport_grader
* @copyright 2014 UC Regents
* @author Alfonso Roman <aroman@oid.ucla.edu>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Alfonso Roman <aroman@oid.ucla.edu>
*/
var LOGNS = 'moodle-gradereport_grader-gradereporttable';
/**
* @module 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');
var SELECTORS = {
FOOTERROW: '#user-grades .avg',
GRADECELL: 'td.grade',
GRADERTABLE: '.gradeparent table',
GRADEPARENT: '.gradeparent',
HEADERCELL: '.gradebook-header-cell',
STUDENTHEADER: '#studentheader',
USERCELL: '#user-grades .user.cell'
},
// Init module
init: function() {
Y.log('Loading grader report floating headers and columns.', 'debug', LOGNS);
CSS = {
OVERRIDDEN: 'overridden',
STICKYFOOTER: 'gradebook-footer-row-sticky',
TOOLTIPACTIVE: 'tooltipactive'
};
// 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');
/**
* The Grader Report Table.
*
* @namespace M.gradereport_grader
* @class ReportTable
* @constructor
*/
function ReportTable() {
ReportTable.superclass.constructor.apply(this, arguments);
}
// 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();
Y.extend(ReportTable, Y.Base, {
/**
* Array of EventHandles.
*
* @type EventHandle[]
* @property _eventHandles
* @protected
*/
_eventHandles: [],
// Onscroll event updates all the floating header/column positions.
var onscroll = function() {
/**
* A Node reference to the grader table.
*
* @property graderTable
* @type Node
*/
graderTable: null,
// 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
/**
* 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);
//
// First get all the readable values needed.
// Setup the floating headers.
this.setupFloatingHeaders();
// Header row
var headercontainer = document.getElementById('gradebook-header-container');
var userheadercell = document.getElementById('studentheader');
// Setup the mouse tooltips.
this.setupTooltips();
var usercolumnheader = document.getElementById('gradebook-user-header-container');
// Hide the loading spinner - we've finished for the moment.
this._hideSpinner();
},
var headercelltop = userheadercell.offsetTop + userheadercell.offsetParent.offsetTop;
/**
* Show the loading spinner.
*
* @method showSpinner
* @protected
*/
showSpinner: function() {
// Show the grading spinner.
Y.one(SELECTORS.SPINNER).show();
},
// User column
var pageleftcutoff = window.pageXOffset;
var firstusercell = document.querySelectorAll("#user-grades .user.cell")[0];
var firstusercellpos = firstusercell.offsetLeft + firstusercell.offsetParent.offsetLeft;
/**
* Hide the loading spinner.
*
* @method hideSpinner
* @protected
*/
hideSpinner: function() {
// Hide the grading spinner.
Y.one(SELECTORS.SPINNER).hide();
},
var usercolumn = document.getElementById('gradebook-user-container');
/**
* 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");
// 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);
};
if (usercell) {
return usercell.get('text');
} else {
return '';
}
// 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);
/**
* 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 '';
}
},
// 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);
/**
* 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);
};

View File

@ -1,9 +1,10 @@
{
"moodle-local_ucla-gradebooktable": {
"moodle-gradereport_grader-gradereporttable": {
"requires": [
"base",
"node",
"event",
"node-event-simulate"
]
}
}
}