dataTables.FixedHeader.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. /*
  2. * File: FixedHeader.js
  3. * Version: 2.0.5
  4. * Description: "Fix" a header at the top of the table, so it scrolls with the table
  5. * Author: Allan Jardine (www.sprymedia.co.uk)
  6. * Created: Wed 16 Sep 2009 19:46:30 BST
  7. * Language: Javascript
  8. * License: GPL v2 or BSD 3 point style
  9. * Project: Just a little bit of fun - enjoy :-)
  10. * Contact: www.sprymedia.co.uk/contact
  11. *
  12. * Copyright 2009-2010 Allan Jardine, all rights reserved.
  13. *
  14. * This source file is free software, under either the GPL v2 license or a
  15. * BSD style license, available at:
  16. * http://datatables.net/license_gpl2
  17. * http://datatables.net/license_bsd
  18. */
  19. /*
  20. * Function: FixedHeader
  21. * Purpose: Provide 'fixed' header, footer and columns on an HTML table
  22. * Returns: object:FixedHeader - must be called with 'new'
  23. * Inputs: mixed:mTable - target table
  24. * 1. DataTable object - when using FixedHeader with DataTables, or
  25. * 2. HTML table node - when using FixedHeader without DataTables
  26. * object:oInit - initialisation settings, with the following properties (each optional)
  27. * bool:top - fix the header (default true)
  28. * bool:bottom - fix the footer (default false)
  29. * bool:left - fix the left most column (default false)
  30. * bool:right - fix the right most column (default false)
  31. * int:zTop - fixed header zIndex
  32. * int:zBottom - fixed footer zIndex
  33. * int:zLeft - fixed left zIndex
  34. * int:zRight - fixed right zIndex
  35. */
  36. var FixedHeader = function ( mTable, oInit ) {
  37. /* Sanity check - you just know it will happen */
  38. if ( typeof this.fnInit != 'function' )
  39. {
  40. alert( "FixedHeader warning: FixedHeader must be initialised with the 'new' keyword." );
  41. return;
  42. }
  43. var that = this;
  44. var oSettings = {
  45. "aoCache": [],
  46. "oSides": {
  47. "top": true,
  48. "bottom": false,
  49. "left": false,
  50. "right": false
  51. },
  52. "oZIndexes": {
  53. "top": 104,
  54. "bottom": 103,
  55. "left": 102,
  56. "right": 101
  57. },
  58. "oMes": {
  59. "iTableWidth": 0,
  60. "iTableHeight": 0,
  61. "iTableLeft": 0,
  62. "iTableRight": 0, /* note this is left+width, not actually "right" */
  63. "iTableTop": 0,
  64. "iTableBottom": 0 /* note this is top+height, not actually "bottom" */
  65. },
  66. "nTable": null,
  67. "bUseAbsPos": false,
  68. "bFooter": false
  69. };
  70. /*
  71. * Function: fnGetSettings
  72. * Purpose: Get the settings for this object
  73. * Returns: object: - settings object
  74. * Inputs: -
  75. */
  76. this.fnGetSettings = function () {
  77. return oSettings;
  78. };
  79. /*
  80. * Function: fnUpdate
  81. * Purpose: Update the positioning and copies of the fixed elements
  82. * Returns: -
  83. * Inputs: -
  84. */
  85. this.fnUpdate = function () {
  86. this._fnUpdateClones();
  87. this._fnUpdatePositions();
  88. };
  89. /*
  90. * Function: fnPosition
  91. * Purpose: Update the positioning of the fixed elements
  92. * Returns: -
  93. * Inputs: -
  94. */
  95. this.fnPosition = function () {
  96. this._fnUpdatePositions();
  97. };
  98. /* Let's do it */
  99. this.fnInit( mTable, oInit );
  100. /* Store the instance on the DataTables object for easy access */
  101. if ( typeof mTable.fnSettings == 'function' )
  102. {
  103. mTable._oPluginFixedHeader = this;
  104. }
  105. };
  106. /*
  107. * Variable: FixedHeader
  108. * Purpose: Prototype for FixedHeader
  109. * Scope: global
  110. */
  111. FixedHeader.prototype = {
  112. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  113. * Initialisation
  114. */
  115. /*
  116. * Function: fnInit
  117. * Purpose: The "constructor"
  118. * Returns: -
  119. * Inputs: {as FixedHeader function}
  120. */
  121. fnInit: function ( oTable, oInit )
  122. {
  123. var s = this.fnGetSettings();
  124. var that = this;
  125. /* Record the user definable settings */
  126. this.fnInitSettings( s, oInit );
  127. /* DataTables specific stuff */
  128. if ( typeof oTable.fnSettings == 'function' )
  129. {
  130. if ( typeof oTable.fnVersionCheck == 'functon' &&
  131. oTable.fnVersionCheck( '1.6.0' ) !== true )
  132. {
  133. alert( "FixedHeader 2 required DataTables 1.6.0 or later. "+
  134. "Please upgrade your DataTables installation" );
  135. return;
  136. }
  137. var oDtSettings = oTable.fnSettings();
  138. if ( oDtSettings.oScroll.sX != "" || oDtSettings.oScroll.sY != "" )
  139. {
  140. alert( "FixedHeader 2 is not supported with DataTables' scrolling mode at this time" );
  141. return;
  142. }
  143. s.nTable = oDtSettings.nTable;
  144. oDtSettings.aoDrawCallback.push( {
  145. "fn": function () {
  146. FixedHeader.fnMeasure();
  147. that._fnUpdateClones.call(that);
  148. that._fnUpdatePositions.call(that);
  149. },
  150. "sName": "FixedHeader"
  151. } );
  152. }
  153. else
  154. {
  155. s.nTable = oTable;
  156. }
  157. s.bFooter = ($('>tfoot', s.nTable).length > 0) ? true : false;
  158. /* "Detect" browsers that don't support absolute positioing - or have bugs */
  159. s.bUseAbsPos = (jQuery.browser.msie && (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0"));
  160. /* Add the 'sides' that are fixed */
  161. if ( s.oSides.top )
  162. {
  163. s.aoCache.push( that._fnCloneTable( "fixedHeader", "FixedHeader_Header", that._fnCloneThead ) );
  164. }
  165. if ( s.oSides.bottom )
  166. {
  167. s.aoCache.push( that._fnCloneTable( "fixedFooter", "FixedHeader_Footer", that._fnCloneTfoot ) );
  168. }
  169. if ( s.oSides.left )
  170. {
  171. s.aoCache.push( that._fnCloneTable( "fixedLeft", "FixedHeader_Left", that._fnCloneTLeft ) );
  172. }
  173. if ( s.oSides.right )
  174. {
  175. s.aoCache.push( that._fnCloneTable( "fixedRight", "FixedHeader_Right", that._fnCloneTRight ) );
  176. }
  177. /* Event listeners for window movement */
  178. FixedHeader.afnScroll.push( function () {
  179. that._fnUpdatePositions.call(that);
  180. } );
  181. jQuery(window).resize( function () {
  182. FixedHeader.fnMeasure();
  183. that._fnUpdateClones.call(that);
  184. that._fnUpdatePositions.call(that);
  185. } );
  186. /* Get things right to start with */
  187. FixedHeader.fnMeasure();
  188. that._fnUpdateClones();
  189. that._fnUpdatePositions();
  190. },
  191. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  192. * Support functions
  193. */
  194. /*
  195. * Function: fnInitSettings
  196. * Purpose: Take the user's settings and copy them to our local store
  197. * Returns: -
  198. * Inputs: object:s - the local settings object
  199. * object:oInit - the user's settings object
  200. */
  201. fnInitSettings: function ( s, oInit )
  202. {
  203. if ( typeof oInit != 'undefined' )
  204. {
  205. if ( typeof oInit.top != 'undefined' ) {
  206. s.oSides.top = oInit.top;
  207. }
  208. if ( typeof oInit.bottom != 'undefined' ) {
  209. s.oSides.bottom = oInit.bottom;
  210. }
  211. if ( typeof oInit.left != 'undefined' ) {
  212. s.oSides.left = oInit.left;
  213. }
  214. if ( typeof oInit.right != 'undefined' ) {
  215. s.oSides.right = oInit.right;
  216. }
  217. if ( typeof oInit.zTop != 'undefined' ) {
  218. s.oZIndexes.top = oInit.zTop;
  219. }
  220. if ( typeof oInit.zBottom != 'undefined' ) {
  221. s.oZIndexes.bottom = oInit.zBottom;
  222. }
  223. if ( typeof oInit.zLeft != 'undefined' ) {
  224. s.oZIndexes.left = oInit.zLeft;
  225. }
  226. if ( typeof oInit.zRight != 'undefined' ) {
  227. s.oZIndexes.right = oInit.zRight;
  228. }
  229. }
  230. /* Detect browsers which have poor position:fixed support so we can use absolute positions.
  231. * This is much slower since the position must be updated for each scroll, but widens
  232. * compatibility
  233. */
  234. s.bUseAbsPos = (jQuery.browser.msie &&
  235. (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0"));
  236. },
  237. /*
  238. * Function: _fnCloneTable
  239. * Purpose: Clone the table node and do basic initialisation
  240. * Returns: -
  241. * Inputs: -
  242. */
  243. _fnCloneTable: function ( sType, sClass, fnClone )
  244. {
  245. var s = this.fnGetSettings();
  246. var nCTable;
  247. /* We know that the table _MUST_ has a DIV wrapped around it, because this is simply how
  248. * DataTables works. Therefore, we can set this to be relatively position (if it is not
  249. * alreadu absolute, and use this as the base point for the cloned header
  250. */
  251. if ( jQuery(s.nTable.parentNode).css('position') != "absolute" )
  252. {
  253. s.nTable.parentNode.style.position = "relative";
  254. }
  255. /* Just a shallow clone will do - we only want the table node */
  256. nCTable = s.nTable.cloneNode( false );
  257. nCTable.removeAttribute( 'id' );
  258. var nDiv = document.createElement( 'div' );
  259. nDiv.style.position = "absolute";
  260. nDiv.style.top = "0px";
  261. nDiv.style.left = "0px";
  262. nDiv.className += " FixedHeader_Cloned "+sType+" "+sClass;
  263. /* Set the zIndexes */
  264. if ( sType == "fixedHeader" )
  265. {
  266. nDiv.style.zIndex = s.oZIndexes.top;
  267. }
  268. if ( sType == "fixedFooter" )
  269. {
  270. nDiv.style.zIndex = s.oZIndexes.bottom;
  271. }
  272. if ( sType == "fixedLeft" )
  273. {
  274. nDiv.style.zIndex = s.oZIndexes.left;
  275. }
  276. else if ( sType == "fixedRight" )
  277. {
  278. nDiv.style.zIndex = s.oZIndexes.right;
  279. }
  280. /* remove margins since we are going to poistion it absolutely */
  281. nCTable.style.margin = "0";
  282. /* Insert the newly cloned table into the DOM, on top of the "real" header */
  283. nDiv.appendChild( nCTable );
  284. document.body.appendChild( nDiv );
  285. return {
  286. "nNode": nCTable,
  287. "nWrapper": nDiv,
  288. "sType": sType,
  289. "sPosition": "",
  290. "sTop": "",
  291. "sLeft": "",
  292. "fnClone": fnClone
  293. };
  294. },
  295. /*
  296. * Function: _fnUpdatePositions
  297. * Purpose: Get the current positioning of the table in the DOM
  298. * Returns: -
  299. * Inputs: -
  300. */
  301. _fnMeasure: function ()
  302. {
  303. var
  304. s = this.fnGetSettings(),
  305. m = s.oMes,
  306. jqTable = jQuery(s.nTable),
  307. oOffset = jqTable.offset(),
  308. iParentScrollTop = this._fnSumScroll( s.nTable.parentNode, 'scrollTop' ),
  309. iParentScrollLeft = this._fnSumScroll( s.nTable.parentNode, 'scrollLeft' );
  310. m.iTableWidth = jqTable.outerWidth();
  311. m.iTableHeight = jqTable.outerHeight();
  312. m.iTableLeft = oOffset.left + s.nTable.parentNode.scrollLeft;
  313. m.iTableTop = oOffset.top + iParentScrollTop;
  314. m.iTableRight = m.iTableLeft + m.iTableWidth;
  315. m.iTableRight = FixedHeader.oDoc.iWidth - m.iTableLeft - m.iTableWidth;
  316. m.iTableBottom = FixedHeader.oDoc.iHeight - m.iTableTop - m.iTableHeight;
  317. },
  318. /*
  319. * Function: _fnSumScroll
  320. * Purpose: Sum node parameters all the way to the top
  321. * Returns: int: sum
  322. * Inputs: node:n - node to consider
  323. * string:side - scrollTop or scrollLeft
  324. */
  325. _fnSumScroll: function ( n, side )
  326. {
  327. var i = n[side];
  328. while ( n = n.parentNode )
  329. {
  330. if ( n.nodeName != 'HTML' && n.nodeName != 'BODY' )
  331. {
  332. break;
  333. }
  334. i = n[side];
  335. }
  336. return i;
  337. },
  338. /*
  339. * Function: _fnUpdatePositions
  340. * Purpose: Loop over the fixed elements for this table and update their positions
  341. * Returns: -
  342. * Inputs: -
  343. */
  344. _fnUpdatePositions: function ()
  345. {
  346. var s = this.fnGetSettings();
  347. this._fnMeasure();
  348. for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
  349. {
  350. if ( s.aoCache[i].sType == "fixedHeader" )
  351. {
  352. this._fnScrollFixedHeader( s.aoCache[i] );
  353. }
  354. else if ( s.aoCache[i].sType == "fixedFooter" )
  355. {
  356. this._fnScrollFixedFooter( s.aoCache[i] );
  357. }
  358. else if ( s.aoCache[i].sType == "fixedLeft" )
  359. {
  360. this._fnScrollHorizontalLeft( s.aoCache[i] );
  361. }
  362. else
  363. {
  364. this._fnScrollHorizontalRight( s.aoCache[i] );
  365. }
  366. }
  367. },
  368. /*
  369. * Function: _fnUpdateClones
  370. * Purpose: Loop over the fixed elements for this table and call their cloning functions
  371. * Returns: -
  372. * Inputs: -
  373. */
  374. _fnUpdateClones: function ()
  375. {
  376. var s = this.fnGetSettings();
  377. for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
  378. {
  379. s.aoCache[i].fnClone.call( this, s.aoCache[i] );
  380. }
  381. },
  382. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  383. * Scrolling functions
  384. */
  385. /*
  386. * Function: _fnScrollHorizontalLeft
  387. * Purpose: Update the positioning of the scrolling elements
  388. * Returns: -
  389. * Inputs: object:oCache - the cahced values for this fixed element
  390. */
  391. _fnScrollHorizontalRight: function ( oCache )
  392. {
  393. var
  394. s = this.fnGetSettings(),
  395. oMes = s.oMes,
  396. oWin = FixedHeader.oWin,
  397. oDoc = FixedHeader.oDoc,
  398. nTable = oCache.nWrapper,
  399. iFixedWidth = jQuery(nTable).outerWidth();
  400. if ( oWin.iScrollRight < oMes.iTableRight )
  401. {
  402. /* Fully right aligned */
  403. this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
  404. this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
  405. this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iFixedWidth)+"px", 'left', nTable.style );
  406. }
  407. else if ( oMes.iTableLeft < oDoc.iWidth-oWin.iScrollRight-iFixedWidth )
  408. {
  409. /* Middle */
  410. if ( s.bUseAbsPos )
  411. {
  412. this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
  413. this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
  414. this._fnUpdateCache( oCache, 'sLeft', (oDoc.iWidth-oWin.iScrollRight-iFixedWidth)+"px", 'left', nTable.style );
  415. }
  416. else
  417. {
  418. this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
  419. this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
  420. this._fnUpdateCache( oCache, 'sLeft', (oWin.iWidth-iFixedWidth)+"px", 'left', nTable.style );
  421. }
  422. }
  423. else
  424. {
  425. /* Fully left aligned */
  426. this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
  427. this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
  428. this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
  429. }
  430. },
  431. /*
  432. * Function: _fnScrollHorizontalLeft
  433. * Purpose: Update the positioning of the scrolling elements
  434. * Returns: -
  435. * Inputs: object:oCache - the cahced values for this fixed element
  436. */
  437. _fnScrollHorizontalLeft: function ( oCache )
  438. {
  439. var
  440. s = this.fnGetSettings(),
  441. oMes = s.oMes,
  442. oWin = FixedHeader.oWin,
  443. oDoc = FixedHeader.oDoc,
  444. nTable = oCache.nWrapper,
  445. iCellWidth = jQuery(nTable).outerWidth();
  446. if ( oWin.iScrollLeft < oMes.iTableLeft )
  447. {
  448. /* Fully left align */
  449. this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
  450. this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
  451. this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
  452. }
  453. else if ( oWin.iScrollLeft < oMes.iTableLeft+oMes.iTableWidth-iCellWidth )
  454. {
  455. /* Middle */
  456. if ( s.bUseAbsPos )
  457. {
  458. this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
  459. this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
  460. this._fnUpdateCache( oCache, 'sLeft', oWin.iScrollLeft+"px", 'left', nTable.style );
  461. }
  462. else
  463. {
  464. this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
  465. this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
  466. this._fnUpdateCache( oCache, 'sLeft', "0px", 'left', nTable.style );
  467. }
  468. }
  469. else
  470. {
  471. /* Fully right align */
  472. this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
  473. this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
  474. this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iCellWidth)+"px", 'left', nTable.style );
  475. }
  476. },
  477. /*
  478. * Function: _fnScrollFixedFooter
  479. * Purpose: Update the positioning of the scrolling elements
  480. * Returns: -
  481. * Inputs: object:oCache - the cahced values for this fixed element
  482. */
  483. _fnScrollFixedFooter: function ( oCache )
  484. {
  485. var
  486. s = this.fnGetSettings(),
  487. oMes = s.oMes,
  488. oWin = FixedHeader.oWin,
  489. oDoc = FixedHeader.oDoc,
  490. nTable = oCache.nWrapper,
  491. iTheadHeight = jQuery("thead", s.nTable).outerHeight(),
  492. iCellHeight = jQuery(nTable).outerHeight();
  493. if ( oWin.iScrollBottom < oMes.iTableBottom )
  494. {
  495. /* Below */
  496. this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
  497. this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+oMes.iTableHeight-iCellHeight)+"px", 'top', nTable.style );
  498. this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
  499. }
  500. else if ( oWin.iScrollBottom < oMes.iTableBottom+oMes.iTableHeight-iCellHeight-iTheadHeight )
  501. {
  502. /* Middle */
  503. if ( s.bUseAbsPos )
  504. {
  505. this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
  506. this._fnUpdateCache( oCache, 'sTop', (oDoc.iHeight-oWin.iScrollBottom-iCellHeight)+"px", 'top', nTable.style );
  507. this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
  508. }
  509. else
  510. {
  511. this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
  512. this._fnUpdateCache( oCache, 'sTop', (oWin.iHeight-iCellHeight)+"px", 'top', nTable.style );
  513. this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
  514. }
  515. }
  516. else
  517. {
  518. /* Above */
  519. this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
  520. this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iCellHeight)+"px", 'top', nTable.style );
  521. this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
  522. }
  523. },
  524. /*
  525. * Function: _fnScrollFixedHeader
  526. * Purpose: Update the positioning of the scrolling elements
  527. * Returns: -
  528. * Inputs: object:oCache - the cahced values for this fixed element
  529. */
  530. _fnScrollFixedHeader: function ( oCache )
  531. {
  532. var
  533. s = this.fnGetSettings(),
  534. oMes = s.oMes,
  535. oWin = FixedHeader.oWin,
  536. oDoc = FixedHeader.oDoc,
  537. nTable = oCache.nWrapper,
  538. iTbodyHeight = s.nTable.getElementsByTagName('tbody')[0].offsetHeight;
  539. if ( oMes.iTableTop > oWin.iScrollTop )
  540. {
  541. /* Above the table */
  542. this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
  543. this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
  544. this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
  545. }
  546. else if ( oWin.iScrollTop > oMes.iTableTop+iTbodyHeight )
  547. {
  548. /* At the bottom of the table */
  549. this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
  550. this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iTbodyHeight)+"px", 'top', nTable.style );
  551. this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
  552. }
  553. else
  554. {
  555. /* In the middle of the table */
  556. if ( s.bUseAbsPos )
  557. {
  558. this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
  559. this._fnUpdateCache( oCache, 'sTop', oWin.iScrollTop+"px", 'top', nTable.style );
  560. this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
  561. }
  562. else
  563. {
  564. this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
  565. this._fnUpdateCache( oCache, 'sTop', "0px", 'top', nTable.style );
  566. this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
  567. }
  568. }
  569. },
  570. /*
  571. * Function: _fnUpdateCache
  572. * Purpose: Check the cache and update cache and value if needed
  573. * Returns: -
  574. * Inputs: object:oCache - local cache object
  575. * string:sCache - cache property
  576. * string:sSet - value to set
  577. * string:sProperty - object property to set
  578. * object:oObj - object to update
  579. */
  580. _fnUpdateCache: function ( oCache, sCache, sSet, sProperty, oObj )
  581. {
  582. if ( oCache[sCache] != sSet )
  583. {
  584. oObj[sProperty] = sSet;
  585. oCache[sCache] = sSet;
  586. }
  587. },
  588. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  589. * Cloning functions
  590. */
  591. /*
  592. * Function: _fnCloneThead
  593. * Purpose: Clone the thead element
  594. * Returns: -
  595. * Inputs: object:oCache - the cahced values for this fixed element
  596. */
  597. _fnCloneThead: function ( oCache )
  598. {
  599. var s = this.fnGetSettings();
  600. var nTable = oCache.nNode;
  601. /* Set the wrapper width to match that of the cloned table */
  602. oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px";
  603. /* Remove any children the cloned table has */
  604. while ( nTable.childNodes.length > 0 )
  605. {
  606. jQuery('thead th', nTable).unbind( 'click' );
  607. nTable.removeChild( nTable.childNodes[0] );
  608. }
  609. /* Clone the DataTables header */
  610. var nThead = jQuery('thead', s.nTable).clone(true)[0];
  611. nTable.appendChild( nThead );
  612. /* Copy the widths across - apparently a clone isn't good enough for this */
  613. jQuery("thead>tr th", s.nTable).each( function (i) {
  614. jQuery("thead>tr th:eq("+i+")", nTable).width( jQuery(this).width() );
  615. } );
  616. jQuery("thead>tr td", s.nTable).each( function (i) {
  617. jQuery("thead>tr td:eq("+i+")", nTable).width( jQuery(this).width() );
  618. } );
  619. },
  620. /*
  621. * Function: _fnCloneTfoot
  622. * Purpose: Clone the tfoot element
  623. * Returns: -
  624. * Inputs: object:oCache - the cahced values for this fixed element
  625. */
  626. _fnCloneTfoot: function ( oCache )
  627. {
  628. var s = this.fnGetSettings();
  629. var nTable = oCache.nNode;
  630. /* Set the wrapper width to match that of the cloned table */
  631. oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px";
  632. /* Remove any children the cloned table has */
  633. while ( nTable.childNodes.length > 0 )
  634. {
  635. nTable.removeChild( nTable.childNodes[0] );
  636. }
  637. /* Clone the DataTables footer */
  638. var nTfoot = jQuery('tfoot', s.nTable).clone(true)[0];
  639. nTable.appendChild( nTfoot );
  640. /* Copy the widths across - apparently a clone isn't good enough for this */
  641. jQuery("tfoot:eq(0)>tr th", s.nTable).each( function (i) {
  642. jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable).width( jQuery(this).width() );
  643. } );
  644. jQuery("tfoot:eq(0)>tr td", s.nTable).each( function (i) {
  645. jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable)[0].style.width( jQuery(this).width() );
  646. } );
  647. },
  648. /*
  649. * Function: _fnCloneTLeft
  650. * Purpose: Clone the left column
  651. * Returns: -
  652. * Inputs: object:oCache - the cahced values for this fixed element
  653. */
  654. _fnCloneTLeft: function ( oCache )
  655. {
  656. var s = this.fnGetSettings();
  657. var nTable = oCache.nNode;
  658. var nBody = $('tbody', s.nTable)[0];
  659. var iCols = $('tbody tr:eq(0) td', s.nTable).length;
  660. var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
  661. /* Remove any children the cloned table has */
  662. while ( nTable.childNodes.length > 0 )
  663. {
  664. nTable.removeChild( nTable.childNodes[0] );
  665. }
  666. /* Is this the most efficient way to do this - it looks horrible... */
  667. nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] );
  668. nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] );
  669. if ( s.bFooter )
  670. {
  671. nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] );
  672. }
  673. jQuery('thead tr th:gt(0)', nTable).remove();
  674. jQuery('tfoot tr th:gt(0)', nTable).remove();
  675. /* Remove unneeded cells */
  676. $('tbody tr', nTable).each( function (k) {
  677. $('td:gt(0)', this).remove();
  678. } );
  679. this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
  680. var iWidth = jQuery('thead tr th:eq(0)', s.nTable).outerWidth();
  681. nTable.style.width = iWidth+"px";
  682. oCache.nWrapper.style.width = iWidth+"px";
  683. },
  684. /*
  685. * Function: _fnCloneTRight
  686. * Purpose: Clone the right most colun
  687. * Returns: -
  688. * Inputs: object:oCache - the cahced values for this fixed element
  689. */
  690. _fnCloneTRight: function ( oCache )
  691. {
  692. var s = this.fnGetSettings();
  693. var nBody = $('tbody', s.nTable)[0];
  694. var nTable = oCache.nNode;
  695. var iCols = jQuery('tbody tr:eq(0) td', s.nTable).length;
  696. var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
  697. /* Remove any children the cloned table has */
  698. while ( nTable.childNodes.length > 0 )
  699. {
  700. nTable.removeChild( nTable.childNodes[0] );
  701. }
  702. /* Is this the most efficient way to do this - it looks horrible... */
  703. nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] );
  704. nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] );
  705. if ( s.bFooter )
  706. {
  707. nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] );
  708. }
  709. jQuery('thead tr th:not(:nth-child('+iCols+'n))', nTable).remove();
  710. jQuery('tfoot tr th:not(:nth-child('+iCols+'n))', nTable).remove();
  711. /* Remove unneeded cells */
  712. $('tbody tr', nTable).each( function (k) {
  713. $('td:lt('+(iCols-1)+')', this).remove();
  714. } );
  715. this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
  716. var iWidth = jQuery('thead tr th:eq('+(iCols-1)+')', s.nTable).outerWidth();
  717. nTable.style.width = iWidth+"px";
  718. oCache.nWrapper.style.width = iWidth+"px";
  719. },
  720. /**
  721. * Equalise the heights of the rows in a given table node in a cross browser way. Note that this
  722. * is more or less lifted as is from FixedColumns
  723. * @method fnEqualiseHeights
  724. * @returns void
  725. * @param {string} parent Node type - thead, tbody or tfoot
  726. * @param {element} original Original node to take the heights from
  727. * @param {element} clone Copy the heights to
  728. * @private
  729. */
  730. "fnEqualiseHeights": function ( parent, original, clone )
  731. {
  732. var that = this,
  733. jqBoxHack = $(parent+' tr:eq(0)', original).children(':eq(0)'),
  734. iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
  735. bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
  736. /* Remove cells which are not needed and copy the height from the original table */
  737. $(parent+' tr', clone).each( function (k) {
  738. /* Can we use some kind of object detection here?! This is very nasty - damn browsers */
  739. if ( $.browser.mozilla || $.browser.opera )
  740. {
  741. $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() );
  742. }
  743. else
  744. {
  745. $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() - iBoxHack );
  746. }
  747. if ( !bRubbishOldIE )
  748. {
  749. $(parent+' tr:eq('+k+')', original).height( $(parent+' tr:eq('+k+')', original).outerHeight() );
  750. }
  751. } );
  752. }
  753. };
  754. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  755. * Static properties and methods
  756. * We use these for speed! This information is common to all instances of FixedHeader, so no
  757. * point if having them calculated and stored for each different instance.
  758. */
  759. /*
  760. * Variable: oWin
  761. * Purpose: Store information about the window positioning
  762. * Scope: FixedHeader
  763. */
  764. FixedHeader.oWin = {
  765. "iScrollTop": 0,
  766. "iScrollRight": 0,
  767. "iScrollBottom": 0,
  768. "iScrollLeft": 0,
  769. "iHeight": 0,
  770. "iWidth": 0
  771. };
  772. /*
  773. * Variable: oDoc
  774. * Purpose: Store information about the document size
  775. * Scope: FixedHeader
  776. */
  777. FixedHeader.oDoc = {
  778. "iHeight": 0,
  779. "iWidth": 0
  780. };
  781. /*
  782. * Variable: afnScroll
  783. * Purpose: Array of functions that are to be used for the scrolling components
  784. * Scope: FixedHeader
  785. */
  786. FixedHeader.afnScroll = [];
  787. /*
  788. * Function: fnMeasure
  789. * Purpose: Update the measurements for the window and document
  790. * Returns: -
  791. * Inputs: -
  792. */
  793. FixedHeader.fnMeasure = function ()
  794. {
  795. var
  796. jqWin = jQuery(window),
  797. jqDoc = jQuery(document),
  798. oWin = FixedHeader.oWin,
  799. oDoc = FixedHeader.oDoc;
  800. oDoc.iHeight = jqDoc.height();
  801. oDoc.iWidth = jqDoc.width();
  802. oWin.iHeight = jqWin.height();
  803. oWin.iWidth = jqWin.width();
  804. oWin.iScrollTop = jqWin.scrollTop();
  805. oWin.iScrollLeft = jqWin.scrollLeft();
  806. oWin.iScrollRight = oDoc.iWidth - oWin.iScrollLeft - oWin.iWidth;
  807. oWin.iScrollBottom = oDoc.iHeight - oWin.iScrollTop - oWin.iHeight;
  808. };
  809. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  810. * Global processing
  811. */
  812. /*
  813. * Just one 'scroll' event handler in FixedHeader, which calls the required components. This is
  814. * done as an optimisation, to reduce calculation and proagation time
  815. */
  816. jQuery(window).scroll( function () {
  817. FixedHeader.fnMeasure();
  818. for ( var i=0, iLen=FixedHeader.afnScroll.length ; i<iLen ; i++ )
  819. {
  820. FixedHeader.afnScroll[i]();
  821. }
  822. } );