1(function ($) { 2 3/** 4 * Attaches sticky table headers. 5 */ 6Drupal.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 */ 24Drupal.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 this.displayWeight = null; 31 32 // React to columns change to avoid making checks in the scroll callback. 33 this.originalTable.bind('columnschange', function (e, display) { 34 // This will force header size to be calculated on scroll. 35 self.widthCalculated = (self.displayWeight !== null && self.displayWeight === display); 36 self.displayWeight = display; 37 }); 38 39 // Clone the table header so it inherits original jQuery properties. Hide 40 // the table to avoid a flash of the header clone upon page load. 41 this.stickyTable = $('<table class="sticky-header"/>') 42 .insertBefore(this.originalTable) 43 .css({ position: 'fixed', top: '0px' }); 44 this.stickyHeader = this.originalHeader.clone(true) 45 .hide() 46 .appendTo(this.stickyTable); 47 this.stickyHeaderCells = this.stickyHeader.find('> tr > th'); 48 49 this.originalTable.addClass('sticky-table'); 50 $(window) 51 .bind('scroll.drupal-tableheader', $.proxy(this, 'eventhandlerRecalculateStickyHeader')) 52 .bind('resize.drupal-tableheader', { calculateWidth: true }, $.proxy(this, 'eventhandlerRecalculateStickyHeader')) 53 // Make sure the anchor being scrolled into view is not hidden beneath the 54 // sticky table header. Adjust the scrollTop if it does. 55 .bind('drupalDisplaceAnchor.drupal-tableheader', function () { 56 window.scrollBy(0, -self.stickyTable.outerHeight()); 57 }) 58 // Make sure the element being focused is not hidden beneath the sticky 59 // table header. Adjust the scrollTop if it does. 60 .bind('drupalDisplaceFocus.drupal-tableheader', function (event) { 61 if (self.stickyVisible && event.clientY < (self.stickyOffsetTop + self.stickyTable.outerHeight()) && event.$target.closest('sticky-header').length === 0) { 62 window.scrollBy(0, -self.stickyTable.outerHeight()); 63 } 64 }) 65 .triggerHandler('resize.drupal-tableheader'); 66 67 // We hid the header to avoid it showing up erroneously on page load; 68 // we need to unhide it now so that it will show up when expected. 69 this.stickyHeader.show(); 70}; 71 72/** 73 * Call the header offset function to prevent use of eval(). 74 * 75 * @param accessor 76 * The callback function name. 77 * @return 78 * The callback result. 79 */ 80Drupal.tableHeader.callHeaderOffsetFunction = function(accessor) { 81 accessor = accessor.split('.'); 82 var callback = window; 83 for (var i = 0, len = accessor.length - 1; i < len; i++) { 84 if (typeof callback[accessor[i]] !== 'function' && typeof callback[accessor[i]] != 'object') { 85 return 0; 86 } 87 callback = callback[accessor[i]]; 88 } 89 if (typeof callback[accessor[accessor.length - 1]] === 'function') { 90 return callback[accessor[accessor.length - 1]](); 91 } 92 return 0; 93}; 94 95/** 96 * Event handler: recalculates position of the sticky table header. 97 * 98 * @param event 99 * Event being triggered. 100 */ 101Drupal.tableHeader.prototype.eventhandlerRecalculateStickyHeader = function (event) { 102 var self = this; 103 var calculateWidth = event.data && event.data.calculateWidth; 104 105 // Reset top position of sticky table headers to the current top offset. 106 this.stickyOffsetTop = Drupal.settings.tableHeaderOffset ? Drupal.tableHeader.callHeaderOffsetFunction(Drupal.settings.tableHeaderOffset) : 0; 107 this.stickyTable.css('top', this.stickyOffsetTop + 'px'); 108 109 // Save positioning data. 110 var viewHeight = document.documentElement.scrollHeight || document.body.scrollHeight; 111 if (calculateWidth || this.viewHeight !== viewHeight) { 112 this.viewHeight = viewHeight; 113 this.vPosition = this.originalTable.offset().top - 4 - this.stickyOffsetTop; 114 this.hPosition = this.originalTable.offset().left; 115 this.vLength = this.originalTable[0].clientHeight - 100; 116 calculateWidth = true; 117 } 118 119 // Track horizontal positioning relative to the viewport and set visibility. 120 var hScroll = document.documentElement.scrollLeft || document.body.scrollLeft; 121 var vOffset = (document.documentElement.scrollTop || document.body.scrollTop) - this.vPosition; 122 this.stickyVisible = vOffset > 0 && vOffset < this.vLength; 123 this.stickyTable.css({ left: (-hScroll + this.hPosition) + 'px', visibility: this.stickyVisible ? 'visible' : 'hidden' }); 124 125 // Only perform expensive calculations if the sticky header is actually 126 // visible or when forced. 127 if (this.stickyVisible && (calculateWidth || !this.widthCalculated)) { 128 this.widthCalculated = true; 129 var $that = null; 130 var $stickyCell = null; 131 var display = null; 132 var cellWidth = null; 133 // Resize header and its cell widths. 134 // Only apply width to visible table cells. This prevents the header from 135 // displaying incorrectly when the sticky header is no longer visible. 136 for (var i = 0, il = this.originalHeaderCells.length; i < il; i += 1) { 137 $that = $(this.originalHeaderCells[i]); 138 $stickyCell = this.stickyHeaderCells.eq($that.index()); 139 display = $that.css('display'); 140 if (display !== 'none') { 141 cellWidth = $that.css('width'); 142 // Exception for IE7. 143 if (cellWidth === 'auto') { 144 cellWidth = $that[0].clientWidth + 'px'; 145 } 146 $stickyCell.css({'width': cellWidth, 'display': display}); 147 } 148 else { 149 $stickyCell.css('display', 'none'); 150 } 151 } 152 this.stickyTable.css('width', this.originalTable.outerWidth()); 153 } 154}; 155 156})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.