1;(function ($, window, undefined) { 2 'use strict'; 3 4 var name = 'fixTableHeaders', 5 id = 0, 6 defaults = { 7 fixedOffset: 0, 8 leftOffset: 0, 9 marginTop: 0, 10 objDocument: document, 11 objHead: 'head', 12 objWindow: window, 13 scrollableArea: window 14 }; 15 16 function Plugin (el, options) { 17 18 var base = this; 19 20 base.$el = $(el); 21 base.el = el; 22 base.id = id++; 23 24 base.$el.bind('destroyed', 25 $.proxy(base.teardown, base)); 26 27 base.$clonedHeader = null; 28 base.$originalHeader = null; 29 30 base.isSticky = false; 31 base.hasBeenSticky = false; 32 base.leftOffset = null; 33 base.topOffset = null; 34 35 base.init = function () { 36 base.setOptions(options); 37 38 base.$el.each(function () { 39 var $this = $(this); 40 41 $this.css('padding', 0); 42 43 base.$originalHeader = $('thead:first', this); 44 base.$clonedHeader = base.$originalHeader.clone(); 45 $this.trigger('clonedHeader.' + name, [base.$clonedHeader]); 46 47 base.$clonedHeader.addClass('tableFloatingHeader'); 48 base.$clonedHeader.css('display', 'none'); 49 50 base.$originalHeader.addClass('tableFloatingHeaderOriginal'); 51 base.$originalHeader.find('tr').attr('data-csv-ignore','ignore'); 52 53 base.$originalHeader.after(base.$clonedHeader); 54 55 base.$printStyle = $('<style type="text/css" media="print">' + 56 '.tableFloatingHeader{display:none !important;}' + 57 '.tableFloatingHeaderOriginal{position:static !important;}' + 58 '</style>'); 59 base.$head.append(base.$printStyle); 60 }); 61 62 base.updateWidth(); 63 base.toggleHeaders(); 64 base.bind(); 65 }; 66 67 base.destroy = function (){ 68 base.$el.unbind('destroyed', base.teardown); 69 base.teardown(); 70 }; 71 72 base.teardown = function(){ 73 if (base.isSticky) { 74 base.$originalHeader.css('position', 'static'); 75 } 76 $.removeData(base.el, 'plugin_' + name); 77 base.unbind(); 78 79 base.$clonedHeader.remove(); 80 base.$originalHeader.removeClass('tableFloatingHeaderOriginal'); 81 base.$originalHeader.css('visibility', 'visible'); 82 base.$printStyle.remove(); 83 84 base.el = null; 85 base.$el = null; 86 }; 87 88 base.bind = function(){ 89 base.$scrollableArea.on('scroll.' + name, base.toggleHeaders); 90 if (!base.isWindowScrolling) { 91 base.$window.on('scroll.' + name + base.id, base.setPositionValues); 92 base.$window.on('resize.' + name + base.id, base.toggleHeaders); 93 } 94 base.$scrollableArea.on('resize.' + name, base.toggleHeaders); 95 base.$scrollableArea.on('resize.' + name, base.updateWidth); 96 }; 97 98 base.unbind = function(){ 99 100 base.$scrollableArea.off('.' + name, base.toggleHeaders); 101 if (!base.isWindowScrolling) { 102 base.$window.off('.' + name + base.id, base.setPositionValues); 103 base.$window.off('.' + name + base.id, base.toggleHeaders); 104 } 105 base.$scrollableArea.off('.' + name, base.updateWidth); 106 }; 107 108 base.toggleHeaders = function () { 109 if (base.$el) { 110 base.$el.each(function () { 111 var $this = $(this), 112 newLeft, 113 newTopOffset = base.isWindowScrolling ? ( 114 isNaN(base.options.fixedOffset) ? 115 base.options.fixedOffset.outerHeight() : 116 base.options.fixedOffset 117 ) : 118 base.$scrollableArea.offset().top + (!isNaN(base.options.fixedOffset) ? base.options.fixedOffset : 0), 119 offset = $this.offset(), 120 121 scrollTop = base.$scrollableArea.scrollTop() + newTopOffset, 122 scrollLeft = base.$scrollableArea.scrollLeft(), 123 124 scrolledPastTop = base.isWindowScrolling ? 125 scrollTop > offset.top : 126 newTopOffset > offset.top, 127 notScrolledPastBottom = (base.isWindowScrolling ? scrollTop : 0) < 128 (offset.top + $this.height() - base.$clonedHeader.height() - (base.isWindowScrolling ? 0 : newTopOffset)); 129 130 if (scrolledPastTop && notScrolledPastBottom) { 131 newLeft = offset.left - scrollLeft + base.options.leftOffset; 132 base.$originalHeader.css({ 133 'position': 'fixed', 134 'margin-top': base.options.marginTop, 135 'left': newLeft, 136 'z-index': 3 137 }); 138 base.leftOffset = newLeft; 139 base.topOffset = newTopOffset; 140 base.$clonedHeader.css('display', ''); 141 if (!base.isSticky) { 142 base.isSticky = true; 143 base.updateWidth(); 144 $this.trigger('enabledStickiness.' + name); 145 } 146 base.setPositionValues(); 147 } else if (base.isSticky) { 148 base.$originalHeader.css('position', 'static'); 149 base.$clonedHeader.css('display', 'none'); 150 base.isSticky = false;
151 base.resetWidth($('td,th', base.$clonedHeader), $('td,th', base.$originalHeader)); 152 $this.trigger('disabledStickiness.' + name); 153 } 154 }); 155 } 156 }; 157 158 base.setPositionValues = function () { 159 var winScrollTop = base.$window.scrollTop(), 160 winScrollLeft = base.$window.scrollLeft(); 161 if (!base.isSticky || 162 winScrollTop < 0 || winScrollTop + base.$window.height() > base.$document.height() || 163 winScrollLeft < 0 || winScrollLeft + base.$window.width() > base.$document.width()) { 164 return; 165 } 166 base.$originalHeader.css({ 167 'top': base.topOffset - (base.isWindowScrolling ? 0 : winScrollTop), 168 'left': base.leftOffset - (base.isWindowScrolling ? 0 : winScrollLeft) 169 }); 170 }; 171 172 base.updateWidth = function () { 173 if (!base.isSticky) { 174 return; 175 } 176 177 if (!base.$originalHeaderCells) { 178 base.$originalHeaderCells = $('th,td', base.$originalHeader); 179 } 180 if (!base.$clonedHeaderCells) { 181 base.$clonedHeaderCells = $('th,td', base.$clonedHeader); 182 } 183 var cellWidths = base.getWidth(base.$clonedHeaderCells); 184 base.setWidth(cellWidths, base.$clonedHeaderCells, base.$originalHeaderCells); 185 186 base.$originalHeader.css('width', base.$clonedHeader.width()); 187 }; 188 189 base.getWidth = function ($clonedHeaders) { 190 var widths = []; 191 $clonedHeaders.each(function (index) { 192 var width, $this = $(this); 193 194 if ($this.css('box-sizing') === 'border-box') { 195 var boundingClientRect = $this[0].getBoundingClientRect(); 196 if(boundingClientRect.width) { 197 width = boundingClientRect.width; 198 } else { 199 width = boundingClientRect.right - boundingClientRect.left; 200 } 201 } else { 202 var $origTh = $('th', base.$originalHeader); 203 if ($origTh.css('border-collapse') === 'collapse') { 204 if (window.getComputedStyle) { 205 width = parseFloat(window.getComputedStyle(this, null).width); 206 } else { 207 208 var leftPadding = parseFloat($this.css('padding-left')); 209 var rightPadding = parseFloat($this.css('padding-right')); 210 211 var border = parseFloat($this.css('border-width')); 212 width = $this.outerWidth() - leftPadding - rightPadding - border; 213 } 214 } else { 215 width = $this.width(); 216 } 217 } 218 219 widths[index] = width; 220 }); 221 return widths; 222 }; 223 224 base.setWidth = function (widths, $clonedHeaders, $origHeaders) { 225 $clonedHeaders.each(function (index) { 226 var width = widths[index]; 227 $origHeaders.eq(index).css({ 228 'min-width': width, 229 'max-width': width 230 }); 231 }); 232 }; 233 234 base.resetWidth = function ($clonedHeaders, $origHeaders) { 235 $clonedHeaders.each(function (index) { 236 var $this = $(this); 237 $origHeaders.eq(index).css({ 238 'min-width': $this.css('min-width'), 239 'max-width': $this.css('max-width') 240 }); 241 }); 242 }; 243 244 base.setOptions = function (options) { 245 base.options = $.extend({}, defaults, options); 246 base.$window = $(base.options.objWindow); 247 base.$head = $(base.options.objHead); 248 base.$document = $(base.options.objDocument); 249 base.$scrollableArea = $(base.options.scrollableArea); 250 base.isWindowScrolling = base.$scrollableArea[0] === base.$window[0]; 251 }; 252 253 base.updateOptions = function (options) { 254 base.setOptions(options); 255 256 base.unbind(); 257 base.bind(); 258 base.updateWidth(); 259 base.toggleHeaders(); 260 }; 261 262 263 base.init(); 264 } 265 266 $.fn[name] = function ( options ) { 267 return this.each(function () { 268 var instance = $.data(this, 'plugin_' + name); 269 if (instance) { 270 if (typeof options === 'string') { 271 instance[options].apply(instance); 272 } else { 273 instance.updateOptions(options); 274 } 275 } else if(options !== 'destroy') { 276 $.data(this, 'plugin_' + name, new Plugin( this, options )); 277 } 278 }); 279 }; 280 281})(jQuery, window);
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.