1 | /*! FixedHeader 3.1.6
|
---|
2 | * ©2009-2019 SpryMedia Ltd - datatables.net/license
|
---|
3 | */
|
---|
4 |
|
---|
5 | /**
|
---|
6 | * @summary FixedHeader
|
---|
7 | * @description Fix a table's header or footer, so it is always visible while
|
---|
8 | * scrolling
|
---|
9 | * @version 3.1.6
|
---|
10 | * @file dataTables.fixedHeader.js
|
---|
11 | * @author SpryMedia Ltd (www.sprymedia.co.uk)
|
---|
12 | * @contact www.sprymedia.co.uk/contact
|
---|
13 | * @copyright Copyright 2009-2019 SpryMedia Ltd.
|
---|
14 | *
|
---|
15 | * This source file is free software, available under the following license:
|
---|
16 | * MIT license - http://datatables.net/license/mit
|
---|
17 | *
|
---|
18 | * This source file is distributed in the hope that it will be useful, but
|
---|
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
---|
20 | * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
|
---|
21 | *
|
---|
22 | * For details please refer to: http://www.datatables.net
|
---|
23 | */
|
---|
24 |
|
---|
25 | (function( factory ){
|
---|
26 | if ( typeof define === 'function' && define.amd ) {
|
---|
27 | // AMD
|
---|
28 | define( ['jquery', 'datatables.net'], function ( $ ) {
|
---|
29 | return factory( $, window, document );
|
---|
30 | } );
|
---|
31 | }
|
---|
32 | else if ( typeof exports === 'object' ) {
|
---|
33 | // CommonJS
|
---|
34 | module.exports = function (root, $) {
|
---|
35 | if ( ! root ) {
|
---|
36 | root = window;
|
---|
37 | }
|
---|
38 |
|
---|
39 | if ( ! $ || ! $.fn.dataTable ) {
|
---|
40 | $ = require('datatables.net')(root, $).$;
|
---|
41 | }
|
---|
42 |
|
---|
43 | return factory( $, root, root.document );
|
---|
44 | };
|
---|
45 | }
|
---|
46 | else {
|
---|
47 | // Browser
|
---|
48 | factory( jQuery, window, document );
|
---|
49 | }
|
---|
50 | }(function( $, window, document, undefined ) {
|
---|
51 | 'use strict';
|
---|
52 | var DataTable = $.fn.dataTable;
|
---|
53 |
|
---|
54 |
|
---|
55 | var _instCounter = 0;
|
---|
56 |
|
---|
57 | var FixedHeader = function ( dt, config ) {
|
---|
58 | // Sanity check - you just know it will happen
|
---|
59 | if ( ! (this instanceof FixedHeader) ) {
|
---|
60 | throw "FixedHeader must be initialised with the 'new' keyword.";
|
---|
61 | }
|
---|
62 |
|
---|
63 | // Allow a boolean true for defaults
|
---|
64 | if ( config === true ) {
|
---|
65 | config = {};
|
---|
66 | }
|
---|
67 |
|
---|
68 | dt = new DataTable.Api( dt );
|
---|
69 |
|
---|
70 | this.c = $.extend( true, {}, FixedHeader.defaults, config );
|
---|
71 |
|
---|
72 | this.s = {
|
---|
73 | dt: dt,
|
---|
74 | position: {
|
---|
75 | theadTop: 0,
|
---|
76 | tbodyTop: 0,
|
---|
77 | tfootTop: 0,
|
---|
78 | tfootBottom: 0,
|
---|
79 | width: 0,
|
---|
80 | left: 0,
|
---|
81 | tfootHeight: 0,
|
---|
82 | theadHeight: 0,
|
---|
83 | windowHeight: $(window).height(),
|
---|
84 | visible: true
|
---|
85 | },
|
---|
86 | headerMode: null,
|
---|
87 | footerMode: null,
|
---|
88 | autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
|
---|
89 | namespace: '.dtfc'+(_instCounter++),
|
---|
90 | scrollLeft: {
|
---|
91 | header: -1,
|
---|
92 | footer: -1
|
---|
93 | },
|
---|
94 | enable: true
|
---|
95 | };
|
---|
96 |
|
---|
97 | this.dom = {
|
---|
98 | floatingHeader: null,
|
---|
99 | thead: $(dt.table().header()),
|
---|
100 | tbody: $(dt.table().body()),
|
---|
101 | tfoot: $(dt.table().footer()),
|
---|
102 | header: {
|
---|
103 | host: null,
|
---|
104 | floating: null,
|
---|
105 | placeholder: null
|
---|
106 | },
|
---|
107 | footer: {
|
---|
108 | host: null,
|
---|
109 | floating: null,
|
---|
110 | placeholder: null
|
---|
111 | }
|
---|
112 | };
|
---|
113 |
|
---|
114 | this.dom.header.host = this.dom.thead.parent();
|
---|
115 | this.dom.footer.host = this.dom.tfoot.parent();
|
---|
116 |
|
---|
117 | var dtSettings = dt.settings()[0];
|
---|
118 | if ( dtSettings._fixedHeader ) {
|
---|
119 | throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
|
---|
120 | }
|
---|
121 |
|
---|
122 | dtSettings._fixedHeader = this;
|
---|
123 |
|
---|
124 | this._constructor();
|
---|
125 | };
|
---|
126 |
|
---|
127 |
|
---|
128 | /*
|
---|
129 | * Variable: FixedHeader
|
---|
130 | * Purpose: Prototype for FixedHeader
|
---|
131 | * Scope: global
|
---|
132 | */
|
---|
133 | $.extend( FixedHeader.prototype, {
|
---|
134 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
---|
135 | * API methods
|
---|
136 | */
|
---|
137 |
|
---|
138 | /**
|
---|
139 | * Kill off FH and any events
|
---|
140 | */
|
---|
141 | destroy: function () {
|
---|
142 | this.s.dt.off( '.dtfc' );
|
---|
143 | $(window).off( this.s.namespace );
|
---|
144 |
|
---|
145 | if ( this.c.header ) {
|
---|
146 | this._modeChange( 'in-place', 'header', true );
|
---|
147 | }
|
---|
148 |
|
---|
149 | if ( this.c.footer && this.dom.tfoot.length ) {
|
---|
150 | this._modeChange( 'in-place', 'footer', true );
|
---|
151 | }
|
---|
152 | },
|
---|
153 |
|
---|
154 | /**
|
---|
155 | * Enable / disable the fixed elements
|
---|
156 | *
|
---|
157 | * @param {boolean} enable `true` to enable, `false` to disable
|
---|
158 | */
|
---|
159 | enable: function ( enable, update )
|
---|
160 | {
|
---|
161 | this.s.enable = enable;
|
---|
162 |
|
---|
163 | if ( update || update === undefined ) {
|
---|
164 | this._positions();
|
---|
165 | this._scroll( true );
|
---|
166 | }
|
---|
167 | },
|
---|
168 |
|
---|
169 | /**
|
---|
170 | * Get enabled status
|
---|
171 | */
|
---|
172 | enabled: function ()
|
---|
173 | {
|
---|
174 | return this.s.enable;
|
---|
175 | },
|
---|
176 |
|
---|
177 | /**
|
---|
178 | * Set header offset
|
---|
179 | *
|
---|
180 | * @param {int} new value for headerOffset
|
---|
181 | */
|
---|
182 | headerOffset: function ( offset )
|
---|
183 | {
|
---|
184 | if ( offset !== undefined ) {
|
---|
185 | this.c.headerOffset = offset;
|
---|
186 | this.update();
|
---|
187 | }
|
---|
188 |
|
---|
189 | return this.c.headerOffset;
|
---|
190 | },
|
---|
191 |
|
---|
192 | /**
|
---|
193 | * Set footer offset
|
---|
194 | *
|
---|
195 | * @param {int} new value for footerOffset
|
---|
196 | */
|
---|
197 | footerOffset: function ( offset )
|
---|
198 | {
|
---|
199 | if ( offset !== undefined ) {
|
---|
200 | this.c.footerOffset = offset;
|
---|
201 | this.update();
|
---|
202 | }
|
---|
203 |
|
---|
204 | return this.c.footerOffset;
|
---|
205 | },
|
---|
206 |
|
---|
207 |
|
---|
208 | /**
|
---|
209 | * Recalculate the position of the fixed elements and force them into place
|
---|
210 | */
|
---|
211 | update: function ()
|
---|
212 | {
|
---|
213 | var table = this.s.dt.table().node();
|
---|
214 |
|
---|
215 | if ( $(table).is(':visible') ) {
|
---|
216 | this.enable( true, false );
|
---|
217 | }
|
---|
218 | else {
|
---|
219 | this.enable( false, false );
|
---|
220 | }
|
---|
221 |
|
---|
222 | this._positions();
|
---|
223 | this._scroll( true );
|
---|
224 | },
|
---|
225 |
|
---|
226 |
|
---|
227 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
---|
228 | * Constructor
|
---|
229 | */
|
---|
230 |
|
---|
231 | /**
|
---|
232 | * FixedHeader constructor - adding the required event listeners and
|
---|
233 | * simple initialisation
|
---|
234 | *
|
---|
235 | * @private
|
---|
236 | */
|
---|
237 | _constructor: function ()
|
---|
238 | {
|
---|
239 | var that = this;
|
---|
240 | var dt = this.s.dt;
|
---|
241 |
|
---|
242 | $(window)
|
---|
243 | .on( 'scroll'+this.s.namespace, function () {
|
---|
244 | that._scroll();
|
---|
245 | } )
|
---|
246 | .on( 'resize'+this.s.namespace, DataTable.util.throttle( function () {
|
---|
247 | that.s.position.windowHeight = $(window).height();
|
---|
248 | that.update();
|
---|
249 | }, 50 ) );
|
---|
250 |
|
---|
251 | var autoHeader = $('.fh-fixedHeader');
|
---|
252 | if ( ! this.c.headerOffset && autoHeader.length ) {
|
---|
253 | this.c.headerOffset = autoHeader.outerHeight();
|
---|
254 | }
|
---|
255 |
|
---|
256 | var autoFooter = $('.fh-fixedFooter');
|
---|
257 | if ( ! this.c.footerOffset && autoFooter.length ) {
|
---|
258 | this.c.footerOffset = autoFooter.outerHeight();
|
---|
259 | }
|
---|
260 |
|
---|
261 | dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc', function () {
|
---|
262 | that.update();
|
---|
263 | } );
|
---|
264 |
|
---|
265 | dt.on( 'destroy.dtfc', function () {
|
---|
266 | that.destroy();
|
---|
267 | } );
|
---|
268 |
|
---|
269 | this._positions();
|
---|
270 | this._scroll();
|
---|
271 | },
|
---|
272 |
|
---|
273 |
|
---|
274 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
---|
275 | * Private methods
|
---|
276 | */
|
---|
277 |
|
---|
278 | /**
|
---|
279 | * Clone a fixed item to act as a place holder for the original element
|
---|
280 | * which is moved into a clone of the table element, and moved around the
|
---|
281 | * document to give the fixed effect.
|
---|
282 | *
|
---|
283 | * @param {string} item 'header' or 'footer'
|
---|
284 | * @param {boolean} force Force the clone to happen, or allow automatic
|
---|
285 | * decision (reuse existing if available)
|
---|
286 | * @private
|
---|
287 | */
|
---|
288 | _clone: function ( item, force )
|
---|
289 | {
|
---|
290 | var dt = this.s.dt;
|
---|
291 | var itemDom = this.dom[ item ];
|
---|
292 | var itemElement = item === 'header' ?
|
---|
293 | this.dom.thead :
|
---|
294 | this.dom.tfoot;
|
---|
295 |
|
---|
296 | if ( ! force && itemDom.floating ) {
|
---|
297 | // existing floating element - reuse it
|
---|
298 | itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
|
---|
299 | }
|
---|
300 | else {
|
---|
301 | if ( itemDom.floating ) {
|
---|
302 | itemDom.placeholder.remove();
|
---|
303 | this._unsize( item );
|
---|
304 | itemDom.floating.children().detach();
|
---|
305 | itemDom.floating.remove();
|
---|
306 | }
|
---|
307 |
|
---|
308 | itemDom.floating = $( dt.table().node().cloneNode( false ) )
|
---|
309 | .css( 'table-layout', 'fixed' )
|
---|
310 | .attr( 'aria-hidden', 'true' )
|
---|
311 | .removeAttr( 'id' )
|
---|
312 | .append( itemElement )
|
---|
313 | .appendTo( 'body' );
|
---|
314 |
|
---|
315 | // Insert a fake thead/tfoot into the DataTable to stop it jumping around
|
---|
316 | itemDom.placeholder = itemElement.clone( false );
|
---|
317 | itemDom.placeholder
|
---|
318 | .find( '*[id]' )
|
---|
319 | .removeAttr( 'id' );
|
---|
320 |
|
---|
321 | itemDom.host.prepend( itemDom.placeholder );
|
---|
322 |
|
---|
323 | // Clone widths
|
---|
324 | this._matchWidths( itemDom.placeholder, itemDom.floating );
|
---|
325 | }
|
---|
326 | },
|
---|
327 |
|
---|
328 | /**
|
---|
329 | * Copy widths from the cells in one element to another. This is required
|
---|
330 | * for the footer as the footer in the main table takes its sizes from the
|
---|
331 | * header columns. That isn't present in the footer so to have it still
|
---|
332 | * align correctly, the sizes need to be copied over. It is also required
|
---|
333 | * for the header when auto width is not enabled
|
---|
334 | *
|
---|
335 | * @param {jQuery} from Copy widths from
|
---|
336 | * @param {jQuery} to Copy widths to
|
---|
337 | * @private
|
---|
338 | */
|
---|
339 | _matchWidths: function ( from, to ) {
|
---|
340 | var get = function ( name ) {
|
---|
341 | return $(name, from)
|
---|
342 | .map( function () {
|
---|
343 | return $(this).width();
|
---|
344 | } ).toArray();
|
---|
345 | };
|
---|
346 |
|
---|
347 | var set = function ( name, toWidths ) {
|
---|
348 | $(name, to).each( function ( i ) {
|
---|
349 | $(this).css( {
|
---|
350 | width: toWidths[i],
|
---|
351 | minWidth: toWidths[i]
|
---|
352 | } );
|
---|
353 | } );
|
---|
354 | };
|
---|
355 |
|
---|
356 | var thWidths = get( 'th' );
|
---|
357 | var tdWidths = get( 'td' );
|
---|
358 |
|
---|
359 | set( 'th', thWidths );
|
---|
360 | set( 'td', tdWidths );
|
---|
361 | },
|
---|
362 |
|
---|
363 | /**
|
---|
364 | * Remove assigned widths from the cells in an element. This is required
|
---|
365 | * when inserting the footer back into the main table so the size is defined
|
---|
366 | * by the header columns and also when auto width is disabled in the
|
---|
367 | * DataTable.
|
---|
368 | *
|
---|
369 | * @param {string} item The `header` or `footer`
|
---|
370 | * @private
|
---|
371 | */
|
---|
372 | _unsize: function ( item ) {
|
---|
373 | var el = this.dom[ item ].floating;
|
---|
374 |
|
---|
375 | if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
|
---|
376 | $('th, td', el).css( {
|
---|
377 | width: '',
|
---|
378 | minWidth: ''
|
---|
379 | } );
|
---|
380 | }
|
---|
381 | else if ( el && item === 'header' ) {
|
---|
382 | $('th, td', el).css( 'min-width', '' );
|
---|
383 | }
|
---|
384 | },
|
---|
385 |
|
---|
386 | /**
|
---|
387 | * Reposition the floating elements to take account of horizontal page
|
---|
388 | * scroll
|
---|
389 | *
|
---|
390 | * @param {string} item The `header` or `footer`
|
---|
391 | * @param {int} scrollLeft Folder scrollLeft
|
---|
392 | * @private
|
---|
393 | */
|
---|
394 | _horizontal: function ( item, scrollLeft )
|
---|
395 | {
|
---|
396 | var itemDom = this.dom[ item ];
|
---|
397 | var position = this.s.position;
|
---|
398 | var lastScrollLeft = this.s.scrollLeft;
|
---|
399 |
|
---|
400 | if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
|
---|
401 | itemDom.floating.css( 'left', position.left - scrollLeft );
|
---|
402 |
|
---|
403 | lastScrollLeft[ item ] = scrollLeft;
|
---|
404 | }
|
---|
405 | },
|
---|
406 |
|
---|
407 | /**
|
---|
408 | * Change from one display mode to another. Each fixed item can be in one
|
---|
409 | * of:
|
---|
410 | *
|
---|
411 | * * `in-place` - In the main DataTable
|
---|
412 | * * `in` - Floating over the DataTable
|
---|
413 | * * `below` - (Header only) Fixed to the bottom of the table body
|
---|
414 | * * `above` - (Footer only) Fixed to the top of the table body
|
---|
415 | *
|
---|
416 | * @param {string} mode Mode that the item should be shown in
|
---|
417 | * @param {string} item 'header' or 'footer'
|
---|
418 | * @param {boolean} forceChange Force a redraw of the mode, even if already
|
---|
419 | * in that mode.
|
---|
420 | * @private
|
---|
421 | */
|
---|
422 | _modeChange: function ( mode, item, forceChange )
|
---|
423 | {
|
---|
424 | var dt = this.s.dt;
|
---|
425 | var itemDom = this.dom[ item ];
|
---|
426 | var position = this.s.position;
|
---|
427 |
|
---|
428 | // Record focus. Browser's will cause input elements to loose focus if
|
---|
429 | // they are inserted else where in the doc
|
---|
430 | var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ];
|
---|
431 | var focus = $.contains( tablePart[0], document.activeElement ) ?
|
---|
432 | document.activeElement :
|
---|
433 | null;
|
---|
434 |
|
---|
435 | if ( focus ) {
|
---|
436 | focus.blur();
|
---|
437 | }
|
---|
438 |
|
---|
439 | if ( mode === 'in-place' ) {
|
---|
440 | // Insert the header back into the table's real header
|
---|
441 | if ( itemDom.placeholder ) {
|
---|
442 | itemDom.placeholder.remove();
|
---|
443 | itemDom.placeholder = null;
|
---|
444 | }
|
---|
445 |
|
---|
446 | this._unsize( item );
|
---|
447 |
|
---|
448 | if ( item === 'header' ) {
|
---|
449 | itemDom.host.prepend( tablePart );
|
---|
450 | }
|
---|
451 | else {
|
---|
452 | itemDom.host.append( tablePart );
|
---|
453 | }
|
---|
454 |
|
---|
455 | if ( itemDom.floating ) {
|
---|
456 | itemDom.floating.remove();
|
---|
457 | itemDom.floating = null;
|
---|
458 | }
|
---|
459 | }
|
---|
460 | else if ( mode === 'in' ) {
|
---|
461 | // Remove the header from the read header and insert into a fixed
|
---|
462 | // positioned floating table clone
|
---|
463 | this._clone( item, forceChange );
|
---|
464 |
|
---|
465 | itemDom.floating
|
---|
466 | .addClass( 'fixedHeader-floating' )
|
---|
467 | .css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] )
|
---|
468 | .css( 'left', position.left+'px' )
|
---|
469 | .css( 'width', position.width+'px' );
|
---|
470 |
|
---|
471 | if ( item === 'footer' ) {
|
---|
472 | itemDom.floating.css( 'top', '' );
|
---|
473 | }
|
---|
474 | }
|
---|
475 | else if ( mode === 'below' ) { // only used for the header
|
---|
476 | // Fix the position of the floating header at base of the table body
|
---|
477 | this._clone( item, forceChange );
|
---|
478 |
|
---|
479 | itemDom.floating
|
---|
480 | .addClass( 'fixedHeader-locked' )
|
---|
481 | .css( 'top', position.tfootTop - position.theadHeight )
|
---|
482 | .css( 'left', position.left+'px' )
|
---|
483 | .css( 'width', position.width+'px' );
|
---|
484 | }
|
---|
485 | else if ( mode === 'above' ) { // only used for the footer
|
---|
486 | // Fix the position of the floating footer at top of the table body
|
---|
487 | this._clone( item, forceChange );
|
---|
488 |
|
---|
489 | itemDom.floating
|
---|
490 | .addClass( 'fixedHeader-locked' )
|
---|
491 | .css( 'top', position.tbodyTop )
|
---|
492 | .css( 'left', position.left+'px' )
|
---|
493 | .css( 'width', position.width+'px' );
|
---|
494 | }
|
---|
495 |
|
---|
496 | // Restore focus if it was lost
|
---|
497 | if ( focus && focus !== document.activeElement ) {
|
---|
498 | setTimeout( function () {
|
---|
499 | focus.focus();
|
---|
500 | }, 10 );
|
---|
501 | }
|
---|
502 |
|
---|
503 | this.s.scrollLeft.header = -1;
|
---|
504 | this.s.scrollLeft.footer = -1;
|
---|
505 | this.s[item+'Mode'] = mode;
|
---|
506 | },
|
---|
507 |
|
---|
508 | /**
|
---|
509 | * Cache the positional information that is required for the mode
|
---|
510 | * calculations that FixedHeader performs.
|
---|
511 | *
|
---|
512 | * @private
|
---|
513 | */
|
---|
514 | _positions: function ()
|
---|
515 | {
|
---|
516 | var dt = this.s.dt;
|
---|
517 | var table = dt.table();
|
---|
518 | var position = this.s.position;
|
---|
519 | var dom = this.dom;
|
---|
520 | var tableNode = $(table.node());
|
---|
521 |
|
---|
522 | // Need to use the header and footer that are in the main table,
|
---|
523 | // regardless of if they are clones, since they hold the positions we
|
---|
524 | // want to measure from
|
---|
525 | var thead = tableNode.children('thead');
|
---|
526 | var tfoot = tableNode.children('tfoot');
|
---|
527 | var tbody = dom.tbody;
|
---|
528 |
|
---|
529 | position.visible = tableNode.is(':visible');
|
---|
530 | position.width = tableNode.outerWidth();
|
---|
531 | position.left = tableNode.offset().left;
|
---|
532 | position.theadTop = thead.offset().top;
|
---|
533 | position.tbodyTop = tbody.offset().top;
|
---|
534 | position.tbodyHeight = tbody.outerHeight();
|
---|
535 | position.theadHeight = position.tbodyTop - position.theadTop;
|
---|
536 |
|
---|
537 | if ( tfoot.length ) {
|
---|
538 | position.tfootTop = tfoot.offset().top;
|
---|
539 | position.tfootBottom = position.tfootTop + tfoot.outerHeight();
|
---|
540 | position.tfootHeight = position.tfootBottom - position.tfootTop;
|
---|
541 | }
|
---|
542 | else {
|
---|
543 | position.tfootTop = position.tbodyTop + tbody.outerHeight();
|
---|
544 | position.tfootBottom = position.tfootTop;
|
---|
545 | position.tfootHeight = position.tfootTop;
|
---|
546 | }
|
---|
547 | },
|
---|
548 |
|
---|
549 |
|
---|
550 | /**
|
---|
551 | * Mode calculation - determine what mode the fixed items should be placed
|
---|
552 | * into.
|
---|
553 | *
|
---|
554 | * @param {boolean} forceChange Force a redraw of the mode, even if already
|
---|
555 | * in that mode.
|
---|
556 | * @private
|
---|
557 | */
|
---|
558 | _scroll: function ( forceChange )
|
---|
559 | {
|
---|
560 | var windowTop = $(document).scrollTop();
|
---|
561 | var windowLeft = $(document).scrollLeft();
|
---|
562 | var position = this.s.position;
|
---|
563 | var headerMode, footerMode;
|
---|
564 |
|
---|
565 | if ( this.c.header ) {
|
---|
566 | if ( ! this.s.enable ) {
|
---|
567 | headerMode = 'in-place';
|
---|
568 | }
|
---|
569 | else if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) {
|
---|
570 | headerMode = 'in-place';
|
---|
571 | }
|
---|
572 | else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) {
|
---|
573 | headerMode = 'in';
|
---|
574 | }
|
---|
575 | else {
|
---|
576 | headerMode = 'below';
|
---|
577 | }
|
---|
578 |
|
---|
579 | if ( forceChange || headerMode !== this.s.headerMode ) {
|
---|
580 | this._modeChange( headerMode, 'header', forceChange );
|
---|
581 | }
|
---|
582 |
|
---|
583 | this._horizontal( 'header', windowLeft );
|
---|
584 | }
|
---|
585 |
|
---|
586 | if ( this.c.footer && this.dom.tfoot.length ) {
|
---|
587 | if ( ! this.s.enable ) {
|
---|
588 | headerMode = 'in-place';
|
---|
589 | }
|
---|
590 | else if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) {
|
---|
591 | footerMode = 'in-place';
|
---|
592 | }
|
---|
593 | else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) {
|
---|
594 | footerMode = 'in';
|
---|
595 | }
|
---|
596 | else {
|
---|
597 | footerMode = 'above';
|
---|
598 | }
|
---|
599 |
|
---|
600 | if ( forceChange || footerMode !== this.s.footerMode ) {
|
---|
601 | this._modeChange( footerMode, 'footer', forceChange );
|
---|
602 | }
|
---|
603 |
|
---|
604 | this._horizontal( 'footer', windowLeft );
|
---|
605 | }
|
---|
606 | }
|
---|
607 | } );
|
---|
608 |
|
---|
609 |
|
---|
610 | /**
|
---|
611 | * Version
|
---|
612 | * @type {String}
|
---|
613 | * @static
|
---|
614 | */
|
---|
615 | FixedHeader.version = "3.1.6";
|
---|
616 |
|
---|
617 | /**
|
---|
618 | * Defaults
|
---|
619 | * @type {Object}
|
---|
620 | * @static
|
---|
621 | */
|
---|
622 | FixedHeader.defaults = {
|
---|
623 | header: true,
|
---|
624 | footer: false,
|
---|
625 | headerOffset: 0,
|
---|
626 | footerOffset: 0
|
---|
627 | };
|
---|
628 |
|
---|
629 |
|
---|
630 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
---|
631 | * DataTables interfaces
|
---|
632 | */
|
---|
633 |
|
---|
634 | // Attach for constructor access
|
---|
635 | $.fn.dataTable.FixedHeader = FixedHeader;
|
---|
636 | $.fn.DataTable.FixedHeader = FixedHeader;
|
---|
637 |
|
---|
638 |
|
---|
639 | // DataTables creation - check if the FixedHeader option has been defined on the
|
---|
640 | // table and if so, initialise
|
---|
641 | $(document).on( 'init.dt.dtfh', function (e, settings, json) {
|
---|
642 | if ( e.namespace !== 'dt' ) {
|
---|
643 | return;
|
---|
644 | }
|
---|
645 |
|
---|
646 | var init = settings.oInit.fixedHeader;
|
---|
647 | var defaults = DataTable.defaults.fixedHeader;
|
---|
648 |
|
---|
649 | if ( (init || defaults) && ! settings._fixedHeader ) {
|
---|
650 | var opts = $.extend( {}, defaults, init );
|
---|
651 |
|
---|
652 | if ( init !== false ) {
|
---|
653 | new FixedHeader( settings, opts );
|
---|
654 | }
|
---|
655 | }
|
---|
656 | } );
|
---|
657 |
|
---|
658 | // DataTables API methods
|
---|
659 | DataTable.Api.register( 'fixedHeader()', function () {} );
|
---|
660 |
|
---|
661 | DataTable.Api.register( 'fixedHeader.adjust()', function () {
|
---|
662 | return this.iterator( 'table', function ( ctx ) {
|
---|
663 | var fh = ctx._fixedHeader;
|
---|
664 |
|
---|
665 | if ( fh ) {
|
---|
666 | fh.update();
|
---|
667 | }
|
---|
668 | } );
|
---|
669 | } );
|
---|
670 |
|
---|
671 | DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
|
---|
672 | return this.iterator( 'table', function ( ctx ) {
|
---|
673 | var fh = ctx._fixedHeader;
|
---|
674 |
|
---|
675 | flag = ( flag !== undefined ? flag : true );
|
---|
676 | if ( fh && flag !== fh.enabled() ) {
|
---|
677 | fh.enable( flag );
|
---|
678 | }
|
---|
679 | } );
|
---|
680 | } );
|
---|
681 |
|
---|
682 | DataTable.Api.register( 'fixedHeader.enabled()', function () {
|
---|
683 | if ( this.context.length ) {
|
---|
684 | var fx = this.content[0]._fixedHeader;
|
---|
685 |
|
---|
686 | if ( fh ) {
|
---|
687 | return fh.enabled();
|
---|
688 | }
|
---|
689 | }
|
---|
690 |
|
---|
691 | return false;
|
---|
692 | } );
|
---|
693 |
|
---|
694 | DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
|
---|
695 | return this.iterator( 'table', function ( ctx ) {
|
---|
696 | var fh = ctx._fixedHeader;
|
---|
697 |
|
---|
698 | if ( fh && fh.enabled() ) {
|
---|
699 | fh.enable( false );
|
---|
700 | }
|
---|
701 | } );
|
---|
702 | } );
|
---|
703 |
|
---|
704 | $.each( ['header', 'footer'], function ( i, el ) {
|
---|
705 | DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
|
---|
706 | var ctx = this.context;
|
---|
707 |
|
---|
708 | if ( offset === undefined ) {
|
---|
709 | return ctx.length && ctx[0]._fixedHeader ?
|
---|
710 | ctx[0]._fixedHeader[el +'Offset']() :
|
---|
711 | undefined;
|
---|
712 | }
|
---|
713 |
|
---|
714 | return this.iterator( 'table', function ( ctx ) {
|
---|
715 | var fh = ctx._fixedHeader;
|
---|
716 |
|
---|
717 | if ( fh ) {
|
---|
718 | fh[ el +'Offset' ]( offset );
|
---|
719 | }
|
---|
720 | } );
|
---|
721 | } );
|
---|
722 | } );
|
---|
723 |
|
---|
724 |
|
---|
725 | return FixedHeader;
|
---|
726 | }));
|
---|