5a920362 |
1 | (function ($) { |
2 | |
3 | /** |
4 | * Attaches sticky table headers. |
5 | */ |
6 | Drupal.behaviors.tableHeader = { |
7 | attach: function (context, settings) { |
8 | if (!$.support.positionFixed) { |
9 | return; |
10 | } |
11 | |
12 | $('table.sticky-enabled', context).once('tableheader', function () { |
13 | $(this).data("drupal-tableheader", new Drupal.tableHeader(this)); |
14 | }); |
15 | } |
16 | }; |
17 | |
18 | /** |
19 | * Constructor for the tableHeader object. Provides sticky table headers. |
20 | * |
21 | * @param table |
22 | * DOM object for the table to add a sticky header to. |
23 | */ |
24 | Drupal.tableHeader = function (table) { |
25 | var self = this; |
26 | |
27 | this.originalTable = $(table); |
28 | this.originalHeader = $(table).children('thead'); |
29 | this.originalHeaderCells = this.originalHeader.find('> tr > th'); |
30 | |
31 | // Clone the table header so it inherits original jQuery properties. Hide |
32 | // the table to avoid a flash of the header clone upon page load. |
33 | this.stickyTable = $('<table class="sticky-header"/>') |
34 | .insertBefore(this.originalTable) |
35 | .css({ position: 'fixed', top: '0px' }); |
36 | this.stickyHeader = this.originalHeader.clone(true) |
37 | .hide() |
38 | .appendTo(this.stickyTable); |
39 | this.stickyHeaderCells = this.stickyHeader.find('> tr > th'); |
40 | |
41 | this.originalTable.addClass('sticky-table'); |
42 | $(window) |
43 | .bind('scroll.drupal-tableheader', $.proxy(this, 'eventhandlerRecalculateStickyHeader')) |
44 | .bind('resize.drupal-tableheader', { calculateWidth: true }, $.proxy(this, 'eventhandlerRecalculateStickyHeader')) |
45 | // Make sure the anchor being scrolled into view is not hidden beneath the |
46 | // sticky table header. Adjust the scrollTop if it does. |
47 | .bind('drupalDisplaceAnchor.drupal-tableheader', function () { |
48 | window.scrollBy(0, -self.stickyTable.outerHeight()); |
49 | }) |
50 | // Make sure the element being focused is not hidden beneath the sticky |
51 | // table header. Adjust the scrollTop if it does. |
52 | .bind('drupalDisplaceFocus.drupal-tableheader', function (event) { |
53 | if (self.stickyVisible && event.clientY < (self.stickyOffsetTop + self.stickyTable.outerHeight()) && event.$target.closest('sticky-header').length === 0) { |
54 | window.scrollBy(0, -self.stickyTable.outerHeight()); |
55 | } |
56 | }) |
57 | .triggerHandler('resize.drupal-tableheader'); |
58 | |
59 | // We hid the header to avoid it showing up erroneously on page load; |
60 | // we need to unhide it now so that it will show up when expected. |
61 | this.stickyHeader.show(); |
62 | }; |
63 | |
64 | /** |
65 | * Event handler: recalculates position of the sticky table header. |
66 | * |
67 | * @param event |
68 | * Event being triggered. |
69 | */ |
70 | Drupal.tableHeader.prototype.eventhandlerRecalculateStickyHeader = function (event) { |
71 | var self = this; |
72 | var calculateWidth = event.data && event.data.calculateWidth; |
73 | |
74 | // Reset top position of sticky table headers to the current top offset. |
75 | this.stickyOffsetTop = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0; |
76 | this.stickyTable.css('top', this.stickyOffsetTop + 'px'); |
77 | |
78 | // Save positioning data. |
79 | var viewHeight = document.documentElement.scrollHeight || document.body.scrollHeight; |
80 | if (calculateWidth || this.viewHeight !== viewHeight) { |
81 | this.viewHeight = viewHeight; |
82 | this.vPosition = this.originalTable.offset().top - 4 - this.stickyOffsetTop; |
83 | this.hPosition = this.originalTable.offset().left; |
84 | this.vLength = this.originalTable[0].clientHeight - 100; |
85 | calculateWidth = true; |
86 | } |
87 | |
88 | // Track horizontal positioning relative to the viewport and set visibility. |
89 | var hScroll = document.documentElement.scrollLeft || document.body.scrollLeft; |
90 | var vOffset = (document.documentElement.scrollTop || document.body.scrollTop) - this.vPosition; |
91 | this.stickyVisible = vOffset > 0 && vOffset < this.vLength; |
92 | this.stickyTable.css({ left: (-hScroll + this.hPosition) + 'px', visibility: this.stickyVisible ? 'visible' : 'hidden' }); |
93 | |
94 | // Only perform expensive calculations if the sticky header is actually |
95 | // visible or when forced. |
96 | if (this.stickyVisible && (calculateWidth || !this.widthCalculated)) { |
97 | this.widthCalculated = true; |
98 | // Resize header and its cell widths. |
99 | this.stickyHeaderCells.each(function (index) { |
100 | var cellWidth = self.originalHeaderCells.eq(index).css('width'); |
101 | // Exception for IE7. |
102 | if (cellWidth == 'auto') { |
103 | cellWidth = self.originalHeaderCells.get(index).clientWidth + 'px'; |
104 | } |
105 | $(this).css('width', cellWidth); |
106 | }); |
107 | this.stickyTable.css('width', this.originalTable.css('width')); |
108 | } |
109 | }; |
110 | |
111 | })(jQuery); |