jquery.fixedheadertable.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. /*!
  2. * jquery.fixedHeaderTable. The jQuery fixedHeaderTable plugin
  3. *
  4. * Copyright (c) 2013 Mark Malek
  5. * http://fixedheadertable.com
  6. *
  7. * Licensed under MIT
  8. * http://www.opensource.org/licenses/mit-license.php
  9. *
  10. * http://docs.jquery.com/Plugins/Authoring
  11. * jQuery authoring guidelines
  12. *
  13. * Launch : October 2009
  14. * Version : 1.3
  15. * Released: May 9th, 2011
  16. *
  17. *
  18. * all CSS sizing (width,height) is done in pixels (px)
  19. */
  20. (function ($) {
  21. $.fn.fixedHeaderTable = function (method) {
  22. // plugin's default options
  23. var defaults = {
  24. width: '100%',
  25. height: '100%',
  26. themeClass: 'fht-default',
  27. borderCollapse: true,
  28. fixedColumns: 0, // fixed first columns
  29. fixedColumn: false, // For backward-compatibility
  30. sortable: false,
  31. autoShow: true, // hide table after its created
  32. footer: false, // show footer
  33. cloneHeadToFoot: false, // clone head and use as footer
  34. autoResize: false, // resize table if its parent wrapper changes size
  35. create: null, // callback after plugin completes
  36. fixedEndColumns: 0
  37. };
  38. var settings = {};
  39. // public methods
  40. var methods = {
  41. init: function (options) {
  42. settings = $.extend({}, defaults, options);
  43. // iterate through all the DOM elements we are attaching the plugin to
  44. return this.each(function () {
  45. var $self = $(this); // reference the jQuery version of the current DOM element
  46. if (helpers._isTable($self)) {
  47. methods.setup.apply(this, Array.prototype.slice.call(arguments, 1));
  48. $.isFunction(settings.create) && settings.create.call(this);
  49. } else {
  50. $.error('Invalid table mark-up');
  51. }
  52. });
  53. },
  54. /*
  55. * Setup table structure for fixed headers and optional footer
  56. */
  57. setup: function () {
  58. var $self = $(this),
  59. self = this,
  60. $thead = $self.find('thead'),
  61. $tfoot = $self.find('tfoot'),
  62. tfootHeight = 0,
  63. $wrapper,
  64. $divHead,
  65. $divBody,
  66. $fixedBody,
  67. widthMinusScrollbar;
  68. settings.originalTable = $(this).clone();
  69. settings.includePadding = helpers._isPaddingIncludedWithWidth();
  70. settings.scrollbarOffset = helpers._getScrollbarWidth();
  71. settings.themeClassName = settings.themeClass;
  72. if (settings.width.search('%') > -1) {
  73. widthMinusScrollbar = $self.parent().width() - settings.scrollbarOffset;
  74. } else {
  75. widthMinusScrollbar = settings.width - settings.scrollbarOffset;
  76. }
  77. $self.css({
  78. width: widthMinusScrollbar
  79. });
  80. if (!$self.closest('.fht-table-wrapper').length) {
  81. $self.addClass('fht-table');
  82. $self.wrap('<div class="fht-table-wrapper"></div>');
  83. }
  84. $wrapper = $self.closest('.fht-table-wrapper');
  85. if(settings.fixedColumn == true && settings.fixedColumns <= 0) {
  86. settings.fixedColumns = 1;
  87. }
  88. if ((settings.fixedColumns > 0 || settings.fixedEndColumns > 0) && $wrapper.find('.fht-fixed-column').length == 0) {
  89. $self.wrap('<div class="fht-fixed-body"></div>');
  90. $('<div class="fht-fixed-column"></div>').prependTo($wrapper);
  91. $fixedBody = $wrapper.find('.fht-fixed-body');
  92. }
  93. $wrapper.css({
  94. width: settings.width,
  95. height: settings.height
  96. })
  97. .addClass(settings.themeClassName);
  98. if (!$self.hasClass('fht-table-init')) {
  99. $self.wrap('<div class="fht-tbody"></div>');
  100. }
  101. $divBody = $self.closest('.fht-tbody');
  102. var tableProps = helpers._getTableProps($self);
  103. helpers._setupClone($divBody, tableProps.tbody);
  104. if (!$self.hasClass('fht-table-init')) {
  105. if (settings.fixedColumns > 0 || settings.fixedEndColumns > 0) {
  106. $divHead = $('<div class="fht-thead"><table class="fht-table"></table><div class="fht-thead-box"></div></div>').prependTo($fixedBody);
  107. } else {
  108. $divHead = $('<div class="fht-thead"><table class="fht-table"></table><div class="fht-thead-box"></div></div>').prependTo($wrapper);
  109. }
  110. $divHead.find('table.fht-table')
  111. .addClass(settings.originalTable.attr('class'))
  112. .attr('style', settings.originalTable.attr('style'));
  113. $thead.clone().appendTo($divHead.find('table'));
  114. } else {
  115. $divHead = $wrapper.find('div.fht-thead');
  116. }
  117. helpers._setupClone($divHead, tableProps.thead);
  118. $self.css({
  119. 'margin-top': -$divHead.outerHeight(true)
  120. });
  121. /*
  122. * Check for footer
  123. * Setup footer if present
  124. */
  125. if (settings.footer == true) {
  126. helpers._setupTableFooter($self, self, tableProps);
  127. if (!$tfoot.length) {
  128. $tfoot = $wrapper.find('div.fht-tfoot table');
  129. }
  130. tfootHeight = $tfoot.outerHeight(true);
  131. }
  132. var tbodyHeight = $wrapper.height() - $thead.outerHeight(true) - tfootHeight - tableProps.border;
  133. $divBody.css({
  134. 'height': tbodyHeight
  135. });
  136. $self.addClass('fht-table-init');
  137. if (typeof(settings.altClass) !== 'undefined') {
  138. methods.altRows.apply(self);
  139. }
  140. if (settings.fixedColumns > 0) {
  141. helpers._setupFixedColumn($self, self, tableProps);
  142. }
  143. if (settings.fixedEndColumns > 0) {
  144. helpers._setupFixedEndColumn($self, self, tableProps);
  145. }
  146. if (!settings.autoShow) {
  147. $wrapper.hide();
  148. }
  149. helpers._bindScroll($divBody, tableProps);
  150. return self;
  151. },
  152. /*
  153. * Resize the table
  154. * Incomplete - not implemented yet
  155. */
  156. resize: function() {
  157. var self = this;
  158. return self;
  159. },
  160. /*
  161. * Add CSS class to alternating rows
  162. */
  163. altRows: function(arg1) {
  164. var $self = $(this),
  165. altClass = (typeof(arg1) !== 'undefined') ? arg1 : settings.altClass;
  166. $self.closest('.fht-table-wrapper')
  167. .find('tbody tr:odd:not(:hidden)')
  168. .addClass(altClass);
  169. },
  170. /*
  171. * Show a hidden fixedHeaderTable table
  172. */
  173. show: function(arg1, arg2, arg3) {
  174. var $self = $(this),
  175. self = this,
  176. $wrapper = $self.closest('.fht-table-wrapper');
  177. // User provided show duration without a specific effect
  178. if (typeof(arg1) !== 'undefined' && typeof(arg1) === 'number') {
  179. $wrapper.show(arg1, function() {
  180. $.isFunction(arg2) && arg2.call(this);
  181. });
  182. return self;
  183. } else if (typeof(arg1) !== 'undefined' && typeof(arg1) === 'string' &&
  184. typeof(arg2) !== 'undefined' && typeof(arg2) === 'number') {
  185. // User provided show duration with an effect
  186. $wrapper.show(arg1, arg2, function() {
  187. $.isFunction(arg3) && arg3.call(this);
  188. });
  189. return self;
  190. }
  191. $self.closest('.fht-table-wrapper')
  192. .show();
  193. $.isFunction(arg1) && arg1.call(this);
  194. return self;
  195. },
  196. /*
  197. * Hide a fixedHeaderTable table
  198. */
  199. hide: function(arg1, arg2, arg3) {
  200. var $self = $(this),
  201. self = this,
  202. $wrapper = $self.closest('.fht-table-wrapper');
  203. // User provided show duration without a specific effect
  204. if (typeof(arg1) !== 'undefined' && typeof(arg1) === 'number') {
  205. $wrapper.hide(arg1, function() {
  206. $.isFunction(arg3) && arg3.call(this);
  207. });
  208. return self;
  209. } else if (typeof(arg1) !== 'undefined' && typeof(arg1) === 'string' &&
  210. typeof(arg2) !== 'undefined' && typeof(arg2) === 'number') {
  211. $wrapper.hide(arg1, arg2, function() {
  212. $.isFunction(arg3) && arg3.call(this);
  213. });
  214. return self;
  215. }
  216. $self.closest('.fht-table-wrapper')
  217. .hide();
  218. $.isFunction(arg3) && arg3.call(this);
  219. return self;
  220. },
  221. /*
  222. * Destory fixedHeaderTable and return table to original state
  223. */
  224. destroy: function() {
  225. var $self = $(this),
  226. self = this,
  227. $wrapper = $self.closest('.fht-table-wrapper');
  228. $self.insertBefore($wrapper)
  229. .removeAttr('style')
  230. .append($wrapper.find('tfoot'))
  231. .removeClass('fht-table fht-table-init')
  232. .find('.fht-cell')
  233. .remove();
  234. $wrapper.remove();
  235. return self;
  236. }
  237. };
  238. // private methods
  239. var helpers = {
  240. /*
  241. * return boolean
  242. * True if a thead and tbody exist.
  243. */
  244. _isTable: function($obj) {
  245. var $self = $obj,
  246. hasTable = $self.is('table'),
  247. hasThead = $self.find('thead').length > 0,
  248. hasTbody = $self.find('tbody').length > 0;
  249. if (hasTable && hasThead && hasTbody) {
  250. return true;
  251. }
  252. return false;
  253. },
  254. /*
  255. * return void
  256. * bind scroll event
  257. */
  258. _bindScroll: function($obj) {
  259. var $self = $obj,
  260. $wrapper = $self.closest('.fht-table-wrapper'),
  261. $thead = $self.siblings('.fht-thead'),
  262. $tfoot = $self.siblings('.fht-tfoot');
  263. $self.bind('scroll', function() {
  264. if (settings.fixedColumns > 0 || settings.fixedEndColumns > 0) {
  265. var $fixedColumns = $wrapper.find('.fht-fixed-column');
  266. $fixedColumns.find('.fht-tbody table')
  267. .css({
  268. 'margin-top': -$self.scrollTop()
  269. });
  270. }
  271. $thead.find('table')
  272. .css({
  273. 'margin-left': -this.scrollLeft
  274. });
  275. if (settings.footer || settings.cloneHeadToFoot) {
  276. $tfoot.find('table')
  277. .css({
  278. 'margin-left': -this.scrollLeft
  279. });
  280. }
  281. });
  282. },
  283. /*
  284. * return void
  285. */
  286. _fixHeightWithCss: function ($obj, tableProps) {
  287. if (settings.includePadding) {
  288. $obj.css({
  289. 'height': $obj.height() + tableProps.border
  290. });
  291. } else {
  292. $obj.css({
  293. 'height': $obj.parent().height() + tableProps.border
  294. });
  295. }
  296. },
  297. /*
  298. * return void
  299. */
  300. _fixWidthWithCss: function($obj, tableProps, width) {
  301. if (settings.includePadding) {
  302. $obj.each(function() {
  303. $(this).css({
  304. 'width': width == undefined ? $(this).width() + tableProps.border : width + tableProps.border
  305. });
  306. });
  307. } else {
  308. $obj.each(function() {
  309. $(this).css({
  310. 'width': width == undefined ? $(this).parent().width() + tableProps.border : width + tableProps.border
  311. });
  312. });
  313. }
  314. },
  315. /*
  316. * return void
  317. */
  318. _setupFixedColumn: function ($obj, obj, tableProps) {
  319. var $self = $obj,
  320. $wrapper = $self.closest('.fht-table-wrapper'),
  321. $fixedBody = $wrapper.find('.fht-fixed-body'),
  322. $fixedColumn = $wrapper.find('.fht-fixed-column'),
  323. $thead = $('<div class="fht-thead"><table class="fht-table"><thead><tr></tr></thead></table></div>'),
  324. $tbody = $('<div class="fht-tbody"><table class="fht-table"><tbody></tbody></table></div>'),
  325. $tfoot = $('<div class="fht-tfoot"><table class="fht-table"><tfoot><tr class="tr-total"></tr></tfoot></table></div>'),
  326. fixedBodyWidth = $wrapper.width(),
  327. fixedBodyHeight = $fixedBody.find('.fht-tbody').height() - settings.scrollbarOffset,
  328. $firstThChildren,
  329. $firstTdChildren,
  330. fixedColumnWidth,
  331. $newRow,
  332. firstTdChildrenSelector;
  333. $thead.find('table.fht-table').addClass(settings.originalTable.attr('class'));
  334. $tbody.find('table.fht-table').addClass(settings.originalTable.attr('class'));
  335. $tfoot.find('table.fht-table').addClass(settings.originalTable.attr('class'));
  336. $firstThChildren = $fixedBody.find('.fht-thead thead tr > *:lt(' + settings.fixedColumns + ')');
  337. fixedColumnWidth = settings.fixedColumns * tableProps.border;
  338. $firstThChildren.each(function() {
  339. fixedColumnWidth += $(this).outerWidth(true);
  340. });
  341. // Fix cell heights
  342. helpers._fixHeightWithCss($firstThChildren, tableProps);
  343. helpers._fixWidthWithCss($firstThChildren, tableProps);
  344. var tdWidths = [];
  345. $firstThChildren.each(function() {
  346. tdWidths.push($(this).width());
  347. });
  348. firstTdChildrenSelector = 'tbody tr > *:not(:nth-child(n+' + (settings.fixedColumns + 1) + '))';
  349. $firstTdChildren = $fixedBody.find(firstTdChildrenSelector)
  350. .each(function(index) {
  351. helpers._fixHeightWithCss($(this), tableProps);
  352. helpers._fixWidthWithCss($(this), tableProps, tdWidths[index % settings.fixedColumns] );
  353. });
  354. // clone header
  355. $thead.appendTo($fixedColumn)
  356. .find('tr')
  357. .append($firstThChildren.clone());
  358. $tbody.appendTo($fixedColumn)
  359. .css({
  360. 'margin-top': -1,
  361. 'height': fixedBodyHeight + tableProps.border
  362. });
  363. $firstTdChildren.each(function(index) {
  364. if (index % settings.fixedColumns == 0) {
  365. $newRow = $('<tr></tr>').appendTo($tbody.find('tbody'));
  366. if (settings.altClass && $(this).parent().hasClass(settings.altClass)) {
  367. $newRow.addClass(settings.altClass);
  368. }
  369. }
  370. $(this).clone()
  371. .appendTo($newRow);
  372. });
  373. // set width of fixed column wrapper
  374. $fixedColumn.css({
  375. 'height': 0,
  376. 'width': fixedColumnWidth
  377. });
  378. // bind mousewheel events
  379. var maxTop = $fixedColumn.find('.fht-tbody .fht-table').height() - $fixedColumn.find('.fht-tbody').height();
  380. $fixedColumn.find('.fht-tbody .fht-table').bind('mousewheel', function(event, delta, deltaX, deltaY) {
  381. if (deltaY == 0) {
  382. return;
  383. }
  384. var top = parseInt($(this).css('marginTop'), 10) + (deltaY > 0 ? 120 : -120);
  385. if (top > 0) {
  386. top = 0;
  387. }
  388. if (top < -maxTop) {
  389. top = -maxTop;
  390. }
  391. $(this).css('marginTop', top);
  392. $fixedBody.find('.fht-tbody').scrollTop(-top).scroll();
  393. return false;
  394. });
  395. // set width of body table wrapper
  396. $fixedBody.css({
  397. 'width': fixedBodyWidth
  398. });
  399. // setup clone footer with fixed column
  400. if (settings.footer == true || settings.cloneHeadToFoot == true) {
  401. var $firstTdFootChild = $fixedBody.find('.fht-tfoot tr > *:lt(' + settings.fixedColumns + ')'),
  402. footwidth;
  403. helpers._fixHeightWithCss($firstTdFootChild, tableProps);
  404. $tfoot.appendTo($fixedColumn)
  405. .find('tr')
  406. .append($firstTdFootChild.clone());
  407. // Set (view width) of $tfoot div to width of table (this accounts for footers with a colspan)
  408. footwidth = $tfoot.find('table').innerWidth();
  409. $tfoot.css({
  410. 'top': settings.scrollbarOffset,
  411. 'width': footwidth
  412. });
  413. }
  414. },
  415. _setupFixedEndColumn: function ($obj, obj, tableProps) {
  416. var $self = $obj,
  417. $wrapper = $self.closest('.fht-table-wrapper'),
  418. $fixedBody = $wrapper.find('.fht-fixed-body'),
  419. $fixedColumn = $wrapper.find('.fht-fixed-column'),
  420. $thead = $('<div class="fht-thead"><table class="fht-table"><thead><tr></tr></thead></table></div></div>'),
  421. $tbody = $('<div class="fht-tbody"><table class="fht-table"><tbody></tbody></table></div>'),
  422. $tfoot = $('<div class="fht-tfoot"><table class="fht-table"><tfoot><tr class="tr-total"></tr></tfoot></table></div>'),
  423. fixedBodyWidth = $wrapper.width(),
  424. fixedBodyHeight = $fixedBody.find('.fht-tbody').height() - settings.scrollbarOffset,
  425. $firstThChildren,
  426. $firstTdChildren,
  427. fixedColumnWidth,
  428. $newRow,
  429. firstTdChildrenSelector;
  430. $thead.find('table.fht-table').addClass(settings.originalTable.attr('class'));
  431. $tbody.find('table.fht-table').addClass(settings.originalTable.attr('class'));
  432. $tfoot.find('table.fht-table').addClass(settings.originalTable.attr('class'));
  433. var fixedColumns = $fixedBody.find('.fht-thead thead tr th').length - settings.fixedEndColumns + 1;
  434. $firstThChildren = $fixedBody.find('.fht-thead thead tr > *:gt(' + (fixedColumns - 2) + ')');
  435. fixedColumnWidth = fixedColumns * tableProps.border;
  436. $firstThChildren.each(function () {
  437. fixedColumnWidth += $(this).outerWidth(true);
  438. });
  439. // Fix cell heights
  440. helpers._fixHeightWithCss($firstThChildren, tableProps);
  441. helpers._fixWidthWithCss($firstThChildren, tableProps);
  442. var tdWidths = [];
  443. $firstThChildren.each(function () {
  444. tdWidths.push($(this).width());
  445. });
  446. firstTdChildrenSelector = 'tbody tr > *:nth-child(n+' + (fixedColumns) + ')';
  447. $firstTdChildren = $fixedBody.find(firstTdChildrenSelector)
  448. .each(function (index) {
  449. helpers._fixHeightWithCss($(this), tableProps);
  450. helpers._fixWidthWithCss($(this), tableProps, tdWidths[index % fixedColumns]);
  451. });
  452. // clone header
  453. $thead.appendTo($fixedColumn)
  454. .find('tr')
  455. .append($firstThChildren.clone());
  456. $tbody.appendTo($fixedColumn)
  457. .css({
  458. 'margin-top': -1,
  459. 'height': fixedBodyHeight + tableProps.border
  460. });
  461. $firstTdChildren.each(function (index) {
  462. $newRow = $('<tr></tr>').appendTo($tbody.find('tbody'));
  463. if (settings.altClass && $(this).parent().hasClass(settings.altClass)) {
  464. $newRow.addClass(settings.altClass);
  465. }
  466. $(this).clone()
  467. .appendTo($newRow);
  468. });
  469. // set width of fixed column wrapper
  470. $fixedColumn.css({
  471. 'height': 0,
  472. 'width': fixedColumnWidth - 10,
  473. 'position': "relative",
  474. 'float': "right",
  475. 'padding-right': ($fixedBody.find('.fht-tbody').find("table").height() - $fixedBody.find('.fht-tbody').find("table").find("thead").height()) > $fixedBody.find('.fht-tbody').height() ? "7px" : "0px",
  476. 'margin-right': ($fixedBody.find('.fht-tbody').find("table").height() - $fixedBody.find('.fht-tbody').find("table").find("thead").height()) > $fixedBody.find('.fht-tbody').height() ? "0px" : "-1px"
  477. });
  478. if (($fixedBody.find('.fht-tbody').find("table").height() - $fixedBody.find('.fht-tbody').find("table").find("thead").height()) > $fixedBody.find('.fht-tbody').height()) {
  479. $fixedColumn.find(".fht-thead").css({
  480. 'margin-right': "-8px"
  481. });
  482. }
  483. // bind mousewheel events
  484. var maxTop = $fixedColumn.find('.fht-tbody .fht-table').height() - $fixedColumn.find('.fht-tbody').height();
  485. $fixedColumn.find('.fht-tbody .fht-table').bind('mousewheel', function (event, delta, deltaX, deltaY) {
  486. if (deltaY == 0) {
  487. return;
  488. }
  489. var top = parseInt($(this).css('marginTop'), 10) + (deltaY > 0 ? 120 : -120);
  490. if (top > 0) {
  491. top = 0;
  492. }
  493. if (top < -maxTop) {
  494. top = -maxTop;
  495. }
  496. $(this).css('marginTop', top);
  497. $fixedBody.find('.fht-tbody').scrollTop(-top).scroll();
  498. return false;
  499. });
  500. // set width of body table wrapper
  501. $fixedBody.css({
  502. 'width': fixedBodyWidth
  503. });
  504. // setup clone footer with fixed column
  505. if (settings.footer == true || settings.cloneHeadToFoot == true) {
  506. var $firstTdFootChild = $fixedBody.find('.fht-tfoot tr > *:gt(' + (fixedColumns - 2) + ')'),
  507. footwidth;
  508. helpers._fixHeightWithCss($firstTdFootChild, tableProps);
  509. $tfoot.appendTo($fixedColumn)
  510. .find('tr')
  511. .append($firstTdFootChild.clone());
  512. // Set (view width) of $tfoot div to width of table (this accounts for footers with a colspan)
  513. footwidth = $tfoot.find('table').innerWidth();
  514. $tfoot.css({
  515. 'top': settings.scrollbarOffset,
  516. 'width': footwidth
  517. });
  518. }
  519. },
  520. /*
  521. * return void
  522. */
  523. _setupTableFooter: function ($obj, obj, tableProps) {
  524. var $self = $obj,
  525. $wrapper = $self.closest('.fht-table-wrapper'),
  526. $tfoot = $self.find('tfoot'),
  527. $divFoot = $wrapper.find('div.fht-tfoot');
  528. if (!$divFoot.length) {
  529. if (settings.fixedColumns > 0) {
  530. $divFoot = $('<div class="fht-tfoot"><table class="fht-table"></table></div>').appendTo($wrapper.find('.fht-fixed-body'));
  531. } else {
  532. $divFoot = $('<div class="fht-tfoot"><table class="fht-table"></table></div>').appendTo($wrapper);
  533. }
  534. }
  535. $divFoot.find('table.fht-table').addClass(settings.originalTable.attr('class'));
  536. switch (true) {
  537. case !$tfoot.length && settings.cloneHeadToFoot == true && settings.footer == true:
  538. var $divHead = $wrapper.find('div.fht-thead');
  539. $divFoot.empty();
  540. $divHead.find('table')
  541. .clone()
  542. .appendTo($divFoot);
  543. break;
  544. case $tfoot.length && settings.cloneHeadToFoot == false && settings.footer == true:
  545. $divFoot.find('table')
  546. .append($tfoot)
  547. .css({
  548. 'margin-top': -tableProps.border
  549. });
  550. helpers._setupClone($divFoot, tableProps.tfoot);
  551. break;
  552. }
  553. },
  554. /*
  555. * return object
  556. * Widths of each thead cell and tbody cell for the first rows.
  557. * Used in fixing widths for the fixed header and optional footer.
  558. */
  559. _getTableProps: function($obj) {
  560. var tableProp = {
  561. thead: {},
  562. tbody: {},
  563. tfoot: {},
  564. border: 0
  565. },
  566. borderCollapse = 1;
  567. if (settings.borderCollapse == true) {
  568. borderCollapse = 2;
  569. }
  570. tableProp.border = ($obj.find('th:first-child').outerWidth() - $obj.find('th:first-child').innerWidth()) / borderCollapse;
  571. $obj.find('thead tr:first-child > *').each(function(index) {
  572. tableProp.thead[index] = $(this).width() + tableProp.border;
  573. });
  574. $obj.find('tfoot tr:first-child > *').each(function(index) {
  575. tableProp.tfoot[index] = $(this).width() + tableProp.border;
  576. });
  577. $obj.find('tbody tr:first-child > *').each(function(index) {
  578. tableProp.tbody[index] = $(this).width() + tableProp.border;
  579. });
  580. return tableProp;
  581. },
  582. /*
  583. * return void
  584. * Fix widths of each cell in the first row of obj.
  585. */
  586. _setupClone: function($obj, cellArray) {
  587. var $self = $obj,
  588. selector = ($self.find('thead').length) ?
  589. 'thead tr:first-child > *' :
  590. ($self.find('tfoot').length) ?
  591. 'tfoot tr:first-child > *' :
  592. 'tbody tr:first-child > *',
  593. $cell;
  594. $self.find(selector).each(function(index) {
  595. $cell = ($(this).find('div.fht-cell').length) ? $(this).find('div.fht-cell') : $('<div class="fht-cell"></div>').appendTo($(this));
  596. $cell.css({
  597. 'width': parseInt(cellArray[index], 10)
  598. });
  599. /*
  600. * Fixed Header and Footer should extend the full width
  601. * to align with the scrollbar of the body
  602. */
  603. if (!$(this).closest('.fht-tbody').length && $(this).is(':last-child') && !$(this).closest('.fht-fixed-column').length && !$(this).closest('.fht-tfoot').length) {
  604. var padding = Math.max((($(this).innerWidth() - $(this).width()) / 2), settings.scrollbarOffset);
  605. $(this).css({
  606. //'padding-right': parseInt($(this).css('padding-right')) + padding + 'px'
  607. });
  608. }
  609. });
  610. },
  611. /*
  612. * return boolean
  613. * Determine how the browser calculates fixed widths with padding for tables
  614. * true if width = padding + width
  615. * false if width = width
  616. */
  617. _isPaddingIncludedWithWidth: function() {
  618. var $obj = $('<table class="fht-table"><tr><td style="padding: 10px; font-size: 10px;">test</td></tr></table>'),
  619. defaultHeight,
  620. newHeight;
  621. $obj.addClass(settings.originalTable.attr('class'));
  622. $obj.appendTo('body');
  623. defaultHeight = $obj.find('td').height();
  624. $obj.find('td')
  625. .css('height', $obj.find('tr').height());
  626. newHeight = $obj.find('td').height();
  627. $obj.remove();
  628. if (defaultHeight != newHeight) {
  629. return true;
  630. } else {
  631. return false;
  632. }
  633. },
  634. /*
  635. * return int
  636. * get the width of the browsers scroll bar
  637. */
  638. _getScrollbarWidth: function() {
  639. var scrollbarWidth = 0;
  640. if (!scrollbarWidth) {
  641. if (/msie/.test(navigator.userAgent.toLowerCase())) {
  642. var $textarea1 = $('<textarea cols="10" rows="2"></textarea>')
  643. .css({ position: 'absolute', top: -1000, left: -1000 }).appendTo('body'),
  644. $textarea2 = $('<textarea cols="10" rows="2" style="overflow: hidden;"></textarea>')
  645. .css({ position: 'absolute', top: -1000, left: -1000 }).appendTo('body');
  646. scrollbarWidth = $textarea1.width() - $textarea2.width() + 2; // + 2 for border offset
  647. $textarea1.add($textarea2).remove();
  648. } else {
  649. var $div = $('<div />')
  650. .css({ width: 100, height: 100, overflow: 'auto', position: 'absolute', top: -1000, left: -1000 })
  651. .prependTo('body').append('<div />').find('div')
  652. .css({ width: '100%', height: 200 });
  653. scrollbarWidth = 100 - $div.width();
  654. $div.parent().remove();
  655. }
  656. }
  657. return scrollbarWidth;
  658. }
  659. };
  660. // if a method as the given argument exists
  661. if (methods[method]) {
  662. // call the respective method
  663. return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  664. // if an object is given as method OR nothing is given as argument
  665. } else if (typeof method === 'object' || !method) {
  666. // call the initialization method
  667. return methods.init.apply(this, arguments);
  668. // otherwise
  669. } else {
  670. // trigger an error
  671. $.error('Method "' + method + '" does not exist in fixedHeaderTable plugin!');
  672. }
  673. };
  674. })(jQuery);