cherrypy-foundation 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. cherrypy_foundation/__init__.py +0 -0
  2. cherrypy_foundation/components/ColorModes.jinja +70 -0
  3. cherrypy_foundation/components/Datatable.css +47 -0
  4. cherrypy_foundation/components/Datatable.jinja +63 -0
  5. cherrypy_foundation/components/Datatable.js +358 -0
  6. cherrypy_foundation/components/Field.css +10 -0
  7. cherrypy_foundation/components/Field.jinja +66 -0
  8. cherrypy_foundation/components/Field.js +56 -0
  9. cherrypy_foundation/components/Fields.jinja +4 -0
  10. cherrypy_foundation/components/Flash.jinja +13 -0
  11. cherrypy_foundation/components/Icon.jinja +3 -0
  12. cherrypy_foundation/components/LocaleSelection.jinja +13 -0
  13. cherrypy_foundation/components/LocaleSelection.js +26 -0
  14. cherrypy_foundation/components/SideBySideMultiSelect.css +25 -0
  15. cherrypy_foundation/components/SideBySideMultiSelect.jinja +9 -0
  16. cherrypy_foundation/components/SideBySideMultiSelect.js +9 -0
  17. cherrypy_foundation/components/Typeahead.css +55 -0
  18. cherrypy_foundation/components/Typeahead.jinja +106 -0
  19. cherrypy_foundation/components/Typeahead.js +8 -0
  20. cherrypy_foundation/components/__init__.py +51 -0
  21. cherrypy_foundation/components/tests/__init__.py +0 -0
  22. cherrypy_foundation/components/tests/test_static.py +90 -0
  23. cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.css +2106 -0
  24. cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.min.css +5 -0
  25. cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  26. cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  27. cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css +9262 -0
  28. cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css.map +95 -0
  29. cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css +6 -0
  30. cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css.map +7 -0
  31. cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js +4846 -0
  32. cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js.map +1 -0
  33. cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js +7 -0
  34. cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js.map +7 -0
  35. cherrypy_foundation/components/vendor/bootstrap5/js/color-modes.js +80 -0
  36. cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.css +849 -0
  37. cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.min.css +1 -0
  38. cherrypy_foundation/components/vendor/datatables/images/sort_asc.png +0 -0
  39. cherrypy_foundation/components/vendor/datatables/images/sort_asc_disabled.png +0 -0
  40. cherrypy_foundation/components/vendor/datatables/images/sort_both.png +0 -0
  41. cherrypy_foundation/components/vendor/datatables/images/sort_desc.png +0 -0
  42. cherrypy_foundation/components/vendor/datatables/images/sort_desc_disabled.png +0 -0
  43. cherrypy_foundation/components/vendor/datatables/js/dataTables.js +14073 -0
  44. cherrypy_foundation/components/vendor/datatables/js/dataTables.min.js +4 -0
  45. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.css +556 -0
  46. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.min.css +1 -0
  47. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.js +1700 -0
  48. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.min.js +8 -0
  49. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.js +2944 -0
  50. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.min.js +4 -0
  51. cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.css +13 -0
  52. cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.min.css +1 -0
  53. cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.js +1202 -0
  54. cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.min.js +4 -0
  55. cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.js +11577 -0
  56. cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.min.js +13 -0
  57. cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.css +194 -0
  58. cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.min.css +1 -0
  59. cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.js +1861 -0
  60. cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.min.js +4 -0
  61. cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.js +75023 -0
  62. cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.min.js +3 -0
  63. cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/vfs_fonts.js +6 -0
  64. cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.css +53 -0
  65. cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.min.css +1 -0
  66. cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.js +485 -0
  67. cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.min.js +4 -0
  68. cherrypy_foundation/components/vendor/jquery/jquery.min.js +2 -0
  69. cherrypy_foundation/components/vendor/multi/LICENSE +7 -0
  70. cherrypy_foundation/components/vendor/multi/README.md +109 -0
  71. cherrypy_foundation/components/vendor/multi/multi.css +95 -0
  72. cherrypy_foundation/components/vendor/multi/multi.js +328 -0
  73. cherrypy_foundation/components/vendor/popper/popper.js +1825 -0
  74. cherrypy_foundation/components/vendor/popper/popper.min.js +6 -0
  75. cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.css +1 -0
  76. cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.js +10 -0
  77. cherrypy_foundation/error_page.py +94 -0
  78. cherrypy_foundation/flash.py +50 -0
  79. cherrypy_foundation/form.py +119 -0
  80. cherrypy_foundation/logging.py +103 -0
  81. cherrypy_foundation/passwd.py +65 -0
  82. cherrypy_foundation/plugins/__init__.py +0 -0
  83. cherrypy_foundation/plugins/db.py +286 -0
  84. cherrypy_foundation/plugins/ldap.py +257 -0
  85. cherrypy_foundation/plugins/restapi.py +74 -0
  86. cherrypy_foundation/plugins/scheduler.py +287 -0
  87. cherrypy_foundation/plugins/smtp.py +223 -0
  88. cherrypy_foundation/plugins/tests/__init__.py +0 -0
  89. cherrypy_foundation/plugins/tests/test_db.py +118 -0
  90. cherrypy_foundation/plugins/tests/test_ldap.py +451 -0
  91. cherrypy_foundation/plugins/tests/test_scheduler.py +100 -0
  92. cherrypy_foundation/plugins/tests/test_scheduler_db.py +107 -0
  93. cherrypy_foundation/plugins/tests/test_smtp.py +140 -0
  94. cherrypy_foundation/sessions.py +93 -0
  95. cherrypy_foundation/tests/__init__.py +72 -0
  96. cherrypy_foundation/tests/templates/test_flash.html +9 -0
  97. cherrypy_foundation/tests/templates/test_form.html +16 -0
  98. cherrypy_foundation/tests/templates/test_url.html +15 -0
  99. cherrypy_foundation/tests/test_error_page.py +78 -0
  100. cherrypy_foundation/tests/test_flash.py +61 -0
  101. cherrypy_foundation/tests/test_form.py +148 -0
  102. cherrypy_foundation/tests/test_logging.py +78 -0
  103. cherrypy_foundation/tests/test_passwd.py +51 -0
  104. cherrypy_foundation/tests/test_sessions.py +89 -0
  105. cherrypy_foundation/tests/test_url.py +161 -0
  106. cherrypy_foundation/tools/__init__.py +0 -0
  107. cherrypy_foundation/tools/auth.py +263 -0
  108. cherrypy_foundation/tools/auth_mfa.py +249 -0
  109. cherrypy_foundation/tools/i18n.py +529 -0
  110. cherrypy_foundation/tools/jinja2.py +158 -0
  111. cherrypy_foundation/tools/ratelimit.py +265 -0
  112. cherrypy_foundation/tools/secure_headers.py +119 -0
  113. cherrypy_foundation/tools/sessions_timeout.py +167 -0
  114. cherrypy_foundation/tools/tests/__init__.py +0 -0
  115. cherrypy_foundation/tools/tests/components/Button.jinja +2 -0
  116. cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.mo +0 -0
  117. cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.po +15 -0
  118. cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.mo +0 -0
  119. cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po +15 -0
  120. cherrypy_foundation/tools/tests/locales/messages.pot +2 -0
  121. cherrypy_foundation/tools/tests/templates/test_jinja2.html +11 -0
  122. cherrypy_foundation/tools/tests/templates/test_jinjax.html +9 -0
  123. cherrypy_foundation/tools/tests/templates/test_jinjax_i18n.html +22 -0
  124. cherrypy_foundation/tools/tests/test_auth.py +110 -0
  125. cherrypy_foundation/tools/tests/test_auth_mfa.py +369 -0
  126. cherrypy_foundation/tools/tests/test_i18n.py +247 -0
  127. cherrypy_foundation/tools/tests/test_jinja2.py +153 -0
  128. cherrypy_foundation/tools/tests/test_ratelimit.py +109 -0
  129. cherrypy_foundation/tools/tests/test_secure_headers.py +200 -0
  130. cherrypy_foundation/url.py +66 -0
  131. cherrypy_foundation/widgets.py +48 -0
  132. cherrypy_foundation-1.0.0.dist-info/METADATA +71 -0
  133. cherrypy_foundation-1.0.0.dist-info/RECORD +136 -0
  134. cherrypy_foundation-1.0.0.dist-info/WHEEL +5 -0
  135. cherrypy_foundation-1.0.0.dist-info/licenses/LICENSE.md +674 -0
  136. cherrypy_foundation-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1202 @@
1
+ /*! FixedHeader 4.0.3
2
+ * © SpryMedia Ltd - datatables.net/license
3
+ */
4
+
5
+ (function( factory ){
6
+ if ( typeof define === 'function' && define.amd ) {
7
+ // AMD
8
+ define( ['jquery', 'datatables.net'], function ( $ ) {
9
+ return factory( $, window, document );
10
+ } );
11
+ }
12
+ else if ( typeof exports === 'object' ) {
13
+ // CommonJS
14
+ var jq = require('jquery');
15
+ var cjsRequires = function (root, $) {
16
+ if ( ! $.fn.dataTable ) {
17
+ require('datatables.net')(root, $);
18
+ }
19
+ };
20
+
21
+ if (typeof window === 'undefined') {
22
+ module.exports = function (root, $) {
23
+ if ( ! root ) {
24
+ // CommonJS environments without a window global must pass a
25
+ // root. This will give an error otherwise
26
+ root = window;
27
+ }
28
+
29
+ if ( ! $ ) {
30
+ $ = jq( root );
31
+ }
32
+
33
+ cjsRequires( root, $ );
34
+ return factory( $, root, root.document );
35
+ };
36
+ }
37
+ else {
38
+ cjsRequires( window, jq );
39
+ module.exports = factory( jq, window, window.document );
40
+ }
41
+ }
42
+ else {
43
+ // Browser
44
+ factory( jQuery, window, document );
45
+ }
46
+ }(function( $, window, document ) {
47
+ 'use strict';
48
+ var DataTable = $.fn.dataTable;
49
+
50
+
51
+
52
+ /**
53
+ * @summary FixedHeader
54
+ * @description Fix a table's header or footer, so it is always visible while
55
+ * scrolling
56
+ * @version 4.0.3
57
+ * @author SpryMedia Ltd
58
+ * @contact datatables.net
59
+ *
60
+ * This source file is free software, available under the following license:
61
+ * MIT license - http://datatables.net/license/mit
62
+ *
63
+ * This source file is distributed in the hope that it will be useful, but
64
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
65
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
66
+ *
67
+ * For details please refer to: http://www.datatables.net
68
+ */
69
+
70
+ var _instCounter = 0;
71
+
72
+ var FixedHeader = function (dt, config) {
73
+ if (!DataTable.versionCheck('2')) {
74
+ throw 'Warning: FixedHeader requires DataTables 2 or newer';
75
+ }
76
+
77
+ // Sanity check - you just know it will happen
78
+ if (!(this instanceof FixedHeader)) {
79
+ throw "FixedHeader must be initialised with the 'new' keyword.";
80
+ }
81
+
82
+ // Allow a boolean true for defaults
83
+ if (config === true) {
84
+ config = {};
85
+ }
86
+
87
+ dt = new DataTable.Api(dt);
88
+
89
+ this.c = $.extend(true, {}, FixedHeader.defaults, config);
90
+
91
+ this.s = {
92
+ dt: dt,
93
+ position: {
94
+ theadTop: 0,
95
+ tbodyTop: 0,
96
+ tfootTop: 0,
97
+ tfootBottom: 0,
98
+ width: 0,
99
+ left: 0,
100
+ tfootHeight: 0,
101
+ theadHeight: 0,
102
+ windowHeight: $(window).height(),
103
+ visible: true
104
+ },
105
+ headerMode: null,
106
+ footerMode: null,
107
+ autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
108
+ namespace: '.dtfc' + _instCounter++,
109
+ scrollLeft: {
110
+ header: -1,
111
+ footer: -1
112
+ },
113
+ enable: true,
114
+ autoDisable: false
115
+ };
116
+
117
+ this.dom = {
118
+ floatingHeader: null,
119
+ thead: $(dt.table().header()),
120
+ tbody: $(dt.table().body()),
121
+ tfoot: $(dt.table().footer()),
122
+ header: {
123
+ host: null,
124
+ scrollAdjust: null,
125
+ floating: null,
126
+ floatingParent: $(
127
+ '<div class="dtfh-floatingparent">' + // location
128
+ '<div class="dtfh-floating-limiter">' + // hidden overflow / scrolling
129
+ '<div></div>' + // adjustment for scrollbar (padding)
130
+ '</div>' +
131
+ '</div>'),
132
+ limiter: null,
133
+ placeholder: null
134
+ },
135
+ footer: {
136
+ host: null,
137
+ scrollAdjust: null,
138
+ floating: null,
139
+ floatingParent: $(
140
+ '<div class="dtfh-floatingparent">' +
141
+ '<div class="dtfh-floating-limiter">' +
142
+ '<div></div>' +
143
+ '</div>' +
144
+ '</div>'),
145
+ limiter: null,
146
+ placeholder: null
147
+ }
148
+ };
149
+
150
+ var dom = this.dom;
151
+
152
+ dom.header.host = dom.thead.parent();
153
+ dom.header.limiter = dom.header.floatingParent.children();
154
+ dom.header.scrollAdjust = dom.header.limiter.children();
155
+
156
+ dom.footer.host = dom.tfoot.parent();
157
+ dom.footer.limiter = dom.footer.floatingParent.children();
158
+ dom.footer.scrollAdjust = dom.footer.limiter.children();
159
+
160
+ var dtSettings = dt.settings()[0];
161
+ if (dtSettings._fixedHeader) {
162
+ throw (
163
+ 'FixedHeader already initialised on table ' + dtSettings.nTable.id
164
+ );
165
+ }
166
+
167
+ dtSettings._fixedHeader = this;
168
+
169
+ this._constructor();
170
+ };
171
+
172
+ /*
173
+ * Variable: FixedHeader
174
+ * Purpose: Prototype for FixedHeader
175
+ * Scope: global
176
+ */
177
+ $.extend(FixedHeader.prototype, {
178
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
179
+ * API methods
180
+ */
181
+
182
+ /**
183
+ * Kill off FH and any events
184
+ */
185
+ destroy: function () {
186
+ var dom = this.dom;
187
+
188
+ this.s.dt.off('.dtfc');
189
+ $(window).off(this.s.namespace);
190
+
191
+ // Remove clones of FC blockers
192
+ if (dom.header.rightBlocker) {
193
+ dom.header.rightBlocker.remove();
194
+ }
195
+ if (dom.header.leftBlocker) {
196
+ dom.header.leftBlocker.remove();
197
+ }
198
+ if (dom.footer.rightBlocker) {
199
+ dom.footer.rightBlocker.remove();
200
+ }
201
+ if (dom.footer.leftBlocker) {
202
+ dom.footer.leftBlocker.remove();
203
+ }
204
+
205
+ if (this.c.header) {
206
+ this._modeChange('in-place', 'header', true);
207
+ }
208
+
209
+ if (this.c.footer && dom.tfoot.length) {
210
+ this._modeChange('in-place', 'footer', true);
211
+ }
212
+ },
213
+
214
+ /**
215
+ * Enable / disable the fixed elements
216
+ *
217
+ * @param {boolean} enable `true` to enable, `false` to disable
218
+ */
219
+ enable: function (enable, update, type) {
220
+ this.s.enable = enable;
221
+
222
+ this.s.enableType = type;
223
+
224
+ if (update || update === undefined) {
225
+ this._positions();
226
+ this._scroll(true);
227
+ }
228
+ },
229
+
230
+ /**
231
+ * Get enabled status
232
+ */
233
+ enabled: function () {
234
+ return this.s.enable;
235
+ },
236
+
237
+ /**
238
+ * Set header offset
239
+ *
240
+ * @param {int} new value for headerOffset
241
+ */
242
+ headerOffset: function (offset) {
243
+ if (offset !== undefined) {
244
+ this.c.headerOffset = offset;
245
+ this.update();
246
+ }
247
+
248
+ return this.c.headerOffset;
249
+ },
250
+
251
+ /**
252
+ * Set footer offset
253
+ *
254
+ * @param {int} new value for footerOffset
255
+ */
256
+ footerOffset: function (offset) {
257
+ if (offset !== undefined) {
258
+ this.c.footerOffset = offset;
259
+ this.update();
260
+ }
261
+
262
+ return this.c.footerOffset;
263
+ },
264
+
265
+ /**
266
+ * Recalculate the position of the fixed elements and force them into place
267
+ */
268
+ update: function (force) {
269
+ var table = this.s.dt.table().node();
270
+
271
+ // Update should only do something if enabled by the dev.
272
+ if (!this.s.enable && !this.s.autoDisable) {
273
+ return;
274
+ }
275
+
276
+ if ($(table).is(':visible')) {
277
+ this.s.autoDisable = false;
278
+ this.enable(true, false);
279
+ }
280
+ else {
281
+ this.s.autoDisable = true;
282
+ this.enable(false, false);
283
+ }
284
+
285
+ // Don't update if header is not in the document atm (due to
286
+ // async events)
287
+ if ($(table).children('thead').length === 0) {
288
+ return;
289
+ }
290
+
291
+ this._positions();
292
+ this._scroll(force !== undefined ? force : true);
293
+ this._widths(this.dom.header);
294
+ this._widths(this.dom.footer);
295
+ },
296
+
297
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
298
+ * Constructor
299
+ */
300
+
301
+ /**
302
+ * FixedHeader constructor - adding the required event listeners and
303
+ * simple initialisation
304
+ *
305
+ * @private
306
+ */
307
+ _constructor: function () {
308
+ var that = this;
309
+ var dt = this.s.dt;
310
+
311
+ $(window)
312
+ .on('scroll' + this.s.namespace, function () {
313
+ that._scroll();
314
+ })
315
+ .on(
316
+ 'resize' + this.s.namespace,
317
+ DataTable.util.throttle(function () {
318
+ that.s.position.windowHeight = $(window).height();
319
+ that.update();
320
+ }, 50)
321
+ );
322
+
323
+ var autoHeader = $('.fh-fixedHeader');
324
+ if (!this.c.headerOffset && autoHeader.length) {
325
+ this.c.headerOffset = autoHeader.outerHeight();
326
+ }
327
+
328
+ var autoFooter = $('.fh-fixedFooter');
329
+ if (!this.c.footerOffset && autoFooter.length) {
330
+ this.c.footerOffset = autoFooter.outerHeight();
331
+ }
332
+
333
+ dt.on(
334
+ 'column-reorder.dt.dtfc column-visibility.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc',
335
+ function (e, ctx) {
336
+ that.update();
337
+ }
338
+ ).on('draw.dt.dtfc', function (e, ctx) {
339
+ // For updates from our own table, don't reclone, but for all others, do
340
+ that.update(ctx === dt.settings()[0] ? false : true);
341
+ });
342
+
343
+ dt.on('destroy.dtfc', function () {
344
+ that.destroy();
345
+ });
346
+
347
+ this._positions();
348
+ this._scroll();
349
+ },
350
+
351
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
352
+ * Private methods
353
+ */
354
+
355
+ /**
356
+ * Clone a fixed item to act as a place holder for the original element
357
+ * which is moved into a clone of the table element, and moved around the
358
+ * document to give the fixed effect.
359
+ *
360
+ * @param {string} item 'header' or 'footer'
361
+ * @param {boolean} force Force the clone to happen, or allow automatic
362
+ * decision (reuse existing if available)
363
+ * @private
364
+ */
365
+ _clone: function (item, force) {
366
+ var that = this;
367
+ var dt = this.s.dt;
368
+ var itemDom = this.dom[item];
369
+ var itemElement = item === 'header' ? this.dom.thead : this.dom.tfoot;
370
+
371
+ // If footer and scrolling is enabled then we don't clone
372
+ // Instead the table's height is decreased accordingly - see `_scroll()`
373
+ if (item === 'footer' && this._scrollEnabled()) {
374
+ return;
375
+ }
376
+
377
+ if (!force && itemDom.floating) {
378
+ // existing floating element - reuse it
379
+ itemDom.floating.removeClass(
380
+ 'fixedHeader-floating fixedHeader-locked'
381
+ );
382
+ }
383
+ else {
384
+ if (itemDom.floating) {
385
+ if (itemDom.placeholder !== null) {
386
+ itemDom.placeholder.remove();
387
+ }
388
+
389
+ itemDom.floating.remove();
390
+ }
391
+
392
+ var tableNode = $(dt.table().node());
393
+ var scrollBody = $(tableNode.parent());
394
+ var scrollEnabled = this._scrollEnabled();
395
+
396
+ itemDom.floating = $(dt.table().node().cloneNode(false))
397
+ .attr('aria-hidden', 'true')
398
+ .css({
399
+ top: 0,
400
+ left: 0
401
+ })
402
+ .removeAttr('id');
403
+
404
+ itemDom.floatingParent
405
+ .css({
406
+ width: scrollBody[0].offsetWidth,
407
+ position: 'fixed',
408
+ left: scrollEnabled
409
+ ? tableNode.offset().left + scrollBody.scrollLeft()
410
+ : 0
411
+ })
412
+ .css(
413
+ item === 'header'
414
+ ? {
415
+ top: this.c.headerOffset,
416
+ bottom: ''
417
+ }
418
+ : {
419
+ top: '',
420
+ bottom: this.c.footerOffset
421
+ }
422
+ )
423
+ .addClass(
424
+ item === 'footer'
425
+ ? 'dtfh-floatingparent-foot'
426
+ : 'dtfh-floatingparent-head'
427
+ )
428
+ .appendTo('body')
429
+ .children()
430
+ .eq(0);
431
+
432
+ itemDom.limiter
433
+ .css({
434
+ width: '100%',
435
+ overflow: 'hidden',
436
+ height: 'fit-content'
437
+ });
438
+
439
+ itemDom.scrollAdjust
440
+ .append(itemDom.floating);
441
+
442
+ this._stickyPosition(itemDom.floating, '-');
443
+
444
+ var scrollLeftUpdate = function () {
445
+ var scrollLeft = scrollBody.scrollLeft();
446
+ that.s.scrollLeft = { footer: scrollLeft, header: scrollLeft };
447
+ itemDom.limiter.scrollLeft(that.s.scrollLeft.header);
448
+ };
449
+
450
+ scrollLeftUpdate();
451
+ scrollBody.off('scroll.dtfh').on('scroll.dtfh', scrollLeftUpdate);
452
+
453
+ // Need padding on the header's container to allow for a scrollbar,
454
+ // just like how DataTables handles it
455
+ itemDom.scrollAdjust.css({
456
+ width: 'fit-content',
457
+ paddingRight: that.s.dt.settings()[0].oBrowser.barWidth
458
+ });
459
+
460
+ // Blocker to hide the table behind the scrollbar - this needs to use
461
+ // fixed positioning in the container since we don't have an outer wrapper
462
+ let blocker = $(
463
+ item === 'footer'
464
+ ? 'div.dtfc-bottom-blocker'
465
+ : 'div.dtfc-top-blocker',
466
+ dt.table().container()
467
+ );
468
+
469
+ if (blocker.length) {
470
+ blocker
471
+ .clone()
472
+ .appendTo(itemDom.floatingParent)
473
+ .css({
474
+ position: 'fixed',
475
+ right: blocker.width()
476
+ });
477
+ }
478
+
479
+ // Insert a fake thead/tfoot into the DataTable to stop it jumping around
480
+ itemDom.placeholder = itemElement.clone(false);
481
+ itemDom.placeholder.find('*[id]').removeAttr('id');
482
+
483
+ // Move the thead / tfoot elements around - original into the floating
484
+ // element and clone into the original table
485
+ itemDom.host.prepend(itemDom.placeholder);
486
+ itemDom.floating.append(itemElement);
487
+
488
+ this._widths(itemDom);
489
+ }
490
+ },
491
+
492
+ /**
493
+ * This method sets the sticky position of the header elements to match fixed columns
494
+ * @param {JQuery<HTMLElement>} el
495
+ * @param {string} sign
496
+ */
497
+ _stickyPosition: function (el, sign) {
498
+ if (this._scrollEnabled()) {
499
+ var that = this;
500
+ var rtl = $(that.s.dt.table().node()).css('direction') === 'rtl';
501
+
502
+ el.find('th').each(function () {
503
+ // Find out if fixed header has previously set this column
504
+ if ($(this).css('position') === 'sticky') {
505
+ var right = $(this).css('right');
506
+ var left = $(this).css('left');
507
+ var potential;
508
+
509
+ if (right !== 'auto' && !rtl) {
510
+ potential = +right.replace(/px/g, '')
511
+
512
+ $(this).css('right', potential > 0 ? potential : 0);
513
+ }
514
+ else if (left !== 'auto' && rtl) {
515
+ potential = +left.replace(/px/g, '');
516
+
517
+ $(this).css('left', potential > 0 ? potential : 0);
518
+ }
519
+ }
520
+ });
521
+ }
522
+ },
523
+
524
+ /**
525
+ * Reposition the floating elements to take account of horizontal page
526
+ * scroll
527
+ *
528
+ * @param {string} item The `header` or `footer`
529
+ * @param {int} scrollLeft Document scrollLeft
530
+ * @private
531
+ */
532
+ _horizontal: function (item, scrollLeft) {
533
+ var itemDom = this.dom[item];
534
+ var lastScrollLeft = this.s.scrollLeft;
535
+
536
+ if (itemDom.floating && lastScrollLeft[item] !== scrollLeft) {
537
+ // If scrolling is enabled we need to match the floating header to the body
538
+ if (this._scrollEnabled()) {
539
+ var newScrollLeft = $(
540
+ $(this.s.dt.table().node()).parent()
541
+ ).scrollLeft();
542
+ itemDom.floating.scrollLeft(newScrollLeft);
543
+ itemDom.floatingParent.scrollLeft(newScrollLeft);
544
+ }
545
+
546
+ lastScrollLeft[item] = scrollLeft;
547
+ }
548
+ },
549
+
550
+ /**
551
+ * Change from one display mode to another. Each fixed item can be in one
552
+ * of:
553
+ *
554
+ * * `in-place` - In the main DataTable
555
+ * * `in` - Floating over the DataTable
556
+ * * `below` - (Header only) Fixed to the bottom of the table body
557
+ * * `above` - (Footer only) Fixed to the top of the table body
558
+ *
559
+ * @param {string} mode Mode that the item should be shown in
560
+ * @param {string} item 'header' or 'footer'
561
+ * @param {boolean} forceChange Force a redraw of the mode, even if already
562
+ * in that mode.
563
+ * @private
564
+ */
565
+ _modeChange: function (mode, item, forceChange) {
566
+ var dt = this.s.dt;
567
+ var itemDom = this.dom[item];
568
+ var position = this.s.position;
569
+
570
+ // Just determine if scroll is enabled once
571
+ var scrollEnabled = this._scrollEnabled();
572
+
573
+ // If footer and scrolling is enabled then we don't clone
574
+ // Instead the table's height is decreased accordingly - see `_scroll()`
575
+ if (item === 'footer' && scrollEnabled) {
576
+ return;
577
+ }
578
+
579
+ // It isn't trivial to add a !important css attribute...
580
+ var importantWidth = function (w) {
581
+ itemDom.floating[0].style.setProperty('width', w + 'px', 'important');
582
+
583
+ // If not scrolling also have to update the floatingParent
584
+ if (!scrollEnabled) {
585
+ itemDom.floatingParent[0].style.setProperty('width', w + 'px', 'important');
586
+ }
587
+ };
588
+
589
+ // Record focus. Browser's will cause input elements to loose focus if
590
+ // they are inserted else where in the doc
591
+ var tablePart = this.dom[item === 'footer' ? 'tfoot' : 'thead'];
592
+ var focus = $.contains(tablePart[0], document.activeElement)
593
+ ? document.activeElement
594
+ : null;
595
+ var scrollBody = $($(this.s.dt.table().node()).parent());
596
+
597
+ if (mode === 'in-place') {
598
+ // Insert the header back into the table's real header
599
+ if (itemDom.placeholder) {
600
+ itemDom.placeholder.remove();
601
+ itemDom.placeholder = null;
602
+ }
603
+
604
+ if (!$.contains(itemDom.host[0], tablePart[0])) {
605
+ if (item === 'header') {
606
+ itemDom.host.prepend(tablePart);
607
+ }
608
+ else {
609
+ itemDom.host.append(tablePart);
610
+ }
611
+ }
612
+
613
+ if (itemDom.floating) {
614
+ itemDom.floating.remove();
615
+ itemDom.floating = null;
616
+ this._stickyPosition(itemDom.host, '+');
617
+ }
618
+
619
+ if (itemDom.floatingParent) {
620
+ itemDom.floatingParent.find('div.dtfc-top-blocker').remove();
621
+ itemDom.floatingParent.remove();
622
+ }
623
+
624
+ $($(itemDom.host.parent()).parent()).scrollLeft(
625
+ scrollBody.scrollLeft()
626
+ );
627
+ }
628
+ else if (mode === 'in') {
629
+ // Remove the header from the real table and insert into a fixed
630
+ // positioned floating table clone
631
+ this._clone(item, forceChange);
632
+
633
+ // Get useful position values
634
+ var scrollOffset = scrollBody.offset();
635
+ var windowTop = $(document).scrollTop();
636
+ var windowHeight = $(window).height();
637
+ var windowBottom = windowTop + windowHeight;
638
+ var bodyTop = scrollEnabled ? scrollOffset.top : position.tbodyTop;
639
+ var bodyBottom = scrollEnabled
640
+ ? scrollOffset.top + scrollBody.outerHeight()
641
+ : position.tfootTop;
642
+
643
+ // Calculate the amount that the footer or header needs to be shuffled
644
+ var shuffle;
645
+
646
+ if (item === 'footer') {
647
+ shuffle =
648
+ bodyTop > windowBottom
649
+ ? position.tfootHeight // Yes - push the footer below
650
+ : bodyTop + position.tfootHeight - windowBottom; // No
651
+ }
652
+ else {
653
+ // Otherwise must be a header so get the difference from the bottom of the
654
+ // desired floating header and the bottom of the table body
655
+ shuffle =
656
+ windowTop +
657
+ this.c.headerOffset +
658
+ position.theadHeight -
659
+ bodyBottom;
660
+ }
661
+
662
+ // Set the top or bottom based off of the offset and the shuffle value
663
+ var prop = item === 'header' ? 'top' : 'bottom';
664
+ var val = this.c[item + 'Offset'] - (shuffle > 0 ? shuffle : 0);
665
+
666
+ itemDom.floating.addClass('fixedHeader-floating');
667
+ itemDom.floatingParent
668
+ .css(prop, val)
669
+ .css({
670
+ left: position.left,
671
+ 'z-index': 3
672
+ });
673
+
674
+ importantWidth(position.width);
675
+
676
+ if (item === 'footer') {
677
+ itemDom.floating.css('top', '');
678
+ }
679
+ }
680
+ else if (mode === 'below') {
681
+ // only used for the header
682
+ // Fix the position of the floating header at base of the table body
683
+ this._clone(item, forceChange);
684
+
685
+ itemDom.floating.addClass('fixedHeader-locked');
686
+ itemDom.floatingParent.css({
687
+ position: 'absolute',
688
+ top: position.tfootTop - position.theadHeight,
689
+ left: position.left + 'px'
690
+ });
691
+
692
+ importantWidth(position.width);
693
+ }
694
+ else if (mode === 'above') {
695
+ // only used for the footer
696
+ // Fix the position of the floating footer at top of the table body
697
+ this._clone(item, forceChange);
698
+
699
+ itemDom.floating.addClass('fixedHeader-locked');
700
+ itemDom.floatingParent.css({
701
+ position: 'absolute',
702
+ top: position.tbodyTop,
703
+ left: position.left + 'px'
704
+ });
705
+
706
+ importantWidth(position.width);
707
+ }
708
+
709
+ // Restore focus if it was lost
710
+ if (focus && focus !== document.activeElement) {
711
+ setTimeout(function () {
712
+ focus.focus();
713
+ }, 10);
714
+ }
715
+
716
+ this.s.scrollLeft.header = -1;
717
+ this.s.scrollLeft.footer = -1;
718
+ this.s[item + 'Mode'] = mode;
719
+
720
+ dt.trigger('fixedheader-mode', [mode, item]);
721
+ },
722
+
723
+ /**
724
+ * Cache the positional information that is required for the mode
725
+ * calculations that FixedHeader performs.
726
+ *
727
+ * @private
728
+ */
729
+ _positions: function () {
730
+ var dt = this.s.dt;
731
+ var table = dt.table();
732
+ var position = this.s.position;
733
+ var dom = this.dom;
734
+ var tableNode = $(table.node());
735
+ var scrollEnabled = this._scrollEnabled();
736
+
737
+ // Need to use the header and footer that are in the main table,
738
+ // regardless of if they are clones, since they hold the positions we
739
+ // want to measure from
740
+ var thead = $(dt.table().header());
741
+ var tfoot = $(dt.table().footer());
742
+ var tbody = dom.tbody;
743
+ var scrollBody = tableNode.parent();
744
+
745
+ position.visible = tableNode.is(':visible');
746
+ position.width = tableNode.outerWidth();
747
+ position.left = tableNode.offset().left;
748
+ position.theadTop = thead.offset().top;
749
+ position.tbodyTop = scrollEnabled
750
+ ? scrollBody.offset().top
751
+ : tbody.offset().top;
752
+ position.tbodyHeight = scrollEnabled
753
+ ? scrollBody.outerHeight()
754
+ : tbody.outerHeight();
755
+ position.theadHeight = thead.outerHeight();
756
+ position.theadBottom = position.theadTop + position.theadHeight;
757
+ position.tfootTop = position.tbodyTop + position.tbodyHeight; //tfoot.offset().top;
758
+
759
+ if (tfoot.length) {
760
+ position.tfootBottom = position.tfootTop + tfoot.outerHeight();
761
+ position.tfootHeight = tfoot.outerHeight();
762
+ }
763
+ else {
764
+ position.tfootBottom = position.tfootTop;
765
+ position.tfootHeight = 0;
766
+ }
767
+ },
768
+
769
+ /**
770
+ * Mode calculation - determine what mode the fixed items should be placed
771
+ * into.
772
+ *
773
+ * @param {boolean} forceChange Force a redraw of the mode, even if already
774
+ * in that mode.
775
+ * @private
776
+ */
777
+ _scroll: function (forceChange) {
778
+ if (this.s.dt.settings()[0].bDestroying) {
779
+ return;
780
+ }
781
+
782
+ // ScrollBody details
783
+ var scrollEnabled = this._scrollEnabled();
784
+ var scrollBody = $(this.s.dt.table().node()).parent();
785
+ var scrollOffset = scrollBody.offset();
786
+ var scrollHeight = scrollBody.outerHeight();
787
+
788
+ // Window details
789
+ var windowLeft = $(document).scrollLeft();
790
+ var windowTop = $(document).scrollTop();
791
+ var windowHeight = $(window).height();
792
+ var windowBottom = windowHeight + windowTop;
793
+
794
+ var position = this.s.position;
795
+ var headerMode, footerMode;
796
+
797
+ // Body Details
798
+ var bodyTop = scrollEnabled ? scrollOffset.top : position.tbodyTop;
799
+ var bodyLeft = scrollEnabled ? scrollOffset.left : position.left;
800
+ var bodyBottom = scrollEnabled
801
+ ? scrollOffset.top + scrollHeight
802
+ : position.tfootTop;
803
+ var bodyWidth = scrollEnabled
804
+ ? scrollBody.outerWidth()
805
+ : position.tbodyWidth;
806
+
807
+ if (this.c.header) {
808
+ if (!this.s.enable) {
809
+ headerMode = 'in-place';
810
+ }
811
+ // The header is in it's normal place if the body top is lower than
812
+ // the scroll of the window plus the headerOffset and the height of the header
813
+ else if (
814
+ !position.visible ||
815
+ windowTop + this.c.headerOffset + position.theadHeight <=
816
+ bodyTop
817
+ ) {
818
+ headerMode = 'in-place';
819
+ }
820
+ // The header should be floated if
821
+ else if (
822
+ // The scrolling plus the header offset plus the height of the header is lower than the top of the body
823
+ windowTop + this.c.headerOffset + position.theadHeight >
824
+ bodyTop &&
825
+ // And the scrolling at the top plus the header offset is above the bottom of the body
826
+ windowTop + this.c.headerOffset + position.theadHeight <
827
+ bodyBottom
828
+ ) {
829
+ headerMode = 'in';
830
+
831
+ // Further to the above, If the scrolling plus the header offset plus the header height is lower
832
+ // than the bottom of the table a shuffle is required so have to force the calculation
833
+ if (
834
+ windowTop + this.c.headerOffset + position.theadHeight >
835
+ bodyBottom ||
836
+ this.dom.header.floatingParent === undefined
837
+ ) {
838
+ forceChange = true;
839
+ }
840
+ else {
841
+ var child = this.dom.header.floatingParent
842
+ .css({
843
+ top: this.c.headerOffset,
844
+ position: 'fixed'
845
+ })
846
+ .children()
847
+ .eq(0);
848
+
849
+ if (child.find(this.dom.header.floating).length === 0) {
850
+ child.append(this.dom.header.floating);
851
+ }
852
+ }
853
+ }
854
+ // Anything else and the view is below the table
855
+ else {
856
+ headerMode = 'below';
857
+ }
858
+
859
+ if (forceChange || headerMode !== this.s.headerMode) {
860
+ this._modeChange(headerMode, 'header', forceChange);
861
+ }
862
+
863
+ this._horizontal('header', windowLeft);
864
+ }
865
+
866
+ var header = {
867
+ offset: { top: 0, left: 0 },
868
+ height: 0
869
+ };
870
+ var footer = {
871
+ offset: { top: 0, left: 0 },
872
+ height: 0
873
+ };
874
+
875
+ if (
876
+ this.c.footer &&
877
+ this.dom.tfoot.length &&
878
+ this.dom.tfoot.find('th, td').length
879
+ ) {
880
+ if (!this.s.enable) {
881
+ footerMode = 'in-place';
882
+ }
883
+ else if (
884
+ !position.visible ||
885
+ position.tfootBottom + this.c.footerOffset <= windowBottom
886
+ ) {
887
+ footerMode = 'in-place';
888
+ }
889
+ else if (
890
+ bodyBottom + position.tfootHeight + this.c.footerOffset >
891
+ windowBottom &&
892
+ bodyTop + this.c.footerOffset < windowBottom
893
+ ) {
894
+ footerMode = 'in';
895
+ forceChange = true;
896
+ }
897
+ else {
898
+ footerMode = 'above';
899
+ }
900
+
901
+ if (forceChange || footerMode !== this.s.footerMode) {
902
+ this._modeChange(footerMode, 'footer', forceChange);
903
+ }
904
+
905
+ this._horizontal('footer', windowLeft);
906
+
907
+ var getOffsetHeight = function (el) {
908
+ return {
909
+ offset: el.offset(),
910
+ height: el.outerHeight()
911
+ };
912
+ };
913
+
914
+ header = this.dom.header.floating
915
+ ? getOffsetHeight(this.dom.header.floating)
916
+ : getOffsetHeight(this.dom.thead);
917
+ footer = this.dom.footer.floating
918
+ ? getOffsetHeight(this.dom.footer.floating)
919
+ : getOffsetHeight(this.dom.tfoot);
920
+
921
+ // If scrolling is enabled and the footer is off the screen
922
+ if (scrollEnabled && footer.offset.top > windowTop) {
923
+ // && footer.offset.top >= windowBottom) {
924
+ // Calculate the gap between the top of the scrollBody and the top of the window
925
+ var overlap = windowTop - scrollOffset.top;
926
+ // The new height is the bottom of the window
927
+ var newHeight =
928
+ windowBottom +
929
+ // If the gap between the top of the scrollbody and the window is more than
930
+ // the height of the header then the top of the table is still visible so add that gap
931
+ // Doing this has effectively calculated the height from the top of the table to the bottom of the current page
932
+ (overlap > -header.height ? overlap : 0) -
933
+ // Take from that
934
+ // The top of the header plus
935
+ (header.offset.top +
936
+ // The header height if the standard header is present
937
+ (overlap < -header.height ? header.height : 0) +
938
+ // And the height of the footer
939
+ footer.height);
940
+
941
+ // Don't want a negative height
942
+ if (newHeight < 0) {
943
+ newHeight = 0;
944
+ }
945
+
946
+ // At the end of the above calculation the space between the header (top of the page if floating)
947
+ // and the point just above the footer should be the new value for the height of the table.
948
+ scrollBody.outerHeight(newHeight);
949
+
950
+ // Need some rounding here as sometimes very small decimal places are encountered
951
+ // If the actual height is bigger or equal to the height we just applied then the footer is "Floating"
952
+ if (
953
+ Math.round(scrollBody.outerHeight()) >=
954
+ Math.round(newHeight)
955
+ ) {
956
+ $(this.dom.tfoot.parent()).addClass('fixedHeader-floating');
957
+ }
958
+ // Otherwise max-width has kicked in so it is not floating
959
+ else {
960
+ $(this.dom.tfoot.parent()).removeClass(
961
+ 'fixedHeader-floating'
962
+ );
963
+ }
964
+ }
965
+ }
966
+
967
+ if (this.dom.header.floating) {
968
+ this.dom.header.floatingParent.css('left', bodyLeft - windowLeft);
969
+ }
970
+ if (this.dom.footer.floating) {
971
+ this.dom.footer.floatingParent.css('left', bodyLeft - windowLeft);
972
+ }
973
+
974
+ // If fixed columns is being used on this table then the blockers need to be copied across
975
+ // Cloning these is cleaner than creating as our own as it will keep consistency with fixedColumns automatically
976
+ // ASSUMING that the class remains the same
977
+ if (this.s.dt.settings()[0]._fixedColumns !== undefined) {
978
+ var adjustBlocker = function (side, end, el) {
979
+ if (el === undefined) {
980
+ var blocker = $(
981
+ 'div.dtfc-' + side + '-' + end + '-blocker'
982
+ );
983
+
984
+ el =
985
+ blocker.length === 0
986
+ ? null
987
+ : blocker.clone().css('z-index', 1);
988
+ }
989
+
990
+ if (el !== null) {
991
+ if (headerMode === 'in' || headerMode === 'below') {
992
+ el.appendTo('body').css({
993
+ top:
994
+ end === 'top'
995
+ ? header.offset.top
996
+ : footer.offset.top,
997
+ left:
998
+ side === 'right'
999
+ ? bodyLeft + bodyWidth - el.width()
1000
+ : bodyLeft
1001
+ });
1002
+ }
1003
+ else {
1004
+ el.detach();
1005
+ }
1006
+ }
1007
+
1008
+ return el;
1009
+ };
1010
+
1011
+ // Adjust all blockers
1012
+ this.dom.header.rightBlocker = adjustBlocker(
1013
+ 'right',
1014
+ 'top',
1015
+ this.dom.header.rightBlocker
1016
+ );
1017
+ this.dom.header.leftBlocker = adjustBlocker(
1018
+ 'left',
1019
+ 'top',
1020
+ this.dom.header.leftBlocker
1021
+ );
1022
+ this.dom.footer.rightBlocker = adjustBlocker(
1023
+ 'right',
1024
+ 'bottom',
1025
+ this.dom.footer.rightBlocker
1026
+ );
1027
+ this.dom.footer.leftBlocker = adjustBlocker(
1028
+ 'left',
1029
+ 'bottom',
1030
+ this.dom.footer.leftBlocker
1031
+ );
1032
+ }
1033
+ },
1034
+
1035
+ /**
1036
+ * Function to check if scrolling is enabled on the table or not
1037
+ * @returns Boolean value indicating if scrolling on the table is enabled or not
1038
+ */
1039
+ _scrollEnabled: function () {
1040
+ var oScroll = this.s.dt.settings()[0].oScroll;
1041
+ if (oScroll.sY !== '' || oScroll.sX !== '') {
1042
+ return true;
1043
+ }
1044
+ return false;
1045
+ },
1046
+
1047
+ /**
1048
+ * Realign columns by using the colgroup tag and
1049
+ * checking column widths
1050
+ */
1051
+ _widths: function (itemDom) {
1052
+ if (! itemDom || ! itemDom.placeholder) {
1053
+ return;
1054
+ }
1055
+
1056
+ // Match the table overall width
1057
+ var tableNode = $(this.s.dt.table().node());
1058
+ var scrollBody = $(tableNode.parent());
1059
+
1060
+ itemDom.floatingParent.css('width', scrollBody[0].offsetWidth);
1061
+ itemDom.floating.css('width', tableNode[0].offsetWidth);
1062
+
1063
+ // Strip out the old colgroup
1064
+ $('colgroup', itemDom.floating).remove();
1065
+
1066
+ // Copy the `colgroup` element to define the number of columns - needed
1067
+ // for complex header cases where a column might not have a unique
1068
+ // header
1069
+ var cols = itemDom.placeholder
1070
+ .parent()
1071
+ .find('colgroup')
1072
+ .clone()
1073
+ .appendTo(itemDom.floating)
1074
+ .find('col');
1075
+
1076
+ // However, the widths defined in the colgroup from the DataTable might
1077
+ // not exactly reflect the actual widths of the columns (content can
1078
+ // force it to stretch). So we need to copy the actual widths into the
1079
+ // colgroup / col's used for the floating header.
1080
+ var widths = this.s.dt.columns(':visible').widths();
1081
+
1082
+ for (var i=0 ; i<widths.length ; i++) {
1083
+ cols.eq(i).css('width', widths[i]);
1084
+ }
1085
+ }
1086
+ });
1087
+
1088
+ /**
1089
+ * Version
1090
+ * @type {String}
1091
+ * @static
1092
+ */
1093
+ FixedHeader.version = '4.0.3';
1094
+
1095
+ /**
1096
+ * Defaults
1097
+ * @type {Object}
1098
+ * @static
1099
+ */
1100
+ FixedHeader.defaults = {
1101
+ header: true,
1102
+ footer: false,
1103
+ headerOffset: 0,
1104
+ footerOffset: 0
1105
+ };
1106
+
1107
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1108
+ * DataTables interfaces
1109
+ */
1110
+
1111
+ // Attach for constructor access
1112
+ $.fn.dataTable.FixedHeader = FixedHeader;
1113
+ $.fn.DataTable.FixedHeader = FixedHeader;
1114
+
1115
+ // DataTables creation - check if the FixedHeader option has been defined on the
1116
+ // table and if so, initialise
1117
+ $(document).on('init.dt.dtfh', function (e, settings, json) {
1118
+ if (e.namespace !== 'dt') {
1119
+ return;
1120
+ }
1121
+
1122
+ var init = settings.oInit.fixedHeader;
1123
+ var defaults = DataTable.defaults.fixedHeader;
1124
+
1125
+ if ((init || defaults) && !settings._fixedHeader) {
1126
+ var opts = $.extend({}, defaults, init);
1127
+
1128
+ if (init !== false) {
1129
+ new FixedHeader(settings, opts);
1130
+ }
1131
+ }
1132
+ });
1133
+
1134
+ // DataTables API methods
1135
+ DataTable.Api.register('fixedHeader()', function () { });
1136
+
1137
+ DataTable.Api.register('fixedHeader.adjust()', function () {
1138
+ return this.iterator('table', function (ctx) {
1139
+ var fh = ctx._fixedHeader;
1140
+
1141
+ if (fh) {
1142
+ fh.update();
1143
+ }
1144
+ });
1145
+ });
1146
+
1147
+ DataTable.Api.register('fixedHeader.enable()', function (flag) {
1148
+ return this.iterator('table', function (ctx) {
1149
+ var fh = ctx._fixedHeader;
1150
+
1151
+ flag = flag !== undefined ? flag : true;
1152
+ if (fh && flag !== fh.enabled()) {
1153
+ fh.enable(flag);
1154
+ }
1155
+ });
1156
+ });
1157
+
1158
+ DataTable.Api.register('fixedHeader.enabled()', function () {
1159
+ if (this.context.length) {
1160
+ var fh = this.context[0]._fixedHeader;
1161
+
1162
+ if (fh) {
1163
+ return fh.enabled();
1164
+ }
1165
+ }
1166
+
1167
+ return false;
1168
+ });
1169
+
1170
+ DataTable.Api.register('fixedHeader.disable()', function () {
1171
+ return this.iterator('table', function (ctx) {
1172
+ var fh = ctx._fixedHeader;
1173
+
1174
+ if (fh && fh.enabled()) {
1175
+ fh.enable(false);
1176
+ }
1177
+ });
1178
+ });
1179
+
1180
+ $.each(['header', 'footer'], function (i, el) {
1181
+ DataTable.Api.register('fixedHeader.' + el + 'Offset()', function (offset) {
1182
+ var ctx = this.context;
1183
+
1184
+ if (offset === undefined) {
1185
+ return ctx.length && ctx[0]._fixedHeader
1186
+ ? ctx[0]._fixedHeader[el + 'Offset']()
1187
+ : undefined;
1188
+ }
1189
+
1190
+ return this.iterator('table', function (ctx) {
1191
+ var fh = ctx._fixedHeader;
1192
+
1193
+ if (fh) {
1194
+ fh[el + 'Offset'](offset);
1195
+ }
1196
+ });
1197
+ });
1198
+ });
1199
+
1200
+
1201
+ return DataTable;
1202
+ }));