[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 // This file is part of Moodle - http://moodle.org/ 2 // 3 // Moodle is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // Moodle is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 15 16 /** 17 * @module moodle-gradereport_grader-gradereporttable 18 * @submodule floatingheaders 19 */ 20 21 /** 22 * Provides floating headers to the grader report. 23 * 24 * See {{#crossLink "M.gradereport_grader.ReportTable"}}{{/crossLink}} for details. 25 * 26 * @namespace M.gradereport_grader 27 * @class FloatingHeaders 28 */ 29 30 var HEIGHT = 'height', 31 WIDTH = 'width', 32 OFFSETWIDTH = 'offsetWidth', 33 OFFSETHEIGHT = 'offsetHeight'; 34 35 CSS.FLOATING = 'floating'; 36 37 function FloatingHeaders() {} 38 39 FloatingHeaders.ATTRS= { 40 }; 41 42 FloatingHeaders.prototype = { 43 /** 44 * The height of the page header if a fixed position, floating header 45 * was found. 46 * 47 * @property pageHeaderHeight 48 * @type Number 49 * @default 0 50 * @protected 51 */ 52 pageHeaderHeight: 0, 53 54 /** 55 * A Node representing the container div. 56 * 57 * Positioning will be based on this element, which must have 58 * the CSS rule 'position: relative'. 59 * 60 * @property container 61 * @type Node 62 * @protected 63 */ 64 container: null, 65 66 /** 67 * A Node representing the header cell. 68 * 69 * @property headerCell 70 * @type Node 71 * @protected 72 */ 73 headerCell: null, 74 75 /** 76 * A Node representing the header row. 77 * 78 * @property headerRow 79 * @type Node 80 * @protected 81 */ 82 headerRow: null, 83 84 /** 85 * A Node representing the first cell which contains user name information. 86 * 87 * @property firstUserCell 88 * @type Node 89 * @protected 90 */ 91 firstUserCell: null, 92 93 /** 94 * A Node representing the first cell which does not contain a user header. 95 * 96 * @property firstNonUserCell 97 * @type Node 98 * @protected 99 */ 100 firstNonUserCell: null, 101 102 /** 103 * The position of the left of the first non-header cell in a row - the one after the email address. 104 * This is used when processing the scroll event as an optimisation. It must be updated when 105 * additional rows are loaded, or the window changes in some fashion. 106 * 107 * @property firstNonUserCellLeft 108 * @type Number 109 * @protected 110 */ 111 firstNonUserCellLeft: 0, 112 113 /** 114 * The width of the first non-header cell in a row - the one after the email address. 115 * This is used when processing the scroll event as an optimisation. It must be updated when 116 * additional rows are loaded, or the window changes in some fashion. 117 * This is only used for RTL calculations. 118 * 119 * @property firstNonUserCellWidth 120 * @type Number 121 * @protected 122 */ 123 firstNonUserCellWidth: 0, 124 125 /** 126 * A Node representing the original table footer row. 127 * 128 * @property tableFooterRow 129 * @type Node 130 * @protected 131 */ 132 tableFooterRow: null, 133 134 /** 135 * A Node representing the floating footer row in the grading table. 136 * 137 * @property footerRow 138 * @type Node 139 * @protected 140 */ 141 footerRow: null, 142 143 /** 144 * A Node representing the floating grade item header. 145 * 146 * @property gradeItemHeadingContainer 147 * @type Node 148 * @protected 149 */ 150 gradeItemHeadingContainer: null, 151 152 /** 153 * A Node representing the floating user header. This is the header with the Surname/First name 154 * sorting. 155 * 156 * @property userColumnHeader 157 * @type Node 158 * @protected 159 */ 160 userColumnHeader: null, 161 162 /** 163 * A Node representing the floating user column. This is the column containing all of the user 164 * names. 165 * 166 * @property userColumn 167 * @type Node 168 * @protected 169 */ 170 userColumn: null, 171 172 /** 173 * The position of the bottom of the first user cell. 174 * This is used when processing the scroll event as an optimisation. It must be updated when 175 * additional rows are loaded, or the window changes in some fashion. 176 * 177 * @property firstUserCellBottom 178 * @type Number 179 * @protected 180 */ 181 firstUserCellBottom: 0, 182 183 /** 184 * The position of the left of the first user cell. 185 * This is used when processing the scroll event as an optimisation. It must be updated when 186 * additional rows are loaded, or the window changes in some fashion. 187 * 188 * @property firstUserCellLeft 189 * @type Number 190 * @protected 191 */ 192 firstUserCellLeft: 0, 193 194 /** 195 * The width of the first user cell. 196 * This is used when processing the scroll event as an optimisation. It must be updated when 197 * additional rows are loaded, or the window changes in some fashion. 198 * This is only used for RTL calculations. 199 * 200 * @property firstUserCellWidth 201 * @type Number 202 * @protected 203 */ 204 firstUserCellWidth: 0, 205 206 /** 207 * The width of the dock if it is visible. 208 * 209 * @property dockWidth 210 * @type Number 211 * @protected 212 */ 213 dockWidth: 0, 214 215 /** 216 * The position of the top of the final user cell. 217 * This is used when processing the scroll event as an optimisation. It must be updated when 218 * additional rows are loaded, or the window changes in some fashion. 219 * 220 * @property lastUserCellTop 221 * @type Number 222 * @protected 223 */ 224 lastUserCellTop: 0, 225 226 /** 227 * A list of Nodes representing the generic floating rows. 228 * 229 * @property floatingHeaderRow 230 * @type Node{} 231 * @protected 232 */ 233 floatingHeaderRow: null, 234 235 /** 236 * Array of EventHandles. 237 * 238 * @type EventHandle[] 239 * @property _eventHandles 240 * @protected 241 */ 242 _eventHandles: [], 243 244 /** 245 * Setup the grader report table. 246 * 247 * @method setupFloatingHeaders 248 * @chainable 249 */ 250 setupFloatingHeaders: function() { 251 // Grab references to commonly used Nodes. 252 this.firstUserCell = Y.one(SELECTORS.USERCELL); 253 this.container = Y.one(SELECTORS.GRADEPARENT); 254 this.firstNonUserCell = Y.one(SELECTORS.USERMAIL).next(); 255 256 if (!this.firstUserCell) { 257 // No need for floating elements, there are no users. 258 return this; 259 } 260 261 // Generate floating elements. 262 this._setupFloatingUserColumn(); 263 this._setupFloatingUserHeader(); 264 this._setupFloatingAssignmentHeaders(); 265 this._setupFloatingAssignmentFooter(); 266 267 // Setup generic floating left-aligned headers. 268 this.floatingHeaderRow = {}; 269 270 // The 'Controls' row (shown in editing mode when certain options are set). 271 this._setupFloatingLeftHeaders('.controls .controls'); 272 273 // The 'Range' row (shown in editing mode when certain options are set). 274 this._setupFloatingLeftHeaders('.range .range'); 275 276 // The 'Overall Average' field. 277 this._setupFloatingLeftHeaders(SELECTORS.FOOTERTITLE); 278 279 // Additional setup for the footertitle. 280 this._setupFloatingAssignmentFooterTitle(); 281 282 // Calculate the positions of edge cells. These are used for positioning of the floating headers. 283 // This must be called after the floating headers are setup, but before the scroll event handler is invoked. 284 this._calculateCellPositions(); 285 286 // Setup the floating element initial positions by simulating scroll. 287 this._handleScrollEvent(); 288 289 // Setup the event handlers. 290 this._setupEventHandlers(); 291 292 // Listen for a resize event globally - other parts of the code not in this YUI wrapper may make changes to the 293 // fields which result in size changes. 294 Y.Global.on('moodle-gradereport_grader:resized', this._handleResizeEvent, this); 295 296 return this; 297 }, 298 299 /** 300 * Calculate the positions of some cells. These values are used heavily 301 * in scroll event handling. 302 * 303 * @method _calculateCellPositions 304 * @protected 305 */ 306 _calculateCellPositions: function() { 307 // The header row shows the grade item headers and is floated to the top of the window. 308 this.headerRowTop = this.headerRow.getY(); 309 310 // The footer row shows the grade averages and will be floated to the page bottom. 311 if (this.tableFooterRow) { 312 this.footerRowPosition = this.tableFooterRow.getY(); 313 } 314 315 // Add the width of the dock if it is visible. 316 this.dockWidth = 0; 317 var dock = Y.one('.has_dock #dock'); 318 if (dock) { 319 this.dockWidth = dock.get(OFFSETWIDTH); 320 } 321 322 var userCellList = Y.all(SELECTORS.USERCELL); 323 324 // The left of the user cells matches the left of the headerRow. 325 this.firstUserCellLeft = this.firstUserCell.getX(); 326 this.firstUserCellWidth = this.firstUserCell.get(OFFSETWIDTH); 327 328 // The left of the user cells matches the left of the footer title. 329 this.firstNonUserCellLeft = this.firstNonUserCell.getX(); 330 this.firstNonUserCellWidth = this.firstNonUserCell.get(OFFSETWIDTH); 331 332 if (userCellList.size() > 1) { 333 // Use the top of the second cell for the bottom of the first cell. 334 // This is used when scrolling to fix the footer to the top edge of the window. 335 var firstUserCell = userCellList.item(1); 336 this.firstUserCellBottom = firstUserCell.getY() + parseInt(firstUserCell.getComputedStyle(HEIGHT), 10); 337 338 // Use the top of the penultimate cell when scrolling the header. 339 // The header is the same size as the cells. 340 this.lastUserCellTop = userCellList.item(userCellList.size() - 2).getY(); 341 } else { 342 var firstItem = userCellList.item(0); 343 // We can't use the top of the second row as there is only one row. 344 this.lastUserCellTop = firstItem.getY(); 345 346 if (this.tableFooterRow) { 347 // The footer is present so we can use that. 348 this.firstUserCellBottom = this.footerRowPosition + parseInt(this.tableFooterRow.getComputedStyle(HEIGHT), 10); 349 } else { 350 // No other clues - calculate the top instead. 351 this.firstUserCellBottom = firstItem.getY() + firstItem.get('offsetHeight'); 352 } 353 } 354 355 // Check whether a header is present and whether it is floating. 356 var header = Y.one('header'); 357 this.pageHeaderHeight = 0; 358 if (header) { 359 if (header.getComputedStyle('position') === 'fixed') { 360 this.pageHeaderHeight = header.get(OFFSETHEIGHT); 361 } else { 362 var navbar = Y.one('.navbar'); 363 364 if (navbar && navbar.getComputedStyle('position') === 'fixed') { 365 // If the navbar exists and isn't fixed, we need to offset the page header to accommodate for it. 366 this.pageHeaderHeight = navbar.get(OFFSETHEIGHT); 367 } 368 } 369 } 370 }, 371 372 /** 373 * Get the relative XY of the node. 374 * 375 * @method _getRelativeXY 376 * @protected 377 * @param {Node} node The node to get the position of. 378 * @return {Array} Containing X and Y. 379 */ 380 _getRelativeXY: function(node) { 381 return this._getRelativeXYFromXY(node.getX(), node.getY()); 382 }, 383 384 /** 385 * Get the relative positioning from coordinates. 386 * 387 * This gives the position according to the parent of the table, which must 388 * be set as position: relative. 389 * 390 * @method _getRelativeXYFromXY 391 * @protected 392 * @param {Number} x X position. 393 * @param {Number} y Y position. 394 * @return {Array} Containing X and Y. 395 */ 396 _getRelativeXYFromXY: function(x, y) { 397 var parentXY = this.container.getXY(); 398 return [x - parentXY[0], y - parentXY[1]]; 399 }, 400 401 /** 402 * Get the relative positioning of an elements from coordinates. 403 * 404 * @method _getRelativeXFromX 405 * @protected 406 * @param {Number} pos X position. 407 * @return {Number} relative X position. 408 */ 409 _getRelativeXFromX: function(pos) { 410 return this._getRelativeXYFromXY(pos, 0)[0]; 411 }, 412 413 /** 414 * Get the relative positioning of an elements from coordinates. 415 * 416 * @method _getRelativeYFromY 417 * @protected 418 * @param {Number} pos Y position. 419 * @return {Number} relative Y position. 420 */ 421 _getRelativeYFromY: function(pos) { 422 return this._getRelativeXYFromXY(0, pos)[1]; 423 }, 424 425 /** 426 * Return the size of the horizontal scrollbar. 427 * 428 * @method _getScrollBarHeight 429 * @protected 430 * @return {Number} Height of the scrollbar. 431 */ 432 _getScrollBarHeight: function() { 433 if (Y.UA.ie && Y.UA.ie >= 10) { 434 // IE has transparent scrollbars, which sometimes disappear... it's better to ignore them. 435 return 0; 436 } else if (Y.config.doc.body.scrollWidth > Y.config.doc.body.clientWidth) { 437 // The document can be horizontally scrolled. 438 return Y.DOM.getScrollbarWidth(); 439 } 440 return 0; 441 }, 442 443 /** 444 * Setup the main event listeners. 445 * These deal with things like window events. 446 * 447 * @method _setupEventHandlers 448 * @protected 449 */ 450 _setupEventHandlers: function() { 451 this._eventHandles.push( 452 // Listen for window scrolls, resizes, and rotation events. 453 Y.one(Y.config.win).on('scroll', this._handleScrollEvent, this), 454 Y.one(Y.config.win).on('resize', this._handleResizeEvent, this), 455 Y.one(Y.config.win).on('orientationchange', this._handleResizeEvent, this), 456 Y.Global.on('dock:shown', this._handleResizeEvent, this), 457 Y.Global.on('dock:hidden', this._handleResizeEvent, this) 458 ); 459 }, 460 461 /** 462 * Create and setup the floating column of user names. 463 * 464 * @method _setupFloatingUserColumn 465 * @protected 466 */ 467 _setupFloatingUserColumn: function() { 468 // Grab all cells in the user names column. 469 var userColumn = Y.all(SELECTORS.USERCELL), 470 471 // Create a floating table. 472 floatingUserColumn = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater sideonly"></div>'), 473 474 // Get the XY for the floating element. 475 coordinates = this._getRelativeXY(this.firstUserCell); 476 477 // Generate the new fields. 478 userColumn.each(function(node) { 479 // Create and configure the new container. 480 var containerNode = Y.Node.create('<div></div>'); 481 containerNode.set('innerHTML', node.get('innerHTML')) 482 .setAttribute('class', node.getAttribute('class')) 483 .setAttribute('data-uid', node.ancestor('tr').getData('uid')) 484 .setStyles({ 485 height: node.getComputedStyle(HEIGHT), 486 width: node.getComputedStyle(WIDTH) 487 }); 488 489 // Add the new nodes to our floating table. 490 floatingUserColumn.appendChild(containerNode); 491 }, this); 492 493 // Style the floating user container. 494 floatingUserColumn.setStyles({ 495 left: coordinates[0] + 'px', 496 position: 'absolute', 497 top: coordinates[1] + 'px' 498 }); 499 500 // Append to the grader region. 501 this.graderRegion.append(floatingUserColumn); 502 503 // Store a reference to this for later - we use it in the event handlers. 504 this.userColumn = floatingUserColumn; 505 }, 506 507 /** 508 * Create and setup the floating username header cell. 509 * 510 * @method _setupFloatingUserHeader 511 * @protected 512 */ 513 _setupFloatingUserHeader: function() { 514 // We make various references to the header cells. Store it for later. 515 this.headerRow = Y.one(SELECTORS.HEADERROW); 516 this.headerCell = Y.one(SELECTORS.STUDENTHEADER); 517 518 // Create the floating row and cell. 519 var floatingUserHeaderRow = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater sideonly heading"></div>'), 520 floatingUserHeaderCell = Y.Node.create('<div></div>'), 521 nodepos = this._getRelativeXY(this.headerCell)[0], 522 coordinates = this._getRelativeXY(this.headerRow), 523 gradeHeadersOffset = coordinates[0]; 524 525 // Append the content and style to the floating cell. 526 floatingUserHeaderCell 527 .set('innerHTML', this.headerCell.getHTML()) 528 .setAttribute('class', this.headerCell.getAttribute('class')) 529 .setStyles({ 530 // The header is larger than the user cells, so we take the user cell. 531 width: this.firstUserCell.getComputedStyle(WIDTH), 532 left: (nodepos - gradeHeadersOffset) + 'px' 533 }); 534 535 // Style the floating row. 536 floatingUserHeaderRow 537 .setStyles({ 538 left: coordinates[0] + 'px', 539 position: 'absolute', 540 top: coordinates[1] + 'px' 541 }); 542 543 // Append the cell to the row, and finally to the region. 544 floatingUserHeaderRow.append(floatingUserHeaderCell); 545 this.graderRegion.append(floatingUserHeaderRow); 546 547 // Store a reference to this for later - we use it in the event handlers. 548 this.userColumnHeader = floatingUserHeaderRow; 549 }, 550 551 /** 552 * Create and setup the floating grade item header row. 553 * 554 * @method _setupFloatingAssignmentHeaders 555 * @protected 556 */ 557 _setupFloatingAssignmentHeaders: function() { 558 this.headerRow = Y.one('#user-grades tr.heading'); 559 560 var gradeHeaders = Y.all('#user-grades tr.heading .cell'); 561 562 // Generate a floating headers 563 var floatingGradeHeaders = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater heading"></div>'); 564 565 var coordinates = this._getRelativeXY(this.headerRow); 566 567 var floatingGradeHeadersWidth = 0; 568 var floatingGradeHeadersHeight = 0; 569 var gradeHeadersOffset = coordinates[0]; 570 571 gradeHeaders.each(function(node) { 572 var nodepos = this._getRelativeXY(node)[0]; 573 574 var newnode = Y.Node.create('<div></div>'); 575 newnode.append(node.getHTML()) 576 .setAttribute('class', node.getAttribute('class')) 577 .setData('itemid', node.getData('itemid')) 578 .setStyles({ 579 height: node.getComputedStyle(HEIGHT), 580 left: (nodepos - gradeHeadersOffset) + 'px', 581 position: 'absolute', 582 width: node.getComputedStyle(WIDTH) 583 }); 584 585 // Sum up total widths - these are used in the container styles. 586 // Use the offsetHeight and Width here as this contains the 587 // padding, margin, and borders. 588 floatingGradeHeadersWidth += parseInt(node.get(OFFSETWIDTH), 10); 589 floatingGradeHeadersHeight = node.get(OFFSETHEIGHT); 590 591 // Append to our floating table. 592 floatingGradeHeaders.appendChild(newnode); 593 }, this); 594 595 // Position header table. 596 floatingGradeHeaders.setStyles({ 597 height: floatingGradeHeadersHeight + 'px', 598 left: coordinates[0] + 'px', 599 position: 'absolute', 600 top: coordinates[1] + 'px', 601 width: floatingGradeHeadersWidth + 'px' 602 }); 603 604 // Insert in place before the grader headers. 605 this.userColumnHeader.insert(floatingGradeHeaders, 'before'); 606 607 // Store a reference to this for later - we use it in the event handlers. 608 this.gradeItemHeadingContainer = floatingGradeHeaders; 609 }, 610 611 /** 612 * Create and setup the floating header row of grade item titles. 613 * 614 * @method _setupFloatingAssignmentFooter 615 * @protected 616 */ 617 _setupFloatingAssignmentFooter: function() { 618 this.tableFooterRow = Y.one('#user-grades .avg'); 619 if (!this.tableFooterRow) { 620 Y.log('Averages footer not found - unable to float it.', 'warn', LOGNS); 621 return; 622 } 623 624 // Generate the sticky footer row. 625 var footerCells = this.tableFooterRow.all('.cell'); 626 627 // Create a container. 628 var floatingGraderFooter = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater avg"></div>'); 629 var footerWidth = 0; 630 var coordinates = this._getRelativeXY(this.tableFooterRow); 631 var footerRowOffset = coordinates[0]; 632 var floatingGraderFooterHeight = 0; 633 634 // Copy cell content. 635 footerCells.each(function(node) { 636 var newnode = Y.Node.create('<div></div>'); 637 var nodepos = this._getRelativeXY(node)[0]; 638 newnode.set('innerHTML', node.getHTML()) 639 .setAttribute('class', node.getAttribute('class')) 640 .setStyles({ 641 height: node.getComputedStyle(HEIGHT), 642 left: (nodepos - footerRowOffset) + 'px', 643 position: 'absolute', 644 width: node.getComputedStyle(WIDTH) 645 }); 646 647 floatingGraderFooter.append(newnode); 648 floatingGraderFooterHeight = node.get(OFFSETHEIGHT); 649 footerWidth += parseInt(node.get(OFFSETWIDTH), 10); 650 }, this); 651 652 // Position the row. 653 floatingGraderFooter.setStyles({ 654 position: 'absolute', 655 left: coordinates[0] + 'px', 656 bottom: '1px', 657 height: floatingGraderFooterHeight + 'px', 658 width: footerWidth + 'px' 659 }); 660 661 // Append to the grader region. 662 this.graderRegion.append(floatingGraderFooter); 663 664 this.footerRow = floatingGraderFooter; 665 }, 666 667 /** 668 * Create and setup the floating footer title cell. 669 * 670 * @method _setupFloatingAssignmentFooterTitle 671 * @protected 672 */ 673 _setupFloatingAssignmentFooterTitle: function() { 674 var floatingFooterRow = this.floatingHeaderRow[SELECTORS.FOOTERTITLE]; 675 if (floatingFooterRow) { 676 // Style the floating row. 677 floatingFooterRow 678 .setStyles({ 679 bottom: '1px' 680 }); 681 } 682 }, 683 684 /** 685 * Create and setup the floating left headers. 686 * 687 * @method _setupFloatingLeftHeaders 688 * @protected 689 */ 690 _setupFloatingLeftHeaders: function(headerSelector) { 691 // We make various references to the origin cell. Store it for later. 692 var origin = Y.one(headerSelector); 693 694 if (!origin) { 695 return; 696 } 697 698 // Create the floating row and cell. 699 var floatingRow = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater sideonly"></div>'), 700 floatingCell = Y.Node.create('<div></div>'), 701 coordinates = this._getRelativeXY(origin), 702 width = this.firstUserCell.getComputedStyle(WIDTH), 703 height = origin.get(OFFSETHEIGHT); 704 705 // Append the content and style to the floating cell. 706 floatingCell 707 .set('innerHTML', origin.getHTML()) 708 .setAttribute('class', origin.getAttribute('class')) 709 .setStyles({ 710 // The header is larger than the user cells, so we take the user cell. 711 width: width 712 }); 713 714 // Style the floating row. 715 floatingRow 716 .setStyles({ 717 position: 'absolute', 718 top: coordinates[1] + 'px', 719 left: coordinates[0] + 'px', 720 height: height + 'px' 721 }) 722 // Add all classes from the parent to the row 723 .addClass(origin.get('parentNode').get('className')); 724 725 // Append the cell to the row, and finally to the region. 726 floatingRow.append(floatingCell); 727 this.graderRegion.append(floatingRow); 728 729 // Store a reference to this for later - we use it in the event handlers. 730 this.floatingHeaderRow[headerSelector] = floatingRow; 731 }, 732 733 /** 734 * Process a Scroll Event on the window. 735 * 736 * @method _handleScrollEvent 737 * @protected 738 */ 739 _handleScrollEvent: function() { 740 // Performance is important in this function as it is called frequently and in quick succesion. 741 // To prevent layout thrashing when the DOM is repeatedly updated and queried, updated and queried, 742 // updates must be batched. 743 744 // Next do all the calculations. 745 var gradeItemHeadingContainerStyles = {}, 746 userColumnHeaderStyles = {}, 747 userColumnStyles = {}, 748 footerStyles = {}, 749 coord = 0, 750 floatingUserTriggerPoint = 0, // The X position at which the floating should start. 751 floatingUserRelativePoint = 0, // The point to use when calculating the new position. 752 headerFloats = false, 753 userFloats = false, 754 footerFloats = false, 755 leftTitleFloats = false, 756 floatingHeaderStyles = {}, 757 floatingFooterTitleStyles = {}, 758 floatingFooterTitleRow = false; 759 760 // Header position. 761 gradeItemHeadingContainerStyles.left = this._getRelativeXFromX(this.headerRow.getX()); 762 if (Y.config.win.pageYOffset + this.pageHeaderHeight > this.headerRowTop) { 763 headerFloats = true; 764 if (Y.config.win.pageYOffset + this.pageHeaderHeight < this.lastUserCellTop) { 765 coord = this._getRelativeYFromY(Y.config.win.pageYOffset + this.pageHeaderHeight); 766 gradeItemHeadingContainerStyles.top = coord + 'px'; 767 userColumnHeaderStyles.top = coord + 'px'; 768 } else { 769 coord = this._getRelativeYFromY(this.lastUserCellTop); 770 gradeItemHeadingContainerStyles.top = coord + 'px'; 771 userColumnHeaderStyles.top = coord + 'px'; 772 } 773 } else { 774 headerFloats = false; 775 coord = this._getRelativeYFromY(this.headerRowTop); 776 gradeItemHeadingContainerStyles.top = coord + 'px'; 777 userColumnHeaderStyles.top = coord + 'px'; 778 } 779 780 // User column position. 781 if (right_to_left()) { 782 floatingUserTriggerPoint = Y.config.win.innerWidth + Y.config.win.pageXOffset - this.dockWidth; 783 floatingUserRelativePoint = floatingUserTriggerPoint - this.firstUserCellWidth; 784 userFloats = floatingUserTriggerPoint < (this.firstUserCellLeft + this.firstUserCellWidth); 785 leftTitleFloats = (floatingUserTriggerPoint - this.firstNonUserCellWidth) < 786 (this.firstNonUserCellLeft + this.firstUserCellWidth); 787 } else { 788 floatingUserRelativePoint = Y.config.win.pageXOffset; 789 floatingUserTriggerPoint = floatingUserRelativePoint + this.dockWidth; 790 userFloats = floatingUserTriggerPoint > this.firstUserCellLeft; 791 leftTitleFloats = floatingUserTriggerPoint > (this.firstNonUserCellLeft - this.firstUserCellWidth); 792 } 793 794 if (userFloats) { 795 coord = this._getRelativeXFromX(floatingUserRelativePoint); 796 userColumnStyles.left = coord + 'px'; 797 userColumnHeaderStyles.left = coord + 'px'; 798 } else { 799 coord = this._getRelativeXFromX(this.firstUserCellLeft); 800 userColumnStyles.left = coord + 'px'; 801 userColumnHeaderStyles.left = coord + 'px'; 802 } 803 804 // Update the miscellaneous left-only floats. 805 Y.Object.each(this.floatingHeaderRow, function(origin, key) { 806 floatingHeaderStyles[key] = { 807 left: userColumnStyles.left 808 }; 809 }, this); 810 811 // Update footer. 812 if (this.footerRow) { 813 footerStyles.left = this._getRelativeXFromX(this.headerRow.getX()); 814 815 // Determine whether the footer should now be shown as sticky. 816 var pageHeight = Y.config.win.innerHeight, 817 pageOffset = Y.config.win.pageYOffset, 818 bottomScrollPosition = pageHeight - this._getScrollBarHeight() + pageOffset, 819 footerRowHeight = parseInt(this.footerRow.getComputedStyle(HEIGHT), 10), 820 footerBottomPosition = footerRowHeight + this.footerRowPosition; 821 822 floatingFooterTitleStyles = floatingHeaderStyles[SELECTORS.FOOTERTITLE]; 823 floatingFooterTitleRow = this.floatingHeaderRow[SELECTORS.FOOTERTITLE]; 824 if (bottomScrollPosition < footerBottomPosition && bottomScrollPosition > this.firstUserCellBottom) { 825 // We have not scrolled below the footer, nor above the first row. 826 footerStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px'; 827 footerFloats = true; 828 } else { 829 // The footer should not float any more. 830 footerStyles.bottom = '1px'; 831 footerFloats = false; 832 } 833 if (floatingFooterTitleStyles) { 834 floatingFooterTitleStyles.bottom = footerStyles.bottom; 835 floatingFooterTitleStyles.top = null; 836 } 837 floatingHeaderStyles[SELECTORS.FOOTERTITLE] = floatingFooterTitleStyles; 838 } 839 840 // Apply the styles. 841 this.gradeItemHeadingContainer.setStyles(gradeItemHeadingContainerStyles); 842 this.userColumnHeader.setStyles(userColumnHeaderStyles); 843 this.userColumn.setStyles(userColumnStyles); 844 this.footerRow.setStyles(footerStyles); 845 846 // And apply the styles to the generic left headers. 847 Y.Object.each(floatingHeaderStyles, function(styles, key) { 848 this.floatingHeaderRow[key].setStyles(styles); 849 }, this); 850 851 // Mark the elements as floating, or not. 852 if (headerFloats) { 853 this.gradeItemHeadingContainer.addClass(CSS.FLOATING); 854 } else { 855 this.gradeItemHeadingContainer.removeClass(CSS.FLOATING); 856 } 857 858 if (userFloats) { 859 this.userColumnHeader.addClass(CSS.FLOATING); 860 this.userColumn.addClass(CSS.FLOATING); 861 } else { 862 this.userColumnHeader.removeClass(CSS.FLOATING); 863 this.userColumn.removeClass(CSS.FLOATING); 864 } 865 866 if (footerFloats) { 867 this.footerRow.addClass(CSS.FLOATING); 868 } else { 869 this.footerRow.removeClass(CSS.FLOATING); 870 } 871 872 Y.Object.each(this.floatingHeaderRow, function(value, key) { 873 if (leftTitleFloats) { 874 this.floatingHeaderRow[key].addClass(CSS.FLOATING); 875 } else { 876 this.floatingHeaderRow[key].removeClass(CSS.FLOATING); 877 } 878 }, this); 879 880 // The footer title has a more specific float setting. 881 if (floatingFooterTitleRow) { 882 if (leftTitleFloats) { 883 floatingFooterTitleRow.addClass(CSS.FLOATING); 884 } else { 885 floatingFooterTitleRow.removeClass(CSS.FLOATING); 886 } 887 } 888 889 }, 890 891 /** 892 * Process a size change Event on the window. 893 * 894 * @method _handleResizeEvent 895 * @protected 896 */ 897 _handleResizeEvent: function() { 898 // Recalculate the position of the edge cells for scroll positioning. 899 this._calculateCellPositions(); 900 901 // Simulate a scroll. 902 this._handleScrollEvent(); 903 904 // Resize user cells. 905 var userWidth = this.firstUserCell.getComputedStyle(WIDTH); 906 var userCells = Y.all(SELECTORS.USERCELL); 907 this.userColumnHeader.one('.cell').setStyle('width', userWidth); 908 this.userColumn.all('.cell').each(function(cell, idx) { 909 cell.setStyles({ 910 width: userWidth, 911 height: userCells.item(idx).getComputedStyle(HEIGHT) 912 }); 913 }, this); 914 915 // Resize headers & footers. 916 // This is an expensive operation, not expected to happen often. 917 var headers = this.gradeItemHeadingContainer.all('.cell'); 918 var resizedcells = Y.all(SELECTORS.HEADERCELLS); 919 920 var headeroffsetleft = this.headerRow.getX(); 921 var newcontainerwidth = 0; 922 resizedcells.each(function(cell, idx) { 923 var headercell = headers.item(idx); 924 925 newcontainerwidth += cell.get(OFFSETWIDTH); 926 var styles = { 927 width: cell.getComputedStyle(WIDTH), 928 left: cell.getX() - headeroffsetleft + 'px' 929 }; 930 headercell.setStyles(styles); 931 }); 932 933 if (this.footerRow) { 934 var footers = this.footerRow.all('.cell'); 935 if (footers.size() !== 0) { 936 var resizedavgcells = Y.all(SELECTORS.FOOTERCELLS); 937 938 resizedavgcells.each(function(cell, idx) { 939 var footercell = footers.item(idx); 940 var styles = { 941 width: cell.getComputedStyle(WIDTH), 942 left: cell.getX() - headeroffsetleft + 'px' 943 }; 944 footercell.setStyles(styles); 945 }); 946 } 947 } 948 949 // Resize the title areas too. 950 Y.Object.each(this.floatingHeaderRow, function(row) { 951 row.one('div').setStyle('width', userWidth); 952 }, this); 953 954 this.gradeItemHeadingContainer.setStyle('width', newcontainerwidth); 955 } 956 957 }; 958 959 Y.Base.mix(Y.M.gradereport_grader.ReportTable, [FloatingHeaders]);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |