diff --git a/grade/report/grader/index.php b/grade/report/grader/index.php
index 85e6776b9de..473669ac922 100644
--- a/grade/report/grader/index.php
+++ b/grade/report/grader/index.php
@@ -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(
- '', '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);
diff --git a/grade/report/grader/lang/en/gradereport_grader.php b/grade/report/grader/lang/en/gradereport_grader.php
index 7f18997187f..5286ba369d0 100644
--- a/grade/report/grader/lang/en/gradereport_grader.php
+++ b/grade/report/grader/lang/en/gradereport_grader.php
@@ -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';
diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php
index 2ad04b3a118..0143996f271 100644
--- a/grade/report/grader/lib.php
+++ b/grade/report/grader/lib.php
@@ -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)) {
diff --git a/grade/report/grader/pix/corner-fold.png b/grade/report/grader/pix/corner-fold.png
deleted file mode 100644
index 14f7089f4cf..00000000000
Binary files a/grade/report/grader/pix/corner-fold.png and /dev/null differ
diff --git a/grade/report/grader/styles.css b/grade/report/grader/styles.css
index d11d789a716..32f38d5a008 100644
--- a/grade/report/grader/styles.css
+++ b/grade/report/grader/styles.css
@@ -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 **/
diff --git a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js
index 4d2378b0e49..795ffa1c827 100644
--- a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js
+++ b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js
@@ -15,383 +15,767 @@ YUI.add('moodle-gradereport_grader-gradereporttable', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-
/**
- * 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
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author Alfonso Roman
*/
-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;
+ if (usercell) {
+ return usercell.get('text');
+ } else {
+ return '';
+ }
+ },
- // Check that Average footer is available.
- if (lastrow !== undefined) {
- footer = document.getElementById('gradebook-footer-container');
- lastrowpos = lastrow.offsetTop + lastrow.offsetParent.offsetTop;
- }
+ /**
+ * 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 '';
+ }
+ },
- // Viewport values
- var pageYOffset = window.pageYOffset;
- var windowInnerHeight = window.innerHeight;
+ /**
+ * 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');
+ }
+});
- //
- // Next do all the writing.
+Y.namespace('M.gradereport_grader').ReportTable = ReportTable;
+Y.namespace('M.gradereport_grader').init = function(config) {
+ return new Y.M.gradereport_grader.ReportTable(config);
+};
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
- // 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';
- }
+/**
+ * @module moodle-gradereport_grader-gradereporttable
+ * @submodule floatingheaders
+ */
- // 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';
- }
+/**
+ * Provides floating headers to the grader report.
+ *
+ * See {{#crossLink "M.gradereport_grader.ReportTable"}}{{/crossLink}} for details.
+ *
+ * @namespace M.gradereport_grader
+ * @class FloatingHeaders
+ */
- // Update footer
- if (lastrow !== undefined) {
- footer.style.left = userheadercell.offsetLeft + userheadercell.offsetParent.offsetLeft + 'px';
+var HEIGHT = 'height',
+ WIDTH = 'width',
+ OFFSETWIDTH = 'offsetWidth',
+ OFFSETHEIGHT = 'offsetHeight';
- 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');
- }
- }
- };
+function FloatingHeaders() {}
- // Add the floating 'average' footer if available.
- if (this.node_footer_row) {
- this.float_assignment_footer();
- }
+FloatingHeaders.ATTRS= {
+};
- // Set floating element initial positions by simulating scroll.
- onscroll();
+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,
- // 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');
+ /**
+ * A Node representing the header cell.
+ *
+ * @property headerCell
+ * @type Node
+ * @protected
+ */
+ headerCell: null,
- 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);
- }
+ /**
+ * A Node representing the first cell which contains user name information.
+ *
+ * @property firstUserCell
+ * @type Node
+ * @protected
+ */
+ firstUserCell: null,
- Y.one('#gradebook-header-container').setStyle('width', newcontainerwidth);
-
- };
+ /**
+ * 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;
}
- // 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');
- });
+ // 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;
},
- 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');
+ /**
+ * 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();
- // Generate a floating table
- var floating_user_column = Y.Node.create('');
- var floating_user_column_height = 0;
- var user_column_offset = this.node_student_cell.getY();
+ // The footer row shows the grade averages and will be floated to the page bottom.
+ if (this.tableFooterRow) {
+ this.footerRowPosition = this.tableFooterRow.getY();
+ }
- user_column.each(function(node) {
+ var userCellList = Y.all(SELECTORS.USERCELL);
- // 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('');
+ // The left of the user cells matches the left of the headerCell.
+ this.firstUserCellLeft = this.headerCell.getX();
- // Grab the username
- var usernamenode = node.cloneNode(true);
- container_node.append(usernamenode.getHTML());
- usernamenode = null;
+ 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();
- container_node.setStyles({
- 'height': node.get('offsetHeight') + 'px',
- 'width': node.get('offsetWidth') + 'px',
- 'position': 'absolute',
- 'top': (node.getY() - user_column_offset) + 'px'
- });
+ // 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();
- 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);
+ 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('');
+
+ // Generate the new fields.
+ userColumn.each(function(node) {
+ // Create and configure the new container.
+ var containerNode = Y.Node.create('');
+ 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 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'
+ // Style the floating user container.
+ floatingUserColumn.setStyles({
+ left: this.firstUserCell.getX() + 'px',
+ position: 'absolute',
+ top: this.firstUserCell.getY() + 'px'
});
- Y.one('body').append(floating_user_column);
+ // 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;
},
- float_user_header: function() {
- Y.log('Floating the user column header.', 'debug', LOGNS);
+
+ /**
+ * 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 floating_user_header_cell = Y.Node.create('');
+ var floatingUserCell = Y.Node.create('');
- // 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'
+ 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)
});
- // Safe for collection
- cellnode = null;
+ // Append to the grader region.
+ this.graderRegion.append(floatingUserCell);
- Y.one('body').append(floating_user_header_cell);
+ // Store a reference to this for later - we use it in the event handlers.
+ this.userColumnHeader = floatingUserCell;
},
- float_assignment_header: function() {
- Y.log('Floating the assignment header.', 'debug', LOGNS);
- var grade_headers = Y.all('#user-grades tr.heading .cell');
+ /**
+ * 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 floating_grade_headers = Y.Node.create('');
+ var floatingGradeHeaders = Y.Node.create('');
- 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 floatingGradeHeadersWidth = 0;
+ var floatingGradeHeadersHeight = 0;
+ var gradeHeadersOffset = this.headerCell.getX();
+ gradeHeaders.each(function(node) {
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('');
- newnode.append(nodeclone.getHTML());
- newnode.addClass(nodeclone.getAttribute('class'));
- nodeclone = null;
+ 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)
+ });
- newnode.setStyles({
- 'width': node.get('offsetWidth') + 'px',
- 'height': node.get('offsetHeight') + 'px',
- 'position': 'absolute',
- 'left': (nodepos - grade_headers_offset) + 'px'
- });
+ // 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);
- // 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);
+ // Append to our floating table.
+ floatingGradeHeaders.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'
+ floatingGradeHeaders.setStyles({
+ height: floatingGradeHeadersHeight + 'px',
+ left: this.headerCell.getX() + 'px',
+ position: 'absolute',
+ top: this.headerCell.getY() + 'px',
+ width: floatingGradeHeadersWidth + 'px'
});
- Y.one('body').append(floating_grade_headers);
+ // 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;
},
- float_assignment_footer: function() {
- Y.log('Floating the averages footer.', 'debug', LOGNS);
+
+ /**
+ * 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.
- // Grab the row.
- var footer_row = Y.all('#user-grades .lastrow .cell');
+ var footerCells = this.tableFooterRow.all('.cell');
+
// Create a container.
- var floating_grade_footers = Y.Node.create('');
- 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 floatingGraderFooter = Y.Node.create('');
+ var footerWidth = 0;
+ var footerRowOffset = this.tableFooterRow.getX();
+ // Copy cell content.
+ footerCells.each(function(node) {
var newnode = Y.Node.create('');
- newnode.append(cellnodeclone.getHTML());
+ newnode.set('innerHTML', node.getHTML());
newnode.setStyles({
- 'width': node.get('offsetWidth') + 'px',
- 'height': '50px',
- 'position': 'absolute',
- 'left': (nodepos - footer_row_offset) + 'px'
+ height: this._getHeight(node),
+ left: (node.getX() - footerRowOffset) + 'px',
+ position: 'absolute',
+ width: this._getWidth(node)
});
- floating_grade_footers.append(newnode);
- floating_grade_footer_width += parseInt(node.get('offsetWidth'), 10);
+ floatingGraderFooter.append(newnode);
+ footerWidth += parseInt(node.get(OFFSETWIDTH), 10);
}, this);
// Attach 'Update' button.
- var update_button = Y.one('#gradersubmit');
- if (update_button) {
- var button = Y.Node.create('');
+ 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.on('click', function() {
- YUI().use('node-event-simulate', function(Y) {
- Y.one('#gradersubmit').simulate('click');
- });
+ updateButton.simulate('click');
});
- floating_grade_footers.one('.gradebook-footer-cell').append(button);
+ floatingGraderFooter.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'
+ floatingGraderFooter.setStyles({
+ position: 'absolute',
+ left: this.tableFooterRow.getX() + 'px',
+ bottom: 0,
+ height: this._getHeight(this.tableFooterRow),
+ width: footerWidth + 'px',
+ borderBottomWidth: Y.DOM.getScrollbarWidth() + 'px'
});
- Y.one('body').append(floating_grade_footers);
+ // 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]);
-}, '@VERSION@');
+
+}, '@VERSION@', {"requires": ["base", "node", "event", "node-event-simulate"]});
diff --git a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js
index cfa5af8a406..91d7bb7a9ab 100644
--- a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js
+++ b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js
@@ -1 +1,2 @@
-YUI.add("moodle-gradereport_grader-gradereporttable",function(e,t){var n="moodle-gradereport_grader-gradereporttable";M.gradereport_grader=M.gradereport_grader||{},M.gradereport_grader.gradereporttable={node_student_header_cell:{},node_student_cell:{},node_footer_row:{},scrollevent:function(){console.log("scrolll event")},init:function(){this.node_student_header_cell=e.one("#studentheader"),this.node_student_cell=e.one("#user-grades .user.cell"),this.node_footer_row=e.one("#user-grades .avg");if(this.node_student_cell){this.float_user_column(),this.float_assignment_header(),this.float_user_header();var t=function(){var e=document.getElementById("gradebook-header-container"),t=document.getElementById("studentheader"),n=document.getElementById("gradebook-user-header-container"),r=t.offsetTop+t.offsetParent.offsetTop,i=window.pageXOffset,s=document.querySelectorAll("#user-grades .user.cell")[0],o=s.offsetLeft+s.offsetParent.offsetLeft,u=document.getElementById("gradebook-user-container"),a=document.querySelectorAll("#user-grades .avg")[0],f,l;a!==undefined&&(f=document.getElementById("gradebook-footer-container"),l=a.offsetTop+a.offsetParent.offsetTop);var c=window.pageYOffset,h=window.innerHeight;e.style.left=t.offsetLeft+t.offsetParent.offsetLeft+"px",c>r?(e.style.top=c+40+"px",n.style.top=c+40+"px"):(e.style.top=r+"px",n.style.top=r+"px"),i>o?(u.style.left=i+"px",n.style.left=i+"px"):(u.style.left=o+"px",n.style.left=o+"px"),a!==undefined&&(f.style.left=t.offsetLeft+t.offsetParent.offsetLeft+"px",c+h'),r=0,i=this.node_student_cell.getY();t.each(function(t){var s=e.Node.create(''),o=t.cloneNode(!0);s.append(o.getHTML()),o=null,s.setStyles({height:t.get("offsetHeight")+"px",width:t.get("offsetWidth")+"px",position:"absolute",top:t.getY()-i+"px"}),r+=t.get("offsetHeight");var u=t.ancestor().getAttribute("class").split(" ").join(".");s.on("click",function(){e.one("."+u).all(".grade").toggleClass("hmarked")}),n.appendChild(s)},this),n.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:r+"px","background-color":"#f9f9f9"}),e.one("body").append(n)},float_user_header:function(){var t=e.Node.create(''),n=this.node_student_header_cell.cloneNode(!0);t.append(n.getHTML()),t.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"}),n=null,e.one("body").append(t)},float_assignment_header:function(){var t=e.all("#user-grades tr.heading .cell"),n=e.Node.create(''),r=0,i=0,s=this.node_student_header_cell.getX();t.each(function(t){var o=t.getAttribute("class"),u=/c[0-9]+/g,a=u.exec(o);a=a[0].replace("c","");var f=parseInt(a,10);++f;var l=t.getX(),c=t.cloneNode(!0),h=e.Node.create('');h.append(c.getHTML()),h.addClass(c.getAttribute("class")),c=null,h.setStyles({width:t.get("offsetWidth")+"px",height:t.get("offsetHeight")+"px",position:"absolute",left:l-s+"px"}),r+=parseInt(t.get("offsetWidth"),10),i=t.get("offsetHeight"),h.on("click",function(){e.all(".cell.c"+f).toggleClass("vmarked")}),n.appendChild(h)},this),n.setStyles({position:"absolute",top:this.node_student_header_cell.getY()+"px",left:this.node_student_header_cell.getX()+"px",width:r+"px",height:i+"px"}),e.one("body").append(n)},float_assignment_footer:function(){var t=e.all("#user-grades .lastrow .cell"),n=e.Node.create(''),r=0,i=this.node_footer_row.getX();t.each(function(t){var s=t.getX(),o=t.cloneNode(!0),u=e.Node.create('');u.append(o.getHTML()),u.setStyles({width:t.get("offsetWidth")+"px",height:"50px",position:"absolute",left:s-i+"px"}),n.append(u),r+=parseInt(t.get("offsetWidth"),10)},this);var s=e.one("#gradersubmit");if(s){var o=e.Node.create('");o.on("click",function(){YUI().use("node-event-simulate",function(e){e.one("#gradersubmit").simulate("click")})}),n.one(".gradebook-footer-cell").append(o)}n.setStyles({position:"absolute",left:this.node_footer_row.getX()+"px",bottom:"0",height:"50px",width:r+"px"}),e.one("body").append(n)}}},"@VERSION@");
+YUI.add("moodle-gradereport_grader-gradereporttable",function(e,t){function i(){i.superclass.constructor.apply(this,arguments)}function f(){}var n={FOOTERROW:"#user-grades .avg",GRADECELL:"td.grade",GRADERTABLE:".gradeparent table",GRADEPARENT:".gradeparent",HEADERCELL:".gradebook-header-cell",STUDENTHEADER:"#studentheader",USERCELL:"#user-grades .user.cell"},r={OVERRIDDEN:"overridden",STICKYFOOTER:"gradebook-footer-row-sticky",TOOLTIPACTIVE:"tooltipactive"};e.extend(i,e.Base,{_eventHandles:[],graderTable:null,initializer:function(){this.graderRegion=e.one(n.GRADEPARENT),this.graderTable=e.one(n.GRADERTABLE),this.setupFloatingHeaders(),this.setupTooltips(),this._hideSpinner()},showSpinner:function(){e.one(n.SPINNER).show()},hideSpinner:function(){e.one(n.SPINNER).hide()},getGradeUserName:function(e){var t=e.ancestor("tr"),n=t.one("th.user .username");return n?n.get("text"):""},getGradeItemName:function(t){var n=e.one("th.item[data-itemid='"+t.getData("itemid")+"']");return n?n.get("text"):""},getGradeFeedback:function(e){return e.getData("feedback")}}),e.namespace("M.gradereport_grader").ReportTable=i,e.namespace("M.gradereport_grader").init=function(t){return new e.M.gradereport_grader.ReportTable(t)};var s="height",o="width",u="offsetWidth",a="offsetHeight";f.ATTRS={},f.prototype={pageHeaderHeight:0,headerCell:null,firstUserCell:null,tableFooterRow:null,footerRow:null,gradeItemHeadingContainer:null,userColumnHeader:null,userColumn:null,firstUserCellBottom:0,firstUserCellLeft:0,lastUserCellTop:0,_eventHandles:[],setupFloatingHeaders:function(){this.firstUserCell=e.one(n.USERCELL);if(!this.firstUserCell){this._hideSpinner();return}return this._setupFloatingUserColumn(),this._setupFloatingUserHeader(),this._setupFloatingAssignmentHeaders(),this._setupFloatingAssignmentFooter(),this._calculateCellPositions(),this._handleScrollEvent(),this._setupEventHandlers(),this},_calculateCellPositions:function(){this.headerCellTop=this.headerCell.getY(),this.tableFooterRow&&(this.footerRowPosition=this.tableFooterRow.getY());var t=e.all(n.USERCELL);this.firstUserCellLeft=this.headerCell.getX();if(t.size()>1)this.firstUserCellBottom=t.item(1).getY(),this.lastUserCellTop=t.item(t.size()-2).getY();else{var r=t.item(0);this.lastUserCellTop=r.getY(),this.tableFooterRow?this.firstUserCellBottom=this.footerRowPosition:this.firstUserCellBottom=r.getY()+r.get("offsetHeight")}var i=e.one("header");this.pageHeaderHeight=0,i&&i.getComputedStyle("position")==="fixed"&&(this.pageHeaderHeight=i.get(a))},_setupEventHandlers:function(){this._eventHandles.push(e.one(e.config.win).on("scroll",this._handleScrollEvent,this),e.one(e.config.win).on("resize",this._handleResizeEvent,this),e.one(e.config.win).on("orientationchange",this._handleResizeEvent,this))},_setupFloatingUserColumn:function(){var t=e.all(n.USERCELL),r=e.Node.create('');t.each(function(t){var n=e.Node.create('');n.set("innerHTML",t.get("innerHTML")).setAttribute("data-uid",t.ancestor("tr").getData("uid")).setStyles({height:t.getComputedStyle(s),width:t.getComputedStyle(o)}),r.appendChild(n)},this),r.setStyles({left:this.firstUserCell.getX()+"px",position:"absolute",top:this.firstUserCell.getY()+"px"}),this.graderRegion.append(r),this.userColumn=r},_setupFloatingUserHeader:function(){this.headerCell=e.one(n.STUDENTHEADER);var t=e.Node.create('');t.set("innerHTML",this.headerCell.getHTML()),t.setStyles({height:this.headerCell.getComputedStyle(s),left:this.firstUserCell.getX()+"px",position:"absolute",top:this.headerCell.getY()+"px",width:this.firstUserCell.getComputedStyle(o)}),this.graderRegion.append(t),this.userColumnHeader=t},_setupFloatingAssignmentHeaders:function(){var t=e.all("#user-grades tr.heading .cell"),n=e.Node.create(''),r=0,i=0,f=this.headerCell.getX();t.each(function(t){var l=t.getX(),c=e.Node.create('');c.append(t.getHTML()).addClass(t.getAttribute("class")).setData("itemid",t.getData("itemid")).setStyles({height:t.getComputedStyle(s),left:l-f+"px",position:"absolute",width:t.getComputedStyle(o)}),r+=parseInt(t.get(u),10),i=t.get(a),n.appendChild(c)},this),n.setStyles({height:i+"px",left:this.headerCell.getX()+"px",position:"absolute",top:this.headerCell.getY()+"px",width:r+"px"}),this.userColumnHeader.insert(n,"before"),this.gradeItemHeadingContainer=n},_setupFloatingAssignmentFooter:function(){this.tableFooterRow=e.one("#user-grades .avg");if(!this.tableFooterRow)return;var t=this.tableFooterRow.all(".cell"),n=e.Node.create(''),r=0,i=this.tableFooterRow.getX();t.each(function(t){var s=e.Node.create('');s.set("innerHTML",t.getHTML()),s.setStyles({height:this._getHeight(t),left:t.getX()-i+"px",position:"absolute",width:this._getWidth(t)}),n.append(s),r+=parseInt(t.get(u),10)},this);var s=e.one("#gradersubmit");if(s){var o=e.Node.create('");o.on("click",function(){s.simulate("click")}),n.one(".gradebook-footer-cell").append(o)}n.setStyles({position:"absolute",left:this.tableFooterRow.getX()+"px",bottom:0,height:this._getHeight(this.tableFooterRow),width:r+"px",borderBottomWidth:e.DOM.getScrollbarWidth()+"px"}),this.graderRegion.append(n),this.footerRow=n},_handleScrollEvent:function(){var t={},n={},i={},s={};t.left=this.headerCell.getX(),e.config.win.pageYOffset+this.pageHeaderHeight>this.headerCellTop?e.config.win.pageYOffset+this.pageHeaderHeightthis.firstUserCellLeft?(i.left=e.config.win.pageXOffset+"px",n.left=e.config.win.pageXOffset+"px"):(i.left=this.firstUserCellLeft+"px",n.left=this.firstUserCellLeft+"px");if(this.footerRow){s.left=this.headerCell.getX();var o=e.config.win.pageYOffset+e.config.win.innerHeight,u=o-e.DOM.getScrollbarWidth();u-this.pageHeaderHeightthis.firstUserCellBottom?s.top=o-this.footerRow.get(a)+"px":s.top=this.firstUserCellBottom):(s.top=this.footerRowPosition+"px",this.footerRow.removeClass(r.STICKYFOOTER))}this.gradeItemHeadingContainer.setStyles(t),this.userColumnHeader.setStyles(n),this.userColumn.setStyles(i),this.footerRow.setStyles(s)},_handleResizeEvent:function(){this._calculateCellPositions(),this._handleScrollEvent();var t=this.gradeItemHeadingContainer.all(n.HEADERCELL),r=e.all("#user-grades .heading .cell"),i=this.headerCell.getX(),s=0;r.each(function(e,n){var r=t.item(n);s+=e.get(u);var a={width:e.getComputedStyle(o),left:e.getX()-i+"px"};r.setStyles(a)});var a=e.all("#gradebook-footer-container .gradebook-footer-cell");if(a.size()!==0){var f=e.all("#user-grades .avg .cell");f.each(function(e,t){var n=a.item(t),r={width:e.getComputedStyle(o),left:e.getX()-i+"px"};n.setStyles(r)})}this.gradeItemHeadingContainer.setStyle("width",s)},_getHeight:function(t){return e.UA.ie?t.get(a)+"px":t.getComputedStyle(s)},_getWidth:function(t){return e.UA.ie?t.get(u)+"px":t.getComputedStyle(o)}},e.Base.mix(e.M.gradereport_grader.ReportTable,[f])},"@VERSION@",{requires:["base","node","event","node-event-simulate"]});
diff --git a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js
index df09be6723c..d7360a6e6b7 100644
--- a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js
+++ b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js
@@ -15,378 +15,766 @@ YUI.add('moodle-gradereport_grader-gradereporttable', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-
/**
- * 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
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author Alfonso Roman
*/
-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() {
+ 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;
+ if (usercell) {
+ return usercell.get('text');
+ } else {
+ return '';
+ }
+ },
- // Check that Average footer is available.
- if (lastrow !== undefined) {
- footer = document.getElementById('gradebook-footer-container');
- lastrowpos = lastrow.offsetTop + lastrow.offsetParent.offsetTop;
- }
+ /**
+ * 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 '';
+ }
+ },
- // Viewport values
- var pageYOffset = window.pageYOffset;
- var windowInnerHeight = window.innerHeight;
+ /**
+ * 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');
+ }
+});
- //
- // Next do all the writing.
+Y.namespace('M.gradereport_grader').ReportTable = ReportTable;
+Y.namespace('M.gradereport_grader').init = function(config) {
+ return new Y.M.gradereport_grader.ReportTable(config);
+};
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
- // 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';
- }
+/**
+ * @module moodle-gradereport_grader-gradereporttable
+ * @submodule floatingheaders
+ */
- // 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';
- }
+/**
+ * Provides floating headers to the grader report.
+ *
+ * See {{#crossLink "M.gradereport_grader.ReportTable"}}{{/crossLink}} for details.
+ *
+ * @namespace M.gradereport_grader
+ * @class FloatingHeaders
+ */
- // Update footer
- if (lastrow !== undefined) {
- footer.style.left = userheadercell.offsetLeft + userheadercell.offsetParent.offsetLeft + 'px';
+var HEIGHT = 'height',
+ WIDTH = 'width',
+ OFFSETWIDTH = 'offsetWidth',
+ OFFSETHEIGHT = 'offsetHeight';
- 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');
- }
- }
- };
+function FloatingHeaders() {}
- // Add the floating 'average' footer if available.
- if (this.node_footer_row) {
- this.float_assignment_footer();
- }
+FloatingHeaders.ATTRS= {
+};
- // Set floating element initial positions by simulating scroll.
- onscroll();
+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,
- // 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');
+ /**
+ * A Node representing the header cell.
+ *
+ * @property headerCell
+ * @type Node
+ * @protected
+ */
+ headerCell: null,
- 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);
- }
+ /**
+ * A Node representing the first cell which contains user name information.
+ *
+ * @property firstUserCell
+ * @type Node
+ * @protected
+ */
+ firstUserCell: null,
- Y.one('#gradebook-header-container').setStyle('width', newcontainerwidth);
-
- };
+ /**
+ * 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;
}
- // 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');
- });
+ // 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;
},
- float_user_column: function() {
- // Grab the user names column
- var user_column = Y.all('#user-grades .user.cell');
+ /**
+ * 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();
- // Generate a floating table
- var floating_user_column = Y.Node.create('');
- var floating_user_column_height = 0;
- var user_column_offset = this.node_student_cell.getY();
+ // The footer row shows the grade averages and will be floated to the page bottom.
+ if (this.tableFooterRow) {
+ this.footerRowPosition = this.tableFooterRow.getY();
+ }
- user_column.each(function(node) {
+ var userCellList = Y.all(SELECTORS.USERCELL);
- // 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('');
+ // The left of the user cells matches the left of the headerCell.
+ this.firstUserCellLeft = this.headerCell.getX();
- // Grab the username
- var usernamenode = node.cloneNode(true);
- container_node.append(usernamenode.getHTML());
- usernamenode = null;
+ 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();
- container_node.setStyles({
- 'height': node.get('offsetHeight') + 'px',
- 'width': node.get('offsetWidth') + 'px',
- 'position': 'absolute',
- 'top': (node.getY() - user_column_offset) + 'px'
- });
+ // 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();
- 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);
+ 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('');
+
+ // Generate the new fields.
+ userColumn.each(function(node) {
+ // Create and configure the new container.
+ var containerNode = Y.Node.create('');
+ 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 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'
+ // Style the floating user container.
+ floatingUserColumn.setStyles({
+ left: this.firstUserCell.getX() + 'px',
+ position: 'absolute',
+ top: this.firstUserCell.getY() + 'px'
});
- Y.one('body').append(floating_user_column);
+ // 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;
},
- float_user_header: function() {
+
+ /**
+ * 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 floating_user_header_cell = Y.Node.create('');
+ var floatingUserCell = Y.Node.create('');
- // 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'
+ 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)
});
- // Safe for collection
- cellnode = null;
+ // Append to the grader region.
+ this.graderRegion.append(floatingUserCell);
- Y.one('body').append(floating_user_header_cell);
+ // Store a reference to this for later - we use it in the event handlers.
+ this.userColumnHeader = floatingUserCell;
},
- float_assignment_header: function() {
- var grade_headers = Y.all('#user-grades tr.heading .cell');
+ /**
+ * 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 floating_grade_headers = Y.Node.create('');
+ var floatingGradeHeaders = Y.Node.create('');
- 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 floatingGradeHeadersWidth = 0;
+ var floatingGradeHeadersHeight = 0;
+ var gradeHeadersOffset = this.headerCell.getX();
+ gradeHeaders.each(function(node) {
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('');
- newnode.append(nodeclone.getHTML());
- newnode.addClass(nodeclone.getAttribute('class'));
- nodeclone = null;
+ 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)
+ });
- newnode.setStyles({
- 'width': node.get('offsetWidth') + 'px',
- 'height': node.get('offsetHeight') + 'px',
- 'position': 'absolute',
- 'left': (nodepos - grade_headers_offset) + 'px'
- });
+ // 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);
- // 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);
+ // Append to our floating table.
+ floatingGradeHeaders.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'
+ floatingGradeHeaders.setStyles({
+ height: floatingGradeHeadersHeight + 'px',
+ left: this.headerCell.getX() + 'px',
+ position: 'absolute',
+ top: this.headerCell.getY() + 'px',
+ width: floatingGradeHeadersWidth + 'px'
});
- Y.one('body').append(floating_grade_headers);
+ // 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;
},
- float_assignment_footer: function() {
+
+ /**
+ * Create and setup the floating header row of grade item titles.
+ *
+ * @method _setupFloatingAssignmentFooter
+ * @protected
+ */
+ _setupFloatingAssignmentFooter: function() {
+ this.tableFooterRow = Y.one('#user-grades .avg');
+ if (!this.tableFooterRow) {
+ return;
+ }
// Generate the sticky footer row.
- // Grab the row.
- var footer_row = Y.all('#user-grades .lastrow .cell');
+ var footerCells = this.tableFooterRow.all('.cell');
+
// Create a container.
- var floating_grade_footers = Y.Node.create('');
- 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 floatingGraderFooter = Y.Node.create('');
+ var footerWidth = 0;
+ var footerRowOffset = this.tableFooterRow.getX();
+ // Copy cell content.
+ footerCells.each(function(node) {
var newnode = Y.Node.create('');
- newnode.append(cellnodeclone.getHTML());
+ newnode.set('innerHTML', node.getHTML());
newnode.setStyles({
- 'width': node.get('offsetWidth') + 'px',
- 'height': '50px',
- 'position': 'absolute',
- 'left': (nodepos - footer_row_offset) + 'px'
+ height: this._getHeight(node),
+ left: (node.getX() - footerRowOffset) + 'px',
+ position: 'absolute',
+ width: this._getWidth(node)
});
- floating_grade_footers.append(newnode);
- floating_grade_footer_width += parseInt(node.get('offsetWidth'), 10);
+ floatingGraderFooter.append(newnode);
+ footerWidth += parseInt(node.get(OFFSETWIDTH), 10);
}, this);
// Attach 'Update' button.
- var update_button = Y.one('#gradersubmit');
- if (update_button) {
- var button = Y.Node.create('');
+ 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.on('click', function() {
- YUI().use('node-event-simulate', function(Y) {
- Y.one('#gradersubmit').simulate('click');
- });
+ updateButton.simulate('click');
});
- floating_grade_footers.one('.gradebook-footer-cell').append(button);
+ floatingGraderFooter.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'
+ floatingGraderFooter.setStyles({
+ position: 'absolute',
+ left: this.tableFooterRow.getX() + 'px',
+ bottom: 0,
+ height: this._getHeight(this.tableFooterRow),
+ width: footerWidth + 'px',
+ borderBottomWidth: Y.DOM.getScrollbarWidth() + 'px'
});
- Y.one('body').append(floating_grade_footers);
+ // 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]);
-}, '@VERSION@');
+
+}, '@VERSION@', {"requires": ["base", "node", "event", "node-event-simulate"]});
diff --git a/grade/report/grader/yui/src/gradereporttable/build.json b/grade/report/grader/yui/src/gradereporttable/build.json
index 4fadacf58c5..1c838e87e26 100644
--- a/grade/report/grader/yui/src/gradereporttable/build.json
+++ b/grade/report/grader/yui/src/gradereporttable/build.json
@@ -3,8 +3,9 @@
"builds": {
"moodle-gradereport_grader-gradereporttable": {
"jsfiles": [
- "gradereporttable.js"
+ "gradereporttable.js",
+ "floatingheaders.js"
]
}
}
-}
\ No newline at end of file
+}
diff --git a/grade/report/grader/yui/src/gradereporttable/js/floatingheaders.js b/grade/report/grader/yui/src/gradereporttable/js/floatingheaders.js
new file mode 100644
index 00000000000..a2b5382458e
--- /dev/null
+++ b/grade/report/grader/yui/src/gradereporttable/js/floatingheaders.js
@@ -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 .
+
+/**
+ * @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('');
+
+ // Generate the new fields.
+ userColumn.each(function(node) {
+ // Create and configure the new container.
+ var containerNode = Y.Node.create('');
+ 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('');
+
+ // 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('');
+
+ var floatingGradeHeadersWidth = 0;
+ var floatingGradeHeadersHeight = 0;
+ var gradeHeadersOffset = this.headerCell.getX();
+
+ gradeHeaders.each(function(node) {
+ var nodepos = node.getX();
+
+ var newnode = Y.Node.create('');
+ 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('');
+ var footerWidth = 0;
+ var footerRowOffset = this.tableFooterRow.getX();
+
+ // Copy cell content.
+ footerCells.each(function(node) {
+ var newnode = Y.Node.create('');
+ 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.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]);
diff --git a/grade/report/grader/yui/src/gradereporttable/js/gradereporttable.js b/grade/report/grader/yui/src/gradereporttable/js/gradereporttable.js
index 37e0d0217e1..fc19bb863a8 100644
--- a/grade/report/grader/yui/src/gradereporttable/js/gradereporttable.js
+++ b/grade/report/grader/yui/src/gradereporttable/js/gradereporttable.js
@@ -13,380 +13,153 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-
/**
- * 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
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author Alfonso Roman
*/
-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('');
- 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('');
-
- // 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('');
-
- // 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('');
-
- 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('');
- 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('');
- 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('');
- 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.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);
};
diff --git a/grade/report/grader/yui/src/gradereporttable/meta/gradereporttable.json b/grade/report/grader/yui/src/gradereporttable/meta/gradereporttable.json
index ce28d26304a..685cc043c35 100644
--- a/grade/report/grader/yui/src/gradereporttable/meta/gradereporttable.json
+++ b/grade/report/grader/yui/src/gradereporttable/meta/gradereporttable.json
@@ -1,9 +1,10 @@
{
- "moodle-local_ucla-gradebooktable": {
+ "moodle-gradereport_grader-gradereporttable": {
"requires": [
+ "base",
"node",
"event",
"node-event-simulate"
]
}
-}
\ No newline at end of file
+}