source: public/vendors/dataTable/FixedHeader-3.1.6/js/dataTables.fixedHeader.js@ c6b84df

develop
Last change on this file since c6b84df was c6b84df, checked in by beratkjufliju <kufliju@…>, 3 years ago

added fileTypes controller, notifications, excel export, edited views

  • Property mode set to 100644
File size: 17.2 KB
Line 
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';
52var DataTable = $.fn.dataTable;
53
54
55var _instCounter = 0;
56
57var 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 */
615FixedHeader.version = "3.1.6";
616
617/**
618 * Defaults
619 * @type {Object}
620 * @static
621 */
622FixedHeader.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
659DataTable.Api.register( 'fixedHeader()', function () {} );
660
661DataTable.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
671DataTable.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
682DataTable.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
694DataTable.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
725return FixedHeader;
726}));
Note: See TracBrowser for help on using the repository browser.