diff -r 07239de796bb -r e756a8c72c3d cms/drupal/misc/tableheader.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cms/drupal/misc/tableheader.js Fri Sep 08 12:04:06 2017 +0200 @@ -0,0 +1,133 @@ +(function ($) { + +/** + * Attaches sticky table headers. + */ +Drupal.behaviors.tableHeader = { + attach: function (context, settings) { + if (!$.support.positionFixed) { + return; + } + + $('table.sticky-enabled', context).once('tableheader', function () { + $(this).data("drupal-tableheader", new Drupal.tableHeader(this)); + }); + } +}; + +/** + * Constructor for the tableHeader object. Provides sticky table headers. + * + * @param table + * DOM object for the table to add a sticky header to. + */ +Drupal.tableHeader = function (table) { + var self = this; + + this.originalTable = $(table); + this.originalHeader = $(table).children('thead'); + this.originalHeaderCells = this.originalHeader.find('> tr > th'); + this.displayWeight = null; + + // React to columns change to avoid making checks in the scroll callback. + this.originalTable.bind('columnschange', function (e, display) { + // This will force header size to be calculated on scroll. + self.widthCalculated = (self.displayWeight !== null && self.displayWeight === display); + self.displayWeight = display; + }); + + // Clone the table header so it inherits original jQuery properties. Hide + // the table to avoid a flash of the header clone upon page load. + this.stickyTable = $('') + .insertBefore(this.originalTable) + .css({ position: 'fixed', top: '0px' }); + this.stickyHeader = this.originalHeader.clone(true) + .hide() + .appendTo(this.stickyTable); + this.stickyHeaderCells = this.stickyHeader.find('> tr > th'); + + this.originalTable.addClass('sticky-table'); + $(window) + .bind('scroll.drupal-tableheader', $.proxy(this, 'eventhandlerRecalculateStickyHeader')) + .bind('resize.drupal-tableheader', { calculateWidth: true }, $.proxy(this, 'eventhandlerRecalculateStickyHeader')) + // Make sure the anchor being scrolled into view is not hidden beneath the + // sticky table header. Adjust the scrollTop if it does. + .bind('drupalDisplaceAnchor.drupal-tableheader', function () { + window.scrollBy(0, -self.stickyTable.outerHeight()); + }) + // Make sure the element being focused is not hidden beneath the sticky + // table header. Adjust the scrollTop if it does. + .bind('drupalDisplaceFocus.drupal-tableheader', function (event) { + if (self.stickyVisible && event.clientY < (self.stickyOffsetTop + self.stickyTable.outerHeight()) && event.$target.closest('sticky-header').length === 0) { + window.scrollBy(0, -self.stickyTable.outerHeight()); + } + }) + .triggerHandler('resize.drupal-tableheader'); + + // We hid the header to avoid it showing up erroneously on page load; + // we need to unhide it now so that it will show up when expected. + this.stickyHeader.show(); +}; + +/** + * Event handler: recalculates position of the sticky table header. + * + * @param event + * Event being triggered. + */ +Drupal.tableHeader.prototype.eventhandlerRecalculateStickyHeader = function (event) { + var self = this; + var calculateWidth = event.data && event.data.calculateWidth; + + // Reset top position of sticky table headers to the current top offset. + this.stickyOffsetTop = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0; + this.stickyTable.css('top', this.stickyOffsetTop + 'px'); + + // Save positioning data. + var viewHeight = document.documentElement.scrollHeight || document.body.scrollHeight; + if (calculateWidth || this.viewHeight !== viewHeight) { + this.viewHeight = viewHeight; + this.vPosition = this.originalTable.offset().top - 4 - this.stickyOffsetTop; + this.hPosition = this.originalTable.offset().left; + this.vLength = this.originalTable[0].clientHeight - 100; + calculateWidth = true; + } + + // Track horizontal positioning relative to the viewport and set visibility. + var hScroll = document.documentElement.scrollLeft || document.body.scrollLeft; + var vOffset = (document.documentElement.scrollTop || document.body.scrollTop) - this.vPosition; + this.stickyVisible = vOffset > 0 && vOffset < this.vLength; + this.stickyTable.css({ left: (-hScroll + this.hPosition) + 'px', visibility: this.stickyVisible ? 'visible' : 'hidden' }); + + // Only perform expensive calculations if the sticky header is actually + // visible or when forced. + if (this.stickyVisible && (calculateWidth || !this.widthCalculated)) { + this.widthCalculated = true; + var $that = null; + var $stickyCell = null; + var display = null; + var cellWidth = null; + // Resize header and its cell widths. + // Only apply width to visible table cells. This prevents the header from + // displaying incorrectly when the sticky header is no longer visible. + for (var i = 0, il = this.originalHeaderCells.length; i < il; i += 1) { + $that = $(this.originalHeaderCells[i]); + $stickyCell = this.stickyHeaderCells.eq($that.index()); + display = $that.css('display'); + if (display !== 'none') { + cellWidth = $that.css('width'); + // Exception for IE7. + if (cellWidth === 'auto') { + cellWidth = $that[0].clientWidth + 'px'; + } + $stickyCell.css({'width': cellWidth, 'display': display}); + } + else { + $stickyCell.css('display', 'none'); + } + } + this.stickyTable.css('width', this.originalTable.outerWidth()); + } +}; + +})(jQuery);