FixedHeader.js 27 KB

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